pipes-bytestring 1.0.1 → 1.0.2
raw patch · 3 files changed
+310/−219 lines, 3 files
Files
- pipes-bytestring.cabal +4/−2
- src/Pipes/ByteString.hs +164/−217
- src/Pipes/ByteString/Parse.hs +142/−0
pipes-bytestring.cabal view
@@ -1,5 +1,5 @@ Name: pipes-bytestring-Version: 1.0.1+Version: 1.0.2 Cabal-Version: >=1.8.0.2 Build-Type: Simple License: BSD3@@ -23,5 +23,7 @@ pipes >= 4.0 && < 4.1 , pipes-parse >= 2.0.0 && < 2.1 , transformers >= 0.2.0.0 && < 0.4- Exposed-Modules: Pipes.ByteString+ Exposed-Modules:+ Pipes.ByteString,+ Pipes.ByteString.Parse GHC-Options: -O2 -Wall
src/Pipes/ByteString.hs view
@@ -1,5 +1,10 @@-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RankNTypes, CPP #-} +-- The rewrite rules require the Trustworthy annotation+#if __GLASGOW_HASKELL__ >= 702+{-# LANGUAGE Trustworthy #-}+#endif+ {-| This module provides @pipes@ utilities for \"byte streams\", which are streams of strict 'ByteString's chunks. Use byte streams to interact with both 'IO.Handle's and lazy 'ByteString's.@@ -52,91 +57,93 @@ module Pipes.ByteString ( -- * Producers- fromLazy,- stdin,- fromHandle,- hGetSome,- hGet,+ fromLazy+ , stdin+ , fromHandle+ , hGetSome+ , hGet+ , pack -- * Servers- hGetSomeN,- hGetN,+ , hGetSomeN+ , hGetN -- * Consumers- stdout,- toHandle,+ , stdout+ , toHandle -- * Pipes- map,- concatMap,- take,- drop,- takeWhile,- dropWhile,- filter,- elemIndices,- findIndices,- scan,+ , map+ , concatMap+ , take+ , drop+ , takeWhile+ , dropWhile+ , filter+ , elemIndices+ , findIndices+ , scan+ , unpack -- * Folds- toLazy,- toLazyM,- fold,- head,- last,- null,- length,- any,- all,- maximum,- minimum,- elem,- notElem,- find,- index,- elemIndex,- findIndex,- count,+ , toLazy+ , toLazyM+ , fold+ , head+ , last+ , null+ , length+ , any+ , all+ , maximum+ , minimum+ , elem+ , notElem+ , find+ , index+ , elemIndex+ , findIndex+ , count -- * Splitters- splitAt,- chunksOf,- span,- break,- splitWith,- split,- groupBy,- group,- lines,- words,+ , splitAt+ , chunksOf+ , span+ , break+ , splitWith+ , split+ , groupBy+ , group+ , lines+ , words -- * Transformations- intersperse,+ , intersperse -- * Joiners- intercalate,- unlines,- unwords,+ , intercalate+ , unlines+ , unwords -- * Low-level Parsers -- $parse- nextByte,- drawByte,- unDrawByte,- peekByte,- isEndOfBytes,- takeWhile',+ , nextByte+ , drawByte+ , unDrawByte+ , peekByte+ , isEndOfBytes+ , takeWhile' -- * Re-exports -- $reexports- module Data.ByteString,- module Data.Word,- module Pipes.Parse+ , module Data.ByteString+ , module Data.Word+ , module Pipes.Parse ) where import Control.Exception (throwIO, try) import Control.Monad (liftM)-import Control.Monad.Trans.State.Strict (StateT, modify)+import Control.Monad.Trans.State.Strict (StateT) import qualified Data.ByteString as BS import Data.ByteString (ByteString) import Data.ByteString.Internal (isSpaceWord8)@@ -150,37 +157,42 @@ 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.Parse as PP-import Pipes.Parse (input, concat, FreeT)+import Pipes.Parse (input, concat, FreeT, isEndOfInput) import qualified Pipes.Prelude as P import qualified System.IO as IO import Prelude hiding (- all,- any,- break,- concat,- concatMap,- drop,- dropWhile,- elem,- filter,- head,- last,- lines,- length,- map,- maximum,- minimum,- notElem,- null,- span,- splitAt,- take,- takeWhile,- unlines,- unwords,- words )+ all+ , any+ , break+ , concat+ , concatMap+ , drop+ , dropWhile+ , elem+ , filter+ , head+ , last+ , lines+ , length+ , map+ , maximum+ , minimum+ , notElem+ , null+ , span+ , 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 ()@@ -232,6 +244,19 @@ 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 #-}+ {-| Like 'hGetSome', except you can vary the maximum chunk size for each request -} hGetSomeN :: MonadIO m => IO.Handle -> Int -> Server' Int ByteString m ()@@ -290,6 +315,10 @@ toHandle h = for cat (liftIO . BS.hPut h) {-# INLINABLE toHandle #-} +{-# RULES "p >-> toHandle h" forall p h .+ p >-> toHandle h = for p (\bs -> liftIO (BS.hPut h bs))+ #-}+ -- | Apply a transformation to each 'Word8' in the stream map :: (Monad m) => (Word8 -> Word8) -> Pipe ByteString ByteString m r map f = P.map (BS.map f)@@ -316,7 +345,7 @@ go (n - len) {-# INLINABLE take #-} --- | @(dropD n)@ drops the first @n@ bytes+-- | @(drop n)@ drops the first @n@ bytes drop :: (Monad m, Integral a) => a -> Pipe ByteString ByteString m r drop n0 = go n0 where go n@@ -391,6 +420,16 @@ 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' -}@@ -422,9 +461,9 @@ where go p = do x <- nextByte p- case x of- Left _ -> return Nothing- Right (w8, _) -> return (Just w8)+ return $ case x of+ Left _ -> Nothing+ Right (w8, _) -> Just w8 {-# INLINABLE head #-} -- | Retrieve the last 'Word8'@@ -436,9 +475,7 @@ case x of Left () -> return r Right (bs, p') ->- if (BS.null bs)- then go r p'- else go (Just $ BS.last bs) p'+ go (if BS.null bs then r else (Just $ BS.last bs)) p' -- TODO: Change this to 'unsafeLast' when bytestring-0.10.2.0 -- becomes more widespread {-# INLINABLE last #-}@@ -560,15 +597,15 @@ chunksOf :: (Monad m, Integral n) => n -> Producer ByteString m r -> FreeT (Producer ByteString m) m r-chunksOf n p0 = PP.FreeT (go p0)+chunksOf n = go where- go p = do+ 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 $ PP.FreeT (go p'')+ return (go p'') {-# INLINABLE chunksOf #-} {-| Split a byte stream in two, where the first byte stream is the longest@@ -577,7 +614,7 @@ span :: (Monad m) => (Word8 -> Bool)- -> Producer ByteString m r+ -> Producer ByteString m r -> Producer' ByteString m (Producer ByteString m r) span predicate = go where@@ -602,7 +639,7 @@ break :: (Monad m) => (Word8 -> Bool)- -> Producer ByteString m r+ -> Producer ByteString m r -> Producer ByteString m (Producer ByteString m r) break predicate = span (not . predicate) {-# INLINABLE break #-}@@ -624,16 +661,14 @@ Right (bs, p') -> if (BS.null bs) then go0 p'- else return $ PP.Free $ do- p'' <- span (not . predicate) (yield bs >> p')- return $ PP.FreeT (go1 p'')- go1 p = do- x <- nextByte p- return $ case x of- Left r -> PP.Pure r- Right (_, p') -> PP.Free $ do- p'' <- span (not . predicate) p'- return $ PP.FreeT (go1 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@@ -641,7 +676,7 @@ => Word8 -> Producer ByteString m r -> FreeT (Producer ByteString m) m r-split w8 = splitWith (w8 /=)+split w8 = splitWith (w8 ==) {-# INLINABLE split #-} {-| Group a byte stream into 'FreeT'-delimited byte streams using the supplied@@ -692,12 +727,11 @@ else return $ PP.Free $ go1 (yield bs >> p') go1 p = do p' <- break (fromIntegral (ord '\n') ==) p- return $ PP.FreeT (go2 p')- go2 p = do- x <- nextByte p- return $ case x of- Left r -> PP.Pure r- Right (_, p') -> PP.Free (go1 p')+ return $ PP.FreeT $ do+ x <- nextByte p'+ case x of+ Left r -> return (PP.Pure r)+ Right (_, p'') -> go0 p'' {-# INLINABLE lines #-} {-| Split a byte stream into 'FreeT'-delimited words@@ -708,20 +742,15 @@ -} words :: (Monad m) => Producer ByteString m r -> FreeT (Producer ByteString m) m r-words p0 = removeEmpty (splitWith isSpaceWord8 p0)+words = go where- removeEmpty f = PP.FreeT $ do- x <- PP.runFreeT f- case x of- PP.Pure r -> return (PP.Pure r)- PP.Free p -> do- y <- next p- case y of- Left f' -> PP.runFreeT (removeEmpty f')- Right (bs, p') -> return $ PP.Free $ do- yield bs- f' <- p'- return (removeEmpty f')+ 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 #-} -- | Intersperse a 'Word8' in between the bytes of the byte stream@@ -809,81 +838,6 @@ in @pipes-parse@. -} -{-| 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 bytes until they fail the predicate Unlike 'takeWhile', this 'PP.unDraw's unused bytes@@ -892,21 +846,14 @@ :: (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+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.Word@ re-exports the 'Word8' type.
+ src/Pipes/ByteString/Parse.hs view
@@ -0,0 +1,142 @@+-- | 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 #-}