dynomite/util/dict.rs
1//! Typed hash-map wrappers over [`ahash::AHashMap`].
2//!
3//! The engine needs a small generic hash map plus a specialized
4//! variant keyed on [`MsgId`] for the in-flight message index. Both
5//! are thin wrappers around [`ahash::AHashMap`] that expose only the
6//! operations the rest of the engine actually uses.
7
8use std::collections::hash_map::IntoIter;
9use std::hash::Hash;
10
11use ahash::AHashMap;
12
13use crate::core::types::MsgId;
14
15/// Generic typed map keyed by `K` with an [`ahash`] hasher.
16///
17/// The wrapper exposes the small set of operations used by the rest
18/// of the engine and intentionally hides the underlying hasher and
19/// table layout. Use [`DictMap::iter`] for read-only traversal and
20/// [`DictMap::drain`] when you need to consume the entire map.
21///
22/// # Examples
23///
24/// ```
25/// use dynomite::util::dict::DictMap;
26///
27/// let mut m: DictMap<&'static str, u32> = DictMap::new();
28/// m.insert("k", 7);
29/// assert_eq!(m.get(&"k"), Some(&7));
30/// assert_eq!(m.len(), 1);
31/// assert!(m.remove(&"k").is_some());
32/// assert!(m.is_empty());
33/// ```
34#[derive(Debug, Default, Clone)]
35pub struct DictMap<K: Eq + Hash, V> {
36 inner: AHashMap<K, V>,
37}
38
39impl<K: Eq + Hash, V> DictMap<K, V> {
40 /// Construct an empty map.
41 ///
42 /// # Examples
43 ///
44 /// ```
45 /// use dynomite::util::dict::DictMap;
46 /// let m: DictMap<u32, u32> = DictMap::new();
47 /// assert!(m.is_empty());
48 /// ```
49 pub fn new() -> Self {
50 Self {
51 inner: AHashMap::new(),
52 }
53 }
54
55 /// Construct an empty map with at least `capacity` slots.
56 ///
57 /// # Examples
58 ///
59 /// ```
60 /// use dynomite::util::dict::DictMap;
61 /// let m: DictMap<u32, u32> = DictMap::with_capacity(8);
62 /// assert!(m.is_empty());
63 /// ```
64 pub fn with_capacity(capacity: usize) -> Self {
65 Self {
66 inner: AHashMap::with_capacity(capacity),
67 }
68 }
69
70 /// Insert `value` at `key`, returning the previous value (if any).
71 ///
72 /// # Examples
73 ///
74 /// ```
75 /// use dynomite::util::dict::DictMap;
76 /// let mut m: DictMap<u32, u32> = DictMap::new();
77 /// assert_eq!(m.insert(1, 10), None);
78 /// assert_eq!(m.insert(1, 20), Some(10));
79 /// ```
80 pub fn insert(&mut self, key: K, value: V) -> Option<V> {
81 self.inner.insert(key, value)
82 }
83
84 /// Remove and return the value at `key`.
85 ///
86 /// # Examples
87 ///
88 /// ```
89 /// use dynomite::util::dict::DictMap;
90 /// let mut m: DictMap<u32, u32> = DictMap::new();
91 /// m.insert(1, 10);
92 /// assert_eq!(m.remove(&1), Some(10));
93 /// assert_eq!(m.remove(&1), None);
94 /// ```
95 pub fn remove(&mut self, key: &K) -> Option<V> {
96 self.inner.remove(key)
97 }
98
99 /// Look up an immutable reference to the value at `key`.
100 ///
101 /// # Examples
102 ///
103 /// ```
104 /// use dynomite::util::dict::DictMap;
105 /// let mut m: DictMap<u32, u32> = DictMap::new();
106 /// m.insert(1, 10);
107 /// assert_eq!(m.get(&1), Some(&10));
108 /// ```
109 pub fn get(&self, key: &K) -> Option<&V> {
110 self.inner.get(key)
111 }
112
113 /// Look up a mutable reference to the value at `key`.
114 ///
115 /// # Examples
116 ///
117 /// ```
118 /// use dynomite::util::dict::DictMap;
119 /// let mut m: DictMap<u32, u32> = DictMap::new();
120 /// m.insert(1, 10);
121 /// *m.get_mut(&1).unwrap() = 20;
122 /// assert_eq!(m.get(&1), Some(&20));
123 /// ```
124 pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
125 self.inner.get_mut(key)
126 }
127
128 /// Whether `key` is in the map.
129 ///
130 /// # Examples
131 ///
132 /// ```
133 /// use dynomite::util::dict::DictMap;
134 /// let mut m: DictMap<u32, u32> = DictMap::new();
135 /// m.insert(1, 10);
136 /// assert!(m.contains_key(&1));
137 /// assert!(!m.contains_key(&2));
138 /// ```
139 pub fn contains_key(&self, key: &K) -> bool {
140 self.inner.contains_key(key)
141 }
142
143 /// Number of entries.
144 ///
145 /// # Examples
146 ///
147 /// ```
148 /// use dynomite::util::dict::DictMap;
149 /// let mut m: DictMap<u32, u32> = DictMap::new();
150 /// m.insert(1, 10);
151 /// assert_eq!(m.len(), 1);
152 /// ```
153 pub fn len(&self) -> usize {
154 self.inner.len()
155 }
156
157 /// Whether the map is empty.
158 ///
159 /// # Examples
160 ///
161 /// ```
162 /// use dynomite::util::dict::DictMap;
163 /// let m: DictMap<u32, u32> = DictMap::new();
164 /// assert!(m.is_empty());
165 /// ```
166 pub fn is_empty(&self) -> bool {
167 self.inner.is_empty()
168 }
169
170 /// Drop every entry.
171 ///
172 /// # Examples
173 ///
174 /// ```
175 /// use dynomite::util::dict::DictMap;
176 /// let mut m: DictMap<u32, u32> = DictMap::new();
177 /// m.insert(1, 10);
178 /// m.clear();
179 /// assert!(m.is_empty());
180 /// ```
181 pub fn clear(&mut self) {
182 self.inner.clear();
183 }
184
185 /// Borrowed iterator over `(key, value)` pairs.
186 ///
187 /// # Examples
188 ///
189 /// ```
190 /// use dynomite::util::dict::DictMap;
191 /// let mut m: DictMap<u32, u32> = DictMap::new();
192 /// m.insert(1, 10);
193 /// m.insert(2, 20);
194 /// let mut sum = 0u32;
195 /// for (_k, v) in m.iter() { sum += v; }
196 /// assert_eq!(sum, 30);
197 /// ```
198 pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
199 self.inner.iter()
200 }
201
202 /// Mutable iterator over `(key, value)` pairs.
203 ///
204 /// # Examples
205 ///
206 /// ```
207 /// use dynomite::util::dict::DictMap;
208 /// let mut m: DictMap<u32, u32> = DictMap::new();
209 /// m.insert(1, 10);
210 /// for (_k, v) in m.iter_mut() { *v += 1; }
211 /// assert_eq!(m.get(&1), Some(&11));
212 /// ```
213 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
214 self.inner.iter_mut()
215 }
216
217 /// Consume the map and yield `(key, value)` pairs.
218 ///
219 /// # Examples
220 ///
221 /// ```
222 /// use dynomite::util::dict::DictMap;
223 /// let mut m: DictMap<u32, u32> = DictMap::new();
224 /// m.insert(1, 10);
225 /// let drained: Vec<_> = m.drain().collect();
226 /// assert_eq!(drained, vec![(1, 10)]);
227 /// assert!(m.is_empty());
228 /// ```
229 pub fn drain(&mut self) -> impl Iterator<Item = (K, V)> + '_ {
230 self.inner.drain()
231 }
232}
233
234impl<K: Eq + Hash, V> IntoIterator for DictMap<K, V> {
235 type Item = (K, V);
236 type IntoIter = IntoIter<K, V>;
237 fn into_iter(self) -> Self::IntoIter {
238 self.inner.into_iter()
239 }
240}
241
242/// Specialization keyed on [`MsgId`] for the in-flight message index.
243///
244/// # Examples
245///
246/// ```
247/// use dynomite::util::dict::MsgIndex;
248///
249/// let mut idx: MsgIndex<&'static str> = MsgIndex::new();
250/// idx.insert(42, "hello");
251/// assert_eq!(idx.get(&42), Some(&"hello"));
252/// ```
253pub type MsgIndex<V> = DictMap<MsgId, V>;
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 #[test]
260 fn insert_get_remove_round_trip() {
261 let mut m: DictMap<u64, &'static str> = DictMap::new();
262 assert!(m.is_empty());
263 m.insert(1, "one");
264 m.insert(2, "two");
265 assert_eq!(m.len(), 2);
266 assert_eq!(m.get(&1), Some(&"one"));
267 assert!(m.contains_key(&2));
268 assert_eq!(m.remove(&1), Some("one"));
269 assert_eq!(m.remove(&1), None);
270 assert_eq!(m.len(), 1);
271 m.clear();
272 assert!(m.is_empty());
273 }
274
275 #[test]
276 fn msg_index_alias_resolves() {
277 let mut idx: MsgIndex<u32> = MsgIndex::new();
278 idx.insert(7, 700);
279 assert_eq!(idx.get(&7), Some(&700));
280 }
281
282 #[test]
283 fn iter_visits_every_entry() {
284 let mut m: DictMap<u32, u32> = DictMap::with_capacity(4);
285 for i in 0..4 {
286 m.insert(i, i * i);
287 }
288 let mut seen: Vec<(u32, u32)> = m.iter().map(|(k, v)| (*k, *v)).collect();
289 seen.sort_unstable();
290 assert_eq!(seen, vec![(0, 0), (1, 1), (2, 4), (3, 9)]);
291 }
292}