]> git.lizzy.rs Git - rust.git/blob - src/libserialize/hex.rs
Merge pull request #1 from rust-lang/master
[rust.git] / src / libserialize / hex.rs
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
15 const CHARS: &[u8] = b"0123456789abcdef";
16
17 impl ToHex for [u8] {
18     /// Turn a vector of `u8` bytes into a hexadecimal string.
19     ///
20     /// # Examples
21     ///
22     /// ```
23     /// #![feature(rustc_private)]
24     ///
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);
35         for &byte in self {
36             v.push(CHARS[(byte >> 4) as usize]);
37             v.push(CHARS[(byte & 0xf) as usize]);
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
54 #[derive(Copy, Clone, Debug)]
55 pub enum FromHexError {
56     /// The input contained a character not part of the hex format
57     InvalidHexCharacter(char, usize),
58     /// The input had an invalid length
59     InvalidHexLength,
60 }
61
62 impl fmt::Display for FromHexError {
63     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
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 {
75             InvalidHexCharacter(..) => "invalid character",
76             InvalidHexLength => "invalid length",
77         }
78     }
79 }
80
81
82 impl FromHex for str {
83     /// Converts any hexadecimal encoded string (literal, `@`, `&`, or `~`)
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     ///
89     /// # Examples
90     ///
91     /// This converts a string literal to hexadecimal and back.
92     ///
93     /// ```
94     /// #![feature(rustc_private)]
95     ///
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);
102     ///     let bytes = hello_str.from_hex().unwrap();
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);
111         let mut modulus = 0;
112         let mut buf = 0;
113
114         for (idx, byte) in self.bytes().enumerate() {
115             buf <<= 4;
116
117             match byte {
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',
121                 b' '|b'\r'|b'\n'|b'\t' => {
122                     buf >>= 4;
123                     continue
124                 }
125                 _ => {
126                     let ch = self[idx..].chars().next().unwrap();
127                     return Err(InvalidHexCharacter(ch, idx))
128                 }
129             }
130
131             modulus += 1;
132             if modulus == 2 {
133                 modulus = 0;
134                 b.push(buf);
135             }
136         }
137
138         match modulus {
139             0 => Ok(b),
140             _ => Err(InvalidHexLength),
141         }
142     }
143 }
144
145 #[cfg(test)]
146 mod tests;