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