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 }