1 ///
2 module sbin.serialize;
3 
4 import std.array : appender;
5 
6 import sbin.type;
7 import sbin.vluint;
8 import sbin.zigzag;
9 import sbin.repr;
10 
11 /++ Serialize to output ubyte range
12 
13     Params:
14         val - serializible value
15         r - output range
16 +/
17 void sbinSerialize(RH=EmptyReprHandler, R, Ts...)(auto ref R r, auto ref const Ts vals)
18     if (isOutputRange!(R, ubyte) && Ts.length && isReprHandler!RH)
19 {
20     static if (Ts.length == 1)
21     {
22         alias T = Unqual!(Ts[0]);
23         alias val = vals[0];
24 
25         static if (hasRepr!(RH, T))
26         {
27             sbinSerialize!RH(r, RH.repr(val));
28         }
29         else static if (is(T == enum))
30         {
31             put(r, getEnumNum(val).pack[]);
32         }
33         else static if (is(T == vluint))
34         {
35             dumpVLUInt(r, val.value);
36         }
37         else static if (is(T == vlint))
38         {
39             dumpVLUInt(r, zzEncode(val.value));
40         }
41         else static if (is(T : double) || is(T : long))
42         {
43             put(r, val.pack[]);
44         }
45         else static if (isVoidArray!T)
46         {
47             static if (isDynamicArray!T)
48                 dumpVLUInt(r, val.length);
49             put(r, (() @trusted => cast(ubyte[])val[])());
50         }
51         else static if (isStaticArray!T)
52         {
53             foreach (ref v; val)
54                 sbinSerialize!RH(r, v);
55         }
56         else static if (isSomeString!T)
57         {
58             dumpVLUInt(r, val.length);
59             put(r, (() @trusted => cast(ubyte[])val)());
60         }
61         else static if (isDynamicArray!T)
62         {
63             dumpVLUInt(r, val.length);
64             foreach (ref v; val)
65                 sbinSerialize!RH(r, v);
66         }
67         else static if (isAssociativeArray!T)
68         {
69             dumpVLUInt(r, val.length);
70             foreach (k, ref v; val)
71             {
72                 sbinSerialize!RH(r, k);
73                 sbinSerialize!RH(r, v);
74             }
75         }
76         else static if (isTagged!(T).any)
77         {
78             sbinSerialize!RH(r, getTaggedTag(val));
79             val.taggedMatch!(
80                 (v) {
81                     static if (!is(Unqual!(typeof(v)) == typeof(null)))
82                         sbinSerialize!RH(r, v);
83                 }
84             );
85         }
86         else static if (hasCustomRepr!(T, RH))
87         {
88             // for @safe sbinSerialize sbinCustomRepr must be @trusted or @safe
89             sbinSerialize!RH(r, val.sbinCustomRepr());
90         }
91         else static if (is(T == struct))
92         {
93             import std.traits : hasUDA;
94             foreach (i, ref v; val.tupleof)
95                 static if (!hasUDA!(T.tupleof[i], sbinSkip))
96                     sbinSerialize!RH(r, v);
97         }
98         else static if (is(T == union))
99         {
100             sbinSerialize!RH(r, (() @trusted => cast(void[T.sizeof])((cast(void*)&val)[0..T.sizeof]))());
101         }
102         else static assert(0, "unsupported type: " ~ T.stringof);
103     }
104     else foreach (ref v; vals) sbinSerialize!RH(r, v);
105 }
106 
107 /++ Serialize to ubyte[]
108 
109     using `appender!(ubyte[])` as output range
110 
111     Params:
112         val = serializible value
113 
114     Returns:
115         serialized data
116 +/
117 ubyte[] sbinSerialize(RH=EmptyReprHandler, T)(auto ref const T val) if (isReprHandler!RH)
118 {
119     auto buf = appender!(ubyte[]);
120     sbinSerialize!RH(buf, val);
121     return buf.data;
122 }