]> git.lizzy.rs Git - rust.git/blob - src/libextra/hex.rs
5609c566d927315cf598da9d64c21aa2b9c19dd3
[rust.git] / src / libextra / hex.rs
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 hexadecimal encoded data,
78     /// into 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      * Whitespace is ignored.
87      *
88      * # Example
89      *
90      * ~~~ {.rust}
91      * extern mod extra;
92      * use extra::hex::{ToHex, FromHex};
93      *
94      * fn main () {
95      *     let str = [52,32].to_hex();
96      *     printfln!("%s", str);
97      *     let bytes = str.from_hex().get();
98      *     printfln!("%?", bytes);
99      * }
100      * ~~~
101      */
102     fn from_hex(&self) -> Result<~[u8], ~str> {
103         // This may be an overestimate if there is any whitespace
104         let mut b = vec::with_capacity(self.len() / 2);
105         let mut modulus = 0;
106         let mut buf = 0u8;
107
108         for (idx, &byte) in self.iter().enumerate() {
109             buf <<= 4;
110
111             match byte as char {
112                 'A'..'F' => buf |= byte - ('A' as u8) + 10,
113                 'a'..'f' => buf |= byte - ('a' as u8) + 10,
114                 '0'..'9' => buf |= byte - ('0' as u8),
115                 ' '|'\r'|'\n'|'\t' => {
116                     buf >>= 4;
117                     loop
118                 }
119                 _ => return Err(fmt!("Invalid byte '%c' found at position %u",
120                                      byte as char, idx))
121             }
122
123             modulus += 1;
124             if modulus == 2 {
125                 modulus = 0;
126                 b.push(buf);
127             }
128         }
129
130         match modulus {
131             0 => Ok(b),
132             _ => Err(~"Invalid input length")
133         }
134     }
135 }
136
137 impl<'self> FromHex for &'self str {
138     /**
139      * Convert any hexadecimal encoded string (literal, `@`, `&`, or `~`)
140      * to the byte values it encodes.
141      *
142      * You can use the `from_bytes` function in `std::str`
143      * to turn a `[u8]` into a string with characters corresponding to those
144      * values.
145      *
146      * # Example
147      *
148      * This converts a string literal to hexadecimal and back.
149      *
150      * ~~~ {.rust}
151      * extern mod extra;
152      * use extra::hex::{FromHex, ToHex};
153      * use std::str;
154      *
155      * fn main () {
156      *     let hello_str = "Hello, World".to_hex();
157      *     printfln!("%s", hello_str);
158      *     let bytes = hello_str.from_hex().get();
159      *     printfln!("%?", bytes);
160      *     let result_str = str::from_bytes(bytes);
161      *     printfln!("%s", result_str);
162      * }
163      * ~~~
164      */
165     fn from_hex(&self) -> Result<~[u8], ~str> {
166         self.as_bytes().from_hex()
167     }
168 }
169
170 #[cfg(test)]
171 mod tests {
172     use test::BenchHarness;
173     use hex::*;
174
175     #[test]
176     pub fn test_to_hex() {
177         assert_eq!("foobar".to_hex(), ~"666f6f626172");
178     }
179
180     #[test]
181     pub fn test_from_hex_okay() {
182         assert_eq!("666f6f626172".from_hex().get(),
183                    "foobar".as_bytes().to_owned());
184         assert_eq!("666F6F626172".from_hex().get(),
185                    "foobar".as_bytes().to_owned());
186     }
187
188     #[test]
189     pub fn test_from_hex_odd_len() {
190         assert!("666".from_hex().is_err());
191         assert!("66 6".from_hex().is_err());
192     }
193
194     #[test]
195     pub fn test_from_hex_invalid_char() {
196         assert!("66y6".from_hex().is_err());
197     }
198
199     #[test]
200     pub fn test_from_hex_ignores_whitespace() {
201         assert_eq!("666f 6f6\r\n26172 ".from_hex().get(),
202                    "foobar".as_bytes().to_owned());
203     }
204
205     #[test]
206     pub fn test_to_hex_all_bytes() {
207         for i in range(0, 256) {
208             assert_eq!([i as u8].to_hex(), fmt!("%02x", i as uint));
209         }
210     }
211
212     #[test]
213     pub fn test_from_hex_all_bytes() {
214         for i in range(0, 256) {
215             assert_eq!(fmt!("%02x", i as uint).from_hex().get(), ~[i as u8]);
216             assert_eq!(fmt!("%02X", i as uint).from_hex().get(), ~[i as u8]);
217         }
218     }
219
220     #[bench]
221     pub fn bench_to_hex(bh: & mut BenchHarness) {
222         let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
223                  ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
224         do bh.iter {
225             s.to_hex();
226         }
227         bh.bytes = s.len() as u64;
228     }
229
230     #[bench]
231     pub fn bench_from_hex(bh: & mut BenchHarness) {
232         let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
233                  ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
234         let b = s.to_hex();
235         do bh.iter {
236             b.from_hex();
237         }
238         bh.bytes = b.len() as u64;
239     }
240 }