#[cfg(test)]
pub(crate) const R: usize = 4;
#[cfg(not(test))]
const R: usize = 8;
use core::iter::FusedIterator;
use core::mem;
use hashbrown::raw;
#[derive(Clone)]
pub struct RawTable<T> {
table: raw::RawTable<T>,
leftovers: Option<OldTable<T>>,
}
impl<T> RawTable<T> {
#[cfg_attr(feature = "inline-more", inline)]
pub fn new() -> Self {
Self {
table: raw::RawTable::new(),
leftovers: None,
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn with_capacity(capacity: usize) -> Self {
Self {
table: raw::RawTable::with_capacity(capacity),
leftovers: None,
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub unsafe fn erase_no_drop(&mut self, item: &Bucket<T>) {
if item.in_main {
self.table.erase_no_drop(item);
} else if let Some(ref mut lo) = self.leftovers {
lo.table.erase_no_drop(item);
lo.items = lo.table.iter();
} else {
unreachable!("invalid bucket state");
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn clear(&mut self) {
let _ = self.leftovers.take();
self.table.clear();
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn insert(&mut self, hash: u64, value: T, hasher: impl Fn(&T) -> u64) -> Bucket<T> {
let bucket = if self.leftovers.is_some() {
let bucket = self.table.insert(hash, value, &hasher);
self.carry(hasher);
bucket
} else if self.table.capacity() == self.table.len() {
let need_cap = ((R + 1) / R) * (self.table.len() + 1);
let mut new_table = raw::RawTable::with_capacity(need_cap);
let bucket = new_table.insert(hash, value, &hasher);
let old_table = mem::replace(&mut self.table, new_table);
let old_table_items = unsafe { old_table.iter() };
self.leftovers = Some(OldTable {
table: old_table,
items: old_table_items,
});
self.carry(hasher);
bucket
} else {
self.table.insert(hash, value, hasher)
};
Bucket {
bucket,
in_main: true,
}
}
#[inline]
pub fn find(&self, hash: u64, mut eq: impl FnMut(&T) -> bool) -> Option<Bucket<T>> {
let e = self.table.find(hash, &mut eq);
if let Some(bucket) = e {
return Some(Bucket {
bucket,
in_main: true,
});
}
if let Some(OldTable { ref table, .. }) = self.leftovers {
table.find(hash, eq).map(|bucket| Bucket {
bucket,
in_main: false,
})
} else {
None
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn capacity(&self) -> usize {
self.table.capacity()
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn len(&self) -> usize {
self.table.len() + self.leftovers.as_ref().map_or(0, |t| t.table.len())
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn buckets(&self) -> usize {
self.table.buckets()
}
#[cfg_attr(feature = "inline-more", inline)]
pub unsafe fn iter(&self) -> RawIter<T> {
RawIter {
table: self.table.iter(),
leftovers: self.leftovers.as_ref().map(|lo| lo.items.clone()),
}
}
}
impl<T> RawTable<T> {
#[cold]
#[inline(never)]
pub(crate) fn carry(&mut self, hasher: impl Fn(&T) -> u64) {
if let Some(ref mut lo) = self.leftovers {
for _ in 0..R {
if let Some(e) = lo.items.next() {
let value = unsafe {
lo.table.erase_no_drop(&e);
e.read()
};
let hash = hasher(&value);
self.table.insert(hash, value, &hasher);
} else {
let _ = self.leftovers.take();
break;
}
}
}
}
pub(crate) fn is_split(&self) -> bool {
self.leftovers.is_some()
}
#[cfg(test)]
pub(crate) fn main(&self) -> &raw::RawTable<T> {
&self.table
}
#[cfg(test)]
pub(crate) fn leftovers(&self) -> Option<&raw::RawTable<T>> {
self.leftovers.as_ref().map(|lo| &lo.table)
}
}
impl<T> IntoIterator for RawTable<T> {
type Item = T;
type IntoIter = RawIntoIter<T>;
#[cfg_attr(feature = "inline-more", inline)]
fn into_iter(self) -> RawIntoIter<T> {
RawIntoIter {
table: self.table.into_iter(),
leftovers: self.leftovers.map(|lo| {
lo.table.into_iter()
}),
}
}
}
#[derive(Clone)]
struct OldTable<T> {
table: raw::RawTable<T>,
items: raw::RawIter<T>,
}
pub struct Bucket<T> {
bucket: raw::Bucket<T>,
in_main: bool,
}
impl<T> Bucket<T> {
pub fn will_move(&self) -> bool {
!self.in_main
}
}
impl<T> core::ops::Deref for Bucket<T> {
type Target = raw::Bucket<T>;
fn deref(&self) -> &Self::Target {
&self.bucket
}
}
pub struct RawIter<T> {
table: raw::RawIter<T>,
leftovers: Option<raw::RawIter<T>>,
}
impl<T> Clone for RawIter<T> {
#[cfg_attr(feature = "inline-more", inline)]
fn clone(&self) -> Self {
Self {
table: self.table.clone(),
leftovers: self.leftovers.clone(),
}
}
}
impl<T> Iterator for RawIter<T> {
type Item = Bucket<T>;
#[cfg_attr(feature = "inline-more", inline)]
fn next(&mut self) -> Option<Self::Item> {
let leftovers = &mut self.leftovers;
self.table
.next()
.map(|bucket| Bucket {
bucket,
in_main: true,
})
.or_else(|| {
leftovers.as_mut()?.next().map(|bucket| Bucket {
bucket,
in_main: false,
})
})
}
#[cfg_attr(feature = "inline-more", inline)]
fn size_hint(&self) -> (usize, Option<usize>) {
let (mut lo, mut hi) = self.table.size_hint();
if let Some(ref left) = self.leftovers {
let (lo2, hi2) = left.size_hint();
lo += lo2;
if let (Some(ref mut hi), Some(hi2)) = (&mut hi, hi2) {
*hi += hi2;
}
}
(lo, hi)
}
}
impl<T> ExactSizeIterator for RawIter<T> {}
impl<T> FusedIterator for RawIter<T> {}
pub struct RawIntoIter<T> {
table: raw::RawIntoIter<T>,
leftovers: Option<raw::RawIntoIter<T>>,
}
impl<T> RawIntoIter<T> {
#[cfg_attr(feature = "inline-more", inline)]
pub fn iter(&self) -> RawIter<T> {
RawIter {
table: self.table.iter(),
leftovers: self.leftovers.as_ref().map(|lo| lo.iter()),
}
}
}
impl<T> Iterator for RawIntoIter<T> {
type Item = T;
#[cfg_attr(feature = "inline-more", inline)]
fn next(&mut self) -> Option<T> {
let leftovers = &mut self.leftovers;
self.table.next().or_else(|| leftovers.as_mut()?.next())
}
#[cfg_attr(feature = "inline-more", inline)]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter().size_hint()
}
}
impl<T> ExactSizeIterator for RawIntoIter<T> {}
impl<T> FusedIterator for RawIntoIter<T> {}