130
|
1 /**
|
|
2 * Encodes/decodes MIME base64 data.
|
|
3 *
|
|
4 * Macros:
|
|
5 * WIKI=Phobos/StdBase64
|
|
6 * References:
|
|
7 * <a href="http://en.wikipedia.org/wiki/Base64">Wikipedia Base64</a>$(BR)
|
|
8 * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>$(BR)
|
|
9 */
|
|
10
|
|
11
|
|
12 /* base64.d
|
|
13 * Modified from C. Miller's version, his copyright is below.
|
|
14 */
|
|
15
|
|
16 /*
|
|
17 Copyright (C) 2004 Christopher E. Miller
|
|
18
|
|
19 This software is provided 'as-is', without any express or implied
|
|
20 warranty. In no event will the authors be held liable for any damages
|
|
21 arising from the use of this software.
|
|
22
|
|
23 Permission is granted to anyone to use this software for any purpose,
|
|
24 including commercial applications, and to alter it and redistribute it
|
|
25 freely, subject to the following restrictions:
|
|
26
|
|
27 1. The origin of this software must not be misrepresented; you must not
|
|
28 claim that you wrote the original software. If you use this software
|
|
29 in a product, an acknowledgment in the product documentation would be
|
|
30 appreciated but is not required.
|
|
31 2. Altered source versions must be plainly marked as such, and must not be
|
|
32 misrepresented as being the original software.
|
|
33 3. This notice may not be removed or altered from any source distribution.
|
|
34 */
|
|
35
|
|
36 module std.base64;
|
|
37
|
|
38 /**
|
|
39 */
|
|
40
|
|
41 class Base64Exception: Exception
|
|
42 {
|
|
43 this(char[] msg)
|
|
44 {
|
|
45 super(msg);
|
|
46 }
|
|
47 }
|
|
48
|
|
49
|
|
50 /**
|
|
51 */
|
|
52
|
|
53 class Base64CharException: Base64Exception
|
|
54 {
|
|
55 this(char[] msg)
|
|
56 {
|
|
57 super(msg);
|
|
58 }
|
|
59 }
|
|
60
|
|
61
|
|
62 const char[] array = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
63
|
|
64
|
|
65 /**
|
|
66 * Returns the number of bytes needed to encode a string of length slen.
|
|
67 */
|
|
68
|
|
69 uint encodeLength(uint slen)
|
|
70 {
|
|
71 uint result;
|
|
72 result = slen / 3;
|
|
73 if(slen % 3)
|
|
74 result++;
|
|
75 return result * 4;
|
|
76 }
|
|
77
|
|
78 /**
|
|
79 * Encodes str[] and places the result in buf[].
|
|
80 * Params:
|
|
81 * str = string to encode
|
|
82 * buf = destination buffer, must be large enough for the result.
|
|
83 * Returns:
|
|
84 * slice into buf[] representing encoded result
|
|
85 */
|
|
86
|
|
87 char[] encode(char[] str, char[] buf)
|
|
88 in
|
|
89 {
|
|
90 assert(buf.length >= encodeLength(str.length));
|
|
91 }
|
|
92 body
|
|
93 {
|
|
94 if(!str.length)
|
|
95 return buf[0 .. 0];
|
|
96
|
|
97 uint stri;
|
|
98 uint strmax = str.length / 3;
|
|
99 uint strleft = str.length % 3;
|
|
100 uint x;
|
|
101 char* sp, bp;
|
|
102
|
|
103 bp = &buf[0];
|
|
104 sp = &str[0];
|
|
105 for(stri = 0; stri != strmax; stri++)
|
|
106 {
|
|
107 x = (sp[0] << 16) | (sp[1] << 8) | (sp[2]);
|
|
108 sp+= 3;
|
|
109 *bp++ = array[(x & 0b11111100_00000000_00000000) >> 18];
|
|
110 *bp++ = array[(x & 0b00000011_11110000_00000000) >> 12];
|
|
111 *bp++ = array[(x & 0b00000000_00001111_11000000) >> 6];
|
|
112 *bp++ = array[(x & 0b00000000_00000000_00111111)];
|
|
113 }
|
|
114
|
|
115 switch(strleft)
|
|
116 {
|
|
117 case 2:
|
|
118 x = (sp[0] << 16) | (sp[1] << 8);
|
|
119 sp += 2;
|
|
120 *bp++ = array[(x & 0b11111100_00000000_00000000) >> 18];
|
|
121 *bp++ = array[(x & 0b00000011_11110000_00000000) >> 12];
|
|
122 *bp++ = array[(x & 0b00000000_00001111_11000000) >> 6];
|
|
123 *bp++ = '=';
|
|
124 break;
|
|
125
|
|
126 case 1:
|
|
127 x = *sp++ << 16;
|
|
128 *bp++ = array[(x & 0b11111100_00000000_00000000) >> 18];
|
|
129 *bp++ = array[(x & 0b00000011_11110000_00000000) >> 12];
|
|
130 *bp++ = '=';
|
|
131 *bp++ = '=';
|
|
132 break;
|
|
133
|
|
134 case 0:
|
|
135 break;
|
|
136
|
|
137 default:
|
|
138 assert(0);
|
|
139 }
|
|
140
|
|
141 return buf[0 .. (bp - &buf[0])];
|
|
142 }
|
|
143
|
|
144
|
|
145 /**
|
|
146 * Encodes str[] and returns the result.
|
|
147 */
|
|
148
|
|
149 char[] encode(char[] str)
|
|
150 {
|
|
151 return encode(str, new char[encodeLength(str.length)]);
|
|
152 }
|
|
153
|
|
154
|
|
155 unittest
|
|
156 {
|
|
157 assert(encode("f") == "Zg==");
|
|
158 assert(encode("fo") == "Zm8=");
|
|
159 assert(encode("foo") == "Zm9v");
|
|
160 assert(encode("foos") == "Zm9vcw==");
|
|
161 assert(encode("all your base64 are belong to foo") == "YWxsIHlvdXIgYmFzZTY0IGFyZSBiZWxvbmcgdG8gZm9v");
|
|
162 }
|
|
163
|
|
164
|
|
165 /**
|
|
166 * Returns the number of bytes needed to decode an encoded string of this
|
|
167 * length.
|
|
168 */
|
|
169 uint decodeLength(uint elen)
|
|
170 {
|
|
171 return elen / 4 * 3;
|
|
172 }
|
|
173
|
|
174
|
|
175 /**
|
|
176 * Decodes str[] and places the result in buf[].
|
|
177 * Params:
|
|
178 * str = string to encode
|
|
179 * buf = destination buffer, must be large enough for the result.
|
|
180 * Returns:
|
|
181 * slice into buf[] representing encoded result
|
|
182 * Errors:
|
|
183 * Throws Base64Exception on invalid base64 encoding in estr[].
|
|
184 * Throws Base64CharException on invalid base64 character in estr[].
|
|
185 */
|
|
186 char[] decode(char[] estr, char[] buf)
|
|
187 in
|
|
188 {
|
|
189 assert(buf.length + 2 >= decodeLength(estr.length)); //account for '=' padding
|
|
190 }
|
|
191 body
|
|
192 {
|
|
193 void badc(char ch)
|
|
194 {
|
|
195 throw new Base64CharException("Invalid base64 character '" ~ (&ch)[0 .. 1] ~ "'");
|
|
196 }
|
|
197
|
|
198
|
|
199 uint arrayIndex(char ch)
|
|
200 out(result)
|
|
201 {
|
|
202 assert(ch == array[result]);
|
|
203 }
|
|
204 body
|
|
205 {
|
|
206 if(ch >= 'A' && ch <= 'Z')
|
|
207 return ch - 'A';
|
|
208 if(ch >= 'a' && ch <= 'z')
|
|
209 return 'Z' - 'A' + 1 + ch - 'a';
|
|
210 if(ch >= '0' && ch <= '9')
|
|
211 return 'Z' - 'A' + 1 + 'z' - 'a' + 1 + ch - '0';
|
|
212 if(ch == '+')
|
|
213 return 'Z' - 'A' + 1 + 'z' - 'a' + 1 + '9' - '0' + 1;
|
|
214 if(ch == '/')
|
|
215 return 'Z' - 'A' + 1 + 'z' - 'a' + 1 + '9' - '0' + 1 + 1;
|
|
216 badc(ch);
|
|
217 assert(0);
|
|
218 }
|
|
219
|
|
220
|
|
221 if(!estr.length)
|
|
222 return buf[0 .. 0];
|
|
223
|
|
224 if(estr.length % 4)
|
|
225 throw new Base64Exception("Invalid encoded base64 string");
|
|
226
|
|
227 uint estri;
|
|
228 uint estrmax = estr.length / 4;
|
|
229 uint x;
|
|
230 char* sp, bp;
|
|
231 char ch;
|
|
232
|
|
233 sp = &estr[0];
|
|
234 bp = &buf[0];
|
|
235 for(estri = 0; estri != estrmax; estri++)
|
|
236 {
|
|
237 x = arrayIndex(sp[0]) << 18 | arrayIndex(sp[1]) << 12;
|
|
238 sp += 2;
|
|
239
|
|
240 ch = *sp++;
|
|
241 if(ch == '=')
|
|
242 {
|
|
243 if(*sp++ != '=')
|
|
244 badc('=');
|
|
245 *bp++ = cast(char) (x >> 16);
|
|
246 break;
|
|
247 }
|
|
248 x |= arrayIndex(ch) << 6;
|
|
249
|
|
250 ch = *sp++;
|
|
251 if(ch == '=')
|
|
252 {
|
|
253 *bp++ = cast(char) (x >> 16);
|
|
254 *bp++ = cast(char) ((x >> 8) & 0xFF);
|
|
255 break;
|
|
256 }
|
|
257 x |= arrayIndex(ch);
|
|
258
|
|
259 *bp++ = cast(char) (x >> 16);
|
|
260 *bp++ = cast(char) ((x >> 8) & 0xFF);
|
|
261 *bp++ = cast(char) (x & 0xFF);
|
|
262 }
|
|
263
|
|
264 return buf[0 .. (bp - &buf[0])];
|
|
265 }
|
|
266
|
|
267 /**
|
|
268 * Decodes estr[] and returns the result.
|
|
269 * Errors:
|
|
270 * Throws Base64Exception on invalid base64 encoding in estr[].
|
|
271 * Throws Base64CharException on invalid base64 character in estr[].
|
|
272 */
|
|
273
|
|
274 char[] decode(char[] estr)
|
|
275 {
|
|
276 return decode(estr, new char[decodeLength(estr.length)]);
|
|
277 }
|
|
278
|
|
279
|
|
280 unittest
|
|
281 {
|
|
282 assert(decode(encode("f")) == "f");
|
|
283 assert(decode(encode("fo")) == "fo");
|
|
284 assert(decode(encode("foo")) == "foo");
|
|
285 assert(decode(encode("foos")) == "foos");
|
|
286 assert(decode(encode("all your base64 are belong to foo")) == "all your base64 are belong to foo");
|
|
287
|
|
288 assert(decode(encode("testing some more")) == "testing some more");
|
|
289 assert(decode(encode("asdf jkl;")) == "asdf jkl;");
|
|
290 assert(decode(encode("base64 stuff")) == "base64 stuff");
|
|
291 assert(decode(encode("\1\2\3\4\5\6\7foo\7\6\5\4\3\2\1!")) == "\1\2\3\4\5\6\7foo\7\6\5\4\3\2\1!");
|
|
292 }
|
|
293
|