1 /** 2 Copyright: Copyright (c) 2014-2016 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 6 Concise Binary Object Representation (CBOR) for D lang. 7 8 The Concise Binary Object Representation (CBOR) is a data format 9 whose design goals include the possibility of extremely small code 10 size, fairly small message size, and extensibility without the need 11 for version negotiation. These design goals make it different from 12 earlier binary serializations such as ASN.1 and MessagePack. 13 14 Standards: Conforms to RFC 7049. 15 */ 16 17 module cbor; 18 19 private import std.string : format; 20 private import std.traits : Unqual, isArray, isAssociativeArray, isBoolean, isDynamicArray, 21 isExpressionTuple, isFloatingPoint, isIntegral, isSomeChar, isStaticArray, isUnsigned; 22 public import std.typecons : Flag, Yes, No; 23 private import std.range : ElementEncodingType, hasLength, save, tee; 24 private import std.conv : to; 25 private import std.utf : byChar; 26 27 version(unittest) { 28 version = Cbor_Debug; 29 } 30 31 //------------------------------------------------------------------------------ 32 // EEEEEEE NN NN CCCCC OOOOO DDDDD IIIII NN NN GGGG 33 // EE NNN NN CC C OO OO DD DD III NNN NN GG GG 34 // EEEEE NN N NN CC OO OO DD DD III NN N NN GG 35 // EE NN NNN CC C OO OO DD DD III NN NNN GG GG 36 // EEEEEEE NN NN CCCCC OOOO0 DDDDDD IIIII NN NN GGGGGG 37 //------------------------------------------------------------------------------ 38 39 private import std.range : isInputRange, isOutputRange, ElementType; 40 private import std.typecons : isTuple; 41 42 //------------------------------------------------------------------------------ 43 // Simple types (int, float, bool, null, undefined, break, simple) 44 //------------------------------------------------------------------------------ 45 46 // Encode integer types as separate type or as part of arrays or map. 47 private size_t encodeLongType(R)(auto ref R sink, ubyte majorType, ulong length) 48 if(isOutputRange!(R, ubyte)) 49 { 50 import std.bitmanip : nativeToBigEndian; 51 52 majorType <<= 5; 53 if (length < 24) { 54 putChecked(sink, cast(ubyte)(majorType | length)); 55 return 1; 56 } else if (length <= ubyte.max) { 57 putChecked(sink, cast(ubyte)(majorType | 24)); 58 putChecked(sink, cast(ubyte)length); 59 return 2; 60 } else if (length <= ushort.max) { 61 putChecked(sink, cast(ubyte)(majorType | 25)); 62 putChecked(sink, nativeToBigEndian!ushort(cast(ushort)length)[]); 63 return 3; 64 } else if (length <= uint.max) { 65 putChecked(sink, cast(ubyte)(majorType | 26)); 66 putChecked(sink, nativeToBigEndian!uint(cast(uint)length)[]); 67 return 5; 68 } else { // if (length <= ulong.max) 69 putChecked(sink, cast(ubyte)(majorType | 27)); 70 putChecked(sink, nativeToBigEndian!ulong(cast(ulong)length)[]); 71 return 9; 72 } 73 } 74 75 /// Encodes integer. 76 size_t encodeCborInt(R, E)(auto ref R sink, E value) 77 if(isOutputRange!(R, ubyte) && isIntegral!E) 78 { 79 ulong val; 80 ubyte majorType; 81 82 if (value < 0) { 83 val = -value - 1; 84 majorType = 1; 85 } else { 86 val = value; 87 majorType = 0; 88 } 89 90 return encodeLongType(sink, majorType, val); 91 } 92 93 /// Encodes floating. 94 size_t encodeCborFloat(R, E)(auto ref R sink, E value) 95 if(isOutputRange!(R, ubyte) && isFloatingPoint!E) 96 { 97 import std.bitmanip : nativeToBigEndian; 98 enum majorType = 7 << 5; 99 100 static if (is(Unqual!E == float)) 101 { 102 __FloatRep flt; 103 flt.f = value; 104 putChecked(sink, cast(ubyte)(majorType | 26)); 105 putChecked(sink, nativeToBigEndian!uint(flt.u)[]); 106 return 5; 107 } 108 else static if (is(Unqual!E == double) || is(Unqual!E == real)) 109 { 110 __DoubleRep dbl; 111 dbl.d = cast(double)value; 112 putChecked(sink, cast(ubyte)(majorType | 27)); 113 putChecked(sink, nativeToBigEndian!ulong(dbl.u)[]); 114 return 9; 115 } 116 } 117 118 /// Encodes boolean. 119 size_t encodeCborBool(R, E)(auto ref R sink, E value) 120 if(isOutputRange!(R, ubyte) && isBoolean!E) 121 { 122 if (value) 123 putChecked(sink, cast(ubyte)0xf5); 124 else 125 putChecked(sink, cast(ubyte)0xf4); 126 return 1; 127 } 128 129 /// Encodes null. 130 size_t encodeCborNull(R)(auto ref R sink) 131 if(isOutputRange!(R, ubyte)) 132 { 133 putChecked(sink, cast(ubyte)0xf6); 134 return 1; 135 } 136 137 /// Encodes undefined. 138 size_t encodeCborUndefined(R)(auto ref R sink) if(isOutputRange!(R, ubyte)) 139 { 140 putChecked(sink, cast(ubyte)0xf7); 141 return 1; 142 } 143 144 /// Encodes break. Use after all items of indefinite-length sequence were encoded. 145 size_t encodeCborBreak(R)(auto ref R sink) if(isOutputRange!(R, ubyte)) 146 { 147 putChecked(sink, cast(ubyte)0xff); 148 return 1; 149 } 150 151 /// Encodes simple. Simple data type is essentially a number that has special meaning. 152 /// Value must lie in range [0..23] or [32..255]. 153 size_t encodeCborSimple(R)(auto ref R sink, ubyte simple) if(isOutputRange!(R, ubyte)) 154 { 155 enum ubyte majorType = 7 << 5; 156 if (simple < 24) { 157 putChecked(sink, cast(ubyte)(majorType | simple)); 158 return 1; 159 } else if (simple > 31) { 160 putChecked(sink, cast(ubyte)0xf8); 161 putChecked(sink, simple); 162 return 2; 163 } else { 164 assert(false, format("Invalid simple value %s", simple)); 165 } 166 } 167 168 //------------------------------------------------------------------------------ 169 // Complex data (byte-string, text-string, array, map) 170 //------------------------------------------------------------------------------ 171 // Headers 172 //------------------------------------------------------------------------------ 173 174 size_t encodeCborBytesHeader(R)(auto ref R sink, ulong bytesLength) if(isOutputRange!(R, ubyte)) 175 { 176 return encodeLongType(sink, 2, bytesLength); 177 } 178 179 size_t encodeCborBytesHeader(R)(auto ref R sink) if(isOutputRange!(R, ubyte)) 180 { 181 putChecked(sink, cast(ubyte)0x5f); 182 return 1; 183 } 184 185 size_t encodeCborStringHeader(R)(auto ref R sink, ulong textLength) if(isOutputRange!(R, ubyte)) 186 { 187 return encodeLongType(sink, 3, textLength); 188 } 189 190 size_t encodeCborStringHeader(R)(auto ref R sink) if(isOutputRange!(R, ubyte)) 191 { 192 putChecked(sink, cast(ubyte)0x7f); 193 return 1; 194 } 195 196 size_t encodeCborArrayHeader(R)(auto ref R sink, ulong arrayLength) if(isOutputRange!(R, ubyte)) 197 { 198 return encodeLongType(sink, 4, arrayLength); 199 } 200 201 size_t encodeCborArrayHeader(R)(auto ref R sink) if(isOutputRange!(R, ubyte)) 202 { 203 putChecked(sink, cast(ubyte)0x9f); 204 return 1; 205 } 206 207 size_t encodeCborMapHeader(R)(auto ref R sink, ulong mapLength) if(isOutputRange!(R, ubyte)) 208 { 209 return encodeLongType(sink, 5, mapLength); 210 } 211 212 size_t encodeCborMapHeader(R)(auto ref R sink) if(isOutputRange!(R, ubyte)) 213 { 214 putChecked(sink, cast(ubyte)0xbf); 215 return 1; 216 } 217 218 //------------------------------------------------------------------------------ 219 // Items 220 //------------------------------------------------------------------------------ 221 222 /// Writes range of ubyte to the sink. Needs to go after a call to encodeCborBytesHeader. 223 /// The length of supplied range must be equal to one provided to encodeCborBytesHeader. 224 size_t encodeCborBytesItems(R, D)(auto ref R sink, D bytes) 225 if(isOutputRange!(R, ubyte) && 226 (isArray!D || isInputRange!D) && is(Unqual!(ElementType!D) == ubyte)) 227 { 228 static if (hasLength!D) { 229 static if (isStaticArray!D) 230 putChecked(sink, bytes[]); 231 else 232 putChecked(sink, bytes); 233 return bytes.length; 234 } else { 235 size_t count; 236 putChecked(sink, tee!((a){++count;})(bytes)); 237 return count; 238 } 239 } 240 241 //------------------------------------------------------------------------------ 242 // Tag 243 //------------------------------------------------------------------------------ 244 245 /// 246 size_t encodeCborTag(R)(auto ref R sink, ulong value) if(isOutputRange!(R, ubyte)) 247 { 248 return encodeLongType(sink, 6, value); 249 } 250 251 //------------------------------------------------------------------------------ 252 // SSSSS EEEEEEE RRRRRR IIIII AAA LL IIIII ZZZZZ AAA TTTTTTT IIIII OOOOO NN NN 253 // SS EE RR RR III AAAAA LL III ZZ AAAAA TTT III OO OO NNN NN 254 // SSSSS EEEEE RRRRRR III AA AA LL III ZZ AA AA TTT III OO OO NN N NN 255 // SS EE RR RR III AAAAAAA LL III ZZ AAAAAAA TTT III OO OO NN NNN 256 // SSSSS EEEEEEE RR RR IIIII AA AA LLLLLLL IIIII ZZZZZ AA AA TTT IIIII OOOO0 NN NN 257 // 258 //------------------------------------------------------------------------------ 259 260 /// Encodes value E into output range sink. 261 /// Returns number of bytes written to sink. 262 /// If flatten flag is yes then static arrays and structs will be encoded in place without headers. 263 size_t encodeCbor(Flag!"Flatten" flatten = No.Flatten, R, E)(auto ref R sink, auto ref const E value) 264 if(isOutputRange!(R, ubyte)) 265 { 266 import std.typecons : isTuple; 267 268 static if (isIntegral!E) 269 { 270 return encodeCborInt(sink, value); 271 } 272 else static if (isSomeChar!E) 273 { 274 return encodeCborInt(sink, cast(ulong)value); 275 } 276 else static if (isFloatingPoint!E) 277 { 278 return encodeCborFloat(sink, value); 279 } 280 else static if (isBoolean!E) 281 { 282 return encodeCborBool(sink, value); 283 } 284 else static if (is(Unqual!E == typeof(null))) 285 { 286 return encodeCborNull(sink); 287 } 288 else static if ((isArray!E || isInputRange!E) && is(Unqual!(ElementType!E) == ubyte)) 289 { 290 return encodeCborBytes!(flatten)(sink, value); 291 } 292 else static if ((isArray!E || isInputRange!E) && isSomeChar!(Unqual!(ElementEncodingType!E))) 293 { 294 return encodeCborString!(flatten)(sink, value); 295 } 296 else static if (isInputRange!E || isArray!E || isTuple!E || 297 is(E == class) || is(E == struct)) 298 { 299 return encodeCborArray!(flatten)(sink, value); 300 } 301 else static if (isAssociativeArray!E) 302 { 303 return encodeCborMap(sink, value); 304 } 305 else 306 { 307 static assert(false, "Unable to encode " ~ E.stringof); 308 } 309 } 310 311 /// Encodes range of ubytes. 312 size_t encodeCborBytes(Flag!"Flatten" flatten = No.Flatten, R, E)(auto ref R sink, E value) 313 if(isOutputRange!(R, ubyte) && 314 (isArray!E || isInputRange!E) && is(Unqual!(ElementType!E) == ubyte)) 315 { 316 size_t size = 0; 317 // drop type and length for static arrays in flatten mode 318 static if (!needsFlattening!(E, flatten)) 319 { 320 size = encodeCborBytesHeader(sink, value.length); 321 } 322 size += encodeCborBytesItems(sink, value); 323 return size; 324 } 325 326 /// Encodes string. 327 size_t encodeCborString(Flag!"Flatten" flatten = No.Flatten, R, E)(auto ref R sink, E value) 328 if(isOutputRange!(R, ubyte) && isSomeChar!(Unqual!(ElementEncodingType!E))) 329 { 330 size_t size = 0; 331 // drop type and length for static arrays in flatten mode 332 static if (!needsFlattening!(E, flatten)) 333 { 334 size = encodeCborStringHeader(sink, value.length); 335 } 336 size += value.length; 337 338 static if (is(Unqual!(ElementEncodingType!E) == char)) 339 { 340 putChecked(sink, cast(ubyte[])value); 341 } 342 else 343 { 344 foreach(char elem; value[].byChar) 345 { 346 putChecked(sink, cast(ubyte)elem); 347 } 348 } 349 return size; 350 } 351 352 /// Encodes array of any items or a tuple as cbor array. 353 size_t encodeCborArray(Flag!"Flatten" flatten = No.Flatten, R, E)(auto ref R sink, E value) 354 if(isOutputRange!(R, ubyte) && 355 (isInputRange!E || isArray!E || isTuple!E)) 356 { 357 static if (isArray!E && is(Unqual!(ElementType!E) == void)) // accept [] 358 { 359 return encodeCborArrayHeader(sink, 0); 360 } 361 else 362 { 363 size_t size = 0; 364 // drop type and length for static arrays and expression tuples in flatten mode 365 static if (!needsFlattening!(E, flatten)) 366 size = encodeCborArrayHeader(sink, value.length); 367 368 foreach(item; value) 369 size += encodeCbor!(flatten)(sink, item); 370 return size; 371 } 372 } 373 374 /// Encodes structs and classes as cbor array. 375 size_t encodeCborArray(Flag!"Flatten" flatten = No.Flatten, R, A)(auto ref R sink, A aggregate) 376 if(isOutputRange!(R, ubyte) && 377 (is(A == struct) || is(A == class)) && 378 !isTuple!A) 379 { 380 return encodeCborAggregate!(Flag!"WithFieldName".no, flatten)(sink, aggregate); 381 } 382 383 /// Encodes asociative array as cbor map. 384 size_t encodeCborMap(R, E)(auto ref R sink, E value) 385 if(isOutputRange!(R, ubyte) && isAssociativeArray!E) 386 { 387 size_t size = encodeCborMapHeader(sink, value.length); 388 foreach(key, element; value) 389 { 390 size += encodeCbor(sink, key); 391 size += encodeCbor(sink, element); 392 } 393 return size; 394 } 395 396 /// Encodes structs and classes as cbor map. 397 /// Note, that decoding of structs and classes from maps is not supported (yet). 398 size_t encodeCborMap(Flag!"Flatten" flatten = Flag!"Flatten".no, R, A)(auto ref R sink, A aggregate) 399 if(isOutputRange!(R, ubyte) && (is(A == struct) || is(A == class)) && !isTuple!A) 400 { 401 return encodeCborAggregate!(Flag!"WithFieldName".yes, flatten)(sink, aggregate); 402 } 403 404 /// Encodes classes and structs. If withFieldName is yes, than value is encoded as map. 405 /// If withFieldName is no, then value is encoded as an array. 406 /// If flatten flag is yes then static arrays and structs will be encoded in place without headers. 407 size_t encodeCborAggregate( 408 Flag!"WithFieldName" withFieldName, 409 Flag!"Flatten" flatten = Flag!"Flatten".no, 410 R, 411 A)( 412 auto ref R sink, 413 auto ref A aggregate) 414 if (isOutputRange!(R, ubyte) && (is(A == struct) || is(A == class))) 415 { 416 size_t size; 417 static if (is(A == class)) 418 if (aggregate is null) 419 return encodeCbor(sink, null); 420 421 // flatten structs only 422 static if (!needsFlattening!(A, flatten)) 423 { 424 static if (withFieldName) 425 size += encodeCborMapHeader(sink, numEncodableMembers!A); 426 else 427 size += encodeCborArrayHeader(sink, numEncodableMembers!A); 428 } 429 430 foreach(i, member; aggregate.tupleof) 431 { 432 static if (isEncodedField!(typeof(member))) 433 { 434 // when encoded as map order is unspecified and flatten is not possible 435 static if (withFieldName) 436 { 437 size += encodeCborString(sink, __traits(identifier, aggregate.tupleof[i])); 438 size += encodeCbor(sink, member); 439 } 440 else 441 size += encodeCbor!flatten(sink, member); 442 } 443 } 444 return size; 445 } 446 447 //------------------------------------------------------------------------------ 448 // SSSSS TTTTTTT OOOOO RRRRRR AAA GGGG EEEEEEE 449 // SS TTT OO OO RR RR AAAAA GG GG EE 450 // SSSSS TTT OO OO RRRRRR AA AA GG EEEEE 451 // SS TTT OO OO RR RR AAAAAAA GG GG EE 452 // SSSSS TTT OOOO0 RR RR AA AA GGGGGG EEEEEEE 453 //------------------------------------------------------------------------------ 454 455 /// 456 enum CborTokenType : ubyte 457 { 458 /// CborToken.uinteger stores array length. 459 /// Indefinite-length array has no length. 460 /// Header is followed by zero or more data items, terminated by break. 461 arrayHeader = 0b000, 462 arrayIndefiniteHeader = 0b001, /// ditto 463 464 /// CborToken.uinteger stores map's pair count. 465 /// Indefinite-length map has no length. 466 /// Header is followed by zero or more pairs, terminated by break. 467 mapHeader = 0b010, 468 mapIndefiniteHeader = 0b011, /// ditto 469 470 /// CborToken.uinteger stores byte string length. 471 /// Indefinite-length byte string has no length. 472 /// Header is follower by zero or more definite-length byte strings terminated by break. 473 bytesHeader = 0b100, 474 bytesIndefiniteHeader = 0b101, /// ditto 475 476 /// CborToken.uinteger stores text string length. 477 /// Indefinite-length text string has no length. 478 /// Header is follower by zero or more definite-length text strings terminated by break. 479 textHeader = 0b110, 480 textIndefiniteHeader = 0b111, /// ditto 481 482 undefined, /// CborToken value is undefined. 483 nil, /// CborToken value is null. 484 boolean, /// CborToken.boolean stores actual value. 485 tag, /// uinteger stores tag value. 486 simple, /// Simple value is integer in range [0-255], stored in uinteger. 487 breakCode, /// "break" stop code, used to terminate indefinite-length sequences. 488 489 posinteger, /// CborToken.uinteger stores actual value. 490 neginteger, /// CborToken.integer stores actual value. 491 floating, /// CborToken.floating stores actual value. 492 } 493 494 /// 495 struct CborToken 496 { 497 CborTokenType type; 498 union 499 { 500 bool boolean; 501 long integer; 502 double floating; 503 // used for storing positive integers, array, map, raw and text size 504 ulong uinteger; 505 } 506 507 this(CborTokenType t) @nogc { 508 type = t; 509 } 510 this(CborTokenType t, bool v) @nogc { 511 type = t; 512 boolean = v; 513 } 514 this(CborTokenType t, long v) @nogc { 515 type = t; 516 integer = v; 517 } 518 this(CborTokenType t, double v) @nogc { 519 type = t; 520 floating = v; 521 } 522 523 bool opEquals(const CborToken other) const @nogc { 524 ubyte[] thisRep = (cast(ubyte*)(&this))[0..this.sizeof]; 525 ubyte[] otherRep = (cast(ubyte*)(&other))[0..other.sizeof]; 526 return thisRep == otherRep; 527 } 528 529 string toString() const 530 { 531 import std.string : format; 532 final switch(type) with(CborTokenType) 533 { 534 case boolean: return format("CborToken(%s)", boolean); 535 case nil: return "CborToken(null)"; 536 case undefined: return "CborToken(undefined)"; 537 case tag: return format("CborToken(tag, %s)", type, uinteger); 538 case simple: return format("CborToken(simple, %s)", uinteger); 539 case breakCode: return format("CborToken(break)"); 540 case posinteger: return format("CborToken(%s, %s)", type, uinteger); 541 case neginteger: return format("CborToken(%s, %s)", type, integer); 542 case floating: return format("CborToken(%s, %s)", type, floating); 543 case arrayHeader: return format("CborToken(array, %s)", uinteger); 544 case arrayIndefiniteHeader: return format("CborToken(arrayIndefiniteLength, _)"); 545 case mapHeader: return format("CborToken(map, %s)", uinteger); 546 case mapIndefiniteHeader: return format("CborToken(mapIndefiniteLength, _)"); 547 case bytesHeader: return format("CborToken(byteString, %s)", uinteger); 548 case bytesIndefiniteHeader: return format("CborToken(byteStringIndefiniteLength, _)"); 549 case textHeader: return format("CborToken(textString, %s)", uinteger); 550 case textIndefiniteHeader: return format("CborToken(textStringIndefiniteLength, _)"); 551 } 552 } 553 } 554 555 //------------------------------------------------------------------------------ 556 // DDDDD EEEEEEE CCCCC OOOOO DDDDD IIIII NN NN GGGG 557 // DD DD EE CC C OO OO DD DD III NNN NN GG GG 558 // DD DD EEEEE CC OO OO DD DD III NN N NN GG 559 // DD DD EE CC C OO OO DD DD III NN NNN GG GG 560 // DDDDDD EEEEEEE CCCCC OOOO0 DDDDDD IIIII NN NN GGGGGG 561 //------------------------------------------------------------------------------ 562 563 CborToken decodeCborToken(R)(auto ref R input) 564 if(isInputRange!R && is(ElementType!R == ubyte)) 565 { 566 import std.array; 567 568 if (input.empty) onInsufficientInput(); 569 570 ubyte item = input.front; 571 input.popFront; 572 573 switch(item) 574 { 575 case 0x00: .. case 0x17: // Integer 0x00..0x17 (0..23) 576 return CborToken(CborTokenType.posinteger, item); 577 case 0x18: // Unsigned integer (one-byte uint8_t follows) 578 return CborToken(CborTokenType.posinteger, readInteger!ubyte(input)); 579 case 0x19: // Unsigned integer (two-byte uint16_t follows) 580 return CborToken(CborTokenType.posinteger, readInteger!ushort(input)); 581 case 0x1a: // Unsigned integer (four-byte uint32_t follows) 582 return CborToken(CborTokenType.posinteger, readInteger!uint(input)); 583 case 0x1b: // Unsigned integer (eight-byte uint64_t follows) 584 return CborToken(CborTokenType.posinteger, readInteger!ulong(input)); 585 case 0x20: .. case 0x37: // Negative integer -1-0x00..-1-0x17 (-1..-24) 586 return CborToken(CborTokenType.neginteger, cast(byte)(-1 - item + 0x20)); 587 case 0x38: // Negative integer -1-n (one-byte uint8_t for n follows) 588 return CborToken(CborTokenType.neginteger, -1 - cast(long)readInteger!ubyte(input)); 589 case 0x39: // Negative integer -1-n (two-byte uint16_t for n follows) 590 return CborToken(CborTokenType.neginteger, -1 - cast(long)readInteger!ushort(input)); 591 case 0x3a: // Negative integer -1-n (four-byte uint32_t for n follows) 592 return CborToken(CborTokenType.neginteger, -1 - cast(long)readInteger!uint(input)); 593 case 0x3b: // Negative integer -1-n (eight-byte uint64_t for n follows) 594 return CborToken(CborTokenType.neginteger, -1 - cast(long)readInteger!ulong(input)); 595 case 0x40: .. case 0x57: // byte string (0x00..0x17 bytes follow) 596 return CborToken(CborTokenType.bytesHeader, item - 0x40); 597 case 0x58: // byte string (one-byte uint8_t for n, and then n bytes follow) 598 return CborToken(CborTokenType.bytesHeader, readInteger!ubyte(input)); 599 case 0x59: // byte string (two-byte uint16_t for n, and then n bytes follow) 600 return CborToken(CborTokenType.bytesHeader, readInteger!ushort(input)); 601 case 0x5a: // byte string (four-byte uint_t for n, and then n bytes follow) 602 return CborToken(CborTokenType.bytesHeader, readInteger!uint(input)); 603 case 0x5b: // byte string (eight-byte uint64_t for n, and then n bytes follow) 604 return CborToken(CborTokenType.bytesHeader, readInteger!ulong(input)); 605 case 0x5f: // indefinite-length byte string, byte strings follow, terminated by "break" 606 return CborToken(CborTokenType.bytesIndefiniteHeader); 607 case 0x60: .. case 0x77: // UTF-8 string (0x00..0x17 bytes follow) 608 return CborToken(CborTokenType.textHeader, item - 0x60); 609 case 0x78: // UTF-8 string (one-byte uint8_t for n, and then n bytes follow) 610 return CborToken(CborTokenType.textHeader, readInteger!ubyte(input)); 611 case 0x79: // UTF-8 string (two-byte uint16_t for n, and then n bytes follow) 612 return CborToken(CborTokenType.textHeader, readInteger!ushort(input)); 613 case 0x7a: // UTF-8 string (four-byte uint_t for n, and then n bytes follow) 614 return CborToken(CborTokenType.textHeader, readInteger!uint(input)); 615 case 0x7b: // UTF-8 string (eight-byte uint64_t for n, and then n bytes follow) 616 return CborToken(CborTokenType.textHeader, readInteger!ulong(input)); 617 case 0x7f: // indefinite-length UTF-8 string, UTF-8 strings follow, terminated by "break" 618 return CborToken(CborTokenType.textIndefiniteHeader); 619 case 0x80: .. case 0x97: // array (0x00..0x17 data items follow) 620 return CborToken(CborTokenType.arrayHeader, item - 0x80); 621 case 0x98: // array (one-byte uint8_t for n, and then n data items follow) 622 return CborToken(CborTokenType.arrayHeader, readInteger!ubyte(input)); 623 case 0x99: // array (two-byte uint16_t for n, and then n data items follow) 624 return CborToken(CborTokenType.arrayHeader, readInteger!ushort(input)); 625 case 0x9a: // array (four-byte uint_t for n, and then n data items follow) 626 return CborToken(CborTokenType.arrayHeader, readInteger!uint(input)); 627 case 0x9b: // array (eight-byte uint64_t for n, and then n data items follow) 628 return CborToken(CborTokenType.arrayHeader, readInteger!ulong(input)); 629 case 0x9f: // indefinite-length array, data items follow, terminated by "break" 630 return CborToken(CborTokenType.arrayIndefiniteHeader); 631 case 0xa0: .. case 0xb7: // map (0x00..0x17 pairs of data items follow) 632 return CborToken(CborTokenType.mapHeader, item - 0xa0); 633 case 0xb8: // map (one-byte uint8_t for n, and then n pairs of data items follow) 634 return CborToken(CborTokenType.mapHeader, readInteger!ubyte(input)); 635 case 0xb9: // map (two-byte uint16_t for n, and then n pairs of data items follow) 636 return CborToken(CborTokenType.mapHeader, readInteger!ushort(input)); 637 case 0xba: // map (four-byte uint_t for n, and then n pairs of data items follow) 638 return CborToken(CborTokenType.mapHeader, readInteger!uint(input)); 639 case 0xbb: // map (eight-byte uint64_t for n, and then n pairs of data items follow) 640 return CborToken(CborTokenType.mapHeader, readInteger!ulong(input)); 641 case 0xbf: // indefinite-length map, pairs of data items follow, terminated by "break" 642 return CborToken(CborTokenType.mapIndefiniteHeader); 643 case 0xc0: .. case 0xd7: // (tagged item) 644 return CborToken(CborTokenType.tag, item - 0xc0); 645 case 0xd8: // 1-byte tag 646 return CborToken(CborTokenType.tag, readInteger!ubyte(input)); 647 case 0xd9: // 2-byte tag 648 return CborToken(CborTokenType.tag, readInteger!ushort(input)); 649 case 0xda: // 4-byte tag 650 return CborToken(CborTokenType.tag, readInteger!uint(input)); 651 case 0xdb: // 8-byte tag 652 return CborToken(CborTokenType.tag, readInteger!ulong(input)); 653 case 0xe0: .. case 0xf3: // (simple value) 654 return CborToken(CborTokenType.simple, item - 0xe0); 655 case 0xf4: // False 656 return CborToken(CborTokenType.boolean, false); 657 case 0xf5: // True 658 return CborToken(CborTokenType.boolean, true); 659 case 0xf6: // Null 660 return CborToken(CborTokenType.nil); 661 case 0xf7: // Undefined 662 return CborToken(CborTokenType.undefined); 663 case 0xf8: // (simple value, one byte follows) 664 return CborToken(CborTokenType.simple, readInteger!ubyte(input)); 665 case 0xf9: // Half-Precision Float (two-byte IEEE 754) 666 ushort u = readInteger!ushort(input); 667 return CborToken(CborTokenType.floating, halfToFloat(u)); 668 case 0xfa: // Single-Precision Float (four-byte IEEE 754) 669 __FloatRep fr = {u : readInteger!uint(input)}; 670 return CborToken(CborTokenType.floating, fr.f); 671 case 0xfb: // Double-Precision Float (eight-byte IEEE 754) 672 __DoubleRep dr = {u : readInteger!ulong(input)}; 673 return CborToken(CborTokenType.floating, dr.d); 674 case 0xff: // "break" stop code 675 return CborToken(CborTokenType.breakCode); 676 default: 677 onUnsupportedTag(item); 678 } 679 680 assert(false); 681 } 682 683 //------------------------------------------------------------------------------ 684 // DDDDD EEEEEEE SSSSS EEEEEEE RRRRRR IIIII AAA LL IIIII ZZZZZ EEEEEEE 685 // DD DD EE SS EE RR RR III AAAAA LL III ZZ EE 686 // DD DD EEEEE SSSSS EEEEE RRRRRR III AA AA LL III ZZ EEEEE 687 // DD DD EE SS EE RR RR III AAAAAAA LL III ZZ EE 688 // DDDDDD EEEEEEE SSSSS EEEEEEE RR RR IIIII AA AA LLLLLLL IIIII ZZZZZ EEEEEEE 689 // 690 //------------------------------------------------------------------------------ 691 692 // If duplicate is true then dups incoming byte arrays and utf-8 string. 693 // If duplicate is false and input is array, then resulting byte arrays (ubyte[]) and 694 // utf-8 strings (char[], string) are slices of the input range. 695 void decodeCbor( 696 Flag!"Duplicate" duplicate = Yes.Duplicate, 697 Flag!"Flatten" flatten = No.Flatten, 698 R, 699 T) 700 (auto ref R input, ref T outValue) 701 if(isInputRange!R && is(ElementType!R == ubyte)) 702 { 703 import std.typecons : isTuple; 704 CborToken token; 705 706 static if (isIntegral!T) { 707 token = decodeCborToken(input); 708 if (token.type == CborTokenType.neginteger) { 709 outValue = cast(T)token.integer; 710 return; 711 } else if (token.type == CborTokenType.posinteger) { 712 outValue = cast(T)token.uinteger; 713 return; 714 } 715 onCastErrorToFrom!T(token.type); 716 } else static if (isSomeChar!T) { 717 token = decodeCborToken(input); 718 if (token.type == CborTokenType.posinteger) { 719 outValue = cast(T)token.uinteger; 720 return; 721 } 722 onCastErrorToFrom!T(token.type); 723 } else static if (isFloatingPoint!T) { 724 token = decodeCborToken(input); 725 if (token.type == CborTokenType.floating) { 726 outValue = cast(T)token.floating; 727 return; 728 } 729 onCastErrorToFrom!T(token.type); 730 } else static if (isBoolean!T) { 731 token = decodeCborToken(input); 732 if (token.type == CborTokenType.boolean) { 733 outValue = token.boolean; 734 return; 735 } 736 onCastErrorToFrom!T(token.type); 737 } else static if ((isArray!T || isOutputRange!(T, ubyte)) && is(Unqual!(ElementType!T) == ubyte)) { 738 decodeCborExactByteArray!(duplicate, flatten)(input, outValue); 739 return; 740 } else static if (isArray!T && isSomeChar!(Unqual!(ElementEncodingType!T))) { 741 decodeString!(duplicate, flatten)(input, outValue); 742 return; 743 } else static if (isArray!T) { 744 decodeSequence!(duplicate, flatten)(input, outValue); 745 return; 746 } else static if (is(T == class) || is(T == struct) || isTuple!T) { 747 decodeAggregate!(duplicate, flatten)(input, outValue); 748 return; 749 } else static if (isAssociativeArray!T) { 750 token = decodeCborToken(input); 751 752 if (token.type == CborTokenType.nil) { 753 outValue = null; 754 return; 755 } 756 757 alias K = typeof(T.init.keys[0]); 758 alias V = typeof(T.init.values[0]); 759 760 if (token.type == CborTokenType.mapHeader) { 761 foreach (_; 0..token.uinteger) { 762 K key; 763 V value; 764 decodeCbor!(duplicate, flatten)(input, key); 765 decodeCbor!(duplicate, flatten)(input, value); 766 outValue[key] = value; 767 } 768 } else if (token.type == CborTokenType.mapIndefiniteHeader) { 769 while (true) { 770 token = decodeCborToken(input.save); 771 if (token.type == CborTokenType.breakCode) { 772 break; 773 } else { 774 K key; 775 V value; 776 decodeCbor!(duplicate, flatten)(input, key); 777 decodeCbor!(duplicate, flatten)(input, value); 778 outValue[key] = value; 779 } 780 } 781 } 782 return; 783 } else { 784 static assert(false, "Unable to decode " ~ T.stringof); 785 } 786 787 assert(false); 788 } 789 790 private void decodeString( 791 Flag!"Duplicate" duplicate = Yes.Duplicate, 792 Flag!"Flatten" flatten = No.Flatten, 793 R, 794 T) 795 (auto ref R input, ref T outValue) 796 if(isInputRange!R && is(ElementType!R == ubyte)) 797 { 798 enum bool flat = needsFlattening!(T, flatten); 799 static if (flat) 800 { 801 static assert(isStaticArray!T, "Only static arrays can be flat"); 802 ubyte[] bytes = readBytes(input, T.length); 803 readStaticString(bytes, outValue); 804 return; 805 } 806 else 807 { 808 CborToken token = decodeCborToken(input); 809 bool definite = !(token.type & 0b001); 810 811 if (definite) { 812 static if (isDynamicArray!T) 813 { 814 ubyte[] bytes = readBytes(input, cast(size_t)token.uinteger); 815 outValue = to!T(cast(char[])bytes); 816 static if (is(Unqual!(ElementEncodingType!T) == ubyte) && duplicate) 817 outValue = outValue.dup; // TODO allocation 818 return; 819 } 820 else static if (isStaticArray!T) 821 { 822 ubyte[] bytes = readBytes(input, cast(size_t)token.uinteger); 823 if (bytes.length != T.length) onCastErrorToFrom!T(token.type); 824 readStaticString(bytes, outValue); 825 return; 826 } 827 } 828 else 829 { 830 static if (isDynamicArray!T) 831 { 832 alias Unqualified = Unqual!(ElementEncodingType!T)[]; 833 Unqualified output; 834 size_t i; 835 836 while (true) { 837 token = decodeCborToken(input.save); 838 if (token.type == CborTokenType.breakCode) { 839 import std.range : dropExactly; 840 dropExactly(input, 1); 841 break; 842 } else { 843 decodeCborToken(input); 844 if (token.type != CborTokenType.textHeader) 845 throw new CborException(format("Expected textHeader but got %s", token.type)); 846 output.length += cast(size_t)token.uinteger - (output.length - i); // TODO allocation 847 ubyte[] innerBytes = readBytes(input, token.uinteger); 848 i += readStaticString(innerBytes, output[i..$]); 849 } 850 } 851 output.length = i; 852 outValue = cast(T)output; 853 return; 854 } 855 else static if (isStaticArray!T) 856 assert(false, "TODO"); 857 } 858 assert(false, "TODO"); 859 } 860 } 861 862 private size_t readStaticString(T)(ubyte[] bytes, auto ref T outValue) 863 { 864 static if (is(Unqual!(ElementEncodingType!T) == char)) 865 { 866 outValue[] = cast(ElementEncodingType!T[])(bytes[]); 867 return bytes.length; 868 } 869 else 870 { 871 import std.utf : byChar, byWchar, byDchar; 872 alias V = Unqual!(ElementEncodingType!T); 873 874 static if (is(V == char)) 875 alias byElem = byChar; 876 else static if (is(V == wchar)) 877 alias byElem = byWchar; 878 else static if (is(V == dchar)) 879 alias byElem = byDchar; 880 881 size_t i; 882 foreach(c; byElem(cast(char[])bytes)) { 883 outValue[i] = c; 884 ++i; 885 } 886 return i; 887 } 888 } 889 890 private void decodeSequence( 891 Flag!"Duplicate" duplicate = Yes.Duplicate, 892 Flag!"Flatten" flatten = No.Flatten, 893 R, 894 T) 895 (auto ref R input, ref T outValue) 896 if(isInputRange!R && is(ElementType!R == ubyte)) 897 { 898 enum bool flat = needsFlattening!(T, flatten); 899 enum bool dynamicArray = isDynamicArray!T; 900 enum bool staticArray = isStaticArray!T; 901 enum bool range = !(staticArray || dynamicArray); 902 903 //------------------------------------------------------------------------------ 904 // Flat. Without header. Read only elements. 905 static if (flat) 906 { 907 static assert(isStaticArray!T, "Only static arrays can be flat"); 908 foreach (ref elem; outValue) 909 decodeCbor!(duplicate, flatten)(input, elem); 910 return; 911 } 912 else 913 { 914 //------------------------------------------------------------------------------ 915 // non-flat. Read header. 916 CborToken token = decodeCborToken(input); 917 bool definite = !(token.type & 0b001); 918 919 if (definite) { 920 static if (dynamicArray) 921 { 922 ulong lengthToRead = token.uinteger; 923 checkArraySize(lengthToRead); 924 if (outValue.length != cast(size_t)lengthToRead) 925 outValue.length = cast(size_t)token.uinteger; 926 foreach (ref elem; outValue) 927 decodeCbor!(duplicate, flatten)(input, elem); 928 return; 929 } 930 else static if (staticArray) 931 { 932 foreach (ref elem; outValue) 933 decodeCbor!(duplicate, flatten)(input, elem); 934 return; 935 } 936 } 937 else // indefinite 938 { 939 static if (dynamicArray) 940 { 941 alias Unqualified = Unqual!(ElementEncodingType!T)[]; 942 Unqualified output; 943 output.length = 1; 944 size_t i; 945 946 while (true) { 947 token = decodeCborToken(input.save); 948 if (token.type == CborTokenType.breakCode) { 949 import std.range : dropExactly; 950 dropExactly(input, 1); 951 break; 952 } else { 953 if (output.length == i) 954 output.length = output.length * 2; // TODO allocation 955 decodeCbor!(No.Duplicate, flatten)(input, output[i]); 956 } 957 ++i; 958 } 959 output.length = i; 960 outValue = cast(T)output; 961 return; 962 } 963 else static if (staticArray) 964 assert(false, "TODO"); 965 } 966 assert(false, "TODO"); 967 } 968 } 969 970 private void decodeCborExactByteArray( 971 Flag!"Duplicate" duplicate = Yes.Duplicate, 972 Flag!"Flatten" flatten = No.Flatten, 973 R, 974 T) 975 (auto ref R input, ref T outValue) 976 if(isInputRange!R && is(ElementType!R == ubyte)) 977 { 978 static if (needsFlattening!(T, flatten)) 979 { 980 outValue[] = cast(ElementEncodingType!T[])(readBytes(input, T.length)); 981 return; 982 } 983 else 984 { 985 CborToken token = decodeCborToken(input); 986 if (token.type == CborTokenType.bytesHeader) 987 { 988 ubyte[] data = readBytes(input, token.uinteger); 989 static if (isDynamicArray!T) { 990 outValue = cast(T)data; 991 static if (duplicate) 992 outValue = outValue.dup; 993 } else static if (isStaticArray!T) { 994 if (data.length != T.length) 995 onCastErrorToFrom!T(token.type); 996 outValue[] = data; 997 } else { 998 static assert(false); 999 } 1000 return; 1001 } 1002 else if (token.type == CborTokenType.bytesIndefiniteHeader) 1003 { 1004 import std.algorithm : copy; 1005 import std.array : Appender, appender; 1006 1007 static if (isArray!T) { 1008 alias unqualified = Unqual!(ElementEncodingType!T)[]; 1009 Appender!(unqualified) sink = appender(cast(unqualified)outValue); 1010 sink.clear(); 1011 } else 1012 alias sink = outValue; 1013 1014 while (true) { 1015 token = decodeCborToken(input); 1016 if (token.type == CborTokenType.breakCode) { 1017 break; 1018 } else if (token.type == CborTokenType.bytesHeader) { 1019 copy(readBytes(input, token.uinteger), sink); 1020 } else { 1021 throw new CborException( 1022 format("Unexpected token inside indefinite-length byte array: %s; "~ 1023 "Expected definite-length byteString", token)); 1024 } 1025 } 1026 1027 static if (isDynamicArray!T) { 1028 outValue = cast(T)sink.data; 1029 } else static if (isStaticArray!T) { 1030 outValue[] = sink.data; 1031 } else 1032 static assert(false); 1033 1034 return; 1035 } 1036 } 1037 } 1038 1039 private void decodeAggregate( 1040 Flag!"Duplicate" duplicate = Yes.Duplicate, 1041 Flag!"Flatten" flatten = No.Flatten, R, T) 1042 (auto ref R input, ref T outValue) 1043 if(isInputRange!R && is(ElementType!R == ubyte)) 1044 { 1045 static if (!needsFlattening!(T, flatten)) { 1046 CborToken token = decodeCborToken(input); 1047 } 1048 1049 static if (is(T == class)) 1050 if (token.type == CborTokenType.nil) { 1051 outValue = null; 1052 return; 1053 } 1054 1055 static if (!needsFlattening!(T, flatten)) 1056 { 1057 if (token.type != CborTokenType.arrayHeader) 1058 throw new CborException(format("Can not decode %s from %s", T.stringof, token.type)); 1059 1060 size_t numMembers; 1061 1062 static if (isTuple!T) 1063 numMembers = T.Types.length; 1064 else 1065 numMembers = numEncodableMembers!T; 1066 1067 if (token.uinteger != numMembers) 1068 { 1069 throw new CborException( 1070 format("The number of deserialized members of %s is mismatched."~ 1071 " Got %s, while expected %s members", 1072 T.stringof, token.uinteger, numMembers)); 1073 } 1074 } 1075 1076 static if (isTuple!T) 1077 { 1078 foreach (i, Type; T.Types) 1079 decodeCbor!(duplicate, flatten)(input, outValue.field[i]); 1080 } 1081 else 1082 { 1083 static if (is(T == class)) 1084 if (outValue is null) 1085 outValue = newClassInstance!T(); 1086 1087 foreach(i, ref member; outValue.tupleof) 1088 { 1089 static if (isEncodedField!(typeof(member))) 1090 decodeCbor!(duplicate, flatten)(input, outValue.tupleof[i]); 1091 } 1092 } 1093 } 1094 1095 /// Decodes single cbor value and tries to convert it to requested type. 1096 /// If types don't match CborException is thrown. 1097 /// Note, that ubyte[] and string types are slices of input range if ubyte[] was provided. 1098 /// Will consume input range, decoding all the elements of T. 1099 T decodeCborSingle(T, Flag!"Flatten" flatten = No.Flatten, R)(auto ref R input) 1100 if(isInputRange!R && is(ElementType!R == ubyte)) 1101 { 1102 Unqual!T value; 1103 decodeCbor!(No.Duplicate, flatten)(input, value); 1104 return cast(T)value; 1105 } 1106 1107 /// Decodes single cbor value and tries to convert it to requested type. 1108 /// If types don't match CborException is thrown. 1109 /// Note, that this version will dup all array slices for you. 1110 /// Will consume input range, decoding all the elements of T. 1111 T decodeCborSingleDup(T, Flag!"Flatten" flatten = No.Flatten, R)(auto ref R input) 1112 if(isInputRange!R && is(ElementType!R == ubyte)) 1113 { 1114 Unqual!T value; 1115 decodeCbor!(Yes.Duplicate, flatten)(input, value); 1116 return cast(T)value; 1117 } 1118 1119 //------------------------------------------------------------------------------ 1120 // HH HH EEEEEEE LL PPPPPP EEEEEEE RRRRRR SSSSS 1121 // HH HH EE LL PP PP EE RR RR SS 1122 // HHHHHHH EEEEE LL PPPPPP EEEEE RRRRRR SSSSS 1123 // HH HH EE LL PP EE RR RR SS 1124 // HH HH EEEEEEE LLLLLLL PP EEEEEEE RR RR SSSSS 1125 //------------------------------------------------------------------------------ 1126 1127 private void putChecked(R, E)(ref R sink, auto ref E e) 1128 { 1129 import std.range : put, hasLength; 1130 version(Cbor_Debug) 1131 static if (hasLength!R) 1132 { 1133 size_t elemSize; 1134 1135 static if (hasLength!E) 1136 elemSize = e.length * ElementType!E.sizeof; 1137 else 1138 elemSize = ElementType!E.sizeof; 1139 1140 assert(sink.length >= elemSize, "Provided sink length is to small"); 1141 } 1142 put(sink, e); 1143 } 1144 1145 private T readInteger(T, R)(auto ref R input) 1146 if(isInputRange!R && is(ElementType!R == ubyte)) 1147 { 1148 enum ubyte size = T.sizeof; 1149 import std.algorithm : copy; 1150 import std.bitmanip : bigEndianToNative; 1151 import std.range : dropExactly, take; 1152 1153 static assert(T.sizeof == size); 1154 static assert(size > 0); 1155 if (input.length < size) onInsufficientInput(); 1156 1157 ubyte[size] data; 1158 1159 copy(take(input, size), data[]); 1160 input = input.dropExactly(size); 1161 T result = bigEndianToNative!(T, size)(data); 1162 1163 return result; 1164 } 1165 1166 // Reads byte array from input range. On 32-bit can read up to uint.max bytes. 1167 // If ubyte[] is passed as input, a slice will be returned. 1168 // Make sure to dup array when input buffer is reused. 1169 ubyte[] readBytes(R)(auto ref R input, ulong length) 1170 if(isInputRange!R && is(ElementType!R == ubyte)) 1171 { 1172 import std.array; 1173 import std.range : take; 1174 if (input.length < length) onInsufficientInput(); 1175 1176 static if (size_t.sizeof < ulong.sizeof) 1177 if (length > size_t.max) 1178 throw new CborException(format("Array size is too big %s", length)); 1179 1180 size_t dataLength = cast(size_t)length; 1181 ubyte[] result; 1182 static if (is(R == ubyte[])) 1183 { 1184 result = input[0..dataLength]; 1185 input = input[dataLength..$]; 1186 } 1187 else 1188 { 1189 result = take(input, dataLength).array; // TODO allocation 1190 } 1191 1192 return result; 1193 } 1194 1195 private void checkArraySize(T)(T length) 1196 { 1197 static if (size_t.sizeof < ulong.sizeof) 1198 if (length > size_t.max) 1199 throw new CborException(format("Array size is too big: %s > size_t.max", length)); 1200 } 1201 1202 private union __FloatRep { float f; uint u;} 1203 private union __DoubleRep { double d; ulong u; } 1204 // from http://stackoverflow.com/questions/6162651/half-precision-floating-point-in-java 1205 private float halfToFloat(ushort half) @nogc 1206 { 1207 int mant = half & 0x03ff; // 10 bits mantissa 1208 int exp = half & 0x7c00; // 5 bits exponent 1209 if( exp == 0x7c00 ) // NaN/Inf 1210 exp = 0x3fc00; // -> NaN/Inf 1211 else if( exp != 0 ) // normalized value 1212 { 1213 exp += 0x1c000; // exp - 15 + 127 1214 if( mant == 0 && exp > 0x1c400 ){ // smooth transition 1215 __FloatRep f = {u : (( half & 0x8000 ) << 16 | exp << 13 | 0x3ff) }; 1216 return f.f; 1217 } 1218 } 1219 else if( mant != 0 ) // && exp==0 -> subnormal 1220 { 1221 exp = 0x1c400; // make it normal 1222 do { 1223 mant <<= 1; // mantissa * 2 1224 exp -= 0x400; // decrease exp by 1 1225 } while( ( mant & 0x400 ) == 0 ); // while not normal 1226 mant &= 0x3ff; // discard subnormal bit 1227 } // else +/-0 -> +/-0 1228 __FloatRep f = {u : ( // combine all parts 1229 ( half & 0x8000 ) << 16 // sign << ( 31 - 15 ) 1230 | ( exp | mant ) << 13 ) // value << ( 23 - 10 ) 1231 }; 1232 return f.f; 1233 } 1234 1235 /// Outputs textual representation of cbor stream into sink or stdout if not provided. 1236 void printCborStream(string singleIndent=" ", R)(auto ref R input) 1237 { 1238 import std.stdio : stdout; 1239 auto writer = stdout.lockingTextWriter; 1240 printCborStream!singleIndent(input, writer); 1241 } 1242 1243 /// ditto 1244 void printCborStream(string singleIndent=" ", Sink, R)( 1245 auto ref R input, 1246 auto ref Sink sink, 1247 ulong numItems = ulong.max, 1248 string indent = "" 1249 ) 1250 if(isInputRange!R && is(ElementType!R == ubyte) && isOutputRange!(Sink, char)) 1251 { 1252 import std.format : formattedWrite; 1253 1254 while(input.length > 0 && numItems > 0) 1255 { 1256 CborToken token = decodeCborToken(input); 1257 auto type = token.type; 1258 final switch(type) with(CborTokenType) 1259 { 1260 case boolean: formattedWrite(sink, "%s(%s)\n", indent, token.boolean); break; 1261 case nil: formattedWrite(sink, "%s(null)\n", indent); break; 1262 case undefined: formattedWrite(sink, "%s(undefined)\n", indent); break; 1263 case tag: 1264 formattedWrite(sink, "%s(tag, %s)\n", indent, token.uinteger); 1265 printCborStream!singleIndent(input, sink, 1, indent~singleIndent); 1266 break; 1267 case simple: formattedWrite(sink, "%s(simple, %s)\n", indent, token.uinteger); break; 1268 case breakCode: 1269 formattedWrite(sink, "%s(break)\n", indent, token.uinteger); 1270 return; 1271 case posinteger: formattedWrite(sink, "%s(posinteger, %s)\n", indent, token.uinteger); break; 1272 case neginteger: formattedWrite(sink, "%s(neginteger, %s)\n", indent, token.integer); break; 1273 case floating: formattedWrite(sink, "%s(floating, %s)\n", indent, token.floating); break; 1274 case arrayHeader: 1275 formattedWrite(sink, "%s(array, %s)\n", indent, token.uinteger); 1276 printCborStream!singleIndent(input, sink, token.uinteger, indent~singleIndent); 1277 break; 1278 case arrayIndefiniteHeader: 1279 formattedWrite(sink, "%s(array, _)\n", indent); 1280 printCborStream!singleIndent(input, sink, ulong.max, indent~singleIndent); 1281 break; 1282 case mapHeader: 1283 formattedWrite(sink, "%s(map, %s)\n", indent, token.uinteger); 1284 printCborStream!singleIndent(input, sink, token.uinteger*2, indent~singleIndent); 1285 break; 1286 case mapIndefiniteHeader: 1287 formattedWrite(sink, "%s(map, _)\n", indent); 1288 printCborStream!singleIndent(input, sink, ulong.max, indent~singleIndent); 1289 break; 1290 case bytesHeader: 1291 formattedWrite(sink, "%s(bytes, %s)\n%s%s(%(%02x%))\n", 1292 indent, token.uinteger, indent, singleIndent, readBytes(input, token.uinteger)); 1293 break; 1294 case bytesIndefiniteHeader: 1295 formattedWrite(sink, "%s(bytes, _)\n", indent); 1296 printCborStream!singleIndent(input, sink, ulong.max, indent~singleIndent); 1297 break; 1298 case textHeader: 1299 formattedWrite(sink, "%s(text, %s)\n%s%s\"%s\"\n", 1300 indent, token.uinteger, indent, singleIndent, cast(string)readBytes(input, token.uinteger)); 1301 break; 1302 case textIndefiniteHeader: 1303 formattedWrite(sink, "%s(text, _)\n", indent); 1304 printCborStream!singleIndent(input, sink, ulong.max, indent~singleIndent); 1305 break; 1306 } 1307 --numItems; 1308 } 1309 } 1310 1311 private template isEncodedField(T) 1312 { 1313 enum isEncodedField = isIntegral!T || isFloatingPoint!T || isBoolean!T || 1314 is(Unqual!T == typeof(null)) || isArray!T || isInputRange!T || 1315 isTuple!T || is(T == string) || is(T == class) || is(T == struct) || 1316 isAssociativeArray!T; 1317 } 1318 1319 /// Tests if type can be encoded in flat mode, i.e. without header 1320 private template canBeFlattened(T) 1321 { 1322 enum bool canBeFlattened = 1323 isStaticArray!T || 1324 (isTuple!T && isExpressionTuple!T) || 1325 is(T == struct); 1326 } 1327 1328 private enum bool needsFlattening(T, Flag!"Flatten" flatten) = canBeFlattened!T && flatten; 1329 1330 /// Returns a number of aggregate members that will be encoded by cbor-d. 1331 template numEncodableMembers(alias T) 1332 { 1333 enum numEncodableMembers = numEncodableMembersImpl!(T.tupleof); 1334 } 1335 1336 private template numEncodableMembersImpl(members ...) 1337 { 1338 static if (members.length == 0) 1339 enum numEncodableMembersImpl = 0; 1340 else 1341 enum numEncodableMembersImpl = 1342 cast(int)isEncodedField!(typeof(members[0])) + 1343 numEncodableMembersImpl!(members[1..$]); 1344 } 1345 1346 private C newClassInstance(C)() if (is(C == class)) 1347 { 1348 import core.memory : GC; 1349 void* memory = GC.malloc(typeid(C).init.length); 1350 memory[0 .. typeid(C).init.length] = typeid(C).init[]; 1351 return cast(C) memory; 1352 } 1353 1354 //------------------------------------------------------------------------------ 1355 // TTTTTTT EEEEEEE SSSSS TTTTTTT SSSSS 1356 // TTT EE SS TTT SS 1357 // TTT EEEEE SSSSS TTT SSSSS 1358 // TTT EE SS TTT SS 1359 // TTT EEEEEEE SSSSS TTT SSSSS 1360 //------------------------------------------------------------------------------ 1361 1362 // Testing helpers 1363 version(unittest) 1364 { 1365 import std.array : Appender; 1366 import std.stdio; 1367 import std.exception : assertThrown; 1368 import core.exception : AssertError; 1369 Appender!(ubyte[]) testBuf; 1370 1371 CborToken getTokenAndReset() 1372 { 1373 CborToken res = decodeCborToken(testBuf.data); 1374 testBuf.clear(); 1375 return res; 1376 } 1377 1378 void assertToken(CborToken expected) 1379 { 1380 CborToken decoded = getTokenAndReset(); 1381 ubyte[] decodedRep = (cast(ubyte*)(&decoded))[0..decoded.sizeof]; 1382 ubyte[] expectedRep = (cast(ubyte*)(&expected))[0..expected.sizeof]; 1383 //writefln("%s %s", decodedRep, expectedRep); 1384 assert(decoded == expected, format("decoded %s, %s, expected %s, %s", 1385 decoded, decodedRep, expected, expectedRep)); 1386 } 1387 1388 void assertHexAndClear(string file = __MODULE__, size_t line = __LINE__)(string hex) 1389 { 1390 assertEqual!(file, line)(toHexString(testBuf.data), hex); 1391 testBuf.clear(); 1392 } 1393 1394 void printBufferAndReset() 1395 { 1396 import std.stdio : stdout; 1397 printCborStream(testBuf.data); 1398 testBuf.clear(); 1399 } 1400 1401 void assertEqual(string file = __MODULE__, size_t line = __LINE__, A, B)(A a, B b) 1402 { 1403 assert(a == b, format("%s(%s) != %s(%s) at %s:%s", typeid(a), a, typeid(b), b, file, line)); 1404 } 1405 1406 void assertf(Args...)(bool condition, string formatStr, Args args) 1407 { 1408 assert(condition, format(formatStr, args)); 1409 } 1410 1411 private ubyte[1024] buf; 1412 1413 private ubyte[] getEncoded(T)(T value) 1414 { 1415 return buf[0..encodeCbor(buf[], value)]; 1416 } 1417 1418 private string toHexString(ubyte[] arr) 1419 { 1420 return format("0x%(%02x%)", arr); 1421 } 1422 private string encodedString(T)(T value) 1423 { 1424 return toHexString(getEncoded(value)); 1425 } 1426 private void printEncoded(T)(T value) 1427 { 1428 import std.stdio : writeln; 1429 encodedString(value).writeln; 1430 } 1431 private void cmpEncoded(T)(T value, string encodedStr) 1432 { 1433 auto encoded = encodedString(value); 1434 assert(encoded == encodedStr, format("%s != %s", encoded, encodedStr)); 1435 } 1436 private void cmpEncodedConst(T)(T value, string encodedStr) 1437 { 1438 auto encoded = encodedString(cast(const)value); 1439 assert(encoded == encodedStr, format("%s != %s", encoded, encodedStr)); 1440 } 1441 private void cmpEncodedImmutable(T)(T value, string encodedStr) 1442 { 1443 auto encoded = encodedString(cast(immutable)value); 1444 assert(encoded == encodedStr, format("%s != %s", encoded, encodedStr)); 1445 } 1446 } 1447 // Basic encoding/decoding 1448 unittest 1449 { 1450 // positive integer 1451 encodeCborInt(testBuf, 1); 1452 assertToken(CborToken(CborTokenType.posinteger, 1)); 1453 encodeCborInt(testBuf, ubyte.max + 10); 1454 assertToken(CborToken(CborTokenType.posinteger, ubyte.max + 10)); 1455 encodeCborInt(testBuf, ushort.max + 10); 1456 assertToken(CborToken(CborTokenType.posinteger, ushort.max + 10)); 1457 encodeCborInt(testBuf, ulong.max); 1458 assertToken(CborToken(CborTokenType.posinteger, ulong.max)); 1459 // negative integer 1460 encodeCborInt(testBuf, -10); 1461 assertToken(CborToken(CborTokenType.neginteger, -10)); 1462 encodeCborInt(testBuf, -long.max); 1463 assertToken(CborToken(CborTokenType.neginteger, -long.max)); 1464 // bool true 1465 encodeCborBool(testBuf, true); 1466 assertToken(CborToken(CborTokenType.boolean, true)); 1467 // bool false 1468 encodeCborBool(testBuf, false); 1469 assertToken(CborToken(CborTokenType.boolean, false)); 1470 // null 1471 encodeCborNull(testBuf); 1472 assertToken(CborToken(CborTokenType.nil)); 1473 // undefined 1474 encodeCborUndefined(testBuf); 1475 assertToken(CborToken(CborTokenType.undefined)); 1476 // break 1477 encodeCborBreak(testBuf); 1478 assertToken(CborToken(CborTokenType.breakCode)); 1479 // simple 1480 encodeCborSimple(testBuf, 10); 1481 assertToken(CborToken(CborTokenType.simple, 10)); 1482 encodeCborSimple(testBuf, ubyte.max); 1483 assertToken(CborToken(CborTokenType.simple, ubyte.max)); 1484 foreach(ubyte invalidValue; 25..32) { 1485 assertThrown!AssertError(encodeCborSimple(testBuf, invalidValue)); 1486 } 1487 // bytes 1488 encodeCborBytesHeader(testBuf, 10); 1489 assertToken(CborToken(CborTokenType.bytesHeader, 10)); 1490 encodeCborBytesHeader(testBuf, ubyte.max); 1491 assertToken(CborToken(CborTokenType.bytesHeader, ubyte.max)); 1492 encodeCborBytesHeader(testBuf, ushort.max); 1493 assertToken(CborToken(CborTokenType.bytesHeader, ushort.max)); 1494 encodeCborBytesHeader(testBuf, uint.max); 1495 assertToken(CborToken(CborTokenType.bytesHeader, uint.max)); 1496 encodeCborBytesHeader(testBuf, ulong.max); 1497 assertToken(CborToken(CborTokenType.bytesHeader, ulong.max)); 1498 // bytes 1499 encodeCborBytesHeader(testBuf); 1500 assertToken(CborToken(CborTokenType.bytesIndefiniteHeader)); 1501 // text 1502 encodeCborStringHeader(testBuf, 10); 1503 assertToken(CborToken(CborTokenType.textHeader, 10)); 1504 encodeCborStringHeader(testBuf, ubyte.max); 1505 assertToken(CborToken(CborTokenType.textHeader, ubyte.max)); 1506 encodeCborStringHeader(testBuf, ushort.max); 1507 assertToken(CborToken(CborTokenType.textHeader, ushort.max)); 1508 encodeCborStringHeader(testBuf, uint.max); 1509 assertToken(CborToken(CborTokenType.textHeader, uint.max)); 1510 encodeCborStringHeader(testBuf, ulong.max); 1511 assertToken(CborToken(CborTokenType.textHeader, ulong.max)); 1512 // text 1513 encodeCborStringHeader(testBuf); 1514 assertToken(CborToken(CborTokenType.textIndefiniteHeader)); 1515 // array 1516 encodeCborArrayHeader(testBuf, 10); 1517 assertToken(CborToken(CborTokenType.arrayHeader, 10)); 1518 encodeCborArrayHeader(testBuf, ubyte.max); 1519 assertToken(CborToken(CborTokenType.arrayHeader, ubyte.max)); 1520 encodeCborArrayHeader(testBuf, ushort.max); 1521 assertToken(CborToken(CborTokenType.arrayHeader, ushort.max)); 1522 encodeCborArrayHeader(testBuf, uint.max); 1523 assertToken(CborToken(CborTokenType.arrayHeader, uint.max)); 1524 encodeCborArrayHeader(testBuf, ulong.max); 1525 assertToken(CborToken(CborTokenType.arrayHeader, ulong.max)); 1526 // array 1527 encodeCborArrayHeader(testBuf); 1528 assertToken(CborToken(CborTokenType.arrayIndefiniteHeader)); 1529 // map 1530 encodeCborMapHeader(testBuf, 10); 1531 assertToken(CborToken(CborTokenType.mapHeader, 10)); 1532 encodeCborMapHeader(testBuf, ubyte.max); 1533 assertToken(CborToken(CborTokenType.mapHeader, ubyte.max)); 1534 encodeCborMapHeader(testBuf, ushort.max); 1535 assertToken(CborToken(CborTokenType.mapHeader, ushort.max)); 1536 encodeCborMapHeader(testBuf, uint.max); 1537 assertToken(CborToken(CborTokenType.mapHeader, uint.max)); 1538 encodeCborMapHeader(testBuf, ulong.max); 1539 assertToken(CborToken(CborTokenType.mapHeader, ulong.max)); 1540 // map 1541 encodeCborMapHeader(testBuf); 1542 assertToken(CborToken(CborTokenType.mapIndefiniteHeader)); 1543 // tag 1544 encodeCborTag(testBuf, 1); 1545 assertToken(CborToken(CborTokenType.tag, 1)); 1546 encodeCborTag(testBuf, ubyte.max); 1547 assertToken(CborToken(CborTokenType.tag, ubyte.max)); 1548 encodeCborTag(testBuf, ushort.max); 1549 assertToken(CborToken(CborTokenType.tag, ushort.max)); 1550 encodeCborTag(testBuf, uint.max); 1551 assertToken(CborToken(CborTokenType.tag, uint.max)); 1552 encodeCborTag(testBuf, ulong.max); 1553 assertToken(CborToken(CborTokenType.tag, ulong.max)); 1554 } 1555 1556 unittest // encoding 1557 { 1558 testEncoding!cmpEncoded(); 1559 testEncoding!cmpEncodedConst(); 1560 testEncoding!cmpEncodedImmutable(); 1561 } 1562 1563 version(unittest) 1564 private void testEncoding(alias cmpEncoded)() 1565 { 1566 // Test vectors 1567 cmpEncoded(0, "0x00"); 1568 cmpEncoded(1, "0x01"); 1569 cmpEncoded(10, "0x0a"); 1570 cmpEncoded(23, "0x17"); 1571 cmpEncoded(24, "0x1818"); 1572 cmpEncoded(25, "0x1819"); 1573 cmpEncoded(100, "0x1864"); 1574 cmpEncoded(1000, "0x1903e8"); 1575 cmpEncoded(1000000, "0x1a000f4240"); 1576 cmpEncoded(1000000000000, "0x1b000000e8d4a51000"); 1577 1578 //bignums 1579 //cmpEncoded(18446744073709551615, "0x1bffffffffffffffff"); 1580 //cmpEncoded(18446744073709551616, "0xc249010000000000000000"); 1581 //cmpEncoded(-18446744073709551616, "0x3bffffffffffffffff"); 1582 //cmpEncoded(-18446744073709551617, "0xc349010000000000000000"); 1583 cmpEncoded(-1, "0x20"); 1584 cmpEncoded(-10, "0x29"); 1585 cmpEncoded(-100, "0x3863"); 1586 cmpEncoded(-1000, "0x3903e7"); 1587 1588 //printEncoded(0.0f, "0xf90000"); // half-float 1589 //printEncoded(-0.0f, "0xf98000"); // half-float 1590 //printEncoded(1.0f, "0xf93c00"); // half-float 1591 cmpEncoded(1.1, "0xfb3ff199999999999a"); 1592 //printEncoded(1.5f, "0xf93e00"); // half-float 1593 //printEncoded(65504.0f, "0xf97bff"); // half-float 1594 cmpEncoded(100000.0f, "0xfa47c35000"); 1595 cmpEncoded(3.4028234663852886e+38f, "0xfa7f7fffff"); 1596 cmpEncoded(1.0e+300, "0xfb7e37e43c8800759c"); 1597 //printEncoded(5.960464477539063e-8f, "0xf90001"); // half-float 1598 //printEncoded(0.00006103515625f, "0xf90400"); // half-float 1599 //printEncoded(-4.0f, "0xf9c400"); // half-float 1600 cmpEncoded(-4.1, "0xfbc010666666666666"); 1601 cmpEncoded(1.0f/0, "0xfa7f800000"); // Infinity 1602 //cmpEncoded(NaN, "0xfa7fc00000"); // produces 0xfa7fe00000 1603 cmpEncoded(-1.0f/0, "0xfaff800000"); // -Infinity 1604 cmpEncoded(1.0/0, "0xfb7ff0000000000000"); // Infinity 1605 //cmpEncoded(NaN, "0xfb7ff8000000000000"); // produces 0xfb7ffc000000000000 1606 cmpEncoded(-1.0/0, "0xfbfff0000000000000");// -Infinity 1607 1608 cmpEncoded(false, "0xf4"); 1609 cmpEncoded(true, "0xf5"); 1610 cmpEncoded(null, "0xf6"); 1611 1612 // raw arrays, ubyte[] 1613 cmpEncoded(cast(ubyte[])[], "0x40"); 1614 cmpEncoded(cast(ubyte[])[1, 2, 3, 4], "0x4401020304"); 1615 1616 // strings 1617 cmpEncoded("", "0x60"); 1618 cmpEncoded("a", "0x6161"); 1619 cmpEncoded("IETF", "0x6449455446"); 1620 cmpEncoded("\"\\", "0x62225c"); 1621 cmpEncoded("\u00fc", "0x62c3bc"); 1622 cmpEncoded("\u6c34", "0x63e6b0b4"); 1623 //cmpEncoded("\ud800\udd51", "0x64f0908591"); // invalid character 1624 1625 import std.typecons : tuple; 1626 // arrays 1627 cmpEncoded([], "0x80"); 1628 cmpEncoded([1, 2, 3], "0x83010203"); 1629 cmpEncoded(tuple(1, [2, 3], [4, 5]), "0x8301820203820405"); 1630 enum arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1631 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]; 1632 cmpEncoded(arr, "0x98190102030405060708090a0b0c0d0e0f101112131415161718181819"); 1633 1634 // AA 1635 uint[uint] emptyAA; 1636 cmpEncoded(emptyAA, "0xa0"); 1637 cmpEncoded([1:2], "0xa10102"); 1638 1639 cmpEncoded(cast(ubyte)42, "0x182a"); 1640 cmpEncoded(cast(ushort)42, "0x182a"); 1641 cmpEncoded(cast(uint)42, "0x182a"); 1642 cmpEncoded(cast(ulong)42, "0x182a"); 1643 cmpEncoded(cast(byte)42, "0x182a"); 1644 cmpEncoded(cast(short)42, "0x182a"); 1645 cmpEncoded(cast(int)42, "0x182a"); 1646 cmpEncoded(cast(long)42, "0x182a"); 1647 } 1648 1649 unittest // decoding 1650 { 1651 ubyte[1024] buf; 1652 size_t size; 1653 1654 int[string] maptest1; 1655 // decode map from empty map 1656 size = encodeCbor(buf[], maptest1); 1657 assertEqual(decodeCborSingle!(int[string])(buf[0..size]), maptest1); 1658 // decode map from null 1659 size = encodeCbor(buf[], null); 1660 assertEqual(decodeCborSingle!(int[string])(buf[0..size]), maptest1); 1661 } 1662 1663 // indefinite-length encoding 1664 unittest 1665 { 1666 // {"a": 1, "b": [2, 3]} 1667 encodeCborMapHeader(testBuf, 2); 1668 encodeCborString(testBuf, "a"); 1669 encodeCborInt(testBuf, 1); 1670 encodeCborString(testBuf, "b"); 1671 encodeCborArrayHeader(testBuf, 2); 1672 encodeCborInt(testBuf, 2); 1673 encodeCborInt(testBuf, 3); 1674 assertHexAndClear("0xa26161016162820203"); 1675 1676 // ["a", {"b": "c"}] 1677 encodeCborArrayHeader(testBuf, 2); 1678 encodeCborString(testBuf, "a"); 1679 encodeCborMapHeader(testBuf, 1); 1680 encodeCborString(testBuf, "b"); 1681 encodeCborString(testBuf, "c"); 1682 assertHexAndClear("0x826161a161626163"); 1683 1684 // ["a": "A", "b": "B", "c":"C", "d": "D", "e": "E"] 1685 encodeCborMapHeader(testBuf, 5); 1686 foreach(chr; "aAbBcCdDeE") 1687 encodeCborString(testBuf, (&chr)[0..1]); 1688 assertHexAndClear("0xa56161614161626142616361436164614461656145"); 1689 1690 // (_ h'0102', h'030405') 1691 encodeCborBytesHeader(testBuf); 1692 encodeCborBytes(testBuf, cast(ubyte[])[1, 2]); 1693 encodeCborBytes(testBuf, cast(ubyte[])[3, 4, 5]); 1694 encodeCborBreak(testBuf); 1695 assertHexAndClear("0x5f42010243030405ff"); 1696 1697 // (_ "strea", "ming") 1698 encodeCborStringHeader(testBuf); 1699 encodeCborString(testBuf, "strea"); 1700 encodeCborString(testBuf, "ming"); 1701 encodeCborBreak(testBuf); 1702 assertHexAndClear("0x7f657374726561646d696e67ff"); 1703 1704 // [_ ] 1705 encodeCborArrayHeader(testBuf); 1706 encodeCborBreak(testBuf); 1707 assertHexAndClear("0x9fff"); 1708 1709 // [_ 1, [2, 3], [_ 4, 5]] 1710 encodeCborArrayHeader(testBuf); 1711 encodeCborInt(testBuf, 1); 1712 encodeCborArrayHeader(testBuf, 2); 1713 encodeCborInt(testBuf, 2); 1714 encodeCborInt(testBuf, 3); 1715 encodeCborArrayHeader(testBuf); 1716 encodeCborInt(testBuf, 4); 1717 encodeCborInt(testBuf, 5); 1718 encodeCborBreak(testBuf); 1719 encodeCborBreak(testBuf); 1720 assertHexAndClear("0x9f018202039f0405ffff"); 1721 1722 // [_ 1, [2, 3], [4, 5]] 1723 encodeCborArrayHeader(testBuf); 1724 encodeCborInt(testBuf, 1); 1725 encodeCborArrayHeader(testBuf, 2); 1726 encodeCborInt(testBuf, 2); 1727 encodeCborInt(testBuf, 3); 1728 encodeCborArrayHeader(testBuf, 2); 1729 encodeCborInt(testBuf, 4); 1730 encodeCborInt(testBuf, 5); 1731 encodeCborBreak(testBuf); 1732 assertHexAndClear("0x9f01820203820405ff"); 1733 1734 // [1, [2, 3], [_ 4, 5]] 1735 encodeCborArrayHeader(testBuf, 3); 1736 encodeCborInt(testBuf, 1); 1737 encodeCborArrayHeader(testBuf, 2); 1738 encodeCborInt(testBuf, 2); 1739 encodeCborInt(testBuf, 3); 1740 encodeCborArrayHeader(testBuf); 1741 encodeCborInt(testBuf, 4); 1742 encodeCborInt(testBuf, 5); 1743 encodeCborBreak(testBuf); 1744 assertHexAndClear("0x83018202039f0405ff"); 1745 1746 // [1, [_ 2, 3], [4, 5]] 1747 encodeCborArrayHeader(testBuf, 3); 1748 encodeCborInt(testBuf, 1); 1749 encodeCborArrayHeader(testBuf); 1750 encodeCborInt(testBuf, 2); 1751 encodeCborInt(testBuf, 3); 1752 encodeCborBreak(testBuf); 1753 encodeCborArrayHeader(testBuf, 2); 1754 encodeCborInt(testBuf, 4); 1755 encodeCborInt(testBuf, 5); 1756 assertHexAndClear("0x83019f0203ff820405"); 1757 1758 import std.range : iota; 1759 import std.algorithm : each; 1760 // [_ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25], 1761 encodeCborArrayHeader(testBuf); 1762 iota(1, 26).each!(a => encodeCborInt(testBuf, a)); 1763 encodeCborBreak(testBuf); 1764 assertHexAndClear("0x9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff"); 1765 1766 // {_ "a": 1, "b": [_ 2, 3]} 1767 encodeCborMapHeader(testBuf); 1768 encodeCborString(testBuf, "a"); 1769 encodeCborInt(testBuf, 1); 1770 encodeCborString(testBuf, "b"); 1771 encodeCborArrayHeader(testBuf); 1772 encodeCborInt(testBuf, 2); 1773 encodeCborInt(testBuf, 3); 1774 encodeCborBreak(testBuf); 1775 encodeCborBreak(testBuf); 1776 assertHexAndClear("0xbf61610161629f0203ffff"); 1777 1778 // ["a", {_ "b": "c"}] 1779 encodeCborArrayHeader(testBuf, 2); 1780 encodeCborString(testBuf, "a"); 1781 encodeCborMapHeader(testBuf); 1782 encodeCborString(testBuf, "b"); 1783 encodeCborString(testBuf, "c"); 1784 encodeCborBreak(testBuf); 1785 assertHexAndClear("0x826161bf61626163ff"); 1786 1787 // {_ "Fun": true, "Amt": -2} 1788 encodeCborMapHeader(testBuf); 1789 encodeCborString(testBuf, "Fun"); 1790 encodeCborBool(testBuf, true); 1791 encodeCborString(testBuf, "Amt"); 1792 encodeCborInt(testBuf, -2); 1793 encodeCborBreak(testBuf); 1794 assertHexAndClear("0xbf6346756ef563416d7421ff"); 1795 } 1796 1797 // indefinite-length decoding 1798 unittest 1799 { 1800 encodeCborBytesHeader(testBuf); 1801 encodeCborBytes(testBuf, cast(ubyte[])[1, 2]); 1802 encodeCborBytes(testBuf, cast(ubyte[])[1, 2]); 1803 encodeCborBreak(testBuf); 1804 ubyte[] output1; 1805 decodeCbor(testBuf.data, output1); 1806 assertEqual(output1, [1,2,1,2]); 1807 testBuf.clear(); 1808 1809 encodeCborStringHeader(testBuf); 1810 encodeCborString(testBuf, "zzz"); 1811 encodeCborString(testBuf, "zzz"); 1812 encodeCborString(testBuf, "zzz"); 1813 encodeCborBreak(testBuf); 1814 string output2; 1815 decodeCbor(testBuf.data, output2); 1816 assertEqual(output2, "zzzzzzzzz"); 1817 testBuf.clear(); 1818 1819 encodeCborArrayHeader(testBuf); 1820 encodeCborInt(testBuf, 1); 1821 encodeCborInt(testBuf, 2); 1822 encodeCborInt(testBuf, 3); 1823 encodeCborInt(testBuf, 4); 1824 encodeCborInt(testBuf, 5); 1825 encodeCborBreak(testBuf); 1826 int[] output3; 1827 decodeCbor(testBuf.data, output3); 1828 assertEqual(output3, [1,2,3,4,5]); 1829 testBuf.clear(); 1830 1831 encodeCborMapHeader(testBuf); 1832 encodeCborInt(testBuf, 1); 1833 encodeCborString(testBuf, "abc"); 1834 encodeCborInt(testBuf, 2); 1835 encodeCborString(testBuf, "def"); 1836 encodeCborBreak(testBuf); 1837 string[int] output4; 1838 decodeCbor(testBuf.data, output4); 1839 assertEqual(output4, [1:"abc", 2:"def"]); 1840 testBuf.clear(); 1841 } 1842 1843 // Printing 1844 unittest 1845 { 1846 Appender!(char[]) strBuf; strBuf.reserve(1024); 1847 void cmpStreamStringAndReset(string file = __MODULE__, size_t line = __LINE__)(string expected){ 1848 assertEqual!(file, line)(strBuf.data, expected); 1849 testBuf.clear(); 1850 strBuf.clear(); 1851 } 1852 import std.range : cycle, take, takeExactly, repeat, Repeat, iota; 1853 1854 encodeCborInt(testBuf, 1); 1855 printCborStream(testBuf.data, strBuf); 1856 cmpStreamStringAndReset("(posinteger, 1)\n"); 1857 1858 encodeCborArrayHeader(testBuf); 1859 encodeCborInt(testBuf, 1); 1860 encodeCborArrayHeader(testBuf, 2); 1861 encodeCborInt(testBuf, 2); 1862 encodeCborInt(testBuf, 3); 1863 encodeCborArrayHeader(testBuf); 1864 encodeCborInt(testBuf, 4); 1865 encodeCborInt(testBuf, 5); 1866 encodeCborBreak(testBuf); 1867 encodeCborBreak(testBuf); 1868 1869 printCborStream(testBuf.data, strBuf); 1870 cmpStreamStringAndReset( 1871 "(array, _)\n" 1872 " (posinteger, 1)\n" 1873 " (array, 2)\n" 1874 " (posinteger, 2)\n" 1875 " (posinteger, 3)\n" 1876 " (array, _)\n" 1877 " (posinteger, 4)\n" 1878 " (posinteger, 5)\n" 1879 " (break)\n" 1880 " (break)\n"); 1881 1882 encodeCborTag(testBuf, 100); 1883 encodeCborBytesHeader(testBuf, 16); 1884 auto bytes = iota!ubyte(10).cycle.take(16); 1885 encodeCborBytesItems(testBuf, bytes); 1886 1887 printCborStream(testBuf.data, strBuf); 1888 cmpStreamStringAndReset( 1889 "(tag, 100)\n"~ 1890 " (bytes, 16)\n"~ 1891 " (00010203040506070809000102030405)\n"); 1892 1893 encodeCbor(testBuf, true); 1894 printCborStream(testBuf.data, strBuf); 1895 cmpStreamStringAndReset("(true)\n"); 1896 1897 encodeCbor(testBuf, false); 1898 printCborStream(testBuf.data, strBuf); 1899 cmpStreamStringAndReset("(false)\n"); 1900 1901 encodeCbor(testBuf, null); 1902 printCborStream(testBuf.data, strBuf); 1903 cmpStreamStringAndReset("(null)\n"); 1904 1905 encodeCborUndefined(testBuf); 1906 printCborStream(testBuf.data, strBuf); 1907 cmpStreamStringAndReset("(undefined)\n"); 1908 1909 encodeCborSimple(testBuf, 4); 1910 printCborStream(testBuf.data, strBuf); 1911 cmpStreamStringAndReset("(simple, 4)\n"); 1912 1913 encodeCborInt(testBuf, -4); 1914 printCborStream(testBuf.data, strBuf); 1915 cmpStreamStringAndReset("(neginteger, -4)\n"); 1916 1917 encodeCborFloat(testBuf, 0.0); 1918 printCborStream(testBuf.data, strBuf); 1919 cmpStreamStringAndReset("(floating, 0)\n"); 1920 1921 encodeCborMapHeader(testBuf, 1); 1922 encodeCborInt(testBuf, 1); 1923 encodeCborInt(testBuf, 2); 1924 printCborStream(testBuf.data, strBuf); 1925 cmpStreamStringAndReset( 1926 "(map, 1)\n" 1927 " (posinteger, 1)\n" 1928 " (posinteger, 2)\n"); 1929 1930 encodeCborMapHeader(testBuf); 1931 encodeCborBreak(testBuf); 1932 printCborStream(testBuf.data, strBuf); 1933 cmpStreamStringAndReset( 1934 "(map, _)\n" 1935 " (break)\n"); 1936 1937 encodeCborBytesHeader(testBuf); 1938 encodeCborBreak(testBuf); 1939 printCborStream(testBuf.data, strBuf); 1940 cmpStreamStringAndReset( 1941 "(bytes, _)\n" 1942 " (break)\n"); 1943 1944 encodeCborStringHeader(testBuf, 0); 1945 printCborStream(testBuf.data, strBuf); 1946 cmpStreamStringAndReset( 1947 "(text, 0)\n" 1948 ` ""`"\n"); 1949 1950 encodeCborStringHeader(testBuf); 1951 encodeCborBreak(testBuf); 1952 printCborStream(testBuf.data, strBuf); 1953 cmpStreamStringAndReset( 1954 "(text, _)\n" 1955 " (break)\n"); 1956 1957 struct NoLengthRange(int val) 1958 { 1959 private int itemsLeft = 5; 1960 ubyte front() { return val; } 1961 bool empty() { return itemsLeft == 0; } 1962 void popFront() { --itemsLeft; } 1963 } 1964 // test byte range withou length 1965 encodeCborBytesHeader(testBuf, 5); 1966 encodeCborBytesItems(testBuf, NoLengthRange!1()); 1967 1968 printCborStream(testBuf.data, strBuf); 1969 cmpStreamStringAndReset( 1970 "(bytes, 5)\n"~ 1971 " (0101010101)\n"); 1972 } 1973 1974 /// structs, classes, tuples 1975 unittest // decoding exact 1976 { 1977 static struct Inner 1978 { 1979 int[] array; 1980 string someText; 1981 } 1982 1983 static struct Test1 1984 { 1985 ubyte b; 1986 short s; 1987 uint i; 1988 long l; 1989 float f; 1990 double d; 1991 ubyte[] arr; 1992 int[] intarr; 1993 string str; 1994 Inner inner; 1995 1996 void fun(){} // not encoded 1997 void* pointer; // not encoded 1998 } 1999 2000 ubyte[1024] buf1; 2001 size_t size; 2002 2003 Test1 test = Test1(42, -120, 111111, -123456789, 0.1234, -0.987654, 2004 cast(ubyte[])[1,2,3,4,5,6,7,8], [42, 41], "It is a test string", 2005 Inner([1,2,3,4,5], "Test of inner struct")); 2006 2007 size = encodeCborArray(buf1[], test); 2008 Test1 result = decodeCborSingle!Test1(buf1[0..size]); 2009 assertEqual(test, result); 2010 2011 import std.typecons : Tuple; 2012 2013 alias TupleVal = Tuple!(int, string, byte, string); 2014 auto testTuple = TupleVal(1, "hello", 56, "there"); 2015 2016 size = encodeCborArray(buf1[], testTuple); 2017 TupleVal resultTuple = decodeCborSingle!TupleVal(buf1[0..size]); 2018 assertEqual(testTuple, resultTuple); 2019 2020 static class Inner2 2021 { 2022 int val; 2023 ulong u; 2024 } 2025 2026 static class Test2 2027 { 2028 ubyte b; 2029 short s; 2030 uint i; 2031 long l; 2032 float f; 2033 double d; 2034 ubyte[] arr; 2035 string str; 2036 Inner2 inner; 2037 } 2038 2039 Test2 testClass = new Test2(); 2040 testClass.b = 42; 2041 testClass.s = -120; 2042 testClass.i = 111111; 2043 testClass.l = -123456789; 2044 testClass.f = 0.1234; 2045 testClass.d = -0.987654; 2046 testClass.arr = cast(ubyte[])[1,2,3,4,5,6,7,8]; 2047 testClass.str = "It is a test string"; 2048 testClass.inner = null; 2049 2050 size = encodeCborArray(buf1[], testClass); 2051 ubyte[] encodedBuf = buf1[0..size]; 2052 Test2 resultClass = decodeCborSingle!Test2(encodedBuf); 2053 assertEqual(encodedBuf.length, 0); 2054 2055 foreach(i, m; resultClass.tupleof) 2056 assertEqual(testClass.tupleof[i], m); 2057 2058 testClass.inner = new Inner2; 2059 testClass.inner.val = -555; 2060 testClass.inner.u = 123456789; 2061 2062 size = encodeCborArray(buf1[], testClass); 2063 resultClass = decodeCborSingle!Test2(buf1[0..size]); 2064 2065 foreach(i, m; resultClass.inner.tupleof) 2066 assertEqual(testClass.inner.tupleof[i], m); 2067 } 2068 2069 unittest // decoding with dup 2070 { 2071 ubyte[128] buf1; 2072 size_t size; 2073 2074 // with dup 2075 size = encodeCbor(buf1[], cast(ubyte[])[0, 1, 2, 3, 4, 5]); 2076 ubyte[] data = decodeCborSingleDup!(ubyte[])(buf1[0..size]); 2077 buf1[] = 0; // zero-out initial data, to check that result was duped. 2078 2079 assertEqual(data, [0, 1, 2, 3, 4, 5]); 2080 2081 // without dup 2082 size = encodeCbor(buf1[], cast(ubyte[])[0, 1, 2, 3, 4, 5]); 2083 data = decodeCborSingle!(ubyte[])(buf1[0..size]); 2084 buf1[] = 0; 2085 2086 assertEqual(data, [0, 0, 0, 0, 0, 0]); 2087 2088 // dup is only needed for ubyte[] and string types, 2089 // because they can be sliced from ubyte[] input range 2090 size = encodeCbor(buf1[], [0, 1, 2, 3, 4, 5]); // int array 2091 int[] intData = decodeCborSingle!(int[])(buf1[0..size]); // no dup 2092 buf1[] = 0; 2093 2094 assertEqual(intData, [0, 1, 2, 3, 4, 5]); 2095 2096 // also no slicing will occur if data was encoded as regular array and than 2097 // ubyte[] retreived. integer[] -> ubyte[] cast would occur causing possible data loss. 2098 // size = encodeCbor(buf1[], [0, 1, 2, 3, 4, 5]); // int array 2099 // integer array cannot be decoded as ubyte[], only raw arrays can 2100 // data = decodeCborSingle!(ubyte[])(buf1[0..size]); // CborException: Attempt to cast array to ubyte[] 2101 } 2102 2103 unittest // static arrays 2104 { 2105 ubyte[128] buf; 2106 size_t size; 2107 2108 // raw static array 2109 size = encodeCbor(buf[], cast(ubyte[6])[0, 1, 2, 3, 4, 5]); 2110 ubyte[6] data1 = decodeCborSingle!(ubyte[6])(buf[0..size]); 2111 assertEqual(data1, [0, 1, 2, 3, 4, 5]); 2112 2113 // regular static array 2114 size = encodeCbor(buf[], cast(int[6])[0, 1, 2, 3, 4, 5]); 2115 int[6] data2 = decodeCborSingle!(int[6])(buf[0..size]); 2116 assertEqual(data2, [0, 1, 2, 3, 4, 5]); 2117 } 2118 2119 unittest // const 2120 { 2121 ubyte[1024] buf; 2122 size_t size = encodeCbor(buf[], cast(const)0.0); 2123 2124 double cam2 = decodeCborSingle!double(buf[0..size]); 2125 } 2126 2127 unittest // using output range 2128 { 2129 import std.array : Appender; 2130 2131 Appender!(ubyte[]) buffer; 2132 ubyte[] testData = [0, 1, 2, 3, 4, 5]; 2133 2134 size_t size = encodeCbor(buffer, testData); 2135 2136 assertEqual(testData, decodeCborSingle!(ubyte[])(buffer.data)); 2137 } 2138 2139 unittest // recursive type 2140 { 2141 import std.array : Appender; 2142 2143 Appender!(ubyte[]) a; 2144 class Class 2145 { 2146 Class[] groups; 2147 } 2148 encodeCborArray(a, Class[].init); 2149 } 2150 2151 unittest // char arrays 2152 { 2153 ubyte[1024] buf; 2154 2155 size_t size = encodeCbor(buf[], cast(char[])"abc"); 2156 char[] str1 = decodeCborSingle!(char[])(buf[0..size]); 2157 assertEqual(str1, "abc"); 2158 2159 size = encodeCbor(buf[], cast(const char[])"abc"); 2160 const char[] str2 = decodeCborSingle!(const char[])(buf[0..size]); 2161 assertEqual(str2, "abc"); 2162 2163 size = encodeCbor(buf[], cast(immutable char[])"abc"); 2164 immutable char[] str3 = decodeCborSingle!(immutable char[])(buf[0..size]); 2165 assertEqual(str3, "abc"); 2166 } 2167 2168 unittest // char wchar dchar 2169 { 2170 ubyte[1024] buf; 2171 char testChar = 'c'; 2172 2173 size_t size = encodeCbor(buf[], cast(char)testChar); 2174 char chr = decodeCborSingle!(char)(buf[0..size]); 2175 assertEqual(chr, testChar); 2176 2177 size = encodeCbor(buf[], cast(wchar)testChar); 2178 wchar wchr = decodeCborSingle!(wchar)(buf[0..size]); 2179 assertEqual(wchr, testChar); 2180 2181 size = encodeCbor(buf[], cast(dchar)testChar); 2182 dchar dchr = decodeCborSingle!(dchar)(buf[0..size]); 2183 assertEqual(dchr, testChar); 2184 2185 size = encodeCbor(buf[], cast(const char)testChar); 2186 const char constchr = decodeCborSingle!(const char)(buf[0..size]); 2187 assertEqual(constchr, testChar); 2188 2189 size = encodeCbor(buf[], cast(const wchar)testChar); 2190 const wchar constwchr = decodeCborSingle!(const wchar)(buf[0..size]); 2191 assertEqual(constwchr, testChar); 2192 2193 size = encodeCbor(buf[], cast(immutable dchar)testChar); 2194 immutable dchar immdchr = decodeCborSingle!(immutable dchar)(buf[0..size]); 2195 assertEqual(immdchr, testChar); 2196 } 2197 2198 unittest // wstring dstring; static char wchar dchar arrays 2199 { 2200 ubyte[1024] buf; 2201 2202 size_t size = encodeCbor(buf[], "hello w"w); 2203 wstring wstr = decodeCborSingle!(wstring)(buf[0..size]); 2204 assertEqual(wstr, "hello w"w); 2205 2206 size = encodeCbor(buf[], "hello d"d); 2207 dstring dstr = decodeCborSingle!(dstring)(buf[0..size]); 2208 assertEqual(dstr, "hello d"d); 2209 2210 size = encodeCbor(buf[], cast(char[7])"hello c"); 2211 char[7] str1 = decodeCborSingle!(char[7])(buf[0..size]); 2212 assertEqual(str1, "hello c"); 2213 2214 size = encodeCbor(buf[], cast(wchar[7])"hello w"); 2215 wchar[7] wstr1 = decodeCborSingle!(wchar[7])(buf[0..size]); 2216 assertEqual(wstr1, "hello w"w); 2217 2218 size = encodeCbor(buf[], cast(dchar[7])"hello d"); 2219 dchar[7] dstr1 = decodeCborSingle!(dchar[7])(buf[0..size]); 2220 assertEqual(dstr1, "hello d"d); 2221 } 2222 2223 unittest // char[] wchar[] dchar[] 2224 { 2225 ubyte[1024] buf; 2226 2227 size_t size = encodeCbor(buf[], cast(char[])"hello"); 2228 char[] str1 = decodeCborSingle!(char[])(buf[0..size]); 2229 assertEqual(str1, "hello"); 2230 2231 size = encodeCbor(buf[], cast(wchar[])"hello"); 2232 wchar[] wstr1 = decodeCborSingle!(wchar[])(buf[0..size]); 2233 assertEqual(wstr1, "hello"w); 2234 2235 size = encodeCbor(buf[], cast(dchar[])"hello"); 2236 dchar[] dstr1 = decodeCborSingle!(dchar[])(buf[0..size]); 2237 assertEqual(dstr1, "hello"d); 2238 } 2239 2240 unittest // flatten mode 2241 { 2242 ubyte[1024] buf; 2243 size_t size; 2244 2245 size = encodeCbor(buf[], cast(ubyte[2])[1, 2]); 2246 assertEqual(toHexString(buf[0..size]), "0x420102"); 2247 2248 // Raw 2249 size = encodeCbor!(Yes.Flatten)(buf[], cast(ubyte[2])[1, 2]); 2250 assertEqual(toHexString(buf[0..size]), "0x0102"); 2251 auto arr1 = decodeCborSingle!(ubyte[2], Yes.Flatten)(buf[0..size]); 2252 assertEqual(arr1, cast(ubyte[2])[1, 2]); 2253 2254 // Array 2255 size = encodeCbor!(Yes.Flatten)(buf[], cast(int[2])[1, 2]); 2256 assertEqual(toHexString(buf[0..size]), "0x0102"); 2257 auto arr2 = decodeCborSingle!(int[2], Yes.Flatten)(buf[0..size]); 2258 assertEqual(arr2, cast(int[2])[1, 2]); 2259 2260 // String 2261 size = encodeCbor!(Yes.Flatten)(buf[], cast(immutable(char)[1])"a"); 2262 assertEqual(toHexString(buf[0..size]), "0x61"); 2263 auto str1 = decodeCborSingle!(immutable(char)[1], Yes.Flatten)(buf[0..size]); 2264 assertEqual(str1, "a"); 2265 2266 // Tuple 2267 import std.typecons : tuple; 2268 size = encodeCbor!(Yes.Flatten)(buf[], tuple(1, 2)); 2269 assertEqual(toHexString(buf[0..size]), "0x0102"); 2270 2271 // Struct 2272 static struct A { 2273 int a, b; 2274 } 2275 static struct B { 2276 A a; 2277 } 2278 static struct C { 2279 B b; 2280 } 2281 static struct D { 2282 C c; 2283 } 2284 size = encodeCbor!(Yes.Flatten)(buf[], D(C(B(A(1, 2))))); 2285 assertEqual(toHexString(buf[0..size]), "0x0102"); 2286 2287 size = encodeCbor!(No.Flatten)(buf[], D(C(B(A(1, 2))))); 2288 assertEqual(toHexString(buf[0..size]), "0x818181820102"); 2289 2290 size = encodeCbor!(Yes.Flatten)(buf[], cast(int)1); 2291 assertEqual(decodeCborSingle!(int, Yes.Flatten)(buf[0..size]), 1); 2292 2293 size = encodeCbor!(Yes.Flatten)(buf[], cast(float)1); 2294 assertEqual(decodeCborSingle!(float, Yes.Flatten)(buf[0..size]), 1); 2295 2296 size = encodeCbor!(Yes.Flatten)(buf[], true); 2297 assertEqual(decodeCborSingle!(bool, Yes.Flatten)(buf[0..size]), true); 2298 2299 size = encodeCbor!(Yes.Flatten)(buf[], 'a'); 2300 assertEqual(decodeCborSingle!(char, Yes.Flatten)(buf[0..size]), 'a'); 2301 2302 size = encodeCbor!(Yes.Flatten)(buf[], cast(ubyte[])[1,2]); 2303 assertEqual(decodeCborSingle!(ubyte[], Yes.Flatten)(buf[0..size]), cast(ubyte[])[1,2]); 2304 2305 size = encodeCbor!(Yes.Flatten)(buf[], cast(ubyte[2])[1,2]); 2306 assertEqual(decodeCborSingle!(ubyte[2], Yes.Flatten)(buf[0..size]), cast(ubyte[2])[1,2]); 2307 2308 size = encodeCborAggregate!(Yes.WithFieldName, Yes.Flatten)(buf[], new class{int a, b;}); 2309 assertEqual(decodeCborSingle!(int[string], Yes.Flatten)(buf[0..size]), ["a":0,"b":0]); 2310 2311 static struct ivec4 {int x,y,z,w;} 2312 static struct Transform 2313 { 2314 ivec4 pos; 2315 } 2316 2317 size = encodeCbor!(Yes.Flatten)(buf[], Transform( ivec4(1,2,3,4) ) ); 2318 assertEqual(decodeCborSingle!(Transform, Yes.Flatten)(buf[0..size]), Transform( ivec4(1,2,3,4) )); 2319 } 2320 2321 @nogc unittest // @nogc 2322 { 2323 ubyte[1024] buf; 2324 size_t size; 2325 2326 size = encodeCbor(buf[], 42); 2327 int num = decodeCborSingle!int(buf[0..size]); 2328 } 2329 2330 //------------------------------------------------------------------------------ 2331 // EEEEEEE XX XX CCCCC EEEEEEE PPPPPP TTTTTTT IIIII OOOOO NN NN 2332 // EE XX XX CC C EE PP PP TTT III OO OO NNN NN 2333 // EEEEE XXXX CC EEEEE PPPPPP TTT III OO OO NN N NN 2334 // EE XX XX CC C EE PP TTT III OO OO NN NNN 2335 // EEEEEEE XX XX CCCCC EEEEEEE PP TTT IIIII OOOO0 NN NN 2336 // 2337 //------------------------------------------------------------------------------ 2338 2339 class CborException : Exception 2340 { 2341 @trusted pure @nogc this(string message, string file = __FILE__, size_t line = __LINE__) 2342 { 2343 super(message, file, line); 2344 } 2345 } 2346 2347 private: 2348 2349 auto customEmplace(T, A...)(void[] buffer, A args) @nogc 2350 { 2351 buffer[] = typeid(T).init; 2352 return (cast(T)buffer.ptr).__ctor(args); 2353 } 2354 2355 CborException getException(A...)(string file, size_t line, string fmt, A args) @nogc 2356 { 2357 static ubyte[__traits(classInstanceSize, CborException)] exceptionBuffer; 2358 static char[512] charBuffer; 2359 import core.stdc.stdio : snprintf; 2360 int written = snprintf(charBuffer.ptr, charBuffer.length, fmt.ptr, args); 2361 return customEmplace!CborException(exceptionBuffer, cast(string)charBuffer[0..written], file, line); 2362 } 2363 2364 void onCastErrorToFrom(To)(CborTokenType from, string file = __FILE__, size_t line = __LINE__) @nogc 2365 { 2366 throw getException(file, line, "Attempt to cast %s to %s", from, typeid(To)); 2367 } 2368 2369 void onInsufficientInput(string file = __FILE__, size_t line = __LINE__) @nogc 2370 { 2371 throw getException(file, line, "Input range is too short"); 2372 } 2373 2374 void onUnsupportedTag(ubyte tag, string file = __FILE__, size_t line = __LINE__) @nogc 2375 { 2376 throw getException(file, line, "Unsupported tag found: %02x", tag); 2377 }