1use thiserror::Error;
4
5#[derive(Debug, Clone, PartialEq, Eq, Error)]
7pub enum HexDecodeError {
8 #[error("odd hex length {length}")]
10 OddLength {
11 length: usize,
13 },
14 #[error("invalid hex byte {byte:#04x}")]
16 InvalidByte {
17 byte: u8,
19 },
20}
21
22pub fn hex_encode_bytes(bytes: &[u8]) -> String {
24 const HEX: &[u8; 16] = b"0123456789abcdef";
25 let mut encoded = String::with_capacity(bytes.len() * 2);
26 for byte in bytes {
27 encoded.push(char::from(HEX[(byte >> 4) as usize]));
28 encoded.push(char::from(HEX[(byte & 0x0f) as usize]));
29 }
30 encoded
31}
32
33pub fn hex_decode_bytes(encoded: &str) -> Result<Vec<u8>, HexDecodeError> {
36 fn nibble(byte: u8) -> Result<u8, HexDecodeError> {
37 match byte {
38 b'0'..=b'9' => Ok(byte - b'0'),
39 b'a'..=b'f' => Ok(byte - b'a' + 10),
40 _ => Err(HexDecodeError::InvalidByte { byte }),
41 }
42 }
43
44 let bytes = encoded.as_bytes();
45 if bytes.len() % 2 != 0 {
46 return Err(HexDecodeError::OddLength {
47 length: bytes.len(),
48 });
49 }
50 let mut decoded = Vec::with_capacity(bytes.len() / 2);
51 for pair in bytes.chunks_exact(2) {
52 decoded.push((nibble(pair[0])? << 4) | nibble(pair[1])?);
53 }
54 Ok(decoded)
55}