diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -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,
diff --git a/pipes-bytestring.cabal b/pipes-bytestring.cabal
--- a/pipes-bytestring.cabal
+++ b/pipes-bytestring.cabal
@@ -1,10 +1,10 @@
 Name: pipes-bytestring
-Version: 1.0.3
-Cabal-Version: >=1.8.0.2
+Version: 2.1.7
+Cabal-Version: >=1.10
 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
@@ -19,11 +19,12 @@
     Hs-Source-Dirs: src
     Build-Depends:
         base         >= 4       && < 5   ,
-        bytestring   >= 0.9.2.1 && < 0.11,
-        pipes        >= 4.0     && < 4.1 ,
-        pipes-parse  >= 2.0.0   && < 2.1 ,
-        transformers >= 0.2.0.0 && < 0.4
-    Exposed-Modules:
-        Pipes.ByteString,
-        Pipes.ByteString.Parse
+        bytestring   >= 0.9.2.1 && < 0.12,
+        pipes        >= 4.0     && < 4.4 ,
+        pipes-group  >= 1.0.0   && < 1.1 ,
+        pipes-parse  >= 3.0.0   && < 3.1 ,
+        stringsearch >= 0.3.0   && < 0.4 ,
+        transformers >= 0.2.0.0 && < 0.6
+    Exposed-Modules: Pipes.ByteString
     GHC-Options: -O2 -Wall
