#![forbid(unsafe_code)]
use std::collections::HashMap;
use crate::store::inode::Inode;
#[derive(Debug)]
pub struct MetadataCache {
entries: HashMap<u64, (Inode, u64)>,
capacity: usize,
clock: u64,
}
impl MetadataCache {
pub fn new(capacity: usize) -> Self {
Self {
entries: HashMap::new(),
capacity,
clock: 0,
}
}
pub fn get(&mut self, ino: u64) -> Option<Inode> {
let entry = self.entries.get_mut(&ino)?;
self.clock += 1;
entry.1 = self.clock;
Some(entry.0.clone())
}
pub fn insert(&mut self, ino: u64, inode: Inode) {
self.clock += 1;
self.entries.insert(ino, (inode, self.clock));
while self.entries.len() > self.capacity {
let victim = self
.entries
.iter()
.min_by_key(|(_, (_, rec))| *rec)
.map(|(k, _)| *k)
.expect("non-empty");
self.entries.remove(&victim);
}
}
pub fn invalidate(&mut self, ino: u64) {
self.entries.remove(&ino);
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bounded_and_lru() {
let mut c = MetadataCache::new(2);
fn mk() -> Inode {
Inode::new_file(0, 0, 0o644)
}
let a = mk();
let b = mk();
c.insert(1, a.clone());
c.insert(2, b.clone());
assert!(c.get(1).is_some());
c.insert(3, mk());
assert_eq!(c.len(), 2);
assert!(c.get(1).is_some());
assert!(c.get(2).is_none());
assert!(c.get(3).is_some());
c.invalidate(1);
assert!(c.get(1).is_none());
}
}