1use core::borrow::Borrow;
29use core::fmt;
30
31use super::Case;
32use crate::buf_encoder::{BufEncoder, FixedLenBuf, OutBytes};
33#[cfg(feature = "alloc")]
34use crate::prelude::*;
35
36pub trait DisplayHex: Copy + sealed::IsRef {
45 type Display: fmt::Display + fmt::Debug + fmt::LowerHex + fmt::UpperHex;
49
50 fn as_hex(self) -> Self::Display;
52
53 #[cfg(feature = "alloc")]
59 fn to_lower_hex_string(self) -> String { self.to_hex_string(Case::Lower) }
60
61 #[cfg(feature = "alloc")]
67 fn to_upper_hex_string(self) -> String { self.to_hex_string(Case::Upper) }
68
69 #[cfg(feature = "alloc")]
73 fn to_hex_string(self, case: Case) -> String {
74 let mut string = String::new();
75 self.append_hex_to_string(case, &mut string);
76 string
77 }
78
79 #[cfg(feature = "alloc")]
84 fn append_hex_to_string(self, case: Case, string: &mut String) {
85 use fmt::Write;
86
87 string.reserve(self.hex_reserve_suggestion());
88 match case {
89 Case::Lower => write!(string, "{:x}", self.as_hex()),
90 Case::Upper => write!(string, "{:X}", self.as_hex()),
91 }
92 .unwrap_or_else(|_| {
93 let name = core::any::type_name::<Self::Display>();
94 panic!("The implementation of Display for {} returned an error when it shouldn't", name)
97 })
98 }
99
100 fn hex_reserve_suggestion(self) -> usize { 0 }
107}
108
109mod sealed {
110 pub trait IsRef: Copy {}
112
113 impl<T: ?Sized> IsRef for &'_ T {}
114}
115
116impl<'a> DisplayHex for &'a [u8] {
117 type Display = DisplayByteSlice<'a>;
118
119 #[inline]
120 fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } }
121
122 #[inline]
123 fn hex_reserve_suggestion(self) -> usize {
124 self.len().checked_mul(2).expect("the string wouldn't fit into address space")
128 }
129}
130
131#[cfg(feature = "alloc")]
132impl<'a> DisplayHex for &'a alloc::vec::Vec<u8> {
133 type Display = DisplayByteSlice<'a>;
134
135 #[inline]
136 fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } }
137
138 #[inline]
139 fn hex_reserve_suggestion(self) -> usize {
140 self.len().checked_mul(2).expect("the string wouldn't fit into address space")
144 }
145}
146
147pub struct DisplayByteSlice<'a> {
151 pub(crate) bytes: &'a [u8],
153}
154
155impl<'a> DisplayByteSlice<'a> {
156 fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
157 let mut buf = [0u8; 1024];
158 let mut encoder = BufEncoder::new(&mut buf);
159
160 if self.bytes.len() < 512 {
163 encoder.put_bytes(self.bytes, case);
164 return f.pad(encoder.as_str());
165 }
166
167 let mut chunks = self.bytes.chunks_exact(512);
168 for chunk in &mut chunks {
169 encoder.put_bytes(chunk, case);
170 f.write_str(encoder.as_str())?;
171 encoder.clear();
172 }
173 encoder.put_bytes(chunks.remainder(), case);
174 f.write_str(encoder.as_str())
175 }
176}
177
178impl<'a> fmt::Display for DisplayByteSlice<'a> {
179 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
180}
181
182impl<'a> fmt::Debug for DisplayByteSlice<'a> {
183 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
184}
185
186impl<'a> fmt::LowerHex for DisplayByteSlice<'a> {
187 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
188}
189
190impl<'a> fmt::UpperHex for DisplayByteSlice<'a> {
191 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
192}
193
194pub struct DisplayArray<A: Clone + IntoIterator, B: FixedLenBuf>
199where
200 A::Item: Borrow<u8>,
201{
202 array: A,
203 _buffer_marker: core::marker::PhantomData<B>,
204}
205
206impl<A: Clone + IntoIterator, B: FixedLenBuf> DisplayArray<A, B>
207where
208 A::Item: Borrow<u8>,
209{
210 pub fn new(array: A) -> Self { DisplayArray { array, _buffer_marker: Default::default() } }
212
213 fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
214 let mut buf = B::uninit();
215 let mut encoder = BufEncoder::new(&mut buf);
216 encoder.put_bytes(self.array.clone(), case);
217 f.pad_integral(true, "0x", encoder.as_str())
218 }
219}
220
221impl<A: Clone + IntoIterator, B: FixedLenBuf> fmt::Display for DisplayArray<A, B>
222where
223 A::Item: Borrow<u8>,
224{
225 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
226}
227
228impl<A: Clone + IntoIterator, B: FixedLenBuf> fmt::Debug for DisplayArray<A, B>
229where
230 A::Item: Borrow<u8>,
231{
232 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
233}
234
235impl<A: Clone + IntoIterator, B: FixedLenBuf> fmt::LowerHex for DisplayArray<A, B>
236where
237 A::Item: Borrow<u8>,
238{
239 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
240}
241
242impl<A: Clone + IntoIterator, B: FixedLenBuf> fmt::UpperHex for DisplayArray<A, B>
243where
244 A::Item: Borrow<u8>,
245{
246 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
247}
248
249#[macro_export]
272macro_rules! fmt_hex_exact {
273 ($formatter:expr, $len:expr, $bytes:expr, $case:expr) => {{
274 #[allow(deprecated)]
276 const _: () = [()][($len > usize::MAX / 2) as usize];
277 assert_eq!($bytes.len(), $len);
278 let mut buf = [0u8; $len * 2];
279 let buf = $crate::buf_encoder::AsOutBytes::as_mut_out_bytes(&mut buf);
280 $crate::display::fmt_hex_exact_fn($formatter, buf, $bytes, $case)
281 }};
282}
283pub use fmt_hex_exact;
284
285#[doc(hidden)]
287#[inline]
288pub fn fmt_hex_exact_fn<I>(
289 f: &mut fmt::Formatter,
290 buf: &mut OutBytes,
291 bytes: I,
292 case: Case,
293) -> fmt::Result
294where
295 I: IntoIterator,
296 I::Item: Borrow<u8>,
297{
298 let mut encoder = BufEncoder::new(buf);
299 encoder.put_bytes(bytes, case);
300 f.pad_integral(true, "0x", encoder.as_str())
301}
302
303#[cfg(test)]
304mod tests {
305 #[cfg(feature = "alloc")]
306 use super::*;
307
308 #[cfg(feature = "alloc")]
309 mod alloc {
310 use super::*;
311
312 fn check_encoding(bytes: &[u8]) {
313 use core::fmt::Write;
314
315 let s1 = bytes.to_lower_hex_string();
316 let mut s2 = String::with_capacity(bytes.len() * 2);
317 for b in bytes {
318 write!(s2, "{:02x}", b).unwrap();
319 }
320 assert_eq!(s1, s2);
321 }
322
323 #[test]
324 fn empty() { check_encoding(b""); }
325
326 #[test]
327 fn single() { check_encoding(b"*"); }
328
329 #[test]
330 fn two() { check_encoding(b"*x"); }
331
332 #[test]
333 fn just_below_boundary() { check_encoding(&[42; 512]); }
334
335 #[test]
336 fn just_above_boundary() { check_encoding(&[42; 513]); }
337
338 #[test]
339 fn just_above_double_boundary() { check_encoding(&[42; 1025]); }
340
341 #[test]
342 fn fmt_exact_macro() {
343 use crate::alloc::string::ToString;
344
345 struct Dummy([u8; 32]);
346
347 impl fmt::Display for Dummy {
348 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
349 fmt_hex_exact!(f, 32, &self.0, Case::Lower)
350 }
351 }
352
353 assert_eq!(Dummy([42; 32]).to_string(), "2a".repeat(32));
354 }
355
356 #[test]
357 fn display_short_with_padding() {
358 let v = vec![0xbe, 0xef];
359 assert_eq!(format!("Hello {:<8}!", v.as_hex()), "Hello beef !");
360 assert_eq!(format!("Hello {:-<8}!", v.as_hex()), "Hello beef----!");
361 assert_eq!(format!("Hello {:^8}!", v.as_hex()), "Hello beef !");
362 assert_eq!(format!("Hello {:>8}!", v.as_hex()), "Hello beef!");
363 }
364
365 #[test]
367 fn display_long_no_padding() {
368 let x = 1;
370 let want = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001";
372 let got = format!("{:0>2000}", x);
373 assert_eq!(got, want);
374
375 let v = vec![0xab; 512];
377 let want = "abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab";
378 let got = format!("{:0>2000}", v.as_hex());
379 assert_eq!(got, want)
380 }
381 }
382}