diff --git a/Cargo.toml b/Cargo.toml index 54b8586..6ad17d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,8 @@ std = ["alloc", "serde?/std"] alloc = [] serde = ["dep:serde"] - +[target.'cfg(not(feature = "alloc"))'.dependencies] +heapless = { version = "0.8" } [dev-dependencies] criterion = "0.3" diff --git a/src/decode.rs b/src/decode.rs index 0d32dca..be8ea59 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -304,21 +304,27 @@ pub fn hex_decode_fallback(src: &[u8], dst: &mut [u8]) { #[cfg(test)] mod tests { - #[cfg(feature = "alloc")] - use crate::decode::hex_decode_fallback; use crate::decode::NIL; - use crate::decode::{hex_check_fallback, hex_check_fallback_with_case, CheckCase}; - #[cfg(feature = "alloc")] - use crate::encode::hex_string; + use crate::{ + decode::{ + hex_check_fallback, hex_check_fallback_with_case, hex_decode_fallback, CheckCase, + }, + encode::hex_string, + }; use proptest::proptest; - #[cfg(feature = "alloc")] + #[cfg(not(feature = "alloc"))] + const CAPACITY: usize = 128; + fn _test_decode_fallback(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::(s.as_bytes()); hex_decode_fallback(hex_string.as_bytes(), &mut dst); @@ -333,6 +339,14 @@ mod tests { } } + #[cfg(not(feature = "alloc"))] + proptest! { + #[test] + fn test_decode_fallback(ref s in ".{1,16}") { + _test_decode_fallback(s); + } + } + fn _test_check_fallback_true(s: &String) { assert!(hex_check_fallback(s.as_bytes())); match ( diff --git a/src/encode.rs b/src/encode.rs index 6657856..5095778 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -6,6 +6,9 @@ use core::arch::x86_64::*; #[cfg(feature = "alloc")] use alloc::{string::String, vec}; +#[cfg(not(feature = "alloc"))] +use heapless::{String, Vec}; + use crate::error::Error; static TABLE_LOWER: &[u8] = b"0123456789abcdef"; @@ -28,16 +31,46 @@ fn hex_string_custom_case(src: &[u8], upper_case: bool) -> String { } } +#[cfg(not(feature = "alloc"))] +fn hex_string_custom_case(src: &[u8], upper_case: bool) -> String { + let mut buffer = Vec::<_, N>::new(); + buffer + .resize(src.len() * 2, 0) + .expect("String capacity too short"); + if upper_case { + hex_encode_upper(src, &mut buffer).expect("hex_string"); + } else { + hex_encode(src, &mut buffer).expect("hex_string"); + } + + if cfg!(debug_assertions) { + String::from_utf8(buffer).unwrap() + } else { + // Saftey: We just wrote valid utf8 hex string into the dst + unsafe { String::from_utf8_unchecked(buffer) } + } +} + #[cfg(feature = "alloc")] pub fn hex_string(src: &[u8]) -> String { hex_string_custom_case(src, false) } +#[cfg(not(feature = "alloc"))] +pub fn hex_string(src: &[u8]) -> String { + hex_string_custom_case(src, false) +} + #[cfg(feature = "alloc")] pub fn hex_string_upper(src: &[u8]) -> String { hex_string_custom_case(src, true) } +#[cfg(not(feature = "alloc"))] +pub fn hex_string_upper(src: &[u8]) -> String { + hex_string_custom_case(src, true) +} + pub fn hex_encode_custom<'a>( src: &[u8], dst: &'a mut [u8], diff --git a/src/lib.rs b/src/lib.rs index f63bb0b..d4acf1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,10 +15,9 @@ pub use crate::decode::{ hex_decode_unchecked, }; pub use crate::encode::{ - hex_encode, hex_encode_fallback, hex_encode_upper, hex_encode_upper_fallback, + hex_encode, hex_encode_fallback, hex_encode_upper, hex_encode_upper_fallback, hex_string, + hex_string_upper, }; -#[cfg(feature = "alloc")] -pub use crate::encode::{hex_string, hex_string_upper}; pub use crate::error::Error; @@ -130,16 +129,14 @@ unsafe fn avx2_support_no_cache_x86() -> bool { #[cfg(test)] mod tests { - use crate::decode::hex_decode; - #[cfg(feature = "alloc")] - use crate::decode::{hex_decode_with_case, CheckCase}; - #[cfg(feature = "alloc")] + use crate::decode::{hex_decode, hex_decode_with_case, CheckCase}; use crate::encode::{hex_encode, hex_string}; - #[cfg(feature = "alloc")] - use crate::{hex_encode_upper, hex_string_upper}; - use crate::{vectorization_support, Vectorization}; + 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(); @@ -158,25 +155,30 @@ mod tests { assert_eq!(vector_support, Vectorization::None); } - #[cfg(feature = "alloc")] 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::(s.as_bytes()); assert_eq!(encode, hex::encode(s)); - assert_eq!(hex_string, 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::(s.as_bytes()); assert_eq!(encode_upper, hex::encode_upper(s)); - assert_eq!(hex_string_upper, hex::encode_upper(s)); + assert_eq!(hex_string_upper.as_str(), hex::encode_upper(s)); } } @@ -188,14 +190,23 @@ mod tests { } } - #[cfg(feature = "alloc")] + #[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::(s.as_bytes()); hex_decode(hex_string.as_bytes(), &mut dst).unwrap(); @@ -206,7 +217,10 @@ mod tests { { 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::(s.as_bytes()); hex_decode_with_case(hex_string_upper.as_bytes(), &mut dst, CheckCase::Upper).unwrap(); @@ -222,6 +236,14 @@ mod tests { } } + #[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);