1 ///
2 module sbin.deserialize;
3 
4 import std.format;
5 
6 import sbin.type;
7 import sbin.vluint;
8 import sbin.zigzag;
9 import sbin.exception;
10 import sbin.repr;
11 
12 /++ Deserialize part of input ragne to `Target` value
13 
14     Params:
15         range = input range with serialized data (not saved before work)
16         target = reference to result object
17  +/
18 void sbinDeserializePart(RH=EmptyReprHandler, R, Target...)(ref R range, ref Target target)
19     if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte) && isReprHandler!RH)
20 {
21     import sbin.util.stack : Stack;
22 
23     static struct FieldName
24     {
25         string name;
26         ptrdiff_t index = -1;
27 
28         string toString() const
29         {
30             if (index != -1) return format!"%s[%d]"(name, index);
31             else return name;
32         }
33     }
34 
35     alias FNStack = Stack!(FieldName, 8);
36 
37     FNStack fNameStack;
38 
39     static struct WrapRng
40     {
41         R* rng;
42         size_t* count;
43         FNStack* fstack;
44 
45         string field() const
46         {
47             import std.algorithm: map, joiner;
48             import std.array : array;
49             import std.conv : to;
50             return fstack.getData().map!(a=>a.toString()).joiner(".").array.to!string;
51         }
52 
53         string type;
54         size_t vcnt;
55         size_t vexp;
56 
57         ubyte front() @property
58         {
59             enforce (!(*rng).empty, new SBinDeserializeEmptyRangeException(
60                         Target.stringof, field(), type, vcnt, vexp, *count));
61             return (*rng).front;
62         }
63 
64         void popFront()
65         {
66             (*rng).popFront();
67             (*count)++;
68         }
69 
70         bool empty() @property { return (*rng).empty; }
71     }
72 
73     void setRngFields(ref WrapRng rng, string type, size_t vcnt, size_t vexp)
74     { rng.type = type; rng.vcnt = vcnt; rng.vexp = vexp; }
75 
76     struct StackHolder
77     {
78         this(string name, ptrdiff_t idx=-1)
79         { fNameStack.push(FieldName(name, idx)); }
80         ~this() { fNameStack.pop(); }
81     }
82 
83     ubyte pop(ref WrapRng rng, string field, size_t idx, string type, size_t vcnt, size_t vexp)
84     {
85         const _ = StackHolder(field, idx);
86         setRngFields(rng, type, vcnt, vexp);
87         auto ret = rng.front();
88         rng.popFront();
89         return ret;
90     }
91 
92     void impl(T)(ref WrapRng r, ref T trg, string field, ptrdiff_t idx=-1)
93     {
94         const __ = StackHolder(field, idx);
95 
96         static if (hasRepr!(RH, T))
97         {
98             serializeRepr!(RH, T) repr;
99             impl(r, repr, "repr");
100             trg = RH.fromRepr(repr);
101         }
102         else static if (is(T == enum))
103         {
104             immutable EM = [EnumMembers!T];
105             alias ENT = EnumNumType!T;
106             ubyte[ENT.sizeof] tmp;
107             foreach (i, ref v; tmp)
108                 v = pop(r, "byte", i, T.stringof, i, ENT.sizeof);
109             trg = EM[tmp.unpack!ENT];
110         }
111         else static if (is(T == vluint))
112         {
113             setRngFields(r, "vluint", 0, 10);
114             trg = vluint(readVLUInt(r));
115         }
116         else static if (is(T == vlint))
117         {
118             setRngFields(r, "vlint", 0, 10);
119             trg = vlint(zzDecode(readVLUInt(r)));
120         }
121         else static if (is(T : double) || is(T : long))
122         {
123             ubyte[T.sizeof] tmp;
124             foreach (i, ref v; tmp)
125                 v = pop(r, "byte", i, T.stringof, i, T.sizeof);
126             trg = tmp.unpack!T;
127         }
128         else static if (isVoidArray!T)
129         {
130             static if (isDynamicArray!T)
131             {
132                 auto _ = StackHolder("length");
133                 setRngFields(r, "vluint", 0, 10);
134                 const l = cast(size_t)readVLUInt(r);
135                 if (trg.length != l) trg.length = l;
136             }
137 
138             auto tmp = (() @trusted => cast(ubyte[])trg[])();
139             foreach (i, ref v; tmp) impl(r, v, "elem", i);
140         }
141         else static if (isStaticArray!T)
142         {
143             foreach (i, ref v; trg) impl(r, v, "elem", i);
144         }
145         else static if (isSomeString!T)
146         {
147             const len = (() {
148                 auto _ = StackHolder("length");
149                 setRngFields(r, "vluint", 0, 10);
150                 return cast(size_t)readVLUInt(r);
151             })();
152             auto tmp = new ubyte[](len);
153             foreach (i, ref v; tmp)
154                 v = pop(r, "elem", i, T.stringof, i, len);
155             trg = (() @trusted => cast(T)tmp)();
156         }
157         else static if (isDynamicArray!T)
158         {
159             const len = (()
160             {
161                 auto _ = StackHolder("length");
162                 setRngFields(r, "vluint", 0, 10);
163                 return cast(size_t)readVLUInt(r);
164             })();
165             if (trg.length != len) trg.length = len;
166             foreach (i, ref v; trg) impl(r, v, "elem", i);
167         }
168         else static if (isAssociativeArray!T)
169         {
170             const len = ((){
171                 auto _ = StackHolder("length");
172                 setRngFields(r, "vluint", 0, 10);
173                 return cast(size_t)readVLUInt(r);
174             })();
175 
176             (() @trusted => trg.clear())();
177 
178             foreach (i; 0 .. len)
179             {
180                 KeyType!T k;
181                 ValueType!T v;
182                 impl(r, k, "key", i);
183                 impl(r, v, "value", i);
184                 trg[k] = v;
185             }
186 
187             (() @trusted => trg.rehash())();
188         }
189         else static if (isTagged!(T).any)
190         {
191             import std.algorithm.mutation : move;
192 
193             TaggedTagType!T tag;
194             impl(r, tag, "tag");
195 
196             FS: final switch (tag)
197             {
198                 static foreach (t; getTaggedAllTags!T)
199                 {
200                     case t:
201                         TaggedTypeByTag!(T, t) tmp;
202 
203                         // do not try deserialize null for nullable type
204                         static if (!is(typeof(tmp) == typeof(null)))
205                             impl(r, tmp, "value");
206 
207                         static if (__traits(compiles, trg = tmp)) trg = tmp;
208                         else
209                         {
210                             static if (isTagged!(T).isSumType)
211                                 trg = T(move(tmp));
212                             else
213                                 trg.set!t(move(tmp));
214                         }
215 
216                         break FS;
217                 }
218             }
219         }
220         else static if (hasCustomRepr!(T, RH))
221         {
222             ReturnType!(trg.sbinCustomRepr) tmp;
223             impl(r, tmp, "customRepr");
224             // for @safe sbinDeserialize sbinFromCustomRepr must be @trusted or @safe
225             trg = T.sbinFromCustomRepr(tmp);
226         }
227         else static if (is(T == struct))
228         {
229             import std.traits : hasUDA;
230             foreach (i, ref v; trg.tupleof)
231                 static if (!hasUDA!(T.tupleof[i], sbinSkip))
232                     impl(r, v, __traits(identifier, trg.tupleof[i]));
233         }
234         else static if (is(T == union))
235         {
236             auto tmp = (() @trusted => cast(ubyte[])((cast(void*)&trg)[0..T.sizeof]))();
237             foreach (i, ref v; tmp)
238                 v = pop(r, "byte", i, T.stringof, i, T.sizeof);
239         }
240         else static assert(0, "unsupported type: " ~ T.stringof);
241     }
242 
243     size_t cnt;
244 
245     auto wr = (() @trusted => WrapRng(&range, &cnt, &fNameStack))();
246 
247     static if (Target.length == 1)
248         impl(wr, target[0], "root");
249     else foreach (i, ref v; target)
250         impl(wr, v, "root", i);
251 }
252 
253 /++ Deserialize part of input range to `Target` value
254 
255     Params:
256         range = input range with serialized data (not saved before work)
257 
258     Returns:
259         deserialized value
260  +/
261 Target sbinDeserializePart(RH=EmptyReprHandler, Target, R)(ref R range)
262     if (isReprHandler!RH)
263 {
264     Unqual!Target ret;
265     sbinDeserializePart!RH(range, ret);
266     return ret;
267 }
268 
269 /// ditto
270 Target sbinDeserializePart(Target, R)(ref R range)
271 {
272     Unqual!Target ret;
273     sbinDeserializePart!EmptyReprHandler(range, ret);
274     return ret;
275 }
276 
277 /++ Deserialize `Target` value
278 
279     Params:
280         range = input range with serialized data (not saved before work)
281 
282     Returns:
283         deserialized value
284  +/
285 Target sbinDeserialize(RH=EmptyReprHandler, Target, R)(R range)
286     if (isReprHandler!RH)
287 {
288     Unqual!Target ret;
289     sbinDeserialize!RH(range, ret);
290     return ret;
291 }
292 
293 /// ditto
294 Target sbinDeserialize(Target, R)(R range)
295 {
296     Unqual!Target ret;
297     sbinDeserialize!EmptyReprHandler(range, ret);
298     return ret;
299 }
300 
301 /++ Deserialize `Target` value
302 
303     Params:
304         range = input range with serialized data (not saved before work)
305         target = reference to result object
306 
307     Throws:
308         SBinDeserializeException if range isn't empty after deseriazlie
309  +/
310 void sbinDeserialize(RH=EmptyReprHandler, R, Target...)(R range, ref Target target)
311     if (isReprHandler!RH)
312 {
313     sbinDeserializePart!RH(range, target);
314 
315     enforce(range.empty, new SBinDeserializeException(
316         format("input range not empty after full '%s' deserialize", Target.stringof)));
317 }