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