OXYGENHUB
◆ v0.4.2 oxygen / vendor / crc-fast / src / cache.rs
62.6 KiB · Rust · last changed Strata: the cloud stores send through ureq, so every release target links 1 month ago source blame history raw
2cd40ea1 Andy McCall ↶ 1
// Copyright 2025 Don MacAskill. Licensed under MIT or Apache-2.0.
2
3
//! CRC parameter caching system
4
//!
5
//! This module provides a thread-safe cache for CRC folding keys to avoid expensive
6
//! regeneration when the same CRC parameters are used multiple times. The cache uses
7
//! a read-write lock pattern optimized for the common case of cache hits.
8
//!
9
//! # Performance Characteristics
10
//!
11
//! - Cache hits: ~50-100x faster than key generation
12
//! - Cache misses: ~100-200ns overhead compared to direct generation
13
//! - Memory usage: ~200 bytes per unique parameter set
14
//! - Thread safety: Multiple concurrent readers, exclusive writers
15
//!
16
//! # Usage
17
//!
18
//! The cache is used automatically by `CrcParams::new()` and requires no manual management.
19
//! The cache is transparent to users and handles all memory management internally.
20
21
use crate::generate;
22
23
#[cfg(feature = "std")]
24
use std::collections::HashMap;
25
#[cfg(feature = "std")]
26
use std::sync::{OnceLock, RwLock};
27
28
#[cfg(all(not(feature = "std"), feature = "cache"))]
29
use hashbrown::HashMap;
30
#[cfg(all(not(feature = "std"), feature = "cache"))]
31
use spin::{Once, RwLock};
32
33
/// Global cache storage for CRC parameter keys
34
///
35
/// Uses OnceLock for thread-safe lazy initialization and RwLock for concurrent access.
36
/// The cache maps parameter combinations to their pre-computed folding keys.
37
#[cfg(feature = "std")]
38
static CACHE: OnceLock<RwLock<HashMap<CrcParamsCacheKey, [u64; 23]>>> = OnceLock::new();
39
40
#[cfg(all(not(feature = "std"), feature = "cache"))]
41
static CACHE: Once<RwLock<HashMap<CrcParamsCacheKey, [u64; 23]>>> = Once::new();
42
43
/// Cache key for storing CRC parameters that affect key generation
44
///
45
/// Only includes parameters that directly influence the mathematical computation
46
/// of folding keys. Parameters like `init`, `xorout`, and `check` are excluded
47
/// because they don't affect the key generation process.
48
///
49
/// The cache key implements `Hash`, `Eq`, and `PartialEq` to enable efficient
50
/// HashMap storage and lookup operations.
51
#[cfg(any(feature = "std", feature = "cache"))]
52
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
53
pub(crate) struct CrcParamsCacheKey {
54
    /// CRC width in bits (32 or 64)
55
    pub width: u8,
56
    /// Polynomial value used for CRC calculation
57
    pub poly: u64,
58
    /// Whether the CRC uses reflected input/output processing
59
    pub reflected: bool,
60
}
61
62
#[cfg(any(feature = "std", feature = "cache"))]
63
impl CrcParamsCacheKey {
64
    /// Create a new cache key from CRC parameters
65
    ///
66
    /// # Arguments
67
    ///
68
    /// * `width` - CRC width in bits (32 or 64)
69
    /// * `poly` - Polynomial value for the CRC algorithm
70
    /// * `reflected` - Whether input/output should be bit-reflected
71
    pub fn new(width: u8, poly: u64, reflected: bool) -> Self {
72
        Self {
73
            width,
74
            poly,
75
            reflected,
76
        }
77
    }
78
}
79
80
/// Initialize and return reference to the global cache
81
///
82
/// Uses OnceLock to ensure thread-safe lazy initialization without requiring
83
/// static initialization overhead. The cache is only created when first accessed.
84
#[cfg(feature = "std")]
85
fn get_cache() -> &'static RwLock<HashMap<CrcParamsCacheKey, [u64; 23]>> {
86
    CACHE.get_or_init(|| RwLock::new(HashMap::new()))
87
}
88
89
#[cfg(all(not(feature = "std"), feature = "cache"))]
90
fn get_cache() -> &'static RwLock<HashMap<CrcParamsCacheKey, [u64; 23]>> {
91
    CACHE.call_once(|| RwLock::new(HashMap::new()))
