(index<- )        ./libserialize/base64.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   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.
   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  //! Base64 binary-to-text encoding
  12  use std::str;
  13  use std::fmt;
  14  
  15  /// Available encoding character sets
  16  pub enum CharacterSet {
  17      /// The standard character set (uses `+` and `/`)
  18      Standard,
  19      /// The URL safe character set (uses `-` and `_`)
  20      UrlSafe
  21  }
  22  
  23  /// Contains configuration parameters for `to_base64`.
  24  pub struct Config {
  25      /// Character set to use
  26      pub char_set: CharacterSet,
  27      /// True to pad output with `=` characters
  28      pub pad: bool,
  29      /// `Some(len)` to wrap lines at `len`, `None` to disable line wrapping
  30      pub line_length: Option<uint>
  31  }
  32  
  33  /// Configuration for RFC 4648 standard base64 encoding
  34  pub static STANDARD: Config =
  35      Config {char_set: Standard, pad: true, line_length: None};
  36  
  37  /// Configuration for RFC 4648 base64url encoding
  38  pub static URL_SAFE: Config =
  39      Config {char_set: UrlSafe, pad: false, line_length: None};
  40  
  41  /// Configuration for RFC 2045 MIME base64 encoding
  42  pub static MIME: Config =
  43      Config {char_set: Standard, pad: true, line_length: Some(76)};
  44  
  45  static STANDARD_CHARS: &'static[u8] = bytes!("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
  46                                               "abcdefghijklmnopqrstuvwxyz",
  47                                               "0123456789+/");
  48  
  49  static URLSAFE_CHARS: &'static[u8] = bytes!("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
  50                                              "abcdefghijklmnopqrstuvwxyz",
  51                                              "0123456789-_");
  52  
  53  /// A trait for converting a value to base64 encoding.
  54  pub trait ToBase64 {
  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;
  58  }
  59  
  60  impl<'a> ToBase64 for &'a [u8] {
  61      /**
  62       * Turn a vector of `u8` bytes into a base64 string.
  63       *
  64       * # Example
  65       *
  66       * ```rust
  67       * extern crate serialize;
  68       * use serialize::base64::{ToBase64, STANDARD};
  69       *
  70       * fn main () {
  71       *     let str = [52,32].to_base64(STANDARD);
  72       *     println!("base 64 output: {}", str);
  73       * }
  74       * ```
  75       */
  76      fn to_base64(&self, configConfig) -> ~str {
  77          let bytes = match config.char_set {
  78              Standard => STANDARD_CHARS,
  79              UrlSafe => URLSAFE_CHARS
  80          };
  81  
  82          let mut v = Vec::new();
  83          let mut i = 0;
  84          let mut cur_length = 0;
  85          let len = self.len();
  86          while i < len - (len % 3) {
  87              match config.line_length {
  88                  Some(line_length) =>
  89                      if cur_length >= line_length {
  90                          v.push('\r' as u8);
  91                          v.push('\n' as u8);
  92                          cur_length = 0;
  93                      },
  94                  None => ()
  95              }
  96  
  97              let n = (self[i] as u32) << 16 |
  98                      (self[i + 1] as u32) << 8 |
  99                      (self[i + 2] as u32);
 100  
 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]);
 106  
 107              cur_length += 4;
 108              i += 3;
 109          }
 110  
 111          if len % 3 != 0 {
 112              match config.line_length {
 113                  Some(line_length) =>
 114                      if cur_length >= line_length {
 115                          v.push('\r' as u8);
 116                          v.push('\n' as u8);
 117                      },
 118                  None => ()
 119              }
 120          }
 121  
 122          // Heh, would be cool if we knew this was exhaustive
 123          // (the dream of bounded integer types)
 124          match len % 3 {
 125              0 => (),
 126              1 => {
 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]);
 130                  if config.pad {
 131                      v.push('=' as u8);
 132                      v.push('=' as u8);
 133                  }
 134              }
 135              2 => {
 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]);
 141                  if config.pad {
 142                      v.push('=' as u8);
 143                  }
 144              }
 145              _ => fail!("Algebra is broken, please alert the math police")
 146          }
 147  
 148          unsafe {
 149              str::raw::from_utf8(v.as_slice()).to_owned()
 150          }
 151      }
 152  }
 153  
 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>;
 159  }
 160  
 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
 166      InvalidBase64Length,
 167  }
 168  
 169  impl fmt::Show for FromBase64Error {
 170      fn fmt(&self, f&mut fmt::Formatter) -> fmt::Result {
 171          match *self {
 172              InvalidBase64Character(ch, idx) =>
 173                  write!(f.buf, "Invalid character '{}' at position {}", ch, idx),
 174              InvalidBase64Length => write!(f.buf, "Invalid length"),
 175          }
 176      }
 177  }
 178  
 179  impl<'a> FromBase64 for &'a str {
 180      /**
 181       * Convert any base64 encoded string (literal, `@`, `&`, or `~`)
 182       * to the byte values it encodes.
 183       *
 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
 186       * values.
 187       *
 188       * # Example
 189       *
 190       * This converts a string literal to base64 and back.
 191       *
 192       * ```rust
 193       * extern crate serialize;
 194       * use serialize::base64::{ToBase64, FromBase64, STANDARD};
 195       *
 196       * fn main () {
 197       *     let hello_str = bytes!("Hello, World").to_base64(STANDARD);
 198       *     println!("base64 output: {}", hello_str);
 199       *     let res = hello_str.from_base64();
 200       *     if res.is_ok() {
 201       *       let opt_bytes = StrBuf::from_utf8(res.unwrap());
 202       *       if opt_bytes.is_some() {
 203       *         println!("decoded from base64: {}", opt_bytes.unwrap());
 204       *       }
 205       *     }
 206       * }
 207       * ```
 208       */
 209      fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> {
 210          let mut r = Vec::new();
 211          let mut bufu32 = 0;
 212          let mut modulus = 0;
 213  
 214          let mut it = self.bytes().enumerate();
 215          for (idx, byte) in it {
 216              let val = byte as u32;
 217  
 218              match byte as char {
 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,
 225                  '=' => break,
 226                  _ => return Err(InvalidBase64Character(self.char_at(idx), idx)),
 227              }
 228  
 229              buf <<= 6;
 230              modulus += 1;
 231              if modulus == 4 {
 232                  modulus = 0;
 233                  r.push((buf >> 22) as u8);
 234                  r.push((buf >> 14) as u8);
 235                  r.push((buf >> 6 ) as u8);
 236              }
 237          }
 238  
 239          for (idx, byte) in it {
 240              match byte as char {
 241                  '='|'\r'|'\n' => continue,
 242                  _ => return Err(InvalidBase64Character(self.char_at(idx), idx)),
 243              }
 244          }
 245  
 246          match modulus {
 247              2 => {
 248                  r.push((buf >> 10) as u8);
 249              }
 250              3 => {
 251                  r.push((buf >> 16) as u8);
 252                  r.push((buf >> 8 ) as u8);
 253              }
 254              0 => (),
 255              _ => return Err(InvalidBase64Length),
 256          }
 257  
 258          Ok(r)
 259      }
 260  }
 261  
 262  #[cfg(test)]
 263  mod tests {
 264      extern crate test;
 265      extern crate rand;
 266      use self::test::Bencher;
 267      use base64::{Config, FromBase64, ToBase64, STANDARD, URL_SAFE};
 268  
 269      #[test]
 270      fn test_to_base64_basic() {
 271          assert_eq!("".as_bytes().to_base64(STANDARD), "".to_owned());
 272          assert_eq!("f".as_bytes().to_base64(STANDARD), "Zg==".to_owned());
 273          assert_eq!("fo".as_bytes().to_base64(STANDARD), "Zm8=".to_owned());
 274          assert_eq!("foo".as_bytes().to_base64(STANDARD), "Zm9v".to_owned());
 275          assert_eq!("foob".as_bytes().to_base64(STANDARD), "Zm9vYg==".to_owned());
 276          assert_eq!("fooba".as_bytes().to_base64(STANDARD), "Zm9vYmE=".to_owned());
 277          assert_eq!("foobar".as_bytes().to_base64(STANDARD), "Zm9vYmFy".to_owned());
 278      }
 279  
 280      #[test]
 281      fn test_to_base64_line_break() {
 282          assert!(![0u8, ..1000].to_base64(Config {line_length: None, ..STANDARD})
 283                  .contains("\r\n"));
 284          assert_eq!("foobar".as_bytes().to_base64(Config {line_length: Some(4),
 285                                                           ..STANDARD}),
 286                     "Zm9v\r\nYmFy".to_owned());
 287      }
 288  
 289      #[test]
 290      fn test_to_base64_padding() {
 291          assert_eq!("f".as_bytes().to_base64(Config {pad: false, ..STANDARD}), "Zg".to_owned());
 292          assert_eq!("fo".as_bytes().to_base64(Config {pad: false, ..STANDARD}), "Zm8".to_owned());
 293      }
 294  
 295      #[test]
 296      fn test_to_base64_url_safe() {
 297          assert_eq!([251, 255].to_base64(URL_SAFE), "-_8".to_owned());
 298          assert_eq!([251, 255].to_base64(STANDARD), "+/8=".to_owned());
 299      }
 300  
 301      #[test]
 302      fn test_from_base64_basic() {
 303          assert_eq!("".from_base64().unwrap().as_slice(), "".as_bytes());
 304          assert_eq!("Zg==".from_base64().unwrap().as_slice(), "f".as_bytes());
 305          assert_eq!("Zm8=".from_base64().unwrap().as_slice(), "fo".as_bytes());
 306          assert_eq!("Zm9v".from_base64().unwrap().as_slice(), "foo".as_bytes());
 307          assert_eq!("Zm9vYg==".from_base64().unwrap().as_slice(), "foob".as_bytes());
 308          assert_eq!("Zm9vYmE=".from_base64().unwrap().as_slice(), "fooba".as_bytes());
 309          assert_eq!("Zm9vYmFy".from_base64().unwrap().as_slice(), "foobar".as_bytes());
 310      }
 311  
 312      #[test]
 313      fn test_from_base64_newlines() {
 314          assert_eq!("Zm9v\r\nYmFy".from_base64().unwrap().as_slice(),
 315                     "foobar".as_bytes());
 316          assert_eq!("Zm9vYg==\r\n".from_base64().unwrap().as_slice(),
 317                     "foob".as_bytes());
 318      }
 319  
 320      #[test]
 321      fn test_from_base64_urlsafe() {
 322          assert_eq!("-_8".from_base64().unwrap(), "+/8=".from_base64().unwrap());
 323      }
 324  
 325      #[test]
 326      fn test_from_base64_invalid_char() {
 327          assert!("Zm$=".from_base64().is_err())
 328          assert!("Zg==$".from_base64().is_err());
 329      }
 330  
 331      #[test]
 332      fn test_from_base64_invalid_padding() {
 333          assert!("Z===".from_base64().is_err());
 334      }
 335  
 336      #[test]
 337      fn test_base64_random() {
 338          use self::rand::{task_rng, random, Rng};
 339  
 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.as_slice().to_base64(STANDARD).from_base64().unwrap().as_slice(),
 344                         v.as_slice());
 345          }
 346      }
 347  
 348      #[bench]
 349      pub fn bench_to_base64(b: &mut Bencher) {
 350          let s = "イロハニホヘト ãƒãƒªãƒŒãƒ«ãƒ² ãƒ¯ã‚«ãƒ¨ã‚¿ãƒ¬ã‚½ ãƒ„ネナラム \
 351                   ã‚¦ãƒ°ãƒŽã‚ªã‚¯ãƒ¤ãƒž ã‚±ãƒ•ã‚³ã‚¨ãƒ† ã‚¢ã‚µã‚­ãƒ¦ãƒ¡ãƒŸã‚· ãƒ±ãƒ’モセスン";
 352          b.iter(|| {
 353              s.as_bytes().to_base64(STANDARD);
 354          });
 355          b.bytes = s.len() as u64;
 356      }
 357  
 358      #[bench]
 359      pub fn bench_from_base64(b: &mut Bencher) {
 360          let s = "イロハニホヘト ãƒãƒªãƒŒãƒ«ãƒ² ãƒ¯ã‚«ãƒ¨ã‚¿ãƒ¬ã‚½ ãƒ„ネナラム \
 361                   ã‚¦ãƒ°ãƒŽã‚ªã‚¯ãƒ¤ãƒž ã‚±ãƒ•ã‚³ã‚¨ãƒ† ã‚¢ã‚µã‚­ãƒ¦ãƒ¡ãƒŸã‚· ãƒ±ãƒ’モセスン";
 362          let sb = s.as_bytes().to_base64(STANDARD);
 363          b.iter(|| {
 364              sb.from_base64().unwrap();
 365          });
 366          b.bytes = sb.len() as u64;
 367      }
 368  
 369  }