#![cfg_attr(not(test), no_std)]
use core::fmt::{Alignment, Debug, Display, Formatter, LowerHex, Result, UpperHex, Write};
const ELLIPSIS: &str = "..";
pub struct HexFmt<T>(pub T);
impl<T: AsRef<[u8]>> Debug for HexFmt<T> {
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result {
LowerHex::fmt(self, f)
}
}
impl<T: AsRef<[u8]>> Display for HexFmt<T> {
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result {
LowerHex::fmt(self, f)
}
}
impl<T: AsRef<[u8]>> LowerHex for HexFmt<T> {
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result {
Lowercase::fmt(self.0.as_ref(), f)
}
}
impl<T: AsRef<[u8]>> UpperHex for HexFmt<T> {
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result {
Uppercase::fmt(self.0.as_ref(), f)
}
}
pub struct HexList<T>(pub T);
impl<T> Debug for HexList<T>
where
T: Clone + IntoIterator,
T::Item: AsRef<[u8]>,
{
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result {
LowerHex::fmt(self, f)
}
}
impl<T> Display for HexList<T>
where
T: Clone + IntoIterator,
T::Item: AsRef<[u8]>,
{
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result {
LowerHex::fmt(self, f)
}
}
impl<T> LowerHex for HexList<T>
where
T: Clone + IntoIterator,
T::Item: AsRef<[u8]>,
{
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result {
let entries = self.0.clone().into_iter().map(HexFmt);
f.debug_list().entries(entries).finish()
}
}
impl<T> UpperHex for HexList<T>
where
T: Clone + IntoIterator,
T::Item: AsRef<[u8]>,
{
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result {
let mut iter = self.0.clone().into_iter();
write!(f, "[")?;
if let Some(item) = iter.next() {
UpperHex::fmt(&HexFmt(item), f)?;
}
for item in iter {
write!(f, ", ")?;
UpperHex::fmt(&HexFmt(item), f)?;
}
write!(f, "]")
}
}
trait Case {
fn fmt_byte(f: &mut Formatter, byte: u8) -> Result;
fn fmt_digit(f: &mut Formatter, digit: u8) -> Result;
#[inline]
fn fmt(bytes: &[u8], f: &mut Formatter) -> Result {
let min_width = f.width().unwrap_or(0);
let max_width = f
.precision()
.or_else(|| f.width())
.unwrap_or_else(usize::max_value);
let align = f.align().unwrap_or(Alignment::Center);
if 2 * bytes.len() <= max_width {
let fill = f.fill();
let missing = min_width.saturating_sub(2 * bytes.len());
let (left, right) = match align {
Alignment::Left => (0, missing),
Alignment::Right => (missing, 0),
Alignment::Center => (missing / 2, missing - missing / 2),
};
for _ in 0..left {
f.write_char(fill)?;
}
for byte in bytes {
Self::fmt_byte(f, *byte)?;
}
for _ in 0..right {
f.write_char(fill)?;
}
return Ok(());
}
if max_width <= ELLIPSIS.len() {
return write!(f, "{:.*}", max_width, ELLIPSIS);
}
let digits = max_width.saturating_sub(ELLIPSIS.len());
let (left, right) = match align {
Alignment::Left => (digits, 0),
Alignment::Right => (0, digits),
Alignment::Center => (digits - digits / 2, digits / 2),
};
for byte in &bytes[..(left / 2)] {
Self::fmt_byte(f, *byte)?;
}
if left & 1 == 1 {
Self::fmt_digit(f, bytes[left / 2] >> 4)?;
}
f.write_str(ELLIPSIS)?;
if right & 1 == 1 {
Self::fmt_digit(f, bytes[(bytes.len() - right / 2 - 1)] & 0x0f)?;
}
for byte in &bytes[(bytes.len() - right / 2)..] {
Self::fmt_byte(f, *byte)?;
}
Ok(())
}
}
#[derive(Clone, Copy)]
struct Uppercase;
impl Case for Uppercase {
#[inline]
fn fmt_byte(f: &mut Formatter, byte: u8) -> Result {
write!(f, "{:02X}", byte)
}
#[inline]
fn fmt_digit(f: &mut Formatter, digit: u8) -> Result {
write!(f, "{:1X}", digit)
}
}
#[derive(Clone, Copy)]
struct Lowercase;
impl Case for Lowercase {
#[inline]
fn fmt_byte(f: &mut Formatter, byte: u8) -> Result {
write!(f, "{:02x}", byte)
}
#[inline]
fn fmt_digit(f: &mut Formatter, digit: u8) -> Result {
write!(f, "{:1x}", digit)
}
}
#[cfg(test)]
mod tests {
use super::HexFmt;
#[test]
fn test_fmt() {
assert_eq!("", &format!("{:.0}", HexFmt(&[0x01])));
assert_eq!(".", &format!("{:.1}", HexFmt(&[0x01])));
assert_eq!("01", &format!("{:.2}", HexFmt(&[0x01])));
assert_eq!("..", &format!("{:.2}", HexFmt(&[0x01, 0x23])));
}
}