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