1 // Written in the D programming language. 2 3 /** 4 * _Base32 encoder and decoder, according to 5 * $(LINK2 http://tools.ietf.org/html/rfc4648, RFC 4648). 6 * 7 * Example: 8 * ----- 9 * ubyte[] data = [0xde, 0xad, 0xbe, 0xef, 0x01, 0x23]; 10 * 11 * const(char)[] encoded = Base32.encode(data); 12 * assert(encoded == "32W353YBEM======"); 13 * 14 * ubyte[] decoded = Base32.decode("32W353YBEM======"); 15 * assert(decoded == [0xde, 0xad, 0xbe, 0xef, 0x01, 0x23]); 16 * ----- 17 * 18 * Copyright: Copyright Kazuya Takahashi 2015. 19 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 20 * Authors: Kazuya Takahashi 21 * Standards: $(LINK2 http://tools.ietf.org/html/rfc4648, 22 * RFC 4648 - The Base16, _Base32, and Base64 Data Encodings) 23 */ 24 module base32; 25 26 27 import std.typecons : Flag; 28 29 30 /// 31 alias UseHex = Flag!"base32hex"; 32 /// 33 alias UsePad = Flag!"base32pad"; 34 35 36 /** 37 * The standard _base32 encoding, containing A-Z, 2-7 and =. 38 */ 39 alias Base32 = Base32Impl!(); 40 41 42 /** 43 * The _base32 encoding with "extended hex alphabet", also known as "base32hex", 44 * containing 0-9, A-V, and =. 45 */ 46 alias Base32Hex = Base32Impl!(UseHex.yes); 47 48 49 /** 50 * Implementation for _base32. 51 */ 52 template Base32Impl(UseHex useHex = UseHex.no, UsePad usePad= UsePad.yes) 53 { 54 // For all 55 import std.range.primitives; 56 import std.traits; 57 58 private immutable char pad = '='; 59 60 61 // For encoding 62 static if (useHex) 63 { 64 // convert to 0123456789ABCDEFGHIJKLMNOPQRSTUV 65 private @safe pure nothrow char encodingMap(ubyte a) 66 in 67 { 68 assert(0 <= a && a <= 31); 69 } 70 body 71 { 72 if (0 <= a && a <= 9) 73 { 74 return cast(char)(a + '0'); 75 } 76 else 77 { 78 return cast(char)(a - 10 + 'A'); 79 } 80 } 81 } 82 else 83 { 84 // convert to ABCDEFGHIJKLMNOPQRSTUVWXYZ234567 85 private @safe pure nothrow char encodingMap(ubyte a) 86 in 87 { 88 assert(0 <= a && a <= 31); 89 } 90 body 91 { 92 if (0 <= a && a <= 25) 93 { 94 return cast(char)(a + 'A'); 95 } 96 else 97 { 98 return cast(char)(a - 26 + '2'); 99 } 100 } 101 } 102 103 104 private enum size_t encSourceChunkLength = 5; 105 private enum size_t encResultChunkLength = 8; 106 private immutable size_t[5] shortfallToMeaningfulLength = [8, 7, 5, 4, 2]; 107 108 109 /** 110 * Calculates the length for encoding. 111 * 112 * Params: 113 * sourceLength = The length of a source. 114 * 115 * Returns: 116 * The resulting length after a source with $(D_PARAM sourceLength) is 117 * encoded. 118 */ 119 @safe pure nothrow 120 size_t encodeLength(in size_t sourceLength) 121 { 122 static if (usePad) 123 { 124 return (sourceLength / 5 + (sourceLength % 5 ? 1 : 0)) * 8; 125 } 126 else 127 { 128 immutable remainLength = sourceLength % encSourceChunkLength; 129 130 if (remainLength) 131 { 132 immutable sourceShortfallLength = encSourceChunkLength - remainLength; 133 immutable meaningfulLength = shortfallToMeaningfulLength[sourceShortfallLength]; 134 immutable padLength = encResultChunkLength - meaningfulLength; 135 136 return (sourceLength / 5 + (sourceLength % 5 ? 1 : 0)) * 8 - padLength; 137 } 138 else 139 { 140 return (sourceLength / 5 + (sourceLength % 5 ? 1 : 0)) * 8; 141 } 142 } 143 } 144 145 146 // ubyte[] to char[] 147 148 /** 149 * Encodes $(D_PARAM source) and stores the result in $(D_PARAM buffer). 150 * If no _buffer is assigned, a new buffer will be created. 151 * 152 * Params: 153 * source = An array to _encode. 154 * buffer = An array to store the encoded result. 155 * 156 * Returns: 157 * The slice of $(D_PARAM buffer) containing the result. 158 */ 159 @trusted pure 160 char[] encode(UB : ubyte, C = char)(in UB[] source, C[] buffer = null) 161 if (is(C == char)) 162 in 163 { 164 assert(!buffer.ptr || buffer.length >= encodeLength(source.length), 165 "Insufficient buffer for encoding"); 166 } 167 out(result) 168 { 169 assert(result.length == encodeLength(source.length), 170 "The length of result is different from Base32"); 171 } 172 body 173 { 174 immutable srcLen = source.length; 175 if (srcLen == 0) 176 { 177 return []; 178 } 179 180 if (!buffer.ptr) 181 { 182 buffer = new char[encodeLength(srcLen)]; 183 } 184 185 auto bufPtr = buffer.ptr; 186 auto srcPtr = source.ptr; 187 188 foreach (_; 0 .. srcLen / encSourceChunkLength) 189 { 190 *bufPtr++ = encodingMap(*srcPtr >>> 3 & 0x1f); 191 *bufPtr++ = encodingMap((*srcPtr << 2 | *++srcPtr >>> 6) & 0x1f); 192 *bufPtr++ = encodingMap(*srcPtr >>> 1 & 0x1f); 193 *bufPtr++ = encodingMap((*srcPtr << 4 | *++srcPtr >>> 4) & 0x1f); 194 *bufPtr++ = encodingMap((*srcPtr << 1 | *++srcPtr >>> 7) & 0x1f); 195 *bufPtr++ = encodingMap(*srcPtr >>> 2 & 0x1f); 196 *bufPtr++ = encodingMap((*srcPtr << 3 | *++srcPtr >>> 5) & 0x1f); 197 *bufPtr++ = encodingMap(*srcPtr & 0x1f); 198 199 srcPtr++; 200 } 201 202 immutable remainLength = srcLen % encSourceChunkLength; 203 204 if (remainLength != 0) 205 { 206 immutable sourceShortfallLength = encSourceChunkLength - remainLength; 207 immutable meaningfulLength = shortfallToMeaningfulLength[sourceShortfallLength]; 208 immutable padLength = encResultChunkLength - meaningfulLength; 209 210 if (meaningfulLength >= 2) 211 { 212 immutable a = *srcPtr; 213 immutable b = meaningfulLength == 2 ? 0 : *++srcPtr; 214 215 *bufPtr++ = encodingMap(a >>> 3 & 0x1f); 216 *bufPtr++ = encodingMap((a << 2 | b >>> 6) & 0x1f); 217 } 218 if (meaningfulLength >= 4) 219 { 220 immutable b = *srcPtr; 221 immutable c = meaningfulLength == 4 ? 0 : *++srcPtr; 222 223 *bufPtr++ = encodingMap(b >>> 1 & 0x1f); 224 *bufPtr++ = encodingMap((b << 4 | c >>> 4) & 0x1f); 225 } 226 if (meaningfulLength >= 5) 227 { 228 immutable c = *srcPtr; 229 immutable d = meaningfulLength == 5 ? 0 : *++srcPtr; 230 231 *bufPtr++ = encodingMap((c << 1 | d >>> 7) & 0x1f); 232 } 233 if (meaningfulLength == 7) 234 { 235 immutable d = *srcPtr; 236 237 *bufPtr++ = encodingMap(d >>> 2 & 0x1f); 238 *bufPtr++ = encodingMap(d << 3 & 0x1f); 239 } 240 241 static if (usePad) 242 { 243 buffer[bufPtr - buffer.ptr .. bufPtr - buffer.ptr + padLength] = pad; 244 bufPtr += padLength; 245 } 246 } 247 248 return buffer[0 .. bufPtr - buffer.ptr]; 249 } 250 251 252 // InputRange to char[] 253 254 /** 255 * Encodes $(D_PARAM source) and stores the result in $(D_PARAM buffer). 256 * If no _buffer is assigned, a new buffer will be created. 257 * 258 * Params: 259 * source = An InputRange to _encode. 260 * buffer = An array to store the encoded result. 261 * 262 * Returns: 263 * The slice of $(D_PARAM buffer) containing the result. 264 */ 265 char[] encode(UBR, C = char)(UBR source, C[] buffer = null) 266 if (!isArray!UBR && isInputRange!UBR && is(ElementType!UBR : ubyte) 267 && hasLength!UBR && is(C == char)) 268 in 269 { 270 assert(!buffer.ptr || buffer.length >= encodeLength(source.length), 271 "Insufficient buffer for encoding"); 272 } 273 out(result) 274 { 275 // delegate to the proxies 276 } 277 body 278 { 279 immutable srcLen = source.length; 280 if (srcLen == 0) 281 { 282 // proxy out contract 283 assert(0 == encodeLength(srcLen), 284 "The length of result is different from Base32"); 285 286 return []; 287 } 288 289 if (!buffer.ptr) 290 { 291 buffer = new char[encodeLength(srcLen)]; 292 } 293 294 auto bufPtr = buffer.ptr; 295 296 foreach (_; 0 .. srcLen / encSourceChunkLength) 297 { 298 immutable a = source.front; 299 source.popFront(); 300 immutable b = source.front; 301 source.popFront(); 302 immutable c = source.front; 303 source.popFront(); 304 immutable d = source.front; 305 source.popFront(); 306 immutable e = source.front; 307 source.popFront(); 308 309 *bufPtr++ = encodingMap(a >>> 3); 310 *bufPtr++ = encodingMap((a << 2 | b >>> 6) & 0x1f); 311 *bufPtr++ = encodingMap(b >>> 1 & 0x1f); 312 *bufPtr++ = encodingMap((b << 4 | c >>> 4) & 0x1f); 313 *bufPtr++ = encodingMap((c << 1 | d >>> 7) & 0x1f); 314 *bufPtr++ = encodingMap(d >>> 2 & 0x1f); 315 *bufPtr++ = encodingMap((d << 3 | e >>> 5) & 0x1f); 316 *bufPtr++ = encodingMap(e & 0x1f); 317 } 318 319 immutable remainLength = srcLen % encSourceChunkLength; 320 321 if (remainLength != 0) 322 { 323 immutable sourceShortfallLength = encSourceChunkLength - remainLength; 324 immutable meaningfulLength = shortfallToMeaningfulLength[sourceShortfallLength]; 325 immutable padLength = encResultChunkLength - meaningfulLength; 326 327 if (meaningfulLength >= 2) 328 { 329 immutable a = source.front; 330 source.popFront(); 331 immutable b = source.empty ? 0 : source.front; 332 333 *bufPtr++ = encodingMap(a >>> 3); 334 *bufPtr++ = encodingMap((a << 2 | b >>> 6) & 0x1f); 335 } 336 if (meaningfulLength >= 4) 337 { 338 immutable b = source.front; 339 source.popFront(); 340 immutable c = source.empty ? 0 : source.front; 341 342 *bufPtr++ = encodingMap(b >>> 1 & 0x1f); 343 *bufPtr++ = encodingMap((b << 4 | c >>> 4) & 0x1f); 344 } 345 if (meaningfulLength >= 5) 346 { 347 immutable c = source.front; 348 source.popFront(); 349 immutable d = source.empty ? 0 : source.front; 350 351 *bufPtr++ = encodingMap((c << 1 | d >>> 7) & 0x1f); 352 } 353 if (meaningfulLength == 7) 354 { 355 immutable d = source.front; 356 source.popFront(); 357 assert(source.empty); 358 359 *bufPtr++ = encodingMap(d >>> 2 & 0x1f); 360 *bufPtr++ = encodingMap(d << 3 & 0x1f); 361 } 362 363 static if (usePad) 364 { 365 buffer[bufPtr - buffer.ptr .. bufPtr - buffer.ptr + padLength] = pad; 366 bufPtr += padLength; 367 } 368 } 369 370 // proxy out contract 371 assert(bufPtr - buffer.ptr == encodeLength(srcLen), 372 "The length of result is different from Base32"); 373 374 return buffer[0 .. bufPtr - buffer.ptr]; 375 } 376 377 378 // ubyte[] to OutputRange 379 380 /** 381 * Encodes $(D_PARAM source) and outputs the result to $(D_PARAM range). 382 * 383 * Params: 384 * source = An array to _encode. 385 * range = An OutputRange to receive the encoded result. 386 * 387 * Returns: 388 * The length of the output characters. 389 */ 390 size_t encode(UB : ubyte, CR)(in UB[] source, CR range) 391 if (isOutputRange!(CR, char)) 392 out(result) 393 { 394 assert(result == encodeLength(source.length), 395 "The number of put is different from the length of Base32"); 396 } 397 body 398 { 399 immutable srcLen = source.length; 400 if (srcLen == 0) 401 { 402 return 0; 403 } 404 405 size_t putCnt; 406 auto srcPtr = source.ptr; 407 408 foreach (_; 0 .. srcLen / encSourceChunkLength) 409 { 410 range.put(encodingMap(*srcPtr >>> 3 & 0x1f)); 411 range.put(encodingMap((*srcPtr << 2 | *++srcPtr >>> 6) & 0x1f)); 412 range.put(encodingMap(*srcPtr >>> 1 & 0x1f)); 413 range.put(encodingMap((*srcPtr << 4 | *++srcPtr >>> 4) & 0x1f)); 414 range.put(encodingMap((*srcPtr << 1 | *++srcPtr >>> 7) & 0x1f)); 415 range.put(encodingMap(*srcPtr >>> 2 & 0x1f)); 416 range.put(encodingMap((*srcPtr << 3 | *++srcPtr >>> 5) & 0x1f)); 417 range.put(encodingMap(*srcPtr & 0x1f)); 418 419 putCnt += 8; 420 srcPtr++; 421 } 422 423 immutable remainLength = srcLen % encSourceChunkLength; 424 425 if (remainLength != 0) 426 { 427 immutable sourceShortfallLength = encSourceChunkLength - remainLength; 428 immutable meaningfulLength = shortfallToMeaningfulLength[sourceShortfallLength]; 429 immutable padLength = encResultChunkLength - meaningfulLength; 430 431 if (meaningfulLength >= 2) 432 { 433 immutable a = *srcPtr; 434 immutable b = meaningfulLength == 2 ? 0 : *++srcPtr; 435 436 range.put(encodingMap(a >>> 3 & 0x1f)); 437 range.put(encodingMap((a << 2 | b >>> 6) & 0x1f)); 438 439 putCnt += 2; 440 } 441 if (meaningfulLength >= 4) 442 { 443 immutable b = *srcPtr; 444 immutable c = meaningfulLength == 4 ? 0 : *++srcPtr; 445 446 range.put(encodingMap(b >>> 1 & 0x1f)); 447 range.put(encodingMap((b << 4 | c >>> 4) & 0x1f)); 448 449 putCnt += 2; 450 } 451 if (meaningfulLength >= 5) 452 { 453 immutable c = *srcPtr; 454 immutable d = meaningfulLength == 5 ? 0 : *++srcPtr; 455 456 range.put(encodingMap((c << 1 | d >>> 7) & 0x1f)); 457 458 putCnt++; 459 } 460 if (meaningfulLength == 7) 461 { 462 immutable d = *srcPtr; 463 464 range.put(encodingMap(d >>> 2 & 0x1f)); 465 range.put(encodingMap(d << 3 & 0x1f)); 466 467 putCnt += 2; 468 } 469 470 static if (usePad) 471 { 472 foreach (_; 0 .. padLength) 473 { 474 range.put(pad); 475 } 476 putCnt += padLength; 477 } 478 } 479 480 return putCnt; 481 } 482 483 484 // InputRange to OutputRange 485 486 /** 487 * Encodes $(D_PARAM source) and outputs the result to $(D_PARAM range). 488 * 489 * Params: 490 * source = An InputRange to _encode. 491 * range = An OutputRange to receive the encoded result. 492 * 493 * Returns: 494 * The length of the output characters. 495 */ 496 size_t encode(UBR, CR)(UBR source, CR range) 497 if (!isArray!UBR && isInputRange!UBR && is(ElementType!UBR : ubyte) 498 && hasLength!UBR && isOutputRange!(CR, char)) 499 out(result) 500 { 501 // delegate to the proxies 502 } 503 body 504 { 505 immutable srcLen = source.length; 506 if (srcLen == 0) 507 { 508 // proxy out contract 509 assert(0 == encodeLength(srcLen), 510 "The number of put is different from the length of Base32"); 511 512 return 0; 513 } 514 515 size_t putCnt; 516 517 foreach (_; 0 .. srcLen / encSourceChunkLength) 518 { 519 immutable a = source.front; 520 source.popFront(); 521 immutable b = source.front; 522 source.popFront(); 523 immutable c = source.front; 524 source.popFront(); 525 immutable d = source.front; 526 source.popFront(); 527 immutable e = source.front; 528 source.popFront(); 529 530 range.put(encodingMap(a >>> 3)); 531 range.put(encodingMap((a << 2 | b >>> 6) & 0x1f)); 532 range.put(encodingMap(b >>> 1 & 0x1f)); 533 range.put(encodingMap((b << 4 | c >>> 4) & 0x1f)); 534 range.put(encodingMap((c << 1 | d >>> 7) & 0x1f)); 535 range.put(encodingMap(d >>> 2 & 0x1f)); 536 range.put(encodingMap((d << 3 | e >>> 5) & 0x1f)); 537 range.put(encodingMap(e & 0x1f)); 538 539 putCnt += 8; 540 } 541 542 immutable remainLength = srcLen % encSourceChunkLength; 543 544 if (remainLength != 0) 545 { 546 immutable sourceShortfallLength = encSourceChunkLength - remainLength; 547 immutable meaningfulLength = shortfallToMeaningfulLength[sourceShortfallLength]; 548 immutable padLength = encResultChunkLength - meaningfulLength; 549 550 if (meaningfulLength >= 2) 551 { 552 immutable a = source.front; 553 source.popFront(); 554 immutable b = source.empty ? 0 : source.front; 555 556 range.put(encodingMap(a >>> 3)); 557 range.put(encodingMap((a << 2 | b >>> 6) & 0x1f)); 558 559 putCnt += 2; 560 } 561 if (meaningfulLength >= 4) 562 { 563 immutable b = source.front; 564 source.popFront(); 565 immutable c = source.empty ? 0 : source.front; 566 567 range.put(encodingMap(b >>> 1 & 0x1f)); 568 range.put(encodingMap((b << 4 | c >>> 4) & 0x1f)); 569 570 putCnt += 2; 571 } 572 if (meaningfulLength >= 5) 573 { 574 immutable c = source.front; 575 source.popFront(); 576 immutable d = source.empty ? 0 : source.front; 577 578 range.put(encodingMap((c << 1 | d >>> 7) & 0x1f)); 579 580 putCnt++; 581 } 582 if (meaningfulLength == 7) 583 { 584 immutable d = source.front; 585 source.popFront(); 586 assert(source.empty); 587 588 range.put(encodingMap(d >>> 2 & 0x1f)); 589 range.put(encodingMap(d << 3 & 0x1f)); 590 591 putCnt += 2; 592 } 593 594 static if (usePad) 595 { 596 foreach (_; 0 .. padLength) 597 { 598 range.put(pad); 599 } 600 putCnt += padLength; 601 } 602 } 603 604 // proxy out contract 605 assert(putCnt == encodeLength(srcLen), 606 "The number of put is different from the length of Base32"); 607 608 return putCnt; 609 } 610 611 612 // For decoding 613 private enum size_t decSourceChunkLength = 8; 614 private enum size_t decResultChunkLength = 5; 615 616 617 private @safe pure nothrow 618 size_t normalize(in size_t sourceLength) 619 { 620 size_t r = sourceLength; 621 622 while(r % decSourceChunkLength) 623 { 624 r++; 625 } 626 627 return r; 628 } 629 630 631 private @safe pure nothrow 632 size_t padToMeaningfulLength(in size_t padLength) 633 { 634 switch (padLength) 635 { 636 case 0: 637 return 5; 638 case 1: 639 return 4; 640 case 3: 641 return 3; 642 case 4: 643 return 2; 644 case 6: 645 return 1; 646 default: 647 return size_t.max; 648 } 649 } 650 651 652 private @safe pure nothrow 653 bool isValidPadLength(in size_t padLength) 654 { 655 immutable m = padToMeaningfulLength(padLength); 656 657 return 0 < m && m < 6; 658 } 659 660 661 /** 662 * Calculates the length for decoding. 663 * 664 * Params: 665 * sourceLength = The length of a source. 666 * 667 * Returns: 668 * The maximum length which the result may have after a source with 669 * $(D_PARAM sourceLength) is decoded. 670 */ 671 @safe pure nothrow 672 size_t decodeLength(in size_t sourceLength) 673 { 674 static if (usePad){ 675 return (sourceLength / 8) * 5; 676 } 677 else 678 { 679 return (sourceLength / 8 + (sourceLength % 8 ? 1 : 0))* 5; 680 } 681 } 682 683 684 // This should be called after input validation 685 private @safe pure nothrow 686 size_t preciseDecodeLength(Array)(Array source) 687 { 688 immutable approx = decodeLength(source.length); 689 if (approx == 0) 690 { 691 return 0; 692 } 693 694 695 static if (usePad) 696 { 697 size_t padLength; 698 699 if (source[$ - 1] == pad) 700 { 701 padLength = 1; 702 703 if (source[$ - 2] == pad && source[$ - 3] == pad) 704 { 705 padLength = 3; 706 707 if (source[$ - 4] == pad) 708 { 709 padLength = 4; 710 711 if (source[$ - 5] == pad && source[$ - 6] == pad) 712 { 713 padLength = 6; 714 } 715 } 716 } 717 } 718 } 719 else 720 { 721 immutable padLength = normalize(source.length) - source.length; 722 } 723 724 immutable meaninglessLength = decResultChunkLength - padToMeaningfulLength(padLength); 725 return approx - meaninglessLength; 726 } 727 728 729 // 730 private @safe pure nothrow 731 size_t preciseDecodeLength(in size_t sourceLength, in size_t padLength) 732 { 733 immutable approx = decodeLength(sourceLength); 734 if (approx == 0) 735 { 736 return 0; 737 } 738 739 immutable meaninglessLength = decResultChunkLength - padToMeaningfulLength(padLength); 740 return approx - meaninglessLength; 741 } 742 743 744 // char[] to ubyte[] 745 746 /** 747 * Decodes $(D_PARAM source) and stores the result in $(D_PARAM buffer). 748 * If no _buffer is assigned, a new buffer will be created. 749 * 750 * Params: 751 * source = A _base32 character array to _decode. 752 * buffer = An array to store the decoded result. 753 * 754 * Returns: 755 * The slice of $(D_PARAM buffer) containing the result. 756 * 757 * Throws: 758 * Exception if $(D_PARAM source) is an invalid _base32 data. 759 */ 760 @trusted pure 761 ubyte[] decode(C : dchar, UB = ubyte)(in C[] source, UB[] buffer = null) 762 if (isOutputRange!(UB[], ubyte)) 763 in 764 { 765 assert(!buffer.ptr || buffer.length >= decodeLength(source.length), 766 "Insufficient buffer for decoding"); 767 } 768 out(result) 769 { 770 immutable expect = preciseDecodeLength(source); 771 assert(result.length == expect, 772 "The result length is different from the expected"); 773 } 774 body 775 { 776 immutable actualLen = source.length; 777 778 if (actualLen == 0) 779 { 780 return []; 781 } 782 783 static if (usePad) 784 { 785 immutable normalizedLen = actualLen; 786 787 if (normalizedLen % decSourceChunkLength) 788 { 789 throw new Base32Exception("Invalid source length"); 790 } 791 } 792 else 793 { 794 immutable normalizedLen = normalize(actualLen); 795 796 if (!isValidPadLength(normalizedLen - actualLen)) 797 { 798 throw new Base32Exception("Invalid source length"); 799 } 800 } 801 802 if (!buffer.ptr) 803 { 804 buffer = new ubyte[decodeLength(normalizedLen)]; 805 } 806 807 size_t padLength; 808 size_t processedLength; 809 auto srcPtr = source.ptr; 810 auto bufPtr = buffer.ptr; 811 812 ubyte[decSourceChunkLength] tmpBuffer; 813 foreach (_; 0 .. normalizedLen / decSourceChunkLength) 814 { 815 bool firstPadFound; 816 817 foreach (i, ref e; tmpBuffer) 818 { 819 static if (!usePad) 820 { 821 if (firstPadFound || srcPtr - source.ptr == actualLen) 822 { 823 e = 0; 824 825 if (!firstPadFound) 826 { 827 firstPadFound = true; 828 padLength = decSourceChunkLength - i; 829 } 830 831 continue; 832 } 833 } 834 835 immutable c = *srcPtr++; 836 837 static if (useHex) 838 { 839 if (!firstPadFound && '0' <= c && c <= '9') 840 { 841 e = cast(ubyte)(c - '0'); 842 } 843 else if (!firstPadFound && 'A' <= c && c <= 'V') 844 { 845 e = cast(ubyte)(c - 'A' + 10); 846 } 847 else if (c == pad) 848 { 849 e = 0; 850 851 if (!firstPadFound) 852 { 853 firstPadFound = true; 854 padLength = decSourceChunkLength - i; 855 856 if (!usePad || !isValidPadLength(padLength)) 857 { 858 throw new Base32Exception("Invalid padding found"); 859 } 860 } 861 } 862 else 863 { 864 import std.conv : text; 865 866 throw new Base32Exception(text("Invalid character '", c, "' found")); 867 } 868 } 869 else 870 { 871 if (!firstPadFound && 'A' <= c && c <= 'Z') 872 { 873 e = cast(ubyte)(c - 'A'); 874 } 875 else if (!firstPadFound && '2' <= c && c <= '7') 876 { 877 e = cast(ubyte)(c - '2' + 26); 878 } 879 else if (c == pad) 880 { 881 e = 0; 882 883 if (!firstPadFound) 884 { 885 firstPadFound = true; 886 padLength = decSourceChunkLength - i; 887 888 if (!usePad || !isValidPadLength(padLength)) 889 { 890 throw new Base32Exception("Invalid padding found"); 891 } 892 } 893 } 894 else 895 { 896 import std.conv : text; 897 898 throw new Base32Exception(text("Invalid character '", c, "' found")); 899 } 900 } 901 } 902 903 immutable meaningfulLength = padToMeaningfulLength(padLength); 904 processedLength += meaningfulLength; 905 906 *bufPtr++ = cast(ubyte)(tmpBuffer[0] << 3 | tmpBuffer[1] >>> 2); 907 if (meaningfulLength == 1) 908 { 909 break; 910 } 911 912 *bufPtr++ = cast(ubyte)(tmpBuffer[1] << 6 | tmpBuffer[2] << 1 | tmpBuffer[3] >>> 4); 913 if (meaningfulLength == 2) 914 { 915 break; 916 } 917 918 *bufPtr++ = cast(ubyte)(tmpBuffer[3] << 4 | tmpBuffer[4] >>> 1); 919 if (meaningfulLength == 3) 920 { 921 break; 922 } 923 924 *bufPtr++ = cast(ubyte)(tmpBuffer[4] << 7 | tmpBuffer[5] << 2 | tmpBuffer[6] >>> 3); 925 if (meaningfulLength == 4) 926 { 927 break; 928 } 929 930 *bufPtr++ = cast(ubyte)(tmpBuffer[6] << 5 | tmpBuffer[7]); 931 } 932 933 return buffer[0 .. processedLength]; 934 } 935 936 937 // InputRange to ubyte[] 938 939 /** 940 * Decodes $(D_PARAM source) and stores the result in $(D_PARAM buffer). 941 * If no _buffer is assigned, a new buffer will be created. 942 * 943 * Params: 944 * source = A _base32 InputRange to _decode. 945 * buffer = An array to store the decoded result. 946 * 947 * Returns: 948 * The slice of $(D_PARAM buffer) containing the result. 949 * 950 * Throws: 951 * Exception if $(D_PARAM source) is an invalid _base32 data. 952 */ 953 ubyte[] decode(CR, UB = ubyte)(CR source, UB[] buffer = null) 954 if (!isArray!CR && isInputRange!CR && is(ElementType!CR : dchar) 955 && hasLength!CR && isOutputRange!(UB[], ubyte)) 956 in 957 { 958 assert(!buffer.ptr || buffer.length >= decodeLength(source.length), 959 "Insufficient buffer for decoding"); 960 } 961 out(result) 962 { 963 // delegate to the proxies 964 } 965 body 966 { 967 immutable actualLen = source.length; 968 969 if (actualLen == 0) 970 { 971 // proxy out contract 972 assert(0 == preciseDecodeLength(actualLen, 0)); 973 return []; 974 } 975 976 static if (usePad) 977 { 978 immutable normalizedLen = actualLen; 979 980 if (normalizedLen % decSourceChunkLength) 981 { 982 throw new Base32Exception("Invalid source length"); 983 } 984 } 985 else 986 { 987 immutable normalizedLen = normalize(actualLen); 988 989 if (!isValidPadLength(normalizedLen - actualLen)) 990 { 991 throw new Base32Exception("Invalid source length"); 992 } 993 } 994 995 if (!buffer.ptr) 996 { 997 buffer = new ubyte[decodeLength(normalizedLen)]; 998 } 999 1000 size_t padLength; 1001 size_t processedLength; 1002 auto bufPtr = buffer.ptr; 1003 1004 ubyte[decSourceChunkLength] tmpBuffer; 1005 foreach (_; 0 .. normalizedLen / decSourceChunkLength) 1006 { 1007 bool firstPadFound; 1008 1009 foreach (i, ref e; tmpBuffer) 1010 { 1011 static if (!usePad) 1012 { 1013 if (firstPadFound || source.empty) 1014 { 1015 e = 0; 1016 1017 if (!firstPadFound) 1018 { 1019 firstPadFound = true; 1020 padLength = decSourceChunkLength - i; 1021 } 1022 1023 continue; 1024 } 1025 } 1026 1027 immutable c = source.front; 1028 source.popFront(); 1029 1030 static if (useHex) 1031 { 1032 if (!firstPadFound && '0' <= c && c <= '9') 1033 { 1034 e = cast(ubyte)(c - '0'); 1035 } 1036 else if (!firstPadFound && 'A' <= c && c <= 'V') 1037 { 1038 e = cast(ubyte)(c - 'A' + 10); 1039 } 1040 else if (c == pad) 1041 { 1042 e = 0; 1043 1044 if (!firstPadFound) 1045 { 1046 firstPadFound = true; 1047 padLength = decSourceChunkLength - i; 1048 1049 if (!isValidPadLength(padLength)) 1050 { 1051 throw new Base32Exception("Invalid padding found"); 1052 } 1053 } 1054 } 1055 else 1056 { 1057 import std.conv : text; 1058 1059 throw new Base32Exception(text("Invalid character '", c, "' found")); 1060 } 1061 } 1062 else 1063 { 1064 if (!firstPadFound && 'A' <= c && c <= 'Z') 1065 { 1066 e = cast(ubyte)(c - 'A'); 1067 } 1068 else if (!firstPadFound && '2' <= c && c <= '7') 1069 { 1070 e = cast(ubyte)(c - '2' + 26); 1071 } 1072 else if (c == pad) 1073 { 1074 e = 0; 1075 1076 if (!firstPadFound) 1077 { 1078 firstPadFound = true; 1079 padLength = decSourceChunkLength - i; 1080 1081 if (!isValidPadLength(padLength)) 1082 { 1083 throw new Base32Exception("Invalid padding found"); 1084 } 1085 } 1086 } 1087 else 1088 { 1089 import std.conv : text; 1090 1091 throw new Base32Exception(text("Invalid character '", c, "' found")); 1092 } 1093 } 1094 } 1095 1096 immutable meaningfulLength = padToMeaningfulLength(padLength); 1097 processedLength += meaningfulLength; 1098 1099 *bufPtr++ = cast(ubyte)(tmpBuffer[0] << 3 | tmpBuffer[1] >>> 2); 1100 if (meaningfulLength == 1) 1101 { 1102 break; 1103 } 1104 1105 *bufPtr++ = cast(ubyte)(tmpBuffer[1] << 6 | tmpBuffer[2] << 1 | tmpBuffer[3] >>> 4); 1106 if (meaningfulLength == 2) 1107 { 1108 break; 1109 } 1110 1111 *bufPtr++ = cast(ubyte)(tmpBuffer[3] << 4 | tmpBuffer[4] >>> 1); 1112 if (meaningfulLength == 3) 1113 { 1114 break; 1115 } 1116 1117 *bufPtr++ = cast(ubyte)(tmpBuffer[4] << 7 | tmpBuffer[5] << 2 | tmpBuffer[6] >>> 3); 1118 if (meaningfulLength == 4) 1119 { 1120 break; 1121 } 1122 1123 *bufPtr++ = cast(ubyte)(tmpBuffer[6] << 5 | tmpBuffer[7]); 1124 } 1125 1126 // proxy out contract 1127 assert(processedLength == preciseDecodeLength(actualLen, padLength)); 1128 1129 return buffer[0 .. processedLength]; 1130 } 1131 1132 1133 // char[] to OutputRange 1134 1135 /** 1136 * Decodes $(D_PARAM source) and outputs the result to $(D_PARAM range). 1137 * 1138 * Params: 1139 * source = A _base32 array to _decode. 1140 * range = An OutputRange to receive decoded result 1141 * 1142 * Returns: 1143 * The number of the output characters. 1144 * 1145 * Throws: 1146 * Exception if $(D_PARAM source) is invalid _base32 data. 1147 */ 1148 size_t decode(C : dchar, UBR)(in C[] source, UBR range) 1149 if (!is(UBR == ubyte[]) && isOutputRange!(UBR, ubyte)) 1150 out(result) 1151 { 1152 immutable expect = preciseDecodeLength(source); 1153 assert(result == expect, 1154 "The result is different from the expected"); 1155 } 1156 body 1157 { 1158 immutable actualLen = source.length; 1159 1160 if (actualLen == 0) 1161 { 1162 return 0; 1163 } 1164 1165 static if (usePad) 1166 { 1167 immutable normalizedLen = actualLen; 1168 1169 if (normalizedLen % decSourceChunkLength) 1170 { 1171 throw new Base32Exception("Invalid source length"); 1172 } 1173 } 1174 else 1175 { 1176 immutable normalizedLen = normalize(actualLen); 1177 1178 if (!isValidPadLength(normalizedLen - actualLen)) 1179 { 1180 throw new Base32Exception("Invalid source length"); 1181 } 1182 } 1183 1184 size_t padLength; 1185 size_t processedLength; 1186 auto srcPtr = source.ptr; 1187 1188 ubyte[decSourceChunkLength] tmpBuffer; 1189 foreach (_; 0 .. normalizedLen / decSourceChunkLength) 1190 { 1191 bool firstPadFound; 1192 1193 foreach (i, ref e; tmpBuffer) 1194 { 1195 static if (!usePad) 1196 { 1197 if (firstPadFound || srcPtr - source.ptr == actualLen) 1198 { 1199 e = 0; 1200 1201 if (!firstPadFound) 1202 { 1203 firstPadFound = true; 1204 padLength = decSourceChunkLength - i; 1205 } 1206 1207 continue; 1208 } 1209 } 1210 1211 immutable c = *srcPtr++; 1212 1213 static if (useHex) 1214 { 1215 if (!firstPadFound && '0' <= c && c <= '9') 1216 { 1217 e = cast(ubyte)(c - '0'); 1218 } 1219 else if (!firstPadFound && 'A' <= c && c <= 'V') 1220 { 1221 e = cast(ubyte)(c - 'A' + 10); 1222 } 1223 else if (c == pad) 1224 { 1225 e = 0; 1226 1227 if (!firstPadFound) 1228 { 1229 firstPadFound = true; 1230 padLength = decSourceChunkLength - i; 1231 1232 if (!usePad || !isValidPadLength(padLength)) 1233 { 1234 throw new Base32Exception("Invalid padding found"); 1235 } 1236 } 1237 } 1238 else 1239 { 1240 import std.conv : text; 1241 1242 throw new Base32Exception(text("Invalid character '", c, "' found")); 1243 } 1244 } 1245 else 1246 { 1247 if (!firstPadFound && 'A' <= c && c <= 'Z') 1248 { 1249 e = cast(ubyte)(c - 'A'); 1250 } 1251 else if (!firstPadFound && '2' <= c && c <= '7') 1252 { 1253 e = cast(ubyte)(c - '2' + 26); 1254 } 1255 else if (c == pad) 1256 { 1257 e = 0; 1258 1259 if (!firstPadFound) 1260 { 1261 firstPadFound = true; 1262 padLength = decSourceChunkLength - i; 1263 1264 if (!usePad || !isValidPadLength(padLength)) 1265 { 1266 throw new Base32Exception("Invalid padding found"); 1267 } 1268 } 1269 } 1270 else 1271 { 1272 import std.conv : text; 1273 1274 throw new Base32Exception(text("Invalid character '", c, "' found")); 1275 } 1276 } 1277 } 1278 1279 immutable meaningfulLength = padToMeaningfulLength(padLength); 1280 processedLength += meaningfulLength; 1281 1282 range.put(cast(ubyte)(tmpBuffer[0] << 3 | tmpBuffer[1] >>> 2)); 1283 if (meaningfulLength == 1) 1284 { 1285 break; 1286 } 1287 1288 range.put(cast(ubyte)(tmpBuffer[1] << 6 | tmpBuffer[2] << 1 | tmpBuffer[3] >>> 4)); 1289 if (meaningfulLength == 2) 1290 { 1291 break; 1292 } 1293 1294 range.put(cast(ubyte)(tmpBuffer[3] << 4 | tmpBuffer[4] >>> 1)); 1295 if (meaningfulLength == 3) 1296 { 1297 break; 1298 } 1299 1300 range.put(cast(ubyte)(tmpBuffer[4] << 7 | tmpBuffer[5] << 2 | tmpBuffer[6] >>> 3)); 1301 if (meaningfulLength == 4) 1302 { 1303 break; 1304 } 1305 1306 range.put(cast(ubyte)(tmpBuffer[6] << 5 | tmpBuffer[7])); 1307 } 1308 1309 return processedLength; 1310 } 1311 1312 1313 // InputRange to OutputRange 1314 1315 /** 1316 * Decodes $(D_PARAM source) and outputs the result to $(D_PARAM range). 1317 * 1318 * Params: 1319 * source = A _base32 InputRange to _decode. 1320 * range = An OutputRange to receive decoded result 1321 * 1322 * Returns: 1323 * The number of the output characters. 1324 * 1325 * Throws: 1326 * Exception if $(D_PARAM source) is invalid _base32 data. 1327 */ 1328 size_t decode(CR, UBR)(CR source, UBR range) 1329 if (!isArray!CR && isInputRange!CR && is(ElementType!CR : dchar) 1330 && hasLength!CR && !is(UBR == ubyte[]) 1331 && isOutputRange!(UBR, ubyte)) 1332 out(result) 1333 { 1334 // delegate to the proxies 1335 } 1336 body 1337 { 1338 immutable actualLen = source.length; 1339 1340 if (actualLen == 0) 1341 { 1342 // proxy out contract 1343 assert(0 == preciseDecodeLength(actualLen, 0)); 1344 return 0; 1345 } 1346 1347 static if (usePad) 1348 { 1349 immutable normalizedLen = actualLen; 1350 1351 if (normalizedLen % decSourceChunkLength) 1352 { 1353 throw new Base32Exception("Invalid source length"); 1354 } 1355 } 1356 else 1357 { 1358 immutable normalizedLen = normalize(actualLen); 1359 1360 if (!isValidPadLength(normalizedLen - actualLen)) 1361 { 1362 throw new Base32Exception("Invalid source length"); 1363 } 1364 } 1365 1366 size_t padLength; 1367 size_t processedLength; 1368 1369 ubyte[decSourceChunkLength] tmpBuffer; 1370 foreach (_; 0 .. normalizedLen / decSourceChunkLength) 1371 { 1372 bool firstPadFound; 1373 1374 foreach (i, ref e; tmpBuffer) 1375 { 1376 static if (!usePad) 1377 { 1378 if (firstPadFound || source.empty) 1379 { 1380 e = 0; 1381 1382 if (!firstPadFound) 1383 { 1384 firstPadFound = true; 1385 padLength = decSourceChunkLength - i; 1386 } 1387 1388 continue; 1389 } 1390 } 1391 1392 auto c = source.front; 1393 // assert(!source.empty); 1394 source.popFront(); 1395 1396 static if (useHex) 1397 { 1398 if (!firstPadFound && '0' <= c && c <= '9') 1399 { 1400 e = cast(ubyte)(c - '0'); 1401 } 1402 else if (!firstPadFound && 'A' <= c && c <= 'V') 1403 { 1404 e = cast(ubyte)(c - 'A' + 10); 1405 } 1406 else if (c == pad) 1407 { 1408 e = 0; 1409 1410 if (!firstPadFound) 1411 { 1412 firstPadFound = true; 1413 padLength = decSourceChunkLength - i; 1414 1415 if (!isValidPadLength(padLength)) 1416 { 1417 throw new Base32Exception("Invalid padding found"); 1418 } 1419 } 1420 } 1421 else 1422 { 1423 import std.conv : text; 1424 1425 throw new Base32Exception(text("Invalid character '", c, "' found")); 1426 } 1427 } 1428 else 1429 { 1430 if (!firstPadFound && 'A' <= c && c <= 'Z') 1431 { 1432 e = cast(ubyte)(c - 'A'); 1433 } 1434 else if (!firstPadFound && '2' <= c && c <= '7') 1435 { 1436 e = cast(ubyte)(c - '2' + 26); 1437 } 1438 else if (c == pad) 1439 { 1440 e = 0; 1441 1442 if (!firstPadFound) 1443 { 1444 firstPadFound = true; 1445 padLength = decSourceChunkLength - i; 1446 1447 if (!isValidPadLength(padLength)) 1448 { 1449 throw new Base32Exception("Invalid padding found"); 1450 } 1451 } 1452 } 1453 else 1454 { 1455 import std.conv : text; 1456 1457 throw new Base32Exception(text("Invalid character '", c, "' found")); 1458 } 1459 } 1460 } 1461 1462 immutable meaningfulLength = padToMeaningfulLength(padLength); 1463 processedLength += meaningfulLength; 1464 1465 range.put(cast(ubyte)(tmpBuffer[0] << 3 | tmpBuffer[1] >>> 2)); 1466 if (meaningfulLength == 1) 1467 { 1468 break; 1469 } 1470 1471 range.put(cast(ubyte)(tmpBuffer[1] << 6 | tmpBuffer[2] << 1 | tmpBuffer[3] >>> 4)); 1472 if (meaningfulLength == 2) 1473 { 1474 break; 1475 } 1476 1477 range.put(cast(ubyte)(tmpBuffer[3] << 4 | tmpBuffer[4] >>> 1)); 1478 if (meaningfulLength == 3) 1479 { 1480 break; 1481 } 1482 1483 range.put(cast(ubyte)(tmpBuffer[4] << 7 | tmpBuffer[5] << 2 | tmpBuffer[6] >>> 3)); 1484 if (meaningfulLength == 4) 1485 { 1486 break; 1487 } 1488 1489 range.put(cast(ubyte)(tmpBuffer[6] << 5 | tmpBuffer[7])); 1490 } 1491 1492 assert(source.empty); 1493 1494 // proxy out contract 1495 assert(processedLength == preciseDecodeLength(actualLen, padLength)); 1496 1497 return processedLength; 1498 } 1499 } 1500 1501 1502 /** 1503 * Exception thrown on errors in _base32 functions. 1504 */ 1505 class Base32Exception : Exception 1506 { 1507 @safe pure nothrow 1508 this(string s, string fn = __FILE__, size_t ln = __LINE__) 1509 { 1510 super(s, fn, ln); 1511 } 1512 } 1513 1514 1515 // Encoding 1516 unittest 1517 { 1518 alias Base32NoPad = Base32Impl!(UseHex.no, UsePad.no); 1519 alias Base32HexNoPad = Base32Impl!(UseHex.yes, UsePad.no); 1520 1521 pure auto toA(string s) 1522 { 1523 return cast(ubyte[])s; 1524 } 1525 1526 pure auto toR(string s) 1527 { 1528 struct Range 1529 { 1530 private ubyte[] source; 1531 private size_t index; 1532 1533 this(string s) 1534 { 1535 source = cast(ubyte[])s; 1536 } 1537 1538 bool empty() @property 1539 { 1540 return index == source.length; 1541 } 1542 1543 ubyte front() @property 1544 { 1545 return source[index]; 1546 } 1547 1548 void popFront() 1549 { 1550 index++; 1551 } 1552 1553 size_t length() @property 1554 { 1555 return source.length; 1556 } 1557 } 1558 1559 import std.range.primitives, std.traits; 1560 1561 static assert(isInputRange!Range && is(ElementType!Range : ubyte) 1562 && hasLength!Range); 1563 1564 return Range(s); 1565 } 1566 1567 1568 // Length 1569 { 1570 assert(Base32.encodeLength(toA("").length) == 0); 1571 assert(Base32.encodeLength(toA("f").length) == 8); 1572 assert(Base32.encodeLength(toA("fo").length) == 8); 1573 assert(Base32.encodeLength(toA("foo").length) == 8); 1574 assert(Base32.encodeLength(toA("foob").length) == 8); 1575 assert(Base32.encodeLength(toA("fooba").length) == 8); 1576 assert(Base32.encodeLength(toA("foobar").length) == 16); 1577 1578 assert(Base32Hex.encodeLength(toA("").length) == 0); 1579 assert(Base32Hex.encodeLength(toA("f").length) == 8); 1580 assert(Base32Hex.encodeLength(toA("fo").length) == 8); 1581 assert(Base32Hex.encodeLength(toA("foo").length) == 8); 1582 assert(Base32Hex.encodeLength(toA("foob").length) == 8); 1583 assert(Base32Hex.encodeLength(toA("fooba").length) == 8); 1584 assert(Base32Hex.encodeLength(toA("foobar").length) == 16); 1585 1586 assert(Base32NoPad.encodeLength(toA("").length) == 0); 1587 assert(Base32NoPad.encodeLength(toA("f").length) == 2); 1588 assert(Base32NoPad.encodeLength(toA("fo").length) == 4); 1589 assert(Base32NoPad.encodeLength(toA("foo").length) == 5); 1590 assert(Base32NoPad.encodeLength(toA("foob").length) == 7); 1591 assert(Base32NoPad.encodeLength(toA("fooba").length) == 8); 1592 assert(Base32NoPad.encodeLength(toA("foobar").length) == 10); 1593 1594 assert(Base32HexNoPad.encodeLength(toA("").length) == 0); 1595 assert(Base32HexNoPad.encodeLength(toA("f").length) == 2); 1596 assert(Base32HexNoPad.encodeLength(toA("fo").length) == 4); 1597 assert(Base32HexNoPad.encodeLength(toA("foo").length) == 5); 1598 assert(Base32HexNoPad.encodeLength(toA("foob").length) == 7); 1599 assert(Base32HexNoPad.encodeLength(toA("fooba").length) == 8); 1600 assert(Base32HexNoPad.encodeLength(toA("foobar").length) == 10); 1601 } 1602 1603 // Array to Array 1604 { 1605 assert(Base32.encode(toA("")) == ""); 1606 assert(Base32.encode(toA("f")) == "MY======"); 1607 assert(Base32.encode(toA("fo")) == "MZXQ===="); 1608 assert(Base32.encode(toA("foo")) == "MZXW6==="); 1609 assert(Base32.encode(toA("foob")) == "MZXW6YQ="); 1610 assert(Base32.encode(toA("fooba")) == "MZXW6YTB"); 1611 assert(Base32.encode(toA("foobar")) == "MZXW6YTBOI======"); 1612 1613 assert(Base32Hex.encode(toA("")) == ""); 1614 assert(Base32Hex.encode(toA("f")) == "CO======"); 1615 assert(Base32Hex.encode(toA("fo")) == "CPNG===="); 1616 assert(Base32Hex.encode(toA("foo")) == "CPNMU==="); 1617 assert(Base32Hex.encode(toA("foob")) == "CPNMUOG="); 1618 assert(Base32Hex.encode(toA("fooba")) == "CPNMUOJ1"); 1619 assert(Base32Hex.encode(toA("foobar")) == "CPNMUOJ1E8======"); 1620 1621 assert(Base32NoPad.encode(toA("")) == ""); 1622 assert(Base32NoPad.encode(toA("f")) == "MY"); 1623 assert(Base32NoPad.encode(toA("fo")) == "MZXQ"); 1624 assert(Base32NoPad.encode(toA("foo")) == "MZXW6"); 1625 assert(Base32NoPad.encode(toA("foob")) == "MZXW6YQ"); 1626 assert(Base32NoPad.encode(toA("fooba")) == "MZXW6YTB"); 1627 assert(Base32NoPad.encode(toA("foobar")) == "MZXW6YTBOI"); 1628 1629 assert(Base32HexNoPad.encode(toA("")) == ""); 1630 assert(Base32HexNoPad.encode(toA("f")) == "CO"); 1631 assert(Base32HexNoPad.encode(toA("fo")) == "CPNG"); 1632 assert(Base32HexNoPad.encode(toA("foo")) == "CPNMU"); 1633 assert(Base32HexNoPad.encode(toA("foob")) == "CPNMUOG"); 1634 assert(Base32HexNoPad.encode(toA("fooba")) == "CPNMUOJ1"); 1635 assert(Base32HexNoPad.encode(toA("foobar")) == "CPNMUOJ1E8"); 1636 } 1637 1638 // InputRange to Array 1639 { 1640 assert(Base32.encode(toR("")) == ""); 1641 assert(Base32.encode(toR("f")) == "MY======"); 1642 assert(Base32.encode(toR("fo")) == "MZXQ===="); 1643 assert(Base32.encode(toR("foo")) == "MZXW6==="); 1644 assert(Base32.encode(toR("foob")) == "MZXW6YQ="); 1645 assert(Base32.encode(toR("fooba")) == "MZXW6YTB"); 1646 assert(Base32.encode(toR("foobar")) == "MZXW6YTBOI======"); 1647 1648 assert(Base32Hex.encode(toR("")) == ""); 1649 assert(Base32Hex.encode(toR("f")) == "CO======"); 1650 assert(Base32Hex.encode(toR("fo")) == "CPNG===="); 1651 assert(Base32Hex.encode(toR("foo")) == "CPNMU==="); 1652 assert(Base32Hex.encode(toR("foob")) == "CPNMUOG="); 1653 assert(Base32Hex.encode(toR("fooba")) == "CPNMUOJ1"); 1654 assert(Base32Hex.encode(toR("foobar")) == "CPNMUOJ1E8======"); 1655 1656 assert(Base32NoPad.encode(toR("")) == ""); 1657 assert(Base32NoPad.encode(toR("f")) == "MY"); 1658 assert(Base32NoPad.encode(toR("fo")) == "MZXQ"); 1659 assert(Base32NoPad.encode(toR("foo")) == "MZXW6"); 1660 assert(Base32NoPad.encode(toR("foob")) == "MZXW6YQ"); 1661 assert(Base32NoPad.encode(toR("fooba")) == "MZXW6YTB"); 1662 assert(Base32NoPad.encode(toR("foobar")) == "MZXW6YTBOI"); 1663 1664 assert(Base32HexNoPad.encode(toR("")) == ""); 1665 assert(Base32HexNoPad.encode(toR("f")) == "CO"); 1666 assert(Base32HexNoPad.encode(toR("fo")) == "CPNG"); 1667 assert(Base32HexNoPad.encode(toR("foo")) == "CPNMU"); 1668 assert(Base32HexNoPad.encode(toR("foob")) == "CPNMUOG"); 1669 assert(Base32HexNoPad.encode(toR("fooba")) == "CPNMUOJ1"); 1670 assert(Base32HexNoPad.encode(toR("foobar")) == "CPNMUOJ1E8"); 1671 } 1672 1673 // Array to OutputRange 1674 { 1675 import std.array : appender; 1676 1677 auto app = appender!(char[])(); 1678 1679 assert(Base32.encode(toA(""), app) == 0); 1680 assert(app.data == ""); 1681 app.clear(); 1682 assert(Base32.encode(toA("f"), app) == 8); 1683 assert(app.data == "MY======"); 1684 app.clear(); 1685 assert(Base32.encode(toA("fo"), app) == 8); 1686 assert(app.data == "MZXQ===="); 1687 app.clear(); 1688 assert(Base32.encode(toA("foo"), app) == 8); 1689 assert(app.data == "MZXW6==="); 1690 app.clear(); 1691 assert(Base32.encode(toA("foob"), app) == 8); 1692 assert(app.data == "MZXW6YQ="); 1693 app.clear(); 1694 assert(Base32.encode(toA("fooba"), app) == 8); 1695 assert(app.data == "MZXW6YTB"); 1696 app.clear(); 1697 assert(Base32.encode(toA("foobar"), app) == 16); 1698 assert(app.data == "MZXW6YTBOI======"); 1699 app.clear(); 1700 1701 assert(Base32Hex.encode(toA(""), app) == 0); 1702 assert(app.data == ""); 1703 app.clear(); 1704 assert(Base32Hex.encode(toA("f"), app) == 8); 1705 assert(app.data == "CO======"); 1706 app.clear(); 1707 assert(Base32Hex.encode(toA("fo"), app) == 8); 1708 assert(app.data == "CPNG===="); 1709 app.clear(); 1710 assert(Base32Hex.encode(toA("foo"), app) == 8); 1711 assert(app.data == "CPNMU==="); 1712 app.clear(); 1713 assert(Base32Hex.encode(toA("foob"), app) == 8); 1714 assert(app.data == "CPNMUOG="); 1715 app.clear(); 1716 assert(Base32Hex.encode(toA("fooba"), app) == 8); 1717 assert(app.data == "CPNMUOJ1"); 1718 app.clear(); 1719 assert(Base32Hex.encode(toA("foobar"), app) == 16); 1720 assert(app.data == "CPNMUOJ1E8======"); 1721 app.clear(); 1722 1723 assert(Base32NoPad.encode(toA(""), app) == 0); 1724 assert(app.data == ""); 1725 app.clear(); 1726 assert(Base32NoPad.encode(toA("f"), app) == 2); 1727 assert(app.data == "MY"); 1728 app.clear(); 1729 assert(Base32NoPad.encode(toA("fo"), app) == 4); 1730 assert(app.data == "MZXQ"); 1731 app.clear(); 1732 assert(Base32NoPad.encode(toA("foo"), app) == 5); 1733 assert(app.data == "MZXW6"); 1734 app.clear(); 1735 assert(Base32NoPad.encode(toA("foob"), app) == 7); 1736 assert(app.data == "MZXW6YQ"); 1737 app.clear(); 1738 assert(Base32NoPad.encode(toA("fooba"), app) == 8); 1739 assert(app.data == "MZXW6YTB"); 1740 app.clear(); 1741 assert(Base32NoPad.encode(toA("foobar"), app) == 10); 1742 assert(app.data == "MZXW6YTBOI"); 1743 app.clear(); 1744 1745 assert(Base32HexNoPad.encode(toA(""), app) == 0); 1746 assert(app.data == ""); 1747 app.clear(); 1748 assert(Base32HexNoPad.encode(toA("f"), app) == 2); 1749 assert(app.data == "CO"); 1750 app.clear(); 1751 assert(Base32HexNoPad.encode(toA("fo"), app) == 4); 1752 assert(app.data == "CPNG"); 1753 app.clear(); 1754 assert(Base32HexNoPad.encode(toA("foo"), app) == 5); 1755 assert(app.data == "CPNMU"); 1756 app.clear(); 1757 assert(Base32HexNoPad.encode(toA("foob"), app) == 7); 1758 assert(app.data == "CPNMUOG"); 1759 app.clear(); 1760 assert(Base32HexNoPad.encode(toA("fooba"), app) == 8); 1761 assert(app.data == "CPNMUOJ1"); 1762 app.clear(); 1763 assert(Base32HexNoPad.encode(toA("foobar"), app) == 10); 1764 assert(app.data == "CPNMUOJ1E8"); 1765 app.clear(); 1766 } 1767 1768 // InputRange to OutputRange 1769 { 1770 import std.array : appender; 1771 1772 auto app = appender!(char[])(); 1773 1774 assert(Base32.encode(toR(""), app) == 0); 1775 assert(app.data == ""); 1776 app.clear(); 1777 assert(Base32.encode(toR("f"), app) == 8); 1778 assert(app.data == "MY======"); 1779 app.clear(); 1780 assert(Base32.encode(toR("fo"), app) == 8); 1781 assert(app.data == "MZXQ===="); 1782 app.clear(); 1783 assert(Base32.encode(toR("foo"), app) == 8); 1784 assert(app.data == "MZXW6==="); 1785 app.clear(); 1786 assert(Base32.encode(toR("foob"), app) == 8); 1787 assert(app.data == "MZXW6YQ="); 1788 app.clear(); 1789 assert(Base32.encode(toR("fooba"), app) == 8); 1790 assert(app.data == "MZXW6YTB"); 1791 app.clear(); 1792 assert(Base32.encode(toR("foobar"), app) == 16); 1793 assert(app.data == "MZXW6YTBOI======"); 1794 app.clear(); 1795 1796 assert(Base32Hex.encode(toR(""), app) == 0); 1797 assert(app.data == ""); 1798 app.clear(); 1799 assert(Base32Hex.encode(toR("f"), app) == 8); 1800 assert(app.data == "CO======"); 1801 app.clear(); 1802 assert(Base32Hex.encode(toR("fo"), app) == 8); 1803 assert(app.data == "CPNG===="); 1804 app.clear(); 1805 assert(Base32Hex.encode(toR("foo"), app) == 8); 1806 assert(app.data == "CPNMU==="); 1807 app.clear(); 1808 assert(Base32Hex.encode(toR("foob"), app) == 8); 1809 assert(app.data == "CPNMUOG="); 1810 app.clear(); 1811 assert(Base32Hex.encode(toR("fooba"), app) == 8); 1812 assert(app.data == "CPNMUOJ1"); 1813 app.clear(); 1814 assert(Base32Hex.encode(toR("foobar"), app) == 16); 1815 assert(app.data == "CPNMUOJ1E8======"); 1816 app.clear(); 1817 1818 assert(Base32NoPad.encode(toR(""), app) == 0); 1819 assert(app.data == ""); 1820 app.clear(); 1821 assert(Base32NoPad.encode(toR("f"), app) == 2); 1822 assert(app.data == "MY"); 1823 app.clear(); 1824 assert(Base32NoPad.encode(toR("fo"), app) == 4); 1825 assert(app.data == "MZXQ"); 1826 app.clear(); 1827 assert(Base32NoPad.encode(toR("foo"), app) == 5); 1828 assert(app.data == "MZXW6"); 1829 app.clear(); 1830 assert(Base32NoPad.encode(toR("foob"), app) == 7); 1831 assert(app.data == "MZXW6YQ"); 1832 app.clear(); 1833 assert(Base32NoPad.encode(toR("fooba"), app) == 8); 1834 assert(app.data == "MZXW6YTB"); 1835 app.clear(); 1836 assert(Base32NoPad.encode(toR("foobar"), app) == 10); 1837 assert(app.data == "MZXW6YTBOI"); 1838 app.clear(); 1839 1840 assert(Base32HexNoPad.encode(toR(""), app) == 0); 1841 assert(app.data == ""); 1842 app.clear(); 1843 assert(Base32HexNoPad.encode(toR("f"), app) == 2); 1844 assert(app.data == "CO"); 1845 app.clear(); 1846 assert(Base32HexNoPad.encode(toR("fo"), app) == 4); 1847 assert(app.data == "CPNG"); 1848 app.clear(); 1849 assert(Base32HexNoPad.encode(toR("foo"), app) == 5); 1850 assert(app.data == "CPNMU"); 1851 app.clear(); 1852 assert(Base32HexNoPad.encode(toR("foob"), app) == 7); 1853 assert(app.data == "CPNMUOG"); 1854 app.clear(); 1855 assert(Base32HexNoPad.encode(toR("fooba"), app) == 8); 1856 assert(app.data == "CPNMUOJ1"); 1857 app.clear(); 1858 assert(Base32HexNoPad.encode(toR("foobar"), app) == 10); 1859 assert(app.data == "CPNMUOJ1E8"); 1860 app.clear(); 1861 } 1862 } 1863 1864 // Decoding 1865 unittest 1866 { 1867 alias Base32NoPad = Base32Impl!(UseHex.no, UsePad.no); 1868 alias Base32HexNoPad = Base32Impl!(UseHex.yes, UsePad.no); 1869 1870 pure auto toA(in string s) 1871 { 1872 return cast(ubyte[])s; 1873 } 1874 1875 pure auto toR(in string s) 1876 { 1877 struct Range 1878 { 1879 private char[] source; 1880 private size_t index; 1881 1882 this(string s) 1883 { 1884 source = cast(char[])s; 1885 } 1886 1887 bool empty() @property 1888 { 1889 return index == source.length; 1890 } 1891 1892 ubyte front() @property 1893 { 1894 return source[index]; 1895 } 1896 1897 void popFront() 1898 { 1899 index++; 1900 } 1901 1902 size_t length() @property 1903 { 1904 return source.length; 1905 } 1906 } 1907 1908 import std.range.primitives, std.traits; 1909 1910 static assert(isInputRange!Range && is(ElementType!Range : dchar) 1911 && hasLength!Range); 1912 1913 return Range(s); 1914 } 1915 1916 1917 import std.exception : assertThrown; 1918 1919 // Length 1920 { 1921 assert(Base32.decodeLength(0) == 0); 1922 assert(Base32.decodeLength(8) == 5); 1923 assert(Base32.decodeLength(16) == 10); 1924 1925 assert(Base32.preciseDecodeLength("") == 0); 1926 assert(Base32.preciseDecodeLength("MY======") == 1); 1927 assert(Base32.preciseDecodeLength("MZXQ====") == 2); 1928 assert(Base32.preciseDecodeLength("MZXW6===") == 3); 1929 assert(Base32.preciseDecodeLength("MZXW6YQ=") == 4); 1930 assert(Base32.preciseDecodeLength("MZXW6YTB") == 5); 1931 assert(Base32.preciseDecodeLength("MZXW6YTBOI======") == 6); 1932 1933 assert(Base32Hex.decodeLength(0) == 0); 1934 assert(Base32Hex.decodeLength(8) == 5); 1935 assert(Base32Hex.decodeLength(16) == 10); 1936 1937 assert(Base32Hex.preciseDecodeLength("") == 0); 1938 assert(Base32Hex.preciseDecodeLength("CO======") == 1); 1939 assert(Base32Hex.preciseDecodeLength("CPNG====") == 2); 1940 assert(Base32Hex.preciseDecodeLength("CPNMU===") == 3); 1941 assert(Base32Hex.preciseDecodeLength("CPNMUOG=") == 4); 1942 assert(Base32Hex.preciseDecodeLength("CPNMUOJ1") == 5); 1943 assert(Base32Hex.preciseDecodeLength("CPNMUOJ1E8======") == 6); 1944 1945 assert(Base32NoPad.decodeLength(0) == 0); 1946 assert(Base32NoPad.decodeLength(2) == 5); 1947 assert(Base32NoPad.decodeLength(4) == 5); 1948 assert(Base32NoPad.decodeLength(5) == 5); 1949 assert(Base32NoPad.decodeLength(7) == 5); 1950 assert(Base32NoPad.decodeLength(8) == 5); 1951 assert(Base32NoPad.decodeLength(10) == 10); 1952 1953 assert(Base32NoPad.preciseDecodeLength("") == 0); 1954 assert(Base32NoPad.preciseDecodeLength("MY") == 1); 1955 assert(Base32NoPad.preciseDecodeLength("MZXQ") == 2); 1956 assert(Base32NoPad.preciseDecodeLength("MZXW6") == 3); 1957 assert(Base32NoPad.preciseDecodeLength("MZXW6YQ") == 4); 1958 assert(Base32NoPad.preciseDecodeLength("MZXW6YTB") == 5); 1959 assert(Base32NoPad.preciseDecodeLength("MZXW6YTBOI") == 6); 1960 1961 assert(Base32HexNoPad.decodeLength(0) == 0); 1962 assert(Base32HexNoPad.decodeLength(2) == 5); 1963 assert(Base32HexNoPad.decodeLength(4) == 5); 1964 assert(Base32HexNoPad.decodeLength(5) == 5); 1965 assert(Base32HexNoPad.decodeLength(7) == 5); 1966 assert(Base32HexNoPad.decodeLength(8) == 5); 1967 assert(Base32HexNoPad.decodeLength(10) == 10); 1968 1969 assert(Base32HexNoPad.preciseDecodeLength("") == 0); 1970 assert(Base32HexNoPad.preciseDecodeLength("CO") == 1); 1971 assert(Base32HexNoPad.preciseDecodeLength("CPNG") == 2); 1972 assert(Base32HexNoPad.preciseDecodeLength("CPNMU") == 3); 1973 assert(Base32HexNoPad.preciseDecodeLength("CPNMUOG") == 4); 1974 assert(Base32HexNoPad.preciseDecodeLength("CPNMUOJ1") == 5); 1975 assert(Base32HexNoPad.preciseDecodeLength("CPNMUOJ1E8") == 6); 1976 } 1977 1978 // Array to Array 1979 { 1980 assert(Base32.decode("") == toA("")); 1981 assert(Base32.decode("MY======") == toA("f")); 1982 assert(Base32.decode("MZXQ====") == toA("fo")); 1983 assert(Base32.decode("MZXW6===") == toA("foo")); 1984 assert(Base32.decode("MZXW6YQ=") == toA("foob")); 1985 assert(Base32.decode("MZXW6YTB") == toA("fooba")); 1986 assert(Base32.decode("MZXW6YTBOI======") == toA("foobar")); 1987 1988 assert(Base32Hex.decode("") == toA("")); 1989 assert(Base32Hex.decode("CO======") == toA("f")); 1990 assert(Base32Hex.decode("CPNG====") == toA("fo")); 1991 assert(Base32Hex.decode("CPNMU===") == toA("foo")); 1992 assert(Base32Hex.decode("CPNMUOG=") == toA("foob")); 1993 assert(Base32Hex.decode("CPNMUOJ1") == toA("fooba")); 1994 assert(Base32Hex.decode("CPNMUOJ1E8======") == toA("foobar")); 1995 1996 assert(Base32NoPad.decode("") == toA("")); 1997 assert(Base32NoPad.decode("MY") == toA("f")); 1998 assert(Base32NoPad.decode("MZXQ") == toA("fo")); 1999 assert(Base32NoPad.decode("MZXW6") == toA("foo")); 2000 assert(Base32NoPad.decode("MZXW6YQ") == toA("foob")); 2001 assert(Base32NoPad.decode("MZXW6YTB") == toA("fooba")); 2002 assert(Base32NoPad.decode("MZXW6YTBOI") == toA("foobar")); 2003 2004 assert(Base32HexNoPad.decode("") == toA("")); 2005 assert(Base32HexNoPad.decode("CO") == toA("f")); 2006 assert(Base32HexNoPad.decode("CPNG") == toA("fo")); 2007 assert(Base32HexNoPad.decode("CPNMU") == toA("foo")); 2008 assert(Base32HexNoPad.decode("CPNMUOG") == toA("foob")); 2009 assert(Base32HexNoPad.decode("CPNMUOJ1") == toA("fooba")); 2010 assert(Base32HexNoPad.decode("CPNMUOJ1E8") == toA("foobar")); 2011 2012 // Invalid source length 2013 assertThrown!Base32Exception(Base32.decode("A")); 2014 assertThrown!Base32Exception(Base32.decode("AA")); 2015 assertThrown!Base32Exception(Base32.decode("AAA")); 2016 assertThrown!Base32Exception(Base32.decode("AAAA")); 2017 assertThrown!Base32Exception(Base32.decode("AAAAA")); 2018 assertThrown!Base32Exception(Base32.decode("AAAAAA")); 2019 assertThrown!Base32Exception(Base32.decode("AAAAAAA")); 2020 2021 assertThrown!Base32Exception(Base32Hex.decode("A")); 2022 assertThrown!Base32Exception(Base32Hex.decode("AA")); 2023 assertThrown!Base32Exception(Base32Hex.decode("AAA")); 2024 assertThrown!Base32Exception(Base32Hex.decode("AAAA")); 2025 assertThrown!Base32Exception(Base32Hex.decode("AAAAA")); 2026 assertThrown!Base32Exception(Base32Hex.decode("AAAAAA")); 2027 assertThrown!Base32Exception(Base32Hex.decode("AAAAAAA")); 2028 2029 assertThrown!Base32Exception(Base32NoPad.decode("A")); 2030 assertThrown!Base32Exception(Base32NoPad.decode("AAA")); 2031 assertThrown!Base32Exception(Base32NoPad.decode("AAAAAA")); 2032 assertThrown!Base32Exception(Base32NoPad.decode("AAAAAAAAA")); 2033 2034 assertThrown!Base32Exception(Base32HexNoPad.decode("A")); 2035 assertThrown!Base32Exception(Base32HexNoPad.decode("AAA")); 2036 assertThrown!Base32Exception(Base32HexNoPad.decode("AAAAAA")); 2037 assertThrown!Base32Exception(Base32HexNoPad.decode("AAAAAAAAA")); 2038 2039 // Invalid character 2040 assertThrown!Base32Exception(Base32.decode("AA?AA?AA")); 2041 assertThrown!Base32Exception(Base32Hex.decode("AA?AA?AA")); 2042 assertThrown!Base32Exception(Base32NoPad.decode("AA?AA?AA")); 2043 assertThrown!Base32Exception(Base32HexNoPad.decode("AA?AA?AA")); 2044 2045 // Invalid padding 2046 assertThrown!Base32Exception(Base32.decode("=AAAAAAA")); 2047 assertThrown!Base32Exception(Base32Hex.decode("=AAAAAAA")); 2048 assertThrown!Base32Exception(Base32NoPad.decode("=AAAAAAA")); 2049 assertThrown!Base32Exception(Base32HexNoPad.decode("=AAAAAAA")); 2050 } 2051 2052 // InputRange to Array 2053 { 2054 assert(Base32.decode(toR("")) == toA("")); 2055 assert(Base32.decode(toR("MY======")) == toA("f")); 2056 assert(Base32.decode(toR("MZXQ====")) == toA("fo")); 2057 assert(Base32.decode(toR("MZXW6===")) == toA("foo")); 2058 assert(Base32.decode(toR("MZXW6YQ=")) == toA("foob")); 2059 assert(Base32.decode(toR("MZXW6YTB")) == toA("fooba")); 2060 assert(Base32.decode(toR("MZXW6YTBOI======")) == toA("foobar")); 2061 2062 assert(Base32Hex.decode(toR("")) == toA("")); 2063 assert(Base32Hex.decode(toR("CO======")) == toA("f")); 2064 assert(Base32Hex.decode(toR("CPNG====")) == toA("fo")); 2065 assert(Base32Hex.decode(toR("CPNMU===")) == toA("foo")); 2066 assert(Base32Hex.decode(toR("CPNMUOG=")) == toA("foob")); 2067 assert(Base32Hex.decode(toR("CPNMUOJ1")) == toA("fooba")); 2068 assert(Base32Hex.decode(toR("CPNMUOJ1E8======")) == toA("foobar")); 2069 2070 assert(Base32NoPad.decode(toR("")) == toA("")); 2071 assert(Base32NoPad.decode(toR("MY")) == toA("f")); 2072 assert(Base32NoPad.decode(toR("MZXQ")) == toA("fo")); 2073 assert(Base32NoPad.decode(toR("MZXW6")) == toA("foo")); 2074 assert(Base32NoPad.decode(toR("MZXW6YQ")) == toA("foob")); 2075 assert(Base32NoPad.decode(toR("MZXW6YTB")) == toA("fooba")); 2076 assert(Base32NoPad.decode(toR("MZXW6YTBOI")) == toA("foobar")); 2077 2078 assert(Base32HexNoPad.decode(toR("")) == toA("")); 2079 assert(Base32HexNoPad.decode(toR("CO")) == toA("f")); 2080 assert(Base32HexNoPad.decode(toR("CPNG")) == toA("fo")); 2081 assert(Base32HexNoPad.decode(toR("CPNMU")) == toA("foo")); 2082 assert(Base32HexNoPad.decode(toR("CPNMUOG")) == toA("foob")); 2083 assert(Base32HexNoPad.decode(toR("CPNMUOJ1")) == toA("fooba")); 2084 assert(Base32HexNoPad.decode(toR("CPNMUOJ1E8")) == toA("foobar")); 2085 2086 // Invalid source length 2087 assertThrown!Base32Exception(Base32.decode(toR("A"))); 2088 assertThrown!Base32Exception(Base32.decode(toR("AA"))); 2089 assertThrown!Base32Exception(Base32.decode(toR("AAA"))); 2090 assertThrown!Base32Exception(Base32.decode(toR("AAAA"))); 2091 assertThrown!Base32Exception(Base32.decode(toR("AAAAA"))); 2092 assertThrown!Base32Exception(Base32.decode(toR("AAAAAA"))); 2093 assertThrown!Base32Exception(Base32.decode(toR("AAAAAAA"))); 2094 2095 assertThrown!Base32Exception(Base32Hex.decode(toR("A"))); 2096 assertThrown!Base32Exception(Base32Hex.decode(toR("AA"))); 2097 assertThrown!Base32Exception(Base32Hex.decode(toR("AAA"))); 2098 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAA"))); 2099 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAAA"))); 2100 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAAAA"))); 2101 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAAAAA"))); 2102 2103 assertThrown!Base32Exception(Base32NoPad.decode(toR("A"))); 2104 assertThrown!Base32Exception(Base32NoPad.decode(toR("AAA"))); 2105 assertThrown!Base32Exception(Base32NoPad.decode(toR("AAAAAA"))); 2106 assertThrown!Base32Exception(Base32NoPad.decode(toR("AAAAAAAAA"))); 2107 2108 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("A"))); 2109 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AAA"))); 2110 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AAAAAA"))); 2111 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AAAAAAAAA"))); 2112 2113 // Invalid character 2114 assertThrown!Base32Exception(Base32.decode(toR("AA?AA?AA"))); 2115 assertThrown!Base32Exception(Base32Hex.decode(toR("AA?AA?AA"))); 2116 assertThrown!Base32Exception(Base32NoPad.decode(toR("AA?AA?AA"))); 2117 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AA?AA?AA"))); 2118 2119 // Invalid padding 2120 assertThrown!Base32Exception(Base32.decode(toR("=AAAAAAA"))); 2121 assertThrown!Base32Exception(Base32Hex.decode(toR("=AAAAAAA"))); 2122 assertThrown!Base32Exception(Base32NoPad.decode(toR("=AAAAAAA"))); 2123 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("=AAAAAAA"))); 2124 } 2125 2126 // Array to OutputRange 2127 { 2128 import std.array : appender; 2129 2130 auto app = appender!(ubyte[])(); 2131 2132 assert(Base32.decode("", app) == 0); 2133 assert(app.data == toA("")); 2134 app.clear(); 2135 assert(Base32.decode("MY======", app) == 1); 2136 assert(app.data == toA("f")); 2137 app.clear(); 2138 assert(Base32.decode("MZXQ====", app) == 2); 2139 assert(app.data == toA("fo")); 2140 app.clear(); 2141 assert(Base32.decode("MZXW6===", app) == 3); 2142 assert(app.data == toA("foo")); 2143 app.clear(); 2144 assert(Base32.decode("MZXW6YQ=", app) == 4); 2145 assert(app.data == toA("foob")); 2146 app.clear(); 2147 assert(Base32.decode("MZXW6YTB", app) == 5); 2148 assert(app.data == toA("fooba")); 2149 app.clear(); 2150 assert(Base32.decode("MZXW6YTBOI======", app) == 6); 2151 assert(app.data == toA("foobar")); 2152 app.clear(); 2153 2154 assert(Base32Hex.decode("", app) == 0); 2155 assert(app.data == toA("")); 2156 app.clear(); 2157 assert(Base32Hex.decode("CO======", app) == 1); 2158 assert(app.data == toA("f")); 2159 app.clear(); 2160 assert(Base32Hex.decode("CPNG====", app) == 2); 2161 assert(app.data == toA("fo")); 2162 app.clear(); 2163 assert(Base32Hex.decode("CPNMU===", app) == 3); 2164 assert(app.data == toA("foo")); 2165 app.clear(); 2166 assert(Base32Hex.decode("CPNMUOG=", app) == 4); 2167 assert(app.data == toA("foob")); 2168 app.clear(); 2169 assert(Base32Hex.decode("CPNMUOJ1", app) == 5); 2170 assert(app.data == toA("fooba")); 2171 app.clear(); 2172 assert(Base32Hex.decode("CPNMUOJ1E8======", app) == 6); 2173 assert(app.data == toA("foobar")); 2174 app.clear(); 2175 2176 assert(Base32NoPad.decode("", app) == 0); 2177 assert(app.data == toA("")); 2178 app.clear(); 2179 assert(Base32NoPad.decode("MY", app) == 1); 2180 assert(app.data == toA("f")); 2181 app.clear(); 2182 assert(Base32NoPad.decode("MZXQ", app) == 2); 2183 assert(app.data == toA("fo")); 2184 app.clear(); 2185 assert(Base32NoPad.decode("MZXW6", app) == 3); 2186 assert(app.data == toA("foo")); 2187 app.clear(); 2188 assert(Base32NoPad.decode("MZXW6YQ", app) == 4); 2189 assert(app.data == toA("foob")); 2190 app.clear(); 2191 assert(Base32NoPad.decode("MZXW6YTB", app) == 5); 2192 assert(app.data == toA("fooba")); 2193 app.clear(); 2194 assert(Base32NoPad.decode("MZXW6YTBOI", app) == 6); 2195 assert(app.data == toA("foobar")); 2196 app.clear(); 2197 2198 assert(Base32HexNoPad.decode("", app) == 0); 2199 assert(app.data == toA("")); 2200 app.clear(); 2201 assert(Base32HexNoPad.decode("CO", app) == 1); 2202 assert(app.data == toA("f")); 2203 app.clear(); 2204 assert(Base32HexNoPad.decode("CPNG", app) == 2); 2205 assert(app.data == toA("fo")); 2206 app.clear(); 2207 assert(Base32HexNoPad.decode("CPNMU", app) == 3); 2208 assert(app.data == toA("foo")); 2209 app.clear(); 2210 assert(Base32HexNoPad.decode("CPNMUOG", app) == 4); 2211 assert(app.data == toA("foob")); 2212 app.clear(); 2213 assert(Base32HexNoPad.decode("CPNMUOJ1", app) == 5); 2214 assert(app.data == toA("fooba")); 2215 app.clear(); 2216 assert(Base32HexNoPad.decode("CPNMUOJ1E8", app) == 6); 2217 assert(app.data == toA("foobar")); 2218 app.clear(); 2219 2220 // Invalid source length 2221 assertThrown!Base32Exception(Base32.decode("A", app)); 2222 app.clear(); 2223 assertThrown!Base32Exception(Base32.decode("AA", app)); 2224 app.clear(); 2225 assertThrown!Base32Exception(Base32.decode("AAA", app)); 2226 app.clear(); 2227 assertThrown!Base32Exception(Base32.decode("AAAA", app)); 2228 app.clear(); 2229 assertThrown!Base32Exception(Base32.decode("AAAAA", app)); 2230 app.clear(); 2231 assertThrown!Base32Exception(Base32.decode("AAAAAA", app)); 2232 app.clear(); 2233 assertThrown!Base32Exception(Base32.decode("AAAAAAA", app)); 2234 app.clear(); 2235 2236 assertThrown!Base32Exception(Base32Hex.decode("A", app)); 2237 app.clear(); 2238 assertThrown!Base32Exception(Base32Hex.decode("AA", app)); 2239 app.clear(); 2240 assertThrown!Base32Exception(Base32Hex.decode("AAA", app)); 2241 app.clear(); 2242 assertThrown!Base32Exception(Base32Hex.decode("AAAA", app)); 2243 app.clear(); 2244 assertThrown!Base32Exception(Base32Hex.decode("AAAAA", app)); 2245 app.clear(); 2246 assertThrown!Base32Exception(Base32Hex.decode("AAAAAA", app)); 2247 app.clear(); 2248 assertThrown!Base32Exception(Base32Hex.decode("AAAAAAA", app)); 2249 app.clear(); 2250 2251 assertThrown!Base32Exception(Base32NoPad.decode("A", app)); 2252 app.clear(); 2253 assertThrown!Base32Exception(Base32NoPad.decode("AAA", app)); 2254 app.clear(); 2255 assertThrown!Base32Exception(Base32NoPad.decode("AAAAAA", app)); 2256 app.clear(); 2257 assertThrown!Base32Exception(Base32NoPad.decode("AAAAAAAAA", app)); 2258 app.clear(); 2259 2260 assertThrown!Base32Exception(Base32HexNoPad.decode("A", app)); 2261 app.clear(); 2262 assertThrown!Base32Exception(Base32HexNoPad.decode("AAA", app)); 2263 app.clear(); 2264 assertThrown!Base32Exception(Base32HexNoPad.decode("AAAAAA", app)); 2265 app.clear(); 2266 assertThrown!Base32Exception(Base32HexNoPad.decode("AAAAAAAAA", app)); 2267 app.clear(); 2268 2269 // Invalid character 2270 assertThrown!Base32Exception(Base32.decode("AA?AA?AA", app)); 2271 app.clear(); 2272 assertThrown!Base32Exception(Base32Hex.decode("AA?AA?AA", app)); 2273 app.clear(); 2274 assertThrown!Base32Exception(Base32NoPad.decode("AA?AA?AA", app)); 2275 app.clear(); 2276 assertThrown!Base32Exception(Base32HexNoPad.decode("AA?AA?AA", app)); 2277 app.clear(); 2278 2279 // Invalid padding 2280 assertThrown!Base32Exception(Base32.decode("=AAAAAAA", app)); 2281 app.clear(); 2282 assertThrown!Base32Exception(Base32Hex.decode("=AAAAAAA", app)); 2283 app.clear(); 2284 assertThrown!Base32Exception(Base32NoPad.decode("=AAAAAAA", app)); 2285 app.clear(); 2286 assertThrown!Base32Exception(Base32HexNoPad.decode("=AAAAAAA", app)); 2287 app.clear(); 2288 } 2289 2290 // InputRange to OutputRange 2291 { 2292 import std.array : appender; 2293 2294 auto app = appender!(ubyte[])(); 2295 2296 assert(Base32.decode(toR(""), app) == 0); 2297 assert(app.data == toA("")); 2298 app.clear(); 2299 assert(Base32.decode(toR("MY======"), app) == 1); 2300 assert(app.data == toA("f")); 2301 app.clear(); 2302 assert(Base32.decode(toR("MZXQ===="), app) == 2); 2303 assert(app.data == toA("fo")); 2304 app.clear(); 2305 assert(Base32.decode(toR("MZXW6==="), app) == 3); 2306 assert(app.data == toA("foo")); 2307 app.clear(); 2308 assert(Base32.decode(toR("MZXW6YQ="), app) == 4); 2309 assert(app.data == toA("foob")); 2310 app.clear(); 2311 assert(Base32.decode(toR("MZXW6YTB"), app) == 5); 2312 assert(app.data == toA("fooba")); 2313 app.clear(); 2314 assert(Base32.decode(toR("MZXW6YTBOI======"), app) == 6); 2315 assert(app.data == toA("foobar")); 2316 app.clear(); 2317 2318 assert(Base32Hex.decode(toR(""), app) == 0); 2319 assert(app.data == toA("")); 2320 app.clear(); 2321 assert(Base32Hex.decode(toR("CO======"), app) == 1); 2322 assert(app.data == toA("f")); 2323 app.clear(); 2324 assert(Base32Hex.decode(toR("CPNG===="), app) == 2); 2325 assert(app.data == toA("fo")); 2326 app.clear(); 2327 assert(Base32Hex.decode(toR("CPNMU==="), app) == 3); 2328 assert(app.data == toA("foo")); 2329 app.clear(); 2330 assert(Base32Hex.decode(toR("CPNMUOG="), app) == 4); 2331 assert(app.data == toA("foob")); 2332 app.clear(); 2333 assert(Base32Hex.decode(toR("CPNMUOJ1"), app) == 5); 2334 assert(app.data == toA("fooba")); 2335 app.clear(); 2336 assert(Base32Hex.decode(toR("CPNMUOJ1E8======"), app) == 6); 2337 assert(app.data == toA("foobar")); 2338 app.clear(); 2339 2340 assert(Base32NoPad.decode(toR(""), app) == 0); 2341 assert(app.data == toA("")); 2342 app.clear(); 2343 assert(Base32NoPad.decode(toR("MY"), app) == 1); 2344 assert(app.data == toA("f")); 2345 app.clear(); 2346 assert(Base32NoPad.decode(toR("MZXQ"), app) == 2); 2347 assert(app.data == toA("fo")); 2348 app.clear(); 2349 assert(Base32NoPad.decode(toR("MZXW6"), app) == 3); 2350 assert(app.data == toA("foo")); 2351 app.clear(); 2352 assert(Base32NoPad.decode(toR("MZXW6YQ"), app) == 4); 2353 assert(app.data == toA("foob")); 2354 app.clear(); 2355 assert(Base32NoPad.decode(toR("MZXW6YTB"), app) == 5); 2356 assert(app.data == toA("fooba")); 2357 app.clear(); 2358 assert(Base32NoPad.decode(toR("MZXW6YTBOI"), app) == 6); 2359 assert(app.data == toA("foobar")); 2360 app.clear(); 2361 2362 assert(Base32HexNoPad.decode(toR(""), app) == 0); 2363 assert(app.data == toA("")); 2364 app.clear(); 2365 assert(Base32HexNoPad.decode(toR("CO"), app) == 1); 2366 assert(app.data == toA("f")); 2367 app.clear(); 2368 assert(Base32HexNoPad.decode(toR("CPNG"), app) == 2); 2369 assert(app.data == toA("fo")); 2370 app.clear(); 2371 assert(Base32HexNoPad.decode(toR("CPNMU"), app) == 3); 2372 assert(app.data == toA("foo")); 2373 app.clear(); 2374 assert(Base32HexNoPad.decode(toR("CPNMUOG"), app) == 4); 2375 assert(app.data == toA("foob")); 2376 app.clear(); 2377 assert(Base32HexNoPad.decode(toR("CPNMUOJ1"), app) == 5); 2378 assert(app.data == toA("fooba")); 2379 app.clear(); 2380 assert(Base32HexNoPad.decode(toR("CPNMUOJ1E8"), app) == 6); 2381 assert(app.data == toA("foobar")); 2382 app.clear(); 2383 2384 // Invalid source length 2385 assertThrown!Base32Exception(Base32.decode(toR("A"), app)); 2386 app.clear(); 2387 assertThrown!Base32Exception(Base32.decode(toR("AA"), app)); 2388 app.clear(); 2389 assertThrown!Base32Exception(Base32.decode(toR("AAA"), app)); 2390 app.clear(); 2391 assertThrown!Base32Exception(Base32.decode(toR("AAAA"), app)); 2392 app.clear(); 2393 assertThrown!Base32Exception(Base32.decode(toR("AAAAA"), app)); 2394 app.clear(); 2395 assertThrown!Base32Exception(Base32.decode(toR("AAAAAA"), app)); 2396 app.clear(); 2397 assertThrown!Base32Exception(Base32.decode(toR("AAAAAAA"), app)); 2398 app.clear(); 2399 2400 assertThrown!Base32Exception(Base32Hex.decode(toR("A"), app)); 2401 app.clear(); 2402 assertThrown!Base32Exception(Base32Hex.decode(toR("AA"), app)); 2403 app.clear(); 2404 assertThrown!Base32Exception(Base32Hex.decode(toR("AAA"), app)); 2405 app.clear(); 2406 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAA"), app)); 2407 app.clear(); 2408 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAAA"), app)); 2409 app.clear(); 2410 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAAAA"), app)); 2411 app.clear(); 2412 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAAAAA"), app)); 2413 app.clear(); 2414 2415 assertThrown!Base32Exception(Base32NoPad.decode(toR("A"), app)); 2416 app.clear(); 2417 assertThrown!Base32Exception(Base32NoPad.decode(toR("AAA"), app)); 2418 app.clear(); 2419 assertThrown!Base32Exception(Base32NoPad.decode(toR("AAAAAA"), app)); 2420 app.clear(); 2421 assertThrown!Base32Exception(Base32NoPad.decode(toR("AAAAAAAAA"), app)); 2422 app.clear(); 2423 2424 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("A"), app)); 2425 app.clear(); 2426 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AAA"), app)); 2427 app.clear(); 2428 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AAAAAA"), app)); 2429 app.clear(); 2430 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AAAAAAAAA"), app)); 2431 app.clear(); 2432 2433 // Invalid character 2434 assertThrown!Base32Exception(Base32.decode(toR("AA?AA?AA"), app)); 2435 app.clear(); 2436 assertThrown!Base32Exception(Base32Hex.decode(toR("AA?AA?AA"), app)); 2437 app.clear(); 2438 assertThrown!Base32Exception(Base32NoPad.decode(toR("AA?AA?AA"), app)); 2439 app.clear(); 2440 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AA?AA?AA"), app)); 2441 app.clear(); 2442 2443 // Invalid padding 2444 assertThrown!Base32Exception(Base32.decode(toR("=AAAAAAA"), app)); 2445 app.clear(); 2446 assertThrown!Base32Exception(Base32Hex.decode(toR("=AAAAAAA"), app)); 2447 app.clear(); 2448 assertThrown!Base32Exception(Base32NoPad.decode(toR("=AAAAAAA"), app)); 2449 app.clear(); 2450 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("=AAAAAAA"), app)); 2451 app.clear(); 2452 } 2453 }