#![deny(
unsafe_code,
unused,
warnings,
clippy::all,
clippy::cargo,
clippy::nursery,
clippy::pedantic
)]
#![allow(
clippy::arithmetic_side_effects,
clippy::implicit_return,
clippy::integer_arithmetic
)]
use core::ops::Index;
pub struct Cache<T, const N: usize> {
vals: [T; N],
len: usize,
next_head: usize,
}
impl<T, const N: usize> Cache<T, N> {
#[inline]
pub const fn len(&self) -> usize {
self.len
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
}
macro_rules! cache {
( $( $x:literal),* ) => {
$(
impl<T> Cache<T, $x> {
#[inline]
pub fn get(&self, idx: usize) -> Option<&T> {
(idx < self.len).then(|| self.vals.index(self.next_head.wrapping_sub(1).wrapping_sub(idx) & ($x - 1)))
}
#[inline]
#[allow(unsafe_code)]
pub unsafe fn get_unsafe(&self, idx: usize) -> &T {
self.vals.index(self.next_head.wrapping_sub(1).wrapping_sub(idx) & ($x - 1))
}
#[inline]
#[allow(clippy::indexing_slicing)]
pub fn push(&mut self, val: T) -> &T {
if self.len < $x {
self.len += 1;
}
self.vals[self.next_head] = val;
let ret = self.vals.index(self.next_head);
self.next_head = (self.next_head + 1) & ($x - 1);
ret
}
}
impl<T> Index<usize> for Cache<T, $x> {
type Output = T;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
self.get(index).unwrap()
}
}
)*
};
}
cache!(
0x1,
0x2,
0x4,
0x8,
0x10,
0x20,
0x40,
0x80,
0x100,
0x200,
0x400,
0x800,
0x1000,
0x2000,
0x4000,
0x8000,
0x0001_0000,
0x0002_0000,
0x0004_0000,
0x0008_0000,
0x0010_0000,
0x0020_0000,
0x0040_0000,
0x0080_0000,
0x0100_0000,
0x0200_0000,
0x0400_0000,
0x0800_0000,
0x1000_0000,
0x2000_0000,
0x4000_0000,
0x8000_0000,
0x0001_0000_0000,
0x0002_0000_0000,
0x0004_0000_0000,
0x0008_0000_0000,
0x0010_0000_0000,
0x0020_0000_0000,
0x0040_0000_0000,
0x0080_0000_0000,
0x0100_0000_0000,
0x0200_0000_0000,
0x0400_0000_0000,
0x0800_0000_0000,
0x1000_0000_0000,
0x2000_0000_0000,
0x4000_0000_0000,
0x8000_0000_0000,
0x0001_0000_0000_0000,
0x0002_0000_0000_0000,
0x0004_0000_0000_0000,
0x0008_0000_0000_0000,
0x0010_0000_0000_0000,
0x0020_0000_0000_0000,
0x0040_0000_0000_0000,
0x0080_0000_0000_0000,
0x0100_0000_0000_0000,
0x0200_0000_0000_0000,
0x0400_0000_0000_0000,
0x0800_0000_0000_0000,
0x1000_0000_0000_0000,
0x2000_0000_0000_0000,
0x4000_0000_0000_0000,
0x8000_0000_0000_0000
);
impl<T, const N: usize> Cache<T, N>
where
[T; N]: Default,
{
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
impl<T, const N: usize> Default for Cache<T, N>
where
[T; N]: Default,
{
#[inline]
fn default() -> Self {
Self {
vals: Default::default(),
len: 0,
next_head: 0,
}
}
}
#[cfg(test)]
mod tests {
use super::Cache;
#[test]
fn test_len() {
let mut c = Cache::<bool, 1>::new();
assert_eq!(0, c.len());
c.push(false);
assert_eq!(1, c.len());
c.push(false);
assert_eq!(1, c.len());
}
#[test]
fn test_is_empty() {
let mut c = Cache::<bool, 1>::new();
assert!(c.is_empty());
c.push(false);
assert!(!c.is_empty());
}
#[test]
fn test_get() {
let mut c = Cache::<bool, 4>::new();
assert!(c.get(0).is_none());
assert!(c.get(1).is_none());
assert!(c.get(2).is_none());
assert!(c.get(3).is_none());
assert!(c.get(4).is_none());
assert!(c.get(5).is_none());
assert!(c.get(usize::MAX).is_none());
c.push(true);
assert!(c.get(0).unwrap());
assert!(c.get(1).is_none());
assert!(c.get(2).is_none());
assert!(c.get(3).is_none());
assert!(c.get(4).is_none());
assert!(c.get(5).is_none());
assert!(c.get(usize::MAX).is_none());
c.push(false);
assert!(!c.get(0).unwrap());
assert!(c.get(1).unwrap());
assert!(c.get(2).is_none());
assert!(c.get(3).is_none());
assert!(c.get(4).is_none());
assert!(c.get(5).is_none());
assert!(c.get(usize::MAX).is_none());
c.push(false);
assert!(!c.get(0).unwrap());
assert!(!c.get(1).unwrap());
assert!(c.get(2).unwrap());
assert!(c.get(3).is_none());
assert!(c.get(4).is_none());
assert!(c.get(5).is_none());
assert!(c.get(usize::MAX).is_none());
c.push(true);
assert!(c.get(0).unwrap());
assert!(!c.get(1).unwrap());
assert!(!c.get(2).unwrap());
assert!(c.get(3).unwrap());
assert!(c.get(4).is_none());
assert!(c.get(5).is_none());
assert!(c.get(usize::MAX).is_none());
c.push(true);
assert!(c.get(0).unwrap());
assert!(c.get(1).unwrap());
assert!(!c.get(2).unwrap());
assert!(!c.get(3).unwrap());
assert!(c.get(4).is_none());
assert!(c.get(5).is_none());
assert!(c.get(usize::MAX).is_none());
}
#[test]
#[allow(unsafe_code)]
fn test_get_unsafe() {
let mut c = Cache::<bool, 4>::new();
unsafe {
assert!(!c.get_unsafe(0));
assert!(!c.get_unsafe(1));
assert!(!c.get_unsafe(2));
assert!(!c.get_unsafe(3));
assert!(!c.get_unsafe(4));
}
c.push(true);
unsafe {
assert!(c.get_unsafe(0));
assert!(!c.get_unsafe(1));
assert!(!c.get_unsafe(2));
assert!(!c.get_unsafe(3));
assert!(c.get_unsafe(4));
}
}
#[test]
fn test_index() {
let mut c = Cache::<bool, 4>::new();
c.push(true);
assert!(c[0]);
}
#[test]
#[should_panic]
fn test_index_panic() {
let c = Cache::<bool, 4>::new();
assert!(c[0]);
}
#[test]
fn test_push() {
let mut c = Cache::<bool, 4>::new();
assert!(c.push(true));
assert!(c.push(true));
assert!(!c.push(false));
assert!(c.push(true));
assert!(!c.push(false));
assert!(!c.push(false));
}
#[test]
fn test_new() {
_ = Cache::<bool, 0>::new();
_ = Cache::<bool, 32>::new();
_ = Cache::<bool, 31>::new();
}
}