#![doc = include_str!("../README.md")]
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(feature = "portable-simd", feature(portable_simd))]
#![cfg_attr(
all(feature = "experimental-loongarch64-simd", target_arch = "loongarch64"),
feature(stdarch_loongarch)
)]
#[cfg(any(test, feature = "alloc"))]
extern crate alloc;
#[cfg(any(test, feature = "std"))]
extern crate std;
mod backend;
mod buffer;
#[cfg(feature = "alloc")]
mod display;
mod error;
#[cfg(feature = "__internal_fuzz")]
pub mod fuzz;
mod util;
use core::mem::MaybeUninit;
pub use crate::buffer::Buffer;
#[cfg(feature = "alloc")]
pub use crate::display::Display;
pub use crate::error::InvalidInput;
pub use crate::util::{HEX_CHARS_LOWER, HEX_CHARS_UPPER};
pub fn encode<'dst, B: Buffer + ?Sized, const UPPER: bool>(
src: &[u8],
dst: &'dst mut B,
) -> Result<&'dst [u8], InvalidInput> {
backend::encode::<UPPER>(src, dst.spare_capacity_mut())?;
#[allow(unsafe_code, reason = "XXX")]
unsafe {
Ok(dst.advance(src.len() * 2))
}
}
#[cfg(feature = "__internal_cargo_asm")]
#[doc(hidden)]
pub fn __cargo_asm_encode<'dst>(
src: &[u8],
dst: &'dst mut [MaybeUninit<u8>],
) -> Result<&'dst [u8], InvalidInput> {
encode::<_, false>(src, dst)
}
pub fn decode<'dst, B: Buffer + ?Sized>(
src: &[u8],
dst: &'dst mut B,
) -> Result<&'dst [u8], InvalidInput> {
backend::decode(src, dst.spare_capacity_mut())?;
#[allow(unsafe_code, reason = "XXX")]
unsafe {
Ok(dst.advance(src.len() / 2))
}
}
#[cfg(feature = "__internal_cargo_asm")]
#[doc(hidden)]
pub fn __cargo_asm_decode<'dst>(
src: &[u8],
dst: &'dst mut [MaybeUninit<u8>],
) -> Result<&'dst [u8], InvalidInput> {
decode(src, dst)
}
pub const fn encode_generic<'dst, const UPPER: bool>(
src: &[u8],
dst: &'dst mut [MaybeUninit<u8>],
) -> Result<&'dst [u8], InvalidInput> {
let (dst, _) = dst.as_chunks_mut::<2>();
if src.len() > dst.len() {
return Err(InvalidInput);
}
#[allow(unsafe_code, reason = "The length is validated")]
unsafe {
backend::generic::encode_generic_unchecked::<UPPER>(src, dst);
};
let dst = dst.as_flattened();
#[allow(
unsafe_code,
reason = "We have encoded the input bytes to a hexadecimal string and fully initialized \
the output buffer"
)]
let dst = unsafe { &*(&raw const *dst as *const [u8]) };
Ok(dst)
}
pub const fn decode_generic<'dst>(
src: &[u8],
dst: &'dst mut [MaybeUninit<u8>],
) -> Result<&'dst [u8], InvalidInput> {
let (src, &[]) = src.as_chunks::<2>() else {
return Err(InvalidInput);
};
if src.len() > dst.len() {
return Err(InvalidInput);
}
#[allow(unsafe_code, reason = "The length is validated")]
let ret = unsafe { backend::generic::decode_generic_unchecked::<false>(src, dst) };
match ret {
Ok(()) => {
#[allow(
unsafe_code,
reason = "We have decoded the input hexadecimal string to bytes and fully \
initialized the output buffer"
)]
let dst = unsafe { &*((&raw const *dst) as *const [u8]) };
Ok(dst)
}
Err(e) => Err(e),
}
}
#[macro_export]
macro_rules! encode {
($bytes:expr) => {
$crate::encode!($bytes, false)
};
($bytes:expr, $uppercase:expr) => {{
const ENCODED: [u8; $bytes.len() * 2] = {
let buf: &mut [::core::mem::MaybeUninit<u8>; const { $bytes.len() * 2 }] =
&mut [::core::mem::MaybeUninit::uninit(); _];
#[allow(unsafe_code, reason = "XXX")]
let bytes = unsafe { ::core::slice::from_raw_parts($bytes.as_ptr(), $bytes.len()) };
match $crate::encode_generic::<{ $uppercase }>(bytes, buf) {
Ok(_) => {}
Err(_) => unreachable!(),
};
#[allow(unsafe_code, reason = "XXX")]
unsafe {
::core::mem::transmute::<_, _>(*buf)
}
};
#[allow(unsafe_code, reason = "XXX")]
unsafe {
::core::str::from_utf8_unchecked(&ENCODED)
}
}};
}
#[macro_export]
macro_rules! decode {
($bytes:expr) => {{
const DECODED: [u8; $bytes.len() / 2] = {
assert!(
$bytes.len() % 2 == 0,
"the length of the input must be even"
);
let buf: &mut [::core::mem::MaybeUninit<u8>; const { $bytes.len() / 2 }] =
&mut [::core::mem::MaybeUninit::uninit(); _];
#[allow(unsafe_code, reason = "XXX")]
let bytes = unsafe { ::core::slice::from_raw_parts($bytes.as_ptr(), $bytes.len()) };
match $crate::decode_generic(bytes, buf) {
Ok(_) => {}
Err(_) => panic!("invalid hexadecimal string"),
};
#[allow(unsafe_code, reason = "XXX")]
unsafe {
::core::mem::transmute::<_, _>(*buf)
}
};
&DECODED
}};
}