1#[cfg(feature = "alloc")]
29use alloc::string::String;
30use core::borrow::Borrow;
31use core::fmt;
32
33use super::Case;
34#[cfg(feature = "std")]
35use super::Table;
36use crate::buf_encoder::BufEncoder;
37
38pub trait DisplayHex {
43 type Display<'a>: fmt::Display + fmt::Debug + fmt::LowerHex + fmt::UpperHex
47 where
48 Self: 'a;
49
50 fn as_hex<'a>(&'a self) -> Self::Display<'a>;
52
53 #[cfg(feature = "alloc")]
59 #[inline]
60 fn to_lower_hex_string(&self) -> String { self.to_hex_string(Case::Lower) }
61
62 #[cfg(feature = "alloc")]
68 #[inline]
69 fn to_upper_hex_string(&self) -> String { self.to_hex_string(Case::Upper) }
70
71 #[cfg(feature = "alloc")]
75 fn to_hex_string(&self, case: Case) -> String {
76 let mut string = String::new();
77 self.append_hex_to_string(case, &mut string);
78 string
79 }
80
81 #[cfg(feature = "alloc")]
86 fn append_hex_to_string<'a>(&'a self, case: Case, string: &mut String) {
87 use fmt::Write;
88
89 string.reserve(self.hex_reserve_suggestion());
90 match case {
91 Case::Lower => write!(string, "{:x}", self.as_hex()),
92 Case::Upper => write!(string, "{:X}", self.as_hex()),
93 }
94 .unwrap_or_else(|_| {
95 let name = core::any::type_name::<Self::Display<'a>>();
96 panic!("The implementation of Display for {} returned an error when it shouldn't", name)
99 });
100 }
101
102 fn hex_reserve_suggestion(&self) -> usize;
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 let pad_right = write_pad_left(f, bytes.len(), &mut encoder)?;
120
121 if f.alternate() {
122 f.write_str("0x")?;
123 }
124 match f.precision() {
125 Some(max) if bytes.len() > max / 2 => {
126 match case {
127 Case::Lower => write!(f, "{:x}", bytes[..(max / 2)].as_hex())?,
128 Case::Upper => write!(f, "{:X}", bytes[..(max / 2)].as_hex())?,
129 }
130 if max % 2 == 1 {
131 f.write_char(case.table().byte_to_chars(bytes[max / 2])[0])?;
132 }
133 }
134 Some(_) | None => {
135 let mut chunks = bytes.chunks_exact(512);
136 for chunk in &mut chunks {
137 encoder.put_bytes(chunk);
138 f.write_str(encoder.as_str())?;
139 encoder.clear();
140 }
141 encoder.put_bytes(chunks.remainder());
142 f.write_str(encoder.as_str())?;
143 }
144 }
145
146 write_pad_right(f, pad_right, &mut encoder)
147}
148
149fn write_pad_left(
150 f: &mut fmt::Formatter,
151 bytes_len: usize,
152 encoder: &mut BufEncoder<1024>,
153) -> Result<usize, fmt::Error> {
154 let pad_right = if let Some(width) = f.width() {
155 let full_string_len = if f.alternate() { bytes_len * 2 + 2 } else { bytes_len * 2 };
157 let string_len = match f.precision() {
158 Some(max) => core::cmp::min(max, full_string_len),
159 None => full_string_len,
160 };
161
162 if string_len < width {
163 let (left, right) = match f.align().unwrap_or(fmt::Alignment::Left) {
164 fmt::Alignment::Left => (0, width - string_len),
165 fmt::Alignment::Right => (width - string_len, 0),
166 fmt::Alignment::Center =>
167 ((width - string_len) / 2, (width - string_len).div_ceil(2)),
168 };
169 if left > 0 {
171 let c = f.fill();
172 let chunk_len = encoder.put_filler(c, left);
173 let padding = encoder.as_str();
174 for _ in 0..(left / chunk_len) {
175 f.write_str(padding)?;
176 }
177 f.write_str(&padding[..((left % chunk_len) * c.len_utf8())])?;
178 encoder.clear();
179 }
180 right
181 } else {
182 0
183 }
184 } else {
185 0
186 };
187 Ok(pad_right)
188}
189
190fn write_pad_right(
191 f: &mut fmt::Formatter,
192 pad_right: usize,
193 encoder: &mut BufEncoder<1024>,
194) -> fmt::Result {
195 if pad_right > 0 {
197 encoder.clear();
198 let c = f.fill();
199 let chunk_len = encoder.put_filler(c, pad_right);
200 let padding = encoder.as_str();
201 for _ in 0..(pad_right / chunk_len) {
202 f.write_str(padding)?;
203 }
204 f.write_str(&padding[..((pad_right % chunk_len) * c.len_utf8())])?;
205 }
206 Ok(())
207}
208
209impl DisplayHex for [u8] {
210 type Display<'a> = DisplayByteSlice<'a>;
211
212 #[inline]
213 fn as_hex<'a>(&'a self) -> Self::Display<'a> { DisplayByteSlice { bytes: self } }
214
215 #[inline]
216 fn hex_reserve_suggestion(&self) -> usize {
217 self.len().checked_mul(2).expect("the string wouldn't fit into address space")
221 }
222}
223
224#[derive(Clone, PartialEq, Eq, Hash)]
228pub struct DisplayByteSlice<'a> {
229 pub(crate) bytes: &'a [u8],
231}
232
233impl DisplayByteSlice<'_> {
234 #[inline]
235 fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
236 internal_display(self.bytes, f, case)
237 }
238}
239
240impl fmt::Display for DisplayByteSlice<'_> {
241 #[inline]
242 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
243}
244
245impl fmt::Debug for DisplayByteSlice<'_> {
246 #[inline]
247 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
248}
249
250impl fmt::LowerHex for DisplayByteSlice<'_> {
251 #[inline]
252 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
253}
254
255impl fmt::UpperHex for DisplayByteSlice<'_> {
256 #[inline]
257 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
258}
259
260#[macro_export]
319macro_rules! fmt_hex_max {
320 ($formatter:expr, $len:expr, $bytes:expr, $case:expr) => {{
321 #[allow(deprecated)]
323 const _: () = [()][($len > usize::MAX / 2) as usize];
324 assert!(
325 $bytes.len() <= $len,
326 "length of the encoded item ({}) is larger than {}",
327 $bytes.len(),
328 $len
329 );
330 $crate::display::fmt_hex_max_fn::<_, { $len * 2 }>($formatter, $bytes, $case)
331 }};
332}
333pub use fmt_hex_max;
334
335#[macro_export]
343macro_rules! fmt_hex_exact {
344 ($formatter:expr, $len:expr, $bytes:expr, $case:expr) => {{
345 assert_eq!($bytes.len(), $len);
346 $crate::fmt_hex_max!($formatter, $len, $bytes, $case)
347 }};
348}
349pub use fmt_hex_exact;
350
351#[macro_export]
355macro_rules! fmt_hex_lower {
356 ($formatter:expr, $len:expr, $bytes:expr) => {
357 $crate::fmt_hex_max!($formatter, $len, $bytes, $crate::Case::Lower)
358 };
359}
360pub use fmt_hex_lower;
361
362#[macro_export]
366macro_rules! fmt_hex_upper {
367 ($formatter:expr, $len:expr, $bytes:expr) => {
368 $crate::fmt_hex_max!($formatter, $len, $bytes, $crate::Case::Upper)
369 };
370}
371pub use fmt_hex_upper;
372
373#[macro_export]
458macro_rules! impl_fmt_traits {
459 (impl fmt_traits for $ty:ident { const LENGTH: usize = $len:expr; }) => {
461 $crate::impl_fmt_traits! {
462 #[display_backward(false)]
463 impl<> fmt_traits for $ty<> {
464 const LENGTH: usize = $len;
465 }
466 }
467 };
468 (#[display_backward($reverse:expr)] impl fmt_traits for $ty:ident { const LENGTH: usize = $len:expr; }) => {
470 $crate::impl_fmt_traits! {
471 #[display_backward($reverse)]
472 impl<> fmt_traits for $ty<> {
473 const LENGTH: usize = $len;
474 }
475 }
476 };
477 (impl<$($gen:ident: $gent:ident),*> fmt_traits for $ty:ident<$($unused:ident),*> { const LENGTH: usize = $len:expr; }) => {
479 $crate::impl_fmt_traits! {
480 #[display_backward(false)]
481 impl<$($gen: $gent),*> fmt_traits for $ty<$($unused),*> {
482 const LENGTH: usize = $len;
483 }
484 }
485 };
486 (#[display_backward($reverse:expr)] impl<$($gen:ident: $gent:ident),*> fmt_traits for $ty:ident<$($unused:ident),*> { const LENGTH: usize = $len:expr; }) => {
488 impl<$($gen: $gent),*> $crate::_export::_core::fmt::LowerHex for $ty<$($gen),*> {
489 #[inline]
490 fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
491 let case = $crate::Case::Lower;
492
493 if $reverse {
494 let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter().rev();
495 $crate::fmt_hex_exact!(f, $len, bytes, case)
496 } else {
497 let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter();
498 $crate::fmt_hex_exact!(f, $len, bytes, case)
499 }
500 }
501 }
502
503 impl<$($gen: $gent),*> $crate::_export::_core::fmt::UpperHex for $ty<$($gen),*> {
504 #[inline]
505 fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
506 let case = $crate::Case::Upper;
507
508 if $reverse {
509 let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter().rev();
510 $crate::fmt_hex_exact!(f, $len, bytes, case)
511 } else {
512 let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter();
513 $crate::fmt_hex_exact!(f, $len, bytes, case)
514 }
515 }
516 }
517
518 impl<$($gen: $gent),*> $crate::_export::_core::fmt::Display for $ty<$($gen),*> {
519 #[inline]
520 fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
521 $crate::_export::_core::fmt::LowerHex::fmt(self, f)
522 }
523 }
524
525 impl<$($gen: $gent),*> $crate::_export::_core::fmt::Debug for $ty<$($gen),*> {
526 #[inline]
527 fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
528 $crate::_export::_core::fmt::LowerHex::fmt(&self, f)
529 }
530 }
531 };
532}
533pub use impl_fmt_traits;
534
535#[doc(hidden)]
543#[inline]
544pub fn fmt_hex_max_fn<I, const N: usize>(
545 f: &mut fmt::Formatter,
546 bytes: I,
547 case: Case,
548) -> fmt::Result
549where
550 I: IntoIterator,
551 I::Item: Borrow<u8>,
552{
553 let mut padding_encoder = BufEncoder::<1024>::new(case);
554 let pad_right = write_pad_left(f, N / 2, &mut padding_encoder)?;
555
556 if f.alternate() {
557 f.write_str("0x")?;
558 }
559 let mut encoder = BufEncoder::<N>::new(case);
560 let encoded = match f.precision() {
561 Some(p) if p < N => {
562 let n = p.div_ceil(2);
563 encoder.put_bytes(bytes.into_iter().take(n));
564 &encoder.as_str()[..p]
565 }
566 _ => {
567 encoder.put_bytes(bytes);
568 encoder.as_str()
569 }
570 };
571 f.write_str(encoded)?;
572
573 write_pad_right(f, pad_right, &mut padding_encoder)
574}
575
576#[cfg(feature = "std")]
579#[derive(Debug, Clone, PartialEq, Eq, Hash)]
580pub struct HexWriter<T> {
581 writer: T,
582 table: &'static Table,
583}
584
585#[cfg(feature = "std")]
586impl<T> HexWriter<T> {
587 pub fn new(dest: T, case: Case) -> Self { Self { writer: dest, table: case.table() } }
592 pub fn into_inner(self) -> T { self.writer }
594}
595
596#[cfg(feature = "std")]
597impl<T> std::io::Write for HexWriter<T>
598where
599 T: core::fmt::Write,
600{
601 fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
608 let mut n = 0;
609 for byte in buf {
610 let mut hex_chars = [0u8; 2];
611 let hex_str = self.table.byte_to_str(&mut hex_chars, *byte);
612 if self.writer.write_str(hex_str).is_err() {
613 break;
614 }
615 n += 1;
616 }
617 if n == 0 && !buf.is_empty() {
618 Err(std::io::ErrorKind::Other.into())
619 } else {
620 Ok(n)
621 }
622 }
623
624 fn flush(&mut self) -> Result<(), std::io::Error> { Ok(()) }
630}
631
632#[cfg(test)]
633mod tests {
634 #[cfg(feature = "alloc")]
635 use super::*;
636
637 #[cfg(feature = "alloc")]
638 mod alloc {
639 use core::marker::PhantomData;
640
641 use super::*;
642 use crate::alloc::vec::Vec;
643
644 fn check_encoding(bytes: &[u8]) {
645 use core::fmt::Write;
646
647 let s1 = bytes.to_lower_hex_string();
648 let mut s2 = String::with_capacity(bytes.len() * 2);
649 for b in bytes {
650 write!(s2, "{:02x}", b).unwrap();
651 }
652 assert_eq!(s1, s2);
653 }
654
655 #[test]
656 fn empty() { check_encoding(b""); }
657
658 #[test]
659 fn single() { check_encoding(b"*"); }
660
661 #[test]
662 fn two() { check_encoding(b"*x"); }
663
664 #[test]
665 fn just_below_boundary() { check_encoding(&[42; 512]); }
666
667 #[test]
668 fn just_above_boundary() { check_encoding(&[42; 513]); }
669
670 #[test]
671 fn just_above_double_boundary() { check_encoding(&[42; 1025]); }
672
673 #[test]
674 fn fmt_exact_macro() {
675 use crate::alloc::string::ToString;
676
677 struct Dummy([u8; 32]);
678
679 impl fmt::Display for Dummy {
680 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
681 fmt_hex_exact!(f, 32, &self.0, Case::Lower)
682 }
683 }
684 let dummy = Dummy([42; 32]);
685 assert_eq!(dummy.to_string(), "2a".repeat(32));
686 assert_eq!(format!("{:.10}", dummy), "2a".repeat(5));
687 assert_eq!(format!("{:.11}", dummy), "2a".repeat(5) + "2");
688 assert_eq!(format!("{:.65}", dummy), "2a".repeat(32));
689 }
690
691 struct TestHexUpperLower<'a>(&'a [u8], bool);
692
693 impl fmt::Display for TestHexUpperLower<'_> {
694 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
695 if self.1 {
696 fmt_hex_upper!(f, 3, self.0)
697 } else {
698 fmt_hex_lower!(f, 3, self.0)
699 }
700 }
701 }
702
703 #[test]
704 fn fmt_hex_lower_macro() {
705 let bytes = [0x1a, 0x2b, 0x3c];
706 assert_eq!(format!("{}", TestHexUpperLower(&bytes, false)), "1a2b3c");
707 }
708
709 #[test]
710 fn fmt_hex_upper_macro() {
711 let bytes = [0x1a, 0x2b, 0x3c];
712 assert_eq!(format!("{}", TestHexUpperLower(&bytes, true)), "1A2B3C");
713 }
714
715 macro_rules! define_dummy {
716 ($len:literal) => {
717 struct Dummy([u8; $len]);
718 impl fmt::Debug for Dummy {
719 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
720 fmt_hex_exact!(f, $len, &self.0, Case::Lower)
721 }
722 }
723 impl fmt::Display for Dummy {
724 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
725 fmt_hex_exact!(f, $len, &self.0, Case::Lower)
726 }
727 }
728 };
729 }
730
731 macro_rules! test_display_hex {
732 ($fs: expr, $a: expr, $check: expr) => {
733 let array = $a;
734 let slice = &$a;
735 let vec = Vec::from($a);
736 let dummy = Dummy($a);
737 assert_eq!(format!($fs, array.as_hex()), $check);
738 assert_eq!(format!($fs, slice.as_hex()), $check);
739 assert_eq!(format!($fs, vec.as_hex()), $check);
740 assert_eq!(format!($fs, dummy), $check);
741 };
742 }
743
744 #[test]
745 fn alternate_flag() {
746 define_dummy!(4);
747
748 test_display_hex!("{:#?}", [0xc0, 0xde, 0xca, 0xfe], "0xc0decafe");
749 test_display_hex!("{:#}", [0xc0, 0xde, 0xca, 0xfe], "0xc0decafe");
750 }
751
752 #[test]
753 fn display_short_with_padding() {
754 define_dummy!(2);
755
756 test_display_hex!("Hello {:<8}!", [0xbe, 0xef], "Hello beef !");
757 test_display_hex!("Hello {:-<8}!", [0xbe, 0xef], "Hello beef----!");
758 test_display_hex!("Hello {:^8}!", [0xbe, 0xef], "Hello beef !");
759 test_display_hex!("Hello {:>8}!", [0xbe, 0xef], "Hello beef!");
760
761 test_display_hex!("Hello {:<#8}!", [0xbe, 0xef], "Hello 0xbeef !");
762 test_display_hex!("Hello {:-<#8}!", [0xbe, 0xef], "Hello 0xbeef--!");
763 test_display_hex!("Hello {:^#8}!", [0xbe, 0xef], "Hello 0xbeef !");
764 test_display_hex!("Hello {:>#8}!", [0xbe, 0xef], "Hello 0xbeef!");
765 }
766
767 #[test]
768 fn display_long() {
769 define_dummy!(512);
770 let a = [0xab; 512];
772
773 let mut want = "0".repeat(2000 - 1024);
774 want.extend(core::iter::repeat("ab").take(512));
775 test_display_hex!("{:0>2000}", a, want);
776
777 let mut want = "0".repeat(2000 - 1026);
778 want.push_str("0x");
779 want.extend(core::iter::repeat("ab").take(512));
780 test_display_hex!("{:0>#2000}", a, want);
781 }
782
783 #[test]
786 fn precision_truncates() {
787 define_dummy!(4);
790
791 test_display_hex!("{0:.4}", [0x12, 0x34, 0x56, 0x78], "1234");
792 test_display_hex!("{0:.5}", [0x12, 0x34, 0x56, 0x78], "12345");
793
794 test_display_hex!("{0:#.4}", [0x12, 0x34, 0x56, 0x78], "0x1234");
795 test_display_hex!("{0:#.5}", [0x12, 0x34, 0x56, 0x78], "0x12345");
796 }
797
798 #[test]
799 fn precision_with_padding_truncates() {
800 define_dummy!(4);
802
803 test_display_hex!("{0:10.4}", [0x12, 0x34, 0x56, 0x78], "1234 ");
804 test_display_hex!("{0:10.5}", [0x12, 0x34, 0x56, 0x78], "12345 ");
805
806 test_display_hex!("{0:#10.4}", [0x12, 0x34, 0x56, 0x78], "0x1234 ");
807 test_display_hex!("{0:#10.5}", [0x12, 0x34, 0x56, 0x78], "0x12345 ");
808 }
809
810 #[test]
811 fn precision_with_padding_pads_right() {
812 define_dummy!(4);
813
814 test_display_hex!("{0:10.20}", [0x12, 0x34, 0x56, 0x78], "12345678 ");
815 test_display_hex!("{0:10.14}", [0x12, 0x34, 0x56, 0x78], "12345678 ");
816
817 test_display_hex!("{0:#12.20}", [0x12, 0x34, 0x56, 0x78], "0x12345678 ");
818 test_display_hex!("{0:#12.14}", [0x12, 0x34, 0x56, 0x78], "0x12345678 ");
819 }
820
821 #[test]
822 fn precision_with_padding_pads_left() {
823 define_dummy!(4);
824
825 test_display_hex!("{0:>10.20}", [0x12, 0x34, 0x56, 0x78], " 12345678");
826
827 test_display_hex!("{0:>#12.20}", [0x12, 0x34, 0x56, 0x78], " 0x12345678");
828 }
829
830 #[test]
831 fn precision_with_padding_pads_center() {
832 define_dummy!(4);
833
834 test_display_hex!("{0:^10.20}", [0x12, 0x34, 0x56, 0x78], " 12345678 ");
835
836 test_display_hex!("{0:^#12.20}", [0x12, 0x34, 0x56, 0x78], " 0x12345678 ");
837 }
838
839 #[test]
840 fn precision_with_padding_pads_center_odd() {
841 define_dummy!(4);
842
843 test_display_hex!("{0:^11.20}", [0x12, 0x34, 0x56, 0x78], " 12345678 ");
844
845 test_display_hex!("{0:^#13.20}", [0x12, 0x34, 0x56, 0x78], " 0x12345678 ");
846 }
847
848 #[test]
849 fn precision_does_not_extend() {
850 define_dummy!(4);
851
852 test_display_hex!("{0:.16}", [0x12, 0x34, 0x56, 0x78], "12345678");
853
854 test_display_hex!("{0:#.16}", [0x12, 0x34, 0x56, 0x78], "0x12345678");
855 }
856
857 #[test]
858 fn padding_extends() {
859 define_dummy!(2);
860
861 test_display_hex!("{:0>8}", [0xab; 2], "0000abab");
862
863 test_display_hex!("{:0>#8}", [0xab; 2], "000xabab");
864 }
865
866 #[test]
867 fn padding_does_not_truncate() {
868 define_dummy!(4);
869
870 test_display_hex!("{:0>4}", [0x12, 0x34, 0x56, 0x78], "12345678");
871 test_display_hex!("{:0>4}", [0x12, 0x34, 0x56, 0x78], "12345678");
872
873 test_display_hex!("{:0>#4}", [0x12, 0x34, 0x56, 0x78], "0x12345678");
874 test_display_hex!("{:0>#4}", [0x12, 0x34, 0x56, 0x78], "0x12345678");
875 }
876
877 #[allow(dead_code)]
880 struct Wrapper([u8; 4]);
881
882 impl Borrow<[u8]> for Wrapper {
883 fn borrow(&self) -> &[u8] { &self.0[..] }
884 }
885
886 impl_fmt_traits! {
887 #[display_backward(false)]
888 impl fmt_traits for Wrapper {
889 const LENGTH: usize = 4;
890 }
891 }
892
893 #[test]
894 fn hex_fmt_impl_macro_forward() {
895 struct Wrapper([u8; 4]);
896
897 impl Borrow<[u8]> for Wrapper {
898 fn borrow(&self) -> &[u8] { &self.0[..] }
899 }
900
901 impl_fmt_traits! {
902 #[display_backward(false)]
903 impl fmt_traits for Wrapper {
904 const LENGTH: usize = 4;
905 }
906 }
907
908 let tc = Wrapper([0x12, 0x34, 0x56, 0x78]);
909
910 let want = "12345678";
911 let got = format!("{}", tc);
912 assert_eq!(got, want);
913 }
914
915 #[test]
916 fn hex_fmt_impl_macro_backwards() {
917 struct Wrapper([u8; 4]);
918
919 impl Borrow<[u8]> for Wrapper {
920 fn borrow(&self) -> &[u8] { &self.0[..] }
921 }
922
923 impl_fmt_traits! {
924 #[display_backward(true)]
925 impl fmt_traits for Wrapper {
926 const LENGTH: usize = 4;
927 }
928 }
929
930 let tc = Wrapper([0x12, 0x34, 0x56, 0x78]);
931
932 let want = "78563412";
933 let got = format!("{}", tc);
934 assert_eq!(got, want);
935 }
936
937 #[test]
938 fn hex_fmt_impl_macro_gen_forward() {
939 struct Wrapper<T>([u8; 4], PhantomData<T>);
940
941 impl<T: Clone> Borrow<[u8]> for Wrapper<T> {
942 fn borrow(&self) -> &[u8] { &self.0[..] }
943 }
944
945 impl_fmt_traits! {
946 #[display_backward(false)]
947 impl<T: Clone> fmt_traits for Wrapper<T> {
948 const LENGTH: usize = 4;
949 }
950 }
951
952 let tc = Wrapper([0x12, 0x34, 0x56, 0x78], PhantomData::<u32>);
954
955 let want = "12345678";
956 let got = format!("{}", tc);
957 assert_eq!(got, want);
958 }
959
960 #[test]
961 fn hex_fmt_impl_macro_gen_backwards() {
962 struct Wrapper<T>([u8; 4], PhantomData<T>);
963
964 impl<T: Clone> Borrow<[u8]> for Wrapper<T> {
965 fn borrow(&self) -> &[u8] { &self.0[..] }
966 }
967
968 impl_fmt_traits! {
969 #[display_backward(true)]
970 impl<T: Clone> fmt_traits for Wrapper<T> {
971 const LENGTH: usize = 4;
972 }
973 }
974
975 let tc = Wrapper([0x12, 0x34, 0x56, 0x78], PhantomData::<u32>);
977
978 let want = "78563412";
979 let got = format!("{}", tc);
980 assert_eq!(got, want);
981 }
982
983 #[test]
984 fn hex_display_case() {
985 let bytes = [0xaa, 0xbb, 0xcc, 0xdd];
986 let upper = "AABBCCDD";
987 let lower = "aabbccdd";
988 assert_eq!(bytes.to_upper_hex_string(), upper);
989 assert_eq!(bytes.to_lower_hex_string(), lower);
990 }
991
992 #[test]
993 fn upper_hex_precision_preserves_case() {
994 let bytes: [u8; 4] = [0xab, 0xcd, 0xef, 0x12];
995 let slice: &[u8] = &bytes;
996 assert_eq!(format!("{:.4X}", slice.as_hex()), "ABCD");
997 assert_eq!(format!("{:.5X}", slice.as_hex()), "ABCDE");
998 }
999
1000 #[test]
1001 fn lower_hex_precision_works_correctly_with_lowecase() {
1002 let bytes: [u8; 4] = [0xab, 0xcd, 0xef, 0x12];
1003 let slice: &[u8] = &bytes;
1004 assert_eq!(format!("{:.4x}", slice.as_hex()), "abcd");
1005 assert_eq!(format!("{:.5x}", slice.as_hex()), "abcde");
1006 }
1007
1008 #[test]
1009 fn hex_precision_extreme_boundaries() {
1010 let bytes: [u8; 2] = [0xaa, 0xbb];
1011 let slice: &[u8] = &bytes;
1012
1013 assert_eq!(format!("{:.0X}", slice.as_hex()), "");
1015 assert_eq!(format!("{:.0x}", slice.as_hex()), "");
1016
1017 assert_eq!(format!("{:.1X}", slice.as_hex()), "A");
1019 assert_eq!(format!("{:.1x}", slice.as_hex()), "a");
1020 }
1021
1022 #[test]
1023 fn hex_precision_greater_than_length() {
1024 let bytes: [u8; 2] = [0xab, 0xcd];
1025 let slice: &[u8] = &bytes;
1026
1027 assert_eq!(format!("{:.10X}", slice.as_hex()), "ABCD");
1028 assert_eq!(format!("{:.10x}", slice.as_hex()), "abcd");
1029 }
1030 }
1031
1032 #[cfg(feature = "std")]
1033 mod std {
1034 use alloc::string::String;
1035 use alloc::vec::Vec;
1036 use std::io::Write as _;
1037
1038 use arrayvec::ArrayString;
1039
1040 use super::{Case, DisplayHex, HexWriter};
1041
1042 #[test]
1043 fn hex_writer() {
1044 use std::io::{ErrorKind, Result, Write};
1045
1046 use super::Case::{Lower, Upper};
1047
1048 macro_rules! test_hex_writer {
1049 ($cap:expr, $case: expr, $src: expr, $want: expr, $hex_result: expr) => {
1050 let dest_buf = ArrayString::<$cap>::new();
1051 let mut dest = HexWriter::new(dest_buf, $case);
1052 let got = dest.write($src);
1053 match $want {
1054 Ok(n) => assert_eq!(got.unwrap(), n),
1055 Err(e) => assert_eq!(got.unwrap_err().kind(), e.kind()),
1056 }
1057 assert_eq!(dest.into_inner().as_str(), $hex_result);
1058 };
1059 }
1060
1061 test_hex_writer!(0, Lower, &[], Result::Ok(0), "");
1062 test_hex_writer!(
1063 0,
1064 Lower,
1065 &[0xab, 0xcd],
1066 Result::<usize>::Err(ErrorKind::Other.into()),
1067 ""
1068 );
1069 test_hex_writer!(
1070 1,
1071 Lower,
1072 &[0xab, 0xcd],
1073 Result::<usize>::Err(ErrorKind::Other.into()),
1074 ""
1075 );
1076 test_hex_writer!(2, Lower, &[0xab, 0xcd], Result::Ok(1), "ab");
1077 test_hex_writer!(3, Lower, &[0xab, 0xcd], Result::Ok(1), "ab");
1078 test_hex_writer!(4, Lower, &[0xab, 0xcd], Result::Ok(2), "abcd");
1079 test_hex_writer!(8, Lower, &[0xab, 0xcd], Result::Ok(2), "abcd");
1080 test_hex_writer!(8, Upper, &[0xab, 0xcd], Result::Ok(2), "ABCD");
1081
1082 let vec: Vec<_> = (0u8..32).collect();
1083 let mut writer = HexWriter::new(String::new(), Lower);
1084 writer.write_all(&vec[..]).unwrap();
1085 assert_eq!(writer.into_inner(), vec.to_lower_hex_string());
1086 }
1087
1088 #[test]
1089 fn hex_writer_accepts_and_mut() {
1090 let mut dest_buf = ArrayString::<64>::new();
1091 let mut dest = HexWriter::new(&mut dest_buf, Case::Lower);
1092 let _got = dest.write(b"some data").unwrap();
1093 }
1094 }
1095}