]> git.lizzy.rs Git - rust.git/blob - src/libserialize/hex.rs
rollup merge of #17355 : gamazeps/issue17210
[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 // ignore-lexer-test FIXME #15679
12
13 //! Hex binary-to-text encoding
14 use std::fmt;
15 use std::string;
16
17 /// A trait for converting a value to hexadecimal encoding
18 pub trait ToHex {
19     /// Converts the value of `self` to a hex value, returning the owned
20     /// string.
21     fn to_hex(&self) -> String;
22 }
23
24 static CHARS: &'static[u8] = b"0123456789abcdef";
25
26 impl<'a> ToHex for &'a [u8] {
27     /**
28      * Turn a vector of `u8` bytes into a hexadecimal string.
29      *
30      * # Example
31      *
32      * ```rust
33      * extern crate serialize;
34      * use serialize::hex::ToHex;
35      *
36      * fn main () {
37      *     let str = [52,32].to_hex();
38      *     println!("{}", str);
39      * }
40      * ```
41      */
42     fn to_hex(&self) -> String {
43         let mut v = Vec::with_capacity(self.len() * 2);
44         for &byte in self.iter() {
45             v.push(CHARS[(byte >> 4) as uint]);
46             v.push(CHARS[(byte & 0xf) as uint]);
47         }
48
49         unsafe {
50             string::raw::from_utf8(v)
51         }
52     }
53 }
54
55 /// A trait for converting hexadecimal encoded values
56 pub trait FromHex {
57     /// Converts the value of `self`, interpreted as hexadecimal encoded data,
58     /// into an owned vector of bytes, returning the vector.
59     fn from_hex(&self) -> Result<Vec<u8>, FromHexError>;
60 }
61
62 /// Errors that can occur when decoding a hex encoded string
63 pub enum FromHexError {
64     /// The input contained a character not part of the hex format
65     InvalidHexCharacter(char, uint),
66     /// The input had an invalid length
67     InvalidHexLength,
68 }
69
70 impl fmt::Show for FromHexError {
71     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72         match *self {
73             InvalidHexCharacter(ch, idx) =>
74                 write!(f, "Invalid character '{}' at position {}", ch, idx),
75             InvalidHexLength => write!(f, "Invalid input length"),
76         }
77     }
78 }
79
80 impl<'a> FromHex for &'a str {
81     /**
82      * Convert any hexadecimal encoded string (literal, `@`, `&`, or `~`)
83      * to the byte values it encodes.
84      *
85      * You can use the `String::from_utf8` function in `std::string` to turn a
86      * `Vec<u8>` into a string with characters corresponding to those values.
87      *
88      * # Example
89      *
90      * This converts a string literal to hexadecimal and back.
91      *
92      * ```rust
93      * extern crate serialize;
94      * use serialize::hex::{FromHex, ToHex};
95      *
96      * fn main () {
97      *     let hello_str = "Hello, World".as_bytes().to_hex();
98      *     println!("{}", hello_str);
99      *     let bytes = hello_str.as_slice().from_hex().unwrap();
100      *     println!("{}", bytes);
101      *     let result_str = String::from_utf8(bytes).unwrap();
102      *     println!("{}", result_str);
103      * }
104      * ```
105      */
106     fn from_hex(&self) -> Result<Vec<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 = 0i;
110         let mut buf = 0u8;
111
112         for (idx, byte) in self.bytes().enumerate() {
113             buf <<= 4;
114
115             match byte {
116                 b'A'..b'F' => buf |= byte - b'A' + 10,
117                 b'a'..b'f' => buf |= byte - b'a' + 10,
118                 b'0'..b'9' => buf |= byte - b'0',
119                 b' '|b'\r'|b'\n'|b'\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.into_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".to_string());
149     }
150
151     #[test]
152     pub fn test_from_hex_okay() {
153         assert_eq!("666f6f626172".from_hex().unwrap().as_slice(),
154                    "foobar".as_bytes());
155         assert_eq!("666F6F626172".from_hex().unwrap().as_slice(),
156                    "foobar".as_bytes());
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().as_slice(),
173                    "foobar".as_bytes());
174     }
175
176     #[test]
177     pub fn test_to_hex_all_bytes() {
178         for i in range(0u, 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(0u, 256) {
186             let ii: &[u8] = &[i as u8];
187             assert_eq!(format!("{:02x}", i as uint).as_slice()
188                                                    .from_hex()
189                                                    .unwrap()
190                                                    .as_slice(),
191                        ii);
192             assert_eq!(format!("{:02X}", i as uint).as_slice()
193                                                    .from_hex()
194                                                    .unwrap()
195                                                    .as_slice(),
196                        ii);
197         }
198     }
199
200     #[bench]
201     pub fn bench_to_hex(b: &mut Bencher) {
202         let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
203                  ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
204         b.iter(|| {
205             s.as_bytes().to_hex();
206         });
207         b.bytes = s.len() as u64;
208     }
209
210     #[bench]
211     pub fn bench_from_hex(b: &mut Bencher) {
212         let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
213                  ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
214         let sb = s.as_bytes().to_hex();
215         b.iter(|| {
216             sb.as_slice().from_hex().unwrap();
217         });
218         b.bytes = sb.len() as u64;
219     }
220 }