pub struct ScratchRegion {
data: Vec<f32>,
cursor: usize,
}
impl ScratchRegion {
pub fn new(initial_capacity: usize) -> Self {
Self {
data: vec![0.0; initial_capacity],
cursor: 0,
}
}
pub fn alloc(&mut self, len: usize) -> Option<&mut [f32]> {
let new_cursor = self.cursor.checked_add(len)?;
if new_cursor > self.data.len() {
let new_cap = self
.data
.len()
.max(1024)
.max(new_cursor)
.checked_mul(2)
.unwrap_or(new_cursor);
self.data.resize(new_cap, 0.0);
}
let start = self.cursor;
self.cursor = new_cursor;
let slice = &mut self.data[start..new_cursor];
slice.fill(0.0);
Some(slice)
}
pub fn reset(&mut self) {
self.cursor = 0;
}
pub fn used(&self) -> usize {
self.cursor
}
pub fn capacity(&self) -> usize {
self.data.len()
}
pub fn memory_bytes(&self) -> usize {
self.data.len() * std::mem::size_of::<f32>()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alloc_returns_zeroed_slice() {
let mut scratch = ScratchRegion::new(1024);
let s = scratch.alloc(10).unwrap();
assert_eq!(s.len(), 10);
assert!(s.iter().all(|&v| v == 0.0));
}
#[test]
fn sequential_allocs_dont_overlap() {
let mut scratch = ScratchRegion::new(1024);
let a = scratch.alloc(5).unwrap();
a[0] = 1.0;
a[4] = 5.0;
let a_ptr = a.as_ptr();
let b = scratch.alloc(3).unwrap();
b[0] = 10.0;
let b_ptr = b.as_ptr();
assert_ne!(a_ptr, b_ptr);
assert_eq!(scratch.used(), 8);
}
#[test]
fn reset_allows_reuse() {
let mut scratch = ScratchRegion::new(1024);
scratch.alloc(100).unwrap();
assert_eq!(scratch.used(), 100);
scratch.reset();
assert_eq!(scratch.used(), 0);
let s = scratch.alloc(50).unwrap();
assert_eq!(s.len(), 50);
assert!(s.iter().all(|&v| v == 0.0));
}
#[test]
fn grows_beyond_initial_capacity() {
let mut scratch = ScratchRegion::new(10);
let s = scratch.alloc(100).unwrap();
assert_eq!(s.len(), 100);
assert!(scratch.capacity() >= 100);
}
#[test]
fn zero_alloc_is_valid() {
let mut scratch = ScratchRegion::new(1024);
let s = scratch.alloc(0).unwrap();
assert!(s.is_empty());
assert_eq!(scratch.used(), 0);
}
#[test]
fn memory_bytes_tracks_capacity() {
let scratch = ScratchRegion::new(1024);
assert_eq!(scratch.memory_bytes(), 1024 * 4);
}
#[test]
fn growth_overflow_falls_back_to_exact_fit() {
let mut scratch = ScratchRegion::new(0);
let s = scratch.alloc(10).unwrap();
assert_eq!(s.len(), 10);
assert!(scratch.capacity() >= 2048);
}
}