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}