1use core::ops::Index;
2#[derive(Debug)]
6pub struct Cache<T, const N: usize> {
7 vals: [T; N],
9 len: usize,
13 next_head: usize,
15}
16impl<T, const N: usize> Cache<T, N> {
17 #[inline]
21 pub const fn len(&self) -> usize {
22 self.len
23 }
24 #[inline]
26 pub const fn is_empty(&self) -> bool {
27 self.len == 0
28 }
29}
30macro_rules! cache {
35 ( $( $x:literal),* ) => {
36 $(
37impl<T> Cache<T, $x> {
38 #[inline]
43 pub fn get(&self, idx: usize) -> Option<&T> {
44 (idx < self.len).then(|| self.vals.index(self.next_head.wrapping_sub(1).wrapping_sub(idx) & ($x - 1)))
45 }
46 #[inline]
55 pub fn get_unchecked(&self, idx: usize) -> &T {
56 self.vals.index(self.next_head.wrapping_sub(1).wrapping_sub(idx) & ($x - 1))
57 }
58 #[expect(clippy::arithmetic_side_effects, reason = "must, and overflow is not possible")]
61 #[expect(clippy::indexing_slicing, reason = "wont panic since next_head is always correct")]
62 #[inline]
63 pub fn push(&mut self, val: T) {
64 if self.len < $x {
65 self.len += 1;
66 }
67 self.vals[self.next_head] = val;
70 self.next_head = (self.next_head + 1) & ($x - 1);
71 }
72}
73impl<T> Index<usize> for Cache<T, $x> {
74 type Output = T;
75 #[inline]
76 fn index(&self, index: usize) -> &Self::Output {
77 self.get(index).unwrap()
78 }
79}
80 )*
81 };
82}
83cache!(
85 0x1,
86 0x2,
87 0x4,
88 0x8,
89 0x10,
90 0x20,
91 0x40,
92 0x80,
93 0x100,
94 0x200,
95 0x400,
96 0x800,
97 0x1000,
98 0x2000,
99 0x4000,
100 0x8000,
101 0x0001_0000,
102 0x0002_0000,
103 0x0004_0000,
104 0x0008_0000,
105 0x0010_0000,
106 0x0020_0000,
107 0x0040_0000,
108 0x0080_0000,
109 0x0100_0000,
110 0x0200_0000,
111 0x0400_0000,
112 0x0800_0000,
113 0x1000_0000,
114 0x2000_0000,
115 0x4000_0000,
116 0x8000_0000,
117 0x0001_0000_0000,
118 0x0002_0000_0000,
119 0x0004_0000_0000,
120 0x0008_0000_0000,
121 0x0010_0000_0000,
122 0x0020_0000_0000,
123 0x0040_0000_0000,
124 0x0080_0000_0000,
125 0x0100_0000_0000,
126 0x0200_0000_0000,
127 0x0400_0000_0000,
128 0x0800_0000_0000,
129 0x1000_0000_0000,
130 0x2000_0000_0000,
131 0x4000_0000_0000,
132 0x8000_0000_0000,
133 0x0001_0000_0000_0000,
134 0x0002_0000_0000_0000,
135 0x0004_0000_0000_0000,
136 0x0008_0000_0000_0000,
137 0x0010_0000_0000_0000,
138 0x0020_0000_0000_0000,
139 0x0040_0000_0000_0000,
140 0x0080_0000_0000_0000,
141 0x0100_0000_0000_0000,
142 0x0200_0000_0000_0000,
143 0x0400_0000_0000_0000,
144 0x0800_0000_0000_0000,
145 0x1000_0000_0000_0000,
146 0x2000_0000_0000_0000,
147 0x4000_0000_0000_0000,
148 0x8000_0000_0000_0000
149);
150impl<T, const N: usize> Cache<T, N>
151where
152 [T; N]: Default,
153{
154 #[inline]
159 #[must_use]
160 pub fn new() -> Self {
161 Self::default()
162 }
163}
164impl<T, const N: usize> Default for Cache<T, N>
165where
166 [T; N]: Default,
167{
168 #[inline]
169 fn default() -> Self {
170 Self {
171 vals: Default::default(),
172 len: 0,
173 next_head: 0,
174 }
175 }
176}
177#[cfg(test)]
178mod tests {
179 use super::Cache;
180 #[test]
181 fn len() {
182 let mut c = Cache::<bool, 1>::new();
183 assert_eq!(0, c.len());
184 c.push(false);
185 assert_eq!(1, c.len());
186 c.push(false);
187 assert_eq!(1, c.len());
188 }
189 #[test]
190 fn is_empty() {
191 let mut c = Cache::<bool, 1>::new();
192 assert!(c.is_empty());
193 c.push(false);
194 assert!(!c.is_empty());
195 }
196 #[expect(clippy::cognitive_complexity, reason = "a lot to test")]
197 #[test]
198 fn get() {
199 let mut c = Cache::<bool, 4>::new();
200 assert_eq!(c.get(0), None);
201 assert_eq!(c.get(1), None);
202 assert_eq!(c.get(2), None);
203 assert_eq!(c.get(3), None);
204 assert_eq!(c.get(4), None);
205 assert_eq!(c.get(5), None);
206 assert_eq!(c.get(usize::MAX), None);
207 c.push(true);
208 assert_eq!(c.get(0), Some(&true));
209 assert_eq!(c.get(1), None);
210 assert_eq!(c.get(2), None);
211 assert_eq!(c.get(3), None);
212 assert_eq!(c.get(4), None);
213 assert_eq!(c.get(5), None);
214 assert_eq!(c.get(usize::MAX), None);
215 c.push(false);
216 assert_eq!(c.get(0), Some(&false));
217 assert_eq!(c.get(1), Some(&true));
218 assert_eq!(c.get(2), None);
219 assert_eq!(c.get(3), None);
220 assert_eq!(c.get(4), None);
221 assert_eq!(c.get(5), None);
222 assert_eq!(c.get(usize::MAX), None);
223 c.push(false);
224 assert_eq!(c.get(0), Some(&false));
225 assert_eq!(c.get(1), Some(&false));
226 assert_eq!(c.get(2), Some(&true));
227 assert_eq!(c.get(3), None);
228 assert_eq!(c.get(4), None);
229 assert_eq!(c.get(5), None);
230 assert_eq!(c.get(usize::MAX), None);
231 c.push(true);
232 assert_eq!(c.get(0), Some(&true));
233 assert_eq!(c.get(1), Some(&false));
234 assert_eq!(c.get(2), Some(&false));
235 assert_eq!(c.get(3), Some(&true));
236 assert_eq!(c.get(4), None);
237 assert_eq!(c.get(5), None);
238 assert_eq!(c.get(usize::MAX), None);
239 c.push(true);
240 assert_eq!(c.get(0), Some(&true));
241 assert_eq!(c.get(1), Some(&true));
242 assert_eq!(c.get(2), Some(&false));
243 assert_eq!(c.get(3), Some(&false));
244 assert_eq!(c.get(4), None);
245 assert_eq!(c.get(5), None);
246 assert_eq!(c.get(usize::MAX), None);
247 }
248 #[test]
249 fn get_unsafe() {
250 let mut c = Cache::<bool, 4>::new();
251 assert!(!c.get_unchecked(0));
252 assert!(!c.get_unchecked(1));
253 assert!(!c.get_unchecked(2));
254 assert!(!c.get_unchecked(3));
255 assert!(!c.get_unchecked(4));
256 c.push(true);
257 assert!(c.get_unchecked(0));
258 assert!(!c.get_unchecked(1));
259 assert!(!c.get_unchecked(2));
260 assert!(!c.get_unchecked(3));
261 assert!(c.get_unchecked(4));
262 }
263 #[expect(clippy::indexing_slicing, reason = "comment justifies correctness")]
264 #[test]
265 fn index() {
266 let mut c = Cache::<bool, 4>::new();
267 c.push(true);
268 assert!(c[0]);
270 }
271 #[expect(clippy::indexing_slicing, reason = "comment justifies correctness")]
272 #[test]
273 #[should_panic(expected = "called `Option::unwrap()` on a `None` value")]
274 fn index_panic() {
275 let c = Cache::<bool, 4>::new();
276 assert!(c[0]);
278 }
279 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")]
280 #[test]
281 fn push() {
282 let mut c = Cache::<bool, 4>::new();
283 c.push(true);
284 assert!(c[0]);
286 c.push(true);
287 assert!(c[0]);
289 c.push(false);
290 assert!(!c[0]);
292 c.push(true);
293 assert!(c[0]);
295 c.push(false);
296 assert!(!c[0]);
298 c.push(false);
299 assert!(!c[0]);
301 }
302 #[test]
303 fn new() {
304 _ = Cache::<bool, 0>::new();
305 _ = Cache::<bool, 32>::new();
306 _ = Cache::<bool, 31>::new();
307 }
308}