use crate::encode::encode;
use core::fmt::{self, Alignment, Write};
use core::mem::MaybeUninit;
#[derive(Clone, Copy, Debug)]
#[must_use]
pub struct Hex<'a> {
bytes: &'a [u8],
}
impl<'a> Hex<'a> {
pub const fn new(bytes: &'a [u8]) -> Self {
Self { bytes }
}
fn format(&self, f: &mut fmt::Formatter<'_>, upper: bool) -> fmt::Result {
if let Some(width) = f.width() {
return self.format_padded(f, upper, width);
}
if f.sign_plus() {
f.write_str("+")?;
}
if f.alternate() {
f.write_str("0x")?;
}
self.write_hex(f, upper)
}
#[inline(never)]
fn format_padded(&self, f: &mut fmt::Formatter<'_>, upper: bool, width: usize) -> fmt::Result {
let prefix = if f.alternate() { "0x" } else { "" };
let sign = if f.sign_plus() { "+" } else { "" };
let len = self
.bytes
.len()
.saturating_mul(2)
.saturating_add(prefix.len())
.saturating_add(sign.len());
let padding = width.saturating_sub(len);
let zero_pad = f.sign_aware_zero_pad();
let (left, right) = if zero_pad {
(0, 0)
} else {
match f.align().unwrap_or(Alignment::Right) {
Alignment::Left => (0, padding),
Alignment::Right => (padding, 0),
Alignment::Center => (padding / 2, padding - padding / 2),
}
};
let fill = f.fill();
write_fill(f, fill, left)?;
if !sign.is_empty() {
f.write_str(sign)?;
}
if !prefix.is_empty() {
f.write_str(prefix)?;
}
if zero_pad {
write_fill(f, '0', padding)?;
}
self.write_hex(f, upper)?;
write_fill(f, fill, right)
}
#[inline]
fn write_hex(&self, f: &mut fmt::Formatter<'_>, upper: bool) -> fmt::Result {
if self.bytes.is_empty() {
return Ok(());
}
if self.bytes.len() <= 128 {
let mut buffer = [MaybeUninit::uninit(); 256];
let text = encode(self.bytes, &mut buffer, upper)
.expect("short input fits the fixed encoding buffer");
return f.write_str(text);
}
self.write_long(f, upper)
}
#[inline(never)]
fn write_long(&self, f: &mut fmt::Formatter<'_>, upper: bool) -> fmt::Result {
let mut buffer = [MaybeUninit::uninit(); 1024];
for chunk in self.bytes.chunks(buffer.len() / 2) {
let text = encode(chunk, &mut buffer, upper)
.expect("each chunk fits the fixed encoding buffer");
f.write_str(text)?;
}
Ok(())
}
}
fn write_fill(f: &mut fmt::Formatter<'_>, fill: char, mut count: usize) -> fmt::Result {
let block = match fill {
' ' => " ",
'0' => "00000000000000000000000000000000",
_ => {
for _ in 0..count {
f.write_char(fill)?;
}
return Ok(());
}
};
while count >= block.len() {
f.write_str(block)?;
count -= block.len();
}
if count != 0 {
f.write_str(&block[..count])?;
}
Ok(())
}
impl fmt::Display for Hex<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.format(f, false)
}
}
impl fmt::LowerHex for Hex<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.format(f, false)
}
}
impl fmt::UpperHex for Hex<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.format(f, true)
}
}