extern crate linked_hash_map;
use std::borrow::Borrow;
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hash};
#[cfg(feature = "stats")]
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{Duration, Instant};
use linked_hash_map::LinkedHashMap;
use linked_hash_map::Entry as LinkedHashMapEntry;
use linked_hash_map::OccupiedEntry as OccupiedLinkHashMapEntry;
use linked_hash_map::VacantEntry as VacantLinkHashMapEntry;
pub enum Entry<'a, K: 'a, V: 'a, S: 'a = RandomState> {
Occupied(OccupiedEntry<'a, K, V, S>),
Vacant(VacantEntry<'a, K, V, S>),
}
impl<'a, K: Hash + Eq, V, S: BuildHasher> Entry<'a, K, V, S> {
pub fn key(&self) -> &K {
match *self {
Entry::Occupied(ref e) => e.key(),
Entry::Vacant(ref e) => e.key(),
}
}
}
pub struct OccupiedEntry<'a, K: 'a, V: 'a, S: 'a = RandomState> {
entry: OccupiedLinkHashMapEntry<'a, K, InternalEntry<V>, S>
}
impl<'a, K: Hash + Eq, V, S: BuildHasher> OccupiedEntry<'a, K, V, S> {
pub fn key(&self) -> &K {
self.entry.key()
}
pub fn get(&self) -> &V {
&self.entry.get().value
}
pub fn get_mut(&mut self) -> &mut V {
&mut self.entry.get_mut().value
}
pub fn insert(&mut self, value: V, duration: Duration) -> V {
let internal_entry = self.entry.insert(InternalEntry::new(value, duration));
internal_entry.value
}
}
pub struct VacantEntry<'a, K: 'a, V: 'a, S: 'a = RandomState> {
entry: VacantLinkHashMapEntry<'a, K, InternalEntry<V>, S>
}
impl<'a, K: 'a + Hash + Eq, V: 'a, S: BuildHasher> VacantEntry<'a, K, V, S> {
pub fn key(&self) -> &K {
self.entry.key()
}
pub fn insert(self, value: V, duration: Duration) -> &'a mut V {
let internal_entry = self.entry.insert(InternalEntry::new(value, duration));
&mut internal_entry.value
}
}
#[derive(Clone)]
struct InternalEntry<V> {
value: V,
expiration: Instant,
}
impl<V> InternalEntry<V> {
fn new(v: V, duration: Duration) -> Self {
InternalEntry {
value: v,
expiration: Instant::now() + duration,
}
}
fn is_expired(&self) -> bool {
Instant::now() > self.expiration
}
}
pub struct TtlCache<K: Eq + Hash, V, S: BuildHasher = RandomState> {
map: LinkedHashMap<K, InternalEntry<V>, S>,
max_size: usize,
#[cfg(feature = "stats")]
hits: AtomicUsize,
#[cfg(feature = "stats")]
misses: AtomicUsize,
#[cfg(feature = "stats")]
since: Instant,
}
impl<K: Eq + Hash, V> TtlCache<K, V> {
pub fn new(capacity: usize) -> Self {
TtlCache {
map: LinkedHashMap::new(),
max_size: capacity,
#[cfg(feature = "stats")]
hits: AtomicUsize::new(0),
#[cfg(feature = "stats")]
misses: AtomicUsize::new(0),
#[cfg(feature = "stats")]
since: Instant::now(),
}
}
}
impl<K: Eq + Hash, V, S: BuildHasher> TtlCache<K, V, S> {
pub fn with_hasher(capacity: usize, hash_builder: S) -> Self {
TtlCache {
map: LinkedHashMap::with_hasher(hash_builder),
max_size: capacity,
#[cfg(feature = "stats")]
hits: AtomicUsize::new(0),
#[cfg(feature = "stats")]
misses: AtomicUsize::new(0),
#[cfg(feature = "stats")]
since: Instant::now(),
}
}
pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.get(key).is_some()
}
pub fn insert(&mut self, k: K, v: V, ttl: Duration) -> Option<V> {
let to_insert = InternalEntry::new(v, ttl);
let old_val = self.map.insert(k, to_insert);
if self.len() > self.capacity() {
self.remove_oldest();
}
old_val.and_then(|x| if x.is_expired() { None } else { Some(x.value) })
}
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
let to_ret = self.map
.get(k)
.and_then(|x| if x.is_expired() { None } else { Some(&x.value) });
#[cfg(feature = "stats")]
{
if to_ret.is_some() {
self.hits.fetch_add(1, Ordering::Relaxed);
} else {
self.misses.fetch_add(1, Ordering::Relaxed);
}
}
to_ret
}
pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
let to_ret = self.map.get_mut(k).and_then(|x| {
if x.is_expired() {
None
} else {
Some(&mut x.value)
}
});
#[cfg(feature = "stats")]
{
if to_ret.is_some() {
self.hits.fetch_add(1, Ordering::Relaxed);
} else {
self.misses.fetch_add(1, Ordering::Relaxed);
}
}
to_ret
}
pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.map
.remove(k)
.and_then(|x| if x.is_expired() { None } else { Some(x.value) })
}
pub fn capacity(&self) -> usize {
self.max_size
}
pub fn set_capacity(&mut self, capacity: usize) {
for _ in capacity..self.len() {
self.remove_oldest();
}
self.max_size = capacity;
}
pub fn clear(&mut self) {
self.map.clear();
}
pub fn entry(&mut self, k: K) -> Entry<K, V, S> {
let should_remove = self.map.get(&k).map(|value| value.is_expired()).unwrap_or(false);
if should_remove {
self.map.remove(&k);
}
match self.map.entry(k){
LinkedHashMapEntry::Occupied(entry) => {
Entry::Occupied(OccupiedEntry {
entry
})
}
LinkedHashMapEntry::Vacant(entry) => {
Entry::Vacant(VacantEntry{
entry
})
}
}
}
pub fn iter(&mut self) -> Iter<K, V> {
self.remove_expired();
Iter(self.map.iter())
}
pub fn iter_mut(&mut self) -> IterMut<K, V> {
self.remove_expired();
IterMut(self.map.iter_mut())
}
#[cfg(feature = "stats")]
pub fn reset_stats_counter(&mut self) {
self.hits = AtomicUsize::new(0);
self.misses = AtomicUsize::new(0);
self.since = Instant::now();
}
#[cfg(feature = "stats")]
pub fn hit_count(&self) -> usize {
self.hits.load(Ordering::Relaxed)
}
#[cfg(feature = "stats")]
pub fn miss_count(&self) -> usize {
self.misses.load(Ordering::Relaxed)
}
#[cfg(feature = "stats")]
pub fn stats_since(&self) -> Instant {
self.since
}
fn len(&self) -> usize {
self.map.len()
}
fn remove_expired(&mut self) {
let should_pop_head = |map: &LinkedHashMap<K, InternalEntry<V>, S>| match map.front() {
Some(entry) => entry.1.is_expired(),
None => false,
};
while should_pop_head(&self.map) {
self.map.pop_front();
}
}
fn remove_oldest(&mut self) {
self.map.pop_front();
}
}
impl<K: Eq + Hash, V> Clone for TtlCache<K, V>
where
K: Clone,
V: Clone,
{
fn clone(&self) -> TtlCache<K, V> {
TtlCache {
map: self.map.clone(),
max_size: self.max_size,
#[cfg(feature = "stats")]
hits: AtomicUsize::new(self.hits.load(Ordering::Relaxed)),
#[cfg(feature = "stats")]
misses: AtomicUsize::new(self.misses.load(Ordering::Relaxed)),
#[cfg(feature = "stats")]
since: self.since,
}
}
}
pub struct Iter<'a, K: 'a, V: 'a>(linked_hash_map::Iter<'a, K, InternalEntry<V>>);
impl<'a, K, V> Clone for Iter<'a, K, V> {
fn clone(&self) -> Iter<'a, K, V> {
Iter(self.0.clone())
}
}
impl<'a, K, V> Iterator for Iter<'a, K, V> {
type Item = (&'a K, &'a V);
fn next(&mut self) -> Option<(&'a K, &'a V)> {
match self.0.next() {
Some(entry) => {
if entry.1.is_expired() {
self.next()
} else {
Some((entry.0, &entry.1.value))
}
}
None => None,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<'a, K, V> DoubleEndedIterator for Iter<'a, K, V> {
fn next_back(&mut self) -> Option<(&'a K, &'a V)> {
match self.0.next_back() {
Some(entry) => {
if entry.1.is_expired() {
None
} else {
Some((entry.0, &entry.1.value))
}
}
None => None,
}
}
}
pub struct IterMut<'a, K: 'a, V: 'a>(linked_hash_map::IterMut<'a, K, InternalEntry<V>>);
impl<'a, K, V> Iterator for IterMut<'a, K, V> {
type Item = (&'a K, &'a mut V);
fn next(&mut self) -> Option<(&'a K, &'a mut V)> {
match self.0.next() {
Some(entry) => {
if entry.1.is_expired() {
self.next()
} else {
Some((entry.0, &mut entry.1.value))
}
}
None => None,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<'a, K, V> DoubleEndedIterator for IterMut<'a, K, V> {
fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> {
match self.0.next_back() {
Some(entry) => {
if entry.1.is_expired() {
None
} else {
Some((entry.0, &mut entry.1.value))
}
}
None => None,
}
}
}