Skip to main content

fpzip_rs/core/
front.rs

1extern crate alloc;
2use alloc::vec;
3use alloc::vec::Vec;
4
5/// Circular buffer for the 3D wavefront predictor.
6pub struct Front<T: Copy + Default> {
7    zero: T,
8    dx: i32,
9    dy: i32,
10    dz: i32,
11    mask: i32,
12    index: i32,
13    buffer: Vec<T>,
14}
15
16impl<T: Copy + Default> Front<T> {
17    /// Creates a new Front buffer for the given dimensions.
18    pub fn new(nx: i32, ny: i32, zero: T) -> Self {
19        let dx = 1;
20        let dy = nx + 1;
21        let dz = dy * (ny + 1);
22        let mask = compute_mask(dx + dy + dz);
23        Self {
24            zero,
25            dx,
26            dy,
27            dz,
28            mask,
29            index: 0,
30            buffer: vec![T::default(); (mask + 1) as usize],
31        }
32    }
33
34    /// Fetches a neighbor relative to the current sample position.
35    #[inline]
36    pub fn get(&self, x: i32, y: i32, z: i32) -> T {
37        let idx = (self.index - self.dx * x - self.dy * y - self.dz * z) & self.mask;
38        self.buffer[idx as usize]
39    }
40
41    /// Adds a sample to the front.
42    #[inline]
43    pub fn push(&mut self, value: T) {
44        self.buffer[(self.index & self.mask) as usize] = value;
45        self.index += 1;
46    }
47
48    /// Adds n copies of a value to the front.
49    pub fn push_n(&mut self, value: T, count: i32) {
50        for _ in 0..count {
51            self.buffer[(self.index & self.mask) as usize] = value;
52            self.index += 1;
53        }
54    }
55
56    /// Advances the front, filling with zeros.
57    pub fn advance(&mut self, x: i32, y: i32, z: i32) {
58        self.push_n(self.zero, self.dx * x + self.dy * y + self.dz * z);
59    }
60}
61
62/// Computes the smallest power-of-2-minus-1 mask that is >= n-1.
63fn compute_mask(mut n: i32) -> i32 {
64    n -= 1;
65    n |= n >> 1;
66    n |= n >> 2;
67    n |= n >> 4;
68    n |= n >> 8;
69    n |= n >> 16;
70    n
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn init_to_zero() {
79        let f = Front::<u32>::new(4, 4, 0);
80        assert_eq!(f.get(0, 0, 0), 0);
81        assert_eq!(f.get(1, 0, 0), 0);
82        assert_eq!(f.get(0, 1, 0), 0);
83        assert_eq!(f.get(0, 0, 1), 0);
84    }
85
86    #[test]
87    fn push_and_retrieve() {
88        let mut f = Front::<u32>::new(4, 4, 0);
89        f.push(42);
90        // After push, index advanced by 1, so get(1,0,0) should be 42
91        // since dx=1 and the value was at old index
92        assert_eq!(f.get(1, 0, 0), 42);
93    }
94
95    #[test]
96    fn advance_fills_zeros() {
97        let mut f = Front::<u32>::new(4, 4, 0);
98        f.push(99);
99        f.advance(1, 0, 0); // advance by dx=1 slot, filling with zero
100        assert_eq!(f.get(1, 0, 0), 0);
101        // The 99 should now be at distance 2 in the x direction
102        assert_eq!(f.get(2, 0, 0), 99);
103    }
104
105    #[test]
106    fn circular_buffer_wraps() {
107        let mut f = Front::<u32>::new(2, 2, 0);
108        let buf_size = (f.mask + 1) as u32;
109        // Fill entire buffer and beyond
110        for i in 0..buf_size * 2 {
111            f.push(i);
112        }
113        // Should still work (wrapped around)
114        let last = buf_size * 2 - 1;
115        assert_eq!(f.get(1, 0, 0), last);
116    }
117}