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