#[derive(Debug, Clone, Default)]
pub struct VisibleSet {
stamp: Vec<u32>,
frame: u32,
touched: Vec<u32>,
}
impl VisibleSet {
pub fn new() -> Self {
Self::default()
}
pub fn begin_frame(&mut self) {
self.touched.clear();
match self.frame.checked_add(1) {
Some(next) => self.frame = next,
None => {
for s in &mut self.stamp {
*s = 0;
}
self.frame = 1;
}
}
}
pub fn insert_all(&mut self, keys: &[u32]) {
if let Some(&max) = keys.iter().max() {
let need = max as usize + 1;
if self.stamp.len() < need {
self.stamp.resize(need, 0);
}
}
for &k in keys {
let slot = &mut self.stamp[k as usize];
if *slot != self.frame {
*slot = self.frame;
self.touched.push(k);
}
}
}
#[inline]
pub fn contains(&self, key: u32) -> bool {
self.stamp
.get(key as usize)
.is_some_and(|&s| s == self.frame)
}
#[inline]
pub fn keys(&self) -> &[u32] {
&self.touched
}
#[inline]
pub fn len(&self) -> usize {
self.touched.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.touched.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn begin_frame_clears_without_touching_memory() {
let mut s = VisibleSet::new();
s.begin_frame();
s.insert_all(&[3, 7, 11]);
assert!(s.contains(7));
assert_eq!(s.len(), 3);
let ptr_before = s.stamp.as_ptr();
let len_before = s.stamp.len();
s.begin_frame();
assert_eq!(s.stamp.as_ptr(), ptr_before, "begin_frame reallocated");
assert_eq!(s.stamp.len(), len_before, "begin_frame resized");
assert!(!s.contains(7));
assert!(s.is_empty());
assert_eq!(s.keys(), &[] as &[u32]);
}
#[test]
fn inserting_a_key_twice_in_one_frame_lists_it_once() {
let mut s = VisibleSet::new();
s.begin_frame();
s.insert_all(&[4, 4, 9]);
s.insert_all(&[9, 4]);
assert_eq!(s.len(), 2);
assert_eq!(s.keys(), &[4, 9]);
}
#[test]
fn stamp_wraparound_is_safe() {
let mut s = VisibleSet::new();
s.begin_frame();
s.insert_all(&[1, 2, 3]);
s.frame = u32::MAX;
s.touched.clear();
s.insert_all(&[2]);
assert!(s.contains(2));
assert!(!s.contains(1), "key 1 was stamped on an older frame");
s.begin_frame();
assert_eq!(s.frame, 1, "counter must restart, not wrap onto a live stamp");
assert!(
!s.contains(2),
"the u32::MAX stamp must not survive the wrap and read as frame 1"
);
assert!(!s.contains(1));
assert!(!s.contains(3));
s.insert_all(&[3]);
assert!(s.contains(3));
assert!(!s.contains(2));
}
#[test]
fn a_key_beyond_the_backing_array_is_absent_not_a_panic() {
let mut s = VisibleSet::new();
s.begin_frame();
s.insert_all(&[2]);
assert!(!s.contains(9_999));
assert!(!s.contains(u32::MAX));
}
}