pipes-bytestring 1.0.3 → 2.0.0
raw patch · 4 files changed
+417/−425 lines, 4 filesdep +pipes-groupdep +profunctorsdep ~pipesdep ~pipes-parse
Dependencies added: pipes-group, profunctors
Dependency ranges changed: pipes, pipes-parse
Files
- LICENSE +1/−1
- pipes-bytestring.cabal +7/−7
- src/Pipes/ByteString.hs +409/−275
- src/Pipes/ByteString/Parse.hs +0/−142
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2012, 2013 Gabriel Gonzalez+Copyright (c) 2012-2014 Gabriel Gonzalez All rights reserved. Redistribution and use in source and binary forms, with or without modification,
pipes-bytestring.cabal view
@@ -1,10 +1,10 @@ Name: pipes-bytestring-Version: 1.0.3+Version: 2.0.0 Cabal-Version: >=1.8.0.2 Build-Type: Simple License: BSD3 License-File: LICENSE-Copyright: 2012, 2013 Gabriel Gonzalez+Copyright: 2012-2014 Gabriel Gonzalez Author: Gabriel Gonzalez Maintainer: Gabriel439@gmail.com Bug-Reports: https://github.com/Gabriel439/Haskell-Pipes-ByteString-Library/issues@@ -20,10 +20,10 @@ Build-Depends: base >= 4 && < 5 , bytestring >= 0.9.2.1 && < 0.11,- pipes >= 4.0 && < 4.1 ,- pipes-parse >= 2.0.0 && < 2.1 ,+ pipes >= 4.0 && < 4.2 ,+ pipes-group >= 1.0.0 && < 1.1 ,+ pipes-parse >= 3.0.0 && < 3.1 ,+ profunctors >= 3.1.1 && < 4.1 , transformers >= 0.2.0.0 && < 0.4- Exposed-Modules:- Pipes.ByteString,- Pipes.ByteString.Parse+ Exposed-Modules: Pipes.ByteString GHC-Options: -O2 -Wall
src/Pipes/ByteString.hs view
@@ -22,11 +22,11 @@ > runEffect $ P.fromHandle hIn >-> P.toHandle hOut You can stream to and from 'stdin' and 'stdout' using the predefined 'stdin'- and 'stdout' proxies, like in the following \"echo\" program:+ and 'stdout' pipes, like in the following \"echo\" program: > main = runEffect $ P.stdin >-> P.stdout - You can also translate pure lazy 'BL.ByteString's to and from proxies:+ You can also translate pure lazy 'BL.ByteString's to and from pipes: > import qualified Data.ByteString.Lazy.Char8 as BL >@@ -37,13 +37,12 @@ example, to stream only the first three lines of 'stdin' to 'stdout' you would write: +> import Lens.Family (over) > import Pipes > import qualified Pipes.ByteString as PB-> import qualified Pipes.Parse as PP+> import Pipes.Parse (takes) >-> main = runEffect $ takeLines 3 PB.stdin >-> PB.stdout-> where-> takeLines n = PB.unlines . PP.takeFree n . PB.lines+> main = runEffect $ over PB.lines (takes 3) PB.stdin >-> PB.stdout The above program will never bring more than one chunk (~ 32 KB) into memory, no matter how long the lines are.@@ -62,7 +61,6 @@ , fromHandle , hGetSome , hGet- , pack -- * Servers , hGetSomeN@@ -83,12 +81,11 @@ , elemIndices , findIndices , scan- , unpack -- * Folds , toLazy , toLazyM- , fold+ , foldBytes , head , last , null@@ -105,45 +102,50 @@ , findIndex , count - -- * Splitters+ -- * Parsing+ -- $parse+ , nextByte+ , drawByte+ , unDrawByte+ , peekByte+ , isEndOfBytes++ -- * Parsing Lenses , splitAt- , chunksOf , span , break- , splitWith- , split , groupBy , group- , lines- , words+ , word+ , line - -- * Transformations+ -- * Transforming Byte Streams , intersperse-- -- * Joiners- , intercalate- , unlines- , unwords+ , pack+ , chunksOf' - -- * Low-level Parsers- -- $parse- , nextByte- , drawByte- , unDrawByte- , peekByte- , isEndOfBytes- , takeWhile'+ -- * FreeT Splitters+ , chunksOf+ , splitsWith+ , splits+ , groupsBy+ , groups+ , lines+ , words -- * Re-exports -- $reexports , module Data.ByteString+ , module Data.Profunctor , module Data.Word+ , module Pipes.Group , module Pipes.Parse ) where +import Control.Applicative ((<*)) import Control.Exception (throwIO, try)-import Control.Monad (liftM)-import Control.Monad.Trans.State.Strict (StateT)+import Control.Monad (liftM, join)+import Control.Monad.Trans.State.Strict (modify) import qualified Data.ByteString as BS import Data.ByteString (ByteString) import Data.ByteString.Internal (isSpaceWord8)@@ -151,26 +153,26 @@ import Data.ByteString.Lazy.Internal (foldrChunks, defaultChunkSize) import Data.ByteString.Unsafe (unsafeTake, unsafeDrop) import Data.Char (ord)+import Data.Functor.Constant (Constant(Constant, getConstant)) import Data.Functor.Identity (Identity)+import Data.Profunctor (Profunctor)+import qualified Data.Profunctor import qualified Data.List as List import Data.Word (Word8) import Foreign.C.Error (Errno(Errno), ePIPE) import qualified GHC.IO.Exception as G import Pipes-import qualified Pipes.ByteString.Parse as PBP-import Pipes.ByteString.Parse (- nextByte, drawByte, unDrawByte, peekByte, isEndOfBytes ) import Pipes.Core (respond, Server')-import Pipes.Lift (evalStateP)+import qualified Pipes.Group as PG+import Pipes.Group (concats, intercalates, FreeT) import qualified Pipes.Parse as PP-import Pipes.Parse (input, concat, FreeT, isEndOfInput)+import Pipes.Parse (Parser) import qualified Pipes.Prelude as P import qualified System.IO as IO import Prelude hiding ( all , any , break- , concat , concatMap , drop , dropWhile@@ -189,13 +191,11 @@ , splitAt , take , takeWhile- , unlines- , unwords , words ) -- | Convert a lazy 'BL.ByteString' into a 'Producer' of strict 'ByteString's-fromLazy :: (Monad m) => BL.ByteString -> Producer' ByteString m ()+fromLazy :: Monad m => BL.ByteString -> Producer' ByteString m () fromLazy bs = foldrChunks (\e a -> yield e >> a) (return ()) bs {-# INLINABLE fromLazy #-} @@ -244,18 +244,8 @@ go {-# INLINABLE hGet #-} --- | Convert a 'Word8' producer into a byte stream using a default chunk size-pack :: Monad m => Producer Word8 m () -> Producer ByteString m ()-pack p = evalStateP p go where- go = do- eof <- lift isEndOfInput- if eof- then return ()- else do- bytes <- lift $ P.toListM (P.take defaultChunkSize <-< input)- yield $ BS.pack bytes- go-{-# INLINABLE pack #-}+(^.) :: a -> ((b -> Constant b b) -> (a -> Constant b a)) -> b+a ^. lens = getConstant (lens Constant a) {-| Like 'hGetSome', except you can vary the maximum chunk size for each request -}@@ -287,9 +277,6 @@ {-| Stream bytes to 'stdout' Unlike 'toHandle', 'stdout' gracefully terminates on a broken output pipe.-- Note: For best performance, use @(for source (liftIO . putStr))@ instead of- @(source >-> stdout)@. -} stdout :: MonadIO m => Consumer' ByteString m () stdout = go@@ -308,8 +295,7 @@ {-| Convert a byte stream into a 'Handle' - Note: For best performance, use @(for source (liftIO . hPutStr handle))@- instead of @(source >-> toHandle handle)@.+> p >-> toHandle handle = for p (liftIO . hPutStr handle) -} toHandle :: MonadIO m => IO.Handle -> Consumer' ByteString m r toHandle h = for cat (liftIO . BS.hPut h)@@ -320,13 +306,13 @@ #-} -- | Apply a transformation to each 'Word8' in the stream-map :: (Monad m) => (Word8 -> Word8) -> Pipe ByteString ByteString m r+map :: Monad m => (Word8 -> Word8) -> Pipe ByteString ByteString m r map f = P.map (BS.map f) {-# INLINABLE map #-} -- | Map a function over the byte stream and concatenate the results concatMap- :: (Monad m) => (Word8 -> ByteString) -> Pipe ByteString ByteString m r+ :: Monad m => (Word8 -> ByteString) -> Pipe ByteString ByteString m r concatMap f = P.map (BS.concatMap f) {-# INLINABLE concatMap #-} @@ -361,7 +347,7 @@ {-# INLINABLE drop #-} -- | Take bytes until they fail the predicate-takeWhile :: (Monad m) => (Word8 -> Bool) -> Pipe ByteString ByteString m ()+takeWhile :: Monad m => (Word8 -> Bool) -> Pipe ByteString ByteString m () takeWhile predicate = go where go = do@@ -375,7 +361,7 @@ {-# INLINABLE takeWhile #-} -- | Drop bytes until they fail the predicate-dropWhile :: (Monad m) => (Word8 -> Bool) -> Pipe ByteString ByteString m r+dropWhile :: Monad m => (Word8 -> Bool) -> Pipe ByteString ByteString m r dropWhile predicate = go where go = do bs <- await@@ -387,7 +373,7 @@ {-# INLINABLE dropWhile #-} -- | Only allows 'Word8's to pass if they satisfy the predicate-filter :: (Monad m) => (Word8 -> Bool) -> Pipe ByteString ByteString m r+filter :: Monad m => (Word8 -> Bool) -> Pipe ByteString ByteString m r filter predicate = P.map (BS.filter predicate) {-# INLINABLE filter #-} @@ -408,7 +394,7 @@ -- | Strict left scan over the bytes scan- :: (Monad m)+ :: Monad m => (Word8 -> Word8 -> Word8) -> Word8 -> Pipe ByteString ByteString m r scan step begin = go begin where@@ -420,16 +406,6 @@ go w8' {-# INLINABLE scan #-} --- | Unpack the bytes-unpack :: Monad m => Pipe ByteString Word8 m ()-unpack = for cat (mapM_ yield . BS.unpack)-{-# INLINABLE unpack #-}--{-# RULES- "p >-> unpack" forall p .- p >-> unpack = for p (\bs -> mapM_ yield (BS.unpack bs))- #-}- {-| Fold a pure 'Producer' of strict 'ByteString's into a lazy 'BL.ByteString' -}@@ -444,19 +420,23 @@ simple testing purposes. Idiomatic @pipes@ style consumes the chunks immediately as they are generated instead of loading them all into memory. -}-toLazyM :: (Monad m) => Producer ByteString m () -> m BL.ByteString+toLazyM :: Monad m => Producer ByteString m () -> m BL.ByteString toLazyM = liftM BL.fromChunks . P.toListM {-# INLINABLE toLazyM #-} --- | Reduce the stream of bytes using a strict left fold-fold+{-| Reduce the stream of bytes using a strict left fold++ Note: It's more efficient to use folds from @Control.Foldl.ByteString@ in+ conjunction with @Pipes.Prelude.'Pipes.Prelude.fold'@ when possible+-}+foldBytes :: Monad m => (x -> Word8 -> x) -> x -> (x -> r) -> Producer ByteString m () -> m r-fold step begin done = P.fold (\x bs -> BS.foldl' step x bs) begin done-{-# INLINABLE fold #-}+foldBytes step begin done = P.fold (\x bs -> BS.foldl' step x bs) begin done+{-# INLINABLE foldBytes #-} -- | Retrieve the first 'Word8'-head :: (Monad m) => Producer ByteString m () -> m (Maybe Word8)+head :: Monad m => Producer ByteString m () -> m (Maybe Word8) head = go where go p = do@@ -467,7 +447,7 @@ {-# INLINABLE head #-} -- | Retrieve the last 'Word8'-last :: (Monad m) => Producer ByteString m () -> m (Maybe Word8)+last :: Monad m => Producer ByteString m () -> m (Maybe Word8) last = go Nothing where go r p = do@@ -481,7 +461,7 @@ {-# INLINABLE last #-} -- | Determine if the stream is empty-null :: (Monad m) => Producer ByteString m () -> m Bool+null :: Monad m => Producer ByteString m () -> m Bool null = P.all BS.null {-# INLINABLE null #-} @@ -491,17 +471,17 @@ {-# INLINABLE length #-} -- | Fold that returns whether 'M.Any' received 'Word8's satisfy the predicate-any :: (Monad m) => (Word8 -> Bool) -> Producer ByteString m () -> m Bool+any :: Monad m => (Word8 -> Bool) -> Producer ByteString m () -> m Bool any predicate = P.any (BS.any predicate) {-# INLINABLE any #-} -- | Fold that returns whether 'M.All' received 'Word8's satisfy the predicate-all :: (Monad m) => (Word8 -> Bool) -> Producer ByteString m () -> m Bool+all :: Monad m => (Word8 -> Bool) -> Producer ByteString m () -> m Bool all predicate = P.all (BS.all predicate) {-# INLINABLE all #-} -- | Return the maximum 'Word8' within a byte stream-maximum :: (Monad m) => Producer ByteString m () -> m (Maybe Word8)+maximum :: Monad m => Producer ByteString m () -> m (Maybe Word8) maximum = P.fold step Nothing id where step mw8 bs =@@ -513,7 +493,7 @@ {-# INLINABLE maximum #-} -- | Return the minimum 'Word8' within a byte stream-minimum :: (Monad m) => Producer ByteString m () -> m (Maybe Word8)+minimum :: Monad m => Producer ByteString m () -> m (Maybe Word8) minimum = P.fold step Nothing id where step mw8 bs =@@ -525,20 +505,20 @@ {-# INLINABLE minimum #-} -- | Determine whether any element in the byte stream matches the given 'Word8'-elem :: (Monad m) => Word8 -> Producer ByteString m () -> m Bool+elem :: Monad m => Word8 -> Producer ByteString m () -> m Bool elem w8 = P.any (BS.elem w8) {-# INLINABLE elem #-} {-| Determine whether all elements in the byte stream do not match the given 'Word8' -}-notElem :: (Monad m) => Word8 -> Producer ByteString m () -> m Bool+notElem :: Monad m => Word8 -> Producer ByteString m () -> m Bool notElem w8 = P.all (BS.notElem w8) {-# INLINABLE notElem #-} -- | Find the first element in the stream that matches the predicate find- :: (Monad m)+ :: Monad m => (Word8 -> Bool) -> Producer ByteString m () -> m (Maybe Word8) find predicate p = head (p >-> filter predicate) {-# INLINABLE find #-}@@ -568,55 +548,126 @@ count w8 p = P.fold (+) 0 id (p >-> P.map (fromIntegral . BS.count w8)) {-# INLINABLE count #-} --- | Splits a 'Producer' after the given number of bytes-splitAt- :: (Monad m, Integral n)- => n- -> Producer ByteString m r- -> Producer' ByteString m (Producer ByteString m r)-splitAt = go+{-| Consume the first byte from a byte stream++ 'next' either fails with a 'Left' if the 'Producer' has no more bytes or+ succeeds with a 'Right' providing the next byte and the remainder of the+ 'Producer'.+-}+nextByte+ :: Monad m+ => Producer ByteString m r+ -> m (Either r (Word8, Producer ByteString m r))+nextByte = go where- go 0 p = return p- go n p = do- x <- lift (next p)+ go p = do+ x <- next p case x of- Left r -> return (return r)- Right (bs, p') -> do- let len = fromIntegral (BS.length bs)- if (len <= n)- then do- yield bs- go (n - len) p'- else do- let (prefix, suffix) = BS.splitAt (fromIntegral n) bs- yield prefix- return (yield suffix >> p')-{-# INLINABLE splitAt #-}+ Left r -> return (Left r)+ Right (bs, p') -> case (BS.uncons bs) of+ Nothing -> go p'+ Just (w8, bs') -> return (Right (w8, yield bs' >> p'))+{-# INLINABLE nextByte #-} --- | Split a byte stream into 'FreeT'-delimited byte streams of fixed size-chunksOf+{-| Draw one 'Word8' from the underlying 'Producer', returning 'Nothing' if the+ 'Producer' is empty+-}+drawByte :: Monad m => Parser ByteString m (Maybe Word8)+drawByte = do+ x <- PP.draw+ case x of+ Nothing -> return Nothing+ Just bs -> case (BS.uncons bs) of+ Nothing -> drawByte+ Just (w8, bs') -> do+ PP.unDraw bs'+ return (Just w8)+{-# INLINABLE drawByte #-}++-- | Push back a 'Word8' onto the underlying 'Producer'+unDrawByte :: Monad m => Word8 -> Parser ByteString m ()+unDrawByte w8 = modify (yield (BS.singleton w8) >>)+{-# INLINABLE unDrawByte #-}++{-| 'peekByte' checks the first 'Word8' in the stream, but uses 'unDrawByte' to+ push the 'Word8' back++> peekByte = do+> x <- drawByte+> case x of+> Nothing -> return ()+> Just w8 -> unDrawByte w8+> return x+-}+peekByte :: Monad m => Parser ByteString m (Maybe Word8)+peekByte = do+ x <- drawByte+ case x of+ Nothing -> return ()+ Just w8 -> unDrawByte w8+ return x+{-# INLINABLE peekByte #-}++{-| Check if the underlying 'Producer' has no more bytes++ Note that this will skip over empty 'ByteString' chunks, unlike+ 'Pipes.Parse.isEndOfInput' from @pipes-parse@.++> isEndOfBytes = liftM isNothing peekByte+-}+isEndOfBytes :: Monad m => Parser ByteString m Bool+isEndOfBytes = do+ x <- peekByte+ return (case x of+ Nothing -> True+ Just _ -> False )+{-# INLINABLE isEndOfBytes #-}++type Lens' a b = forall f . Functor f => (b -> f b) -> (a -> f a)++type Iso' a b = forall f p . (Functor f, Profunctor p) => p b (f b) -> p a (f a)++-- | Improper lens that splits a 'Producer' after the given number of bytes+splitAt :: (Monad m, Integral n)- => n -> Producer ByteString m r -> FreeT (Producer ByteString m) m r-chunksOf n = go+ => n+ -> Lens' (Producer ByteString m x)+ (Producer ByteString m (Producer ByteString m x))+splitAt n0 k p0 = fmap join (k (go n0 p0)) where- go p = PP.FreeT $ do- x <- next p- return $ case x of- Left r -> PP.Pure r- Right (bs, p') -> PP.Free $ do- p'' <- splitAt n (yield bs >> p')- return (go p'')-{-# INLINABLE chunksOf #-}+ -- go :: (Monad m, Integral n)+ -- => n+ -- -> Producer ByteString m r+ -- -> Producer' ByteString m (Producer ByteString m r)+ go n p =+ if (n <= 0)+ then return p+ else do+ x <- lift (next p)+ case x of+ Left r -> return (return r)+ Right (bs, p') -> do+ let len = fromIntegral (BS.length bs)+ if (len <= n)+ then do+ yield bs+ go (n - len) p'+ else do+ let (prefix, suffix) =+ BS.splitAt (fromIntegral n) bs+ yield prefix+ return (yield suffix >> p')+{-# INLINABLE splitAt #-} -{-| Split a byte stream in two, where the first byte stream is the longest- consecutive group of bytes that satisfy the predicate+{-| Improper lens that splits after the longest consecutive group of bytes that+ satisfy the given predicate -} span- :: (Monad m)+ :: Monad m => (Word8 -> Bool)- -> Producer ByteString m r- -> Producer' ByteString m (Producer ByteString m r)-span predicate = go+ -> Lens' (Producer ByteString m x)+ (Producer ByteString m (Producer ByteString m x))+span predicate k p0 = fmap join (k (go p0)) where go p = do x <- lift (next p)@@ -633,129 +684,93 @@ return (yield suffix >> p') {-# INLINABLE span #-} -{-| Split a byte stream in two, where the first byte stream is the longest- consecutive group of bytes that don't satisfy the predicate+{-| Improper lens that splits after the longest consecutive group of bytes that+ fail the given predicate -} break- :: (Monad m)+ :: Monad m => (Word8 -> Bool)- -> Producer ByteString m r- -> Producer ByteString m (Producer ByteString m r)+ -> Lens' (Producer ByteString m x)+ (Producer ByteString m (Producer ByteString m x)) break predicate = span (not . predicate) {-# INLINABLE break #-} -{-| Split a byte stream into sub-streams delimited by bytes that satisfy the- predicate--}-splitWith- :: (Monad m)- => (Word8 -> Bool)- -> Producer ByteString m r- -> PP.FreeT (Producer ByteString m) m r-splitWith predicate p0 = PP.FreeT (go0 p0)- where- go0 p = do- x <- next p- case x of- Left r -> return (PP.Pure r)- Right (bs, p') ->- if (BS.null bs)- then go0 p'- else go1 (yield bs >> p')- go1 p = return $ PP.Free $ do- p' <- span (not . predicate) p- return $ PP.FreeT $ do- x <- nextByte p'- case x of- Left r -> return (PP.Pure r)- Right (_, p'') -> go1 p''-{-# INLINABLE splitWith #-}---- | Split a byte stream using the given 'Word8' as the delimiter-split :: (Monad m)- => Word8- -> Producer ByteString m r- -> FreeT (Producer ByteString m) m r-split w8 = splitWith (w8 ==)-{-# INLINABLE split #-}--{-| Group a byte stream into 'FreeT'-delimited byte streams using the supplied- equality predicate+{-| Improper lens that splits after the first group of matching bytes, as+ defined by the given equality predicate -} groupBy- :: (Monad m)+ :: Monad m => (Word8 -> Word8 -> Bool)- -> Producer ByteString m r- -> FreeT (Producer ByteString m) m r-groupBy equal p0 = PP.FreeT (go p0)+ -> Lens' (Producer ByteString m r)+ (Producer ByteString m (Producer ByteString m r))+groupBy equals k p0 = fmap join (k (_groupBy p0)) where- go p = do- x <- next p+ -- _groupBy+ -- :: Monad m+ -- => Producer ByteString m r+ -- -> Producer ByteString m (Producer ByteString m r)+ _groupBy p = do+ x <- lift (next p) case x of- Left r -> return (PP.Pure r)+ Left r -> return (return r) Right (bs, p') -> case (BS.uncons bs) of- Nothing -> go p'- Just (w8, _) -> do- return $ PP.Free $ do- p'' <- span (equal w8) (yield bs >> p')- return $ PP.FreeT (go p'')+ Nothing -> _groupBy p'+ Just (w8, _) -> (yield bs >> p')^.span (equals w8) {-# INLINABLE groupBy #-} --- | Group a byte stream into 'FreeT'-delimited byte streams of identical bytes+-- | Like 'groupBy', where the equality predicate is ('==') group- :: (Monad m) => Producer ByteString m r -> FreeT (Producer ByteString m) m r+ :: Monad m+ => Lens' (Producer ByteString m r)+ (Producer ByteString m (Producer ByteString m r)) group = groupBy (==) {-# INLINABLE group #-} -{-| Split a byte stream into 'FreeT'-delimited lines+{-| Improper lens that splits a 'Producer' after the first word + Unlike 'words', this does not drop leading whitespace+ Note: This function is purely for demonstration purposes since it assumes a particular encoding. You should prefer the 'Data.Text.Text' equivalent of this function from the upcoming @pipes-text@ library. -}-lines- :: (Monad m) => Producer ByteString m r -> FreeT (Producer ByteString m) m r-lines p0 = PP.FreeT (go0 p0)+word+ :: Monad m+ => Lens' (Producer ByteString m r)+ (Producer ByteString m (Producer ByteString m r))+word k p0 = fmap join (k (to p0)) where- go0 p = do- x <- next p- case x of- Left r -> return (PP.Pure r)- Right (bs, p') ->- if (BS.null bs)- then go0 p'- else return $ PP.Free $ go1 (yield bs >> p')- go1 p = do- p' <- break (fromIntegral (ord '\n') ==) p- return $ PP.FreeT $ do- x <- nextByte p'- case x of- Left r -> return (PP.Pure r)- Right (_, p'') -> go0 p''-{-# INLINABLE lines #-}+ -- to+ -- :: Monad m+ -- => Producer ByteString m r+ -- -> Producer ByteString m (Producer ByteString m r)+ to p = do+ p' <- p^.span isSpaceWord8+ p'^.break isSpaceWord8+{-# INLINABLE word #-} -{-| Split a byte stream into 'FreeT'-delimited words+nl :: Word8+nl = fromIntegral (ord '\n') +{-| Improper lens that splits a 'Producer' after the first line++ Unlike 'lines', this does not consume the newline marker, which is stored+ within the inner 'Producer'+ Note: This function is purely for demonstration purposes since it assumes a particular encoding. You should prefer the 'Data.Text.Text' equivalent of this function from the upcoming @pipes-text@ library. -}-words- :: (Monad m) => Producer ByteString m r -> FreeT (Producer ByteString m) m r-words = go- where- go p = PP.FreeT $ do- x <- next (p >-> dropWhile isSpaceWord8)- return $ case x of- Left r -> PP.Pure r- Right (bs, p') -> PP.Free $ do- p'' <- break isSpaceWord8 (yield bs >> p')- return (go p'')-{-# INLINABLE words #-}+line+ :: Monad m+ => Lens' (Producer ByteString m r)+ (Producer ByteString m (Producer ByteString m r))+line = break (== nl)+{-# INLINABLE line #-} -- | Intersperse a 'Word8' in between the bytes of the byte stream intersperse- :: (Monad m) => Word8 -> Producer ByteString m r -> Producer ByteString m r+ :: Monad m => Word8 -> Producer ByteString m r -> Producer ByteString m r intersperse w8 = go0 where go0 p = do@@ -775,88 +790,207 @@ go1 p' {-# INLINABLE intersperse #-} -{-| 'intercalate' concatenates the 'FreeT'-delimited byte streams after- interspersing a byte stream in between them+-- | Improper isomorphism between a 'Producer' of 'ByteString's and 'Word8's+pack :: Monad m => Iso' (Producer Word8 m x) (Producer ByteString m x)+pack = Data.Profunctor.dimap to (fmap from)+ where+ -- to :: Monad m => Producer Word8 m x -> Producer ByteString m x+ to p = PG.folds step id done (p^.PG.chunksOf defaultChunkSize)++ step diffAs w8 = diffAs . (w8:)++ done diffAs = BS.pack (diffAs [])++ -- from :: Monad m => Producer ByteString m x -> Producer Word8 m x+ from p = for p (each . BS.unpack)+{-# INLINABLE pack #-}++{-| Group byte stream chunks into chunks of fixed length++ Note: This is the /only/ function in this API that concatenates+ 'ByteString' chunks, which requires allocating new `ByteString`s -}-intercalate- :: (Monad m)- => Producer ByteString m ()- -> FreeT (Producer ByteString m) m r- -> Producer ByteString m r-intercalate p0 = go0+chunksOf'+ :: (Monad m, Integral n)+ => n -> Producer ByteString m r -> Producer ByteString m r+chunksOf' n p =+ PG.folds+ (\diffBs bs -> diffBs . (bs:))+ id+ (\diffBs -> BS.concat (diffBs []))+ (p ^. chunksOf n)+{-# INLINABLE chunksOf' #-}++-- | Split a byte stream into 'FreeT'-delimited byte streams of fixed size+chunksOf+ :: (Monad m, Integral n)+ => n+ -> Lens' (Producer ByteString m x) (FreeT (Producer ByteString m) m x)+chunksOf n k p0 = fmap concats (k (go p0)) where- go0 f = do- x <- lift (PP.runFreeT f)- case x of- PP.Pure r -> return r- PP.Free p -> do- f' <- p- go1 f'- go1 f = do- x <- lift (PP.runFreeT f)+ go p = PG.FreeT $ do+ x <- next p+ return $ case x of+ Left r -> PG.Pure r+ Right (bs, p') -> PG.Free $ do+ p'' <- (yield bs >> p')^.splitAt n+ return (go p'')+{-# INLINABLE chunksOf #-}++{-| Split a byte stream into groups separated by bytes that satisfy the+ predicate+-}+splitsWith+ :: Monad m+ => (Word8 -> Bool)+ -> Producer ByteString m x -> FreeT (Producer ByteString m) m x+splitsWith predicate p0 = PG.FreeT (go0 p0)+ where+ go0 p = do+ x <- next p case x of- PP.Pure r -> return r- PP.Free p -> do- p0- f' <- p- go1 f'-{-# INLINABLE intercalate #-}+ Left r -> return (PG.Pure r)+ Right (bs, p') ->+ if (BS.null bs)+ then go0 p'+ else go1 (yield bs >> p')+ go1 p = return $ PG.Free $ do+ p' <- p^.break predicate+ return $ PG.FreeT $ do+ x <- nextByte p'+ case x of+ Left r -> return (PG.Pure r)+ Right (_, p'') -> go1 p''+{-# INLINABLE splitsWith #-} -{-| Join 'FreeT'-delimited lines into a byte stream+-- | Split a byte stream into groups separated by the given byte+splits+ :: Monad m+ => Word8+ -> Lens' (Producer ByteString m x) (FreeT (Producer ByteString m) m x)+splits w8 k p =+ fmap (PG.intercalates (yield (BS.singleton w8))) (k (splitsWith (w8 ==) p))+{-# INLINABLE splits #-} +{-| Isomorphism between a byte stream and groups of identical bytes using the+ supplied equality predicate+-}+groupsBy+ :: Monad m+ => (Word8 -> Word8 -> Bool)+ -> Lens' (Producer ByteString m x) (FreeT (Producer ByteString m) m x)+groupsBy equals k p0 = fmap concats (k (_groupsBy p0))+ where+ -- _groupsBy+ -- :: Monad m+ -- => (Word8 -> Word8 -> Bool)+ -- -> Producer ByteString m x+ -- -> FreeT (Producer ByteString m) m x+ _groupsBy p0' = PG.FreeT (go p0')+ where+ go p = do+ x <- next p+ case x of+ Left r -> return (PG.Pure r)+ Right (bs, p') -> case (BS.uncons bs) of+ Nothing -> go p'+ Just (w8, _) -> do+ return $ PG.Free $ do+ p'' <- (yield bs >> p')^.span (equals w8)+ return $ PG.FreeT (go p'')+{-# INLINABLE groupsBy #-}++-- | Like 'groupsBy', where the equality predicate is ('==')+groups+ :: Monad m+ => Lens' (Producer ByteString m x) (FreeT (Producer ByteString m) m x)+groups = groupsBy (==)+{-# INLINABLE groups #-}++{-| Improper isomorphism between a bytestream and its lines+ Note: This function is purely for demonstration purposes since it assumes a particular encoding. You should prefer the 'Data.Text.Text' equivalent of this function from the upcoming @pipes-text@ library. -}-unlines- :: (Monad m) => FreeT (Producer ByteString m) m r -> Producer ByteString m r-unlines = go+lines+ :: Monad m+ => Iso' (Producer ByteString m x) (FreeT (Producer ByteString m) m x)+lines = Data.Profunctor.dimap _lines (fmap _unlines) where- go f = do- x <- lift (PP.runFreeT f)- case x of- PP.Pure r -> return r- PP.Free p -> do- f' <- p- yield $ BS.singleton $ fromIntegral (ord '\n')- go f'-{-# INLINABLE unlines #-}+ -- _lines+ -- :: Monad m+ -- => Producer ByteString m x -> FreeT (Producer ByteString m) m x+ _lines p0 = PG.FreeT (go0 p0)+ where+ go0 p = do+ x <- next p+ case x of+ Left r -> return (PG.Pure r)+ Right (bs, p') ->+ if (BS.null bs)+ then go0 p'+ else return $ PG.Free $ go1 (yield bs >> p')+ go1 p = do+ p' <- p^.line+ return $ PG.FreeT $ do+ x <- nextByte p'+ case x of+ Left r -> return (PG.Pure r)+ Right (_, p'') -> go0 p'' -{-| Join 'FreeT'-delimited words into a byte stream+ -- _unlines+ -- :: Monad m+ -- => FreeT (Producer ByteString m) m x -> Producer ByteString m x+ _unlines = concats . PG.maps addNewline + -- addNewline+ -- :: Monad m => Producer ByteString m r -> Producer ByteString m r+ addNewline p = p <* yield (BS.singleton nl)+{-# INLINABLE lines #-}++{-| Improper isomorphism between a bytestream and its words+ Note: This function is purely for demonstration purposes since it assumes a particular encoding. You should prefer the 'Data.Text.Text' equivalent of this function from the upcoming @pipes-text@ library. -}-unwords- :: (Monad m) => FreeT (Producer ByteString m) m r -> Producer ByteString m r-unwords = intercalate (yield $ BS.singleton $ fromIntegral $ ord ' ')-{-# INLINABLE unwords #-}+words+ :: Monad m+ => Iso' (Producer ByteString m x) (FreeT (Producer ByteString m) m x)+words = Data.Profunctor.dimap _words (fmap _unwords)+ where+ -- _words+ -- :: Monad m+ -- => Producer ByteString m x -> FreeT (Producer ByteString m) m x+ _words p = PG.FreeT $ do+ x <- next (p >-> dropWhile isSpaceWord8)+ return $ case x of+ Left r -> PG.Pure r+ Right (bs, p') -> PG.Free $ do+ p'' <- (yield bs >> p')^.break isSpaceWord8+ return (_words p'') + -- _unwords+ -- :: Monad m+ -- => FreeT (Producer ByteString m) m x -> Producer ByteString m x+ _unwords = PG.intercalates (yield $ BS.singleton $ fromIntegral $ ord ' ')+{-# INLINABLE words #-}+ {- $parse The following parsing utilities are single-byte analogs of the ones found in @pipes-parse@. -} -{-| Take bytes until they fail the predicate-- Unlike 'takeWhile', this 'PP.unDraw's unused bytes--}-takeWhile'- :: (Monad m)- => (Word8 -> Bool)- -> Pipe ByteString ByteString (StateT (Producer ByteString m r) m) ()-takeWhile' = PBP.takeWhile-{-# INLINABLE takeWhile' #-}-{-# DEPRECATED takeWhile' "Use Pipes.ByteString.Parse.takeWhile instead" #-}- {- $reexports- "Pipes.ByteString.Parse" re-exports 'nextByte', 'drawByte', 'unDrawByte',- 'peekByte', and 'isEndOfBytes'.- @Data.ByteString@ re-exports the 'ByteString' type. + @Data.Profunctor@ re-exports the 'Profunctor' type.+ @Data.Word@ re-exports the 'Word8' type. - @Pipes.Parse@ re-exports 'input', 'concat', and 'FreeT' (the type).+ @Pipes.Parse@ re-exports 'Parser'.++ @Pipes.Group@ re-exports 'concats', 'intercalates', and 'FreeT'+ (the type). -}
− src/Pipes/ByteString/Parse.hs
@@ -1,142 +0,0 @@--- | Parsing utilities for bytestrings, in the style of @pipes-parse@--module Pipes.ByteString.Parse (- -- * Parsers- nextByte- , drawByte- , unDrawByte- , peekByte- , isEndOfBytes- , take- , takeWhile- ) where--import Control.Monad.Trans.State.Strict (StateT, modify)-import qualified Data.ByteString as BS-import Data.ByteString (ByteString)-import Data.ByteString.Unsafe (unsafeTake, unsafeDrop)-import Data.Word (Word8)-import Pipes-import qualified Pipes.Parse as PP--import Prelude hiding (take, takeWhile)--{-| Consume the first byte from a byte stream-- 'next' either fails with a 'Left' if the 'Producer' has no more bytes or- succeeds with a 'Right' providing the next byte and the remainder of the- 'Producer'.--}-nextByte- :: (Monad m)- => Producer ByteString m r- -> m (Either r (Word8, Producer ByteString m r))-nextByte = go- where- go p = do- x <- next p- case x of- Left r -> return (Left r)- Right (bs, p') -> case (BS.uncons bs) of- Nothing -> go p'- Just (w8, bs') -> return (Right (w8, yield bs' >> p'))-{-# INLINABLE nextByte #-}--{-| Draw one 'Word8' from the underlying 'Producer', returning 'Left' if the- 'Producer' is empty--}-drawByte :: (Monad m) => StateT (Producer ByteString m r) m (Either r Word8)-drawByte = do- x <- PP.draw- case x of- Left r -> return (Left r)- Right bs -> case (BS.uncons bs) of- Nothing -> drawByte- Just (w8, bs') -> do- PP.unDraw bs'- return (Right w8)-{-# INLINABLE drawByte #-}---- | Push back a 'Word8' onto the underlying 'Producer'-unDrawByte :: (Monad m) => Word8 -> StateT (Producer ByteString m r) m ()-unDrawByte w8 = modify (yield (BS.singleton w8) >>)-{-# INLINABLE unDrawByte #-}--{-| 'peekByte' checks the first 'Word8' in the stream, but uses 'unDrawByte' to- push the 'Word8' back--> peekByte = do-> x <- drawByte-> case x of-> Left _ -> return ()-> Right w8 -> unDrawByte w8-> return x--}-peekByte :: (Monad m) => StateT (Producer ByteString m r) m (Either r Word8)-peekByte = do- x <- drawByte- case x of- Left _ -> return ()- Right w8 -> unDrawByte w8- return x-{-# INLINABLE peekByte #-}--{-| Check if the underlying 'Producer' has no more bytes-- Note that this will skip over empty 'ByteString' chunks, unlike- 'PP.isEndOfInput' from @pipes-parse@.--> isEndOfBytes = liftM isLeft peekByte--}-isEndOfBytes :: (Monad m) => StateT (Producer ByteString m r) m Bool-isEndOfBytes = do- x <- peekByte- return (case x of- Left _ -> True- Right _ -> False )-{-# INLINABLE isEndOfBytes #-}--{-| @(take n)@ only allows @n@ bytes to pass-- Unlike @Pipes.ByteString.'Pipes.ByteString.take'@, this 'PP.unDraw's unused- bytes--}-take :: (Monad m, Integral a) => a -> Pipe ByteString ByteString (StateT (Producer ByteString m r) m) ()-take n0 = go n0 where- go n- | n <= 0 = return ()- | otherwise = do- bs <- await- let len = fromIntegral (BS.length bs)- if (len > n)- then do- let n' = fromIntegral n- lift . PP.unDraw $ unsafeDrop n' bs- yield $ unsafeTake n' bs- else do- yield bs- go (n - len)-{-# INLINABLE take #-}--{-| Take bytes until they fail the predicate-- Unlike @Pipes.ByteString.'Pipes.ByteString.takeWhile'@, this 'PP.unDraw's- unused bytes--}-takeWhile- :: (Monad m)- => (Word8 -> Bool)- -> Pipe ByteString ByteString (StateT (Producer ByteString m r) m) ()-takeWhile predicate = go- where- go = do- bs <- await- let (prefix, suffix) = BS.span predicate bs- if (BS.null suffix)- then do- yield bs- go- else do- lift $ PP.unDraw suffix- yield prefix-{-# INLINABLE takeWhile #-}