1use core::{cmp::min, fmt, ops::Add, str};
16
17use typenum::*;
18
19use crate::{ArrayLength, GenericArray};
20
21#[inline(always)]
22fn hex_encode_fallback<const UPPER: bool>(src: &[u8], dst: &mut [u8]) {
23 if dst.len() < src.len() * 2 {
24 unsafe { core::hint::unreachable_unchecked() };
25 }
26
27 let alphabet = match UPPER {
28 true => b"0123456789ABCDEF",
29 false => b"0123456789abcdef",
30 };
31
32 dst.chunks_exact_mut(2).zip(src).for_each(|(s, c)| {
33 s[0] = alphabet[(c >> 4) as usize];
34 s[1] = alphabet[(c & 0xF) as usize];
35 });
36}
37
38#[inline]
39fn hex_encode<const UPPER: bool>(src: &[u8], dst: &mut [u8]) {
40 debug_assert!(dst.len() >= (src.len() * 2));
41
42 #[cfg(any(miri, not(feature = "faster-hex")))]
43 hex_encode_fallback::<UPPER>(src, dst);
44
45 #[cfg(all(feature = "faster-hex", not(miri)))]
47 match UPPER {
48 true => unsafe { faster_hex::hex_encode_upper(src, dst).unwrap_unchecked() },
49 false => unsafe { faster_hex::hex_encode(src, dst).unwrap_unchecked() },
50 };
51}
52
53fn generic_hex<N, const UPPER: bool>(
54 arr: &GenericArray<u8, N>,
55 f: &mut fmt::Formatter<'_>,
56) -> fmt::Result
57where
58 N: ArrayLength + Add<N>,
59 Sum<N, N>: ArrayLength,
60{
61 let max_digits = N::USIZE * 2;
62 let max_digits = match f.precision() {
63 Some(precision) if precision < max_digits => precision,
64 _ => max_digits,
65 };
66
67 let max_bytes = (max_digits >> 1) + (max_digits & 1);
69
70 let input = {
71 if max_bytes > N::USIZE {
73 unsafe { core::hint::unreachable_unchecked() };
74 }
75
76 &arr[..max_bytes]
77 };
78
79 if N::USIZE <= 1024 {
80 let mut buf = GenericArray::<u8, Sum<N, N>>::default();
82
83 if N::USIZE < 16 {
84 hex_encode_fallback::<UPPER>(arr, &mut buf);
88 } else {
89 hex_encode::<UPPER>(input, &mut buf);
90 }
91
92 f.write_str(unsafe { str::from_utf8_unchecked(buf.get_unchecked(..max_digits)) })?;
93 } else {
94 let mut buf = [0u8; 2048];
96 let mut digits_left = max_digits;
97
98 for chunk in input.chunks(1024) {
99 hex_encode::<UPPER>(chunk, &mut buf);
100
101 let n = min(chunk.len() * 2, digits_left);
102 f.write_str(unsafe { str::from_utf8_unchecked(buf.get_unchecked(..n)) })?;
104 digits_left -= n;
105 }
106 }
107 Ok(())
108}
109
110impl<N: ArrayLength> fmt::LowerHex for GenericArray<u8, N>
111where
112 N: Add<N>,
113 Sum<N, N>: ArrayLength,
114{
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 generic_hex::<_, false>(self, f)
117 }
118}
119
120impl<N: ArrayLength> fmt::UpperHex for GenericArray<u8, N>
121where
122 N: Add<N>,
123 Sum<N, N>: ArrayLength,
124{
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 generic_hex::<_, true>(self, f)
127 }
128}