1#[cfg(all(feature = "alloc", not(feature = "std")))]
29use alloc::string::String;
30use core::borrow::Borrow;
31use core::fmt;
32
33use super::{Case, Table};
34use crate::buf_encoder::BufEncoder;
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
109fn internal_display(bytes: &[u8], f: &mut fmt::Formatter, case: Case) -> fmt::Result {
110 use fmt::Write;
111 let mut encoder = BufEncoder::<1024>::new(case);
119
120 let pad_right = if let Some(width) = f.width() {
121 let string_len = match f.precision() {
122 Some(max) => core::cmp::min(max, bytes.len() * 2),
123 None => bytes.len() * 2,
124 };
125
126 if string_len < width {
127 let (left, right) = match f.align().unwrap_or(fmt::Alignment::Left) {
128 fmt::Alignment::Left => (0, width - string_len),
129 fmt::Alignment::Right => (width - string_len, 0),
130 fmt::Alignment::Center => ((width - string_len) / 2, (width - string_len + 1) / 2),
131 };
132 if left > 0 {
134 let c = f.fill();
135 let chunk_len = encoder.put_filler(c, left);
136 let padding = encoder.as_str();
137 for _ in 0..(left / chunk_len) {
138 f.write_str(padding)?;
139 }
140 f.write_str(&padding[..((left % chunk_len) * c.len_utf8())])?;
141 encoder.clear();
142 }
143 right
144 } else {
145 0
146 }
147 } else {
148 0
149 };
150
151 match f.precision() {
152 Some(max) if bytes.len() > max / 2 => {
153 write!(f, "{}", bytes[..(max / 2)].as_hex())?;
154 if max % 2 == 1 {
155 f.write_char(case.table().byte_to_chars(bytes[max / 2])[0])?;
156 }
157 }
158 Some(_) | None => {
159 let mut chunks = bytes.chunks_exact(512);
160 for chunk in &mut chunks {
161 encoder.put_bytes(chunk);
162 f.write_str(encoder.as_str())?;
163 encoder.clear();
164 }
165 encoder.put_bytes(chunks.remainder());
166 f.write_str(encoder.as_str())?;
167 }
168 }
169
170 if pad_right > 0 {
172 encoder.clear();
173 let c = f.fill();
174 let chunk_len = encoder.put_filler(c, pad_right);
175 let padding = encoder.as_str();
176 for _ in 0..(pad_right / chunk_len) {
177 f.write_str(padding)?;
178 }
179 f.write_str(&padding[..((pad_right % chunk_len) * c.len_utf8())])?;
180 }
181 Ok(())
182}
183
184mod sealed {
185 pub trait IsRef: Copy {}
187
188 impl<T: ?Sized> IsRef for &'_ T {}
189}
190
191impl<'a> DisplayHex for &'a [u8] {
192 type Display = DisplayByteSlice<'a>;
193
194 #[inline]
195 fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } }
196
197 #[inline]
198 fn hex_reserve_suggestion(self) -> usize {
199 self.len().checked_mul(2).expect("the string wouldn't fit into address space")
203 }
204}
205
206#[cfg(feature = "alloc")]
207impl<'a> DisplayHex for &'a alloc::vec::Vec<u8> {
208 type Display = DisplayByteSlice<'a>;
209
210 #[inline]
211 fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } }
212
213 #[inline]
214 fn hex_reserve_suggestion(self) -> usize {
215 self.len().checked_mul(2).expect("the string wouldn't fit into address space")
219 }
220}
221
222pub struct DisplayByteSlice<'a> {
226 pub(crate) bytes: &'a [u8],
228}
229
230impl<'a> DisplayByteSlice<'a> {
231 fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
232 internal_display(self.bytes, f, case)
233 }
234}
235
236impl<'a> fmt::Display for DisplayByteSlice<'a> {
237 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
238}
239
240impl<'a> fmt::Debug for DisplayByteSlice<'a> {
241 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
242}
243
244impl<'a> fmt::LowerHex for DisplayByteSlice<'a> {
245 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
246}
247
248impl<'a> fmt::UpperHex for DisplayByteSlice<'a> {
249 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
250}
251
252pub struct DisplayArray<'a, const CAP: usize> {
256 array: &'a [u8],
257}
258
259impl<'a, const CAP: usize> DisplayArray<'a, CAP> {
260 #[inline]
266 fn new(array: &'a [u8]) -> Self {
267 assert!(array.len() <= CAP / 2);
268 DisplayArray { array }
269 }
270
271 fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
272 internal_display(self.array, f, case)
273 }
274}
275
276impl<'a, const LEN: usize> fmt::Display for DisplayArray<'a, LEN> {
277 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
278}
279
280impl<'a, const LEN: usize> fmt::Debug for DisplayArray<'a, LEN> {
281 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
282}
283
284impl<'a, const LEN: usize> fmt::LowerHex for DisplayArray<'a, LEN> {
285 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
286}
287
288impl<'a, const LEN: usize> fmt::UpperHex for DisplayArray<'a, LEN> {
289 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
290}
291
292macro_rules! impl_array_as_hex {
293 ($($len:expr),*) => {
294 $(
295 impl<'a> DisplayHex for &'a [u8; $len] {
296 type Display = DisplayArray<'a, {$len * 2}>;
297
298 fn as_hex(self) -> Self::Display {
299 DisplayArray::new(self)
300 }
301 }
302 )*
303 }
304}
305
306impl_array_as_hex!(
307 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 32, 33, 64, 65, 128, 256, 512, 1024,
308 2048, 4096
309);
310
311#[macro_export]
334macro_rules! fmt_hex_exact {
335 ($formatter:expr, $len:expr, $bytes:expr, $case:expr) => {{
336 #[allow(deprecated)]
338 const _: () = [()][($len > usize::MAX / 2) as usize];
339 assert_eq!($bytes.len(), $len);
340 $crate::display::fmt_hex_exact_fn::<_, { $len * 2 }>($formatter, $bytes, $case)
341 }};
342}
343pub use fmt_hex_exact;
344
345#[macro_export]
430macro_rules! impl_fmt_traits {
431 (impl fmt_traits for $ty:ident { const LENGTH: usize = $len:expr; }) => {
433 $crate::impl_fmt_traits! {
434 #[display_backward(false)]
435 impl<> fmt_traits for $ty<> {
436 const LENGTH: usize = $len;
437 }
438 }
439 };
440 (#[display_backward($reverse:expr)] impl fmt_traits for $ty:ident { const LENGTH: usize = $len:expr; }) => {
442 $crate::impl_fmt_traits! {
443 #[display_backward($reverse)]
444 impl<> fmt_traits for $ty<> {
445 const LENGTH: usize = $len;
446 }
447 }
448 };
449 (impl<$($gen:ident: $gent:ident),*> fmt_traits for $ty:ident<$($unused:ident),*> { const LENGTH: usize = $len:expr; }) => {
451 $crate::impl_fmt_traits! {
452 #[display_backward(false)]
453 impl<$($gen: $gent),*> fmt_traits for $ty<$($unused),*> {
454 const LENGTH: usize = $len;
455 }
456 }
457 };
458 (#[display_backward($reverse:expr)] impl<$($gen:ident: $gent:ident),*> fmt_traits for $ty:ident<$($unused:ident),*> { const LENGTH: usize = $len:expr; }) => {
460 impl<$($gen: $gent),*> $crate::_export::_core::fmt::LowerHex for $ty<$($gen),*> {
461 #[inline]
462 fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
463 let case = $crate::Case::Lower;
464
465 if $reverse {
466 let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter().rev();
467 $crate::fmt_hex_exact!(f, $len, bytes, case)
468 } else {
469 let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter();
470 $crate::fmt_hex_exact!(f, $len, bytes, case)
471 }
472 }
473 }
474
475 impl<$($gen: $gent),*> $crate::_export::_core::fmt::UpperHex for $ty<$($gen),*> {
476 #[inline]
477 fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
478 let case = $crate::Case::Upper;
479
480 if $reverse {
481 let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter().rev();
482 $crate::fmt_hex_exact!(f, $len, bytes, case)
483 } else {
484 let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter();
485 $crate::fmt_hex_exact!(f, $len, bytes, case)
486 }
487 }
488 }
489
490 impl<$($gen: $gent),*> $crate::_export::_core::fmt::Display for $ty<$($gen),*> {
491 #[inline]
492 fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
493 $crate::_export::_core::fmt::LowerHex::fmt(self, f)
494 }
495 }
496
497 impl<$($gen: $gent),*> $crate::_export::_core::fmt::Debug for $ty<$($gen),*> {
498 #[inline]
499 fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
500 $crate::_export::_core::fmt::LowerHex::fmt(&self, f)
501 }
502 }
503 };
504}
505pub use impl_fmt_traits;
506
507#[doc(hidden)]
515#[inline]
516pub fn fmt_hex_exact_fn<I, const N: usize>(
517 f: &mut fmt::Formatter,
518 bytes: I,
519 case: Case,
520) -> fmt::Result
521where
522 I: IntoIterator,
523 I::Item: Borrow<u8>,
524{
525 let mut encoder = BufEncoder::<N>::new(case);
526 let encoded = match f.precision() {
527 Some(p) if p < N => {
528 let n = (p + 1) / 2;
529 encoder.put_bytes(bytes.into_iter().take(n));
530 &encoder.as_str()[..p]
531 }
532 _ => {
533 encoder.put_bytes(bytes);
534 encoder.as_str()
535 }
536 };
537 f.pad_integral(true, "0x", encoded)
538}
539
540#[cfg(any(test, feature = "std"))]
543#[cfg_attr(docsrs, doc(cfg(any(test, feature = "std"))))]
544pub struct HexWriter<T> {
545 writer: T,
546 table: &'static Table,
547}
548
549#[cfg(any(test, feature = "std"))]
550#[cfg_attr(docsrs, doc(cfg(any(test, feature = "std"))))]
551impl<T> HexWriter<T> {
552 pub fn new(dest: T, case: Case) -> Self { Self { writer: dest, table: case.table() } }
555 pub fn into_inner(self) -> T { self.writer }
557}
558
559#[cfg(any(test, feature = "std"))]
560#[cfg_attr(docsrs, doc(cfg(any(test, feature = "std"))))]
561impl<T> std::io::Write for HexWriter<T>
562where
563 T: core::fmt::Write,
564{
565 fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
570 let mut n = 0;
571 for byte in buf {
572 let mut hex_chars = [0u8; 2];
573 let hex_str = self.table.byte_to_str(&mut hex_chars, *byte);
574 if self.writer.write_str(hex_str).is_err() {
575 break;
576 }
577 n += 1;
578 }
579 if n == 0 && !buf.is_empty() {
580 Err(std::io::ErrorKind::Other.into())
581 } else {
582 Ok(n)
583 }
584 }
585 fn flush(&mut self) -> Result<(), std::io::Error> { Ok(()) }
586}
587
588#[cfg(test)]
589mod tests {
590 #[cfg(feature = "alloc")]
591 use super::*;
592
593 #[cfg(feature = "alloc")]
594 mod alloc {
595 use core::marker::PhantomData;
596
597 use super::*;
598
599 fn check_encoding(bytes: &[u8]) {
600 use core::fmt::Write;
601
602 let s1 = bytes.to_lower_hex_string();
603 let mut s2 = String::with_capacity(bytes.len() * 2);
604 for b in bytes {
605 write!(s2, "{:02x}", b).unwrap();
606 }
607 assert_eq!(s1, s2);
608 }
609
610 #[test]
611 fn empty() { check_encoding(b""); }
612
613 #[test]
614 fn single() { check_encoding(b"*"); }
615
616 #[test]
617 fn two() { check_encoding(b"*x"); }
618
619 #[test]
620 fn just_below_boundary() { check_encoding(&[42; 512]); }
621
622 #[test]
623 fn just_above_boundary() { check_encoding(&[42; 513]); }
624
625 #[test]
626 fn just_above_double_boundary() { check_encoding(&[42; 1025]); }
627
628 #[test]
629 fn fmt_exact_macro() {
630 use crate::alloc::string::ToString;
631
632 struct Dummy([u8; 32]);
633
634 impl fmt::Display for Dummy {
635 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
636 fmt_hex_exact!(f, 32, &self.0, Case::Lower)
637 }
638 }
639 let dummy = Dummy([42; 32]);
640 assert_eq!(dummy.to_string(), "2a".repeat(32));
641 assert_eq!(format!("{:.10}", dummy), "2a".repeat(5));
642 assert_eq!(format!("{:.11}", dummy), "2a".repeat(5) + "2");
643 assert_eq!(format!("{:.65}", dummy), "2a".repeat(32));
644 }
645
646 macro_rules! test_display_hex {
647 ($fs: expr, $a: expr, $check: expr) => {
648 let to_display_array = $a;
649 let to_display_byte_slice = Vec::from($a);
650 assert_eq!(format!($fs, to_display_array.as_hex()), $check);
651 assert_eq!(format!($fs, to_display_byte_slice.as_hex()), $check);
652 };
653 }
654
655 #[test]
656 fn display_short_with_padding() {
657 test_display_hex!("Hello {:<8}!", [0xbe, 0xef], "Hello beef !");
658 test_display_hex!("Hello {:-<8}!", [0xbe, 0xef], "Hello beef----!");
659 test_display_hex!("Hello {:^8}!", [0xbe, 0xef], "Hello beef !");
660 test_display_hex!("Hello {:>8}!", [0xbe, 0xef], "Hello beef!");
661 }
662
663 #[test]
664 fn display_long() {
665 let a = [0xab; 512];
667 let mut want = "0".repeat(2000 - 1024);
668 want.extend(core::iter::repeat("ab").take(512));
669 test_display_hex!("{:0>2000}", a, want);
670 }
671
672 #[test]
675 fn precision_truncates() {
676 test_display_hex!("{0:.4}", [0x12, 0x34, 0x56, 0x78], "1234");
679 test_display_hex!("{0:.5}", [0x12, 0x34, 0x56, 0x78], "12345");
680 }
681
682 #[test]
683 fn precision_with_padding_truncates() {
684 test_display_hex!("{0:10.4}", [0x12, 0x34, 0x56, 0x78], "1234 ");
686 test_display_hex!("{0:10.5}", [0x12, 0x34, 0x56, 0x78], "12345 ");
687 }
688
689 #[test]
690 fn precision_with_padding_pads_right() {
691 test_display_hex!("{0:10.20}", [0x12, 0x34, 0x56, 0x78], "12345678 ");
692 test_display_hex!("{0:10.14}", [0x12, 0x34, 0x56, 0x78], "12345678 ");
693 }
694
695 #[test]
696 fn precision_with_padding_pads_left() {
697 test_display_hex!("{0:>10.20}", [0x12, 0x34, 0x56, 0x78], " 12345678");
698 }
699
700 #[test]
701 fn precision_with_padding_pads_center() {
702 test_display_hex!("{0:^10.20}", [0x12, 0x34, 0x56, 0x78], " 12345678 ");
703 }
704
705 #[test]
706 fn precision_with_padding_pads_center_odd() {
707 test_display_hex!("{0:^11.20}", [0x12, 0x34, 0x56, 0x78], " 12345678 ");
708 }
709
710 #[test]
711 fn precision_does_not_extend() {
712 test_display_hex!("{0:.16}", [0x12, 0x34, 0x56, 0x78], "12345678");
713 }
714
715 #[test]
716 fn padding_extends() {
717 test_display_hex!("{:0>8}", [0xab; 2], "0000abab");
718 }
719
720 #[test]
721 fn padding_does_not_truncate() {
722 test_display_hex!("{:0>4}", [0x12, 0x34, 0x56, 0x78], "12345678");
723 }
724
725 #[test]
726 fn hex_fmt_impl_macro_forward() {
727 struct Wrapper([u8; 4]);
728
729 impl Borrow<[u8]> for Wrapper {
730 fn borrow(&self) -> &[u8] { &self.0[..] }
731 }
732
733 impl_fmt_traits! {
734 #[display_backward(false)]
735 impl fmt_traits for Wrapper {
736 const LENGTH: usize = 4;
737 }
738 }
739
740 let tc = Wrapper([0x12, 0x34, 0x56, 0x78]);
741
742 let want = "12345678";
743 let got = format!("{}", tc);
744 assert_eq!(got, want);
745 }
746
747 #[test]
748 fn hex_fmt_impl_macro_backwards() {
749 struct Wrapper([u8; 4]);
750
751 impl Borrow<[u8]> for Wrapper {
752 fn borrow(&self) -> &[u8] { &self.0[..] }
753 }
754
755 impl_fmt_traits! {
756 #[display_backward(true)]
757 impl fmt_traits for Wrapper {
758 const LENGTH: usize = 4;
759 }
760 }
761
762 let tc = Wrapper([0x12, 0x34, 0x56, 0x78]);
763
764 let want = "78563412";
765 let got = format!("{}", tc);
766 assert_eq!(got, want);
767 }
768
769 #[test]
770 fn hex_fmt_impl_macro_gen_forward() {
771 struct Wrapper<T>([u8; 4], PhantomData<T>);
772
773 impl<T: Clone> Borrow<[u8]> for Wrapper<T> {
774 fn borrow(&self) -> &[u8] { &self.0[..] }
775 }
776
777 impl_fmt_traits! {
778 #[display_backward(false)]
779 impl<T: Clone> fmt_traits for Wrapper<T> {
780 const LENGTH: usize = 4;
781 }
782 }
783
784 let tc = Wrapper([0x12, 0x34, 0x56, 0x78], PhantomData::<u32>);
786
787 let want = "12345678";
788 let got = format!("{}", tc);
789 assert_eq!(got, want);
790 }
791
792 #[test]
793 fn hex_fmt_impl_macro_gen_backwards() {
794 struct Wrapper<T>([u8; 4], PhantomData<T>);
795
796 impl<T: Clone> Borrow<[u8]> for Wrapper<T> {
797 fn borrow(&self) -> &[u8] { &self.0[..] }
798 }
799
800 impl_fmt_traits! {
801 #[display_backward(true)]
802 impl<T: Clone> fmt_traits for Wrapper<T> {
803 const LENGTH: usize = 4;
804 }
805 }
806
807 let tc = Wrapper([0x12, 0x34, 0x56, 0x78], PhantomData::<u32>);
809
810 let want = "78563412";
811 let got = format!("{}", tc);
812 assert_eq!(got, want);
813 }
814 }
815
816 #[cfg(feature = "std")]
817 mod std {
818
819 #[test]
820 fn hex_writer() {
821 use std::io::{ErrorKind, Result, Write};
822
823 use arrayvec::ArrayString;
824
825 use super::Case::{Lower, Upper};
826 use super::{DisplayHex, HexWriter};
827
828 macro_rules! test_hex_writer {
829 ($cap:expr, $case: expr, $src: expr, $want: expr, $hex_result: expr) => {
830 let dest_buf = ArrayString::<$cap>::new();
831 let mut dest = HexWriter::new(dest_buf, $case);
832 let got = dest.write($src);
833 match $want {
834 Ok(n) => assert_eq!(got.unwrap(), n),
835 Err(e) => assert_eq!(got.unwrap_err().kind(), e.kind()),
836 }
837 assert_eq!(dest.into_inner().as_str(), $hex_result);
838 };
839 }
840
841 test_hex_writer!(0, Lower, &[], Result::Ok(0), "");
842 test_hex_writer!(0, Lower, &[0xab, 0xcd], Result::Err(ErrorKind::Other.into()), "");
843 test_hex_writer!(1, Lower, &[0xab, 0xcd], Result::Err(ErrorKind::Other.into()), "");
844 test_hex_writer!(2, Lower, &[0xab, 0xcd], Result::Ok(1), "ab");
845 test_hex_writer!(3, Lower, &[0xab, 0xcd], Result::Ok(1), "ab");
846 test_hex_writer!(4, Lower, &[0xab, 0xcd], Result::Ok(2), "abcd");
847 test_hex_writer!(8, Lower, &[0xab, 0xcd], Result::Ok(2), "abcd");
848 test_hex_writer!(8, Upper, &[0xab, 0xcd], Result::Ok(2), "ABCD");
849
850 let vec: Vec<_> = (0u8..32).collect();
851 let mut writer = HexWriter::new(String::new(), Lower);
852 writer.write_all(&vec[..]).unwrap();
853 assert_eq!(writer.into_inner(), vec.to_lower_hex_string());
854 }
855 }
856}