use super::{SetError, ThinSlice};
use core::hash::{BuildHasher, Hash};
use core::hint::unreachable_unchecked;
use hashbrown::HashTable;
use libdd_alloc::{ChainAllocator, VirtualAllocator};
use super::SetHasher as Hasher;
pub struct SliceSet<T: Copy + Hash + Eq + 'static> {
pub(crate) arena: ChainAllocator<VirtualAllocator>,
pub(crate) slices: HashTable<ThinSlice<'static, T>>,
}
impl<T: Copy + Hash + Eq + 'static> SliceSet<T> {
const SIZE_HINT: usize = 1024 * 1024;
pub fn try_with_capacity(capacity: usize) -> Result<Self, SetError> {
let arena = ChainAllocator::new_in(Self::SIZE_HINT, VirtualAllocator {});
let mut slices = HashTable::new();
slices.try_reserve(capacity, |_| unsafe { unreachable_unchecked() })?;
Ok(SliceSet { arena, slices })
}
pub unsafe fn insert_unique_uncontended(
&mut self,
slice: &[T],
) -> Result<ThinSlice<'static, T>, SetError> {
let hash = Hasher::default().hash_one(slice);
self.insert_unique_uncontended_with_hash(hash, slice)
}
#[inline(never)]
pub unsafe fn insert_unique_uncontended_with_hash(
&mut self,
hash: u64,
slice: &[T],
) -> Result<ThinSlice<'static, T>, SetError> {
let obj = ThinSlice::try_allocate_for(slice, &self.arena)?;
let uninit = unsafe { &mut *obj.as_ptr() };
let new_slice = ThinSlice::try_from_slice_in(slice, uninit)?;
self.slices
.try_reserve(1, |thin| Hasher::default().hash_one(thin.as_slice()))?;
self.slices
.insert_unique(hash, new_slice, |_| unsafe { unreachable_unchecked() });
Ok(new_slice)
}
pub fn try_insert(&mut self, slice: &[T]) -> Result<ThinSlice<'static, T>, SetError>
where
T: Hash,
{
let hash = Hasher::default().hash_one(slice);
if let Some(id) = unsafe { self.find_with_hash(hash, slice) } {
return Ok(id);
}
unsafe { self.insert_unique_uncontended(slice) }
}
#[inline(never)]
pub(crate) unsafe fn find_with_hash(
&self,
hash: u64,
slice: &[T],
) -> Option<ThinSlice<'static, T>>
where
T: PartialEq,
{
let interned_slice = self
.slices
.find(hash, |thin_slice| thin_slice.as_slice() == slice)?;
Some(*interned_slice)
}
pub fn iter(&self) -> impl Iterator<Item = ThinSlice<'_, T>> + '_ {
self.slices.iter().copied()
}
pub fn len(&self) -> usize {
self.slices.len()
}
pub fn is_empty(&self) -> bool {
self.slices.is_empty()
}
pub fn capacity(&self) -> usize {
self.slices.capacity()
}
}