use std::{alloc::Allocator, hash::Hash};
use zerocopy::FromZeros;
use crate::abi::{BVec, SandboxSafe, hash64};
pub const EMPTY: u32 = 0;
pub const VALID: u32 = 1;
const _: () = assert!(
EMPTY == 0,
"BMap relies on EMPTY=0 for zero-init empty-table semantics"
);
#[repr(C)]
#[derive(FromZeros)]
pub struct Slot<K, V> {
pub state: u32,
_pad: u32,
pub key: K,
pub value: V,
}
unsafe impl<K: SandboxSafe, V: SandboxSafe> SandboxSafe for Slot<K, V> {}
#[repr(C)]
pub struct BMap<K, V, A: Allocator> {
slots: BVec<Slot<K, V>, A>,
len: u64,
}
unsafe impl<K: SandboxSafe, V: SandboxSafe, A: Allocator + SandboxSafe> SandboxSafe
for BMap<K, V, A>
{
}
impl<K, V, A: Allocator> BMap<K, V, A> {
pub fn len(&self) -> usize {
self.len as usize
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
impl<K: Eq + Hash + FromZeros, V: FromZeros, A: Allocator> BMap<K, V, A> {
pub fn try_with_capacity_in(alloc: A, want: usize) -> Option<Self> {
let cap = (want.saturating_mul(10) / 7)
.max(8)
.checked_next_power_of_two()?;
Some(Self {
slots: BVec::try_new_in(alloc, cap)?,
len: 0,
})
}
pub fn insert(&mut self, key: K, value: V) -> bool {
let cap = self.slots.len();
if cap == 0 {
return false;
}
let (mask, start) = (cap - 1, hash64(&key) as usize);
for probe in 0..cap {
let slot = &mut self.slots[(start + probe) & mask];
match slot.state {
EMPTY => {
*slot = Slot {
state: VALID,
_pad: 0,
key: key,
value: value,
};
self.len += 1;
return true;
},
_ if slot.key == key => return false,
_ => {},
}
}
false
}
pub fn get(&self, key: &K) -> Option<&V> {
let cap = self.slots.len();
if cap == 0 {
return None;
}
let (mask, start) = (cap - 1, hash64(key) as usize);
for probe in 0..cap {
let slot = &self.slots[(start + probe) & mask];
match slot.state {
EMPTY => return None,
_ if &slot.key == key => return Some(&slot.value),
_ => {},
}
}
None
}
}