Bug 1631721 - Use hashbrown instead of hashglobe r=manishearth

Differential Revision: https://phabricator.services.mozilla.com/D71741
This commit is contained in:
Simon Sapin 2020-04-23 00:19:51 +00:00
parent cbe24024bb
commit 67ea7f6752
11 changed files with 90 additions and 64 deletions

2
Cargo.lock generated
View File

@ -1271,6 +1271,7 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
name = "fallible"
version = "0.0.1"
dependencies = [
"hashbrown",
"hashglobe",
"smallvec",
]
@ -2534,6 +2535,7 @@ dependencies = [
"app_units",
"cssparser",
"euclid",
"hashbrown",
"hashglobe",
"selectors",
"servo_arc",

View File

@ -11,6 +11,7 @@ path = "lib.rs"
[dependencies]
smallvec = "1.0"
hashbrown = "0.7"
hashglobe = { path = "../hashglobe" }
# This crate effectively does nothing except if the `known_system_malloc`

View File

@ -2,20 +2,62 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
extern crate hashbrown;
extern crate hashglobe;
extern crate smallvec;
use hashbrown::hash_map::Entry;
use hashbrown::CollectionAllocErr;
#[cfg(feature = "known_system_malloc")]
use hashglobe::alloc;
use hashglobe::FailedAllocationError;
use smallvec::Array;
use smallvec::SmallVec;
use std::alloc::Layout;
use std::vec::Vec;
pub trait FallibleVec<T> {
/// Append |val| to the end of |vec|. Returns Ok(()) on success,
/// Err(reason) if it fails, with |reason| describing the failure.
fn try_push(&mut self, value: T) -> Result<(), FailedAllocationError>;
fn try_push(&mut self, value: T) -> Result<(), CollectionAllocErr>;
}
pub trait FallibleHashMap<K, V, H> {
fn try_insert(&mut self, k: K, v: V) -> Result<Option<V>, CollectionAllocErr>;
fn try_entry(&mut self, k: K) -> Result<Entry<K, V, H>, CollectionAllocErr>;
}
pub trait FallibleHashSet<T, H> {
fn try_insert(&mut self, x: T) -> Result<bool, CollectionAllocErr>;
}
impl<K, V, H> FallibleHashMap<K, V, H> for hashbrown::HashMap<K, V, H>
where
K: Eq + std::hash::Hash,
H: std::hash::BuildHasher,
{
#[inline]
fn try_insert(&mut self, k: K, v: V) -> Result<Option<V>, CollectionAllocErr> {
self.try_reserve(1)?;
Ok(self.insert(k, v))
}
#[inline]
fn try_entry(&mut self, k: K) -> Result<Entry<K, V, H>, CollectionAllocErr> {
self.try_reserve(1)?;
Ok(self.entry(k))
}
}
impl<T, H> FallibleHashSet<T, H> for hashbrown::HashSet<T, H>
where
T: Eq + std::hash::Hash,
H: std::hash::BuildHasher,
{
#[inline]
fn try_insert(&mut self, x: T) -> Result<bool, CollectionAllocErr> {
self.try_reserve(1)?;
Ok(self.insert(x))
}
}
/////////////////////////////////////////////////////////////////
@ -23,7 +65,7 @@ pub trait FallibleVec<T> {
impl<T> FallibleVec<T> for Vec<T> {
#[inline(always)]
fn try_push(&mut self, val: T) -> Result<(), FailedAllocationError> {
fn try_push(&mut self, val: T) -> Result<(), CollectionAllocErr> {
#[cfg(feature = "known_system_malloc")]
{
if self.capacity() == self.len() {
@ -41,7 +83,7 @@ impl<T> FallibleVec<T> for Vec<T> {
#[cfg(feature = "known_system_malloc")]
#[inline(never)]
#[cold]
fn try_double_vec<T>(vec: &mut Vec<T>) -> Result<(), FailedAllocationError> {
fn try_double_vec<T>(vec: &mut Vec<T>) -> Result<(), CollectionAllocErr> {
use std::mem;
let old_ptr = vec.as_mut_ptr();
@ -53,12 +95,15 @@ fn try_double_vec<T>(vec: &mut Vec<T>) -> Result<(), FailedAllocationError> {
} else {
old_cap
.checked_mul(2)
.ok_or(FailedAllocationError::new("capacity overflow for Vec"))?
.ok_or(CollectionAllocErr::CapacityOverflow)?
};
let new_size_bytes = new_cap
.checked_mul(mem::size_of::<T>())
.ok_or(FailedAllocationError::new("capacity overflow for Vec"))?;
.ok_or(CollectionAllocErr::CapacityOverflow)?;
let layout = Layout::from_size_align(new_size_bytes, std::mem::align_of::<T>())
.map_err(|_| CollectionAllocErr::CapacityOverflow)?;
let new_ptr = unsafe {
if old_cap == 0 {
@ -69,9 +114,7 @@ fn try_double_vec<T>(vec: &mut Vec<T>) -> Result<(), FailedAllocationError> {
};
if new_ptr.is_null() {
return Err(FailedAllocationError::new(
"out of memory when allocating Vec",
));
return Err(CollectionAllocErr::AllocErr { layout });
}
let new_vec = unsafe { Vec::from_raw_parts(new_ptr as *mut T, old_len, new_cap) };
@ -85,7 +128,7 @@ fn try_double_vec<T>(vec: &mut Vec<T>) -> Result<(), FailedAllocationError> {
impl<T: Array> FallibleVec<T::Item> for SmallVec<T> {
#[inline(always)]
fn try_push(&mut self, val: T::Item) -> Result<(), FailedAllocationError> {
fn try_push(&mut self, val: T::Item) -> Result<(), CollectionAllocErr> {
#[cfg(feature = "known_system_malloc")]
{
if self.capacity() == self.len() {
@ -103,7 +146,7 @@ impl<T: Array> FallibleVec<T::Item> for SmallVec<T> {
#[cfg(feature = "known_system_malloc")]
#[inline(never)]
#[cold]
fn try_double_small_vec<T>(svec: &mut SmallVec<T>) -> Result<(), FailedAllocationError>
fn try_double_small_vec<T>(svec: &mut SmallVec<T>) -> Result<(), CollectionAllocErr>
where
T: Array,
{
@ -119,18 +162,21 @@ where
} else {
old_cap
.checked_mul(2)
.ok_or(FailedAllocationError::new("capacity overflow for SmallVec"))?
.ok_or(CollectionAllocErr::CapacityOverflow)?
};
// This surely shouldn't fail, if |old_cap| was previously accepted as a
// valid value. But err on the side of caution.
let old_size_bytes = old_cap
.checked_mul(mem::size_of::<T>())
.ok_or(FailedAllocationError::new("capacity overflow for SmallVec"))?;
.ok_or(CollectionAllocErr::CapacityOverflow)?;
let new_size_bytes = new_cap
.checked_mul(mem::size_of::<T>())
.ok_or(FailedAllocationError::new("capacity overflow for SmallVec"))?;
.ok_or(CollectionAllocErr::CapacityOverflow)?;
let layout = Layout::from_size_align(new_size_bytes, std::mem::align_of::<T>())
.map_err(|_| CollectionAllocErr::CapacityOverflow)?;
let new_ptr;
if svec.spilled() {
@ -150,9 +196,7 @@ where
}
if new_ptr.is_null() {
return Err(FailedAllocationError::new(
"out of memory when allocating SmallVec",
));
return Err(CollectionAllocErr::AllocErr { layout });
}
let new_vec = unsafe { Vec::from_raw_parts(new_ptr as *mut T::Item, old_len, new_cap) };

View File

@ -33,6 +33,7 @@ content-security-policy = {version = "0.3.0", features = ["serde"], optional = t
crossbeam-channel = { version = "0.3", optional = true }
cssparser = "0.27"
euclid = "0.20"
hashbrown = "0.7"
hashglobe = { path = "../hashglobe" }
hyper = { version = "0.12", optional = true }
hyper_serde = { version = "0.11", optional = true }

View File

@ -55,6 +55,7 @@ extern crate content_security_policy;
extern crate crossbeam_channel;
extern crate cssparser;
extern crate euclid;
extern crate hashbrown;
extern crate hashglobe;
#[cfg(feature = "servo")]
extern crate hyper;
@ -487,6 +488,7 @@ macro_rules! malloc_size_of_hash_set {
}
malloc_size_of_hash_set!(std::collections::HashSet<T, S>);
malloc_size_of_hash_set!(hashbrown::HashSet<T, S>);
malloc_size_of_hash_set!(hashglobe::hash_set::HashSet<T, S>);
malloc_size_of_hash_set!(hashglobe::fake::HashSet<T, S>);
@ -528,6 +530,7 @@ macro_rules! malloc_size_of_hash_map {
}
malloc_size_of_hash_map!(std::collections::HashMap<K, V, S>);
malloc_size_of_hash_map!(hashbrown::HashMap<K, V, S>);
malloc_size_of_hash_map!(hashglobe::hash_map::HashMap<K, V, S>);
malloc_size_of_hash_map!(hashglobe::fake::HashMap<K, V, S>);

View File

@ -2,28 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Reexports of hashglobe types in Gecko mode, and stdlib hashmap shims in Servo mode
//!
//! Can go away when the stdlib gets fallible collections
//! https://github.com/rust-lang/rfcs/pull/2116
//! Reexports of hashbrown, without and with FxHash
use fxhash;
#[cfg(feature = "gecko")]
pub use hashglobe::hash_map::HashMap;
#[cfg(feature = "gecko")]
pub use hashglobe::hash_set::HashSet;
#[cfg(feature = "servo")]
pub use hashglobe::fake::{HashMap, HashSet};
/// Appropriate reexports of hash_map types
pub mod map {
#[cfg(feature = "gecko")]
pub use hashglobe::hash_map::{Entry, Iter};
#[cfg(feature = "servo")]
pub use std::collections::hash_map::{Entry, Iter};
}
pub use hashbrown::{hash_map as map, HashMap, HashSet};
/// Hash map that uses the Fx hasher
pub type FxHashMap<K, V> = HashMap<K, V, fxhash::FxBuildHasher>;

View File

@ -10,7 +10,7 @@ use crate::selector_map::{MaybeCaseInsensitiveHashMap, SelectorMap, SelectorMapE
use crate::selector_parser::SelectorImpl;
use crate::{Atom, LocalName, Namespace};
use fallible::FallibleVec;
use hashglobe::FailedAllocationError;
use hashbrown::CollectionAllocErr;
use selectors::attr::NamespaceConstraint;
use selectors::parser::{Combinator, Component};
use selectors::parser::{Selector, SelectorIter};
@ -224,7 +224,7 @@ impl InvalidationMap {
&mut self,
selector: &Selector<SelectorImpl>,
quirks_mode: QuirksMode,
) -> Result<(), FailedAllocationError> {
) -> Result<(), CollectionAllocErr> {
debug!("InvalidationMap::note_selector({:?})", selector);
let mut iter = selector.iter();

View File

@ -18,6 +18,7 @@ use crate::stylesheets::{CssRule, StylesheetInDocument};
use crate::Atom;
use crate::CaseSensitivityExt;
use crate::LocalName as SelectorLocalName;
use fallible::FallibleHashSet;
use fxhash::FxHasher;
use selectors::attr::CaseSensitivity;
use selectors::parser::{Component, LocalName, Selector};

View File

@ -46,7 +46,7 @@ extern crate fxhash;
#[cfg(feature = "gecko")]
#[macro_use]
pub mod gecko_string_cache;
extern crate hashglobe;
extern crate hashbrown;
#[cfg(feature = "servo")]
#[macro_use]
extern crate html5ever;

View File

@ -14,8 +14,8 @@ use crate::rule_tree::CascadeLevel;
use crate::selector_parser::SelectorImpl;
use crate::stylist::Rule;
use crate::{Atom, LocalName, Namespace, WeakAtom};
use fallible::FallibleVec;
use hashglobe::FailedAllocationError;
use fallible::{FallibleHashMap, FallibleVec};
use hashbrown::CollectionAllocErr;
use precomputed_hash::PrecomputedHash;
use selectors::matching::{matches_selector, ElementSelectorFlags, MatchingContext};
use selectors::parser::{Combinator, Component, SelectorIter};
@ -95,7 +95,7 @@ pub trait SelectorMapEntry: Sized + Clone {
///
/// TODO: Tune the initial capacity of the HashMap
#[derive(Debug, MallocSizeOf)]
pub struct SelectorMap<T: 'static> {
pub struct SelectorMap<T> {
/// Rules that have `:root` selectors.
pub root: SmallVec<[T; 1]>,
/// A hash from an ID to rules which contain that ID selector.
@ -112,17 +112,14 @@ pub struct SelectorMap<T: 'static> {
pub count: usize,
}
impl<T: 'static> Default for SelectorMap<T> {
impl<T> Default for SelectorMap<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
// FIXME(Manishearth) the 'static bound can be removed when
// our HashMap fork (hashglobe) is able to use NonZero,
// or when stdlib gets fallible collections
impl<T: 'static> SelectorMap<T> {
impl<T> SelectorMap<T> {
/// Trivially constructs an empty `SelectorMap`.
pub fn new() -> Self {
SelectorMap {
@ -280,11 +277,7 @@ impl SelectorMap<Rule> {
impl<T: SelectorMapEntry> SelectorMap<T> {
/// Inserts into the correct hash, trying id, class, localname and
/// namespace.
pub fn insert(
&mut self,
entry: T,
quirks_mode: QuirksMode,
) -> Result<(), FailedAllocationError> {
pub fn insert(&mut self, entry: T, quirks_mode: QuirksMode) -> Result<(), CollectionAllocErr> {
self.count += 1;
let vector = match find_bucket(entry.selector()) {
@ -563,14 +556,11 @@ fn find_bucket<'a>(mut iter: SelectorIter<'a, SelectorImpl>) -> Bucket<'a> {
/// Wrapper for PrecomputedHashMap that does ASCII-case-insensitive lookup in quirks mode.
#[derive(Debug, MallocSizeOf)]
pub struct MaybeCaseInsensitiveHashMap<K: PrecomputedHash + Hash + Eq, V: 'static>(
pub struct MaybeCaseInsensitiveHashMap<K: PrecomputedHash + Hash + Eq, V>(
PrecomputedHashMap<K, V>,
);
// FIXME(Manishearth) the 'static bound can be removed when
// our HashMap fork (hashglobe) is able to use NonZero,
// or when stdlib gets fallible collections
impl<V: 'static> MaybeCaseInsensitiveHashMap<Atom, V> {
impl<V> MaybeCaseInsensitiveHashMap<Atom, V> {
/// Empty map
pub fn new() -> Self {
MaybeCaseInsensitiveHashMap(PrecomputedHashMap::default())
@ -581,7 +571,8 @@ impl<V: 'static> MaybeCaseInsensitiveHashMap<Atom, V> {
&mut self,
mut key: Atom,
quirks_mode: QuirksMode,
) -> Result<hash_map::Entry<Atom, V>, FailedAllocationError> {
) -> Result<hash_map::Entry<Atom, V, BuildHasherDefault<PrecomputedHasher>>, CollectionAllocErr>
{
if quirks_mode == QuirksMode::Quirks {
key = key.to_ascii_lowercase()
}

View File

@ -33,8 +33,8 @@ use crate::stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule,
use crate::stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter};
use crate::thread_state::{self, ThreadState};
use crate::{Atom, LocalName, Namespace, WeakAtom};
use fallible::FallibleVec;
use hashglobe::FailedAllocationError;
use fallible::{FallibleHashMap, FallibleVec};
use hashbrown::CollectionAllocErr;
use malloc_size_of::MallocSizeOf;
#[cfg(feature = "gecko")]
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
@ -88,7 +88,7 @@ impl UserAgentCascadeDataCache {
device: &Device,
quirks_mode: QuirksMode,
guard: &SharedRwLockReadGuard,
) -> Result<Arc<UserAgentCascadeData>, FailedAllocationError>
) -> Result<Arc<UserAgentCascadeData>, CollectionAllocErr>
where
I: Iterator<Item = &'a S> + Clone,
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
@ -260,7 +260,7 @@ impl DocumentCascadeData {
quirks_mode: QuirksMode,
mut flusher: DocumentStylesheetFlusher<'a, S>,
guards: &StylesheetGuards,
) -> Result<(), FailedAllocationError>
) -> Result<(), CollectionAllocErr>
where
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
{
@ -1839,7 +1839,7 @@ impl CascadeData {
quirks_mode: QuirksMode,
collection: SheetCollectionFlusher<S>,
guard: &SharedRwLockReadGuard,
) -> Result<(), FailedAllocationError>
) -> Result<(), CollectionAllocErr>
where
S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
{
@ -1976,7 +1976,7 @@ impl CascadeData {
guard: &SharedRwLockReadGuard,
rebuild_kind: SheetRebuildKind,
mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
) -> Result<(), FailedAllocationError>
) -> Result<(), CollectionAllocErr>
where
S: StylesheetInDocument + ToMediaListKey + 'static,
{