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