    {-# LANGUAGE CPP, BangPatterns, OverloadedStrings #-}

    #ifdef USE_MONO_PAT_BINDS
    {-# LANGUAGE MonoPatBinds #-}
    #endif

    {-# OPTIONS_GHC -fno-warn-unused-imports #-}

    -- |
    -- Module      : Blaze.ByteString.Builder.ByteString
    -- Copyright   : (c) 2010 Jasper Van der Jeugt & Simon Meier
    -- License     : BSD3-style (see LICENSE)
    --
    -- Maintainer  : Simon Meier <iridcode@gmail.com>
    -- Stability   : experimental
    -- Portability : tested on GHC only
    --
    -- 'Write's and 'Builder's for strict and lazy bytestrings.
    --
    -- We assume the following qualified imports in order to differentiate between
    -- strict and lazy bytestrings in the code examples.
    --
    -- > import qualified Data.ByteString      as S
    -- > import qualified Data.ByteString.Lazy as L
    --
    module Blaze.ByteString.Builder.ByteString
        (
        -- * Strict bytestrings
          writeByteString
        , fromByteString
        , fromByteStringWith
        , copyByteString
        , insertByteString

        -- * Lazy bytestrings
        , fromLazyByteString
        , fromLazyByteStringWith
        , copyLazyByteString
        , insertLazyByteString

        ) where

    import           Blaze.ByteString.Builder.Internal hiding (insertByteString)
    import qualified Blaze.ByteString.Builder.Internal as I   (insertByteString)

    #ifdef HAS_FOREIGN_UNSAFE_MODULE
    import Foreign                   (withForeignPtr, touchForeignPtr, copyBytes, plusPtr, minusPtr)
    import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
    #else
    import Foreign                   (unsafeForeignPtrToPtr, withForeignPtr, touchForeignPtr, copyBytes, plusPtr, minusPtr)
    #endif

    import Data.Monoid

    import qualified Data.ByteString      as S
    import qualified Data.ByteString.Lazy as L

    #ifdef BYTESTRING_IN_BASE
    import qualified Data.ByteString.Base as S
    import qualified Data.ByteString.Lazy.Base as L -- FIXME: check if this is the right module
    #else
    import qualified Data.ByteString.Internal      as S
    import qualified Data.ByteString.Lazy.Internal as L
    #endif

    ------------------------------------------------------------------------------
    -- Strict ByteStrings
    ------------------------------------------------------------------------------

    -- | Write a strict 'S.ByteString' to a buffer.
    --
    writeByteString :: S.ByteString -> Write
    writeByteString bs = exactWrite l io
      where
      (fptr, o, l) = S.toForeignPtr bs
      io pf = withForeignPtr fptr $ \p -> copyBytes pf (p `plusPtr` o) l
    {-# INLINE writeByteString #-}

    -- | Smart serialization of a strict bytestring.
    --
    -- @'fromByteString' = 'fromByteStringWith' 'defaultMaximalCopySize'@
    --
    -- Use this function to serialize strict bytestrings. It guarantees an
    -- average chunk size of 4kb, which has been shown to be a reasonable size in
    -- benchmarks. Note that the check whether to copy or to insert is (almost)
    -- free as the builder performance is mostly memory-bound.
    --
    -- If you statically know that copying or inserting the strict bytestring is
    -- always the best choice, then you can use the 'copyByteString' or
    -- 'insertByteString' functions.
    --
    fromByteString :: S.ByteString -> Builder
    fromByteString = fromByteStringWith defaultMaximalCopySize
    {-# INLINE fromByteString #-}

    -- | @fromByteStringWith maximalCopySize bs@ serializes the strict bytestring
    -- @bs@ according to the following rules.
    --
    --   [@S.length bs <= maximalCopySize@:] @bs@ is copied to the output buffer.
    --
    --   [@S.length bs >  maximalCopySize@:] @bs@ the output buffer is flushed and
    --   @bs@ is inserted directly as separate chunk in the output stream.
    --
    -- These rules guarantee that average chunk size in the output stream is at
    -- least half the @maximalCopySize@.
    --
    fromByteStringWith :: Int          -- ^ Maximal number of bytes to copy.
                       -> S.ByteString -- ^ Strict 'S.ByteString' to serialize.
                       -> Builder      -- ^ Resulting 'Builder'.
    fromByteStringWith maxCopySize =
        \bs -> fromBuildStepCont $ step bs
      where
        step !bs !k br@(BufRange !op _)
          | maxCopySize < S.length bs = return $ I.insertByteString op bs k
          | otherwise                 = copyByteStringStep bs k br
    {-# INLINE fromByteStringWith #-}

    -- | @copyByteString bs@ serialize the strict bytestring @bs@ by copying it to
    -- the output buffer.
    --
    -- Use this function to serialize strict bytestrings that are statically known
    -- to be smallish (@<= 4kb@).
    --
    copyByteString :: S.ByteString -> Builder
    copyByteString = \bs -> fromBuildStepCont $ copyByteStringStep bs
    {-# INLINE copyByteString #-}

    copyByteStringStep :: S.ByteString
                       -> (BufRange -> IO (BuildSignal a))
                       -> (BufRange -> IO (BuildSignal a))
    copyByteStringStep (S.PS ifp ioff isize) !k =
        goBS (unsafeForeignPtrToPtr ifp `plusPtr` ioff)
      where
        !ipe = unsafeForeignPtrToPtr ifp `plusPtr` (ioff + isize)
        goBS !ip !(BufRange op ope)
          | inpRemaining <= outRemaining = do
              copyBytes op ip inpRemaining
              touchForeignPtr ifp -- input consumed: OK to release from here
              let !br' = BufRange (op `plusPtr` inpRemaining) ope
              k br'
          | otherwise = do
              copyBytes op ip outRemaining
              let !ip' = ip `plusPtr` outRemaining
              return $ bufferFull 1 ope (goBS ip')
          where
            outRemaining = ope `minusPtr` op
            inpRemaining = ipe `minusPtr` ip
    {-# INLINE copyByteStringStep #-}

    -- | @insertByteString bs@ serializes the strict bytestring @bs@ by inserting
    -- it directly as a chunk of the output stream.
    --
    -- Note that this implies flushing the output buffer; even if it contains just
    -- a single byte. Hence, you should use this operation only for large (@> 8kb@)
    -- bytestrings, as otherwise the resulting output stream may be too fragmented
    -- to be processed efficiently.
    --
    insertByteString :: S.ByteString -> Builder
    insertByteString =
        \bs -> fromBuildStepCont $ step bs
      where
        step !bs !k !(BufRange op _) = return $ I.insertByteString op bs k
    {-# INLINE insertByteString #-}

    -- Lazy bytestrings
    ------------------------------------------------------------------------------

    -- | /O(n)/. Smart serialization of a lazy bytestring.
    --
    -- @'fromLazyByteString' = 'fromLazyByteStringWith' 'defaultMaximalCopySize'@
    --
    -- Use this function to serialize lazy bytestrings. It guarantees an average
    -- chunk size of 4kb, which has been shown to be a reasonable size in
    -- benchmarks. Note that the check whether to copy or to insert is (almost)
    -- free as the builder performance is mostly memory-bound.
    --
    -- If you statically know that copying or inserting /all/ chunks of the lazy
    -- bytestring is always the best choice, then you can use the
    -- 'copyLazyByteString' or 'insertLazyByteString' functions.
    --
    fromLazyByteString :: L.ByteString -> Builder
    fromLazyByteString = fromLazyByteStringWith defaultMaximalCopySize
    {-# INLINE fromLazyByteString #-}

    -- | /O(n)/. Serialize a lazy bytestring chunk-wise according to the same rules
    -- as in 'fromByteStringWith'.
    --
    -- Semantically, it holds that
    --
    -- >   fromLazyByteStringWith maxCopySize
    -- > = mconcat . map (fromByteStringWith maxCopySize) . L.toChunks
    --
    -- However, the left-hand-side is much more efficient, as it moves the
    -- end-of-buffer pointer out of the inner loop and provides the compiler with
    -- more strictness information.
    --
    fromLazyByteStringWith :: Int          -- ^ Maximal number of bytes to copy.
                           -> L.ByteString -- ^ Lazy 'L.ByteString' to serialize.
                           -> Builder      -- ^ Resulting 'Builder'.
    fromLazyByteStringWith maxCopySize =
      L.foldrChunks (\bs b -> fromByteStringWith maxCopySize bs `mappend` b) mempty
    {-# INLINE fromLazyByteStringWith #-}

    -- | /O(n)/. Serialize a lazy bytestring by copying /all/ chunks sequentially
    -- to the output buffer.
    --
    -- See 'copyByteString' for usage considerations.
    --
    copyLazyByteString :: L.ByteString -> Builder
    copyLazyByteString =
      L.foldrChunks (\bs b -> copyByteString bs `mappend` b) mempty
    {-# INLINE copyLazyByteString #-}

    -- | /O(n)/. Serialize a lazy bytestring by inserting /all/ its chunks directly
    -- into the output stream.
    --
    -- See 'insertByteString' for usage considerations.
    --
    -- For library developers, see the 'ModifyChunks' build signal, if you
    -- need an /O(1)/ lazy bytestring insert based on difference lists.
    --
    insertLazyByteString :: L.ByteString -> Builder
    insertLazyByteString =
      L.foldrChunks (\bs b -> insertByteString bs `mappend` b) mempty
    {-# INLINE insertLazyByteString #-}