pipes-bytestring 2.0.1 → 2.1.0
raw patch · 2 files changed
+136/−118 lines, 2 filesdep −profunctorsdep ~transformers
Dependencies removed: profunctors
Dependency ranges changed: transformers
Files
- pipes-bytestring.cabal +2/−3
- src/Pipes/ByteString.hs +134/−115
pipes-bytestring.cabal view
@@ -1,5 +1,5 @@ Name: pipes-bytestring -Version: 2.0.1 +Version: 2.1.0 Cabal-Version: >=1.8.0.2 Build-Type: Simple License: BSD3 @@ -23,7 +23,6 @@ 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 + transformers >= 0.2.0.0 && < 0.5 Exposed-Modules: Pipes.ByteString GHC-Options: -O2 -Wall
src/Pipes/ByteString.hs view
@@ -56,6 +56,7 @@ , fromHandle , hGetSome , hGet + , hGetRange -- * Servers , hGetSomeN @@ -69,9 +70,7 @@ , map , concatMap , take - , drop , takeWhile - , dropWhile , filter , elemIndices , findIndices @@ -115,23 +114,27 @@ , line -- * Transforming Byte Streams + , drop + , dropWhile , intersperse , pack + , unpack , chunksOf' - -- * FreeT Splitters + -- * FreeT Transformations , chunksOf , splitsWith , splits , groupsBy , groups , lines + , unlines , words + , unwords -- * Re-exports -- $reexports , module Data.ByteString - , module Data.Profunctor , module Data.Word , module Pipes.Group , module Pipes.Parse @@ -146,12 +149,10 @@ import Data.ByteString.Internal (isSpaceWord8) import qualified Data.ByteString.Lazy as BL import Data.ByteString.Lazy.Internal (foldrChunks, defaultChunkSize) -import Data.ByteString.Unsafe (unsafeTake, unsafeDrop) +import Data.ByteString.Unsafe (unsafeTake) 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) @@ -186,6 +187,8 @@ , splitAt , take , takeWhile + , unlines + , unwords , words ) @@ -239,6 +242,19 @@ go {-# INLINABLE hGet #-} +{-| Like 'hGet' but with an extra parameter specifying an initial handle offset +-} +hGetRange + :: MonadIO m + => Int -- ^ Offset + -> Int -- ^ Size + -> IO.Handle + -> Producer' ByteString m () +hGetRange offset size h = do + liftIO $ IO.hSeek h IO.AbsoluteSeek (fromIntegral offset) + hGet size h +{-# INLINABLE hGetRange #-} + (^.) :: a -> ((b -> Constant b b) -> (a -> Constant b a)) -> b a ^. lens = getConstant (lens Constant a) @@ -306,13 +322,12 @@ {-# INLINE map #-} -- | Map a function over the byte stream and concatenate the results -concatMap - :: Monad m => (Word8 -> ByteString) -> Pipe ByteString ByteString m r +concatMap :: Monad m => (Word8 -> ByteString) -> Pipe ByteString ByteString m r concatMap f = P.map (BS.concatMap f) {-# INLINABLE concatMap #-} -- | @(take n)@ only allows @n@ bytes to pass -take :: (Monad m, Integral a) => a -> Pipe ByteString ByteString m () +take :: (Monad m, Integral n) => n -> Pipe ByteString ByteString m () take n0 = go n0 where go n | n <= 0 = return () @@ -326,21 +341,6 @@ go (n - len) {-# INLINABLE take #-} --- | @(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 - | n <= 0 = cat - | otherwise = do - bs <- await - let len = fromIntegral (BS.length bs) - if (len >= n) - then do - yield (unsafeDrop (fromIntegral n) bs) - cat - else go (n - len) -{-# INLINABLE drop #-} - -- | Take bytes until they fail the predicate takeWhile :: Monad m => (Word8 -> Bool) -> Pipe ByteString ByteString m () takeWhile predicate = go @@ -355,18 +355,6 @@ else yield prefix {-# INLINABLE takeWhile #-} --- | Drop bytes until they fail the predicate -dropWhile :: Monad m => (Word8 -> Bool) -> Pipe ByteString ByteString m r -dropWhile predicate = go where - go = do - bs <- await - case BS.findIndex (not . predicate) bs of - Nothing -> go - Just i -> do - yield (unsafeDrop i bs) - cat -{-# INLINABLE dropWhile #-} - -- | Only allows 'Word8's to pass if they satisfy the predicate filter :: Monad m => (Word8 -> Bool) -> Pipe ByteString ByteString m r filter predicate = P.map (BS.filter predicate) @@ -522,9 +510,9 @@ -- | Index into a byte stream index - :: (Monad m, Integral a) - => a-> Producer ByteString m () -> m (Maybe Word8) -index n p = head (p >-> drop n) + :: (Monad m, Integral n) + => n -> Producer ByteString m () -> m (Maybe Word8) +index n p = head (drop n p) {-# INLINABLE index #-} -- | Find the index of an element that matches the given 'Word8' @@ -622,8 +610,6 @@ 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) @@ -698,8 +684,8 @@ groupBy :: Monad m => (Word8 -> Word8 -> Bool) - -> Lens' (Producer ByteString m r) - (Producer ByteString m (Producer ByteString m r)) + -> Lens' (Producer ByteString m x) + (Producer ByteString m (Producer ByteString m x)) groupBy equals k p0 = fmap join (k (_groupBy p0)) where -- _groupBy @@ -718,8 +704,8 @@ -- | Like 'groupBy', where the equality predicate is ('==') group :: Monad m - => Lens' (Producer ByteString m r) - (Producer ByteString m (Producer ByteString m r)) + => Lens' (Producer ByteString m x) + (Producer ByteString m (Producer ByteString m x)) group = groupBy (==) {-# INLINABLE group #-} @@ -729,12 +715,12 @@ 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. + this function from the @pipes-text@ library. -} word :: Monad m - => Lens' (Producer ByteString m r) - (Producer ByteString m (Producer ByteString m r)) + => Lens' (Producer ByteString m x) + (Producer ByteString m (Producer ByteString m x)) word k p0 = fmap join (k (to p0)) where -- to @@ -756,15 +742,33 @@ 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. + this function from the @pipes-text@ library. -} line :: Monad m - => Lens' (Producer ByteString m r) - (Producer ByteString m (Producer ByteString m r)) + => Lens' (Producer ByteString m x) + (Producer ByteString m (Producer ByteString m x)) line = break (== nl) {-# INLINABLE line #-} +-- | @(drop n)@ drops the first @n@ bytes +drop + :: (Monad m, Integral n) + => n -> Producer ByteString m r -> Producer ByteString m r +drop n p = do + p' <- lift $ runEffect (for (p ^. splitAt n) discard) + p' +{-# INLINABLE drop #-} + +-- | Drop bytes until they fail the predicate +dropWhile + :: Monad m + => (Word8 -> Bool) -> Producer ByteString m r -> Producer ByteString m r +dropWhile predicate p = do + p' <- lift $ runEffect (for (p ^. span predicate) discard) + p' +{-# INLINABLE dropWhile #-} + -- | Intersperse a 'Word8' in between the bytes of the byte stream intersperse :: Monad m => Word8 -> Producer ByteString m r -> Producer ByteString m r @@ -787,20 +791,27 @@ go1 p' {-# INLINABLE intersperse #-} --- | 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) +-- | Improper lens from unpacked 'Word8's to packaged 'ByteString's +pack :: Monad m => Lens' (Producer Word8 m x) (Producer ByteString m x) +pack k p = fmap _unpack (k (_pack p)) +{-# INLINABLE pack #-} +-- | Improper lens from packed 'ByteString's to unpacked 'Word8's +unpack :: Monad m => Lens' (Producer ByteString m x) (Producer Word8 m x) +unpack k p = fmap _pack (k (_unpack p)) +{-# INLINABLE unpack #-} + +_pack :: Monad m => Producer Word8 m x -> Producer ByteString m x +_pack p = PG.folds step id done (p^.PG.chunksOf defaultChunkSize) + where step diffAs w8 = diffAs . (w8:) done diffAs = BS.pack (diffAs []) +{-# INLINABLE _pack #-} - -- from :: Monad m => Producer ByteString m x -> Producer Word8 m x - from p = for p (each . BS.unpack) -{-# INLINABLE pack #-} +_unpack :: Monad m => Producer ByteString m x -> Producer Word8 m x +_unpack p = for p (each . BS.unpack) +{-# INLINABLE _unpack #-} {-| Group byte stream chunks into chunks of fixed length @@ -821,8 +832,7 @@ -- | 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) + => n -> Lens' (Producer ByteString m x) (FreeT (Producer ByteString m) m x) chunksOf n k p0 = fmap concats (k (go p0)) where go p = PG.FreeT $ do @@ -904,75 +914,84 @@ groups = groupsBy (==) {-# INLINABLE groups #-} -{-| Improper isomorphism between a bytestream and its lines +{-| Improper lens 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. + this function from the @pipes-text@ library. -} lines :: Monad m - => Iso' (Producer ByteString m x) (FreeT (Producer ByteString m) m x) -lines = Data.Profunctor.dimap _lines (fmap _unlines) + => Lens' (Producer ByteString m x) (FreeT (Producer ByteString m) m x) +lines k p = fmap _unlines (k (_lines p)) +{-# INLINABLE lines #-} + +{-| Improper lens between lines and a bytestream + + 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 @pipes-text@ library. +-} +unlines + :: Monad m + => Lens' (FreeT (Producer ByteString m) m x) (Producer ByteString m x) +unlines k p = fmap _lines (k (_unlines p)) +{-# INLINABLE unlines #-} + +_lines + :: Monad m => Producer ByteString m x -> FreeT (Producer ByteString m) m x +_lines p0 = PG.FreeT (go0 p0) where - -- _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 + 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 (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'' - - -- _unlines - -- :: Monad m - -- => FreeT (Producer ByteString m) m x -> Producer ByteString m x - _unlines = concats . PG.maps addNewline + Right (_, p'') -> go0 p'' +{-# INLINABLE _lines #-} - -- addNewline - -- :: Monad m => Producer ByteString m r -> Producer ByteString m r +_unlines + :: Monad m => FreeT (Producer ByteString m) m x -> Producer ByteString m x +_unlines = concats . PG.maps addNewline + where addNewline p = p <* yield (BS.singleton nl) -{-# INLINABLE lines #-} +{-# INLINABLE _unlines #-} -{-| Improper isomorphism between a bytestream and its words +{-| Convert a bytestream to delimited 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. + this function from the @pipes-text@ library. -} -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 ' ') +words :: Monad m => Producer ByteString m x -> FreeT (Producer ByteString m) m x +words p = PG.FreeT $ do + x <- next (dropWhile isSpaceWord8 p) + return $ case x of + Left r -> PG.Pure r + Right (bs, p') -> PG.Free $ do + p'' <- (yield bs >> p')^.break isSpaceWord8 + return (words p'') {-# INLINABLE words #-} + +{-| Convert delimited words back to a byte stream + + 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 @pipes-text@ library. +-} +unwords + :: Monad m => FreeT (Producer ByteString m) m x -> Producer ByteString m x +unwords = PG.intercalates (yield $ BS.singleton $ fromIntegral $ ord ' ') +{-# INLINABLE unwords #-} {- $parse The following parsing utilities are single-byte analogs of the ones found