99// except according to those terms.
1010
1111//! Base64 binary-to-text encoding
12+ use std:: str;
1213
1314/// Available encoding character sets
1415pub enum CharacterSet {
@@ -40,21 +41,13 @@ pub static URL_SAFE: Config =
4041pub static MIME : Config =
4142 Config { char_set : Standard , pad : true , line_length : Some ( 76 ) } ;
4243
43- static STANDARD_CHARS : [ char , ..64 ] = [
44- 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' , 'H' , 'I' , 'J' , 'K' , 'L' , 'M' ,
45- 'N' , 'O' , 'P' , 'Q' , 'R' , 'S' , 'T' , 'U' , 'V' , 'W' , 'X' , 'Y' , 'Z' ,
46- 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' ,
47- 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' ,
48- '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , '+' , '/'
49- ] ;
50-
51- static URLSAFE_CHARS : [ char , ..64 ] = [
52- 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' , 'H' , 'I' , 'J' , 'K' , 'L' , 'M' ,
53- 'N' , 'O' , 'P' , 'Q' , 'R' , 'S' , 'T' , 'U' , 'V' , 'W' , 'X' , 'Y' , 'Z' ,
54- 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' ,
55- 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' ,
56- '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , '-' , '_'
57- ] ;
44+ static STANDARD_CHARS : & ' static [ u8 ] = bytes ! ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ,
45+ "abcdefghijklmnopqrstuvwxyz" ,
46+ "0123456789+/" ) ;
47+
48+ static URLSAFE_CHARS : & ' static [ u8 ] = bytes ! ( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ,
49+ "abcdefghijklmnopqrstuvwxyz" ,
50+ "0123456789-_" ) ;
5851
5952/// A trait for converting a value to base64 encoding.
6053pub trait ToBase64 {
@@ -80,20 +73,21 @@ impl<'self> ToBase64 for &'self [u8] {
8073 * ~~~
8174 */
8275 fn to_base64 ( & self , config : Config ) -> ~str {
83- let chars = match config. char_set {
76+ let bytes = match config. char_set {
8477 Standard => STANDARD_CHARS ,
8578 UrlSafe => URLSAFE_CHARS
8679 } ;
8780
88- let mut s = ~"" ;
81+ let mut v : ~ [ u8 ] = ~[ ] ;
8982 let mut i = 0 ;
9083 let mut cur_length = 0 ;
9184 let len = self . len ( ) ;
9285 while i < len - ( len % 3 ) {
9386 match config. line_length {
9487 Some ( line_length) =>
9588 if cur_length >= line_length {
96- s. push_str ( "\r \n " ) ;
89+ v. push ( '\r' as u8 ) ;
90+ v. push ( '\n' as u8 ) ;
9791 cur_length = 0 ;
9892 } ,
9993 None => ( )
@@ -104,10 +98,10 @@ impl<'self> ToBase64 for &'self [u8] {
10498 ( self [ i + 2 ] as u32 ) ;
10599
106100 // This 24-bit number gets separated into four 6-bit numbers.
107- s . push_char ( chars [ ( n >> 18 ) & 63 ] ) ;
108- s . push_char ( chars [ ( n >> 12 ) & 63 ] ) ;
109- s . push_char ( chars [ ( n >> 6 ) & 63 ] ) ;
110- s . push_char ( chars [ n & 63 ] ) ;
101+ v . push ( bytes [ ( n >> 18 ) & 63 ] ) ;
102+ v . push ( bytes [ ( n >> 12 ) & 63 ] ) ;
103+ v . push ( bytes [ ( n >> 6 ) & 63 ] ) ;
104+ v . push ( bytes [ n & 63 ] ) ;
111105
112106 cur_length += 4 ;
113107 i += 3 ;
@@ -117,7 +111,8 @@ impl<'self> ToBase64 for &'self [u8] {
117111 match config. line_length {
118112 Some ( line_length) =>
119113 if cur_length >= line_length {
120- s. push_str ( "\r \n " ) ;
114+ v. push ( '\r' as u8 ) ;
115+ v. push ( '\n' as u8 ) ;
121116 } ,
122117 None => ( )
123118 }
@@ -129,48 +124,29 @@ impl<'self> ToBase64 for &'self [u8] {
129124 0 => ( ) ,
130125 1 => {
131126 let n = ( self [ i] as u32 ) << 16 ;
132- s . push_char ( chars [ ( n >> 18 ) & 63 ] ) ;
133- s . push_char ( chars [ ( n >> 12 ) & 63 ] ) ;
127+ v . push ( bytes [ ( n >> 18 ) & 63 ] ) ;
128+ v . push ( bytes [ ( n >> 12 ) & 63 ] ) ;
134129 if config. pad {
135- s. push_str ( "==" ) ;
130+ v. push ( '=' as u8 ) ;
131+ v. push ( '=' as u8 ) ;
136132 }
137133 }
138134 2 => {
139135 let n = ( self [ i] as u32 ) << 16 |
140136 ( self [ i + 1 u] as u32 ) << 8 ;
141- s . push_char ( chars [ ( n >> 18 ) & 63 ] ) ;
142- s . push_char ( chars [ ( n >> 12 ) & 63 ] ) ;
143- s . push_char ( chars [ ( n >> 6 ) & 63 ] ) ;
137+ v . push ( bytes [ ( n >> 18 ) & 63 ] ) ;
138+ v . push ( bytes [ ( n >> 12 ) & 63 ] ) ;
139+ v . push ( bytes [ ( n >> 6 ) & 63 ] ) ;
144140 if config. pad {
145- s . push_char ( '=' ) ;
141+ v . push ( '=' as u8 ) ;
146142 }
147143 }
148144 _ => fail ! ( "Algebra is broken, please alert the math police" )
149145 }
150- s
151- }
152- }
153146
154- impl < ' self > ToBase64 for & ' self str {
155- /**
156- * Convert any string (literal, `@`, `&`, or `~`) to base64 encoding.
157- *
158- *
159- * # Example
160- *
161- * ~~~ {.rust}
162- * extern mod extra;
163- * use extra::base64::{ToBase64, standard};
164- *
165- * fn main () {
166- * let str = "Hello, World".to_base64(standard);
167- * printfln!("%s", str);
168- * }
169- * ~~~
170- *
171- */
172- fn to_base64 ( & self , config : Config ) -> ~str {
173- self . as_bytes ( ) . to_base64 ( config)
147+ unsafe {
148+ str:: raw:: from_bytes_owned ( v)
149+ }
174150 }
175151}
176152
@@ -181,22 +157,31 @@ pub trait FromBase64 {
181157 fn from_base64 ( & self ) -> Result < ~[ u8 ] , ~str > ;
182158}
183159
184- impl < ' self > FromBase64 for & ' self [ u8 ] {
160+ impl < ' self > FromBase64 for & ' self str {
185161 /**
186- * Convert base64 `u8` vector into u8 byte values.
187- * Every 4 encoded characters is converted into 3 octets, modulo padding.
162+ * Convert any base64 encoded string (literal, `@`, `&`, or `~`)
163+ * to the byte values it encodes.
164+ *
165+ * You can use the `from_bytes` function in `std::str`
166+ * to turn a `[u8]` into a string with characters corresponding to those
167+ * values.
188168 *
189169 * # Example
190170 *
171+ * This converts a string literal to base64 and back.
172+ *
191173 * ~~~ {.rust}
192174 * extern mod extra;
193175 * use extra::base64::{ToBase64, FromBase64, standard};
176+ * use std::str;
194177 *
195178 * fn main () {
196- * let str = [52,32] .to_base64(standard);
197- * printfln!("%s", str );
198- * let bytes = str .from_base64();
179+ * let hello_str = "Hello, World" .to_base64(standard);
180+ * printfln!("%s", hello_str );
181+ * let bytes = hello_str .from_base64();
199182 * printfln!("%?", bytes);
183+ * let result_str = str::from_bytes(bytes);
184+ * printfln!("%s", result_str);
200185 * }
201186 * ~~~
202187 */
@@ -205,20 +190,20 @@ impl<'self> FromBase64 for &'self [u8] {
205190 let mut buf: u32 = 0 ;
206191 let mut modulus = 0 ;
207192
208- let mut it = self . iter ( ) ;
209- for & byte in it {
210- let ch = byte as char ;
193+ let mut it = self . byte_iter ( ) . enumerate ( ) ;
194+ for ( idx, byte) in it {
211195 let val = byte as u32 ;
212196
213- match ch {
197+ match byte as char {
214198 'A' ..'Z' => buf |= val - 0x41 ,
215199 'a' ..'z' => buf |= val - 0x47 ,
216200 '0' ..'9' => buf |= val + 0x04 ,
217201 '+' |'-' => buf |= 0x3E ,
218202 '/' |'_' => buf |= 0x3F ,
219203 '\r' |'\n' => loop ,
220204 '=' => break ,
221- _ => return Err ( ~"Invalid Base64 character")
205+ _ => return Err ( fmt ! ( "Invalid character '%c' at position %u" ,
206+ self . char_at( idx) , idx) )
222207 }
223208
224209 buf <<= 6 ;
@@ -231,8 +216,11 @@ impl<'self> FromBase64 for &'self [u8] {
231216 }
232217 }
233218
234- if !it. all ( |& byte| { byte as char == '=' } ) {
235- return Err ( ~"Invalid Base64 character") ;
219+ for ( idx, byte) in it {
220+ if ( byte as char ) != '=' {
221+ return Err ( fmt ! ( "Invalid character '%c' at position %u" ,
222+ self . char_at( idx) , idx) ) ;
223+ }
236224 }
237225
238226 match modulus {
@@ -251,67 +239,35 @@ impl<'self> FromBase64 for &'self [u8] {
251239 }
252240}
253241
254- impl < ' self > FromBase64 for & ' self str {
255- /**
256- * Convert any base64 encoded string (literal, `@`, `&`, or `~`)
257- * to the byte values it encodes.
258- *
259- * You can use the `from_bytes` function in `std::str`
260- * to turn a `[u8]` into a string with characters corresponding to those
261- * values.
262- *
263- * # Example
264- *
265- * This converts a string literal to base64 and back.
266- *
267- * ~~~ {.rust}
268- * extern mod extra;
269- * use extra::base64::{ToBase64, FromBase64, standard};
270- * use std::str;
271- *
272- * fn main () {
273- * let hello_str = "Hello, World".to_base64(standard);
274- * printfln!("%s", hello_str);
275- * let bytes = hello_str.from_base64();
276- * printfln!("%?", bytes);
277- * let result_str = str::from_bytes(bytes);
278- * printfln!("%s", result_str);
279- * }
280- * ~~~
281- */
282- fn from_base64 ( & self ) -> Result < ~[ u8 ] , ~str > {
283- self . as_bytes ( ) . from_base64 ( )
284- }
285- }
286-
287242#[ cfg( test) ]
288243mod test {
289244 use test:: BenchHarness ;
290245 use base64:: * ;
291246
292247 #[ test]
293248 fn test_to_base64_basic ( ) {
294- assert_eq ! ( "" . to_base64( STANDARD ) , ~"") ;
295- assert_eq ! ( "f" . to_base64( STANDARD ) , ~"Zg ==");
296- assert_eq!(" fo".to_base64(STANDARD), ~" Zm8 =");
297- assert_eq!(" foo".to_base64(STANDARD), ~" Zm9v ");
298- assert_eq!(" foob".to_base64(STANDARD), ~" Zm9vYg ==");
299- assert_eq!(" fooba".to_base64(STANDARD), ~" Zm9vYmE =");
300- assert_eq!(" foobar".to_base64(STANDARD), ~" Zm9vYmFy ");
249+ assert_eq ! ( "" . as_bytes ( ) . to_base64( STANDARD ) , ~"") ;
250+ assert_eq ! ( "f" . as_bytes ( ) . to_base64( STANDARD ) , ~"Zg ==");
251+ assert_eq!(" fo".as_bytes(). to_base64(STANDARD), ~" Zm8 =");
252+ assert_eq!(" foo".as_bytes(). to_base64(STANDARD), ~" Zm9v ");
253+ assert_eq!(" foob".as_bytes(). to_base64(STANDARD), ~" Zm9vYg ==");
254+ assert_eq!(" fooba".as_bytes(). to_base64(STANDARD), ~" Zm9vYmE =");
255+ assert_eq!(" foobar".as_bytes(). to_base64(STANDARD), ~" Zm9vYmFy ");
301256 }
302257
303258 #[test]
304259 fn test_to_base64_line_break() {
305260 assert!(![0u8, 1000].to_base64(Config {line_length: None, ..STANDARD})
306261 .contains("\r \n " ) ) ;
307- assert_eq ! ( "foobar" . to_base64( Config { line_length: Some ( 4 ) , ..STANDARD } ) ,
262+ assert_eq ! ( "foobar" . as_bytes( ) . to_base64( Config { line_length: Some ( 4 ) ,
263+ ..STANDARD } ) ,
308264 ~"Zm9v \r \n YmFy ");
309265 }
310266
311267 #[test]
312268 fn test_to_base64_padding() {
313- assert_eq!(" f".to_base64(Config {pad: false, ..STANDARD}), ~" Zg ");
314- assert_eq!(" fo".to_base64(Config {pad: false, ..STANDARD}), ~" Zm8 ");
269+ assert_eq!(" f".as_bytes(). to_base64(Config {pad: false, ..STANDARD}), ~" Zg ");
270+ assert_eq!(" fo".as_bytes(). to_base64(Config {pad: false, ..STANDARD}), ~" Zm8 ");
315271 }
316272
317273 #[test]
@@ -345,7 +301,7 @@ mod test {
345301 #[test]
346302 fn test_from_base64_invalid_char() {
347303 assert!(" Zm $=".from_base64().is_err())
348- assert!(" Zg ==$".from_base64().is_err());
304+ assert!(" Zg ==$".from_base64().is_err());
349305 }
350306
351307 #[test]
@@ -369,20 +325,20 @@ mod test {
369325 }
370326
371327 #[bench]
372- pub fn to_base64 (bh: & mut BenchHarness) {
328+ pub fn bench_to_base64 (bh: & mut BenchHarness) {
373329 let s = " イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
374330 ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
375331 do bh.iter {
376- s.to_base64(STANDARD);
332+ s.as_bytes(). to_base64(STANDARD);
377333 }
378334 bh.bytes = s.len() as u64;
379335 }
380336
381337 #[bench]
382- pub fn from_base64 (bh: & mut BenchHarness) {
338+ pub fn bench_from_base64 (bh: & mut BenchHarness) {
383339 let s = " イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
384340 ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン" ;
385- let b = s. to_base64( STANDARD ) ;
341+ let b = s. as_bytes ( ) . to_base64( STANDARD ) ;
386342 do bh. iter {
387343 b. from_base64( ) ;
388344 }
0 commit comments