1 module msgpack.unpacker;
2 
3 import msgpack.common;
4 import msgpack.attribute;
5 import msgpack.exception;
6 
7 import std.array;
8 import std.exception;
9 import std.range;
10 import std.stdio;
11 import std.traits;
12 import std.typecons;
13 import std.typetuple;
14 import std.container;
15 
16 
17 // for unpack without calling constructor
18 private extern(C) Object _d_newclass(in ClassInfo);
19 
20 
21 /**
22  * This $(D Unpacker) is a $(D MessagePack) direct-conversion deserializer
23  *
24  * This implementation is suitable for fixed data.
25  *
26  * Example:
27  * -----
28  * // serializedData is [10, 0.1, false]
29  * auto unpacker = Unpacker(serializedData);
30  *
31  * uint   n;
32  * double d;
33  * bool   b;
34  *
35  * unpacker.unpackArray(n, d, b);
36  *
37  * // using Tuple
38  * Tuple!(uint, double, bool) record;
39  * unpacker.unpack(record);  // record is [10, 0.1, false]
40  * -----
41  *
42  * NOTE:
43  *  Unpacker becomes template struct if Phobos supports truly IO module.
44  */
45 struct Unpacker
46 {
47   private:
48     static @system
49     {
50         alias void delegate(ref Unpacker, void*) UnpackHandler;
51         UnpackHandler[TypeInfo] unpackHandlers;
52 
53         public void registerHandler(T, alias Handler)()
54         {
55             unpackHandlers[typeid(T)] = delegate(ref Unpacker unpacker, void* obj) {
56                 Handler(unpacker, *cast(T*)obj);
57             };
58         }
59 
60         public void register(T)()
61         {
62             unpackHandlers[typeid(T)] = delegate(ref Unpacker unpacker, void* obj) {
63                 unpacker.unpackObject(*cast(T*)obj);
64             };
65         }
66 
67     }
68 
69     enum Offset = 1;
70 
71     mixin InternalBuffer;
72 
73     bool withFieldName_;
74 
75 
76   public:
77     /**
78      * Constructs a $(D Unpacker).
79      *
80      * Params:
81      *  target     = byte buffer to deserialize
82      *  bufferSize = size limit of buffer size
83      */
84     this(in ubyte[] target, in size_t bufferSize = 8192, bool withFieldName = false)
85     {
86         initializeBuffer(target, bufferSize);
87         withFieldName_ = withFieldName;
88     }
89 
90 
91     /**
92      * Clears states for next deserialization.
93      */
94     @safe
95     nothrow void clear()
96     {
97         used_ = offset_ = parsed_ = 0;
98         hasRaw_ = false;
99     }
100 
101 
102     /**
103      * Deserializes $(D_PARAM T) object and assigns to $(D_PARAM value).
104      *
105      * If the argument is pointer, dereferences pointer and assigns deserialized value.
106      * -----
107      * int* a;
108      * unpacker.unpack(a)  // enforce throws Exception because a is null or
109      *                     // no throw if deserialized value is nil
110      *
111      * int b; a = &b;
112      * unpacker.unpack(b)  // b is deserialized value or
113      *                     // assigns null if deserialized value is nil
114      * -----
115      *
116      * Params:
117      *  value = the reference of value to assign.
118      *
119      * Returns:
120      *  self, i.e. for method chaining.
121      *
122      * Throws:
123      *  UnpackException when doesn't read from buffer or precision loss occurs and
124      *  MessagePackException when $(D_PARAM T) type doesn't match serialized type.
125      */
126     ref Unpacker unpack(T)(ref T value) if (is(Unqual!T == bool))
127     {
128         canRead(Offset, 0);
129         const header = read();
130 
131         switch (header) {
132         case Format.TRUE:
133             value = true;
134             break;
135         case Format.FALSE:
136             value = false;
137             break;
138         default:
139             rollback(0, "bool", cast(Format)header);
140         }
141 
142         return this;
143     }
144 
145 
146     /// ditto
147     ref Unpacker unpack(T)(ref T value) if (isUnsigned!T && !is(Unqual!T == enum))
148     {
149         canRead(Offset, 0);
150         const header = read();
151 
152         if (0x00 <= header && header <= 0x7f) {
153             value = header;
154         } else {
155             switch (header) {
156             case Format.UINT8:
157                 canRead(ubyte.sizeof);
158                 value = read();
159                 break;
160             case Format.UINT16:
161                 canRead(ushort.sizeof);
162                 auto us = load16To!ushort(read(ushort.sizeof));
163                 if (us > T.max)
164                     rollback(ushort.sizeof, T.stringof, Format.UINT16);
165                 value = cast(T)us;
166                 break;
167             case Format.UINT32:
168                 canRead(uint.sizeof);
169                 auto ui = load32To!uint(read(uint.sizeof));
170                 if (ui > T.max)
171                     rollback(uint.sizeof, T.stringof, Format.UINT32);
172                 value = cast(T)ui;
173                 break;
174             case Format.UINT64:
175                 canRead(ulong.sizeof);
176                 auto ul = load64To!ulong(read(ulong.sizeof));
177                 if (ul > T.max)
178                     rollback(ulong.sizeof, T.stringof, Format.UINT64);
179                 value = cast(T)ul;
180                 break;
181             default:
182                 rollback(0, T.stringof, cast(Format)header);
183             }
184         }
185 
186         return this;
187     }
188 
189 
190     /// ditto
191     ref Unpacker unpack(T)(ref T value) if (isSigned!T && isIntegral!T && !is(Unqual!T == enum))
192     {
193         canRead(Offset, 0);
194         const header = read();
195 
196         if (0x00 <= header && header <= 0x7f) {
197             value = cast(T)header;
198         } else if (0xe0 <= header && header <= 0xff) {
199             auto b = cast(T)(cast(ubyte)(-int(header)));
200             static if(T.sizeof < int.sizeof)
201                 value = cast(T)(-int(b));
202             else
203                 value = -b;
204         } else {
205             switch (header) {
206             case Format.UINT8:
207                 canRead(ubyte.sizeof);
208                 auto ub = read();
209                 if (ub > T.max)
210                     rollback(ubyte.sizeof, T.stringof, Format.UINT8);
211                 value = cast(T)ub;
212                 break;
213             case Format.UINT16:
214                 canRead(ushort.sizeof);
215                 auto us = load16To!ushort(read(ushort.sizeof));
216                 if (us > T.max)
217                     rollback(ushort.sizeof, T.stringof, Format.UINT16);
218                 value = cast(T)us;
219                 break;
220             case Format.UINT32:
221                 canRead(uint.sizeof);
222                 auto ui = load32To!uint(read(uint.sizeof));
223                 if (ui > T.max)
224                     rollback(uint.sizeof, T.stringof, Format.UINT32);
225                 value = cast(T)ui;
226                 break;
227             case Format.UINT64:
228                 canRead(ulong.sizeof);
229                 auto ul = load64To!ulong(read(ulong.sizeof));
230                 if (ul > T.max)
231                     rollback(ulong.sizeof, T.stringof, Format.UINT64);
232                 value = cast(T)ul;
233                 break;
234             case Format.INT8:
235                 canRead(byte.sizeof);
236                 value = cast(byte)read();
237                 break;
238             case Format.INT16:
239                 canRead(short.sizeof);
240                 auto s = load16To!short(read(short.sizeof));
241                 if (s < T.min || T.max < s)
242                     rollback(short.sizeof, T.stringof, Format.INT16);
243                 value = cast(T)s;
244                 break;
245             case Format.INT32:
246                 canRead(int.sizeof);
247                 auto i = load32To!int(read(int.sizeof));
248                 if (i < T.min || T.max < i)
249                     rollback(int.sizeof, T.stringof, Format.INT32);
250                 value = cast(T)i;
251                 break;
252             case Format.INT64:
253                 canRead(long.sizeof);
254                 auto l = load64To!long(read(long.sizeof));
255                 if (l < T.min || T.max < l)
256                     rollback(long.sizeof, T.stringof, Format.INT64);
257                 value = cast(T)l;
258                 break;
259             default:
260                 rollback(0, T.stringof, cast(Format)header);
261             }
262         }
263 
264         return this;
265     }
266 
267 
268     /// ditto
269     ref Unpacker unpack(T)(ref T value) if (isSomeChar!T && !is(Unqual!T == enum))
270     {
271         static if (is(Unqual!T == char)) {
272             ubyte tmp;
273         } else static if (is(Unqual!T == wchar)) {
274             ushort tmp;
275         } else static if (is(Unqual!T == dchar)) {
276             uint tmp;
277         }
278         unpack(tmp);
279         value = cast(T)(tmp);
280         return this;
281     }
282 
283 
284     /// ditto
285     ref Unpacker unpack(T)(ref T value) if (isFloatingPoint!T && !is(Unqual!T == enum))
286     {
287         canRead(Offset, 0);
288         const header = read();
289 
290         switch (header) {
291         case Format.FLOAT:
292             _f temp;
293 
294             canRead(uint.sizeof);
295             temp.i = load32To!uint(read(uint.sizeof));
296             value  = temp.f;
297             break;
298         case Format.DOUBLE:
299             // check precision loss
300             static if (is(Unqual!T == float))
301                 rollback(0, T.stringof, Format.DOUBLE);
302 
303             _d temp;
304 
305             canRead(ulong.sizeof);
306             temp.i = load64To!ulong(read(ulong.sizeof));
307             value  = temp.f;
308             break;
309         case Format.REAL:
310             static if (!EnableReal)
311             {
312                 rollback(0, "real is disabled", Format.REAL);
313             }
314             else
315             {
316                 // check precision loss
317                 static if (is(Unqual!T == float) || is(Unqual!T == double))
318                     rollback(0, T.stringof, Format.REAL);
319 
320                 canRead(RealSize);
321 
322                 version (NonX86)
323                 {
324                     CustomFloat!80 temp;
325 
326                     const frac = load64To!ulong (read(ulong.sizeof));
327                     const exp  = load16To!ushort(read(ushort.sizeof));
328 
329                     temp.significand = frac;
330                     temp.exponent    = exp & 0x7fff;
331                     temp.sign        = exp & 0x8000 ? true : false;
332 
333                     // NOTE: temp.get!real is inf on non-x86 when deserialized value is larger than double.max.
334                     value = temp.get!real;
335                 }
336                 else
337                 {
338                     _r temp;
339 
340                     temp.fraction = load64To!(typeof(temp.fraction))(read(temp.fraction.sizeof));
341                     temp.exponent = load16To!(typeof(temp.exponent))(read(temp.exponent.sizeof));
342 
343                     value = temp.f;
344                 }
345             }
346 
347             break;
348         default:
349             rollback(0, T.stringof, cast(Format)header);
350         }
351 
352         return this;
353     }
354 
355 
356     /// ditto
357     ref Unpacker unpack(T)(ref T value) if (is(Unqual!T == enum))
358     {
359         OriginalType!T temp;
360 
361         unpack(temp);
362 
363         value = cast(T)temp;
364 
365         return this;
366     }
367 
368 
369     /// ditto
370     ref Unpacker unpack(T)(T value) if (isPointer!T)
371     {
372         static if (is(Unqual!T == void*)) {
373             enforce(value !is null,  "Can't deserialize void type");
374             unpackNil(value);
375         } else {
376             if (checkNil())
377                 unpackNil(value);
378             else
379                 enforce(value !is null, T.stringof ~ " is null pointer");
380 
381             unpack(mixin(AsteriskOf!T ~ "value"));
382         }
383 
384         return this;
385     }
386 
387 
388     /// ditto
389     ref Unpacker unpack(T)(ref T value) if (is(T == ExtValue))
390     {
391         canRead(Offset, 0);
392         const header = read();
393 
394         // Fixed
395         if (header >= Format.EXT && header <= Format.EXT + 4)
396         {
397             const length = 2^^(header - Format.EXT);
398             canRead(1 + length);
399 
400             value.type = read();
401             value.data = read(length);
402             return this;
403         }
404 
405         // Dynamic length
406         uint length;
407         switch (header) with (Format)
408         {
409             case EXT8:
410                 canRead(1);
411                 length = read();
412                 break;
413             case EXT16:
414                 canRead(2);
415                 length = load16To!ushort(read(2));
416                 break;
417             case EXT32:
418                 canRead(4);
419                 length = load32To!uint(read(4));
420                 break;
421             default:
422                 rollback(0, T.stringof, cast(Format)header);
423         }
424 
425         canRead(1 + length);
426         value.type = read();
427         value.data = read(length);
428 
429         return this;
430     }
431 
432 
433     /// ditto
434     ref Unpacker unpack(Types...)(ref Types objects) if (Types.length > 1)
435     {
436         foreach (i, T; Types)
437             unpack!(T)(objects[i]);
438 
439         return this;
440     }
441 
442 
443     /**
444      * Deserializes $(D_PARAM T) object and assigns to $(D_PARAM array).
445      *
446      * This is convenient method for array deserialization.
447      * Rollback will be completely successful if you deserialize raw type((u)byte[] or string types).
448      * But, Rollback will be one element(e.g. int) if you deserialize other types(e.g. int[], int[int])
449      *
450      * No assign if the length of deserialized object is 0.
451      *
452      * In a static array, this method checks the length. Do rollback and throw exception
453      * if length of $(D_PARAM array) is different from length of deserialized object.
454      *
455      * Params:
456      *  array = the reference of array to assign.
457      *
458      * Returns:
459      *  self, i.e. for method chaining.
460      *
461      * Throws:
462      *  UnpackException when doesn't read from buffer or precision loss occurs and
463      *  MessagePackException when $(D_PARAM T) type doesn't match serialized type.
464      */
465     ref Unpacker unpack(T)(ref T array) if ((isArray!T ||
466                                              isInstanceOf!(Array, T)) &&
467                                             !is(Unqual!T == enum))
468     {
469         alias typeof(T.init[0]) U;
470 
471         /*
472          * Deserializes type-information of raw type.
473          */
474         @safe
475         size_t beginRaw()
476         {
477             canRead(Offset, 0);
478             const  header = read();
479             size_t length;
480 
481             if (0xa0 <= header && header <= 0xbf) {
482                 length = header & 0x1f;
483             } else {
484                 switch (header) {
485                 case Format.BIN8, Format.STR8:
486                     canRead(ubyte.sizeof);
487                     length = read();
488                     break;
489                 case Format.BIN16, Format.RAW16:
490                     canRead(ushort.sizeof);
491                     length = load16To!size_t(read(ushort.sizeof));
492                     break;
493                 case Format.BIN32, Format.RAW32:
494                     canRead(uint.sizeof);
495                     length = load32To!size_t(read(uint.sizeof));
496                     break;
497                 case Format.NIL:
498                     break;
499                 default:
500                     rollback(0, T.stringof, cast(Format)header);
501                 }
502             }
503 
504             return length;
505         }
506 
507 
508         if (checkNil()) {
509             static if (isStaticArray!T) {
510                 onInvalidType("static array", Format.NIL);
511             } else {
512                 return unpackNil(array);
513             }
514         }
515 
516         // Raw bytes
517         static if (isByte!U || isSomeChar!U)
518             auto length = beginRaw();
519         else
520             auto length = beginArray();
521 
522         if(length > buffer_.length) {
523             import std.conv: text;
524             throw new MessagePackException(text("Invalid array size in byte stream: Length (", length,
525                                                 ") is larger than internal buffer size (", buffer_.length, ")"));
526         }
527 
528         // Raw bytes
529         static if (isByte!U || isSomeChar!U) {
530             auto offset = calculateSize!(true)(length);
531             if (length == 0)
532                 return this;
533 
534             static if (isStaticArray!T) {
535                 if (length != array.length)
536                     rollback(offset, "static array was given but the length is mismatched");
537             }
538 
539             canRead(length, offset + Offset);
540             static if (isStaticArray!T) {
541                 array[] = (cast(U[])read(length))[0 .. T.length];
542             } else {
543                 array = cast(T)read(length);
544             }
545 
546             static if (isDynamicArray!T)
547                 hasRaw_ = true;
548         } else {
549             if (length == 0)
550                 return this;
551 
552             static if (isStaticArray!T) {
553                 if (length != array.length)
554                     rollback(calculateSize(length), "static array was given but the length is mismatched");
555             } else {
556                 array.length = length;
557             }
558 
559             foreach (i; 0..length)
560                 unpack(array[i]);
561         }
562 
563         return this;
564     }
565 
566 
567     /// ditto
568     ref Unpacker unpack(T)(ref T array) if (isAssociativeArray!T)
569     {
570         alias typeof(T.init.keys[0])   K;
571         alias typeof(T.init.values[0]) V;
572 
573         if (checkNil())
574             return unpackNil(array);
575 
576         auto length = beginMap();
577         if (length == 0)
578             return this;
579 
580         foreach (i; 0..length) {
581             K k; unpack(k);
582             V v; unpack(v);
583             array[k] = v;
584         }
585 
586         return this;
587     }
588 
589     /**
590      * Deserializes $(D_PARAM T) object and assigns to $(D_PARAM object).
591      *
592      * Calling $(D fromMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D fromMsgpack) method. $(D fromMsgpack) signature is:
593      * -----
594      * void fromMsgpack(ref Unpacker unpacker)
595      * -----
596      * Assumes $(D std.typecons.Tuple) or simple struct if $(D_KEYWORD struct) doesn't implement $(D fromMsgpack).
597      * Checks length if $(D_PARAM T) is a $(D std.typecons.Tuple) or simple struct.
598      *
599      * Params:
600      *  object = the reference of object to assign.
601      *  args   = the arguments to class constructor(class only).
602      *           This is used at new statement if $(D_PARAM object) is $(D_KEYWORD null).
603      *
604      * Returns:
605      *  self, i.e. for method chaining.
606      */
607     ref Unpacker unpack(T, Args...)(ref T object, auto ref Args args) if (is(Unqual!T == class))
608     {
609         if (checkNil())
610             return unpackNil(object);
611 
612         if (object is null) {
613             static if (Args.length == 0) {
614                 static if (__traits(compiles, { new T(); }))
615                     object = new T();
616                 else
617                     object = cast(T)_d_newclass(T.classinfo);
618             } else static if (__traits(compiles, { new T(args); })) {
619                 object = new T(args);
620             } else {
621                 throw new MessagePackException("Don't know how to construct class type '" ~ Unqual!T.stringof ~ "' with argument types '" ~ Args.stringof ~ "'.");
622             }
623         }
624 
625         static if (hasMember!(T, "fromMsgpack"))
626         {
627             static if (__traits(compiles, { object.fromMsgpack(this, withFieldName_); })) {
628               object.fromMsgpack(this, withFieldName_);
629             } else static if (__traits(compiles, { object.fromMsgpack(this); })) { // backward compatible
630                 object.fromMsgpack(this);
631             } else {
632                 static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
633             }
634         } else {
635             if (auto handler = object.classinfo in unpackHandlers) {
636                 (*handler)(this, cast(void*)&object);
637                 return this;
638             }
639             if (T.classinfo !is object.classinfo) {
640                 throw new MessagePackException("Can't unpack derived class through reference to base class.");
641             }
642 
643             unpackObject(object);
644         }
645 
646         return this;
647     }
648 
649 
650     /// ditto
651     ref Unpacker unpack(T)(ref T object) if (is(Unqual!T == struct) &&
652                                              !is(Unqual!T == ExtValue))
653     {
654         static if (hasMember!(T, "fromMsgpack"))
655         {
656             static if (__traits(compiles, { object.fromMsgpack(this); })) {
657                 object.fromMsgpack(this);
658             } else {
659                 static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
660             }
661         } else {
662             if (auto handler = typeid(T) in unpackHandlers) {
663                 (*handler)(this, cast(void*)&object);
664                 return this;
665             }
666 
667             size_t length = withFieldName_ ? beginMap() : beginArray();
668             if (length == 0)
669                 return this;
670 
671             static if (isTuple!T) {
672                 if (length != T.Types.length)
673                     rollback(calculateSize(length), "the number of tuple fields is mismatched");
674 
675                 foreach (i, Type; T.Types)
676                     unpack(object.field[i]);
677             } else {  // simple struct
678                 //if (length != object.tupleof.length)
679                 if (length != SerializingMemberNumbers!(T))
680                     rollback(calculateSize(length), "the number of struct fields is mismatched");
681 
682                 if (withFieldName_) {
683                     foreach (i, member; object.tupleof) {
684                         static if (isPackedField!(T.tupleof[i]))
685                         {
686                             string fieldName;
687                             unpack(fieldName);
688 
689                             if (fieldName == getFieldName!(T, i)) {
690                                 static if (hasSerializedAs!(T.tupleof[i])) {
691                                     alias Proxy = getSerializedAs!(T.tupleof[i]);
692                                     Proxy.deserialize(this, object.tupleof[i]);
693                                 } else {
694                                     unpack(object.tupleof[i]);
695                                 }
696                             } else {
697                                 assert(false, "Invalid field name: '" ~ fieldName ~ "', expect '" ~ getFieldName!(T, i) ~ "'");
698                             }
699                         }
700                     }
701                 } else {
702                     foreach (i, member; object.tupleof) {
703                         static if (isPackedField!(T.tupleof[i])) {
704                             static if (hasSerializedAs!(T.tupleof[i])) {
705                                 alias Proxy = getSerializedAs!(T.tupleof[i]);
706                                 Proxy.deserialize(this, object.tupleof[i]);
707                             } else {
708                                 unpack(object.tupleof[i]);
709                             }
710                         }
711                     }
712                 }
713             }
714         }
715 
716         return this;
717     }
718 
719 
720     void unpackObject(T)(ref T object) if (is(Unqual!T == class))
721     {
722         alias SerializingClasses!(T) Classes;
723 
724         size_t length = withFieldName_ ? beginMap() : beginArray();
725         if (length == 0)
726             return;
727 
728         if (length != SerializingMemberNumbers!(Classes))
729             rollback(calculateSize(length),  "the number of class fields is mismatched");
730 
731         if (withFieldName_) {
732             foreach (_; 0..length) {
733                 string fieldName;
734                 unpack(fieldName);
735 
736                 foreach (Class; Classes) {
737                     Class obj = cast(Class)object;
738 
739                     foreach (i, member; obj.tupleof) {
740                         static if (isPackedField!(Class.tupleof[i]))
741                         {
742                             if (fieldName == getFieldName!(Class, i)) {
743                                 static if (hasSerializedAs!(Class.tupleof[i])) {
744                                     alias Proxy = getSerializedAs!(Class.tupleof[i]);
745                                     Proxy.deserialize(this, obj.tupleof[i]);
746                                 } else {
747                                     unpack(obj.tupleof[i]);
748                                 }
749                                 goto endLoop;
750                             }
751                         }
752                     }
753                 }
754                 assert(false, "Invalid field name: '" ~ fieldName~"' ");
755 
756             endLoop:
757                 continue;
758             }
759         } else {
760             foreach (Class; Classes) {
761                 Class obj = cast(Class)object;
762 
763                 foreach (i, member; obj.tupleof) {
764                     static if (isPackedField!(Class.tupleof[i])) {
765                         static if (hasSerializedAs!(Class.tupleof[i])) {
766                             alias Proxy = getSerializedAs!(Class.tupleof[i]);
767                             Proxy.deserialize(this, obj.tupleof[i]);
768                         } else {
769                             unpack(obj.tupleof[i]);
770                         }
771                     }
772                 }
773             }
774         }
775     }
776 
777 
778     /**
779      * Deserializes the container object and assigns to each argument.
780      *
781      * These methods check the length. Do rollback if
782      * the length of arguments is different from length of deserialized object.
783      *
784      * In unpackMap, the number of arguments must be even.
785      *
786      * Params:
787      *  objects = the references of object to assign.
788      *
789      * Returns:
790      *  self, i.e. for method chaining.
791      */
792     ref Unpacker unpackArray(Types...)(ref Types objects)
793     {
794         auto length = beginArray();
795         if (length != Types.length)
796             rollback(calculateSize(length), "the number of deserialized objects is mismatched");
797 
798         foreach (i, T; Types)
799             unpack(objects[i]);
800         // unpack(objects);  // slow :(
801 
802         return this;
803     }
804 
805 
806     /// ditto
807     ref Unpacker unpackMap(Types...)(ref Types objects)
808     {
809         static assert(Types.length % 2 == 0, "The number of arguments must be even");
810 
811         auto length = beginMap();
812         if (length != Types.length / 2)
813             rollback(calculateSize(length), "the number of deserialized objects is mismatched");
814 
815         foreach (i, T; Types)
816             unpack(objects[i]);
817 
818         return this;
819     }
820 
821 
822     /**
823      * Deserializes the type-information of container.
824      *
825      * These methods don't deserialize contents.
826      * You need to call unpack method to deserialize contents at your own risk.
827      * -----
828      * // serialized data is [1, "Hi!"];
829      * int num;
830      * unpacker.beginArray(2).unpack(num);  // num is 1
831      *
832      * // other operation
833      *
834      * string str;
835      * unpacker.unpack(str);  // str is "Hi!"
836      * -----
837      *
838      * Returns:
839      *  the container size.
840      */
841     @safe
842     size_t beginArray()
843     {
844         canRead(Offset, 0);
845         const  header = read();
846         size_t length;
847 
848         if (0x90 <= header && header <= 0x9f) {
849             length = header & 0x0f;
850         } else {
851             switch (header) {
852             case Format.ARRAY16:
853                 canRead(ushort.sizeof);
854                 length = load16To!size_t(read(ushort.sizeof));
855                 break;
856             case Format.ARRAY32:
857                 canRead(uint.sizeof);
858                 length = load32To!size_t(read(uint.sizeof));
859                 break;
860             case Format.NIL:
861                 break;
862             default:
863                 rollback(0, "array", cast(Format)header);
864             }
865         }
866 
867         return length;
868     }
869 
870 
871     /// ditto
872     @safe
873     size_t beginMap()
874     {
875         canRead(Offset, 0);
876         const  header = read();
877         size_t length;
878 
879         if (0x80 <= header && header <= 0x8f) {
880             length = header & 0x0f;
881         } else {
882             switch (header) {
883             case Format.MAP16:
884                 canRead(ushort.sizeof);
885                 length = load16To!size_t(read(ushort.sizeof));
886                 break;
887             case Format.MAP32:
888                 canRead(uint.sizeof);
889                 length = load32To!size_t(read(uint.sizeof));
890                 break;
891             case Format.NIL:
892                 break;
893             default:
894                 rollback(0, "map", cast(Format)header);
895             }
896         }
897 
898         return length;
899     }
900 
901 
902     /**
903      * Unpacks an EXT value into $(D type) and $(D data).
904      * $(D type) is checked and a $(D MessagePackException) is thrown if it does
905      *  not match. The length of $(D data) is checked and a $(D MessagePackException)
906      *  is thrown if the lengths do not match.  If $(D data) is null, a new slice
907      *  is returned.
908      */
909     ref Unpacker unpackExt(ref byte type, ref ubyte[] data) return
910     {
911         import std.conv : text;
912 
913         canRead(Offset, 0);
914         const header = read();
915 
916         uint length;
917         uint rollbackLength = 0;
918         if (header >= Format.EXT && header <= Format.EXT + 4)
919         {
920             // Fixed
921             length = 2^^(header - Format.EXT);
922 
923         } else {
924             // Dynamic length
925             switch (header) with (Format)
926             {
927                 case EXT8:
928                     canRead(1);
929                     length = read();
930                     rollbackLength = 1;
931                     break;
932                 case EXT16:
933                     canRead(2);
934                     length = load16To!ushort(read(2));
935                     rollbackLength = 2;
936                     break;
937                 case EXT32:
938                     canRead(4);
939                     length = load32To!uint(read(4));
940                     rollbackLength = 4;
941                     break;
942                 default:
943                     rollback(0, "ext", cast(Format)header);
944             }
945 
946         }
947 
948         canRead(1 + length);
949 
950         // Read and check the type
951         byte type_ = read();
952         rollbackLength += 1;
953         if (type_ != type)
954             rollback(rollbackLength, text("Cannot unpack EXT of type ", type_, " into type ", type));
955 
956         // Read and check data
957         if (data is null)
958             data = new ubyte[](length);
959         else if (data.length != length) {
960             rollback(rollbackLength, text("Length mismatch while unpacking EXT: ", data.length, " was given, actual length is ", length));
961         }
962         data[] = read(length);
963         return this;
964     }
965 
966     /**
967      * Scans an entire buffer and converts each objects.
968      *
969      * This method is used for unpacking record-like objects.
970      *
971      * Example:
972      * -----
973      * // serialized data is "[1, 2][3, 4][5, 6][...".
974      * auto unpacker = Unpacker(serializedData);
975      * foreach (n, d; &unpacker.scan!(int, int))  // == "foreach (int n, int d; unpacker)"
976      *     writeln(n, d); // 1st loop "1, 2", 2nd loop "3, 4"...
977      * -----
978      */
979     int scan(Types...)(scope int delegate(ref Types) dg)
980     {
981         return opApply!(Types)(delegate int(ref Types objects) { return dg(objects); });
982     }
983 
984 
985     /// ditto
986     int opApply(Types...)(scope int delegate(ref Types) dg)
987     {
988         int result;
989 
990         while (used_ - offset_) {
991             auto length = beginArray();
992             if (length != Types.length)
993                 rollback(calculateSize(length), "the number of deserialized objects is mismatched");
994 
995             Types objects;
996             foreach (i, T; Types)
997                 unpack(objects[i]);
998 
999             result = dg(objects);
1000             if (result)
1001                 return result;
1002         }
1003 
1004         return result;
1005     }
1006 
1007 
1008   private:
1009     /*
1010      * Deserializes nil object and assigns to $(D_PARAM value).
1011      *
1012      * Params:
1013      *  value = the reference of value to assign.
1014      *
1015      * Returns:
1016      *  self, i.e. for method chaining.
1017      *
1018      * Throws:
1019      *  UnpackException when doesn't read from buffer or precision loss occurs and
1020      *  MessagePackException when $(D_PARAM T) type doesn't match serialized type.
1021      */
1022     @safe
1023     ref Unpacker unpackNil(T)(ref T value)
1024     {
1025         canRead(Offset, 0);
1026         const header = read();
1027 
1028         if (header == Format.NIL)
1029             value = null;
1030         else
1031             rollback(0, "nil", cast(Format)header);
1032 
1033         return this;
1034     }
1035 
1036 
1037     /*
1038      * Next object is nil?
1039      *
1040      * Returns:
1041      *  true if next object is nil.
1042      */
1043     @safe
1044     bool checkNil()
1045     {
1046         canRead(Offset, 0);
1047 
1048         return buffer_[offset_] == Format.NIL;
1049     }
1050 
1051 
1052     /*
1053      * Calculates the format size of container length.
1054      */
1055     size_t calculateSize(bool rawType = false)(in size_t length)
1056     {
1057         static if (rawType)
1058             return length < 32 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof;
1059         else
1060             return length < 16 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof;
1061     }
1062 
1063 
1064     /*
1065      * Reading test.
1066      *
1067      * Params:
1068      *  size   = the size to read.
1069      *  offset = the offset to subtract when doesn't read from buffer.
1070      *
1071      * Throws:
1072      *  UnpackException when doesn't read from buffer.
1073      */
1074     @safe
1075     void canRead(in size_t size, in size_t offset = Offset)
1076     {
1077         if (used_ - offset_ < size) {
1078             if (offset)
1079                 offset_ -= offset;
1080 
1081             throw new UnpackException("Insufficient buffer");
1082         }
1083     }
1084 
1085 
1086     /*
1087      * Reads value from buffer and advances offset.
1088      */
1089     @safe
1090     nothrow ubyte read()
1091     {
1092         return buffer_[offset_++];
1093     }
1094 
1095 
1096     /*
1097      * Reads value from buffer and advances offset.
1098      */
1099     @safe
1100     nothrow ubyte[] read(in size_t size)
1101     {
1102         auto result = buffer_[offset_..offset_ + size];
1103 
1104         offset_ += size;
1105 
1106         return result;
1107     }
1108 
1109 
1110     /*
1111      * Do rollback and throws exception.
1112      */
1113     @safe
1114     void rollback(in size_t size, in string reason)
1115     {
1116         offset_ -= size + Offset;
1117         onInvalidType(reason);
1118     }
1119 
1120     @safe
1121     void rollback(in size_t size, in string expected, in Format actual)
1122     {
1123         offset_ -= size + Offset;
1124         onInvalidType(expected, actual);
1125     }
1126 }
1127 
1128 
1129 private:
1130 
1131 
1132 /*
1133  * A callback for type-mismatched error in deserialization process.
1134  */
1135 @safe
1136 pure void onInvalidType(in string reason)
1137 {
1138     throw new MessagePackException("Attempt to unpack with non-compatible type: reason = " ~ reason);
1139 }
1140 
1141 @safe
1142 pure void onInvalidType(in string expected, in Format actual)
1143 {
1144     import std.conv: text;
1145     throw new MessagePackException(text("Attempt to unpack with non-compatible type: expected = ", expected, ", actual = ", actual));
1146 }
1147 
1148 
1149 unittest
1150 {
1151     import msgpack.packer;
1152 
1153     { // unique
1154         mixin DefinePacker;
1155 
1156         Tuple!(bool, bool) result;
1157         Tuple!(bool, bool) test = tuple(true, false);
1158 
1159         packer.pack(test);
1160 
1161         auto unpacker = Unpacker(packer.stream.data);
1162 
1163         unpacker.unpack(result);
1164         assert(test == result);
1165     }
1166     { // uint *
1167         mixin DefinePacker;
1168 
1169         Tuple!(ubyte, ushort, uint, ulong) result;
1170         Tuple!(ubyte, ushort, uint, ulong) test = tuple(cast(ubyte)ubyte.max, cast(ushort)ushort.max,
1171                                                         cast(uint)uint.max,   cast(ulong)ulong.max);
1172 
1173         packer.pack(test);
1174 
1175         auto unpacker = Unpacker(packer.stream.data);
1176 
1177         unpacker.unpack(result);
1178         assert(test == result);
1179     }
1180     { // int *
1181         mixin DefinePacker;
1182 
1183         Tuple!(byte, short, int, long) result;
1184         Tuple!(byte, short, int, long) test = tuple(cast(byte)byte.min, cast(short)short.min,
1185                                                     cast(int)int.min,   cast(long)long.min);
1186 
1187         packer.pack(test);
1188 
1189         auto unpacker = Unpacker(packer.stream.data);
1190 
1191         unpacker.unpack(result);
1192         assert(test == result);
1193     }
1194     { // floating point
1195         mixin DefinePacker;
1196 
1197         static if (real.sizeof == double.sizeof || !EnableReal)
1198         {
1199             Tuple!(float, double, double) result;
1200             Tuple!(float, double, double) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)double.min_normal);
1201         }
1202         else
1203         {
1204             Tuple!(float, double, real) result;
1205             Tuple!(float, double, real) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)real.min_normal);
1206         }
1207 
1208         packer.pack(test);
1209 
1210         auto unpacker = Unpacker(packer.stream.data);
1211 
1212         unpacker.unpack(result);
1213         assert(test == result);
1214     }
1215     { // pointer
1216         mixin DefinePacker;
1217 
1218         Tuple!(ulong, long, double) origin;
1219         Tuple!(ulong, long, double) values = tuple(ulong.max, long.min, double.min_normal);
1220         Tuple!(ulong*, long*, double*) result = tuple(&origin.field[0], &origin.field[1], &origin.field[2]);
1221         Tuple!(ulong*, long*, double*) test = tuple(&values.field[0], &values.field[1], &values.field[2]);
1222 
1223         packer.pack(test);
1224 
1225         auto unpacker = Unpacker(packer.stream.data);
1226 
1227         unpacker.unpack(result);
1228         foreach (i, v; test.field)
1229             assert(*v == *result.field[i]);
1230         assert(origin == values);
1231     }
1232     { // enum
1233         enum   : float { D = 0.5 }
1234         enum E : ulong { U = 100 }
1235 
1236         mixin DefinePacker;
1237 
1238         float f = D,   resultF;
1239         E     e = E.U, resultE;
1240 
1241         packer.pack(D, e);
1242 
1243         auto unpacker = Unpacker(packer.stream.data);
1244 
1245         unpacker.unpack(resultF, resultE);
1246         assert(f == resultF);
1247         assert(e == resultE);
1248     }
1249     { // container
1250         mixin DefinePacker;
1251 
1252         Tuple!(ulong[], double[uint], string, bool[2], char[2]) test
1253             = tuple([1UL, 2], [3U:4.0, 5:6.0, 7:8.0], "MessagePack is nice!", [true, false], "D!");
1254 
1255         packer.pack(test);
1256 
1257         auto unpacker = Unpacker(packer.stream.data);
1258         Tuple!(ulong[], double[uint], string, bool[2], char[2]) result;
1259 
1260         unpacker.unpack(result);
1261         assert(test == result);
1262     }
1263     { // ext
1264 
1265         // Try a variety of lengths, making sure to hit all the fixexts
1266         foreach (L; TypeTuple!(1, 2, 3, 4, 5, 8, 9, 16, 32, 512, 2^^16))
1267         {
1268             mixin DefinePacker;
1269 
1270             ubyte[] data = new ubyte[](L);
1271             ExtValue ext = ExtValue(7, data);
1272             packer.pack(ext);
1273 
1274             auto unpacker1 = Unpacker(packer.stream.data);
1275             ExtValue witness;
1276 
1277             unpacker1.unpack(witness);
1278             assert(ext == witness);
1279 
1280             // And try unpackExt
1281             auto unpacker2 = Unpacker(packer.stream.data);
1282             byte type = 1;
1283             ubyte[] deserializedData = new ubyte[](7);
1284 
1285             // This should be a type mismatch (1 != 7)
1286             assertThrown!MessagePackException(
1287                 unpacker2.unpackExt(type, deserializedData));
1288             type = 7;
1289 
1290             // A data size mismatch
1291             assertThrown!MessagePackException(
1292                 unpacker2.unpackExt(type, deserializedData));
1293             deserializedData = new ubyte[](L);
1294 
1295             // And this should succeed
1296             unpacker2.unpackExt(type, deserializedData);
1297             assert(deserializedData == data);
1298         }
1299     }
1300     { // user defined
1301         {
1302             static struct S
1303             {
1304                 uint num;
1305 
1306                 void toMsgpack(P)(ref P p) const { p.packArray(num); }
1307                 void fromMsgpack(ref Unpacker u)
1308                 {
1309                     assert(u.beginArray() == 1);
1310                     u.unpack(num);
1311                 }
1312             }
1313 
1314             mixin DefinePacker; S result, test = S(uint.max);
1315 
1316             packer.pack(test);
1317 
1318             auto unpacker = Unpacker(packer.stream.data);
1319             unpacker.unpack(result);
1320 
1321             assert(test.num == result.num);
1322         }
1323         {
1324             static class C
1325             {
1326                 uint num;
1327 
1328                 this(uint n) { num = n; }
1329 
1330                 void toMsgpack(P)(ref P p) const { p.packArray(num - 1); }
1331                 void fromMsgpack(ref Unpacker u)
1332                 {
1333                     assert(u.beginArray() == 1);
1334                     u.unpack(num);
1335                 }
1336             }
1337 
1338             mixin DefinePacker; C result, test = new C(ushort.max);
1339 
1340             packer.pack(test);
1341 
1342             auto unpacker = Unpacker(packer.stream.data);
1343             unpacker.unpack(result, ushort.max);
1344 
1345             assert(test.num == result.num + 1);
1346         }
1347     }
1348     { // simple struct and class
1349         {
1350             static struct Simple
1351             {
1352                 uint num;
1353                 @nonPacked string str;
1354             }
1355 
1356             static struct Simple2
1357             {
1358                 @nonPacked string str;
1359                 uint num;
1360             }
1361 
1362             foreach (Type; TypeTuple!(Simple, Simple2)) {
1363                 mixin DefinePacker;
1364                 Type result, test;
1365                 test.num = uint.max;
1366                 test.str = "ignored";
1367 
1368                 packer.pack(test);
1369                 auto unpacker = Unpacker(packer.stream.data);
1370                 unpacker.unpack(result);
1371 
1372                 assert(test.num == result.num);
1373                 assert(test.str != result.str);
1374             }
1375         }
1376 
1377         {
1378             static struct SimpleProxy1
1379             {
1380                 import std.conv;
1381                 static void serialize(ref Packer p, ref string val) { p.pack(to!uint(val)); }
1382                 static void deserialize(ref Unpacker u, ref string val) { uint tmp; u.unpack(tmp); val = to!string(tmp); }
1383             }
1384             static struct SimpleWithProxied1
1385             {
1386                 @serializedAs!SimpleProxy1 string data;
1387                 enum string defaultValue = "10";
1388             }
1389 
1390             // https://github.com/msgpack/msgpack-d/issues/83
1391             static struct SimpleProxy2
1392             {
1393                 import std.datetime;
1394                 static void serialize(ref Packer p, ref SysTime val) { p.pack(val.toISOExtString()); }
1395                 static void deserialize(ref Unpacker u, ref SysTime val) { string tmp; u.unpack(tmp); val = SysTime.fromISOExtString(tmp); }
1396             }
1397             static struct SimpleWithProxied2
1398             {
1399                 import std.datetime;
1400                 @serializedAs!SimpleProxy2 SysTime data;
1401                 static SysTime defaultValue() @property { return SysTime(DateTime(2019,1,1,0,0,0)); }
1402             }
1403             
1404             foreach (Type; TypeTuple!(SimpleWithProxied1, SimpleWithProxied2)) {
1405                 mixin DefinePacker;
1406                 Type result, test;
1407                 test.data = Type.defaultValue;
1408                 
1409                 packer.pack(test);
1410                 auto unpacker = Unpacker(packer.stream.data);
1411                 unpacker.unpack(result);
1412                 assert(test.data == result.data);
1413             }
1414         }
1415 
1416         static class SimpleA
1417         {
1418             bool flag = true;
1419         }
1420 
1421         static class SimpleB : SimpleA
1422         {
1423             ubyte type = 100;
1424         }
1425 
1426         static class SimpleC : SimpleB
1427         {
1428             uint num = uint.max;
1429             @nonPacked string str;
1430         }
1431 
1432         static class SimpleC2 : SimpleB
1433         {
1434             @nonPacked string str;
1435             uint num = uint.max;
1436         }
1437 
1438         static class SimpleD
1439         {
1440             static struct Proxy
1441             {
1442                 import std.conv;
1443                 static void serialize(ref Packer p, ref bool val)      { p.pack(to!string(val)); }
1444                 static void serialize(ref Packer p, ref uint val)      { p.pack(to!string(val)); }
1445                 static void serialize(ref Packer p, ref ubyte val)     { p.pack(to!string(val)); }
1446                 static void deserialize(ref Unpacker u, ref bool val)  { string tmp; u.unpack(tmp); val = to!bool(tmp); }
1447                 static void deserialize(ref Unpacker u, ref uint val)  { string tmp; u.unpack(tmp); val = to!uint(tmp); }
1448                 static void deserialize(ref Unpacker u, ref ubyte val) { string tmp; u.unpack(tmp); val = to!ubyte(tmp); }
1449             }
1450             @serializedAs!Proxy bool flag = true;
1451             @serializedAs!Proxy ubyte type = 100;
1452             @serializedAs!Proxy uint num = uint.max;
1453             @nonPacked string str;
1454         }
1455 
1456         { // from derived class
1457             foreach (Type; TypeTuple!(SimpleC, SimpleC2, SimpleD)) {
1458                 mixin DefinePacker;
1459                 Type result, test = new Type();
1460                 test.flag = false;
1461                 test.type = 99;
1462                 test.num  = uint.max / 2;
1463                 test.str  = "ignored";
1464 
1465                 packer.pack(test);
1466                 auto unpacker = Unpacker(packer.stream.data);
1467                 unpacker.unpack(result);
1468 
1469                 assert(test.flag == result.flag);
1470                 assert(test.type == result.type);
1471                 assert(test.num  == result.num);
1472                 assert(test.str  != result.str);
1473             }
1474         }
1475         { // from base class
1476             mixin DefinePacker; SimpleC test = new SimpleC();
1477 
1478             packer.pack(test);
1479 
1480             SimpleB result = new SimpleC();
1481             auto unpacker  = Unpacker(packer.stream.data);
1482 
1483             try {
1484                 unpacker.unpack(result);
1485                 assert(false);
1486             } catch (Exception e) { }
1487         }
1488         { // https://github.com/msgpack/msgpack-d/issues/16
1489             mixin DefinePacker;
1490 
1491             static class Issue16
1492             {
1493                 int i;
1494                 this(int i) { this.i = i; }
1495             }
1496 
1497             Issue16 c1 = new Issue16(10);
1498 
1499             // change behaviour to accept null with new object without constructor
1500             Issue16 c2 = null;
1501             packer.pack(c1);
1502             auto unpacker1 = Unpacker(packer.stream.data);
1503             unpacker1.unpack(c2);
1504             //unpack(pack(c1), c2);
1505             assert(c2.i == c1.i);
1506 
1507             Issue16 c3 = new Issue16(20);
1508             packer.stream.clear();
1509             packer.pack(c1);
1510             auto unpacker2 = Unpacker(packer.stream.data);
1511             unpacker2.unpack(c3);
1512             //unpack(pack(c1), c3);
1513             assert(c3.i == c1.i);
1514         }
1515     }
1516     { // variadic
1517         mixin DefinePacker;
1518 
1519         Tuple!(uint, long, double) test = tuple(uint.max, long.min, double.max);
1520 
1521         packer.pack(test);
1522 
1523         auto unpacker = Unpacker(packer.stream.data);
1524 
1525         uint u; long l; double d;
1526 
1527         unpacker.unpackArray(u, l, d);
1528         assert(test == tuple(u, l, d));
1529     }
1530     { // scan / opApply
1531         ubyte[] data;
1532         mixin DefinePacker;
1533 
1534         foreach (i; 0..2)
1535             packer.pack(tuple(1, 0.5, "Hi!"));
1536 
1537         foreach (n, d, s; &Unpacker(packer.stream.data).scan!(int, double, string)) {
1538             assert(n == 1);
1539             assert(d == 0.5);
1540             assert(s == "Hi!");
1541         }
1542     }
1543 }