1 module sbin.ut;
2 
3 import std.array : appender;
4 
5 import sbin.type;
6 import sbin.exception;
7 import sbin.serialize;
8 import sbin.deserialize;
9 
10 version (unittest) import std.algorithm : equal;
11 
12 @safe unittest
13 {
14     const a = 123;
15     assert(a.sbinSerialize.sbinDeserialize!int == a);
16 }
17 
18 @safe unittest
19 {
20     enum V { v }
21     const a = V.v;
22     static assert(is(V == enum));
23     static assert(is(EnumNumType!V == ubyte));
24     const s = a.sbinSerialize;
25     assert (s.length == 1);
26     assert(s.sbinDeserialize!V == a);
27 }
28 
29 @safe unittest
30 {
31     const a = 123;
32     auto as = a.sbinSerialize;
33     int x;
34     sbinDeserialize(as, x);
35     assert(a == x);
36 }
37 
38 @safe unittest
39 {
40     auto s = "hello world";
41     assert(equal(s.sbinSerialize.sbinDeserialize!string, s));
42 }
43 
44 @safe unittest
45 {
46     immutable(int[]) a = [1,2,3,2,3,2,1];
47     assert(a.sbinSerialize.sbinDeserialize!(int[]) == a);
48 }
49 
50 @safe unittest
51 {
52     const int[5] a = [1,2,3,2,3];
53     assert(a.sbinSerialize.sbinDeserialize!(typeof(a)) == a);
54 }
55 
56 @safe unittest
57 {
58     enum Color
59     {
60         black = "#000000",
61         red = "#ff0000",
62         green = "#00ff00",
63         blue = "#0000ff",
64         white = "#ffffff"
65     }
66 
67     enum Level { low, medium, high }
68 
69     struct Foo
70     {
71         ulong a;
72         float b, c;
73         ushort d;
74         string str;
75         Color color;
76         @sbinSkip int local = 42;
77     }
78     
79     const foo1 = Foo(10, 3.14, 2.17, 8, "s1", Color.red);
80 
81     //                  a              b         c       d
82     const foo1Size = ulong.sizeof + float.sizeof * 2 + ushort.sizeof +
83     //                            str                      color
84             (1 + foo1.str.length) + ubyte.sizeof; // 1 is length data because length < 127 (vluint pack)
85 
86     // color is ubyte because [EnumMembers!Color].length < ubyte.max
87 
88     const foo1Data = foo1.sbinSerialize;
89 
90     assert(foo1Data.length == foo1Size);
91     assert(foo1Data.sbinDeserialize!Foo == foo1);
92     
93     const foo2 = Foo(2, 2.22, 2.22, 2, "str2", Color.green);
94 
95     const foo2Size = ulong.sizeof + float.sizeof * 2 + ushort.sizeof +
96             (1 + foo2.str.length) + ubyte.sizeof;
97 
98     const foo2Data = foo2.sbinSerialize;
99 
100     assert(foo2Data.length == foo2Size);
101     assert(foo2Data.sbinDeserialize!Foo == foo2);
102 
103     struct Bar
104     {
105         ulong a;
106         float b;
107         Level level;
108         Foo[] foos;
109     }
110 
111     auto bar = Bar(123, 3.14, Level.high, [ foo1, foo2 ]);
112     
113     //                   a               b          level
114     const barSize = ulong.sizeof + float.sizeof + ubyte.sizeof +
115     //                                 foos
116                     (1 + foo1Size + foo2Size);
117     
118     assert(bar.sbinSerialize.length == barSize);
119 
120     auto data = [
121         bar,
122         Bar(23,
123             31.4, Level.high,
124             [
125                 Foo(10, .11, .22, 50, "1one1"),
126                 Foo(20, .13, .25, 70, "2two2", Color.black),
127                 Foo(30, .15, .28, 30, "3three3", Color.white),
128             ]
129         ),
130     ];
131 
132     auto sdata = data.sbinSerialize;
133     assert( equal(sdata.sbinDeserialize!(Bar[]), data));
134     data[0].foos[1].d = 12_345;
135     assert(!equal(sdata.sbinDeserialize!(Bar[]), data));
136 }
137 
138 @safe unittest
139 {
140     struct S
141     {
142         @sbinSkip int* p;
143     }
144 
145     auto s1 = S(new int(42));
146 
147     auto data = s1.sbinSerialize;
148     assert(data.length == 0);
149 
150     auto s2 = data.sbinDeserialize!S;
151     assert(s2.p == null);
152 }
153 
154 @safe unittest
155 {
156     static void foo(int a=123, string b="hello")
157     { assert(a==123); assert(b=="hello"); }
158 
159     auto a = ParameterDefaults!foo;
160 
161     import std.typecons : tuple;
162     const sa = tuple(a).sbinSerialize;
163 
164     Parameters!foo b;
165     b = sa.sbinDeserialize!(typeof(tuple(b)));
166     assert(a == b);
167     foo(b);
168 
169     a[0] = 234;
170     a[1] = "okda";
171     auto sn = tuple(a).sbinSerialize;
172 
173     sn.sbinDeserialize(b);
174 
175     assert(b[0] == 234);
176     assert(b[1] == "okda");
177 }
178 
179 @safe unittest
180 {
181     auto a = [1,2,3,4];
182     auto as = a.sbinSerialize;
183     auto as_tr = as[0..$-3];
184     assertThrown!SBinDeserializeEmptyRangeException(as_tr.sbinDeserialize!(typeof(a)));
185 }
186 
187 @safe unittest
188 {
189     auto a = [1,2,3,4];
190     auto as = a.sbinSerialize;
191     auto as_tr = as ~ as;
192     assertThrown!SBinDeserializeException(as_tr.sbinDeserialize!(typeof(a)));
193 }
194 
195 @safe unittest
196 {
197     auto a = ["hello" : 123, "ok" : 43];
198     auto as = a.sbinSerialize;
199 
200     auto b = as.sbinDeserialize!(typeof(a));
201     assert(b["hello"] == 123);
202     assert(b["ok"] == 43);
203 }
204 
205 unittest
206 {
207     static struct X
208     {
209         string[int] one;
210         int[string] two;
211     }
212 
213     auto a = X([3: "hello", 8: "abc"], ["ok": 1, "no": 2]);
214     auto b = X([8: "abc", 15: "ololo"], ["zb": 10]);
215 
216     const as = a.sbinSerialize;
217     const bs = b.sbinSerialize;
218 
219     auto c = as.sbinDeserialize!X;
220 
221     import std.algorithm : sort;
222     assert(equal(sort(a.one.keys.dup), sort(c.one.keys.dup)));
223     assert(equal(sort(a.one.values.dup), sort(c.one.values.dup)));
224 
225     bs.sbinDeserialize(c);
226 
227     assert(equal(sort(b.one.keys.dup), sort(c.one.keys.dup)));
228     assert(equal(sort(b.one.values.dup), sort(c.one.values.dup)));
229 }
230 
231 @safe unittest
232 {
233     enum T { one, two, three }
234     T[] a;
235     with(T) a = [one, two, three, two, three, two, one];
236     const as = a.sbinSerialize;
237 
238     auto b = as.sbinDeserialize!(typeof(a));
239     assert(equal(a, b));
240 }
241 
242 @safe unittest
243 {
244     enum T { one="one", two="2", three="III" }
245     T[] a;
246     with(T) a = [one, two, three, two, three, two, one];
247     const as = a.sbinSerialize;
248 
249     assert(as.length == 7 + 1);
250 
251     auto b = as.sbinDeserialize!(typeof(a));
252     assert(equal(a, b));
253 }
254 
255 @safe unittest
256 {
257     const int ai = 543;
258     auto as = "hello";
259 
260     import std.typecons : tuple;
261     auto buf = sbinSerialize(tuple(ai, as));
262 
263     int bi;
264     string bs;
265     sbinDeserialize(buf, bi, bs);
266 
267     assert(ai == bi);
268     assert(bs == as);
269 }
270 
271 @safe unittest
272 {
273     const int ai = 543;
274     auto as = "hello";
275 
276     auto buf = appender!(ubyte[]);
277     sbinSerialize(buf, ai, as);
278 
279     int bi;
280     string bs;
281     sbinDeserialize(buf.data, bi, bs);
282 
283     assert(ai == bi);
284     assert(bs == as);
285 }
286 
287 @safe unittest
288 {
289     static struct ImplaceAppender(Arr)
290     {
291         Arr _data;
292         size_t cur;
293 
294         this(Arr arr) { _data = arr; }
295 
296         @safe @nogc pure nothrow
297         {
298             void put(E)(E e)
299                 if (is(typeof(_data[0] = e)))
300             {
301                 _data[cur] = e;
302                 cur++;
303             }
304 
305             bool filled() const @property
306             { return cur == data.length; }
307 
308             inout(Arr) data() inout { return _data[0..cur]; }
309 
310             void clear() { cur = 0; }
311         }
312     }
313 
314     alias Buffer = ImplaceAppender!(ubyte[]);
315 
316     static assert(isOutputRange!(Buffer, ubyte));
317 
318     enum State
319     {
320         one   = "ONE",
321         two   = "TWO",
322         three = "THREE",
323     }
324 
325     struct Cell
326     {
327         ulong id;
328         float volt, temp;
329         ushort soc, soh;
330         string strData;
331         State state;
332     }
333 
334     struct Line
335     {
336         ulong id;
337         float volt, curr;
338         Cell[] cells;
339     }
340 
341     auto lines = [
342         Line(123,
343             3.14, 2.17,
344             [
345                 Cell(1, 1.1, 2.2, 5, 8, "one", State.one),
346                 Cell(2, 1.3, 2.5, 7, 9, "two", State.two),
347                 Cell(3, 1.5, 2.8, 3, 7, "three", State.three),
348             ]
349         ),
350         Line(23,
351             31.4, 21.7,
352             [
353                 Cell(10, .11, .22, 50, 80, "1one1", State.two),
354                 Cell(20, .13, .25, 70, 90, "2two2", State.three),
355                 Cell(30, .15, .28, 30, 70, "3three3", State.one),
356             ]
357         ),
358     ];
359 
360     ubyte[300] bdata;
361     auto buffer = Buffer(bdata[]);
362 
363     () @nogc { buffer.sbinSerialize(lines); }();
364 
365     assert(equal(buffer.data.sbinDeserialize!(typeof(lines)), lines));
366 }
367 
368 @safe unittest
369 {
370     static bool ser, deser;
371     static struct Foo
372     {
373         ulong id;
374         ulong sbinCustomRepr() const @property
375         {
376             ser = true;
377             return id;
378         }
379         static Foo sbinFromCustomRepr(ulong v)
380         {
381             deser = true;
382             return Foo(v);
383         }
384     }
385 
386     auto foo = Foo(12);
387 
388     assert(foo.sbinSerialize.sbinDeserialize!Foo == foo);
389     assert(ser);
390     assert(deser);
391 }
392 
393 @safe unittest
394 {
395     static bool ser, deser;
396     static class Foo
397     {
398         ulong id;
399         this(ulong v) @safe { id = v; }
400         ulong sbinCustomRepr() const @safe
401         {
402             ser = true;
403             return id;
404         }
405         static Foo sbinFromCustomRepr()(auto ref const ulong v) @safe
406         {
407             deser = true;
408             return new Foo(v);
409         }
410     }
411 
412     auto foo = new Foo(12);
413 
414     assert(foo.sbinSerialize.sbinDeserialize!Foo.id == 12);
415     assert(ser);
416     assert(deser);
417 
418     Foo[] fooArr;
419     foreach (i; 0 .. 10)
420         fooArr ~= new Foo(i);
421 
422     import std.algorithm : map;
423 
424     auto fooArr2 = fooArr.sbinSerialize.sbinDeserialize!(Foo[]);
425     assert(equal(fooArr.map!"a.id", fooArr2.map!"a.id"));
426 }
427 
428 @safe unittest
429 {
430     // for classes need
431     // T sbinCustomRepr() const
432     // static Foo sbinCustomDeserialize(T repr)
433     static class Foo
434     {
435         ulong id;
436         this(ulong v) { id = v; }
437     }
438 
439     auto foo = new Foo(12);
440 
441     static assert(!is(typeof(foo.sbinSerialize)));
442     static assert(!is(typeof(foo.sbinSerialize.sbinDeserialize!Foo.id)));
443 }
444 
445 @safe unittest
446 {
447     import std.bitmanip : bitfields;
448 
449     static struct Foo
450     {
451         mixin(bitfields!(
452             bool, "a", 1,
453             bool, "b", 1,
454             ubyte, "c", 4,
455             ubyte, "d", 2
456         ));
457     }
458 
459     static assert(Foo.sizeof == 1);
460 
461     Foo foo;
462     foo.a = true;
463     foo.b = false;
464     foo.c = 9;
465     foo.d = 3;
466 
467     assert(foo.a);
468     assert(foo.b == false);
469     assert(foo.c == 9);
470     assert(foo.d == 3);
471 
472     auto sfoo = foo.sbinSerialize;
473 
474     assert(sfoo.length == 1);
475 
476     auto bar = sfoo.sbinDeserialize!Foo;
477 
478     assert(bar.a);
479     assert(bar.b == false);
480     assert(bar.c == 9);
481     assert(bar.d == 3);
482 
483     assert(foo == bar);
484 }
485 
486 @safe unittest
487 {
488     struct Foo
489     {
490         ubyte[] a, b;
491     }
492 
493     auto arr = cast(ubyte[])[1,2,3,4,5,6];
494     const foo = Foo(arr, arr[0..2]);
495     assert (foo.a.ptr == foo.b.ptr);
496     
497     const foo2 = foo.sbinSerialize.sbinDeserialize!Foo;
498     assert(foo == foo2);
499     assert(foo.a.ptr != foo2.a.ptr);
500     assert(foo.b.ptr != foo2.b.ptr);
501     assert(foo2.a.ptr != foo2.b.ptr);
502 }
503 
504 unittest
505 {
506     struct Foo { void[] a; }
507     auto foo = Foo("hello".dup);
508     auto foo2 = foo.sbinSerialize.sbinDeserialize!Foo;
509     assert(equal(cast(ubyte[])foo.a, cast(ubyte[])foo2.a));
510 }
511 
512 unittest
513 {
514     struct Foo { void[5] a; }
515     auto foo = Foo(cast(void[5])"hello");
516     auto foo2 = foo.sbinSerialize.sbinDeserialize!Foo;
517     assert(equal(cast(ubyte[])foo.a, cast(ubyte[])foo2.a));
518 }
519 
520 unittest
521 {
522     struct Foo { void[] a; void[5] b; }
523     auto foo = Foo("hello".dup, cast(void[5])"world");
524     auto foo2 = foo.sbinSerialize.sbinDeserialize!Foo;
525     assert(equal(cast(ubyte[])foo.a, cast(ubyte[])foo2.a));
526     assert(equal(cast(ubyte[])foo.b, cast(ubyte[])foo2.b));
527 }
528 
529 unittest
530 {
531     import std.variant : Algebraic;
532 
533     struct Foo
534     {
535         Algebraic!(int, float, string) data;
536         this(int a) { data = a; }
537         this(float a) { data = a; }
538         this(string a) { data = a; }
539     }
540 
541     auto foo = Foo(12);
542     static assert(!__traits(compiles, foo.sbinSerialize.sbinDeserialize!Foo));
543 }
544 
545 @safe unittest
546 {
547     import std.algorithm : max;
548 
549     union Union
550     {
551         float fval;
552         byte ival;
553     }
554 
555     static assert(Union.init.sizeof == max(float.sizeof, byte.sizeof));
556 
557     Union u;
558     u.ival = 114;
559     assert (u.ival == 114);
560 
561     const su = u.sbinSerialize.sbinDeserialize!Union;
562     assert (su.ival == 114);
563 }
564 
565 unittest
566 {
567     auto buf = appender!(ubyte[]);
568 
569     struct Foo1 { void[] a; void[5] b; }
570     auto foo1 = Foo1("hello".dup, cast(void[5])"world");
571 
572     sbinSerialize(buf, foo1);
573 
574     static struct Foo2
575     {
576         import std.bitmanip : bitfields;
577         mixin(bitfields!(
578             bool, "a", 1,
579             bool, "b", 1,
580             ubyte, "c", 4,
581             ubyte, "d", 2
582         ));
583     }
584 
585     Foo2 foo2;
586     foo2.a = true;
587     foo2.b = false;
588     foo2.c = 9;
589     foo2.d = 3;
590 
591     sbinSerialize(buf, foo2);
592 
593     auto data = buf.data;
594 
595     auto dsfoo1 = sbinDeserializePart!Foo1(data);
596     const dsfoo2 = sbinDeserializePart!Foo2(data);
597 
598     assert (data.empty);
599 
600     assert(equal(cast(ubyte[])foo1.a, cast(ubyte[])dsfoo1.a));
601     assert(equal(cast(ubyte[])foo1.b, cast(ubyte[])dsfoo1.b));
602     assert(dsfoo2.a);
603     assert(dsfoo2.b == false);
604     assert(dsfoo2.c == 9);
605     assert(dsfoo2.d == 3);
606 }
607 
608 @safe unittest
609 {
610     enum Label { good, bad }
611     static struct Pos { int x, y; }
612     static struct Point { Pos position; Label label; }
613 
614     Point[2] val = [
615         Point(Pos(3,7), Label.good),
616         Point(Pos(9,5), Label.bad),
617     ];
618     auto data = sbinSerialize(val);
619 
620     assert(data.length == 18);
621 
622     import std.algorithm : canFind;
623 
624     {
625         bool throws;
626         try auto dsv = sbinDeserialize!(Point[2])(data[0..$-3]);
627         catch (SBinDeserializeEmptyRangeException e)
628         {
629             throws = true;
630             assert (e.msg.canFind("root.elem[1].position.y.byte[2]:int 2/4"), e.msg);
631         }
632         assert (throws);
633     }
634     {
635         bool throws;
636         try const dsv = sbinDeserialize!(Point[2])(data[0..$-1]);
637         catch (SBinDeserializeEmptyRangeException e)
638         {
639             throws = true;
640             assert (e.msg.canFind("root.elem[1].label.byte[0]:Label 0/1"), e.msg);
641         }
642         assert (throws);
643     }
644     {
645         bool throws;
646         try const dsv = sbinDeserialize!(Point[2])(data[0..$/2+1]);
647         catch (SBinDeserializeEmptyRangeException e)
648         {
649             throws = true;
650             assert (e.msg.canFind("root.elem[1].position.x.byte[1]:int 1/4"), e.msg);
651         }
652         assert (throws);
653     }
654     {
655         bool throws;
656         try const dsv = sbinDeserialize!(Point[2])(data[0..$/2-1]);
657         catch (SBinDeserializeEmptyRangeException e)
658         {
659             throws = true;
660             assert (e.msg.canFind("root.elem[0].label.byte[0]:Label 0/1"), e.msg);
661         }
662         assert (throws);
663     }
664 }
665 
666 @safe unittest
667 {
668     ushort[] val = [10,12,14,15];
669 
670     auto data = sbinSerialize(val);
671 
672     assert(data.length == 9);
673 
674     import std.algorithm : canFind;
675 
676     {
677         bool throws;
678         try const dsv = sbinDeserialize!(ushort[])(data[0..$-3]);
679         catch (SBinDeserializeEmptyRangeException e)
680         {
681             throws = true;
682             assert (e.msg.canFind("root.elem[2].byte[1]:ushort 1/2"), e.msg);
683         }
684         assert (throws);
685     }
686 
687     {
688         bool throws;
689         try const dsv = sbinDeserialize!(ushort[])(data[0..0]);
690         catch (SBinDeserializeEmptyRangeException e)
691         {
692             throws = true;
693             assert (e.msg.canFind("root.length:vluint 0/10"), e.msg);
694         }
695         assert (throws);
696     }
697 }
698 
699 @safe unittest
700 {
701     short[] value;
702     auto rng = sbinSerialize(value);
703     assert (rng.length == 1);
704     assert (rng[0] == 0);
705 
706     assert (sbinDeserialize!(short[])(rng).length == 0);
707 }
708 
709 @safe unittest
710 {
711     vlint[] value = [vlint(1),vlint(2),vlint(3)];
712     auto rng = sbinSerialize(value);
713     assert (rng.length == 4);
714     assert (rng[0] == 3);
715     assert (rng[1] == 2);
716     assert (rng[2] == 4);
717     assert (rng[3] == 6);
718 
719     assert (value ==  sbinDeserialize!(typeof(value))(rng));
720 }
721 
722 @safe unittest
723 {
724     vlint[] value = [vlint(1),vlint(-130),vlint(3)];
725     auto rng = sbinSerialize(value);
726     assert (rng.length == 5);
727     assert (rng[0] == 3);
728     assert (rng[1] == 2);
729     //assert (rng[2] == 4);
730     assert (rng[4] == 6);
731 
732     assert (value ==  sbinDeserialize!(typeof(value))(rng));
733 }
734 
735 @safe unittest
736 {
737     vluint[] value = [vluint(1),vluint(2),vluint(3)];
738     auto rng = sbinSerialize(value);
739     assert (rng.length == 4);
740     assert (rng[0] == 3);
741     assert (rng[1] == 1);
742     assert (rng[2] == 2);
743     assert (rng[3] == 3);
744 
745     assert (value == sbinDeserialize!(typeof(value))(rng));
746 }
747 
748 unittest
749 {
750     import std.bitmanip;
751     import std.random : uniform;
752     import std.range : iota;
753     import std.algorithm : map;
754     import std.array : array;
755 
756     BitArray b = iota(273).map!(a=>cast(bool)uniform!"[]"(0,1)).array;
757 
758     static struct BitArrayWrap
759     {
760         BitArray arr;
761         this(BitArray a) { arr = a; }
762         this(Repr r) { arr = BitArray(r.data, r.bitcount); }
763 
764         static struct Repr
765         {
766             vluint bitcount;
767             void[] data;
768         }
769 
770         Repr sbinCustomRepr() @property const
771         { return Repr(vluint(arr.length), cast(void[])arr.dup); }
772 
773         static BitArrayWrap sbinFromCustomRepr(Repr r)
774         { return BitArrayWrap(r); }
775     }
776 
777     const t1 = sbinDeserialize!BitArrayWrap(sbinSerialize(BitArrayWrap(b))).arr;
778 
779     assert (b == t1);
780 
781     static struct BitArrayWrap2
782     {
783         vluint bitcount;
784         void[] data;
785 
786         this(BitArray ba) { bitcount = ba.length; data = cast(void[])ba; }
787     }
788 
789     const pft2 = sbinDeserialize!BitArrayWrap2(sbinSerialize(BitArrayWrap2(b)));
790 
791     const t2 = BitArray(pft2.data.dup, pft2.bitcount);
792 
793     assert (b == t2);
794 
795     import std.datetime;
796 
797     static struct RH
798     {
799         enum sbinReprHandler;
800     static:
801         static struct BAW3 { vluint bc; void[] data; }
802         BAW3 repr(const BitArray ba) { return BAW3(vluint(ba.length), cast(void[])ba.dup); }
803         BitArray fromRepr(BAW3 w) { return BitArray(w.data.dup, w.bc); }
804 
805         long repr(const SysTime t) { return t.toUTC.stdTime; }
806         SysTime fromRepr(long r) { return SysTime(r, UTC()); }
807     }
808 
809     auto t3 = sbinDeserialize!(RH, BitArray)(sbinSerialize!RH(b));
810 
811     assert (b == t3);
812 
813     struct Foo
814     {
815         string name;
816         BitArray data;
817         SysTime tm;
818     }
819 
820     auto foo = Foo("hello", b, Clock.currTime);
821 
822     const foo_bytes = sbinSerialize!RH(foo);
823 
824     auto foo2 = sbinDeserialize!(RH, Foo)(foo_bytes);
825 
826     assert (foo.name == foo2.name);
827     assert (foo.data == foo2.data);
828     assert (foo.tm == foo2.tm);
829     assert (foo == foo2);
830 }