use super::*;
use std::cell::Cell;
use std::ops::{Deref, DerefMut};
use std::time::Instant;
pub trait LruTimestamp {
type Timestamp<'a>: PartialOrd
where
Self: 'a;
fn get_timestamp(&self) -> Self::Timestamp<'_>;
fn update_timestamp(&self);
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct WithLruTimestamp<T> {
timestamp: Cell<Instant>,
inner: T,
}
impl<T> Default for WithLruTimestamp<T>
where
T: Default,
{
#[inline]
fn default() -> Self {
WithLruTimestamp {
timestamp: Cell::new(Instant::now()),
inner: Default::default(),
}
}
}
impl<T> AsRef<T> for WithLruTimestamp<T> {
#[inline]
fn as_ref(&self) -> &T {
&self.inner
}
}
impl<T> AsMut<T> for WithLruTimestamp<T> {
#[inline]
fn as_mut(&mut self) -> &mut T {
&mut self.inner
}
}
impl<T> Deref for WithLruTimestamp<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.inner
}
}
impl<T> DerefMut for WithLruTimestamp<T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
&mut self.inner
}
}
impl<T> From<T> for WithLruTimestamp<T> {
#[inline]
fn from(inner: T) -> WithLruTimestamp<T> {
WithLruTimestamp::new(inner)
}
}
impl<T> WithLruTimestamp<T> {
#[inline]
pub fn new(inner: T) -> WithLruTimestamp<T> {
WithLruTimestamp {
timestamp: Cell::new(Instant::now()),
inner,
}
}
#[inline]
pub fn into_inner(outer: WithLruTimestamp<T>) -> T {
outer.inner
}
}
impl<T> LruTimestamp for WithLruTimestamp<T> {
type Timestamp<'a> = &'a Cell<Instant> where T: 'a;
#[inline]
fn get_timestamp(&self) -> Self::Timestamp<'_> {
&self.timestamp
}
#[inline]
fn update_timestamp(&self) {
self.timestamp.set(Instant::now());
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LruReplacement {
_private: (),
}
impl<V, C> Replacement<V, C> for LruReplacement
where
C: Capacity,
V: LruTimestamp,
{
#[inline]
fn choose_for_replacement<'a>(
&mut self,
candidates: impl ExactSizeIterator<Item = (usize, &'a V)>,
) -> usize
where
V: 'a,
{
let mut lru = None;
for (index, value) in candidates {
let timestamp = value.get_timestamp();
lru = match lru {
Some((t, i)) if t < timestamp => Some((t, i)),
_ => Some((timestamp, index)),
};
}
lru.unwrap().1
}
#[inline]
fn on_hit(&self, value: &V) {
value.update_timestamp();
}
#[inline]
fn on_insert(&self, value: &V) {
value.update_timestamp();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Capacity4;
use std::time::Duration;
#[test]
fn lru_replacement() {
let now = Instant::now();
let candidates = vec![
now,
now - Duration::from_secs(1),
now - Duration::from_secs(2),
now - Duration::from_secs(3),
]
.into_iter()
.map(|t| WithLruTimestamp {
timestamp: Cell::new(t),
inner: (),
})
.collect::<Vec<_>>();
let replacement = &mut LruReplacement::default();
let index = <LruReplacement as Replacement<_, Capacity4>>::choose_for_replacement(
replacement,
candidates.iter().enumerate(),
);
assert_eq!(index, 3);
}
#[test]
fn lru_timestamp_ref() {
struct Wrap {
timestamp: Instant,
}
impl LruTimestamp for Wrap {
type Timestamp<'a> = &'a Instant;
fn get_timestamp(&self) -> Self::Timestamp<'_> {
&self.timestamp
}
fn update_timestamp(&self) {}
}
let now = Instant::now();
let candidates = vec![
now,
now - Duration::from_secs(1),
now - Duration::from_secs(2),
now - Duration::from_secs(3),
]
.into_iter()
.map(|t| Wrap { timestamp: t })
.collect::<Vec<_>>();
let replacement = &mut LruReplacement::default();
let index = <LruReplacement as Replacement<_, Capacity4>>::choose_for_replacement(
replacement,
candidates.iter().enumerate(),
);
assert_eq!(index, 3);
}
#[test]
fn lru_timestamp_owned() {
#[repr(packed)]
struct Wrap {
timestamp: Instant,
}
impl LruTimestamp for Wrap {
type Timestamp<'a> = Instant;
fn get_timestamp(&self) -> Self::Timestamp<'_> {
self.timestamp
}
fn update_timestamp(&self) {}
}
let now = Instant::now();
let candidates = vec![
now,
now - Duration::from_secs(1),
now - Duration::from_secs(2),
now - Duration::from_secs(3),
]
.into_iter()
.map(|t| Wrap { timestamp: t })
.collect::<Vec<_>>();
let replacement = &mut LruReplacement::default();
let index = <LruReplacement as Replacement<_, Capacity4>>::choose_for_replacement(
replacement,
candidates.iter().enumerate(),
);
assert_eq!(index, 3);
}
}