use core::{
hash::{BuildHasher, Hash},
iter::FusedIterator,
mem::{self, transmute, ManuallyDrop, MaybeUninit},
ptr::NonNull,
};
use crate::{
raw::{
h2,
util::{
equivalent_key, likely, make_hash, unlikely, Bucket, InsertSlot, SizedTypeProperties,
},
Group,
},
Equivalent,
};
enum HashValue<F> {
Pending(F),
Computed(u64),
}
impl<F: FnOnce() -> u64> HashValue<F> {
#[inline]
fn new(f: F) -> Self {
Self::Pending(f)
}
#[inline]
fn get(&mut self) -> u64 {
match self {
Self::Pending(_) => {
let f = match core::mem::replace(self, Self::Computed(0)) {
Self::Pending(f) => f,
Self::Computed(_) => unsafe { core::hint::unreachable_unchecked() },
};
let hash = f();
*self = Self::Computed(hash);
hash
}
Self::Computed(hash) => *hash,
}
}
}
#[derive(Clone)]
pub struct Inline<const N: usize, K, V, SH, SI, const LINEAR_THRESHOLD: usize = { Group::WIDTH }> {
raw: RawInline<N, (K, V), LINEAR_THRESHOLD>,
inline_hasher: Option<SI>,
heap_hasher: Option<SH>,
}
struct RawInline<const N: usize, T, const LINEAR_THRESHOLD: usize = { Group::WIDTH }> {
aligned_groups: AlignedGroups<N>,
len: usize,
data: [MaybeUninit<T>; N],
}
impl<const N: usize, T: Clone, const LINEAR_THRESHOLD: usize> Clone
for RawInline<N, T, LINEAR_THRESHOLD>
{
#[inline]
fn clone(&self) -> Self {
struct DropGuard<'a, T> {
data: &'a mut [MaybeUninit<T>],
len: usize,
}
impl<'a, T> Drop for DropGuard<'a, T> {
fn drop(&mut self) {
for i in 0..self.len {
unsafe {
core::ptr::drop_in_place(self.data[i].as_mut_ptr());
}
}
}
}
let mut aligned_groups: AlignedGroups<N> = unsafe { MaybeUninit::uninit().assume_init() };
let mut data: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
let mut guard = DropGuard {
data: &mut data,
len: 0,
};
for (i, group) in self.aligned_groups.groups.iter().take(self.len).enumerate() {
aligned_groups.groups[i] = *group;
guard.data[i] = MaybeUninit::new(unsafe { self.data[i].assume_init_ref().clone() });
guard.len += 1;
}
mem::forget(guard);
Self {
aligned_groups,
len: self.len,
data,
}
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub(crate) struct AlignedGroups<const N: usize> {
groups: [MaybeUninit<u8>; N],
_align: [Group; 0],
}
impl<const N: usize> AlignedGroups<N> {
#[inline]
unsafe fn ctrl(&self, index: usize) -> *mut u8 {
(self.groups.as_ptr() as *const u8).add(index).cast_mut()
}
}
impl<const N: usize, T, const LINEAR_THRESHOLD: usize> Drop for RawInline<N, T, LINEAR_THRESHOLD> {
#[inline]
fn drop(&mut self) {
unsafe { self.drop_elements() }
}
}
impl<const N: usize, T, const LINEAR_THRESHOLD: usize> RawInline<N, T, LINEAR_THRESHOLD> {
const MIN_LINEAR_LEN: usize = if LINEAR_THRESHOLD > 2 {
LINEAR_THRESHOLD
} else {
2
};
#[inline]
unsafe fn drop_elements(&mut self) {
if T::NEEDS_DROP {
for i in 0..self.len {
core::ptr::drop_in_place(self.data[i].as_mut_ptr());
}
}
}
#[inline]
fn find<F: FnOnce() -> u64>(
&self,
hash: &mut HashValue<F>,
mut eq: impl FnMut(&T) -> bool,
) -> Option<Bucket<T>> {
if N < LINEAR_THRESHOLD || self.len < Self::MIN_LINEAR_LEN {
for i in 0..self.len {
if eq(unsafe { self.data.get_unchecked(i).assume_init_ref() }) {
return Some(unsafe { self.bucket(i) });
}
}
None
} else {
unsafe {
let h2_hash = h2(hash.get());
let full_groups = self.len / Group::WIDTH;
let mut probe_pos = 0;
for _ in 0..full_groups {
let group = Group::load(self.aligned_groups.ctrl(probe_pos));
for bit in group.match_byte(h2_hash) {
let index = probe_pos + bit;
let item: &T = self.data.get_unchecked(index).assume_init_ref();
if likely(eq(item)) {
return Some(self.bucket(index));
}
}
probe_pos += Group::WIDTH;
}
let tail_len = self.len % Group::WIDTH;
if tail_len > 0 {
let group = Group::load(self.aligned_groups.ctrl(probe_pos));
for bit in group.match_byte(h2_hash).and(Group::LOWEST_MASK[tail_len]) {
let index = probe_pos + bit;
let item: &T = self.data.get_unchecked(index).assume_init_ref();
if likely(eq(item)) {
return Some(self.bucket(index));
}
}
}
None
}
}
}
#[inline]
unsafe fn insert_in_slot(&mut self, hash: u64, slot: InsertSlot, value: T) -> Bucket<T> {
*self.aligned_groups.ctrl(slot.index) = h2(hash);
self.len += 1;
let bucket = self.bucket(slot.index);
bucket.write(value);
bucket
}
#[inline]
fn remove_entry<F: FnOnce() -> u64>(
&mut self,
hash: &mut HashValue<F>,
eq: impl FnMut(&T) -> bool,
) -> Option<T> {
match self.find(hash, eq) {
Some(bucket) => Some(unsafe { self.remove(bucket).0 }),
None => None,
}
}
#[inline]
#[allow(clippy::needless_pass_by_value)]
unsafe fn remove(&mut self, item: Bucket<T>) -> (T, InsertSlot) {
let index = self.bucket_index(&item);
let value = item.read();
self.erase(index);
(value, InsertSlot { index })
}
#[inline]
unsafe fn bucket_index(&self, bucket: &Bucket<T>) -> usize {
bucket.to_base_index(NonNull::new_unchecked(self.data.as_ptr() as _))
}
#[inline]
unsafe fn erase(&mut self, index: usize) {
let last = self.len - 1;
if index != last {
core::ptr::copy_nonoverlapping(
self.data[last].as_ptr(),
self.data[index].as_mut_ptr(),
1,
);
self.aligned_groups.groups[index] = self.aligned_groups.groups[last];
}
self.len -= 1;
}
#[inline]
unsafe fn bucket(&self, index: usize) -> Bucket<T> {
Bucket::from_base_index(
NonNull::new_unchecked(transmute::<*mut MaybeUninit<T>, *mut T>(
self.data.as_ptr().cast_mut(),
)),
index,
)
}
}
impl<const N: usize, K, V, const LINEAR_THRESHOLD: usize> RawInline<N, (K, V), LINEAR_THRESHOLD> {
#[inline]
fn retain<F>(&mut self, f: &mut F)
where
F: FnMut(&K, &mut V) -> bool,
{
let mut i = 0;
while i < self.len {
let (k, v) = unsafe { self.data[i].assume_init_mut() };
if f(k, v) {
i += 1;
} else {
unsafe {
core::ptr::drop_in_place(self.data[i].as_mut_ptr());
self.erase(i);
}
}
}
}
}
pub struct Iter<'a, const N: usize, K, V> {
data: &'a [MaybeUninit<(K, V)>; N],
index: usize,
len: usize,
}
impl<'a, const N: usize, K, V> Iterator for Iter<'a, N, K, V> {
type Item = (&'a K, &'a V);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.len {
let kv = unsafe { self.data[self.index].assume_init_ref() };
self.index += 1;
Some((&kv.0, &kv.1))
} else {
None
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.len - self.index;
(remaining, Some(remaining))
}
}
impl<'a, const N: usize, K, V> ExactSizeIterator for Iter<'a, N, K, V> {
#[inline]
fn len(&self) -> usize {
self.len - self.index
}
}
impl<'a, const N: usize, K, V> FusedIterator for Iter<'a, N, K, V> {}
impl<'a, const N: usize, K, V> Clone for Iter<'a, N, K, V> {
#[inline]
fn clone(&self) -> Self {
Self {
data: self.data,
index: self.index,
len: self.len,
}
}
}
pub struct IntoIter<const N: usize, K, V> {
data: [MaybeUninit<(K, V)>; N],
index: usize,
len: usize,
}
impl<const N: usize, K, V> Iterator for IntoIter<N, K, V> {
type Item = (K, V);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.len {
let kv = unsafe { self.data[self.index].as_ptr().read() };
self.index += 1;
Some(kv)
} else {
None
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.len - self.index;
(remaining, Some(remaining))
}
}
impl<const N: usize, K, V> ExactSizeIterator for IntoIter<N, K, V> {
#[inline]
fn len(&self) -> usize {
self.len - self.index
}
}
impl<const N: usize, K, V> FusedIterator for IntoIter<N, K, V> {}
impl<const N: usize, K, V> Drop for IntoIter<N, K, V> {
fn drop(&mut self) {
for i in self.index..self.len {
unsafe { core::ptr::drop_in_place(self.data[i].as_mut_ptr()) };
}
}
}
impl<const N: usize, K, V, SH, SI, const LINEAR_THRESHOLD: usize> IntoIterator
for Inline<N, K, V, SH, SI, LINEAR_THRESHOLD>
{
type Item = (K, V);
type IntoIter = IntoIter<N, K, V>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
let mut this = ManuallyDrop::new(self);
let data = unsafe { core::ptr::read(&this.raw.data) };
let len = this.raw.len;
unsafe {
core::ptr::drop_in_place(&mut this.inline_hasher);
core::ptr::drop_in_place(&mut this.heap_hasher);
}
IntoIter {
data,
index: 0,
len,
}
}
}
impl<const N: usize, K, V, SH, SI, const LINEAR_THRESHOLD: usize>
Inline<N, K, V, SH, SI, LINEAR_THRESHOLD>
{
#[inline]
pub(crate) fn iter(&self) -> Iter<'_, N, K, V> {
Iter {
data: &self.raw.data,
index: 0,
len: self.raw.len,
}
}
const VALIDNESS_CHECK: () = assert!(N != 0, "SmallMap cannot be initialized with zero size.");
#[inline]
pub(crate) const fn new(hash_builder: SI, heap_hasher: SH) -> Self {
#[allow(clippy::let_unit_value)]
let _ = Self::VALIDNESS_CHECK;
Self {
raw: RawInline {
aligned_groups: unsafe { MaybeUninit::uninit().assume_init() },
len: 0,
data: unsafe { MaybeUninit::uninit().assume_init() },
},
inline_hasher: Some(hash_builder),
heap_hasher: Some(heap_hasher),
}
}
#[inline]
pub(crate) fn is_empty(&self) -> bool {
self.raw.len == 0
}
#[inline]
pub(crate) fn is_full(&self) -> bool {
self.raw.len == N
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.raw.len
}
#[inline]
pub(crate) unsafe fn take_inline_hasher(&mut self) -> SI {
self.inline_hasher.take().unwrap_unchecked()
}
#[inline]
pub(crate) unsafe fn take_heap_hasher(&mut self) -> SH {
self.heap_hasher.take().unwrap_unchecked()
}
#[inline]
fn inline_hasher(&self) -> &SI {
self.inline_hasher.as_ref().unwrap()
}
}
impl<const N: usize, K, V, SH, SI, const LINEAR_THRESHOLD: usize>
Inline<N, K, V, SH, SI, LINEAR_THRESHOLD>
where
K: Eq + Hash,
SI: BuildHasher,
{
#[inline]
pub(crate) fn get<Q>(&self, k: &Q) -> Option<&V>
where
Q: ?Sized + Hash + Equivalent<K>,
{
match self.get_inner(k) {
Some((_, v)) => Some(v),
None => None,
}
}
#[inline]
pub(crate) fn get_mut<Q>(&mut self, k: &Q) -> Option<&mut V>
where
Q: ?Sized + Hash + Equivalent<K>,
{
match self.get_inner_mut(k) {
Some((_, v)) => Some(v),
None => None,
}
}
#[inline]
pub(crate) fn get_key_value<Q>(&self, k: &Q) -> Option<(&K, &V)>
where
Q: ?Sized + Hash + Equivalent<K>,
{
match self.get_inner(k) {
Some((key, value)) => Some((key, value)),
None => None,
}
}
#[inline]
pub(crate) fn insert(&mut self, k: K, v: V) -> Option<V> {
if N < LINEAR_THRESHOLD {
let len = self.raw.len;
for i in 0..len {
let item = unsafe { self.raw.data[i].assume_init_mut() };
if k.equivalent(&item.0) {
return Some(mem::replace(&mut item.1, v));
}
}
self.raw.data[len].write((k, v));
self.raw.len = len + 1;
None
} else {
let hash_builder = self.inline_hasher.as_ref().unwrap();
let mut hash = HashValue::new(|| make_hash::<K, SI>(hash_builder, &k));
match self.raw.find(&mut hash, equivalent_key(&k)) {
Some(bucket) => Some(mem::replace(unsafe { &mut bucket.as_mut().1 }, v)),
None => {
let slot = InsertSlot {
index: self.raw.len,
};
unsafe { self.raw.insert_in_slot(hash.get(), slot, (k, v)) };
None
}
}
}
}
#[inline]
pub(crate) unsafe fn insert_unique_unchecked(&mut self, k: K, v: V) -> (&K, &mut V) {
let len = self.raw.len;
if N < LINEAR_THRESHOLD {
self.raw.data[len].write((k, v));
self.raw.len = len + 1;
let item = self.raw.data[len].assume_init_mut();
(&item.0, &mut item.1)
} else {
let hash_builder = self.inline_hasher.as_ref().unwrap();
let hash = make_hash::<K, SI>(hash_builder, &k);
let slot = InsertSlot { index: len };
let bucket = self.raw.insert_in_slot(hash, slot, (k, v));
let (k, v) = bucket.as_mut();
(k, v)
}
}
#[inline]
pub(crate) fn remove_entry<Q>(&mut self, k: &Q) -> Option<(K, V)>
where
Q: ?Sized + Hash + Equivalent<K>,
{
let hash_builder = self.inline_hasher.as_ref().unwrap();
let mut hash = HashValue::new(|| make_hash::<Q, SI>(hash_builder, k));
self.raw.remove_entry(&mut hash, equivalent_key(k))
}
#[inline]
pub(crate) fn retain<F>(&mut self, f: &mut F)
where
F: FnMut(&K, &mut V) -> bool,
{
self.raw.retain(f);
}
#[inline]
fn get_inner<Q>(&self, k: &Q) -> Option<&(K, V)>
where
Q: ?Sized + Hash + Equivalent<K>,
{
if unlikely(self.is_empty()) {
return None;
}
let mut hash = HashValue::new(|| make_hash::<Q, SI>(self.inline_hasher(), k));
match self.raw.find(&mut hash, equivalent_key(k)) {
Some(bucket) => Some(unsafe { bucket.as_ref() }),
None => None,
}
}
#[inline]
fn get_inner_mut<Q>(&mut self, k: &Q) -> Option<&mut (K, V)>
where
Q: ?Sized + Hash + Equivalent<K>,
{
if unlikely(self.is_empty()) {
return None;
}
let hash_builder = self.inline_hasher.as_ref().unwrap();
let mut hash = HashValue::new(|| make_hash::<Q, SI>(hash_builder, k));
match self.raw.find(&mut hash, equivalent_key(k)) {
Some(bucket) => Some(unsafe { bucket.as_mut() }),
None => None,
}
}
}