serializeCbor

Serializes a value to CBOR format, returning the encoded byte array. (community summary)

  1. void serializeCbor(Appender appender, T value, int serdeTarget)
  2. immutable(ubyte)[] serializeCbor(T value, int serdeTarget)
    immutable(ubyte)[]
    serializeCbor
    (
    T
    )
    (
    auto ref T value
    ,
    int serdeTarget = SerdeTarget.cbor
    )

Examples

Test serializing booleans

assert(serializeCbor(true) == [0xf5]);
assert(serializeCbor(false) == [0xf4]);

Test serializing nulls

assert(serializeCbor(null) == [0xf6]);

Test serializing signed integral types

import mir.test;
// Bytes
serializeCbor(byte.min).should == [0x38, 0x7F];
serializeCbor(byte.max).should == [0x18, 0x7F];

// Shorts
serializeCbor(short(byte.max)).should == [0x18, 0x7F];
serializeCbor(short(byte.max) + 1).should == [0x18, 0x80];
serializeCbor(short.min).should == [0x39, 0x7F, 0xFF];
serializeCbor(short.max).should == [0x19, 0x7F, 0xFF];

// Integers
serializeCbor(int(-32)).should == [0x38, 0x1F];
serializeCbor(int(byte.max)).should == [0x18, 0x7F];
serializeCbor(int(short.max)).should == [0x19, 0x7F, 0xFF];
serializeCbor(int(short.max) + 1).should == [0x19, 0x80, 0x00];
serializeCbor(int.min).should == [0x3A, 0x7f, 0xff, 0xff, 0xff];
serializeCbor(int.max).should == [0x1A, 0x7f, 0xff, 0xff, 0xff];

