{-# OPTIONS_GHC -cpp -fglasgow-exts -fno-warn-orphans -fno-warn-incomplete-patterns #-} -- #prune -- | -- Module : Data.ByteString.Lazy -- Copyright : (c) Don Stewart 2006 -- (c) Duncan Coutts 2006 -- License : BSD-style -- -- Maintainer : dons@galois.com -- Stability : experimental -- Portability : portable -- -- A time and space-efficient implementation of lazy byte vectors -- using lists of packed 'Word8' arrays, suitable for high performance -- use, both in terms of large data quantities, or high speed -- requirements. Byte vectors are encoded as lazy lists of strict 'Word8' -- arrays of bytes. They provide a means to manipulate large byte vectors -- without requiring the entire vector be resident in memory. -- -- Some operations, such as concat, append, reverse and cons, have -- better complexity than their "Data.ByteString" equivalents, due to -- optimisations resulting from the list spine structure. And for other -- operations lazy ByteStrings are usually within a few percent of -- strict ones, but with better heap usage. For data larger than the -- available memory, or if you have tight memory constraints, this -- module will be the only option. The default chunk size is 64k, which -- should be good in most circumstances. For people with large L2 -- caches, you may want to increase this to fit your cache. -- -- This module is intended to be imported @qualified@, to avoid name -- clashes with "Prelude" functions. eg. -- -- > import qualified Data.ByteString.Lazy as B -- -- Original GHC implementation by Bryan O\'Sullivan. -- Rewritten to use 'Data.Array.Unboxed.UArray' by Simon Marlow. -- Rewritten to support slices and use 'Foreign.ForeignPtr.ForeignPtr' -- by David Roundy. -- Polished and extended by Don Stewart. -- Lazy variant by Duncan Coutts and Don Stewart. -- module Data.ByteString.Lazy ( -- * The @ByteString@ type ByteString, -- instances: Eq, Ord, Show, Read, Data, Typeable -- * Introducing and eliminating 'ByteString's empty, -- :: ByteString singleton, -- :: Word8 -> ByteString pack, -- :: [Word8] -> ByteString unpack, -- :: ByteString -> [Word8] fromChunks, -- :: [Strict.ByteString] -> ByteString toChunks, -- :: ByteString -> [Strict.ByteString] -- * Basic interface cons, -- :: Word8 -> ByteString -> ByteString cons', -- :: Word8 -> ByteString -> ByteString snoc, -- :: ByteString -> Word8 -> ByteString append, -- :: ByteString -> ByteString -> ByteString head, -- :: ByteString -> Word8 uncons, -- :: ByteString -> Maybe (Word8, ByteString) last, -- :: ByteString -> Word8 tail, -- :: ByteString -> ByteString init, -- :: ByteString -> ByteString null, -- :: ByteString -> Bool length, -- :: ByteString -> Int64 -- * Transforming ByteStrings map, -- :: (Word8 -> Word8) -> ByteString -> ByteString reverse, -- :: ByteString -> ByteString intersperse, -- :: Word8 -> ByteString -> ByteString intercalate, -- :: ByteString -> [ByteString] -> ByteString transpose, -- :: [ByteString] -> [ByteString] -- * Reducing 'ByteString's (folds) foldl, -- :: (a -> Word8 -> a) -> a -> ByteString -> a foldl', -- :: (a -> Word8 -> a) -> a -> ByteString -> a foldl1, -- :: (Word8 -> Word8 -> Word8) -> ByteString -> Word8 foldl1', -- :: (Word8 -> Word8 -> Word8) -> ByteString -> Word8 foldr, -- :: (Word8 -> a -> a) -> a -> ByteString -> a foldr1, -- :: (Word8 -> Word8 -> Word8) -> ByteString -> Word8 -- ** Special folds concat, -- :: [ByteString] -> ByteString concatMap, -- :: (Word8 -> ByteString) -> ByteString -> ByteString any, -- :: (Word8 -> Bool) -> ByteString -> Bool all, -- :: (Word8 -> Bool) -> ByteString -> Bool maximum, -- :: ByteString -> Word8 minimum, -- :: ByteString -> Word8 -- * Building ByteStrings -- ** Scans scanl, -- :: (Word8 -> Word8 -> Word8) -> Word8 -> ByteString -> ByteString -- scanl1, -- :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -- scanr, -- :: (Word8 -> Word8 -> Word8) -> Word8 -> ByteString -> ByteString -- scanr1, -- :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -- ** Accumulating maps mapAccumL, -- :: (acc -> Word8 -> (acc, Word8)) -> acc -> ByteString -> (acc, ByteString) mapAccumR, -- :: (acc -> Word8 -> (acc, Word8)) -> acc -> ByteString -> (acc, ByteString) mapIndexed, -- :: (Int64 -> Word8 -> Word8) -> ByteString -> ByteString -- ** Infinite ByteStrings repeat, -- :: Word8 -> ByteString replicate, -- :: Int64 -> Word8 -> ByteString cycle, -- :: ByteString -> ByteString iterate, -- :: (Word8 -> Word8) -> Word8 -> ByteString -- ** Unfolding ByteStrings unfoldr, -- :: (a -> Maybe (Word8, a)) -> a -> ByteString -- * Substrings -- ** Breaking strings take, -- :: Int64 -> ByteString -> ByteString drop, -- :: Int64 -> ByteString -> ByteString splitAt, -- :: Int64 -> ByteString -> (ByteString, ByteString) takeWhile, -- :: (Word8 -> Bool) -> ByteString -> ByteString dropWhile, -- :: (Word8 -> Bool) -> ByteString -> ByteString span, -- :: (Word8 -> Bool) -> ByteString -> (ByteString, ByteString) break, -- :: (Word8 -> Bool) -> ByteString -> (ByteString, ByteString) group, -- :: ByteString -> [ByteString] groupBy, -- :: (Word8 -> Word8 -> Bool) -> ByteString -> [ByteString] inits, -- :: ByteString -> [ByteString] tails, -- :: ByteString -> [ByteString] -- ** Breaking into many substrings split, -- :: Word8 -> ByteString -> [ByteString] splitWith, -- :: (Word8 -> Bool) -> ByteString -> [ByteString] -- * Predicates isPrefixOf, -- :: ByteString -> ByteString -> Bool isSuffixOf, -- :: ByteString -> ByteString -> Bool -- isInfixOf, -- :: ByteString -> ByteString -> Bool -- ** Search for arbitrary substrings -- isSubstringOf, -- :: ByteString -> ByteString -> Bool -- findSubstring, -- :: ByteString -> ByteString -> Maybe Int -- findSubstrings, -- :: ByteString -> ByteString -> [Int] -- * Searching ByteStrings -- ** Searching by equality elem, -- :: Word8 -> ByteString -> Bool notElem, -- :: Word8 -> ByteString -> Bool -- ** Searching with a predicate find, -- :: (Word8 -> Bool) -> ByteString -> Maybe Word8 filter, -- :: (Word8 -> Bool) -> ByteString -> ByteString partition, -- :: (Word8 -> Bool) -> ByteString -> (ByteString, ByteString) -- * Indexing ByteStrings index, -- :: ByteString -> Int64 -> Word8 elemIndex, -- :: Word8 -> ByteString -> Maybe Int64 elemIndices, -- :: Word8 -> ByteString -> [Int64] findIndex, -- :: (Word8 -> Bool) -> ByteString -> Maybe Int64 findIndices, -- :: (Word8 -> Bool) -> ByteString -> [Int64] count, -- :: Word8 -> ByteString -> Int64 -- * Zipping and unzipping ByteStrings zip, -- :: ByteString -> ByteString -> [(Word8,Word8)] zipWith, -- :: (Word8 -> Word8 -> c) -> ByteString -> ByteString -> [c] unzip, -- :: [(Word8,Word8)] -> (ByteString,ByteString) -- * Ordered ByteStrings -- sort, -- :: ByteString -> ByteString -- * Low level conversions -- ** Copying ByteStrings copy, -- :: ByteString -> ByteString -- defrag, -- :: ByteString -> ByteString -- * I\/O with 'ByteString's -- ** Standard input and output getContents, -- :: IO ByteString putStr, -- :: ByteString -> IO () putStrLn, -- :: ByteString -> IO () interact, -- :: (ByteString -> ByteString) -> IO () -- ** Files readFile, -- :: FilePath -> IO ByteString writeFile, -- :: FilePath -> ByteString -> IO () appendFile, -- :: FilePath -> ByteString -> IO () -- ** I\/O with Handles hGetContents, -- :: Handle -> IO ByteString hGet, -- :: Handle -> Int -> IO ByteString hGetNonBlocking, -- :: Handle -> Int -> IO ByteString hPut, -- :: Handle -> ByteString -> IO () hPutStr, -- :: Handle -> ByteString -> IO () -- hGetN, -- :: Int -> Handle -> Int -> IO ByteString -- hGetContentsN, -- :: Int -> Handle -> IO ByteString -- hGetNonBlockingN, -- :: Int -> Handle -> IO ByteString -- undocumented deprecated things: join -- :: ByteString -> [ByteString] -> ByteString ) where import qualified Prelude import Prelude hiding (reverse,head,tail,last,init,null,length,map,lines,foldl,foldr,unlines ,concat,any,take,drop,splitAt,takeWhile,dropWhile,span,break,elem,filter,maximum ,minimum,all,concatMap,foldl1,foldr1,scanl, scanl1, scanr, scanr1 ,repeat, cycle, interact, iterate,readFile,writeFile,appendFile,replicate ,getContents,getLine,putStr,putStrLn ,zip,zipWith,unzip,notElem) import qualified Data.List as L -- L for list/lazy import qualified Data.ByteString as S -- S for strict (hmm...) import qualified Data.ByteString.Internal as S import qualified Data.ByteString.Unsafe as S import Data.ByteString.Lazy.Internal import qualified Data.ByteString.Fusion as F import Data.Monoid (Monoid(..)) import Data.Word (Word8) import Data.Int (Int64) import System.IO (Handle,stdin,stdout,openBinaryFile,IOMode(..) ,hClose,hWaitForInput,hIsEOF) import System.IO.Unsafe #ifndef __NHC__ import Control.Exception (bracket) #else import IO (bracket) #endif import Foreign.ForeignPtr (withForeignPtr) import Foreign.Ptr import Foreign.Storable -- ----------------------------------------------------------------------------- -- -- Useful macros, until we have bang patterns -- #define STRICT1(f) f a | a `seq` False = undefined #define STRICT2(f) f a b | a `seq` b `seq` False = undefined #define STRICT3(f) f a b c | a `seq` b `seq` c `seq` False = undefined #define STRICT4(f) f a b c d | a `seq` b `seq` c `seq` d `seq` False = undefined #define STRICT5(f) f a b c d e | a `seq` b `seq` c `seq` d `seq` e `seq` False = undefined -- ----------------------------------------------------------------------------- instance Eq ByteString where (==) = eq instance Ord ByteString where compare = cmp instance Monoid ByteString where mempty = empty mappend = append mconcat = concat eq :: ByteString -> ByteString -> Bool eq Empty Empty = True eq Empty _ = False eq _ Empty = False eq (Chunk a as) (Chunk b bs) = case compare (S.length a) (S.length b) of LT -> a == (S.take (S.length a) b) && eq as (Chunk (S.drop (S.length a) b) bs) EQ -> a == b && eq as bs GT -> (S.take (S.length b) a) == b && eq (Chunk (S.drop (S.length b) a) as) bs cmp :: ByteString -> ByteString -> Ordering cmp Empty Empty = EQ cmp Empty _ = LT cmp _ Empty = GT cmp (Chunk a as) (Chunk b bs) = case compare (S.length a) (S.length b) of LT -> case compare a (S.take (S.length a) b) of EQ -> cmp as (Chunk (S.drop (S.length a) b) bs) result -> result EQ -> case compare a b of EQ -> cmp as bs result -> result GT -> case compare (S.take (S.length b) a) b of EQ -> cmp (Chunk (S.drop (S.length b) a) as) bs result -> result -- ----------------------------------------------------------------------------- -- Introducing and eliminating 'ByteString's -- | /O(1)/ The empty 'ByteString' empty :: ByteString empty = Empty {-# INLINE empty #-} -- | /O(1)/ Convert a 'Word8' into a 'ByteString' singleton :: Word8 -> ByteString singleton w = Chunk (S.singleton w) Empty {-# INLINE singleton #-} -- | /O(n)/ Convert a '[Word8]' into a 'ByteString'. pack :: [Word8] -> ByteString pack ws = L.foldr (Chunk . S.pack) Empty (chunks defaultChunkSize ws) where chunks :: Int -> [a] -> [[a]] chunks _ [] = [] chunks size xs = case L.splitAt size xs of (xs', xs'') -> xs' : chunks size xs'' -- | /O(n)/ Converts a 'ByteString' to a '[Word8]'. unpack :: ByteString -> [Word8] unpack cs = L.concatMap S.unpack (toChunks cs) --TODO: we can do better here by integrating the concat with the unpack -- | /O(c)/ Convert a list of strict 'ByteString' into a lazy 'ByteString' fromChunks :: [S.ByteString] -> ByteString fromChunks cs = L.foldr chunk Empty cs -- | /O(n)/ Convert a lazy 'ByteString' into a list of strict 'ByteString' toChunks :: ByteString -> [S.ByteString] toChunks cs = foldrChunks (:) [] cs ------------------------------------------------------------------------ {- -- | /O(n)/ Convert a '[a]' into a 'ByteString' using some -- conversion function packWith :: (a -> Word8) -> [a] -> ByteString packWith k str = LPS $ L.map (P.packWith k) (chunk defaultChunkSize str) {-# INLINE packWith #-} {-# SPECIALIZE packWith :: (Char -> Word8) -> [Char] -> ByteString #-} -- | /O(n)/ Converts a 'ByteString' to a '[a]', using a conversion function. unpackWith :: (Word8 -> a) -> ByteString -> [a] unpackWith k (LPS ss) = L.concatMap (S.unpackWith k) ss {-# INLINE unpackWith #-} {-# SPECIALIZE unpackWith :: (Word8 -> Char) -> ByteString -> [Char] #-} -} -- --------------------------------------------------------------------- -- Basic interface -- | /O(1)/ Test whether a ByteString is empty. null :: ByteString -> Bool null Empty = True null _ = False {-# INLINE null #-} -- | /O(n\/c)/ 'length' returns the length of a ByteString as an 'Int64' length :: ByteString -> Int64 length cs = foldlChunks (\n c -> n + fromIntegral (S.length c)) 0 cs {-# INLINE length #-} -- | /O(1)/ 'cons' is analogous to '(:)' for lists. -- cons :: Word8 -> ByteString -> ByteString cons c cs = Chunk (S.singleton c) cs {-# INLINE cons #-} -- | /O(1)/ Unlike 'cons', 'cons\'' is -- strict in the ByteString that we are consing onto. More precisely, it forces -- the head and the first chunk. It does this because, for space efficiency, it -- may coalesce the new byte onto the first \'chunk\' rather than starting a -- new \'chunk\'. -- -- So that means you can't use a lazy recursive contruction like this: -- -- > let xs = cons\' c xs in xs -- -- You can however use 'cons', as well as 'repeat' and 'cycle', to build -- infinite lazy ByteStrings. -- cons' :: Word8 -> ByteString -> ByteString cons' w (Chunk c cs) | S.length c < 16 = Chunk (S.cons w c) cs cons' w cs = Chunk (S.singleton w) cs {-# INLINE cons' #-} -- | /O(n\/c)/ Append a byte to the end of a 'ByteString' snoc :: ByteString -> Word8 -> ByteString snoc cs w = foldrChunks Chunk (singleton w) cs {-# INLINE snoc #-} -- | /O(1)/ Extract the first element of a ByteString, which must be non-empty. head :: ByteString -> Word8 head Empty = errorEmptyList "head" head (Chunk c _) = S.unsafeHead c {-# INLINE head #-} -- | /O(1)/ Extract the head and tail of a ByteString, returning Nothing -- if it is empty. uncons :: ByteString -> Maybe (Word8, ByteString) uncons Empty = Nothing uncons (Chunk c cs) = Just (S.unsafeHead c, if S.length c == 1 then cs else Chunk (S.unsafeTail c) cs) {-# INLINE uncons #-} -- | /O(1)/ Extract the elements after the head of a ByteString, which must be -- non-empty. tail :: ByteString -> ByteString tail Empty = errorEmptyList "tail" tail (Chunk c cs) | S.length c == 1 = cs | otherwise = Chunk (S.unsafeTail c) cs {-# INLINE tail #-} -- | /O(n\/c)/ Extract the last element of a ByteString, which must be finite -- and non-empty. last :: ByteString -> Word8 last Empty = errorEmptyList "last" last (Chunk c0 cs0) = go c0 cs0 where go c Empty = S.last c go _ (Chunk c cs) = go c cs -- XXX Don't inline this. Something breaks with 6.8.2 (haven't investigated yet) -- | /O(n\/c)/ Return all the elements of a 'ByteString' except the last one. init :: ByteString -> ByteString init Empty = errorEmptyList "init" init (Chunk c0 cs0) = go c0 cs0 where go c Empty | S.length c == 1 = Empty | otherwise = Chunk (S.init c) Empty go c (Chunk c' cs) = Chunk c (go c' cs) -- | /O(n\/c)/ Append two ByteStrings append :: ByteString -> ByteString -> ByteString append xs ys = foldrChunks Chunk ys xs {-# INLINE append #-} -- --------------------------------------------------------------------- -- Transformations -- | /O(n)/ 'map' @f xs@ is the ByteString obtained by applying @f@ to each -- element of @xs@. map :: (Word8 -> Word8) -> ByteString -> ByteString map f s = go s where go Empty = Empty go (Chunk x xs) = Chunk y ys where y = S.map f x ys = go xs {-# INLINE map #-} -- | /O(n)/ 'reverse' @xs@ returns the elements of @xs@ in reverse order. reverse :: ByteString -> ByteString reverse cs0 = rev Empty cs0 where rev a Empty = a rev a (Chunk c cs) = rev (Chunk (S.reverse c) a) cs {-# INLINE reverse #-} -- | The 'intersperse' function takes a 'Word8' and a 'ByteString' and -- \`intersperses\' that byte between the elements of the 'ByteString'. -- It is analogous to the intersperse function on Lists. intersperse :: Word8 -> ByteString -> ByteString intersperse _ Empty = Empty intersperse w (Chunk c cs) = Chunk (S.intersperse w c) (foldrChunks (Chunk . intersperse') Empty cs) where intersperse' :: S.ByteString -> S.ByteString intersperse' (S.PS fp o l) = S.unsafeCreate (2*l) $ \p' -> withForeignPtr fp $ \p -> do poke p' w S.c_intersperse (p' `plusPtr` 1) (p `plusPtr` o) (fromIntegral l) w -- | The 'transpose' function transposes the rows and columns of its -- 'ByteString' argument. transpose :: [ByteString] -> [ByteString] transpose css = L.map (\ss -> Chunk (S.pack ss) Empty) (L.transpose (L.map unpack css)) --TODO: make this fast -- --------------------------------------------------------------------- -- Reducing 'ByteString's -- | 'foldl', applied to a binary operator, a starting value (typically -- the left-identity of the operator), and a ByteString, reduces the -- ByteString using the binary operator, from left to right. foldl :: (a -> Word8 -> a) -> a -> ByteString -> a foldl f z = go z where go a Empty = a go a (Chunk c cs) = go (S.foldl f a c) cs {-# INLINE foldl #-} -- | 'foldl\'' is like 'foldl', but strict in the accumulator. foldl' :: (a -> Word8 -> a) -> a -> ByteString -> a foldl' f z = go z where go a _ | a `seq` False = undefined go a Empty = a go a (Chunk c cs) = go (S.foldl f a c) cs {-# INLINE foldl' #-} -- | 'foldr', applied to a binary operator, a starting value -- (typically the right-identity of the operator), and a ByteString, -- reduces the ByteString using the binary operator, from right to left. foldr :: (Word8 -> a -> a) -> a -> ByteString -> a foldr k z cs = foldrChunks (flip (S.foldr k)) z cs {-# INLINE foldr #-} -- | 'foldl1' is a variant of 'foldl' that has no starting value -- argument, and thus must be applied to non-empty 'ByteStrings'. -- This function is subject to array fusion. foldl1 :: (Word8 -> Word8 -> Word8) -> ByteString -> Word8 foldl1 _ Empty = errorEmptyList "foldl1" foldl1 f (Chunk c cs) = foldl f (S.unsafeHead c) (Chunk (S.unsafeTail c) cs) -- | 'foldl1\'' is like 'foldl1', but strict in the accumulator. foldl1' :: (Word8 -> Word8 -> Word8) -> ByteString -> Word8 foldl1' _ Empty = errorEmptyList "foldl1'" foldl1' f (Chunk c cs) = foldl' f (S.unsafeHead c) (Chunk (S.unsafeTail c) cs) -- | 'foldr1' is a variant of 'foldr' that has no starting value argument, -- and thus must be applied to non-empty 'ByteString's foldr1 :: (Word8 -> Word8 -> Word8) -> ByteString -> Word8 foldr1 _ Empty = errorEmptyList "foldr1" foldr1 f (Chunk c0 cs0) = go c0 cs0 where go c Empty = S.foldr1 f c go c (Chunk c' cs) = S.foldr f (go c' cs) c -- --------------------------------------------------------------------- -- Special folds -- | /O(n)/ Concatenate a list of ByteStrings. concat :: [ByteString] -> ByteString concat css0 = to css0 where go Empty css = to css go (Chunk c cs) css = Chunk c (go cs css) to [] = Empty to (cs:css) = go cs css -- | Map a function over a 'ByteString' and concatenate the results concatMap :: (Word8 -> ByteString) -> ByteString -> ByteString concatMap _ Empty = Empty concatMap f (Chunk c0 cs0) = to c0 cs0 where go :: ByteString -> S.ByteString -> ByteString -> ByteString go Empty c' cs' = to c' cs' go (Chunk c cs) c' cs' = Chunk c (go cs c' cs') to :: S.ByteString -> ByteString -> ByteString to c cs | S.null c = case cs of Empty -> Empty (Chunk c' cs') -> to c' cs' | otherwise = go (f (S.unsafeHead c)) (S.unsafeTail c) cs -- | /O(n)/ Applied to a predicate and a ByteString, 'any' determines if -- any element of the 'ByteString' satisfies the predicate. any :: (Word8 -> Bool) -> ByteString -> Bool any f cs = foldrChunks (\c rest -> S.any f c || rest) False cs {-# INLINE any #-} -- todo fuse -- | /O(n)/ Applied to a predicate and a 'ByteString', 'all' determines -- if all elements of the 'ByteString' satisfy the predicate. all :: (Word8 -> Bool) -> ByteString -> Bool all f cs = foldrChunks (\c rest -> S.all f c && rest) True cs {-# INLINE all #-} -- todo fuse -- | /O(n)/ 'maximum' returns the maximum value from a 'ByteString' maximum :: ByteString -> Word8 maximum Empty = errorEmptyList "maximum" maximum (Chunk c cs) = foldlChunks (\n c' -> n `max` S.maximum c') (S.maximum c) cs {-# INLINE maximum #-} -- | /O(n)/ 'minimum' returns the minimum value from a 'ByteString' minimum :: ByteString -> Word8 minimum Empty = errorEmptyList "minimum" minimum (Chunk c cs) = foldlChunks (\n c' -> n `min` S.minimum c') (S.minimum c) cs {-# INLINE minimum #-} -- | The 'mapAccumL' function behaves like a combination of 'map' and -- 'foldl'; it applies a function to each element of a ByteString, -- passing an accumulating parameter from left to right, and returning a -- final value of this accumulator together with the new ByteString. mapAccumL :: (acc -> Word8 -> (acc, Word8)) -> acc -> ByteString -> (acc, ByteString) mapAccumL f s0 cs0 = go s0 cs0 where go s Empty = (s, Empty) go s (Chunk c cs) = (s'', Chunk c' cs') where (s', c') = S.mapAccumL f s c (s'', cs') = go s' cs -- | The 'mapAccumR' function behaves like a combination of 'map' and -- 'foldr'; it applies a function to each element of a ByteString, -- passing an accumulating parameter from right to left, and returning a -- final value of this accumulator together with the new ByteString. mapAccumR :: (acc -> Word8 -> (acc, Word8)) -> acc -> ByteString -> (acc, ByteString) mapAccumR f s0 cs0 = go s0 cs0 where go s Empty = (s, Empty) go s (Chunk c cs) = (s'', Chunk c' cs') where (s'', c') = S.mapAccumR f s' c (s', cs') = go s cs -- | /O(n)/ map Word8 functions, provided with the index at each position mapIndexed :: (Int -> Word8 -> Word8) -> ByteString -> ByteString mapIndexed f = F.loopArr . F.loopL (F.mapIndexEFL f) 0 -- --------------------------------------------------------------------- -- Building ByteStrings -- | 'scanl' is similar to 'foldl', but returns a list of successive -- reduced values from the left. This function will fuse. -- -- > scanl f z [x1, x2, ...] == [z, z `f` x1, (z `f` x1) `f` x2, ...] -- -- Note that -- -- > last (scanl f z xs) == foldl f z xs. scanl :: (Word8 -> Word8 -> Word8) -> Word8 -> ByteString -> ByteString scanl f z ps = F.loopArr . F.loopL (F.scanEFL f) z $ (ps `snoc` 0) {-# INLINE scanl #-} -- --------------------------------------------------------------------- -- Unfolds and replicates -- | @'iterate' f x@ returns an infinite ByteString of repeated applications -- of @f@ to @x@: -- -- > iterate f x == [x, f x, f (f x), ...] -- iterate :: (Word8 -> Word8) -> Word8 -> ByteString iterate f = unfoldr (\x -> case f x of x' -> x' `seq` Just (x', x')) -- | @'repeat' x@ is an infinite ByteString, with @x@ the value of every -- element. -- repeat :: Word8 -> ByteString repeat w = cs where cs = Chunk (S.replicate smallChunkSize w) cs -- | /O(n)/ @'replicate' n x@ is a ByteString of length @n@ with @x@ -- the value of every element. -- replicate :: Int64 -> Word8 -> ByteString replicate n w | n <= 0 = Empty | n < fromIntegral smallChunkSize = Chunk (S.replicate (fromIntegral n) w) Empty | r == 0 = cs -- preserve invariant | otherwise = Chunk (S.unsafeTake (fromIntegral r) c) cs where c = S.replicate smallChunkSize w cs = nChunks q (q, r) = quotRem n (fromIntegral smallChunkSize) nChunks 0 = Empty nChunks m = Chunk c (nChunks (m-1)) -- | 'cycle' ties a finite ByteString into a circular one, or equivalently, -- the infinite repetition of the original ByteString. -- cycle :: ByteString -> ByteString cycle Empty = errorEmptyList "cycle" cycle cs = cs' where cs' = foldrChunks Chunk cs' cs -- | /O(n)/ The 'unfoldr' function is analogous to the List \'unfoldr\'. -- 'unfoldr' builds a ByteString from a seed value. The function takes -- the element and returns 'Nothing' if it is done producing the -- ByteString or returns 'Just' @(a,b)@, in which case, @a@ is a -- prepending to the ByteString and @b@ is used as the next element in a -- recursive call. unfoldr :: (a -> Maybe (Word8, a)) -> a -> ByteString unfoldr f s0 = unfoldChunk 32 s0 where unfoldChunk n s = case S.unfoldrN n f s of (c, Nothing) | S.null c -> Empty | otherwise -> Chunk c Empty (c, Just s') -> Chunk c (unfoldChunk (n*2) s') -- --------------------------------------------------------------------- -- Substrings -- | /O(n\/c)/ 'take' @n@, applied to a ByteString @xs@, returns the prefix -- of @xs@ of length @n@, or @xs@ itself if @n > 'length' xs@. take :: Int64 -> ByteString -> ByteString take i _ | i <= 0 = Empty take i cs0 = take' i cs0 where take' 0 _ = Empty take' _ Empty = Empty take' n (Chunk c cs) = if n < fromIntegral (S.length c) then Chunk (S.take (fromIntegral n) c) Empty else Chunk c (take' (n - fromIntegral (S.length c)) cs) -- | /O(n\/c)/ 'drop' @n xs@ returns the suffix of @xs@ after the first @n@ -- elements, or @[]@ if @n > 'length' xs@. drop :: Int64 -> ByteString -> ByteString drop i p | i <= 0 = p drop i cs0 = drop' i cs0 where drop' 0 cs = cs drop' _ Empty = Empty drop' n (Chunk c cs) = if n < fromIntegral (S.length c) then Chunk (S.drop (fromIntegral n) c) cs else drop' (n - fromIntegral (S.length c)) cs -- | /O(n\/c)/ 'splitAt' @n xs@ is equivalent to @('take' n xs, 'drop' n xs)@. splitAt :: Int64 -> ByteString -> (