use crate::hash::Key;
use crate::object::Object;
const EMPTY: usize = usize::MAX;
const DUMMY: usize = usize::MAX - 1;
const MINIMUM: usize = 8;
#[derive(Debug, Clone)]
struct Entry {
hash: i64,
key: Key,
value: Object,
}
#[derive(Debug, Clone, Default)]
pub struct Dict {
indices: Vec<usize>,
entries: Vec<Option<Entry>>,
used: usize,
}
struct Walk {
mask: usize,
slot: usize,
perturb: u64,
}
impl Walk {
#[expect(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
reason = "a hash is a bag of bits here rather than a number, so \
dropping the sign or the top half on a 32-bit target costs \
a little spread and nothing else"
)]
fn new(hash: i64, size: usize) -> Self {
let perturb = hash as u64;
Walk {
mask: size - 1,
slot: (perturb as usize) & (size - 1),
perturb,
}
}
}
impl Iterator for Walk {
type Item = usize;
#[expect(
clippy::cast_possible_truncation,
reason = "the same as in `new`, and for the same reason"
)]
fn next(&mut self) -> Option<usize> {
let slot = self.slot;
self.perturb >>= 5;
self.slot = slot
.wrapping_mul(5)
.wrapping_add(self.perturb as usize)
.wrapping_add(1)
& self.mask;
Some(slot)
}
}
enum Probe {
Occupied { slot: usize, entry: usize },
Vacant { slot: usize },
}
impl Dict {
#[must_use]
pub const fn new() -> Self {
Dict {
indices: Vec::new(),
entries: Vec::new(),
used: 0,
}
}
#[must_use]
pub const fn len(&self) -> usize {
self.used
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.used == 0
}
#[must_use]
pub fn get(&self, key: &Key) -> Option<&Object> {
match self.probe(key)? {
Probe::Occupied { entry, .. } => Some(&self.entry(entry).value),
Probe::Vacant { .. } => None,
}
}
#[must_use]
pub fn contains(&self, key: &Key) -> bool {
self.get(key).is_some()
}
pub fn insert(&mut self, key: Key, value: Object) -> Option<Object> {
self.reserve();
match self
.probe(&key)
.expect("a table was just made if there was none")
{
Probe::Occupied { entry, .. } => {
Some(std::mem::replace(&mut self.entry_mut(entry).value, value))
}
Probe::Vacant { slot } => {
self.indices[slot] = self.entries.len();
self.entries.push(Some(Entry {
hash: key.hash(),
key,
value,
}));
self.used += 1;
None
}
}
}
pub fn remove(&mut self, key: &Key) -> Option<Object> {
match self.probe(key)? {
Probe::Occupied { slot, entry } => {
self.indices[slot] = DUMMY;
let removed = self.entries[entry].take().expect("a live position");
self.used -= 1;
Some(removed.value)
}
Probe::Vacant { .. } => None,
}
}
pub fn clear(&mut self) {
*self = Dict::new();
}
pub fn iter(&self) -> impl Iterator<Item = (&Key, &Object)> {
self.entries
.iter()
.flatten()
.map(|entry| (&entry.key, &entry.value))
}
pub fn keys(&self) -> impl Iterator<Item = &Key> {
self.iter().map(|(key, _)| key)
}
#[must_use]
pub fn entry_at(&self, from: usize) -> Option<(&Key, &Object, usize)> {
let mut at = from;
while let Some(slot) = self.entries.get(at) {
at += 1;
if let Some(entry) = slot {
return Some((&entry.key, &entry.value, at));
}
}
None
}
#[must_use]
pub fn equals(&self, other: &Self) -> bool {
self.used == other.used
&& self
.iter()
.all(|(key, value)| other.get(key).is_some_and(|found| value.same_value(found)))
}
fn probe(&self, key: &Key) -> Option<Probe> {
if self.indices.is_empty() {
return None;
}
let hash = key.hash();
let mut reusable = None;
for slot in Walk::new(hash, self.indices.len()) {
match self.indices[slot] {
EMPTY => {
return Some(Probe::Vacant {
slot: reusable.unwrap_or(slot),
});
}
DUMMY => {
if reusable.is_none() {
reusable = Some(slot);
}
}
entry => {
let candidate = self.entry(entry);
if candidate.hash == hash && candidate.key == *key {
return Some(Probe::Occupied { slot, entry });
}
}
}
}
unreachable!("a table is never full, so a walk always reaches an empty slot")
}
fn reserve(&mut self) {
if self.indices.is_empty() {
self.indices = vec![EMPTY; MINIMUM];
return;
}
if (self.entries.len() + 1) * 3 > self.indices.len() * 2 {
self.rebuild();
}
}
fn rebuild(&mut self) {
let wanted = (self.used + 1).saturating_mul(3).max(MINIMUM);
let size = wanted.next_power_of_two();
self.entries.retain(Option::is_some);
let mut indices = vec![EMPTY; size];
for (position, entry) in self.entries.iter().enumerate() {
let hash = entry.as_ref().expect("the holes were just dropped").hash;
let slot = Walk::new(hash, size)
.find(|&slot| indices[slot] == EMPTY)
.expect("a fresh table has empty slots in it");
indices[slot] = position;
}
self.indices = indices;
}
fn entry(&self, position: usize) -> &Entry {
self.entries[position]
.as_ref()
.expect("a slot only ever points at a live entry")
}
fn entry_mut(&mut self, position: usize) -> &mut Entry {
self.entries[position]
.as_mut()
.expect("a slot only ever points at a live entry")
}
}
impl FromIterator<(Key, Object)> for Dict {
fn from_iter<I: IntoIterator<Item = (Key, Object)>>(pairs: I) -> Self {
let mut dict = Dict::new();
for (key, value) in pairs {
dict.insert(key, value);
}
dict
}
}
#[derive(Debug, Clone, Default)]
pub struct Set {
members: Dict,
}
impl Set {
#[must_use]
pub const fn new() -> Self {
Set {
members: Dict::new(),
}
}
#[must_use]
pub const fn len(&self) -> usize {
self.members.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.members.is_empty()
}
#[must_use]
pub fn contains(&self, value: &Key) -> bool {
self.members.contains(value)
}
pub fn insert(&mut self, value: Key) -> bool {
self.members.insert(value, Object::None).is_none()
}
pub fn remove(&mut self, value: &Key) -> bool {
self.members.remove(value).is_some()
}
pub fn iter(&self) -> impl Iterator<Item = &Key> {
self.members.keys()
}
#[must_use]
pub fn member_at(&self, from: usize) -> Option<(&Key, usize)> {
self.members
.entry_at(from)
.map(|(key, _, next)| (key, next))
}
#[must_use]
pub fn equals(&self, other: &Self) -> bool {
self.len() == other.len() && self.iter().all(|value| other.contains(value))
}
}
impl FromIterator<Key> for Set {
fn from_iter<I: IntoIterator<Item = Key>>(values: I) -> Self {
let mut set = Set::new();
for value in values {
set.insert(value);
}
set
}
}
#[cfg(test)]
mod tests {
use super::*;
fn key(object: Object) -> Key {
Key::new(object).expect("expected this to be hashable")
}
fn int(value: i64) -> Key {
key(Object::int(value))
}
fn order(dict: &Dict) -> Vec<i64> {
dict.keys()
.map(|key| match key.object() {
Object::Int(value) => value.to_i64().expect("small enough"),
other => panic!("not an integer key: {}", other.repr()),
})
.collect()
}
#[test]
fn an_empty_dict_has_no_table_and_answers_anyway() {
let dict = Dict::new();
assert_eq!(dict.len(), 0);
assert!(dict.is_empty());
assert!(dict.get(&int(1)).is_none());
assert!(!dict.contains(&int(1)));
assert_eq!(order(&dict), Vec::<i64>::new());
}
#[test]
fn what_goes_in_comes_back_out() {
let mut dict = Dict::new();
assert!(dict.insert(int(1), Object::str("a")).is_none());
assert!(dict.insert(int(2), Object::str("b")).is_none());
assert_eq!(dict.len(), 2);
assert_eq!(dict.get(&int(1)).expect("present").repr(), "'a'");
assert_eq!(dict.get(&int(2)).expect("present").repr(), "'b'");
assert!(dict.get(&int(3)).is_none());
}
#[test]
fn iteration_is_in_the_order_things_went_in() {
let mut dict: Dict = (0..50).rev().map(|n| (int(n), Object::int(n))).collect();
assert_eq!(order(&dict), (0..50).rev().collect::<Vec<_>>());
for n in (0..50).step_by(3) {
dict.remove(&int(n));
}
let expected: Vec<i64> = (0..50).rev().filter(|n| n % 3 != 0).collect();
assert_eq!(order(&dict), expected);
dict.insert(int(0), Object::None);
let mut expected = expected;
expected.push(0);
assert_eq!(order(&dict), expected);
}
#[test]
fn writing_over_a_key_leaves_it_where_it_was() {
let mut dict: Dict = (0..5).map(|n| (int(n), Object::int(n))).collect();
let previous = dict.insert(int(1), Object::str("new"));
assert_eq!(previous.expect("there was a value").repr(), "1");
assert_eq!(order(&dict), vec![0, 1, 2, 3, 4]);
assert_eq!(dict.get(&int(1)).expect("present").repr(), "'new'");
assert_eq!(dict.len(), 5);
}
#[test]
fn an_equal_key_does_not_replace_the_one_already_there() {
let mut dict = Dict::new();
dict.insert(int(1), Object::str("a"));
dict.insert(key(Object::Bool(true)), Object::str("b"));
assert_eq!(dict.len(), 1);
assert_eq!(dict.get(&int(1)).expect("present").repr(), "'b'");
let stored = dict.keys().next().expect("one key");
assert_eq!(stored.object().repr(), "1");
}
#[test]
fn the_three_numeric_types_are_one_key() {
let mut dict = Dict::new();
dict.insert(int(1), Object::str("int"));
dict.insert(key(Object::Float(1.0)), Object::str("float"));
dict.insert(key(Object::Bool(true)), Object::str("bool"));
assert_eq!(dict.len(), 1);
assert_eq!(dict.get(&int(1)).expect("present").repr(), "'bool'");
}
#[test]
fn taking_a_key_out_takes_the_value_with_it() {
let mut dict: Dict = (0..5).map(|n| (int(n), Object::int(n))).collect();
assert_eq!(dict.remove(&int(2)).expect("was there").repr(), "2");
assert!(dict.remove(&int(2)).is_none());
assert_eq!(dict.len(), 4);
assert!(!dict.contains(&int(2)));
assert_eq!(order(&dict), vec![0, 1, 3, 4]);
}
#[test]
fn a_key_is_not_lost_when_a_key_before_it_is_deleted() {
let mut dict = Dict::new();
for round in 0..40i64 {
for n in 0..40 {
dict.insert(int(round * 40 + n), Object::int(n));
}
for n in 0..40 {
if (round + n) % 3 == 0 {
dict.remove(&int(round * 40 + n));
}
}
for n in 0..40 {
let n = round * 40 + n;
let present = dict.contains(&int(n));
assert_eq!(present, (n / 40 + n % 40) % 3 != 0, "key {n}");
}
}
}
#[test]
fn a_dict_churned_through_does_not_grow_without_end() {
let mut dict = Dict::new();
for n in 0..10_000 {
dict.insert(int(n), Object::int(n));
dict.remove(&int(n));
assert!(dict.is_empty());
}
dict.insert(int(0), Object::None);
assert_eq!(order(&dict), vec![0]);
assert!(
dict.entries.len() <= MINIMUM,
"entries grew to {}",
dict.entries.len()
);
assert!(
dict.indices.len() <= MINIMUM,
"table grew to {}",
dict.indices.len()
);
}
#[test]
fn a_sliding_window_keeps_what_is_still_in_it() {
let mut dict = Dict::new();
let width = 32;
for n in 0..2_000 {
dict.insert(int(n), Object::int(n));
if n >= width {
assert_eq!(
dict.remove(&int(n - width)).expect("was there").repr(),
(n - width).to_string()
);
}
let live = (n + 1).min(width);
assert_eq!(dict.len(), usize::try_from(live).expect("a small count"));
}
assert_eq!(order(&dict), (2_000 - width..2_000).collect::<Vec<_>>());
assert!(
dict.entries.len() < 200,
"entries grew to {}",
dict.entries.len()
);
}
#[test]
fn clearing_it_leaves_an_empty_dict_that_still_works() {
let mut dict: Dict = (0..20).map(|n| (int(n), Object::int(n))).collect();
dict.clear();
assert!(dict.is_empty());
assert!(!dict.contains(&int(1)));
dict.insert(int(7), Object::None);
assert_eq!(order(&dict), vec![7]);
}
#[test]
fn keys_that_all_collide_still_all_come_back() {
let colliding = |n: i64| key(Object::tuple(vec![Object::int(0), Object::int(n)]));
let mut dict = Dict::new();
for n in 0..50 {
dict.insert(colliding(n), Object::int(n));
}
assert_eq!(dict.len(), 50);
for n in 0..50 {
assert_eq!(
dict.get(&colliding(n)).expect("present").repr(),
n.to_string()
);
}
for n in (0..50).step_by(2) {
assert!(dict.remove(&colliding(n)).is_some());
}
for n in 0..50 {
assert_eq!(dict.contains(&colliding(n)), n % 2 == 1, "key {n}");
}
}
#[test]
fn two_dicts_are_equal_when_they_hold_the_same_thing() {
let a: Dict = (0..5).map(|n| (int(n), Object::int(n))).collect();
let b: Dict = (0..5).rev().map(|n| (int(n), Object::int(n))).collect();
assert!(a.equals(&b));
assert_ne!(order(&a), order(&b));
let c: Dict = (0..4).map(|n| (int(n), Object::int(n))).collect();
assert!(!a.equals(&c));
let d: Dict = (0..5)
.map(|n| (int(i64::from(n)), Object::Float(f64::from(n))))
.collect();
assert!(a.equals(&d));
}
#[test]
fn a_set_holds_each_value_once() {
let mut set = Set::new();
assert!(set.insert(int(1)));
assert!(!set.insert(int(1)));
assert!(!set.insert(key(Object::Float(1.0))));
assert!(set.insert(int(2)));
assert_eq!(set.len(), 2);
assert!(set.contains(&int(1)));
assert!(!set.contains(&int(3)));
assert!(set.remove(&int(1)));
assert!(!set.remove(&int(1)));
assert_eq!(set.len(), 1);
}
#[test]
fn two_sets_are_equal_when_they_hold_the_same_values() {
let a: Set = (0..5).map(int).collect();
let b: Set = (0..5).rev().map(int).collect();
assert!(a.equals(&b));
assert!(!a.equals(&(0..4).map(int).collect()));
assert!(!a.equals(&(1..6).map(int).collect()));
}
}