1 module mutils.serializer.binary_maped; 2 3 import std.algorithm : min; 4 import std.meta; 5 import std.stdio; 6 import std.traits; 7 8 import mutils.container.vector; 9 import mutils.conv; 10 public import mutils.serializer.common; 11 12 // COS==ContainerOrSlice 13 14 // THINK ABOUT: if serializer returns false con should: notbe changed, shoud be at the end of var, undefined?? 15 16 ubyte[] toBytes(T)(ref T val) { 17 return (cast(ubyte*)&val)[0 .. T.sizeof]; 18 } 19 20 enum VariableType : byte { 21 bool_, 22 char_, 23 byte_, 24 ubyte_, 25 short_, 26 ushort_, 27 int_, 28 uint_, 29 long_, 30 ulong_, 31 float_, 32 double_, 33 real_, 34 struct_, 35 class_, 36 stringVector, 37 customMap, 38 array, 39 enum_, 40 } 41 42 VariableType getSerVariableType(TTT)() { 43 alias T = Unqual!TTT; 44 45 static if (is(T == bool)) { 46 return VariableType.bool_; 47 } else static if (is(T == char)) { 48 return VariableType.char_; 49 } else static if (is(T == byte)) { 50 return VariableType.byte_; 51 } else static if (is(T == ubyte)) { 52 return VariableType.ubyte_; 53 } else static if (is(T == short)) { 54 return VariableType.short_; 55 } else static if (is(T == ushort)) { 56 return VariableType.ushort_; 57 } else static if (is(T == int)) { 58 return VariableType.int_; 59 } else static if (is(T == uint)) { 60 return VariableType.uint_; 61 } else static if (is(T == long)) { 62 return VariableType.long_; 63 } else static if (is(T == ulong)) { 64 return VariableType.ulong_; 65 } else static if (is(T == float)) { 66 return VariableType.float_; 67 } else static if (is(T == double)) { 68 return VariableType.double_; 69 } else static if (is(T == real)) { 70 return VariableType.real_; 71 } else static if (is(T == struct)) { 72 return VariableType.struct_; 73 } else static if (is(T == class)) { 74 return VariableType.class_; 75 } else static if (isStringVector!T) { 76 return VariableType.stringVector; 77 } else static if (isCustomMap!T) { 78 return VariableType.customMap; 79 } else static if (isStaticArray!T) { 80 return VariableType.staticArray; 81 } else static if (isCustomMap!T) { 82 return VariableType.customMap; 83 } else static if (is(T == enum)) { 84 return VariableType.enum_; 85 } else { 86 static assert(0, "Type not supported 2307"); 87 } 88 } 89 90 bool isSerBasicType(VariableType type) { 91 return (type >= VariableType.bool_ && type <= VariableType.real_); 92 } 93 94 SizeType getSerVariableTypeSize(VariableType type) { 95 switch (type) { 96 case VariableType.bool_: 97 case VariableType.char_: 98 case VariableType.byte_: 99 case VariableType.ubyte_: 100 return 1; 101 case VariableType.short_: 102 case VariableType.ushort_: 103 return 2; 104 case VariableType.int_: 105 case VariableType.uint_: 106 case VariableType.float_: 107 return 4; 108 case VariableType.long_: 109 case VariableType.ulong_: 110 case VariableType.double_: 111 return 8; 112 case VariableType.real_: 113 //static assert(real.sizeof == 16);// On windows 10 114 //static assert(real.alignof == 16);// On windows 2 115 return 16; 116 default: 117 return 0; 118 } 119 } 120 121 void serializeType(Load load, COS)(ref VariableType type, ref COS con) { 122 if (load == Load.yes) { 123 type = cast(VariableType) con[0]; 124 con = con[1 .. $]; 125 } else { 126 con ~= cast(ubyte) type; 127 } 128 } 129 130 struct SerBasicVariable { 131 align(16) ubyte[16] data; // Strictest aligment and biggest size of basic types 132 133 VariableType type; 134 bool serialize(Load load, COS)(ref COS con) { 135 serializeType!(load)(type, con); 136 137 if (!isSerBasicType(type)) { 138 return false; 139 } 140 141 SizeType varSize = getSerVariableTypeSize(type); 142 143 static if (load == Load.yes) { 144 data[0 .. varSize] = con[0 .. varSize]; 145 con = con[varSize .. $]; 146 } else { 147 con ~= data[0 .. varSize]; 148 } 149 return true; 150 } 151 152 T get(T)() { 153 enum VariableType typeT = getSerVariableType!T; 154 enum SizeType varSize = getSerVariableTypeSize(typeT); 155 assert(typeT == type); 156 //static assert(T.sizeof == varSize); // real on windows 10 on linux 16 157 T var; 158 toBytes(var)[0 .. T.sizeof] = data[0 .. T.sizeof]; 159 return var; 160 } 161 162 real getReal() { 163 switch (type) { 164 case VariableType.bool_: 165 return get!bool; 166 case VariableType.char_: 167 return get!char; 168 case VariableType.byte_: 169 return get!byte; 170 case VariableType.ubyte_: 171 return get!ubyte; 172 case VariableType.short_: 173 return get!short; 174 case VariableType.ushort_: 175 return get!ushort; 176 case VariableType.int_: 177 return get!int; 178 case VariableType.uint_: 179 return get!uint; 180 case VariableType.float_: 181 return get!float; 182 case VariableType.long_: 183 return get!long; 184 case VariableType.ulong_: 185 return get!ulong; 186 case VariableType.double_: 187 return get!double; 188 case VariableType.real_: 189 return get!real; 190 default: 191 break; 192 } 193 assert(0); // TODO Log 194 //return 0; 195 } 196 197 real getLong() { 198 switch (type) { 199 case VariableType.bool_: 200 return get!bool; 201 case VariableType.char_: 202 return get!char; 203 case VariableType.byte_: 204 return get!byte; 205 case VariableType.ubyte_: 206 return get!ubyte; 207 case VariableType.short_: 208 return get!short; 209 case VariableType.ushort_: 210 return get!ushort; 211 case VariableType.int_: 212 return get!int; 213 case VariableType.uint_: 214 return get!uint; 215 case VariableType.float_: 216 return get!float; 217 case VariableType.long_: 218 return get!long; 219 case VariableType.ulong_: 220 return get!ulong; 221 case VariableType.double_: 222 return get!double; 223 case VariableType.real_: 224 return get!real; 225 default: 226 break; 227 } 228 229 assert(0); // TODO Log 230 //return 0; 231 } 232 } 233 234 alias SizeNameType = ubyte; 235 alias SizeType = uint; 236 237 struct BinarySerializerMaped { 238 alias SliceElementType = ubyte; 239 __gshared static BinarySerializerMaped instance; 240 241 static ubyte[] beginObject(Load load, COS)(ref COS con) { 242 ubyte[] orginalSlice = con[]; 243 244 SizeType objectSize = 0; // 0 is a placeholder value during save, proper value will be assigned in endObject 245 serializeSize!(load)(objectSize, con); 246 247 static if (load == Load.yes) { 248 con = con[0 .. objectSize]; 249 ubyte[] afterObjectSlice = orginalSlice[SizeType.sizeof + objectSize .. $]; 250 return afterObjectSlice; 251 } else { 252 return orginalSlice; 253 } 254 255 } 256 257 static void endObject(Load load, COS)(ref COS con, ubyte[] slice) { 258 static if (load == Load.yes) { 259 con = slice; 260 } else { 261 SizeType objectSize = cast(SizeType)(con.length - (slice.length + SizeType.sizeof)); 262 con[slice.length .. slice.length + SizeType.sizeof] = toBytes(objectSize); // override object size 263 } 264 265 } 266 267 //support for rvalues during load 268 void serializeWithName(Load load, string name, T, COS)(ref T var, COS con) { 269 static assert(load == Load.yes); 270 serializeWithName!(load, name)(var, con); 271 } 272 273 static bool serializeWithName(Load load, string name, T, COS)(ref T var, ref COS con) { 274 static if (load == Load.yes) { 275 return serializeByName!(load, name)(var, con); 276 } else { 277 serializeName!(load)(name, con); 278 serialize!(load)(var, con); 279 return true; 280 } 281 282 } 283 //support for rvalues during load 284 void serialize(Load load, T, COS)(ref T var, COS con) { 285 static assert(load == Load.yes); 286 serialize!(load)(var, con); 287 } 288 289 static bool serialize(Load load, T, COS)(ref T var, ref COS con) { 290 static if (hasMember!(T, "customSerialize")) { 291 VariableType type = VariableType.struct_; 292 serializeType!(load)(type, con); 293 assert(type==VariableType.struct_); 294 var.customSerialize!(load)(instance, con); 295 return true; 296 } else static if (is(T == enum)) { 297 return serializeEnum!(load)(var, con); 298 } else static if (isBasicType!T) { 299 return serializeBasicVar!(load)(var, con); 300 } else static if (isStringVector!T) { 301 return serializeStringVector!(load)(var, con); 302 } else static if (isCustomVector!T) { 303 return serializeCustomVector!(load)(var, con); 304 } else static if (isStaticArray!T) { 305 return serializeStaticArray!(load)(var, con); 306 } else static if (isCustomMap!T) { 307 return serializeCustomMap!(load)(var, con); 308 } else static if (is(T == struct)) { 309 return serializeStruct!(load)(var, con); 310 } else static if (isPointer!T) { 311 static assert(0, "Can not serialzie pointer"); 312 } else { 313 static assert(0, "Not supported"); 314 } 315 } 316 317 //////////////////////// IMPL 318 319 static bool serializeBasicVar(Load load, T, COS)(ref T var, ref COS con) { 320 static assert(isBasicType!T); 321 322 enum VariableType properType = getSerVariableType!T; 323 VariableType type = properType; 324 325 static if (load == Load.yes) { 326 serializeTypeNoPop!(load)(type, con); 327 if (type != properType) { 328 return serializeBasicVarWithConversion!(load)(var, con); 329 } 330 } 331 332 serializeType!(load)(type, con); 333 static if (load == Load.yes) { 334 toBytes(var)[0 .. T.sizeof] = con[0 .. T.sizeof]; 335 con = con[T.sizeof .. $]; 336 } else { 337 con ~= toBytes(var); 338 } 339 return true; 340 } 341 342 static bool serializeBasicVarWithConversion(Load load, T, COS)(ref T var, ref COS con) { 343 static assert(load == Load.yes); 344 345 enum VariableType properType = getSerVariableType!T; 346 347 VariableType type; 348 serializeTypeNoPop!(load)(type, con); 349 350 if (!isSerBasicType(type)) { 351 return false; 352 } 353 354 SerBasicVariable serBasicVar; 355 serBasicVar.serialize!(load)(con); 356 357 static if (isFloatingPoint!T) { 358 var = cast(T) serBasicVar.getReal(); 359 } else static if (isIntegral!T || is(T == char)) { 360 var = cast(T) serBasicVar.getLong(); 361 } 362 363 return true; 364 } 365 366 static bool serializeEnum(Load load, T, COS)(ref T var, ref COS con) { 367 368 VariableType type = VariableType.enum_; 369 serializeType!(load)(type, con); 370 371 if (type != VariableType.enum_) { 372 return false; 373 } 374 375 static if (load == Load.yes) { 376 SizeType varSize; 377 serializeSize!(load)(varSize, con); 378 string str = cast(string) con[0 .. varSize]; 379 var = str2enum!(T)(str); 380 assert(str.length == 0); 381 } else { 382 char[256] buffer; 383 string enumStr = enum2str(var, buffer); 384 SizeType varSize = cast(SizeType) enumStr.length; 385 serializeSize!(load)(varSize, con); 386 con ~= cast(ubyte[]) enumStr; 387 } 388 return true; //TODO return false when wrong enum is loaded 389 } 390 391 static bool serializeStruct(Load load, T, COS)(ref T var, ref COS con) { 392 static assert(is(T == struct)); 393 394 enum VariableType properType = getSerVariableType!T; 395 VariableType type = properType; 396 serializeType!(load)(type, con); 397 if (type != VariableType.struct_) { 398 return false; 399 } 400 401 ubyte[] begin = beginObject!(load)(con); 402 scope (exit) 403 endObject!(load)(con, begin); 404 405 foreach (i, ref a; var.tupleof) { 406 alias TP = AliasSeq!(__traits(getAttributes, var.tupleof[i])); 407 enum bool doSerialize = !hasNoserializeUda!(TP); 408 static if (doSerialize) { 409 enum string varName = __traits(identifier, var.tupleof[i]); 410 static if (load == Load.yes) { 411 serializeByName!(load, varName)(a, con); 412 } else { 413 serializeName!(load)(varName, con); 414 serialize!(load)(a, con); 415 } 416 } 417 } 418 419 return true; 420 } 421 422 static bool serializeArray(Load load, T, COS)(ref T var, ref COS con) { 423 assert(var.length < SizeType.max); 424 425 ubyte[] begin = beginObject!(load)(con); 426 scope (exit) 427 endObject!(load)(con, begin); 428 429 static if (load == Load.yes) { 430 SizeType elemntsNum; 431 serializeSize!(load)(elemntsNum, con); 432 433 if (elemntsNum == 0) { 434 return true; 435 } 436 437 VariableType elementType; 438 serializeTypeNoPop!(load)(elementType, con); 439 auto conSliceStart = con; 440 441 alias ElementType = Unqual!(ForeachType!(T)); 442 foreach (kkkkk; 0 .. elemntsNum) { 443 ElementType el; 444 bool ok = serialize!(load)(el, con); 445 if (!ok) { 446 return false; 447 } 448 var ~= el; 449 } 450 451 SizeType oneElementSize = getSerVariableTypeSize(elementType); 452 con = conSliceStart[oneElementSize * elemntsNum .. $]; 453 } else { 454 SizeType elemntsNum = 0; 455 size_t conLengthSizeStart = con.length; 456 serializeSize!(load)(elemntsNum, con); // Place holder, proper elementsNum will be saved later 457 458 foreach (ref el; var) { 459 bool ok = serialize!(load)(el, con); 460 assert(ok); 461 elemntsNum++; 462 } 463 SizeType* elementsNumPtr = cast(SizeType*)(con[].ptr + conLengthSizeStart); 464 *elementsNumPtr = elemntsNum; 465 466 } 467 468 return true; 469 } 470 471 static bool serializeStaticArray(Load load, T, COS)(ref T var, ref COS con) { 472 static assert(isStaticArray!T); 473 474 VariableType type = VariableType.array; 475 serializeType!(load)(type, con); 476 if (type != VariableType.array) { 477 return false; 478 } 479 ubyte[] begin = beginObject!(load)(con); 480 scope (exit) 481 endObject!(load)(con, begin); 482 483 SizeType elemntsNum = cast(SizeType) var.length; 484 serializeSize!(load)(elemntsNum, con); 485 486 size_t elementsToLoadSave = min(var.length, elemntsNum); 487 488 static if (load == Load.yes) { 489 VariableType elementType; 490 serializeTypeNoPop!(load)(elementType, con); 491 auto conSliceStart = con; 492 } 493 494 foreach (i, ref el; var) { 495 bool ok = serialize!(load)(el, con); 496 if (!ok) { 497 return false; 498 } 499 if (i >= elementsToLoadSave) { 500 break; 501 } 502 } 503 504 static if (load == Load.yes) { 505 SizeType oneElementSize = getSerVariableTypeSize(elementType); 506 con = conSliceStart[oneElementSize * elementsToLoadSave .. $]; 507 } 508 return true; 509 510 } 511 512 static bool serializeCustomVector(Load load, T, COS)(ref T var, ref COS con) { 513 alias ElementType = Unqual!(ForeachType!(T)); 514 515 VariableType type = VariableType.array; 516 serializeType!(load)(type, con); 517 if (type != VariableType.array) { 518 return false; 519 } 520 521 static if (load == Load.yes) { 522 static if (hasMember!(T, "initialize")) { 523 var.initialize(); 524 } 525 auto sliceTmp = con; 526 SizeType elementsNum; 527 serializeSize!(load)(elementsNum, sliceTmp); // Size of whole array data - ignore 528 serializeSize!(load)(elementsNum, sliceTmp); 529 530 static if (hasMember!(T, "reserve")) { 531 var.reserve(elementsNum); 532 } 533 } 534 535 return serializeArray!(load)(var, con); 536 } 537 538 static bool serializeStringVector(Load load, T, COS)(ref T var, ref COS con) { 539 alias ElementType = char; 540 541 VariableType type = VariableType.stringVector; 542 serializeType!(load)(type, con); 543 if (type != VariableType.stringVector) { 544 return false; 545 } 546 547 assert(var.length < SizeType.max); 548 SizeType size = cast(SizeType) var.length; 549 serializeSize!(load)(size, con); 550 551 static if (load == Load.yes) { 552 static if (hasMember!(T, "initialize")) { 553 var.initialize(); 554 } 555 var = cast(string) con[0 .. size]; 556 con = con[size .. $]; 557 } else { 558 assert(var[].length == size); 559 con ~= cast(ubyte[]) var[]; 560 } 561 return true; 562 } 563 564 static bool serializeRange(Load load, T, COS)(ref T var, ref COS con) { 565 static assert(load == Load.no); 566 alias ElementType = Unqual!(ForeachType!(T)); 567 568 VariableType type = VariableType.staticArray; // Pretend it is staticArray 569 serializeType!(load)(type, con); 570 return serializeSlice!(load)(var[], con); 571 } 572 573 static bool serializeCustomMap(Load load, T, COS)(ref T var, ref COS con) { 574 static assert(isCustomMap!T); 575 576 VariableType type = VariableType.customMap; 577 serializeType!(load)(type, con); 578 if (type != VariableType.customMap) { 579 return false; 580 } 581 //uint dataLength = cast(uint)(var.length); 582 //serialize!(loadOrSkip!load)(dataLength, con); 583 584 ubyte[] begin = beginObject!(load)(con); 585 scope (exit) 586 endObject!(load)(con, begin); 587 588 SizeType elemntsNum = cast(SizeType) var.length; 589 serializeSize!(load)(elemntsNum, con); 590 591 static if (load == Load.yes) { 592 static if (hasMember!(T, "initialize")) { 593 var.initialize(); 594 } 595 static if (hasMember!(T, "reserve")) { 596 var.reserve(elemntsNum); 597 } 598 foreach (i; 0 .. elemntsNum) { 599 bool ok; 600 T.Key key; 601 T.Value value; 602 ok = serialize!(load)(key, con); 603 if (!ok) { 604 return false; 605 } 606 ok = serialize!(load)(value, con); 607 if (!ok) { 608 return false; 609 } 610 var.add(key, value); 611 } 612 } else { 613 foreach (ref key, ref value; &var.byKeyValue) { 614 serialize!(load)(key, con); 615 serialize!(load)(value, con); 616 } 617 } 618 return false; 619 } 620 621 //////////////////////////////////////////////// HELPERS 622 623 static bool serializeByName(Load load, string name, T, COS)(ref T var, ref COS con) { 624 static assert(load == Load.yes); 625 auto conBegin = con; 626 scope (exit) 627 con = conBegin; // Revert slice 628 629 foreach (noInfiniteLoop; 0 .. 1000) { 630 string varName; 631 serializeName!(load)(varName, con); 632 633 if (varName is null) { 634 break; 635 } 636 637 ubyte[] conStartVar = con; 638 639 VariableType type; // Custom serialize might not have it 640 serializeType!(load)(type, con); 641 642 SizeType varSize; 643 SizeType sizeSize; 644 645 if (isSerBasicType(type)) { 646 varSize = getSerVariableTypeSize(type); 647 } else { 648 serializeSize!(load)(varSize, con); 649 sizeSize = SizeType.sizeof; 650 } 651 SizeType varEnd = 1 + sizeSize + varSize; 652 653 if (varName == name) { 654 ubyte[] conStartVarTmp = conStartVar[0 .. varEnd]; 655 bool loaded = serialize!(load)(var, conStartVarTmp); 656 if (loaded) { 657 if (noInfiniteLoop == 0) { // Move con because no one will read this value again(no key duplicates) 658 conBegin = conStartVar[varEnd .. $]; 659 } 660 return true; 661 } 662 } 663 if (varEnd >= conStartVar.length) { 664 return false; 665 } 666 con = conStartVar[varEnd .. $]; 667 } 668 return false; 669 } 670 671 private static void serializeName(Load load, COS)(auto ref string name, ref COS con) { 672 if (load == Load.yes) { 673 if (con.length < SizeNameType.sizeof) { 674 name = null; 675 return; 676 } 677 SizeNameType nameLength; 678 toBytes(nameLength)[0 .. SizeNameType.sizeof] = con[0 .. SizeNameType.sizeof]; 679 con = con[SizeNameType.sizeof .. $]; 680 name = cast(string) con[0 .. nameLength]; 681 con = con[nameLength .. $]; 682 } else { 683 assert(name.length <= SizeNameType.max); 684 SizeNameType nameLength = cast(SizeNameType) name.length; 685 con ~= toBytes(nameLength); 686 con ~= cast(ubyte[]) name; 687 } 688 //writeln(name.length); 689 } 690 691 private static void serializeTypeNoPop(Load load, COS)(ref VariableType type, ref COS con) { 692 static assert(load == Load.yes); 693 694 type = cast(VariableType) con[0]; 695 696 } 697 698 private static void serializeSize(Load load, COS)(ref SizeType size, ref COS con) { 699 if (load == Load.yes) { 700 toBytes(size)[0 .. SizeType.sizeof] = con[0 .. SizeType.sizeof]; 701 con = con[SizeType.sizeof .. $]; 702 } else { 703 con ~= toBytes(size); 704 } 705 } 706 707 private static void serializeSizeNoPop(Load load, COS)(ref SizeType size, ref COS con) { 708 static assert(load == Load.yes); 709 710 toBytes(size)[0 .. SizeType.sizeof] = con[0 .. SizeType.sizeof]; 711 } 712 713 } 714 715 import MSC; 716 717 // test basic type 718 unittest { 719 int test = 1; 720 721 CON_UB container; 722 723 //save 724 BinarySerializerMaped.serializeWithName!(Load.no, "name",)(test, container); 725 assert(container.length == SizeNameType.sizeof + 4 + VariableType.sizeof + 4); 726 727 //reset var 728 test = 0; 729 //load 730 ubyte[] dataSlice = container[]; 731 BinarySerializerMaped.serializeWithName!(Load.yes, "name",)(test, dataSlice); 732 assert(test == 1); 733 //assert(dataSlice.length==0); 734 } 735 736 unittest { 737 static struct Test { 738 int a; 739 long b; 740 ubyte c; 741 } 742 743 static struct TestB { 744 int bbbb; 745 long c; 746 char a; 747 } 748 749 Test test = Test(1, 2, 3); 750 Vector!ubyte container; 751 //save 752 BinarySerializerMaped.serializeWithName!(Load.no, "name",)(test, container); 753 assert(container.length == SizeNameType.sizeof + 4 + VariableType.sizeof 754 + SizeType.sizeof + 3 * SizeNameType.sizeof + 3 + 3 * VariableType.sizeof + 4 + 8 + 1); 755 //writeln("Ratio to ideal minimal size: ", container.length/(4.0f+8.0f+1.0f)); 756 //reset var 757 TestB testB; 758 759 //load 760 ubyte[] dataSlice = container[]; 761 BinarySerializerMaped.serializeWithName!(Load.yes, "name",)(testB, dataSlice); 762 assert(testB == TestB(0, 3, 1)); 763 } 764 // empty struct 765 unittest { 766 static struct Test { 767 } 768 769 static struct TestB { 770 int aa; 771 int bb; 772 } 773 774 Test test = Test(); 775 Vector!ubyte container; 776 //save 777 BinarySerializerMaped.serializeWithName!(Load.no, "name",)(test, container); 778 //reset var 779 TestB testB; 780 //load 781 ubyte[] dataSlice = container[]; 782 BinarySerializerMaped.serializeWithName!(Load.yes, "name",)(testB, dataSlice); 783 784 } 785 786 // vectors 787 unittest { 788 static struct Test { 789 int[4] aa; 790 Vector!ubyte bb; 791 int[3] cc; 792 } 793 794 static struct TestB { 795 Vector!long bb; 796 int[2] aa; 797 Vector!int cc; 798 } 799 800 enum ubyte[] bytesA = [3, 2, 1]; 801 enum long[] bytesB = [3, 2, 1]; 802 Test test = Test([1, 2, 3, 4], Vector!ubyte(bytesA), [10, 20, 30]); 803 Vector!ubyte container; 804 //save 805 BinarySerializerMaped.serializeWithName!(Load.no, "name",)(test, container); 806 807 //reset var 808 TestB testB; 809 //load 810 ubyte[] dataSlice = container[]; 811 BinarySerializerMaped.serializeWithName!(Load.yes, "name",)(testB, dataSlice); 812 813 assert(testB == TestB(Vector!long(bytesB), [1, 2], Vector!int([10, 20, 30]))); 814 } 815 816 // test custom map 817 unittest { 818 import mutils.container.hash_map; 819 820 static struct TestStruct { 821 int a; 822 int b; 823 } 824 825 static struct TestStructB { 826 ushort b; 827 long a; 828 char c; 829 } 830 831 HashMap!(int, TestStruct) map; 832 map.add(1, TestStruct(1, 11)); 833 map.add(7, TestStruct(7, 77)); 834 835 Vector!ubyte container; 836 837 //save 838 BinarySerializerMaped.serializeWithName!(Load.no, "name",)(map, container); 839 840 //reset var 841 HashMap!(byte, TestStructB) mapB; 842 //load 843 ubyte[] dataSlice = container[]; 844 BinarySerializerMaped.serializeWithName!(Load.yes, "name",)(mapB, dataSlice); 845 assert(mapB.get(1) == TestStructB(11, 1)); 846 assert(mapB.get(7) == TestStructB(77, 7)); 847 } 848 849 // test string 850 unittest { 851 import mutils.container.string_intern; 852 853 static struct Test { 854 Vector!char aa; 855 StringIntern bb; 856 } 857 858 static struct TestB { 859 Vector!char bb; 860 StringIntern aa; 861 } 862 863 Test test = Test(Vector!char("aaa"), StringIntern("bbb")); 864 Vector!ubyte container; 865 //save 866 BinarySerializerMaped.serializeWithName!(Load.no, "name",)(test, container); 867 //reset var 868 TestB testB; 869 //load 870 ubyte[] dataSlice = container[]; 871 BinarySerializerMaped.serializeWithName!(Load.yes, "name",)(testB, dataSlice); 872 assert(testB.aa == "aaa"); 873 assert(testB.bb[] == "bbb"); 874 875 } 876 877 // test enum 878 unittest { 879 880 enum EnumA { 881 aaa = 10, 882 bbb = 20, 883 } 884 885 enum EnumB { 886 bbb = 10, 887 aaa = 20, 888 } 889 890 EnumA test = EnumA.aaa; 891 Vector!ubyte container; 892 //save 893 BinarySerializerMaped.serializeWithName!(Load.no, "name",)(test, container); 894 //reset var 895 EnumB testB; 896 //load 897 ubyte[] dataSlice = container[]; 898 BinarySerializerMaped.serializeWithName!(Load.yes, "name",)(testB, dataSlice); 899 assert(testB == EnumB.aaa); 900 }