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