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 pub char_set: CharacterSet,
27 /// True to pad output with `=` characters
29 /// `Some(len)` to wrap lines at `len`, `None` to disable line wrapping
30 pub 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] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
46 abcdefghijklmnopqrstuvwxyz\
49 static URLSAFE_CHARS: &'static[u8] = b"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) -> String;
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) -> String {
77 let bytes = match config.char_set {
78 Standard => STANDARD_CHARS,
79 UrlSafe => URLSAFE_CHARS
82 let mut v = Vec::new();
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) 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]);
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) as uint]);
129 v.push(bytes[((n >> 12) & 63) as uint]);
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]);
145 _ => fail!("Algebra is broken, please alert the math police")
149 str::raw::from_utf8(v.as_slice()).to_string()
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>;
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, "Invalid character '{}' at position {}", ch, idx),
174 InvalidBase64Length => write!(f, "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 `String::from_utf8` function in `std::string` to turn a
185 * `Vec<u8>` into a string with characters corresponding to those values.
189 * This converts a string literal to base64 and back.
192 * extern crate serialize;
193 * use serialize::base64::{ToBase64, FromBase64, STANDARD};
196 * let hello_str = b"Hello, World".to_base64(STANDARD);
197 * println!("base64 output: {}", hello_str);
198 * let res = hello_str.as_slice().from_base64();
200 * let opt_bytes = String::from_utf8(res.unwrap());
201 * if opt_bytes.is_ok() {
202 * println!("decoded from base64: {}", opt_bytes.unwrap());
208 fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> {
209 let mut r = Vec::new();
210 let mut buf: u32 = 0;
213 let mut it = self.bytes().enumerate();
214 for (idx, byte) in it {
215 let val = byte as u32;
218 'A'..'Z' => buf |= val - 0x41,
219 'a'..'z' => buf |= val - 0x47,
220 '0'..'9' => buf |= val + 0x04,
221 '+'|'-' => buf |= 0x3E,
222 '/'|'_' => buf |= 0x3F,
223 '\r'|'\n' => continue,
225 _ => return Err(InvalidBase64Character(self.char_at(idx), idx)),
232 r.push((buf >> 22) as u8);
233 r.push((buf >> 14) as u8);
234 r.push((buf >> 6 ) as u8);
238 for (idx, byte) in it {
240 '='|'\r'|'\n' => continue,
241 _ => return Err(InvalidBase64Character(self.char_at(idx), idx)),
247 r.push((buf >> 10) as u8);
250 r.push((buf >> 16) as u8);
251 r.push((buf >> 8 ) as u8);
254 _ => return Err(InvalidBase64Length),
264 use self::test::Bencher;
265 use base64::{Config, FromBase64, ToBase64, STANDARD, URL_SAFE};
268 fn test_to_base64_basic() {
269 assert_eq!("".as_bytes().to_base64(STANDARD), "".to_string());
270 assert_eq!("f".as_bytes().to_base64(STANDARD), "Zg==".to_string());
271 assert_eq!("fo".as_bytes().to_base64(STANDARD), "Zm8=".to_string());
272 assert_eq!("foo".as_bytes().to_base64(STANDARD), "Zm9v".to_string());
273 assert_eq!("foob".as_bytes().to_base64(STANDARD), "Zm9vYg==".to_string());
274 assert_eq!("fooba".as_bytes().to_base64(STANDARD), "Zm9vYmE=".to_string());
275 assert_eq!("foobar".as_bytes().to_base64(STANDARD), "Zm9vYmFy".to_string());
279 fn test_to_base64_line_break() {
280 assert!(![0u8, ..1000].to_base64(Config {line_length: None, ..STANDARD})
283 assert_eq!("foobar".as_bytes().to_base64(Config {line_length: Some(4),
285 "Zm9v\r\nYmFy".to_string());
289 fn test_to_base64_padding() {
290 assert_eq!("f".as_bytes().to_base64(Config {pad: false, ..STANDARD}), "Zg".to_string());
291 assert_eq!("fo".as_bytes().to_base64(Config {pad: false, ..STANDARD}), "Zm8".to_string());
295 fn test_to_base64_url_safe() {
296 assert_eq!([251, 255].to_base64(URL_SAFE), "-_8".to_string());
297 assert_eq!([251, 255].to_base64(STANDARD), "+/8=".to_string());
301 fn test_from_base64_basic() {
302 assert_eq!("".from_base64().unwrap().as_slice(), "".as_bytes());
303 assert_eq!("Zg==".from_base64().unwrap().as_slice(), "f".as_bytes());
304 assert_eq!("Zm8=".from_base64().unwrap().as_slice(), "fo".as_bytes());
305 assert_eq!("Zm9v".from_base64().unwrap().as_slice(), "foo".as_bytes());
306 assert_eq!("Zm9vYg==".from_base64().unwrap().as_slice(), "foob".as_bytes());
307 assert_eq!("Zm9vYmE=".from_base64().unwrap().as_slice(), "fooba".as_bytes());
308 assert_eq!("Zm9vYmFy".from_base64().unwrap().as_slice(), "foobar".as_bytes());
312 fn test_from_base64_newlines() {
313 assert_eq!("Zm9v\r\nYmFy".from_base64().unwrap().as_slice(),
314 "foobar".as_bytes());
315 assert_eq!("Zm9vYg==\r\n".from_base64().unwrap().as_slice(),
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};
339 for _ in range(0, 1000) {
340 let times = task_rng().gen_range(1u, 100);
341 let v = Vec::from_fn(times, |_| random::<u8>());
342 assert_eq!(v.as_slice()
353 pub fn bench_to_base64(b: &mut Bencher) {
354 let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
355 ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
357 s.as_bytes().to_base64(STANDARD);
359 b.bytes = s.len() as u64;
363 pub fn bench_from_base64(b: &mut Bencher) {
364 let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
365 ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
366 let sb = s.as_bytes().to_base64(STANDARD);
368 sb.as_slice().from_base64().unwrap();
370 b.bytes = sb.len() as u64;