elm-toulouse / cbor / Cbor.Encode

The Concise Binary Object Representation (CBOR) is a data format whose design goals include the possibility of extremely small code size, fairly small message size, and extensibility without the need for version negotiation. These design goals make it different from earlier binary serializations such as ASN.1 and MessagePack.

Encoder


type Encoder

Describes how to encode a data structure or a type into binary CBOR

encode : Encoder -> Bytes

Turn a CBOR Encoder into Bytes.

sequence : List Encoder -> Encoder

Combine a bunch of encoders sequentially.

maybe : (a -> Encoder) -> Maybe a -> Encoder

Optionally encode a value. Nothing is encoded as null.

keyValue : (a -> Encoder) -> (b -> Encoder) -> ( a, b ) -> Encoder

Encode a key-value pair as a sequence of a key and a value. This is merely a shorthand for a sequence on a 2-tuple.

E.keyValue E.string E.int ( "a", 14 )
    == E.sequence
        [ E.string "a"
        , E.int 14
        ]

Primitives

bool : Basics.Bool -> Encoder

Encode booleans.

E.bool False == Bytes<0xF4>

E.bool True == Bytes<0xF5>

int : Basics.Int -> Encoder

Encode integers from -9007199254740992 (-2⁵³) to 9007199254740991 (2⁵³ - 1).

E.int 0 == Bytes<0x00>

E.int 1337 == Bytes<0x19, 0x05, 0x39>

float : Basics.Float -> Encoder

Encode floating numbers with maximum precision (64-bit).

E.float 0 == Bytes<0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00>

E.float -4.1 == Bytes<0xFB, 0xC0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66>

NOTE:

This is an alias for float64.

string : String -> Encoder

Encode a String of fixed size as a (definite) CBOR text string.

E.string "" == Bytes<0x60>

E.string "IETF" == Bytes <0x64, 0x49, 0x45, 0x54, 0x46>

E.string "🌈" == Bytes<0x64, 0xF0, 0x9F, 0x8C, 0x88>

bytes : Bytes -> Encoder

Encode raw Bytes of fixed size as a (definite) CBOR byte string.

E.bytes Bytes<> == Bytes<0x40>

E.bytes Bytes<0x01, 0x02, 0x03, 0x04> = Bytes<0x44, 0x01, 0x02, 0x03, 0x04>

null : Encoder

Create a CBOR null value. This can be decoded using Cbor.decode.maybe.

E.null == Bytes<0xF6>

undefined : Encoder

Create a CBOR undefined value. This can be decoded using Cbor.decode.maybe.

E.undefined == Bytes<0xF7>

Fancier Primitives

float16 : Basics.Float -> Encoder

Encode floating numbers with half-precision (16-bit).

E.float16 0.0 == Bytes<0xF9, 0x00, 0x00>

E.float16 -0.0 == Bytes<0xF9, 0x80, 0x00>

E.float16 1.5 == Bytes<0xF9, 0x3E, 0x00>

float32 : Basics.Float -> Encoder

Encode floating numbers with simple precision (32-bit).

E.float32 0.0 == Bytes<0xFA, 0x00, 0x00, 0x00, 0x00>

E.float32 3.4028234663852886e38 == Bytes<0xFA, 0x7F, 0x7F, 0xFF, 0xFF>

float64 : Basics.Float -> Encoder

Encode floating numbers with double precision (64-bit).

E.float64 0 == Bytes<0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00>

E.float64 -4.1 == Bytes<0xFB, 0xC0, 0x10, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66>

Simple Data-Structures

list : (a -> Encoder) -> List a -> Encoder

Turn a List into a (definite) CBOR array

E.list E.int [1,2,3] == Bytes<0x83, 0x01, 0x02, 0x03>

indefiniteList : (a -> Encoder) -> List a -> Encoder

Turn a List into a (indefinite) CBOR array

length : Basics.Int -> Encoder

Encode a (definite) list length only. This may be useful to stream a definite list or simply, to have even more fine-grained control over the creation of a definite list.

E.sequence
    [ E.length 2
    , E.int 1
    , E.int 2
    ]
    == E.list E.int [ 1, 2 ]

associativeList : (k -> Encoder) -> (v -> Encoder) -> List ( k, v ) -> Encoder

Turn a (key, value) associative list into a (definite) CBOR map. Note that, if keys are comparable, you should consider using a Dict and dict instead.

dict : (k -> Encoder) -> (v -> Encoder) -> Dict k v -> Encoder

Turn a Dict into a (definite) CBOR map.

E.dict E.string E.int (Dict.fromList [ ( "a", 1 ), ( "b", 2 ) ])
    == Bytes<0xA2, 0x61, 0x61, 0x01, 0x61, 0x62, 0x02>

size : Basics.Int -> Encoder

Encode a (definite) dict size only. This may be useful to stream a definite dict or simply, to have even more fine-grained control over the creation of a definite dict.

Records & Tuples


type Step k result

An intermediate (opaque) step in the encoding of a record or tuple. See record or tuple for more detail.

