1 /** 2 * A low-level $(D_INLINECODE pure @nogc, nothrow, @safe) and $(D_INLINECODE betterC) MessagePack implementation. 3 * 4 * Note: 5 * As this is a low-level implementation certain error checking a some handling 6 * of the MessagePack data format has to be done by the API user. 7 * The following conditions need to be ensured by the user: 8 * $(UL 9 * $(LI When calling $(D_INLINECODE parseType) the compile time type must match the actual data 10 * type or incorrect results will be returned. Use $(D_INLINECODE getType) to verify the type 11 * before calling $(D_INLINECODE parseType).) 12 * $(LI The *fix types have certain maximum and minimum values. Read the source 13 * code and serach for $(D_INLINECODE DebugMsgpackLL) or refer to the MessagePack specification 14 * for details.) 15 * $(LI The $(D_INLINECODE debug=DebugMsgpackLL) debug version can be used to enable debug checks for 16 * these problems.)) 17 * 18 * Requires only $(D_INLINECODE std.bitmanip) for $(D_INLINECODE bigEndianToNative) and $(D_INLINECODE nativeToBigEndian) as 19 * external dependency. 20 * 21 * TODO: 22 * Could try to avoid this dependency. This is only a compile time 23 * dependency anyway though, as these functions are templates and get inlined 24 * into this module. 25 */ 26 module msgpack_ll; 27 28 import std.bitmanip : bigEndianToNative, nativeToBigEndian; 29 30 nothrow @nogc pure @safe: 31 32 /// Most types are handled like this: 33 @safe unittest 34 { 35 ubyte[128] buffer; 36 enum type = MsgpackType.uint8; 37 38 // Serialization 39 formatType!(type)(42, buffer[0 .. DataSize!type]); 40 41 // Now deserialize 42 // Get the type at runtime 43 assert(getType(buffer[0]) == type); 44 // and deserialize specifying the type at compile time 45 const result = parseType!type(buffer[0 .. DataSize!type]); 46 assert(result == 42); 47 } 48 49 /// Values for nil, true8 and false8 are ignored and can be skipped: 50 @safe unittest 51 { 52 ubyte[128] buffer; 53 enum type = MsgpackType.true8; 54 55 // Serialization 56 formatType!(type)(buffer[0 .. DataSize!type]); 57 58 // Now deserialize 59 // Get the type at runtime 60 assert(getType(buffer[0]) == type); 61 // and deserialize specifying the type at compile time 62 const result = parseType!type(buffer[0 .. DataSize!type]); 63 assert(result == true); 64 } 65 66 /// The fixExt types accept an additional extType parameter and data: 67 @safe unittest 68 { 69 ubyte[128] buffer; 70 ubyte[1] value = [1]; 71 enum type = MsgpackType.fixExt1; 72 73 // Serialization 74 formatType!(type)(42, value, buffer[0 .. DataSize!type]); 75 76 // Now deserialize 77 // Get the type at runtime 78 assert(getType(buffer[0]) == type); 79 const result = parseType!type(buffer[0 .. DataSize!type]); 80 // and deserialize specifying the type at compile time 81 assert(result[0] == 42); 82 assert(result[1 .. $] == value); 83 } 84 85 /// The ext types accept an additional extType parameter and data length. 86 @safe unittest 87 { 88 ubyte[128] buffer; 89 enum type = MsgpackType.ext8; 90 91 // Serialization 92 formatType!(type)(10, 42, buffer[0 .. DataSize!type]); 93 94 // Now deserialize 95 // Get the type at runtime 96 assert(getType(buffer[0]) == type); 97 // and deserialize specifying the type at compile time 98 const result = parseType!type(buffer[0 .. DataSize!type]); 99 assert(result.type == 42); 100 assert(result.length == 10); 101 } 102 103 /// Often you'll want to decode multiple possible types: 104 @safe unittest 105 { 106 ulong decodeSomeUint(ubyte[] data) 107 { 108 switch (data[0].getType()) 109 { 110 case MsgpackType.posFixInt: 111 return parseType!(MsgpackType.posFixInt)( 112 data[0 .. DataSize!(MsgpackType.posFixInt)]); 113 case MsgpackType.uint8: 114 return parseType!(MsgpackType.uint8)( 115 data[0 .. DataSize!(MsgpackType.uint8)]); 116 case MsgpackType.uint16: 117 return parseType!(MsgpackType.uint16)( 118 data[0 .. DataSize!(MsgpackType.uint16)]); 119 case MsgpackType.uint32: 120 return parseType!(MsgpackType.uint32)( 121 data[0 .. DataSize!(MsgpackType.uint32)]); 122 case MsgpackType.uint64: 123 return parseType!(MsgpackType.uint64)( 124 data[0 .. DataSize!(MsgpackType.uint64)]); 125 default: 126 throw new Exception("Expected integer type"); 127 } 128 } 129 } 130 131 version (unittest) 132 { 133 debug = DebugMsgpackLL; 134 } 135 136 /** 137 * Enum of MessagePack types. 138 */ 139 enum MsgpackType 140 { 141 nil = 0, /// 142 invalid, /// 143 false8, /// 144 true8, /// 145 bin8, /// 146 bin16, /// 147 bin32, /// 148 ext8, /// 149 ext16, /// 150 ext32, /// 151 float32, /// 152 float64, /// 153 uint8, /// 154 uint16, /// 155 uint32, /// 156 uint64, /// 157 int8, /// 158 int16, /// 159 int32, /// 160 int64, /// 161 fixExt1, /// 162 fixExt2, /// 163 fixExt4, /// 164 fixExt8, /// 165 fixExt16, /// 166 str8, /// 167 str16, /// 168 str32, /// 169 array16, /// 170 array32, /// 171 map16, /// 172 map32, /// 173 negFixInt, /// 174 posFixInt, /// 175 fixMap, /// 176 fixArray, /// 177 fixStr /// 178 } 179 180 /** 181 * Look at the first byte of an object to determine the type. 182 * 183 * Note: For some types it's entirely possible that this byte 184 * also contains data. It needs to be part of the data passed to parseType. 185 */ 186 MsgpackType getType(ubyte value) 187 { 188 if (value <= 0x7f) 189 return MsgpackType.posFixInt; 190 if (value <= 0x8f) 191 return MsgpackType.fixMap; 192 if (value <= 0x9f) 193 return MsgpackType.fixArray; 194 if (value <= 0xbf) 195 return MsgpackType.fixStr; 196 if (value >= 0xe0) 197 return MsgpackType.negFixInt; 198 199 return cast(MsgpackType)(value - 0xc0); 200 } 201 202 /** 203 * Get serialized data size at runtime. $(D_INLINECODE DataSize!()) should be preferred 204 * if the type is known at compile time. 205 */ 206 size_t getDataSize(MsgpackType type) 207 { 208 with (MsgpackType) final switch (type) 209 { 210 case nil: 211 return 1; 212 case invalid: 213 return 1; 214 case false8: 215 return 1; 216 case true8: 217 return 1; 218 case bin8: 219 return 2; 220 case bin16: 221 return 3; 222 case bin32: 223 return 5; 224 case ext8: 225 return 3; 226 case ext16: 227 return 4; 228 case ext32: 229 return 6; 230 case float32: 231 return 5; 232 case float64: 233 return 9; 234 case uint8: 235 return 2; 236 case uint16: 237 return 3; 238 case uint32: 239 return 5; 240 case uint64: 241 return 9; 242 case int8: 243 return 2; 244 case int16: 245 return 3; 246 case int32: 247 return 5; 248 case int64: 249 return 9; 250 case fixExt1: 251 return 3; 252 case fixExt2: 253 return 4; 254 case fixExt4: 255 return 6; 256 case fixExt8: 257 return 10; 258 case fixExt16: 259 return 18; 260 case str8: 261 return 2; 262 case str16: 263 return 3; 264 case str32: 265 return 5; 266 case array16: 267 return 3; 268 case array32: 269 return 5; 270 case map16: 271 return 3; 272 case map32: 273 return 5; 274 case negFixInt: 275 return 1; 276 case posFixInt: 277 return 1; 278 case fixMap: 279 return 1; 280 case fixArray: 281 return 1; 282 case fixStr: 283 return 1; 284 } 285 } 286 287 // This test is kinda useless, but getDataSize is properly tested 288 // at compile time through DataSize! and the other @safe unittests. 289 @safe unittest 290 { 291 for (size_t i = 0; i <= MsgpackType.max; i++) 292 cast(void) getDataSize(cast(MsgpackType) i); 293 } 294 295 /** 296 * Get serialized data size at compile time. 297 */ 298 enum DataSize(MsgpackType type) = getDataSize(type); 299 300 @safe unittest 301 { 302 assert(DataSize!(MsgpackType.posFixInt) == 1); 303 } 304 305 private enum isFixExt(MsgpackType type) = (type == MsgpackType.fixExt1) 306 || (type == MsgpackType.fixExt2) || (type == MsgpackType.fixExt4) 307 || (type == MsgpackType.fixExt8) || (type == MsgpackType.fixExt16); 308 309 /** 310 * Serialization information about an ext type. 311 */ 312 struct ExtType 313 { 314 /// Number of bytes in this extension type 315 size_t length; 316 /// Type information about this extension type; 317 ubyte type; 318 } 319 320 /** 321 * Parses the MessagePack object with specified type. 322 * 323 * Note: 324 * For fixext types returns a ubyte[N] reference to the data input buffer. 325 * The first element in the return value contains the type, the rest of the 326 * array is the ubyte[fixExtLength] part. 327 * 328 * Warning: The type is not verified in this function and this function 329 * will return incorrect results if the type does not match the input data. 330 * 331 * Memory safety is not affected when passing a wrong type. 332 */ 333 auto parseType(MsgpackType type)(ref ubyte[DataSize!type] data) if (!isFixExt!type) 334 { 335 debug (DebugMsgpackLL) 336 assert(type == getType(data[0])); 337 338 // nil 339 static if (type == MsgpackType.nil) 340 { 341 return null; 342 } 343 // boolean 344 else static if (type == MsgpackType.false8) 345 { 346 return false; 347 } 348 else static if (type == MsgpackType.true8) 349 { 350 return true; 351 } 352 // integers 353 else static if (type == MsgpackType.posFixInt) 354 { 355 // Optimize: pos fixnum is a valid ubyte even with type information contained in first byte 356 return data[0]; 357 } 358 else static if (type == MsgpackType.negFixInt) 359 { 360 // Optimize: neg fixnum is a valid byte even with type information contained in first byte 361 return cast(byte) data[0]; 362 } 363 else static if (type == MsgpackType.uint8) 364 { 365 return data[1]; 366 } 367 else static if (type == MsgpackType.uint16) 368 { 369 return bigEndianToNative!ushort(data[1 .. 3]); 370 } 371 else static if (type == MsgpackType.uint32) 372 { 373 return bigEndianToNative!uint(data[1 .. 5]); 374 } 375 else static if (type == MsgpackType.uint64) 376 { 377 return bigEndianToNative!ulong(data[1 .. 9]); 378 } 379 else static if (type == MsgpackType.int8) 380 { 381 return cast(byte) data[1]; 382 } 383 else static if (type == MsgpackType.int16) 384 { 385 return bigEndianToNative!short(data[1 .. 3]); 386 } 387 else static if (type == MsgpackType.int32) 388 { 389 return bigEndianToNative!int(data[1 .. 5]); 390 } 391 else static if (type == MsgpackType.int64) 392 { 393 return bigEndianToNative!long(data[1 .. 9]); 394 } 395 // floating point 396 else static if (type == MsgpackType.float32) 397 { 398 return bigEndianToNative!float(data[1 .. 5]); 399 } 400 else static if (type == MsgpackType.float64) 401 { 402 return bigEndianToNative!double(data[1 .. 9]); 403 } 404 // str 405 else static if (type == MsgpackType.fixStr) 406 { 407 return data[0] & 0x1F; 408 } 409 else static if (type == MsgpackType.str8) 410 { 411 return data[1]; 412 } 413 else static if (type == MsgpackType.str16) 414 { 415 return bigEndianToNative!ushort(data[1 .. 3]); 416 } 417 else static if (type == MsgpackType.str32) 418 { 419 return bigEndianToNative!uint(data[1 .. 5]); 420 } 421 // bin 422 else static if (type == MsgpackType.bin8) 423 { 424 return data[1]; 425 } 426 else static if (type == MsgpackType.bin16) 427 { 428 return bigEndianToNative!ushort(data[1 .. 3]); 429 } 430 else static if (type == MsgpackType.bin32) 431 { 432 return bigEndianToNative!uint(data[1 .. 5]); 433 } 434 // array 435 else static if (type == MsgpackType.fixArray) 436 { 437 return data[0] & 0x0F; 438 } 439 else static if (type == MsgpackType.array16) 440 { 441 return bigEndianToNative!ushort(data[1 .. 3]); 442 } 443 else static if (type == MsgpackType.array32) 444 { 445 return bigEndianToNative!uint(data[1 .. 5]); 446 } 447 // map 448 else static if (type == MsgpackType.fixMap) 449 { 450 return data[0] & 0x0F; 451 } 452 else static if (type == MsgpackType.map16) 453 { 454 return bigEndianToNative!ushort(data[1 .. 3]); 455 } 456 else static if (type == MsgpackType.map32) 457 { 458 return bigEndianToNative!uint(data[1 .. 5]); 459 } 460 // ext 461 else static if (type == MsgpackType.ext8) 462 { 463 return ExtType(data[1], data[2]); 464 } 465 else static if (type == MsgpackType.ext16) 466 { 467 return ExtType(bigEndianToNative!ushort(data[1 .. 3]), data[3]); 468 } 469 else static if (type == MsgpackType.ext32) 470 { 471 return ExtType(bigEndianToNative!uint(data[1 .. 5]), data[5]); 472 } 473 } 474 475 /// ditto 476 ref ubyte[DataSize!type - 1] parseType(MsgpackType type)(ref ubyte[DataSize!type] data) if ( 477 isFixExt!type) 478 { 479 return data[1 .. $]; 480 } 481 482 version (unittest) 483 { 484 void testFormat(MsgpackType type, T)(T value) 485 { 486 ubyte[128] buffer; 487 formatType!(type)(value, buffer[0 .. DataSize!type]); 488 assert(getType(buffer[0]) == type); 489 490 const result = parseType!type(buffer[0 .. DataSize!type]); 491 assert(result == value); 492 } 493 494 void testFormatNoArg(MsgpackType type, T)(T value) 495 { 496 ubyte[128] buffer; 497 formatType!(type)(buffer[0 .. DataSize!type]); 498 assert(getType(buffer[0]) == type); 499 500 const result = parseType!type(buffer[0 .. DataSize!type]); 501 assert(result == value); 502 } 503 } 504 505 /** 506 * Serialize a value to a certain type. 507 */ 508 void formatType(MsgpackType type)(typeof(null) value, ref ubyte[DataSize!type] data) if ( 509 type == MsgpackType.nil) 510 { 511 formatType!type(data); 512 } 513 514 /// ditto 515 void formatType(MsgpackType type)(ref ubyte[DataSize!type] data) if (type == MsgpackType.nil) 516 { 517 data[0] = 0xc0; 518 } 519 520 @safe unittest 521 { 522 testFormat!(MsgpackType.nil)(null); 523 testFormatNoArg!(MsgpackType.nil)(null); 524 } 525 526 /// ditto 527 void formatType(MsgpackType type)(bool value, ref ubyte[DataSize!type] data) if ( 528 type == MsgpackType.false8) 529 { 530 formatType!type(data); 531 } 532 533 /// ditto 534 void formatType(MsgpackType type)(ref ubyte[DataSize!type] data) if (type == MsgpackType.false8) 535 { 536 data[0] = 0xc2; 537 } 538 539 @safe unittest 540 { 541 testFormat!(MsgpackType.false8)(false); 542 testFormatNoArg!(MsgpackType.false8)(false); 543 } 544 545 /// ditto 546 void formatType(MsgpackType type)(bool value, ref ubyte[DataSize!type] data) if ( 547 type == MsgpackType.true8) 548 { 549 formatType!type(data); 550 } 551 552 /// ditto 553 void formatType(MsgpackType type)(ref ubyte[DataSize!type] data) if (type == MsgpackType.true8) 554 { 555 data[0] = 0xc3; 556 } 557 558 @safe unittest 559 { 560 testFormat!(MsgpackType.true8)(true); 561 testFormatNoArg!(MsgpackType.true8)(true); 562 } 563 564 /// ditto 565 void formatType(MsgpackType type)(ubyte value, ref ubyte[DataSize!type] data) if ( 566 type == MsgpackType.posFixInt) 567 { 568 debug (DebugMsgpackLL) 569 assert(value < 0x80); 570 571 // Optimize: pos fixnum is a valid ubyte even with type information contained in first byte 572 data[0] = value; 573 } 574 575 @safe unittest 576 { 577 testFormat!(MsgpackType.posFixInt)(cast(ubyte)(0x80 - 1)); 578 testFormat!(MsgpackType.posFixInt)(ubyte(0)); 579 } 580 581 /// ditto 582 void formatType(MsgpackType type)(byte value, ref ubyte[DataSize!type] data) if ( 583 type == MsgpackType.negFixInt) 584 { 585 debug (DebugMsgpackLL) 586 assert(value >= -32 && value < 0); 587 588 // Optimize: neg fixnum is a valid byte even with type information contained in first byte 589 data[0] = value; 590 } 591 592 @safe unittest 593 { 594 testFormat!(MsgpackType.negFixInt)(cast(byte)-32); 595 testFormat!(MsgpackType.negFixInt)(cast(byte)-1); 596 } 597 598 /// ditto 599 void formatType(MsgpackType type)(ubyte value, ref ubyte[DataSize!type] data) if ( 600 type == MsgpackType.uint8) 601 { 602 data[0] = 0xcc; 603 data[1] = value; 604 } 605 606 @safe unittest 607 { 608 testFormat!(MsgpackType.uint8, ubyte)(ubyte.max); 609 testFormat!(MsgpackType.uint8, ubyte)(0); 610 } 611 612 /// ditto 613 void formatType(MsgpackType type)(ushort value, ref ubyte[DataSize!type] data) if ( 614 type == MsgpackType.uint16) 615 { 616 data[0] = 0xcd; 617 data[1 .. 3] = nativeToBigEndian(value); 618 } 619 620 @safe unittest 621 { 622 testFormat!(MsgpackType.uint16, ushort)(ushort.max); 623 testFormat!(MsgpackType.uint16, ushort)(0); 624 } 625 626 /// ditto 627 void formatType(MsgpackType type)(uint value, ref ubyte[DataSize!type] data) if ( 628 type == MsgpackType.uint32) 629 { 630 data[0] = 0xce; 631 data[1 .. 5] = nativeToBigEndian(value); 632 } 633 634 @safe unittest 635 { 636 testFormat!(MsgpackType.uint32, uint)(uint.max); 637 testFormat!(MsgpackType.uint32, uint)(0); 638 } 639 640 /// ditto 641 void formatType(MsgpackType type)(ulong value, ref ubyte[DataSize!type] data) if ( 642 type == MsgpackType.uint64) 643 { 644 data[0] = 0xcf; 645 data[1 .. 9] = nativeToBigEndian(value); 646 } 647 648 @safe unittest 649 { 650 testFormat!(MsgpackType.uint64, ulong)(ulong.max); 651 testFormat!(MsgpackType.uint64, ulong)(0); 652 } 653 654 /// ditto 655 void formatType(MsgpackType type)(byte value, ref ubyte[DataSize!type] data) if ( 656 type == MsgpackType.int8) 657 { 658 data[0] = 0xd0; 659 data[1] = value; 660 } 661 662 @safe unittest 663 { 664 testFormat!(MsgpackType.int8, byte)(byte.min); 665 testFormat!(MsgpackType.int8, byte)(byte.max); 666 } 667 668 /// ditto 669 void formatType(MsgpackType type)(short value, ref ubyte[DataSize!type] data) if ( 670 type == MsgpackType.int16) 671 { 672 data[0] = 0xd1; 673 data[1 .. 3] = nativeToBigEndian(value); 674 } 675 676 @safe unittest 677 { 678 testFormat!(MsgpackType.int16, short)(short.min); 679 testFormat!(MsgpackType.int16, short)(short.max); 680 } 681 682 /// ditto 683 void formatType(MsgpackType type)(int value, ref ubyte[DataSize!type] data) if ( 684 type == MsgpackType.int32) 685 { 686 data[0] = 0xd2; 687 data[1 .. 5] = nativeToBigEndian(value); 688 } 689 690 @safe unittest 691 { 692 testFormat!(MsgpackType.int32, int)(int.min); 693 testFormat!(MsgpackType.int32, int)(int.max); 694 } 695 696 /// ditto 697 void formatType(MsgpackType type)(long value, ref ubyte[DataSize!type] data) if ( 698 type == MsgpackType.int64) 699 { 700 data[0] = 0xd3; 701 data[1 .. 9] = nativeToBigEndian(value); 702 } 703 704 @safe unittest 705 { 706 testFormat!(MsgpackType.int64, long)(long.min); 707 testFormat!(MsgpackType.int64, long)(long.max); 708 } 709 710 /// ditto 711 void formatType(MsgpackType type)(float value, ref ubyte[DataSize!type] data) if ( 712 type == MsgpackType.float32) 713 { 714 data[0] = 0xca; 715 data[1 .. 5] = nativeToBigEndian(value); 716 } 717 718 @safe unittest 719 { 720 testFormat!(MsgpackType.float32)(0.125); 721 } 722 723 /// ditto 724 void formatType(MsgpackType type)(double value, ref ubyte[DataSize!type] data) if ( 725 type == MsgpackType.float64) 726 { 727 data[0] = 0xcb; 728 data[1 .. 9] = nativeToBigEndian(value); 729 } 730 731 @safe unittest 732 { 733 testFormat!(MsgpackType.float64)(0.125); 734 } 735 736 /// ditto 737 void formatType(MsgpackType type)(ubyte length, ref ubyte[DataSize!type] data) if ( 738 type == MsgpackType.fixStr) 739 { 740 debug (DebugMsgpackLL) 741 assert(length < 32); 742 743 data[0] = 0b10100000 | (length & 0b00011111); 744 } 745 746 @safe unittest 747 { 748 testFormat!(MsgpackType.fixStr)(cast(ubyte) 0); 749 testFormat!(MsgpackType.fixStr)(cast(ubyte) 31); 750 } 751 752 /// ditto 753 void formatType(MsgpackType type)(ubyte length, ref ubyte[DataSize!type] data) if ( 754 type == MsgpackType.str8) 755 { 756 data[0] = 0xd9; 757 data[1] = length; 758 } 759 760 @safe unittest 761 { 762 testFormat!(MsgpackType.str8)(cast(ubyte) 0); 763 testFormat!(MsgpackType.str8)(ubyte.max); 764 } 765 766 /// ditto 767 void formatType(MsgpackType type)(ushort length, ref ubyte[DataSize!type] data) if ( 768 type == MsgpackType.str16) 769 { 770 data[0] = 0xda; 771 data[1 .. 3] = nativeToBigEndian(length); 772 } 773 774 @safe unittest 775 { 776 testFormat!(MsgpackType.str16)(cast(ushort) 0); 777 testFormat!(MsgpackType.str16)(ushort.max); 778 } 779 780 /// ditto 781 void formatType(MsgpackType type)(uint length, ref ubyte[DataSize!type] data) if ( 782 type == MsgpackType.str32) 783 { 784 data[0] = 0xdb; 785 data[1 .. 5] = nativeToBigEndian(length); 786 } 787 788 @safe unittest 789 { 790 testFormat!(MsgpackType.str32)(cast(uint) 0); 791 testFormat!(MsgpackType.str32)(uint.max); 792 } 793 794 /// ditto 795 void formatType(MsgpackType type)(ubyte length, ref ubyte[DataSize!type] data) if ( 796 type == MsgpackType.bin8) 797 { 798 data[0] = 0xc4; 799 data[1] = length; 800 } 801 802 @safe unittest 803 { 804 testFormat!(MsgpackType.bin8)(cast(ubyte) 0); 805 testFormat!(MsgpackType.bin8)(ubyte.max); 806 } 807 808 /// ditto 809 void formatType(MsgpackType type)(ushort length, ref ubyte[DataSize!type] data) if ( 810 type == MsgpackType.bin16) 811 { 812 data[0] = 0xc5; 813 data[1 .. 3] = nativeToBigEndian(length); 814 } 815 816 @safe unittest 817 { 818 testFormat!(MsgpackType.bin16)(cast(ushort) 0); 819 testFormat!(MsgpackType.bin16)(ushort.max); 820 } 821 822 /// ditto 823 void formatType(MsgpackType type)(uint length, ref ubyte[DataSize!type] data) if ( 824 type == MsgpackType.bin32) 825 { 826 data[0] = 0xc6; 827 data[1 .. 5] = nativeToBigEndian(length); 828 } 829 830 @safe unittest 831 { 832 testFormat!(MsgpackType.bin32)(cast(uint) 0); 833 testFormat!(MsgpackType.bin32)(uint.max); 834 } 835 836 /// ditto 837 void formatType(MsgpackType type)(ubyte length, ref ubyte[DataSize!type] data) if ( 838 type == MsgpackType.fixArray) 839 { 840 debug (DebugMsgpackLL) 841 assert(length < 16); 842 843 data[0] = 0b10010000 | (length & 0b00001111); 844 } 845 846 @safe unittest 847 { 848 testFormat!(MsgpackType.fixArray)(cast(ubyte) 0); 849 testFormat!(MsgpackType.fixArray)(cast(ubyte) 15); 850 } 851 852 /// ditto 853 void formatType(MsgpackType type)(ushort length, ref ubyte[DataSize!type] data) if ( 854 type == MsgpackType.array16) 855 { 856 data[0] = 0xdc; 857 data[1 .. 3] = nativeToBigEndian(length); 858 } 859 860 @safe unittest 861 { 862 testFormat!(MsgpackType.array16)(cast(ushort) 0); 863 testFormat!(MsgpackType.array16)(ushort.max); 864 } 865 866 /// ditto 867 void formatType(MsgpackType type)(uint length, ref ubyte[DataSize!type] data) if ( 868 type == MsgpackType.array32) 869 { 870 data[0] = 0xdd; 871 data[1 .. 5] = nativeToBigEndian(length); 872 } 873 874 @safe unittest 875 { 876 testFormat!(MsgpackType.array32)(cast(uint) 0); 877 testFormat!(MsgpackType.array32)(uint.max); 878 } 879 880 /// ditto 881 void formatType(MsgpackType type)(ubyte length, ref ubyte[DataSize!type] data) if ( 882 type == MsgpackType.fixMap) 883 { 884 debug (DebugMsgpackLL) 885 assert(length < 16); 886 887 data[0] = 0b10000000 | (cast(ubyte) length & 0b00001111); 888 } 889 890 @safe unittest 891 { 892 testFormat!(MsgpackType.fixMap)(cast(ubyte) 0); 893 testFormat!(MsgpackType.fixMap)(cast(ubyte) 15); 894 } 895 896 /// ditto 897 void formatType(MsgpackType type)(ushort length, ref ubyte[DataSize!type] data) if ( 898 type == MsgpackType.map16) 899 { 900 data[0] = 0xde; 901 data[1 .. 3] = nativeToBigEndian(length); 902 } 903 904 @safe unittest 905 { 906 testFormat!(MsgpackType.map16)(cast(ushort) 0); 907 testFormat!(MsgpackType.map16)(ushort.max); 908 } 909 910 /// ditto 911 void formatType(MsgpackType type)(uint length, ref ubyte[DataSize!type] data) if ( 912 type == MsgpackType.map32) 913 { 914 data[0] = 0xdf; 915 data[1 .. 5] = nativeToBigEndian(length); 916 } 917 918 @safe unittest 919 { 920 testFormat!(MsgpackType.map32)(cast(uint) 0); 921 testFormat!(MsgpackType.map32)(uint.max); 922 } 923 924 /// ditto 925 void formatType(MsgpackType type)(ubyte extType, ref ubyte[1] value, ref ubyte[DataSize!type] data) if ( 926 type == MsgpackType.fixExt1) 927 { 928 data[0] = 0xd4; 929 data[1] = extType; 930 data[2] = value[0]; 931 } 932 933 version (unittest) 934 { 935 void testFixExt(MsgpackType type, T)(ubyte extType, ref T value) 936 { 937 ubyte[128] buffer; 938 formatType!(type)(extType, value, buffer[0 .. DataSize!type]); 939 assert(getType(buffer[0]) == type); 940 941 const result = parseType!type(buffer[0 .. DataSize!type]); 942 assert(result[0] == extType); 943 assert(result[1 .. $] == value); 944 } 945 } 946 947 @safe unittest 948 { 949 ubyte[1] testData = [42]; 950 testFixExt!(MsgpackType.fixExt1)(ubyte.max, testData); 951 } 952 953 /// ditto 954 void formatType(MsgpackType type)(ubyte extType, ref ubyte[2] value, ref ubyte[DataSize!type] data) if ( 955 type == MsgpackType.fixExt2) 956 { 957 data[0] = 0xd5; 958 data[1] = extType; 959 data[2 .. 4] = value[0 .. 2]; 960 } 961 962 @safe unittest 963 { 964 ubyte[2] testData = [42, 42]; 965 testFixExt!(MsgpackType.fixExt2)(ubyte.max, testData); 966 } 967 968 /// ditto 969 void formatType(MsgpackType type)(ubyte extType, ref ubyte[4] value, ref ubyte[DataSize!type] data) if ( 970 type == MsgpackType.fixExt4) 971 { 972 data[0] = 0xd6; 973 data[1] = extType; 974 data[2 .. 6] = value[0 .. 4]; 975 } 976 977 @safe unittest 978 { 979 ubyte[4] testData = [42, 42, 42, 42]; 980 testFixExt!(MsgpackType.fixExt4)(ubyte.max, testData); 981 } 982 983 /// ditto 984 void formatType(MsgpackType type)(ubyte extType, ref ubyte[8] value, ref ubyte[DataSize!type] data) if ( 985 type == MsgpackType.fixExt8) 986 { 987 data[0] = 0xd7; 988 data[1] = extType; 989 data[2 .. 10] = value[0 .. 8]; 990 } 991 992 @safe unittest 993 { 994 ubyte[8] testData = [42, 42, 42, 42, 42, 42, 42, 42]; 995 testFixExt!(MsgpackType.fixExt8)(ubyte.max, testData); 996 } 997 998 /// ditto 999 void formatType(MsgpackType type)(ubyte extType, ref ubyte[16] value, ref ubyte[DataSize!type] data) if ( 1000 type == MsgpackType.fixExt16) 1001 { 1002 data[0] = 0xd8; 1003 data[1] = extType; 1004 data[2 .. 18] = value[0 .. 16]; 1005 } 1006 1007 @safe unittest 1008 { 1009 ubyte[16] testData = [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 1010 42]; 1011 testFixExt!(MsgpackType.fixExt16)(ubyte.max, testData); 1012 } 1013 1014 version (unittest) 1015 { 1016 void testExt(MsgpackType type, T)(T length, ubyte extType) 1017 { 1018 ubyte[128] buffer; 1019 formatType!(type)(length, extType, buffer[0 .. DataSize!type]); 1020 assert(getType(buffer[0]) == type); 1021 1022 const result = parseType!type(buffer[0 .. DataSize!type]); 1023 assert(result.type == extType); 1024 assert(result.length == length); 1025 } 1026 } 1027 1028 /// ditto 1029 void formatType(MsgpackType type)(ubyte length, ubyte extType, ref ubyte[DataSize!type] data) if ( 1030 type == MsgpackType.ext8) 1031 { 1032 data[0] = 0xc7; 1033 data[1] = length; 1034 data[2] = extType; 1035 } 1036 1037 @safe unittest 1038 { 1039 testExt!(MsgpackType.ext8)(ubyte.max, ubyte.max); 1040 } 1041 1042 /// ditto 1043 void formatType(MsgpackType type)(ushort length, ubyte extType, ref ubyte[DataSize!type] data) if ( 1044 type == MsgpackType.ext16) 1045 { 1046 data[0] = 0xc8; 1047 data[1 .. 3] = nativeToBigEndian(length); 1048 data[3] = extType; 1049 } 1050 1051 @safe unittest 1052 { 1053 testExt!(MsgpackType.ext16)(ushort.max, ubyte.max); 1054 } 1055 1056 /// ditto 1057 void formatType(MsgpackType type)(uint length, ubyte extType, ref ubyte[DataSize!type] data) if ( 1058 type == MsgpackType.ext32) 1059 { 1060 data[0] = 0xc9; 1061 data[1 .. 5] = nativeToBigEndian(length); 1062 data[5] = extType; 1063 } 1064 1065 @safe unittest 1066 { 1067 testExt!(MsgpackType.ext32)(uint.max, ubyte.max); 1068 }