// Long integers
serializeCbor(long(int.max)).should == [0x1A, 0x7f, 0xff, 0xff, 0xff];
serializeCbor(long(int.max) + 1).should == [0x1A, 0x80, 0x00, 0x00, 0x00];
serializeCbor(long.max).should == [0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
serializeCbor(long.min).should == [0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];

Test serializing unsigned integral types

import mir.test;
// Unsigned bytes
serializeCbor(ubyte.min).should == [0x00];
serializeCbor(ubyte((1 << 7) - 1)).should == [0x18, 0x7F];
serializeCbor(ubyte((1 << 7))).should == [0x18, 0x80];
serializeCbor(ubyte.max).should == [0x18, 0xff];

// Unsigned shorts
serializeCbor(ushort(ubyte.max)).should == [0x18, 0xff];
serializeCbor(ushort(ubyte.max + 1)).should == [0x19, 0x01, 0x00];
serializeCbor(ushort.min).should == [0x00];
serializeCbor(ushort.max).should == [0x19, 0xff, 0xff]; 

// Unsigned integers
serializeCbor(uint(ubyte.max)).should == [0x18, 0xff];
serializeCbor(uint(ushort.max)).should == [0x19, 0xff, 0xff];
serializeCbor(uint(ushort.max + 1)).should == [0x1A, 0x00, 0x01, 0x00, 0x00];
serializeCbor(uint.min).should == [0x00];
serializeCbor(uint.max).should == [0x1A, 0xff, 0xff, 0xff, 0xff];

// Long unsigned integers
serializeCbor(ulong(ubyte.max)).should == [0x18, 0xff];
serializeCbor(ulong(ushort.max)).should == [0x19, 0xff, 0xff];
serializeCbor(ulong(uint.max)).should == [0x1A, 0xff, 0xff, 0xff, 0xff];
serializeCbor(ulong(uint.max) + 1).should == [0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00];
serializeCbor(ulong.min).should == [0x00];
serializeCbor(ulong.max).should == [0x1B, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];

// Mir's BigIntView
import mir.bignum.integer : BigInt;
serializeCbor(BigInt!2(0xDEADBEEF)).should == [0x1A, 0xde, 0xad, 0xbe, 0xef];

Test serializing floats / doubles / reals

import mir.test;

serializeCbor(float.min_normal).should == [0xfa, 0x00, 0x80, 0x00, 0x00];
serializeCbor(float.max).should == [0xfa, 0x7f, 0x7f, 0xff, 0xff];
serializeCbor(double.min_normal).should == [0xfb, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
serializeCbor(double.max).should == [0xfb, 0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
static if (real.mant_dig == 64)
{
    serializeCbor(real.min_normal).should == serializeCbor(double(0));
    serializeCbor(real.max).should == serializeCbor(double.infinity);
}

// Mir's Decimal
import mir.bignum.decimal : Decimal;
serializeCbor(Decimal!2("777.777")).should == [0xfb,0x40,0x88,0x4e,0x37,0x4b,0xc6,0xa7,0xf0];
serializeCbor(Decimal!2("-777.7")).should == [0xfb,0xc0,0x88,0x4d,0x99,0x99,0x99,0x99,0x9a];

Test serializing timestamps

import mir.test;
import mir.timestamp : Timestamp;

serializeCbor(Timestamp(1970, 1, 1)).should == [0x6a, 0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31];

Test serializing strings

import mir.test;
import std.array : replicate;
serializeCbor("a").should == [0x61, 0x61];

// These need to be trusted because we cast const(char)[] to ubyte[] (which is fine here!)
() @trusted {
    auto a = "a".replicate(32);
    serializeCbor(a).should == 
        cast(ubyte[])[0x78, 0x20] ~ cast(ubyte[])a;
} ();

() @trusted {
    auto a = "a".replicate(ushort.max);
    serializeCbor(a).should == 
        cast(ubyte[])[0x79, 0xff, 0xff] ~ cast(ubyte[])a;
} ();

() @trusted {
    auto a = "a".replicate(ushort.max + 1);
    serializeCbor(a).should == 
        cast(ubyte[])[0x7a, 0x00, 0x01, 0x00, 0x00] ~ cast(ubyte[])a;
} ();

Test serializing blobs / clobs

import mir.test;
import mir.lob : Blob, Clob;
import std.array : replicate;

// Blobs
// These need to be trusted because we cast const(char)[] to ubyte[] (which is fine here!)
() @trusted {
    auto de = "\xde".replicate(32);
    serializeCbor(Blob(cast(ubyte[])de)).should ==
        cast(ubyte[])[0x58, 0x20] ~ cast(ubyte[])de;
} ();

() @trusted {
    auto de = "\xde".replicate(ushort.max);
    serializeCbor(Blob(cast(ubyte[])de)).should ==
        cast(ubyte[])[0x59, 0xff, 0xff] ~ cast(ubyte[])de;
} ();

() @trusted {
    auto de = "\xde".replicate(ushort.max + 1);
    serializeCbor(Blob(cast(ubyte[])de)).should ==
        cast(ubyte[])[0x5a, 0x00, 0x01, 0x00, 0x00] ~ cast(ubyte[])de;
} ();

// Clobs (serialized just as regular strings here)
() @trusted {
    auto de = "\xde".replicate(32);
    serializeCbor(Clob(de)).should == 
        cast(ubyte[])[0x78, 0x20] ~ cast(ubyte[])de;
} ();

Test serializing arrays

import mir.test;
// nested arrays
serializeCbor([["foo"], ["bar"], ["baz"]]).should == [0x83, 0x81, 0x63, 0x66, 0x6F, 0x6F, 0x81, 0x63, 0x62, 0x61, 0x72, 0x81, 0x63, 0x62, 0x61, 0x7A];
serializeCbor([0xDEADBEEF, 0xCAFEBABE, 0xAAAA_AAAA]).should == [0x83, 0x1A, 0xDE, 0xAD, 0xBE, 0xEF, 0x1A, 0xCA, 0xFE, 0xBA, 0xBE, 0x1A, 0xAA, 0xAA, 0xAA, 0xAA];
serializeCbor(["foo", "bar", "baz"]).should == [0x83, 0x63, 0x66, 0x6F, 0x6F, 0x63, 0x62, 0x61, 0x72, 0x63, 0x62, 0x61, 0x7A];
serializeCbor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]).should == [0x91, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11];

Test serializing enums

enum Foo
{
    Bar,
    Baz
}

assert(serializeCbor(Foo.Bar) == [0x63,0x42,0x61,0x72]);
assert(serializeCbor(Foo.Baz) == [0x63,0x42,0x61,0x7a]);

Test serializing maps (structs)

import mir.test;

struct Book
{
    string title;
    bool wouldRecommend;
    string description;
    uint numberOfNovellas;
    double price;
    float weight;
    string[] tags;
}

Book book = Book("A Hero of Our Time", true, "", 5, 7.99, 6.88, ["russian", "novel", "19th century"]);

// This will probably break if you modify how any of the data types
// are serialized.
serializeCbor(book).should == [0xBF, 0x65, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x72, 0x41, 0x20, 0x48, 0x65, 0x72, 0x6F, 0x20, 0x6F, 0x66, 0x20, 0x4F, 0x75, 0x72, 0x20, 0x54, 0x69, 0x6D, 0x65, 0x6E, 0x77, 0x6F, 0x75, 0x6C, 0x64, 0x52, 0x65, 0x63, 0x6F, 0x6D, 0x6D, 0x65, 0x6E, 0x64, 0xF5, 0x6B, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x60, 0x70, 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x4F, 0x66, 0x4E, 0x6F, 0x76, 0x65, 0x6C, 0x6C, 0x61, 0x73, 0x05, 0x65, 0x70, 0x72, 0x69, 0x63, 0x65, 0xFB, 0x40, 0x1F, 0xF5, 0xC2, 0x8F, 0x5C, 0x28, 0xF6, 0x66, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0xFA, 0x40, 0xDC, 0x28, 0xF6, 0x64, 0x74, 0x61, 0x67, 0x73, 0x83, 0x67, 0x72, 0x75, 0x73, 0x73, 0x69, 0x61, 0x6E, 0x65, 0x6E, 0x6F, 0x76, 0x65, 0x6C, 0x6C, 0x31, 0x39, 0x74, 0x68, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x75, 0x72, 0x79, 0xFF];

Test maps with serdeMembersExactly

import mir.test;
import mir.serde: serdeMembersExactly;

@serdeMembersExactly(7)
struct Book
{
    string title;
    bool wouldRecommend;
    string description;
    uint numberOfNovellas;
    double price;
    float weight;
    string[] tags;
}

Book book = Book("A Hero of Our Time", true, "", 5, 7.99, 6.88, ["russian", "novel", "19th century"]);

// This will probably break if you modify how any of the data types
// are serialized.
serializeCbor(book).should == [0xA7, 0x65, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x72, 0x41, 0x20, 0x48, 0x65, 0x72, 0x6F, 0x20, 0x6F, 0x66, 0x20, 0x4F, 0x75, 0x72, 0x20, 0x54, 0x69, 0x6D, 0x65, 0x6E, 0x77, 0x6F, 0x75, 0x6C, 0x64, 0x52, 0x65, 0x63, 0x6F, 0x6D, 0x6D, 0x65, 0x6E, 0x64, 0xF5, 0x6B, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x60, 0x70, 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x4F, 0x66, 0x4E, 0x6F, 0x76, 0x65, 0x6C, 0x6C, 0x61, 0x73, 0x05, 0x65, 0x70, 0x72, 0x69, 0x63, 0x65, 0xFB, 0x40, 0x1F, 0xF5, 0xC2, 0x8F, 0x5C, 0x28, 0xF6, 0x66, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0xFA, 0x40, 0xDC, 0x28, 0xF6, 0x64, 0x74, 0x61, 0x67, 0x73, 0x83, 0x67, 0x72, 0x75, 0x73, 0x73, 0x69, 0x61, 0x6E, 0x65, 0x6E, 0x6F, 0x76, 0x65, 0x6C, 0x6C, 0x31, 0x39, 0x74, 0x68, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x75, 0x72, 0x79];

Test serializing annotated structs

import mir.test;
import mir.algebraic;
import mir.serde : serdeAlgebraicAnnotation;

@serdeAlgebraicAnnotation("Foo")
static struct Foo
{
    string bar;
}

@serdeAlgebraicAnnotation("Fooz")
static struct Fooz
{
    long bar;
}

alias V = Variant!(Foo, Fooz);
auto foo = V(Foo("baz"));

serializeCbor(foo).should == [0xA1, 0x63, 0x46, 0x6F, 0x6F, 0xBF, 0x63, 0x62, 0x61, 0x72, 0x63, 0x62, 0x61, 0x7A, 0xFF];

Test custom serialize function with Cbor

import mir.test;
static class MyExampleClass
{
    string text;

    this(string text)
    {
        this.text = text;
    }

    void serialize(S)(scope ref S serializer) scope const
    {
        auto state = serializer.stringBegin;
        serializer.putStringPart("Hello! ");
        serializer.putStringPart("String passed: ");
        serializer.putStringPart(this.text);
        serializer.stringEnd(state);
    }
}

serializeCbor(new MyExampleClass("foo bar baz")).should == [0x7F, 0x67, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x21, 0x20, 0x6F, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x70, 0x61, 0x73, 0x73, 0x65, 0x64, 0x3A, 0x20, 0x6B, 0x66, 0x6F, 0x6F, 0x20, 0x62, 0x61, 0x72, 0x20, 0x62, 0x61, 0x7A, 0xFF];