    {-# LANGUAGE BangPatterns #-}
    {-# LANGUAGE Trustworthy #-}
    -----------------------------------------------------------------------------
    -- | Copyright : (c) 2010      Jasper Van der Jeugt
    --               (c) 2010-2011 Simon Meier
    -- License     : BSD3-style (see LICENSE)
    --
    -- Maintainer  : Simon Meier <iridcode@gmail.com>
    -- Portability : GHC
    --
    -- Extra functions for creating and executing 'Builder's. They are intended
    -- for application-specific fine-tuning the performance of 'Builder's.
    --
    -----------------------------------------------------------------------------
    module Data.ByteString.Builder.Extra
        (
        -- * Execution strategies
          toLazyByteStringWith
        , AllocationStrategy
        , safeStrategy
        , untrimmedStrategy
        , smallChunkSize
        , defaultChunkSize

        -- * Controlling chunk boundaries
        , byteStringCopy
        , byteStringInsert
        , byteStringThreshold

        , lazyByteStringCopy
        , lazyByteStringInsert
        , lazyByteStringThreshold

        , flush

        -- * Low level execution
        , BufferWriter
        , Next(..)
        , runBuilder

        -- * Host-specific binary encodings
        , intHost
        , int16Host
        , int32Host
        , int64Host

        , wordHost
        , word16Host
        , word32Host
        , word64Host

        , floatHost
        , doubleHost

        ) where

    import Data.ByteString.Builder.Internal
             ( Builder, toLazyByteStringWith
             , AllocationStrategy, safeStrategy, untrimmedStrategy
             , smallChunkSize, defaultChunkSize, flush
             , byteStringCopy, byteStringInsert, byteStringThreshold
             , lazyByteStringCopy, lazyByteStringInsert, lazyByteStringThreshold )

    import qualified Data.ByteString.Builder.Internal as I
    import qualified Data.ByteString.Builder.Prim  as P
    import qualified Data.ByteString.Internal      as S

    import Foreign

    ------------------------------------------------------------------------------
    -- Builder execution public API
    ------------------------------------------------------------------------------

    -- | A 'BufferWriter' represents the result of running a 'Builder'.
    -- It unfolds as a sequence of chunks of data. These chunks come in two forms:
    --
    --  * an IO action for writing the Builder's data into a user-supplied memory
    --    buffer.
    --
    --  * a pre-existing chunks of data represented by a strict 'S.ByteString'
    --
    -- While this is rather low level, it provides you with full flexibility in
    -- how the data is written out.
    --
    -- The 'BufferWriter' itself is an IO action: you supply it with a buffer
    -- (as a pointer and length) and it will write data into the buffer.
    -- It returns a number indicating how many bytes were actually written
    -- (which can be @0@). It also returns a 'Next' which describes what
    -- comes next.
    --
    type BufferWriter = Ptr Word8 -> Int -> IO (Int, Next)

    -- | After running a 'BufferWriter' action there are three possibilities for
    -- what comes next:
    --
    data Next =
         -- | This means we're all done. All the builder data has now been written.
         Done

         -- | This indicates that there may be more data to write. It
         -- gives you the next 'BufferWriter' action. You should call that action
         -- with an appropriate buffer. The int indicates the /minimum/ buffer size
         -- required by the next 'BufferWriter' action. That is, if you call the next
         -- action you /must/ supply it with a buffer length of at least this size.
       | More   !Int          BufferWriter

         -- | In addition to the data that has just been written into your buffer
         -- by the 'BufferWriter' action, it gives you a pre-existing chunk
         -- of data as a 'S.ByteString'. It also gives you the following 'BufferWriter'
         -- action. It is safe to run this following action using a buffer with as
         -- much free space as was left by the previous run action.
       | Chunk  !S.ByteString BufferWriter

    -- | Turn a 'Builder' into its initial 'BufferWriter' action.
    --
    runBuilder :: Builder -> BufferWriter
    runBuilder :: Builder -> BufferWriter
    runBuilder = BuildStep () -> BufferWriter
    run (BuildStep () -> BufferWriter)
    -> (Builder -> BuildStep ()) -> Builder -> BufferWriter
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Builder -> BuildStep ()
    I.runBuilder
      where
        bytesWritten :: Ptr b -> Ptr a -> Int
    bytesWritten Ptr b
    startPtr Ptr a
    endPtr = Ptr a
    endPtr Ptr a -> Ptr b -> Int
    forall a b. Ptr a -> Ptr b -> Int
    `minusPtr` Ptr b
    startPtr

        run :: I.BuildStep () -> BufferWriter
        run :: BuildStep () -> BufferWriter
    run BuildStep ()
    step = \Ptr Word8
    buf Int
    len ->
          let doneH :: Ptr a -> () -> m (Int, Next)
    doneH Ptr a
    endPtr () =
                let !wc :: Int
    wc  = Ptr Word8 -> Ptr a -> Int
    forall a b. Ptr a -> Ptr b -> Int
    bytesWritten Ptr Word8
    buf Ptr a
    endPtr
                    next :: Next
    next = Next
    Done
                 in (Int, Next) -> m (Int, Next)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Int
    wc, Next
    next)

              bufferFullH :: Ptr a -> Int -> BuildStep () -> m (Int, Next)
    bufferFullH Ptr a
    endPtr Int
    minReq BuildStep ()
    step' =
                let !wc :: Int
    wc  = Ptr Word8 -> Ptr a -> Int
    forall a b. Ptr a -> Ptr b -> Int
    bytesWritten Ptr Word8
    buf Ptr a
    endPtr
                    next :: Next
    next = Int -> BufferWriter -> Next
    More Int
    minReq (BuildStep () -> BufferWriter
    run BuildStep ()
    step')
                 in (Int, Next) -> m (Int, Next)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Int
    wc, Next
    next)

              insertChunkH :: Ptr a -> ByteString -> BuildStep () -> m (Int, Next)
    insertChunkH Ptr a
    endPtr ByteString
    bs BuildStep ()
    step' =
                let !wc :: Int
    wc  = Ptr Word8 -> Ptr a -> Int
    forall a b. Ptr a -> Ptr b -> Int
    bytesWritten Ptr Word8
    buf Ptr a
    endPtr
                    next :: Next
    next = ByteString -> BufferWriter -> Next
    Chunk ByteString
    bs (BuildStep () -> BufferWriter
    run BuildStep ()
    step')
                 in (Int, Next) -> m (Int, Next)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Int
    wc, Next
    next)

              br :: BufferRange
    br = Ptr Word8 -> Ptr Word8 -> BufferRange
    I.BufferRange Ptr Word8
    buf (Ptr Word8
    buf Ptr Word8 -> Int -> Ptr Word8
    forall a b. Ptr a -> Int -> Ptr b
    `plusPtr` Int
    len)

          in BuildStep ()
    -> (Ptr Word8 -> () -> IO (Int, Next))
    -> (Ptr Word8 -> Int -> BuildStep () -> IO (Int, Next))
    -> (Ptr Word8 -> ByteString -> BuildStep () -> IO (Int, Next))
    -> BufferRange
    -> IO (Int, Next)
    forall a b.
    BuildStep a
    -> (Ptr Word8 -> a -> IO b)
    -> (Ptr Word8 -> Int -> BuildStep a -> IO b)
    -> (Ptr Word8 -> ByteString -> BuildStep a -> IO b)
    -> BufferRange
    -> IO b
    I.fillWithBuildStep BuildStep ()
    step Ptr Word8 -> () -> IO (Int, Next)
    forall {m :: * -> *} {a}. Monad m => Ptr a -> () -> m (Int, Next)
    doneH Ptr Word8 -> Int -> BuildStep () -> IO (Int, Next)
    forall {m :: * -> *} {a}.
    Monad m =>
    Ptr a -> Int -> BuildStep () -> m (Int, Next)
    bufferFullH Ptr Word8 -> ByteString -> BuildStep () -> IO (Int, Next)
    forall {m :: * -> *} {a}.
    Monad m =>
    Ptr a -> ByteString -> BuildStep () -> m (Int, Next)
    insertChunkH BufferRange
    br

    ------------------------------------------------------------------------------
    -- Host-specific encodings
    ------------------------------------------------------------------------------

    -- | Encode a single native machine 'Int'. The 'Int' is encoded in host order,
    -- host endian form, for the machine you're on. On a 64 bit machine the 'Int'
    -- is an 8 byte value, on a 32 bit machine, 4 bytes. Values encoded this way
    -- are not portable to different endian or int sized machines, without
    -- conversion.
    --
    {-# INLINE intHost #-}
    intHost :: Int -> Builder
    intHost :: Int -> Builder
    intHost = FixedPrim Int -> Int -> Builder
    forall a. FixedPrim a -> a -> Builder
    P.primFixed FixedPrim Int
    P.intHost

    -- | Encode a 'Int16' in native host order and host endianness.
    {-# INLINE int16Host #-}
    int16Host :: Int16 -> Builder
    int16Host :: Int16 -> Builder
    int16Host = FixedPrim Int16 -> Int16 -> Builder
    forall a. FixedPrim a -> a -> Builder
    P.primFixed FixedPrim Int16
    P.int16Host

    -- | Encode a 'Int32' in native host order and host endianness.
    {-# INLINE int32Host #-}
    int32Host :: Int32 -> Builder
    int32Host :: Int32 -> Builder
    int32Host = FixedPrim Int32 -> Int32 -> Builder
    forall a. FixedPrim a -> a -> Builder
    P.primFixed FixedPrim Int32
    P.int32Host

    -- | Encode a 'Int64' in native host order and host endianness.
    {-# INLINE int64Host #-}
    int64Host :: Int64 -> Builder
    int64Host :: Int64 -> Builder
    int64Host = FixedPrim Int64 -> Int64 -> Builder
    forall a. FixedPrim a -> a -> Builder
    P.primFixed FixedPrim Int64
    P.int64Host

    -- | Encode a single native machine 'Word'. The 'Word' is encoded in host order,
    -- host endian form, for the machine you're on. On a 64 bit machine the 'Word'
    -- is an 8 byte value, on a 32 bit machine, 4 bytes. Values encoded this way
    -- are not portable to different endian or word sized machines, without
    -- conversion.
    --
    {-# INLINE wordHost #-}
    wordHost :: Word -> Builder
    wordHost :: Word -> Builder
    wordHost = FixedPrim Word -> Word -> Builder
    forall a. FixedPrim a -> a -> Builder
    P.primFixed FixedPrim Word
    P.wordHost

    -- | Encode a 'Word16' in native host order and host endianness.
    {-# INLINE word16Host #-}
    word16Host :: Word16 -> Builder
    word16Host :: Word16 -> Builder
    word16Host = FixedPrim Word16 -> Word16 -> Builder
    forall a. FixedPrim a -> a -> Builder
    P.primFixed FixedPrim Word16
    P.word16Host

    -- | Encode a 'Word32' in native host order and host endianness.
    {-# INLINE word32Host #-}
    word32Host :: Word32 -> Builder
    word32Host :: Word32 -> Builder
    word32Host = FixedPrim Word32 -> Word32 -> Builder
    forall a. FixedPrim a -> a -> Builder
    P.primFixed FixedPrim Word32
    P.word32Host

    -- | Encode a 'Word64' in native host order and host endianness.
    {-# INLINE word64Host #-}
    word64Host :: Word64 -> Builder
    word64Host :: Word64 -> Builder
    word64Host = FixedPrim Word64 -> Word64 -> Builder
    forall a. FixedPrim a -> a -> Builder
    P.primFixed FixedPrim Word64
    P.word64Host

    -- | Encode a 'Float' in native host order. Values encoded this way are not
    -- portable to different endian machines, without conversion.
    {-# INLINE floatHost #-}
    floatHost :: Float -> Builder
    floatHost :: Float -> Builder
    floatHost = FixedPrim Float -> Float -> Builder
    forall a. FixedPrim a -> a -> Builder
    P.primFixed FixedPrim Float
    P.floatHost

    -- | Encode a 'Double' in native host order.
    {-# INLINE doubleHost #-}
    doubleHost :: Double -> Builder
    doubleHost :: Double -> Builder
    doubleHost = FixedPrim Double -> Double -> Builder
    forall a. FixedPrim a -> a -> Builder
    P.primFixed FixedPrim Double
    P.doubleHost