      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105

    //! Generic array are commonly used as a return value for hash digests, so
    //! it's a good idea to allow to hexlify them easily. This module implements
    //! `std::fmt::LowerHex` and `std::fmt::UpperHex` traits.
    //!
    //! Example:
    //!
    //! ```rust
    //! # #[macro_use]
    //! # extern crate generic_array;
    //! # extern crate typenum;
    //! # fn main() {
    //! let array = arr![u8; 10, 20, 30];
    //! assert_eq!(format!("{:x}", array), "0a141e");
    //! # }
    //! ```
    //!

    use core::{fmt, str, ops::Add, cmp::min};

    use typenum::*;

    use crate::{ArrayLength, GenericArray};

    static LOWER_CHARS: &'static [u8] = b"0123456789abcdef";
    static UPPER_CHARS: &'static [u8] = b"0123456789ABCDEF";

    impl<T: ArrayLength<u8>> fmt::LowerHex for GenericArray<u8, T>
    where
        T: Add<T>,
        <T as Add<T>>::Output: ArrayLength<u8>,
    {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            let max_digits = f.precision().unwrap_or_else(|| self.len() * 2);
            let max_hex = (max_digits >> 1) + (max_digits & 1);

            if T::USIZE < 1024 {
                // For small arrays use a stack allocated
                // buffer of 2x number of bytes
                let mut res = GenericArray::<u8, Sum<T, T>>::default();

                self.iter().take(max_hex).enumerate().for_each(|(i, c)| {
                    res[i * 2] = LOWER_CHARS[(c >> 4) as usize];
                    res[i * 2 + 1] = LOWER_CHARS[(c & 0xF) as usize];
                });

                f.write_str(unsafe { str::from_utf8_unchecked(&res[..max_digits]) })?;
            } else {
                // For large array use chunks of up to 1024 bytes (2048 hex chars)
                let mut buf = [0u8; 2048];
                let mut digits_left = max_digits;

                for chunk in self[..max_hex].chunks(1024) {
                    chunk.iter().enumerate().for_each(|(i, c)| {
                        buf[i * 2] = LOWER_CHARS[(c >> 4) as usize];
                        buf[i * 2 + 1] = LOWER_CHARS[(c & 0xF) as usize];
                    });

                    let n = min(chunk.len() * 2, digits_left);
                    f.write_str(unsafe { str::from_utf8_unchecked(&buf[..n]) })?;
                    digits_left -= n;
                }
            }
            Ok(())
        }
    }

    impl<T: ArrayLength<u8>> fmt::UpperHex for GenericArray<u8, T>
    where
        T: Add<T>,
        <T as Add<T>>::Output: ArrayLength<u8>,
    {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            let max_digits = f.precision().unwrap_or_else(|| self.len() * 2);
            let max_hex = (max_digits >> 1) + (max_digits & 1);

            if T::USIZE < 1024 {
                // For small arrays use a stack allocated
                // buffer of 2x number of bytes
                let mut res = GenericArray::<u8, Sum<T, T>>::default();

                self.iter().take(max_hex).enumerate().for_each(|(i, c)| {
                    res[i * 2] = UPPER_CHARS[(c >> 4) as usize];
                    res[i * 2 + 1] = UPPER_CHARS[(c & 0xF) as usize];
                });

                f.write_str(unsafe { str::from_utf8_unchecked(&res[..max_digits]) })?;
            } else {
                // For large array use chunks of up to 1024 bytes (2048 hex chars)
                let mut buf = [0u8; 2048];
                let mut digits_left = max_digits;

                for chunk in self[..max_hex].chunks(1024) {
                    chunk.iter().enumerate().for_each(|(i, c)| {
                        buf[i * 2] = UPPER_CHARS[(c >> 4) as usize];
                        buf[i * 2 + 1] = UPPER_CHARS[(c & 0xF) as usize];
                    });

                    let n = min(chunk.len() * 2, digits_left);
                    f.write_str(unsafe { str::from_utf8_unchecked(&buf[..n]) })?;
                    digits_left -= n;
                }
            }
            Ok(())
        }
    }