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.buffer.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 /* 429 * Serializes raw type-information to stream. 430 */ 431 void beginRaw(in size_t length) 432 { 433 if (length < 32) { 434 const ubyte temp = Format.RAW | cast(ubyte)length; 435 stream_.put(take8from(temp)); 436 } else if (length < 65536) { 437 const temp = convertEndianTo!16(length); 438 439 store_[0] = Format.RAW16; 440 *cast(ushort*)&store_[Offset] = temp; 441 stream_.put(store_[0..Offset + ushort.sizeof]); 442 } else { 443 const temp = convertEndianTo!32(length); 444 445 store_[0] = Format.RAW32; 446 *cast(uint*)&store_[Offset] = temp; 447 stream_.put(store_[0..Offset + uint.sizeof]); 448 } 449 } 450 451 if (array.empty) 452 return packNil(); 453 454 // Raw bytes 455 static if (isByte!(U) || isSomeChar!(U)) { 456 ubyte[] raw = cast(ubyte[])array; 457 458 beginRaw(raw.length); 459 stream_.put(raw); 460 } else { 461 beginArray(array.length); 462 foreach (elem; array) 463 pack(elem); 464 } 465 466 return this; 467 } 468 469 470 /// ditto 471 ref PackerImpl pack(T)(in T array) if (isAssociativeArray!T) 472 { 473 if (array is null) 474 return packNil(); 475 476 beginMap(array.length); 477 foreach (key, value; array) { 478 pack(key); 479 pack(value); 480 } 481 482 return this; 483 } 484 485 486 /// ditto 487 ref PackerImpl pack(Types...)(auto ref const Types objects) if (Types.length > 1) 488 { 489 foreach (i, T; Types) 490 pack(objects[i]); 491 492 return this; 493 } 494 495 496 /** 497 * Serializes $(D_PARAM object) and writes to stream. 498 * 499 * Calling $(D toMsgpack) if $(D_KEYWORD class) and $(D_KEYWORD struct) implement $(D toMsgpack) method. $(D toMsgpack) signature is: 500 * ----- 501 * void toMsgpack(Packer)(ref Packer packer) const 502 * ----- 503 * This method serializes all members of T object if $(D_KEYWORD class) and $(D_KEYWORD struct) don't implement $(D toMsgpack). 504 * 505 * An object that doesn't implement $(D toMsgpack) is serialized to Array type. 506 * ----- 507 * packer.pack(tuple(true, 1, "Hi!")) // -> '[true, 1, "Hi!"]', not 'ture, 1, "Hi!"' 508 * 509 * struct Foo 510 * { 511 * int num = 10; 512 * string msg = "D!"; 513 * } 514 * packer.pack(Foo()); // -> '[10, "D!"]' 515 * 516 * class Base 517 * { 518 * bool flag = true; 519 * } 520 * class Derived : Base 521 * { 522 * double = 0.5f; 523 * } 524 * packer.pack(new Derived()); // -> '[true, 0.5f]' 525 * ----- 526 * 527 * Params: 528 * object = the content to serialize. 529 * 530 * Returns: 531 * self, i.e. for method chaining. 532 */ 533 ref PackerImpl pack(T)(in T object) if (is(Unqual!T == class)) 534 { 535 if (object is null) 536 return packNil(); 537 538 static if (hasMember!(T, "toMsgpack")) 539 { 540 static if (__traits(compiles, { object.toMsgpack(this, withFieldName_); })) { 541 object.toMsgpack(this, withFieldName_); 542 } else static if (__traits(compiles, { object.toMsgpack(this); })) { // backward compatible 543 object.toMsgpack(this); 544 } else { 545 static assert(0, "Failed to invoke 'toMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 546 } 547 } else { 548 if (auto handler = object.classinfo in packHandlers) { 549 (*handler)(this, cast(void*)&object); 550 return this; 551 } 552 if (T.classinfo !is object.classinfo) { 553 throw new MessagePackException("Can't pack derived class through reference to base class."); 554 } 555 556 packObject!(T)(object); 557 } 558 559 return this; 560 } 561 562 563 /// ditto 564 @trusted 565 ref PackerImpl pack(T)(auto ref T object) if (is(Unqual!T == struct) && 566 !isInstanceOf!(Array, T) && 567 !is(Unqual!T == ExtValue)) 568 { 569 static if (hasMember!(T, "toMsgpack")) 570 { 571 static if (__traits(compiles, { object.toMsgpack(this, withFieldName_); })) { 572 object.toMsgpack(this, withFieldName_); 573 } else static if (__traits(compiles, { object.toMsgpack(this); })) { // backward compatible 574 object.toMsgpack(this); 575 } else { 576 static assert(0, "Failed to invoke 'toMsgpack' on type '" ~ Unqual!T.stringof ~ "'"); 577 } 578 } else static if (isTuple!T) { 579 beginArray(object.field.length); 580 foreach (f; object.field) 581 pack(f); 582 } else { // simple struct 583 if (auto handler = typeid(Unqual!T) in packHandlers) { 584 (*handler)(this, cast(void*)&object); 585 return this; 586 } 587 588 immutable memberNum = SerializingMemberNumbers!(T); 589 if (withFieldName_) 590 beginMap(memberNum); 591 else 592 beginArray(memberNum); 593 594 if (withFieldName_) { 595 foreach (i, f; object.tupleof) { 596 static if (isPackedField!(T.tupleof[i]) && __traits(compiles, { pack(f); })) 597 { 598 pack(getFieldName!(T, i)); 599 pack(f); 600 } 601 } 602 } else { 603 foreach (i, f; object.tupleof) { 604 static if (isPackedField!(T.tupleof[i]) && __traits(compiles, { pack(f); })) 605 pack(f); 606 } 607 } 608 } 609 610 return this; 611 } 612 613 614 void packObject(T)(in T object) if (is(Unqual!T == class)) 615 { 616 alias SerializingClasses!(T) Classes; 617 618 immutable memberNum = SerializingMemberNumbers!(Classes); 619 if (withFieldName_) 620 beginMap(memberNum); 621 else 622 beginArray(memberNum); 623 624 foreach (Class; Classes) { 625 Class obj = cast(Class)object; 626 if (withFieldName_) { 627 foreach (i, f ; obj.tupleof) { 628 static if (isPackedField!(Class.tupleof[i])) { 629 pack(getFieldName!(Class, i)); 630 pack(f); 631 } 632 } 633 } else { 634 foreach (i, f ; obj.tupleof) { 635 static if (isPackedField!(Class.tupleof[i])) 636 pack(f); 637 } 638 } 639 } 640 } 641 642 643 /** 644 * Serializes the arguments as container to stream. 645 * 646 * ----- 647 * packer.packArray(true, 1); // -> [true, 1] 648 * packer.packMap("Hi", 100); // -> ["Hi":100] 649 * ----- 650 * 651 * In packMap, the number of arguments must be even. 652 * 653 * Params: 654 * objects = the contents to serialize. 655 * 656 * Returns: 657 * self, i.e. for method chaining. 658 */ 659 ref PackerImpl packArray(Types...)(auto ref const Types objects) 660 { 661 beginArray(Types.length); 662 foreach (i, T; Types) 663 pack(objects[i]); 664 //pack(objects); // slow :( 665 666 return this; 667 } 668 669 670 /// ditto 671 ref PackerImpl packMap(Types...)(auto ref const Types objects) 672 { 673 static assert(Types.length % 2 == 0, "The number of arguments must be even"); 674 675 beginMap(Types.length / 2); 676 foreach (i, T; Types) 677 pack(objects[i]); 678 679 return this; 680 } 681 682 /** 683 * Packs $(D data) as an extended value of $(D type). 684 * 685 * ---- 686 * packer.packExt(3, bytes); 687 * ---- 688 * 689 * $(D type) must be a signed byte 0-127. 690 * 691 * Params: 692 * type = the application-defined type for the data 693 * data = an array of bytes 694 * 695 * Returns: 696 * seld, i.e. for method chaining. 697 */ 698 ref PackerImpl pack(T)(auto ref const T data) if (is(Unqual!T == ExtValue)) 699 { 700 packExt(data.type, data.data); 701 return this; 702 } 703 704 /** 705 * Packs $(D data) as an extended value of $(D type). 706 * 707 * ---- 708 * packer.packExt(3, bytes); 709 * ---- 710 * 711 * $(D type) must be a signed byte 0-127. 712 * 713 * Params: 714 * type = the application-defined type for the data 715 * data = an array of bytes 716 * 717 * Returns: 718 * seld, i.e. for method chaining. 719 */ 720 ref PackerImpl packExt(in byte type, const ubyte[] data) 721 { 722 ref PackerImpl packExtFixed(int fmt) 723 { 724 store_[0] = cast(ubyte)fmt; 725 store_[1] = type; 726 stream_.put(store_[0 .. 2]); 727 stream_.put(data); 728 return this; 729 } 730 731 // Try packing to a fixed-length type 732 if (data.length == 1) 733 return packExtFixed(Format.EXT + 0); 734 else if (data.length == 2) 735 return packExtFixed(Format.EXT + 1); 736 else if (data.length == 4) 737 return packExtFixed(Format.EXT + 2); 738 else if (data.length == 8) 739 return packExtFixed(Format.EXT + 3); 740 else if (data.length == 16) 741 return packExtFixed(Format.EXT + 4); 742 743 int typeByte = void; 744 if (data.length <= (2^^8)-1) 745 { 746 store_[0] = Format.EXT8; 747 store_[1] = cast(ubyte)data.length; 748 typeByte = 2; 749 750 } else if (data.length <= (2^^16)-1) { 751 store_[0] = Format.EXT16; 752 const temp = convertEndianTo!16(data.length); 753 *cast(ushort*)&store_[Offset] = temp; 754 typeByte = 3; 755 } else if (data.length <= (2^^32)-1) { 756 store_[0] = Format.EXT32; 757 const temp = convertEndianTo!32(data.length); 758 *cast(uint*)&store_[Offset] = temp; 759 typeByte = 5; 760 } else 761 throw new MessagePackException("Data too large to pack as EXT"); 762 763 store_[typeByte] = type; 764 stream_.put(store_[0..typeByte+1]); 765 stream_.put(data); 766 767 return this; 768 } 769 770 771 /** 772 * Serializes the type-information to stream. 773 * 774 * These methods don't serialize contents. 775 * You need to call pack method to serialize contents at your own risk. 776 * ----- 777 * packer.beginArray(3).pack(true, 1); // -> [true, 1, 778 * 779 * // other operation 780 * 781 * packer.pack("Hi!"); // -> [true, 1, "Hi!"] 782 * ----- 783 * 784 * Params: 785 * length = the length of container. 786 * 787 * Returns: 788 * self, i.e. for method chaining. 789 */ 790 ref PackerImpl beginArray(in size_t length) 791 { 792 if (length < 16) { 793 const ubyte temp = Format.ARRAY | cast(ubyte)length; 794 stream_.put(take8from(temp)); 795 } else if (length < 65536) { 796 const temp = convertEndianTo!16(length); 797 798 store_[0] = Format.ARRAY16; 799 *cast(ushort*)&store_[Offset] = temp; 800 stream_.put(store_[0..Offset + ushort.sizeof]); 801 } else { 802 const temp = convertEndianTo!32(length); 803 804 store_[0] = Format.ARRAY32; 805 *cast(uint*)&store_[Offset] = temp; 806 stream_.put(store_[0..Offset + uint.sizeof]); 807 } 808 809 return this; 810 } 811 812 813 /// ditto 814 ref PackerImpl beginMap(in size_t length) 815 { 816 if (length < 16) { 817 const ubyte temp = Format.MAP | cast(ubyte)length; 818 stream_.put(take8from(temp)); 819 } else if (length < 65536) { 820 const temp = convertEndianTo!16(length); 821 822 store_[0] = Format.MAP16; 823 *cast(ushort*)&store_[Offset] = temp; 824 stream_.put(store_[0..Offset + ushort.sizeof]); 825 } else { 826 const temp = convertEndianTo!32(length); 827 828 store_[0] = Format.MAP32; 829 *cast(uint*)&store_[Offset] = temp; 830 stream_.put(store_[0..Offset + uint.sizeof]); 831 } 832 833 return this; 834 } 835 836 837 private: 838 /* 839 * Serializes the nil value. 840 */ 841 ref PackerImpl packNil() 842 { 843 stream_.put(Format.NIL); 844 return this; 845 } 846 } 847 848 849 /// Default serializer 850 alias PackerImpl!(Appender!(ubyte[])) Packer; // should be pure struct? 851 852 853 /** 854 * Helper for $(D Packer) construction. 855 * 856 * Params: 857 * stream = the stream to write. 858 * withFieldName = serialize class / struct with field name 859 * 860 * Returns: 861 * a $(D Packer) object instantiated and initialized according to the arguments. 862 */ 863 PackerImpl!(Stream) packer(Stream)(Stream stream, bool withFieldName = false) 864 { 865 return typeof(return)(stream, withFieldName); 866 } 867 868 869 version(unittest) 870 { 871 package import std.file, core.stdc.string; 872 873 package mixin template DefinePacker() 874 { 875 Packer packer; 876 } 877 878 package mixin template DefineDictionalPacker() 879 { 880 Packer packer = Packer(false); 881 } 882 } 883 884 885 unittest 886 { 887 { // unique value 888 mixin DefinePacker; 889 890 ubyte[] result = [Format.NIL, Format.TRUE, Format.FALSE]; 891 892 packer.pack(null, true, false); 893 foreach (i, value; packer.stream.data) 894 assert(value == result[i]); 895 } 896 { // uint * 897 static struct UTest { ubyte format; ulong value; } 898 899 enum : ulong { A = ubyte.max, B = ushort.max, C = uint.max, D = ulong.max } 900 901 static UTest[][] utests = [ 902 [{Format.UINT8, A}], 903 [{Format.UINT8, A}, {Format.UINT16, B}], 904 [{Format.UINT8, A}, {Format.UINT16, B}, {Format.UINT32, C}], 905 [{Format.UINT8, A}, {Format.UINT16, B}, {Format.UINT32, C}, {Format.UINT64, D}], 906 ]; 907 908 foreach (I, T; TypeTuple!(ubyte, ushort, uint, ulong)) { 909 foreach (i, test; utests[I]) { 910 mixin DefinePacker; 911 912 packer.pack(cast(T)test.value); 913 assert(packer.stream.data[0] == test.format); 914 915 switch (i) { 916 case 0: 917 auto answer = take8from!(T.sizeof * 8)(test.value); 918 assert(memcmp(&packer.stream.data[1], &answer, ubyte.sizeof) == 0); 919 break; 920 case 1: 921 auto answer = convertEndianTo!16(test.value); 922 assert(memcmp(&packer.stream.data[1], &answer, ushort.sizeof) == 0); 923 break; 924 case 2: 925 auto answer = convertEndianTo!32(test.value); 926 assert(memcmp(&packer.stream.data[1], &answer, uint.sizeof) == 0); 927 break; 928 default: 929 auto answer = convertEndianTo!64(test.value); 930 assert(memcmp(&packer.stream.data[1], &answer, ulong.sizeof) == 0); 931 } 932 } 933 } 934 } 935 { // int * 936 static struct STest { ubyte format; long value; } 937 938 enum : long { A = byte.min, B = short.min, C = int.min, D = long.min } 939 940 static STest[][] stests = [ 941 [{Format.INT8, A}], 942 [{Format.INT8, A}, {Format.INT16, B}], 943 [{Format.INT8, A}, {Format.INT16, B}, {Format.INT32, C}], 944 [{Format.INT8, A}, {Format.INT16, B}, {Format.INT32, C}, {Format.INT64, D}], 945 ]; 946 947 foreach (I, T; TypeTuple!(byte, short, int, long)) { 948 foreach (i, test; stests[I]) { 949 mixin DefinePacker; 950 951 packer.pack(cast(T)test.value); 952 assert(packer.stream.data[0] == test.format); 953 954 switch (i) { 955 case 0: 956 auto answer = take8from!(T.sizeof * 8)(test.value); 957 assert(memcmp(&packer.stream.data[1], &answer, byte.sizeof) == 0); 958 break; 959 case 1: 960 auto answer = convertEndianTo!16(test.value); 961 assert(memcmp(&packer.stream.data[1], &answer, short.sizeof) == 0); 962 break; 963 case 2: 964 auto answer = convertEndianTo!32(test.value); 965 assert(memcmp(&packer.stream.data[1], &answer, int.sizeof) == 0); 966 break; 967 default: 968 auto answer = convertEndianTo!64(test.value); 969 assert(memcmp(&packer.stream.data[1], &answer, long.sizeof) == 0); 970 } 971 } 972 } 973 } 974 { // fload, double 975 static if ((real.sizeof == double.sizeof) || !EnableReal) 976 { 977 alias TypeTuple!(float, double, double) FloatingTypes; 978 static struct FTest { ubyte format; double value; } 979 980 static FTest[] ftests = [ 981 {Format.FLOAT, float.min_normal}, 982 {Format.DOUBLE, double.max}, 983 {Format.DOUBLE, double.max}, 984 ]; 985 } 986 else 987 { 988 alias TypeTuple!(float, double, real) FloatingTypes; 989 static struct FTest { ubyte format; real value; } 990 991 static FTest[] ftests = [ 992 {Format.FLOAT, float.min_normal}, 993 {Format.DOUBLE, double.max}, 994 {Format.REAL, real.max}, 995 ]; 996 } 997 998 foreach (I, T; FloatingTypes) { 999 mixin DefinePacker; 1000 1001 packer.pack(cast(T)ftests[I].value); 1002 assert(packer.stream.data[0] == ftests[I].format); 1003 1004 switch (I) { 1005 case 0: 1006 const answer = convertEndianTo!32(_f(cast(T)ftests[I].value).i); 1007 assert(memcmp(&packer.stream.data[1], &answer, float.sizeof) == 0); 1008 break; 1009 case 1: 1010 const answer = convertEndianTo!64(_d(cast(T)ftests[I].value).i); 1011 assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0); 1012 break; 1013 default: 1014 static if (EnableReal) 1015 { 1016 const t = _r(cast(T)ftests[I].value); 1017 const f = convertEndianTo!64(t.fraction); 1018 const e = convertEndianTo!16(t.exponent); 1019 assert(memcmp(&packer.stream.data[1], &f, f.sizeof) == 0); 1020 assert(memcmp(&packer.stream.data[1 + f.sizeof], &e, e.sizeof) == 0); 1021 } 1022 else 1023 { 1024 const answer = convertEndianTo!64(_d(cast(T)ftests[I].value).i); 1025 assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0); 1026 } 1027 } 1028 } 1029 } 1030 { // pointer 1031 static struct PTest 1032 { 1033 ubyte format; 1034 1035 union 1036 { 1037 ulong* p0; 1038 long* p1; 1039 double* p2; 1040 } 1041 } 1042 1043 PTest[] ptests = [PTest(Format.UINT64), PTest(Format.INT64), PTest(Format.DOUBLE)]; 1044 1045 ulong v0 = ulong.max; 1046 long v1 = long.min; 1047 double v2 = double.max; 1048 1049 foreach (I, Index; TypeTuple!("0", "1", "2")) { 1050 mixin DefinePacker; 1051 1052 mixin("ptests[I].p" ~ Index ~ " = &v" ~ Index ~ ";"); 1053 1054 packer.pack(mixin("ptests[I].p" ~ Index)); 1055 assert(packer.stream.data[0] == ptests[I].format); 1056 1057 switch (I) { 1058 case 0: 1059 auto answer = convertEndianTo!64(*ptests[I].p0); 1060 assert(memcmp(&packer.stream.data[1], &answer, ulong.sizeof) == 0); 1061 break; 1062 case 1: 1063 auto answer = convertEndianTo!64(*ptests[I].p1); 1064 assert(memcmp(&packer.stream.data[1], &answer, long.sizeof) == 0); 1065 break; 1066 default: 1067 const answer = convertEndianTo!64(_d(*ptests[I].p2).i); 1068 assert(memcmp(&packer.stream.data[1], &answer, double.sizeof) == 0); 1069 } 1070 } 1071 } 1072 { // enum 1073 enum E : ubyte { A = ubyte.max } 1074 1075 mixin DefinePacker; E e = E.A; 1076 1077 packer.pack(e); 1078 assert(packer.stream.data[0] == Format.UINT8); 1079 1080 auto answer = E.A; 1081 assert(memcmp(&packer.stream.data[1], &answer, (OriginalType!E).sizeof) == 0); 1082 } 1083 { // enum with string 1084 enum E2 : string { A = "test" } 1085 1086 mixin DefinePacker; E2 e = E2.A; 1087 1088 packer.pack(e); 1089 assert(packer.stream.data[0] == (Format.RAW | 0x04)); 1090 } 1091 { // container 1092 static struct CTest { ubyte format; size_t value; } 1093 1094 enum : ulong { A = 16 / 2, B = ushort.max, C = uint.max } 1095 1096 static CTest[][] ctests = [ 1097 [{Format.ARRAY | A, Format.ARRAY | A}, {Format.ARRAY16, B}, {Format.ARRAY32, C}], 1098 [{Format.MAP | A, Format.MAP | A}, {Format.MAP16, B}, {Format.MAP32, C}], 1099 ]; 1100 1101 foreach (I, Name; TypeTuple!("Array", "Map")) { 1102 auto test = ctests[I]; 1103 1104 foreach (i, T; TypeTuple!(ubyte, ushort, uint)) { 1105 mixin DefinePacker; 1106 mixin("packer.begin" ~ Name ~ "(i ? test[i].value : A);"); 1107 1108 assert(packer.stream.data[0] == test[i].format); 1109 1110 switch (i) { 1111 case 0: 1112 auto answer = take8from(test[i].value); 1113 assert(memcmp(&packer.stream.data[0], &answer, ubyte.sizeof) == 0); 1114 break; 1115 case 1: 1116 auto answer = convertEndianTo!16(test[i].value); 1117 assert(memcmp(&packer.stream.data[1], &answer, ushort.sizeof) == 0); 1118 break; 1119 default: 1120 auto answer = convertEndianTo!32(test[i].value); 1121 assert(memcmp(&packer.stream.data[1], &answer, uint.sizeof) == 0); 1122 } 1123 } 1124 } 1125 } 1126 { // user defined 1127 { 1128 static struct S 1129 { 1130 uint num = uint.max; 1131 1132 void toMsgpack(P)(ref P p) const { p.packArray(num); } 1133 } 1134 1135 mixin DefinePacker; S test; 1136 1137 packer.pack(test); 1138 1139 assert(packer.stream.data[0] == (Format.ARRAY | 1)); 1140 assert(packer.stream.data[1] == Format.UINT32); 1141 assert(memcmp(&packer.stream.data[2], &test.num, uint.sizeof) == 0); 1142 } 1143 { 1144 mixin DefinePacker; auto test = tuple(true, false, uint.max); 1145 1146 packer.pack(test); 1147 1148 assert(packer.stream.data[0] == (Format.ARRAY | 3)); 1149 assert(packer.stream.data[1] == Format.TRUE); 1150 assert(packer.stream.data[2] == Format.FALSE); 1151 assert(packer.stream.data[3] == Format.UINT32); 1152 assert(memcmp(&packer.stream.data[4], &test.field[2], uint.sizeof) == 0); 1153 } 1154 { 1155 static class C 1156 { 1157 uint num; 1158 1159 this(uint n) { num = n; } 1160 1161 void toMsgpack(P)(ref P p) const { p.packArray(num); } 1162 } 1163 1164 mixin DefinePacker; C test = new C(ushort.max); 1165 1166 packer.pack(test); 1167 1168 assert(packer.stream.data[0] == (Format.ARRAY | 1)); 1169 assert(packer.stream.data[1] == Format.UINT16); 1170 assert(memcmp(&packer.stream.data[2], &test.num, ushort.sizeof) == 0); 1171 } 1172 } 1173 { // simple struct and class 1174 { 1175 static struct Simple 1176 { 1177 uint num = uint.max; 1178 } 1179 1180 static struct SimpleWithNonPacked1 1181 { 1182 uint num = uint.max; 1183 @nonPacked string str = "ignored"; 1184 } 1185 1186 static struct SimpleWithNonPacked2 1187 { 1188 @nonPacked string str = "ignored"; 1189 uint num = uint.max; 1190 } 1191 1192 static struct SimpleWithSkippedTypes 1193 { 1194 int function(int) fn; 1195 int delegate(int) dg; 1196 uint num = uint.max; 1197 } 1198 1199 foreach (Type; TypeTuple!(Simple, SimpleWithNonPacked1, SimpleWithNonPacked2, SimpleWithSkippedTypes)) { 1200 mixin DefinePacker; 1201 1202 Type test; 1203 packer.pack(test); 1204 1205 assert(packer.stream.data[0] == (Format.ARRAY | 1)); 1206 assert(packer.stream.data[1] == Format.UINT32); 1207 assert(memcmp(&packer.stream.data[2], &test.num, uint.sizeof) == 0); 1208 } 1209 } 1210 1211 static class SimpleA 1212 { 1213 bool flag = true; 1214 } 1215 1216 static class SimpleB : SimpleA 1217 { 1218 ubyte type = 100; 1219 } 1220 1221 static class SimpleC : SimpleB 1222 { 1223 uint num = uint.max; 1224 } 1225 1226 static class SimpleCWithNonPacked1 : SimpleB 1227 { 1228 uint num = uint.max; 1229 @nonPacked string str = "ignored"; 1230 } 1231 1232 static class SimpleCWithNonPacked2 : SimpleB 1233 { 1234 @nonPacked string str = "ignored"; 1235 uint num = uint.max; 1236 } 1237 1238 static class SimpleCWithSkippedTypes : SimpleB 1239 { 1240 uint num = uint.max; 1241 int function(int) fn; 1242 int delegate(int) dg; 1243 } 1244 1245 { // from derived class 1246 foreach (Type; TypeTuple!(SimpleC, SimpleCWithNonPacked1, SimpleCWithNonPacked2, SimpleCWithSkippedTypes)) { 1247 mixin DefinePacker; 1248 1249 Type test = new Type(); 1250 packer.pack(test); 1251 1252 assert(packer.stream.data[0] == (Format.ARRAY | 3)); 1253 assert(packer.stream.data[1] == Format.TRUE); 1254 assert(packer.stream.data[2] == 100); 1255 assert(packer.stream.data[3] == Format.UINT32); 1256 assert(memcmp(&packer.stream.data[4], &test.num, uint.sizeof) == 0); 1257 } 1258 } 1259 { // from base class 1260 mixin DefinePacker; SimpleB test = new SimpleC(); 1261 1262 try { 1263 packer.pack(test); 1264 assert(false); 1265 } catch (Exception e) { } 1266 } 1267 } 1268 1269 // ext types 1270 { 1271 byte type = 7; // an arbitrary type value 1272 1273 // fixexts 1274 { 1275 ubyte[16] data; 1276 data[] = 1; 1277 foreach (L; TypeTuple!(1, 2, 4, 8, 16)) 1278 { 1279 mixin DefinePacker; 1280 packer.pack(ExtValue(type, data[0 .. L])); 1281 1282 // format, type, data 1283 assert(packer.stream.data.length == 2 + L); 1284 const l = 2 ^^ (packer.stream.data[0] - Format.EXT); 1285 assert(l == L); 1286 assert(packer.stream.data[1] == type); 1287 assert(packer.stream.data[2 .. 2+l] == data[0 .. L]); 1288 } 1289 } 1290 1291 // ext8 1292 { 1293 foreach (L; TypeTuple!(3, 7, 255)) 1294 { 1295 ubyte[] data = new ubyte[](L); 1296 data[] = 1; 1297 1298 mixin DefinePacker; 1299 packer.pack(ExtValue(type, data[0 .. L])); 1300 1301 // format, length, type, data 1302 assert(packer.stream.data.length == 3 + L); 1303 assert(packer.stream.data[0] == Format.EXT8); 1304 assert(packer.stream.data[1] == L); 1305 assert(packer.stream.data[2] == type); 1306 assert(packer.stream.data[3 .. 3 + L] == data); 1307 } 1308 } 1309 1310 // ext16 1311 { 1312 foreach (L; TypeTuple!(256, (2^^16)-1)) 1313 { 1314 ubyte[] data = new ubyte[](L); 1315 data[] = 1; 1316 1317 mixin DefinePacker; 1318 packer.pack(ExtValue(type, data[0 .. L])); 1319 1320 // format, length, type, data 1321 import std.conv : text; 1322 assert(packer.stream.data.length == 4 + L, text(packer.stream.data.length)); 1323 assert(packer.stream.data[0] == Format.EXT16); 1324 1325 ushort l = convertEndianTo!16(L); 1326 assert(memcmp(&packer.stream.data[1], &l, ushort.sizeof) == 0); 1327 assert(packer.stream.data[3] == type); 1328 assert(packer.stream.data[4 .. 4 + L] == data); 1329 } 1330 } 1331 1332 // ext32 1333 { 1334 foreach (L; TypeTuple!(2^^16, 2^^17)) 1335 { 1336 ubyte[] data = new ubyte[](L); 1337 data[] = 1; 1338 1339 mixin DefinePacker; 1340 packer.pack(ExtValue(type, data[0 .. L])); 1341 1342 // format, length, type, data 1343 import std.conv : text; 1344 assert(packer.stream.data.length == 6 + L, text(packer.stream.data.length)); 1345 assert(packer.stream.data[0] == Format.EXT32); 1346 1347 uint l = convertEndianTo!32(L); 1348 assert(memcmp(&packer.stream.data[1], &l, uint.sizeof) == 0); 1349 assert(packer.stream.data[5] == type); 1350 assert(packer.stream.data[6 .. 6 + L] == data); 1351 } 1352 } 1353 } 1354 }