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