1use crate::hash::Key;
33use crate::object::Object;
34
35const EMPTY: usize = usize::MAX;
37
38const DUMMY: usize = usize::MAX - 1;
41
42const MINIMUM: usize = 8;
44
45#[derive(Debug, Clone)]
47struct Entry {
48 hash: i64,
51 key: Key,
52 value: Object,
53}
54
55#[derive(Debug, Clone, Default)]
57pub struct Dict {
58 indices: Vec<usize>,
61 entries: Vec<Option<Entry>>,
65 used: usize,
67}
68
69struct Walk {
81 mask: usize,
82 slot: usize,
83 perturb: u64,
84}
85
86impl Walk {
87 #[expect(
89 clippy::cast_possible_truncation,
90 clippy::cast_sign_loss,
91 reason = "a hash is a bag of bits here rather than a number, so \
92 dropping the sign or the top half on a 32-bit target costs \
93 a little spread and nothing else"
94 )]
95 fn new(hash: i64, size: usize) -> Self {
96 let perturb = hash as u64;
97 Walk {
98 mask: size - 1,
99 slot: (perturb as usize) & (size - 1),
100 perturb,
101 }
102 }
103}
104
105impl Iterator for Walk {
106 type Item = usize;
107
108 #[expect(
111 clippy::cast_possible_truncation,
112 reason = "the same as in `new`, and for the same reason"
113 )]
114 fn next(&mut self) -> Option<usize> {
115 let slot = self.slot;
116 self.perturb >>= 5;
117 self.slot = slot
118 .wrapping_mul(5)
119 .wrapping_add(self.perturb as usize)
120 .wrapping_add(1)
121 & self.mask;
122 Some(slot)
123 }
124}
125
126enum Probe {
128 Occupied { slot: usize, entry: usize },
130 Vacant { slot: usize },
132}
133
134impl Dict {
135 #[must_use]
137 pub const fn new() -> Self {
138 Dict {
139 indices: Vec::new(),
140 entries: Vec::new(),
141 used: 0,
142 }
143 }
144
145 #[must_use]
147 pub const fn len(&self) -> usize {
148 self.used
149 }
150
151 #[must_use]
153 pub const fn is_empty(&self) -> bool {
154 self.used == 0
155 }
156
157 #[must_use]
159 pub fn get(&self, key: &Key) -> Option<&Object> {
160 match self.probe(key)? {
161 Probe::Occupied { entry, .. } => Some(&self.entry(entry).value),
162 Probe::Vacant { .. } => None,
163 }
164 }
165
166 #[must_use]
168 pub fn contains(&self, key: &Key) -> bool {
169 self.get(key).is_some()
170 }
171
172 pub fn insert(&mut self, key: Key, value: Object) -> Option<Object> {
179 self.reserve();
180 match self
182 .probe(&key)
183 .expect("a table was just made if there was none")
184 {
185 Probe::Occupied { entry, .. } => {
186 Some(std::mem::replace(&mut self.entry_mut(entry).value, value))
187 }
188 Probe::Vacant { slot } => {
189 self.indices[slot] = self.entries.len();
190 self.entries.push(Some(Entry {
191 hash: key.hash(),
192 key,
193 value,
194 }));
195 self.used += 1;
196 None
197 }
198 }
199 }
200
201 pub fn remove(&mut self, key: &Key) -> Option<Object> {
203 match self.probe(key)? {
204 Probe::Occupied { slot, entry } => {
205 self.indices[slot] = DUMMY;
210 let removed = self.entries[entry].take().expect("a live position");
211 self.used -= 1;
212 Some(removed.value)
213 }
214 Probe::Vacant { .. } => None,
215 }
216 }
217
218 pub fn clear(&mut self) {
220 *self = Dict::new();
221 }
222
223 pub fn iter(&self) -> impl Iterator<Item = (&Key, &Object)> {
225 self.entries
226 .iter()
227 .flatten()
228 .map(|entry| (&entry.key, &entry.value))
229 }
230
231 pub fn keys(&self) -> impl Iterator<Item = &Key> {
233 self.iter().map(|(key, _)| key)
234 }
235
236 #[must_use]
245 pub fn entry_at(&self, from: usize) -> Option<(&Key, &Object, usize)> {
246 let mut at = from;
247 while let Some(slot) = self.entries.get(at) {
248 at += 1;
249 if let Some(entry) = slot {
250 return Some((&entry.key, &entry.value, at));
251 }
252 }
253 None
254 }
255
256 #[must_use]
259 pub fn equals(&self, other: &Self) -> bool {
260 self.used == other.used
261 && self
262 .iter()
263 .all(|(key, value)| other.get(key).is_some_and(|found| value.same_value(found)))
264 }
265
266 fn probe(&self, key: &Key) -> Option<Probe> {
273 if self.indices.is_empty() {
274 return None;
275 }
276 let hash = key.hash();
277 let mut reusable = None;
280 for slot in Walk::new(hash, self.indices.len()) {
281 match self.indices[slot] {
282 EMPTY => {
283 return Some(Probe::Vacant {
284 slot: reusable.unwrap_or(slot),
285 });
286 }
287 DUMMY => {
288 if reusable.is_none() {
289 reusable = Some(slot);
290 }
291 }
292 entry => {
293 let candidate = self.entry(entry);
294 if candidate.hash == hash && candidate.key == *key {
297 return Some(Probe::Occupied { slot, entry });
298 }
299 }
300 }
301 }
302 unreachable!("a table is never full, so a walk always reaches an empty slot")
303 }
304
305 fn reserve(&mut self) {
312 if self.indices.is_empty() {
313 self.indices = vec![EMPTY; MINIMUM];
314 return;
315 }
316 if (self.entries.len() + 1) * 3 > self.indices.len() * 2 {
317 self.rebuild();
318 }
319 }
320
321 fn rebuild(&mut self) {
327 let wanted = (self.used + 1).saturating_mul(3).max(MINIMUM);
328 let size = wanted.next_power_of_two();
329 self.entries.retain(Option::is_some);
330
331 let mut indices = vec![EMPTY; size];
332 for (position, entry) in self.entries.iter().enumerate() {
333 let hash = entry.as_ref().expect("the holes were just dropped").hash;
334 let slot = Walk::new(hash, size)
337 .find(|&slot| indices[slot] == EMPTY)
338 .expect("a fresh table has empty slots in it");
339 indices[slot] = position;
340 }
341 self.indices = indices;
342 }
343
344 fn entry(&self, position: usize) -> &Entry {
345 self.entries[position]
346 .as_ref()
347 .expect("a slot only ever points at a live entry")
348 }
349
350 fn entry_mut(&mut self, position: usize) -> &mut Entry {
351 self.entries[position]
352 .as_mut()
353 .expect("a slot only ever points at a live entry")
354 }
355}
356
357impl FromIterator<(Key, Object)> for Dict {
358 fn from_iter<I: IntoIterator<Item = (Key, Object)>>(pairs: I) -> Self {
359 let mut dict = Dict::new();
360 for (key, value) in pairs {
361 dict.insert(key, value);
362 }
363 dict
364 }
365}
366
367#[derive(Debug, Clone, Default)]
373pub struct Set {
374 members: Dict,
375}
376
377impl Set {
378 #[must_use]
380 pub const fn new() -> Self {
381 Set {
382 members: Dict::new(),
383 }
384 }
385
386 #[must_use]
388 pub const fn len(&self) -> usize {
389 self.members.len()
390 }
391
392 #[must_use]
394 pub const fn is_empty(&self) -> bool {
395 self.members.is_empty()
396 }
397
398 #[must_use]
400 pub fn contains(&self, value: &Key) -> bool {
401 self.members.contains(value)
402 }
403
404 pub fn insert(&mut self, value: Key) -> bool {
406 self.members.insert(value, Object::None).is_none()
407 }
408
409 pub fn remove(&mut self, value: &Key) -> bool {
411 self.members.remove(value).is_some()
412 }
413
414 pub fn iter(&self) -> impl Iterator<Item = &Key> {
419 self.members.keys()
420 }
421
422 #[must_use]
426 pub fn member_at(&self, from: usize) -> Option<(&Key, usize)> {
427 self.members
428 .entry_at(from)
429 .map(|(key, _, next)| (key, next))
430 }
431
432 #[must_use]
434 pub fn equals(&self, other: &Self) -> bool {
435 self.len() == other.len() && self.iter().all(|value| other.contains(value))
436 }
437}
438
439impl FromIterator<Key> for Set {
440 fn from_iter<I: IntoIterator<Item = Key>>(values: I) -> Self {
441 let mut set = Set::new();
442 for value in values {
443 set.insert(value);
444 }
445 set
446 }
447}
448
449#[cfg(test)]
450mod tests {
451 use super::*;
452
453 fn key(object: Object) -> Key {
454 Key::new(object).expect("expected this to be hashable")
455 }
456
457 fn int(value: i64) -> Key {
458 key(Object::int(value))
459 }
460
461 fn order(dict: &Dict) -> Vec<i64> {
464 dict.keys()
465 .map(|key| match key.object() {
466 Object::Int(value) => value.to_i64().expect("small enough"),
467 other => panic!("not an integer key: {}", other.repr()),
468 })
469 .collect()
470 }
471
472 #[test]
473 fn an_empty_dict_has_no_table_and_answers_anyway() {
474 let dict = Dict::new();
475 assert_eq!(dict.len(), 0);
476 assert!(dict.is_empty());
477 assert!(dict.get(&int(1)).is_none());
478 assert!(!dict.contains(&int(1)));
479 assert_eq!(order(&dict), Vec::<i64>::new());
480 }
481
482 #[test]
483 fn what_goes_in_comes_back_out() {
484 let mut dict = Dict::new();
485 assert!(dict.insert(int(1), Object::str("a")).is_none());
486 assert!(dict.insert(int(2), Object::str("b")).is_none());
487 assert_eq!(dict.len(), 2);
488 assert_eq!(dict.get(&int(1)).expect("present").repr(), "'a'");
489 assert_eq!(dict.get(&int(2)).expect("present").repr(), "'b'");
490 assert!(dict.get(&int(3)).is_none());
491 }
492
493 #[test]
497 fn iteration_is_in_the_order_things_went_in() {
498 let mut dict: Dict = (0..50).rev().map(|n| (int(n), Object::int(n))).collect();
499 assert_eq!(order(&dict), (0..50).rev().collect::<Vec<_>>());
500
501 for n in (0..50).step_by(3) {
503 dict.remove(&int(n));
504 }
505 let expected: Vec<i64> = (0..50).rev().filter(|n| n % 3 != 0).collect();
506 assert_eq!(order(&dict), expected);
507
508 dict.insert(int(0), Object::None);
510 let mut expected = expected;
511 expected.push(0);
512 assert_eq!(order(&dict), expected);
513 }
514
515 #[test]
517 fn writing_over_a_key_leaves_it_where_it_was() {
518 let mut dict: Dict = (0..5).map(|n| (int(n), Object::int(n))).collect();
519 let previous = dict.insert(int(1), Object::str("new"));
520 assert_eq!(previous.expect("there was a value").repr(), "1");
521 assert_eq!(order(&dict), vec![0, 1, 2, 3, 4]);
522 assert_eq!(dict.get(&int(1)).expect("present").repr(), "'new'");
523 assert_eq!(dict.len(), 5);
524 }
525
526 #[test]
530 fn an_equal_key_does_not_replace_the_one_already_there() {
531 let mut dict = Dict::new();
532 dict.insert(int(1), Object::str("a"));
533 dict.insert(key(Object::Bool(true)), Object::str("b"));
534 assert_eq!(dict.len(), 1);
535 assert_eq!(dict.get(&int(1)).expect("present").repr(), "'b'");
536 let stored = dict.keys().next().expect("one key");
537 assert_eq!(stored.object().repr(), "1");
538 }
539
540 #[test]
541 fn the_three_numeric_types_are_one_key() {
542 let mut dict = Dict::new();
543 dict.insert(int(1), Object::str("int"));
544 dict.insert(key(Object::Float(1.0)), Object::str("float"));
545 dict.insert(key(Object::Bool(true)), Object::str("bool"));
546 assert_eq!(dict.len(), 1);
547 assert_eq!(dict.get(&int(1)).expect("present").repr(), "'bool'");
548 }
549
550 #[test]
551 fn taking_a_key_out_takes_the_value_with_it() {
552 let mut dict: Dict = (0..5).map(|n| (int(n), Object::int(n))).collect();
553 assert_eq!(dict.remove(&int(2)).expect("was there").repr(), "2");
554 assert!(dict.remove(&int(2)).is_none());
555 assert_eq!(dict.len(), 4);
556 assert!(!dict.contains(&int(2)));
557 assert_eq!(order(&dict), vec![0, 1, 3, 4]);
558 }
559
560 #[test]
568 fn a_key_is_not_lost_when_a_key_before_it_is_deleted() {
569 let mut dict = Dict::new();
570 for round in 0..40i64 {
571 for n in 0..40 {
572 dict.insert(int(round * 40 + n), Object::int(n));
573 }
574 for n in 0..40 {
575 if (round + n) % 3 == 0 {
576 dict.remove(&int(round * 40 + n));
577 }
578 }
579 for n in 0..40 {
581 let n = round * 40 + n;
582 let present = dict.contains(&int(n));
583 assert_eq!(present, (n / 40 + n % 40) % 3 != 0, "key {n}");
584 }
585 }
586 }
587
588 #[test]
598 fn a_dict_churned_through_does_not_grow_without_end() {
599 let mut dict = Dict::new();
600 for n in 0..10_000 {
601 dict.insert(int(n), Object::int(n));
602 dict.remove(&int(n));
603 assert!(dict.is_empty());
604 }
605 dict.insert(int(0), Object::None);
606 assert_eq!(order(&dict), vec![0]);
607 assert!(
608 dict.entries.len() <= MINIMUM,
609 "entries grew to {}",
610 dict.entries.len()
611 );
612 assert!(
613 dict.indices.len() <= MINIMUM,
614 "table grew to {}",
615 dict.indices.len()
616 );
617 }
618
619 #[test]
623 fn a_sliding_window_keeps_what_is_still_in_it() {
624 let mut dict = Dict::new();
625 let width = 32;
626 for n in 0..2_000 {
627 dict.insert(int(n), Object::int(n));
628 if n >= width {
629 assert_eq!(
630 dict.remove(&int(n - width)).expect("was there").repr(),
631 (n - width).to_string()
632 );
633 }
634 let live = (n + 1).min(width);
635 assert_eq!(dict.len(), usize::try_from(live).expect("a small count"));
636 }
637 assert_eq!(order(&dict), (2_000 - width..2_000).collect::<Vec<_>>());
638 assert!(
639 dict.entries.len() < 200,
640 "entries grew to {}",
641 dict.entries.len()
642 );
643 }
644
645 #[test]
646 fn clearing_it_leaves_an_empty_dict_that_still_works() {
647 let mut dict: Dict = (0..20).map(|n| (int(n), Object::int(n))).collect();
648 dict.clear();
649 assert!(dict.is_empty());
650 assert!(!dict.contains(&int(1)));
651 dict.insert(int(7), Object::None);
652 assert_eq!(order(&dict), vec![7]);
653 }
654
655 #[test]
658 fn keys_that_all_collide_still_all_come_back() {
659 let colliding = |n: i64| key(Object::tuple(vec![Object::int(0), Object::int(n)]));
662 let mut dict = Dict::new();
663 for n in 0..50 {
664 dict.insert(colliding(n), Object::int(n));
665 }
666 assert_eq!(dict.len(), 50);
667 for n in 0..50 {
668 assert_eq!(
669 dict.get(&colliding(n)).expect("present").repr(),
670 n.to_string()
671 );
672 }
673 for n in (0..50).step_by(2) {
674 assert!(dict.remove(&colliding(n)).is_some());
675 }
676 for n in 0..50 {
677 assert_eq!(dict.contains(&colliding(n)), n % 2 == 1, "key {n}");
678 }
679 }
680
681 #[test]
682 fn two_dicts_are_equal_when_they_hold_the_same_thing() {
683 let a: Dict = (0..5).map(|n| (int(n), Object::int(n))).collect();
684 let b: Dict = (0..5).rev().map(|n| (int(n), Object::int(n))).collect();
685 assert!(a.equals(&b));
687 assert_ne!(order(&a), order(&b));
688
689 let c: Dict = (0..4).map(|n| (int(n), Object::int(n))).collect();
690 assert!(!a.equals(&c));
691
692 let d: Dict = (0..5)
694 .map(|n| (int(i64::from(n)), Object::Float(f64::from(n))))
695 .collect();
696 assert!(a.equals(&d));
697 }
698
699 #[test]
700 fn a_set_holds_each_value_once() {
701 let mut set = Set::new();
702 assert!(set.insert(int(1)));
703 assert!(!set.insert(int(1)));
704 assert!(!set.insert(key(Object::Float(1.0))));
705 assert!(set.insert(int(2)));
706 assert_eq!(set.len(), 2);
707 assert!(set.contains(&int(1)));
708 assert!(!set.contains(&int(3)));
709 assert!(set.remove(&int(1)));
710 assert!(!set.remove(&int(1)));
711 assert_eq!(set.len(), 1);
712 }
713
714 #[test]
715 fn two_sets_are_equal_when_they_hold_the_same_values() {
716 let a: Set = (0..5).map(int).collect();
717 let b: Set = (0..5).rev().map(int).collect();
718 assert!(a.equals(&b));
719 assert!(!a.equals(&(0..4).map(int).collect()));
720 assert!(!a.equals(&(1..6).map(int).collect()));
721 }
722}