1 // Written in the D programming language. 2 3 /** 4 * MessagePack serializer and deserializer implementation. 5 * 6 * MessagePack is a binary-based serialization specification. 7 * 8 * Example: 9 * ----- 10 * auto data = tuple("MessagePack!", [1, 2], true); 11 * 12 * auto serialized = pack(data); 13 * 14 * // ... 15 * 16 * typeof(data) deserialized; 17 * 18 * unpack(serialized, deserialized); 19 * 20 * assert(data == deserialized); 21 * ----- 22 * 23 * See_Also: 24 * $(LINK2 http://msgpack.org/, The MessagePack Project)$(BR) 25 * $(LINK2 http://wiki.msgpack.org/display/MSGPACK/Design+of+Serialization, MessagePack Design concept)$(BR) 26 * $(LINK2 http://wiki.msgpack.org/display/MSGPACK/Format+specification, MessagePack data format) 27 * 28 * Copyright: Copyright Masahiro Nakagawa 2010-. 29 * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. 30 * Authors: Masahiro Nakagawa 31 */ 32 module bio.bam.thirdparty.msgpack; 33 34 import std.array; 35 import std.exception; 36 import std.range; 37 import std.stdio; 38 import std.traits; 39 import std.typecons; 40 import std.typetuple; 41 42 // for RefBuffer 43 version(Posix) 44 { 45 import core.sys.posix.sys.uio : iovec; 46 } 47 else 48 { 49 /** 50 * from core.sys.posix.sys.uio.iovec for compatibility with posix. 51 */ 52 struct iovec 53 { 54 void* iov_base; 55 size_t iov_len; 56 } 57 } 58 59 // for Converting Endian using ntohs and ntohl; 60 version(Windows) 61 { 62 import core.stdc.windows.winsock; 63 } 64 else 65 { 66 import core.sys.posix.arpa.inet; 67 } 68 69 version(EnableReal) 70 { 71 enum EnableReal = true; 72 } 73 else 74 { 75 enum EnableReal = false; 76 } 77 78 static if (real.sizeof == double.sizeof) { 79 // for 80bit real inter-operation on non-x86 CPU 80 version = NonX86; 81 82 import std.numeric; 83 } 84 85 version(unittest) import std.file, core.stdc.string; 86 87 88 @trusted: 89 90 91 public: 92 93 94 // Convenient functions 95 96 97 /** 98 * Serializes $(D_PARAM args). 99 * 100 * Assumes single object if the length of $(D_PARAM args) == 1, 101 * otherwise array object. 102 * 103 * Params: 104 * args = the contents to serialize. 105 * 106 * Returns: 107 * a serialized data. 108 */ 109 ubyte[] pack(bool withFieldName = false, Args...)(in Args args) 110 { 111 auto packer = Packer(withFieldName); 112 113 static if (Args.length == 1) 114 packer.pack(args[0]); 115 else 116 packer.packArray(args); 117 118 return packer.stream.data; 119 } 120 121 122 unittest 123 { 124 auto serialized = pack(false); 125 126 assert(serialized[0] == Format.FALSE); 127 128 auto deserialized = unpack(pack(1, true, "Foo")); 129 130 assert(deserialized.type == Value.Type.array); 131 assert(deserialized.via.array[0].type == Value.Type.unsigned); 132 assert(deserialized.via.array[1].type == Value.Type.boolean); 133 assert(deserialized.via.array[2].type == Value.Type.raw); 134 } 135 136 137 /** 138 * Deserializes $(D_PARAM buffer) using stream deserializer. 139 * 140 * Params: 141 * buffer = the buffer to deserialize. 142 * 143 * Returns: 144 * a $(D Unpacked) contains deserialized object. 145 * 146 * Throws: 147 * UnpackException if deserialization doesn't succeed. 148 */ 149 Unpacked unpack(in ubyte[] buffer) 150 { 151 auto unpacker = StreamingUnpacker(buffer); 152 153 if (!unpacker.execute()) 154 throw new UnpackException("Deserialization failure"); 155 156 return unpacker.unpacked; 157 } 158 159 160 /** 161 * Deserializes $(D_PARAM buffer) using direct-conversion deserializer. 162 * 163 * Assumes single object if the length of $(D_PARAM args) == 1, 164 * otherwise array object. 165 * 166 * Params: 167 * buffer = the buffer to deserialize. 168 * args = the references of values to assign. 169 */ 170 void unpack(bool withFieldName = false, Args...)(in ubyte[] buffer, ref Args args) 171 { 172 auto unpacker = Unpacker(buffer, buffer.length, withFieldName); 173 174 static if (Args.length == 1) 175 unpacker.unpack(args[0]); 176 else 177 unpacker.unpackArray(args); 178 } 179 180 181 /** 182 * Return value version 183 */ 184 Type unpack(Type, bool withFieldName = false)(in ubyte[] buffer) 185 { 186 auto unpacker = Unpacker(buffer, buffer.length, withFieldName); 187 188 Type result; 189 unpacker.unpack(result); 190 return result; 191 } 192 193 194 unittest 195 { 196 { // stream 197 auto result = unpack(pack(false)); 198 199 assert(result.via.boolean == false); 200 } 201 { // direct conversion 202 Tuple!(uint, string) result; 203 Tuple!(uint, string) test = tuple(1, "Hi!"); 204 205 unpack(pack(test), result); 206 assert(result == test); 207 208 test.field[0] = 2; 209 test.field[1] = "Hey!"; 210 unpack(pack(test.field[0], test.field[1]), result.field[0], result.field[1]); 211 assert(result == test); 212 } 213 { // return value direct conversion 214 Tuple!(uint, string) test = tuple(1, "Hi!"); 215 216 auto data = pack(test); 217 assert(data.unpack!(Tuple!(uint, string)) == test); 218 } 219 { // serialize object as a Map 220 static class C 221 { 222 int num; 223 224 this(int num) { this.num = num; } 225 } 226 227 auto test = new C(10); 228 auto result = new C(100); 229 230 unpack!(true)(pack!(true)(test), result); 231 assert(result.num == 10, "Unpacking with field names failed"); 232 } 233 } 234 235 236 unittest 237 { 238 // unittest for https://github.com/msgpack/msgpack-d/issues/8 239 foreach (Type; TypeTuple!(byte, short, int, long)) { 240 foreach (i; [-33, -20, -1, 0, 1, 20, 33]) { 241 Type a = cast(Type)i; 242 Type b; 243 unpack(pack(a), b); 244 assert(a == b); 245 } 246 } 247 } 248 249 250 /** 251 * $(D MessagePackException) is a root Exception for MessagePack related operation. 252 */ 253 class MessagePackException : Exception 254 { 255 pure this(string message) 256 { 257 super(message); 258 } 259 } 260 261 262 /** 263 * Attribute for specifying non pack/unpack field. 264 * This is an alternative approach of MessagePackable mixin. 265 * 266 * Example: 267 * ----- 268 * struct S 269 * { 270 * int num; 271 * // Packer/Unpacker ignores this field; 272 * @nonPacked string str; 273 * } 274 * ----- 275 */ 276 struct nonPacked {} 277 278 template isPackedField(alias field) 279 { 280 enum isPackedField = (staticIndexOf!(nonPacked, __traits(getAttributes, field)) == -1) && (!isSomeFunction!(typeof(field))); 281 } 282 283 284 // Serializing routines 285 286 287 /** 288 * $(D Packer) is a $(D MessagePack) serializer 289 * 290 * Example: 291 * ----- 292 * auto packer = packer(Appender!(ubyte[])()); 293 * 294 * packer.packArray(false, 100, 1e-10, null); 295 * 296 * stdout.rawWrite(packer.buffer.data); 297 * ----- 298 * 299 * NOTE: 300 * Current implementation can't deal with a circular reference. 301 * If you try to serialize a object that has circular reference, runtime raises 'Stack Overflow'. 302 */ 303 struct PackerImpl(Stream) if (isOutputRange!(Stream, ubyte) && isOutputRange!(Stream, ubyte[])) 304 { 305 private: 306 static @system 307 { 308 alias void delegate(ref PackerImpl, void*) PackHandler; 309 PackHandler[TypeInfo] packHandlers; 310 311 public void registerHandler(T, alias Handler)() 312 { 313 packHandlers[typeid(T)] = delegate(ref PackerImpl packer, void* obj) { 314 Handler(packer, *cast(T*)obj); 315 }; 316 } 317 } 318 319 enum size_t Offset = 1; // type-information offset 320 321 Stream stream_; // the stream to write 322 ubyte[Offset + RealSize] store_; // stores serialized value 323 bool withFieldName_; 324 325 326 public: 327 /** 328 * Constructs a packer with $(D_PARAM stream). 329 * 330 * Params: 331 * stream = the stream to write. 332 * withFieldName = serialize class / struct with field name 333 */ 334 this(Stream stream, bool withFieldName = false) 335 { 336 stream_ = stream; 337 withFieldName_ = withFieldName; 338 } 339 340 341 /** 342 * Constructs a packer with $(D_PARAM withFieldName). 343 * 344 * Params: 345 * withFieldName = serialize class / struct with field name 346 */ 347 this(bool withFieldName) 348 { 349 withFieldName_ = withFieldName; 350 } 351 352 353 /** 354 * Forwards to stream. 355 * 356 * Returns: 357 * the stream. 358 */ 359 @property @safe 360 nothrow ref Stream stream() 361 { 362 return stream_; 363 } 364 365 366 /** 367 * Serializes argument and writes to stream. 368 * 369 * If the argument is the pointer type, dereferences the pointer and serializes pointed value. 370 * ----- 371 * int a = 10; 372 * int* b = &b; 373 * 374 * packer.pack(b); // serializes 10, not address of a 375 * ----- 376 * Serializes nil if the argument of nullable type is null. 377 * 378 * NOTE: 379 * MessagePack doesn't define $(D_KEYWORD real) type format. 380 * Don't serialize $(D_KEYWORD real) if you communicate with other languages. 381 * Transfer $(D_KEYWORD double) serialization if $(D_KEYWORD real) on your environment equals $(D_KEYWORD double). 382 * 383 * Params: 384 * value = the content to serialize. 385 * 386 * Returns: 387 * self, i.e. for method chaining. 388 */ 389 ref PackerImpl pack(T)(in T value) if (is(Unqual!T == bool)) 390 { 391 if (value) 392 stream_.put(Format.TRUE); 393 else 394 stream_.put(Format.FALSE); 395 396 return this; 397 } 398 399 400 /// ditto 401 ref PackerImpl pack(T)(in T value) if (isUnsigned!T && !is(Unqual!T == enum)) 402 { 403 // ulong < ulong is slower than uint < uint 404 static if (!is(Unqual!T == ulong)) { 405 enum Bits = T.sizeof * 8; 406 407 if (value < (1 << 8)) { 408 if (value < (1 << 7)) { 409 // fixnum 410 stream_.put(take8from!Bits(value)); 411 } else { 412 // uint 8 413 store_[0] = Format.UINT8; 414 store_[1] = take8from!Bits(value); 415 stream_.put(store_[0..Offset + ubyte.sizeof]); 416 } 417 } else { 418 if (value < (1 << 16)) { 419 // uint 16 420 const temp = convertEndianTo!16(value); 421 422 store_[0] = Format.UINT16; 423 *cast(ushort*)&store_[Offset] = temp; 424 stream_.put(store_[0..Offset + ushort.sizeof]); 425 } else { 426 // uint 32 427 const temp = convertEndianTo!32(value); 428 429 store_[0] = Format.UINT32; 430 *cast(uint*)&store_[Offset] = temp; 431 stream_.put(store_[0..Offset + uint.sizeof]); 432 } 433 } 434 } else { 435 if (value < (1UL << 8)) { 436 if (value < (1UL << 7)) { 437 // fixnum 438 stream_.put(take8from!64(value)); 439 } else { 440 // uint 8 441 store_[0] = Format.UINT8; 442 store_[1] = take8from!64(value); 443 stream_.put(store_[0..Offset + ubyte.sizeof]); 444 } 445 } else { 446 if (value < (1UL << 16)) { 447 // uint 16 448 const temp = convertEndianTo!16(value); 449 450 store_[0] = Format.UINT16; 451 *cast(ushort*)&store_[Offset] = temp; 452 stream_.put(store_[0..Offset + ushort.sizeof]); 453 } else if (value < (1UL << 32)){ 454 // uint 32 455 const temp = convertEndianTo!32(value); 456 457 store_[0] = Format.UINT32; 458 *cast(uint*)&store_[Offset] = temp; 459 stream_.put(store_[0..Offset + uint.sizeof]); 460 } else { 461 // uint 64 462 const temp = convertEndianTo!64(value); 463 464 store_[0] = Format.UINT64; 465 *cast(ulong*)&store_[Offset] = temp; 466 stream_.put(store_[0..Offset + ulong.sizeof]); 467 } 468 } 469 } 470 471 return this; 472 } 473 474 475 /// ditto 476 ref PackerImpl pack(T)(in T value) if (isSigned!T && isIntegral!T && !is(Unqual!T == enum)) 477 { 478 // long < long is slower than int < int 479 static if (!is(Unqual!T == long)) { 480 enum Bits = T.sizeof * 8; 481 482 if (value < -(1 << 5)) { 483 if (value < -(1 << 15)) { 484 // int 32 485 const temp = convertEndianTo!32(value); 486 487 store_[0] = Format.INT32; 488 *cast(int*)&store_[Offset] = temp; 489 stream_.put(store_[0..Offset + int.sizeof]); 490 } else if (value < -(1 << 7)) { 491 // int 16 492 const temp = convertEndianTo!16(value); 493 494 store_[0] = Format.INT16; 495 *cast(short*)&store_[Offset] = temp; 496 stream_.put(store_[0..Offset + short.sizeof]); 497 } else { 498 // int 8 499 store_[0] = Format.INT8; 500 store_[1] = take8from!Bits(value); 501 stream_.put(store_[0..Offset + byte.sizeof]); 502 } 503 } else if (value < (1 << 7)) { 504 // fixnum 505 stream_.put(take8from!Bits(value)); 506 } else { 507 if (value < (1 << 8)) { 508 // uint 8 509 store_[0] = Format.UINT8; 510 store_[1] = take8from!Bits(value); 511 stream_.put(store_[0..Offset + ubyte.sizeof]); 512 } else if (value < (1 << 16)) { 513 // uint 16 514 const temp = convertEndianTo!16(value); 515 516 store_[0] = Format.UINT16; 517 *cast(ushort*)&store_[Offset] = temp; 518 stream_.put(store_[0..Offset + ushort.sizeof]); 519 } else { 520 // uint 32 521 const temp = convertEndianTo!32(value); 522 523 store_[0] = Format.UINT32; 524 *cast(uint*)&store_[Offset] = temp; 525 stream_.put(store_[0..Offset + uint.sizeof]); 526 } 527 } 528 } else { 529 if (value < -(1L << 5)) { 530 if (value < -(1L << 15)) { 531 if (value < -(1L << 31)) { 532 // int 64 533 const temp = convertEndianTo!64(value); 534 535 store_[0] = Format.INT64; 536 *cast(long*)&store_[Offset] = temp; 537 stream_.put(store_[0..Offset + long.sizeof]); 538 } else { 539 // int 32 540 const temp = convertEndianTo!32(value); 541 542 store_[0] = Format.INT32; 543 *cast(int*)&store_[Offset] = temp; 544 stream_.put(store_[0..Offset + int.sizeof]); 545 } 546 } else { 547 if (value < -(1L << 7)) { 548 // int 16 549 const temp = convertEndianTo!16(value); 550 551 store_[0] = Format.INT16; 552 *cast(short*)&store_[Offset] = temp; 553 stream_.put(store_[0..Offset + short.sizeof]); 554 } else { 555 // int 8 556 store_[0] = Format.INT8; 557 store_[1] = take8from!64(value); 558 stream_.put(store_[0..Offset + byte.sizeof]); 559 } 560 } 561 } else if (value < (1L << 7)) { 562 // fixnum 563 stream_.put(take8from!64(value)); 564 } else { 565 if (value < (1L << 16)) { 566 if (value < (1L << 8)) { 567 // uint 8 568 store_[0] = Format.UINT8; 569 store_[1] = take8from!64(value); 570 stream_.put(store_[0..Offset + ubyte.sizeof]); 571 } else { 572 // uint 16 573 const temp = convertEndianTo!16(value); 574 575 store_[0] = Format.UINT16; 576 *cast(ushort*)&store_[Offset] = temp; 577 stream_.put(store_[0..Offset + ushort.sizeof]); 578 } 579 } else { 580 if (value < (1L << 32)) { 581 // uint 32 582 const temp = convertEndianTo!32(value); 583 584 store_[0] = Format.UINT32; 585 *cast(uint*)&store_[Offset] = temp; 586 stream_.put(store_[0..Offset + uint.sizeof]); 587 } else { 588 // uint 64 589 const temp = convertEndianTo!64(value); 590 591 store_[0] = Format.UINT64; 592 *cast(ulong*)&store_[Offset] = temp; 593 stream_.put(store_[0..Offset + ulong.sizeof]); 594 } 595 } 596 } 597 } 598 599 return this; 600 } 601 602 603 /// ditto 604 ref PackerImpl pack(T)(in T value) if (isFloatingPoint!T && !is(Unqual!T == enum)) 605 { 606 static if (is(Unqual!T == float)) { 607 const temp = convertEndianTo!32(_f(value).i); 608 609 store_[0] = Format.FLOAT; 610 *cast(uint*)&store_[Offset] = temp; 611 stream_.put(store_[0..Offset + uint.sizeof]); 612 } else static if (is(Unqual!T == double)) { 613 const temp = convertEndianTo!64(_d(value).i); 614 615 store_[0] = Format.DOUBLE; 616 *cast(ulong*)&store_[Offset] = temp; 617 stream_.put(store_[0..Offset + ulong.sizeof]); 618 } else { 619 static if ((real.sizeof > double.sizeof) && EnableReal) { 620 store_[0] = Format.REAL; 621 const temp = _r(value); 622 const fraction = convertEndianTo!64(temp.fraction); 623 const exponent = convertEndianTo!16(temp.exponent); 624 625 *cast(Unqual!(typeof(fraction))*)&store_[Offset] = fraction; 626 *cast(Unqual!(typeof(exponent))*)&store_[Offset + fraction.sizeof] = exponent; 627 stream_.put(store_[0..$]); 628 } else { // Non-x86 CPUs, real type equals double type. 629 pack(cast(double)value); 630 } 631 } 632 633 return this; 634 } 635 636 637 /// ditto 638 ref PackerImpl pack(T)(in T value) if (is(Unqual!T == enum)) 639 { 640 pack(cast(OriginalType!T)value); 641 642 return this; 643 } 644 645 646 /// Overload for pack(null) for 2.057 or later 647 static if (!is(typeof(null) == void*)) 648 { 649 ref PackerImpl pack(T)(in T value) if (is(Unqual!T == typeof(null))) 650 { 651 return packNil(); 652 } 653 } 654 655 656 /// ditto 657 ref PackerImpl pack(T)(in T value) if (isPointer!T) 658 { 659 static if (is(Unqual!T == void*)) { // for pack(null) for 2.056 or earlier 660 enforce(value is null, "Can't serialize void type"); 661 stream_.put(Format.NIL); 662 } else { 663 if (value is null) 664 stream_.put(Format.NIL); 665 else 666 pack(mixin(AsteriskOf!T ~ "value")); 667 } 668 669 return this; 670 } 671 672 673 /// ditto 674 ref PackerImpl pack(T)(in T array) if (isArray!T) 675 { 676 alias typeof(T.init[0]) U; 677 678 /* 679 * Serializes raw type-information to stream. 680 */ 681 void beginRaw(in size_t length) 682 { 683 if (length < 32) { 684 const ubyte temp = Format.RAW | cast(ubyte)length; 685 stream_.put(take8from(temp)); 686 } else if (length < 65536) { 687 const temp = convertEndianTo!16(length); 688 689 store_[0] = Format.RAW16; 690 *cast(ushort*)&store_[Offset] = temp; 691 stream_.put(store_[0..Offset + ushort.sizeof]); 692 } else { 693 const temp = convertEndianTo!32(length); 694 695 store_[0] = Format.RAW32; 696 *cast(uint*)&store_[Offset] = temp; 697 stream_.put(store_[0..Offset + uint.sizeof]); 698 } 699 } 700 701 if (array.empty) 702 return packNil(); 703 704 // Raw bytes 705 static if (isByte!(U) || isSomeChar!(U)) { 706 ubyte[] raw = cast(ubyte[])array; 707 708 beginRaw(raw.length); 709 stream_.put(raw); 710 } else { 711 beginArray(array.length); 712 foreach (elem; array) 713 pack(elem); 714 } 715 716 return this; 717 } 718 719 720 /// ditto 721 ref PackerImpl pack(T)(in T array) if (isAssociativeArray!T) 722 { 723 if (array is null) 724 return packNil(); 725 726 beginMap(array.length); 727 foreach (key, value; array) { 728 pack(key); 729 pack(value); 730 } 731 732 return this; 733 } 734 735 736 /// ditto 737 ref PackerImpl pack(Types...)(auto ref const Types objects) if (Types.length > 1) 738 { 739 foreach (i, T; Types) 740 pack(objects[i]); 741 742 return this; 743 } 744 745 746 /** 747 * Serializes $(D_PARAM object) and writes to stream. 748 * 749 * Calling $(D toMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D toMsgpack) method. $(D toMsgpack) signature is: 750 * ----- 751 * void toMsgpack(Packer)(ref Packer packer) const 752 * ----- 753 * This method serializes all members of T object if $(D_KEYWORD class) and $(D_KEYWORD struct) don't implement $(D toMsgpack). 754 * 755 * An object that doesn't implement $(D toMsgpack) is serialized to Array type. 756 * ----- 757 * packer.pack(tuple(true, 1, "Hi!")) // -> '[true, 1, "Hi!"]', not 'ture, 1, "Hi!"' 758 * 759 * struct Foo 760 * { 761 * int num = 10; 762 * string msg = "D!"; 763 * } 764 * packer.pack(Foo()); // -> '[10, "D!"]' 765 * 766 * class Base 767 * { 768 * bool flag = true; 769 * } 770 * class Derived : Base 771 * { 772 * double = 0.5f; 773 * } 774 * packer.pack(new Derived()); // -> '[true, 0.5f]' 775 * ----- 776 * 777 * Params: 778 * object = the content to serialize. 779 * 780 * Returns: 781 * self, i.e. for method chaining. 782 */ 783 ref PackerImpl pack(T)(in T object) if (is(Unqual!T == class)) 784 { 785 if (object is null) 786 return packNil(); 787 788 static if (hasMember!(T, "toMsgpack")) 789 { 790 static if (__traits(compiles, { T t; t.toMsgpack(this, withFieldName_); })) { 791 object.toMsgpack(this, withFieldName_); 792 } else static if (__traits(compiles, { T t; t.toMsgpack(this); })) { // backward compatible 793 object.toMsgpack(this); 794 } else { 795 static assert(0, "Failed to invoke 'toMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 796 } 797 } else { 798 if (auto handler = object.classinfo in packHandlers) { 799 (*handler)(this, cast(void*)&object); 800 return this; 801 } 802 if (T.classinfo !is object.classinfo) { 803 throw new MessagePackException("Can't pack derived class through reference to base class."); 804 } 805 806 alias SerializingClasses!(T) Classes; 807 808 immutable memberNum = SerializingMemberNumbers!(Classes); 809 if (withFieldName_) 810 beginMap(memberNum); 811 else 812 beginArray(memberNum); 813 814 foreach (Class; Classes) { 815 Class obj = cast(Class)object; 816 if (withFieldName_) { 817 foreach (i, f ; obj.tupleof) { 818 static if (isPackedField!(Class.tupleof[i])) { 819 pack(getFieldName!(Class, i)); 820 pack(f); 821 } 822 } 823 } else { 824 foreach (i, f ; obj.tupleof) { 825 static if (isPackedField!(Class.tupleof[i])) 826 pack(f); 827 } 828 } 829 } 830 } 831 832 return this; 833 } 834 835 836 /// ditto 837 @trusted 838 ref PackerImpl pack(T)(auto ref T object) if (is(Unqual!T == struct)) 839 { 840 static if (hasMember!(T, "toMsgpack")) 841 { 842 static if (__traits(compiles, { T t; t.toMsgpack(this, withFieldName_); })) { 843 object.toMsgpack(this, withFieldName_); 844 } else static if (__traits(compiles, { T t; t.toMsgpack(this); })) { // backward compatible 845 object.toMsgpack(this); 846 } else { 847 static assert(0, "Failed to invoke 'toMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 848 } 849 } else static if (isTuple!T) { 850 beginArray(object.field.length); 851 foreach (f; object.field) 852 pack(f); 853 } else { // simple struct 854 if (auto handler = typeid(T) in packHandlers) { 855 (*handler)(this, cast(void*)&object); 856 return this; 857 } 858 859 immutable memberNum = SerializingMemberNumbers!(T); 860 if (withFieldName_) 861 beginMap(memberNum); 862 else 863 beginArray(memberNum); 864 865 if (withFieldName_) { 866 foreach (i, f; object.tupleof) { 867 static if (isPackedField!(T.tupleof[i]) && __traits(compiles, { pack(f); })) 868 { 869 pack(getFieldName!(T, i)); 870 pack(f); 871 } 872 } 873 } else { 874 foreach (i, f; object.tupleof) { 875 static if (isPackedField!(T.tupleof[i]) && __traits(compiles, { pack(f); })) 876 pack(f); 877 } 878 } 879 } 880 881 return this; 882 } 883 884 885 /** 886 * Serializes the arguments as container to stream. 887 * 888 * ----- 889 * packer.packArray(true, 1); // -> [true, 1] 890 * packer.packMap("Hi", 100); // -> ["Hi":100] 891 * ----- 892 * 893 * In packMap, the number of arguments must be even. 894 * 895 * Params: 896 * objects = the contents to serialize. 897 * 898 * Returns: 899 * self, i.e. for method chaining. 900 */ 901 ref PackerImpl packArray(Types...)(auto ref const Types objects) 902 { 903 beginArray(Types.length); 904 foreach (i, T; Types) 905 pack(objects[i]); 906 //pack(objects); // slow :( 907 908 return this; 909 } 910 911 912 /// ditto 913 ref PackerImpl packMap(Types...)(auto ref const Types objects) 914 { 915 static assert(Types.length % 2 == 0, "The number of arguments must be even"); 916 917 beginMap(Types.length / 2); 918 foreach (i, T; Types) 919 pack(objects[i]); 920 921 return this; 922 } 923 924 925 /** 926 * Serializes the type-information to stream. 927 * 928 * These methods don't serialize contents. 929 * You need to call pack method to serialize contents at your own risk. 930 * ----- 931 * packer.beginArray(3).pack(true, 1); // -> [true, 1, 932 * 933 * // other operation 934 * 935 * packer.pack("Hi!"); // -> [true, 1, "Hi!"] 936 * ----- 937 * 938 * Params: 939 * length = the length of container. 940 * 941 * Returns: 942 * self, i.e. for method chaining. 943 */ 944 ref PackerImpl beginArray(in size_t length) 945 { 946 if (length < 16) { 947 const ubyte temp = Format.ARRAY | cast(ubyte)length; 948 stream_.put(take8from(temp)); 949 } else if (length < 65536) { 950 const temp = convertEndianTo!16(length); 951 952 store_[0] = Format.ARRAY16; 953 *cast(ushort*)&store_[Offset] = temp; 954 stream_.put(store_[0..Offset + ushort.sizeof]); 955 } else { 956 const temp = convertEndianTo!32(length); 957 958 store_[0] = Format.ARRAY32; 959 *cast(uint*)&store_[Offset] = temp; 960 stream_.put(store_[0..Offset + uint.sizeof]); 961 } 962 963 return this; 964 } 965 966 967 /// ditto 968 ref PackerImpl beginMap(in size_t length) 969 { 970 if (length < 16) { 971 const ubyte temp = Format.MAP | cast(ubyte)length; 972 stream_.put(take8from(temp)); 973 } else if (length < 65536) { 974 const temp = convertEndianTo!16(length); 975 976 store_[0] = Format.MAP16; 977 *cast(ushort*)&store_[Offset] = temp; 978 stream_.put(store_[0..Offset + ushort.sizeof]); 979 } else { 980 const temp = convertEndianTo!32(length); 981 982 store_[0] = Format.MAP32; 983 *cast(uint*)&store_[Offset] = temp; 984 stream_.put(store_[0..Offset + uint.sizeof]); 985 } 986 987 return this; 988 } 989 990 991 private: 992 /* 993 * Serializes the nil value. 994 */ 995 ref PackerImpl packNil() 996 { 997 stream_.put(Format.NIL); 998 return this; 999 } 1000 } 1001 1002 1003 /// Default serializer 1004 alias PackerImpl!(Appender!(ubyte[])) Packer; // should be pure struct? 1005 1006 1007 /** 1008 * Register a serialization handler for $(D_PARAM T) type 1009 * 1010 * Example: 1011 * ----- 1012 * registerPackHandler!(Foo, fooPackHandler); 1013 * ----- 1014 */ 1015 void registerPackHandler(T, alias Handler, Stream = Appender!(ubyte[]))() 1016 { 1017 PackerImpl!(Stream).registerHandler!(T, Handler); 1018 } 1019 1020 1021 /** 1022 * Helper for $(D Packer) construction. 1023 * 1024 * Params: 1025 * stream = the stream to write. 1026 * withFieldName = serialize class / struct with field name 1027 * 1028 * Returns: 1029 * a $(D Packer) object instantiated and initialized according to the arguments. 1030 */ 1031 PackerImpl!(Stream) packer(Stream)(Stream stream, bool withFieldName = false) 1032 { 1033 return typeof(return)(stream, withFieldName); 1034 } 1035 1036 1037 // Buffer implementations 1038 1039 1040 /** 1041 * $(D RefBuffer) is a reference stored buffer for more efficient serialization 1042 * 1043 * Example: 1044 * ----- 1045 * auto packer = packer(RefBuffer(16)); // threshold is 16 1046 * 1047 * // packs data 1048 * 1049 * writev(fd, cast(void*)packer.buffer.vector.ptr, packer.buffer.vector.length); 1050 * ----- 1051 */ 1052 struct RefBuffer 1053 { 1054 private: 1055 static struct Chunk 1056 { 1057 ubyte[] data; // storing serialized value 1058 size_t used; // used size of data 1059 } 1060 1061 immutable size_t Threshold; 1062 immutable size_t ChunkSize; 1063 1064 // for putCopy 1065 Chunk[] chunks_; // memory chunk for buffer 1066 size_t index_; // index for cunrrent chunk 1067 1068 // for putRef 1069 iovec[] vecList_; // reference to large data or copied data. 1070 1071 1072 public: 1073 /** 1074 * Constructs a buffer. 1075 * 1076 * Params: 1077 * threshold = the threshold of writing value or stores reference. 1078 * chunkSize = the default size of chunk for allocation. 1079 */ 1080 @safe 1081 this(in size_t threshold, in size_t chunkSize = 8192) 1082 { 1083 Threshold = threshold; 1084 ChunkSize = chunkSize; 1085 1086 chunks_.length = 1; 1087 chunks_[index_].data.length = chunkSize; 1088 } 1089 1090 1091 /** 1092 * Returns the buffer contents that excluding references. 1093 * 1094 * Returns: 1095 * the non-contiguous copied contents. 1096 */ 1097 @property @safe 1098 nothrow ubyte[] data() 1099 { 1100 ubyte[] result; 1101 1102 foreach (ref chunk; chunks_) 1103 result ~= chunk.data[0..chunk.used]; 1104 1105 return result; 1106 } 1107 1108 1109 /** 1110 * Forwards to all buffer contents. 1111 * 1112 * Returns: 1113 * the array of iovec struct that stores references. 1114 */ 1115 @property @safe 1116 nothrow ref iovec[] vector() 1117 { 1118 return vecList_; 1119 } 1120 1121 1122 /** 1123 * Writes the argument to buffer and stores the reference of writed content 1124 * if the argument size is smaller than threshold, 1125 * otherwise stores the reference of argument directly. 1126 * 1127 * Params: 1128 * value = the content to write. 1129 */ 1130 @safe 1131 void put(in ubyte value) 1132 { 1133 ubyte[1] values = [value]; 1134 putCopy(values); 1135 } 1136 1137 1138 /// ditto 1139 @safe 1140 void put(in ubyte[] value) 1141 { 1142 if (value.length < Threshold) 1143 putCopy(value); 1144 else 1145 putRef(value); 1146 } 1147 1148 1149 private: 1150 /* 1151 * Stores the reference of $(D_PARAM value). 1152 * 1153 * Params: 1154 * value = the content to write. 1155 */ 1156 @trusted 1157 void putRef(in ubyte[] value) 1158 { 1159 vecList_.length += 1; 1160 vecList_[$ - 1] = iovec(cast(void*)value.ptr, value.length); 1161 } 1162 1163 1164 /* 1165 * Writes $(D_PARAM value) to buffer and appends to its reference. 1166 * 1167 * Params: 1168 * value = the contents to write. 1169 */ 1170 @trusted 1171 void putCopy(in ubyte[] value) 1172 { 1173 /* 1174 * Helper for expanding new space. 1175 */ 1176 void expand(in size_t size) 1177 { 1178 const newSize = size < ChunkSize ? ChunkSize : size; 1179 1180 index_++; 1181 chunks_.length = 1; 1182 chunks_[index_].data.length = newSize; 1183 } 1184 1185 const size = value.length; 1186 1187 // lacks current chunk? 1188 if (chunks_[index_].data.length - chunks_[index_].used < size) 1189 expand(size); 1190 1191 const base = chunks_[index_].used; // start index 1192 auto data = chunks_[index_].data[base..base + size]; // chunk to write 1193 1194 data[] = value[]; 1195 chunks_[index_].used += size; 1196 1197 // Optimization for avoiding iovec allocation. 1198 if (vecList_.length && data.ptr == (vecList_[$ - 1].iov_base + 1199 vecList_[$ - 1].iov_len)) 1200 vecList_[$ - 1].iov_len += size; 1201 else 1202 putRef(data); 1203 } 1204 } 1205 1206 1207 unittest 1208 { 1209 static assert(isOutputRange!(RefBuffer, ubyte) && 1210 isOutputRange!(RefBuffer, ubyte[])); 1211 1212 auto buffer = RefBuffer(2, 4); 1213 1214 ubyte[] tests = [1, 2]; 1215 foreach (v; tests) 1216 buffer.put(v); 1217 buffer.put(tests); 1218 1219 assert(buffer.data == tests, "putCopy failed"); 1220 1221 iovec[] vector = buffer.vector; 1222 ubyte[] result; 1223 1224 assert(vector.length == 2, "Optimization failed"); 1225 1226 foreach (v; vector) 1227 result ~= (cast(ubyte*)v.iov_base)[0..v.iov_len]; 1228 1229 assert(result == tests ~ tests); 1230 } 1231 1232 1233 version (unittest) 1234 { 1235 mixin template DefinePacker() 1236 { 1237 Packer packer; 1238 } 1239 1240 mixin template DefineDictionalPacker() 1241 { 1242 Packer packer = Packer(false); 1243 } 1244 } 1245 1246 unittest 1247 { 1248 { // unique value 1249 mixin DefinePacker; 1250 1251 ubyte[] result = [Format.NIL, Format.TRUE, Format.FALSE]; 1252 1253 packer.pack(null, true, false); 1254 foreach (i, value; packer.stream.data) 1255 assert(value == result[i]); 1256 } 1257 { // uint * 1258 static struct UTest { ubyte format; ulong value; } 1259 1260 enum : ulong { A = ubyte.max, B = ushort.max, C = uint.max, D = ulong.max } 1261 1262 static UTest[][] utests = [ 1263 [{Format.UINT8, A}], 1264 [{Format.UINT8, A}, {Format.UINT16, B}], 1265 [{Format.UINT8, A}, {Format.UINT16, B}, {Format.UINT32, C}], 1266 [{Format.UINT8, A}, {Format.UINT16, B}, {Format.UINT32, C}, {Format.UINT64, D}], 1267 ]; 1268 1269 foreach (I, T; TypeTuple!(ubyte, ushort, uint, ulong)) { 1270 foreach (i, test; utests[I]) { 1271 mixin DefinePacker; 1272 1273 packer.pack(cast(T)test.value); 1274 assert(packer.stream.data[0] == test.format); 1275 1276 switch (i) { 1277 case 0: 1278 auto answer = take8from!(T.sizeof * 8)(test.value); 1279 assert(memcmp(&packer.stream.data[1], &answer, ubyte.sizeof) == 0); 1280 break; 1281 case 1: 1282 auto answer = convertEndianTo!16(test.value); 1283 assert(memcmp(&packer.stream.data[1], &answer, ushort.sizeof) == 0); 1284 break; 1285 case 2: 1286 auto answer = convertEndianTo!32(test.value); 1287 assert(memcmp(&packer.stream.data[1], &answer, uint.sizeof) == 0); 1288 break; 1289 default: 1290 auto answer = convertEndianTo!64(test.value); 1291 assert(memcmp(&packer.stream.data[1], &answer, ulong.sizeof) == 0); 1292 } 1293 } 1294 } 1295 } 1296 { // int * 1297 static struct STest { ubyte format; long value; } 1298 1299 enum : long { A = byte.min, B = short.min, C = int.min, D = long.min } 1300 1301 static STest[][] stests = [ 1302 [{Format.INT8, A}], 1303 [{Format.INT8, A}, {Format.INT16, B}], 1304 [{Format.INT8, A}, {Format.INT16, B}, {Format.INT32, C}], 1305 [{Format.INT8, A}, {Format.INT16, B}, {Format.INT32, C}, {Format.INT64, D}], 1306 ]; 1307 1308 foreach (I, T; TypeTuple!(byte, short, int, long)) { 1309 foreach (i, test; stests[I]) { 1310 mixin DefinePacker; 1311 1312 packer.pack(cast(T)test.value); 1313 assert(packer.stream.data[0] == test.format); 1314 1315 switch (i) { 1316 case 0: 1317 auto answer = take8from!(T.sizeof * 8)(test.value); 1318 assert(memcmp(&packer.stream.data[1], &answer, byte.sizeof) == 0); 1319 break; 1320 case 1: 1321 auto answer = convertEndianTo!16(test.value); 1322 assert(memcmp(&packer.stream.data[1], &answer, short.sizeof) == 0); 1323 break; 1324 case 2: 1325 auto answer = convertEndianTo!32(test.value); 1326 assert(memcmp(&packer.stream.data[1], &answer, int.sizeof) == 0); 1327 break; 1328 default: 1329 auto answer = convertEndianTo!64(test.value); 1330 assert(memcmp(&packer.stream.data[1], &answer, long.sizeof) == 0); 1331 } 1332 } 1333 } 1334 } 1335 { // fload, double 1336 static if ((real.sizeof == double.sizeof) || !EnableReal) 1337 { 1338 alias TypeTuple!(float, double, double) FloatingTypes; 1339 static struct FTest { ubyte format; double value; } 1340 1341 static FTest[] ftests = [ 1342 {Format.FLOAT, float.min_normal}, 1343 {Format.DOUBLE, double.max}, 1344 {Format.DOUBLE, double.max}, 1345 ]; 1346 } 1347 else 1348 { 1349 alias TypeTuple!(float, double, real) FloatingTypes; 1350 static struct FTest { ubyte format; real value; } 1351 1352 static FTest[] ftests = [ 1353 {Format.FLOAT, float.min_normal}, 1354 {Format.DOUBLE, double.max}, 1355 {Format.REAL, real.max}, 1356 ]; 1357 } 1358 1359 foreach (I, T; FloatingTypes) { 1360 mixin DefinePacker; 1361 1362 packer.pack(cast(T)ftests[I].value); 1363 assert(packer.stream.data[0] == ftests[I].format); 1364 1365 switch (I) { 1366 case 0: 1367 const answer = convertEndianTo!32(_f(cast(T)ftests[I].value).i); 1368 assert(memcmp(&packer.stream.data[1], &answer, float.sizeof) == 0); 1369 break; 1370 case 1: 1371 const answer = convertEndianTo!64(_d(cast(T)ftests[I].value).i); 1372 assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0); 1373 break; 1374 default: 1375 static if (EnableReal) 1376 { 1377 const t = _r(cast(T)ftests[I].value); 1378 const f = convertEndianTo!64(t.fraction); 1379 const e = convertEndianTo!16(t.exponent); 1380 assert(memcmp(&packer.stream.data[1], &f, f.sizeof) == 0); 1381 assert(memcmp(&packer.stream.data[1 + f.sizeof], &e, e.sizeof) == 0); 1382 } 1383 else 1384 { 1385 const answer = convertEndianTo!64(_d(cast(T)ftests[I].value).i); 1386 assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0); 1387 } 1388 } 1389 } 1390 } 1391 { // pointer 1392 static struct PTest 1393 { 1394 ubyte format; 1395 1396 union 1397 { 1398 ulong* p0; 1399 long* p1; 1400 double* p2; 1401 } 1402 } 1403 1404 PTest[] ptests = [PTest(Format.UINT64), PTest(Format.INT64), PTest(Format.DOUBLE)]; 1405 1406 ulong v0 = ulong.max; 1407 long v1 = long.min; 1408 double v2 = double.max; 1409 1410 foreach (I, Index; TypeTuple!("0", "1", "2")) { 1411 mixin DefinePacker; 1412 1413 mixin("ptests[I].p" ~ Index ~ " = &v" ~ Index ~ ";"); 1414 1415 packer.pack(mixin("ptests[I].p" ~ Index)); 1416 assert(packer.stream.data[0] == ptests[I].format); 1417 1418 switch (I) { 1419 case 0: 1420 auto answer = convertEndianTo!64(*ptests[I].p0); 1421 assert(memcmp(&packer.stream.data[1], &answer, ulong.sizeof) == 0); 1422 break; 1423 case 1: 1424 auto answer = convertEndianTo!64(*ptests[I].p1); 1425 assert(memcmp(&packer.stream.data[1], &answer, long.sizeof) == 0); 1426 break; 1427 default: 1428 const answer = convertEndianTo!64(_d(*ptests[I].p2).i); 1429 assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0); 1430 } 1431 } 1432 } 1433 { // enum 1434 enum E : ubyte { A = ubyte.max } 1435 1436 mixin DefinePacker; E e = E.A; 1437 1438 packer.pack(e); 1439 assert(packer.stream.data[0] == Format.UINT8); 1440 1441 auto answer = E.A; 1442 assert(memcmp(&packer.stream.data[1], &answer, (OriginalType!E).sizeof) == 0); 1443 } 1444 { // container 1445 static struct CTest { ubyte format; size_t value; } 1446 1447 enum : ulong { A = 16 / 2, B = ushort.max, C = uint.max } 1448 1449 static CTest[][] ctests = [ 1450 [{Format.ARRAY | A, Format.ARRAY | A}, {Format.ARRAY16, B}, {Format.ARRAY32, C}], 1451 [{Format.MAP | A, Format.MAP | A}, {Format.MAP16, B}, {Format.MAP32, C}], 1452 ]; 1453 1454 foreach (I, Name; TypeTuple!("Array", "Map")) { 1455 auto test = ctests[I]; 1456 1457 foreach (i, T; TypeTuple!(ubyte, ushort, uint)) { 1458 mixin DefinePacker; 1459 mixin("packer.begin" ~ Name ~ "(i ? test[i].value : A);"); 1460 1461 assert(packer.stream.data[0] == test[i].format); 1462 1463 switch (i) { 1464 case 0: 1465 auto answer = take8from(test[i].value); 1466 assert(memcmp(&packer.stream.data[0], &answer, ubyte.sizeof) == 0); 1467 break; 1468 case 1: 1469 auto answer = convertEndianTo!16(test[i].value); 1470 assert(memcmp(&packer.stream.data[1], &answer, ushort.sizeof) == 0); 1471 break; 1472 default: 1473 auto answer = convertEndianTo!32(test[i].value); 1474 assert(memcmp(&packer.stream.data[1], &answer, uint.sizeof) == 0); 1475 } 1476 } 1477 } 1478 } 1479 { // user defined 1480 { 1481 static struct S 1482 { 1483 uint num = uint.max; 1484 1485 void toMsgpack(P)(ref P p) const { p.packArray(num); } 1486 } 1487 1488 mixin DefinePacker; S test; 1489 1490 packer.pack(test); 1491 1492 assert(packer.stream.data[0] == (Format.ARRAY | 1)); 1493 assert(packer.stream.data[1] == Format.UINT32); 1494 assert(memcmp(&packer.stream.data[2], &test.num, uint.sizeof) == 0); 1495 } 1496 { 1497 mixin DefinePacker; auto test = tuple(true, false, uint.max); 1498 1499 packer.pack(test); 1500 1501 assert(packer.stream.data[0] == (Format.ARRAY | 3)); 1502 assert(packer.stream.data[1] == Format.TRUE); 1503 assert(packer.stream.data[2] == Format.FALSE); 1504 assert(packer.stream.data[3] == Format.UINT32); 1505 assert(memcmp(&packer.stream.data[4], &test.field[2], uint.sizeof) == 0); 1506 } 1507 { 1508 static class C 1509 { 1510 uint num; 1511 1512 this(uint n) { num = n; } 1513 1514 void toMsgpack(P)(ref P p) const { p.packArray(num); } 1515 } 1516 1517 mixin DefinePacker; C test = new C(ushort.max); 1518 1519 packer.pack(test); 1520 1521 assert(packer.stream.data[0] == (Format.ARRAY | 1)); 1522 assert(packer.stream.data[1] == Format.UINT16); 1523 assert(memcmp(&packer.stream.data[2], &test.num, ushort.sizeof) == 0); 1524 } 1525 } 1526 { // simple struct and class 1527 { 1528 static struct Simple 1529 { 1530 uint num = uint.max; 1531 } 1532 1533 static struct SimpleWithNonPacked1 1534 { 1535 uint num = uint.max; 1536 @nonPacked string str = "ignored"; 1537 } 1538 1539 static struct SimpleWithNonPacked2 1540 { 1541 @nonPacked string str = "ignored"; 1542 uint num = uint.max; 1543 } 1544 1545 static struct SimpleWithSkippedTypes 1546 { 1547 int function(int) fn; 1548 int delegate(int) dg; 1549 uint num = uint.max; 1550 } 1551 1552 foreach (Type; TypeTuple!(Simple, SimpleWithNonPacked1, SimpleWithNonPacked2, SimpleWithSkippedTypes)) { 1553 mixin DefinePacker; 1554 1555 Type test; 1556 packer.pack(test); 1557 1558 assert(packer.stream.data[0] == (Format.ARRAY | 1)); 1559 assert(packer.stream.data[1] == Format.UINT32); 1560 assert(memcmp(&packer.stream.data[2], &test.num, uint.sizeof) == 0); 1561 } 1562 } 1563 1564 static class SimpleA 1565 { 1566 bool flag = true; 1567 } 1568 1569 static class SimpleB : SimpleA 1570 { 1571 ubyte type = 100; 1572 } 1573 1574 static class SimpleC : SimpleB 1575 { 1576 uint num = uint.max; 1577 } 1578 1579 static class SimpleCWithNonPacked1 : SimpleB 1580 { 1581 uint num = uint.max; 1582 @nonPacked string str = "ignored"; 1583 } 1584 1585 static class SimpleCWithNonPacked2 : SimpleB 1586 { 1587 @nonPacked string str = "ignored"; 1588 uint num = uint.max; 1589 } 1590 1591 static class SimpleCWithSkippedTypes : SimpleB 1592 { 1593 uint num = uint.max; 1594 int function(int) fn; 1595 int delegate(int) dg; 1596 } 1597 1598 { // from derived class 1599 foreach (Type; TypeTuple!(SimpleC, SimpleCWithNonPacked1, SimpleCWithNonPacked2, SimpleCWithSkippedTypes)) { 1600 mixin DefinePacker; 1601 1602 Type test = new Type(); 1603 packer.pack(test); 1604 1605 assert(packer.stream.data[0] == (Format.ARRAY | 3)); 1606 assert(packer.stream.data[1] == Format.TRUE); 1607 assert(packer.stream.data[2] == 100); 1608 assert(packer.stream.data[3] == Format.UINT32); 1609 assert(memcmp(&packer.stream.data[4], &test.num, uint.sizeof) == 0); 1610 } 1611 } 1612 { // from base class 1613 mixin DefinePacker; SimpleB test = new SimpleC(); 1614 1615 try { 1616 packer.pack(test); 1617 assert(false); 1618 } catch (Exception e) { } 1619 } 1620 } 1621 } 1622 1623 1624 // deserializing routines 1625 1626 1627 /** 1628 * $(D UnpackException) is thrown on deserialization failure 1629 */ 1630 class UnpackException : MessagePackException 1631 { 1632 this(string message) 1633 { 1634 super(message); 1635 } 1636 } 1637 1638 1639 version (D_Ddoc) 1640 { 1641 /** 1642 * Internal buffer and related operations for Unpacker 1643 * 1644 * Following Unpackers mixin this template. So, Unpacker can use following methods. 1645 * 1646 * ----- 1647 * //buffer image: 1648 * +-------------------------------------------+ 1649 * | [object] | [obj | unparsed... | unused... | 1650 * +-------------------------------------------+ 1651 * ^ offset 1652 * ^ current 1653 * ^ used 1654 * ^ buffer.length 1655 * ----- 1656 * 1657 * This mixin template is a private. 1658 */ 1659 mixin template InternalBuffer() 1660 { 1661 private: 1662 ubyte[] buffer_; // internal buffer 1663 size_t used_; // index that buffer cosumed 1664 size_t offset_; // index that buffer parsed 1665 size_t parsed_; // total size of parsed message 1666 bool hasRaw_; // indicates whether Raw object has been deserialized 1667 1668 1669 public: 1670 /** 1671 * Forwards to internal buffer. 1672 * 1673 * Returns: 1674 * the reference of internal buffer. 1675 */ 1676 @property @safe 1677 nothrow ubyte[] buffer(); 1678 1679 1680 /** 1681 * Fills internal buffer with $(D_PARAM target). 1682 * 1683 * Params: 1684 * target = new serialized buffer to deserialize. 1685 */ 1686 @safe void feed(in ubyte[] target); 1687 1688 1689 /** 1690 * Consumes buffer. This method is helper for buffer property. 1691 * You must use this method if you write bytes to buffer directly. 1692 * 1693 * Params: 1694 * size = the number of consuming. 1695 */ 1696 @safe 1697 nothrow void bufferConsumed(in size_t size); 1698 1699 1700 /** 1701 * Removes unparsed buffer. 1702 */ 1703 @safe 1704 nothrow void removeUnparsed(); 1705 1706 1707 /** 1708 * Returns: 1709 * the total size including unparsed buffer size. 1710 */ 1711 @property @safe 1712 nothrow size_t size() const; 1713 1714 1715 /** 1716 * Returns: 1717 * the parsed size of buffer. 1718 */ 1719 @property @safe 1720 nothrow size_t parsedSize() const; 1721 1722 1723 /** 1724 * Returns: 1725 * the unparsed size of buffer. 1726 */ 1727 @property @safe 1728 nothrow size_t unparsedSize() const; 1729 1730 1731 private: 1732 @safe 1733 void initializeBuffer(in ubyte[] target, in size_t bufferSize = 8192); 1734 } 1735 } 1736 else 1737 { 1738 private mixin template InternalBuffer() 1739 { 1740 private: 1741 ubyte[] buffer_; // internal buffer 1742 size_t used_; // index that buffer cosumed 1743 size_t offset_; // index that buffer parsed 1744 size_t parsed_; // total size of parsed message 1745 bool hasRaw_; // indicates whether Raw object has been deserialized 1746 1747 1748 public: 1749 @property @safe 1750 nothrow ubyte[] buffer() 1751 { 1752 return buffer_; 1753 } 1754 1755 1756 @safe 1757 void feed(in ubyte[] target) 1758 in 1759 { 1760 assert(target.length); 1761 } 1762 body 1763 { 1764 /* 1765 * Expands internal buffer. 1766 * 1767 * Params: 1768 * size = new buffer size to append. 1769 */ 1770 void expandBuffer(in size_t size) 1771 { 1772 // rewinds buffer(completed deserialization) 1773 if (used_ == offset_ && !hasRaw_) { 1774 used_ = offset_ = 0; 1775 1776 if (buffer_.length < size) 1777 buffer_.length = size; 1778 1779 return; 1780 } 1781 1782 // deserializing state is mid-flow(buffer has non-parsed data yet) 1783 auto unparsed = buffer_[offset_..used_]; 1784 auto restSize = buffer_.length - used_ + offset_; 1785 auto newSize = size > restSize ? unparsedSize + size : buffer_.length; 1786 1787 if (hasRaw_) { 1788 hasRaw_ = false; 1789 buffer_ = new ubyte[](newSize); 1790 } else { 1791 buffer_.length = newSize; 1792 1793 // avoids overlapping copy 1794 auto area = buffer_[0..unparsedSize]; 1795 unparsed = area.overlap(unparsed) ? unparsed.dup : unparsed; 1796 } 1797 1798 buffer_[0..unparsedSize] = unparsed[]; 1799 used_ = unparsedSize; 1800 offset_ = 0; 1801 } 1802 1803 const size = target.length; 1804 1805 // lacks current buffer? 1806 if (buffer_.length - used_ < size) 1807 expandBuffer(size); 1808 1809 buffer_[used_..used_ + size] = target[]; 1810 used_ += size; 1811 } 1812 1813 1814 @safe 1815 nothrow void bufferConsumed(in size_t size) 1816 { 1817 if (used_ + size > buffer_.length) 1818 used_ = buffer_.length; 1819 else 1820 used_ += size; 1821 } 1822 1823 1824 @safe 1825 nothrow void removeUnparsed() 1826 { 1827 used_ = offset_; 1828 } 1829 1830 1831 @property @safe 1832 nothrow size_t size() const 1833 { 1834 return parsed_ - offset_ + used_; 1835 } 1836 1837 1838 @property @safe 1839 nothrow size_t parsedSize() const 1840 { 1841 return parsed_; 1842 } 1843 1844 1845 @property @safe 1846 nothrow size_t unparsedSize() const 1847 { 1848 return used_ - offset_; 1849 } 1850 1851 1852 private: 1853 @safe 1854 nothrow void initializeBuffer(in ubyte[] target, in size_t bufferSize = 8192) 1855 { 1856 const size = target.length; 1857 1858 buffer_ = new ubyte[](size > bufferSize ? size : bufferSize); 1859 used_ = size; 1860 buffer_[0..size] = target[]; 1861 } 1862 } 1863 } 1864 1865 1866 /** 1867 * This $(D Unpacker) is a $(D MessagePack) direct-conversion deserializer 1868 * 1869 * This implementation is suitable for fixed data. 1870 * 1871 * Example: 1872 * ----- 1873 * // serializedData is [10, 0.1, false] 1874 * auto unpacker = Unpacker(serializedData); 1875 * 1876 * uint n; 1877 * double d; 1878 * bool b; 1879 * 1880 * unpacker.unpackArray(n, d, b); 1881 * 1882 * // using Tuple 1883 * Tuple!(uint, double, bool) record; 1884 * unpacker.unpack(record); // record is [10, 0.1, false] 1885 * ----- 1886 * 1887 * NOTE: 1888 * Unpacker becomes template struct if Phobos supports truly IO module. 1889 */ 1890 struct Unpacker 1891 { 1892 private: 1893 static @system 1894 { 1895 alias void delegate(ref Unpacker, void*) UnpackHandler; 1896 UnpackHandler[TypeInfo] unpackHandlers; 1897 1898 public void registerHandler(T, alias Handler)() 1899 { 1900 unpackHandlers[typeid(T)] = delegate(ref Unpacker unpacker, void* obj) { 1901 Handler(unpacker, *cast(T*)obj); 1902 }; 1903 } 1904 } 1905 1906 enum Offset = 1; 1907 1908 mixin InternalBuffer; 1909 1910 bool withFieldName_; 1911 1912 1913 public: 1914 /** 1915 * Constructs a $(D Unpacker). 1916 * 1917 * Params: 1918 * target = byte buffer to deserialize 1919 * bufferSize = size limit of buffer size 1920 */ 1921 this(in ubyte[] target, in size_t bufferSize = 8192, bool withFieldName = false) 1922 { 1923 initializeBuffer(target, bufferSize); 1924 withFieldName_ = withFieldName; 1925 } 1926 1927 1928 /** 1929 * Clears states for next deserialization. 1930 */ 1931 @safe 1932 nothrow void clear() 1933 { 1934 parsed_ = 0; 1935 } 1936 1937 1938 /** 1939 * Deserializes $(D_PARAM T) object and assigns to $(D_PARAM value). 1940 * 1941 * If the argument is pointer, dereferences pointer and assigns deserialized value. 1942 * ----- 1943 * int* a; 1944 * unpacker.unpack(a) // enforce throws Exception because a is null or 1945 * // no throw if deserialized value is nil 1946 * 1947 * int b; a = &b; 1948 * unpacker.unpack(b) // b is deserialized value or 1949 * // assigns null if deserialized value is nil 1950 * ----- 1951 * 1952 * Params: 1953 * value = the reference of value to assign. 1954 * 1955 * Returns: 1956 * self, i.e. for method chaining. 1957 * 1958 * Throws: 1959 * UnpackException when doesn't read from buffer or precision loss occurs and 1960 * MessagePackException when $(D_PARAM T) type doesn't match serialized type. 1961 */ 1962 ref Unpacker unpack(T)(ref T value) if (is(Unqual!T == bool)) 1963 { 1964 canRead(Offset, 0); 1965 const header = read(); 1966 1967 switch (header) { 1968 case Format.TRUE: 1969 value = true; 1970 break; 1971 case Format.FALSE: 1972 value = false; 1973 break; 1974 default: 1975 rollback(); 1976 } 1977 1978 return this; 1979 } 1980 1981 1982 /// ditto 1983 ref Unpacker unpack(T)(ref T value) if (isUnsigned!T && !is(Unqual!T == enum)) 1984 { 1985 canRead(Offset, 0); 1986 const header = read(); 1987 1988 if (0x00 <= header && header <= 0x7f) { 1989 value = header; 1990 } else { 1991 switch (header) { 1992 case Format.UINT8: 1993 canRead(ubyte.sizeof); 1994 value = read(); 1995 break; 1996 case Format.UINT16: 1997 canRead(ushort.sizeof); 1998 auto us = load16To!ushort(read(ushort.sizeof)); 1999 if (us > T.max) 2000 rollback(ushort.sizeof); 2001 value = cast(T)us; 2002 break; 2003 case Format.UINT32: 2004 canRead(uint.sizeof); 2005 auto ui = load32To!uint(read(uint.sizeof)); 2006 if (ui > T.max) 2007 rollback(uint.sizeof); 2008 value = cast(T)ui; 2009 break; 2010 case Format.UINT64: 2011 canRead(ulong.sizeof); 2012 auto ul = load64To!ulong(read(ulong.sizeof)); 2013 if (ul > T.max) 2014 rollback(ulong.sizeof); 2015 value = cast(T)ul; 2016 break; 2017 default: 2018 rollback(); 2019 } 2020 } 2021 2022 return this; 2023 } 2024 2025 2026 /// ditto 2027 ref Unpacker unpack(T)(ref T value) if (isSigned!T && isIntegral!T && !is(Unqual!T == enum)) 2028 { 2029 canRead(Offset, 0); 2030 const header = read(); 2031 2032 if (0x00 <= header && header <= 0x7f) { 2033 value = cast(T)header; 2034 } else if (0xe0 <= header && header <= 0xff) { 2035 value = -(cast(T)-header); 2036 } else { 2037 switch (header) { 2038 case Format.UINT8: 2039 canRead(ubyte.sizeof); 2040 auto ub = read(); 2041 if (ub > T.max) 2042 rollback(ubyte.sizeof); 2043 value = cast(T)ub; 2044 break; 2045 case Format.UINT16: 2046 canRead(ushort.sizeof); 2047 auto us = load16To!ushort(read(ushort.sizeof)); 2048 if (us > T.max) 2049 rollback(ushort.sizeof); 2050 value = cast(T)us; 2051 break; 2052 case Format.UINT32: 2053 canRead(uint.sizeof); 2054 auto ui = load32To!uint(read(uint.sizeof)); 2055 if (ui > T.max) 2056 rollback(uint.sizeof); 2057 value = cast(T)ui; 2058 break; 2059 case Format.UINT64: 2060 canRead(ulong.sizeof); 2061 auto ul = load64To!ulong(read(ulong.sizeof)); 2062 if (ul > T.max) 2063 rollback(ulong.sizeof); 2064 value = cast(T)ul; 2065 break; 2066 case Format.INT8: 2067 canRead(byte.sizeof); 2068 value = cast(byte)read(); 2069 break; 2070 case Format.INT16: 2071 canRead(short.sizeof); 2072 auto s = load16To!short(read(short.sizeof)); 2073 if (s < T.min || T.max < s) 2074 rollback(short.sizeof); 2075 value = cast(T)s; 2076 break; 2077 case Format.INT32: 2078 canRead(int.sizeof); 2079 auto i = load32To!int(read(int.sizeof)); 2080 if (i < T.min || T.max < i) 2081 rollback(int.sizeof); 2082 value = cast(T)i; 2083 break; 2084 case Format.INT64: 2085 canRead(long.sizeof); 2086 auto l = load64To!long(read(long.sizeof)); 2087 if (l < T.min || T.max < l) 2088 rollback(long.sizeof); 2089 value = cast(T)l; 2090 break; 2091 default: 2092 rollback(); 2093 } 2094 } 2095 2096 return this; 2097 } 2098 2099 2100 /// ditto 2101 ref Unpacker unpack(T)(ref T value) if (isFloatingPoint!T && !is(Unqual!T == enum)) 2102 { 2103 canRead(Offset, 0); 2104 const header = read(); 2105 2106 switch (header) { 2107 case Format.FLOAT: 2108 _f temp; 2109 2110 canRead(uint.sizeof); 2111 temp.i = load32To!uint(read(uint.sizeof)); 2112 value = temp.f; 2113 break; 2114 case Format.DOUBLE: 2115 // check precision loss 2116 static if (is(Unqual!T == float)) 2117 rollback(); 2118 2119 _d temp; 2120 2121 canRead(ulong.sizeof); 2122 temp.i = load64To!ulong(read(ulong.sizeof)); 2123 value = temp.f; 2124 break; 2125 case Format.REAL: 2126 static if (!EnableReal) 2127 { 2128 rollback(); 2129 } 2130 else 2131 { 2132 // check precision loss 2133 static if (is(Unqual!T == float) || is(Unqual!T == double)) 2134 rollback(); 2135 2136 canRead(RealSize); 2137 2138 version (NonX86) 2139 { 2140 CustomFloat!80 temp; 2141 2142 const frac = load64To!ulong (read(ulong.sizeof)); 2143 const exp = load16To!ushort(read(ushort.sizeof)); 2144 2145 temp.significand = frac; 2146 temp.exponent = exp & 0x7fff; 2147 temp.sign = exp & 0x8000 ? true : false; 2148 2149 // NOTE: temp.get!real is inf on non-x86 when deserialized value is larger than double.max. 2150 value = temp.get!real; 2151 } 2152 else 2153 { 2154 _r temp; 2155 2156 temp.fraction = load64To!(typeof(temp.fraction))(read(temp.fraction.sizeof)); 2157 temp.exponent = load16To!(typeof(temp.exponent))(read(temp.exponent.sizeof)); 2158 2159 value = temp.f; 2160 } 2161 } 2162 2163 break; 2164 default: 2165 rollback(); 2166 } 2167 2168 return this; 2169 } 2170 2171 2172 /// ditto 2173 ref Unpacker unpack(T)(ref T value) if (is(Unqual!T == enum)) 2174 { 2175 OriginalType!T temp; 2176 2177 unpack(temp); 2178 2179 value = cast(T)temp; 2180 2181 return this; 2182 } 2183 2184 2185 /// ditto 2186 ref Unpacker unpack(T)(T value) if (isPointer!T) 2187 { 2188 static if (is(Unqual!T == void*)) { 2189 enforce(value !is null, "Can't deserialize void type"); 2190 unpackNil(value); 2191 } else { 2192 if (checkNil()) 2193 unpackNil(value); 2194 else 2195 enforce(value !is null, T.stringof ~ " is null pointer"); 2196 2197 unpack(mixin(AsteriskOf!T ~ "value")); 2198 } 2199 2200 return this; 2201 } 2202 2203 2204 /// ditto 2205 ref Unpacker unpack(Types...)(ref Types objects) if (Types.length > 1) 2206 { 2207 foreach (i, T; Types) 2208 unpack!(T)(objects[i]); 2209 2210 return this; 2211 } 2212 2213 2214 /** 2215 * Deserializes $(D_PARAM T) object and assigns to $(D_PARAM array). 2216 * 2217 * This is convenient method for array deserialization. 2218 * Rollback will be completely successful if you deserialize raw type((u)byte[] or string types). 2219 * But, Rollback will be one element(e.g. int) if you deserialize other types(e.g. int[], int[int]) 2220 * 2221 * No assign if the length of deserialized object is 0. 2222 * 2223 * In a static array, this method checks the length. Do rollback and throw exception 2224 * if length of $(D_PARAM array) is different from length of deserialized object. 2225 * 2226 * Params: 2227 * array = the reference of array to assign. 2228 * 2229 * Returns: 2230 * self, i.e. for method chaining. 2231 * 2232 * Throws: 2233 * UnpackException when doesn't read from buffer or precision loss occurs and 2234 * MessagePackException when $(D_PARAM T) type doesn't match serialized type. 2235 */ 2236 ref Unpacker unpack(T)(ref T array) if (isArray!T && !is(Unqual!T == enum)) 2237 { 2238 alias typeof(T.init[0]) U; 2239 2240 /* 2241 * Deserializes type-information of raw type. 2242 */ 2243 @safe 2244 size_t beginRaw() 2245 { 2246 canRead(Offset, 0); 2247 const header = read(); 2248 size_t length; 2249 2250 if (0xa0 <= header && header <= 0xbf) { 2251 length = header & 0x1f; 2252 } else { 2253 switch (header) { 2254 case Format.BIN8, Format.STR8: 2255 canRead(ubyte.sizeof); 2256 length = read(); 2257 break; 2258 case Format.BIN16, Format.RAW16: 2259 canRead(ushort.sizeof); 2260 length = load16To!size_t(read(ushort.sizeof)); 2261 break; 2262 case Format.BIN32, Format.RAW32: 2263 canRead(uint.sizeof); 2264 length = load32To!size_t(read(uint.sizeof)); 2265 break; 2266 case Format.NIL: 2267 break; 2268 default: 2269 rollback(); 2270 } 2271 } 2272 2273 return length; 2274 } 2275 2276 2277 if (checkNil()) { 2278 static if (isStaticArray!T) { 2279 onInvalidType(); 2280 } else { 2281 return unpackNil(array); 2282 } 2283 } 2284 2285 // Raw bytes 2286 static if (isByte!U || isSomeChar!U) { 2287 auto length = beginRaw(); 2288 auto offset = calculateSize!(true)(length); 2289 if (length == 0) 2290 return this; 2291 2292 static if (isStaticArray!T) { 2293 if (length != array.length) 2294 rollback(offset); 2295 } 2296 2297 canRead(length, offset + Offset); 2298 static if (isStaticArray!T) { 2299 array[] = (cast(U[])read(length))[0 .. T.length]; 2300 } else { 2301 array = cast(T)read(length); 2302 } 2303 2304 static if (isDynamicArray!T) 2305 hasRaw_ = true; 2306 } else { 2307 auto length = beginArray(); 2308 if (length == 0) 2309 return this; 2310 2311 static if (isStaticArray!T) { 2312 if (length != array.length) 2313 rollback(calculateSize(length)); 2314 } else { 2315 array.length = length; 2316 } 2317 2318 foreach (i; 0..length) 2319 unpack(array[i]); 2320 } 2321 2322 return this; 2323 } 2324 2325 2326 /// ditto 2327 ref Unpacker unpack(T)(ref T array) if (isAssociativeArray!T) 2328 { 2329 alias typeof(T.init.keys[0]) K; 2330 alias typeof(T.init.values[0]) V; 2331 2332 if (checkNil()) 2333 return unpackNil(array); 2334 2335 auto length = beginMap(); 2336 if (length == 0) 2337 return this; 2338 2339 foreach (i; 0..length) { 2340 K k; unpack(k); 2341 V v; unpack(v); 2342 array[k] = v; 2343 } 2344 2345 return this; 2346 } 2347 2348 2349 /** 2350 * Deserializes $(D_PARAM T) object and assigns to $(D_PARAM object). 2351 * 2352 * Calling $(D fromMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D fromMsgpack) method. $(D fromMsgpack) signature is: 2353 * ----- 2354 * void fromMsgpack(ref Unpacker unpacker) 2355 * ----- 2356 * Assumes $(D std.typecons.Tuple) or simple struct if $(D_KEYWORD struct) doesn't implement $(D fromMsgpack). 2357 * Checks length if $(D_PARAM T) is a $(D std.typecons.Tuple) or simple struct. 2358 * 2359 * Params: 2360 * object = the reference of object to assign. 2361 * args = the arguments to class constructor(class only). 2362 * This is used at new statement if $(D_PARAM object) is $(D_KEYWORD null). 2363 * 2364 * Returns: 2365 * self, i.e. for method chaining. 2366 */ 2367 ref Unpacker unpack(T, Args...)(ref T object, auto ref Args args) if (is(Unqual!T == class)) 2368 { 2369 if (checkNil()) 2370 return unpackNil(object); 2371 2372 if (object is null) { 2373 //static if (is(typeof(new T(args)))) 2374 static if (__traits(compiles, { new T(args); })) 2375 object = new T(args); 2376 else 2377 throw new MessagePackException("Don't know how to construct class type '" ~ Unqual!T.stringof ~ "' with argument types '" ~ Args.stringof ~ "'."); 2378 } 2379 2380 static if (hasMember!(T, "fromMsgpack")) 2381 { 2382 static if (__traits(compiles, { T t; t.fromMsgpack(this, withFieldName_); })) { 2383 object.fromMsgpack(this, withFieldName_); 2384 } else static if (__traits(compiles, { T t; t.fromMsgpack(this); })) { // backward compatible 2385 object.fromMsgpack(this); 2386 } else { 2387 static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 2388 } 2389 } else { 2390 if (auto handler = object.classinfo in unpackHandlers) { 2391 (*handler)(this, cast(void*)&object); 2392 return this; 2393 } 2394 if (T.classinfo !is object.classinfo) { 2395 throw new MessagePackException("Can't unpack derived class through reference to base class."); 2396 } 2397 2398 alias SerializingClasses!(T) Classes; 2399 2400 size_t length = withFieldName_ ? beginMap() : beginArray(); 2401 if (length == 0) 2402 return this; 2403 2404 if (length != SerializingMemberNumbers!(Classes)) 2405 rollback(calculateSize(length)); 2406 2407 if (withFieldName_) { 2408 foreach (_; 0..length) { 2409 string fieldName; 2410 unpack(fieldName); 2411 2412 foreach (Class; Classes) { 2413 Class obj = cast(Class)object; 2414 2415 foreach (i, member; obj.tupleof) { 2416 static if (isPackedField!(Class.tupleof[i])) 2417 { 2418 if (fieldName == getFieldName!(Class, i)) { 2419 unpack(obj.tupleof[i]); 2420 goto endLoop; 2421 } 2422 } 2423 } 2424 } 2425 assert(false, "Invalid field name: '" ~ fieldName~"' "); 2426 2427 endLoop: 2428 continue; 2429 } 2430 } else { 2431 foreach (Class; Classes) { 2432 Class obj = cast(Class)object; 2433 2434 foreach (i, member; obj.tupleof) { 2435 static if (isPackedField!(Class.tupleof[i])) 2436 unpack(obj.tupleof[i]); 2437 } 2438 } 2439 } 2440 } 2441 2442 return this; 2443 } 2444 2445 2446 /// ditto 2447 ref Unpacker unpack(T)(ref T object) if (is(Unqual!T == struct)) 2448 { 2449 static if (hasMember!(T, "fromMsgpack")) 2450 { 2451 static if (__traits(compiles, { T t; t.fromMsgpack(this); })) { 2452 object.fromMsgpack(this); 2453 } else { 2454 static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 2455 } 2456 } else { 2457 if (auto handler = typeid(T) in unpackHandlers) { 2458 (*handler)(this, cast(void*)&object); 2459 return this; 2460 } 2461 2462 auto length = beginArray(); 2463 if (length == 0) 2464 return this; 2465 2466 static if (isTuple!T) { 2467 if (length != T.Types.length) 2468 rollback(calculateSize(length)); 2469 2470 foreach (i, Type; T.Types) 2471 unpack(object.field[i]); 2472 } else { // simple struct 2473 //if (length != object.tupleof.length) 2474 if (length != SerializingMemberNumbers!(T)) 2475 rollback(calculateSize(length)); 2476 2477 foreach (i, member; object.tupleof) { 2478 static if (isPackedField!(T.tupleof[i])) 2479 unpack(object.tupleof[i]); 2480 } 2481 } 2482 } 2483 2484 return this; 2485 } 2486 2487 2488 /** 2489 * Deserializes the container object and assigns to each argument. 2490 * 2491 * These methods check the length. Do rollback if 2492 * the length of arguments is different from length of deserialized object. 2493 * 2494 * In unpackMap, the number of arguments must be even. 2495 * 2496 * Params: 2497 * objects = the references of object to assign. 2498 * 2499 * Returns: 2500 * self, i.e. for method chaining. 2501 */ 2502 ref Unpacker unpackArray(Types...)(ref Types objects) 2503 { 2504 auto length = beginArray(); 2505 if (length != Types.length) 2506 rollback(calculateSize(length)); 2507 2508 foreach (i, T; Types) 2509 unpack(objects[i]); 2510 // unpack(objects); // slow :( 2511 2512 return this; 2513 } 2514 2515 2516 /// ditto 2517 ref Unpacker unpackMap(Types...)(ref Types objects) 2518 { 2519 static assert(Types.length % 2 == 0, "The number of arguments must be even"); 2520 2521 auto length = beginMap(); 2522 if (length != Types.length / 2) 2523 rollback(calculateSize(length)); 2524 2525 foreach (i, T; Types) 2526 unpack(objects[i]); 2527 2528 return this; 2529 } 2530 2531 2532 /** 2533 * Deserializes the type-information of container. 2534 * 2535 * These methods don't deserialize contents. 2536 * You need to call unpack method to deserialize contents at your own risk. 2537 * ----- 2538 * // serialized data is [1, "Hi!"]; 2539 * int num; 2540 * unpacker.beginArray(2).unpack(num); // num is 1 2541 * 2542 * // other operation 2543 * 2544 * string str; 2545 * unpacker.unpack(str); // str is "Hi!" 2546 * ----- 2547 * 2548 * Returns: 2549 * the container size. 2550 */ 2551 @safe 2552 size_t beginArray() 2553 { 2554 canRead(Offset, 0); 2555 const header = read(); 2556 size_t length; 2557 2558 if (0x90 <= header && header <= 0x9f) { 2559 length = header & 0x0f; 2560 } else { 2561 switch (header) { 2562 case Format.ARRAY16: 2563 canRead(ushort.sizeof); 2564 length = load16To!size_t(read(ushort.sizeof)); 2565 break; 2566 case Format.ARRAY32: 2567 canRead(uint.sizeof); 2568 length = load32To!size_t(read(uint.sizeof)); 2569 break; 2570 case Format.NIL: 2571 break; 2572 default: 2573 rollback(); 2574 } 2575 } 2576 2577 return length; 2578 } 2579 2580 2581 /// ditto 2582 @safe 2583 size_t beginMap() 2584 { 2585 canRead(Offset, 0); 2586 const header = read(); 2587 size_t length; 2588 2589 if (0x80 <= header && header <= 0x8f) { 2590 length = header & 0x0f; 2591 } else { 2592 switch (header) { 2593 case Format.MAP16: 2594 canRead(ushort.sizeof); 2595 length = load16To!size_t(read(ushort.sizeof)); 2596 break; 2597 case Format.MAP32: 2598 canRead(uint.sizeof); 2599 length = load32To!size_t(read(uint.sizeof)); 2600 break; 2601 case Format.NIL: 2602 break; 2603 default: 2604 rollback(); 2605 } 2606 } 2607 2608 return length; 2609 } 2610 2611 2612 /** 2613 * Scans an entire buffer and converts each objects. 2614 * 2615 * This method is used for unpacking record-like objects. 2616 * 2617 * Example: 2618 * ----- 2619 * // serialized data is "[1, 2][3, 4][5, 6][...". 2620 * auto unpacker = Unpacker(serializedData); 2621 * foreach (n, d; &unpacker.scan!(int, int)) // == "foreach (int n, int d; unpacker)" 2622 * writeln(n, d); // 1st loop "1, 2", 2nd loop "3, 4"... 2623 * ----- 2624 */ 2625 int scan(Types...)(scope int delegate(ref Types) dg) 2626 { 2627 return opApply!(Types)(delegate int(ref Types objects) { return dg(objects); }); 2628 } 2629 2630 2631 /// ditto 2632 int opApply(Types...)(scope int delegate(ref Types) dg) 2633 { 2634 int result; 2635 2636 while (used_ - offset_) { 2637 auto length = beginArray(); 2638 if (length != Types.length) 2639 rollback(calculateSize(length)); 2640 2641 Types objects; 2642 foreach (i, T; Types) 2643 unpack(objects[i]); 2644 2645 result = dg(objects); 2646 if (result) 2647 return result; 2648 } 2649 2650 return result; 2651 } 2652 2653 2654 private: 2655 /* 2656 * Deserializes nil object and assigns to $(D_PARAM value). 2657 * 2658 * Params: 2659 * value = the reference of value to assign. 2660 * 2661 * Returns: 2662 * self, i.e. for method chaining. 2663 * 2664 * Throws: 2665 * UnpackException when doesn't read from buffer or precision loss occurs and 2666 * MessagePackException when $(D_PARAM T) type doesn't match serialized type. 2667 */ 2668 @safe 2669 ref Unpacker unpackNil(T)(ref T value) 2670 { 2671 canRead(Offset, 0); 2672 const header = read(); 2673 2674 if (header == Format.NIL) 2675 value = null; 2676 else 2677 rollback(); 2678 2679 return this; 2680 } 2681 2682 2683 /* 2684 * Next object is nil? 2685 * 2686 * Returns: 2687 * true if next object is nil. 2688 */ 2689 @safe 2690 bool checkNil() 2691 { 2692 canRead(Offset, 0); 2693 2694 return buffer_[offset_] == Format.NIL; 2695 } 2696 2697 2698 /* 2699 * Calculates the format size of container length. 2700 */ 2701 size_t calculateSize(bool rawType = false)(in size_t length) 2702 { 2703 static if (rawType) 2704 return length < 32 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof; 2705 else 2706 return length < 16 ? 0 : length < 65536 ? ushort.sizeof : uint.sizeof; 2707 } 2708 2709 2710 /* 2711 * Reading test. 2712 * 2713 * Params: 2714 * size = the size to read. 2715 * offset = the offset to subtract when doesn't read from buffer. 2716 * 2717 * Throws: 2718 * UnpackException when doesn't read from buffer. 2719 */ 2720 @safe 2721 void canRead(in size_t size, in size_t offset = Offset) 2722 { 2723 if (used_ - offset_ < size) { 2724 if (offset) 2725 offset_ -= offset; 2726 2727 throw new UnpackException("Insufficient buffer"); 2728 } 2729 } 2730 2731 2732 /* 2733 * Reads value from buffer and advances offset. 2734 */ 2735 @safe 2736 nothrow ubyte read() 2737 { 2738 return buffer_[offset_++]; 2739 } 2740 2741 2742 /* 2743 * Reads value from buffer and advances offset. 2744 */ 2745 @safe 2746 nothrow ubyte[] read(in size_t size) 2747 { 2748 auto result = buffer_[offset_..offset_ + size]; 2749 2750 offset_ += size; 2751 2752 return result; 2753 } 2754 2755 2756 /* 2757 * Do rollback and throws exception. 2758 */ 2759 @safe 2760 void rollback(in size_t size = 0) 2761 { 2762 offset_ -= size + Offset; 2763 onInvalidType(); 2764 } 2765 } 2766 2767 2768 /** 2769 * Register a deserialization handler for $(D_PARAM T) type 2770 * 2771 * Example: 2772 * ----- 2773 * registerUnackHandler!(Foo, fooUnackHandler); 2774 * ----- 2775 */ 2776 void registerUnpackHandler(T, alias Handler)() 2777 { 2778 Unpacker.registerHandler!(T, Handler); 2779 } 2780 2781 2782 unittest 2783 { 2784 { // unique 2785 mixin DefinePacker; 2786 2787 Tuple!(bool, bool) result; 2788 Tuple!(bool, bool) test = tuple(true, false); 2789 2790 packer.pack(test); 2791 2792 auto unpacker = Unpacker(packer.stream.data); 2793 2794 unpacker.unpack(result); 2795 assert(test == result); 2796 } 2797 { // uint * 2798 mixin DefinePacker; 2799 2800 Tuple!(ubyte, ushort, uint, ulong) result; 2801 Tuple!(ubyte, ushort, uint, ulong) test = tuple(cast(ubyte)ubyte.max, cast(ushort)ushort.max, 2802 cast(uint)uint.max, cast(ulong)ulong.max); 2803 2804 packer.pack(test); 2805 2806 auto unpacker = Unpacker(packer.stream.data); 2807 2808 unpacker.unpack(result); 2809 assert(test == result); 2810 } 2811 { // int * 2812 mixin DefinePacker; 2813 2814 Tuple!(byte, short, int, long) result; 2815 Tuple!(byte, short, int, long) test = tuple(cast(byte)byte.min, cast(short)short.min, 2816 cast(int)int.min, cast(long)long.min); 2817 2818 packer.pack(test); 2819 2820 auto unpacker = Unpacker(packer.stream.data); 2821 2822 unpacker.unpack(result); 2823 assert(test == result); 2824 } 2825 { // floating point 2826 mixin DefinePacker; 2827 2828 static if (real.sizeof == double.sizeof || !EnableReal) 2829 { 2830 Tuple!(float, double, double) result; 2831 Tuple!(float, double, double) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)double.min_normal); 2832 } 2833 else 2834 { 2835 Tuple!(float, double, real) result; 2836 Tuple!(float, double, real) test = tuple(cast(float)float.min_normal, cast(double)double.max, cast(real)real.min_normal); 2837 } 2838 2839 packer.pack(test); 2840 2841 auto unpacker = Unpacker(packer.stream.data); 2842 2843 unpacker.unpack(result); 2844 assert(test == result); 2845 } 2846 { // pointer 2847 mixin DefinePacker; 2848 2849 Tuple!(ulong, long, double) origin; 2850 Tuple!(ulong, long, double) values = tuple(ulong.max, long.min, double.min_normal); 2851 Tuple!(ulong*, long*, double*) result = tuple(&origin.field[0], &origin.field[1], &origin.field[2]); 2852 Tuple!(ulong*, long*, double*) test = tuple(&values.field[0], &values.field[1], &values.field[2]); 2853 2854 packer.pack(test); 2855 2856 auto unpacker = Unpacker(packer.stream.data); 2857 2858 unpacker.unpack(result); 2859 foreach (i, v; test.field) 2860 assert(*v == *result.field[i]); 2861 assert(origin == values); 2862 } 2863 { // enum 2864 enum : float { D = 0.5 } 2865 enum E : ulong { U = 100 } 2866 2867 mixin DefinePacker; 2868 2869 float f = D, resultF; 2870 E e = E.U, resultE; 2871 2872 packer.pack(D, e); 2873 2874 auto unpacker = Unpacker(packer.stream.data); 2875 2876 unpacker.unpack(resultF, resultE); 2877 assert(f == resultF); 2878 assert(e == resultE); 2879 } 2880 { // container 2881 mixin DefinePacker; 2882 2883 Tuple!(ulong[], double[uint], string, bool[2], char[2]) test 2884 = tuple([1UL, 2], [3U:4.0, 5:6.0, 7:8.0], "MessagePack is nice!", [true, false], "D!"); 2885 2886 packer.pack(test); 2887 2888 auto unpacker = Unpacker(packer.stream.data); 2889 Tuple!(ulong[], double[uint], string, bool[2], char[2]) result; 2890 2891 unpacker.unpack(result); 2892 assert(test == result); 2893 } 2894 { // user defined 2895 { 2896 static struct S 2897 { 2898 uint num; 2899 2900 void toMsgpack(P)(ref P p) const { p.packArray(num); } 2901 void fromMsgpack(ref Unpacker u) 2902 { 2903 assert(u.beginArray() == 1); 2904 u.unpack(num); 2905 } 2906 } 2907 2908 mixin DefinePacker; S result, test = S(uint.max); 2909 2910 packer.pack(test); 2911 2912 auto unpacker = Unpacker(packer.stream.data); 2913 unpacker.unpack(result); 2914 2915 assert(test.num == result.num); 2916 } 2917 { 2918 static class C 2919 { 2920 uint num; 2921 2922 this(uint n) { num = n; } 2923 2924 void toMsgpack(P)(ref P p) const { p.packArray(num - 1); } 2925 void fromMsgpack(ref Unpacker u) 2926 { 2927 assert(u.beginArray() == 1); 2928 u.unpack(num); 2929 } 2930 } 2931 2932 mixin DefinePacker; C result, test = new C(ushort.max); 2933 2934 packer.pack(test); 2935 2936 auto unpacker = Unpacker(packer.stream.data); 2937 unpacker.unpack(result, ushort.max); 2938 2939 assert(test.num == result.num + 1); 2940 } 2941 } 2942 { // simple struct and class 2943 { 2944 static struct Simple 2945 { 2946 uint num; 2947 @nonPacked string str; 2948 } 2949 2950 static struct Simple2 2951 { 2952 @nonPacked string str; 2953 uint num; 2954 } 2955 2956 foreach (Type; TypeTuple!(Simple, Simple2)) { 2957 mixin DefinePacker; 2958 Type result, test; 2959 test.num = uint.max; 2960 test.str = "ignored"; 2961 2962 packer.pack(test); 2963 auto unpacker = Unpacker(packer.stream.data); 2964 unpacker.unpack(result); 2965 2966 assert(test.num == result.num); 2967 assert(test.str != result.str); 2968 } 2969 } 2970 2971 static class SimpleA 2972 { 2973 bool flag = true; 2974 } 2975 2976 static class SimpleB : SimpleA 2977 { 2978 ubyte type = 100; 2979 } 2980 2981 static class SimpleC : SimpleB 2982 { 2983 uint num = uint.max; 2984 @nonPacked string str; 2985 } 2986 2987 static class SimpleC2 : SimpleB 2988 { 2989 @nonPacked string str; 2990 uint num = uint.max; 2991 } 2992 2993 { // from derived class 2994 foreach (Type; TypeTuple!(SimpleC, SimpleC2)) { 2995 mixin DefinePacker; 2996 Type result, test = new Type(); 2997 test.flag = false; 2998 test.type = 99; 2999 test.num = uint.max / 2; 3000 test.str = "ignored"; 3001 3002 packer.pack(test); 3003 auto unpacker = Unpacker(packer.stream.data); 3004 unpacker.unpack(result); 3005 3006 assert(test.flag == result.flag); 3007 assert(test.type == result.type); 3008 assert(test.num == result.num); 3009 assert(test.str != result.str); 3010 } 3011 } 3012 { // from base class 3013 mixin DefinePacker; SimpleC test = new SimpleC(); 3014 3015 packer.pack(test); 3016 3017 SimpleB result = new SimpleC(); 3018 auto unpacker = Unpacker(packer.stream.data); 3019 3020 try { 3021 unpacker.unpack(result); 3022 assert(false); 3023 } catch (Exception e) { } 3024 } 3025 { // https://github.com/msgpack/msgpack-d/issues/16 3026 static class Issue16 3027 { 3028 int i; 3029 this(int i) { this.i = i; } 3030 } 3031 3032 Issue16 c1 = new Issue16(10); 3033 3034 try { 3035 Issue16 c2 = null; 3036 unpack(pack(c1), c2); 3037 assert(false); 3038 } catch (Exception e) {} 3039 3040 Issue16 c3 = new Issue16(20); 3041 unpack(pack(c1), c3); 3042 assert(c3.i == c1.i); 3043 } 3044 } 3045 { // variadic 3046 mixin DefinePacker; 3047 3048 Tuple!(uint, long, double) test = tuple(uint.max, long.min, double.max); 3049 3050 packer.pack(test); 3051 3052 auto unpacker = Unpacker(packer.stream.data); 3053 3054 uint u; long l; double d; 3055 3056 unpacker.unpackArray(u, l, d); 3057 assert(test == tuple(u, l, d)); 3058 } 3059 { // scan / opApply 3060 ubyte[] data; 3061 mixin DefinePacker; 3062 3063 foreach (i; 0..2) 3064 packer.pack(tuple(1, 0.5, "Hi!")); 3065 3066 foreach (n, d, s; &Unpacker(packer.stream.data).scan!(int, double, string)) { 3067 assert(n == 1); 3068 assert(d == 0.5); 3069 assert(s == "Hi!"); 3070 } 3071 } 3072 } 3073 3074 3075 // Static resolution routines for Stream deserializer 3076 3077 3078 /** 3079 * $(D Value) is a $(D MessagePack) value representation 3080 * 3081 * Example: 3082 * ----- 3083 * auto unpacker = StreamingUnpacker(pack(1, 0.1L) ~ pack(true) ~ pack("foobarbaz")); 3084 * 3085 * foreach (unpacked; unpacker) { 3086 * if (unpacked.type == Value.Type.array) { 3087 * foreach (obj; unpacked) { 3088 * switch (obj.type) { 3089 * case Value.Type.unsigned: writeln(obj.as!(uint)); break; 3090 * case Value.Type.floating: writeln(obj.as!(real)); break; 3091 * defalut: 3092 * throw new Exception("Unknown type"); 3093 * } 3094 * } 3095 * } else { 3096 * if (unpacked.type == Value.Type.boolean) 3097 * writeln(unpacked.as!(bool)); 3098 * else 3099 * writeln("Message: ", unpacked.as!(string)); 3100 * } 3101 * } 3102 * ----- 3103 */ 3104 struct Value 3105 { 3106 /** 3107 * $(D MessagePack) value type 3108 */ 3109 static enum Type 3110 { 3111 nil, /// nil(null in D) 3112 boolean, /// true, false 3113 unsigned, /// positive fixnum, uint 8, uint 16, uint 32, uint 64 3114 signed, /// negative fixnum, int 8, int 16, int 32, int 64 3115 floating, /// float, double, real 3116 array, /// fix array, array 16, array 32 3117 map, /// fix map, map 16, map 32 3118 raw /// fix raw, raw 16, raw 32 3119 } 3120 3121 /** 3122 * msgpack value representation 3123 */ 3124 static union Via 3125 { 3126 bool boolean; /// corresponding to Type.boolean 3127 ulong uinteger; /// corresponding to Type.unsigned 3128 long integer; /// corresponding to Type.signed 3129 real floating; /// corresponding to Type.floating 3130 Value[] array; /// corresponding to Type.array 3131 Value[Value] map; /// corresponding to Type.map 3132 ubyte[] raw; /// corresponding to Type.raw 3133 } 3134 3135 3136 Type type; /// represents value type 3137 Via via; /// represents real value 3138 3139 3140 /** 3141 * Constructs a $(D Value) with arguments. 3142 * 3143 * Params: 3144 * value = the real content. 3145 * type = the type of value. 3146 */ 3147 @safe 3148 this(Type type) 3149 { 3150 this.type = type; 3151 } 3152 3153 @safe 3154 this(typeof(null)) 3155 { 3156 this(Type.nil); 3157 } 3158 3159 /// ditto 3160 @trusted 3161 this(bool value, Type type = Type.boolean) 3162 { 3163 this(type); 3164 via.boolean = value; 3165 } 3166 3167 3168 /// ditto 3169 @trusted 3170 this(ulong value, Type type = Type.unsigned) 3171 { 3172 this(type); 3173 via.uinteger = value; 3174 } 3175 3176 3177 /// ditto 3178 @trusted 3179 this(long value, Type type = Type.signed) 3180 { 3181 this(type); 3182 via.integer = value; 3183 } 3184 3185 3186 /// ditto 3187 @trusted 3188 this(real value, Type type = Type.floating) 3189 { 3190 this(type); 3191 via.floating = value; 3192 } 3193 3194 3195 /// ditto 3196 @trusted 3197 this(Value[] value, Type type = Type.array) 3198 { 3199 this(type); 3200 via.array = value; 3201 } 3202 3203 3204 /// ditto 3205 @trusted 3206 this(Value[Value] value, Type type = Type.map) 3207 { 3208 this(type); 3209 via.map = value; 3210 } 3211 3212 3213 /// ditto 3214 @trusted 3215 this(ubyte[] value, Type type = Type.raw) 3216 { 3217 this(type); 3218 via.raw = value; 3219 } 3220 3221 /// This is unsafe overload because using cast internally. 3222 @trusted 3223 this(string value, Type type = Type.raw) 3224 { 3225 this(type); 3226 via.raw = cast(ubyte[])value; 3227 } 3228 3229 /** 3230 * Converts value to $(D_PARAM T) type. 3231 * 3232 * Returns: 3233 * converted value. 3234 * 3235 * Throws: 3236 * MessagePackException if type is mismatched. 3237 * 3238 * NOTE: 3239 * Current implementation uses cast. 3240 */ 3241 @property @trusted 3242 T as(T)() if (is(Unqual!T == bool)) 3243 { 3244 if (type != Type.boolean) 3245 onCastError(); 3246 3247 return via.boolean; 3248 } 3249 3250 3251 /// ditto 3252 @property @trusted 3253 T as(T)() if (isIntegral!T && !is(Unqual!T == enum)) 3254 { 3255 if (type == Type.unsigned) 3256 return cast(T)via.uinteger; 3257 3258 if (type == Type.signed) 3259 return cast(T)via.integer; 3260 3261 onCastError(); 3262 3263 assert(false); 3264 } 3265 3266 3267 /// ditto 3268 @property @trusted 3269 T as(T)() if (isFloatingPoint!T && !is(Unqual!T == enum)) 3270 { 3271 if (type != Type.floating) 3272 onCastError(); 3273 3274 return cast(T)via.floating; 3275 } 3276 3277 3278 /// ditto 3279 @property @trusted 3280 T as(T)() if (is(Unqual!T == enum)) 3281 { 3282 return cast(T)as!(OriginalType!T); 3283 } 3284 3285 3286 /// ditto 3287 @property @trusted 3288 T as(T)() if (isArray!T && !is(Unqual!T == enum)) 3289 { 3290 alias typeof(T.init[0]) V; 3291 3292 if (type == Type.nil) { 3293 static if (isDynamicArray!T) { 3294 return null; 3295 } else { 3296 return T.init; 3297 } 3298 } 3299 3300 static if (isByte!V || isSomeChar!V) { 3301 if (type != Type.raw) 3302 onCastError(); 3303 3304 static if (isDynamicArray!T) { 3305 return cast(T)via.raw; 3306 } else { 3307 if (via.raw.length != T.length) 3308 onCastError(); 3309 3310 return cast(T)(via.raw[0 .. T.length]); 3311 } 3312 } else { 3313 if (type != Type.array) 3314 onCastError(); 3315 3316 V[] array; 3317 3318 foreach (elem; via.array) 3319 array ~= elem.as!(V); 3320 3321 return array; 3322 } 3323 } 3324 3325 3326 /// ditto 3327 @property @trusted 3328 T as(T)() if (isAssociativeArray!T) 3329 { 3330 alias typeof(T.init.keys[0]) K; 3331 alias typeof(T.init.values[0]) V; 3332 3333 if (type == Type.nil) 3334 return null; 3335 3336 if (type != Type.map) 3337 onCastError(); 3338 3339 V[K] map; 3340 3341 foreach (key, value; via.map) 3342 map[key.as!(K)] = value.as!(V); 3343 3344 return map; 3345 } 3346 3347 3348 /** 3349 * Converts to $(D_PARAM T) type. 3350 * 3351 * Calling $(D fromMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D fromMsgpack) method. $(D fromMsgpack) signature is: 3352 * ----- 3353 * void fromMsgpack(Value value) 3354 * ----- 3355 * This method assigns converted values to all members of T object if $(D_KEYWORD class) and $(D_KEYWORD struct) don't implement $(D fromMsgpack). 3356 * 3357 * Params: 3358 * args = arguments to class constructor(class only). 3359 * 3360 * Returns: 3361 * converted value. 3362 */ 3363 @property @trusted 3364 T as(T, Args...)(Args args) if (is(Unqual!T == class)) 3365 { 3366 if (type == Type.nil) 3367 return null; 3368 3369 T object = new T(args); 3370 3371 static if (hasMember!(T, "fromMsgpack")) 3372 { 3373 static if (__traits(compiles, { T t; t.fromMsgpack(this); })) { 3374 object.fromMsgpack(this); 3375 } else { 3376 static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 3377 } 3378 } else { 3379 alias SerializingClasses!(T) Classes; 3380 3381 if (via.array.length != SerializingMemberNumbers!(Classes)) 3382 throw new MessagePackException("The number of deserialized object member is mismatched"); 3383 3384 size_t offset; 3385 foreach (Class; Classes) { 3386 Class obj = cast(Class)object; 3387 foreach (i, member; obj.tupleof) { 3388 static if (isPackedField!(Class.tupleof[i])) 3389 obj.tupleof[i] = via.array[offset++].as!(typeof(member)); 3390 } 3391 } 3392 } 3393 3394 return object; 3395 } 3396 3397 3398 /// ditto 3399 @property @trusted 3400 T as(T)() if (is(Unqual!T == struct)) 3401 { 3402 T obj; 3403 3404 static if (hasMember!(T, "fromMsgpack")) 3405 { 3406 static if (__traits(compiles, { T t; t.fromMsgpack(this); })) { 3407 obj.fromMsgpack(this); 3408 } else { 3409 static assert(0, "Failed to invoke 'fromMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 3410 } 3411 } else { 3412 static if (isTuple!T) { 3413 if (via.array.length != T.Types.length) 3414 throw new MessagePackException("The number of deserialized Tuple element is mismatched"); 3415 3416 foreach (i, Type; T.Types) 3417 obj.field[i] = via.array[i].as!(Type); 3418 } else { // simple struct 3419 if (via.array.length != SerializingMemberNumbers!T) 3420 throw new MessagePackException("The number of deserialized struct member is mismatched"); 3421 3422 size_t offset; 3423 foreach (i, member; obj.tupleof) { 3424 static if (isPackedField!(T.tupleof[i])) 3425 obj.tupleof[i] = via.array[offset++].as!(typeof(member)); 3426 } 3427 } 3428 } 3429 3430 return obj; 3431 } 3432 3433 3434 /** 3435 * Special method called by $(D Packer). 3436 * 3437 * Params: 3438 * packer = a MessagePack serializer. 3439 */ 3440 void toMsgpack(Packer)(ref Packer packer) const 3441 { 3442 final switch (type) { 3443 case Type.nil: 3444 packer.packNil(); 3445 break; 3446 case Type.boolean: 3447 packer.pack(via.boolean); 3448 break; 3449 case Type.unsigned: 3450 packer.pack(via.uinteger); 3451 break; 3452 case Type.signed: 3453 packer.pack(via.integer); 3454 break; 3455 case Type.floating: 3456 packer.pack(via.floating); 3457 break; 3458 case Type.raw: 3459 packer.pack(via.raw); 3460 break; 3461 case Type.array: 3462 packer.beginArray(via.array.length); 3463 foreach (elem; via.array) 3464 elem.toMsgpack(packer); 3465 break; 3466 case Type.map: 3467 packer.beginMap(via.map.length); 3468 foreach (key, value; via.map) { 3469 key.toMsgpack(packer); 3470 value.toMsgpack(packer); 3471 } 3472 break; 3473 } 3474 } 3475 3476 3477 /** 3478 * Comparison for equality. @trusted for union. 3479 */ 3480 @trusted 3481 bool opEquals(Tdummy = void)(ref const Value other) const 3482 { 3483 if (type != other.type) 3484 return false; 3485 3486 final switch (other.type) { 3487 case Type.nil: return true; 3488 case Type.boolean: return opEquals(other.via.boolean); 3489 case Type.unsigned: return opEquals(other.via.uinteger); 3490 case Type.signed: return opEquals(other.via.integer); 3491 case Type.floating: return opEquals(other.via.floating); 3492 case Type.raw: return opEquals(other.via.raw); 3493 case Type.array: return opEquals(other.via.array); 3494 case Type.map: return opEquals(other.via.map); 3495 } 3496 } 3497 3498 3499 /// ditto 3500 @trusted 3501 bool opEquals(T : bool)(in T other) const 3502 { 3503 if (type != Type.boolean) 3504 return false; 3505 3506 return via.boolean == other; 3507 } 3508 3509 3510 /// ditto 3511 @trusted 3512 bool opEquals(T : ulong)(in T other) const 3513 { 3514 static if (__traits(isUnsigned, T)) { 3515 if (type != Type.unsigned) 3516 return false; 3517 3518 return via.uinteger == other; 3519 } else { 3520 if (type != Type.signed) 3521 return false; 3522 3523 return via.integer == other; 3524 } 3525 } 3526 3527 3528 /// ditto 3529 @trusted 3530 bool opEquals(T : real)(in T other) const 3531 { 3532 if (type != Type.floating) 3533 return false; 3534 3535 return via.floating == other; 3536 } 3537 3538 3539 /// ditto 3540 @trusted 3541 bool opEquals(T : const Value[])(in T other) const 3542 { 3543 if (type != Type.array) 3544 return false; 3545 3546 return via.array == other; 3547 } 3548 3549 3550 /// ditto 3551 @trusted 3552 bool opEquals(T : const Value[Value])(in T other) const 3553 { 3554 if (type != Type.map) 3555 return false; 3556 3557 // This comparison is instead of default comparison because 'via.map == other' raises "Access Violation". 3558 foreach (key, value; via.map) { 3559 if (key in other) { 3560 if (other[key] != value) 3561 return false; 3562 } else { 3563 return false; 3564 } 3565 } 3566 3567 return true; 3568 } 3569 3570 3571 /// ditto 3572 @trusted 3573 bool opEquals(T : const(ubyte)[])(in T other) const 3574 { 3575 if (type != Type.raw) 3576 return false; 3577 3578 return via.raw == other; 3579 } 3580 3581 3582 /// ditto 3583 @trusted 3584 bool opEquals(T : string)(in T other) const 3585 { 3586 if (type != Type.raw) 3587 return false; 3588 3589 return via.raw == cast(ubyte[])other; 3590 } 3591 3592 @trusted 3593 hash_t toHash() const nothrow 3594 { 3595 static hash_t getHash(T)(T* v) @safe nothrow 3596 { 3597 return typeid(T).getHash(v); 3598 } 3599 3600 final switch (type) { 3601 case Type.nil: return 0; 3602 case Type.boolean: return getHash(&via.boolean); 3603 case Type.unsigned: return getHash(&via.uinteger); 3604 case Type.signed: return getHash(&via.integer); 3605 case Type.floating: return getHash(&via.floating); 3606 case Type.raw: return getHash(&via.raw); 3607 case Type.array: 3608 hash_t ret; 3609 foreach (elem; via.array) 3610 ret ^= elem.toHash(); 3611 return ret; 3612 case Type.map: 3613 try { 3614 hash_t ret; 3615 foreach (key, value; via.map) { 3616 ret ^= key.toHash(); 3617 ret ^= value.toHash(); 3618 } 3619 return ret; 3620 } catch (Exception) assert(0); 3621 } 3622 } 3623 } 3624 3625 3626 unittest 3627 { 3628 // nil 3629 Value value = Value(null); 3630 Value other = Value(); 3631 3632 assert(value == other); 3633 assert(value.type == Value.Type.nil); 3634 3635 // boolean 3636 value = Value(true); 3637 other = Value(false); 3638 3639 assert(value != other); 3640 assert(value.type == Value.Type.boolean); 3641 assert(value.as!(bool) == true); 3642 assert(other == false); 3643 3644 try { 3645 auto b = value.as!(uint); 3646 assert(false); 3647 } catch (MessagePackException e) { } 3648 3649 // unsigned integer 3650 value = Value(10UL); 3651 other = Value(10UL); 3652 3653 assert(value == other); 3654 assert(value.type == Value.Type.unsigned); 3655 assert(value.as!(uint) == 10); 3656 assert(other == 10UL); 3657 3658 // signed integer 3659 value = Value(-20L); 3660 other = Value(-10L); 3661 3662 assert(value != other); 3663 assert(value.type == Value.Type.signed); 3664 assert(value.as!(int) == -20); 3665 assert(other == -10L); 3666 3667 // enum 3668 enum E : int { F = -20 } 3669 3670 E e = value.as!(E); 3671 assert(e == E.F); 3672 3673 // floating point 3674 value = Value(0.1e-10L); 3675 other = Value(0.1e-20L); 3676 3677 assert(value != other); 3678 assert(value.type == Value.Type.floating); 3679 assert(value.as!(real) == 0.1e-10L); 3680 assert(other == 0.1e-20L); 3681 3682 // raw 3683 value = Value(cast(ubyte[])[72, 105, 33]); 3684 other = Value(cast(ubyte[])[72, 105, 33]); 3685 3686 assert(value == other); 3687 assert(value.type == Value.Type.raw); 3688 assert(value.as!(string) == "Hi!"); 3689 assert(value.as!(ubyte[3]) == [72, 105, 33]); 3690 assert(other == cast(ubyte[])[72, 105, 33]); 3691 3692 // raw with string 3693 value = Value("hello"); 3694 other = Value("hello"); 3695 3696 assert(value == other); 3697 assert(value.type == Value.Type.raw); 3698 assert(value.as!(string) == "hello"); 3699 3700 // enum : string 3701 enum EStr : string { elem = "hello" } 3702 3703 assert(value.as!(EStr) == EStr.elem); 3704 3705 // array 3706 auto t = Value(cast(ubyte[])[72, 105, 33]); 3707 value = Value([t]); 3708 other = Value([t]); 3709 3710 assert(value == other); 3711 assert(value.type == Value.Type.array); 3712 assert(value.as!(string[]) == ["Hi!"]); 3713 assert(other == [t]); 3714 3715 // map 3716 value = Value([Value(1L):Value(2L)]); 3717 other = Value([Value(1L):Value(1L)]); 3718 3719 assert(value != other); 3720 assert(value.type == Value.Type.map); 3721 assert(value.as!(int[int]) == [1:2]); 3722 assert(other == [Value(1L):Value(1L)]); 3723 3724 value = Value(10UL); 3725 3726 // struct 3727 static struct S 3728 { 3729 ulong num; 3730 3731 void fromMsgpack(Value value) { num = value.via.uinteger; } 3732 } 3733 3734 S s = value.as!(S); 3735 assert(s.num == 10); 3736 3737 value = Value([Value(0.5f), Value(cast(ubyte[])[72, 105, 33])]); 3738 3739 // struct 3740 static struct Simple 3741 { 3742 @nonPacked int era; 3743 double num; 3744 string msg; 3745 } 3746 3747 Simple simple = value.as!(Simple); 3748 assert(simple.era == int.init); 3749 assert(simple.num == 0.5f); 3750 assert(simple.msg == "Hi!"); 3751 3752 value = Value(10UL); 3753 3754 // class 3755 static class C 3756 { 3757 ulong num; 3758 3759 void fromMsgpack(Value value) { num = value.via.uinteger; } 3760 } 3761 3762 C c = value.as!(C); 3763 assert(c.num == 10); 3764 3765 static class SimpleA 3766 { 3767 bool flag = true; 3768 } 3769 3770 static class SimpleB : SimpleA 3771 { 3772 ubyte type = 100; 3773 } 3774 3775 static class SimpleC : SimpleB 3776 { 3777 @nonPacked string str; 3778 uint num = uint.max; 3779 } 3780 3781 value = Value([Value(false), Value(99UL), Value(cast(ulong)(uint.max / 2u))]); 3782 3783 SimpleC sc = value.as!(SimpleC); 3784 assert(sc.flag == false); 3785 assert(sc.type == 99); 3786 assert(sc.num == uint.max / 2); 3787 assert(sc.str.empty); 3788 3789 // std.typecons.Tuple 3790 value = Value([Value(true), Value(1UL), Value(cast(ubyte[])"Hi!")]); 3791 3792 auto tuple = value.as!(Tuple!(bool, uint, string)); 3793 assert(tuple.field[0] == true); 3794 assert(tuple.field[1] == 1u); 3795 assert(tuple.field[2] == "Hi!"); 3796 3797 /* 3798 * non-MessagePackable object is stopped by static assert 3799 * static struct NonMessagePackable {} 3800 * auto nonMessagePackable = value.as!(NonMessagePackable); 3801 */ 3802 } 3803 3804 3805 /** 3806 * $(D Unpacked) is a $(D Range) wrapper for stream deserialization result 3807 */ 3808 struct Unpacked 3809 { 3810 Value value; /// deserialized value 3811 3812 alias value this; 3813 3814 3815 /** 3816 * Constructs a $(D Unpacked) with argument. 3817 * 3818 * Params: 3819 * value = a deserialized value. 3820 */ 3821 @safe 3822 this(ref Value value) 3823 { 3824 this.value = value; 3825 } 3826 3827 3828 /** 3829 * InputRange primitive operation that checks iteration state. 3830 * 3831 * Returns: 3832 * true if there are no more elements to be iterated. 3833 */ 3834 @property @trusted 3835 nothrow bool empty() const // std.array.empty isn't nothrow function 3836 { 3837 return (value.type == Value.Type.array) && !value.via.array.length; 3838 } 3839 3840 3841 /** 3842 * Range primitive operation that returns the length of the range. 3843 * 3844 * Returns: 3845 * the number of values. 3846 */ 3847 @property @trusted 3848 size_t length() 3849 { 3850 return value.via.array.length; 3851 } 3852 3853 3854 /** 3855 * InputRange primitive operation that returns the currently iterated element. 3856 * 3857 * Returns: 3858 * the deserialized $(D Value). 3859 */ 3860 @property @trusted 3861 ref Value front() 3862 { 3863 return value.via.array.front; 3864 } 3865 3866 3867 /** 3868 * InputRange primitive operation that advances the range to its next element. 3869 */ 3870 @trusted 3871 void popFront() 3872 { 3873 value.via.array.popFront(); 3874 } 3875 3876 /** 3877 * RandomAccessRange primitive operation. 3878 * 3879 * Returns: 3880 * the deserialized $(D Value) at $(D_PARAM n) position. 3881 */ 3882 @trusted 3883 nothrow ref Value opIndex(size_t n) 3884 { 3885 return value.via.array[n]; 3886 } 3887 3888 /** 3889 * Returns a slice of the range. 3890 * 3891 * Paramas: 3892 * from = the start point of slicing. 3893 * to = the end point of slicing. 3894 * 3895 * Returns: 3896 * the slice of Values. 3897 */ 3898 @trusted 3899 Value[] opSlice(size_t from, size_t to) 3900 { 3901 return value.via.array[from..to]; 3902 } 3903 3904 /** 3905 * Range primitive operation that returns the snapshot. 3906 * 3907 * Returns: 3908 * the snapshot of this Value. 3909 */ 3910 @property @safe 3911 Unpacked save() 3912 { 3913 return Unpacked(value); 3914 } 3915 } 3916 3917 3918 unittest 3919 { 3920 static assert(isForwardRange!Unpacked); 3921 static assert(hasLength!Unpacked); 3922 } 3923 3924 3925 /** 3926 * This $(D StreamingUnpacker) is a $(D MessagePack) streaming deserializer 3927 * 3928 * This implementation enables you to load multiple objects from a stream(like network). 3929 * 3930 * Example: 3931 * ----- 3932 * ... 3933 * auto unpacker = StreamingUnpacker(serializedData); 3934 * ... 3935 * 3936 * // appends new data to buffer if pre execute() call didn't finish deserialization. 3937 * unpacker.feed(newSerializedData); 3938 * 3939 * while (unpacker.execute()) { 3940 * foreach (obj; unpacker.purge()) { 3941 * // do stuff (obj is a Value) 3942 * } 3943 * } 3944 * 3945 * if (unpacker.size) 3946 * throw new Exception("Message is too large"); 3947 * ----- 3948 */ 3949 struct StreamingUnpacker 3950 { 3951 private: 3952 /* 3953 * Context state of deserialization 3954 */ 3955 enum State 3956 { 3957 HEADER = 0x00, 3958 3959 BIN8 = 0x04, 3960 BIN16, 3961 BIN32, 3962 3963 // Floating point, Unsigned, Signed interger (== header & 0x03) 3964 FLOAT = 0x0a, 3965 DOUBLE, 3966 UINT8, 3967 UINT16, 3968 UINT32, 3969 UINT64, 3970 INT8, 3971 INT16, 3972 INT32, 3973 INT64, 3974 3975 // Container (== header & 0x01) 3976 STR8 = 0x19, 3977 RAW16 = 0x1a, 3978 RAW32, 3979 ARRAY16, 3980 ARRAY36, 3981 MAP16, 3982 MAP32, 3983 RAW, 3984 3985 // D-specific type 3986 REAL 3987 } 3988 3989 3990 /* 3991 * Element type of container 3992 */ 3993 enum ContainerElement 3994 { 3995 ARRAY_ITEM, 3996 MAP_KEY, 3997 MAP_VALUE 3998 } 3999 4000 4001 /* 4002 * Internal stack context 4003 */ 4004 static struct Context 4005 { 4006 static struct Container 4007 { 4008 ContainerElement type; // value container type 4009 Value value; // current value 4010 Value key; // for map value 4011 size_t count; // container length 4012 } 4013 4014 State state; // current state of deserialization 4015 size_t trail; // current deserializing size 4016 size_t top; // current index of stack 4017 Container[] stack; // storing values 4018 } 4019 4020 Context context_; // stack environment for streaming deserialization 4021 4022 mixin InternalBuffer; 4023 4024 4025 public: 4026 /** 4027 * Constructs a $(D StreamingUnpacker). 4028 * 4029 * Params: 4030 * target = byte buffer to deserialize 4031 * bufferSize = size limit of buffer size 4032 */ 4033 @safe 4034 this(in ubyte[] target, in size_t bufferSize = 8192) 4035 { 4036 initializeBuffer(target, bufferSize); 4037 initializeContext(); 4038 } 4039 4040 4041 /** 4042 * Forwards to deserialized object. 4043 * 4044 * Returns: 4045 * the $(D Unpacked) object contains deserialized value. 4046 */ 4047 @property @safe 4048 Unpacked unpacked() 4049 { 4050 return Unpacked(context_.stack[0].value); 4051 } 4052 4053 4054 /** 4055 * Clears some states for next deserialization. 4056 */ 4057 @safe 4058 nothrow void clear() 4059 { 4060 initializeContext(); 4061 4062 parsed_ = 0; 4063 } 4064 4065 4066 /** 4067 * Convenient method for unpacking and clearing states. 4068 * 4069 * Example: 4070 * ----- 4071 * foreach (obj; unpacker.purge()) { 4072 * // do stuff 4073 * } 4074 * ----- 4075 * is equivalent to 4076 * ----- 4077 * foreach (obj; unpacker.unpacked) { 4078 * // do stuff 4079 * } 4080 * unpacker.clear(); 4081 * ----- 4082 * 4083 * Returns: 4084 * the $(D Unpacked) object contains deserialized value. 4085 */ 4086 @safe 4087 Unpacked purge() 4088 { 4089 auto result = Unpacked(context_.stack[0].value); 4090 4091 clear(); 4092 4093 return result; 4094 } 4095 4096 4097 /** 4098 * Executes deserialization. 4099 * 4100 * Returns: 4101 * true if deserialization has been completed, otherwise false. 4102 * 4103 * Throws: 4104 * $(D UnpackException) when parse error occurs. 4105 */ 4106 bool execute() 4107 { 4108 /* 4109 * Current implementation is very dirty(goto! goto!! goto!!!). 4110 * This Complexity for performance(avoid function call). 4111 */ 4112 4113 bool ret; 4114 size_t cur = offset_; 4115 Value obj; 4116 4117 // restores before state 4118 auto state = context_.state; 4119 auto trail = context_.trail; 4120 auto top = context_.top; 4121 auto stack = &context_.stack; 4122 4123 /* 4124 * Helper for container deserialization 4125 */ 4126 bool startContainer(string Type)(ContainerElement type, size_t length) 4127 { 4128 mixin("callback" ~ Type ~ "((*stack)[top].value, length);"); 4129 4130 if (length == 0) 4131 return false; 4132 4133 (*stack)[top].type = type; 4134 (*stack)[top].count = length; 4135 (*stack).length = ++top + 1; 4136 4137 return true; 4138 } 4139 4140 // non-deserialized data is nothing 4141 if (used_ - offset_ == 0) 4142 goto Labort; 4143 4144 do { 4145 Lstart: 4146 if (state == State.HEADER) { 4147 const header = buffer_[cur]; 4148 4149 if (0x00 <= header && header <= 0x7f) { // positive 4150 callbackUInt(obj, header); 4151 goto Lpush; 4152 } else if (0xe0 <= header && header <= 0xff) { // negative 4153 callbackInt(obj, cast(byte)header); 4154 goto Lpush; 4155 } else if (0xa0 <= header && header <= 0xbf) { // fix raw 4156 trail = header & 0x1f; 4157 state = State.RAW; 4158 cur++; 4159 continue; 4160 } else if (0x90 <= header && header <= 0x9f) { // fix array 4161 if (!startContainer!"Array"(ContainerElement.ARRAY_ITEM, header & 0x0f)) 4162 goto Lpush; 4163 cur++; 4164 continue; 4165 } else if (0x80 <= header && header <= 0x8f) { // fix map 4166 if (!startContainer!"Map"(ContainerElement.MAP_KEY, header & 0x0f)) 4167 goto Lpush; 4168 cur++; 4169 continue; 4170 } else { 4171 switch (header) { 4172 case Format.UINT8, Format.UINT16, Format.UINT32, Format.UINT64, 4173 Format.INT8, Format.INT16, Format.INT32, Format.INT64, 4174 Format.FLOAT, Format.DOUBLE: 4175 trail = 1 << (header & 0x03); // computes object size 4176 state = cast(State)(header & 0x1f); 4177 break; 4178 case Format.REAL: 4179 trail = RealSize; 4180 state = State.REAL; 4181 break; 4182 case Format.ARRAY16, Format.ARRAY32, 4183 Format.MAP16, Format.MAP32: 4184 trail = 2 << (header & 0x01); // computes container size 4185 state = cast(State)(header & 0x1f); 4186 break; 4187 // raw will become str format in new spec 4188 case Format.STR8: 4189 case Format.RAW16: // will be STR16 4190 case Format.RAW32: // will be STR32 4191 trail = 1 << ((header & 0x03) - 1); // computes container size 4192 state = cast(State)(header & 0x1f); 4193 break; 4194 case Format.BIN8, Format.BIN16, Format.BIN32: 4195 trail = 1 << (header & 0x03); // computes container size 4196 state = cast(State)(header & 0x1f); 4197 break; 4198 case Format.NIL: 4199 callbackNil(obj); 4200 goto Lpush; 4201 case Format.TRUE: 4202 callbackBool(obj, true); 4203 goto Lpush; 4204 case Format.FALSE: 4205 callbackBool(obj, false); 4206 goto Lpush; 4207 default: 4208 throw new UnpackException("Unknown type"); 4209 } 4210 4211 cur++; 4212 goto Lstart; 4213 } 4214 } else { 4215 // data lack for deserialization 4216 if (used_ - cur < trail) 4217 goto Labort; 4218 4219 const base = cur; cur += trail - 1; // fix current position 4220 4221 final switch (state) { 4222 case State.FLOAT: 4223 _f temp; 4224 4225 temp.i = load32To!uint(buffer_[base..base + trail]); 4226 callbackFloat(obj, temp.f); 4227 goto Lpush; 4228 case State.DOUBLE: 4229 _d temp; 4230 4231 temp.i = load64To!ulong(buffer_[base..base + trail]); 4232 callbackFloat(obj, temp.f); 4233 goto Lpush; 4234 case State.REAL: 4235 const expb = base + ulong.sizeof; 4236 4237 version (NonX86) 4238 { 4239 CustomFloat!80 temp; 4240 4241 const frac = load64To!ulong (buffer_[base..expb]); 4242 const exp = load16To!ushort(buffer_[expb..expb + ushort.sizeof]); 4243 4244 temp.significand = frac; 4245 temp.exponent = exp & 0x7fff; 4246 temp.sign = exp & 0x8000 ? true : false; 4247 4248 // NOTE: temp.get!real is inf on non-x86 when deserialized value is larger than double.max. 4249 callbackFloat(obj, temp.get!real); 4250 } 4251 else 4252 { 4253 _r temp; 4254 4255 temp.fraction = load64To!(typeof(temp.fraction))(buffer_[base..expb]); 4256 temp.exponent = load16To!(typeof(temp.exponent))(buffer_[expb..expb + temp.exponent.sizeof]); 4257 4258 callbackFloat(obj, temp.f); 4259 } 4260 4261 goto Lpush; 4262 case State.UINT8: 4263 callbackUInt(obj, buffer_[base]); 4264 goto Lpush; 4265 case State.UINT16: 4266 callbackUInt(obj, load16To!ulong(buffer_[base..base + trail])); 4267 goto Lpush; 4268 case State.UINT32: 4269 callbackUInt(obj, load32To!ulong(buffer_[base..base + trail])); 4270 goto Lpush; 4271 case State.UINT64: 4272 callbackUInt(obj, load64To!ulong(buffer_[base..base + trail])); 4273 goto Lpush; 4274 case State.INT8: 4275 callbackInt(obj, cast(byte)buffer_[base]); 4276 goto Lpush; 4277 case State.INT16: 4278 callbackInt(obj, load16To!long(buffer_[base..base + trail])); 4279 goto Lpush; 4280 case State.INT32: 4281 callbackInt(obj, load32To!long(buffer_[base..base + trail])); 4282 goto Lpush; 4283 case State.INT64: 4284 callbackInt(obj, load64To!long(buffer_[base..base + trail])); 4285 goto Lpush; 4286 case State.RAW: Lraw: 4287 hasRaw_ = true; 4288 callbackRaw(obj, buffer_[base..base + trail]); 4289 goto Lpush; 4290 case State.STR8, State.BIN8: 4291 trail = buffer_[base]; 4292 if (trail == 0) 4293 goto Lraw; 4294 state = State.RAW; 4295 cur++; 4296 goto Lstart; 4297 case State.RAW16, State.BIN16: 4298 trail = load16To!size_t(buffer_[base..base + trail]); 4299 if (trail == 0) 4300 goto Lraw; 4301 state = State.RAW; 4302 cur++; 4303 goto Lstart; 4304 case State.RAW32, State.BIN32: 4305 trail = load32To!size_t(buffer_[base..base + trail]); 4306 if (trail == 0) 4307 goto Lraw; 4308 state = State.RAW; 4309 cur++; 4310 goto Lstart; 4311 case State.ARRAY16: 4312 if (!startContainer!"Array"(ContainerElement.ARRAY_ITEM, 4313 load16To!size_t(buffer_[base..base + trail]))) 4314 goto Lpush; 4315 state = State.HEADER; 4316 cur++; 4317 continue; 4318 case State.ARRAY36: 4319 if (!startContainer!"Array"(ContainerElement.ARRAY_ITEM, 4320 load32To!size_t(buffer_[base..base + trail]))) 4321 goto Lpush; 4322 state = State.HEADER; 4323 cur++; 4324 continue; 4325 case State.MAP16: 4326 if (!startContainer!"Map"(ContainerElement.MAP_KEY, 4327 load16To!size_t(buffer_[base..base + trail]))) 4328 goto Lpush; 4329 state = State.HEADER; 4330 cur++; 4331 continue; 4332 case State.MAP32: 4333 if (!startContainer!"Map"(ContainerElement.MAP_KEY, 4334 load32To!size_t(buffer_[base..base + trail]))) 4335 goto Lpush; 4336 state = State.HEADER; 4337 cur++; 4338 continue; 4339 case State.HEADER: 4340 break; 4341 } 4342 } 4343 4344 Lpush: 4345 if (top == 0) 4346 goto Lfinish; 4347 4348 auto container = &(*stack)[top - 1]; 4349 4350 final switch (container.type) { 4351 case ContainerElement.ARRAY_ITEM: 4352 container.value.via.array ~= obj; 4353 if (--container.count == 0) { 4354 obj = container.value; 4355 top--; 4356 goto Lpush; 4357 } 4358 break; 4359 case ContainerElement.MAP_KEY: 4360 container.key = obj; 4361 container.type = ContainerElement.MAP_VALUE; 4362 break; 4363 case ContainerElement.MAP_VALUE: 4364 container.value.via.map[container.key] = obj; 4365 if (--container.count == 0) { 4366 obj = container.value; 4367 top--; 4368 goto Lpush; 4369 } 4370 container.type = ContainerElement.MAP_KEY; 4371 } 4372 4373 state = State.HEADER; 4374 cur++; 4375 } while (cur < used_); 4376 4377 goto Labort; 4378 4379 Lfinish: 4380 (*stack)[0].value = obj; 4381 ret = true; 4382 cur++; 4383 goto Lend; 4384 4385 Labort: 4386 ret = false; 4387 4388 Lend: 4389 context_.state = state; 4390 context_.trail = trail; 4391 context_.top = top; 4392 parsed_ += cur - offset_; 4393 offset_ = cur; 4394 4395 return ret; 4396 } 4397 4398 4399 /** 4400 * supports foreach. One loop provides $(D Unpacked) object contains execute() result. 4401 * This is convenient in case that $(D MessagePack) values are continuous. 4402 */ 4403 int opApply(scope int delegate(ref Unpacked) dg) 4404 { 4405 int result; 4406 4407 while (execute()) { 4408 auto unpackedResult = Unpacked(context_.stack[0].value); 4409 result = dg(unpackedResult); 4410 if (result) 4411 break; 4412 4413 clear(); 4414 } 4415 4416 return result; 4417 } 4418 4419 4420 private: 4421 /* 4422 * initializes internal stack environment. 4423 */ 4424 @safe 4425 nothrow void initializeContext() 4426 { 4427 context_.state = State.HEADER; 4428 context_.trail = 0; 4429 context_.top = 0; 4430 context_.stack.length = 1; 4431 } 4432 } 4433 4434 4435 unittest 4436 { 4437 // serialize 4438 mixin DefinePacker; 4439 4440 packer.packArray(null, true, 1, -2, "Hi!", [1], [1:1], double.max); 4441 4442 // deserialize 4443 auto unpacker = StreamingUnpacker(packer.stream.data); unpacker.execute(); 4444 auto unpacked = unpacker.purge(); 4445 4446 // Range test 4447 foreach (unused; 0..2) { 4448 uint i; 4449 4450 foreach (obj; unpacked) 4451 i++; 4452 4453 assert(i == unpacked.via.array.length); 4454 } 4455 4456 auto result = unpacked.via.array; 4457 4458 assert(result[0].type == Value.Type.nil); 4459 assert(result[1].via.boolean == true); 4460 assert(result[2].via.uinteger == 1); 4461 assert(result[3].via.integer == -2); 4462 assert(result[4].via.raw == [72, 105, 33]); 4463 assert(result[5].as!(int[]) == [1]); 4464 assert(result[6].as!(int[int]) == [1:1]); 4465 assert(result[7].as!(double) == double.max); 4466 } 4467 4468 4469 private: 4470 4471 4472 /* 4473 * Sets value type and value. 4474 * 4475 * Params: 4476 * value = the value to set 4477 * number = the content to set 4478 */ 4479 @trusted 4480 void callbackUInt(ref Value value, ulong number) 4481 { 4482 value.type = Value.Type.unsigned; 4483 value.via.uinteger = number; 4484 } 4485 4486 4487 /// ditto 4488 @trusted 4489 void callbackInt(ref Value value, long number) 4490 { 4491 value.type = Value.Type.signed; 4492 value.via.integer = number; 4493 } 4494 4495 4496 /// ditto 4497 @trusted 4498 void callbackFloat(ref Value value, real number) 4499 { 4500 value.type = Value.Type.floating; 4501 value.via.floating = number; 4502 } 4503 4504 4505 /// ditto 4506 @trusted 4507 void callbackRaw(ref Value value, ubyte[] raw) 4508 { 4509 value.type = Value.Type.raw; 4510 value.via.raw = raw; 4511 } 4512 4513 4514 /// ditto 4515 @trusted 4516 void callbackArray(ref Value value, size_t length) 4517 { 4518 value.type = Value.Type.array; 4519 value.via.array.length = 0; 4520 value.via.array.reserve(length); 4521 } 4522 4523 4524 /// ditto 4525 @trusted 4526 void callbackMap(ref Value value, lazy size_t length) 4527 { 4528 value.type = Value.Type.map; 4529 value.via.map = null; // clears previous result avoiding 'Access Violation' 4530 } 4531 4532 4533 /// ditto 4534 @safe 4535 void callbackNil(ref Value value) 4536 { 4537 value.type = Value.Type.nil; 4538 } 4539 4540 4541 /// ditto 4542 @trusted 4543 void callbackBool(ref Value value, bool boolean) 4544 { 4545 value.type = Value.Type.boolean; 4546 value.via.boolean = boolean; 4547 } 4548 4549 4550 unittest 4551 { 4552 Value value; 4553 4554 // Unsigned integer 4555 callbackUInt(value, uint.max); 4556 assert(value.type == Value.Type.unsigned); 4557 assert(value.via.uinteger == uint.max); 4558 4559 // Signed integer 4560 callbackInt(value, int.min); 4561 assert(value.type == Value.Type.signed); 4562 assert(value.via.integer == int.min); 4563 4564 // Floating point 4565 callbackFloat(value, real.max); 4566 assert(value.type == Value.Type.floating); 4567 assert(value.via.floating == real.max); 4568 4569 // Raw 4570 callbackRaw(value, cast(ubyte[])[1]); 4571 assert(value.type == Value.Type.raw); 4572 assert(value.via.raw == cast(ubyte[])[1]); 4573 4574 // Array 4575 Value[] array; array.reserve(16); 4576 4577 callbackArray(value, 16); 4578 assert(value.type == Value.Type.array); 4579 assert(value.via.array.capacity == array.capacity); 4580 4581 // Map 4582 Value[Value] map; 4583 4584 callbackMap(value, 16); 4585 assert(value.type == Value.Type.map); 4586 assert(value.via.map == null); 4587 4588 // NIL 4589 callbackNil(value); 4590 assert(value.type == Value.Type.nil); 4591 4592 // Bool 4593 callbackBool(value, true); 4594 assert(value.type == Value.Type.boolean); 4595 assert(value.via.boolean == true); 4596 } 4597 4598 4599 private: 4600 4601 4602 /* 4603 * A callback for type-mismatched error in cast conversion. 4604 */ 4605 @safe 4606 pure void onCastError() 4607 { 4608 throw new MessagePackException("Attempt to cast with another type"); 4609 } 4610 4611 4612 /* 4613 * A callback for type-mismatched error in deserialization process. 4614 */ 4615 @safe 4616 pure void onInvalidType() 4617 { 4618 throw new MessagePackException("Attempt to unpack with non-compatible type"); 4619 } 4620 4621 4622 public: 4623 4624 4625 /* 4626 * Handy helper for creating MessagePackable object. 4627 * 4628 * toMsgpack / fromMsgpack are special methods for serialization / deserialization. 4629 * This template provides those methods to struct/class. 4630 * 4631 * Example: 4632 * ----- 4633 * struct S 4634 * { 4635 * int num; string str; 4636 * 4637 * // http://d.puremagic.com/issues/show_bug.cgi?id = 1099 4638 * mixin MessagePackable; // all members 4639 * // mixin MessagePackable!("num"); // num only 4640 * } 4641 * ----- 4642 * 4643 * Defines those methods manually if you treat complex data-structure. 4644 */ 4645 mixin template MessagePackable(Members...) 4646 { 4647 static if (Members.length == 0) { 4648 /** 4649 * Serializes members using $(D_PARAM packer). 4650 * 4651 * Params: 4652 * packer = the serializer to pack. 4653 */ 4654 void toMsgpack(Packer)(ref Packer packer, bool withFieldName = false) const 4655 { 4656 if (withFieldName) { 4657 packer.beginMap(this.tupleof.length); 4658 foreach (i, member; this.tupleof) { 4659 packer.pack(getFieldName!(typeof(this), i)); 4660 packer.pack(member); 4661 } 4662 } else { 4663 packer.beginArray(this.tupleof.length); 4664 foreach (member; this.tupleof) 4665 packer.pack(member); 4666 } 4667 } 4668 4669 4670 /** 4671 * Deserializes $(D MessagePack) object to members using Value. 4672 * 4673 * Params: 4674 * value = the MessagePack value to unpack. 4675 * 4676 * Throws: 4677 * MessagePackException if $(D_PARAM value) is not an Array type. 4678 */ 4679 void fromMsgpack(Value value) 4680 { 4681 // enables if std.contracts.enforce is moved to object_.d 4682 // enforceEx!MessagePackException(value.type == Value.Type.array, "Value must be Array type"); 4683 if (value.type != Value.Type.array) 4684 throw new MessagePackException("Value must be an Array type"); 4685 if (value.via.array.length != this.tupleof.length) 4686 throw new MessagePackException("The size of deserialized value is mismatched"); 4687 4688 foreach (i, member; this.tupleof) 4689 this.tupleof[i] = value.via.array[i].as!(typeof(member)); 4690 } 4691 4692 4693 /** 4694 * Deserializes $(D MessagePack) object to members using direct-conversion deserializer. 4695 * 4696 * Params: 4697 * value = the reference to direct-conversion deserializer. 4698 * 4699 * Throws: 4700 * MessagePackException if the size of deserialized value is mismatched. 4701 */ 4702 void fromMsgpack(ref Unpacker unpacker) 4703 { 4704 auto length = unpacker.beginArray(); 4705 if (length != this.tupleof.length) 4706 throw new MessagePackException("The size of deserialized value is mismatched"); 4707 4708 foreach (i, member; this.tupleof) 4709 unpacker.unpack(this.tupleof[i]); 4710 } 4711 } else { 4712 /** 4713 * Member selecting version of toMsgpack. 4714 */ 4715 void toMsgpack(Packer)(ref Packer packer, bool withFieldName = false) const 4716 { 4717 if (withFieldName) { 4718 packer.beginMap(Members.length); 4719 foreach (member; Members) { 4720 packer.pack(member); 4721 packer.pack(mixin(member)); 4722 } 4723 } else { 4724 packer.beginArray(Members.length); 4725 foreach (member; Members) 4726 packer.pack(mixin(member)); 4727 } 4728 } 4729 4730 4731 /** 4732 * Member selecting version of fromMsgpack for Value. 4733 */ 4734 void fromMsgpack(Value value) 4735 { 4736 if (value.type != Value.Type.array) 4737 throw new MessagePackException("Value must be an Array type"); 4738 if (value.via.array.length != Members.length) 4739 throw new MessagePackException("The size of deserialized value is mismatched"); 4740 4741 foreach (i, member; Members) 4742 mixin(member ~ "= value.via.array[i].as!(typeof(" ~ member ~ "));"); 4743 } 4744 4745 4746 /** 4747 * Member selecting version of fromMsgpack for direct-converion deserializer. 4748 */ 4749 void fromMsgpack(ref Unpacker unpacker) 4750 { 4751 auto length = unpacker.beginArray(); 4752 if (length != Members.length) 4753 throw new MessagePackException("The size of deserialized value is mismatched"); 4754 4755 foreach (member; Members) 4756 unpacker.unpack(mixin(member)); 4757 } 4758 } 4759 } 4760 4761 4762 unittest 4763 { 4764 { // all members 4765 /* 4766 * Comment out because "src/msgpack.d(4048): Error: struct msgpack.__unittest16.S no size yet for forward reference" occurs 4767 */ 4768 static struct S 4769 { 4770 uint num; string str; 4771 mixin MessagePackable; 4772 } 4773 4774 mixin DefinePacker; 4775 4776 S orig = S(10, "Hi!"); orig.toMsgpack(packer); 4777 4778 { // stream 4779 auto unpacker = StreamingUnpacker(packer.stream.data); unpacker.execute(); 4780 4781 S result; result.fromMsgpack(unpacker.unpacked); 4782 4783 assert(result.num == 10); 4784 assert(result.str == "Hi!"); 4785 } 4786 { // direct conversion 4787 auto unpacker = Unpacker(packer.stream.data); 4788 4789 S result; unpacker.unpack(result); 4790 4791 assert(result.num == 10); 4792 assert(result.str == "Hi!"); 4793 } 4794 } 4795 { // member select 4796 static class C 4797 { 4798 uint num; string str; 4799 4800 this() {} 4801 this(uint n, string s) { num = n; str = s; } 4802 4803 mixin MessagePackable!("num"); 4804 } 4805 4806 mixin DefinePacker; 4807 4808 C orig = new C(10, "Hi!"); orig.toMsgpack(packer); 4809 4810 { // stream 4811 auto unpacker = StreamingUnpacker(packer.stream.data); unpacker.execute(); 4812 4813 C result = new C; result.fromMsgpack(unpacker.unpacked); 4814 4815 assert(result.num == 10); 4816 } 4817 { // direct conversion 4818 auto unpacker = Unpacker(packer.stream.data); 4819 4820 C result; unpacker.unpack(result); 4821 4822 assert(result.num == 10); 4823 } 4824 } 4825 } 4826 4827 4828 private: 4829 4830 4831 // Common and system dependent operations 4832 4833 4834 /* 4835 * MessagePack type-information format 4836 * 4837 * See_Also: 4838 * $(LINK2 http://redmine.msgpack.org/projects/msgpack/wiki/FormatSpec, MessagePack Specificaton) 4839 */ 4840 enum Format : ubyte 4841 { 4842 // unsinged integer 4843 UINT8 = 0xcc, // ubyte 4844 UINT16 = 0xcd, // ushort 4845 UINT32 = 0xce, // uint 4846 UINT64 = 0xcf, // ulong 4847 4848 // signed integer 4849 INT8 = 0xd0, // byte 4850 INT16 = 0xd1, // short 4851 INT32 = 0xd2, // int 4852 INT64 = 0xd3, // long 4853 4854 // floating point 4855 FLOAT = 0xca, // float 4856 DOUBLE = 0xcb, // double 4857 4858 // raw byte 4859 RAW = 0xa0, 4860 RAW16 = 0xda, 4861 RAW32 = 0xdb, 4862 4863 // bin type 4864 BIN8 = 0xc4, 4865 BIN16 = 0xc5, 4866 BIN32 = 0xc6, 4867 4868 // str type 4869 STR8 = 0xd9, 4870 //STR16 = 0xda, 4871 //STR32 = 0xdb, 4872 4873 // array 4874 ARRAY = 0x90, 4875 ARRAY16 = 0xdc, 4876 ARRAY32 = 0xdd, 4877 4878 // map 4879 MAP = 0x80, 4880 MAP16 = 0xde, 4881 MAP32 = 0xdf, 4882 4883 // other 4884 NIL = 0xc0, // null 4885 TRUE = 0xc3, 4886 FALSE = 0xc2, 4887 4888 // real (This format is D only!) 4889 REAL = 0xd4 4890 } 4891 4892 4893 /* 4894 * For float type serialization / deserialization 4895 */ 4896 union _f 4897 { 4898 float f; 4899 uint i; 4900 } 4901 4902 4903 /* 4904 * For double type serialization / deserialization 4905 */ 4906 union _d 4907 { 4908 double f; 4909 ulong i; 4910 } 4911 4912 4913 /* 4914 * For real type serialization / deserialization 4915 * 4916 * 80-bit real is padded to 12 bytes(Linux) and 16 bytes(Mac). 4917 * http://lists.puremagic.com/pipermail/digitalmars-d/2010-June/077394.html 4918 */ 4919 union _r 4920 { 4921 real f; 4922 4923 struct 4924 { 4925 ulong fraction; 4926 ushort exponent; // includes sign 4927 } 4928 } 4929 4930 enum RealSize = 10; // Real size is 80bit 4931 4932 4933 /* 4934 * Detects whether $(D_PARAM T) is a built-in byte type. 4935 */ 4936 template isByte(T) 4937 { 4938 enum isByte = staticIndexOf!(Unqual!T, byte, ubyte) >= 0; 4939 } 4940 4941 4942 unittest 4943 { 4944 static assert(isByte!(byte)); 4945 static assert(isByte!(const(byte))); 4946 static assert(isByte!(ubyte)); 4947 static assert(isByte!(immutable(ubyte))); 4948 static assert(!isByte!(short)); 4949 static assert(!isByte!(char)); 4950 static assert(!isByte!(string)); 4951 } 4952 4953 4954 /* 4955 * Gets asterisk string from pointer type 4956 */ 4957 template AsteriskOf(T) 4958 { 4959 static if (is(T P == U*, U)) 4960 enum AsteriskOf = "*" ~ AsteriskOf!U; 4961 else 4962 enum AsteriskOf = ""; 4963 } 4964 4965 /** 4966 * Get the number of member to serialize. 4967 */ 4968 template SerializingMemberNumbers(Classes...) 4969 { 4970 static if (Classes.length == 0) 4971 enum SerializingMemberNumbers = 0; 4972 else 4973 enum SerializingMemberNumbers = Filter!(isPackedField, Classes[0].tupleof).length + SerializingMemberNumbers!(Classes[1..$]); 4974 } 4975 4976 /** 4977 * Get derived classes with serialization-order 4978 */ 4979 template SerializingClasses(T) 4980 { 4981 // There is no information in Object type. Currently disable Object serialization. 4982 static if (is(T == Object)) 4983 static assert(false, "Object type serialization doesn't support yet. Please define toMsgpack/fromMsgpack and use cast"); 4984 else 4985 alias TypeTuple!(Reverse!(Erase!(Object, BaseClassesTuple!(T))), T) SerializingClasses; 4986 } 4987 4988 4989 /** 4990 * Get a field name of class or struct. 4991 */ 4992 template getFieldName(Type, size_t i) 4993 { 4994 import std.conv : text; 4995 4996 static assert((is(Unqual!Type == class) || is(Unqual!Type == struct)), "Type must be class or struct: type = " ~ Type.stringof); 4997 static assert(i < Type.tupleof.length, text(Type.stringof, " has ", Type.tupleof.length, " attributes: given index = ", i)); 4998 4999 enum getFieldName = __traits(identifier, Type.tupleof[i]); 5000 } 5001 5002 5003 version (LittleEndian) 5004 { 5005 /* 5006 * Converts $(value) to different Endian. 5007 * 5008 * Params: 5009 * value = the LittleEndian value to convert. 5010 * 5011 * Returns: 5012 * the converted value. 5013 */ 5014 @trusted 5015 ushort convertEndianTo(size_t Bit, T)(in T value) if (Bit == 16) 5016 { 5017 return ntohs(cast(ushort)value); 5018 } 5019 5020 5021 // ditto 5022 @trusted 5023 uint convertEndianTo(size_t Bit, T)(in T value) if (Bit == 32) 5024 { 5025 return ntohl(cast(uint)value); 5026 } 5027 5028 5029 // ditto 5030 @trusted 5031 ulong convertEndianTo(size_t Bit, T)(in T value) if (Bit == 64) 5032 { 5033 // dmd has convert function? 5034 return ((((cast(ulong)value) << 56) & 0xff00000000000000UL) | 5035 (((cast(ulong)value) << 40) & 0x00ff000000000000UL) | 5036 (((cast(ulong)value) << 24) & 0x0000ff0000000000UL) | 5037 (((cast(ulong)value) << 8) & 0x000000ff00000000UL) | 5038 (((cast(ulong)value) >> 8) & 0x00000000ff000000UL) | 5039 (((cast(ulong)value) >> 24) & 0x0000000000ff0000UL) | 5040 (((cast(ulong)value) >> 40) & 0x000000000000ff00UL) | 5041 (((cast(ulong)value) >> 56) & 0x00000000000000ffUL)); 5042 } 5043 5044 5045 unittest 5046 { 5047 assert(convertEndianTo!16(0x0123) == 0x2301); 5048 assert(convertEndianTo!32(0x01234567) == 0x67452301); 5049 assert(convertEndianTo!64(0x0123456789abcdef) == 0xefcdab8967452301); 5050 } 5051 5052 5053 /* 5054 * Comapatible for BigEndian environment. 5055 */ 5056 ubyte take8from(size_t bit = 8, T)(T value) 5057 { 5058 static if (bit == 8 || bit == 16 || bit == 32 || bit == 64) 5059 return (cast(ubyte*)&value)[0]; 5060 else 5061 static assert(false, bit.stringof ~ " is not support bit width."); 5062 } 5063 5064 5065 unittest 5066 { 5067 foreach (Integer; TypeTuple!(ubyte, ushort, uint, ulong)) { 5068 assert(take8from!8 (cast(Integer)0x01) == 0x01); 5069 assert(take8from!16(cast(Integer)0x0123) == 0x23); 5070 assert(take8from!32(cast(Integer)0x01234567) == 0x67); 5071 assert(take8from!64(cast(Integer)0x0123456789abcdef) == 0xef); 5072 } 5073 } 5074 } 5075 else 5076 { 5077 /* 5078 * Comapatible for LittleEndian environment. 5079 */ 5080 @safe 5081 ushort convertEndianTo(size_t Bit, T)(in T value) if (Bit == 16) 5082 { 5083 return cast(ushort)value; 5084 } 5085 5086 5087 // ditto 5088 @safe 5089 uint convertEndianTo(size_t Bit, T)(in T value) if (Bit == 32) 5090 { 5091 return cast(uint)value; 5092 } 5093 5094 5095 // ditto 5096 @safe 5097 ulong convertEndianTo(size_t Bit, T)(in T value) if (Bit == 64) 5098 { 5099 return cast(ulong)value; 5100 } 5101 5102 5103 unittest 5104 { 5105 assert(convertEndianTo!16(0x0123) == 0x0123); 5106 assert(convertEndianTo!32(0x01234567) == 0x01234567); 5107 assert(convertEndianTo!64(0x0123456789) == 0x0123456789); 5108 } 5109 5110 5111 /* 5112 * Takes 8bit from $(D_PARAM value) 5113 * 5114 * Params: 5115 * value = the content to take. 5116 * 5117 * Returns: 5118 * the 8bit value corresponding $(D_PARAM bit) width. 5119 */ 5120 ubyte take8from(size_t bit = 8, T)(T value) 5121 { 5122 static if (bit == 8) 5123 return (cast(ubyte*)&value)[0]; 5124 else static if (bit == 16) 5125 return (cast(ubyte*)&value)[1]; 5126 else static if (bit == 32) 5127 return (cast(ubyte*)&value)[3]; 5128 else static if (bit == 64) 5129 return (cast(ubyte*)&value)[7]; 5130 else 5131 static assert(false, bit.stringof ~ " is not support bit width."); 5132 } 5133 5134 5135 unittest 5136 { 5137 foreach (Integer; TypeTuple!(ubyte, ushort, uint, ulong)) { 5138 assert(take8from!8 (cast(Integer)0x01) == 0x01); 5139 assert(take8from!16(cast(Integer)0x0123) == 0x23); 5140 assert(take8from!32(cast(Integer)0x01234567) == 0x67); 5141 assert(take8from!64(cast(Integer)0x0123456789abcdef) == 0xef); 5142 } 5143 } 5144 } 5145 5146 5147 /* 5148 * Loads $(D_PARAM T) type value from $(D_PARAM buffer). 5149 * 5150 * Params: 5151 * buffer = the serialized contents. 5152 * 5153 * Returns: 5154 * the Endian-converted value. 5155 */ 5156 T load16To(T)(ubyte[] buffer) 5157 { 5158 return cast(T)(convertEndianTo!16(*cast(ushort*)buffer.ptr)); 5159 } 5160 5161 5162 // ditto 5163 T load32To(T)(ubyte[] buffer) 5164 { 5165 return cast(T)(convertEndianTo!32(*cast(uint*)buffer.ptr)); 5166 } 5167 5168 5169 // ditto 5170 T load64To(T)(ubyte[] buffer) 5171 { 5172 return cast(T)(convertEndianTo!64(*cast(ulong*)buffer.ptr)); 5173 }