| #[forbid(deprecated_mode)]; |
| use io::Reader; |
| |
| pub trait ToBase64 { |
| fn to_base64() -> ~str; |
| } |
| |
| impl &[u8]: ToBase64 { |
| fn to_base64() -> ~str { |
| let chars = str::chars( |
| ~"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" |
| ); |
| |
| let len = self.len(); |
| let mut s = ~""; |
| str::reserve(&mut s, ((len + 3u) / 4u) * 3u); |
| |
| let mut i = 0u; |
| |
| while i < len - (len % 3u) { |
| let n = (self[i] as uint) << 16u | |
| (self[i + 1u] as uint) << 8u | |
| (self[i + 2u] as uint); |
| |
| // This 24-bit number gets separated into four 6-bit numbers. |
| str::push_char(&mut s, chars[(n >> 18u) & 63u]); |
| str::push_char(&mut s, chars[(n >> 12u) & 63u]); |
| str::push_char(&mut s, chars[(n >> 6u) & 63u]); |
| str::push_char(&mut s, chars[n & 63u]); |
| |
| i += 3u; |
| } |
| |
| // Heh, would be cool if we knew this was exhaustive |
| // (the dream of bounded integer types) |
| match len % 3 { |
| 0 => (), |
| 1 => { |
| let n = (self[i] as uint) << 16u; |
| str::push_char(&mut s, chars[(n >> 18u) & 63u]); |
| str::push_char(&mut s, chars[(n >> 12u) & 63u]); |
| str::push_char(&mut s, '='); |
| str::push_char(&mut s, '='); |
| } |
| 2 => { |
| let n = (self[i] as uint) << 16u | (self[i + 1u] as uint) << 8u; |
| str::push_char(&mut s, chars[(n >> 18u) & 63u]); |
| str::push_char(&mut s, chars[(n >> 12u) & 63u]); |
| str::push_char(&mut s, chars[(n >> 6u) & 63u]); |
| str::push_char(&mut s, '='); |
| } |
| _ => fail ~"Algebra is broken, please alert the math police" |
| } |
| |
| s |
| } |
| } |
| |
| impl &str: ToBase64 { |
| fn to_base64() -> ~str { |
| str::to_bytes(self).to_base64() |
| } |
| } |
| |
| pub trait FromBase64 { |
| fn from_base64() -> ~[u8]; |
| } |
| |
| impl ~[u8]: FromBase64 { |
| fn from_base64() -> ~[u8] { |
| if self.len() % 4u != 0u { fail ~"invalid base64 length"; } |
| |
| let len = self.len(); |
| let mut padding = 0u; |
| |
| if len != 0u { |
| if self[len - 1u] == '=' as u8 { padding += 1u; } |
| if self[len - 2u] == '=' as u8 { padding += 1u; } |
| } |
| |
| let mut r = vec::with_capacity((len / 4u) * 3u - padding); |
| |
| let mut i = 0u; |
| while i < len { |
| let mut n = 0u; |
| |
| for iter::repeat(4u) { |
| let ch = self[i] as char; |
| n <<= 6u; |
| |
| if ch >= 'A' && ch <= 'Z' { |
| n |= (ch as uint) - 0x41u; |
| } else if ch >= 'a' && ch <= 'z' { |
| n |= (ch as uint) - 0x47u; |
| } else if ch >= '0' && ch <= '9' { |
| n |= (ch as uint) + 0x04u; |
| } else if ch == '+' { |
| n |= 0x3Eu; |
| } else if ch == '/' { |
| n |= 0x3Fu; |
| } else if ch == '=' { |
| match len - i { |
| 1u => { |
| r.push(((n >> 16u) & 0xFFu) as u8); |
| r.push(((n >> 8u ) & 0xFFu) as u8); |
| return copy r; |
| } |
| 2u => { |
| r.push(((n >> 10u) & 0xFFu) as u8); |
| return copy r; |
| } |
| _ => fail ~"invalid base64 padding" |
| } |
| } else { |
| fail ~"invalid base64 character"; |
| } |
| |
| i += 1u; |
| }; |
| |
| r.push(((n >> 16u) & 0xFFu) as u8); |
| r.push(((n >> 8u ) & 0xFFu) as u8); |
| r.push(((n ) & 0xFFu) as u8); |
| } |
| |
| r |
| } |
| } |
| |
| impl ~str: FromBase64 { |
| fn from_base64() -> ~[u8] { |
| str::to_bytes(self).from_base64() |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| #[legacy_exports]; |
| #[test] |
| fn test_to_base64() { |
| assert (~"").to_base64() == ~""; |
| assert (~"f").to_base64() == ~"Zg=="; |
| assert (~"fo").to_base64() == ~"Zm8="; |
| assert (~"foo").to_base64() == ~"Zm9v"; |
| assert (~"foob").to_base64() == ~"Zm9vYg=="; |
| assert (~"fooba").to_base64() == ~"Zm9vYmE="; |
| assert (~"foobar").to_base64() == ~"Zm9vYmFy"; |
| } |
| |
| #[test] |
| fn test_from_base64() { |
| assert (~"").from_base64() == str::to_bytes(~""); |
| assert (~"Zg==").from_base64() == str::to_bytes(~"f"); |
| assert (~"Zm8=").from_base64() == str::to_bytes(~"fo"); |
| assert (~"Zm9v").from_base64() == str::to_bytes(~"foo"); |
| assert (~"Zm9vYg==").from_base64() == str::to_bytes(~"foob"); |
| assert (~"Zm9vYmE=").from_base64() == str::to_bytes(~"fooba"); |
| assert (~"Zm9vYmFy").from_base64() == str::to_bytes(~"foobar"); |
| } |
| } |