1747ad3c4Slazypassion //! `ScopedHashMap`
2747ad3c4Slazypassion //!
382c0a09bSChris Fallin //! This module defines a struct `ScopedHashMap<C, K, V>` which
482c0a09bSChris Fallin //! defines a `FxHashMap`-like container that has a concept of scopes
582c0a09bSChris Fallin //! that can be entered and exited, such that values inserted while
682c0a09bSChris Fallin //! inside a scope aren't visible outside the scope.
782c0a09bSChris Fallin //!
882c0a09bSChris Fallin //! The context type `C` is given to `CtxEq` and `CtxHash` methods on
982c0a09bSChris Fallin //! the key values so that keys do not need to be fully
1082c0a09bSChris Fallin //! self-contained.
11747ad3c4Slazypassion 
1282c0a09bSChris Fallin use crate::ctxhash::{CtxEq, CtxHash, CtxHashMap};
1390ac295eSAlex Crichton use smallvec::{SmallVec, smallvec};
14747ad3c4Slazypassion 
152be12a51SChris Fallin struct Val<V> {
16747ad3c4Slazypassion     value: V,
172be12a51SChris Fallin     level: u32,
182be12a51SChris Fallin     generation: u32,
19747ad3c4Slazypassion }
20747ad3c4Slazypassion 
21747ad3c4Slazypassion /// A view into an occupied entry in a `ScopedHashMap`. It is part of the `Entry` enum.
22747ad3c4Slazypassion pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
2382c0a09bSChris Fallin     entry: crate::ctxhash::OccupiedEntry<'a, K, Val<V>>,
24747ad3c4Slazypassion }
25747ad3c4Slazypassion 
26747ad3c4Slazypassion impl<'a, K, V> OccupiedEntry<'a, K, V> {
27747ad3c4Slazypassion     /// Gets a reference to the value in the entry.
get(&self) -> &V28747ad3c4Slazypassion     pub fn get(&self) -> &V {
29747ad3c4Slazypassion         &self.entry.get().value
30747ad3c4Slazypassion     }
31747ad3c4Slazypassion }
32747ad3c4Slazypassion 
33747ad3c4Slazypassion /// A view into a vacant entry in a `ScopedHashMap`. It is part of the `Entry` enum.
34747ad3c4Slazypassion pub struct VacantEntry<'a, K: 'a, V: 'a> {
352be12a51SChris Fallin     entry: InsertLoc<'a, K, V>,
362be12a51SChris Fallin     depth: u32,
372be12a51SChris Fallin     generation: u32,
38747ad3c4Slazypassion }
39747ad3c4Slazypassion 
402be12a51SChris Fallin /// Where to insert from a `VacantEntry`. May be vacant or occupied in
412be12a51SChris Fallin /// the underlying map because of lazy (generation-based) deletion.
422be12a51SChris Fallin enum InsertLoc<'a, K: 'a, V: 'a> {
4382c0a09bSChris Fallin     Vacant(crate::ctxhash::VacantEntry<'a, K, Val<V>>),
4482c0a09bSChris Fallin     Occupied(crate::ctxhash::OccupiedEntry<'a, K, Val<V>>),
452be12a51SChris Fallin }
462be12a51SChris Fallin 
472be12a51SChris Fallin impl<'a, K, V> VacantEntry<'a, K, V> {
48747ad3c4Slazypassion     /// Sets the value of the entry with the `VacantEntry`'s key.
insert(self, value: V)49747ad3c4Slazypassion     pub fn insert(self, value: V) {
502be12a51SChris Fallin         let val = Val {
51747ad3c4Slazypassion             value,
522be12a51SChris Fallin             level: self.depth,
532be12a51SChris Fallin             generation: self.generation,
542be12a51SChris Fallin         };
552be12a51SChris Fallin         match self.entry {
562be12a51SChris Fallin             InsertLoc::Vacant(v) => {
572be12a51SChris Fallin                 v.insert(val);
582be12a51SChris Fallin             }
592be12a51SChris Fallin             InsertLoc::Occupied(mut o) => {
6082c0a09bSChris Fallin                 *o.get_mut() = val;
612be12a51SChris Fallin             }
622be12a51SChris Fallin         }
63747ad3c4Slazypassion     }
64747ad3c4Slazypassion }
65747ad3c4Slazypassion 
66747ad3c4Slazypassion /// A view into a single entry in a map, which may either be vacant or occupied.
67747ad3c4Slazypassion ///
68747ad3c4Slazypassion /// This enum is constructed from the `entry` method on `ScopedHashMap`.
69747ad3c4Slazypassion pub enum Entry<'a, K: 'a, V: 'a> {
70747ad3c4Slazypassion     Occupied(OccupiedEntry<'a, K, V>),
71747ad3c4Slazypassion     Vacant(VacantEntry<'a, K, V>),
72747ad3c4Slazypassion }
73747ad3c4Slazypassion 
74747ad3c4Slazypassion /// A wrapper around a `FxHashMap` which adds the concept of scopes. Items inserted
75747ad3c4Slazypassion /// within a scope are removed when the scope is exited.
76747ad3c4Slazypassion ///
77747ad3c4Slazypassion /// Shadowing, where one scope has entries with the same keys as a containing scope,
78747ad3c4Slazypassion /// is not supported in this implementation.
79747ad3c4Slazypassion pub struct ScopedHashMap<K, V> {
8082c0a09bSChris Fallin     map: CtxHashMap<K, Val<V>>,
812be12a51SChris Fallin     generation_by_depth: SmallVec<[u32; 8]>,
822be12a51SChris Fallin     generation: u32,
83747ad3c4Slazypassion }
84747ad3c4Slazypassion 
85747ad3c4Slazypassion impl<K, V> ScopedHashMap<K, V>
86747ad3c4Slazypassion where
8782c0a09bSChris Fallin     K: Clone,
88747ad3c4Slazypassion {
89747ad3c4Slazypassion     /// Creates an empty `ScopedHashMap`.
90*099102d9SAlex Crichton     #[cfg(test)]
new() -> Self91747ad3c4Slazypassion     pub fn new() -> Self {
9282c0a09bSChris Fallin         Self::with_capacity(16)
932be12a51SChris Fallin     }
942be12a51SChris Fallin 
952be12a51SChris Fallin     /// Creates an empty `ScopedHashMap` with some pre-allocated capacity.
with_capacity(cap: usize) -> Self962be12a51SChris Fallin     pub fn with_capacity(cap: usize) -> Self {
972be12a51SChris Fallin         Self {
9882c0a09bSChris Fallin             map: CtxHashMap::with_capacity(cap),
992be12a51SChris Fallin             generation: 0,
1002be12a51SChris Fallin             generation_by_depth: smallvec![0],
101747ad3c4Slazypassion         }
102747ad3c4Slazypassion     }
103747ad3c4Slazypassion 
104747ad3c4Slazypassion     /// Similar to `FxHashMap::entry`, gets the given key's corresponding entry in the map for
105747ad3c4Slazypassion     /// in-place manipulation.
entry<'a, C>(&'a mut self, ctx: &C, key: K) -> Entry<'a, K, V> where C: CtxEq<K, K> + CtxHash<K>,10682c0a09bSChris Fallin     pub fn entry<'a, C>(&'a mut self, ctx: &C, key: K) -> Entry<'a, K, V>
10782c0a09bSChris Fallin     where
10882c0a09bSChris Fallin         C: CtxEq<K, K> + CtxHash<K>,
10982c0a09bSChris Fallin     {
11082c0a09bSChris Fallin         self.entry_with_depth(ctx, key, self.depth())
1112be12a51SChris Fallin     }
1122be12a51SChris Fallin 
1132be12a51SChris Fallin     /// Get the entry, setting the scope depth at which to insert.
entry_with_depth<'a, C>(&'a mut self, ctx: &C, key: K, depth: usize) -> Entry<'a, K, V> where C: CtxEq<K, K> + CtxHash<K>,11482c0a09bSChris Fallin     pub fn entry_with_depth<'a, C>(&'a mut self, ctx: &C, key: K, depth: usize) -> Entry<'a, K, V>
11582c0a09bSChris Fallin     where
11682c0a09bSChris Fallin         C: CtxEq<K, K> + CtxHash<K>,
11782c0a09bSChris Fallin     {
1182be12a51SChris Fallin         debug_assert!(depth <= self.generation_by_depth.len());
1192be12a51SChris Fallin         let generation = self.generation_by_depth[depth];
1202be12a51SChris Fallin         let depth = depth as u32;
12182c0a09bSChris Fallin         match self.map.entry(key, ctx) {
12282c0a09bSChris Fallin             crate::ctxhash::Entry::Occupied(entry) => {
1232be12a51SChris Fallin                 let entry_generation = entry.get().generation;
1242be12a51SChris Fallin                 let entry_depth = entry.get().level as usize;
1252be12a51SChris Fallin                 if self.generation_by_depth.get(entry_depth).cloned() == Some(entry_generation) {
1262be12a51SChris Fallin                     Entry::Occupied(OccupiedEntry { entry })
1272be12a51SChris Fallin                 } else {
128747ad3c4Slazypassion                     Entry::Vacant(VacantEntry {
1292be12a51SChris Fallin                         entry: InsertLoc::Occupied(entry),
1302be12a51SChris Fallin                         depth,
1312be12a51SChris Fallin                         generation,
132747ad3c4Slazypassion                     })
133747ad3c4Slazypassion                 }
134747ad3c4Slazypassion             }
13582c0a09bSChris Fallin             crate::ctxhash::Entry::Vacant(entry) => Entry::Vacant(VacantEntry {
1362be12a51SChris Fallin                 entry: InsertLoc::Vacant(entry),
1372be12a51SChris Fallin                 depth,
1382be12a51SChris Fallin                 generation,
1392be12a51SChris Fallin             }),
1402be12a51SChris Fallin         }
1412be12a51SChris Fallin     }
1422be12a51SChris Fallin 
1432be12a51SChris Fallin     /// Get a value from a key, if present.
get<'a, C>(&'a self, ctx: &C, key: &K) -> Option<&'a V> where C: CtxEq<K, K> + CtxHash<K>,14482c0a09bSChris Fallin     pub fn get<'a, C>(&'a self, ctx: &C, key: &K) -> Option<&'a V>
14582c0a09bSChris Fallin     where
14682c0a09bSChris Fallin         C: CtxEq<K, K> + CtxHash<K>,
14782c0a09bSChris Fallin     {
1482be12a51SChris Fallin         self.map
14982c0a09bSChris Fallin             .get(key, ctx)
1502be12a51SChris Fallin             .filter(|entry| {
1512be12a51SChris Fallin                 let level = entry.level as usize;
1522be12a51SChris Fallin                 self.generation_by_depth.get(level).cloned() == Some(entry.generation)
1532be12a51SChris Fallin             })
1542be12a51SChris Fallin             .map(|entry| &entry.value)
1552be12a51SChris Fallin     }
1562be12a51SChris Fallin 
1572be12a51SChris Fallin     /// Insert a key-value pair if absent. No-op if already exists.
insert_if_absent<C>(&mut self, ctx: &C, key: K, value: V) where C: CtxEq<K, K> + CtxHash<K>,15882c0a09bSChris Fallin     pub fn insert_if_absent<C>(&mut self, ctx: &C, key: K, value: V)
15982c0a09bSChris Fallin     where
16082c0a09bSChris Fallin         C: CtxEq<K, K> + CtxHash<K>,
16182c0a09bSChris Fallin     {
16282c0a09bSChris Fallin         self.insert_if_absent_with_depth(ctx, key, value, self.depth());
1632be12a51SChris Fallin     }
1642be12a51SChris Fallin 
1652be12a51SChris Fallin     /// Insert a key-value pair if absent, using the given depth for
1662be12a51SChris Fallin     /// the insertion. No-op if already exists.
insert_if_absent_with_depth<C>(&mut self, ctx: &C, key: K, value: V, depth: usize) where C: CtxEq<K, K> + CtxHash<K>,16782c0a09bSChris Fallin     pub fn insert_if_absent_with_depth<C>(&mut self, ctx: &C, key: K, value: V, depth: usize)
16882c0a09bSChris Fallin     where
16982c0a09bSChris Fallin         C: CtxEq<K, K> + CtxHash<K>,
17082c0a09bSChris Fallin     {
17182c0a09bSChris Fallin         match self.entry_with_depth(ctx, key, depth) {
1722be12a51SChris Fallin             Entry::Vacant(v) => {
1732be12a51SChris Fallin                 v.insert(value);
1742be12a51SChris Fallin             }
1752be12a51SChris Fallin             Entry::Occupied(_) => {
1762be12a51SChris Fallin                 // Nothing.
1772be12a51SChris Fallin             }
1782be12a51SChris Fallin         }
179747ad3c4Slazypassion     }
180747ad3c4Slazypassion 
18182c0a09bSChris Fallin     /// Insert a key-value pair, using the given depth for the
18282c0a09bSChris Fallin     /// insertion. Removes existing entry and overwrites if already
18382c0a09bSChris Fallin     /// existed.
insert_with_depth<C>(&mut self, ctx: &C, key: K, value: V, depth: usize) where C: CtxEq<K, K> + CtxHash<K>,18482c0a09bSChris Fallin     pub fn insert_with_depth<C>(&mut self, ctx: &C, key: K, value: V, depth: usize)
18582c0a09bSChris Fallin     where
18682c0a09bSChris Fallin         C: CtxEq<K, K> + CtxHash<K>,
18782c0a09bSChris Fallin     {
18882c0a09bSChris Fallin         let val = Val {
18982c0a09bSChris Fallin             value,
19082c0a09bSChris Fallin             level: depth as u32,
19182c0a09bSChris Fallin             generation: self.generation_by_depth[depth],
19282c0a09bSChris Fallin         };
19382c0a09bSChris Fallin         self.map.insert(key, val, ctx);
19482c0a09bSChris Fallin     }
19582c0a09bSChris Fallin 
196747ad3c4Slazypassion     /// Enter a new scope.
increment_depth(&mut self)197747ad3c4Slazypassion     pub fn increment_depth(&mut self) {
1982be12a51SChris Fallin         self.generation_by_depth.push(self.generation);
199747ad3c4Slazypassion     }
200747ad3c4Slazypassion 
201747ad3c4Slazypassion     /// Exit the current scope.
decrement_depth(&mut self)202747ad3c4Slazypassion     pub fn decrement_depth(&mut self) {
2032be12a51SChris Fallin         self.generation += 1;
2042be12a51SChris Fallin         self.generation_by_depth.pop();
205747ad3c4Slazypassion     }
206747ad3c4Slazypassion 
2072be12a51SChris Fallin     /// Return the current scope depth.
depth(&self) -> usize2082be12a51SChris Fallin     pub fn depth(&self) -> usize {
2092be12a51SChris Fallin         self.generation_by_depth
2102be12a51SChris Fallin             .len()
2112be12a51SChris Fallin             .checked_sub(1)
2122be12a51SChris Fallin             .expect("generation_by_depth cannot be empty")
2132be12a51SChris Fallin     }
214747ad3c4Slazypassion }
215747ad3c4Slazypassion 
216747ad3c4Slazypassion #[cfg(test)]
217747ad3c4Slazypassion mod tests {
218747ad3c4Slazypassion     use super::*;
21982c0a09bSChris Fallin     use crate::ctxhash::NullCtx;
220747ad3c4Slazypassion 
221747ad3c4Slazypassion     #[test]
basic()222747ad3c4Slazypassion     fn basic() {
223747ad3c4Slazypassion         let mut map: ScopedHashMap<i32, i32> = ScopedHashMap::new();
224747ad3c4Slazypassion 
22582c0a09bSChris Fallin         match map.entry(&NullCtx, 0) {
226747ad3c4Slazypassion             Entry::Occupied(_entry) => panic!(),
227747ad3c4Slazypassion             Entry::Vacant(entry) => entry.insert(1),
228747ad3c4Slazypassion         }
22982c0a09bSChris Fallin         match map.entry(&NullCtx, 2) {
230747ad3c4Slazypassion             Entry::Occupied(_entry) => panic!(),
231747ad3c4Slazypassion             Entry::Vacant(entry) => entry.insert(8),
232747ad3c4Slazypassion         }
23382c0a09bSChris Fallin         match map.entry(&NullCtx, 2) {
234747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 8),
235747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
236747ad3c4Slazypassion         }
237747ad3c4Slazypassion         map.increment_depth();
23882c0a09bSChris Fallin         match map.entry(&NullCtx, 2) {
239747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 8),
240747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
241747ad3c4Slazypassion         }
24282c0a09bSChris Fallin         match map.entry(&NullCtx, 1) {
243747ad3c4Slazypassion             Entry::Occupied(_entry) => panic!(),
244747ad3c4Slazypassion             Entry::Vacant(entry) => entry.insert(3),
245747ad3c4Slazypassion         }
24682c0a09bSChris Fallin         match map.entry(&NullCtx, 1) {
247747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 3),
248747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
249747ad3c4Slazypassion         }
25082c0a09bSChris Fallin         match map.entry(&NullCtx, 0) {
251747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 1),
252747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
253747ad3c4Slazypassion         }
25482c0a09bSChris Fallin         match map.entry(&NullCtx, 2) {
255747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 8),
256747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
257747ad3c4Slazypassion         }
258747ad3c4Slazypassion         map.decrement_depth();
25982c0a09bSChris Fallin         match map.entry(&NullCtx, 0) {
260747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 1),
261747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
262747ad3c4Slazypassion         }
26382c0a09bSChris Fallin         match map.entry(&NullCtx, 2) {
264747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 8),
265747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
266747ad3c4Slazypassion         }
267747ad3c4Slazypassion         map.increment_depth();
26882c0a09bSChris Fallin         match map.entry(&NullCtx, 2) {
269747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 8),
270747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
271747ad3c4Slazypassion         }
27282c0a09bSChris Fallin         match map.entry(&NullCtx, 1) {
273747ad3c4Slazypassion             Entry::Occupied(_entry) => panic!(),
274747ad3c4Slazypassion             Entry::Vacant(entry) => entry.insert(4),
275747ad3c4Slazypassion         }
27682c0a09bSChris Fallin         match map.entry(&NullCtx, 1) {
277747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 4),
278747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
279747ad3c4Slazypassion         }
28082c0a09bSChris Fallin         match map.entry(&NullCtx, 2) {
281747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 8),
282747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
283747ad3c4Slazypassion         }
284747ad3c4Slazypassion         map.decrement_depth();
285747ad3c4Slazypassion         map.increment_depth();
286747ad3c4Slazypassion         map.increment_depth();
287747ad3c4Slazypassion         map.increment_depth();
28882c0a09bSChris Fallin         match map.entry(&NullCtx, 2) {
289747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 8),
290747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
291747ad3c4Slazypassion         }
29282c0a09bSChris Fallin         match map.entry(&NullCtx, 1) {
293747ad3c4Slazypassion             Entry::Occupied(_entry) => panic!(),
294747ad3c4Slazypassion             Entry::Vacant(entry) => entry.insert(5),
295747ad3c4Slazypassion         }
29682c0a09bSChris Fallin         match map.entry(&NullCtx, 1) {
297747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 5),
298747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
299747ad3c4Slazypassion         }
30082c0a09bSChris Fallin         match map.entry(&NullCtx, 2) {
301747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 8),
302747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
303747ad3c4Slazypassion         }
304747ad3c4Slazypassion         map.decrement_depth();
305747ad3c4Slazypassion         map.decrement_depth();
306747ad3c4Slazypassion         map.decrement_depth();
30782c0a09bSChris Fallin         match map.entry(&NullCtx, 2) {
308747ad3c4Slazypassion             Entry::Occupied(entry) => assert!(*entry.get() == 8),
309747ad3c4Slazypassion             Entry::Vacant(_entry) => panic!(),
310747ad3c4Slazypassion         }
31182c0a09bSChris Fallin         match map.entry(&NullCtx, 1) {
312747ad3c4Slazypassion             Entry::Occupied(_entry) => panic!(),
313747ad3c4Slazypassion             Entry::Vacant(entry) => entry.insert(3),
314747ad3c4Slazypassion         }
315747ad3c4Slazypassion     }
3162be12a51SChris Fallin 
3172be12a51SChris Fallin     #[test]
insert_arbitrary_depth()3182be12a51SChris Fallin     fn insert_arbitrary_depth() {
3192be12a51SChris Fallin         let mut map: ScopedHashMap<i32, i32> = ScopedHashMap::new();
32082c0a09bSChris Fallin         map.insert_if_absent(&NullCtx, 1, 2);
32182c0a09bSChris Fallin         assert_eq!(map.get(&NullCtx, &1), Some(&2));
3222be12a51SChris Fallin         map.increment_depth();
32382c0a09bSChris Fallin         assert_eq!(map.get(&NullCtx, &1), Some(&2));
32482c0a09bSChris Fallin         map.insert_if_absent(&NullCtx, 3, 4);
32582c0a09bSChris Fallin         assert_eq!(map.get(&NullCtx, &3), Some(&4));
3262be12a51SChris Fallin         map.decrement_depth();
32782c0a09bSChris Fallin         assert_eq!(map.get(&NullCtx, &3), None);
3282be12a51SChris Fallin         map.increment_depth();
32982c0a09bSChris Fallin         map.insert_if_absent_with_depth(&NullCtx, 3, 4, 0);
33082c0a09bSChris Fallin         assert_eq!(map.get(&NullCtx, &3), Some(&4));
3312be12a51SChris Fallin         map.decrement_depth();
33282c0a09bSChris Fallin         assert_eq!(map.get(&NullCtx, &3), Some(&4));
3332be12a51SChris Fallin     }
334747ad3c4Slazypassion }
335