use core::borrow::Borrow;
use core::fmt;
use super::Case;
use crate::buf_encoder::{BufEncoder, FixedLenBuf, OutBytes};
#[cfg(feature = "alloc")]
use crate::prelude::*;
pub trait DisplayHex: Copy + sealed::IsRef {
type Display: fmt::Display + fmt::Debug + fmt::LowerHex + fmt::UpperHex;
fn as_hex(self) -> Self::Display;
#[cfg(feature = "alloc")]
fn to_lower_hex_string(self) -> String { self.to_hex_string(Case::Lower) }
#[cfg(feature = "alloc")]
fn to_upper_hex_string(self) -> String { self.to_hex_string(Case::Upper) }
#[cfg(feature = "alloc")]
fn to_hex_string(self, case: Case) -> String {
let mut string = String::new();
self.append_hex_to_string(case, &mut string);
string
}
#[cfg(feature = "alloc")]
fn append_hex_to_string(self, case: Case, string: &mut String) {
use fmt::Write;
string.reserve(self.hex_reserve_suggestion());
match case {
Case::Lower => write!(string, "{:x}", self.as_hex()),
Case::Upper => write!(string, "{:X}", self.as_hex()),
}
.unwrap_or_else(|_| {
let name = core::any::type_name::<Self::Display>();
panic!("The implementation of Display for {} returned an error when it shouldn't", name)
})
}
fn hex_reserve_suggestion(self) -> usize { 0 }
}
mod sealed {
pub trait IsRef: Copy {}
impl<T: ?Sized> IsRef for &'_ T {}
}
impl<'a> DisplayHex for &'a [u8] {
type Display = DisplayByteSlice<'a>;
#[inline]
fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } }
#[inline]
fn hex_reserve_suggestion(self) -> usize {
self.len().checked_mul(2).expect("the string wouldn't fit into address space")
}
}
#[cfg(feature = "alloc")]
impl<'a> DisplayHex for &'a alloc::vec::Vec<u8> {
type Display = DisplayByteSlice<'a>;
#[inline]
fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } }
#[inline]
fn hex_reserve_suggestion(self) -> usize {
self.len().checked_mul(2).expect("the string wouldn't fit into address space")
}
}
pub struct DisplayByteSlice<'a> {
pub(crate) bytes: &'a [u8],
}
impl<'a> DisplayByteSlice<'a> {
fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
let mut buf = [0u8; 1024];
let mut encoder = BufEncoder::new(&mut buf);
if self.bytes.len() < 512 {
encoder.put_bytes(self.bytes, case);
return f.pad(encoder.as_str());
}
let mut chunks = self.bytes.chunks_exact(512);
for chunk in &mut chunks {
encoder.put_bytes(chunk, case);
f.write_str(encoder.as_str())?;
encoder.clear();
}
encoder.put_bytes(chunks.remainder(), case);
f.write_str(encoder.as_str())
}
}
impl<'a> fmt::Display for DisplayByteSlice<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
}
impl<'a> fmt::Debug for DisplayByteSlice<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
}
impl<'a> fmt::LowerHex for DisplayByteSlice<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
}
impl<'a> fmt::UpperHex for DisplayByteSlice<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
}
pub struct DisplayArray<A: Clone + IntoIterator, B: FixedLenBuf>
where
A::Item: Borrow<u8>,
{
array: A,
_buffer_marker: core::marker::PhantomData<B>,
}
impl<A: Clone + IntoIterator, B: FixedLenBuf> DisplayArray<A, B>
where
A::Item: Borrow<u8>,
{
pub fn new(array: A) -> Self { DisplayArray { array, _buffer_marker: Default::default() } }
fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
let mut buf = B::uninit();
let mut encoder = BufEncoder::new(&mut buf);
encoder.put_bytes(self.array.clone(), case);
f.pad_integral(true, "0x", encoder.as_str())
}
}
impl<A: Clone + IntoIterator, B: FixedLenBuf> fmt::Display for DisplayArray<A, B>
where
A::Item: Borrow<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
}
impl<A: Clone + IntoIterator, B: FixedLenBuf> fmt::Debug for DisplayArray<A, B>
where
A::Item: Borrow<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
}
impl<A: Clone + IntoIterator, B: FixedLenBuf> fmt::LowerHex for DisplayArray<A, B>
where
A::Item: Borrow<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
}
impl<A: Clone + IntoIterator, B: FixedLenBuf> fmt::UpperHex for DisplayArray<A, B>
where
A::Item: Borrow<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
}
#[macro_export]
macro_rules! fmt_hex_exact {
($formatter:expr, $len:expr, $bytes:expr, $case:expr) => {{
#[allow(deprecated)]
const _: () = [()][($len > usize::MAX / 2) as usize];
assert_eq!($bytes.len(), $len);
let mut buf = [0u8; $len * 2];
let buf = $crate::buf_encoder::AsOutBytes::as_mut_out_bytes(&mut buf);
$crate::display::fmt_hex_exact_fn($formatter, buf, $bytes, $case)
}};
}
pub use fmt_hex_exact;
#[doc(hidden)]
#[inline]
pub fn fmt_hex_exact_fn<I>(
f: &mut fmt::Formatter,
buf: &mut OutBytes,
bytes: I,
case: Case,
) -> fmt::Result
where
I: IntoIterator,
I::Item: Borrow<u8>,
{
let mut encoder = BufEncoder::new(buf);
encoder.put_bytes(bytes, case);
f.pad_integral(true, "0x", encoder.as_str())
}
#[cfg(test)]
mod tests {
#[cfg(feature = "alloc")]
use super::*;
#[cfg(feature = "alloc")]
mod alloc {
use super::*;
fn check_encoding(bytes: &[u8]) {
use core::fmt::Write;
let s1 = bytes.to_lower_hex_string();
let mut s2 = String::with_capacity(bytes.len() * 2);
for b in bytes {
write!(s2, "{:02x}", b).unwrap();
}
assert_eq!(s1, s2);
}
#[test]
fn empty() { check_encoding(b""); }
#[test]
fn single() { check_encoding(b"*"); }
#[test]
fn two() { check_encoding(b"*x"); }
#[test]
fn just_below_boundary() { check_encoding(&[42; 512]); }
#[test]
fn just_above_boundary() { check_encoding(&[42; 513]); }
#[test]
fn just_above_double_boundary() { check_encoding(&[42; 1025]); }
#[test]
fn fmt_exact_macro() {
use crate::alloc::string::ToString;
struct Dummy([u8; 32]);
impl fmt::Display for Dummy {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_hex_exact!(f, 32, &self.0, Case::Lower)
}
}
assert_eq!(Dummy([42; 32]).to_string(), "2a".repeat(32));
}
#[test]
fn display_short_with_padding() {
let v = vec![0xbe, 0xef];
assert_eq!(format!("Hello {:<8}!", v.as_hex()), "Hello beef !");
assert_eq!(format!("Hello {:-<8}!", v.as_hex()), "Hello beef----!");
assert_eq!(format!("Hello {:^8}!", v.as_hex()), "Hello beef !");
assert_eq!(format!("Hello {:>8}!", v.as_hex()), "Hello beef!");
}
#[test]
fn display_long_no_padding() {
let x = 1;
let want = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001";
let got = format!("{:0>2000}", x);
assert_eq!(got, want);
let v = vec![0xab; 512];
let want = "abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab";
let got = format!("{:0>2000}", v.as_hex());
assert_eq!(got, want)
}
}
}