use alloc::vec::Vec;
#[derive(Clone, Debug, Default)]
#[expect(
clippy::redundant_pub_crate,
reason = "shared with engine and builder modules"
)]
pub(crate) struct TraverseScratch {
marks: Vec<u32>,
frontier: Vec<u32>,
next: Vec<u32>,
output: Vec<u32>,
epoch: u32,
}
#[expect(
clippy::missing_const_for_fn,
reason = "Vec mutation and borrows are not const-compatible"
)]
impl TraverseScratch {
pub(crate) fn resize_for_nodes(&mut self, node_count: usize) {
self.marks.resize(node_count, 0);
if self.frontier.capacity() < node_count {
self.frontier.reserve(node_count);
}
if self.next.capacity() < node_count {
self.next.reserve(node_count);
}
}
pub(crate) fn reset_after_snapshot(&mut self, node_count: usize) {
self.resize_for_nodes(node_count);
self.marks.fill(0);
self.frontier.clear();
self.next.clear();
self.output.clear();
self.epoch = 0;
}
pub(crate) fn bump_epoch(&mut self) -> u32 {
if self.epoch == u32::MAX {
self.marks.fill(0);
self.epoch = 1;
} else {
self.epoch += 1;
}
self.epoch
}
#[must_use]
pub(crate) fn try_mark_visited(&mut self, node: u32, epoch: u32) -> bool {
let Some(slot) = self.marks.get_mut(node as usize) else {
return false;
};
if *slot == epoch {
return false;
}
*slot = epoch;
true
}
pub(crate) fn frontier(&self) -> &[u32] {
&self.frontier
}
pub(crate) fn frontier_mut(&mut self) -> &mut Vec<u32> {
&mut self.frontier
}
pub(crate) fn next_mut(&mut self) -> &mut Vec<u32> {
&mut self.next
}
pub(crate) fn swap_frontiers(&mut self) {
core::mem::swap(&mut self.frontier, &mut self.next);
}
pub(crate) fn clear_next(&mut self) {
self.next.clear();
}
pub(crate) fn output_mut(&mut self) -> &mut Vec<u32> {
&mut self.output
}
}