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