Skip to content

Commit

Permalink
impl hex_string() and hex_string_upper() using heapless::String
Browse files Browse the repository at this point in the history
  • Loading branch information
Georges760 committed Sep 13, 2024
1 parent a45bc53 commit d987e1c
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 22 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
26 changes: 20 additions & 6 deletions src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<CAPACITY>(s.as_bytes());

hex_decode_fallback(hex_string.as_bytes(), &mut dst);

Expand All @@ -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 (
Expand Down
33 changes: 33 additions & 0 deletions src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -28,16 +31,46 @@ fn hex_string_custom_case(src: &[u8], upper_case: bool) -> String {
}
}

#[cfg(not(feature = "alloc"))]
fn hex_string_custom_case<const N: usize>(src: &[u8], upper_case: bool) -> String<N> {
let mut buffer = Vec::<_, N>::new();
buffer
.resize(src.len() * 2, 0)
.expect("String<N> 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<const N: usize>(src: &[u8]) -> String<N> {
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<const N: usize>(src: &[u8]) -> String<N> {
hex_string_custom_case(src, true)
}

pub fn hex_encode_custom<'a>(
src: &[u8],
dst: &'a mut [u8],
Expand Down
52 changes: 37 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();
Expand All @@ -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::<CAPACITY>(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::<CAPACITY>(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));
}
}

Expand All @@ -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::<CAPACITY>(s.as_bytes());

hex_decode(hex_string.as_bytes(), &mut dst).unwrap();

Expand All @@ -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::<CAPACITY>(s.as_bytes());

hex_decode_with_case(hex_string_upper.as_bytes(), &mut dst, CheckCase::Upper).unwrap();

Expand All @@ -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);
Expand Down

0 comments on commit d987e1c

Please sign in to comment.