use crate::buffer::{OwnedBuf, OwnedUninitBuf as _, StringBuf};
use crate::error::InvalidInput;
use crate::{decode, encode};
trait HexPriv {}
#[allow(private_bounds, reason = "Sealed trait.")]
pub trait Hex: HexPriv {
fn encode_hex<O: StringBuf, const UPPER: bool>(&self) -> Result<O, InvalidInput>;
fn decode_hex<O: OwnedBuf>(&self) -> Result<O, InvalidInput>;
}
impl<T: AsRef<[u8]>> HexPriv for T {}
impl<T: AsRef<[u8]>> Hex for T {
fn encode_hex<O: StringBuf, const UPPER: bool>(&self) -> Result<O, InvalidInput> {
let this = self.as_ref();
let mut buf = O::Buf::new_uninit_slice(this.len() * 2)?;
encode::<UPPER>(this, buf.as_uninit())?;
#[allow(unsafe_code, reason = "`encode` has fully initialized the buffer.")]
let buf = unsafe { buf.assume_init() };
#[allow(
unsafe_code,
reason = "Hexadecimal output contains only ASCII bytes, which are valid UTF-8."
)]
let buf = unsafe { O::from_utf8_unchecked(buf) };
Ok(buf)
}
fn decode_hex<O: OwnedBuf>(&self) -> Result<O, InvalidInput> {
let this = self.as_ref();
if this.len() % 2 != 0 {
return Err(InvalidInput);
}
let mut buf = O::new_uninit_slice(this.len() / 2)?;
decode(this, buf.as_uninit())?;
#[allow(unsafe_code, reason = "`decode` has fully initialized the buffer.")]
let buf = unsafe { buf.assume_init() };
Ok(buf)
}
}
#[cfg(test)]
mod smoking {
use alloc::boxed::Box;
use alloc::rc::Rc;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use super::*;
#[test]
fn test_encode() {
let bytes = &[0xde, 0xad, 0xbe, 0xef];
macro_rules! test {
($ty:ty, $expected:expr) => {
assert!(
bytes
.encode_hex::<$ty, false>()
.unwrap()
.eq_ignore_ascii_case($expected)
);
};
}
test!([u8; 8], b"deadbeef");
test!(Vec<u8>, b"deadbeef");
test!(Box<[u8]>, b"deadbeef");
test!(Arc<[u8]>, b"deadbeef");
test!(Rc<[u8]>, b"deadbeef");
test!(String, "deadbeef");
test!(Box<str>, "deadbeef");
test!(Arc<str>, "deadbeef");
test!(Rc<str>, "deadbeef");
}
#[test]
fn test_decode() {
let hex = b"deadbeef";
macro_rules! test {
($ty:ty, $expected:expr) => {
assert_eq!(&hex.decode_hex::<$ty>().unwrap()[..], $expected);
};
}
test!([u8; 4], b"\xde\xad\xbe\xef");
test!(Vec<u8>, b"\xde\xad\xbe\xef");
test!(Box<[u8]>, b"\xde\xad\xbe\xef");
test!(Arc<[u8]>, b"\xde\xad\xbe\xef");
test!(Rc<[u8]>, b"\xde\xad\xbe\xef");
}
}