Skip to content

Commit 2266df5

Browse files
committed
Added hexadecimal encoding module
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.
1 parent ba3d03d commit 2266df5

File tree

2 files changed

+239
-0
lines changed

2 files changed

+239
-0
lines changed

src/libextra/extra.rs

+1
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;

src/libextra/hex.rs

+238
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
// Copyright 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+
//! Hex binary-to-text encoding
12+
use std::str;
13+
use std::vec;
14+
15+
/// A trait for converting a value to hexadecimal encoding
16+
pub trait ToHex {
17+
/// Converts the value of `self` to a hex value, returning the owned
18+
/// string.
19+
fn to_hex(&self) -> ~str;
20+
}
21+
22+
static CHARS: [char, ..16] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
23+
'a', 'b', 'c', 'd', 'e', 'f'];
24+
25+
impl<'self> ToHex for &'self [u8] {
26+
/**
27+
* Turn a vector of `u8` bytes into a hexadecimal string.
28+
*
29+
* # Example
30+
*
31+
* ~~~ {.rust}
32+
* extern mod extra;
33+
* use extra::hex::ToHex;
34+
*
35+
* fn main () {
36+
* let str = [52,32].to_hex();
37+
* printfln!("%s", str);
38+
* }
39+
* ~~~
40+
*/
41+
fn to_hex(&self) -> ~str {
42+
let mut s = str::with_capacity(self.len() * 2);
43+
for &byte in self.iter() {
44+
s.push_char(CHARS[byte >> 4]);
45+
s.push_char(CHARS[byte & 0xf]);
46+
}
47+
48+
s
49+
}
50+
}
51+
52+
impl<'self> ToHex for &'self str {
53+
/**
54+
* Convert any string (literal, `@`, `&`, or `~`) to hexadecimal encoding.
55+
*
56+
*
57+
* # Example
58+
*
59+
* ~~~ {.rust}
60+
* extern mod extra;
61+
* use extra::ToHex;
62+
*
63+
* fn main () {
64+
* let str = "Hello, World".to_hex();
65+
* printfln!("%s", str);
66+
* }
67+
* ~~~
68+
*
69+
*/
70+
fn to_hex(&self) -> ~str {
71+
self.as_bytes().to_hex()
72+
}
73+
}
74+
75+
/// A trait for converting hexadecimal encoded values
76+
pub trait FromHex {
77+
/// Converts the value of `self`, interpreted as base64 encoded data, into
78+
/// an owned vector of bytes, returning the vector.
79+
fn from_hex(&self) -> Result<~[u8], ~str>;
80+
}
81+
82+
impl<'self> FromHex for &'self [u8] {
83+
/**
84+
* Convert hexadecimal `u8` vector into u8 byte values.
85+
* Every 2 encoded characters is converted into 1 octet.
86+
*
87+
* # Example
88+
*
89+
* ~~~ {.rust}
90+
* extern mod extra;
91+
* use extra::hex::{ToHex, FromHex};
92+
*
93+
* fn main () {
94+
* let str = [52,32].to_hex();
95+
* printfln!("%s", str);
96+
* let bytes = str.from_hex().get();
97+
* printfln!("%?", bytes);
98+
* }
99+
* ~~~
100+
*/
101+
fn from_hex(&self) -> Result<~[u8], ~str> {
102+
// This may be an overestimate if there is any whitespace
103+
let mut b = vec::with_capacity(self.len() / 2);
104+
let mut modulus = 0;
105+
let mut buf = 0u8;
106+
107+
for &byte in self.iter() {
108+
buf <<= 4;
109+
110+
match byte as char {
111+
'A'..'F' => buf |= byte - ('A' as u8) + 10,
112+
'a'..'f' => buf |= byte - ('a' as u8) + 10,
113+
'0'..'9' => buf |= byte - ('0' as u8),
114+
' '|'\r'|'\n' => {
115+
buf >>= 4;
116+
loop
117+
}
118+
_ => return Err(~"Invalid hex char")
119+
}
120+
121+
modulus += 1;
122+
if modulus == 2 {
123+
modulus = 0;
124+
b.push(buf);
125+
}
126+
}
127+
128+
match modulus {
129+
0 => Ok(b),
130+
_ => Err(~"Invalid input length")
131+
}
132+
}
133+
}
134+
135+
impl<'self> FromHex for &'self str {
136+
/**
137+
* Convert any hexadecimal encoded string (literal, `@`, `&`, or `~`)
138+
* to the byte values it encodes.
139+
*
140+
* You can use the `from_bytes` function in `std::str`
141+
* to turn a `[u8]` into a string with characters corresponding to those
142+
* values.
143+
*
144+
* # Example
145+
*
146+
* This converts a string literal to hexadecimal and back.
147+
*
148+
* ~~~ {.rust}
149+
* extern mod extra;
150+
* use extra::hex::{FromHex, ToHex};
151+
* use std::str;
152+
*
153+
* fn main () {
154+
* let hello_str = "Hello, World".to_hex();
155+
* printfln!("%s", hello_str);
156+
* let bytes = hello_str.from_hex().get();
157+
* printfln!("%?", bytes);
158+
* let result_str = str::from_bytes(bytes);
159+
* printfln!("%s", result_str);
160+
* }
161+
* ~~~
162+
*/
163+
fn from_hex(&self) -> Result<~[u8], ~str> {
164+
self.as_bytes().from_hex()
165+
}
166+
}
167+
168+
#[cfg(test)]
169+
mod tests {
170+
use test::BenchHarness;
171+
use hex::*;
172+
173+
#[test]
174+
pub fn test_to_hex() {
175+
assert_eq!("foobar".to_hex(), ~"666f6f626172");
176+
}
177+
178+
#[test]
179+
pub fn test_from_hex_okay() {
180+
assert_eq!("666f6f626172".from_hex().get(),
181+
"foobar".as_bytes().to_owned());
182+
assert_eq!("666F6F626172".from_hex().get(),
183+
"foobar".as_bytes().to_owned());
184+
}
185+
186+
#[test]
187+
pub fn test_from_hex_odd_len() {
188+
assert!("666".from_hex().is_err());
189+
assert!("66 6".from_hex().is_err());
190+
}
191+
192+
#[test]
193+
pub fn test_from_hex_invalid_char() {
194+
assert!("66y6".from_hex().is_err());
195+
}
196+
197+
#[test]
198+
pub fn test_from_hex_ignores_whitespace() {
199+
assert_eq!("666f 6f6\r\n26172 ".from_hex().get(),
200+
"foobar".as_bytes().to_owned());
201+
}
202+
203+
#[test]
204+
pub fn test_to_hex_all_bytes() {
205+
for i in range(0, 256) {
206+
assert_eq!([i as u8].to_hex(), fmt!("%02x", i as uint));
207+
}
208+
}
209+
210+
#[test]
211+
pub fn test_from_hex_all_bytes() {
212+
for i in range(0, 256) {
213+
assert_eq!(fmt!("%02x", i as uint).from_hex().get(), ~[i as u8]);
214+
assert_eq!(fmt!("%02X", i as uint).from_hex().get(), ~[i as u8]);
215+
}
216+
}
217+
218+
#[bench]
219+
pub fn bench_to_hex(bh: & mut BenchHarness) {
220+
let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
221+
ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
222+
do bh.iter {
223+
s.to_hex();
224+
}
225+
bh.bytes = s.len() as u64;
226+
}
227+
228+
#[bench]
229+
pub fn bench_from_hex(bh: & mut BenchHarness) {
230+
let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
231+
ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
232+
let b = s.to_hex();
233+
do bh.iter {
234+
b.from_hex();
235+
}
236+
bh.bytes = b.len() as u64;
237+
}
238+
}

0 commit comments

Comments
 (0)