]> git.lizzy.rs Git - rust.git/blob - src/mono_hash_map.rs
Various cosmetic improvements.
[rust.git] / src / mono_hash_map.rs
1 //! This is a "monotonic `HashMap`": A `HashMap` that, when shared, can be pushed to but not
2 //! otherwise mutated. We also box items in the map. This means we can safely provide
3 //! shared references into existing items in the `HashMap`, because they will not be dropped
4 //! (from being removed) or moved (because they are boxed).
5 //! The API is is completely tailored to what `memory.rs` needs. It is still in
6 //! a separate file to minimize the amount of code that has to care about the unsafety.
7
8 use std::collections::hash_map::Entry;
9 use std::cell::RefCell;
10 use std::hash::Hash;
11 use std::borrow::Borrow;
12
13 use rustc_data_structures::fx::FxHashMap;
14
15 use crate::AllocMap;
16
17 #[derive(Debug, Clone)]
18 pub struct MonoHashMap<K: Hash + Eq, V>(RefCell<FxHashMap<K, Box<V>>>);
19
20 impl<K: Hash + Eq, V> MonoHashMap<K, V> {
21     /// This function exists for priroda to be able to iterate over all evaluator memory.
22     ///
23     /// The function is somewhat roundabout with the closure argument because internally the
24     /// `MonoHashMap` uses a `RefCell`. When iterating over the `HashMap` inside the `RefCell`,
25     /// we need to keep a borrow to the `HashMap` inside the iterator. The borrow is only alive
26     /// as long as the `Ref` returned by `RefCell::borrow()` is alive. So we can't return the
27     /// iterator, as that would drop the `Ref`. We can't return both, as it's not possible in Rust
28     /// to have a struct/tuple with a field that refers to another field.
29     pub fn iter<T>(&self, f: impl FnOnce(&mut dyn Iterator<Item=(&K, &V)>) -> T) -> T {
30         f(&mut self.0.borrow().iter().map(|(k, v)| (k, &**v)))
31     }
32 }
33
34 impl<K: Hash + Eq, V> Default for MonoHashMap<K, V> {
35     fn default() -> Self {
36         MonoHashMap(RefCell::new(Default::default()))
37     }
38 }
39
40 impl<K: Hash + Eq, V> AllocMap<K, V> for MonoHashMap<K, V> {
41     #[inline(always)]
42     fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
43         where K: Borrow<Q>
44     {
45         self.0.get_mut().contains_key(k)
46     }
47
48     #[inline(always)]
49     fn insert(&mut self, k: K, v: V) -> Option<V>
50     {
51         self.0.get_mut().insert(k, Box::new(v)).map(|x| *x)
52     }
53
54     #[inline(always)]
55     fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
56         where K: Borrow<Q>
57     {
58         self.0.get_mut().remove(k).map(|x| *x)
59     }
60
61     #[inline(always)]
62     fn filter_map_collect<T>(&self, mut f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T> {
63         self.0.borrow()
64             .iter()
65             .filter_map(move |(k, v)| f(k, &*v))
66             .collect()
67     }
68
69     /// The most interesting method: Providing a shared ref without
70     /// holding the `RefCell` open, and inserting new data if the key
71     /// is not used yet.
72     /// `vacant` is called if the key is not found in the map;
73     /// if it returns a reference, that is used directly, if it
74     /// returns owned data, that is put into the map and returned.
75     #[inline(always)]
76     fn get_or<E>(
77         &self,
78         k: K,
79         vacant: impl FnOnce() -> Result<V, E>
80     ) -> Result<&V, E> {
81         let val: *const V = match self.0.borrow_mut().entry(k) {
82             Entry::Occupied(entry) => &**entry.get(),
83             Entry::Vacant(entry) => &**entry.insert(Box::new(vacant()?)),
84         };
85         // This is safe because `val` points into a `Box`, that we know will not move and
86         // will also not be dropped as long as the shared reference `self` is live.
87         unsafe { Ok(&*val) }
88     }
89
90     #[inline(always)]
91     fn get_mut_or<E>(
92         &mut self,
93         k: K,
94         vacant: impl FnOnce() -> Result<V, E>
95     ) -> Result<&mut V, E>
96     {
97         match self.0.get_mut().entry(k) {
98             Entry::Occupied(e) => Ok(e.into_mut()),
99             Entry::Vacant(e) => {
100                 let v = vacant()?;
101                 Ok(e.insert(Box::new(v)))
102             }
103         }
104     }
105 }