1 /** 2 Copyright: Copyright (c) 2014 Andrey Penechko. 3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 6 Some code is based on msgpack-d by Masahiro Nakagawa. 7 8 Concise Binary Object Representation (CBOR) for D lang. 9 10 The Concise Binary Object Representation (CBOR) is a data format 11 whose design goals include the possibility of extremely small code 12 size, fairly small message size, and extensibility without the need 13 for version negotiation. These design goals make it different from 14 earlier binary serializations such as ASN.1 and MessagePack. 15 16 Standards: Conforms to RFC 7049. 17 */ 18 19 module cbor; 20 21 private import std.string : format; 22 private import std.traits; 23 private import std.typecons : Flag; 24 25 //version = Cbor_Debug; 26 27 /// Thrown in a case of decoding error. 28 class CborException : Exception 29 { 30 @trusted pure this(string message, string file = __FILE__, size_t line = __LINE__) 31 { 32 super(message, file, line); 33 } 34 } 35 36 //------------------------------------------------------------------------------ 37 // SSSSS TTTTTTT OOOOO RRRRRR AAA GGGG EEEEEEE 38 // SS TTT OO OO RR RR AAAAA GG GG EE 39 // SSSSS TTT OO OO RRRRRR AA AA GG EEEEE 40 // SS TTT OO OO RR RR AAAAAAA GG GG EE 41 // SSSSS TTT OOOO0 RR RR AA AA GGGGGG EEEEEEE 42 //------------------------------------------------------------------------------ 43 44 /// Tagged union for CBOR items. 45 align(1) struct CborValue 46 { 47 align(1): 48 enum Type : ubyte 49 { 50 boolean, 51 nil, 52 undefined, 53 54 posinteger, 55 neginteger, 56 floating, 57 58 array, 59 map, 60 61 raw, 62 text, 63 } 64 65 static union Via 66 { 67 bool boolean; 68 long integer; 69 ulong uinteger; 70 double floating; 71 72 CborValue[] array; 73 CborValue[CborValue] map; 74 75 ubyte[] raw; 76 string text; 77 } 78 79 Type type; 80 Via via; 81 82 /** 83 * Constructs a $(D CborValue) with arguments. 84 * 85 * Params: 86 * value = the real content. 87 * type = the type of value. 88 */ 89 @safe 90 this(Type type = Type.nil) 91 { 92 this.type = type; 93 } 94 95 @safe 96 this(typeof(null)) 97 { 98 this(Type.nil); 99 } 100 101 /// ditto 102 @trusted 103 this(bool value, Type type = Type.boolean) 104 { 105 this(type); 106 via.boolean = value; 107 } 108 109 110 /// ditto 111 @trusted 112 this(T)(T value) if (isIntegral!T) 113 { 114 if (value < 0) 115 { 116 this(Type.neginteger); 117 via.integer = value; 118 } 119 else 120 { 121 this(Type.posinteger); 122 via.uinteger = value; 123 } 124 } 125 126 127 /// ditto 128 @trusted 129 this(T)(T value, Type type = Type.floating) if (isFloatingPoint!T) 130 { 131 this(type); 132 via.floating = value; 133 } 134 135 136 /// ditto 137 @trusted 138 this(CborValue[] value, Type type = Type.array) 139 { 140 this(type); 141 via.array = value; 142 } 143 144 145 /// ditto 146 @trusted 147 this(CborValue[CborValue] value, Type type = Type.map) 148 { 149 this(type); 150 via.map = value; 151 } 152 153 154 /// ditto 155 @trusted 156 this(ubyte[] value, Type type = Type.raw) 157 { 158 this(type); 159 via.raw = value; 160 } 161 162 /// ditto 163 @trusted 164 this(string value, Type type = Type.text) 165 { 166 this(type); 167 via.text = value; 168 } 169 170 171 /** 172 * Converts value to $(D_PARAM T) type. 173 * 174 * Returns: 175 * converted value. 176 * 177 * Throws: 178 * CborException if type is mismatched. 179 * 180 * NOTE: 181 * Current implementation uses cast. 182 */ 183 @property @trusted 184 T as(T)() if (is(Unqual!T == bool)) 185 { 186 if (type != Type.boolean) 187 onCastErrorToFrom!T(type); 188 189 return via.boolean; 190 } 191 192 193 /// ditto 194 @property @trusted 195 T as(T)() if (isIntegral!T && !is(Unqual!T == enum)) 196 { 197 if (type == Type.neginteger) 198 return cast(T)via.integer; 199 200 if (type == Type.posinteger) 201 return cast(T)via.uinteger; 202 203 onCastErrorToFrom!T(type); 204 205 assert(false); 206 } 207 208 209 /// ditto 210 @property @trusted 211 T as(T)() if (isFloatingPoint!T && !is(Unqual!T == enum)) 212 { 213 if (type != Type.floating) 214 onCastErrorToFrom!T(type); 215 216 return cast(T)via.floating; 217 } 218 219 220 /// ditto 221 @property @trusted 222 T as(T)() if (is(Unqual!T == enum)) 223 { 224 return cast(T)as!(OriginalType!T); 225 } 226 227 228 /// ditto 229 @property @trusted 230 T as(T)() if (isArray!T && !is(Unqual!T == enum)) 231 { 232 alias V = typeof(T.init[0]); 233 234 if (type == Type.nil) 235 { 236 static if (isDynamicArray!T) 237 { 238 return null; 239 } 240 else 241 { 242 return T.init; 243 } 244 } 245 246 static if (is(V == ubyte)) 247 { 248 if (type != Type.raw) 249 onCastErrorToFrom!T(type); 250 251 static if (isDynamicArray!T) 252 { 253 return cast(T)via.raw; 254 } 255 else 256 { 257 if (via.raw.length != T.length) 258 onCastErrorToFrom!T(type); 259 260 return cast(T)(via.raw[0 .. T.length]); 261 } 262 } 263 else static if(is(T == string)) 264 { 265 if (type != Type.text) 266 onCastErrorToFrom!T(type); 267 268 static if (isDynamicArray!T) 269 { 270 return cast(T)via.text; 271 } 272 else 273 { 274 if (via.text.length != T.length) 275 onCastErrorToFrom!T(type); 276 277 return cast(T)(via.text[0 .. T.length]); 278 } 279 } 280 else 281 { 282 if (type != Type.array) 283 onCastErrorToFrom!T(type); 284 285 V[] array = new V[via.array.length]; 286 287 foreach (i, elem; via.array) 288 array[i] = elem.as!(V); 289 290 return array; 291 } 292 } 293 294 295 /// ditto 296 @property @trusted 297 T as(T)() if (isAssociativeArray!T) 298 { 299 alias K = typeof(T.init.keys[0]); 300 alias V = typeof(T.init.values[0]); 301 302 if (type == Type.nil) 303 return null; 304 305 if (type != Type.map) 306 onCastErrorToFrom!T(type); 307 308 V[K] map; 309 310 foreach (key, value; via.map) 311 map[key.as!(K)] = value.as!(V); 312 313 return map; 314 } 315 316 317 /// ditto 318 @property @trusted 319 T as(T)() 320 if (is(T == struct) || is(T == class) || isTuple!T) 321 { 322 static if (is(T == class)) 323 if (type == CborValue.Type.nil) 324 return null; 325 326 if (type != CborValue.Type.array) 327 throw new CborException(format("Can not decode %s from %s", T.stringof, type)); 328 329 T obj; 330 331 size_t arrLength = via.array.length; 332 size_t numMembers; 333 334 static if (isTuple!T) 335 numMembers = T.Types.length; 336 else 337 numMembers = numEncodableMembers!T; 338 339 if (arrLength != numMembers) 340 { 341 throw new CborException( 342 format("The number of deserialized members of %s is mismatched."~ 343 " Got %s, while expected %s members", 344 T.stringof, numMembers, arrLength)); 345 } 346 347 static if (isTuple!T) 348 { 349 foreach (i, Type; T.Types) 350 obj.field[i] = via.array[i].as!(Type); 351 } 352 else 353 { // simple struct 354 static if (is(T == class)) 355 obj = new T(); 356 357 foreach(i, ref member; obj.tupleof) 358 { 359 static if (isEncodedField!(typeof(member))) 360 member = via.array[i].as!(typeof(member)); 361 } 362 } 363 364 return obj; 365 } 366 367 368 /// Comparison for equality. 369 @trusted 370 bool opEquals()(auto ref const CborValue other) const 371 { 372 if (type != other.type) 373 return false; 374 375 final switch(other.type) 376 { 377 case Type.boolean: return opEquals(other.via.boolean); 378 case Type.nil: return type == Type.nil; 379 case Type.undefined: return type == Type.undefined; 380 case Type.neginteger: return opEquals(other.via.integer); 381 case Type.posinteger: return opEquals(other.via.uinteger); 382 case Type.floating: return opEquals(other.via.floating); 383 case Type.array: return opEquals(other.via.array); 384 case Type.map: return opEquals(other.via.map); 385 case Type.raw: return opEquals(other.via.raw); 386 case Type.text: return opEquals(other.via.text); 387 } 388 } 389 390 391 /// ditto 392 @trusted 393 bool opEquals(T : bool)(const T other) const 394 { 395 if (type != Type.boolean) 396 return false; 397 398 return via.boolean == other; 399 } 400 401 402 /// ditto 403 @trusted 404 bool opEquals(T)(const T other) const if (isIntegral!T && !is(T == typeof(null))) 405 { 406 static if (isUnsigned!T) 407 { 408 if (type == Type.posinteger) 409 return via.uinteger == other; 410 else 411 return false; 412 } 413 else 414 { 415 if (type == Type.neginteger || type == Type.posinteger) 416 return via.integer == other; 417 else 418 return false; 419 } 420 } 421 422 423 /// ditto 424 @trusted 425 bool opEquals(T)(const T other) const if (isFloatingPoint!T) 426 { 427 if (type != Type.floating) 428 return false; 429 430 return via.floating == other; 431 } 432 433 434 /// ditto 435 @trusted 436 bool opEquals(T)(const typeof(null) other) const if (is(T == typeof(null))) 437 { 438 if (type == Type.array || type == Type.raw || type == Type.text) 439 { 440 return via.raw.length == 0; 441 } 442 else if (type == Type.map) 443 { 444 return via.map.length == 0; 445 } 446 447 return false; 448 } 449 450 451 /// ditto 452 @trusted 453 bool opEquals(T : const CborValue[])(const T other) const if (!is(T == typeof(null))) 454 { 455 if (type != Type.array) 456 return false; 457 458 return via.array == other; 459 } 460 461 /// ditto 462 @trusted 463 bool opEquals(T : const(V)[], V)(const T other) const if (!is(T == typeof(null))) 464 { 465 if (type != Type.array) 466 return false; 467 468 if (other.length != via.array.length) return false; 469 470 auto arr = via.array; 471 472 foreach(i, ref item; other) 473 { 474 if (item != arr[i]) return false; 475 } 476 477 return true; 478 } 479 480 481 /// ditto 482 @trusted 483 bool opEquals(T : const CborValue[CborValue])(const T other) const if (!is(T == typeof(null))) 484 { 485 if (type != Type.map) 486 return false; 487 488 // This comparison is instead of default comparison because 'via.map == other' raises "Access Violation". 489 foreach (key, value; via.map) { 490 if (key in other) { 491 if (other[key] != value) 492 return false; 493 } else { 494 return false; 495 } 496 } 497 498 return true; 499 } 500 501 /// ditto 502 @trusted 503 bool opEquals(T : const V[K], K, V)(const T other) const if (!is(T == typeof(null))) 504 { 505 if (type != Type.map) 506 return false; 507 508 // This comparison is instead of default comparison because 'via.map == other' raises "Access Violation". 509 auto map = via.map; 510 511 if (map.length != other.length) return false; 512 513 foreach (key, value; other) { 514 if (auto thisVal = CborValue(key) in map) { 515 if (*thisVal != value) 516 return false; 517 } else { 518 return false; 519 } 520 } 521 522 return true; 523 } 524 525 526 /// ditto 527 @trusted 528 bool opEquals(T : const(ubyte)[])(const T other) const if (!is(T == typeof(null))) 529 { 530 if (type != Type.raw) 531 return false; 532 533 return via.raw == other; 534 } 535 536 537 /// ditto 538 @trusted 539 bool opEquals(T : string)(const T other) const if (!is(T == typeof(null))) 540 { 541 if (type != Type.text) 542 return false; 543 544 return via.text == other; 545 } 546 547 /// Hashing. 548 size_t toHash() const nothrow @trusted 549 { 550 final switch(type) 551 { 552 case Type.boolean: return typeid(bool).getHash(&via.boolean); 553 case Type.nil: return 0; 554 case Type.undefined: return size_t.max; 555 case Type.neginteger: return typeid(long).getHash(&via.integer); 556 case Type.posinteger: return typeid(ulong).getHash(&via.uinteger); 557 case Type.floating: return typeid(real).getHash(&via.floating); 558 case Type.array: return typeid(CborValue[]).getHash(&via.array); 559 case Type.map: return typeid(CborValue[CborValue]).getHash(&via.map); 560 case Type.raw: return typeid(ubyte[]).getHash(&via.raw); 561 case Type.text: return typeid(string).getHash(&via.text); 562 } 563 } 564 565 /// String representation. 566 string toString() 567 { 568 import std.string : format; 569 final switch(type) 570 { 571 case Type.boolean: return format("CborValue(%s)", via.boolean); 572 case Type.nil: return "CborValue(null)"; 573 case Type.undefined: return "CborValue(undefined)"; 574 case Type.neginteger: return format("CborValue(%s, %s)", type, via.integer); 575 case Type.posinteger: return format("CborValue(%s, %s)", type, via.uinteger); 576 case Type.floating: return format("CborValue(%s, %s)", type, via.floating); 577 case Type.array: return format("CborValue(%s, %s)", type, via.array); 578 case Type.map: return format("CborValue(%s, %s)", type, via.map); 579 case Type.raw: return format("CborValue(%s, %s)", type, via.raw); 580 case Type.text: return format("CborValue(%s, \"%s\")", type, via.text); 581 } 582 } 583 } 584 585 //------------------------------------------------------------------------------ 586 // EEEEEEE NN NN CCCCC OOOOO DDDDD IIIII NN NN GGGG 587 // EE NNN NN CC C OO OO DD DD III NNN NN GG GG 588 // EEEEE NN N NN CC OO OO DD DD III NN N NN GG 589 // EE NN NNN CC C OO OO DD DD III NN NNN GG GG 590 // EEEEEEE NN NN CCCCC OOOO0 DDDDDD IIIII NN NN GGGGGG 591 //------------------------------------------------------------------------------ 592 593 594 private import std.range : isInputRange, isOutputRange, ElementType; 595 private import std.typecons : isTuple; 596 597 /// Encodes value E into output range sink. 598 /// Returns number of bytes written to sink. 599 size_t encodeCbor(R, E)(auto ref R sink, E value) 600 if(isOutputRange!(R, ubyte)) 601 { 602 import std.typecons : isTuple; 603 604 static if (isIntegral!E) 605 { 606 return encodeCborInt(sink, value); 607 } 608 else static if (isFloatingPoint!E) 609 { 610 return encodeCborFloat(sink, value); 611 } 612 else static if (isBoolean!E) 613 { 614 return encodeCborBool(sink, value); 615 } 616 else static if (is(E == typeof(null))) 617 { 618 return encodeCborNull(sink, value); 619 } 620 else static if (isInputRange!E && is(ElementType!E == ubyte)) 621 { 622 return encodeCborRaw(sink, value); 623 } 624 else static if (is(E == string)) 625 { 626 return encodeCborString(sink, value); 627 } 628 else static if (isInputRange!E || isArray!E || isTuple!E || 629 is(E == class) || is(E == struct)) 630 { 631 return encodeCborArray(sink, value); 632 } 633 else static if (isAssociativeArray!E) 634 { 635 return encodeCborMap(sink, value); 636 } 637 else 638 { 639 static assert(false, "Unable to encode " ~ E.stringof); 640 } 641 } 642 643 // Encode integer types as separate type or as part of arrays or map. 644 private size_t encodeLongType(R)(auto ref R sink, ubyte majorType, ulong length) 645 if(isOutputRange!(R, ubyte)) 646 { 647 import std.bitmanip : nativeToBigEndian; 648 import std.array; 649 650 majorType <<= 5; 651 if (length < 24) { 652 putChecked(sink, cast(ubyte)(majorType | length)); 653 return 1; 654 } else if (length <= ubyte.max) { 655 putChecked(sink, cast(ubyte)(majorType | 24)); 656 putChecked(sink, cast(ubyte)length); 657 return 2; 658 } else if (length <= ushort.max) { 659 putChecked(sink, cast(ubyte)(majorType | 25)); 660 putChecked(sink, nativeToBigEndian!ushort(cast(ushort)length)[]); 661 return 3; 662 } else if (length <= uint.max) { 663 putChecked(sink, cast(ubyte)(majorType | 26)); 664 putChecked(sink, nativeToBigEndian!uint(cast(uint)length)[]); 665 return 5; 666 } else { // if (length <= ulong.max) 667 putChecked(sink, cast(ubyte)(majorType | 27)); 668 putChecked(sink, nativeToBigEndian!ulong(cast(ulong)length)[]); 669 return 9; 670 } 671 } 672 673 /// Encodes integer. 674 size_t encodeCborInt(R, E)(auto ref R sink, E value) 675 if(isOutputRange!(R, ubyte) && isIntegral!E) 676 { 677 ulong val; 678 ubyte majorType; 679 680 if (value < 0) { 681 val = -value - 1; 682 majorType = 1; 683 } else { 684 val = value; 685 majorType = 0; 686 } 687 688 return encodeLongType(sink, majorType, val); 689 } 690 691 /// Encodes floating. 692 size_t encodeCborFloat(R, E)(auto ref R sink, E value) 693 if(isOutputRange!(R, ubyte) && isFloatingPoint!E) 694 { 695 import std.bitmanip : nativeToBigEndian; 696 enum majorType = 7 << 5; 697 698 699 static if (is(E == float)) 700 { 701 702 __FloatRep flt; 703 flt.f = value; 704 putChecked(sink, cast(ubyte)(majorType | 26)); 705 putChecked(sink, nativeToBigEndian!uint(flt.u)[]); 706 return 5; 707 } 708 else static if (is(E == double) || is(E == real)) 709 { 710 711 __DoubleRep dbl; 712 dbl.d = cast(double)value; 713 putChecked(sink, cast(ubyte)(majorType | 27)); 714 putChecked(sink, nativeToBigEndian!ulong(dbl.u)[]); 715 return 9; 716 } 717 } 718 719 /// Encodes boolean. 720 size_t encodeCborBool(R, E)(auto ref R sink, E value) 721 if(isOutputRange!(R, ubyte) && isBoolean!E) 722 { 723 if (value) 724 putChecked(sink, cast(ubyte)0xf5); 725 else 726 putChecked(sink, cast(ubyte)0xf4); 727 return 1; 728 } 729 730 /// Encodes null. 731 size_t encodeCborNull(R, E)(auto ref R sink, E value) 732 if(isOutputRange!(R, ubyte) && is(E == typeof(null))) 733 { 734 735 putChecked(sink, cast(ubyte)0xf6); 736 return 1; 737 } 738 739 /// Encodes range of ubytes. 740 size_t encodeCborRaw(R, E)(auto ref R sink, E value) 741 if(isOutputRange!(R, ubyte) && is(ElementType!E == ubyte)) 742 { 743 auto size = encodeLongType(sink, 2, value.length); 744 size += value.length; 745 putChecked(sink, value); 746 return size; 747 } 748 749 /// Encodes string. 750 size_t encodeCborString(R, E)(auto ref R sink, E value) 751 if(isOutputRange!(R, ubyte) && is(E == string)) 752 { 753 auto size = encodeLongType(sink, 3, value.length); 754 size += value.length; 755 putChecked(sink, cast(ubyte[])value); 756 return size; 757 } 758 759 /// Encodes array of any items or a tuple as cbor array. 760 size_t encodeCborArray(R, E)(auto ref R sink, E value) 761 if(isOutputRange!(R, ubyte) && 762 (isInputRange!E || isArray!E || isTuple!E)) 763 { 764 static if (is(E == void[])) // accept [] 765 { 766 return encodeCborArrayHead(sink, 0); 767 } 768 else 769 { 770 auto size = encodeCborArrayHead(sink, value.length); 771 foreach(item; value) 772 size += encodeCbor(sink, item); 773 return size; 774 } 775 } 776 777 /// Encodes structs and classes as cbor array. 778 size_t encodeCborArray(R, A)(auto ref R sink, A aggregate) 779 if(isOutputRange!(R, ubyte) && 780 (is(A == struct) || is(A == class)) && 781 !isTuple!A) 782 { 783 return encodeCborAggregate!(Flag!"WithFieldName".no)(sink, aggregate); 784 } 785 786 /// Encodes asociative array as cbor map. 787 size_t encodeCborMap(R, E)(auto ref R sink, E value) 788 if(isOutputRange!(R, ubyte) && isAssociativeArray!E) 789 { 790 auto size = encodeCborMapHead(sink, value.length); 791 foreach(key, element; value) 792 { 793 size += encodeCbor(sink, key); 794 size += encodeCbor(sink, element); 795 } 796 return size; 797 } 798 799 /// Encodes structs and classes as cbor map. 800 /// Note, that decoding of structs and classes from maps is not supported (yet). 801 size_t encodeCborMap(R, A)(auto ref R sink, A aggregate) 802 if(isOutputRange!(R, ubyte) && 803 (is(A == struct) || is(A == class)) && 804 !isTuple!A) 805 { 806 return encodeCborAggregate!(Flag!"WithFieldName".yes)(sink, aggregate); 807 } 808 809 /// Encode array head with arrayLength elements. 810 /// arrayLength items must follow. 811 size_t encodeCborArrayHead(R)(auto ref R sink, ulong arrayLength) 812 if(isOutputRange!(R, ubyte)) 813 { 814 return encodeLongType(sink, 4, arrayLength); 815 } 816 817 /// Encode map head with mapLength elements. 818 /// mapLength pairs of items must follow. Keys first, then values. 819 size_t encodeCborMapHead(R)(auto ref R sink, ulong mapLength) 820 if(isOutputRange!(R, ubyte)) 821 { 822 return encodeLongType(sink, 5, mapLength); 823 } 824 825 /// Encodes classes and structs. If withFieldName is yes, than value is encoded as map. 826 /// If withFieldName is no, then value is encoded as an array. 827 size_t encodeCborAggregate(Flag!"WithFieldName" withFieldName, R, A)(auto ref R sink, auto ref A aggregate) 828 if (isOutputRange!(R, ubyte) && (is(A == struct) || is(A == class))) 829 { 830 size_t size; 831 static if (is(A == class)) 832 if (aggregate is null) 833 return encodeCbor(sink, null); 834 835 size += encodeCborArrayHead(sink, numEncodableMembers!A); 836 foreach(i, member; aggregate.tupleof) 837 { 838 static if (__traits(compiles, { encodeCbor(sink, member); })) 839 { 840 static if (withFieldName) 841 size += encodeCborString(sink, __traits(identifier, aggregate.tupleof[i])); 842 size += encodeCbor(sink, member); 843 } 844 } 845 return size; 846 } 847 848 //------------------------------------------------------------------------------ 849 // DDDDD EEEEEEE CCCCC OOOOO DDDDD IIIII NN NN GGGG 850 // DD DD EE CC C OO OO DD DD III NNN NN GG GG 851 // DD DD EEEEE CC OO OO DD DD III NN N NN GG 852 // DD DD EE CC C OO OO DD DD III NN NNN GG GG 853 // DDDDDD EEEEEEE CCCCC OOOO0 DDDDDD IIIII NN NN GGGGGG 854 //------------------------------------------------------------------------------ 855 856 857 /// Decodes single value and returns it as CborValue tagged union type. 858 /// Throws CborException if data is not well-formed. 859 /// Note, that ubyte[] and string types are slices of input range if ubyte[] was provided. 860 /// Will modify input range, popping all the bytes of the first item. 861 CborValue decodeCbor(R)(auto ref R input) 862 if(isInputRange!R && is(ElementType!R == ubyte)) 863 { 864 import std.array; 865 import std.bitmanip; 866 867 // tags will be ignored and decoding will restart from here 868 start_label: 869 870 assert(!input.empty); 871 if (input.empty) onInsufficientInput(); 872 873 ubyte item = input.front; 874 input.popFront; 875 876 switch(item) 877 { 878 case 0x00: .. case 0x17: // Integer 0x00..0x17 (0..23) 879 return CborValue(item); 880 case 0x18: // Unsigned integer (one-byte uint8_t follows) 881 return CborValue(readInteger!ubyte(input)); 882 case 0x19: // Unsigned integer (two-byte uint16_t follows) 883 return CborValue(readInteger!ushort(input)); 884 case 0x1a: // Unsigned integer (four-byte uint_t follows) 885 return CborValue(readInteger!uint(input)); 886 case 0x1b: // Unsigned integer (eight-byte uint64_t follows) 887 return CborValue(readInteger!ulong(input)); 888 case 0x20: .. case 0x37: // Negative integer -1-0x00..-1-0x17 (-1..-24) 889 return CborValue(cast(byte)(-1 - item + 0x20)); 890 case 0x38: // Negative integer -1-n (one-byte uint8_t for n follows) 891 return CborValue(-1 - cast(long)readInteger!ubyte(input)); 892 case 0x39: // Negative integer -1-n (two-byte uint16_t for n follows) 893 return CborValue(-1 - cast(long)readInteger!ushort(input)); 894 case 0x3a: // Negative integer -1-n (four-byte uint_t for n follows) 895 return CborValue(-1 - cast(long)readInteger!uint(input)); 896 case 0x3b: // Negative integer -1-n (eight-byte uint64_t for n follows) 897 return CborValue(-1 - cast(long)readInteger!ulong(input)); 898 case 0x40: .. case 0x57: // byte string (0x00..0x17 bytes follow) 899 return CborValue(readBytes(input, item - 0x40)); 900 case 0x58: // byte string (one-byte uint8_t for n, and then n bytes follow) 901 return CborValue(readBytes(input, readInteger!ubyte(input))); 902 case 0x59: // byte string (two-byte uint16_t for n, and then n bytes follow) 903 return CborValue(readBytes(input, readInteger!ushort(input))); 904 case 0x5a: // byte string (four-byte uint_t for n, and then n bytes follow) 905 return CborValue(readBytes(input, readInteger!uint(input))); 906 case 0x5b: // byte string (eight-byte uint64_t for n, and then n bytes follow) 907 return CborValue(readBytes(input, readInteger!ulong(input))); 908 case 0x5f: // byte string, byte strings follow, terminated by "break" 909 onUnsupportedTag(item); break; 910 case 0x60: .. case 0x77: // UTF-8 string (0x00..0x17 bytes follow) 911 return CborValue(cast(string)readBytes(input, item - 0x60)); 912 case 0x78: // UTF-8 string (one-byte uint8_t for n, and then n bytes follow) 913 return CborValue(cast(string)readBytes(input, readInteger!ubyte(input))); 914 case 0x79: // UTF-8 string (two-byte uint16_t for n, and then n bytes follow) 915 return CborValue(cast(string)readBytes(input, readInteger!ushort(input))); 916 case 0x7a: // UTF-8 string (four-byte uint_t for n, and then n bytes follow) 917 return CborValue(cast(string)readBytes(input, readInteger!uint(input))); 918 case 0x7b: // UTF-8 string (eight-byte uint64_t for n, and then n bytes follow) 919 return CborValue(cast(string)readBytes(input, readInteger!ulong(input))); 920 case 0x7f: // UTF-8 string, UTF-8 strings follow, terminated by "break" 921 onUnsupportedTag(item); break; 922 case 0x80: .. case 0x97: // array (0x00..0x17 data items follow) 923 return CborValue(readArray(input, item - 0x80)); 924 case 0x98: // array (one-byte uint8_t for n, and then n data items follow) 925 return CborValue(readArray(input, readInteger!ubyte(input))); 926 case 0x99: // array (two-byte uint16_t for n, and then n data items follow) 927 return CborValue(readArray(input, readInteger!ushort(input))); 928 case 0x9a: // array (four-byte uint_t for n, and then n data items follow) 929 return CborValue(readArray(input, readInteger!uint(input))); 930 case 0x9b: // array (eight-byte uint64_t for n, and then n data items follow) 931 return CborValue(readArray(input, readInteger!ulong(input))); 932 case 0x9f: // array, data items follow, terminated by "break" 933 onUnsupportedTag(item); break; 934 case 0xa0: .. case 0xb7: // map (0x00..0x17 pairs of data items follow) 935 return CborValue(readMap(input, item - 0xa0)); 936 case 0xb8: // map (one-byte uint8_t for n, and then n pairs of data items follow) 937 return CborValue(readMap(input, readInteger!ubyte(input))); 938 case 0xb9: // map (two-byte uint16_t for n, and then n pairs of data items follow) 939 return CborValue(readMap(input, readInteger!ushort(input))); 940 case 0xba: // map (four-byte uint_t for n, and then n pairs of data items follow) 941 return CborValue(readMap(input, readInteger!uint(input))); 942 case 0xbb: // map (eight-byte uint64_t for n, and then n pairs of data items follow) 943 return CborValue(readMap(input, readInteger!ulong(input))); 944 case 0xbf: // map, pairs of data items follow, terminated by "break" 945 onUnsupportedTag(item); break; 946 case 0xc0: .. case 0xd7: // (tagged item) ignore 947 goto start_label; 948 case 0xd8: // ignore 1-byte tag 949 dropBytes(input, 1); 950 goto start_label; 951 case 0xd9: // ignore 2-byte tag 952 dropBytes(input, 2); 953 goto start_label; 954 case 0xda: // ignore 4-byte tag 955 dropBytes(input, 4); 956 goto start_label; 957 case 0xdb: // ignore 8-byte tag 958 dropBytes(input, 8); 959 goto start_label; 960 case 0xe0: .. case 0xf3: // (simple value) 961 onUnsupportedTag(item); break; 962 case 0xf4: // False 963 return CborValue(false); 964 case 0xf5: // True 965 return CborValue(true); 966 case 0xf6: // Null 967 return CborValue(null); 968 case 0xf7: // Undefined 969 return CborValue(CborValue.Type.undefined); 970 case 0xf8: // (simple value, one byte follows) 971 onUnsupportedTag(item); break; 972 case 0xf9: // Half-Precision Float (two-byte IEEE 754) 973 __HalfRep hr = {u : readInteger!ushort(input)}; 974 return CborValue(hr.h.get!double); 975 case 0xfa: // Single-Precision Float (four-byte IEEE 754) 976 __FloatRep fr = {u : readInteger!uint(input)}; 977 return CborValue(fr.f); 978 case 0xfb: // Double-Precision Float (eight-byte IEEE 754) 979 __DoubleRep dr = {u : readInteger!ulong(input)}; 980 return CborValue(dr.d); 981 case 0xff: // "break" stop code 982 onUnsupportedTag(item); break; 983 default: 984 onUnsupportedTag(item); 985 } 986 987 assert(false); 988 } 989 990 /// Decodes single cbor value and tries to convert it to requested type. 991 /// If types doesn't match CborException is thrown. 992 /// Note, that ubyte[] and string types are slices of input range if ubyte[] was provided. 993 /// Will modify input range, popping all the elements of T. 994 T decodeCborSingle(T, R)(auto ref R input) 995 if(isInputRange!R && is(ElementType!R == ubyte)) 996 { 997 CborValue value = decodeCbor(input); 998 return value.as!T; 999 } 1000 1001 /// Decodes single cbor value and tries to convert it to requested type. 1002 /// If types doesn't match CborException is thrown. 1003 /// Note, that this version will dup all arrays for you. 1004 /// Will modify input range, popping all the elements of T. 1005 T decodeCborSingleDup(T, R)(auto ref R input) 1006 if(isInputRange!R && is(ElementType!R == ubyte)) 1007 { 1008 CborValue value = decodeCbor(input); 1009 1010 static if (is(T == E[], E)) 1011 return value.as!T.dup; 1012 else 1013 return value.as!T; 1014 } 1015 1016 //------------------------------------------------------------------------------ 1017 // HH HH EEEEEEE LL PPPPPP EEEEEEE RRRRRR SSSSS 1018 // HH HH EE LL PP PP EE RR RR SS 1019 // HHHHHHH EEEEE LL PPPPPP EEEEE RRRRRR SSSSS 1020 // HH HH EE LL PP EE RR RR SS 1021 // HH HH EEEEEEE LLLLLLL PP EEEEEEE RR RR SSSSS 1022 //------------------------------------------------------------------------------ 1023 1024 private void putChecked(R, E)(ref R sink, const auto ref E e) 1025 { 1026 import std.range : put, hasLength; 1027 version(Cbor_Debug) 1028 static if (hasLength!R) 1029 { 1030 size_t elemSize; 1031 1032 static if (hasLength!E) 1033 elemSize = e.length * ElementType!E.sizeof; 1034 else 1035 elemSize = E.sizeof; 1036 1037 assert(sink.length >= elemSize, 1038 format("Provided sink length is to small. Sink.$ %s < Data.$ %s", sink.length, elemSize)); 1039 } 1040 put(sink, e); 1041 } 1042 1043 private T readInteger(T, R)(auto ref R input) 1044 { 1045 return readN!(T.sizeof, T, R)(input); 1046 } 1047 1048 private T readN(ubyte size, T, R)(auto ref R input) 1049 if(isInputRange!R && is(ElementType!R == ubyte)) 1050 { 1051 import std.algorithm : copy, take; 1052 import std.bitmanip : bigEndianToNative; 1053 import std.range : dropExactly; 1054 1055 static assert(T.sizeof == size); 1056 static assert(size > 0); 1057 if (input.length < size) onInsufficientInput(); 1058 1059 ubyte[size] data; 1060 1061 copy(take(input, size), data[]); 1062 input = input.dropExactly(size); 1063 T result = bigEndianToNative!(T, size)(data); 1064 1065 return result; 1066 } 1067 1068 // Drops exactly length bytes from input range. 1069 // If there is not sufficient bytes in input, CborException is thrown. 1070 private void dropBytes(R)(auto ref R input, ulong length) 1071 if(isInputRange!R && is(ElementType!R == ubyte)) 1072 { 1073 import std.range : dropExactly; 1074 if (input.length < length) onInsufficientInput(); 1075 input = input.dropExactly(cast(size_t)length); 1076 } 1077 1078 // Reads byte array from input range. On 32-bit can read up to uint.max bytes. 1079 // If ubyte[] is passed as input, a slice will be returned. 1080 // Make sure to dup array when input buffer is reused. 1081 private ubyte[] readBytes(R)(auto ref R input, ulong length) 1082 if(isInputRange!R && is(ElementType!R == ubyte)) 1083 { 1084 import std.algorithm : take; 1085 import std.array; 1086 if (input.length < length) onInsufficientInput(); 1087 1088 static if (size_t.sizeof < ulong.sizeof) 1089 if (length > size_t.max) 1090 throw new CborException(format("Array size is too big %s", length)); 1091 1092 size_t dataLength = cast(size_t)length; 1093 ubyte[] result; 1094 static if (is(R == ubyte[])) 1095 { 1096 result = input[0..dataLength]; 1097 input = input[dataLength..$]; 1098 } 1099 else 1100 { 1101 result = take(input, dataLength).array; 1102 } 1103 1104 return result; 1105 } 1106 1107 // Read array of 'length' items into CborValue[]. 1108 private CborValue[] readArray(R)(auto ref R input, ulong length) 1109 if(isInputRange!R && is(ElementType!R == ubyte)) 1110 { 1111 static if (size_t.sizeof < ulong.sizeof) 1112 if (length > size_t.max) 1113 throw new CborException(format("Array size is too big %s", length)); 1114 1115 size_t arrayLength = cast(size_t)length; 1116 CborValue[] result = new CborValue[arrayLength]; // can use uninitializedArray 1117 foreach(ref elem; result) 1118 { 1119 elem = decodeCbor(input); 1120 } 1121 1122 return result; 1123 } 1124 1125 // length - num of pairs 1126 private CborValue[CborValue] readMap(R)(auto ref R input, ulong length) 1127 if(isInputRange!R && is(ElementType!R == ubyte)) 1128 { 1129 static if (size_t.sizeof < ulong.sizeof) 1130 if (length > size_t.max) 1131 throw new CborException(format("Map size is too big: %s", length)); 1132 1133 size_t mapLength = cast(size_t)length; 1134 CborValue[CborValue] result; // can use uninitializedArray 1135 foreach(i; 0..mapLength) 1136 { 1137 auto key = decodeCbor(input); 1138 if (key in result) throw new CborException(format("duplicate key %s in map found", key)); 1139 result[key] = decodeCbor(input); 1140 } 1141 1142 return result; 1143 } 1144 1145 private import std.numeric : CustomFloat; 1146 private union __HalfRep { CustomFloat!16 h; ushort u; string toString(){return format("__HalfRep(%s, %x)", h, u);};} 1147 private union __FloatRep { float f; uint u; string toString(){return format("__FloatRep(%s, %x)", f, u);};} 1148 private union __DoubleRep { double d; ulong u; string toString(){return format("__DoubleRep(%s, %x)", d, u);};} 1149 1150 private template isEncodedField(T) 1151 { 1152 enum isEncodedField = __traits(compiles, { encodeCbor((ubyte[]).init, T.init); }); 1153 } 1154 1155 template numEncodableMembers(alias T) 1156 { 1157 enum numEncodableMembers = numEncodableMembersImpl!(T.tupleof); 1158 } 1159 1160 private template numEncodableMembersImpl(members ...) 1161 { 1162 static if (members.length == 0) 1163 enum numEncodableMembersImpl = 0; 1164 else 1165 enum numEncodableMembersImpl = 1166 cast(int)__traits(compiles, { encodeCbor(cast(ubyte[])null, members[0].init); }) + 1167 numEncodableMembersImpl!(members[1..$]); 1168 } 1169 1170 //------------------------------------------------------------------------------ 1171 // TTTTTTT EEEEEEE SSSSS TTTTTTT SSSSS 1172 // TTT EE SS TTT SS 1173 // TTT EEEEE SSSSS TTT SSSSS 1174 // TTT EE SS TTT SS 1175 // TTT EEEEEEE SSSSS TTT SSSSS 1176 //------------------------------------------------------------------------------ 1177 1178 1179 unittest // positive integer 1180 { 1181 CborValue val = CborValue(22); 1182 assert(val == 22); 1183 assert(val.as!ubyte == 22); 1184 assert(val.as!ushort == 22); 1185 assert(val.as!uint == 22); 1186 assert(val.as!ulong == 22); 1187 assert(val.as!byte == 22); 1188 assert(val.as!short == 22); 1189 assert(val.as!int == 22); 1190 assert(val.as!long == 22); 1191 assert(val.type == CborValue.Type.posinteger); 1192 1193 val = CborValue(ulong.max); 1194 assert(val == ulong.max); 1195 assert(val.type == CborValue.Type.posinteger); 1196 } 1197 1198 unittest // negative integer 1199 { 1200 CborValue val = CborValue(-22); 1201 assert(val == -22); 1202 assert(val != 22); 1203 assert(val.as!byte == -22); 1204 assert(val.as!short == -22); 1205 assert(val.as!int == -22); 1206 assert(val.as!long == -22); 1207 assert(val.as!ulong == cast(ulong)-22); 1208 assert(val.type == CborValue.Type.neginteger); 1209 } 1210 1211 unittest // floating point 1212 { 1213 CborValue val = CborValue(-22.0f); 1214 assert(val == -22f); 1215 assert(val.as!real == -22f); 1216 assert(val.as!double == -22f); 1217 assert(val.as!float == -22f); 1218 assert(val.type == CborValue.Type.floating); 1219 } 1220 1221 unittest // boolean 1222 { 1223 CborValue val = CborValue(true); 1224 assert(val == true); 1225 assert(val.as!bool == true); 1226 assert(val.type == CborValue.Type.boolean); 1227 1228 val = CborValue(false); 1229 assert(val == false); 1230 assert(val.as!bool == false); 1231 assert(val.type == CborValue.Type.boolean); 1232 } 1233 1234 unittest // undefined 1235 { 1236 CborValue val = CborValue(CborValue.Type.undefined); 1237 assert(val == CborValue(CborValue.Type.undefined)); 1238 assert(val.type == CborValue.Type.undefined); 1239 } 1240 1241 unittest // null value 1242 { 1243 CborValue val = CborValue(null); 1244 assert(val == CborValue(null)); // prefer 1245 assert(val.as!(ubyte[]) == null); 1246 assert(val.as!(ubyte[3]) == [0,0,0]); 1247 assert(val == CborValue(CborValue.Type.nil)); 1248 assert(val.type == CborValue.Type.nil); 1249 } 1250 1251 unittest // array 1252 { 1253 CborValue val = CborValue([CborValue(1), CborValue("string"), CborValue(true), CborValue(null)]); 1254 assert(val == [CborValue(1), CborValue("string"), CborValue(true), CborValue(null)]); 1255 assert(val.type == CborValue.Type.array); 1256 } 1257 1258 unittest // map 1259 { 1260 CborValue val = CborValue([CborValue("a") : CborValue(42)]); 1261 assert(val == [CborValue("a") : CborValue(42)]); 1262 assert(val.type == CborValue.Type.map); 1263 } 1264 1265 unittest // byte string 1266 { 1267 CborValue val = CborValue(cast(ubyte[])[1, 2, 3, 4, 5]); 1268 assert(val == cast(ubyte[])[1, 2, 3, 4, 5]); 1269 assert(val.type == CborValue.Type.raw); 1270 } 1271 1272 unittest // string 1273 { 1274 CborValue val = CborValue("hello"); 1275 assert(val == "hello"); 1276 assert(val.type == CborValue.Type.text); 1277 } 1278 1279 // Testing helpers 1280 version(unittest) 1281 { 1282 private ubyte[1024] buf; 1283 1284 private ubyte[] getEncoded(T)(T value) 1285 { 1286 return buf[0..encodeCbor(buf[], value)]; 1287 } 1288 1289 private string toHexString(ubyte[] arr) 1290 { 1291 return format("0x%(%02x%)", arr); 1292 } 1293 private string encodedString(T)(T value) 1294 { 1295 return toHexString(getEncoded(value)); 1296 } 1297 private void printEncoded(T)(T value) 1298 { 1299 import std.stdio : writeln; 1300 encodedString(value).writeln; 1301 } 1302 private void cmpEncoded(T)(T value, string encodedStr) 1303 { 1304 assert(encodedString(value) == encodedStr); 1305 } 1306 } 1307 1308 unittest // encoding 1309 { 1310 // Test vectors 1311 cmpEncoded(0, "0x00"); 1312 cmpEncoded(1, "0x01"); 1313 cmpEncoded(10, "0x0a"); 1314 cmpEncoded(23, "0x17"); 1315 cmpEncoded(24, "0x1818"); 1316 cmpEncoded(25, "0x1819"); 1317 cmpEncoded(100, "0x1864"); 1318 cmpEncoded(1000, "0x1903e8"); 1319 cmpEncoded(1000000, "0x1a000f4240"); 1320 cmpEncoded(1000000000000, "0x1b000000e8d4a51000"); 1321 1322 //bignums 1323 //cmpEncoded(18446744073709551615, "0x1bffffffffffffffff"); 1324 //cmpEncoded(18446744073709551616, "0xc249010000000000000000"); 1325 //cmpEncoded(-18446744073709551616, "0x3bffffffffffffffff"); 1326 //cmpEncoded(-18446744073709551617, "0xc349010000000000000000"); 1327 cmpEncoded(-1, "0x20"); 1328 cmpEncoded(-10, "0x29"); 1329 cmpEncoded(-100, "0x3863"); 1330 cmpEncoded(-1000, "0x3903e7"); 1331 1332 //printEncoded(0.0f, "0xf90000"); // half-float 1333 //printEncoded(-0.0f, "0xf98000"); // half-float 1334 //printEncoded(1.0f, "0xf93c00"); // half-float 1335 cmpEncoded(1.1, "0xfb3ff199999999999a"); 1336 //printEncoded(1.5f, "0xf93e00"); // half-float 1337 //printEncoded(65504.0f, "0xf97bff"); // half-float 1338 cmpEncoded(100000.0f, "0xfa47c35000"); 1339 cmpEncoded(3.4028234663852886e+38f, "0xfa7f7fffff"); 1340 cmpEncoded(1.0e+300, "0xfb7e37e43c8800759c"); 1341 //printEncoded(5.960464477539063e-8f, "0xf90001"); // half-float 1342 //printEncoded(0.00006103515625f, "0xf90400"); // half-float 1343 //printEncoded(-4.0f, "0xf9c400"); // half-float 1344 cmpEncoded(-4.1, "0xfbc010666666666666"); 1345 cmpEncoded(1.0f/0, "0xfa7f800000"); // Infinity 1346 //cmpEncoded(NaN, "0xfa7fc00000"); // produces 0xfa7fe00000 1347 cmpEncoded(-1.0f/0, "0xfaff800000"); // -Infinity 1348 cmpEncoded(1.0/0, "0xfb7ff0000000000000"); // Infinity 1349 //cmpEncoded(NaN, "0xfb7ff8000000000000"); // produces 0xfb7ffc000000000000 1350 cmpEncoded(-1.0/0, "0xfbfff0000000000000");// -Infinity 1351 1352 cmpEncoded(false, "0xf4"); 1353 cmpEncoded(true, "0xf5"); 1354 cmpEncoded(null, "0xf6"); 1355 1356 // raw arrays, ubyte[] 1357 cmpEncoded(cast(ubyte[])[], "0x40"); 1358 cmpEncoded(cast(ubyte[])[1, 2, 3, 4], "0x4401020304"); 1359 1360 // strings 1361 cmpEncoded("", "0x60"); 1362 cmpEncoded("a", "0x6161"); 1363 cmpEncoded("IETF", "0x6449455446"); 1364 cmpEncoded("\"\\", "0x62225c"); 1365 cmpEncoded("\u00fc", "0x62c3bc"); 1366 cmpEncoded("\u6c34", "0x63e6b0b4"); 1367 //cmpEncoded("\ud800\udd51", "0x64f0908591"); // invalid character 1368 1369 import std.typecons : tuple; 1370 // arrays 1371 cmpEncoded([], "0x80"); 1372 cmpEncoded([1, 2, 3], "0x83010203"); 1373 cmpEncoded(tuple(1, [2, 3], [4, 5]), "0x8301820203820405"); 1374 enum arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1375 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]; 1376 cmpEncoded(arr, "0x98190102030405060708090a0b0c0d0e0f101112131415161718181819"); 1377 1378 // AA 1379 uint[uint] emptyAA; 1380 cmpEncoded(emptyAA, "0xa0"); 1381 cmpEncoded([1:2, 3:4], "0xa201020304"); 1382 1383 //cmpEncoded({"a": 1, "b": [2, 3]}, "0xa26161016162820203"); 1384 //cmpEncoded(["a", {"b": "c"}], "0x826161a161626163"); 1385 cmpEncoded(["a": "A", "b": "B", "c":"C", "d": "D", "e": "E"], 1386 "0xa56161614161626142616361436164614461656145"); 1387 //cmpEncoded((_ h'0102', h'030405'), "0x5f42010243030405ff"); 1388 //cmpEncoded((_ "strea", "ming"), "0x7f657374726561646d696e67ff"); 1389 //cmpEncoded([_ ], "0x9fff"); 1390 //cmpEncoded([_ 1, [2, 3], [_ 4, 5]], "0x9f018202039f0405ffff"); 1391 //cmpEncoded([_ 1, [2, 3], [4, 5]], "0x9f01820203820405ff"); 1392 //cmpEncoded([1, [2, 3], [_ 4, 5]], "0x83018202039f0405ff"); 1393 //cmpEncoded([1, [_ 2, 3], [4, 5]], "0x83019f0203ff820405"); 1394 //cmpEncoded([_ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1395 // 16, 17, 18, 19, 20, 21, 22, 23, 24, 25], 1396 // "0x9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff"); 1397 //cmpEncoded({_ "a": 1, "b": [_ 2, 3]}, "0xbf61610161629f0203ffff"); 1398 //cmpEncoded(["a", {_ "b": "c"}], "0x826161bf61626163ff"); 1399 //cmpEncoded({_ "Fun": true, "Amt": -2}, "0xbf6346756ef563416d7421ff"); 1400 1401 cmpEncoded(cast(ubyte)42, "0x182a"); 1402 cmpEncoded(cast(ushort)42, "0x182a"); 1403 cmpEncoded(cast(uint)42, "0x182a"); 1404 cmpEncoded(cast(ulong)42, "0x182a"); 1405 cmpEncoded(cast(byte)42, "0x182a"); 1406 cmpEncoded(cast(short)42, "0x182a"); 1407 cmpEncoded(cast(int)42, "0x182a"); 1408 cmpEncoded(cast(long)42, "0x182a"); 1409 } 1410 1411 unittest // decoding decodeCbor 1412 { 1413 import std.range : only, chain, iota; 1414 import std.exception; 1415 import std.bitmanip; 1416 import std.array; 1417 import std.algorithm : equal; 1418 1419 alias ntob = nativeToBigEndian; 1420 alias bton = bigEndianToNative; 1421 1422 // Integer 0x00..0x17 (0..23) 1423 foreach(ubyte item; 0x00..0x18) 1424 { 1425 assert(decodeCbor(only(item))==item); 1426 } 1427 1428 // Unsigned integer (one-byte uint8_t follows) 1429 assert(decodeCbor(cast(ubyte[])[0x18, ubyte.max]) == ubyte.max); 1430 assertThrown!CborException(decodeCbor(cast(ubyte[])[0x18])); 1431 1432 // Unsigned integer (two-byte uint16_t follows) 1433 assert(decodeCbor(chain(cast(ubyte[])[0x19], ntob!ushort(1234)[])) == 1234); 1434 assertThrown!CborException(decodeCbor(cast(ubyte[])[0x19])); 1435 1436 // Unsigned integer (four-byte uint_t follows) 1437 assert(decodeCbor(chain(cast(ubyte[])[0x1a], ntob!uint(1234)[])) == 1234); 1438 assertThrown!CborException(decodeCbor(cast(ubyte[])[0x1a])); 1439 1440 // Unsigned integer (eight-byte uint64_t follows) 1441 assert(decodeCbor(chain(cast(ubyte[])[0x1b], ntob!ulong(1234)[])) == 1234); 1442 assertThrown!CborException(decodeCbor(cast(ubyte[])[0x1b])); 1443 1444 // Negative integer -1-0x00..-1-0x17 (-1..-24) 1445 foreach(ubyte item; 0x20..0x38) // [-1..-24] 1446 { 1447 assert(decodeCbor(only(item)) == -cast(long)item+0x1f); 1448 } 1449 1450 // Negative integer -1-n (one-byte uint8_t for n follows) 1451 assert(decodeCbor(cast(ubyte[])[0x38, byte.max-1]) == -byte.max); 1452 assertThrown!CborException(decodeCbor(cast(ubyte[])[0x38])); 1453 1454 // Negative integer -1-n (two-byte uint16_t for n follows) 1455 assert(decodeCbor(chain(cast(ubyte[])[0x39], ntob!short(1234-1)[])) == -1234); 1456 assertThrown!CborException(decodeCbor(cast(ubyte[])[0x39])); 1457 1458 // Negative integer -1-n (four-byte uint_t for n follows) 1459 assert(decodeCbor(chain(cast(ubyte[])[0x3a], ntob!int(1234-1)[])) == -1234); 1460 assertThrown!CborException(decodeCbor(cast(ubyte[])[0x3a])); 1461 1462 // Negative integer -1-n (eight-byte uint64_t for n follows) 1463 assert(decodeCbor(chain(cast(ubyte[])[0x3b], ntob!long(1234-1)[])) == -1234); 1464 assertThrown!CborException(decodeCbor(cast(ubyte[])[0x3b])); 1465 1466 // byte string (0x00..0x17 bytes follow) 1467 foreach(ubyte item; 0x40..0x58) 1468 { 1469 assert(equal( 1470 decodeCbor(chain( 1471 cast(ubyte[])[item], 1472 iota(cast(ubyte)(item - 0x40))) 1473 ).via.raw, 1474 iota(0, item - 0x40) 1475 )); 1476 } 1477 1478 // byte string (one-byte uint8_t for n, and then n bytes follow) 1479 assert(decodeCbor(cast(ubyte[])[0x58, 1, 42]) == cast(ubyte[])[42]); 1480 // byte string (two-byte uint16_t for n, and then n bytes follow) 1481 assert(decodeCbor(cast(ubyte[])[0x59, 0, 1, 42]) == cast(ubyte[])[42]); 1482 // byte string (four-byte uint_t for n, and then n bytes follow) 1483 assert(decodeCbor(cast(ubyte[])[0x5a, 0, 0, 0, 1, 42]) == cast(ubyte[])[42]); 1484 // byte string (eight-byte uint64_t for n, and then n bytes follow) 1485 assert(decodeCbor(cast(ubyte[])[0x5b, 0, 0, 0, 0, 0, 0, 0, 1, 42]) == cast(ubyte[])[42]); 1486 1487 // UTF-8 string (0x00..0x17 bytes follow) 1488 assert(decodeCbor(cast(ubyte[])[0x64] ~ cast(ubyte[])"abcd") == "abcd"); 1489 // UTF-8 string (one-byte uint8_t for n, and then n bytes follow) 1490 assert(decodeCbor(cast(ubyte[])[0x78, 4] ~ cast(ubyte[])"abcd") == "abcd"); 1491 // UTF-8 string (two-byte uint16_t for n, and then n bytes follow) 1492 assert(decodeCbor(cast(ubyte[])[0x79, 0, 4] ~ cast(ubyte[])"abcd") == "abcd"); 1493 // UTF-8 string (four-byte uint_t for n, and then n bytes follow) 1494 assert(decodeCbor(cast(ubyte[])[0x7a, 0, 0, 0, 4] ~ cast(ubyte[])"abcd") == "abcd"); 1495 // UTF-8 string (eight-byte uint64_t for n, and then n bytes follow) 1496 assert(decodeCbor(cast(ubyte[])[0x7b, 0, 0, 0, 0, 0, 0, 0, 4] ~ cast(ubyte[])"abcd") == "abcd"); 1497 // UTF-8 string, UTF-8 strings follow, terminated by "break" 1498 1499 // array (0x00..0x17 data items follow) 1500 assert(decodeCbor(cast(ubyte[])[0x84, 1, 2, 3, 4]) == [1, 2, 3, 4]); 1501 // array (one-byte uint8_t for n, and then n data items follow) 1502 assert(decodeCbor(cast(ubyte[])[0x98, 4, 1, 2, 3, 4]) == [1, 2, 3, 4]); 1503 // array (two-byte uint16_t for n, and then n data items follow) 1504 assert(decodeCbor(cast(ubyte[])[0x99, 0, 4, 1, 2, 3, 4]) == [1, 2, 3, 4]); 1505 // array (four-byte uint_t for n, and then n data items follow) 1506 assert(decodeCbor(cast(ubyte[])[0x9a, 0, 0, 0, 4, 1, 2, 3, 4]) == [1, 2, 3, 4]); 1507 // array (eight-byte uint64_t for n, and then n data items follow) 1508 assert(decodeCbor(cast(ubyte[])[0x9b, 0, 0, 0, 0, 0, 0, 0, 4, 1, 2, 3, 4]) == [1, 2, 3, 4]); 1509 // array, data items follow, terminated by "break" 1510 1511 // map (0x00..0x17 pairs of data items follow) 1512 assert(decodeCbor(getEncoded([1:2, 3:4])) == [1:2, 3:4]); 1513 assert(decodeCbor(cast(ubyte[])[0xa2, 1, 2, 3, 4]) == [1:2, 3:4]); 1514 // map (one-byte uint8_t for n, and then n pairs of data items follow) 1515 assert(decodeCbor(cast(ubyte[])[0xb8, 2, 1, 2, 3, 4]) == [1:2, 3:4]); 1516 // map (two-byte uint16_t for n, and then n pairs of data items follow) 1517 assert(decodeCbor(cast(ubyte[])[0xb9, 0, 2, 1, 2, 3, 4]) == [1:2, 3:4]); 1518 // map (four-byte uint_t for n, and then n pairs of data items follow) 1519 assert(decodeCbor(cast(ubyte[])[0xba, 0, 0, 0, 2, 1, 2, 3, 4]) == [1:2, 3:4]); 1520 // map (eight-byte uint64_t for n, and then n pairs of data items follow) 1521 assert(decodeCbor(cast(ubyte[])[0xbb, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 3, 4]) == [1:2, 3:4]); 1522 1523 // False 1524 assert(decodeCbor(cast(ubyte[])[0xf4]) == false); 1525 // True 1526 assert(decodeCbor(cast(ubyte[])[0xf5]) == true); 1527 // Null 1528 assert(decodeCbor(cast(ubyte[])[0xf6]) == CborValue(null)); 1529 // Undefined 1530 assert(decodeCbor(cast(ubyte[])[0xf7]) == CborValue(CborValue.Type.undefined)); 1531 1532 // (simple value, one byte follows) 0xf8 1533 1534 // Half-Precision Float (two-byte IEEE 754) 1535 __HalfRep hr = {h : CustomFloat!16(1.234f)}; 1536 assert(decodeCbor(cast(ubyte[])[0xf9] ~ 1537 ntob!ushort((hr.u))[]) == CborValue(CustomFloat!16(1.234f).get!float)); 1538 1539 // Single-Precision Float (four-byte IEEE 754) 1540 assert(decodeCbor(cast(ubyte[])[0xfa] ~ 1541 ntob!uint((cast(__FloatRep)1.234f).u)[]) == CborValue(1.234f)); 1542 1543 // Double-Precision Float (eight-byte IEEE 754) 1544 assert(decodeCbor(cast(ubyte[])[0xfb] ~ 1545 ntob!ulong((cast(__DoubleRep)1.234).u)[]) == CborValue(1.234)); 1546 } 1547 1548 unittest // test tag ignoring 1549 { 1550 assert(decodeCbor(cast(ubyte[])[0xc0, 0x18, ubyte.max]) == ubyte.max); 1551 1552 assert(decodeCbor(cast(ubyte[])[0xd8, 0, 0x18, ubyte.max]) == ubyte.max); 1553 assert(decodeCbor(cast(ubyte[])[0xd9, 0, 0, 0x18, ubyte.max]) == ubyte.max); 1554 assert(decodeCbor(cast(ubyte[])[0xda, 0, 0, 0, 0, 0x18, ubyte.max]) == ubyte.max); 1555 assert(decodeCbor(cast(ubyte[])[0xdb, 0, 0, 0, 0, 0, 0, 0, 0, 0x18, ubyte.max]) == ubyte.max); 1556 } 1557 1558 /// 1559 unittest // decoding exact 1560 { 1561 static struct Inner 1562 { 1563 int[] array; 1564 string someText; 1565 } 1566 1567 static struct Test1 1568 { 1569 ubyte b; 1570 short s; 1571 uint i; 1572 long l; 1573 float f; 1574 double d; 1575 ubyte[] arr; 1576 string str; 1577 Inner inner; 1578 1579 void fun(){} // not encoded 1580 void* pointer; // not encoded 1581 } 1582 1583 ubyte[1024] buf1; 1584 size_t size; 1585 1586 Test1 test = Test1(42, -120, 111111, -123456789, 0.1234, -0.987654, 1587 cast(ubyte[])[1,2,3,4,5,6,7,8], "It is a test string", 1588 Inner([1,2,3,4,5], "Test of inner struct")); 1589 1590 size = encodeCborArray(buf1[], test); 1591 Test1 result = decodeCborSingle!Test1(buf1[0..size]); 1592 assert(test == result); 1593 1594 import std.typecons : Tuple; 1595 1596 alias TupleVal = Tuple!(int, string, byte, string); 1597 auto testTuple = TupleVal(1, "hello", 56, "there"); 1598 1599 size = encodeCborArray(buf1[], testTuple); 1600 TupleVal resultTuple = decodeCborSingle!TupleVal(buf1[0..size]); 1601 assert(testTuple == resultTuple); 1602 1603 static class Inner2 1604 { 1605 int val; 1606 ulong u; 1607 } 1608 1609 static class Test2 1610 { 1611 ubyte b; 1612 short s; 1613 uint i; 1614 long l; 1615 float f; 1616 double d; 1617 ubyte[] arr; 1618 string str; 1619 Inner2 inner; 1620 } 1621 1622 Test2 testClass = new Test2(); 1623 testClass.b = 42; 1624 testClass.s = -120; 1625 testClass.i = 111111; 1626 testClass.l = -123456789; 1627 testClass.f = 0.1234; 1628 testClass.d = -0.987654; 1629 testClass.arr = cast(ubyte[])[1,2,3,4,5,6,7,8]; 1630 testClass.str = "It is a test string"; 1631 testClass.inner = null; 1632 1633 size = encodeCborArray(buf1[], testClass); 1634 ubyte[] encodedBuf = buf1[0..size]; 1635 Test2 resultClass = decodeCborSingle!Test2(encodedBuf); 1636 assert(encodedBuf.length == 0); 1637 1638 foreach(i, m; resultClass.tupleof) 1639 assert(testClass.tupleof[i] == m); 1640 1641 testClass.inner = new Inner2; 1642 testClass.inner.val = -555; 1643 testClass.inner.u = 123456789; 1644 1645 size = encodeCborArray(buf1[], testClass); 1646 resultClass = decodeCborSingle!Test2(buf1[0..size]); 1647 1648 foreach(i, m; resultClass.inner.tupleof) 1649 assert(testClass.inner.tupleof[i] == m); 1650 } 1651 1652 unittest // decoding with dup 1653 { 1654 ubyte[128] buf1; 1655 size_t size; 1656 1657 // with dup 1658 size = encodeCbor(buf1[], cast(ubyte[])[0, 1, 2, 3, 4, 5]); 1659 ubyte[] data = decodeCborSingleDup!(ubyte[])(buf1[0..size]); 1660 buf1[] = 0; 1661 1662 assert(data == [0, 1, 2, 3, 4, 5]); 1663 1664 // without dup 1665 size = encodeCbor(buf1[], cast(ubyte[])[0, 1, 2, 3, 4, 5]); 1666 data = decodeCborSingle!(ubyte[])(buf1[0..size]); 1667 buf1[] = 0; 1668 1669 assert(data == [0, 0, 0, 0, 0, 0]); 1670 1671 // dup is only needed for ubyte[] and string types, 1672 // because they can be sliced from ubyte[] input range 1673 size = encodeCbor(buf1[], [0, 1, 2, 3, 4, 5]); // int array 1674 int[] intData = decodeCborSingle!(int[])(buf1[0..size]); // no dup 1675 buf1[] = 0; 1676 1677 assert(intData == [0, 1, 2, 3, 4, 5]); 1678 1679 // also no slicing will occur if data was encoded as regular array and than 1680 // ubyte[] retreived. integer[] -> ubyte[] cast would occur causing possible data loss. 1681 // size = encodeCbor(buf1[], [0, 1, 2, 3, 4, 5]); // int array 1682 // integer array cannot be decoded as ubyte[], only raw arrays can 1683 // data = decodeCborSingle!(ubyte[])(buf1[0..size]); // CborException: Attempt to cast array to ubyte[] 1684 } 1685 1686 private: 1687 1688 @safe pure 1689 void onCastErrorToFrom(To)(CborValue.Type from, string file = __FILE__, size_t line = __LINE__) 1690 { 1691 throw new CborException(format("Attempt to cast %s to %s", from, typeid(To)), file, line); 1692 } 1693 1694 @safe pure 1695 void onInsufficientInput(string file = __FILE__, size_t line = __LINE__) 1696 { 1697 throw new CborException("Input range is too short", file, line); 1698 } 1699 1700 @safe pure 1701 void onUnsupportedTag(ubyte tag, string file = __FILE__, size_t line = __LINE__) 1702 { 1703 throw new CborException(format("Unsupported tag found: %02x", tag), file, line); 1704 }