1 /****************************************************************************** 2 3 Home for binary contiguous Deserializer and utilities that use it. Check 4 documentation of `Deserializer` for more details. 5 6 Copyright: 7 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 8 All rights reserved. 9 10 License: 11 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 12 Alternatively, this file may be distributed under the terms of the Tango 13 3-Clause BSD License (see LICENSE_BSD.txt for details). 14 15 ******************************************************************************/ 16 17 module ocean.util.serialize.contiguous.Deserializer; 18 19 20 import ocean.meta.types.Qualifiers; 21 import ocean.core.Verify; 22 23 import ocean.util.serialize.contiguous.Contiguous; 24 25 import ocean.core.Exception; 26 import ocean.core.Enforce; 27 import ocean.core.Buffer; 28 import ocean.meta.traits.Indirections; 29 30 debug (DeserializationTrace) import ocean.io.Stdout; 31 32 version (unittest) import ocean.core.Test; 33 34 /******************************************************************************* 35 36 Indicates a problem during deserialization process, most often some sort 37 of data corruption 38 39 *******************************************************************************/ 40 41 class DeserializationException : Exception 42 { 43 mixin ReusableExceptionImplementation!(); 44 45 /********************************************************************** 46 47 Ensures length `len` does not exceed hard size limit `max` 48 49 Params: 50 S = type of the struct which is currently loaded 51 len = length of a dynamic array to deserialize 52 max = allowed maximum dynamic array length 53 file = file where size limit is enforced 54 line = line where size limit is enforced 55 56 Throws: 57 this instance if len is not at most max. 58 59 ***********************************************************************/ 60 61 void enforceSizeLimit ( S ) ( size_t len, size_t max, 62 istring file = __FILE__, int line = __LINE__ ) 63 { 64 if (len > max) 65 { 66 throw this 67 .set("", file, line) 68 .fmtAppend( 69 "Error deserializing '{}' : length {} exceeds limit {}", 70 S.stringof, 71 len, 72 max 73 ); 74 } 75 } 76 77 /********************************************************************** 78 79 Throws this instance if len is not at lest required. 80 81 Params: 82 S = type of the struct that is currently loaded 83 len = provided number of data bytes 84 required = required number of data bytes 85 file = file where size limit is enforced 86 line = line where size limit is enforced 87 88 Throws: 89 this instance if len is not at most max. 90 91 ***********************************************************************/ 92 93 void enforceInputSize ( S ) ( size_t len, size_t required, 94 istring file = __FILE__, int line = __LINE__ ) 95 { 96 if (len < required) 97 { 98 throw this 99 .set("", file, line) 100 .fmtAppend( 101 "Error deserializing '{}' : input data length {} < required {}", 102 S.stringof, 103 len, 104 required 105 ); 106 } 107 } 108 109 unittest 110 { 111 struct S { int i; } 112 113 auto e = new DeserializationException(); 114 try 115 { 116 e.enforceSizeLimit!(S)(1,2); 117 e.enforceSizeLimit!(S)(2,2); 118 e.enforceSizeLimit!(S)(3,2); 119 assert(false); 120 } 121 catch ( DeserializationException e ) 122 { 123 test(e.message() == "Error deserializing 'S' : length 3 exceeds limit 2"); 124 } 125 } 126 } 127 128 /******************************************************************************* 129 130 Binary deserializer that operates on Contiguous structs 131 132 It operates on data buffers generated by calling matching Serializer or ones 133 with an identicaly binary layout. Deserialization process is relatively 134 simple and requires iteration over the struct and updating array fields 135 to point to internal buffer slices. 136 137 The contents of dynamic arrays are stored in the buffer with the array 138 length prepended. For dynamic arrays of dynamic arrays that means that only 139 one single length field gets stored for each top-level array. 140 When such an array is encountered the Deserializer needs to extend the buffer 141 and put the expanded array slice (with all ppointers restored) to the end. 142 This process is called "array branching". 143 144 All deserialization methods that return a struct instance or a pointer 145 use in fact one of the argument data buffers as backing memory storage. 146 Modifying those directly will invalidate/corrupt your struct pointer. 147 148 Deserialized structures can be written to, as well as any referenced 149 arrays / structures. However, resizing arrays (i.e. appending) will cause 150 the buffer to be reallocated, causing the struct to no longer be contiguous. 151 As contiguity invariant is disabled by default that may result in undefined 152 behaviour. 153 154 For copying structures obtained via deserialization you must use 155 the `copy` function defined above in this module. 156 157 Example: 158 --- 159 struct Test { int a; int[] b; } 160 161 // in-place 162 void[] input = getFromExternalSource(); 163 Contiguous!(Test) s1 = Deserializer.deserialize(input); 164 assert(s1.ptr is input.ptr); 165 166 // don't modify original source 167 Contiguous!(Test) target; 168 Contiguous!(Test) s2 = Deserializer.deserialize(input, target); 169 assert(s2.ptr is target.ptr); 170 assert(s2.ptr !is input.ptr); 171 --- 172 173 *******************************************************************************/ 174 175 struct Deserializer 176 { 177 /************************************************************************** 178 179 Convenience shortcut 180 181 **************************************************************************/ 182 183 alias typeof(this) This; 184 185 /*************************************************************************** 186 187 Maximum allowed dynamic array length. 188 If any dynamic array is longer than this value, a DeserializationException 189 is thrown. 190 191 ***************************************************************************/ 192 193 public enum max_length = size_t.max; 194 195 /*************************************************************************** 196 197 Reused Exception instance 198 199 ***************************************************************************/ 200 201 private static DeserializationException e; 202 203 /************************************************************************** 204 205 Type alias definition 206 207 **************************************************************************/ 208 209 alias void[] delegate ( size_t len ) GetBufferDg; 210 211 /************************************************************************** 212 213 Initialization of static member fields 214 215 ***************************************************************************/ 216 217 static this() 218 { 219 This.e = new DeserializationException(); 220 } 221 222 /*************************************************************************** 223 224 Deserializes `src` in-place. If array branching is needed, length of 225 src will be increased to be able to store those. 226 227 Params: 228 S = struct type expected 229 src = data buffer previously created by Serializer 230 231 Returns: 232 src wrapped in Contiguous, ready to use data. Must not outlive 233 src origin. 234 235 ***************************************************************************/ 236 237 public static Contiguous!(S) deserialize ( S ) ( ref Buffer!(void) src ) 238 out (_s) 239 { 240 auto s = cast(Contiguous!(S)) _s; 241 242 assert (s.data.ptr is src[].ptr); 243 244 debug (DeserializationTrace) 245 { 246 Stdout.formatln("< deserialize!({})({}) : {}", S.stringof, 247 src[].ptr, s.ptr); 248 } 249 } 250 do 251 { 252 debug (DeserializationTrace) 253 { 254 Stdout.formatln("> deserialize!({})({})", S.stringof, src[].ptr); 255 nesting = 0; 256 scope (exit) nesting = 0; 257 } 258 size_t slices_len = 0, 259 data_len = This.countRequiredSize!(S)(src[], slices_len); 260 261 debug (DeserializationTrace) 262 Stdout.formatln(" data_len = {}, slices_len = {}", data_len, slices_len); 263 264 size_t total_length = data_len + slices_len; 265 266 if (src.length < total_length) 267 src.length = total_length; 268 269 verify(src.length >= data_len); 270 271 This.handleBranching!(S)( 272 src[0 .. data_len], 273 src[data_len .. total_length] 274 ); 275 276 return Contiguous!(S)(src[]); 277 } 278 279 /// ditto 280 public static Contiguous!(S) deserialize ( S ) ( ref void[] src ) 281 { 282 return deserialize!(S)(* cast(Buffer!(void)*) &src); 283 } 284 285 /*************************************************************************** 286 287 Identical to contiguous.Deserializer.deserialize but instead of 288 modifying input buffer copies the deserialized data to provided 289 Contiguous wrapper. 290 291 Params: 292 S = struct type `src` is assumed to contain 293 src = buffer previously created by Serializer, unchanged 294 dst = buffer to store deserialized data 295 296 Returns: 297 dst by value 298 299 ***************************************************************************/ 300 301 public static Contiguous!(S) deserialize ( S ) ( in void[] src, 302 ref Contiguous!(S) dst ) 303 out (_s) 304 { 305 auto s = cast(Contiguous!(S)) _s; 306 307 assert (s.data.ptr is dst.ptr); 308 309 debug (DeserializationTrace) 310 { 311 Stdout.formatln("< deserialize!({})({}, {}) : {}", S.stringof, 312 src.ptr, dst.ptr, s.ptr); 313 } 314 } 315 do 316 { 317 static assert (is(S == Unqual!(S)), 318 "Cannot deserialize qualified type : " ~ S.stringof); 319 320 debug (DeserializationTrace) 321 { 322 Stdout.formatln("> deserialize!({})({}, {})", S.stringof, 323 src.ptr, dst.ptr); 324 nesting = 0; 325 scope (exit) nesting = 0; 326 } 327 328 This.e.enforceInputSize!(S)(src.length, S.sizeof); 329 330 assumeSafeAppend(dst.data); 331 332 /* 333 * Calculate the number of bytes used in src, data_len, and the number 334 * of bytes required for branched array instances, slices_len. 335 * data_len is at least S.sizeof; src[0 .. S.sizeof] refers to the S 336 * instance while src[S.sizeof .. data_len] contains the serialised 337 * dynamic arrays. 338 * slices_len = 0 indicates that there are no branched arrays at all or 339 * none of non-zero size. data_len + slice_len is the minimum required 340 * size for dst. 341 */ 342 343 size_t slices_len = 0, 344 data_len = This.countRequiredSize!(S)(src, slices_len); 345 346 /* 347 * Resize dst and copy src[0 .. data_len] to dst[0 .. data_len]. 348 */ 349 size_t total_length = data_len + slices_len; 350 351 if (dst.data.length < total_length) 352 { 353 dst.data.length = total_length; 354 assumeSafeAppend(dst.data); 355 } 356 357 size_t end_copy = (src.length < total_length) ? src.length : total_length; 358 dst.data[0 .. end_copy] = src[0 .. end_copy]; 359 (cast(ubyte[]) dst.data)[end_copy .. $] = 0; 360 361 verify(dst.data.length >= data_len); 362 363 /* 364 * Adjust the dynamic array instances in dst to slice the data in the 365 * tail of dst, dst[S.sizeof .. data_len]. If S contains branched 366 * arrays of non-zero length, call get_slice_buffer() to obtain memory 367 * buffers for the dynamic array instances. 368 */ 369 370 This.handleBranching!(S)(dst.data[0 .. data_len], dst.data[data_len .. $]); 371 372 return dst; 373 } 374 375 /*************************************************************************** 376 377 Calculates total amount of bytes needed for an array to store 378 deserialized S instance. 379 380 Params: 381 S = struct type `instance` is assumed to contain 382 instance = serialized struct buffer to calculate data for 383 384 Returns: 385 required array size, total 386 387 ***************************************************************************/ 388 389 public static size_t countRequiredSize ( S ) ( in void[] instance ) 390 out(size) 391 { 392 debug (DeserializationTrace) 393 { 394 Stdout.formatln("{}< countRequiredSize!({})({}) : {}", tabs, 395 S.stringof, instance.ptr, size); 396 nesting--; 397 } 398 } 399 do 400 { 401 debug (DeserializationTrace) 402 { 403 nesting++; 404 Stdout.formatln("{}> countRequiredSize!({})({})", tabs, 405 S.stringof, instance.ptr); 406 } 407 408 size_t extra; 409 size_t base = This.countRequiredSize!(S)(instance, extra); 410 return extra + base; 411 } 412 413 /*************************************************************************** 414 415 Calculates extra amount of bytes needed for a buffer to store 416 deserialized branched arrays, as well as amount of bytes used by an 417 actual struct instance. 418 419 This is the private version used internally as public one does not 420 make that distinction and only return single size. 421 422 Params: 423 S = struct type 424 data = struct slice to calculate data for 425 extra_bytes = extra space needed for branched arrays, this number 426 is consequently accumulated through recursive calls to count* 427 methods (which is why it is not `out` parameter) 428 429 Returns: 430 sized used by S data (before braching) 431 432 ***************************************************************************/ 433 434 private static size_t countRequiredSize ( S ) ( in void[] data, 435 ref size_t extra_bytes ) 436 out (size) 437 { 438 assert (size >= S.sizeof); 439 440 debug (DeserializationTrace) 441 { 442 Stdout.formatln("{}< countRequiredSize!({})({}, {}) : {}", 443 tabs, 444 S.stringof, 445 data.ptr, 446 extra_bytes, 447 cast(size_t)size 448 ); 449 nesting--; 450 } 451 } 452 do 453 { 454 debug (DeserializationTrace) 455 { 456 nesting++; 457 Stdout.formatln("{}> countRequiredSize!({})({}, {})", 458 tabs, 459 S.stringof, 460 data.ptr, 461 extra_bytes 462 ); 463 } 464 465 This.e.enforceInputSize!(S)(data.length, S.sizeof); 466 467 // array data starts immediately after the struct value type 468 // members. Calculating size for those is in separate function 469 // to make recursive calls possible - first call needs to strip 470 // away S.sizeof part 471 472 return countStructArraySizes!(S)( data[S.sizeof .. $], extra_bytes ) + S.sizeof; 473 } 474 475 /*************************************************************************** 476 477 For a given struct type S calculates buffer size needed to store 478 its deserialized arrays including branched arrays. This method can 479 be called recursively for any nested struct type, given a proper 480 sub-slice 481 482 Params: 483 S = struct type 484 data = slice of a serialized struct where array dumps are 485 stored. This can also be any recursive array dump, not 486 just the very first one. 487 extra_bytes = extra space needed for branched arrays for these 488 arrays and any of member arrays (recursively) 489 490 Returns: 491 size used by arrays stored at data[0] 492 493 ***************************************************************************/ 494 495 private static size_t countStructArraySizes ( S ) ( in void[] data, 496 ref size_t extra_bytes ) 497 out (size) 498 { 499 debug (DeserializationTrace) 500 { 501 Stdout.formatln("{}< countStructArraySizes!({})({}, {}) : {}", 502 tabs, 503 S.stringof, 504 data.ptr, 505 extra_bytes, 506 cast(size_t)size 507 ); 508 nesting--; 509 } 510 } 511 do 512 { 513 debug (DeserializationTrace) 514 { 515 nesting++; 516 Stdout.formatln("{}> countStructArraySizes!({})({}, {})", 517 tabs, 518 S.stringof, 519 data.ptr, 520 extra_bytes 521 ); 522 } 523 524 size_t pos = 0; 525 526 foreach (i, QualField; typeof (S.tupleof)) 527 { 528 alias StripQualifier!(QualField) Field; 529 530 static if (is (Field == struct)) 531 { 532 This.e.enforceInputSize!(S)(data.length, pos); 533 534 pos += This.countStructArraySizes!(Field)(data[pos .. $], extra_bytes); 535 } 536 else static if (is (Field QualElement : QualElement[])) 537 { 538 alias StripQualifier!(QualElement) Element; 539 This.e.enforceInputSize!(S)(data.length, pos); 540 541 static if (is (QualElement[] == Field)) 542 { 543 // dynamic array 544 pos += This.countDynamicArraySize!(Element)( 545 data[pos .. $], extra_bytes); 546 } 547 else static if (hasIndirections!(Element)) 548 { 549 // static array with indirections 550 pos += This.countArraySize!(Element)( 551 Field.length, data[pos .. $], extra_bytes); 552 } 553 } 554 } 555 556 return pos; 557 } 558 559 /*************************************************************************** 560 561 Calculates number of bytes needed to deserialize an array. 562 Additionally calculates number of extra bytes needed to store 563 deserialized branched arrays (as per `sliceArray()` algorithm). 564 565 Takes into consideration dynamic array serialization format (length 566 word stored first) 567 568 If there are no branched arrays available via T, amount of extra 569 bytes is always expected to be 0 570 571 Params: 572 T = array element type 573 data = slice of serialized buffer where array data starts 574 extra_bytes = incremented by the number of bytes required to 575 store branched arrays 576 577 Returns: 578 number of bytes used in data 579 580 ***************************************************************************/ 581 582 private static size_t countDynamicArraySize ( T ) ( in void[] data, 583 ref size_t extra_bytes ) 584 out(size) 585 { 586 debug (DeserializationTrace) 587 { 588 Stdout.formatln("{}< countDynamicArraySize!({})({}, {}) : {}", tabs, 589 T.stringof, data.ptr, extra_bytes, cast(size_t)size); 590 nesting--; 591 } 592 } 593 do 594 { 595 debug (DeserializationTrace) 596 { 597 nesting++; 598 Stdout.formatln("{}> countDynamicArraySize!({})({}, {})", tabs, 599 T.stringof, data.ptr, extra_bytes); 600 } 601 602 This.e.enforceInputSize!(T)(data.length, size_t.sizeof); 603 604 /* 605 * Obtain the array length from data, calculate the number of bytes of 606 * the array and define the reading position in data. 607 */ 608 609 size_t len = *cast (size_t*) data[0 .. size_t.sizeof].ptr, 610 bytes = len * T.sizeof, 611 pos = len.sizeof; 612 613 debug (DeserializationTrace) 614 { 615 Stdout.formatln("{} len = {}, bytes = {}, pos = {}", tabs, len, bytes, pos); 616 } 617 618 This.e.enforceSizeLimit!(T[])(len, This.max_length); 619 620 static if (is (StripQualifier!(T) Element == Element[])) 621 { 622 /* 623 * If array is an array of slices (dynamic arrays), obtain a data 624 * buffer for these slices. 625 */ 626 debug (DeserializationTrace) 627 { 628 Stdout.formatln("{} need {} more bytes for branched arrays", tabs, bytes); 629 } 630 631 extra_bytes += bytes; 632 } 633 else 634 { 635 /* 636 * array is an array of values so the values will follow in data. 637 */ 638 639 debug (DeserializationTrace) 640 { 641 Stdout.formatln("{} pos += {}", tabs, bytes); 642 } 643 644 pos += bytes; 645 646 This.e.enforceInputSize!(T[])(data.length, pos); 647 } 648 649 static if (!hasIndirections!(T)) 650 { 651 return pos; 652 } 653 else 654 { 655 return pos + This.countArraySize!(T)(len, 656 data[pos .. $], extra_bytes); 657 } 658 } 659 660 /*************************************************************************** 661 662 Recursively calculates data taken by all arrays available through T, 663 applicable both to static and dynamic arrays. 664 665 If no branched arrays are transitively stored in T, `extra_bytes` is not 666 supposed to be incremeneted 667 668 Params: 669 T = array element type 670 len = array length 671 data = data of top-most array elements 672 bytes = incremented by the number of bytes required by 673 sliceSubArrays() 674 675 Returns: 676 number of bytes used in data 677 678 ***************************************************************************/ 679 680 private static size_t countArraySize ( T ) ( size_t len, in void[] data, 681 ref size_t extra_bytes ) 682 out(size) 683 { 684 debug (DeserializationTrace) 685 { 686 Stdout.formatln("{}< countArraySize!({})({}, {}, {}) : {}", tabs, 687 T.stringof, len, data.ptr, extra_bytes, cast(size_t)size); 688 nesting--; 689 } 690 } 691 do 692 { 693 debug (DeserializationTrace) 694 { 695 nesting++; 696 Stdout.formatln("{}> countArraySize!({})({}, {}, {})", tabs, 697 T.stringof, len, data.ptr, extra_bytes); 698 } 699 700 size_t pos = 0; 701 702 static if (is (T == struct)) 703 { 704 for (size_t i = 0; i < len; i++) 705 { 706 This.e.enforceInputSize!(T[])(data.length, pos, __FILE__, __LINE__); 707 708 pos += This.countStructArraySizes!(T)(data[pos .. $], extra_bytes); 709 } 710 } 711 else static if (is (T Element : Element[])) 712 { 713 for (size_t i = 0; i < len; i++) 714 { 715 This.e.enforceInputSize!(T[])(data.length, pos); 716 717 static if (is (Element[] == StripQualifier!(T))) 718 { 719 pos += This.countDynamicArraySize!(Element)( 720 data[pos .. $], extra_bytes); 721 } 722 else 723 { 724 pos += This.countArraySize!(Element)( 725 T.length, data[pos .. $], extra_bytes); 726 } 727 } 728 } 729 else 730 { 731 pragma (msg, "Warning: type \"", T, "\" contains no array, " ~ 732 "ignoring it. This indicates a bug, please report " ~ 733 "that this warning was triggered @", 734 __FILE__ , ":", __LINE__); 735 } 736 737 return pos; 738 } 739 740 /*************************************************************************** 741 742 Deserialization implementation that takes care of branched arrays by 743 putting those into dedicated memory slice. This target slice is never 744 resized so it is legal to use sub-slice of original buffer (unused 745 memory part) as `slices_buffer` argument. 746 747 Params: 748 S = struct type to deserialize 749 src = buffer that is expected to contain serialized S 750 slices_buffer = place to store expanded branched array slices 751 752 Returns: 753 src slice for deserialized data, branched part not included (as it 754 may be in a different memory chunk). Adjust it to also include 755 branched array if necessary at the call site 756 757 ***************************************************************************/ 758 759 private static void[] handleBranching ( S ) ( void[] src, 760 void[] slices_buffer ) 761 out (data) 762 { 763 assert (data.ptr is src.ptr); 764 assert (data.length <= src.length); 765 766 debug (DeserializationTrace) 767 { 768 Stdout.formatln("{}< handleBranching!({})({}, {}) : {}", tabs, 769 S.stringof, src.ptr, slices_buffer.ptr, data.ptr); 770 nesting--; 771 } 772 } 773 do 774 { 775 debug (DeserializationTrace) 776 { 777 nesting++; 778 Stdout.formatln("{}> handleBranching!({})({}, {})", tabs, 779 S.stringof, src.ptr, slices_buffer.ptr); 780 } 781 782 /* 783 * Adjust the dynamic array instances in src to slice the data in the 784 * tail of src, src[S.sizeof .. $]. If S contains branched 785 * arrays of non-zero length, slices_buffer gets used to store that data 786 */ 787 788 size_t arrays_length = This.sliceArrays( 789 *cast (S*) src[0 .. src.length].ptr, 790 src[S.sizeof .. $], slices_buffer 791 ); 792 793 return src[0 .. arrays_length + S.sizeof]; 794 } 795 796 /*************************************************************************** 797 798 Sets all dynamic array members of s to slice the corresponding sections 799 of data. data must be a concatenated sequence of chunks generated by 800 transmitArray() for each dynamic array member of S. 801 802 Params: 803 s = struct instance to set arrays to slice data 804 data = array data to slice 805 slices_buffer = place to put branched array slices 806 807 Returns: 808 number of data bytes sliced 809 810 Throws: 811 DeserializationException if data is too short 812 813 ***************************************************************************/ 814 815 private static size_t sliceArrays ( S ) ( ref S s, void[] data, 816 ref void[] slices_buffer ) 817 out 818 { 819 debug (DeserializationTrace) 820 { 821 Stdout.formatln("{}< sliceArrays!({})({}, {}, {}) : {}", tabs, 822 S.stringof, &s, data.ptr, slices_buffer.ptr, data.ptr); 823 nesting--; 824 } 825 } 826 do 827 { 828 verify(slices_buffer !is null); 829 830 debug (DeserializationTrace) 831 { 832 nesting++; 833 Stdout.formatln("{}> sliceArrays!({})({}, {}, {})", tabs, 834 S.stringof, &s, data.ptr, slices_buffer.ptr); 835 } 836 837 size_t pos = 0; 838 839 foreach (i, ref field; s.tupleof) 840 { 841 alias StripQualifier!(typeof(field)) Field; 842 auto pfield = cast(Field*) &field; 843 844 static if (is (Field == struct)) 845 { 846 This.e.enforceInputSize!(S)(data.length, pos); 847 848 pos += This.sliceArrays(*pfield, data[pos .. $], slices_buffer); 849 } 850 else static if (is (Field Element : Element[])) 851 { 852 This.e.enforceInputSize!(S)(data.length, pos); 853 854 static if (is (Element[] == Field)) 855 { 856 auto increment = This.sliceArray(*pfield, data[pos .. $], 857 slices_buffer); 858 859 // if host struct is Contiguous, internal `data` array 860 // needs to be deserialized as a struct to ensure that 861 // internal pointers are updated and it stays contiguous 862 static if (is(S T == Contiguous!(T))) 863 { 864 static assert (is(Element == void)); 865 auto orig_length = field.length; 866 deserialize!(T)(*pfield); 867 verify (orig_length == field.length); 868 } 869 870 pos += increment; 871 } 872 else static if (hasIndirections!(Element)) 873 { 874 pos += This.sliceSubArrays(*pfield, data[pos .. $], slices_buffer); 875 } 876 } 877 } 878 879 return pos; 880 } 881 882 /*************************************************************************** 883 884 Creates an array slice to data. Data must start with a size_t value 885 reflecting the byte length, followed by the array content data. 886 887 Params: 888 array = resulting array 889 data = array data to slice 890 slices_buffer = place to store branched array slices if any 891 892 Returns: 893 number of data bytes sliced 894 895 Throws: 896 DeserializationException if data is too short 897 898 ***************************************************************************/ 899 900 private static size_t sliceArray ( T ) ( out T[] array, void[] data, 901 ref void[] slices_buffer ) 902 out(size) 903 { 904 debug (DeserializationTrace) 905 { 906 Stdout.formatln("{}< sliceArray!({})({}, {}, {}) : {}", tabs, 907 T.stringof, array.ptr, data.ptr, slices_buffer.ptr, cast(size_t)size); 908 nesting--; 909 } 910 } 911 do 912 { 913 verify (slices_buffer !is null); 914 915 debug (DeserializationTrace) 916 { 917 nesting++; 918 Stdout.formatln("{}> sliceArray!({})({}, {}, {})", tabs, 919 T.stringof, array.ptr, data.ptr, slices_buffer.ptr); 920 } 921 922 This.e.enforceInputSize!(T[])(data.length, size_t.sizeof); 923 924 /* 925 * Obtain the array length from data, calculate the number of bytes of 926 * the array and define the reading position in data. 927 */ 928 929 size_t len = *cast (size_t*) data[0 .. size_t.sizeof].ptr, 930 bytes = len * T.sizeof, 931 pos = len.sizeof; 932 933 debug (DeserializationTrace) 934 { 935 Stdout.formatln("{} len = {}, bytes = {}, pos = {}", tabs, len, bytes, pos); 936 } 937 938 This.e.enforceSizeLimit!(T[])(len, This.max_length); 939 940 static if (is (StripQualifier!(T) Element == Element[])) 941 { 942 /* 943 * If array is an array of slices (dynamic arrays), obtain a data 944 * buffer for these slices. 945 */ 946 947 debug (DeserializationTrace) 948 { 949 Stdout.formatln("{} obtaining buffer for branched arrays from slice {}", 950 tabs, slices_buffer.ptr); 951 } 952 953 This.e.enforceInputSize!(T[])(slices_buffer.length, bytes); 954 array = cast (T[]) slices_buffer[0 .. bytes]; 955 slices_buffer = slices_buffer[bytes .. $]; 956 } 957 else 958 { 959 /* 960 * array is an array of values so the values will follow in data. 961 */ 962 963 pos += bytes; 964 965 This.e.enforceInputSize!(T[])(data.length, pos); 966 967 array = cast (T[]) data[len.sizeof .. pos]; 968 } 969 970 verify (array.length == len); 971 972 static if (!hasIndirections!(T)) 973 { 974 return pos; 975 } 976 else 977 { 978 /* 979 * If array is an array of a non-primitive type, recurse into the 980 * array elements. 981 */ 982 983 debug (DeserializationTrace) 984 { 985 Stdout.formatln("{} writing branched array elements to {}", tabs, array.ptr); 986 } 987 988 return pos + This.sliceSubArrays(array, data[pos .. $], slices_buffer); 989 } 990 } 991 992 /*************************************************************************** 993 994 Sets the elements of array to slice the corresponding parts of data if 995 T contains a dynamic array. 996 997 Params: 998 array = array to adjust elements 999 data = array data to slice 1000 slices_buffer = slice to store branched array data to 1001 1002 Returns: 1003 number of data bytes sliced 1004 1005 Throws: 1006 DeserializationException if data is too short 1007 1008 ***************************************************************************/ 1009 1010 private static size_t sliceSubArrays ( QualT ) 1011 ( QualT[] array, void[] data, ref void[] slices_buffer ) 1012 out(size) 1013 { 1014 debug (DeserializationTrace) 1015 { 1016 Stdout.formatln("{}< sliceSubArrays!({})({} @{}, {}, {}) : {}", tabs, 1017 QualT.stringof, array.length, array.ptr, data.ptr, 1018 slices_buffer.ptr, cast(size_t)size); 1019 nesting--; 1020 } 1021 } 1022 do 1023 { 1024 verify (slices_buffer !is null); 1025 1026 debug (DeserializationTrace) 1027 { 1028 nesting++; 1029 Stdout.formatln("{}> sliceSubArrays!({})({} @{}, {}, {})", tabs, 1030 QualT.stringof, array.length, array.ptr, data.ptr, slices_buffer.ptr); 1031 } 1032 1033 size_t pos = 0; 1034 1035 alias StripQualifier!(QualT) T; 1036 1037 static if (is (T == struct)) 1038 { 1039 foreach (ref element; array) 1040 { 1041 This.e.enforceInputSize!(T[])(data.length, pos); 1042 1043 pos += This.sliceArrays(element, data[pos .. $], slices_buffer); 1044 } 1045 } 1046 else static if (is (T Element : Element[])) 1047 { 1048 1049 foreach (ref element; array) 1050 { 1051 This.e.enforceInputSize!(T[])(data.length, pos); 1052 auto pelement = cast(T*) &element; 1053 1054 static if (is (Element[] == T)) 1055 { 1056 pos += This.sliceArray(*pelement, data[pos .. $], slices_buffer); 1057 } 1058 else static if (hasIndirections!(Element)) 1059 { 1060 pos += This.sliceSubArrays(*pelement, data[pos .. $], slices_buffer); 1061 } 1062 } 1063 } 1064 1065 return pos; 1066 } 1067 1068 /************************************************************************** 1069 1070 `static assert`s that `T` has no qualifier. 1071 1072 **************************************************************************/ 1073 1074 template StripQualifier ( T ) 1075 { 1076 static assert (!is(T == immutable)); 1077 alias Unqual!(T) StripQualifier; 1078 } 1079 1080 /**************************************************************************/ 1081 1082 debug (DeserializationTrace) private static 1083 { 1084 char[20] tabs_ = '\t'; 1085 uint nesting = 0; 1086 cstring tabs ( ) { return tabs_[0 .. nesting]; } 1087 } 1088 } 1089 1090 unittest 1091 { 1092 // only instantiation, check package_test.d for actual tests 1093 1094 struct Trivial 1095 { 1096 int a,b; 1097 } 1098 1099 void[] buf = cast(void[]) [ 42, 43 ]; 1100 auto r1 = Deserializer.deserialize!(Trivial)(buf); 1101 1102 Contiguous!(Trivial) copy_dst; 1103 auto r2 = Deserializer.deserialize(buf, copy_dst); 1104 }