#[cfg(test)]
use crate::key::CompactCacheKey;
use crate::CacheKey;
#[cfg(test)]
use std::collections::HashMap;
#[cfg(test)]
use std::num::NonZeroU32;
#[cfg(test)]
use std::sync::Mutex;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Decision {
Defer {
observed: u32,
},
Ready {
observed: u32,
},
}
impl Decision {
pub fn observed(self) -> u32 {
match self {
Self::Defer { observed } | Self::Ready { observed } => observed,
}
}
pub fn is_deferred(self) -> bool {
matches!(self, Self::Defer { .. })
}
}
pub trait AdmissionPolicy: Send + Sync {
fn observe(&self, key: &CacheKey) -> Decision;
}
#[cfg(test)]
pub(crate) struct MinUsesAdmissionPolicy {
min_uses: NonZeroU32,
observations: Mutex<HashMap<CompactCacheKey, u32>>,
}
#[cfg(test)]
impl MinUsesAdmissionPolicy {
pub(crate) fn new(min_uses: NonZeroU32) -> Self {
Self {
min_uses,
observations: Mutex::new(HashMap::new()),
}
}
}
#[cfg(test)]
impl AdmissionPolicy for MinUsesAdmissionPolicy {
fn observe(&self, key: &CacheKey) -> Decision {
let mut observations = self.observations.lock().unwrap();
let observed = observations
.entry(key.to_compact())
.and_modify(|observed| *observed = observed.saturating_add(1).min(self.min_uses.get()))
.or_insert(1);
if *observed >= self.min_uses.get() {
Decision::Ready {
observed: *observed,
}
} else {
Decision::Defer {
observed: *observed,
}
}
}
}