use core::fmt::{self, Write};
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct HexSlice([u8]);
impl HexSlice {
#[inline]
#[must_use]
pub const fn new(bytes: &[u8]) -> &Self {
unsafe { &*(bytes as *const [u8] as *const Self) }
}
#[inline]
#[must_use]
pub const fn as_bytes(&self) -> &[u8] {
&self.0
}
}
pub(crate) struct HexDisplay<'a>(pub(crate) &'a [u8]);
impl fmt::Display for HexDisplay<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in self.0 {
write!(f, "{byte:02x}")?;
}
Ok(())
}
}
impl fmt::Debug for HexDisplay<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
fn fmt_hex_padded(
bytes: &[u8],
f: &mut fmt::Formatter<'_>,
write_byte: fn(&mut fmt::Formatter<'_>, u8) -> fmt::Result,
) -> fmt::Result {
let content_len = bytes.len() * 2;
match f.width() {
Some(width) if width > content_len => {
let padding = width - content_len;
let fill = f.fill();
let (pre, post) = match f.align() {
Some(fmt::Alignment::Left) => (0, padding),
Some(fmt::Alignment::Right) | None => (padding, 0),
Some(fmt::Alignment::Center) => {
(padding / 2, padding - padding / 2)
}
};
for _ in 0..pre {
f.write_char(fill)?;
}
for &byte in bytes {
write_byte(f, byte)?;
}
for _ in 0..post {
f.write_char(fill)?;
}
Ok(())
}
Some(_) | None => {
for &byte in bytes {
write_byte(f, byte)?;
}
Ok(())
}
}
}
impl fmt::Debug for HexSlice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("HexSlice").field(&HexDisplay(&self.0)).finish()
}
}
impl fmt::Display for HexSlice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(self, f)
}
}
impl fmt::LowerHex for HexSlice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_hex_padded(&self.0, f, |f, b| write!(f, "{b:02x}"))
}
}
impl fmt::UpperHex for HexSlice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_hex_padded(&self.0, f, |f, b| write!(f, "{b:02X}"))
}
}
impl core::ops::Deref for HexSlice {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<[u8]> for HexSlice {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsRef<HexSlice> for HexSlice {
#[inline]
fn as_ref(&self) -> &HexSlice {
self
}
}
#[cfg(feature = "serde")]
mod serde_impls {
use super::{HexDisplay, HexSlice};
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
impl serde_core::Serialize for HexSlice {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde_core::Serializer,
{
if serializer.is_human_readable() {
serializer.collect_str(&HexDisplay(&self.0))
} else {
serializer.serialize_bytes(&self.0)
}
}
}
}