{-# 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