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