use std::{
future::Future,
io::Result,
pin::Pin,
sync::{Arc, Mutex},
task::Poll,
};
use crate::ipldfs::{IpldFS, Multipart};
#[derive(Debug, Clone)]
pub struct LruCache<FS>
where
FS: IpldFS + Clone,
{
inner: FS,
lru: Arc<Mutex<lru_mem::LruCache<String, Multipart>>>,
}
impl<FS> LruCache<FS>
where
FS: IpldFS + Clone,
{
pub fn new(fs: FS, maxinum_memory_size: usize) -> Self {
Self {
inner: fs,
lru: Arc::new(Mutex::new(lru_mem::LruCache::new(maxinum_memory_size))),
}
}
}
impl<FS> IpldFS for LruCache<FS>
where
FS: IpldFS + Clone + Sync + Send + Unpin + 'static,
{
type Read<'cx> = LruCacheRead<'cx, FS>;
type ReadEntry<'cx> = FS::ReadEntry<'cx>;
type Write<'cx> = FS::Write<'cx>;
fn read_entry<'cx, 'a>(&'a mut self, cid: libipld::Cid) -> Self::ReadEntry<'cx>
where
'a: 'cx,
{
self.inner.read_entry(cid)
}
fn read<'cx, 'a>(&'a mut self, cid: &'cx libipld::Cid) -> Self::Read<'cx>
where
'a: 'cx,
{
let cid_string = cid.to_string();
{
let mut lru = self.lru.lock().unwrap();
if let Some(multipart) = lru.get(&cid_string) {
return LruCacheRead::Cached(Some(multipart.clone()));
}
}
LruCacheRead::Invoke(cid.to_string(), self.inner.read(cid), self.lru.clone())
}
fn write<'cx, 'a>(
&'a mut self,
cid: libipld::Cid,
multipart: Multipart,
lease: std::time::Duration,
) -> Self::Write<'cx>
where
'a: 'cx,
{
self.inner.write(cid, multipart, lease)
}
}
pub enum LruCacheRead<'cx, FS>
where
FS: IpldFS + Send + Unpin + 'static,
{
Cached(Option<Multipart>),
Invoke(
String,
FS::Read<'cx>,
Arc<Mutex<lru_mem::LruCache<String, Multipart>>>,
),
}
impl<'cx, FS> Future for LruCacheRead<'cx, FS>
where
FS: IpldFS + Send + Unpin + 'static,
{
type Output = Result<Multipart>;
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
match self.get_mut() {
LruCacheRead::Cached(multpart) => return Poll::Ready(Ok(multpart.take().unwrap())),
LruCacheRead::Invoke(key, read, lru) => match Pin::new(read).poll(cx) {
Poll::Ready(Ok(multipart)) => {
{
lru.lock()
.unwrap()
.insert(key.clone(), multipart.clone())
.map_err(|err| {
std::io::Error::new(std::io::ErrorKind::OutOfMemory, err)
})?;
}
return Poll::Ready(Ok(multipart));
}
p => return p,
},
}
}
}