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 import sbin.stdtypesrh; 10 11 /* A note about `stable_format` arrays below. 12 13 The binary format of serializations has been stable since release v0.5.0. 14 The literal byte arrays in the unit tests are there to ensure that this 15 format remains stable across releases, and their values should not be 16 edited if at all possible. This is important where `sbin` is used for file 17 i/o. 18 19 If a change in the format is unavoidable, a deserialization method for the 20 previous format(s) will need to be implemented, and the release notes will 21 have to clearly state the incompatibility with instructions for how to deal 22 with it. One way would be for `sbinDeserialize` to accept an additional 23 template parameter containing a version string. 24 25 Semantic versioning (semver.org) applies to the binary compatibility of 26 APIs, but due to the scope of sbin we also keep the format stable across 27 at least minor and patch version increments. Meaning that if the format 28 changes then a major version bump is required. See also README.md. 29 */ 30 31 version (unittest) import std.algorithm : equal; 32 33 @safe unittest 34 { 35 const a = 123; 36 immutable ubyte[] stable_format = [123, 0, 0, 0]; 37 assert (a.sbinSerialize == stable_format); 38 assert (stable_format.sbinDeserialize!int == a); 39 } 40 41 @safe unittest 42 { 43 enum V { v } 44 const a = V.v; 45 static assert (is(V == enum)); 46 static assert (is(EnumNumType!V == ubyte)); 47 immutable ubyte[] stable_format = [0]; 48 assert (a.sbinSerialize == stable_format); 49 assert (stable_format.sbinDeserialize!V == a); 50 } 51 52 @safe unittest 53 { 54 const a = 123; 55 auto as = a.sbinSerialize; 56 assert (as == [123, 0, 0, 0]); 57 int x; 58 sbinDeserialize(as, x); 59 assert (a == x); 60 } 61 62 @safe unittest 63 { 64 auto s = "hello world"; 65 immutable ubyte[] stable_format = [11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]; 66 assert (s.sbinSerialize == stable_format); 67 assert (equal(stable_format.sbinDeserialize!string, s)); 68 } 69 70 @safe unittest 71 { 72 immutable(int[]) a = [1, 2, 3, 2, 3, 2, 1]; 73 immutable ubyte[] stable_format = [7, 1, 0, 0, 0, 74 2, 0, 0, 0, 75 3, 0, 0, 0, 76 2, 0, 0, 0, 77 3, 0, 0, 0, 78 2, 0, 0, 0, 79 1, 0, 0, 0]; 80 assert (a.sbinSerialize == stable_format); 81 assert (stable_format.sbinDeserialize!(int[]) == a); 82 } 83 84 @safe unittest 85 { 86 const int[5] a = [1, 2, 3, 2, 3]; 87 immutable ubyte[] stable_format = [1, 0, 0, 0, 88 2, 0, 0, 0, 89 3, 0, 0, 0, 90 2, 0, 0, 0, 91 3, 0, 0, 0]; 92 assert (a.sbinSerialize == stable_format); 93 assert (stable_format.sbinDeserialize!(typeof(a)) == a); 94 } 95 96 @safe unittest 97 { 98 enum Color 99 { 100 black = "#000000", 101 red = "#ff0000", 102 green = "#00ff00", 103 blue = "#0000ff", 104 white = "#ffffff" 105 } 106 107 enum Level { low, medium, high } 108 109 struct Foo 110 { 111 ulong a; 112 float b, c; 113 ushort d; 114 string str; 115 Color color; 116 @sbinSkip int local = 42; 117 } 118 119 const foo1 = Foo(10, 3.14, 2.17, 8, "s1", Color.red); 120 121 // a b c d 122 const foo1Size = ulong.sizeof + float.sizeof * 2 + ushort.sizeof + 123 // str color 124 (1 + foo1.str.length) + ubyte.sizeof; // 1 is length data because length < 127 (vluint pack) 125 126 // color is ubyte because [EnumMembers!Color].length < ubyte.max 127 128 const foo1Data = foo1.sbinSerialize; 129 130 assert (foo1Data.length == foo1Size); 131 assert (foo1Data == [10, 0, 0, 0, 0, 0, 0, 0, 195, 245, 72, 64, 72, 225, 10, 64, 8, 0, 2, 115, 49, 1]); 132 assert (foo1Data.sbinDeserialize!Foo == foo1); 133 134 const foo2 = Foo(2, 2.22, 2.22, 2, "str2", Color.green); 135 136 const foo2Size = ulong.sizeof + float.sizeof * 2 + ushort.sizeof + 137 (1 + foo2.str.length) + ubyte.sizeof; 138 139 const foo2Data = foo2.sbinSerialize; 140 141 assert (foo2Data.length == foo2Size); 142 assert (foo2Data == [2, 0, 0, 0, 0, 0, 0, 0, 123, 20, 14, 64, 123, 20, 14, 64, 2, 0, 4, 115, 116, 114, 50, 2]); 143 assert (foo2Data.sbinDeserialize!Foo == foo2); 144 145 struct Bar 146 { 147 ulong a; 148 float b; 149 Level level; 150 Foo[] foos; 151 } 152 153 auto bar = Bar(123, 3.14, Level.high, [ foo1, foo2 ]); 154 155 // a b level 156 const barSize = ulong.sizeof + float.sizeof + ubyte.sizeof + 157 // foos 158 (1 + foo1Size + foo2Size); 159 160 const barData = bar.sbinSerialize; 161 assert (barData.length == barSize); 162 assert (barData == [123, 0, 0, 0, 0, 0, 0, 0, 195, 245, 72, 64, 2, 2, 10, 0, 163 0, 0, 0, 0, 0, 0, 195, 245, 72, 64, 72, 225, 10, 64, 8, 164 0, 2, 115, 49, 1, 2, 0, 0, 0, 0, 0, 0, 0, 123, 20, 14, 165 64, 123, 20, 14, 64, 2, 0, 4, 115, 116, 114, 50, 2]); 166 167 auto data = [ 168 bar, 169 Bar(23, 170 31.4, Level.high, 171 [ 172 Foo(10, .11, .22, 50, "1one1"), 173 Foo(20, .13, .25, 70, "2two2", Color.black), 174 Foo(30, .15, .28, 30, "3three3", Color.white), 175 ] 176 ), 177 ]; 178 179 auto sdata = data.sbinSerialize; 180 assert (sdata == [2, 123, 0, 0, 0, 0, 0, 0, 0, 195, 245, 72, 64, 2, 2, 10, 181 0, 0, 0, 0, 0, 0, 0, 195, 245, 72, 64, 72, 225, 10, 64, 8, 182 0, 2, 115, 49, 1, 2, 0, 0, 0, 0, 0, 0, 0, 123, 20, 14, 64, 183 123, 20, 14, 64, 2, 0, 4, 115, 116, 114, 50, 2, 23, 0, 0, 184 0, 0, 0, 0, 0, 51, 51, 251, 65, 2, 3, 10, 0, 0, 0, 0, 0, 185 0, 0, 174, 71, 225, 61, 174, 71, 97, 62, 50, 0, 5, 49, 186 111, 110, 101, 49, 0, 20, 0, 0, 0, 0, 0, 0, 0, 184, 30, 5, 187 62, 0, 0, 128, 62, 70, 0, 5, 50, 116, 119, 111, 50, 0, 30, 188 0, 0, 0, 0, 0, 0, 0, 154, 153, 25, 62, 41, 92, 143, 62, 189 30, 0, 7, 51, 116, 104, 114, 101, 101, 51, 4]); 190 assert ( equal(sdata.sbinDeserialize!(Bar[]), data)); 191 data[0].foos[1].d = 12_345; 192 assert (!equal(sdata.sbinDeserialize!(Bar[]), data)); 193 } 194 195 @safe unittest 196 { 197 struct S 198 { 199 @sbinSkip int* p; 200 } 201 202 auto s1 = S(new int(42)); 203 204 auto data = s1.sbinSerialize; 205 assert(data.length == 0); 206 207 auto s2 = data.sbinDeserialize!S; 208 assert(s2.p == null); 209 } 210 211 @safe unittest 212 { 213 static void foo(int a=123, string b="hello") 214 { assert (a==123); assert (b=="hello"); } 215 216 auto a = ParameterDefaults!foo; 217 218 import std.typecons : tuple; 219 const sa = tuple(a).sbinSerialize; 220 assert (sa == [123, 0, 0, 0, 5, 104, 101, 108, 108, 111]); 221 222 Parameters!foo b; 223 b = sa.sbinDeserialize!(typeof(tuple(b))); 224 assert (a == b); 225 foo(b); 226 227 a[0] = 234; 228 a[1] = "okda"; 229 auto sn = tuple(a).sbinSerialize; 230 assert (sn == [234, 0, 0, 0, 4, 111, 107, 100, 97]); 231 232 sn.sbinDeserialize(b); 233 234 assert (b[0] == 234); 235 assert (b[1] == "okda"); 236 } 237 238 @safe unittest 239 { 240 auto a = [1,2,3,4]; 241 auto as = a.sbinSerialize; 242 assert (as == [4, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0]); 243 auto as_tr1 = as[0..$-3]; 244 assertThrown!SBinDeserializeEmptyRangeException(as_tr1.sbinDeserialize!(typeof(a))); 245 auto as_tr2 = as ~ as; 246 assertThrown!SBinDeserializeException(as_tr2.sbinDeserialize!(typeof(a))); 247 } 248 249 @safe unittest 250 { 251 auto a = ["hello" : 123, "ok" : 43]; 252 auto as = a.sbinSerialize; 253 assert (as == [2, 2, 111, 107, 43, 0, 0, 0, 5, 104, 101, 108, 108, 111, 123, 0, 0, 0]); 254 255 auto b = as.sbinDeserialize!(typeof(a)); 256 assert (b["hello"] == 123); 257 assert (b["ok"] == 43); 258 } 259 260 unittest 261 { 262 static struct X 263 { 264 string[int] one; 265 int[string] two; 266 } 267 268 auto a = X([3: "hello", 8: "abc"], ["ok": 1, "no": 2]); 269 auto b = X([8: "abc", 15: "ololo"], ["zb": 10]); 270 271 const as = a.sbinSerialize; 272 assert (as == [2, 3, 0, 0, 0, 5, 104, 101, 108, 108, 111, 8, 0, 0, 0, 3, 97, 273 98, 99, 2, 2, 111, 107, 1, 0, 0, 0, 2, 110, 111, 2, 0, 0, 0]); 274 const bs = b.sbinSerialize; 275 assert (bs == [2, 8, 0, 0, 0, 3, 97, 98, 99, 15, 0, 0, 0, 5, 111, 108, 111, 276 108, 111, 1, 2, 122, 98, 10, 0, 0, 0]); 277 278 auto c = as.sbinDeserialize!X; 279 280 import std.algorithm : sort; 281 assert (equal(sort(a.one.keys.dup), sort(c.one.keys.dup))); 282 assert (equal(sort(a.one.values.dup), sort(c.one.values.dup))); 283 284 bs.sbinDeserialize(c); 285 286 assert (equal(sort(b.one.keys.dup), sort(c.one.keys.dup))); 287 assert (equal(sort(b.one.values.dup), sort(c.one.values.dup))); 288 } 289 290 @safe unittest 291 { 292 enum T { one, two, three } 293 T[] a; 294 with(T) a = [one, two, three, two, three, two, one]; 295 const as = a.sbinSerialize; 296 assert (as == [7, 0, 1, 2, 1, 2, 1, 0]); 297 298 auto b = as.sbinDeserialize!(typeof(a)); 299 assert (equal(a, b)); 300 } 301 302 @safe unittest 303 { 304 enum T { one="one", two="2", three="III" } 305 T[] a; 306 with(T) a = [one, two, three, two, three, two, one]; 307 const as = a.sbinSerialize; 308 assert (as == [7, 0, 1, 2, 1, 2, 1, 0]); 309 310 assert (as.length == 7 + 1); 311 312 auto b = as.sbinDeserialize!(typeof(a)); 313 assert (equal(a, b)); 314 } 315 316 @safe unittest 317 { 318 const int ai = 543; 319 auto as = "hello"; 320 321 import std.typecons : tuple; 322 auto buf = sbinSerialize(tuple(ai, as)); 323 assert (buf == [31, 2, 0, 0, 5, 104, 101, 108, 108, 111]); 324 325 int bi; 326 string bs; 327 sbinDeserialize(buf, bi, bs); 328 329 assert (ai == bi); 330 assert (bs == as); 331 } 332 333 @safe unittest 334 { 335 const int ai = 543; 336 auto as = "hello"; 337 338 auto buf = appender!(ubyte[]); 339 sbinSerialize(buf, ai, as); 340 assert (buf.data == [31, 2, 0, 0, 5, 104, 101, 108, 108, 111]); 341 342 int bi; 343 string bs; 344 sbinDeserialize(buf.data, bi, bs); 345 346 assert (ai == bi); 347 assert (bs == as); 348 } 349 350 @safe unittest 351 { 352 static struct ImplaceAppender(Arr) 353 { 354 Arr _data; 355 size_t cur; 356 357 this(Arr arr) { _data = arr; } 358 359 @safe @nogc pure nothrow 360 { 361 void put(E)(E e) 362 if (is(typeof(_data[0] = e))) 363 { 364 _data[cur] = e; 365 cur++; 366 } 367 368 bool filled() const @property 369 { return cur == data.length; } 370 371 inout(Arr) data() inout { return _data[0..cur]; } 372 373 void clear() { cur = 0; } 374 } 375 } 376 377 alias Buffer = ImplaceAppender!(ubyte[]); 378 379 static assert (isOutputRange!(Buffer, ubyte)); 380 381 enum State 382 { 383 one = "ONE", 384 two = "TWO", 385 three = "THREE", 386 } 387 388 struct Cell 389 { 390 ulong id; 391 float volt, temp; 392 ushort soc, soh; 393 string strData; 394 State state; 395 } 396 397 struct Line 398 { 399 ulong id; 400 float volt, curr; 401 Cell[] cells; 402 } 403 404 auto lines = [ 405 Line(123, 406 3.14, 2.17, 407 [ 408 Cell(1, 1.1, 2.2, 5, 8, "one", State.one), 409 Cell(2, 1.3, 2.5, 7, 9, "two", State.two), 410 Cell(3, 1.5, 2.8, 3, 7, "three", State.three), 411 ] 412 ), 413 Line(23, 414 31.4, 21.7, 415 [ 416 Cell(10, .11, .22, 50, 80, "1one1", State.two), 417 Cell(20, .13, .25, 70, 90, "2two2", State.three), 418 Cell(30, .15, .28, 30, 70, "3three3", State.one), 419 ] 420 ), 421 ]; 422 423 ubyte[300] bdata; 424 auto buffer = Buffer(bdata[]); 425 426 () @nogc { buffer.sbinSerialize(lines); }(); 427 428 assert (buffer.data == [2, 123, 0, 0, 0, 0, 0, 0, 0, 195, 245, 72, 64, 72, 225, 10, 64, 3, 1, 0, 0, 0, 0, 0, 0, 0, 429 205, 204, 140, 63, 205, 204, 12, 64, 5, 0, 8, 0, 3, 111, 110, 101, 0, 2, 0, 0, 0, 0, 0, 0, 430 0, 102, 102, 166, 63, 0, 0, 32, 64, 7, 0, 9, 0, 3, 116, 119, 111, 1, 3, 0, 0, 0, 0, 0, 0, 0, 431 0, 0, 192, 63, 51, 51, 51, 64, 3, 0, 7, 0, 5, 116, 104, 114, 101, 101, 2, 23, 0, 0, 0, 0, 0, 432 0, 0, 51, 51, 251, 65, 154, 153, 173, 65, 3, 10, 0, 0, 0, 0, 0, 0, 0, 174, 71, 225, 61, 174, 433 71, 97, 62, 50, 0, 80, 0, 5, 49, 111, 110, 101, 49, 1, 20, 0, 0, 0, 0, 0, 0, 0, 184, 30, 5, 434 62, 0, 0, 128, 62, 70, 0, 90, 0, 5, 50, 116, 119, 111, 50, 2, 30, 0, 0, 0, 0, 0, 0, 0, 154, 435 153, 25, 62, 41, 92, 143, 62, 30, 0, 70, 0, 7, 51, 116, 104, 114, 101, 101, 51, 0]); 436 assert (equal(buffer.data.sbinDeserialize!(typeof(lines)), lines)); 437 } 438 439 @safe unittest 440 { 441 static bool ser, deser; 442 static struct Foo 443 { 444 ulong id; 445 ulong sbinCustomRepr() const @property 446 { 447 ser = true; 448 return id; 449 } 450 static Foo sbinFromCustomRepr(ulong v) 451 { 452 deser = true; 453 return Foo(v); 454 } 455 } 456 457 auto foo = Foo(12); 458 459 immutable ubyte[] stable_format = [12, 0, 0, 0, 0, 0, 0, 0]; 460 assert (foo.sbinSerialize == stable_format); 461 assert (stable_format.sbinDeserialize!Foo == foo); 462 463 assert (ser); 464 assert (deser); 465 } 466 467 @safe unittest 468 { 469 static bool ser, deser; 470 static class Foo 471 { 472 ulong id; 473 this(ulong v) @safe { id = v; } 474 ulong sbinCustomRepr() const @safe 475 { 476 ser = true; 477 return id; 478 } 479 static Foo sbinFromCustomRepr()(auto ref const ulong v) @safe 480 { 481 deser = true; 482 return new Foo(v); 483 } 484 } 485 486 auto foo = new Foo(12); 487 488 immutable ubyte[] stable_format = [12, 0, 0, 0, 0, 0, 0, 0]; 489 assert (foo.sbinSerialize == stable_format); 490 assert (stable_format.sbinDeserialize!Foo.id == 12); 491 assert (ser); 492 assert (deser); 493 494 Foo[] fooArr; 495 foreach (i; 0 .. 10) 496 fooArr ~= new Foo(i); 497 498 import std.algorithm : map; 499 500 immutable ubyte[] stable_format2 = [10, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 501 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 502 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 503 0, 0, 0]; 504 assert (fooArr.sbinSerialize == stable_format2); 505 auto fooArr2 = stable_format2.sbinDeserialize!(Foo[]); 506 assert (equal(fooArr.map!"a.id", fooArr2.map!"a.id")); 507 } 508 509 @safe unittest 510 { 511 // for classes need 512 // T sbinCustomRepr() const 513 // static Foo sbinCustomDeserialize(T repr) 514 static class Foo 515 { 516 ulong id; 517 this(ulong v) { id = v; } 518 } 519 520 auto foo = new Foo(12); 521 522 static assert (!is(typeof(foo.sbinSerialize))); 523 static assert (!is(typeof(foo.sbinSerialize.sbinDeserialize!Foo.id))); 524 } 525 526 @safe unittest 527 { 528 import std.bitmanip : bitfields; 529 530 static struct Foo 531 { 532 mixin(bitfields!( 533 bool, "a", 1, 534 bool, "b", 1, 535 ubyte, "c", 4, 536 ubyte, "d", 2 537 )); 538 } 539 540 static assert (Foo.sizeof == 1); 541 542 Foo foo; 543 foo.a = true; 544 foo.b = false; 545 foo.c = 9; 546 foo.d = 3; 547 548 assert (foo.a); 549 assert (foo.b == false); 550 assert (foo.c == 9); 551 assert (foo.d == 3); 552 553 auto sfoo = foo.sbinSerialize; 554 555 assert (sfoo.length == 1); 556 assert (sfoo == [229]); 557 558 auto bar = sfoo.sbinDeserialize!Foo; 559 560 assert (bar.a); 561 assert (bar.b == false); 562 assert (bar.c == 9); 563 assert (bar.d == 3); 564 565 assert (foo == bar); 566 } 567 568 @safe unittest 569 { 570 struct Foo 571 { 572 ubyte[] a, b; 573 } 574 575 auto arr = cast(ubyte[])[1,2,3,4,5,6]; 576 const foo = Foo(arr, arr[0..2]); 577 assert (foo.a.ptr == foo.b.ptr); 578 579 immutable ubyte[] stable_format = [6, 1, 2, 3, 4, 5, 6, 2, 1, 2]; 580 assert (foo.sbinSerialize == stable_format); 581 const foo2 = stable_format.sbinDeserialize!Foo; 582 assert (foo == foo2); 583 assert (foo.a.ptr != foo2.a.ptr); 584 assert (foo.b.ptr != foo2.b.ptr); 585 assert (foo2.a.ptr != foo2.b.ptr); 586 } 587 588 unittest 589 { 590 struct Foo { void[] a; } 591 auto foo = Foo("hello".dup); 592 immutable ubyte[] stable_format = [5, 104, 101, 108, 108, 111]; 593 assert (foo.sbinSerialize == stable_format); 594 auto foo2 = stable_format.sbinDeserialize!Foo; 595 assert (equal(cast(ubyte[])foo.a, cast(ubyte[])foo2.a)); 596 } 597 598 unittest 599 { 600 struct Foo { void[5] a; } 601 auto foo = Foo(cast(void[5])"hello"); 602 immutable ubyte[] stable_format = [104, 101, 108, 108, 111]; 603 assert (foo.sbinSerialize == stable_format); 604 auto foo2 = stable_format.sbinDeserialize!Foo; 605 assert (equal(cast(ubyte[])foo.a, cast(ubyte[])foo2.a)); 606 } 607 608 unittest 609 { 610 struct Foo { void[] a; void[5] b; } 611 auto foo = Foo("hello".dup, cast(void[5])"world"); 612 immutable ubyte[] stable_format = [5, 104, 101, 108, 108, 111, 119, 111, 114, 108, 100]; 613 assert (foo.sbinSerialize == stable_format); 614 auto foo2 = stable_format.sbinDeserialize!Foo; 615 assert (equal(cast(ubyte[])foo.a, cast(ubyte[])foo2.a)); 616 assert (equal(cast(ubyte[])foo.b, cast(ubyte[])foo2.b)); 617 } 618 619 unittest 620 { 621 import std.variant : Algebraic; 622 623 struct Foo 624 { 625 Algebraic!(int, float, string) data; 626 this(int a) { data = a; } 627 this(float a) { data = a; } 628 this(string a) { data = a; } 629 } 630 631 auto foo = Foo(12); 632 static assert (!__traits(compiles, foo.sbinSerialize.sbinDeserialize!Foo)); 633 } 634 635 version (allowRawUnions) 636 { 637 @safe unittest 638 { 639 import std.algorithm : max; 640 641 union Union 642 { 643 float fval; 644 byte ival; 645 } 646 647 static assert (Union.init.sizeof == max(float.sizeof, byte.sizeof)); 648 649 Union u; 650 u.ival = 114; 651 assert (u.ival == 114); 652 653 immutable ubyte[] stable_format = [114, 0, 192, 127]; 654 assert (u.sbinSerialize == stable_format); 655 const su = stable_format.sbinDeserialize!Union; 656 assert (su.ival == 114); 657 } 658 } 659 660 unittest 661 { 662 auto buf = appender!(ubyte[]); 663 664 struct Foo1 { void[] a; void[5] b; } 665 auto foo1 = Foo1("hello".dup, cast(void[5])"world"); 666 667 sbinSerialize(buf, foo1); 668 assert (buf.data == [5, 104, 101, 108, 108, 111, 119, 111, 114, 108, 100]); 669 670 static struct Foo2 671 { 672 import std.bitmanip : bitfields; 673 mixin(bitfields!( 674 bool, "a", 1, 675 bool, "b", 1, 676 ubyte, "c", 4, 677 ubyte, "d", 2 678 )); 679 } 680 681 Foo2 foo2; 682 foo2.a = true; 683 foo2.b = false; 684 foo2.c = 9; 685 foo2.d = 3; 686 687 sbinSerialize(buf, foo2); 688 689 auto data = buf.data; 690 assert (data == [5, 104, 101, 108, 108, 111, 119, 111, 114, 108, 100, 229]); 691 692 auto dsfoo1 = sbinDeserializePart!Foo1(data); 693 const dsfoo2 = sbinDeserializePart!Foo2(data); 694 695 assert (data.empty); 696 697 assert (equal(cast(ubyte[])foo1.a, cast(ubyte[])dsfoo1.a)); 698 assert (equal(cast(ubyte[])foo1.b, cast(ubyte[])dsfoo1.b)); 699 assert (dsfoo2.a); 700 assert (dsfoo2.b == false); 701 assert (dsfoo2.c == 9); 702 assert (dsfoo2.d == 3); 703 } 704 705 @safe unittest 706 { 707 enum Label { good, bad } 708 static struct Pos { int x, y; } 709 static struct Point { Pos position; Label label; } 710 711 Point[2] val = [ 712 Point(Pos(3,7), Label.good), 713 Point(Pos(9,5), Label.bad), 714 ]; 715 auto data = sbinSerialize(val); 716 assert (data == [3, 0, 0, 0, 7, 0, 0, 0, 0, 9, 0, 0, 0, 5, 0, 0, 0, 1]); 717 718 assert (data.length == 18); 719 720 import std.algorithm : canFind; 721 722 { 723 bool throws; 724 try auto dsv = sbinDeserialize!(Point[2])(data[0..$-3]); 725 catch (SBinDeserializeEmptyRangeException e) 726 { 727 throws = true; 728 assert (e.msg.canFind("root.elem[1].position.y.byte[2]:int 2/4"), e.msg); 729 } 730 assert (throws); 731 } 732 { 733 bool throws; 734 try const dsv = sbinDeserialize!(Point[2])(data[0..$-1]); 735 catch (SBinDeserializeEmptyRangeException e) 736 { 737 throws = true; 738 assert (e.msg.canFind("root.elem[1].label.byte[0]:Label 0/1"), e.msg); 739 } 740 assert (throws); 741 } 742 { 743 bool throws; 744 try const dsv = sbinDeserialize!(Point[2])(data[0..$/2+1]); 745 catch (SBinDeserializeEmptyRangeException e) 746 { 747 throws = true; 748 assert (e.msg.canFind("root.elem[1].position.x.byte[1]:int 1/4"), e.msg); 749 } 750 assert (throws); 751 } 752 { 753 bool throws; 754 try const dsv = sbinDeserialize!(Point[2])(data[0..$/2-1]); 755 catch (SBinDeserializeEmptyRangeException e) 756 { 757 throws = true; 758 assert (e.msg.canFind("root.elem[0].label.byte[0]:Label 0/1"), e.msg); 759 } 760 assert (throws); 761 } 762 } 763 764 @safe unittest 765 { 766 ushort[] val = [10,12,14,15]; 767 768 auto data = sbinSerialize(val); 769 assert (data == [4, 10, 0, 12, 0, 14, 0, 15, 0]); 770 771 assert (data.length == 9); 772 773 import std.algorithm : canFind; 774 775 { 776 bool throws; 777 try const dsv = sbinDeserialize!(ushort[])(data[0..$-3]); 778 catch (SBinDeserializeEmptyRangeException e) 779 { 780 throws = true; 781 assert (e.msg.canFind("root.elem[2].byte[1]:ushort 1/2"), e.msg); 782 } 783 assert (throws); 784 } 785 786 { 787 bool throws; 788 try const dsv = sbinDeserialize!(ushort[])(data[0..0]); 789 catch (SBinDeserializeEmptyRangeException e) 790 { 791 throws = true; 792 assert (e.msg.canFind("root.length:vluint 0/10"), e.msg); 793 } 794 assert (throws); 795 } 796 } 797 798 @safe unittest 799 { 800 short[] value; 801 auto rng = sbinSerialize(value); 802 assert (rng == [0]); 803 assert (rng.length == 1); 804 assert (rng[0] == 0); 805 806 assert (sbinDeserialize!(short[])(rng).length == 0); 807 } 808 809 @safe unittest 810 { 811 vlint[] value = [vlint(1),vlint(2),vlint(3)]; 812 auto rng = sbinSerialize(value); 813 assert (rng.length == 4); 814 assert (rng[0] == 3); 815 assert (rng[1] == 2); 816 assert (rng[2] == 4); 817 assert (rng[3] == 6); 818 819 assert (value == sbinDeserialize!(typeof(value))(rng)); 820 } 821 822 @safe unittest 823 { 824 vlint[] value = [vlint(1),vlint(-130),vlint(3)]; 825 auto rng = sbinSerialize(value); 826 assert (rng.length == 5); 827 assert (rng[0] == 3); 828 assert (rng[1] == 2); 829 //assert (rng[2] == 4); 830 assert (rng[4] == 6); 831 assert (rng == [3, 2, 131, 2, 6]); 832 833 assert (value == sbinDeserialize!(typeof(value))(rng)); 834 } 835 836 @safe unittest 837 { 838 vluint[] value = [vluint(1),vluint(2),vluint(3)]; 839 auto rng = sbinSerialize(value); 840 assert (rng.length == 4); 841 assert (rng[0] == 3); 842 assert (rng[1] == 1); 843 assert (rng[2] == 2); 844 assert (rng[3] == 3); 845 846 assert (value == sbinDeserialize!(typeof(value))(rng)); 847 } 848 849 unittest 850 { 851 import std.bitmanip; 852 import std.random : uniform; 853 import std.range : iota; 854 import std.algorithm : map; 855 import std.array : array; 856 857 // 273 random bits. 858 BitArray b = [0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 859 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 860 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 861 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 862 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 863 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 864 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 865 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 866 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 867 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 868 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1]; 869 870 static struct BitArrayWrap 871 { 872 BitArray arr; 873 this(BitArray a) { arr = a; } 874 this(Repr r) { arr = BitArray(r.data, r.bitcount); } 875 876 static struct Repr 877 { 878 vluint bitcount; 879 void[] data; 880 } 881 882 Repr sbinCustomRepr() @property const 883 { return Repr(vluint(arr.length), cast(void[])arr.dup); } 884 885 static BitArrayWrap sbinFromCustomRepr(Repr r) 886 { return BitArrayWrap(r); } 887 } 888 889 immutable ubyte[] stable_format = [145, 2, 40, 78, 215, 143, 77, 12, 42, 60, 164, 101, 132, 890 114, 188, 53, 58, 249, 88, 202, 201, 53, 159, 251, 57, 891 163, 202, 10, 33, 70, 43, 39, 187, 246, 155, 79, 207, 1, 892 0, 0, 0, 0, 0]; 893 assert (equal(sbinSerialize(BitArrayWrap(b)), stable_format)); 894 const t1 = sbinDeserialize!BitArrayWrap(stable_format).arr; 895 896 assert (b == t1); 897 898 static struct BitArrayWrap2 899 { 900 vluint bitcount; 901 void[] data; 902 903 this(BitArray ba) { bitcount = ba.length; data = cast(void[])ba; } 904 } 905 906 assert (equal(sbinSerialize(BitArrayWrap2(b)), stable_format)); 907 const pft2 = sbinDeserialize!BitArrayWrap2(stable_format); 908 909 const t2 = BitArray(pft2.data.dup, pft2.bitcount); 910 911 assert (b == t2); 912 913 import std.datetime; 914 915 alias RH = CombineReprHandler!(BitArrayRH, SysTimeAsHNSecsRH!true); 916 917 assert (sbinSerialize!RH(b) == stable_format); 918 auto t3 = sbinDeserialize!(RH, BitArray)(stable_format); 919 920 assert (b == t3); 921 922 struct Foo 923 { 924 string name; 925 BitArray data; 926 SysTime tm; 927 } 928 929 auto foo = Foo("hello", b, SysTime(DateTime(2018, 1, 1, 10, 30, 0), UTC())); 930 931 const foo_bytes = sbinSerialize!RH(foo); 932 assert (foo_bytes == [5, 104, 101, 108, 108, 111, 145, 2, 40, 78, 215, 143, 77, 12, 42, 933 60, 164, 101, 132, 114, 188, 53, 58, 249, 88, 202, 201, 53, 159, 934 251, 57, 163, 202, 10, 33, 70, 43, 39, 187, 246, 155, 79, 207, 1, 935 0, 0, 0, 0, 0, 0, 196, 124, 156, 2, 81, 213, 8]); 936 937 auto foo2 = sbinDeserialize!(RH, Foo)(foo_bytes); 938 939 assert (foo.name == foo2.name); 940 assert (foo.data == foo2.data); 941 assert (foo.tm == foo2.tm); 942 assert (foo == foo2); 943 } 944 945 static if (__VERSION__ >= 2097) 946 { 947 import std.sumtype; 948 949 alias UT1 = std.sumtype.SumType!(string, int); 950 951 //@safe // sumtype opAssign not safe 952 unittest 953 { 954 const a = "hello"; 955 956 auto val1 = UT1(a); 957 958 assert (val1.match!(v => is(typeof(v) == string))); 959 960 auto data = sbinSerialize(val1); 961 962 assert (data == [0, 5, 104, 101, 108, 108, 111]); 963 964 auto val2 = sbinDeserialize!UT1(data); 965 966 assert (val1 == val2); 967 assert (val2.match!(v => is(typeof(v) == string))); 968 } 969 970 unittest 971 { 972 const a = 42; 973 974 auto val1 = UT1(a); 975 976 assert (val1.match!(v => is(typeof(v) == int))); 977 978 auto data = sbinSerialize(val1); 979 980 assert (data == [1, 42, 0, 0, 0]); 981 982 auto val2 = sbinDeserialize!UT1(data); 983 984 assert (val1 == val2); 985 assert (val2.match!(v => is(typeof(v) == int))); 986 } 987 988 alias UT2 = std.sumtype.SumType!(typeof(null), byte, byte[3]); 989 990 unittest 991 { 992 auto val1 = [UT2(null), UT2(6), UT2([11,12,13])]; 993 auto data = sbinSerialize(val1); 994 assert (data == [3, 0, 1, 6, 2, 11, 12, 13]); 995 auto val2 = sbinDeserialize!(UT2[])(data); 996 assert (val1 == val2); 997 } 998 999 alias UT3 = std.sumtype.SumType!(typeof(null), byte, std.sumtype.This[]); 1000 1001 unittest 1002 { 1003 auto val1 = UT3([ 1004 UT3(42), 1005 UT3(null), 1006 UT3([ UT3(null), UT3(65) ]), 1007 UT3(12) 1008 ]); 1009 1010 auto data = sbinSerialize(val1); 1011 assert (data == [2, 4, 1, 42, 0, 2, 2, 0, 1, 65, 1, 12]); 1012 auto val2 = sbinDeserialize!UT3(data); 1013 assert (val1 == val2); 1014 } 1015 1016 import std : Nullable; 1017 1018 unittest 1019 { 1020 alias NB = Nullable!ubyte; 1021 1022 alias SNB = std.sumtype.SumType!(typeof(null), ubyte); 1023 1024 const val1 = [ NB.init, NB(11), NB(12), NB.init, NB(13) ]; 1025 1026 alias RH = NullableAsSumTypeRH; 1027 1028 assert (RH.repr(NB.init) == SNB(null)); 1029 assert (RH.repr(NB(12)) == SNB(12)); 1030 assert (RH.fromRepr(SNB(null)) == NB.init); 1031 assert (RH.fromRepr(SNB(21)) == NB(21)); 1032 1033 import sbin.repr; 1034 1035 static assert (hasSerializeRepr!(RH, NB)); 1036 static assert (hasDeserializeRepr!(RH, NB, SNB)); 1037 static assert (hasRepr!(RH, NB)); 1038 1039 const data = sbinSerialize!RH(val1); 1040 1041 assert (data == [5, 0, 1,11, 1,12, 0, 1,13]); 1042 1043 const val2 = sbinDeserialize!(RH, NB[])(data); 1044 1045 assert (val1 == val2); 1046 } 1047 1048 unittest 1049 { 1050 static struct F1 { ubyte value; } 1051 static struct F2 { ubyte value; } 1052 static struct Foo 1053 { 1054 static struct Bar(T) 1055 { 1056 string name; 1057 Nullable!T value; 1058 } 1059 1060 alias BF1 = Bar!F1; 1061 alias BF2 = Bar!F2; 1062 1063 BF1[] f1s; 1064 BF2[] f2s; 1065 } 1066 1067 alias RH = NullableAsSumTypeRH; 1068 1069 const val1 = Foo( 1070 [ 1071 Foo.BF1("ok", Nullable!F1(F1(12))), 1072 Foo.BF1("da", Nullable!F1(F1(32))), 1073 Foo.BF1("ne", Nullable!(F1).init), 1074 ], 1075 [ 1076 Foo.BF2("o", Nullable!F2(F2(5))), 1077 Foo.BF2("t", Nullable!(F2).init), 1078 Foo.BF2("o", Nullable!F2(F2(7))), 1079 ] 1080 ); 1081 1082 const data = sbinSerialize!RH(val1); 1083 //const data = sbinSerialize(val1); 1084 1085 assert (data == [ 1086 3, 1087 2, 111, 107, 1, 12, 1088 2, 100, 97, 1, 32, 1089 2, 110, 101, 0, 1090 3, 1091 1, 111, 1, 5, 1092 1, 116, 0, 1093 1, 111, 1, 7 1094 ]); 1095 1096 const val2 = sbinDeserialize!(RH, Foo)(data); 1097 //const val2 = sbinDeserialize!Foo(data); 1098 assert (val1 == val2); 1099 } 1100 1101 unittest 1102 { 1103 import std.datetime; 1104 1105 alias RH = CombineReprHandler!(SysTimeAsHNSecsRH!false, NullableAsSumTypeRH); 1106 1107 alias NB = Nullable!ubyte; 1108 1109 alias S = std.sumtype.SumType!(NB, SysTime); 1110 1111 const val1 = [ S(NB.init), S(NB(11)), S(NB.init), S(NB(33)), S(SysTime(0)) ]; 1112 1113 const data = sbinSerialize!RH(val1); 1114 1115 assert (data == [5, 0,0, 0,1,11, 0,0, 0,1,33, 1,0,0,0,0,0,0,0,0]); 1116 1117 const val2 = sbinDeserialize!(RH, S[])(data); 1118 1119 assert (val1 == val2); 1120 } 1121 }