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             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, T.stringof, Format.UINT8);
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, T.stringof, Format.UINT16);
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, T.stringof, Format.UINT32);
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, T.stringof, Format.UINT64);
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, T.stringof, Format.INT16);
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, T.stringof, Format.INT32);
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, T.stringof, Format.INT64);
253                 value = cast(T)l;
254                 break;
255             default:
256                 rollback(0, T.stringof, cast(Format)header);
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(0, T.stringof, Format.DOUBLE);
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(0, "real is disabled", Format.REAL);
309             }
310             else
311             {
312                 // check precision loss
313                 static if (is(Unqual!T == float) || is(Unqual!T == double))
314                     rollback(0, T.stringof, Format.REAL);
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(0, T.stringof, cast(Format)header);
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(0, T.stringof, cast(Format)header);
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(0, T.stringof, cast(Format)header);
497                 }
498             }
499 
500             return length;
501         }
502 
503 
504         if (checkNil()) {
505             static if (isStaticArray!T) {
506                 onInvalidType("static array", Format.NIL);
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, "static array was given but the length is mismatched");
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), "static array was given but the length is mismatched");
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), "the number of tuple fields is mismatched");
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), "the number of struct fields is mismatched");
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),  "the number of class fields is mismatched");
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), "the number of deserialized objects is mismatched");
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), "the number of deserialized objects is mismatched");
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(0, "array", cast(Format)header);
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(0, "map", cast(Format)header);
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(0, "ext", cast(Format)header);
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             rollback(rollbackLength, text("Cannot unpack EXT of type ", type_, " into type ", type));
919 
920         // Read and check data
921         if (data is null)
922             data = new ubyte[](length);
923         else if (data.length != length) {
924             rollback(rollbackLength, text("Length mismatch while unpacking EXT: ", data.length, " was given, actual length is ", length));
925         }
926         data[] = read(length);
927         return this;
928     }
929 
930     /**
931      * Scans an entire buffer and converts each objects.
932      *
933      * This method is used for unpacking record-like objects.
934      *
935      * Example:
936      * -----
937      * // serialized data is "[1, 2][3, 4][5, 6][...".
938      * auto unpacker = Unpacker(serializedData);
939      * foreach (n, d; &unpacker.scan!(int, int))  // == "foreach (int n, int d; unpacker)"
940      *     writeln(n, d); // 1st loop "1, 2", 2nd loop "3, 4"...
941      * -----
942      */
943     int scan(Types...)(scope int delegate(ref Types) dg)
944     {
945         return opApply!(Types)(delegate int(ref Types objects) { return dg(objects); });
946     }
947 
948 
949     /// ditto
950     int opApply(Types...)(scope int delegate(ref Types) dg)
951     {
952         int result;
953 
954         while (used_ - offset_) {
955             auto length = beginArray();
956             if (length != Types.length)
957                 rollback(calculateSize(length), "the number of deserialized objects is mismatched");
958 
959             Types objects;
960             foreach (i, T; Types)
961                 unpack(objects[i]);
962 
963             result = dg(objects);
964             if (result)
965                 return result;
966         }
967 
968         return result;
969     }
970 
971 
972   private:
973     /*
974      * Deserializes nil object and assigns to $(D_PARAM value).
975      *
976      * Params:
977      *  value = the reference of value to assign.
978      *
979      * Returns:
980      *  self, i.e. for method chaining.
981      *
982      * Throws:
983      *  UnpackException when doesn't read from buffer or precision loss occurs and
984      *  MessagePackException when $(D_PARAM T) type doesn't match serialized type.
985      */
986     @safe
987     ref Unpacker unpackNil(T)(ref T value)
988     {
989         canRead(Offset, 0);
990         const header = read();
991 
992         if (header == Format.NIL)
993             value = null;
994         else
995             rollback(0, "nil", cast(Format)header);
996 
997         return this;
998     }
999 
1000 
1001     /*
1002      * Next object is nil?
1003      *
1004      * Returns:
1005      *  true if next object is nil.
1006      */
1007     @safe
1008     bool checkNil()
1009     {
1010         canRead(Offset, 0);
1011 
1012         return buffer_[offset_] == Format.NIL;
1013     }
1014 
1015 
1016     /*
1017      * Calculates the format size of container length.
1018      */
1019     size_t calculateSize(bool rawType = false)(in size_t length)
1020     {
1021         static if (rawType)
1022             return length < 32 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof;
1023         else
1024             return length < 16 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof;
1025     }
1026 
1027 
1028     /*
1029      * Reading test.
1030      *
1031      * Params:
1032      *  size   = the size to read.
1033      *  offset = the offset to subtract when doesn't read from buffer.
1034      *
1035      * Throws:
1036      *  UnpackException when doesn't read from buffer.
1037      */
1038     @safe
1039     void canRead(in size_t size, in size_t offset = Offset)
1040     {
1041         if (used_ - offset_ < size) {
1042             if (offset)
1043                 offset_ -= offset;
1044 
1045             throw new UnpackException("Insufficient buffer");
1046         }
1047     }
1048 
1049 
1050     /*
1051      * Reads value from buffer and advances offset.
1052      */
1053     @safe
1054     nothrow ubyte read()
1055     {
1056         return buffer_[offset_++];
1057     }
1058 
1059 
1060     /*
1061      * Reads value from buffer and advances offset.
1062      */
1063     @safe
1064     nothrow ubyte[] read(in size_t size)
1065     {
1066         auto result = buffer_[offset_..offset_ + size];
1067 
1068         offset_ += size;
1069 
1070         return result;
1071     }
1072 
1073 
1074     /*
1075      * Do rollback and throws exception.
1076      */
1077     @safe
1078     void rollback(in size_t size, in string reason)
1079     {
1080         offset_ -= size + Offset;
1081         onInvalidType(reason);
1082     }
1083 
1084     @safe
1085     void rollback(in size_t size, in string expected, in Format actual)
1086     {
1087         offset_ -= size + Offset;
1088         onInvalidType(expected, actual);
1089     }
1090 }
1091 
1092 
1093 private:
1094 
1095 
1096 /*
1097  * A callback for type-mismatched error in deserialization process.
1098  */
1099 @safe
1100 pure void onInvalidType(in string reason)
1101 {
1102     throw new MessagePackException("Attempt to unpack with non-compatible type: reason = " ~ reason);
1103 }
1104 
1105 @safe
1106 pure void onInvalidType(in string expected, in Format actual)
1107 {
1108     import std.conv: text;
1109     throw new MessagePackException(text("Attempt to unpack with non-compatible type: expected = ", expected, ", actual = ", actual));
1110 }
1111 
1112 
1113 unittest
1114 {
1115     import msgpack.packer;
1116 
1117     { // unique
1118         mixin DefinePacker;
1119 
1120         Tuple!(bool, bool) result;
1121         Tuple!(bool, bool) test = tuple(true, false);
1122 
1123         packer.pack(test);
1124 
1125         auto unpacker = Unpacker(packer.stream.data);
1126 
1127         unpacker.unpack(result);
1128         assert(test == result);
1129     }
1130     { // uint *
1131         mixin DefinePacker;
1132 
1133         Tuple!(ubyte, ushort, uint, ulong) result;
1134         Tuple!(ubyte, ushort, uint, ulong) test = tuple(cast(ubyte)ubyte.max, cast(ushort)ushort.max,
1135                                                         cast(uint)uint.max,   cast(ulong)ulong.max);
1136 
1137         packer.pack(test);
1138 
1139         auto unpacker = Unpacker(packer.stream.data);
1140 
1141         unpacker.unpack(result);
1142         assert(test == result);
1143     }
1144     { // int *
1145         mixin DefinePacker;
1146 
1147         Tuple!(byte, short, int, long) result;
1148         Tuple!(byte, short, int, long) test = tuple(cast(byte)byte.min, cast(short)short.min,
1149                                                     cast(int)int.min,   cast(long)long.min);
1150 
1151         packer.pack(test);
1152 
1153         auto unpacker = Unpacker(packer.stream.data);
1154 
1155         unpacker.unpack(result);
1156         assert(test == result);
1157     }
1158     { // floating point
1159         mixin DefinePacker;
1160 
1161         static if (real.sizeof == double.sizeof || !EnableReal)
1162         {
1163             Tuple!(float, double, double) result;
1164             Tuple!(float, double, double) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)double.min_normal);
1165         }
1166         else
1167         {
1168             Tuple!(float, double, real) result;
1169             Tuple!(float, double, real) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)real.min_normal);
1170         }
1171 
1172         packer.pack(test);
1173 
1174         auto unpacker = Unpacker(packer.stream.data);
1175 
1176         unpacker.unpack(result);
1177         assert(test == result);
1178     }
1179     { // pointer
1180         mixin DefinePacker;
1181 
1182         Tuple!(ulong, long, double) origin;
1183         Tuple!(ulong, long, double) values = tuple(ulong.max, long.min, double.min_normal);
1184         Tuple!(ulong*, long*, double*) result = tuple(&origin.field[0], &origin.field[1], &origin.field[2]);
1185         Tuple!(ulong*, long*, double*) test = tuple(&values.field[0], &values.field[1], &values.field[2]);
1186 
1187         packer.pack(test);
1188 
1189         auto unpacker = Unpacker(packer.stream.data);
1190 
1191         unpacker.unpack(result);
1192         foreach (i, v; test.field)
1193             assert(*v == *result.field[i]);
1194         assert(origin == values);
1195     }
1196     { // enum
1197         enum   : float { D = 0.5 }
1198         enum E : ulong { U = 100 }
1199 
1200         mixin DefinePacker;
1201 
1202         float f = D,   resultF;
1203         E     e = E.U, resultE;
1204 
1205         packer.pack(D, e);
1206 
1207         auto unpacker = Unpacker(packer.stream.data);
1208 
1209         unpacker.unpack(resultF, resultE);
1210         assert(f == resultF);
1211         assert(e == resultE);
1212     }
1213     { // container
1214         mixin DefinePacker;
1215 
1216         Tuple!(ulong[], double[uint], string, bool[2], char[2]) test
1217             = tuple([1UL, 2], [3U:4.0, 5:6.0, 7:8.0], "MessagePack is nice!", [true, false], "D!");
1218 
1219         packer.pack(test);
1220 
1221         auto unpacker = Unpacker(packer.stream.data);
1222         Tuple!(ulong[], double[uint], string, bool[2], char[2]) result;
1223 
1224         unpacker.unpack(result);
1225         assert(test == result);
1226     }
1227     { // ext
1228 
1229         // Try a variety of lengths, making sure to hit all the fixexts
1230         foreach (L; TypeTuple!(1, 2, 3, 4, 5, 8, 9, 16, 32, 512, 2^^16))
1231         {
1232             mixin DefinePacker;
1233 
1234             ubyte[] data = new ubyte[](L);
1235             ExtValue ext = ExtValue(7, data);
1236             packer.pack(ext);
1237 
1238             auto unpacker1 = Unpacker(packer.stream.data);
1239             ExtValue witness;
1240 
1241             unpacker1.unpack(witness);
1242             assert(ext == witness);
1243 
1244             // And try unpackExt
1245             auto unpacker2 = Unpacker(packer.stream.data);
1246             byte type = 1;
1247             ubyte[] deserializedData = new ubyte[](7);
1248 
1249             // This should be a type mismatch (1 != 7)
1250             assertThrown!MessagePackException(
1251                 unpacker2.unpackExt(type, deserializedData));
1252             type = 7;
1253 
1254             // A data size mismatch
1255             assertThrown!MessagePackException(
1256                 unpacker2.unpackExt(type, deserializedData));
1257             deserializedData = new ubyte[](L);
1258 
1259             // And this should succeed
1260             unpacker2.unpackExt(type, deserializedData);
1261             assert(deserializedData == data);
1262         }
1263     }
1264     { // user defined
1265         {
1266             static struct S
1267             {
1268                 uint num;
1269 
1270                 void toMsgpack(P)(ref P p) const { p.packArray(num); }
1271                 void fromMsgpack(ref Unpacker u)
1272                 {
1273                     assert(u.beginArray() == 1);
1274                     u.unpack(num);
1275                 }
1276             }
1277 
1278             mixin DefinePacker; S result, test = S(uint.max);
1279 
1280             packer.pack(test);
1281 
1282             auto unpacker = Unpacker(packer.stream.data);
1283             unpacker.unpack(result);
1284 
1285             assert(test.num == result.num);
1286         }
1287         {
1288             static class C
1289             {
1290                 uint num;
1291 
1292                 this(uint n) { num = n; }
1293 
1294                 void toMsgpack(P)(ref P p) const { p.packArray(num - 1); }
1295                 void fromMsgpack(ref Unpacker u)
1296                 {
1297                     assert(u.beginArray() == 1);
1298                     u.unpack(num);
1299                 }
1300             }
1301 
1302             mixin DefinePacker; C result, test = new C(ushort.max);
1303 
1304             packer.pack(test);
1305 
1306             auto unpacker = Unpacker(packer.stream.data);
1307             unpacker.unpack(result, ushort.max);
1308 
1309             assert(test.num == result.num + 1);
1310         }
1311     }
1312     { // simple struct and class
1313         {
1314             static struct Simple
1315             {
1316                 uint num;
1317                 @nonPacked string str;
1318             }
1319 
1320             static struct Simple2
1321             {
1322                 @nonPacked string str;
1323                 uint num;
1324             }
1325 
1326             foreach (Type; TypeTuple!(Simple, Simple2)) {
1327                 mixin DefinePacker;
1328                 Type result, test;
1329                 test.num = uint.max;
1330                 test.str = "ignored";
1331 
1332                 packer.pack(test);
1333                 auto unpacker = Unpacker(packer.stream.data);
1334                 unpacker.unpack(result);
1335 
1336                 assert(test.num == result.num);
1337                 assert(test.str != result.str);
1338             }
1339         }
1340 
1341         static class SimpleA
1342         {
1343             bool flag = true;
1344         }
1345 
1346         static class SimpleB : SimpleA
1347         {
1348             ubyte type = 100;
1349         }
1350 
1351         static class SimpleC : SimpleB
1352         {
1353             uint num = uint.max;
1354             @nonPacked string str;
1355         }
1356 
1357         static class SimpleC2 : SimpleB
1358         {
1359             @nonPacked string str;
1360             uint num = uint.max;
1361         }
1362 
1363         { // from derived class
1364             foreach (Type; TypeTuple!(SimpleC, SimpleC2)) {
1365                 mixin DefinePacker;
1366                 Type result, test = new Type();
1367                 test.flag = false;
1368                 test.type = 99;
1369                 test.num  = uint.max / 2;
1370                 test.str  = "ignored";
1371 
1372                 packer.pack(test);
1373                 auto unpacker = Unpacker(packer.stream.data);
1374                 unpacker.unpack(result);
1375 
1376                 assert(test.flag == result.flag);
1377                 assert(test.type == result.type);
1378                 assert(test.num  == result.num);
1379                 assert(test.str  != result.str);
1380             }
1381         }
1382         { // from base class
1383             mixin DefinePacker; SimpleC test = new SimpleC();
1384 
1385             packer.pack(test);
1386 
1387             SimpleB result = new SimpleC();
1388             auto unpacker  = Unpacker(packer.stream.data);
1389 
1390             try {
1391                 unpacker.unpack(result);
1392                 assert(false);
1393             } catch (Exception e) { }
1394         }
1395         { // https://github.com/msgpack/msgpack-d/issues/16
1396             mixin DefinePacker;
1397 
1398             static class Issue16
1399             {
1400                 int i;
1401                 this(int i) { this.i = i; }
1402             }
1403 
1404             Issue16 c1 = new Issue16(10);
1405 
1406             // change behaviour to accept null with new object without constructor
1407             Issue16 c2 = null;
1408             packer.pack(c1);
1409             auto unpacker1 = Unpacker(packer.stream.data);
1410             unpacker1.unpack(c2);
1411             //unpack(pack(c1), c2);
1412             assert(c2.i == c1.i);
1413 
1414             Issue16 c3 = new Issue16(20);
1415             packer.stream.clear();
1416             packer.pack(c1);
1417             auto unpacker2 = Unpacker(packer.stream.data);
1418             unpacker2.unpack(c3);            
1419             //unpack(pack(c1), c3);
1420             assert(c3.i == c1.i);
1421         }
1422     }
1423     { // variadic
1424         mixin DefinePacker;
1425 
1426         Tuple!(uint, long, double) test = tuple(uint.max, long.min, double.max);
1427 
1428         packer.pack(test);
1429 
1430         auto unpacker = Unpacker(packer.stream.data);
1431 
1432         uint u; long l; double d;
1433 
1434         unpacker.unpackArray(u, l, d);
1435         assert(test == tuple(u, l, d));
1436     }
1437     { // scan / opApply
1438         ubyte[] data;
1439         mixin DefinePacker;
1440 
1441         foreach (i; 0..2)
1442             packer.pack(tuple(1, 0.5, "Hi!"));
1443 
1444         foreach (n, d, s; &Unpacker(packer.stream.data).scan!(int, double, string)) {
1445             assert(n == 1);
1446             assert(d == 0.5);
1447             assert(s == "Hi!");
1448         }
1449     }
1450 }