1 module hunt.serialization.Specify;
2 
3 import std.traits;
4 import std.range;
5 
6 import hunt.serialization.Common;
7 import hunt.serialization.BinarySerializer;
8 import hunt.serialization.BinaryDeserializer;
9 
10 import hunt.logging.ConsoleLogger;
11 
12 template PtrType(T) {
13     static if (is(T == bool) || is(T == char)) {
14         alias PtrType = ubyte*;
15     } else static if (is(T == float)) {
16         alias PtrType = uint*;
17     } else static if (is(T == double)) {
18         alias PtrType = ulong*;
19     } else {
20         alias PtrType = Unsigned!T*;
21     }
22 }
23 
24 enum ubyte[] NULL = ['n', 'u', 'l', 'l'];
25 
26 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == wchar)) {
27     specify!(options)(obj, *cast(ushort*)&val);
28 }
29 
30 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == dchar)) {
31     specify!(options)(obj, *cast(uint*)&val);
32 }
33 
34 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == ushort)) {
35     ubyte valh = (val >> 8);
36     ubyte vall = val & 0xff;
37     obj.putUbyte(valh);
38     obj.putUbyte(vall);
39     val = (valh << 8) + vall;
40 }
41 
42 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == uint)) {
43     ubyte val0 = (val >> 24);
44     ubyte val1 = cast(ubyte)(val >> 16);
45     ubyte val2 = cast(ubyte)(val >> 8);
46     ubyte val3 = val & 0xff;
47     obj.putUbyte(val0);
48     obj.putUbyte(val1);
49     obj.putUbyte(val2);
50     obj.putUbyte(val3);
51     val = (val0 << 24) + (val1 << 16) + (val2 << 8) + val3;
52 }
53 
54 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == ulong)) {
55     T newVal;
56     for (int i = 0; i < T.sizeof; ++i) {
57         immutable shiftBy = 64 - (i + 1) * T.sizeof;
58         ubyte byteVal = (val >> shiftBy) & 0xff;
59         obj.putUbyte(byteVal);
60         newVal |= (cast(T) byteVal << shiftBy);
61     }
62     val = newVal;
63 }
64 
65 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (isStaticArray!T) {
66     static if (is(Unqual!(ElementType!T) : ubyte) && T.sizeof == 1) {
67         obj.putRaw(cast(ubyte[]) val);
68     } else {
69         foreach (ref v; val) {
70             specify!(options)(obj, v);
71         }
72     }
73 }
74 
75 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == string)) {
76     ushort len = cast(ushort) val.length;
77     specify!(options)(obj, len);
78 
79     static if (is(C == BinarySerializer)) {
80         obj.putRaw(cast(ubyte[]) val);
81     } else {
82         val = cast(string) obj.take(len).idup;
83     }
84 }
85 
86 void specify(SerializationOptions options, U, C, T)(auto ref C obj, ref T val) if(is(T == string)) {
87     U length = cast(U)val.length;
88     assert(length == val.length, "overflow");
89     specify!(options)(obj,length);
90 
91     static if(is(C == BinarySerializer))
92         obj.putRaw(cast(ubyte[])val);
93     else
94         val = cast(string) obj.putRaw(length).idup;
95 }
96 
97 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (isAssociativeArray!T) {
98     ushort length = cast(ushort) val.length;
99     specify!(options)(obj, length);
100     const keys = val.keys;
101     for (ushort i = 0; i < length; ++i) {
102         KeyType!T k = keys.length ? keys[i] : KeyType!T.init;
103         auto v = keys.length ? val[k] : ValueType!T.init;
104 
105         specify!(options)(obj, k);
106         specify!(options)(obj, v);
107         val[k] = v;
108     }
109 }
110 
111 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (isPointer!T) {
112     alias ValueType = PointerTarget!T;
113     specify!(options)(obj, *val);
114 }
115 
116 //ubyte
117 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == ubyte)) {
118     obj.putUbyte(val);
119 }
120 
121 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val)
122         if (!is(T == enum) && (isSigned!T || isBoolean!T || is(T == char) || isFloatingPoint!T)) {
123     specifyPtr!(options)(obj, val);
124 }
125 
126 //ENUM
127 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == enum)) {
128     specify!(options)(obj, cast(Unqual!(OriginalType!(T))) val);
129 }
130 
131 void specify(SerializationOptions options, C, T : E[], E)(auto ref C obj, ref T val)
132         if (is(C == BinarySerializer) && isInputRange!T && !isInfinite!T
133             && !is(T == string) && !isStaticArray!T && !isAssociativeArray!T) {
134     enum hasLength = is(typeof(() { auto l = val.length; }));
135     ushort length = cast(ushort) val.length;
136     specify!(options)(obj, length);
137 
138     static if (hasSlicing!(Unqual!T) && is(Unqual!(ElementType!T) : ubyte) && T.sizeof == 1) {
139         obj.putRaw(cast(ubyte[]) val.array);
140     } else {
141         foreach (ref v; val) {
142             specify!(options)(obj, v);
143         }
144     }
145 }
146 
147 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val)
148         if (isAggregateType!T && !isInputRange!T && !isOutputRange!(T, ubyte)) {
149     
150     debug(HUNT_DEBUG_MORE) tracef("Setting: %s, value: %s", T.stringof, val);
151     loopMembers!(options, C, T)(obj, val);
152 }
153 
154 void specify(SerializationOptions options, C, T)(auto ref C obj, ref T val)
155         if (isDecerealiser!C && !isOutputRange!(T, ubyte) && isDynamicArray!T && !is(T == string)) {
156     ushort length;
157 
158     specify!(options)(obj, length);
159     decerealiseArrayImpl!(options)(obj, val, length);
160 }
161 
162 void decerealiseArrayImpl(SerializationOptions options, C, T : E[], E, U)(auto ref C obj, ref T val, U length)
163         if (is(T == E[], E) && isDecerealiser!C) {
164 
165     ulong neededBytes(T)(ulong length) {
166         alias E = ElementType!T;
167         static if (isScalarType!E)
168             return length * E.sizeof;
169         else static if (isInputRange!E)
170             return neededBytes!E(length);
171         else
172             return 0;
173     }
174 
175     immutable needed = neededBytes!T(length);
176 
177     static if (is(Unqual!(ElementType!T) : ubyte) && T.sizeof == 1) {
178         val = obj.putRaw(length).dup;
179     } else {
180         if (val.length != length)
181             val.length = cast(uint) length;
182 
183         foreach (ref e; val) {
184             obj.specify!(options)(e);
185         }
186     }
187 }
188 
189 void specifyPtr(SerializationOptions options, C, T)(auto ref C obj, ref T val) {
190     auto ptr = cast(PtrType!T)(&val);
191     specify!(options)(obj, *ptr);
192 }
193 
194 void loopMembers(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == struct)) {
195     loopMembersImpl!(T, options)(obj, val);
196 }
197 
198 void loopMembers(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == class)) {
199 
200     debug(HUNT_DEBUG_MORE) tracef("Setting: %s, value: %s", T.stringof, val);
201 
202     static if (is(C == BinarySerializer)) {
203         if (val is null) {
204             obj.putRaw(NULL);
205             return;
206         }
207         //assert(val !is null, "null value cannot be serialised");
208     }
209 
210     static if (is(C == BinaryDeserializer)) {
211         if (obj.isNullObj) {
212             obj.take(NULL.length);
213             val = null;
214             return;
215         }
216     }
217 
218     static if (is(typeof(() { val = new T; }))) {
219         if (val is null)
220             val = new T;
221     } 
222 
223     obj.putClass!(options, T)(val);
224 }
225 
226 
227 void loopMembersImpl(T, SerializationOptions options, C, VT)
228         (auto ref C obj, ref VT val) {
229     // foreach (member; __traits(derivedMembers, T)) {
230     //     enum isMemberVariable = is(typeof(() {
231     //                 __traits(getMember, val, member) = __traits(getMember, val, member).init;
232     //             }));
233     //     static if (isMemberVariable) {
234     //         specifyAggregateMember!member(obj, val);
235     //     }
236     // }
237     
238         debug(HUNT_DEBUG_MORE) pragma(msg, "T=> " ~ T.stringof);
239     static foreach (string member; FieldNameTuple!T) {
240         debug(HUNT_DEBUG_MORE) pragma(msg, "Field member: " ~ member);
241         static if(!member.empty())
242         {{
243             alias currentMember = __traits(getMember, T, member);
244             alias memberType = typeof(currentMember);
245 
246             static if(hasUDA!(currentMember, Ignore)) {
247                 enum canDeserialize = false;
248                 version(HUNT_DEBUG) {
249                     infof("Ignore a member: %s %s", memberType.stringof, member);
250                 } 
251             } else static if(options.onlyPublic) {
252                 static if (__traits(getProtection, currentMember) == "public") {
253                     enum canDeserialize = true;
254                 } else {
255                     enum canDeserialize = false;
256                 }
257             } else {
258                 enum canDeserialize = true;
259             }
260 
261             static if(canDeserialize) {
262                 debug(HUNT_DEBUG_MORE) tracef("name: %s", member);
263                 specify!(options)(obj, __traits(getMember, val, member));
264                 debug(HUNT_DEBUG_MORE) infof("value: %s", __traits(getMember, val, member));
265             }
266         }}
267     }    
268 }
269 
270 
271 // void specifyAggregateMember(string member, SerializationOptions options, C, T)(auto ref C obj, ref T val) {
272 //     import std.meta : staticIndexOf;
273 
274 //     enum NoCereal;
275 //     enum noCerealIndex = staticIndexOf!(NoCereal, __traits(getAttributes,
276 //                 __traits(getMember, val, member)));
277 //     static if (noCerealIndex == -1) {
278 //         specifyMember!(member, options)(obj, val);
279 //     }
280 // }
281 
282 // void specifyMember(string member, SerializationOptions options, C, T)(auto ref C obj, ref T val) {
283 //     // alias currentMember = __traits(getMember, val, member);
284 //     // static if(isAggregateType!(typeof(currentMember))) {
285 //     //     specify!(options)(obj, __traits(getMember, val, member));
286 //     // } else {
287 //     //     specify(obj, __traits(getMember, val, member));
288 //     // }
289 //     debug(HUNT_DEBUG_MORE) tracef("name: %s", member);
290 //     specify!(options)(obj, __traits(getMember, val, member));
291 //     debug(HUNT_DEBUG_MORE) infof("value: %s", __traits(getMember, val, member));
292 // }
293 
294 void specifyBaseClass(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == class)) {
295     foreach (base; BaseTypeTuple!T) {
296         loopMembersImpl!(base, options)(obj, val);
297     }
298 }
299 
300 void specifyClass(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == class)) {
301     static if(options.traverseBase) {
302         specifyBaseClass!(options)(obj, val);
303     }
304     loopMembersImpl!(T, options)(obj, val);
305 }
306 
307 void checkDecerealiser(T)() {
308     //static assert(T.type == CerealType.ReadBytes);
309     auto dec = T();
310     ulong bl = dec.bytesLeft;
311 }
312 
313 enum isDecerealiser(T) = (is(T == BinaryDeserializer) && is(typeof(checkDecerealiser!T)));