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 | {
|
32 | 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 | {
|
72 | 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 | |