irithyll_core/reservoir/
delay_buffer.rs1use alloc::vec;
10use alloc::vec::Vec;
11
12pub struct DelayBuffer {
34 data: Vec<Vec<f64>>,
36 capacity: usize,
38 head: usize,
40 count: usize,
42}
43
44impl DelayBuffer {
45 pub fn new(capacity: usize) -> Self {
51 assert!(capacity > 0, "DelayBuffer capacity must be > 0");
52 Self {
53 data: vec![Vec::new(); capacity],
54 capacity,
55 head: 0,
56 count: 0,
57 }
58 }
59
60 pub fn push(&mut self, obs: &[f64]) {
64 self.data[self.head] = obs.to_vec();
65 self.head = (self.head + 1) % self.capacity;
66 if self.count < self.capacity {
67 self.count += 1;
68 }
69 }
70
71 pub fn get(&self, delay_idx: usize) -> Option<&[f64]> {
77 if delay_idx >= self.count {
78 return None;
79 }
80 let idx = (self.head + self.capacity - 1 - delay_idx) % self.capacity;
84 Some(&self.data[idx])
85 }
86
87 #[inline]
92 pub fn is_ready(&self) -> bool {
93 self.count >= self.capacity
94 }
95
96 #[inline]
98 pub fn len(&self) -> usize {
99 self.count
100 }
101
102 #[inline]
104 pub fn is_empty(&self) -> bool {
105 self.count == 0
106 }
107
108 #[inline]
110 pub fn capacity(&self) -> usize {
111 self.capacity
112 }
113
114 pub fn reset(&mut self) {
116 for slot in &mut self.data {
117 slot.clear();
118 }
119 self.head = 0;
120 self.count = 0;
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn basic_push_and_get() {
130 let mut buf = DelayBuffer::new(3);
131 buf.push(&[1.0, 10.0]);
132 buf.push(&[2.0, 20.0]);
133 buf.push(&[3.0, 30.0]);
134
135 assert!(buf.is_ready());
136 assert_eq!(buf.len(), 3);
137
138 assert_eq!(buf.get(0).unwrap(), &[3.0, 30.0]);
139 assert_eq!(buf.get(1).unwrap(), &[2.0, 20.0]);
140 assert_eq!(buf.get(2).unwrap(), &[1.0, 10.0]);
141 assert!(buf.get(3).is_none());
142 }
143
144 #[test]
145 fn wraparound_overwrites_oldest() {
146 let mut buf = DelayBuffer::new(2);
147 buf.push(&[1.0]);
148 buf.push(&[2.0]);
149 buf.push(&[3.0]); assert_eq!(buf.len(), 2);
152 assert_eq!(buf.get(0).unwrap(), &[3.0]);
153 assert_eq!(buf.get(1).unwrap(), &[2.0]);
154 assert!(buf.get(2).is_none());
155 }
156
157 #[test]
158 fn not_ready_until_full() {
159 let mut buf = DelayBuffer::new(3);
160 assert!(!buf.is_ready());
161 assert!(buf.is_empty());
162
163 buf.push(&[1.0]);
164 assert!(!buf.is_ready());
165 assert_eq!(buf.len(), 1);
166
167 buf.push(&[2.0]);
168 assert!(!buf.is_ready());
169 assert_eq!(buf.len(), 2);
170
171 buf.push(&[3.0]);
172 assert!(buf.is_ready());
173 assert_eq!(buf.len(), 3);
174 }
175
176 #[test]
177 fn get_returns_none_for_empty() {
178 let buf = DelayBuffer::new(5);
179 assert!(buf.get(0).is_none());
180 }
181
182 #[test]
183 fn get_partial_fill() {
184 let mut buf = DelayBuffer::new(5);
185 buf.push(&[10.0]);
186 buf.push(&[20.0]);
187
188 assert_eq!(buf.get(0).unwrap(), &[20.0]);
189 assert_eq!(buf.get(1).unwrap(), &[10.0]);
190 assert!(buf.get(2).is_none());
191 }
192
193 #[test]
194 fn reset_clears_all() {
195 let mut buf = DelayBuffer::new(3);
196 buf.push(&[1.0]);
197 buf.push(&[2.0]);
198 buf.push(&[3.0]);
199 assert!(buf.is_ready());
200
201 buf.reset();
202 assert!(buf.is_empty());
203 assert!(!buf.is_ready());
204 assert_eq!(buf.len(), 0);
205 assert!(buf.get(0).is_none());
206 }
207
208 #[test]
209 fn capacity_one() {
210 let mut buf = DelayBuffer::new(1);
211 buf.push(&[42.0]);
212 assert!(buf.is_ready());
213 assert_eq!(buf.get(0).unwrap(), &[42.0]);
214
215 buf.push(&[99.0]);
216 assert_eq!(buf.get(0).unwrap(), &[99.0]);
217 assert!(buf.get(1).is_none());
218 }
219
220 #[test]
221 fn many_pushes_stress_test() {
222 let cap = 5;
223 let mut buf = DelayBuffer::new(cap);
224 for i in 0..100 {
225 buf.push(&[i as f64]);
226 }
227 assert!(buf.is_ready());
228 assert_eq!(buf.len(), cap);
229
230 for d in 0..cap {
232 let expected = (99 - d) as f64;
233 assert_eq!(
234 buf.get(d).unwrap(),
235 &[expected],
236 "delay {} should be {}",
237 d,
238 expected,
239 );
240 }
241 }
242
243 #[test]
244 #[should_panic(expected = "capacity must be > 0")]
245 fn zero_capacity_panics() {
246 let _ = DelayBuffer::new(0);
247 }
248}