msgpack_ll

A low-level pure @nogc and betterC MessagePack implementation.

Note: As this is a low-level implementation certain error checking a some handling of the MessagePack data format has to be done by the API user. The following conditions need to be ensured by the user:

  • When calling parseType the compile time type must match the actual data type or incorrect results will be returned. Use getType to verify the type before calling parseType.
  • The fix types have certain maximum and minimum values. These conditions need to be ensured when calling formatType!T:
    • MsgpackType.posFixInt: Value must satisfy value < 128
    • MsgpackType.negFixInt: Value must satisfy -33 < value < 0
    • MsgpackType.fixStr: Length must satisfy length < 32
    • MsgpackType.fixArray: Length must satisfy length < 16
    • MsgpackType.fixMap: Length must satisfy length < 16
    • All ext types: extType must satisfy extType < 128

    Other size restrictions are automatically enforced by proper typing.

  • The debug=DebugMsgpackLL debug version can be used to enable debug checks for these problems.
  • 1 ubyte[] data = ...;
    2 byte[] result;
    3 
    4 // First read array length
    5 enforce(getType(data[0]) == MsgpackType.array16);
    6 auto length = parseType!(MsgpackType.array16)(data[0..DataSize!(MsgpackType.array16)]);
    7 data = data[DataSize!(MsgpackType.array16) .. $];
    8 
    9 // Then read array values
    10 for(size_t i = 0; i < length; i++)
    11 {
    12     enforce(getType(data[0]) == MsgpackType.int8);
    13     result ~= parseType!(MsgpackType.int8)(data[0..DataSize!(MsgpackType.int8)]);
    14     data = data[DataSize!(MsgpackType.int8) .. $];
    15 }
    Proper formatting and parsing of complex types (maps, arrays, ext types) needs help from the API user and must be done according to the MessagePack specification. For example to parse an array16 of int8:
  • Requires only std.bitmanip for bigEndianToNative and nativeToBigEndian as external dependency.

    TODO: Could try to avoid that dependency. This is only a compile time dependency anyway though, as these functions are templates and get inlined into this module.

    Members

    Enums

    MsgpackType
    enum MsgpackType

    Enum of MessagePack types.

    Functions

    formatType
    void formatType(typeof(null) value, ref ubyte[DataSize!type] data)
    void formatType(ref ubyte[DataSize!type] data)
    void formatType(bool value, ref ubyte[DataSize!type] data)
    void formatType(ref ubyte[DataSize!type] data)
    void formatType(bool value, ref ubyte[DataSize!type] data)
    void formatType(ref ubyte[DataSize!type] data)
    void formatType(ubyte value, ref ubyte[DataSize!type] data)
    void formatType(byte value, ref ubyte[DataSize!type] data)
    void formatType(ubyte value, ref ubyte[DataSize!type] data)
    void formatType(ushort value, ref ubyte[DataSize!type] data)
    void formatType(uint value, ref ubyte[DataSize!type] data)
    void formatType(ulong value, ref ubyte[DataSize!type] data)
    void formatType(byte value, ref ubyte[DataSize!type] data)
    void formatType(short value, ref ubyte[DataSize!type] data)
    void formatType(int value, ref ubyte[DataSize!type] data)
    void formatType(long value, ref ubyte[DataSize!type] data)
    void formatType(float value, ref ubyte[DataSize!type] data)
    void formatType(double value, ref ubyte[DataSize!type] data)
    void formatType(ubyte length, ref ubyte[DataSize!type] data)
    void formatType(ubyte length, ref ubyte[DataSize!type] data)
    void formatType(ushort length, ref ubyte[DataSize!type] data)
    void formatType(uint length, ref ubyte[DataSize!type] data)
    void formatType(ubyte length, ref ubyte[DataSize!type] data)
    void formatType(ushort length, ref ubyte[DataSize!type] data)
    void formatType(uint length, ref ubyte[DataSize!type] data)
    void formatType(ubyte length, ref ubyte[DataSize!type] data)
    void formatType(ushort length, ref ubyte[DataSize!type] data)
    void formatType(uint length, ref ubyte[DataSize!type] data)
    void formatType(ubyte length, ref ubyte[DataSize!type] data)
    void formatType(ushort length, ref ubyte[DataSize!type] data)
    void formatType(uint length, ref ubyte[DataSize!type] data)
    void formatType(ubyte extType, ref ubyte[1] value, ref ubyte[DataSize!type] data)
    void formatType(ubyte extType, ref ubyte[2] value, ref ubyte[DataSize!type] data)
    void formatType(ubyte extType, ref ubyte[4] value, ref ubyte[DataSize!type] data)
    void formatType(ubyte extType, ref ubyte[8] value, ref ubyte[DataSize!type] data)
    void formatType(ubyte extType, ref ubyte[16] value, ref ubyte[DataSize!type] data)
    void formatType(ubyte length, ubyte extType, ref ubyte[DataSize!type] data)
    void formatType(ushort length, ubyte extType, ref ubyte[DataSize!type] data)
    void formatType(uint length, ubyte extType, ref ubyte[DataSize!type] data)

    Serialize a value to a certain type.

    getDataSize
    size_t getDataSize(MsgpackType type)

    Get serialized data size at runtime. DataSize!() should be preferred if the type is known at compile time.

    getType
    MsgpackType getType(ubyte value)

    Look at the first byte of an object to determine the type.

    parseType
    auto parseType(ref ubyte[DataSize!type] data)
    ubyte[DataSize!type - 1] parseType(ref ubyte[DataSize!type] data)

    Parses the MessagePack object with specified type.

    Structs

    ExtType
    struct ExtType

    Serialization information about an ext type.

    Examples

    Most types are handled like this:

    1 ubyte[128] buffer;
    2 enum type = MsgpackType.uint8;
    3 
    4 // Serialization
    5 formatType!(type)(42, buffer[0 .. DataSize!type]);
    6 
    7 // Now deserialize
    8 // Get the type at runtime
    9 assert(getType(buffer[0]) == type);
    10 // and deserialize specifying the type at compile time
    11 const result = parseType!type(buffer[0 .. DataSize!type]);
    12 assert(result == 42);

    Values for nil, true8 and false8 are ignored and can be skipped:

    1 ubyte[128] buffer;
    2 enum type = MsgpackType.true8;
    3 
    4 // Serialization
    5 formatType!(type)(buffer[0 .. DataSize!type]);
    6 
    7 // Now deserialize
    8 // Get the type at runtime
    9 assert(getType(buffer[0]) == type);
    10 // and deserialize specifying the type at compile time
    11 const result = parseType!type(buffer[0 .. DataSize!type]);
    12 assert(result == true);

    The fixExt types accept an additional extType parameter and data:

    1 ubyte[128] buffer;
    2 ubyte[1] value = [1];
    3 enum type = MsgpackType.fixExt1;
    4 
    5 // Serialization
    6 formatType!(type)(42, value, buffer[0 .. DataSize!type]);
    7 
    8 // Now deserialize
    9 // Get the type at runtime
    10 assert(getType(buffer[0]) == type);
    11 const result = parseType!type(buffer[0 .. DataSize!type]);
    12 // and deserialize specifying the type at compile time
    13 assert(result[0] == 42);
    14 assert(result[1 .. $] == value);

    The ext types accept an additional extType parameter and data length.

    1 ubyte[128] buffer;
    2 enum type = MsgpackType.ext8;
    3 
    4 // Serialization
    5 formatType!(type)(10, 42, buffer[0 .. DataSize!type]);
    6 
    7 // Now deserialize
    8 // Get the type at runtime
    9 assert(getType(buffer[0]) == type);
    10 // and deserialize specifying the type at compile time
    11 const result = parseType!type(buffer[0 .. DataSize!type]);
    12 assert(result.type == 42);
    13 assert(result.length == 10);

    Often you'll want to decode multiple possible types:

    1 ulong decodeSomeUint(ubyte[] data) {
    2     switch (data[0].getType()) {
    3     case MsgpackType.posFixInt:
    4         return parseType!(MsgpackType.posFixInt)(data[0 .. DataSize!(MsgpackType.posFixInt)]);
    5     case MsgpackType.uint8:
    6         return parseType!(MsgpackType.uint8)(data[0 .. DataSize!(MsgpackType.uint8)]);
    7     case MsgpackType.uint16:
    8         return parseType!(MsgpackType.uint16)(data[0 .. DataSize!(MsgpackType.uint16)]);
    9     case MsgpackType.uint32:
    10         return parseType!(MsgpackType.uint32)(data[0 .. DataSize!(MsgpackType.uint32)]);
    11     case MsgpackType.uint64:
    12         return parseType!(MsgpackType.uint64)(data[0 .. DataSize!(MsgpackType.uint64)]);
    13     default:
    14         throw new Exception("Expected integer type");
    15     }
    16 }

    Meta