]> git.lizzy.rs Git - rust.git/blob - src/libserialize/hex.rs
auto merge of #13600 : brandonw/rust/master, r=brson
[rust.git] / src / libserialize / hex.rs
1 // Copyright 2013-2014 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::fmt;
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<'a> ToHex for &'a [u8] {
25     /**
26      * Turn a vector of `u8` bytes into a hexadecimal string.
27      *
28      * # Example
29      *
30      * ```rust
31      * extern crate serialize;
32      * use serialize::hex::ToHex;
33      *
34      * fn main () {
35      *     let str = [52,32].to_hex();
36      *     println!("{}", 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) as uint]);
44             v.push(CHARS[(byte & 0xf) as uint]);
45         }
46
47         unsafe {
48             str::raw::from_utf8_owned(v.move_iter().collect())
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], FromHexError>;
58 }
59
60 /// Errors that can occur when decoding a hex encoded string
61 pub enum FromHexError {
62     /// The input contained a character not part of the hex format
63     InvalidHexCharacter(char, uint),
64     /// The input had an invalid length
65     InvalidHexLength,
66 }
67
68 impl fmt::Show for FromHexError {
69     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70         match *self {
71             InvalidHexCharacter(ch, idx) =>
72                 write!(f.buf, "Invalid character '{}' at position {}", ch, idx),
73             InvalidHexLength => write!(f.buf, "Invalid input length"),
74         }
75     }
76 }
77
78 impl<'a> FromHex for &'a str {
79     /**
80      * Convert any hexadecimal encoded string (literal, `@`, `&`, or `~`)
81      * to the byte values it encodes.
82      *
83      * You can use the `from_utf8_owned` function in `std::str`
84      * to turn a `[u8]` into a string with characters corresponding to those
85      * values.
86      *
87      * # Example
88      *
89      * This converts a string literal to hexadecimal and back.
90      *
91      * ```rust
92      * extern crate serialize;
93      * use serialize::hex::{FromHex, ToHex};
94      * use std::str;
95      *
96      * fn main () {
97      *     let hello_str = "Hello, World".as_bytes().to_hex();
98      *     println!("{}", hello_str);
99      *     let bytes = hello_str.from_hex().unwrap();
100      *     println!("{:?}", bytes);
101      *     let result_str = str::from_utf8_owned(bytes).unwrap();
102      *     println!("{}", result_str);
103      * }
104      * ```
105      */
106     fn from_hex(&self) -> Result<~[u8], FromHexError> {
107         // This may be an overestimate if there is any whitespace
108         let mut b = Vec::with_capacity(self.len() / 2);
109         let mut modulus = 0;
110         let mut buf = 0u8;
111
112         for (idx, byte) in self.bytes().enumerate() {
113             buf <<= 4;
114
115             match byte as char {
116                 'A'..'F' => buf |= byte - ('A' as u8) + 10,
117                 'a'..'f' => buf |= byte - ('a' as u8) + 10,
118                 '0'..'9' => buf |= byte - ('0' as u8),
119                 ' '|'\r'|'\n'|'\t' => {
120                     buf >>= 4;
121                     continue
122                 }
123                 _ => return Err(InvalidHexCharacter(self.char_at(idx), idx)),
124             }
125
126             modulus += 1;
127             if modulus == 2 {
128                 modulus = 0;
129                 b.push(buf);
130             }
131         }
132
133         match modulus {
134             0 => Ok(b.move_iter().collect()),
135             _ => Err(InvalidHexLength),
136         }
137     }
138 }
139
140 #[cfg(test)]
141 mod tests {
142     extern crate test;
143     use self::test::Bencher;
144     use hex::{FromHex, ToHex};
145
146     #[test]
147     pub fn test_to_hex() {
148         assert_eq!("foobar".as_bytes().to_hex(), ~"666f6f626172");
149     }
150
151     #[test]
152     pub fn test_from_hex_okay() {
153         assert_eq!("666f6f626172".from_hex().unwrap(),
154                    "foobar".as_bytes().to_owned());
155         assert_eq!("666F6F626172".from_hex().unwrap(),
156                    "foobar".as_bytes().to_owned());
157     }
158
159     #[test]
160     pub fn test_from_hex_odd_len() {
161         assert!("666".from_hex().is_err());
162         assert!("66 6".from_hex().is_err());
163     }
164
165     #[test]
166     pub fn test_from_hex_invalid_char() {
167         assert!("66y6".from_hex().is_err());
168     }
169
170     #[test]
171     pub fn test_from_hex_ignores_whitespace() {
172         assert_eq!("666f 6f6\r\n26172 ".from_hex().unwrap(),
173                    "foobar".as_bytes().to_owned());
174     }
175
176     #[test]
177     pub fn test_to_hex_all_bytes() {
178         for i in range(0, 256) {
179             assert_eq!([i as u8].to_hex(), format!("{:02x}", i as uint));
180         }
181     }
182
183     #[test]
184     pub fn test_from_hex_all_bytes() {
185         for i in range(0, 256) {
186             assert_eq!(format!("{:02x}", i as uint).from_hex().unwrap(), ~[i as u8]);
187             assert_eq!(format!("{:02X}", i as uint).from_hex().unwrap(), ~[i as u8]);
188         }
189     }
190
191     #[bench]
192     pub fn bench_to_hex(b: &mut Bencher) {
193         let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
194                  ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
195         b.iter(|| {
196             s.as_bytes().to_hex();
197         });
198         b.bytes = s.len() as u64;
199     }
200
201     #[bench]
202     pub fn bench_from_hex(b: &mut Bencher) {
203         let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
204                  ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
205         let sb = s.as_bytes().to_hex();
206         b.iter(|| {
207             sb.from_hex().unwrap();
208         });
209         b.bytes = sb.len() as u64;
210     }
211 }