serializeMsgpack

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

Examples

Test serializing booleans

assert(serializeMsgpack(true) == [0xc3]);
assert(serializeMsgpack(false) == [0xc2]);

Test serializing nulls

assert(serializeMsgpack(null) == [0xc0]);

Test serializing signed integral types

// Bytes
assert(serializeMsgpack(byte.min) == [0xd0, 0x80]);
assert(serializeMsgpack(byte.max) == [0x7f]);

// Shorts
assert(serializeMsgpack(short(byte.max)) == [0x7f]);
assert(serializeMsgpack(short(byte.max) + 1) == [0xd1, 0x00, 0x80]);
assert(serializeMsgpack(short.min) == [0xd1, 0x80, 0x00]);
assert(serializeMsgpack(short.max) == [0xd1, 0x7f, 0xff]);

// Integers
assert(serializeMsgpack(int(-32)) == [0xe0]);
assert(serializeMsgpack(int(byte.max)) == [0x7f]);
assert(serializeMsgpack(int(short.max)) == [0xd1, 0x7f, 0xff]);
assert(serializeMsgpack(int(short.max) + 1) == [0xd2, 0x00, 0x00, 0x80, 0x00]);
assert(serializeMsgpack(int.min) == [0xd2, 0x80, 0x00, 0x00, 0x00]);
assert(serializeMsgpack(int.max) == [0xd2, 0x7f, 0xff, 0xff, 0xff]);

