Skip to main content

keyhog_scanner/engine/
gpu_phase2.rs

1use super::*;
2
3impl CompiledScanner {
4    pub fn scan_coalesced_gpu_phase2(
5        &self,
6        chunks: &[keyhog_core::Chunk],
7        per_chunk_hits: Vec<Vec<(u32, u32, u32)>>,
8    ) -> Vec<Vec<keyhog_core::RawMatch>> {
9        use rayon::prelude::*;
10        let mut results: Vec<Vec<keyhog_core::RawMatch>> = chunks
11            .par_iter()
12            .zip(per_chunk_hits.into_par_iter())
13            .map(|(chunk, hits)| {
14                let prepared = self.prepare_chunk(chunk);
15                let mut matches = self.scan_prepared_with_pattern_hits(prepared, hits, None);
16                // Parity with SIMD's `scan_chunks_with_backend` path:
17                // `scan_with_backend` → `scan_with_deadline_and_backend`
18                // calls `post_process_matches` after the in-chunk scan,
19                // which decode-recurses (base64/hex/url) and reassembles
20                // cross-chunk-fragment secrets. The GPU path previously
21                // skipped this - the gpu_parity test catches the
22                // missed StackBlitz finding extracted from the
23                // base64-decoded sub-chunk of the stripe-aws fixture.
24                // A prior comment here claimed SIMD's `scan_coalesced`
25                // also skips post-process; that's true for the bulk-
26                // scan entry point but NOT for `scan_chunks_with_backend`,
27                // which is the API the parity test (and operators
28                // forcing `--backend gpu`) actually call.
29                self.post_process_matches(chunk, &mut matches, None);
30                matches
31            })
32            .collect();
33
34        // Cross-chunk boundary reassembly: identical contract to the
35        // SIMD path. Without this, a secret straddling the seam between
36        // two adjacent windows of one big file slips through the GPU
37        // dispatch (the inter-chunk separator bytes intentionally make
38        // the literal-set engine ignore the seam) AND through the
39        // per-chunk extraction loop above (each chunk only sees its
40        // own slice). The boundary helper synthesises a thin tail+head
41        // buffer per gapless pair and rescans it on the CPU path, so
42        // GPU users get the same recall as SIMD users on big files.
43        super::boundary::scan_chunk_boundaries(self, chunks, &mut results);
44        results
45    }
46}