Change Map::get_or_insert_with to Map::entry

This commit is contained in:
Anthony Ramine 2020-04-20 14:11:45 +02:00
parent 306e8ac5f9
commit 1c2de5641c
2 changed files with 66 additions and 46 deletions

View file

@ -13,11 +13,10 @@ use smallvec::SmallVec;
use std::fmt; use std::fmt;
use std::hash; use std::hash;
use std::io::Write; use std::io::Write;
use std::mem;
use std::ptr; use std::ptr;
use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use super::map::Map; use super::map::{Entry, Map};
use super::unsafe_box::UnsafeBox; use super::unsafe_box::UnsafeBox;
use super::{CascadeLevel, StyleSource}; use super::{CascadeLevel, StyleSource};
@ -402,33 +401,21 @@ impl StrongRuleNode {
return child.upgrade(); return child.upgrade();
} }
let mut children = RwLockUpgradableReadGuard::upgrade(children); let mut children = RwLockUpgradableReadGuard::upgrade(children);
let mut is_new = false; match children.entry(key, |node| node.p.key()) {
let weak = { Entry::Occupied(child) => {
let is_new = &mut is_new; child.upgrade()
children.get_or_insert_with( },
key, Entry::Vacant(entry) => {
|node| node.p.key(), let node = StrongRuleNode::new(Box::new(RuleNode::new(
move || { unsafe { root.downgrade() },
*is_new = true;
let root = unsafe { root.downgrade() };
let strong = StrongRuleNode::new(Box::new(RuleNode::new(
root,
self.clone(), self.clone(),
source, source,
level, level,
))); )));
let weak = unsafe { strong.downgrade() }; entry.insert(unsafe { node.downgrade() });
mem::forget(strong); node
weak
}, },
)
};
if !is_new {
return weak.upgrade();
} }
unsafe { StrongRuleNode::from_unsafe_box(UnsafeBox::clone(&weak.p)) }
} }
/// Get the style source corresponding to this rule node. May return `None` /// Get the style source corresponding to this rule node. May return `None`

View file

@ -6,6 +6,7 @@
use fxhash::FxHashMap; use fxhash::FxHashMap;
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps}; use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps};
use std::collections::hash_map;
use std::hash::Hash; use std::hash::Hash;
use std::mem; use std::mem;
@ -28,6 +29,20 @@ enum MapIterInner<'a, K, V> {
Map(std::collections::hash_map::Values<'a, K, V>), Map(std::collections::hash_map::Values<'a, K, V>),
} }
pub(super) enum Entry<'a, K, V> {
Occupied(&'a mut V),
Vacant(VacantEntry<'a, K, V>),
}
pub(super) struct VacantEntry<'a, K, V> {
inner: VacantEntryInner<'a, K, V>,
}
enum VacantEntryInner<'a, K, V> {
One(&'a mut MapInner<K, V>),
Map(hash_map::VacantEntry<'a, K, V>),
}
impl<K, V> Default for Map<K, V> { impl<K, V> Default for Map<K, V> {
fn default() -> Self { fn default() -> Self {
Map { Map {
@ -91,20 +106,15 @@ where
} }
} }
pub(super) fn get_or_insert_with( pub(super) fn entry(
&mut self, &mut self,
key: K, key: K,
key_from_value: impl FnOnce(&V) -> K, key_from_value: impl FnOnce(&V) -> K,
new_value: impl FnOnce() -> V, ) -> Entry<'_, K, V> {
) -> &mut V {
match self.inner { match self.inner {
MapInner::Empty => { ref mut inner @ MapInner::Empty => Entry::Vacant(VacantEntry {
self.inner = MapInner::One(new_value()); inner: VacantEntryInner::One(inner),
match &mut self.inner { }),
MapInner::One(one) => one,
_ => unreachable!(),
}
},
MapInner::One(_) => { MapInner::One(_) => {
let one = match mem::replace(&mut self.inner, MapInner::Empty) { let one = match mem::replace(&mut self.inner, MapInner::Empty) {
MapInner::One(one) => one, MapInner::One(one) => one,
@ -115,10 +125,11 @@ where
// Same for the equality test. // Same for the equality test.
if key == one_key { if key == one_key {
self.inner = MapInner::One(one); self.inner = MapInner::One(one);
match &mut self.inner { let one = match &mut self.inner {
MapInner::One(one) => return one, MapInner::One(one) => one,
_ => unreachable!(), _ => unreachable!(),
} };
return Entry::Occupied(one);
} }
self.inner = MapInner::Map(Box::new(FxHashMap::with_capacity_and_hasher( self.inner = MapInner::Map(Box::new(FxHashMap::with_capacity_and_hasher(
2, 2,
@ -129,12 +140,19 @@ where
_ => unreachable!(), _ => unreachable!(),
}; };
map.insert(one_key, one); map.insert(one_key, one);
// But it doesn't matter if f panics, by this point match map.entry(key) {
// the map is as before but represented as a map instead hash_map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry {
// of a single value. inner: VacantEntryInner::Map(entry),
map.entry(key).or_insert_with(new_value) }),
_ => unreachable!(),
}
},
MapInner::Map(ref mut map) => match map.entry(key) {
hash_map::Entry::Occupied(entry) => Entry::Occupied(entry.into_mut()),
hash_map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry {
inner: VacantEntryInner::Map(entry),
}),
}, },
MapInner::Map(ref mut map) => map.entry(key).or_insert_with(new_value),
} }
} }
@ -152,6 +170,21 @@ where
} }
} }
impl<'a, K, V> VacantEntry<'a, K, V> {
pub(super) fn insert(self, value: V) -> &'a mut V {
match self.inner {
VacantEntryInner::One(map) => {
*map = MapInner::One(value);
match map {
MapInner::One(one) => one,
_ => unreachable!(),
}
},
VacantEntryInner::Map(entry) => entry.insert(value),
}
}
}
impl<K, V> MallocShallowSizeOf for Map<K, V> impl<K, V> MallocShallowSizeOf for Map<K, V>
where where
K: Eq + Hash, K: Eq + Hash,