1 module msgpack.unpacker; 2 3 import msgpack.common; 4 import msgpack.attribute; 5 import msgpack.exception; 6 7 import std.array; 8 import std.exception; 9 import std.range; 10 import std.stdio; 11 import std.traits; 12 import std.typecons; 13 import std.typetuple; 14 import std.container; 15 16 17 // for unpack without calling constructor 18 private extern(C) Object _d_newclass(in ClassInfo); 19 20 21 /** 22 * This $(D Unpacker) is a $(D MessagePack) direct-conversion deserializer 23 * 24 * This implementation is suitable for fixed data. 25 * 26 * Example: 27 * ----- 28 * // serializedData is [10, 0.1, false] 29 * auto unpacker = Unpacker(serializedData); 30 * 31 * uint n; 32 * double d; 33 * bool b; 34 * 35 * unpacker.unpackArray(n, d, b); 36 * 37 * // using Tuple 38 * Tuple!(uint, double, bool) record; 39 * unpacker.unpack(record); // record is [10, 0.1, false] 40 * ----- 41 * 42 * NOTE: 43 * Unpacker becomes template struct if Phobos supports truly IO module. 44 */ 45 struct Unpacker 46 { 47 private: 48 static @system 49 { 50 alias void delegate(ref Unpacker, void*) UnpackHandler; 51 UnpackHandler[TypeInfo] unpackHandlers; 52 53 public void registerHandler(T, alias Handler)() 54 { 55 unpackHandlers[typeid(T)] = delegate(ref Unpacker unpacker, void* obj) { 56 Handler(unpacker, *cast(T*)obj); 57 }; 58 } 59 60 public void register(T)() 61 { 62 unpackHandlers[typeid(T)] = delegate(ref Unpacker unpacker, void* obj) { 63 unpacker.unpackObject(*cast(T*)obj); 64 }; 65 } 66 67 } 68 69 enum Offset = 1; 70 71 mixin InternalBuffer; 72 73 bool withFieldName_; 74 75 76 public: 77 /** 78 * Constructs a $(D Unpacker). 79 * 80 * Params: 81 * target = byte buffer to deserialize 82 * bufferSize = size limit of buffer size 83 */ 84 this(in ubyte[] target, in size_t bufferSize = 8192, bool withFieldName = false) 85 { 86 initializeBuffer(target, bufferSize); 87 withFieldName_ = withFieldName; 88 } 89 90 91 /** 92 * Clears states for next deserialization. 93 */ 94 @safe 95 nothrow void clear() 96 { 97 used_ = offset_ = parsed_ = 0; 98 hasRaw_ = false; 99 } 100 101 102 /** 103 * Deserializes $(D_PARAM T) object and assigns to $(D_PARAM value). 104 * 105 * If the argument is pointer, dereferences pointer and assigns deserialized value. 106 * ----- 107 * int* a; 108 * unpacker.unpack(a) // enforce throws Exception because a is null or 109 * // no throw if deserialized value is nil 110 * 111 * int b; a = &b; 112 * unpacker.unpack(b) // b is deserialized value or 113 * // assigns null if deserialized value is nil 114 * ----- 115 * 116 * Params: 117 * value = the reference of value to assign. 118 * 119 * Returns: 120 * self, i.e. for method chaining. 121 * 122 * Throws: 123 * UnpackException when doesn't read from buffer or precision loss occurs and 124 * MessagePackException when $(D_PARAM T) type doesn't match serialized type. 125 */ 126 ref Unpacker unpack(T)(ref T value) if (is(Unqual!T == bool)) 127 { 128 canRead(Offset, 0); 129 const header = read(); 130 131 switch (header) { 132 case Format.TRUE: 133 value = true; 134 break; 135 case Format.FALSE: 136 value = false; 137 break; 138 default: 139 rollback(0, "bool", cast(Format)header); 140 } 141 142 return this; 143 } 144 145 146 /// ditto 147 ref Unpacker unpack(T)(ref T value) if (isUnsigned!T && !is(Unqual!T == enum)) 148 { 149 canRead(Offset, 0); 150 const header = read(); 151 152 if (0x00 <= header && header <= 0x7f) { 153 value = header; 154 } else { 155 switch (header) { 156 case Format.UINT8: 157 canRead(ubyte.sizeof); 158 value = read(); 159 break; 160 case Format.UINT16: 161 canRead(ushort.sizeof); 162 auto us = load16To!ushort(read(ushort.sizeof)); 163 if (us > T.max) 164 rollback(ushort.sizeof, T.stringof, Format.UINT16); 165 value = cast(T)us; 166 break; 167 case Format.UINT32: 168 canRead(uint.sizeof); 169 auto ui = load32To!uint(read(uint.sizeof)); 170 if (ui > T.max) 171 rollback(uint.sizeof, T.stringof, Format.UINT32); 172 value = cast(T)ui; 173 break; 174 case Format.UINT64: 175 canRead(ulong.sizeof); 176 auto ul = load64To!ulong(read(ulong.sizeof)); 177 if (ul > T.max) 178 rollback(ulong.sizeof, T.stringof, Format.UINT64); 179 value = cast(T)ul; 180 break; 181 default: 182 rollback(0, T.stringof, cast(Format)header); 183 } 184 } 185 186 return this; 187 } 188 189 190 /// ditto 191 ref Unpacker unpack(T)(ref T value) if (isSigned!T && isIntegral!T && !is(Unqual!T == enum)) 192 { 193 canRead(Offset, 0); 194 const header = read(); 195 196 if (0x00 <= header && header <= 0x7f) { 197 value = cast(T)header; 198 } else if (0xe0 <= header && header <= 0xff) { 199 value = -(cast(T)-header); 200 } else { 201 switch (header) { 202 case Format.UINT8: 203 canRead(ubyte.sizeof); 204 auto ub = read(); 205 if (ub > T.max) 206 rollback(ubyte.sizeof, T.stringof, Format.UINT8); 207 value = cast(T)ub; 208 break; 209 case Format.UINT16: 210 canRead(ushort.sizeof); 211 auto us = load16To!ushort(read(ushort.sizeof)); 212 if (us > T.max) 213 rollback(ushort.sizeof, T.stringof, Format.UINT16); 214 value = cast(T)us; 215 break; 216 case Format.UINT32: 217 canRead(uint.sizeof); 218 auto ui = load32To!uint(read(uint.sizeof)); 219 if (ui > T.max) 220 rollback(uint.sizeof, T.stringof, Format.UINT32); 221 value = cast(T)ui; 222 break; 223 case Format.UINT64: 224 canRead(ulong.sizeof); 225 auto ul = load64To!ulong(read(ulong.sizeof)); 226 if (ul > T.max) 227 rollback(ulong.sizeof, T.stringof, Format.UINT64); 228 value = cast(T)ul; 229 break; 230 case Format.INT8: 231 canRead(byte.sizeof); 232 value = cast(byte)read(); 233 break; 234 case Format.INT16: 235 canRead(short.sizeof); 236 auto s = load16To!short(read(short.sizeof)); 237 if (s < T.min || T.max < s) 238 rollback(short.sizeof, T.stringof, Format.INT16); 239 value = cast(T)s; 240 break; 241 case Format.INT32: 242 canRead(int.sizeof); 243 auto i = load32To!int(read(int.sizeof)); 244 if (i < T.min || T.max < i) 245 rollback(int.sizeof, T.stringof, Format.INT32); 246 value = cast(T)i; 247 break; 248 case Format.INT64: 249 canRead(long.sizeof); 250 auto l = load64To!long(read(long.sizeof)); 251 if (l < T.min || T.max < l) 252 rollback(long.sizeof, T.stringof, Format.INT64); 253 value = cast(T)l; 254 break; 255 default: 256 rollback(0, T.stringof, cast(Format)header); 257 } 258 } 259 260 return this; 261 } 262 263 264 /// ditto 265 ref Unpacker unpack(T)(ref T value) if (isSomeChar!T && !is(Unqual!T == enum)) 266 { 267 static if (is(Unqual!T == char)) { 268 ubyte tmp; 269 } else static if (is(Unqual!T == wchar)) { 270 ushort tmp; 271 } else static if (is(Unqual!T == dchar)) { 272 uint tmp; 273 } 274 unpack(tmp); 275 value = cast(T)(tmp); 276 return this; 277 } 278 279 280 /// ditto 281 ref Unpacker unpack(T)(ref T value) if (isFloatingPoint!T && !is(Unqual!T == enum)) 282 { 283 canRead(Offset, 0); 284 const header = read(); 285 286 switch (header) { 287 case Format.FLOAT: 288 _f temp; 289 290 canRead(uint.sizeof); 291 temp.i = load32To!uint(read(uint.sizeof)); 292 value = temp.f; 293 break; 294 case Format.DOUBLE: 295 // check precision loss 296 static if (is(Unqual!T == float)) 297 rollback(0, T.stringof, Format.DOUBLE); 298 299 _d temp; 300 301 canRead(ulong.sizeof); 302 temp.i = load64To!ulong(read(ulong.sizeof)); 303 value = temp.f; 304 break; 305 case Format.REAL: 306 static if (!EnableReal) 307 { 308 rollback(0, "real is disabled", Format.REAL); 309 } 310 else 311 { 312 // check precision loss 313 static if (is(Unqual!T == float) || is(Unqual!T == double)) 314 rollback(0, T.stringof, Format.REAL); 315 316 canRead(RealSize); 317 318 version (NonX86) 319 { 320 CustomFloat!80 temp; 321 322 const frac = load64To!ulong (read(ulong.sizeof)); 323 const exp = load16To!ushort(read(ushort.sizeof)); 324 325 temp.significand = frac; 326 temp.exponent = exp & 0x7fff; 327 temp.sign = exp & 0x8000 ? true : false; 328 329 // NOTE: temp.get!real is inf on non-x86 when deserialized value is larger than double.max. 330 value = temp.get!real; 331 } 332 else 333 { 334 _r temp; 335 336 temp.fraction = load64To!(typeof(temp.fraction))(read(temp.fraction.sizeof)); 337 temp.exponent = load16To!(typeof(temp.exponent))(read(temp.exponent.sizeof)); 338 339 value = temp.f; 340 } 341 } 342 343 break; 344 default: 345 rollback(0, T.stringof, cast(Format)header); 346 } 347 348 return this; 349 } 350 351 352 /// ditto 353 ref Unpacker unpack(T)(ref T value) if (is(Unqual!T == enum)) 354 { 355 OriginalType!T temp; 356 357 unpack(temp); 358 359 value = cast(T)temp; 360 361 return this; 362 } 363 364 365 /// ditto 366 ref Unpacker unpack(T)(T value) if (isPointer!T) 367 { 368 static if (is(Unqual!T == void*)) { 369 enforce(value !is null, "Can't deserialize void type"); 370 unpackNil(value); 371 } else { 372 if (checkNil()) 373 unpackNil(value); 374 else 375 enforce(value !is null, T.stringof ~ " is null pointer"); 376 377 unpack(mixin(AsteriskOf!T ~ "value")); 378 } 379 380 return this; 381 } 382 383 384 /// ditto 385 ref Unpacker unpack(T)(ref T value) if (is(T == ExtValue)) 386 { 387 canRead(Offset, 0); 388 const header = read(); 389 390 // Fixed 391 if (header >= Format.EXT && header <= Format.EXT + 4) 392 { 393 const length = 2^^(header - Format.EXT); 394 canRead(1 + length); 395 396 value.type = read(); 397 value.data = read(length); 398 return this; 399 } 400 401 // Dynamic length 402 uint length; 403 switch (header) with (Format) 404 { 405 case EXT8: 406 canRead(1); 407 length = read(); 408 break; 409 case EXT16: 410 canRead(2); 411 length = load16To!ushort(read(2)); 412 break; 413 case EXT32: 414 canRead(4); 415 length = load32To!uint(read(4)); 416 break; 417 default: 418 rollback(0, T.stringof, cast(Format)header); 419 } 420 421 canRead(1 + length); 422 value.type = read(); 423 value.data = read(length); 424 425 return this; 426 } 427 428 429 /// ditto 430 ref Unpacker unpack(Types...)(ref Types objects) if (Types.length > 1) 431 { 432 foreach (i, T; Types) 433 unpack!(T)(objects[i]); 434 435 return this; 436 } 437 438 439 /** 440 * Deserializes $(D_PARAM T) object and assigns to $(D_PARAM array). 441 * 442 * This is convenient method for array deserialization. 443 * Rollback will be completely successful if you deserialize raw type((u)byte[] or string types). 444 * But, Rollback will be one element(e.g. int) if you deserialize other types(e.g. int[], int[int]) 445 * 446 * No assign if the length of deserialized object is 0. 447 * 448 * In a static array, this method checks the length. Do rollback and throw exception 449 * if length of $(D_PARAM array) is different from length of deserialized object. 450 * 451 * Params: 452 * array = the reference of array to assign. 453 * 454 * Returns: 455 * self, i.e. for method chaining. 456 * 457 * Throws: 458 * UnpackException when doesn't read from buffer or precision loss occurs and 459 * MessagePackException when $(D_PARAM T) type doesn't match serialized type. 460 */ 461 ref Unpacker unpack(T)(ref T array) if ((isArray!T || 462 isInstanceOf!(Array, T)) && 463 !is(Unqual!T == enum)) 464 { 465 alias typeof(T.init[0]) U; 466 467 /* 468 * Deserializes type-information of raw type. 469 */ 470 @safe 471 size_t beginRaw() 472 { 473 canRead(Offset, 0); 474 const header = read(); 475 size_t length; 476 477 if (0xa0 <= header && header <= 0xbf) { 478 length = header & 0x1f; 479 } else { 480 switch (header) { 481 case Format.BIN8, Format.STR8: 482 canRead(ubyte.sizeof); 483 length = read(); 484 break; 485 case Format.BIN16, Format.RAW16: 486 canRead(ushort.sizeof); 487 length = load16To!size_t(read(ushort.sizeof)); 488 break; 489 case Format.BIN32, Format.RAW32: 490 canRead(uint.sizeof); 491 length = load32To!size_t(read(uint.sizeof)); 492 break; 493 case Format.NIL: 494 break; 495 default: 496 rollback(0, T.stringof, cast(Format)header); 497 } 498 } 499 500 return length; 501 } 502 503 504 if (checkNil()) { 505 static if (isStaticArray!T) { 506 onInvalidType("static array", Format.NIL); 507 } else { 508 return unpackNil(array); 509 } 510 } 511 512 // Raw bytes 513 static if (isByte!U || isSomeChar!U) 514 auto length = beginRaw(); 515 else 516 auto length = beginArray(); 517 518 if(length > buffer_.length) { 519 import std.conv: text; 520 throw new MessagePackException(text("Invalid array size in byte stream: Length (", length, 521 ") is larger than internal buffer size (", buffer_.length, ")")); 522 } 523 524 // Raw bytes 525 static if (isByte!U || isSomeChar!U) { 526 auto offset = calculateSize!(true)(length); 527 if (length == 0) 528 return this; 529 530 static if (isStaticArray!T) { 531 if (length != array.length) 532 rollback(offset, "static array was given but the length is mismatched"); 533 } 534 535 canRead(length, offset + Offset); 536 static if (isStaticArray!T) { 537 array[] = (cast(U[])read(length))[0 .. T.length]; 538 } else { 539 array = cast(T)read(length); 540 } 541 542 static if (isDynamicArray!T) 543 hasRaw_ = true; 544 } else { 545 if (length == 0) 546 return this; 547 548 static if (isStaticArray!T) { 549 if (length != array.length) 550 rollback(calculateSize(length), "static array was given but the length is mismatched"); 551 } else { 552 array.length = length; 553 } 554 555 foreach (i; 0..length) 556 unpack(array[i]); 557 } 558 559 return this; 560 } 561 562 563 /// ditto 564 ref Unpacker unpack(T)(ref T array) if (isAssociativeArray!T) 565 { 566 alias typeof(T.init.keys[0]) K; 567 alias typeof(T.init.values[0]) V; 568 569 if (checkNil()) 570 return unpackNil(array); 571 572 auto length = beginMap(); 573 if (length == 0) 574 return this; 575 576 foreach (i; 0..length) { 577 K k; unpack(k); 578 V v; unpack(v); 579 array[k] = v; 580 } 581 582 return this; 583 } 584 585 /** 586 * Deserializes $(D_PARAM T) object and assigns to $(D_PARAM object). 587 * 588 * Calling $(D fromMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D fromMsgpack) method. $(D fromMsgpack) signature is: 589 * ----- 590 * void fromMsgpack(ref Unpacker unpacker) 591 * ----- 592 * Assumes $(D std.typecons.Tuple) or simple struct if $(D_KEYWORD struct) doesn't implement $(D fromMsgpack). 593 * Checks length if $(D_PARAM T) is a $(D std.typecons.Tuple) or simple struct. 594 * 595 * Params: 596 * object = the reference of object to assign. 597 * args = the arguments to class constructor(class only). 598 * This is used at new statement if $(D_PARAM object) is $(D_KEYWORD null). 599 * 600 * Returns: 601 * self, i.e. for method chaining. 602 */ 603 ref Unpacker unpack(T, Args...)(ref T object, auto ref Args args) if (is(Unqual!T == class)) 604 { 605 if (checkNil()) 606 return unpackNil(object); 607 608 if (object is null) { 609 static if (Args.length == 0) { 610 static if (__traits(compiles, { new T(); })) 611 object = new T(); 612 else 613 object = cast(T)_d_newclass(T.classinfo); 614 } else static if (__traits(compiles, { new T(args); })) { 615 object = new T(args); 616 } else { 617 throw new MessagePackException("Don't know how to construct class type '" ~ Unqual!T.stringof ~ "' with argument types '" ~ Args.stringof ~ "'."); 618 } 619 } 620 621 static if (hasMember!(T, "fromMsgpack")) 622 { 623 static if (__traits(compiles, { object.fromMsgpack(this, withFieldName_); })) { 624 object.fromMsgpack(this, withFieldName_); 625 } else static if (__traits(compiles, { object.fromMsgpack(this); })) { // backward compatible 626 object.fromMsgpack(this); 627 } else { 628 static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 629 } 630 } else { 631 if (auto handler = object.classinfo in unpackHandlers) { 632 (*handler)(this, cast(void*)&object); 633 return this; 634 } 635 if (T.classinfo !is object.classinfo) { 636 throw new MessagePackException("Can't unpack derived class through reference to base class."); 637 } 638 639 unpackObject(object); 640 } 641 642 return this; 643 } 644 645 646 /// ditto 647 ref Unpacker unpack(T)(ref T object) if (is(Unqual!T == struct) && 648 !is(Unqual!T == ExtValue)) 649 { 650 static if (hasMember!(T, "fromMsgpack")) 651 { 652 static if (__traits(compiles, { object.fromMsgpack(this); })) { 653 object.fromMsgpack(this); 654 } else { 655 static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 656 } 657 } else { 658 if (auto handler = typeid(T) in unpackHandlers) { 659 (*handler)(this, cast(void*)&object); 660 return this; 661 } 662 663 size_t length = withFieldName_ ? beginMap() : beginArray(); 664 if (length == 0) 665 return this; 666 667 static if (isTuple!T) { 668 if (length != T.Types.length) 669 rollback(calculateSize(length), "the number of tuple fields is mismatched"); 670 671 foreach (i, Type; T.Types) 672 unpack(object.field[i]); 673 } else { // simple struct 674 //if (length != object.tupleof.length) 675 if (length != SerializingMemberNumbers!(T)) 676 rollback(calculateSize(length), "the number of struct fields is mismatched"); 677 678 if (withFieldName_) { 679 foreach (i, member; object.tupleof) { 680 static if (isPackedField!(T.tupleof[i])) 681 { 682 string fieldName; 683 unpack(fieldName); 684 685 if (fieldName == getFieldName!(T, i)) { 686 unpack(object.tupleof[i]); 687 } else { 688 assert(false, "Invalid field name: '" ~ fieldName ~ "', expect '" ~ getFieldName!(T, i) ~ "'"); 689 } 690 } 691 } 692 } else { 693 foreach (i, member; object.tupleof) { 694 static if (isPackedField!(T.tupleof[i])) 695 unpack(object.tupleof[i]); 696 } 697 } 698 } 699 } 700 701 return this; 702 } 703 704 705 void unpackObject(T)(ref T object) if (is(Unqual!T == class)) 706 { 707 alias SerializingClasses!(T) Classes; 708 709 size_t length = withFieldName_ ? beginMap() : beginArray(); 710 if (length == 0) 711 return; 712 713 if (length != SerializingMemberNumbers!(Classes)) 714 rollback(calculateSize(length), "the number of class fields is mismatched"); 715 716 if (withFieldName_) { 717 foreach (_; 0..length) { 718 string fieldName; 719 unpack(fieldName); 720 721 foreach (Class; Classes) { 722 Class obj = cast(Class)object; 723 724 foreach (i, member; obj.tupleof) { 725 static if (isPackedField!(Class.tupleof[i])) 726 { 727 if (fieldName == getFieldName!(Class, i)) { 728 unpack(obj.tupleof[i]); 729 goto endLoop; 730 } 731 } 732 } 733 } 734 assert(false, "Invalid field name: '" ~ fieldName~"' "); 735 736 endLoop: 737 continue; 738 } 739 } else { 740 foreach (Class; Classes) { 741 Class obj = cast(Class)object; 742 743 foreach (i, member; obj.tupleof) { 744 static if (isPackedField!(Class.tupleof[i])) 745 unpack(obj.tupleof[i]); 746 } 747 } 748 } 749 } 750 751 752 /** 753 * Deserializes the container object and assigns to each argument. 754 * 755 * These methods check the length. Do rollback if 756 * the length of arguments is different from length of deserialized object. 757 * 758 * In unpackMap, the number of arguments must be even. 759 * 760 * Params: 761 * objects = the references of object to assign. 762 * 763 * Returns: 764 * self, i.e. for method chaining. 765 */ 766 ref Unpacker unpackArray(Types...)(ref Types objects) 767 { 768 auto length = beginArray(); 769 if (length != Types.length) 770 rollback(calculateSize(length), "the number of deserialized objects is mismatched"); 771 772 foreach (i, T; Types) 773 unpack(objects[i]); 774 // unpack(objects); // slow :( 775 776 return this; 777 } 778 779 780 /// ditto 781 ref Unpacker unpackMap(Types...)(ref Types objects) 782 { 783 static assert(Types.length % 2 == 0, "The number of arguments must be even"); 784 785 auto length = beginMap(); 786 if (length != Types.length / 2) 787 rollback(calculateSize(length), "the number of deserialized objects is mismatched"); 788 789 foreach (i, T; Types) 790 unpack(objects[i]); 791 792 return this; 793 } 794 795 796 /** 797 * Deserializes the type-information of container. 798 * 799 * These methods don't deserialize contents. 800 * You need to call unpack method to deserialize contents at your own risk. 801 * ----- 802 * // serialized data is [1, "Hi!"]; 803 * int num; 804 * unpacker.beginArray(2).unpack(num); // num is 1 805 * 806 * // other operation 807 * 808 * string str; 809 * unpacker.unpack(str); // str is "Hi!" 810 * ----- 811 * 812 * Returns: 813 * the container size. 814 */ 815 @safe 816 size_t beginArray() 817 { 818 canRead(Offset, 0); 819 const header = read(); 820 size_t length; 821 822 if (0x90 <= header && header <= 0x9f) { 823 length = header & 0x0f; 824 } else { 825 switch (header) { 826 case Format.ARRAY16: 827 canRead(ushort.sizeof); 828 length = load16To!size_t(read(ushort.sizeof)); 829 break; 830 case Format.ARRAY32: 831 canRead(uint.sizeof); 832 length = load32To!size_t(read(uint.sizeof)); 833 break; 834 case Format.NIL: 835 break; 836 default: 837 rollback(0, "array", cast(Format)header); 838 } 839 } 840 841 return length; 842 } 843 844 845 /// ditto 846 @safe 847 size_t beginMap() 848 { 849 canRead(Offset, 0); 850 const header = read(); 851 size_t length; 852 853 if (0x80 <= header && header <= 0x8f) { 854 length = header & 0x0f; 855 } else { 856 switch (header) { 857 case Format.MAP16: 858 canRead(ushort.sizeof); 859 length = load16To!size_t(read(ushort.sizeof)); 860 break; 861 case Format.MAP32: 862 canRead(uint.sizeof); 863 length = load32To!size_t(read(uint.sizeof)); 864 break; 865 case Format.NIL: 866 break; 867 default: 868 rollback(0, "map", cast(Format)header); 869 } 870 } 871 872 return length; 873 } 874 875 876 /** 877 * Unpacks an EXT value into $(D type) and $(D data). 878 * $(D type) is checked and a $(D MessagePackException) is thrown if it does 879 * not match. The length of $(D data) is checked and a $(D MessagePackException) 880 * is thrown if the lengths do not match. If $(D data) is null, a new slice 881 * is returned. 882 */ 883 ref Unpacker unpackExt(ref byte type, ref ubyte[] data) 884 { 885 import std.conv : text; 886 887 canRead(Offset, 0); 888 const header = read(); 889 890 uint length; 891 uint rollbackLength = 0; 892 if (header >= Format.EXT && header <= Format.EXT + 4) 893 { 894 // Fixed 895 length = 2^^(header - Format.EXT); 896 897 } else { 898 // Dynamic length 899 switch (header) with (Format) 900 { 901 case EXT8: 902 canRead(1); 903 length = read(); 904 rollbackLength = 1; 905 break; 906 case EXT16: 907 canRead(2); 908 length = load16To!ushort(read(2)); 909 rollbackLength = 2; 910 break; 911 case EXT32: 912 canRead(4); 913 length = load32To!uint(read(4)); 914 rollbackLength = 4; 915 break; 916 default: 917 rollback(0, "ext", cast(Format)header); 918 } 919 920 } 921 922 canRead(1 + length); 923 924 // Read and check the type 925 byte type_ = read(); 926 rollbackLength += 1; 927 if (type_ != type) 928 rollback(rollbackLength, text("Cannot unpack EXT of type ", type_, " into type ", type)); 929 930 // Read and check data 931 if (data is null) 932 data = new ubyte[](length); 933 else if (data.length != length) { 934 rollback(rollbackLength, text("Length mismatch while unpacking EXT: ", data.length, " was given, actual length is ", length)); 935 } 936 data[] = read(length); 937 return this; 938 } 939 940 /** 941 * Scans an entire buffer and converts each objects. 942 * 943 * This method is used for unpacking record-like objects. 944 * 945 * Example: 946 * ----- 947 * // serialized data is "[1, 2][3, 4][5, 6][...". 948 * auto unpacker = Unpacker(serializedData); 949 * foreach (n, d; &unpacker.scan!(int, int)) // == "foreach (int n, int d; unpacker)" 950 * writeln(n, d); // 1st loop "1, 2", 2nd loop "3, 4"... 951 * ----- 952 */ 953 int scan(Types...)(scope int delegate(ref Types) dg) 954 { 955 return opApply!(Types)(delegate int(ref Types objects) { return dg(objects); }); 956 } 957 958 959 /// ditto 960 int opApply(Types...)(scope int delegate(ref Types) dg) 961 { 962 int result; 963 964 while (used_ - offset_) { 965 auto length = beginArray(); 966 if (length != Types.length) 967 rollback(calculateSize(length), "the number of deserialized objects is mismatched"); 968 969 Types objects; 970 foreach (i, T; Types) 971 unpack(objects[i]); 972 973 result = dg(objects); 974 if (result) 975 return result; 976 } 977 978 return result; 979 } 980 981 982 private: 983 /* 984 * Deserializes nil object and assigns to $(D_PARAM value). 985 * 986 * Params: 987 * value = the reference of value to assign. 988 * 989 * Returns: 990 * self, i.e. for method chaining. 991 * 992 * Throws: 993 * UnpackException when doesn't read from buffer or precision loss occurs and 994 * MessagePackException when $(D_PARAM T) type doesn't match serialized type. 995 */ 996 @safe 997 ref Unpacker unpackNil(T)(ref T value) 998 { 999 canRead(Offset, 0); 1000 const header = read(); 1001 1002 if (header == Format.NIL) 1003 value = null; 1004 else 1005 rollback(0, "nil", cast(Format)header); 1006 1007 return this; 1008 } 1009 1010 1011 /* 1012 * Next object is nil? 1013 * 1014 * Returns: 1015 * true if next object is nil. 1016 */ 1017 @safe 1018 bool checkNil() 1019 { 1020 canRead(Offset, 0); 1021 1022 return buffer_[offset_] == Format.NIL; 1023 } 1024 1025 1026 /* 1027 * Calculates the format size of container length. 1028 */ 1029 size_t calculateSize(bool rawType = false)(in size_t length) 1030 { 1031 static if (rawType) 1032 return length < 32 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof; 1033 else 1034 return length < 16 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof; 1035 } 1036 1037 1038 /* 1039 * Reading test. 1040 * 1041 * Params: 1042 * size = the size to read. 1043 * offset = the offset to subtract when doesn't read from buffer. 1044 * 1045 * Throws: 1046 * UnpackException when doesn't read from buffer. 1047 */ 1048 @safe 1049 void canRead(in size_t size, in size_t offset = Offset) 1050 { 1051 if (used_ - offset_ < size) { 1052 if (offset) 1053 offset_ -= offset; 1054 1055 throw new UnpackException("Insufficient buffer"); 1056 } 1057 } 1058 1059 1060 /* 1061 * Reads value from buffer and advances offset. 1062 */ 1063 @safe 1064 nothrow ubyte read() 1065 { 1066 return buffer_[offset_++]; 1067 } 1068 1069 1070 /* 1071 * Reads value from buffer and advances offset. 1072 */ 1073 @safe 1074 nothrow ubyte[] read(in size_t size) 1075 { 1076 auto result = buffer_[offset_..offset_ + size]; 1077 1078 offset_ += size; 1079 1080 return result; 1081 } 1082 1083 1084 /* 1085 * Do rollback and throws exception. 1086 */ 1087 @safe 1088 void rollback(in size_t size, in string reason) 1089 { 1090 offset_ -= size + Offset; 1091 onInvalidType(reason); 1092 } 1093 1094 @safe 1095 void rollback(in size_t size, in string expected, in Format actual) 1096 { 1097 offset_ -= size + Offset; 1098 onInvalidType(expected, actual); 1099 } 1100 } 1101 1102 1103 private: 1104 1105 1106 /* 1107 * A callback for type-mismatched error in deserialization process. 1108 */ 1109 @safe 1110 pure void onInvalidType(in string reason) 1111 { 1112 throw new MessagePackException("Attempt to unpack with non-compatible type: reason = " ~ reason); 1113 } 1114 1115 @safe 1116 pure void onInvalidType(in string expected, in Format actual) 1117 { 1118 import std.conv: text; 1119 throw new MessagePackException(text("Attempt to unpack with non-compatible type: expected = ", expected, ", actual = ", actual)); 1120 } 1121 1122 1123 unittest 1124 { 1125 import msgpack.packer; 1126 1127 { // unique 1128 mixin DefinePacker; 1129 1130 Tuple!(bool, bool) result; 1131 Tuple!(bool, bool) test = tuple(true, false); 1132 1133 packer.pack(test); 1134 1135 auto unpacker = Unpacker(packer.stream.data); 1136 1137 unpacker.unpack(result); 1138 assert(test == result); 1139 } 1140 { // uint * 1141 mixin DefinePacker; 1142 1143 Tuple!(ubyte, ushort, uint, ulong) result; 1144 Tuple!(ubyte, ushort, uint, ulong) test = tuple(cast(ubyte)ubyte.max, cast(ushort)ushort.max, 1145 cast(uint)uint.max, cast(ulong)ulong.max); 1146 1147 packer.pack(test); 1148 1149 auto unpacker = Unpacker(packer.stream.data); 1150 1151 unpacker.unpack(result); 1152 assert(test == result); 1153 } 1154 { // int * 1155 mixin DefinePacker; 1156 1157 Tuple!(byte, short, int, long) result; 1158 Tuple!(byte, short, int, long) test = tuple(cast(byte)byte.min, cast(short)short.min, 1159 cast(int)int.min, cast(long)long.min); 1160 1161 packer.pack(test); 1162 1163 auto unpacker = Unpacker(packer.stream.data); 1164 1165 unpacker.unpack(result); 1166 assert(test == result); 1167 } 1168 { // floating point 1169 mixin DefinePacker; 1170 1171 static if (real.sizeof == double.sizeof || !EnableReal) 1172 { 1173 Tuple!(float, double, double) result; 1174 Tuple!(float, double, double) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)double.min_normal); 1175 } 1176 else 1177 { 1178 Tuple!(float, double, real) result; 1179 Tuple!(float, double, real) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)real.min_normal); 1180 } 1181 1182 packer.pack(test); 1183 1184 auto unpacker = Unpacker(packer.stream.data); 1185 1186 unpacker.unpack(result); 1187 assert(test == result); 1188 } 1189 { // pointer 1190 mixin DefinePacker; 1191 1192 Tuple!(ulong, long, double) origin; 1193 Tuple!(ulong, long, double) values = tuple(ulong.max, long.min, double.min_normal); 1194 Tuple!(ulong*, long*, double*) result = tuple(&origin.field[0], &origin.field[1], &origin.field[2]); 1195 Tuple!(ulong*, long*, double*) test = tuple(&values.field[0], &values.field[1], &values.field[2]); 1196 1197 packer.pack(test); 1198 1199 auto unpacker = Unpacker(packer.stream.data); 1200 1201 unpacker.unpack(result); 1202 foreach (i, v; test.field) 1203 assert(*v == *result.field[i]); 1204 assert(origin == values); 1205 } 1206 { // enum 1207 enum : float { D = 0.5 } 1208 enum E : ulong { U = 100 } 1209 1210 mixin DefinePacker; 1211 1212 float f = D, resultF; 1213 E e = E.U, resultE; 1214 1215 packer.pack(D, e); 1216 1217 auto unpacker = Unpacker(packer.stream.data); 1218 1219 unpacker.unpack(resultF, resultE); 1220 assert(f == resultF); 1221 assert(e == resultE); 1222 } 1223 { // container 1224 mixin DefinePacker; 1225 1226 Tuple!(ulong[], double[uint], string, bool[2], char[2]) test 1227 = tuple([1UL, 2], [3U:4.0, 5:6.0, 7:8.0], "MessagePack is nice!", [true, false], "D!"); 1228 1229 packer.pack(test); 1230 1231 auto unpacker = Unpacker(packer.stream.data); 1232 Tuple!(ulong[], double[uint], string, bool[2], char[2]) result; 1233 1234 unpacker.unpack(result); 1235 assert(test == result); 1236 } 1237 { // ext 1238 1239 // Try a variety of lengths, making sure to hit all the fixexts 1240 foreach (L; TypeTuple!(1, 2, 3, 4, 5, 8, 9, 16, 32, 512, 2^^16)) 1241 { 1242 mixin DefinePacker; 1243 1244 ubyte[] data = new ubyte[](L); 1245 ExtValue ext = ExtValue(7, data); 1246 packer.pack(ext); 1247 1248 auto unpacker1 = Unpacker(packer.stream.data); 1249 ExtValue witness; 1250 1251 unpacker1.unpack(witness); 1252 assert(ext == witness); 1253 1254 // And try unpackExt 1255 auto unpacker2 = Unpacker(packer.stream.data); 1256 byte type = 1; 1257 ubyte[] deserializedData = new ubyte[](7); 1258 1259 // This should be a type mismatch (1 != 7) 1260 assertThrown!MessagePackException( 1261 unpacker2.unpackExt(type, deserializedData)); 1262 type = 7; 1263 1264 // A data size mismatch 1265 assertThrown!MessagePackException( 1266 unpacker2.unpackExt(type, deserializedData)); 1267 deserializedData = new ubyte[](L); 1268 1269 // And this should succeed 1270 unpacker2.unpackExt(type, deserializedData); 1271 assert(deserializedData == data); 1272 } 1273 } 1274 { // user defined 1275 { 1276 static struct S 1277 { 1278 uint num; 1279 1280 void toMsgpack(P)(ref P p) const { p.packArray(num); } 1281 void fromMsgpack(ref Unpacker u) 1282 { 1283 assert(u.beginArray() == 1); 1284 u.unpack(num); 1285 } 1286 } 1287 1288 mixin DefinePacker; S result, test = S(uint.max); 1289 1290 packer.pack(test); 1291 1292 auto unpacker = Unpacker(packer.stream.data); 1293 unpacker.unpack(result); 1294 1295 assert(test.num == result.num); 1296 } 1297 { 1298 static class C 1299 { 1300 uint num; 1301 1302 this(uint n) { num = n; } 1303 1304 void toMsgpack(P)(ref P p) const { p.packArray(num - 1); } 1305 void fromMsgpack(ref Unpacker u) 1306 { 1307 assert(u.beginArray() == 1); 1308 u.unpack(num); 1309 } 1310 } 1311 1312 mixin DefinePacker; C result, test = new C(ushort.max); 1313 1314 packer.pack(test); 1315 1316 auto unpacker = Unpacker(packer.stream.data); 1317 unpacker.unpack(result, ushort.max); 1318 1319 assert(test.num == result.num + 1); 1320 } 1321 } 1322 { // simple struct and class 1323 { 1324 static struct Simple 1325 { 1326 uint num; 1327 @nonPacked string str; 1328 } 1329 1330 static struct Simple2 1331 { 1332 @nonPacked string str; 1333 uint num; 1334 } 1335 1336 foreach (Type; TypeTuple!(Simple, Simple2)) { 1337 mixin DefinePacker; 1338 Type result, test; 1339 test.num = uint.max; 1340 test.str = "ignored"; 1341 1342 packer.pack(test); 1343 auto unpacker = Unpacker(packer.stream.data); 1344 unpacker.unpack(result); 1345 1346 assert(test.num == result.num); 1347 assert(test.str != result.str); 1348 } 1349 } 1350 1351 static class SimpleA 1352 { 1353 bool flag = true; 1354 } 1355 1356 static class SimpleB : SimpleA 1357 { 1358 ubyte type = 100; 1359 } 1360 1361 static class SimpleC : SimpleB 1362 { 1363 uint num = uint.max; 1364 @nonPacked string str; 1365 } 1366 1367 static class SimpleC2 : SimpleB 1368 { 1369 @nonPacked string str; 1370 uint num = uint.max; 1371 } 1372 1373 { // from derived class 1374 foreach (Type; TypeTuple!(SimpleC, SimpleC2)) { 1375 mixin DefinePacker; 1376 Type result, test = new Type(); 1377 test.flag = false; 1378 test.type = 99; 1379 test.num = uint.max / 2; 1380 test.str = "ignored"; 1381 1382 packer.pack(test); 1383 auto unpacker = Unpacker(packer.stream.data); 1384 unpacker.unpack(result); 1385 1386 assert(test.flag == result.flag); 1387 assert(test.type == result.type); 1388 assert(test.num == result.num); 1389 assert(test.str != result.str); 1390 } 1391 } 1392 { // from base class 1393 mixin DefinePacker; SimpleC test = new SimpleC(); 1394 1395 packer.pack(test); 1396 1397 SimpleB result = new SimpleC(); 1398 auto unpacker = Unpacker(packer.stream.data); 1399 1400 try { 1401 unpacker.unpack(result); 1402 assert(false); 1403 } catch (Exception e) { } 1404 } 1405 { // https://github.com/msgpack/msgpack-d/issues/16 1406 mixin DefinePacker; 1407 1408 static class Issue16 1409 { 1410 int i; 1411 this(int i) { this.i = i; } 1412 } 1413 1414 Issue16 c1 = new Issue16(10); 1415 1416 // change behaviour to accept null with new object without constructor 1417 Issue16 c2 = null; 1418 packer.pack(c1); 1419 auto unpacker1 = Unpacker(packer.stream.data); 1420 unpacker1.unpack(c2); 1421 //unpack(pack(c1), c2); 1422 assert(c2.i == c1.i); 1423 1424 Issue16 c3 = new Issue16(20); 1425 packer.stream.clear(); 1426 packer.pack(c1); 1427 auto unpacker2 = Unpacker(packer.stream.data); 1428 unpacker2.unpack(c3); 1429 //unpack(pack(c1), c3); 1430 assert(c3.i == c1.i); 1431 } 1432 } 1433 { // variadic 1434 mixin DefinePacker; 1435 1436 Tuple!(uint, long, double) test = tuple(uint.max, long.min, double.max); 1437 1438 packer.pack(test); 1439 1440 auto unpacker = Unpacker(packer.stream.data); 1441 1442 uint u; long l; double d; 1443 1444 unpacker.unpackArray(u, l, d); 1445 assert(test == tuple(u, l, d)); 1446 } 1447 { // scan / opApply 1448 ubyte[] data; 1449 mixin DefinePacker; 1450 1451 foreach (i; 0..2) 1452 packer.pack(tuple(1, 0.5, "Hi!")); 1453 1454 foreach (n, d, s; &Unpacker(packer.stream.data).scan!(int, double, string)) { 1455 assert(n == 1); 1456 assert(d == 0.5); 1457 assert(s == "Hi!"); 1458 } 1459 } 1460 }