keyfork/crates/util/smex/src/lib.rs

65 lines
1.7 KiB
Rust
Raw Normal View History

2024-01-16 02:44:48 +00:00
//! Zero-dependency hex encoding and decoding.
use std::fmt::Write;
2024-01-16 02:44:48 +00:00
/// The type could not be decoded.
#[derive(Debug)]
pub enum DecodeError {
2024-01-16 02:44:48 +00:00
/// An invalid character was encountered.
InvalidCharacter(u8),
2024-01-16 02:44:48 +00:00
/// The amount of characters was invalid. Hex strings must be in pairs of two.
InvalidCharacterCount(usize),
}
impl std::fmt::Display for DecodeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidCharacter(c) => {
write!(f, "Invalid character: {c} not in [0123456789ABCDEF]")
}
Self::InvalidCharacterCount(n) => {
write!(f, "Invalid character count: {n} % 2 != 0")
}
}
}
}
impl std::error::Error for DecodeError {}
2024-01-16 02:44:48 +00:00
/// Encode a given input as a hex string.
pub fn encode(input: &[u8]) -> String {
let mut s = String::new();
for byte in input {
write!(s, "{byte:02x}").unwrap();
}
s
}
fn val(c: u8) -> Result<u8, DecodeError> {
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(DecodeError::InvalidCharacter(c)),
}
}
2024-01-16 02:44:48 +00:00
/// Attempt to decode a string as hex.
///
/// # Errors
/// The function may error if a non-hex character is encountered or if the character count is not
/// evenly divisible by two.
pub fn decode(input: &str) -> Result<Vec<u8>, DecodeError> {
let len = input.len();
if len % 2 != 0 {
return Err(DecodeError::InvalidCharacterCount(len));
}
input
.as_bytes()
.chunks_exact(2)
.map(|pair| Ok(val(pair[0])? << 4 | val(pair[1])?))
.collect()
}