use crate::{byte2hex, Output, HEX_DECODE_LUT, NIL};
use core::mem::size_of;
#[allow(dead_code)]
pub(crate) const USE_CHECK_FN: bool = false;
pub(crate) unsafe fn encode<const UPPER: bool>(input: &[u8], mut output: impl Output) {
for &byte in input {
let (high, low) = byte2hex::<UPPER>(byte);
output.write_byte(high);
output.write_byte(low);
}
}
#[inline]
#[allow(dead_code)]
pub(crate) unsafe fn encode_unaligned_chunks<const UPPER: bool, T: Copy, U: Copy>(
input: &[u8],
mut output: impl Output,
mut encode_chunk: impl FnMut(T) -> U,
) {
debug_assert_eq!(size_of::<U>(), size_of::<T>() * 2);
let (chunks, remainder) = chunks_unaligned::<T>(input);
for chunk in chunks {
output.write(as_bytes(&encode_chunk(chunk)));
}
unsafe { encode::<UPPER>(remainder, output) };
}
#[inline]
#[allow(dead_code)]
pub(crate) unsafe fn encode_unaligned_chunks_with<
const UPPER: bool,
T: Copy,
U: Copy,
O: Output,
>(
input: &[u8],
mut output: O,
mut encode_chunk: impl FnMut(T) -> U,
encode_remainder: impl FnOnce(&[u8], O),
) {
debug_assert_eq!(size_of::<U>(), size_of::<T>() * 2);
let (chunks, remainder) = chunks_unaligned::<T>(input);
for chunk in chunks {
output.write(as_bytes(&encode_chunk(chunk)));
}
if !remainder.is_empty() {
encode_remainder(remainder, output);
}
}
#[inline]
#[allow(dead_code)]
pub(crate) unsafe fn encode_one_unaligned_chunk<const UPPER: bool, T: Copy, U: Copy>(
input: &[u8],
mut output: impl Output,
encode_chunk: impl FnOnce(T) -> U,
) {
debug_assert_eq!(size_of::<U>(), size_of::<T>() * 2);
if input.len() >= size_of::<T>() {
debug_assert!(input.len() < size_of::<T>() * 2);
let (l, r) = input.split_at(size_of::<T>());
let chunk = l.as_ptr().cast::<T>().read_unaligned();
output.write(as_bytes(&encode_chunk(chunk)));
encode::<UPPER>(r, output);
} else {
encode::<UPPER>(input, output);
}
}
#[inline]
pub(crate) const fn check(mut input: &[u8]) -> bool {
while let &[byte, ref rest @ ..] = input {
if HEX_DECODE_LUT[byte as usize] == NIL {
return false;
}
input = rest;
}
true
}
#[inline]
#[allow(dead_code)]
pub(crate) fn check_unaligned_chunks<T: Copy>(
input: &[u8],
check_chunk: impl FnMut(T) -> bool,
) -> bool {
check_unaligned_chunks_with(input, check_chunk, check)
}
#[inline]
#[allow(dead_code)]
pub(crate) fn check_unaligned_chunks_with<T: Copy>(
input: &[u8],
check_chunk: impl FnMut(T) -> bool,
check_remainder: impl FnOnce(&[u8]) -> bool,
) -> bool {
let (mut chunks, remainder) = chunks_unaligned(input);
chunks.all(check_chunk) && (remainder.is_empty() || check_remainder(remainder))
}
#[inline]
#[allow(dead_code)]
pub(crate) fn check_one_unaligned_chunk<T: Copy>(
input: &[u8],
check_chunk: impl FnOnce(T) -> bool,
) -> bool {
if input.len() >= size_of::<T>() {
debug_assert!(input.len() < size_of::<T>() * 2);
let (l, r) = input.split_at(size_of::<T>());
let chunk = unsafe { l.as_ptr().cast::<T>().read_unaligned() };
check_chunk(chunk) && check(r)
} else {
check(input)
}
}
#[allow(dead_code)]
pub(crate) unsafe fn decode_checked(input: &[u8], output: &mut [u8]) -> bool {
unsafe { decode_maybe_check::<true>(input, output) }
}
pub(crate) unsafe fn decode_unchecked(input: &[u8], output: impl Output) {
#[allow(unused_braces)] let success = unsafe { decode_maybe_check::<{ cfg!(debug_assertions) }>(input, output) };
debug_assert!(success);
}
#[inline(always)]
unsafe fn decode_maybe_check<const CHECK: bool>(input: &[u8], mut output: impl Output) -> bool {
let l = output.remaining().unwrap_or(input.len() / 2);
debug_assert_eq!(l, input.len() / 2);
let mut i = 0;
while i < l {
let hex = unsafe { *input.get_unchecked(i * 2) };
let high = HEX_DECODE_LUT[hex as usize];
let hex = unsafe { *input.get_unchecked(i * 2 + 1) };
let low = HEX_DECODE_LUT[hex as usize];
if CHECK && (low | high) == NIL {
return false;
}
output.write_byte(high << 4 | low);
i += 1;
}
true
}
#[inline]
#[allow(dead_code)]
pub(crate) unsafe fn decode_unchecked_unaligned_chunks<T: Copy, U: Copy>(
input: &[u8],
mut output: impl Output,
mut decode_chunk: impl FnMut(U) -> T,
) {
debug_assert_eq!(size_of::<U>(), size_of::<T>() * 2);
let (chunks, remainder) = chunks_unaligned::<U>(input);
for chunk in chunks {
output.write(as_bytes(&decode_chunk(chunk)));
}
unsafe { decode_unchecked(remainder, output) };
}
#[inline]
#[allow(dead_code)]
pub(crate) unsafe fn decode_checked_unaligned_chunks<T: Copy, U: Copy>(
input: &[u8],
output: impl Output,
decode_chunk: impl FnMut(U) -> Option<T>,
) -> bool {
decode_checked_unaligned_chunks_with(input, output, decode_chunk, |remainder, out| unsafe {
decode_maybe_check::<true>(remainder, out)
})
}
#[inline]
#[allow(dead_code)]
pub(crate) unsafe fn decode_checked_unaligned_chunks_with<T: Copy, U: Copy, O: Output>(
input: &[u8],
mut output: O,
mut decode_chunk: impl FnMut(U) -> Option<T>,
decode_remainder: impl FnOnce(&[u8], O) -> bool,
) -> bool {
debug_assert_eq!(size_of::<U>(), size_of::<T>() * 2);
let (chunks, remainder) = chunks_unaligned::<U>(input);
for chunk in chunks {
match decode_chunk(chunk) {
Some(decoded) => output.write(as_bytes(&decoded)),
None => return false,
}
}
if !remainder.is_empty() {
decode_remainder(remainder, output)
} else {
true
}
}
#[inline]
#[allow(dead_code)]
pub(crate) unsafe fn decode_checked_one_unaligned_chunk<T: Copy, U: Copy>(
input: &[u8],
mut output: impl Output,
decode_chunk: impl FnOnce(U) -> Option<T>,
) -> bool {
debug_assert_eq!(size_of::<U>(), size_of::<T>() * 2);
if input.len() >= size_of::<U>() {
debug_assert!(input.len() < size_of::<U>() * 2);
let (l, r) = input.split_at(size_of::<U>());
let chunk = unsafe { l.as_ptr().cast::<U>().read_unaligned() };
match decode_chunk(chunk) {
Some(decoded) => {
output.write(as_bytes(&decoded));
unsafe { decode_maybe_check::<true>(r, output) }
}
None => false,
}
} else {
unsafe { decode_maybe_check::<true>(input, output) }
}
}
#[inline]
fn chunks_unaligned<T: Copy>(input: &[u8]) -> (impl ExactSizeIterator<Item = T> + '_, &[u8]) {
let chunks = input.chunks_exact(core::mem::size_of::<T>());
let remainder = chunks.remainder();
(
chunks.map(|chunk| unsafe { chunk.as_ptr().cast::<T>().read_unaligned() }),
remainder,
)
}
#[inline]
const fn as_bytes<T: Copy>(x: &T) -> &[u8] {
unsafe { core::slice::from_raw_parts(x as *const _ as *const u8, size_of::<T>()) }
}