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()