plain_cache/
cache.rs

1use parking_lot::RwLock;
2use shard::Shard;
3use std::borrow::Borrow;
4use std::hash::{BuildHasher, Hash};
5use std::num::NonZero;
6use std::{cmp, thread};
7
8mod entry;
9mod fixed_size_hash_table;
10mod ring_buffer;
11mod shard;
12
13pub(crate) type RandomState = ahash::RandomState;
14
15pub struct Cache<K, V, S = RandomState> {
16    hash_builder: S,
17    shards: Vec<RwLock<Shard<K, V, S>>>,
18}
19
20impl<K, V, S> Cache<K, V, S>
21where
22    K: Clone + Eq + Hash,
23    V: Clone,
24    S: BuildHasher,
25{
26    pub fn with_capacity(capacity: usize) -> Cache<K, V> {
27        Cache::with_capacity_and_hasher(capacity, RandomState::new())
28    }
29
30    pub fn insert(&self, key: K, value: V) -> Option<V> {
31        let hash = self.hash_builder.hash_one(&key);
32        let shard_lock = self.get_shard(hash);
33
34        let mut shard = shard_lock.write();
35        shard.insert(key, value)
36    }
37
38    pub fn get<Q>(&self, key: &Q) -> Option<V>
39    where
40        K: Borrow<Q>,
41        Q: ?Sized + Hash + Eq,
42    {
43        let hash = self.hash_builder.hash_one(key);
44        let shard_lock = self.get_shard(hash);
45
46        let shard = shard_lock.read();
47        shard.get(key)
48    }
49
50    fn get_shard(&self, hash: u64) -> &RwLock<Shard<K, V, S>> {
51        let shard_idx = hash as usize % cmp::max(self.shards.len() - 1, 1);
52        self.shards
53            .get(shard_idx)
54            .expect("modulo op must return valid shard index")
55    }
56}
57
58impl<K, V, S> Cache<K, V, S>
59where
60    K: Clone + Eq + Hash,
61    V: Clone,
62    S: Clone + BuildHasher,
63{
64    pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> Cache<K, V, S> {
65        let number_of_shards = cmp::min(
66            thread::available_parallelism()
67                .map(NonZero::get)
68                .unwrap_or(1),
69            capacity,
70        ) * 4;
71
72        let mut shards = Vec::with_capacity(number_of_shards);
73        let capacity_per_shard = capacity.div_ceil(number_of_shards);
74
75        for _ in 0..number_of_shards {
76            let shard = Shard::with_capacity_and_hasher(capacity_per_shard, hash_builder.clone());
77            shards.push(RwLock::new(shard))
78        }
79
80        Self {
81            hash_builder,
82            shards,
83        }
84    }
85}