record : (k -> Encoder) -> (Step k record -> Step k record) -> record -> Encoder

Encode a record as a (definite) CBOR map. Keys in the map can be arbitrary CBOR but are expected to be homogeneous across the record.

type alias Album =
    { artist : String
    , title : String
    , label : Maybe String
    }

-- In this example, we use compact integer as keys.
encodeAlbumCompact : Album -> E.Encoder
encodeAlbumCompact =
    E.record E.int <|
        E.fields
            >> E.field 0 E.string .artist
            >> E.field 1 E.string .title
            >> E.optionalField 2 E.string .genre

-- In this example, we use more verbose string keys.
encodeAlbumVerbose : Album -> E.Encoder
encodeAlbumVerbose =
    E.record E.string <|
        E.fields
            >> E.field "artist" E.string .artist
            >> E.field "title" E.string .title
            >> E.optionalField "label" E.string .genre

fields : Step k record -> Step k record

A helper that makes writing record encoders nicer. It is equivalent to identity, but let us align encoders to fight compulsory OCDs.

field : k -> (field -> Encoder) -> (record -> field) -> Step k record -> Step k record

Encode a field of record and step through the encoding. See record for detail about usage.

optionalField : k -> (field -> Encoder) -> (record -> Maybe field) -> Step k record -> Step k record

Encode an optional field of record and step through the encoding. See record for detail about usage.

NOTE:

When the value is Nothing, the field (and its key) is completely omitted from the final record.

tuple : (Step Basics.Never tuple -> Step Basics.Never tuple) -> tuple -> Encoder

Encode a record / tuple as a (definite) CBOR array.

type alias Track =
    { title : String
    , duration : Int
    }

encodeTrack : Track -> E.Encoder
encodeTrack =
    E.tuple <|
        E.elems
            >> E.elem E.string .title
            >> E.elem E.int .duration

elems : Step Basics.Never tuple -> Step Basics.Never tuple

A helper that makes writing tuple encoders nicer. It is equivalent to identity, but let us align encoders to fight compulsory OCDs.

elem : (elem -> Encoder) -> (tuple -> elem) -> Step Basics.Never tuple -> Step Basics.Never tuple

Encode an elements of a tuple and step through the encoding. See tuple for detail about usage.

optionalElem : (elem -> Encoder) -> (tuple -> Maybe elem) -> Step Basics.Never tuple -> Step Basics.Never tuple

Optionally encode an element in a tuple. See tuple for detail about usage.

Streaming

Four CBOR items (arrays, maps, byte strings, and text strings) can be encoded with an indefinite length. This is useful if the encoding of the item needs to begin before the number of items inside the array or map, or the total length of the string, is known. (The application of this is often referred to as "streaming" within a data item.)

NOTE:

Indefinite-length arrays and maps are dealt with differently than indefinite-length byte strings and text strings.

beginString : Encoder

Encode a String of indefinite length in chunks. This indicates the beginning of multiple calls to string, followed by a break to signal the end of the stream. For example:

E.sequence
    [ E.beginString
    , E.string "elm"
    , E.string "rocks"
    , E.string "!"
    , E.break
    ]

beginBytes : Encoder

Encode a Bytes of indefinite length in chunks. This indicates the beginning of multiple calls to bytes, followed by a break to signal the end of the stream. For example:

E.sequence
    [ E.beginBytes
    , E.bytes Bytes<0x01, 0x02>
    , E.bytes Bytes<0x03, 0x04>
    , E.break
    ]

beginList : Encoder

Encode a List of indefinite length. This indicates the beginning of multiple calls for encoding elements, followed by a break to signal the end of the stream. For example:

E.sequence
    [ E.beginList
    , E.string "elm"
    , E.string "rocks"
    , E.string "!"
    , E.break
    ]

beginDict : Encoder

Encode a Dict of indefinite length. This indicates the beginning of multiple calls for encoding pairs of elements, followed by a break to signal the end of the stream. For example:

E.sequence
    [ E.beginDict
    , E.keyValue E.int E.string ( 1, "elm" )
    , E.keyValue E.int E.string ( 2, "rocks" )
    , E.keyValue E.int E.string ( 3, "!" )
    , E.break
    ]

break : Encoder

Encode termination of an indefinite structure. See beginString, beginBytes, beginList, beginDict for detail about usage.

Tagging

tag : Cbor.Tag.Tag -> Encoder

Encode a particular Tag as a CBOR tag prefix.

tagged : Cbor.Tag.Tag -> (a -> Encoder) -> a -> Encoder

Helper to quickly a tagged value

E.tagged t encodeA a == E.sequence [ E.tag t, encodeA a ]

Debugging

any : CborItem -> Encoder

Encode any generic CBOR item. This is particularly useful when dealing with heterogeneous data structures (e.g. tuples).

E.list E.any [ CborInt 42, CborBool True, CborString "awesome!" ]

raw : Bytes -> Encoder

Unsafe encoder to inject any arbitrary bytes into the encoding sequence. Do not use unless you know what you're doing, this may result in invalid CBOR encoding!