#![no_std]
#![doc = include_str!("../README.md")]
use core::fmt::{Debug, Display, LowerHex, UpperHex};
#[cfg(any(feature = "std", test))]
extern crate std;
#[cfg(feature = "alloc")]
extern crate alloc;
pub trait HexDisplayExt {
fn hex(&self) -> Hex<'_>;
#[cfg(feature = "alloc")]
fn upper_hex_string(&self) -> alloc::string::String {
alloc::format!("{:X}", self.hex())
}
#[cfg(feature = "alloc")]
fn hex_string(&self) -> alloc::string::String {
use alloc::string::ToString;
self.hex().to_string()
}
}
impl HexDisplayExt for [u8] {
fn hex(&self) -> Hex<'_> {
Hex(self)
}
}
impl<const N: usize> HexDisplayExt for [u8; N] {
fn hex(&self) -> Hex<'_> {
Hex(self)
}
}
pub struct Hex<'a>(
pub &'a [u8],
);
const LOWER_HEX: &[u8; 16] = b"0123456789abcdef";
const UPPER_HEX: &[u8; 16] = b"0123456789ABCDEF";
fn write_hex(
f: &mut core::fmt::Formatter<'_>,
bytes: &[u8],
table: &[u8; 16],
) -> core::fmt::Result {
const CHUNK: usize = 32;
let mut buf = [0u8; CHUNK * 2];
for chunk in bytes.chunks(CHUNK) {
for (i, &byte) in chunk.iter().enumerate() {
buf[i * 2] = table[(byte >> 4) as usize];
buf[i * 2 + 1] = table[(byte & 0x0f) as usize];
}
let s = core::str::from_utf8(&buf[..chunk.len() * 2])
.expect("hex lookup table only emits ASCII");
f.write_str(s)?;
}
Ok(())
}
impl UpperHex for Hex<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write_hex(f, self.0, UPPER_HEX)
}
}
impl LowerHex for Hex<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write_hex(f, self.0, LOWER_HEX)
}
}
impl Debug for Hex<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write_hex(f, self.0, LOWER_HEX)
}
}
impl Display for Hex<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write_hex(f, self.0, LOWER_HEX)
}
}
#[cfg(test)]
mod tests {
use std::format;
use super::*;
#[test]
fn test_all_bytes() {
for byte in 0..=0xff {
assert_eq!(format!("{:02x}", byte), format!("{}", Hex(&[byte])));
assert_eq!(format!("{:02X}", byte), format!("{:X}", Hex(&[byte])));
}
}
#[test]
fn test_all_byte_pairs() {
for (a, b) in (0..=0xff).zip(0..=0xff) {
assert_eq!(format!("{:02x}{:02x}", a, b), format!("{}", Hex(&[a, b])));
assert_eq!(format!("{:02X}{:02X}", a, b), format!("{:X}", Hex(&[a, b])));
}
}
}