saxy: fixed bug with """ parsing
[iv.d.git] / base64.d
blob15fd33a9f5228c5570a9763752199fd4294a2d20
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, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // BASE64 codec; because i can!
18 module iv.base64 /*is aliced*/;
19 import iv.alice;
22 // ////////////////////////////////////////////////////////////////////////// //
23 mixin(NewExceptionClass!"Base64Exception");
26 // ////////////////////////////////////////////////////////////////////////// //
27 public static immutable string b64alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
29 private static immutable ubyte[256] b64dc = () {
30 ubyte[256] res = 0xff; // invalid
31 foreach (immutable idx, immutable char ch; b64alphabet) {
32 res[cast(ubyte)ch] = cast(ubyte)idx;
34 res['='] = 0xfe; // padding
35 // ignore
36 res[8..14] = 0xf0;
37 res[32] = 0xf0;
38 res[127] = 0xf0; // just in case
39 return res;
40 }();
43 // ////////////////////////////////////////////////////////////////////////// //
44 public void base64Encode(bool padding=true, RO, RI) (auto ref RO ro, auto ref RI ri)
45 if (Imp!"std.range.primitives".isInputRange!RI && is(Imp!"std.range.primitives".ElementEncodingType!RI : ubyte) &&
46 (Imp!"std.range.primitives".isOutputRange!(RO, char) || Imp!"std.range.primitives".isOutputRange!(RO, ubyte))
49 ubyte[3] bts;
50 uint btspos;
52 void encodeChunk() () {
53 if (btspos == 0) return;
54 ro.put(b64alphabet.ptr[(bts.ptr[0]&0xfc)>>2]);
55 if (btspos == 1) {
56 ro.put(b64alphabet.ptr[(bts.ptr[0]&0x03)<<4]);
57 static if (padding) { ro.put('='); ro.put('='); }
58 } else {
59 // 2 or more
60 ro.put(b64alphabet.ptr[((bts.ptr[0]&0x03)<<4)|((bts.ptr[1]&0xf0)>>4)]);
61 if (btspos == 2) {
62 ro.put(b64alphabet.ptr[(bts.ptr[1]&0x0f)<<2]);
63 static if (padding) ro.put('=');
64 } else {
65 // 3 bytes
66 ro.put(b64alphabet.ptr[((bts.ptr[1]&0x0f)<<2)|((bts.ptr[2]&0xc0)>>6)]);
67 ro.put(b64alphabet.ptr[bts.ptr[2]&0x3f]);
70 btspos = 0;
73 while (!ri.empty) {
74 ubyte ib = cast(ubyte)ri.front;
75 ri.popFront();
76 bts.ptr[btspos++] = ib;
77 if (btspos == 3) encodeChunk();
79 if (btspos != 0) encodeChunk();
82 public OT[] base64Encode(OT=ubyte, bool padding=true, RI) (auto ref RI ri)
83 if (!is(RI : AE[], AE) &&
84 Imp!"std.range.primitives".isInputRange!RI && is(Imp!"std.range.primitives".ElementEncodingType!RI : ubyte) &&
85 (is(OT == ubyte) || is(OT == char))
88 static struct OutRange {
89 OT[] res;
90 void put (const(OT)[] b...) nothrow @trusted { if (b.length) res ~= b[]; }
92 OutRange ro;
93 base64Encode!padding(ro, ri);
94 return ro.res;
97 public OT[] base64Encode(OT=ubyte, bool padding=true) (const(void)[] buf) if (is(OT == ubyte) || is(OT == char)) {
98 static struct InRange {
99 const(ubyte)[] data;
100 pure nothrow @trusted @nogc:
101 @property bool empty () const { pragma(inline, true); return (data.length == 0); }
102 @property ubyte front () const { pragma(inline, true); return data.ptr[0]; }
103 void popFront () { data = data[1..$]; }
105 static struct OutRange {
106 OT[] res;
107 void put (const(OT)[] b...) nothrow @trusted { if (b.length) res ~= b[]; }
109 auto ri = InRange(cast(const(ubyte)[])buf);
110 OutRange ro;
111 base64Encode!padding(ro, ri);
112 return ro.res;
116 // ////////////////////////////////////////////////////////////////////////// //
117 public void base64Decode(RO, RI) (auto ref RO ro, auto ref RI ri)
118 if (Imp!"std.range.primitives".isInputRange!RI && is(Imp!"std.range.primitives".ElementEncodingType!RI : dchar) &&
119 (Imp!"std.range.primitives".isOutputRange!(RO, char) || Imp!"std.range.primitives".isOutputRange!(RO, ubyte))
122 bool inPadding = false;
123 ubyte[4] bts;
124 uint btspos;
126 void decodeChunk() () {
127 if (btspos == 0) return;
128 if (btspos == 1) throw new Base64Exception("incomplete data in base64 decoder");
129 ro.put(cast(char)((bts.ptr[0]<<2)|((bts.ptr[1]&0x30)>>4))); // 2 and more
130 if (btspos > 2) ro.put(cast(char)(((bts.ptr[1]&0x0f)<<4)|((bts.ptr[2]&0x3c)>>2))); // 3 and more
131 if (btspos > 3) ro.put(cast(char)(((bts.ptr[2]&0x03)<<6)|bts.ptr[3]));
134 while (!ri.empty) {
135 static if (is(typeof(ri.front) : char)) {
136 ubyte cb = b64dc.ptr[cast(ubyte)ri.front];
137 } else {
138 auto ccw = ri.front;
139 ubyte cb = (ccw >= 0 && ccw <= 255 ? b64dc.ptr[cast(ubyte)ccw] : 0xff);
141 if (cb == 0xff) throw new Base64Exception("invalid input char in base64 decoder");
142 ri.popFront();
143 if (cb == 0xf0) continue; // empty
144 if (cb == 0xfe) {
145 // padding
146 if (!inPadding) { decodeChunk(); inPadding = true; }
147 if (++btspos == 4) { inPadding = false; btspos = 0; }
148 } else {
149 // normal
150 if (inPadding) {
151 if (btspos != 0) throw new Base64Exception("invalid input char in base64 decoder");
152 inPadding = false;
154 bts.ptr[btspos++] = cb;
155 if (btspos == 4) { decodeChunk(); btspos = 0; }
158 if (btspos != 0 && !inPadding) decodeChunk(); // assume that it is not padded
161 public OT[] base64Decode(OT=ubyte, RI) (auto ref RI ri)
162 if (!is(RI : AE[], AE) &&
163 Imp!"std.range.primitives".isInputRange!RI && is(Imp!"std.range.primitives".ElementEncodingType!RI : dchar) &&
164 (is(OT == ubyte) || is(OT == char))
167 static struct OutRange {
168 OT[] res;
169 void put (const(OT)[] b...) nothrow @trusted { if (b.length) res ~= b[]; }
171 OutRange ro;
172 base64Decode(ro, ri);
173 return ro.res;
176 public OT[] base64Decode(OT=ubyte) (const(void)[] buf) if (is(OT == ubyte) || is(OT == char)) {
177 static struct InRange {
178 const(ubyte)[] data;
179 pure nothrow @trusted @nogc:
180 @property bool empty () const { pragma(inline, true); return (data.length == 0); }
181 @property ubyte front () const { pragma(inline, true); return data.ptr[0]; }
182 void popFront () { data = data[1..$]; }
184 static struct OutRange {
185 OT[] res;
186 void put (const(OT)[] b...) nothrow @trusted { if (b.length) res ~= b[]; }
188 auto ri = InRange(cast(const(ubyte)[])buf);
189 OutRange ro;
190 base64Decode(ro, ri);
191 return ro.res;
195 // ////////////////////////////////////////////////////////////////////////// //
196 version(iv_base64_test) {
197 import iv.cmdcon;
198 void main () {
199 conwriteln(base64Decode!char("Zm\r9 vY\tmF\ny\n"), "|");
200 conwriteln(base64Decode!char("Zg=="), "|");
201 conwriteln(base64Decode!char("Zg"), "|");
202 conwriteln(base64Decode!char("Zm8="), "|");
203 conwriteln(base64Decode!char("Zm8"), "|");
204 conwriteln(base64Decode!char("Zm9v"), "|");
205 conwriteln(base64Decode!char("Zm9vYg=="), "|");
206 conwriteln(base64Decode!char("Zm9vYg="), "|");
207 conwriteln(base64Decode!char("Zm9vYg"), "|");
208 conwriteln(base64Decode!char("Zm9vYmE="), "|");
209 conwriteln(base64Decode!char("Zm9vYmE"), "|");
210 conwriteln(base64Decode!char("Zm9vYmFy"), "|");
211 conwriteln(base64Decode!char("Zm9vYmFy==="), "|");
212 conwriteln("==================");
213 conwriteln(base64Encode!char(""));
214 conwriteln(base64Encode!char("f"));
215 conwriteln(base64Encode!char("fo"));
216 conwriteln(base64Encode!char("foo"));
217 conwriteln(base64Encode!char("foob"));
218 conwriteln(base64Encode!char("fooba"));
219 conwriteln(base64Encode!char("foobar"));