tsz_compress/v2/
queue.rs

1use super::encode::Bits;
2
3///
4/// A statically sized ring-buffer queue used
5/// while compressing a column.
6///
7/// The absolute max size of this buffer is 16 elements.
8///
9#[derive(Debug)]
10pub struct CompressionQueue<const N: usize> {
11    zigzag: [usize; 16],
12    bitcount: [usize; 16],
13    front: usize,
14    len: usize,
15}
16
17impl<const N: usize> CompressionQueue<N> {
18    ///
19    /// Creates an empty queue.
20    ///
21    pub const fn new() -> Self {
22        assert!(N <= 16);
23        CompressionQueue {
24            zigzag: [0; 16],
25            bitcount: [0; 16],
26            front: 0,
27            len: 0,
28        }
29    }
30
31    ///
32    /// Returns the number of elements in the queue.
33    ///
34    pub const fn len(&self) -> usize {
35        self.len
36    }
37
38    ///
39    /// Returns true if the queue if the queue would
40    /// have to overwrite an element to push a new value.
41    ///
42    pub fn is_full(&self) -> bool {
43        self.len >= N
44    }
45
46    ///
47    /// Returns true if the queue if there is
48    /// nothing to pop from the queue.
49    ///
50    pub fn is_empty(&self) -> bool {
51        self.len == 0
52    }
53
54    ///
55    /// Pushes a value into the queue,
56    /// overwriting the oldest value if the queue is full.
57    ///
58    pub fn push<T: Bits + Sized>(&mut self, value: T) {
59        let index = (self.front + self.len) % 16;
60        unsafe { self.write(index, value) };
61        if self.len < 16 {
62            self.len += 1;
63        } else {
64            self.front = (self.front + 1) % 16;
65        }
66    }
67
68    ///
69    /// Pops the oldest value from the queue,
70    /// returning None if the queue is empty.
71    ///
72    pub fn pop(&mut self) -> Option<usize> {
73        if self.is_empty() {
74            return None;
75        }
76
77        let value = unsafe { self.value_at(self.front) };
78        self.front = (self.front + 1) % 16;
79        self.len -= 1;
80        Some(value)
81    }
82
83    ///
84    /// Pop N values from the queue at once,
85    /// values may not be meaningful if the queue is
86    /// not of length N.
87    ///
88    #[inline(always)]
89    pub fn pop_n<const M: usize>(&mut self) -> [usize; M] {
90        let mut values: [usize; M] = [0; M];
91        for i in 0..M {
92            let index = (self.front + i) % 16;
93            unsafe {
94                *values.get_unchecked_mut(i) = self.value_at(index);
95            }
96        }
97        self.front = (self.front + M) % 16;
98        self.len -= M;
99        values
100    }
101
102    ///
103    /// Peak N values from the queue at once,
104    /// values may not be meaningful if the queue is
105    /// not of length N.
106    ///
107    #[inline(always)]
108    pub fn peak_bitcounts<const M: usize>(&mut self) -> [usize; M] {
109        let mut values: [usize; M] = [0; M];
110        for i in 0..M {
111            let index = (self.front + i) % 16;
112            unsafe {
113                *values.get_unchecked_mut(i) = self.count_at(index);
114            }
115        }
116        values
117    }
118
119    ///
120    /// Internal use accessor to an initialized value.
121    ///
122    /// # Safety
123    /// This function is unsafe because it assumes that
124    /// the index is inbounds and initialized.
125    ///
126    unsafe fn value_at(&self, index: usize) -> usize {
127        *self.zigzag.get_unchecked(index)
128    }
129
130    ///
131    /// Internal use accessor to an initialized value.
132    ///
133    /// # Safety
134    /// This function is unsafe because it assumes that
135    /// the index is inbounds and initialized.
136    ///
137    unsafe fn count_at(&self, index: usize) -> usize {
138        *self.bitcount.get_unchecked(index)
139    }
140
141    ///
142    /// Internal use mutable accessor to an initialized value.
143    ///
144    /// # Safety
145    /// This function is unsafe because it assumes that
146    /// the index is inbounds. It does *not* drop the value
147    /// at the index if it was initialized; therefore, T must
148    /// be trivially dropable.
149    ///
150    unsafe fn write<T: Bits + Sized>(&mut self, index: usize, t: T) {
151        let (zbits, zcount) = t.zigzag_bits();
152        *self.zigzag.get_unchecked_mut(index) = zbits;
153        *self.bitcount.get_unchecked_mut(index) = zcount;
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::*;
160
161    #[test]
162    fn can_init() {
163        let queue: CompressionQueue<10> = CompressionQueue::new();
164        assert_eq!(queue.len(), 0);
165        assert_eq!(queue.is_full(), false);
166        assert_eq!(queue.is_empty(), true);
167    }
168
169    #[test]
170    fn is_empty_or_full() {
171        let mut queue: CompressionQueue<4> = CompressionQueue::new();
172        assert_eq!(queue.len(), 0);
173        assert_eq!(queue.is_empty(), true);
174        assert_eq!(queue.is_full(), false);
175
176        // push 4 values, queue should be full
177        for i in 0..4 {
178            queue.push(i as i8);
179            assert_eq!(queue.len(), i + 1);
180            assert_eq!(queue.is_empty(), false);
181            assert_eq!(queue.is_full(), i == 3);
182        }
183
184        // pop 4 values, queue should be empty
185        for i in 0..4 {
186            assert_eq!(queue.pop(), Some((i as i8).zigzag()));
187            assert_eq!(queue.len(), 3 - i);
188            assert_eq!(queue.is_empty(), (i == 3));
189            assert_eq!(queue.is_full(), false);
190        }
191    }
192
193    #[test]
194    fn can_overwrite() {
195        let mut queue: CompressionQueue<4> = CompressionQueue::new();
196
197        // push 4 values, queue should be full
198        for i in 0..4 {
199            queue.push(i as i8);
200            assert_eq!(queue.len(), i + 1);
201            assert_eq!(queue.is_empty(), false);
202            assert_eq!(queue.is_full(), i == 3);
203        }
204
205        // keep pushing, queue should still be full
206        for i in 4..8 {
207            queue.push(i as i8);
208            assert_eq!(queue.len(), i + 1);
209            assert_eq!(queue.is_empty(), false);
210            assert_eq!(queue.is_full(), true);
211        }
212
213        // keep pushing, queue should still be full and start overwriting
214        for i in 8..20 {
215            queue.push(i as i8);
216            assert_eq!(queue.len(), (i + 1).min(16));
217            assert_eq!(queue.is_empty(), false);
218            assert_eq!(queue.is_full(), true);
219        }
220
221        // pop the values, they should be 16..20
222        for j in 0..4 {
223            assert_eq!(queue.pop(), Some(((j + 4) as i8).zigzag()));
224            assert_eq!(queue.len(), 15 - j);
225            assert_eq!(queue.is_empty(), false);
226            assert_eq!(queue.is_full(), true);
227        }
228
229        // pop another 8 values, then the queue will start to empty
230        queue.pop_n::<8>();
231        assert_eq!(queue.len(), 4);
232        assert_eq!(queue.is_empty(), false);
233        assert_eq!(queue.is_full(), true);
234
235        // pop the remaining 4 values, then the queue will be empty
236        for j in 0..4 {
237            assert_eq!(queue.pop(), Some(((j + 16) as i8).zigzag()));
238            assert_eq!(queue.len(), 3 - j);
239            assert_eq!(queue.is_empty(), (j == 3));
240            assert_eq!(queue.is_full(), false);
241        }
242    }
243
244    #[test]
245    fn fuzz() {
246        use alloc::collections::VecDeque;
247        use rand::Rng;
248
249        let mut rng = rand::thread_rng();
250        let mut std_queue: VecDeque<usize> = VecDeque::new();
251        let mut queue: CompressionQueue<10> = CompressionQueue::new();
252        for _ in 0..10000 {
253            let value = rng.gen::<i32>();
254            let zig_zag_value = value.zigzag();
255            if rng.gen::<bool>() {
256                std_queue.push_back(zig_zag_value as usize);
257                if queue.len() == 16 {
258                    assert_eq!(std_queue.pop_front(), queue.pop());
259                }
260                queue.push(value);
261            } else {
262                assert_eq!(std_queue.pop_front(), queue.pop());
263            }
264            assert_eq!(std_queue.len(), queue.len());
265
266            for _ in 0..queue.len() {
267                assert_eq!(std_queue.pop_front(), queue.pop());
268            }
269        }
270    }
271}