]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //! Hex binary-to-text encoding |
2 | ||
3 | pub use self::FromHexError::*; | |
4 | ||
5 | use std::fmt; | |
6 | use std::error; | |
7 | ||
8 | /// A trait for converting a value to hexadecimal encoding | |
9 | pub trait ToHex { | |
10 | /// Converts the value of `self` to a hex value, returning the owned | |
11 | /// string. | |
12 | fn to_hex(&self) -> String; | |
13 | } | |
14 | ||
b7449926 | 15 | const CHARS: &[u8] = b"0123456789abcdef"; |
1a4d82fc JJ |
16 | |
17 | impl ToHex for [u8] { | |
18 | /// Turn a vector of `u8` bytes into a hexadecimal string. | |
19 | /// | |
c34b1796 | 20 | /// # Examples |
1a4d82fc | 21 | /// |
c34b1796 | 22 | /// ``` |
c1a9b12d SL |
23 | /// #![feature(rustc_private)] |
24 | /// | |
1a4d82fc JJ |
25 | /// extern crate serialize; |
26 | /// use serialize::hex::ToHex; | |
27 | /// | |
28 | /// fn main () { | |
29 | /// let str = [52,32].to_hex(); | |
30 | /// println!("{}", str); | |
31 | /// } | |
32 | /// ``` | |
33 | fn to_hex(&self) -> String { | |
34 | let mut v = Vec::with_capacity(self.len() * 2); | |
85aaf69f | 35 | for &byte in self { |
c34b1796 AL |
36 | v.push(CHARS[(byte >> 4) as usize]); |
37 | v.push(CHARS[(byte & 0xf) as usize]); | |
1a4d82fc JJ |
38 | } |
39 | ||
40 | unsafe { | |
41 | String::from_utf8_unchecked(v) | |
42 | } | |
43 | } | |
44 | } | |
45 | ||
46 | /// A trait for converting hexadecimal encoded values | |
47 | pub trait FromHex { | |
48 | /// Converts the value of `self`, interpreted as hexadecimal encoded data, | |
49 | /// into an owned vector of bytes, returning the vector. | |
50 | fn from_hex(&self) -> Result<Vec<u8>, FromHexError>; | |
51 | } | |
52 | ||
53 | /// Errors that can occur when decoding a hex encoded string | |
c34b1796 | 54 | #[derive(Copy, Clone, Debug)] |
1a4d82fc JJ |
55 | pub enum FromHexError { |
56 | /// The input contained a character not part of the hex format | |
c34b1796 | 57 | InvalidHexCharacter(char, usize), |
1a4d82fc JJ |
58 | /// The input had an invalid length |
59 | InvalidHexLength, | |
60 | } | |
61 | ||
85aaf69f | 62 | impl fmt::Display for FromHexError { |
9fa01778 | 63 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
1a4d82fc JJ |
64 | match *self { |
65 | InvalidHexCharacter(ch, idx) => | |
66 | write!(f, "Invalid character '{}' at position {}", ch, idx), | |
67 | InvalidHexLength => write!(f, "Invalid input length"), | |
68 | } | |
69 | } | |
70 | } | |
71 | ||
72 | impl error::Error for FromHexError { | |
73 | fn description(&self) -> &str { | |
74 | match *self { | |
9e0c209e | 75 | InvalidHexCharacter(..) => "invalid character", |
1a4d82fc JJ |
76 | InvalidHexLength => "invalid length", |
77 | } | |
78 | } | |
1a4d82fc JJ |
79 | } |
80 | ||
81 | ||
82 | impl FromHex for str { | |
9fa01778 | 83 | /// Converts any hexadecimal encoded string (literal, `@`, `&`, or `~`) |
1a4d82fc JJ |
84 | /// to the byte values it encodes. |
85 | /// | |
86 | /// You can use the `String::from_utf8` function to turn a | |
87 | /// `Vec<u8>` into a string with characters corresponding to those values. | |
88 | /// | |
c34b1796 | 89 | /// # Examples |
1a4d82fc JJ |
90 | /// |
91 | /// This converts a string literal to hexadecimal and back. | |
92 | /// | |
c34b1796 | 93 | /// ``` |
c1a9b12d SL |
94 | /// #![feature(rustc_private)] |
95 | /// | |
1a4d82fc JJ |
96 | /// extern crate serialize; |
97 | /// use serialize::hex::{FromHex, ToHex}; | |
98 | /// | |
99 | /// fn main () { | |
100 | /// let hello_str = "Hello, World".as_bytes().to_hex(); | |
101 | /// println!("{}", hello_str); | |
85aaf69f | 102 | /// let bytes = hello_str.from_hex().unwrap(); |
1a4d82fc JJ |
103 | /// println!("{:?}", bytes); |
104 | /// let result_str = String::from_utf8(bytes).unwrap(); | |
105 | /// println!("{}", result_str); | |
106 | /// } | |
107 | /// ``` | |
108 | fn from_hex(&self) -> Result<Vec<u8>, FromHexError> { | |
109 | // This may be an overestimate if there is any whitespace | |
110 | let mut b = Vec::with_capacity(self.len() / 2); | |
85aaf69f | 111 | let mut modulus = 0; |
c34b1796 | 112 | let mut buf = 0; |
1a4d82fc JJ |
113 | |
114 | for (idx, byte) in self.bytes().enumerate() { | |
115 | buf <<= 4; | |
116 | ||
117 | match byte { | |
8faf50e0 XL |
118 | b'A'..=b'F' => buf |= byte - b'A' + 10, |
119 | b'a'..=b'f' => buf |= byte - b'a' + 10, | |
120 | b'0'..=b'9' => buf |= byte - b'0', | |
1a4d82fc JJ |
121 | b' '|b'\r'|b'\n'|b'\t' => { |
122 | buf >>= 4; | |
123 | continue | |
124 | } | |
54a0048b SL |
125 | _ => { |
126 | let ch = self[idx..].chars().next().unwrap(); | |
127 | return Err(InvalidHexCharacter(ch, idx)) | |
128 | } | |
1a4d82fc JJ |
129 | } |
130 | ||
131 | modulus += 1; | |
132 | if modulus == 2 { | |
133 | modulus = 0; | |
134 | b.push(buf); | |
135 | } | |
136 | } | |
137 | ||
138 | match modulus { | |
a1dfa0c6 | 139 | 0 => Ok(b), |
1a4d82fc JJ |
140 | _ => Err(InvalidHexLength), |
141 | } | |
142 | } | |
143 | } | |
144 | ||
145 | #[cfg(test)] | |
dc9dc135 | 146 | mod tests; |