Skip to main content

hex_conservative/
display.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Helpers for displaying bytes as hex strings.
4//!
5//! This module provides a trait for displaying things as hex as well as an implementation for
6//! `&[u8]`.
7//!
8//! For arrays and slices we support padding and precision for length < 512 bytes.
9//!
10//! # Examples
11//!
12//! ```
13//! use hex_conservative::DisplayHex;
14//!
15//! // Display as hex.
16//! let v = vec![0xde, 0xad, 0xbe, 0xef];
17//! assert_eq!(format!("{}", v.as_hex()), "deadbeef");
18//!
19//! // Get the most significant bytes.
20//! let v = vec![0x01, 0x23, 0x45, 0x67];
21//! assert_eq!(format!("{0:.4}", v.as_hex()), "0123");
22//!
23//! // Padding with zeros
24//! let v = vec![0xab; 2];
25//! assert_eq!(format!("{:0>8}", v.as_hex()), "0000abab");
26//!```
27
28#[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
38/// Extension trait for types that can be displayed as hex.
39///
40/// Types that have a single, obvious text representation being hex should **not** implement this
41/// trait and simply implement `Display` instead.
42pub trait DisplayHex {
43    /// The type providing [`fmt::Display`] implementation.
44    ///
45    /// This is a wrapper type holding a reference to `Self`.
46    type Display<'a>: fmt::Display + fmt::Debug + fmt::LowerHex + fmt::UpperHex
47    where
48        Self: 'a;
49
50    /// Display `Self` as a continuous sequence of ASCII hex chars.
51    fn as_hex<'a>(&'a self) -> Self::Display<'a>;
52
53    /// Create a lower-hex-encoded string.
54    ///
55    /// A shorthand for `to_hex_string(Case::Lower)`, so that `Case` doesn't need to be imported.
56    ///
57    /// This may be faster than `.display_hex().to_string()` because it uses `reserve_suggestion`.
58    #[cfg(feature = "alloc")]
59    #[inline]
60    fn to_lower_hex_string(&self) -> String { self.to_hex_string(Case::Lower) }
61
62    /// Create an upper-hex-encoded string.
63    ///
64    /// A shorthand for `to_hex_string(Case::Upper)`, so that `Case` doesn't need to be imported.
65    ///
66    /// This may be faster than `.display_hex().to_string()` because it uses `reserve_suggestion`.
67    #[cfg(feature = "alloc")]
68    #[inline]
69    fn to_upper_hex_string(&self) -> String { self.to_hex_string(Case::Upper) }
70
71    /// Create a hex-encoded string.
72    ///
73    /// This may be faster than `.display_hex().to_string()` because it uses `reserve_suggestion`.
74    #[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    /// Appends hex-encoded content to an existing `String`.
82    ///
83    /// This may be faster than `write!(string, "{:x}", self.as_hex())` because it uses
84    /// `hex_reserve_sugggestion`.
85    #[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            // We don't expect `std` to ever be buggy, so the bug is most likely in the `Display`
97            // impl of `Self::Display`.
98            panic!("The implementation of Display for {} returned an error when it shouldn't", name)
99        });
100    }
101
102    /// Hints how many bytes to reserve when creating a `String`.
103    ///
104    /// If you don't know you can just return 0 and take the perf hit.
105    // We prefix the name with `hex_` to avoid potential collision with other methods.
106    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    // There are at least two optimizations left:
112    //
113    // * Reusing the buffer (encoder) which may decrease the number of virtual calls
114    // * Not recursing, avoiding another 1024B allocation and zeroing
115    //
116    // This would complicate the code so I was too lazy to do them but feel free to send a PR!
117
118    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            write!(f, "{}", bytes[..(max / 2)].as_hex())?;
127            if max % 2 == 1 {
128                f.write_char(case.table().byte_to_chars(bytes[max / 2])[0])?;
129            }
130        }
131        Some(_) | None => {
132            let mut chunks = bytes.chunks_exact(512);
133            for chunk in &mut chunks {
134                encoder.put_bytes(chunk);
135                f.write_str(encoder.as_str())?;
136                encoder.clear();
137            }
138            encoder.put_bytes(chunks.remainder());
139            f.write_str(encoder.as_str())?;
140        }
141    }
142
143    write_pad_right(f, pad_right, &mut encoder)
144}
145
146fn write_pad_left(
147    f: &mut fmt::Formatter,
148    bytes_len: usize,
149    encoder: &mut BufEncoder<1024>,
150) -> Result<usize, fmt::Error> {
151    let pad_right = if let Some(width) = f.width() {
152        // Add space for 2 characters if the '#' flag is set
153        let full_string_len = if f.alternate() { bytes_len * 2 + 2 } else { bytes_len * 2 };
154        let string_len = match f.precision() {
155            Some(max) => core::cmp::min(max, full_string_len),
156            None => full_string_len,
157        };
158
159        if string_len < width {
160            let (left, right) = match f.align().unwrap_or(fmt::Alignment::Left) {
161                fmt::Alignment::Left => (0, width - string_len),
162                fmt::Alignment::Right => (width - string_len, 0),
163                fmt::Alignment::Center =>
164                    ((width - string_len) / 2, (width - string_len).div_ceil(2)),
165            };
166            // Avoid division by zero and optimize for common case.
167            if left > 0 {
168                let c = f.fill();
169                let chunk_len = encoder.put_filler(c, left);
170                let padding = encoder.as_str();
171                for _ in 0..(left / chunk_len) {
172                    f.write_str(padding)?;
173                }
174                f.write_str(&padding[..((left % chunk_len) * c.len_utf8())])?;
175                encoder.clear();
176            }
177            right
178        } else {
179            0
180        }
181    } else {
182        0
183    };
184    Ok(pad_right)
185}
186
187fn write_pad_right(
188    f: &mut fmt::Formatter,
189    pad_right: usize,
190    encoder: &mut BufEncoder<1024>,
191) -> fmt::Result {
192    // Avoid division by zero and optimize for common case.
193    if pad_right > 0 {
194        encoder.clear();
195        let c = f.fill();
196        let chunk_len = encoder.put_filler(c, pad_right);
197        let padding = encoder.as_str();
198        for _ in 0..(pad_right / chunk_len) {
199            f.write_str(padding)?;
200        }
201        f.write_str(&padding[..((pad_right % chunk_len) * c.len_utf8())])?;
202    }
203    Ok(())
204}
205
206impl DisplayHex for [u8] {
207    type Display<'a> = DisplayByteSlice<'a>;
208
209    #[inline]
210    fn as_hex<'a>(&'a self) -> Self::Display<'a> { DisplayByteSlice { bytes: self } }
211
212    #[inline]
213    fn hex_reserve_suggestion(&self) -> usize {
214        // Since the string wouldn't fit into address space if this overflows (actually even for
215        // smaller amounts) it's better to panic right away. It should also give the optimizer
216        // better opportunities.
217        self.len().checked_mul(2).expect("the string wouldn't fit into address space")
218    }
219}
220
221/// Displays byte slice as hex.
222///
223/// Created by [`<&[u8] as DisplayHex>::as_hex`](DisplayHex::as_hex).
224#[derive(Clone, PartialEq, Eq, Hash)]
225pub struct DisplayByteSlice<'a> {
226    // pub because we want to keep lengths in sync
227    pub(crate) bytes: &'a [u8],
228}
229
230impl DisplayByteSlice<'_> {
231    #[inline]
232    fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
233        internal_display(self.bytes, f, case)
234    }
235}
236
237impl fmt::Display for DisplayByteSlice<'_> {
238    #[inline]
239    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
240}
241
242impl fmt::Debug for DisplayByteSlice<'_> {
243    #[inline]
244    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
245}
246
247impl fmt::LowerHex for DisplayByteSlice<'_> {
248    #[inline]
249    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
250}
251
252impl fmt::UpperHex for DisplayByteSlice<'_> {
253    #[inline]
254    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
255}
256
257/// Efficiently formats a sequence of bytes as a hexadecimal string with compile-time-known length.
258///
259/// Whenever the length of a sequence of bytes (usually an array, but it could be any iterator)
260/// is known at compile time, it is more efficient to use this macro because various length checks
261/// and loops are elided. The buffer is just filled once and then emitted to the formatter.
262///
263/// This supports all formatting options of formatter and may be faster than calling `as_hex()` on
264/// an arbitrary `&[u8]`. Note that the implementation intentionally keeps leading zeros even when
265/// not requested. This is designed to display values such as hashes and keys and removing leading
266/// zeros would be confusing.
267///
268/// Note that the bytes parameter is `IntoIterator` this means that if you would like to do some
269/// manipulation to the byte array before formatting then you can. For example `bytes.iter().rev()`
270/// to print the array backwards.
271///
272/// ## Parameters
273///
274/// * `$formatter` - a [`fmt::Formatter`].
275/// * `$len` known length of `$bytes`, must be a const expression.
276/// * `$bytes` - bytes to be encoded, most likely a reference to an array.
277/// * `$case` - value of type [`Case`] determining whether to format as lower or upper case.
278///
279/// ## Returns
280///
281/// Returns [`core::fmt::Result`].
282///
283/// ## Panics
284///
285/// This macro panics if the length of the encoded item is larger than `$len`.
286///
287/// ## Static Assertions
288///
289/// The use of a macro instead of a function allows for compile-time length validation. This macro
290/// fails to compile if `$len` is more than half of `usize::MAX`. This prevents runtime panics or
291/// logic errors when formatting fixed-size primitives like Bitcoin hashes or public keys.
292///
293/// ## Examples
294///
295/// ```rust
296/// use hex_conservative::{fmt_hex_max, Case};
297/// use std::fmt;
298///
299/// struct MyHash([u8; 32]);
300///
301/// impl fmt::LowerHex for MyHash {
302///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
303///         // Explicitly use Lower case for {:x}
304///         fmt_hex_max!(f, 32, &self.0, Case::Lower)
305///     }
306/// }
307///
308/// impl fmt::UpperHex for MyHash {
309///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
310///         // Explicitly use Upper case for {:X}
311///         fmt_hex_max!(f, 32, &self.0, Case::Upper)
312///     }
313/// }
314/// ```
315#[macro_export]
316macro_rules! fmt_hex_max {
317    ($formatter:expr, $len:expr, $bytes:expr, $case:expr) => {{
318        // statically check $len
319        #[allow(deprecated)]
320        const _: () = [()][($len > usize::MAX / 2) as usize];
321        assert!(
322            $bytes.len() <= $len,
323            "length of the encoded item ({}) is larger than {}",
324            $bytes.len(),
325            $len
326        );
327        $crate::display::fmt_hex_max_fn::<_, { $len * 2 }>($formatter, $bytes, $case)
328    }};
329}
330pub use fmt_hex_max;
331
332/// Formats bytes as hex with runtime and compile-time length checks.
333///
334/// ## Panics
335///
336/// This macro panics if `$len` is not equal to `$bytes.len()`
337///
338/// See [`fmt_hex_max`] for details.
339#[macro_export]
340macro_rules! fmt_hex_exact {
341    ($formatter:expr, $len:expr, $bytes:expr, $case:expr) => {{
342        assert_eq!($bytes.len(), $len);
343        $crate::fmt_hex_max!($formatter, $len, $bytes, $case)
344    }};
345}
346pub use fmt_hex_exact;
347
348/// Formats bytes as hex in lower case.
349///
350/// See [`fmt_hex_max!`] for details.
351#[macro_export]
352macro_rules! fmt_hex_lower {
353    ($formatter:expr, $len:expr, $bytes:expr) => {
354        $crate::fmt_hex_max!($formatter, $len, $bytes, $crate::Case::Lower)
355    };
356}
357pub use fmt_hex_lower;
358
359/// Formats bytes as hex in upper case.
360///
361/// See [`fmt_hex_max!`] for details.
362#[macro_export]
363macro_rules! fmt_hex_upper {
364    ($formatter:expr, $len:expr, $bytes:expr) => {
365        $crate::fmt_hex_max!($formatter, $len, $bytes, $crate::Case::Upper)
366    };
367}
368pub use fmt_hex_upper;
369
370/// Adds `core::fmt` trait implementations to type `$ty`.
371///
372/// Implements:
373///
374/// - `fmt::{LowerHex, UpperHex}` using [`fmt_hex_exact`].
375/// - `fmt::{Display, Debug}` by calling `LowerHex`.
376///
377/// Requires:
378///
379/// - `$ty` must implement `IntoIterator<Item=Borrow<u8>>`.
380///
381/// ## Parameters
382///
383/// * `$ty` - the type to implement traits on.
384/// * `$len` - known length of `$bytes`, must be a const expression.
385/// * `$bytes` - bytes to be encoded, most likely a reference to an array.
386/// * `$reverse` - true if you want the array to be displayed backwards.
387/// * `$gen: $gent` - optional generic type(s) and trait bound(s) to put on `$ty` e.g, `F: Foo`.
388///
389/// ## Examples
390///
391/// ```
392/// # use core::borrow::Borrow;
393/// # use hex_conservative::impl_fmt_traits;
394/// struct Wrapper([u8; 4]);
395///
396/// impl Borrow<[u8]> for Wrapper {
397///     fn borrow(&self) -> &[u8] { &self.0[..] }
398/// }
399///
400/// impl_fmt_traits! {
401///     impl fmt_traits for Wrapper {
402///         const LENGTH: usize = 4;
403///     }
404/// }
405///
406/// let w = Wrapper([0x12, 0x34, 0x56, 0x78]);
407/// assert_eq!(format!("{}", w), "12345678");
408/// ```
409///
410/// We support generics on `$ty`:
411///
412/// ```
413/// # use core::borrow::Borrow;
414/// # use core::marker::PhantomData;
415/// # use hex_conservative::impl_fmt_traits;
416/// struct Wrapper<T>([u8; 4], PhantomData<T>);
417///
418/// // `Clone` is just some arbitrary trait.
419/// impl<T: Clone> Borrow<[u8]> for Wrapper<T> {
420///     fn borrow(&self) -> &[u8] { &self.0[..] }
421/// }
422///
423/// impl_fmt_traits! {
424///     impl<T: Clone> fmt_traits for Wrapper<T> {
425///         const LENGTH: usize = 4;
426///     }
427/// }
428///
429/// let w = Wrapper([0x12, 0x34, 0x56, 0x78], PhantomData::<u32>);
430/// assert_eq!(format!("{}", w), "12345678");
431/// ```
432///
433/// And also, as is required by `rust-bitcoin`, we support displaying
434/// the hex string byte-wise backwards:
435///
436/// ```
437/// # use core::borrow::Borrow;
438/// # use hex_conservative::impl_fmt_traits;
439/// struct Wrapper([u8; 4]);
440///
441/// impl Borrow<[u8]> for Wrapper {
442///     fn borrow(&self) -> &[u8] { &self.0[..] }
443/// }
444///
445/// impl_fmt_traits! {
446///     #[display_backward(true)]
447///     impl fmt_traits for Wrapper {
448///         const LENGTH: usize = 4;
449///     }
450/// }
451/// let w = Wrapper([0x12, 0x34, 0x56, 0x78]);
452/// assert_eq!(format!("{}", w), "78563412");
453/// ```
454#[macro_export]
455macro_rules! impl_fmt_traits {
456    // Without generic and trait bounds and without display_backward attribute.
457    (impl fmt_traits for $ty:ident { const LENGTH: usize = $len:expr; }) => {
458        $crate::impl_fmt_traits! {
459            #[display_backward(false)]
460            impl<> fmt_traits for $ty<> {
461                const LENGTH: usize = $len;
462            }
463        }
464    };
465    // Without generic and trait bounds and with display_backward attribute.
466    (#[display_backward($reverse:expr)] impl fmt_traits for $ty:ident { const LENGTH: usize = $len:expr; }) => {
467        $crate::impl_fmt_traits! {
468            #[display_backward($reverse)]
469            impl<> fmt_traits for $ty<> {
470                const LENGTH: usize = $len;
471            }
472        }
473    };
474    // With generic and trait bounds and without display_backward attribute.
475    (impl<$($gen:ident: $gent:ident),*> fmt_traits for $ty:ident<$($unused:ident),*> { const LENGTH: usize = $len:expr; }) => {
476        $crate::impl_fmt_traits! {
477            #[display_backward(false)]
478            impl<$($gen: $gent),*> fmt_traits for $ty<$($unused),*> {
479                const LENGTH: usize = $len;
480            }
481        }
482    };
483    // With generic and trait bounds and display_backward attribute.
484    (#[display_backward($reverse:expr)] impl<$($gen:ident: $gent:ident),*> fmt_traits for $ty:ident<$($unused:ident),*> { const LENGTH: usize = $len:expr; }) => {
485        impl<$($gen: $gent),*> $crate::_export::_core::fmt::LowerHex for $ty<$($gen),*> {
486            #[inline]
487            fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
488                let case = $crate::Case::Lower;
489
490                if $reverse {
491                    let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter().rev();
492                    $crate::fmt_hex_exact!(f, $len, bytes, case)
493                } else {
494                    let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter();
495                    $crate::fmt_hex_exact!(f, $len, bytes, case)
496                }
497            }
498        }
499
500        impl<$($gen: $gent),*> $crate::_export::_core::fmt::UpperHex for $ty<$($gen),*> {
501            #[inline]
502            fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
503                let case = $crate::Case::Upper;
504
505                if $reverse {
506                    let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter().rev();
507                    $crate::fmt_hex_exact!(f, $len, bytes, case)
508                } else {
509                    let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter();
510                    $crate::fmt_hex_exact!(f, $len, bytes, case)
511                }
512            }
513        }
514
515        impl<$($gen: $gent),*> $crate::_export::_core::fmt::Display for $ty<$($gen),*> {
516            #[inline]
517            fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
518                $crate::_export::_core::fmt::LowerHex::fmt(self, f)
519            }
520        }
521
522        impl<$($gen: $gent),*> $crate::_export::_core::fmt::Debug for $ty<$($gen),*> {
523            #[inline]
524            fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
525                $crate::_export::_core::fmt::LowerHex::fmt(&self, f)
526            }
527        }
528    };
529}
530pub use impl_fmt_traits;
531
532// Implementation detail of `fmt_hex_max` macro to de-duplicate the code
533//
534// Whether hex is an integer or a string is debatable, we cater a little bit to each.
535// - We support users adding `0x` prefix using "{:#}" (treating hex like an integer).
536// - We support limiting the output using precision "{:.10}" (treating hex like a string).
537//
538// This assumes `bytes.len() * 2 == N`.
539#[doc(hidden)]
540#[inline]
541pub fn fmt_hex_max_fn<I, const N: usize>(
542    f: &mut fmt::Formatter,
543    bytes: I,
544    case: Case,
545) -> fmt::Result
546where
547    I: IntoIterator,
548    I::Item: Borrow<u8>,
549{
550    let mut padding_encoder = BufEncoder::<1024>::new(case);
551    let pad_right = write_pad_left(f, N / 2, &mut padding_encoder)?;
552
553    if f.alternate() {
554        f.write_str("0x")?;
555    }
556    let mut encoder = BufEncoder::<N>::new(case);
557    let encoded = match f.precision() {
558        Some(p) if p < N => {
559            let n = p.div_ceil(2);
560            encoder.put_bytes(bytes.into_iter().take(n));
561            &encoder.as_str()[..p]
562        }
563        _ => {
564            encoder.put_bytes(bytes);
565            encoder.as_str()
566        }
567    };
568    f.write_str(encoded)?;
569
570    write_pad_right(f, pad_right, &mut padding_encoder)
571}
572
573/// Given a `T:` [`fmt::Write`], `HexWriter` implements [`std::io::Write`]
574/// and writes the source bytes to its inner `T` as hex characters.
575#[cfg(feature = "std")]
576#[derive(Debug, Clone, PartialEq, Eq, Hash)]
577pub struct HexWriter<T> {
578    writer: T,
579    table: &'static Table,
580}
581
582#[cfg(feature = "std")]
583impl<T> HexWriter<T> {
584    /// Creates a `HexWriter` that writes the source bytes to `dest` as hex characters
585    /// in the given `case`.
586    ///
587    /// Note even though we take ownership of the writer one can also call this with `&mut dest`.
588    pub fn new(dest: T, case: Case) -> Self { Self { writer: dest, table: case.table() } }
589    /// Consumes this `HexWriter` returning the inner `T`.
590    pub fn into_inner(self) -> T { self.writer }
591}
592
593#[cfg(feature = "std")]
594impl<T> std::io::Write for HexWriter<T>
595where
596    T: core::fmt::Write,
597{
598    /// Writes `buf` into [`HexWriter`].
599    ///
600    /// # Errors
601    ///
602    /// If no bytes could be written to this `HexWriter`, and the provided buffer is not empty,
603    /// returns [`std::io::ErrorKind::Other`], otherwise returns `Ok`.
604    fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
605        let mut n = 0;
606        for byte in buf {
607            let mut hex_chars = [0u8; 2];
608            let hex_str = self.table.byte_to_str(&mut hex_chars, *byte);
609            if self.writer.write_str(hex_str).is_err() {
610                break;
611            }
612            n += 1;
613        }
614        if n == 0 && !buf.is_empty() {
615            Err(std::io::ErrorKind::Other.into())
616        } else {
617            Ok(n)
618        }
619    }
620
621    /// `flush` is a no-op for [`HexWriter`].
622    ///
623    /// # Errors
624    ///
625    /// [`HexWriter`] never errors when flushing.
626    fn flush(&mut self) -> Result<(), std::io::Error> { Ok(()) }
627}
628
629#[cfg(test)]
630mod tests {
631    #[cfg(feature = "alloc")]
632    use super::*;
633
634    #[cfg(feature = "alloc")]
635    mod alloc {
636        use core::marker::PhantomData;
637
638        use super::*;
639        use crate::alloc::vec::Vec;
640
641        fn check_encoding(bytes: &[u8]) {
642            use core::fmt::Write;
643
644            let s1 = bytes.to_lower_hex_string();
645            let mut s2 = String::with_capacity(bytes.len() * 2);
646            for b in bytes {
647                write!(s2, "{:02x}", b).unwrap();
648            }
649            assert_eq!(s1, s2);
650        }
651
652        #[test]
653        fn empty() { check_encoding(b""); }
654
655        #[test]
656        fn single() { check_encoding(b"*"); }
657
658        #[test]
659        fn two() { check_encoding(b"*x"); }
660
661        #[test]
662        fn just_below_boundary() { check_encoding(&[42; 512]); }
663
664        #[test]
665        fn just_above_boundary() { check_encoding(&[42; 513]); }
666
667        #[test]
668        fn just_above_double_boundary() { check_encoding(&[42; 1025]); }
669
670        #[test]
671        fn fmt_exact_macro() {
672            use crate::alloc::string::ToString;
673
674            struct Dummy([u8; 32]);
675
676            impl fmt::Display for Dummy {
677                fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
678                    fmt_hex_exact!(f, 32, &self.0, Case::Lower)
679                }
680            }
681            let dummy = Dummy([42; 32]);
682            assert_eq!(dummy.to_string(), "2a".repeat(32));
683            assert_eq!(format!("{:.10}", dummy), "2a".repeat(5));
684            assert_eq!(format!("{:.11}", dummy), "2a".repeat(5) + "2");
685            assert_eq!(format!("{:.65}", dummy), "2a".repeat(32));
686        }
687
688        struct TestHexUpperLower<'a>(&'a [u8], bool);
689
690        impl fmt::Display for TestHexUpperLower<'_> {
691            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
692                if self.1 {
693                    fmt_hex_upper!(f, 3, self.0)
694                } else {
695                    fmt_hex_lower!(f, 3, self.0)
696                }
697            }
698        }
699
700        #[test]
701        fn fmt_hex_lower_macro() {
702            let bytes = [0x1a, 0x2b, 0x3c];
703            assert_eq!(format!("{}", TestHexUpperLower(&bytes, false)), "1a2b3c");
704        }
705
706        #[test]
707        fn fmt_hex_upper_macro() {
708            let bytes = [0x1a, 0x2b, 0x3c];
709            assert_eq!(format!("{}", TestHexUpperLower(&bytes, true)), "1A2B3C");
710        }
711
712        macro_rules! define_dummy {
713            ($len:literal) => {
714                struct Dummy([u8; $len]);
715                impl fmt::Debug for Dummy {
716                    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
717                        fmt_hex_exact!(f, $len, &self.0, Case::Lower)
718                    }
719                }
720                impl fmt::Display for Dummy {
721                    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
722                        fmt_hex_exact!(f, $len, &self.0, Case::Lower)
723                    }
724                }
725            };
726        }
727
728        macro_rules! test_display_hex {
729            ($fs: expr, $a: expr, $check: expr) => {
730                let array = $a;
731                let slice = &$a;
732                let vec = Vec::from($a);
733                let dummy = Dummy($a);
734                assert_eq!(format!($fs, array.as_hex()), $check);
735                assert_eq!(format!($fs, slice.as_hex()), $check);
736                assert_eq!(format!($fs, vec.as_hex()), $check);
737                assert_eq!(format!($fs, dummy), $check);
738            };
739        }
740
741        #[test]
742        fn alternate_flag() {
743            define_dummy!(4);
744
745            test_display_hex!("{:#?}", [0xc0, 0xde, 0xca, 0xfe], "0xc0decafe");
746            test_display_hex!("{:#}", [0xc0, 0xde, 0xca, 0xfe], "0xc0decafe");
747        }
748
749        #[test]
750        fn display_short_with_padding() {
751            define_dummy!(2);
752
753            test_display_hex!("Hello {:<8}!", [0xbe, 0xef], "Hello beef    !");
754            test_display_hex!("Hello {:-<8}!", [0xbe, 0xef], "Hello beef----!");
755            test_display_hex!("Hello {:^8}!", [0xbe, 0xef], "Hello   beef  !");
756            test_display_hex!("Hello {:>8}!", [0xbe, 0xef], "Hello     beef!");
757
758            test_display_hex!("Hello {:<#8}!", [0xbe, 0xef], "Hello 0xbeef  !");
759            test_display_hex!("Hello {:-<#8}!", [0xbe, 0xef], "Hello 0xbeef--!");
760            test_display_hex!("Hello {:^#8}!", [0xbe, 0xef], "Hello  0xbeef !");
761            test_display_hex!("Hello {:>#8}!", [0xbe, 0xef], "Hello   0xbeef!");
762        }
763
764        #[test]
765        fn display_long() {
766            define_dummy!(512);
767            // Note this string is shorter than the one above.
768            let a = [0xab; 512];
769
770            let mut want = "0".repeat(2000 - 1024);
771            want.extend(core::iter::repeat("ab").take(512));
772            test_display_hex!("{:0>2000}", a, want);
773
774            let mut want = "0".repeat(2000 - 1026);
775            want.push_str("0x");
776            want.extend(core::iter::repeat("ab").take(512));
777            test_display_hex!("{:0>#2000}", a, want);
778        }
779
780        // Precision and padding act the same as for strings in the stdlib (because we use `Formatter::pad`).
781
782        #[test]
783        fn precision_truncates() {
784            // Precision gets the most significant bytes.
785            // Remember the integer is number of hex chars not number of bytes.
786            define_dummy!(4);
787
788            test_display_hex!("{0:.4}", [0x12, 0x34, 0x56, 0x78], "1234");
789            test_display_hex!("{0:.5}", [0x12, 0x34, 0x56, 0x78], "12345");
790
791            test_display_hex!("{0:#.4}", [0x12, 0x34, 0x56, 0x78], "0x1234");
792            test_display_hex!("{0:#.5}", [0x12, 0x34, 0x56, 0x78], "0x12345");
793        }
794
795        #[test]
796        fn precision_with_padding_truncates() {
797            // Precision gets the most significant bytes.
798            define_dummy!(4);
799
800            test_display_hex!("{0:10.4}", [0x12, 0x34, 0x56, 0x78], "1234      ");
801            test_display_hex!("{0:10.5}", [0x12, 0x34, 0x56, 0x78], "12345     ");
802
803            test_display_hex!("{0:#10.4}", [0x12, 0x34, 0x56, 0x78], "0x1234      ");
804            test_display_hex!("{0:#10.5}", [0x12, 0x34, 0x56, 0x78], "0x12345     ");
805        }
806
807        #[test]
808        fn precision_with_padding_pads_right() {
809            define_dummy!(4);
810
811            test_display_hex!("{0:10.20}", [0x12, 0x34, 0x56, 0x78], "12345678  ");
812            test_display_hex!("{0:10.14}", [0x12, 0x34, 0x56, 0x78], "12345678  ");
813
814            test_display_hex!("{0:#12.20}", [0x12, 0x34, 0x56, 0x78], "0x12345678  ");
815            test_display_hex!("{0:#12.14}", [0x12, 0x34, 0x56, 0x78], "0x12345678  ");
816        }
817
818        #[test]
819        fn precision_with_padding_pads_left() {
820            define_dummy!(4);
821
822            test_display_hex!("{0:>10.20}", [0x12, 0x34, 0x56, 0x78], "  12345678");
823
824            test_display_hex!("{0:>#12.20}", [0x12, 0x34, 0x56, 0x78], "  0x12345678");
825        }
826
827        #[test]
828        fn precision_with_padding_pads_center() {
829            define_dummy!(4);
830
831            test_display_hex!("{0:^10.20}", [0x12, 0x34, 0x56, 0x78], " 12345678 ");
832
833            test_display_hex!("{0:^#12.20}", [0x12, 0x34, 0x56, 0x78], " 0x12345678 ");
834        }
835
836        #[test]
837        fn precision_with_padding_pads_center_odd() {
838            define_dummy!(4);
839
840            test_display_hex!("{0:^11.20}", [0x12, 0x34, 0x56, 0x78], " 12345678  ");
841
842            test_display_hex!("{0:^#13.20}", [0x12, 0x34, 0x56, 0x78], " 0x12345678  ");
843        }
844
845        #[test]
846        fn precision_does_not_extend() {
847            define_dummy!(4);
848
849            test_display_hex!("{0:.16}", [0x12, 0x34, 0x56, 0x78], "12345678");
850
851            test_display_hex!("{0:#.16}", [0x12, 0x34, 0x56, 0x78], "0x12345678");
852        }
853
854        #[test]
855        fn padding_extends() {
856            define_dummy!(2);
857
858            test_display_hex!("{:0>8}", [0xab; 2], "0000abab");
859
860            test_display_hex!("{:0>#8}", [0xab; 2], "000xabab");
861        }
862
863        #[test]
864        fn padding_does_not_truncate() {
865            define_dummy!(4);
866
867            test_display_hex!("{:0>4}", [0x12, 0x34, 0x56, 0x78], "12345678");
868            test_display_hex!("{:0>4}", [0x12, 0x34, 0x56, 0x78], "12345678");
869
870            test_display_hex!("{:0>#4}", [0x12, 0x34, 0x56, 0x78], "0x12345678");
871            test_display_hex!("{:0>#4}", [0x12, 0x34, 0x56, 0x78], "0x12345678");
872        }
873
874        // Tests `impl_fmt_traits` in module scope.
875        // ref: https://rust-lang.github.io/api-guidelines/macros.html#c-anywhere
876        #[allow(dead_code)]
877        struct Wrapper([u8; 4]);
878
879        impl Borrow<[u8]> for Wrapper {
880            fn borrow(&self) -> &[u8] { &self.0[..] }
881        }
882
883        impl_fmt_traits! {
884            #[display_backward(false)]
885            impl fmt_traits for Wrapper {
886                const LENGTH: usize = 4;
887            }
888        }
889
890        #[test]
891        fn hex_fmt_impl_macro_forward() {
892            struct Wrapper([u8; 4]);
893
894            impl Borrow<[u8]> for Wrapper {
895                fn borrow(&self) -> &[u8] { &self.0[..] }
896            }
897
898            impl_fmt_traits! {
899                #[display_backward(false)]
900                impl fmt_traits for Wrapper {
901                    const LENGTH: usize = 4;
902                }
903            }
904
905            let tc = Wrapper([0x12, 0x34, 0x56, 0x78]);
906
907            let want = "12345678";
908            let got = format!("{}", tc);
909            assert_eq!(got, want);
910        }
911
912        #[test]
913        fn hex_fmt_impl_macro_backwards() {
914            struct Wrapper([u8; 4]);
915
916            impl Borrow<[u8]> for Wrapper {
917                fn borrow(&self) -> &[u8] { &self.0[..] }
918            }
919
920            impl_fmt_traits! {
921                #[display_backward(true)]
922                impl fmt_traits for Wrapper {
923                    const LENGTH: usize = 4;
924                }
925            }
926
927            let tc = Wrapper([0x12, 0x34, 0x56, 0x78]);
928
929            let want = "78563412";
930            let got = format!("{}", tc);
931            assert_eq!(got, want);
932        }
933
934        #[test]
935        fn hex_fmt_impl_macro_gen_forward() {
936            struct Wrapper<T>([u8; 4], PhantomData<T>);
937
938            impl<T: Clone> Borrow<[u8]> for Wrapper<T> {
939                fn borrow(&self) -> &[u8] { &self.0[..] }
940            }
941
942            impl_fmt_traits! {
943                #[display_backward(false)]
944                impl<T: Clone> fmt_traits for Wrapper<T> {
945                    const LENGTH: usize = 4;
946                }
947            }
948
949            // We just use `u32` here as some arbitrary type that implements some arbitrary trait.
950            let tc = Wrapper([0x12, 0x34, 0x56, 0x78], PhantomData::<u32>);
951
952            let want = "12345678";
953            let got = format!("{}", tc);
954            assert_eq!(got, want);
955        }
956
957        #[test]
958        fn hex_fmt_impl_macro_gen_backwards() {
959            struct Wrapper<T>([u8; 4], PhantomData<T>);
960
961            impl<T: Clone> Borrow<[u8]> for Wrapper<T> {
962                fn borrow(&self) -> &[u8] { &self.0[..] }
963            }
964
965            impl_fmt_traits! {
966                #[display_backward(true)]
967                impl<T: Clone> fmt_traits for Wrapper<T> {
968                    const LENGTH: usize = 4;
969                }
970            }
971
972            // We just use `u32` here as some arbitrary type that implements some arbitrary trait.
973            let tc = Wrapper([0x12, 0x34, 0x56, 0x78], PhantomData::<u32>);
974
975            let want = "78563412";
976            let got = format!("{}", tc);
977            assert_eq!(got, want);
978        }
979
980        #[test]
981        fn hex_display_case() {
982            let bytes = [0xaa, 0xbb, 0xcc, 0xdd];
983            let upper = "AABBCCDD";
984            let lower = "aabbccdd";
985            assert_eq!(bytes.to_upper_hex_string(), upper);
986            assert_eq!(bytes.to_lower_hex_string(), lower);
987        }
988    }
989
990    #[cfg(feature = "std")]
991    mod std {
992        use alloc::string::String;
993        use alloc::vec::Vec;
994        use std::io::Write as _;
995
996        use arrayvec::ArrayString;
997
998        use super::{Case, DisplayHex, HexWriter};
999
1000        #[test]
1001        fn hex_writer() {
1002            use std::io::{ErrorKind, Result, Write};
1003
1004            use super::Case::{Lower, Upper};
1005
1006            macro_rules! test_hex_writer {
1007                ($cap:expr, $case: expr, $src: expr, $want: expr, $hex_result: expr) => {
1008                    let dest_buf = ArrayString::<$cap>::new();
1009                    let mut dest = HexWriter::new(dest_buf, $case);
1010                    let got = dest.write($src);
1011                    match $want {
1012                        Ok(n) => assert_eq!(got.unwrap(), n),
1013                        Err(e) => assert_eq!(got.unwrap_err().kind(), e.kind()),
1014                    }
1015                    assert_eq!(dest.into_inner().as_str(), $hex_result);
1016                };
1017            }
1018
1019            test_hex_writer!(0, Lower, &[], Result::Ok(0), "");
1020            test_hex_writer!(
1021                0,
1022                Lower,
1023                &[0xab, 0xcd],
1024                Result::<usize>::Err(ErrorKind::Other.into()),
1025                ""
1026            );
1027            test_hex_writer!(
1028                1,
1029                Lower,
1030                &[0xab, 0xcd],
1031                Result::<usize>::Err(ErrorKind::Other.into()),
1032                ""
1033            );
1034            test_hex_writer!(2, Lower, &[0xab, 0xcd], Result::Ok(1), "ab");
1035            test_hex_writer!(3, Lower, &[0xab, 0xcd], Result::Ok(1), "ab");
1036            test_hex_writer!(4, Lower, &[0xab, 0xcd], Result::Ok(2), "abcd");
1037            test_hex_writer!(8, Lower, &[0xab, 0xcd], Result::Ok(2), "abcd");
1038            test_hex_writer!(8, Upper, &[0xab, 0xcd], Result::Ok(2), "ABCD");
1039
1040            let vec: Vec<_> = (0u8..32).collect();
1041            let mut writer = HexWriter::new(String::new(), Lower);
1042            writer.write_all(&vec[..]).unwrap();
1043            assert_eq!(writer.into_inner(), vec.to_lower_hex_string());
1044        }
1045
1046        #[test]
1047        fn hex_writer_accepts_and_mut() {
1048            let mut dest_buf = ArrayString::<64>::new();
1049            let mut dest = HexWriter::new(&mut dest_buf, Case::Lower);
1050            let _got = dest.write(b"some data").unwrap();
1051        }
1052    }
1053}