#[derive(Debug, Eq, PartialEq, thiserror::Error)]
pub enum HexError {
#[error("invalid character in hex input")]
InvalidCharacter,
#[error("hex padding required")]
PaddingRequired,
}
#[inline]
fn from_hex_byte(b: u8) -> Result<u8, HexError> {
match b {
48..=57 => Ok(b - 48),
65..=70 => Ok(b - 65 + 10),
97..=102 => Ok(b - 97 + 10),
_ => Err(HexError::InvalidCharacter),
}
}
pub fn decode(from: &[u8], to: &mut [u8]) -> Result<(), HexError> {
if from.len() % 2 != 0 {
return Err(HexError::PaddingRequired);
}
assert_eq!(from.len(), to.len() * 2);
for i in 0..to.len() {
let hi = from[2 * i];
let lo = from[2 * i + 1];
to[i] = from_hex_byte(hi)? << 4 | from_hex_byte(lo)?;
}
Ok(())
}
pub fn decode_string(from: &str, to: &mut [u8]) -> Result<(), HexError> {
decode(from.as_bytes(), to)
}
pub fn easy_decode_string(from: &str) -> Result<Vec<u8>, HexError> {
let n = from.len() / 2;
let mut v = vec![0; n];
match decode_string(from, &mut v) {
Ok(()) => Ok(v),
Err(e) => Err(e),
}
}
#[inline]
fn to_hex_bytes(b: u8) -> (u8, u8) {
let tab = b"0123456789abcdef";
let hi = (b & 0xf0) >> 4;
let lo = b & 0x0f;
(tab[hi as usize], tab[lo as usize])
}
#[inline]
fn to_hex_chars(b: u8) -> (char, char) {
let (hi, lo) = to_hex_bytes(b);
(hi as char, lo as char)
}
pub fn encode(from: &[u8], to: &mut [u8]) {
assert!(to.len() == 2 * from.len());
for i in 0..from.len() {
let (hi, lo) = to_hex_bytes(from[i]);
to[2 * i] = hi;
to[2 * i + 1] = lo;
}
}
pub fn easy_encode_to_string(from: &[u8]) -> String {
let mut s = String::with_capacity(2 * from.len());
for b in from {
let (hi, lo) = to_hex_chars(*b);
s.push(hi);
s.push(lo);
}
s
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode() {
let buf: [u8; 8] = [18, 52, 86, 120, 154, 188, 222, 240];
let mut encoded: [u8; 16] = [0; 16];
encode(&buf, &mut encoded);
assert_eq!(std::str::from_utf8(&encoded).unwrap(), "123456789abcdef0");
}
#[test]
fn test_easy_encode_to_string() {
let buf: [u8; 8] = [18, 52, 86, 120, 154, 188, 222, 240];
assert_eq!(easy_encode_to_string(&buf), "123456789abcdef0");
}
#[test]
fn test_easy_decode_string() {
let buf: [u8; 8] = [18, 52, 86, 120, 154, 188, 222, 240];
assert_eq!(
easy_decode_string("123456789abcdef0").unwrap().as_slice(),
&buf[..]
);
assert_eq!(
easy_decode_string("123456789ABCDEF0").unwrap().as_slice(),
&buf[..]
);
assert_eq!(
easy_decode_string("1234!6789ABCDEF0").unwrap_err(),
HexError::InvalidCharacter
);
assert_eq!(
easy_decode_string("23456789ABCDEF0").unwrap_err(),
HexError::PaddingRequired
);
}
}