rust/src/mono_hash_map.rs

95 lines
3.7 KiB
Rust
Raw Normal View History

2020-03-02 22:36:15 +01:00
//! This is a "monotonic `FxHashMap`": A `FxHashMap` that, when shared, can be pushed to but not
2019-02-16 01:29:38 +00:00
//! otherwise mutated. We also box items in the map. This means we can safely provide
2020-03-02 22:36:15 +01:00
//! shared references into existing items in the `FxHashMap`, because they will not be dropped
2018-10-05 18:08:50 +02:00
//! (from being removed) or moved (because they are boxed).
//! The API is is completely tailored to what `memory.rs` needs. It is still in
//! a separate file to minimize the amount of code that has to care about the unsafety.
2019-12-23 12:56:23 +01:00
use std::borrow::Borrow;
2018-10-05 18:08:50 +02:00
use std::cell::RefCell;
2019-12-23 12:56:23 +01:00
use std::collections::hash_map::Entry;
2018-10-05 18:08:50 +02:00
use std::hash::Hash;
use rustc_data_structures::fx::FxHashMap;
2018-11-01 08:56:41 +01:00
use crate::AllocMap;
2018-10-05 18:08:50 +02:00
#[derive(Debug, Clone)]
pub struct MonoHashMap<K: Hash + Eq, V>(RefCell<FxHashMap<K, Box<V>>>);
2018-11-27 14:41:53 +01:00
impl<K: Hash + Eq, V> MonoHashMap<K, V> {
2019-02-16 01:29:38 +00:00
/// This function exists for priroda to be able to iterate over all evaluator memory.
2019-01-22 16:46:05 +01:00
///
2019-01-22 17:19:19 +01:00
/// The function is somewhat roundabout with the closure argument because internally the
2020-03-02 22:36:15 +01:00
/// `MonoHashMap` uses a `RefCell`. When iterating over the `FxHashMap` inside the `RefCell`,
/// we need to keep a borrow to the `FxHashMap` inside the iterator. The borrow is only alive
2019-01-22 17:19:19 +01:00
/// as long as the `Ref` returned by `RefCell::borrow()` is alive. So we can't return the
/// iterator, as that would drop the `Ref`. We can't return both, as it's not possible in Rust
/// to have a struct/tuple with a field that refers to another field.
2019-12-23 12:56:23 +01:00
pub fn iter<T>(&self, f: impl FnOnce(&mut dyn Iterator<Item = (&K, &V)>) -> T) -> T {
2019-01-22 16:46:05 +01:00
f(&mut self.0.borrow().iter().map(|(k, v)| (k, &**v)))
2018-11-27 14:41:53 +01:00
}
}
2018-10-05 18:08:50 +02:00
impl<K: Hash + Eq, V> Default for MonoHashMap<K, V> {
fn default() -> Self {
MonoHashMap(RefCell::new(Default::default()))
}
}
impl<K: Hash + Eq, V> AllocMap<K, V> for MonoHashMap<K, V> {
#[inline(always)]
fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
2019-12-23 12:56:23 +01:00
where
K: Borrow<Q>,
2018-10-05 18:08:50 +02:00
{
self.0.get_mut().contains_key(k)
}
#[inline(always)]
2019-12-23 12:56:23 +01:00
fn insert(&mut self, k: K, v: V) -> Option<V> {
2018-10-05 18:08:50 +02:00
self.0.get_mut().insert(k, Box::new(v)).map(|x| *x)
}
#[inline(always)]
fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
2019-12-23 12:56:23 +01:00
where
K: Borrow<Q>,
2018-10-05 18:08:50 +02:00
{
self.0.get_mut().remove(k).map(|x| *x)
}
#[inline(always)]
fn filter_map_collect<T>(&self, mut f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T> {
2019-12-23 12:56:23 +01:00
self.0.borrow().iter().filter_map(move |(k, v)| f(k, &*v)).collect()
2018-10-05 18:08:50 +02:00
}
/// The most interesting method: Providing a shared ref without
/// holding the `RefCell` open, and inserting new data if the key
/// is not used yet.
/// `vacant` is called if the key is not found in the map;
/// if it returns a reference, that is used directly, if it
/// returns owned data, that is put into the map and returned.
#[inline(always)]
2019-12-23 12:56:23 +01:00
fn get_or<E>(&self, k: K, vacant: impl FnOnce() -> Result<V, E>) -> Result<&V, E> {
2018-10-05 18:08:50 +02:00
let val: *const V = match self.0.borrow_mut().entry(k) {
Entry::Occupied(entry) => &**entry.get(),
Entry::Vacant(entry) => &**entry.insert(Box::new(vacant()?)),
};
// This is safe because `val` points into a `Box`, that we know will not move and
// will also not be dropped as long as the shared reference `self` is live.
unsafe { Ok(&*val) }
}
#[inline(always)]
2019-12-23 12:56:23 +01:00
fn get_mut_or<E>(&mut self, k: K, vacant: impl FnOnce() -> Result<V, E>) -> Result<&mut V, E> {
2018-10-05 18:08:50 +02:00
match self.0.get_mut().entry(k) {
Entry::Occupied(e) => Ok(e.into_mut()),
Entry::Vacant(e) => {
let v = vacant()?;
Ok(e.insert(Box::new(v)))
}
}
}
}