1 /**
2  * A low-level $(D_INLINECODE pure @nogc, nothrow, @safe) and $(D_INLINECODE betterC) MessagePack implementation.
3  *
4  * Note:
5  * As this is a low-level implementation certain error checking a some handling
6  * of the MessagePack data format has to be done by the API user.
7  * The following conditions need to be ensured by the user:
8  * $(UL
9  *  $(LI When calling $(D_INLINECODE parseType) the compile time type must match the actual data
10  *    type or incorrect results will be returned. Use $(D_INLINECODE getType) to verify the type
11  *    before calling $(D_INLINECODE parseType).)
12  *  $(LI The $(D_INLINECODE fix) types have certain maximum and minimum values. These conditions
13  *  need to be ensured when calling $(D_INLINECODE formatType!T):
14  *  $(UL
15  *    $(LI $(D_INLINECODE MsgpackType.posFixInt): Value must satisfy $(D_INLINECODE value < 128))
16  *    $(LI $(D_INLINECODE MsgpackType.negFixInt): Value must satisfy  $(D_INLINECODE -33 < value < 0))
17  *    $(LI $(D_INLINECODE MsgpackType.fixStr): Length must satisfy  $(D_INLINECODE length < 32))
18  *    $(LI $(D_INLINECODE MsgpackType.fixArray): Length must satisfy  $(D_INLINECODE length < 16))
19  *    $(LI $(D_INLINECODE MsgpackType.fixMap): Length must satisfy  $(D_INLINECODE length < 16))
20  *    $(LI All $(D_INLINECODE ext) types: extType must satisfy  $(D_INLINECODE extType < 128))
21  *  )
22  *  Other size restrictions are automatically enforced by proper typing.
23  *  )
24  *  $(LI The $(D_INLINECODE debug=DebugMsgpackLL) debug version can be used to enable debug checks for
25  *    these problems.))
26  *  $(LI Proper formatting and parsing of complex types (maps, arrays, ext types)
27  *    needs help from the API user and must be done according to the MessagePack
28  *    specification. For example to parse an array16 of int8:
29  *    ---------------------
30  *    ubyte[] data = ...;
31  *    byte[] result;
32  *
33  *    // First read array length
34  *    enforce(getType(data[0]) == MsgpackType.array16);
35  *    auto length = parseType!(MsgpackType.array16)(data[0..DataSize!(MsgpackType.array16)]);
36  *    data = data[DataSize!(MsgpackType.array16) .. $];
37  *
38  *    // Then read array values
39  *    for(size_t i = 0; i < length; i++)
40  *    {
41  *        enforce(getType(data[0]) == MsgpackType.int8);
42  *        result ~= parseType!(MsgpackType.int8)(data[0..DataSize!(MsgpackType.int8)]);
43  *        data = data[DataSize!(MsgpackType.int8) .. $];
44  *    }
45  *    ---------------------
46  *    )
47  *
48  * Requires only $(D_INLINECODE std.bitmanip) for $(D_INLINECODE bigEndianToNative) and $(D_INLINECODE nativeToBigEndian) as
49  * external dependency.
50  *
51  * TODO:
52  * Could try to avoid that dependency. This is only a compile time
53  * dependency anyway though, as these functions are templates and get inlined
54  * into this module.
55  */
56 module msgpack_ll;
57 
58 import std.bitmanip : bigEndianToNative, nativeToBigEndian;
59 
60 nothrow @nogc pure @safe:
61 
62 /// Most types are handled like this:
63 @safe unittest
64 {
65     ubyte[128] buffer;
66     enum type = MsgpackType.uint8;
67 
68     // Serialization
69     formatType!(type)(42, buffer[0 .. DataSize!type]);
70 
71     // Now deserialize
72     // Get the type at runtime
73     assert(getType(buffer[0]) == type);
74     // and deserialize specifying the type at compile time
75     const result = parseType!type(buffer[0 .. DataSize!type]);
76     assert(result == 42);
77 }
78 
79 /// Values for nil, true8 and false8 are ignored and can be skipped:
80 @safe unittest
81 {
82     ubyte[128] buffer;
83     enum type = MsgpackType.true8;
84 
85     // Serialization
86     formatType!(type)(buffer[0 .. DataSize!type]);
87 
88     // Now deserialize
89     // Get the type at runtime
90     assert(getType(buffer[0]) == type);
91     // and deserialize specifying the type at compile time
92     const result = parseType!type(buffer[0 .. DataSize!type]);
93     assert(result == true);
94 }
95 
96 /// The fixExt types accept an additional extType parameter and data:
97 @safe unittest
98 {
99     ubyte[128] buffer;
100     ubyte[1] value = [1];
101     enum type = MsgpackType.fixExt1;
102 
103     // Serialization
104     formatType!(type)(42, value, buffer[0 .. DataSize!type]);
105 
106     // Now deserialize
107     // Get the type at runtime
108     assert(getType(buffer[0]) == type);
109     const result = parseType!type(buffer[0 .. DataSize!type]);
110     // and deserialize specifying the type at compile time
111     assert(result[0] == 42);
112     assert(result[1 .. $] == value);
113 }
114 
115 /// The ext types accept an additional extType parameter and data length.
116 @safe unittest
117 {
118     ubyte[128] buffer;
119     enum type = MsgpackType.ext8;
120 
121     // Serialization
122     formatType!(type)(10, 42, buffer[0 .. DataSize!type]);
123 
124     // Now deserialize
125     // Get the type at runtime
126     assert(getType(buffer[0]) == type);
127     // and deserialize specifying the type at compile time
128     const result = parseType!type(buffer[0 .. DataSize!type]);
129     assert(result.type == 42);
130     assert(result.length == 10);
131 }
132 
133 /// Often you'll want to decode multiple possible types:
134 @safe unittest
135 {
136     ulong decodeSomeUint(ubyte[] data)
137     {
138         switch (data[0].getType())
139         {
140         case MsgpackType.posFixInt:
141             return parseType!(MsgpackType.posFixInt)(
142                 data[0 .. DataSize!(MsgpackType.posFixInt)]);
143         case MsgpackType.uint8:
144             return parseType!(MsgpackType.uint8)(
145                 data[0 .. DataSize!(MsgpackType.uint8)]);
146         case MsgpackType.uint16:
147             return parseType!(MsgpackType.uint16)(
148                 data[0 .. DataSize!(MsgpackType.uint16)]);
149         case MsgpackType.uint32:
150             return parseType!(MsgpackType.uint32)(
151                 data[0 .. DataSize!(MsgpackType.uint32)]);
152         case MsgpackType.uint64:
153             return parseType!(MsgpackType.uint64)(
154                 data[0 .. DataSize!(MsgpackType.uint64)]);
155         default:
156             throw new Exception("Expected integer type");
157         }
158     }
159 }
160 
161 version (unittest)
162 {
163     debug = DebugMsgpackLL;
164 }
165 
166 /**
167  * Enum of MessagePack types.
168  */
169 enum MsgpackType
170 {
171     nil = 0, ///
172     invalid, ///
173     false8, ///
174     true8, ///
175     bin8, ///
176     bin16, ///
177     bin32, ///
178     ext8, ///
179     ext16, ///
180     ext32, ///
181     float32, ///
182     float64, ///
183     uint8, ///
184     uint16, ///
185     uint32, ///
186     uint64, ///
187     int8, ///
188     int16, ///
189     int32, ///
190     int64, ///
191     fixExt1, ///
192     fixExt2, ///
193     fixExt4, ///
194     fixExt8, ///
195     fixExt16, ///
196     str8, ///
197     str16, ///
198     str32, ///
199     array16, ///
200     array32, ///
201     map16, ///
202     map32, ///
203     negFixInt, ///
204     posFixInt, ///
205     fixMap, ///
206     fixArray, ///
207     fixStr ///
208 }
209 
210 /**
211  * Look at the first byte of an object to determine the type.
212  *
213  * Note: For some types it's entirely possible that this byte
214  * also contains data. It needs to be part of the data passed to parseType.
215  */
216 MsgpackType getType(ubyte value)
217 {
218     if (value <= 0x7f)
219         return MsgpackType.posFixInt;
220     if (value <= 0x8f)
221         return MsgpackType.fixMap;
222     if (value <= 0x9f)
223         return MsgpackType.fixArray;
224     if (value <= 0xbf)
225         return MsgpackType.fixStr;
226     if (value >= 0xe0)
227         return MsgpackType.negFixInt;
228 
229     return cast(MsgpackType)(value - 0xc0);
230 }
231 
232 /**
233  * Get serialized data size at runtime. $(D_INLINECODE DataSize!()) should be preferred
234  * if the type is known at compile time.
235  */
236 size_t getDataSize(MsgpackType type)
237 {
238     with (MsgpackType) final switch (type)
239     {
240     case nil:
241         return 1;
242     case invalid:
243         return 1;
244     case false8:
245         return 1;
246     case true8:
247         return 1;
248     case bin8:
249         return 2;
250     case bin16:
251         return 3;
252     case bin32:
253         return 5;
254     case ext8:
255         return 3;
256     case ext16:
257         return 4;
258     case ext32:
259         return 6;
260     case float32:
261         return 5;
262     case float64:
263         return 9;
264     case uint8:
265         return 2;
266     case uint16:
267         return 3;
268     case uint32:
269         return 5;
270     case uint64:
271         return 9;
272     case int8:
273         return 2;
274     case int16:
275         return 3;
276     case int32:
277         return 5;
278     case int64:
279         return 9;
280     case fixExt1:
281         return 3;
282     case fixExt2:
283         return 4;
284     case fixExt4:
285         return 6;
286     case fixExt8:
287         return 10;
288     case fixExt16:
289         return 18;
290     case str8:
291         return 2;
292     case str16:
293         return 3;
294     case str32:
295         return 5;
296     case array16:
297         return 3;
298     case array32:
299         return 5;
300     case map16:
301         return 3;
302     case map32:
303         return 5;
304     case negFixInt:
305         return 1;
306     case posFixInt:
307         return 1;
308     case fixMap:
309         return 1;
310     case fixArray:
311         return 1;
312     case fixStr:
313         return 1;
314     }
315 }
316 
317 // This test is kinda useless, but getDataSize is properly tested
318 // at compile time through DataSize! and the other @safe unittests.
319 @safe unittest
320 {
321     for (size_t i = 0; i <= MsgpackType.max; i++)
322         cast(void) getDataSize(cast(MsgpackType) i);
323 }
324 
325 /**
326  * Get serialized data size at compile time.
327  */
328 enum DataSize(MsgpackType type) = getDataSize(type);
329 
330 @safe unittest
331 {
332     assert(DataSize!(MsgpackType.posFixInt) == 1);
333 }
334 
335 private enum isFixExt(MsgpackType type) = (type == MsgpackType.fixExt1)
336         || (type == MsgpackType.fixExt2) || (type == MsgpackType.fixExt4)
337         || (type == MsgpackType.fixExt8) || (type == MsgpackType.fixExt16);
338 
339 /**
340  * Serialization information about an ext type.
341  */
342 struct ExtType
343 {
344     /// Number of bytes in this extension type
345     size_t length;
346     /// Type information about this extension type;
347     ubyte type;
348 }
349 
350 /**
351  * Parses the MessagePack object with specified type.
352  *
353  * Note:
354  * For fixext types returns a ubyte[N] reference to the data input buffer.
355  * The first element in the return value contains the type, the rest of the
356  * array is the ubyte[fixExtLength] part.
357  *
358  * Warning: The type is not verified in this function and this function
359  * will return incorrect results if the type does not match the input data.
360  *
361  * Memory safety is not affected when passing a wrong type.
362  */
363 auto parseType(MsgpackType type)(ref ubyte[DataSize!type] data) if (!isFixExt!type)
364 {
365     debug (DebugMsgpackLL)
366         assert(type == getType(data[0]));
367 
368     // nil
369     static if (type == MsgpackType.nil)
370     {
371         return null;
372     }
373     // boolean
374     else static if (type == MsgpackType.false8)
375     {
376         return false;
377     }
378     else static if (type == MsgpackType.true8)
379     {
380         return true;
381     }
382     // integers
383     else static if (type == MsgpackType.posFixInt)
384     {
385         // Optimize: pos fixnum is a valid ubyte even with type information contained in first byte
386         return data[0];
387     }
388     else static if (type == MsgpackType.negFixInt)
389     {
390         // Optimize: neg fixnum is a valid byte even with type information contained in first byte
391         return cast(byte) data[0];
392     }
393     else static if (type == MsgpackType.uint8)
394     {
395         return data[1];
396     }
397     else static if (type == MsgpackType.uint16)
398     {
399         return bigEndianToNative!ushort(data[1 .. 3]);
400     }
401     else static if (type == MsgpackType.uint32)
402     {
403         return bigEndianToNative!uint(data[1 .. 5]);
404     }
405     else static if (type == MsgpackType.uint64)
406     {
407         return bigEndianToNative!ulong(data[1 .. 9]);
408     }
409     else static if (type == MsgpackType.int8)
410     {
411         return cast(byte) data[1];
412     }
413     else static if (type == MsgpackType.int16)
414     {
415         return bigEndianToNative!short(data[1 .. 3]);
416     }
417     else static if (type == MsgpackType.int32)
418     {
419         return bigEndianToNative!int(data[1 .. 5]);
420     }
421     else static if (type == MsgpackType.int64)
422     {
423         return bigEndianToNative!long(data[1 .. 9]);
424     }
425     // floating point
426     else static if (type == MsgpackType.float32)
427     {
428         return bigEndianToNative!float(data[1 .. 5]);
429     }
430     else static if (type == MsgpackType.float64)
431     {
432         return bigEndianToNative!double(data[1 .. 9]);
433     }
434     // str
435     else static if (type == MsgpackType.fixStr)
436     {
437         return data[0] & 0x1F;
438     }
439     else static if (type == MsgpackType.str8)
440     {
441         return data[1];
442     }
443     else static if (type == MsgpackType.str16)
444     {
445         return bigEndianToNative!ushort(data[1 .. 3]);
446     }
447     else static if (type == MsgpackType.str32)
448     {
449         return bigEndianToNative!uint(data[1 .. 5]);
450     }
451     // bin
452     else static if (type == MsgpackType.bin8)
453     {
454         return data[1];
455     }
456     else static if (type == MsgpackType.bin16)
457     {
458         return bigEndianToNative!ushort(data[1 .. 3]);
459     }
460     else static if (type == MsgpackType.bin32)
461     {
462         return bigEndianToNative!uint(data[1 .. 5]);
463     }
464     // array
465     else static if (type == MsgpackType.fixArray)
466     {
467         return data[0] & 0x0F;
468     }
469     else static if (type == MsgpackType.array16)
470     {
471         return bigEndianToNative!ushort(data[1 .. 3]);
472     }
473     else static if (type == MsgpackType.array32)
474     {
475         return bigEndianToNative!uint(data[1 .. 5]);
476     }
477     // map
478     else static if (type == MsgpackType.fixMap)
479     {
480         return data[0] & 0x0F;
481     }
482     else static if (type == MsgpackType.map16)
483     {
484         return bigEndianToNative!ushort(data[1 .. 3]);
485     }
486     else static if (type == MsgpackType.map32)
487     {
488         return bigEndianToNative!uint(data[1 .. 5]);
489     }
490     // ext
491     else static if (type == MsgpackType.ext8)
492     {
493         return ExtType(data[1], data[2]);
494     }
495     else static if (type == MsgpackType.ext16)
496     {
497         return ExtType(bigEndianToNative!ushort(data[1 .. 3]), data[3]);
498     }
499     else static if (type == MsgpackType.ext32)
500     {
501         return ExtType(bigEndianToNative!uint(data[1 .. 5]), data[5]);
502     }
503 }
504 
505 /// ditto
506 ref ubyte[DataSize!type - 1] parseType(MsgpackType type)(ref ubyte[DataSize!type] data) if (
507         isFixExt!type)
508 {
509     return data[1 .. $];
510 }
511 
512 version (unittest)
513 {
514     void testFormat(MsgpackType type, T)(T value)
515     {
516         ubyte[128] buffer;
517         formatType!(type)(value, buffer[0 .. DataSize!type]);
518         assert(getType(buffer[0]) == type);
519 
520         const result = parseType!type(buffer[0 .. DataSize!type]);
521         assert(result == value);
522     }
523 
524     void testFormatNoArg(MsgpackType type, T)(T value)
525     {
526         ubyte[128] buffer;
527         formatType!(type)(buffer[0 .. DataSize!type]);
528         assert(getType(buffer[0]) == type);
529 
530         const result = parseType!type(buffer[0 .. DataSize!type]);
531         assert(result == value);
532     }
533 }
534 
535 /**
536  * Serialize a value to a certain type.
537  */
538 void formatType(MsgpackType type)(typeof(null) value, ref ubyte[DataSize!type] data) if (
539         type == MsgpackType.nil)
540 {
541     formatType!type(data);
542 }
543 
544 /// ditto
545 void formatType(MsgpackType type)(ref ubyte[DataSize!type] data) if (type == MsgpackType.nil)
546 {
547     data[0] = 0xc0;
548 }
549 
550 @safe unittest
551 {
552     testFormat!(MsgpackType.nil)(null);
553     testFormatNoArg!(MsgpackType.nil)(null);
554 }
555 
556 /// ditto
557 void formatType(MsgpackType type)(bool value, ref ubyte[DataSize!type] data) if (
558         type == MsgpackType.false8)
559 {
560     formatType!type(data);
561 }
562 
563 /// ditto
564 void formatType(MsgpackType type)(ref ubyte[DataSize!type] data) if (type == MsgpackType.false8)
565 {
566     data[0] = 0xc2;
567 }
568 
569 @safe unittest
570 {
571     testFormat!(MsgpackType.false8)(false);
572     testFormatNoArg!(MsgpackType.false8)(false);
573 }
574 
575 /// ditto
576 void formatType(MsgpackType type)(bool value, ref ubyte[DataSize!type] data) if (
577         type == MsgpackType.true8)
578 {
579     formatType!type(data);
580 }
581 
582 /// ditto
583 void formatType(MsgpackType type)(ref ubyte[DataSize!type] data) if (type == MsgpackType.true8)
584 {
585     data[0] = 0xc3;
586 }
587 
588 @safe unittest
589 {
590     testFormat!(MsgpackType.true8)(true);
591     testFormatNoArg!(MsgpackType.true8)(true);
592 }
593 
594 /// ditto
595 void formatType(MsgpackType type)(ubyte value, ref ubyte[DataSize!type] data) if (
596         type == MsgpackType.posFixInt)
597 {
598     debug (DebugMsgpackLL)
599         assert(value < 0x80);
600 
601     // Optimize: pos fixnum is a valid ubyte even with type information contained in first byte
602     data[0] = value;
603 }
604 
605 @safe unittest
606 {
607     testFormat!(MsgpackType.posFixInt)(cast(ubyte)(0x80 - 1));
608     testFormat!(MsgpackType.posFixInt)(ubyte(0));
609 }
610 
611 /// ditto
612 void formatType(MsgpackType type)(byte value, ref ubyte[DataSize!type] data) if (
613         type == MsgpackType.negFixInt)
614 {
615     debug (DebugMsgpackLL)
616         assert(value >= -32 && value < 0);
617 
618     // Optimize: neg fixnum is a valid byte even with type information contained in first byte
619     data[0] = value;
620 }
621 
622 @safe unittest
623 {
624     testFormat!(MsgpackType.negFixInt)(cast(byte)-32);
625     testFormat!(MsgpackType.negFixInt)(cast(byte)-1);
626 }
627 
628 /// ditto
629 void formatType(MsgpackType type)(ubyte value, ref ubyte[DataSize!type] data) if (
630         type == MsgpackType.uint8)
631 {
632     data[0] = 0xcc;
633     data[1] = value;
634 }
635 
636 @safe unittest
637 {
638     testFormat!(MsgpackType.uint8, ubyte)(ubyte.max);
639     testFormat!(MsgpackType.uint8, ubyte)(0);
640 }
641 
642 /// ditto
643 void formatType(MsgpackType type)(ushort value, ref ubyte[DataSize!type] data) if (
644         type == MsgpackType.uint16)
645 {
646     data[0] = 0xcd;
647     data[1 .. 3] = nativeToBigEndian(value);
648 }
649 
650 @safe unittest
651 {
652     testFormat!(MsgpackType.uint16, ushort)(ushort.max);
653     testFormat!(MsgpackType.uint16, ushort)(0);
654 }
655 
656 /// ditto
657 void formatType(MsgpackType type)(uint value, ref ubyte[DataSize!type] data) if (
658         type == MsgpackType.uint32)
659 {
660     data[0] = 0xce;
661     data[1 .. 5] = nativeToBigEndian(value);
662 }
663 
664 @safe unittest
665 {
666     testFormat!(MsgpackType.uint32, uint)(uint.max);
667     testFormat!(MsgpackType.uint32, uint)(0);
668 }
669 
670 /// ditto
671 void formatType(MsgpackType type)(ulong value, ref ubyte[DataSize!type] data) if (
672         type == MsgpackType.uint64)
673 {
674     data[0] = 0xcf;
675     data[1 .. 9] = nativeToBigEndian(value);
676 }
677 
678 @safe unittest
679 {
680     testFormat!(MsgpackType.uint64, ulong)(ulong.max);
681     testFormat!(MsgpackType.uint64, ulong)(0);
682 }
683 
684 /// ditto
685 void formatType(MsgpackType type)(byte value, ref ubyte[DataSize!type] data) if (
686         type == MsgpackType.int8)
687 {
688     data[0] = 0xd0;
689     data[1] = value;
690 }
691 
692 @safe unittest
693 {
694     testFormat!(MsgpackType.int8, byte)(byte.min);
695     testFormat!(MsgpackType.int8, byte)(byte.max);
696 }
697 
698 /// ditto
699 void formatType(MsgpackType type)(short value, ref ubyte[DataSize!type] data) if (
700         type == MsgpackType.int16)
701 {
702     data[0] = 0xd1;
703     data[1 .. 3] = nativeToBigEndian(value);
704 }
705 
706 @safe unittest
707 {
708     testFormat!(MsgpackType.int16, short)(short.min);
709     testFormat!(MsgpackType.int16, short)(short.max);
710 }
711 
712 /// ditto
713 void formatType(MsgpackType type)(int value, ref ubyte[DataSize!type] data) if (
714         type == MsgpackType.int32)
715 {
716     data[0] = 0xd2;
717     data[1 .. 5] = nativeToBigEndian(value);
718 }
719 
720 @safe unittest
721 {
722     testFormat!(MsgpackType.int32, int)(int.min);
723     testFormat!(MsgpackType.int32, int)(int.max);
724 }
725 
726 /// ditto
727 void formatType(MsgpackType type)(long value, ref ubyte[DataSize!type] data) if (
728         type == MsgpackType.int64)
729 {
730     data[0] = 0xd3;
731     data[1 .. 9] = nativeToBigEndian(value);
732 }
733 
734 @safe unittest
735 {
736     testFormat!(MsgpackType.int64, long)(long.min);
737     testFormat!(MsgpackType.int64, long)(long.max);
738 }
739 
740 /// ditto
741 void formatType(MsgpackType type)(float value, ref ubyte[DataSize!type] data) if (
742         type == MsgpackType.float32)
743 {
744     data[0] = 0xca;
745     data[1 .. 5] = nativeToBigEndian(value);
746 }
747 
748 @safe unittest
749 {
750     testFormat!(MsgpackType.float32)(0.125);
751 }
752 
753 /// ditto
754 void formatType(MsgpackType type)(double value, ref ubyte[DataSize!type] data) if (
755         type == MsgpackType.float64)
756 {
757     data[0] = 0xcb;
758     data[1 .. 9] = nativeToBigEndian(value);
759 }
760 
761 @safe unittest
762 {
763     testFormat!(MsgpackType.float64)(0.125);
764 }
765 
766 /// ditto
767 void formatType(MsgpackType type)(ubyte length, ref ubyte[DataSize!type] data) if (
768         type == MsgpackType.fixStr)
769 {
770     debug (DebugMsgpackLL)
771         assert(length < 32);
772 
773     data[0] = 0b10100000 | (length & 0b00011111);
774 }
775 
776 @safe unittest
777 {
778     testFormat!(MsgpackType.fixStr)(cast(ubyte) 0);
779     testFormat!(MsgpackType.fixStr)(cast(ubyte) 31);
780 }
781 
782 /// ditto
783 void formatType(MsgpackType type)(ubyte length, ref ubyte[DataSize!type] data) if (
784         type == MsgpackType.str8)
785 {
786     data[0] = 0xd9;
787     data[1] = length;
788 }
789 
790 @safe unittest
791 {
792     testFormat!(MsgpackType.str8)(cast(ubyte) 0);
793     testFormat!(MsgpackType.str8)(ubyte.max);
794 }
795 
796 /// ditto
797 void formatType(MsgpackType type)(ushort length, ref ubyte[DataSize!type] data) if (
798         type == MsgpackType.str16)
799 {
800     data[0] = 0xda;
801     data[1 .. 3] = nativeToBigEndian(length);
802 }
803 
804 @safe unittest
805 {
806     testFormat!(MsgpackType.str16)(cast(ushort) 0);
807     testFormat!(MsgpackType.str16)(ushort.max);
808 }
809 
810 /// ditto
811 void formatType(MsgpackType type)(uint length, ref ubyte[DataSize!type] data) if (
812         type == MsgpackType.str32)
813 {
814     data[0] = 0xdb;
815     data[1 .. 5] = nativeToBigEndian(length);
816 }
817 
818 @safe unittest
819 {
820     testFormat!(MsgpackType.str32)(cast(uint) 0);
821     testFormat!(MsgpackType.str32)(uint.max);
822 }
823 
824 /// ditto
825 void formatType(MsgpackType type)(ubyte length, ref ubyte[DataSize!type] data) if (
826         type == MsgpackType.bin8)
827 {
828     data[0] = 0xc4;
829     data[1] = length;
830 }
831 
832 @safe unittest
833 {
834     testFormat!(MsgpackType.bin8)(cast(ubyte) 0);
835     testFormat!(MsgpackType.bin8)(ubyte.max);
836 }
837 
838 /// ditto
839 void formatType(MsgpackType type)(ushort length, ref ubyte[DataSize!type] data) if (
840         type == MsgpackType.bin16)
841 {
842     data[0] = 0xc5;
843     data[1 .. 3] = nativeToBigEndian(length);
844 }
845 
846 @safe unittest
847 {
848     testFormat!(MsgpackType.bin16)(cast(ushort) 0);
849     testFormat!(MsgpackType.bin16)(ushort.max);
850 }
851 
852 /// ditto
853 void formatType(MsgpackType type)(uint length, ref ubyte[DataSize!type] data) if (
854         type == MsgpackType.bin32)
855 {
856     data[0] = 0xc6;
857     data[1 .. 5] = nativeToBigEndian(length);
858 }
859 
860 @safe unittest
861 {
862     testFormat!(MsgpackType.bin32)(cast(uint) 0);
863     testFormat!(MsgpackType.bin32)(uint.max);
864 }
865 
866 /// ditto
867 void formatType(MsgpackType type)(ubyte length, ref ubyte[DataSize!type] data) if (
868         type == MsgpackType.fixArray)
869 {
870     debug (DebugMsgpackLL)
871         assert(length < 16);
872 
873     data[0] = 0b10010000 | (length & 0b00001111);
874 }
875 
876 @safe unittest
877 {
878     testFormat!(MsgpackType.fixArray)(cast(ubyte) 0);
879     testFormat!(MsgpackType.fixArray)(cast(ubyte) 15);
880 }
881 
882 /// ditto
883 void formatType(MsgpackType type)(ushort length, ref ubyte[DataSize!type] data) if (
884         type == MsgpackType.array16)
885 {
886     data[0] = 0xdc;
887     data[1 .. 3] = nativeToBigEndian(length);
888 }
889 
890 @safe unittest
891 {
892     testFormat!(MsgpackType.array16)(cast(ushort) 0);
893     testFormat!(MsgpackType.array16)(ushort.max);
894 }
895 
896 /// ditto
897 void formatType(MsgpackType type)(uint length, ref ubyte[DataSize!type] data) if (
898         type == MsgpackType.array32)
899 {
900     data[0] = 0xdd;
901     data[1 .. 5] = nativeToBigEndian(length);
902 }
903 
904 @safe unittest
905 {
906     testFormat!(MsgpackType.array32)(cast(uint) 0);
907     testFormat!(MsgpackType.array32)(uint.max);
908 }
909 
910 /// ditto
911 void formatType(MsgpackType type)(ubyte length, ref ubyte[DataSize!type] data) if (
912         type == MsgpackType.fixMap)
913 {
914     debug (DebugMsgpackLL)
915         assert(length < 16);
916 
917     data[0] = 0b10000000 | (cast(ubyte) length & 0b00001111);
918 }
919 
920 @safe unittest
921 {
922     testFormat!(MsgpackType.fixMap)(cast(ubyte) 0);
923     testFormat!(MsgpackType.fixMap)(cast(ubyte) 15);
924 }
925 
926 /// ditto
927 void formatType(MsgpackType type)(ushort length, ref ubyte[DataSize!type] data) if (
928         type == MsgpackType.map16)
929 {
930     data[0] = 0xde;
931     data[1 .. 3] = nativeToBigEndian(length);
932 }
933 
934 @safe unittest
935 {
936     testFormat!(MsgpackType.map16)(cast(ushort) 0);
937     testFormat!(MsgpackType.map16)(ushort.max);
938 }
939 
940 /// ditto
941 void formatType(MsgpackType type)(uint length, ref ubyte[DataSize!type] data) if (
942         type == MsgpackType.map32)
943 {
944     data[0] = 0xdf;
945     data[1 .. 5] = nativeToBigEndian(length);
946 }
947 
948 @safe unittest
949 {
950     testFormat!(MsgpackType.map32)(cast(uint) 0);
951     testFormat!(MsgpackType.map32)(uint.max);
952 }
953 
954 /// ditto
955 void formatType(MsgpackType type)(ubyte extType, ref ubyte[1] value, ref ubyte[DataSize!type] data) if (
956         type == MsgpackType.fixExt1)
957 {
958     debug (DebugMsgpackLL)
959         assert(extType < 128);
960 
961     data[0] = 0xd4;
962     data[1] = extType;
963     data[2] = value[0];
964 }
965 
966 version (unittest)
967 {
968     void testFixExt(MsgpackType type, T)(ubyte extType, ref T value)
969     {
970         ubyte[128] buffer;
971         formatType!(type)(extType, value, buffer[0 .. DataSize!type]);
972         assert(getType(buffer[0]) == type);
973 
974         const result = parseType!type(buffer[0 .. DataSize!type]);
975         assert(result[0] == extType);
976         assert(result[1 .. $] == value);
977     }
978 }
979 
980 @safe unittest
981 {
982     ubyte[1] testData = [42];
983     testFixExt!(MsgpackType.fixExt1)(127, testData);
984 }
985 
986 /// ditto
987 void formatType(MsgpackType type)(ubyte extType, ref ubyte[2] value, ref ubyte[DataSize!type] data) if (
988         type == MsgpackType.fixExt2)
989 {
990     debug (DebugMsgpackLL)
991         assert(extType < 128);
992 
993     data[0] = 0xd5;
994     data[1] = extType;
995     data[2 .. 4] = value[0 .. 2];
996 }
997 
998 @safe unittest
999 {
1000     ubyte[2] testData = [42, 42];
1001     testFixExt!(MsgpackType.fixExt2)(127, testData);
1002 }
1003 
1004 /// ditto
1005 void formatType(MsgpackType type)(ubyte extType, ref ubyte[4] value, ref ubyte[DataSize!type] data) if (
1006         type == MsgpackType.fixExt4)
1007 {
1008     debug (DebugMsgpackLL)
1009         assert(extType < 128);
1010 
1011     data[0] = 0xd6;
1012     data[1] = extType;
1013     data[2 .. 6] = value[0 .. 4];
1014 }
1015 
1016 @safe unittest
1017 {
1018     ubyte[4] testData = [42, 42, 42, 42];
1019     testFixExt!(MsgpackType.fixExt4)(127, testData);
1020 }
1021 
1022 /// ditto
1023 void formatType(MsgpackType type)(ubyte extType, ref ubyte[8] value, ref ubyte[DataSize!type] data) if (
1024         type == MsgpackType.fixExt8)
1025 {
1026     debug (DebugMsgpackLL)
1027         assert(extType < 128);
1028 
1029     data[0] = 0xd7;
1030     data[1] = extType;
1031     data[2 .. 10] = value[0 .. 8];
1032 }
1033 
1034 @safe unittest
1035 {
1036     ubyte[8] testData = [42, 42, 42, 42, 42, 42, 42, 42];
1037     testFixExt!(MsgpackType.fixExt8)(127, testData);
1038 }
1039 
1040 /// ditto
1041 void formatType(MsgpackType type)(ubyte extType, ref ubyte[16] value, ref ubyte[DataSize!type] data) if (
1042         type == MsgpackType.fixExt16)
1043 {
1044     debug (DebugMsgpackLL)
1045         assert(extType < 128);
1046 
1047     data[0] = 0xd8;
1048     data[1] = extType;
1049     data[2 .. 18] = value[0 .. 16];
1050 }
1051 
1052 @safe unittest
1053 {
1054     ubyte[16] testData = [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
1055         42];
1056     testFixExt!(MsgpackType.fixExt16)(127, testData);
1057 }
1058 
1059 version (unittest)
1060 {
1061     void testExt(MsgpackType type, T)(T length, ubyte extType)
1062     {
1063         ubyte[128] buffer;
1064         formatType!(type)(length, extType, buffer[0 .. DataSize!type]);
1065         assert(getType(buffer[0]) == type);
1066 
1067         const result = parseType!type(buffer[0 .. DataSize!type]);
1068         assert(result.type == extType);
1069         assert(result.length == length);
1070     }
1071 }
1072 
1073 /// ditto
1074 void formatType(MsgpackType type)(ubyte length, ubyte extType, ref ubyte[DataSize!type] data) if (
1075         type == MsgpackType.ext8)
1076 {
1077     debug (DebugMsgpackLL)
1078         assert(extType < 128);
1079 
1080     data[0] = 0xc7;
1081     data[1] = length;
1082     data[2] = extType;
1083 }
1084 
1085 @safe unittest
1086 {
1087     testExt!(MsgpackType.ext8)(ubyte.max, 127);
1088 }
1089 
1090 /// ditto
1091 void formatType(MsgpackType type)(ushort length, ubyte extType, ref ubyte[DataSize!type] data) if (
1092         type == MsgpackType.ext16)
1093 {
1094     debug (DebugMsgpackLL)
1095         assert(extType < 128);
1096 
1097     data[0] = 0xc8;
1098     data[1 .. 3] = nativeToBigEndian(length);
1099     data[3] = extType;
1100 }
1101 
1102 @safe unittest
1103 {
1104     testExt!(MsgpackType.ext16)(ushort.max, 127);
1105 }
1106 
1107 /// ditto
1108 void formatType(MsgpackType type)(uint length, ubyte extType, ref ubyte[DataSize!type] data) if (
1109         type == MsgpackType.ext32)
1110 {
1111     debug (DebugMsgpackLL)
1112         assert(extType < 128);
1113 
1114     data[0] = 0xc9;
1115     data[1 .. 5] = nativeToBigEndian(length);
1116     data[5] = extType;
1117 }
1118 
1119 @safe unittest
1120 {
1121     testExt!(MsgpackType.ext32)(uint.max, 127);
1122 }