use core::mem::MaybeUninit;
use crate::error::InvalidInput;
use crate::util::lut16;
#[allow(unsafe_code, reason = "XXX")]
pub(crate) const unsafe fn encode_generic_unchecked<const UPPER: bool>(
src: &[u8],
dst: &mut [[MaybeUninit<u8>; 2]],
) {
const LUT_ENCODE_LOWERCASE: [[u8; 2]; const { u8::MAX as usize + 1 }] = const {
let mut lut = [[0; 2]; _];
let mut i: usize = 0;
while i <= u8::MAX as usize {
lut[i][0] = lut16::<false>()[i >> 4];
lut[i][1] = lut16::<false>()[i & 0b1111];
i += 1;
}
lut
};
const LUT_ENCODE_UPPERCASE: [[u8; 2]; const { u8::MAX as usize + 1 }] = const {
let mut lut = [[0; 2]; _];
let mut i: usize = 0;
while i <= u8::MAX as usize {
lut[i][0] = lut16::<true>()[i >> 4];
lut[i][1] = lut16::<true>()[i & 0b1111];
i += 1;
}
lut
};
debug_assert!(
src.len() <= dst.len(),
"implementation bug: `src` should not be longer than `dst`"
);
let lut = if UPPER {
&LUT_ENCODE_UPPERCASE
} else {
&LUT_ENCODE_LOWERCASE
};
let mut i = 0;
while i < src.len() {
unsafe {
*dst.as_mut_ptr().add(i).cast::<[u8; 2]>() = lut[src[i] as usize];
}
i += 1;
}
}
#[allow(unsafe_code, reason = "XXX")]
pub(crate) const unsafe fn decode_generic_unchecked<const VALIDATED: bool>(
src: *const [[u8; 2]],
dst: *mut [MaybeUninit<u8>],
) -> Result<(), InvalidInput> {
const NIL: u8 = u8::MAX;
const LUT: [u8; u8::MAX as usize + 1] = {
let mut lut = [NIL; _];
let mut i = 0;
while i <= u8::MAX as usize {
#[allow(clippy::cast_possible_truncation, reason = "XXX")]
match i as u8 {
b'0'..=b'9' => {
lut[i] = i as u8 - b'0';
}
b'a'..=b'f' => {
lut[i] = i as u8 - b'a' + 10;
}
b'A'..=b'F' => {
lut[i] = i as u8 - b'A' + 10;
}
_ => {}
};
i += 1;
}
lut
};
debug_assert!(
src.len() <= dst.len(),
"implementation bug: `src` should not be longer than `dst`"
);
let mut i = 0;
while i < src.len() {
let [hi, lo] = *((src.cast::<[u8; 2]>()).add(i));
let [hi, lo] = [LUT[hi as usize], LUT[lo as usize]];
if !VALIDATED && (hi | lo == NIL) {
return Err(InvalidInput);
}
(&mut *(dst.cast::<MaybeUninit<u8>>()).add(i)).write(hi << 4 | lo);
i += 1;
}
Ok(())
}
#[cfg(test)]
mod smoking {
use super::*;
use crate::backend::tests::{
check_decode_validation_any_backend, check_encode_decode_any_backend,
};
#[test]
fn test_encode_decode_generic() {
check_encode_decode_any_backend::<true>(
encode_generic_unchecked::<true>,
decode_generic_unchecked::<false>,
);
check_encode_decode_any_backend::<false>(
encode_generic_unchecked::<false>,
decode_generic_unchecked::<false>,
);
}
#[test]
fn test_decode_validation_generic() {
check_decode_validation_any_backend(decode_generic_unchecked::<false>);
}
}