1 //! Generic array are commonly used as a return value for hash digests, so
2 //! it's a good idea to allow to hexlify them easily. This module implements
3 //! `std::fmt::LowerHex` and `std::fmt::UpperHex` traits.
4 //!
5 //! Example:
6 //!
7 //! ```rust
8 //! # #[macro_use]
9 //! # extern crate generic_array;
10 //! # extern crate typenum;
11 //! # fn main() {
12 //! let array = arr![u8; 10, 20, 30];
13 //! assert_eq!(format!("{:x}", array), "0a141e");
14 //! # }
15 //! ```
16 //!
17 
18 use core::{fmt, str, ops::Add, cmp::min};
19 
20 use typenum::*;
21 
22 use crate::{ArrayLength, GenericArray};
23 
24 static LOWER_CHARS: &'static [u8] = b"0123456789abcdef";
25 static UPPER_CHARS: &'static [u8] = b"0123456789ABCDEF";
26 
27 impl<T: ArrayLength<u8>> fmt::LowerHex for GenericArray<u8, T>
28 where
29     T: Add<T>,
30     <T as Add<T>>::Output: ArrayLength<u8>,
31 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result32     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33         let max_digits = f.precision().unwrap_or_else(|| self.len() * 2);
34         let max_hex = (max_digits >> 1) + (max_digits & 1);
35 
36         if T::USIZE < 1024 {
37             // For small arrays use a stack allocated
38             // buffer of 2x number of bytes
39             let mut res = GenericArray::<u8, Sum<T, T>>::default();
40 
41             self.iter().take(max_hex).enumerate().for_each(|(i, c)| {
42                 res[i * 2] = LOWER_CHARS[(c >> 4) as usize];
43                 res[i * 2 + 1] = LOWER_CHARS[(c & 0xF) as usize];
44             });
45 
46             f.write_str(unsafe { str::from_utf8_unchecked(&res[..max_digits]) })?;
47         } else {
48             // For large array use chunks of up to 1024 bytes (2048 hex chars)
49             let mut buf = [0u8; 2048];
50             let mut digits_left = max_digits;
51 
52             for chunk in self[..max_hex].chunks(1024) {
53                 chunk.iter().enumerate().for_each(|(i, c)| {
54                     buf[i * 2] = LOWER_CHARS[(c >> 4) as usize];
55                     buf[i * 2 + 1] = LOWER_CHARS[(c & 0xF) as usize];
56                 });
57 
58                 let n = min(chunk.len() * 2, digits_left);
59                 f.write_str(unsafe { str::from_utf8_unchecked(&buf[..n]) })?;
60                 digits_left -= n;
61             }
62         }
63         Ok(())
64     }
65 }
66 
67 impl<T: ArrayLength<u8>> fmt::UpperHex for GenericArray<u8, T>
68 where
69     T: Add<T>,
70     <T as Add<T>>::Output: ArrayLength<u8>,
71 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result72     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73         let max_digits = f.precision().unwrap_or_else(|| self.len() * 2);
74         let max_hex = (max_digits >> 1) + (max_digits & 1);
75 
76         if T::USIZE < 1024 {
77             // For small arrays use a stack allocated
78             // buffer of 2x number of bytes
79             let mut res = GenericArray::<u8, Sum<T, T>>::default();
80 
81             self.iter().take(max_hex).enumerate().for_each(|(i, c)| {
82                 res[i * 2] = UPPER_CHARS[(c >> 4) as usize];
83                 res[i * 2 + 1] = UPPER_CHARS[(c & 0xF) as usize];
84             });
85 
86             f.write_str(unsafe { str::from_utf8_unchecked(&res[..max_digits]) })?;
87         } else {
88             // For large array use chunks of up to 1024 bytes (2048 hex chars)
89             let mut buf = [0u8; 2048];
90             let mut digits_left = max_digits;
91 
92             for chunk in self[..max_hex].chunks(1024) {
93                 chunk.iter().enumerate().for_each(|(i, c)| {
94                     buf[i * 2] = UPPER_CHARS[(c >> 4) as usize];
95                     buf[i * 2 + 1] = UPPER_CHARS[(c & 0xF) as usize];
96                 });
97 
98                 let n = min(chunk.len() * 2, digits_left);
99                 f.write_str(unsafe { str::from_utf8_unchecked(&buf[..n]) })?;
100                 digits_left -= n;
101             }
102         }
103         Ok(())
104     }
105 }
106