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.buffer.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         /*
429          * Serializes raw type-information to stream.
430          */
431         void beginRaw(in size_t length)
432         {
433             if (length < 32) {
434                 const ubyte temp = Format.RAW | cast(ubyte)length;
435                 stream_.put(take8from(temp));
436             } else if (length < 65536) {
437                 const temp = convertEndianTo!16(length);
438 
439                 store_[0] = Format.RAW16;
440                 *cast(ushort*)&store_[Offset] = temp;
441                 stream_.put(store_[0..Offset + ushort.sizeof]);
442             } else {
443                 const temp = convertEndianTo!32(length);
444 
445                 store_[0] = Format.RAW32;
446                 *cast(uint*)&store_[Offset] = temp;
447                 stream_.put(store_[0..Offset + uint.sizeof]);
448             }
449         }
450 
451         if (array.empty)
452             return packNil();
453 
454         // Raw bytes
455         static if (isByte!(U) || isSomeChar!(U)) {
456             ubyte[] raw = cast(ubyte[])array;
457 
458             beginRaw(raw.length);
459             stream_.put(raw);
460         } else {
461             beginArray(array.length);
462             foreach (elem; array)
463                 pack(elem);
464         }
465 
466         return this;
467     }
468 
469 
470     /// ditto
471     ref PackerImpl pack(T)(in T array) if (isAssociativeArray!T)
472     {
473         if (array is null)
474             return packNil();
475 
476         beginMap(array.length);
477         foreach (key, value; array) {
478             pack(key);
479             pack(value);
480         }
481 
482         return this;
483     }
484 
485 
486     /// ditto
487     ref PackerImpl pack(Types...)(auto ref const Types objects) if (Types.length > 1)
488     {
489         foreach (i, T; Types)
490             pack(objects[i]);
491 
492         return this;
493     }
494 
495 
496     /**
497      * Serializes $(D_PARAM object) and writes to stream.
498      *
499      * Calling $(D toMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D toMsgpack) method. $(D toMsgpack) signature is:
500      * -----
501      * void toMsgpack(Packer)(ref Packer packer) const
502      * -----
503      * This method serializes all members of T object if $(D_KEYWORD class) and $(D_KEYWORD struct) don't implement $(D toMsgpack).
504      *
505      * An object that doesn't implement $(D toMsgpack) is serialized to Array type.
506      * -----
507      * packer.pack(tuple(true, 1, "Hi!"))  // -> '[true, 1, "Hi!"]', not 'ture, 1, "Hi!"'
508      *
509      * struct Foo
510      * {
511      *     int num    = 10;
512      *     string msg = "D!";
513      * }
514      * packer.pack(Foo());  // -> '[10, "D!"]'
515      *
516      * class Base
517      * {
518      *     bool flag = true;
519      * }
520      * class Derived : Base
521      * {
522      *     double = 0.5f;
523      * }
524      * packer.pack(new Derived());  // -> '[true, 0.5f]'
525      * -----
526      *
527      * Params:
528      *  object = the content to serialize.
529      *
530      * Returns:
531      *  self, i.e. for method chaining.
532      */
533     ref PackerImpl pack(T)(in T object) if (is(Unqual!T == class))
534     {
535         if (object is null)
536             return packNil();
537 
538         static if (hasMember!(T, "toMsgpack"))
539         {
540             static if (__traits(compiles, { object.toMsgpack(this, withFieldName_); })) {
541                 object.toMsgpack(this, withFieldName_);
542             } else static if (__traits(compiles, { object.toMsgpack(this); })) { // backward compatible
543                 object.toMsgpack(this);
544             } else {
545                 static assert(0, "Failed to invoke 'toMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
546             }
547         } else {
548             if (auto handler = object.classinfo in packHandlers) {
549                 (*handler)(this, cast(void*)&object);
550                 return this;
551             }
552             if (T.classinfo !is object.classinfo) {
553                 throw new MessagePackException("Can't pack derived class through reference to base class.");
554             }
555 
556             packObject!(T)(object);
557         }
558 
559         return this;
560     }
561 
562 
563     /// ditto
564     @trusted
565     ref PackerImpl pack(T)(auto ref T object) if (is(Unqual!T == struct) &&
566                                                   !isInstanceOf!(Array, T) &&
567                                                   !is(Unqual!T == ExtValue))
568     {
569         static if (hasMember!(T, "toMsgpack"))
570         {
571             static if (__traits(compiles, { object.toMsgpack(this, withFieldName_); })) {
572                 object.toMsgpack(this, withFieldName_);
573             } else static if (__traits(compiles, { object.toMsgpack(this); })) { // backward compatible
574                 object.toMsgpack(this);
575             } else {
576                 static assert(0, "Failed to invoke 'toMsgpack' on type '" ~ Unqual!T.stringof ~ "'");
577             }
578         } else static if (isTuple!T) {
579             beginArray(object.field.length);
580             foreach (f; object.field)
581                 pack(f);
582         } else {  // simple struct
583             if (auto handler = typeid(Unqual!T) in packHandlers) {
584                 (*handler)(this, cast(void*)&object);
585                 return this;
586             }
587 
588             immutable memberNum = SerializingMemberNumbers!(T);
589             if (withFieldName_)
590                 beginMap(memberNum);
591             else
592                 beginArray(memberNum);
593 
594             if (withFieldName_) {
595                 foreach (i, f; object.tupleof) {
596                     static if (isPackedField!(T.tupleof[i]) && __traits(compiles, { pack(f); }))
597                     {
598                         pack(getFieldName!(T, i));
599                         pack(f);
600                     }
601                 }
602             } else {
603                 foreach (i, f; object.tupleof) {
604                     static if (isPackedField!(T.tupleof[i]) && __traits(compiles, { pack(f); }))
605                         pack(f);
606                 }
607             }
608         }
609 
610         return this;
611     }
612 
613 
614     void packObject(T)(in T object) if (is(Unqual!T == class))
615     {
616         alias SerializingClasses!(T) Classes;
617 
618         immutable memberNum = SerializingMemberNumbers!(Classes);
619         if (withFieldName_)
620             beginMap(memberNum);
621         else
622             beginArray(memberNum);
623 
624         foreach (Class; Classes) {
625             Class obj = cast(Class)object;
626             if (withFieldName_) {
627                 foreach (i, f ; obj.tupleof) {
628                     static if (isPackedField!(Class.tupleof[i])) {
629                         pack(getFieldName!(Class, i));
630                         pack(f);
631                     }
632                 }
633             } else {
634                 foreach (i, f ; obj.tupleof) {
635                     static if (isPackedField!(Class.tupleof[i]))
636                         pack(f);
637                 }
638             }
639         }
640     }
641 
642 
643     /**
644      * Serializes the arguments as container to stream.
645      *
646      * -----
647      * packer.packArray(true, 1);  // -> [true, 1]
648      * packer.packMap("Hi", 100);  // -> ["Hi":100]
649      * -----
650      *
651      * In packMap, the number of arguments must be even.
652      *
653      * Params:
654      *  objects = the contents to serialize.
655      *
656      * Returns:
657      *  self, i.e. for method chaining.
658      */
659     ref PackerImpl packArray(Types...)(auto ref const Types objects)
660     {
661         beginArray(Types.length);
662         foreach (i, T; Types)
663             pack(objects[i]);
664         //pack(objects);  // slow :(
665 
666         return this;
667     }
668 
669 
670     /// ditto
671     ref PackerImpl packMap(Types...)(auto ref const Types objects)
672     {
673         static assert(Types.length % 2 == 0, "The number of arguments must be even");
674 
675         beginMap(Types.length / 2);
676         foreach (i, T; Types)
677             pack(objects[i]);
678 
679         return this;
680     }
681 
682     /**
683      * Packs $(D data) as an extended value of $(D type).
684      *
685      * ----
686      * packer.packExt(3, bytes);
687      * ----
688      *
689      * $(D type) must be a signed byte 0-127.
690      *
691      * Params:
692      *  type = the application-defined type for the data
693      *  data = an array of bytes
694      *
695      * Returns:
696      *  seld, i.e. for method chaining.
697      */
698     ref PackerImpl pack(T)(auto ref const T data) if (is(Unqual!T == ExtValue))
699     {
700         packExt(data.type, data.data);
701         return this;
702     }
703 
704     /**
705      * Packs $(D data) as an extended value of $(D type).
706      *
707      * ----
708      * packer.packExt(3, bytes);
709      * ----
710      *
711      * $(D type) must be a signed byte 0-127.
712      *
713      * Params:
714      *  type = the application-defined type for the data
715      *  data = an array of bytes
716      *
717      * Returns:
718      *  seld, i.e. for method chaining.
719      */
720     ref PackerImpl packExt(in byte type, const ubyte[] data)
721     {
722         ref PackerImpl packExtFixed(int fmt)
723         {
724             store_[0] = cast(ubyte)fmt;
725             store_[1] = type;
726             stream_.put(store_[0 .. 2]);
727             stream_.put(data);
728             return this;
729         }
730 
731         // Try packing to a fixed-length type
732         if (data.length == 1)
733             return packExtFixed(Format.EXT + 0);
734         else if (data.length == 2)
735             return packExtFixed(Format.EXT + 1);
736         else if (data.length == 4)
737             return packExtFixed(Format.EXT + 2);
738         else if (data.length == 8)
739             return packExtFixed(Format.EXT + 3);
740         else if (data.length == 16)
741             return packExtFixed(Format.EXT + 4);
742 
743         int typeByte = void;
744         if (data.length <= (2^^8)-1)
745         {
746             store_[0] = Format.EXT8;
747             store_[1] = cast(ubyte)data.length;
748             typeByte = 2;
749 
750         } else if (data.length <= (2^^16)-1) {
751             store_[0] = Format.EXT16;
752             const temp = convertEndianTo!16(data.length);
753             *cast(ushort*)&store_[Offset] = temp;
754             typeByte = 3;
755         } else if (data.length <= (2^^32)-1) {
756             store_[0] = Format.EXT32;
757             const temp = convertEndianTo!32(data.length);
758             *cast(uint*)&store_[Offset] = temp;
759             typeByte = 5;
760         } else
761             throw new MessagePackException("Data too large to pack as EXT");
762 
763         store_[typeByte] = type;
764         stream_.put(store_[0..typeByte+1]);
765         stream_.put(data);
766 
767         return this;
768     }
769 
770 
771     /**
772      * Serializes the type-information to stream.
773      *
774      * These methods don't serialize contents.
775      * You need to call pack method to serialize contents at your own risk.
776      * -----
777      * packer.beginArray(3).pack(true, 1);  // -> [true, 1,
778      *
779      * // other operation
780      *
781      * packer.pack("Hi!");                  // -> [true, 1, "Hi!"]
782      * -----
783      *
784      * Params:
785      *  length = the length of container.
786      *
787      * Returns:
788      *  self, i.e. for method chaining.
789      */
790     ref PackerImpl beginArray(in size_t length)
791     {
792         if (length < 16) {
793             const ubyte temp = Format.ARRAY | cast(ubyte)length;
794             stream_.put(take8from(temp));
795         } else if (length < 65536) {
796             const temp = convertEndianTo!16(length);
797 
798             store_[0] = Format.ARRAY16;
799             *cast(ushort*)&store_[Offset] = temp;
800             stream_.put(store_[0..Offset + ushort.sizeof]);
801         } else {
802             const temp = convertEndianTo!32(length);
803 
804             store_[0] = Format.ARRAY32;
805             *cast(uint*)&store_[Offset] = temp;
806             stream_.put(store_[0..Offset + uint.sizeof]);
807         }
808 
809         return this;
810     }
811 
812 
813     /// ditto
814     ref PackerImpl beginMap(in size_t length)
815     {
816         if (length < 16) {
817             const ubyte temp = Format.MAP | cast(ubyte)length;
818             stream_.put(take8from(temp));
819         } else if (length < 65536) {
820             const temp = convertEndianTo!16(length);
821 
822             store_[0] = Format.MAP16;
823             *cast(ushort*)&store_[Offset] = temp;
824             stream_.put(store_[0..Offset + ushort.sizeof]);
825         } else {
826             const temp = convertEndianTo!32(length);
827 
828             store_[0] = Format.MAP32;
829             *cast(uint*)&store_[Offset] = temp;
830             stream_.put(store_[0..Offset + uint.sizeof]);
831         }
832 
833         return this;
834     }
835 
836 
837   private:
838     /*
839      * Serializes the nil value.
840      */
841     ref PackerImpl packNil()
842     {
843         stream_.put(Format.NIL);
844         return this;
845     }
846 }
847 
848 
849 /// Default serializer
850 alias PackerImpl!(Appender!(ubyte[])) Packer;  // should be pure struct?
851 
852 
853 /**
854  * Helper for $(D Packer) construction.
855  *
856  * Params:
857  *  stream = the stream to write.
858  *  withFieldName = serialize class / struct with field name
859  *
860  * Returns:
861  *  a $(D Packer) object instantiated and initialized according to the arguments.
862  */
863 PackerImpl!(Stream) packer(Stream)(Stream stream, bool withFieldName = false)
864 {
865     return typeof(return)(stream, withFieldName);
866 }
867 
868 
869 version(unittest)
870 {
871     package import std.file, core.stdc.string;
872 
873     package mixin template DefinePacker()
874     {
875         Packer packer;
876     }
877 
878     package mixin template DefineDictionalPacker()
879     {
880         Packer packer = Packer(false);
881     }
882 }
883 
884 
885 unittest
886 {
887     { // unique value
888         mixin DefinePacker;
889 
890         ubyte[] result = [Format.NIL, Format.TRUE, Format.FALSE];
891 
892         packer.pack(null, true, false);
893         foreach (i, value; packer.stream.data)
894             assert(value == result[i]);
895     }
896     { // uint *
897         static struct UTest { ubyte format; ulong value; }
898 
899         enum : ulong { A = ubyte.max, B = ushort.max, C = uint.max, D = ulong.max }
900 
901         static UTest[][] utests = [
902             [{Format.UINT8, A}],
903             [{Format.UINT8, A}, {Format.UINT16, B}],
904             [{Format.UINT8, A}, {Format.UINT16, B}, {Format.UINT32, C}],
905             [{Format.UINT8, A}, {Format.UINT16, B}, {Format.UINT32, C}, {Format.UINT64, D}],
906         ];
907 
908         foreach (I, T; TypeTuple!(ubyte, ushort, uint, ulong)) {
909             foreach (i, test; utests[I]) {
910                 mixin DefinePacker;
911 
912                 packer.pack(cast(T)test.value);
913                 assert(packer.stream.data[0] == test.format);
914 
915                 switch (i) {
916                 case 0:
917                     auto answer = take8from!(T.sizeof * 8)(test.value);
918                     assert(memcmp(&packer.stream.data[1], &answer, ubyte.sizeof) == 0);
919                     break;
920                 case 1:
921                     auto answer = convertEndianTo!16(test.value);
922                     assert(memcmp(&packer.stream.data[1], &answer, ushort.sizeof) == 0);
923                     break;
924                 case 2:
925                     auto answer = convertEndianTo!32(test.value);
926                     assert(memcmp(&packer.stream.data[1], &answer, uint.sizeof) == 0);
927                     break;
928                 default:
929                     auto answer = convertEndianTo!64(test.value);
930                     assert(memcmp(&packer.stream.data[1], &answer, ulong.sizeof) == 0);
931                 }
932             }
933         }
934     }
935     { // int *
936         static struct STest { ubyte format; long value; }
937 
938         enum : long { A = byte.min, B = short.min, C = int.min, D = long.min }
939 
940         static STest[][] stests = [
941             [{Format.INT8, A}],
942             [{Format.INT8, A}, {Format.INT16, B}],
943             [{Format.INT8, A}, {Format.INT16, B}, {Format.INT32, C}],
944             [{Format.INT8, A}, {Format.INT16, B}, {Format.INT32, C}, {Format.INT64, D}],
945         ];
946 
947         foreach (I, T; TypeTuple!(byte, short, int, long)) {
948             foreach (i, test; stests[I]) {
949                 mixin DefinePacker;
950 
951                 packer.pack(cast(T)test.value);
952                 assert(packer.stream.data[0] == test.format);
953 
954                 switch (i) {
955                 case 0:
956                     auto answer = take8from!(T.sizeof * 8)(test.value);
957                     assert(memcmp(&packer.stream.data[1], &answer, byte.sizeof) == 0);
958                     break;
959                 case 1:
960                     auto answer = convertEndianTo!16(test.value);
961                     assert(memcmp(&packer.stream.data[1], &answer, short.sizeof) == 0);
962                     break;
963                 case 2:
964                     auto answer = convertEndianTo!32(test.value);
965                     assert(memcmp(&packer.stream.data[1], &answer, int.sizeof) == 0);
966                     break;
967                 default:
968                     auto answer = convertEndianTo!64(test.value);
969                     assert(memcmp(&packer.stream.data[1], &answer, long.sizeof) == 0);
970                 }
971             }
972         }
973     }
974     { // fload, double
975         static if ((real.sizeof == double.sizeof) || !EnableReal)
976         {
977             alias TypeTuple!(float, double, double) FloatingTypes;
978             static struct FTest { ubyte format; double value; }
979 
980             static FTest[] ftests = [
981                 {Format.FLOAT,  float.min_normal},
982                 {Format.DOUBLE, double.max},
983                 {Format.DOUBLE, double.max},
984             ];
985         }
986         else
987         {
988             alias TypeTuple!(float, double, real) FloatingTypes;
989             static struct FTest { ubyte format; real value; }
990 
991             static FTest[] ftests = [
992                 {Format.FLOAT,  float.min_normal},
993                 {Format.DOUBLE, double.max},
994                 {Format.REAL,   real.max},
995             ];
996         }
997 
998         foreach (I, T; FloatingTypes) {
999             mixin DefinePacker;
1000 
1001             packer.pack(cast(T)ftests[I].value);
1002             assert(packer.stream.data[0] == ftests[I].format);
1003 
1004             switch (I) {
1005             case 0:
1006                 const answer = convertEndianTo!32(_f(cast(T)ftests[I].value).i);
1007                 assert(memcmp(&packer.stream.data[1], &answer, float.sizeof) == 0);
1008                 break;
1009             case 1:
1010                 const answer = convertEndianTo!64(_d(cast(T)ftests[I].value).i);
1011                 assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0);
1012                 break;
1013             default:
1014                 static if (EnableReal)
1015                 {
1016                     const t = _r(cast(T)ftests[I].value);
1017                     const f = convertEndianTo!64(t.fraction);
1018                     const e = convertEndianTo!16(t.exponent);
1019                     assert(memcmp(&packer.stream.data[1],            &f, f.sizeof) == 0);
1020                     assert(memcmp(&packer.stream.data[1 + f.sizeof], &e, e.sizeof) == 0);
1021                 }
1022                 else
1023                 {
1024                     const answer = convertEndianTo!64(_d(cast(T)ftests[I].value).i);
1025                     assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0);
1026                 }
1027             }
1028         }
1029     }
1030     { // pointer
1031         static struct PTest
1032         {
1033             ubyte format;
1034 
1035             union
1036             {
1037                 ulong*  p0;
1038                 long*   p1;
1039                 double* p2;
1040             }
1041         }
1042 
1043         PTest[] ptests = [PTest(Format.UINT64), PTest(Format.INT64), PTest(Format.DOUBLE)];
1044 
1045         ulong  v0 = ulong.max;
1046         long   v1 = long.min;
1047         double v2 = double.max;
1048 
1049         foreach (I, Index; TypeTuple!("0", "1", "2")) {
1050             mixin DefinePacker;
1051 
1052             mixin("ptests[I].p" ~ Index ~ " = &v" ~ Index ~ ";");
1053 
1054             packer.pack(mixin("ptests[I].p" ~ Index));
1055             assert(packer.stream.data[0] == ptests[I].format);
1056 
1057             switch (I) {
1058             case 0:
1059                 auto answer = convertEndianTo!64(*ptests[I].p0);
1060                 assert(memcmp(&packer.stream.data[1], &answer, ulong.sizeof) == 0);
1061                 break;
1062             case 1:
1063                 auto answer = convertEndianTo!64(*ptests[I].p1);
1064                 assert(memcmp(&packer.stream.data[1], &answer, long.sizeof) == 0);
1065                 break;
1066             default:
1067                 const answer = convertEndianTo!64(_d(*ptests[I].p2).i);
1068                 assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0);
1069             }
1070         }
1071     }
1072     { // enum
1073         enum E : ubyte { A = ubyte.max }
1074 
1075         mixin DefinePacker; E e = E.A;
1076 
1077         packer.pack(e);
1078         assert(packer.stream.data[0] == Format.UINT8);
1079 
1080         auto answer = E.A;
1081         assert(memcmp(&packer.stream.data[1], &answer, (OriginalType!E).sizeof) == 0);
1082     }
1083     { // enum with string
1084         enum E2 : string { A = "test" }
1085 
1086         mixin DefinePacker; E2 e = E2.A;
1087 
1088         packer.pack(e);
1089         assert(packer.stream.data[0] == (Format.RAW | 0x04));
1090     }
1091     { // container
1092         static struct CTest { ubyte format; size_t value; }
1093 
1094         enum : ulong { A = 16 / 2, B = ushort.max, C = uint.max }
1095 
1096         static CTest[][] ctests = [
1097             [{Format.ARRAY | A, Format.ARRAY | A}, {Format.ARRAY16, B}, {Format.ARRAY32, C}],
1098             [{Format.MAP   | A, Format.MAP   | A}, {Format.MAP16,   B}, {Format.MAP32,   C}],
1099         ];
1100 
1101         foreach (I, Name; TypeTuple!("Array", "Map")) {
1102             auto test = ctests[I];
1103 
1104             foreach (i, T; TypeTuple!(ubyte, ushort, uint)) {
1105                 mixin DefinePacker;
1106                 mixin("packer.begin" ~ Name ~ "(i ? test[i].value : A);");
1107 
1108                 assert(packer.stream.data[0] == test[i].format);
1109 
1110                 switch (i) {
1111                 case 0:
1112                     auto answer = take8from(test[i].value);
1113                     assert(memcmp(&packer.stream.data[0], &answer, ubyte.sizeof) == 0);
1114                     break;
1115                 case 1:
1116                     auto answer = convertEndianTo!16(test[i].value);
1117                     assert(memcmp(&packer.stream.data[1], &answer, ushort.sizeof) == 0);
1118                     break;
1119                 default:
1120                     auto answer = convertEndianTo!32(test[i].value);
1121                     assert(memcmp(&packer.stream.data[1], &answer, uint.sizeof) == 0);
1122                 }
1123             }
1124         }
1125     }
1126     { // user defined
1127         {
1128             static struct S
1129             {
1130                 uint num = uint.max;
1131 
1132                 void toMsgpack(P)(ref P p) const { p.packArray(num); }
1133             }
1134 
1135             mixin DefinePacker; S test;
1136 
1137             packer.pack(test);
1138 
1139             assert(packer.stream.data[0] == (Format.ARRAY | 1));
1140             assert(packer.stream.data[1] ==  Format.UINT32);
1141             assert(memcmp(&packer.stream.data[2], &test.num, uint.sizeof) == 0);
1142         }
1143         {
1144             mixin DefinePacker; auto test = tuple(true, false, uint.max);
1145 
1146             packer.pack(test);
1147 
1148             assert(packer.stream.data[0] == (Format.ARRAY | 3));
1149             assert(packer.stream.data[1] ==  Format.TRUE);
1150             assert(packer.stream.data[2] ==  Format.FALSE);
1151             assert(packer.stream.data[3] ==  Format.UINT32);
1152             assert(memcmp(&packer.stream.data[4], &test.field[2], uint.sizeof) == 0);
1153         }
1154         {
1155             static class C
1156             {
1157                 uint num;
1158 
1159                 this(uint n) { num = n; }
1160 
1161                 void toMsgpack(P)(ref P p) const { p.packArray(num); }
1162             }
1163 
1164             mixin DefinePacker; C test = new C(ushort.max);
1165 
1166             packer.pack(test);
1167 
1168             assert(packer.stream.data[0] == (Format.ARRAY | 1));
1169             assert(packer.stream.data[1] ==  Format.UINT16);
1170             assert(memcmp(&packer.stream.data[2], &test.num, ushort.sizeof) == 0);
1171         }
1172     }
1173     { // simple struct and class
1174         {
1175             static struct Simple
1176             {
1177                 uint num = uint.max;
1178             }
1179 
1180             static struct SimpleWithNonPacked1
1181             {
1182                 uint num = uint.max;
1183                 @nonPacked string str = "ignored";
1184             }
1185 
1186             static struct SimpleWithNonPacked2
1187             {
1188                 @nonPacked string str = "ignored";
1189                 uint num = uint.max;
1190             }
1191 
1192             static struct SimpleWithSkippedTypes
1193             {
1194                 int function(int) fn;
1195                 int delegate(int) dg;
1196                 uint num = uint.max;
1197             }
1198 
1199             foreach (Type; TypeTuple!(Simple, SimpleWithNonPacked1, SimpleWithNonPacked2, SimpleWithSkippedTypes)) {
1200                 mixin DefinePacker;
1201 
1202                 Type test;
1203                 packer.pack(test);
1204 
1205                 assert(packer.stream.data[0] == (Format.ARRAY | 1));
1206                 assert(packer.stream.data[1] ==  Format.UINT32);
1207                 assert(memcmp(&packer.stream.data[2], &test.num, uint.sizeof) == 0);
1208             }
1209         }
1210 
1211         static class SimpleA
1212         {
1213             bool flag = true;
1214         }
1215 
1216         static class SimpleB : SimpleA
1217         {
1218             ubyte type = 100;
1219         }
1220 
1221         static class SimpleC : SimpleB
1222         {
1223             uint num = uint.max;
1224         }
1225 
1226         static class SimpleCWithNonPacked1 : SimpleB
1227         {
1228             uint num = uint.max;
1229             @nonPacked string str = "ignored";
1230         }
1231 
1232         static class SimpleCWithNonPacked2 : SimpleB
1233         {
1234             @nonPacked string str = "ignored";
1235             uint num = uint.max;
1236         }
1237 
1238         static class SimpleCWithSkippedTypes : SimpleB
1239         {
1240             uint num = uint.max;
1241             int function(int) fn;
1242             int delegate(int) dg;
1243         }
1244 
1245         {  // from derived class
1246             foreach (Type; TypeTuple!(SimpleC, SimpleCWithNonPacked1, SimpleCWithNonPacked2, SimpleCWithSkippedTypes)) {
1247                 mixin DefinePacker;
1248 
1249                 Type test = new Type();
1250                 packer.pack(test);
1251 
1252                 assert(packer.stream.data[0] == (Format.ARRAY | 3));
1253                 assert(packer.stream.data[1] ==  Format.TRUE);
1254                 assert(packer.stream.data[2] ==  100);
1255                 assert(packer.stream.data[3] ==  Format.UINT32);
1256                 assert(memcmp(&packer.stream.data[4], &test.num, uint.sizeof) == 0);
1257             }
1258         }
1259         {  // from base class
1260             mixin DefinePacker; SimpleB test = new SimpleC();
1261 
1262             try {
1263                 packer.pack(test);
1264                 assert(false);
1265             } catch (Exception e) { }
1266         }
1267     }
1268 
1269     // ext types
1270     {
1271         byte type = 7; // an arbitrary type value
1272 
1273         // fixexts
1274         {
1275             ubyte[16] data;
1276             data[] = 1;
1277             foreach (L; TypeTuple!(1, 2, 4, 8, 16))
1278             {
1279                 mixin DefinePacker;
1280                 packer.pack(ExtValue(type, data[0 .. L]));
1281 
1282                 // format, type, data
1283                 assert(packer.stream.data.length == 2 + L);
1284                 const l = 2 ^^ (packer.stream.data[0] - Format.EXT);
1285                 assert(l == L);
1286                 assert(packer.stream.data[1] == type);
1287                 assert(packer.stream.data[2 .. 2+l] == data[0 .. L]);
1288             }
1289         }
1290 
1291         // ext8
1292         {
1293             foreach (L; TypeTuple!(3, 7, 255))
1294             {
1295                 ubyte[] data = new ubyte[](L);
1296                 data[] = 1;
1297 
1298                 mixin DefinePacker;
1299                 packer.pack(ExtValue(type, data[0 .. L]));
1300 
1301                 // format, length, type, data
1302                 assert(packer.stream.data.length == 3 + L);
1303                 assert(packer.stream.data[0] == Format.EXT8);
1304                 assert(packer.stream.data[1] == L);
1305                 assert(packer.stream.data[2] == type);
1306                 assert(packer.stream.data[3 .. 3 + L] == data);
1307             }
1308         }
1309 
1310         // ext16
1311         {
1312             foreach (L; TypeTuple!(256, (2^^16)-1))
1313             {
1314                 ubyte[] data = new ubyte[](L);
1315                 data[] = 1;
1316 
1317                 mixin DefinePacker;
1318                 packer.pack(ExtValue(type, data[0 .. L]));
1319 
1320                 // format, length, type, data
1321                 import std.conv : text;
1322                 assert(packer.stream.data.length == 4 + L, text(packer.stream.data.length));
1323                 assert(packer.stream.data[0] == Format.EXT16);
1324 
1325                 ushort l = convertEndianTo!16(L);
1326                 assert(memcmp(&packer.stream.data[1], &l, ushort.sizeof) == 0);
1327                 assert(packer.stream.data[3] == type);
1328                 assert(packer.stream.data[4 .. 4 + L] == data);
1329             }
1330         }
1331 
1332         // ext32
1333         {
1334             foreach (L; TypeTuple!(2^^16, 2^^17))
1335             {
1336                 ubyte[] data = new ubyte[](L);
1337                 data[] = 1;
1338 
1339                 mixin DefinePacker;
1340                 packer.pack(ExtValue(type, data[0 .. L]));
1341 
1342                 // format, length, type, data
1343                 import std.conv : text;
1344                 assert(packer.stream.data.length == 6 + L, text(packer.stream.data.length));
1345                 assert(packer.stream.data[0] == Format.EXT32);
1346 
1347                 uint l = convertEndianTo!32(L);
1348                 assert(memcmp(&packer.stream.data[1], &l, uint.sizeof) == 0);
1349                 assert(packer.stream.data[5] == type);
1350                 assert(packer.stream.data[6 .. 6 + L] == data);
1351             }
1352         }
1353     }
1354 }