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, string file=__FILE__, size_t line=__LINE__, Ts...)
18     (auto ref R r, auto ref const Ts vals) if (isOutputRange!(R, ubyte) && Ts.length && isReprHandler!RH)
19 {
20     void impl(T)(auto ref R r, auto ref const T val)
21     {
22         static if (hasRepr!(RH, T))
23         {
24             impl(r, RH.repr(val));
25         }
26         else static if (is(T == enum))
27         {
28             put(r, getEnumNum(val).pack[]);
29         }
30         else static if (is(T == vluint))
31         {
32             dumpVLUInt(r, val.value);
33         }
34         else static if (is(T == vlint))
35         {
36             dumpVLUInt(r, zzEncode(val.value));
37         }
38         else static if (is(T : double) || is(T : long))
39         {
40             put(r, val.pack[]);
41         }
42         else static if (isVoidArray!T)
43         {
44             static if (isDynamicArray!T)
45                 dumpVLUInt(r, val.length);
46             put(r, (() @trusted => cast(ubyte[])val[])());
47         }
48         else static if (isStaticArray!T)
49         {
50             foreach (ref v; val)
51                 impl(r, v);
52         }
53         else static if (isSomeString!T)
54         {
55             dumpVLUInt(r, val.length);
56             put(r, (() @trusted => cast(ubyte[])val)());
57         }
58         else static if (isDynamicArray!T)
59         {
60             dumpVLUInt(r, val.length);
61             foreach (ref v; val)
62                 impl(r, v);
63         }
64         else static if (isAssociativeArray!T)
65         {
66             dumpVLUInt(r, val.length);
67             foreach (k, ref v; val)
68             {
69                 impl(r, k);
70                 impl(r, v);
71             }
72         }
73         else static if (isTagged!(T).any)
74         {
75             impl(r, getTaggedTag(val));
76             val.taggedMatch!(
77                 (v) {
78                     static if (!is(Unqual!(typeof(v)) == typeof(null)))
79                         impl(r, v);
80                 }
81             );
82         }
83         else static if (hasCustomRepr!(T, RH))
84         {
85             // for @safe sbinSerialize sbinCustomRepr must be @trusted or @safe
86             impl(r, val.sbinCustomRepr());
87         }
88         else static if (is(T == struct))
89         {
90             version (allowRawUnions)
91             {
92                 import std : Nullable;
93                 static if (is(T == Nullable!A, A))
94                     pragma(msg, file, "(", cast(int)line, "): ", "\033[33mWarning:\033[0m ",
95                         T, " serialize as union, use NullableAsSumTypeRH for proper serialize!");
96             }
97 
98             import std.traits : hasUDA;
99             foreach (i, ref v; val.tupleof)
100                 static if (!hasUDA!(T.tupleof[i], sbinSkip))
101                     impl(r, v);
102         }
103         else static if (is(T == union))
104         {
105             version (allowRawUnions)
106                 impl(r, (() @trusted => cast(void[T.sizeof])((cast(void*)&val)[0..T.sizeof]))());
107             else
108                 static assert(0, "raw unions are not allowed, for allow build "~
109                                     "with configuration 'allow-raw-unions'");
110         }
111         else static assert(0, "unsupported type: " ~ T.stringof);
112     }
113 
114     static if (vals.length == 1) impl(r, vals[0]);
115     else foreach (ref v; vals) impl(r, v);
116 }
117 
118 /++ Serialize to ubyte[]
119 
120     using `appender!(ubyte[])` as output range
121 
122     Params:
123         val = serializible value
124 
125     Returns:
126         serialized data
127 +/
128 ubyte[] sbinSerialize(RH=EmptyReprHandler, T, string file=__FILE__, size_t line=__LINE__)
129     (auto ref const T val) if (isReprHandler!RH)
130 {
131     auto buf = appender!(ubyte[]);
132     sbinSerialize!(RH, typeof(buf), file, line)(buf, val);
133     return buf.data;
134 }