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 
10 struct NoLength {
11 	bool value = true;
12 }
13 struct LengthType(T) {
14 	alias Type = T;
15 }
16 
17 struct CastType(T) {
18 	alias Type = T;
19 }
20 
21 struct ElementAttributes(Attribs...) {
22 	alias Attributes = Attribs;
23 }
24 
25 alias DefaultAttributes = AliasSeq!(NoLength(false), LengthType!ushort);
26 
27 
28 template Serializer(Attributes_...) {
29 	alias Attributes = AliasSeq!(DefaultAttributes, Attributes_);
30 	
31 	template Subserializer(MoreAttributes_...) {
32 		alias Subserializer = Serializer!(Attributes_, MoreAttributes_);
33 	}
34 	alias Subdeserializer = Subserializer;
35 	
36 	template isAttribute(alias T) {
37 		enum isAttribute = Filter!(isDesiredUDA!T, Attributes).length > 0;
38 	}
39 	template GetAttributeType(alias T) {
40 		alias GetAttributeType = Filter!(isDesiredUDA!T, Attributes)[$-1].Type;
41 	}
42 	template getAttributeValue(alias T) {
43 		static if (is(Filter!(isDesiredUDA!T, Attributes)[$-1]))// For if the struct is not created but referencing the type: instantiate it, and use the default.
44 			enum getAttributeValue = Filter!(isDesiredUDA!T, Attributes)[$-1]().value;
45 		else
46 			enum getAttributeValue = Filter!(isDesiredUDA!T, Attributes)[$-1].value;
47 	}
48 	template GetAttributes(alias T) {
49 		template Get(alias EA) {
50 			alias Get = EA.Attributes;
51 		}
52 		alias GetAttributes = staticMap!(Get, Filter!(isDesiredUDA!T, Attributes));
53 	}
54 	template FilterOut_(alias T) {
55 		alias FilterOut_ = Filter!(templateNot!(isDesiredUDA!T), Attributes_);
56 	}
57 	
58 	static if (isAttribute!CastType) {
59 		ubyte[] serialize(T)(T data) if (__traits(compiles, cast(GetAttributeType!CastType) rvalueOf!T) && serializable!(GetAttributeType!CastType)) {
60 			return Serializer!(FilterOut_!CastType).serialize(cast(GetAttributeType!CastType) data);
61 		}
62 		T deserialize(T)(ref const(ubyte)[] data) if (__traits(compiles, cast(T) rvalueOf!(GetAttributeType!CastType)) && deserializable!(GetAttributeType!CastType)) {
63 			return cast(T) Serializer!(FilterOut_!CastType).deserialize!(GetAttributeType!CastType)(data);
64 		}
65 	}
66 	else {
67 		ubyte[] serialize(T)(T data) if(isIntegral!T || isSomeChar!T || isBoolean!T || isFloatOrDouble!T) {
68 			return nativeToLittleEndian(data).dup;
69 		}
70 		ubyte[] serialize(T)(T data) if(isDynamicArray!T) {
71 			alias Serializer_ = Serializer!(FilterOut_!ElementAttributes, GetAttributes!ElementAttributes);
72 			static if (getAttributeValue!NoLength)
73 				return data.map!(Serializer_.serialize).joiner.array;
74 			else
75 				return serialize(cast(GetAttributeType!LengthType) data.length) ~ data.map!(Serializer_.serialize).joiner.array;
76 		}
77 		
78 		T deserialize(T)(ref const(ubyte)[] data) if (isIntegral!T || isSomeChar!T || isBoolean!T || isFloatOrDouble!T) {
79 			scope (success)
80 				data = data[T.sizeof..$];
81 			return littleEndianToNative!T(data[0..T.sizeof]);
82 		}
83 		T deserialize(T)(ref const(ubyte)[] data) if (isDynamicArray!T) {
84 			alias Serializer_ = Serializer!(FilterOut_!ElementAttributes, GetAttributes!ElementAttributes);
85 			static if (getAttributeValue!NoLength)
86 				return repeat(null).map!(_=>Serializer_.deserialize!(ForeachType!T)(data)).until!((lazy _)=>data.length==0).array;
87 			else
88 				return repeat(null, deserialize!(GetAttributeType!LengthType)(data)).map!(_=>Serializer_.deserialize!(ForeachType!T)(data)).array;
89 		}
90 	}
91 }
92 alias Deserializer = Serializer;
93 template serialize(Attributes...) {
94 	alias serialize = Serializer!Attributes.serialize;
95 }
96 template deserialize(T, Attributes...) {
97 	alias deserialize = Deserializer!Attributes.deserialize!T;
98 }
99 
100 template serializable(T) {
101 	enum serializable = __traits(compiles, rvalueOf!T.serialize);
102 }
103 template serializable(alias v) {
104 	enum serializable = __traits(compiles, v.serialize);
105 }
106 template deserializable(T) {
107 	enum deserializable = __traits(compiles, deserialize!T(lvalueOf!(const(ubyte)[])));
108 }
109 
110 /// Copied from phobas/std/bitmanip
111 private template isFloatOrDouble(T)
112 {
113     enum isFloatOrDouble = isFloatingPoint!T &&
114                            !is(Unqual!(FloatingPointTypeOf!T) == real);
115 }
116 
117 
118 @("number")
119 unittest {
120 	{
121 		int data = 5;
122 		const(ubyte)[] bytes = serialize(data);
123 		assert(bytes == [5,0,0,0] || bytes == [0,0,0,5]);
124 		assert(bytes.deserialize!(typeof(data)) == data);
125 	}
126 	{
127 		ushort data = 5;
128 		const(ubyte)[] bytes = serialize(data);
129 		assert(bytes == [5,0] || bytes == [0,5]);
130 		assert(bytes.deserialize!(typeof(data)) == data);
131 	}
132 }
133 @("cast type")
134 unittest {
135 	{
136 		int data = 5;
137 		alias Serializer_ = Serializer!(CastType!byte);
138 		const(ubyte)[] bytes = Serializer_.serialize(data);
139 		assert(bytes == [5]);
140 		assert(Serializer_.deserialize!(typeof(data))(bytes) == data);
141 	}
142 }
143 @("array")
144 unittest {
145 	{
146 		int[] data = [1,2];
147 		const(ubyte)[] bytes = serialize(data);
148 		assert(bytes == [2,0, 1,0,0,0, 2,0,0,0] || bytes == [0,2, 0,0,0,1, 0,0,0,2]);
149 		assert(bytes.deserialize!(typeof(data)) == data);
150 	}
151 	// LengthType
152 	{
153 		int[] data = [1,2];
154 		alias Serializer_ = Serializer!(LengthType!uint);
155 		const(ubyte)[] bytes = Serializer_.serialize(data);
156 		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]);
157 		assert(Serializer_.deserialize!(typeof(data))(bytes) == data);
158 	}
159 	// NoLength
160 	{
161 		int[] data = [1,2];
162 		alias Serializer_ = Serializer!(NoLength);
163 		const(ubyte)[] bytes = Serializer_.serialize(data);
164 		assert(bytes == [1,0,0,0, 2,0,0,0] || bytes == [0,0,0,1, 0,0,0,2]);
165 		assert(Serializer_.deserialize!(typeof(data))(bytes) == data);
166 	}
167 	// ElementAttributes
168 	{
169 		int[] data = [1,2];
170 		alias Serializer_ = Serializer!(ElementAttributes!(CastType!ubyte));
171 		const(ubyte)[] bytes = Serializer_.serialize(data);
172 		assert(bytes == [2,0, 1,2] || bytes == [0,2, 1,2]);
173 		assert(Serializer_.deserialize!(typeof(data))(bytes) == data);
174 	}
175 	// Nested & ElementAttributes
176 	{
177 		int[][] data = [[1,2],[3,4]];
178 		alias Serializer_ = Serializer!(LengthType!ubyte, ElementAttributes!(ElementAttributes!(CastType!ubyte)));
179 		const(ubyte)[] bytes = Serializer_.serialize(data);
180 		assert(bytes == [2, 2,1,2, 2,3,4]);
181 		assert(Serializer_.deserialize!(typeof(data))(bytes) == data);
182 	}
183 }
184 
185 /// Copied from std.traits
186 private template isDesiredUDA(alias attribute)
187 {
188     template isDesiredUDA(alias toCheck)
189     {
190         static if (is(typeof(attribute)) && !__traits(isTemplate, attribute))
191         {
192             static if (__traits(compiles, toCheck == attribute))
193                 enum isDesiredUDA = toCheck == attribute;
194             else
195                 enum isDesiredUDA = false;
196         }
197         else static if (is(typeof(toCheck)))
198         {
199             static if (__traits(isTemplate, attribute))
200                 enum isDesiredUDA =  isInstanceOf!(attribute, typeof(toCheck));
201             else
202                 enum isDesiredUDA = is(typeof(toCheck) == attribute);
203         }
204         else static if (__traits(isTemplate, attribute))
205             enum isDesiredUDA = isInstanceOf!(attribute, toCheck);
206         else
207             enum isDesiredUDA = is(toCheck == attribute);
208     }
209 }
210