]> git.lizzy.rs Git - rust.git/blob - src/libserialize/base64.rs
rollup merge of #20642: michaelwoerister/sane-source-locations-pt1
[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 {
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 {
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, Show)]
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::Display 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
207 impl FromBase64 for str {
208     /// Convert any base64 encoded string (literal, `@`, `&`, or `~`)
209     /// to the byte values it encodes.
210     ///
211     /// You can use the `String::from_utf8` function to turn a `Vec<u8>` into a
212     /// string with characters corresponding to those values.
213     ///
214     /// # Example
215     ///
216     /// This converts a string literal to base64 and back.
217     ///
218     /// ```rust
219     /// extern crate serialize;
220     /// use serialize::base64::{ToBase64, FromBase64, STANDARD};
221     ///
222     /// fn main () {
223     ///     let hello_str = b"Hello, World".to_base64(STANDARD);
224     ///     println!("base64 output: {}", hello_str);
225     ///     let res = hello_str.as_slice().from_base64();
226     ///     if res.is_ok() {
227     ///       let opt_bytes = String::from_utf8(res.unwrap());
228     ///       if opt_bytes.is_ok() {
229     ///         println!("decoded from base64: {}", opt_bytes.unwrap());
230     ///       }
231     ///     }
232     /// }
233     /// ```
234     #[inline]
235     fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> {
236         self.as_bytes().from_base64()
237     }
238 }
239
240 impl FromBase64 for [u8] {
241     fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> {
242         let mut r = Vec::with_capacity(self.len());
243         let mut buf: u32 = 0;
244         let mut modulus = 0i;
245
246         let mut it = self.iter().enumerate();
247         for (idx, &byte) in it {
248             let val = byte as u32;
249
250             match byte {
251                 b'A'...b'Z' => buf |= val - 0x41,
252                 b'a'...b'z' => buf |= val - 0x47,
253                 b'0'...b'9' => buf |= val + 0x04,
254                 b'+' | b'-' => buf |= 0x3E,
255                 b'/' | b'_' => buf |= 0x3F,
256                 b'\r' | b'\n' => continue,
257                 b'=' => break,
258                 _ => return Err(InvalidBase64Byte(self[idx], idx)),
259             }
260
261             buf <<= 6;
262             modulus += 1;
263             if modulus == 4 {
264                 modulus = 0;
265                 r.push((buf >> 22) as u8);
266                 r.push((buf >> 14) as u8);
267                 r.push((buf >> 6 ) as u8);
268             }
269         }
270
271         for (idx, &byte) in it {
272             match byte {
273                 b'=' | b'\r' | b'\n' => continue,
274                 _ => return Err(InvalidBase64Byte(self[idx], idx)),
275             }
276         }
277
278         match modulus {
279             2 => {
280                 r.push((buf >> 10) as u8);
281             }
282             3 => {
283                 r.push((buf >> 16) as u8);
284                 r.push((buf >> 8 ) as u8);
285             }
286             0 => (),
287             _ => return Err(InvalidBase64Length),
288         }
289
290         Ok(r)
291     }
292 }
293
294 #[cfg(test)]
295 mod tests {
296     extern crate test;
297     use self::test::Bencher;
298     use base64::{Config, Newline, FromBase64, ToBase64, STANDARD, URL_SAFE};
299
300     #[test]
301     fn test_to_base64_basic() {
302         assert_eq!("".as_bytes().to_base64(STANDARD), "");
303         assert_eq!("f".as_bytes().to_base64(STANDARD), "Zg==");
304         assert_eq!("fo".as_bytes().to_base64(STANDARD), "Zm8=");
305         assert_eq!("foo".as_bytes().to_base64(STANDARD), "Zm9v");
306         assert_eq!("foob".as_bytes().to_base64(STANDARD), "Zm9vYg==");
307         assert_eq!("fooba".as_bytes().to_base64(STANDARD), "Zm9vYmE=");
308         assert_eq!("foobar".as_bytes().to_base64(STANDARD), "Zm9vYmFy");
309     }
310
311     #[test]
312     fn test_to_base64_crlf_line_break() {
313         assert!(![0u8; 1000].to_base64(Config {line_length: None, ..STANDARD})
314                               .contains("\r\n"));
315         assert_eq!(b"foobar".to_base64(Config {line_length: Some(4),
316                                                ..STANDARD}),
317                    "Zm9v\r\nYmFy");
318     }
319
320     #[test]
321     fn test_to_base64_lf_line_break() {
322         assert!(![0u8; 1000].to_base64(Config {line_length: None,
323                                                  newline: Newline::LF,
324                                                  ..STANDARD})
325                               .as_slice()
326                               .contains("\n"));
327         assert_eq!(b"foobar".to_base64(Config {line_length: Some(4),
328                                                newline: Newline::LF,
329                                                ..STANDARD}),
330                    "Zm9v\nYmFy");
331     }
332
333     #[test]
334     fn test_to_base64_padding() {
335         assert_eq!("f".as_bytes().to_base64(Config {pad: false, ..STANDARD}), "Zg");
336         assert_eq!("fo".as_bytes().to_base64(Config {pad: false, ..STANDARD}), "Zm8");
337     }
338
339     #[test]
340     fn test_to_base64_url_safe() {
341         assert_eq!([251, 255].to_base64(URL_SAFE), "-_8");
342         assert_eq!([251, 255].to_base64(STANDARD), "+/8=");
343     }
344
345     #[test]
346     fn test_from_base64_basic() {
347         assert_eq!("".from_base64().unwrap(), b"");
348         assert_eq!("Zg==".from_base64().unwrap(), b"f");
349         assert_eq!("Zm8=".from_base64().unwrap(), b"fo");
350         assert_eq!("Zm9v".from_base64().unwrap(), b"foo");
351         assert_eq!("Zm9vYg==".from_base64().unwrap(), b"foob");
352         assert_eq!("Zm9vYmE=".from_base64().unwrap(), b"fooba");
353         assert_eq!("Zm9vYmFy".from_base64().unwrap(), b"foobar");
354     }
355
356     #[test]
357     fn test_from_base64_bytes() {
358         assert_eq!(b"Zm9vYmFy".from_base64().unwrap(), b"foobar");
359     }
360
361     #[test]
362     fn test_from_base64_newlines() {
363         assert_eq!("Zm9v\r\nYmFy".from_base64().unwrap(),
364                    b"foobar");
365         assert_eq!("Zm9vYg==\r\n".from_base64().unwrap(),
366                    b"foob");
367         assert_eq!("Zm9v\nYmFy".from_base64().unwrap(),
368                    b"foobar");
369         assert_eq!("Zm9vYg==\n".from_base64().unwrap(),
370                    b"foob");
371     }
372
373     #[test]
374     fn test_from_base64_urlsafe() {
375         assert_eq!("-_8".from_base64().unwrap(), "+/8=".from_base64().unwrap());
376     }
377
378     #[test]
379     fn test_from_base64_invalid_char() {
380         assert!("Zm$=".from_base64().is_err());
381         assert!("Zg==$".from_base64().is_err());
382     }
383
384     #[test]
385     fn test_from_base64_invalid_padding() {
386         assert!("Z===".from_base64().is_err());
387     }
388
389     #[test]
390     fn test_base64_random() {
391         use std::rand::{thread_rng, random, Rng};
392
393         for _ in range(0u, 1000) {
394             let times = thread_rng().gen_range(1u, 100);
395             let v = thread_rng().gen_iter::<u8>().take(times).collect::<Vec<_>>();
396             assert_eq!(v.to_base64(STANDARD)
397                         .from_base64()
398                         .unwrap(),
399                        v);
400         }
401     }
402
403     #[bench]
404     pub fn bench_to_base64(b: &mut Bencher) {
405         let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
406                  ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
407         b.iter(|| {
408             s.as_bytes().to_base64(STANDARD);
409         });
410         b.bytes = s.len() as u64;
411     }
412
413     #[bench]
414     pub fn bench_from_base64(b: &mut Bencher) {
415         let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
416                  ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
417         let sb = s.as_bytes().to_base64(STANDARD);
418         b.iter(|| {
419             sb.from_base64().unwrap();
420         });
421         b.bytes = sb.len() as u64;
422     }
423
424 }