use super::phase2::Phase2AlwaysActiveGpuEvidence;
use super::windowed_support::{record_window_match, window_chunk, window_ranges};
use super::*;
use std::collections::{HashSet, VecDeque};
impl CompiledScanner {
pub(crate) fn scan_windowed(
&self,
chunk: &Chunk,
backend: crate::hw_probe::ScanBackend,
deadline: Option<std::time::Instant>,
route: crate::ScanExecutionRoute,
) -> crate::error::Result<Vec<RawMatch>> {
use rayon::prelude::*;
let chunk_text = &chunk.data;
if reject_oversized_window_chunk(chunk, chunk_text) {
return Ok(Vec::new());
}
let line_offsets = crate::compute_line_offsets(chunk_text);
let ranges = window_ranges(chunk_text, MAX_SCAN_CHUNK_BYTES, WINDOW_OVERLAP_BYTES);
let telemetry = crate::telemetry::capture_scan_telemetry();
let recovery_receipts = crate::gpu::capture_recovery_receipts();
let window_matches: crate::error::Result<Vec<(usize, usize, Vec<RawMatch>)>> = ranges
.par_iter()
.map(|&(offset, end)| {
crate::gpu::with_captured_recovery_receipts(recovery_receipts.as_ref(), || {
crate::telemetry::with_captured_scan_telemetry(telemetry.as_ref(), || {
let window_len = end - offset;
if crate::deadline::expired(deadline) {
return Ok((offset, window_len, Vec::new()));
}
let window_chunk = window_chunk(chunk, offset, end);
self.scan_inner(&window_chunk, backend, deadline, route)
.map(|matches| (offset, window_len, matches))
})
})
})
.collect();
let mut all_matches = Vec::with_capacity(estimate_window_match_capacity(chunk_text.len()));
let mut seen = HashSet::new();
let mut seen_order = VecDeque::new();
for (offset, window_len, matches) in window_matches? {
for mut raw_match in matches {
if record_window_match(
&line_offsets,
chunk.metadata.base_offset,
chunk.metadata.base_line,
offset,
window_len,
&mut raw_match,
&mut seen,
&mut seen_order,
) {
all_matches.push(raw_match);
}
}
}
Ok(all_matches)
}
pub(crate) fn scan_windowed_with_triggered(
&self,
chunk: &Chunk,
triggered_patterns: &[u64],
deadline: Option<std::time::Instant>,
phase2_keyword_hints: Option<&[u32]>,
phase2_always_active_gpu_evidence: Option<Phase2AlwaysActiveGpuEvidence<'_>>,
confirmed_anchor_literal_matches: Option<&[(u32, u32)]>,
generic_keyword_positions: Option<&[u32]>,
route: crate::ScanExecutionRoute,
) -> crate::error::Result<Vec<RawMatch>> {
use rayon::prelude::*;
let chunk_text = &chunk.data;
if reject_oversized_window_chunk(chunk, chunk_text) {
return Ok(Vec::new());
}
let mut all_matches = Vec::with_capacity(estimate_window_match_capacity(chunk_text.len()));
let mut seen = HashSet::new();
let mut seen_order = VecDeque::new();
let line_offsets = crate::compute_line_offsets(chunk_text);
let ranges = window_ranges(chunk_text, MAX_SCAN_CHUNK_BYTES, WINDOW_OVERLAP_BYTES);
let telemetry = crate::telemetry::capture_scan_telemetry();
let recovery_receipts = crate::gpu::capture_recovery_receipts();
let window_matches: crate::error::Result<Vec<(usize, usize, Vec<RawMatch>)>> = ranges
.par_iter()
.map(|&(offset, end)| {
crate::gpu::with_captured_recovery_receipts(recovery_receipts.as_ref(), || {
crate::telemetry::with_captured_scan_telemetry(telemetry.as_ref(), || {
let window_len = end - offset;
if crate::deadline::expired(deadline) {
return Ok((offset, window_len, Vec::new()));
}
let window_chunk = window_chunk(chunk, offset, end);
let prepared = self.prepare_chunk(&window_chunk);
let window_phase2_always_anchor_matches;
let phase2_always_evidence =
if let Some(evidence) = phase2_always_active_gpu_evidence {
if let Some(matches) = evidence.anchor_literal_matches {
window_phase2_always_anchor_matches = matches
.iter()
.filter_map(|&(literal_idx, pos)| {
let pos = pos as usize;
(pos >= offset && pos < end)
.then(|| (literal_idx, (pos - offset) as u32))
})
.collect::<Vec<_>>();
Some(Phase2AlwaysActiveGpuEvidence {
anchor_literal_matches: Some(
window_phase2_always_anchor_matches.as_slice(),
),
..evidence
})
} else {
Some(evidence)
}
} else {
None
};
let window_confirmed_anchor_matches;
let confirmed_anchor_matches =
if let Some(matches) = confirmed_anchor_literal_matches {
window_confirmed_anchor_matches = matches
.iter()
.filter_map(|&(literal_idx, pos)| {
let pos = pos as usize;
(pos >= offset && pos < end)
.then(|| (literal_idx, (pos - offset) as u32))
})
.collect::<Vec<_>>();
Some(window_confirmed_anchor_matches.as_slice())
} else {
None
};
let window_generic_keyword_positions;
let generic_positions = if let Some(positions) = generic_keyword_positions {
window_generic_keyword_positions = positions
.iter()
.filter_map(|&pos| {
let pos = pos as usize;
(pos >= offset && pos < end).then(|| (pos - offset) as u32)
})
.collect::<Vec<_>>();
Some(window_generic_keyword_positions.as_slice())
} else {
None
};
let matches = self.scan_prepared_with_triggered(
prepared,
triggered_patterns,
deadline,
phase2_keyword_hints,
phase2_always_evidence,
confirmed_anchor_matches,
generic_positions,
route,
)?;
Ok((offset, window_len, matches))
})
})
})
.collect();
let window_matches = window_matches?;
for (offset, window_len, matches) in window_matches {
for mut raw_match in matches {
if record_window_match(
&line_offsets,
chunk.metadata.base_offset,
chunk.metadata.base_line,
offset,
window_len,
&mut raw_match,
&mut seen,
&mut seen_order,
) {
all_matches.push(raw_match);
}
}
}
Ok(all_matches)
}
}
fn estimate_window_match_capacity(chunk_len: usize) -> usize {
(chunk_len / 4096).max(16)
}
pub(crate) const MAX_WINDOW_CHUNK_BYTES: usize = 4 * 1024 * 1024 * 1024;
pub(crate) fn reject_oversized_window_chunk(chunk: &Chunk, chunk_text: &str) -> bool {
if chunk_text.len() <= MAX_WINDOW_CHUNK_BYTES {
return false;
}
tracing::warn!(
"Chunk from {} exceeds {}MiB windowed-scan ceiling ({} bytes); skipping this chunk to prevent OOM. COVERAGE LOSS for this input.",
chunk.metadata.path.as_deref().unwrap_or("unknown"), MAX_WINDOW_CHUNK_BYTES / (1024 * 1024),
chunk_text.len()
);
true
}