1 /++
2 $(H4 High level Msgpack deserialization API)
3 
4 Macros:
5 IONREF = $(REF_ALTTEXT $(TT $2), $2, mir, ion, $1)$(NBSP)
6 +/
7 module mir.deser.msgpack;
8 import mir.algebraic : Nullable;
9 import mir.ser.msgpack : MessagePackFmt;
10 import mir.ion.exception : IonErrorCode, ionException, ionErrorMsg;
11 import mir.lob : Blob;
12 
13 struct MsgpackExtension
14 {
15     Blob data;
16     ubyte type;
17 }
18 
19 private static T unpackMsgPackVal(T)(scope ref const(ubyte)[] data)
20 {
21     import std.traits : Unsigned;
22     alias UT = Unsigned!T;
23 
24     if (data.length < UT.sizeof)
25     {
26         version (D_Exceptions)
27             throw IonErrorCode.unexpectedEndOfData.ionException;
28         else
29             assert(0, IonErrorCode.unexpectedEndOfData.ionErrorMsg);
30     }
31 
32     UT ret = (cast(UT[1])cast(ubyte[UT.sizeof])data[0 .. UT.sizeof])[0];
33 
34     version (LittleEndian)
35     {
36         import core.bitop : bswap, byteswap;
37         static if (T.sizeof >= 4)
38         {
39             ret = bswap(ret);
40         } 
41         else static if (T.sizeof == 2)
42         {
43             ret = byteswap(ret);
44         }
45     }
46 
47     data = data[UT.sizeof .. $];
48     return cast(typeof(return))ret;
49 }
50 
51 @safe pure
52 private static void handleMsgPackElement(S)(scope ref S serializer, MessagePackFmt type, scope ref const(ubyte)[] data)
53 {
54     size_t length = 0;
55     switch (type) 
56     {
57         // fixint
58         case MessagePackFmt.fixint: .. case (1 << 7) - 1:
59             serializer.putValue(cast(ubyte)type);
60             break;
61 
62         // fixnint
63         case MessagePackFmt.fixnint: .. case 0xFF:
64             serializer.putValue(cast(byte)type);
65             break;
66 
67         case MessagePackFmt.uint8:
68             serializer.putValue(data[0]);
69             data = data[1 .. $];
70             break;
71         
72         case MessagePackFmt.uint16:
73             serializer.putValue(unpackMsgPackVal!ushort(data));
74             break;
75 
76         case MessagePackFmt.uint32:
77             serializer.putValue(unpackMsgPackVal!uint(data));
78             break;
79         
80         case MessagePackFmt.uint64:
81             serializer.putValue(unpackMsgPackVal!ulong(data));
82             break;
83 
84         case MessagePackFmt.int8:
85             serializer.putValue(cast(byte)data[0]);
86             data = data[1 .. $];
87             break;
88         
89         case MessagePackFmt.int16:
90             serializer.putValue(unpackMsgPackVal!short(data));
91             break;
92 
93         case MessagePackFmt.int32:
94             serializer.putValue(unpackMsgPackVal!int(data));
95             break;
96 
97         case MessagePackFmt.int64:
98             serializer.putValue(unpackMsgPackVal!long(data));
99             break;
100 
101         case MessagePackFmt.fixmap: .. case MessagePackFmt.fixmap + 0xF:
102         case MessagePackFmt.map16:
103         case MessagePackFmt.map32:
104             goto ReadMap;
105 
106         case MessagePackFmt.fixarray: .. case MessagePackFmt.fixarray + 0xF:
107         case MessagePackFmt.array16:
108         case MessagePackFmt.array32:
109             goto ReadArray;
110 
111         case MessagePackFmt.fixstr: .. case MessagePackFmt.fixstr + 0x1F:
112         case MessagePackFmt.str8:
113         case MessagePackFmt.str16:
114         case MessagePackFmt.str32:
115             goto ReadStr;
116 
117         case MessagePackFmt.nil:
118             serializer.putValue(null);
119             break;
120 
121         case MessagePackFmt.true_:
122         case MessagePackFmt.false_:
123             serializer.putValue(type == MessagePackFmt.true_ ? true : false);
124             break;
125 
126         case MessagePackFmt.bin8: .. case MessagePackFmt.bin32:
127             goto ReadBin;
128             
129         case MessagePackFmt.fixext1: .. case MessagePackFmt.fixext16:
130         case MessagePackFmt.ext8: .. case MessagePackFmt.ext32:
131             goto ReadExt;
132 
133         case MessagePackFmt.float32:
134         case MessagePackFmt.float64:
135             goto ReadFloat;
136 
137         ReadMap:
138         {
139             import mir.format : stringBuf, print;
140             if (type <= MessagePackFmt.fixmap + 0xF && type >= MessagePackFmt.fixmap)
141             {
142                 length = (type - MessagePackFmt.fixmap);
143             }
144             else if (type == MessagePackFmt.map16)
145             {
146                 length = unpackMsgPackVal!ushort(data);
147             }
148             else if (type == MessagePackFmt.map32)
149             {
150                 length = unpackMsgPackVal!uint(data);
151             }
152             else
153             {
154                 assert(0, "Should never happen");
155             }
156 
157             auto state = serializer.structBegin();
158             foreach(i; 0 .. length)
159             {
160                 if (data.length < 1)
161                 {
162                     version (D_Exceptions)
163                         throw IonErrorCode.unexpectedEndOfData.ionException;
164                     else
165                         assert(0, IonErrorCode.unexpectedEndOfData.ionErrorMsg);
166                 }
167 
168                 MessagePackFmt keyType = cast(MessagePackFmt)data[0];
169                 data = data[1 .. $];
170                 auto keyBuf = stringBuf;
171                 uint keyLength = 0;
172                 switch (keyType)
173                 {
174                     // fixstr
175                     case MessagePackFmt.fixstr: .. case MessagePackFmt.fixstr + 0x1F:
176                     case MessagePackFmt.str8:
177                     case MessagePackFmt.str16:
178                     case MessagePackFmt.str32:
179                         goto StrKey;
180 
181                     case MessagePackFmt.fixint: .. case (1 << 7) - 1:
182                     case MessagePackFmt.uint8:
183                     case MessagePackFmt.uint16:
184                     case MessagePackFmt.uint32:
185                     case MessagePackFmt.uint64:
186                         goto UIntKey;
187 
188                     case MessagePackFmt.fixnint: .. case 0xFF:
189                     case MessagePackFmt.int8:
190                     case MessagePackFmt.int16:
191                     case MessagePackFmt.int32:
192                     case MessagePackFmt.int64:
193                         goto IntKey;
194                     
195                     case MessagePackFmt.fixext1: .. case MessagePackFmt.fixext16:
196                     case MessagePackFmt.ext8: .. case MessagePackFmt.ext32:
197                         goto TimestampKey;
198 
199                     StrKey:
200                     {
201                         if (keyType <= MessagePackFmt.fixstr + 0x1F && keyType >= MessagePackFmt.fixstr)
202                         {
203                             keyLength = (keyType - MessagePackFmt.fixstr);
204                         }
205                         else if (keyType == MessagePackFmt.str8)
206                         {
207                             keyLength = unpackMsgPackVal!ubyte(data);
208                         }
209                         else if (keyType == MessagePackFmt.str16)
210                         {
211                             keyLength = unpackMsgPackVal!ushort(data);
212                         }
213                         else if (keyType == MessagePackFmt.str32)
214                         {
215                             keyLength = unpackMsgPackVal!uint(data);
216                         }
217                         else
218                         {
219                             assert(0, "Should never happen");
220                         }
221 
222                         serializer.putKey((() @trusted => cast(const(char[]))data[0 .. keyLength])());
223                         data = data[keyLength .. $];
224                         break;
225                     }
226 
227                     UIntKey:
228                     {
229                         ulong val = 0;
230                         if (keyType <= (1 << 7) - 1 && keyType >= MessagePackFmt.fixint)
231                         {
232                             val = keyType;
233                         }
234                         else if (keyType == MessagePackFmt.uint8)
235                         {
236                             val = unpackMsgPackVal!ubyte(data);
237                         }
238                         else if (keyType == MessagePackFmt.uint16)
239                         {
240                             val = unpackMsgPackVal!ushort(data);
241                         }
242                         else if (keyType == MessagePackFmt.uint32)
243                         {
244                             val = unpackMsgPackVal!uint(data);
245                         }
246                         else if (keyType == MessagePackFmt.uint64)
247                         {
248                             val = unpackMsgPackVal!ulong(data);
249                         }
250                         else
251                         {
252                             assert(0, "Should never happen");
253                         }
254 
255                         keyBuf.print(val);
256                         serializer.putKey(keyBuf.data);
257                         break;
258                     }
259 
260                     IntKey:
261                     {
262                         long val = 0;
263                         if (keyType <= 0xFF && keyType >= MessagePackFmt.fixnint)
264                         {
265                             val = cast(byte)keyType;
266                         }
267                         else if (keyType == MessagePackFmt.int8)
268                         {
269                             val = unpackMsgPackVal!byte(data);
270                         }
271                         else if (keyType == MessagePackFmt.int16)
272                         {
273                             val = unpackMsgPackVal!short(data);
274                         }
275                         else if (keyType == MessagePackFmt.int32)
276                         {
277                             val = unpackMsgPackVal!int(data);
278                         }
279                         else if (keyType == MessagePackFmt.int64)
280                         {
281                             val = unpackMsgPackVal!long(data);
282                         }
283                         else
284                         {
285                             assert(0, "Should never happen");
286                         }
287 
288                         keyBuf.print(val);
289                         serializer.putKey(keyBuf.data[0 .. keyBuf.length]);
290                         break;
291                     }
292 
293                     // Ugly mess here
294                     TimestampKey: {
295                         if (keyType <= MessagePackFmt.fixext16 && keyType >= MessagePackFmt.fixext1)
296                         {
297                             keyLength = 1 << (keyType - MessagePackFmt.fixext1);
298                         }
299                         else if (keyType == MessagePackFmt.ext8)
300                         {
301                             keyLength = unpackMsgPackVal!ubyte(data);
302                         }
303                         else if (keyType == MessagePackFmt.ext16)
304                         {
305                             keyLength = unpackMsgPackVal!ushort(data);
306                         }
307                         else if (keyType == MessagePackFmt.ext32)
308                         {
309                             keyLength = unpackMsgPackVal!uint(data);
310                         }
311                         else
312                         {
313                             assert(0, "Should never happen");
314                         }
315 
316                         if (data.length < (keyLength + 1)) 
317                         {
318                             version (D_Exceptions)
319                                 throw IonErrorCode.unexpectedEndOfData.ionException;
320                             else
321                                 assert(0, IonErrorCode.unexpectedEndOfData.ionErrorMsg);
322                         }
323                         
324                         ubyte ext_type = data[0];
325                         data = data[1 .. $];
326 
327                         if (ext_type == cast(ubyte)-1)
328                         {
329                             import mir.timestamp : Timestamp;
330                             Timestamp time;
331                             if (keyLength == 4)
332                             {
333                                 uint unixTime = unpackMsgPackVal!uint(data);
334                                 time = Timestamp.fromUnixTime(unixTime);
335                             }
336                             else if (keyLength == 8)
337                             {
338                                 ulong packedUnixTime = unpackMsgPackVal!ulong(data);
339                                 ulong nanosecs = packedUnixTime >> 34;
340                                 ulong seconds = packedUnixTime & 0x3ffffffff;
341                                 time = Timestamp.fromUnixTime(seconds);
342                                 time.fractionExponent = -9;
343                                 time.fractionCoefficient = nanosecs;
344                                 time.precision = Timestamp.Precision.fraction;
345                             }
346                             else if (keyLength == 12)
347                             {
348                                 uint nanosecs = unpackMsgPackVal!uint(data);
349                                 long seconds = unpackMsgPackVal!long(data);
350                                 time = Timestamp.fromUnixTime(seconds);
351                                 time.fractionExponent = -9;
352                                 time.fractionCoefficient = nanosecs;
353                                 time.precision = Timestamp.Precision.fraction;
354                             }
355                             else
356                             {
357                                 version (D_Exceptions)
358                                     throw IonErrorCode.cantParseValueStream.ionException;
359                                 else
360                                     assert(0, IonErrorCode.cantParseValueStream.ionErrorMsg);
361                             }
362                             time.offset = 0;
363                             time.toString(keyBuf);
364                             serializer.putKey(keyBuf.data[0 .. keyBuf.length]);
365                         }
366                         else
367                         {
368                             version (D_Exceptions)
369                                 throw IonErrorCode.cantParseValueStream.ionException;
370                             else
371                                 assert(0, IonErrorCode.cantParseValueStream.ionErrorMsg);
372                         }
373                         break;
374                     }
375 
376                     default:
377                         version (D_Exceptions)
378                             throw IonErrorCode.cantParseValueStream.ionException;
379                         else
380                             assert(0, IonErrorCode.cantParseValueStream.ionErrorMsg);
381                 }
382 
383                 MessagePackFmt valueType = cast(MessagePackFmt)data[0];
384                 data = data[1 .. $];
385                 handleMsgPackElement(serializer, valueType, data);
386             }
387             serializer.structEnd(state);
388             break;
389         }
390         
391         ReadArray:
392         {
393             if (type <= MessagePackFmt.fixarray + 0xF && type >= MessagePackFmt.fixarray)
394             {
395                 length = (type - MessagePackFmt.fixarray);
396             }
397             else if (type == MessagePackFmt.array16)
398             {
399                 length = unpackMsgPackVal!ushort(data);
400             }
401             else if (type == MessagePackFmt.array32)
402             {
403                 length = unpackMsgPackVal!uint(data);
404             }
405             else
406             {
407                 assert(0, "Should never happen");
408             }
409 
410             auto state = serializer.listBegin(length);
411             foreach(i; 0 .. length)
412             {
413                 if (data.length < 1)
414                 {
415                     version (D_Exceptions)
416                         throw IonErrorCode.unexpectedEndOfData.ionException;
417                     else
418                         assert(0, IonErrorCode.unexpectedEndOfData.ionErrorMsg);
419                 }
420 
421                 MessagePackFmt elementType = cast(MessagePackFmt)data[0];
422                 data = data[1 .. $];
423             
424                 serializer.elemBegin;
425                 handleMsgPackElement(serializer, elementType, data);
426             }
427             serializer.listEnd(state);
428             break;
429         }
430 
431         ReadExt:
432         {
433             if (type <= MessagePackFmt.fixext16 && type >= MessagePackFmt.fixext1)
434             {
435                 length = 1 << (type - MessagePackFmt.fixext1);
436             }
437             else if (type == MessagePackFmt.ext8)
438             {
439                 length = unpackMsgPackVal!ubyte(data);
440             }
441             else if (type == MessagePackFmt.ext16)
442             {
443                 length = unpackMsgPackVal!ushort(data);
444             }
445             else if (type == MessagePackFmt.ext32)
446             {
447                 length = unpackMsgPackVal!uint(data);
448             }
449             else
450             {
451                 assert(0, "Should never happen");
452             }
453 
454             if (data.length < (length + 1)) 
455             {
456                 version (D_Exceptions)
457                     throw IonErrorCode.unexpectedEndOfData.ionException;
458                 else
459                     assert(0, IonErrorCode.unexpectedEndOfData.ionErrorMsg);
460             }
461             
462             ubyte ext_type = data[0];
463             data = data[1 .. $];
464 
465             if (ext_type == cast(ubyte)-1)
466             {
467                 import mir.timestamp : Timestamp;
468                 Timestamp time;
469                 if (length == 4)
470                 {
471                     uint unixTime = unpackMsgPackVal!uint(data);
472                     time = Timestamp.fromUnixTime(unixTime);
473                 }
474                 else if (length == 8)
475                 {
476                     ulong packedUnixTime = unpackMsgPackVal!ulong(data);
477                     ulong nanosecs = packedUnixTime >> 34;
478                     ulong seconds = packedUnixTime & 0x3ffffffff;
479                     time = Timestamp.fromUnixTime(seconds);
480                     time.fractionExponent = -9;
481                     time.fractionCoefficient = nanosecs;
482                     time.precision = Timestamp.Precision.fraction;
483                 }
484                 else if (length == 12)
485                 {
486                     uint nanosecs = unpackMsgPackVal!uint(data);
487                     long seconds = unpackMsgPackVal!long(data);
488                     time = Timestamp.fromUnixTime(seconds);
489                     time.fractionExponent = -9;
490                     time.fractionCoefficient = nanosecs;
491                     time.precision = Timestamp.Precision.fraction;
492                 }
493                 time.offset = 0;
494                 serializer.putValue(time);
495             }
496             else
497             {
498                 // XXX: How do we want to serialize exts that we don't recognize?
499                 auto state = serializer.structBegin();
500                 serializer.putKey("type");
501                 serializer.putValue(ext_type);
502                 serializer.putKey("data");
503                 serializer.putValue(Blob(data[0 .. length]));
504                 serializer.structEnd(state);
505                 data = data[length .. $];
506             }
507             break;
508         }
509         
510         ReadStr:
511         {
512             if (type <= MessagePackFmt.fixstr + 0x1F && type >= MessagePackFmt.fixstr)
513             {
514                 length = (type - MessagePackFmt.fixstr);
515             }
516             else if (type == MessagePackFmt.str8)
517             {
518                 length = unpackMsgPackVal!ubyte(data); 
519             }
520             else if (type == MessagePackFmt.str16)
521             {
522                 length = unpackMsgPackVal!ushort(data);
523             }
524             else if (type == MessagePackFmt.str32)
525             {
526                 length = unpackMsgPackVal!uint(data);
527             }
528             else 
529             {
530                 assert(0, "Should never happen");
531             }
532 
533             if (data.length < length) 
534             {
535                 version (D_Exceptions)
536                     throw IonErrorCode.unexpectedEndOfData.ionException;
537                 else
538                     assert(0, IonErrorCode.unexpectedEndOfData.ionErrorMsg);
539             }
540 
541             serializer.putValue((() @trusted => cast(const(char)[])data[0 .. length])());
542             data = data[length .. $];
543             break;
544         }
545         
546         ReadBin:
547         {
548             length = 0;
549 
550             if (type == MessagePackFmt.bin8)
551             {
552                 length = unpackMsgPackVal!ubyte(data);
553             }
554             else if (type == MessagePackFmt.bin16)
555             {
556                 length = unpackMsgPackVal!ushort(data);
557             }
558             else if (type == MessagePackFmt.bin32)
559             {
560                 length = unpackMsgPackVal!uint(data);
561             }
562             else 
563             {
564                 assert(0, "Should never happen");
565             }
566 
567             if (data.length < length)
568             {
569                 version (D_Exceptions)
570                     throw IonErrorCode.unexpectedEndOfData.ionException;
571                 else
572                     assert(0, IonErrorCode.unexpectedEndOfData.ionErrorMsg);
573             }
574             serializer.putValue(Blob(data[0 .. length]));
575             data = data[length .. $];
576             break;
577         }
578         
579         ReadFloat:
580         {
581             length = type == MessagePackFmt.float32 ? 4 : 8;
582 
583             if (length == 4)
584             {
585                 uint v = unpackMsgPackVal!uint(data);
586                 serializer.putValue((() @trusted => *cast(float*)&v)());
587             }
588             else if (length == 8)
589             {
590                 // manually construct the ulong
591                 ulong v = unpackMsgPackVal!ulong(data);
592                 serializer.putValue((() @trusted => *cast(double*)&v)());
593             }
594             break;
595         }
596 
597         default:
598             version (D_Exceptions)
599                 throw IonErrorCode.cantParseValueStream.ionException;
600             else
601                 assert(0, IonErrorCode.cantParseValueStream.ionErrorMsg);
602     }
603 }
604 
605 ///
606 struct MsgpackValueStream
607 {
608     const(ubyte)[] data;
609 
610     void serialize(S)(scope ref S serializer) scope const
611     {
612         auto window = data[0 .. $];
613         bool following = false;
614         while (window.length)
615         {
616             if (following)
617                 serializer.nextTopLevelValue;
618             following = true;
619 
620             MessagePackFmt type = cast(MessagePackFmt)window[0];
621             window = window[1 .. $];
622             handleMsgPackElement(serializer, type, window);
623         }
624     }
625 }
626 
627 ///
628 template deserializeMsgpack(T)
629 {
630 @safe:
631     ///
632     void deserializeMsgpack(scope ref T value, scope const(ubyte)[] data)
633     {
634         import mir.appender : scopedBuffer;
635         import mir.deser.ion : deserializeIon;
636         import mir.ion.conv : msgpack2ion;
637         auto buf = scopedBuffer!ubyte();
638         data.msgpack2ion(buf);
639         return deserializeIon!T(value, buf.data);
640     }
641 
642     ///
643     T deserializeMsgpack()(scope const(ubyte)[] data)
644     {
645         T value;
646         deserializeMsgpack(value, data);
647         return value;
648     }
649 }
650 
651 /// Test round-trip serialization/deserialization of signed integral types
652 @safe pure
653 version (mir_ion_test)
654 unittest
655 {
656     import mir.ser.msgpack : serializeMsgpack;
657 
658     // Bytes
659     assert(serializeMsgpack(byte.min).deserializeMsgpack!byte == byte.min);
660     assert(serializeMsgpack(byte.max).deserializeMsgpack!byte == byte.max);
661     assert(serializeMsgpack(byte(-32)).deserializeMsgpack!byte == -32);
662 
663     // Shorts
664     assert(serializeMsgpack(short.min).deserializeMsgpack!short == short.min);
665     assert(serializeMsgpack(short.max).deserializeMsgpack!short == short.max);
666 
667     // Integers
668     assert(serializeMsgpack(int.min).deserializeMsgpack!int == int.min);
669     assert(serializeMsgpack(int.max).deserializeMsgpack!int == int.max);
670 
671     // Longs
672     assert(serializeMsgpack(long.min).deserializeMsgpack!long == long.min);
673     assert(serializeMsgpack(long.max).deserializeMsgpack!long == long.max);
674 }
675 
676 /// Test round-trip serialization/deserialization of unsigned integral types
677 @safe pure
678 version (mir_ion_test)
679 unittest
680 {
681     import mir.ser.msgpack : serializeMsgpack;
682 
683     // Unsigned bytes
684     assert(serializeMsgpack(ubyte.min).deserializeMsgpack!ubyte == ubyte.min);
685     assert(serializeMsgpack(ubyte.max).deserializeMsgpack!ubyte == ubyte.max);
686 
687     // Unsigned shorts
688     assert(serializeMsgpack(ushort.min).deserializeMsgpack!ushort == ushort.min);
689     assert(serializeMsgpack(ushort.max).deserializeMsgpack!ushort == ushort.max);
690 
691     // Unsigned integers
692     assert(serializeMsgpack(uint.min).deserializeMsgpack!uint == uint.min);
693     assert(serializeMsgpack(uint.max).deserializeMsgpack!uint == uint.max);
694 
695     // Unsigned logns
696     assert(serializeMsgpack(ulong.min).deserializeMsgpack!ulong == ulong.min);
697     assert(serializeMsgpack(ulong.max).deserializeMsgpack!ulong == ulong.max);
698 
699     // BigInt
700     import mir.bignum.integer : BigInt;
701     assert(serializeMsgpack(BigInt!2(0xDEADBEEF)).deserializeMsgpack!long == 0xDEADBEEF);
702 }
703 
704 /// Test round-trip serialization/deserialization of null
705 @safe pure
706 version (mir_ion_test)
707 unittest
708 {
709     import mir.ser.msgpack : serializeMsgpack;
710 
711     assert(serializeMsgpack(null).deserializeMsgpack!(typeof(null)) == null);
712 }
713 
714 /// Test round-trip serialization/deserialization of booleans
715 @safe pure
716 version (mir_ion_test)
717 unittest
718 {
719     import mir.ser.msgpack : serializeMsgpack;
720 
721     assert(serializeMsgpack(true).deserializeMsgpack!bool == true);
722     assert(serializeMsgpack(false).deserializeMsgpack!bool == false);
723 }
724 
725 /// Test round-trip serialization/deserialization of strings
726 @safe pure
727 version (mir_ion_test)
728 unittest
729 {
730     import std.array : replicate;
731     import mir.ser.msgpack : serializeMsgpack;
732     import mir.test;
733 
734     assert("foobar".serializeMsgpack.deserializeMsgpack!string == "foobar");
735     assert("bazfoo".serializeMsgpack.deserializeMsgpack!string == "bazfoo");
736 
737     {
738         auto str = "a".replicate(32);
739         serializeMsgpack(str).deserializeMsgpack!string.should == str;
740     }
741 
742     {
743         auto str = "a".replicate(ubyte.max);
744         serializeMsgpack(str).deserializeMsgpack!string.should == str;
745     }
746 
747     {
748         auto str = "a".replicate(ubyte.max + 1);
749         serializeMsgpack(str).deserializeMsgpack!string.should == str;
750     }
751 
752     {
753         auto str = "a".replicate(ushort.max);
754         serializeMsgpack(str).deserializeMsgpack!string.should == str;
755     }
756 
757     {
758         auto str = "a".replicate(ushort.max + 1);
759         serializeMsgpack(str).deserializeMsgpack!string.should == str;
760     }
761 }
762 
763 /// Test round-trip serializing/deserialization blobs / clobs
764 @safe pure
765 version(mir_ion_test)
766 unittest
767 {
768     import mir.lob : Blob, Clob;
769     import mir.ser.msgpack : serializeMsgpack;
770     import std.array : replicate;
771 
772     // Blobs
773     // These need to be trusted because we cast const(char)[] to ubyte[] (which is fine here!)
774     () @trusted {
775         auto de = "\xde".replicate(32);
776         auto blob = Blob(cast(ubyte[])de);
777         assert(serializeMsgpack(blob).deserializeMsgpack!Blob == blob);
778     } ();
779     
780     () @trusted {
781         auto de = "\xde".replicate(ushort.max);
782         auto blob = Blob(cast(ubyte[])de);
783         assert(serializeMsgpack(blob).deserializeMsgpack!Blob == blob);
784     } ();
785 
786     () @trusted {
787         auto de = "\xde".replicate(ushort.max + 1);
788         auto blob = Blob(cast(ubyte[])de);
789         assert(serializeMsgpack(blob).deserializeMsgpack!Blob == blob);
790     } ();
791 
792     // Clobs (serialized just as regular strings here)
793     () @trusted {
794         auto de = "\xde".replicate(32);
795         auto clob = Clob(de);
796         assert(serializeMsgpack(clob).deserializeMsgpack!string == clob.data);
797     } ();
798 }
799 
800 /// Test round-trip serialization/deserialization of arrays
801 @safe pure
802 version (mir_ion_test)
803 unittest
804 {
805     import mir.ser.msgpack : serializeMsgpack;
806 
807     {
808         auto arr = [["foo"], ["bar"], ["baz"]];
809         assert(serializeMsgpack(arr).deserializeMsgpack!(typeof(arr)) == arr);
810     }
811 
812     {
813         auto arr = [0xDEADBEEF, 0xCAFEBABE, 0xAAAA_AAAA];
814         assert(serializeMsgpack(arr).deserializeMsgpack!(typeof(arr)) == arr);
815     }
816 
817     {
818         auto arr = ["foo", "bar", "baz"];
819         assert(serializeMsgpack(arr).deserializeMsgpack!(typeof(arr)) == arr);
820     }
821 
822     {
823         auto arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17];
824         assert(serializeMsgpack(arr).deserializeMsgpack!(typeof(arr)) == arr);
825     }
826 }
827 
828 @safe pure
829 version (mir_ion_test)
830 unittest
831 {
832     import mir.ser.msgpack : serializeMsgpack;
833     assert((0.0f).serializeMsgpack.deserializeMsgpack!(float) == 0.0f);
834     assert((0.0).serializeMsgpack.deserializeMsgpack!(double) == 0.0);
835 
836     assert((float.min_normal).serializeMsgpack.deserializeMsgpack!(float) == float.min_normal);
837     assert((float.max).serializeMsgpack.deserializeMsgpack!(float) == float.max);
838     assert((double.min_normal).serializeMsgpack.deserializeMsgpack!(double) == double.min_normal);
839     assert((double.max).serializeMsgpack.deserializeMsgpack!(double) == double.max);
840 }
841 
842 @safe pure
843 version (mir_ion_test)
844 unittest
845 {
846     import mir.ser.msgpack : serializeMsgpack;
847     import mir.timestamp : Timestamp;
848     assert(Timestamp(2022, 2, 14).serializeMsgpack.deserializeMsgpack!(Timestamp) == Timestamp(2022, 2, 14, 0, 0, 0).withOffset(0));
849     assert(Timestamp(2038, 1, 19, 3, 14, 7).serializeMsgpack.deserializeMsgpack!Timestamp == Timestamp(2038, 1, 19, 3, 14, 7).withOffset(0));
850     assert(Timestamp(2299, 12, 31, 23, 59, 59).serializeMsgpack.deserializeMsgpack!Timestamp == Timestamp(2299, 12, 31, 23, 59, 59, -9, 0).withOffset(0));
851     assert(Timestamp(2514, 5, 30, 1, 53, 5).serializeMsgpack.deserializeMsgpack!Timestamp == Timestamp(2514, 5, 30, 1, 53, 5, -9, 0).withOffset(0));
852     assert(Timestamp(2000, 7, 8, 2, 3, 4, -3, 16).serializeMsgpack.deserializeMsgpack!(Timestamp) == Timestamp(2000, 7, 8, 2, 3, 4, -9, 16000000).withOffset(0));
853 }
854 
855 /// Test serializing maps (structs)
856 @safe pure
857 version(mir_ion_test)
858 unittest
859 {
860     import mir.ser.msgpack : serializeMsgpack;
861     static struct Book
862     {
863         string title;
864         bool wouldRecommend;
865         string description;
866         uint numberOfNovellas;
867         double price;
868         float weight;
869         string[] tags;
870     }
871 
872     Book book = Book("A Hero of Our Time", true, "", 5, 7.99, 6.88, ["russian", "novel", "19th century"]);
873 
874     assert(serializeMsgpack(book).deserializeMsgpack!(Book) == book);
875 }
876 
877 /// Test serializing maps (structs), assuming @nogc
878 @safe pure @nogc
879 version(mir_ion_test)
880 unittest
881 {
882     import mir.appender : scopedBuffer;
883     import mir.ser.msgpack : serializeMsgpack;
884     // import mir.small_string;
885 
886     static struct Book
887     {
888         // SmallStrings apparently cannot be serialized
889         // without allocating?
890         // SmallString!64 title;
891         bool wouldRecommend;
892         // SmallString!64 description;
893         uint numberOfNovellas;
894         double price;
895         float weight;
896     }
897 
898     auto buf = scopedBuffer!ubyte();
899     Book book = Book(true, 5, 7.99, 6.88);
900     serializeMsgpack(buf, book);
901     assert(buf.data.deserializeMsgpack!Book() == book);
902 }
903 
904 /// Test round-trip serialization/deserialization of a large map
905 @safe pure
906 version(mir_ion_test)
907 unittest
908 {
909     import mir.ser.msgpack : serializeMsgpack;
910     static struct HugeStruct
911     {
912         bool a;
913         bool b;
914         bool c;
915         bool d;
916         bool e;
917         string f;
918         string g;
919         string h;
920         string i;
921         string j;
922         int k;
923         int l;
924         int m;
925         int n;
926         int o;
927         long p;
928     }
929 
930     HugeStruct s = HugeStruct(true, true, true, true, true, "", "", "", "", "", 123, 456, 789, 123, 456, 0xDEADBEEF);
931     assert(serializeMsgpack(s).deserializeMsgpack!HugeStruct == s);
932 }
933 
934 /// Test excessively large array
935 @safe pure
936 version(mir_ion_test)
937 unittest
938 {
939     import mir.ser.msgpack : serializeMsgpack;
940     static struct HugeArray
941     {
942         ubyte[] arg;
943 
944         void serialize(S)(scope ref S serializer) scope const
945         {
946             auto state = serializer.structBegin();
947             serializer.putKey("arg");
948             auto arrayState = serializer.listBegin(); 
949             foreach(i; 0 .. (ushort.max + 1))
950             {
951                 serializer.elemBegin; serializer.putValue(ubyte(0));
952             }
953             serializer.listEnd(arrayState);
954             serializer.structEnd(state);
955         }
956     }
957 
958     auto arr = HugeArray();
959     assert((serializeMsgpack(arr).deserializeMsgpack!HugeArray).arg.length == ushort.max + 1);
960 }
961 
962 /// Test excessively large map
963 @safe pure
964 version(mir_ion_test)
965 unittest
966 {
967     import mir.serde : serdeAllowMultiple;
968     import mir.ser.msgpack : serializeMsgpack;
969     static struct BFM // Big Freakin' Map
970     {
971         @serdeAllowMultiple
972         ubyte asdf;
973 
974         void serialize(S)(scope ref S serializer) scope const
975         {
976             auto state = serializer.structBegin();
977             foreach (i; 0 .. (ushort.max + 1))
978             {
979                 serializer.putKey("asdf");
980                 serializer.putValue(ubyte(0));
981             }
982             serializer.structEnd(state);
983         }
984     }
985 
986     auto map = BFM();
987     assert(serializeMsgpack(map).deserializeMsgpack!BFM == map);
988 }
989 
990 /// Test map with varying key lengths
991 @safe pure
992 version(mir_ion_test)
993 unittest
994 {
995     import mir.ser.msgpack : serializeMsgpack;
996     import std.array : replicate;
997     ubyte[string] map;
998     map["a".replicate(32)] = 0xFF;
999     map["b".replicate(ubyte.max + 1)] = 0xFF;
1000     map["c".replicate(ushort.max + 1)] = 0xFF;
1001 
1002     assert(serializeMsgpack(map).deserializeMsgpack!(typeof(map)) == map);
1003 }
1004 
1005 /// Test deserializing an extension type
1006 @safe pure
1007 version(mir_ion_test)
1008 unittest
1009 {
1010     import mir.lob : Blob;
1011 
1012     {
1013         const(ubyte)[] data = [0xc7, 0x01, 0x02, 0xff];
1014         MsgpackExtension ext = MsgpackExtension(Blob([0xff]), 0x02);
1015         assert(data.deserializeMsgpack!MsgpackExtension == ext);
1016     }
1017 
1018     {
1019         const(ubyte)[] data = [0xc8, 0x00, 0x01, 0x02, 0xff];
1020         MsgpackExtension ext = MsgpackExtension(Blob([0xff]), 0x02);
1021         assert(data.deserializeMsgpack!MsgpackExtension == ext);
1022     }
1023 
1024     {
1025         const(ubyte)[] data = [0xc9, 0x00, 0x00, 0x00, 0x01, 0x02, 0xff];
1026         MsgpackExtension ext = MsgpackExtension(Blob([0xff]), 0x02);
1027         assert(data.deserializeMsgpack!MsgpackExtension == ext);
1028     }
1029 }
1030 
1031 @safe pure
1032 version(mir_ion_test) unittest
1033 {
1034     static struct S
1035     {
1036         bool compact;
1037         int schema;
1038     }
1039     const(ubyte)[] data = [0x82, 0xa7, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xc3, 0xa6, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x04];
1040     assert(data.deserializeMsgpack!S == S(true, 4));
1041 }
1042 
1043 @safe pure
1044 version(mir_ion_test) unittest
1045 {
1046     // fixnint
1047     {
1048         const(ubyte)[] data = [0x81, 0xe0, 0xcc, 0xfe];
1049         assert(data.deserializeMsgpack!(int[string]) == ["-32": 0xfe]);
1050     }
1051 
1052     // fixint
1053     {
1054         const(ubyte)[] data = [0x81, 0x7f, 0xcc, 0xfe];
1055         assert(data.deserializeMsgpack!(int[string]) == ["127": 0xfe]);
1056     }
1057 
1058     // uint8
1059     {
1060         const(ubyte)[] data = [0x81, 0xcc, 0xff, 0xcc, 0xfe];
1061         assert(data.deserializeMsgpack!(int[string]) == ["255": 0xfe]);
1062     }
1063 
1064     // int8
1065     {
1066         const(ubyte)[] data = [0x81, 0xd0, 0x81, 0xcc, 0xfe];
1067         assert(data.deserializeMsgpack!(int[string]) == ["-127": 0xfe]);
1068     }
1069 
1070     // uint16
1071     {
1072         const(ubyte)[] data = [0x81, 0xcd, 0xde, 0xad, 0xcc, 0xfe];
1073         assert(data.deserializeMsgpack!(int[string]) == ["57005": 0xfe]);
1074     }
1075     
1076     // int16
1077     {
1078         const(ubyte)[] data = [0x81, 0xd1, 0xde, 0xad, 0xcc, 0xfe];
1079         assert(data.deserializeMsgpack!(int[string]) == ["-8531": 0xfe]);
1080     }
1081 
1082     // uint32
1083     {
1084         const(ubyte)[] data = [0x81, 0xce, 0xde, 0xad, 0xbe, 0xef, 0xcc, 0xfe];
1085         assert(data.deserializeMsgpack!(int[string]) == ["3735928559": 0xfe]);
1086     }
1087 
1088     // int32
1089     {
1090         const(ubyte)[] data = [0x81, 0xd2, 0xde, 0xad, 0xbe, 0xef, 0xcc, 0xfe];
1091         assert(data.deserializeMsgpack!(int[string]) == ["-559038737": 0xfe]);
1092     }
1093 
1094     static if (ulong.sizeof == 8)
1095     {
1096         // uint64
1097         {
1098             const(ubyte)[] data = [0x81, 0xcf, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xcc, 0xfe];
1099             assert(data.deserializeMsgpack!(int[string]) == ["16045690984833335023": 0xfe]);
1100         }
1101 
1102         // int64
1103         {
1104             const(ubyte)[] data = [0x81, 0xd3, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xcc, 0xfe];
1105             assert(data.deserializeMsgpack!(int[string]) == ["-2401053088876216593": 0xfe]);
1106         }
1107     }
1108 }
1109 
1110 @safe pure
1111 version(mir_ion_test) unittest
1112 {
1113     import mir.ser.msgpack : serializeMsgpack;
1114     import mir.timestamp : Timestamp;
1115 
1116     {
1117         auto time = Timestamp(2022, 2, 14, 0, 0, 0).withOffset(0);
1118         const(ubyte)[] data = [0x81];
1119         data ~= time.serializeMsgpack;
1120         data ~= [0xcc, 0xfe];
1121         assert(data.deserializeMsgpack!(int[string]) == [time.toString(): 0xfe]);
1122     }
1123 
1124     {
1125         auto time = Timestamp(2038, 1, 19, 3, 14, 7).withOffset(0);
1126         const(ubyte)[] data = [0x81];
1127         data ~= time.serializeMsgpack;
1128         data ~= [0xcc, 0xfe];
1129         assert(data.deserializeMsgpack!(int[string]) == [time.toString(): 0xfe]);
1130     }
1131 
1132     {
1133         auto time = Timestamp(2299, 12, 31, 23, 59, 59).withOffset(0);
1134         const(ubyte)[] data = [0x81];
1135         data ~= time.serializeMsgpack;
1136         data ~= [0xcc, 0xfe];
1137         assert(data.deserializeMsgpack!(int[string]) == [Timestamp(2299, 12, 31, 23, 59, 59, -9, 0).withOffset(0).toString(): 0xfe]);
1138     }
1139 
1140     {
1141         auto time = Timestamp(2514, 5, 30, 1, 53, 5).withOffset(0);
1142         const(ubyte)[] data = [0x81];
1143         data ~= time.serializeMsgpack;
1144         data ~= [0xcc, 0xfe];
1145         assert(data.deserializeMsgpack!(int[string]) == [Timestamp(2514, 5, 30, 1, 53, 5, -9, 0).withOffset(0).toString(): 0xfe]);
1146     }
1147 
1148     // ext8
1149     {
1150         auto time = Timestamp(2000, 7, 8, 2, 3, 4, -3, 16).withOffset(0);
1151         const(ubyte)[] data = [0x81];
1152         data ~= time.serializeMsgpack;
1153         data ~= [0xcc, 0xfe];
1154         assert(data.deserializeMsgpack!(int[string]) == [Timestamp(2000, 7, 8, 2, 3, 4, -9, 16000000).withOffset(0).toString(): 0xfe]);
1155     }
1156 
1157     // ext16
1158     {
1159         const(ubyte)[] data = [0x81, 0xc8, 0x00, 0x08, 0xff, 0x03, 0xd0, 0x90, 0x00, 0x39, 0x66, 0x8b, 0xd8, 0xcc, 0xfe];
1160         assert(data.deserializeMsgpack!(int[string]) == [Timestamp(2000, 7, 8, 2, 3, 4, -9, 16_000_000).withOffset(0).toString(): 0xfe]);
1161     }
1162 
1163     // ext32
1164     {
1165         const(ubyte)[] data = [0x81, 0xc9, 0x00, 0x00, 0x00, 0x08, 0xff, 0x03, 0xd0, 0x90, 0x00, 0x39, 0x66, 0x8b, 0xd8, 0xcc, 0xfe];
1166         assert(data.deserializeMsgpack!(int[string]) == [Timestamp(2000, 7, 8, 2, 3, 4, -9, 16_000_000).withOffset(0).toString(): 0xfe]);
1167     }
1168 }
1169 
1170 // Test bad MessagePack data
1171 version (mir_ion_test)
1172 unittest
1173 {
1174     import mir.ion.exception : IonException;
1175 
1176     // Run out of bytes before a full integer can be read
1177     {
1178         const(ubyte)[] data = [0xdb, 0x00, 0x00];
1179         bool caught = false;
1180         try
1181         {
1182             data.deserializeMsgpack!(string);
1183         }
1184         catch (IonException e)
1185         {
1186             caught = true;
1187         }
1188 
1189         assert(caught);
1190     }
1191 
1192     // Run out of bytes in a map
1193     {
1194         const(ubyte)[] data = [0x81];
1195         bool caught = false;
1196         try
1197         {
1198             data.deserializeMsgpack!(int[string]);
1199         }
1200         catch (IonException e)
1201         {
1202             caught = true;
1203         }
1204 
1205         assert(caught);
1206     }
1207     
1208     // Run out of bytes in a list
1209     {
1210         const(ubyte)[] data = [0x91];
1211         bool caught = false;
1212         try
1213         {
1214             data.deserializeMsgpack!(int[]);
1215         }
1216         catch (IonException e)
1217         {
1218             caught = true;
1219         }
1220 
1221         assert(caught);
1222     }
1223 
1224     // Run out of bytes in a string
1225     {
1226         const(ubyte)[] data = [0xa1];
1227         bool caught = false;
1228         try
1229         {
1230             data.deserializeMsgpack!(string);
1231         }
1232         catch (IonException e)
1233         {
1234             caught = true;
1235         }
1236 
1237         assert(caught);
1238     }
1239     
1240     // Run out of bytes in a binary blob
1241     {
1242         import mir.lob : Blob;
1243         const(ubyte)[] data = [0xc4, 0x01];
1244         bool caught = false;
1245         try
1246         {
1247             data.deserializeMsgpack!(Blob);
1248         }
1249         catch (IonException e)
1250         {
1251             caught = true;
1252         }
1253         
1254         assert(caught);
1255     }
1256 
1257     // Run out of bytes in a extension type
1258     {
1259         const(ubyte)[] data = [0xd4];
1260         bool caught = false;
1261         try
1262         {
1263             data.deserializeMsgpack!(MsgpackExtension);
1264         }
1265         catch (IonException e)
1266         {
1267             caught = true;
1268         }
1269 
1270         assert(caught);
1271     }
1272 
1273     // Test a map with a timestamp key that runs out of bytes
1274     {
1275         const(ubyte)[] data = [0x81, 0xc7, 0xff];
1276         bool caught = false;
1277         try
1278         {
1279             data.deserializeMsgpack!(int[string]);
1280         }
1281         catch (IonException e)
1282         {
1283             caught = true;
1284         }
1285 
1286         assert(caught);
1287     }
1288 
1289     // Test a map with a timestamp key that has an invalid length
1290     {
1291         const(ubyte)[] data = [0x81, 0xc7, 0x01, 0xff, 0x00, 0xcc, 0xfe];
1292         bool caught = false;
1293         try
1294         {
1295             data.deserializeMsgpack!(int[string]);
1296         }
1297         catch (IonException e)
1298         {
1299             caught = true;
1300         }
1301 
1302         assert(caught);
1303     }
1304 
1305     // Test a map with an invalid extension type
1306     {
1307         const(ubyte)[] data = [0x81, 0xc7, 0x01, 0xfe, 0x00, 0xcc, 0xfe];
1308         bool caught = false;
1309         try
1310         {
1311             data.deserializeMsgpack!(int[string]);
1312         }
1313         catch (IonException e)
1314         {
1315             caught = true;
1316         }
1317 
1318         assert(caught);
1319     }
1320 
1321     // Test a map with an unrecognized key
1322     {
1323         const(ubyte)[] data = [0x81, 0xc4];
1324         bool caught = false;
1325         try
1326         {
1327             data.deserializeMsgpack!(int[string]);
1328         }
1329         catch (IonException e)
1330         {
1331             caught = true;
1332         }
1333 
1334         assert(caught);
1335     }
1336 
1337     // Test an invalid type descriptor
1338     {
1339         const(ubyte)[] data = [0xc1];
1340         bool caught = false;
1341         try
1342         {
1343             data.deserializeMsgpack!(bool);
1344         }
1345         catch (IonException e)
1346         {
1347             caught = true;
1348         }
1349 
1350         assert(caught);
1351     }
1352 }