| 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 | |