+    Default-Language: Haskell2010
diff --git a/src/Pipes/ByteString.hs b/src/Pipes/ByteString.hs
--- a/src/Pipes/ByteString.hs
+++ b/src/Pipes/ByteString.hs
@@ -1,9 +1,4 @@
-{-# LANGUAGE RankNTypes, CPP #-}
-
--- The rewrite rules require the Trustworthy annotation
-#if __GLASGOW_HASKELL__ >= 702
-{-# LANGUAGE Trustworthy #-}
-#endif
+{-# LANGUAGE RankNTypes, Trustworthy #-}
 
 {-| This module provides @pipes@ utilities for \"byte streams\", which are
     streams of strict 'ByteString's chunks.  Use byte streams to interact
@@ -22,11 +17,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 +32,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.Group (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.
@@ -61,8 +55,9 @@
     , stdin
     , fromHandle
     , hGetSome
+    , hGetNonBlocking
     , hGet
-    , pack
+    , hGetRange
 
     -- * Servers
     , hGetSomeN
@@ -76,19 +71,17 @@
     , map
     , concatMap
     , take
-    , drop
     , takeWhile
-    , dropWhile
     , filter
     , elemIndices
     , findIndices
     , scan
-    , unpack
 
     -- * Folds
     , toLazy
     , toLazyM
-    , fold
+    , toLazyM'
+    , foldBytes
     , head
     , last
     , null
@@ -105,72 +98,83 @@
     , findIndex
     , count
 
-    -- * Splitters
+    -- * Parsing
+    -- $parse
+    , nextByte
+    , drawByte
+    , unDrawByte
+    , peekByte
+    , isEndOfBytes
+
+    -- * Parsing Lenses
     , splitAt
-    , chunksOf
     , span
     , break
-    , splitWith
-    , split
+    , breakOn
     , groupBy
     , group
-    , lines
-    , words
+    , word
+    , line
 
-    -- * Transformations
+    -- * Transforming Byte Streams
+    , drop
+    , dropWhile
     , intersperse
+    , pack
+    , unpack
+    , chunksOf'
 
-    -- * Joiners
-    , intercalate
+    -- * FreeT Transformations
+    , chunksOf
+    , splitsWith
+    , splits
+    , splitOn
+    , groupsBy
+    , groups
+    , lines
     , unlines
+    , words
     , unwords
 
-    -- * Low-level Parsers
-    -- $parse
-    , nextByte
-    , drawByte
-    , unDrawByte
-    , peekByte
-    , isEndOfBytes
-    , takeWhile'
-
     -- * Re-exports
     -- $reexports
     , module Data.ByteString
     , 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)
 import qualified Data.ByteString.Lazy as BL
+import qualified Data.ByteString.Search
 import Data.ByteString.Lazy.Internal (foldrChunks, defaultChunkSize)
-import Data.ByteString.Unsafe (unsafeTake, unsafeDrop)
+import Data.ByteString.Unsafe (unsafeTake)
 import Data.Char (ord)
+import Data.Monoid (mempty, (<>))
+import Data.Functor.Constant (Constant(Constant, getConstant))
 import Data.Functor.Identity (Identity)
 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
@@ -195,7 +199,7 @@
     )
 
 -- | 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 #-}
 
@@ -229,6 +233,23 @@
 
 {-| Convert a handle into a byte stream using a fixed chunk size
 
+    Similar to 'hGet' except that it will never block waiting for data
+    to become available.
+-}
+hGetNonBlocking :: MonadIO m => Int -> IO.Handle -> Producer' ByteString m ()
+hGetNonBlocking size h = go where
+    go = do
+        eof <- liftIO (IO.hIsEOF h)
+        if eof
+            then return ()
+            else do
+                bs <- liftIO (BS.hGetNonBlocking h size)
+                yield bs
+                go
+{-# INLINABLE hGetNonBlocking #-}
+
+{-| Convert a handle into a byte stream using a fixed chunk size
+
     'hGet' waits until exactly the requested number of bytes are available for
     each chunk.
 -}
@@ -244,23 +265,26 @@
                 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 '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)
+
 {-| Like 'hGetSome', except you can vary the maximum chunk size for each request
 -}
 hGetSomeN :: MonadIO m => IO.Handle -> Int -> Server' Int ByteString m ()
-hGetSomeN h = go
+hGetSomeN h size = go size
   where
     go size = do
         bs <- liftIO (BS.hGetSome h size)
@@ -273,7 +297,7 @@
 
 -- | Like 'hGet', except you can vary the chunk size for each request
 hGetN :: MonadIO m => IO.Handle -> Int -> Server' Int ByteString m ()
-hGetN h = go
+hGetN h size = go size
   where
     go size = do
         bs <- liftIO (BS.hGet h size)
@@ -287,9 +311,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,30 +329,28 @@
 
 {-| 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)
-{-# INLINABLE toHandle #-}
+{-# INLINABLE [1] 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 :: Monad m => (Word8 -> Word8) -> Pipe ByteString ByteString m r
 map f = P.map (BS.map f)
-{-# INLINABLE map #-}
+{-# 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 ()
@@ -345,23 +364,8 @@
                     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 :: Monad m => (Word8 -> Bool) -> Pipe ByteString ByteString m ()
 takeWhile predicate = go
   where
     go = do
@@ -374,20 +378,8 @@
             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 :: Monad m => (Word8 -> Bool) -> Pipe ByteString ByteString m r
 filter predicate = P.map (BS.filter predicate)
 {-# INLINABLE filter #-}
 
@@ -402,34 +394,26 @@
   where
     go n = do
         bs <- await
-	each $ List.map (\i -> n + fromIntegral i) (BS.findIndices predicate bs)
+        each $ List.map (\i -> n + fromIntegral i) (BS.findIndices predicate bs)
         go $! n + fromIntegral (BS.length bs)
 {-# INLINABLE findIndices #-}
 
 -- | 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
+scan step begin = do
+    yield (BS.singleton begin)
+    go begin
   where
     go w8 = do
         bs <- await
         let bs' = BS.scanl step w8 bs
             w8' = BS.last bs'
-        yield bs'
+        yield (BS.tail bs')
         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 +428,35 @@
     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
+{-| Fold an effectful 'Producer' of strict 'ByteString's into a lazy
+    'BL.ByteString' alongside the return value
+
+    Note: 'toLazyM'' is not an idiomatic use of @pipes@, but I provide it for
+    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 a -> m (BL.ByteString, a)
+toLazyM' p = do (chunks, a) <- P.toListM' p
+                return (BL.fromChunks chunks, a)
+{-# INLINABLE toLazyM' #-}
+
+{-| 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 +467,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 +481,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 +491,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 +513,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,29 +525,29 @@
 {-# 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 #-}
 
 -- | 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'
@@ -568,55 +568,124 @@
 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)
+
+-- | 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 +702,147 @@
                         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
+{-| Improper lens that splits at the first occurrence of the pattern.
 -}
-splitWith
-    :: (Monad m)
-    => (Word8 -> Bool)
-    -> Producer ByteString m r
-    -> PP.FreeT (Producer ByteString m) m r
-splitWith predicate p0 = PP.FreeT (go0 p0)
+breakOn
+    :: Monad m
+    => ByteString
+    -> Lens' (Producer ByteString m x)
+             (Producer ByteString m (Producer ByteString m x))
+breakOn needle k p0 =
+    fmap join (k (go mempty 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 #-}
+    len0 = BS.length needle
 
--- | 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 #-}
+    go leftovers p =
+        if BS.length leftovers < len0
+        then do
+            x <- lift (next p)
+            case x of
+                Left   r          -> do
+                    yield leftovers
+                    return (return r)
+                Right (bytes, p') -> do
+                    go (leftovers <> bytes) p'
+        else do
+            let (prefix, suffix) = Data.ByteString.Search.breakOn needle leftovers
+            if BS.null suffix
+                then do
+                    let len = BS.length leftovers
+                    let (output, leftovers') =
+                            BS.splitAt (len + 1 - len0) leftovers
+                    yield output
+                    go leftovers' p
+                else do
+                    yield prefix
+                    return (yield suffix >> p)
+{-# INLINABLE breakOn #-}
 
-{-| 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 x)
+             (Producer ByteString m (Producer ByteString m x))
+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 x)
+             (Producer ByteString m (Producer ByteString m x))
 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.
+    this function from the @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 x)
+             (Producer ByteString m (Producer ByteString m x))
+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.
+    this function from the @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 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
+    :: Monad m => Word8 -> Producer ByteString m r -> Producer ByteString m r
 intersperse w8 = go0
   where
     go0 p = do
@@ -775,62 +862,226 @@
                 go1 p'
 {-# INLINABLE intersperse #-}
 
-{-| 'intercalate' concatenates the 'FreeT'-delimited byte streams after
-    interspersing a byte stream in between them
+-- | 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 #-}
+
+_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
+
+    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 #-}
 
+-- | Split a byte stream into groups separated by the given `ByteString`
+splitOn
+    :: Monad m
+    => ByteString
+    -> Lens' (Producer ByteString m x) (FreeT (Producer ByteString m) m x)
+splitOn needle k p0 =
+    fmap
+        (PG.intercalates (yield needle))
+        (k (go p0))
+  where
+    len0 = BS.length needle
+    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')^.(breakOn needle)
+                return (go (drop len0 p''))
+{-# INLINABLE splitOn #-}
+
+{-| 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 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
+    => 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) => FreeT (Producer ByteString m) m r -> Producer ByteString m r
-unlines = go
+    :: 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
-    go f = do
-        x <- lift (PP.runFreeT f)
+    go0 p = do
+        x <- next p
         case x of
-            PP.Pure r -> return r
-            PP.Free p -> do
-                f' <- p
-                yield $ BS.singleton $ fromIntegral (ord '\n')
-                go f'
-{-# INLINABLE unlines #-}
+            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''
+{-# INLINABLE _lines #-}
 
-{-| 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
+  where
+    addNewline p = p <* yield (BS.singleton nl)
+{-# INLINABLE _unlines #-}
 
+{-| 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 => 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 r -> Producer ByteString m r
-unwords = intercalate (yield $ BS.singleton $ fromIntegral $ ord ' ')
+    :: Monad m => FreeT (Producer ByteString m) m x -> Producer ByteString m x
+unwords = PG.intercalates (yield $ BS.singleton $ fromIntegral $ ord ' ')
 {-# INLINABLE unwords #-}
 
 {- $parse
@@ -838,25 +1089,13 @@
     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.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).
 -}
diff --git a/src/Pipes/ByteString/Parse.hs b/src/Pipes/ByteString/Parse.hs
deleted file mode 100644
--- a/src/Pipes/ByteString/Parse.hs
+++ /dev/null
@@ -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 #-}
