1 /// Deserialize a message buffer into a D type. 2 module deserialize; 3 4 import std.conv, std.traits, std.string, std.outbuffer; 5 6 import leb128, common; 7 8 /// Deserialize top-level, either array, associative array or struct from 9 /// a given ubyte array. 10 /// For aggregate types like structs, deserialize each member. 11 /// Params: msg = serialized data ubyte array 12 /// Return: Variable of type `T`. 13 auto fromMsgBuffer(T, MsgBufferType E = MsgBufferType.Var)(const ubyte[] msg) { 14 size_t processed = 0; 15 return fromMsgBuffer!(T, E)(msg, processed); 16 } // fromMsgBuffer() 17 18 /// Deserialize top-level, either array, associative array or struct from 19 /// a given OutBuffer. 20 /// For aggregate types like structs, deserialize each member. 21 /// Params: buf = serialized data OutBuffer 22 /// Return: Variable of type `T`. 23 auto fromMsgBuffer(T, MsgBufferType E = MsgBufferType.Var)(const OutBuffer buf) { 24 size_t processed = 0; 25 return fromMsgBuffer!(T, E)(buf.toBytes, processed); 26 } // fromMsgBuffer() 27 28 /// Deserialize an integer value from the given `msg`. 29 pragma(inline, true) 30 const(T) deserializeInt(MsgBufferType E, T)(const ubyte[] msg, ref size_t processed) { 31 static if (E == MsgBufferType.Flat) { 32 // See: https://github.com/KabukiStarship/KabukiToolkit/wiki/Fastest-Method-to-Align-Pointers 33 processed += (-processed) & (T.alignof - 1); 34 immutable n = *cast(T *)&msg[processed]; 35 processed += n.sizeof; 36 } else { 37 immutable n = fromLEB128!T(msg, processed); 38 } 39 return n; 40 } // deserializeInt() 41 42 /// Deserialize a single value, either a string, a floating point value or a scalar, e.g. 43 /// int, bool, long, etc. If it is neither, then forward to top-level deserialize. 44 auto deserializeValue(T, MsgBufferType E)(const ubyte[] msg, ref size_t processed) { 45 if (processed >= msg.length) { 46 // check for out of data, e.g. when trying to read a newer message format with more 47 // fields from data that has been serialised using an older message format version 48 T val; 49 return val; 50 } 51 static if (isSomeString!(T)) { 52 immutable n = deserializeInt!(E, uint)(msg, processed); 53 immutable s = processed; 54 processed += n; 55 return to!T(msg[s..processed].assumeUTF); 56 } else static if (isBoolean!(T)) { 57 immutable n = processed; 58 processed += T.sizeof; 59 static assert(ubyte.sizeof == bool.sizeof); 60 return *cast(T *)&msg[n]; 61 } else static if (is(T == enum)) { 62 static if (__traits(compiles, to!int(EnumMembers!T[0]))) { 63 static if (E == MsgBufferType.Flat) { 64 processed += (-processed) & (int.alignof - 1); 65 immutable n = processed; 66 processed += int.sizeof; 67 return *cast(T *)&msg[n]; 68 } else { 69 return to!T(fromLEB128!int(msg, processed)); 70 } 71 } else { 72 static assert(0, "only integral enums supported for enum " ~ T.stringof); 73 } 74 } else static if (isScalarType!(T) || isFloatingPoint!(T)) { 75 static if (!isFloatingPoint!(T) && T.sizeof > 1 && E == MsgBufferType.Var) { 76 return fromLEB128!T(msg, processed); 77 } else { 78 processed += (-processed) & (T.alignof - 1); 79 immutable n = processed; 80 processed += T.sizeof; 81 return *cast(T *)&msg[n]; 82 } 83 } else static if (isArray!(T)) { 84 T val; 85 auto n = deserializeInt!(E, uint)(msg, processed); 86 static if (isDynamicArray!(T)) 87 val = new typeof(val[0])[n]; 88 static if (isScalarType!(typeof(val[0])) || isFloatingPoint!(typeof(val[0]))) { 89 static if (!isFloatingPoint!(typeof(val[0])) && typeof(val[0]).sizeof > 1 && E == MsgBufferType.Var) { 90 arrayFromLEB128!T(msg, processed, val); 91 } else { 92 processed += (-processed) & (T.alignof - 1); 93 import core.stdc.string : memcpy; 94 memcpy(&val[0], &msg[processed], val.length * typeof(val[0]).sizeof); 95 // I think memcpy() is faster than the below code, but I might be wrong... 96 // val[0..n][] = (cast(const(typeof(val[0]))*)&msg[processed])[0..n]; 97 processed += val.length * typeof(val[0]).sizeof; 98 } 99 } else { 100 static foreach (i; 0..n) 101 val[i] = deserializeValue!(typeof(val[i]), E)(msg, processed); 102 } 103 return val; 104 } else static if (isAssociativeArray!(T)) { 105 T val; 106 immutable n = deserializeInt!(E, uint)(msg, processed); 107 foreach (i; 0..n) { 108 auto k = deserializeValue!(KeyType!T, E)(msg, processed); 109 val[k] = deserializeValue!(ValueType!T, E)(msg, processed); 110 } 111 return val; 112 } else static if (is(T == struct) && T.stringof.startsWith("Oneof")) { 113 T val; 114 immutable oneOfIdx = deserializeInt!(E, ubyte)(msg, processed); 115 if (oneOfIdx > 0) { 116 static foreach (idx, s; T.tupleof) {{ 117 alias OneofMemberType = typeof(s); 118 enum OneofMemberName = __traits(identifier, s); 119 static if (!is(OneofMemberType == TypeInfo)) { 120 if (OneofMemberName.startsWith("___data_field_" ~ to!string(oneOfIdx-1))) { 121 val = deserializeValue!(OneofMemberType, E)(msg, processed); 122 } 123 } 124 }} 125 } 126 return val; 127 } else { 128 return fromMsgBuffer!(T, E)(msg, processed); 129 } 130 } // deserializeValue() 131 132 /// Deserialize top-level, either array, associative array or struct from 133 /// a given ubyte array. 134 /// For aggregate types like structs, deserialize each member. 135 /// Return: Variable of type `T`. 136 auto fromMsgBuffer(T, MsgBufferType E)(const ubyte[] msg, ref size_t processed) { 137 T val; 138 static if (isAggregateType!(T) && !is(T == class)) { 139 static foreach (v; T.tupleof) 140 mixin("val." ~ __traits(identifier, v)) = deserializeValue!(typeof(v), E)(msg, processed); 141 } else static if (isArray!(T) || isAssociativeArray!(T)) { 142 val = deserializeValue!(T, E)(msg, processed); 143 } else { 144 static assert(0, "expected struct or array, not " ~ T.stringof); 145 } 146 return val; 147 } // fromMsgBuffer()