content: {-# LANGUAGE CPP #-}
  
  -- |
  -- Module      : Codec.CBOR.Encoding
  -- Copyright   : (c) Duncan Coutts 2015-2017
  -- License     : BSD3-style (see LICENSE.txt)
  --
  -- Maintainer  : duncan@community.haskell.org
  -- Stability   : experimental
  -- Portability : non-portable (GHC extensions)
  --
  -- High level API for encoding values, for later serialization into
  -- CBOR binary format, using a 'Monoid' based interface.
  --
  module Codec.CBOR.Encoding
    ( -- * Encoding implementation
      Encoding(..)             -- :: *
    , Tokens(..)               -- :: *
  
      -- * 'Encoding' API for serialisation
    , encodeWord               -- :: Word -> Encoding
    , encodeWord8              -- :: Word8 -> Encoding
    , encodeWord16             -- :: Word16 -> Encoding
    , encodeWord32             -- :: Word32 -> Encoding
    , encodeWord64             -- :: Word64 -> Encoding
    , encodeInt                -- :: Int -> Encoding
    , encodeInt8               -- :: Int8 -> Encoding
    , encodeInt16              -- :: Int16 -> Encoding
    , encodeInt32              -- :: Int32 -> Encoding
    , encodeInt64              -- :: Int64 -> Encoding
    , encodeInteger            -- :: Integer -> Encoding
    , encodeBytes              -- :: B.ByteString -> Encoding
    , encodeBytesIndef         -- :: Encoding
    , encodeByteArray          -- :: ByteArray -> Encoding
    , encodeString             -- :: T.Text -> Encoding
    , encodeStringIndef        -- :: Encoding
    , encodeUtf8ByteArray      -- :: ByteArray -> Encoding
    , encodeListLen            -- :: Word -> Encoding
    , encodeListLenIndef       -- :: Encoding
    , encodeMapLen             -- :: Word -> Encoding
    , encodeMapLenIndef        -- :: Encoding
    , encodeBreak              -- :: Encoding
    , encodeTag                -- :: Word -> Encoding
    , encodeTag64              -- :: Word64 -> Encoding
    , encodeBool               -- :: Bool -> Encoding
    , encodeUndef              -- :: Encoding
    , encodeNull               -- :: Encoding
    , encodeSimple             -- :: Word8 -> Encoding
    , encodeFloat16            -- :: Float -> Encoding
    , encodeFloat              -- :: Float -> Encoding
    , encodeDouble             -- :: Double -> Encoding
    , encodePreEncoded         -- :: B.ByteString -> Encoding
    ) where
  
  #include "cbor.h"
  
  import           Data.Int
  import           Data.Word
  import           Data.Semigroup
  
  import qualified Data.ByteString as B
  import qualified Data.Text       as T
  
  import           Codec.CBOR.ByteArray.Sliced (SlicedByteArray)
  
  import           Prelude         hiding (encodeFloat)
  
  import {-# SOURCE #-} qualified Codec.CBOR.FlatTerm as FlatTerm
  
  -- | An intermediate form used during serialisation, specified as a
  -- 'Monoid'. It supports efficient concatenation, and is equivalent
  -- to a specialised 'Data.Monoid.Endo' 'Tokens' type.
  --
  -- It is used for the stage in serialisation where we flatten out the
  -- Haskell data structure but it is independent of any specific
  -- external binary or text format.
  --
  -- Traditionally, to build any arbitrary 'Encoding' value, you specify
  -- larger structures from smaller ones and append the small ones together
  -- using 'Data.Monoid.mconcat'.
  --
  -- @since 0.2.0.0
  newtype Encoding = Encoding (Tokens -> Tokens)
  
  instance Show Encoding where
    show = show . FlatTerm.toFlatTerm
  
  -- | A flattened representation of a term, which is independent
  -- of any underlying binary representation, but which we later
  -- serialise into CBOR format.
  --
  -- @since 0.2.0.0
  data Tokens =
  
      -- Positive and negative integers (type 0,1)
        TkWord     {-# UNPACK #-} !Word         Tokens
      | TkWord64   {-# UNPACK #-} !Word64       Tokens
        -- convenience for either positive or negative
      | TkInt      {-# UNPACK #-} !Int          Tokens
      | TkInt64    {-# UNPACK #-} !Int64        Tokens
  
      -- Bytes and string (type 2,3)
      | TkBytes         {-# UNPACK #-} !B.ByteString    Tokens
      | TkBytesBegin                                    Tokens
      | TkByteArray     {-# UNPACK #-} !SlicedByteArray Tokens
      | TkString        {-# UNPACK #-} !T.Text          Tokens
      | TkUtf8ByteArray {-# UNPACK #-} !SlicedByteArray Tokens
      | TkStringBegin                                   Tokens
  
      -- Structures (type 4,5)
      | TkListLen  {-# UNPACK #-} !Word         Tokens
      | TkListBegin                             Tokens
      | TkMapLen   {-# UNPACK #-} !Word         Tokens
      | TkMapBegin                              Tokens
  
      -- Tagged values (type 6)
      | TkTag      {-# UNPACK #-} !Word         Tokens
      | TkTag64    {-# UNPACK #-} !Word64       Tokens
      | TkInteger                 !Integer      Tokens
  
      -- Simple and floats (type 7)
      | TkNull                                  Tokens
      | TkUndef                                 Tokens
      | TkBool                    !Bool         Tokens
      | TkSimple   {-# UNPACK #-} !Word8        Tokens
      | TkFloat16  {-# UNPACK #-} !Float        Tokens
      | TkFloat32  {-# UNPACK #-} !Float        Tokens
      | TkFloat64  {-# UNPACK #-} !Double       Tokens
      | TkBreak                                 Tokens
  
      -- Special
      | TkEncoded  {-# UNPACK #-} !B.ByteString Tokens
  
      | TkEnd
      deriving (Show,Eq)
  
  -- | @since 0.2.0.0
  instance Semigroup Encoding where
    Encoding b1 <> Encoding b2 = Encoding (\ts -> b1 (b2 ts))
    {-# INLINE (<>) #-}
  
  -- | @since 0.2.0.0
  instance Monoid Encoding where
    mempty = Encoding (\ts -> ts)
    {-# INLINE mempty #-}
  
    mappend = (<>)
    {-# INLINE mappend #-}
  
    mconcat = foldr (<>) mempty
    {-# INLINE mconcat #-}
  
  -- | Encode a 'Word' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeWord :: Word -> Encoding
  encodeWord = Encoding . TkWord
  
  -- | Encode a 'Word8' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeWord8 :: Word8 -> Encoding
  encodeWord8 = Encoding . TkWord . fromIntegral
  
  -- | Encode a 'Word16' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeWord16 :: Word16 -> Encoding
  encodeWord16 = Encoding . TkWord . fromIntegral
  
  -- | Encode a 'Word32' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeWord32 :: Word32 -> Encoding
  encodeWord32 = Encoding . TkWord . fromIntegral
  
  -- | Encode a 'Word64' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeWord64 :: Word64 -> Encoding
  encodeWord64 = Encoding . TkWord64
  
  -- | Encode an 'Int' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeInt :: Int -> Encoding
  encodeInt = Encoding . TkInt
  
  -- | Encode an 'Int8' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeInt8 :: Int8 -> Encoding
  encodeInt8 = Encoding . TkInt . fromIntegral
  
  -- | Encode an 'Int16' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeInt16 :: Int16 -> Encoding
  encodeInt16 = Encoding . TkInt . fromIntegral
  
  -- | Encode an 'Int32' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeInt32 :: Int32 -> Encoding
  encodeInt32 = Encoding . TkInt . fromIntegral
  
  -- | Encode an @'Int64' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeInt64 :: Int64 -> Encoding
  encodeInt64 = Encoding . TkInt64
  
  -- | Encode an arbitrarily large @'Integer' in a
  -- flattened format.
  --
  -- @since 0.2.0.0
  encodeInteger :: Integer -> Encoding
  encodeInteger n = Encoding (TkInteger n)
  
  -- | Encode an arbitrary strict 'B.ByteString' in
  -- a flattened format.
  --
  -- @since 0.2.0.0
  encodeBytes :: B.ByteString -> Encoding
  encodeBytes = Encoding . TkBytes
  
  -- | Encode a bytestring in a flattened format.
  --
  -- @since 0.2.0.0
  encodeByteArray :: SlicedByteArray -> Encoding
  encodeByteArray = Encoding . TkByteArray
  
  -- | Encode a token specifying the beginning of a string of bytes of
  -- indefinite length. In reality, this specifies a stream of many
  -- occurrences of `encodeBytes`, each specifying a single chunk of the
  -- overall string. After all the bytes desired have been encoded, you
  -- should follow it with a break token (see 'encodeBreak').
  --
  -- @since 0.2.0.0
  encodeBytesIndef :: Encoding
  encodeBytesIndef = Encoding TkBytesBegin
  
  -- | Encode a 'T.Text' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeString :: T.Text -> Encoding
  encodeString = Encoding . TkString
  
  -- | Encode the beginning of an indefinite string.
  --
  -- @since 0.2.0.0
  encodeStringIndef :: Encoding
  encodeStringIndef = Encoding TkStringBegin
  
  -- | Encode a UTF-8 string in a flattened format. Note that the contents
  -- is not validated to be well-formed UTF-8.
  --
  -- @since 0.2.0.0
  encodeUtf8ByteArray :: SlicedByteArray -> Encoding
  encodeUtf8ByteArray = Encoding . TkUtf8ByteArray
  
  -- | Encode the length of a list, used to indicate that the following
  -- tokens represent the list values.
  --
  -- @since 0.2.0.0
  encodeListLen :: Word -> Encoding
  encodeListLen = Encoding . TkListLen
  
  -- | Encode a token specifying that this is the beginning of an
  -- indefinite list of unknown size. Tokens representing the list are
  -- expected afterwords, followed by a break token (see
  -- 'encodeBreak') when the list has ended.
  --
  -- @since 0.2.0.0
  encodeListLenIndef :: Encoding
  encodeListLenIndef = Encoding TkListBegin
  
  -- | Encode the length of a Map, used to indicate that
  -- the following tokens represent the map values.
  --
  -- @since 0.2.0.0
  encodeMapLen :: Word -> Encoding
  encodeMapLen = Encoding . TkMapLen
  
  -- | Encode a token specifying that this is the beginning of an
  -- indefinite map of unknown size. Tokens representing the map are
  -- expected afterwords, followed by a break token (see
  -- 'encodeBreak') when the map has ended.
  --
  -- @since 0.2.0.0
  encodeMapLenIndef :: Encoding
  encodeMapLenIndef = Encoding TkMapBegin
  
  -- | Encode a \'break\', used to specify the end of indefinite
  -- length objects like maps or lists.
  --
  -- @since 0.2.0.0
  encodeBreak :: Encoding
  encodeBreak = Encoding TkBreak
  
  -- | Encode an arbitrary 'Word' tag.
  --
  -- @since 0.2.0.0
  encodeTag :: Word -> Encoding
  encodeTag = Encoding . TkTag
  
  -- | Encode an arbitrary 64-bit 'Word64' tag.
  --
  -- @since 0.2.0.0
  encodeTag64 :: Word64 -> Encoding
  encodeTag64 = Encoding . TkTag64
  
  -- | Encode a 'Bool'.
  --
  -- @since 0.2.0.0
  encodeBool :: Bool -> Encoding
  encodeBool b = Encoding (TkBool b)
  
  -- | Encode an @Undef@ value.
  --
  -- @since 0.2.0.0
  encodeUndef :: Encoding
  encodeUndef = Encoding TkUndef
  
  -- | Encode a @Null@ value.
  --
  -- @since 0.2.0.0
  encodeNull :: Encoding
  encodeNull = Encoding TkNull
  
  -- | Encode a \'simple\' CBOR token that can be represented with an
  -- 8-bit word. You probably don't ever need this.
  --
  -- @since 0.2.0.0
  encodeSimple :: Word8 -> Encoding
  encodeSimple = Encoding . TkSimple
  
  -- | Encode a small 16-bit 'Float' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeFloat16 :: Float -> Encoding
  encodeFloat16 = Encoding . TkFloat16
  
  -- | Encode a full precision 'Float' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeFloat :: Float -> Encoding
  encodeFloat = Encoding . TkFloat32
  
  -- | Encode a 'Double' in a flattened format.
  --
  -- @since 0.2.0.0
  encodeDouble :: Double -> Encoding
  encodeDouble = Encoding . TkFloat64
  
  -- | Include pre-encoded valid CBOR data into the 'Encoding'.
  --
  -- The data is included into the output as-is without any additional wrapper.
  --
  -- This should be used with care. The data /must/ be a valid CBOR encoding, but
  -- this is /not/ checked.
  --
  -- This is useful when you have CBOR data that you know is already valid, e.g.
  -- previously validated and stored on disk, and you wish to include it without
  -- having to decode and re-encode it.
  --
  -- @since 0.2.2.0
  encodePreEncoded :: B.ByteString -> Encoding
  encodePreEncoded = Encoding . TkEncoded
  