#[cfg(not(feature = "std"))]
use crate::nostd_prelude::*;
use alloc::sync::Arc;
use kevy_bytes::SmallBytes;
use kevy_hash::KevyHash;
use kevy_map::KevyMap;
pub const BUCKET_SPLIT: usize = 512;
pub const HS_PROMOTE: usize = 16 * 1024;
const MAX_BITS: u8 = 40;
pub(crate) struct Bucket<V> {
local_bits: u8,
map: KevyMap<SmallBytes, V>,
}
impl<V: Clone> Clone for Bucket<V> {
fn clone(&self) -> Self {
Bucket { local_bits: self.local_bits, map: self.map.clone() }
}
}
pub struct SegMap<V> {
global_bits: u8,
dirs: Vec<u32>,
buckets: Vec<Arc<Bucket<V>>>,
len: usize,
}
impl<V: Clone> Clone for SegMap<V> {
fn clone(&self) -> Self {
SegMap {
global_bits: self.global_bits,
dirs: self.dirs.clone(),
buckets: self.buckets.clone(),
len: self.len,
}
}
}
impl<V: Clone> Default for SegMap<V> {
fn default() -> Self {
SegMap {
global_bits: 0,
dirs: alloc::vec![0],
buckets: alloc::vec![Arc::new(Bucket { local_bits: 0, map: KevyMap::new() })],
len: 0,
}
}
}
impl<V: Clone> SegMap<V> {
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
fn route(&self, hash: u64) -> usize {
if self.global_bits == 0 { 0 } else { (hash >> (64 - self.global_bits)) as usize }
}
#[inline]
fn bucket_of(&self, key: &[u8]) -> usize {
self.dirs[self.route(key.kevy_hash())] as usize
}
pub fn get(&self, key: &[u8]) -> Option<&V> {
self.buckets[self.bucket_of(key)].map.get(key)
}
pub fn contains_key(&self, key: &[u8]) -> bool {
self.buckets[self.bucket_of(key)].map.contains_key(key)
}
pub fn insert(&mut self, key: SmallBytes, value: V) -> Option<V> {
let slot = self.route(key.as_slice().kevy_hash());
let bi = self.dirs[slot] as usize;
let b = Arc::make_mut(&mut self.buckets[bi]);
let old = b.map.insert(key, value);
if old.is_none() {
self.len += 1;
if b.map.len() > BUCKET_SPLIT {
self.split(slot);
}
}
old
}
pub fn remove(&mut self, key: &[u8]) -> Option<V> {
let bi = self.bucket_of(key);
let old = Arc::make_mut(&mut self.buckets[bi]).map.remove(key);
if old.is_some() {
self.len -= 1;
}
old
}
fn split(&mut self, mut slot: usize) {
loop {
let bi = self.dirs[slot] as usize;
let lb = self.buckets[bi].local_bits;
if self.buckets[bi].map.len() <= BUCKET_SPLIT || lb >= MAX_BITS {
return;
}
if lb == self.global_bits {
self.double_directory();
slot <<= 1;
}
let k = self.global_bits - lb;
let span = 1usize << k;
let start = (slot >> k) << k;
let (lo, hi) = self.partition_bucket(bi, lb);
self.buckets[bi] = Arc::new(lo);
let hi_ix = self.buckets.len() as u32;
self.buckets.push(Arc::new(hi));
for s in start + span / 2..start + span {
self.dirs[s] = hi_ix;
}
}
}
fn double_directory(&mut self) {
let mut next = Vec::with_capacity(self.dirs.len() * 2);
for &d in &self.dirs {
next.push(d);
next.push(d);
}
self.dirs = next;
self.global_bits += 1;
}
fn partition_bucket(&self, bi: usize, lb: u8) -> (Bucket<V>, Bucket<V>) {
let src = &self.buckets[bi].map;
let mut lo = KevyMap::with_capacity(src.len() / 2);
let mut hi = KevyMap::with_capacity(src.len() / 2);
let bit = 1u64 << (63 - lb);
for (k, v) in src.iter() {
if k.as_slice().kevy_hash() & bit == 0 {
lo.insert(k.clone(), v.clone());
} else {
hi.insert(k.clone(), v.clone());
}
}
(
Bucket { local_bits: lb + 1, map: lo },
Bucket { local_bits: lb + 1, map: hi },
)
}
pub fn iter(&self) -> impl Iterator<Item = (&SmallBytes, &V)> {
self.buckets.iter().flat_map(|b| b.map.iter())
}
pub fn keys(&self) -> impl Iterator<Item = &SmallBytes> {
self.iter().map(|(k, _)| k)
}
pub fn values(&self) -> impl Iterator<Item = &V> {
self.iter().map(|(_, v)| v)
}
pub fn rand_entry(&self, draw: u64) -> Option<(&SmallBytes, &V)> {
if self.len == 0 {
return None;
}
let mut target = crate::rng::below(draw, self.len as u64) as usize;
for b in &self.buckets {
let m = &b.map;
if target < m.len() {
let s = (draw as usize) % m.capacity().max(1);
return m.iter_from_bucket(s).next().or_else(|| m.iter().next());
}
target -= m.len();
}
None
}
pub fn from_flat(flat: KevyMap<SmallBytes, V>) -> Self {
let mut out = SegMap::default();
for (k, v) in flat.iter() {
out.insert(k.clone(), v.clone());
}
out
}
pub(crate) fn capacity_sum(&self) -> usize {
self.buckets.iter().map(|b| b.map.capacity()).sum()
}
pub(crate) fn dir_len(&self) -> usize {
self.dirs.len()
}
fn shell_weight(&self, per_slot: u64) -> u64 {
(self.dir_len() as u64).saturating_mul(4)
+ (self.buckets.len() as u64).saturating_mul(8)
+ (self.capacity_sum() as u64).saturating_mul(per_slot)
}
pub(crate) fn all_unique(&self) -> bool {
self.buckets.iter().all(|b| Arc::strong_count(b) == 1)
}
#[cfg(test)]
pub(crate) fn bucket_stats(&self) -> Vec<(usize, usize)> {
self.buckets
.iter()
.map(|b| (Arc::strong_count(b), b.map.len()))
.collect()
}
}
impl SegMap<SmallBytes> {
pub(crate) fn weight_as_hash(&self) -> u64 {
self.shell_weight(crate::value::HASH_SLOT_BYTES)
+ self
.iter()
.map(|(f, v)| f.heap_bytes() as u64 + v.heap_bytes() as u64)
.sum::<u64>()
}
}
impl SegMap<f64> {
pub(crate) fn weight_shell_only(&self) -> u64 {
self.shell_weight(crate::value::HASH_SLOT_BYTES)
}
}
impl SegMap<()> {
pub(crate) fn weight_as_set(&self) -> u64 {
self.shell_weight(crate::value::SET_SLOT_BYTES)
+ self.keys().map(|m| m.heap_bytes() as u64).sum::<u64>()
}
}