]> git.lizzy.rs Git - rust.git/blob - src/libserialize/hex.rs
clean up error codes explanation
[rust.git] / src / libserialize / hex.rs
1 //! Hex binary-to-text encoding
2
3 pub use self::FromHexError::*;
4
5 use std::error;
6 use std::fmt;
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 { String::from_utf8_unchecked(v) }
41     }
42 }
43
44 /// A trait for converting hexadecimal encoded values
45 pub trait FromHex {
46     /// Converts the value of `self`, interpreted as hexadecimal encoded data,
47     /// into an owned vector of bytes, returning the vector.
48     fn from_hex(&self) -> Result<Vec<u8>, FromHexError>;
49 }
50
51 /// Errors that can occur when decoding a hex encoded string
52 #[derive(Copy, Clone, Debug)]
53 pub enum FromHexError {
54     /// The input contained a character not part of the hex format
55     InvalidHexCharacter(char, usize),
56     /// The input had an invalid length
57     InvalidHexLength,
58 }
59
60 impl fmt::Display for FromHexError {
61     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62         match *self {
63             InvalidHexCharacter(ch, idx) => {
64                 write!(f, "Invalid character '{}' at position {}", ch, idx)
65             }
66             InvalidHexLength => write!(f, "Invalid input length"),
67         }
68     }
69 }
70
71 impl error::Error for FromHexError {}
72
73 impl FromHex for str {
74     /// Converts any hexadecimal encoded string (literal, `@`, `&`, or `~`)
75     /// to the byte values it encodes.
76     ///
77     /// You can use the `String::from_utf8` function to turn a
78     /// `Vec<u8>` into a string with characters corresponding to those values.
79     ///
80     /// # Examples
81     ///
82     /// This converts a string literal to hexadecimal and back.
83     ///
84     /// ```
85     /// #![feature(rustc_private)]
86     ///
87     /// extern crate serialize;
88     /// use serialize::hex::{FromHex, ToHex};
89     ///
90     /// fn main () {
91     ///     let hello_str = "Hello, World".as_bytes().to_hex();
92     ///     println!("{}", hello_str);
93     ///     let bytes = hello_str.from_hex().unwrap();
94     ///     println!("{:?}", bytes);
95     ///     let result_str = String::from_utf8(bytes).unwrap();
96     ///     println!("{}", result_str);
97     /// }
98     /// ```
99     fn from_hex(&self) -> Result<Vec<u8>, FromHexError> {
100         // This may be an overestimate if there is any whitespace
101         let mut b = Vec::with_capacity(self.len() / 2);
102         let mut modulus = 0;
103         let mut buf = 0;
104
105         for (idx, byte) in self.bytes().enumerate() {
106             buf <<= 4;
107
108             match byte {
109                 b'A'..=b'F' => buf |= byte - b'A' + 10,
110                 b'a'..=b'f' => buf |= byte - b'a' + 10,
111                 b'0'..=b'9' => buf |= byte - b'0',
112                 b' ' | b'\r' | b'\n' | b'\t' => {
113                     buf >>= 4;
114                     continue;
115                 }
116                 _ => {
117                     let ch = self[idx..].chars().next().unwrap();
118                     return Err(InvalidHexCharacter(ch, idx));
119                 }
120             }
121
122             modulus += 1;
123             if modulus == 2 {
124                 modulus = 0;
125                 b.push(buf);
126             }
127         }
128
129         match modulus {
130             0 => Ok(b),
131             _ => Err(InvalidHexLength),
132         }
133     }
134 }
135
136 #[cfg(test)]
137 mod tests;