#![cfg_attr(not(any(test, feature = "std")), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
mod decode;
mod encode;
mod error;
#[cfg(feature = "serde")]
mod serde;
pub use crate::decode::{
hex_check, hex_check_fallback, hex_check_with_case, hex_decode, hex_decode_fallback,
hex_decode_unchecked,
};
pub use crate::encode::{
hex_encode, hex_encode_fallback, hex_encode_upper, hex_encode_upper_fallback, hex_string,
hex_string_upper,
};
pub use crate::error::Error;
#[cfg(feature = "serde")]
pub use crate::serde::{
deserialize, nopfx_ignorecase, nopfx_lowercase, nopfx_uppercase, serialize, withpfx_ignorecase,
withpfx_lowercase, withpfx_uppercase,
};
#[allow(deprecated)]
pub use crate::encode::hex_to;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub use crate::decode::{hex_check_sse, hex_check_sse_with_case};
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(crate) enum Vectorization {
None = 0,
SSE41 = 1,
AVX2 = 2,
}
#[inline(always)]
pub(crate) fn vectorization_support() -> Vectorization {
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "sse"
))]
{
use core::sync::atomic::{AtomicU8, Ordering};
static FLAGS: AtomicU8 = AtomicU8::new(u8::MAX);
let current_flags = FLAGS.load(Ordering::Relaxed);
if current_flags != u8::MAX {
return match current_flags {
0 => Vectorization::None,
1 => Vectorization::SSE41,
2 => Vectorization::AVX2,
_ => unreachable!(),
};
}
let val = vectorization_support_no_cache_x86();
FLAGS.store(val as u8, Ordering::Relaxed);
return val;
}
#[allow(unreachable_code)]
Vectorization::None
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[cold]
fn vectorization_support_no_cache_x86() -> Vectorization {
#[cfg(target_arch = "x86")]
use core::arch::x86::__cpuid_count;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::__cpuid_count;
if cfg!(target_env = "sgx") || !cfg!(target_feature = "sse") {
return Vectorization::None;
}
let proc_info_ecx = unsafe { __cpuid_count(1, 0) }.ecx;
let have_sse4 = (proc_info_ecx >> 19) & 1 == 1;
if !have_sse4 {
return Vectorization::None;
}
let have_xsave = (proc_info_ecx >> 26) & 1 == 1;
let have_osxsave = (proc_info_ecx >> 27) & 1 == 1;
let have_avx = (proc_info_ecx >> 27) & 1 == 1;
if have_xsave && have_osxsave && have_avx {
if unsafe { avx2_support_no_cache_x86() } {
return Vectorization::AVX2;
}
}
Vectorization::SSE41
}
#[target_feature(enable = "xsave")]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[cold]
unsafe fn avx2_support_no_cache_x86() -> bool {
#[cfg(target_arch = "x86")]
use core::arch::x86::{__cpuid_count, _xgetbv};
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::{__cpuid_count, _xgetbv};
let xcr0 = _xgetbv(0);
let os_avx_support = xcr0 & 6 == 6;
if os_avx_support {
let extended_features_ebx = __cpuid_count(7, 0).ebx;
let have_avx2 = (extended_features_ebx >> 5) & 1 == 1;
if have_avx2 {
return true;
}
}
false
}
#[cfg(test)]
mod tests {
use crate::decode::{hex_decode, hex_decode_with_case, CheckCase};
use crate::encode::{hex_encode, hex_string};
use crate::{hex_encode_upper, hex_string_upper, vectorization_support, Vectorization};
use proptest::proptest;
#[cfg(not(feature = "alloc"))]
const CAPACITY: usize = 128;
#[test]
fn test_feature_detection() {
let vector_support = vectorization_support();
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
match vector_support {
Vectorization::AVX2 => assert!(is_x86_feature_detected!("avx2")),
Vectorization::SSE41 => assert!(is_x86_feature_detected!("sse4.1")),
Vectorization::None => assert!(
!cfg!(target_feature = "sse")
|| !is_x86_feature_detected!("avx2") && !is_x86_feature_detected!("sse4.1")
),
}
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
assert_eq!(vector_support, Vectorization::None);
}
fn _test_hex_encode(s: &String) {
let mut buffer = vec![0; s.as_bytes().len() * 2];
{
let encode = &*hex_encode(s.as_bytes(), &mut buffer).unwrap();
#[cfg(feature = "alloc")]
let hex_string = hex_string(s.as_bytes());
#[cfg(not(feature = "alloc"))]
let hex_string = hex_string::<CAPACITY>(s.as_bytes());
assert_eq!(encode, hex::encode(s));
assert_eq!(hex_string.as_str(), hex::encode(s));
}
{
let encode_upper = &*hex_encode_upper(s.as_bytes(), &mut buffer).unwrap();
#[cfg(feature = "alloc")]
let hex_string_upper = hex_string_upper(s.as_bytes());
#[cfg(not(feature = "alloc"))]
let hex_string_upper = hex_string_upper::<CAPACITY>(s.as_bytes());
assert_eq!(encode_upper, hex::encode_upper(s));
assert_eq!(hex_string_upper.as_str(), hex::encode_upper(s));
}
}
#[cfg(feature = "alloc")]
proptest! {
#[test]
fn test_hex_encode(ref s in ".*") {
_test_hex_encode(s);
}
}
#[cfg(not(feature = "alloc"))]
proptest! {
#[test]
fn test_hex_encode(ref s in ".{0,16}") {
_test_hex_encode(s);
}
}
fn _test_hex_decode(s: &String) {
let len = s.as_bytes().len();
{
let mut dst = Vec::with_capacity(len);
dst.resize(len, 0);
#[cfg(feature = "alloc")]
let hex_string = hex_string(s.as_bytes());
#[cfg(not(feature = "alloc"))]
let hex_string = hex_string::<CAPACITY>(s.as_bytes());
hex_decode(hex_string.as_bytes(), &mut dst).unwrap();
hex_decode_with_case(hex_string.as_bytes(), &mut dst, CheckCase::Lower).unwrap();
assert_eq!(&dst[..], s.as_bytes());
}
{
let mut dst = Vec::with_capacity(len);
dst.resize(len, 0);
#[cfg(feature = "alloc")]
let hex_string_upper = hex_string_upper(s.as_bytes());
#[cfg(not(feature = "alloc"))]
let hex_string_upper = hex_string_upper::<CAPACITY>(s.as_bytes());
hex_decode_with_case(hex_string_upper.as_bytes(), &mut dst, CheckCase::Upper).unwrap();
assert_eq!(&dst[..], s.as_bytes());
}
}
#[cfg(feature = "alloc")]
proptest! {
#[test]
fn test_hex_decode(ref s in ".+") {
_test_hex_decode(s);
}
}
#[cfg(not(feature = "alloc"))]
proptest! {
#[test]
fn test_hex_decode(ref s in ".{1,16}") {
_test_hex_decode(s);
}
}
fn _test_hex_decode_check(s: &String, ok: bool) {
let len = s.as_bytes().len();
let mut dst = Vec::with_capacity(len / 2);
dst.resize(len / 2, 0);
assert!(hex_decode(s.as_bytes(), &mut dst).is_ok() == ok);
}
proptest! {
#[test]
fn test_hex_decode_check(ref s in "([0-9a-fA-F][0-9a-fA-F])+") {
_test_hex_decode_check(s, true);
}
}
proptest! {
#[test]
fn test_hex_decode_check_odd(ref s in "[0-9a-fA-F]{11}") {
_test_hex_decode_check(s, false);
}
}
}