1 /******************************************************************************* 2 3 Format Explained 4 ---------------- 5 6 This package implements a binary serialization format useful for efficient 7 struct representation in monomorphic environment. All servers are expected to 8 have similar enough h/w architecture and software to have identical in-memory 9 representation of D structures. It doesn't work as a generic cross-platform 10 serialization format. 11 12 Essential idea here is storing all struct instance data (including all data 13 transitively accessible via arrays / pointers) in a single contiguous memory 14 buffer. Which is exactly the reason why the package is named like that. That 15 way deserialization is very fast and doesn't need any memory allocation for 16 simple cases - all the deserializer needs to do is to iterate through the 17 memory chunk and update internal pointers. 18 19 ``contiguous.Deserializer`` returns a memory buffer wrapped in 20 ``Contiguous!(S)`` struct. Such wrapper is guaranteed to conform to the 21 contiguity expectation explained above. It is recommended to use it in your 22 application instead of plain ``void[]`` for added type safety. 23 24 There are certain practical complications with it that are explained as part of 25 ``contiguous.Serializer`` and ``contiguous.Deserializer`` API docs. Those should 26 not concern most applications and won't be mentioned in the overview. 27 28 Available Decorators 29 -------------------- 30 31 ``contiguous.VersionDecorator`` adds struct versioning information to the basic 32 binary serialization format. It expects struct definitions with additional 33 meta-information available at compile-time and prepends a version number byte 34 before the actual data buffer. Upon loading the serialized data, the stored 35 version number is compared against the expected one and automatic struct 36 conversion is done if needed. It only allows conversion through one version 37 increment/decrement at a time. 38 39 ``contiguous.MultiVersionDecorator`` is almost identical to 40 plain ``VersionDecorator`` but allows the version increment range to be defined 41 in the constructor. Distinct classes are used so that, if incoming data 42 accidentally is too old, performance-critical applications will emit an error 43 rather than wasting CPU cycles converting through multiple versions. 44 For other aplications multi-version implementation should be more convenient. 45 46 API 47 --- 48 49 All methods that do deserialization of data (``Deserializer.deserialize``, 50 ``VersionDecorator.load``, ``VersionDecorator.loadCopy``) return 51 ``Contiguous!(S)`` struct. Lifetime of such struct is identical to lifetime 52 of buffer used for deserialization. For 1-argument methods it is that argument, 53 for 2-argument ones it is the destination argument. 54 55 To get a detailed overview of serializer API check the modules: 56 57 ``ocean.util.serialize.contiguous.Serializer`` 58 ``ocean.util.serialize.contiguous.Deserializer`` 59 60 To get a detailed overview of decorator API check the mixins used for its 61 generation: 62 63 ``ocean.util.serialize.model.VersionDecoratorMixins`` 64 ``ocean.util.serialize.contiguous.model.LoadCopyMixin`` 65 66 Serializer methods are static because they work only with argument state. 67 Decorators need to be created as persistent objects because they need an 68 intermediate state for version conversions. 69 70 If a method refers to ``DeserializerReturnType!(Deserializer, S)`` as return 71 type, you can substitute it with ``Contiguous!(S)`` as it is the return type 72 used by existing contiguous deserializer. 73 74 There is also the ``ocean.util.serialize.contiguous.Util`` module which provides 75 higher level ``copy`` functions for optimized deep copying of data between 76 contiguous structs as well as from normal structs to contiguous ones. 77 78 Recommended Usage 79 ----------------- 80 81 The contiguous serialization format and the version decorator are primarily 82 designed as a way to interchange D structs between different applications 83 that may expect different versions of those struct layout. 84 It is recommended to completely strip the version information with the help 85 of the decorator upon initially reading a record, and to use the resulting 86 raw contiguous buffer internally in the application (i.e. in a cache). That 87 way you can use any of the serialization/deserialization utilities in the 88 application without thinking about the version meta-data 89 90 Typical code pattern for a cache: 91 92 1. Define a ``Cache`` of ``Contiguous!(S)`` elements. 93 94 2. When receiving external data we do 95 ``version_decorator.loadCopy!(S)(dht_data, cache_element)`` 96 97 3. Use ``contiguous.Util.copy(cache_element, contiguous_instance)`` for 98 copying the struct instance if needed 99 100 4. Use ``contiguous_instance.ptr`` to work with deserialized data as if 101 it was ``S*`` 102 103 It is likely that you will need to change the code to use strongly-typed 104 ``Contiguous!(S)`` persistent buffer instead of raw ``void[]`` buffer. 105 Possibly multiple such buffers if the old one was reused for different 106 struct types. This may result in small memory footprint increase at the 107 benefit of better type safety and optimized copy operations. 108 109 You can completely abandon the resulting the ``Contiguous!(S)`` instance and 110 use/store the plain ``S*`` instead (retrieved via .ptr getter) which will 111 work as long as underlying buffer persists. This is however slightly 112 discouraged because if you will need to copy the data later, it 113 will be impossible to use optimized version without unsafe explicit casts. 114 115 License: 116 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 117 Alternatively, this file may be distributed under the terms of the Tango 118 3-Clause BSD License (see LICENSE_BSD.txt for details). 119 120 Copyright: 121 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 122 All rights reserved. 123 124 *******************************************************************************/ 125 126 module ocean.util.serialize.contiguous; 127 128 /****************************************************************************** 129 130 Public imports 131 132 ******************************************************************************/ 133 134 public import ocean.util.serialize.contiguous.Contiguous, 135 ocean.util.serialize.contiguous.Deserializer, 136 ocean.util.serialize.contiguous.Serializer, 137 ocean.util.serialize.contiguous.Util; 138 139 version (unittest): 140 141 /****************************************************************************** 142 143 Test imports 144 145 ******************************************************************************/ 146 147 import ocean.meta.types.Qualifiers; 148 import ocean.core.Test; 149 import ocean.core.Verify; 150 import ocean.core.StructConverter; 151 import ocean.core.DeepCompare; 152 import ocean.core.Buffer; 153 154 /****************************************************************************** 155 156 Complex data structure used in most tests 157 158 Some test define more specialized structures as nested types for debugging 159 simplicity 160 161 ******************************************************************************/ 162 163 struct S 164 { 165 struct S_1 166 { 167 int a; 168 double b; 169 } 170 171 struct S_2 172 { 173 int[] a; 174 int[][] b; 175 } 176 177 struct S_3 178 { 179 float[][2] a; 180 } 181 182 struct S_4 183 { 184 char[][] a; 185 } 186 187 S_1 s1; 188 189 S_2 s2; 190 S_2[1] s2_static_array; 191 192 S_3 s3; 193 194 S_4[] s4_dynamic_array; 195 196 char[][3] static_of_dynamic; 197 198 char[][2][3][] dynamic_of_static_of_static_of_dynamic; 199 200 union 201 { 202 int union_a; 203 int union_b; 204 } 205 206 alias typeof(this) This; 207 208 /*************************************************************************** 209 210 Ensure all dynamic array references in this instance are `null`, which 211 they should be if this instance references the output data of 212 `Serializer.serialize`. 213 214 ***************************************************************************/ 215 216 void testNullReferences ( ) const 217 { 218 foreach (s2_static_array_element; this.s2_static_array) 219 { 220 testArray!("is")(s2_static_array_element.a, null); 221 testArray!("is")(s2_static_array_element.b, null); 222 } 223 224 foreach (s3_a_element; this.s3.a) 225 testArray!("is")(s3_a_element, null); 226 227 testArray!("is")(this.s4_dynamic_array, null); 228 229 foreach (static_of_dynamic_element; this.static_of_dynamic) 230 testArray!("is")(static_of_dynamic_element, null); 231 } 232 233 /*************************************************************************** 234 235 Convenience alias, this template instantiation is used a lot in tests. 236 237 ***************************************************************************/ 238 239 alias .trivialDeserialize!(This) trivialDeserialize; 240 241 /*************************************************************************** 242 243 Returns the number of bytes the `Serializer` should use to serialise 244 this instance. 245 246 ***************************************************************************/ 247 248 size_t serialized_length ( ) const 249 { 250 static size_t s2_length ( ref const(S_2) s2 ) 251 { 252 return serialArrayLength(s2.a) + serialArrayLength(s2.b); 253 } 254 255 size_t n = This.sizeof; 256 257 n += s2_length(this.s2); 258 259 foreach (s2_static_array_element; this.s2_static_array) 260 n += s2_length(s2_static_array_element); 261 262 foreach (s3_a_element; this.s3.a) 263 n += serialArrayLength(s3_a_element); 264 265 n += serialArrayLength(this.s4_dynamic_array); 266 foreach (s4_dynamic_array_element; this.s4_dynamic_array) 267 n += serialArrayLength(s4_dynamic_array_element.a); 268 269 foreach (static_of_dynamic_element; this.static_of_dynamic) 270 n += serialArrayLength(static_of_dynamic_element); 271 272 n += serialArrayLength(this.dynamic_of_static_of_static_of_dynamic); 273 foreach (dynamic_element; this.dynamic_of_static_of_static_of_dynamic) 274 foreach (static_element; dynamic_element) 275 foreach (static_element2; static_element) 276 n += serialArrayLength(static_element2); 277 278 return n; 279 } 280 } 281 282 /****************************************************************************** 283 284 Returns: 285 S instance with fields set to some meaningful values 286 287 ******************************************************************************/ 288 289 S defaultS() 290 { 291 S s; 292 293 s.s1.a = 42; 294 s.s1.b = 42.42; 295 296 s.s2.a = [ 1, 2, 3, 4 ]; 297 s.s2.b = [ [ 0 ], [ 20, 21 ], [ 22 ] ]; 298 299 structConvert(s.s2, s.s2_static_array[0]); 300 301 s.s3.a[0] = [ 1.0, 2.0 ]; 302 s.s3.a[1] = [ 100.1, 200.2 ]; 303 304 s.s4_dynamic_array = [ 305 S.S_4([ "aaa".dup, "bbb".dup, "ccc".dup ]), 306 S.S_4([ "a".dup, "bb".dup, "ccc".dup, "dddd".dup ]), 307 S.S_4([ "".dup ]) 308 ]; 309 310 s.static_of_dynamic[] = [ "a".dup, "b".dup, "c".dup ]; 311 312 s.dynamic_of_static_of_static_of_dynamic = [ 313 [["Die".dup, "Katze".dup], ["tritt".dup, "die".dup], ["Treppe".dup, "krumm.".dup]], 314 [["abc".dup, "def".dup], ["ghi".dup, "jkl".dup], ["mno".dup, "pqr".dup]] 315 ]; 316 317 s.union_a = 42; 318 319 return s; 320 } 321 322 /****************************************************************************** 323 324 Does series of tests on `checked` to verify that it is equal to struct 325 returned by `defaultS()` 326 327 328 Params: 329 checked = S instance to check for equality 330 t = test to check 331 332 ******************************************************************************/ 333 334 void testS(NamedTest t, ref S checked) 335 { 336 with (t) 337 { 338 test!("==")(checked.s1, defaultS().s1); 339 test!("==")(checked.s2.a, defaultS().s2.a); 340 test!("==")(checked.s2.b, defaultS().s2.b); 341 342 foreach (index, elem; checked.s2_static_array) 343 { 344 test!("==")(elem.a, defaultS().s2_static_array[index].a); 345 } 346 347 foreach (index, elem; checked.s3.a) 348 { 349 test!("==")(elem, defaultS().s3.a[index]); 350 } 351 352 foreach (index, elem; checked.s4_dynamic_array) 353 { 354 test!("==")(elem.a, defaultS().s4_dynamic_array[index].a); 355 } 356 357 test!("==")(checked.static_of_dynamic[], 358 defaultS().static_of_dynamic[]); 359 360 test!("==")(checked.dynamic_of_static_of_static_of_dynamic, 361 defaultS().dynamic_of_static_of_static_of_dynamic); 362 363 test!("==")(checked.union_a, defaultS().union_b); 364 } 365 } 366 367 /****************************************************************************** 368 369 Sanity test for helper functions 370 371 ******************************************************************************/ 372 373 unittest 374 { 375 auto t = new NamedTest("Sanity"); 376 auto s = defaultS(); 377 testS(t, s); 378 } 379 380 /****************************************************************************** 381 382 Standard workflow 383 384 ******************************************************************************/ 385 386 unittest 387 { 388 auto t = new NamedTest("Basic"); 389 auto s = defaultS(); 390 void[] buffer; 391 392 Serializer.serialize(s, buffer); 393 test!("==")(buffer.length, s.serialized_length); 394 S.trivialDeserialize(buffer).testNullReferences(); 395 auto cont_S = Deserializer.deserialize!(S)(buffer); 396 cont_S.enforceIntegrity(); 397 testS(t, *cont_S.ptr); 398 } 399 400 /****************************************************************************** 401 402 Standard workflow, copy version 403 404 ******************************************************************************/ 405 406 unittest 407 { 408 auto t = new NamedTest("Basic + Copy"); 409 auto s = defaultS(); 410 void[] buffer; 411 412 Serializer.serialize(s, buffer); 413 test!("==")(buffer.length, s.serialized_length); 414 S.trivialDeserialize(buffer).testNullReferences(); 415 Contiguous!(S) destination; 416 auto cont_S = Deserializer.deserialize(buffer, destination); 417 cont_S.enforceIntegrity(); 418 419 t.test(cont_S.ptr is destination.ptr); 420 testS(t, *cont_S.ptr); 421 } 422 423 /****************************************************************************** 424 425 Serialize in-place 426 427 ******************************************************************************/ 428 429 unittest 430 { 431 auto t = new NamedTest("In-place serialization"); 432 auto s = defaultS(); 433 void[] buffer; 434 435 // create Contiguous!(S) instance first 436 Serializer.serialize(s, buffer); 437 test!("==")(buffer.length, s.serialized_length); 438 S.trivialDeserialize(buffer).testNullReferences(); 439 auto cont_S = Deserializer.deserialize!(S)(buffer); 440 441 // check that serializations nulls pointers 442 auto serialized = Serializer.serialize(cont_S); 443 test!("is")(serialized.ptr, cont_S.ptr); 444 test!("is")(cont_S.ptr.s4_dynamic_array.ptr, null); 445 test!("is")(cont_S.ptr.s2.a.ptr, null); 446 test!("is")(cont_S.ptr.s2.b.ptr, null); 447 } 448 449 /****************************************************************************** 450 451 Extra unused bytes in source 452 453 ******************************************************************************/ 454 455 unittest 456 { 457 auto t = new NamedTest("Basic + Copy"); 458 auto s = defaultS(); 459 void[] buffer; 460 461 Serializer.serialize(s, buffer); 462 test!("==")(buffer.length, s.serialized_length); 463 S.trivialDeserialize(buffer).testNullReferences(); 464 465 // emulate left-over bytes from previous deserializations 466 buffer.length = buffer.length * 2; 467 468 Contiguous!(S) destination; 469 auto cont_S = Deserializer.deserialize(buffer, destination); 470 cont_S.enforceIntegrity(); 471 472 t.test(cont_S.ptr is destination.ptr); 473 testS(t, *cont_S.ptr); 474 } 475 476 /****************************************************************************** 477 478 Some arrays set to null 479 480 ******************************************************************************/ 481 482 unittest 483 { 484 auto t = new NamedTest("Null Arrays"); 485 auto s = defaultS(); 486 s.s2.a = null; 487 void[] buffer; 488 489 Serializer.serialize(s, buffer); 490 test!("==")(buffer.length, s.serialized_length); 491 S.trivialDeserialize(buffer).testNullReferences(); 492 auto cont_S = Deserializer.deserialize!(S)(buffer); 493 cont_S.enforceIntegrity(); 494 495 t.test!("==")(cont_S.ptr.s2.a.length, 0); 496 auto s_ = cont_S.ptr; // hijack the invariant 497 s_.s2.a = defaultS().s2.a; // revert the difference 498 testS(t, *s_); // check the rest 499 } 500 501 /****************************************************************************** 502 503 Nested arrays set to null 504 505 ******************************************************************************/ 506 507 unittest 508 { 509 auto t = new NamedTest("Nested Null Arrays"); 510 auto s = defaultS(); 511 s.s2.b[0] = null; 512 void[] buffer; 513 514 Serializer.serialize(s, buffer); 515 test!("==")(buffer.length, s.serialized_length); 516 S.trivialDeserialize(buffer).testNullReferences(); 517 auto cont_S = Deserializer.deserialize!(S)(buffer); 518 cont_S.enforceIntegrity(); 519 520 t.test!("==")(cont_S.ptr.s2.b[0].length, 0); 521 auto s_ = cont_S.ptr; // hijack the invariant 522 s_.s2.b[0] = defaultS().s2.b[0]; // revert the difference 523 testS(t, *s_); // check the rest 524 } 525 526 /****************************************************************************** 527 528 Recursie static arrays 529 530 ******************************************************************************/ 531 532 unittest 533 { 534 auto t = new NamedTest("Recursive static"); 535 536 struct Outer 537 { 538 struct Inner 539 { 540 char[][] a; 541 } 542 543 Inner[2][1][1] a; 544 } 545 546 Outer s; 547 s.a[0][0][0].a = [ "1".dup, "2".dup, "3".dup ]; 548 s.a[0][0][1].a = [ "1".dup, "2".dup ]; 549 550 void[] buffer; 551 Serializer.serialize(s, buffer); 552 553 size_t expected_length = s.sizeof; 554 foreach (a1; s.a) 555 foreach (a2; a1) 556 foreach (a3; a2) 557 expected_length += serialArrayLength(a3.a); 558 test!("==")(buffer.length, expected_length); 559 560 with (*trivialDeserialize!(Outer)(buffer)) 561 foreach (a1; a) 562 foreach (a2; a1) 563 foreach (a3; a2) 564 testArray!("is")(a3.a, null); 565 566 auto cont = Deserializer.deserialize!(Outer)(buffer); 567 568 test!("==")(cont.ptr.a[0][0][0].a, s.a[0][0][0].a); 569 test!("==")(cont.ptr.a[0][0][1].a, s.a[0][0][1].a); 570 } 571 572 /****************************************************************************** 573 574 Partial loading of extended struct 575 576 Ensures that if struct definition has been extended incrementaly one can 577 still load old definition from the serialized buffer 578 579 ******************************************************************************/ 580 581 unittest 582 { 583 struct Old 584 { 585 int one; 586 } 587 588 struct New 589 { 590 int one; 591 int two; 592 } 593 594 auto input = New(32, 42); 595 void[] buffer; 596 Serializer.serialize(input, buffer); 597 auto output = Deserializer.deserialize!(Old)(buffer); 598 599 test!("==")(input.one, output.ptr.one); 600 } 601 602 /****************************************************************************** 603 604 Serialization of unions of structs with no dynamic arrays 605 606 ******************************************************************************/ 607 608 unittest 609 { 610 struct A { int x; } 611 struct B { int[3] arr; } 612 613 struct S 614 { 615 union 616 { 617 A a; 618 B b; 619 }; 620 } 621 622 void[] buffer; 623 auto input = S(A(42)); 624 Serializer.serialize(input, buffer); 625 auto output = Deserializer.deserialize!(S)(buffer); 626 627 test!("==")(output.ptr.a, A(42)); 628 629 input.b.arr[] = [0, 1, 2]; 630 Serializer.serialize(input, buffer); 631 output = Deserializer.deserialize!(S)(buffer); 632 633 test!("==")(output.ptr.b.arr[], [0, 1, 2][]); 634 } 635 636 /****************************************************************************** 637 638 Serialization of unions of structs with dynamic arrays (fails) 639 640 ******************************************************************************/ 641 642 unittest 643 { 644 struct A { int x; } 645 struct B { int[] arr; } 646 647 struct S 648 { 649 union XX 650 { 651 A a; 652 B b; 653 }; 654 655 XX field; 656 } 657 658 void[] buffer; 659 S input; 660 661 static assert (!is(typeof(Serializer.serialize(input, buffer)))); 662 } 663 664 /****************************************************************************** 665 666 Allocation Control 667 668 ******************************************************************************/ 669 670 unittest 671 { 672 auto t = new NamedTest("Memory Usage"); 673 auto s = defaultS(); 674 void[] buffer; 675 676 Serializer.serialize(s, buffer); 677 S.trivialDeserialize(buffer).testNullReferences(); 678 testNoAlloc(Serializer.serialize(s, buffer)); 679 auto cont_s = Deserializer.deserialize!(S)(buffer); 680 testNoAlloc(Deserializer.deserialize!(S)(buffer)); 681 buffer = buffer.dup; 682 testNoAlloc(Deserializer.deserialize(buffer, cont_s)); 683 } 684 685 686 /****************************************************************************** 687 688 Array of const elements 689 690 ******************************************************************************/ 691 692 unittest 693 { 694 static struct CS 695 { 696 cstring s; 697 } 698 699 auto cs = CS("Hello world"); 700 void[] buffer; 701 702 Serializer.serialize(cs, buffer); 703 test!("==")(buffer.length, cs.sizeof + serialArrayLength(cs.s)); 704 with (*trivialDeserialize!(CS)(buffer)) 705 testArray!("is")(s, null); 706 auto new_s = Deserializer.deserialize!(CS)(buffer); 707 test!("==")(cs.s, new_s.ptr.s); 708 } 709 710 711 /****************************************************************************** 712 713 Ensure that immutable elements are rejected 714 715 ******************************************************************************/ 716 717 unittest 718 { 719 static struct IS 720 { 721 istring s; 722 } 723 724 static struct II 725 { 726 immutable(int) s; 727 } 728 729 IS s1 = IS("Hello world"); 730 II s2 = II(42); 731 void[] buffer1, buffer2; 732 733 /* 734 * There is no check for the serializer because it is "okay" to 735 * serialize immutable data. 736 * Obviously they won't be deserializable but that is where 737 * we could break the type system. 738 */ 739 740 // Uncomment to check error message 741 //Deserializer.deserialize!(IS)(buffer1); 742 //Deserializer.deserialize!(II)(buffer2); 743 744 static assert(!is(typeof({Deserializer.deserialize!(IS)(buffer1);})), 745 "Serializer should reject a struct with 'istring'"); 746 static assert(!is(typeof({Deserializer.deserialize!(II)(buffer2);})), 747 "Deserializer should reject a struct with 'immutable' element"); 748 } 749 750 /****************************************************************************** 751 752 Ensure that full-const struct can be serialized 753 754 ******************************************************************************/ 755 756 unittest 757 { 758 static struct S1 759 { 760 mstring s; 761 } 762 763 static struct S2 764 { 765 S1[] nested; 766 } 767 768 auto s = const(S2)([ const(S1)("Hello world") ]); 769 void[] buffer; 770 771 Serializer.serialize(s, buffer); 772 773 size_t expected_length = s.sizeof + serialArrayLength(s.nested); 774 foreach (nested_element; s.nested) 775 expected_length += serialArrayLength(nested_element.s); 776 test!("==")(buffer.length, expected_length); 777 778 with (*trivialDeserialize!(S2)(buffer)) 779 testArray!("is")(nested, null); 780 781 auto d = Deserializer.deserialize!(S2)(buffer); 782 test(deepEquals(*d.ptr, s)); 783 } 784 785 /****************************************************************************** 786 787 Const arrays of arrays 788 789 ******************************************************************************/ 790 791 struct ConstS 792 { 793 const(int) n = 3; 794 const(char[][]) a = ["Hello", "World"]; 795 const(char[])[2][3] b = [ 796 ["Die", "Katze"], ["tritt", "die"], ["Treppe", "krumm."] 797 ]; 798 } 799 800 unittest 801 { 802 void[] buffer; 803 804 ConstS s; 805 806 Serializer.serialize(s, buffer); 807 808 size_t expected_length = s.sizeof + serialArrayLength(s.a); 809 foreach (b1; s.b) 810 foreach (b2; b1) 811 expected_length += serialArrayLength(b2); 812 test!("==")(buffer.length, expected_length); 813 814 with (*trivialDeserialize!(ConstS)(buffer)) 815 { 816 testArray!("is")(a, null); 817 foreach (b1; b) 818 foreach (b2; b1) 819 testArray!("is")(b2, null); 820 } 821 822 auto cont_S = Deserializer.deserialize!(ConstS)(buffer); 823 cont_S.enforceIntegrity(); 824 825 test!("==")(cont_S.ptr.n, 3); 826 test!("==")(cont_S.ptr.a, ["Hello", "World"]); 827 828 cstring[2][3] b = [ 829 ["Die", "Katze"], ["tritt", "die"], ["Treppe", "krumm."] 830 ]; 831 test!("==")(cont_S.ptr.b, b); 832 } 833 834 unittest 835 { 836 static struct Inner 837 { 838 mstring s; 839 } 840 841 static struct Outer 842 { 843 Contiguous!(Inner) inner; 844 } 845 846 auto s1 = Inner("abcd".dup); 847 Outer s2; 848 copy(s1, s2.inner); 849 850 Contiguous!(Outer) s3; 851 copy(s2, s3); 852 853 s3.ptr.inner.enforceIntegrity(); 854 test!("==")(s3.ptr.inner.ptr.s, "abcd"); 855 } 856 857 unittest 858 { 859 static struct S 860 { 861 mstring s; 862 } 863 864 auto s = S("abcd".dup); 865 866 Buffer!(void) src; 867 Contiguous!(S) dst; 868 869 Serializer.serialize(s, src); 870 Deserializer.deserialize(src[], dst); 871 872 test!("==")(dst.ptr.s, "abcd"); 873 } 874 875 /******************************************************************************* 876 877 Deserialise `serializer_output` in the trivial way to verify all dynamic 878 array slices are `null`. 879 880 *******************************************************************************/ 881 882 static const(Struct)* trivialDeserialize ( Struct ) 883 ( const(void)[] serializer_output ) 884 { 885 verify(serializer_output.length >= Struct.sizeof); 886 return cast(const(Struct)*)serializer_output.ptr; 887 } 888 889 /******************************************************************************* 890 891 Returns the number of bytes used to serialise `array`, recursing into the 892 elements of `array` if `Element` is a dynamic array type. No recursion is 893 done if `Element` is a value type containing dynamic arrays. 894 895 *******************************************************************************/ 896 897 static size_t serialArrayLength ( T : Element[], Element ) ( T array ) 898 { 899 size_t n = array.length.sizeof; 900 901 static if (is(Unqual!(Element) Sub == Sub[])) 902 { 903 foreach (element; array) 904 n += serialArrayLength(element); 905 } 906 else 907 n += (array.length * array[0].sizeof); 908 909 return n; 910 } 911 912 /******************************************************************************* 913 914 `testArray!(op)(a, b)` is equivalent to 915 `testArray!(op)(cast(const(void)[])a, cast(const(void)[])b)`, which allows 916 for `testArray!(op)(a, null)` avoiding D2 trouble with `typeof(null)`. 917 918 *******************************************************************************/ 919 920 template testArray ( istring op ) 921 { 922 alias test!(op, const(void)[], const(void)[]) testArray; 923 }