Module msgpack_ll
A low-level pure @nogc, nothrow, @safe
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. UsegetType
to verify the type before callingparseType
. - The
fix
types have certain maximum and minimum values. These conditions need to be ensured when callingformatType!T
:MsgpackType.posFixInt
: Value must satisfyvalue < 128
MsgpackType.negFixInt
: Value must satisfy-33 < value < 0
MsgpackType.fixStr
: Length must satisfylength < 32
MsgpackType.fixArray
: Length must satisfylength < 16
MsgpackType.fixMap
: Length must satisfylength < 16
- All
ext
types: extType must satisfyextType < 128
- The
debug=DebugMsgpackLL
debug version can be used to enable debug checks for these problems.
ubyte[] data = ...;
byte[] result;
// First read array length
enforce(getType(data[0]) == MsgpackType .array16);
auto length = parseType!(MsgpackType .array16)(data[0..DataSize!(MsgpackType .array16)]);
data = data[DataSize!(MsgpackType .array16) .. $];
// Then read array values
for(size_t i = 0; i < length; i++)
{
enforce(getType(data[0]) == MsgpackType .int8);
result ~= parseType!(MsgpackType .int8)(data[0..DataSize!(MsgpackType .int8)]);
data = data[DataSize!(MsgpackType .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.
Example
Most types are handled like this:
ubyte[128] buffer;
enum type = MsgpackType .uint8;
// Serialization
formatType!(type)(42, buffer[0 .. DataSize!type]);
// Now deserialize
// Get the type at runtime
assert(getType(buffer[0]) == type);
// and deserialize specifying the type at compile time
const result = parseType!type(buffer[0 .. DataSize!type]);
assert(result == 42);
Example
Values for nil, true8 and false8 are ignored and can be skipped:
ubyte[128] buffer;
enum type = MsgpackType .true8;
// Serialization
formatType!(type)(buffer[0 .. DataSize!type]);
// Now deserialize
// Get the type at runtime
assert(getType(buffer[0]) == type);
// and deserialize specifying the type at compile time
const result = parseType!type(buffer[0 .. DataSize!type]);
assert(result == true);
Example
The fixExt types accept an additional extType parameter and data:
ubyte[128] buffer;
ubyte[1] value = [1];
enum type = MsgpackType .fixExt1;
// Serialization
formatType!(type)(42, value, buffer[0 .. DataSize!type]);
// Now deserialize
// Get the type at runtime
assert(getType(buffer[0]) == type);
const result = parseType!type(buffer[0 .. DataSize!type]);
// and deserialize specifying the type at compile time
assert(result[0] == 42);
assert(result[1 .. $] == value);
Example
The ext types accept an additional extType parameter and data length.
ubyte[128] buffer;
enum type = MsgpackType .ext8;
// Serialization
formatType!(type)(10, 42, buffer[0 .. DataSize!type]);
// Now deserialize
// Get the type at runtime
assert(getType(buffer[0]) == type);
// and deserialize specifying the type at compile time
const result = parseType!type(buffer[0 .. DataSize!type]);
assert(result .type == 42);
assert(result .length == 10);
Example
Often you'll want to decode multiple possible types:
ulong decodeSomeUint(ubyte[] data)
{
switch (data[0] .getType())
{
case MsgpackType .posFixInt:
return parseType!(MsgpackType .posFixInt)(
data[0 .. DataSize!(MsgpackType .posFixInt)]);
case MsgpackType .uint8:
return parseType!(MsgpackType .uint8)(
data[0 .. DataSize!(MsgpackType .uint8)]);
case MsgpackType .uint16:
return parseType!(MsgpackType .uint16)(
data[0 .. DataSize!(MsgpackType .uint16)]);
case MsgpackType .uint32:
return parseType!(MsgpackType .uint32)(
data[0 .. DataSize!(MsgpackType .uint32)]);
case MsgpackType .uint64:
return parseType!(MsgpackType .uint64)(
data[0 .. DataSize!(MsgpackType .uint64)]);
default:
throw new Exception("Expected integer type");
}
}
Functions
Name | Description |
---|---|
formatType(value, data) | Serialize a value to a certain type. |
getDataSize(type) | Get serialized data size at runtime. DataSize!() should be preferred
if the type is known at compile time.
|
getType(value) | Look at the first byte of an object to determine the type. |
parseType(data) | Parses the MessagePack object with specified type. |
Structs
Name | Description |
---|---|
ExtType | Serialization information about an ext type. |
Enums
Name | Description |
---|---|
MsgpackType | Enum of MessagePack types. |
Manifest constants
Name | Type | Description |
---|---|---|
DataSize | Get serialized data size at compile time. |