comparison lphobos/std/base64.d @ 130:a7dfa0ed966c trunk

[svn r134] Merged the DMD 1.024 frontend. Added std.base64.
author lindquist
date Fri, 28 Dec 2007 23:52:40 +0100
parents
children
comparison
equal deleted inserted replaced
129:8096ba7082db 130:a7dfa0ed966c
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