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