Mercurial > projects > ldc
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 |