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         else
516             auto length = beginArray();
517 
518         if(length > buffer_.length) {
519             import std.conv: text;
520             throw new MessagePackException(text("Invalid array size in byte stream: Length (", length,
521                                                 ") is larger than internal buffer size (", buffer_.length, ")"));
522         }
523 
524         // Raw bytes
525         static if (isByte!U || isSomeChar!U) {
526             auto offset = calculateSize!(true)(length);
527             if (length == 0)
528                 return this;
529 
530             static if (isStaticArray!T) {
531                 if (length != array.length)
532                     rollback(offset, "static array was given but the length is mismatched");
533             }
534 
535             canRead(length, offset + Offset);
536             static if (isStaticArray!T) {
537                 array[] = (cast(U[])read(length))[0 .. T.length];
538             } else {
539                 array = cast(T)read(length);
540             }
541 
542             static if (isDynamicArray!T)
543                 hasRaw_ = true;
544         } else {
545             if (length == 0)
546                 return this;
547 
548             static if (isStaticArray!T) {
549                 if (length != array.length)
550                     rollback(calculateSize(length), "static array was given but the length is mismatched");
551             } else {
552                 array.length = length;
553             }
554 
555             foreach (i; 0..length)
556                 unpack(array[i]);
557         }
558 
559         return this;
560     }
561 
562 
563     /// ditto
564     ref Unpacker unpack(T)(ref T array) if (isAssociativeArray!T)
565     {
566         alias typeof(T.init.keys[0])   K;
567         alias typeof(T.init.values[0]) V;
568 
569         if (checkNil())
570             return unpackNil(array);
571 
572         auto length = beginMap();
573         if (length == 0)
574             return this;
575 
576         foreach (i; 0..length) {
577             K k; unpack(k);
578             V v; unpack(v);
579             array[k] = v;
580         }
581 
582         return this;
583     }
584 
585     /**
586      * Deserializes $(D_PARAM T) object and assigns to $(D_PARAM object).
587      *
588      * Calling $(D fromMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D fromMsgpack) method. $(D fromMsgpack) signature is:
589      * -----
590      * void fromMsgpack(ref Unpacker unpacker)
591      * -----
592      * Assumes $(D std.typecons.Tuple) or simple struct if $(D_KEYWORD struct) doesn't implement $(D fromMsgpack).
593      * Checks length if $(D_PARAM T) is a $(D std.typecons.Tuple) or simple struct.
594      *
595      * Params:
596      *  object = the reference of object to assign.
597      *  args   = the arguments to class constructor(class only).
598      *           This is used at new statement if $(D_PARAM object) is $(D_KEYWORD null).
599      *
600      * Returns:
601      *  self, i.e. for method chaining.
602      */
603     ref Unpacker unpack(T, Args...)(ref T object, auto ref Args args) if (is(Unqual!T == class))
604     {
605         if (checkNil())
606             return unpackNil(object);
607 
608         if (object is null) {
609             static if (Args.length == 0) {
610                 static if (__traits(compiles, { new T(); }))
611                     object = new T();
612                 else
613                     object = cast(T)_d_newclass(T.classinfo);
614             } else static if (__traits(compiles, { new T(args); })) {
615                 object = new T(args);
616             } else {
617                 throw new MessagePackException("Don't know how to construct class type '" ~ Unqual!T.stringof ~ "' with argument types '" ~ Args.stringof ~ "'.");
618             }
619         }
620 
621         static if (hasMember!(T, "fromMsgpack"))
622         {
623             static if (__traits(compiles, { object.fromMsgpack(this, withFieldName_); })) {
624               object.fromMsgpack(this, withFieldName_);
625             } else static if (__traits(compiles, { object.fromMsgpack(this); })) { // backward compatible
626                 object.fromMsgpack(this);
627             } else {
628                 static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
629             }
630         } else {
631             if (auto handler = object.classinfo in unpackHandlers) {
632                 (*handler)(this, cast(void*)&object);
633                 return this;
634             }
635             if (T.classinfo !is object.classinfo) {
636                 throw new MessagePackException("Can't unpack derived class through reference to base class.");
637             }
638 
639             unpackObject(object);
640         }
641 
642         return this;
643     }
644 
645 
646     /// ditto
647     ref Unpacker unpack(T)(ref T object) if (is(Unqual!T == struct) &&
648                                              !is(Unqual!T == ExtValue))
649     {
650         static if (hasMember!(T, "fromMsgpack"))
651         {
652             static if (__traits(compiles, { object.fromMsgpack(this); })) {
653                 object.fromMsgpack(this);
654             } else {
655                 static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
656             }
657         } else {
658             if (auto handler = typeid(T) in unpackHandlers) {
659                 (*handler)(this, cast(void*)&object);
660                 return this;
661             }
662 
663             size_t length = withFieldName_ ? beginMap() : beginArray();
664             if (length == 0)
665                 return this;
666 
667             static if (isTuple!T) {
668                 if (length != T.Types.length)
669                     rollback(calculateSize(length), "the number of tuple fields is mismatched");
670 
671                 foreach (i, Type; T.Types)
672                     unpack(object.field[i]);
673             } else {  // simple struct
674                 //if (length != object.tupleof.length)
675                 if (length != SerializingMemberNumbers!(T))
676                     rollback(calculateSize(length), "the number of struct fields is mismatched");
677 
678                 if (withFieldName_) {
679                     foreach (i, member; object.tupleof) {
680                         static if (isPackedField!(T.tupleof[i]))
681                         {
682                             string fieldName;
683                             unpack(fieldName);
684 
685                             if (fieldName == getFieldName!(T, i)) {
686                                 unpack(object.tupleof[i]);
687                             } else {
688                                 assert(false, "Invalid field name: '" ~ fieldName ~ "', expect '" ~ getFieldName!(T, i) ~ "'");
689                             }
690                         }
691                     }
692                 } else {
693                     foreach (i, member; object.tupleof) {
694                         static if (isPackedField!(T.tupleof[i]))
695                             unpack(object.tupleof[i]);
696                     }
697                 }
698             }
699         }
700 
701         return this;
702     }
703 
704 
705     void unpackObject(T)(ref T object) if (is(Unqual!T == class))
706     {
707         alias SerializingClasses!(T) Classes;
708 
709         size_t length = withFieldName_ ? beginMap() : beginArray();
710         if (length == 0)
711             return;
712 
713         if (length != SerializingMemberNumbers!(Classes))
714             rollback(calculateSize(length),  "the number of class fields is mismatched");
715 
716         if (withFieldName_) {
717             foreach (_; 0..length) {
718                 string fieldName;
719                 unpack(fieldName);
720 
721                 foreach (Class; Classes) {
722                     Class obj = cast(Class)object;
723 
724                     foreach (i, member; obj.tupleof) {
725                         static if (isPackedField!(Class.tupleof[i]))
726                         {
727                             if (fieldName == getFieldName!(Class, i)) {
728                                 unpack(obj.tupleof[i]);
729                                 goto endLoop;
730                             }
731                         }
732                     }
733                 }
734                 assert(false, "Invalid field name: '" ~ fieldName~"' ");
735 
736             endLoop:
737                 continue;
738             }
739         } else {
740             foreach (Class; Classes) {
741                 Class obj = cast(Class)object;
742 
743                 foreach (i, member; obj.tupleof) {
744                     static if (isPackedField!(Class.tupleof[i]))
745                         unpack(obj.tupleof[i]);
746                 }
747             }
748         }
749     }
750 
751 
752     /**
753      * Deserializes the container object and assigns to each argument.
754      *
755      * These methods check the length. Do rollback if
756      * the length of arguments is different from length of deserialized object.
757      *
758      * In unpackMap, the number of arguments must be even.
759      *
760      * Params:
761      *  objects = the references of object to assign.
762      *
763      * Returns:
764      *  self, i.e. for method chaining.
765      */
766     ref Unpacker unpackArray(Types...)(ref Types objects)
767     {
768         auto length = beginArray();
769         if (length != Types.length)
770             rollback(calculateSize(length), "the number of deserialized objects is mismatched");
771 
772         foreach (i, T; Types)
773             unpack(objects[i]);
774         // unpack(objects);  // slow :(
775 
776         return this;
777     }
778 
779 
780     /// ditto
781     ref Unpacker unpackMap(Types...)(ref Types objects)
782     {
783         static assert(Types.length % 2 == 0, "The number of arguments must be even");
784 
785         auto length = beginMap();
786         if (length != Types.length / 2)
787             rollback(calculateSize(length), "the number of deserialized objects is mismatched");
788 
789         foreach (i, T; Types)
790             unpack(objects[i]);
791 
792         return this;
793     }
794 
795 
796     /**
797      * Deserializes the type-information of container.
798      *
799      * These methods don't deserialize contents.
800      * You need to call unpack method to deserialize contents at your own risk.
801      * -----
802      * // serialized data is [1, "Hi!"];
803      * int num;
804      * unpacker.beginArray(2).unpack(num);  // num is 1
805      *
806      * // other operation
807      *
808      * string str;
809      * unpacker.unpack(str);  // str is "Hi!"
810      * -----
811      *
812      * Returns:
813      *  the container size.
814      */
815     @safe
816     size_t beginArray()
817     {
818         canRead(Offset, 0);
819         const  header = read();
820         size_t length;
821 
822         if (0x90 <= header && header <= 0x9f) {
823             length = header & 0x0f;
824         } else {
825             switch (header) {
826             case Format.ARRAY16:
827                 canRead(ushort.sizeof);
828                 length = load16To!size_t(read(ushort.sizeof));
829                 break;
830             case Format.ARRAY32:
831                 canRead(uint.sizeof);
832                 length = load32To!size_t(read(uint.sizeof));
833                 break;
834             case Format.NIL:
835                 break;
836             default:
837                 rollback(0, "array", cast(Format)header);
838             }
839         }
840 
841         return length;
842     }
843 
844 
845     /// ditto
846     @safe
847     size_t beginMap()
848     {
849         canRead(Offset, 0);
850         const  header = read();
851         size_t length;
852 
853         if (0x80 <= header && header <= 0x8f) {
854             length = header & 0x0f;
855         } else {
856             switch (header) {
857             case Format.MAP16:
858                 canRead(ushort.sizeof);
859                 length = load16To!size_t(read(ushort.sizeof));
860                 break;
861             case Format.MAP32:
862                 canRead(uint.sizeof);
863                 length = load32To!size_t(read(uint.sizeof));
864                 break;
865             case Format.NIL:
866                 break;
867             default:
868                 rollback(0, "map", cast(Format)header);
869             }
870         }
871 
872         return length;
873     }
874 
875 
876     /**
877      * Unpacks an EXT value into $(D type) and $(D data).
878      * $(D type) is checked and a $(D MessagePackException) is thrown if it does
879      *  not match. The length of $(D data) is checked and a $(D MessagePackException)
880      *  is thrown if the lengths do not match.  If $(D data) is null, a new slice
881      *  is returned.
882      */
883     ref Unpacker unpackExt(ref byte type, ref ubyte[] data)
884     {
885         import std.conv : text;
886 
887         canRead(Offset, 0);
888         const header = read();
889 
890         uint length;
891         uint rollbackLength = 0;
892         if (header >= Format.EXT && header <= Format.EXT + 4)
893         {
894             // Fixed
895             length = 2^^(header - Format.EXT);
896 
897         } else {
898             // Dynamic length
899             switch (header) with (Format)
900             {
901                 case EXT8:
902                     canRead(1);
903                     length = read();
904                     rollbackLength = 1;
905                     break;
906                 case EXT16:
907                     canRead(2);
908                     length = load16To!ushort(read(2));
909                     rollbackLength = 2;
910                     break;
911                 case EXT32:
912                     canRead(4);
913                     length = load32To!uint(read(4));
914                     rollbackLength = 4;
915                     break;
916                 default:
917                     rollback(0, "ext", cast(Format)header);
918             }
919 
920         }
921 
922         canRead(1 + length);
923 
924         // Read and check the type
925         byte type_ = read();
926         rollbackLength += 1;
927         if (type_ != type)
928             rollback(rollbackLength, text("Cannot unpack EXT of type ", type_, " into type ", type));
929 
930         // Read and check data
931         if (data is null)
932             data = new ubyte[](length);
933         else if (data.length != length) {
934             rollback(rollbackLength, text("Length mismatch while unpacking EXT: ", data.length, " was given, actual length is ", length));
935         }
936         data[] = read(length);
937         return this;
938     }
939 
940     /**
941      * Scans an entire buffer and converts each objects.
942      *
943      * This method is used for unpacking record-like objects.
944      *
945      * Example:
946      * -----
947      * // serialized data is "[1, 2][3, 4][5, 6][...".
948      * auto unpacker = Unpacker(serializedData);
949      * foreach (n, d; &unpacker.scan!(int, int))  // == "foreach (int n, int d; unpacker)"
950      *     writeln(n, d); // 1st loop "1, 2", 2nd loop "3, 4"...
951      * -----
952      */
953     int scan(Types...)(scope int delegate(ref Types) dg)
954     {
955         return opApply!(Types)(delegate int(ref Types objects) { return dg(objects); });
956     }
957 
958 
959     /// ditto
960     int opApply(Types...)(scope int delegate(ref Types) dg)
961     {
962         int result;
963 
964         while (used_ - offset_) {
965             auto length = beginArray();
966             if (length != Types.length)
967                 rollback(calculateSize(length), "the number of deserialized objects is mismatched");
968 
969             Types objects;
970             foreach (i, T; Types)
971                 unpack(objects[i]);
972 
973             result = dg(objects);
974             if (result)
975                 return result;
976         }
977 
978         return result;
979     }
980 
981 
982   private:
983     /*
984      * Deserializes nil object and assigns to $(D_PARAM value).
985      *
986      * Params:
987      *  value = the reference of value to assign.
988      *
989      * Returns:
990      *  self, i.e. for method chaining.
991      *
992      * Throws:
993      *  UnpackException when doesn't read from buffer or precision loss occurs and
994      *  MessagePackException when $(D_PARAM T) type doesn't match serialized type.
995      */
996     @safe
997     ref Unpacker unpackNil(T)(ref T value)
998     {
999         canRead(Offset, 0);
1000         const header = read();
1001 
1002         if (header == Format.NIL)
1003             value = null;
1004         else
1005             rollback(0, "nil", cast(Format)header);
1006 
1007         return this;
1008     }
1009 
1010 
1011     /*
1012      * Next object is nil?
1013      *
1014      * Returns:
1015      *  true if next object is nil.
1016      */
1017     @safe
1018     bool checkNil()
1019     {
1020         canRead(Offset, 0);
1021 
1022         return buffer_[offset_] == Format.NIL;
1023     }
1024 
1025 
1026     /*
1027      * Calculates the format size of container length.
1028      */
1029     size_t calculateSize(bool rawType = false)(in size_t length)
1030     {
1031         static if (rawType)
1032             return length < 32 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof;
1033         else
1034             return length < 16 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof;
1035     }
1036 
1037 
1038     /*
1039      * Reading test.
1040      *
1041      * Params:
1042      *  size   = the size to read.
1043      *  offset = the offset to subtract when doesn't read from buffer.
1044      *
1045      * Throws:
1046      *  UnpackException when doesn't read from buffer.
1047      */
1048     @safe
1049     void canRead(in size_t size, in size_t offset = Offset)
1050     {
1051         if (used_ - offset_ < size) {
1052             if (offset)
1053                 offset_ -= offset;
1054 
1055             throw new UnpackException("Insufficient buffer");
1056         }
1057     }
1058 
1059 
1060     /*
1061      * Reads value from buffer and advances offset.
1062      */
1063     @safe
1064     nothrow ubyte read()
1065     {
1066         return buffer_[offset_++];
1067     }
1068 
1069 
1070     /*
1071      * Reads value from buffer and advances offset.
1072      */
1073     @safe
1074     nothrow ubyte[] read(in size_t size)
1075     {
1076         auto result = buffer_[offset_..offset_ + size];
1077 
1078         offset_ += size;
1079 
1080         return result;
1081     }
1082 
1083 
1084     /*
1085      * Do rollback and throws exception.
1086      */
1087     @safe
1088     void rollback(in size_t size, in string reason)
1089     {
1090         offset_ -= size + Offset;
1091         onInvalidType(reason);
1092     }
1093 
1094     @safe
1095     void rollback(in size_t size, in string expected, in Format actual)
1096     {
1097         offset_ -= size + Offset;
1098         onInvalidType(expected, actual);
1099     }
1100 }
1101 
1102 
1103 private:
1104 
1105 
1106 /*
1107  * A callback for type-mismatched error in deserialization process.
1108  */
1109 @safe
1110 pure void onInvalidType(in string reason)
1111 {
1112     throw new MessagePackException("Attempt to unpack with non-compatible type: reason = " ~ reason);
1113 }
1114 
1115 @safe
1116 pure void onInvalidType(in string expected, in Format actual)
1117 {
1118     import std.conv: text;
1119     throw new MessagePackException(text("Attempt to unpack with non-compatible type: expected = ", expected, ", actual = ", actual));
1120 }
1121 
1122 
1123 unittest
1124 {
1125     import msgpack.packer;
1126 
1127     { // unique
1128         mixin DefinePacker;
1129 
1130         Tuple!(bool, bool) result;
1131         Tuple!(bool, bool) test = tuple(true, false);
1132 
1133         packer.pack(test);
1134 
1135         auto unpacker = Unpacker(packer.stream.data);
1136 
1137         unpacker.unpack(result);
1138         assert(test == result);
1139     }
1140     { // uint *
1141         mixin DefinePacker;
1142 
1143         Tuple!(ubyte, ushort, uint, ulong) result;
1144         Tuple!(ubyte, ushort, uint, ulong) test = tuple(cast(ubyte)ubyte.max, cast(ushort)ushort.max,
1145                                                         cast(uint)uint.max,   cast(ulong)ulong.max);
1146 
1147         packer.pack(test);
1148 
1149         auto unpacker = Unpacker(packer.stream.data);
1150 
1151         unpacker.unpack(result);
1152         assert(test == result);
1153     }
1154     { // int *
1155         mixin DefinePacker;
1156 
1157         Tuple!(byte, short, int, long) result;
1158         Tuple!(byte, short, int, long) test = tuple(cast(byte)byte.min, cast(short)short.min,
1159                                                     cast(int)int.min,   cast(long)long.min);
1160 
1161         packer.pack(test);
1162 
1163         auto unpacker = Unpacker(packer.stream.data);
1164 
1165         unpacker.unpack(result);
1166         assert(test == result);
1167     }
1168     { // floating point
1169         mixin DefinePacker;
1170 
1171         static if (real.sizeof == double.sizeof || !EnableReal)
1172         {
1173             Tuple!(float, double, double) result;
1174             Tuple!(float, double, double) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)double.min_normal);
1175         }
1176         else
1177         {
1178             Tuple!(float, double, real) result;
1179             Tuple!(float, double, real) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)real.min_normal);
1180         }
1181 
1182         packer.pack(test);
1183 
1184         auto unpacker = Unpacker(packer.stream.data);
1185 
1186         unpacker.unpack(result);
1187         assert(test == result);
1188     }
1189     { // pointer
1190         mixin DefinePacker;
1191 
1192         Tuple!(ulong, long, double) origin;
1193         Tuple!(ulong, long, double) values = tuple(ulong.max, long.min, double.min_normal);
1194         Tuple!(ulong*, long*, double*) result = tuple(&origin.field[0], &origin.field[1], &origin.field[2]);
1195         Tuple!(ulong*, long*, double*) test = tuple(&values.field[0], &values.field[1], &values.field[2]);
1196 
1197         packer.pack(test);
1198 
1199         auto unpacker = Unpacker(packer.stream.data);
1200 
1201         unpacker.unpack(result);
1202         foreach (i, v; test.field)
1203             assert(*v == *result.field[i]);
1204         assert(origin == values);
1205     }
1206     { // enum
1207         enum   : float { D = 0.5 }
1208         enum E : ulong { U = 100 }
1209 
1210         mixin DefinePacker;
1211 
1212         float f = D,   resultF;
1213         E     e = E.U, resultE;
1214 
1215         packer.pack(D, e);
1216 
1217         auto unpacker = Unpacker(packer.stream.data);
1218 
1219         unpacker.unpack(resultF, resultE);
1220         assert(f == resultF);
1221         assert(e == resultE);
1222     }
1223     { // container
1224         mixin DefinePacker;
1225 
1226         Tuple!(ulong[], double[uint], string, bool[2], char[2]) test
1227             = tuple([1UL, 2], [3U:4.0, 5:6.0, 7:8.0], "MessagePack is nice!", [true, false], "D!");
1228 
1229         packer.pack(test);
1230 
1231         auto unpacker = Unpacker(packer.stream.data);
1232         Tuple!(ulong[], double[uint], string, bool[2], char[2]) result;
1233 
1234         unpacker.unpack(result);
1235         assert(test == result);
1236     }
1237     { // ext
1238 
1239         // Try a variety of lengths, making sure to hit all the fixexts
1240         foreach (L; TypeTuple!(1, 2, 3, 4, 5, 8, 9, 16, 32, 512, 2^^16))
1241         {
1242             mixin DefinePacker;
1243 
1244             ubyte[] data = new ubyte[](L);
1245             ExtValue ext = ExtValue(7, data);
1246             packer.pack(ext);
1247 
1248             auto unpacker1 = Unpacker(packer.stream.data);
1249             ExtValue witness;
1250 
1251             unpacker1.unpack(witness);
1252             assert(ext == witness);
1253 
1254             // And try unpackExt
1255             auto unpacker2 = Unpacker(packer.stream.data);
1256             byte type = 1;
1257             ubyte[] deserializedData = new ubyte[](7);
1258 
1259             // This should be a type mismatch (1 != 7)
1260             assertThrown!MessagePackException(
1261                 unpacker2.unpackExt(type, deserializedData));
1262             type = 7;
1263 
1264             // A data size mismatch
1265             assertThrown!MessagePackException(
1266                 unpacker2.unpackExt(type, deserializedData));
1267             deserializedData = new ubyte[](L);
1268 
1269             // And this should succeed
1270             unpacker2.unpackExt(type, deserializedData);
1271             assert(deserializedData == data);
1272         }
1273     }
1274     { // user defined
1275         {
1276             static struct S
1277             {
1278                 uint num;
1279 
1280                 void toMsgpack(P)(ref P p) const { p.packArray(num); }
1281                 void fromMsgpack(ref Unpacker u)
1282                 {
1283                     assert(u.beginArray() == 1);
1284                     u.unpack(num);
1285                 }
1286             }
1287 
1288             mixin DefinePacker; S result, test = S(uint.max);
1289 
1290             packer.pack(test);
1291 
1292             auto unpacker = Unpacker(packer.stream.data);
1293             unpacker.unpack(result);
1294 
1295             assert(test.num == result.num);
1296         }
1297         {
1298             static class C
1299             {
1300                 uint num;
1301 
1302                 this(uint n) { num = n; }
1303 
1304                 void toMsgpack(P)(ref P p) const { p.packArray(num - 1); }
1305                 void fromMsgpack(ref Unpacker u)
1306                 {
1307                     assert(u.beginArray() == 1);
1308                     u.unpack(num);
1309                 }
1310             }
1311 
1312             mixin DefinePacker; C result, test = new C(ushort.max);
1313 
1314             packer.pack(test);
1315 
1316             auto unpacker = Unpacker(packer.stream.data);
1317             unpacker.unpack(result, ushort.max);
1318 
1319             assert(test.num == result.num + 1);
1320         }
1321     }
1322     { // simple struct and class
1323         {
1324             static struct Simple
1325             {
1326                 uint num;
1327                 @nonPacked string str;
1328             }
1329 
1330             static struct Simple2
1331             {
1332                 @nonPacked string str;
1333                 uint num;
1334             }
1335 
1336             foreach (Type; TypeTuple!(Simple, Simple2)) {
1337                 mixin DefinePacker;
1338                 Type result, test;
1339                 test.num = uint.max;
1340                 test.str = "ignored";
1341 
1342                 packer.pack(test);
1343                 auto unpacker = Unpacker(packer.stream.data);
1344                 unpacker.unpack(result);
1345 
1346                 assert(test.num == result.num);
1347                 assert(test.str != result.str);
1348             }
1349         }
1350 
1351         static class SimpleA
1352         {
1353             bool flag = true;
1354         }
1355 
1356         static class SimpleB : SimpleA
1357         {
1358             ubyte type = 100;
1359         }
1360 
1361         static class SimpleC : SimpleB
1362         {
1363             uint num = uint.max;
1364             @nonPacked string str;
1365         }
1366 
1367         static class SimpleC2 : SimpleB
1368         {
1369             @nonPacked string str;
1370             uint num = uint.max;
1371         }
1372 
1373         { // from derived class
1374             foreach (Type; TypeTuple!(SimpleC, SimpleC2)) {
1375                 mixin DefinePacker;
1376                 Type result, test = new Type();
1377                 test.flag = false;
1378                 test.type = 99;
1379                 test.num  = uint.max / 2;
1380                 test.str  = "ignored";
1381 
1382                 packer.pack(test);
1383                 auto unpacker = Unpacker(packer.stream.data);
1384                 unpacker.unpack(result);
1385 
1386                 assert(test.flag == result.flag);
1387                 assert(test.type == result.type);
1388                 assert(test.num  == result.num);
1389                 assert(test.str  != result.str);
1390             }
1391         }
1392         { // from base class
1393             mixin DefinePacker; SimpleC test = new SimpleC();
1394 
1395             packer.pack(test);
1396 
1397             SimpleB result = new SimpleC();
1398             auto unpacker  = Unpacker(packer.stream.data);
1399 
1400             try {
1401                 unpacker.unpack(result);
1402                 assert(false);
1403             } catch (Exception e) { }
1404         }
1405         { // https://github.com/msgpack/msgpack-d/issues/16
1406             mixin DefinePacker;
1407 
1408             static class Issue16
1409             {
1410                 int i;
1411                 this(int i) { this.i = i; }
1412             }
1413 
1414             Issue16 c1 = new Issue16(10);
1415 
1416             // change behaviour to accept null with new object without constructor
1417             Issue16 c2 = null;
1418             packer.pack(c1);
1419             auto unpacker1 = Unpacker(packer.stream.data);
1420             unpacker1.unpack(c2);
1421             //unpack(pack(c1), c2);
1422             assert(c2.i == c1.i);
1423 
1424             Issue16 c3 = new Issue16(20);
1425             packer.stream.clear();
1426             packer.pack(c1);
1427             auto unpacker2 = Unpacker(packer.stream.data);
1428             unpacker2.unpack(c3);
1429             //unpack(pack(c1), c3);
1430             assert(c3.i == c1.i);
1431         }
1432     }
1433     { // variadic
1434         mixin DefinePacker;
1435 
1436         Tuple!(uint, long, double) test = tuple(uint.max, long.min, double.max);
1437 
1438         packer.pack(test);
1439 
1440         auto unpacker = Unpacker(packer.stream.data);
1441 
1442         uint u; long l; double d;
1443 
1444         unpacker.unpackArray(u, l, d);
1445         assert(test == tuple(u, l, d));
1446     }
1447     { // scan / opApply
1448         ubyte[] data;
1449         mixin DefinePacker;
1450 
1451         foreach (i; 0..2)
1452             packer.pack(tuple(1, 0.5, "Hi!"));
1453 
1454         foreach (n, d, s; &Unpacker(packer.stream.data).scan!(int, double, string)) {
1455             assert(n == 1);
1456             assert(d == 0.5);
1457             assert(s == "Hi!");
1458         }
1459     }
1460 }