base64.rs 4.8 KB
Newer Older
K
Kevin Cantu 已提交
1 2
#[forbid(deprecated_mode)];
#[forbid(deprecated_pattern)];
P
Patrick Walton 已提交
3
use io::Reader;
E
Erick Tryzelaar 已提交
4

5
pub trait ToBase64 {
6
    fn to_base64() -> ~str;
E
Erick Tryzelaar 已提交
7 8
}

9
impl &[u8]: ToBase64 {
10
    fn to_base64() -> ~str {
E
Erick Tryzelaar 已提交
11
        let chars = str::chars(
12
          ~"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
E
Erick Tryzelaar 已提交
13 14 15
        );

        let len = self.len();
16
        let mut s = ~"";
P
Patrick Walton 已提交
17
        str::reserve(&mut s, ((len + 3u) / 4u) * 3u);
E
Erick Tryzelaar 已提交
18 19 20 21 22 23 24 25 26

        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.
P
Patrick Walton 已提交
27 28 29 30
            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]);
E
Erick Tryzelaar 已提交
31 32 33 34

            i += 3u;
        }

T
Tim Chevalier 已提交
35 36 37 38 39
        // Heh, would be cool if we knew this was exhaustive
        // (the dream of bounded integer types)
        match len % 3 {
          0 => (),
          1 => {
E
Erick Tryzelaar 已提交
40
            let n = (self[i] as uint) << 16u;
P
Patrick Walton 已提交
41 42 43 44
            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, '=');
E
Erick Tryzelaar 已提交
45
          }
T
Tim Chevalier 已提交
46
          2 => {
E
Erick Tryzelaar 已提交
47
            let n = (self[i] as uint) << 16u | (self[i + 1u] as uint) << 8u;
P
Patrick Walton 已提交
48 49 50 51
            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, '=');
E
Erick Tryzelaar 已提交
52
          }
T
Tim Chevalier 已提交
53
          _ => fail ~"Algebra is broken, please alert the math police"
E
Erick Tryzelaar 已提交
54 55 56 57 58 59
        }

        s
    }
}

60
impl &str: ToBase64 {
61
    fn to_base64() -> ~str {
62
        str::to_bytes(self).to_base64()
E
Erick Tryzelaar 已提交
63 64 65
    }
}

66
pub trait FromBase64 {
E
Erick Tryzelaar 已提交
67 68 69
    fn from_base64() -> ~[u8];
}

B
Ben Striegel 已提交
70
impl ~[u8]: FromBase64 {
E
Erick Tryzelaar 已提交
71
    fn from_base64() -> ~[u8] {
72
        if self.len() % 4u != 0u { fail ~"invalid base64 length"; }
E
Erick Tryzelaar 已提交
73 74 75 76 77 78 79 80 81

        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; }
        }

82
        let mut r = vec::with_capacity((len / 4u) * 3u - padding);
E
Erick Tryzelaar 已提交
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102

        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 == '=' {
103
                    match len - i {
B
Brian Anderson 已提交
104
                      1u => {
105 106
                        r.push(((n >> 16u) & 0xFFu) as u8);
                        r.push(((n >> 8u ) & 0xFFu) as u8);
B
Brian Anderson 已提交
107
                        return copy r;
E
Erick Tryzelaar 已提交
108
                      }
B
Brian Anderson 已提交
109
                      2u => {
110
                        r.push(((n >> 10u) & 0xFFu) as u8);
B
Brian Anderson 已提交
111
                        return copy r;
E
Erick Tryzelaar 已提交
112
                      }
B
Brian Anderson 已提交
113
                      _ => fail ~"invalid base64 padding"
E
Erick Tryzelaar 已提交
114 115
                    }
                } else {
116
                    fail ~"invalid base64 character";
E
Erick Tryzelaar 已提交
117 118 119 120 121
                }

                i += 1u;
            };

122 123 124
            r.push(((n >> 16u) & 0xFFu) as u8);
            r.push(((n >> 8u ) & 0xFFu) as u8);
            r.push(((n       ) & 0xFFu) as u8);
E
Erick Tryzelaar 已提交
125 126 127 128 129 130
        }

        r
    }
}

B
Ben Striegel 已提交
131
impl ~str: FromBase64 {
E
Erick Tryzelaar 已提交
132
    fn from_base64() -> ~[u8] {
133
        str::to_bytes(self).from_base64()
E
Erick Tryzelaar 已提交
134 135 136 137 138
    }
}

#[cfg(test)]
mod tests {
139
    #[legacy_exports];
E
Erick Tryzelaar 已提交
140 141
    #[test]
    fn test_to_base64() {
142 143 144 145 146 147 148
        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";
E
Erick Tryzelaar 已提交
149 150 151 152
    }

    #[test]
    fn test_from_base64() {
153 154 155 156 157 158 159
        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");
E
Erick Tryzelaar 已提交
160 161
    }
}