Skip to content

Commit 512dace

Browse files
committed
crc-fast-rust: adding clean, clear docs for the tricky bits. fixing the FFI panic issues with errors. regenerated the libcrc_fast.h (header).
1 parent 32aff04 commit 512dace

File tree

3 files changed

+238
-47
lines changed

3 files changed

+238
-47
lines changed

libcrc_fast.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ struct CrcFastDigestHandle *crc_fast_digest_new_with_init_state(enum CrcFastAlgo
8585

8686
/**
8787
* Creates a new Digest to compute CRC checksums using custom parameters
88+
* Returns NULL if parameters are invalid (invalid key count or null pointer)
8889
*/
8990
struct CrcFastDigestHandle *crc_fast_digest_new_with_params(struct CrcFastParams params);
9091

@@ -136,20 +137,23 @@ uint64_t crc_fast_checksum(enum CrcFastAlgorithm algorithm, const char *data, ui
136137

137138
/**
138139
* Helper method to calculate a CRC checksum directly for data using custom parameters
140+
* Returns 0 if parameters are invalid or data is null
139141
*/
140142
uint64_t crc_fast_checksum_with_params(struct CrcFastParams params,
141143
const char *data,
142144
uintptr_t len);
143145

144146
/**
145147
* Helper method to just calculate a CRC checksum directly for a file using algorithm
148+
* Returns 0 if path is null or file I/O fails
146149
*/
147150
uint64_t crc_fast_checksum_file(enum CrcFastAlgorithm algorithm,
148151
const uint8_t *path_ptr,
149152
uintptr_t path_len);
150153

151154
/**
152155
* Helper method to calculate a CRC checksum directly for a file using custom parameters
156+
* Returns 0 if parameters are invalid, path is null, or file I/O fails
153157
*/
154158
uint64_t crc_fast_checksum_file_with_params(struct CrcFastParams params,
155159
const uint8_t *path_ptr,
@@ -165,6 +169,7 @@ uint64_t crc_fast_checksum_combine(enum CrcFastAlgorithm algorithm,
165169

166170
/**
167171
* Combine two CRC checksums using custom parameters
172+
* Returns 0 if parameters are invalid
168173
*/
169174
uint64_t crc_fast_checksum_combine_with_params(struct CrcFastParams params,
170175
uint64_t checksum1,
@@ -184,11 +189,13 @@ struct CrcFastParams crc_fast_get_custom_params(const char *name_ptr,
184189

185190
/**
186191
* Gets the target build properties (CPU architecture and fine-tuning parameters) for this algorithm
192+
* Returns NULL if string conversion fails (should never happen)
187193
*/
188194
const char *crc_fast_get_calculator_target(enum CrcFastAlgorithm algorithm);
189195

190196
/**
191197
* Gets the version of this library
198+
* Returns a pointer to "unknown" if version string is invalid
192199
*/
193200
const char *crc_fast_get_version(void);
194201

src/ffi.rs

Lines changed: 118 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,34 @@ use std::slice;
2020
use std::sync::Mutex;
2121
use std::sync::OnceLock;
2222

23+
/// Error codes for FFI operations
24+
#[repr(C)]
25+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26+
pub enum CrcFastError {
27+
/// Operation completed successfully
28+
Success = 0,
29+
/// Lock was poisoned (thread panicked while holding lock)
30+
LockPoisoned = 1,
31+
/// Null pointer was passed where non-null required
32+
NullPointer = 2,
33+
/// Invalid key count for CRC parameters
34+
InvalidKeyCount = 3,
35+
/// Unsupported CRC width (must be 32 or 64)
36+
UnsupportedWidth = 4,
37+
/// Invalid UTF-8 string
38+
InvalidUtf8 = 5,
39+
/// File I/O error
40+
IoError = 6,
41+
/// Internal string conversion error
42+
StringConversionError = 7,
43+
}
44+
2345
// Global storage for stable key pointers to ensure they remain valid across FFI boundary
2446
static STABLE_KEY_STORAGE: OnceLock<Mutex<HashMap<u64, Box<[u64]>>>> = OnceLock::new();
2547

2648
/// Creates a stable pointer to the keys for FFI usage.
2749
/// The keys are stored in global memory to ensure the pointer remains valid.
50+
/// Returns (pointer, count) on success, or (null, 0) on error.
2851
fn create_stable_key_pointer(keys: &crate::CrcKeysStorage) -> (*const u64, u32) {
2952
let storage = STABLE_KEY_STORAGE.get_or_init(|| Mutex::new(HashMap::new()));
3053

@@ -44,7 +67,10 @@ fn create_stable_key_pointer(keys: &crate::CrcKeysStorage) -> (*const u64, u32)
4467
}
4568
};
4669

47-
let mut storage_map = storage.lock().unwrap();
70+
let mut storage_map = match storage.lock() {
71+
Ok(guard) => guard,
72+
Err(_) => return (std::ptr::null(), 0), // Lock poisoned
73+
};
4874

4975
// Check if we already have this key set stored
5076
if let Some(stored_keys) = storage_map.get(&key_hash) {
@@ -140,34 +166,49 @@ pub struct CrcFastParams {
140166
pub keys: *const u64,
141167
}
142168

143-
// Convert from FFI struct to internal struct
169+
/// Fallible conversion from FFI struct to internal struct
170+
/// Returns None if the parameters are invalid (unsupported key count)
171+
fn try_params_from_ffi(value: &CrcFastParams) -> Option<CrcParams> {
172+
// Validate key pointer
173+
if value.keys.is_null() {
174+
return None;
175+
}
176+
177+
// Convert C array back to appropriate CrcKeysStorage
178+
let keys = unsafe { std::slice::from_raw_parts(value.keys, value.key_count as usize) };
179+
180+
let storage = match value.key_count {
181+
23 => match keys.try_into() {
182+
Ok(arr) => crate::CrcKeysStorage::from_keys_fold_256(arr),
183+
Err(_) => return None,
184+
},
185+
25 => match keys.try_into() {
186+
Ok(arr) => crate::CrcKeysStorage::from_keys_fold_future_test(arr),
187+
Err(_) => return None,
188+
},
189+
_ => return None, // Unsupported key count
190+
};
191+
192+
Some(CrcParams {
193+
algorithm: value.algorithm.into(),
194+
name: "custom", // C interface doesn't need the name field
195+
width: value.width,
196+
poly: value.poly,
197+
init: value.init,
198+
refin: value.refin,
199+
refout: value.refout,
200+
xorout: value.xorout,
201+
check: value.check,
202+
keys: storage,
203+
})
204+
}
205+
206+
// Convert from FFI struct to internal struct (legacy, may panic)
207+
// For backwards compatibility, but prefer try_params_from_ffi
144208
impl From<CrcFastParams> for CrcParams {
145209
fn from(value: CrcFastParams) -> Self {
146-
// Convert C array back to appropriate CrcKeysStorage
147-
let keys = unsafe { std::slice::from_raw_parts(value.keys, value.key_count as usize) };
148-
149-
let storage = match value.key_count {
150-
23 => crate::CrcKeysStorage::from_keys_fold_256(
151-
keys.try_into().expect("Invalid key count for fold_256"),
152-
),
153-
25 => crate::CrcKeysStorage::from_keys_fold_future_test(
154-
keys.try_into().expect("Invalid key count for future_test"),
155-
),
156-
_ => panic!("Unsupported key count: {}", value.key_count),
157-
};
158-
159-
CrcParams {
160-
algorithm: value.algorithm.into(),
161-
name: "custom", // C interface doesn't need the name field
162-
width: value.width,
163-
poly: value.poly,
164-
init: value.init,
165-
refin: value.refin,
166-
refout: value.refout,
167-
xorout: value.xorout,
168-
check: value.check,
169-
keys: storage,
170-
}
210+
try_params_from_ffi(&value)
211+
.expect("Invalid CRC parameters: unsupported key count or null pointer")
171212
}
172213
}
173214

@@ -234,13 +275,19 @@ pub extern "C" fn crc_fast_digest_new_with_init_state(
234275
}
235276

236277
/// Creates a new Digest to compute CRC checksums using custom parameters
278+
/// Returns NULL if parameters are invalid (invalid key count or null pointer)
237279
#[no_mangle]
238280
pub extern "C" fn crc_fast_digest_new_with_params(
239281
params: CrcFastParams,
240282
) -> *mut CrcFastDigestHandle {
241-
let digest = Box::new(Digest::new_with_params(params.into()));
242-
let handle = Box::new(CrcFastDigestHandle(Box::into_raw(digest)));
243-
Box::into_raw(handle)
283+
match try_params_from_ffi(&params) {
284+
Some(crc_params) => {
285+
let digest = Box::new(Digest::new_with_params(crc_params));
286+
let handle = Box::new(CrcFastDigestHandle(Box::into_raw(digest)));
287+
Box::into_raw(handle)
288+
}
289+
None => std::ptr::null_mut(), // Invalid parameters
290+
}
244291
}
245292

246293
/// Updates the Digest with data
@@ -377,6 +424,7 @@ pub extern "C" fn crc_fast_checksum(
377424
}
378425

379426
/// Helper method to calculate a CRC checksum directly for data using custom parameters
427+
/// Returns 0 if parameters are invalid or data is null
380428
#[no_mangle]
381429
pub extern "C" fn crc_fast_checksum_with_params(
382430
params: CrcFastParams,
@@ -386,14 +434,18 @@ pub extern "C" fn crc_fast_checksum_with_params(
386434
if data.is_null() {
387435
return 0;
388436
}
389-
unsafe {
390-
#[allow(clippy::unnecessary_cast)]
391-
let bytes = slice::from_raw_parts(data as *const u8, len);
392-
crate::checksum_with_params(params.into(), bytes)
437+
match try_params_from_ffi(&params) {
438+
Some(crc_params) => unsafe {
439+
#[allow(clippy::unnecessary_cast)]
440+
let bytes = slice::from_raw_parts(data as *const u8, len);
441+
crate::checksum_with_params(crc_params, bytes)
442+
},
443+
None => 0, // Invalid parameters
393444
}
394445
}
395446

396447
/// Helper method to just calculate a CRC checksum directly for a file using algorithm
448+
/// Returns 0 if path is null or file I/O fails
397449
#[no_mangle]
398450
pub extern "C" fn crc_fast_checksum_file(
399451
algorithm: CrcFastAlgorithm,
@@ -410,11 +462,12 @@ pub extern "C" fn crc_fast_checksum_file(
410462
&convert_to_string(path_ptr, path_len),
411463
None,
412464
)
413-
.unwrap()
465+
.unwrap_or(0) // Return 0 on I/O error
414466
}
415467
}
416468

417469
/// Helper method to calculate a CRC checksum directly for a file using custom parameters
470+
/// Returns 0 if parameters are invalid, path is null, or file I/O fails
418471
#[no_mangle]
419472
pub extern "C" fn crc_fast_checksum_file_with_params(
420473
params: CrcFastParams,
@@ -425,13 +478,16 @@ pub extern "C" fn crc_fast_checksum_file_with_params(
425478
return 0;
426479
}
427480

428-
unsafe {
429-
crate::checksum_file_with_params(
430-
params.into(),
431-
&convert_to_string(path_ptr, path_len),
432-
None,
433-
)
434-
.unwrap_or(0) // Return 0 on error instead of panicking
481+
match try_params_from_ffi(&params) {
482+
Some(crc_params) => unsafe {
483+
crate::checksum_file_with_params(
484+
crc_params,
485+
&convert_to_string(path_ptr, path_len),
486+
None,
487+
)
488+
.unwrap_or(0) // Return 0 on error instead of panicking
489+
},
490+
None => 0, // Invalid parameters
435491
}
436492
}
437493

@@ -447,14 +503,20 @@ pub extern "C" fn crc_fast_checksum_combine(
447503
}
448504

449505
/// Combine two CRC checksums using custom parameters
506+
/// Returns 0 if parameters are invalid
450507
#[no_mangle]
451508
pub extern "C" fn crc_fast_checksum_combine_with_params(
452509
params: CrcFastParams,
453510
checksum1: u64,
454511
checksum2: u64,
455512
checksum2_len: u64,
456513
) -> u64 {
457-
crate::checksum_combine_with_params(params.into(), checksum1, checksum2, checksum2_len)
514+
match try_params_from_ffi(&params) {
515+
Some(crc_params) => {
516+
crate::checksum_combine_with_params(crc_params, checksum1, checksum2, checksum2_len)
517+
}
518+
None => 0, // Invalid parameters
519+
}
458520
}
459521

460522
/// Returns the custom CRC parameters for a given set of Rocksoft CRC parameters
@@ -494,7 +556,8 @@ pub extern "C" fn crc_fast_get_custom_params(
494556
algorithm: match width {
495557
32 => CrcFastAlgorithm::Crc32Custom,
496558
64 => CrcFastAlgorithm::Crc64Custom,
497-
_ => panic!("Unsupported width: {width}",),
559+
// Default to 32-bit for unsupported widths (defensive programming)
560+
_ => CrcFastAlgorithm::Crc32Custom,
498561
},
499562
width: params.width,
500563
poly: params.poly,
@@ -509,20 +572,28 @@ pub extern "C" fn crc_fast_get_custom_params(
509572
}
510573

511574
/// Gets the target build properties (CPU architecture and fine-tuning parameters) for this algorithm
575+
/// Returns NULL if string conversion fails (should never happen)
512576
#[no_mangle]
513577
pub extern "C" fn crc_fast_get_calculator_target(algorithm: CrcFastAlgorithm) -> *const c_char {
514578
let target = get_calculator_target(algorithm.into());
515579

516-
std::ffi::CString::new(target).unwrap().into_raw()
580+
std::ffi::CString::new(target)
581+
.map(|s| s.into_raw())
582+
.unwrap_or(std::ptr::null())
517583
}
518584

519585
/// Gets the version of this library
586+
/// Returns a pointer to "unknown" if version string is invalid
520587
#[no_mangle]
521588
pub extern "C" fn crc_fast_get_version() -> *const c_char {
522589
const VERSION: &CStr =
523590
match CStr::from_bytes_with_nul(concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes()) {
524591
Ok(version) => version,
525-
Err(_) => panic!("package version contains null bytes??"),
592+
// Fallback to "unknown" if version string is malformed
593+
Err(_) => match CStr::from_bytes_with_nul(b"unknown\0") {
594+
Ok(v) => v,
595+
Err(_) => unsafe { CStr::from_bytes_with_nul_unchecked(b"unknown\0") },
596+
},
526597
};
527598

528599
VERSION.as_ptr()
@@ -536,6 +607,6 @@ unsafe fn convert_to_string(data: *const u8, len: usize) -> String {
536607
// Safely construct string slice from raw parts
537608
match std::str::from_utf8(slice::from_raw_parts(data, len)) {
538609
Ok(s) => s.to_string(),
539-
Err(_) => panic!("Invalid UTF-8 string"),
610+
Err(_) => String::new(), // Return empty string for invalid UTF-8
540611
}
541612
}

0 commit comments

Comments
 (0)