Skip to content

Commit 8c7104f

Browse files
committed
TB: Util: an efficient mapping for permissions
1 parent 8fcfd9e commit 8c7104f

File tree

1 file changed

+304
-0
lines changed
  • src/tools/miri/src/borrow_tracker/tree_borrows

1 file changed

+304
-0
lines changed
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
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+
15+
use std::hash::Hash;
16+
17+
use rustc_data_structures::fx::FxHashMap;
18+
19+
/// Intermediate key between a UniKeyMap and a UniValMap.
20+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21+
pub struct UniIndex {
22+
idx: u32,
23+
}
24+
25+
/// From K to UniIndex
26+
#[derive(Debug, Clone, Default)]
27+
pub struct UniKeyMap<K> {
28+
/// Underlying map that does all the hard work.
29+
/// Key invariant: the contents of `deassigned` are disjoint from the
30+
/// keys of `mapping`, and together they form the set of contiguous integers
31+
/// `0 .. (mapping.len() + deassigned.len())`.
32+
mapping: FxHashMap<K, u32>,
33+
/// Indexes that can be reused: memory gain when the map gets sparse
34+
/// due to many deletions.
35+
deassigned: Vec<u32>,
36+
}
37+
38+
/// From UniIndex to V
39+
#[derive(Debug, Clone, PartialEq, Eq)]
40+
pub struct UniValMap<V> {
41+
/// The mapping data. Thanks to Vec we get both fast accesses, and
42+
/// a memory-optimal representation if there are few deletions.
43+
data: Vec<Option<V>>,
44+
}
45+
46+
impl<V> Default for UniValMap<V> {
47+
fn default() -> Self {
48+
Self { data: Vec::default() }
49+
}
50+
}
51+
52+
impl<K> UniKeyMap<K>
53+
where
54+
K: Hash + Eq,
55+
{
56+
/// How many keys/index pairs are currently active.
57+
pub fn len(&self) -> usize {
58+
self.mapping.len()
59+
}
60+
61+
/// Whether this key has an associated index or not.
62+
pub fn contains_key(&self, key: &K) -> bool {
63+
self.mapping.contains_key(key)
64+
}
65+
66+
/// Assign this key to a new index. Panics if the key is already assigned,
67+
/// use `get_or_insert` for a version that instead returns the existing
68+
/// assignment.
69+
#[track_caller]
70+
pub fn insert(&mut self, key: K) -> UniIndex {
71+
// We want an unused index. First we attempt to find one from `deassigned`,
72+
// and if `deassigned` is empty we generate a fresh index.
73+
let idx = self.deassigned.pop().unwrap_or_else(|| {
74+
// `deassigned` is empty, so all keys in use are already in `mapping`.
75+
// The next available key is `mapping.len()`.
76+
self.mapping.len().try_into().expect("UniMap ran out of useable keys")
77+
});
78+
if self.mapping.insert(key, idx).is_some() {
79+
panic!(
80+
"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."
81+
);
82+
};
83+
UniIndex { idx }
84+
}
85+
86+
/// If it exists, the index this key maps to.
87+
pub fn get(&self, key: &K) -> Option<UniIndex> {
88+
self.mapping.get(key).map(|&idx| UniIndex { idx })
89+
}
90+
91+
/// Either get a previously existing entry, or create a new one if it
92+
/// is not yet present.
93+
pub fn get_or_insert(&mut self, key: K) -> UniIndex {
94+
self.get(&key).unwrap_or_else(|| self.insert(key))
95+
}
96+
97+
/// Return whatever index this key was using to the deassigned pool.
98+
///
99+
/// Note: calling this function can be dangerous. If the index still exists
100+
/// somewhere in a `UniValMap` and is reassigned by the `UniKeyMap` then
101+
/// it will inherit the old value of a completely unrelated key.
102+
/// If you `UniKeyMap::remove` a key you should make sure to also `UniValMap::remove`
103+
/// the associated `UniIndex` from ALL `UniValMap`s.
104+
///
105+
/// Example of such behavior:
106+
/// ```
107+
/// let mut keymap = UniKeyMap::<char>::default();
108+
/// let mut valmap = UniValMap::<char>::default();
109+
/// // Insert 'a' -> _ -> 'A'
110+
/// let idx_a = keymap.insert('a');
111+
/// valmap.insert(idx_a, 'A');
112+
/// // Remove 'a' -> _, but forget to remove _ -> 'A'
113+
/// keymap.remove(&'a');
114+
/// // valmap.remove(idx_a); // If we uncomment this line the issue is fixed
115+
/// // Insert 'b' -> _
116+
/// let idx_b = keymap.insert('b');
117+
/// let val_b = valmap.get(idx_b);
118+
/// assert_eq!(val_b, Some('A')); // Oh no
119+
/// // assert_eq!(val_b, None); // This is what we would have expected
120+
/// ```
121+
pub fn remove(&mut self, key: &K) {
122+
if let Some(idx) = self.mapping.remove(key) {
123+
self.deassigned.push(idx);
124+
}
125+
}
126+
}
127+
128+
impl<V> UniValMap<V> {
129+
/// Whether this index has an associated value.
130+
pub fn contains_idx(&self, idx: UniIndex) -> bool {
131+
self.data.get(idx.idx as usize).and_then(Option::as_ref).is_some()
132+
}
133+
134+
/// Reserve enough space to insert the value at the right index.
135+
fn extend_to_length(&mut self, len: usize) {
136+
if len > self.data.len() {
137+
let nb = len - self.data.len();
138+
self.data.reserve(nb);
139+
for _ in 0..nb {
140+
self.data.push(None);
141+
}
142+
}
143+
}
144+
145+
/// Assign a value to the index. Permanently overwrites any previous value.
146+
pub fn insert(&mut self, idx: UniIndex, val: V) {
147+
self.extend_to_length(idx.idx as usize + 1);
148+
self.data[idx.idx as usize] = Some(val)
149+
}
150+
151+
/// Get the value at this index, if it exists.
152+
pub fn get(&self, idx: UniIndex) -> Option<&V> {
153+
self.data.get(idx.idx as usize).and_then(Option::as_ref)
154+
}
155+
156+
/// Get the value at this index mutably, if it exists.
157+
pub fn get_mut(&mut self, idx: UniIndex) -> Option<&mut V> {
158+
self.data.get_mut(idx.idx as usize).and_then(Option::as_mut)
159+
}
160+
161+
/// Delete any value associated with this index. Ok even if the index
162+
/// has no associated value.
163+
pub fn remove(&mut self, idx: UniIndex) {
164+
if idx.idx as usize >= self.data.len() {
165+
return;
166+
}
167+
self.data[idx.idx as usize] = None;
168+
}
169+
}
170+
171+
/// An access to a single value of the map.
172+
pub struct UniEntry<'a, V> {
173+
inner: &'a mut Option<V>,
174+
}
175+
176+
impl<'a, V> UniValMap<V> {
177+
/// Get a wrapper around a mutable access to the value corresponding to `idx`.
178+
pub fn entry(&'a mut self, idx: UniIndex) -> UniEntry<'a, V> {
179+
self.extend_to_length(idx.idx as usize + 1);
180+
UniEntry { inner: &mut self.data[idx.idx as usize] }
181+
}
182+
}
183+
184+
impl<'a, V> UniEntry<'a, V> {
185+
/// Insert in the map and get the value.
186+
pub fn or_insert_with<F>(&mut self, default: F) -> &mut V
187+
where
188+
F: FnOnce() -> V,
189+
{
190+
if self.inner.is_none() {
191+
*self.inner = Some(default());
192+
}
193+
self.inner.as_mut().unwrap()
194+
}
195+
}
196+
197+
mod tests {
198+
use super::*;
199+
200+
#[test]
201+
fn extend_to_length() {
202+
let mut km = UniValMap::<char>::default();
203+
km.extend_to_length(10);
204+
assert!(km.data.len() == 10);
205+
km.extend_to_length(0);
206+
assert!(km.data.len() == 10);
207+
km.extend_to_length(10);
208+
assert!(km.data.len() == 10);
209+
km.extend_to_length(11);
210+
assert!(km.data.len() == 11);
211+
}
212+
213+
#[derive(Default)]
214+
struct MapWitness<K, V> {
215+
key: UniKeyMap<K>,
216+
val: UniValMap<V>,
217+
map: FxHashMap<K, V>,
218+
}
219+
220+
impl<K, V> MapWitness<K, V>
221+
where
222+
K: Copy + Hash + Eq,
223+
V: Copy + Eq + std::fmt::Debug,
224+
{
225+
fn insert(&mut self, k: K, v: V) {
226+
// UniMap
227+
let i = self.key.get_or_insert(k);
228+
self.val.insert(i, v);
229+
// HashMap
230+
self.map.insert(k, v);
231+
// Consistency: nothing to check
232+
}
233+
234+
fn get(&self, k: &K) {
235+
// UniMap
236+
let v1 = self.key.get(k).and_then(|i| self.val.get(i));
237+
// HashMap
238+
let v2 = self.map.get(k);
239+
// Consistency
240+
assert_eq!(v1, v2);
241+
}
242+
243+
fn get_mut(&mut self, k: &K) {
244+
// UniMap
245+
let v1 = self.key.get(k).and_then(|i| self.val.get_mut(i));
246+
// HashMap
247+
let v2 = self.map.get_mut(k);
248+
// Consistency
249+
assert_eq!(v1, v2);
250+
}
251+
fn remove(&mut self, k: &K) {
252+
// UniMap
253+
if let Some(i) = self.key.get(k) {
254+
self.val.remove(i);
255+
}
256+
self.key.remove(k);
257+
// HashMap
258+
self.map.remove(k);
259+
// Consistency: nothing to check
260+
}
261+
}
262+
263+
#[test]
264+
fn consistency_small() {
265+
let mut m = MapWitness::<u64, char>::default();
266+
m.insert(1, 'a');
267+
m.insert(2, 'b');
268+
m.get(&1);
269+
m.get_mut(&2);
270+
m.remove(&2);
271+
m.insert(1, 'c');
272+
m.get(&1);
273+
m.insert(3, 'd');
274+
m.insert(4, 'e');
275+
m.insert(4, 'f');
276+
m.get(&2);
277+
m.get(&3);
278+
m.get(&4);
279+
m.get(&5);
280+
m.remove(&100);
281+
m.get_mut(&100);
282+
m.get(&100);
283+
}
284+
285+
#[test]
286+
fn consistency_large() {
287+
use std::collections::hash_map::DefaultHasher;
288+
use std::hash::{Hash, Hasher};
289+
let mut hasher = DefaultHasher::new();
290+
let mut map = MapWitness::<u64, u64>::default();
291+
for i in 0..1000 {
292+
i.hash(&mut hasher);
293+
let rng = hasher.finish();
294+
let op = rng % 3 == 0;
295+
let key = (rng / 2) % 50;
296+
let val = (rng / 100) % 1000;
297+
if op {
298+
map.insert(key, val);
299+
} else {
300+
map.get(&key);
301+
}
302+
}
303+
}
304+
}

0 commit comments

Comments
 (0)