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.
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.
11 //! Base64 binary-to-text encoding
14 /// Available encoding character sets
15 pub enum CharacterSet {
16 /// The standard character set (uses `+` and `/`)
18 /// The URL safe character set (uses `-` and `_`)
22 /// Contains configuration parameters for `to_base64`.
24 /// Character set to use
25 char_set: CharacterSet,
26 /// True to pad output with `=` characters
28 /// `Some(len)` to wrap lines at `len`, `None` to disable line wrapping
29 line_length: Option<uint>
32 /// Configuration for RFC 4648 standard base64 encoding
33 pub static STANDARD: Config =
34 Config {char_set: Standard, pad: true, line_length: None};
36 /// Configuration for RFC 4648 base64url encoding
37 pub static URL_SAFE: Config =
38 Config {char_set: UrlSafe, pad: false, line_length: None};
40 /// Configuration for RFC 2045 MIME base64 encoding
41 pub static MIME: Config =
42 Config {char_set: Standard, pad: true, line_length: Some(76)};
44 static STANDARD_CHARS: &'static[u8] = bytes!("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
45 "abcdefghijklmnopqrstuvwxyz",
48 static URLSAFE_CHARS: &'static[u8] = bytes!("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
49 "abcdefghijklmnopqrstuvwxyz",
52 /// A trait for converting a value to base64 encoding.
54 /// Converts the value of `self` to a base64 value following the specified
55 /// format configuration, returning the owned string.
56 fn to_base64(&self, config: Config) -> ~str;
59 impl<'a> ToBase64 for &'a [u8] {
61 * Turn a vector of `u8` bytes into a base64 string.
66 * extern crate serialize;
67 * use serialize::base64::{ToBase64, STANDARD};
70 * let str = [52,32].to_base64(STANDARD);
71 * println!("base 64 output: {}", str);
75 fn to_base64(&self, config: Config) -> ~str {
76 let bytes = match config.char_set {
77 Standard => STANDARD_CHARS,
78 UrlSafe => URLSAFE_CHARS
81 let mut v: ~[u8] = ~[];
83 let mut cur_length = 0;
85 while i < len - (len % 3) {
86 match config.line_length {
88 if cur_length >= line_length {
96 let n = (self[i] as u32) << 16 |
97 (self[i + 1] as u32) << 8 |
100 // This 24-bit number gets separated into four 6-bit numbers.
101 v.push(bytes[(n >> 18) & 63]);
102 v.push(bytes[(n >> 12) & 63]);
103 v.push(bytes[(n >> 6 ) & 63]);
104 v.push(bytes[n & 63]);
111 match config.line_length {
113 if cur_length >= line_length {
121 // Heh, would be cool if we knew this was exhaustive
122 // (the dream of bounded integer types)
126 let n = (self[i] as u32) << 16;
127 v.push(bytes[(n >> 18) & 63]);
128 v.push(bytes[(n >> 12) & 63]);
135 let n = (self[i] as u32) << 16 |
136 (self[i + 1u] as u32) << 8;
137 v.push(bytes[(n >> 18) & 63]);
138 v.push(bytes[(n >> 12) & 63]);
139 v.push(bytes[(n >> 6 ) & 63]);
144 _ => fail!("Algebra is broken, please alert the math police")
148 str::raw::from_utf8_owned(v)
153 /// A trait for converting from base64 encoded values.
154 pub trait FromBase64 {
155 /// Converts the value of `self`, interpreted as base64 encoded data, into
156 /// an owned vector of bytes, returning the vector.
157 fn from_base64(&self) -> Result<~[u8], FromBase64Error>;
160 /// Errors that can occur when decoding a base64 encoded string
161 pub enum FromBase64Error {
162 /// The input contained a character not part of the base64 format
163 InvalidBase64Character(char, uint),
164 /// The input had an invalid length
168 impl ToStr for FromBase64Error {
169 fn to_str(&self) -> ~str {
171 InvalidBase64Character(ch, idx) =>
172 format!("Invalid character '{}' at position {}", ch, idx),
173 InvalidBase64Length => ~"Invalid length",
178 impl<'a> FromBase64 for &'a str {
180 * Convert any base64 encoded string (literal, `@`, `&`, or `~`)
181 * to the byte values it encodes.
183 * You can use the `from_utf8_owned` function in `std::str`
184 * to turn a `[u8]` into a string with characters corresponding to those
189 * This converts a string literal to base64 and back.
192 * extern crate serialize;
193 * use serialize::base64::{ToBase64, FromBase64, STANDARD};
197 * let hello_str = bytes!("Hello, World").to_base64(STANDARD);
198 * println!("base64 output: {}", hello_str);
199 * let res = hello_str.from_base64();
201 * let optBytes = str::from_utf8_owned(res.unwrap());
202 * if optBytes.is_some() {
203 * println!("decoded from base64: {}", optBytes.unwrap());
209 fn from_base64(&self) -> Result<~[u8], FromBase64Error> {
211 let mut buf: u32 = 0;
214 let mut it = self.bytes().enumerate();
215 for (idx, byte) in it {
216 let val = byte as u32;
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,
226 _ => return Err(InvalidBase64Character(self.char_at(idx), idx)),
233 r.push((buf >> 22) as u8);
234 r.push((buf >> 14) as u8);
235 r.push((buf >> 6 ) as u8);
239 for (idx, byte) in it {
241 '='|'\r'|'\n' => continue,
242 _ => return Err(InvalidBase64Character(self.char_at(idx), idx)),
248 r.push((buf >> 10) as u8);
251 r.push((buf >> 16) as u8);
252 r.push((buf >> 8 ) as u8);
255 _ => return Err(InvalidBase64Length),
265 use self::test::BenchHarness;
266 use base64::{Config, FromBase64, ToBase64, STANDARD, URL_SAFE};
269 fn test_to_base64_basic() {
270 assert_eq!("".as_bytes().to_base64(STANDARD), ~"");
271 assert_eq!("f".as_bytes().to_base64(STANDARD), ~"Zg==");
272 assert_eq!("fo".as_bytes().to_base64(STANDARD), ~"Zm8=");
273 assert_eq!("foo".as_bytes().to_base64(STANDARD), ~"Zm9v");
274 assert_eq!("foob".as_bytes().to_base64(STANDARD), ~"Zm9vYg==");
275 assert_eq!("fooba".as_bytes().to_base64(STANDARD), ~"Zm9vYmE=");
276 assert_eq!("foobar".as_bytes().to_base64(STANDARD), ~"Zm9vYmFy");
280 fn test_to_base64_line_break() {
281 assert!(![0u8, ..1000].to_base64(Config {line_length: None, ..STANDARD})
283 assert_eq!("foobar".as_bytes().to_base64(Config {line_length: Some(4),
289 fn test_to_base64_padding() {
290 assert_eq!("f".as_bytes().to_base64(Config {pad: false, ..STANDARD}), ~"Zg");
291 assert_eq!("fo".as_bytes().to_base64(Config {pad: false, ..STANDARD}), ~"Zm8");
295 fn test_to_base64_url_safe() {
296 assert_eq!([251, 255].to_base64(URL_SAFE), ~"-_8");
297 assert_eq!([251, 255].to_base64(STANDARD), ~"+/8=");
301 fn test_from_base64_basic() {
302 assert_eq!("".from_base64().unwrap(), "".as_bytes().to_owned());
303 assert_eq!("Zg==".from_base64().unwrap(), "f".as_bytes().to_owned());
304 assert_eq!("Zm8=".from_base64().unwrap(), "fo".as_bytes().to_owned());
305 assert_eq!("Zm9v".from_base64().unwrap(), "foo".as_bytes().to_owned());
306 assert_eq!("Zm9vYg==".from_base64().unwrap(), "foob".as_bytes().to_owned());
307 assert_eq!("Zm9vYmE=".from_base64().unwrap(), "fooba".as_bytes().to_owned());
308 assert_eq!("Zm9vYmFy".from_base64().unwrap(), "foobar".as_bytes().to_owned());
312 fn test_from_base64_newlines() {
313 assert_eq!("Zm9v\r\nYmFy".from_base64().unwrap(),
314 "foobar".as_bytes().to_owned());
315 assert_eq!("Zm9vYg==\r\n".from_base64().unwrap(),
316 "foob".as_bytes().to_owned());
320 fn test_from_base64_urlsafe() {
321 assert_eq!("-_8".from_base64().unwrap(), "+/8=".from_base64().unwrap());
325 fn test_from_base64_invalid_char() {
326 assert!("Zm$=".from_base64().is_err())
327 assert!("Zg==$".from_base64().is_err());
331 fn test_from_base64_invalid_padding() {
332 assert!("Z===".from_base64().is_err());
336 fn test_base64_random() {
337 use std::rand::{task_rng, random, Rng};
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.to_base64(STANDARD).from_base64().unwrap(), v);
348 pub fn bench_to_base64(bh: & mut BenchHarness) {
349 let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
350 ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
352 s.as_bytes().to_base64(STANDARD);
354 bh.bytes = s.len() as u64;
358 pub fn bench_from_base64(bh: & mut BenchHarness) {
359 let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
360 ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
361 let b = s.as_bytes().to_base64(STANDARD);
363 b.from_base64().unwrap();
365 bh.bytes = b.len() as u64;