Fix build on sparc64-linux-gnu.
[official-gcc.git] / libphobos / src / std / base64.d
blob6211d10b72a5a5a94da34a321779fad51d139b0e
1 // Written in the D programming language.
3 /**
4 * Support for Base64 encoding and decoding.
6 * This module provides two default implementations of Base64 encoding,
7 * $(LREF Base64) with a standard encoding alphabet, and a variant
8 * $(LREF Base64URL) that has a modified encoding alphabet designed to be
9 * safe for embedding in URLs and filenames.
11 * Both variants are implemented as instantiations of the template
12 * $(LREF Base64Impl). Most users will not need to use this template
13 * directly; however, it can be used to create customized Base64 encodings,
14 * such as one that omits padding characters, or one that is safe to embed
15 * inside a regular expression.
17 * Example:
18 * -----
19 * ubyte[] data = [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e];
21 * const(char)[] encoded = Base64.encode(data);
22 * assert(encoded == "FPucA9l+");
24 * ubyte[] decoded = Base64.decode("FPucA9l+");
25 * assert(decoded == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]);
26 * -----
28 * The range API is supported for both encoding and decoding:
30 * Example:
31 * -----
32 * // Create MIME Base64 with CRLF, per line 76.
33 * File f = File("./text.txt", "r");
34 * scope(exit) f.close();
36 * Appender!string mime64 = appender!string;
38 * foreach (encoded; Base64.encoder(f.byChunk(57)))
39 * {
40 * mime64.put(encoded);
41 * mime64.put("\r\n");
42 * }
44 * writeln(mime64.data);
45 * -----
47 * References:
48 * $(LINK2 https://tools.ietf.org/html/rfc4648, RFC 4648 - The Base16, Base32, and Base64
49 * Data Encodings)
51 * Copyright: Masahiro Nakagawa 2010-.
52 * License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
53 * Authors: Masahiro Nakagawa, Daniel Murphy (Single value Encoder and Decoder)
54 * Source: $(PHOBOSSRC std/_base64.d)
55 * Macros:
56 * LREF2=<a href="#$1">$(D $2)</a>
58 module std.base64;
60 import std.exception; // enforce
61 import std.range.primitives; // isInputRange, isOutputRange, isForwardRange, ElementType, hasLength
62 import std.traits; // isArray
64 // Make sure module header code examples work correctly.
65 @safe unittest
67 ubyte[] data = [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e];
69 const(char)[] encoded = Base64.encode(data);
70 assert(encoded == "FPucA9l+");
72 ubyte[] decoded = Base64.decode("FPucA9l+");
73 assert(decoded == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]);
76 /**
77 * Implementation of standard _Base64 encoding.
79 * See $(LREF Base64Impl) for a description of available methods.
81 alias Base64 = Base64Impl!('+', '/');
83 ///
84 @safe unittest
86 ubyte[] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f];
87 assert(Base64.encode(data) == "g9cwegE/");
88 assert(Base64.decode("g9cwegE/") == data);
92 /**
93 * Variation of Base64 encoding that is safe for use in URLs and filenames.
95 * See $(LREF Base64Impl) for a description of available methods.
97 alias Base64URL = Base64Impl!('-', '_');
99 ///
100 @safe unittest
102 ubyte[] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f];
103 assert(Base64URL.encode(data) == "g9cwegE_");
104 assert(Base64URL.decode("g9cwegE_") == data);
108 * Unpadded variation of Base64 encoding that is safe for use in URLs and
109 * filenames, as used in RFCs 4648 and 7515 (JWS/JWT/JWE).
111 * See $(LREF Base64Impl) for a description of available methods.
113 alias Base64URLNoPadding = Base64Impl!('-', '_', Base64.NoPadding);
116 @safe unittest
118 ubyte[] data = [0x83, 0xd7, 0x30, 0x7b, 0xef];
119 assert(Base64URLNoPadding.encode(data) == "g9cwe-8");
120 assert(Base64URLNoPadding.decode("g9cwe-8") == data);
124 * Template for implementing Base64 encoding and decoding.
126 * For most purposes, direct usage of this template is not necessary; instead,
127 * this module provides default implementations: $(LREF Base64), implementing
128 * basic Base64 encoding, and $(LREF Base64URL) and $(LREF Base64URLNoPadding),
129 * that implement the Base64 variant for use in URLs and filenames, with
130 * and without padding, respectively.
132 * Customized Base64 encoding schemes can be implemented by instantiating this
133 * template with the appropriate arguments. For example:
135 * -----
136 * // Non-standard Base64 format for embedding in regular expressions.
137 * alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding);
138 * -----
140 * NOTE:
141 * Encoded strings will not have any padding if the $(D Padding) parameter is
142 * set to $(D NoPadding).
144 template Base64Impl(char Map62th, char Map63th, char Padding = '=')
146 enum NoPadding = '\0'; /// represents no-padding encoding
149 // Verify Base64 characters
150 static assert(Map62th < 'A' || Map62th > 'Z', "Character '" ~ Map62th ~ "' cannot be used twice");
151 static assert(Map63th < 'A' || Map63th > 'Z', "Character '" ~ Map63th ~ "' cannot be used twice");
152 static assert(Padding < 'A' || Padding > 'Z', "Character '" ~ Padding ~ "' cannot be used twice");
153 static assert(Map62th < 'a' || Map62th > 'z', "Character '" ~ Map62th ~ "' cannot be used twice");
154 static assert(Map63th < 'a' || Map63th > 'z', "Character '" ~ Map63th ~ "' cannot be used twice");
155 static assert(Padding < 'a' || Padding > 'z', "Character '" ~ Padding ~ "' cannot be used twice");
156 static assert(Map62th < '0' || Map62th > '9', "Character '" ~ Map62th ~ "' cannot be used twice");
157 static assert(Map63th < '0' || Map63th > '9', "Character '" ~ Map63th ~ "' cannot be used twice");
158 static assert(Padding < '0' || Padding > '9', "Character '" ~ Padding ~ "' cannot be used twice");
159 static assert(Map62th != Map63th, "Character '" ~ Map63th ~ "' cannot be used twice");
160 static assert(Map62th != Padding, "Character '" ~ Padding ~ "' cannot be used twice");
161 static assert(Map63th != Padding, "Character '" ~ Padding ~ "' cannot be used twice");
162 static assert(Map62th != NoPadding, "'\\0' is not a valid Base64character");
163 static assert(Map63th != NoPadding, "'\\0' is not a valid Base64character");
166 /* Encode functions */
169 private immutable EncodeMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ~ Map62th ~ Map63th;
173 * Calculates the length needed to store the encoded string corresponding
174 * to an input of the given length.
176 * Params:
177 * sourceLength = Length of the source array.
179 * Returns:
180 * The length of a Base64 encoding of an array of the given length.
182 @safe
183 pure nothrow size_t encodeLength(in size_t sourceLength)
185 static if (Padding == NoPadding)
186 return (sourceLength / 3) * 4 + (sourceLength % 3 == 0 ? 0 : sourceLength % 3 == 1 ? 2 : 3);
187 else
188 return (sourceLength / 3 + (sourceLength % 3 ? 1 : 0)) * 4;
192 @safe unittest
194 ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e];
196 // Allocate a buffer large enough to hold the encoded string.
197 auto buf = new char[Base64.encodeLength(data.length)];
199 Base64.encode(data, buf);
200 assert(buf == "Gis8TV1u");
204 // ubyte[] to char[]
208 * Encode $(D_PARAM source) into a $(D char[]) buffer using Base64
209 * encoding.
211 * Params:
212 * source = The $(LINK2 std_range_primitives.html#isInputRange, input
213 * range) to _encode.
214 * buffer = The $(D char[]) buffer to store the encoded result.
216 * Returns:
217 * The slice of $(D_PARAM buffer) that contains the encoded string.
219 @trusted
220 pure char[] encode(R1, R2)(in R1 source, R2 buffer) if (isArray!R1 && is(ElementType!R1 : ubyte) &&
221 is(R2 == char[]))
224 assert(buffer.length >= encodeLength(source.length), "Insufficient buffer for encoding");
226 out(result)
228 assert(result.length == encodeLength(source.length), "The length of result is different from Base64");
230 body
232 immutable srcLen = source.length;
233 if (srcLen == 0)
234 return [];
236 immutable blocks = srcLen / 3;
237 immutable remain = srcLen % 3;
238 auto bufptr = buffer.ptr;
239 auto srcptr = source.ptr;
241 foreach (Unused; 0 .. blocks)
243 immutable val = srcptr[0] << 16 | srcptr[1] << 8 | srcptr[2];
244 *bufptr++ = EncodeMap[val >> 18 ];
245 *bufptr++ = EncodeMap[val >> 12 & 0x3f];
246 *bufptr++ = EncodeMap[val >> 6 & 0x3f];
247 *bufptr++ = EncodeMap[val & 0x3f];
248 srcptr += 3;
251 if (remain)
253 immutable val = srcptr[0] << 16 | (remain == 2 ? srcptr[1] << 8 : 0);
254 *bufptr++ = EncodeMap[val >> 18 ];
255 *bufptr++ = EncodeMap[val >> 12 & 0x3f];
257 final switch (remain)
259 case 2:
260 *bufptr++ = EncodeMap[val >> 6 & 0x3f];
261 static if (Padding != NoPadding)
262 *bufptr++ = Padding;
263 break;
264 case 1:
265 static if (Padding != NoPadding)
267 *bufptr++ = Padding;
268 *bufptr++ = Padding;
270 break;
274 // encode method can't assume buffer length. So, slice needed.
275 return buffer[0 .. bufptr - buffer.ptr];
279 @safe unittest
281 ubyte[] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f];
282 char[32] buffer; // much bigger than necessary
284 // Just to be sure...
285 auto encodedLength = Base64.encodeLength(data.length);
286 assert(buffer.length >= encodedLength);
288 // encode() returns a slice to the provided buffer.
289 auto encoded = Base64.encode(data, buffer[]);
290 assert(encoded is buffer[0 .. encodedLength]);
291 assert(encoded == "g9cwegE/");
295 // InputRange to char[]
299 * ditto
301 char[] encode(R1, R2)(R1 source, R2 buffer) if (!isArray!R1 && isInputRange!R1 &&
302 is(ElementType!R1 : ubyte) && hasLength!R1 &&
303 is(R2 == char[]))
306 assert(buffer.length >= encodeLength(source.length), "Insufficient buffer for encoding");
308 out(result)
310 // @@@BUG@@@ D's DbC can't caputre an argument of function and store the result of precondition.
311 //assert(result.length == encodeLength(source.length), "The length of result is different from Base64");
313 body
315 immutable srcLen = source.length;
316 if (srcLen == 0)
317 return [];
319 immutable blocks = srcLen / 3;
320 immutable remain = srcLen % 3;
321 auto bufptr = buffer.ptr;
323 foreach (Unused; 0 .. blocks)
325 immutable v1 = source.front; source.popFront();
326 immutable v2 = source.front; source.popFront();
327 immutable v3 = source.front; source.popFront();
328 immutable val = v1 << 16 | v2 << 8 | v3;
329 *bufptr++ = EncodeMap[val >> 18 ];
330 *bufptr++ = EncodeMap[val >> 12 & 0x3f];
331 *bufptr++ = EncodeMap[val >> 6 & 0x3f];
332 *bufptr++ = EncodeMap[val & 0x3f];
335 if (remain)
337 size_t val = source.front << 16;
338 if (remain == 2)
340 source.popFront();
341 val |= source.front << 8;
344 *bufptr++ = EncodeMap[val >> 18 ];
345 *bufptr++ = EncodeMap[val >> 12 & 0x3f];
347 final switch (remain)
349 case 2:
350 *bufptr++ = EncodeMap[val >> 6 & 0x3f];
351 static if (Padding != NoPadding)
352 *bufptr++ = Padding;
353 break;
354 case 1:
355 static if (Padding != NoPadding)
357 *bufptr++ = Padding;
358 *bufptr++ = Padding;
360 break;
364 // @@@BUG@@@ Workaround for DbC problem. See comment on 'out'.
365 version (unittest)
366 assert(
367 bufptr - buffer.ptr == encodeLength(srcLen),
368 "The length of result is different from Base64"
371 // encode method can't assume buffer length. So, slice needed.
372 return buffer[0 .. bufptr - buffer.ptr];
376 // ubyte[] to OutputRange
380 * Encodes $(D_PARAM source) into an
381 * $(LINK2 std_range_primitives.html#isOutputRange, output range) using
382 * Base64 encoding.
384 * Params:
385 * source = The $(LINK2 std_range_primitives.html#isInputRange, input
386 * range) to _encode.
387 * range = The $(LINK2 std_range_primitives.html#isOutputRange, output
388 * range) to store the encoded result.
390 * Returns:
391 * The number of times the output range's $(D put) method was invoked.
393 size_t encode(R1, R2)(in R1 source, auto ref R2 range)
394 if (isArray!R1 && is(ElementType!R1 : ubyte) &&
395 !is(R2 == char[]) && isOutputRange!(R2, char))
396 out(result)
398 assert(result == encodeLength(source.length), "The number of put is different from the length of Base64");
400 body
402 immutable srcLen = source.length;
403 if (srcLen == 0)
404 return 0;
406 immutable blocks = srcLen / 3;
407 immutable remain = srcLen % 3;
408 auto srcptr = source.ptr;
409 size_t pcount;
411 foreach (Unused; 0 .. blocks)
413 immutable val = srcptr[0] << 16 | srcptr[1] << 8 | srcptr[2];
414 put(range, EncodeMap[val >> 18 ]);
415 put(range, EncodeMap[val >> 12 & 0x3f]);
416 put(range, EncodeMap[val >> 6 & 0x3f]);
417 put(range, EncodeMap[val & 0x3f]);
418 srcptr += 3;
419 pcount += 4;
422 if (remain)
424 immutable val = srcptr[0] << 16 | (remain == 2 ? srcptr[1] << 8 : 0);
425 put(range, EncodeMap[val >> 18 ]);
426 put(range, EncodeMap[val >> 12 & 0x3f]);
427 pcount += 2;
429 final switch (remain)
431 case 2:
432 put(range, EncodeMap[val >> 6 & 0x3f]);
433 pcount++;
435 static if (Padding != NoPadding)
437 put(range, Padding);
438 pcount++;
440 break;
441 case 1:
442 static if (Padding != NoPadding)
444 put(range, Padding);
445 put(range, Padding);
446 pcount += 2;
448 break;
452 return pcount;
456 @system unittest
458 // @system because encode for OutputRange is @system
459 struct OutputRange
461 char[] result;
462 void put(const(char) ch) @safe { result ~= ch; }
465 ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e];
467 // This overload of encode() returns the number of calls to the output
468 // range's put method.
469 OutputRange output;
470 assert(Base64.encode(data, output) == 8);
471 assert(output.result == "Gis8TV1u");
475 // InputRange to OutputRange
479 * ditto
481 size_t encode(R1, R2)(R1 source, auto ref R2 range)
482 if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : ubyte) &&
483 hasLength!R1 && !is(R2 == char[]) && isOutputRange!(R2, char))
484 out(result)
486 // @@@BUG@@@ Workaround for DbC problem.
487 //assert(result == encodeLength(source.length), "The number of put is different from the length of Base64");
489 body
491 immutable srcLen = source.length;
492 if (srcLen == 0)
493 return 0;
495 immutable blocks = srcLen / 3;
496 immutable remain = srcLen % 3;
497 size_t pcount;
499 foreach (Unused; 0 .. blocks)
501 immutable v1 = source.front; source.popFront();
502 immutable v2 = source.front; source.popFront();
503 immutable v3 = source.front; source.popFront();
504 immutable val = v1 << 16 | v2 << 8 | v3;
505 put(range, EncodeMap[val >> 18 ]);
506 put(range, EncodeMap[val >> 12 & 0x3f]);
507 put(range, EncodeMap[val >> 6 & 0x3f]);
508 put(range, EncodeMap[val & 0x3f]);
509 pcount += 4;
512 if (remain)
514 size_t val = source.front << 16;
515 if (remain == 2)
517 source.popFront();
518 val |= source.front << 8;
521 put(range, EncodeMap[val >> 18 ]);
522 put(range, EncodeMap[val >> 12 & 0x3f]);
523 pcount += 2;
525 final switch (remain)
527 case 2:
528 put(range, EncodeMap[val >> 6 & 0x3f]);
529 pcount++;
531 static if (Padding != NoPadding)
533 put(range, Padding);
534 pcount++;
536 break;
537 case 1:
538 static if (Padding != NoPadding)
540 put(range, Padding);
541 put(range, Padding);
542 pcount += 2;
544 break;
548 // @@@BUG@@@ Workaround for DbC problem.
549 version (unittest)
550 assert(
551 pcount == encodeLength(srcLen),
552 "The number of put is different from the length of Base64"
555 return pcount;
560 * Encodes $(D_PARAM source) to newly-allocated buffer.
562 * This convenience method alleviates the need to manually manage output
563 * buffers.
565 * Params:
566 * source = The $(LINK2 std_range_primitives.html#isInputRange, input
567 * range) to _encode.
569 * Returns:
570 * A newly-allocated $(D char[]) buffer containing the encoded string.
572 @safe
573 pure char[] encode(Range)(Range source) if (isArray!Range && is(ElementType!Range : ubyte))
575 return encode(source, new char[encodeLength(source.length)]);
579 @safe unittest
581 ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e];
582 assert(Base64.encode(data) == "Gis8TV1u");
587 * ditto
589 char[] encode(Range)(Range source) if (!isArray!Range && isInputRange!Range &&
590 is(ElementType!Range : ubyte) && hasLength!Range)
592 return encode(source, new char[encodeLength(source.length)]);
597 * An $(LINK2 std_range_primitives.html#isInputRange, input range) that
598 * iterates over the respective Base64 encodings of a range of data items.
600 * This range will be a $(LINK2 std_range_primitives.html#isForwardRange,
601 * forward range) if the underlying data source is at least a forward
602 * range.
604 * Note: This struct is not intended to be created in user code directly;
605 * use the $(LREF encoder) function instead.
607 struct Encoder(Range) if (isInputRange!Range && (is(ElementType!Range : const(ubyte)[]) ||
608 is(ElementType!Range : const(char)[])))
610 private:
611 Range range_;
612 char[] buffer_, encoded_;
615 public:
616 this(Range range)
618 range_ = range;
619 doEncoding();
624 * Returns:
625 * true if there is no more encoded data left.
627 @property @trusted
628 bool empty()
630 return range_.empty;
635 * Returns: The current chunk of encoded data.
637 @property @safe
638 nothrow char[] front()
640 return encoded_;
645 * Advance the range to the next chunk of encoded data.
647 * Throws:
648 * $(D Base64Exception) If invoked when
649 * $(LREF2 .Base64Impl.Encoder.empty, empty) returns $(D true).
651 void popFront()
653 enforce(!empty, new Base64Exception("Cannot call popFront on Encoder with no data remaining"));
655 range_.popFront();
658 * This check is very ugly. I think this is a Range's flaw.
659 * I very strongly want the Range guideline for unified implementation.
661 * In this case, Encoder becomes a beautiful implementation if 'front' performs Base64 encoding.
663 if (!empty)
664 doEncoding();
668 static if (isForwardRange!Range)
671 * Save the current iteration state of the range.
673 * This method is only available if the underlying range is a
674 * $(LINK2 std_range_primitives.html#isForwardRange, forward
675 * range).
677 * Returns:
678 * A copy of $(D this).
680 @property
681 typeof(this) save()
683 typeof(return) encoder;
685 encoder.range_ = range_.save;
686 encoder.buffer_ = buffer_.dup;
687 encoder.encoded_ = encoder.buffer_[0 .. encoded_.length];
689 return encoder;
694 private:
695 void doEncoding()
697 auto data = cast(const(ubyte)[])range_.front;
698 auto size = encodeLength(data.length);
699 if (size > buffer_.length)
700 buffer_.length = size;
702 encoded_ = encode(data, buffer_);
708 * An $(LINK2 std_range_primitives.html#isInputRange, input range) that
709 * iterates over the encoded bytes of the given source data.
711 * It will be a $(LINK2 std_range_primitives.html#isForwardRange, forward
712 * range) if the underlying data source is at least a forward range.
714 * Note: This struct is not intended to be created in user code directly;
715 * use the $(LREF encoder) function instead.
717 struct Encoder(Range) if (isInputRange!Range && is(ElementType!Range : ubyte))
719 private:
720 Range range_;
721 ubyte first;
722 int pos, padding;
725 public:
726 this(Range range)
728 range_ = range;
729 static if (isForwardRange!Range)
730 range_ = range_.save;
732 if (range_.empty)
733 pos = -1;
734 else
735 popFront();
740 * Returns:
741 * true if there are no more encoded characters to be iterated.
743 @property @safe
744 nothrow bool empty() const
746 static if (Padding == NoPadding)
747 return pos < 0;
748 else
749 return pos < 0 && !padding;
754 * Returns: The current encoded character.
756 @property @safe
757 nothrow ubyte front()
759 return first;
764 * Advance to the next encoded character.
766 * Throws:
767 * $(D Base64Exception) If invoked when $(LREF2 .Base64Impl.Encoder.empty.2,
768 * empty) returns $(D true).
770 void popFront()
772 enforce(!empty, new Base64Exception("Cannot call popFront on Encoder with no data remaining"));
774 static if (Padding != NoPadding)
775 if (padding)
777 first = Padding;
778 pos = -1;
779 padding--;
780 return;
783 if (range_.empty)
785 pos = -1;
786 return;
789 final switch (pos)
791 case 0:
792 first = EncodeMap[range_.front >> 2];
793 break;
794 case 1:
795 immutable t = (range_.front & 0b11) << 4;
796 range_.popFront();
798 if (range_.empty)
800 first = EncodeMap[t];
801 padding = 3;
803 else
805 first = EncodeMap[t | (range_.front >> 4)];
807 break;
808 case 2:
809 immutable t = (range_.front & 0b1111) << 2;
810 range_.popFront();
812 if (range_.empty)
814 first = EncodeMap[t];
815 padding = 2;
817 else
819 first = EncodeMap[t | (range_.front >> 6)];
821 break;
822 case 3:
823 first = EncodeMap[range_.front & 0b111111];
824 range_.popFront();
825 break;
828 ++pos %= 4;
832 static if (isForwardRange!Range)
835 * Save the current iteration state of the range.
837 * This method is only available if the underlying range is a
838 * $(LINK2 std_range_primitives.html#isForwardRange, forward
839 * range).
841 * Returns:
842 * A copy of $(D this).
844 @property
845 typeof(this) save()
847 auto encoder = this;
848 encoder.range_ = encoder.range_.save;
849 return encoder;
856 * Construct an $(D Encoder) that iterates over the Base64 encoding of the
857 * given $(LINK2 std_range_primitives.html#isInputRange, input range).
859 * Params:
860 * range = An $(LINK2 std_range_primitives.html#isInputRange, input
861 * range) over the data to be encoded.
863 * Returns:
864 * If $(D_PARAM range) is a range of bytes, an $(D Encoder) that iterates
865 * over the bytes of the corresponding Base64 encoding.
867 * If $(D_PARAM range) is a range of ranges of bytes, an $(D Encoder) that
868 * iterates over the Base64 encoded strings of each element of the range.
870 * In both cases, the returned $(D Encoder) will be a
871 * $(LINK2 std_range_primitives.html#isForwardRange, forward range) if the
872 * given $(D range) is at least a forward range, otherwise it will be only
873 * an input range.
875 * Example:
876 * This example encodes the input one line at a time.
877 * -----
878 * File f = File("text.txt", "r");
879 * scope(exit) f.close();
881 * uint line = 0;
882 * foreach (encoded; Base64.encoder(f.byLine()))
884 * writeln(++line, ". ", encoded);
886 * -----
888 * Example:
889 * This example encodes the input data one byte at a time.
890 * -----
891 * ubyte[] data = cast(ubyte[]) "0123456789";
893 * // The ElementType of data is not aggregation type
894 * foreach (encoded; Base64.encoder(data))
896 * writeln(encoded);
898 * -----
900 Encoder!(Range) encoder(Range)(Range range) if (isInputRange!Range)
902 return typeof(return)(range);
906 /* Decode functions */
909 private immutable int[char.max + 1] DecodeMap = [
910 'A':0b000000, 'B':0b000001, 'C':0b000010, 'D':0b000011, 'E':0b000100,
911 'F':0b000101, 'G':0b000110, 'H':0b000111, 'I':0b001000, 'J':0b001001,
912 'K':0b001010, 'L':0b001011, 'M':0b001100, 'N':0b001101, 'O':0b001110,
913 'P':0b001111, 'Q':0b010000, 'R':0b010001, 'S':0b010010, 'T':0b010011,
914 'U':0b010100, 'V':0b010101, 'W':0b010110, 'X':0b010111, 'Y':0b011000,
915 'Z':0b011001, 'a':0b011010, 'b':0b011011, 'c':0b011100, 'd':0b011101,
916 'e':0b011110, 'f':0b011111, 'g':0b100000, 'h':0b100001, 'i':0b100010,
917 'j':0b100011, 'k':0b100100, 'l':0b100101, 'm':0b100110, 'n':0b100111,
918 'o':0b101000, 'p':0b101001, 'q':0b101010, 'r':0b101011, 's':0b101100,
919 't':0b101101, 'u':0b101110, 'v':0b101111, 'w':0b110000, 'x':0b110001,
920 'y':0b110010, 'z':0b110011, '0':0b110100, '1':0b110101, '2':0b110110,
921 '3':0b110111, '4':0b111000, '5':0b111001, '6':0b111010, '7':0b111011,
922 '8':0b111100, '9':0b111101, Map62th:0b111110, Map63th:0b111111, Padding:-1
927 * Given a Base64 encoded string, calculates the length of the decoded
928 * string.
930 * Params:
931 * sourceLength = The length of the Base64 encoding.
933 * Returns:
934 * The length of the decoded string corresponding to a Base64 encoding of
935 * length $(D_PARAM sourceLength).
937 @safe
938 pure nothrow size_t decodeLength(in size_t sourceLength)
940 static if (Padding == NoPadding)
941 return (sourceLength / 4) * 3 + (sourceLength % 4 < 2 ? 0 : sourceLength % 4 == 2 ? 1 : 2);
942 else
943 return (sourceLength / 4) * 3;
947 @safe unittest
949 auto encoded = "Gis8TV1u";
951 // Allocate a sufficiently large buffer to hold to decoded result.
952 auto buffer = new ubyte[Base64.decodeLength(encoded.length)];
954 Base64.decode(encoded, buffer);
955 assert(buffer == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]);
959 // Used in decode contracts. Calculates the actual size the decoded
960 // result should have, taking into account trailing padding.
961 @safe
962 pure nothrow private size_t realDecodeLength(R)(R source)
964 auto expect = decodeLength(source.length);
965 static if (Padding != NoPadding)
967 if (source.length % 4 == 0)
969 expect -= source.length == 0 ? 0 :
970 source[$ - 2] == Padding ? 2 :
971 source[$ - 1] == Padding ? 1 : 0;
974 return expect;
978 // char[] to ubyte[]
982 * Decodes $(D_PARAM source) into the given buffer.
984 * Params:
985 * source = The $(LINK2 std_range_primitives.html#isInputRange, input
986 * range) to _decode.
987 * buffer = The buffer to store decoded result.
989 * Returns:
990 * The slice of $(D_PARAM buffer) containing the decoded result.
992 * Throws:
993 * $(D Base64Exception) if $(D_PARAM source) contains characters outside the
994 * base alphabet of the current Base64 encoding scheme.
996 @trusted
997 pure ubyte[] decode(R1, R2)(in R1 source, R2 buffer) if (isArray!R1 && is(ElementType!R1 : dchar) &&
998 is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
1001 assert(buffer.length >= realDecodeLength(source), "Insufficient buffer for decoding");
1003 out(result)
1005 immutable expect = realDecodeLength(source);
1006 assert(result.length == expect, "The length of result is different from the expected length");
1008 body
1010 immutable srcLen = source.length;
1011 if (srcLen == 0)
1012 return [];
1013 static if (Padding != NoPadding)
1014 enforce(srcLen % 4 == 0, new Base64Exception("Invalid length of encoded data"));
1016 immutable blocks = srcLen / 4;
1017 auto srcptr = source.ptr;
1018 auto bufptr = buffer.ptr;
1020 foreach (Unused; 0 .. blocks)
1022 immutable v1 = decodeChar(*srcptr++);
1023 immutable v2 = decodeChar(*srcptr++);
1025 *bufptr++ = cast(ubyte)(v1 << 2 | v2 >> 4);
1027 immutable v3 = decodeChar(*srcptr++);
1028 if (v3 == -1)
1029 break;
1031 *bufptr++ = cast(ubyte)((v2 << 4 | v3 >> 2) & 0xff);
1033 immutable v4 = decodeChar(*srcptr++);
1034 if (v4 == -1)
1035 break;
1037 *bufptr++ = cast(ubyte)((v3 << 6 | v4) & 0xff);
1040 static if (Padding == NoPadding)
1042 immutable remain = srcLen % 4;
1044 if (remain)
1046 immutable v1 = decodeChar(*srcptr++);
1047 immutable v2 = decodeChar(*srcptr++);
1049 *bufptr++ = cast(ubyte)(v1 << 2 | v2 >> 4);
1051 if (remain == 3)
1052 *bufptr++ = cast(ubyte)((v2 << 4 | decodeChar(*srcptr++) >> 2) & 0xff);
1056 return buffer[0 .. bufptr - buffer.ptr];
1060 @safe unittest
1062 auto encoded = "Gis8TV1u";
1063 ubyte[32] buffer; // much bigger than necessary
1065 // Just to be sure...
1066 auto decodedLength = Base64.decodeLength(encoded.length);
1067 assert(buffer.length >= decodedLength);
1069 // decode() returns a slice of the given buffer.
1070 auto decoded = Base64.decode(encoded, buffer[]);
1071 assert(decoded is buffer[0 .. decodedLength]);
1072 assert(decoded == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]);
1075 // InputRange to ubyte[]
1079 * ditto
1081 ubyte[] decode(R1, R2)(R1 source, R2 buffer) if (!isArray!R1 && isInputRange!R1 &&
1082 is(ElementType!R1 : dchar) && hasLength!R1 &&
1083 is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
1086 assert(buffer.length >= decodeLength(source.length), "Insufficient buffer for decoding");
1088 out(result)
1090 // @@@BUG@@@ Workaround for DbC problem.
1091 //immutable expect = decodeLength(source.length) - 2;
1092 //assert(result.length >= expect, "The length of result is smaller than expected length");
1094 body
1096 immutable srcLen = source.length;
1097 if (srcLen == 0)
1098 return [];
1099 static if (Padding != NoPadding)
1100 enforce(srcLen % 4 == 0, new Base64Exception("Invalid length of encoded data"));
1102 immutable blocks = srcLen / 4;
1103 auto bufptr = buffer.ptr;
1105 foreach (Unused; 0 .. blocks)
1107 immutable v1 = decodeChar(source.front); source.popFront();
1108 immutable v2 = decodeChar(source.front); source.popFront();
1110 *bufptr++ = cast(ubyte)(v1 << 2 | v2 >> 4);
1112 immutable v3 = decodeChar(source.front);
1113 if (v3 == -1)
1114 break;
1116 *bufptr++ = cast(ubyte)((v2 << 4 | v3 >> 2) & 0xff);
1117 source.popFront();
1119 immutable v4 = decodeChar(source.front);
1120 if (v4 == -1)
1121 break;
1123 *bufptr++ = cast(ubyte)((v3 << 6 | v4) & 0xff);
1124 source.popFront();
1127 static if (Padding == NoPadding)
1129 immutable remain = srcLen % 4;
1131 if (remain)
1133 immutable v1 = decodeChar(source.front); source.popFront();
1134 immutable v2 = decodeChar(source.front);
1136 *bufptr++ = cast(ubyte)(v1 << 2 | v2 >> 4);
1138 if (remain == 3)
1140 source.popFront();
1141 *bufptr++ = cast(ubyte)((v2 << 4 | decodeChar(source.front) >> 2) & 0xff);
1146 // @@@BUG@@@ Workaround for DbC problem.
1147 version (unittest)
1148 assert(
1149 (bufptr - buffer.ptr) >= (decodeLength(srcLen) - 2),
1150 "The length of result is smaller than expected length"
1153 return buffer[0 .. bufptr - buffer.ptr];
1157 // char[] to OutputRange
1161 * Decodes $(D_PARAM source) into a given
1162 * $(LINK2 std_range_primitives.html#isOutputRange, output range).
1164 * Params:
1165 * source = The $(LINK2 std_range_primitives.html#isInputRange, input
1166 * range) to _decode.
1167 * range = The $(LINK2 std_range_primitives.html#isOutputRange, output
1168 * range) to store the decoded result.
1170 * Returns:
1171 * The number of times the output range's $(D put) method was invoked.
1173 * Throws:
1174 * $(D Base64Exception) if $(D_PARAM source) contains characters outside the
1175 * base alphabet of the current Base64 encoding scheme.
1177 size_t decode(R1, R2)(in R1 source, auto ref R2 range)
1178 if (isArray!R1 && is(ElementType!R1 : dchar) &&
1179 !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
1180 out(result)
1182 immutable expect = realDecodeLength(source);
1183 assert(result == expect, "The result of decode is different from the expected");
1185 body
1187 immutable srcLen = source.length;
1188 if (srcLen == 0)
1189 return 0;
1190 static if (Padding != NoPadding)
1191 enforce(srcLen % 4 == 0, new Base64Exception("Invalid length of encoded data"));
1193 immutable blocks = srcLen / 4;
1194 auto srcptr = source.ptr;
1195 size_t pcount;
1197 foreach (Unused; 0 .. blocks)
1199 immutable v1 = decodeChar(*srcptr++);
1200 immutable v2 = decodeChar(*srcptr++);
1202 put(range, cast(ubyte)(v1 << 2 | v2 >> 4));
1203 pcount++;
1205 immutable v3 = decodeChar(*srcptr++);
1206 if (v3 == -1)
1207 break;
1209 put(range, cast(ubyte)((v2 << 4 | v3 >> 2) & 0xff));
1210 pcount++;
1212 immutable v4 = decodeChar(*srcptr++);
1213 if (v4 == -1)
1214 break;
1216 put(range, cast(ubyte)((v3 << 6 | v4) & 0xff));
1217 pcount++;
1220 static if (Padding == NoPadding)
1222 immutable remain = srcLen % 4;
1224 if (remain)
1226 immutable v1 = decodeChar(*srcptr++);
1227 immutable v2 = decodeChar(*srcptr++);
1229 put(range, cast(ubyte)(v1 << 2 | v2 >> 4));
1230 pcount++;
1232 if (remain == 3)
1234 put(range, cast(ubyte)((v2 << 4 | decodeChar(*srcptr++) >> 2) & 0xff));
1235 pcount++;
1240 return pcount;
1244 @system unittest
1246 struct OutputRange
1248 ubyte[] result;
1249 void put(ubyte b) { result ~= b; }
1251 OutputRange output;
1253 // This overload of decode() returns the number of calls to put().
1254 assert(Base64.decode("Gis8TV1u", output) == 6);
1255 assert(output.result == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]);
1259 // InputRange to OutputRange
1263 * ditto
1265 size_t decode(R1, R2)(R1 source, auto ref R2 range)
1266 if (!isArray!R1 && isInputRange!R1 && is(ElementType!R1 : dchar) &&
1267 hasLength!R1 && !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
1268 out(result)
1270 // @@@BUG@@@ Workaround for DbC problem.
1271 //immutable expect = decodeLength(source.length) - 2;
1272 //assert(result >= expect, "The length of result is smaller than expected length");
1274 body
1276 immutable srcLen = source.length;
1277 if (srcLen == 0)
1278 return 0;
1279 static if (Padding != NoPadding)
1280 enforce(srcLen % 4 == 0, new Base64Exception("Invalid length of encoded data"));
1282 immutable blocks = srcLen / 4;
1283 size_t pcount;
1285 foreach (Unused; 0 .. blocks)
1287 immutable v1 = decodeChar(source.front); source.popFront();
1288 immutable v2 = decodeChar(source.front); source.popFront();
1290 put(range, cast(ubyte)(v1 << 2 | v2 >> 4));
1291 pcount++;
1293 immutable v3 = decodeChar(source.front);
1294 if (v3 == -1)
1295 break;
1297 put(range, cast(ubyte)((v2 << 4 | v3 >> 2) & 0xff));
1298 source.popFront();
1299 pcount++;
1301 immutable v4 = decodeChar(source.front);
1302 if (v4 == -1)
1303 break;
1305 put(range, cast(ubyte)((v3 << 6 | v4) & 0xff));
1306 source.popFront();
1307 pcount++;
1310 static if (Padding == NoPadding)
1312 immutable remain = srcLen % 4;
1314 if (remain)
1316 immutable v1 = decodeChar(source.front); source.popFront();
1317 immutable v2 = decodeChar(source.front);
1319 put(range, cast(ubyte)(v1 << 2 | v2 >> 4));
1320 pcount++;
1322 if (remain == 3)
1324 source.popFront();
1325 put(range, cast(ubyte)((v2 << 4 | decodeChar(source.front) >> 2) & 0xff));
1326 pcount++;
1331 // @@@BUG@@@ Workaround for DbC problem.
1332 version (unittest)
1333 assert(
1334 pcount >= (decodeLength(srcLen) - 2),
1335 "The length of result is smaller than expected length"
1338 return pcount;
1343 * Decodes $(D_PARAM source) into newly-allocated buffer.
1345 * This convenience method alleviates the need to manually manage decoding
1346 * buffers.
1348 * Params:
1349 * source = The $(LINK2 std_range_primitives.html#isInputRange, input
1350 * range) to _decode.
1352 * Returns:
1353 * A newly-allocated $(D ubyte[]) buffer containing the decoded string.
1355 @safe
1356 pure ubyte[] decode(Range)(Range source) if (isArray!Range && is(ElementType!Range : dchar))
1358 return decode(source, new ubyte[decodeLength(source.length)]);
1362 @safe unittest
1364 auto data = "Gis8TV1u";
1365 assert(Base64.decode(data) == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]);
1370 * ditto
1372 ubyte[] decode(Range)(Range source) if (!isArray!Range && isInputRange!Range &&
1373 is(ElementType!Range : dchar) && hasLength!Range)
1375 return decode(source, new ubyte[decodeLength(source.length)]);
1380 * An $(LINK2 std_range_primitives.html#isInputRange, input range) that
1381 * iterates over the decoded data of a range of Base64 encodings.
1383 * This range will be a $(LINK2 std_range_primitives.html#isForwardRange,
1384 * forward range) if the underlying data source is at least a forward
1385 * range.
1387 * Note: This struct is not intended to be created in user code directly;
1388 * use the $(LREF decoder) function instead.
1390 struct Decoder(Range) if (isInputRange!Range && (is(ElementType!Range : const(char)[]) ||
1391 is(ElementType!Range : const(ubyte)[])))
1393 private:
1394 Range range_;
1395 ubyte[] buffer_, decoded_;
1398 public:
1399 this(Range range)
1401 range_ = range;
1402 doDecoding();
1407 * Returns:
1408 * true if there are no more elements to be iterated.
1410 @property @trusted
1411 bool empty()
1413 return range_.empty;
1418 * Returns: The decoding of the current element in the input.
1420 @property @safe
1421 nothrow ubyte[] front()
1423 return decoded_;
1428 * Advance to the next element in the input to be decoded.
1430 * Throws:
1431 * $(D Base64Exception) if invoked when $(LREF2 .Base64Impl.Decoder.empty,
1432 * empty) returns $(D true).
1434 void popFront()
1436 enforce(!empty, new Base64Exception("Cannot call popFront on Decoder with no data remaining."));
1438 range_.popFront();
1441 * I mentioned Encoder's popFront.
1443 if (!empty)
1444 doDecoding();
1448 static if (isForwardRange!Range)
1451 * Saves the current iteration state.
1453 * This method is only available if the underlying range is a
1454 * $(LINK2 std_range_primitives.html#isForwardRange, forward
1455 * range).
1457 * Returns: A copy of $(D this).
1459 @property
1460 typeof(this) save()
1462 typeof(return) decoder;
1464 decoder.range_ = range_.save;
1465 decoder.buffer_ = buffer_.dup;
1466 decoder.decoded_ = decoder.buffer_[0 .. decoded_.length];
1468 return decoder;
1473 private:
1474 void doDecoding()
1476 auto data = cast(const(char)[])range_.front;
1478 static if (Padding == NoPadding)
1480 while (data.length % 4 == 1)
1482 range_.popFront();
1483 data ~= cast(const(char)[])range_.front;
1486 else
1488 while (data.length % 4 != 0)
1490 range_.popFront();
1491 data ~= cast(const(char)[])range_.front;
1495 auto size = decodeLength(data.length);
1496 if (size > buffer_.length)
1497 buffer_.length = size;
1499 decoded_ = decode(data, buffer_);
1505 * An $(LINK2 std_range_primitives.html#isInputRange, input range) that
1506 * iterates over the bytes of data decoded from a Base64 encoded string.
1508 * This range will be a $(LINK2 std_range_primitives.html#isForwardRange,
1509 * forward range) if the underlying data source is at least a forward
1510 * range.
1512 * Note: This struct is not intended to be created in user code directly;
1513 * use the $(LREF decoder) function instead.
1515 struct Decoder(Range) if (isInputRange!Range && is(ElementType!Range : char))
1517 private:
1518 Range range_;
1519 ubyte first;
1520 int pos;
1523 public:
1524 this(Range range)
1526 range_ = range;
1527 static if (isForwardRange!Range)
1528 range_ = range_.save;
1530 static if (Padding != NoPadding && hasLength!Range)
1531 enforce(range_.length % 4 == 0, new Base64Exception("Invalid length of encoded data"));
1533 if (range_.empty)
1534 pos = -1;
1535 else
1536 popFront();
1541 * Returns:
1542 * true if there are no more elements to be iterated.
1544 @property @safe
1545 nothrow bool empty() const
1547 return pos < 0;
1552 * Returns: The current decoded byte.
1554 @property @safe
1555 nothrow ubyte front()
1557 return first;
1562 * Advance to the next decoded byte.
1564 * Throws:
1565 * $(D Base64Exception) if invoked when $(LREF2 .Base64Impl.Decoder.empty,
1566 * empty) returns $(D true).
1568 void popFront()
1570 enforce(!empty, new Base64Exception("Cannot call popFront on Decoder with no data remaining"));
1572 static if (Padding == NoPadding)
1574 bool endCondition()
1576 return range_.empty;
1579 else
1581 bool endCondition()
1583 enforce(!range_.empty, new Base64Exception("Missing padding"));
1584 return range_.front == Padding;
1588 if (range_.empty || range_.front == Padding)
1590 pos = -1;
1591 return;
1594 final switch (pos)
1596 case 0:
1597 enforce(!endCondition(), new Base64Exception("Premature end of data found"));
1599 immutable t = DecodeMap[range_.front] << 2;
1600 range_.popFront();
1602 enforce(!endCondition(), new Base64Exception("Premature end of data found"));
1603 first = cast(ubyte)(t | (DecodeMap[range_.front] >> 4));
1604 break;
1605 case 1:
1606 immutable t = (DecodeMap[range_.front] & 0b1111) << 4;
1607 range_.popFront();
1609 if (endCondition())
1611 pos = -1;
1612 return;
1614 else
1616 first = cast(ubyte)(t | (DecodeMap[range_.front] >> 2));
1618 break;
1619 case 2:
1620 immutable t = (DecodeMap[range_.front] & 0b11) << 6;
1621 range_.popFront();
1623 if (endCondition())
1625 pos = -1;
1626 return;
1628 else
1630 first = cast(ubyte)(t | DecodeMap[range_.front]);
1633 range_.popFront();
1634 break;
1637 ++pos %= 3;
1641 static if (isForwardRange!Range)
1644 * Saves the current iteration state.
1646 * This method is only available if the underlying range is a
1647 * $(LINK2 std_range_primitives.html#isForwardRange, forward
1648 * range).
1650 * Returns: A copy of $(D this).
1652 @property
1653 typeof(this) save()
1655 auto decoder = this;
1656 decoder.range_ = decoder.range_.save;
1657 return decoder;
1664 * Construct a $(D Decoder) that iterates over the decoding of the given
1665 * Base64 encoded data.
1667 * Params:
1668 * range = An $(LINK2 std_range_primitives.html#isInputRange, input
1669 * range) over the data to be decoded.
1671 * Returns:
1672 * If $(D_PARAM range) is a range of characters, a $(D Decoder) that
1673 * iterates over the bytes of the corresponding Base64 decoding.
1675 * If $(D_PARAM range) is a range of ranges of characters, a $(D Decoder)
1676 * that iterates over the decoded strings corresponding to each element of
1677 * the range. In this case, the length of each subrange must be a multiple
1678 * of 4; the returned _decoder does not keep track of Base64 decoding
1679 * state across subrange boundaries.
1681 * In both cases, the returned $(D Decoder) will be a
1682 * $(LINK2 std_range_primitives.html#isForwardRange, forward range) if the
1683 * given $(D range) is at least a forward range, otherwise it will be only
1684 * an input range.
1686 * If the input data contains characters not found in the base alphabet of
1687 * the current Base64 encoding scheme, the returned range may throw a
1688 * $(D Base64Exception).
1690 * Example:
1691 * This example shows decoding over a range of input data lines.
1692 * -----
1693 * foreach (decoded; Base64.decoder(stdin.byLine()))
1695 * writeln(decoded);
1697 * -----
1699 * Example:
1700 * This example shows decoding one byte at a time.
1701 * -----
1702 * auto encoded = Base64.encoder(cast(ubyte[])"0123456789");
1703 * foreach (n; map!q{a - '0'}(Base64.decoder(encoded)))
1705 * writeln(n);
1707 * -----
1709 Decoder!(Range) decoder(Range)(Range range) if (isInputRange!Range)
1711 return typeof(return)(range);
1715 private:
1716 @safe
1717 pure int decodeChar()(char chr)
1719 immutable val = DecodeMap[chr];
1721 // enforce can't be a pure function, so I use trivial check.
1722 if (val == 0 && chr != 'A')
1723 throw new Base64Exception("Invalid character: " ~ chr);
1725 return val;
1729 @safe
1730 pure int decodeChar()(dchar chr)
1732 // See above comment.
1733 if (chr > 0x7f)
1734 throw new Base64Exception("Base64-encoded character must be a single byte");
1736 return decodeChar(cast(char) chr);
1741 @safe unittest
1743 import std.string : representation;
1745 // pre-defined: alias Base64 = Base64Impl!('+', '/');
1746 ubyte[] emptyArr;
1747 assert(Base64.encode(emptyArr) == "");
1748 assert(Base64.encode("f".representation) == "Zg==");
1749 assert(Base64.encode("foo".representation) == "Zm9v");
1751 alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding);
1752 assert(Base64Re.encode("f".representation) == "Zg");
1753 assert(Base64Re.encode("foo".representation) == "Zm9v");
1757 * Exception thrown upon encountering Base64 encoding or decoding errors.
1759 class Base64Exception : Exception
1761 @safe pure nothrow
1762 this(string s, string fn = __FILE__, size_t ln = __LINE__)
1764 super(s, fn, ln);
1769 @system unittest
1771 import std.exception : assertThrown;
1772 assertThrown!Base64Exception(Base64.decode("ab|c"));
1776 @system unittest
1778 import std.algorithm.comparison : equal;
1779 import std.algorithm.sorting : sort;
1780 import std.conv;
1781 import std.file;
1782 import std.stdio;
1784 alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding);
1786 // Test vectors from RFC 4648
1787 ubyte[][string] tv = [
1788 "" :cast(ubyte[])"",
1789 "f" :cast(ubyte[])"f",
1790 "fo" :cast(ubyte[])"fo",
1791 "foo" :cast(ubyte[])"foo",
1792 "foob" :cast(ubyte[])"foob",
1793 "fooba" :cast(ubyte[])"fooba",
1794 "foobar":cast(ubyte[])"foobar"
1797 { // Base64
1798 // encode
1799 assert(Base64.encodeLength(tv[""].length) == 0);
1800 assert(Base64.encodeLength(tv["f"].length) == 4);
1801 assert(Base64.encodeLength(tv["fo"].length) == 4);
1802 assert(Base64.encodeLength(tv["foo"].length) == 4);
1803 assert(Base64.encodeLength(tv["foob"].length) == 8);
1804 assert(Base64.encodeLength(tv["fooba"].length) == 8);
1805 assert(Base64.encodeLength(tv["foobar"].length) == 8);
1807 assert(Base64.encode(tv[""]) == "");
1808 assert(Base64.encode(tv["f"]) == "Zg==");
1809 assert(Base64.encode(tv["fo"]) == "Zm8=");
1810 assert(Base64.encode(tv["foo"]) == "Zm9v");
1811 assert(Base64.encode(tv["foob"]) == "Zm9vYg==");
1812 assert(Base64.encode(tv["fooba"]) == "Zm9vYmE=");
1813 assert(Base64.encode(tv["foobar"]) == "Zm9vYmFy");
1815 // decode
1816 assert(Base64.decodeLength(Base64.encode(tv[""]).length) == 0);
1817 assert(Base64.decodeLength(Base64.encode(tv["f"]).length) == 3);
1818 assert(Base64.decodeLength(Base64.encode(tv["fo"]).length) == 3);
1819 assert(Base64.decodeLength(Base64.encode(tv["foo"]).length) == 3);
1820 assert(Base64.decodeLength(Base64.encode(tv["foob"]).length) == 6);
1821 assert(Base64.decodeLength(Base64.encode(tv["fooba"]).length) == 6);
1822 assert(Base64.decodeLength(Base64.encode(tv["foobar"]).length) == 6);
1824 assert(Base64.decode(Base64.encode(tv[""])) == tv[""]);
1825 assert(Base64.decode(Base64.encode(tv["f"])) == tv["f"]);
1826 assert(Base64.decode(Base64.encode(tv["fo"])) == tv["fo"]);
1827 assert(Base64.decode(Base64.encode(tv["foo"])) == tv["foo"]);
1828 assert(Base64.decode(Base64.encode(tv["foob"])) == tv["foob"]);
1829 assert(Base64.decode(Base64.encode(tv["fooba"])) == tv["fooba"]);
1830 assert(Base64.decode(Base64.encode(tv["foobar"])) == tv["foobar"]);
1832 assertThrown!Base64Exception(Base64.decode("ab|c"));
1834 // Test decoding incomplete strings. RFC does not specify the correct
1835 // behavior, but the code should never throw Errors on invalid input.
1837 // decodeLength is nothrow
1838 assert(Base64.decodeLength(1) == 0);
1839 assert(Base64.decodeLength(2) <= 1);
1840 assert(Base64.decodeLength(3) <= 2);
1842 // may throw Exceptions, may not throw Errors
1843 assertThrown!Base64Exception(Base64.decode("Zg"));
1844 assertThrown!Base64Exception(Base64.decode("Zg="));
1845 assertThrown!Base64Exception(Base64.decode("Zm8"));
1846 assertThrown!Base64Exception(Base64.decode("Zg==;"));
1849 { // No padding
1850 // encode
1851 assert(Base64Re.encodeLength(tv[""].length) == 0);
1852 assert(Base64Re.encodeLength(tv["f"].length) == 2);
1853 assert(Base64Re.encodeLength(tv["fo"].length) == 3);
1854 assert(Base64Re.encodeLength(tv["foo"].length) == 4);
1855 assert(Base64Re.encodeLength(tv["foob"].length) == 6);
1856 assert(Base64Re.encodeLength(tv["fooba"].length) == 7);
1857 assert(Base64Re.encodeLength(tv["foobar"].length) == 8);
1859 assert(Base64Re.encode(tv[""]) == "");
1860 assert(Base64Re.encode(tv["f"]) == "Zg");
1861 assert(Base64Re.encode(tv["fo"]) == "Zm8");
1862 assert(Base64Re.encode(tv["foo"]) == "Zm9v");
1863 assert(Base64Re.encode(tv["foob"]) == "Zm9vYg");
1864 assert(Base64Re.encode(tv["fooba"]) == "Zm9vYmE");
1865 assert(Base64Re.encode(tv["foobar"]) == "Zm9vYmFy");
1867 // decode
1868 assert(Base64Re.decodeLength(Base64Re.encode(tv[""]).length) == 0);
1869 assert(Base64Re.decodeLength(Base64Re.encode(tv["f"]).length) == 1);
1870 assert(Base64Re.decodeLength(Base64Re.encode(tv["fo"]).length) == 2);
1871 assert(Base64Re.decodeLength(Base64Re.encode(tv["foo"]).length) == 3);
1872 assert(Base64Re.decodeLength(Base64Re.encode(tv["foob"]).length) == 4);
1873 assert(Base64Re.decodeLength(Base64Re.encode(tv["fooba"]).length) == 5);
1874 assert(Base64Re.decodeLength(Base64Re.encode(tv["foobar"]).length) == 6);
1876 assert(Base64Re.decode(Base64Re.encode(tv[""])) == tv[""]);
1877 assert(Base64Re.decode(Base64Re.encode(tv["f"])) == tv["f"]);
1878 assert(Base64Re.decode(Base64Re.encode(tv["fo"])) == tv["fo"]);
1879 assert(Base64Re.decode(Base64Re.encode(tv["foo"])) == tv["foo"]);
1880 assert(Base64Re.decode(Base64Re.encode(tv["foob"])) == tv["foob"]);
1881 assert(Base64Re.decode(Base64Re.encode(tv["fooba"])) == tv["fooba"]);
1882 assert(Base64Re.decode(Base64Re.encode(tv["foobar"])) == tv["foobar"]);
1884 // decodeLength is nothrow
1885 assert(Base64.decodeLength(1) == 0);
1888 { // with OutputRange
1889 import std.array;
1891 auto a = Appender!(char[])([]);
1892 auto b = Appender!(ubyte[])([]);
1894 assert(Base64.encode(tv[""], a) == 0);
1895 assert(Base64.decode(a.data, b) == 0);
1896 assert(tv[""] == b.data); a.clear(); b.clear();
1898 assert(Base64.encode(tv["f"], a) == 4);
1899 assert(Base64.decode(a.data, b) == 1);
1900 assert(tv["f"] == b.data); a.clear(); b.clear();
1902 assert(Base64.encode(tv["fo"], a) == 4);
1903 assert(Base64.decode(a.data, b) == 2);
1904 assert(tv["fo"] == b.data); a.clear(); b.clear();
1906 assert(Base64.encode(tv["foo"], a) == 4);
1907 assert(Base64.decode(a.data, b) == 3);
1908 assert(tv["foo"] == b.data); a.clear(); b.clear();
1910 assert(Base64.encode(tv["foob"], a) == 8);
1911 assert(Base64.decode(a.data, b) == 4);
1912 assert(tv["foob"] == b.data); a.clear(); b.clear();
1914 assert(Base64.encode(tv["fooba"], a) == 8);
1915 assert(Base64.decode(a.data, b) == 5);
1916 assert(tv["fooba"] == b.data); a.clear(); b.clear();
1918 assert(Base64.encode(tv["foobar"], a) == 8);
1919 assert(Base64.decode(a.data, b) == 6);
1920 assert(tv["foobar"] == b.data); a.clear(); b.clear();
1923 // @@@9543@@@ These tests were disabled because they actually relied on the input range having length.
1924 // The implementation (currently) doesn't support encoding/decoding from a length-less source.
1925 version (none)
1926 { // with InputRange
1927 // InputRange to ubyte[] or char[]
1928 auto encoded = Base64.encode(map!(to!(ubyte))(["20", "251", "156", "3", "217", "126"]));
1929 assert(encoded == "FPucA9l+");
1930 assert(Base64.decode(map!q{a}(encoded)) == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]);
1932 // InputRange to OutputRange
1933 auto a = Appender!(char[])([]);
1934 auto b = Appender!(ubyte[])([]);
1935 assert(Base64.encode(map!(to!(ubyte))(["20", "251", "156", "3", "217", "126"]), a) == 8);
1936 assert(a.data == "FPucA9l+");
1937 assert(Base64.decode(map!q{a}(a.data), b) == 6);
1938 assert(b.data == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]);
1941 { // Encoder and Decoder
1943 string encode_file = std.file.deleteme ~ "-testingEncoder";
1944 std.file.write(encode_file, "\nf\nfo\nfoo\nfoob\nfooba\nfoobar");
1946 auto witness = ["", "Zg==", "Zm8=", "Zm9v", "Zm9vYg==", "Zm9vYmE=", "Zm9vYmFy"];
1947 auto f = File(encode_file);
1948 scope(exit)
1950 f.close();
1951 assert(!f.isOpen);
1952 std.file.remove(encode_file);
1955 size_t i;
1956 foreach (encoded; Base64.encoder(f.byLine()))
1957 assert(encoded == witness[i++]);
1959 assert(i == witness.length);
1963 string decode_file = std.file.deleteme ~ "-testingDecoder";
1964 std.file.write(decode_file, "\nZg==\nZm8=\nZm9v\nZm9vYg==\nZm9vYmE=\nZm9vYmFy");
1966 auto witness = sort(tv.keys);
1967 auto f = File(decode_file);
1968 scope(exit)
1970 f.close();
1971 assert(!f.isOpen);
1972 std.file.remove(decode_file);
1975 size_t i;
1976 foreach (decoded; Base64.decoder(f.byLine()))
1977 assert(decoded == witness[i++]);
1979 assert(i == witness.length);
1982 { // ForwardRange
1984 auto encoder = Base64.encoder(sort(tv.values));
1985 auto witness = ["", "Zg==", "Zm8=", "Zm9v", "Zm9vYg==", "Zm9vYmE=", "Zm9vYmFy"];
1986 size_t i;
1988 assert(encoder.front == witness[i++]); encoder.popFront();
1989 assert(encoder.front == witness[i++]); encoder.popFront();
1990 assert(encoder.front == witness[i++]); encoder.popFront();
1992 foreach (encoded; encoder.save)
1993 assert(encoded == witness[i++]);
1997 auto decoder = Base64.decoder(["", "Zg==", "Zm8=", "Zm9v", "Zm9vYg==", "Zm9vYmE=", "Zm9vYmFy"]);
1998 auto witness = sort(tv.values);
1999 size_t i;
2001 assert(decoder.front == witness[i++]); decoder.popFront();
2002 assert(decoder.front == witness[i++]); decoder.popFront();
2003 assert(decoder.front == witness[i++]); decoder.popFront();
2005 foreach (decoded; decoder.save)
2006 assert(decoded == witness[i++]);
2011 { // Encoder and Decoder for single character encoding and decoding
2012 alias Base64NoPadding = Base64Impl!('+', '/', Base64.NoPadding);
2014 auto tests = [
2015 "" : ["", "", "", ""],
2016 "f" : ["Zg==", "Zg==", "Zg", "Zg"],
2017 "fo" : ["Zm8=", "Zm8=", "Zm8", "Zm8"],
2018 "foo" : ["Zm9v", "Zm9v", "Zm9v", "Zm9v"],
2019 "foob" : ["Zm9vYg==", "Zm9vYg==", "Zm9vYg", "Zm9vYg"],
2020 "fooba" : ["Zm9vYmE=", "Zm9vYmE=", "Zm9vYmE", "Zm9vYmE"],
2021 "foobar" : ["Zm9vYmFy", "Zm9vYmFy", "Zm9vYmFy", "Zm9vYmFy"],
2024 foreach (u, e; tests)
2026 assert(equal(Base64.encoder(cast(ubyte[]) u), e[0]));
2027 assert(equal(Base64.decoder(Base64.encoder(cast(ubyte[]) u)), u));
2029 assert(equal(Base64URL.encoder(cast(ubyte[]) u), e[1]));
2030 assert(equal(Base64URL.decoder(Base64URL.encoder(cast(ubyte[]) u)), u));
2032 assert(equal(Base64NoPadding.encoder(cast(ubyte[]) u), e[2]));
2033 assert(equal(Base64NoPadding.decoder(Base64NoPadding.encoder(cast(ubyte[]) u)), u));
2035 assert(equal(Base64Re.encoder(cast(ubyte[]) u), e[3]));
2036 assert(equal(Base64Re.decoder(Base64Re.encoder(cast(ubyte[]) u)), u));
2041 // Regression control for the output range ref bug in encode.
2042 @system unittest
2044 struct InputRange
2046 ubyte[] impl = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e];
2047 @property bool empty() { return impl.length == 0; }
2048 @property ubyte front() { return impl[0]; }
2049 void popFront() { impl = impl[1 .. $]; }
2050 @property size_t length() { return impl.length; }
2053 struct OutputRange
2055 char[] result;
2056 void put(char b) { result ~= b; }
2059 InputRange ir;
2060 OutputRange or;
2061 assert(Base64.encode(ir, or) == 8);
2062 assert(or.result == "Gis8TV1u");
2064 // Verify that any existing workaround that uses & still works.
2065 InputRange ir2;
2066 OutputRange or2;
2067 assert(Base64.encode(ir2, &or2) == 8);
2068 assert(or2.result == "Gis8TV1u");
2071 // Regression control for the output range ref bug in decode.
2072 @system unittest
2074 struct InputRange
2076 const(char)[] impl = "Gis8TV1u";
2077 @property bool empty() { return impl.length == 0; }
2078 @property dchar front() { return impl[0]; }
2079 void popFront() { impl = impl[1 .. $]; }
2080 @property size_t length() { return impl.length; }
2083 struct OutputRange
2085 ubyte[] result;
2086 void put(ubyte b) { result ~= b; }
2089 InputRange ir;
2090 OutputRange or;
2091 assert(Base64.decode(ir, or) == 6);
2092 assert(or.result == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]);
2094 // Verify that any existing workaround that uses & still works.
2095 InputRange ir2;
2096 OutputRange or2;
2097 assert(Base64.decode(ir2, &or2) == 6);
2098 assert(or2.result == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]);