#![cfg(feature = "std")]
use lib::*;
use evmap::{self, ReadHandle, WriteHandle};
use parking_lot::Mutex;
use {
algorithms::{Algorithm, DefaultAlgorithm, KeyableRateLimitState, RateLimitStateWithClock},
instant, InconsistentCapacity, NegativeMultiDecision,
};
type MapWriteHandle<K, P, A, H> =
Arc<Mutex<WriteHandle<K, <A as Algorithm<P>>::BucketState, (), H>>>;
#[derive(Clone)]
pub struct KeyedRateLimiter<
K: Eq + Hash + Clone,
A: Algorithm<P> = DefaultAlgorithm,
P: instant::Absolute = Instant,
H: BuildHasher + Clone = RandomState,
> where
A::BucketState: KeyableRateLimitState<A, P>,
{
algorithm: A,
map_reader: ReadHandle<K, A::BucketState, (), H>,
map_writer: MapWriteHandle<K, P, A, H>,
}
impl<A, K, P: instant::Absolute> fmt::Debug for KeyedRateLimiter<K, A, P>
where
A: Algorithm<P>,
A::BucketState: KeyableRateLimitState<A, P>,
K: Eq + Hash + Clone,
{
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "KeyedRateLimiter{{{params:?}}}", params = self.algorithm)
}
}
impl<P, A, K> KeyedRateLimiter<K, A, P>
where
P: instant::Absolute,
A: Algorithm<P>,
A::BucketState: KeyableRateLimitState<A, P>,
K: Eq + Hash + Clone,
{
pub fn new(capacity: NonZeroU32, per_time_unit: Duration) -> Self {
let (r, mut w): (
ReadHandle<K, A::BucketState>,
WriteHandle<K, A::BucketState>,
) = evmap::new();
w.refresh();
KeyedRateLimiter {
algorithm: <A as Algorithm<P>>::construct(capacity, nonzero!(1u32), per_time_unit)
.unwrap(),
map_reader: r,
map_writer: Arc::new(Mutex::new(w)),
}
}
pub fn per_second(capacity: NonZeroU32) -> Self {
Self::new(capacity, Duration::from_secs(1))
}
pub fn build_with_capacity(capacity: NonZeroU32) -> Builder<K, P, A, RandomState> {
Builder {
capacity,
..Default::default()
}
}
fn check_and_update_key<E, F>(&self, key: K, update: F) -> Result<(), E>
where
F: Fn(&A::BucketState) -> Result<(), E>,
{
self.map_reader
.get_and(&key, |v| {
let state = &v[0];
update(state)
})
.unwrap_or_else(|| {
let mut w = self.map_writer.lock();
let state: A::BucketState = Default::default();
let result = update(&state);
w.update(key, state);
w.flush();
result
})
}
pub fn check(&mut self, key: K) -> Result<(), <A as Algorithm<P>>::NegativeDecision> {
self.check_at(key, P::now())
}
pub fn check_n(
&mut self,
key: K,
n: u32,
) -> Result<(), NegativeMultiDecision<<A as Algorithm<P>>::NegativeDecision>> {
self.check_n_at(key, n, P::now())
}
pub fn check_at(&mut self, key: K, at: P) -> Result<(), <A as Algorithm<P>>::NegativeDecision> {
self.check_and_update_key(key, |state| self.algorithm.test_and_update(state, at))
}
pub fn check_n_at(
&mut self,
key: K,
n: u32,
at: P,
) -> Result<(), NegativeMultiDecision<<A as Algorithm<P>>::NegativeDecision>> {
self.check_and_update_key(key, |state| self.algorithm.test_n_and_update(state, n, at))
}
pub fn cleanup<D: Into<Option<Duration>>>(&mut self, min_age: D) -> Vec<K> {
self.cleanup_at(min_age, P::now())
}
pub fn cleanup_at<D: Into<Option<Duration>>, I: Into<Option<P>>>(
&mut self,
min_age: D,
at: I,
) -> Vec<K> {
let params = &self.algorithm;
let min_age = min_age.into().unwrap_or_else(|| Duration::new(0, 0));
let at = at.into().unwrap_or_else(P::now);
let mut expireable: Vec<K> = vec![];
self.map_reader.for_each(|k, v| {
if let Some(state) = v.get(0) {
if state.last_touched(params) < at - min_age {
expireable.push(k.clone());
}
}
});
let mut w = self.map_writer.lock();
for key in expireable.iter().cloned() {
w.empty(key);
}
w.refresh();
expireable
}
}
pub struct Builder<K: Eq + Hash + Clone, P: instant::Absolute, A: Algorithm<P>, H: BuildHasher> {
end_result: PhantomData<(K, P, A)>,
capacity: NonZeroU32,
cell_weight: NonZeroU32,
per_time_unit: Duration,
hasher: H,
map_capacity: Option<usize>,
}
impl<K, A, P> Default for Builder<K, P, A, RandomState>
where
K: Eq + Hash + Clone,
P: instant::Absolute,
A: Algorithm<P>,
A::BucketState: KeyableRateLimitState<A, P>,
{
fn default() -> Builder<K, P, A, RandomState> {
Builder {
end_result: PhantomData,
map_capacity: None,
capacity: nonzero!(1u32),
cell_weight: nonzero!(1u32),
per_time_unit: Duration::from_secs(1),
hasher: RandomState::new(),
}
}
}
impl<K, P, A, H> Builder<K, P, A, H>
where
K: Eq + Hash + Clone,
P: instant::Absolute,
A: Algorithm<P>,
A::BucketState: KeyableRateLimitState<A, P>,
H: BuildHasher,
{
pub fn with_hasher<H2: BuildHasher>(self, hash_builder: H2) -> Builder<K, P, A, H2> {
Builder {
hasher: hash_builder,
end_result: self.end_result,
capacity: self.capacity,
cell_weight: self.cell_weight,
per_time_unit: self.per_time_unit,
map_capacity: self.map_capacity,
}
}
pub fn with_cell_weight(self, cell_weight: NonZeroU32) -> Result<Self, InconsistentCapacity> {
if self.cell_weight > self.capacity {
return Err(InconsistentCapacity::new(self.capacity, cell_weight));
}
Ok(Builder {
cell_weight,
..self
})
}
pub fn with_map_capacity(self, map_capacity: usize) -> Self {
Builder {
map_capacity: Some(map_capacity),
..self
}
}
pub fn build(self) -> Result<KeyedRateLimiter<K, A, P, H>, InconsistentCapacity>
where
H: Clone,
{
let map_opts = evmap::Options::default().with_hasher(self.hasher);
let (r, mut w) = if self.map_capacity.is_some() {
map_opts
.with_capacity(self.map_capacity.unwrap())
.construct()
} else {
map_opts.construct()
};
w.refresh();
Ok(KeyedRateLimiter {
algorithm: <A as Algorithm<P>>::construct(
self.capacity,
self.cell_weight,
self.per_time_unit,
)?,
map_reader: r,
map_writer: Arc::new(Mutex::new(w)),
})
}
}