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