1 module drmi.sbin;
2 
3 import std.algorithm;
4 import std.array;
5 import std.bitmanip;
6 import std.traits;
7 import std.range;
8 
9 alias pack = nativeToLittleEndian;
10 alias unpack = littleEndianToNative;
11 
12 void sbinSerialize(T, R)(auto ref const T val, ref R r)
13     if (isOutputRange!(R, ubyte))
14 {
15     static if (is(T : double) || is(T : long))
16         r.put(val.pack[]);
17     else static if (isStaticArray!T)
18         foreach (v; val) sbinSerialize(v, r);
19     else static if (isSomeString!T)
20     {
21         r.put((cast(ulong)val.length).pack[]);
22         r.put(cast(ubyte[])val);
23     }
24     else static if (isDynamicArray!T)
25     {
26         r.put((cast(ulong)val.length).pack[]);
27         foreach (v; val) sbinSerialize(v, r);
28     }
29     else static if (is(T == struct) || isTypeTuple!T)
30         foreach (v; val.tupleof) sbinSerialize(v, r);
31     else
32         static assert(0, "unsupported type: " ~ T.stringof);
33 }
34 
35 ubyte[] sbinSerialize(T)(auto ref const T val)
36 {
37     auto buf = appender!(ubyte[]);
38     sbinSerialize(val, buf);
39     return buf.data.dup;
40 }
41 
42 Tb sbinDeserialize(Tb, Rb)(Rb r)
43 {
44     auto impl(T,R)(ref R r)
45     {
46         static if (is(T : double) || is(T : long))
47         {
48             ubyte[T.sizeof] tmp;
49             foreach (ref v; tmp)
50             {
51                 v = r.front;
52                 r.popFront();
53             }
54             return tmp.unpack!T;
55         }
56         else static if (isSomeString!T)
57         {
58             auto length = cast(size_t)impl!ulong(r);
59             auto tmp = new ubyte[](length);
60             foreach (ref v; tmp)
61             {
62                 v = r.front;
63                 r.popFront();
64             }
65             return cast(T)tmp;
66         }
67         else static if (isStaticArray!T)
68         {
69             T ret;
70             foreach (ref v; ret) v = impl!(typeof(ret[0]))(r);
71             return ret;
72         }
73         else static if (isDynamicArray!T)
74         {
75             T ret;
76             ret.length = cast(size_t)impl!ulong(r);
77             foreach (ref v; ret) v = impl!(typeof(ret[0]))(r);
78             return ret;
79         }
80         else static if (is(T == struct) || isTypeTuple!T)
81         {
82             T ret;
83             foreach (i, ref v; ret.tupleof)
84                 v = impl!(Unqual!(typeof(v)))(r);
85             return ret;
86         }
87         else
88             static assert(0, "unsupported type: " ~ T.stringof);
89     }
90     return impl!Tb(r);
91 }
92 
93 unittest
94 {
95     auto a = 123;
96     assert(a.sbinSerialize.sbinDeserialize!int == a);
97 }
98 
99 unittest
100 {
101     auto s = "hello world";
102     assert(equal(s.sbinSerialize.sbinDeserialize!string, s));
103 }
104 
105 unittest
106 {
107     immutable(int[]) a = [1,2,3,2,3,2,1];
108     assert(a.sbinSerialize.sbinDeserialize!(int[]) == a);
109 }
110 
111 unittest
112 {
113     import std.array;
114     auto ap = appender!(ubyte[]);
115 
116     struct Cell
117     {
118         ulong id;
119         float volt, temp;
120         ushort soc, soh;
121         string strData;
122         bool tr;
123     }
124 
125     struct Line
126     {
127         ulong id;
128         float volt, curr;
129         Cell[] cells;
130     }
131 
132     auto lines = [
133         Line(123,
134             3.14, 2.17,
135             [
136                 Cell(1, 1.1, 2.2, 5, 8, "one", true),
137                 Cell(2, 1.3, 2.5, 7, 9, "two"),
138                 Cell(3, 1.5, 2.8, 3, 7, "three"),
139             ]
140         ),
141         Line(23,
142             31.4, 21.7,
143             [
144                 Cell(10, .11, .22, 50, 80, "1one1"),
145                 Cell(20, .13, .25, 70, 90, "2two2", true),
146                 Cell(30, .15, .28, 30, 70, "3three3"),
147             ]
148         ),
149     ];
150 
151     auto sdata = lines.sbinSerialize;
152     assert( equal(sdata.sbinDeserialize!(Line[]), lines));
153     lines[0].cells[1].soh = 123;
154     assert(!equal(sdata.sbinDeserialize!(Line[]), lines));
155 }
156 
157 unittest
158 {
159     static void foo(int a=123, string b="hello")
160     {
161 
162     }
163     auto a = ParameterDefaults!foo;
164 
165     import std.typecons;
166     auto sa = tuple(a).sbinSerialize;
167 
168     Parameters!foo b;
169     b = sa.sbinDeserialize!(typeof(tuple(b)));
170     assert(a == b);
171 }