1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Base64 binary-to-text encoding
12 use std::str;
13 use std::fmt;
14
15 /// Available encoding character sets
16 pub enum CharacterSet {
17 /// The standard character set (uses `+` and `/`)
18 Standard,
19 /// The URL safe character set (uses `-` and `_`)
20 UrlSafe
21 }
22
23 /// Contains configuration parameters for `to_base64`.
24 pub struct Config {
25 /// Character set to use
26 pub char_set: CharacterSet,
27 /// True to pad output with `=` characters
28 pub pad: bool,
29 /// `Some(len)` to wrap lines at `len`, `None` to disable line wrapping
30 pub line_length: Option<uint>
31 }
32
33 /// Configuration for RFC 4648 standard base64 encoding
34 pub static STANDARD: Config =
35 Config {char_set: Standard, pad: true, line_length: None};
36
37 /// Configuration for RFC 4648 base64url encoding
38 pub static URL_SAFE: Config =
39 Config {char_set: UrlSafe, pad: false, line_length: None};
40
41 /// Configuration for RFC 2045 MIME base64 encoding
42 pub static MIME: Config =
43 Config {char_set: Standard, pad: true, line_length: Some(76)};
44
45 static STANDARD_CHARS: &'static[u8] = bytes!("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
46 "abcdefghijklmnopqrstuvwxyz",
47 "0123456789+/");
48
49 static URLSAFE_CHARS: &'static[u8] = bytes!("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
50 "abcdefghijklmnopqrstuvwxyz",
51 "0123456789-_");
52
53 /// A trait for converting a value to base64 encoding.
54 pub trait ToBase64 {
55 /// Converts the value of `self` to a base64 value following the specified
56 /// format configuration, returning the owned string.
57 fn to_base64(&self, config: Config) -> ~str;
58 }
59
60 impl<'a> ToBase64 for &'a [u8] {
61 /**
62 * Turn a vector of `u8` bytes into a base64 string.
63 *
64 * # Example
65 *
66 * ```rust
67 * extern crate serialize;
68 * use serialize::base64::{ToBase64, STANDARD};
69 *
70 * fn main () {
71 * let str = [52,32].to_base64(STANDARD);
72 * println!("base 64 output: {}", str);
73 * }
74 * ```
75 */
76 fn to_base64(&self, config: Config) -> ~str {
77 let bytes = match config.char_set {
78 Standard => STANDARD_CHARS,
79 UrlSafe => URLSAFE_CHARS
80 };
81
82 let mut v = Vec::new();
83 let mut i = 0;
84 let mut cur_length = 0;
85 let len = self.len();
86 while i < len - (len % 3) {
87 match config.line_length {
88 Some(line_length) =>
89 if cur_length >= line_length {
90 v.push('\r' as u8);
91 v.push('\n' as u8);
92 cur_length = 0;
93 },
94 None => ()
95 }
96
97 let n = (self[i] as u32) << 16 |
98 (self[i + 1] as u32) << 8 |
99 (self[i + 2] as u32);
100
101 // This 24-bit number gets separated into four 6-bit numbers.
102 v.push(bytes[((n >> 18) & 63) as uint]);
103 v.push(bytes[((n >> 12) & 63) as uint]);
104 v.push(bytes[((n >> 6 ) & 63) as uint]);
105 v.push(bytes[(n & 63) as uint]);
106
107 cur_length += 4;
108 i += 3;
109 }
110
111 if len % 3 != 0 {
112 match config.line_length {
113 Some(line_length) =>
114 if cur_length >= line_length {
115 v.push('\r' as u8);
116 v.push('\n' as u8);
117 },
118 None => ()
119 }
120 }
121
122 // Heh, would be cool if we knew this was exhaustive
123 // (the dream of bounded integer types)
124 match len % 3 {
125 0 => (),
126 1 => {
127 let n = (self[i] as u32) << 16;
128 v.push(bytes[((n >> 18) & 63) as uint]);
129 v.push(bytes[((n >> 12) & 63) as uint]);
130 if config.pad {
131 v.push('=' as u8);
132 v.push('=' as u8);
133 }
134 }
135 2 => {
136 let n = (self[i] as u32) << 16 |
137 (self[i + 1u] as u32) << 8;
138 v.push(bytes[((n >> 18) & 63) as uint]);
139 v.push(bytes[((n >> 12) & 63) as uint]);
140 v.push(bytes[((n >> 6 ) & 63) as uint]);
141 if config.pad {
142 v.push('=' as u8);
143 }
144 }
145 _ => fail!("Algebra is broken, please alert the math police")
146 }
147
148 unsafe {
149 str::raw::from_utf8(v.as_slice()).to_owned()
150 }
151 }
152 }
153
154 /// A trait for converting from base64 encoded values.
155 pub trait FromBase64 {
156 /// Converts the value of `self`, interpreted as base64 encoded data, into
157 /// an owned vector of bytes, returning the vector.
158 fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error>;
159 }
160
161 /// Errors that can occur when decoding a base64 encoded string
162 pub enum FromBase64Error {
163 /// The input contained a character not part of the base64 format
164 InvalidBase64Character(char, uint),
165 /// The input had an invalid length
166 InvalidBase64Length,
167 }
168
169 impl fmt::Show for FromBase64Error {
170 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171 match *self {
172 InvalidBase64Character(ch, idx) =>
173 write!(f.buf, "Invalid character '{}' at position {}", ch, idx),
174 InvalidBase64Length => write!(f.buf, "Invalid length"),
175 }
176 }
177 }
178
179 impl<'a> FromBase64 for &'a str {
180 /**
181 * Convert any base64 encoded string (literal, `@`, `&`, or `~`)
182 * to the byte values it encodes.
183 *
184 * You can use the `from_utf8_owned` function in `std::str`
185 * to turn a `[u8]` into a string with characters corresponding to those
186 * values.
187 *
188 * # Example
189 *
190 * This converts a string literal to base64 and back.
191 *
192 * ```rust
193 * extern crate serialize;
194 * use serialize::base64::{ToBase64, FromBase64, STANDARD};
195 *
196 * fn main () {
197 * let hello_str = bytes!("Hello, World").to_base64(STANDARD);
198 * println!("base64 output: {}", hello_str);
199 * let res = hello_str.from_base64();
200 * if res.is_ok() {
201 * let opt_bytes = StrBuf::from_utf8(res.unwrap());
202 * if opt_bytes.is_some() {
203 * println!("decoded from base64: {}", opt_bytes.unwrap());
204 * }
205 * }
206 * }
207 * ```
208 */
209 fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> {
210 let mut r = Vec::new();
211 let mut buf: u32 = 0;
212 let mut modulus = 0;
213
214 let mut it = self.bytes().enumerate();
215 for (idx, byte) in it {
216 let val = byte as u32;
217
218 match byte as char {
219 'A'..'Z' => buf |= val - 0x41,
220 'a'..'z' => buf |= val - 0x47,
221 '0'..'9' => buf |= val + 0x04,
222 '+'|'-' => buf |= 0x3E,
223 '/'|'_' => buf |= 0x3F,
224 '\r'|'\n' => continue,
225 '=' => break,
226 _ => return Err(InvalidBase64Character(self.char_at(idx), idx)),
227 }
228
229 buf <<= 6;
230 modulus += 1;
231 if modulus == 4 {
232 modulus = 0;
233 r.push((buf >> 22) as u8);
234 r.push((buf >> 14) as u8);
235 r.push((buf >> 6 ) as u8);
236 }
237 }
238
239 for (idx, byte) in it {
240 match byte as char {
241 '='|'\r'|'\n' => continue,
242 _ => return Err(InvalidBase64Character(self.char_at(idx), idx)),
243 }
244 }
245
246 match modulus {
247 2 => {
248 r.push((buf >> 10) as u8);
249 }
250 3 => {
251 r.push((buf >> 16) as u8);
252 r.push((buf >> 8 ) as u8);
253 }
254 0 => (),
255 _ => return Err(InvalidBase64Length),
256 }
257
258 Ok(r)
259 }
260 }
261
262 #[cfg(test)]
263 mod tests {
264 extern crate test;
265 extern crate rand;
266 use self::test::Bencher;
267 use base64::{Config, FromBase64, ToBase64, STANDARD, URL_SAFE};
268
269 #[test]
270 fn test_to_base64_basic() {
271 assert_eq!("".as_bytes().to_base64(STANDARD), "".to_owned());
272 assert_eq!("f".as_bytes().to_base64(STANDARD), "Zg==".to_owned());
273 assert_eq!("fo".as_bytes().to_base64(STANDARD), "Zm8=".to_owned());
274 assert_eq!("foo".as_bytes().to_base64(STANDARD), "Zm9v".to_owned());
275 assert_eq!("foob".as_bytes().to_base64(STANDARD), "Zm9vYg==".to_owned());
276 assert_eq!("fooba".as_bytes().to_base64(STANDARD), "Zm9vYmE=".to_owned());
277 assert_eq!("foobar".as_bytes().to_base64(STANDARD), "Zm9vYmFy".to_owned());
278 }
279
280 #[test]
281 fn test_to_base64_line_break() {
282 assert!(![0u8, ..1000].to_base64(Config {line_length: None, ..STANDARD})
283 .contains("\r\n"));
284 assert_eq!("foobar".as_bytes().to_base64(Config {line_length: Some(4),
285 ..STANDARD}),
286 "Zm9v\r\nYmFy".to_owned());
287 }
288
289 #[test]
290 fn test_to_base64_padding() {
291 assert_eq!("f".as_bytes().to_base64(Config {pad: false, ..STANDARD}), "Zg".to_owned());
292 assert_eq!("fo".as_bytes().to_base64(Config {pad: false, ..STANDARD}), "Zm8".to_owned());
293 }
294
295 #[test]
296 fn test_to_base64_url_safe() {
297 assert_eq!([251, 255].to_base64(URL_SAFE), "-_8".to_owned());
298 assert_eq!([251, 255].to_base64(STANDARD), "+/8=".to_owned());
299 }
300
301 #[test]
302 fn test_from_base64_basic() {
303 assert_eq!("".from_base64().unwrap().as_slice(), "".as_bytes());
304 assert_eq!("Zg==".from_base64().unwrap().as_slice(), "f".as_bytes());
305 assert_eq!("Zm8=".from_base64().unwrap().as_slice(), "fo".as_bytes());
306 assert_eq!("Zm9v".from_base64().unwrap().as_slice(), "foo".as_bytes());
307 assert_eq!("Zm9vYg==".from_base64().unwrap().as_slice(), "foob".as_bytes());
308 assert_eq!("Zm9vYmE=".from_base64().unwrap().as_slice(), "fooba".as_bytes());
309 assert_eq!("Zm9vYmFy".from_base64().unwrap().as_slice(), "foobar".as_bytes());
310 }
311
312 #[test]
313 fn test_from_base64_newlines() {
314 assert_eq!("Zm9v\r\nYmFy".from_base64().unwrap().as_slice(),
315 "foobar".as_bytes());
316 assert_eq!("Zm9vYg==\r\n".from_base64().unwrap().as_slice(),
317 "foob".as_bytes());
318 }
319
320 #[test]
321 fn test_from_base64_urlsafe() {
322 assert_eq!("-_8".from_base64().unwrap(), "+/8=".from_base64().unwrap());
323 }
324
325 #[test]
326 fn test_from_base64_invalid_char() {
327 assert!("Zm$=".from_base64().is_err())
328 assert!("Zg==$".from_base64().is_err());
329 }
330
331 #[test]
332 fn test_from_base64_invalid_padding() {
333 assert!("Z===".from_base64().is_err());
334 }
335
336 #[test]
337 fn test_base64_random() {
338 use self::rand::{task_rng, random, Rng};
339
340 for _ in range(0, 1000) {
341 let times = task_rng().gen_range(1u, 100);
342 let v = Vec::from_fn(times, |_| random::<u8>());
343 assert_eq!(v.as_slice().to_base64(STANDARD).from_base64().unwrap().as_slice(),
344 v.as_slice());
345 }
346 }
347
348 #[bench]
349 pub fn bench_to_base64(b: &mut Bencher) {
350 let s = "ã¤ãããããã ããªãã«ã² ã¯ã«ã¨ã¿ã¬ã½ ãããã©ã \
351 ã¦ã°ããªã¯ã¤ã ã±ãã³ã¨ã ã¢ãµãã¦ã¡ãã· ã±ãã¢ã»ã¹ã³";
352 b.iter(|| {
353 s.as_bytes().to_base64(STANDARD);
354 });
355 b.bytes = s.len() as u64;
356 }
357
358 #[bench]
359 pub fn bench_from_base64(b: &mut Bencher) {
360 let s = "ã¤ãããããã ããªãã«ã² ã¯ã«ã¨ã¿ã¬ã½ ãããã©ã \
361 ã¦ã°ããªã¯ã¤ã ã±ãã³ã¨ã ã¢ãµãã¦ã¡ãã· ã±ãã¢ã»ã¹ã³";
362 let sb = s.as_bytes().to_base64(STANDARD);
363 b.iter(|| {
364 sb.from_base64().unwrap();
365 });
366 b.bytes = sb.len() as u64;
367 }
368
369 }
libserialize/base64.rs:161:64-161:64 -enum- definition:
/// Errors that can occur when decoding a base64 encoded string
pub enum FromBase64Error {
/// The input contained a character not part of the base64 format
references:- 3157: /// an owned vector of bytes, returning the vector.
158: fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error>;
159: }
--
208: */
209: fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> {
210: let mut r = Vec::new();
libserialize/base64.rs:23:55-23:55 -struct- definition:
/// Contains configuration parameters for `to_base64`.
pub struct Config {
/// Character set to use
references:- 838: pub static URL_SAFE: Config =
39: Config {char_set: UrlSafe, pad: false, line_length: None};
--
42: pub static MIME: Config =
43: Config {char_set: Standard, pad: true, line_length: Some(76)};
--
56: /// format configuration, returning the owned string.
57: fn to_base64(&self, config: Config) -> ~str;
58: }
--
75: */
76: fn to_base64(&self, config: Config) -> ~str {
77: let bytes = match config.char_set {