miri/borrow_tracker/tree_borrows/
unimap.rs

1//! This module implements the `UniMap`, which is a way to get efficient mappings
2//! optimized for the setting of `tree_borrows/tree.rs`.
3//!
4//! A `UniKeyMap<K>` is a (slow) mapping from `K` to `UniIndex`,
5//! and `UniValMap<V>` is a (fast) mapping from `UniIndex` to `V`.
6//! Thus a pair `(UniKeyMap<K>, UniValMap<V>)` acts as a virtual `HashMap<K, V>`.
7//!
8//! Because of the asymmetry in access time, the use-case for `UniMap` is the following:
9//! a tuple `(UniKeyMap<K>, Vec<UniValMap<V>>)` is much more efficient than
10//! the equivalent `Vec<HashMap<K, V>>` it represents if all maps have similar
11//! sets of keys.
12
13#![allow(dead_code)]
14
15use std::hash::Hash;
16use std::mem;
17
18use rustc_data_structures::fx::FxHashMap;
19
20use crate::helpers::ToUsize;
21
22/// Intermediate key between a UniKeyMap and a UniValMap.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct UniIndex {
25    idx: u32,
26}
27
28/// From K to UniIndex
29#[derive(Debug, Clone, Default)]
30pub struct UniKeyMap<K> {
31    /// Underlying map that does all the hard work.
32    /// Key invariant: the contents of `deassigned` are disjoint from the
33    /// keys of `mapping`, and together they form the set of contiguous integers
34    /// `0 .. (mapping.len() + deassigned.len())`.
35    mapping: FxHashMap<K, u32>,
36    /// Indexes that can be reused: memory gain when the map gets sparse
37    /// due to many deletions.
38    deassigned: Vec<u32>,
39}
40
41/// From UniIndex to V
42#[derive(Debug, Clone, Eq)]
43pub struct UniValMap<V> {
44    /// The mapping data. Thanks to Vec we get both fast accesses, and
45    /// a memory-optimal representation if there are few deletions.
46    data: Vec<Option<V>>,
47}
48
49impl<V: PartialEq> UniValMap<V> {
50    /// Exact equality of two maps.
51    /// Less accurate but faster than `equivalent`, mostly because
52    /// of the fast path when the lengths are different.
53    pub fn identical(&self, other: &Self) -> bool {
54        self.data == other.data
55    }
56
57    /// Equality up to trailing `None`s of two maps, i.e.
58    /// do they represent the same mapping ?
59    pub fn equivalent(&self, other: &Self) -> bool {
60        let min_len = self.data.len().min(other.data.len());
61        self.data[min_len..].iter().all(Option::is_none)
62            && other.data[min_len..].iter().all(Option::is_none)
63            && (self.data[..min_len] == other.data[..min_len])
64    }
65}
66
67impl<V: PartialEq> PartialEq for UniValMap<V> {
68    /// 2023-05: We found that using `equivalent` rather than `identical`
69    /// in the equality testing of the `RangeMap` is neutral for most
70    /// benchmarks, while being quite beneficial for `zip-equal`
71    /// and to a lesser extent for `unicode`, `slice-get-unchecked` and
72    /// `backtraces` as well.
73    fn eq(&self, other: &Self) -> bool {
74        self.equivalent(other)
75    }
76}
77
78impl<V> Default for UniValMap<V> {
79    fn default() -> Self {
80        Self { data: Vec::default() }
81    }
82}
83
84impl<K> UniKeyMap<K>
85where
86    K: Hash + Eq,
87{
88    /// How many keys/index pairs are currently active.
89    pub fn len(&self) -> usize {
90        self.mapping.len()
91    }
92
93    /// Whether this key has an associated index or not.
94    pub fn contains_key(&self, key: &K) -> bool {
95        self.mapping.contains_key(key)
96    }
97
98    /// Assign this key to a new index. Panics if the key is already assigned,
99    /// use `get_or_insert` for a version that instead returns the existing
100    /// assignment.
101    #[track_caller]
102    pub fn insert(&mut self, key: K) -> UniIndex {
103        // We want an unused index. First we attempt to find one from `deassigned`,
104        // and if `deassigned` is empty we generate a fresh index.
105        let idx = self.deassigned.pop().unwrap_or_else(|| {
106            // `deassigned` is empty, so all keys in use are already in `mapping`.
107            // The next available key is `mapping.len()`.
108            self.mapping.len().try_into().expect("UniMap ran out of useable keys")
109        });
110        if self.mapping.insert(key, idx).is_some() {
111            panic!(
112                "This key is already assigned to a different index; either use `get_or_insert` instead if you care about this data, or first call `remove` to undo the preexisting assignment."
113            );
114        };
115        UniIndex { idx }
116    }
117
118    /// If it exists, the index this key maps to.
119    pub fn get(&self, key: &K) -> Option<UniIndex> {
120        self.mapping.get(key).map(|&idx| UniIndex { idx })
121    }
122
123    /// Either get a previously existing entry, or create a new one if it
124    /// is not yet present.
125    pub fn get_or_insert(&mut self, key: K) -> UniIndex {
126        self.get(&key).unwrap_or_else(|| self.insert(key))
127    }
128
129    /// Return whatever index this key was using to the deassigned pool.
130    ///
131    /// Note: calling this function can be dangerous. If the index still exists
132    /// somewhere in a `UniValMap` and is reassigned by the `UniKeyMap` then
133    /// it will inherit the old value of a completely unrelated key.
134    /// If you `UniKeyMap::remove` a key you should make sure to also `UniValMap::remove`
135    /// the associated `UniIndex` from ALL `UniValMap`s.
136    ///
137    /// Example of such behavior:
138    /// ```rust,ignore (private type can't be doctested)
139    /// let mut keymap = UniKeyMap::<char>::default();
140    /// let mut valmap = UniValMap::<char>::default();
141    /// // Insert 'a' -> _ -> 'A'
142    /// let idx_a = keymap.insert('a');
143    /// valmap.insert(idx_a, 'A');
144    /// // Remove 'a' -> _, but forget to remove _ -> 'A'
145    /// keymap.remove(&'a');
146    /// // valmap.remove(idx_a); // If we uncomment this line the issue is fixed
147    /// // Insert 'b' -> _
148    /// let idx_b = keymap.insert('b');
149    /// let val_b = valmap.get(idx_b);
150    /// assert_eq!(val_b, Some('A')); // Oh no
151    /// // assert_eq!(val_b, None); // This is what we would have expected
152    /// ```
153    pub fn remove(&mut self, key: &K) {
154        if let Some(idx) = self.mapping.remove(key) {
155            self.deassigned.push(idx);
156        }
157    }
158}
159
160impl<V> UniValMap<V> {
161    /// Whether this index has an associated value.
162    pub fn contains_idx(&self, idx: UniIndex) -> bool {
163        self.data.get(idx.idx.to_usize()).and_then(Option::as_ref).is_some()
164    }
165
166    /// Reserve enough space to insert the value at the right index.
167    fn extend_to_length(&mut self, len: usize) {
168        if len > self.data.len() {
169            let nb = len - self.data.len();
170            self.data.reserve(nb);
171            for _ in 0..nb {
172                self.data.push(None);
173            }
174        }
175    }
176
177    /// Assign a value to the index. Permanently overwrites any previous value.
178    pub fn insert(&mut self, idx: UniIndex, val: V) {
179        self.extend_to_length(idx.idx.to_usize() + 1);
180        self.data[idx.idx.to_usize()] = Some(val)
181    }
182
183    /// Get the value at this index, if it exists.
184    pub fn get(&self, idx: UniIndex) -> Option<&V> {
185        self.data.get(idx.idx.to_usize()).and_then(Option::as_ref)
186    }
187
188    /// Get the value at this index mutably, if it exists.
189    pub fn get_mut(&mut self, idx: UniIndex) -> Option<&mut V> {
190        self.data.get_mut(idx.idx.to_usize()).and_then(Option::as_mut)
191    }
192
193    /// Delete any value associated with this index.
194    /// Returns None if the value was not present, otherwise
195    /// returns the previously stored value.
196    pub fn remove(&mut self, idx: UniIndex) -> Option<V> {
197        if idx.idx.to_usize() >= self.data.len() {
198            return None;
199        }
200        let mut res = None;
201        mem::swap(&mut res, &mut self.data[idx.idx.to_usize()]);
202        res
203    }
204}
205
206/// An access to a single value of the map.
207pub struct UniEntry<'a, V> {
208    inner: &'a mut Option<V>,
209}
210
211impl<'a, V> UniValMap<V> {
212    /// Get a wrapper around a mutable access to the value corresponding to `idx`.
213    pub fn entry(&'a mut self, idx: UniIndex) -> UniEntry<'a, V> {
214        self.extend_to_length(idx.idx.to_usize() + 1);
215        UniEntry { inner: &mut self.data[idx.idx.to_usize()] }
216    }
217}
218
219impl<'a, V> UniEntry<'a, V> {
220    /// Insert in the map and get the value.
221    pub fn or_insert(&mut self, default: V) -> &mut V {
222        if self.inner.is_none() {
223            *self.inner = Some(default);
224        }
225        self.inner.as_mut().unwrap()
226    }
227
228    pub fn get(&self) -> Option<&V> {
229        self.inner.as_ref()
230    }
231}
232
233mod tests {
234    use super::*;
235
236    #[test]
237    fn extend_to_length() {
238        let mut km = UniValMap::<char>::default();
239        km.extend_to_length(10);
240        assert!(km.data.len() == 10);
241        km.extend_to_length(0);
242        assert!(km.data.len() == 10);
243        km.extend_to_length(10);
244        assert!(km.data.len() == 10);
245        km.extend_to_length(11);
246        assert!(km.data.len() == 11);
247    }
248
249    #[derive(Default)]
250    struct MapWitness<K, V> {
251        key: UniKeyMap<K>,
252        val: UniValMap<V>,
253        map: FxHashMap<K, V>,
254    }
255
256    impl<K, V> MapWitness<K, V>
257    where
258        K: Copy + Hash + Eq,
259        V: Copy + Eq + std::fmt::Debug,
260    {
261        fn insert(&mut self, k: K, v: V) {
262            // UniMap
263            let i = self.key.get_or_insert(k);
264            self.val.insert(i, v);
265            // HashMap
266            self.map.insert(k, v);
267            // Consistency: nothing to check
268        }
269
270        fn get(&self, k: &K) {
271            // UniMap
272            let v1 = self.key.get(k).and_then(|i| self.val.get(i));
273            // HashMap
274            let v2 = self.map.get(k);
275            // Consistency
276            assert_eq!(v1, v2);
277        }
278
279        fn get_mut(&mut self, k: &K) {
280            // UniMap
281            let v1 = self.key.get(k).and_then(|i| self.val.get_mut(i));
282            // HashMap
283            let v2 = self.map.get_mut(k);
284            // Consistency
285            assert_eq!(v1, v2);
286        }
287        fn remove(&mut self, k: &K) {
288            // UniMap
289            if let Some(i) = self.key.get(k) {
290                self.val.remove(i);
291            }
292            self.key.remove(k);
293            // HashMap
294            self.map.remove(k);
295            // Consistency: nothing to check
296        }
297    }
298
299    #[test]
300    fn consistency_small() {
301        let mut m = MapWitness::<u64, char>::default();
302        m.insert(1, 'a');
303        m.insert(2, 'b');
304        m.get(&1);
305        m.get_mut(&2);
306        m.remove(&2);
307        m.insert(1, 'c');
308        m.get(&1);
309        m.insert(3, 'd');
310        m.insert(4, 'e');
311        m.insert(4, 'f');
312        m.get(&2);
313        m.get(&3);
314        m.get(&4);
315        m.get(&5);
316        m.remove(&100);
317        m.get_mut(&100);
318        m.get(&100);
319    }
320
321    #[test]
322    fn consistency_large() {
323        use std::collections::hash_map::DefaultHasher;
324        use std::hash::{Hash, Hasher};
325        let mut hasher = DefaultHasher::new();
326        let mut map = MapWitness::<u64, u64>::default();
327        for i in 0..1000 {
328            i.hash(&mut hasher);
329            let rng = hasher.finish();
330            let op = rng % 3 == 0;
331            let key = (rng / 2) % 50;
332            let val = (rng / 100) % 1000;
333            if op {
334                map.insert(key, val);
335            } else {
336                map.get(&key);
337            }
338        }
339    }
340}