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