use core::borrow::Borrow;
use core::ptr;
use kevy_hash::KevyHash;
use crate::group::Group;
use crate::map::{DELETED, EMPTY, GROUP_WIDTH, KevyMap, ProbeOutcome, h2};
impl<K: KevyHash + Eq, V> KevyMap<K, V> {
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
self.maybe_grow();
self.insert_with_room(key, value)
}
#[inline]
pub(crate) fn insert_with_room(&mut self, key: K, value: V) -> Option<V> {
let hash = key.kevy_hash();
match self.probe_with_key(hash, &key) {
ProbeOutcome::Found(idx) => {
let v_ptr = unsafe {
let kv: *mut (K, V) = self.slots_ptr.as_ptr().add(idx).cast::<(K, V)>();
ptr::addr_of_mut!((*kv).1)
};
let old_v = unsafe { ptr::replace(v_ptr, value) };
drop(key);
Some(old_v)
}
ProbeOutcome::NotFound { insert_at, via_tombstone } => {
self.set_meta(insert_at, h2(hash));
unsafe {
(*self.slots_ptr.as_ptr().add(insert_at)).write((key, value));
}
self.occupied += 1;
if via_tombstone {
self.deleted -= 1;
}
None
}
}
}
pub(crate) fn maybe_grow(&mut self) {
if self.grows_on_insert() {
self.grow();
}
}
fn probe_with_key(&self, hash: u64, key: &K) -> ProbeOutcome {
if self.cap == 0 {
return ProbeOutcome::NotFound { insert_at: 0, via_tombstone: false };
}
let h2v = h2(hash);
let mut group_start = (hash as usize) & self.mask;
if self.deleted == 0 {
loop {
let g = unsafe { Group::load(self.metadata_ptr.as_ptr().add(group_start)) };
for m in g.match_byte(h2v).iter() {
let slot = (group_start + m) & self.mask;
let kv = unsafe { (*self.slots_ptr.as_ptr().add(slot)).assume_init_ref() };
if &kv.0 == key {
return ProbeOutcome::Found(slot);
}
}
if let Some(m) = g.match_byte(EMPTY).lowest_set() {
return ProbeOutcome::NotFound {
insert_at: (group_start + m) & self.mask,
via_tombstone: false,
};
}
group_start = (group_start + GROUP_WIDTH) & self.mask;
}
}
let mut first_deleted: Option<usize> = None;
loop {
let g = unsafe { Group::load(self.metadata_ptr.as_ptr().add(group_start)) };
for m in g.match_byte(h2v).iter() {
let slot = (group_start + m) & self.mask;
let kv = unsafe { (*self.slots_ptr.as_ptr().add(slot)).assume_init_ref() };
if &kv.0 == key {
return ProbeOutcome::Found(slot);
}
}
if first_deleted.is_none()
&& let Some(m) = g.match_byte(DELETED).lowest_set()
{
first_deleted = Some((group_start + m) & self.mask);
}
if let Some(m) = g.match_byte(EMPTY).lowest_set() {
let probe_empty = (group_start + m) & self.mask;
return ProbeOutcome::NotFound {
insert_at: first_deleted.unwrap_or(probe_empty),
via_tombstone: first_deleted.is_some(),
};
}
group_start = (group_start + GROUP_WIDTH) & self.mask;
}
}
}
impl<K, V> KevyMap<K, V> {
pub fn get<Q>(&self, key: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: KevyHash + Eq + ?Sized,
{
let idx = self.find_by_borrow(key)?;
let kv = unsafe { (*self.slots_ptr.as_ptr().add(idx)).assume_init_ref() };
Some(&kv.1)
}
pub fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: KevyHash + Eq + ?Sized,
{
let idx = self.find_by_borrow(key)?;
let kv = unsafe { (*self.slots_ptr.as_ptr().add(idx)).assume_init_mut() };
Some(&mut kv.1)
}
pub fn contains_key<Q>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: KevyHash + Eq + ?Sized,
{
self.find_by_borrow(key).is_some()
}
pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: KevyHash + Eq + ?Sized,
{
let idx = self.find_by_borrow(key)?;
let mark = self.erase_mark(idx);
self.set_meta(idx, mark);
self.occupied -= 1;
if mark == DELETED {
self.deleted += 1;
}
let (_k, v) = unsafe { ptr::read(self.slots_ptr.as_ptr().add(idx) as *const (K, V)) };
Some(v)
}
fn erase_mark(&self, idx: usize) -> u8 {
let before = idx.wrapping_sub(GROUP_WIDTH) & self.mask;
let (gb, ga) = unsafe {
(
Group::load(self.metadata_ptr.as_ptr().add(before)),
Group::load(self.metadata_ptr.as_ptr().add(idx)),
)
};
let empty_before = gb.match_byte(EMPTY).slot_mask().leading_zeros() as usize;
let empty_after = ga.match_byte(EMPTY).slot_mask().trailing_zeros() as usize;
if empty_before + empty_after >= GROUP_WIDTH { DELETED } else { EMPTY }
}
pub(crate) fn find_by_borrow<Q>(&self, key: &Q) -> Option<usize>
where
K: Borrow<Q>,
Q: KevyHash + Eq + ?Sized,
{
if self.cap == 0 {
return None;
}
let hash = key.kevy_hash();
let h2v = h2(hash);
let mut group_start = (hash as usize) & self.mask;
loop {
let g = unsafe { Group::load(self.metadata_ptr.as_ptr().add(group_start)) };
for m in g.match_byte(h2v).iter() {
let slot = (group_start + m) & self.mask;
let kv = unsafe { (*self.slots_ptr.as_ptr().add(slot)).assume_init_ref() };
if kv.0.borrow() == key {
return Some(slot);
}
}
if !g.match_byte(EMPTY).is_empty() {
return None;
}
group_start = (group_start + GROUP_WIDTH) & self.mask;
}
}
pub(crate) fn probe_by_borrow<Q>(&self, key: &Q) -> ProbeOutcome
where
K: Borrow<Q>,
Q: KevyHash + Eq + ?Sized,
{
if self.cap == 0 {
return ProbeOutcome::NotFound { insert_at: 0, via_tombstone: false };
}
let hash = key.kevy_hash();
let h2v = h2(hash);
let group_start = (hash as usize) & self.mask;
if self.deleted == 0 {
self.probe_by_borrow_fast(key, h2v, group_start)
} else {
self.probe_by_borrow_slow(key, h2v, group_start)
}
}
fn probe_by_borrow_fast<Q>(&self, key: &Q, h2v: u8, mut group_start: usize) -> ProbeOutcome
where
K: Borrow<Q>,
Q: KevyHash + Eq + ?Sized,
{
loop {
let g = unsafe { Group::load(self.metadata_ptr.as_ptr().add(group_start)) };
for m in g.match_byte(h2v).iter() {
let slot = (group_start + m) & self.mask;
let kv = unsafe { (*self.slots_ptr.as_ptr().add(slot)).assume_init_ref() };
if kv.0.borrow() == key {
return ProbeOutcome::Found(slot);
}
}
if let Some(m) = g.match_byte(EMPTY).lowest_set() {
return ProbeOutcome::NotFound {
insert_at: (group_start + m) & self.mask,
via_tombstone: false,
};
}
group_start = (group_start + GROUP_WIDTH) & self.mask;
}
}
fn probe_by_borrow_slow<Q>(&self, key: &Q, h2v: u8, mut group_start: usize) -> ProbeOutcome
where
K: Borrow<Q>,
Q: KevyHash + Eq + ?Sized,
{
let mut first_deleted: Option<usize> = None;
loop {
let g = unsafe { Group::load(self.metadata_ptr.as_ptr().add(group_start)) };
for m in g.match_byte(h2v).iter() {
let slot = (group_start + m) & self.mask;
let kv = unsafe { (*self.slots_ptr.as_ptr().add(slot)).assume_init_ref() };
if kv.0.borrow() == key {
return ProbeOutcome::Found(slot);
}
}
if first_deleted.is_none()
&& let Some(m) = g.match_byte(DELETED).lowest_set()
{
first_deleted = Some((group_start + m) & self.mask);
}
if let Some(m) = g.match_byte(EMPTY).lowest_set() {
let probe_empty = (group_start + m) & self.mask;
return ProbeOutcome::NotFound {
insert_at: first_deleted.unwrap_or(probe_empty),
via_tombstone: first_deleted.is_some(),
};
}
group_start = (group_start + GROUP_WIDTH) & self.mask;
}
}
}