Skip to main content

luaur_common/records/
dense_hash_map.rs

1//! Faithful port of `Luau::DenseHashMap` — a fast `unordered_map` alternative
2//! that does not support erasing and uses `find()` instead of returning an
3//! iterator. Reference: `luau/Common/include/Luau/DenseHash.h:558-669`.
4//!
5//! Construction takes an `empty_key` sentinel that must never be used as a real
6//! key; this is part of the observable interface (downstream code constructs
7//! `DenseHashMap<K, V>(emptyKey)` at hundreds of sites).
8
9use core::marker::PhantomData;
10
11use crate::records::const_iterator::ConstIterator;
12use crate::records::dense_hash_table::{
13    DenseEq, DenseEqDefault, DenseHashTable, DenseHasher, ItemInterfaceMap,
14};
15use crate::records::iterator::MutIterator;
16use crate::type_aliases::dense_hash_default::DenseHashDefault;
17
18type MapImpl<K, V, H, E> = DenseHashTable<K, (K, V), ItemInterfaceMap<K, V>, H, E>;
19
20pub struct DenseHashMap<K, V, H = DenseHashDefault<K>, E = DenseEqDefault<K>> {
21    pub(crate) impl_: MapImpl<K, V, H, E>,
22}
23
24impl<K: Clone, V: Clone, H: Clone, E: Clone> Clone for DenseHashMap<K, V, H, E> {
25    fn clone(&self) -> Self {
26        DenseHashMap {
27            impl_: self.impl_.clone(),
28        }
29    }
30}
31
32impl<K: core::fmt::Debug, V: core::fmt::Debug, H, E> core::fmt::Debug for DenseHashMap<K, V, H, E> {
33    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
34        f.debug_struct("DenseHashMap")
35            .field("impl_", &self.impl_)
36            .finish()
37    }
38}
39
40impl<K, V, H, E> DenseHashMap<K, V, H, E>
41where
42    K: Clone,
43    V: crate::records::dense_hash_table::DenseDefault,
44    H: DenseHasher<K> + Default,
45    E: DenseEq<K> + Default + Clone,
46{
47    /// `DenseHashMap(empty_key, buckets = 0)`. Reference: `DenseHash.h:570-573`.
48    pub fn new(empty_key: K) -> Self {
49        DenseHashMap {
50            impl_: DenseHashTable::new(empty_key, 0),
51        }
52    }
53
54    /// `operator[]` — inserts a default value when absent and returns a mutable
55    /// reference to the slot's value. Reference: `DenseHash.h:580-585`.
56    pub fn get_or_insert(&mut self, key: K) -> &mut V {
57        self.impl_.rehash_if_full(&key);
58        let idx = self.impl_.insert_unsafe(key);
59        &mut self.impl_.data[idx].1
60    }
61
62    /// `const Value* find(const Key&) const`. Reference: `DenseHash.h:588-593`.
63    pub fn find(&self, key: &K) -> Option<&V> {
64        self.impl_.find(key).map(|idx| &self.impl_.data[idx].1)
65    }
66
67    /// std-style alias for generated Rust that spelled C++ `find` as `get`.
68    pub fn get(&self, key: &K) -> Option<&V> {
69        self.find(key)
70    }
71
72    /// `Value* find(const Key&)`. Reference: `DenseHash.h:596-601`.
73    pub fn find_mut(&mut self, key: &K) -> Option<&mut V> {
74        match self.impl_.find(key) {
75            Some(idx) => Some(&mut self.impl_.data[idx].1),
76            None => None,
77        }
78    }
79
80    /// std-style alias for generated Rust that spelled C++ `find` as `get_mut`.
81    pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
82        self.find_mut(key)
83    }
84
85    /// `contains`. Reference: `DenseHash.h:603-606`.
86    /// std-style alias for `contains` — translations use HashMap idioms.
87    pub fn contains_key(&self, key: &K) -> bool {
88        self.contains(key)
89    }
90
91    pub fn contains(&self, key: &K) -> bool {
92        self.impl_.find(key).is_some()
93    }
94
95    /// `try_insert` — returns `(value_ref, fresh)`, where `fresh` is true only
96    /// when the key was newly inserted; an existing slot keeps its value.
97    /// Reference: `DenseHash.h:608-638`.
98    pub fn try_insert(&mut self, key: K, value: V) -> (&mut V, bool) {
99        self.impl_.rehash_if_full(&key);
100
101        let before = self.impl_.size();
102        let idx = self.impl_.insert_unsafe(key);
103
104        // Value is fresh if container count has increased
105        let fresh = self.impl_.size() > before;
106
107        if fresh {
108            self.impl_.data[idx].1 = value;
109        }
110
111        (&mut self.impl_.data[idx].1, fresh)
112    }
113
114    /// `size`. Reference: `DenseHash.h:640-643`.
115    pub fn size(&self) -> usize {
116        self.impl_.size()
117    }
118
119    /// `empty`. Reference: `DenseHash.h:645-648`.
120    pub fn empty(&self) -> bool {
121        self.impl_.size() == 0
122    }
123
124    /// std-style alias for translated code that calls C++ `empty()` as `is_empty()`.
125    pub fn is_empty(&self) -> bool {
126        self.empty()
127    }
128
129    /// `clear`. Reference: `DenseHash.h:575-578`.
130    pub fn clear(&mut self) {
131        self.impl_.clear();
132    }
133
134    /// `begin()/end()` const iteration, yielding `(&Key, &Value)`.
135    pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
136        ConstIterator {
137            table: &self.impl_,
138            index: self.impl_.first_occupied(),
139        }
140        .map(|item| (&item.0, &item.1))
141    }
142
143    /// `begin()/end()` mutable iteration, yielding `(&Key, &mut Value)`.
144    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
145        let empty_key = self.impl_.empty_key.clone();
146        let eq = self.impl_.eq.clone();
147        MutIterator::<K, (K, V), ItemInterfaceMap<K, V>, E> {
148            inner: self.impl_.data.iter_mut(),
149            empty_key,
150            eq,
151            _iface: PhantomData,
152        }
153        .map(|item| (&item.0, &mut item.1))
154    }
155}