Skip to content

Commit a77eedf

Browse files
committed
Add Data.ByteString.Short.unconsN
Fixes #524
1 parent 2471604 commit a77eedf

4 files changed

Lines changed: 67 additions & 0 deletions

File tree

Data/ByteString/Short.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ module Data.ByteString.Short (
8282
last,
8383
tail,
8484
uncons,
85+
unconsN,
8586
head,
8687
init,
8788
unsnoc,

Data/ByteString/Short/Internal.hs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ module Data.ByteString.Short.Internal (
5757
last,
5858
tail,
5959
uncons,
60+
unconsN,
6061
head,
6162
init,
6263
unsnoc,
@@ -758,6 +759,33 @@ uncons = \sbs ->
758759
t = create nl $ \mba -> copyByteArray (asBA sbs) 1 mba 0 nl
759760
in Just (h, t)
760761

762+
-- | /O(n)/ Extract the given number of elements from a ShortByteString and the remainder.
763+
-- Returns 'Nothing' if the ShortByteString is smaller than the requested number of elements.
764+
--
765+
-- >>> unconsN 3 "abcdefg"
766+
-- Just ([97,98,99],"defg")
767+
-- >>> unconsN 11 "abcdefg"
768+
-- Nothing
769+
-- >>> unconsN 0 "abcdefg"
770+
-- Nothing
771+
--
772+
-- Satisfies the following properties:
773+
--
774+
-- > \x i -> unconsN i x == (if length x < i || i < 1 then Nothing else let u = unpack in Just (take i u, pack (drop i u)))
775+
-- > \x -> unconsN 1 x == fmap (\(x, y) -> ([x], y)) (uncons x)
776+
-- > \x i -> maybe i (\(xs, _) -> length xs) (unconsN i x) === i
777+
--
778+
-- @since 0.11.3.2
779+
unconsN :: Int -> ShortByteString -> Maybe ([Word8], ShortByteString)
780+
unconsN n = \sbs ->
781+
let l = length sbs
782+
nl = l - n
783+
ix = n - 1
784+
in if | n < 1 || l <= ix -> Nothing
785+
| otherwise -> let h = List.map (indexWord8Array (asBA sbs)) [0..ix]
786+
t = create nl $ \mba -> copyByteArray (asBA sbs) n mba 0 nl
787+
in Just (h, t)
788+
761789
-- | /O(1)/ Extract the first element of a ShortByteString, which must be non-empty.
762790
-- An exception will be thrown in the case of an empty ShortByteString.
763791
--

bench/BenchShort.hs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,4 +247,31 @@ benchShort = bgroup "ShortByteString"
247247
, bench "unpack and get last element" $ nf (\x -> last . S.unpack $ x) absurdlong
248248
, bench "unpack and get first 120 elements" $ nf (\x -> take 120 . S.unpack $ x) absurdlong
249249
]
250+
, bgroup "ShortByteString unpack/uncons comparison" $
251+
[ bench "unpack and look at first 5 elements" $ nf (unpack5) absurdlong
252+
, bench "uncons consecutively 5 times" $ nf (uncons5) absurdlong
253+
, bench "unconsN 5" $ nf (unconsN) absurdlong
254+
]
250255
]
256+
257+
258+
unpack5 :: ShortByteString -> Bool
259+
unpack5 sbs = case S.unpack sbs of
260+
(a:b:c:d:e:_) -> True
261+
_ -> False
262+
263+
264+
uncons5 :: ShortByteString -> Bool
265+
uncons5 sbs
266+
| Just (a, r1) <- S.uncons sbs
267+
, Just (b, r2) <- S.uncons r1
268+
, Just (c, r3) <- S.uncons r2
269+
, Just (d, r4) <- S.uncons r3
270+
, Just (e, xs) <- S.uncons r4 = True
271+
| otherwise = False
272+
273+
unconsN :: ShortByteString -> Bool
274+
unconsN sbs = case S.unconsN 5 sbs of
275+
Just ([a, b, c, d, e], xs) -> True
276+
Just _ -> error "oops"
277+
_ -> False

tests/Properties/ByteString.hs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,17 @@ tests =
655655
\s -> fromString s == B.pack (map (fromIntegral . ord :: Char -> Word8) s)
656656
, testProperty "fromString literal" $
657657
fromString "\0\1\2\3\4" == B.pack [0,1,2,3,4]
658+
#endif
659+
#ifdef BYTESTRING_SHORT
660+
, testProperty "unconsN == unpack" $
661+
\x i -> B.unconsN i x === (if B.length x < i || i < 1 then Nothing else let u = B.unpack x
662+
l = take i u
663+
r = B.pack (drop i u)
664+
in Just (l, r))
665+
, testProperty "unconsN 1 == uncons" $
666+
\x -> B.unconsN 1 x === fmap (\(x, y) -> ([x], y)) (B.uncons x)
667+
, testProperty "length of items matches input (unconsN)" $
668+
\x i -> maybe i (\(xs, _) -> length xs) (B.unconsN i x) === i
658669
#endif
659670
]
660671

0 commit comments

Comments
 (0)