#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::rc::Rc;
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use alloc::sync::Arc;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::mem::MaybeUninit;
use crate::decode;
#[cfg(feature = "alloc")]
use crate::encode;
use crate::error::InvalidInput;
trait FromHexPriv {}
#[allow(private_bounds, reason = "Sealed trait.")]
pub trait FromHex: FromHexPriv + Sized {
fn decode<T: AsRef<[u8]>>(bytes: T) -> Result<Self, InvalidInput>;
}
#[cfg(feature = "alloc")]
impl FromHexPriv for Vec<u8> {}
#[cfg(feature = "alloc")]
impl FromHex for Vec<u8> {
fn decode<T: AsRef<[u8]>>(bytes: T) -> Result<Self, InvalidInput> {
let bytes = bytes.as_ref();
if bytes.len() % 2 != 0 {
return Err(InvalidInput);
}
let mut buf = Self::with_capacity(bytes.len() / 2);
decode(bytes, &mut buf)?;
Ok(buf)
}
}
#[cfg(feature = "alloc")]
impl FromHexPriv for Box<[u8]> {}
#[cfg(feature = "alloc")]
impl FromHex for Box<[u8]> {
fn decode<T: AsRef<[u8]>>(bytes: T) -> Result<Self, InvalidInput> {
let bytes = bytes.as_ref();
if bytes.len() % 2 != 0 {
return Err(InvalidInput);
}
let mut buf = Self::new_uninit_slice(bytes.len() / 2);
decode(bytes, &mut buf[..])?;
#[allow(unsafe_code, reason = "XXX")]
let buf = unsafe { buf.assume_init() };
Ok(buf)
}
}
#[cfg(feature = "alloc")]
impl FromHexPriv for Arc<[u8]> {}
#[cfg(feature = "alloc")]
impl FromHex for Arc<[u8]> {
fn decode<T: AsRef<[u8]>>(bytes: T) -> Result<Self, InvalidInput> {
let bytes = bytes.as_ref();
if bytes.len() % 2 != 0 {
return Err(InvalidInput);
}
let mut buf = Self::new_uninit_slice(bytes.len() / 2);
decode(bytes, Arc::make_mut(&mut buf))?;
#[allow(unsafe_code, reason = "XXX")]
let buf = unsafe { buf.assume_init() };
Ok(buf)
}
}
#[cfg(feature = "alloc")]
impl FromHexPriv for Rc<[u8]> {}
#[cfg(feature = "alloc")]
impl FromHex for Rc<[u8]> {
fn decode<T: AsRef<[u8]>>(bytes: T) -> Result<Self, InvalidInput> {
let bytes = bytes.as_ref();
if bytes.len() % 2 != 0 {
return Err(InvalidInput);
}
let mut buf = Self::new_uninit_slice(bytes.len() / 2);
decode(bytes, Rc::make_mut(&mut buf))?;
#[allow(unsafe_code, reason = "XXX")]
let buf = unsafe { buf.assume_init() };
Ok(buf)
}
}
impl<const N: usize> FromHexPriv for [u8; N] {}
impl<const N: usize> FromHex for [u8; N] {
fn decode<T: AsRef<[u8]>>(bytes: T) -> Result<Self, InvalidInput> {
let bytes = bytes.as_ref();
if bytes.len() % 2 != 0 {
return Err(InvalidInput);
}
if bytes.len() / 2 != N {
return Err(InvalidInput);
}
let mut buf = [MaybeUninit::uninit(); N];
decode(bytes, &mut buf)?;
#[allow(unsafe_code, reason = "XXX")]
let buf = unsafe { *((&raw const buf).cast::<[u8; N]>()) };
Ok(buf)
}
}
#[cfg(feature = "alloc")]
trait ToHexPriv {}
#[cfg(feature = "alloc")]
#[allow(private_bounds, reason = "Sealed trait.")]
pub trait ToHex: ToHexPriv {
fn encode<const UPPER: bool>(&self) -> String;
}
#[cfg(feature = "alloc")]
impl<T> ToHexPriv for T where T: AsRef<[u8]> + ?Sized {}
#[cfg(feature = "alloc")]
impl<T> ToHex for T
where
T: AsRef<[u8]> + ?Sized,
{
fn encode<const UPPER: bool>(&self) -> String {
let this = self.as_ref();
let mut buf = Vec::with_capacity(this.len() * 2);
let _ = encode::<Vec<u8>, UPPER>(this, &mut buf);
#[allow(unsafe_code, reason = "XXX")]
unsafe {
String::from_utf8_unchecked(buf)
}
}
}
#[cfg(test)]
mod smoking {
use super::*;
#[test]
fn test_decoding() {
macro_rules! test {
($expr:expr, $expected:expr) => {
assert_eq!(Vec::decode($expr).unwrap(), $expected);
assert_eq!(&*Box::<[u8]>::decode($expr).unwrap(), $expected);
assert_eq!(&*Arc::<[u8]>::decode($expr).unwrap(), $expected);
assert_eq!(&*Rc::<[u8]>::decode($expr).unwrap(), $expected);
assert_eq!(&<[u8; $expected.len()]>::decode($expr).unwrap(), $expected);
};
}
test!("", b"");
test!("68656c6c6f20776f726c64", b"hello world");
test!("68656C6C6F20776F726C64", b"hello world");
test!("68656c6C6f20776F726C64", b"hello world");
macro_rules! test_should_fail {
($expr:expr) => {
assert!(Vec::decode($expr).is_err());
assert!(Box::<[u8]>::decode($expr).is_err());
assert!(Arc::<[u8]>::decode($expr).is_err());
assert!(Rc::<[u8]>::decode($expr).is_err());
assert!(<[u8; $expr.len() / 2]>::decode($expr).is_err());
};
}
test_should_fail!("68656c6c6f20776f726c6");
test_should_fail!("68656x6c6f20776f726c64");
assert!(<[u8; 2]>::decode("68656c6c6f20776f726c64").is_err());
}
#[test]
fn test_encoding() {
macro_rules! test {
($expr:expr, $lower:expr, $upper:expr) => {
assert_eq!($expr.encode::<false>(), $lower);
assert_eq!($expr.encode::<true>(), $upper);
};
}
test!(b"", "", "");
test!(
b"hello world",
"68656c6c6f20776f726c64",
"68656C6C6F20776F726C64"
);
}
}