use super::bucket::{Bucket, BucketPtr};
use super::entry::{Entry, OccupiedEntry, VacantEntry};
use core::cell::UnsafeCell;
use core::hash::Hash;
use equivalent::Equivalent;
use hashbrown::{HashTable, hash_table};
pub(super) type Entries<K, V> = crate::stk::BumpStk<Bucket<K, V>>;
pub(super) struct Core<K, V> {
pub(super) hash_table: UnsafeCell<HashTable<BucketPtr<K, V>>>,
pub(super) entries: Entries<K, V>,
}
impl<K, V> Core<K, V> {
pub(super) const fn new() -> Self {
Self {
hash_table: UnsafeCell::new(HashTable::new()),
entries: Entries::new(),
}
}
pub(super) fn with_capacity(n: usize) -> Self {
Self {
hash_table: UnsafeCell::new(HashTable::with_capacity(n)),
entries: Entries::with_capacity(n),
}
}
pub(super) fn capacity(&self) -> usize {
usize::min(
self.entries.capacity(),
unsafe { self.hashtab() }.capacity(),
)
}
pub(super) fn clear(&mut self) {
self.hash_table.get_mut().clear();
self.entries.clear();
}
pub(super) fn len(&self) -> usize {
assert_eq!(self.entries.len(), unsafe { self.hashtab() }.len());
self.entries.len()
}
pub(super) fn contains_key<F, Q>(&self, hasher: F, key: &Q) -> bool
where
F: Fn() -> u64,
Q: ?Sized + Hash + Equivalent<K>,
{
let hash_table = unsafe { self.hashtab() };
if hash_table.is_empty() {
false
} else {
hash_table.find(hasher(), eq_fn(key)).is_some()
}
}
pub(super) fn get_key_value<Q>(&self, hash: u64, key: &Q) -> Option<(&K, &V)>
where
Q: ?Sized + Hash + Equivalent<K>,
{
let hash_table = unsafe { self.hashtab() };
if let Some(ptr) = hash_table.find(hash, eq_fn(key)) {
let bucket = unsafe { ptr.as_ref() };
Some(bucket.as_tuple())
} else {
None
}
}
pub(super) fn get_mut<Q>(&mut self, hash: u64, key: &Q) -> Option<&mut V>
where
Q: ?Sized + Hash + Equivalent<K>,
{
let hash_table = self.hash_table.get_mut();
if let Some(ptr) = hash_table.find_mut(hash, eq_fn(key)) {
let bucket = unsafe { ptr.as_mut() };
Some(bucket.as_tuple_mut().1)
} else {
None
}
}
pub(super) fn insert_unique(&self, hash: u64, key: K, value: V) -> BucketPtr<K, V> {
let bucket = Bucket::new(hash, key, value);
let bucket_ptr = self.entries.push_ptr(|| bucket);
let hash_table = unsafe { &mut *self.hash_table.get() };
hash_table.insert_unique(hash, bucket_ptr, hash_fn());
bucket_ptr
}
unsafe fn hashtab(&self) -> &HashTable<BucketPtr<K, V>> {
unsafe { &*self.hash_table.get() }
}
}
impl<K: Eq, V> Core<K, V> {
pub(super) fn pop(&mut self) -> Option<Bucket<K, V>> {
if let Some(bucket) = self.entries.pop() {
let hash_table = self.hash_table.get_mut();
let entry = hash_table.find_entry(bucket.hash, eq_fn(&bucket.key));
unsafe { entry.unwrap_unchecked().remove() };
Some(bucket)
} else {
None
}
}
pub(super) fn entry(&self, hash: u64, key: K) -> Entry<'_, K, V> {
let hash_table = unsafe { &mut *self.hash_table.get() };
match hash_table.entry(hash, eq_fn(&key), hash_fn()) {
hash_table::Entry::Occupied(entry) => {
Entry::Occupied(unsafe { OccupiedEntry::new(*entry.get()) })
}
hash_table::Entry::Vacant(_) => Entry::Vacant(VacantEntry::new(hash, key, self)),
}
}
pub(super) fn insert(
&self,
hash: u64,
key: K,
value: V,
) -> Result<BucketPtr<K, V>, InsertionError<'_, K, V>> {
let hash_table = unsafe { &mut *self.hash_table.get() };
match hash_table.find_entry(hash, eq_fn(&key)) {
Ok(entry) => {
let bucket_ptr = *entry.get();
Err(InsertionError::new(key, value, bucket_ptr))
}
Err(absent_entry) => {
let new_bucket = Bucket::new(hash, key, value);
let bucket_ptr = self.entries.push_ptr(|| new_bucket);
let table = absent_entry.into_table();
table.insert_unique(hash, bucket_ptr, hash_fn());
Ok(bucket_ptr)
}
}
}
pub(super) fn update_full(&mut self, hash: u64, key: K, value: V) -> Option<(K, V)> {
let hash_table = self.hash_table.get_mut();
match hash_table.entry(hash, eq_fn(&key), hash_fn()) {
hash_table::Entry::Occupied(mut entry) => {
let ptr = entry.get_mut();
let bucket = unsafe { ptr.as_mut() };
let mut new_bucket = Bucket::new(hash, key, value);
core::mem::swap(&mut bucket.key, &mut new_bucket.key);
core::mem::swap(&mut bucket.value, &mut new_bucket.value);
Some((new_bucket.key, new_bucket.value))
}
hash_table::Entry::Vacant(entry) => {
let new_bucket = Bucket::new(hash, key, value);
let bucket_ptr = self.entries.push_ptr(|| new_bucket);
entry.insert(bucket_ptr);
None
}
}
}
pub(super) fn update(&mut self, hash: u64, k: K, v: V) -> Option<V> {
let hash_table = self.hash_table.get_mut();
match hash_table.entry(hash, eq_fn(&k), hash_fn()) {
hash_table::Entry::Occupied(mut entry) => {
let ptr = entry.get_mut();
let bucket = unsafe { ptr.as_mut() };
let mut new_bucket = Bucket::new(hash, k, v);
core::mem::swap(&mut bucket.value, &mut new_bucket.value);
Some(new_bucket.value)
}
hash_table::Entry::Vacant(entry) => {
let new_bucket = Bucket::new(hash, k, v);
let bucket_ptr = self.entries.push_ptr(|| new_bucket);
entry.insert(bucket_ptr);
None
}
}
}
}
fn hash_fn<K, V>() -> impl Fn(&BucketPtr<K, V>) -> u64 {
|ptr: &BucketPtr<K, V>| -> u64 {
let bucket = unsafe { ptr.as_ref() };
bucket.hash
}
}
fn eq_fn<K, Q, V>(key: &Q) -> impl FnMut(&BucketPtr<K, V>) -> bool
where
Q: ?Sized + Equivalent<K>,
{
|ptr: &BucketPtr<K, V>| -> bool {
let bucket = unsafe { ptr.as_ref() };
Equivalent::equivalent(key, &bucket.key)
}
}
impl<K, V> core::clone::Clone for Core<K, V>
where
K: Clone,
V: Clone,
{
fn clone(&self) -> Self {
let hash_table = unsafe { self.hashtab() };
Self {
hash_table: UnsafeCell::new(hash_table.clone()),
entries: self.entries.clone(),
}
}
}
#[derive(PartialEq, Eq)]
pub(super) struct InsertionError<'a, K, V> {
pub(super) key: K,
pub(super) new_value: V,
pub(super) old_bucket_ptr: BucketPtr<K, V>,
phantom: std::marker::PhantomData<(&'a K, &'a V)>,
}
impl<'a, K, V> InsertionError<'a, K, V> {
pub(super) fn new(key: K, new_value: V, old_bucket_ptr: BucketPtr<K, V>) -> Self {
Self {
key,
new_value,
old_bucket_ptr,
phantom: std::marker::PhantomData,
}
}
}