use crate::pager::Pager;
use rustc_hash::FxHashMap;
struct LruEntry<Id, Page> {
page: Page,
prev: Option<Id>,
next: Option<Id>,
}
pub struct NodeCache<P>
where
P: Pager,
P::Page: Clone,
P::Id: Clone + Eq + std::hash::Hash,
{
map: FxHashMap<P::Id, LruEntry<P::Id, P::Page>>,
head: Option<P::Id>,
tail: Option<P::Id>,
cap: usize,
}
impl<P> Default for NodeCache<P>
where
P: Pager,
P::Page: Clone,
P::Id: Clone + Eq + std::hash::Hash,
{
fn default() -> Self {
NodeCache::<P>::new(1024, 2)
}
}
impl<P> NodeCache<P>
where
P: Pager,
P::Page: Clone,
P::Id: Clone + Eq + std::hash::Hash,
{
#[inline]
pub fn new(cap: usize, _unused: usize) -> Self {
let cap = cap.max(1);
Self {
map: FxHashMap::default(),
head: None,
tail: None,
cap,
}
}
#[inline]
pub fn peek(&self, id: &P::Id) -> Option<P::Page> {
self.map.get(id).map(|e| e.page.clone())
}
#[inline]
pub fn touch(&mut self, id: &P::Id) {
if self.map.contains_key(id) {
self.move_to_head(id);
}
}
#[inline]
pub fn set_limits(&mut self, read_cap: usize, _sweep_factor: usize) {
self.cap = read_cap.max(1);
while self.map.len() > self.cap {
self.evict_one();
}
}
#[inline]
pub fn get(&mut self, id: &P::Id) -> Option<P::Page> {
if !self.map.contains_key(id) {
return None;
}
self.move_to_head(id);
self.map.get(id).map(|e| e.page.clone())
}
#[inline]
pub fn insert(&mut self, id: P::Id, page: P::Page) {
if self.map.contains_key(&id) {
if let Some(e) = self.map.get_mut(&id) {
e.page = page;
}
self.move_to_head(&id);
return;
}
let old_head = self.head.clone();
let entry = LruEntry {
page,
prev: None,
next: old_head.clone(),
};
self.map.insert(id.clone(), entry);
if let Some(h) = old_head
&& let Some(e) = self.map.get_mut(&h)
{
e.prev = Some(id.clone());
}
self.head = Some(id.clone());
if self.tail.is_none() {
self.tail = Some(id.clone());
}
if self.map.len() > self.cap {
self.evict_one();
}
}
#[inline]
pub fn invalidate(&mut self, id: &P::Id) {
if !self.map.contains_key(id) {
return;
}
let (prev, next) = {
let e = self.map.get(id).unwrap();
(e.prev.clone(), e.next.clone())
};
if let Some(p) = prev.clone()
&& let Some(pe) = self.map.get_mut(&p)
{
pe.next = next.clone();
}
if let Some(n) = next.clone()
&& let Some(ne) = self.map.get_mut(&n)
{
ne.prev = prev.clone();
}
if self.head.as_ref() == Some(id) {
self.head = next;
}
if self.tail.as_ref() == Some(id) {
self.tail = prev;
}
self.map.remove(id);
}
#[inline]
pub fn clear(&mut self) {
self.map.clear();
self.head = None;
self.tail = None;
}
#[inline]
fn move_to_head(&mut self, id: &P::Id) {
if self.head.as_ref() == Some(id) {
return;
}
let (prev, next) = {
let e = self.map.get(id).unwrap();
(e.prev.clone(), e.next.clone())
};
if let Some(p) = prev.clone()
&& let Some(pe) = self.map.get_mut(&p)
{
pe.next = next.clone();
}
if let Some(n) = next.clone()
&& let Some(ne) = self.map.get_mut(&n)
{
ne.prev = prev.clone();
}
if self.tail.as_ref() == Some(id) {
self.tail = prev.clone();
}
let old_head = self.head.clone();
if let Some(e) = self.map.get_mut(id) {
e.prev = None;
e.next = old_head.clone();
}
if let Some(h) = old_head
&& let Some(he) = self.map.get_mut(&h)
{
he.prev = Some(id.clone());
}
self.head = Some(id.clone());
if self.tail.is_none() {
self.tail = Some(id.clone());
}
}
#[inline]
fn evict_one(&mut self) {
let Some(tid) = self.tail.clone() else {
return;
};
self.invalidate(&tid);
}
}