#![no_std]
#![deny(unsafe_code)]
use arrayvec::ArrayVec;
use core::mem::replace;
#[cfg(test)]
mod tests;
#[derive(Debug, Clone)]
pub struct LRUCache<T, const N: usize> {
entries: ArrayVec<Entry<T>, N>,
head: u16,
tail: u16,
}
#[derive(Debug, Clone)]
struct Entry<T> {
val: T,
prev: u16,
next: u16,
}
impl<T, const N: usize> Default for LRUCache<T, N> {
fn default() -> Self {
Self::new()
}
}
impl<T, const N: usize> LRUCache<T, N> {
pub const fn new() -> Self {
assert!(N < u16::MAX as usize, "Capacity overflow");
LRUCache {
entries: ArrayVec::new_const(),
head: 0,
tail: 0,
}
}
pub fn insert(&mut self, val: T) -> Option<T> {
let new_entry = Entry {
val,
prev: 0,
next: 0,
};
if self.entries.is_full() {
let i = self.pop_back();
let old_entry = replace(self.entry(i), new_entry);
self.push_front(i);
Some(old_entry.val)
} else {
let i = self.entries.len() as u16;
self.entries.push(new_entry);
self.push_front(i);
None
}
}
pub fn find<F>(&mut self, pred: F) -> Option<&mut T>
where
F: FnMut(&T) -> bool,
{
if self.touch(pred) {
self.front_mut()
} else {
None
}
}
pub fn lookup<F, R>(&mut self, mut pred: F) -> Option<R>
where
F: FnMut(&mut T) -> Option<R>,
{
let mut iter = self.iter_mut();
while let Some((i, val)) = iter.next() {
if let Some(r) = pred(val) {
self.touch_index(i);
return Some(r);
}
}
None
}
#[inline]
pub fn len(&self) -> usize {
self.entries.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[inline]
pub fn clear(&mut self) {
self.entries.clear();
}
pub fn front(&self) -> Option<&T> {
self.entries.get(self.head as usize).map(|e| &e.val)
}
pub fn front_mut(&mut self) -> Option<&mut T> {
self.entries.get_mut(self.head as usize).map(|e| &mut e.val)
}
pub fn get(&self, index: usize) -> Option<&T> {
self.iter().nth(index)
}
pub fn touch<F>(&mut self, mut pred: F) -> bool
where
F: FnMut(&T) -> bool,
{
let mut iter = self.iter_mut();
while let Some((i, val)) = iter.next() {
if pred(val) {
self.touch_index(i);
return true;
}
}
false
}
pub fn iter(&self) -> Iter<'_, T, N> {
Iter {
pos: self.head,
cache: self,
}
}
fn iter_mut(&mut self) -> IterMut<'_, T, N> {
IterMut {
pos: self.head,
cache: self,
}
}
#[inline]
fn touch_index(&mut self, i: u16) {
if i != self.head {
self.remove(i);
self.push_front(i);
}
}
#[inline(always)]
fn entry(&mut self, i: u16) -> &mut Entry<T> {
&mut self.entries[i as usize]
}
fn remove(&mut self, i: u16) {
let prev = self.entry(i).prev;
let next = self.entry(i).next;
if i == self.head {
self.head = next;
} else {
self.entry(prev).next = next;
}
if i == self.tail {
self.tail = prev;
} else {
self.entry(next).prev = prev;
}
}
fn push_front(&mut self, i: u16) {
if self.entries.len() == 1 {
self.tail = i;
} else {
self.entry(i).next = self.head;
self.entry(self.head).prev = i;
}
self.head = i;
}
fn pop_back(&mut self) -> u16 {
let new_tail = self.entry(self.tail).prev;
replace(&mut self.tail, new_tail)
}
}
struct IterMut<'a, T, const N: usize> {
cache: &'a mut LRUCache<T, N>,
pos: u16,
}
impl<'a, T, const N: usize> IterMut<'a, T, N> {
fn next(&mut self) -> Option<(u16, &mut T)> {
let index = self.pos;
let entry = self.cache.entries.get_mut(index as usize)?;
self.pos = if index == self.cache.tail {
N as u16 } else {
entry.next
};
Some((index, &mut entry.val))
}
}
pub struct Iter<'a, T, const N: usize> {
cache: &'a LRUCache<T, N>,
pos: u16,
}
impl<'a, T, const N: usize> Iterator for Iter<'a, T, N> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
let entry = self.cache.entries.get(self.pos as usize)?;
self.pos = if self.pos == self.cache.tail {
N as u16 } else {
entry.next
};
Some(&entry.val)
}
}