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