use core::marker::PhantomData;
use crate::growth_policy::{GrowthPolicy, LengthError};
use crate::hasher::{EqKey, HashKey};
use crate::serialize::{
Deserialize, DeserializeError, Deserializer, Serialize, Serializer,
SERIALIZATION_PROTOCOL_VERSION,
};
use crate::sparse_array::{
index_in_sparse_bucket, nb_sparse_buckets, popcount_bitmap, sparse_ibucket, Bitmap,
SparseArray, BITMAP_NB_BITS,
};
use crate::sparsity::Sparsity;
pub const DEFAULT_INIT_BUCKET_COUNT: usize = 0;
pub const DEFAULT_MAX_LOAD_FACTOR: f32 = 0.5;
const MAX_BUCKET_COUNT: usize = isize::MAX as usize;
pub trait KeySelect<T> {
type Key;
fn key(value: &T) -> &Self::Key;
}
pub struct SparseHash<T, K, H, E, P, S> {
sparse_buckets: Vec<SparseArray<T>>,
bucket_count: usize,
nb_elements: usize,
nb_deleted_buckets: usize,
load_threshold_rehash: usize,
load_threshold_clear_deleted: usize,
max_load_factor: f32,
hash: H,
key_eq: E,
policy: P,
_key: PhantomData<K>,
_sparsity: PhantomData<S>,
}
impl<T, K, H, E, P, S> SparseHash<T, K, H, E, P, S>
where
P: GrowthPolicy,
{
pub fn new(
bucket_count: usize,
hash: H,
key_eq: E,
max_load_factor: f32,
) -> Result<Self, LengthError> {
let (policy, settled) = P::new(bucket_count)?;
if settled > MAX_BUCKET_COUNT {
return Err(LengthError);
}
let mut sparse_buckets = Vec::new();
if settled > 0 {
let n = nb_sparse_buckets(settled);
sparse_buckets.reserve_exact(n);
for _ in 0..n {
sparse_buckets.push(SparseArray::new());
}
sparse_buckets
.last_mut()
.expect("non-empty by construction")
.set_as_last();
}
let mut this = Self {
sparse_buckets,
bucket_count: settled,
nb_elements: 0,
nb_deleted_buckets: 0,
load_threshold_rehash: 0,
load_threshold_clear_deleted: 0,
max_load_factor: DEFAULT_MAX_LOAD_FACTOR,
hash,
key_eq,
policy,
_key: PhantomData,
_sparsity: PhantomData,
};
this.set_max_load_factor(max_load_factor);
Ok(this)
}
#[inline]
pub fn len(&self) -> usize {
self.nb_elements
}
#[inline]
pub fn is_empty(&self) -> bool {
self.nb_elements == 0
}
#[inline]
pub fn bucket_count(&self) -> usize {
self.bucket_count
}
#[inline]
pub fn max_bucket_count(&self) -> usize {
self.policy.max_bucket_count().min(MAX_BUCKET_COUNT)
}
#[inline]
pub fn max_size(&self) -> usize {
self.max_bucket_count()
}
#[inline]
pub fn load_factor(&self) -> f32 {
if self.bucket_count == 0 {
return 0.0;
}
self.nb_elements as f32 / self.bucket_count as f32
}
#[inline]
pub fn max_load_factor(&self) -> f32 {
self.max_load_factor
}
pub fn set_max_load_factor(&mut self, ml: f32) {
self.max_load_factor = 0.1_f32.max(ml.min(0.8));
self.load_threshold_rehash = (self.bucket_count as f32 * self.max_load_factor) as usize;
let mlf_with_deleted = self.max_load_factor + 0.5 * (1.0 - self.max_load_factor);
self.load_threshold_clear_deleted = (self.bucket_count as f32 * mlf_with_deleted) as usize;
}
#[inline]
pub fn hash_function(&self) -> &H {
&self.hash
}
#[inline]
pub fn key_eq(&self) -> &E {
&self.key_eq
}
#[inline]
fn hash_key<Q>(&self, key: &Q) -> usize
where
H: HashKey<Q>,
Q: ?Sized,
{
self.hash.hash_key(key)
}
#[inline]
fn bucket_for_hash(&self, hash: usize) -> usize {
self.policy.bucket_for_hash(hash)
}
#[inline]
fn next_bucket(&self, ibucket: usize, iprobe: usize) -> usize {
if P::is_power_of_two() {
(ibucket.wrapping_add(iprobe)) & (self.bucket_count - 1)
} else {
let next = ibucket + iprobe;
if next < self.bucket_count {
next
} else {
next % self.bucket_count
}
}
}
pub fn clear(&mut self) {
for bucket in &mut self.sparse_buckets {
let last = bucket.last();
*bucket = SparseArray::new();
if last {
bucket.set_as_last();
}
}
self.nb_elements = 0;
self.nb_deleted_buckets = 0;
}
}
#[derive(Clone, Copy)]
pub struct Position {
pub sparse_ibucket: usize,
pub index: u8,
}
impl<T, K, H, E, P, S> SparseHash<T, K, H, E, P, S>
where
K: KeySelect<T>,
H: HashKey<K::Key> + Clone,
E: EqKey<K::Key, K::Key> + Clone,
P: GrowthPolicy,
S: Sparsity,
{
pub fn find_position<Q>(&self, key: &Q, hash: usize) -> Option<Position>
where
H: HashKey<Q>,
E: EqKey<K::Key, Q>,
Q: ?Sized,
{
if self.bucket_count == 0 {
return None;
}
let mut ibucket = self.bucket_for_hash(hash);
let mut probe = 0usize;
loop {
let sib = sparse_ibucket(ibucket);
let idx = index_in_sparse_bucket(ibucket);
let bucket = &self.sparse_buckets[sib];
if bucket.has_value(idx) {
if self.key_eq.eq_key(K::key(bucket.value(idx)), key) {
return Some(Position {
sparse_ibucket: sib,
index: idx,
});
}
} else if !bucket.has_deleted_value(idx) || probe >= self.bucket_count {
return None;
}
probe += 1;
ibucket = self.next_bucket(ibucket, probe);
}
}
pub fn get<Q>(&self, key: &Q, hash: usize) -> Option<&T>
where
H: HashKey<Q>,
E: EqKey<K::Key, Q>,
Q: ?Sized,
{
self.find_position(key, hash)
.map(|p| self.sparse_buckets[p.sparse_ibucket].value(p.index))
}
pub fn get_mut<Q>(&mut self, key: &Q, hash: usize) -> Option<&mut T>
where
H: HashKey<Q>,
E: EqKey<K::Key, Q>,
Q: ?Sized,
{
let pos = self.find_position(key, hash)?;
Some(self.sparse_buckets[pos.sparse_ibucket].value_mut(pos.index))
}
pub fn contains<Q>(&self, key: &Q, hash: usize) -> bool
where
H: HashKey<Q>,
E: EqKey<K::Key, Q>,
Q: ?Sized,
{
self.find_position(key, hash).is_some()
}
pub fn insert(&mut self, value: T) -> (Position, bool)
where
K::Key: Sized,
{
let hash = self.hash_key(K::key(&value));
self.insert_with_hash(value, hash)
}
pub fn insert_with_hash(&mut self, value: T, hash: usize) -> (Position, bool) {
loop {
if self.sparse_buckets.is_empty() {
let count = self
.policy
.next_bucket_count()
.expect("grow within policy limit");
self.rehash_impl(count);
continue;
}
let mut found_deleted: Option<(usize, u8)> = None;
let mut ibucket = self.bucket_for_hash(hash);
let mut probe = 0usize;
loop {
if probe >= self.bucket_count {
if let Some((dsib, didx)) = found_deleted {
let pos = self.insert_in_bucket(dsib, didx, value);
self.nb_deleted_buckets -= 1;
return (pos, true);
}
let count = self
.policy
.next_bucket_count()
.expect("grow within policy limit");
self.rehash_impl(count);
break;
}
let sib = sparse_ibucket(ibucket);
let idx = index_in_sparse_bucket(ibucket);
let bucket = &self.sparse_buckets[sib];
if bucket.has_value(idx) {
if self
.key_eq
.eq_key(K::key(bucket.value(idx)), K::key(&value))
{
return (
Position {
sparse_ibucket: sib,
index: idx,
},
false,
);
}
} else if bucket.has_deleted_value(idx) && probe < self.bucket_count {
if found_deleted.is_none() {
found_deleted = Some((sib, idx));
}
} else {
if self.nb_elements >= self.load_threshold_rehash {
let count = self
.policy
.next_bucket_count()
.expect("grow within policy limit");
self.rehash_impl(count);
break;
} else if self.nb_elements + self.nb_deleted_buckets
>= self.load_threshold_clear_deleted
{
self.clear_deleted_buckets();
break;
}
if let Some((dsib, didx)) = found_deleted {
let pos = self.insert_in_bucket(dsib, didx, value);
self.nb_deleted_buckets -= 1;
return (pos, true);
}
let pos = self.insert_in_bucket(sib, idx, value);
return (pos, true);
}
probe += 1;
ibucket = self.next_bucket(ibucket, probe);
}
}
}
fn insert_in_bucket(&mut self, sib: usize, index: u8, value: T) -> Position {
self.sparse_buckets[sib].set(index, value, S::STEP as usize);
self.nb_elements += 1;
Position {
sparse_ibucket: sib,
index,
}
}
pub fn remove<Q>(&mut self, key: &Q, hash: usize) -> Option<T>
where
H: HashKey<Q>,
E: EqKey<K::Key, Q>,
Q: ?Sized,
{
let pos = self.find_position(key, hash)?;
let sib = pos.sparse_ibucket;
let offset = self.sparse_buckets[sib].index_to_offset(pos.index);
let value = self.sparse_buckets[sib].remove_value(offset, pos.index);
self.nb_elements -= 1;
self.nb_deleted_buckets += 1;
Some(value)
}
pub fn erase<Q>(&mut self, key: &Q, hash: usize) -> usize
where
H: HashKey<Q>,
E: EqKey<K::Key, Q>,
Q: ?Sized,
{
if self.bucket_count == 0 {
return 0;
}
let mut ibucket = self.bucket_for_hash(hash);
let mut probe = 0usize;
loop {
let sib = sparse_ibucket(ibucket);
let idx = index_in_sparse_bucket(ibucket);
let bucket = &self.sparse_buckets[sib];
if bucket.has_value(idx) {
if self.key_eq.eq_key(K::key(bucket.value(idx)), key) {
let offset = self.sparse_buckets[sib].index_to_offset(idx);
self.sparse_buckets[sib].remove_value(offset, idx);
self.nb_elements -= 1;
self.nb_deleted_buckets += 1;
return 1;
}
} else if !bucket.has_deleted_value(idx) || probe >= self.bucket_count {
return 0;
}
probe += 1;
ibucket = self.next_bucket(ibucket, probe);
}
}
fn clear_deleted_buckets(&mut self) {
self.rehash_impl(self.bucket_count);
debug_assert_eq!(self.nb_deleted_buckets, 0);
}
pub fn remove_nth(&mut self, n: usize) -> Option<T> {
let (mut sib, mut offset) = self.first_position()?;
for _ in 0..n {
let (s, o) = self.next_position(sib, offset)?;
sib = s;
offset = o;
}
let index = self.sparse_buckets[sib].offset_to_index(offset);
let value = self.sparse_buckets[sib].remove_value(offset, index);
self.nb_elements -= 1;
self.nb_deleted_buckets += 1;
Some(value)
}
pub fn erase_all(&mut self) {
for bucket in &mut self.sparse_buckets {
let removed = bucket.erase_all();
self.nb_deleted_buckets += removed;
}
self.nb_elements = 0;
}
pub fn erase_range(&mut self, skip: usize, count: usize) {
if count == 0 {
return;
}
let Some((mut sib, mut offset)) = self.first_position() else {
return;
};
for _ in 0..skip {
match self.next_position(sib, offset) {
Some((s, o)) => {
sib = s;
offset = o;
}
None => return,
}
}
for _ in 0..count {
while offset >= self.sparse_buckets[sib].len() {
let mut n = sib + 1;
while n < self.sparse_buckets.len() && self.sparse_buckets[n].is_empty() {
n += 1;
}
if n >= self.sparse_buckets.len() {
return;
}
sib = n;
offset = 0;
}
let index = self.sparse_buckets[sib].offset_to_index(offset);
self.sparse_buckets[sib].remove_value(offset, index);
self.nb_elements -= 1;
self.nb_deleted_buckets += 1;
}
}
fn rehash_impl(&mut self, count: usize) {
let mut new_table = SparseHash::<T, K, H, E, P, S>::new(
count,
self.hash.clone(),
self.key_eq.clone(),
self.max_load_factor,
)
.expect("rehash target within policy limit");
let old = core::mem::take(&mut self.sparse_buckets);
for bucket in old {
for value in bucket.into_values() {
new_table.insert_on_rehash(value);
}
}
core::mem::swap(self, &mut new_table);
}
fn insert_on_rehash(&mut self, value: T) {
let hash = self.hash_key(K::key(&value));
let mut ibucket = self.bucket_for_hash(hash);
let mut probe = 0usize;
loop {
let sib = sparse_ibucket(ibucket);
let idx = index_in_sparse_bucket(ibucket);
if !self.sparse_buckets[sib].has_value(idx) {
self.sparse_buckets[sib].set(idx, value, S::STEP as usize);
self.nb_elements += 1;
return;
}
probe += 1;
ibucket = self.next_bucket(ibucket, probe);
}
}
pub fn rehash(&mut self, count: usize) {
let needed = (self.len() as f32 / self.max_load_factor()).ceil() as usize;
let count = count.max(needed);
self.rehash_impl(count);
}
pub fn reserve(&mut self, count: usize) {
let buckets = (count as f32 / self.max_load_factor()).ceil() as usize;
self.rehash(buckets);
}
}
impl<T, K, H, E, P, S> SparseHash<T, K, H, E, P, S> {
#[inline]
pub fn value_at_mut(&mut self, pos: Position) -> &mut T {
self.sparse_buckets[pos.sparse_ibucket].value_mut(pos.index)
}
pub fn retain<F>(&mut self, mut keep: F)
where
F: FnMut(&mut T) -> bool,
{
for array in &mut self.sparse_buckets {
let mut offset = 0;
while offset < array.len() {
let index = array.offset_to_index(offset);
if keep(&mut array.values_mut()[offset]) {
offset += 1;
} else {
array.remove_value(offset, index);
self.nb_elements -= 1;
self.nb_deleted_buckets += 1;
}
}
}
}
}
impl<T, K, H, E, P, S> SparseHash<T, K, H, E, P, S> {
pub fn first_position(&self) -> Option<(usize, usize)> {
let mut sib = 0;
while sib < self.sparse_buckets.len() {
if !self.sparse_buckets[sib].is_empty() {
return Some((sib, 0));
}
sib += 1;
}
None
}
pub fn next_position(&self, sib: usize, offset: usize) -> Option<(usize, usize)> {
let next_offset = offset + 1;
if next_offset < self.sparse_buckets[sib].len() {
return Some((sib, next_offset));
}
let mut n = sib + 1;
while n < self.sparse_buckets.len() && self.sparse_buckets[n].is_empty() {
n += 1;
}
if n < self.sparse_buckets.len() {
Some((n, 0))
} else {
None
}
}
pub fn iter(&self) -> Iter<'_, T> {
Iter {
buckets: &self.sparse_buckets,
pos: self.first_position(),
}
}
pub fn into_values(self) -> IntoIter<T> {
let mut arrays = self.sparse_buckets.into_iter();
let inner = match arrays.next() {
Some(a) => a.into_values().into_iter(),
None => Vec::new().into_iter(),
};
IntoIter {
inner,
remaining_arrays: arrays,
}
}
}
pub struct Iter<'a, T> {
buckets: &'a [SparseArray<T>],
pos: Option<(usize, usize)>,
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
let (sib, offset) = self.pos?;
let value = &self.buckets[sib].values()[offset];
let next_offset = offset + 1;
self.pos = if next_offset < self.buckets[sib].len() {
Some((sib, next_offset))
} else {
let mut n = sib + 1;
while n < self.buckets.len() && self.buckets[n].is_empty() {
n += 1;
}
if n < self.buckets.len() {
Some((n, 0))
} else {
None
}
};
Some(value)
}
}
pub struct IntoIter<T> {
inner: std::vec::IntoIter<T>,
remaining_arrays: std::vec::IntoIter<SparseArray<T>>,
}
impl<T> Iterator for IntoIter<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
loop {
if let Some(v) = self.inner.next() {
return Some(v);
}
let array = self.remaining_arrays.next()?;
self.inner = array.into_values().into_iter();
}
}
}
pub struct IterMut<'a, T> {
inner: std::slice::IterMut<'a, T>,
remaining_buckets: std::slice::IterMut<'a, SparseArray<T>>,
}
impl<'a, T> Iterator for IterMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<&'a mut T> {
loop {
if let Some(v) = self.inner.next() {
return Some(v);
}
let bucket = self.remaining_buckets.next()?;
self.inner = bucket.values_mut().iter_mut();
}
}
}
impl<T, K, H, E, P, S> SparseHash<T, K, H, E, P, S> {
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
let mut buckets = self.sparse_buckets.iter_mut();
let inner = match buckets.next() {
Some(b) => b.values_mut().iter_mut(),
None => [].iter_mut(),
};
IterMut {
inner,
remaining_buckets: buckets,
}
}
}
impl<T, K, H, E, P, S> SparseHash<T, K, H, E, P, S>
where
T: Serialize,
{
pub fn serialize<Sz: Serializer>(&self, serializer: &mut Sz) {
serializer.write_u64(SERIALIZATION_PROTOCOL_VERSION);
serializer.write_u64(self.bucket_count as u64);
serializer.write_u64(self.sparse_buckets.len() as u64);
serializer.write_u64(self.nb_elements as u64);
serializer.write_u64(self.nb_deleted_buckets as u64);
serializer.write_f32(self.max_load_factor);
for bucket in &self.sparse_buckets {
serializer.write_u64(bucket.len() as u64);
#[allow(clippy::useless_conversion)]
serializer.write_u64(u64::from(bucket.bitmap_vals()));
#[allow(clippy::useless_conversion)]
serializer.write_u64(u64::from(bucket.bitmap_deleted_vals()));
for value in bucket.values() {
value.serialize(serializer);
}
}
}
}
impl<T, K, H, E, P, S> SparseHash<T, K, H, E, P, S>
where
K: KeySelect<T>,
H: HashKey<K::Key> + Clone,
E: EqKey<K::Key, K::Key> + Clone,
P: GrowthPolicy,
S: Sparsity,
T: Deserialize + Serialize,
K::Key: Sized,
{
pub fn deserialize<D: Deserializer>(
deserializer: &mut D,
hash_compatible: bool,
hash: H,
key_eq: E,
) -> Result<Self, DeserializeError> {
let version = deserializer.read_u64();
if version != SERIALIZATION_PROTOCOL_VERSION {
return Err(DeserializeError(
"the serialization protocol version header is invalid",
));
}
let bucket_count_ds = deserializer.read_u64() as usize;
let nb_sparse = deserializer.read_u64() as usize;
let nb_elements = deserializer.read_u64() as usize;
let nb_deleted = deserializer.read_u64() as usize;
let max_load_factor = deserializer.read_f32();
if !hash_compatible {
let mut table = Self::new(0, hash, key_eq, DEFAULT_MAX_LOAD_FACTOR)
.map_err(|_| DeserializeError("bucket count too big"))?;
table.set_max_load_factor(max_load_factor);
table.reserve(nb_elements);
for _ in 0..nb_sparse {
let sparse_bucket_size = deserializer.read_u64() as usize;
let _bitmap_vals = deserializer.read_u64();
let _bitmap_deleted = deserializer.read_u64();
for _ in 0..sparse_bucket_size {
let value = T::deserialize(deserializer);
table.insert(value);
}
}
Ok(table)
} else {
let (policy, settled) =
P::new(bucket_count_ds).map_err(|_| DeserializeError("bucket count too big"))?;
if settled != bucket_count_ds {
return Err(DeserializeError(
"the growth policy is not the same even though hash_compatible is true",
));
}
if bucket_count_ds > MAX_BUCKET_COUNT {
return Err(DeserializeError("bucket count too big"));
}
if nb_sparse != nb_sparse_buckets(bucket_count_ds) {
return Err(DeserializeError(
"deserialized nb_sparse_buckets is invalid",
));
}
let mut sparse_buckets = Vec::new();
let mut present_count = 0usize;
let mut deleted_count = 0usize;
for _ in 0..nb_sparse {
let sparse_bucket_size = deserializer.read_u64() as usize;
let bitmap_vals = deserializer.read_u64();
let bitmap_deleted = deserializer.read_u64();
if sparse_bucket_size > BITMAP_NB_BITS {
return Err(DeserializeError(
"deserialized sparse_bucket_size is too big for the platform",
));
}
let bitmap_vals: Bitmap = numeric_cast_bitmap(bitmap_vals)
.ok_or(DeserializeError("deserialized bitmap_vals is too big"))?;
let bitmap_deleted: Bitmap = numeric_cast_bitmap(bitmap_deleted).ok_or(
DeserializeError("deserialized bitmap_deleted_vals is too big"),
)?;
if popcount_bitmap(bitmap_vals) as usize != sparse_bucket_size {
return Err(DeserializeError(
"deserialized bitmap_vals popcount does not match the value count",
));
}
if bitmap_vals & bitmap_deleted != 0 {
return Err(DeserializeError(
"a deserialized slot is both present and a tombstone",
));
}
present_count += sparse_bucket_size;
deleted_count += popcount_bitmap(bitmap_deleted) as usize;
let mut values = Vec::with_capacity(sparse_bucket_size);
for _ in 0..sparse_bucket_size {
values.push(T::deserialize(deserializer));
}
sparse_buckets.push(SparseArray::from_parts(bitmap_vals, bitmap_deleted, values));
}
if let Some(last) = sparse_buckets.last_mut() {
last.set_as_last();
}
if present_count != nb_elements || deleted_count != nb_deleted {
return Err(DeserializeError(
"deserialized element or tombstone count does not match the bitmaps",
));
}
let mut table = Self {
sparse_buckets,
bucket_count: bucket_count_ds,
nb_elements,
nb_deleted_buckets: nb_deleted,
load_threshold_rehash: 0,
load_threshold_clear_deleted: 0,
max_load_factor: DEFAULT_MAX_LOAD_FACTOR,
hash,
key_eq,
policy,
_key: PhantomData,
_sparsity: PhantomData,
};
table.set_max_load_factor(max_load_factor);
if table.load_factor() > table.max_load_factor() {
return Err(DeserializeError(
"invalid max_load_factor after deserialization",
));
}
Ok(table)
}
}
}
#[inline]
fn numeric_cast_bitmap(value: u64) -> Option<Bitmap> {
let cast = value as Bitmap;
#[allow(clippy::useless_conversion)]
let round_trip = u64::from(cast);
if round_trip == value {
Some(cast)
} else {
None
}
}
impl<T, K, H, E, P, S> Clone for SparseHash<T, K, H, E, P, S>
where
T: Clone,
H: Clone,
E: Clone,
P: Clone,
{
fn clone(&self) -> Self {
Self {
sparse_buckets: self.sparse_buckets.clone(),
bucket_count: self.bucket_count,
nb_elements: self.nb_elements,
nb_deleted_buckets: self.nb_deleted_buckets,
load_threshold_rehash: self.load_threshold_rehash,
load_threshold_clear_deleted: self.load_threshold_clear_deleted,
max_load_factor: self.max_load_factor,
hash: self.hash.clone(),
key_eq: self.key_eq.clone(),
policy: self.policy.clone(),
_key: PhantomData,
_sparsity: PhantomData,
}
}
}