iv.vfs: don't turn "w+" mode to "r+" mode, lol
[iv.d.git] / base64.d
blobafec3311acb08e1fdbbeda5c3af49442159bd8c0
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 // BASE64 codec; because i can!
19 module iv.base64 /*is aliced*/;
20 import iv.alice;
23 // ////////////////////////////////////////////////////////////////////////// //
24 mixin(NewExceptionClass!"Base64Exception");
27 // ////////////////////////////////////////////////////////////////////////// //
28 public static immutable string b64alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
30 private static immutable ubyte[256] b64dc = () {
31 ubyte[256] res = 0xff; // invalid
32 foreach (immutable idx, immutable char ch; b64alphabet) {
33 res[cast(ubyte)ch] = cast(ubyte)idx;
35 res['='] = 0xfe; // padding
36 // ignore
37 res[8..14] = 0xf0;
38 res[32] = 0xf0;
39 res[127] = 0xf0; // just in case
40 return res;
41 }();
44 // ////////////////////////////////////////////////////////////////////////// //
45 public void base64Encode(bool padding=true, RO, RI) (auto ref RO ro, auto ref RI ri)
46 if (Imp!"std.range.primitives".isInputRange!RI && is(Imp!"std.range.primitives".ElementEncodingType!RI : ubyte) &&
47 (Imp!"std.range.primitives".isOutputRange!(RO, char) || Imp!"std.range.primitives".isOutputRange!(RO, ubyte))
50 ubyte[3] bts;
51 uint btspos;
53 void encodeChunk() () {
54 if (btspos == 0) return;
55 ro.put(b64alphabet.ptr[(bts.ptr[0]&0xfc)>>2]);
56 if (btspos == 1) {
57 ro.put(b64alphabet.ptr[(bts.ptr[0]&0x03)<<4]);
58 static if (padding) { ro.put('='); ro.put('='); }
59 } else {
60 // 2 or more
61 ro.put(b64alphabet.ptr[((bts.ptr[0]&0x03)<<4)|((bts.ptr[1]&0xf0)>>4)]);
62 if (btspos == 2) {
63 ro.put(b64alphabet.ptr[(bts.ptr[1]&0x0f)<<2]);
64 static if (padding) ro.put('=');
65 } else {
66 // 3 bytes
67 ro.put(b64alphabet.ptr[((bts.ptr[1]&0x0f)<<2)|((bts.ptr[2]&0xc0)>>6)]);
68 ro.put(b64alphabet.ptr[bts.ptr[2]&0x3f]);
71 btspos = 0;
74 while (!ri.empty) {
75 ubyte ib = cast(ubyte)ri.front;
76 ri.popFront();
77 bts.ptr[btspos++] = ib;
78 if (btspos == 3) encodeChunk();
80 if (btspos != 0) encodeChunk();
83 public OT[] base64Encode(OT=ubyte, bool padding=true, RI) (auto ref RI ri)
84 if (!is(RI : AE[], AE) &&
85 Imp!"std.range.primitives".isInputRange!RI && is(Imp!"std.range.primitives".ElementEncodingType!RI : ubyte) &&
86 (is(OT == ubyte) || is(OT == char))
89 static struct OutRange {
90 OT[] res;
91 void put (const(OT)[] b...) nothrow @trusted { if (b.length) res ~= b[]; }
93 OutRange ro;
94 base64Encode!padding(ro, ri);
95 return ro.res;
98 public OT[] base64Encode(OT=ubyte, bool padding=true) (const(void)[] buf) if (is(OT == ubyte) || is(OT == char)) {
99 static struct InRange {
100 const(ubyte)[] data;
101 pure nothrow @trusted @nogc:
102 @property bool empty () const { pragma(inline, true); return (data.length == 0); }
103 @property ubyte front () const { pragma(inline, true); return data.ptr[0]; }
104 void popFront () { data = data[1..$]; }
106 static struct OutRange {
107 OT[] res;
108 void put (const(OT)[] b...) nothrow @trusted { if (b.length) res ~= b[]; }
110 auto ri = InRange(cast(const(ubyte)[])buf);
111 OutRange ro;
112 base64Encode!padding(ro, ri);
113 return ro.res;
117 // ////////////////////////////////////////////////////////////////////////// //
118 public void base64Decode(RO, RI) (auto ref RO ro, auto ref RI ri)
119 if (Imp!"std.range.primitives".isInputRange!RI && is(Imp!"std.range.primitives".ElementEncodingType!RI : dchar) &&
120 (Imp!"std.range.primitives".isOutputRange!(RO, char) || Imp!"std.range.primitives".isOutputRange!(RO, ubyte))
123 bool inPadding = false;
124 ubyte[4] bts;
125 uint btspos;
127 void decodeChunk() () {
128 if (btspos == 0) return;
129 if (btspos == 1) throw new Base64Exception("incomplete data in base64 decoder");
130 ro.put(cast(char)((bts.ptr[0]<<2)|((bts.ptr[1]&0x30)>>4))); // 2 and more
131 if (btspos > 2) ro.put(cast(char)(((bts.ptr[1]&0x0f)<<4)|((bts.ptr[2]&0x3c)>>2))); // 3 and more
132 if (btspos > 3) ro.put(cast(char)(((bts.ptr[2]&0x03)<<6)|bts.ptr[3]));
135 while (!ri.empty) {
136 static if (is(typeof(ri.front) : char)) {
137 ubyte cb = b64dc.ptr[cast(ubyte)ri.front];
138 } else {
139 auto ccw = ri.front;
140 ubyte cb = (ccw >= 0 && ccw <= 255 ? b64dc.ptr[cast(ubyte)ccw] : 0xff);
142 if (cb == 0xff) throw new Base64Exception("invalid input char in base64 decoder");
143 ri.popFront();
144 if (cb == 0xf0) continue; // empty
145 if (cb == 0xfe) {
146 // padding
147 if (!inPadding) { decodeChunk(); inPadding = true; }
148 if (++btspos == 4) { inPadding = false; btspos = 0; }
149 } else {
150 // normal
151 if (inPadding) {
152 if (btspos != 0) throw new Base64Exception("invalid input char in base64 decoder");
153 inPadding = false;
155 bts.ptr[btspos++] = cb;
156 if (btspos == 4) { decodeChunk(); btspos = 0; }
159 if (btspos != 0 && !inPadding) decodeChunk(); // assume that it is not padded
162 public OT[] base64Decode(OT=ubyte, RI) (auto ref RI ri)
163 if (!is(RI : AE[], AE) &&
164 Imp!"std.range.primitives".isInputRange!RI && is(Imp!"std.range.primitives".ElementEncodingType!RI : dchar) &&
165 (is(OT == ubyte) || is(OT == char))
168 static struct OutRange {
169 OT[] res;
170 void put (const(OT)[] b...) nothrow @trusted { if (b.length) res ~= b[]; }
172 OutRange ro;
173 base64Decode(ro, ri);
174 return ro.res;
177 public OT[] base64Decode(OT=ubyte) (const(void)[] buf) if (is(OT == ubyte) || is(OT == char)) {
178 static struct InRange {
179 const(ubyte)[] data;
180 pure nothrow @trusted @nogc:
181 @property bool empty () const { pragma(inline, true); return (data.length == 0); }
182 @property ubyte front () const { pragma(inline, true); return data.ptr[0]; }
183 void popFront () { data = data[1..$]; }
185 static struct OutRange {
186 OT[] res;
187 void put (const(OT)[] b...) nothrow @trusted { if (b.length) res ~= b[]; }
189 auto ri = InRange(cast(const(ubyte)[])buf);
190 OutRange ro;
191 base64Decode(ro, ri);
192 return ro.res;
196 // ////////////////////////////////////////////////////////////////////////// //
197 version(iv_base64_test) {
198 import iv.cmdcon;
199 void main () {
200 conwriteln(base64Decode!char("Zm\r9 vY\tmF\ny\n"), "|");
201 conwriteln(base64Decode!char("Zg=="), "|");
202 conwriteln(base64Decode!char("Zg"), "|");
203 conwriteln(base64Decode!char("Zm8="), "|");
204 conwriteln(base64Decode!char("Zm8"), "|");
205 conwriteln(base64Decode!char("Zm9v"), "|");
206 conwriteln(base64Decode!char("Zm9vYg=="), "|");
207 conwriteln(base64Decode!char("Zm9vYg="), "|");
208 conwriteln(base64Decode!char("Zm9vYg"), "|");
209 conwriteln(base64Decode!char("Zm9vYmE="), "|");
210 conwriteln(base64Decode!char("Zm9vYmE"), "|");
211 conwriteln(base64Decode!char("Zm9vYmFy"), "|");
212 conwriteln(base64Decode!char("Zm9vYmFy==="), "|");
213 conwriteln("==================");
214 conwriteln(base64Encode!char(""));
215 conwriteln(base64Encode!char("f"));
216 conwriteln(base64Encode!char("fo"));
217 conwriteln(base64Encode!char("foo"));
218 conwriteln(base64Encode!char("foob"));
219 conwriteln(base64Encode!char("fooba"));
220 conwriteln(base64Encode!char("foobar"));