Skip to content

Commit 62dbdc4

Browse files
committed
auto merge of #8287 : sfackler/rust/hex, r=alexcrichton
FromHex ignores whitespace and parses either upper or lower case hex digits. ToHex outputs lower case hex digits with no whitespace. Unlike ToBase64, ToHex doesn't allow you to configure the output format. I don't feel that it's super useful in this case.
2 parents 5b4244d + 3b441c4 commit 62dbdc4

File tree

3 files changed

+264
-114
lines changed

3 files changed

+264
-114
lines changed

src/libextra/base64.rs

Lines changed: 70 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
//! Base64 binary-to-text encoding
12+
use std::str;
1213

1314
/// Available encoding character sets
1415
pub enum CharacterSet {
@@ -40,21 +41,13 @@ pub static URL_SAFE: Config =
4041
pub 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.
6053
pub 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 + 1u] 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)]
288243
mod 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\nYmFy");
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
}

src/libextra/extra.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ pub mod stats;
102102
pub mod semver;
103103
pub mod fileinput;
104104
pub mod flate;
105+
pub mod hex;
105106

106107
#[cfg(unicode)]
107108
mod unicode;

0 commit comments

Comments
 (0)