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. to_hex(&self) -> String24 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 /// ``` to_hex(&self) -> String43 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 56 impl<'a, T: ?Sized + ToHex> ToHex for &'a T { to_hex(&self) -> String57 fn to_hex(&self) -> String { 58 (**self).to_hex() 59 } 60 } 61 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. from_hex(&self) -> Result<Vec<u8>, FromHexError>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 { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result79 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 { description(&self) -> &str89 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 { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result98 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 /// ``` from_hex(&self) -> Result<Vec<u8>, FromHexError>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; 131 let mut buf = 0; 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 164 impl<'a, T: ?Sized + FromHex> FromHex for &'a T { from_hex(&self) -> Result<Vec<u8>, FromHexError>165 fn from_hex(&self) -> Result<Vec<u8>, FromHexError> { 166 (**self).from_hex() 167 } 168 } 169 170 #[cfg(test)] 171 mod tests { 172 use hex::{FromHex, ToHex}; 173 174 #[test] test_to_hex()175 pub fn test_to_hex() { 176 assert_eq!("foobar".as_bytes().to_hex(), "666f6f626172"); 177 } 178 179 #[test] test_from_hex_okay()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] test_from_hex_odd_len()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] test_from_hex_invalid_char()194 pub fn test_from_hex_invalid_char() { 195 assert!("66y6".from_hex().is_err()); 196 } 197 198 #[test] test_from_hex_ignores_whitespace()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] test_to_hex_all_bytes()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] test_from_hex_all_bytes()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 } 222