]>
Commit | Line | Data |
---|---|---|
476ff2be SL |
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 | ||
15 | pub use self::FromHexError::*; | |
16 | ||
17 | use std::fmt; | |
18 | use std::error; | |
19 | ||
20 | /// A trait for converting a value to hexadecimal encoding | |
21 | pub trait ToHex { | |
22 | /// Converts the value of `self` to a hex value, returning the owned | |
23 | /// string. | |
24 | fn to_hex(&self) -> String; | |
25 | } | |
26 | ||
27 | static CHARS: &'static[u8] = b"0123456789abcdef"; | |
28 | ||
29 | impl ToHex for [u8] { | |
30 | /// Turn a vector of `u8` bytes into a hexadecimal string. | |
31 | /// | |
32 | /// # Example | |
33 | /// | |
34 | /// ```rust | |
35 | /// extern crate rustc_serialize; | |
36 | /// use rustc_serialize::hex::ToHex; | |
37 | /// | |
38 | /// fn main () { | |
39 | /// let str = [52,32].to_hex(); | |
40 | /// println!("{}", str); | |
41 | /// } | |
42 | /// ``` | |
43 | fn to_hex(&self) -> String { | |
44 | let mut v = Vec::with_capacity(self.len() * 2); | |
45 | for &byte in self.iter() { | |
46 | v.push(CHARS[(byte >> 4) as usize]); | |
47 | v.push(CHARS[(byte & 0xf) as usize]); | |
48 | } | |
49 | ||
50 | unsafe { | |
51 | String::from_utf8_unchecked(v) | |
52 | } | |
53 | } | |
54 | } | |
55 | ||
8bb4bdeb XL |
56 | impl<'a, T: ?Sized + ToHex> ToHex for &'a T { |
57 | fn to_hex(&self) -> String { | |
58 | (**self).to_hex() | |
59 | } | |
60 | } | |
61 | ||
476ff2be SL |
62 | /// A trait for converting hexadecimal encoded values |
63 | pub trait FromHex { | |
64 | /// Converts the value of `self`, interpreted as hexadecimal encoded data, | |
65 | /// into an owned vector of bytes, returning the vector. | |
66 | fn from_hex(&self) -> Result<Vec<u8>, FromHexError>; | |
67 | } | |
68 | ||
69 | /// Errors that can occur when decoding a hex encoded string | |
70 | #[derive(Clone, Copy)] | |
71 | pub enum FromHexError { | |
72 | /// The input contained a character not part of the hex format | |
73 | InvalidHexCharacter(char, usize), | |
74 | /// The input had an invalid length | |
75 | InvalidHexLength, | |
76 | } | |
77 | ||
78 | impl fmt::Debug for FromHexError { | |
79 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
80 | match *self { | |
81 | InvalidHexCharacter(ch, idx) => | |
82 | write!(f, "Invalid character '{}' at position {}", ch, idx), | |
83 | InvalidHexLength => write!(f, "Invalid input length"), | |
84 | } | |
85 | } | |
86 | } | |
87 | ||
88 | impl error::Error for FromHexError { | |
89 | fn description(&self) -> &str { | |
90 | match *self { | |
91 | InvalidHexCharacter(_, _) => "invalid character", | |
92 | InvalidHexLength => "invalid length", | |
93 | } | |
94 | } | |
95 | } | |
96 | ||
97 | impl fmt::Display for FromHexError { | |
98 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
99 | fmt::Debug::fmt(&self, f) | |
100 | } | |
101 | } | |
102 | ||
103 | impl FromHex for str { | |
104 | /// Convert any hexadecimal encoded string (literal, `@`, `&`, or `~`) | |
105 | /// to the byte values it encodes. | |
106 | /// | |
107 | /// You can use the `String::from_utf8` function to turn a | |
108 | /// `Vec<u8>` into a string with characters corresponding to those values. | |
109 | /// | |
110 | /// # Example | |
111 | /// | |
112 | /// This converts a string literal to hexadecimal and back. | |
113 | /// | |
114 | /// ```rust | |
115 | /// extern crate rustc_serialize; | |
116 | /// use rustc_serialize::hex::{FromHex, ToHex}; | |
117 | /// | |
118 | /// fn main () { | |
119 | /// let hello_str = "Hello, World".as_bytes().to_hex(); | |
120 | /// println!("{}", hello_str); | |
121 | /// let bytes = hello_str.from_hex().unwrap(); | |
122 | /// println!("{:?}", bytes); | |
123 | /// let result_str = String::from_utf8(bytes).unwrap(); | |
124 | /// println!("{}", result_str); | |
125 | /// } | |
126 | /// ``` | |
127 | fn from_hex(&self) -> Result<Vec<u8>, FromHexError> { | |
128 | // This may be an overestimate if there is any whitespace | |
129 | let mut b = Vec::with_capacity(self.len() / 2); | |
130 | let mut modulus = 0; | |
7cac9316 | 131 | let mut buf = 0; |
476ff2be SL |
132 | |
133 | for (idx, byte) in self.bytes().enumerate() { | |
134 | buf <<= 4; | |
135 | ||
136 | match byte { | |
137 | b'A'...b'F' => buf |= byte - b'A' + 10, | |
138 | b'a'...b'f' => buf |= byte - b'a' + 10, | |
139 | b'0'...b'9' => buf |= byte - b'0', | |
140 | b' '|b'\r'|b'\n'|b'\t' => { | |
141 | buf >>= 4; | |
142 | continue | |
143 | } | |
144 | _ => { | |
145 | let ch = self[idx..].chars().next().unwrap(); | |
146 | return Err(InvalidHexCharacter(ch, idx)) | |
147 | } | |
148 | } | |
149 | ||
150 | modulus += 1; | |
151 | if modulus == 2 { | |
152 | modulus = 0; | |
153 | b.push(buf); | |
154 | } | |
155 | } | |
156 | ||
157 | match modulus { | |
158 | 0 => Ok(b.into_iter().collect()), | |
159 | _ => Err(InvalidHexLength), | |
160 | } | |
161 | } | |
162 | } | |
163 | ||
8bb4bdeb XL |
164 | impl<'a, T: ?Sized + FromHex> FromHex for &'a T { |
165 | fn from_hex(&self) -> Result<Vec<u8>, FromHexError> { | |
166 | (**self).from_hex() | |
167 | } | |
168 | } | |
169 | ||
476ff2be SL |
170 | #[cfg(test)] |
171 | mod tests { | |
172 | use hex::{FromHex, ToHex}; | |
173 | ||
174 | #[test] | |
175 | pub fn test_to_hex() { | |
176 | assert_eq!("foobar".as_bytes().to_hex(), "666f6f626172"); | |
177 | } | |
178 | ||
179 | #[test] | |
180 | pub fn test_from_hex_okay() { | |
181 | assert_eq!("666f6f626172".from_hex().unwrap(), | |
182 | b"foobar"); | |
183 | assert_eq!("666F6F626172".from_hex().unwrap(), | |
184 | b"foobar"); | |
185 | } | |
186 | ||
187 | #[test] | |
188 | pub fn test_from_hex_odd_len() { | |
189 | assert!("666".from_hex().is_err()); | |
190 | assert!("66 6".from_hex().is_err()); | |
191 | } | |
192 | ||
193 | #[test] | |
194 | pub fn test_from_hex_invalid_char() { | |
195 | assert!("66y6".from_hex().is_err()); | |
196 | } | |
197 | ||
198 | #[test] | |
199 | pub fn test_from_hex_ignores_whitespace() { | |
200 | assert_eq!("666f 6f6\r\n26172 ".from_hex().unwrap(), | |
201 | b"foobar"); | |
202 | } | |
203 | ||
204 | #[test] | |
205 | pub fn test_to_hex_all_bytes() { | |
206 | for i in 0..256 { | |
207 | assert_eq!([i as u8].to_hex(), format!("{:02x}", i)); | |
208 | } | |
209 | } | |
210 | ||
211 | #[test] | |
212 | pub fn test_from_hex_all_bytes() { | |
213 | for i in 0..256 { | |
214 | let ii: &[u8] = &[i as u8]; | |
215 | assert_eq!(format!("{:02x}", i).from_hex().unwrap(), | |
216 | ii); | |
217 | assert_eq!(format!("{:02X}", i).from_hex().unwrap(), | |
218 | ii); | |
219 | } | |
220 | } | |
221 | } |