extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
pub struct Front<T: Copy + Default> {
zero: T,
dx: i32,
dy: i32,
dz: i32,
mask: i32,
index: i32,
buffer: Vec<T>,
}
impl<T: Copy + Default> Front<T> {
pub fn new(nx: i32, ny: i32, zero: T) -> Self {
let dx = 1;
let dy = nx + 1;
let dz = dy * (ny + 1);
let mask = compute_mask(dx + dy + dz);
Self {
zero,
dx,
dy,
dz,
mask,
index: 0,
buffer: vec![T::default(); (mask + 1) as usize],
}
}
#[inline]
pub fn get(&self, x: i32, y: i32, z: i32) -> T {
let idx = (self.index - self.dx * x - self.dy * y - self.dz * z) & self.mask;
self.buffer[idx as usize]
}
#[inline]
pub fn push(&mut self, value: T) {
self.buffer[(self.index & self.mask) as usize] = value;
self.index += 1;
}
pub fn push_n(&mut self, value: T, count: i32) {
for _ in 0..count {
self.buffer[(self.index & self.mask) as usize] = value;
self.index += 1;
}
}
pub fn advance(&mut self, x: i32, y: i32, z: i32) {
self.push_n(self.zero, self.dx * x + self.dy * y + self.dz * z);
}
}
fn compute_mask(mut n: i32) -> i32 {
n -= 1;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn init_to_zero() {
let f = Front::<u32>::new(4, 4, 0);
assert_eq!(f.get(0, 0, 0), 0);
assert_eq!(f.get(1, 0, 0), 0);
assert_eq!(f.get(0, 1, 0), 0);
assert_eq!(f.get(0, 0, 1), 0);
}
#[test]
fn push_and_retrieve() {
let mut f = Front::<u32>::new(4, 4, 0);
f.push(42);
assert_eq!(f.get(1, 0, 0), 42);
}
#[test]
fn advance_fills_zeros() {
let mut f = Front::<u32>::new(4, 4, 0);
f.push(99);
f.advance(1, 0, 0); assert_eq!(f.get(1, 0, 0), 0);
assert_eq!(f.get(2, 0, 0), 99);
}
#[test]
fn circular_buffer_wraps() {
let mut f = Front::<u32>::new(2, 2, 0);
let buf_size = (f.mask + 1) as u32;
for i in 0..buf_size * 2 {
f.push(i);
}
let last = buf_size * 2 - 1;
assert_eq!(f.get(1, 0, 0), last);
}
}