// Long integers
assert(serializeMsgpack(long(int.max)) == [0xd2, 0x7f, 0xff, 0xff, 0xff]);
assert(serializeMsgpack(long(int.max) + 1) == [0xd3, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00]);
assert(serializeMsgpack(long.max) == [0xd3, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
assert(serializeMsgpack(long.min) == [0xd3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);

Test serializing unsigned integral types

// Unsigned bytes
assert(serializeMsgpack(ubyte.min) == [0x00]);
assert(serializeMsgpack(ubyte((1 << 7) - 1)) == [0x7f]);
assert(serializeMsgpack(ubyte((1 << 7))) == [0xcc, 0x80]);
assert(serializeMsgpack(ubyte.max) == [0xcc, 0xff]);

// Unsigned shorts
assert(serializeMsgpack(ushort(ubyte.max)) == [0xcc, 0xff]);
assert(serializeMsgpack(ushort(ubyte.max + 1)) == [0xcd, 0x01, 0x00]);
assert(serializeMsgpack(ushort.min) == [0x00]);
assert(serializeMsgpack(ushort.max) == [0xcd, 0xff, 0xff]); 

// Unsigned integers
assert(serializeMsgpack(uint(ubyte.max)) == [0xcc, 0xff]);
assert(serializeMsgpack(uint(ushort.max)) == [0xcd, 0xff, 0xff]);
assert(serializeMsgpack(uint(ushort.max + 1)) == [0xce, 0x00, 0x01, 0x00, 0x00]);
assert(serializeMsgpack(uint.min) == [0x00]);
assert(serializeMsgpack(uint.max) == [0xce, 0xff, 0xff, 0xff, 0xff]);

// Long unsigned integers
assert(serializeMsgpack(ulong(ubyte.max)) == [0xcc, 0xff]);
assert(serializeMsgpack(ulong(ushort.max)) == [0xcd, 0xff, 0xff]);
assert(serializeMsgpack(ulong(uint.max)) == [0xce, 0xff, 0xff, 0xff, 0xff]);
assert(serializeMsgpack(ulong(uint.max) + 1) == [0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00]);
assert(serializeMsgpack(ulong.min) == [0x00]);
assert(serializeMsgpack(ulong.max) == [0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);

// Mir's BigIntView
import mir.bignum.integer : BigInt;
assert(serializeMsgpack(BigInt!2(0xDEADBEEF)) == [0xd3, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xbe, 0xef]);

Test serializing floats / doubles / reals

import mir.test;

assert(serializeMsgpack(float.min_normal) == [0xca, 0x00, 0x80, 0x00, 0x00]);
assert(serializeMsgpack(float.max) == [0xca, 0x7f, 0x7f, 0xff, 0xff]);
assert(serializeMsgpack(double.min_normal) == [0xcb, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
assert(serializeMsgpack(double.max) == [0xcb, 0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]);
static if (real.mant_dig == 64)
{
    assert(serializeMsgpack(real.min_normal) == [0xcb,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
    assert(serializeMsgpack(real.max) == [0xcb,0x7f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00]);
}

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

Test serializing timestamps

import mir.timestamp : Timestamp;
assert(serializeMsgpack(Timestamp(1970, 1, 1, 0, 0, 0)) == [0xd6, 0xff, 0x00, 0x00, 0x00, 0x00]);
assert(serializeMsgpack(Timestamp(2038, 1, 19, 3, 14, 7)) == [0xd6, 0xff, 0x7f, 0xff, 0xff, 0xff]);
assert(serializeMsgpack(Timestamp(2299, 12, 31, 23, 59, 59)) == [0xd7, 0xff, 0x00, 0x00, 0x00, 0x02, 0x6c, 0xb5, 0xda, 0xff]);
assert(serializeMsgpack(Timestamp(3000, 12, 31, 23, 59, 59)) == [0xc7, 0x0c, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x93, 0x3f, 0xff, 0x7f]);

Test serializing strings

import std.array : replicate;
assert(serializeMsgpack("a") == [0xa1, 0x61]);

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

() @trusted {
    auto a = "a".replicate(ushort.max);
    assert(serializeMsgpack(a) == 
        cast(ubyte[])[0xda, 0xff, 0xff] ~ cast(ubyte[])a);
} ();

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

Test serializing blobs / clobs

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);
    assert(serializeMsgpack(Blob(cast(ubyte[])de)) ==
        cast(ubyte[])[0xc4, 0x20] ~ cast(ubyte[])de);
} ();

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

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

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

Test serializing arrays

// nested arrays
assert(serializeMsgpack([["foo"], ["bar"], ["baz"]]) == [0x93, 0x91, 0xa3, 0x66, 0x6f, 0x6f, 0x91, 0xa3, 0x62, 0x61, 0x72, 0x91, 0xa3, 0x62, 0x61, 0x7a]);
assert(serializeMsgpack([0xDEADBEEF, 0xCAFEBABE, 0xAAAA_AAAA]) == [0x93, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xce, 0xca, 0xfe, 0xba, 0xbe, 0xce, 0xaa, 0xaa, 0xaa, 0xaa]);
assert(serializeMsgpack(["foo", "bar", "baz"]) == [0x93, 0xa3, 0x66, 0x6f, 0x6f, 0xa3, 0x62, 0x61, 0x72, 0xa3, 0x62, 0x61, 0x7a]);
assert(serializeMsgpack([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]) == [0xdc,0x00,0x11,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(serializeMsgpack(Foo.Bar) == [0xa3,0x42,0x61,0x72]);
assert(serializeMsgpack(Foo.Baz) == [0xa3,0x42,0x61,0x7a]);

Test serializing maps (structs)

static 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.
assert(serializeMsgpack(book) == [0x87,0xa5,0x74,0x69,0x74,0x6c,0x65,0xb2,0x41,0x20,0x48,0x65,0x72,0x6f,0x20,0x6f,0x66,0x20,0x4f,0x75,0x72,0x20,0x54,0x69,0x6d,0x65,0xae,0x77,0x6f,0x75,0x6c,0x64,0x52,0x65,0x63,0x6f,0x6d,0x6d,0x65,0x6e,0x64,0xc3,0xab,0x64,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0xa0,0xb0,0x6e,0x75,0x6d,0x62,0x65,0x72,0x4f,0x66,0x4e,0x6f,0x76,0x65,0x6c,0x6c,0x61,0x73,0x05,0xa5,0x70,0x72,0x69,0x63,0x65,0xcb,0x40,0x1f,0xf5,0xc2,0x8f,0x5c,0x28,0xf6,0xa6,0x77,0x65,0x69,0x67,0x68,0x74,0xca,0x40,0xdc,0x28,0xf6,0xa4,0x74,0x61,0x67,0x73,0x93,0xa7,0x72,0x75,0x73,0x73,0x69,0x61,0x6e,0xa5,0x6e,0x6f,0x76,0x65,0x6c,0xac,0x31,0x39,0x74,0x68,0x20,0x63,0x65,0x6e,0x74,0x75,0x72,0x79]);

Test serializing a large map (struct)

static struct HugeStruct
{
    bool a;
    bool b;
    bool c;
    bool d;
    bool e;
    string f;
    string g;
    string h;
    string i;
    string j;
    int k;
    int l;
    int m;
    int n;
    int o;
    long p;
}

HugeStruct s = HugeStruct(true, true, true, true, true, "", "", "", "", "", 123, 456, 789, 123, 456, 0xDEADBEEF);
assert(serializeMsgpack(s) == [0xde,0x00,0x10,0xa1,0x61,0xc3,0xa1,0x62,0xc3,0xa1,0x63,0xc3,0xa1,0x64,0xc3,0xa1,0x65,0xc3,0xa1,0x66,0xa0,0xa1,0x67,0xa0,0xa1,0x68,0xa0,0xa1,0x69,0xa0,0xa1,0x6a,0xa0,0xa1,0x6b,0x7b,0xa1,0x6c,0xd1,0x01,0xc8,0xa1,0x6d,0xd1,0x03,0x15,0xa1,0x6e,0x7b,0xa1,0x6f,0xd1,0x01,0xc8,0xa1,0x70,0xd3,0x00,0x00,0x00,0x00,0xde,0xad,0xbe,0xef]);

Test serializing annotated structs

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"));

assert(serializeMsgpack(foo) == [0x81,0xa3,0x46,0x6f,0x6f,0x81,0xa3,0x62,0x61,0x72,0xa3,0x62,0x61,0x7a]);

Test custom serialize function with MessagePack

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);

        import mir.ion.type_code : IonTypeCode;
        serializer.putNull(IonTypeCode.string);
    }
}

assert(serializeMsgpack(new MyExampleClass("foo bar baz")) == [0xd9,0x21,0x48,0x65,0x6c,0x6c,0x6f,0x21,0x20,0x53,0x74,0x72,0x69,0x6e,0x67,0x20,0x70,0x61,0x73,0x73,0x65,0x64,0x3a,0x20,0x66,0x6f,0x6f,0x20,0x62,0x61,0x72,0x20,0x62,0x61,0x7a,0xc0]);

Meta