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