diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,8 +1,31 @@
+[0.11.1.0] — February 2021
+
+* [Add `Data.ByteString.Char8.findIndexEnd` and `Data.ByteString.Lazy.Char8.{elemIndexEnd,findIndexEnd,unzip}`](https://github.com/haskell/bytestring/pull/342)
+* [Expose `ShortByteString` constructor from `Data.ByteString.Short`](https://github.com/haskell/bytestring/pull/313)
+* [Add `compareLength` function, which is lazier than comparison of lengths](https://github.com/haskell/bytestring/pull/300)
+* [Add strict `takeEnd` and `dropEnd`](https://github.com/haskell/bytestring/pull/290)
+* [Expose `packZipWith` to zip two `ByteString`](https://github.com/haskell/bytestring/pull/295)
+* [Add `instance Show Builder`](https://github.com/haskell/bytestring/pull/296)
+* [Improve lazy `pack` to carry fewer arguments in the inner loop](https://github.com/haskell/bytestring/pull/292)
+* [Improve `map`, `findIndex` and `findIndexEnd` to carry fewer arguments in the inner loop](https://github.com/haskell/bytestring/pull/347)
+* [Improve lazy `{take,drop}While`, `break` and `group{,By}` to carry fewer arguments in the inner loop](https://github.com/haskell/bytestring/pull/337)
+* [Speed up `intersperse` using SSE2 instructions](https://github.com/haskell/bytestring/pull/310)
+* [`fromShort` does not reallocate its argument, if it is pinned](https://github.com/haskell/bytestring/pull/317)
+* [Speed up `words` using a faster test for spaces](https://github.com/haskell/bytestring/pull/315)
+* [Implement `stimes` more efficiently than default definition](https://github.com/haskell/bytestring/pull/301)
+
+[0.11.1.0]: https://github.com/haskell/bytestring/compare/0.11.0.0...0.11.1.0
+
+[0.10.12.1] – January 2021
+
+* [Replace `withForeignPtr` with `unsafeWithForeignPtr` where appropriate](https://github.com/haskell/bytestring/pull/333)
+
+[0.10.12.1]: https://github.com/haskell/bytestring/compare/0.10.12.0...0.10.12.1
+
 [0.11.0.0] — September 2020
  * [Change internal representation of `ByteString`, removing offset](https://github.com/haskell/bytestring/pull/175)
    * The old `PS` constructor has been turned into a pattern synonym that is available with GHC >= 8.0 for backwards compatibility. Consider adding `if !impl(ghc >=8.0) { build-depends: bytestring < 0.11 }` to packages, which use `PS` and still support GHC < 8.0.
  * [Fill `ForeignPtrContents` of `nullForeignPtr` with `FinalPtr` instead of a bottom](https://github.com/haskell/bytestring/pull/284)
-   * While `bytestring-0.11` is compatible with GHC >= 7.0, note that `bytestring < 0.11` will be unbuildable with GHC >= 9.0.
  * [Remove deprecated functions `findSubstring` and `findSubstrings`](https://github.com/haskell/bytestring/pull/181)
  * [Speed up sorting of short strings](https://github.com/haskell/bytestring/pull/267)
  * [Improve handling of literal strings in `Data.ByteString.Builder`](https://github.com/haskell/bytestring/pull/132)
diff --git a/Data/ByteString.hs b/Data/ByteString.hs
--- a/Data/ByteString.hs
+++ b/Data/ByteString.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE TupleSections #-}
 {-# OPTIONS_HADDOCK prune #-}
 #if __GLASGOW_HASKELL__ >= 701
 {-# LANGUAGE Trustworthy #-}
@@ -112,7 +113,9 @@
 
         -- ** Breaking strings
         take,                   -- :: Int -> ByteString -> ByteString
+        takeEnd,                -- :: Int -> ByteString -> ByteString
         drop,                   -- :: Int -> ByteString -> ByteString
+        dropEnd,                -- :: Int -> ByteString -> ByteString
         splitAt,                -- :: Int -> ByteString -> (ByteString, ByteString)
         takeWhile,              -- :: (Word8 -> Bool) -> ByteString -> ByteString
         takeWhileEnd,           -- :: (Word8 -> Bool) -> ByteString -> ByteString
@@ -167,6 +170,7 @@
         -- * Zipping and unzipping ByteStrings
         zip,                    -- :: ByteString -> ByteString -> [(Word8,Word8)]
         zipWith,                -- :: (Word8 -> Word8 -> c) -> ByteString -> ByteString -> [c]
+        packZipWith,            -- :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> ByteString
         unzip,                  -- :: [(Word8,Word8)] -> (ByteString,ByteString)
 
         -- * Ordered ByteStrings
@@ -238,7 +242,7 @@
 import Data.Word                (Word8)
 
 import Control.Exception        (IOException, catch, finally, assert, throwIO)
-import Control.Monad            (when)
+import Control.Monad            (when, void)
 
 import Foreign.C.String         (CString, CStringLen)
 import Foreign.C.Types          (CSize)
@@ -257,19 +261,14 @@
 import System.IO                (stdin,stdout,hClose,hFileSize
                                 ,hGetBuf,hPutBuf,hGetBufNonBlocking
                                 ,hPutBufNonBlocking,withBinaryFile
-                                ,IOMode(..))
+                                ,IOMode(..),hGetBufSome)
 import System.IO.Error          (mkIOError, illegalOperationErrorType)
 
 #if !(MIN_VERSION_base(4,8,0))
+import Control.Applicative      ((<$>))
 import Data.Monoid              (Monoid(..))
 #endif
 
-#if MIN_VERSION_base(4,3,0)
-import System.IO                (hGetBufSome)
-#else
-import System.IO                (hWaitForInput, hIsEOF)
-#endif
-
 import Data.IORef
 import GHC.IO.Handle.Internals
 import GHC.IO.Handle.Types
@@ -365,14 +364,14 @@
 -- | /O(n)/ 'cons' is analogous to (:) for lists, but of different
 -- complexity, as it requires making a copy.
 cons :: Word8 -> ByteString -> ByteString
-cons c (BS x l) = unsafeCreate (l+1) $ \p -> withForeignPtr x $ \f -> do
+cons c (BS x l) = unsafeCreate (l+1) $ \p -> unsafeWithForeignPtr x $ \f -> do
         poke p c
         memcpy (p `plusPtr` 1) f (fromIntegral l)
 {-# INLINE cons #-}
 
 -- | /O(n)/ Append a byte to the end of a 'ByteString'
 snoc :: ByteString -> Word8 -> ByteString
-snoc (BS x l) c = unsafeCreate (l+1) $ \p -> withForeignPtr x $ \f -> do
+snoc (BS x l) c = unsafeCreate (l+1) $ \p -> unsafeWithForeignPtr x $ \f -> do
         memcpy p f (fromIntegral l)
         poke (p `plusPtr` l) c
 {-# INLINE snoc #-}
@@ -384,7 +383,7 @@
 head :: ByteString -> Word8
 head (BS x l)
     | l <= 0    = errorEmptyList "head"
-    | otherwise = accursedUnutterablePerformIO $ withForeignPtr x $ \p -> peek p
+    | otherwise = accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p -> peek p
 {-# INLINE head #-}
 
 -- | /O(1)/ Extract the elements after the head of a ByteString, which must be non-empty.
@@ -400,7 +399,7 @@
 uncons :: ByteString -> Maybe (Word8, ByteString)
 uncons (BS x l)
     | l <= 0    = Nothing
-    | otherwise = Just (accursedUnutterablePerformIO $ withForeignPtr x
+    | otherwise = Just (accursedUnutterablePerformIO $ unsafeWithForeignPtr x
                                                      $ \p -> peek p,
                         BS (plusForeignPtr x 1) (l-1))
 {-# INLINE uncons #-}
@@ -411,7 +410,7 @@
 last ps@(BS x l)
     | null ps   = errorEmptyList "last"
     | otherwise = accursedUnutterablePerformIO $
-                    withForeignPtr x $ \p -> peekByteOff p (l-1)
+                    unsafeWithForeignPtr x $ \p -> peekByteOff p (l-1)
 {-# INLINE last #-}
 
 -- | /O(1)/ Return all the elements of a 'ByteString' except the last one.
@@ -429,7 +428,7 @@
     | l <= 0    = Nothing
     | otherwise = Just (BS x (l-1),
                         accursedUnutterablePerformIO $
-                          withForeignPtr x $ \p -> peekByteOff p (l-1))
+                          unsafeWithForeignPtr x $ \p -> peekByteOff p (l-1))
 {-# INLINE unsnoc #-}
 
 -- | /O(n)/ Append two ByteStrings
@@ -443,21 +442,23 @@
 -- | /O(n)/ 'map' @f xs@ is the ByteString obtained by applying @f@ to each
 -- element of @xs@.
 map :: (Word8 -> Word8) -> ByteString -> ByteString
-map f (BS fp len) = unsafeDupablePerformIO $ withForeignPtr fp $ \a ->
-    create len $ map_ 0 a
+map f (BS fp len) = unsafeDupablePerformIO $ unsafeWithForeignPtr fp $ \srcPtr ->
+    create len $ \dstPtr -> m srcPtr dstPtr
   where
-    map_ :: Int -> Ptr Word8 -> Ptr Word8 -> IO ()
-    map_ !n !p1 !p2
-       | n >= len = return ()
-       | otherwise = do
-            x <- peekByteOff p1 n
-            pokeByteOff p2 n (f x)
-            map_ (n+1) p1 p2
+    m !p1 !p2 = map_ 0
+      where
+      map_ :: Int -> IO ()
+      map_ !n
+         | n >= len = return ()
+         | otherwise = do
+              x <- peekByteOff p1 n
+              pokeByteOff p2 n (f x)
+              map_ (n+1)
 {-# INLINE map #-}
 
 -- | /O(n)/ 'reverse' @xs@ efficiently returns the elements of @xs@ in reverse order.
 reverse :: ByteString -> ByteString
-reverse (BS x l) = unsafeCreate l $ \p -> withForeignPtr x $ \f ->
+reverse (BS x l) = unsafeCreate l $ \p -> unsafeWithForeignPtr x $ \f ->
         c_reverse p f (fromIntegral l)
 
 -- | /O(n)/ The 'intersperse' function takes a 'Word8' and a
@@ -467,13 +468,13 @@
 intersperse :: Word8 -> ByteString -> ByteString
 intersperse c ps@(BS x l)
     | length ps < 2  = ps
-    | otherwise      = unsafeCreate (2*l-1) $ \p -> withForeignPtr x $ \f ->
+    | otherwise      = unsafeCreate (2*l-1) $ \p -> unsafeWithForeignPtr x $ \f ->
         c_intersperse p f (fromIntegral l) c
 
 -- | The 'transpose' function transposes the rows and columns of its
 -- 'ByteString' argument.
 transpose :: [ByteString] -> [ByteString]
-transpose ps = P.map pack . List.transpose . P.map unpack $ ps
+transpose = P.map pack . List.transpose . P.map unpack
 
 -- ---------------------------------------------------------------------
 -- Reducing 'ByteString's
@@ -485,7 +486,7 @@
 foldl :: (a -> Word8 -> a) -> a -> ByteString -> a
 foldl f z (BS fp len) = go (end `plusPtr` len)
   where
-    end = (unsafeForeignPtrToPtr fp) `plusPtr` (-1)
+    end = unsafeForeignPtrToPtr fp `plusPtr` (-1)
     -- not tail recursive; traverses array right to left
     go !p | p == end  = z
           | otherwise = let !x = accursedUnutterablePerformIO $ do
@@ -499,7 +500,7 @@
 --
 foldl' :: (a -> Word8 -> a) -> a -> ByteString -> a
 foldl' f v (BS fp len) =
-    accursedUnutterablePerformIO $ withForeignPtr fp g
+    accursedUnutterablePerformIO $ unsafeWithForeignPtr fp g
   where
     g ptr = go v ptr
       where
@@ -530,7 +531,7 @@
 -- | 'foldr'' is like 'foldr', but strict in the accumulator.
 foldr' :: (Word8 -> a -> a) -> a -> ByteString -> a
 foldr' k v (BS fp len) =
-    accursedUnutterablePerformIO $ withForeignPtr fp g
+    accursedUnutterablePerformIO $ unsafeWithForeignPtr fp g
   where
     g ptr = go v (end `plusPtr` len)
       where
@@ -545,34 +546,34 @@
 -- argument, and thus must be applied to non-empty 'ByteString's.
 -- An exception will be thrown in the case of an empty ByteString.
 foldl1 :: (Word8 -> Word8 -> Word8) -> ByteString -> Word8
-foldl1 f ps
-    | null ps   = errorEmptyList "foldl1"
-    | otherwise = foldl f (unsafeHead ps) (unsafeTail ps)
+foldl1 f ps = case uncons ps of
+  Nothing     -> errorEmptyList "foldl1"
+  Just (h, t) -> foldl f h t
 {-# INLINE foldl1 #-}
 
 -- | 'foldl1'' is like 'foldl1', but strict in the accumulator.
 -- An exception will be thrown in the case of an empty ByteString.
 foldl1' :: (Word8 -> Word8 -> Word8) -> ByteString -> Word8
-foldl1' f ps
-    | null ps   = errorEmptyList "foldl1'"
-    | otherwise = foldl' f (unsafeHead ps) (unsafeTail ps)
+foldl1' f ps = case uncons ps of
+  Nothing     -> errorEmptyList "foldl1'"
+  Just (h, t) -> foldl' f h t
 {-# INLINE foldl1' #-}
 
 -- | 'foldr1' is a variant of 'foldr' that has no starting value argument,
 -- and thus must be applied to non-empty 'ByteString's
 -- An exception will be thrown in the case of an empty ByteString.
 foldr1 :: (Word8 -> Word8 -> Word8) -> ByteString -> Word8
-foldr1 f ps
-    | null ps        = errorEmptyList "foldr1"
-    | otherwise      = foldr f (unsafeLast ps) (unsafeInit ps)
+foldr1 f ps = case unsnoc ps of
+  Nothing -> errorEmptyList "foldr1"
+  Just (b, c) -> foldr f c b
 {-# INLINE foldr1 #-}
 
 -- | 'foldr1'' is a variant of 'foldr1', but is strict in the
 -- accumulator.
 foldr1' :: (Word8 -> Word8 -> Word8) -> ByteString -> Word8
-foldr1' f ps
-    | null ps        = errorEmptyList "foldr1"
-    | otherwise      = foldr' f (unsafeLast ps) (unsafeInit ps)
+foldr1' f ps = case unsnoc ps of
+  Nothing -> errorEmptyList "foldr1'"
+  Just (b, c) -> foldr' f c b
 {-# INLINE foldr1' #-}
 
 -- ---------------------------------------------------------------------
@@ -592,7 +593,7 @@
 -- any element of the 'ByteString' satisfies the predicate.
 any :: (Word8 -> Bool) -> ByteString -> Bool
 any _ (BS _ 0)   = False
-any f (BS x len) = accursedUnutterablePerformIO $ withForeignPtr x g
+any f (BS x len) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x g
   where
     g ptr = go ptr
       where
@@ -621,7 +622,7 @@
 
 -- | Is any element of 'ByteString' equal to c?
 anyByte :: Word8 -> ByteString -> Bool
-anyByte c (BS x l) = accursedUnutterablePerformIO $ withForeignPtr x $ \p -> do
+anyByte c (BS x l) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p -> do
     q <- memchr p c (fromIntegral l)
     return $! q /= nullPtr
 {-# INLINE anyByte #-}
@@ -632,7 +633,7 @@
 -- if all elements of the 'ByteString' satisfy the predicate.
 all :: (Word8 -> Bool) -> ByteString -> Bool
 all _ (BS _ 0)   = True
-all f (BS x len) = accursedUnutterablePerformIO $ withForeignPtr x g
+all f (BS x len) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x g
   where
     g ptr = go ptr
       where
@@ -668,7 +669,7 @@
 maximum :: ByteString -> Word8
 maximum xs@(BS x l)
     | null xs   = errorEmptyList "maximum"
-    | otherwise = accursedUnutterablePerformIO $ withForeignPtr x $ \p ->
+    | otherwise = accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p ->
                       c_maximum p (fromIntegral l)
 {-# INLINE maximum #-}
 
@@ -678,7 +679,7 @@
 minimum :: ByteString -> Word8
 minimum xs@(BS x l)
     | null xs   = errorEmptyList "minimum"
-    | otherwise = accursedUnutterablePerformIO $ withForeignPtr x $ \p ->
+    | otherwise = accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p ->
                       c_minimum p (fromIntegral l)
 {-# INLINE minimum #-}
 
@@ -689,9 +690,9 @@
 -- passing an accumulating parameter from left to right, and returning a
 -- final value of this accumulator together with the new list.
 mapAccumL :: (acc -> Word8 -> (acc, Word8)) -> acc -> ByteString -> (acc, ByteString)
-mapAccumL f acc (BS fp len) = unsafeDupablePerformIO $ withForeignPtr fp $ \a -> do
+mapAccumL f acc (BS fp len) = unsafeDupablePerformIO $ unsafeWithForeignPtr fp $ \a -> do
     gp   <- mallocByteString len
-    acc' <- withForeignPtr gp (go a)
+    acc' <- unsafeWithForeignPtr gp (go a)
     return (acc', BS gp len)
   where
     go src dst = mapAccumL_ acc 0
@@ -710,10 +711,10 @@
 -- passing an accumulating parameter from right to left, and returning a
 -- final value of this accumulator together with the new ByteString.
 mapAccumR :: (acc -> Word8 -> (acc, Word8)) -> acc -> ByteString -> (acc, ByteString)
-mapAccumR f acc (BS fp len) = unsafeDupablePerformIO $ withForeignPtr fp $ \a -> do
+mapAccumR f acc (BS fp len) = unsafeDupablePerformIO $ unsafeWithForeignPtr fp $ \a -> do
     gp   <- mallocByteString len
-    acc' <- withForeignPtr gp (go a)
-    return $! (acc', BS gp len)
+    acc' <- unsafeWithForeignPtr gp (go a)
+    return (acc', BS gp len)
   where
     go src dst = mapAccumR_ acc (len-1)
       where
@@ -747,7 +748,7 @@
     -- ^ input of length n
     -> ByteString
     -- ^ output of length n+1
-scanl f v (BS fp len) = unsafeDupablePerformIO $ withForeignPtr fp $ \a ->
+scanl f v (BS fp len) = unsafeDupablePerformIO $ unsafeWithForeignPtr fp $ \a ->
     create (len+1) $ \q -> do
         poke q v
         go a (q `plusPtr` 1)
@@ -772,9 +773,9 @@
 --
 -- > scanl1 f [x1, x2, ...] == [x1, x1 `f` x2, ...]
 scanl1 :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString
-scanl1 f ps
-    | null ps   = empty
-    | otherwise = scanl f (unsafeHead ps) (unsafeTail ps)
+scanl1 f ps = case uncons ps of
+  Nothing     -> empty
+  Just (h, t) -> scanl f h t
 {-# INLINE scanl1 #-}
 
 -- | 'scanr' is similar to 'foldr', but returns a list of successive
@@ -796,7 +797,7 @@
     -- ^ input of length n
     -> ByteString
     -- ^ output of length n+1
-scanr f v (BS fp len) = unsafeDupablePerformIO $ withForeignPtr fp $ \a ->
+scanr f v (BS fp len) = unsafeDupablePerformIO $ unsafeWithForeignPtr fp $ \a ->
     create (len+1) $ \q -> do
         poke (q `plusPtr` len) v
         go a q
@@ -814,9 +815,9 @@
 
 -- | 'scanr1' is a variant of 'scanr' that has no starting value argument.
 scanr1 :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString
-scanr1 f ps
-    | null ps   = empty
-    | otherwise = scanr f (unsafeLast ps) (unsafeInit ps)
+scanr1 f ps = case unsnoc ps of
+  Nothing -> empty
+  Just (b, c) -> scanr f c b
 {-# INLINE scanr1 #-}
 
 -- ---------------------------------------------------------------------
@@ -832,7 +833,7 @@
 replicate w c
     | w <= 0    = empty
     | otherwise = unsafeCreate w $ \ptr ->
-                      memset ptr c (fromIntegral w) >> return ()
+                      void $ memset ptr c (fromIntegral w)
 {-# INLINE replicate #-}
 
 -- | /O(n)/, where /n/ is the length of the result.  The 'unfoldr'
@@ -851,7 +852,7 @@
 unfoldr f = concat . unfoldChunk 32 64
   where unfoldChunk n n' x =
           case unfoldrN n f x of
-            (s, Nothing) -> s : []
+            (s, Nothing) -> [s]
             (s, Just x') -> s : unfoldChunk n' (n+n') x'
 {-# INLINE unfoldr #-}
 
@@ -889,6 +890,24 @@
     | otherwise = BS x n
 {-# INLINE take #-}
 
+-- | /O(1)/ @'takeEnd' n xs@ is equivalent to @'drop' ('length' xs - n) xs@.
+-- Takes @n@ elements from end of bytestring.
+--
+-- >>> takeEnd 3 "abcdefg"
+-- "efg"
+-- >>> takeEnd 0 "abcdefg"
+-- ""
+-- >>> takeEnd 4 "abc"
+-- "abc"
+--
+-- @since 0.11.1.0
+takeEnd :: Int -> ByteString -> ByteString
+takeEnd n ps@(BS x len)
+  | n >= len  = ps
+  | n <= 0    = empty
+  | otherwise = BS (plusForeignPtr x (len - n)) n
+{-# INLINE takeEnd #-}
+
 -- | /O(1)/ 'drop' @n xs@ returns the suffix of @xs@ after the first @n@
 -- elements, or @[]@ if @n > 'length' xs@.
 drop  :: Int -> ByteString -> ByteString
@@ -898,6 +917,24 @@
     | otherwise = BS (plusForeignPtr x n) (l-n)
 {-# INLINE drop #-}
 
+-- | /O(1)/ @'dropEnd' n xs@ is equivalent to @'take' ('length' xs - n) xs@.
+-- Drops @n@ elements from end of bytestring.
+--
+-- >>> dropEnd 3 "abcdefg"
+-- "abcd"
+-- >>> dropEnd 0 "abcdefg"
+-- "abcdefg"
+-- >>> dropEnd 4 "abc"
+-- ""
+--
+-- @since 0.11.1.0
+dropEnd :: Int -> ByteString -> ByteString
+dropEnd n ps@(BS x len)
+    | n <= 0    = ps
+    | n >= len  = empty
+    | otherwise = BS x (len - n)
+{-# INLINE dropEnd #-}
+
 -- | /O(1)/ 'splitAt' @n xs@ is equivalent to @('take' n xs, 'drop' n xs)@.
 splitAt :: Int -> ByteString -> (ByteString, ByteString)
 splitAt n ps@(BS x l)
@@ -910,7 +947,7 @@
 -- returns the longest (possibly empty) prefix of elements
 -- satisfying the predicate.
 takeWhile :: (Word8 -> Bool) -> ByteString -> ByteString
-takeWhile f ps = unsafeTake (findIndexOrEnd (not . f) ps) ps
+takeWhile f ps = unsafeTake (findIndexOrLength (not . f) ps) ps
 {-# INLINE [1] takeWhile #-}
 
 #if MIN_VERSION_base(4,9,0)
@@ -951,7 +988,7 @@
 -- drops the longest (possibly empty) prefix of elements
 -- satisfying the predicate and returns the remainder.
 dropWhile :: (Word8 -> Bool) -> ByteString -> ByteString
-dropWhile f ps = unsafeDrop (findIndexOrEnd (not . f) ps) ps
+dropWhile f ps = unsafeDrop (findIndexOrLength (not . f) ps) ps
 {-# INLINE [1] dropWhile #-}
 
 #if MIN_VERSION_base(4,9,0)
@@ -989,8 +1026,6 @@
 dropWhileEnd f ps = unsafeTake (findFromEndUntil (not . f) ps) ps
 {-# INLINE dropWhileEnd #-}
 
--- instead of findIndexOrEnd, we could use memchr here.
-
 -- | Similar to 'P.break',
 -- returns the longest (possibly empty) prefix of elements which __do not__
 -- satisfy the predicate and the remainder of the string.
@@ -1004,7 +1039,7 @@
 -- > break (==x) = breakByte x
 --
 break :: (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
-break p ps = case findIndexOrEnd p ps of n -> (unsafeTake n ps, unsafeDrop n ps)
+break p ps = case findIndexOrLength p ps of n -> (unsafeTake n ps, unsafeDrop n ps)
 {-# INLINE [1] break #-}
 
 -- See bytestring #70
@@ -1053,7 +1088,7 @@
 -- 'span' @p@ is equivalent to @'break' (not . p)@ and to @('takeWhile' p &&& 'dropWhile' p)@.
 --
 span :: (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
-span p ps = break (not . p) ps
+span p = break (not . p)
 {-# INLINE [1] span #-}
 
 -- | 'spanByte' breaks its ByteString argument at the first
@@ -1064,7 +1099,7 @@
 --
 spanByte :: Word8 -> ByteString -> (ByteString, ByteString)
 spanByte c ps@(BS x l) =
-    accursedUnutterablePerformIO $  withForeignPtr x g
+    accursedUnutterablePerformIO $  unsafeWithForeignPtr x g
   where
     g p = go 0
       where
@@ -1123,7 +1158,7 @@
 splitWith predicate (BS fp len) = splitWith0 0 len fp
   where splitWith0 !off' !len' !fp' =
           accursedUnutterablePerformIO $
-            withForeignPtr fp $ \p ->
+            unsafeWithForeignPtr fp $ \p ->
               splitLoop p 0 off' len' fp'
 
         splitLoop :: Ptr Word8
@@ -1164,7 +1199,7 @@
 split w (BS x l) = loop 0
     where
         loop !n =
-            let q = accursedUnutterablePerformIO $ withForeignPtr x $ \p ->
+            let q = accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p ->
                       memchr (p `plusPtr` n)
                              w (fromIntegral (l-n))
             in if q == nullPtr
@@ -1187,19 +1222,19 @@
 -- supply their own equality test. It is about 40% faster than
 -- /groupBy (==)/
 group :: ByteString -> [ByteString]
-group xs
-    | null xs   = []
-    | otherwise = ys : group zs
+group xs = case uncons xs of
+  Nothing     -> []
+  Just (h, _) -> ys : group zs
     where
-        (ys, zs) = spanByte (unsafeHead xs) xs
+        (ys, zs) = spanByte h xs
 
 -- | The 'groupBy' function is the non-overloaded version of 'group'.
 groupBy :: (Word8 -> Word8 -> Bool) -> ByteString -> [ByteString]
-groupBy k xs
-    | null xs   = []
-    | otherwise = unsafeTake n xs : groupBy k (unsafeDrop n xs)
+groupBy k xs = case uncons xs of
+  Nothing     -> []
+  Just (h, t) -> unsafeTake n xs : groupBy k (unsafeDrop n xs)
     where
-        n = 1 + findIndexOrEnd (not . k (unsafeHead xs)) (unsafeTail xs)
+        n = 1 + findIndexOrLength (not . k h) t
 
 -- | /O(n)/ The 'intercalate' function takes a 'ByteString' and a list of
 -- 'ByteString's and concatenates the list after interspersing the first
@@ -1218,8 +1253,8 @@
 --
 intercalateWithByte :: Word8 -> ByteString -> ByteString -> ByteString
 intercalateWithByte c f@(BS ffp l) g@(BS fgp m) = unsafeCreate len $ \ptr ->
-    withForeignPtr ffp $ \fp ->
-    withForeignPtr fgp $ \gp -> do
+    unsafeWithForeignPtr ffp $ \fp ->
+    unsafeWithForeignPtr fgp $ \gp -> do
         memcpy ptr fp (fromIntegral l)
         poke (ptr `plusPtr` l) c
         memcpy (ptr `plusPtr` (l + 1)) gp (fromIntegral m)
@@ -1265,7 +1300,7 @@
 -- element, or 'Nothing' if there is no such element.
 -- This implementation uses memchr(3).
 elemIndex :: Word8 -> ByteString -> Maybe Int
-elemIndex c (BS x l) = accursedUnutterablePerformIO $ withForeignPtr x $ \p -> do
+elemIndex c (BS x l) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p -> do
     q <- memchr p c (fromIntegral l)
     return $! if q == nullPtr then Nothing else Just $! q `minusPtr` p
 {-# INLINE elemIndex #-}
@@ -1275,8 +1310,9 @@
 -- element, or 'Nothing' if there is no such element. The following
 -- holds:
 --
--- > elemIndexEnd c xs ==
--- > (-) (length xs - 1) `fmap` elemIndex c (reverse xs)
+-- > elemIndexEnd c xs = case elemIndex c (reverse xs) of
+-- >   Nothing -> Nothing
+-- >   Just i  -> Just (length xs - 1 - i)
 --
 elemIndexEnd :: Word8 -> ByteString -> Maybe Int
 elemIndexEnd = findIndexEnd . (==)
@@ -1288,7 +1324,7 @@
 elemIndices :: Word8 -> ByteString -> [Int]
 elemIndices w (BS x l) = loop 0
     where
-        loop !n = accursedUnutterablePerformIO $ withForeignPtr x $ \p -> do
+        loop !n = accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p -> do
             q <- memchr (p `plusPtr` n) w (fromIntegral (l - n))
             if q == nullPtr
                 then return []
@@ -1302,21 +1338,23 @@
 --
 -- But more efficiently than using length on the intermediate list.
 count :: Word8 -> ByteString -> Int
-count w (BS x m) = accursedUnutterablePerformIO $ withForeignPtr x $ \p ->
-    fmap fromIntegral $ c_count p (fromIntegral m) w
+count w (BS x m) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p ->
+    fromIntegral <$> c_count p (fromIntegral m) w
 {-# INLINE count #-}
 
 -- | /O(n)/ The 'findIndex' function takes a predicate and a 'ByteString' and
 -- returns the index of the first element in the ByteString
 -- satisfying the predicate.
 findIndex :: (Word8 -> Bool) -> ByteString -> Maybe Int
-findIndex k (BS x l) = accursedUnutterablePerformIO $ withForeignPtr x $ \f -> go f 0
+findIndex k (BS x l) = accursedUnutterablePerformIO $ withForeignPtr x g
   where
-    go !ptr !n | n >= l    = return Nothing
-               | otherwise = do w <- peek ptr
-                                if k w
-                                  then return (Just n)
-                                  else go (ptr `plusPtr` 1) (n+1)
+    g !ptr = go 0
+      where
+        go !n | n >= l    = return Nothing
+              | otherwise = do w <- peek $ ptr `plusPtr` n
+                               if k w
+                                 then return (Just n)
+                                 else go (n+1)
 {-# INLINE [1] findIndex #-}
 
 -- | /O(n)/ The 'findIndexEnd' function takes a predicate and a 'ByteString' and
@@ -1325,19 +1363,21 @@
 --
 -- @since 0.10.12.0
 findIndexEnd :: (Word8 -> Bool) -> ByteString -> Maybe Int
-findIndexEnd k (BS x l) = accursedUnutterablePerformIO $ withForeignPtr x $ \ f -> go f (l-1)
+findIndexEnd k (BS x l) = accursedUnutterablePerformIO $ withForeignPtr x g
   where
-    go !ptr !n | n < 0     = return Nothing
-               | otherwise = do w <- peekByteOff ptr n
-                                if k w
-                                  then return (Just n)
-                                  else go ptr (n-1)
+    g !ptr = go (l-1)
+      where
+        go !n | n < 0     = return Nothing
+              | otherwise = do w <- peekByteOff ptr n
+                               if k w
+                                 then return (Just n)
+                                 else go (n-1)
 {-# INLINE findIndexEnd #-}
 
 -- | /O(n)/ The 'findIndices' function extends 'findIndex', by returning the
 -- indices of all elements satisfying the predicate, in ascending order.
 findIndices :: (Word8 -> Bool) -> ByteString -> [Int]
-findIndices p ps = loop 0 ps
+findIndices p = loop 0
    where
      loop !n !qs = case findIndex p qs of
                      Just !i ->
@@ -1373,7 +1413,7 @@
 
 -- | /O(n)/ 'notElem' is the inverse of 'elem'
 notElem :: Word8 -> ByteString -> Bool
-notElem c ps = not (elem c ps)
+notElem c ps = not (c `elem` ps)
 {-# INLINE notElem #-}
 
 -- | /O(n)/ 'filter', applied to a predicate and a ByteString,
@@ -1479,7 +1519,7 @@
 isPrefixOf (BS x1 l1) (BS x2 l2)
     | l1 == 0   = True
     | l2 < l1   = False
-    | otherwise = accursedUnutterablePerformIO $ withForeignPtr x1 $ \p1 ->
+    | otherwise = accursedUnutterablePerformIO $ unsafeWithForeignPtr x1 $ \p1 ->
         withForeignPtr x2 $ \p2 -> do
             i <- memcmp p1 p2 (fromIntegral l1)
             return $! i == 0
@@ -1507,7 +1547,7 @@
 isSuffixOf (BS x1 l1) (BS x2 l2)
     | l1 == 0   = True
     | l2 < l1   = False
-    | otherwise = accursedUnutterablePerformIO $ withForeignPtr x1 $ \p1 ->
+    | otherwise = accursedUnutterablePerformIO $ unsafeWithForeignPtr x1 $ \p1 ->
         withForeignPtr x2 $ \p2 -> do
             i <- memcmp p1 (p2 `plusPtr` (l2 - l1)) (fromIntegral l1)
             return $! i == 0
@@ -1553,7 +1593,7 @@
                -> (ByteString,ByteString) -- ^ Head and tail of string broken at substring
 breakSubstring pat =
   case lp of
-    0 -> \src -> (empty,src)
+    0 -> (empty,)
     1 -> breakByte (unsafeHead pat)
     _ -> if lp * 8 <= finiteBitSize (0 :: Word)
              then shift
@@ -1608,28 +1648,30 @@
 -- excess elements of the longer ByteString are discarded. This is
 -- equivalent to a pair of 'unpack' operations.
 zip :: ByteString -> ByteString -> [(Word8,Word8)]
-zip ps qs
-    | null ps || null qs = []
-    | otherwise = (unsafeHead ps, unsafeHead qs) : zip (unsafeTail ps) (unsafeTail qs)
+zip ps qs = case uncons ps of
+  Nothing         -> []
+  Just (psH, psT) -> case uncons qs of
+    Nothing         -> []
+    Just (qsH, qsT) -> (psH, qsH) : zip psT qsT
 
 -- | 'zipWith' generalises 'zip' by zipping with the function given as
 -- the first argument, instead of a tupling function.  For example,
 -- @'zipWith' (+)@ is applied to two ByteStrings to produce the list of
 -- corresponding sums.
 zipWith :: (Word8 -> Word8 -> a) -> ByteString -> ByteString -> [a]
-zipWith f ps qs
-    | null ps || null qs = []
-    | otherwise = f (unsafeHead ps) (unsafeHead qs) : zipWith f (unsafeTail ps) (unsafeTail qs)
+zipWith f ps qs = case uncons ps of
+  Nothing         -> []
+  Just (psH, psT) -> case uncons qs of
+    Nothing         -> []
+    Just (qsH, qsT) -> f psH qsH : zipWith f psT qsT
 {-# NOINLINE [1] zipWith #-}
 
---
--- | A specialised version of zipWith for the common case of a
--- simultaneous map over two bytestrings, to build a 3rd. Rewrite rules
--- are used to automatically covert zipWith into zipWith' when a pack is
--- performed on the result of zipWith.
+-- | A specialised version of `zipWith` for the common case of a
+-- simultaneous map over two ByteStrings, to build a 3rd.
 --
-zipWith' :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> ByteString
-zipWith' f (BS fp l) (BS fq m) = unsafeDupablePerformIO $
+-- @since 0.11.1.0
+packZipWith :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> ByteString
+packZipWith f (BS fp l) (BS fq m) = unsafeDupablePerformIO $
     withForeignPtr fp $ \a ->
     withForeignPtr fq $ \b ->
     create len $ go a b
@@ -1646,11 +1688,11 @@
                 zipWith_ (n+1) r
 
     len = min l m
-{-# INLINE zipWith' #-}
+{-# INLINE packZipWith #-}
 
 {-# RULES
 "ByteString specialise zipWith" forall (f :: Word8 -> Word8 -> Word8) p q .
-    zipWith f p q = unpack (zipWith' f p q)
+    zipWith f p q = unpack (packZipWith f p q)
   #-}
 
 -- | /O(n)/ 'unzip' transforms a list of pairs of bytes into a pair of
@@ -1686,11 +1728,11 @@
   | otherwise = unsafeCreate l $ \p -> allocaArray 256 $ \arr -> do
 
     _ <- memset (castPtr arr) 0 (256 * fromIntegral (sizeOf (undefined :: CSize)))
-    withForeignPtr input (\x -> countOccurrences arr x l)
+    unsafeWithForeignPtr input (\x -> countOccurrences arr x l)
 
     let go 256 !_   = return ()
         go i   !ptr = do n <- peekElemOff arr i
-                         when (n /= 0) $ memset ptr (fromIntegral i) n >> return ()
+                         when (n /= 0) $ void $ memset ptr (fromIntegral i) n
                          go (i + 1) (ptr `plusPtr` fromIntegral n)
     go 0 p
   where
@@ -1757,7 +1799,7 @@
 -- is needed in the rest of the program.
 --
 copy :: ByteString -> ByteString
-copy (BS x l) = unsafeCreate l $ \p -> withForeignPtr x $ \f ->
+copy (BS x l) = unsafeCreate l $ \p -> unsafeWithForeignPtr x $ \f ->
     memcpy p f (fromIntegral l)
 
 -- ---------------------------------------------------------------------
@@ -1832,7 +1874,7 @@
 -- | Outputs a 'ByteString' to the specified 'Handle'.
 hPut :: Handle -> ByteString -> IO ()
 hPut _ (BS _  0) = return ()
-hPut h (BS ps l) = withForeignPtr ps $ \p-> hPutBuf h p l
+hPut h (BS ps l) = unsafeWithForeignPtr ps $ \p-> hPutBuf h p l
 
 -- | Similar to 'hPut' except that it will never block. Instead it returns
 -- any tail that did not get written. This tail may be 'empty' in the case that
@@ -1844,7 +1886,7 @@
 --
 hPutNonBlocking :: Handle -> ByteString -> IO ByteString
 hPutNonBlocking h bs@(BS ps l) = do
-  bytesWritten <- withForeignPtr ps $ \p-> hPutBufNonBlocking h p l
+  bytesWritten <- unsafeWithForeignPtr ps $ \p-> hPutBufNonBlocking h p l
   return $! drop bytesWritten bs
 
 -- | A synonym for @hPut@, for compatibility
@@ -1896,22 +1938,7 @@
 --
 hGetSome :: Handle -> Int -> IO ByteString
 hGetSome hh i
-#if MIN_VERSION_base(4,3,0)
     | i >  0    = createAndTrim i $ \p -> hGetBufSome hh p i
-#else
-    | i >  0    = let
-                   loop = do
-                     s <- hGetNonBlocking hh i
-                     if not (null s)
-                        then return s
-                        else do eof <- hIsEOF hh
-                                if eof then return s
-                                       else hWaitForInput hh (-1) >> loop
-                                         -- for this to work correctly, the
-                                         -- Handle should be in binary mode
-                                         -- (see GHC ticket #3808)
-                  in loop
-#endif
     | i == 0    = return empty
     | otherwise = illegalBufferSize hh "hGetSome" i
 
@@ -1951,7 +1978,7 @@
   where
     readChunks chunks sz sz' = do
       fp        <- mallocByteString sz
-      readcount <- withForeignPtr fp $ \buf -> hGetBuf hnd buf sz
+      readcount <- unsafeWithForeignPtr fp $ \buf -> hGetBuf hnd buf sz
       let chunk = BS fp readcount
       -- We rely on the hGetBuf behaviour (not hGetBufSome) where it reads up
       -- to the size we ask for, or EOF. So short reads indicate EOF.
@@ -2007,21 +2034,6 @@
 -- ---------------------------------------------------------------------
 -- Internal utilities
 
--- | 'findIndexOrEnd' is a variant of findIndex, that returns the length
--- of the string if no element is found, rather than Nothing.
-findIndexOrEnd :: (Word8 -> Bool) -> ByteString -> Int
-findIndexOrEnd k (BS x l) =
-    accursedUnutterablePerformIO $ withForeignPtr x g
-  where
-    g ptr = go 0
-      where
-        go !n | n >= l    = return l
-              | otherwise = do w <- peek $ ptr `plusPtr` n
-                               if k w
-                                 then return n
-                                 else go (n+1)
-{-# INLINE findIndexOrEnd #-}
-
 -- Common up near identical calls to `error' to reduce the number
 -- constant strings created when compiled:
 errorEmptyList :: String -> a
@@ -2041,7 +2053,9 @@
 
 -- Find from the end of the string using predicate
 findFromEndUntil :: (Word8 -> Bool) -> ByteString -> Int
-findFromEndUntil f ps@(BS x l)
-  | null ps = 0
-  | f (unsafeLast ps) = l
-  | otherwise = findFromEndUntil f (BS x (l - 1))
+findFromEndUntil f ps@(BS _ l) = case unsnoc ps of
+  Nothing     -> 0
+  Just (b, c) ->
+    if f c
+      then l
+      else findFromEndUntil f b
diff --git a/Data/ByteString/Builder.hs b/Data/ByteString/Builder.hs
--- a/Data/ByteString/Builder.hs
+++ b/Data/ByteString/Builder.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE CPP, BangPatterns, MagicHash #-}
+{-# LANGUAGE CPP, MagicHash #-}
 {-# OPTIONS_GHC -fno-warn-unused-imports -fno-warn-orphans #-}
 #if __GLASGOW_HASKELL__ >= 701
 {-# LANGUAGE Trustworthy #-}
@@ -58,8 +58,8 @@
 import qualified "Data.ByteString.Lazy"               as L
 import           "Data.ByteString.Builder"
 import           Data.Monoid
-import           Data.Foldable                        ('foldMap')
-import           Data.List                            ('intersperse')
+import           Data.Foldable                        ('Data.Foldable.foldMap')
+import           Data.List                            ('Data.List.intersperse')
 
 infixr 4 \<\>
 (\<\>) :: 'Monoid' m => m -> m -> m
@@ -154,7 +154,7 @@
 >renderRow :: Row -> Builder
 >renderRow  = mconcat . intersperse (charUtf8 ',') . map renderCell
 
-Similarly, using /O(n)/ concatentations like '++' or the equivalent 'S.concat'
+Similarly, using /O(n)/ concatentations like '++' or the equivalent 'Data.ByteString.concat'
   operations on strict and lazy 'L.ByteString's should be avoided.
 The following definition of @renderString@ is also about 20% slower.
 
@@ -266,15 +266,6 @@
 import           GHC.Base (unpackCString#, unpackCStringUtf8#,
                            unpackFoldrCString#, build)
 
--- HADDOCK only imports
-import qualified Data.ByteString               as S (concat)
-#if !(MIN_VERSION_base(4,8,0))
-import           Data.Monoid (Monoid(..))
-#endif
-import           Data.Foldable                      (foldMap)
-import           Data.List                          (intersperse)
-
-
 -- | Execute a 'Builder' and return the generated chunks as a lazy 'L.ByteString'.
 -- The work is performed lazy, i.e., only when a chunk of the lazy 'L.ByteString'
 -- is forced.
@@ -478,3 +469,7 @@
 
 instance IsString Builder where
     fromString = stringUtf8
+
+-- | @since 0.11.1.0
+instance Show Builder where
+    show = show . toLazyByteString
diff --git a/Data/ByteString/Builder/ASCII.hs b/Data/ByteString/Builder/ASCII.hs
--- a/Data/ByteString/Builder/ASCII.hs
+++ b/Data/ByteString/Builder/ASCII.hs
@@ -18,7 +18,7 @@
       -- | Formatting of numbers as ASCII text.
       --
       -- Note that you can also use these functions for the ISO/IEC 8859-1 and
-      -- UTF-8 encodings, as the ASCII encoding is equivalent on the 
+      -- UTF-8 encodings, as the ASCII encoding is equivalent on the
       -- codepoints 0-127.
 
       -- *** Decimal numbers
@@ -104,17 +104,11 @@
 import           GHC.Num     (quotRemInteger)
 # endif
 
-# if __GLASGOW_HASKELL__ < 611
-import GHC.Integer.Internals
-# else
 import GHC.Integer.GMP.Internals
-# endif
 #endif
 
 #if HAS_INTEGER_CONSTR
 import qualified Data.ByteString.Builder.Prim.Internal          as P
-import           Data.ByteString.Builder.Prim.Internal.UncheckedShifts
-                   ( caseWordSize_32_64 )
 import           Foreign.C.Types
 import           GHC.Types   (Int(..))
 #endif
@@ -329,14 +323,14 @@
 -- FIXME: Think about also using the MSB. For 64 bit 'Int's this makes a
 -- difference.
 maxPow10 :: Integer
-maxPow10 = toInteger $ (10 :: Int) ^ caseWordSize_32_64 (9 :: Int) 18
+maxPow10 = toInteger $ (10 :: Int) ^ P.caseWordSize_32_64 (9 :: Int) 18
 
 -- | Decimal encoding of an 'Integer' using the ASCII digits.
 integerDec :: Integer -> Builder
 integerDec (IS i#) = intDec (I# i#)
 integerDec i
     | i < 0     = P.primFixed P.char8 '-' `mappend` go (-i)
-    | otherwise =                                   go ( i)
+    | otherwise =                                   go i
   where
     errImpossible fun =
         error $ "integerDec: " ++ fun ++ ": the impossible happened."
@@ -386,7 +380,7 @@
 
 {-# INLINE intDecPadded #-}
 intDecPadded :: P.BoundedPrim Int
-intDecPadded = P.liftFixedToBounded $ caseWordSize_32_64
+intDecPadded = P.liftFixedToBounded $ P.caseWordSize_32_64
     (P.fixedPrim  9 $ c_int_dec_padded9            . fromIntegral)
     (P.fixedPrim 18 $ c_long_long_int_dec_padded18 . fromIntegral)
 
diff --git a/Data/ByteString/Builder/Internal.hs b/Data/ByteString/Builder/Internal.hs
--- a/Data/ByteString/Builder/Internal.hs
+++ b/Data/ByteString/Builder/Internal.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE ScopedTypeVariables, CPP, BangPatterns, RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables, CPP, BangPatterns, RankNTypes, TupleSections #-}
 #if __GLASGOW_HASKELL__ == 700
 -- This is needed as a workaround for an old bug in GHC 7.0.1 (Trac #4498)
 {-# LANGUAGE MonoPatBinds #-}
@@ -147,16 +147,11 @@
 import qualified Data.ByteString.Lazy.Internal as L
 import qualified Data.ByteString.Short.Internal as Sh
 
-#if __GLASGOW_HASKELL__ >= 611
 import qualified GHC.IO.Buffer as IO (Buffer(..), newByteBuffer)
 import           GHC.IO.Handle.Internals (wantWritableHandle, flushWriteBuffer)
 import           GHC.IO.Handle.Types (Handle__, haByteBuffer, haBufferMode)
-import           System.IO (hFlush, BufferMode(..))
+import           System.IO (hFlush, BufferMode(..), Handle)
 import           Data.IORef
-#else
-import qualified Data.ByteString.Lazy as L
-#endif
-import           System.IO (Handle)
 
 #if MIN_VERSION_base(4,4,0)
 #if MIN_VERSION_base(4,7,0)
@@ -316,7 +311,7 @@
             -> BuildStep a
             -- ^ 'BuildStep' to run on next 'BufferRange'
             -> BuildSignal a
-insertChunk op bs = InsertChunk op bs
+insertChunk = InsertChunk
 
 
 -- | Fill a 'BufferRange' using a 'BuildStep'.
@@ -374,7 +369,7 @@
 
 -- | The final build step that returns the 'done' signal.
 finalBuildStep :: BuildStep ()
-finalBuildStep !(BufferRange op _) = return $ Done op ()
+finalBuildStep (BufferRange op _) = return $ Done op ()
 
 -- | Run a 'Builder' with the 'finalBuildStep'.
 {-# INLINE runBuilder #-}
@@ -394,7 +389,7 @@
 -- only exported for use in rewriting rules. Use 'mempty' otherwise.
 {-# INLINE[1] empty #-}
 empty :: Builder
-empty = Builder (\cont -> (\range -> cont range))
+empty = Builder ($)
 -- This eta expansion (hopefully) allows GHC to worker-wrapper the
 -- 'BufferRange' in the 'empty' base case of loops (since
 -- worker-wrapper requires (TODO: verify this) that all paths match
@@ -429,7 +424,7 @@
 flush :: Builder
 flush = builder step
   where
-    step k !(BufferRange op _) = return $ insertChunk op S.empty k
+    step k (BufferRange op _) = return $ insertChunk op S.empty k
 
 
 ------------------------------------------------------------------------------
@@ -488,7 +483,7 @@
 runPut (Put p) = p $ \x (BufferRange op _) -> return $ Done op x
 
 instance Functor Put where
-  fmap f p = Put $ \k -> unPut p (\x -> k (f x))
+  fmap f p = Put $ \k -> unPut p (k . f)
   {-# INLINE fmap #-}
 
 -- | Synonym for '<*' from 'Applicative'; used in rewriting rules.
@@ -506,7 +501,7 @@
   {-# INLINE pure #-}
   pure x = Put $ \k -> k x
   {-# INLINE (<*>) #-}
-  Put f <*> Put a = Put $ \k -> f (\f' -> a (\a' -> k (f' a')))
+  Put f <*> Put a = Put $ \k -> f (\f' -> a (k . f'))
   {-# INLINE (<*) #-}
   (<*) = ap_l
   {-# INLINE (*>) #-}
@@ -531,7 +526,7 @@
 -- | Convert a @'Put' ()@ action to a 'Builder'.
 {-# INLINE fromPut #-}
 fromPut :: Put () -> Builder
-fromPut (Put p) = Builder $ \k -> p (\_ -> k)
+fromPut (Put p) = Builder $ \k -> p (const k)
 
 -- We rewrite consecutive uses of 'putBuilder' such that the append of the
 -- involved 'Builder's is used. This can significantly improve performance,
@@ -613,7 +608,6 @@
 -- buffer is too small to execute one step of the 'Put' action, then
 -- it is replaced with a large enough buffer.
 hPut :: forall a. Handle -> Put a -> IO a
-#if __GLASGOW_HASKELL__ >= 611
 hPut h p = do
     fillHandle 1 (runPut p)
   where
@@ -660,12 +654,7 @@
 
               | freeSpace buf < minFree = flushWriteBuffer h_
               | otherwise               =
-#if __GLASGOW_HASKELL__ >= 613
                                           return ()
-#else
-                                          -- required for ghc-6.12
-                                          flushWriteBuffer h_
-#endif
 
             fillBuffer buf
               | freeSpace buf < minFree =
@@ -714,16 +703,7 @@
                     return $ do
                         S.hPut h bs
                         fillHandle 1 nextStep
-#else
-hPut h p =
-    go =<< buildStepToCIOS strategy (runPut p)
-  where
-    strategy = untrimmedStrategy L.smallChunkSize L.defaultChunkSize
 
-    go (Finished buf x) = S.hPut h (byteStringFromBuffer buf) >> return x
-    go (Yield1 bs io)   = S.hPut h bs >> io >>= go
-#endif
-
 -- | Execute a 'Put' and return the computed result and the bytes
 -- written during the computation as a lazy 'L.ByteString'.
 --
@@ -775,7 +755,7 @@
     -> (a, L.ByteString)  -- ^ Result and lazy 'L.ByteString'
                           -- written as its side-effect
 putToLazyByteString = putToLazyByteStringWith
-    (safeStrategy L.smallChunkSize L.defaultChunkSize) (\x -> (x, L.Empty))
+    (safeStrategy L.smallChunkSize L.defaultChunkSize) (, L.Empty)
 
 
 -- | Execute a 'Put' with a buffer-allocation strategy and a continuation. For
@@ -824,10 +804,10 @@
 -- | Copy the bytes from a 'BufferRange' into the output stream.
 wrappedBytesCopyStep :: BufferRange  -- ^ Input 'BufferRange'.
                      -> BuildStep a -> BuildStep a
-wrappedBytesCopyStep !(BufferRange ip0 ipe) k =
+wrappedBytesCopyStep (BufferRange ip0 ipe) k =
     go ip0
   where
-    go !ip !(BufferRange op ope)
+    go !ip (BufferRange op ope)
       | inpRemaining <= outRemaining = do
           copyBytes op ip inpRemaining
           let !br' = BufferRange (op `plusPtr` inpRemaining) ope
@@ -859,7 +839,7 @@
 byteStringThreshold maxCopySize =
     \bs -> builder $ step bs
   where
-    step !bs@(S.BS _ len) !k br@(BufferRange !op _)
+    step bs@(S.BS _ len) !k br@(BufferRange !op _)
       | len <= maxCopySize = byteStringCopyStep bs k br
       | otherwise          = return $ insertChunk op bs k
 
@@ -881,7 +861,7 @@
     | op' <= ope = do copyBytes op ip isize
                       touchForeignPtr ifp
                       k0 (BufferRange op' ope)
-    | otherwise  = do wrappedBytesCopyStep (BufferRange ip ipe) k br0
+    | otherwise  = wrappedBytesCopyStep (BufferRange ip ipe) k br0
   where
     op'  = op `plusPtr` isize
     ip   = unsafeForeignPtrToPtr ifp
@@ -918,7 +898,7 @@
 shortByteStringCopyStep !sbs k =
     go 0 (Sh.length sbs)
   where
-    go !ip !ipe !(BufferRange op ope)
+    go !ip !ipe (BufferRange op ope)
       | inpRemaining <= outRemaining = do
           Sh.copyToPtr sbs ip op inpRemaining
           let !br' = BufferRange (op `plusPtr` inpRemaining) ope
@@ -1113,10 +1093,10 @@
     :: AllocationStrategy          -- ^ Buffer allocation strategy to use
     -> BuildStep a                 -- ^ 'BuildStep' to execute
     -> IO (ChunkIOStream a)
-buildStepToCIOS !(AllocationStrategy nextBuffer bufSize trim) =
+buildStepToCIOS (AllocationStrategy nextBuffer bufSize trim) =
     \step -> nextBuffer Nothing >>= fill step
   where
-    fill !step !buf@(Buffer fpbuf br@(BufferRange _ pe)) = do
+    fill !step buf@(Buffer fpbuf br@(BufferRange _ pe)) = do
         res <- fillWithBuildStep step doneH fullH insertChunkH br
         touchForeignPtr fpbuf
         return res
diff --git a/Data/ByteString/Builder/Prim.hs b/Data/ByteString/Builder/Prim.hs
--- a/Data/ByteString/Builder/Prim.hs
+++ b/Data/ByteString/Builder/Prim.hs
@@ -458,14 +458,12 @@
   ) where
 
 import           Data.ByteString.Builder.Internal
-import           Data.ByteString.Builder.Prim.Internal.UncheckedShifts
 
 import qualified Data.ByteString               as S
 import qualified Data.ByteString.Internal      as S
 import qualified Data.ByteString.Lazy.Internal as L
 
 import           Data.Monoid
-import           Data.List (unfoldr)  -- HADDOCK ONLY
 import           Data.Char (chr, ord)
 import           Control.Monad ((<=<), unless)
 
@@ -485,6 +483,7 @@
 #else
 import           Foreign
 #endif
+import           GHC.Word (Word8 (..))
 import           GHC.Exts
 import           GHC.IO
 
@@ -502,7 +501,7 @@
 primMapListFixed :: FixedPrim a -> ([a] -> Builder)
 primMapListFixed = primMapListBounded . toB
 
--- | Encode a list of values represented as an 'unfoldr' with a 'FixedPrim'.
+-- | Encode a list of values represented as an 'Data.List.unfoldr' with a 'FixedPrim'.
 {-# INLINE primUnfoldrFixed #-}
 primUnfoldrFixed :: FixedPrim b -> (a -> Maybe (b, a)) -> a -> Builder
 primUnfoldrFixed = primUnfoldrBounded . toB
@@ -618,12 +617,11 @@
 primUnfoldrBounded w f x0 =
     builder $ fillWith x0
   where
-    fillWith x k !(BufferRange op0 ope0) =
+    fillWith x k (BufferRange op0 ope0) =
         go (f x) op0
       where
-        go !Nothing        !op         = do let !br' = BufferRange op ope0
-                                            k br'
-        go !(Just (y, x')) !op
+        go Nothing        !op         = k (BufferRange op ope0)
+        go (Just (y, x')) !op
           | op `plusPtr` bound <= ope0 = runB w y op >>= go (f x')
           | otherwise                  = return $ bufferFull bound op $
               \(BufferRange opNew opeNew) -> do
@@ -649,7 +647,7 @@
         goBS (unsafeForeignPtrToPtr ifp)
       where
         !ipe = unsafeForeignPtrToPtr ifp `plusPtr` isize
-        goBS !ip0 !br@(BufferRange op0 ope)
+        goBS !ip0 br@(BufferRange op0 ope)
           | ip0 >= ipe = do
               touchForeignPtr ifp -- input buffer consumed
               k br
@@ -695,8 +693,8 @@
     \addr0 -> builder $ step addr0
   where
     step :: Addr# -> BuildStep r -> BuildStep r
-    step !addr !k !br@(BufferRange op0@(Ptr op0#) ope)
-      | isTrue# (ch `eqWord#` 0##) = k br
+    step !addr !k br@(BufferRange op0@(Ptr op0#) ope)
+      | W8# ch == 0 = k br
       | op0 == ope =
           return $ bufferFull defaultChunkSize op0 (step addr k)
       | otherwise = do
@@ -714,14 +712,15 @@
     \addr0 -> builder $ step addr0
   where
     step :: Addr# -> BuildStep r -> BuildStep r
-    step !addr !k !br@(BufferRange op0@(Ptr op0#) ope)
-      | isTrue# (ch `eqWord#` 0##) = k br
+    step !addr !k br@(BufferRange op0@(Ptr op0#) ope)
+      | W8# ch == 0 = k br
       | op0 == ope =
           return $ bufferFull defaultChunkSize op0 (step addr k)
         -- NULL is encoded as 0xc0 0x80
-      | isTrue# (ch `eqWord#` 0xc0##)
-      , isTrue# (indexWord8OffAddr# addr 1# `eqWord#` 0x80##) = do
-          IO $ \s -> case writeWord8OffAddr# op0# 0# 0## s of
+      | W8# ch == 0xc0
+      , W8# (indexWord8OffAddr# addr 1#) == 0x80 = do
+          let !(W8# nullByte#) = 0
+          IO $ \s -> case writeWord8OffAddr# op0# 0# nullByte# s of
                        s' -> (# s', () #)
           let br' = BufferRange (op0 `plusPtr` 1) ope
           step (addr `plusAddr#` 2#) k br'
@@ -754,7 +753,7 @@
   where
     pokeN n io op  = io op >> return (op `plusPtr` n)
 
-    f1 x1          = pokeN 1 $ \op -> do pokeByteOff op 0 x1
+    f1 x1          = pokeN 1 $ \op ->    pokeByteOff op 0 x1
 
     f2 x1 x2       = pokeN 2 $ \op -> do pokeByteOff op 0 x1
                                          pokeByteOff op 1 x2
diff --git a/Data/ByteString/Builder/Prim/ASCII.hs b/Data/ByteString/Builder/Prim/ASCII.hs
--- a/Data/ByteString/Builder/Prim/ASCII.hs
+++ b/Data/ByteString/Builder/Prim/ASCII.hs
@@ -84,7 +84,6 @@
 import Data.ByteString.Builder.Prim.Internal
 import Data.ByteString.Builder.Prim.Internal.Floating
 import Data.ByteString.Builder.Prim.Internal.Base16
-import Data.ByteString.Builder.Prim.Internal.UncheckedShifts
 
 import Data.Char (ord)
 
@@ -242,20 +241,20 @@
 {-# INLINE word16HexFixed #-}
 word16HexFixed :: FixedPrim Word16
 word16HexFixed =
-    (\x -> (fromIntegral $ x `shiftr_w16` 8, fromIntegral x))
+    (\x -> (fromIntegral $ x `shiftR` 8, fromIntegral x))
       >$< pairF word8HexFixed word8HexFixed
 
 -- | Encode a 'Word32' using 8 nibbles.
 {-# INLINE word32HexFixed #-}
 word32HexFixed :: FixedPrim Word32
 word32HexFixed =
-    (\x -> (fromIntegral $ x `shiftr_w32` 16, fromIntegral x))
+    (\x -> (fromIntegral $ x `shiftR` 16, fromIntegral x))
       >$< pairF word16HexFixed word16HexFixed
 -- | Encode a 'Word64' using 16 nibbles.
 {-# INLINE word64HexFixed #-}
 word64HexFixed :: FixedPrim Word64
 word64HexFixed =
-    (\x -> (fromIntegral $ x `shiftr_w64` 32, fromIntegral x))
+    (\x -> (fromIntegral $ x `shiftR` 32, fromIntegral x))
       >$< pairF word32HexFixed word32HexFixed
 
 -- | Encode a 'Int8' using 2 nibbles (hexadecimal digits).
@@ -287,5 +286,3 @@
 {-# INLINE doubleHexFixed #-}
 doubleHexFixed :: FixedPrim Double
 doubleHexFixed = encodeDoubleViaWord64F word64HexFixed
-
-
diff --git a/Data/ByteString/Builder/Prim/Binary.hs b/Data/ByteString/Builder/Prim/Binary.hs
--- a/Data/ByteString/Builder/Prim/Binary.hs
+++ b/Data/ByteString/Builder/Prim/Binary.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE CPP, BangPatterns #-}
+{-# LANGUAGE CPP #-}
 #if __GLASGOW_HASKELL__ >= 701
 {-# LANGUAGE Trustworthy #-}
 #endif
@@ -55,7 +55,6 @@
   ) where
 
 import Data.ByteString.Builder.Prim.Internal
-import Data.ByteString.Builder.Prim.Internal.UncheckedShifts
 import Data.ByteString.Builder.Prim.Internal.Floating
 
 import Foreign
@@ -87,8 +86,8 @@
 word16BE = word16Host
 #else
 word16BE = fixedPrim 2 $ \w p -> do
-    poke p               (fromIntegral (shiftr_w16 w 8) :: Word8)
-    poke (p `plusPtr` 1) (fromIntegral (w)              :: Word8)
+    poke p               (fromIntegral (shiftR w 8) :: Word8)
+    poke (p `plusPtr` 1) (fromIntegral w                :: Word8)
 #endif
 
 -- | Encoding 'Word16's in little endian format.
@@ -96,8 +95,8 @@
 word16LE :: FixedPrim Word16
 #ifdef WORDS_BIGENDIAN
 word16LE = fixedPrim 2 $ \w p -> do
-    poke p               (fromIntegral (w)              :: Word8)
-    poke (p `plusPtr` 1) (fromIntegral (shiftr_w16 w 8) :: Word8)
+    poke p               (fromIntegral w                :: Word8)
+    poke (p `plusPtr` 1) (fromIntegral (shiftR w 8) :: Word8)
 #else
 word16LE = word16Host
 #endif
@@ -109,10 +108,10 @@
 word32BE = word32Host
 #else
 word32BE = fixedPrim 4 $ \w p -> do
-    poke p               (fromIntegral (shiftr_w32 w 24) :: Word8)
-    poke (p `plusPtr` 1) (fromIntegral (shiftr_w32 w 16) :: Word8)
-    poke (p `plusPtr` 2) (fromIntegral (shiftr_w32 w  8) :: Word8)
-    poke (p `plusPtr` 3) (fromIntegral (w)               :: Word8)
+    poke p               (fromIntegral (shiftR w 24) :: Word8)
+    poke (p `plusPtr` 1) (fromIntegral (shiftR w 16) :: Word8)
+    poke (p `plusPtr` 2) (fromIntegral (shiftR w  8) :: Word8)
+    poke (p `plusPtr` 3) (fromIntegral w                 :: Word8)
 #endif
 
 -- | Encoding 'Word32's in little endian format.
@@ -120,10 +119,10 @@
 word32LE :: FixedPrim Word32
 #ifdef WORDS_BIGENDIAN
 word32LE = fixedPrim 4 $ \w p -> do
-    poke p               (fromIntegral (w)               :: Word8)
-    poke (p `plusPtr` 1) (fromIntegral (shiftr_w32 w  8) :: Word8)
-    poke (p `plusPtr` 2) (fromIntegral (shiftr_w32 w 16) :: Word8)
-    poke (p `plusPtr` 3) (fromIntegral (shiftr_w32 w 24) :: Word8)
+    poke p               (fromIntegral w                 :: Word8)
+    poke (p `plusPtr` 1) (fromIntegral (shiftR w  8) :: Word8)
+    poke (p `plusPtr` 2) (fromIntegral (shiftR w 16) :: Word8)
+    poke (p `plusPtr` 3) (fromIntegral (shiftR w 24) :: Word8)
 #else
 word32LE = word32Host
 #endif
@@ -144,26 +143,26 @@
 --
 word64BE =
     fixedPrim 8 $ \w p -> do
-        let a = fromIntegral (shiftr_w64 w 32) :: Word32
+        let a = fromIntegral (shiftR w 32) :: Word32
             b = fromIntegral w                 :: Word32
-        poke p               (fromIntegral (shiftr_w32 a 24) :: Word8)
-        poke (p `plusPtr` 1) (fromIntegral (shiftr_w32 a 16) :: Word8)
-        poke (p `plusPtr` 2) (fromIntegral (shiftr_w32 a  8) :: Word8)
-        poke (p `plusPtr` 3) (fromIntegral (a)               :: Word8)
-        poke (p `plusPtr` 4) (fromIntegral (shiftr_w32 b 24) :: Word8)
-        poke (p `plusPtr` 5) (fromIntegral (shiftr_w32 b 16) :: Word8)
-        poke (p `plusPtr` 6) (fromIntegral (shiftr_w32 b  8) :: Word8)
-        poke (p `plusPtr` 7) (fromIntegral (b)               :: Word8)
+        poke p               (fromIntegral (shiftR a 24) :: Word8)
+        poke (p `plusPtr` 1) (fromIntegral (shiftR a 16) :: Word8)
+        poke (p `plusPtr` 2) (fromIntegral (shiftR a  8) :: Word8)
+        poke (p `plusPtr` 3) (fromIntegral a                 :: Word8)
+        poke (p `plusPtr` 4) (fromIntegral (shiftR b 24) :: Word8)
+        poke (p `plusPtr` 5) (fromIntegral (shiftR b 16) :: Word8)
+        poke (p `plusPtr` 6) (fromIntegral (shiftR b  8) :: Word8)
+        poke (p `plusPtr` 7) (fromIntegral b                 :: Word8)
 #else
 word64BE = fixedPrim 8 $ \w p -> do
-    poke p               (fromIntegral (shiftr_w64 w 56) :: Word8)
-    poke (p `plusPtr` 1) (fromIntegral (shiftr_w64 w 48) :: Word8)
-    poke (p `plusPtr` 2) (fromIntegral (shiftr_w64 w 40) :: Word8)
-    poke (p `plusPtr` 3) (fromIntegral (shiftr_w64 w 32) :: Word8)
-    poke (p `plusPtr` 4) (fromIntegral (shiftr_w64 w 24) :: Word8)
-    poke (p `plusPtr` 5) (fromIntegral (shiftr_w64 w 16) :: Word8)
-    poke (p `plusPtr` 6) (fromIntegral (shiftr_w64 w  8) :: Word8)
-    poke (p `plusPtr` 7) (fromIntegral (w)               :: Word8)
+    poke p               (fromIntegral (shiftR w 56) :: Word8)
+    poke (p `plusPtr` 1) (fromIntegral (shiftR w 48) :: Word8)
+    poke (p `plusPtr` 2) (fromIntegral (shiftR w 40) :: Word8)
+    poke (p `plusPtr` 3) (fromIntegral (shiftR w 32) :: Word8)
+    poke (p `plusPtr` 4) (fromIntegral (shiftR w 24) :: Word8)
+    poke (p `plusPtr` 5) (fromIntegral (shiftR w 16) :: Word8)
+    poke (p `plusPtr` 6) (fromIntegral (shiftR w  8) :: Word8)
+    poke (p `plusPtr` 7) (fromIntegral w                 :: Word8)
 #endif
 #endif
 
@@ -174,26 +173,26 @@
 #if WORD_SIZE_IN_BITS < 64
 word64LE =
     fixedPrim 8 $ \w p -> do
-        let b = fromIntegral (shiftr_w64 w 32) :: Word32
+        let b = fromIntegral (shiftR w 32) :: Word32
             a = fromIntegral w                 :: Word32
-        poke (p)             (fromIntegral (a)               :: Word8)
-        poke (p `plusPtr` 1) (fromIntegral (shiftr_w32 a  8) :: Word8)
-        poke (p `plusPtr` 2) (fromIntegral (shiftr_w32 a 16) :: Word8)
-        poke (p `plusPtr` 3) (fromIntegral (shiftr_w32 a 24) :: Word8)
-        poke (p `plusPtr` 4) (fromIntegral (b)               :: Word8)
-        poke (p `plusPtr` 5) (fromIntegral (shiftr_w32 b  8) :: Word8)
-        poke (p `plusPtr` 6) (fromIntegral (shiftr_w32 b 16) :: Word8)
-        poke (p `plusPtr` 7) (fromIntegral (shiftr_w32 b 24) :: Word8)
+        poke (p)             (fromIntegral a                 :: Word8)
+        poke (p `plusPtr` 1) (fromIntegral (shiftR a  8) :: Word8)
+        poke (p `plusPtr` 2) (fromIntegral (shiftR a 16) :: Word8)
+        poke (p `plusPtr` 3) (fromIntegral (shiftR a 24) :: Word8)
+        poke (p `plusPtr` 4) (fromIntegral b                 :: Word8)
+        poke (p `plusPtr` 5) (fromIntegral (shiftR b  8) :: Word8)
+        poke (p `plusPtr` 6) (fromIntegral (shiftR b 16) :: Word8)
+        poke (p `plusPtr` 7) (fromIntegral (shiftR b 24) :: Word8)
 #else
 word64LE = fixedPrim 8 $ \w p -> do
-    poke p               (fromIntegral (w)               :: Word8)
-    poke (p `plusPtr` 1) (fromIntegral (shiftr_w64 w  8) :: Word8)
-    poke (p `plusPtr` 2) (fromIntegral (shiftr_w64 w 16) :: Word8)
-    poke (p `plusPtr` 3) (fromIntegral (shiftr_w64 w 24) :: Word8)
-    poke (p `plusPtr` 4) (fromIntegral (shiftr_w64 w 32) :: Word8)
-    poke (p `plusPtr` 5) (fromIntegral (shiftr_w64 w 40) :: Word8)
-    poke (p `plusPtr` 6) (fromIntegral (shiftr_w64 w 48) :: Word8)
-    poke (p `plusPtr` 7) (fromIntegral (shiftr_w64 w 56) :: Word8)
+    poke p               (fromIntegral w                 :: Word8)
+    poke (p `plusPtr` 1) (fromIntegral (shiftR w  8) :: Word8)
+    poke (p `plusPtr` 2) (fromIntegral (shiftR w 16) :: Word8)
+    poke (p `plusPtr` 3) (fromIntegral (shiftR w 24) :: Word8)
+    poke (p `plusPtr` 4) (fromIntegral (shiftR w 32) :: Word8)
+    poke (p `plusPtr` 5) (fromIntegral (shiftR w 40) :: Word8)
+    poke (p `plusPtr` 6) (fromIntegral (shiftR w 48) :: Word8)
+    poke (p `plusPtr` 7) (fromIntegral (shiftR w 56) :: Word8)
 #endif
 #else
 word64LE = word64Host
@@ -332,5 +331,3 @@
 {-# INLINE doubleHost #-}
 doubleHost :: FixedPrim Double
 doubleHost = storableToF
-
-
diff --git a/Data/ByteString/Builder/Prim/Internal.hs b/Data/ByteString/Builder/Prim/Internal.hs
--- a/Data/ByteString/Builder/Prim/Internal.hs
+++ b/Data/ByteString/Builder/Prim/Internal.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE ScopedTypeVariables, CPP, BangPatterns #-}
+{-# LANGUAGE ScopedTypeVariables, CPP #-}
 #if __GLASGOW_HASKELL__ >= 703
 {-# LANGUAGE Unsafe #-}
 #endif
@@ -64,6 +64,9 @@
   , (>$<)
   , (>*<)
 
+  -- * Helpers
+  , caseWordSize_32_64
+
   -- * Deprecated
   , boudedPrim
   ) where
@@ -71,10 +74,7 @@
 import Foreign
 import Prelude hiding (maxBound)
 
-#if !(__GLASGOW_HASKELL__ >= 612)
--- ghc-6.10 and older do not support {-# INLINE CONLIKE #-}
-#define CONLIKE
-#endif
+#include "MachDeps.h"
 
 ------------------------------------------------------------------------------
 -- Supporting infrastructure
@@ -187,7 +187,7 @@
 -- >contramapF f . contramapF g = contramapF (g . f)
 {-# INLINE CONLIKE contramapF #-}
 contramapF :: (b -> a) -> FixedPrim a -> FixedPrim b
-contramapF f (FP l io) = FP l (\x op -> io (f x) op)
+contramapF f (FP l io) = FP l (io . f)
 
 -- | Convert a 'FixedPrim' to a 'BoundedPrim'.
 {-# INLINE CONLIKE toB #-}
@@ -211,7 +211,7 @@
 storableToF = FP (sizeOf (undefined :: a)) (\x op -> poke (castPtr op) x)
 #else
 storableToF = FP (sizeOf (undefined :: a)) $ \x op ->
-    if (ptrToWordPtr op) `mod` (fromIntegral (alignment (undefined :: a))) == 0 then poke (castPtr op) x
+    if ptrToWordPtr op `mod` fromIntegral (alignment (undefined :: a)) == 0 then poke (castPtr op) x
     else with x $ \tp -> copyBytes op (castPtr tp) (sizeOf (undefined :: a))
 #endif
 
@@ -257,7 +257,7 @@
 -- >contramapB f . contramapB g = contramapB (g . f)
 {-# INLINE CONLIKE contramapB #-}
 contramapB :: (b -> a) -> BoundedPrim a -> BoundedPrim b
-contramapB f (BP b io) = BP b (\x op -> io (f x) op)
+contramapB f (BP b io) = BP b (io . f)
 
 -- | The 'BoundedPrim' that always results in the zero-length sequence.
 {-# INLINE CONLIKE emptyB #-}
@@ -298,3 +298,17 @@
 condB :: (a -> Bool) -> BoundedPrim a -> BoundedPrim a -> BoundedPrim a
 condB p be1 be2 =
     contramapB (\x -> if p x then Left x else Right x) (eitherB be1 be2)
+
+-- | Select an implementation depending on bitness.
+-- Throw a compile time error if bitness is neither 32 nor 64.
+{-# INLINE caseWordSize_32_64 #-}
+caseWordSize_32_64
+  :: a -- Value for 32-bit architecture
+  -> a -- Value for 64-bit architecture
+  -> a
+#if WORD_SIZE_IN_BITS == 32
+caseWordSize_32_64 = const
+#endif
+#if WORD_SIZE_IN_BITS == 64
+caseWordSize_32_64 = const id
+#endif
diff --git a/Data/ByteString/Builder/Prim/Internal/Floating.hs b/Data/ByteString/Builder/Prim/Internal/Floating.hs
--- a/Data/ByteString/Builder/Prim/Internal/Floating.hs
+++ b/Data/ByteString/Builder/Prim/Internal/Floating.hs
@@ -38,7 +38,7 @@
 encodeFloatViaWord32F :: FixedPrim Word32 -> FixedPrim Float
 encodeFloatViaWord32F w32fe
   | size w32fe < sizeOf (undefined :: Float) =
-      error $ "encodeFloatViaWord32F: encoding not wide enough"
+      error "encodeFloatViaWord32F: encoding not wide enough"
   | otherwise = fixedPrim (size w32fe) $ \x op -> do
       poke (castPtr op) x
       x' <- peek (castPtr op)
@@ -51,7 +51,7 @@
 encodeDoubleViaWord64F :: FixedPrim Word64 -> FixedPrim Double
 encodeDoubleViaWord64F w64fe
   | size w64fe < sizeOf (undefined :: Float) =
-      error $ "encodeDoubleViaWord64F: encoding not wide enough"
+      error "encodeDoubleViaWord64F: encoding not wide enough"
   | otherwise = fixedPrim (size w64fe) $ \x op -> do
       poke (castPtr op) x
       x' <- peek (castPtr op)
diff --git a/Data/ByteString/Builder/Prim/Internal/UncheckedShifts.hs b/Data/ByteString/Builder/Prim/Internal/UncheckedShifts.hs
deleted file mode 100644
--- a/Data/ByteString/Builder/Prim/Internal/UncheckedShifts.hs
+++ /dev/null
@@ -1,107 +0,0 @@
-{-# LANGUAGE CPP, MagicHash #-}
-#if __GLASGOW_HASKELL__ >= 703
-{-# LANGUAGE Unsafe #-}
-#endif
--- |
--- Copyright   : (c) 2010 Simon Meier
---
---               Original serialization code from 'Data.Binary.Builder':
---               (c) Lennart Kolmodin, Ross Patterson
---
--- License     : BSD3-style (see LICENSE)
---
--- Maintainer  : Simon Meier <iridcode@gmail.com>
--- Portability : GHC
---
--- Utilty module defining unchecked shifts.
---
--- These functions are undefined when the amount being shifted by is
--- greater than the size in bits of a machine Int#.-
---
-#if !defined(__HADDOCK__)
-#include "MachDeps.h"
-#endif
-
-module Data.ByteString.Builder.Prim.Internal.UncheckedShifts (
-    shiftr_w16
-  , shiftr_w32
-  , shiftr_w64
-  , shiftr_w
-
-  , caseWordSize_32_64
-  ) where
-
-
-#if !defined(__HADDOCK__)
-import GHC.Base
-import GHC.Word (Word32(..),Word16(..),Word64(..))
-
-#if WORD_SIZE_IN_BITS < 64 && __GLASGOW_HASKELL__ >= 608
-import GHC.Word (uncheckedShiftRL64#)
-#endif
-#else
-import Data.Word
-#endif
-
-import Foreign
-
-
-------------------------------------------------------------------------
--- Unchecked shifts
-
--- | Right-shift of a 'Word16'.
-{-# INLINE shiftr_w16 #-}
-shiftr_w16 :: Word16 -> Int -> Word16
-
--- | Right-shift of a 'Word32'.
-{-# INLINE shiftr_w32 #-}
-shiftr_w32 :: Word32 -> Int -> Word32
-
--- | Right-shift of a 'Word64'.
-{-# INLINE shiftr_w64 #-}
-shiftr_w64 :: Word64 -> Int -> Word64
-
--- | Right-shift of a 'Word'.
-{-# INLINE shiftr_w #-}
-shiftr_w :: Word -> Int -> Word
-#if WORD_SIZE_IN_BITS < 64
-shiftr_w w s = fromIntegral $ (`shiftr_w32` s) $ fromIntegral w
-#else
-shiftr_w w s = fromIntegral $ (`shiftr_w64` s) $ fromIntegral w
-#endif
-
-#if !defined(__HADDOCK__)
-shiftr_w16 (W16# w) (I# i) = W16# (w `uncheckedShiftRL#`   i)
-shiftr_w32 (W32# w) (I# i) = W32# (w `uncheckedShiftRL#`   i)
-
-#if WORD_SIZE_IN_BITS < 64
-shiftr_w64 (W64# w) (I# i) = W64# (w `uncheckedShiftRL64#` i)
-#else
-shiftr_w64 (W64# w) (I# i) = W64# (w `uncheckedShiftRL#` i)
-#endif
-
-#else
-shiftr_w16 = shiftR
-shiftr_w32 = shiftR
-shiftr_w64 = shiftR
-#endif
-
-
--- | Select an implementation depending on the bit-size of 'Word's.
--- Currently, it produces a runtime failure if the bitsize is different.
--- This is detected by the testsuite.
-{-# INLINE caseWordSize_32_64 #-}
-caseWordSize_32_64 :: a -- Value to use for 32-bit 'Word's
-                   -> a -- Value to use for 64-bit 'Word's
-                   -> a
-caseWordSize_32_64 f32 f64 =
-#if MIN_VERSION_base(4,7,0)
-  case finiteBitSize (undefined :: Word) of
-#else
-  case bitSize (undefined :: Word) of
-#endif
-    32 -> f32
-    64 -> f64
-    s  -> error $ "caseWordSize_32_64: unsupported Word bit-size " ++ show s
-
-
diff --git a/Data/ByteString/Char8.hs b/Data/ByteString/Char8.hs
--- a/Data/ByteString/Char8.hs
+++ b/Data/ByteString/Char8.hs
@@ -1,5 +1,4 @@
 {-# LANGUAGE CPP, BangPatterns #-}
-{-# LANGUAGE MagicHash #-}
 {-# OPTIONS_HADDOCK prune #-}
 #if __GLASGOW_HASKELL__ >= 701
 {-# LANGUAGE Trustworthy #-}
@@ -114,7 +113,9 @@
 
         -- ** Breaking strings
         take,                   -- :: Int -> ByteString -> ByteString
+        takeEnd,                -- :: Int -> ByteString -> ByteString
         drop,                   -- :: Int -> ByteString -> ByteString
+        dropEnd,                -- :: Int -> ByteString -> ByteString
         splitAt,                -- :: Int -> ByteString -> (ByteString, ByteString)
         takeWhile,              -- :: (Char -> Bool) -> ByteString -> ByteString
         takeWhileEnd,           -- :: (Char -> Bool) -> ByteString -> ByteString
@@ -171,11 +172,13 @@
         elemIndexEnd,           -- :: Char -> ByteString -> Maybe Int
         findIndex,              -- :: (Char -> Bool) -> ByteString -> Maybe Int
         findIndices,            -- :: (Char -> Bool) -> ByteString -> [Int]
+        findIndexEnd,           -- :: (Char -> Bool) -> ByteString -> Maybe Int
         count,                  -- :: Char -> ByteString -> Int
 
         -- * Zipping and unzipping ByteStrings
         zip,                    -- :: ByteString -> ByteString -> [(Char,Char)]
         zipWith,                -- :: (Char -> Char -> c) -> ByteString -> ByteString -> [c]
+        packZipWith,            -- :: (Char -> Char -> Char) -> ByteString -> ByteString -> ByteString
         unzip,                  -- :: [(Char,Char)] -> (ByteString,ByteString)
 
         -- * Ordered ByteStrings
@@ -248,9 +251,9 @@
 -- Listy functions transparently exported
 import Data.ByteString (empty,null,length,tail,init,append
                        ,inits,tails,reverse,transpose
-                       ,concat,take,drop,splitAt,intercalate
-                       ,sort,isPrefixOf,isSuffixOf,isInfixOf
-                       ,stripPrefix,stripSuffix
+                       ,concat,take,takeEnd,drop,dropEnd,splitAt
+                       ,intercalate,sort,isPrefixOf,isSuffixOf
+                       ,isInfixOf,stripPrefix,stripSuffix
                        ,breakSubstring,copy,group
 
                        ,getLine, getContents, putStr, interact
@@ -263,6 +266,10 @@
 
 import Data.ByteString.Internal
 
+#if !(MIN_VERSION_base(4,8,0))
+import Control.Applicative ((<$>))
+#endif
+
 import Data.Char    ( isSpace )
 #if MIN_VERSION_base(4,9,0)
 -- See bytestring #70
@@ -363,12 +370,12 @@
 -- (typically the right-identity of the operator), and a packed string,
 -- reduces the packed string using the binary operator, from right to left.
 foldr :: (Char -> a -> a) -> a -> ByteString -> a
-foldr f = B.foldr (\c a -> f (w2c c) a)
+foldr f = B.foldr (f . w2c)
 {-# INLINE foldr #-}
 
 -- | 'foldr'' is a strict variant of foldr
 foldr' :: (Char -> a -> a) -> a -> ByteString -> a
-foldr' f = B.foldr' (\c a -> f (w2c c) a)
+foldr' f = B.foldr' (f . w2c)
 {-# INLINE foldr' #-}
 
 -- | 'foldl1' is a variant of 'foldl' that has no starting value
@@ -480,7 +487,7 @@
 --
 -- > unfoldr (\x -> if x <= '9' then Just (x, succ x) else Nothing) '0' == "0123456789"
 unfoldr :: (a -> Maybe (Char, a)) -> a -> ByteString
-unfoldr f x = B.unfoldr (fmap k . f) x
+unfoldr f = B.unfoldr (fmap k . f)
     where k (i, j) = (c2w i, j)
 
 -- | /O(n)/ Like 'unfoldr', 'unfoldrN' builds a ByteString from a seed
@@ -678,8 +685,9 @@
 -- element, or 'Nothing' if there is no such element. The following
 -- holds:
 --
--- > elemIndexEnd c xs ==
--- > (-) (length xs - 1) `fmap` elemIndex c (reverse xs)
+-- > elemIndexEnd c xs = case elemIndex c (reverse xs) of
+-- >   Nothing -> Nothing
+-- >   Just i  -> Just (length xs - 1 - i)
 --
 elemIndexEnd :: Char -> ByteString -> Maybe Int
 elemIndexEnd = B.elemIndexEnd . c2w
@@ -697,6 +705,15 @@
 findIndex f = B.findIndex (f . w2c)
 {-# INLINE [1] findIndex #-}
 
+-- | /O(n)/ The 'findIndexEnd' function takes a predicate and a 'ByteString' and
+-- returns the index of the last element in the ByteString
+-- satisfying the predicate.
+--
+-- @since 0.11.1.0
+findIndexEnd :: (Char -> Bool) -> ByteString -> Maybe Int
+findIndexEnd f = B.findIndexEnd (f . w2c)
+{-# INLINE [1] findIndexEnd #-}
+
 -- | The 'findIndices' function extends 'findIndex', by returning the
 -- indices of all elements satisfying the predicate, in ascending order.
 findIndices :: (Char -> Bool) -> ByteString -> [Int]
@@ -826,9 +843,11 @@
 -- equivalent to a pair of 'unpack' operations, and so space
 -- usage may be large for multi-megabyte ByteStrings
 zip :: ByteString -> ByteString -> [(Char,Char)]
-zip ps qs
-    | B.null ps || B.null qs = []
-    | otherwise = (unsafeHead ps, unsafeHead qs) : zip (B.unsafeTail ps) (B.unsafeTail qs)
+zip ps qs = case uncons ps of
+  Nothing         -> []
+  Just (psH, psT) -> case uncons qs of
+    Nothing         -> []
+    Just (qsH, qsT) -> (psH, qsH) : zip psT qsT
 
 -- | 'zipWith' generalises 'zip' by zipping with the function given as
 -- the first argument, instead of a tupling function.  For example,
@@ -837,6 +856,16 @@
 zipWith :: (Char -> Char -> a) -> ByteString -> ByteString -> [a]
 zipWith f = B.zipWith ((. w2c) . f . w2c)
 
+-- | A specialised version of `zipWith` for the common case of a
+-- simultaneous map over two ByteStrings, to build a 3rd.
+--
+-- @since 0.11.1.0
+packZipWith :: (Char -> Char -> Char) -> ByteString -> ByteString -> ByteString
+packZipWith f = B.packZipWith f'
+    where
+        f' c1 c2 = c2w $ f (w2c c1) (w2c c2)
+{-# INLINE packZipWith #-}
+
 -- | 'unzip' transforms a list of pairs of Chars into a pair of
 -- ByteStrings. Note that this performs two 'pack' operations.
 unzip :: [(Char,Char)] -> (ByteString,ByteString)
@@ -865,7 +894,7 @@
 -- > break isSpace == breakSpace
 --
 breakSpace :: ByteString -> (ByteString,ByteString)
-breakSpace (BS x l) = accursedUnutterablePerformIO $ withForeignPtr x $ \p -> do
+breakSpace (BS x l) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p -> do
     i <- firstspace p 0 l
     return $! case () of {_
         | i == 0    -> (empty, BS x l)
@@ -888,7 +917,7 @@
 --
 -- @since 0.10.12.0
 dropSpace :: ByteString -> ByteString
-dropSpace (BS x l) = accursedUnutterablePerformIO $ withForeignPtr x $ \p -> do
+dropSpace (BS x l) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p -> do
     i <- firstnonspace p 0 l
     return $! if i == l then empty else BS (plusForeignPtr x i) (l-i)
 {-# INLINE dropSpace #-}
@@ -914,7 +943,7 @@
 -- but it is more efficient than using multiple reverses.
 --
 dropSpaceEnd :: ByteString -> ByteString
-dropSpaceEnd (BS x l) = accursedUnutterablePerformIO $ withForeignPtr x $ \p -> do
+dropSpaceEnd (BS x l) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p -> do
     i <- lastnonspace p (l-1)
     return $! if i == (-1) then empty else BS x (i+1)
 {-# INLINE dropSpaceEnd #-}
@@ -947,7 +976,7 @@
     nl = c2w '\n'
     -- It is important to remain lazy in the tail of the list.  The caller
     -- might only want the first few lines.
-    go !f !len = accursedUnutterablePerformIO $ withForeignPtr f $ \p -> do
+    go !f !len = accursedUnutterablePerformIO $ unsafeWithForeignPtr f $ \p -> do
         q <- memchr p nl $! fromIntegral len
         if q == nullPtr
             then return [BS f len]
@@ -1050,7 +1079,7 @@
           combine1 _ [n] = n
           combine1 b ns  = combine1 (b*b) $ combine2 b ns
 
-          combine2 b (n:m:ns) = let t = m*b + n in t `seq` (t : combine2 b ns)
+          combine2 b (n:m:ns) = let !t = m*b + n in t : combine2 b ns
           combine2 _ ns       = ns
 
 ------------------------------------------------------------------------
diff --git a/Data/ByteString/Internal.hs b/Data/ByteString/Internal.hs
--- a/Data/ByteString/Internal.hs
+++ b/Data/ByteString/Internal.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE CPP, ForeignFunctionInterface, BangPatterns #-}
 {-# LANGUAGE UnliftedFFITypes, MagicHash,
             UnboxedTuples, DeriveDataTypeable #-}
+{-# LANGUAGE TupleSections #-}
 {-# LANGUAGE TypeFamilies #-}
 #if __GLASGOW_HASKELL__ >= 800
 {-# LANGUAGE PatternSynonyms, ViewPatterns #-}
@@ -37,6 +38,9 @@
 #endif
         ), -- instances: Eq, Ord, Show, Read, Data, Typeable
 
+        -- * Internal indexing
+        findIndexOrLength,
+
         -- * Conversion with lists: packing and unpacking
         packBytes, packUptoLenBytes, unsafePackLenBytes,
         packChars, packUptoLenChars, unsafePackLenChars,
@@ -89,14 +93,17 @@
         accursedUnutterablePerformIO, -- :: IO a -> a
 
         -- * Exported compatibility shim
-        plusForeignPtr
+        plusForeignPtr,
+        unsafeWithForeignPtr
   ) where
 
 import Prelude hiding (concat, null)
 import qualified Data.List as List
 
+import Control.Monad            (void)
+
 import Foreign.ForeignPtr       (ForeignPtr, withForeignPtr)
-import Foreign.Ptr              (Ptr, FunPtr, plusPtr)
+import Foreign.Ptr              (Ptr, FunPtr, plusPtr, minusPtr)
 import Foreign.Storable         (Storable(..))
 
 #if MIN_VERSION_base(4,5,0) || __GLASGOW_HASKELL__ >= 703
@@ -108,10 +115,10 @@
 import Foreign.C.String         (CString)
 
 #if MIN_VERSION_base(4,13,0)
-import Data.Semigroup           (Semigroup (sconcat))
+import Data.Semigroup           (Semigroup (sconcat, stimes))
 import Data.List.NonEmpty       (NonEmpty ((:|)))
 #elif MIN_VERSION_base(4,9,0)
-import Data.Semigroup           (Semigroup ((<>), sconcat))
+import Data.Semigroup           (Semigroup ((<>), sconcat, stimes))
 import Data.List.NonEmpty       (NonEmpty ((:|)))
 #endif
 
@@ -126,8 +133,9 @@
 
 import Control.Exception        (assert)
 
+import Data.Bits                ((.&.))
 import Data.Char                (ord)
-import Data.Word                (Word8)
+import Data.Word
 
 import Data.Typeable            (Typeable)
 import Data.Data                (Data(..), mkNoRepType)
@@ -146,27 +154,40 @@
 
 import GHC.Prim                 (Addr#)
 
-#if __GLASGOW_HASKELL__ >= 611
 import GHC.IO                   (IO(IO),unsafeDupablePerformIO)
-#else
-import GHC.IOBase               (IO(IO),RawBuffer,unsafeDupablePerformIO)
-#endif
 
 import GHC.ForeignPtr           (ForeignPtr(ForeignPtr)
-                                ,newForeignPtr_, mallocPlainForeignPtrBytes)
+#if __GLASGOW_HASKELL__ < 900
+                                , newForeignPtr_
+#endif
+                                , mallocPlainForeignPtrBytes)
+
 #if MIN_VERSION_base(4,10,0)
 import GHC.ForeignPtr           (plusForeignPtr)
 #else
-import GHC.Types                (Int (..))
 import GHC.Prim                 (plusAddr#)
 #endif
 
 #if __GLASGOW_HASKELL__ >= 811
 import GHC.CString              (cstringLength#)
 import GHC.ForeignPtr           (ForeignPtrContents(FinalPtr))
-#endif
+#else
 import GHC.Ptr                  (Ptr(..), castPtr)
+#endif
 
+#if (__GLASGOW_HASKELL__ < 802) || (__GLASGOW_HASKELL__ >= 811)
+import GHC.Types                (Int (..))
+#endif
+
+#if MIN_VERSION_base(4,15,0)
+import GHC.ForeignPtr           (unsafeWithForeignPtr)
+#endif
+
+#if !MIN_VERSION_base(4,15,0)
+unsafeWithForeignPtr :: ForeignPtr a -> (Ptr a -> IO b) -> IO b
+unsafeWithForeignPtr = withForeignPtr
+#endif
+
 -- CFILES stuff is Hugs only
 {-# CFILES cbits/fpstring.c #-}
 
@@ -218,10 +239,12 @@
 -- as the base will be manipulated by 'plusForeignPtr' instead.
 --
 pattern PS :: ForeignPtr Word8 -> Int -> Int -> ByteString
-pattern PS fp zero len <- BS fp (((,) 0) -> (zero, len)) where
+pattern PS fp zero len <- BS fp ((0,) -> (zero, len)) where
   PS fp o len = BS (plusForeignPtr fp o) len
+#if __GLASGOW_HASKELL__ >= 802
 {-# COMPLETE PS #-}
 #endif
+#endif
 
 instance Eq  ByteString where
     (==)    = eq
@@ -233,6 +256,7 @@
 instance Semigroup ByteString where
     (<>)    = append
     sconcat (b:|bs) = concat (b:bs)
+    stimes = times
 #endif
 
 instance Monoid ByteString where
@@ -274,6 +298,24 @@
   dataTypeOf _   = mkNoRepType "Data.ByteString.ByteString"
 
 ------------------------------------------------------------------------
+-- Internal indexing
+
+-- | 'findIndexOrLength' is a variant of findIndex, that returns the length
+-- of the string if no element is found, rather than Nothing.
+findIndexOrLength :: (Word8 -> Bool) -> ByteString -> Int
+findIndexOrLength k (BS x l) =
+    accursedUnutterablePerformIO $ withForeignPtr x g
+  where
+    g ptr = go 0
+      where
+        go !n | n >= l    = return l
+              | otherwise = do w <- peek $ ptr `plusPtr` n
+                               if k w
+                                 then return n
+                                 else go (n+1)
+{-# INLINE findIndexOrLength #-}
+
+------------------------------------------------------------------------
 -- Packing and unpacking from lists
 
 packBytes :: [Word8] -> ByteString
@@ -357,19 +399,21 @@
 
 packUptoLenBytes :: Int -> [Word8] -> (ByteString, [Word8])
 packUptoLenBytes len xs0 =
-    unsafeCreateUptoN' len $ \p -> go p len xs0
-  where
-    go !_ !n []     = return (len-n, [])
-    go !_ !0 xs     = return (len,   xs)
-    go !p !n (x:xs) = poke p x >> go (p `plusPtr` 1) (n-1) xs
+    unsafeCreateUptoN' len $ \p0 ->
+      let p_end = plusPtr p0 len
+          go !p []              = return (p `minusPtr` p0, [])
+          go !p xs | p == p_end = return (len, xs)
+          go !p (x:xs)          = poke p x >> go (p `plusPtr` 1) xs
+      in go p0 xs0
 
 packUptoLenChars :: Int -> [Char] -> (ByteString, [Char])
 packUptoLenChars len cs0 =
-    unsafeCreateUptoN' len $ \p -> go p len cs0
-  where
-    go !_ !n []     = return (len-n, [])
-    go !_ !0 cs     = return (len,   cs)
-    go !p !n (c:cs) = poke p (c2w c) >> go (p `plusPtr` 1) (n-1) cs
+    unsafeCreateUptoN' len $ \p0 ->
+      let p_end = plusPtr p0 len
+          go !p []              = return (p `minusPtr` p0, [])
+          go !p cs | p == p_end = return (len, cs)
+          go !p (c:cs)          = poke p (c2w c) >> go (p `plusPtr` 1) cs
+      in go p0 cs0
 
 -- Unpacking bytestrings into lists efficiently is a tradeoff: on the one hand
 -- we would like to write a tight loop that just blasts the list into memory, on
@@ -413,7 +457,7 @@
 
 unpackAppendBytesStrict :: ByteString -> [Word8] -> [Word8]
 unpackAppendBytesStrict (BS fp len) xs =
-    accursedUnutterablePerformIO $ withForeignPtr fp $ \base ->
+    accursedUnutterablePerformIO $ unsafeWithForeignPtr fp $ \base ->
       loop (base `plusPtr` (-1)) (base `plusPtr` (-1+len)) xs
   where
     loop !sentinal !p acc
@@ -423,7 +467,7 @@
 
 unpackAppendCharsStrict :: ByteString -> [Char] -> [Char]
 unpackAppendCharsStrict (BS fp len) xs =
-    accursedUnutterablePerformIO $ withForeignPtr fp $ \base ->
+    accursedUnutterablePerformIO $ unsafeWithForeignPtr fp $ \base ->
       loop (base `plusPtr` (-1)) (base `plusPtr` (-1+len)) xs
   where
     loop !sentinal !p acc
@@ -454,7 +498,7 @@
                -> Int -- ^ Offset
                -> Int -- ^ Length
                -> ByteString
-fromForeignPtr fp o len = BS (plusForeignPtr fp o) len
+fromForeignPtr fp o = BS (plusForeignPtr fp o)
 {-# INLINE fromForeignPtr #-}
 
 fromForeignPtr0 :: ForeignPtr Word8
@@ -571,8 +615,8 @@
 compareBytes (BS _   0)    (BS _   0)    = EQ  -- short cut for empty strings
 compareBytes (BS fp1 len1) (BS fp2 len2) =
     accursedUnutterablePerformIO $
-      withForeignPtr fp1 $ \p1 ->
-      withForeignPtr fp2 $ \p2 -> do
+      unsafeWithForeignPtr fp1 $ \p1 ->
+      unsafeWithForeignPtr fp2 $ \p2 -> do
         i <- memcmp p1 p2 (min len1 len2)
         return $! case i `compare` 0 of
                     EQ  -> len1 `compare` len2
@@ -584,8 +628,8 @@
 append (BS fp1 len1) (BS fp2 len2) =
     unsafeCreate (len1+len2) $ \destptr1 -> do
       let destptr2 = destptr1 `plusPtr` len1
-      withForeignPtr fp1 $ \p1 -> memcpy destptr1 p1 len1
-      withForeignPtr fp2 $ \p2 -> memcpy destptr2 p2 len2
+      unsafeWithForeignPtr fp1 $ \p1 -> memcpy destptr1 p1 len1
+      unsafeWithForeignPtr fp2 $ \p2 -> memcpy destptr2 p2 len2
 
 concat :: [ByteString] -> ByteString
 concat = \bss0 -> goLen0 bss0 bss0
@@ -627,7 +671,7 @@
     goCopy []                  !_   = return ()
     goCopy (BS _  0  :bss) !ptr = goCopy bss ptr
     goCopy (BS fp len:bss) !ptr = do
-      withForeignPtr fp $ \p -> memcpy ptr p len
+      unsafeWithForeignPtr fp $ \p -> memcpy ptr p len
       goCopy bss (ptr `plusPtr` len)
 {-# NOINLINE concat #-}
 
@@ -638,6 +682,33 @@
    concat [x] = x
  #-}
 
+#if MIN_VERSION_base(4,9,0)
+-- | /O(log n)/ Repeats the given ByteString n times.
+times :: Integral a => a -> ByteString -> ByteString
+times n (BS fp len)
+  | n < 0 = error "stimes: non-negative multiplier expected"
+  | n == 0 = mempty
+  | n == 1 = BS fp len
+  | len == 0 = mempty
+  | len == 1 = unsafeCreate size $ \destptr ->
+    unsafeWithForeignPtr fp $ \p -> do
+      byte <- peek p
+      void $ memset destptr byte (fromIntegral size)
+  | otherwise = unsafeCreate size $ \destptr ->
+    unsafeWithForeignPtr fp $ \p -> do
+      memcpy destptr p len
+      fillFrom destptr len
+  where
+    size = len * fromIntegral n
+
+    fillFrom :: Ptr Word8 -> Int -> IO ()
+    fillFrom destptr copied
+      | 2 * copied < size = do
+        memcpy (destptr `plusPtr` copied) destptr copied
+        fillFrom destptr (copied * 2)
+      | otherwise = memcpy (destptr `plusPtr` copied) destptr (size - copied)
+#endif
+
 -- | Add two non-negative numbers. Errors out on overflow.
 checkedAdd :: String -> Int -> Int -> Int
 checkedAdd fun x y
@@ -661,28 +732,22 @@
 {-# INLINE c2w #-}
 
 -- | Selects words corresponding to white-space characters in the Latin-1 range
--- ordered by frequency.
 isSpaceWord8 :: Word8 -> Bool
-isSpaceWord8 w =
-    w == 0x20 ||
-    w == 0x0A || -- LF, \n
-    w == 0x09 || -- HT, \t
-    w == 0x0C || -- FF, \f
-    w == 0x0D || -- CR, \r
-    w == 0x0B || -- VT, \v
-    w == 0xA0    -- spotted by QC..
+isSpaceWord8 w8 =
+    -- Avoid the cost of narrowing arithmetic results to Word8,
+    -- the conversion from Word8 to Word is free.
+    let w :: Word
+        !w = fromIntegral w8
+     in w .&. 0x50 == 0    -- Quick non-whitespace filter
+        && w - 0x21 > 0x7e -- Second non-whitespace filter
+        && ( w == 0x20     -- SP
+          || w == 0xa0     -- NBSP
+          || w - 0x09 < 5) -- HT, NL, VT, FF, CR
 {-# INLINE isSpaceWord8 #-}
 
 -- | Selects white-space characters in the Latin-1 range
 isSpaceChar8 :: Char -> Bool
-isSpaceChar8 c =
-    c == ' '     ||
-    c == '\t'    ||
-    c == '\n'    ||
-    c == '\r'    ||
-    c == '\f'    ||
-    c == '\v'    ||
-    c == '\xa0'
+isSpaceChar8 = isSpaceWord8 . c2w
 {-# INLINE isSpaceChar8 #-}
 
 overflowError :: String -> a
@@ -733,7 +798,7 @@
     :: Ptr Word8 -> CInt -> CSize -> IO (Ptr Word8)
 
 memchr :: Ptr Word8 -> Word8 -> CSize -> IO (Ptr Word8)
-memchr p w s = c_memchr p (fromIntegral w) s
+memchr p w = c_memchr p (fromIntegral w)
 
 foreign import ccall unsafe "string.h memcmp" c_memcmp
     :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt
@@ -745,7 +810,7 @@
     :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)
 
 memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()
-memcpy p q s = c_memcpy p q (fromIntegral s) >> return ()
+memcpy p q s = void $ c_memcpy p q (fromIntegral s)
 
 {-
 foreign import ccall unsafe "string.h memmove" c_memmove
@@ -760,7 +825,7 @@
     :: Ptr Word8 -> CInt -> CSize -> IO (Ptr Word8)
 
 memset :: Ptr Word8 -> Word8 -> CSize -> IO (Ptr Word8)
-memset p w s = c_memset p (fromIntegral w) s
+memset p w = c_memset p (fromIntegral w)
 
 -- ---------------------------------------------------------------------
 --
diff --git a/Data/ByteString/Lazy.hs b/Data/ByteString/Lazy.hs
--- a/Data/ByteString/Lazy.hs
+++ b/Data/ByteString/Lazy.hs
@@ -102,6 +102,7 @@
         all,                    -- :: (Word8 -> Bool) -> ByteString -> Bool
         maximum,                -- :: ByteString -> Word8
         minimum,                -- :: ByteString -> Word8
+        compareLength,          -- :: ByteString -> Int64 -> Ordering
 
         -- * Building ByteStrings
         -- ** Scans
@@ -178,6 +179,7 @@
         -- * Zipping and unzipping ByteStrings
         zip,                    -- :: ByteString -> ByteString -> [(Word8,Word8)]
         zipWith,                -- :: (Word8 -> Word8 -> c) -> ByteString -> ByteString -> [c]
+        packZipWith,            -- :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> ByteString
         unzip,                  -- :: [(Word8,Word8)] -> (ByteString,ByteString)
 
         -- * Ordered ByteStrings
@@ -237,7 +239,6 @@
 import System.IO.Error          (mkIOError, illegalOperationErrorType)
 import System.IO.Unsafe
 
-import Foreign.ForeignPtr       (withForeignPtr)
 import Foreign.Ptr
 import Foreign.Storable
 
@@ -265,11 +266,11 @@
 
 -- | /O(c)/ Convert a list of strict 'ByteString' into a lazy 'ByteString'
 fromChunks :: [P.ByteString] -> ByteString
-fromChunks cs = L.foldr chunk Empty cs
+fromChunks = L.foldr chunk Empty
 
 -- | /O(c)/ Convert a lazy 'ByteString' into a list of strict 'ByteString'
 toChunks :: ByteString -> [P.ByteString]
-toChunks cs = foldrChunks (:) [] cs
+toChunks = foldrChunks (:) []
 
 ------------------------------------------------------------------------
 
@@ -299,8 +300,8 @@
 
 -- | /O(c)/ 'length' returns the length of a ByteString as an 'Int64'
 length :: ByteString -> Int64
-length cs = foldlChunks (\n c -> n + fromIntegral (S.length c)) 0 cs
-{-# INLINE length #-}
+length = foldlChunks (\n c -> n + fromIntegral (S.length c)) 0
+{-# INLINE [1] length #-}
 
 infixr 5 `cons`, `cons'` --same as list (:)
 infixl 5 `snoc`
@@ -308,7 +309,7 @@
 -- | /O(1)/ 'cons' is analogous to '(Prelude.:)' for lists.
 --
 cons :: Word8 -> ByteString -> ByteString
-cons c cs = Chunk (S.singleton c) cs
+cons c = Chunk (S.singleton c)
 {-# INLINE cons #-}
 
 -- | /O(1)/ Unlike 'cons', 'cons'' is
@@ -394,7 +395,7 @@
 -- | /O(n)/ 'map' @f xs@ is the ByteString obtained by applying @f@ to each
 -- element of @xs@.
 map :: (Word8 -> Word8) -> ByteString -> ByteString
-map f s = go s
+map f = go
     where
         go Empty        = Empty
         go (Chunk x xs) = Chunk y ys
@@ -405,7 +406,7 @@
 
 -- | /O(n)/ 'reverse' @xs@ returns the elements of @xs@ in reverse order.
 reverse :: ByteString -> ByteString
-reverse cs0 = rev Empty cs0
+reverse = rev Empty
   where rev a Empty        = a
         rev a (Chunk c cs) = rev (Chunk (S.reverse c) a) cs
 {-# INLINE reverse #-}
@@ -419,7 +420,7 @@
                                    (foldrChunks (Chunk . intersperse') Empty cs)
   where intersperse' :: P.ByteString -> P.ByteString
         intersperse' (S.BS fp l) =
-          S.unsafeCreate (2*l) $ \p' -> withForeignPtr fp $ \p -> do
+          S.unsafeCreate (2*l) $ \p' -> S.unsafeWithForeignPtr fp $ \p -> do
             poke p' w
             S.c_intersperse (p' `plusPtr` 1) p (fromIntegral l) w
 
@@ -437,14 +438,14 @@
 -- the left-identity of the operator), and a ByteString, reduces the
 -- ByteString using the binary operator, from left to right.
 foldl :: (a -> Word8 -> a) -> a -> ByteString -> a
-foldl f z = go z
+foldl f = go
   where go a Empty        = a
         go a (Chunk c cs) = go (S.foldl f a c) cs
 {-# INLINE foldl #-}
 
 -- | 'foldl'' is like 'foldl', but strict in the accumulator.
 foldl' :: (a -> Word8 -> a) -> a -> ByteString -> a
-foldl' f z = go z
+foldl' f = go
   where go !a Empty        = a
         go !a (Chunk c cs) = go (S.foldl' f a c) cs
 {-# INLINE foldl' #-}
@@ -453,7 +454,7 @@
 -- (typically the right-identity of the operator), and a ByteString,
 -- reduces the ByteString using the binary operator, from right to left.
 foldr :: (Word8 -> a -> a) -> a -> ByteString -> a
-foldr k z = foldrChunks (flip (S.foldr k)) z
+foldr k = foldrChunks (flip (S.foldr k))
 {-# INLINE foldr #-}
 
 -- | 'foldl1' is a variant of 'foldl' that has no starting value
@@ -500,14 +501,14 @@
 -- | /O(n)/ Applied to a predicate and a ByteString, 'any' determines if
 -- any element of the 'ByteString' satisfies the predicate.
 any :: (Word8 -> Bool) -> ByteString -> Bool
-any f cs = foldrChunks (\c rest -> S.any f c || rest) False cs
+any f = foldrChunks (\c rest -> S.any f c || rest) False
 {-# INLINE any #-}
 -- todo fuse
 
 -- | /O(n)/ Applied to a predicate and a 'ByteString', 'all' determines
 -- if all elements of the 'ByteString' satisfy the predicate.
 all :: (Word8 -> Bool) -> ByteString -> Bool
-all f cs = foldrChunks (\c rest -> S.all f c && rest) True cs
+all f = foldrChunks (\c rest -> S.all f c && rest) True
 {-# INLINE all #-}
 -- todo fuse
 
@@ -525,12 +526,54 @@
                                      (S.minimum c) cs
 {-# INLINE minimum #-}
 
+-- | /O(c)/ 'compareLength' compares the length of a 'ByteString'
+-- to an 'Int64'
+--
+-- @since 0.11.1.0
+compareLength :: ByteString -> Int64 -> Ordering
+compareLength _ toCmp | toCmp < 0 = GT
+compareLength Empty toCmp         = compare 0 toCmp
+compareLength (Chunk c cs) toCmp  = compareLength cs (toCmp - fromIntegral (S.length c))
+{-# INLINE compareLength #-}
+
+{-# RULES
+"ByteString.Lazy length/compareN -> compareLength" [~1] forall t n.
+  compare (length t) n = compareLength t n
+"ByteString.Lazy compareN/length -> compareLength" [~1] forall t n.
+  -- compare EQ LT = GT and vice versa
+  compare n (length t) = compare EQ $ compareLength t n
+"ByteString.Lazy length/==N -> compareLength/==EQ" [~1] forall t n.
+   length t == n = compareLength t n == EQ
+"ByteString.Lazy N==/length -> compareLength/==EQ" [~1] forall t n.
+   n == length t = compareLength t n == EQ
+"ByteString.Lazy length//=N -> compareLength//=EQ" [~1] forall t n.
+   length t /= n = compareLength t n /= EQ
+"ByteString.Lazy N/=/length -> compareLength//=EQ" [~1] forall t n.
+   n /= length t = compareLength t n /= EQ
+"ByteString.Lazy length/<N -> compareLength/==LT" [~1] forall t n.
+   length t < n = compareLength t n == LT
+"ByteString.Lazy >N/length -> compareLength/==LT" [~1] forall t n.
+   n > length t = compareLength t n == LT
+"ByteString.Lazy length/<=N -> compareLength//=GT" [~1] forall t n.
+   length t <= n = compareLength t n /= GT
+"ByteString.Lazy <=N/length -> compareLength//=GT" [~1] forall t n.
+   n >= length t = compareLength t n /= GT
+"ByteString.Lazy length/>N -> compareLength/==GT" [~1] forall t n.
+   length t > n = compareLength t n == GT
+"ByteString.Lazy <N/length -> compareLength/==GT" [~1] forall t n.
+   n < length t = compareLength t n == GT
+"ByteString.Lazy length/>=N -> compareLength//=LT" [~1] forall t n.
+   length t >= n = compareLength t n /= LT
+"ByteString.Lazy >=N/length -> compareLength//=LT" [~1] forall t n.
+   n <= length t = compareLength t n /= LT
+  #-}
+
 -- | The 'mapAccumL' function behaves like a combination of 'map' and
 -- 'foldl'; it applies a function to each element of a ByteString,
 -- passing an accumulating parameter from left to right, and returning a
 -- final value of this accumulator together with the new ByteString.
 mapAccumL :: (acc -> Word8 -> (acc, Word8)) -> acc -> ByteString -> (acc, ByteString)
-mapAccumL f s0 = go s0
+mapAccumL f = go
   where
     go s Empty        = (s, Empty)
     go s (Chunk c cs) = (s'', Chunk c' cs')
@@ -542,7 +585,7 @@
 -- passing an accumulating parameter from right to left, and returning a
 -- final value of this accumulator together with the new ByteString.
 mapAccumR :: (acc -> Word8 -> (acc, Word8)) -> acc -> ByteString -> (acc, ByteString)
-mapAccumR f s0 = go s0
+mapAccumR f = go
   where
     go s Empty        = (s, Empty)
     go s (Chunk c cs) = (s'', Chunk c' cs')
@@ -623,7 +666,7 @@
 -- prepending to the ByteString and @b@ is used as the next element in a
 -- recursive call.
 unfoldr :: (a -> Maybe (Word8, a)) -> a -> ByteString
-unfoldr f z = unfoldChunk 32 z
+unfoldr f = unfoldChunk 32
   where unfoldChunk n x =
           case S.unfoldrN n f x of
             (c, Nothing)
@@ -676,10 +719,10 @@
 -- returns the longest (possibly empty) prefix of elements
 -- satisfying the predicate.
 takeWhile :: (Word8 -> Bool) -> ByteString -> ByteString
-takeWhile f cs0 = takeWhile' cs0
+takeWhile f = takeWhile'
   where takeWhile' Empty        = Empty
         takeWhile' (Chunk c cs) =
-          case findIndexOrEnd (not . f) c of
+          case S.findIndexOrLength (not . f) c of
             0                  -> Empty
             n | n < S.length c -> Chunk (S.take n c) Empty
               | otherwise      -> Chunk c (takeWhile' cs)
@@ -688,10 +731,10 @@
 -- drops the longest (possibly empty) prefix of elements
 -- satisfying the predicate and returns the remainder.
 dropWhile :: (Word8 -> Bool) -> ByteString -> ByteString
-dropWhile f cs0 = dropWhile' cs0
+dropWhile f = dropWhile'
   where dropWhile' Empty        = Empty
         dropWhile' (Chunk c cs) =
-          case findIndexOrEnd (not . f) c of
+          case S.findIndexOrLength (not . f) c of
             n | n < S.length c -> Chunk (S.drop n c) cs
               | otherwise      -> dropWhile' cs
 
@@ -702,10 +745,10 @@
 -- 'break' @p@ is equivalent to @'span' (not . p)@ and to @('takeWhile' (not . p) &&& 'dropWhile' (not . p))@.
 --
 break :: (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
-break f cs0 = break' cs0
+break f = break'
   where break' Empty        = (Empty, Empty)
         break' (Chunk c cs) =
-          case findIndexOrEnd f c of
+          case S.findIndexOrLength f c of
             0                  -> (Empty, Chunk c cs)
             n | n < S.length c -> (Chunk (S.take n c) Empty
                                   ,Chunk (S.drop n c) cs)
@@ -774,9 +817,9 @@
 splitWith p (Chunk c0 cs0) = comb [] (S.splitWith p c0) cs0
 
   where comb :: [P.ByteString] -> [P.ByteString] -> ByteString -> [ByteString]
-        comb acc (s:[]) Empty        = revChunks (s:acc) : []
-        comb acc (s:[]) (Chunk c cs) = comb (s:acc) (S.splitWith p c) cs
-        comb acc (s:ss) cs           = revChunks (s:acc) : comb [] ss cs
+        comb acc [s] Empty        = [revChunks (s:acc)]
+        comb acc [s] (Chunk c cs) = comb (s:acc) (S.splitWith p c) cs
+        comb acc (s:ss) cs        = revChunks (s:acc) : comb [] ss cs
 {-# INLINE splitWith #-}
 
 -- | /O(n)/ Break a 'ByteString' into pieces separated by the byte
@@ -801,8 +844,8 @@
 split w (Chunk c0 cs0) = comb [] (S.split w c0) cs0
 
   where comb :: [P.ByteString] -> [P.ByteString] -> ByteString -> [ByteString]
-        comb acc (s:[]) Empty        = revChunks (s:acc) : []
-        comb acc (s:[]) (Chunk c cs) = comb (s:acc) (S.split w c) cs
+        comb acc [s] Empty        = [revChunks (s:acc)]
+        comb acc [s] (Chunk c cs) = comb (s:acc) (S.split w c) cs
         comb acc (s:ss) cs        = revChunks (s:acc) : comb [] ss cs
 {-# INLINE split #-}
 
@@ -823,9 +866,9 @@
       | S.length c == 1  = to [c] (S.unsafeHead c) cs
       | otherwise        = to [S.unsafeTake 1 c] (S.unsafeHead c) (Chunk (S.unsafeTail c) cs)
 
-    to acc !_ Empty        = revNonEmptyChunks acc : []
+    to acc !_ Empty        = [revNonEmptyChunks acc]
     to acc !w (Chunk c cs) =
-      case findIndexOrEnd (/= w) c of
+      case S.findIndexOrLength (/= w) c of
         0                    -> revNonEmptyChunks acc
                               : go (Chunk c cs)
         n | n == S.length c  -> to (S.unsafeTake n c : acc) w cs
@@ -842,9 +885,9 @@
       | S.length c == 1  = to [c] (S.unsafeHead c) cs
       | otherwise        = to [S.unsafeTake 1 c] (S.unsafeHead c) (Chunk (S.unsafeTail c) cs)
 
-    to acc !_ Empty        = revNonEmptyChunks acc : []
+    to acc !_ Empty        = [revNonEmptyChunks acc]
     to acc !w (Chunk c cs) =
-      case findIndexOrEnd (not . k w) c of
+      case S.findIndexOrLength (not . k w) c of
         0                    -> revNonEmptyChunks acc
                               : go (Chunk c cs)
         n | n == S.length c  -> to (S.unsafeTake n c : acc) w cs
@@ -898,7 +941,7 @@
 -- element, or 'Nothing' if there is no such element.
 -- This implementation uses memchr(3).
 elemIndex :: Word8 -> ByteString -> Maybe Int64
-elemIndex w cs0 = elemIndex' 0 cs0
+elemIndex w = elemIndex' 0
   where elemIndex' _ Empty        = Nothing
         elemIndex' n (Chunk c cs) =
           case S.elemIndex w c of
@@ -910,8 +953,9 @@
 -- element, or 'Nothing' if there is no such element. The following
 -- holds:
 --
--- > elemIndexEnd c xs ==
--- > (-) (length xs - 1) `fmap` elemIndex c (reverse xs)
+-- > elemIndexEnd c xs = case elemIndex c (reverse xs) of
+-- >   Nothing -> Nothing
+-- >   Just i  -> Just (length xs - 1 - i)
 --
 -- @since 0.10.6.0
 elemIndexEnd :: Word8 -> ByteString -> Maybe Int64
@@ -922,7 +966,7 @@
 -- the indices of all elements equal to the query element, in ascending order.
 -- This implementation uses memchr(3).
 elemIndices :: Word8 -> ByteString -> [Int64]
-elemIndices w cs0 = elemIndices' 0 cs0
+elemIndices w = elemIndices' 0
   where elemIndices' _ Empty        = []
         elemIndices' n (Chunk c cs) = L.map ((+n).fromIntegral) (S.elemIndices w c)
                              ++ elemIndices' (n + fromIntegral (S.length c)) cs
@@ -933,13 +977,13 @@
 --
 -- But more efficiently than using length on the intermediate list.
 count :: Word8 -> ByteString -> Int64
-count w cs = foldlChunks (\n c -> n + fromIntegral (S.count w c)) 0 cs
+count w = foldlChunks (\n c -> n + fromIntegral (S.count w c)) 0
 
 -- | The 'findIndex' function takes a predicate and a 'ByteString' and
 -- returns the index of the first element in the ByteString
 -- satisfying the predicate.
 findIndex :: (Word8 -> Bool) -> ByteString -> Maybe Int64
-findIndex k cs0 = findIndex' 0 cs0
+findIndex k = findIndex' 0
   where findIndex' _ Empty        = Nothing
         findIndex' n (Chunk c cs) =
           case S.findIndex k c of
@@ -958,7 +1002,7 @@
     findIndexEnd' _ Empty = Nothing
     findIndexEnd' n (Chunk c cs) =
       let !n' = n + S.length c
-          !i  = fmap (fromIntegral . (n +)) $ S.findIndexEnd k c
+          !i  = fromIntegral . (n +) <$> S.findIndexEnd k c
       in findIndexEnd' n' cs `mplus` i
 {-# INLINE findIndexEnd #-}
 
@@ -969,7 +1013,7 @@
 -- > find f p = case findIndex f p of Just n -> Just (p ! n) ; _ -> Nothing
 --
 find :: (Word8 -> Bool) -> ByteString -> Maybe Word8
-find f cs0 = find' cs0
+find f = find'
   where find' Empty        = Nothing
         find' (Chunk c cs) = case S.find f c of
             Nothing -> find' cs
@@ -979,7 +1023,7 @@
 -- | The 'findIndices' function extends 'findIndex', by returning the
 -- indices of all elements satisfying the predicate, in ascending order.
 findIndices :: (Word8 -> Bool) -> ByteString -> [Int64]
-findIndices k cs0 = findIndices' 0 cs0
+findIndices k = findIndices' 0
   where findIndices' _ Empty        = []
         findIndices' n (Chunk c cs) = L.map ((+n).fromIntegral) (S.findIndices k c)
                              ++ findIndices' (n + fromIntegral (S.length c)) cs
@@ -994,13 +1038,13 @@
 
 -- | /O(n)/ 'notElem' is the inverse of 'elem'
 notElem :: Word8 -> ByteString -> Bool
-notElem w cs = not (elem w cs)
+notElem w cs = not (w `elem` cs)
 
 -- | /O(n)/ 'filter', applied to a predicate and a ByteString,
 -- returns a ByteString containing those characters that satisfy the
 -- predicate.
 filter :: (Word8 -> Bool) -> ByteString -> ByteString
-filter p s = go s
+filter p = go
     where
         go Empty        = Empty
         go (Chunk x xs) = chunk (S.filter p x) (go xs)
@@ -1131,6 +1175,20 @@
     to _ (Chunk x' xs) y ys            | not (S.null y) = go x' xs y  ys
     to _ (Chunk x' xs) _ (Chunk y' ys)                  = go x' xs y' ys
 
+-- | A specialised version of `zipWith` for the common case of a
+-- simultaneous map over two ByteStrings, to build a 3rd.
+--
+-- @since 0.11.1.0
+packZipWith :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> ByteString
+packZipWith _ Empty _ = Empty
+packZipWith _ _ Empty = Empty
+packZipWith f (Chunk a@(S.BS _ al) as) (Chunk b@(S.BS _ bl) bs) = Chunk (S.packZipWith f a b) $
+    case compare al bl of
+        LT -> packZipWith f as $ Chunk (S.drop al b) bs
+        EQ -> packZipWith f as bs
+        GT -> packZipWith f (Chunk (S.drop bl a) as) bs
+{-# INLINE packZipWith #-}
+
 -- | /O(n)/ 'unzip' transforms a list of pairs of bytes into a pair of
 -- ByteStrings. Note that this performs two 'pack' operations.
 unzip :: [(Word8,Word8)] -> (ByteString,ByteString)
@@ -1149,7 +1207,7 @@
 
 -- | /O(n)/ Return all final segments of the given 'ByteString', longest first.
 tails :: ByteString -> [ByteString]
-tails Empty         = Empty : []
+tails Empty         = [Empty]
 tails cs@(Chunk c cs')
   | S.length c == 1 = cs : tails cs'
   | otherwise       = cs : tails (Chunk (S.unsafeTail c) cs')
@@ -1163,7 +1221,7 @@
 --   if a large string has been read in, and only a small part of it
 --   is needed in the rest of the program.
 copy :: ByteString -> ByteString
-copy cs = foldrChunks (Chunk . S.copy) Empty cs
+copy = foldrChunks (Chunk . S.copy) Empty
 --TODO, we could coalese small blocks here
 --FIXME: probably not strict enough, if we're doing this to avoid retaining
 -- the parent blocks then we'd better copy strictly.
@@ -1187,10 +1245,6 @@
 --
 -- The handle is closed on EOF.
 --
--- Note: the 'Handle' should be placed in binary mode with
--- 'System.IO.hSetBinaryMode' for 'hGetContentsN' to
--- work correctly.
---
 hGetContentsN :: Int -> Handle -> IO ByteString
 hGetContentsN k h = lazyRead -- TODO close on exceptions
   where
@@ -1200,8 +1254,7 @@
         c <- S.hGetSome h k -- only blocks if there is no data available
         if S.null c
           then hClose h >> return Empty
-          else do cs <- lazyRead
-                  return (Chunk c cs)
+          else Chunk c <$> lazyRead
 
 -- | Read @n@ bytes into a 'ByteString', directly from the
 -- specified 'Handle', in chunks of size @k@.
@@ -1249,10 +1302,6 @@
 -- File handles are closed on EOF if all the file is read, or through
 -- garbage collection otherwise.
 --
--- Note: the 'Handle' should be placed in binary mode with
--- 'System.IO.hSetBinaryMode' for 'hGetContents' to
--- work correctly.
---
 hGetContents :: Handle -> IO ByteString
 hGetContents = hGetContentsN defaultChunkSize
 
@@ -1305,7 +1354,7 @@
 -- writes, and hence 'hPut' alone might not be suitable for concurrent writes.
 --
 hPut :: Handle -> ByteString -> IO ()
-hPut h cs = foldrChunks (\c rest -> S.hPut h c >> rest) (return ()) cs
+hPut h = foldrChunks (\c rest -> S.hPut h c >> rest) (return ())
 
 -- | Similar to 'hPut' except that it will never block. Instead it returns
 -- any tail that did not get written. This tail may be 'empty' in the case that
@@ -1357,25 +1406,11 @@
 
 -- reverse a list of non-empty chunks into a lazy ByteString
 revNonEmptyChunks :: [P.ByteString] -> ByteString
-revNonEmptyChunks cs = L.foldl' (flip Chunk) Empty cs
+revNonEmptyChunks = L.foldl' (flip Chunk) Empty
 
 -- reverse a list of possibly-empty chunks into a lazy ByteString
 revChunks :: [P.ByteString] -> ByteString
-revChunks cs = L.foldl' (flip chunk) Empty cs
-
--- | 'findIndexOrEnd' is a variant of findIndex, that returns the length
--- of the string if no element is found, rather than Nothing.
-findIndexOrEnd :: (Word8 -> Bool) -> P.ByteString -> Int
-findIndexOrEnd k (S.BS x l) =
-    S.accursedUnutterablePerformIO $
-      withForeignPtr x $ \f -> go f 0
-  where
-    go !ptr !n | n >= l    = return l
-               | otherwise = do w <- peek ptr
-                                if k w
-                                  then return n
-                                  else go (ptr `plusPtr` 1) (n+1)
-{-# INLINE findIndexOrEnd #-}
+revChunks = L.foldl' (flip chunk) Empty
 
 -- $IOChunk
 --
diff --git a/Data/ByteString/Lazy/Char8.hs b/Data/ByteString/Lazy/Char8.hs
--- a/Data/ByteString/Lazy/Char8.hs
+++ b/Data/ByteString/Lazy/Char8.hs
@@ -81,6 +81,7 @@
         all,                    -- :: (Char -> Bool) -> ByteString -> Bool
         maximum,                -- :: ByteString -> Char
         minimum,                -- :: ByteString -> Char
+        compareLength,          -- :: ByteString -> Int -> Ordering
 
         -- * Building ByteStrings
         -- ** Scans
@@ -149,15 +150,18 @@
         indexMaybe,             -- :: ByteString -> Int64 -> Maybe Char
         (!?),                   -- :: ByteString -> Int64 -> Maybe Char
         elemIndex,              -- :: Char -> ByteString -> Maybe Int64
+        elemIndexEnd,           -- :: Char -> ByteString -> Maybe Int64
         elemIndices,            -- :: Char -> ByteString -> [Int64]
         findIndex,              -- :: (Char -> Bool) -> ByteString -> Maybe Int64
+        findIndexEnd,           -- :: (Char -> Bool) -> ByteString -> Maybe Int64
         findIndices,            -- :: (Char -> Bool) -> ByteString -> [Int64]
         count,                  -- :: Char -> ByteString -> Int64
 
         -- * Zipping and unzipping ByteStrings
         zip,                    -- :: ByteString -> ByteString -> [(Char,Char)]
         zipWith,                -- :: (Char -> Char -> c) -> ByteString -> ByteString -> [c]
---      unzip,                  -- :: [(Char,Char)] -> (ByteString,ByteString)
+        packZipWith,            -- :: (Char -> Char -> Char) -> ByteString -> ByteString -> ByteString
+        unzip,                  -- :: [(Char,Char)] -> (ByteString,ByteString)
 
         -- * Ordered ByteStrings
 --        sort,                   -- :: ByteString -> ByteString
@@ -207,7 +211,7 @@
         ,hGetContents, hGet, hPut, getContents
         ,hGetNonBlocking, hPutNonBlocking
         ,putStr, hPutStr, interact
-        ,readFile,writeFile,appendFile)
+        ,readFile,writeFile,appendFile,compareLength)
 
 -- Functions we need to wrap.
 import qualified Data.ByteString.Lazy as L
@@ -218,6 +222,10 @@
 
 import Data.ByteString.Internal (w2c, c2w, isSpaceWord8)
 
+#if !(MIN_VERSION_base(4,8,0))
+import Control.Applicative ((<$>))
+#endif
+
 import Data.Int (Int64)
 import qualified Data.List as List
 
@@ -330,7 +338,7 @@
 -- (typically the right-identity of the operator), and a packed string,
 -- reduces the packed string using the binary operator, from right to left.
 foldr :: (Char -> a -> a) -> a -> ByteString -> a
-foldr f = L.foldr (\c a -> f (w2c c) a)
+foldr f = L.foldr (f . w2c)
 {-# INLINE foldr #-}
 
 -- | 'foldl1' is a variant of 'foldl' that has no starting value
@@ -556,6 +564,20 @@
 elemIndex = L.elemIndex . c2w
 {-# INLINE elemIndex #-}
 
+-- | /O(n)/ The 'elemIndexEnd' function returns the last index of the
+-- element in the given 'ByteString' which is equal to the query
+-- element, or 'Nothing' if there is no such element. The following
+-- holds:
+--
+-- > elemIndexEnd c xs = case elemIndex c (reverse xs) of
+-- >   Nothing -> Nothing
+-- >   Just i  -> Just (length xs - 1 - i)
+--
+-- @since 0.11.1.0
+elemIndexEnd :: Char -> ByteString -> Maybe Int64
+elemIndexEnd = L.elemIndexEnd . c2w
+{-# INLINE elemIndexEnd #-}
+
 -- | /O(n)/ The 'elemIndices' function extends 'elemIndex', by returning
 -- the indices of all elements equal to the query element, in ascending order.
 elemIndices :: Char -> ByteString -> [Int64]
@@ -568,6 +590,15 @@
 findIndex f = L.findIndex (f . w2c)
 {-# INLINE findIndex #-}
 
+-- | The 'findIndexEnd' function takes a predicate and a 'ByteString' and
+-- returns the index of the last element in the ByteString
+-- satisfying the predicate.
+--
+-- @since 0.11.1.0
+findIndexEnd :: (Char -> Bool) -> ByteString -> Maybe Int64
+findIndexEnd f = L.findIndexEnd (f . w2c)
+{-# INLINE findIndexEnd #-}
+
 -- | The 'findIndices' function extends 'findIndex', by returning the
 -- indices of all elements satisfying the predicate, in ascending order.
 findIndices :: (Char -> Bool) -> ByteString -> [Int64]
@@ -683,6 +714,24 @@
 zipWith :: (Char -> Char -> a) -> ByteString -> ByteString -> [a]
 zipWith f = L.zipWith ((. w2c) . f . w2c)
 
+-- | A specialised version of `zipWith` for the common case of a
+-- simultaneous map over two ByteStrings, to build a 3rd.
+--
+-- @since 0.11.1.0
+packZipWith :: (Char -> Char -> Char) -> ByteString -> ByteString -> ByteString
+packZipWith f = L.packZipWith f'
+    where
+        f' c1 c2 = c2w $ f (w2c c1) (w2c c2)
+{-# INLINE packZipWith #-}
+
+-- | /O(n)/ 'unzip' transforms a list of pairs of chars into a pair of
+-- ByteStrings. Note that this performs two 'pack' operations.
+--
+-- @since 0.11.1.0
+unzip :: [(Char, Char)] -> (ByteString, ByteString)
+unzip ls = (pack (fmap fst ls), pack (fmap snd ls))
+{-# INLINE unzip #-}
+
 -- | 'lines' breaks a ByteString up into a list of ByteStrings at
 -- newline Chars (@'\\n'@). The resulting strings do not contain newlines.
 --
@@ -710,8 +759,8 @@
     loop0 c cs =
         case B.elemIndex (c2w '\n') c of
             Nothing -> case cs of
-                           Empty  | B.null c  ->                 []
-                                  | otherwise -> Chunk c Empty : []
+                           Empty  | B.null c  -> []
+                                  | otherwise -> [Chunk c Empty]
                            (Chunk c' cs')
                                | B.null c  -> loop0 c'     cs'
                                | otherwise -> loop  c' [c] cs'
@@ -728,38 +777,14 @@
         case B.elemIndex (c2w '\n') c of
             Nothing ->
                 case cs of
-                    Empty -> let c' = revChunks (c : line)
-                              in c' `seq` (c' : [])
+                    Empty -> let !c' = revChunks (c : line)
+                              in [c']
 
                     (Chunk c' cs') -> loop c' (c : line) cs'
 
             Just n ->
-                let c' = revChunks (B.unsafeTake n c : line)
-                 in c' `seq` (c' : loop0 (B.unsafeDrop (n+1) c) cs)
-
-{-
-
-This function is too strict!  Consider,
-
-> prop_lazy =
-    (L.unpack . head . lazylines $ L.append (L.pack "a\nb\n") (error "failed"))
-  ==
-    "a"
-
-fails.  Here's a properly lazy version of 'lines' for lazy bytestrings
-
-    lazylines           :: L.ByteString -> [L.ByteString]
-    lazylines s
-        | L.null s  = []
-        | otherwise =
-            let (l,s') = L.break ((==) '\n') s
-            in l : if L.null s' then []
-                                else lazylines (L.tail s')
-
-we need a similarly lazy, but efficient version.
-
--}
-
+                let !c' = revChunks (B.unsafeTake n c : line)
+                 in c' : loop0 (B.unsafeDrop (n+1) c) cs
 
 -- | 'unlines' is an inverse operation to 'lines'.  It joins lines,
 -- after appending a terminating newline to each.
@@ -819,7 +844,6 @@
                       e  = n' `seq` c' `seq` Just (n',c')
          --                  in n' `seq` c' `seq` JustS n' c'
 
-
 -- | readInteger reads an Integer from the beginning of the ByteString.  If
 -- there is no integer at the beginning of the string, it returns Nothing,
 -- otherwise it just returns the int read, and the rest of the string.
@@ -866,11 +890,11 @@
           combine1 _ [n] = n
           combine1 b ns  = combine1 (b*b) $ combine2 b ns
 
-          combine2 b (n:m:ns) = let t = n+m*b in t `seq` (t : combine2 b ns)
+          combine2 b (n:m:ns) = let !t = n+m*b in t : combine2 b ns
           combine2 _ ns       = ns
 
-          end n c cs = let c' = chunk c cs
-                        in c' `seq` (n, c')
+          end n c cs = let !c' = chunk c cs
+                        in (n, c')
 
 
 -- | Write a ByteString to a handle, appending a newline byte
@@ -888,4 +912,4 @@
 
 -- reverse a list of possibly-empty chunks into a lazy ByteString
 revChunks :: [S.ByteString] -> ByteString
-revChunks cs = List.foldl' (flip chunk) Empty cs
+revChunks = List.foldl' (flip chunk) Empty
diff --git a/Data/ByteString/Lazy/Internal.hs b/Data/ByteString/Lazy/Internal.hs
--- a/Data/ByteString/Lazy/Internal.hs
+++ b/Data/ByteString/Lazy/Internal.hs
@@ -57,10 +57,10 @@
 import Foreign.Storable (Storable(sizeOf))
 
 #if MIN_VERSION_base(4,13,0)
-import Data.Semigroup   (Semigroup (sconcat))
+import Data.Semigroup   (Semigroup (sconcat, stimes))
 import Data.List.NonEmpty (NonEmpty ((:|)))
 #elif MIN_VERSION_base(4,9,0)
-import Data.Semigroup   (Semigroup ((<>), sconcat))
+import Data.Semigroup   (Semigroup ((<>), sconcat, stimes))
 import Data.List.NonEmpty (NonEmpty ((:|)))
 #endif
 #if !(MIN_VERSION_base(4,8,0))
@@ -98,6 +98,7 @@
 instance Semigroup ByteString where
     (<>)    = append
     sconcat (b:|bs) = concat (b:bs)
+    stimes = times
 #endif
 
 instance Monoid ByteString where
@@ -142,6 +143,7 @@
 -- Packing and unpacking from lists
 
 packBytes :: [Word8] -> ByteString
+packBytes [] = Empty
 packBytes cs0 =
     packChunks 32 cs0
   where
@@ -150,6 +152,7 @@
       (bs, cs') -> Chunk bs (packChunks (min (n * 2) smallChunkSize) cs')
 
 packChars :: [Char] -> ByteString
+packChars [] = Empty
 packChars cs0 = packChunks 32 cs0
   where
     packChunks n cs = case S.packUptoLenChars n cs of
@@ -200,10 +203,9 @@
 -- | Consume the chunks of a lazy ByteString with a strict, tail-recursive,
 -- accumulating left fold.
 foldlChunks :: (a -> S.ByteString -> a) -> a -> ByteString -> a
-foldlChunks f z = go z
-  where go a _ | a `seq` False = undefined
-        go a Empty        = a
-        go a (Chunk c cs) = go (f a c) cs
+foldlChunks f = go
+  where go !a Empty        = a
+        go !a (Chunk c cs) = go (f a c) cs
 {-# INLINE foldlChunks #-}
 
 ------------------------------------------------------------------------
@@ -266,13 +268,27 @@
 append xs ys = foldrChunks Chunk ys xs
 
 concat :: [ByteString] -> ByteString
-concat css0 = to css0
+concat = to
   where
     go Empty        css = to css
     go (Chunk c cs) css = Chunk c (go cs css)
     to []               = Empty
     to (cs:css)         = go cs css
 
+#if MIN_VERSION_base(4,9,0)
+-- | Repeats the given ByteString n times.
+times :: Integral a => a -> ByteString -> ByteString
+times 0 _ = Empty
+times n lbs0
+  | n < 0 = error "stimes: non-negative multiplier expected"
+  | otherwise = case lbs0 of
+    Empty -> Empty
+    Chunk bs lbs -> Chunk bs (go lbs)
+  where
+    go Empty = times (n-1) lbs0
+    go (Chunk c cs) = Chunk c (go cs)
+#endif
+
 ------------------------------------------------------------------------
 -- Conversions
 
@@ -314,7 +330,7 @@
     -- Copy the data
     goCopy Empty                    !_   = return ()
     goCopy (Chunk (S.BS _  0  ) cs) !ptr = goCopy cs ptr
-    goCopy (Chunk (S.BS fp len) cs) !ptr = do
+    goCopy (Chunk (S.BS fp len) cs) !ptr =
       withForeignPtr fp $ \p -> do
         S.memcpy ptr p len
         goCopy cs (ptr `plusPtr` len)
diff --git a/Data/ByteString/Short.hs b/Data/ByteString/Short.hs
--- a/Data/ByteString/Short.hs
+++ b/Data/ByteString/Short.hs
@@ -30,7 +30,7 @@
 
     -- * The @ShortByteString@ type
 
-    ShortByteString,
+    ShortByteString(..),
 
     -- ** Memory overhead
     -- | With GHC, the memory overheads are as follows, expressed in words and
diff --git a/Data/ByteString/Short/Internal.hs b/Data/ByteString/Short/Internal.hs
--- a/Data/ByteString/Short/Internal.hs
+++ b/Data/ByteString/Short/Internal.hs
@@ -86,9 +86,11 @@
                 , byteArrayContents#
                 , unsafeCoerce#
 #endif
-#if MIN_VERSION_base(4,3,0)
-                , sizeofByteArray#
+#if MIN_VERSION_base(4,10,0)
+                , isByteArrayPinned#
+                , isTrue#
 #endif
+                , sizeofByteArray#
                 , indexWord8Array#, indexCharArray#
                 , writeWord8Array#, writeCharArray#
                 , unsafeFreezeByteArray# )
@@ -109,7 +111,6 @@
                , return
                , Maybe(..) )
 
-
 -- | A compact representation of a 'Word8' vector.
 --
 -- It has a lower memory overhead than a 'ByteString' and does not
@@ -124,16 +125,6 @@
 -- more flexible and it supports a wide range of operations.
 --
 data ShortByteString = SBS ByteArray#
-#if !(MIN_VERSION_base(4,3,0))
-           {-# UNPACK #-} !Int  -- ^ Prior to ghc-7.0.x, 'ByteArray#'s reported
-                                -- their length rounded up to the nearest word.
-                                -- This means we have to store the true length
-                                -- separately, wasting a word.
-#define LEN(x) (x)
-#else
-#define _len   /* empty */
-#define LEN(x) /* empty */
-#endif
     deriving Typeable
 
 -- The ByteArray# representation is always word sized and aligned but with a
@@ -199,11 +190,7 @@
 
 -- | /O(1)/ The length of a 'ShortByteString'.
 length :: ShortByteString -> Int
-#if MIN_VERSION_base(4,3,0)
 length (SBS barr#) = I# (sizeofByteArray# barr#)
-#else
-length (SBS _ len) = len
-#endif
 
 -- | /O(1)/ Test whether a 'ShortByteString' is empty.
 null :: ShortByteString -> Bool
@@ -248,15 +235,15 @@
 -- Internal utils
 
 asBA :: ShortByteString -> BA
-asBA (SBS ba# _len) = BA# ba#
+asBA (SBS ba#) = BA# ba#
 
 create :: Int -> (forall s. MBA s -> ST s ()) -> ShortByteString
 create len fill =
-    runST (do
+    runST $ do
       mba <- newByteArray len
       fill mba
       BA# ba# <- unsafeFreezeByteArray mba
-      return (SBS ba# LEN(len)))
+      return (SBS ba#)
 {-# INLINE create #-}
 
 ------------------------------------------------------------------------
@@ -276,12 +263,20 @@
     stToIO (copyAddrToByteArray ptr mba 0 len)
     touchForeignPtr fptr
     BA# ba# <- stToIO (unsafeFreezeByteArray mba)
-    return (SBS ba# LEN(len))
+    return (SBS ba#)
 
 
 -- | /O(n)/. Convert a 'ShortByteString' into a 'ByteString'.
 --
 fromShort :: ShortByteString -> ByteString
+#if MIN_VERSION_base(4,10,0)
+fromShort (SBS b#)
+  | isTrue# (isByteArrayPinned# b#) = BS fp len
+  where
+    addr# = byteArrayContents# b#
+    fp = ForeignPtr addr# (PlainPtr (unsafeCoerce# b#))
+    len = I# (sizeofByteArray# b#)
+#endif
 fromShort !sbs = unsafeDupablePerformIO (fromShortIO sbs)
 
 fromShortIO :: ShortByteString -> IO ByteString
@@ -364,7 +359,7 @@
 -- (5 words per list element, 8 bytes per word, 100 elements = 4000 bytes)
 
 unpackAppendCharsLazy :: ShortByteString -> [Char] -> [Char]
-unpackAppendCharsLazy sbs cs0 = go 0 (length sbs) cs0
+unpackAppendCharsLazy sbs = go 0 (length sbs)
   where
     sz = 100
 
@@ -374,7 +369,7 @@
                       where remainder = go (off+sz) (len-sz) cs
 
 unpackAppendBytesLazy :: ShortByteString -> [Word8] -> [Word8]
-unpackAppendBytesLazy sbs ws0 = go 0 (length sbs) ws0
+unpackAppendBytesLazy sbs = go 0 (length sbs)
   where
     sz = 100
 
@@ -389,7 +384,7 @@
 -- buffer and loops down until we hit the sentinal:
 
 unpackAppendCharsStrict :: ShortByteString -> Int -> Int -> [Char] -> [Char]
-unpackAppendCharsStrict !sbs off len cs = go (off-1) (off-1 + len) cs
+unpackAppendCharsStrict !sbs off len = go (off-1) (off-1 + len)
   where
     go !sentinal !i !acc
       | i == sentinal = acc
@@ -397,7 +392,7 @@
                         in go sentinal (i-1) (c:acc)
 
 unpackAppendBytesStrict :: ShortByteString -> Int -> Int -> [Word8] -> [Word8]
-unpackAppendBytesStrict !sbs off len ws = go (off-1) (off-1 + len) ws
+unpackAppendBytesStrict !sbs off len = go (off-1) (off-1 + len)
   where
     go !sentinal !i !acc
       | i == sentinal = acc
@@ -476,7 +471,7 @@
       mba <- newByteArray len
       copyAddrToByteArray ptr mba 0 len
       BA# ba# <- unsafeFreezeByteArray mba
-      return (SBS ba# LEN(len))
+      return (SBS ba#)
 
 
 ------------------------------------------------------------------------
@@ -572,13 +567,13 @@
 
 #else
 
-copyAddrToByteArray# src dst dst_off len s =
-  unIO_ (memcpy_AddrToByteArray dst (csize dst_off) src 0 (csize len)) s
+copyAddrToByteArray# src dst dst_off len =
+  unIO_ (memcpy_AddrToByteArray dst (csize dst_off) src 0 (csize len))
 
 copyAddrToByteArray0 :: Addr# -> MutableByteArray# s -> Int#
                      -> State# RealWorld -> State# RealWorld
-copyAddrToByteArray0 src dst len s =
-  unIO_ (memcpy_AddrToByteArray0 dst src (csize len)) s
+copyAddrToByteArray0 src dst len =
+  unIO_ (memcpy_AddrToByteArray0 dst src (csize len))
 
 {-# INLINE [0] copyAddrToByteArray# #-}
 {-# RULES "copyAddrToByteArray# dst_off=0"
@@ -593,13 +588,13 @@
   memcpy_AddrToByteArray0 :: MutableByteArray# s -> Addr# -> CSize -> IO ()
 
 
-copyByteArrayToAddr# src src_off dst len s =
-  unIO_ (memcpy_ByteArrayToAddr dst 0 src (csize src_off) (csize len)) s
+copyByteArrayToAddr# src src_off dst len =
+  unIO_ (memcpy_ByteArrayToAddr dst 0 src (csize src_off) (csize len))
 
 copyByteArrayToAddr0 :: ByteArray# -> Addr# -> Int#
                      -> State# RealWorld -> State# RealWorld
-copyByteArrayToAddr0 src dst len s =
-  unIO_ (memcpy_ByteArrayToAddr0 dst src (csize len)) s
+copyByteArrayToAddr0 src dst len =
+  unIO_ (memcpy_ByteArrayToAddr0 dst src (csize len))
 
 {-# INLINE [0] copyByteArrayToAddr# #-}
 {-# RULES "copyByteArrayToAddr# src_off=0"
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # ByteString: Fast, Packed Strings of Bytes
 
-[![Build Status](https://secure.travis-ci.org/haskell/bytestring.svg?branch=master)](http://travis-ci.org/haskell/bytestring) [![Hackage](http://img.shields.io/hackage/v/bytestring.svg)](https://hackage.haskell.org/package/bytestring) [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/bytestring/badge)](https://matrix.hackage.haskell.org/package/bytestring) [![Stackage LTS](http://stackage.org/package/bytestring/badge/lts)](http://stackage.org/lts/package/bytestring) [![Stackage Nightly](http://stackage.org/package/bytestring/badge/nightly)](http://stackage.org/nightly/package/bytestring)
+[![Build Status](https://github.com/haskell/bytestring/workflows/ci/badge.svg)](https://github.com/haskell/bytestring/actions?query=workflow%3Aci) [![Hackage](http://img.shields.io/hackage/v/bytestring.svg)](https://hackage.haskell.org/package/bytestring) [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/bytestring/badge)](https://matrix.hackage.haskell.org/package/bytestring) [![Stackage LTS](http://stackage.org/package/bytestring/badge/lts)](http://stackage.org/lts/package/bytestring) [![Stackage Nightly](http://stackage.org/package/bytestring/badge/nightly)](http://stackage.org/nightly/package/bytestring)
 
 This library provides the `Data.ByteString` module -- strict and lazy
 byte arrays manipulable as strings -- providing very time/space-efficient
diff --git a/bench/BenchAll.hs b/bench/BenchAll.hs
new file mode 100644
--- /dev/null
+++ b/bench/BenchAll.hs
@@ -0,0 +1,453 @@
+-- |
+-- Copyright   : (c) 2011 Simon Meier
+-- License     : BSD3-style (see LICENSE)
+--
+-- Maintainer  : Simon Meier <iridcode@gmail.com>
+-- Stability   : experimental
+-- Portability : tested on GHC only
+--
+
+{-# LANGUAGE BangPatterns        #-}
+{-# LANGUAGE PackageImports      #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE MagicHash           #-}
+
+module Main (main) where
+
+import           Data.Foldable                         (foldMap)
+import           Data.Monoid
+import           Data.String
+import           Test.Tasty.Bench
+import           Prelude                               hiding (words)
+
+import qualified Data.ByteString                       as S
+import qualified Data.ByteString.Char8                 as S8
+import qualified Data.ByteString.Lazy                  as L
+
+import           Data.ByteString.Builder
+import           Data.ByteString.Builder.Extra         (byteStringCopy,
+                                                        byteStringInsert,
+                                                        intHost)
+import           Data.ByteString.Builder.Internal      (ensureFree)
+import           Data.ByteString.Builder.Prim          (BoundedPrim, FixedPrim,
+                                                        (>$<))
+import qualified Data.ByteString.Builder.Prim          as P
+import qualified Data.ByteString.Builder.Prim.Internal as PI
+
+import           Foreign
+
+import System.Random
+
+import BenchBoundsCheckFusion
+import BenchCSV
+import BenchIndices
+
+------------------------------------------------------------------------------
+-- Benchmark support
+------------------------------------------------------------------------------
+
+countToZero :: Int -> Maybe (Int, Int)
+countToZero 0 = Nothing
+countToZero n = Just (n, n - 1)
+
+
+------------------------------------------------------------------------------
+-- Benchmark
+------------------------------------------------------------------------------
+
+-- input data (NOINLINE to ensure memoization)
+----------------------------------------------
+
+-- | Few-enough repetitions to avoid making GC too expensive.
+nRepl :: Int
+nRepl = 10000
+
+{-# NOINLINE intData #-}
+intData :: [Int]
+intData = [1..nRepl]
+
+{-# NOINLINE smallIntegerData #-}
+smallIntegerData :: [Integer]
+smallIntegerData = map fromIntegral intData
+
+{-# NOINLINE largeIntegerData #-}
+largeIntegerData :: [Integer]
+largeIntegerData = map (* (10 ^ (100 :: Integer))) smallIntegerData
+
+
+{-# NOINLINE floatData #-}
+floatData :: [Float]
+floatData = map (\x -> (3.14159 * fromIntegral x) ^ (3 :: Int)) intData
+
+{-# NOINLINE doubleData #-}
+doubleData :: [Double]
+doubleData = map (\x -> (3.14159 * fromIntegral x) ^ (3 :: Int)) intData
+
+{-# NOINLINE byteStringData #-}
+byteStringData :: S.ByteString
+byteStringData = S.pack $ map fromIntegral intData
+
+{-# NOINLINE lazyByteStringData #-}
+lazyByteStringData :: L.ByteString
+lazyByteStringData = case S.splitAt (nRepl `div` 2) byteStringData of
+    (bs1, bs2) -> L.fromChunks [bs1, bs2]
+
+{-# NOINLINE byteStringChunksData #-}
+byteStringChunksData :: [S.ByteString]
+byteStringChunksData = map (S.pack . replicate (4 ) . fromIntegral) intData
+
+{-# NOINLINE loremIpsum #-}
+loremIpsum :: S.ByteString
+loremIpsum = S8.unlines $ map S8.pack
+  [ "  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor"
+  , "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis"
+  , "nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
+  , "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu"
+  , "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in"
+  , "culpa qui officia deserunt mollit anim id est laborum."
+  ]
+
+-- benchmark wrappers
+---------------------
+
+{-# INLINE benchB #-}
+benchB :: String -> a -> (a -> Builder) -> Benchmark
+benchB name x b =
+    bench (name ++" (" ++ show nRepl ++ ")") $
+        whnf (L.length . toLazyByteString . b) x
+
+{-# INLINE benchB' #-}
+benchB' :: String -> a -> (a -> Builder) -> Benchmark
+benchB' name x b = bench name $ whnf (L.length . toLazyByteString . b) x
+
+{-# INLINE benchBInts #-}
+benchBInts :: String -> ([Int] -> Builder) -> Benchmark
+benchBInts name = benchB name intData
+
+-- | Benchmark a 'FixedPrim'. Full inlining to enable specialization.
+{-# INLINE benchFE #-}
+benchFE :: String -> FixedPrim Int -> Benchmark
+benchFE name = benchBE name . P.liftFixedToBounded
+
+-- | Benchmark a 'BoundedPrim'. Full inlining to enable specialization.
+{-# INLINE benchBE #-}
+benchBE :: String -> BoundedPrim Int -> Benchmark
+benchBE name e =
+  bench (name ++" (" ++ show nRepl ++ ")") $ whnfIO (benchIntEncodingB nRepl e)
+
+-- We use this construction of just looping through @n,n-1,..,1@ to ensure that
+-- we measure the speed of the encoding and not the speed of generating the
+-- values to be encoded.
+{-# INLINE benchIntEncodingB #-}
+benchIntEncodingB :: Int              -- ^ Maximal 'Int' to write
+                  -> BoundedPrim Int  -- ^ 'BoundedPrim' to execute
+                  -> IO ()            -- ^ 'IO' action to benchmark
+benchIntEncodingB n0 w
+  | n0 <= 0   = return ()
+  | otherwise = do
+      fpbuf <- mallocForeignPtrBytes (n0 * PI.sizeBound w)
+      withForeignPtr fpbuf (loop n0) >> return ()
+  where
+    loop !n !op
+      | n <= 0    = return op
+      | otherwise = PI.runB w n op >>= loop (n - 1)
+
+hashInt :: Int -> Int
+hashInt x = iterate step x !! 10
+  where
+    step a = e
+      where b = (a `xor` 61) `xor` (a `shiftR` 16)
+            c = b + (b `shiftL` 3)
+            d = c `xor` (c `shiftR` 4)
+            e = d * 0x27d4eb2d
+            f = e `xor` (e `shiftR` 15)
+
+w :: Int -> Word8
+w = fromIntegral
+
+hashWord8 :: Word8 -> Word8
+hashWord8 = fromIntegral . hashInt . fromIntegral
+
+partitionStrict p = nf (S.partition p) . randomStrict $ mkStdGen 98423098
+  where randomStrict = fst . S.unfoldrN 10000 (Just . random)
+
+partitionLazy p = nf (L.partition p) . randomLazy $ (0, mkStdGen 98423098)
+  where step (k, g)
+          | k >= 10000 = Nothing
+          | otherwise  = let (x, g') = random g in Just (x, (k + 1, g'))
+        randomLazy = L.unfoldr step
+
+easySubstrings, randomSubstrings :: Int -> Int -> (S.ByteString, S.ByteString)
+hardSubstrings, pathologicalSubstrings :: Int ->
+                                          Int -> (S.ByteString, S.ByteString)
+
+{-# INLINE easySubstrings #-}
+easySubstrings n h = (S.replicate n $ w 1,
+                      S.replicate h $ w 0)
+
+{-# INLINE randomSubstrings #-}
+randomSubstrings n h = (f 48278379 n, f 98403980 h)
+  where
+    next' g = let (x, g') = next g in (w x, g')
+    f g l = fst $ S.unfoldrN l (Just . next') (mkStdGen g)
+
+{-# INLINE hardSubstrings #-}
+hardSubstrings n h = (f 48278379 n, f 98403980 h)
+  where
+    next' g = let (x, g') = next g
+              in (w $ x `mod` 4, g')
+    f g l = fst $ S.unfoldrN l (Just . next') (mkStdGen g)
+
+{-# INLINE pathologicalSubstrings #-}
+pathologicalSubstrings n h =
+  (S.replicate n (w 0),
+   S.concat . replicate (h `div` n) $ S.replicate (n - 1) (w 0) `S.snoc` w 1)
+
+htmlSubstrings :: S.ByteString -> Int -> Int -> IO (S.ByteString, S.ByteString)
+htmlSubstrings s n h =
+    do i <- randomRIO (0, l - n)
+       return (S.take n . S.drop i $ s', s')
+  where
+    s' = S.take h s
+    l  = S.length s'
+
+-- benchmarks
+-------------
+
+sanityCheckInfo :: [String]
+sanityCheckInfo =
+  [ "Sanity checks:"
+  , " lengths of input data: " ++ show
+      [ length intData, length floatData, length doubleData
+      , length smallIntegerData, length largeIntegerData
+      , S.length byteStringData, fromIntegral (L.length lazyByteStringData)
+      ]
+  ]
+
+sortInputs :: [S.ByteString]
+sortInputs = map (`S.take` S.pack [122, 121 .. 32]) [10..25]
+
+foldInputs :: [S.ByteString]
+foldInputs = map (\k -> S.pack $ if k <= 6 then take (2 ^ k) [32..95] else concat (replicate (2 ^ (k - 6)) [32..95])) [0..16]
+
+zeroes :: L.ByteString
+zeroes = L.replicate 10000 0
+
+zeroOneRepeating :: L.ByteString
+zeroOneRepeating = L.take 10000 (L.cycle (L.pack [0,1]))
+
+
+largeTraversalInput :: S.ByteString
+largeTraversalInput = S.concat (replicate 10 byteStringData)
+
+smallTraversalInput :: S.ByteString
+smallTraversalInput = S8.pack "The quick brown fox"
+
+main :: IO ()
+main = do
+  mapM_ putStrLn sanityCheckInfo
+  defaultMain
+    [ bgroup "Data.ByteString.Builder"
+      [ bgroup "Small payload"
+        [ benchB' "mempty"        ()  (const mempty)
+        , benchB' "ensureFree 8"  ()  (const (ensureFree 8))
+        , benchB' "intHost 1"     1   intHost
+        , benchB' "UTF-8 String (naive)" "hello world\0" fromString
+        , benchB' "UTF-8 String"  () $ \() -> P.cstringUtf8 "hello world\0"#
+        , benchB' "String (naive)" "hello world!" fromString
+        , benchB' "String"        () $ \() -> P.cstring "hello world!"#
+        ]
+
+      , bgroup "Encoding wrappers"
+        [ benchBInts "foldMap word8" $
+            foldMap (word8 . fromIntegral)
+        , benchBInts "primMapListFixed word8" $
+            P.primMapListFixed (fromIntegral >$< P.word8)
+        , benchB     "primUnfoldrFixed word8" nRepl $
+            P.primUnfoldrFixed (fromIntegral >$< P.word8) countToZero
+        , benchB     "primMapByteStringFixed word8" byteStringData $
+            P.primMapByteStringFixed P.word8
+        , benchB     "primMapLazyByteStringFixed word8" lazyByteStringData $
+            P.primMapLazyByteStringFixed P.word8
+        ]
+      , bgroup "ByteString insertion" $
+          let dataName = " byteStringChunks" ++
+                         show (S.length (head byteStringChunksData)) ++ "Data"
+          in
+            [ benchB ("foldMap byteStringInsert" ++ dataName) byteStringChunksData
+                (foldMap byteStringInsert)
+            , benchB ("foldMap byteString" ++ dataName) byteStringChunksData
+                (foldMap byteString)
+            , benchB ("foldMap byteStringCopy" ++ dataName) byteStringChunksData
+                (foldMap byteStringCopy)
+            ]
+
+      , bgroup "Non-bounded encodings"
+        [ benchB "byteStringHex"           byteStringData     $ byteStringHex
+        , benchB "lazyByteStringHex"       lazyByteStringData $ lazyByteStringHex
+        , benchB "foldMap floatDec"        floatData          $ foldMap floatDec
+        , benchB "foldMap doubleDec"       doubleData         $ foldMap doubleDec
+          -- Note that the small data corresponds to the intData pre-converted
+          -- to Integer.
+        , benchB "foldMap integerDec (small)"                     smallIntegerData        $ foldMap integerDec
+        , benchB "foldMap integerDec (large)"                     largeIntegerData        $ foldMap integerDec
+        ]
+      ]
+
+    , bgroup "Data.ByteString.Builder.Prim"
+      [ benchFE "char7"      $ toEnum       >$< P.char7
+      , benchFE "char8"      $ toEnum       >$< P.char8
+      , benchBE "charUtf8"   $ toEnum       >$< P.charUtf8
+
+      -- binary encoding
+      , benchFE "int8"       $ fromIntegral >$< P.int8
+      , benchFE "word8"      $ fromIntegral >$< P.word8
+
+      -- big-endian
+      , benchFE "int16BE"    $ fromIntegral >$< P.int16BE
+      , benchFE "int32BE"    $ fromIntegral >$< P.int32BE
+      , benchFE "int64BE"    $ fromIntegral >$< P.int64BE
+
+      , benchFE "word16BE"   $ fromIntegral >$< P.word16BE
+      , benchFE "word32BE"   $ fromIntegral >$< P.word32BE
+      , benchFE "word64BE"   $ fromIntegral >$< P.word64BE
+
+      , benchFE "floatBE"    $ fromIntegral >$< P.floatBE
+      , benchFE "doubleBE"   $ fromIntegral >$< P.doubleBE
+
+      -- little-endian
+      , benchFE "int16LE"    $ fromIntegral >$< P.int16LE
+      , benchFE "int32LE"    $ fromIntegral >$< P.int32LE
+      , benchFE "int64LE"    $ fromIntegral >$< P.int64LE
+
+      , benchFE "word16LE"   $ fromIntegral >$< P.word16LE
+      , benchFE "word32LE"   $ fromIntegral >$< P.word32LE
+      , benchFE "word64LE"   $ fromIntegral >$< P.word64LE
+
+      , benchFE "floatLE"    $ fromIntegral >$< P.floatLE
+      , benchFE "doubleLE"   $ fromIntegral >$< P.doubleLE
+
+      -- host-dependent
+      , benchFE "int16Host"  $ fromIntegral >$< P.int16Host
+      , benchFE "int32Host"  $ fromIntegral >$< P.int32Host
+      , benchFE "int64Host"  $ fromIntegral >$< P.int64Host
+      , benchFE "intHost"    $ fromIntegral >$< P.intHost
+
+      , benchFE "word16Host" $ fromIntegral >$< P.word16Host
+      , benchFE "word32Host" $ fromIntegral >$< P.word32Host
+      , benchFE "word64Host" $ fromIntegral >$< P.word64Host
+      , benchFE "wordHost"   $ fromIntegral >$< P.wordHost
+
+      , benchFE "floatHost"  $ fromIntegral >$< P.floatHost
+      , benchFE "doubleHost" $ fromIntegral >$< P.doubleHost
+      ]
+
+    , bgroup "Data.ByteString.Builder.Prim.ASCII"
+      [
+      -- decimal number
+        benchBE "int8Dec"     $ fromIntegral >$< P.int8Dec
+      , benchBE "int16Dec"    $ fromIntegral >$< P.int16Dec
+      , benchBE "int32Dec"    $ fromIntegral >$< P.int32Dec
+      , benchBE "int64Dec"    $ fromIntegral >$< P.int64Dec
+      , benchBE "intDec"      $ fromIntegral >$< P.intDec
+
+      , benchBE "word8Dec"    $ fromIntegral >$< P.word8Dec
+      , benchBE "word16Dec"   $ fromIntegral >$< P.word16Dec
+      , benchBE "word32Dec"   $ fromIntegral >$< P.word32Dec
+      , benchBE "word64Dec"   $ fromIntegral >$< P.word64Dec
+      , benchBE "wordDec"     $ fromIntegral >$< P.wordDec
+
+      -- hexadecimal number
+      , benchBE "word8Hex"    $ fromIntegral >$< P.word8Hex
+      , benchBE "word16Hex"   $ fromIntegral >$< P.word16Hex
+      , benchBE "word32Hex"   $ fromIntegral >$< P.word32Hex
+      , benchBE "word64Hex"   $ fromIntegral >$< P.word64Hex
+      , benchBE "wordHex"     $ fromIntegral >$< P.wordHex
+
+      -- fixed-width hexadecimal numbers
+      , benchFE "int8HexFixed"     $ fromIntegral >$< P.int8HexFixed
+      , benchFE "int16HexFixed"    $ fromIntegral >$< P.int16HexFixed
+      , benchFE "int32HexFixed"    $ fromIntegral >$< P.int32HexFixed
+      , benchFE "int64HexFixed"    $ fromIntegral >$< P.int64HexFixed
+
+      , benchFE "word8HexFixed"    $ fromIntegral >$< P.word8HexFixed
+      , benchFE "word16HexFixed"   $ fromIntegral >$< P.word16HexFixed
+      , benchFE "word32HexFixed"   $ fromIntegral >$< P.word32HexFixed
+      , benchFE "word64HexFixed"   $ fromIntegral >$< P.word64HexFixed
+
+      , benchFE "floatHexFixed"    $ fromIntegral >$< P.floatHexFixed
+      , benchFE "doubleHexFixed"   $ fromIntegral >$< P.doubleHexFixed
+      ]
+    , bgroup "intersperse"
+      [ bench "intersperse" $ whnf (S.intersperse 32) byteStringData
+      , bench "intersperse (unaligned)" $ whnf (S.intersperse 32) (S.drop 1 byteStringData)
+      ]
+    , bgroup "partition"
+      [
+        bgroup "strict"
+        [
+          bench "mostlyTrueFast"  $ partitionStrict (< (w 225))
+        , bench "mostlyFalseFast" $ partitionStrict (< (w 10))
+        , bench "balancedFast"    $ partitionStrict (< (w 128))
+
+        , bench "mostlyTrueSlow"  $ partitionStrict (\x -> hashWord8 x < w 225)
+        , bench "mostlyFalseSlow" $ partitionStrict (\x -> hashWord8 x < w 10)
+        , bench "balancedSlow"    $ partitionStrict (\x -> hashWord8 x < w 128)
+        ]
+      , bgroup "lazy"
+        [
+          bench "mostlyTrueFast"  $ partitionLazy (< (w 225))
+        , bench "mostlyFalseFast" $ partitionLazy (< (w 10))
+        , bench "balancedFast"    $ partitionLazy (< (w 128))
+
+        , bench "mostlyTrueSlow"  $ partitionLazy (\x -> hashWord8 x < w 225)
+        , bench "mostlyFalseSlow" $ partitionLazy (\x -> hashWord8 x < w 10)
+        , bench "balancedSlow"    $ partitionLazy (\x -> hashWord8 x < w 128)
+        ]
+      ]
+    , bgroup "sort" $ map (\s -> bench (S8.unpack s) $ nf S.sort s) sortInputs
+    , bgroup "words"
+      [ bench "lorem ipsum" $ nf S8.words loremIpsum
+      , bench "one huge word" $ nf S8.words byteStringData
+      ]
+    , bgroup "folds"
+      [ bgroup "foldl'" $ map (\s -> bench (show $ S.length s) $
+          nf (S.foldl' (\acc x -> acc + fromIntegral x) (0 :: Int)) s) foldInputs
+      , bgroup "foldr'" $ map (\s -> bench (show $ S.length s) $
+          nf (S.foldr' (\x acc -> fromIntegral x + acc) (0 :: Int)) s) foldInputs
+      , bgroup "mapAccumL" $ map (\s -> bench (show $ S.length s) $
+          nf (S.mapAccumL (\acc x -> (acc + fromIntegral x, succ x)) (0 :: Int)) s) foldInputs
+      , bgroup "mapAccumR" $ map (\s -> bench (show $ S.length s) $
+          nf (S.mapAccumR (\acc x -> (fromIntegral x + acc, succ x)) (0 :: Int)) s) foldInputs
+      , bgroup "scanl" $ map (\s -> bench (show $ S.length s) $
+          nf (S.scanl (+) 0) s) foldInputs
+      , bgroup "scanr" $ map (\s -> bench (show $ S.length s) $
+          nf (S.scanr (+) 0) s) foldInputs
+      , bgroup "filter" $ map (\s -> bench (show $ S.length s) $
+          nf (S.filter odd) s) foldInputs
+      ]
+    , bgroup "findIndexOrLength"
+      [ bench "takeWhile"      $ nf (L.takeWhile even) zeroes
+      , bench "dropWhile"      $ nf (L.dropWhile even) zeroes
+      , bench "break"          $ nf (L.break odd) zeroes
+      , bench "group zeroes"   $ nf L.group zeroes
+      , bench "group zero-one" $ nf L.group zeroOneRepeating
+      , bench "groupBy (>=)"   $ nf (L.groupBy (>=)) zeroes
+      , bench "groupBy (>)"    $ nf (L.groupBy (>)) zeroes
+      ]
+    , bgroup "findIndex_"
+      [ bench "findIndices"    $ nf (sum . S.findIndices (\x -> x ==  129 || x == 72)) byteStringData
+      , bench "find"           $ nf (S.find (>= 198)) byteStringData
+      ]
+    , bgroup "findIndexEnd"
+      [ bench "findIndexEnd"   $ nf (S.findIndexEnd (<= 57)) byteStringData
+      , bench "elemIndexInd"   $ nf (S.elemIndexEnd 42) byteStringData
+      ]
+    , bgroup "traversals"
+      [ bench "map (+1)"   $ nf (S.map (+ 1)) largeTraversalInput
+      , bench "map (+1)"   $ nf (S.map (+ 1)) smallTraversalInput
+      ]
+    , benchBoundsCheckFusion
+    , benchCSV
+    , benchIndices
+    ]
diff --git a/bench/BenchBoundsCheckFusion.hs b/bench/BenchBoundsCheckFusion.hs
new file mode 100644
--- /dev/null
+++ b/bench/BenchBoundsCheckFusion.hs
@@ -0,0 +1,105 @@
+-- |
+-- Copyright   : (c) 2011 Simon Meier
+-- License     : BSD3-style (see LICENSE)
+--
+-- Maintainer  : Simon Meier <iridcode@gmail.com>
+-- Stability   : experimental
+-- Portability : tested on GHC only
+--
+-- Benchmark that the bounds checks fuse.
+
+{-# LANGUAGE PackageImports, ScopedTypeVariables, BangPatterns #-}
+
+module BenchBoundsCheckFusion (benchBoundsCheckFusion) where
+
+import Prelude hiding (words)
+import Data.Monoid
+import Data.Foldable (foldMap)
+import Test.Tasty.Bench
+
+import qualified Data.ByteString                  as S
+import qualified Data.ByteString.Lazy             as L
+
+import           Data.ByteString.Builder
+import           Data.ByteString.Builder.Extra
+import           Data.ByteString.Builder.Prim
+                   ( FixedPrim, BoundedPrim, (>$<), (>*<) )
+import qualified Data.ByteString.Builder.Prim          as P
+import qualified Data.ByteString.Builder.Internal      as I
+import qualified Data.ByteString.Builder.Prim.Internal as I
+
+import Foreign
+
+------------------------------------------------------------------------------
+-- Benchmark support
+------------------------------------------------------------------------------
+
+countToZero :: Int -> Maybe (Int, Int)
+countToZero 0 = Nothing
+countToZero n = Just (n, n - 1)
+
+
+------------------------------------------------------------------------------
+-- Benchmark
+------------------------------------------------------------------------------
+
+-- input data (NOINLINE to ensure memoization)
+----------------------------------------------
+
+-- | Few-enough repetitions to avoid making GC too expensive.
+nRepl :: Int
+nRepl = 10000
+
+{-# NOINLINE intData #-}
+intData :: [Int]
+intData = [1..nRepl]
+
+-- benchmark wrappers
+---------------------
+
+{-# INLINE benchB #-}
+benchB :: String -> a -> (a -> Builder) -> Benchmark
+benchB name x b =
+    bench (name ++" (" ++ show nRepl ++ ")") $
+        whnf (L.length . toLazyByteString . b) x
+
+{-# INLINE benchBInts #-}
+benchBInts :: String -> ([Int] -> Builder) -> Benchmark
+benchBInts name = benchB name intData
+
+
+-- benchmarks
+-------------
+
+benchBoundsCheckFusion :: Benchmark
+benchBoundsCheckFusion = bgroup "BoundsCheckFusion"
+    [ bgroup "Data.ByteString.Builder"
+        [ benchBInts "foldMap (left-assoc)" $
+            foldMap (\x -> (stringUtf8 "s" `mappend` intHost x) `mappend` intHost x)
+
+        , benchBInts "foldMap (right-assoc)" $
+            foldMap (\x -> intHost x `mappend` (intHost x `mappend` stringUtf8 "s"))
+
+        , benchBInts "foldMap [manually fused, left-assoc]" $
+            foldMap (\x -> stringUtf8 "s" `mappend` P.primBounded (P.liftFixedToBounded $ P.intHost >*< P.intHost) (x, x))
+
+        , benchBInts "foldMap [manually fused, right-assoc]" $
+            foldMap (\x -> P.primBounded (P.liftFixedToBounded $ P.intHost >*< P.intHost) (x, x) `mappend` stringUtf8 "s")
+        ]
+    ]
+
+{-# RULES
+
+"append/encodeWithB" forall w1 w2 x1 x2.
+       I.append (P.primBounded w1 x1) (P.primBounded w2 x2)
+     = P.primBounded (I.pairB w1 w2) (x1, x2)
+
+"append/encodeWithB/assoc_r" forall w1 w2 x1 x2 b.
+       I.append (P.primBounded w1 x1) (I.append (P.primBounded w2 x2) b)
+     = I.append (P.primBounded (I.pairB w1 w2) (x1, x2)) b
+
+"append/encodeWithB/assoc_l" forall w1 w2 x1 x2 b.
+       I.append (I.append b (P.primBounded w1 x1)) (P.primBounded w2 x2)
+     = I.append b (P.primBounded (I.pairB w1 w2) (x1, x2))
+  #-}
+
diff --git a/bench/BenchCSV.hs b/bench/BenchCSV.hs
new file mode 100644
--- /dev/null
+++ b/bench/BenchCSV.hs
@@ -0,0 +1,547 @@
+-- |
+-- Copyright   : (c) 2010-2011 Simon Meier
+-- License     : BSD3-style (see LICENSE)
+--
+-- Maintainer  : Simon Meier <iridcode@gmail.com>
+-- Stability   : experimental
+-- Portability : tested on GHC only
+--
+-- Running example for documentation of Data.ByteString.Builder
+--
+
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module BenchCSV (benchCSV) where
+
+-- **************************************************************************
+-- CamHac 2011: An introduction to Data.ByteString.Builder
+-- **************************************************************************
+
+
+{- The Encoding Problem
+ ----------------------
+
+ Encoding: Conversion from a Haskell value to a sequence of bytes.
+
+
+ Efficient encoding implementation:
+
+   1. represent sequence of bytes as a list of byte arrays (chunks)
+   2. generate chunks that are large on average
+   3. avoid intermediate copies/datastructures
+
+ Compositionality:
+
+   4. support fast append
+
+
+ Problem: Provide a library for defining compositional, efficient encodings.
+
+-}
+
+
+
+{- Data.ByteString.Builder
+ ------------------------------
+
+ A solution to the "Encoding Problem"  (based on the code of blaze-builder).
+
+ Builder creation:
+
+   word8   :: Word8 -> Builder
+   int64LE :: Int64 -> Builder
+   floatBE :: Float -> Builder
+   ....
+
+
+ Builder composition via its Monoid instance:
+
+   word8 10 `mappend` floatBE 1.4
+
+
+ Builder execution by converting it to a lazy bytestring:
+
+   toLazyByteString :: Builder -> L.ByteString
+
+-}
+
+
+{- Typical users of Builders
+ ---------------------------
+
+ binary, text, aeson, blaze-html, blaze-textual, warp, snap-server, ...
+
+ => they want support for maximal performance!
+ => use of Builders is rather local: in rendering/encoding functions.
+
+-}
+
+
+
+{- Notable properties
+ --------------------
+
+ * Built-in UTF-8 support: very hard to get efficient otherwise.
+
+     stringUtf8 :: String -> Builder
+     intDec :: Int -> Builder
+     intHex :: Int -> Builder
+
+ * Fine-grained control over when to copy/reference existing bytestrings
+
+ * EDSL for defining low-level Encodings of bounded values (e.g., Int, Char)
+   to improve speed of escaping and similar operations.
+
+ * If used together with iteratee-style IO: no 'unsafePerformIO' required
+
+-}
+
+
+{- An example problem:
+ ---------------------
+
+ Rendering a table in comma-separated-value (CSV) format using UTF-8 encoded
+ Unicode characters.
+
+ * We are willing to fuse table-rendering with UTF8-encoding to achieve better
+   performance.
+
+-}
+
+import Control.DeepSeq
+import Data.Char (ord)
+import Data.Foldable (foldMap)
+import Data.Monoid
+
+import Test.Tasty.Bench
+
+import qualified Data.ByteString         as S
+import qualified Data.ByteString.Lazy    as L
+import           Data.ByteString.Builder as B
+import           Data.ByteString.Builder.Prim.Internal ( (>*<), (>$<) )
+import qualified Data.ByteString.Builder.Prim         as E
+
+-- To be used in a later comparison
+import qualified Data.DList                 as D
+
+-- bytestring benchmarks cannot depend on text because of a circular dependency.
+-- Anyways these comparisons are of historical interest only, so disabled for now.
+-- A curious soul can re-enable them by moving benchmarks to a separate package
+-- and adding text to build-depends.
+#ifdef MIN_VERSION_text
+import qualified Data.Text.Lazy             as TL
+import qualified Data.Text.Lazy.Encoding    as TL
+import qualified Data.Text.Lazy.Builder     as TB
+import qualified Data.Text.Lazy.Builder.Int as TB
+#endif
+
+------------------------------------------------------------------------------
+-- Simplife CSV Tables
+------------------------------------------------------------------------------
+
+data Cell = StringC String
+          | IntC Int
+          deriving( Eq, Ord, Show )
+
+type Row   = [Cell]
+type Table = [Row]
+
+-- Example data
+strings :: [String]
+strings =  ["hello", "\"1\"", "λ-wörld"]
+
+table :: Table
+table = [map StringC strings, map IntC [-3..3]]
+
+
+-- | The rendered 'table':
+--
+-- > "hello","\"1\"","λ-wörld"
+-- > -3,-2,-1,0,1,2,3
+--
+
+
+-- | A bigger table for benchmarking our encoding functions.
+maxiTable :: Table
+maxiTable = take 1000 $ cycle table
+
+
+------------------------------------------------------------------------------
+-- String based rendering
+------------------------------------------------------------------------------
+
+renderString :: String -> String
+renderString cs = "\"" ++ concatMap escape cs ++ "\""
+  where
+    escape '\\' = "\\"
+    escape '\"' = "\\\""
+    escape c    = return c
+
+renderCell :: Cell -> String
+renderCell (StringC cs) = renderString cs
+renderCell (IntC i)     = show i
+
+renderRow :: Row -> String
+renderRow []     = ""
+renderRow (c:cs) = renderCell c ++ concat [',' : renderCell c' | c' <- cs]
+
+renderTable :: Table -> String
+renderTable rs = concat [renderRow r ++ "\n" | r <- rs]
+
+-- 1.36 ms
+benchString :: Benchmark
+benchString = bench "renderTable maxiTable" $ nf renderTable maxiTable
+
+-- 1.36 ms
+benchStringUtf8 :: Benchmark
+benchStringUtf8 = bench "utf8 + renderTable maxiTable" $
+  nf (L.length . B.toLazyByteString . B.stringUtf8 . renderTable) maxiTable
+
+
+-- using difference lists:  0.91 ms
+--
+--  (++) is a performance-grinch!
+
+
+------------------------------------------------------------------------------
+-- Builder based rendering
+------------------------------------------------------------------------------
+
+-- As a reminder:
+--
+-- import  Data.ByteString.Builder       as B
+
+renderStringB :: String -> Builder
+renderStringB cs = B.charUtf8 '"' <> foldMap escape cs <> B.charUtf8 '"'
+  where
+    escape '\\' = B.charUtf8 '\\' <> B.charUtf8 '\\'
+    escape '\"' = B.charUtf8 '\\' <> B.charUtf8 '"'
+    escape c    = B.charUtf8 c
+
+renderCellB :: Cell -> Builder
+renderCellB (StringC cs) = renderStringB cs
+renderCellB (IntC i)     = B.intDec i
+
+renderRowB :: Row -> Builder
+renderRowB []     = mempty
+renderRowB (c:cs) =
+    renderCellB c <> mconcat [ B.charUtf8 ',' <> renderCellB c' | c' <- cs ]
+
+renderTableB :: Table -> Builder
+renderTableB rs = mconcat [renderRowB r <> B.charUtf8 '\n' | r <- rs]
+
+-- 0.81ms
+benchBuilderUtf8 :: Benchmark
+benchBuilderUtf8 = bench "utf8 + renderTableB maxiTable" $
+  nf (L.length . B.toLazyByteString . renderTableB) maxiTable
+
+-- 1.11x  faster than DList
+
+-- However: touching the whole table 'nf maxiTable' takes  0.27ms
+
+-- 1.16x  faster than DList on the code path other than touching all data
+--        (0.91 - 0.27) / (0.82 - 0.27)
+
+
+------------------------------------------------------------------------------
+-- Baseline: Touching all data
+------------------------------------------------------------------------------
+
+instance NFData Cell where
+  rnf (StringC cs) = rnf cs
+  rnf (IntC i)     = rnf i
+
+-- 0.27 ms
+benchNF :: Benchmark
+benchNF = bench "nf maxiTable" $ nf id maxiTable
+
+
+------------------------------------------------------------------------------
+-- Exploiting bounded encodings
+------------------------------------------------------------------------------
+
+{- Why 'Bounded Encodings'?
+ --------------------------
+
+ Hot code of encoding implementations:
+
+ * Appending Builders: Optimized already.
+
+ * Encoding primitive Haskell values: room for optimization:
+
+     - reduce buffer-free checks
+     - remove jumps/function calls
+     - hoist constant values out of inner-loops
+       (e.g., the loop for encoding the elements of a list)
+
+ * Bounded encoding:
+     an encoding that never takes more than a fixed number of bytes.
+
+     - intuitively: (Int,     Ptr Word8 -> IO (Ptr Word8))
+                     ^bound   ^ low-level encoding function
+
+     - compositional: coalesce buffer-checks, ...
+
+       E.encodeIfB :: (a -> Bool)
+                   -> BoundedPrim a -> BoundedPrim a -> BoundedPrim a
+       E.charUtf8  :: BoundedPrim Char
+       (>*<)       :: BoundedPrim a -> BoundedPrim b -> BoundedPrim (a, b)
+
+       (>$<)       :: (b -> a) -> BoundedPrim a -> BoundedPrim b
+
+       ^ BoundedPrims are contrafunctors; like most data-sinks
+
+
+     - Implementation relies heavily on inlining to compute bounds and
+       low-level encoding code during compilation.
+-}
+
+renderStringBE :: String -> Builder
+renderStringBE cs =
+    B.charUtf8 '"' <> E.primMapListBounded escape cs <> B.charUtf8 '"'
+  where
+    escape :: E.BoundedPrim Char
+    escape =
+      E.condB (== '\\') (const ('\\', '\\') >$< E.charUtf8 >*< E.charUtf8) $
+      E.condB (== '\"') (const ('\\', '\"') >$< E.charUtf8 >*< E.charUtf8) $
+      E.charUtf8
+
+renderCellBE :: Cell -> Builder
+renderCellBE (StringC cs) = renderStringBE cs
+renderCellBE (IntC i)     = B.intDec i
+
+renderRowBE :: Row -> Builder
+renderRowBE []     = mempty
+renderRowBE (c:cs) =
+    renderCellBE c <> mconcat [ B.charUtf8 ',' <> renderCellBE c' | c' <- cs ]
+
+renderTableBE :: Table -> Builder
+renderTableBE rs = mconcat [renderRowBE r <> B.charUtf8 '\n' | r <- rs]
+
+-- 0.65 ms
+benchBuilderEncodingUtf8 :: Benchmark
+benchBuilderEncodingUtf8 = bench "utf8 + renderTableBE maxiTable" $
+  nf (L.length . B.toLazyByteString . renderTableBE) maxiTable
+
+
+-- 1.4x faster than DList based
+
+-- 1.7x faster than DList based on code other than touching all data
+
+
+------------------------------------------------------------------------------
+-- Difference-list based rendering
+------------------------------------------------------------------------------
+
+type DString = D.DList Char
+
+renderStringD :: String -> DString
+renderStringD cs = return '"' <> foldMap escape cs <> return '"'
+  where
+    escape '\\' = D.fromList "\\\\"
+    escape '\"' = D.fromList "\\\""
+    escape c    = return c
+
+renderCellD :: Cell -> DString
+renderCellD (StringC cs) = renderStringD cs
+renderCellD (IntC i)     = D.fromList $ show i
+
+renderRowD :: Row -> DString
+renderRowD []     = mempty
+renderRowD (c:cs) =
+    renderCellD c <> mconcat [ return ',' <> renderCellD c' | c' <- cs ]
+
+renderTableD :: Table -> DString
+renderTableD rs = mconcat [renderRowD r <> return '\n' | r <- rs]
+
+-- 0.91 ms
+benchDListUtf8 :: Benchmark
+benchDListUtf8 = bench "utf8 + renderTableD maxiTable" $
+  nf (L.length . B.toLazyByteString . B.stringUtf8 . D.toList . renderTableD) maxiTable
+
+------------------------------------------------------------------------------
+-- Text Builder
+------------------------------------------------------------------------------
+
+#ifdef MIN_VERSION_text
+
+renderStringTB :: String -> TB.Builder
+renderStringTB cs = TB.singleton '"' <> foldMap escape cs <> TB.singleton '"'
+  where
+    escape '\\' = "\\\\"
+    escape '\"' = "\\\""
+    escape c    = TB.singleton c
+
+renderCellTB :: Cell -> TB.Builder
+renderCellTB (StringC cs) = renderStringTB cs
+renderCellTB (IntC i)     = TB.decimal i
+
+renderRowTB :: Row -> TB.Builder
+renderRowTB []     = mempty
+renderRowTB (c:cs) =
+    renderCellTB c <> mconcat [ TB.singleton ',' <> renderCellTB c' | c' <- cs ]
+
+renderTableTB :: Table -> TB.Builder
+renderTableTB rs = mconcat [renderRowTB r <> TB.singleton '\n' | r <- rs]
+
+-- 0.95 ms
+benchTextBuilder :: Benchmark
+benchTextBuilder = bench "renderTableTB maxiTable" $
+  nf (TL.length . TB.toLazyText . renderTableTB) maxiTable
+
+-- 1.10 ms
+benchTextBuilderUtf8 :: Benchmark
+benchTextBuilderUtf8 = bench "utf8 + renderTableTB maxiTable" $
+  nf (L.length . TL.encodeUtf8 . TB.toLazyText . renderTableTB) maxiTable
+
+#endif
+
+------------------------------------------------------------------------------
+-- Benchmarking
+------------------------------------------------------------------------------
+
+benchCSV :: Benchmark
+benchCSV = bgroup "CSV"
+      [ benchNF
+      , benchString
+      , benchStringUtf8
+      , benchDListUtf8
+#ifdef MIN_VERSION_text
+      , benchTextBuilder
+      , benchTextBuilderUtf8
+#endif
+      , benchBuilderUtf8
+      , benchBuilderEncodingUtf8
+      ]
+  where
+    encodeUtf8CSV = B.toLazyByteString . renderTableBE
+
+
+{- On a Core 2 Duo 2.2 GHz running a 32-bit Linux:
+
+
+touching all data:                 0.25 ms
+string rendering:                  1.36 ms
+string rendering + utf8 encoding:  1.36 ms
+DList rendering  + utf8 encoding:  0.91 ms
+builder rendering (incl. utf8):    0.82 ms
+builder + faster escaping:         0.65 ms
+
+text builder:                      0.95 ms
+text builder + utf8 encoding:      1.10 ms
+binary builder + char8 (!!):       1.22 ms
+DList render + utf8-light:         4.12 ms
+
+How to improve further?
+  * Use packed formats for string literals
+    - fast memcpy  (that's what blaze-html does for tags)
+    - using Text literals should also help
+
+
+results from criterion:
+
+benchmarking nf maxiTable
+mean: 257.2927 us, lb 255.9210 us, ub 259.6692 us, ci 0.950
+std dev: 9.026280 us, lb 5.887942 us, ub 12.76582 us, ci 0.950
+
+benchmarking renderTable maxiTable
+mean: 1.358458 ms, lb 1.356732 ms, ub 1.362377 ms, ci 0.950
+std dev: 12.66932 us, lb 7.110377 us, ub 24.97397 us, ci 0.950
+
+benchmarking utf8 + renderTable maxiTable
+mean: 1.364343 ms, lb 1.362391 ms, ub 1.366973 ms, ci 0.950
+std dev: 11.65388 us, lb 9.094074 us, ub 17.47765 us, ci 0.950
+
+benchmarking utf8 + renderTableD maxiTable
+mean: 909.5255 us, lb 908.0049 us, ub 911.7639 us, ci 0.950
+std dev: 9.434182 us, lb 6.906120 us, ub 15.43223 us, ci 0.950
+
+benchmarking utf8-light + renderTable maxiTable
+mean: 4.128315 ms, lb 4.121109 ms, ub 4.138436 ms, ci 0.950
+std dev: 42.93755 us, lb 32.58115 us, ub 58.61780 us, ci 0.950
+
+benchmarking char8 + renderTableBinB maxiTable
+mean: 1.224156 ms, lb 1.222510 ms, ub 1.226101 ms, ci 0.950
+std dev: 9.046150 us, lb 7.568433 us, ub 11.74996 us, ci 0.950
+
+benchmarking renderTableTB maxiTable
+mean: 954.8066 us, lb 953.6650 us, ub 957.0134 us, ci 0.950
+std dev: 7.763098 us, lb 5.072194 us, ub 14.09216 us, ci 0.950
+
+benchmarking utf8 + renderTableTB maxiTable
+mean: 1.095913 ms, lb 1.094811 ms, ub 1.098280 ms, ci 0.950
+std dev: 7.865781 us, lb 4.189907 us, ub 15.24606 us, ci 0.950
+
+benchmarking utf8 + renderTableB maxiTable
+mean: 818.0223 us, lb 816.5118 us, ub 819.9397 us, ci 0.950
+std dev: 8.603917 us, lb 6.764347 us, ub 12.29236 us, ci 0.950
+
+benchmarking utf8 + renderTableBE maxiTable
+mean: 646.5248 us, lb 645.3735 us, ub 648.2405 us, ci 0.950
+std dev: 7.147889 us, lb 5.222494 us, ub 11.82482 us, ci 0.950
+
+-}
+
+
+
+{- Conclusion:
+ -------------
+
+ * Whenever generating a sequence of bytes: use the 'Builder' type
+
+   => chunks can always be kept large; impossible when exporting only
+      a strict/lazy bytestring interface.
+
+   => filtering/mapping lazy bytestrings now automatically defragments
+      the output and guarantees a large chunk size.
+
+
+ * Status of work: API complete, documentation needs more reviewing.
+
+
+ * Bounded encodings: safely exploiting low-level optimizations
+
+   => a performance advantage on other outputstream-libraries?
+
+
+                           ---------------
+                           - Questions ? -
+                           ---------------
+
+-}
+
+
+
+
+{- Implementation outline:
+ ------------------------
+
+data BufferRange = BufferRange {-# UNPACK #-} !(Ptr Word8)  -- First byte of range
+                               {-# UNPACK #-} !(Ptr Word8)  -- First byte /after/ range
+
+newtype BuildStep a =
+    BuildStep { runBuildStep :: BufferRange -> IO (BuildSignal a) }
+
+data BuildSignal a =
+    Done             !(Ptr Word8)    -- next free byte in current buffer
+                     a               -- return value
+  | BufferFull
+                     !Int            -- minimal size of next buffer
+                     !(Ptr Word8)    -- next free byte in current buffer
+                     !(BuildStep a)  -- continuation to call on next buffer
+  | InsertByteString
+                     !(Ptr Word8)    -- next free byte in current buffer
+                     !S.ByteString   -- bytestring to insert directly
+                     !(BuildStep a)  -- continuation to call on next buffer
+
+
+-- | A "difference list" of build-steps.
+newtype Builder = Builder (forall r. BuildStep r -> BuildStep r)
+
+
+-- | The corresponding "Writer" monad.
+newtype Put a = Put { unPut :: forall r. (a -> BuildStep r) -> BuildStep r }
+
+
+-}
diff --git a/bench/BenchIndices.hs b/bench/BenchIndices.hs
new file mode 100644
--- /dev/null
+++ b/bench/BenchIndices.hs
@@ -0,0 +1,83 @@
+-- |
+-- Copyright   : (c) 2020 Peter Duchovni
+-- License     : BSD3-style (see LICENSE)
+--
+-- Maintainer  : Peter Duchovni <caufeminecraft+github@gmail.com>
+--
+-- Benchmark elemIndex, findIndex, elemIndices, and findIndices
+
+{-# LANGUAGE BangPatterns        #-}
+
+module BenchIndices (benchIndices) where
+
+import           Data.Foldable                         (foldMap)
+import           Data.Maybe                            (listToMaybe)
+import           Data.Monoid
+import           Data.String
+import           Test.Tasty.Bench
+import           Prelude                               hiding (words)
+import           Data.Word                             (Word8)
+
+import qualified Data.ByteString                       as S
+import qualified Data.ByteString.Unsafe                as S
+
+
+------------------------------------------------------------------------------
+-- Benchmark
+------------------------------------------------------------------------------
+
+-- ASCII \n to ensure no typos
+nl :: Word8
+nl = 0xa
+{-# INLINE nl #-}
+
+-- non-inlined equality test
+nilEq :: Word8 -> Word8 -> Bool
+{-# NOINLINE nilEq #-}
+nilEq = (==)
+
+-- lines of 200 letters from a to e, followed by repeated letter f
+absurdlong :: S.ByteString
+absurdlong = S.replicate 200 0x61 <> S.singleton nl
+          <> S.replicate 200 0x62 <> S.singleton nl
+          <> S.replicate 200 0x63 <> S.singleton nl
+          <> S.replicate 200 0x64 <> S.singleton nl
+          <> S.replicate 200 0x65 <> S.singleton nl
+          <> S.replicate 999999 0x66
+
+benchIndices :: Benchmark
+benchIndices = bgroup "Indices"
+    [ bgroup "ByteString strict first index" $
+        [ bench "FindIndices" $ nf (listToMaybe . S.findIndices (== nl)) absurdlong
+        , bench "ElemIndices" $ nf (listToMaybe . S.elemIndices     nl)  absurdlong
+        , bench "FindIndex"   $ nf (S.findIndex (== nl)) absurdlong
+        , bench "ElemIndex"   $ nf (S.elemIndex     nl)  absurdlong
+        ]
+    , bgroup "ByteString strict second index" $
+        [ bench "FindIndices" $ nf (listToMaybe . tail . S.findIndices (== nl)) absurdlong
+        , bench "ElemIndices" $ nf (listToMaybe . tail . S.elemIndices     nl)  absurdlong
+        , bench "FindIndex"   $ nf bench_find_index_second absurdlong
+        , bench "ElemIndex"   $ nf bench_elem_index_second absurdlong
+        ]
+    , bgroup "ByteString index equality inlining" $
+        [ bench "FindIndices/inlined"     $ nf (S.findIndices    (== nl)) absurdlong
+        , bench "FindIndices/non-inlined" $ nf (S.findIndices (nilEq nl)) absurdlong
+        , bench "FindIndex/inlined"       $ nf (S.findIndex      (== nl)) absurdlong
+        , bench "FindIndex/non-inlined"   $ nf (S.findIndex   (nilEq nl)) absurdlong
+        ]
+    ]
+
+bench_find_index_second :: S.ByteString -> Maybe Int
+bench_find_index_second bs =
+  let isNl = (== nl)
+   in case S.findIndex isNl bs of
+        Just !i -> S.findIndex isNl (S.unsafeDrop (i+1) bs)
+        Nothing -> Nothing
+{-# INLINE bench_find_index_second #-}
+
+bench_elem_index_second :: S.ByteString -> Maybe Int
+bench_elem_index_second bs =
+    case S.elemIndex nl bs of
+        Just !i -> S.elemIndex nl (S.unsafeDrop (i+1) bs)
+        Nothing -> Nothing
+{-# INLINE bench_elem_index_second #-}
diff --git a/bytestring.cabal b/bytestring.cabal
--- a/bytestring.cabal
+++ b/bytestring.cabal
@@ -1,5 +1,5 @@
 Name:                bytestring
-Version:             0.11.0.0
+Version:             0.11.1.0
 Synopsis:            Fast, compact, strict and lazy byte strings with a list interface
 Description:
     An efficient compact, immutable byte string type (both strict and lazy)
@@ -51,7 +51,7 @@
 
 Author:              Don Stewart,
                      Duncan Coutts
-Maintainer:          Duncan Coutts <duncan@community.haskell.org>
+Maintainer:          Haskell Bytestring Team <andrew.lelechenko@gmail.com>, Core Libraries Committee
 Homepage:            https://github.com/haskell/bytestring
 Bug-reports:         https://github.com/haskell/bytestring/issues
 Tested-With:         GHC==8.10.1, GHC==8.8.3, GHC==8.6.5, GHC==8.4.4, GHC==8.2.2,
@@ -94,10 +94,9 @@
                      Data.ByteString.Builder.Prim.Binary
                      Data.ByteString.Builder.Prim.ASCII
                      Data.ByteString.Builder.Prim.Internal.Floating
-                     Data.ByteString.Builder.Prim.Internal.UncheckedShifts
                      Data.ByteString.Builder.Prim.Internal.Base16
 
-  default-language:  Haskell98
+  default-language:  Haskell2010
   other-extensions:  CPP,
                      ForeignFunctionInterface,
                      BangPatterns
@@ -140,3 +139,60 @@
   if impl(ghc >= 6.9) && impl(ghc < 6.11)
     cpp-options: -DINTEGER_GMP
     build-depends: integer >= 0.1 && < 0.2
+
+test-suite prop-compiled
+  type:             exitcode-stdio-1.0
+  main-is:          Properties.hs
+  other-modules:    Rules
+                    QuickCheckUtils
+  hs-source-dirs:   tests
+  build-depends:    base, bytestring, ghc-prim, deepseq,
+                    tasty, tasty-quickcheck
+  ghc-options:      -fwarn-unused-binds
+                    -threaded -rtsopts
+  default-language: Haskell2010
+
+test-suite lazy-hclose
+  type:             exitcode-stdio-1.0
+  main-is:          LazyHClose.hs
+  hs-source-dirs:   tests
+  build-depends:    base, bytestring, ghc-prim, deepseq,
+                    tasty, tasty-quickcheck
+  ghc-options:      -fwarn-unused-binds
+                    -threaded -rtsopts
+  default-language: Haskell2010
+
+test-suite test-builder
+  type:             exitcode-stdio-1.0
+  hs-source-dirs:   tests/builder
+  main-is:          TestSuite.hs
+  other-modules:    Data.ByteString.Builder.Tests
+                    Data.ByteString.Builder.Prim.Tests
+                    Data.ByteString.Builder.Prim.TestUtils
+  build-depends:    base, bytestring, ghc-prim,
+                    deepseq,
+                    dlist                      >= 0.5 && < 0.9,
+                    transformers               >= 0.3,
+                    tasty,
+                    tasty-hunit,
+                    tasty-quickcheck
+  if impl(ghc < 8.4)
+    build-depends:  ghc-byteorder
+  ghc-options:      -Wall -fwarn-tabs -threaded -rtsopts
+  default-language: Haskell2010
+
+benchmark bytestring-bench
+  main-is:          BenchAll.hs
+  other-modules:    BenchBoundsCheckFusion
+                    BenchCSV
+                    BenchIndices
+  type:             exitcode-stdio-1.0
+  hs-source-dirs:   bench
+  default-language: Haskell2010
+  ghc-options:      -O2
+  build-depends:    base,
+                    bytestring,
+                    deepseq,
+                    dlist,
+                    tasty-bench,
+                    random
diff --git a/cbits/fpstring.c b/cbits/fpstring.c
--- a/cbits/fpstring.c
+++ b/cbits/fpstring.c
@@ -30,6 +30,10 @@
  */
 
 #include "fpstring.h"
+#if defined(__x86_64__)
+#include <emmintrin.h>
+#include <xmmintrin.h>
+#endif
 
 /* copy a string in reverse */
 void fps_reverse(unsigned char *q, unsigned char *p, size_t n) {
@@ -44,7 +48,21 @@
                      unsigned char *p,
                      size_t n,
                      unsigned char c) {
-
+#if defined(__x86_64__)
+  {
+    const __m128i separator = _mm_set1_epi8(c);
+    const unsigned char *const p_begin = p;
+    const unsigned char *const p_end = p_begin + n - 9;
+    while (p < p_end) {
+      const __m128i eight_src_bytes = _mm_loadl_epi64((__m128i *)p);
+      const __m128i sixteen_dst_bytes = _mm_unpacklo_epi8(eight_src_bytes, separator);
+      _mm_storeu_si128((__m128i *)q, sixteen_dst_bytes);
+      p += 8;
+      q += 16;
+    }
+    n -= p - p_begin;
+  }
+#endif
     while (n > 1) {
         *q++ = *p++;
         *q++ = c;
diff --git a/tests/LazyHClose.hs b/tests/LazyHClose.hs
new file mode 100644
--- /dev/null
+++ b/tests/LazyHClose.hs
@@ -0,0 +1,64 @@
+module Main (main) where
+
+import Control.Monad (void, forM_)
+import Data.ByteString.Internal (toForeignPtr)
+import Foreign.C.String (withCString)
+import Foreign.ForeignPtr (finalizeForeignPtr)
+import System.IO (openFile, openTempFile, hClose, hPutStrLn, IOMode(..))
+import System.Posix.Internals (c_unlink)
+
+import qualified Data.ByteString            as S
+import qualified Data.ByteString.Char8      as S8
+import qualified Data.ByteString.Lazy       as L
+import qualified Data.ByteString.Lazy.Char8 as L8
+
+main :: IO ()
+main = do
+    let n = 1000
+    (fn, h) <- openTempFile "." "lazy-hclose-test.tmp"
+    hPutStrLn h "x"
+    hClose h
+
+    ------------------------------------------------------------------------
+    -- readFile tests
+
+    putStrLn "Testing resource leaks for Strict.readFile"
+    forM_ [1..n] $ const $ do
+         r <- S.readFile fn
+         appendFile fn "" -- will fail, if fn has not been closed yet
+
+    putStrLn "Testing resource leaks for Lazy.readFile"
+    forM_ [1..n] $ const $ do
+         r <- L.readFile fn
+         L.length r `seq` return ()
+         appendFile fn "" -- will fail, if fn has not been closed yet
+
+    -- manage the resources explicitly.
+    putStrLn "Testing resource leaks when converting lazy to strict"
+    forM_ [1..n] $ const $ do
+         let release c = finalizeForeignPtr fp where (fp,_,_) = toForeignPtr c
+         r <- L.readFile fn
+         mapM_ release (L.toChunks r)
+         appendFile fn "" -- will fail, if fn has not been closed yet
+
+    ------------------------------------------------------------------------
+    -- hGetContents tests
+
+    putStrLn "Testing strict hGetContents"
+    forM_ [1..n] $ const $ do
+         h <- openFile fn ReadMode
+         r <- S.hGetContents h
+         S.last r `seq` return ()
+         appendFile fn "" -- will fail, if fn has not been closed yet
+
+    putStrLn "Testing lazy hGetContents"
+    forM_ [1..n] $ const $ do
+         h <- openFile fn ReadMode
+         r <- L.hGetContents h
+         L.last r `seq` return ()
+         appendFile fn "" -- will fail, if fn has not been closed yet
+
+    removeFile fn
+
+removeFile :: String -> IO ()
+removeFile fn = void $ withCString fn c_unlink
diff --git a/tests/Properties.hs b/tests/Properties.hs
new file mode 100644
--- /dev/null
+++ b/tests/Properties.hs
@@ -0,0 +1,2556 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE UnboxedTuples #-}
+--
+-- Must have rules off, otherwise the rewrite rules will replace the rhs
+-- with the lhs, and we only end up testing lhs == lhs
+--
+
+--
+-- -fhpc interferes with rewrite rules firing.
+--
+
+import Foreign.C.String (withCString)
+import Foreign.Storable
+import Foreign.ForeignPtr
+import Foreign.Marshal.Alloc
+import Foreign.Marshal.Array
+import GHC.Ptr
+import Test.Tasty.QuickCheck
+import Control.Applicative
+import Control.Monad
+import Control.Concurrent
+import Control.Exception
+import System.Posix.Internals (c_unlink)
+
+import Data.List
+import Data.Char
+import Data.Word
+import Data.Maybe
+import Data.Int (Int64)
+import Data.Monoid
+#if MIN_VERSION_base(4,9,0)
+import Data.Semigroup
+#endif
+import GHC.Exts (Int(..), newPinnedByteArray#, unsafeFreezeByteArray#)
+import GHC.ST (ST(..), runST)
+
+import Text.Printf
+import Data.String
+
+import System.Environment
+import System.IO
+
+import Data.ByteString.Lazy (ByteString(..), pack , unpack)
+import qualified Data.ByteString.Lazy as L
+import Data.ByteString.Lazy.Internal (ByteString(..))
+
+import qualified Data.ByteString            as P
+import qualified Data.ByteString.Internal   as P
+import qualified Data.ByteString.Unsafe     as P
+import qualified Data.ByteString.Char8      as C
+import qualified Data.ByteString.Short      as Short
+
+import qualified Data.ByteString.Lazy.Char8 as LC
+import qualified Data.ByteString.Lazy.Char8 as D
+
+import qualified Data.ByteString.Lazy.Internal as L
+import Prelude hiding (abs)
+
+import Rules
+import QuickCheckUtils
+import Test.Tasty
+import Test.Tasty.QuickCheck
+
+toInt64 :: Int -> Int64
+toInt64 = fromIntegral
+
+--
+-- ByteString.Lazy.Char8 <=> ByteString.Char8
+--
+
+prop_concatCC       = D.concat                `eq1`  C.concat
+prop_nullCC         = D.null                  `eq1`  C.null
+prop_reverseCC      = D.reverse               `eq1`  C.reverse
+prop_transposeCC    = D.transpose             `eq1`  C.transpose
+prop_groupCC        = D.group                 `eq1`  C.group
+prop_groupByCC      = D.groupBy               `eq2`  C.groupBy
+prop_initsCC        = D.inits                 `eq1`  C.inits
+prop_tailsCC        = D.tails                 `eq1`  C.tails
+prop_allCC          = D.all                   `eq2`  C.all
+prop_anyCC          = D.any                   `eq2`  C.any
+prop_appendCC       = D.append                `eq2`  C.append
+prop_breakCC        = D.break                 `eq2`  C.break
+prop_concatMapCC    = forAll (sized $ \n -> resize (min 50 n) arbitrary) $
+                      D.concatMap             `eq2`  C.concatMap
+prop_consCC         = D.cons                  `eq2`  C.cons
+prop_consCC'        = D.cons'                 `eq2`  C.cons
+prop_unconsCC       = D.uncons                `eq1`  C.uncons
+prop_unsnocCC       = D.unsnoc                `eq1`  C.unsnoc
+prop_countCC        = D.count                 `eq2`  ((toInt64 .) . C.count)
+prop_dropCC         = (D.drop . toInt64)      `eq2`  C.drop
+prop_dropWhileCC    = D.dropWhile             `eq2`  C.dropWhile
+prop_filterCC       = D.filter                `eq2`  C.filter
+prop_findCC         = D.find                  `eq2`  C.find
+prop_findIndexCC    = D.findIndex             `eq2`  ((fmap toInt64 .) . C.findIndex)
+prop_findIndexEndCC = D.findIndexEnd          `eq2`  ((fmap toInt64 .) . C.findIndexEnd)
+prop_findIndicesCC  = D.findIndices           `eq2`  ((fmap toInt64 .) . C.findIndices)
+prop_isPrefixOfCC   = D.isPrefixOf            `eq2`  C.isPrefixOf
+prop_stripPrefixCC  = D.stripPrefix           `eq2`  C.stripPrefix
+prop_isSuffixOfCC   = D.isSuffixOf            `eq2`  C.isSuffixOf
+prop_stripSuffixCC  = D.stripSuffix           `eq2`  C.stripSuffix
+prop_mapCC          = D.map                   `eq2`  C.map
+prop_replicateCC    = forAll arbitrarySizedIntegral $
+                      (D.replicate . toInt64) `eq2`  C.replicate
+prop_snocCC         = D.snoc                  `eq2`  C.snoc
+prop_spanCC         = D.span                  `eq2`  C.span
+prop_splitCC        = D.split                 `eq2`  C.split
+prop_splitAtCC      = (D.splitAt . toInt64)   `eq2`  C.splitAt
+prop_takeCC         = (D.take    . toInt64)   `eq2`  C.take
+prop_takeWhileCC    = D.takeWhile             `eq2`  C.takeWhile
+prop_elemCC         = D.elem                  `eq2`  C.elem
+prop_notElemCC      = D.notElem               `eq2`  C.notElem
+prop_elemIndexCC    = D.elemIndex             `eq2`  ((fmap toInt64 .) . C.elemIndex)
+prop_elemIndicesCC  = D.elemIndices           `eq2`  ((fmap toInt64 .) . C.elemIndices)
+prop_lengthCC       = D.length                `eq1`  (toInt64 . C.length)
+
+prop_headCC         = D.head        `eqnotnull1` C.head
+prop_initCC         = D.init        `eqnotnull1` C.init
+prop_lastCC         = D.last        `eqnotnull1` C.last
+prop_maximumCC      = D.maximum     `eqnotnull1` C.maximum
+prop_minimumCC      = D.minimum     `eqnotnull1` C.minimum
+prop_tailCC         = D.tail        `eqnotnull1` C.tail
+prop_foldl1CC       = D.foldl1      `eqnotnull2` C.foldl1
+prop_foldl1CC'      = D.foldl1'     `eqnotnull2` C.foldl1'
+prop_foldr1CC       = D.foldr1      `eqnotnull2` C.foldr1
+prop_foldr1CC'      = D.foldr1      `eqnotnull2` C.foldr1'
+prop_scanlCC        = D.scanl       `eqnotnull3` C.scanl
+
+prop_intersperseCC = D.intersperse  `eq2` C.intersperse
+
+prop_foldlCC     = eq3
+    (D.foldl     :: (X -> Char -> X) -> X -> B -> X)
+    (C.foldl     :: (X -> Char -> X) -> X -> P -> X)
+prop_foldlCC'    = eq3
+    (D.foldl'    :: (X -> Char -> X) -> X -> B -> X)
+    (C.foldl'    :: (X -> Char -> X) -> X -> P -> X)
+prop_foldrCC     = eq3
+    (D.foldr     :: (Char -> X -> X) -> X -> B -> X)
+    (C.foldr     :: (Char -> X -> X) -> X -> P -> X)
+prop_foldrCC'    = eq3
+    (D.foldr     :: (Char -> X -> X) -> X -> B -> X)
+    (C.foldr'    :: (Char -> X -> X) -> X -> P -> X)
+prop_mapAccumLCC = eq3
+    (D.mapAccumL :: (X -> Char -> (X,Char)) -> X -> B -> (X, B))
+    (C.mapAccumL :: (X -> Char -> (X,Char)) -> X -> P -> (X, P))
+
+--prop_mapIndexedCC = D.mapIndexed `eq2` C.mapIndexed
+--prop_mapIndexedPL = L.mapIndexed `eq2` P.mapIndexed
+
+--prop_mapAccumL_mapIndexedBP =
+--        P.mapIndexed `eq2`
+--        (\k p -> snd $ P.mapAccumL (\i w -> (i+1, k i w)) (0::Int) p)
+
+--
+-- ByteString.Lazy <=> ByteString
+--
+
+prop_concatBP       = forAll (sized $ \n -> resize (n `div` 2) arbitrary) $
+                      L.concat               `eq1`  P.concat
+prop_nullBP         = L.null                 `eq1`  P.null
+prop_reverseBP      = L.reverse              `eq1`  P.reverse
+
+prop_transposeBP    = L.transpose            `eq1`  P.transpose
+prop_groupBP        = L.group                `eq1`  P.group
+prop_groupByBP      = L.groupBy              `eq2`  P.groupBy
+prop_initsBP        = L.inits                `eq1`  P.inits
+prop_tailsBP        = L.tails                `eq1`  P.tails
+prop_allBP          = L.all                  `eq2`  P.all
+prop_anyBP          = L.any                  `eq2`  P.any
+prop_appendBP       = L.append               `eq2`  P.append
+prop_breakBP        = L.break                `eq2`  P.break
+prop_concatMapBP    = forAll (sized $ \n -> resize (n `div` 4) arbitrary) $
+                      L.concatMap            `eq2`  P.concatMap
+prop_consBP         = L.cons                 `eq2`  P.cons
+prop_consBP'        = L.cons'                `eq2`  P.cons
+prop_unconsBP       = L.uncons               `eq1`  P.uncons
+prop_unsnocBP       = L.unsnoc               `eq1`  P.unsnoc
+prop_countBP        = L.count                `eq2`  ((toInt64 .) . P.count)
+prop_dropBP         = (L.drop. toInt64)      `eq2`  P.drop
+prop_dropWhileBP    = L.dropWhile            `eq2`  P.dropWhile
+prop_filterBP       = L.filter               `eq2`  P.filter
+prop_findBP         = L.find                 `eq2`  P.find
+prop_findIndexBP    = L.findIndex            `eq2`  ((fmap toInt64 .) . P.findIndex)
+prop_findIndexEndBP = L.findIndexEnd         `eq2`  ((fmap toInt64 .) . P.findIndexEnd)
+prop_findIndicesBP  = L.findIndices          `eq2`  ((fmap toInt64 .) . P.findIndices)
+prop_isPrefixOfBP   = L.isPrefixOf           `eq2`  P.isPrefixOf
+prop_stripPrefixBP  = L.stripPrefix          `eq2`  P.stripPrefix
+prop_isSuffixOfBP   = L.isSuffixOf           `eq2`  P.isSuffixOf
+prop_stripSuffixBP  = L.stripSuffix          `eq2`  P.stripSuffix
+prop_mapBP          = L.map                  `eq2`  P.map
+prop_replicateBP    = forAll arbitrarySizedIntegral $
+                      (L.replicate. toInt64) `eq2`  P.replicate
+prop_snocBP         = L.snoc                 `eq2`  P.snoc
+prop_spanBP         = L.span                 `eq2`  P.span
+prop_splitBP        = L.split                `eq2`  P.split
+prop_splitAtBP      = (L.splitAt. toInt64)   `eq2`  P.splitAt
+prop_takeBP         = (L.take   . toInt64)   `eq2`  P.take
+prop_takeWhileBP    = L.takeWhile            `eq2`  P.takeWhile
+prop_elemBP         = L.elem                 `eq2`  P.elem
+prop_notElemBP      = L.notElem              `eq2`  P.notElem
+prop_elemIndexBP    = L.elemIndex            `eq2`  ((fmap toInt64 .) . P.elemIndex)
+prop_elemIndexEndBP = L.elemIndexEnd         `eq2`  ((fmap toInt64 .) . P.elemIndexEnd)
+prop_elemIndicesBP  = L.elemIndices          `eq2`  ((fmap toInt64 .) . P.elemIndices)
+prop_intersperseBP  = L.intersperse          `eq2`  P.intersperse
+prop_lengthBP       = L.length               `eq1`  (toInt64 . P.length)
+prop_readIntBP      = D.readInt              `eq1`  C.readInt
+prop_linesBP        = D.lines                `eq1`  C.lines
+
+-- double check:
+-- Currently there's a bug in the lazy bytestring version of lines, this
+-- catches it:
+prop_linesNLBP      = eq1 D.lines C.lines x
+    where x = D.pack "one\ntwo\n\n\nfive\n\nseven\n"
+
+prop_headBP         = L.head        `eqnotnull1` P.head
+prop_initBP         = L.init        `eqnotnull1` P.init
+prop_lastBP         = L.last        `eqnotnull1` P.last
+prop_maximumBP      = L.maximum     `eqnotnull1` P.maximum
+prop_minimumBP      = L.minimum     `eqnotnull1` P.minimum
+prop_tailBP         = L.tail        `eqnotnull1` P.tail
+prop_foldl1BP       = L.foldl1      `eqnotnull2` P.foldl1
+prop_foldl1BP'      = L.foldl1'     `eqnotnull2` P.foldl1'
+prop_foldr1BP       = L.foldr1      `eqnotnull2` P.foldr1
+prop_foldr1BP'      = L.foldr1      `eqnotnull2` P.foldr1'
+prop_scanlBP        = L.scanl       `eqnotnull3` P.scanl
+
+
+prop_eqBP        = eq2
+    ((==) :: B -> B -> Bool)
+    ((==) :: P -> P -> Bool)
+prop_compareBP   = eq2
+    ((compare) :: B -> B -> Ordering)
+    ((compare) :: P -> P -> Ordering)
+prop_foldlBP     = eq3
+    (L.foldl     :: (X -> W -> X) -> X -> B -> X)
+    (P.foldl     :: (X -> W -> X) -> X -> P -> X)
+prop_foldlBP'    = eq3
+    (L.foldl'    :: (X -> W -> X) -> X -> B -> X)
+    (P.foldl'    :: (X -> W -> X) -> X -> P -> X)
+prop_foldrBP     = eq3
+    (L.foldr     :: (W -> X -> X) -> X -> B -> X)
+    (P.foldr     :: (W -> X -> X) -> X -> P -> X)
+prop_foldrBP'    = eq3
+    (L.foldr     :: (W -> X -> X) -> X -> B -> X)
+    (P.foldr'    :: (W -> X -> X) -> X -> P -> X)
+prop_mapAccumLBP = eq3
+    (L.mapAccumL :: (X -> W -> (X,W)) -> X -> B -> (X, B))
+    (P.mapAccumL :: (X -> W -> (X,W)) -> X -> P -> (X, P))
+
+prop_unfoldrBP   =
+  forAll arbitrarySizedIntegral $
+  eq3
+    ((\n f a -> L.take (fromIntegral n) $
+        L.unfoldr    f a) :: Int -> (X -> Maybe (W,X)) -> X -> B)
+    ((\n f a ->                     fst $
+        P.unfoldrN n f a) :: Int -> (X -> Maybe (W,X)) -> X -> P)
+
+prop_unfoldr2BP   =
+  forAll arbitrarySizedIntegral $ \n ->
+  forAll arbitrarySizedIntegral $ \a ->
+  eq2
+    ((\n a -> P.take (n*100) $
+        P.unfoldr    (\x -> if x <= (n*100) then Just (fromIntegral x, x + 1) else Nothing) a)
+                :: Int -> Int -> P)
+    ((\n a ->                     fst $
+        P.unfoldrN (n*100) (\x -> if x <= (n*100) then Just (fromIntegral x, x + 1) else Nothing) a)
+                :: Int -> Int -> P)
+    n a
+
+prop_unfoldr2CP   =
+  forAll arbitrarySizedIntegral $ \n ->
+  forAll arbitrarySizedIntegral $ \a ->
+  eq2
+    ((\n a -> C.take (n*100) $
+        C.unfoldr    (\x -> if x <= (n*100) then Just (chr (x `mod` 256), x + 1) else Nothing) a)
+                :: Int -> Int -> P)
+    ((\n a ->                     fst $
+        C.unfoldrN (n*100) (\x -> if x <= (n*100) then Just (chr (x `mod` 256), x + 1) else Nothing) a)
+                :: Int -> Int -> P)
+    n a
+
+
+prop_unfoldrLC   =
+  forAll arbitrarySizedIntegral $
+  eq3
+    ((\n f a -> LC.take (fromIntegral n) $
+        LC.unfoldr    f a) :: Int -> (X -> Maybe (Char,X)) -> X -> B)
+    ((\n f a ->                     fst $
+        C.unfoldrN n f a) :: Int -> (X -> Maybe (Char,X)) -> X -> P)
+
+prop_cycleLC  a   =
+  not (LC.null a) ==>
+  forAll arbitrarySizedIntegral $
+  eq1
+    ((\n   -> LC.take (fromIntegral n) $
+              LC.cycle a
+     ) :: Int -> B)
+
+    ((\n   -> LC.take (fromIntegral (n::Int)) . LC.concat $
+              unfoldr (\x ->  Just (x,x) ) a
+     ) :: Int -> B)
+
+
+prop_iterateLC :: Int -> (Char8 -> Char8) -> Char8 -> Bool
+prop_iterateLC n f (Char8 c) =
+  eq3
+    (\n f a -> LC.take (fromIntegral n) $ LC.iterate  f a)
+    (\n f a -> fst $ C.unfoldrN n (\a -> Just (f a, f a)) a)
+    n
+    (castFn f :: Char -> Char)
+    c
+
+prop_iterateLC_2 :: Int -> (Char8 -> Char8) -> Char8 -> Bool
+prop_iterateLC_2 n f (Char8 c) =
+  eq3
+    (\n f a -> LC.take (fromIntegral (n :: Int)) $ LC.iterate f a)
+    (\n f a -> LC.take (fromIntegral (n :: Int)) $ LC.unfoldr (\a -> Just (f a, f a)) a)
+    n
+    (castFn f :: Char -> Char)
+    c
+
+prop_iterateL   =
+  forAll arbitrarySizedIntegral $
+  eq3
+    ((\n f a -> L.take (fromIntegral n) $
+        L.iterate  f a) :: Int -> (W -> W) -> W -> B)
+    ((\n f a -> fst $
+        P.unfoldrN n (\a -> Just (f a, f a)) a) :: Int -> (W -> W) -> W -> P)
+
+prop_repeatLC   =
+  forAll arbitrarySizedIntegral $
+  eq2
+    ((\n a -> LC.take (fromIntegral n) $
+        LC.repeat a) :: Int -> Char -> B)
+    ((\n a -> fst $
+        C.unfoldrN n (\a -> Just (a, a)) a) :: Int -> Char -> P)
+
+prop_repeatL   =
+  forAll arbitrarySizedIntegral $
+  eq2
+    ((\n a -> L.take (fromIntegral n) $
+        L.repeat a) :: Int -> W -> B)
+    ((\n a -> fst $
+        P.unfoldrN n (\a -> Just (a, a)) a) :: Int -> W -> P)
+
+--
+-- properties comparing ByteString.Lazy `eq1` List
+--
+
+prop_concatBL       = forAll (sized $ \n -> resize (n `div` 2) arbitrary) $
+                      L.concat                `eq1` (concat    :: [[W]] -> [W])
+prop_lengthBL       = L.length                `eq1` (toInt64 . length    :: [W] -> Int64)
+prop_nullBL         = L.null                  `eq1` (null      :: [W] -> Bool)
+prop_reverseBL      = L.reverse               `eq1` (reverse   :: [W] -> [W])
+prop_transposeBL    = L.transpose             `eq1` (transpose :: [[W]] -> [[W]])
+prop_groupBL        = L.group                 `eq1` (group     :: [W] -> [[W]])
+prop_groupByBL      = L.groupBy               `eq2` (groupBy   :: (W -> W -> Bool) -> [W] -> [[W]])
+prop_initsBL        = L.inits                 `eq1` (inits     :: [W] -> [[W]])
+prop_tailsBL        = L.tails                 `eq1` (tails     :: [W] -> [[W]])
+prop_allBL          = L.all                   `eq2` (all       :: (W -> Bool) -> [W] -> Bool)
+prop_anyBL          = L.any                   `eq2` (any       :: (W -> Bool) -> [W] -> Bool)
+prop_appendBL       = L.append                `eq2` ((++)      :: [W] -> [W] -> [W])
+prop_breakBL        = L.break                 `eq2` (break     :: (W -> Bool) -> [W] -> ([W],[W]))
+prop_concatMapBL    = forAll (sized $ \n -> resize (n `div` 2) arbitrary) $
+                      L.concatMap             `eq2` (concatMap :: (W -> [W]) -> [W] -> [W])
+prop_consBL         = L.cons                  `eq2` ((:)       :: W -> [W] -> [W])
+prop_dropBL         = (L.drop . toInt64)      `eq2` (drop      :: Int -> [W] -> [W])
+prop_dropWhileBL    = L.dropWhile             `eq2` (dropWhile :: (W -> Bool) -> [W] -> [W])
+prop_filterBL       = L.filter                `eq2` (filter    :: (W -> Bool ) -> [W] -> [W])
+prop_findBL         = L.find                  `eq2` (find      :: (W -> Bool) -> [W] -> Maybe W)
+prop_findIndicesBL  = L.findIndices           `eq2` ((fmap toInt64 .) . findIndices:: (W -> Bool) -> [W] -> [Int64])
+prop_findIndexBL    = L.findIndex             `eq2` ((fmap toInt64 .) . findIndex :: (W -> Bool) -> [W] -> Maybe Int64)
+prop_findIndexEndBL = L.findIndexEnd          `eq2` ((fmap toInt64 .) . findIndexEnd :: (W -> Bool) -> [W] -> Maybe Int64)
+prop_isPrefixOfBL   = L.isPrefixOf            `eq2` (isPrefixOf:: [W] -> [W] -> Bool)
+prop_stripPrefixBL  = L.stripPrefix           `eq2` (stripPrefix:: [W] -> [W] -> Maybe [W])
+prop_isSuffixOfBL   = L.isSuffixOf            `eq2` (isSuffixOf:: [W] -> [W] -> Bool)
+prop_stripSuffixBL  = L.stripSuffix           `eq2` (stripSuffix :: [W] -> [W] -> Maybe [W])
+prop_mapBL          = L.map                   `eq2` (map       :: (W -> W) -> [W] -> [W])
+prop_replicateBL    = forAll arbitrarySizedIntegral $
+                      (L.replicate . toInt64) `eq2` (replicate :: Int -> W -> [W])
+prop_snocBL         = L.snoc                  `eq2` ((\xs x -> xs ++ [x]) :: [W] -> W -> [W])
+prop_spanBL         = L.span                  `eq2` (span      :: (W -> Bool) -> [W] -> ([W],[W]))
+prop_splitAtBL      = (L.splitAt . toInt64)   `eq2` (splitAt :: Int -> [W] -> ([W],[W]))
+prop_takeBL         = (L.take    . toInt64)   `eq2` (take    :: Int -> [W] -> [W])
+prop_takeWhileBL    = L.takeWhile             `eq2` (takeWhile :: (W -> Bool) -> [W] -> [W])
+prop_elemBL         = L.elem                  `eq2` (elem      :: W -> [W] -> Bool)
+prop_notElemBL      = L.notElem               `eq2` (notElem   :: W -> [W] -> Bool)
+prop_elemIndexBL    = L.elemIndex             `eq2` ((fmap toInt64 .) . elemIndex   :: W -> [W] -> Maybe Int64)
+prop_elemIndexEndBL = L.elemIndexEnd          `eq2` ((fmap toInt64 .) . elemIndexEnd:: W -> [W] -> Maybe Int64)
+prop_elemIndicesBL  = L.elemIndices           `eq2` ((fmap toInt64 .) . elemIndices :: W -> [W] -> [Int64])
+prop_linesBL        = D.lines                 `eq1` (lines     :: String -> [String])
+
+prop_foldl1BL       = L.foldl1  `eqnotnull2` (foldl1    :: (W -> W -> W) -> [W] -> W)
+prop_foldl1BL'      = L.foldl1' `eqnotnull2` (foldl1'   :: (W -> W -> W) -> [W] -> W)
+prop_foldr1BL       = L.foldr1  `eqnotnull2` (foldr1    :: (W -> W -> W) -> [W] -> W)
+prop_headBL         = L.head    `eqnotnull1` (head      :: [W] -> W)
+prop_initBL         = L.init    `eqnotnull1` (init      :: [W] -> [W])
+prop_lastBL         = L.last    `eqnotnull1` (last      :: [W] -> W)
+prop_maximumBL      = L.maximum `eqnotnull1` (maximum   :: [W] -> W)
+prop_minimumBL      = L.minimum `eqnotnull1` (minimum   :: [W] -> W)
+prop_tailBL         = L.tail    `eqnotnull1` (tail      :: [W] -> [W])
+
+prop_eqBL         = eq2
+    ((==) :: B   -> B   -> Bool)
+    ((==) :: [W] -> [W] -> Bool)
+prop_compareBL    = eq2
+    ((compare) :: B   -> B   -> Ordering)
+    ((compare) :: [W] -> [W] -> Ordering)
+prop_foldlBL      = eq3
+    (L.foldl  :: (X -> W -> X) -> X -> B   -> X)
+    (  foldl  :: (X -> W -> X) -> X -> [W] -> X)
+prop_foldlBL'     = eq3
+    (L.foldl' :: (X -> W -> X) -> X -> B   -> X)
+    (  foldl' :: (X -> W -> X) -> X -> [W] -> X)
+prop_foldrBL      = eq3
+    (L.foldr  :: (W -> X -> X) -> X -> B   -> X)
+    (  foldr  :: (W -> X -> X) -> X -> [W] -> X)
+prop_mapAccumLBL  = eq3
+    (L.mapAccumL :: (X -> W -> (X,W)) -> X -> B   -> (X, B))
+    (  mapAccumL :: (X -> W -> (X,W)) -> X -> [W] -> (X, [W]))
+
+prop_mapAccumRBL  = eq3
+    (L.mapAccumR :: (X -> W -> (X,W)) -> X -> B   -> (X, B))
+    (  mapAccumR :: (X -> W -> (X,W)) -> X -> [W] -> (X, [W]))
+
+prop_mapAccumRDL :: (X -> Char8 -> (X, Char8)) -> X -> B -> Bool
+prop_mapAccumRDL f = eq3
+    (D.mapAccumR :: (X -> Char -> (X,Char)) -> X -> B   -> (X, B))
+    (  mapAccumR :: (X -> Char -> (X,Char)) -> X -> [Char] -> (X, [Char]))
+    (castFn f)
+
+prop_mapAccumRCC :: (X -> Char8 -> (X, Char8)) -> X -> P -> Bool
+prop_mapAccumRCC f = eq3
+    (C.mapAccumR :: (X -> Char -> (X,Char)) -> X -> P   -> (X, P))
+    (  mapAccumR :: (X -> Char -> (X,Char)) -> X -> [Char] -> (X, [Char]))
+    (castFn f)
+
+prop_unfoldrBL =
+  forAll arbitrarySizedIntegral $
+  eq3
+    ((\n f a -> L.take (fromIntegral n) $
+        L.unfoldr f a) :: Int -> (X -> Maybe (W,X)) -> X -> B)
+    ((\n f a ->                  take n $
+          unfoldr f a) :: Int -> (X -> Maybe (W,X)) -> X -> [W])
+
+prop_packZipWithBL   = L.packZipWith `eq3` (zipWith :: (W -> W -> W) -> [W] -> [W] -> [W])
+
+--
+-- And finally, check correspondance between Data.ByteString and List
+--
+
+prop_lengthPL     = (fromIntegral.P.length :: P -> Int) `eq1` (length :: [W] -> Int)
+prop_nullPL       = P.null      `eq1` (null      :: [W] -> Bool)
+prop_reversePL    = P.reverse   `eq1` (reverse   :: [W] -> [W])
+prop_transposePL  = P.transpose `eq1` (transpose :: [[W]] -> [[W]])
+prop_groupPL      = P.group     `eq1` (group     :: [W] -> [[W]])
+prop_groupByPL    = P.groupBy   `eq2` (groupBy   :: (W -> W -> Bool) -> [W] -> [[W]])
+prop_initsPL      = P.inits     `eq1` (inits     :: [W] -> [[W]])
+prop_tailsPL      = P.tails     `eq1` (tails     :: [W] -> [[W]])
+prop_concatPL     = forAll (sized $ \n -> resize (n `div` 2) arbitrary) $
+                    P.concat    `eq1` (concat    :: [[W]] -> [W])
+prop_allPL        = P.all       `eq2` (all       :: (W -> Bool) -> [W] -> Bool)
+prop_anyPL        = P.any       `eq2`    (any       :: (W -> Bool) -> [W] -> Bool)
+prop_appendPL     = P.append    `eq2`    ((++)      :: [W] -> [W] -> [W])
+prop_breakPL      = P.break     `eq2`    (break     :: (W -> Bool) -> [W] -> ([W],[W]))
+prop_concatMapPL  = forAll (sized $ \n -> resize (n `div` 2) arbitrary) $
+                    P.concatMap `eq2`    (concatMap :: (W -> [W]) -> [W] -> [W])
+prop_consPL       = P.cons      `eq2`    ((:)       :: W -> [W] -> [W])
+prop_dropPL       = P.drop      `eq2`    (drop      :: Int -> [W] -> [W])
+prop_dropWhilePL  = P.dropWhile `eq2`    (dropWhile :: (W -> Bool) -> [W] -> [W])
+prop_filterPL     = P.filter    `eq2`    (filter    :: (W -> Bool ) -> [W] -> [W])
+prop_filterPL_rule= (\x -> P.filter ((==) x))  `eq2` -- test rules
+                    ((\x -> filter ((==) x)) :: W -> [W] -> [W])
+
+-- under lambda doesn't fire?
+prop_filterLC_rule= (f)  `eq2` -- test rules
+                    ((\x -> filter ((==) x)) :: Char -> [Char] -> [Char])
+    where
+         f x s = LC.filter ((==) x) s
+
+prop_partitionPL  = P.partition `eq2`    (partition :: (W -> Bool ) -> [W] -> ([W],[W]))
+prop_partitionLL  = L.partition `eq2`    (partition :: (W -> Bool ) -> [W] -> ([W],[W]))
+prop_findPL       = P.find      `eq2`    (find      :: (W -> Bool) -> [W] -> Maybe W)
+prop_findIndexPL  = P.findIndex `eq2`    (findIndex :: (W -> Bool) -> [W] -> Maybe Int)
+prop_findIndexEndPL = P.findIndexEnd `eq2` (findIndexEnd :: (W -> Bool) -> [W] -> Maybe Int)
+prop_isPrefixOfPL = P.isPrefixOf`eq2`    (isPrefixOf:: [W] -> [W] -> Bool)
+prop_isSuffixOfPL = P.isSuffixOf`eq2`    (isSuffixOf:: [W] -> [W] -> Bool)
+prop_isInfixOfPL  = P.isInfixOf `eq2`    (isInfixOf:: [W] -> [W] -> Bool)
+prop_stripPrefixPL = P.stripPrefix`eq2`  (stripPrefix:: [W] -> [W] -> Maybe [W])
+prop_stripSuffixPL = P.stripSuffix`eq2`  (stripSuffix:: [W] -> [W] -> Maybe [W])
+prop_mapPL        = P.map       `eq2`    (map       :: (W -> W) -> [W] -> [W])
+prop_replicatePL  = forAll arbitrarySizedIntegral $
+                    P.replicate `eq2`    (replicate :: Int -> W -> [W])
+prop_snocPL       = P.snoc      `eq2`    ((\xs x -> xs ++ [x]) :: [W] -> W -> [W])
+prop_spanPL       = P.span      `eq2`    (span      :: (W -> Bool) -> [W] -> ([W],[W]))
+prop_splitAtPL    = P.splitAt   `eq2`    (splitAt   :: Int -> [W] -> ([W],[W]))
+prop_takePL       = P.take      `eq2`    (take      :: Int -> [W] -> [W])
+prop_takeWhilePL  = P.takeWhile `eq2`    (takeWhile :: (W -> Bool) -> [W] -> [W])
+prop_elemPL       = P.elem      `eq2`    (elem      :: W -> [W] -> Bool)
+prop_notElemPL    = P.notElem   `eq2`    (notElem   :: W -> [W] -> Bool)
+prop_elemIndexPL  = P.elemIndex `eq2`    (elemIndex :: W -> [W] -> Maybe Int)
+prop_linesPL      = C.lines     `eq1`    (lines     :: String -> [String])
+prop_findIndicesPL= P.findIndices`eq2`   (findIndices:: (W -> Bool) -> [W] -> [Int])
+prop_elemIndicesPL= P.elemIndices`eq2`   (elemIndices:: W -> [W] -> [Int])
+prop_zipPL        = P.zip        `eq2`   (zip :: [W] -> [W] -> [(W,W)])
+prop_zipCL        = C.zip        `eq2`   (zip :: [Char] -> [Char] -> [(Char,Char)])
+prop_zipLL        = L.zip        `eq2`   (zip :: [W] -> [W] -> [(W,W)])
+prop_unzipPL      = P.unzip      `eq1`   (unzip :: [(W,W)] -> ([W],[W]))
+prop_unzipLL      = L.unzip      `eq1`   (unzip :: [(W,W)] -> ([W],[W]))
+
+prop_unzipCL :: [(Char8, Char8)] -> Bool
+prop_unzipCL xs   = (C.unzip     `eq1`   (unzip :: [(Char,Char)] -> ([Char],[Char])))
+                    [ (a,b) | (Char8 a, Char8 b) <- xs ]
+
+prop_unzipDL :: [(Char8, Char8)] -> Bool
+prop_unzipDL xs   = (D.unzip     `eq1`   (unzip :: [(Char,Char)] -> ([Char],[Char])))
+                    [ (a,b) | (Char8 a, Char8 b) <- xs ]
+
+prop_foldl1PL     = P.foldl1    `eqnotnull2` (foldl1   :: (W -> W -> W) -> [W] -> W)
+prop_foldl1PL'    = P.foldl1'   `eqnotnull2` (foldl1' :: (W -> W -> W) -> [W] -> W)
+prop_foldr1PL     = P.foldr1    `eqnotnull2` (foldr1 :: (W -> W -> W) -> [W] -> W)
+prop_scanlPL      = P.scanl     `eqnotnull3` (scanl  :: (W -> W -> W) -> W -> [W] -> [W])
+prop_scanl1PL     = P.scanl1    `eqnotnull2` (scanl1 :: (W -> W -> W) -> [W] -> [W])
+prop_scanrPL      = P.scanr     `eqnotnull3` (scanr  :: (W -> W -> W) -> W -> [W] -> [W])
+prop_scanr1PL     = P.scanr1    `eqnotnull2` (scanr1 :: (W -> W -> W) -> [W] -> [W])
+prop_headPL       = P.head      `eqnotnull1` (head      :: [W] -> W)
+prop_initPL       = P.init      `eqnotnull1` (init      :: [W] -> [W])
+prop_lastPL       = P.last      `eqnotnull1` (last      :: [W] -> W)
+prop_maximumPL    = P.maximum   `eqnotnull1` (maximum   :: [W] -> W)
+prop_minimumPL    = P.minimum   `eqnotnull1` (minimum   :: [W] -> W)
+prop_tailPL       = P.tail      `eqnotnull1` (tail      :: [W] -> [W])
+
+prop_scanl1CL :: (Char8 -> Char8 -> Char8) -> P -> Property
+prop_scanrCL  :: (Char8 -> Char8 -> Char8) -> Char8 -> P -> Property
+prop_scanr1CL :: (Char8 -> Char8 -> Char8) -> P -> Property
+
+prop_scanl1CL f = eqnotnull2
+    C.scanl1
+    (scanl1 :: (Char -> Char -> Char) -> [Char] -> [Char])
+    (castFn f)
+
+prop_scanrCL f (Char8 c) = eqnotnull3
+    C.scanr
+    (scanr  :: (Char -> Char -> Char) -> Char -> [Char] -> [Char])
+    (castFn f)
+    c
+
+prop_scanr1CL f = eqnotnull2
+    C.scanr1
+    (scanr1 :: (Char -> Char -> Char) -> [Char] -> [Char])
+    (castFn f)
+
+prop_packZipWithPL   = P.packZipWith  `eq3` (zipWith :: (W -> W -> W) -> [W] -> [W] -> [W])
+
+prop_zipWithPL    = (P.zipWith  :: (W -> W -> X) -> P   -> P   -> [X]) `eq3`
+                      (zipWith  :: (W -> W -> X) -> [W] -> [W] -> [X])
+
+prop_zipWithPL_rules   = (P.zipWith  :: (W -> W -> W) -> P -> P -> [W]) `eq3`
+                         (zipWith    :: (W -> W -> W) -> [W] -> [W] -> [W])
+
+prop_eqPL      = eq2
+    ((==) :: P   -> P   -> Bool)
+    ((==) :: [W] -> [W] -> Bool)
+prop_comparePL = eq2
+    ((compare) :: P   -> P   -> Ordering)
+    ((compare) :: [W] -> [W] -> Ordering)
+prop_foldlPL   = eq3
+    (P.foldl  :: (X -> W -> X) -> X -> P        -> X)
+    (  foldl  :: (X -> W -> X) -> X -> [W]      -> X)
+prop_foldlPL'  = eq3
+    (P.foldl' :: (X -> W -> X) -> X -> P        -> X)
+    (  foldl' :: (X -> W -> X) -> X -> [W]      -> X)
+prop_foldrPL   = eq3
+    (P.foldr  :: (W -> X -> X) -> X -> P        -> X)
+    (  foldr  :: (W -> X -> X) -> X -> [W]      -> X)
+prop_mapAccumLPL= eq3
+    (P.mapAccumL :: (X -> W -> (X,W)) -> X -> P -> (X, P))
+    (  mapAccumL :: (X -> W -> (X,W)) -> X -> [W] -> (X, [W]))
+prop_mapAccumRPL= eq3
+    (P.mapAccumR :: (X -> W -> (X,W)) -> X -> P -> (X, P))
+    (  mapAccumR :: (X -> W -> (X,W)) -> X -> [W] -> (X, [W]))
+prop_unfoldrPL =
+  forAll arbitrarySizedIntegral $
+  eq3
+    ((\n f a ->      fst $
+        P.unfoldrN n f a) :: Int -> (X -> Maybe (W,X)) -> X -> P)
+    ((\n f a ->   take n $
+          unfoldr    f a) :: Int -> (X -> Maybe (W,X)) -> X -> [W])
+
+------------------------------------------------------------------------
+--
+-- These are miscellaneous tests left over. Or else they test some
+-- property internal to a type (i.e. head . sort == minimum), without
+-- reference to a model type.
+--
+
+invariant :: L.ByteString -> Bool
+invariant Empty       = True
+invariant (Chunk c cs) = not (P.null c) && invariant cs
+
+prop_invariant = invariant
+
+prop_eq_refl  x     = x        == (x :: ByteString)
+prop_eq_symm  x y   = (x == y) == (y == (x :: ByteString))
+
+prop_eq1 xs      = xs == (unpack . pack $ xs)
+prop_eq2 xs      = xs == (xs :: ByteString)
+prop_eq3 xs ys   = (xs == ys) == (unpack xs == unpack ys)
+
+prop_compare1 xs   = (pack xs        `compare` pack xs) == EQ
+prop_compare2 xs c = (pack (xs++[c]) `compare` pack xs) == GT
+prop_compare3 xs c = (pack xs `compare` pack (xs++[c])) == LT
+
+prop_compare4 xs    = (not (null xs)) ==> (pack xs  `compare` L.empty) == GT
+prop_compare5 xs    = (not (null xs)) ==> (L.empty `compare` pack xs) == LT
+prop_compare6 xs ys = (not (null ys)) ==> (pack (xs++ys)  `compare` pack xs) == GT
+
+prop_compare7 x  y  = x  `compare` y  == (L.singleton x `compare` L.singleton y)
+prop_compare8 xs ys = xs `compare` ys == (L.pack xs `compare` L.pack ys)
+prop_compare9       = (L.singleton 255 `compare` L.singleton 127) == GT
+
+prop_compare7LL (Char8 x) (Char8 y) =
+                      x  `compare` y  == (LC.singleton x `compare` LC.singleton y)
+
+prop_empty1 = L.length L.empty == 0
+prop_empty2 = L.unpack L.empty == []
+
+prop_packunpack s = (L.unpack . L.pack) s == id s
+prop_unpackpack s = (L.pack . L.unpack) s == id s
+
+prop_null xs = null (L.unpack xs) == L.null xs
+
+prop_length1 xs = fromIntegral (length xs) == L.length (L.pack xs)
+
+prop_length2 xs = L.length xs == length1 xs
+  where length1 ys
+            | L.null ys = 0
+            | otherwise = 1 + length1 (L.tail ys)
+
+prop_cons1 c xs = unpack (L.cons c (pack xs)) == (c:xs)
+prop_cons2 c    = L.singleton c == (c `L.cons` L.empty)
+prop_cons3 c    = unpack (L.singleton c) == (c:[])
+prop_cons4 c    = (c `L.cons` L.empty)  == pack (c:[])
+
+prop_snoc1 xs c = xs ++ [c] == unpack ((pack xs) `L.snoc` c)
+
+prop_head  xs = (not (null xs)) ==> head xs == (L.head . pack) xs
+prop_head1 xs = not (L.null xs) ==> L.head xs == head (L.unpack xs)
+
+prop_tail xs  = not (L.null xs) ==> L.tail xs == pack (tail (unpack xs))
+prop_tail1 xs = (not (null xs)) ==> tail xs   == (unpack . L.tail . pack) xs
+
+prop_last xs  = (not (null xs)) ==> last xs    == (L.last . pack) xs
+
+prop_init xs  =
+    (not (null xs)) ==>
+    init xs   == (unpack . L.init . pack) xs
+
+prop_append1 xs    = (xs ++ xs) == (unpack $ pack xs `L.append` pack xs)
+prop_append2 xs ys = (xs ++ ys) == (unpack $ pack xs `L.append` pack ys)
+prop_append3 xs ys = L.append xs ys == pack (unpack xs ++ unpack ys)
+prop_appendLazy xs = L.head (L.pack [xs] `L.append` error "Tail should be lazy") == xs
+
+prop_map1 f xs   = L.map f (pack xs)    == pack (map f xs)
+prop_map2 f g xs = L.map f (L.map g xs) == L.map (f . g) xs
+prop_map3 f xs   = map f xs == (unpack . L.map f .  pack) xs
+
+prop_filter1 c xs = (filter (/=c) xs) == (unpack $ L.filter (/=c) (pack xs))
+prop_filter2 p xs = (filter p xs) == (unpack $ L.filter p (pack xs))
+
+prop_reverse  xs = reverse xs          == (unpack . L.reverse . pack) xs
+prop_reverse1 xs = L.reverse (pack xs) == pack (reverse xs)
+prop_reverse2 xs = reverse (unpack xs) == (unpack . L.reverse) xs
+
+prop_transpose xs = (transpose xs) == ((map unpack) . L.transpose . (map pack)) xs
+
+prop_foldl f c xs = L.foldl f c (pack xs) == foldl f c xs
+    where _ = c :: Char
+
+prop_foldr f c xs = L.foldl f c (pack xs) == foldl f c xs
+    where _ = c :: Char
+
+prop_foldl_1 xs = L.foldl (\xs c -> c `L.cons` xs) L.empty xs == L.reverse xs
+prop_foldr_1 xs = L.foldr (\c xs -> c `L.cons` xs) L.empty xs == id xs
+
+prop_foldl1_1 xs =
+    (not . L.null) xs ==>
+    L.foldl1 (\x c -> if c > x then c else x)   xs ==
+    L.foldl  (\x c -> if c > x then c else x) 0 xs
+
+prop_foldl1_2 xs =
+    (not . L.null) xs ==>
+    L.foldl1 const xs == L.head xs
+
+prop_foldl1_3 xs =
+    (not . L.null) xs ==>
+    L.foldl1 (flip const) xs == L.last xs
+
+prop_foldr1_1 xs =
+    (not . L.null) xs ==>
+    L.foldr1 (\c x -> if c > x then c else x)   xs ==
+    L.foldr  (\c x -> if c > x then c else x) 0 xs
+
+prop_foldr1_2 xs =
+    (not . L.null) xs ==>
+    L.foldr1 (flip const) xs == L.last xs
+
+prop_foldr1_3 xs =
+    (not . L.null) xs ==>
+    L.foldr1 const xs == L.head xs
+
+prop_concat1 xs = (concat [xs,xs]) == (unpack $ L.concat [pack xs, pack xs])
+prop_concat2 xs = (concat [xs,[]]) == (unpack $ L.concat [pack xs, pack []])
+prop_concat3    = forAll (sized $ \n -> resize (n `div` 2) arbitrary) $ \xss ->
+                  L.concat (map pack xss) == pack (concat xss)
+
+prop_concatMap xs = L.concatMap L.singleton xs == (pack . concatMap (:[]) . unpack) xs
+
+prop_any xs a = (any (== a) xs) == (L.any (== a) (pack xs))
+prop_all xs a = (all (== a) xs) == (L.all (== a) (pack xs))
+
+prop_maximum xs = (not (null xs)) ==> (maximum xs) == (L.maximum ( pack xs ))
+prop_minimum xs = (not (null xs)) ==> (minimum xs) == (L.minimum ( pack xs ))
+
+prop_compareLength1 xs  =  (L.pack xs         `L.compareLength` fromIntegral (length xs)) == EQ
+prop_compareLength2 xs c = (L.pack (xs ++ [c]) `L.compareLength` fromIntegral (length xs)) == GT
+prop_compareLength3 xs c = (L.pack xs `L.compareLength` fromIntegral (length (xs ++ [c]))) == LT
+prop_compareLength4 xs c = ((L.pack xs `L.append` L.pack [c] `L.append` L.pack [undefined])
+                            `L.compareLength` fromIntegral (length xs)) == GT
+prop_compareLength5 xs l = L.compareLength xs l == compare (L.length xs) l
+
+prop_replicate1 c =
+    forAll arbitrary $ \(Positive n) ->
+    unpack (L.replicate (fromIntegral n) c) == replicate n c
+
+prop_replicate2 c = unpack (L.replicate 0 c) == replicate 0 c
+
+prop_take1 i xs = L.take (fromIntegral i) (pack xs) == pack (take i xs)
+prop_takeEnd i xs = P.takeEnd i xs == P.drop (P.length xs - i) xs
+
+prop_drop1 i xs = L.drop (fromIntegral i) (pack xs) == pack (drop i xs)
+prop_dropEnd i xs = P.dropEnd i xs == P.take (P.length xs - i) xs
+
+prop_splitAt i xs = --collect (i >= 0 && i < length xs) $
+    L.splitAt (fromIntegral i) (pack xs) == let (a,b) = splitAt i xs in (pack a, pack b)
+
+prop_takeWhile f xs = L.takeWhile f (pack xs) == pack (takeWhile f xs)
+prop_dropWhile f xs = L.dropWhile f (pack xs) == pack (dropWhile f xs)
+prop_takeWhileEnd f = P.takeWhileEnd f `eq1` (P.reverse . P.takeWhile f . P.reverse)
+prop_dropWhileEnd f = P.dropWhileEnd f `eq1` (P.reverse . P.dropWhile f . P.reverse)
+
+prop_break f xs = L.break f (pack xs) ==
+    let (a,b) = break f xs in (pack a, pack b)
+
+prop_breakspan xs c = L.break (==c) xs == L.span (/=c) xs
+
+prop_span xs a = (span (/=a) xs) == (let (x,y) = L.span (/=a) (pack xs) in (unpack x, unpack y))
+
+prop_split c xs = (map L.unpack . map checkInvariant . L.split c $ xs)
+               == (map P.unpack . P.split c . P.pack . L.unpack $ xs)
+
+prop_splitWith_empty f = L.splitWith f mempty == []
+
+prop_splitWith f xs = (l1 == l2 || l1 == l2+1) &&
+        sum (map L.length splits) == L.length xs - l2
+  where splits = L.splitWith f xs
+        l1 = fromIntegral (length splits)
+        l2 = L.length (L.filter f xs)
+
+prop_splitWith_D_empty f = D.splitWith f mempty == []
+
+prop_splitWith_D f xs = (l1 == l2 || l1 == l2+1) &&
+        sum (map D.length splits) == D.length xs - l2
+  where splits = D.splitWith f xs
+        l1 = fromIntegral (length splits)
+        l2 = D.length (D.filter f xs)
+
+prop_splitWith_C_empty f = C.splitWith f mempty == []
+
+prop_splitWith_C f xs = (l1 == l2 || l1 == l2+1) &&
+        sum (map C.length splits) == C.length xs - l2
+  where splits = C.splitWith f xs
+        l1 = fromIntegral (length splits)
+        l2 = C.length (C.filter f xs)
+
+prop_split_empty c = L.split c mempty == []
+
+prop_joinsplit c xs = L.intercalate (pack [c]) (L.split c xs) == id xs
+
+prop_group xs       = group xs == (map unpack . L.group . pack) xs
+prop_groupBy  f xs  = groupBy f xs == (map unpack . L.groupBy f . pack) xs
+
+prop_groupBy_LC :: (Char8 -> Char8 -> Bool) -> String8 -> Bool
+prop_groupBy_LC f' (String8 xs) =
+    groupBy f xs == (map LC.unpack . LC.groupBy f .  LC.pack) xs
+  where
+    f :: Char -> Char -> Bool
+    f = castFn f'
+
+-- prop_joinjoinByte xs ys c = L.joinWithByte c xs ys == L.join (L.singleton c) [xs,ys]
+
+prop_index xs =
+  not (null xs) ==>
+    forAll indices $ \i -> (xs !! i) == L.pack xs `L.index` (fromIntegral i)
+  where indices = choose (0, length xs -1)
+
+prop_index_D (String8 xs) =
+  not (null xs) ==>
+    forAll indices $ \i -> (xs !! i) == D.pack xs `D.index` (fromIntegral i)
+  where indices = choose (0, length xs -1)
+
+prop_index_C (String8 xs) =
+  not (null xs) ==>
+    forAll indices $ \i -> (xs !! i) == C.pack xs `C.index` (fromIntegral i)
+  where indices = choose (0, length xs -1)
+
+-- | Test 'indexMaybe' for Lazy and Strict 'ByteString's.
+--   If we are testing within the bounds it should return a 'Just' value.
+--   If we are testing outside of the bounds it should return a 'Nothing' value.
+prop_indexMaybe_Just_L xs =
+  not (null xs) ==>
+    forAll indices $ \i -> isJust (ys `L.indexMaybe` (fromIntegral i))
+  where
+    ys = L.pack xs
+    indices = choose (0, length xs -1)
+
+prop_indexMaybe_Just_P xs =
+  not (null xs) ==>
+    forAll indices $ \i -> isJust (ys `P.indexMaybe` (fromIntegral i))
+  where
+    ys = P.pack xs
+    indices = choose (0, length xs -1)
+
+prop_indexMaybe_Nothing_L xs =
+  not (null xs) ==>
+    forAll indices $ \i -> isNothing (ys `L.indexMaybe` (fromIntegral i))
+  where
+      ys = L.pack xs
+      outOfBounds = choose (-100, length xs + 100)
+      indices = suchThat outOfBounds (\n -> n < 0 || n >= length xs)
+
+prop_indexMaybe_Nothing_P xs =
+  not (null xs) ==>
+    forAll indices $ \i -> isNothing (ys `P.indexMaybe` (fromIntegral i))
+  where
+    ys = P.pack xs
+    outOfBounds = choose (-100, length xs + 100)
+    indices = suchThat outOfBounds (\n -> n < 0 || n >= length xs)
+
+prop_elemIndex xs c = (elemIndex c xs) == fmap fromIntegral (L.elemIndex c (pack xs))
+
+prop_elemIndexCL :: String8 -> Char8 -> Bool
+prop_elemIndexCL (String8 xs) (Char8 c) =
+    (elemIndex c xs) == (C.elemIndex c (C.pack xs))
+
+prop_elemIndices xs c = elemIndices c xs == map fromIntegral (L.elemIndices c (pack xs))
+
+prop_count c xs = length (L.elemIndices c xs) == fromIntegral (L.count c xs)
+
+prop_findIndex xs f = (findIndex f xs) == fmap fromIntegral (L.findIndex f (pack xs))
+prop_findIndexEnd xs f = (findIndexEnd f xs) == fmap fromIntegral (L.findIndexEnd f (pack xs))
+prop_findIndicies xs f = (findIndices f xs) == map fromIntegral (L.findIndices f (pack xs))
+
+prop_elem    xs c = (c `elem` xs)    == (c `L.elem` (pack xs))
+prop_notElem xs c = (c `notElem` xs) == (L.notElem c (pack xs))
+prop_elem_notelem xs c = c `L.elem` xs == not (c `L.notElem` xs)
+
+-- prop_filterByte  xs c = L.filterByte c xs == L.filter (==c) xs
+-- prop_filterByte2 xs c = unpack (L.filterByte c xs) == filter (==c) (unpack xs)
+
+-- prop_filterNotByte  xs c = L.filterNotByte c xs == L.filter (/=c) xs
+-- prop_filterNotByte2 xs c = unpack (L.filterNotByte c xs) == filter (/=c) (unpack xs)
+
+prop_find p xs = find p xs == L.find p (pack xs)
+
+prop_find_findIndex p xs =
+    L.find p xs == case L.findIndex p xs of
+                                Just n -> Just (xs `L.index` n)
+                                _      -> Nothing
+
+prop_isPrefixOf xs ys = isPrefixOf xs ys == (pack xs `L.isPrefixOf` pack ys)
+prop_stripPrefix xs ys = (pack <$> stripPrefix xs ys) == (pack xs `L.stripPrefix` pack ys)
+
+prop_isSuffixOf xs ys = isSuffixOf xs ys == (pack xs `L.isSuffixOf` pack ys)
+prop_stripSuffix xs ys = (pack <$> stripSuffix xs ys) == (pack xs `L.stripSuffix` pack ys)
+
+{-
+prop_sort1 xs = sort xs == (unpack . L.sort . pack) xs
+prop_sort2 xs = (not (null xs)) ==> (L.head . L.sort . pack $ xs) == minimum xs
+prop_sort3 xs = (not (null xs)) ==> (L.last . L.sort . pack $ xs) == maximum xs
+prop_sort4 xs ys =
+        (not (null xs)) ==>
+        (not (null ys)) ==>
+        (L.head . L.sort) (L.append (pack xs) (pack ys)) == min (minimum xs) (minimum ys)
+
+prop_sort5 xs ys =
+        (not (null xs)) ==>
+        (not (null ys)) ==>
+        (L.last . L.sort) (L.append (pack xs) (pack ys)) == max (maximum xs) (maximum ys)
+
+-}
+
+------------------------------------------------------------------------
+-- Misc ByteString properties
+
+prop_nil1BB = P.length P.empty == 0
+prop_nil2BB = P.unpack P.empty == []
+prop_nil1BB_monoid = P.length mempty == 0
+prop_nil2BB_monoid = P.unpack mempty == []
+
+prop_nil1LL_monoid = L.length mempty == 0
+prop_nil2LL_monoid = L.unpack mempty == []
+
+prop_tailSBB xs = not (P.null xs) ==> P.tail xs == P.pack (tail (P.unpack xs))
+
+prop_nullBB xs = null (P.unpack xs) == P.null xs
+
+prop_lengthBB xs = P.length xs == length1 xs
+    where
+        length1 ys
+            | P.null ys = 0
+            | otherwise = 1 + length1 (P.tail ys)
+
+prop_lengthSBB xs = length xs == P.length (P.pack xs)
+
+prop_indexBB xs =
+  not (null xs) ==>
+    forAll indices $ \i -> (xs !! i) == P.pack xs `P.index` i
+  where indices = choose (0, length xs -1)
+
+prop_unsafeIndexBB xs =
+  not (null xs) ==>
+    forAll indices $ \i -> (xs !! i) == P.pack xs `P.unsafeIndex` i
+  where indices = choose (0, length xs -1)
+
+prop_mapfusionBB f g xs = P.map f (P.map g xs) == P.map (f . g) xs
+
+prop_filterBB f xs = P.filter f (P.pack xs) == P.pack (filter f xs)
+
+prop_filterfusionBB f g xs = P.filter f (P.filter g xs) == P.filter (\c -> f c && g c) xs
+
+prop_elemSBB x xs = P.elem x (P.pack xs) == elem x xs
+
+prop_takeSBB i xs = P.take i (P.pack xs) == P.pack (take i xs)
+prop_dropSBB i xs = P.drop i (P.pack xs) == P.pack (drop i xs)
+
+prop_splitAtSBB i xs = -- collect (i >= 0 && i < length xs) $
+    P.splitAt i (P.pack xs) ==
+    let (a,b) = splitAt i xs in (P.pack a, P.pack b)
+
+prop_foldlBB f c xs = P.foldl f c (P.pack xs) == foldl f c xs
+  where _ = c :: Char
+
+prop_scanlfoldlBB f z xs = not (P.null xs) ==> P.last (P.scanl f z xs) == P.foldl f z xs
+
+prop_foldrBB f c xs = P.foldl f c (P.pack xs) == foldl f c xs
+  where _ = c :: Char
+
+prop_takeWhileSBB f xs = P.takeWhile f (P.pack xs) == P.pack (takeWhile f xs)
+prop_dropWhileSBB f xs = P.dropWhile f (P.pack xs) == P.pack (dropWhile f xs)
+
+prop_spanSBB f xs = P.span f (P.pack xs) ==
+    let (a,b) = span f xs in (P.pack a, P.pack b)
+
+prop_breakSBB f xs = P.break f (P.pack xs) ==
+    let (a,b) = break f xs in (P.pack a, P.pack b)
+
+prop_breakspan_1BB xs c = P.break (== c) xs == P.span (/= c) xs
+
+prop_linesSBB (String8 xs) = C.lines (C.pack xs) == map C.pack (lines xs)
+
+prop_unlinesSBB xss = C.unlines (map C.pack xss) == C.pack (unlines xss)
+
+prop_wordsSBB (String8 xs) =
+    C.words (C.pack xs) == map C.pack (words xs)
+
+prop_wordsLC (String8 xs) =
+    LC.words (LC.pack xs) == map LC.pack (words xs)
+
+prop_unwordsSBB xss = C.unwords (map C.pack xss) == C.pack (unwords xss)
+prop_unwordsSLC xss = LC.unwords (map LC.pack xss) == LC.pack (unwords xss)
+
+prop_splitWithBB_empty f = P.splitWith f mempty == []
+
+prop_splitWithBB f xs = (l1 == l2 || l1 == l2+1) &&
+        sum (map P.length splits) == P.length xs - l2
+  where splits = P.splitWith f xs
+        l1 = length splits
+        l2 = P.length (P.filter f xs)
+
+prop_splitBB_empty c = P.split c mempty == []
+
+prop_joinsplitBB c xs = P.intercalate (P.pack [c]) (P.split c xs) == xs
+
+prop_intercalatePL c x y =
+
+    P.intercalate (P.singleton c) (x : y : []) ==
+ --     intercalate (singleton c) (s1 : s2 : [])
+
+    P.pack (intercalate [c] [P.unpack x,P.unpack y])
+
+-- prop_linessplitBB xs =
+--     (not . C.null) xs ==>
+--     C.lines' xs == C.split '\n' xs
+
+-- false:
+{-
+prop_linessplit2BB xs =
+   (not . C.null) xs ==>
+    C.lines xs == C.split '\n' xs ++ (if C.last xs == '\n' then [C.empty] else [])
+-}
+
+prop_splitsplitWithBB c xs = P.split c xs == P.splitWith (== c) xs
+
+prop_bijectionBB  (Char8 c) = (P.w2c . P.c2w) c == id c
+prop_bijectionBB'        w  = (P.c2w . P.w2c) w == id w
+
+prop_packunpackBB  s = (P.unpack . P.pack) s == id s
+prop_packunpackBB' s = (P.pack . P.unpack) s == id s
+
+prop_eq1BB xs      = xs            == (P.unpack . P.pack $ xs)
+prop_eq2BB xs      = xs == (xs :: P.ByteString)
+prop_eq3BB xs ys   = (xs == ys) == (P.unpack xs == P.unpack ys)
+
+prop_compare1BB xs  = (P.pack xs         `compare` P.pack xs) == EQ
+prop_compare2BB xs c = (P.pack (xs++[c]) `compare` P.pack xs) == GT
+prop_compare3BB xs c = (P.pack xs `compare` P.pack (xs++[c])) == LT
+
+prop_compare4BB xs  = (not (null xs)) ==> (P.pack xs  `compare` P.empty) == GT
+prop_compare5BB xs  = (not (null xs)) ==> (P.empty `compare` P.pack xs) == LT
+prop_compare6BB xs ys= (not (null ys)) ==> (P.pack (xs++ys)  `compare` P.pack xs) == GT
+
+prop_compare7BB (Char8 x) (Char8 y) =
+                        x  `compare` y  == (C.singleton x `compare` C.singleton y)
+prop_compare8BB xs ys = xs `compare` ys == (P.pack xs `compare` P.pack ys)
+
+prop_consBB  c xs = P.unpack (P.cons c (P.pack xs)) == (c:xs)
+prop_cons1BB (String8 xs)
+                  = 'X' : xs == C.unpack ('X' `C.cons` (C.pack xs))
+prop_cons2BB xs c = c : xs == P.unpack (c `P.cons` (P.pack xs))
+prop_cons3BB (Char8 c)
+                  = C.unpack (C.singleton c) == (c:[])
+prop_cons4BB c    = (c `P.cons` P.empty)  == P.pack (c:[])
+
+prop_snoc1BB xs c = xs ++ [c] == P.unpack ((P.pack xs) `P.snoc` c)
+
+prop_head1BB xs     = (not (null xs)) ==> head  xs  == (P.head . P.pack) xs
+prop_head2BB xs    = (not (null xs)) ==> head xs   == (P.unsafeHead . P.pack) xs
+prop_head3BB xs    = not (P.null xs) ==> P.head xs == head (P.unpack xs)
+
+prop_tailBB xs     = (not (null xs)) ==> tail xs    == (P.unpack . P.tail . P.pack) xs
+prop_tail1BB xs    = (not (null xs)) ==> tail xs    == (P.unpack . P.unsafeTail. P.pack) xs
+
+prop_lastBB xs     = (not (null xs)) ==> last xs    == (P.last . P.pack) xs
+prop_last1BB xs    = (not (null xs)) ==> last xs    == (P.unsafeLast . P.pack) xs
+
+prop_initBB xs     =
+    (not (null xs)) ==>
+    init xs    == (P.unpack . P.init . P.pack) xs
+prop_init1BB xs     =
+    (not (null xs)) ==>
+    init xs    == (P.unpack . P.unsafeInit . P.pack) xs
+
+-- prop_null xs = (null xs) ==> null xs == (nullPS (pack xs))
+
+prop_append1BB xs    = (xs ++ xs) == (P.unpack $ P.pack xs `P.append` P.pack xs)
+prop_append2BB xs ys = (xs ++ ys) == (P.unpack $ P.pack xs `P.append` P.pack ys)
+prop_append3BB xs ys = P.append xs ys == P.pack (P.unpack xs ++ P.unpack ys)
+
+prop_append1BB_monoid xs    = (xs ++ xs) == (P.unpack $ P.pack xs `mappend` P.pack xs)
+prop_append2BB_monoid xs ys = (xs ++ ys) == (P.unpack $ P.pack xs `mappend` P.pack ys)
+prop_append3BB_monoid xs ys = mappend xs ys == P.pack (P.unpack xs ++ P.unpack ys)
+
+prop_append1LL_monoid xs    = (xs ++ xs) == (L.unpack $ L.pack xs `mappend` L.pack xs)
+prop_append2LL_monoid xs ys = (xs ++ ys) == (L.unpack $ L.pack xs `mappend` L.pack ys)
+prop_append3LL_monoid xs ys = mappend xs ys == L.pack (L.unpack xs ++ L.unpack ys)
+
+prop_map1BB f xs   = P.map f (P.pack xs)    == P.pack (map f xs)
+prop_map2BB f g xs = P.map f (P.map g xs) == P.map (f . g) xs
+prop_map3BB f xs   = map f xs == (P.unpack . P.map f .  P.pack) xs
+-- prop_mapBB' f xs   = P.map' f (P.pack xs) == P.pack (map f xs)
+
+prop_filter1BB (String8 xs) = (filter (=='X') xs) == (C.unpack $ C.filter (=='X') (C.pack xs))
+prop_filter2BB p        xs  = (filter p       xs) == (P.unpack $ P.filter p (P.pack xs))
+
+prop_findBB p xs = find p xs == P.find p (P.pack xs)
+
+prop_find_findIndexBB p xs =
+    P.find p xs == case P.findIndex p xs of
+                                Just n -> Just (xs `P.unsafeIndex` n)
+                                _      -> Nothing
+
+prop_foldl1BB xs a = ((foldl (\x c -> if c == a then x else c:x) [] xs)) ==
+                   (P.unpack $ P.foldl (\x c -> if c == a then x else c `P.cons` x) P.empty (P.pack xs))
+prop_foldl2BB xs = P.foldl (\xs c -> c `P.cons` xs) P.empty (P.pack xs) == P.reverse (P.pack xs)
+
+prop_foldr1BB xs a = ((foldr (\c x -> if c == a then x else c:x) [] xs)) ==
+                (P.unpack $ P.foldr (\c x -> if c == a then x else c `P.cons` x)
+                    P.empty (P.pack xs))
+
+prop_foldr2BB xs = P.foldr (\c xs -> c `P.cons` xs) P.empty (P.pack xs) == (P.pack xs)
+
+prop_foldl1_1BB xs =
+    (not . P.null) xs ==>
+    P.foldl1 (\x c -> if c > x then c else x)   xs ==
+    P.foldl  (\x c -> if c > x then c else x) 0 xs
+
+prop_foldl1_2BB xs =
+    (not . P.null) xs ==>
+    P.foldl1 const xs == P.head xs
+
+prop_foldl1_3BB xs =
+    (not . P.null) xs ==>
+    P.foldl1 (flip const) xs == P.last xs
+
+prop_foldr1_1BB xs =
+    (not . P.null) xs ==>
+    P.foldr1 (\c x -> if c > x then c else x)   xs ==
+    P.foldr  (\c x -> if c > x then c else x) 0 xs
+
+prop_foldr1_2BB xs =
+    (not . P.null) xs ==>
+    P.foldr1 (flip const) xs == P.last xs
+
+prop_foldr1_3BB xs =
+    (not . P.null) xs ==>
+    P.foldr1 const xs == P.head xs
+
+prop_takeWhileBB_ne xs a =
+  (takeWhile (/= a) xs) == (P.unpack . (P.takeWhile (/= a)) . P.pack) xs
+prop_takeWhileBB_eq xs a =
+  (takeWhile (== a) xs) == (P.unpack . (P.takeWhile (== a)) . P.pack) xs
+
+prop_dropWhileBB_ne xs a =
+  (dropWhile (/= a) xs) == (P.unpack . (P.dropWhile (/= a)) . P.pack) xs
+prop_dropWhileBB_eq xs a =
+  (dropWhile (== a) xs) == (P.unpack . (P.dropWhile (== a)) . P.pack) xs
+
+prop_dropWhileCC_isSpace (String8 xs) =
+        (dropWhile isSpace xs) ==
+       (C.unpack .  (C.dropWhile isSpace) . C.pack) xs
+
+prop_takeBB xs = (take 10 xs) == (P.unpack . (P.take 10) . P.pack) xs
+
+prop_dropBB xs = (drop 10 xs) == (P.unpack . (P.drop 10) . P.pack) xs
+
+prop_splitAtBB i xs = -- collect (i >= 0 && i < length xs) $
+    splitAt i xs ==
+    let (x,y) = P.splitAt i (P.pack xs) in (P.unpack x, P.unpack y)
+
+prop_spanBB xs a = (span (/=a) xs) == (let (x,y) = P.span (/=a) (P.pack xs)
+                                     in (P.unpack x, P.unpack y))
+
+prop_breakBB xs a = (break (/=a) xs) == (let (x,y) = P.break (/=a) (P.pack xs)
+                                       in (P.unpack x, P.unpack y))
+
+prop_reverse1BB xs = (reverse xs) == (P.unpack . P.reverse . P.pack) xs
+prop_reverse2BB xs = P.reverse (P.pack xs) == P.pack (reverse xs)
+prop_reverse3BB xs = reverse (P.unpack xs) == (P.unpack . P.reverse) xs
+
+prop_elemBB xs a = (a `elem` xs) == (a `P.elem` (P.pack xs))
+
+prop_notElemBB c xs = P.notElem c (P.pack xs) == notElem c xs
+
+-- should try to stress it
+prop_concat1BB xs = (concat [xs,xs]) == (P.unpack $ P.concat [P.pack xs, P.pack xs])
+prop_concat2BB xs = (concat [xs,[]]) == (P.unpack $ P.concat [P.pack xs, P.pack []])
+prop_concatBB xss = P.concat (map P.pack xss) == P.pack (concat xss)
+
+prop_concat1BB_monoid xs = (concat [xs,xs]) == (P.unpack $ mconcat [P.pack xs, P.pack xs])
+prop_concat2BB_monoid xs = (concat [xs,[]]) == (P.unpack $ mconcat [P.pack xs, P.pack []])
+prop_concatBB_monoid xss = mconcat (map P.pack xss) == P.pack (concat xss)
+
+prop_concat1LL_monoid xs = (concat [xs,xs]) == (L.unpack $ mconcat [L.pack xs, L.pack xs])
+prop_concat2LL_monoid xs = (concat [xs,[]]) == (L.unpack $ mconcat [L.pack xs, L.pack []])
+prop_concatLL_monoid xss = mconcat (map L.pack xss) == L.pack (concat xss)
+
+prop_concatMapBB xs = C.concatMap C.singleton xs == (C.pack . concatMap (:[]) . C.unpack) xs
+
+prop_anyBB xs a = (any (== a) xs) == (P.any (== a) (P.pack xs))
+prop_allBB xs a = (all (== a) xs) == (P.all (== a) (P.pack xs))
+
+prop_linesBB (String8 xs) =
+    (lines xs) == ((map C.unpack) . C.lines . C.pack) xs
+
+prop_unlinesBB (String8 xs) =
+    (unlines.lines) xs == (C.unpack. C.unlines . C.lines .C.pack) xs
+prop_unlinesLC (String8 xs) =
+    (unlines.lines) xs == (LC.unpack. LC.unlines .  LC.lines .LC.pack) xs
+
+prop_lines_lazy1 =
+    head (LC.lines (LC.append (LC.pack "a\nb\n") undefined)) == LC.pack "a"
+prop_lines_lazy2 =
+    head (tail (LC.lines (LC.append (LC.pack "a\nb\n") undefined))) == LC.pack "b"
+
+prop_wordsBB (String8 xs) =
+    (words xs) == ((map C.unpack) . C.words . C.pack) xs
+-- prop_wordstokensBB xs = C.words xs == C.tokens isSpace xs
+
+prop_unwordsBB (String8 xs) =
+    (C.pack.unwords.words) xs == (C.unwords . C.words .C.pack) xs
+
+prop_groupBB xs   = group xs == (map P.unpack . P.group . P.pack) xs
+
+prop_groupByBB  xs = groupBy (==) xs == (map P.unpack . P.groupBy (==) . P.pack) xs
+prop_groupBy1BB xs = groupBy (/=) xs == (map P.unpack . P.groupBy (/=) . P.pack) xs
+prop_groupBy1CC (String8 xs) = groupBy (==) xs == (map C.unpack . C.groupBy (==) . C.pack) xs
+prop_groupBy2CC (String8 xs) = groupBy (/=) xs == (map C.unpack . C.groupBy (/=) . C.pack) xs
+
+prop_joinBB (String8 xs) (String8 ys) =
+    (concat . (intersperse ys) . lines) xs ==
+    (C.unpack $ C.intercalate (C.pack ys) (C.lines (C.pack xs)))
+
+prop_elemIndex1BB (String8 xs)           = (elemIndex 'X' xs) == (C.elemIndex 'X' (C.pack xs))
+prop_elemIndex2BB (String8 xs) (Char8 c) = (elemIndex  c  xs) == (C.elemIndex  c  (C.pack xs))
+
+-- prop_lineIndices1BB xs = C.elemIndices '\n' xs == C.lineIndices xs
+
+prop_countBB c xs = length (P.elemIndices c xs) == P.count c xs
+
+prop_elemIndexEnd1BB c xs =
+  P.elemIndexEnd c (P.pack xs) ==
+    case P.elemIndex c (P.pack (reverse xs)) of
+      Nothing -> Nothing
+      Just i  -> Just (length xs - 1 - i)
+
+prop_elemIndexEnd1CC (Char8 c) (String8 xs) =
+  C.elemIndexEnd c (C.pack xs) ==
+    case C.elemIndex c (C.pack (reverse xs)) of
+      Nothing -> Nothing
+      Just i  -> Just (length xs - 1 - i)
+
+prop_elemIndexEnd1LL c xs =
+  L.elemIndexEnd c (L.pack xs) ==
+    case L.elemIndex c (L.pack (reverse xs)) of
+      Nothing -> Nothing
+      Just i  -> Just (fromIntegral (length xs) - 1 - i)
+
+prop_elemIndexEnd1DD (Char8 c) (String8 xs) =
+  D.elemIndexEnd c (D.pack xs) ==
+    case D.elemIndex c (D.pack (reverse xs)) of
+      Nothing -> Nothing
+      Just i  -> Just (fromIntegral (length xs) - 1 - i)
+
+prop_elemIndicesBB xs c = elemIndices c xs == P.elemIndices c (P.pack xs)
+
+prop_findIndexBB xs a = (findIndex (==a) xs) == (P.findIndex (==a) (P.pack xs))
+
+prop_findIndexEndBB xs a = (findIndexEnd (==a) xs) == (P.findIndexEnd (==a) (P.pack xs))
+
+prop_findIndexEndLL xs a = (findIndexEnd (==a) xs) == fmap fromIntegral (L.findIndexEnd (==a) (L.pack xs))
+
+prop_findIndexEndDD (String8 xs) (Char8 a) = (findIndexEnd (==a) xs) == fmap fromIntegral (D.findIndexEnd (==a) (D.pack xs))
+
+prop_findIndiciesBB xs c = (findIndices (==c) xs) == (P.findIndices (==c) (P.pack xs))
+
+-- example properties from QuickCheck.Batch
+prop_sort1BB xs = sort xs == (P.unpack . P.sort . P.pack) xs
+prop_sort2BB xs = (not (null xs)) ==> (P.head . P.sort . P.pack $ xs) == minimum xs
+prop_sort3BB xs = (not (null xs)) ==> (P.last . P.sort . P.pack $ xs) == maximum xs
+prop_sort4BB xs ys =
+        (not (null xs)) ==>
+        (not (null ys)) ==>
+        (P.head . P.sort) (P.append (P.pack xs) (P.pack ys)) == min (minimum xs) (minimum ys)
+prop_sort5BB xs ys =
+        (not (null xs)) ==>
+        (not (null ys)) ==>
+        (P.last . P.sort) (P.append (P.pack xs) (P.pack ys)) == max (maximum xs) (maximum ys)
+
+prop_intersperseBB c xs = (intersperse c xs) == (P.unpack $ P.intersperse c (P.pack xs))
+
+-- prop_transposeBB xs = (transpose xs) == ((map P.unpack) . P.transpose .  (map P.pack)) xs
+
+prop_maximumBB xs = (not (null xs)) ==> (maximum xs) == (P.maximum ( P.pack xs ))
+prop_minimumBB xs = (not (null xs)) ==> (minimum xs) == (P.minimum ( P.pack xs ))
+
+prop_strip = C.strip `eq1` (C.dropSpace . C.reverse . C.dropSpace . C.reverse)
+
+-- prop_dropSpaceBB xs    = dropWhile isSpace xs == C.unpack (C.dropSpace (C.pack xs))
+-- prop_dropSpaceEndBB xs = (C.reverse . (C.dropWhile isSpace) . C.reverse) (C.pack xs) ==
+--                        (C.dropSpaceEnd (C.pack xs))
+
+-- prop_breakSpaceBB xs =
+--     (let (x,y) = C.breakSpace (C.pack xs)
+--      in (C.unpack x, C.unpack y)) == (break isSpace xs)
+
+prop_spanEndBB xs =
+        (C.spanEnd (not . isSpace) (C.pack xs)) ==
+        (let (x,y) = C.span (not.isSpace) (C.reverse (C.pack xs)) in (C.reverse y,C.reverse x))
+
+prop_breakEndBB p xs = P.breakEnd (not.p) xs == P.spanEnd p xs
+prop_breakEndCC p xs = C.breakEnd (not.p) xs == C.spanEnd p xs
+
+{-
+prop_breakCharBB c xs =
+        (break (==c) xs) ==
+        (let (x,y) = C.breakChar c (C.pack xs) in (C.unpack x, C.unpack y))
+
+prop_spanCharBB c xs =
+        (break (/=c) xs) ==
+        (let (x,y) = C.spanChar c (C.pack xs) in (C.unpack x, C.unpack y))
+
+prop_spanChar_1BB c xs =
+        (C.span (==c) xs) == C.spanChar c xs
+
+prop_wordsBB' xs =
+    (C.unpack . C.unwords  . C.words' . C.pack) xs ==
+    (map (\c -> if isSpace c then ' ' else c) xs)
+
+-- prop_linesBB' xs = (C.unpack . C.unlines' . C.lines' . C.pack) xs == (xs)
+-}
+
+prop_unfoldrBB c =
+    forAll arbitrarySizedIntegral $ \n ->
+      (fst $ C.unfoldrN n fn c) == (C.pack $ take n $ unfoldr fn c)
+  where
+    fn x = Just (x, if x == maxBound then x else succ x)
+
+prop_prefixBB xs ys = isPrefixOf xs ys == (P.pack xs `P.isPrefixOf` P.pack ys)
+prop_prefixLL xs ys = isPrefixOf xs ys == (L.pack xs `L.isPrefixOf` L.pack ys)
+prop_suffixBB xs ys = isSuffixOf xs ys == (P.pack xs `P.isSuffixOf` P.pack ys)
+prop_suffixLL xs ys = isSuffixOf xs ys == (L.pack xs `L.isSuffixOf` L.pack ys)
+
+prop_stripPrefixBB xs ys = (P.pack <$> stripPrefix xs ys) == (P.pack xs `P.stripPrefix` P.pack ys)
+prop_stripPrefixLL xs ys = (L.pack <$> stripPrefix xs ys) == (L.pack xs `L.stripPrefix` L.pack ys)
+prop_stripSuffixBB xs ys = (P.pack <$> stripSuffix xs ys) == (P.pack xs `P.stripSuffix` P.pack ys)
+prop_stripSuffixLL xs ys = (L.pack <$> stripSuffix xs ys) == (L.pack xs `L.stripSuffix` L.pack ys)
+
+prop_copyBB xs = let p = P.pack xs in P.copy p == p
+prop_copyLL xs = let p = L.pack xs in L.copy p == p
+
+prop_initsBB xs = inits xs == map P.unpack (P.inits (P.pack xs))
+
+prop_tailsBB xs = tails xs == map P.unpack (P.tails (P.pack xs))
+
+-- correspondance between break and breakSubstring
+prop_breakSubstringBB c l
+    = P.break (== c) l == P.breakSubstring (P.singleton c) l
+
+prop_breakSubstring_isInfixOf s l
+    = P.isInfixOf s l == if P.null s then True
+                                     else case P.breakSubstring s l of
+                                            (x,y) | P.null y  -> False
+                                                  | otherwise -> True
+
+prop_replicate1BB c = forAll arbitrarySizedIntegral $ \n ->
+                      P.unpack (P.replicate n c) == replicate n c
+prop_replicate2BB c = forAll arbitrarySizedIntegral $ \n ->
+                      P.replicate n c == fst (P.unfoldrN n (\u -> Just (u,u)) c)
+
+prop_replicate3BB c = P.unpack (P.replicate 0 c) == replicate 0 c
+
+prop_readintBB n = (fst . fromJust . C.readInt . C.pack . show) n == (n :: Int)
+prop_readintLL n = (fst . fromJust . D.readInt . D.pack . show) n == (n :: Int)
+
+prop_readBB x = (read . show) x == (x :: P.ByteString)
+prop_readLL x = (read . show) x == (x :: L.ByteString)
+
+prop_readint2BB (String8 s) =
+    let s' = filter (\c -> c `notElem` ['0'..'9']) s
+    in C.readInt (C.pack s') == Nothing
+
+prop_readintegerBB n = (fst . fromJust . C.readInteger . C.pack . show) n == (n :: Integer)
+prop_readintegerLL n = (fst . fromJust . D.readInteger . D.pack . show) n == (n :: Integer)
+
+prop_readinteger2BB (String8 s) =
+    let s' = filter (\c -> c `notElem` ['0'..'9']) s
+    in C.readInteger (C.pack s') == Nothing
+
+
+-- Ensure that readInt and readInteger over lazy ByteStrings are not
+-- excessively strict.
+prop_readIntSafe         = (fst . fromJust . D.readInt) (Chunk (C.pack "1z") Empty)         == 1
+prop_readIntUnsafe       = (fst . fromJust . D.readInt) (Chunk (C.pack "2z") undefined)     == 2
+prop_readIntegerSafe     = (fst . fromJust . D.readInteger) (Chunk (C.pack "1z") Empty)     == 1
+prop_readIntegerUnsafe   = (fst . fromJust . D.readInteger) (Chunk (C.pack "2z") undefined) == 2
+
+-- prop_filterChar1BB c xs = (filter (==c) xs) == ((C.unpack . C.filterChar c . C.pack) xs)
+-- prop_filterChar2BB c xs = (C.filter (==c) (C.pack xs)) == (C.filterChar c (C.pack xs))
+-- prop_filterChar3BB c xs = C.filterChar c xs == C.replicate (C.count c xs) c
+
+-- prop_filterNotChar1BB c xs = (filter (/=c) xs) == ((C.unpack . C.filterNotChar c . C.pack) xs)
+-- prop_filterNotChar2BB c xs = (C.filter (/=c) (C.pack xs)) == (C.filterNotChar c (C.pack xs))
+
+-- prop_joinjoinpathBB xs ys c = C.joinWithChar c xs ys == C.join (C.singleton c) [xs,ys]
+
+prop_zipBB  xs ys = zip xs ys == P.zip (P.pack xs) (P.pack ys)
+prop_zipLC (String8 xs) (String8 ys)
+                  = zip xs ys == LC.zip (LC.pack xs) (LC.pack ys)
+prop_zip1BB xs ys = P.zip xs ys == zip (P.unpack xs) (P.unpack ys)
+
+prop_zipWithBB xs ys = P.zipWith (,) xs ys == P.zip xs ys
+prop_zipWithCC xs ys = C.zipWith (,) xs ys == C.zip xs ys
+prop_zipWithLC xs ys = LC.zipWith (,) xs ys == LC.zip xs ys
+
+prop_packZipWithBB f xs ys = P.pack (P.zipWith f xs ys) == P.packZipWith f xs ys
+prop_packZipWithLL f xs ys = L.pack (L.zipWith f xs ys) == L.packZipWith f xs ys
+prop_packZipWithBC f xs ys = C.pack (C.zipWith f xs ys) == C.packZipWith f xs ys
+prop_packZipWithLC f xs ys = LC.pack (LC.zipWith f xs ys) == LC.packZipWith f xs ys
+
+
+prop_unzipBB x = let (xs,ys) = unzip x in (P.pack xs, P.pack ys) == P.unzip x
+
+#if MIN_VERSION_base(4,9,0)
+prop_stimesBB :: NonNegative Int -> P.ByteString -> Bool
+prop_stimesBB (NonNegative i) bs = stimes i bs == mtimesDefault i bs
+
+prop_stimesLL :: NonNegative Int -> L.ByteString -> Bool
+prop_stimesLL (NonNegative i) bs = stimes i bs == mtimesDefault i bs
+#endif
+
+-- prop_zipwith_spec f p q =
+--   P.pack (P.zipWith f p q) == P.zipWith' f p q
+--   where _ = f :: Word8 -> Word8 -> Word8
+
+-- prop_join_spec c s1 s2 =
+--  P.join (P.singleton c) (s1 : s2 : []) == P.joinWithByte c s1 s2
+
+------------------------------------------------------------------------
+
+-- Test IsString, Show, Read, pack, unpack
+prop_isstring    :: String8 -> Bool
+prop_isstring_lc :: String8 -> Bool
+
+prop_isstring    (String8 x) = C.unpack  (fromString x :: C.ByteString) == x
+prop_isstring_lc (String8 x) = LC.unpack (fromString x :: LC.ByteString) == x
+
+prop_showP1 x = show x == show (C.unpack x)
+prop_showL1 x = show x == show (LC.unpack x)
+
+prop_readP1 x = read (show x) == (x :: P.ByteString)
+prop_readP2 x = read (show x) == C.pack (x :: String)
+
+prop_readL1 x = read (show x) == (x :: L.ByteString)
+prop_readL2 x = read (show x) == LC.pack (x :: String)
+
+prop_packunpack_s x = (P.unpack . P.pack) x == x
+prop_unpackpack_s x = (P.pack . P.unpack) x == x
+
+prop_packunpack_c (String8 x) = (C.unpack . C.pack) x == x
+prop_unpackpack_c          x  = (C.pack . C.unpack) x == x
+
+prop_packunpack_l x = (L.unpack . L.pack) x == x
+prop_unpackpack_l x = (L.pack . L.unpack) x == x
+
+prop_packunpack_lc (String8 x) = (LC.unpack . LC.pack) x == x
+prop_unpackpack_lc          x  = (LC.pack . LC.unpack) x == x
+
+prop_toFromChunks x = (L.fromChunks . L.toChunks) x == x
+prop_fromToChunks x = (L.toChunks . L.fromChunks) x == filter (not . P.null) x
+
+prop_toFromStrict x = (L.fromStrict . L.toStrict) x == x
+prop_fromToStrict x = (L.toStrict . L.fromStrict) x == x
+
+prop_packUptoLenBytes cs =
+    forAll (choose (0, length cs + 1)) $ \n ->
+      let (bs, cs') = P.packUptoLenBytes n cs
+       in P.length bs == min n (length cs)
+       && take n cs == P.unpack bs
+       && P.pack (take n cs) == bs
+       && drop n cs == cs'
+
+prop_packUptoLenChars (String8 cs) =
+    forAll (choose (0, length cs + 1)) $ \n ->
+      let (bs, cs') = P.packUptoLenChars n cs
+       in P.length bs == min n (length cs)
+       && take n cs == C.unpack bs
+       && C.pack (take n cs) == bs
+       && drop n cs == cs'
+
+prop_unpack_s cs =
+    forAll (choose (0, length cs)) $ \n ->
+      P.unpack (P.drop n $ P.pack cs) == drop n cs
+prop_unpack_c (String8 cs) =
+    forAll (choose (0, length cs)) $ \n ->
+      C.unpack (C.drop n $ C.pack cs) == drop n cs
+
+prop_unpack_l  cs =
+    forAll (choose (0, length cs)) $ \n ->
+      L.unpack (L.drop (fromIntegral n) $ L.pack cs) == drop n cs
+prop_unpack_lc (String8 cs) =
+    forAll (choose (0, length cs)) $ \n ->
+      LC.unpack (L.drop (fromIntegral n) $ LC.pack cs) == drop n cs
+
+prop_unpackBytes cs =
+    forAll (choose (0, length cs)) $ \n ->
+      P.unpackBytes (P.drop n $ P.pack cs) == drop n cs
+prop_unpackChars (String8 cs) =
+    forAll (choose (0, length cs)) $ \n ->
+      P.unpackChars (P.drop n $ C.pack cs) == drop n cs
+
+prop_unpackBytes_l =
+    forAll (sized $ \n -> resize (n * 10) arbitrary) $ \cs ->
+    forAll (choose (0, length cs)) $ \n ->
+      L.unpackBytes (L.drop (fromIntegral n) $ L.pack cs) == drop n cs
+prop_unpackChars_l =
+    forAll (sized $ \n -> resize (n * 10) arbitrary) $ \(String8 cs) ->
+    forAll (choose (0, length cs)) $ \n ->
+      L.unpackChars (L.drop (fromIntegral n) $ LC.pack cs) == drop n cs
+
+prop_unpackAppendBytesLazy cs' =
+    forAll (sized $ \n -> resize (n * 10) arbitrary) $ \cs ->
+    forAll (choose (0, 2)) $ \n ->
+      P.unpackAppendBytesLazy (P.drop n $ P.pack cs) cs' == drop n cs ++ cs'
+prop_unpackAppendCharsLazy (String8 cs') =
+    forAll (sized $ \n -> resize (n * 10) arbitrary) $ \(String8 cs) ->
+    forAll (choose (0, 2)) $ \n ->
+      P.unpackAppendCharsLazy (P.drop n $ C.pack cs) cs' == drop n cs ++ cs'
+
+prop_unpackAppendBytesStrict cs cs' =
+    forAll (choose (0, length cs)) $ \n ->
+      P.unpackAppendBytesStrict (P.drop n $ P.pack cs) cs' == drop n cs ++ cs'
+
+prop_unpackAppendCharsStrict (String8 cs) (String8 cs') =
+    forAll (choose (0, length cs)) $ \n ->
+      P.unpackAppendCharsStrict (P.drop n $ C.pack cs) cs' == drop n cs ++ cs'
+
+------------------------------------------------------------------------
+-- Unsafe functions
+
+-- Test unsafePackAddress
+prop_unsafePackAddress (CByteString x) = ioProperty $ do
+        let (p,_,_) = P.toForeignPtr (x `P.snoc` 0)
+        y <- withForeignPtr p $ \(Ptr addr) ->
+            P.unsafePackAddress addr
+        return (y == x)
+
+-- Test unsafePackAddressLen
+prop_unsafePackAddressLen x = ioProperty $ do
+        let i = P.length x
+            (p,_,_) = P.toForeignPtr (x `P.snoc` 0)
+        y <- withForeignPtr p $ \(Ptr addr) ->
+            P.unsafePackAddressLen i addr
+        return (y == x)
+
+prop_unsafeUseAsCString x = ioProperty $ do
+        let n = P.length x
+        y <- P.unsafeUseAsCString x $ \cstr ->
+                    sequence [ do a <- peekElemOff cstr i
+                                  let b = x `P.index` i
+                                  return (a == fromIntegral b)
+                             | i <- [0.. n-1]     ]
+        return (and y)
+
+prop_unsafeUseAsCStringLen x = ioProperty $ do
+        let n = P.length x
+        y <- P.unsafeUseAsCStringLen x $ \(cstr,_) ->
+                    sequence [ do a <- peekElemOff cstr i
+                                  let b = x `P.index` i
+                                  return (a == fromIntegral b)
+                             | i <- [0.. n-1]     ]
+        return (and y)
+
+prop_internal_invariant x = L.invariant x
+
+prop_useAsCString x = ioProperty $ do
+        let n = P.length x
+        y <- P.useAsCString x $ \cstr ->
+                    sequence [ do a <- peekElemOff cstr i
+                                  let b = x `P.index` i
+                                  return (a == fromIntegral b)
+                             | i <- [0.. n-1]     ]
+        return (and y)
+
+prop_packCString (CByteString x) = ioProperty $ do
+        y <- P.useAsCString x $ P.unsafePackCString
+        return (y == x)
+
+prop_packCString_safe (CByteString x) = ioProperty $ do
+        y <- P.useAsCString x $ P.packCString
+        return (y == x)
+
+prop_packCStringLen x = ioProperty $ do
+        y <- P.useAsCStringLen x $ P.unsafePackCStringLen
+        return (y == x && P.length y == P.length x)
+
+prop_packCStringLen_safe x = ioProperty $ do
+        y <- P.useAsCStringLen x $ P.packCStringLen
+        return (y == x && P.length y == P.length x)
+
+prop_packMallocCString (CByteString x) = ioProperty $ do
+
+         let (fp,_,_) = P.toForeignPtr x
+         ptr <- mallocArray0 (P.length x) :: IO (Ptr Word8)
+         forM_ [0 .. P.length x] $ \n -> pokeElemOff ptr n 0
+         withForeignPtr fp $ \qtr -> copyArray ptr qtr (P.length x)
+         y   <- P.unsafePackMallocCString (castPtr ptr)
+
+         let !z = y == x
+         free ptr `seq` return z
+
+prop_unsafeFinalize    x =
+    P.length x > 0 ==>
+      ioProperty $ do
+        x <- P.unsafeFinalize x
+        return (x == ())
+
+prop_packCStringFinaliser x = ioProperty $ do
+        y <- P.useAsCString x $ \cstr -> P.unsafePackCStringFinalizer (castPtr cstr) (P.length x) (return ())
+        return (y == x)
+
+prop_fromForeignPtr x = (let (a,b,c) = (P.toForeignPtr x)
+                                in P.fromForeignPtr a b c) == x
+
+------------------------------------------------------------------------
+-- IO
+
+prop_read_write_file_P x = ioProperty $ do
+    (fn, h) <- openTempFile "." "prop-compiled.tmp"
+    hClose h
+    P.writeFile fn x
+    y <- P.readFile fn
+    removeFile fn
+    return (x == y)
+
+prop_read_write_file_C x = ioProperty $ do
+    (fn, h) <- openTempFile "." "prop-compiled.tmp"
+    hClose h
+    C.writeFile fn x
+    y <- C.readFile fn
+    removeFile fn
+    return (x == y)
+
+prop_read_write_file_L x = ioProperty $ do
+    (fn, h) <- openTempFile "." "prop-compiled.tmp"
+    hClose h
+    L.writeFile fn x
+    y <- L.readFile fn
+    L.length y `seq` removeFile fn
+    return (x == y)
+
+prop_read_write_file_D x = ioProperty $ do
+    (fn, h) <- openTempFile "." "prop-compiled.tmp"
+    hClose h
+    D.writeFile fn x
+    y <- D.readFile fn
+    D.length y `seq` removeFile fn
+    return (x == y)
+
+------------------------------------------------------------------------
+
+prop_append_file_P x y = ioProperty $ do
+    (fn, h) <- openTempFile "." "prop-compiled.tmp"
+    hClose h
+    P.writeFile fn x
+    P.appendFile fn y
+    z <- P.readFile fn
+    removeFile fn
+    return (z == x `P.append` y)
+
+prop_append_file_C x y = ioProperty $ do
+    (fn, h) <- openTempFile "." "prop-compiled.tmp"
+    hClose h
+    C.writeFile fn x
+    C.appendFile fn y
+    z <- C.readFile fn
+    removeFile fn
+    return (z == x `C.append` y)
+
+prop_append_file_L x y = ioProperty $ do
+    (fn, h) <- openTempFile "." "prop-compiled.tmp"
+    hClose h
+    L.writeFile fn x
+    L.appendFile fn y
+    z <- L.readFile fn
+    L.length y `seq` removeFile fn
+    return (z == x `L.append` y)
+
+prop_append_file_D x y = ioProperty $ do
+    (fn, h) <- openTempFile "." "prop-compiled.tmp"
+    hClose h
+    D.writeFile fn x
+    D.appendFile fn y
+    z <- D.readFile fn
+    D.length y `seq` removeFile fn
+    return (z == x `D.append` y)
+
+prop_packAddress = C.pack "this is a test"
+            ==
+                   C.pack "this is a test"
+
+prop_isSpaceWord8 w = isSpace c == P.isSpaceChar8 c
+   where c = chr (fromIntegral (w :: Word8))
+
+
+------------------------------------------------------------------------
+-- ByteString.Short
+--
+
+prop_short_pack_unpack xs =
+    (Short.unpack . Short.pack) xs == xs
+prop_short_toShort_fromShort bs =
+    (Short.fromShort . Short.toShort) bs == bs
+
+prop_short_toShort_unpack bs =
+    (Short.unpack . Short.toShort) bs == P.unpack bs
+prop_short_pack_fromShort xs =
+    (Short.fromShort . Short.pack) xs == P.pack xs
+
+prop_short_empty =
+    Short.empty == Short.toShort P.empty
+ && Short.empty == Short.pack []
+ && Short.null (Short.toShort P.empty)
+ && Short.null (Short.pack [])
+ && Short.null Short.empty
+
+prop_short_null_toShort bs =
+    P.null bs == Short.null (Short.toShort bs)
+prop_short_null_pack xs =
+    null xs == Short.null (Short.pack xs)
+
+prop_short_length_toShort bs =
+    P.length bs == Short.length (Short.toShort bs)
+prop_short_length_pack xs =
+    length xs == Short.length (Short.pack xs)
+
+prop_short_index_pack xs =
+    all (\i -> Short.pack xs `Short.index` i == xs !! i)
+        [0 .. length xs - 1]
+prop_short_index_toShort bs =
+    all (\i -> Short.toShort bs `Short.index` i == bs `P.index` i)
+        [0 .. P.length bs - 1]
+
+prop_short_eq xs ys =
+    (xs == ys) == (Short.pack xs == Short.pack ys)
+prop_short_ord xs ys =
+    (xs `compare` ys) == (Short.pack xs `compare` Short.pack ys)
+
+prop_short_mappend_empty_empty =
+    Short.empty `mappend` Short.empty  == Short.empty
+prop_short_mappend_empty xs =
+    Short.empty `mappend` Short.pack xs == Short.pack xs
+ && Short.pack xs `mappend` Short.empty == Short.pack xs
+prop_short_mappend xs ys =
+    (xs `mappend` ys) == Short.unpack (Short.pack xs `mappend` Short.pack ys)
+prop_short_mconcat xss =
+    mconcat xss == Short.unpack (mconcat (map Short.pack xss))
+
+prop_short_fromString s =
+    fromString s == Short.fromShort (fromString s)
+
+prop_short_show xs =
+    show (Short.pack xs) == show (map P.w2c xs)
+prop_short_show' xs =
+    show (Short.pack xs) == show (P.pack xs)
+
+prop_short_read xs =
+    read (show (Short.pack xs)) == Short.pack xs
+
+prop_short_pinned :: NonNegative Int -> Property
+prop_short_pinned (NonNegative (I# len#)) = runST $ ST $ \s ->
+  case newPinnedByteArray# len# s of
+    (# s', mba# #) -> case unsafeFreezeByteArray# mba# s' of
+      (# s'', ba# #) -> let sbs = Short.SBS ba# in
+        (# s'', sbs === Short.toShort (Short.fromShort sbs) #)
+
+stripSuffix :: [W] -> [W] -> Maybe [W]
+stripSuffix xs ys = reverse <$> stripPrefix (reverse xs) (reverse ys)
+
+short_tests =
+    [ testProperty "pack/unpack"              prop_short_pack_unpack
+    , testProperty "toShort/fromShort"        prop_short_toShort_fromShort
+    , testProperty "toShort/unpack"           prop_short_toShort_unpack
+    , testProperty "pack/fromShort"           prop_short_pack_fromShort
+    , testProperty "empty"                    prop_short_empty
+    , testProperty "null/toShort"             prop_short_null_toShort
+    , testProperty "null/pack"                prop_short_null_pack
+    , testProperty "length/toShort"           prop_short_length_toShort
+    , testProperty "length/pack"              prop_short_length_pack
+    , testProperty "index/pack"               prop_short_index_pack
+    , testProperty "index/toShort"            prop_short_index_toShort
+    , testProperty "Eq"                       prop_short_eq
+    , testProperty "Ord"                      prop_short_ord
+    , testProperty "mappend/empty/empty"      prop_short_mappend_empty_empty
+    , testProperty "mappend/empty"            prop_short_mappend_empty
+    , testProperty "mappend"                  prop_short_mappend
+    , testProperty "mconcat"                  prop_short_mconcat
+    , testProperty "fromString"               prop_short_fromString
+    , testProperty "show"                     prop_short_show
+    , testProperty "show'"                    prop_short_show'
+    , testProperty "read"                     prop_short_read
+    , testProperty "pinned"                   prop_short_pinned
+    ]
+
+------------------------------------------------------------------------
+-- The entry point
+
+main :: IO ()
+main = defaultMain $ testGroup "All" tests
+
+--
+-- And now a list of all the properties to test.
+--
+
+tests = misc_tests
+     ++ bl_tests
+     ++ cc_tests
+     ++ bp_tests
+     ++ pl_tests
+     ++ bb_tests
+     ++ ll_tests
+     ++ io_tests
+     ++ short_tests
+     ++ rules
+
+--
+-- 'morally sound' IO
+--
+io_tests =
+    [ testProperty "readFile.writeFile" prop_read_write_file_P
+    , testProperty "readFile.writeFile" prop_read_write_file_C
+    , testProperty "readFile.writeFile" prop_read_write_file_L
+    , testProperty "readFile.writeFile" prop_read_write_file_D
+
+    , testProperty "appendFile        " prop_append_file_P
+    , testProperty "appendFile        " prop_append_file_C
+    , testProperty "appendFile        " prop_append_file_L
+    , testProperty "appendFile        " prop_append_file_D
+
+    , testProperty "packAddress       " prop_packAddress
+
+    ]
+
+misc_tests =
+    [ testProperty "packunpack (bytes)"     prop_packunpack_s
+    , testProperty "unpackpack (bytes)"     prop_unpackpack_s
+    , testProperty "packunpack (chars)"     prop_packunpack_c
+    , testProperty "unpackpack (chars)"     prop_unpackpack_c
+    , testProperty "packunpack (lazy bytes)" prop_packunpack_l
+    , testProperty "unpackpack (lazy bytes)" prop_unpackpack_l
+    , testProperty "packunpack (lazy chars)" prop_packunpack_lc
+    , testProperty "unpackpack (lazy chars)" prop_unpackpack_lc
+    , testProperty "unpack (bytes)"         prop_unpack_s
+    , testProperty "unpack (chars)"         prop_unpack_c
+    , testProperty "unpack (lazy bytes)"    prop_unpack_l
+    , testProperty "unpack (lazy chars)"    prop_unpack_lc
+    , testProperty "packUptoLenBytes"       prop_packUptoLenBytes
+    , testProperty "packUptoLenChars"       prop_packUptoLenChars
+    , testProperty "unpackBytes"            prop_unpackBytes
+    , testProperty "unpackChars"            prop_unpackChars
+    , testProperty "unpackBytes"            prop_unpackBytes_l
+    , testProperty "unpackChars"            prop_unpackChars_l
+    , testProperty "unpackAppendBytesLazy"  prop_unpackAppendBytesLazy
+    , testProperty "unpackAppendCharsLazy"  prop_unpackAppendCharsLazy
+    , testProperty "unpackAppendBytesStrict"prop_unpackAppendBytesStrict
+    , testProperty "unpackAppendCharsStrict"prop_unpackAppendCharsStrict
+    , testProperty "toFromChunks"           prop_toFromChunks
+    , testProperty "fromToChunks"           prop_fromToChunks
+    , testProperty "toFromStrict"           prop_toFromStrict
+    , testProperty "fromToStrict"           prop_fromToStrict
+
+    , testProperty "invariant"              prop_invariant
+    , testProperty "unsafe pack address"    prop_unsafePackAddress
+    , testProperty "unsafe pack address len"prop_unsafePackAddressLen
+    , testProperty "unsafeUseAsCString"     prop_unsafeUseAsCString
+    , testProperty "unsafeUseAsCStringLen"  prop_unsafeUseAsCStringLen
+    , testProperty "useAsCString"           prop_useAsCString
+    , testProperty "packCString"            prop_packCString
+    , testProperty "packCString safe"       prop_packCString_safe
+    , testProperty "packCStringLen"         prop_packCStringLen
+    , testProperty "packCStringLen safe"    prop_packCStringLen_safe
+    , testProperty "packCStringFinaliser"   prop_packCStringFinaliser
+    , testProperty "packMallocString"       prop_packMallocCString
+    , testProperty "unsafeFinalise"         prop_unsafeFinalize
+    , testProperty "invariant"              prop_internal_invariant
+    , testProperty "show 1"                 prop_showP1
+    , testProperty "show 2"                 prop_showL1
+    , testProperty "read 1"                 prop_readP1
+    , testProperty "read 2"                 prop_readP2
+    , testProperty "read 3"                 prop_readL1
+    , testProperty "read 4"                 prop_readL2
+    , testProperty "fromForeignPtr"         prop_fromForeignPtr
+    ]
+
+------------------------------------------------------------------------
+-- ByteString.Lazy <=> List
+
+bl_tests =
+    [ testProperty "all"         prop_allBL
+    , testProperty "any"         prop_anyBL
+    , testProperty "append"      prop_appendBL
+    , testProperty "compare"     prop_compareBL
+    , testProperty "concat"      prop_concatBL
+    , testProperty "cons"        prop_consBL
+    , testProperty "eq"          prop_eqBL
+    , testProperty "filter"      prop_filterBL
+    , testProperty "find"        prop_findBL
+    , testProperty "findIndex"   prop_findIndexBL
+    , testProperty "findIndexEnd"prop_findIndexEndBL
+    , testProperty "findIndices" prop_findIndicesBL
+    , testProperty "foldl"       prop_foldlBL
+    , testProperty "foldl'"      prop_foldlBL'
+    , testProperty "foldl1"      prop_foldl1BL
+    , testProperty "foldl1'"     prop_foldl1BL'
+    , testProperty "foldr"       prop_foldrBL
+    , testProperty "foldr1"      prop_foldr1BL
+    , testProperty "mapAccumL"   prop_mapAccumLBL
+    , testProperty "mapAccumR"   prop_mapAccumRBL
+    , testProperty "mapAccumR"   prop_mapAccumRDL
+    , testProperty "mapAccumR"   prop_mapAccumRCC
+    , testProperty "unfoldr"     prop_unfoldrBL
+    , testProperty "unfoldr"     prop_unfoldrLC
+    , testProperty "unfoldr"     prop_cycleLC
+    , testProperty "iterate"     prop_iterateLC
+    , testProperty "iterate"     prop_iterateLC_2
+    , testProperty "iterate"     prop_iterateL
+    , testProperty "repeat"      prop_repeatLC
+    , testProperty "repeat"      prop_repeatL
+    , testProperty "head"        prop_headBL
+    , testProperty "init"        prop_initBL
+    , testProperty "isPrefixOf"  prop_isPrefixOfBL
+    , testProperty "isSuffixOf"  prop_isSuffixOfBL
+    , testProperty "stripPrefix" prop_stripPrefixBL
+    , testProperty "stripSuffix" prop_stripSuffixBL
+    , testProperty "last"        prop_lastBL
+    , testProperty "length"      prop_lengthBL
+    , testProperty "map"         prop_mapBL
+    , testProperty "maximum"     prop_maximumBL
+    , testProperty "minimum"     prop_minimumBL
+    , testProperty "null"        prop_nullBL
+    , testProperty "reverse"     prop_reverseBL
+    , testProperty "snoc"        prop_snocBL
+    , testProperty "tail"        prop_tailBL
+    , testProperty "transpose"   prop_transposeBL
+    , testProperty "replicate"   prop_replicateBL
+    , testProperty "take"        prop_takeBL
+    , testProperty "drop"        prop_dropBL
+    , testProperty "splitAt"     prop_splitAtBL
+    , testProperty "takeWhile"   prop_takeWhileBL
+    , testProperty "dropWhile"   prop_dropWhileBL
+    , testProperty "break"       prop_breakBL
+    , testProperty "span"        prop_spanBL
+    , testProperty "group"       prop_groupBL
+    , testProperty "groupBy"     prop_groupByBL
+    , testProperty "inits"       prop_initsBL
+    , testProperty "tails"       prop_tailsBL
+    , testProperty "elem"        prop_elemBL
+    , testProperty "notElem"     prop_notElemBL
+    , testProperty "lines"       prop_linesBL
+    , testProperty "elemIndex"   prop_elemIndexBL
+    , testProperty "elemIndexEnd"prop_elemIndexEndBL
+    , testProperty "elemIndices" prop_elemIndicesBL
+    , testProperty "concatMap"   prop_concatMapBL
+    , testProperty "zipWith/packZipWithLazy" prop_packZipWithBL
+    ]
+
+------------------------------------------------------------------------
+-- ByteString.Lazy <=> ByteString
+
+cc_tests =
+    [ testProperty "prop_concatCC"      prop_concatCC
+    , testProperty "prop_nullCC"        prop_nullCC
+    , testProperty "prop_reverseCC"     prop_reverseCC
+    , testProperty "prop_transposeCC"   prop_transposeCC
+    , testProperty "prop_groupCC"       prop_groupCC
+    , testProperty "prop_groupByCC"     prop_groupByCC
+    , testProperty "prop_initsCC"       prop_initsCC
+    , testProperty "prop_tailsCC"       prop_tailsCC
+    , testProperty "prop_allCC"         prop_allCC
+    , testProperty "prop_anyCC"         prop_anyCC
+    , testProperty "prop_appendCC"      prop_appendCC
+    , testProperty "prop_breakCC"       prop_breakCC
+    , testProperty "prop_concatMapCC"   prop_concatMapCC
+    , testProperty "prop_consCC"        prop_consCC
+    , testProperty "prop_consCC'"       prop_consCC'
+    , testProperty "prop_unconsCC"      prop_unconsCC
+    , testProperty "prop_unsnocCC"      prop_unsnocCC
+    , testProperty "prop_countCC"       prop_countCC
+    , testProperty "prop_dropCC"        prop_dropCC
+    , testProperty "prop_dropWhileCC"   prop_dropWhileCC
+    , testProperty "prop_filterCC"      prop_filterCC
+    , testProperty "prop_findCC"        prop_findCC
+    , testProperty "prop_findIndexCC"   prop_findIndexCC
+    , testProperty "prop_findIndexEndCC" prop_findIndexEndCC
+    , testProperty "prop_findIndicesCC" prop_findIndicesCC
+    , testProperty "prop_isPrefixCC"    prop_isPrefixOfCC
+    , testProperty "prop_isSuffixCC"    prop_isSuffixOfCC
+    , testProperty "prop_stripPrefixCC" prop_stripPrefixCC
+    , testProperty "prop_stripSuffixCC" prop_stripSuffixCC
+    , testProperty "prop_mapCC"         prop_mapCC
+    , testProperty "prop_replicateCC"   prop_replicateCC
+    , testProperty "prop_snocCC"        prop_snocCC
+    , testProperty "prop_spanCC"        prop_spanCC
+    , testProperty "prop_splitCC"       prop_splitCC
+    , testProperty "prop_splitAtCC"     prop_splitAtCC
+    , testProperty "prop_takeCC"        prop_takeCC
+    , testProperty "prop_takeWhileCC"   prop_takeWhileCC
+    , testProperty "prop_elemCC"        prop_elemCC
+    , testProperty "prop_notElemCC"     prop_notElemCC
+    , testProperty "prop_elemIndexCC"   prop_elemIndexCC
+    , testProperty "prop_elemIndicesCC" prop_elemIndicesCC
+    , testProperty "prop_lengthCC"      prop_lengthCC
+    , testProperty "prop_headCC"        prop_headCC
+    , testProperty "prop_initCC"        prop_initCC
+    , testProperty "prop_lastCC"        prop_lastCC
+    , testProperty "prop_maximumCC"     prop_maximumCC
+    , testProperty "prop_minimumCC"     prop_minimumCC
+    , testProperty "prop_tailCC"        prop_tailCC
+    , testProperty "prop_foldl1CC"      prop_foldl1CC
+    , testProperty "prop_foldl1CC'"     prop_foldl1CC'
+    , testProperty "prop_foldr1CC"      prop_foldr1CC
+    , testProperty "prop_foldr1CC'"     prop_foldr1CC'
+    , testProperty "prop_scanlCC"       prop_scanlCC
+    , testProperty "prop_intersperseCC" prop_intersperseCC
+
+    , testProperty "prop_foldlCC"       prop_foldlCC
+    , testProperty "prop_foldlCC'"      prop_foldlCC'
+    , testProperty "prop_foldrCC"       prop_foldrCC
+    , testProperty "prop_foldrCC'"      prop_foldrCC'
+    , testProperty "prop_mapAccumLCC"   prop_mapAccumLCC
+--    , testProperty "prop_mapIndexedCC" prop_mapIndexedCC
+--    , testProperty "prop_mapIndexedPL" prop_mapIndexedPL
+    ]
+
+bp_tests =
+    [ testProperty "all"         prop_allBP
+    , testProperty "any"         prop_anyBP
+    , testProperty "append"      prop_appendBP
+    , testProperty "compare"     prop_compareBP
+    , testProperty "concat"      prop_concatBP
+    , testProperty "cons"        prop_consBP
+    , testProperty "cons'"       prop_consBP'
+    , testProperty "uncons"      prop_unconsBP
+    , testProperty "unsnoc"      prop_unsnocBP
+    , testProperty "eq"          prop_eqBP
+    , testProperty "filter"      prop_filterBP
+    , testProperty "find"        prop_findBP
+    , testProperty "findIndex"   prop_findIndexBP
+    , testProperty "findIndexEnd"prop_findIndexEndBP
+    , testProperty "findIndices" prop_findIndicesBP
+    , testProperty "foldl"       prop_foldlBP
+    , testProperty "foldl'"      prop_foldlBP'
+    , testProperty "foldl1"      prop_foldl1BP
+    , testProperty "foldl1'"     prop_foldl1BP'
+    , testProperty "foldr"       prop_foldrBP
+    , testProperty "foldr'"      prop_foldrBP'
+    , testProperty "foldr1"      prop_foldr1BP
+    , testProperty "foldr1'"     prop_foldr1BP'
+    , testProperty "mapAccumL"   prop_mapAccumLBP
+--  , testProperty "mapAccumL"   prop_mapAccumL_mapIndexedBP
+    , testProperty "unfoldr"     prop_unfoldrBP
+    , testProperty "unfoldr 2"   prop_unfoldr2BP
+    , testProperty "unfoldr 2"   prop_unfoldr2CP
+    , testProperty "head"        prop_headBP
+    , testProperty "init"        prop_initBP
+    , testProperty "isPrefixOf"  prop_isPrefixOfBP
+    , testProperty "isSuffixOf"  prop_isSuffixOfBP
+    , testProperty "stripPrefix" prop_stripPrefixBP
+    , testProperty "stripSuffix" prop_stripSuffixBP
+    , testProperty "last"        prop_lastBP
+    , testProperty "length"      prop_lengthBP
+    , testProperty "readInt"     prop_readIntBP
+    , testProperty "lines"       prop_linesBP
+    , testProperty "lines \\n"   prop_linesNLBP
+    , testProperty "map"         prop_mapBP
+    , testProperty "maximum   "  prop_maximumBP
+    , testProperty "minimum"     prop_minimumBP
+    , testProperty "null"        prop_nullBP
+    , testProperty "reverse"     prop_reverseBP
+    , testProperty "snoc"        prop_snocBP
+    , testProperty "tail"        prop_tailBP
+    , testProperty "scanl"       prop_scanlBP
+    , testProperty "transpose"   prop_transposeBP
+    , testProperty "replicate"   prop_replicateBP
+    , testProperty "take"        prop_takeBP
+    , testProperty "drop"        prop_dropBP
+    , testProperty "splitAt"     prop_splitAtBP
+    , testProperty "takeWhile"   prop_takeWhileBP
+    , testProperty "dropWhile"   prop_dropWhileBP
+    , testProperty "break"       prop_breakBP
+    , testProperty "span"        prop_spanBP
+    , testProperty "split"       prop_splitBP
+    , testProperty "count"       prop_countBP
+    , testProperty "group"       prop_groupBP
+    , testProperty "groupBy"     prop_groupByBP
+    , testProperty "inits"       prop_initsBP
+    , testProperty "tails"       prop_tailsBP
+    , testProperty "elem"        prop_elemBP
+    , testProperty "notElem"     prop_notElemBP
+    , testProperty "elemIndex"   prop_elemIndexBP
+    , testProperty "elemIndexEnd"prop_elemIndexEndBP
+    , testProperty "elemIndices" prop_elemIndicesBP
+    , testProperty "intersperse" prop_intersperseBP
+    , testProperty "concatMap"   prop_concatMapBP
+    ]
+
+------------------------------------------------------------------------
+-- ByteString <=> List
+
+pl_tests =
+    [ testProperty "all"         prop_allPL
+    , testProperty "any"         prop_anyPL
+    , testProperty "append"      prop_appendPL
+    , testProperty "compare"     prop_comparePL
+    , testProperty "concat"      prop_concatPL
+    , testProperty "cons"        prop_consPL
+    , testProperty "eq"          prop_eqPL
+    , testProperty "filter"      prop_filterPL
+    , testProperty "filter rules"prop_filterPL_rule
+    , testProperty "filter rules"prop_filterLC_rule
+    , testProperty "partition"   prop_partitionPL
+    , testProperty "partition"   prop_partitionLL
+    , testProperty "find"        prop_findPL
+    , testProperty "findIndex"   prop_findIndexPL
+    , testProperty "findIndexEnd"prop_findIndexEndPL
+    , testProperty "findIndices" prop_findIndicesPL
+    , testProperty "foldl"       prop_foldlPL
+    , testProperty "foldl'"      prop_foldlPL'
+    , testProperty "foldl1"      prop_foldl1PL
+    , testProperty "foldl1'"     prop_foldl1PL'
+    , testProperty "foldr1"      prop_foldr1PL
+    , testProperty "foldr"       prop_foldrPL
+    , testProperty "mapAccumL"   prop_mapAccumLPL
+    , testProperty "mapAccumR"   prop_mapAccumRPL
+    , testProperty "unfoldr"     prop_unfoldrPL
+    , testProperty "scanl"       prop_scanlPL
+    , testProperty "scanl1"      prop_scanl1PL
+    , testProperty "scanl1"      prop_scanl1CL
+    , testProperty "scanr"       prop_scanrCL
+    , testProperty "scanr"       prop_scanrPL
+    , testProperty "scanr1"      prop_scanr1PL
+    , testProperty "scanr1"      prop_scanr1CL
+    , testProperty "head"        prop_headPL
+    , testProperty "init"        prop_initPL
+    , testProperty "last"        prop_lastPL
+    , testProperty "maximum"     prop_maximumPL
+    , testProperty "minimum"     prop_minimumPL
+    , testProperty "tail"        prop_tailPL
+    , testProperty "zip"         prop_zipPL
+    , testProperty "zip"         prop_zipLL
+    , testProperty "zip"         prop_zipCL
+    , testProperty "unzip"       prop_unzipPL
+    , testProperty "unzip"       prop_unzipLL
+    , testProperty "unzip"       prop_unzipCL
+    , testProperty "unzip"       prop_unzipDL
+    , testProperty "zipWithPL"          prop_zipWithPL
+    , testProperty "zipWithPL rules"   prop_zipWithPL_rules
+    , testProperty "packZipWithPL" prop_packZipWithPL
+
+    , testProperty "isPrefixOf"  prop_isPrefixOfPL
+    , testProperty "isSuffixOf"  prop_isSuffixOfPL
+    , testProperty "isInfixOf"   prop_isInfixOfPL
+    , testProperty "stripPrefix" prop_stripPrefixPL
+    , testProperty "stripSuffix" prop_stripSuffixPL
+    , testProperty "length"      prop_lengthPL
+    , testProperty "map"         prop_mapPL
+    , testProperty "null"        prop_nullPL
+    , testProperty "reverse"     prop_reversePL
+    , testProperty "snoc"        prop_snocPL
+    , testProperty "transpose"   prop_transposePL
+    , testProperty "replicate"   prop_replicatePL
+    , testProperty "take"        prop_takePL
+    , testProperty "drop"        prop_dropPL
+    , testProperty "splitAt"     prop_splitAtPL
+    , testProperty "takeWhile"   prop_takeWhilePL
+    , testProperty "dropWhile"   prop_dropWhilePL
+    , testProperty "break"       prop_breakPL
+    , testProperty "span"        prop_spanPL
+    , testProperty "group"       prop_groupPL
+    , testProperty "groupBy"     prop_groupByPL
+    , testProperty "inits"       prop_initsPL
+    , testProperty "tails"       prop_tailsPL
+    , testProperty "elem"        prop_elemPL
+    , testProperty "notElem"     prop_notElemPL
+    , testProperty "lines"       prop_linesPL
+    , testProperty "elemIndex"   prop_elemIndexPL
+    , testProperty "elemIndex"   prop_elemIndexCL
+    , testProperty "elemIndices" prop_elemIndicesPL
+    , testProperty "concatMap"   prop_concatMapPL
+    , testProperty "IsString"    prop_isstring
+    , testProperty "IsString LC" prop_isstring_lc
+    ]
+
+------------------------------------------------------------------------
+-- extra ByteString properties
+
+bb_tests =
+    [ testProperty "bijection"      prop_bijectionBB
+    , testProperty "bijection'"     prop_bijectionBB'
+    , testProperty "pack/unpack"    prop_packunpackBB
+    , testProperty "unpack/pack"    prop_packunpackBB'
+    , testProperty "eq 1"           prop_eq1BB
+    , testProperty "eq 2"           prop_eq2BB
+    , testProperty "eq 3"           prop_eq3BB
+    , testProperty "compare 1"      prop_compare1BB
+    , testProperty "compare 2"      prop_compare2BB
+    , testProperty "compare 3"      prop_compare3BB
+    , testProperty "compare 4"      prop_compare4BB
+    , testProperty "compare 5"      prop_compare5BB
+    , testProperty "compare 6"      prop_compare6BB
+    , testProperty "compare 7"      prop_compare7BB
+    , testProperty "compare 7"      prop_compare7LL
+    , testProperty "compare 8"      prop_compare8BB
+    , testProperty "empty 1"        prop_nil1BB
+    , testProperty "empty 2"        prop_nil2BB
+    , testProperty "empty 1 monoid" prop_nil1LL_monoid
+    , testProperty "empty 2 monoid" prop_nil2LL_monoid
+    , testProperty "empty 1 monoid" prop_nil1BB_monoid
+    , testProperty "empty 2 monoid" prop_nil2BB_monoid
+
+    , testProperty "null"           prop_nullBB
+    , testProperty "length 1"       prop_lengthBB
+    , testProperty "length 2"       prop_lengthSBB
+    , testProperty "cons 1"         prop_consBB
+    , testProperty "cons 2"         prop_cons1BB
+    , testProperty "cons 3"         prop_cons2BB
+    , testProperty "cons 4"         prop_cons3BB
+    , testProperty "cons 5"         prop_cons4BB
+    , testProperty "snoc"           prop_snoc1BB
+    , testProperty "head 1"         prop_head1BB
+    , testProperty "head 2"         prop_head2BB
+    , testProperty "head 3"         prop_head3BB
+    , testProperty "tail"           prop_tailBB
+    , testProperty "tail 1"         prop_tail1BB
+    , testProperty "last"           prop_lastBB
+    , testProperty "last 1"         prop_last1BB
+    , testProperty "init"           prop_initBB
+    , testProperty "init 1"         prop_init1BB
+    , testProperty "append 1"       prop_append1BB
+    , testProperty "append 2"       prop_append2BB
+    , testProperty "append 3"       prop_append3BB
+    , testProperty "mappend 1"      prop_append1BB_monoid
+    , testProperty "mappend 2"      prop_append2BB_monoid
+    , testProperty "mappend 3"      prop_append3BB_monoid
+
+    , testProperty "map 1"          prop_map1BB
+    , testProperty "map 2"          prop_map2BB
+    , testProperty "map 3"          prop_map3BB
+    , testProperty "filter1"        prop_filter1BB
+    , testProperty "filter2"        prop_filter2BB
+    , testProperty "map fusion"     prop_mapfusionBB
+    , testProperty "filter fusion"  prop_filterfusionBB
+    , testProperty "reverse 1"      prop_reverse1BB
+    , testProperty "reverse 2"      prop_reverse2BB
+    , testProperty "reverse 3"      prop_reverse3BB
+    , testProperty "foldl 1"        prop_foldl1BB
+    , testProperty "foldl 2"        prop_foldl2BB
+    , testProperty "foldr 1"        prop_foldr1BB
+    , testProperty "foldr 2"        prop_foldr2BB
+    , testProperty "foldl1 1"       prop_foldl1_1BB
+    , testProperty "foldl1 2"       prop_foldl1_2BB
+    , testProperty "foldl1 3"       prop_foldl1_3BB
+    , testProperty "foldr1 1"       prop_foldr1_1BB
+    , testProperty "foldr1 2"       prop_foldr1_2BB
+    , testProperty "foldr1 3"       prop_foldr1_3BB
+    , testProperty "scanl/foldl"    prop_scanlfoldlBB
+    , testProperty "all"            prop_allBB
+    , testProperty "any"            prop_anyBB
+    , testProperty "take"           prop_takeBB
+    , testProperty "drop"           prop_dropBB
+    , testProperty "takeWhile_ne"   prop_takeWhileBB_ne
+    , testProperty "takeWhile_eq"   prop_takeWhileBB_eq
+    , testProperty "dropWhile_ne"   prop_dropWhileBB_ne
+    , testProperty "dropWhile_eq"   prop_dropWhileBB_eq
+    , testProperty "dropWhile_isSpace" prop_dropWhileCC_isSpace
+    , testProperty "splitAt"        prop_splitAtBB
+    , testProperty "span"           prop_spanBB
+    , testProperty "break"          prop_breakBB
+    , testProperty "elem"           prop_elemBB
+    , testProperty "notElem"        prop_notElemBB
+
+    , testProperty "concat 1"       prop_concat1BB
+    , testProperty "concat 2"       prop_concat2BB
+    , testProperty "concat 3"       prop_concatBB
+    , testProperty "mconcat 1"      prop_concat1BB_monoid
+    , testProperty "mconcat 2"      prop_concat2BB_monoid
+    , testProperty "mconcat 3"      prop_concatBB_monoid
+
+    , testProperty "mconcat 1"      prop_concat1LL_monoid
+    , testProperty "mconcat 2"      prop_concat2LL_monoid
+    , testProperty "mconcat 3"      prop_concatLL_monoid
+
+    , testProperty "lines"          prop_linesBB
+    , testProperty "unlines"        prop_unlinesBB
+    , testProperty "unlines"        prop_unlinesLC
+    , testProperty "lines_lazy1"    prop_lines_lazy1
+    , testProperty "lines_lazy2"    prop_lines_lazy2
+    , testProperty "words"          prop_wordsBB
+    , testProperty "words"          prop_wordsLC
+    , testProperty "unwords"        prop_unwordsBB
+    , testProperty "group"          prop_groupBB
+    , testProperty "groupBy 0"      prop_groupByBB
+    , testProperty "groupBy 1"      prop_groupBy1CC
+    , testProperty "groupBy 2"      prop_groupBy1BB
+    , testProperty "groupBy 3"      prop_groupBy2CC
+    , testProperty "join"           prop_joinBB
+    , testProperty "elemIndex 1"    prop_elemIndex1BB
+    , testProperty "elemIndex 2"    prop_elemIndex2BB
+    , testProperty "findIndex"      prop_findIndexBB
+    , testProperty "findIndexEnd"   prop_findIndexEndBB
+    , testProperty "findIndexEnd"   prop_findIndexEndLL
+    , testProperty "findIndexEnd"   prop_findIndexEndDD
+    , testProperty "findIndicies"   prop_findIndiciesBB
+    , testProperty "elemIndices"    prop_elemIndicesBB
+    , testProperty "find"           prop_findBB
+    , testProperty "find/findIndex" prop_find_findIndexBB
+    , testProperty "sort 1"         prop_sort1BB
+    , testProperty "sort 2"         prop_sort2BB
+    , testProperty "sort 3"         prop_sort3BB
+    , testProperty "sort 4"         prop_sort4BB
+    , testProperty "sort 5"         prop_sort5BB
+    , testProperty "intersperse"    prop_intersperseBB
+    , testProperty "maximum"        prop_maximumBB
+    , testProperty "minimum"        prop_minimumBB
+    , testProperty "strip"          prop_strip
+--  , testProperty "breakChar"      prop_breakCharBB
+--  , testProperty "spanChar 1"     prop_spanCharBB
+--  , testProperty "spanChar 2"     prop_spanChar_1BB
+--  , testProperty "breakSpace"     prop_breakSpaceBB
+--  , testProperty "dropSpace"      prop_dropSpaceBB
+    , testProperty "spanEnd"        prop_spanEndBB
+    , testProperty "breakEnd"       prop_breakEndBB
+    , testProperty "breakEnd"       prop_breakEndCC
+    , testProperty "elemIndexEnd"   prop_elemIndexEnd1BB
+    , testProperty "elemIndexEnd"   prop_elemIndexEnd1CC
+    , testProperty "elemIndexEnd"   prop_elemIndexEnd1LL
+    , testProperty "elemIndexEnd"   prop_elemIndexEnd1DD
+--  , testProperty "words'"         prop_wordsBB'
+--  , testProperty "lines'"         prop_linesBB'
+--  , testProperty "dropSpaceEnd"   prop_dropSpaceEndBB
+    , testProperty "unfoldr"        prop_unfoldrBB
+    , testProperty "prefix"         prop_prefixBB
+    , testProperty "prefix"         prop_prefixLL
+    , testProperty "suffix"         prop_suffixBB
+    , testProperty "suffix"         prop_suffixLL
+    , testProperty "stripPrefix"    prop_stripPrefixBB
+    , testProperty "stripPrefix"    prop_stripPrefixLL
+    , testProperty "stripSuffix"    prop_stripSuffixBB
+    , testProperty "stripSuffix"    prop_stripSuffixLL
+    , testProperty "copy"           prop_copyBB
+    , testProperty "copy"           prop_copyLL
+    , testProperty "inits"          prop_initsBB
+    , testProperty "tails"          prop_tailsBB
+    , testProperty "breakSubstring 1"prop_breakSubstringBB
+    , testProperty "breakSubstring 3"prop_breakSubstring_isInfixOf
+
+    , testProperty "replicate1"     prop_replicate1BB
+    , testProperty "replicate2"     prop_replicate2BB
+    , testProperty "replicate3"     prop_replicate3BB
+    , testProperty "readInt"        prop_readintBB
+    , testProperty "readInt 2"      prop_readint2BB
+    , testProperty "readInteger"    prop_readintegerBB
+    , testProperty "readInteger 2"  prop_readinteger2BB
+    , testProperty "read"           prop_readLL
+    , testProperty "read"           prop_readBB
+    , testProperty "Lazy.readInt"   prop_readintLL
+    , testProperty "Lazy.readInt"   prop_readintLL
+    , testProperty "Lazy.readInteger" prop_readintegerLL
+
+    , testProperty "readIntSafe"       prop_readIntSafe
+    , testProperty "readIntUnsafe"     prop_readIntUnsafe
+    , testProperty "readIntegerSafe"   prop_readIntegerSafe
+    , testProperty "readIntegerUnsafe" prop_readIntegerUnsafe
+
+    , testProperty "mconcat 1"      prop_append1LL_monoid
+    , testProperty "mconcat 2"      prop_append2LL_monoid
+    , testProperty "mconcat 3"      prop_append3LL_monoid
+--  , testProperty "filterChar1"    prop_filterChar1BB
+--  , testProperty "filterChar2"    prop_filterChar2BB
+--  , testProperty "filterChar3"    prop_filterChar3BB
+--  , testProperty "filterNotChar1" prop_filterNotChar1BB
+--  , testProperty "filterNotChar2" prop_filterNotChar2BB
+    , testProperty "tail"           prop_tailSBB
+    , testProperty "index"          prop_indexBB
+    , testProperty "unsafeIndex"    prop_unsafeIndexBB
+--  , testProperty "map'"           prop_mapBB'
+    , testProperty "filter"         prop_filterBB
+    , testProperty "elem"           prop_elemSBB
+    , testProperty "take"           prop_takeSBB
+    , testProperty "drop"           prop_dropSBB
+    , testProperty "splitAt"        prop_splitAtSBB
+    , testProperty "foldl"          prop_foldlBB
+    , testProperty "foldr"          prop_foldrBB
+    , testProperty "takeWhile "     prop_takeWhileSBB
+    , testProperty "dropWhile "     prop_dropWhileSBB
+    , testProperty "span "          prop_spanSBB
+    , testProperty "break "         prop_breakSBB
+    , testProperty "breakspan"      prop_breakspan_1BB
+    , testProperty "lines "         prop_linesSBB
+    , testProperty "unlines "       prop_unlinesSBB
+    , testProperty "words "         prop_wordsSBB
+    , testProperty "unwords "       prop_unwordsSBB
+    , testProperty "unwords "       prop_unwordsSLC
+--     , testProperty "wordstokens"    prop_wordstokensBB
+    , testProperty "splitWith_empty" prop_splitWithBB_empty
+    , testProperty "splitWith"      prop_splitWithBB
+    , testProperty "split_empty"    prop_splitBB_empty
+    , testProperty "joinsplit"      prop_joinsplitBB
+    , testProperty "intercalate"    prop_intercalatePL
+--     , testProperty "lineIndices"    prop_lineIndices1BB
+    , testProperty "count"          prop_countBB
+--  , testProperty "linessplit"     prop_linessplit2BB
+    , testProperty "splitsplitWith" prop_splitsplitWithBB
+--  , testProperty "joinjoinpath"   prop_joinjoinpathBB
+    , testProperty "zip"            prop_zipBB
+    , testProperty "zip"            prop_zipLC
+    , testProperty "zip1"           prop_zip1BB
+    , testProperty "zipWithBB"        prop_zipWithBB
+    , testProperty "zipWithCC"        prop_zipWithCC
+    , testProperty "zipWithLC"        prop_zipWithLC
+    , testProperty "packZipWithBB"    prop_packZipWithBB
+    , testProperty "packZipWithLL"    prop_packZipWithLL
+    , testProperty "packZipWithBC"    prop_packZipWithBC
+    , testProperty "packZipWithLC"    prop_packZipWithLC
+    , testProperty "unzip"          prop_unzipBB
+    , testProperty "concatMap"      prop_concatMapBB
+--  , testProperty "join/joinByte"  prop_join_spec
+#if MIN_VERSION_base(4,9,0)
+    , testProperty "stimes strict"  prop_stimesBB
+    , testProperty "stimes lazy"    prop_stimesLL
+#endif
+    ]
+
+
+------------------------------------------------------------------------
+-- Extra lazy properties
+
+ll_tests =
+    [ testProperty "eq 1"               prop_eq1
+    , testProperty "eq 2"               prop_eq2
+    , testProperty "eq 3"               prop_eq3
+    , testProperty "eq refl"            prop_eq_refl
+    , testProperty "eq symm"            prop_eq_symm
+    , testProperty "compare 1"          prop_compare1
+    , testProperty "compare 2"          prop_compare2
+    , testProperty "compare 3"          prop_compare3
+    , testProperty "compare 4"          prop_compare4
+    , testProperty "compare 5"          prop_compare5
+    , testProperty "compare 6"          prop_compare6
+    , testProperty "compare 7"          prop_compare7
+    , testProperty "compare 8"          prop_compare8
+    , testProperty "compare 9"          prop_compare9
+    , testProperty "empty 1"            prop_empty1
+    , testProperty "empty 2"            prop_empty2
+    , testProperty "pack/unpack"        prop_packunpack
+    , testProperty "unpack/pack"        prop_unpackpack
+    , testProperty "null"               prop_null
+    , testProperty "length 1"           prop_length1
+    , testProperty "length 2"           prop_length2
+    , testProperty "cons 1"             prop_cons1
+    , testProperty "cons 2"             prop_cons2
+    , testProperty "cons 3"             prop_cons3
+    , testProperty "cons 4"             prop_cons4
+    , testProperty "snoc"               prop_snoc1
+    , testProperty "head/pack"          prop_head
+    , testProperty "head/unpack"        prop_head1
+    , testProperty "tail/pack"          prop_tail
+    , testProperty "tail/unpack"        prop_tail1
+    , testProperty "last"               prop_last
+    , testProperty "init"               prop_init
+    , testProperty "append 1"           prop_append1
+    , testProperty "appendLazy"         prop_appendLazy
+    , testProperty "append 2"           prop_append2
+    , testProperty "append 3"           prop_append3
+    , testProperty "map 1"              prop_map1
+    , testProperty "map 2"              prop_map2
+    , testProperty "map 3"              prop_map3
+    , testProperty "filter 1"           prop_filter1
+    , testProperty "filter 2"           prop_filter2
+    , testProperty "reverse"            prop_reverse
+    , testProperty "reverse1"           prop_reverse1
+    , testProperty "reverse2"           prop_reverse2
+    , testProperty "transpose"          prop_transpose
+    , testProperty "foldl"              prop_foldl
+    , testProperty "foldl/reverse"      prop_foldl_1
+    , testProperty "foldr"              prop_foldr
+    , testProperty "foldr/id"           prop_foldr_1
+    , testProperty "foldl1/foldl"       prop_foldl1_1
+    , testProperty "foldl1/head"        prop_foldl1_2
+    , testProperty "foldl1/tail"        prop_foldl1_3
+    , testProperty "foldr1/foldr"       prop_foldr1_1
+    , testProperty "foldr1/last"        prop_foldr1_2
+    , testProperty "foldr1/head"        prop_foldr1_3
+    , testProperty "concat 1"           prop_concat1
+    , testProperty "concat 2"           prop_concat2
+    , testProperty "concat/pack"        prop_concat3
+    , testProperty "any"                prop_any
+    , testProperty "all"                prop_all
+    , testProperty "maximum"            prop_maximum
+    , testProperty "minimum"            prop_minimum
+    , testProperty "compareLength 1"    prop_compareLength1
+    , testProperty "compareLength 2"    prop_compareLength2
+    , testProperty "compareLength 3"    prop_compareLength3
+    , testProperty "compareLength 4"    prop_compareLength4
+    , testProperty "compareLength 5"    prop_compareLength5
+    , testProperty "replicate 1"        prop_replicate1
+    , testProperty "replicate 2"        prop_replicate2
+    , testProperty "take"               prop_take1
+    , testProperty "takeEnd"            prop_takeEnd
+    , testProperty "drop"               prop_drop1
+    , testProperty "dropEnd"            prop_dropEnd
+    , testProperty "splitAt"            prop_drop1
+    , testProperty "takeWhile"          prop_takeWhile
+    , testProperty "dropWhile"          prop_dropWhile
+    , testProperty "takeWhileEnd"       prop_takeWhileEnd
+    , testProperty "dropWhileEnd"       prop_dropWhileEnd
+    , testProperty "break"              prop_break
+    , testProperty "span"               prop_span
+    , testProperty "splitAt"            prop_splitAt
+    , testProperty "break/span"         prop_breakspan
+    , testProperty "split"              prop_split
+    , testProperty "splitWith_empty"    prop_splitWith_empty
+    , testProperty "splitWith"          prop_splitWith
+    , testProperty "splitWith_empty"    prop_splitWith_D_empty
+    , testProperty "splitWith"          prop_splitWith_D
+    , testProperty "splitWith_empty"    prop_splitWith_C_empty
+    , testProperty "splitWith"          prop_splitWith_C
+    , testProperty "split_empty"        prop_split_empty
+    , testProperty "join.split/id"      prop_joinsplit
+--  , testProperty "join/joinByte"      prop_joinjoinByte
+    , testProperty "group"              prop_group
+    , testProperty "groupBy"            prop_groupBy
+    , testProperty "groupBy"            prop_groupBy_LC
+    , testProperty "index"              prop_index
+    , testProperty "index"              prop_index_D
+    , testProperty "index"              prop_index_C
+    , testProperty "indexMaybe"         prop_indexMaybe_Just_P
+    , testProperty "indexMaybe"         prop_indexMaybe_Just_L
+    , testProperty "indexMaybe"         prop_indexMaybe_Nothing_P
+    , testProperty "indexMaybe"         prop_indexMaybe_Nothing_L
+    , testProperty "elemIndex"          prop_elemIndex
+    , testProperty "elemIndices"        prop_elemIndices
+    , testProperty "count/elemIndices"  prop_count
+    , testProperty "findIndex"          prop_findIndex
+    , testProperty "findIndexEnd"       prop_findIndexEnd
+    , testProperty "findIndices"        prop_findIndicies
+    , testProperty "find"               prop_find
+    , testProperty "find/findIndex"     prop_find_findIndex
+    , testProperty "elem"               prop_elem
+    , testProperty "notElem"            prop_notElem
+    , testProperty "elem/notElem"       prop_elem_notelem
+--  , testProperty "filterByte 1"       prop_filterByte
+--  , testProperty "filterByte 2"       prop_filterByte2
+--  , testProperty "filterNotByte 1"    prop_filterNotByte
+--  , testProperty "filterNotByte 2"    prop_filterNotByte2
+    , testProperty "isPrefixOf"         prop_isPrefixOf
+    , testProperty "isSuffixOf"         prop_isSuffixOf
+    , testProperty "stripPrefix"        prop_stripPrefix
+    , testProperty "stripSuffix"        prop_stripSuffix
+    , testProperty "concatMap"          prop_concatMap
+    , testProperty "isSpace"            prop_isSpaceWord8
+    ]
+
+findIndexEnd :: (a -> Bool) -> [a] -> Maybe Int
+findIndexEnd p = go . findIndices p
+  where
+    go [] = Nothing
+    go (k:[]) = Just k
+    go (k:ks) = go ks
+
+elemIndexEnd :: Eq a => a -> [a] -> Maybe Int
+elemIndexEnd = findIndexEnd . (==)
+
+removeFile :: String -> IO ()
+removeFile fn = void $ withCString fn c_unlink
diff --git a/tests/QuickCheckUtils.hs b/tests/QuickCheckUtils.hs
new file mode 100644
--- /dev/null
+++ b/tests/QuickCheckUtils.hs
@@ -0,0 +1,197 @@
+{-# LANGUAGE CPP, MultiParamTypeClasses,
+             FlexibleInstances, FlexibleContexts,
+             TypeSynonymInstances #-}
+--
+-- Uses multi-param type classes
+--
+module QuickCheckUtils where
+
+import Test.Tasty.QuickCheck
+import Text.Show.Functions
+
+import Control.Monad        ( liftM2 )
+import Data.Char
+import Data.List
+import Data.Word
+import Data.Int
+import System.IO
+import Foreign.C (CChar)
+
+import qualified Data.ByteString      as P
+import qualified Data.ByteString.Lazy as L
+import qualified Data.ByteString.Lazy.Internal as L (checkInvariant,ByteString(..))
+
+import qualified Data.ByteString.Char8      as PC
+import qualified Data.ByteString.Lazy.Char8 as LC
+
+------------------------------------------------------------------------
+
+sizedByteString n = do m <- choose(0, n)
+                       fmap P.pack $ vectorOf m arbitrary
+
+instance Arbitrary P.ByteString where
+  arbitrary = do
+    bs <- sized sizedByteString
+    n  <- choose (0, 2)
+    return (P.drop n bs) -- to give us some with non-0 offset
+
+instance CoArbitrary P.ByteString where
+  coarbitrary s = coarbitrary (P.unpack s)
+
+instance Arbitrary L.ByteString where
+  arbitrary = sized $ \n -> do numChunks <- choose (0, n)
+                               if numChunks == 0
+                                   then return L.empty
+                                   else fmap (L.checkInvariant .
+                                              L.fromChunks .
+                                              filter (not . P.null)) $
+                                            vectorOf numChunks
+                                                     (sizedByteString
+                                                          (n `div` numChunks))
+
+instance CoArbitrary L.ByteString where
+  coarbitrary s = coarbitrary (L.unpack s)
+
+newtype CByteString = CByteString P.ByteString
+  deriving Show
+
+instance Arbitrary CByteString where
+  arbitrary = fmap (CByteString . P.pack . map fromCChar)
+                   arbitrary
+    where
+      fromCChar :: NonZero CChar -> Word8
+      fromCChar = fromIntegral . getNonZero
+
+-- | 'Char', but only representing 8-bit characters.
+--
+newtype Char8 = Char8 Char
+  deriving (Eq, Ord, Show)
+
+instance Arbitrary Char8 where
+  arbitrary = fmap (Char8 . toChar) arbitrary
+    where
+      toChar :: Word8 -> Char
+      toChar = toEnum . fromIntegral
+
+instance CoArbitrary Char8 where
+  coarbitrary (Char8 c) = coarbitrary c
+
+-- | 'Char', but only representing 8-bit characters.
+--
+newtype String8 = String8 String
+  deriving (Eq, Ord, Show)
+
+instance Arbitrary String8 where
+  arbitrary = fmap (String8 . map toChar) arbitrary
+    where
+      toChar :: Word8 -> Char
+      toChar = toEnum . fromIntegral
+
+------------------------------------------------------------------------
+--
+-- We're doing two forms of testing here. Firstly, model based testing.
+-- For our Lazy and strict bytestring types, we have model types:
+--
+--  i.e.    Lazy    ==   Byte
+--              \\      //
+--                 List
+--
+-- That is, the Lazy type can be modeled by functions in both the Byte
+-- and List type. For each of the 3 models, we have a set of tests that
+-- check those types match.
+--
+-- The Model class connects a type and its model type, via a conversion
+-- function.
+--
+--
+class Model a b where
+  model :: a -> b  -- ^ Get the abstract value from a concrete value
+
+-- | Alias for 'model' that's a better name in the situations where we're
+-- really just converting functions that take or return Char8.
+castFn :: Model a b => a -> b
+castFn = model
+
+--
+-- Connecting our Lazy and Strict types to their models. We also check
+-- the data invariant on Lazy types.
+--
+-- These instances represent the arrows in the above diagram
+--
+instance Model B P      where model = abstr . checkInvariant
+instance Model P [W]    where model = P.unpack
+instance Model P [Char] where model = PC.unpack
+instance Model B [W]    where model = L.unpack  . checkInvariant
+instance Model B [Char] where model = LC.unpack . checkInvariant
+instance Model Char8 Char where model (Char8 c) = c
+
+-- Types are trivially modeled by themselves
+instance Model Bool  Bool         where model = id
+instance Model Int   Int          where model = id
+instance Model P     P            where model = id
+instance Model B     B            where model = id
+instance Model Int64 Int64        where model = id
+instance Model Word8 Word8        where model = id
+instance Model Ordering Ordering  where model = id
+instance Model Char Char  where model = id
+
+-- More structured types are modeled recursively, using the NatTrans class from Gofer.
+class (Functor f, Functor g) => NatTrans f g where
+    eta :: f a -> g a
+
+-- The transformation of the same type is identity
+instance NatTrans [] []             where eta = id
+instance NatTrans Maybe Maybe       where eta = id
+instance NatTrans ((->) X) ((->) X) where eta = id
+instance NatTrans ((->) Char) ((->) Char) where eta = id
+instance NatTrans ((->) Char8) ((->) Char) where eta f = f . Char8
+
+instance NatTrans ((->) W) ((->) W) where eta = id
+
+-- We have a transformation of pairs, if the pairs are in Model
+instance Model f g => NatTrans ((,) f) ((,) g) where eta (f,a) = (model f, a)
+
+-- And finally, we can take any (m a) to (n b), if we can Model m n, and a b
+instance (NatTrans m n, Model a b) => Model (m a) (n b) where model x = fmap model (eta x)
+
+------------------------------------------------------------------------
+
+-- In a form more useful for QC testing (and it's lazy)
+checkInvariant :: L.ByteString -> L.ByteString
+checkInvariant = L.checkInvariant
+
+abstr :: L.ByteString -> P.ByteString
+abstr = P.concat . L.toChunks
+
+-- Some short hand.
+type X = Int
+type W = Word8
+type P = P.ByteString
+type B = L.ByteString
+
+------------------------------------------------------------------------
+--
+-- These comparison functions handle wrapping and equality.
+--
+-- A single class for these would be nice, but note that they differe in
+-- the number of arguments, and those argument types, so we'd need HList
+-- tricks. See here: http://okmij.org/ftp/Haskell/vararg-fn.lhs
+--
+
+eq1 f g = \a         ->
+    model (f a)         == g (model a)
+eq2 f g = \a b       ->
+    model (f a b)       == g (model a) (model b)
+eq3 f g = \a b c     ->
+    model (f a b c)     == g (model a) (model b) (model c)
+
+--
+-- And for functions that take non-null input
+--
+eqnotnull1 f g = \x     -> (not (isNull x)) ==> eq1 f g x
+eqnotnull2 f g = \x y   -> (not (isNull y)) ==> eq2 f g x y
+eqnotnull3 f g = \x y z -> (not (isNull z)) ==> eq3 f g x y z
+
+class    IsNull t            where isNull :: t -> Bool
+instance IsNull L.ByteString where isNull = L.null
+instance IsNull P.ByteString where isNull = P.null
diff --git a/tests/Rules.hs b/tests/Rules.hs
new file mode 100644
--- /dev/null
+++ b/tests/Rules.hs
@@ -0,0 +1,41 @@
+module Rules where
+--
+-- Tests to ensure rules are firing.
+--
+
+import qualified Data.ByteString.Char8       as C
+import qualified Data.ByteString             as P
+import qualified Data.ByteString.Lazy        as L
+import qualified Data.ByteString.Lazy.Char8  as D
+import Data.List
+import Data.Char
+import Data.Word
+
+import QuickCheckUtils
+
+import Test.Tasty.QuickCheck
+
+prop_break_C :: Word8 -> C.ByteString -> Bool
+prop_break_C w = C.break ((==) x) `eq1` break ((==) x)
+  where
+    -- Make sure we're not testing non-octet character values, for which
+    -- C.break is not isomorphic to breaking the corresponding string,
+    -- Instead start with a byte, and make a character out of that.
+    x = chr $ fromIntegral w
+
+prop_break_P :: Word8 -> C.ByteString -> Bool
+prop_break_P x = P.break ((==) x) `eq1` break ((==) x)
+prop_intercalate_P c = (\s1 s2 -> P.intercalate (P.singleton c) (s1 : s2 : []))
+                        `eq2`
+                       (\s1 s2 -> intercalate [c] (s1 : s2 : []))
+
+prop_break_isSpace_C = C.break isSpace `eq1` break isSpace
+prop_dropWhile_isSpace_C = C.dropWhile isSpace `eq1` dropWhile isSpace
+
+rules =
+    [ testProperty "break (==)"        prop_break_C
+    , testProperty "break (==)"        prop_break_P
+    , testProperty "break isSpace"     prop_break_isSpace_C
+    , testProperty "dropWhile isSpace" prop_dropWhile_isSpace_C
+    , testProperty "intercalate"       prop_intercalate_P
+    ]
diff --git a/tests/builder/Data/ByteString/Builder/Prim/TestUtils.hs b/tests/builder/Data/ByteString/Builder/Prim/TestUtils.hs
new file mode 100644
--- /dev/null
+++ b/tests/builder/Data/ByteString/Builder/Prim/TestUtils.hs
@@ -0,0 +1,364 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+-- |
+-- Copyright   : (c) 2011 Simon Meier
+-- License     : BSD3-style (see LICENSE)
+--
+-- Maintainer  : Simon Meier <iridcode@gmail.com>
+-- Stability   : experimental
+-- Portability : tested on GHC only
+--
+-- Testing utilities for comparing
+-- for an example on how to use the functions provided here.
+--
+module Data.ByteString.Builder.Prim.TestUtils (
+
+  -- * Showing
+    evalF
+  , evalB
+
+  , showF
+  , showB
+
+  -- * Testing 'FixedPrim's
+  , testF
+  , testBoundedF
+
+  , testFixedBoundF
+
+  , compareImpls
+
+  -- * Testing 'BoundedPrim's
+  , testBoundedB
+
+  -- * Encoding reference implementations
+
+  , charUtf8_list
+  , char8_list
+
+  -- ** ASCII-based encodings
+  , encodeASCII
+  , encodeForcedASCII
+  , char7_list
+  , dec_list
+  , hex_list
+  , wordHexFixed_list
+  , int8HexFixed_list
+  , int16HexFixed_list
+  , int32HexFixed_list
+  , int64HexFixed_list
+  , floatHexFixed_list
+  , doubleHexFixed_list
+
+  -- ** Binary
+  , parseVar
+
+  , bigEndian_list
+  , littleEndian_list
+  , hostEndian_list
+  , float_list
+  , double_list
+  , coerceFloatToWord32
+  , coerceDoubleToWord64
+
+  ) where
+
+import           Control.Arrow (first)
+
+import           Data.ByteString.Builder.Prim
+
+import qualified Data.ByteString               as S
+import qualified Data.ByteString.Internal      as S
+import qualified Data.ByteString.Builder.Prim.Internal as I
+
+import           Data.Bits (Bits(..))
+import           Data.Char (chr, ord)
+import           Data.Int
+import           Data.Word
+import           Foreign (Storable(..), castPtr, minusPtr, with)
+import           Numeric (showHex)
+import           GHC.ByteOrder
+import           System.IO.Unsafe (unsafePerformIO)
+
+import           Test.Tasty
+import           Test.Tasty.HUnit (assertBool, testCase)
+import           Test.Tasty.QuickCheck (Arbitrary(..), testProperty)
+
+-- Helper functions
+-------------------
+
+-- | Quickcheck test that includes a check that the property holds on the
+-- bounds of a bounded value.
+testBoundedProperty :: forall a. (Arbitrary a, Show a, Bounded a)
+                    => String -> (a -> Bool) -> TestTree
+testBoundedProperty name p = testGroup name
+  [ testProperty name p
+  , testCase (name ++ " minBound") $ assertBool "minBound" (p (minBound :: a))
+  , testCase (name ++ " maxBound") $ assertBool "minBound" (p (maxBound :: a))
+  ]
+
+-- | Quote a 'String' nicely.
+quote :: String -> String
+quote cs = '`' : cs ++ "'"
+
+-- | Quote a @[Word8]@ list as as 'String'.
+quoteWord8s :: [Word8] -> String
+quoteWord8s = quote . map (chr . fromIntegral)
+
+
+------------------------------------------------------------------------------
+-- Testing encodings
+------------------------------------------------------------------------------
+
+-- | /For testing use only./ Evaluate a 'FixedPrim' on a given value.
+evalF :: FixedPrim a -> a -> [Word8]
+evalF fe = S.unpack . S.unsafeCreate (I.size fe) . I.runF fe
+
+-- | /For testing use only./ Evaluate a 'BoundedPrim' on a given value.
+evalB :: BoundedPrim a -> a -> [Word8]
+evalB be x = S.unpack $ unsafePerformIO $
+    S.createAndTrim (I.sizeBound be) $ \op -> do
+        op' <- I.runB be x op
+        return (op' `minusPtr` op)
+
+-- | /For testing use only./ Show the result of a 'FixedPrim' of a given
+-- value as a 'String' by interpreting the resulting bytes as Unicode
+-- codepoints.
+showF :: FixedPrim a -> a -> String
+showF fe = map (chr . fromIntegral) . evalF fe
+
+-- | /For testing use only./ Show the result of a 'BoundedPrim' of a given
+-- value as a 'String' by interpreting the resulting bytes as Unicode
+-- codepoints.
+showB :: BoundedPrim a -> a -> String
+showB be = map (chr . fromIntegral) . evalB be
+
+
+-- FixedPrim
+----------------
+
+-- TODO: Port code that checks for low-level properties of basic encodings (no
+-- overwrites, all bytes written, etc.) from old 'system-io-write' library
+
+-- | Test a 'FixedPrim' against a reference implementation.
+testF :: (Arbitrary a, Show a)
+      => String
+      -> (a -> [Word8])
+      -> FixedPrim a
+      -> TestTree
+testF name ref fe =
+    testProperty name prop
+  where
+    prop x
+      | y == y'   = True
+      | otherwise = error $ unlines $
+          [ "testF: results disagree for " ++ quote (show x)
+          , " fixed encoding: " ++ show y ++ " " ++ quoteWord8s y
+          , " reference:      " ++ show y'++ " " ++ quoteWord8s y'
+          ]
+      where
+        y  = evalF fe x
+        y' = ref x
+
+-- | Test a 'FixedPrim' of a bounded value against a reference implementation
+-- and ensure that the bounds are always included as testcases.
+testBoundedF :: (Arbitrary a, Bounded a, Show a)
+             => String
+             -> (a -> [Word8])
+             -> FixedPrim a
+             -> TestTree
+testBoundedF name ref fe =
+    testBoundedProperty name $ \x -> evalF fe x == ref x
+
+-- FixedPrim derived from a bound on a given value.
+
+testFixedBoundF :: (Arbitrary a, Show a, Integral a)
+                => String
+                -> (a -> a -> [Word8])
+                -> (a -> FixedPrim a)
+                -> TestTree
+testFixedBoundF name ref bfe =
+    testProperty name prop
+  where
+    prop (b, x0)
+      | y == y'   = True
+      | otherwise = error $ unlines $
+          [ "testF: results disagree for " ++ quote (show (b, x))
+          , " fixed encoding: " ++ show y ++ " " ++ quoteWord8s y
+          , " reference:      " ++ show y'++ " " ++ quoteWord8s y'
+          ]
+      where
+        x  | b == 0    = 0
+           | otherwise = x0 `mod` b
+        y  = evalF (bfe b) x
+        y' = ref b x
+
+
+-- BoundedPrim
+------------------
+
+-- | Test a 'BoundedPrim' of a bounded value against a reference implementation
+-- and ensure that the bounds are always included as testcases.
+testBoundedB :: (Arbitrary a, Bounded a, Show a)
+             => String
+             -> (a -> [Word8])
+             -> BoundedPrim a
+             -> TestTree
+testBoundedB name ref fe =
+    testBoundedProperty name check
+  where
+    check x
+      | y == y'   = True
+      | otherwise = error $ unlines $
+          [ "testBoundedB: results disagree for " ++ quote (show x)
+          , " fixed encoding: " ++ show y ++ " " ++ quoteWord8s y
+          , " reference:      " ++ show y'++ " " ++ quoteWord8s y'
+          ]
+      where
+        y  = evalB fe x
+        y' = ref x
+
+-- | Compare two implementations of a function.
+compareImpls :: (Arbitrary a, Show a, Show b, Eq b)
+             => TestName -> (a -> b) -> (a -> b) -> TestTree
+compareImpls name f1 f2 =
+    testProperty name check
+  where
+    check x
+      | y1 == y2  = True
+      | otherwise = error $ unlines $
+          [ "compareImpls: results disagree for " ++ quote (show x)
+          , " f1: " ++ show y1
+          , " f2: " ++ show y2
+          ]
+      where
+        y1 = f1 x
+        y2 = f2 x
+
+
+
+------------------------------------------------------------------------------
+-- Encoding reference implementations
+------------------------------------------------------------------------------
+
+-- | Char8 encoding: truncate Unicode codepoint to 8-bits.
+char8_list :: Char -> [Word8]
+char8_list = return . fromIntegral . ord
+
+-- | Encode a Haskell String to a list of Word8 values, in UTF8 format.
+--
+-- Copied from 'utf8-string-0.3.6' to make tests self-contained.
+-- Copyright (c) 2007, Galois Inc. All rights reserved.
+--
+charUtf8_list :: Char -> [Word8]
+charUtf8_list =
+    map fromIntegral . encode . ord
+  where
+    encode oc
+      | oc <= 0x7f       = [oc]
+
+      | oc <= 0x7ff      = [ 0xc0 + (oc `shiftR` 6)
+                           , 0x80 + oc .&. 0x3f
+                           ]
+
+      | oc <= 0xffff     = [ 0xe0 + (oc `shiftR` 12)
+                           , 0x80 + ((oc `shiftR` 6) .&. 0x3f)
+                           , 0x80 + oc .&. 0x3f
+                           ]
+      | otherwise        = [ 0xf0 + (oc `shiftR` 18)
+                           , 0x80 + ((oc `shiftR` 12) .&. 0x3f)
+                           , 0x80 + ((oc `shiftR` 6) .&. 0x3f)
+                           , 0x80 + oc .&. 0x3f
+                           ]
+
+-- ASCII-based encodings
+------------------------
+
+-- | Encode a 'String' of only ASCII characters using the ASCII encoding.
+encodeASCII :: String -> [Word8]
+encodeASCII =
+    map encode
+  where
+    encode c
+      | c < '\x7f' = fromIntegral $ ord c
+      | otherwise  = error $ "encodeASCII: non-ASCII character '" ++ [c] ++ "'"
+
+-- | Encode an arbitrary 'String' by truncating its characters to the least
+-- significant 7-bits.
+encodeForcedASCII :: String -> [Word8]
+encodeForcedASCII = map ((.&. 0x7f) . fromIntegral . ord)
+
+char7_list :: Char -> [Word8]
+char7_list = encodeForcedASCII . return
+
+dec_list :: Show a =>  a -> [Word8]
+dec_list = encodeASCII . show
+
+hex_list :: (Integral a, Show a) => a -> [Word8]
+hex_list = encodeASCII . (\x -> showHex x "")
+
+wordHexFixed_list :: (Storable a, Integral a, Show a) => a -> [Word8]
+wordHexFixed_list x =
+   encodeASCII $ pad (2 * sizeOf x) $ showHex x ""
+ where
+   pad n cs = replicate (n - length cs) '0' ++ cs
+
+int8HexFixed_list :: Int8 -> [Word8]
+int8HexFixed_list  = wordHexFixed_list . (fromIntegral :: Int8  -> Word8 )
+
+int16HexFixed_list :: Int16 -> [Word8]
+int16HexFixed_list = wordHexFixed_list . (fromIntegral :: Int16 -> Word16)
+
+int32HexFixed_list :: Int32 -> [Word8]
+int32HexFixed_list = wordHexFixed_list . (fromIntegral :: Int32 -> Word32)
+
+int64HexFixed_list :: Int64 -> [Word8]
+int64HexFixed_list = wordHexFixed_list . (fromIntegral :: Int64 -> Word64)
+
+floatHexFixed_list :: Float -> [Word8]
+floatHexFixed_list  = float_list wordHexFixed_list
+
+doubleHexFixed_list :: Double -> [Word8]
+doubleHexFixed_list = double_list wordHexFixed_list
+
+-- Binary
+---------
+
+bigEndian_list :: (Storable a, Bits a, Integral a) => a -> [Word8]
+bigEndian_list = reverse . littleEndian_list
+
+littleEndian_list :: (Storable a, Bits a, Integral a) => a -> [Word8]
+littleEndian_list x =
+    map (fromIntegral . (x `shiftR`) . (8*)) $ [0..sizeOf x - 1]
+
+hostEndian_list :: (Storable a, Bits a, Integral a) => a -> [Word8]
+hostEndian_list = case targetByteOrder of
+    LittleEndian -> littleEndian_list
+    BigEndian    -> bigEndian_list
+
+float_list :: (Word32 -> [Word8]) -> Float -> [Word8]
+float_list f  = f . coerceFloatToWord32
+
+double_list :: (Word64 -> [Word8]) -> Double -> [Word8]
+double_list f = f . coerceDoubleToWord64
+
+-- | Convert a 'Float' to a 'Word32'.
+{-# NOINLINE coerceFloatToWord32 #-}
+coerceFloatToWord32 :: Float -> Word32
+coerceFloatToWord32 x = unsafePerformIO (with x (peek . castPtr))
+
+-- | Convert a 'Double' to a 'Word64'.
+{-# NOINLINE coerceDoubleToWord64 #-}
+coerceDoubleToWord64 :: Double -> Word64
+coerceDoubleToWord64 x = unsafePerformIO (with x (peek . castPtr))
+
+-- | Parse a variable length encoding
+parseVar :: (Num a, Bits a) => [Word8] -> (a, [Word8])
+parseVar =
+    go
+  where
+    go []    = error "parseVar: unterminated variable length int"
+    go (w:ws)
+      | w .&. 0x80 == 0 = (fromIntegral w, ws)
+      | otherwise       = first add (go ws)
+      where
+        add x = (x `shiftL` 7) .|. (fromIntegral w .&. 0x7f)
diff --git a/tests/builder/Data/ByteString/Builder/Prim/Tests.hs b/tests/builder/Data/ByteString/Builder/Prim/Tests.hs
new file mode 100644
--- /dev/null
+++ b/tests/builder/Data/ByteString/Builder/Prim/Tests.hs
@@ -0,0 +1,174 @@
+{-# LANGUAGE MagicHash #-}
+
+-- |
+-- Copyright   : (c) 2011 Simon Meier
+-- License     : BSD3-style (see LICENSE)
+--
+-- Maintainer  : Simon Meier <iridcode@gmail.com>
+-- Stability   : experimental
+-- Portability : tested on GHC only
+--
+-- Testing all encodings provided by this library.
+
+module Data.ByteString.Builder.Prim.Tests (tests) where
+
+import           Data.Char  (ord)
+import qualified Data.ByteString.Lazy                  as L
+import qualified Data.ByteString.Lazy.Char8            as LC
+import           Data.ByteString.Builder
+import qualified Data.ByteString.Builder.Prim          as BP
+import           Data.ByteString.Builder.Prim.TestUtils
+
+import           Test.Tasty
+import           Test.Tasty.QuickCheck
+
+tests :: [TestTree]
+tests = concat [ testsBinary, testsASCII, testsChar8, testsUtf8
+               , testsCombinatorsB, [testCString, testCStringUtf8] ]
+
+testCString :: TestTree
+testCString = testProperty "cstring" $
+    toLazyByteString (BP.cstring "hello world!"#) ==
+      LC.pack "hello" `L.append` L.singleton 0x20 `L.append` LC.pack "world!"
+
+testCStringUtf8 :: TestTree
+testCStringUtf8 = testProperty "cstringUtf8" $
+    toLazyByteString (BP.cstringUtf8 "hello\xc0\x80world!"#) ==
+      LC.pack "hello" `L.append` L.singleton 0x00 `L.append` LC.pack "world!"
+
+------------------------------------------------------------------------------
+-- Binary
+------------------------------------------------------------------------------
+
+testsBinary :: [TestTree]
+testsBinary =
+  [ testBoundedF "word8"     bigEndian_list    BP.word8
+  , testBoundedF "int8"      bigEndian_list    BP.int8
+
+  --  big-endian
+  , testBoundedF "int16BE"   bigEndian_list    BP.int16BE
+  , testBoundedF "int32BE"   bigEndian_list    BP.int32BE
+  , testBoundedF "int64BE"   bigEndian_list    BP.int64BE
+
+  , testBoundedF "word16BE"  bigEndian_list    BP.word16BE
+  , testBoundedF "word32BE"  bigEndian_list    BP.word32BE
+  , testBoundedF "word64BE"  bigEndian_list    BP.word64BE
+
+  , testF "floatLE"     (float_list  littleEndian_list) BP.floatLE
+  , testF "doubleLE"    (double_list littleEndian_list) BP.doubleLE
+
+  --  little-endian
+  , testBoundedF "int16LE"   littleEndian_list BP.int16LE
+  , testBoundedF "int32LE"   littleEndian_list BP.int32LE
+  , testBoundedF "int64LE"   littleEndian_list BP.int64LE
+
+  , testBoundedF "word16LE"  littleEndian_list BP.word16LE
+  , testBoundedF "word32LE"  littleEndian_list BP.word32LE
+  , testBoundedF "word64LE"  littleEndian_list BP.word64LE
+
+  , testF "floatBE"     (float_list  bigEndian_list)   BP.floatBE
+  , testF "doubleBE"    (double_list bigEndian_list)   BP.doubleBE
+
+  --  host dependent
+  , testBoundedF "int16Host"   hostEndian_list  BP.int16Host
+  , testBoundedF "int32Host"   hostEndian_list  BP.int32Host
+  , testBoundedF "int64Host"   hostEndian_list  BP.int64Host
+  , testBoundedF "intHost"     hostEndian_list  BP.intHost
+
+  , testBoundedF "word16Host"  hostEndian_list  BP.word16Host
+  , testBoundedF "word32Host"  hostEndian_list  BP.word32Host
+  , testBoundedF "word64Host"  hostEndian_list  BP.word64Host
+  , testBoundedF "wordHost"    hostEndian_list  BP.wordHost
+
+  , testF "floatHost"   (float_list  hostEndian_list)   BP.floatHost
+  , testF "doubleHost"  (double_list hostEndian_list)   BP.doubleHost
+  ]
+
+
+------------------------------------------------------------------------------
+-- Latin-1  aka  Char8
+------------------------------------------------------------------------------
+
+testsChar8 :: [TestTree]
+testsChar8 =
+  [ testBoundedF "char8"     char8_list        BP.char8  ]
+
+
+------------------------------------------------------------------------------
+-- ASCII
+------------------------------------------------------------------------------
+
+testsASCII :: [TestTree]
+testsASCII =
+  [ testBoundedF "char7" char7_list BP.char7
+
+  , testBoundedB "int8Dec"   dec_list BP.int8Dec
+  , testBoundedB "int16Dec"  dec_list BP.int16Dec
+  , testBoundedB "int32Dec"  dec_list BP.int32Dec
+  , testBoundedB "int64Dec"  dec_list BP.int64Dec
+  , testBoundedB "intDec"    dec_list BP.intDec
+
+  , testBoundedB "word8Dec"  dec_list BP.word8Dec
+  , testBoundedB "word16Dec" dec_list BP.word16Dec
+  , testBoundedB "word32Dec" dec_list BP.word32Dec
+  , testBoundedB "word64Dec" dec_list BP.word64Dec
+  , testBoundedB "wordDec"   dec_list BP.wordDec
+
+  , testBoundedB "word8Hex"  hex_list BP.word8Hex
+  , testBoundedB "word16Hex" hex_list BP.word16Hex
+  , testBoundedB "word32Hex" hex_list BP.word32Hex
+  , testBoundedB "word64Hex" hex_list BP.word64Hex
+  , testBoundedB "wordHex"   hex_list BP.wordHex
+
+  , testBoundedF "word8HexFixed"  wordHexFixed_list BP.word8HexFixed
+  , testBoundedF "word16HexFixed" wordHexFixed_list BP.word16HexFixed
+  , testBoundedF "word32HexFixed" wordHexFixed_list BP.word32HexFixed
+  , testBoundedF "word64HexFixed" wordHexFixed_list BP.word64HexFixed
+
+  , testBoundedF "int8HexFixed"  int8HexFixed_list  BP.int8HexFixed
+  , testBoundedF "int16HexFixed" int16HexFixed_list BP.int16HexFixed
+  , testBoundedF "int32HexFixed" int32HexFixed_list BP.int32HexFixed
+  , testBoundedF "int64HexFixed" int64HexFixed_list BP.int64HexFixed
+
+  , testF "floatHexFixed"  floatHexFixed_list  BP.floatHexFixed
+  , testF "doubleHexFixed" doubleHexFixed_list BP.doubleHexFixed
+  ]
+
+
+------------------------------------------------------------------------------
+-- UTF-8
+------------------------------------------------------------------------------
+
+testsUtf8 :: [TestTree]
+testsUtf8 =
+  [ testBoundedB "charUtf8"  charUtf8_list  BP.charUtf8 ]
+
+
+------------------------------------------------------------------------------
+-- BoundedPrim combinators
+------------------------------------------------------------------------------
+
+maybeB :: BP.BoundedPrim () -> BP.BoundedPrim a -> BP.BoundedPrim (Maybe a)
+maybeB nothing just = maybe (Left ()) Right BP.>$< BP.eitherB nothing just
+
+testsCombinatorsB :: [TestTree]
+testsCombinatorsB =
+  [ compareImpls "mapMaybe (via BoundedPrim)"
+        (L.pack . concatMap encChar)
+        (toLazyByteString . encViaBuilder)
+
+  , compareImpls "filter (via BoundedPrim)"
+        (L.pack . filter (< 32))
+        (toLazyByteString . BP.primMapListBounded (BP.condB (< 32) (BP.liftFixedToBounded BP.word8) BP.emptyB))
+
+  , compareImpls "pairB"
+        (L.pack . concatMap (\(c,w) -> charUtf8_list c ++ [w]))
+        (toLazyByteString . BP.primMapListBounded
+            ((\(c,w) -> (c,(w,undefined))) BP.>$<
+                BP.charUtf8 BP.>*< (BP.liftFixedToBounded BP.word8) BP.>*< (BP.liftFixedToBounded BP.emptyF)))
+  ]
+  where
+    encChar = maybe [112] (hostEndian_list . ord)
+
+    encViaBuilder = BP.primMapListBounded $ maybeB (BP.liftFixedToBounded $ (\_ -> 112) BP.>$< BP.word8)
+                                                (ord BP.>$< (BP.liftFixedToBounded $ BP.intHost))
diff --git a/tests/builder/Data/ByteString/Builder/Tests.hs b/tests/builder/Data/ByteString/Builder/Tests.hs
new file mode 100644
--- /dev/null
+++ b/tests/builder/Data/ByteString/Builder/Tests.hs
@@ -0,0 +1,614 @@
+{-# LANGUAGE BangPatterns     #-}
+{-# LANGUAGE CPP              #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+-- |
+-- Copyright   : (c) 2011 Simon Meier
+-- License     : BSD3-style (see LICENSE)
+--
+-- Maintainer  : Simon Meier <iridcode@gmail.com>
+-- Stability   : experimental
+-- Portability : tested on GHC only
+--
+-- Testing composition of 'Builders'.
+
+module Data.ByteString.Builder.Tests (tests) where
+
+import           Control.Applicative
+import           Control.Monad (unless, void)
+import           Control.Monad.Trans.State (StateT, evalStateT, evalState, put, get)
+import           Control.Monad.Trans.Class (lift)
+import           Control.Monad.Trans.Writer (WriterT, execWriterT, tell)
+
+import           Foreign (minusPtr)
+
+import           Data.Char (chr)
+import qualified Data.DList      as D
+import           Data.Foldable
+import           Data.Word
+
+import qualified Data.ByteString          as S
+import qualified Data.ByteString.Internal as S
+import qualified Data.ByteString.Lazy     as L
+import qualified Data.ByteString.Short    as Sh
+
+import           Data.ByteString.Builder
+import           Data.ByteString.Builder.Extra
+import           Data.ByteString.Builder.Internal (Put, putBuilder, fromPut)
+import qualified Data.ByteString.Builder.Internal   as BI
+import qualified Data.ByteString.Builder.Prim       as BP
+import           Data.ByteString.Builder.Prim.TestUtils
+
+import           Control.Exception (evaluate)
+import           System.IO (openTempFile, hPutStr, hClose, hSetBinaryMode, hSetEncoding, utf8, hSetNewlineMode, noNewlineTranslation)
+import           Foreign (ForeignPtr, withForeignPtr, castPtr)
+import           Foreign.C.String (withCString)
+import           System.Posix.Internals (c_unlink)
+
+import           Test.Tasty (TestTree, TestName, testGroup)
+import           Test.Tasty.QuickCheck
+                   ( Arbitrary(..), oneof, choose, listOf, elements
+                   , counterexample, ioProperty, UnicodeString(..), Property, testProperty )
+
+
+tests :: [TestTree]
+tests =
+  [ testBuilderRecipe
+  , testHandlePutBuilder
+  , testHandlePutBuilderChar8
+  , testPut
+  , testRunBuilder
+  ] ++
+  testsEncodingToBuilder ++
+  testsBinary ++
+  testsASCII ++
+  testsChar8 ++
+  testsUtf8
+
+
+------------------------------------------------------------------------------
+-- Testing 'Builder' execution
+------------------------------------------------------------------------------
+
+testBuilderRecipe :: TestTree
+testBuilderRecipe =
+    testProperty "toLazyByteStringWith" $ testRecipe <$> arbitrary
+  where
+    testRecipe r =
+        counterexample msg $ x1 == x2
+      where
+        x1 = renderRecipe r
+        x2 = buildRecipe r
+        toString = map (chr . fromIntegral)
+        msg = unlines
+          [ "recipe: " ++ show r
+          , "render: " ++ toString x1
+          , "build : " ++ toString x2
+          , "diff  : " ++ show (dropWhile (uncurry (==)) $ zip x1 x2)
+          ]
+
+testHandlePutBuilder :: TestTree
+testHandlePutBuilder =
+    testProperty "hPutBuilder" testRecipe
+  where
+    testRecipe :: (UnicodeString, UnicodeString, UnicodeString, Recipe) -> Property
+    testRecipe args =
+      ioProperty $ do
+        let (UnicodeString a1, UnicodeString a2, UnicodeString a3, recipe) = args
+#if MIN_VERSION_base(4,5,0)
+            before  = a1
+            between = a2
+            after   = a3
+#else
+            -- See https://github.com/haskell/bytestring/issues/212
+            -- write -> read does not roundrip with GHC 7.2 and
+            -- characters in the \xEF00-\xEFFF range.
+            safeChr = \c -> c < '\xEF00' || c > '\xEFFF'
+            before  = filter safeChr a1
+            between = filter safeChr a2
+            after   = filter safeChr a3
+#endif
+        (tempFile, tempH) <- openTempFile "." "test-builder.tmp"
+        -- switch to UTF-8 encoding
+        hSetEncoding tempH utf8
+        hSetNewlineMode tempH noNewlineTranslation
+        -- output recipe with intermediate direct writing to handle
+        let b = fst $ recipeComponents recipe
+        hPutStr tempH before
+        hPutBuilder tempH b
+        hPutStr tempH between
+        hPutBuilder tempH b
+        hPutStr tempH after
+        hClose tempH
+        -- read file
+        lbs <- L.readFile tempFile
+        _ <- evaluate (L.length $ lbs)
+        removeFile tempFile
+        -- compare to pure builder implementation
+        let lbsRef = toLazyByteString $ fold
+              [stringUtf8 before, b, stringUtf8 between, b, stringUtf8 after]
+        -- report
+        let msg = unlines
+              [ "task:     " ++ show args
+              , "via file: " ++ show lbs
+              , "direct :  " ++ show lbsRef
+              -- , "diff  : " ++ show (dropWhile (uncurry (==)) $ zip x1 x2)
+              ]
+            success = lbs == lbsRef
+        unless success (error msg)
+        return success
+
+testHandlePutBuilderChar8 :: TestTree
+testHandlePutBuilderChar8 =
+    testProperty "char8 hPutBuilder" testRecipe
+  where
+    testRecipe :: (String, String, String, Recipe) -> Property
+    testRecipe args@(before, between, after, recipe) = ioProperty $ do
+        (tempFile, tempH) <- openTempFile "." "TestBuilder"
+        -- switch to binary / latin1 encoding
+        hSetBinaryMode tempH True
+        -- output recipe with intermediate direct writing to handle
+        let b = fst $ recipeComponents recipe
+        hPutStr tempH before
+        hPutBuilder tempH b
+        hPutStr tempH between
+        hPutBuilder tempH b
+        hPutStr tempH after
+        hClose tempH
+        -- read file
+        lbs <- L.readFile tempFile
+        _ <- evaluate (L.length $ lbs)
+        removeFile tempFile
+        -- compare to pure builder implementation
+        let lbsRef = toLazyByteString $ fold
+              [string8 before, b, string8 between, b, string8 after]
+        -- report
+        let msg = unlines
+              [ "task:     " ++ show args
+              , "via file: " ++ show lbs
+              , "direct :  " ++ show lbsRef
+              -- , "diff  : " ++ show (dropWhile (uncurry (==)) $ zip x1 x2)
+              ]
+            success = lbs == lbsRef
+        unless success (error msg)
+        return success
+
+removeFile :: String -> IO ()
+removeFile fn = void $ withCString fn c_unlink
+
+-- Recipes with which to test the builder functions
+---------------------------------------------------
+
+data Mode =
+       Threshold Int
+     | Insert
+     | Copy
+     | Smart
+     | Hex
+     deriving( Eq, Ord, Show )
+
+data Action =
+       SBS Mode S.ByteString
+     | LBS Mode L.ByteString
+     | ShBS Sh.ShortByteString
+     | W8  Word8
+     | W8S [Word8]
+     | String String
+     | FDec Float
+     | DDec Double
+     | Flush
+     | EnsureFree Word
+     | ModState Int
+     deriving( Eq, Ord, Show )
+
+data Strategy = Safe | Untrimmed
+     deriving( Eq, Ord, Show )
+
+data Recipe = Recipe Strategy Int Int L.ByteString [Action]
+     deriving( Eq, Ord, Show )
+
+renderRecipe :: Recipe -> [Word8]
+renderRecipe (Recipe _ firstSize _ cont as) =
+    D.toList $ evalState (execWriterT (traverse_ renderAction as)) firstSize
+                 `D.append` renderLBS cont
+  where
+    renderAction :: Monad m => Action -> WriterT (D.DList Word8) (StateT Int m) ()
+    renderAction (SBS Hex bs)   = tell $ foldMap hexWord8 $ S.unpack bs
+    renderAction (SBS _ bs)     = tell $ D.fromList $ S.unpack bs
+    renderAction (LBS Hex lbs)  = tell $ foldMap hexWord8 $ L.unpack lbs
+    renderAction (LBS _ lbs)    = tell $ renderLBS lbs
+    renderAction (ShBS sbs)     = tell $ D.fromList $ Sh.unpack sbs
+    renderAction (W8 w)         = tell $ return w
+    renderAction (W8S ws)       = tell $ D.fromList ws
+    renderAction (String cs)    = tell $ foldMap (D.fromList . charUtf8_list) cs
+    renderAction Flush          = tell $ D.empty
+    renderAction (EnsureFree _) = tell $ D.empty
+    renderAction (FDec f)       = tell $ D.fromList $ encodeASCII $ show f
+    renderAction (DDec d)       = tell $ D.fromList $ encodeASCII $ show d
+    renderAction (ModState i)   = do
+        s <- lift get
+        tell (D.fromList $ encodeASCII $ show s)
+        lift $ put (s - i)
+
+    renderLBS = D.fromList . L.unpack
+    hexWord8  = D.fromList . wordHexFixed_list
+
+buildAction :: Action -> StateT Int Put ()
+buildAction (SBS Hex bs)            = lift $ putBuilder $ byteStringHex bs
+buildAction (SBS Smart bs)          = lift $ putBuilder $ byteString bs
+buildAction (SBS Copy bs)           = lift $ putBuilder $ byteStringCopy bs
+buildAction (SBS Insert bs)         = lift $ putBuilder $ byteStringInsert bs
+buildAction (SBS (Threshold i) bs)  = lift $ putBuilder $ byteStringThreshold i bs
+buildAction (LBS Hex lbs)           = lift $ putBuilder $ lazyByteStringHex lbs
+buildAction (LBS Smart lbs)         = lift $ putBuilder $ lazyByteString lbs
+buildAction (LBS Copy lbs)          = lift $ putBuilder $ lazyByteStringCopy lbs
+buildAction (LBS Insert lbs)        = lift $ putBuilder $ lazyByteStringInsert lbs
+buildAction (LBS (Threshold i) lbs) = lift $ putBuilder $ lazyByteStringThreshold i lbs
+buildAction (ShBS sbs)              = lift $ putBuilder $ shortByteString sbs
+buildAction (W8 w)                  = lift $ putBuilder $ word8 w
+buildAction (W8S ws)                = lift $ putBuilder $ BP.primMapListFixed BP.word8 ws
+buildAction (String cs)             = lift $ putBuilder $ stringUtf8 cs
+buildAction (FDec f)                = lift $ putBuilder $ floatDec f
+buildAction (DDec d)                = lift $ putBuilder $ doubleDec d
+buildAction Flush                   = lift $ putBuilder $ flush
+buildAction (EnsureFree minFree)    = lift $ putBuilder $ ensureFree $ fromIntegral minFree
+buildAction (ModState i)            = do
+    s <- get
+    lift $ putBuilder $ intDec s
+    put (s - i)
+
+buildRecipe :: Recipe -> [Word8]
+buildRecipe recipe =
+    L.unpack $ toLBS b
+  where
+    (b, toLBS) = recipeComponents recipe
+
+
+recipeComponents :: Recipe -> (Builder, Builder -> L.ByteString)
+recipeComponents (Recipe how firstSize otherSize cont as) =
+    (b, toLBS)
+  where
+    toLBS = toLazyByteStringWith (strategy how firstSize otherSize) cont
+      where
+        strategy Safe      = safeStrategy
+        strategy Untrimmed = untrimmedStrategy
+
+    b = fromPut $ evalStateT (traverse_ buildAction as) firstSize
+
+
+-- 'Arbitary' instances
+-----------------------
+
+instance Arbitrary L.ByteString where
+    arbitrary = L.fromChunks <$> listOf arbitrary
+    shrink lbs
+      | L.null lbs = []
+      | otherwise = pure $ L.take (L.length lbs `div` 2) lbs
+
+instance Arbitrary S.ByteString where
+    arbitrary =
+        trim S.drop =<< trim S.take =<< S.pack <$> listOf arbitrary
+      where
+        trim f bs = oneof [pure bs, f <$> choose (0, S.length bs) <*> pure bs]
+
+    shrink bs
+      | S.null bs = []
+      | otherwise = pure $ S.take (S.length bs `div` 2) bs
+
+instance Arbitrary Mode where
+    arbitrary = oneof
+        [Threshold <$> arbitrary, pure Smart, pure Insert, pure Copy, pure Hex]
+
+    shrink (Threshold i) = Threshold <$> shrink i
+    shrink _             = []
+
+instance Arbitrary Action where
+    arbitrary = oneof
+      [ SBS <$> arbitrary <*> arbitrary
+      , LBS <$> arbitrary <*> arbitrary
+      , ShBS . Sh.toShort <$> arbitrary
+      , W8  <$> arbitrary
+      , W8S <$> listOf arbitrary
+        -- ensure that larger character codes are also tested
+      , String . getUnicodeString <$> arbitrary
+      , pure Flush
+        -- never request more than 64kb free space
+      , (EnsureFree . (`mod` 0xffff)) <$> arbitrary
+      , FDec <$> arbitrary
+      , DDec <$> arbitrary
+      , ModState <$> arbitrary
+      ]
+      where
+
+    shrink (SBS m bs) =
+      (SBS <$> shrink m <*> pure bs) <|>
+      (SBS <$> pure m   <*> shrink bs)
+    shrink (LBS m lbs) =
+      (LBS <$> shrink m <*> pure lbs) <|>
+      (LBS <$> pure m   <*> shrink lbs)
+    shrink (ShBS sbs) =
+      ShBS . Sh.toShort <$> shrink (Sh.fromShort sbs)
+    shrink (W8 w)         = W8 <$> shrink w
+    shrink (W8S ws)       = W8S <$> shrink ws
+    shrink (String cs)    = String <$> shrink cs
+    shrink Flush          = []
+    shrink (EnsureFree i) = EnsureFree <$> shrink i
+    shrink (FDec f)       = FDec <$> shrink f
+    shrink (DDec d)       = DDec <$> shrink d
+    shrink (ModState i)   = ModState <$> shrink i
+
+instance Arbitrary Strategy where
+    arbitrary = elements [Safe, Untrimmed]
+    shrink _  = []
+
+instance Arbitrary Recipe where
+    arbitrary =
+        Recipe <$> arbitrary
+               <*> ((`mod` 33333) <$> arbitrary)  -- bound max chunk-sizes
+               <*> ((`mod` 33337) <$> arbitrary)
+               <*> arbitrary
+               <*> listOf arbitrary
+
+    -- shrinking the actions first is desirable
+    shrink (Recipe a b c d e) = asum
+      [ (\x -> Recipe a b c d x) <$> shrink e
+      , (\x -> Recipe a b c x e) <$> shrink d
+      , (\x -> Recipe a b x d e) <$> shrink c
+      , (\x -> Recipe a x c d e) <$> shrink b
+      , (\x -> Recipe x b c d e) <$> shrink a
+      ]
+
+
+------------------------------------------------------------------------------
+-- Creating Builders from basic encodings
+------------------------------------------------------------------------------
+
+testsEncodingToBuilder :: [TestTree]
+testsEncodingToBuilder =
+  [ test_encodeUnfoldrF
+  , test_encodeUnfoldrB
+  ]
+
+
+-- Unfoldr fused with encoding
+------------------------------
+
+test_encodeUnfoldrF :: TestTree
+test_encodeUnfoldrF =
+    compareImpls "encodeUnfoldrF word8" id encode
+  where
+    toLBS = toLazyByteStringWith (safeStrategy 23 101) L.empty
+    encode =
+        L.unpack . toLBS . BP.primUnfoldrFixed BP.word8 go
+      where
+        go []     = Nothing
+        go (w:ws) = Just (w, ws)
+
+
+test_encodeUnfoldrB :: TestTree
+test_encodeUnfoldrB =
+    compareImpls "encodeUnfoldrB charUtf8" (foldMap charUtf8_list) encode
+  where
+    toLBS = toLazyByteStringWith (safeStrategy 23 101) L.empty
+    encode =
+        L.unpack . toLBS . BP.primUnfoldrBounded BP.charUtf8 go
+      where
+        go []     = Nothing
+        go (c:cs) = Just (c, cs)
+
+
+------------------------------------------------------------------------------
+-- Testing the Put monad
+------------------------------------------------------------------------------
+
+testPut :: TestTree
+testPut = testGroup "Put monad"
+  [ testLaw "identity" (\v -> (pure id <*> putInt v) `eqPut` (putInt v))
+
+  , testLaw "composition" $ \(u, v, w) ->
+        (pure (.) <*> minusInt u <*> minusInt v <*> putInt w) `eqPut`
+        (minusInt u <*> (minusInt v <*> putInt w))
+
+  , testLaw "homomorphism" $ \(f, x) ->
+        (pure (f -) <*> pure x) `eqPut` (pure (f - x))
+
+  , testLaw "interchange" $ \(u, y) ->
+        (minusInt u <*> pure y) `eqPut` (pure ($ y) <*> minusInt u)
+
+  , testLaw "ignore left value" $ \(u, v) ->
+        (putInt u *> putInt v) `eqPut` (pure (const id) <*> putInt u <*> putInt v)
+
+  , testLaw "ignore right value" $ \(u, v) ->
+        (putInt u <* putInt v) `eqPut` (pure const <*> putInt u <*> putInt v)
+
+  , testLaw "functor" $ \(f, x) ->
+        (fmap (f -) (putInt x)) `eqPut` (pure (f -) <*> putInt x)
+
+  ]
+  where
+    putInt i    = putBuilder (integerDec i) >> return i
+    minusInt i  = (-) <$> putInt i
+    run p       = toLazyByteString $ fromPut (do i <- p; _ <- putInt i; return ())
+    eqPut p1 p2 = (run p1, run p2)
+
+    testLaw name f = compareImpls name (fst . f) (snd . f)
+
+
+------------------------------------------------------------------------------
+-- Testing the Driver <-> Builder protocol
+------------------------------------------------------------------------------
+
+-- | Ensure that there are at least 'n' free bytes for the following 'Builder'.
+{-# INLINE ensureFree #-}
+ensureFree :: Int -> Builder
+ensureFree minFree =
+    BI.builder step
+  where
+    step k br@(BI.BufferRange op ope)
+      | ope `minusPtr` op < minFree = return $ BI.bufferFull minFree op next
+      | otherwise                   = k br
+      where
+        next br'@(BI.BufferRange op' ope')
+          |  freeSpace < minFree =
+              error $ "ensureFree: requested " ++ show minFree ++ " bytes, " ++
+                      "but got only " ++ show freeSpace ++ " bytes"
+          | otherwise = k br'
+          where
+            freeSpace = ope' `minusPtr` op'
+
+
+------------------------------------------------------------------------------
+-- Testing the Builder runner
+------------------------------------------------------------------------------
+
+testRunBuilder :: TestTree
+testRunBuilder =
+    testProperty "runBuilder" prop
+  where
+    prop actions =
+        ioProperty $ do
+          let (builder, _) = recipeComponents recipe
+              expected     = renderRecipe recipe
+          actual <- bufferWriterOutput (runBuilder builder)
+          return (S.unpack actual == expected)
+      where
+        recipe = Recipe Safe 0 0 L.empty actions
+
+bufferWriterOutput :: BufferWriter -> IO S.ByteString
+bufferWriterOutput bwrite0 = do
+    let len0 = 8
+    buf <- S.mallocByteString len0
+    bss <- go [] buf len0 bwrite0
+    return (S.concat (reverse bss))
+  where
+    go :: [S.ByteString] -> ForeignPtr Word8 -> Int -> BufferWriter -> IO [S.ByteString]
+    go bss !buf !len bwrite = do
+      (wc, next) <- withForeignPtr buf $ \ptr -> bwrite ptr len
+      bs <- getBuffer buf wc
+      case next of
+        Done                        -> return (bs:bss)
+        More  m bwrite' | m <= len  -> go (bs:bss)   buf len bwrite'
+                        | otherwise -> do let len' = m
+                                          buf' <- S.mallocByteString len'
+                                          go (bs:bss) buf' len' bwrite'
+        Chunk c bwrite'             -> go (c:bs:bss) buf len bwrite'
+
+    getBuffer :: ForeignPtr Word8 -> Int -> IO S.ByteString
+    getBuffer buf len = withForeignPtr buf $ \ptr ->
+                          S.packCStringLen (castPtr ptr, len)
+
+
+------------------------------------------------------------------------------
+-- Testing the pre-defined builders
+------------------------------------------------------------------------------
+
+testBuilderConstr :: (Arbitrary a, Show a)
+                  => TestName -> (a -> [Word8]) -> (a -> Builder) -> TestTree
+testBuilderConstr name ref mkBuilder =
+    testProperty name check
+  where
+    check x =
+        (ws ++ ws) ==
+        (L.unpack $ toLazyByteString $ mkBuilder x `BI.append` mkBuilder x)
+      where
+        ws = ref x
+
+
+testsBinary :: [TestTree]
+testsBinary =
+  [ testBuilderConstr "word8"     bigEndian_list    word8
+  , testBuilderConstr "int8"      bigEndian_list    int8
+
+  --  big-endian
+  , testBuilderConstr "int16BE"   bigEndian_list    int16BE
+  , testBuilderConstr "int32BE"   bigEndian_list    int32BE
+  , testBuilderConstr "int64BE"   bigEndian_list    int64BE
+
+  , testBuilderConstr "word16BE"  bigEndian_list    word16BE
+  , testBuilderConstr "word32BE"  bigEndian_list    word32BE
+  , testBuilderConstr "word64BE"  bigEndian_list    word64BE
+
+  , testBuilderConstr "floatLE"     (float_list  littleEndian_list) floatLE
+  , testBuilderConstr "doubleLE"    (double_list littleEndian_list) doubleLE
+
+  --  little-endian
+  , testBuilderConstr "int16LE"   littleEndian_list int16LE
+  , testBuilderConstr "int32LE"   littleEndian_list int32LE
+  , testBuilderConstr "int64LE"   littleEndian_list int64LE
+
+  , testBuilderConstr "word16LE"  littleEndian_list word16LE
+  , testBuilderConstr "word32LE"  littleEndian_list word32LE
+  , testBuilderConstr "word64LE"  littleEndian_list word64LE
+
+  , testBuilderConstr "floatBE"     (float_list  bigEndian_list)   floatBE
+  , testBuilderConstr "doubleBE"    (double_list bigEndian_list)   doubleBE
+
+  --  host dependent
+  , testBuilderConstr "int16Host"   hostEndian_list  int16Host
+  , testBuilderConstr "int32Host"   hostEndian_list  int32Host
+  , testBuilderConstr "int64Host"   hostEndian_list  int64Host
+  , testBuilderConstr "intHost"     hostEndian_list  intHost
+
+  , testBuilderConstr "word16Host"  hostEndian_list  word16Host
+  , testBuilderConstr "word32Host"  hostEndian_list  word32Host
+  , testBuilderConstr "word64Host"  hostEndian_list  word64Host
+  , testBuilderConstr "wordHost"    hostEndian_list  wordHost
+
+  , testBuilderConstr "floatHost"   (float_list  hostEndian_list)   floatHost
+  , testBuilderConstr "doubleHost"  (double_list hostEndian_list)   doubleHost
+  ]
+
+testsASCII :: [TestTree]
+testsASCII =
+  [ testBuilderConstr "char7" char7_list char7
+  , testBuilderConstr "string7" (foldMap char7_list) string7
+
+  , testBuilderConstr "int8Dec"   dec_list int8Dec
+  , testBuilderConstr "int16Dec"  dec_list int16Dec
+  , testBuilderConstr "int32Dec"  dec_list int32Dec
+  , testBuilderConstr "int64Dec"  dec_list int64Dec
+  , testBuilderConstr "intDec"    dec_list intDec
+
+  , testBuilderConstr "word8Dec"  dec_list word8Dec
+  , testBuilderConstr "word16Dec" dec_list word16Dec
+  , testBuilderConstr "word32Dec" dec_list word32Dec
+  , testBuilderConstr "word64Dec" dec_list word64Dec
+  , testBuilderConstr "wordDec"   dec_list wordDec
+
+  , testBuilderConstr "integerDec" (dec_list . enlarge) (integerDec . enlarge)
+  , testBuilderConstr "floatDec"   dec_list floatDec
+  , testBuilderConstr "doubleDec"  dec_list doubleDec
+
+  , testBuilderConstr "word8Hex"  hex_list word8Hex
+  , testBuilderConstr "word16Hex" hex_list word16Hex
+  , testBuilderConstr "word32Hex" hex_list word32Hex
+  , testBuilderConstr "word64Hex" hex_list word64Hex
+  , testBuilderConstr "wordHex"   hex_list wordHex
+
+  , testBuilderConstr "word8HexFixed"  wordHexFixed_list word8HexFixed
+  , testBuilderConstr "word16HexFixed" wordHexFixed_list word16HexFixed
+  , testBuilderConstr "word32HexFixed" wordHexFixed_list word32HexFixed
+  , testBuilderConstr "word64HexFixed" wordHexFixed_list word64HexFixed
+
+  , testBuilderConstr "int8HexFixed"  int8HexFixed_list  int8HexFixed
+  , testBuilderConstr "int16HexFixed" int16HexFixed_list int16HexFixed
+  , testBuilderConstr "int32HexFixed" int32HexFixed_list int32HexFixed
+  , testBuilderConstr "int64HexFixed" int64HexFixed_list int64HexFixed
+
+  , testBuilderConstr "floatHexFixed"  floatHexFixed_list  floatHexFixed
+  , testBuilderConstr "doubleHexFixed" doubleHexFixed_list doubleHexFixed
+  ]
+  where
+    enlarge (n, e) = n ^ (abs (e `mod` (50 :: Integer)))
+
+testsChar8 :: [TestTree]
+testsChar8 =
+  [ testBuilderConstr "charChar8" char8_list char8
+  , testBuilderConstr "stringChar8" (foldMap char8_list) string8
+  ]
+
+testsUtf8 :: [TestTree]
+testsUtf8 =
+  [ testBuilderConstr "charUtf8" charUtf8_list charUtf8
+  , testBuilderConstr "stringUtf8" (foldMap charUtf8_list) stringUtf8
+  ]
diff --git a/tests/builder/TestSuite.hs b/tests/builder/TestSuite.hs
new file mode 100644
--- /dev/null
+++ b/tests/builder/TestSuite.hs
@@ -0,0 +1,17 @@
+module Main where
+
+import qualified Data.ByteString.Builder.Tests
+import qualified Data.ByteString.Builder.Prim.Tests
+import           Test.Tasty (defaultMain, TestTree, testGroup)
+
+main :: IO ()
+main = defaultMain $ testGroup "All" tests
+
+tests :: [TestTree]
+tests =
+  [ testGroup "Data.ByteString.Builder"
+       Data.ByteString.Builder.Tests.tests
+
+  , testGroup "Data.ByteString.Builder.BasicEncoding"
+       Data.ByteString.Builder.Prim.Tests.tests
+  ]
