1 module msgpack.packer; 2 3 import msgpack.common; 4 import msgpack.attribute; 5 import msgpack.exception; 6 7 import std.array; 8 import std.exception; 9 import std.range; 10 import std.stdio; 11 import std.traits; 12 import std.typecons; 13 import std.typetuple; 14 import std.container; 15 16 17 /** 18 * $(D Packer) is a $(D MessagePack) serializer 19 * 20 * Example: 21 * ----- 22 * auto packer = packer(Appender!(ubyte[])()); 23 * 24 * packer.packArray(false, 100, 1e-10, null); 25 * 26 * stdout.rawWrite(packer.stream.data); 27 * ----- 28 * 29 * NOTE: 30 * Current implementation can't deal with a circular reference. 31 * If you try to serialize a object that has circular reference, runtime raises 'Stack Overflow'. 32 */ 33 struct PackerImpl(Stream) if (isOutputRange!(Stream, ubyte) && isOutputRange!(Stream, ubyte[])) 34 { 35 private: 36 static @system 37 { 38 alias void delegate(ref PackerImpl, void*) PackHandler; 39 PackHandler[TypeInfo] packHandlers; 40 41 public void registerHandler(T, alias Handler)() 42 { 43 packHandlers[typeid(T)] = delegate(ref PackerImpl packer, void* obj) { 44 Handler(packer, *cast(T*)obj); 45 }; 46 } 47 48 public void register(T)() 49 { 50 packHandlers[typeid(T)] = delegate(ref PackerImpl packer, void* obj) { 51 packer.packObject(*cast(T*)obj); 52 }; 53 } 54 } 55 56 enum size_t Offset = 1; // type-information offset 57 58 Stream stream_; // the stream to write 59 ubyte[Offset + RealSize] store_; // stores serialized value 60 bool withFieldName_; 61 62 63 public: 64 /** 65 * Constructs a packer with $(D_PARAM stream). 66 * 67 * Params: 68 * stream = the stream to write. 69 * withFieldName = serialize class / struct with field name 70 */ 71 this(Stream stream, bool withFieldName = false) 72 { 73 stream_ = stream; 74 withFieldName_ = withFieldName; 75 } 76 77 78 /** 79 * Constructs a packer with $(D_PARAM withFieldName). 80 * 81 * Params: 82 * withFieldName = serialize class / struct with field name 83 */ 84 this(bool withFieldName) 85 { 86 withFieldName_ = withFieldName; 87 } 88 89 90 /** 91 * Forwards to stream. 92 * 93 * Returns: 94 * the stream. 95 */ 96 @property @safe 97 nothrow ref Stream stream() 98 { 99 return stream_; 100 } 101 102 103 /** 104 * Serializes argument and writes to stream. 105 * 106 * If the argument is the pointer type, dereferences the pointer and serializes pointed value. 107 * ----- 108 * int a = 10; 109 * int* b = &b; 110 * 111 * packer.pack(b); // serializes 10, not address of a 112 * ----- 113 * Serializes nil if the argument of nullable type is null. 114 * 115 * NOTE: 116 * MessagePack doesn't define $(D_KEYWORD real) type format. 117 * Don't serialize $(D_KEYWORD real) if you communicate with other languages. 118 * Transfer $(D_KEYWORD double) serialization if $(D_KEYWORD real) on your environment equals $(D_KEYWORD double). 119 * 120 * Params: 121 * value = the content to serialize. 122 * 123 * Returns: 124 * self, i.e. for method chaining. 125 */ 126 ref PackerImpl pack(T)(in T value) if (is(Unqual!T == bool)) 127 { 128 if (value) 129 stream_.put(Format.TRUE); 130 else 131 stream_.put(Format.FALSE); 132 133 return this; 134 } 135 136 137 /// ditto 138 ref PackerImpl pack(T)(in T value) if (isUnsigned!T && !is(Unqual!T == enum)) 139 { 140 // ulong < ulong is slower than uint < uint 141 static if (!is(Unqual!T == ulong)) { 142 enum Bits = T.sizeof * 8; 143 144 if (value < (1 << 8)) { 145 if (value < (1 << 7)) { 146 // fixnum 147 stream_.put(take8from!Bits(value)); 148 } else { 149 // uint 8 150 store_[0] = Format.UINT8; 151 store_[1] = take8from!Bits(value); 152 stream_.put(store_[0..Offset + ubyte.sizeof]); 153 } 154 } else { 155 if (value < (1 << 16)) { 156 // uint 16 157 const temp = convertEndianTo!16(value); 158 159 store_[0] = Format.UINT16; 160 *cast(ushort*)&store_[Offset] = temp; 161 stream_.put(store_[0..Offset + ushort.sizeof]); 162 } else { 163 // uint 32 164 const temp = convertEndianTo!32(value); 165 166 store_[0] = Format.UINT32; 167 *cast(uint*)&store_[Offset] = temp; 168 stream_.put(store_[0..Offset + uint.sizeof]); 169 } 170 } 171 } else { 172 if (value < (1UL << 8)) { 173 if (value < (1UL << 7)) { 174 // fixnum 175 stream_.put(take8from!64(value)); 176 } else { 177 // uint 8 178 store_[0] = Format.UINT8; 179 store_[1] = take8from!64(value); 180 stream_.put(store_[0..Offset + ubyte.sizeof]); 181 } 182 } else { 183 if (value < (1UL << 16)) { 184 // uint 16 185 const temp = convertEndianTo!16(value); 186 187 store_[0] = Format.UINT16; 188 *cast(ushort*)&store_[Offset] = temp; 189 stream_.put(store_[0..Offset + ushort.sizeof]); 190 } else if (value < (1UL << 32)){ 191 // uint 32 192 const temp = convertEndianTo!32(value); 193 194 store_[0] = Format.UINT32; 195 *cast(uint*)&store_[Offset] = temp; 196 stream_.put(store_[0..Offset + uint.sizeof]); 197 } else { 198 // uint 64 199 const temp = convertEndianTo!64(value); 200 201 store_[0] = Format.UINT64; 202 *cast(ulong*)&store_[Offset] = temp; 203 stream_.put(store_[0..Offset + ulong.sizeof]); 204 } 205 } 206 } 207 208 return this; 209 } 210 211 212 /// ditto 213 ref PackerImpl pack(T)(in T value) if (isSigned!T && isIntegral!T && !is(Unqual!T == enum)) 214 { 215 // long < long is slower than int < int 216 static if (!is(Unqual!T == long)) { 217 enum Bits = T.sizeof * 8; 218 219 if (value < -(1 << 5)) { 220 if (value < -(1 << 15)) { 221 // int 32 222 const temp = convertEndianTo!32(value); 223 224 store_[0] = Format.INT32; 225 *cast(int*)&store_[Offset] = temp; 226 stream_.put(store_[0..Offset + int.sizeof]); 227 } else if (value < -(1 << 7)) { 228 // int 16 229 const temp = convertEndianTo!16(value); 230 231 store_[0] = Format.INT16; 232 *cast(short*)&store_[Offset] = temp; 233 stream_.put(store_[0..Offset + short.sizeof]); 234 } else { 235 // int 8 236 store_[0] = Format.INT8; 237 store_[1] = take8from!Bits(value); 238 stream_.put(store_[0..Offset + byte.sizeof]); 239 } 240 } else if (value < (1 << 7)) { 241 // fixnum 242 stream_.put(take8from!Bits(value)); 243 } else { 244 if (value < (1 << 8)) { 245 // uint 8 246 store_[0] = Format.UINT8; 247 store_[1] = take8from!Bits(value); 248 stream_.put(store_[0..Offset + ubyte.sizeof]); 249 } else if (value < (1 << 16)) { 250 // uint 16 251 const temp = convertEndianTo!16(value); 252 253 store_[0] = Format.UINT16; 254 *cast(ushort*)&store_[Offset] = temp; 255 stream_.put(store_[0..Offset + ushort.sizeof]); 256 } else { 257 // uint 32 258 const temp = convertEndianTo!32(value); 259 260 store_[0] = Format.UINT32; 261 *cast(uint*)&store_[Offset] = temp; 262 stream_.put(store_[0..Offset + uint.sizeof]); 263 } 264 } 265 } else { 266 if (value < -(1L << 5)) { 267 if (value < -(1L << 15)) { 268 if (value < -(1L << 31)) { 269 // int 64 270 const temp = convertEndianTo!64(value); 271 272 store_[0] = Format.INT64; 273 *cast(long*)&store_[Offset] = temp; 274 stream_.put(store_[0..Offset + long.sizeof]); 275 } else { 276 // int 32 277 const temp = convertEndianTo!32(value); 278 279 store_[0] = Format.INT32; 280 *cast(int*)&store_[Offset] = temp; 281 stream_.put(store_[0..Offset + int.sizeof]); 282 } 283 } else { 284 if (value < -(1L << 7)) { 285 // int 16 286 const temp = convertEndianTo!16(value); 287 288 store_[0] = Format.INT16; 289 *cast(short*)&store_[Offset] = temp; 290 stream_.put(store_[0..Offset + short.sizeof]); 291 } else { 292 // int 8 293 store_[0] = Format.INT8; 294 store_[1] = take8from!64(value); 295 stream_.put(store_[0..Offset + byte.sizeof]); 296 } 297 } 298 } else if (value < (1L << 7)) { 299 // fixnum 300 stream_.put(take8from!64(value)); 301 } else { 302 if (value < (1L << 16)) { 303 if (value < (1L << 8)) { 304 // uint 8 305 store_[0] = Format.UINT8; 306 store_[1] = take8from!64(value); 307 stream_.put(store_[0..Offset + ubyte.sizeof]); 308 } else { 309 // uint 16 310 const temp = convertEndianTo!16(value); 311 312 store_[0] = Format.UINT16; 313 *cast(ushort*)&store_[Offset] = temp; 314 stream_.put(store_[0..Offset + ushort.sizeof]); 315 } 316 } else { 317 if (value < (1L << 32)) { 318 // uint 32 319 const temp = convertEndianTo!32(value); 320 321 store_[0] = Format.UINT32; 322 *cast(uint*)&store_[Offset] = temp; 323 stream_.put(store_[0..Offset + uint.sizeof]); 324 } else { 325 // uint 64 326 const temp = convertEndianTo!64(value); 327 328 store_[0] = Format.UINT64; 329 *cast(ulong*)&store_[Offset] = temp; 330 stream_.put(store_[0..Offset + ulong.sizeof]); 331 } 332 } 333 } 334 } 335 336 return this; 337 } 338 339 340 /// ditto 341 ref PackerImpl pack(T)(in T value) if (isSomeChar!T && !is(Unqual!T == enum)) 342 { 343 static if (is(Unqual!T == char)) { 344 return pack(cast(ubyte)(value)); 345 } else static if (is(Unqual!T == wchar)) { 346 return pack(cast(ushort)(value)); 347 } else static if (is(Unqual!T == dchar)) { 348 return pack(cast(uint)(value)); 349 } 350 } 351 352 353 /// ditto 354 ref PackerImpl pack(T)(in T value) if (isFloatingPoint!T && !is(Unqual!T == enum)) 355 { 356 static if (is(Unqual!T == float)) { 357 const temp = convertEndianTo!32(_f(value).i); 358 359 store_[0] = Format.FLOAT; 360 *cast(uint*)&store_[Offset] = temp; 361 stream_.put(store_[0..Offset + uint.sizeof]); 362 } else static if (is(Unqual!T == double)) { 363 const temp = convertEndianTo!64(_d(value).i); 364 365 store_[0] = Format.DOUBLE; 366 *cast(ulong*)&store_[Offset] = temp; 367 stream_.put(store_[0..Offset + ulong.sizeof]); 368 } else { 369 static if ((real.sizeof > double.sizeof) && EnableReal) { 370 store_[0] = Format.REAL; 371 const temp = _r(value); 372 const fraction = convertEndianTo!64(temp.fraction); 373 const exponent = convertEndianTo!16(temp.exponent); 374 375 *cast(Unqual!(typeof(fraction))*)&store_[Offset] = fraction; 376 *cast(Unqual!(typeof(exponent))*)&store_[Offset + fraction.sizeof] = exponent; 377 stream_.put(store_[0..$]); 378 } else { // Non-x86 CPUs, real type equals double type. 379 pack(cast(double)value); 380 } 381 } 382 383 return this; 384 } 385 386 387 /// ditto 388 ref PackerImpl pack(T)(in T value) if (is(Unqual!T == enum)) 389 { 390 pack(cast(OriginalType!T)value); 391 392 return this; 393 } 394 395 396 /// Overload for pack(null) for 2.057 or later 397 static if (!is(typeof(null) == void*)) 398 { 399 ref PackerImpl pack(T)(in T value) if (is(Unqual!T == typeof(null))) 400 { 401 return packNil(); 402 } 403 } 404 405 406 /// ditto 407 ref PackerImpl pack(T)(in T value) if (isPointer!T) 408 { 409 static if (is(Unqual!T == void*)) { // for pack(null) for 2.056 or earlier 410 enforce(value is null, "Can't serialize void type"); 411 stream_.put(Format.NIL); 412 } else { 413 if (value is null) 414 stream_.put(Format.NIL); 415 else 416 pack(mixin(AsteriskOf!T ~ "value")); 417 } 418 419 return this; 420 } 421 422 423 /// ditto 424 ref PackerImpl pack(T)(in T array) if ((isArray!T || isInstanceOf!(Array, T)) && !is(Unqual!T == enum)) 425 { 426 alias typeof(T.init[0]) U; 427 428 if (array.empty) 429 return packNil(); 430 431 // Raw bytes 432 static if (isByte2!(U) || isSomeChar!(U)) { 433 ubyte[] raw = cast(ubyte[])array; 434 435 beginRaw(raw.length); 436 stream_.put(raw); 437 } else { 438 beginArray(array.length); 439 foreach (elem; array) 440 pack(elem); 441 } 442 443 return this; 444 } 445 446 447 /// ditto 448 ref PackerImpl pack(T)(in T array) if (isAssociativeArray!T) 449 { 450 if (array is null) 451 return packNil(); 452 453 beginMap(array.length); 454 foreach (key, value; array) { 455 pack(key); 456 pack(value); 457 } 458 459 return this; 460 } 461 462 463 /// ditto 464 ref PackerImpl pack(Types...)(auto ref const Types objects) if (Types.length > 1) 465 { 466 foreach (i, T; Types) 467 pack(objects[i]); 468 469 return this; 470 } 471 472 473 /** 474 * Serializes $(D_PARAM object) and writes to stream. 475 * 476 * Calling $(D toMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D toMsgpack) method. $(D toMsgpack) signature is: 477 * ----- 478 * void toMsgpack(Packer)(ref Packer packer) const 479 * ----- 480 * This method serializes all members of T object if $(D_KEYWORD class) and $(D_KEYWORD struct) don't implement $(D toMsgpack). 481 * 482 * An object that doesn't implement $(D toMsgpack) is serialized to Array type. 483 * ----- 484 * packer.pack(tuple(true, 1, "Hi!")) // -> '[true, 1, "Hi!"]', not 'ture, 1, "Hi!"' 485 * 486 * struct Foo 487 * { 488 * int num = 10; 489 * string msg = "D!"; 490 * } 491 * packer.pack(Foo()); // -> '[10, "D!"]' 492 * 493 * class Base 494 * { 495 * bool flag = true; 496 * } 497 * class Derived : Base 498 * { 499 * double = 0.5f; 500 * } 501 * packer.pack(new Derived()); // -> '[true, 0.5f]' 502 * ----- 503 * 504 * Params: 505 * object = the content to serialize. 506 * 507 * Returns: 508 * self, i.e. for method chaining. 509 */ 510 ref PackerImpl pack(T)(in T object) if (is(Unqual!T == class)) 511 { 512 if (object is null) 513 return packNil(); 514 515 static if (hasMember!(T, "toMsgpack")) 516 { 517 static if (__traits(compiles, { object.toMsgpack(this, withFieldName_); })) { 518 object.toMsgpack(this, withFieldName_); 519 } else static if (__traits(compiles, { object.toMsgpack(this); })) { // backward compatible 520 object.toMsgpack(this); 521 } else { 522 static assert(0, "Failed to invoke 'toMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 523 } 524 } else { 525 if (auto handler = object.classinfo in packHandlers) { 526 (*handler)(this, cast(void*)&object); 527 return this; 528 } 529 if (T.classinfo !is object.classinfo) { 530 throw new MessagePackException("Can't pack derived class through reference to base class."); 531 } 532 533 packObject!(T)(object); 534 } 535 536 return this; 537 } 538 539 540 /// ditto 541 @trusted 542 ref PackerImpl pack(T)(auto ref T object) if (is(Unqual!T == struct) && 543 !isInstanceOf!(Array, T) && 544 !is(Unqual!T == ExtValue)) 545 { 546 static if (hasMember!(T, "toMsgpack")) 547 { 548 static if (__traits(compiles, { object.toMsgpack(this, withFieldName_); })) { 549 object.toMsgpack(this, withFieldName_); 550 } else static if (__traits(compiles, { object.toMsgpack(this); })) { // backward compatible 551 object.toMsgpack(this); 552 } else { 553 static assert(0, "Failed to invoke 'toMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 554 } 555 } else static if (isTuple!T) { 556 beginArray(object.field.length); 557 foreach (f; object.field) 558 pack(f); 559 } else { // simple struct 560 if (auto handler = typeid(Unqual!T) in packHandlers) { 561 (*handler)(this, cast(void*)&object); 562 return this; 563 } 564 565 immutable memberNum = SerializingMemberNumbers!(T); 566 if (withFieldName_) 567 beginMap(memberNum); 568 else 569 beginArray(memberNum); 570 571 if (withFieldName_) { 572 foreach (i, f; object.tupleof) { 573 static if (isPackedField!(T.tupleof[i]) && __traits(compiles, { pack(f); })) 574 { 575 pack(getFieldName!(T, i)); 576 pack(f); 577 } 578 } 579 } else { 580 foreach (i, f; object.tupleof) { 581 static if (isPackedField!(T.tupleof[i]) && __traits(compiles, { pack(f); })) 582 pack(f); 583 } 584 } 585 } 586 587 return this; 588 } 589 590 591 void packObject(T)(in T object) if (is(Unqual!T == class)) 592 { 593 alias SerializingClasses!(T) Classes; 594 595 immutable memberNum = SerializingMemberNumbers!(Classes); 596 if (withFieldName_) 597 beginMap(memberNum); 598 else 599 beginArray(memberNum); 600 601 foreach (Class; Classes) { 602 Class obj = cast(Class)object; 603 if (withFieldName_) { 604 foreach (i, f ; obj.tupleof) { 605 static if (isPackedField!(Class.tupleof[i])) { 606 pack(getFieldName!(Class, i)); 607 pack(f); 608 } 609 } 610 } else { 611 foreach (i, f ; obj.tupleof) { 612 static if (isPackedField!(Class.tupleof[i])) 613 pack(f); 614 } 615 } 616 } 617 } 618 619 620 /** 621 * Serializes the arguments as container to stream. 622 * 623 * ----- 624 * packer.packArray(true, 1); // -> [true, 1] 625 * packer.packMap("Hi", 100); // -> ["Hi":100] 626 * ----- 627 * 628 * In packMap, the number of arguments must be even. 629 * 630 * Params: 631 * objects = the contents to serialize. 632 * 633 * Returns: 634 * self, i.e. for method chaining. 635 */ 636 ref PackerImpl packArray(Types...)(auto ref const Types objects) 637 { 638 beginArray(Types.length); 639 foreach (i, T; Types) 640 pack(objects[i]); 641 //pack(objects); // slow :( 642 643 return this; 644 } 645 646 647 /// ditto 648 ref PackerImpl packMap(Types...)(auto ref const Types objects) 649 { 650 static assert(Types.length % 2 == 0, "The number of arguments must be even"); 651 652 beginMap(Types.length / 2); 653 foreach (i, T; Types) 654 pack(objects[i]); 655 656 return this; 657 } 658 659 /** 660 * Packs $(D data) as an extended value of $(D type). 661 * 662 * ---- 663 * packer.packExt(3, bytes); 664 * ---- 665 * 666 * $(D type) must be a signed byte 0-127. 667 * 668 * Params: 669 * type = the application-defined type for the data 670 * data = an array of bytes 671 * 672 * Returns: 673 * seld, i.e. for method chaining. 674 */ 675 ref PackerImpl pack(T)(auto ref const T data) if (is(Unqual!T == ExtValue)) 676 { 677 packExt(data.type, data.data); 678 return this; 679 } 680 681 /** 682 * Packs $(D data) as an extended value of $(D type). 683 * 684 * ---- 685 * packer.packExt(3, bytes); 686 * ---- 687 * 688 * $(D type) must be a signed byte 0-127. 689 * 690 * Params: 691 * type = the application-defined type for the data 692 * data = an array of bytes 693 * 694 * Returns: 695 * seld, i.e. for method chaining. 696 */ 697 ref PackerImpl packExt(in byte type, const ubyte[] data) 698 { 699 ref PackerImpl packExtFixed(int fmt) 700 { 701 store_[0] = cast(ubyte)fmt; 702 store_[1] = type; 703 stream_.put(store_[0 .. 2]); 704 stream_.put(data); 705 return this; 706 } 707 708 // Try packing to a fixed-length type 709 if (data.length == 1) 710 return packExtFixed(Format.EXT + 0); 711 else if (data.length == 2) 712 return packExtFixed(Format.EXT + 1); 713 else if (data.length == 4) 714 return packExtFixed(Format.EXT + 2); 715 else if (data.length == 8) 716 return packExtFixed(Format.EXT + 3); 717 else if (data.length == 16) 718 return packExtFixed(Format.EXT + 4); 719 720 int typeByte = void; 721 if (data.length <= (2^^8)-1) 722 { 723 store_[0] = Format.EXT8; 724 store_[1] = cast(ubyte)data.length; 725 typeByte = 2; 726 727 } else if (data.length <= (2^^16)-1) { 728 store_[0] = Format.EXT16; 729 const temp = convertEndianTo!16(data.length); 730 *cast(ushort*)&store_[Offset] = temp; 731 typeByte = 3; 732 } else if (data.length <= (2^^32)-1) { 733 store_[0] = Format.EXT32; 734 const temp = convertEndianTo!32(data.length); 735 *cast(uint*)&store_[Offset] = temp; 736 typeByte = 5; 737 } else 738 throw new MessagePackException("Data too large to pack as EXT"); 739 740 store_[typeByte] = type; 741 stream_.put(store_[0..typeByte+1]); 742 stream_.put(data); 743 744 return this; 745 } 746 747 /* 748 * Serializes raw type-information to stream for binary type. 749 */ 750 void beginRaw(in size_t length) 751 { 752 import std.conv : text; 753 754 if (length < 32) { 755 const ubyte temp = Format.RAW | cast(ubyte)length; 756 stream_.put(take8from(temp)); 757 } else if (length < 65536) { 758 const temp = convertEndianTo!16(length); 759 760 store_[0] = Format.RAW16; 761 *cast(ushort*)&store_[Offset] = temp; 762 stream_.put(store_[0..Offset + ushort.sizeof]); 763 } else { 764 if (length > 0xffffffff) 765 throw new MessagePackException(text("size of raw is too long to pack: ", length, " bytes should be <= ", 0xffffffff)); 766 767 const temp = convertEndianTo!32(length); 768 769 store_[0] = Format.RAW32; 770 *cast(uint*)&store_[Offset] = temp; 771 stream_.put(store_[0..Offset + uint.sizeof]); 772 } 773 } 774 775 /** 776 * Serializes the type-information to stream. 777 * 778 * These methods don't serialize contents. 779 * You need to call pack method to serialize contents at your own risk. 780 * ----- 781 * packer.beginArray(3).pack(true, 1); // -> [true, 1, 782 * 783 * // other operation 784 * 785 * packer.pack("Hi!"); // -> [true, 1, "Hi!"] 786 * ----- 787 * 788 * Params: 789 * length = the length of container. 790 * 791 * Returns: 792 * self, i.e. for method chaining. 793 */ 794 ref PackerImpl beginArray(in size_t length) 795 { 796 if (length < 16) { 797 const ubyte temp = Format.ARRAY | cast(ubyte)length; 798 stream_.put(take8from(temp)); 799 } else if (length < 65536) { 800 const temp = convertEndianTo!16(length); 801 802 store_[0] = Format.ARRAY16; 803 *cast(ushort*)&store_[Offset] = temp; 804 stream_.put(store_[0..Offset + ushort.sizeof]); 805 } else { 806 const temp = convertEndianTo!32(length); 807 808 store_[0] = Format.ARRAY32; 809 *cast(uint*)&store_[Offset] = temp; 810 stream_.put(store_[0..Offset + uint.sizeof]); 811 } 812 813 return this; 814 } 815 816 817 /// ditto 818 ref PackerImpl beginMap(in size_t length) 819 { 820 if (length < 16) { 821 const ubyte temp = Format.MAP | cast(ubyte)length; 822 stream_.put(take8from(temp)); 823 } else if (length < 65536) { 824 const temp = convertEndianTo!16(length); 825 826 store_[0] = Format.MAP16; 827 *cast(ushort*)&store_[Offset] = temp; 828 stream_.put(store_[0..Offset + ushort.sizeof]); 829 } else { 830 const temp = convertEndianTo!32(length); 831 832 store_[0] = Format.MAP32; 833 *cast(uint*)&store_[Offset] = temp; 834 stream_.put(store_[0..Offset + uint.sizeof]); 835 } 836 837 return this; 838 } 839 840 841 private: 842 /* 843 * Serializes the nil value. 844 */ 845 ref PackerImpl packNil() 846 { 847 stream_.put(Format.NIL); 848 return this; 849 } 850 } 851 852 853 /// Default serializer 854 alias PackerImpl!(Appender!(ubyte[])) Packer; // should be pure struct? 855 856 857 /** 858 * Helper for $(D Packer) construction. 859 * 860 * Params: 861 * stream = the stream to write. 862 * withFieldName = serialize class / struct with field name 863 * 864 * Returns: 865 * a $(D Packer) object instantiated and initialized according to the arguments. 866 */ 867 PackerImpl!(Stream) packer(Stream)(Stream stream, bool withFieldName = false) 868 { 869 return typeof(return)(stream, withFieldName); 870 } 871 872 873 version(unittest) 874 { 875 package import std.file, core.stdc.string; 876 877 package mixin template DefinePacker() 878 { 879 Packer packer; 880 } 881 882 package mixin template DefineDictionalPacker() 883 { 884 Packer packer = Packer(false); 885 } 886 } 887 888 889 unittest 890 { 891 { // unique value 892 mixin DefinePacker; 893 894 ubyte[] result = [Format.NIL, Format.TRUE, Format.FALSE]; 895 896 packer.pack(null, true, false); 897 foreach (i, value; packer.stream.data) 898 assert(value == result[i]); 899 } 900 { // uint * 901 static struct UTest { ubyte format; ulong value; } 902 903 enum : ulong { A = ubyte.max, B = ushort.max, C = uint.max, D = ulong.max } 904 905 static UTest[][] utests = [ 906 [{Format.UINT8, A}], 907 [{Format.UINT8, A}, {Format.UINT16, B}], 908 [{Format.UINT8, A}, {Format.UINT16, B}, {Format.UINT32, C}], 909 [{Format.UINT8, A}, {Format.UINT16, B}, {Format.UINT32, C}, {Format.UINT64, D}], 910 ]; 911 912 foreach (I, T; TypeTuple!(ubyte, ushort, uint, ulong)) { 913 foreach (i, test; utests[I]) { 914 mixin DefinePacker; 915 916 packer.pack(cast(T)test.value); 917 assert(packer.stream.data[0] == test.format); 918 919 switch (i) { 920 case 0: 921 auto answer = take8from!(T.sizeof * 8)(test.value); 922 assert(memcmp(&packer.stream.data[1], &answer, ubyte.sizeof) == 0); 923 break; 924 case 1: 925 auto answer = convertEndianTo!16(test.value); 926 assert(memcmp(&packer.stream.data[1], &answer, ushort.sizeof) == 0); 927 break; 928 case 2: 929 auto answer = convertEndianTo!32(test.value); 930 assert(memcmp(&packer.stream.data[1], &answer, uint.sizeof) == 0); 931 break; 932 default: 933 auto answer = convertEndianTo!64(test.value); 934 assert(memcmp(&packer.stream.data[1], &answer, ulong.sizeof) == 0); 935 } 936 } 937 } 938 } 939 { // int * 940 static struct STest { ubyte format; long value; } 941 942 enum : long { A = byte.min, B = short.min, C = int.min, D = long.min } 943 944 static STest[][] stests = [ 945 [{Format.INT8, A}], 946 [{Format.INT8, A}, {Format.INT16, B}], 947 [{Format.INT8, A}, {Format.INT16, B}, {Format.INT32, C}], 948 [{Format.INT8, A}, {Format.INT16, B}, {Format.INT32, C}, {Format.INT64, D}], 949 ]; 950 951 foreach (I, T; TypeTuple!(byte, short, int, long)) { 952 foreach (i, test; stests[I]) { 953 mixin DefinePacker; 954 955 packer.pack(cast(T)test.value); 956 assert(packer.stream.data[0] == test.format); 957 958 switch (i) { 959 case 0: 960 auto answer = take8from!(T.sizeof * 8)(test.value); 961 assert(memcmp(&packer.stream.data[1], &answer, byte.sizeof) == 0); 962 break; 963 case 1: 964 auto answer = convertEndianTo!16(test.value); 965 assert(memcmp(&packer.stream.data[1], &answer, short.sizeof) == 0); 966 break; 967 case 2: 968 auto answer = convertEndianTo!32(test.value); 969 assert(memcmp(&packer.stream.data[1], &answer, int.sizeof) == 0); 970 break; 971 default: 972 auto answer = convertEndianTo!64(test.value); 973 assert(memcmp(&packer.stream.data[1], &answer, long.sizeof) == 0); 974 } 975 } 976 } 977 } 978 { // fload, double 979 static if ((real.sizeof == double.sizeof) || !EnableReal) 980 { 981 alias TypeTuple!(float, double, double) FloatingTypes; 982 static struct FTest { ubyte format; double value; } 983 984 static FTest[] ftests = [ 985 {Format.FLOAT, float.min_normal}, 986 {Format.DOUBLE, double.max}, 987 {Format.DOUBLE, double.max}, 988 ]; 989 } 990 else 991 { 992 alias TypeTuple!(float, double, real) FloatingTypes; 993 static struct FTest { ubyte format; real value; } 994 995 static FTest[] ftests = [ 996 {Format.FLOAT, float.min_normal}, 997 {Format.DOUBLE, double.max}, 998 {Format.REAL, real.max}, 999 ]; 1000 } 1001 1002 foreach (I, T; FloatingTypes) { 1003 mixin DefinePacker; 1004 1005 packer.pack(cast(T)ftests[I].value); 1006 assert(packer.stream.data[0] == ftests[I].format); 1007 1008 switch (I) { 1009 case 0: 1010 const answer = convertEndianTo!32(_f(cast(T)ftests[I].value).i); 1011 assert(memcmp(&packer.stream.data[1], &answer, float.sizeof) == 0); 1012 break; 1013 case 1: 1014 const answer = convertEndianTo!64(_d(cast(T)ftests[I].value).i); 1015 assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0); 1016 break; 1017 default: 1018 static if (EnableReal) 1019 { 1020 const t = _r(cast(T)ftests[I].value); 1021 const f = convertEndianTo!64(t.fraction); 1022 const e = convertEndianTo!16(t.exponent); 1023 assert(memcmp(&packer.stream.data[1], &f, f.sizeof) == 0); 1024 assert(memcmp(&packer.stream.data[1 + f.sizeof], &e, e.sizeof) == 0); 1025 } 1026 else 1027 { 1028 const answer = convertEndianTo!64(_d(cast(T)ftests[I].value).i); 1029 assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0); 1030 } 1031 } 1032 } 1033 } 1034 { // pointer 1035 static struct PTest 1036 { 1037 ubyte format; 1038 1039 union 1040 { 1041 ulong* p0; 1042 long* p1; 1043 double* p2; 1044 } 1045 } 1046 1047 PTest[] ptests = [PTest(Format.UINT64), PTest(Format.INT64), PTest(Format.DOUBLE)]; 1048 1049 ulong v0 = ulong.max; 1050 long v1 = long.min; 1051 double v2 = double.max; 1052 1053 foreach (I, Index; TypeTuple!("0", "1", "2")) { 1054 mixin DefinePacker; 1055 1056 mixin("ptests[I].p" ~ Index ~ " = &v" ~ Index ~ ";"); 1057 1058 packer.pack(mixin("ptests[I].p" ~ Index)); 1059 assert(packer.stream.data[0] == ptests[I].format); 1060 1061 switch (I) { 1062 case 0: 1063 auto answer = convertEndianTo!64(*ptests[I].p0); 1064 assert(memcmp(&packer.stream.data[1], &answer, ulong.sizeof) == 0); 1065 break; 1066 case 1: 1067 auto answer = convertEndianTo!64(*ptests[I].p1); 1068 assert(memcmp(&packer.stream.data[1], &answer, long.sizeof) == 0); 1069 break; 1070 default: 1071 const answer = convertEndianTo!64(_d(*ptests[I].p2).i); 1072 assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0); 1073 } 1074 } 1075 } 1076 { // enum 1077 enum E : ubyte { A = ubyte.max } 1078 1079 mixin DefinePacker; E e = E.A; 1080 1081 packer.pack(e); 1082 assert(packer.stream.data[0] == Format.UINT8); 1083 1084 auto answer = E.A; 1085 assert(memcmp(&packer.stream.data[1], &answer, (OriginalType!E).sizeof) == 0); 1086 } 1087 { // enum with string 1088 enum E2 : string { A = "test" } 1089 1090 mixin DefinePacker; E2 e = E2.A; 1091 1092 packer.pack(e); 1093 assert(packer.stream.data[0] == (Format.RAW | 0x04)); 1094 } 1095 { // container 1096 static struct CTest { ubyte format; size_t value; } 1097 1098 enum : ulong { A = 16 / 2, B = ushort.max, C = uint.max } 1099 1100 static CTest[][] ctests = [ 1101 [{Format.ARRAY | A, Format.ARRAY | A}, {Format.ARRAY16, B}, {Format.ARRAY32, C}], 1102 [{Format.MAP | A, Format.MAP | A}, {Format.MAP16, B}, {Format.MAP32, C}], 1103 [{Format.RAW | A, Format.RAW | A}, {Format.RAW16, B}, {Format.RAW32, C}], 1104 ]; 1105 1106 foreach (I, Name; TypeTuple!("Array", "Map", "Raw")) { 1107 auto test = ctests[I]; 1108 1109 foreach (i, T; TypeTuple!(ubyte, ushort, uint)) { 1110 mixin DefinePacker; 1111 mixin("packer.begin" ~ Name ~ "(i ? test[i].value : A);"); 1112 1113 assert(packer.stream.data[0] == test[i].format); 1114 1115 switch (i) { 1116 case 0: 1117 auto answer = take8from(test[i].value); 1118 assert(memcmp(&packer.stream.data[0], &answer, ubyte.sizeof) == 0); 1119 break; 1120 case 1: 1121 auto answer = convertEndianTo!16(test[i].value); 1122 assert(memcmp(&packer.stream.data[1], &answer, ushort.sizeof) == 0); 1123 break; 1124 default: 1125 auto answer = convertEndianTo!32(test[i].value); 1126 assert(memcmp(&packer.stream.data[1], &answer, uint.sizeof) == 0); 1127 } 1128 } 1129 } 1130 } 1131 { // larger spec size for string / binary 1132 mixin DefinePacker; 1133 1134 try { 1135 byte[] bins = new byte[0xffffffffUL + 1]; 1136 packer.pack(bins); 1137 assert(false); 1138 } catch (MessagePackException e) { 1139 } 1140 } 1141 { // user defined 1142 { 1143 static struct S 1144 { 1145 uint num = uint.max; 1146 1147 void toMsgpack(P)(ref P p) const { p.packArray(num); } 1148 } 1149 1150 mixin DefinePacker; S test; 1151 1152 packer.pack(test); 1153 1154 assert(packer.stream.data[0] == (Format.ARRAY | 1)); 1155 assert(packer.stream.data[1] == Format.UINT32); 1156 assert(memcmp(&packer.stream.data[2], &test.num, uint.sizeof) == 0); 1157 } 1158 { 1159 mixin DefinePacker; auto test = tuple(true, false, uint.max); 1160 1161 packer.pack(test); 1162 1163 assert(packer.stream.data[0] == (Format.ARRAY | 3)); 1164 assert(packer.stream.data[1] == Format.TRUE); 1165 assert(packer.stream.data[2] == Format.FALSE); 1166 assert(packer.stream.data[3] == Format.UINT32); 1167 assert(memcmp(&packer.stream.data[4], &test.field[2], uint.sizeof) == 0); 1168 } 1169 { 1170 static class C 1171 { 1172 uint num; 1173 1174 this(uint n) { num = n; } 1175 1176 void toMsgpack(P)(ref P p) const { p.packArray(num); } 1177 } 1178 1179 mixin DefinePacker; C test = new C(ushort.max); 1180 1181 packer.pack(test); 1182 1183 assert(packer.stream.data[0] == (Format.ARRAY | 1)); 1184 assert(packer.stream.data[1] == Format.UINT16); 1185 assert(memcmp(&packer.stream.data[2], &test.num, ushort.sizeof) == 0); 1186 } 1187 } 1188 { // simple struct and class 1189 { 1190 static struct Simple 1191 { 1192 uint num = uint.max; 1193 } 1194 1195 static struct SimpleWithNonPacked1 1196 { 1197 uint num = uint.max; 1198 @nonPacked string str = "ignored"; 1199 } 1200 1201 static struct SimpleWithNonPacked2 1202 { 1203 @nonPacked string str = "ignored"; 1204 uint num = uint.max; 1205 } 1206 1207 static struct SimpleWithSkippedTypes 1208 { 1209 int function(int) fn; 1210 int delegate(int) dg; 1211 uint num = uint.max; 1212 } 1213 1214 foreach (Type; TypeTuple!(Simple, SimpleWithNonPacked1, SimpleWithNonPacked2, SimpleWithSkippedTypes)) { 1215 mixin DefinePacker; 1216 1217 Type test; 1218 packer.pack(test); 1219 1220 assert(packer.stream.data[0] == (Format.ARRAY | 1)); 1221 assert(packer.stream.data[1] == Format.UINT32); 1222 assert(memcmp(&packer.stream.data[2], &test.num, uint.sizeof) == 0); 1223 } 1224 } 1225 1226 static class SimpleA 1227 { 1228 bool flag = true; 1229 } 1230 1231 static class SimpleB : SimpleA 1232 { 1233 ubyte type = 100; 1234 } 1235 1236 static class SimpleC : SimpleB 1237 { 1238 uint num = uint.max; 1239 } 1240 1241 static class SimpleCWithNonPacked1 : SimpleB 1242 { 1243 uint num = uint.max; 1244 @nonPacked string str = "ignored"; 1245 } 1246 1247 static class SimpleCWithNonPacked2 : SimpleB 1248 { 1249 @nonPacked string str = "ignored"; 1250 uint num = uint.max; 1251 } 1252 1253 static class SimpleCWithSkippedTypes : SimpleB 1254 { 1255 uint num = uint.max; 1256 int function(int) fn; 1257 int delegate(int) dg; 1258 } 1259 1260 { // from derived class 1261 foreach (Type; TypeTuple!(SimpleC, SimpleCWithNonPacked1, SimpleCWithNonPacked2, SimpleCWithSkippedTypes)) { 1262 mixin DefinePacker; 1263 1264 Type test = new Type(); 1265 packer.pack(test); 1266 1267 assert(packer.stream.data[0] == (Format.ARRAY | 3)); 1268 assert(packer.stream.data[1] == Format.TRUE); 1269 assert(packer.stream.data[2] == 100); 1270 assert(packer.stream.data[3] == Format.UINT32); 1271 assert(memcmp(&packer.stream.data[4], &test.num, uint.sizeof) == 0); 1272 } 1273 } 1274 { // from base class 1275 mixin DefinePacker; SimpleB test = new SimpleC(); 1276 1277 try { 1278 packer.pack(test); 1279 assert(false); 1280 } catch (Exception e) { } 1281 } 1282 } 1283 1284 // ext types 1285 { 1286 byte type = 7; // an arbitrary type value 1287 1288 // fixexts 1289 { 1290 ubyte[16] data; 1291 data[] = 1; 1292 foreach (L; TypeTuple!(1, 2, 4, 8, 16)) 1293 { 1294 mixin DefinePacker; 1295 packer.pack(ExtValue(type, data[0 .. L])); 1296 1297 // format, type, data 1298 assert(packer.stream.data.length == 2 + L); 1299 const l = 2 ^^ (packer.stream.data[0] - Format.EXT); 1300 assert(l == L); 1301 assert(packer.stream.data[1] == type); 1302 assert(packer.stream.data[2 .. 2+l] == data[0 .. L]); 1303 } 1304 } 1305 1306 // ext8 1307 { 1308 foreach (L; TypeTuple!(3, 7, 255)) 1309 { 1310 ubyte[] data = new ubyte[](L); 1311 data[] = 1; 1312 1313 mixin DefinePacker; 1314 packer.pack(ExtValue(type, data[0 .. L])); 1315 1316 // format, length, type, data 1317 assert(packer.stream.data.length == 3 + L); 1318 assert(packer.stream.data[0] == Format.EXT8); 1319 assert(packer.stream.data[1] == L); 1320 assert(packer.stream.data[2] == type); 1321 assert(packer.stream.data[3 .. 3 + L] == data); 1322 } 1323 } 1324 1325 // ext16 1326 { 1327 foreach (L; TypeTuple!(256, (2^^16)-1)) 1328 { 1329 ubyte[] data = new ubyte[](L); 1330 data[] = 1; 1331 1332 mixin DefinePacker; 1333 packer.pack(ExtValue(type, data[0 .. L])); 1334 1335 // format, length, type, data 1336 import std.conv : text; 1337 assert(packer.stream.data.length == 4 + L, text(packer.stream.data.length)); 1338 assert(packer.stream.data[0] == Format.EXT16); 1339 1340 ushort l = convertEndianTo!16(L); 1341 assert(memcmp(&packer.stream.data[1], &l, ushort.sizeof) == 0); 1342 assert(packer.stream.data[3] == type); 1343 assert(packer.stream.data[4 .. 4 + L] == data); 1344 } 1345 } 1346 1347 // ext32 1348 { 1349 foreach (L; TypeTuple!(2^^16, 2^^17)) 1350 { 1351 ubyte[] data = new ubyte[](L); 1352 data[] = 1; 1353 1354 mixin DefinePacker; 1355 packer.pack(ExtValue(type, data[0 .. L])); 1356 1357 // format, length, type, data 1358 import std.conv : text; 1359 assert(packer.stream.data.length == 6 + L, text(packer.stream.data.length)); 1360 assert(packer.stream.data[0] == Format.EXT32); 1361 1362 uint l = convertEndianTo!32(L); 1363 assert(memcmp(&packer.stream.data[1], &l, uint.sizeof) == 0); 1364 assert(packer.stream.data[5] == type); 1365 assert(packer.stream.data[6 .. 6 + L] == data); 1366 } 1367 } 1368 } 1369 }