#[cfg(feature = "alloc")]
use alloc::string::String;
use core::{default::Default, fmt};
#[cfg(feature = "alloc")]
pub fn simple_hex<T: AsRef<[u8]>>(source: &T) -> String {
let mut writer = String::new();
hex_write(&mut writer, source, HexConfig::simple()).unwrap_or(());
writer
}
pub fn simple_hex_write<T, W>(writer: &mut W, source: &T) -> fmt::Result
where
T: AsRef<[u8]>,
W: fmt::Write,
{
hex_write(writer, source, HexConfig::simple())
}
#[cfg(feature = "alloc")]
pub fn pretty_hex<T: AsRef<[u8]>>(source: &T) -> String {
let mut writer = String::new();
hex_write(&mut writer, source, HexConfig::default()).unwrap_or(());
writer
}
pub fn pretty_hex_write<T, W>(writer: &mut W, source: &T) -> fmt::Result
where
T: AsRef<[u8]>,
W: fmt::Write,
{
hex_write(writer, source, HexConfig::default())
}
#[cfg(feature = "alloc")]
pub fn config_hex<T: AsRef<[u8]>>(source: &T, cfg: HexConfig) -> String {
let mut writer = String::new();
hex_write(&mut writer, source, cfg).unwrap_or(());
writer
}
#[derive(Clone, Copy, Debug)]
pub struct HexConfig {
pub title: bool,
pub ascii: bool,
pub width: usize,
pub group: usize,
pub chunk: usize,
pub max_bytes: usize,
}
impl Default for HexConfig {
fn default() -> HexConfig {
HexConfig {
title: true,
ascii: true,
width: 16,
group: 4,
chunk: 1,
max_bytes: usize::MAX,
}
}
}
impl HexConfig {
pub fn simple() -> Self {
HexConfig::default().to_simple()
}
fn delimiter(&self, i: usize) -> &'static str {
if i > 0 && self.chunk > 0 && i % self.chunk == 0 {
if self.group > 0 && i % (self.group * self.chunk) == 0 {
" "
} else {
" "
}
} else {
""
}
}
fn to_simple(self) -> Self {
HexConfig {
title: false,
ascii: false,
width: 0,
..self
}
}
}
const NON_ASCII: char = '.';
pub fn hex_write<T, W>(writer: &mut W, source: &T, cfg: HexConfig) -> fmt::Result
where
T: AsRef<[u8]> + ?Sized,
W: fmt::Write,
{
let mut source = source.as_ref();
if cfg.title {
writeln!(writer, "Length: {0} (0x{0:x}) bytes", source.len())?;
}
if source.is_empty() {
return Ok(());
}
let omitted = source.len().checked_sub(cfg.max_bytes);
if omitted.is_some() {
source = &source[..cfg.max_bytes];
}
let lines = source.chunks(if cfg.width > 0 {
cfg.width
} else {
source.len()
});
let lines_len = lines.len();
for (i, row) in lines.enumerate() {
if cfg.width > 0 {
write!(writer, "{:04x}: ", i * cfg.width)?;
}
for (i, x) in row.as_ref().iter().enumerate() {
write!(writer, "{}{:02x}", cfg.delimiter(i), x)?;
}
if cfg.ascii {
for j in row.len()..cfg.width {
write!(writer, "{} ", cfg.delimiter(j))?;
}
write!(writer, " ")?;
for x in row {
if x.is_ascii() && !x.is_ascii_control() {
writer.write_char((*x).into())?;
} else {
writer.write_char(NON_ASCII)?;
}
}
}
if i + 1 < lines_len {
writeln!(writer)?;
}
}
if let Some(o) = omitted {
write!(writer, "\n...{0} (0x{0:x}) bytes not shown...", o)?;
}
Ok(())
}
pub struct Hex<'a, T: 'a + ?Sized>(&'a T, HexConfig);
impl<'a, T: 'a + AsRef<[u8]> + ?Sized> fmt::Display for Hex<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
hex_write(f, self.0, self.1.to_simple())
}
}
impl<'a, T: 'a + AsRef<[u8]> + ?Sized> fmt::Debug for Hex<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
hex_write(f, self.0, self.1)
}
}
pub trait PrettyHex {
fn hex_dump(&self) -> Hex<Self>;
fn hex_conf(&self, cfg: HexConfig) -> Hex<Self>;
}
impl<T> PrettyHex for T
where
T: AsRef<[u8]> + ?Sized,
{
fn hex_dump(&self) -> Hex<Self> {
Hex(self, HexConfig::default())
}
fn hex_conf(&self, cfg: HexConfig) -> Hex<Self> {
Hex(self, cfg)
}
}