use std::error;
use std::fmt;
pub trait ToHex {
fn write_hex<W: fmt::Write>(&self, w: &mut W) -> fmt::Result;
fn write_hex_upper<W: fmt::Write>(&self, w: &mut W) -> fmt::Result;
}
impl<T: AsRef<[u8]>> ToHex for T {
fn write_hex<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
static CHARS: &'static [u8] = b"0123456789abcdef";
for &byte in self.as_ref().iter() {
w.write_char(CHARS[(byte >> 4) as usize].into())?;
w.write_char(CHARS[(byte & 0xf) as usize].into())?;
}
Ok(())
}
fn write_hex_upper<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
static CHARS: &'static [u8] = b"0123456789ABCDEF";
for &byte in self.as_ref().iter() {
w.write_char(CHARS[(byte >> 4) as usize].into())?;
w.write_char(CHARS[(byte & 0xf) as usize].into())?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FromHexError {
InvalidHexCharacter {
c: char,
index: usize,
},
OddLength,
InvalidStringLength,
}
impl error::Error for FromHexError {
fn description(&self) -> &str {
match *self {
FromHexError::InvalidHexCharacter { .. } => "invalid character",
FromHexError::OddLength => "odd number of digits",
FromHexError::InvalidStringLength => "invalid string length",
}
}
}
impl fmt::Display for FromHexError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FromHexError::InvalidHexCharacter { c, index } =>
write!(f, "Invalid character '{}' at position {}", c, index),
FromHexError::OddLength =>
write!(f, "Odd number of digits"),
FromHexError::InvalidStringLength =>
write!(f, "Invalid string length"),
}
}
}
pub trait FromHex: Sized {
type Error;
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error>;
}
fn val(c: u8, idx: usize) -> Result<u8, FromHexError> {
match c {
b'A'..=b'F' => Ok(c - b'A' + 10),
b'a'..=b'f' => Ok(c - b'a' + 10),
b'0'..=b'9' => Ok(c - b'0'),
_ => {
Err(FromHexError::InvalidHexCharacter {
c: c as char,
index: idx,
})
}
}
}
impl FromHex for Vec<u8> {
type Error = FromHexError;
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
let hex = hex.as_ref();
if hex.len() % 2 != 0 {
return Err(FromHexError::OddLength);
}
hex.chunks(2).enumerate().map(|(i, pair)| {
Ok(val(pair[0], 2 * i)? << 4 | val(pair[1], 2 * i + 1)?)
}).collect()
}
}
macro_rules! impl_from_hex_for_array {
($len:expr) => {
impl FromHex for [u8; $len] {
type Error = FromHexError;
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
let hex = hex.as_ref();
if hex.len() % 2 != 0 {
return Err(FromHexError::OddLength);
}
if hex.len() / 2 != $len {
return Err(FromHexError::InvalidStringLength);
}
let mut out = [0; $len];
for (i, byte) in out.iter_mut().enumerate() {
*byte = val(hex[2 * i], 2 * i)? << 4
| val(hex[2 * i + 1], 2 * i + 1)?;
}
Ok(out)
}
}
}
}
impl_from_hex_for_array!(1);
impl_from_hex_for_array!(2);
impl_from_hex_for_array!(3);
impl_from_hex_for_array!(4);
impl_from_hex_for_array!(5);
impl_from_hex_for_array!(6);
impl_from_hex_for_array!(7);
impl_from_hex_for_array!(8);
impl_from_hex_for_array!(9);
impl_from_hex_for_array!(10);
impl_from_hex_for_array!(11);
impl_from_hex_for_array!(12);
impl_from_hex_for_array!(13);
impl_from_hex_for_array!(14);
impl_from_hex_for_array!(15);
impl_from_hex_for_array!(16);
impl_from_hex_for_array!(24);
impl_from_hex_for_array!(32);
impl_from_hex_for_array!(40);
impl_from_hex_for_array!(48);
impl_from_hex_for_array!(56);
impl_from_hex_for_array!(64);
pub fn encode<T: AsRef<[u8]>>(data: T) -> String {
let mut s = String::with_capacity(data.as_ref().len() * 2);
data.write_hex(&mut s).unwrap();
s
}
#[allow(dead_code)]
pub fn encode_upper<T: AsRef<[u8]>>(data: T) -> String {
let mut s = String::with_capacity(data.as_ref().len() * 2);
data.write_hex_upper(&mut s).unwrap();
s
}
pub fn decode<T: AsRef<[u8]>>(data: T) -> Result<Vec<u8>, FromHexError> {
FromHex::from_hex(data)
}
#[cfg(test)]
mod test {
use super::{encode, decode, FromHex, FromHexError};
#[test]
fn test_encode() {
assert_eq!(encode("foobar"), "666f6f626172");
}
#[test]
fn test_decode() {
assert_eq!(decode("666f6f626172"), Ok("foobar".to_owned().into_bytes()));
}
#[test]
pub fn test_from_hex_okay_str() {
assert_eq!(
Vec::from_hex("666f6f626172").unwrap(),
b"foobar"
);
assert_eq!(
Vec::from_hex("666F6F626172").unwrap(),
b"foobar"
);
}
#[test]
pub fn test_from_hex_okay_bytes() {
assert_eq!(
Vec::from_hex(b"666f6f626172").unwrap(),
b"foobar"
);
assert_eq!(
Vec::from_hex(b"666F6F626172").unwrap(),
b"foobar"
);
}
#[test]
pub fn test_invalid_length() {
assert_eq!(
Vec::from_hex("1").unwrap_err(),
FromHexError::OddLength
);
assert_eq!(
Vec::from_hex("666f6f6261721").unwrap_err(),
FromHexError::OddLength
);
}
#[test]
pub fn test_invalid_char() {
assert_eq!(
Vec::from_hex("66ag").unwrap_err(),
FromHexError::InvalidHexCharacter {
c: 'g',
index: 3
}
);
}
#[test]
pub fn test_empty() {
assert_eq!(Vec::from_hex("").unwrap(), b"");
}
#[test]
pub fn test_from_hex_whitespace() {
assert_eq!(
Vec::from_hex("666f 6f62617").unwrap_err(),
FromHexError::InvalidHexCharacter {
c: ' ',
index: 4
}
);
}
#[test]
pub fn test_from_hex_array() {
assert_eq!(
<[u8; 6] as FromHex>::from_hex("666f6f626172"),
Ok([0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72])
);
assert_eq!(
<[u8; 5] as FromHex>::from_hex("666f6f626172"),
Err(FromHexError::InvalidStringLength)
);
}
}