1 module msgpack.packer;
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 /**
18  * $(D Packer) is a $(D MessagePack) serializer
19  *
20  * Example:
21  * -----
22  * auto packer = packer(Appender!(ubyte[])());
23  *
24  * packer.packArray(false, 100, 1e-10, null);
25  *
26  * stdout.rawWrite(packer.stream.data);
27  * -----
28  *
29  * NOTE:
30  *  Current implementation can't deal with a circular reference.
31  *  If you try to serialize a object that has circular reference, runtime raises 'Stack Overflow'.
32  */
33 struct PackerImpl(Stream) if (isOutputRange!(Stream, ubyte) && isOutputRange!(Stream, ubyte[]))
34 {
35   private:
36     static @system
37     {
38         alias void delegate(ref PackerImpl, void*) PackHandler;
39         PackHandler[TypeInfo] packHandlers;
40 
41         public void registerHandler(T, alias Handler)()
42         {
43             packHandlers[typeid(T)] = delegate(ref PackerImpl packer, void* obj) {
44                 Handler(packer, *cast(T*)obj);
45             };
46         }
47 
48         public void register(T)()
49         {
50             packHandlers[typeid(T)] = delegate(ref PackerImpl packer, void* obj) {
51                 packer.packObject(*cast(T*)obj);
52             };
53         }
54     }
55 
56     enum size_t Offset = 1;  // type-information offset
57 
58     Stream                   stream_;  // the stream to write
59     ubyte[Offset + RealSize] store_;   // stores serialized value
60     bool                     withFieldName_;
61 
62 
63   public:
64     /**
65      * Constructs a packer with $(D_PARAM stream).
66      *
67      * Params:
68      *  stream        = the stream to write.
69      *  withFieldName = serialize class / struct with field name
70      */
71     this(Stream stream, bool withFieldName = false)
72     {
73         stream_        = stream;
74         withFieldName_ = withFieldName;
75     }
76 
77 
78     /**
79      * Constructs a packer with $(D_PARAM withFieldName).
80      *
81      * Params:
82      *  withFieldName = serialize class / struct with field name
83      */
84     this(bool withFieldName)
85     {
86         withFieldName_ = withFieldName;
87     }
88 
89 
90     /**
91      * Forwards to stream.
92      *
93      * Returns:
94      *  the stream.
95      */
96     @property @safe
97     nothrow ref Stream stream()
98     {
99         return stream_;
100     }
101 
102 
103     /**
104      * Serializes argument and writes to stream.
105      *
106      * If the argument is the pointer type, dereferences the pointer and serializes pointed value.
107      * -----
108      * int  a = 10;
109      * int* b = &b;
110      *
111      * packer.pack(b);  // serializes 10, not address of a
112      * -----
113      * Serializes nil if the argument of nullable type is null.
114      *
115      * NOTE:
116      *  MessagePack doesn't define $(D_KEYWORD real) type format.
117      *  Don't serialize $(D_KEYWORD real) if you communicate with other languages.
118      *  Transfer $(D_KEYWORD double) serialization if $(D_KEYWORD real) on your environment equals $(D_KEYWORD double).
119      *
120      * Params:
121      *  value = the content to serialize.
122      *
123      * Returns:
124      *  self, i.e. for method chaining.
125      */
126     ref PackerImpl pack(T)(in T value) if (is(Unqual!T == bool))
127     {
128         if (value)
129             stream_.put(Format.TRUE);
130         else
131             stream_.put(Format.FALSE);
132 
133         return this;
134     }
135 
136 
137     /// ditto
138     ref PackerImpl pack(T)(in T value) if (isUnsigned!T && !is(Unqual!T == enum))
139     {
140         // ulong < ulong is slower than uint < uint
141         static if (!is(Unqual!T  == ulong)) {
142             enum Bits = T.sizeof * 8;
143 
144             if (value < (1 << 8)) {
145                 if (value < (1 << 7)) {
146                     // fixnum
147                     stream_.put(take8from!Bits(value));
148                 } else {
149                     // uint 8
150                     store_[0] = Format.UINT8;
151                     store_[1] = take8from!Bits(value);
152                     stream_.put(store_[0..Offset + ubyte.sizeof]);
153                 }
154             } else {
155                 if (value < (1 << 16)) {
156                     // uint 16
157                     const temp = convertEndianTo!16(value);
158 
159                     store_[0] = Format.UINT16;
160                     *cast(ushort*)&store_[Offset] = temp;
161                     stream_.put(store_[0..Offset + ushort.sizeof]);
162                 } else {
163                     // uint 32
164                     const temp = convertEndianTo!32(value);
165 
166                     store_[0] = Format.UINT32;
167                     *cast(uint*)&store_[Offset] = temp;
168                     stream_.put(store_[0..Offset + uint.sizeof]);
169                 }
170             }
171         } else {
172             if (value < (1UL << 8)) {
173                 if (value < (1UL << 7)) {
174                     // fixnum
175                     stream_.put(take8from!64(value));
176                 } else {
177                     // uint 8
178                     store_[0] = Format.UINT8;
179                     store_[1] = take8from!64(value);
180                     stream_.put(store_[0..Offset + ubyte.sizeof]);
181                 }
182             } else {
183                 if (value < (1UL << 16)) {
184                     // uint 16
185                     const temp = convertEndianTo!16(value);
186 
187                     store_[0] = Format.UINT16;
188                     *cast(ushort*)&store_[Offset] = temp;
189                     stream_.put(store_[0..Offset + ushort.sizeof]);
190                 } else if (value < (1UL << 32)){
191                     // uint 32
192                     const temp = convertEndianTo!32(value);
193 
194                     store_[0] = Format.UINT32;
195                     *cast(uint*)&store_[Offset] = temp;
196                     stream_.put(store_[0..Offset + uint.sizeof]);
197                 } else {
198                     // uint 64
199                     const temp = convertEndianTo!64(value);
200 
201                     store_[0] = Format.UINT64;
202                     *cast(ulong*)&store_[Offset] = temp;
203                     stream_.put(store_[0..Offset + ulong.sizeof]);
204                 }
205             }
206         }
207 
208         return this;
209     }
210 
211 
212     /// ditto
213     ref PackerImpl pack(T)(in T value) if (isSigned!T && isIntegral!T && !is(Unqual!T == enum))
214     {
215         // long < long is slower than int < int
216         static if (!is(Unqual!T == long)) {
217             enum Bits = T.sizeof * 8;
218 
219             if (value < -(1 << 5)) {
220                 if (value < -(1 << 15)) {
221                     // int 32
222                     const temp = convertEndianTo!32(value);
223 
224                     store_[0] = Format.INT32;
225                     *cast(int*)&store_[Offset] = temp;
226                     stream_.put(store_[0..Offset + int.sizeof]);
227                 } else if (value < -(1 << 7)) {
228                     // int 16
229                     const temp = convertEndianTo!16(value);
230 
231                     store_[0] = Format.INT16;
232                     *cast(short*)&store_[Offset] = temp;
233                     stream_.put(store_[0..Offset + short.sizeof]);
234                 } else {
235                     // int 8
236                     store_[0] = Format.INT8;
237                     store_[1] = take8from!Bits(value);
238                     stream_.put(store_[0..Offset + byte.sizeof]);
239                 }
240             } else if (value < (1 << 7)) {
241                 // fixnum
242                 stream_.put(take8from!Bits(value));
243             } else {
244                 if (value < (1 << 8)) {
245                     // uint 8
246                     store_[0] = Format.UINT8;
247                     store_[1] = take8from!Bits(value);
248                     stream_.put(store_[0..Offset + ubyte.sizeof]);
249                 } else if (value < (1 << 16)) {
250                     // uint 16
251                     const temp = convertEndianTo!16(value);
252 
253                     store_[0] = Format.UINT16;
254                     *cast(ushort*)&store_[Offset] = temp;
255                     stream_.put(store_[0..Offset + ushort.sizeof]);
256                 } else {
257                     // uint 32
258                     const temp = convertEndianTo!32(value);
259 
260                     store_[0] = Format.UINT32;
261                     *cast(uint*)&store_[Offset] = temp;
262                     stream_.put(store_[0..Offset + uint.sizeof]);
263                 }
264             }
265         } else {
266             if (value < -(1L << 5)) {
267                 if (value < -(1L << 15)) {
268                     if (value < -(1L << 31)) {
269                         // int 64
270                         const temp = convertEndianTo!64(value);
271 
272                         store_[0] = Format.INT64;
273                         *cast(long*)&store_[Offset] = temp;
274                         stream_.put(store_[0..Offset + long.sizeof]);
275                     } else {
276                         // int 32
277                         const temp = convertEndianTo!32(value);
278 
279                         store_[0] = Format.INT32;
280                         *cast(int*)&store_[Offset] = temp;
281                         stream_.put(store_[0..Offset + int.sizeof]);
282                     }
283                 } else {
284                     if (value < -(1L << 7)) {
285                         // int 16
286                         const temp = convertEndianTo!16(value);
287 
288                         store_[0] = Format.INT16;
289                         *cast(short*)&store_[Offset] = temp;
290                         stream_.put(store_[0..Offset + short.sizeof]);
291                     } else {
292                         // int 8
293                         store_[0] = Format.INT8;
294                         store_[1] = take8from!64(value);
295                         stream_.put(store_[0..Offset + byte.sizeof]);
296                     }
297                 }
298             } else if (value < (1L << 7)) {
299                 // fixnum
300                 stream_.put(take8from!64(value));
301             } else {
302                 if (value < (1L << 16)) {
303                     if (value < (1L << 8)) {
304                         // uint 8
305                         store_[0] = Format.UINT8;
306                         store_[1] = take8from!64(value);
307                         stream_.put(store_[0..Offset + ubyte.sizeof]);
308                     } else {
309                         // uint 16
310                         const temp = convertEndianTo!16(value);
311 
312                         store_[0] = Format.UINT16;
313                         *cast(ushort*)&store_[Offset] = temp;
314                         stream_.put(store_[0..Offset + ushort.sizeof]);
315                     }
316                 } else {
317                     if (value < (1L << 32)) {
318                         // uint 32
319                         const temp = convertEndianTo!32(value);
320 
321                         store_[0] = Format.UINT32;
322                         *cast(uint*)&store_[Offset] = temp;
323                         stream_.put(store_[0..Offset + uint.sizeof]);
324                     } else {
325                         // uint 64
326                         const temp = convertEndianTo!64(value);
327 
328                         store_[0] = Format.UINT64;
329                         *cast(ulong*)&store_[Offset] = temp;
330                         stream_.put(store_[0..Offset + ulong.sizeof]);
331                     }
332                 }
333             }
334         }
335 
336         return this;
337     }
338 
339 
340     /// ditto
341     ref PackerImpl pack(T)(in T value) if (isSomeChar!T && !is(Unqual!T == enum))
342     {
343         static if (is(Unqual!T == char)) {
344             return pack(cast(ubyte)(value));
345         } else static if (is(Unqual!T == wchar)) {
346             return pack(cast(ushort)(value));
347         } else static if (is(Unqual!T == dchar)) {
348             return pack(cast(uint)(value));
349         }
350     }
351 
352 
353     /// ditto
354     ref PackerImpl pack(T)(in T value) if (isFloatingPoint!T && !is(Unqual!T == enum))
355     {
356         static if (is(Unqual!T == float)) {
357             const temp = convertEndianTo!32(_f(value).i);
358 
359             store_[0] = Format.FLOAT;
360             *cast(uint*)&store_[Offset] = temp;
361             stream_.put(store_[0..Offset + uint.sizeof]);
362         } else static if (is(Unqual!T == double)) {
363             const temp = convertEndianTo!64(_d(value).i);
364 
365             store_[0] = Format.DOUBLE;
366             *cast(ulong*)&store_[Offset] = temp;
367             stream_.put(store_[0..Offset + ulong.sizeof]);
368         } else {
369             static if ((real.sizeof > double.sizeof) && EnableReal) {
370                 store_[0]      = Format.REAL;
371                 const temp     = _r(value);
372                 const fraction = convertEndianTo!64(temp.fraction);
373                 const exponent = convertEndianTo!16(temp.exponent);
374 
375                 *cast(Unqual!(typeof(fraction))*)&store_[Offset]                   = fraction;
376                 *cast(Unqual!(typeof(exponent))*)&store_[Offset + fraction.sizeof] = exponent;
377                 stream_.put(store_[0..$]);
378             } else {  // Non-x86 CPUs, real type equals double type.
379                 pack(cast(double)value);
380             }
381         }
382 
383         return this;
384     }
385 
386 
387     /// ditto
388     ref PackerImpl pack(T)(in T value) if (is(Unqual!T == enum))
389     {
390         pack(cast(OriginalType!T)value);
391 
392         return this;
393     }
394 
395 
396     /// Overload for pack(null) for 2.057 or later
397     static if (!is(typeof(null) == void*))
398     {
399         ref PackerImpl pack(T)(in T value) if (is(Unqual!T == typeof(null)))
400         {
401             return packNil();
402         }
403     }
404 
405 
406     /// ditto
407     ref PackerImpl pack(T)(in T value) if (isPointer!T)
408     {
409         static if (is(Unqual!T == void*)) {  // for pack(null) for 2.056 or earlier
410             enforce(value is null, "Can't serialize void type");
411             stream_.put(Format.NIL);
412         } else {
413             if (value is null)
414                 stream_.put(Format.NIL);
415             else
416                 pack(mixin(AsteriskOf!T ~ "value"));
417         }
418 
419         return this;
420     }
421 
422 
423     /// ditto
424     ref PackerImpl pack(T)(in T array) if ((isArray!T || isInstanceOf!(Array, T)) && !is(Unqual!T == enum))
425     {
426         alias typeof(T.init[0]) U;
427 
428         if (array.empty)
429             return packNil();
430 
431         // Raw bytes
432         static if (isByte2!(U) || isSomeChar!(U)) {
433             ubyte[] raw = cast(ubyte[])array;
434 
435             beginRaw(raw.length);
436             stream_.put(raw);
437         } else {
438             beginArray(array.length);
439             foreach (elem; array)
440                 pack(elem);
441         }
442 
443         return this;
444     }
445 
446 
447     /// ditto
448     ref PackerImpl pack(T)(in T array) if (isAssociativeArray!T)
449     {
450         if (array is null)
451             return packNil();
452 
453         beginMap(array.length);
454         foreach (key, value; array) {
455             pack(key);
456             pack(value);
457         }
458 
459         return this;
460     }
461 
462 
463     /// ditto
464     ref PackerImpl pack(Types...)(auto ref const Types objects) if (Types.length > 1)
465     {
466         foreach (i, T; Types)
467             pack(objects[i]);
468 
469         return this;
470     }
471 
472 
473     /**
474      * Serializes $(D_PARAM object) and writes to stream.
475      *
476      * Calling $(D toMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D toMsgpack) method. $(D toMsgpack) signature is:
477      * -----
478      * void toMsgpack(Packer)(ref Packer packer) const
479      * -----
480      * This method serializes all members of T object if $(D_KEYWORD class) and $(D_KEYWORD struct) don't implement $(D toMsgpack).
481      *
482      * An object that doesn't implement $(D toMsgpack) is serialized to Array type.
483      * -----
484      * packer.pack(tuple(true, 1, "Hi!"))  // -> '[true, 1, "Hi!"]', not 'ture, 1, "Hi!"'
485      *
486      * struct Foo
487      * {
488      *     int num    = 10;
489      *     string msg = "D!";
490      * }
491      * packer.pack(Foo());  // -> '[10, "D!"]'
492      *
493      * class Base
494      * {
495      *     bool flag = true;
496      * }
497      * class Derived : Base
498      * {
499      *     double = 0.5f;
500      * }
501      * packer.pack(new Derived());  // -> '[true, 0.5f]'
502      * -----
503      *
504      * Params:
505      *  object = the content to serialize.
506      *
507      * Returns:
508      *  self, i.e. for method chaining.
509      */
510     ref PackerImpl pack(T)(in T object) if (is(Unqual!T == class))
511     {
512         if (object is null)
513             return packNil();
514 
515         static if (hasMember!(T, "toMsgpack"))
516         {
517             static if (__traits(compiles, { object.toMsgpack(this, withFieldName_); })) {
518                 object.toMsgpack(this, withFieldName_);
519             } else static if (__traits(compiles, { object.toMsgpack(this); })) { // backward compatible
520                 object.toMsgpack(this);
521             } else {
522                 static assert(0, "Failed to invoke 'toMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
523             }
524         } else {
525             if (auto handler = object.classinfo in packHandlers) {
526                 (*handler)(this, cast(void*)&object);
527                 return this;
528             }
529             if (T.classinfo !is object.classinfo) {
530                 throw new MessagePackException("Can't pack derived class through reference to base class.");
531             }
532 
533             packObject!(T)(object);
534         }
535 
536         return this;
537     }
538 
539 
540     /// ditto
541     @trusted
542     ref PackerImpl pack(T)(auto ref T object) if (is(Unqual!T == struct) &&
543                                                   !isInstanceOf!(Array, T) &&
544                                                   !is(Unqual!T == ExtValue))
545     {
546         static if (hasMember!(T, "toMsgpack"))
547         {
548             static if (__traits(compiles, { object.toMsgpack(this, withFieldName_); })) {
549                 object.toMsgpack(this, withFieldName_);
550             } else static if (__traits(compiles, { object.toMsgpack(this); })) { // backward compatible
551                 object.toMsgpack(this);
552             } else {
553                 static assert(0, "Failed to invoke 'toMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
554             }
555         } else static if (isTuple!T) {
556             beginArray(object.field.length);
557             foreach (f; object.field)
558                 pack(f);
559         } else {  // simple struct
560             if (auto handler = typeid(Unqual!T) in packHandlers) {
561                 (*handler)(this, cast(void*)&object);
562                 return this;
563             }
564 
565             immutable memberNum = SerializingMemberNumbers!(T);
566             if (withFieldName_)
567                 beginMap(memberNum);
568             else
569                 beginArray(memberNum);
570 
571             if (withFieldName_) {
572                 foreach (i, f; object.tupleof) {
573                     static if (isPackedField!(T.tupleof[i]) && __traits(compiles, { pack(f); }))
574                     {
575                         pack(getFieldName!(T, i));
576                         pack(f);
577                     }
578                 }
579             } else {
580                 foreach (i, f; object.tupleof) {
581                     static if (isPackedField!(T.tupleof[i]) && __traits(compiles, { pack(f); }))
582                         pack(f);
583                 }
584             }
585         }
586 
587         return this;
588     }
589 
590 
591     void packObject(T)(in T object) if (is(Unqual!T == class))
592     {
593         alias SerializingClasses!(T) Classes;
594 
595         immutable memberNum = SerializingMemberNumbers!(Classes);
596         if (withFieldName_)
597             beginMap(memberNum);
598         else
599             beginArray(memberNum);
600 
601         foreach (Class; Classes) {
602             Class obj = cast(Class)object;
603             if (withFieldName_) {
604                 foreach (i, f ; obj.tupleof) {
605                     static if (isPackedField!(Class.tupleof[i])) {
606                         pack(getFieldName!(Class, i));
607                         pack(f);
608                     }
609                 }
610             } else {
611                 foreach (i, f ; obj.tupleof) {
612                     static if (isPackedField!(Class.tupleof[i]))
613                         pack(f);
614                 }
615             }
616         }
617     }
618 
619 
620     /**
621      * Serializes the arguments as container to stream.
622      *
623      * -----
624      * packer.packArray(true, 1);  // -> [true, 1]
625      * packer.packMap("Hi", 100);  // -> ["Hi":100]
626      * -----
627      *
628      * In packMap, the number of arguments must be even.
629      *
630      * Params:
631      *  objects = the contents to serialize.
632      *
633      * Returns:
634      *  self, i.e. for method chaining.
635      */
636     ref PackerImpl packArray(Types...)(auto ref const Types objects)
637     {
638         beginArray(Types.length);
639         foreach (i, T; Types)
640             pack(objects[i]);
641         //pack(objects);  // slow :(
642 
643         return this;
644     }
645 
646 
647     /// ditto
648     ref PackerImpl packMap(Types...)(auto ref const Types objects)
649     {
650         static assert(Types.length % 2 == 0, "The number of arguments must be even");
651 
652         beginMap(Types.length / 2);
653         foreach (i, T; Types)
654             pack(objects[i]);
655 
656         return this;
657     }
658 
659     /**
660      * Packs $(D data) as an extended value of $(D type).
661      *
662      * ----
663      * packer.packExt(3, bytes);
664      * ----
665      *
666      * $(D type) must be a signed byte 0-127.
667      *
668      * Params:
669      *  type = the application-defined type for the data
670      *  data = an array of bytes
671      *
672      * Returns:
673      *  seld, i.e. for method chaining.
674      */
675     ref PackerImpl pack(T)(auto ref const T data) if (is(Unqual!T == ExtValue))
676     {
677         packExt(data.type, data.data);
678         return this;
679     }
680 
681     /**
682      * Packs $(D data) as an extended value of $(D type).
683      *
684      * ----
685      * packer.packExt(3, bytes);
686      * ----
687      *
688      * $(D type) must be a signed byte 0-127.
689      *
690      * Params:
691      *  type = the application-defined type for the data
692      *  data = an array of bytes
693      *
694      * Returns:
695      *  seld, i.e. for method chaining.
696      */
697     ref PackerImpl packExt(in byte type, const ubyte[] data)
698     {
699         ref PackerImpl packExtFixed(int fmt)
700         {
701             store_[0] = cast(ubyte)fmt;
702             store_[1] = type;
703             stream_.put(store_[0 .. 2]);
704             stream_.put(data);
705             return this;
706         }
707 
708         // Try packing to a fixed-length type
709         if (data.length == 1)
710             return packExtFixed(Format.EXT + 0);
711         else if (data.length == 2)
712             return packExtFixed(Format.EXT + 1);
713         else if (data.length == 4)
714             return packExtFixed(Format.EXT + 2);
715         else if (data.length == 8)
716             return packExtFixed(Format.EXT + 3);
717         else if (data.length == 16)
718             return packExtFixed(Format.EXT + 4);
719 
720         int typeByte = void;
721         if (data.length <= (2^^8)-1)
722         {
723             store_[0] = Format.EXT8;
724             store_[1] = cast(ubyte)data.length;
725             typeByte = 2;
726 
727         } else if (data.length <= (2^^16)-1) {
728             store_[0] = Format.EXT16;
729             const temp = convertEndianTo!16(data.length);
730             *cast(ushort*)&store_[Offset] = temp;
731             typeByte = 3;
732         } else if (data.length <= (2^^32)-1) {
733             store_[0] = Format.EXT32;
734             const temp = convertEndianTo!32(data.length);
735             *cast(uint*)&store_[Offset] = temp;
736             typeByte = 5;
737         } else
738             throw new MessagePackException("Data too large to pack as EXT");
739 
740         store_[typeByte] = type;
741         stream_.put(store_[0..typeByte+1]);
742         stream_.put(data);
743 
744         return this;
745     }
746 
747     /*
748      * Serializes raw type-information to stream for binary type.
749      */
750     void beginRaw(in size_t length)
751     {
752         import std.conv : text;
753 
754         if (length < 32) {
755             const ubyte temp = Format.RAW | cast(ubyte)length;
756             stream_.put(take8from(temp));
757         } else if (length < 65536) {
758             const temp = convertEndianTo!16(length);
759 
760             store_[0] = Format.RAW16;
761             *cast(ushort*)&store_[Offset] = temp;
762             stream_.put(store_[0..Offset + ushort.sizeof]);
763         } else {
764             if (length > 0xffffffff)
765                 throw new MessagePackException(text("size of raw is too long to pack: ", length,  " bytes should be <= ", 0xffffffff));
766 
767             const temp = convertEndianTo!32(length);
768 
769             store_[0] = Format.RAW32;
770             *cast(uint*)&store_[Offset] = temp;
771             stream_.put(store_[0..Offset + uint.sizeof]);
772         }
773     }
774 
775     /**
776      * Serializes the type-information to stream.
777      *
778      * These methods don't serialize contents.
779      * You need to call pack method to serialize contents at your own risk.
780      * -----
781      * packer.beginArray(3).pack(true, 1);  // -> [true, 1,
782      *
783      * // other operation
784      *
785      * packer.pack("Hi!");                  // -> [true, 1, "Hi!"]
786      * -----
787      *
788      * Params:
789      *  length = the length of container.
790      *
791      * Returns:
792      *  self, i.e. for method chaining.
793      */
794     ref PackerImpl beginArray(in size_t length)
795     {
796         if (length < 16) {
797             const ubyte temp = Format.ARRAY | cast(ubyte)length;
798             stream_.put(take8from(temp));
799         } else if (length < 65536) {
800             const temp = convertEndianTo!16(length);
801 
802             store_[0] = Format.ARRAY16;
803             *cast(ushort*)&store_[Offset] = temp;
804             stream_.put(store_[0..Offset + ushort.sizeof]);
805         } else {
806             const temp = convertEndianTo!32(length);
807 
808             store_[0] = Format.ARRAY32;
809             *cast(uint*)&store_[Offset] = temp;
810             stream_.put(store_[0..Offset + uint.sizeof]);
811         }
812 
813         return this;
814     }
815 
816 
817     /// ditto
818     ref PackerImpl beginMap(in size_t length)
819     {
820         if (length < 16) {
821             const ubyte temp = Format.MAP | cast(ubyte)length;
822             stream_.put(take8from(temp));
823         } else if (length < 65536) {
824             const temp = convertEndianTo!16(length);
825 
826             store_[0] = Format.MAP16;
827             *cast(ushort*)&store_[Offset] = temp;
828             stream_.put(store_[0..Offset + ushort.sizeof]);
829         } else {
830             const temp = convertEndianTo!32(length);
831 
832             store_[0] = Format.MAP32;
833             *cast(uint*)&store_[Offset] = temp;
834             stream_.put(store_[0..Offset + uint.sizeof]);
835         }
836 
837         return this;
838     }
839 
840 
841   private:
842     /*
843      * Serializes the nil value.
844      */
845     ref PackerImpl packNil()
846     {
847         stream_.put(Format.NIL);
848         return this;
849     }
850 }
851 
852 
853 /// Default serializer
854 alias PackerImpl!(Appender!(ubyte[])) Packer;  // should be pure struct?
855 
856 
857 /**
858  * Helper for $(D Packer) construction.
859  *
860  * Params:
861  *  stream = the stream to write.
862  *  withFieldName = serialize class / struct with field name
863  *
864  * Returns:
865  *  a $(D Packer) object instantiated and initialized according to the arguments.
866  */
867 PackerImpl!(Stream) packer(Stream)(Stream stream, bool withFieldName = false)
868 {
869     return typeof(return)(stream, withFieldName);
870 }
871 
872 
873 version(unittest)
874 {
875     package import std.file, core.stdc.string;
876 
877     package mixin template DefinePacker()
878     {
879         Packer packer;
880     }
881 
882     package mixin template DefineDictionalPacker()
883     {
884         Packer packer = Packer(false);
885     }
886 }
887 
888 
889 unittest
890 {
891     { // unique value
892         mixin DefinePacker;
893 
894         ubyte[] result = [Format.NIL, Format.TRUE, Format.FALSE];
895 
896         packer.pack(null, true, false);
897         foreach (i, value; packer.stream.data)
898             assert(value == result[i]);
899     }
900     { // uint *
901         static struct UTest { ubyte format; ulong value; }
902 
903         enum : ulong { A = ubyte.max, B = ushort.max, C = uint.max, D = ulong.max }
904 
905         static UTest[][] utests = [
906             [{Format.UINT8, A}],
907             [{Format.UINT8, A}, {Format.UINT16, B}],
908             [{Format.UINT8, A}, {Format.UINT16, B}, {Format.UINT32, C}],
909             [{Format.UINT8, A}, {Format.UINT16, B}, {Format.UINT32, C}, {Format.UINT64, D}],
910         ];
911 
912         foreach (I, T; TypeTuple!(ubyte, ushort, uint, ulong)) {
913             foreach (i, test; utests[I]) {
914                 mixin DefinePacker;
915 
916                 packer.pack(cast(T)test.value);
917                 assert(packer.stream.data[0] == test.format);
918 
919                 switch (i) {
920                 case 0:
921                     auto answer = take8from!(T.sizeof * 8)(test.value);
922                     assert(memcmp(&packer.stream.data[1], &answer, ubyte.sizeof) == 0);
923                     break;
924                 case 1:
925                     auto answer = convertEndianTo!16(test.value);
926                     assert(memcmp(&packer.stream.data[1], &answer, ushort.sizeof) == 0);
927                     break;
928                 case 2:
929                     auto answer = convertEndianTo!32(test.value);
930                     assert(memcmp(&packer.stream.data[1], &answer, uint.sizeof) == 0);
931                     break;
932                 default:
933                     auto answer = convertEndianTo!64(test.value);
934                     assert(memcmp(&packer.stream.data[1], &answer, ulong.sizeof) == 0);
935                 }
936             }
937         }
938     }
939     { // int *
940         static struct STest { ubyte format; long value; }
941 
942         enum : long { A = byte.min, B = short.min, C = int.min, D = long.min }
943 
944         static STest[][] stests = [
945             [{Format.INT8, A}],
946             [{Format.INT8, A}, {Format.INT16, B}],
947             [{Format.INT8, A}, {Format.INT16, B}, {Format.INT32, C}],
948             [{Format.INT8, A}, {Format.INT16, B}, {Format.INT32, C}, {Format.INT64, D}],
949         ];
950 
951         foreach (I, T; TypeTuple!(byte, short, int, long)) {
952             foreach (i, test; stests[I]) {
953                 mixin DefinePacker;
954 
955                 packer.pack(cast(T)test.value);
956                 assert(packer.stream.data[0] == test.format);
957 
958                 switch (i) {
959                 case 0:
960                     auto answer = take8from!(T.sizeof * 8)(test.value);
961                     assert(memcmp(&packer.stream.data[1], &answer, byte.sizeof) == 0);
962                     break;
963                 case 1:
964                     auto answer = convertEndianTo!16(test.value);
965                     assert(memcmp(&packer.stream.data[1], &answer, short.sizeof) == 0);
966                     break;
967                 case 2:
968                     auto answer = convertEndianTo!32(test.value);
969                     assert(memcmp(&packer.stream.data[1], &answer, int.sizeof) == 0);
970                     break;
971                 default:
972                     auto answer = convertEndianTo!64(test.value);
973                     assert(memcmp(&packer.stream.data[1], &answer, long.sizeof) == 0);
974                 }
975             }
976         }
977     }
978     { // fload, double
979         static if ((real.sizeof == double.sizeof) || !EnableReal)
980         {
981             alias TypeTuple!(float, double, double) FloatingTypes;
982             static struct FTest { ubyte format; double value; }
983 
984             static FTest[] ftests = [
985                 {Format.FLOAT,  float.min_normal},
986                 {Format.DOUBLE, double.max},
987                 {Format.DOUBLE, double.max},
988             ];
989         }
990         else
991         {
992             alias TypeTuple!(float, double, real) FloatingTypes;
993             static struct FTest { ubyte format; real value; }
994 
995             static FTest[] ftests = [
996                 {Format.FLOAT,  float.min_normal},
997                 {Format.DOUBLE, double.max},
998                 {Format.REAL,   real.max},
999             ];
1000         }
1001 
1002         foreach (I, T; FloatingTypes) {
1003             mixin DefinePacker;
1004 
1005             packer.pack(cast(T)ftests[I].value);
1006             assert(packer.stream.data[0] == ftests[I].format);
1007 
1008             switch (I) {
1009             case 0:
1010                 const answer = convertEndianTo!32(_f(cast(T)ftests[I].value).i);
1011                 assert(memcmp(&packer.stream.data[1], &answer, float.sizeof) == 0);
1012                 break;
1013             case 1:
1014                 const answer = convertEndianTo!64(_d(cast(T)ftests[I].value).i);
1015                 assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0);
1016                 break;
1017             default:
1018                 static if (EnableReal)
1019                 {
1020                     const t = _r(cast(T)ftests[I].value);
1021                     const f = convertEndianTo!64(t.fraction);
1022                     const e = convertEndianTo!16(t.exponent);
1023                     assert(memcmp(&packer.stream.data[1],            &f, f.sizeof) == 0);
1024                     assert(memcmp(&packer.stream.data[1 + f.sizeof], &e, e.sizeof) == 0);
1025                 }
1026                 else
1027                 {
1028                     const answer = convertEndianTo!64(_d(cast(T)ftests[I].value).i);
1029                     assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0);
1030                 }
1031             }
1032         }
1033     }
1034     { // pointer
1035         static struct PTest
1036         {
1037             ubyte format;
1038 
1039             union
1040             {
1041                 ulong*  p0;
1042                 long*   p1;
1043                 double* p2;
1044             }
1045         }
1046 
1047         PTest[] ptests = [PTest(Format.UINT64), PTest(Format.INT64), PTest(Format.DOUBLE)];
1048 
1049         ulong  v0 = ulong.max;
1050         long   v1 = long.min;
1051         double v2 = double.max;
1052 
1053         foreach (I, Index; TypeTuple!("0", "1", "2")) {
1054             mixin DefinePacker;
1055 
1056             mixin("ptests[I].p" ~ Index ~ " = &v" ~ Index ~ ";");
1057 
1058             packer.pack(mixin("ptests[I].p" ~ Index));
1059             assert(packer.stream.data[0] == ptests[I].format);
1060 
1061             switch (I) {
1062             case 0:
1063                 auto answer = convertEndianTo!64(*ptests[I].p0);
1064                 assert(memcmp(&packer.stream.data[1], &answer, ulong.sizeof) == 0);
1065                 break;
1066             case 1:
1067                 auto answer = convertEndianTo!64(*ptests[I].p1);
1068                 assert(memcmp(&packer.stream.data[1], &answer, long.sizeof) == 0);
1069                 break;
1070             default:
1071                 const answer = convertEndianTo!64(_d(*ptests[I].p2).i);
1072                 assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0);
1073             }
1074         }
1075     }
1076     { // enum
1077         enum E : ubyte { A = ubyte.max }
1078 
1079         mixin DefinePacker; E e = E.A;
1080 
1081         packer.pack(e);
1082         assert(packer.stream.data[0] == Format.UINT8);
1083 
1084         auto answer = E.A;
1085         assert(memcmp(&packer.stream.data[1], &answer, (OriginalType!E).sizeof) == 0);
1086     }
1087     { // enum with string
1088         enum E2 : string { A = "test" }
1089 
1090         mixin DefinePacker; E2 e = E2.A;
1091 
1092         packer.pack(e);
1093         assert(packer.stream.data[0] == (Format.RAW | 0x04));
1094     }
1095     { // container
1096         static struct CTest { ubyte format; size_t value; }
1097 
1098         enum : ulong { A = 16 / 2, B = ushort.max, C = uint.max }
1099 
1100         static CTest[][] ctests = [
1101             [{Format.ARRAY | A, Format.ARRAY | A}, {Format.ARRAY16, B}, {Format.ARRAY32, C}],
1102             [{Format.MAP   | A, Format.MAP   | A}, {Format.MAP16,   B}, {Format.MAP32,   C}],
1103             [{Format.RAW   | A, Format.RAW   | A}, {Format.RAW16,   B}, {Format.RAW32,   C}],
1104         ];
1105 
1106         foreach (I, Name; TypeTuple!("Array", "Map", "Raw")) {
1107             auto test = ctests[I];
1108 
1109             foreach (i, T; TypeTuple!(ubyte, ushort, uint)) {
1110                 mixin DefinePacker;
1111                 mixin("packer.begin" ~ Name ~ "(i ? test[i].value : A);");
1112 
1113                 assert(packer.stream.data[0] == test[i].format);
1114 
1115                 switch (i) {
1116                 case 0:
1117                     auto answer = take8from(test[i].value);
1118                     assert(memcmp(&packer.stream.data[0], &answer, ubyte.sizeof) == 0);
1119                     break;
1120                 case 1:
1121                     auto answer = convertEndianTo!16(test[i].value);
1122                     assert(memcmp(&packer.stream.data[1], &answer, ushort.sizeof) == 0);
1123                     break;
1124                 default:
1125                     auto answer = convertEndianTo!32(test[i].value);
1126                     assert(memcmp(&packer.stream.data[1], &answer, uint.sizeof) == 0);
1127                 }
1128             }
1129         }
1130     }
1131     { // larger spec size for string / binary
1132         mixin DefinePacker;
1133 
1134         try {
1135             byte[] bins = new byte[0xffffffffUL + 1];
1136             packer.pack(bins);
1137             assert(false);
1138         } catch (MessagePackException e) {
1139         }
1140     }
1141     { // user defined
1142         {
1143             static struct S
1144             {
1145                 uint num = uint.max;
1146 
1147                 void toMsgpack(P)(ref P p) const { p.packArray(num); }
1148             }
1149 
1150             mixin DefinePacker; S test;
1151 
1152             packer.pack(test);
1153 
1154             assert(packer.stream.data[0] == (Format.ARRAY | 1));
1155             assert(packer.stream.data[1] ==  Format.UINT32);
1156             assert(memcmp(&packer.stream.data[2], &test.num, uint.sizeof) == 0);
1157         }
1158         {
1159             mixin DefinePacker; auto test = tuple(true, false, uint.max);
1160 
1161             packer.pack(test);
1162 
1163             assert(packer.stream.data[0] == (Format.ARRAY | 3));
1164             assert(packer.stream.data[1] ==  Format.TRUE);
1165             assert(packer.stream.data[2] ==  Format.FALSE);
1166             assert(packer.stream.data[3] ==  Format.UINT32);
1167             assert(memcmp(&packer.stream.data[4], &test.field[2], uint.sizeof) == 0);
1168         }
1169         {
1170             static class C
1171             {
1172                 uint num;
1173 
1174                 this(uint n) { num = n; }
1175 
1176                 void toMsgpack(P)(ref P p) const { p.packArray(num); }
1177             }
1178 
1179             mixin DefinePacker; C test = new C(ushort.max);
1180 
1181             packer.pack(test);
1182 
1183             assert(packer.stream.data[0] == (Format.ARRAY | 1));
1184             assert(packer.stream.data[1] ==  Format.UINT16);
1185             assert(memcmp(&packer.stream.data[2], &test.num, ushort.sizeof) == 0);
1186         }
1187     }
1188     { // simple struct and class
1189         {
1190             static struct Simple
1191             {
1192                 uint num = uint.max;
1193             }
1194 
1195             static struct SimpleWithNonPacked1
1196             {
1197                 uint num = uint.max;
1198                 @nonPacked string str = "ignored";
1199             }
1200 
1201             static struct SimpleWithNonPacked2
1202             {
1203                 @nonPacked string str = "ignored";
1204                 uint num = uint.max;
1205             }
1206 
1207             static struct SimpleWithSkippedTypes
1208             {
1209                 int function(int) fn;
1210                 int delegate(int) dg;
1211                 uint num = uint.max;
1212             }
1213 
1214             foreach (Type; TypeTuple!(Simple, SimpleWithNonPacked1, SimpleWithNonPacked2, SimpleWithSkippedTypes)) {
1215                 mixin DefinePacker;
1216 
1217                 Type test;
1218                 packer.pack(test);
1219 
1220                 assert(packer.stream.data[0] == (Format.ARRAY | 1));
1221                 assert(packer.stream.data[1] ==  Format.UINT32);
1222                 assert(memcmp(&packer.stream.data[2], &test.num, uint.sizeof) == 0);
1223             }
1224         }
1225 
1226         static class SimpleA
1227         {
1228             bool flag = true;
1229         }
1230 
1231         static class SimpleB : SimpleA
1232         {
1233             ubyte type = 100;
1234         }
1235 
1236         static class SimpleC : SimpleB
1237         {
1238             uint num = uint.max;
1239         }
1240 
1241         static class SimpleCWithNonPacked1 : SimpleB
1242         {
1243             uint num = uint.max;
1244             @nonPacked string str = "ignored";
1245         }
1246 
1247         static class SimpleCWithNonPacked2 : SimpleB
1248         {
1249             @nonPacked string str = "ignored";
1250             uint num = uint.max;
1251         }
1252 
1253         static class SimpleCWithSkippedTypes : SimpleB
1254         {
1255             uint num = uint.max;
1256             int function(int) fn;
1257             int delegate(int) dg;
1258         }
1259 
1260         {  // from derived class
1261             foreach (Type; TypeTuple!(SimpleC, SimpleCWithNonPacked1, SimpleCWithNonPacked2, SimpleCWithSkippedTypes)) {
1262                 mixin DefinePacker;
1263 
1264                 Type test = new Type();
1265                 packer.pack(test);
1266 
1267                 assert(packer.stream.data[0] == (Format.ARRAY | 3));
1268                 assert(packer.stream.data[1] ==  Format.TRUE);
1269                 assert(packer.stream.data[2] ==  100);
1270                 assert(packer.stream.data[3] ==  Format.UINT32);
1271                 assert(memcmp(&packer.stream.data[4], &test.num, uint.sizeof) == 0);
1272             }
1273         }
1274         {  // from base class
1275             mixin DefinePacker; SimpleB test = new SimpleC();
1276 
1277             try {
1278                 packer.pack(test);
1279                 assert(false);
1280             } catch (Exception e) { }
1281         }
1282     }
1283 
1284     // ext types
1285     {
1286         byte type = 7; // an arbitrary type value
1287 
1288         // fixexts
1289         {
1290             ubyte[16] data;
1291             data[] = 1;
1292             foreach (L; TypeTuple!(1, 2, 4, 8, 16))
1293             {
1294                 mixin DefinePacker;
1295                 packer.pack(ExtValue(type, data[0 .. L]));
1296 
1297                 // format, type, data
1298                 assert(packer.stream.data.length == 2 + L);
1299                 const l = 2 ^^ (packer.stream.data[0] - Format.EXT);
1300                 assert(l == L);
1301                 assert(packer.stream.data[1] == type);
1302                 assert(packer.stream.data[2 .. 2+l] == data[0 .. L]);
1303             }
1304         }
1305 
1306         // ext8
1307         {
1308             foreach (L; TypeTuple!(3, 7, 255))
1309             {
1310                 ubyte[] data = new ubyte[](L);
1311                 data[] = 1;
1312 
1313                 mixin DefinePacker;
1314                 packer.pack(ExtValue(type, data[0 .. L]));
1315 
1316                 // format, length, type, data
1317                 assert(packer.stream.data.length == 3 + L);
1318                 assert(packer.stream.data[0] == Format.EXT8);
1319                 assert(packer.stream.data[1] == L);
1320                 assert(packer.stream.data[2] == type);
1321                 assert(packer.stream.data[3 .. 3 + L] == data);
1322             }
1323         }
1324 
1325         // ext16
1326         {
1327             foreach (L; TypeTuple!(256, (2^^16)-1))
1328             {
1329                 ubyte[] data = new ubyte[](L);
1330                 data[] = 1;
1331 
1332                 mixin DefinePacker;
1333                 packer.pack(ExtValue(type, data[0 .. L]));
1334 
1335                 // format, length, type, data
1336                 import std.conv : text;
1337                 assert(packer.stream.data.length == 4 + L, text(packer.stream.data.length));
1338                 assert(packer.stream.data[0] == Format.EXT16);
1339 
1340                 ushort l = convertEndianTo!16(L);
1341                 assert(memcmp(&packer.stream.data[1], &l, ushort.sizeof) == 0);
1342                 assert(packer.stream.data[3] == type);
1343                 assert(packer.stream.data[4 .. 4 + L] == data);
1344             }
1345         }
1346 
1347         // ext32
1348         {
1349             foreach (L; TypeTuple!(2^^16, 2^^17))
1350             {
1351                 ubyte[] data = new ubyte[](L);
1352                 data[] = 1;
1353 
1354                 mixin DefinePacker;
1355                 packer.pack(ExtValue(type, data[0 .. L]));
1356 
1357                 // format, length, type, data
1358                 import std.conv : text;
1359                 assert(packer.stream.data.length == 6 + L, text(packer.stream.data.length));
1360                 assert(packer.stream.data[0] == Format.EXT32);
1361 
1362                 uint l = convertEndianTo!32(L);
1363                 assert(memcmp(&packer.stream.data[1], &l, uint.sizeof) == 0);
1364                 assert(packer.stream.data[5] == type);
1365                 assert(packer.stream.data[6 .. 6 + L] == data);
1366             }
1367         }
1368     }
1369 }