1 module more.base64;
2 
3 immutable encode64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4 auto formatBase64(T, size_t StackSize = 100)(const(T)[] data) if(T.sizeof == 1)
5 {
6     static struct Formatter
7     {
8         const(T)[] data;
9         void toString(scope void delegate(const(char)[]) sink) const
10         {
11             ubyte stackIndex = 0;
12             size_t dataIndex = 0;
13             char[StackSize] stackBuffer;
14 
15             for(; dataIndex + 2 < data.length; dataIndex += 3)
16             {
17                 stackBuffer[stackIndex++] = encode64[        (data[dataIndex + 0] >> 2                           ) ];
18                 stackBuffer[stackIndex++] = encode64[ 0x3F & (data[dataIndex + 0] << 4 | data[dataIndex + 1] >> 4) ];
19                 stackBuffer[stackIndex++] = encode64[ 0x3F & (data[dataIndex + 1] << 2 | data[dataIndex + 2] >> 6) ];
20                 stackBuffer[stackIndex++] = encode64[ 0x3F & (data[dataIndex + 2]                                ) ];
21                 if(stackIndex + 3 >= stackBuffer.length)
22                 {
23                     sink(stackBuffer[0..stackIndex]);
24                     stackIndex = 0;
25                 }
26             }
27 
28             if(dataIndex < data.length)
29             {
30                 stackBuffer[stackIndex++] = encode64[        (data[dataIndex + 0] >> 2                           ) ];
31                 if(dataIndex + 1 >= data.length)
32                 {
33                     stackBuffer[stackIndex++] = encode64[ 0x3F & (data[dataIndex + 0] << 4                           ) ];
34                     stackBuffer[stackIndex++] = '=';
35                     stackBuffer[stackIndex++] = '=';
36                 }
37                 else
38                 {
39                     stackBuffer[stackIndex++] = encode64[ 0x3F & (data[dataIndex + 0] << 4 | data[dataIndex + 1] >> 4) ];
40                     stackBuffer[stackIndex++] = encode64[ 0x3F & (data[dataIndex + 1] << 2                           ) ];
41                     stackBuffer[stackIndex++] = '=';
42                 }
43             }
44             sink(stackBuffer[0..stackIndex]);
45         }
46     }
47     return Formatter(data);
48 }
49 unittest
50 {
51     import more.test;
52     mixin(scopedTest!"base64");
53 
54     import std.format : format;
55     assert(format("%s", formatBase64(""))       == "");
56     assert(format("%s", formatBase64("f"))      == "Zg==");
57     assert(format("%s", formatBase64("fo"))     == "Zm8=");
58     assert(format("%s", formatBase64("foo"))    == "Zm9v");
59     assert(format("%s", formatBase64("foob"))   == "Zm9vYg==");
60     assert(format("%s", formatBase64("fooba"))  == "Zm9vYmE=");
61     assert(format("%s", formatBase64("foobar")) == "Zm9vYmFy");
62 }