#![doc=vsimd::shared_docs!()]
#![cfg_attr(not(any(feature = "std", test)), no_std)]
#![cfg_attr(feature = "unstable", feature(arm_target_feature))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(test, deny(warnings))]
#![deny(
missing_debug_implementations,
missing_docs,
clippy::all,
clippy::pedantic,
clippy::cargo,
clippy::missing_inline_in_public_items
)]
#![warn(clippy::todo)]
#![allow(
clippy::inline_always,
clippy::wildcard_imports,
clippy::cast_possible_wrap,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::module_name_repetitions
)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[macro_use]
mod error;
pub use self::error::Error;
mod check;
mod decode;
mod encode;
mod multiversion;
#[cfg(feature = "alloc")]
mod heap;
pub use outref::{AsOut, Out};
pub use vsimd::ascii::AsciiCase;
use vsimd::tools::{slice_mut, slice_parts};
#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};
#[inline]
#[must_use]
pub const fn encoded_length(n: usize) -> usize {
assert!(n <= usize::MAX / 2);
n * 2
}
#[inline]
pub fn decoded_length(n: usize) -> Result<usize, Error> {
ensure!(n % 2 == 0);
Ok(n / 2)
}
#[inline]
pub fn check(data: &[u8]) -> Result<(), Error> {
let (src, len) = slice_parts(data);
unsafe { crate::multiversion::check::auto(src, len) }
}
#[inline]
#[must_use]
pub fn encode<'d>(src: &[u8], mut dst: Out<'d, [u8]>, case: AsciiCase) -> &'d mut [u8] {
assert!(dst.len() / 2 >= src.len());
unsafe {
let (src, len) = slice_parts(src);
let dst = dst.as_mut_ptr();
crate::multiversion::encode::auto(src, len, dst, case);
slice_mut(dst, len * 2)
}
}
#[inline]
pub fn decode<'d>(src: &[u8], mut dst: Out<'d, [u8]>) -> Result<&'d mut [u8], Error> {
ensure!(src.len() % 2 == 0);
assert!(dst.len() >= src.len() / 2);
let len = src.len();
let dst = dst.as_mut_ptr();
let src = src.as_ptr();
unsafe {
crate::multiversion::decode::auto(src, len, dst)?;
Ok(slice_mut(dst, len / 2))
}
}
#[inline]
pub fn decode_inplace(data: &mut [u8]) -> Result<&mut [u8], Error> {
ensure!(data.len() % 2 == 0);
unsafe {
let len = data.len();
let dst: *mut u8 = data.as_mut_ptr();
let src: *const u8 = dst;
crate::multiversion::decode::auto(src, len, dst)?;
Ok(slice_mut(dst, len / 2))
}
}
#[inline]
#[must_use]
pub fn encode_as_str<'d>(src: &[u8], dst: Out<'d, [u8]>, case: AsciiCase) -> &'d mut str {
let ans = encode(src, dst, case);
unsafe { core::str::from_utf8_unchecked_mut(ans) }
}
pub trait FromHexDecode: Sized {
fn from_hex_decode(data: &[u8]) -> Result<Self, Error>;
}
pub trait FromHexEncode: Sized {
fn from_hex_encode(data: &[u8], case: AsciiCase) -> Self;
}
#[inline]
#[must_use]
pub fn encode_type<T: FromHexEncode>(data: impl AsRef<[u8]>, case: AsciiCase) -> T {
T::from_hex_encode(data.as_ref(), case)
}
#[inline]
pub fn decode_type<T: FromHexDecode>(data: impl AsRef<[u8]>) -> Result<T, Error> {
T::from_hex_decode(data.as_ref())
}
pub trait AppendHexEncode: FromHexEncode {
fn append_hex_encode(src: &[u8], dst: &mut Self, case: AsciiCase);
}
pub trait AppendHexDecode: FromHexDecode {
fn append_hex_decode(src: &[u8], dst: &mut Self) -> Result<(), Error>;
}
#[inline]
pub fn encode_append<T: AppendHexEncode>(src: impl AsRef<[u8]>, dst: &mut T, case: AsciiCase) {
T::append_hex_encode(src.as_ref(), dst, case);
}
#[inline]
pub fn decode_append<T: AppendHexDecode>(src: impl AsRef<[u8]>, dst: &mut T) -> Result<(), Error> {
T::append_hex_decode(src.as_ref(), dst)
}
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[cfg(feature = "alloc")]
#[inline]
#[must_use]
pub fn encode_to_string(data: impl AsRef<[u8]>, case: AsciiCase) -> String {
encode_type(data, case)
}
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[cfg(feature = "alloc")]
#[inline]
pub fn decode_to_vec(data: impl AsRef<[u8]>) -> Result<Vec<u8>, Error> {
decode_type(data)
}