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