{-# LANGUAGE CPP, MagicHash #-}
{-# LANGUAGE Safe #-}

-----------------------------------------------------------------------------
-- |
-- Module      : Data.Binary.Builder
-- Copyright   : Lennart Kolmodin, Ross Paterson
-- License     : BSD3-style (see LICENSE)
--
-- Maintainer  : Lennart Kolmodin <kolmodin@gmail.com>
-- Stability   : experimental
-- Portability : portable to Hugs and GHC
--
-- Efficient constructions of lazy bytestrings.
--
-- This now re-exports 'Data.ByteString.Lazy.Builder'.
--
-----------------------------------------------------------------------------

module Data.Binary.Builder (
    -- * The Builder type
      Builder
    , toLazyByteString

    -- * Constructing Builders
    , empty
    , singleton
    , append
    , fromByteString        -- :: S.ByteString -> Builder
    , fromLazyByteString    -- :: L.ByteString -> Builder
#if MIN_VERSION_bytestring(0,10,4)
    , fromShortByteString   -- :: T.ByteString -> Builder
#endif
    -- * Flushing the buffer state
    , flush

    -- * Derived Builders
    -- ** Big-endian writes
    , putWord16be           -- :: Word16 -> Builder
    , putWord32be           -- :: Word32 -> Builder
    , putWord64be           -- :: Word64 -> Builder
    , putInt16be            -- :: Int16 -> Builder
    , putInt32be            -- :: Int32 -> Builder
    , putInt64be            -- :: Int64 -> Builder

    -- ** Little-endian writes
    , putWord16le           -- :: Word16 -> Builder
    , putWord32le           -- :: Word32 -> Builder
    , putWord64le           -- :: Word64 -> Builder
    , putInt16le            -- :: Int16 -> Builder
    , putInt32le            -- :: Int32 -> Builder
    , putInt64le            -- :: Int64 -> Builder

    -- ** Host-endian, unaligned writes
    , putWordhost           -- :: Word -> Builder
    , putWord16host         -- :: Word16 -> Builder
    , putWord32host         -- :: Word32 -> Builder
    , putWord64host         -- :: Word64 -> Builder
    , putInthost            -- :: Int -> Builder
    , putInt16host          -- :: Int16 -> Builder
    , putInt32host          -- :: Int32 -> Builder
    , putInt64host          -- :: Int64 -> Builder

      -- ** Unicode
    , putCharUtf8
    , putStringUtf8
    ) where

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

#if MIN_VERSION_bytestring(0,10,4)
import qualified Data.ByteString.Short as T
#endif

import qualified Data.ByteString.Builder as B
import qualified Data.ByteString.Builder.Prim as Prim
import Data.ByteString.Builder ( Builder, toLazyByteString )
import Data.ByteString.Builder.Extra ( flush )
import Data.Monoid
import Data.Word
import Data.Int
import Prelude -- Silence AMP warning.

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

-- | /O(1)./ The empty Builder, satisfying
--
--  * @'toLazyByteString' 'empty' = 'L.empty'@
--
empty :: Builder
empty :: Builder
empty = Builder
forall a. Monoid a => a
mempty
{-# INLINE empty #-}

-- | /O(1)./ A Builder taking a single byte, satisfying
--
--  * @'toLazyByteString' ('singleton' b) = 'L.singleton' b@
--
singleton :: Word8 -> Builder
singleton :: Word8 -> Builder
singleton = Word8 -> Builder
B.word8
{-# INLINE singleton #-}

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

-- | /O(1)./ The concatenation of two Builders, an associative operation
-- with identity 'empty', satisfying
--
--  * @'toLazyByteString' ('append' x y) = 'L.append' ('toLazyByteString' x) ('toLazyByteString' y)@
--
append :: Builder -> Builder -> Builder
append :: Builder -> Builder -> Builder
append = Builder -> Builder -> Builder
forall a. Monoid a => a -> a -> a
mappend
{-# INLINE append #-}

-- | /O(1)./ A Builder taking a 'S.ByteString', satisfying
--
--  * @'toLazyByteString' ('fromByteString' bs) = 'L.fromChunks' [bs]@
--
fromByteString :: S.ByteString -> Builder
fromByteString :: ByteString -> Builder
fromByteString = ByteString -> Builder
B.byteString
{-# INLINE fromByteString #-}

-- | /O(1)./ A Builder taking a lazy 'L.ByteString', satisfying
--
--  * @'toLazyByteString' ('fromLazyByteString' bs) = bs@
--
fromLazyByteString :: L.ByteString -> Builder
fromLazyByteString :: ByteString -> Builder
fromLazyByteString = ByteString -> Builder
B.lazyByteString
{-# INLINE fromLazyByteString #-}

#if MIN_VERSION_bytestring(0,10,4)
-- | /O(n)./ A builder taking 'T.ShortByteString' and copy it to a Builder,
-- satisfying
--
-- * @'toLazyByteString' ('fromShortByteString' bs) = 'L.fromChunks' ['T.fromShort' bs]
fromShortByteString :: T.ShortByteString -> Builder
fromShortByteString :: ShortByteString -> Builder
fromShortByteString = ShortByteString -> Builder
B.shortByteString
{-# INLINE fromShortByteString #-}
#endif

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

-- | Write a Word16 in big endian format
putWord16be :: Word16 -> Builder
putWord16be :: Word16 -> Builder
putWord16be = Word16 -> Builder
B.word16BE
{-# INLINE putWord16be #-}

-- | Write a Word16 in little endian format
putWord16le :: Word16 -> Builder
putWord16le :: Word16 -> Builder
putWord16le = Word16 -> Builder
B.word16LE
{-# INLINE putWord16le #-}

-- | Write a Word32 in big endian format
putWord32be :: Word32 -> Builder
putWord32be :: Word32 -> Builder
putWord32be = Word32 -> Builder
B.word32BE
{-# INLINE putWord32be #-}

-- | Write a Word32 in little endian format
putWord32le :: Word32 -> Builder
putWord32le :: Word32 -> Builder
putWord32le = Word32 -> Builder
B.word32LE
{-# INLINE putWord32le #-}

-- | Write a Word64 in big endian format
putWord64be :: Word64 -> Builder
putWord64be :: Word64 -> Builder
putWord64be = Word64 -> Builder
B.word64BE
{-# INLINE putWord64be #-}

-- | Write a Word64 in little endian format
putWord64le :: Word64 -> Builder
putWord64le :: Word64 -> Builder
putWord64le = Word64 -> Builder
B.word64LE
{-# INLINE putWord64le #-}

-- | Write a Int16 in big endian format
putInt16be :: Int16 -> Builder
putInt16be :: Int16 -> Builder
putInt16be = Int16 -> Builder
B.int16BE
{-# INLINE putInt16be #-}

-- | Write a Int16 in little endian format
putInt16le :: Int16 -> Builder
putInt16le :: Int16 -> Builder
putInt16le = Int16 -> Builder
B.int16LE
{-# INLINE putInt16le #-}

-- | Write a Int32 in big endian format
putInt32be :: Int32 -> Builder
putInt32be :: Int32 -> Builder
putInt32be = Int32 -> Builder
B.int32BE
{-# INLINE putInt32be #-}

-- | Write a Int32 in little endian format
putInt32le :: Int32 -> Builder
putInt32le :: Int32 -> Builder
putInt32le = Int32 -> Builder
B.int32LE
{-# INLINE putInt32le #-}

-- | Write a Int64 in big endian format
putInt64be :: Int64 -> Builder
putInt64be :: Int64 -> Builder
putInt64be = Int64 -> Builder
B.int64BE

-- | Write a Int64 in little endian format
putInt64le :: Int64 -> Builder
putInt64le :: Int64 -> Builder
putInt64le = Int64 -> Builder
B.int64LE


------------------------------------------------------------------------
-- Unaligned, word size ops

-- | /O(1)./ A Builder taking a single native machine word. The word is
-- written 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 written this way are not portable to
-- different endian or word sized machines, without conversion.
--
putWordhost :: Word -> Builder
putWordhost :: Word -> Builder
putWordhost = FixedPrim Word -> Word -> Builder
forall a. FixedPrim a -> a -> Builder
Prim.primFixed FixedPrim Word
Prim.wordHost
{-# INLINE putWordhost #-}

-- | Write a Word16 in native host order and host endianness.
-- 2 bytes will be written, unaligned.
putWord16host :: Word16 -> Builder
putWord16host :: Word16 -> Builder
putWord16host = FixedPrim Word16 -> Word16 -> Builder
forall a. FixedPrim a -> a -> Builder
Prim.primFixed FixedPrim Word16
Prim.word16Host
{-# INLINE putWord16host #-}

-- | Write a Word32 in native host order and host endianness.
-- 4 bytes will be written, unaligned.
putWord32host :: Word32 -> Builder
putWord32host :: Word32 -> Builder
putWord32host = FixedPrim Word32 -> Word32 -> Builder
forall a. FixedPrim a -> a -> Builder
Prim.primFixed FixedPrim Word32
Prim.word32Host
{-# INLINE putWord32host #-}

-- | Write a Word64 in native host order.
-- On a 32 bit machine we write two host order Word32s, in big endian form.
-- 8 bytes will be written, unaligned.
putWord64host :: Word64 -> Builder
putWord64host :: Word64 -> Builder
putWord64host = FixedPrim Word64 -> Word64 -> Builder
forall a. FixedPrim a -> a -> Builder
Prim.primFixed FixedPrim Word64
Prim.word64Host
{-# INLINE putWord64host #-}

-- | /O(1)./ A Builder taking a single native machine word. The word is
-- written 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 written this way are not portable to
-- different endian or word sized machines, without conversion.
--
putInthost :: Int -> Builder
putInthost :: Int -> Builder
putInthost = FixedPrim Int -> Int -> Builder
forall a. FixedPrim a -> a -> Builder
Prim.primFixed FixedPrim Int
Prim.intHost
{-# INLINE putInthost #-}

-- | Write a Int16 in native host order and host endianness.
-- 2 bytes will be written, unaligned.
putInt16host :: Int16 -> Builder
putInt16host :: Int16 -> Builder
putInt16host = FixedPrim Int16 -> Int16 -> Builder
forall a. FixedPrim a -> a -> Builder
Prim.primFixed FixedPrim Int16
Prim.int16Host
{-# INLINE putInt16host #-}

-- | Write a Int32 in native host order and host endianness.
-- 4 bytes will be written, unaligned.
putInt32host :: Int32 -> Builder
putInt32host :: Int32 -> Builder
putInt32host = FixedPrim Int32 -> Int32 -> Builder
forall a. FixedPrim a -> a -> Builder
Prim.primFixed FixedPrim Int32
Prim.int32Host
{-# INLINE putInt32host #-}

-- | Write a Int64 in native host order.
-- On a 32 bit machine we write two host order Int32s, in big endian form.
-- 8 bytes will be written, unaligned.
putInt64host :: Int64 -> Builder
putInt64host :: Int64 -> Builder
putInt64host = FixedPrim Int64 -> Int64 -> Builder
forall a. FixedPrim a -> a -> Builder
Prim.primFixed FixedPrim Int64
Prim.int64Host
{-# INLINE putInt64host #-}


------------------------------------------------------------------------
-- Unicode

-- | Write a character using UTF-8 encoding.
putCharUtf8 :: Char -> Builder
putCharUtf8 :: Char -> Builder
putCharUtf8 = BoundedPrim Char -> Char -> Builder
forall a. BoundedPrim a -> a -> Builder
Prim.primBounded BoundedPrim Char
Prim.charUtf8
{-# INLINE putCharUtf8 #-}

-- | Write a String using UTF-8 encoding.
putStringUtf8 :: String -> Builder
putStringUtf8 :: String -> Builder
putStringUtf8 = BoundedPrim Char -> String -> Builder
forall a. BoundedPrim a -> [a] -> Builder
Prim.primMapListBounded BoundedPrim Char
Prim.charUtf8
{-# INLINE putStringUtf8 #-}