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