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