From e97413a3edf1c180e18450685d555c32b23fd009 Mon Sep 17 00:00:00 2001 From: Anayo Anyafulu Date: Tue, 24 Feb 2026 13:02:05 +0200 Subject: [PATCH 1/5] Port hex_to_int from C to Rust --- src/lib_ccx/utility.c | 39 ++++++++++++++++------------ src/rust/lib_ccxr/src/util/hex.rs | 31 ++++++++++++++++++++++ src/rust/lib_ccxr/src/util/mod.rs | 34 +----------------------- src/rust/src/libccxr_exports/mod.rs | 1 + src/rust/src/libccxr_exports/util.rs | 9 +++++++ 5 files changed, 65 insertions(+), 49 deletions(-) create mode 100644 src/rust/lib_ccxr/src/util/hex.rs create mode 100644 src/rust/src/libccxr_exports/util.rs diff --git a/src/lib_ccx/utility.c b/src/lib_ccx/utility.c index 00f213414..6137b3b45 100644 --- a/src/lib_ccx/utility.c +++ b/src/lib_ccx/utility.c @@ -8,7 +8,6 @@ int temp_debug = 0; // This is a convenience variable used to enable/disable debug on variable conditions. Find references to understand. volatile sig_atomic_t change_filename_requested = 0; - extern int ccxr_verify_crc32(uint8_t *buf, int len); extern int ccxr_levenshtein_dist(const uint64_t *s1, const uint64_t *s2, unsigned s1len, unsigned s2len); extern int ccxr_levenshtein_dist_char(const char *s1, const char *s2, unsigned s1len, unsigned s2len); @@ -269,23 +268,32 @@ void sleep_secs(int secs) #endif } +#ifndef DISABLE_RUST +extern int ccxr_hex_to_int(char high, char low); int hex_to_int(char high, char low) { - unsigned char h, l; - if (high >= '0' && high <= '9') - h = high - '0'; - else if (high >= 'a' && high <= 'f') - h = high - 'a' + 10; - else - return -1; - if (low >= '0' && low <= '9') - l = low - '0'; - else if (low >= 'a' && low <= 'f') - l = low - 'a' + 10; - else - return -1; - return h * 16 + l; + return ccxr_hex_to_int(high, low); +} +#else +int hex_to_int(char high, char low) +{ + unsigned char h, l; + if (high >= '0' && high <= '9') + h = high - '0'; + else if (high >= 'a' && high <= 'f') + h = high - 'a' + 10; + else + return -1; + if (low >= '0' && low <= '9') + l = low - '0'; + else if (low >= 'a' && low <= 'f') + l = low - 'a' + 10; + else + return -1; + return h * 16 + l; } +#endif + int hex_string_to_int(char *string, int len) { int total_return = 0; @@ -302,7 +310,6 @@ int hex_string_to_int(char *string, int len) } return total_return; } - #ifndef _WIN32 void m_signal(int sig, void (*func)(int)) { diff --git a/src/rust/lib_ccxr/src/util/hex.rs b/src/rust/lib_ccxr/src/util/hex.rs new file mode 100644 index 000000000..370e30947 --- /dev/null +++ b/src/rust/lib_ccxr/src/util/hex.rs @@ -0,0 +1,31 @@ +/// Converts a single lowercase hex character to its integer value. +/// Returns None if the character is not valid (0-9 or a-f). +fn hex_char_to_val(c: char) -> Option { + match c { + '0'..='9' => Some(c as i32 - '0' as i32), + 'a'..='f' => Some(c as i32 - 'a' as i32 + 10), + _ => None, + } +} + +/// Converts two lowercase hex characters into a combined integer value. +/// Returns None if either character is invalid. +pub fn hex_to_int(high: char, low: char) -> Option { + let h = hex_char_to_val(high)?; + let l = hex_char_to_val(low)?; + Some(h * 16 + l) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_hex_to_int() { + assert_eq!(hex_to_int('4', 'f'), Some(79)); + assert_eq!(hex_to_int('0', '0'), Some(0)); + assert_eq!(hex_to_int('f', 'f'), Some(255)); + assert_eq!(hex_to_int('z', '1'), None); // invalid + assert_eq!(hex_to_int('A', 'F'), None); // uppercase not supported + } +} diff --git a/src/rust/lib_ccxr/src/util/mod.rs b/src/rust/lib_ccxr/src/util/mod.rs index 2d05f1221..166440df3 100644 --- a/src/rust/lib_ccxr/src/util/mod.rs +++ b/src/rust/lib_ccxr/src/util/mod.rs @@ -1,48 +1,16 @@ //! Provides basic utilities used throughout the program. -//! -//! # Conversion Guide -//! -//! | From | To | -//! |--------------------------------------------|--------------------------------| -//! | `PARITY_8` | [`parity`] | -//! | `REVERSE_8` | [`reverse`] | -//! | `UNHAM_8_4` | [`decode_hamming_8_4`] | -//! | `unham_24_18` | [`decode_hamming_24_18`] | -//! | `levenshtein_dist`, levenshtein_dist_char` | [`levenshtein`](levenshtein()) | - pub mod bits; pub mod encoders_helper; pub mod encoding; +pub mod hex; pub mod levenshtein; pub mod log; pub mod time; use std::os::raw::c_char; -/// Helper function that converts a Rust-String (`string`) to C-String (`buffer`). -/// -/// # Safety -/// -/// `buffer` must have enough allocated space for `string` to fit. pub fn write_string_into_pointer(buffer: *mut c_char, string: &str) { let buffer = unsafe { std::slice::from_raw_parts_mut(buffer as *mut u8, string.len() + 1) }; buffer[..string.len()].copy_from_slice(string.as_bytes()); buffer[string.len()] = b'\0'; } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_write_string_into_pointer() { - let test_string = "CCExtractor is the best"; - let mut buffer = vec![0u8; test_string.len() + 1]; - let buffer_ptr = buffer.as_mut_ptr() as *mut c_char; - - write_string_into_pointer(buffer_ptr, test_string); - - assert_eq!(&buffer[..test_string.len()], test_string.as_bytes()); - assert_eq!(buffer[test_string.len()], 0); // Check null terminator - } -} diff --git a/src/rust/src/libccxr_exports/mod.rs b/src/rust/src/libccxr_exports/mod.rs index f5016a95b..aebaf5210 100644 --- a/src/rust/src/libccxr_exports/mod.rs +++ b/src/rust/src/libccxr_exports/mod.rs @@ -5,6 +5,7 @@ pub mod demuxer; pub mod demuxerdata; pub mod net; pub mod time; +pub mod util; use crate::ccx_options; use lib_ccxr::util::log::*; use lib_ccxr::util::{bits::*, levenshtein::*}; diff --git a/src/rust/src/libccxr_exports/util.rs b/src/rust/src/libccxr_exports/util.rs new file mode 100644 index 000000000..6c5cfd43c --- /dev/null +++ b/src/rust/src/libccxr_exports/util.rs @@ -0,0 +1,9 @@ +#[no_mangle] +pub unsafe extern "C" fn ccxr_hex_to_int( high: c_char, low: c_char) -> c_int { + lib_ccxr::util::hex::hex_to_int( + high as u8 as char, + low as u8 as char, + + ) + .unwrap_or(-1) +} From 19ad75a8f6d37472e14cc3e44ea9e803cc87da1d Mon Sep 17 00:00:00 2001 From: Anayo Anyafulu Date: Tue, 24 Feb 2026 13:30:44 +0200 Subject: [PATCH 2/5] Restore mod.rs and add missing imports to util.rs --- src/rust/lib_ccxr/src/util/mod.rs | 25 +++++++++++++++++++++++-- src/rust/src/libccxr_exports/util.rs | 5 +++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/rust/lib_ccxr/src/util/mod.rs b/src/rust/lib_ccxr/src/util/mod.rs index 166440df3..ef0e092fa 100644 --- a/src/rust/lib_ccxr/src/util/mod.rs +++ b/src/rust/lib_ccxr/src/util/mod.rs @@ -1,4 +1,14 @@ //! Provides basic utilities used throughout the program. +//! +//! # Conversion Guide +//! +//! | From | To | +//! |--------------------------------------------|--------------------------------| +//! | `PARITY_8` | [`parity`] | +//! | `REVERSE_8` | [`reverse`] | +//! | `UNHAM_8_4` | [`decode_hamming_8_4`] | +//! | `unham_24_18` | [`decode_hamming_24_18`] | +//! | `levenshtein_dist`, levenshtein_dist_char` | [`levenshtein`](levenshtein()) | pub mod bits; pub mod encoders_helper; pub mod encoding; @@ -6,11 +16,22 @@ pub mod hex; pub mod levenshtein; pub mod log; pub mod time; - use std::os::raw::c_char; - pub fn write_string_into_pointer(buffer: *mut c_char, string: &str) { let buffer = unsafe { std::slice::from_raw_parts_mut(buffer as *mut u8, string.len() + 1) }; buffer[..string.len()].copy_from_slice(string.as_bytes()); buffer[string.len()] = b'\0'; } +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_write_string_into_pointer() { + let test_string = "CCExtractor is the best"; + let mut buffer = vec![0u8; test_string.len() + 1]; + let buffer_ptr = buffer.as_mut_ptr() as *mut c_char; + write_string_into_pointer(buffer_ptr, test_string); + assert_eq!(&buffer[..test_string.len()], test_string.as_bytes()); + assert_eq!(buffer[test_string.len()], 0); + } +} diff --git a/src/rust/src/libccxr_exports/util.rs b/src/rust/src/libccxr_exports/util.rs index 6c5cfd43c..7665b263a 100644 --- a/src/rust/src/libccxr_exports/util.rs +++ b/src/rust/src/libccxr_exports/util.rs @@ -1,9 +1,10 @@ +use std::ffi::{c_char, c_int}; + #[no_mangle] -pub unsafe extern "C" fn ccxr_hex_to_int( high: c_char, low: c_char) -> c_int { +pub unsafe extern "C" fn ccxr_hex_to_int(high: c_char, low: c_char) -> c_int { lib_ccxr::util::hex::hex_to_int( high as u8 as char, low as u8 as char, - ) .unwrap_or(-1) } From 7b006e226f3b5e71efe8ec90e3a4a5cccb9a2bc5 Mon Sep 17 00:00:00 2001 From: Anayo Anyafulu Date: Tue, 24 Feb 2026 18:46:03 +0200 Subject: [PATCH 3/5] Fix formatting: apply clang-format and cargo fmt --- src/lib_ccx/utility.c | 30 ++++++++++++++-------------- src/rust/src/libccxr_exports/util.rs | 6 +----- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/lib_ccx/utility.c b/src/lib_ccx/utility.c index 6137b3b45..ac5bfc878 100644 --- a/src/lib_ccx/utility.c +++ b/src/lib_ccx/utility.c @@ -272,25 +272,25 @@ void sleep_secs(int secs) extern int ccxr_hex_to_int(char high, char low); int hex_to_int(char high, char low) { - return ccxr_hex_to_int(high, low); + return ccxr_hex_to_int(high, low); } #else int hex_to_int(char high, char low) { - unsigned char h, l; - if (high >= '0' && high <= '9') - h = high - '0'; - else if (high >= 'a' && high <= 'f') - h = high - 'a' + 10; - else - return -1; - if (low >= '0' && low <= '9') - l = low - '0'; - else if (low >= 'a' && low <= 'f') - l = low - 'a' + 10; - else - return -1; - return h * 16 + l; + unsigned char h, l; + if (high >= '0' && high <= '9') + h = high - '0'; + else if (high >= 'a' && high <= 'f') + h = high - 'a' + 10; + else + return -1; + if (low >= '0' && low <= '9') + l = low - '0'; + else if (low >= 'a' && low <= 'f') + l = low - 'a' + 10; + else + return -1; + return h * 16 + l; } #endif diff --git a/src/rust/src/libccxr_exports/util.rs b/src/rust/src/libccxr_exports/util.rs index 7665b263a..3babafb21 100644 --- a/src/rust/src/libccxr_exports/util.rs +++ b/src/rust/src/libccxr_exports/util.rs @@ -2,9 +2,5 @@ use std::ffi::{c_char, c_int}; #[no_mangle] pub unsafe extern "C" fn ccxr_hex_to_int(high: c_char, low: c_char) -> c_int { - lib_ccxr::util::hex::hex_to_int( - high as u8 as char, - low as u8 as char, - ) - .unwrap_or(-1) + lib_ccxr::util::hex::hex_to_int(high as u8 as char, low as u8 as char).unwrap_or(-1) } From 874e603d546c123b3298427c70e9f15719bc1af8 Mon Sep 17 00:00:00 2001 From: Anayo Anyafulu Date: Tue, 24 Feb 2026 23:22:26 +0200 Subject: [PATCH 4/5] Add Safety doc comment to ccxr_hex_to_int --- src/rust/src/libccxr_exports/util.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/rust/src/libccxr_exports/util.rs b/src/rust/src/libccxr_exports/util.rs index 3babafb21..9884fe789 100644 --- a/src/rust/src/libccxr_exports/util.rs +++ b/src/rust/src/libccxr_exports/util.rs @@ -1,6 +1,16 @@ use std::ffi::{c_char, c_int}; +/// Rust equivalent for `hex_to_int` function in C. Uses C-native types as input and output. +/// +/// # Safety +/// +/// `high` and `low` must be valid ASCII characters. Invalid characters will +/// return -1 rather than causing undefined behavior. #[no_mangle] pub unsafe extern "C" fn ccxr_hex_to_int(high: c_char, low: c_char) -> c_int { - lib_ccxr::util::hex::hex_to_int(high as u8 as char, low as u8 as char).unwrap_or(-1) + lib_ccxr::util::hex::hex_to_int( + high as u8 as char, + low as u8 as char, + ) + .unwrap_or(-1) } From a080606af952b5c98e193527274174cfab8ae4a7 Mon Sep 17 00:00:00 2001 From: Anayo Anyafulu Date: Tue, 24 Feb 2026 23:25:14 +0200 Subject: [PATCH 5/5] Fix rustfmt formatting in util.rs --- src/rust/src/libccxr_exports/util.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/rust/src/libccxr_exports/util.rs b/src/rust/src/libccxr_exports/util.rs index 9884fe789..e6734f9e7 100644 --- a/src/rust/src/libccxr_exports/util.rs +++ b/src/rust/src/libccxr_exports/util.rs @@ -8,9 +8,5 @@ use std::ffi::{c_char, c_int}; /// return -1 rather than causing undefined behavior. #[no_mangle] pub unsafe extern "C" fn ccxr_hex_to_int(high: c_char, low: c_char) -> c_int { - lib_ccxr::util::hex::hex_to_int( - high as u8 as char, - low as u8 as char, - ) - .unwrap_or(-1) + lib_ccxr::util::hex::hex_to_int(high as u8 as char, low as u8 as char).unwrap_or(-1) }