use {
crate::{AccessType, EvictError, EvictResult, EvictionPolicy, FrameId},
hlc_gen::{HlcGenerator, HlcTimestamp},
parking_lot::{RwLock, RwLockWriteGuard},
priority_queue::PriorityQueue,
std::{cmp::Reverse, sync::Arc},
};
pub struct LruReplacer<F: FrameId> {
inner: Arc<RwLock<Inner<F>>>,
}
struct Inner<F: FrameId> {
capacity: usize,
frames: PriorityQueue<F, Reverse<HlcTimestamp>>,
seq: HlcGenerator,
}
impl<F: FrameId> LruReplacer<F> {
pub fn new(capacity: usize) -> Self {
Self {
inner: Arc::new(RwLock::new(Inner {
capacity,
frames: PriorityQueue::with_capacity(capacity),
seq: HlcGenerator::default(),
})),
}
}
fn push(mut inner: RwLockWriteGuard<'_, Inner<F>>, id: F) -> EvictResult<(), F> {
if inner.frames.len() >= inner.capacity {
return Err(EvictError::FrameReplacerFull);
}
let priority = inner
.seq
.next_timestamp()
.ok_or(EvictError::SequenceExhausted)?;
inner.frames.push(id, Reverse(priority));
Ok(())
}
}
impl<F: FrameId> EvictionPolicy<F> for LruReplacer<F> {
type Error = EvictError<F>;
fn evict(&self) -> Option<F> {
let mut inner = self.inner.write();
inner.frames.pop().map(|(frame_id, _)| frame_id)
}
fn peek(&self) -> Option<F> {
let inner = self.inner.read();
inner.frames.peek().map(|(frame_id, _)| frame_id.clone())
}
fn touch(&self, id: F) -> EvictResult<(), F> {
Self::push(self.inner.write(), id)
}
fn touch_with<T: AccessType>(&self, id: F, _access_type: T) -> EvictResult<(), F> {
self.touch(id)
}
fn pin(&self, id: F) -> EvictResult<(), F> {
let mut inner = self.inner.write();
inner.frames.remove(&id);
Ok(())
}
fn unpin(&self, id: F) -> EvictResult<(), F> {
let inner = self.inner.write();
if inner.frames.get(&id).is_none() {
Self::push(inner, id)?;
}
Ok(())
}
fn remove(&self, id: F) -> EvictResult<(), F> {
let res = self.inner.write().frames.remove(&id);
if res.is_none() {
return Err(EvictError::PinnedFrameRemoval(id));
}
Ok(())
}
fn capacity(&self) -> usize {
self.inner.read().capacity
}
fn size(&self) -> usize {
self.inner.read().frames.len()
}
}