use crate::util::CacheEntry;
use ahash::RandomState;
use core::hash::{BuildHasher, Hash};
use core::sync::atomic::Ordering;
use std::collections::HashSet;
use std::collections::VecDeque;
pub(crate) const MAX_FREQ: u8 = 3;
pub(crate) const LOC_SMALL: u8 = 0;
pub(crate) const LOC_MAIN: u8 = 1;
pub(crate) struct ShardData<K, V> {
pub(crate) map: hashbrown::raw::RawTable<(K, CacheEntry<V>)>,
pub(crate) small_hashes: VecDeque<u64>,
pub(crate) small_keys: VecDeque<K>,
pub(crate) main_hashes: VecDeque<u64>,
pub(crate) main_keys: VecDeque<K>,
pub(crate) ghost_set: HashSet<u64, RandomState>,
pub(crate) small_live: usize,
pub(crate) main_live: usize,
pub(crate) shard_cap: usize,
pub(crate) small_cap: usize,
pub(crate) main_cap: usize,
pub(crate) ghost_cap: usize,
}
impl<K, V> ShardData<K, V> {
pub(crate) fn new(map_cap: usize, shard_cap: usize) -> Self {
let (small_cap, main_cap, ghost_cap) = if shard_cap == 0 {
(0, 0, 0)
} else {
let s = shard_cap.div_ceil(10).max(1);
let m = shard_cap.saturating_sub(s).max(1);
(s, m, shard_cap)
};
Self {
map: hashbrown::raw::RawTable::with_capacity(map_cap),
small_hashes: VecDeque::new(),
small_keys: VecDeque::new(),
main_hashes: VecDeque::new(),
main_keys: VecDeque::new(),
ghost_set: HashSet::with_hasher(RandomState::new()),
small_live: 0,
main_live: 0,
shard_cap,
small_cap,
main_cap,
ghost_cap,
}
}
#[inline]
pub(crate) fn total_live(&self) -> usize {
self.small_live + self.main_live
}
#[allow(dead_code)]
#[inline]
pub(crate) fn len(&self) -> usize {
self.map.len()
}
#[allow(dead_code)]
#[inline]
pub(crate) fn capacity(&self) -> usize {
self.map.capacity()
}
}
impl<K, V> Default for ShardData<K, V> {
fn default() -> Self {
Self {
map: hashbrown::raw::RawTable::new(),
small_hashes: VecDeque::new(),
small_keys: VecDeque::new(),
main_hashes: VecDeque::new(),
main_keys: VecDeque::new(),
ghost_set: HashSet::with_hasher(RandomState::new()),
small_live: 0,
main_live: 0,
shard_cap: 0,
small_cap: 0,
main_cap: 0,
ghost_cap: 0,
}
}
}
impl<K: Clone + Eq + Hash, V: Clone> Clone for ShardData<K, V> {
fn clone(&self) -> Self {
Self {
map: self.map.clone(),
small_hashes: self.small_hashes.clone(),
small_keys: self.small_keys.clone(),
main_hashes: self.main_hashes.clone(),
main_keys: self.main_keys.clone(),
ghost_set: self.ghost_set.clone(),
small_live: self.small_live,
main_live: self.main_live,
shard_cap: self.shard_cap,
small_cap: self.small_cap,
main_cap: self.main_cap,
ghost_cap: self.ghost_cap,
}
}
}
impl<K: Clone + Eq + Hash, V> ShardData<K, V> {
#[cold]
pub(crate) fn evict_one(&mut self) {
if self.small_live >= self.small_cap {
self.evict_from_small();
} else {
self.evict_from_main();
}
}
fn evict_from_small(&mut self) {
let (hash, key, bucket) = loop {
match (self.small_hashes.pop_front(), self.small_keys.pop_front()) {
(None, _) | (_, None) => {
self.evict_from_main();
return;
}
(Some(h), Some(k)) => {
if let Some(b) = self.map.find(h, |(mk, _)| mk == &k) {
break (h, k, b);
}
}
}
};
let freq = unsafe { bucket.as_ref().1.freq.load(Ordering::Relaxed) };
if freq > 0 {
unsafe {
bucket.as_mut().1.loc = LOC_MAIN;
}
self.small_live -= 1;
self.main_hashes.push_back(hash);
self.main_keys.push_back(key);
self.main_live += 1;
} else {
self.small_live -= 1;
unsafe {
self.map.remove(bucket);
}
self.add_to_ghost(hash);
}
}
fn evict_from_main(&mut self) {
loop {
let (hash, key, bucket) = loop {
match (self.main_hashes.pop_front(), self.main_keys.pop_front()) {
(None, _) | (_, None) => return,
(Some(h), Some(k)) => {
if let Some(b) = self.map.find(h, |(mk, _)| mk == &k) {
break (h, k, b);
}
}
}
};
let freq = unsafe { bucket.as_ref().1.freq.load(Ordering::Relaxed) };
if freq > 0 {
unsafe {
bucket.as_ref().1.freq.store(freq - 1, Ordering::Relaxed);
}
self.main_hashes.push_back(hash);
self.main_keys.push_back(key);
} else {
self.main_live -= 1;
unsafe {
self.map.remove(bucket);
}
return;
}
}
}
#[cold]
pub(crate) fn add_to_ghost(&mut self, hash: u64) {
if self.ghost_set.len() >= self.ghost_cap {
self.ghost_set.clear();
}
self.ghost_set.insert(hash);
}
#[cold]
pub(crate) fn clear_all(&mut self) {
unsafe {
for bucket in self.map.iter() {
self.map.erase(bucket);
}
}
self.small_hashes.clear();
self.small_keys.clear();
self.main_hashes.clear();
self.main_keys.clear();
self.ghost_set.clear();
self.small_live = 0;
self.main_live = 0;
}
}
impl<K: Eq + Hash, V> ShardData<K, V> {
pub(crate) fn map_try_reserve<S: BuildHasher>(
&mut self,
additional: usize,
hasher: &S,
) -> Result<(), hashbrown::TryReserveError> {
self.map
.try_reserve(additional, |(k, _v)| hasher.hash_one(k))
}
pub(crate) fn map_shrink_to<S: BuildHasher>(&mut self, min_capacity: usize, hasher: &S) {
self.map
.shrink_to(min_capacity, |(k, _v)| hasher.hash_one(k));
}
}