#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::string::String;
use core::borrow::Borrow;
use core::fmt;
use super::{Case, Table};
use crate::buf_encoder::BufEncoder;
pub trait DisplayHex: Copy + sealed::IsRef {
type Display: fmt::Display + fmt::Debug + fmt::LowerHex + fmt::UpperHex;
fn as_hex(self) -> Self::Display;
#[cfg(feature = "alloc")]
fn to_lower_hex_string(self) -> String { self.to_hex_string(Case::Lower) }
#[cfg(feature = "alloc")]
fn to_upper_hex_string(self) -> String { self.to_hex_string(Case::Upper) }
#[cfg(feature = "alloc")]
fn to_hex_string(self, case: Case) -> String {
let mut string = String::new();
self.append_hex_to_string(case, &mut string);
string
}
#[cfg(feature = "alloc")]
fn append_hex_to_string(self, case: Case, string: &mut String) {
use fmt::Write;
string.reserve(self.hex_reserve_suggestion());
match case {
Case::Lower => write!(string, "{:x}", self.as_hex()),
Case::Upper => write!(string, "{:X}", self.as_hex()),
}
.unwrap_or_else(|_| {
let name = core::any::type_name::<Self::Display>();
panic!("The implementation of Display for {} returned an error when it shouldn't", name)
})
}
fn hex_reserve_suggestion(self) -> usize { 0 }
}
fn internal_display(bytes: &[u8], f: &mut fmt::Formatter, case: Case) -> fmt::Result {
use fmt::Write;
let mut encoder = BufEncoder::<1024>::new(case);
let pad_right = if let Some(width) = f.width() {
let string_len = match f.precision() {
Some(max) => core::cmp::min(max, bytes.len() * 2),
None => bytes.len() * 2,
};
if string_len < width {
let (left, right) = match f.align().unwrap_or(fmt::Alignment::Left) {
fmt::Alignment::Left => (0, width - string_len),
fmt::Alignment::Right => (width - string_len, 0),
fmt::Alignment::Center => ((width - string_len) / 2, (width - string_len + 1) / 2),
};
if left > 0 {
let c = f.fill();
let chunk_len = encoder.put_filler(c, left);
let padding = encoder.as_str();
for _ in 0..(left / chunk_len) {
f.write_str(padding)?;
}
f.write_str(&padding[..((left % chunk_len) * c.len_utf8())])?;
encoder.clear();
}
right
} else {
0
}
} else {
0
};
match f.precision() {
Some(max) if bytes.len() > max / 2 => {
write!(f, "{}", bytes[..(max / 2)].as_hex())?;
if max % 2 == 1 {
f.write_char(case.table().byte_to_chars(bytes[max / 2])[0])?;
}
}
Some(_) | None => {
let mut chunks = bytes.chunks_exact(512);
for chunk in &mut chunks {
encoder.put_bytes(chunk);
f.write_str(encoder.as_str())?;
encoder.clear();
}
encoder.put_bytes(chunks.remainder());
f.write_str(encoder.as_str())?;
}
}
if pad_right > 0 {
encoder.clear();
let c = f.fill();
let chunk_len = encoder.put_filler(c, pad_right);
let padding = encoder.as_str();
for _ in 0..(pad_right / chunk_len) {
f.write_str(padding)?;
}
f.write_str(&padding[..((pad_right % chunk_len) * c.len_utf8())])?;
}
Ok(())
}
mod sealed {
pub trait IsRef: Copy {}
impl<T: ?Sized> IsRef for &'_ T {}
}
impl<'a> DisplayHex for &'a [u8] {
type Display = DisplayByteSlice<'a>;
#[inline]
fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } }
#[inline]
fn hex_reserve_suggestion(self) -> usize {
self.len().checked_mul(2).expect("the string wouldn't fit into address space")
}
}
#[cfg(feature = "alloc")]
impl<'a> DisplayHex for &'a alloc::vec::Vec<u8> {
type Display = DisplayByteSlice<'a>;
#[inline]
fn as_hex(self) -> Self::Display { DisplayByteSlice { bytes: self } }
#[inline]
fn hex_reserve_suggestion(self) -> usize {
self.len().checked_mul(2).expect("the string wouldn't fit into address space")
}
}
pub struct DisplayByteSlice<'a> {
pub(crate) bytes: &'a [u8],
}
impl<'a> DisplayByteSlice<'a> {
fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
internal_display(self.bytes, f, case)
}
}
impl<'a> fmt::Display for DisplayByteSlice<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
}
impl<'a> fmt::Debug for DisplayByteSlice<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
}
impl<'a> fmt::LowerHex for DisplayByteSlice<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
}
impl<'a> fmt::UpperHex for DisplayByteSlice<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
}
pub struct DisplayArray<'a, const CAP: usize> {
array: &'a [u8],
}
impl<'a, const CAP: usize> DisplayArray<'a, CAP> {
#[inline]
fn new(array: &'a [u8]) -> Self {
assert!(array.len() <= CAP / 2);
DisplayArray { array }
}
fn display(&self, f: &mut fmt::Formatter, case: Case) -> fmt::Result {
internal_display(self.array, f, case)
}
}
impl<'a, const LEN: usize> fmt::Display for DisplayArray<'a, LEN> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
}
impl<'a, const LEN: usize> fmt::Debug for DisplayArray<'a, LEN> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
}
impl<'a, const LEN: usize> fmt::LowerHex for DisplayArray<'a, LEN> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Lower) }
}
impl<'a, const LEN: usize> fmt::UpperHex for DisplayArray<'a, LEN> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.display(f, Case::Upper) }
}
macro_rules! impl_array_as_hex {
($($len:expr),*) => {
$(
impl<'a> DisplayHex for &'a [u8; $len] {
type Display = DisplayArray<'a, {$len * 2}>;
fn as_hex(self) -> Self::Display {
DisplayArray::new(self)
}
}
)*
}
}
impl_array_as_hex!(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 32, 33, 64, 65, 128, 256, 512, 1024,
2048, 4096
);
#[macro_export]
macro_rules! fmt_hex_exact {
($formatter:expr, $len:expr, $bytes:expr, $case:expr) => {{
#[allow(deprecated)]
const _: () = [()][($len > usize::MAX / 2) as usize];
assert_eq!($bytes.len(), $len);
$crate::display::fmt_hex_exact_fn::<_, { $len * 2 }>($formatter, $bytes, $case)
}};
}
pub use fmt_hex_exact;
#[macro_export]
macro_rules! impl_fmt_traits {
(impl fmt_traits for $ty:ident { const LENGTH: usize = $len:expr; }) => {
$crate::impl_fmt_traits! {
#[display_backward(false)]
impl<> fmt_traits for $ty<> {
const LENGTH: usize = $len;
}
}
};
(#[display_backward($reverse:expr)] impl fmt_traits for $ty:ident { const LENGTH: usize = $len:expr; }) => {
$crate::impl_fmt_traits! {
#[display_backward($reverse)]
impl<> fmt_traits for $ty<> {
const LENGTH: usize = $len;
}
}
};
(impl<$($gen:ident: $gent:ident),*> fmt_traits for $ty:ident<$($unused:ident),*> { const LENGTH: usize = $len:expr; }) => {
$crate::impl_fmt_traits! {
#[display_backward(false)]
impl<$($gen: $gent),*> fmt_traits for $ty<$($unused),*> {
const LENGTH: usize = $len;
}
}
};
(#[display_backward($reverse:expr)] impl<$($gen:ident: $gent:ident),*> fmt_traits for $ty:ident<$($unused:ident),*> { const LENGTH: usize = $len:expr; }) => {
impl<$($gen: $gent),*> $crate::_export::_core::fmt::LowerHex for $ty<$($gen),*> {
#[inline]
fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
let case = $crate::Case::Lower;
if $reverse {
let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter().rev();
$crate::fmt_hex_exact!(f, $len, bytes, case)
} else {
let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter();
$crate::fmt_hex_exact!(f, $len, bytes, case)
}
}
}
impl<$($gen: $gent),*> $crate::_export::_core::fmt::UpperHex for $ty<$($gen),*> {
#[inline]
fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
let case = $crate::Case::Upper;
if $reverse {
let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter().rev();
$crate::fmt_hex_exact!(f, $len, bytes, case)
} else {
let bytes = $crate::_export::_core::borrow::Borrow::<[u8]>::borrow(self).iter();
$crate::fmt_hex_exact!(f, $len, bytes, case)
}
}
}
impl<$($gen: $gent),*> $crate::_export::_core::fmt::Display for $ty<$($gen),*> {
#[inline]
fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
$crate::_export::_core::fmt::LowerHex::fmt(self, f)
}
}
impl<$($gen: $gent),*> $crate::_export::_core::fmt::Debug for $ty<$($gen),*> {
#[inline]
fn fmt(&self, f: &mut $crate::_export::_core::fmt::Formatter) -> $crate::_export::_core::fmt::Result {
$crate::_export::_core::fmt::LowerHex::fmt(&self, f)
}
}
};
}
pub use impl_fmt_traits;
#[doc(hidden)]
#[inline]
pub fn fmt_hex_exact_fn<I, const N: usize>(
f: &mut fmt::Formatter,
bytes: I,
case: Case,
) -> fmt::Result
where
I: IntoIterator,
I::Item: Borrow<u8>,
{
let mut encoder = BufEncoder::<N>::new(case);
let encoded = match f.precision() {
Some(p) if p < N => {
let n = (p + 1) / 2;
encoder.put_bytes(bytes.into_iter().take(n));
&encoder.as_str()[..p]
}
_ => {
encoder.put_bytes(bytes);
encoder.as_str()
}
};
f.pad_integral(true, "0x", encoded)
}
#[cfg(any(test, feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(any(test, feature = "std"))))]
pub struct HexWriter<T> {
writer: T,
table: &'static Table,
}
#[cfg(any(test, feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(any(test, feature = "std"))))]
impl<T> HexWriter<T> {
pub fn new(dest: T, case: Case) -> Self { Self { writer: dest, table: case.table() } }
pub fn into_inner(self) -> T { self.writer }
}
#[cfg(any(test, feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(any(test, feature = "std"))))]
impl<T> std::io::Write for HexWriter<T>
where
T: core::fmt::Write,
{
fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
let mut n = 0;
for byte in buf {
let mut hex_chars = [0u8; 2];
let hex_str = self.table.byte_to_str(&mut hex_chars, *byte);
if self.writer.write_str(hex_str).is_err() {
break;
}
n += 1;
}
if n == 0 && !buf.is_empty() {
Err(std::io::ErrorKind::Other.into())
} else {
Ok(n)
}
}
fn flush(&mut self) -> Result<(), std::io::Error> { Ok(()) }
}
#[cfg(test)]
mod tests {
#[cfg(feature = "alloc")]
use super::*;
#[cfg(feature = "alloc")]
mod alloc {
use core::marker::PhantomData;
use super::*;
fn check_encoding(bytes: &[u8]) {
use core::fmt::Write;
let s1 = bytes.to_lower_hex_string();
let mut s2 = String::with_capacity(bytes.len() * 2);
for b in bytes {
write!(s2, "{:02x}", b).unwrap();
}
assert_eq!(s1, s2);
}
#[test]
fn empty() { check_encoding(b""); }
#[test]
fn single() { check_encoding(b"*"); }
#[test]
fn two() { check_encoding(b"*x"); }
#[test]
fn just_below_boundary() { check_encoding(&[42; 512]); }
#[test]
fn just_above_boundary() { check_encoding(&[42; 513]); }
#[test]
fn just_above_double_boundary() { check_encoding(&[42; 1025]); }
#[test]
fn fmt_exact_macro() {
use crate::alloc::string::ToString;
struct Dummy([u8; 32]);
impl fmt::Display for Dummy {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_hex_exact!(f, 32, &self.0, Case::Lower)
}
}
let dummy = Dummy([42; 32]);
assert_eq!(dummy.to_string(), "2a".repeat(32));
assert_eq!(format!("{:.10}", dummy), "2a".repeat(5));
assert_eq!(format!("{:.11}", dummy), "2a".repeat(5) + "2");
assert_eq!(format!("{:.65}", dummy), "2a".repeat(32));
}
macro_rules! test_display_hex {
($fs: expr, $a: expr, $check: expr) => {
let to_display_array = $a;
let to_display_byte_slice = Vec::from($a);
assert_eq!(format!($fs, to_display_array.as_hex()), $check);
assert_eq!(format!($fs, to_display_byte_slice.as_hex()), $check);
};
}
#[test]
fn display_short_with_padding() {
test_display_hex!("Hello {:<8}!", [0xbe, 0xef], "Hello beef !");
test_display_hex!("Hello {:-<8}!", [0xbe, 0xef], "Hello beef----!");
test_display_hex!("Hello {:^8}!", [0xbe, 0xef], "Hello beef !");
test_display_hex!("Hello {:>8}!", [0xbe, 0xef], "Hello beef!");
}
#[test]
fn display_long() {
let a = [0xab; 512];
let mut want = "0".repeat(2000 - 1024);
want.extend(core::iter::repeat("ab").take(512));
test_display_hex!("{:0>2000}", a, want);
}
#[test]
fn precision_truncates() {
test_display_hex!("{0:.4}", [0x12, 0x34, 0x56, 0x78], "1234");
test_display_hex!("{0:.5}", [0x12, 0x34, 0x56, 0x78], "12345");
}
#[test]
fn precision_with_padding_truncates() {
test_display_hex!("{0:10.4}", [0x12, 0x34, 0x56, 0x78], "1234 ");
test_display_hex!("{0:10.5}", [0x12, 0x34, 0x56, 0x78], "12345 ");
}
#[test]
fn precision_with_padding_pads_right() {
test_display_hex!("{0:10.20}", [0x12, 0x34, 0x56, 0x78], "12345678 ");
test_display_hex!("{0:10.14}", [0x12, 0x34, 0x56, 0x78], "12345678 ");
}
#[test]
fn precision_with_padding_pads_left() {
test_display_hex!("{0:>10.20}", [0x12, 0x34, 0x56, 0x78], " 12345678");
}
#[test]
fn precision_with_padding_pads_center() {
test_display_hex!("{0:^10.20}", [0x12, 0x34, 0x56, 0x78], " 12345678 ");
}
#[test]
fn precision_with_padding_pads_center_odd() {
test_display_hex!("{0:^11.20}", [0x12, 0x34, 0x56, 0x78], " 12345678 ");
}
#[test]
fn precision_does_not_extend() {
test_display_hex!("{0:.16}", [0x12, 0x34, 0x56, 0x78], "12345678");
}
#[test]
fn padding_extends() {
test_display_hex!("{:0>8}", [0xab; 2], "0000abab");
}
#[test]
fn padding_does_not_truncate() {
test_display_hex!("{:0>4}", [0x12, 0x34, 0x56, 0x78], "12345678");
}
#[test]
fn hex_fmt_impl_macro_forward() {
struct Wrapper([u8; 4]);
impl Borrow<[u8]> for Wrapper {
fn borrow(&self) -> &[u8] { &self.0[..] }
}
impl_fmt_traits! {
#[display_backward(false)]
impl fmt_traits for Wrapper {
const LENGTH: usize = 4;
}
}
let tc = Wrapper([0x12, 0x34, 0x56, 0x78]);
let want = "12345678";
let got = format!("{}", tc);
assert_eq!(got, want);
}
#[test]
fn hex_fmt_impl_macro_backwards() {
struct Wrapper([u8; 4]);
impl Borrow<[u8]> for Wrapper {
fn borrow(&self) -> &[u8] { &self.0[..] }
}
impl_fmt_traits! {
#[display_backward(true)]
impl fmt_traits for Wrapper {
const LENGTH: usize = 4;
}
}
let tc = Wrapper([0x12, 0x34, 0x56, 0x78]);
let want = "78563412";
let got = format!("{}", tc);
assert_eq!(got, want);
}
#[test]
fn hex_fmt_impl_macro_gen_forward() {
struct Wrapper<T>([u8; 4], PhantomData<T>);
impl<T: Clone> Borrow<[u8]> for Wrapper<T> {
fn borrow(&self) -> &[u8] { &self.0[..] }
}
impl_fmt_traits! {
#[display_backward(false)]
impl<T: Clone> fmt_traits for Wrapper<T> {
const LENGTH: usize = 4;
}
}
let tc = Wrapper([0x12, 0x34, 0x56, 0x78], PhantomData::<u32>);
let want = "12345678";
let got = format!("{}", tc);
assert_eq!(got, want);
}
#[test]
fn hex_fmt_impl_macro_gen_backwards() {
struct Wrapper<T>([u8; 4], PhantomData<T>);
impl<T: Clone> Borrow<[u8]> for Wrapper<T> {
fn borrow(&self) -> &[u8] { &self.0[..] }
}
impl_fmt_traits! {
#[display_backward(true)]
impl<T: Clone> fmt_traits for Wrapper<T> {
const LENGTH: usize = 4;
}
}
let tc = Wrapper([0x12, 0x34, 0x56, 0x78], PhantomData::<u32>);
let want = "78563412";
let got = format!("{}", tc);
assert_eq!(got, want);
}
}
#[cfg(feature = "std")]
mod std {
#[test]
fn hex_writer() {
use std::io::{ErrorKind, Result, Write};
use arrayvec::ArrayString;
use super::Case::{Lower, Upper};
use super::{DisplayHex, HexWriter};
macro_rules! test_hex_writer {
($cap:expr, $case: expr, $src: expr, $want: expr, $hex_result: expr) => {
let dest_buf = ArrayString::<$cap>::new();
let mut dest = HexWriter::new(dest_buf, $case);
let got = dest.write($src);
match $want {
Ok(n) => assert_eq!(got.unwrap(), n),
Err(e) => assert_eq!(got.unwrap_err().kind(), e.kind()),
}
assert_eq!(dest.into_inner().as_str(), $hex_result);
};
}
test_hex_writer!(0, Lower, &[], Result::Ok(0), "");
test_hex_writer!(0, Lower, &[0xab, 0xcd], Result::Err(ErrorKind::Other.into()), "");
test_hex_writer!(1, Lower, &[0xab, 0xcd], Result::Err(ErrorKind::Other.into()), "");
test_hex_writer!(2, Lower, &[0xab, 0xcd], Result::Ok(1), "ab");
test_hex_writer!(3, Lower, &[0xab, 0xcd], Result::Ok(1), "ab");
test_hex_writer!(4, Lower, &[0xab, 0xcd], Result::Ok(2), "abcd");
test_hex_writer!(8, Lower, &[0xab, 0xcd], Result::Ok(2), "abcd");
test_hex_writer!(8, Upper, &[0xab, 0xcd], Result::Ok(2), "ABCD");
let vec: Vec<_> = (0u8..32).collect();
let mut writer = HexWriter::new(String::new(), Lower);
writer.write_all(&vec[..]).unwrap();
assert_eq!(writer.into_inner(), vec.to_lower_hex_string());
}
}
}