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