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
15 /// Available encoding character sets
16 pub enum CharacterSet {
17 /// The standard character set (uses `+` and `/`)
19 /// The URL safe character set (uses `-` and `_`)
23 /// Contains configuration parameters for `to_base64`.
25 /// Character set to use
26 char_set: CharacterSet,
27 /// True to pad output with `=` characters
29 /// `Some(len)` to wrap lines at `len`, `None` to disable line wrapping
30 line_length: Option<uint>
33 /// Configuration for RFC 4648 standard base64 encoding
34 pub static STANDARD: Config =
35 Config {char_set: Standard, pad: true, line_length: None};
37 /// Configuration for RFC 4648 base64url encoding
38 pub static URL_SAFE: Config =
39 Config {char_set: UrlSafe, pad: false, line_length: None};
41 /// Configuration for RFC 2045 MIME base64 encoding
42 pub static MIME: Config =
43 Config {char_set: Standard, pad: true, line_length: Some(76)};
45 static STANDARD_CHARS: &'static[u8] = bytes!("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
46 "abcdefghijklmnopqrstuvwxyz",
49 static URLSAFE_CHARS: &'static[u8] = bytes!("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
50 "abcdefghijklmnopqrstuvwxyz",
53 /// A trait for converting a value to base64 encoding.
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;
60 impl<'a> ToBase64 for &'a [u8] {
62 * Turn a vector of `u8` bytes into a base64 string.
67 * extern crate serialize;
68 * use serialize::base64::{ToBase64, STANDARD};
71 * let str = [52,32].to_base64(STANDARD);
72 * println!("base 64 output: {}", str);
76 fn to_base64(&self, config: Config) -> ~str {
77 let bytes = match config.char_set {
78 Standard => STANDARD_CHARS,
79 UrlSafe => URLSAFE_CHARS
82 let mut v: ~[u8] = ~[];
84 let mut cur_length = 0;
86 while i < len - (len % 3) {
87 match config.line_length {
89 if cur_length >= line_length {
97 let n = (self[i] as u32) << 16 |
98 (self[i + 1] as u32) << 8 |
101 // This 24-bit number gets separated into four 6-bit numbers.
102 v.push(bytes[(n >> 18) & 63]);
103 v.push(bytes[(n >> 12) & 63]);
104 v.push(bytes[(n >> 6 ) & 63]);
105 v.push(bytes[n & 63]);
112 match config.line_length {
114 if cur_length >= line_length {
122 // Heh, would be cool if we knew this was exhaustive
123 // (the dream of bounded integer types)
127 let n = (self[i] as u32) << 16;
128 v.push(bytes[(n >> 18) & 63]);
129 v.push(bytes[(n >> 12) & 63]);
136 let n = (self[i] as u32) << 16 |
137 (self[i + 1u] as u32) << 8;
138 v.push(bytes[(n >> 18) & 63]);
139 v.push(bytes[(n >> 12) & 63]);
140 v.push(bytes[(n >> 6 ) & 63]);
145 _ => fail!("Algebra is broken, please alert the math police")
149 str::raw::from_utf8_owned(v)
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<~[u8], FromBase64Error>;
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
169 impl fmt::Show for FromBase64Error {
170 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
172 InvalidBase64Character(ch, idx) =>
173 write!(f.buf, "Invalid character '{}' at position {}", ch, idx),
174 InvalidBase64Length => write!(f.buf, "Invalid length"),
179 impl<'a> FromBase64 for &'a str {
181 * Convert any base64 encoded string (literal, `@`, `&`, or `~`)
182 * to the byte values it encodes.
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
190 * This converts a string literal to base64 and back.
193 * extern crate serialize;
194 * use serialize::base64::{ToBase64, FromBase64, STANDARD};
198 * let hello_str = bytes!("Hello, World").to_base64(STANDARD);
199 * println!("base64 output: {}", hello_str);
200 * let res = hello_str.from_base64();
202 * let optBytes = str::from_utf8_owned(res.unwrap());
203 * if optBytes.is_some() {
204 * println!("decoded from base64: {}", optBytes.unwrap());
210 fn from_base64(&self) -> Result<~[u8], FromBase64Error> {
212 let mut buf: u32 = 0;
215 let mut it = self.bytes().enumerate();
216 for (idx, byte) in it {
217 let val = byte as u32;
220 'A'..'Z' => buf |= val - 0x41,
221 'a'..'z' => buf |= val - 0x47,
222 '0'..'9' => buf |= val + 0x04,
223 '+'|'-' => buf |= 0x3E,
224 '/'|'_' => buf |= 0x3F,
225 '\r'|'\n' => continue,
227 _ => return Err(InvalidBase64Character(self.char_at(idx), idx)),
234 r.push((buf >> 22) as u8);
235 r.push((buf >> 14) as u8);
236 r.push((buf >> 6 ) as u8);
240 for (idx, byte) in it {
242 '='|'\r'|'\n' => continue,
243 _ => return Err(InvalidBase64Character(self.char_at(idx), idx)),
249 r.push((buf >> 10) as u8);
252 r.push((buf >> 16) as u8);
253 r.push((buf >> 8 ) as u8);
256 _ => return Err(InvalidBase64Length),
266 use self::test::BenchHarness;
267 use base64::{Config, FromBase64, ToBase64, STANDARD, URL_SAFE};
270 fn test_to_base64_basic() {
271 assert_eq!("".as_bytes().to_base64(STANDARD), ~"");
272 assert_eq!("f".as_bytes().to_base64(STANDARD), ~"Zg==");
273 assert_eq!("fo".as_bytes().to_base64(STANDARD), ~"Zm8=");
274 assert_eq!("foo".as_bytes().to_base64(STANDARD), ~"Zm9v");
275 assert_eq!("foob".as_bytes().to_base64(STANDARD), ~"Zm9vYg==");
276 assert_eq!("fooba".as_bytes().to_base64(STANDARD), ~"Zm9vYmE=");
277 assert_eq!("foobar".as_bytes().to_base64(STANDARD), ~"Zm9vYmFy");
281 fn test_to_base64_line_break() {
282 assert!(![0u8, ..1000].to_base64(Config {line_length: None, ..STANDARD})
284 assert_eq!("foobar".as_bytes().to_base64(Config {line_length: Some(4),
290 fn test_to_base64_padding() {
291 assert_eq!("f".as_bytes().to_base64(Config {pad: false, ..STANDARD}), ~"Zg");
292 assert_eq!("fo".as_bytes().to_base64(Config {pad: false, ..STANDARD}), ~"Zm8");
296 fn test_to_base64_url_safe() {
297 assert_eq!([251, 255].to_base64(URL_SAFE), ~"-_8");
298 assert_eq!([251, 255].to_base64(STANDARD), ~"+/8=");
302 fn test_from_base64_basic() {
303 assert_eq!("".from_base64().unwrap(), "".as_bytes().to_owned());
304 assert_eq!("Zg==".from_base64().unwrap(), "f".as_bytes().to_owned());
305 assert_eq!("Zm8=".from_base64().unwrap(), "fo".as_bytes().to_owned());
306 assert_eq!("Zm9v".from_base64().unwrap(), "foo".as_bytes().to_owned());
307 assert_eq!("Zm9vYg==".from_base64().unwrap(), "foob".as_bytes().to_owned());
308 assert_eq!("Zm9vYmE=".from_base64().unwrap(), "fooba".as_bytes().to_owned());
309 assert_eq!("Zm9vYmFy".from_base64().unwrap(), "foobar".as_bytes().to_owned());
313 fn test_from_base64_newlines() {
314 assert_eq!("Zm9v\r\nYmFy".from_base64().unwrap(),
315 "foobar".as_bytes().to_owned());
316 assert_eq!("Zm9vYg==\r\n".from_base64().unwrap(),
317 "foob".as_bytes().to_owned());
321 fn test_from_base64_urlsafe() {
322 assert_eq!("-_8".from_base64().unwrap(), "+/8=".from_base64().unwrap());
326 fn test_from_base64_invalid_char() {
327 assert!("Zm$=".from_base64().is_err())
328 assert!("Zg==$".from_base64().is_err());
332 fn test_from_base64_invalid_padding() {
333 assert!("Z===".from_base64().is_err());
337 fn test_base64_random() {
338 use std::rand::{task_rng, random, Rng};
341 for _ in range(0, 1000) {
342 let times = task_rng().gen_range(1u, 100);
343 let v = vec::from_fn(times, |_| random::<u8>());
344 assert_eq!(v.to_base64(STANDARD).from_base64().unwrap(), v);
349 pub fn bench_to_base64(bh: & mut BenchHarness) {
350 let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
351 ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
353 s.as_bytes().to_base64(STANDARD);
355 bh.bytes = s.len() as u64;
359 pub fn bench_from_base64(bh: & mut BenchHarness) {
360 let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
361 ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
362 let b = s.as_bytes().to_base64(STANDARD);
364 b.from_base64().unwrap();
366 bh.bytes = b.len() as u64;