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 static if (hasSerializedAs!(T.tupleof[i])) { 691 alias Proxy = getSerializedAs!(T.tupleof[i]); 692 Proxy.deserialize(this, object.tupleof[i]); 693 } else { 694 unpack(object.tupleof[i]); 695 } 696 } else { 697 assert(false, "Invalid field name: '" ~ fieldName ~ "', expect '" ~ getFieldName!(T, i) ~ "'"); 698 } 699 } 700 } 701 } else { 702 foreach (i, member; object.tupleof) { 703 static if (isPackedField!(T.tupleof[i])) { 704 static if (hasSerializedAs!(T.tupleof[i])) { 705 alias Proxy = getSerializedAs!(T.tupleof[i]); 706 Proxy.deserialize(this, object.tupleof[i]); 707 } else { 708 unpack(object.tupleof[i]); 709 } 710 } 711 } 712 } 713 } 714 } 715 716 return this; 717 } 718 719 720 void unpackObject(T)(ref T object) if (is(Unqual!T == class)) 721 { 722 alias SerializingClasses!(T) Classes; 723 724 size_t length = withFieldName_ ? beginMap() : beginArray(); 725 if (length == 0) 726 return; 727 728 if (length != SerializingMemberNumbers!(Classes)) 729 rollback(calculateSize(length), "the number of class fields is mismatched"); 730 731 if (withFieldName_) { 732 foreach (_; 0..length) { 733 string fieldName; 734 unpack(fieldName); 735 736 foreach (Class; Classes) { 737 Class obj = cast(Class)object; 738 739 foreach (i, member; obj.tupleof) { 740 static if (isPackedField!(Class.tupleof[i])) 741 { 742 if (fieldName == getFieldName!(Class, i)) { 743 static if (hasSerializedAs!(Class.tupleof[i])) { 744 alias Proxy = getSerializedAs!(Class.tupleof[i]); 745 Proxy.deserialize(this, obj.tupleof[i]); 746 } else { 747 unpack(obj.tupleof[i]); 748 } 749 goto endLoop; 750 } 751 } 752 } 753 } 754 assert(false, "Invalid field name: '" ~ fieldName~"' "); 755 756 endLoop: 757 continue; 758 } 759 } else { 760 foreach (Class; Classes) { 761 Class obj = cast(Class)object; 762 763 foreach (i, member; obj.tupleof) { 764 static if (isPackedField!(Class.tupleof[i])) { 765 static if (hasSerializedAs!(Class.tupleof[i])) { 766 alias Proxy = getSerializedAs!(Class.tupleof[i]); 767 Proxy.deserialize(this, obj.tupleof[i]); 768 } else { 769 unpack(obj.tupleof[i]); 770 } 771 } 772 } 773 } 774 } 775 } 776 777 778 /** 779 * Deserializes the container object and assigns to each argument. 780 * 781 * These methods check the length. Do rollback if 782 * the length of arguments is different from length of deserialized object. 783 * 784 * In unpackMap, the number of arguments must be even. 785 * 786 * Params: 787 * objects = the references of object to assign. 788 * 789 * Returns: 790 * self, i.e. for method chaining. 791 */ 792 ref Unpacker unpackArray(Types...)(ref Types objects) 793 { 794 auto length = beginArray(); 795 if (length != Types.length) 796 rollback(calculateSize(length), "the number of deserialized objects is mismatched"); 797 798 foreach (i, T; Types) 799 unpack(objects[i]); 800 // unpack(objects); // slow :( 801 802 return this; 803 } 804 805 806 /// ditto 807 ref Unpacker unpackMap(Types...)(ref Types objects) 808 { 809 static assert(Types.length % 2 == 0, "The number of arguments must be even"); 810 811 auto length = beginMap(); 812 if (length != Types.length / 2) 813 rollback(calculateSize(length), "the number of deserialized objects is mismatched"); 814 815 foreach (i, T; Types) 816 unpack(objects[i]); 817 818 return this; 819 } 820 821 822 /** 823 * Deserializes the type-information of container. 824 * 825 * These methods don't deserialize contents. 826 * You need to call unpack method to deserialize contents at your own risk. 827 * ----- 828 * // serialized data is [1, "Hi!"]; 829 * int num; 830 * unpacker.beginArray(2).unpack(num); // num is 1 831 * 832 * // other operation 833 * 834 * string str; 835 * unpacker.unpack(str); // str is "Hi!" 836 * ----- 837 * 838 * Returns: 839 * the container size. 840 */ 841 @safe 842 size_t beginArray() 843 { 844 canRead(Offset, 0); 845 const header = read(); 846 size_t length; 847 848 if (0x90 <= header && header <= 0x9f) { 849 length = header & 0x0f; 850 } else { 851 switch (header) { 852 case Format.ARRAY16: 853 canRead(ushort.sizeof); 854 length = load16To!size_t(read(ushort.sizeof)); 855 break; 856 case Format.ARRAY32: 857 canRead(uint.sizeof); 858 length = load32To!size_t(read(uint.sizeof)); 859 break; 860 case Format.NIL: 861 break; 862 default: 863 rollback(0, "array", cast(Format)header); 864 } 865 } 866 867 return length; 868 } 869 870 871 /// ditto 872 @safe 873 size_t beginMap() 874 { 875 canRead(Offset, 0); 876 const header = read(); 877 size_t length; 878 879 if (0x80 <= header && header <= 0x8f) { 880 length = header & 0x0f; 881 } else { 882 switch (header) { 883 case Format.MAP16: 884 canRead(ushort.sizeof); 885 length = load16To!size_t(read(ushort.sizeof)); 886 break; 887 case Format.MAP32: 888 canRead(uint.sizeof); 889 length = load32To!size_t(read(uint.sizeof)); 890 break; 891 case Format.NIL: 892 break; 893 default: 894 rollback(0, "map", cast(Format)header); 895 } 896 } 897 898 return length; 899 } 900 901 902 /** 903 * Unpacks an EXT value into $(D type) and $(D data). 904 * $(D type) is checked and a $(D MessagePackException) is thrown if it does 905 * not match. The length of $(D data) is checked and a $(D MessagePackException) 906 * is thrown if the lengths do not match. If $(D data) is null, a new slice 907 * is returned. 908 */ 909 ref Unpacker unpackExt(ref byte type, ref ubyte[] data) return 910 { 911 import std.conv : text; 912 913 canRead(Offset, 0); 914 const header = read(); 915 916 uint length; 917 uint rollbackLength = 0; 918 if (header >= Format.EXT && header <= Format.EXT + 4) 919 { 920 // Fixed 921 length = 2^^(header - Format.EXT); 922 923 } else { 924 // Dynamic length 925 switch (header) with (Format) 926 { 927 case EXT8: 928 canRead(1); 929 length = read(); 930 rollbackLength = 1; 931 break; 932 case EXT16: 933 canRead(2); 934 length = load16To!ushort(read(2)); 935 rollbackLength = 2; 936 break; 937 case EXT32: 938 canRead(4); 939 length = load32To!uint(read(4)); 940 rollbackLength = 4; 941 break; 942 default: 943 rollback(0, "ext", cast(Format)header); 944 } 945 946 } 947 948 canRead(1 + length); 949 950 // Read and check the type 951 byte type_ = read(); 952 rollbackLength += 1; 953 if (type_ != type) 954 rollback(rollbackLength, text("Cannot unpack EXT of type ", type_, " into type ", type)); 955 956 // Read and check data 957 if (data is null) 958 data = new ubyte[](length); 959 else if (data.length != length) { 960 rollback(rollbackLength, text("Length mismatch while unpacking EXT: ", data.length, " was given, actual length is ", length)); 961 } 962 data[] = read(length); 963 return this; 964 } 965 966 /** 967 * Scans an entire buffer and converts each objects. 968 * 969 * This method is used for unpacking record-like objects. 970 * 971 * Example: 972 * ----- 973 * // serialized data is "[1, 2][3, 4][5, 6][...". 974 * auto unpacker = Unpacker(serializedData); 975 * foreach (n, d; &unpacker.scan!(int, int)) // == "foreach (int n, int d; unpacker)" 976 * writeln(n, d); // 1st loop "1, 2", 2nd loop "3, 4"... 977 * ----- 978 */ 979 int scan(Types...)(scope int delegate(ref Types) dg) 980 { 981 return opApply!(Types)(delegate int(ref Types objects) { return dg(objects); }); 982 } 983 984 985 /// ditto 986 int opApply(Types...)(scope int delegate(ref Types) dg) 987 { 988 int result; 989 990 while (used_ - offset_) { 991 auto length = beginArray(); 992 if (length != Types.length) 993 rollback(calculateSize(length), "the number of deserialized objects is mismatched"); 994 995 Types objects; 996 foreach (i, T; Types) 997 unpack(objects[i]); 998 999 result = dg(objects); 1000 if (result) 1001 return result; 1002 } 1003 1004 return result; 1005 } 1006 1007 1008 private: 1009 /* 1010 * Deserializes nil object and assigns to $(D_PARAM value). 1011 * 1012 * Params: 1013 * value = the reference of value to assign. 1014 * 1015 * Returns: 1016 * self, i.e. for method chaining. 1017 * 1018 * Throws: 1019 * UnpackException when doesn't read from buffer or precision loss occurs and 1020 * MessagePackException when $(D_PARAM T) type doesn't match serialized type. 1021 */ 1022 @safe 1023 ref Unpacker unpackNil(T)(ref T value) 1024 { 1025 canRead(Offset, 0); 1026 const header = read(); 1027 1028 if (header == Format.NIL) 1029 value = null; 1030 else 1031 rollback(0, "nil", cast(Format)header); 1032 1033 return this; 1034 } 1035 1036 1037 /* 1038 * Next object is nil? 1039 * 1040 * Returns: 1041 * true if next object is nil. 1042 */ 1043 @safe 1044 bool checkNil() 1045 { 1046 canRead(Offset, 0); 1047 1048 return buffer_[offset_] == Format.NIL; 1049 } 1050 1051 1052 /* 1053 * Calculates the format size of container length. 1054 */ 1055 size_t calculateSize(bool rawType = false)(in size_t length) 1056 { 1057 static if (rawType) 1058 return length < 32 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof; 1059 else 1060 return length < 16 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof; 1061 } 1062 1063 1064 /* 1065 * Reading test. 1066 * 1067 * Params: 1068 * size = the size to read. 1069 * offset = the offset to subtract when doesn't read from buffer. 1070 * 1071 * Throws: 1072 * UnpackException when doesn't read from buffer. 1073 */ 1074 @safe 1075 void canRead(in size_t size, in size_t offset = Offset) 1076 { 1077 if (used_ - offset_ < size) { 1078 if (offset) 1079 offset_ -= offset; 1080 1081 throw new UnpackException("Insufficient buffer"); 1082 } 1083 } 1084 1085 1086 /* 1087 * Reads value from buffer and advances offset. 1088 */ 1089 @safe 1090 nothrow ubyte read() 1091 { 1092 return buffer_[offset_++]; 1093 } 1094 1095 1096 /* 1097 * Reads value from buffer and advances offset. 1098 */ 1099 @safe 1100 nothrow ubyte[] read(in size_t size) 1101 { 1102 auto result = buffer_[offset_..offset_ + size]; 1103 1104 offset_ += size; 1105 1106 return result; 1107 } 1108 1109 1110 /* 1111 * Do rollback and throws exception. 1112 */ 1113 @safe 1114 void rollback(in size_t size, in string reason) 1115 { 1116 offset_ -= size + Offset; 1117 onInvalidType(reason); 1118 } 1119 1120 @safe 1121 void rollback(in size_t size, in string expected, in Format actual) 1122 { 1123 offset_ -= size + Offset; 1124 onInvalidType(expected, actual); 1125 } 1126 } 1127 1128 1129 private: 1130 1131 1132 /* 1133 * A callback for type-mismatched error in deserialization process. 1134 */ 1135 @safe 1136 pure void onInvalidType(in string reason) 1137 { 1138 throw new MessagePackException("Attempt to unpack with non-compatible type: reason = " ~ reason); 1139 } 1140 1141 @safe 1142 pure void onInvalidType(in string expected, in Format actual) 1143 { 1144 import std.conv: text; 1145 throw new MessagePackException(text("Attempt to unpack with non-compatible type: expected = ", expected, ", actual = ", actual)); 1146 } 1147 1148 1149 unittest 1150 { 1151 import msgpack.packer; 1152 1153 { // unique 1154 mixin DefinePacker; 1155 1156 Tuple!(bool, bool) result; 1157 Tuple!(bool, bool) test = tuple(true, false); 1158 1159 packer.pack(test); 1160 1161 auto unpacker = Unpacker(packer.stream.data); 1162 1163 unpacker.unpack(result); 1164 assert(test == result); 1165 } 1166 { // uint * 1167 mixin DefinePacker; 1168 1169 Tuple!(ubyte, ushort, uint, ulong) result; 1170 Tuple!(ubyte, ushort, uint, ulong) test = tuple(cast(ubyte)ubyte.max, cast(ushort)ushort.max, 1171 cast(uint)uint.max, cast(ulong)ulong.max); 1172 1173 packer.pack(test); 1174 1175 auto unpacker = Unpacker(packer.stream.data); 1176 1177 unpacker.unpack(result); 1178 assert(test == result); 1179 } 1180 { // int * 1181 mixin DefinePacker; 1182 1183 Tuple!(byte, short, int, long) result; 1184 Tuple!(byte, short, int, long) test = tuple(cast(byte)byte.min, cast(short)short.min, 1185 cast(int)int.min, cast(long)long.min); 1186 1187 packer.pack(test); 1188 1189 auto unpacker = Unpacker(packer.stream.data); 1190 1191 unpacker.unpack(result); 1192 assert(test == result); 1193 } 1194 { // floating point 1195 mixin DefinePacker; 1196 1197 static if (real.sizeof == double.sizeof || !EnableReal) 1198 { 1199 Tuple!(float, double, double) result; 1200 Tuple!(float, double, double) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)double.min_normal); 1201 } 1202 else 1203 { 1204 Tuple!(float, double, real) result; 1205 Tuple!(float, double, real) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)real.min_normal); 1206 } 1207 1208 packer.pack(test); 1209 1210 auto unpacker = Unpacker(packer.stream.data); 1211 1212 unpacker.unpack(result); 1213 assert(test == result); 1214 } 1215 { // pointer 1216 mixin DefinePacker; 1217 1218 Tuple!(ulong, long, double) origin; 1219 Tuple!(ulong, long, double) values = tuple(ulong.max, long.min, double.min_normal); 1220 Tuple!(ulong*, long*, double*) result = tuple(&origin.field[0], &origin.field[1], &origin.field[2]); 1221 Tuple!(ulong*, long*, double*) test = tuple(&values.field[0], &values.field[1], &values.field[2]); 1222 1223 packer.pack(test); 1224 1225 auto unpacker = Unpacker(packer.stream.data); 1226 1227 unpacker.unpack(result); 1228 foreach (i, v; test.field) 1229 assert(*v == *result.field[i]); 1230 assert(origin == values); 1231 } 1232 { // enum 1233 enum : float { D = 0.5 } 1234 enum E : ulong { U = 100 } 1235 1236 mixin DefinePacker; 1237 1238 float f = D, resultF; 1239 E e = E.U, resultE; 1240 1241 packer.pack(D, e); 1242 1243 auto unpacker = Unpacker(packer.stream.data); 1244 1245 unpacker.unpack(resultF, resultE); 1246 assert(f == resultF); 1247 assert(e == resultE); 1248 } 1249 { // container 1250 mixin DefinePacker; 1251 1252 Tuple!(ulong[], double[uint], string, bool[2], char[2]) test 1253 = tuple([1UL, 2], [3U:4.0, 5:6.0, 7:8.0], "MessagePack is nice!", [true, false], "D!"); 1254 1255 packer.pack(test); 1256 1257 auto unpacker = Unpacker(packer.stream.data); 1258 Tuple!(ulong[], double[uint], string, bool[2], char[2]) result; 1259 1260 unpacker.unpack(result); 1261 assert(test == result); 1262 } 1263 { // ext 1264 1265 // Try a variety of lengths, making sure to hit all the fixexts 1266 foreach (L; TypeTuple!(1, 2, 3, 4, 5, 8, 9, 16, 32, 512, 2^^16)) 1267 { 1268 mixin DefinePacker; 1269 1270 ubyte[] data = new ubyte[](L); 1271 ExtValue ext = ExtValue(7, data); 1272 packer.pack(ext); 1273 1274 auto unpacker1 = Unpacker(packer.stream.data); 1275 ExtValue witness; 1276 1277 unpacker1.unpack(witness); 1278 assert(ext == witness); 1279 1280 // And try unpackExt 1281 auto unpacker2 = Unpacker(packer.stream.data); 1282 byte type = 1; 1283 ubyte[] deserializedData = new ubyte[](7); 1284 1285 // This should be a type mismatch (1 != 7) 1286 assertThrown!MessagePackException( 1287 unpacker2.unpackExt(type, deserializedData)); 1288 type = 7; 1289 1290 // A data size mismatch 1291 assertThrown!MessagePackException( 1292 unpacker2.unpackExt(type, deserializedData)); 1293 deserializedData = new ubyte[](L); 1294 1295 // And this should succeed 1296 unpacker2.unpackExt(type, deserializedData); 1297 assert(deserializedData == data); 1298 } 1299 } 1300 { // user defined 1301 { 1302 static struct S 1303 { 1304 uint num; 1305 1306 void toMsgpack(P)(ref P p) const { p.packArray(num); } 1307 void fromMsgpack(ref Unpacker u) 1308 { 1309 assert(u.beginArray() == 1); 1310 u.unpack(num); 1311 } 1312 } 1313 1314 mixin DefinePacker; S result, test = S(uint.max); 1315 1316 packer.pack(test); 1317 1318 auto unpacker = Unpacker(packer.stream.data); 1319 unpacker.unpack(result); 1320 1321 assert(test.num == result.num); 1322 } 1323 { 1324 static class C 1325 { 1326 uint num; 1327 1328 this(uint n) { num = n; } 1329 1330 void toMsgpack(P)(ref P p) const { p.packArray(num - 1); } 1331 void fromMsgpack(ref Unpacker u) 1332 { 1333 assert(u.beginArray() == 1); 1334 u.unpack(num); 1335 } 1336 } 1337 1338 mixin DefinePacker; C result, test = new C(ushort.max); 1339 1340 packer.pack(test); 1341 1342 auto unpacker = Unpacker(packer.stream.data); 1343 unpacker.unpack(result, ushort.max); 1344 1345 assert(test.num == result.num + 1); 1346 } 1347 } 1348 { // simple struct and class 1349 { 1350 static struct Simple 1351 { 1352 uint num; 1353 @nonPacked string str; 1354 } 1355 1356 static struct Simple2 1357 { 1358 @nonPacked string str; 1359 uint num; 1360 } 1361 1362 foreach (Type; TypeTuple!(Simple, Simple2)) { 1363 mixin DefinePacker; 1364 Type result, test; 1365 test.num = uint.max; 1366 test.str = "ignored"; 1367 1368 packer.pack(test); 1369 auto unpacker = Unpacker(packer.stream.data); 1370 unpacker.unpack(result); 1371 1372 assert(test.num == result.num); 1373 assert(test.str != result.str); 1374 } 1375 } 1376 1377 { 1378 static struct SimpleProxy1 1379 { 1380 import std.conv; 1381 static void serialize(ref Packer p, ref string val) { p.pack(to!uint(val)); } 1382 static void deserialize(ref Unpacker u, ref string val) { uint tmp; u.unpack(tmp); val = to!string(tmp); } 1383 } 1384 static struct SimpleWithProxied1 1385 { 1386 @serializedAs!SimpleProxy1 string data; 1387 enum string defaultValue = "10"; 1388 } 1389 1390 // https://github.com/msgpack/msgpack-d/issues/83 1391 static struct SimpleProxy2 1392 { 1393 import std.datetime; 1394 static void serialize(ref Packer p, ref SysTime val) { p.pack(val.toISOExtString()); } 1395 static void deserialize(ref Unpacker u, ref SysTime val) { string tmp; u.unpack(tmp); val = SysTime.fromISOExtString(tmp); } 1396 } 1397 static struct SimpleWithProxied2 1398 { 1399 import std.datetime; 1400 @serializedAs!SimpleProxy2 SysTime data; 1401 static SysTime defaultValue() @property { return SysTime(DateTime(2019,1,1,0,0,0)); } 1402 } 1403 1404 foreach (Type; TypeTuple!(SimpleWithProxied1, SimpleWithProxied2)) { 1405 mixin DefinePacker; 1406 Type result, test; 1407 test.data = Type.defaultValue; 1408 1409 packer.pack(test); 1410 auto unpacker = Unpacker(packer.stream.data); 1411 unpacker.unpack(result); 1412 assert(test.data == result.data); 1413 } 1414 } 1415 1416 static class SimpleA 1417 { 1418 bool flag = true; 1419 } 1420 1421 static class SimpleB : SimpleA 1422 { 1423 ubyte type = 100; 1424 } 1425 1426 static class SimpleC : SimpleB 1427 { 1428 uint num = uint.max; 1429 @nonPacked string str; 1430 } 1431 1432 static class SimpleC2 : SimpleB 1433 { 1434 @nonPacked string str; 1435 uint num = uint.max; 1436 } 1437 1438 static class SimpleD 1439 { 1440 static struct Proxy 1441 { 1442 import std.conv; 1443 static void serialize(ref Packer p, ref bool val) { p.pack(to!string(val)); } 1444 static void serialize(ref Packer p, ref uint val) { p.pack(to!string(val)); } 1445 static void serialize(ref Packer p, ref ubyte val) { p.pack(to!string(val)); } 1446 static void deserialize(ref Unpacker u, ref bool val) { string tmp; u.unpack(tmp); val = to!bool(tmp); } 1447 static void deserialize(ref Unpacker u, ref uint val) { string tmp; u.unpack(tmp); val = to!uint(tmp); } 1448 static void deserialize(ref Unpacker u, ref ubyte val) { string tmp; u.unpack(tmp); val = to!ubyte(tmp); } 1449 } 1450 @serializedAs!Proxy bool flag = true; 1451 @serializedAs!Proxy ubyte type = 100; 1452 @serializedAs!Proxy uint num = uint.max; 1453 @nonPacked string str; 1454 } 1455 1456 { // from derived class 1457 foreach (Type; TypeTuple!(SimpleC, SimpleC2, SimpleD)) { 1458 mixin DefinePacker; 1459 Type result, test = new Type(); 1460 test.flag = false; 1461 test.type = 99; 1462 test.num = uint.max / 2; 1463 test.str = "ignored"; 1464 1465 packer.pack(test); 1466 auto unpacker = Unpacker(packer.stream.data); 1467 unpacker.unpack(result); 1468 1469 assert(test.flag == result.flag); 1470 assert(test.type == result.type); 1471 assert(test.num == result.num); 1472 assert(test.str != result.str); 1473 } 1474 } 1475 { // from base class 1476 mixin DefinePacker; SimpleC test = new SimpleC(); 1477 1478 packer.pack(test); 1479 1480 SimpleB result = new SimpleC(); 1481 auto unpacker = Unpacker(packer.stream.data); 1482 1483 try { 1484 unpacker.unpack(result); 1485 assert(false); 1486 } catch (Exception e) { } 1487 } 1488 { // https://github.com/msgpack/msgpack-d/issues/16 1489 mixin DefinePacker; 1490 1491 static class Issue16 1492 { 1493 int i; 1494 this(int i) { this.i = i; } 1495 } 1496 1497 Issue16 c1 = new Issue16(10); 1498 1499 // change behaviour to accept null with new object without constructor 1500 Issue16 c2 = null; 1501 packer.pack(c1); 1502 auto unpacker1 = Unpacker(packer.stream.data); 1503 unpacker1.unpack(c2); 1504 //unpack(pack(c1), c2); 1505 assert(c2.i == c1.i); 1506 1507 Issue16 c3 = new Issue16(20); 1508 packer.stream.clear(); 1509 packer.pack(c1); 1510 auto unpacker2 = Unpacker(packer.stream.data); 1511 unpacker2.unpack(c3); 1512 //unpack(pack(c1), c3); 1513 assert(c3.i == c1.i); 1514 } 1515 } 1516 { // variadic 1517 mixin DefinePacker; 1518 1519 Tuple!(uint, long, double) test = tuple(uint.max, long.min, double.max); 1520 1521 packer.pack(test); 1522 1523 auto unpacker = Unpacker(packer.stream.data); 1524 1525 uint u; long l; double d; 1526 1527 unpacker.unpackArray(u, l, d); 1528 assert(test == tuple(u, l, d)); 1529 } 1530 { // scan / opApply 1531 ubyte[] data; 1532 mixin DefinePacker; 1533 1534 foreach (i; 0..2) 1535 packer.pack(tuple(1, 0.5, "Hi!")); 1536 1537 foreach (n, d, s; &Unpacker(packer.stream.data).scan!(int, double, string)) { 1538 assert(n == 1); 1539 assert(d == 0.5); 1540 assert(s == "Hi!"); 1541 } 1542 } 1543 }