use std::path::PathBuf;
use compio::{buf::IoBufMut, io::AsyncReadAtExt};
use compio_fs::File;
use hashlink::lru_cache::Entry;
use ider::path::id_path;
use jdb_lru::Lru;
use log::error;
pub struct FileLru {
pub dir: PathBuf,
pub cache: Lru<u64, File>,
}
impl FileLru {
const MIN_CACHE_SIZE: usize = 16;
#[inline]
pub fn new(dir: impl Into<PathBuf>, cache_size: usize) -> Self {
Self {
dir: dir.into(),
cache: Lru::new(cache_size.max(Self::MIN_CACHE_SIZE)),
}
}
#[inline(always)]
pub async fn read_into<B: IoBufMut>(
&mut self,
file_id: u64,
buf: B,
offset: u64,
) -> std::io::Result<B> {
let file = self.open(file_id).await?;
let res = file.read_exact_at(buf, offset).await;
res.0?;
Ok(res.1)
}
async fn open(&mut self, file_id: u64) -> std::io::Result<&File> {
match self.cache.0.entry(file_id) {
Entry::Occupied(e) => Ok(e.into_mut()),
Entry::Vacant(e) => {
let path = id_path(&self.dir, file_id);
let file = File::open(&path).await?;
Ok(e.insert(file))
}
}
}
#[inline]
pub fn evict(&mut self, file_id: u64) {
self.cache.0.remove(&file_id);
}
#[inline]
pub fn rm(&mut self, file_id: u64) {
self.evict(file_id);
let path = id_path(&self.dir, file_id);
compio::runtime::spawn(async move {
if let Err(e) = compio_fs::remove_file(&path).await {
error!("remove {}, error={}", path.display(), e);
}
})
.detach();
}
}