use std::cell::{Cell, RefCell};
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use crate::{Quota, QuotaResult};
use crate::QuotaPolicy;
pub struct QuotaPool {
quotas: Cell<Vec<Quota>>,
last_access_times_ns: Cell<Vec<std::time::Instant>>,
policy: QuotaPolicy,
key_map: Cell<HashMap<&'static str, usize>>,
initial_available_tokens: u64,
epoch: Cell<std::time::Instant>
}
impl QuotaPool {
#[inline]
pub fn new(policy: QuotaPolicy, initial_available_tokens: u64) -> QuotaPool {
QuotaPool {
quotas: Cell::new(Vec::new()),
last_access_times_ns: Cell::new(Vec::new()),
policy,
key_map: Cell::new(HashMap::new()),
initial_available_tokens,
epoch: Cell::new(std::time::Instant::now())
}
}
#[inline]
pub fn start(&mut self) {
let now = std::time::Instant::now();
self.epoch = Cell::new(now);
self.last_access_times_ns.get_mut().fill(now);
}
#[inline]
pub fn quota(&self, key: &'static str) -> &Quota {
unsafe {
let key_map = &mut *self.key_map.as_ptr();
let quotas = &mut *self.quotas.as_ptr();
match key_map.entry(key) {
Entry::Occupied(occupied_entry) => {
"as[*occupied_entry.get()]
}
Entry::Vacant(vacant_entry) => {
let last_access_times_ns = &mut *self.last_access_times_ns.as_ptr();
let new_index = quotas.len();
vacant_entry.insert(new_index);
quotas.push(Quota::with_initial_tokens(self.initial_available_tokens));
last_access_times_ns.push(std::time::Instant::now());
"as.get_unchecked(new_index)
}
}
}
}
#[inline]
pub fn quota_with_index(&self, key: &'static str) -> (&Quota, usize) {
unsafe {
let key_map = &mut *self.key_map.as_ptr();
let quotas = &mut *self.quotas.as_ptr();
match key_map.entry(key) {
Entry::Occupied(occupied_entry) => {
let i = *occupied_entry.get();
("as[i], i)
}
Entry::Vacant(vacant_entry) => {
let last_access_times_ns = &mut *self.last_access_times_ns.as_ptr();
let new_index = quotas.len();
vacant_entry.insert(new_index);
quotas.push(Quota::with_initial_tokens(self.initial_available_tokens));
last_access_times_ns.push(std::time::Instant::now());
("as.get_unchecked(new_index), new_index)
}
}
}
}
#[inline]
pub fn consume(&self, key: &'static str, cost: u64) -> QuotaResult {
let (quota, index) = self.quota_with_index(key);
let last_access_time_ns = unsafe { (&mut *self.last_access_times_ns.as_ptr())[index] };
let delta = last_access_time_ns.elapsed();
self.policy.tick(delta.as_nanos() as u64, "a);
quota.consume(cost)
}
}
#[cfg(test)]
mod tests;