]> git.lizzy.rs Git - rust.git/blob - src/libserialize/base64.rs
Merge pull request #20510 from tshepang/patch-6
[rust.git] / src / libserialize / base64.rs
1 // Copyright 2012-2014 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 // ignore-lexer-test FIXME #15679
12
13 //! Base64 binary-to-text encoding
14
15 pub use self::FromBase64Error::*;
16 pub use self::CharacterSet::*;
17
18 use std::fmt;
19 use std::error;
20
21 /// Available encoding character sets
22 #[derive(Copy)]
23 pub enum CharacterSet {
24     /// The standard character set (uses `+` and `/`)
25     Standard,
26     /// The URL safe character set (uses `-` and `_`)
27     UrlSafe
28 }
29
30 /// Available newline types
31 #[derive(Copy)]
32 pub enum Newline {
33     /// A linefeed (i.e. Unix-style newline)
34     LF,
35     /// A carriage return and a linefeed (i.e. Windows-style newline)
36     CRLF
37 }
38
39 /// Contains configuration parameters for `to_base64`.
40 #[derive(Copy)]
41 pub struct Config {
42     /// Character set to use
43     pub char_set: CharacterSet,
44     /// Newline to use
45     pub newline: Newline,
46     /// True to pad output with `=` characters
47     pub pad: bool,
48     /// `Some(len)` to wrap lines at `len`, `None` to disable line wrapping
49     pub line_length: Option<uint>
50 }
51
52 /// Configuration for RFC 4648 standard base64 encoding
53 pub static STANDARD: Config =
54     Config {char_set: Standard, newline: Newline::CRLF, pad: true, line_length: None};
55
56 /// Configuration for RFC 4648 base64url encoding
57 pub static URL_SAFE: Config =
58     Config {char_set: UrlSafe, newline: Newline::CRLF, pad: false, line_length: None};
59
60 /// Configuration for RFC 2045 MIME base64 encoding
61 pub static MIME: Config =
62     Config {char_set: Standard, newline: Newline::CRLF, pad: true, line_length: Some(76)};
63
64 static STANDARD_CHARS: &'static[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
65                                         abcdefghijklmnopqrstuvwxyz\
66                                         0123456789+/";
67
68 static URLSAFE_CHARS: &'static[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
69                                        abcdefghijklmnopqrstuvwxyz\
70                                        0123456789-_";
71
72 /// A trait for converting a value to base64 encoding.
73 pub trait ToBase64 for Sized? {
74     /// Converts the value of `self` to a base64 value following the specified
75     /// format configuration, returning the owned string.
76     fn to_base64(&self, config: Config) -> String;
77 }
78
79 impl ToBase64 for [u8] {
80     /// Turn a vector of `u8` bytes into a base64 string.
81     ///
82     /// # Example
83     ///
84     /// ```rust
85     /// extern crate serialize;
86     /// use serialize::base64::{ToBase64, STANDARD};
87     ///
88     /// fn main () {
89     ///     let str = [52,32].to_base64(STANDARD);
90     ///     println!("base 64 output: {}", str);
91     /// }
92     /// ```
93     fn to_base64(&self, config: Config) -> String {
94         let bytes = match config.char_set {
95             Standard => STANDARD_CHARS,
96             UrlSafe => URLSAFE_CHARS
97         };
98
99         // In general, this Vec only needs (4/3) * self.len() memory, but
100         // addition is faster than multiplication and division.
101         let mut v = Vec::with_capacity(self.len() + self.len());
102         let mut i = 0;
103         let mut cur_length = 0;
104         let len = self.len();
105         let mod_len = len % 3;
106         let cond_len = len - mod_len;
107         let newline = match config.newline {
108             Newline::LF => b"\n",
109             Newline::CRLF => b"\r\n"
110         };
111         while i < cond_len {
112             let (first, second, third) = (self[i], self[i + 1], self[i + 2]);
113             if let Some(line_length) = config.line_length {
114                 if cur_length >= line_length {
115                     v.push_all(newline);
116                     cur_length = 0;
117                 }
118             }
119
120             let n = (first  as u32) << 16 |
121                     (second as u32) << 8 |
122                     (third  as u32);
123
124             // This 24-bit number gets separated into four 6-bit numbers.
125             v.push(bytes[((n >> 18) & 63) as uint]);
126             v.push(bytes[((n >> 12) & 63) as uint]);
127             v.push(bytes[((n >> 6 ) & 63) as uint]);
128             v.push(bytes[(n & 63) as uint]);
129
130             cur_length += 4;
131             i += 3;
132         }
133
134         if mod_len != 0 {
135             if let Some(line_length) = config.line_length {
136                 if cur_length >= line_length {
137                     v.push_all(newline);
138                 }
139             }
140         }
141
142         // Heh, would be cool if we knew this was exhaustive
143         // (the dream of bounded integer types)
144         match mod_len {
145             0 => (),
146             1 => {
147                 let n = (self[i] as u32) << 16;
148                 v.push(bytes[((n >> 18) & 63) as uint]);
149                 v.push(bytes[((n >> 12) & 63) as uint]);
150                 if config.pad {
151                     v.push(b'=');
152                     v.push(b'=');
153                 }
154             }
155             2 => {
156                 let n = (self[i] as u32) << 16 |
157                     (self[i + 1u] as u32) << 8;
158                 v.push(bytes[((n >> 18) & 63) as uint]);
159                 v.push(bytes[((n >> 12) & 63) as uint]);
160                 v.push(bytes[((n >> 6 ) & 63) as uint]);
161                 if config.pad {
162                     v.push(b'=');
163                 }
164             }
165             _ => panic!("Algebra is broken, please alert the math police")
166         }
167
168         unsafe { String::from_utf8_unchecked(v) }
169     }
170 }
171
172 /// A trait for converting from base64 encoded values.
173 pub trait FromBase64 for Sized? {
174     /// Converts the value of `self`, interpreted as base64 encoded data, into
175     /// an owned vector of bytes, returning the vector.
176     fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error>;
177 }
178
179 /// Errors that can occur when decoding a base64 encoded string
180 #[derive(Copy)]
181 pub enum FromBase64Error {
182     /// The input contained a character not part of the base64 format
183     InvalidBase64Byte(u8, uint),
184     /// The input had an invalid length
185     InvalidBase64Length,
186 }
187
188 impl fmt::Show for FromBase64Error {
189     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
190         match *self {
191             InvalidBase64Byte(ch, idx) =>
192                 write!(f, "Invalid character '{}' at position {}", ch, idx),
193             InvalidBase64Length => write!(f, "Invalid length"),
194         }
195     }
196 }
197
198 impl error::Error for FromBase64Error {
199     fn description(&self) -> &str {
200         match *self {
201             InvalidBase64Byte(_, _) => "invalid character",
202             InvalidBase64Length => "invalid length",
203         }
204     }
205
206     fn detail(&self) -> Option<String> {
207         Some(self.to_string())
208     }
209 }
210
211 impl FromBase64 for str {
212     /// Convert any base64 encoded string (literal, `@`, `&`, or `~`)
213     /// to the byte values it encodes.
214     ///
215     /// You can use the `String::from_utf8` function to turn a `Vec<u8>` into a
216     /// string with characters corresponding to those values.
217     ///
218     /// # Example
219     ///
220     /// This converts a string literal to base64 and back.
221     ///
222     /// ```rust
223     /// extern crate serialize;
224     /// use serialize::base64::{ToBase64, FromBase64, STANDARD};
225     ///
226     /// fn main () {
227     ///     let hello_str = b"Hello, World".to_base64(STANDARD);
228     ///     println!("base64 output: {}", hello_str);
229     ///     let res = hello_str.as_slice().from_base64();
230     ///     if res.is_ok() {
231     ///       let opt_bytes = String::from_utf8(res.unwrap());
232     ///       if opt_bytes.is_ok() {
233     ///         println!("decoded from base64: {}", opt_bytes.unwrap());
234     ///       }
235     ///     }
236     /// }
237     /// ```
238     #[inline]
239     fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> {
240         self.as_bytes().from_base64()
241     }
242 }
243
244 impl FromBase64 for [u8] {
245     fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> {
246         let mut r = Vec::with_capacity(self.len());
247         let mut buf: u32 = 0;
248         let mut modulus = 0i;
249
250         let mut it = self.iter().enumerate();
251         for (idx, &byte) in it {
252             let val = byte as u32;
253
254             match byte {
255                 b'A'...b'Z' => buf |= val - 0x41,
256                 b'a'...b'z' => buf |= val - 0x47,
257                 b'0'...b'9' => buf |= val + 0x04,
258                 b'+' | b'-' => buf |= 0x3E,
259                 b'/' | b'_' => buf |= 0x3F,
260                 b'\r' | b'\n' => continue,
261                 b'=' => break,
262                 _ => return Err(InvalidBase64Byte(self[idx], idx)),
263             }
264
265             buf <<= 6;
266             modulus += 1;
267             if modulus == 4 {
268                 modulus = 0;
269                 r.push((buf >> 22) as u8);
270                 r.push((buf >> 14) as u8);
271                 r.push((buf >> 6 ) as u8);
272             }
273         }
274
275         for (idx, &byte) in it {
276             match byte {
277                 b'=' | b'\r' | b'\n' => continue,
278                 _ => return Err(InvalidBase64Byte(self[idx], idx)),
279             }
280         }
281
282         match modulus {
283             2 => {
284                 r.push((buf >> 10) as u8);
285             }
286             3 => {
287                 r.push((buf >> 16) as u8);
288                 r.push((buf >> 8 ) as u8);
289             }
290             0 => (),
291             _ => return Err(InvalidBase64Length),
292         }
293
294         Ok(r)
295     }
296 }
297
298 #[cfg(test)]
299 mod tests {
300     extern crate test;
301     use self::test::Bencher;
302     use base64::{Config, Newline, FromBase64, ToBase64, STANDARD, URL_SAFE};
303
304     #[test]
305     fn test_to_base64_basic() {
306         assert_eq!("".as_bytes().to_base64(STANDARD), "");
307         assert_eq!("f".as_bytes().to_base64(STANDARD), "Zg==");
308         assert_eq!("fo".as_bytes().to_base64(STANDARD), "Zm8=");
309         assert_eq!("foo".as_bytes().to_base64(STANDARD), "Zm9v");
310         assert_eq!("foob".as_bytes().to_base64(STANDARD), "Zm9vYg==");
311         assert_eq!("fooba".as_bytes().to_base64(STANDARD), "Zm9vYmE=");
312         assert_eq!("foobar".as_bytes().to_base64(STANDARD), "Zm9vYmFy");
313     }
314
315     #[test]
316     fn test_to_base64_crlf_line_break() {
317         assert!(![0u8; 1000].to_base64(Config {line_length: None, ..STANDARD})
318                               .contains("\r\n"));
319         assert_eq!(b"foobar".to_base64(Config {line_length: Some(4),
320                                                ..STANDARD}),
321                    "Zm9v\r\nYmFy");
322     }
323
324     #[test]
325     fn test_to_base64_lf_line_break() {
326         assert!(![0u8; 1000].to_base64(Config {line_length: None,
327                                                  newline: Newline::LF,
328                                                  ..STANDARD})
329                               .as_slice()
330                               .contains("\n"));
331         assert_eq!(b"foobar".to_base64(Config {line_length: Some(4),
332                                                newline: Newline::LF,
333                                                ..STANDARD}),
334                    "Zm9v\nYmFy");
335     }
336
337     #[test]
338     fn test_to_base64_padding() {
339         assert_eq!("f".as_bytes().to_base64(Config {pad: false, ..STANDARD}), "Zg");
340         assert_eq!("fo".as_bytes().to_base64(Config {pad: false, ..STANDARD}), "Zm8");
341     }
342
343     #[test]
344     fn test_to_base64_url_safe() {
345         assert_eq!([251, 255].to_base64(URL_SAFE), "-_8");
346         assert_eq!([251, 255].to_base64(STANDARD), "+/8=");
347     }
348
349     #[test]
350     fn test_from_base64_basic() {
351         assert_eq!("".from_base64().unwrap(), b"");
352         assert_eq!("Zg==".from_base64().unwrap(), b"f");
353         assert_eq!("Zm8=".from_base64().unwrap(), b"fo");
354         assert_eq!("Zm9v".from_base64().unwrap(), b"foo");
355         assert_eq!("Zm9vYg==".from_base64().unwrap(), b"foob");
356         assert_eq!("Zm9vYmE=".from_base64().unwrap(), b"fooba");
357         assert_eq!("Zm9vYmFy".from_base64().unwrap(), b"foobar");
358     }
359
360     #[test]
361     fn test_from_base64_bytes() {
362         assert_eq!(b"Zm9vYmFy".from_base64().unwrap(), b"foobar");
363     }
364
365     #[test]
366     fn test_from_base64_newlines() {
367         assert_eq!("Zm9v\r\nYmFy".from_base64().unwrap(),
368                    b"foobar");
369         assert_eq!("Zm9vYg==\r\n".from_base64().unwrap(),
370                    b"foob");
371         assert_eq!("Zm9v\nYmFy".from_base64().unwrap(),
372                    b"foobar");
373         assert_eq!("Zm9vYg==\n".from_base64().unwrap(),
374                    b"foob");
375     }
376
377     #[test]
378     fn test_from_base64_urlsafe() {
379         assert_eq!("-_8".from_base64().unwrap(), "+/8=".from_base64().unwrap());
380     }
381
382     #[test]
383     fn test_from_base64_invalid_char() {
384         assert!("Zm$=".from_base64().is_err());
385         assert!("Zg==$".from_base64().is_err());
386     }
387
388     #[test]
389     fn test_from_base64_invalid_padding() {
390         assert!("Z===".from_base64().is_err());
391     }
392
393     #[test]
394     fn test_base64_random() {
395         use std::rand::{thread_rng, random, Rng};
396
397         for _ in range(0u, 1000) {
398             let times = thread_rng().gen_range(1u, 100);
399             let v = thread_rng().gen_iter::<u8>().take(times).collect::<Vec<_>>();
400             assert_eq!(v.to_base64(STANDARD)
401                         .from_base64()
402                         .unwrap(),
403                        v);
404         }
405     }
406
407     #[bench]
408     pub fn bench_to_base64(b: &mut Bencher) {
409         let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
410                  ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
411         b.iter(|| {
412             s.as_bytes().to_base64(STANDARD);
413         });
414         b.bytes = s.len() as u64;
415     }
416
417     #[bench]
418     pub fn bench_from_base64(b: &mut Bencher) {
419         let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
420                  ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
421         let sb = s.as_bytes().to_base64(STANDARD);
422         b.iter(|| {
423             sb.from_base64().unwrap();
424         });
425         b.bytes = sb.len() as u64;
426     }
427
428 }