use allocator_api2::{alloc::Allocator, vec::Vec};
use core::fmt;
pub(crate) mod hash_table {
pub(crate) use super::{Entry, OccupiedEntry, VacantEntry};
}
pub(crate) struct HashTable<T, A: Allocator> {
entries: Vec<(u64, T), A>,
}
impl<T, A: Allocator> HashTable<T, A> {
#[inline]
pub(crate) const fn new_in(alloc: A) -> Self {
Self { entries: Vec::new_in(alloc) }
}
#[inline]
pub(crate) fn with_capacity_in(capacity: usize, alloc: A) -> Self {
Self { entries: Vec::with_capacity_in(capacity, alloc) }
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.entries.len()
}
#[inline]
pub(crate) fn iter(&self) -> impl Iterator<Item = &T> {
self.entries.iter().map(|(_, value)| value)
}
#[inline]
pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.entries.iter_mut().map(|(_, value)| value)
}
pub(crate) fn find(
&self,
hash: u64,
mut eq: impl FnMut(&T) -> bool,
) -> Option<&T> {
self.entries
.iter()
.find(|(stored_hash, value)| *stored_hash == hash && eq(value))
.map(|(_, value)| value)
}
pub(crate) fn entry(
&mut self,
hash: u64,
mut eq: impl FnMut(&T) -> bool,
_hasher: impl Fn(&T) -> u64,
) -> Entry<'_, T, A> {
match self.position(hash, &mut eq) {
Some(index) => {
Entry::Occupied(OccupiedEntry { table: self, index })
}
None => Entry::Vacant(VacantEntry { table: self, hash }),
}
}
pub(crate) fn find_entry(
&mut self,
hash: u64,
mut eq: impl FnMut(&T) -> bool,
) -> Result<OccupiedEntry<'_, T, A>, ()> {
match self.position(hash, &mut eq) {
Some(index) => Ok(OccupiedEntry { table: self, index }),
None => Err(()),
}
}
pub(crate) fn retain(&mut self, mut f: impl FnMut(&T) -> bool) {
self.entries.retain(|(_, value)| f(value));
}
pub(crate) fn insert_unique(
&mut self,
hash: u64,
value: T,
_hasher: impl Fn(&T) -> u64,
) -> OccupiedEntry<'_, T, A> {
self.entries.push((hash, value));
let index = self.entries.len() - 1;
OccupiedEntry { table: self, index }
}
#[inline]
pub(crate) fn clear(&mut self) {
self.entries.clear();
}
#[inline]
pub(crate) fn reserve(
&mut self,
additional: usize,
_hasher: impl Fn(&T) -> u64,
) {
self.entries.reserve(additional);
}
#[inline]
pub(crate) fn shrink_to_fit(&mut self, _hasher: impl Fn(&T) -> u64) {
self.entries.shrink_to_fit();
}
#[inline]
pub(crate) fn shrink_to(
&mut self,
min_capacity: usize,
_hasher: impl Fn(&T) -> u64,
) {
self.entries.shrink_to(min_capacity);
}
#[inline]
pub(crate) fn try_reserve(
&mut self,
additional: usize,
_hasher: impl Fn(&T) -> u64,
) -> Result<(), hashbrown::TryReserveError> {
self.entries.reserve(additional);
Ok(())
}
fn position(
&self,
hash: u64,
eq: &mut impl FnMut(&T) -> bool,
) -> Option<usize> {
self.entries
.iter()
.position(|(stored_hash, value)| *stored_hash == hash && eq(value))
}
}
impl<T: Clone, A: Allocator + Clone> Clone for HashTable<T, A> {
fn clone(&self) -> Self {
Self { entries: self.entries.clone() }
}
}
impl<T, A: Allocator + Default> Default for HashTable<T, A> {
fn default() -> Self {
Self { entries: Vec::new_in(A::default()) }
}
}
impl<T: fmt::Debug, A: Allocator> fmt::Debug for HashTable<T, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
pub(crate) enum Entry<'a, T, A: Allocator> {
Occupied(OccupiedEntry<'a, T, A>),
Vacant(VacantEntry<'a, T, A>),
}
pub(crate) struct OccupiedEntry<'a, T, A: Allocator> {
table: &'a mut HashTable<T, A>,
index: usize,
}
impl<'a, T, A: Allocator> OccupiedEntry<'a, T, A> {
#[inline]
pub(crate) fn get(&self) -> &T {
&self.table.entries[self.index].1
}
#[inline]
pub(crate) fn remove(self) -> T {
self.table.entries.swap_remove(self.index).1
}
}
pub(crate) struct VacantEntry<'a, T, A: Allocator> {
table: &'a mut HashTable<T, A>,
hash: u64,
}
impl<'a, T, A: Allocator> VacantEntry<'a, T, A> {
#[inline]
pub(crate) fn insert(self, value: T) -> OccupiedEntry<'a, T, A> {
self.table.entries.push((self.hash, value));
let index = self.table.entries.len() - 1;
OccupiedEntry { table: self.table, index }
}
}