use super::splitmix64;
use std::collections::BTreeMap;
use std::hash::Hash;
const MAX_PROBE: usize = 32;
const INITIAL_SLOTS: usize = 16;
const LOAD_NUM: usize = 3;
const LOAD_DEN: usize = 4;
#[derive(Debug, Clone)]
enum Slot<K, V> {
Empty,
Occupied(K, V),
Tombstone,
}
#[derive(Debug)]
pub struct DetOpenMap<K: Hash + Eq + Ord + Clone, V: Clone> {
slots: Vec<Slot<K, V>>,
occupied: usize,
tombstones: usize,
fallback: BTreeMap<K, V>,
overflow_to_fallback: u64,
}
impl<K: Hash + Eq + Ord + Clone, V: Clone> Default for DetOpenMap<K, V> {
fn default() -> Self {
Self::new()
}
}
impl<K: Hash + Eq + Ord + Clone, V: Clone> DetOpenMap<K, V> {
pub fn new() -> Self {
Self::with_capacity(INITIAL_SLOTS)
}
pub fn with_capacity(cap: usize) -> Self {
let cap = cap.max(INITIAL_SLOTS).next_power_of_two();
Self {
slots: (0..cap).map(|_| Slot::Empty).collect(),
occupied: 0,
tombstones: 0,
fallback: BTreeMap::new(),
overflow_to_fallback: 0,
}
}
pub fn len(&self) -> usize {
self.occupied + self.fallback.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn overflow_count(&self) -> u64 {
self.overflow_to_fallback
}
fn hash_key(&self, key: &K) -> u64 {
let mut h = std::hash::DefaultHasher::new();
use std::hash::Hasher;
h.write_u64(0xCBF29CE484222325);
key.hash(&mut h);
splitmix64(h.finish())
}
fn slot_index(&self, hash: u64, probe: usize) -> usize {
let n = self.slots.len();
let step = splitmix64(hash.wrapping_add(probe as u64));
(step as usize) & (n - 1)
}
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
if (self.occupied + 1) * LOAD_DEN > self.slots.len() * LOAD_NUM {
self.resize();
}
let h = self.hash_key(&key);
let mut first_tombstone: Option<usize> = None;
for probe in 0..MAX_PROBE {
let idx = self.slot_index(h, probe);
match &self.slots[idx] {
Slot::Empty => {
let slot_idx = first_tombstone.unwrap_or(idx);
if matches!(self.slots[slot_idx], Slot::Tombstone) {
self.tombstones -= 1;
}
self.slots[slot_idx] = Slot::Occupied(key, value);
self.occupied += 1;
return None;
}
Slot::Tombstone => {
if first_tombstone.is_none() {
first_tombstone = Some(idx);
}
}
Slot::Occupied(k, _) if k == &key => {
if let Slot::Occupied(_, old_v) =
std::mem::replace(&mut self.slots[idx], Slot::Empty)
{
self.slots[idx] = Slot::Occupied(key, value);
return Some(old_v);
}
unreachable!()
}
Slot::Occupied(_, _) => continue,
}
}
self.overflow_to_fallback += 1;
self.fallback.insert(key, value)
}
pub fn get(&self, key: &K) -> Option<&V> {
if let Some(v) = self.fallback.get(key) {
return Some(v);
}
let h = self.hash_key(key);
for probe in 0..MAX_PROBE {
let idx = self.slot_index(h, probe);
match &self.slots[idx] {
Slot::Empty => return None,
Slot::Occupied(k, v) if k == key => return Some(v),
Slot::Occupied(_, _) | Slot::Tombstone => continue,
}
}
None
}
pub fn contains_key(&self, key: &K) -> bool {
self.get(key).is_some()
}
pub fn remove(&mut self, key: &K) -> Option<V> {
if let Some(v) = self.fallback.remove(key) {
return Some(v);
}
let h = self.hash_key(key);
for probe in 0..MAX_PROBE {
let idx = self.slot_index(h, probe);
match &self.slots[idx] {
Slot::Empty => return None,
Slot::Occupied(k, _) if k == key => {
if let Slot::Occupied(_, v) =
std::mem::replace(&mut self.slots[idx], Slot::Tombstone)
{
self.occupied -= 1;
self.tombstones += 1;
return Some(v);
}
unreachable!()
}
_ => continue,
}
}
None
}
fn resize(&mut self) {
let new_cap = (self.slots.len() * 2).max(INITIAL_SLOTS);
let old = std::mem::replace(
&mut self.slots,
(0..new_cap).map(|_| Slot::Empty).collect(),
);
self.occupied = 0;
self.tombstones = 0;
for slot in old {
if let Slot::Occupied(k, v) = slot {
self.insert(k, v);
}
}
}
pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> + '_ {
let from_slots = self.slots.iter().filter_map(|s| match s {
Slot::Occupied(k, v) => Some((k, v)),
_ => None,
});
let from_fallback = self.fallback.iter();
from_slots.chain(from_fallback)
}
pub fn iter_sorted(&self) -> Vec<(&K, &V)> {
let mut all: Vec<(&K, &V)> = self.iter().collect();
all.sort_by(|a, b| a.0.cmp(b.0));
all
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn insert_get_basic() {
let mut m: DetOpenMap<i32, &str> = DetOpenMap::new();
m.insert(1, "a");
m.insert(2, "b");
m.insert(3, "c");
assert_eq!(m.get(&1), Some(&"a"));
assert_eq!(m.get(&2), Some(&"b"));
assert_eq!(m.get(&3), Some(&"c"));
assert_eq!(m.get(&4), None);
}
#[test]
fn insert_overwrites() {
let mut m: DetOpenMap<i32, &str> = DetOpenMap::new();
m.insert(1, "a");
let prev = m.insert(1, "b");
assert_eq!(prev, Some("a"));
assert_eq!(m.get(&1), Some(&"b"));
assert_eq!(m.len(), 1);
}
#[test]
fn remove_works_via_tombstone() {
let mut m: DetOpenMap<i32, &str> = DetOpenMap::new();
m.insert(1, "a");
m.insert(2, "b");
m.insert(3, "c");
assert_eq!(m.remove(&2), Some("b"));
assert_eq!(m.get(&2), None);
assert_eq!(m.get(&1), Some(&"a"));
assert_eq!(m.get(&3), Some(&"c"));
}
#[test]
fn resize_preserves_entries() {
let mut m: DetOpenMap<i32, i32> = DetOpenMap::new();
for i in 0..1000 {
m.insert(i, i * 2);
}
for i in 0..1000 {
assert_eq!(m.get(&i), Some(&(i * 2)));
}
}
#[test]
fn deterministic_double_run_iter_sorted() {
let mut a: DetOpenMap<i32, i32> = DetOpenMap::new();
let mut b: DetOpenMap<i32, i32> = DetOpenMap::new();
for i in [3, 1, 4, 1, 5, 9, 2, 6, 5, 3] {
a.insert(i, i);
b.insert(i, i);
}
let av = a.iter_sorted();
let bv = b.iter_sorted();
assert_eq!(av, bv);
}
#[test]
fn iter_sorted_is_canonical_regardless_of_insertion_order() {
let mut a: DetOpenMap<i32, i32> = DetOpenMap::new();
let mut b: DetOpenMap<i32, i32> = DetOpenMap::new();
for i in [1, 2, 3, 4, 5] {
a.insert(i, i);
}
for i in [5, 4, 3, 2, 1] {
b.insert(i, i);
}
let av: Vec<i32> = a.iter_sorted().into_iter().map(|(_, v)| *v).collect();
let bv: Vec<i32> = b.iter_sorted().into_iter().map(|(_, v)| *v).collect();
assert_eq!(av, bv);
assert_eq!(av, vec![1, 2, 3, 4, 5]);
}
}