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 }