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