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 }