pub struct LruMap<K: PartialEq, V, const S: usize> {
data: arrayvec::ArrayVec<(usize, K, V), S>,
next_operation: usize,
}
impl<K: PartialEq, V, const S: usize> LruMap<K, V, S> {
pub const fn new() -> Self {
LruMap {
data: arrayvec::ArrayVec::new_const(),
next_operation: 0,
}
}
pub fn insert(&mut self, key: K, value: V) -> Option<(K, V)> {
let mut popped = None;
let new_op = self.get_and_inc_op();
if self.data.is_full() {
let lru = self.least_recently_used().expect(
"Capacity was full and LRU was not found. Confirm LRU Map capacity is not zero?",
);
let (_op, k, v) = self.data.remove(lru);
popped = Some((k, v));
}
self.data.push((new_op, key, value));
popped
}
pub fn get(&mut self, key: &K) -> Option<&V> {
let new_op = self.get_and_inc_op();
for (op, k, v) in self.data.iter_mut() {
if key == k {
*op = new_op;
return Some(v);
}
}
None
}
pub fn get_least_recently_used(&mut self) -> Option<(&mut K, &mut V)> {
if !self.data.is_full() {
return None;
};
match self.least_recently_used() {
None => None,
Some(index) => {
let new_op = self.get_and_inc_op();
let (op, k, v) = self.data.get_mut(index)?;
*op = new_op;
Some((k, v))
}
}
}
pub fn capacity(&self) -> usize {
S
}
pub fn len(&self) -> usize {
self.data.len()
}
fn get_and_inc_op(&mut self) -> usize {
let v = self.next_operation;
self.next_operation += 1;
v
}
fn least_recently_used(&self) -> Option<usize> {
struct IndexAndOp {
index: usize,
operation: usize,
}
let mut ret_least_index: Option<IndexAndOp> = None;
for (i, (sz, _k, _v)) in self.data.iter().enumerate() {
match &mut ret_least_index {
None => {
ret_least_index = Some(IndexAndOp {
index: i,
operation: *sz,
});
}
Some(least) => {
if &least.operation > sz {
ret_least_index = Some(IndexAndOp {
index: i,
operation: *sz,
});
}
}
}
}
ret_least_index.map(|index_and_op| index_and_op.index)
}
}
#[cfg(test)]
mod test {
use crate::structs::lru_map::LruMap;
#[test]
pub fn can_add_and_remove_lru() {
let mut lru: LruMap<_, _, 2> = LruMap::new();
assert!(lru.insert(1, "one").is_none());
assert!(lru.insert(2, "two").is_none());
let evicted = lru.insert(3, "three").unwrap();
assert_eq!(evicted, (1, "one"));
assert_eq!(lru.get(&2), Some(&"two"));
let evicted = lru.insert(4, "four").unwrap();
assert_eq!(evicted, (3, "three"));
}
}