{-# LANGUAGE CPP #-}
{-# LANGUAGE Unsafe #-}

{-# OPTIONS_HADDOCK not-home #-}

{-# LANGUAGE TypeFamilies #-}

-- |
-- Module      : Data.ByteString.Lazy.Internal
-- Copyright   : (c) Don Stewart 2006-2008
--               (c) Duncan Coutts 2006-2011
-- License     : BSD-style
-- Maintainer  : dons00@gmail.com, duncan@community.haskell.org
-- Stability   : unstable
-- Portability : non-portable
--
-- A module containing semi-public 'ByteString' internals. This exposes
-- the 'ByteString' representation and low level construction functions.
-- Modules which extend the 'ByteString' system will need to use this module
-- while ideally most users will be able to make do with the public interface
-- modules.
--
module Data.ByteString.Lazy.Internal (

        -- * The lazy @ByteString@ type and representation
        ByteString(Empty, Chunk),
        LazyByteString,
        chunk,
        foldrChunks,
        foldlChunks,

        -- * Data type invariant and abstraction function
        invariant,
        checkInvariant,

        -- * Chunk allocation sizes
        defaultChunkSize,
        smallChunkSize,
        chunkOverhead,

        -- * Conversion with lists: packing and unpacking
        packBytes, packChars,
        unpackBytes, unpackChars,
        -- * Conversions with strict ByteString
        fromStrict, toStrict,

  ) where

import Prelude hiding (concat)

import qualified Data.ByteString.Internal.Type as S

import Data.Word (Word8)
import Foreign.Storable (Storable(sizeOf))

import Data.Semigroup   (Semigroup (..))
import Data.List.NonEmpty (NonEmpty ((:|)))
import Control.DeepSeq  (NFData, rnf)

import Data.String      (IsString(..))

import Data.Data                (Data(..), mkConstr, mkNoRepType, Constr, DataType, Fixity(Prefix), constrIndex)

import GHC.Exts                 (IsList(..))

import qualified Language.Haskell.TH.Syntax as TH

#ifdef HS_BYTESTRING_ASSERTIONS
import Control.Exception (assert)
#endif


-- | A space-efficient representation of a 'Word8' vector, supporting many
-- efficient operations.
--
-- A 'LazyByteString' contains 8-bit bytes, or by using the operations
-- from "Data.ByteString.Lazy.Char8" it can be interpreted as containing
-- 8-bit characters.
--
#ifndef HS_BYTESTRING_ASSERTIONS
data ByteString = Empty | Chunk  {-# UNPACK #-} !S.StrictByteString ByteString
  -- INVARIANT: The S.StrictByteString field of any Chunk is not empty.
  -- (See also the 'invariant' and 'checkInvariant' functions.)

  -- To make testing of this invariant convenient, we add an
  -- assertion to that effect when the HS_BYTESTRING_ASSERTIONS
  -- preprocessor macro is defined, by renaming the actual constructor
  -- and providing a pattern synonym that does the checking:
#else
data ByteString = Empty | Chunk_ {-# UNPACK #-} !S.StrictByteString ByteString

pattern Chunk :: S.StrictByteString -> ByteString -> ByteString
pattern Chunk c cs <- Chunk_ c cs where
  Chunk c@(S.BS _ len) cs = assert (len > 0) Chunk_ c cs

{-# COMPLETE Empty, Chunk #-}
#endif

deriving instance TH.Lift ByteString


-- | Type synonym for the lazy flavour of 'ByteString'.
--
-- @since 0.11.2.0
type LazyByteString = ByteString

instance Eq  ByteString where
    == :: ByteString -> ByteString -> Bool
(==)    = ByteString -> ByteString -> Bool
eq

instance Ord ByteString where
    compare :: ByteString -> ByteString -> Ordering
compare = ByteString -> ByteString -> Ordering
cmp

instance Semigroup ByteString where
    <> :: ByteString -> ByteString -> ByteString
(<>)    = ByteString -> ByteString -> ByteString
append
    sconcat :: NonEmpty ByteString -> ByteString
sconcat (ByteString
b:|[ByteString]
bs) = [ByteString] -> ByteString
concat (ByteString
bByteString -> [ByteString] -> [ByteString]
forall a. a -> [a] -> [a]
:[ByteString]
bs)
    stimes :: forall b. Integral b => b -> ByteString -> ByteString
stimes  = b -> ByteString -> ByteString
forall b. Integral b => b -> ByteString -> ByteString
times

instance Monoid ByteString where
    mempty :: ByteString
mempty  = ByteString
Empty
    mappend :: ByteString -> ByteString -> ByteString
mappend = ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
(<>)
    mconcat :: [ByteString] -> ByteString
mconcat = [ByteString] -> ByteString
concat

instance NFData ByteString where
    rnf :: ByteString -> ()
rnf ByteString
Empty       = ()
    rnf (Chunk StrictByteString
_ ByteString
b) = ByteString -> ()
forall a. NFData a => a -> ()
rnf ByteString
b

instance Show ByteString where
    showsPrec :: Int -> ByteString -> ShowS
showsPrec Int
p ByteString
ps String
r = Int -> String -> ShowS
forall a. Show a => Int -> a -> ShowS
showsPrec Int
p (ByteString -> String
unpackChars ByteString
ps) String
r

instance Read ByteString where
    readsPrec :: Int -> ReadS ByteString
readsPrec Int
p String
str = [ (String -> ByteString
packChars String
x, String
y) | (String
x, String
y) <- Int -> ReadS String
forall a. Read a => Int -> ReadS a
readsPrec Int
p String
str ]

-- | @since 0.10.12.0
instance IsList ByteString where
  type Item ByteString = Word8
  fromList :: [Item ByteString] -> ByteString
fromList = [Word8] -> ByteString
[Item ByteString] -> ByteString
packBytes
  toList :: ByteString -> [Item ByteString]
toList   = ByteString -> [Word8]
ByteString -> [Item ByteString]
unpackBytes

-- | Beware: 'fromString' truncates multi-byte characters to octets.
-- e.g. "枯朶に烏のとまりけり秋の暮" becomes �6k�nh~�Q��n�
instance IsString ByteString where
    fromString :: String -> ByteString
fromString = String -> ByteString
packChars

instance Data ByteString where
  gfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> ByteString -> c ByteString
gfoldl forall d b. Data d => c (d -> b) -> d -> c b
f forall g. g -> c g
z ByteString
txt = ([Word8] -> ByteString) -> c ([Word8] -> ByteString)
forall g. g -> c g
z [Word8] -> ByteString
packBytes c ([Word8] -> ByteString) -> [Word8] -> c ByteString
forall d b. Data d => c (d -> b) -> d -> c b
`f` ByteString -> [Word8]
unpackBytes ByteString
txt
  toConstr :: ByteString -> Constr
toConstr ByteString
_     = Constr
packConstr
  gunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c ByteString
gunfold forall b r. Data b => c (b -> r) -> c r
k forall r. r -> c r
z Constr
c = case Constr -> Int
constrIndex Constr
c of
    Int
1 -> c ([Word8] -> ByteString) -> c ByteString
forall b r. Data b => c (b -> r) -> c r
k (([Word8] -> ByteString) -> c ([Word8] -> ByteString)
forall r. r -> c r
z [Word8] -> ByteString
packBytes)
    Int
_ -> String -> c ByteString
forall a. HasCallStack => String -> a
error String
"gunfold: unexpected constructor of lazy ByteString"
  dataTypeOf :: ByteString -> DataType
dataTypeOf ByteString
_   = DataType
byteStringDataType

packConstr :: Constr
packConstr :: Constr
packConstr = DataType -> String -> [String] -> Fixity -> Constr
mkConstr DataType
byteStringDataType String
"pack" [] Fixity
Prefix

byteStringDataType :: DataType
byteStringDataType :: DataType
byteStringDataType = String -> DataType
mkNoRepType String
"Data.ByteString.Lazy.ByteString"

------------------------------------------------------------------------
-- Packing and unpacking from lists

packBytes :: [Word8] -> ByteString
packBytes :: [Word8] -> ByteString
packBytes [Word8]
cs0 =
    Int -> [Word8] -> ByteString
packChunks Int
32 [Word8]
cs0
  where
    packChunks :: Int -> [Word8] -> ByteString
packChunks Int
n [Word8]
cs = case Int -> [Word8] -> (StrictByteString, [Word8])
S.packUptoLenBytes Int
n [Word8]
cs of
      (StrictByteString
bs, [])  -> StrictByteString -> ByteString -> ByteString
chunk StrictByteString
bs ByteString
Empty
      (StrictByteString
bs, [Word8]
cs') -> StrictByteString -> ByteString -> ByteString
Chunk StrictByteString
bs (Int -> [Word8] -> ByteString
packChunks (Int -> Int -> Int
forall a. Ord a => a -> a -> a
min (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2) Int
smallChunkSize) [Word8]
cs')

packChars :: [Char] -> ByteString
packChars :: String -> ByteString
packChars String
cs0 = Int -> String -> ByteString
packChunks Int
32 String
cs0
  where
    packChunks :: Int -> String -> ByteString
packChunks Int
n String
cs = case Int -> String -> (StrictByteString, String)
S.packUptoLenChars Int
n String
cs of
      (StrictByteString
bs, [])  -> StrictByteString -> ByteString -> ByteString
chunk StrictByteString
bs ByteString
Empty
      (StrictByteString
bs, String
cs') -> StrictByteString -> ByteString -> ByteString
Chunk StrictByteString
bs (Int -> String -> ByteString
packChunks (Int -> Int -> Int
forall a. Ord a => a -> a -> a
min (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2) Int
smallChunkSize) String
cs')

unpackBytes :: ByteString -> [Word8]
unpackBytes :: ByteString -> [Word8]
unpackBytes ByteString
Empty        = []
unpackBytes (Chunk StrictByteString
c ByteString
cs) = StrictByteString -> [Word8] -> [Word8]
S.unpackAppendBytesLazy StrictByteString
c (ByteString -> [Word8]
unpackBytes ByteString
cs)

unpackChars :: ByteString -> [Char]
unpackChars :: ByteString -> String
unpackChars ByteString
Empty        = []
unpackChars (Chunk StrictByteString
c ByteString
cs) = StrictByteString -> ShowS
S.unpackAppendCharsLazy StrictByteString
c (ByteString -> String
unpackChars ByteString
cs)

------------------------------------------------------------------------

-- We no longer use these invariant-checking functions internally,
-- preferring an assertion on `Chunk` itself, controlled by the
-- HS_BYTESTRING_ASSERTIONS preprocessor macro.

-- | The data type invariant:
-- Every ByteString is either 'Empty' or consists of non-null
-- 'S.StrictByteString's. All functions must preserve this.
--
invariant :: ByteString -> Bool
invariant :: ByteString -> Bool
invariant ByteString
Empty                     = Bool
True
invariant (Chunk (S.BS ForeignPtr Word8
_ Int
len) ByteString
cs) = Int
len Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 Bool -> Bool -> Bool
&& ByteString -> Bool
invariant ByteString
cs

-- | Lazily checks that the given 'ByteString' satisfies the data type's
-- "no empty chunks" invariant, raising an exception in place of the
-- first chunk that does not satisfy the invariant.
checkInvariant :: ByteString -> ByteString
checkInvariant :: ByteString -> ByteString
checkInvariant ByteString
Empty = ByteString
Empty
checkInvariant (Chunk c :: StrictByteString
c@(S.BS ForeignPtr Word8
_ Int
len) ByteString
cs)
    | Int
len Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0   = StrictByteString -> ByteString -> ByteString
Chunk StrictByteString
c (ByteString -> ByteString
checkInvariant ByteString
cs)
    | Bool
otherwise = String -> ByteString
forall a. HasCallStack => String -> a
error (String -> ByteString) -> String -> ByteString
forall a b. (a -> b) -> a -> b
$ String
"Data.ByteString.Lazy: invariant violation:"
               String -> ShowS
forall a. [a] -> [a] -> [a]
++ ByteString -> String
forall a. Show a => a -> String
show (StrictByteString -> ByteString -> ByteString
Chunk StrictByteString
c ByteString
cs)

------------------------------------------------------------------------

-- | Smart constructor for 'Chunk'. Guarantees the data type invariant.
chunk :: S.StrictByteString -> ByteString -> ByteString
chunk :: StrictByteString -> ByteString -> ByteString
chunk c :: StrictByteString
c@(S.BS ForeignPtr Word8
_ Int
len) ByteString
cs | Int
len Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0  = ByteString
cs
                        | Bool
otherwise = StrictByteString -> ByteString -> ByteString
Chunk StrictByteString
c ByteString
cs
{-# INLINE chunk #-}

-- | Consume the chunks of a lazy ByteString with a natural right fold.
foldrChunks :: (S.StrictByteString -> a -> a) -> a -> ByteString -> a
foldrChunks :: forall a. (StrictByteString -> a -> a) -> a -> ByteString -> a
foldrChunks StrictByteString -> a -> a
f a
z = ByteString -> a
go
  where go :: ByteString -> a
go ByteString
Empty        = a
z
        go (Chunk StrictByteString
c ByteString
cs) = StrictByteString -> a -> a
f StrictByteString
c (ByteString -> a
go ByteString
cs)
{-# INLINE foldrChunks #-}

-- | Consume the chunks of a lazy ByteString with a strict, tail-recursive,
-- accumulating left fold.
foldlChunks :: (a -> S.StrictByteString -> a) -> a -> ByteString -> a
foldlChunks :: forall a. (a -> StrictByteString -> a) -> a -> ByteString -> a
foldlChunks a -> StrictByteString -> a
f = a -> ByteString -> a
go
  where go :: a -> ByteString -> a
go !a
a ByteString
Empty        = a
a
        go !a
a (Chunk StrictByteString
c ByteString
cs) = a -> ByteString -> a
go (a -> StrictByteString -> a
f a
a StrictByteString
c) ByteString
cs
{-# INLINE foldlChunks #-}

------------------------------------------------------------------------

-- The representation uses lists of packed chunks. When we have to convert from
-- a lazy list to the chunked representation, then by default we use this
-- chunk size. Some functions give you more control over the chunk size.
--
-- Measurements here:
--  http://www.cse.unsw.edu.au/~dons/tmp/chunksize_v_cache.png
--
-- indicate that a value around 0.5 to 1 x your L2 cache is best.
-- The following value assumes people have something greater than 128k,
-- and need to share the cache with other programs.

-- | The chunk size used for I\/O. Currently set to 32k, less the memory management overhead
defaultChunkSize :: Int
defaultChunkSize :: Int
defaultChunkSize = Int
32 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
k Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
chunkOverhead
   where k :: Int
k = Int
1024

-- | The recommended chunk size. Currently set to 4k, less the memory management overhead
smallChunkSize :: Int
smallChunkSize :: Int
smallChunkSize = Int
4 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
k Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
chunkOverhead
   where k :: Int
k = Int
1024

-- | The memory management overhead. Currently this is tuned for GHC only.
chunkOverhead :: Int
chunkOverhead :: Int
chunkOverhead = Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int -> Int
forall a. Storable a => a -> Int
sizeOf (Int
forall a. HasCallStack => a
undefined :: Int)

------------------------------------------------------------------------
-- Implementations for Eq, Ord and Monoid instances

eq :: ByteString -> ByteString -> Bool
eq :: ByteString -> ByteString -> Bool
eq ByteString
Empty ByteString
Empty = Bool
True
eq ByteString
Empty ByteString
_     = Bool
False
eq ByteString
_     ByteString
Empty = Bool
False
eq (Chunk a :: StrictByteString
a@(S.BS ForeignPtr Word8
ap Int
al) ByteString
as) (Chunk b :: StrictByteString
b@(S.BS ForeignPtr Word8
bp Int
bl) ByteString
bs) =
  case Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Int
al Int
bl of
    Ordering
LT -> StrictByteString
a StrictByteString -> StrictByteString -> Bool
forall a. Eq a => a -> a -> Bool
== ForeignPtr Word8 -> Int -> StrictByteString
S.BS ForeignPtr Word8
bp Int
al Bool -> Bool -> Bool
&& ByteString -> ByteString -> Bool
eq ByteString
as (StrictByteString -> ByteString -> ByteString
Chunk (ForeignPtr Word8 -> Int -> StrictByteString
S.BS (ForeignPtr Word8 -> Int -> ForeignPtr Word8
forall a b. ForeignPtr a -> Int -> ForeignPtr b
S.plusForeignPtr ForeignPtr Word8
bp Int
al) (Int
bl Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
al)) ByteString
bs)
    Ordering
EQ -> StrictByteString
a StrictByteString -> StrictByteString -> Bool
forall a. Eq a => a -> a -> Bool
== StrictByteString
b Bool -> Bool -> Bool
&& ByteString -> ByteString -> Bool
eq ByteString
as ByteString
bs
    Ordering
GT -> ForeignPtr Word8 -> Int -> StrictByteString
S.BS ForeignPtr Word8
ap Int
bl StrictByteString -> StrictByteString -> Bool
forall a. Eq a => a -> a -> Bool
== StrictByteString
b Bool -> Bool -> Bool
&& ByteString -> ByteString -> Bool
eq (StrictByteString -> ByteString -> ByteString
Chunk (ForeignPtr Word8 -> Int -> StrictByteString
S.BS (ForeignPtr Word8 -> Int -> ForeignPtr Word8
forall a b. ForeignPtr a -> Int -> ForeignPtr b
S.plusForeignPtr ForeignPtr Word8
ap Int
bl) (Int
al Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
bl)) ByteString
as) ByteString
bs

cmp :: ByteString -> ByteString -> Ordering
cmp :: ByteString -> ByteString -> Ordering
cmp ByteString
Empty ByteString
Empty = Ordering
EQ
cmp ByteString
Empty ByteString
_     = Ordering
LT
cmp ByteString
_     ByteString
Empty = Ordering
GT
cmp (Chunk a :: StrictByteString
a@(S.BS ForeignPtr Word8
ap Int
al) ByteString
as) (Chunk b :: StrictByteString
b@(S.BS ForeignPtr Word8
bp Int
bl) ByteString
bs) =
  case Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare Int
al Int
bl of
    Ordering
LT -> case StrictByteString -> StrictByteString -> Ordering
forall a. Ord a => a -> a -> Ordering
compare StrictByteString
a (ForeignPtr Word8 -> Int -> StrictByteString
S.BS ForeignPtr Word8
bp Int
al) of
            Ordering
EQ     -> ByteString -> ByteString -> Ordering
cmp ByteString
as (StrictByteString -> ByteString -> ByteString
Chunk (ForeignPtr Word8 -> Int -> StrictByteString
S.BS (ForeignPtr Word8 -> Int -> ForeignPtr Word8
forall a b. ForeignPtr a -> Int -> ForeignPtr b
S.plusForeignPtr ForeignPtr Word8
bp Int
al) (Int
bl Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
al)) ByteString
bs)
            Ordering
result -> Ordering
result
    Ordering
EQ -> case StrictByteString -> StrictByteString -> Ordering
forall a. Ord a => a -> a -> Ordering
compare StrictByteString
a StrictByteString
b of
            Ordering
EQ     -> ByteString -> ByteString -> Ordering
cmp ByteString
as ByteString
bs
            Ordering
result -> Ordering
result
    Ordering
GT -> case StrictByteString -> StrictByteString -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (ForeignPtr Word8 -> Int -> StrictByteString
S.BS ForeignPtr Word8
ap Int
bl) StrictByteString
b of
            Ordering
EQ     -> ByteString -> ByteString -> Ordering
cmp (StrictByteString -> ByteString -> ByteString
Chunk (ForeignPtr Word8 -> Int -> StrictByteString
S.BS (ForeignPtr Word8 -> Int -> ForeignPtr Word8
forall a b. ForeignPtr a -> Int -> ForeignPtr b
S.plusForeignPtr ForeignPtr Word8
ap Int
bl) (Int
al Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
bl)) ByteString
as) ByteString
bs
            Ordering
result -> Ordering
result

append :: ByteString -> ByteString -> ByteString
append :: ByteString -> ByteString -> ByteString
append ByteString
xs ByteString
ys = (StrictByteString -> ByteString -> ByteString)
-> ByteString -> ByteString -> ByteString
forall a. (StrictByteString -> a -> a) -> a -> ByteString -> a
foldrChunks StrictByteString -> ByteString -> ByteString
Chunk ByteString
ys ByteString
xs

concat :: [ByteString] -> ByteString
concat :: [ByteString] -> ByteString
concat = [ByteString] -> ByteString
to
  where
    go :: ByteString -> [ByteString] -> ByteString
go ByteString
Empty        [ByteString]
css = [ByteString] -> ByteString
to [ByteString]
css
    go (Chunk StrictByteString
c ByteString
cs) [ByteString]
css = StrictByteString -> ByteString -> ByteString
Chunk StrictByteString
c (ByteString -> [ByteString] -> ByteString
go ByteString
cs [ByteString]
css)
    to :: [ByteString] -> ByteString
to []               = ByteString
Empty
    to (ByteString
cs:[ByteString]
css)         = ByteString -> [ByteString] -> ByteString
go ByteString
cs [ByteString]
css

-- | Repeats the given ByteString n times.
times :: Integral a => a -> ByteString -> ByteString
times :: forall b. Integral b => b -> ByteString -> ByteString
times a
0 ByteString
_ = ByteString
Empty
times a
n ByteString
lbs0
  | a
n a -> a -> Bool
forall a. Ord a => a -> a -> Bool
< a
0 = String -> ByteString
forall a. HasCallStack => String -> a
error String
"stimes: non-negative multiplier expected"
  | Bool
otherwise = case ByteString
lbs0 of
    ByteString
Empty -> ByteString
Empty
    Chunk StrictByteString
bs ByteString
lbs -> StrictByteString -> ByteString -> ByteString
Chunk StrictByteString
bs (ByteString -> ByteString
go ByteString
lbs)
  where
    go :: ByteString -> ByteString
go ByteString
Empty = a -> ByteString -> ByteString
forall b. Integral b => b -> ByteString -> ByteString
times (a
na -> a -> a
forall a. Num a => a -> a -> a
-a
1) ByteString
lbs0
    go (Chunk StrictByteString
c ByteString
cs) = StrictByteString -> ByteString -> ByteString
Chunk StrictByteString
c (ByteString -> ByteString
go ByteString
cs)

------------------------------------------------------------------------
-- Conversions

-- |/O(1)/ Convert a 'S.StrictByteString' into a 'LazyByteString'.
fromStrict :: S.StrictByteString -> LazyByteString
fromStrict :: StrictByteString -> ByteString
fromStrict (S.BS ForeignPtr Word8
_ Int
0) = ByteString
Empty
fromStrict StrictByteString
bs = StrictByteString -> ByteString -> ByteString
Chunk StrictByteString
bs ByteString
Empty

-- |/O(n)/ Convert a 'LazyByteString' into a 'S.StrictByteString'.
--
-- Note that this is an /expensive/ operation that forces the whole
-- 'LazyByteString' into memory and then copies all the data. If possible, try to
-- avoid converting back and forth between strict and lazy bytestrings.
--
toStrict :: LazyByteString -> S.StrictByteString
toStrict :: ByteString -> StrictByteString
toStrict = \ByteString
cs -> ByteString -> ByteString -> StrictByteString
goLen0 ByteString
cs ByteString
cs
    -- We pass the original [ByteString] (bss0) through as an argument through
    -- goLen0, goLen1, and goLen since we will need it again in goCopy. Passing
    -- it as an explicit argument avoids capturing it in these functions'
    -- closures which would result in unnecessary closure allocation.
  where
    -- It's still possible that the result is empty
    goLen0 :: ByteString -> ByteString -> StrictByteString
goLen0 ByteString
_   ByteString
Empty                 = ForeignPtr Word8 -> Int -> StrictByteString
S.BS ForeignPtr Word8
S.nullForeignPtr Int
0
    goLen0 ByteString
cs0 (Chunk StrictByteString
c ByteString
cs)          = ByteString -> StrictByteString -> ByteString -> StrictByteString
goLen1 ByteString
cs0 StrictByteString
c ByteString
cs

    -- It's still possible that the result is a single chunk
    goLen1 :: ByteString -> StrictByteString -> ByteString -> StrictByteString
goLen1 ByteString
_   StrictByteString
bs ByteString
Empty = StrictByteString
bs
    goLen1 ByteString
cs0 (S.BS ForeignPtr Word8
_ Int
bl) (Chunk (S.BS ForeignPtr Word8
_ Int
cl) ByteString
cs) =
        ByteString -> Int -> ByteString -> StrictByteString
goLen ByteString
cs0 (String -> Int -> Int -> Int
S.checkedAdd String
"Lazy.toStrict" Int
bl Int
cl) ByteString
cs

    -- General case, just find the total length we'll need
    goLen :: ByteString -> Int -> ByteString -> StrictByteString
goLen ByteString
cs0 !Int
total (Chunk (S.BS ForeignPtr Word8
_ Int
cl) ByteString
cs) =
      ByteString -> Int -> ByteString -> StrictByteString
goLen ByteString
cs0 (String -> Int -> Int -> Int
S.checkedAdd String
"Lazy.toStrict" Int
total Int
cl) ByteString
cs
    goLen ByteString
cs0 Int
total ByteString
Empty =
      Int -> (ForeignPtr Word8 -> IO ()) -> StrictByteString
S.unsafeCreateFp Int
total ((ForeignPtr Word8 -> IO ()) -> StrictByteString)
-> (ForeignPtr Word8 -> IO ()) -> StrictByteString
forall a b. (a -> b) -> a -> b
$ \ForeignPtr Word8
ptr -> ByteString -> ForeignPtr Word8 -> IO ()
goCopy ByteString
cs0 ForeignPtr Word8
ptr

    -- Copy the data
    goCopy :: ByteString -> ForeignPtr Word8 -> IO ()
goCopy ByteString
Empty                    !ForeignPtr Word8
_   = () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
    goCopy (Chunk (S.BS ForeignPtr Word8
fp Int
len) ByteString
cs) !ForeignPtr Word8
ptr = do
      ForeignPtr Word8 -> ForeignPtr Word8 -> Int -> IO ()
S.memcpyFp ForeignPtr Word8
ptr ForeignPtr Word8
fp Int
len
      ByteString -> ForeignPtr Word8 -> IO ()
goCopy ByteString
cs (ForeignPtr Word8
ptr ForeignPtr Word8 -> Int -> ForeignPtr Word8
forall a b. ForeignPtr a -> Int -> ForeignPtr b
`S.plusForeignPtr` Int
len)
-- See the comment on Data.ByteString.Internal.concat for some background on
-- this implementation.