1 module treeserial;
2 
3 import std.traits;
4 import std.meta;
5 import std.bitmanip;
6 
7 import std.typecons;
8 
9 import std.algorithm;
10 import std.range;
11 import std.conv;
12 
13 struct NoLength {
14 	bool value = true;
15 }
16 struct LengthType(T) {
17 	alias Type = T;
18 }
19 
20 struct CastType(T) {
21 	alias Type = T;
22 }
23 
24 struct ElementAttributes(Attribs...) {
25 	alias Attributes = Attribs;
26 }
27 
28 struct Include {
29 	bool value = true;
30 }
31 enum Exclude = Include(false);
32 
33 alias DefaultAttributes = AliasSeq!(NoLength(false), LengthType!ushort);
34 
35 
36 template Serializer(Attributes...) {
37 	
38 	template Subserializer(MoreAttributes...) {
39 		alias Subserializer = Serializer!(Attributes, MoreAttributes);
40 	}
41 	alias Subdeserializer = Subserializer;
42 	
43 	template isAttribute(alias T, AddedDefaults...) {
44 		enum isAttribute = Filter!(isDesiredUDA!T, AliasSeq!(DefaultAttributes, AddedDefaults, Attributes)).length > 0;
45 	}
46 	template GetAttributeType(alias T, AddedDefaults...) {
47 		alias GetAttributeType = Filter!(isDesiredUDA!T, AliasSeq!(DefaultAttributes, AddedDefaults, Attributes))[$-1].Type;
48 	}
49 	template getAttributeValue(alias T, AddedDefaults...) {
50 		static if (is(Filter!(isDesiredUDA!T, AliasSeq!(DefaultAttributes, AddedDefaults, Attributes))[$-1]))// For if the struct is not created but referencing the type: instantiate it, and use the default.
51 			enum getAttributeValue = Filter!(isDesiredUDA!T, AliasSeq!(DefaultAttributes, AddedDefaults, Attributes))[$-1]().value;
52 		else
53 			enum getAttributeValue = Filter!(isDesiredUDA!T, AliasSeq!(DefaultAttributes, AddedDefaults, Attributes))[$-1].value;
54 	}
55 	template GetAttributes(alias T) {
56 		template Get(alias EA) {
57 			alias Get = EA.Attributes;
58 		}
59 		alias GetAttributes = staticMap!(Get, Filter!(isDesiredUDA!T, Attributes));
60 	}
61 	template FilterOut(alias T) {
62 		alias FilterOut = Filter!(templateNot!(isDesiredUDA!T), Attributes);
63 	}
64 	
65 	static if (isAttribute!CastType) {
66 		ubyte[] serialize(T)(T value) if (__traits(compiles, cast(GetAttributeType!CastType) rvalueOf!T) && serializable!(GetAttributeType!CastType)) {
67 			return Serializer!(FilterOut!CastType).serialize(cast(GetAttributeType!CastType) value);
68 		}
69 		T deserialize(T)(ref const(ubyte)[] value) if (__traits(compiles, cast(T) rvalueOf!(GetAttributeType!CastType)) && deserializable!(GetAttributeType!CastType)) {
70 			return cast(T) Serializer!(FilterOut!CastType).deserialize!(GetAttributeType!CastType)(value);
71 		}
72 	}
73 	else {
74 		ubyte[] serialize(T)(T value) if(isIntegral!T || isSomeChar!T || isBoolean!T || isFloatOrDouble!T) {
75 			return nativeToLittleEndian(value).dup;
76 		}
77 		ubyte[] serialize(T)(T value) if(isDynamicArray!T || isStaticArray!T) {
78 			alias Serializer_ = Serializer!(FilterOut!ElementAttributes, GetAttributes!ElementAttributes);
79 			static if (isSomeChar!(ForeachType!T)) {
80 				import std.utf;
81 				static if (isStaticArray!T || getAttributeValue!NoLength)
82 					return value[].byCodeUnit.map!(Serializer!(FilterOut!NoLength).serialize).joiner.array;
83 				else
84 					return serialize(cast(GetAttributeType!LengthType) value.length) ~ value.byCodeUnit.map!(Serializer_.serialize).joiner.array;
85 			}
86 			else {
87 				static if (isStaticArray!T || getAttributeValue!NoLength)
88 					return value[].map!(Serializer!(FilterOut!NoLength).serialize).joiner.array;
89 				else
90 					return serialize(cast(GetAttributeType!LengthType) value.length) ~ value.map!(Serializer_.serialize).joiner.array;
91 			}
92 		}
93 		ubyte[] serialize(T)(T value) if(isTuple!T) {
94 			import std.stdio;
95 			ubyte[] bytes;
96 			static foreach (i; 0..value.length)
97 				bytes ~= serialize(value[i]);
98 			return bytes;
99 		}
100 		ubyte[] serialize(T)(T value) if((is(T == class) || is(T == struct)) && !isTuple!T) {
101 			static if(__traits(hasMember, T, "serialize")) {
102 				return value.serialize;
103 			}
104 			else {
105 				ubyte[] bytes;
106 				foreach (memName; __traits(derivedMembers, T)) {{
107 					static if (memName != "this") {
108 						alias mem = __traits(getMember, T, memName);
109 						alias Mem = typeof(mem);
110 						static if (Subserializer!(__traits(getAttributes, mem)).getAttributeValue!(Include, Include(
111 							!isCallable!mem
112 							&& !__traits(compiles, { mixin("auto test=T." ~ memName ~ ";"); })	// static members
113 						))) {
114 							bytes ~= Serializer!(Serializer!(FilterOut!ElementAttributes).FilterOut!Include, GetAttributes!ElementAttributes).serialize(mixin("value."~memName));
115 						}
116 					}
117 				}}
118 				return bytes;
119 			}
120 		}
121 		
122 		T deserialize(T)(ref const(ubyte)[] buffer) if (isIntegral!T || isSomeChar!T || isBoolean!T || isFloatOrDouble!T) {
123 			scope (success)
124 				buffer = buffer[T.sizeof..$];
125 			return littleEndianToNative!T(buffer[0..T.sizeof]);
126 		}
127 		T deserialize(T)(ref const(ubyte)[] buffer) if (isDynamicArray!T || isStaticArray!T) {
128 			alias Serializer_ = Serializer!(FilterOut!ElementAttributes, GetAttributes!ElementAttributes);
129 			static if (isStaticArray!T)
130 				return repeat(null, T.length).map!(_=>Serializer_.deserialize!(ForeachType!T)(buffer)).array.to!T;
131 			else static if (getAttributeValue!NoLength)
132 				return repeat(null).map!(_=>Serializer!(FilterOut!NoLength).deserialize!(ForeachType!T)(buffer)).until!((lazy _)=>buffer.length==0).array;
133 			else
134 				return repeat(null, deserialize!(GetAttributeType!LengthType)(buffer)).map!(_=>Serializer_.deserialize!(ForeachType!T)(buffer)).array;
135 		}
136 		T deserialize(T)(ref const(ubyte)[] buffer) if(is(T == class) || is(T == struct)) {
137 			static if(__traits(hasMember, T, "deserialize")) {
138 				return buffer.serialize;
139 			}
140 			else {
141 				T value;
142 				static if (is(T == class))
143 					value = new T();
144 				foreach (memName; __traits(derivedMembers, T)) {{
145 					static if (memName != "this") {
146 						alias mem = __traits(getMember, T, memName);
147 						alias Mem = typeof(mem);
148 						static if(isCallable!Mem)
149 							alias MemT = ReturnType!Mem;
150 						else
151 							alias MemT = Mem;
152 						static if (Subserializer!(__traits(getAttributes, mem)).getAttributeValue!(Include, Include(
153 							!isCallable!mem
154 							&& !__traits(compiles, { mixin("auto test=T." ~ memName ~ ";"); })	// static members
155 						))) {
156 							mixin(q{value..}~memName) = Serializer!(Serializer!(FilterOut!ElementAttributes).FilterOut!Include, GetAttributes!ElementAttributes).deserialize!MemT(buffer);
157 						}
158 					}
159 				}}
160 				return value;
161 			}
162 		}
163 	}
164 }
165 alias Deserializer = Serializer;
166 template serialize(Attributes...) {
167 	alias serialize = Serializer!Attributes.serialize;
168 }
169 template deserialize(T, Attributes...) {
170 	alias deserialize = Deserializer!Attributes.deserialize!T;
171 }
172 
173 template serializable(T) {
174 	enum serializable = __traits(compiles, rvalueOf!T.serialize);
175 }
176 template serializable(alias v) {
177 	enum serializable = __traits(compiles, v.serialize);
178 }
179 template deserializable(T) {
180 	enum deserializable = __traits(compiles, deserialize!T(lvalueOf!(const(ubyte)[])));
181 }
182 
183 /// Copied from phobas/std/bitmanip
184 private template isFloatOrDouble(T)
185 {
186     enum isFloatOrDouble = isFloatingPoint!T &&
187                            !is(Unqual!(FloatingPointTypeOf!T) == real);
188 }
189 
190 
191 @("number")
192 unittest {
193 	{
194 		int data = 5;
195 		const(ubyte)[] bytes = serialize(data);
196 		assert(bytes == [5,0,0,0] || bytes == [0,0,0,5]);
197 		assert(bytes.deserialize!(typeof(data)) == data);
198 	}
199 	{
200 		ushort data = 5;
201 		const(ubyte)[] bytes = serialize(data);
202 		assert(bytes == [5,0] || bytes == [0,5]);
203 		assert(bytes.deserialize!(typeof(data)) == data);
204 	}
205 }
206 @("cast type")
207 unittest {
208 	{
209 		int data = 5;
210 		alias Serializer_ = Serializer!(CastType!byte);
211 		const(ubyte)[] bytes = Serializer_.serialize(data);
212 		assert(bytes == [5]);
213 		assert(Serializer_.deserialize!(typeof(data))(bytes) == data);
214 	}
215 }
216 @("array")
217 unittest {
218 	{
219 		int[] data = [1,2];
220 		const(ubyte)[] bytes = serialize(data);
221 		assert(bytes == [2,0, 1,0,0,0, 2,0,0,0] || bytes == [0,2, 0,0,0,1, 0,0,0,2]);
222 		assert(bytes.deserialize!(typeof(data)) == data);
223 	}
224 	// LengthType
225 	{
226 		int[] data = [1,2];
227 		alias Serializer_ = Serializer!(LengthType!uint);
228 		const(ubyte)[] bytes = Serializer_.serialize(data);
229 		assert(bytes == [2,0,0,0, 1,0,0,0, 2,0,0,0] || bytes == [0,0,0,2, 0,0,0,1, 0,0,0,2]);
230 		assert(Serializer_.deserialize!(typeof(data))(bytes) == data);
231 	}
232 	// NoLength
233 	{
234 		int[] data = [1,2];
235 		alias Serializer_ = Serializer!(NoLength);
236 		const(ubyte)[] bytes = Serializer_.serialize(data);
237 		assert(bytes == [1,0,0,0, 2,0,0,0] || bytes == [0,0,0,1, 0,0,0,2]);
238 		assert(Serializer_.deserialize!(typeof(data))(bytes) == data);
239 	}
240 	// ElementAttributes
241 	{
242 		int[] data = [1,2];
243 		alias Serializer_ = Serializer!(ElementAttributes!(CastType!ubyte));
244 		const(ubyte)[] bytes = Serializer_.serialize(data);
245 		assert(bytes == [2,0, 1,2] || bytes == [0,2, 1,2]);
246 		assert(Serializer_.deserialize!(typeof(data))(bytes) == data);
247 	}
248 	// Nested & ElementAttributes
249 	{
250 		int[][] data = [[1,2],[3,4]];
251 		alias Serializer_ = Serializer!(LengthType!ubyte, ElementAttributes!(ElementAttributes!(CastType!ubyte)));
252 		const(ubyte)[] bytes = Serializer_.serialize(data);
253 		assert(bytes == [2, 2,1,2, 2,3,4]);
254 		assert(Serializer_.deserialize!(typeof(data))(bytes) == data);
255 	}
256 	// Static
257 	{
258 		int[2] data = [1,2];
259 		const(ubyte)[] bytes = serialize(data);
260 		assert(bytes == [1,0,0,0, 2,0,0,0] || bytes == [0,0,0,1, 0,0,0,2]);
261 		assert(bytes.deserialize!(typeof(data)) == data);
262 	}
263 }
264 @("class")
265 unittest {
266 	{
267 		static class C {
268 			ubyte a = 1;
269 			ubyte b = 2;
270 			@Exclude ubyte c = 3;
271 			@property ubyte d() { return 4; }
272 			@Include @property ubyte e() { return 5; } @Include @property void e(ubyte) {}
273 			ubyte f() { return 6; }
274 			@Include ubyte g() { return 7; } @Include void g(ubyte) {}
275 			static ubyte h = 8;
276 			@Include static ubyte i = 9;
277 			static ubyte j() { return 10; }
278 			@Include static ubyte k() { return 11; } @Include static void k(ubyte) {}
279 		}
280 		C data = new C;
281 		const(ubyte)[] bytes = serialize(data);
282 		assert(bytes == [1,2,5,7,9,11]);
283 		
284 		const(ubyte)[] nb = [11,22,55,77,99,110];
285 		C nd = nb.deserialize!(typeof(data));
286 		assert(nd.a == 11);
287 		assert(nd.b == 22);
288 		assert(nd.c == 3);
289 		assert(nd.d == 4);
290 		assert(nd.e == 5);
291 		assert(nd.f == 6);
292 		assert(nd.g == 7);
293 		assert(nd.h == 8);
294 		assert(nd.i == 99);
295 		assert(nd.j == 10);
296 		assert(nd.k == 11);
297 	}
298 }
299 
300 /// Copied from std.traits
301 private template isDesiredUDA(alias attribute)
302 {
303     template isDesiredUDA(alias toCheck)
304     {
305         static if (is(typeof(attribute)) && !__traits(isTemplate, attribute))
306         {
307             static if (__traits(compiles, toCheck == attribute))
308                 enum isDesiredUDA = toCheck == attribute;
309             else
310                 enum isDesiredUDA = false;
311         }
312         else static if (is(typeof(toCheck)))
313         {
314             static if (__traits(isTemplate, attribute))
315                 enum isDesiredUDA =  isInstanceOf!(attribute, typeof(toCheck));
316             else
317                 enum isDesiredUDA = is(typeof(toCheck) == attribute);
318         }
319         else static if (__traits(isTemplate, attribute))
320             enum isDesiredUDA = isInstanceOf!(attribute, toCheck);
321         else
322             enum isDesiredUDA = is(toCheck == attribute);
323     }
324 }
325