1 module xserial.serial;
2 
3 import std.system : Endian, endian;
4 import std.traits : isArray, isDynamicArray, isStaticArray, isAssociativeArray, ForeachType, KeyType, ValueType, isIntegral, isFloatingPoint, isSomeChar, isType, isCallable, isPointer, hasUDA, getUDAs;
5 
6 import xbuffer.buffer : canSwapEndianness, Buffer, BufferOverflowException;
7 import xbuffer.varint : isVar;
8 
9 import xserial.attribute;
10 
11 /**
12  * Serializes some data.
13  */
14 ubyte[] serialize(Endian endianness, L, T)(T value, Buffer buffer) {
15 	serializeImpl!(endianness, L, T)(buffer, value);
16 	return buffer.data!ubyte;
17 }
18 
19 /// ditto
20 ubyte[] serialize(Endian endianness, L, T)(T value) {
21 	return serialize!(endianness, L, T)(value, new Buffer(64));
22 }
23 
24 /// ditto
25 ubyte[] serialize(Endian endianness, T)(T value, Buffer buffer) {
26 	return serialize!(endianness, size_t)(value, buffer);
27 }
28 
29 /// ditto
30 ubyte[] serialize(Endian endianness, T)(T value) {
31 	return serialize!(endianness, size_t)(value);
32 }
33 
34 /// ditto
35 ubyte[] serialize(T)(T value, Buffer buffer) {
36 	return serialize!(endian, size_t, T)(value, buffer);
37 }
38 
39 /// ditto
40 ubyte[] serialize(T)(T value) {
41 	return serialize!(endian, size_t, T)(value);
42 }
43 
44 /**
45  * Deserializes some data.
46  */
47 T deserialize(T, Endian endianness, L)(Buffer buffer) {
48 	return deserializeImpl!(endianness, L, T)(buffer);
49 }
50 
51 /// ditto
52 T deserialize(T, Endian endianness, L)(in ubyte[] buffer) {
53 	return deserialize!(T, endianness, L)(new Buffer(buffer));
54 }
55 
56 /// ditto
57 T deserialize(T, Endian endianness)(Buffer buffer) {
58 	return deserialize!(T, endianness, size_t)(buffer);
59 }
60 
61 /// ditto
62 T deserialize(T, Endian endianness)(in ubyte[] buffer) {
63 	return deserialize!(T, endianness, size_t)(buffer);
64 }
65 
66 /// ditto
67 T deserialize(T)(Buffer buffer) {
68 	return deserialize!(T, endian, size_t)(buffer);
69 }
70 
71 /// ditto
72 T deserialize(T)(in ubyte[] buffer) {
73 	return deserialize!(T, endian, size_t)(buffer);
74 }
75 
76 // -----------
77 // common data
78 // -----------
79 
80 enum EndianType {
81 	
82 	bigEndian = cast(int)Endian.bigEndian,
83 	littleEndian = cast(int)Endian.littleEndian,
84 	var,
85 	
86 }
87 
88 template Members(T, alias Only) {
89 	
90 	import std.typetuple : TypeTuple;
91 	
92 	mixin({
93 			
94 		string ret = "alias Members = TypeTuple!(";
95 		foreach(member ; __traits(derivedMembers, T)) {
96 			static if(is(typeof(mixin("T." ~ member)))) {
97 				mixin("alias M = typeof(T." ~ member ~ ");");
98 				static if(
99 					isType!M &&
100 					!isCallable!M &&
101 					!__traits(compiles, { mixin("auto test=T." ~ member ~ ";"); }) &&			// static members
102 					!__traits(compiles, { mixin("auto test=T.init." ~ member ~ "();"); }) &&	// properties
103 					!hasUDA!(__traits(getMember, T, member), Exclude) &&
104 					!hasUDA!(__traits(getMember, T, member), Only)
105 					) {
106 					ret ~= `"` ~ member ~ `",`;
107 					
108 				}
109 			}
110 		}
111 		return ret ~ ");";
112 		
113 	}());
114 	
115 }
116 
117 // -------------
118 // serialization
119 // -------------
120 
121 void serializeImpl(Endian endianness, L, T)(Buffer buffer, T value) {
122 	static if(isVar!L) serializeImpl!(cast(EndianType)endianness, L.Type, EndianType.var, L.Type, EndianType.var, T)(buffer, value);
123 	else serializeImpl!(cast(EndianType)endianness, L, cast(EndianType)endianness, L, cast(EndianType)endianness, T)(buffer, value);
124 }
125 
126 void serializeImpl(EndianType endianness, OL, EndianType ole, CL, EndianType cle, T)(Buffer buffer, T value) {
127 	static if(isArray!T) {
128 		static if(isDynamicArray!T) serializeLength!(cle, CL)(buffer, value.length);
129 		serializeArray!(endianness, OL, ole)(buffer, value);
130 	} else static if(isAssociativeArray!T) {
131 		serializeLength!(cle, CL)(buffer, value.length);
132 		serializeAssociativeArray!(endianness, OL, ole)(buffer, value);
133 	} else static if(is(T == class) || is(T == struct)) {
134 		serializeMembers!(endianness, OL, ole)(buffer, value);
135 	} else static if(is(T : bool) || isIntegral!T || isFloatingPoint!T || isSomeChar!T) {
136 		serializeNumber!endianness(buffer, value);
137 	} else {
138 		static assert(0, "Cannot serialize " ~ T.stringof);
139 	}
140 }
141 
142 void serializeNumber(EndianType endianness, T)(Buffer buffer, T value) {
143 	static if(endianness == EndianType.var) {
144 		static assert(isIntegral!T && T.sizeof > 1, T.stringof ~ " cannot be annotated with @Var");
145 		buffer.writeVar!T(value);
146 	} else static if(endianness == EndianType.bigEndian) {
147 		buffer.write!(Endian.bigEndian, T)(value);
148 	} else static if(endianness == EndianType.littleEndian) {
149 		buffer.write!(Endian.littleEndian, T)(value);
150 	}
151 }
152 
153 void serializeLength(EndianType endianness, L)(Buffer buffer, size_t length) {
154 	static if(L.sizeof < size_t.sizeof) serializeNumber!(endianness, L)(buffer, cast(L)length);
155 	else serializeNumber!(endianness, L)(buffer, length);
156 }
157 
158 void serializeArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer, T array) if(isArray!T) {
159 	static if(canSwapEndianness!(ForeachType!T) && !is(ForeachType!T == struct) && !is(ForeachType!T == class) && endianness != EndianType.var) {
160 		buffer.write!(cast(Endian)endianness)(array);
161 	} else {
162 		foreach(value ; array) {
163 			serializeImpl!(endianness, OL, ole, OL, ole)(buffer, value);
164 		}
165 	}
166 }
167 
168 void serializeAssociativeArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer, T array) if(isAssociativeArray!T) {
169 	foreach(key, value; array) {
170 		serializeImpl!(endianness, OL, ole, OL, ole)(buffer, key);
171 		serializeImpl!(endianness, OL, ole, OL, ole)(buffer, value);
172 	}
173 }
174 
175 void serializeMembers(EndianType endianness, L, EndianType le, T)(Buffer __buffer, T __container) {
176 	foreach(member ; Members!(T, DecodeOnly)) {
177 		
178 		mixin("alias M = typeof(__container." ~ member ~ ");");
179 		
180 		static foreach(uda ; __traits(getAttributes, __traits(getMember, T, member))) {
181 			static if(is(uda : Custom!C, C)) {
182 				enum __custom = true;
183 				uda.C.serialize(mixin("__container." ~ member), __buffer);
184 			}
185 		}
186 		
187 		static if(!is(typeof(__custom))) mixin({
188 				
189 			static if(hasUDA!(__traits(getMember, T, member), LengthImpl)) {
190 				import std.conv : to;
191 				auto length = getUDAs!(__traits(getMember, T, member), LengthImpl)[0];
192 				immutable e = "L, le, " ~ length.type ~ ", " ~ (length.endianness == -1 ? "endianness" : "EndianType." ~ (cast(EndianType)length.endianness).to!string);
193 			} else {
194 				immutable e = "L, le, L, le";
195 			}
196 			
197 			static if(hasUDA!(__traits(getMember, T, member), NoLength)) immutable ret = "xserial.serial.serializeArray!(endianness, L, le, M)(__buffer, __container." ~ member ~ ");";
198 			else static if(hasUDA!(__traits(getMember, T, member), Var)) immutable ret = "xserial.serial.serializeImpl!(EndianType.var, " ~ e ~ ", M)(__buffer, __container." ~ member ~ ");";
199 			else static if(hasUDA!(__traits(getMember, T, member), BigEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.bigEndian, " ~ e ~ ", M)(__buffer, __container." ~ member ~ ");";
200 			else static if(hasUDA!(__traits(getMember, T, member), LittleEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.littleEndian, " ~ e ~ ", M)(__buffer, __container." ~ member ~ ");";
201 			else immutable ret = "xserial.serial.serializeImpl!(endianness, " ~ e ~ ", M)(__buffer, __container." ~ member ~ ");";
202 
203 			static if(!hasUDA!(__traits(getMember, T, member), Condition)) return ret;
204 			else return "with(__container){if(" ~ getUDAs!(__traits(getMember, T, member), Condition)[0].condition ~ "){" ~ ret ~ "}}";
205 			
206 		}());
207 		
208 	}
209 }
210 
211 // ---------------
212 // deserialization
213 // ---------------
214 
215 T deserializeImpl(Endian endianness, L, T)(Buffer buffer) {
216 	static if(isVar!L) return deserializeImpl!(cast(EndianType)endianness, L.Type, EndianType.var, L.Type, EndianType.var, T)(buffer);
217 	else return deserializeImpl!(cast(EndianType)endianness, L, cast(EndianType)endianness, L, cast(EndianType)endianness, T)(buffer);
218 }
219 
220 T deserializeImpl(EndianType endianness, OL, EndianType ole, CL, EndianType cle, T)(Buffer buffer) {
221 	static if(isStaticArray!T) {
222 		return deserializeStaticArray!(endianness, OL, ole, T)(buffer);
223 	} else static if(isDynamicArray!T) {
224 		return deserializeDynamicArray!(endianness, OL, ole, T)(buffer, deserializeLength!(cle, CL)(buffer));
225 	} else static if(isAssociativeArray!T) {
226 		return deserializeAssociativeArray!(endianness, OL, ole, T)(buffer, deserializeLength!(cle, CL)(buffer));
227 	} else static if(is(T == class)) {
228 		T ret = new T();
229 		deserializeMembers!(endianness, OL, ole)(buffer, ret);
230 		return ret;
231 	} else static if(is(T == struct)) {
232 		T ret;
233 		deserializeMembers!(endianness, OL, ole)(buffer, &ret);
234 		return ret;
235 	} else static if(is(T : bool) || isIntegral!T || isFloatingPoint!T || isSomeChar!T) {
236 		return deserializeNumber!(endianness, T)(buffer);
237 	} else {
238 		static assert(0, "Cannot deserialize " ~ T.stringof);
239 	}
240 }
241 
242 T deserializeNumber(EndianType endianness, T)(Buffer buffer) {
243 	static if(endianness == EndianType.var) {
244 		static assert(isIntegral!T && T.sizeof > 1, T.stringof ~ " cannot be annotated with @Var");
245 		return buffer.readVar!T();
246 	} else static if(endianness == EndianType.bigEndian) {
247 		return buffer.read!(Endian.bigEndian, T)();
248 	} else static if(endianness == EndianType.littleEndian) {
249 		return buffer.read!(Endian.littleEndian, T)();
250 	}
251 }
252 
253 size_t deserializeLength(EndianType endianness, L)(Buffer buffer) {
254 	static if(L.sizeof > size_t.sizeof) return cast(size_t)deserializeNumber!(endianness, L)(buffer);
255 	else return deserializeNumber!(endianness, L)(buffer);
256 }
257 
258 T deserializeStaticArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer) if(isStaticArray!T) {
259 	T ret;
260 	foreach(ref value ; ret) {
261 		value = deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T)(buffer);
262 	}
263 	return ret;
264 }
265 
266 T deserializeDynamicArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer, size_t length) if(isDynamicArray!T) {
267 	T ret;
268 	foreach(i ; 0..length) {
269 		ret ~= deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T)(buffer);
270 	}
271 	return ret;
272 }
273 
274 T deserializeAssociativeArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer, size_t length) if(isAssociativeArray!T) {
275 	T ret;
276 	foreach(i ; 0..length) {
277 		ret[deserializeImpl!(endianness, OL, ole, OL, ole, KeyType!T)(buffer)] = deserializeImpl!(endianness, OL, ole, OL, ole, ValueType!T)(buffer);
278 	}
279 	return ret;
280 }
281 
282 T deserializeNoLengthArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer) if(isDynamicArray!T) {
283 	T ret;
284 	try {
285 		while(true) ret ~= deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T)(buffer);
286 	} catch(BufferOverflowException) {}
287 	return ret;
288 }
289 
290 void deserializeMembers(EndianType endianness, L, EndianType le, C)(Buffer __buffer, C __container) {
291 	static if(isPointer!C) alias T = typeof(*__container);
292 	else alias T = C;
293 	foreach(member ; Members!(T, EncodeOnly)) {
294 		
295 		mixin("alias M = typeof(__container." ~ member ~ ");");
296 		
297 		static foreach(uda ; __traits(getAttributes, __traits(getMember, T, member))) {
298 			static if(is(uda : Custom!C, C)) {
299 				enum __custom = true;
300 				mixin("__container." ~ member) = uda.C.deserialize(__buffer);
301 			}
302 		}
303 		
304 		static if(!is(typeof(__custom))) mixin({
305 				
306 				static if(hasUDA!(__traits(getMember, T, member), LengthImpl)) {
307 					import std.conv : to;
308 					auto length = getUDAs!(__traits(getMember, T, member), LengthImpl)[0];
309 					immutable e = "L, le, " ~ length.type ~ ", " ~ (length.endianness == -1 ? "endianness" : "EndianType." ~ (cast(EndianType)length.endianness).to!string);
310 				} else {
311 					immutable e = "L, le, L, le";
312 				}
313 				
314 				static if(hasUDA!(__traits(getMember, T, member), NoLength)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeNoLengthArray!(endianness, L, le, M)(__buffer);";
315 				else static if(hasUDA!(__traits(getMember, T, member), Var)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.var, " ~ e ~ ", M)(__buffer);";
316 				else static if(hasUDA!(__traits(getMember, T, member), BigEndian)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.bigEndian, " ~ e ~ ", M)(__buffer);";
317 				else static if(hasUDA!(__traits(getMember, T, member), LittleEndian)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.littleEndian, " ~ e ~ ", M)(__buffer);";
318 				else immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(endianness, " ~ e ~ ", M)(__buffer);";
319 				
320 				static if(!hasUDA!(__traits(getMember, T, member), Condition)) return ret;
321 				else return "with(__container){if(" ~ getUDAs!(__traits(getMember, T, member), Condition)[0].condition ~ "){" ~ ret ~ "}}";
322 				
323 			}());
324 		
325 	}
326 }
327 
328 // ---------
329 // unittests
330 // ---------
331 
332 @("numbers") unittest {
333 
334 	// bools and numbers
335 	
336 	assert(true.serialize() == [1]);
337 	assert(5.serialize!(Endian.bigEndian)() == [0, 0, 0, 5]);
338 	
339 	assert(deserialize!(int, Endian.bigEndian)([0, 0, 0, 5]) == 5);
340 
341 	version(LittleEndian) assert(12.serialize() == [12, 0, 0, 0]);
342 	version(BigEndian) assert(12.serialize() == [0, 0, 0, 12]);
343 
344 }
345 
346 @("arrays") unittest {
347 
348 	assert([1, 2, 3].serialize!(Endian.bigEndian, uint)() == [0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3]);
349 	assert([1, 2, 3].serialize().deserialize!(int[])() == [1, 2, 3]);
350 
351 	ushort[2] test1 = [1, 2];
352 	assert(test1.serialize!(Endian.bigEndian)() == [0, 1, 0, 2]);
353 	test1 = deserialize!(ushort[2], Endian.littleEndian)([2, 0, 1, 0]);
354 	assert(test1 == [2, 1]);
355 
356 }
357 
358 @("associative arrays") unittest {
359 
360 	// associative arrays
361 
362 	int[ushort] test;
363 	test[1] = 112;
364 	assert(test.serialize!(Endian.bigEndian, uint)() == [0, 0, 0, 1, 0, 1, 0, 0, 0, 112]);
365 
366 	test = deserialize!(int[ushort], Endian.bigEndian, ubyte)([1, 0, 0, 0, 0, 0, 55]);
367 	assert(test == [ushort(0): 55]);
368 
369 }
370 
371 @("structs and classes") unittest {
372 
373 	struct Test {
374 
375 		byte a, b, c;
376 
377 	}
378 
379 	Test test = Test(1, 3, 55);
380 	assert(test.serialize() == [1, 3, 55]);
381 
382 	assert(deserialize!Test([1, 3, 55]) == test);
383 
384 }
385 
386 @("attributes") unittest {
387 
388 	struct Test1 {
389 
390 		@BigEndian int a;
391 
392 		@EncodeOnly @LittleEndian ushort b;
393 
394 		@Condition("a==1") @Var uint c;
395 
396 		@DecodeOnly @Var uint d;
397 
398 		@Exclude ubyte e;
399 
400 	}
401 
402 	Test1 test1 = Test1(1, 2, 3, 4, 5);
403 	assert(test1.serialize() == [0, 0, 0, 1, 2, 0, 3]);
404 	assert(deserialize!Test1([0, 0, 0, 1, 4, 12]) == Test1(1, 0, 4, 12));
405 
406 	test1.a = 0;
407 	assert(test1.serialize() == [0, 0, 0, 0, 2, 0]);
408 	assert(deserialize!Test1([0, 0, 0, 0, 0, 0, 0, 0]) == Test1(0, 0, 0, 0));
409 
410 	struct Test2 {
411 
412 		ubyte[] a;
413 
414 		@Length!ushort ushort[] b;
415 
416 		@NoLength uint[] c;
417 
418 	}
419 
420 	Test2 test2 = Test2([1, 2], [3, 4], [5, 6]);
421 	assert(test2.serialize!(Endian.bigEndian, uint)() == [0, 0, 0, 2, 1, 2, 0, 2, 0, 3, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6]);
422 	assert(deserialize!(Test2, Endian.bigEndian, uint)([0, 0, 0, 2, 1, 2, 0, 2, 0, 3, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 1]) == test2);
423 
424 	struct Test3 {
425 
426 		@EndianLength!ushort(Endian.littleEndian) @LittleEndian ushort[] a;
427 
428 		@NoLength ushort[] b;
429 
430 	}
431 
432 	Test3 test3 = Test3([1, 2], [3, 4]);
433 	assert(test3.serialize!(Endian.bigEndian)() == [2, 0, 1, 0, 2, 0, 0, 3, 0, 4]);
434 
435 	struct Test4 {
436 
437 		ubyte a;
438 
439 		@LittleEndian uint b;
440 
441 	}
442 
443 	struct Test5 {
444 
445 		@Length!ubyte Test4[] a;
446 
447 		@NoLength Test4[] b;
448 
449 	}
450 
451 	Test5 test5 = Test5([Test4(1, 2)], [Test4(1, 2), Test4(3, 4)]);
452 	assert(test5.serialize() == [1, 1, 2, 0, 0, 0, 1, 2, 0, 0, 0, 3, 4, 0, 0, 0]);
453 	assert(deserialize!Test5([1, 1, 2, 0, 0, 0, 1, 2, 0, 0, 0, 3, 4, 0, 0, 0]) == test5);
454 
455 }
456 
457 @("using buffer") unittest {
458 
459 	Buffer buffer = new Buffer(64);
460 
461 	serialize(ubyte(55), buffer);
462 	assert(buffer.data.length == 1);
463 	assert(buffer.data!ubyte == [55]);
464 
465 	assert(deserialize!ubyte(buffer) == 55);
466 	assert(buffer.data.length == 0);
467 
468 }