{-# LANGUAGE CPP, MagicHash #-}
#if __GLASGOW_HASKELL__ >= 701
{-# LANGUAGE Safe #-}
#endif

-----------------------------------------------------------------------------
-- |
-- 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
import qualified Data.ByteString.Short as T
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 = mempty
{-# INLINE empty #-}

-- | /O(1)./ A Builder taking a single byte, satisfying
--
--  * @'toLazyByteString' ('singleton' b) = 'L.singleton' b@
--
singleton :: Word8 -> Builder
singleton = 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 = mappend
{-# INLINE append #-}

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

-- | /O(1)./ A Builder taking a lazy 'L.ByteString', satisfying
--
--  * @'toLazyByteString' ('fromLazyByteString' bs) = bs@
--
fromLazyByteString :: L.ByteString -> Builder
fromLazyByteString = 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 = B.shortByteString
{-# INLINE fromShortByteString #-}
#endif

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

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

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

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

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

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

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

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

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

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

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

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

-- | Write a Int64 in little endian format
putInt64le :: Int64 -> Builder
putInt64le = 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 = Prim.primFixed Prim.wordHost
{-# INLINE putWordhost #-}

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

-- | Write a Word32 in native host order and host endianness.
-- 4 bytes will be written, unaligned.
putWord32host :: Word32 -> Builder
putWord32host = Prim.primFixed 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 = Prim.primFixed 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 = Prim.primFixed Prim.intHost
{-# INLINE putInthost #-}

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

-- | Write a Int32 in native host order and host endianness.
-- 4 bytes will be written, unaligned.
putInt32host :: Int32 -> Builder
putInt32host = Prim.primFixed 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 = Prim.primFixed Prim.int64Host
{-# INLINE putInt64host #-}


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

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

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