fashex 0.0.6

Hexadecimal string encoding and decoding with best-effort SIMD acceleration.
Documentation
//! See [`Display`].

use alloc::vec::Vec;
use core::{fmt, str};

wrapper_lite::wrapper!(
    #[wrapper(Debug)]
    #[wrapper(AsRef)]
    #[wrapper(Deref)]
    #[wrapper(From)]
    #[bound(T: AsRef<[u8]>)]
    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
    #[derive(Clone, Copy, PartialEq, Eq)]
    /// A wrapper for displaying a byte slice as a hexadecimal string.
    ///
    /// ```rust
    /// let bytes = &[0xde, 0xad, 0xbe, 0xef];
    ///
    /// let displayed = fashex::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");
    /// ```
    pub struct Display<T> {
        value: T,
    }
);

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 {
        if f.alternate() {
            f.write_str("0x")?;
        }

        let bytes = self.value.as_ref();

        let mut buf = Vec::with_capacity(bytes.len() * 2);

        let encoded = crate::encode::<_, UPPER>(bytes, &mut buf);

        #[allow(unsafe_code, reason = "XXX")]
        unsafe {
            debug_assert!(encoded.is_ok(), "pre-allocated capacity is sufficient");

            // SAFETY: pre-allocated capacity is sufficient
            let encoded = encoded.unwrap_unchecked();

            // SAFETY: the returned slice is guaranteed to be valid UTF-8 since it only
            // contains ASCII characters.
            f.write_str(str::from_utf8_unchecked(encoded))
        }
    }
}