Skip to main content

lzip_parallel/
deflate_scan.rs

1//! DEFLATE full-flush boundary scanner — crate-local parallel split.
2//!
3//! The SIMD scanner itself now lives in the shared `linflate` crate
4//! (`linflate::deflate_scan`); this module keeps only this codec's rayon-based
5//! parallel split-point search, which uses the crate's own thread pool.
6
7use linflate::deflate_scan::find_next_flush;
8
9pub use linflate::deflate_scan::{FlushBoundary, find_all_flushes};
10
11/// Parallel split-point search: N−1 gatling workers each find the nearest flush
12/// boundary at or after their nominal byte offset.
13///
14/// `n_splits` is typically the number of physical cores.
15/// Returns up to `n_splits − 1` boundaries, sorted and deduplicated.
16pub fn split_boundaries_parallel(buf: &[u8], n_splits: usize) -> Vec<FlushBoundary> {
17    if n_splits <= 1 || buf.len() < 8 {
18        return Vec::new();
19    }
20
21    // Indices 1..n_splits ⇒ gatling units 0..n_splits-1 (unit k ⇒ split i=k+1).
22    let mut found: Vec<Option<FlushBoundary>> =
23        gatling::gatling_forkjoin::gatling_for_each(n_splits - 1, 0, |k| {
24            let i = k + 1;
25            let nominal = buf.len() * i / n_splits;
26            find_next_flush(buf, nominal)
27        });
28
29    let mut result: Vec<FlushBoundary> = found.drain(..).flatten().collect();
30    result.sort_by_key(|b| b.start);
31    result.dedup_by_key(|b| b.start);
32    result
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38
39    fn make_flush_marker() -> Vec<u8> {
40        vec![0x00, 0x00, 0x00, 0xFF, 0xFF]
41    }
42
43    #[test]
44    fn split_finds_markers() {
45        let mut buf = Vec::new();
46        buf.extend_from_slice(&[0xAA; 32]);
47        buf.extend_from_slice(&make_flush_marker());
48        buf.extend_from_slice(&[0xBB; 32]);
49        // Raw pattern scan must locate the synthetic boundary.
50        let all = find_all_flushes(&buf);
51        assert_eq!(all.len(), 1);
52        assert_eq!(all[0].start, 37);
53    }
54}