#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::fmt;
#[cfg(not(feature = "alloc"))]
use core::mem::MaybeUninit;
use crate::encode;
wrapper_lite::wrapper!(
#[wrapper(AsRef)]
#[wrapper([const] AsMut)]
#[wrapper(BorrowMut)]
#[wrapper(DerefMut)]
#[wrapper(From)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Display<T> {
value: T,
}
);
impl<T: AsRef<[u8]>> fmt::Debug for Display<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(self, f)
}
}
impl<T: AsRef<[u8]>> fmt::Display for Display<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(self, f)
}
}
impl<T: AsRef<[u8]>> fmt::LowerHex for Display<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write::<false>(f)
}
}
impl<T: AsRef<[u8]>> fmt::UpperHex for Display<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.write::<true>(f)
}
}
impl<T> Display<T>
where
T: AsRef<[u8]>,
{
fn write<const UPPER: bool>(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let src = self.as_inner().as_ref();
if f.alternate() {
f.write_str("0x")?;
}
#[cfg(feature = "alloc")]
let mut buf = Vec::with_capacity(src.len() * 2);
#[cfg(not(feature = "alloc"))]
let mut buf = [MaybeUninit::<u8>::uninit(); 256];
f.write_str(encode::<UPPER>(src, &mut buf).map_err(|_| fmt::Error)?)
}
}
#[cfg(test)]
mod smoking {
use core::fmt::Write;
use core::mem::MaybeUninit;
use core::slice;
use super::*;
struct Buffer {
buf: [MaybeUninit<u8>; 1024],
len: usize,
}
impl Write for Buffer {
fn write_str(&mut self, s: &str) -> fmt::Result {
let bytes = s.as_bytes();
let len = bytes.len();
if self.len + len > self.buf.len() {
return Err(fmt::Error);
}
#[allow(unsafe_code, reason = "XXX")]
unsafe {
self.buf
.as_mut_ptr()
.add(self.len)
.copy_from_nonoverlapping(bytes.as_ptr().cast::<MaybeUninit<u8>>(), len);
}
self.len += len;
Ok(())
}
}
impl Buffer {
const fn new() -> Self {
Self {
buf: [MaybeUninit::uninit(); 1024],
len: 0,
}
}
const fn as_str(&self) -> &str {
#[allow(unsafe_code, reason = "XXX")]
unsafe {
str::from_utf8_unchecked(slice::from_raw_parts(
self.buf.as_ptr().cast::<u8>(),
self.len,
))
}
}
}
#[test]
fn test_display() {
let bytes = &[0xde, 0xad, 0xbe, 0xef];
let displayed = Display::from(bytes);
{
let mut buf = Buffer::new();
write!(&mut buf, "{}", &displayed).unwrap();
assert_eq!(buf.as_str(), "deadbeef");
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:X}", &displayed).unwrap();
assert_eq!(buf.as_str(), "DEADBEEF");
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:#x}", &displayed).unwrap();
assert_eq!(buf.as_str(), "0xdeadbeef");
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:#X}", &displayed).unwrap();
assert_eq!(buf.as_str(), "0xDEADBEEF");
}
}
#[test]
fn test_display_oversized() {
let bytes = [0xAAu8; 129];
{
let case = 127;
let displayed = Display::from(&bytes[..case]);
{
let mut buf = Buffer::new();
write!(&mut buf, "{}", &displayed).unwrap();
assert_eq!(buf.as_str().len(), case * 2);
assert!(buf.as_str().chars().all(|c| c == 'a'));
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:X}", &displayed).unwrap();
assert_eq!(buf.as_str().len(), case * 2);
assert!(buf.as_str().chars().all(|c| c == 'A'));
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:#x}", &displayed).unwrap();
assert_eq!(buf.as_str().len(), case * 2 + 2);
assert!(buf.as_str()[2..].chars().all(|c| c == 'a'));
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:#X}", &displayed).unwrap();
assert_eq!(buf.as_str().len(), case * 2 + 2);
assert!(buf.as_str()[2..].chars().all(|c| c == 'A'));
}
}
{
let case = 128;
let displayed = Display::from(&bytes[..case]);
{
let mut buf = Buffer::new();
write!(&mut buf, "{}", &displayed).unwrap();
assert_eq!(buf.as_str().len(), case * 2);
assert!(buf.as_str().chars().all(|c| c == 'a'));
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:X}", &displayed).unwrap();
assert_eq!(buf.as_str().len(), case * 2);
assert!(buf.as_str().chars().all(|c| c == 'A'));
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:#x}", &displayed).unwrap();
assert_eq!(buf.as_str().len(), case * 2 + 2);
assert!(buf.as_str()[2..].chars().all(|c| c == 'a'));
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:#X}", &displayed).unwrap();
assert_eq!(buf.as_str().len(), case * 2 + 2);
assert!(buf.as_str()[2..].chars().all(|c| c == 'A'));
}
}
#[cfg(feature = "alloc")]
{
let case = 129;
let displayed = Display::from(&bytes[..case]);
{
let mut buf = Buffer::new();
write!(&mut buf, "{}", &displayed).unwrap();
assert_eq!(buf.as_str().len(), case * 2);
assert!(buf.as_str().chars().all(|c| c == 'a'));
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:X}", &displayed).unwrap();
assert_eq!(buf.as_str().len(), case * 2);
assert!(buf.as_str().chars().all(|c| c == 'A'));
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:#x}", &displayed).unwrap();
assert_eq!(buf.as_str().len(), case * 2 + 2);
assert!(buf.as_str()[2..].chars().all(|c| c == 'a'));
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:#X}", &displayed).unwrap();
assert_eq!(buf.as_str().len(), case * 2 + 2);
assert!(buf.as_str()[2..].chars().all(|c| c == 'A'));
}
}
#[cfg(not(feature = "alloc"))]
{
let case = 129;
let displayed = Display::from(&bytes[..case]);
{
let mut buf = Buffer::new();
write!(&mut buf, "{}", &displayed).unwrap_err();
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:X}", &displayed).unwrap_err();
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:#x}", &displayed).unwrap_err();
}
{
let mut buf = Buffer::new();
write!(&mut buf, "{:#X}", &displayed).unwrap_err();
}
}
}
#[test]
#[cfg(feature = "alloc")]
fn test_display_alloc() {
use alloc::format;
let bytes = &[0xde, 0xad, 0xbe, 0xef];
let displayed = Display::from(bytes);
assert_eq!(format!("{}", &displayed), "deadbeef");
assert_eq!(format!("{:X}", &displayed), "DEADBEEF");
assert_eq!(format!("{:#x}", &displayed), "0xdeadbeef");
assert_eq!(format!("{:#X}", &displayed), "0xDEADBEEF");
}
}