1use crate::hash::DefaultHashBuilder;
4use crate::reentrancy::DebugReentrancy;
5use core::borrow::Borrow;
6use core::hash::{BuildHasher, Hash};
7use hashbrown::HashTable;
8use slotmap::{DefaultKey, SlotMap};
9
10#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
11pub struct Handle(DefaultKey);
12
13impl Handle {
14 pub(crate) fn new(k: DefaultKey) -> Self {
15 Handle(k)
16 }
17 pub(crate) fn raw_handle(&self) -> DefaultKey {
18 self.0
19 }
20
21 pub fn key<'a, K, V, S>(&self, map: &'a HandleHashMap<K, V, S>) -> Option<&'a K>
22 where
23 K: Eq + Hash,
24 S: BuildHasher + Clone + Default,
25 {
26 map.handle_key(*self)
27 }
28
29 pub fn value<'a, K, V, S>(&self, map: &'a HandleHashMap<K, V, S>) -> Option<&'a V>
30 where
31 K: Eq + Hash,
32 S: BuildHasher + Clone + Default,
33 {
34 map.handle_value(*self)
35 }
36
37 pub fn value_mut<'a, K, V, S>(&self, map: &'a mut HandleHashMap<K, V, S>) -> Option<&'a mut V>
38 where
39 K: Eq + Hash,
40 S: BuildHasher + Clone + Default,
41 {
42 map.handle_value_mut(*self)
43 }
44}
45
46#[derive(Debug)]
47struct Entry<K, V> {
48 key: K,
49 value: V,
50 hash: u64,
51}
52
53pub struct HandleHashMap<K, V, S = DefaultHashBuilder> {
54 hasher: S,
55 index: HashTable<DefaultKey>,
56 slots: SlotMap<DefaultKey, Entry<K, V>>, reentrancy: DebugReentrancy,
58}
59
60#[derive(Debug)]
61pub enum InsertError {
62 DuplicateKey,
63}
64
65impl<K, V> HandleHashMap<K, V>
66where
67 K: Eq + Hash,
68{
69 pub fn new() -> Self {
70 Self::with_hasher(Default::default())
71 }
72}
73
74impl<K, V> Default for HandleHashMap<K, V>
75where
76 K: Eq + Hash,
77{
78 fn default() -> Self {
79 Self::new()
80 }
81}
82
83pub struct Iter<'a, K, V, S> {
85 it: slotmap::basic::Iter<'a, DefaultKey, Entry<K, V>>,
86 pub(crate) _pd: core::marker::PhantomData<&'a (K, V, S)>,
87}
88
89impl<'a, K, V, S> Iterator for Iter<'a, K, V, S> {
90 type Item = (Handle, &'a K, &'a V);
91 #[inline]
92 fn next(&mut self) -> Option<Self::Item> {
93 self.it
94 .next()
95 .map(|(k, e)| (Handle::new(k), &e.key, &e.value))
96 }
97}
98
99pub struct IterMut<'a, K, V, S> {
101 it: slotmap::basic::IterMut<'a, DefaultKey, Entry<K, V>>,
102 pub(crate) _pd: core::marker::PhantomData<&'a (K, V, S)>,
103}
104
105impl<'a, K, V, S> Iterator for IterMut<'a, K, V, S> {
106 type Item = (Handle, &'a K, &'a mut V);
107 #[inline]
108 fn next(&mut self) -> Option<Self::Item> {
109 self.it
110 .next()
111 .map(|(k, e)| (Handle::new(k), &e.key, &mut e.value))
112 }
113}
114
115impl<K, V, S> HandleHashMap<K, V, S>
116where
117 K: Eq + Hash,
118 S: BuildHasher + Clone + Default,
119{
120 pub fn with_hasher(hasher: S) -> Self {
121 Self {
122 index: HashTable::new(),
123 hasher,
124 slots: SlotMap::with_key(),
125 reentrancy: DebugReentrancy::new(),
126 }
127 }
128
129 fn make_hash<Q>(&self, q: &Q) -> u64
130 where
131 Q: ?Sized + Hash,
132 {
133 self.hasher.hash_one(q)
134 }
135
136 pub fn len(&self) -> usize {
137 self.slots.len()
138 }
139 pub fn is_empty(&self) -> bool {
140 self.slots.is_empty()
141 }
142
143 pub fn find<Q>(&self, q: &Q) -> Option<Handle>
144 where
145 K: Borrow<Q>,
146 Q: ?Sized + Hash + Eq,
147 {
148 let _g = self.reentrancy.enter();
149 let hash = self.make_hash(q);
150 if let Some(&k) = self.index.find(hash, |&k| {
151 self.slots
152 .get(k)
153 .map(|e| e.key.borrow() == q)
154 .unwrap_or(false)
155 }) {
156 return Some(Handle::new(k));
157 }
158 None
159 }
160
161 pub fn contains_key<Q>(&self, q: &Q) -> bool
162 where
163 K: Borrow<Q>,
164 Q: ?Sized + Hash + Eq,
165 {
166 let _g = self.reentrancy.enter();
167 let hash = self.make_hash(q);
168 self.index
169 .find(hash, |&k| {
170 self.slots
171 .get(k)
172 .map(|e| e.key.borrow() == q)
173 .unwrap_or(false)
174 })
175 .is_some()
176 }
177
178 pub fn insert(&mut self, key: K, value: V) -> Result<Handle, InsertError> {
179 let _g = self.reentrancy.enter();
180 let hash = self.make_hash(&key);
181 let entry = Entry { key, value, hash };
182 match self.index.entry(
184 hash,
185 |&kk| {
186 self.slots
187 .get(kk)
188 .map(|e| e.key == entry.key)
189 .unwrap_or(false)
190 },
191 |&kk| self.slots.get(kk).map(|e| e.hash).unwrap_or(0),
192 ) {
193 hashbrown::hash_table::Entry::Occupied(_) => Err(InsertError::DuplicateKey),
194 hashbrown::hash_table::Entry::Vacant(v) => {
195 let k = self.slots.insert(entry);
196 let _ = v.insert(k);
197 Ok(Handle::new(k))
198 }
199 }
200 }
201
202 pub fn insert_with<F>(&mut self, key: K, default: F) -> Result<Handle, InsertError>
203 where
204 F: FnOnce() -> V,
205 {
206 let _g = self.reentrancy.enter();
207 let hash = self.make_hash(&key);
208 match self.index.entry(
209 hash,
210 |&kk| self.slots.get(kk).map(|e| e.key == key).unwrap_or(false),
211 |&kk| self.slots.get(kk).map(|e| e.hash).unwrap_or(0),
212 ) {
213 hashbrown::hash_table::Entry::Occupied(_) => Err(InsertError::DuplicateKey),
214 hashbrown::hash_table::Entry::Vacant(v) => {
215 let value = default();
216 let entry = Entry { key, value, hash };
217 let k = self.slots.insert(entry);
218 let _ = v.insert(k);
219 Ok(Handle::new(k))
220 }
221 }
222 }
223
224 pub fn remove(&mut self, handle: Handle) -> Option<(K, V)> {
225 let _g = self.reentrancy.enter();
226 let k = handle.raw_handle();
227
228 let entry = self.slots.remove(k)?;
230
231 self.index
233 .find_entry(entry.hash, |&kk| kk == k)
234 .unwrap()
235 .remove();
236
237 Some((entry.key, entry.value))
238 }
239
240 pub(crate) fn handle_key(&self, h: Handle) -> Option<&K> {
241 let _g = self.reentrancy.enter();
242 self.slots.get(h.raw_handle()).map(|e| &e.key)
243 }
244
245 pub(crate) fn handle_value(&self, h: Handle) -> Option<&V> {
246 let _g = self.reentrancy.enter();
247 self.slots.get(h.raw_handle()).map(|e| &e.value)
248 }
249
250 pub(crate) fn handle_value_mut(&mut self, h: Handle) -> Option<&mut V> {
251 let _g = self.reentrancy.enter();
252 self.slots.get_mut(h.raw_handle()).map(|e| &mut e.value)
253 }
254
255 pub fn iter(&self) -> Iter<'_, K, V, S> {
256 let it = self.slots.iter();
257 Iter {
258 it,
259 _pd: core::marker::PhantomData,
260 }
261 }
262
263 pub fn iter_mut(&mut self) -> IterMut<'_, K, V, S> {
264 let it = self.slots.iter_mut();
265 IterMut {
266 it,
267 _pd: core::marker::PhantomData,
268 }
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275 use std::cell::Cell;
276 use std::collections::BTreeSet;
277 use std::hash::Hasher;
278
279 #[test]
281 fn duplicate_insert_rejected() {
282 let mut m: HandleHashMap<String, i32> = HandleHashMap::new();
283 let handle = m.insert("dup".to_string(), 1).unwrap();
284 match m.insert("dup".to_string(), 2) {
285 Err(InsertError::DuplicateKey) => {}
286 other => panic!("unexpected result: {:?}", other),
287 }
288 assert_eq!(*handle.value(&m).unwrap(), 1);
289 assert_eq!(m.len(), 1);
290 }
291
292 #[test]
294 fn find_contains_parity() {
295 let mut m: HandleHashMap<String, i32> = HandleHashMap::new();
296 let present = ["a", "b", "c"];
297 for (i, k) in present.iter().enumerate() {
298 m.insert((*k).to_string(), i as i32).unwrap();
299 }
300
301 for k in present {
302 let s = k.to_string();
303 assert!(m.find(&s).is_some());
304 assert!(m.contains_key(&s));
305 }
306
307 for k in ["x", "y", "z"] {
308 let s = k.to_string();
309 assert!(m.find(&s).is_none());
310 assert!(!m.contains_key(&s));
311 }
312 }
313
314 #[test]
316 fn borrowed_lookup_with_str() {
317 let mut m: HandleHashMap<String, i32> = HandleHashMap::new();
318 m.insert("hello".to_string(), 1).unwrap();
319 assert!(m.contains_key("hello"));
320 assert!(!m.contains_key("world"));
321
322 assert!(m.find("hello").is_some());
324 assert!(m.find("world").is_none());
325 }
326
327 #[test]
330 fn handle_access_and_mutation() {
331 let mut m: HandleHashMap<String, i32> = HandleHashMap::new();
332 let h = m.insert("k1".to_string(), 10).unwrap();
333 assert_eq!(h.key(&m), Some(&"k1".to_string()));
334 assert_eq!(h.value(&m), Some(&10));
335 let new_val = h
336 .value_mut(&mut m)
337 .map(|v| {
338 *v += 5;
339 *v
340 })
341 .unwrap();
342 assert_eq!(new_val, 15);
343 assert_eq!(h.value(&m), Some(&15));
344
345 let (_k, _v) = m.remove(h).unwrap();
346 assert!(h.value(&m).is_none());
347 }
348
349 #[test]
352 fn stale_handle_does_not_alias_new_entry() {
353 let mut m: HandleHashMap<String, i32> = HandleHashMap::new();
354 let h1 = m.insert("old".to_string(), 1).unwrap();
355 let (_k, _v) = m.remove(h1).unwrap();
356 let h2 = m.insert("new".to_string(), 2).unwrap();
358 assert_ne!(h1, h2, "handles must differ across generations");
359 assert!(h1.value(&m).is_none(), "stale handle must not resolve");
360 assert!(m.contains_key("new"));
361 assert!(!m.contains_key("old"));
362 }
363
364 #[test]
367 fn iteration_and_mutation() {
368 let mut m: HandleHashMap<String, i32> = HandleHashMap::new();
369 let keys = ["k1", "k2", "k3"];
370 for (i, k) in keys.iter().enumerate() {
371 m.insert((*k).to_string(), i as i32).unwrap();
372 }
373
374 let seen: BTreeSet<String> = m.iter().map(|(_h, k, _v)| k.clone()).collect();
375 let expected: BTreeSet<String> = keys.iter().map(|s| (*s).to_string()).collect();
376 assert_eq!(seen, expected);
377
378 for (_h, _k, v) in m.iter_mut() {
379 *v += 10;
380 }
381 for k in keys {
382 let h = m.find(&k.to_string()).unwrap();
383 assert_eq!(
384 h.value(&m),
385 Some(&match k {
386 "k1" => 10,
387 "k2" => 11,
388 "k3" => 12,
389 _ => unreachable!(),
390 })
391 );
392 }
393 }
394
395 #[test]
398 fn collision_handling_with_const_hasher() {
399 #[derive(Clone, Default)]
400 struct ConstBuildHasher;
401 struct ConstHasher;
402 impl BuildHasher for ConstBuildHasher {
403 type Hasher = ConstHasher;
404 fn build_hasher(&self) -> Self::Hasher {
405 ConstHasher
406 }
407 }
408 impl core::hash::Hasher for ConstHasher {
409 fn write(&mut self, _bytes: &[u8]) {}
410 fn finish(&self) -> u64 {
411 0
412 } }
414
415 let mut m: HandleHashMap<String, i32, ConstBuildHasher> =
416 HandleHashMap::with_hasher(ConstBuildHasher);
417 m.insert("a".to_string(), 1).unwrap();
418 m.insert("b".to_string(), 2).unwrap();
419
420 let ha = m.find(&"a".to_string()).expect("find a");
421 let hb = m.find(&"b".to_string()).expect("find b");
422 assert_ne!(ha, hb);
423 assert_eq!(ha.key(&m), Some(&"a".to_string()));
424 assert_eq!(hb.key(&m), Some(&"b".to_string()));
425 }
426
427 #[test]
430 fn remove_then_reinsert_same_key_yields_new_value() {
431 let mut m: HandleHashMap<String, i32> = HandleHashMap::new();
432 let h1 = m.insert("k".to_string(), 1).unwrap();
433
434 let (k_removed, v_removed) = m.remove(h1).expect("present for removal");
436 assert_eq!(k_removed, "k");
437 assert_eq!(v_removed, 1);
438 assert!(!m.contains_key("k"));
439 assert!(m.find(&"k".to_string()).is_none());
440 assert!(h1.value(&m).is_none());
441
442 let h2 = m.insert("k".to_string(), 2).expect("reinsert allowed");
444 assert!(m.contains_key("k"));
445 let hf = m.find(&"k".to_string()).expect("find reinserted key");
446 assert_eq!(hf.value(&m), Some(&2));
447 assert_eq!(h2.value(&m), Some(&2));
448 assert_ne!(h1, h2, "old handle must not alias new entry");
449 assert!(h1.value(&m).is_none(), "stale handle stays invalid");
450 }
451
452 #[cfg(debug_assertions)]
455 #[test]
456 fn reentrancy_panics_from_eq_during_find() {
457 #[derive(Clone, Default)]
458 struct ConstBuildHasher;
459 struct ConstHasher;
460 impl BuildHasher for ConstBuildHasher {
461 type Hasher = ConstHasher;
462 fn build_hasher(&self) -> Self::Hasher {
463 ConstHasher
464 }
465 }
466 impl core::hash::Hasher for ConstHasher {
467 fn write(&mut self, _bytes: &[u8]) {}
468 fn finish(&self) -> u64 {
469 0
470 }
471 }
472
473 struct ReentryKey {
474 id: &'static str,
475 map: *const HandleHashMap<ReentryKey, i32, ConstBuildHasher>,
476 trigger: bool,
477 }
478 impl core::fmt::Debug for ReentryKey {
479 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
480 f.write_str(self.id)
481 }
482 }
483 impl PartialEq for ReentryKey {
484 fn eq(&self, other: &Self) -> bool {
485 if self.id == other.id {
486 return true;
487 }
488 if other.trigger {
489 unsafe {
491 let m = &*other.map;
492 let _ = m.contains_key(self.id);
493 }
494 }
495 false
496 }
497 }
498 impl Eq for ReentryKey {}
499 impl Hash for ReentryKey {
500 fn hash<H: Hasher>(&self, state: &mut H) {
501 self.id.hash(state);
502 }
503 }
504 impl core::borrow::Borrow<str> for ReentryKey {
505 fn borrow(&self) -> &str {
506 self.id
507 }
508 }
509
510 let mut m: HandleHashMap<ReentryKey, i32, ConstBuildHasher> =
511 HandleHashMap::with_hasher(ConstBuildHasher);
512 let key = ReentryKey {
513 id: "a",
514 map: core::ptr::null(),
515 trigger: false,
516 };
517 let key = ReentryKey {
519 map: &m as *const _,
520 ..key
521 };
522 m.insert(key, 1).unwrap();
523
524 let query = ReentryKey {
525 id: "b",
526 map: &m as *const _,
527 trigger: true,
528 };
529 let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
530 let _ = m.find(&query);
531 }));
532 assert!(res.is_err(), "expected reentrancy to panic in debug builds");
533 }
534
535 #[test]
538 fn insert_with_is_lazy_and_deduplicates() {
539 let mut m: HandleHashMap<String, String> = HandleHashMap::new();
540 let calls = Cell::new(0);
541
542 let r = m.insert_with("k".to_string(), || {
543 calls.set(calls.get() + 1);
544 "v".to_string()
545 });
546 assert!(r.is_ok());
547 assert_eq!(calls.get(), 1);
548
549 let r2 = m.insert_with("k".to_string(), || {
551 calls.set(calls.get() + 1);
552 "v2".to_string()
553 });
554 match r2 {
555 Err(InsertError::DuplicateKey) => {}
556 other => panic!("unexpected result: {:?}", other),
557 }
558 assert_eq!(calls.get(), 1, "default() must not run on duplicate");
559
560 let h = m.find(&"k".to_string()).unwrap();
562 assert_eq!(h.value(&m), Some(&"v".to_string()));
563 }
564
565 #[test]
568 fn insert_with_value_equivalence() {
569 let mut m1: HandleHashMap<&'static str, i32> = HandleHashMap::new();
570 let mut m2: HandleHashMap<&'static str, i32> = HandleHashMap::new();
571
572 let h1 = m1.insert("a", 1).unwrap();
573 let h2 = m2.insert_with("a", || 1).unwrap();
574
575 assert!(m1.contains_key(&"a"));
576 assert!(m2.contains_key(&"a"));
577 assert_eq!(h1.value(&m1), h2.value(&m2));
578
579 assert!(m1.insert_with("a", || 2).is_err());
581 assert!(m2.insert_with("a", || 3).is_err());
582 }
583
584 #[test]
587 fn handles_alias_same_entry_between_insert_and_find() {
588 let mut m: HandleHashMap<String, i32> = HandleHashMap::new();
589 let h_insert = m.insert("k".to_string(), 10).unwrap();
590
591 let h_find = m.find("k").expect("key present");
593
594 assert_eq!(h_insert, h_find);
596
597 *h_insert.value_mut(&mut m).expect("value_mut present") = 20;
599 assert_eq!(h_find.value(&m), Some(&20));
600
601 *h_find.value_mut(&mut m).expect("value_mut present") = 30;
603 assert_eq!(h_insert.value(&m), Some(&30));
604 }
605
606 #[test]
609 fn len_and_is_empty_behaviors() {
610 let mut m: HandleHashMap<String, i32> = HandleHashMap::new();
611 assert_eq!(m.len(), 0);
612 assert!(m.is_empty());
613
614 let h1 = m.insert("a".to_string(), 1).unwrap();
615 assert_eq!(m.len(), 1);
616 assert!(!m.is_empty());
617
618 match m.insert("a".to_string(), 2) {
620 Err(InsertError::DuplicateKey) => {}
621 other => panic!("unexpected result: {:?}", other),
622 }
623 assert_eq!(m.len(), 1);
624 assert!(!m.is_empty());
625
626 let h2 = m.insert("b".to_string(), 2).unwrap();
627 assert_eq!(m.len(), 2);
628 assert!(!m.is_empty());
629
630 let _ = m.remove(h1).unwrap();
631 assert_eq!(m.len(), 1);
632 assert!(!m.is_empty());
633
634 let _ = m.remove(h2).unwrap();
635 assert_eq!(m.len(), 0);
636 assert!(m.is_empty());
637 }
638}