use core::{cmp::min, fmt, ops::Add, str};
use typenum::*;
use crate::{ArrayLength, GenericArray};
#[inline(always)]
fn hex_encode_fallback<const UPPER: bool>(src: &[u8], dst: &mut [u8]) {
if dst.len() < src.len() * 2 {
unsafe { core::hint::unreachable_unchecked() };
}
let alphabet = match UPPER {
true => b"0123456789ABCDEF",
false => b"0123456789abcdef",
};
dst.chunks_exact_mut(2).zip(src).for_each(|(s, c)| {
s[0] = alphabet[(c >> 4) as usize];
s[1] = alphabet[(c & 0xF) as usize];
});
}
#[inline]
fn hex_encode<const UPPER: bool>(src: &[u8], dst: &mut [u8]) {
debug_assert!(dst.len() >= (src.len() * 2));
#[cfg(any(miri, not(feature = "faster-hex")))]
hex_encode_fallback::<UPPER>(src, dst);
#[cfg(all(feature = "faster-hex", not(miri)))]
match UPPER {
true => unsafe { faster_hex::hex_encode_upper(src, dst).unwrap_unchecked() },
false => unsafe { faster_hex::hex_encode(src, dst).unwrap_unchecked() },
};
}
fn generic_hex<N: ArrayLength, const UPPER: bool>(
arr: &GenericArray<u8, N>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result
where
N: Add<N>,
Sum<N, N>: ArrayLength,
{
let max_digits = N::USIZE * 2;
let max_digits = match f.precision() {
Some(precision) if precision < max_digits => precision,
_ => max_digits,
};
let max_bytes = (max_digits >> 1) + (max_digits & 1);
let input = {
if max_bytes > N::USIZE {
unsafe { core::hint::unreachable_unchecked() };
}
&arr[..max_bytes]
};
if N::USIZE <= 1024 {
let mut buf = GenericArray::<u8, Sum<N, N>>::default();
if N::USIZE < 16 {
hex_encode_fallback::<UPPER>(arr, &mut buf);
} else {
hex_encode::<UPPER>(input, &mut buf);
}
f.write_str(unsafe { str::from_utf8_unchecked(buf.get_unchecked(..max_digits)) })?;
} else {
let mut buf = [0u8; 2048];
let mut digits_left = max_digits;
for chunk in input.chunks(1024) {
hex_encode::<UPPER>(chunk, &mut buf);
let n = min(chunk.len() * 2, digits_left);
f.write_str(unsafe { str::from_utf8_unchecked(buf.get_unchecked(..n)) })?;
digits_left -= n;
}
}
Ok(())
}
impl<N: ArrayLength> fmt::LowerHex for GenericArray<u8, N>
where
N: Add<N>,
Sum<N, N>: ArrayLength,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
generic_hex::<_, false>(self, f)
}
}
impl<N: ArrayLength> fmt::UpperHex for GenericArray<u8, N>
where
N: Add<N>,
Sum<N, N>: ArrayLength,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
generic_hex::<_, true>(self, f)
}
}