use crate::generate;
#[cfg(feature = "std")]
use std::collections::HashMap;
#[cfg(feature = "std")]
use std::sync::{OnceLock, RwLock};
#[cfg(all(not(feature = "std"), feature = "cache"))]
use hashbrown::HashMap;
#[cfg(all(not(feature = "std"), feature = "cache"))]
use spin::{Once, RwLock};
#[cfg(feature = "std")]
static CACHE: OnceLock<RwLock<HashMap<CrcParamsCacheKey, [u64; 23]>>> = OnceLock::new();
#[cfg(all(not(feature = "std"), feature = "cache"))]
static CACHE: Once<RwLock<HashMap<CrcParamsCacheKey, [u64; 23]>>> = Once::new();
#[cfg(any(feature = "std", feature = "cache"))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct CrcParamsCacheKey {
pub width: u8,
pub poly: u64,
pub reflected: bool,
}
#[cfg(any(feature = "std", feature = "cache"))]
impl CrcParamsCacheKey {
pub fn new(width: u8, poly: u64, reflected: bool) -> Self {
Self {
width,
poly,
reflected,
}
}
}
#[cfg(feature = "std")]
fn get_cache() -> &'static RwLock<HashMap<CrcParamsCacheKey, [u64; 23]>> {
CACHE.get_or_init(|| RwLock::new(HashMap::new()))
}
#[cfg(all(not(feature = "std"), feature = "cache"))]
fn get_cache() -> &'static RwLock<HashMap<CrcParamsCacheKey, [u64; 23]>> {
CACHE.call_once(|| RwLock::new(HashMap::new()))
}
pub fn get_or_generate_keys(width: u8, poly: u64, reflected: bool) -> [u64; 23] {
#[cfg(feature = "std")]
{
let cache_key = CrcParamsCacheKey::new(width, poly, reflected);
if let Ok(cache) = get_cache().read() {
if let Some(keys) = cache.get(&cache_key) {
return *keys;
}
}
let keys = generate::keys(width, poly, reflected);
let _ = get_cache()
.write()
.map(|mut cache| cache.insert(cache_key, keys));
keys
}
#[cfg(all(not(feature = "std"), feature = "cache"))]
{
let cache_key = CrcParamsCacheKey::new(width, poly, reflected);
{
let cache = get_cache().read();
if let Some(keys) = cache.get(&cache_key) {
return *keys;
}
}
let keys = generate::keys(width, poly, reflected);
{
let mut cache = get_cache().write();
cache.insert(cache_key, keys);
}
keys
}
#[cfg(not(any(feature = "std", feature = "cache")))]
{
generate::keys(width, poly, reflected)
}
}
#[cfg(test)]
pub(crate) fn clear_cache() {
#[cfg(feature = "std")]
{
let _ = get_cache().write().map(|mut cache| cache.clear());
}
#[cfg(all(not(feature = "std"), feature = "cache"))]
{
let mut cache = get_cache().write();
cache.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn test_cache_key_creation() {
let key1 = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
let key2 = CrcParamsCacheKey::new(64, 0x42F0E1EBA9EA3693, false);
assert_eq!(key1.width, 32);
assert_eq!(key1.poly, 0x04C11DB7);
assert!(key1.reflected);
assert_eq!(key2.width, 64);
assert_eq!(key2.poly, 0x42F0E1EBA9EA3693);
assert!(!key2.reflected);
}
#[test]
fn test_cache_key_equality() {
let key1 = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
let key2 = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
let key3 = CrcParamsCacheKey::new(32, 0x04C11DB7, false); let key4 = CrcParamsCacheKey::new(64, 0x04C11DB7, true); let key5 = CrcParamsCacheKey::new(32, 0x1EDC6F41, true);
assert_eq!(key1, key2);
assert_eq!(key1.clone(), key2.clone());
assert_ne!(key1, key3);
assert_ne!(key1, key4);
assert_ne!(key1, key5);
assert_ne!(key3, key4);
assert_ne!(key3, key5);
assert_ne!(key4, key5);
}
#[test]
fn test_cache_key_hashing() {
let key1 = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
let key2 = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
let key3 = CrcParamsCacheKey::new(32, 0x04C11DB7, false);
let mut set = HashSet::new();
set.insert(key1.clone());
set.insert(key2.clone());
set.insert(key3.clone());
assert_eq!(set.len(), 2);
assert!(set.contains(&key1));
assert!(set.contains(&key2));
assert!(set.contains(&key3));
let key4 = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
assert!(set.contains(&key4));
}
#[test]
fn test_cache_hit_scenarios() {
clear_cache();
let keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
let keys2 = get_or_generate_keys(32, 0x04C11DB7, true);
assert_eq!(keys1, keys2);
let keys3 = get_or_generate_keys(32, 0x04C11DB7, true);
let keys4 = get_or_generate_keys(32, 0x04C11DB7, true);
assert_eq!(keys1, keys3);
assert_eq!(keys1, keys4);
assert_eq!(keys2, keys3);
assert_eq!(keys2, keys4);
}
#[test]
fn test_cache_miss_scenarios() {
clear_cache();
let keys_32 = get_or_generate_keys(32, 0x04C11DB7, true);
let keys_64 = get_or_generate_keys(64, 0x04C11DB7, true);
assert_ne!(keys_32, keys_64);
let keys_poly1 = get_or_generate_keys(32, 0x04C11DB7, true);
let keys_poly2 = get_or_generate_keys(32, 0x1EDC6F41, true);
assert_ne!(keys_poly1, keys_poly2);
let keys_refl_true = get_or_generate_keys(32, 0x04C11DB7, true);
let keys_refl_false = get_or_generate_keys(32, 0x04C11DB7, false);
assert_ne!(keys_refl_true, keys_refl_false);
let keys_32_again = get_or_generate_keys(32, 0x04C11DB7, true);
let keys_64_again = get_or_generate_keys(64, 0x04C11DB7, true);
let keys_poly1_again = get_or_generate_keys(32, 0x04C11DB7, true);
let keys_poly2_again = get_or_generate_keys(32, 0x1EDC6F41, true);
let keys_refl_true_again = get_or_generate_keys(32, 0x04C11DB7, true);
let keys_refl_false_again = get_or_generate_keys(32, 0x04C11DB7, false);
assert_eq!(keys_32, keys_32_again);
assert_eq!(keys_64, keys_64_again);
assert_eq!(keys_poly1, keys_poly1_again);
assert_eq!(keys_poly2, keys_poly2_again);
assert_eq!(keys_refl_true, keys_refl_true_again);
assert_eq!(keys_refl_false, keys_refl_false_again);
}
#[test]
fn test_cached_keys_identical_to_generated_keys() {
clear_cache();
let width = 32;
let poly = 0x04C11DB7;
let reflected = true;
let direct_keys = generate::keys(width, poly, reflected);
let cached_keys_first = get_or_generate_keys(width, poly, reflected);
let cached_keys_second = get_or_generate_keys(width, poly, reflected);
assert_eq!(direct_keys, cached_keys_first);
assert_eq!(direct_keys, cached_keys_second);
assert_eq!(cached_keys_first, cached_keys_second);
let width64 = 64;
let poly64 = 0x42F0E1EBA9EA3693;
let reflected64 = false;
let direct_keys64 = generate::keys(width64, poly64, reflected64);
let cached_keys64_first = get_or_generate_keys(width64, poly64, reflected64);
let cached_keys64_second = get_or_generate_keys(width64, poly64, reflected64);
assert_eq!(direct_keys64, cached_keys64_first);
assert_eq!(direct_keys64, cached_keys64_second);
assert_eq!(cached_keys64_first, cached_keys64_second);
assert_ne!(direct_keys, direct_keys64);
assert_ne!(cached_keys_first, cached_keys64_first);
}
#[test]
fn test_multiple_parameter_combinations() {
clear_cache();
let test_cases = [
(32, 0x04C11DB7, true), (32, 0x04C11DB7, false), (32, 0x1EDC6F41, true), (64, 0x42F0E1EBA9EA3693, true), (64, 0x42F0E1EBA9EA3693, false), (64, 0xD800000000000000, true), ];
let mut all_keys = Vec::new();
for &(width, poly, reflected) in &test_cases {
let keys = get_or_generate_keys(width, poly, reflected);
all_keys.push(keys);
}
for i in 0..all_keys.len() {
for j in (i + 1)..all_keys.len() {
assert_ne!(
all_keys[i], all_keys[j],
"Keys should be different for test cases {} and {}",
i, j
);
}
}
for (i, &(width, poly, reflected)) in test_cases.iter().enumerate() {
let cached_keys = get_or_generate_keys(width, poly, reflected);
assert_eq!(
all_keys[i], cached_keys,
"Cache hit should return same keys for test case {}",
i
);
}
}
#[test]
fn test_cache_management_utilities() {
clear_cache();
let keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
let keys2 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
let cached_keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
let cached_keys2 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
assert_eq!(keys1, cached_keys1);
assert_eq!(keys2, cached_keys2);
clear_cache();
let new_keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
assert_eq!(keys1, new_keys1); }
#[test]
fn test_cache_error_handling() {
clear_cache();
clear_cache();
let keys = get_or_generate_keys(32, 0x04C11DB7, true);
clear_cache();
let keys2 = get_or_generate_keys(32, 0x04C11DB7, true);
assert_eq!(keys, keys2);
}
#[test]
fn test_cache_key_debug_and_clone() {
let key = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
let debug_str = format!("{:?}", key);
assert!(debug_str.contains("CrcParamsCacheKey"));
assert!(debug_str.contains("32"));
assert!(debug_str.contains("0x4c11db7") || debug_str.contains("79764919"));
assert!(debug_str.contains("true"));
let cloned_key = key.clone();
assert_eq!(key, cloned_key);
assert_eq!(key.width, cloned_key.width);
assert_eq!(key.poly, cloned_key.poly);
assert_eq!(key.reflected, cloned_key.reflected);
}
#[test]
fn test_concurrent_cache_reads() {
use std::sync::{Arc, Barrier};
use std::thread;
clear_cache();
let expected_keys = get_or_generate_keys(32, 0x04C11DB7, true);
let num_threads = 8;
let barrier = Arc::new(Barrier::new(num_threads));
let mut handles = Vec::new();
for i in 0..num_threads {
let barrier_clone = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier_clone.wait();
let keys = get_or_generate_keys(32, 0x04C11DB7, true);
(i, keys)
});
handles.push(handle);
}
let mut results = Vec::new();
for handle in handles {
results.push(handle.join().expect("Thread should not panic"));
}
assert_eq!(results.len(), num_threads);
for (thread_id, keys) in results {
assert_eq!(
keys, expected_keys,
"Thread {} should get same cached keys",
thread_id
);
}
}
#[test]
#[allow(clippy::needless_range_loop)] fn test_concurrent_cache_writes() {
use std::sync::{Arc, Barrier};
use std::thread;
clear_cache();
let num_threads = 6;
let barrier = Arc::new(Barrier::new(num_threads));
let mut handles = Vec::new();
let test_params = [
(32, 0x04C11DB7, true),
(32, 0x04C11DB7, false),
(32, 0x1EDC6F41, true),
(64, 0x42F0E1EBA9EA3693, true),
(64, 0x42F0E1EBA9EA3693, false),
(64, 0xD800000000000000, true),
];
for i in 0..num_threads {
let barrier_clone = Arc::clone(&barrier);
let (width, poly, reflected) = test_params[i];
let handle = thread::spawn(move || {
barrier_clone.wait();
let keys = get_or_generate_keys(width, poly, reflected);
(i, width, poly, reflected, keys)
});
handles.push(handle);
}
let mut results = Vec::new();
for handle in handles {
results.push(handle.join().expect("Thread should not panic"));
}
assert_eq!(results.len(), num_threads);
for (thread_id, width, poly, reflected, keys) in results {
let expected_keys = generate::keys(width, poly, reflected);
assert_eq!(
keys, expected_keys,
"Thread {} should generate correct keys for params ({}, {:#x}, {})",
thread_id, width, poly, reflected
);
let cached_keys = get_or_generate_keys(width, poly, reflected);
assert_eq!(
keys, cached_keys,
"Thread {} keys should be cached",
thread_id
);
}
}
#[test]
fn test_read_write_contention() {
use std::sync::{Arc, Barrier};
use std::thread;
use std::time::Duration;
clear_cache();
let _keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
let _keys2 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
let num_readers = 6;
let num_writers = 3;
let total_threads = num_readers + num_writers;
let barrier = Arc::new(Barrier::new(total_threads));
let mut handles = Vec::new();
for i in 0..num_readers {
let barrier_clone = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier_clone.wait();
let mut read_count = 0;
let start = std::time::Instant::now();
while start.elapsed() < Duration::from_millis(50) {
let keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
let keys2 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
assert_eq!(keys1.len(), 23);
assert_eq!(keys2.len(), 23);
read_count += 1;
}
(format!("reader_{}", i), read_count)
});
handles.push(handle);
}
for i in 0..num_writers {
let barrier_clone = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier_clone.wait();
let mut write_count = 0;
let start = std::time::Instant::now();
while start.elapsed() < Duration::from_millis(50) {
let poly = 0x1EDC6F41 + (i as u64 * 0x1000) + (write_count as u64);
let keys = get_or_generate_keys(32, poly, true);
assert_eq!(keys.len(), 23);
write_count += 1;
}
(format!("writer_{}", i), write_count)
});
handles.push(handle);
}
let mut results = Vec::new();
for handle in handles {
results.push(handle.join().expect("Thread should not panic"));
}
assert_eq!(results.len(), total_threads);
let reader_results: Vec<_> = results
.iter()
.filter(|(name, _)| name.starts_with("reader_"))
.collect();
let writer_results: Vec<_> = results
.iter()
.filter(|(name, _)| name.starts_with("writer_"))
.collect();
assert_eq!(reader_results.len(), num_readers);
assert_eq!(writer_results.len(), num_writers);
for (name, count) in &results {
assert!(*count > 0, "Thread {} should have made progress", name);
}
}
#[test]
fn test_cache_consistency_under_concurrent_access() {
use std::sync::{Arc, Barrier};
use std::thread;
clear_cache();
let num_threads = 10;
let barrier = Arc::new(Barrier::new(num_threads));
let mut handles = Vec::new();
for i in 0..num_threads {
let barrier_clone = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier_clone.wait();
let keys = get_or_generate_keys(32, 0x04C11DB7, true);
(i, keys)
});
handles.push(handle);
}
let mut results = Vec::new();
for handle in handles {
results.push(handle.join().expect("Thread should not panic"));
}
assert_eq!(results.len(), num_threads);
let first_keys = results[0].1;
for (thread_id, keys) in results {
assert_eq!(
keys, first_keys,
"Thread {} should get identical keys to other threads",
thread_id
);
}
let expected_keys = generate::keys(32, 0x04C11DB7, true);
assert_eq!(
first_keys, expected_keys,
"Cached keys should match directly generated keys"
);
let final_keys = get_or_generate_keys(32, 0x04C11DB7, true);
assert_eq!(
final_keys, first_keys,
"Final cache access should return same keys"
);
}
#[test]
fn test_mixed_concurrent_operations() {
use std::sync::{Arc, Barrier};
use std::thread;
use std::time::Duration;
clear_cache();
let num_threads = 8;
let barrier = Arc::new(Barrier::new(num_threads));
let mut handles = Vec::new();
for i in 0..num_threads {
let barrier_clone = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier_clone.wait();
let mut operations = 0;
let start = std::time::Instant::now();
while start.elapsed() < Duration::from_millis(30) {
match i % 4 {
0 => {
let _keys = get_or_generate_keys(32, 0x04C11DB7, true);
}
1 => {
let poly = 0x1EDC6F41 + (operations as u64);
let _keys = get_or_generate_keys(32, poly, true);
}
2 => {
let _keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
let _keys2 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
}
3 => {
if operations % 10 == 0 {
clear_cache();
}
let _keys = get_or_generate_keys(32, 0x04C11DB7, true);
}
_ => unreachable!(),
}
operations += 1;
}
(i, operations)
});
handles.push(handle);
}
let mut results = Vec::new();
for handle in handles {
results.push(handle.join().expect("Thread should not panic"));
}
assert_eq!(results.len(), num_threads);
for (thread_id, operations) in results {
assert!(
operations > 0,
"Thread {} should have completed some operations",
thread_id
);
}
let final_keys = get_or_generate_keys(32, 0x04C11DB7, true);
let expected_keys = generate::keys(32, 0x04C11DB7, true);
assert_eq!(
final_keys, expected_keys,
"Cache should still work correctly after concurrent operations"
);
}
#[test]
fn test_cache_lock_poisoning_recovery() {
use std::panic;
use std::sync::{Arc, Mutex};
use std::thread;
clear_cache();
let expected_keys = get_or_generate_keys(32, 0x04C11DB7, true);
let poisoned_flag = Arc::new(Mutex::new(false));
let poisoned_flag_clone = Arc::clone(&poisoned_flag);
let handle = thread::spawn(move || {
let _guard = poisoned_flag_clone.lock().unwrap();
panic!("Simulated panic");
});
let result = handle.join();
assert!(result.is_err(), "Thread should have panicked");
let keys_after_panic = get_or_generate_keys(32, 0x04C11DB7, true);
assert_eq!(
keys_after_panic, expected_keys,
"Cache should still work after simulated panic scenario"
);
let new_keys = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
assert_eq!(new_keys.len(), 23, "New cache entries should still work");
let cached_new_keys = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
assert_eq!(new_keys, cached_new_keys, "New keys should be cached");
}
#[test]
fn test_cache_fallback_to_direct_generation() {
clear_cache();
let expected_keys_32 = generate::keys(32, 0x04C11DB7, true);
let expected_keys_64 = generate::keys(64, 0x42F0E1EBA9EA3693, false);
let cached_keys_32 = get_or_generate_keys(32, 0x04C11DB7, true);
let cached_keys_64 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
assert_eq!(
cached_keys_32, expected_keys_32,
"CRC32 keys should be correct even with cache issues"
);
assert_eq!(
cached_keys_64, expected_keys_64,
"CRC64 keys should be correct even with cache issues"
);
for _ in 0..5 {
let keys_32 = get_or_generate_keys(32, 0x04C11DB7, true);
let keys_64 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
assert_eq!(
keys_32, expected_keys_32,
"Repeated calls should return consistent CRC32 keys"
);
assert_eq!(
keys_64, expected_keys_64,
"Repeated calls should return consistent CRC64 keys"
);
}
}
#[test]
fn test_cache_operations_under_memory_pressure() {
clear_cache();
let mut test_keys = Vec::new();
let num_entries = 100;
for i in 0..num_entries {
let poly = 0x04C11DB7 + (i as u64);
let reflected = i % 2 == 0;
let width = if i % 3 == 0 { 64 } else { 32 };
let keys = get_or_generate_keys(width, poly, reflected);
test_keys.push((width, poly, reflected, keys));
}
for (i, &(width, poly, reflected, ref expected_keys)) in test_keys.iter().enumerate() {
let cached_keys = get_or_generate_keys(width, poly, reflected);
assert_eq!(
cached_keys, *expected_keys,
"Entry {} should be correctly cached",
i
);
}
let new_keys = get_or_generate_keys(32, 0x1EDC6F41, true);
assert_eq!(
new_keys.len(),
23,
"New entries should still work under memory pressure"
);
let cached_new_keys = get_or_generate_keys(32, 0x1EDC6F41, true);
assert_eq!(new_keys, cached_new_keys, "New entry should be cached");
clear_cache();
let post_clear_keys = get_or_generate_keys(32, 0x04C11DB7, true);
assert_eq!(
post_clear_keys.len(),
23,
"Cache should work after clearing under memory pressure"
);
}
#[test]
fn test_cache_error_recovery_patterns() {
clear_cache();
for i in 0..50 {
let poly = 0x04C11DB7 + (i as u64 % 10); let keys = get_or_generate_keys(32, poly, true);
assert_eq!(keys.len(), 23, "Rapid operation {} should succeed", i);
}
let base_keys = get_or_generate_keys(32, 0x04C11DB7, true);
for i in 0..20 {
let hit_keys = get_or_generate_keys(32, 0x04C11DB7, true);
assert_eq!(hit_keys, base_keys, "Cache hit {} should be consistent", i);
let miss_keys = get_or_generate_keys(32, 0x1EDC6F41 + (i as u64), false);
assert_eq!(miss_keys.len(), 23, "Cache miss {} should succeed", i);
}
for i in 0..10 {
let keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
let keys2 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
if i % 3 == 0 {
clear_cache();
}
let keys3 = get_or_generate_keys(32, 0x04C11DB7, true);
let keys4 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
assert_eq!(keys1, keys3, "Keys should be consistent across clears");
assert_eq!(keys2, keys4, "Keys should be consistent across clears");
}
}
#[test]
fn test_cache_concurrent_error_scenarios() {
use std::sync::{Arc, Barrier};
use std::thread;
use std::time::Duration;
clear_cache();
let num_threads = 8;
let barrier = Arc::new(Barrier::new(num_threads));
let mut handles = Vec::new();
for i in 0..num_threads {
let barrier_clone = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier_clone.wait();
let mut operations = 0;
let errors = 0;
let start = std::time::Instant::now();
while start.elapsed() < Duration::from_millis(100) {
match operations % 5 {
0 => {
let _keys = get_or_generate_keys(32, 0x04C11DB7, true);
}
1 => {
let poly = 0x1EDC6F41 + (operations as u64);
let _keys = get_or_generate_keys(32, poly, true);
}
2 => {
clear_cache();
}
3 => {
let _keys32 = get_or_generate_keys(32, 0x04C11DB7, true);
let _keys64 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
}
4 => {
for _ in 0..5 {
let _keys = get_or_generate_keys(32, 0x04C11DB7, true);
}
}
_ => unreachable!(),
}
operations += 1;
}
(i, operations, errors)
});
handles.push(handle);
}
let mut results = Vec::new();
for handle in handles {
match handle.join() {
Ok(result) => results.push(result),
Err(_) => panic!("Thread should not panic during error scenarios"),
}
}
assert_eq!(results.len(), num_threads);
for (thread_id, operations, errors) in results {
assert!(
operations > 0,
"Thread {} should have completed operations",
thread_id
);
assert_eq!(
errors, 0,
"Thread {} should not have unhandled errors",
thread_id
);
}
let final_keys = get_or_generate_keys(32, 0x04C11DB7, true);
let expected_keys = generate::keys(32, 0x04C11DB7, true);
assert_eq!(
final_keys, expected_keys,
"Cache should still work correctly after concurrent error scenarios"
);
}
#[test]
fn test_cache_memory_allocation_non_stress() {
cache_memory_allocation_stress(2);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_cache_memory_allocation_stress() {
cache_memory_allocation_stress(1000);
}
fn cache_memory_allocation_stress(count: i32) {
clear_cache();
let mut created_entries = Vec::new();
let stress_count = count;
for i in 0..stress_count {
let width = if i % 2 == 0 { 32 } else { 64 };
let poly = 0x04C11DB7 + (i as u64);
let reflected = i % 3 == 0;
let keys = get_or_generate_keys(width, poly, reflected);
created_entries.push((width, poly, reflected, keys));
assert_eq!(keys.len(), 23, "Entry {} should have valid keys", i);
}
for (i, &(width, poly, reflected, ref expected_keys)) in created_entries.iter().enumerate()
{
let retrieved_keys = get_or_generate_keys(width, poly, reflected);
assert_eq!(
retrieved_keys, *expected_keys,
"Entry {} should be retrievable after stress test",
i
);
}
let new_keys = get_or_generate_keys(32, 0xFFFFFFFF, true);
assert_eq!(
new_keys.len(),
23,
"New entries should work after memory stress"
);
clear_cache();
let post_stress_keys = get_or_generate_keys(32, 0x04C11DB7, true);
assert_eq!(
post_stress_keys.len(),
23,
"Cache should work after memory stress and clearing"
);
}
#[test]
fn test_crc_params_new_behavior_unchanged() {
use crate::CrcParams;
clear_cache();
let params1 = CrcParams::new(
"TEST_CRC32",
32,
0x04C11DB7,
0xFFFFFFFF,
true,
0xFFFFFFFF,
0xCBF43926,
);
let params2 = CrcParams::new(
"TEST_CRC32",
32,
0x04C11DB7,
0xFFFFFFFF,
true,
0xFFFFFFFF,
0xCBF43926,
);
assert_eq!(params1.name, params2.name);
assert_eq!(params1.width, params2.width);
assert_eq!(params1.poly, params2.poly);
assert_eq!(params1.init, params2.init);
assert_eq!(params1.refin, params2.refin);
assert_eq!(params1.refout, params2.refout);
assert_eq!(params1.xorout, params2.xorout);
assert_eq!(params1.check, params2.check);
assert_eq!(params1.keys, params2.keys);
let params64_1 = CrcParams::new(
"TEST_CRC64",
64,
0x42F0E1EBA9EA3693,
0xFFFFFFFFFFFFFFFF,
false,
0x0,
0x6C40DF5F0B497347,
);
let params64_2 = CrcParams::new(
"TEST_CRC64",
64,
0x42F0E1EBA9EA3693,
0xFFFFFFFFFFFFFFFF,
false,
0x0,
0x6C40DF5F0B497347,
);
assert_eq!(params64_1.name, params64_2.name);
assert_eq!(params64_1.width, params64_2.width);
assert_eq!(params64_1.poly, params64_2.poly);
assert_eq!(params64_1.init, params64_2.init);
assert_eq!(params64_1.refin, params64_2.refin);
assert_eq!(params64_1.refout, params64_2.refout);
assert_eq!(params64_1.xorout, params64_2.xorout);
assert_eq!(params64_1.check, params64_2.check);
assert_eq!(params64_1.keys, params64_2.keys);
}
#[test]
fn test_existing_crc_parameter_combinations() {
use crate::test::consts::TEST_ALL_CONFIGS;
clear_cache();
for config in TEST_ALL_CONFIGS {
let params = crate::CrcParams::new(
config.get_name(),
config.get_width(),
config.get_poly(),
config.get_init(),
config.get_refin(),
config.get_xorout(),
config.get_check(),
);
assert_eq!(params.name, config.get_name());
assert_eq!(params.width, config.get_width());
assert_eq!(params.poly, config.get_poly());
assert_eq!(params.init, config.get_init());
assert_eq!(params.refin, config.get_refin());
assert_eq!(params.refout, config.get_refin());
assert_eq!(params.xorout, config.get_xorout());
assert_eq!(params.check, config.get_check());
let expected_keys = config.get_keys();
assert_eq!(
params.keys,
expected_keys,
"Keys mismatch for {}: expected {:?}, got {:?}",
config.get_name(),
expected_keys,
params.keys
);
}
}
#[test]
fn test_cached_vs_uncached_results_identical() {
clear_cache();
let test_cases = [
(32, 0x04C11DB7, true), (32, 0x04C11DB7, false), (32, 0x1EDC6F41, true), (64, 0x42F0E1EBA9EA3693, true), (64, 0x42F0E1EBA9EA3693, false), (64, 0xD800000000000000, true), ];
for &(width, poly, reflected) in &test_cases {
let uncached_keys = generate::keys(width, poly, reflected);
clear_cache();
let params1 =
crate::CrcParams::new("TEST", width, poly, 0xFFFFFFFFFFFFFFFF, reflected, 0x0, 0x0);
let params2 =
crate::CrcParams::new("TEST", width, poly, 0xFFFFFFFFFFFFFFFF, reflected, 0x0, 0x0);
assert_eq!(
uncached_keys, params1.keys,
"Uncached keys should match CrcParams keys for width={}, poly={:#x}, reflected={}",
width, poly, reflected
);
assert_eq!(
params1.keys, params2.keys,
"Cached and uncached CrcParams should have identical keys for width={}, poly={:#x}, reflected={}",
width, poly, reflected
);
assert_eq!(
uncached_keys, params2.keys,
"All key generation methods should produce identical results for width={}, poly={:#x}, reflected={}",
width, poly, reflected
);
}
}
#[test]
fn test_multiple_crc_params_instances_use_cached_keys() {
clear_cache();
let width = 32;
let poly = 0x04C11DB7;
let reflected = true;
let init = 0xFFFFFFFF;
let xorout = 0xFFFFFFFF;
let check = 0xCBF43926;
let params1 = crate::CrcParams::new("TEST1", width, poly, init, reflected, xorout, check);
let params2 = crate::CrcParams::new("TEST2", width, poly, init, reflected, xorout, check);
let params3 = crate::CrcParams::new("TEST3", width, poly, init, reflected, xorout, check);
let params4 = crate::CrcParams::new("TEST4", width, poly, init, reflected, xorout, check);
assert_eq!(
params1.keys, params2.keys,
"Instance 1 and 2 should have identical keys"
);
assert_eq!(
params1.keys, params3.keys,
"Instance 1 and 3 should have identical keys"
);
assert_eq!(
params1.keys, params4.keys,
"Instance 1 and 4 should have identical keys"
);
assert_eq!(
params2.keys, params3.keys,
"Instance 2 and 3 should have identical keys"
);
assert_eq!(
params2.keys, params4.keys,
"Instance 2 and 4 should have identical keys"
);
assert_eq!(
params3.keys, params4.keys,
"Instance 3 and 4 should have identical keys"
);
let expected_keys = generate::keys(width, poly, reflected);
assert_eq!(
params1.keys, expected_keys,
"Cached keys should be mathematically correct"
);
let params5 = crate::CrcParams::new(
"DIFFERENT_NAME",
width,
poly,
0x12345678,
reflected,
0x87654321,
0xABCDEF,
);
assert_eq!(
params1.keys, params5.keys,
"Different init/xorout/check/name should not affect cached keys"
);
let width64 = 64;
let poly64 = 0x42F0E1EBA9EA3693;
let reflected64 = false;
let params64_1 = crate::CrcParams::new(
"CRC64_1",
width64,
poly64,
0xFFFFFFFFFFFFFFFF,
reflected64,
0x0,
0x0,
);
let params64_2 = crate::CrcParams::new(
"CRC64_2",
width64,
poly64,
0x0,
reflected64,
0xFFFFFFFFFFFFFFFF,
0x12345,
);
let params64_3 = crate::CrcParams::new(
"CRC64_3",
width64,
poly64,
0x123456789ABCDEF0,
reflected64,
0x0FEDCBA987654321,
0x999,
);
assert_eq!(
params64_1.keys, params64_2.keys,
"CRC64 instances should have identical keys"
);
assert_eq!(
params64_1.keys, params64_3.keys,
"CRC64 instances should have identical keys"
);
let expected_keys64 = generate::keys(width64, poly64, reflected64);
assert_eq!(
params64_1.keys, expected_keys64,
"CRC64 cached keys should be mathematically correct"
);
}
#[test]
fn test_crc_params_api_compatibility() {
use crate::{CrcAlgorithm, CrcParams};
clear_cache();
let params = CrcParams::new(
"API_TEST", 32, 0x04C11DB7, 0xFFFFFFFF, true, 0xFFFFFFFF, 0xCBF43926,
);
let _algorithm: CrcAlgorithm = params.algorithm;
let _name: &'static str = params.name;
let _width: u8 = params.width;
let _poly: u64 = params.poly;
let _init: u64 = params.init;
let _refin: bool = params.refin;
let _refout: bool = params.refout;
let _xorout: u64 = params.xorout;
let _check: u64 = params.check;
let _keys: [u64; 23] = params.keys.to_keys_array_23();
assert!(matches!(params.algorithm, CrcAlgorithm::CrcCustom));
let params_copy = params;
let params_clone = params;
assert_eq!(params.keys, params_copy.keys);
assert_eq!(params.keys, params_clone.keys);
let debug_str = format!("{:?}", params);
assert!(debug_str.contains("CrcParams"));
assert!(debug_str.contains("API_TEST"));
}
#[test]
fn test_crc_params_with_all_standard_algorithms() {
use crate::test::consts::TEST_ALL_CONFIGS;
clear_cache();
for config in TEST_ALL_CONFIGS {
let params = crate::CrcParams::new(
config.get_name(),
config.get_width(),
config.get_poly(),
config.get_init(),
config.get_refin(),
config.get_xorout(),
config.get_check(),
);
assert_eq!(params.name, config.get_name());
assert_eq!(params.width, config.get_width());
assert_eq!(params.poly, config.get_poly());
assert_eq!(params.init, config.get_init());
assert_eq!(params.refin, config.get_refin());
assert_eq!(params.refout, config.get_refin());
assert_eq!(params.xorout, config.get_xorout());
assert_eq!(params.check, config.get_check());
assert_eq!(
params.keys,
config.get_keys(),
"Keys should match expected values for {}",
config.get_name()
);
let params2 = crate::CrcParams::new(
"CACHED_VERSION", config.get_width(),
config.get_poly(),
0x12345678, config.get_refin(),
0x87654321, 0xABCDEF, );
assert_eq!(
params.keys,
params2.keys,
"Cached keys should be identical for {}",
config.get_name()
);
}
}
#[test]
fn test_crc_params_edge_cases() {
clear_cache();
let params_min_poly = crate::CrcParams::new("MIN_POLY", 32, 0x1, 0x0, false, 0x0, 0x0);
let params_max_poly =
crate::CrcParams::new("MAX_POLY", 32, 0xFFFFFFFF, 0x0, false, 0x0, 0x0);
assert_eq!(params_min_poly.width, 32);
assert_eq!(params_min_poly.poly, 0x1);
assert_eq!(params_max_poly.width, 32);
assert_eq!(params_max_poly.poly, 0xFFFFFFFF);
let params_reflected =
crate::CrcParams::new("REFLECTED", 32, 0x04C11DB7, 0x0, true, 0x0, 0x0);
let params_normal = crate::CrcParams::new("NORMAL", 32, 0x04C11DB7, 0x0, false, 0x0, 0x0);
assert_ne!(params_reflected.keys, params_normal.keys);
assert!(params_reflected.refin);
assert!(params_reflected.refout);
assert!(!params_normal.refin);
assert!(!params_normal.refout);
let params64_min = crate::CrcParams::new("CRC64_MIN", 64, 0x1, 0x0, false, 0x0, 0x0);
let params64_max =
crate::CrcParams::new("CRC64_MAX", 64, 0xFFFFFFFFFFFFFFFF, 0x0, false, 0x0, 0x0);
assert_eq!(params64_min.width, 64);
assert_eq!(params64_min.poly, 0x1);
assert_eq!(params64_max.width, 64);
assert_eq!(params64_max.poly, 0xFFFFFFFFFFFFFFFF);
assert_eq!(params_min_poly.keys.key_count(), 23);
assert_eq!(params_max_poly.keys.key_count(), 23);
assert_eq!(params_reflected.keys.key_count(), 23);
assert_eq!(params_normal.keys.key_count(), 23);
assert_eq!(params64_min.keys.key_count(), 23);
assert_eq!(params64_max.keys.key_count(), 23);
}
#[test]
fn test_crc_params_concurrent_creation() {
use std::sync::{Arc, Barrier};
use std::thread;
clear_cache();
let num_threads = 8;
let barrier = Arc::new(Barrier::new(num_threads));
let mut handles = Vec::new();
for i in 0..num_threads {
let barrier_clone = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier_clone.wait();
let params = crate::CrcParams::new(
"CONCURRENT_TEST",
32,
0x04C11DB7,
0xFFFFFFFF,
true,
0xFFFFFFFF,
0xCBF43926,
);
(i, params)
});
handles.push(handle);
}
let mut results = Vec::new();
for handle in handles {
results.push(handle.join().expect("Thread should not panic"));
}
assert_eq!(results.len(), num_threads);
let first_keys = results[0].1.keys;
for (thread_id, params) in results {
assert_eq!(
params.keys, first_keys,
"Thread {} should have identical keys to other threads",
thread_id
);
assert_eq!(params.name, "CONCURRENT_TEST");
assert_eq!(params.width, 32);
assert_eq!(params.poly, 0x04C11DB7);
assert_eq!(params.init, 0xFFFFFFFF);
assert!(params.refin);
assert!(params.refout);
assert_eq!(params.xorout, 0xFFFFFFFF);
assert_eq!(params.check, 0xCBF43926);
}
let expected_keys = generate::keys(32, 0x04C11DB7, true);
assert_eq!(
first_keys, expected_keys,
"All concurrent CrcParams should have correct keys"
);
}
#[test]
fn test_lock_poisoning_recovery() {
use std::sync::{Arc, Barrier};
use std::thread;
clear_cache();
let keys_before = get_or_generate_keys(32, 0x04C11DB7, true);
assert_eq!(keys_before.len(), 23);
let num_threads = 4;
let barrier = Arc::new(Barrier::new(num_threads));
let mut handles = Vec::new();
for i in 0..num_threads {
let barrier_clone = Arc::clone(&barrier);
let handle = thread::spawn(move || {
barrier_clone.wait();
for j in 0..20 {
let poly = 0x04C11DB7 + (i as u64 * 1000) + (j as u64);
let keys = get_or_generate_keys(32, poly, true);
assert_eq!(
keys.len(),
23,
"Thread {} iteration {} should return valid keys",
i,
j
);
if j % 7 == 0 {
clear_cache();
}
}
i
});
handles.push(handle);
}
for handle in handles {
let thread_id = handle.join().expect("Thread should not panic");
assert!(thread_id < num_threads);
}
let keys_after = get_or_generate_keys(32, 0x04C11DB7, true);
assert_eq!(keys_after.len(), 23);
let expected_keys = generate::keys(32, 0x04C11DB7, true);
assert_eq!(keys_after, expected_keys);
}
#[test]
fn test_cache_behavior_with_thread_local_access() {
use std::thread;
clear_cache();
let keys_main = get_or_generate_keys(32, 0x04C11DB7, true);
let handle = thread::spawn(|| {
get_or_generate_keys(32, 0x04C11DB7, true)
});
let keys_from_thread = handle.join().expect("Thread should not panic");
assert_eq!(keys_main, keys_from_thread);
let mut thread_keys = Vec::new();
for i in 0..5 {
let handle = thread::spawn(move || {
let keys = get_or_generate_keys(32, 0x04C11DB7, true);
(i, keys)
});
let (thread_id, keys) = handle.join().expect("Thread should not panic");
thread_keys.push((thread_id, keys));
}
for (thread_id, keys) in thread_keys {
assert_eq!(
keys, keys_main,
"Thread {} should get same cached keys",
thread_id
);
}
}
}