use crate::Error;
#[inline]
pub fn decoded_len(hex_len: usize) -> Result<usize, Error> {
if hex_len.is_multiple_of(2) {
Ok(hex_len / 2)
} else {
Err(Error::OddLength)
}
}
#[inline]
pub fn decode_to_slice(src_hex: &[u8], dst: &mut [u8]) -> Result<usize, Error> {
let out_len = decoded_len(src_hex.len())?;
if dst.len() < out_len {
return Err(Error::OutputTooSmall);
}
#[cfg(feature = "simd")]
{
crate::simd::decode_to_slice_simd(src_hex, &mut dst[..out_len])
}
#[cfg(not(feature = "simd"))]
{
decode_scalar(src_hex, &mut dst[..out_len])
}
}
pub fn decode_to_array<const N: usize>(src_hex: &[u8]) -> Result<[u8; N], Error> {
let out_len = decoded_len(src_hex.len())?;
if out_len != N {
return Err(Error::OutputTooSmall);
}
let mut arr = [0u8; N];
decode_to_slice(src_hex, &mut arr)?;
Ok(arr)
}
#[inline]
pub fn decode_in_place(buf: &mut [u8]) -> Result<usize, Error> {
let out_len = decoded_len(buf.len())?;
for i in 0..out_len {
let hi = buf[2 * i];
let lo = buf[2 * i + 1];
if unhex_byte(hi).is_none() {
return Err(Error::InvalidByte {
index: 2 * i,
byte: hi,
});
}
if unhex_byte(lo).is_none() {
return Err(Error::InvalidByte {
index: 2 * i + 1,
byte: lo,
});
}
}
for i in 0..out_len {
let hi = buf[2 * i];
let lo = buf[2 * i + 1];
buf[i] = u8::try_from(decode_pair(hi, lo)).unwrap();
}
Ok(out_len)
}
#[inline]
pub(crate) fn decode_scalar(src_hex: &[u8], dst: &mut [u8]) -> Result<usize, Error> {
let out_len = src_hex.len() >> 1;
let mut j = 0usize;
for out in dst.iter_mut().take(out_len) {
let hi = src_hex[j];
let lo = src_hex[j + 1];
let v = decode_pair(hi, lo);
if (v & 0x0100) != 0 {
if unhex_byte(hi).is_none() {
return Err(Error::InvalidByte { index: j, byte: hi });
}
return Err(Error::InvalidByte {
index: j + 1,
byte: lo,
});
}
*out = u8::try_from(v).unwrap();
j += 2;
}
Ok(out_len)
}
#[inline]
pub(crate) fn unhex_byte(b: u8) -> Option<u8> {
let v = UNHEX_TABLE[b as usize];
if v == 0xFF {
None
} else {
Some(v)
}
}
const UNHEX_TABLE: [u8; 256] = make_unhex_table();
static HEXPAIR_TABLE: [u16; 65536] = make_hexpair_table();
#[inline]
fn decode_pair(hi: u8, lo: u8) -> u16 {
let idx = ((hi as usize) << 8) | (lo as usize);
HEXPAIR_TABLE[idx]
}
const fn make_unhex_table() -> [u8; 256] {
let mut t = [0xFFu8; 256];
let mut b = 0u8;
loop {
t[b as usize] = if b >= b'0' && b <= b'9' {
b - b'0'
} else if b >= b'a' && b <= b'f' {
b - b'a' + 10
} else if b >= b'A' && b <= b'F' {
b - b'A' + 10
} else {
0xFF
};
if b == u8::MAX {
break;
}
b = b.wrapping_add(1);
}
t
}
#[allow(clippy::large_stack_arrays)]
const fn make_hexpair_table() -> [u16; 65536] {
let mut t = [0x0100u16; 65536];
let unhex = make_unhex_table();
let mut hi = 0u32;
while hi < 256 {
let mut lo = 0u32;
while lo < 256 {
let hn = unhex[hi as usize];
let ln = unhex[lo as usize];
if hn != 0xFF && ln != 0xFF {
let out = ((hn as u16) << 4) | (ln as u16);
t[((hi as usize) << 8) | (lo as usize)] = out;
}
lo += 1;
}
hi += 1;
}
t
}
#[cfg(test)]
#[path = "decode/tests.rs"]
mod tests;