use crate::Error::{InsufficientTargetSpace, InvalidEncodedData};
use crate::{Decoder, Error};
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Default)]
#[non_exhaustive]
pub struct HexDecoder {}
impl HexDecoder {
const DECODING_TABLE: [u8; 128] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
}
impl HexDecoder {
#[inline(always)]
pub const fn decode_bytes(high: u8, low: u8) -> u8 {
(Self::DECODING_TABLE[(high & 0x7F) as usize] << 4)
| Self::DECODING_TABLE[(low & 0x7F) as usize]
}
#[inline(always)]
pub const fn decode_chars(high: char, low: char) -> u8 {
Self::decode_bytes(((high as u32) & 0xFF) as u8, ((low as u32) & 0xFF) as u8)
}
}
impl Decoder for HexDecoder {
fn decoded_len(&self, data: &[u8]) -> Result<usize, Error> {
let div: usize = data.len() / 2;
let rem: usize = data.len() % 2;
if rem == 1 {
Err(InvalidEncodedData {
reason: Some(format!("odd number of bytes: {}", data.len()).into()),
})
} else {
Ok(div)
}
}
fn decode_to_slice(&self, data: &[u8], target: &mut [u8]) -> Result<usize, Error> {
let decoded_len: usize = self.decoded_len(data)?;
if decoded_len > target.len() {
Err(InsufficientTargetSpace)
} else {
let target: &mut [u8] = &mut target[..decoded_len];
for (d, t) in data.chunks_exact(2).zip(target.iter_mut()) {
*t = Self::decode_bytes(d[0], d[1])
}
Ok(decoded_len)
}
}
}
#[cfg(test)]
#[cfg(feature = "dev")]
mod tests {
use crate::Decoder;
use crate::hex::HexDecoder;
use crate::test::test_decoder;
#[test]
fn decode_bytes_decode_chars() {
let cases: &[(u8, u8, u8)] = &[
(b'0', b'0', 0x00),
(b'F', b'F', 0xFF),
(b'a', b'b', 0xAB),
(b'A', b'B', 0xAB),
(b'a', b'B', 0xAB),
(b'A', b'b', 0xAB),
(b'0', b'9', 0x09),
(b'f', b'f', 0xFF),
];
for (high, low, expected) in cases {
assert_eq!(HexDecoder::decode_bytes(*high, *low), *expected);
assert_eq!(
HexDecoder::decode_chars(*high as char, *low as char),
*expected
);
}
}
#[test]
fn decode_odd_length() {
let decoder: HexDecoder = HexDecoder::default();
let mut target: Vec<u8> = vec![0u8; 4];
assert!(matches!(
decoder.decode_to_slice(b"FFF", &mut target),
Err(crate::Error::InvalidEncodedData { .. })
));
}
#[test]
fn decode_insufficient_space() {
let decoder: HexDecoder = HexDecoder::default();
let mut target: Vec<u8> = vec![];
assert!(matches!(
decoder.decode_to_slice(b"FF", &mut target),
Err(crate::Error::InsufficientTargetSpace)
));
}
#[test]
fn decode() {
let test_cases: &[(&str, &[u8])] = &[
("", b""),
("0123456789", b"\x01\x23\x45\x67\x89"),
("1032547698", b"\x10\x32\x54\x76\x98"),
("abcdef", b"\xAB\xCD\xEF"),
("badcfe", b"\xBA\xDC\xFE"),
("ABCDEF", b"\xAB\xCD\xEF"),
("BADCFE", b"\xBA\xDC\xFE"),
("aBcDeF", b"\xAB\xCD\xEF"),
("AbCdEf", b"\xAB\xCD\xEF"),
("BaDcFe", b"\xBA\xDC\xFE"),
("bAdCfE", b"\xBA\xDC\xFE"),
];
test_decoder(&HexDecoder::default(), test_cases);
}
}