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 do 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 do 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 do 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 do 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 do 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 do 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 /** 613 * Creates an InputRange which lazily encodes $(D_PARAM source). 614 * 615 * Params: 616 * source = An InputRange to _encode. 617 * 618 * Returns: 619 * An InputRange which iterates over $(D_PARAM source) and lazily encodes it. 620 * If $(D_PARAM source) is a ForwardRange, the returned range will be the same. 621 */ 622 template encoder(Range) if (isInputRange!Range && is(ElementType!Range : ubyte)) 623 { 624 // 625 auto encoder(Range source) 626 { 627 return Encoder(source); 628 } 629 630 631 struct Encoder 632 { 633 private 634 { 635 Range source; 636 bool _empty; 637 char _front; 638 639 static if (hasLength!Range) 640 { 641 size_t _length; 642 } 643 644 size_t resultChunkIdx = 0; 645 size_t resultChunkLen = encResultChunkLength; 646 ubyte srcBuf0, srcBuf1; 647 } 648 649 650 this(Range source) 651 { 652 _empty = source.empty; 653 654 if (!_empty) 655 { 656 static if (isForwardRange!Range) 657 { 658 this.source = source.save; 659 } 660 else 661 { 662 this.source = source; 663 } 664 665 static if (hasLength!Range) 666 { 667 _length = encodeLength(this.source.length); 668 } 669 670 _front = makeFront(); 671 } 672 } 673 674 static if (isInfinite!Range) 675 { 676 enum empty = false; 677 } 678 else 679 { 680 @property @safe @nogc nothrow 681 bool empty() const 682 { 683 static if (usePad) 684 { 685 return _empty && resultChunkIdx == 0; 686 } 687 else 688 { 689 return _empty; 690 } 691 } 692 } 693 694 @property @safe 695 char front() const 696 { 697 if (empty) 698 { 699 throw new Base32Exception("Cannot call front on Encoder with no data remaining"); 700 } 701 702 return _front; 703 } 704 705 void popFront() 706 { 707 if (empty) 708 { 709 throw new Base32Exception("Cannot call popFront on Encoder with no data remaining"); 710 } 711 712 ++resultChunkIdx %= encResultChunkLength; 713 714 if (!_empty) 715 { 716 _empty = resultChunkLen == resultChunkIdx; 717 } 718 719 static if (hasLength!Range) 720 { 721 if (_length) 722 { 723 _length--; 724 } 725 } 726 727 _front = makeFront(); 728 } 729 730 static if (isForwardRange!Range) 731 { 732 @property 733 typeof(this) save() 734 { 735 auto self = this; 736 self.source = self.source.save; 737 return self; 738 } 739 } 740 741 static if (hasLength!Range) 742 { 743 @property @safe @nogc nothrow 744 size_t length() const 745 { 746 return _length; 747 } 748 } 749 750 private 751 char makeFront() 752 { 753 ubyte result; 754 755 final switch (resultChunkIdx) 756 { 757 case 0: 758 if (source.empty) 759 { 760 srcBuf0 = 0; 761 resultChunkLen = 0; 762 } 763 else 764 { 765 srcBuf0 = source.front; 766 source.popFront(); 767 result = srcBuf0 >>> 3; 768 } 769 770 break; 771 case 1: 772 if (source.empty) 773 { 774 srcBuf1 = 0; 775 resultChunkLen = 2; 776 } 777 else 778 { 779 srcBuf1 = source.front; 780 source.popFront(); 781 } 782 783 result = (srcBuf0 << 2 | srcBuf1 >>> 6) & 0x1f; 784 break; 785 case 2: 786 result = srcBuf1 >>> 1 & 0x1f; 787 break; 788 case 3: 789 if (source.empty) 790 { 791 srcBuf0 = 0; 792 resultChunkLen = 4; 793 } 794 else 795 { 796 srcBuf0 = source.front; 797 source.popFront(); 798 } 799 800 result = (srcBuf1 << 4 | srcBuf0 >>> 4) & 0x1f; 801 break; 802 case 4: 803 if (source.empty) 804 { 805 srcBuf1 = 0; 806 resultChunkLen = 5; 807 } 808 else 809 { 810 srcBuf1 = source.front; 811 source.popFront(); 812 } 813 814 result = (srcBuf0 << 1 | srcBuf1 >>> 7) & 0x1f; 815 break; 816 case 5: 817 result = srcBuf1 >>> 2 & 0x1f; 818 break; 819 case 6: 820 if (source.empty) 821 { 822 srcBuf0 = 0; 823 resultChunkLen = 7; 824 } 825 else 826 { 827 srcBuf0 = source.front; 828 source.popFront(); 829 } 830 831 result = (srcBuf1 << 3 | srcBuf0 >>> 5) & 0x1f; 832 break; 833 case 7: 834 if (source.empty) 835 { 836 resultChunkLen = 0; 837 } 838 839 result = srcBuf0 & 0x1f; 840 break; 841 } 842 843 static if (usePad) 844 { 845 return _empty ? pad : encodingMap(result); 846 } 847 else 848 { 849 return encodingMap(result); 850 } 851 } 852 } 853 } 854 855 856 // For decoding 857 private enum size_t decSourceChunkLength = 8; 858 private enum size_t decResultChunkLength = 5; 859 860 861 private @safe pure nothrow 862 size_t normalize(in size_t sourceLength) 863 { 864 size_t r = sourceLength; 865 866 while(r % decSourceChunkLength) 867 { 868 r++; 869 } 870 871 return r; 872 } 873 874 875 private @safe pure nothrow 876 size_t padToMeaningfulLength(in size_t padLength) 877 { 878 switch (padLength) 879 { 880 case 0: 881 return 5; 882 case 1: 883 return 4; 884 case 3: 885 return 3; 886 case 4: 887 return 2; 888 case 6: 889 return 1; 890 default: 891 return size_t.max; 892 } 893 } 894 895 896 private @safe pure nothrow 897 bool isValidPadLength(in size_t padLength) 898 { 899 immutable m = padToMeaningfulLength(padLength); 900 901 return 0 < m && m < 6; 902 } 903 904 905 /** 906 * Calculates the length for decoding. 907 * 908 * Params: 909 * sourceLength = The length of a source. 910 * 911 * Returns: 912 * The maximum length which the result may have after a source with 913 * $(D_PARAM sourceLength) is decoded. 914 */ 915 @safe pure nothrow 916 size_t decodeLength(in size_t sourceLength) 917 { 918 static if (usePad){ 919 return (sourceLength / 8) * 5; 920 } 921 else 922 { 923 return (sourceLength / 8 + (sourceLength % 8 ? 1 : 0))* 5; 924 } 925 } 926 927 928 // This should be called after input validation 929 private @safe pure nothrow 930 size_t preciseDecodeLength(Array)(Array source) 931 { 932 immutable approx = decodeLength(source.length); 933 if (approx == 0) 934 { 935 return 0; 936 } 937 938 939 static if (usePad) 940 { 941 size_t padLength; 942 943 if (source[$ - 1] == pad) 944 { 945 padLength = 1; 946 947 if (source[$ - 2] == pad && source[$ - 3] == pad) 948 { 949 padLength = 3; 950 951 if (source[$ - 4] == pad) 952 { 953 padLength = 4; 954 955 if (source[$ - 5] == pad && source[$ - 6] == pad) 956 { 957 padLength = 6; 958 } 959 } 960 } 961 } 962 } 963 else 964 { 965 immutable padLength = normalize(source.length) - source.length; 966 } 967 968 immutable meaninglessLength = decResultChunkLength - padToMeaningfulLength(padLength); 969 return approx - meaninglessLength; 970 } 971 972 973 // 974 private @safe pure nothrow 975 size_t preciseDecodeLength(in size_t sourceLength, in size_t padLength) 976 { 977 immutable approx = decodeLength(sourceLength); 978 if (approx == 0) 979 { 980 return 0; 981 } 982 983 immutable meaninglessLength = decResultChunkLength - padToMeaningfulLength(padLength); 984 return approx - meaninglessLength; 985 } 986 987 988 // char[] to ubyte[] 989 990 /** 991 * Decodes $(D_PARAM source) and stores the result in $(D_PARAM buffer). 992 * If no _buffer is assigned, a new buffer will be created. 993 * 994 * Params: 995 * source = A _base32 character array to _decode. 996 * buffer = An array to store the decoded result. 997 * 998 * Returns: 999 * The slice of $(D_PARAM buffer) containing the result. 1000 * 1001 * Throws: 1002 * Exception if $(D_PARAM source) is an invalid _base32 data. 1003 */ 1004 @trusted pure 1005 ubyte[] decode(C : dchar, UB = ubyte)(in C[] source, UB[] buffer = null) 1006 if (isOutputRange!(UB[], ubyte)) 1007 in 1008 { 1009 assert(!buffer.ptr || buffer.length >= decodeLength(source.length), 1010 "Insufficient buffer for decoding"); 1011 } 1012 out(result) 1013 { 1014 immutable expect = preciseDecodeLength(source); 1015 assert(result.length == expect, 1016 "The result length is different from the expected"); 1017 } 1018 do 1019 { 1020 immutable actualLen = source.length; 1021 1022 if (actualLen == 0) 1023 { 1024 return []; 1025 } 1026 1027 static if (usePad) 1028 { 1029 immutable normalizedLen = actualLen; 1030 1031 if (normalizedLen % decSourceChunkLength) 1032 { 1033 throw new Base32Exception("Invalid source length"); 1034 } 1035 } 1036 else 1037 { 1038 immutable normalizedLen = normalize(actualLen); 1039 1040 if (!isValidPadLength(normalizedLen - actualLen)) 1041 { 1042 throw new Base32Exception("Invalid source length"); 1043 } 1044 } 1045 1046 if (!buffer.ptr) 1047 { 1048 buffer = new ubyte[decodeLength(normalizedLen)]; 1049 } 1050 1051 size_t padLength; 1052 size_t processedLength; 1053 auto srcPtr = source.ptr; 1054 auto bufPtr = buffer.ptr; 1055 1056 ubyte[decSourceChunkLength] tmpBuffer; 1057 foreach (_; 0 .. normalizedLen / decSourceChunkLength) 1058 { 1059 bool firstPadFound; 1060 1061 foreach (i, ref e; tmpBuffer) 1062 { 1063 static if (!usePad) 1064 { 1065 if (firstPadFound || srcPtr - source.ptr == actualLen) 1066 { 1067 e = 0; 1068 1069 if (!firstPadFound) 1070 { 1071 firstPadFound = true; 1072 padLength = decSourceChunkLength - i; 1073 } 1074 1075 continue; 1076 } 1077 } 1078 1079 immutable c = *srcPtr++; 1080 1081 static if (useHex) 1082 { 1083 if (!firstPadFound && '0' <= c && c <= '9') 1084 { 1085 e = cast(ubyte)(c - '0'); 1086 } 1087 else if (!firstPadFound && 'A' <= c && c <= 'V') 1088 { 1089 e = cast(ubyte)(c - 'A' + 10); 1090 } 1091 else if (c == pad) 1092 { 1093 e = 0; 1094 1095 if (!firstPadFound) 1096 { 1097 firstPadFound = true; 1098 padLength = decSourceChunkLength - i; 1099 1100 if (!usePad || !isValidPadLength(padLength)) 1101 { 1102 throw new Base32Exception("Invalid padding found"); 1103 } 1104 } 1105 } 1106 else 1107 { 1108 import std.conv : text; 1109 1110 throw new Base32Exception(text("Invalid character '", c, "' found")); 1111 } 1112 } 1113 else 1114 { 1115 if (!firstPadFound && 'A' <= c && c <= 'Z') 1116 { 1117 e = cast(ubyte)(c - 'A'); 1118 } 1119 else if (!firstPadFound && '2' <= c && c <= '7') 1120 { 1121 e = cast(ubyte)(c - '2' + 26); 1122 } 1123 else if (c == pad) 1124 { 1125 e = 0; 1126 1127 if (!firstPadFound) 1128 { 1129 firstPadFound = true; 1130 padLength = decSourceChunkLength - i; 1131 1132 if (!usePad || !isValidPadLength(padLength)) 1133 { 1134 throw new Base32Exception("Invalid padding found"); 1135 } 1136 } 1137 } 1138 else 1139 { 1140 import std.conv : text; 1141 1142 throw new Base32Exception(text("Invalid character '", c, "' found")); 1143 } 1144 } 1145 } 1146 1147 immutable meaningfulLength = padToMeaningfulLength(padLength); 1148 processedLength += meaningfulLength; 1149 1150 *bufPtr++ = cast(ubyte)(tmpBuffer[0] << 3 | tmpBuffer[1] >>> 2); 1151 if (meaningfulLength == 1) 1152 { 1153 break; 1154 } 1155 1156 *bufPtr++ = cast(ubyte)(tmpBuffer[1] << 6 | tmpBuffer[2] << 1 | tmpBuffer[3] >>> 4); 1157 if (meaningfulLength == 2) 1158 { 1159 break; 1160 } 1161 1162 *bufPtr++ = cast(ubyte)(tmpBuffer[3] << 4 | tmpBuffer[4] >>> 1); 1163 if (meaningfulLength == 3) 1164 { 1165 break; 1166 } 1167 1168 *bufPtr++ = cast(ubyte)(tmpBuffer[4] << 7 | tmpBuffer[5] << 2 | tmpBuffer[6] >>> 3); 1169 if (meaningfulLength == 4) 1170 { 1171 break; 1172 } 1173 1174 *bufPtr++ = cast(ubyte)(tmpBuffer[6] << 5 | tmpBuffer[7]); 1175 } 1176 1177 return buffer[0 .. processedLength]; 1178 } 1179 1180 1181 // InputRange to ubyte[] 1182 1183 /** 1184 * Decodes $(D_PARAM source) and stores the result in $(D_PARAM buffer). 1185 * If no _buffer is assigned, a new buffer will be created. 1186 * 1187 * Params: 1188 * source = A _base32 InputRange to _decode. 1189 * buffer = An array to store the decoded result. 1190 * 1191 * Returns: 1192 * The slice of $(D_PARAM buffer) containing the result. 1193 * 1194 * Throws: 1195 * Exception if $(D_PARAM source) is an invalid _base32 data. 1196 */ 1197 ubyte[] decode(CR, UB = ubyte)(CR source, UB[] buffer = null) 1198 if (!isArray!CR && isInputRange!CR && is(ElementType!CR : dchar) 1199 && hasLength!CR && isOutputRange!(UB[], ubyte)) 1200 in 1201 { 1202 assert(!buffer.ptr || buffer.length >= decodeLength(source.length), 1203 "Insufficient buffer for decoding"); 1204 } 1205 out(result) 1206 { 1207 // delegate to the proxies 1208 } 1209 do 1210 { 1211 immutable actualLen = source.length; 1212 1213 if (actualLen == 0) 1214 { 1215 // proxy out contract 1216 assert(0 == preciseDecodeLength(actualLen, 0)); 1217 return []; 1218 } 1219 1220 static if (usePad) 1221 { 1222 immutable normalizedLen = actualLen; 1223 1224 if (normalizedLen % decSourceChunkLength) 1225 { 1226 throw new Base32Exception("Invalid source length"); 1227 } 1228 } 1229 else 1230 { 1231 immutable normalizedLen = normalize(actualLen); 1232 1233 if (!isValidPadLength(normalizedLen - actualLen)) 1234 { 1235 throw new Base32Exception("Invalid source length"); 1236 } 1237 } 1238 1239 if (!buffer.ptr) 1240 { 1241 buffer = new ubyte[decodeLength(normalizedLen)]; 1242 } 1243 1244 size_t padLength; 1245 size_t processedLength; 1246 auto bufPtr = buffer.ptr; 1247 1248 ubyte[decSourceChunkLength] tmpBuffer; 1249 foreach (_; 0 .. normalizedLen / decSourceChunkLength) 1250 { 1251 bool firstPadFound; 1252 1253 foreach (i, ref e; tmpBuffer) 1254 { 1255 static if (!usePad) 1256 { 1257 if (firstPadFound || source.empty) 1258 { 1259 e = 0; 1260 1261 if (!firstPadFound) 1262 { 1263 firstPadFound = true; 1264 padLength = decSourceChunkLength - i; 1265 } 1266 1267 continue; 1268 } 1269 } 1270 1271 immutable c = source.front; 1272 source.popFront(); 1273 1274 static if (useHex) 1275 { 1276 if (!firstPadFound && '0' <= c && c <= '9') 1277 { 1278 e = cast(ubyte)(c - '0'); 1279 } 1280 else if (!firstPadFound && 'A' <= c && c <= 'V') 1281 { 1282 e = cast(ubyte)(c - 'A' + 10); 1283 } 1284 else if (c == pad) 1285 { 1286 e = 0; 1287 1288 if (!firstPadFound) 1289 { 1290 firstPadFound = true; 1291 padLength = decSourceChunkLength - i; 1292 1293 if (!isValidPadLength(padLength)) 1294 { 1295 throw new Base32Exception("Invalid padding found"); 1296 } 1297 } 1298 } 1299 else 1300 { 1301 import std.conv : text; 1302 1303 throw new Base32Exception(text("Invalid character '", c, "' found")); 1304 } 1305 } 1306 else 1307 { 1308 if (!firstPadFound && 'A' <= c && c <= 'Z') 1309 { 1310 e = cast(ubyte)(c - 'A'); 1311 } 1312 else if (!firstPadFound && '2' <= c && c <= '7') 1313 { 1314 e = cast(ubyte)(c - '2' + 26); 1315 } 1316 else if (c == pad) 1317 { 1318 e = 0; 1319 1320 if (!firstPadFound) 1321 { 1322 firstPadFound = true; 1323 padLength = decSourceChunkLength - i; 1324 1325 if (!isValidPadLength(padLength)) 1326 { 1327 throw new Base32Exception("Invalid padding found"); 1328 } 1329 } 1330 } 1331 else 1332 { 1333 import std.conv : text; 1334 1335 throw new Base32Exception(text("Invalid character '", c, "' found")); 1336 } 1337 } 1338 } 1339 1340 immutable meaningfulLength = padToMeaningfulLength(padLength); 1341 processedLength += meaningfulLength; 1342 1343 *bufPtr++ = cast(ubyte)(tmpBuffer[0] << 3 | tmpBuffer[1] >>> 2); 1344 if (meaningfulLength == 1) 1345 { 1346 break; 1347 } 1348 1349 *bufPtr++ = cast(ubyte)(tmpBuffer[1] << 6 | tmpBuffer[2] << 1 | tmpBuffer[3] >>> 4); 1350 if (meaningfulLength == 2) 1351 { 1352 break; 1353 } 1354 1355 *bufPtr++ = cast(ubyte)(tmpBuffer[3] << 4 | tmpBuffer[4] >>> 1); 1356 if (meaningfulLength == 3) 1357 { 1358 break; 1359 } 1360 1361 *bufPtr++ = cast(ubyte)(tmpBuffer[4] << 7 | tmpBuffer[5] << 2 | tmpBuffer[6] >>> 3); 1362 if (meaningfulLength == 4) 1363 { 1364 break; 1365 } 1366 1367 *bufPtr++ = cast(ubyte)(tmpBuffer[6] << 5 | tmpBuffer[7]); 1368 } 1369 1370 // proxy out contract 1371 assert(processedLength == preciseDecodeLength(actualLen, padLength)); 1372 1373 return buffer[0 .. processedLength]; 1374 } 1375 1376 1377 // char[] to OutputRange 1378 1379 /** 1380 * Decodes $(D_PARAM source) and outputs the result to $(D_PARAM range). 1381 * 1382 * Params: 1383 * source = A _base32 array to _decode. 1384 * range = An OutputRange to receive decoded result 1385 * 1386 * Returns: 1387 * The number of the output characters. 1388 * 1389 * Throws: 1390 * Exception if $(D_PARAM source) is invalid _base32 data. 1391 */ 1392 size_t decode(C : dchar, UBR)(in C[] source, UBR range) 1393 if (!is(UBR == ubyte[]) && isOutputRange!(UBR, ubyte)) 1394 out(result) 1395 { 1396 immutable expect = preciseDecodeLength(source); 1397 assert(result == expect, 1398 "The result is different from the expected"); 1399 } 1400 do 1401 { 1402 immutable actualLen = source.length; 1403 1404 if (actualLen == 0) 1405 { 1406 return 0; 1407 } 1408 1409 static if (usePad) 1410 { 1411 immutable normalizedLen = actualLen; 1412 1413 if (normalizedLen % decSourceChunkLength) 1414 { 1415 throw new Base32Exception("Invalid source length"); 1416 } 1417 } 1418 else 1419 { 1420 immutable normalizedLen = normalize(actualLen); 1421 1422 if (!isValidPadLength(normalizedLen - actualLen)) 1423 { 1424 throw new Base32Exception("Invalid source length"); 1425 } 1426 } 1427 1428 size_t padLength; 1429 size_t processedLength; 1430 auto srcPtr = source.ptr; 1431 1432 ubyte[decSourceChunkLength] tmpBuffer; 1433 foreach (_; 0 .. normalizedLen / decSourceChunkLength) 1434 { 1435 bool firstPadFound; 1436 1437 foreach (i, ref e; tmpBuffer) 1438 { 1439 static if (!usePad) 1440 { 1441 if (firstPadFound || srcPtr - source.ptr == actualLen) 1442 { 1443 e = 0; 1444 1445 if (!firstPadFound) 1446 { 1447 firstPadFound = true; 1448 padLength = decSourceChunkLength - i; 1449 } 1450 1451 continue; 1452 } 1453 } 1454 1455 immutable c = *srcPtr++; 1456 1457 static if (useHex) 1458 { 1459 if (!firstPadFound && '0' <= c && c <= '9') 1460 { 1461 e = cast(ubyte)(c - '0'); 1462 } 1463 else if (!firstPadFound && 'A' <= c && c <= 'V') 1464 { 1465 e = cast(ubyte)(c - 'A' + 10); 1466 } 1467 else if (c == pad) 1468 { 1469 e = 0; 1470 1471 if (!firstPadFound) 1472 { 1473 firstPadFound = true; 1474 padLength = decSourceChunkLength - i; 1475 1476 if (!usePad || !isValidPadLength(padLength)) 1477 { 1478 throw new Base32Exception("Invalid padding found"); 1479 } 1480 } 1481 } 1482 else 1483 { 1484 import std.conv : text; 1485 1486 throw new Base32Exception(text("Invalid character '", c, "' found")); 1487 } 1488 } 1489 else 1490 { 1491 if (!firstPadFound && 'A' <= c && c <= 'Z') 1492 { 1493 e = cast(ubyte)(c - 'A'); 1494 } 1495 else if (!firstPadFound && '2' <= c && c <= '7') 1496 { 1497 e = cast(ubyte)(c - '2' + 26); 1498 } 1499 else if (c == pad) 1500 { 1501 e = 0; 1502 1503 if (!firstPadFound) 1504 { 1505 firstPadFound = true; 1506 padLength = decSourceChunkLength - i; 1507 1508 if (!usePad || !isValidPadLength(padLength)) 1509 { 1510 throw new Base32Exception("Invalid padding found"); 1511 } 1512 } 1513 } 1514 else 1515 { 1516 import std.conv : text; 1517 1518 throw new Base32Exception(text("Invalid character '", c, "' found")); 1519 } 1520 } 1521 } 1522 1523 immutable meaningfulLength = padToMeaningfulLength(padLength); 1524 processedLength += meaningfulLength; 1525 1526 range.put(cast(ubyte)(tmpBuffer[0] << 3 | tmpBuffer[1] >>> 2)); 1527 if (meaningfulLength == 1) 1528 { 1529 break; 1530 } 1531 1532 range.put(cast(ubyte)(tmpBuffer[1] << 6 | tmpBuffer[2] << 1 | tmpBuffer[3] >>> 4)); 1533 if (meaningfulLength == 2) 1534 { 1535 break; 1536 } 1537 1538 range.put(cast(ubyte)(tmpBuffer[3] << 4 | tmpBuffer[4] >>> 1)); 1539 if (meaningfulLength == 3) 1540 { 1541 break; 1542 } 1543 1544 range.put(cast(ubyte)(tmpBuffer[4] << 7 | tmpBuffer[5] << 2 | tmpBuffer[6] >>> 3)); 1545 if (meaningfulLength == 4) 1546 { 1547 break; 1548 } 1549 1550 range.put(cast(ubyte)(tmpBuffer[6] << 5 | tmpBuffer[7])); 1551 } 1552 1553 return processedLength; 1554 } 1555 1556 1557 // InputRange to OutputRange 1558 1559 /** 1560 * Decodes $(D_PARAM source) and outputs the result to $(D_PARAM range). 1561 * 1562 * Params: 1563 * source = A _base32 InputRange to _decode. 1564 * range = An OutputRange to receive decoded result 1565 * 1566 * Returns: 1567 * The number of the output characters. 1568 * 1569 * Throws: 1570 * Exception if $(D_PARAM source) is invalid _base32 data. 1571 */ 1572 size_t decode(CR, UBR)(CR source, UBR range) 1573 if (!isArray!CR && isInputRange!CR && is(ElementType!CR : dchar) 1574 && hasLength!CR && !is(UBR == ubyte[]) 1575 && isOutputRange!(UBR, ubyte)) 1576 out(result) 1577 { 1578 // delegate to the proxies 1579 } 1580 do 1581 { 1582 immutable actualLen = source.length; 1583 1584 if (actualLen == 0) 1585 { 1586 // proxy out contract 1587 assert(0 == preciseDecodeLength(actualLen, 0)); 1588 return 0; 1589 } 1590 1591 static if (usePad) 1592 { 1593 immutable normalizedLen = actualLen; 1594 1595 if (normalizedLen % decSourceChunkLength) 1596 { 1597 throw new Base32Exception("Invalid source length"); 1598 } 1599 } 1600 else 1601 { 1602 immutable normalizedLen = normalize(actualLen); 1603 1604 if (!isValidPadLength(normalizedLen - actualLen)) 1605 { 1606 throw new Base32Exception("Invalid source length"); 1607 } 1608 } 1609 1610 size_t padLength; 1611 size_t processedLength; 1612 1613 ubyte[decSourceChunkLength] tmpBuffer; 1614 foreach (_; 0 .. normalizedLen / decSourceChunkLength) 1615 { 1616 bool firstPadFound; 1617 1618 foreach (i, ref e; tmpBuffer) 1619 { 1620 static if (!usePad) 1621 { 1622 if (firstPadFound || source.empty) 1623 { 1624 e = 0; 1625 1626 if (!firstPadFound) 1627 { 1628 firstPadFound = true; 1629 padLength = decSourceChunkLength - i; 1630 } 1631 1632 continue; 1633 } 1634 } 1635 1636 auto c = source.front; 1637 // assert(!source.empty); 1638 source.popFront(); 1639 1640 static if (useHex) 1641 { 1642 if (!firstPadFound && '0' <= c && c <= '9') 1643 { 1644 e = cast(ubyte)(c - '0'); 1645 } 1646 else if (!firstPadFound && 'A' <= c && c <= 'V') 1647 { 1648 e = cast(ubyte)(c - 'A' + 10); 1649 } 1650 else if (c == pad) 1651 { 1652 e = 0; 1653 1654 if (!firstPadFound) 1655 { 1656 firstPadFound = true; 1657 padLength = decSourceChunkLength - i; 1658 1659 if (!isValidPadLength(padLength)) 1660 { 1661 throw new Base32Exception("Invalid padding found"); 1662 } 1663 } 1664 } 1665 else 1666 { 1667 import std.conv : text; 1668 1669 throw new Base32Exception(text("Invalid character '", c, "' found")); 1670 } 1671 } 1672 else 1673 { 1674 if (!firstPadFound && 'A' <= c && c <= 'Z') 1675 { 1676 e = cast(ubyte)(c - 'A'); 1677 } 1678 else if (!firstPadFound && '2' <= c && c <= '7') 1679 { 1680 e = cast(ubyte)(c - '2' + 26); 1681 } 1682 else if (c == pad) 1683 { 1684 e = 0; 1685 1686 if (!firstPadFound) 1687 { 1688 firstPadFound = true; 1689 padLength = decSourceChunkLength - i; 1690 1691 if (!isValidPadLength(padLength)) 1692 { 1693 throw new Base32Exception("Invalid padding found"); 1694 } 1695 } 1696 } 1697 else 1698 { 1699 import std.conv : text; 1700 1701 throw new Base32Exception(text("Invalid character '", c, "' found")); 1702 } 1703 } 1704 } 1705 1706 immutable meaningfulLength = padToMeaningfulLength(padLength); 1707 processedLength += meaningfulLength; 1708 1709 range.put(cast(ubyte)(tmpBuffer[0] << 3 | tmpBuffer[1] >>> 2)); 1710 if (meaningfulLength == 1) 1711 { 1712 break; 1713 } 1714 1715 range.put(cast(ubyte)(tmpBuffer[1] << 6 | tmpBuffer[2] << 1 | tmpBuffer[3] >>> 4)); 1716 if (meaningfulLength == 2) 1717 { 1718 break; 1719 } 1720 1721 range.put(cast(ubyte)(tmpBuffer[3] << 4 | tmpBuffer[4] >>> 1)); 1722 if (meaningfulLength == 3) 1723 { 1724 break; 1725 } 1726 1727 range.put(cast(ubyte)(tmpBuffer[4] << 7 | tmpBuffer[5] << 2 | tmpBuffer[6] >>> 3)); 1728 if (meaningfulLength == 4) 1729 { 1730 break; 1731 } 1732 1733 range.put(cast(ubyte)(tmpBuffer[6] << 5 | tmpBuffer[7])); 1734 } 1735 1736 assert(source.empty); 1737 1738 // proxy out contract 1739 assert(processedLength == preciseDecodeLength(actualLen, padLength)); 1740 1741 return processedLength; 1742 } 1743 } 1744 1745 1746 /** 1747 * Exception thrown on errors in _base32 functions. 1748 */ 1749 class Base32Exception : Exception 1750 { 1751 @safe pure nothrow 1752 this(string s, string fn = __FILE__, size_t ln = __LINE__) 1753 { 1754 super(s, fn, ln); 1755 } 1756 } 1757 1758 1759 // Encoding 1760 unittest 1761 { 1762 alias Base32NoPad = Base32Impl!(UseHex.no, UsePad.no); 1763 alias Base32HexNoPad = Base32Impl!(UseHex.yes, UsePad.no); 1764 1765 pure auto toA(string s) 1766 { 1767 return cast(ubyte[])s; 1768 } 1769 1770 pure auto toR(string s) 1771 { 1772 struct Range 1773 { 1774 private ubyte[] source; 1775 private size_t index; 1776 1777 this(string s) 1778 { 1779 source = cast(ubyte[])s; 1780 } 1781 1782 bool empty() @property 1783 { 1784 return index == source.length; 1785 } 1786 1787 ubyte front() @property 1788 { 1789 return source[index]; 1790 } 1791 1792 void popFront() 1793 { 1794 index++; 1795 } 1796 1797 size_t length() @property 1798 { 1799 return source.length; 1800 } 1801 } 1802 1803 import std.range.primitives, std.traits; 1804 1805 static assert(isInputRange!Range && is(ElementType!Range : ubyte) 1806 && hasLength!Range); 1807 1808 return Range(s); 1809 } 1810 1811 1812 // Length 1813 { 1814 assert(Base32.encodeLength(toA("").length) == 0); 1815 assert(Base32.encodeLength(toA("f").length) == 8); 1816 assert(Base32.encodeLength(toA("fo").length) == 8); 1817 assert(Base32.encodeLength(toA("foo").length) == 8); 1818 assert(Base32.encodeLength(toA("foob").length) == 8); 1819 assert(Base32.encodeLength(toA("fooba").length) == 8); 1820 assert(Base32.encodeLength(toA("foobar").length) == 16); 1821 1822 assert(Base32Hex.encodeLength(toA("").length) == 0); 1823 assert(Base32Hex.encodeLength(toA("f").length) == 8); 1824 assert(Base32Hex.encodeLength(toA("fo").length) == 8); 1825 assert(Base32Hex.encodeLength(toA("foo").length) == 8); 1826 assert(Base32Hex.encodeLength(toA("foob").length) == 8); 1827 assert(Base32Hex.encodeLength(toA("fooba").length) == 8); 1828 assert(Base32Hex.encodeLength(toA("foobar").length) == 16); 1829 1830 assert(Base32NoPad.encodeLength(toA("").length) == 0); 1831 assert(Base32NoPad.encodeLength(toA("f").length) == 2); 1832 assert(Base32NoPad.encodeLength(toA("fo").length) == 4); 1833 assert(Base32NoPad.encodeLength(toA("foo").length) == 5); 1834 assert(Base32NoPad.encodeLength(toA("foob").length) == 7); 1835 assert(Base32NoPad.encodeLength(toA("fooba").length) == 8); 1836 assert(Base32NoPad.encodeLength(toA("foobar").length) == 10); 1837 1838 assert(Base32HexNoPad.encodeLength(toA("").length) == 0); 1839 assert(Base32HexNoPad.encodeLength(toA("f").length) == 2); 1840 assert(Base32HexNoPad.encodeLength(toA("fo").length) == 4); 1841 assert(Base32HexNoPad.encodeLength(toA("foo").length) == 5); 1842 assert(Base32HexNoPad.encodeLength(toA("foob").length) == 7); 1843 assert(Base32HexNoPad.encodeLength(toA("fooba").length) == 8); 1844 assert(Base32HexNoPad.encodeLength(toA("foobar").length) == 10); 1845 } 1846 1847 // Array to Array 1848 { 1849 assert(Base32.encode(toA("")) == ""); 1850 assert(Base32.encode(toA("f")) == "MY======"); 1851 assert(Base32.encode(toA("fo")) == "MZXQ===="); 1852 assert(Base32.encode(toA("foo")) == "MZXW6==="); 1853 assert(Base32.encode(toA("foob")) == "MZXW6YQ="); 1854 assert(Base32.encode(toA("fooba")) == "MZXW6YTB"); 1855 assert(Base32.encode(toA("foobar")) == "MZXW6YTBOI======"); 1856 1857 assert(Base32Hex.encode(toA("")) == ""); 1858 assert(Base32Hex.encode(toA("f")) == "CO======"); 1859 assert(Base32Hex.encode(toA("fo")) == "CPNG===="); 1860 assert(Base32Hex.encode(toA("foo")) == "CPNMU==="); 1861 assert(Base32Hex.encode(toA("foob")) == "CPNMUOG="); 1862 assert(Base32Hex.encode(toA("fooba")) == "CPNMUOJ1"); 1863 assert(Base32Hex.encode(toA("foobar")) == "CPNMUOJ1E8======"); 1864 1865 assert(Base32NoPad.encode(toA("")) == ""); 1866 assert(Base32NoPad.encode(toA("f")) == "MY"); 1867 assert(Base32NoPad.encode(toA("fo")) == "MZXQ"); 1868 assert(Base32NoPad.encode(toA("foo")) == "MZXW6"); 1869 assert(Base32NoPad.encode(toA("foob")) == "MZXW6YQ"); 1870 assert(Base32NoPad.encode(toA("fooba")) == "MZXW6YTB"); 1871 assert(Base32NoPad.encode(toA("foobar")) == "MZXW6YTBOI"); 1872 1873 assert(Base32HexNoPad.encode(toA("")) == ""); 1874 assert(Base32HexNoPad.encode(toA("f")) == "CO"); 1875 assert(Base32HexNoPad.encode(toA("fo")) == "CPNG"); 1876 assert(Base32HexNoPad.encode(toA("foo")) == "CPNMU"); 1877 assert(Base32HexNoPad.encode(toA("foob")) == "CPNMUOG"); 1878 assert(Base32HexNoPad.encode(toA("fooba")) == "CPNMUOJ1"); 1879 assert(Base32HexNoPad.encode(toA("foobar")) == "CPNMUOJ1E8"); 1880 } 1881 1882 // InputRange to Array 1883 { 1884 assert(Base32.encode(toR("")) == ""); 1885 assert(Base32.encode(toR("f")) == "MY======"); 1886 assert(Base32.encode(toR("fo")) == "MZXQ===="); 1887 assert(Base32.encode(toR("foo")) == "MZXW6==="); 1888 assert(Base32.encode(toR("foob")) == "MZXW6YQ="); 1889 assert(Base32.encode(toR("fooba")) == "MZXW6YTB"); 1890 assert(Base32.encode(toR("foobar")) == "MZXW6YTBOI======"); 1891 1892 assert(Base32Hex.encode(toR("")) == ""); 1893 assert(Base32Hex.encode(toR("f")) == "CO======"); 1894 assert(Base32Hex.encode(toR("fo")) == "CPNG===="); 1895 assert(Base32Hex.encode(toR("foo")) == "CPNMU==="); 1896 assert(Base32Hex.encode(toR("foob")) == "CPNMUOG="); 1897 assert(Base32Hex.encode(toR("fooba")) == "CPNMUOJ1"); 1898 assert(Base32Hex.encode(toR("foobar")) == "CPNMUOJ1E8======"); 1899 1900 assert(Base32NoPad.encode(toR("")) == ""); 1901 assert(Base32NoPad.encode(toR("f")) == "MY"); 1902 assert(Base32NoPad.encode(toR("fo")) == "MZXQ"); 1903 assert(Base32NoPad.encode(toR("foo")) == "MZXW6"); 1904 assert(Base32NoPad.encode(toR("foob")) == "MZXW6YQ"); 1905 assert(Base32NoPad.encode(toR("fooba")) == "MZXW6YTB"); 1906 assert(Base32NoPad.encode(toR("foobar")) == "MZXW6YTBOI"); 1907 1908 assert(Base32HexNoPad.encode(toR("")) == ""); 1909 assert(Base32HexNoPad.encode(toR("f")) == "CO"); 1910 assert(Base32HexNoPad.encode(toR("fo")) == "CPNG"); 1911 assert(Base32HexNoPad.encode(toR("foo")) == "CPNMU"); 1912 assert(Base32HexNoPad.encode(toR("foob")) == "CPNMUOG"); 1913 assert(Base32HexNoPad.encode(toR("fooba")) == "CPNMUOJ1"); 1914 assert(Base32HexNoPad.encode(toR("foobar")) == "CPNMUOJ1E8"); 1915 } 1916 1917 // Array to OutputRange 1918 { 1919 import std.array : appender; 1920 1921 auto app = appender!(char[])(); 1922 1923 assert(Base32.encode(toA(""), app) == 0); 1924 assert(app.data == ""); 1925 app.clear(); 1926 assert(Base32.encode(toA("f"), app) == 8); 1927 assert(app.data == "MY======"); 1928 app.clear(); 1929 assert(Base32.encode(toA("fo"), app) == 8); 1930 assert(app.data == "MZXQ===="); 1931 app.clear(); 1932 assert(Base32.encode(toA("foo"), app) == 8); 1933 assert(app.data == "MZXW6==="); 1934 app.clear(); 1935 assert(Base32.encode(toA("foob"), app) == 8); 1936 assert(app.data == "MZXW6YQ="); 1937 app.clear(); 1938 assert(Base32.encode(toA("fooba"), app) == 8); 1939 assert(app.data == "MZXW6YTB"); 1940 app.clear(); 1941 assert(Base32.encode(toA("foobar"), app) == 16); 1942 assert(app.data == "MZXW6YTBOI======"); 1943 app.clear(); 1944 1945 assert(Base32Hex.encode(toA(""), app) == 0); 1946 assert(app.data == ""); 1947 app.clear(); 1948 assert(Base32Hex.encode(toA("f"), app) == 8); 1949 assert(app.data == "CO======"); 1950 app.clear(); 1951 assert(Base32Hex.encode(toA("fo"), app) == 8); 1952 assert(app.data == "CPNG===="); 1953 app.clear(); 1954 assert(Base32Hex.encode(toA("foo"), app) == 8); 1955 assert(app.data == "CPNMU==="); 1956 app.clear(); 1957 assert(Base32Hex.encode(toA("foob"), app) == 8); 1958 assert(app.data == "CPNMUOG="); 1959 app.clear(); 1960 assert(Base32Hex.encode(toA("fooba"), app) == 8); 1961 assert(app.data == "CPNMUOJ1"); 1962 app.clear(); 1963 assert(Base32Hex.encode(toA("foobar"), app) == 16); 1964 assert(app.data == "CPNMUOJ1E8======"); 1965 app.clear(); 1966 1967 assert(Base32NoPad.encode(toA(""), app) == 0); 1968 assert(app.data == ""); 1969 app.clear(); 1970 assert(Base32NoPad.encode(toA("f"), app) == 2); 1971 assert(app.data == "MY"); 1972 app.clear(); 1973 assert(Base32NoPad.encode(toA("fo"), app) == 4); 1974 assert(app.data == "MZXQ"); 1975 app.clear(); 1976 assert(Base32NoPad.encode(toA("foo"), app) == 5); 1977 assert(app.data == "MZXW6"); 1978 app.clear(); 1979 assert(Base32NoPad.encode(toA("foob"), app) == 7); 1980 assert(app.data == "MZXW6YQ"); 1981 app.clear(); 1982 assert(Base32NoPad.encode(toA("fooba"), app) == 8); 1983 assert(app.data == "MZXW6YTB"); 1984 app.clear(); 1985 assert(Base32NoPad.encode(toA("foobar"), app) == 10); 1986 assert(app.data == "MZXW6YTBOI"); 1987 app.clear(); 1988 1989 assert(Base32HexNoPad.encode(toA(""), app) == 0); 1990 assert(app.data == ""); 1991 app.clear(); 1992 assert(Base32HexNoPad.encode(toA("f"), app) == 2); 1993 assert(app.data == "CO"); 1994 app.clear(); 1995 assert(Base32HexNoPad.encode(toA("fo"), app) == 4); 1996 assert(app.data == "CPNG"); 1997 app.clear(); 1998 assert(Base32HexNoPad.encode(toA("foo"), app) == 5); 1999 assert(app.data == "CPNMU"); 2000 app.clear(); 2001 assert(Base32HexNoPad.encode(toA("foob"), app) == 7); 2002 assert(app.data == "CPNMUOG"); 2003 app.clear(); 2004 assert(Base32HexNoPad.encode(toA("fooba"), app) == 8); 2005 assert(app.data == "CPNMUOJ1"); 2006 app.clear(); 2007 assert(Base32HexNoPad.encode(toA("foobar"), app) == 10); 2008 assert(app.data == "CPNMUOJ1E8"); 2009 app.clear(); 2010 } 2011 2012 // InputRange to OutputRange 2013 { 2014 import std.array : appender; 2015 2016 auto app = appender!(char[])(); 2017 2018 assert(Base32.encode(toR(""), app) == 0); 2019 assert(app.data == ""); 2020 app.clear(); 2021 assert(Base32.encode(toR("f"), app) == 8); 2022 assert(app.data == "MY======"); 2023 app.clear(); 2024 assert(Base32.encode(toR("fo"), app) == 8); 2025 assert(app.data == "MZXQ===="); 2026 app.clear(); 2027 assert(Base32.encode(toR("foo"), app) == 8); 2028 assert(app.data == "MZXW6==="); 2029 app.clear(); 2030 assert(Base32.encode(toR("foob"), app) == 8); 2031 assert(app.data == "MZXW6YQ="); 2032 app.clear(); 2033 assert(Base32.encode(toR("fooba"), app) == 8); 2034 assert(app.data == "MZXW6YTB"); 2035 app.clear(); 2036 assert(Base32.encode(toR("foobar"), app) == 16); 2037 assert(app.data == "MZXW6YTBOI======"); 2038 app.clear(); 2039 2040 assert(Base32Hex.encode(toR(""), app) == 0); 2041 assert(app.data == ""); 2042 app.clear(); 2043 assert(Base32Hex.encode(toR("f"), app) == 8); 2044 assert(app.data == "CO======"); 2045 app.clear(); 2046 assert(Base32Hex.encode(toR("fo"), app) == 8); 2047 assert(app.data == "CPNG===="); 2048 app.clear(); 2049 assert(Base32Hex.encode(toR("foo"), app) == 8); 2050 assert(app.data == "CPNMU==="); 2051 app.clear(); 2052 assert(Base32Hex.encode(toR("foob"), app) == 8); 2053 assert(app.data == "CPNMUOG="); 2054 app.clear(); 2055 assert(Base32Hex.encode(toR("fooba"), app) == 8); 2056 assert(app.data == "CPNMUOJ1"); 2057 app.clear(); 2058 assert(Base32Hex.encode(toR("foobar"), app) == 16); 2059 assert(app.data == "CPNMUOJ1E8======"); 2060 app.clear(); 2061 2062 assert(Base32NoPad.encode(toR(""), app) == 0); 2063 assert(app.data == ""); 2064 app.clear(); 2065 assert(Base32NoPad.encode(toR("f"), app) == 2); 2066 assert(app.data == "MY"); 2067 app.clear(); 2068 assert(Base32NoPad.encode(toR("fo"), app) == 4); 2069 assert(app.data == "MZXQ"); 2070 app.clear(); 2071 assert(Base32NoPad.encode(toR("foo"), app) == 5); 2072 assert(app.data == "MZXW6"); 2073 app.clear(); 2074 assert(Base32NoPad.encode(toR("foob"), app) == 7); 2075 assert(app.data == "MZXW6YQ"); 2076 app.clear(); 2077 assert(Base32NoPad.encode(toR("fooba"), app) == 8); 2078 assert(app.data == "MZXW6YTB"); 2079 app.clear(); 2080 assert(Base32NoPad.encode(toR("foobar"), app) == 10); 2081 assert(app.data == "MZXW6YTBOI"); 2082 app.clear(); 2083 2084 assert(Base32HexNoPad.encode(toR(""), app) == 0); 2085 assert(app.data == ""); 2086 app.clear(); 2087 assert(Base32HexNoPad.encode(toR("f"), app) == 2); 2088 assert(app.data == "CO"); 2089 app.clear(); 2090 assert(Base32HexNoPad.encode(toR("fo"), app) == 4); 2091 assert(app.data == "CPNG"); 2092 app.clear(); 2093 assert(Base32HexNoPad.encode(toR("foo"), app) == 5); 2094 assert(app.data == "CPNMU"); 2095 app.clear(); 2096 assert(Base32HexNoPad.encode(toR("foob"), app) == 7); 2097 assert(app.data == "CPNMUOG"); 2098 app.clear(); 2099 assert(Base32HexNoPad.encode(toR("fooba"), app) == 8); 2100 assert(app.data == "CPNMUOJ1"); 2101 app.clear(); 2102 assert(Base32HexNoPad.encode(toR("foobar"), app) == 10); 2103 assert(app.data == "CPNMUOJ1E8"); 2104 app.clear(); 2105 } 2106 2107 // Encoder 2108 { 2109 import std.algorithm.comparison : equal; 2110 import std.exception : assertThrown; 2111 2112 auto enc1 = Base32.encoder(toA("")); 2113 assert(enc1.empty); 2114 assert(enc1.length == 0); 2115 assert(enc1.equal("")); 2116 enc1 = Base32.encoder(toA("f")); 2117 assert(enc1.length == 8); 2118 assert(enc1.equal("MY======")); 2119 enc1 = Base32.encoder(toA("fo")); 2120 assert(enc1.length == 8); 2121 assert(enc1.equal("MZXQ====")); 2122 enc1 = Base32.encoder(toA("foo")); 2123 assert(enc1.length == 8); 2124 assert(enc1.equal("MZXW6===")); 2125 enc1 = Base32.encoder(toA("foob")); 2126 assert(enc1.length == 8); 2127 assert(enc1.equal("MZXW6YQ=")); 2128 enc1 = Base32.encoder(toA("fooba")); 2129 assert(enc1.length == 8); 2130 assert(enc1.equal("MZXW6YTB")); 2131 enc1 = Base32.encoder(toA("foobar")); 2132 assert(enc1.length == 16); 2133 assert(enc1.equal("MZXW6YTBOI======")); 2134 2135 auto enc2 = Base32NoPad.encoder(toA("")); 2136 assert(enc2.empty); 2137 assert(enc2.length == 0); 2138 assert(enc2.equal("")); 2139 enc2 = Base32NoPad.encoder(toA("f")); 2140 assert(enc2.length == 2); 2141 assert(enc2.equal("MY")); 2142 enc2 = Base32NoPad.encoder(toA("fo")); 2143 assert(enc2.length == 4); 2144 assert(enc2.equal("MZXQ")); 2145 enc2 = Base32NoPad.encoder(toA("foo")); 2146 assert(enc2.length == 5); 2147 assert(enc2.equal("MZXW6")); 2148 enc2 = Base32NoPad.encoder(toA("foob")); 2149 assert(enc2.length == 7); 2150 assert(enc2.equal("MZXW6YQ")); 2151 enc2 = Base32NoPad.encoder(toA("fooba")); 2152 assert(enc2.length == 8); 2153 assert(enc2.equal("MZXW6YTB")); 2154 enc2 = Base32NoPad.encoder(toA("foobar")); 2155 assert(enc2.length == 10); 2156 assert(enc2.equal("MZXW6YTBOI")); 2157 2158 enc2 = Base32NoPad.encoder(toA("f")); 2159 // assert(enc2.length == 2); 2160 // assert(enc2.equal("MY")); 2161 auto enc3 = enc2.save; 2162 enc2.popFront(); 2163 enc2.popFront(); 2164 assert(enc2.empty); 2165 assert(enc2.length == 0); 2166 assert(enc2.equal("")); 2167 assert(enc3.length == 2); 2168 assert(enc3.equal("MY")); 2169 2170 assertThrown!Base32Exception(enc2.popFront()); 2171 assertThrown!Base32Exception(enc2.front); 2172 } 2173 } 2174 2175 // Decoding 2176 unittest 2177 { 2178 alias Base32NoPad = Base32Impl!(UseHex.no, UsePad.no); 2179 alias Base32HexNoPad = Base32Impl!(UseHex.yes, UsePad.no); 2180 2181 pure auto toA(in string s) 2182 { 2183 return cast(ubyte[])s; 2184 } 2185 2186 pure auto toR(in string s) 2187 { 2188 struct Range 2189 { 2190 private char[] source; 2191 private size_t index; 2192 2193 this(string s) 2194 { 2195 source = cast(char[])s; 2196 } 2197 2198 bool empty() @property 2199 { 2200 return index == source.length; 2201 } 2202 2203 ubyte front() @property 2204 { 2205 return source[index]; 2206 } 2207 2208 void popFront() 2209 { 2210 index++; 2211 } 2212 2213 size_t length() @property 2214 { 2215 return source.length; 2216 } 2217 } 2218 2219 import std.range.primitives, std.traits; 2220 2221 static assert(isInputRange!Range && is(ElementType!Range : dchar) 2222 && hasLength!Range); 2223 2224 return Range(s); 2225 } 2226 2227 2228 import std.exception : assertThrown; 2229 2230 // Length 2231 { 2232 assert(Base32.decodeLength(0) == 0); 2233 assert(Base32.decodeLength(8) == 5); 2234 assert(Base32.decodeLength(16) == 10); 2235 2236 assert(Base32.preciseDecodeLength("") == 0); 2237 assert(Base32.preciseDecodeLength("MY======") == 1); 2238 assert(Base32.preciseDecodeLength("MZXQ====") == 2); 2239 assert(Base32.preciseDecodeLength("MZXW6===") == 3); 2240 assert(Base32.preciseDecodeLength("MZXW6YQ=") == 4); 2241 assert(Base32.preciseDecodeLength("MZXW6YTB") == 5); 2242 assert(Base32.preciseDecodeLength("MZXW6YTBOI======") == 6); 2243 2244 assert(Base32Hex.decodeLength(0) == 0); 2245 assert(Base32Hex.decodeLength(8) == 5); 2246 assert(Base32Hex.decodeLength(16) == 10); 2247 2248 assert(Base32Hex.preciseDecodeLength("") == 0); 2249 assert(Base32Hex.preciseDecodeLength("CO======") == 1); 2250 assert(Base32Hex.preciseDecodeLength("CPNG====") == 2); 2251 assert(Base32Hex.preciseDecodeLength("CPNMU===") == 3); 2252 assert(Base32Hex.preciseDecodeLength("CPNMUOG=") == 4); 2253 assert(Base32Hex.preciseDecodeLength("CPNMUOJ1") == 5); 2254 assert(Base32Hex.preciseDecodeLength("CPNMUOJ1E8======") == 6); 2255 2256 assert(Base32NoPad.decodeLength(0) == 0); 2257 assert(Base32NoPad.decodeLength(2) == 5); 2258 assert(Base32NoPad.decodeLength(4) == 5); 2259 assert(Base32NoPad.decodeLength(5) == 5); 2260 assert(Base32NoPad.decodeLength(7) == 5); 2261 assert(Base32NoPad.decodeLength(8) == 5); 2262 assert(Base32NoPad.decodeLength(10) == 10); 2263 2264 assert(Base32NoPad.preciseDecodeLength("") == 0); 2265 assert(Base32NoPad.preciseDecodeLength("MY") == 1); 2266 assert(Base32NoPad.preciseDecodeLength("MZXQ") == 2); 2267 assert(Base32NoPad.preciseDecodeLength("MZXW6") == 3); 2268 assert(Base32NoPad.preciseDecodeLength("MZXW6YQ") == 4); 2269 assert(Base32NoPad.preciseDecodeLength("MZXW6YTB") == 5); 2270 assert(Base32NoPad.preciseDecodeLength("MZXW6YTBOI") == 6); 2271 2272 assert(Base32HexNoPad.decodeLength(0) == 0); 2273 assert(Base32HexNoPad.decodeLength(2) == 5); 2274 assert(Base32HexNoPad.decodeLength(4) == 5); 2275 assert(Base32HexNoPad.decodeLength(5) == 5); 2276 assert(Base32HexNoPad.decodeLength(7) == 5); 2277 assert(Base32HexNoPad.decodeLength(8) == 5); 2278 assert(Base32HexNoPad.decodeLength(10) == 10); 2279 2280 assert(Base32HexNoPad.preciseDecodeLength("") == 0); 2281 assert(Base32HexNoPad.preciseDecodeLength("CO") == 1); 2282 assert(Base32HexNoPad.preciseDecodeLength("CPNG") == 2); 2283 assert(Base32HexNoPad.preciseDecodeLength("CPNMU") == 3); 2284 assert(Base32HexNoPad.preciseDecodeLength("CPNMUOG") == 4); 2285 assert(Base32HexNoPad.preciseDecodeLength("CPNMUOJ1") == 5); 2286 assert(Base32HexNoPad.preciseDecodeLength("CPNMUOJ1E8") == 6); 2287 } 2288 2289 // Array to Array 2290 { 2291 assert(Base32.decode("") == toA("")); 2292 assert(Base32.decode("MY======") == toA("f")); 2293 assert(Base32.decode("MZXQ====") == toA("fo")); 2294 assert(Base32.decode("MZXW6===") == toA("foo")); 2295 assert(Base32.decode("MZXW6YQ=") == toA("foob")); 2296 assert(Base32.decode("MZXW6YTB") == toA("fooba")); 2297 assert(Base32.decode("MZXW6YTBOI======") == toA("foobar")); 2298 2299 assert(Base32Hex.decode("") == toA("")); 2300 assert(Base32Hex.decode("CO======") == toA("f")); 2301 assert(Base32Hex.decode("CPNG====") == toA("fo")); 2302 assert(Base32Hex.decode("CPNMU===") == toA("foo")); 2303 assert(Base32Hex.decode("CPNMUOG=") == toA("foob")); 2304 assert(Base32Hex.decode("CPNMUOJ1") == toA("fooba")); 2305 assert(Base32Hex.decode("CPNMUOJ1E8======") == toA("foobar")); 2306 2307 assert(Base32NoPad.decode("") == toA("")); 2308 assert(Base32NoPad.decode("MY") == toA("f")); 2309 assert(Base32NoPad.decode("MZXQ") == toA("fo")); 2310 assert(Base32NoPad.decode("MZXW6") == toA("foo")); 2311 assert(Base32NoPad.decode("MZXW6YQ") == toA("foob")); 2312 assert(Base32NoPad.decode("MZXW6YTB") == toA("fooba")); 2313 assert(Base32NoPad.decode("MZXW6YTBOI") == toA("foobar")); 2314 2315 assert(Base32HexNoPad.decode("") == toA("")); 2316 assert(Base32HexNoPad.decode("CO") == toA("f")); 2317 assert(Base32HexNoPad.decode("CPNG") == toA("fo")); 2318 assert(Base32HexNoPad.decode("CPNMU") == toA("foo")); 2319 assert(Base32HexNoPad.decode("CPNMUOG") == toA("foob")); 2320 assert(Base32HexNoPad.decode("CPNMUOJ1") == toA("fooba")); 2321 assert(Base32HexNoPad.decode("CPNMUOJ1E8") == toA("foobar")); 2322 2323 // Invalid source length 2324 assertThrown!Base32Exception(Base32.decode("A")); 2325 assertThrown!Base32Exception(Base32.decode("AA")); 2326 assertThrown!Base32Exception(Base32.decode("AAA")); 2327 assertThrown!Base32Exception(Base32.decode("AAAA")); 2328 assertThrown!Base32Exception(Base32.decode("AAAAA")); 2329 assertThrown!Base32Exception(Base32.decode("AAAAAA")); 2330 assertThrown!Base32Exception(Base32.decode("AAAAAAA")); 2331 2332 assertThrown!Base32Exception(Base32Hex.decode("A")); 2333 assertThrown!Base32Exception(Base32Hex.decode("AA")); 2334 assertThrown!Base32Exception(Base32Hex.decode("AAA")); 2335 assertThrown!Base32Exception(Base32Hex.decode("AAAA")); 2336 assertThrown!Base32Exception(Base32Hex.decode("AAAAA")); 2337 assertThrown!Base32Exception(Base32Hex.decode("AAAAAA")); 2338 assertThrown!Base32Exception(Base32Hex.decode("AAAAAAA")); 2339 2340 assertThrown!Base32Exception(Base32NoPad.decode("A")); 2341 assertThrown!Base32Exception(Base32NoPad.decode("AAA")); 2342 assertThrown!Base32Exception(Base32NoPad.decode("AAAAAA")); 2343 assertThrown!Base32Exception(Base32NoPad.decode("AAAAAAAAA")); 2344 2345 assertThrown!Base32Exception(Base32HexNoPad.decode("A")); 2346 assertThrown!Base32Exception(Base32HexNoPad.decode("AAA")); 2347 assertThrown!Base32Exception(Base32HexNoPad.decode("AAAAAA")); 2348 assertThrown!Base32Exception(Base32HexNoPad.decode("AAAAAAAAA")); 2349 2350 // Invalid character 2351 assertThrown!Base32Exception(Base32.decode("AA?AA?AA")); 2352 assertThrown!Base32Exception(Base32Hex.decode("AA?AA?AA")); 2353 assertThrown!Base32Exception(Base32NoPad.decode("AA?AA?AA")); 2354 assertThrown!Base32Exception(Base32HexNoPad.decode("AA?AA?AA")); 2355 2356 // Invalid padding 2357 assertThrown!Base32Exception(Base32.decode("=AAAAAAA")); 2358 assertThrown!Base32Exception(Base32Hex.decode("=AAAAAAA")); 2359 assertThrown!Base32Exception(Base32NoPad.decode("=AAAAAAA")); 2360 assertThrown!Base32Exception(Base32HexNoPad.decode("=AAAAAAA")); 2361 } 2362 2363 // InputRange to Array 2364 { 2365 assert(Base32.decode(toR("")) == toA("")); 2366 assert(Base32.decode(toR("MY======")) == toA("f")); 2367 assert(Base32.decode(toR("MZXQ====")) == toA("fo")); 2368 assert(Base32.decode(toR("MZXW6===")) == toA("foo")); 2369 assert(Base32.decode(toR("MZXW6YQ=")) == toA("foob")); 2370 assert(Base32.decode(toR("MZXW6YTB")) == toA("fooba")); 2371 assert(Base32.decode(toR("MZXW6YTBOI======")) == toA("foobar")); 2372 2373 assert(Base32Hex.decode(toR("")) == toA("")); 2374 assert(Base32Hex.decode(toR("CO======")) == toA("f")); 2375 assert(Base32Hex.decode(toR("CPNG====")) == toA("fo")); 2376 assert(Base32Hex.decode(toR("CPNMU===")) == toA("foo")); 2377 assert(Base32Hex.decode(toR("CPNMUOG=")) == toA("foob")); 2378 assert(Base32Hex.decode(toR("CPNMUOJ1")) == toA("fooba")); 2379 assert(Base32Hex.decode(toR("CPNMUOJ1E8======")) == toA("foobar")); 2380 2381 assert(Base32NoPad.decode(toR("")) == toA("")); 2382 assert(Base32NoPad.decode(toR("MY")) == toA("f")); 2383 assert(Base32NoPad.decode(toR("MZXQ")) == toA("fo")); 2384 assert(Base32NoPad.decode(toR("MZXW6")) == toA("foo")); 2385 assert(Base32NoPad.decode(toR("MZXW6YQ")) == toA("foob")); 2386 assert(Base32NoPad.decode(toR("MZXW6YTB")) == toA("fooba")); 2387 assert(Base32NoPad.decode(toR("MZXW6YTBOI")) == toA("foobar")); 2388 2389 assert(Base32HexNoPad.decode(toR("")) == toA("")); 2390 assert(Base32HexNoPad.decode(toR("CO")) == toA("f")); 2391 assert(Base32HexNoPad.decode(toR("CPNG")) == toA("fo")); 2392 assert(Base32HexNoPad.decode(toR("CPNMU")) == toA("foo")); 2393 assert(Base32HexNoPad.decode(toR("CPNMUOG")) == toA("foob")); 2394 assert(Base32HexNoPad.decode(toR("CPNMUOJ1")) == toA("fooba")); 2395 assert(Base32HexNoPad.decode(toR("CPNMUOJ1E8")) == toA("foobar")); 2396 2397 // Invalid source length 2398 assertThrown!Base32Exception(Base32.decode(toR("A"))); 2399 assertThrown!Base32Exception(Base32.decode(toR("AA"))); 2400 assertThrown!Base32Exception(Base32.decode(toR("AAA"))); 2401 assertThrown!Base32Exception(Base32.decode(toR("AAAA"))); 2402 assertThrown!Base32Exception(Base32.decode(toR("AAAAA"))); 2403 assertThrown!Base32Exception(Base32.decode(toR("AAAAAA"))); 2404 assertThrown!Base32Exception(Base32.decode(toR("AAAAAAA"))); 2405 2406 assertThrown!Base32Exception(Base32Hex.decode(toR("A"))); 2407 assertThrown!Base32Exception(Base32Hex.decode(toR("AA"))); 2408 assertThrown!Base32Exception(Base32Hex.decode(toR("AAA"))); 2409 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAA"))); 2410 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAAA"))); 2411 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAAAA"))); 2412 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAAAAA"))); 2413 2414 assertThrown!Base32Exception(Base32NoPad.decode(toR("A"))); 2415 assertThrown!Base32Exception(Base32NoPad.decode(toR("AAA"))); 2416 assertThrown!Base32Exception(Base32NoPad.decode(toR("AAAAAA"))); 2417 assertThrown!Base32Exception(Base32NoPad.decode(toR("AAAAAAAAA"))); 2418 2419 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("A"))); 2420 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AAA"))); 2421 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AAAAAA"))); 2422 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AAAAAAAAA"))); 2423 2424 // Invalid character 2425 assertThrown!Base32Exception(Base32.decode(toR("AA?AA?AA"))); 2426 assertThrown!Base32Exception(Base32Hex.decode(toR("AA?AA?AA"))); 2427 assertThrown!Base32Exception(Base32NoPad.decode(toR("AA?AA?AA"))); 2428 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AA?AA?AA"))); 2429 2430 // Invalid padding 2431 assertThrown!Base32Exception(Base32.decode(toR("=AAAAAAA"))); 2432 assertThrown!Base32Exception(Base32Hex.decode(toR("=AAAAAAA"))); 2433 assertThrown!Base32Exception(Base32NoPad.decode(toR("=AAAAAAA"))); 2434 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("=AAAAAAA"))); 2435 } 2436 2437 // Array to OutputRange 2438 { 2439 import std.array : appender; 2440 2441 auto app = appender!(ubyte[])(); 2442 2443 assert(Base32.decode("", app) == 0); 2444 assert(app.data == toA("")); 2445 app.clear(); 2446 assert(Base32.decode("MY======", app) == 1); 2447 assert(app.data == toA("f")); 2448 app.clear(); 2449 assert(Base32.decode("MZXQ====", app) == 2); 2450 assert(app.data == toA("fo")); 2451 app.clear(); 2452 assert(Base32.decode("MZXW6===", app) == 3); 2453 assert(app.data == toA("foo")); 2454 app.clear(); 2455 assert(Base32.decode("MZXW6YQ=", app) == 4); 2456 assert(app.data == toA("foob")); 2457 app.clear(); 2458 assert(Base32.decode("MZXW6YTB", app) == 5); 2459 assert(app.data == toA("fooba")); 2460 app.clear(); 2461 assert(Base32.decode("MZXW6YTBOI======", app) == 6); 2462 assert(app.data == toA("foobar")); 2463 app.clear(); 2464 2465 assert(Base32Hex.decode("", app) == 0); 2466 assert(app.data == toA("")); 2467 app.clear(); 2468 assert(Base32Hex.decode("CO======", app) == 1); 2469 assert(app.data == toA("f")); 2470 app.clear(); 2471 assert(Base32Hex.decode("CPNG====", app) == 2); 2472 assert(app.data == toA("fo")); 2473 app.clear(); 2474 assert(Base32Hex.decode("CPNMU===", app) == 3); 2475 assert(app.data == toA("foo")); 2476 app.clear(); 2477 assert(Base32Hex.decode("CPNMUOG=", app) == 4); 2478 assert(app.data == toA("foob")); 2479 app.clear(); 2480 assert(Base32Hex.decode("CPNMUOJ1", app) == 5); 2481 assert(app.data == toA("fooba")); 2482 app.clear(); 2483 assert(Base32Hex.decode("CPNMUOJ1E8======", app) == 6); 2484 assert(app.data == toA("foobar")); 2485 app.clear(); 2486 2487 assert(Base32NoPad.decode("", app) == 0); 2488 assert(app.data == toA("")); 2489 app.clear(); 2490 assert(Base32NoPad.decode("MY", app) == 1); 2491 assert(app.data == toA("f")); 2492 app.clear(); 2493 assert(Base32NoPad.decode("MZXQ", app) == 2); 2494 assert(app.data == toA("fo")); 2495 app.clear(); 2496 assert(Base32NoPad.decode("MZXW6", app) == 3); 2497 assert(app.data == toA("foo")); 2498 app.clear(); 2499 assert(Base32NoPad.decode("MZXW6YQ", app) == 4); 2500 assert(app.data == toA("foob")); 2501 app.clear(); 2502 assert(Base32NoPad.decode("MZXW6YTB", app) == 5); 2503 assert(app.data == toA("fooba")); 2504 app.clear(); 2505 assert(Base32NoPad.decode("MZXW6YTBOI", app) == 6); 2506 assert(app.data == toA("foobar")); 2507 app.clear(); 2508 2509 assert(Base32HexNoPad.decode("", app) == 0); 2510 assert(app.data == toA("")); 2511 app.clear(); 2512 assert(Base32HexNoPad.decode("CO", app) == 1); 2513 assert(app.data == toA("f")); 2514 app.clear(); 2515 assert(Base32HexNoPad.decode("CPNG", app) == 2); 2516 assert(app.data == toA("fo")); 2517 app.clear(); 2518 assert(Base32HexNoPad.decode("CPNMU", app) == 3); 2519 assert(app.data == toA("foo")); 2520 app.clear(); 2521 assert(Base32HexNoPad.decode("CPNMUOG", app) == 4); 2522 assert(app.data == toA("foob")); 2523 app.clear(); 2524 assert(Base32HexNoPad.decode("CPNMUOJ1", app) == 5); 2525 assert(app.data == toA("fooba")); 2526 app.clear(); 2527 assert(Base32HexNoPad.decode("CPNMUOJ1E8", app) == 6); 2528 assert(app.data == toA("foobar")); 2529 app.clear(); 2530 2531 // Invalid source length 2532 assertThrown!Base32Exception(Base32.decode("A", app)); 2533 app.clear(); 2534 assertThrown!Base32Exception(Base32.decode("AA", app)); 2535 app.clear(); 2536 assertThrown!Base32Exception(Base32.decode("AAA", app)); 2537 app.clear(); 2538 assertThrown!Base32Exception(Base32.decode("AAAA", app)); 2539 app.clear(); 2540 assertThrown!Base32Exception(Base32.decode("AAAAA", app)); 2541 app.clear(); 2542 assertThrown!Base32Exception(Base32.decode("AAAAAA", app)); 2543 app.clear(); 2544 assertThrown!Base32Exception(Base32.decode("AAAAAAA", app)); 2545 app.clear(); 2546 2547 assertThrown!Base32Exception(Base32Hex.decode("A", app)); 2548 app.clear(); 2549 assertThrown!Base32Exception(Base32Hex.decode("AA", app)); 2550 app.clear(); 2551 assertThrown!Base32Exception(Base32Hex.decode("AAA", app)); 2552 app.clear(); 2553 assertThrown!Base32Exception(Base32Hex.decode("AAAA", app)); 2554 app.clear(); 2555 assertThrown!Base32Exception(Base32Hex.decode("AAAAA", app)); 2556 app.clear(); 2557 assertThrown!Base32Exception(Base32Hex.decode("AAAAAA", app)); 2558 app.clear(); 2559 assertThrown!Base32Exception(Base32Hex.decode("AAAAAAA", app)); 2560 app.clear(); 2561 2562 assertThrown!Base32Exception(Base32NoPad.decode("A", app)); 2563 app.clear(); 2564 assertThrown!Base32Exception(Base32NoPad.decode("AAA", app)); 2565 app.clear(); 2566 assertThrown!Base32Exception(Base32NoPad.decode("AAAAAA", app)); 2567 app.clear(); 2568 assertThrown!Base32Exception(Base32NoPad.decode("AAAAAAAAA", app)); 2569 app.clear(); 2570 2571 assertThrown!Base32Exception(Base32HexNoPad.decode("A", app)); 2572 app.clear(); 2573 assertThrown!Base32Exception(Base32HexNoPad.decode("AAA", app)); 2574 app.clear(); 2575 assertThrown!Base32Exception(Base32HexNoPad.decode("AAAAAA", app)); 2576 app.clear(); 2577 assertThrown!Base32Exception(Base32HexNoPad.decode("AAAAAAAAA", app)); 2578 app.clear(); 2579 2580 // Invalid character 2581 assertThrown!Base32Exception(Base32.decode("AA?AA?AA", app)); 2582 app.clear(); 2583 assertThrown!Base32Exception(Base32Hex.decode("AA?AA?AA", app)); 2584 app.clear(); 2585 assertThrown!Base32Exception(Base32NoPad.decode("AA?AA?AA", app)); 2586 app.clear(); 2587 assertThrown!Base32Exception(Base32HexNoPad.decode("AA?AA?AA", app)); 2588 app.clear(); 2589 2590 // Invalid padding 2591 assertThrown!Base32Exception(Base32.decode("=AAAAAAA", app)); 2592 app.clear(); 2593 assertThrown!Base32Exception(Base32Hex.decode("=AAAAAAA", app)); 2594 app.clear(); 2595 assertThrown!Base32Exception(Base32NoPad.decode("=AAAAAAA", app)); 2596 app.clear(); 2597 assertThrown!Base32Exception(Base32HexNoPad.decode("=AAAAAAA", app)); 2598 app.clear(); 2599 } 2600 2601 // InputRange to OutputRange 2602 { 2603 import std.array : appender; 2604 2605 auto app = appender!(ubyte[])(); 2606 2607 assert(Base32.decode(toR(""), app) == 0); 2608 assert(app.data == toA("")); 2609 app.clear(); 2610 assert(Base32.decode(toR("MY======"), app) == 1); 2611 assert(app.data == toA("f")); 2612 app.clear(); 2613 assert(Base32.decode(toR("MZXQ===="), app) == 2); 2614 assert(app.data == toA("fo")); 2615 app.clear(); 2616 assert(Base32.decode(toR("MZXW6==="), app) == 3); 2617 assert(app.data == toA("foo")); 2618 app.clear(); 2619 assert(Base32.decode(toR("MZXW6YQ="), app) == 4); 2620 assert(app.data == toA("foob")); 2621 app.clear(); 2622 assert(Base32.decode(toR("MZXW6YTB"), app) == 5); 2623 assert(app.data == toA("fooba")); 2624 app.clear(); 2625 assert(Base32.decode(toR("MZXW6YTBOI======"), app) == 6); 2626 assert(app.data == toA("foobar")); 2627 app.clear(); 2628 2629 assert(Base32Hex.decode(toR(""), app) == 0); 2630 assert(app.data == toA("")); 2631 app.clear(); 2632 assert(Base32Hex.decode(toR("CO======"), app) == 1); 2633 assert(app.data == toA("f")); 2634 app.clear(); 2635 assert(Base32Hex.decode(toR("CPNG===="), app) == 2); 2636 assert(app.data == toA("fo")); 2637 app.clear(); 2638 assert(Base32Hex.decode(toR("CPNMU==="), app) == 3); 2639 assert(app.data == toA("foo")); 2640 app.clear(); 2641 assert(Base32Hex.decode(toR("CPNMUOG="), app) == 4); 2642 assert(app.data == toA("foob")); 2643 app.clear(); 2644 assert(Base32Hex.decode(toR("CPNMUOJ1"), app) == 5); 2645 assert(app.data == toA("fooba")); 2646 app.clear(); 2647 assert(Base32Hex.decode(toR("CPNMUOJ1E8======"), app) == 6); 2648 assert(app.data == toA("foobar")); 2649 app.clear(); 2650 2651 assert(Base32NoPad.decode(toR(""), app) == 0); 2652 assert(app.data == toA("")); 2653 app.clear(); 2654 assert(Base32NoPad.decode(toR("MY"), app) == 1); 2655 assert(app.data == toA("f")); 2656 app.clear(); 2657 assert(Base32NoPad.decode(toR("MZXQ"), app) == 2); 2658 assert(app.data == toA("fo")); 2659 app.clear(); 2660 assert(Base32NoPad.decode(toR("MZXW6"), app) == 3); 2661 assert(app.data == toA("foo")); 2662 app.clear(); 2663 assert(Base32NoPad.decode(toR("MZXW6YQ"), app) == 4); 2664 assert(app.data == toA("foob")); 2665 app.clear(); 2666 assert(Base32NoPad.decode(toR("MZXW6YTB"), app) == 5); 2667 assert(app.data == toA("fooba")); 2668 app.clear(); 2669 assert(Base32NoPad.decode(toR("MZXW6YTBOI"), app) == 6); 2670 assert(app.data == toA("foobar")); 2671 app.clear(); 2672 2673 assert(Base32HexNoPad.decode(toR(""), app) == 0); 2674 assert(app.data == toA("")); 2675 app.clear(); 2676 assert(Base32HexNoPad.decode(toR("CO"), app) == 1); 2677 assert(app.data == toA("f")); 2678 app.clear(); 2679 assert(Base32HexNoPad.decode(toR("CPNG"), app) == 2); 2680 assert(app.data == toA("fo")); 2681 app.clear(); 2682 assert(Base32HexNoPad.decode(toR("CPNMU"), app) == 3); 2683 assert(app.data == toA("foo")); 2684 app.clear(); 2685 assert(Base32HexNoPad.decode(toR("CPNMUOG"), app) == 4); 2686 assert(app.data == toA("foob")); 2687 app.clear(); 2688 assert(Base32HexNoPad.decode(toR("CPNMUOJ1"), app) == 5); 2689 assert(app.data == toA("fooba")); 2690 app.clear(); 2691 assert(Base32HexNoPad.decode(toR("CPNMUOJ1E8"), app) == 6); 2692 assert(app.data == toA("foobar")); 2693 app.clear(); 2694 2695 // Invalid source length 2696 assertThrown!Base32Exception(Base32.decode(toR("A"), app)); 2697 app.clear(); 2698 assertThrown!Base32Exception(Base32.decode(toR("AA"), app)); 2699 app.clear(); 2700 assertThrown!Base32Exception(Base32.decode(toR("AAA"), app)); 2701 app.clear(); 2702 assertThrown!Base32Exception(Base32.decode(toR("AAAA"), app)); 2703 app.clear(); 2704 assertThrown!Base32Exception(Base32.decode(toR("AAAAA"), app)); 2705 app.clear(); 2706 assertThrown!Base32Exception(Base32.decode(toR("AAAAAA"), app)); 2707 app.clear(); 2708 assertThrown!Base32Exception(Base32.decode(toR("AAAAAAA"), app)); 2709 app.clear(); 2710 2711 assertThrown!Base32Exception(Base32Hex.decode(toR("A"), app)); 2712 app.clear(); 2713 assertThrown!Base32Exception(Base32Hex.decode(toR("AA"), app)); 2714 app.clear(); 2715 assertThrown!Base32Exception(Base32Hex.decode(toR("AAA"), app)); 2716 app.clear(); 2717 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAA"), app)); 2718 app.clear(); 2719 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAAA"), app)); 2720 app.clear(); 2721 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAAAA"), app)); 2722 app.clear(); 2723 assertThrown!Base32Exception(Base32Hex.decode(toR("AAAAAAA"), app)); 2724 app.clear(); 2725 2726 assertThrown!Base32Exception(Base32NoPad.decode(toR("A"), app)); 2727 app.clear(); 2728 assertThrown!Base32Exception(Base32NoPad.decode(toR("AAA"), app)); 2729 app.clear(); 2730 assertThrown!Base32Exception(Base32NoPad.decode(toR("AAAAAA"), app)); 2731 app.clear(); 2732 assertThrown!Base32Exception(Base32NoPad.decode(toR("AAAAAAAAA"), app)); 2733 app.clear(); 2734 2735 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("A"), app)); 2736 app.clear(); 2737 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AAA"), app)); 2738 app.clear(); 2739 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AAAAAA"), app)); 2740 app.clear(); 2741 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AAAAAAAAA"), app)); 2742 app.clear(); 2743 2744 // Invalid character 2745 assertThrown!Base32Exception(Base32.decode(toR("AA?AA?AA"), app)); 2746 app.clear(); 2747 assertThrown!Base32Exception(Base32Hex.decode(toR("AA?AA?AA"), app)); 2748 app.clear(); 2749 assertThrown!Base32Exception(Base32NoPad.decode(toR("AA?AA?AA"), app)); 2750 app.clear(); 2751 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("AA?AA?AA"), app)); 2752 app.clear(); 2753 2754 // Invalid padding 2755 assertThrown!Base32Exception(Base32.decode(toR("=AAAAAAA"), app)); 2756 app.clear(); 2757 assertThrown!Base32Exception(Base32Hex.decode(toR("=AAAAAAA"), app)); 2758 app.clear(); 2759 assertThrown!Base32Exception(Base32NoPad.decode(toR("=AAAAAAA"), app)); 2760 app.clear(); 2761 assertThrown!Base32Exception(Base32HexNoPad.decode(toR("=AAAAAAA"), app)); 2762 app.clear(); 2763 } 2764 }