92
}
93
94
/// Get cached keys or generate and cache them if not present
95
///
96
/// This function implements a read-then-write pattern optimized for the common case
97
/// of cache hits while minimizing lock contention:
98
///
99
/// 1. **Read phase**: Attempts read lock to check for cached keys (allows concurrent reads)
100
/// 2. **Generation phase**: If cache miss, generates keys outside any lock to minimize hold time
101
/// 3. **Write phase**: Acquires write lock only to store the generated keys
102
///
103
/// The key generation happens outside the write lock because it's computationally expensive
104
/// (~1000x slower than cache lookup) and we want to minimize the time other threads are blocked.
105
///
106
/// All cache operations use best-effort error handling - lock poisoning or allocation failures
107
/// don't cause panics, instead falling back to direct key generation to maintain functionality.
108
///
109
/// # Arguments
110
///
111
/// * `width` - CRC width in bits (32 or 64)
112
/// * `poly` - Polynomial value for the CRC algorithm
113
/// * `reflected` - Whether input/output should be bit-reflected
114
///
115
/// # Returns
116
///
117
/// Array of 23 pre-computed folding keys for SIMD CRC calculation
118
pub fn get_or_generate_keys(width: u8, poly: u64, reflected: bool) -> [u64; 23] {
119
    #[cfg(feature = "std")]
120
    {
121
        let cache_key = CrcParamsCacheKey::new(width, poly, reflected);
122
123
        // Try cache read first - multiple threads can read simultaneously
124
        // If lock is poisoned or read fails, continue to key generation
125
        if let Ok(cache) = get_cache().read() {
126
            if let Some(keys) = cache.get(&cache_key) {
127
                return *keys;
128
            }
129
        }
130
131
        // Generate keys outside of write lock to minimize lock hold time
132
        let keys = generate::keys(width, poly, reflected);
133
134
        // Try to cache the result (best effort - if this fails, we still return valid keys)
135
        // Lock poisoning or write failure doesn't affect functionality
136
        let _ = get_cache()
137
            .write()
138
            .map(|mut cache| cache.insert(cache_key, keys));
139
140
        keys
141
    }
142
143
    #[cfg(all(not(feature = "std"), feature = "cache"))]
144
    {
145
        let cache_key = CrcParamsCacheKey::new(width, poly, reflected);
146
147
        // Try cache read first - multiple threads can read simultaneously
148
        // spin::RwLock returns guards directly (no Result wrapper)
149
        {
150
            let cache = get_cache().read();
151
            if let Some(keys) = cache.get(&cache_key) {
152
                return *keys;
153
            }
154
        } // Drop read lock before generating keys
155
156
        // Generate keys outside of write lock to minimize lock hold time
157
        let keys = generate::keys(width, poly, reflected);
158
159
        // Cache the result - spin::RwLock doesn't use Result wrapper
160
        {
161
            let mut cache = get_cache().write();
162
            cache.insert(cache_key, keys);
163
        }
164
165
        keys
166
    }
167
168
    #[cfg(not(any(feature = "std", feature = "cache")))]
169
    {
170
        generate::keys(width, poly, reflected)
171
    }
172
}
173
174
/// Clear all cached CRC parameter keys
175
///
176
/// This function is primarily intended for testing scenarios where you need to reset
177
/// the cache state to ensure test isolation.
178
///
179
/// Uses best-effort error handling - lock poisoning or other failures don't cause
180
/// panics, ensuring this function never disrupts program execution. If the cache
181
/// cannot be cleared, the function silently continues without error.
182
///
183
/// # Thread Safety
184
///
185
/// This function is thread-safe and can be called concurrently with other cache operations.
186
/// However, clearing the cache while other threads are actively using it may temporarily
187
/// reduce performance as those threads will need to regenerate keys on their next access.
188
#[cfg(test)]
189
pub(crate) fn clear_cache() {
190
    #[cfg(feature = "std")]
191
    {
192
        // Best-effort cache clear - if lock is poisoned or unavailable, silently continue
193
        // This ensures the function never panics or blocks program execution
194
        let _ = get_cache().write().map(|mut cache| cache.clear());
195
    }
196
197
    #[cfg(all(not(feature = "std"), feature = "cache"))]
198
    {
199
        // spin::RwLock doesn't use Result wrapper
200
        let mut cache = get_cache().write();
201
        cache.clear();
202
    }
203
}
204
205
#[cfg(test)]
206
mod tests {
207
    use super::*;
208
    use std::collections::HashSet;
209
210
    #[test]
211
    fn test_cache_key_creation() {
212
        let key1 = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
213
        let key2 = CrcParamsCacheKey::new(64, 0x42F0E1EBA9EA3693, false);
214
215
        assert_eq!(key1.width, 32);
216
        assert_eq!(key1.poly, 0x04C11DB7);
217
        assert!(key1.reflected);
218
219
        assert_eq!(key2.width, 64);
220
        assert_eq!(key2.poly, 0x42F0E1EBA9EA3693);
221
        assert!(!key2.reflected);
222
    }
223
224
    #[test]
225
    fn test_cache_key_equality() {
226
        let key1 = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
227
        let key2 = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
228
        let key3 = CrcParamsCacheKey::new(32, 0x04C11DB7, false); // Different reflected
229
        let key4 = CrcParamsCacheKey::new(64, 0x04C11DB7, true); // Different width
230
        let key5 = CrcParamsCacheKey::new(32, 0x1EDC6F41, true); // Different poly
231
232
        // Test equality
233
        assert_eq!(key1, key2);
234
        assert_eq!(key1.clone(), key2.clone());
235
236
        // Test inequality
237
        assert_ne!(key1, key3);
238
        assert_ne!(key1, key4);
239
        assert_ne!(key1, key5);
240
        assert_ne!(key3, key4);
241
        assert_ne!(key3, key5);
242
        assert_ne!(key4, key5);
243
    }
244
245
    #[test]
246
    fn test_cache_key_hashing() {
247
        let key1 = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
248
        let key2 = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
249
        let key3 = CrcParamsCacheKey::new(32, 0x04C11DB7, false);
250
251
        // Create a HashSet to test that keys can be used as hash keys
252
        let mut set = HashSet::new();
253
        set.insert(key1.clone());
254
        set.insert(key2.clone());
255
        set.insert(key3.clone());
256
257
        // Should only have 2 unique keys (key1 and key2 are equal)
258
        assert_eq!(set.len(), 2);
259
260
        // Test that equal keys can be found in the set
261
        assert!(set.contains(&key1));
262
        assert!(set.contains(&key2));
263
        assert!(set.contains(&key3));
264
265
        // Test that a new equivalent key can be found
266
        let key4 = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
267
        assert!(set.contains(&key4));
268
    }
269
270
    #[test]
271
    fn test_cache_hit_scenarios() {
272
        clear_cache();
273
274
        // First call should be a cache miss and generate keys
275
        let keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
276
277
        // Second call with same parameters should be a cache hit
278
        let keys2 = get_or_generate_keys(32, 0x04C11DB7, true);
279
280
        // Keys should be identical (same array contents)
281
        assert_eq!(keys1, keys2);
282
283
        // Test multiple cache hits
284
        let keys3 = get_or_generate_keys(32, 0x04C11DB7, true);
285
        let keys4 = get_or_generate_keys(32, 0x04C11DB7, true);
286
287
        assert_eq!(keys1, keys3);
288
        assert_eq!(keys1, keys4);
289
        assert_eq!(keys2, keys3);
290
        assert_eq!(keys2, keys4);
291
    }
292
293
    #[test]
294
    fn test_cache_miss_scenarios() {
295
        clear_cache();
296
297
        // Different width - should be cache miss
298
        let keys_32 = get_or_generate_keys(32, 0x04C11DB7, true);
299
        let keys_64 = get_or_generate_keys(64, 0x04C11DB7, true);
300
        assert_ne!(keys_32, keys_64);
301
302
        // Different poly - should be cache miss
303
        let keys_poly1 = get_or_generate_keys(32, 0x04C11DB7, true);
304
        let keys_poly2 = get_or_generate_keys(32, 0x1EDC6F41, true);
305
        assert_ne!(keys_poly1, keys_poly2);
306
307
        // Different reflected - should be cache miss
308
        let keys_refl_true = get_or_generate_keys(32, 0x04C11DB7, true);
309
        let keys_refl_false = get_or_generate_keys(32, 0x04C11DB7, false);
310
        assert_ne!(keys_refl_true, keys_refl_false);
311
312
        // Verify each parameter set is cached independently
313
        let keys_32_again = get_or_generate_keys(32, 0x04C11DB7, true);
314
        let keys_64_again = get_or_generate_keys(64, 0x04C11DB7, true);
315
        let keys_poly1_again = get_or_generate_keys(32, 0x04C11DB7, true);
316
        let keys_poly2_again = get_or_generate_keys(32, 0x1EDC6F41, true);
317
        let keys_refl_true_again = get_or_generate_keys(32, 0x04C11DB7, true);
318
        let keys_refl_false_again = get_or_generate_keys(32, 0x04C11DB7, false);
319
320
        assert_eq!(keys_32, keys_32_again);
321
        assert_eq!(keys_64, keys_64_again);
322
        assert_eq!(keys_poly1, keys_poly1_again);
323
        assert_eq!(keys_poly2, keys_poly2_again);
324
        assert_eq!(keys_refl_true, keys_refl_true_again);
325
        assert_eq!(keys_refl_false, keys_refl_false_again);
326
    }
327
328
    #[test]
329
    fn test_cached_keys_identical_to_generated_keys() {
330
        clear_cache();
331
332
        // Test CRC32 parameters
333
        let width = 32;
334
        let poly = 0x04C11DB7;
335
        let reflected = true;
336
337
        // Generate keys directly (bypassing cache)
338
        let direct_keys = generate::keys(width, poly, reflected);
339
340
        // Get keys through cache (first call will be cache miss, generates and caches)
341
        let cached_keys_first = get_or_generate_keys(width, poly, reflected);
342
343
        // Get keys through cache again (should be cache hit)
344
        let cached_keys_second = get_or_generate_keys(width, poly, reflected);
345
346
        // All should be identical
347
        assert_eq!(direct_keys, cached_keys_first);
348
        assert_eq!(direct_keys, cached_keys_second);
349
        assert_eq!(cached_keys_first, cached_keys_second);
350
351
        // Test CRC64 parameters
352
        let width64 = 64;
353
        let poly64 = 0x42F0E1EBA9EA3693;
354
        let reflected64 = false;
355
356
        let direct_keys64 = generate::keys(width64, poly64, reflected64);
357
        let cached_keys64_first = get_or_generate_keys(width64, poly64, reflected64);
358
        let cached_keys64_second = get_or_generate_keys(width64, poly64, reflected64);
359
360
        assert_eq!(direct_keys64, cached_keys64_first);
361
        assert_eq!(direct_keys64, cached_keys64_second);
362
        assert_eq!(cached_keys64_first, cached_keys64_second);
363
364
        // Verify different parameters produce different keys
365
        assert_ne!(direct_keys, direct_keys64);
366
        assert_ne!(cached_keys_first, cached_keys64_first);
367
    }
368
369
    #[test]
370
    fn test_multiple_parameter_combinations() {
371
        clear_cache();
372
373
        // Test various common CRC parameter combinations
374
        let test_cases = [
375
            (32, 0x04C11DB7, true),          // CRC32
376
            (32, 0x04C11DB7, false),         // CRC32 non-reflected
377
            (32, 0x1EDC6F41, true),          // CRC32C
378
            (64, 0x42F0E1EBA9EA3693, true),  // CRC64 ISO
379
            (64, 0x42F0E1EBA9EA3693, false), // CRC64 ISO non-reflected
380
            (64, 0xD800000000000000, true),  // CRC64 ECMA
381
        ];
382
383
        let mut all_keys = Vec::new();
384
385
        // Generate keys for all test cases
386
        for &(width, poly, reflected) in &test_cases {
387
            let keys = get_or_generate_keys(width, poly, reflected);
388
            all_keys.push(keys);
389
        }
390
391
        // Verify all keys are different (no collisions)
392
        for i in 0..all_keys.len() {
393
            for j in (i + 1)..all_keys.len() {
394
                assert_ne!(
395
                    all_keys[i], all_keys[j],
396
                    "Keys should be different for test cases {} and {}",
397
                    i, j
398
                );
399
            }
400
        }
401
402
        // Verify cache hits return same keys
403
        for (i, &(width, poly, reflected)) in test_cases.iter().enumerate() {
404
            let cached_keys = get_or_generate_keys(width, poly, reflected);
405
            assert_eq!(
406
                all_keys[i], cached_keys,
407
                "Cache hit should return same keys for test case {}",
408
                i
409
            );
410
        }
411
    }
412
413
    #[test]
414
    fn test_cache_management_utilities() {
415
        // Clear cache to start with clean state
416
        clear_cache();
417
418
        // Generate and cache some keys
419
        let keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
420
        let keys2 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
421
422
        // Verify cache hits return same keys
423
        let cached_keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
424
        let cached_keys2 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
425
426
        assert_eq!(keys1, cached_keys1);
427
        assert_eq!(keys2, cached_keys2);
428
429
        // Clear cache
430
        clear_cache();
431
432
        // Verify cache was cleared by checking that new calls still work
433
        // (we can't directly verify cache is empty, but we can verify functionality)
434
        let new_keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
435
        assert_eq!(keys1, new_keys1); // Should be same values, but freshly generated
436
    }
437
438
    #[test]
439
    fn test_cache_error_handling() {
440
        // Test that cache operations don't panic even if called multiple times
441
        clear_cache();
442
        clear_cache(); // Should not panic on empty cache
443
444
        // Test that get_or_generate_keys works even after multiple clears
445
        let keys = get_or_generate_keys(32, 0x04C11DB7, true);
446
        clear_cache();
447
        let keys2 = get_or_generate_keys(32, 0x04C11DB7, true);
448
449
        // Keys should be identical (same parameters produce same keys)
450
        assert_eq!(keys, keys2);
451
    }
452
453
    #[test]
454
    fn test_cache_key_debug_and_clone() {
455
        let key = CrcParamsCacheKey::new(32, 0x04C11DB7, true);
456
457
        // Test Debug trait
458
        let debug_str = format!("{:?}", key);
459
        assert!(debug_str.contains("CrcParamsCacheKey"));
460
        assert!(debug_str.contains("32"));
461
        assert!(debug_str.contains("0x4c11db7") || debug_str.contains("79764919"));
462
        assert!(debug_str.contains("true"));
463
464
        // Test Clone trait
465
        let cloned_key = key.clone();
466
        assert_eq!(key, cloned_key);
467
        assert_eq!(key.width, cloned_key.width);
468
        assert_eq!(key.poly, cloned_key.poly);
469
        assert_eq!(key.reflected, cloned_key.reflected);
470
    }
471
472
    // Thread safety tests
473
    #[test]
474
    fn test_concurrent_cache_reads() {
475
        use std::sync::{Arc, Barrier};
476
        use std::thread;
477
478
        clear_cache();
479
480
        // Pre-populate cache with a known value
481
        let expected_keys = get_or_generate_keys(32, 0x04C11DB7, true);
482
483
        let num_threads = 8;
484
        let barrier = Arc::new(Barrier::new(num_threads));
485
        let mut handles = Vec::new();
486
487
        // Spawn multiple threads that all read the same cached value simultaneously
488
        for i in 0..num_threads {
489
            let barrier_clone = Arc::clone(&barrier);
490
            let handle = thread::spawn(move || {
491
                // Wait for all threads to be ready
492
                barrier_clone.wait();
493
494
                // All threads read from cache simultaneously
495
                let keys = get_or_generate_keys(32, 0x04C11DB7, true);
496
                (i, keys)
497
            });
498
            handles.push(handle);
499
        }
500
501
        // Collect results from all threads
502
        let mut results = Vec::new();
503
        for handle in handles {
504
            results.push(handle.join().expect("Thread should not panic"));
505
        }
506
507
        // Verify all threads got the same cached keys
508
        assert_eq!(results.len(), num_threads);
509
        for (thread_id, keys) in results {
510
            assert_eq!(
511
                keys, expected_keys,
512
                "Thread {} should get same cached keys",
513
                thread_id
514
            );
515
        }
516
    }
517
518
    #[test]
519
    #[allow(clippy::needless_range_loop)] // Intentionally testing concurrent indexed access
520
    fn test_concurrent_cache_writes() {
521
        use std::sync::{Arc, Barrier};
522
        use std::thread;
523
524
        clear_cache();
525
526
        let num_threads = 6;
527
        let barrier = Arc::new(Barrier::new(num_threads));
528
        let mut handles = Vec::new();
529
530
        // Test parameters for different cache entries
531
        let test_params = [
532
            (32, 0x04C11DB7, true),
533
            (32, 0x04C11DB7, false),
534
            (32, 0x1EDC6F41, true),
535
            (64, 0x42F0E1EBA9EA3693, true),
536
            (64, 0x42F0E1EBA9EA3693, false),
537
            (64, 0xD800000000000000, true),
538
        ];
539
540
        // Spawn threads that write different cache entries simultaneously
541
        for i in 0..num_threads {
542
            let barrier_clone = Arc::clone(&barrier);
543
            let (width, poly, reflected) = test_params[i];
544
545
            let handle = thread::spawn(move || {
546
                // Wait for all threads to be ready
547
                barrier_clone.wait();
548
549
                // Each thread generates and caches different parameters
550
                let keys = get_or_generate_keys(width, poly, reflected);
551
                (i, width, poly, reflected, keys)
552
            });
553
            handles.push(handle);
554
        }
555
556
        // Collect results from all threads
557
        let mut results = Vec::new();
558
        for handle in handles {
559
            results.push(handle.join().expect("Thread should not panic"));
560
        }
561
562
        // Verify all threads completed successfully and got correct keys
563
        assert_eq!(results.len(), num_threads);
564
565
        // Verify each thread's keys match what we'd expect from direct generation
566
        for (thread_id, width, poly, reflected, keys) in results {
567
            let expected_keys = generate::keys(width, poly, reflected);
568
            assert_eq!(
569
                keys, expected_keys,
570
                "Thread {} should generate correct keys for params ({}, {:#x}, {})",
571
                thread_id, width, poly, reflected
572
            );
573
574
            // Verify the keys are now cached by reading them again
575
            let cached_keys = get_or_generate_keys(width, poly, reflected);
576
            assert_eq!(
577
                keys, cached_keys,
578
                "Thread {} keys should be cached",
579
                thread_id
580
            );
581
        }
582
    }
583
584
    #[test]
585
    fn test_read_write_contention() {
586
        use std::sync::{Arc, Barrier};
587
        use std::thread;
588
        use std::time::Duration;
589
590
        clear_cache();
591
592
        // Pre-populate cache with some values
593
        let _keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
594
        let _keys2 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
595
596
        let num_readers = 6;
597
        let num_writers = 3;
598
        let total_threads = num_readers + num_writers;
599
        let barrier = Arc::new(Barrier::new(total_threads));
600
        let mut handles = Vec::new();
601
602
        // Spawn reader threads that continuously read cached values
603
        for i in 0..num_readers {
604
            let barrier_clone = Arc::clone(&barrier);
605
            let handle = thread::spawn(move || {
606
                barrier_clone.wait();
607
608
                let mut read_count = 0;
609
                let start = std::time::Instant::now();
610
611
                // Read for a short duration
612
                while start.elapsed() < Duration::from_millis(50) {
613
                    let keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
614
                    let keys2 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
615
616
                    // Verify we get consistent results
617
                    assert_eq!(keys1.len(), 23);
618
                    assert_eq!(keys2.len(), 23);
619
                    read_count += 1;
620
                }
621
622
                (format!("reader_{}", i), read_count)
623
            });
624
            handles.push(handle);
625
        }
626
627
        // Spawn writer threads that add new cache entries
628
        for i in 0..num_writers {
629
            let barrier_clone = Arc::clone(&barrier);
630
            let handle = thread::spawn(move || {
631
                barrier_clone.wait();
632
633
                let mut write_count = 0;
634
                let start = std::time::Instant::now();
635
636
                // Write new entries for a short duration
637
                while start.elapsed() < Duration::from_millis(50) {
638
                    // Use different parameters to create new cache entries
639
                    let poly = 0x1EDC6F41 + (i as u64 * 0x1000) + (write_count as u64);
640
                    let keys = get_or_generate_keys(32, poly, true);
641
642
                    assert_eq!(keys.len(), 23);
643
                    write_count += 1;
644
                }
645
646
                (format!("writer_{}", i), write_count)
647
            });
648
            handles.push(handle);
649
        }
650
651
        // Wait for all threads to complete
652
        let mut results = Vec::new();
653
        for handle in handles {
654
            results.push(handle.join().expect("Thread should not panic"));
655
        }
656
657
        // Verify all threads completed successfully
658
        assert_eq!(results.len(), total_threads);
659
660
        // Verify readers and writers both made progress
661
        let reader_results: Vec<_> = results
662
            .iter()
663
            .filter(|(name, _)| name.starts_with("reader_"))
664
            .collect();
665
        let writer_results: Vec<_> = results
666
            .iter()
667
            .filter(|(name, _)| name.starts_with("writer_"))
668
            .collect();
669
670
        assert_eq!(reader_results.len(), num_readers);
671
        assert_eq!(writer_results.len(), num_writers);
672
673
        // All threads should have made some progress
674
        for (name, count) in &results {
675
            assert!(*count > 0, "Thread {} should have made progress", name);
676
        }
677
    }
678
679
    #[test]
680
    fn test_cache_consistency_under_concurrent_access() {
681
        use std::sync::{Arc, Barrier};
682
        use std::thread;
683
684
        clear_cache();
685
686
        let num_threads = 10;
687
        let barrier = Arc::new(Barrier::new(num_threads));
688
        let mut handles = Vec::new();
689
690
        // All threads will try to get the same cache entry simultaneously
691
        // This tests the race condition where multiple threads might try to
692
        // generate and cache the same keys at the same time
693
        for i in 0..num_threads {
694
            let barrier_clone = Arc::clone(&barrier);
695
            let handle = thread::spawn(move || {
696
                barrier_clone.wait();
697
698
                // All threads request the same parameters simultaneously
699
                let keys = get_or_generate_keys(32, 0x04C11DB7, true);
700
                (i, keys)
701
            });
702
            handles.push(handle);
703
        }
704
705
        // Collect results from all threads
706
        let mut results = Vec::new();
707
        for handle in handles {
708
            results.push(handle.join().expect("Thread should not panic"));
709
        }
710
711
        // Verify all threads got identical keys
712
        assert_eq!(results.len(), num_threads);
713
        let first_keys = results[0].1;
714
715
        for (thread_id, keys) in results {
716
            assert_eq!(
717
                keys, first_keys,
718
                "Thread {} should get identical keys to other threads",
719
                thread_id
720
            );
721
        }
722
723
        // Verify the keys are correct by comparing with direct generation
724
        let expected_keys = generate::keys(32, 0x04C11DB7, true);
725
        assert_eq!(
726
            first_keys, expected_keys,
727
            "Cached keys should match directly generated keys"
728
        );
729
730
        // Verify subsequent access still returns the same keys
731
        let final_keys = get_or_generate_keys(32, 0x04C11DB7, true);
732
        assert_eq!(
733
            final_keys, first_keys,
734
            "Final cache access should return same keys"
735
        );
736
    }
737
738
    #[test]
739
    fn test_mixed_concurrent_operations() {
740
        use std::sync::{Arc, Barrier};
741
        use std::thread;
742
        use std::time::Duration;
743
744
        clear_cache();
745
746
        let num_threads = 8;
747
        let barrier = Arc::new(Barrier::new(num_threads));
748
        let mut handles = Vec::new();
749
750
        // Mix of operations: some threads do cache hits, some do cache misses,
751
        // some clear the cache, all happening concurrently
752
        for i in 0..num_threads {
753
            let barrier_clone = Arc::clone(&barrier);
754
            let handle = thread::spawn(move || {
755
                barrier_clone.wait();
756
757
                let mut operations = 0;
758
                let start = std::time::Instant::now();
759
760
                while start.elapsed() < Duration::from_millis(30) {
761
                    match i % 4 {
762
                        0 => {
763
                            // Cache hit operations - same parameters
764
                            let _keys = get_or_generate_keys(32, 0x04C11DB7, true);
765
                        }
766
                        1 => {
767
                            // Cache miss operations - different parameters each time
768
                            let poly = 0x1EDC6F41 + (operations as u64);
769
                            let _keys = get_or_generate_keys(32, poly, true);
770
                        }
771
                        2 => {
772
                            // Mixed read operations
773
                            let _keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
774
                            let _keys2 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
775
                        }
776
                        3 => {
777
                            // Occasional cache clear (but not too often to avoid disrupting other tests)
778
                            if operations % 10 == 0 {
779
                                clear_cache();
780
                            }
781
                            let _keys = get_or_generate_keys(32, 0x04C11DB7, true);
782
                        }
783
                        _ => unreachable!(),
784
                    }
785
                    operations += 1;
786
                }
787
788
                (i, operations)
789
            });
790
            handles.push(handle);
791
        }
792
793
        // Wait for all threads to complete
794
        let mut results = Vec::new();
795
        for handle in handles {
796
            results.push(handle.join().expect("Thread should not panic"));
797
        }
798
799
        // Verify all threads completed successfully
800
        assert_eq!(results.len(), num_threads);
801
802
        for (thread_id, operations) in results {
803
            assert!(
804
                operations > 0,
805
                "Thread {} should have completed some operations",
806
                thread_id
807
            );
808
        }
809
810
        // Verify cache is still functional after all the concurrent operations
811
        let final_keys = get_or_generate_keys(32, 0x04C11DB7, true);
812
        let expected_keys = generate::keys(32, 0x04C11DB7, true);
813
        assert_eq!(
814
            final_keys, expected_keys,
815
            "Cache should still work correctly after concurrent operations"
816
        );
817
    }
818
819
    // Error handling tests
820
    #[test]
821
    fn test_cache_lock_poisoning_recovery() {
822
        use std::panic;
823
        use std::sync::{Arc, Mutex};
824
        use std::thread;
825
826
        clear_cache();
827
828
        // Pre-populate cache with known values
829
        let expected_keys = get_or_generate_keys(32, 0x04C11DB7, true);
830
831
        // Create a scenario that could potentially poison the lock
832
        // We'll use a separate test to avoid actually poisoning our cache
833
        let poisoned_flag = Arc::new(Mutex::new(false));
834
        let poisoned_flag_clone = Arc::clone(&poisoned_flag);
835
836
        // Spawn a thread that panics while holding a lock (simulated)
837
        let handle = thread::spawn(move || {
838
            // Simulate a panic scenario - we don't actually poison the cache lock
839
            // because that would break other tests, but we test the recovery path
840
            let _guard = poisoned_flag_clone.lock().unwrap();
841
            panic!("Simulated panic");
842
        });
843
844
        // Wait for the thread to panic
845
        let result = handle.join();
846
        assert!(result.is_err(), "Thread should have panicked");
847
848
        // Verify that our cache still works despite the simulated error scenario
849
        let keys_after_panic = get_or_generate_keys(32, 0x04C11DB7, true);
850
        assert_eq!(
851
            keys_after_panic, expected_keys,
852
            "Cache should still work after simulated panic scenario"
853
        );
854
855
        // Test that new cache entries can still be created
856
        let new_keys = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
857
        assert_eq!(new_keys.len(), 23, "New cache entries should still work");
858
859
        // Verify the new keys are cached
860
        let cached_new_keys = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
861
        assert_eq!(new_keys, cached_new_keys, "New keys should be cached");
862
    }
863
864
    #[test]
865
    fn test_cache_fallback_to_direct_generation() {
866
        clear_cache();
867
868
        // Test that even if cache operations fail, we still get valid keys
869
        // This tests the fallback mechanism in get_or_generate_keys
870
871
        // Generate expected keys directly
872
        let expected_keys_32 = generate::keys(32, 0x04C11DB7, true);
873
        let expected_keys_64 = generate::keys(64, 0x42F0E1EBA9EA3693, false);
874
875
        // Get keys through cache (should work normally)
876
        let cached_keys_32 = get_or_generate_keys(32, 0x04C11DB7, true);
877
        let cached_keys_64 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
878
879
        // Verify keys are correct regardless of cache state
880
        assert_eq!(
881
            cached_keys_32, expected_keys_32,
882
            "CRC32 keys should be correct even with cache issues"
883
        );
884
        assert_eq!(
885
            cached_keys_64, expected_keys_64,
886
            "CRC64 keys should be correct even with cache issues"
887
        );
888
889
        // Test multiple calls to ensure consistency
890
        for _ in 0..5 {
891
            let keys_32 = get_or_generate_keys(32, 0x04C11DB7, true);
892
            let keys_64 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
893
894
            assert_eq!(
895
                keys_32, expected_keys_32,
896
                "Repeated calls should return consistent CRC32 keys"
897
            );
898
            assert_eq!(
899
                keys_64, expected_keys_64,
900
                "Repeated calls should return consistent CRC64 keys"
901
            );
902
        }
903
    }
904
905
    #[test]
906
    fn test_cache_operations_under_memory_pressure() {
907
        clear_cache();
908
909
        // Simulate memory pressure by creating many cache entries
910
        // This tests that cache operations remain stable under load
911
        let mut test_keys = Vec::new();
912
        let num_entries = 100;
913
914
        // Create many different cache entries
915
        for i in 0..num_entries {
916
            let poly = 0x04C11DB7 + (i as u64);
917
            let reflected = i % 2 == 0;
918
            let width = if i % 3 == 0 { 64 } else { 32 };
919
920
            let keys = get_or_generate_keys(width, poly, reflected);
921
            test_keys.push((width, poly, reflected, keys));
922
        }
923
924
        // Verify all entries are correctly cached and retrievable
925
        for (i, &(width, poly, reflected, ref expected_keys)) in test_keys.iter().enumerate() {
926
            let cached_keys = get_or_generate_keys(width, poly, reflected);
927
            assert_eq!(
928
                cached_keys, *expected_keys,
929
                "Entry {} should be correctly cached",
930
                i
931
            );
932
        }
933
934
        // Test that cache operations still work after creating many entries
935
        let new_keys = get_or_generate_keys(32, 0x1EDC6F41, true);
936
        assert_eq!(
937
            new_keys.len(),
938
            23,
939
            "New entries should still work under memory pressure"
940
        );
941
942
        // Verify the new entry is cached
943
        let cached_new_keys = get_or_generate_keys(32, 0x1EDC6F41, true);
944
        assert_eq!(new_keys, cached_new_keys, "New entry should be cached");
945
946
        // Test cache clearing still works
947
        clear_cache();
948
949
        // Verify cache was cleared by testing that operations still work
950
        let post_clear_keys = get_or_generate_keys(32, 0x04C11DB7, true);
951
        assert_eq!(
952
            post_clear_keys.len(),
953
            23,
954
            "Cache should work after clearing under memory pressure"
955
        );
956
    }
957
958
    #[test]
959
    fn test_cache_error_recovery_patterns() {
960
        clear_cache();
961
962
        // Test various error recovery patterns to ensure robustness
963
964
        // Pattern 1: Rapid cache operations
965
        for i in 0..50 {
966
            let poly = 0x04C11DB7 + (i as u64 % 10); // Create some duplicates
967
            let keys = get_or_generate_keys(32, poly, true);
968
            assert_eq!(keys.len(), 23, "Rapid operation {} should succeed", i);
969
        }
970
971
        // Pattern 2: Interleaved cache hits and misses
972
        let base_keys = get_or_generate_keys(32, 0x04C11DB7, true);
973
        for i in 0..20 {
974
            // Cache hit
975
            let hit_keys = get_or_generate_keys(32, 0x04C11DB7, true);
976
            assert_eq!(hit_keys, base_keys, "Cache hit {} should be consistent", i);
977
978
            // Cache miss
979
            let miss_keys = get_or_generate_keys(32, 0x1EDC6F41 + (i as u64), false);
980
            assert_eq!(miss_keys.len(), 23, "Cache miss {} should succeed", i);
981
        }
982
983
        // Pattern 3: Mixed operations with clearing
984
        for i in 0..10 {
985
            let keys1 = get_or_generate_keys(32, 0x04C11DB7, true);
986
            let keys2 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
987
988
            if i % 3 == 0 {
989
                clear_cache();
990
            }
991
992
            // Operations should still work after clearing
993
            let keys3 = get_or_generate_keys(32, 0x04C11DB7, true);
994
            let keys4 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
995
996
            // Keys should be consistent (same parameters = same keys)
997
            assert_eq!(keys1, keys3, "Keys should be consistent across clears");
998
            assert_eq!(keys2, keys4, "Keys should be consistent across clears");
999
        }
1000
    }
1001
1002
    #[test]
1003
    fn test_cache_concurrent_error_scenarios() {
1004
        use std::sync::{Arc, Barrier};
1005
        use std::thread;
1006
        use std::time::Duration;
1007
1008
        clear_cache();
1009
1010
        let num_threads = 8;
1011
        let barrier = Arc::new(Barrier::new(num_threads));
1012
        let mut handles = Vec::new();
1013
1014
        // Create concurrent scenarios that could potentially cause errors
1015
        for i in 0..num_threads {
1016
            let barrier_clone = Arc::clone(&barrier);
1017
            let handle = thread::spawn(move || {
1018
                barrier_clone.wait();
1019
1020
                let mut operations = 0;
1021
                let errors = 0;
1022
                let start = std::time::Instant::now();
1023
1024
                // Run operations for a short time with various patterns
1025
                while start.elapsed() < Duration::from_millis(100) {
1026
                    match operations % 5 {
1027
                        0 => {
1028
                            // Normal cache operations
1029
                            let _keys = get_or_generate_keys(32, 0x04C11DB7, true);
1030
                        }
1031
                        1 => {
1032
                            // Rapid different parameters
1033
                            let poly = 0x1EDC6F41 + (operations as u64);
1034
                            let _keys = get_or_generate_keys(32, poly, true);
1035
                        }
1036
                        2 => {
1037
                            // Cache clearing (potential contention point)
1038
                            clear_cache();
1039
                        }
1040
                        3 => {
1041
                            // Mixed width operations
1042
                            let _keys32 = get_or_generate_keys(32, 0x04C11DB7, true);
1043
                            let _keys64 = get_or_generate_keys(64, 0x42F0E1EBA9EA3693, false);
1044
                        }
1045
                        4 => {
1046
                            // Rapid same-parameter calls (cache hits)
1047
                            for _ in 0..5 {
1048
                                let _keys = get_or_generate_keys(32, 0x04C11DB7, true);
1049
                            }
1050
                        }
1051
                        _ => unreachable!(),
1052
                    }
1053
                    operations += 1;
1054
                }
1055
1056
                (i, operations, errors)
1057
            });
1058
            handles.push(handle);
1059
        }
1060
1061
        // Collect results
1062
        let mut results = Vec::new();
1063
        for handle in handles {
1064
            match handle.join() {
1065
                Ok(result) => results.push(result),
1066
                Err(_) => panic!("Thread should not panic during error scenarios"),
1067
            }
1068
        }
1069
1070
        // Verify all threads completed successfully
1071
        assert_eq!(results.len(), num_threads);
1072
1073
        for (thread_id, operations, errors) in results {
1074
            assert!(
1075
                operations > 0,
1076
                "Thread {} should have completed operations",
1077
                thread_id
1078
            );
1079
            // In our implementation, errors should be handled gracefully without propagating
1080
            assert_eq!(
1081
                errors, 0,
1082
                "Thread {} should not have unhandled errors",
1083
                thread_id
1084
            );
1085
        }
1086
1087
        // Verify cache is still functional after all concurrent error scenarios
1088
        let final_keys = get_or_generate_keys(32, 0x04C11DB7, true);
1089
        let expected_keys = generate::keys(32, 0x04C11DB7, true);
1090
        assert_eq!(
1091
            final_keys, expected_keys,
1092
            "Cache should still work correctly after concurrent error scenarios"
1093
        );
1094
    }
1095
1096
    /// Non-stress version of the stress test to allow Miri to evaluate without timing out.
1097
    #[test]
1098
    fn test_cache_memory_allocation_non_stress() {
1099
        cache_memory_allocation_stress(2);
1100
    }
1101
1102
    /// Skipping for Miri runs due to time constraints, underlying code already covered by other
1103
    /// tests.
1104
    #[test]
1105
    #[cfg_attr(miri, ignore)]
1106
    fn test_cache_memory_allocation_stress() {
1107
        cache_memory_allocation_stress(1000);
1108
    }
1109
1110
    fn cache_memory_allocation_stress(count: i32) {
1111
        clear_cache();
1112
1113
        // Test cache behavior under memory allocation stress
1114
        // Create a large number of unique cache entries to stress memory allocation
1115
        let mut created_entries = Vec::new();
1116
        let stress_count = count;
1117
1118
        // Create many unique cache entries
1119
        for i in 0..stress_count {
1120
            let width = if i % 2 == 0 { 32 } else { 64 };
1121
            let poly = 0x04C11DB7 + (i as u64);
1122
            let reflected = i % 3 == 0;
1123
1124
            let keys = get_or_generate_keys(width, poly, reflected);
1125
            created_entries.push((width, poly, reflected, keys));
1126
1127
            // Verify each entry is valid
1128
            assert_eq!(keys.len(), 23, "Entry {} should have valid keys", i);
1129
        }
1130
1131
        // Verify all entries are still accessible (testing cache integrity)
1132
        for (i, &(width, poly, reflected, ref expected_keys)) in created_entries.iter().enumerate()
1133
        {
1134
            let retrieved_keys = get_or_generate_keys(width, poly, reflected);
1135
            assert_eq!(
1136
                retrieved_keys, *expected_keys,
1137
                "Entry {} should be retrievable after stress test",
1138
                i
1139
            );
1140
        }
1141
1142
        // Test that new entries can still be created
1143
        let new_keys = get_or_generate_keys(32, 0xFFFFFFFF, true);
1144
        assert_eq!(
1145
            new_keys.len(),
1146
            23,
1147
            "New entries should work after memory stress"
1148
        );
1149
1150
        // Test cache clearing works under memory pressure
1151
        clear_cache();
1152
1153
        // Verify cache operations still work after clearing
1154
        let post_stress_keys = get_or_generate_keys(32, 0x04C11DB7, true);
1155
        assert_eq!(
1156
            post_stress_keys.len(),
1157
            23,
1158
            "Cache should work after memory stress and clearing"
1159
        );
1160
    }
1161
1162
    // Integration tests for CrcParams compatibility
1163
    #[test]
1164
    fn test_crc_params_new_behavior_unchanged() {
1165
        use crate::CrcParams;
1166
1167
        clear_cache();
1168
1169
        // Test that CrcParams::new() creates identical instances regardless of caching
1170
        let params1 = CrcParams::new(
1171
            "TEST_CRC32",
1172
            32,
1173
            0x04C11DB7,
1174
            0xFFFFFFFF,
1175
            true,
1176
            0xFFFFFFFF,
1177
            0xCBF43926,
1178
        );
1179
        let params2 = CrcParams::new(
1180
            "TEST_CRC32",
1181
            32,
1182
            0x04C11DB7,
1183
            0xFFFFFFFF,
1184
            true,
1185
            0xFFFFFFFF,
1186
            0xCBF43926,
1187
        );
1188
1189
        // All fields should be identical
1190
        assert_eq!(params1.name, params2.name);
1191
        assert_eq!(params1.width, params2.width);
1192
        assert_eq!(params1.poly, params2.poly);
1193
        assert_eq!(params1.init, params2.init);
1194
        assert_eq!(params1.refin, params2.refin);
1195
        assert_eq!(params1.refout, params2.refout);
1196
        assert_eq!(params1.xorout, params2.xorout);
1197
        assert_eq!(params1.check, params2.check);
1198
        assert_eq!(params1.keys, params2.keys);
1199
1200
        // Test CRC64 parameters as well
1201
        let params64_1 = CrcParams::new(
1202
            "TEST_CRC64",
1203
            64,
1204
            0x42F0E1EBA9EA3693,
1205
            0xFFFFFFFFFFFFFFFF,
1206
            false,
1207
            0x0,
1208
            0x6C40DF5F0B497347,
1209
        );
1210
        let params64_2 = CrcParams::new(
1211
            "TEST_CRC64",
1212
            64,
1213
            0x42F0E1EBA9EA3693,
1214
            0xFFFFFFFFFFFFFFFF,
1215
            false,
1216
            0x0,
1217
            0x6C40DF5F0B497347,
1218
        );
1219
1220
        assert_eq!(params64_1.name, params64_2.name);
1221
        assert_eq!(params64_1.width, params64_2.width);
1222
        assert_eq!(params64_1.poly, params64_2.poly);
1223
        assert_eq!(params64_1.init, params64_2.init);
1224
        assert_eq!(params64_1.refin, params64_2.refin);
1225
        assert_eq!(params64_1.refout, params64_2.refout);
1226
        assert_eq!(params64_1.xorout, params64_2.xorout);
1227
        assert_eq!(params64_1.check, params64_2.check);
1228
        assert_eq!(params64_1.keys, params64_2.keys);
1229
    }
1230
1231
    #[test]
1232
    fn test_existing_crc_parameter_combinations() {
1233
        use crate::test::consts::TEST_ALL_CONFIGS;
1234
1235
        clear_cache();
1236
1237
        // Test all existing CRC parameter combinations work correctly with caching
1238
        for config in TEST_ALL_CONFIGS {
1239
            let params = crate::CrcParams::new(
1240
                config.get_name(),
1241
                config.get_width(),
1242
                config.get_poly(),
1243
                config.get_init(),
1244
                config.get_refin(),
1245
                config.get_xorout(),
1246
                config.get_check(),
1247
            );
1248
1249
            // Verify the parameters are set correctly
1250
            assert_eq!(params.name, config.get_name());
1251
            assert_eq!(params.width, config.get_width());
1252
            assert_eq!(params.poly, config.get_poly());
1253
            assert_eq!(params.init, config.get_init());
1254
            assert_eq!(params.refin, config.get_refin());
1255
            assert_eq!(params.refout, config.get_refin());
1256
            assert_eq!(params.xorout, config.get_xorout());
1257
            assert_eq!(params.check, config.get_check());
1258
1259
            // Verify keys are correct by comparing with expected keys
1260
            let expected_keys = config.get_keys();
1261
            assert_eq!(
1262
                params.keys,
1263
                expected_keys,
1264
                "Keys mismatch for {}: expected {:?}, got {:?}",
1265
                config.get_name(),
1266
                expected_keys,
1267
                params.keys
1268
            );
1269
        }
1270
    }
1271
1272
    #[test]
1273
    fn test_cached_vs_uncached_results_identical() {
1274
        clear_cache();
1275
1276
        // Test parameters that affect key generation
1277
        let test_cases = [
1278
            (32, 0x04C11DB7, true),          // CRC32 reflected
1279
            (32, 0x04C11DB7, false),         // CRC32 non-reflected
1280
            (32, 0x1EDC6F41, true),          // CRC32C
1281
            (64, 0x42F0E1EBA9EA3693, true),  // CRC64 ISO reflected
1282
            (64, 0x42F0E1EBA9EA3693, false), // CRC64 ISO non-reflected
1283
            (64, 0xD800000000000000, true),  // CRC64 ECMA
1284
        ];
1285
1286
        for &(width, poly, reflected) in &test_cases {
1287
            // Generate keys directly (uncached)
1288
            let uncached_keys = generate::keys(width, poly, reflected);
1289
1290
            // Clear cache to ensure first call is cache miss
1291
            clear_cache();
1292
1293
            // Create CrcParams instance (first call - cache miss)
1294
            let params1 =
1295
                crate::CrcParams::new("TEST", width, poly, 0xFFFFFFFFFFFFFFFF, reflected, 0x0, 0x0);
1296
1297
            // Create another CrcParams instance with same parameters (cache hit)
1298
            let params2 =
1299
                crate::CrcParams::new("TEST", width, poly, 0xFFFFFFFFFFFFFFFF, reflected, 0x0, 0x0);
1300
1301
            // All should be identical
1302
            assert_eq!(
1303
                uncached_keys, params1.keys,
1304
                "Uncached keys should match CrcParams keys for width={}, poly={:#x}, reflected={}",
1305
                width, poly, reflected
1306
            );
1307
            assert_eq!(
1308
                params1.keys, params2.keys,
1309
                "Cached and uncached CrcParams should have identical keys for width={}, poly={:#x}, reflected={}",
1310
                width, poly, reflected
1311
            );
1312
            assert_eq!(
1313
                uncached_keys, params2.keys,
1314
                "All key generation methods should produce identical results for width={}, poly={:#x}, reflected={}",
1315
                width, poly, reflected
1316
            );
1317
        }
1318
    }
1319
1320
    #[test]
1321
    fn test_multiple_crc_params_instances_use_cached_keys() {
1322
        clear_cache();
1323
1324
        // Create multiple CrcParams instances with the same parameters
1325
        let width = 32;
1326
        let poly = 0x04C11DB7;
1327
        let reflected = true;
1328
        let init = 0xFFFFFFFF;
1329
        let xorout = 0xFFFFFFFF;
1330
        let check = 0xCBF43926;
1331
1332
        // First instance - should generate and cache keys
1333
        let params1 = crate::CrcParams::new("TEST1", width, poly, init, reflected, xorout, check);
1334
1335
        // Subsequent instances - should use cached keys
1336
        let params2 = crate::CrcParams::new("TEST2", width, poly, init, reflected, xorout, check);
1337
        let params3 = crate::CrcParams::new("TEST3", width, poly, init, reflected, xorout, check);
1338
        let params4 = crate::CrcParams::new("TEST4", width, poly, init, reflected, xorout, check);
1339
1340
        // All should have identical keys (proving cache is working)
1341
        assert_eq!(
1342
            params1.keys, params2.keys,
1343
            "Instance 1 and 2 should have identical keys"
1344
        );
1345
        assert_eq!(
1346
            params1.keys, params3.keys,
1347
            "Instance 1 and 3 should have identical keys"
1348
        );
1349
        assert_eq!(
1350
            params1.keys, params4.keys,
1351
            "Instance 1 and 4 should have identical keys"
1352
        );
1353
        assert_eq!(
1354
            params2.keys, params3.keys,
1355
            "Instance 2 and 3 should have identical keys"
1356
        );
1357
        assert_eq!(
1358
            params2.keys, params4.keys,
1359
            "Instance 2 and 4 should have identical keys"
1360
        );
1361
        assert_eq!(
1362
            params3.keys, params4.keys,
1363
            "Instance 3 and 4 should have identical keys"
1364
        );
1365
1366
        // Verify keys are mathematically correct
1367
        let expected_keys = generate::keys(width, poly, reflected);
1368
        assert_eq!(
1369
            params1.keys, expected_keys,
1370
            "Cached keys should be mathematically correct"
1371
        );
1372
1373
        // Test with different parameters that don't affect key generation
1374
        let params5 = crate::CrcParams::new(
1375
            "DIFFERENT_NAME",
1376
            width,
1377
            poly,
1378
            0x12345678,
1379
            reflected,
1380
            0x87654321,
1381
            0xABCDEF,
1382
        );
1383
        assert_eq!(
1384
            params1.keys, params5.keys,
1385
            "Different init/xorout/check/name should not affect cached keys"
1386
        );
1387
1388
        // Test with CRC64 parameters
1389
        let width64 = 64;
1390
        let poly64 = 0x42F0E1EBA9EA3693;
1391
        let reflected64 = false;
1392
1393
        let params64_1 = crate::CrcParams::new(
1394
            "CRC64_1",
1395
            width64,
1396
            poly64,
1397
            0xFFFFFFFFFFFFFFFF,
1398
            reflected64,
1399
            0x0,
1400
            0x0,
1401
        );
1402
        let params64_2 = crate::CrcParams::new(
1403
            "CRC64_2",
1404
            width64,
1405
            poly64,
1406
            0x0,
1407
            reflected64,
1408
            0xFFFFFFFFFFFFFFFF,
1409
            0x12345,
1410
        );
1411
        let params64_3 = crate::CrcParams::new(
1412
            "CRC64_3",
1413
            width64,
1414
            poly64,
1415
            0x123456789ABCDEF0,
1416
            reflected64,
1417
            0x0FEDCBA987654321,
1418
            0x999,
1419
        );
1420
1421
        assert_eq!(
1422
            params64_1.keys, params64_2.keys,
1423
            "CRC64 instances should have identical keys"
1424
        );
1425
        assert_eq!(
1426
            params64_1.keys, params64_3.keys,
1427
            "CRC64 instances should have identical keys"
1428
        );
1429
1430
        let expected_keys64 = generate::keys(width64, poly64, reflected64);
1431
        assert_eq!(
1432
            params64_1.keys, expected_keys64,
1433
            "CRC64 cached keys should be mathematically correct"
1434
        );
1435
    }
1436
1437
    #[test]
1438
    fn test_crc_params_api_compatibility() {
1439
        use crate::{CrcAlgorithm, CrcParams};
1440
1441
        clear_cache();
1442
1443
        // Test that the CrcParams API remains unchanged
1444
        let params = CrcParams::new(
1445
            "API_TEST", 32, 0x04C11DB7, 0xFFFFFFFF, true, 0xFFFFFFFF, 0xCBF43926,
1446
        );
1447
1448
        // Verify all public fields are accessible and have expected types
1449
        let _algorithm: CrcAlgorithm = params.algorithm;
1450
        let _name: &'static str = params.name;
1451
        let _width: u8 = params.width;
1452
        let _poly: u64 = params.poly;
1453
        let _init: u64 = params.init;
1454
        let _refin: bool = params.refin;
1455
        let _refout: bool = params.refout;
1456
        let _xorout: u64 = params.xorout;
1457
        let _check: u64 = params.check;
1458
        let _keys: [u64; 23] = params.keys.to_keys_array_23();
1459
1460
        // Verify the algorithm is set to CrcCustom (unified custom variant)
1461
        assert!(matches!(params.algorithm, CrcAlgorithm::CrcCustom));
1462
1463
        // Test that CrcParams can be copied and cloned
1464
        let params_copy = params;
1465
        let params_clone = params;
1466
1467
        assert_eq!(params.keys, params_copy.keys);
1468
        assert_eq!(params.keys, params_clone.keys);
1469
1470
        // Test Debug formatting works
1471
        let debug_str = format!("{:?}", params);
1472
        assert!(debug_str.contains("CrcParams"));
1473
        assert!(debug_str.contains("API_TEST"));
1474
    }
1475
1476
    #[test]
1477
    fn test_crc_params_with_all_standard_algorithms() {
1478
        use crate::test::consts::TEST_ALL_CONFIGS;
1479
1480
        clear_cache();
1481
1482
        // Test creating CrcParams for all standard CRC algorithms
1483
        for config in TEST_ALL_CONFIGS {
1484
            // Create CrcParams using the same parameters as the standard algorithm
1485
            let params = crate::CrcParams::new(
1486
                config.get_name(),
1487
                config.get_width(),
1488
                config.get_poly(),
1489
                config.get_init(),
1490
                config.get_refin(),
1491
                config.get_xorout(),
1492
                config.get_check(),
1493
            );
1494
1495
            // Verify the created params match the expected configuration
1496
            assert_eq!(params.name, config.get_name());
1497
            assert_eq!(params.width, config.get_width());
1498
            assert_eq!(params.poly, config.get_poly());
1499
            assert_eq!(params.init, config.get_init());
1500
            assert_eq!(params.refin, config.get_refin());
1501
            assert_eq!(params.refout, config.get_refin());
1502
            assert_eq!(params.xorout, config.get_xorout());
1503
            assert_eq!(params.check, config.get_check());
1504
1505
            // Most importantly, verify the keys are correct
1506
            assert_eq!(
1507
                params.keys,
1508
                config.get_keys(),
1509
                "Keys should match expected values for {}",
1510
                config.get_name()
1511
            );
1512
1513
            // Create a second instance to test caching
1514
            let params2 = crate::CrcParams::new(
1515
                "CACHED_VERSION", // Different name shouldn't affect caching
1516
                config.get_width(),
1517
                config.get_poly(),
1518
                0x12345678, // Different init shouldn't affect caching
1519
                config.get_refin(),
1520
                0x87654321, // Different xorout shouldn't affect caching
1521
                0xABCDEF,   // Different check shouldn't affect caching
1522
            );
1523
1524
            // Keys should be identical (proving cache hit)
1525
            assert_eq!(
1526
                params.keys,
1527
                params2.keys,
1528
                "Cached keys should be identical for {}",
1529
                config.get_name()
1530
            );
1531
        }
1532
    }
1533
1534
    #[test]
1535
    fn test_crc_params_edge_cases() {
1536
        clear_cache();
1537
1538
        // Test edge cases for CrcParams creation
1539
1540
        // Test minimum and maximum polynomial values
1541
        let params_min_poly = crate::CrcParams::new("MIN_POLY", 32, 0x1, 0x0, false, 0x0, 0x0);
1542
        let params_max_poly =
1543
            crate::CrcParams::new("MAX_POLY", 32, 0xFFFFFFFF, 0x0, false, 0x0, 0x0);
1544
1545
        // Both should create valid instances
1546
        assert_eq!(params_min_poly.width, 32);
1547
        assert_eq!(params_min_poly.poly, 0x1);
1548
        assert_eq!(params_max_poly.width, 32);
1549
        assert_eq!(params_max_poly.poly, 0xFFFFFFFF);
1550
1551
        // Test both reflection modes
1552
        let params_reflected =
1553
            crate::CrcParams::new("REFLECTED", 32, 0x04C11DB7, 0x0, true, 0x0, 0x0);
1554
        let params_normal = crate::CrcParams::new("NORMAL", 32, 0x04C11DB7, 0x0, false, 0x0, 0x0);
1555
1556
        // Should have different keys due to different reflection
1557
        assert_ne!(params_reflected.keys, params_normal.keys);
1558
        assert!(params_reflected.refin);
1559
        assert!(params_reflected.refout);
1560
        assert!(!params_normal.refin);
1561
        assert!(!params_normal.refout);
1562
1563
        // Test 64-bit edge cases
1564
        let params64_min = crate::CrcParams::new("CRC64_MIN", 64, 0x1, 0x0, false, 0x0, 0x0);
1565
        let params64_max =
1566
            crate::CrcParams::new("CRC64_MAX", 64, 0xFFFFFFFFFFFFFFFF, 0x0, false, 0x0, 0x0);
1567
1568
        assert_eq!(params64_min.width, 64);
1569
        assert_eq!(params64_min.poly, 0x1);
1570
        assert_eq!(params64_max.width, 64);
1571
        assert_eq!(params64_max.poly, 0xFFFFFFFFFFFFFFFF);
1572
1573
        // Verify all instances have valid 23-element key arrays
1574
        assert_eq!(params_min_poly.keys.key_count(), 23);
1575
        assert_eq!(params_max_poly.keys.key_count(), 23);
1576
        assert_eq!(params_reflected.keys.key_count(), 23);
1577
        assert_eq!(params_normal.keys.key_count(), 23);
1578
        assert_eq!(params64_min.keys.key_count(), 23);
1579
        assert_eq!(params64_max.keys.key_count(), 23);
1580
    }
1581
1582
    #[test]
1583
    fn test_crc_params_concurrent_creation() {
1584
        use std::sync::{Arc, Barrier};
1585
        use std::thread;
1586
1587
        clear_cache();
1588
1589
        let num_threads = 8;
1590
        let barrier = Arc::new(Barrier::new(num_threads));
1591
        let mut handles = Vec::new();
1592
1593
        // All threads create CrcParams with the same parameters simultaneously
1594
        for i in 0..num_threads {
1595
            let barrier_clone = Arc::clone(&barrier);
1596
            let handle = thread::spawn(move || {
1597
                barrier_clone.wait();
1598
1599
                // All threads create the same CrcParams
1600
                let params = crate::CrcParams::new(
1601
                    "CONCURRENT_TEST",
1602
                    32,
1603
                    0x04C11DB7,
1604
                    0xFFFFFFFF,
1605
                    true,
1606
                    0xFFFFFFFF,
1607
                    0xCBF43926,
1608
                );
1609
1610
                (i, params)
1611
            });
1612
            handles.push(handle);
1613
        }
1614
1615
        // Collect results from all threads
1616
        let mut results = Vec::new();
1617
        for handle in handles {
1618
            results.push(handle.join().expect("Thread should not panic"));
1619
        }
1620
1621
        // Verify all threads completed successfully
1622
        assert_eq!(results.len(), num_threads);
1623
1624
        // Verify all CrcParams instances have identical keys
1625
        let first_keys = results[0].1.keys;
1626
        for (thread_id, params) in results {
1627
            assert_eq!(
1628
                params.keys, first_keys,
1629
                "Thread {} should have identical keys to other threads",
1630
                thread_id
1631
            );
1632
1633
            // Verify other fields are also correct
1634
            assert_eq!(params.name, "CONCURRENT_TEST");
1635
            assert_eq!(params.width, 32);
1636
            assert_eq!(params.poly, 0x04C11DB7);
1637
            assert_eq!(params.init, 0xFFFFFFFF);
1638
            assert!(params.refin);
1639
            assert!(params.refout);
1640
            assert_eq!(params.xorout, 0xFFFFFFFF);
1641
            assert_eq!(params.check, 0xCBF43926);
1642
        }
1643
1644
        // Verify the keys are mathematically correct
1645
        let expected_keys = generate::keys(32, 0x04C11DB7, true);
1646
        assert_eq!(
1647
            first_keys, expected_keys,
1648
            "All concurrent CrcParams should have correct keys"
1649
        );
1650
    }
1651
1652
    #[test]
1653
    fn test_lock_poisoning_recovery() {
1654
        use std::sync::{Arc, Barrier};
1655
        use std::thread;
1656
1657
        clear_cache();
1658
1659
        // This test is tricky because we need to poison the lock without
1660
        // actually breaking our test. We'll simulate lock poisoning by
1661
        // creating a scenario where a thread panics while holding a write lock.
1662
        // However, since our implementation uses best-effort error handling,
1663
        // it should gracefully degrade rather than propagate panics.
1664
1665
        // First, verify normal operation
1666
        let keys_before = get_or_generate_keys(32, 0x04C11DB7, true);
1667
        assert_eq!(keys_before.len(), 23);
1668
1669
        // Test that even if internal operations fail, the function still returns valid keys
1670
        // We can't easily poison the lock in a controlled way, but we can verify
1671
        // that our error handling works by testing edge cases
1672
1673
        // Multiple rapid cache operations that might stress the locking mechanism
1674
        let num_threads = 4;
1675
        let barrier = Arc::new(Barrier::new(num_threads));
1676
        let mut handles = Vec::new();
1677
1678
        for i in 0..num_threads {
1679
            let barrier_clone = Arc::clone(&barrier);
1680
            let handle = thread::spawn(move || {
1681
                barrier_clone.wait();
1682
1683
                // Rapid cache operations that might cause contention
1684
                for j in 0..20 {
1685
                    let poly = 0x04C11DB7 + (i as u64 * 1000) + (j as u64);
1686
                    let keys = get_or_generate_keys(32, poly, true);
1687
                    assert_eq!(
1688
                        keys.len(),
1689
                        23,
1690
                        "Thread {} iteration {} should return valid keys",
1691
                        i,
1692
                        j
1693
                    );
1694
1695
                    // Occasional cache clear to increase contention
1696
                    if j % 7 == 0 {
1697
                        clear_cache();
1698
                    }
1699
                }
1700
1701
                i
1702
            });
1703
            handles.push(handle);
1704
        }
1705
1706
        // All threads should complete successfully
1707
        for handle in handles {
1708
            let thread_id = handle.join().expect("Thread should not panic");
1709
            assert!(thread_id < num_threads);
1710
        }
1711
1712
        // Verify cache is still functional after stress testing
1713
        let keys_after = get_or_generate_keys(32, 0x04C11DB7, true);
1714
        assert_eq!(keys_after.len(), 23);
1715
1716
        // Keys should be mathematically correct regardless of cache state
1717
        let expected_keys = generate::keys(32, 0x04C11DB7, true);
1718
        assert_eq!(keys_after, expected_keys);
1719
    }
1720
1721
    #[test]
1722
    fn test_cache_behavior_with_thread_local_access() {
1723
        use std::thread;
1724
1725
        clear_cache();
1726
1727
        // Test that cache works correctly when accessed from different threads
1728
        // in sequence (not concurrently)
1729
1730
        let keys_main = get_or_generate_keys(32, 0x04C11DB7, true);
1731
1732
        let handle = thread::spawn(|| {
1733
            // This thread should see the cached value from the main thread
1734
1735
            get_or_generate_keys(32, 0x04C11DB7, true)
1736
        });
1737
1738
        let keys_from_thread = handle.join().expect("Thread should not panic");
1739
1740
        // Both should be identical
1741
        assert_eq!(keys_main, keys_from_thread);
1742
1743
        // Test multiple sequential threads
1744
        let mut thread_keys = Vec::new();
1745
1746
        for i in 0..5 {
1747
            let handle = thread::spawn(move || {
1748
                let keys = get_or_generate_keys(32, 0x04C11DB7, true);
1749
                (i, keys)
1750
            });
1751
1752
            let (thread_id, keys) = handle.join().expect("Thread should not panic");
1753
            thread_keys.push((thread_id, keys));
1754
        }
1755
1756
        // All threads should get the same cached keys
1757
        for (thread_id, keys) in thread_keys {
1758
            assert_eq!(
1759
                keys, keys_main,
1760
                "Thread {} should get same cached keys",
1761
                thread_id
1762
            );
1763
        }
1764
    }
1765
}