From 5c224a484dc6ba2a70c9cd0d73a04849f6d7aa68 Mon Sep 17 00:00:00 2001 From: Valerii Lashmanov Date: Wed, 23 Sep 2020 23:32:11 -0500 Subject: [PATCH 1/5] MiniSet/MiniMap moved and renamed into SsoHashSet/SsoHashMap It is a more descriptive name and with upcoming changes there will be nothing "mini" about them. --- compiler/rustc_data_structures/src/lib.rs | 3 +-- .../src/{mini_map.rs => sso/map.rs} | 18 +++++++++--------- compiler/rustc_data_structures/src/sso/mod.rs | 5 +++++ .../src/{mini_set.rs => sso/set.rs} | 14 +++++++------- compiler/rustc_infer/src/infer/combine.rs | 6 +++--- .../rustc_infer/src/infer/outlives/verify.rs | 10 +++++----- compiler/rustc_middle/src/ty/outlives.rs | 10 +++++----- compiler/rustc_middle/src/ty/print/mod.rs | 6 +++--- compiler/rustc_middle/src/ty/walk.rs | 8 ++++---- .../src/traits/query/normalize.rs | 6 +++--- 10 files changed, 45 insertions(+), 41 deletions(-) rename compiler/rustc_data_structures/src/{mini_map.rs => sso/map.rs} (79%) create mode 100644 compiler/rustc_data_structures/src/sso/mod.rs rename compiler/rustc_data_structures/src/{mini_set.rs => sso/set.rs} (77%) diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 9ded10e9c26ac..d0f2a4148d307 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -101,8 +101,7 @@ pub mod vec_linked_list; pub mod work_queue; pub use atomic_ref::AtomicRef; pub mod frozen; -pub mod mini_map; -pub mod mini_set; +pub mod sso; pub mod tagged_ptr; pub mod temp_dir; pub mod unhash; diff --git a/compiler/rustc_data_structures/src/mini_map.rs b/compiler/rustc_data_structures/src/sso/map.rs similarity index 79% rename from compiler/rustc_data_structures/src/mini_map.rs rename to compiler/rustc_data_structures/src/sso/map.rs index cd3e949d3831a..c253e9d66161e 100644 --- a/compiler/rustc_data_structures/src/mini_map.rs +++ b/compiler/rustc_data_structures/src/sso/map.rs @@ -8,21 +8,21 @@ use std::hash::Hash; /// /// Stores elements in a small array up to a certain length /// and switches to `HashMap` when that length is exceeded. -pub enum MiniMap { +pub enum SsoHashMap { Array(ArrayVec<[(K, V); 8]>), Map(FxHashMap), } -impl MiniMap { - /// Creates an empty `MiniMap`. +impl SsoHashMap { + /// Creates an empty `SsoHashMap`. pub fn new() -> Self { - MiniMap::Array(ArrayVec::new()) + SsoHashMap::Array(ArrayVec::new()) } /// Inserts or updates value in the map. pub fn insert(&mut self, key: K, value: V) { match self { - MiniMap::Array(array) => { + SsoHashMap::Array(array) => { for pair in array.iter_mut() { if pair.0 == key { pair.1 = value; @@ -33,10 +33,10 @@ impl MiniMap { let mut map: FxHashMap = array.drain(..).collect(); let (key, value) = error.element(); map.insert(key, value); - *self = MiniMap::Map(map); + *self = SsoHashMap::Map(map); } } - MiniMap::Map(map) => { + SsoHashMap::Map(map) => { map.insert(key, value); } } @@ -45,7 +45,7 @@ impl MiniMap { /// Return value by key if any. pub fn get(&self, key: &K) -> Option<&V> { match self { - MiniMap::Array(array) => { + SsoHashMap::Array(array) => { for pair in array { if pair.0 == *key { return Some(&pair.1); @@ -53,7 +53,7 @@ impl MiniMap { } return None; } - MiniMap::Map(map) => { + SsoHashMap::Map(map) => { return map.get(key); } } diff --git a/compiler/rustc_data_structures/src/sso/mod.rs b/compiler/rustc_data_structures/src/sso/mod.rs new file mode 100644 index 0000000000000..ef634b9adcec3 --- /dev/null +++ b/compiler/rustc_data_structures/src/sso/mod.rs @@ -0,0 +1,5 @@ +mod map; +mod set; + +pub use map::SsoHashMap; +pub use set::SsoHashSet; diff --git a/compiler/rustc_data_structures/src/mini_set.rs b/compiler/rustc_data_structures/src/sso/set.rs similarity index 77% rename from compiler/rustc_data_structures/src/mini_set.rs rename to compiler/rustc_data_structures/src/sso/set.rs index 9d45af723deb6..b403c9dcc332e 100644 --- a/compiler/rustc_data_structures/src/mini_set.rs +++ b/compiler/rustc_data_structures/src/sso/set.rs @@ -5,15 +5,15 @@ use std::hash::Hash; /// /// Stores elements in a small array up to a certain length /// and switches to `HashSet` when that length is exceeded. -pub enum MiniSet { +pub enum SsoHashSet { Array(ArrayVec<[T; 8]>), Set(FxHashSet), } -impl MiniSet { - /// Creates an empty `MiniSet`. +impl SsoHashSet { + /// Creates an empty `SsoHashSet`. pub fn new() -> Self { - MiniSet::Array(ArrayVec::new()) + SsoHashSet::Array(ArrayVec::new()) } /// Adds a value to the set. @@ -23,19 +23,19 @@ impl MiniSet { /// If the set did have this value present, false is returned. pub fn insert(&mut self, elem: T) -> bool { match self { - MiniSet::Array(array) => { + SsoHashSet::Array(array) => { if array.iter().any(|e| *e == elem) { false } else { if let Err(error) = array.try_push(elem) { let mut set: FxHashSet = array.drain(..).collect(); set.insert(error.element()); - *self = MiniSet::Set(set); + *self = SsoHashSet::Set(set); } true } } - MiniSet::Set(set) => set.insert(elem), + SsoHashSet::Set(set) => set.insert(elem), } } } diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index a540face4f235..6a1715ef81899 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -35,7 +35,7 @@ use super::{InferCtxt, MiscVariable, TypeTrace}; use crate::traits::{Obligation, PredicateObligations}; use rustc_ast as ast; -use rustc_data_structures::mini_map::MiniMap; +use rustc_data_structures::sso::SsoHashMap; use rustc_hir::def_id::DefId; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::TypeError; @@ -429,7 +429,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { needs_wf: false, root_ty: ty, param_env: self.param_env, - cache: MiniMap::new(), + cache: SsoHashMap::new(), }; let ty = match generalize.relate(ty, ty) { @@ -490,7 +490,7 @@ struct Generalizer<'cx, 'tcx> { param_env: ty::ParamEnv<'tcx>, - cache: MiniMap, RelateResult<'tcx, Ty<'tcx>>>, + cache: SsoHashMap, RelateResult<'tcx, Ty<'tcx>>>, } /// Result from a generalization operation. This includes diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 21b0836563f6c..07924298c241b 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -1,7 +1,7 @@ use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::{GenericKind, VerifyBound}; use rustc_data_structures::captures::Captures; -use rustc_data_structures::mini_set::MiniSet; +use rustc_data_structures::sso::SsoHashSet; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -32,7 +32,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// Returns a "verify bound" that encodes what we know about /// `generic` and the regions it outlives. pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> { - let mut visited = MiniSet::new(); + let mut visited = SsoHashSet::new(); match generic { GenericKind::Param(param_ty) => self.param_bound(param_ty), GenericKind::Projection(projection_ty) => { @@ -44,7 +44,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn type_bound( &self, ty: Ty<'tcx>, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) -> VerifyBound<'tcx> { match *ty.kind() { ty::Param(p) => self.param_bound(p), @@ -148,7 +148,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { pub fn projection_bound( &self, projection_ty: ty::ProjectionTy<'tcx>, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) -> VerifyBound<'tcx> { debug!("projection_bound(projection_ty={:?})", projection_ty); @@ -186,7 +186,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn recursive_bound( &self, parent: GenericArg<'tcx>, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) -> VerifyBound<'tcx> { let mut bounds = parent .walk_shallow(visited) diff --git a/compiler/rustc_middle/src/ty/outlives.rs b/compiler/rustc_middle/src/ty/outlives.rs index ca992d36e9545..4c20141bbe691 100644 --- a/compiler/rustc_middle/src/ty/outlives.rs +++ b/compiler/rustc_middle/src/ty/outlives.rs @@ -4,7 +4,7 @@ use crate::ty::subst::{GenericArg, GenericArgKind}; use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc_data_structures::mini_set::MiniSet; +use rustc_data_structures::sso::SsoHashSet; use smallvec::SmallVec; #[derive(Debug)] @@ -51,7 +51,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Push onto `out` all the things that must outlive `'a` for the condition /// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**. pub fn push_outlives_components(self, ty0: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) { - let mut visited = MiniSet::new(); + let mut visited = SsoHashSet::new(); compute_components(self, ty0, out, &mut visited); debug!("components({:?}) = {:?}", ty0, out); } @@ -61,7 +61,7 @@ fn compute_components( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) { // Descend through the types, looking for the various "base" // components and collecting them into `out`. This is not written @@ -142,7 +142,7 @@ fn compute_components( // OutlivesProjectionComponents. Continue walking // through and constrain Pi. let mut subcomponents = smallvec![]; - let mut subvisited = MiniSet::new(); + let mut subvisited = SsoHashSet::new(); compute_components_recursive(tcx, ty.into(), &mut subcomponents, &mut subvisited); out.push(Component::EscapingProjection(subcomponents.into_iter().collect())); } @@ -194,7 +194,7 @@ fn compute_components_recursive( tcx: TyCtxt<'tcx>, parent: GenericArg<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) { for child in parent.walk_shallow(visited) { match child.unpack() { diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 225ea2399fbfd..2e00be2395b8c 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -2,7 +2,7 @@ use crate::ty::subst::{GenericArg, Subst}; use crate::ty::{self, DefIdTree, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::mini_set::MiniSet; +use rustc_data_structures::sso::SsoHashSet; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; @@ -269,7 +269,7 @@ pub trait Printer<'tcx>: Sized { /// deeply nested tuples that have no DefId. fn characteristic_def_id_of_type_cached<'a>( ty: Ty<'a>, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) -> Option { match *ty.kind() { ty::Adt(adt_def, _) => Some(adt_def.did), @@ -316,7 +316,7 @@ fn characteristic_def_id_of_type_cached<'a>( } } pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option { - characteristic_def_id_of_type_cached(ty, &mut MiniSet::new()) + characteristic_def_id_of_type_cached(ty, &mut SsoHashSet::new()) } impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::RegionKind { diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 80ade7dda4ca1..357a0dd65c414 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -3,7 +3,7 @@ use crate::ty; use crate::ty::subst::{GenericArg, GenericArgKind}; -use rustc_data_structures::mini_set::MiniSet; +use rustc_data_structures::sso::SsoHashSet; use smallvec::{self, SmallVec}; // The TypeWalker's stack is hot enough that it's worth going to some effort to @@ -13,7 +13,7 @@ type TypeWalkerStack<'tcx> = SmallVec<[GenericArg<'tcx>; 8]>; pub struct TypeWalker<'tcx> { stack: TypeWalkerStack<'tcx>, last_subtree: usize, - visited: MiniSet>, + visited: SsoHashSet>, } /// An iterator for walking the type tree. @@ -26,7 +26,7 @@ pub struct TypeWalker<'tcx> { /// skips any types that are already there. impl<'tcx> TypeWalker<'tcx> { pub fn new(root: GenericArg<'tcx>) -> Self { - Self { stack: smallvec![root], last_subtree: 1, visited: MiniSet::new() } + Self { stack: smallvec![root], last_subtree: 1, visited: SsoHashSet::new() } } /// Skips the subtree corresponding to the last type @@ -87,7 +87,7 @@ impl GenericArg<'tcx> { /// and skips any types that are already there. pub fn walk_shallow( self, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) -> impl Iterator> { let mut stack = SmallVec::new(); push_inner(&mut stack, self); diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 3dcebbcc24482..bdbf45f78a23b 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -7,7 +7,7 @@ use crate::infer::canonical::OriginalQueryValues; use crate::infer::{InferCtxt, InferOk}; use crate::traits::error_reporting::InferCtxtExt; use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; -use rustc_data_structures::mini_map::MiniMap; +use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::traits::Normalized; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; @@ -58,7 +58,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { param_env: self.param_env, obligations: vec![], error: false, - cache: MiniMap::new(), + cache: SsoHashMap::new(), anon_depth: 0, }; @@ -87,7 +87,7 @@ struct QueryNormalizer<'cx, 'tcx> { cause: &'cx ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, obligations: Vec>, - cache: MiniMap, Ty<'tcx>>, + cache: SsoHashMap, Ty<'tcx>>, error: bool, anon_depth: usize, } From 0600b178aa0e9f310067bf8ccaf736e77a03eb1d Mon Sep 17 00:00:00 2001 From: Valerii Lashmanov Date: Thu, 24 Sep 2020 01:21:31 -0500 Subject: [PATCH 2/5] SsoHashSet/SsoHashMap API greatly expanded Now both provide almost complete API of their non-SSO counterparts. --- compiler/rustc_data_structures/src/sso/map.rs | 508 +++++++++++++++++- compiler/rustc_data_structures/src/sso/mod.rs | 72 +++ compiler/rustc_data_structures/src/sso/set.rs | 307 ++++++++++- 3 files changed, 864 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_data_structures/src/sso/map.rs b/compiler/rustc_data_structures/src/sso/map.rs index c253e9d66161e..258368c8ef322 100644 --- a/compiler/rustc_data_structures/src/sso/map.rs +++ b/compiler/rustc_data_structures/src/sso/map.rs @@ -1,32 +1,202 @@ +use super::EitherIter; use crate::fx::FxHashMap; use arrayvec::ArrayVec; - +use std::borrow::Borrow; +use std::fmt; use std::hash::Hash; +use std::iter::FromIterator; +use std::ops::Index; -/// Small-storage-optimized implementation of a map -/// made specifically for caching results. +/// Small-storage-optimized implementation of a map. /// /// Stores elements in a small array up to a certain length /// and switches to `HashMap` when that length is exceeded. +/// +/// Implements subset of HashMap API. +/// +/// Missing HashMap API: +/// all hasher-related +/// try_reserve (unstable) +/// shrink_to (unstable) +/// drain_filter (unstable) +/// into_keys/into_values (unstable) +/// all raw_entry-related +/// PartialEq/Eq (requires sorting the array) +/// Entry::or_insert_with_key (unstable) +/// Vacant/Occupied entries and related +#[derive(Clone)] pub enum SsoHashMap { Array(ArrayVec<[(K, V); 8]>), Map(FxHashMap), } -impl SsoHashMap { +impl SsoHashMap { /// Creates an empty `SsoHashMap`. pub fn new() -> Self { SsoHashMap::Array(ArrayVec::new()) } - /// Inserts or updates value in the map. - pub fn insert(&mut self, key: K, value: V) { + /// Creates an empty `SsoHashMap` with the specified capacity. + pub fn with_capacity(cap: usize) -> Self { + let array = ArrayVec::new(); + if array.capacity() >= cap { + SsoHashMap::Array(array) + } else { + SsoHashMap::Map(FxHashMap::with_capacity_and_hasher(cap, Default::default())) + } + } + + /// Clears the map, removing all key-value pairs. Keeps the allocated memory + /// for reuse. + pub fn clear(&mut self) { + match self { + SsoHashMap::Array(array) => array.clear(), + SsoHashMap::Map(map) => map.clear(), + } + } + + /// Returns the number of elements the map can hold without reallocating. + pub fn capacity(&self) -> usize { + match self { + SsoHashMap::Array(array) => array.capacity(), + SsoHashMap::Map(map) => map.capacity(), + } + } + + /// Returns the number of elements in the map. + pub fn len(&self) -> usize { + match self { + SsoHashMap::Array(array) => array.len(), + SsoHashMap::Map(map) => map.len(), + } + } + + /// Returns `true` if the map contains no elements. + pub fn is_empty(&self) -> bool { + match self { + SsoHashMap::Array(array) => array.is_empty(), + SsoHashMap::Map(map) => map.is_empty(), + } + } + + /// An iterator visiting all key-value pairs in arbitrary order. + /// The iterator element type is `(&'a K, &'a V)`. + pub fn iter(&self) -> impl Iterator { + self.into_iter() + } + + /// An iterator visiting all key-value pairs in arbitrary order, + /// with mutable references to the values. + /// The iterator element type is `(&'a K, &'a mut V)`. + pub fn iter_mut(&mut self) -> impl Iterator { + self.into_iter() + } + + /// An iterator visiting all keys in arbitrary order. + /// The iterator element type is `&'a K`. + pub fn keys(&self) -> impl Iterator { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.iter().map(|(k, _v)| k)), + SsoHashMap::Map(map) => EitherIter::Right(map.keys()), + } + } + + /// An iterator visiting all values in arbitrary order. + /// The iterator element type is `&'a V`. + pub fn values(&self) -> impl Iterator { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.iter().map(|(_k, v)| v)), + SsoHashMap::Map(map) => EitherIter::Right(map.values()), + } + } + + /// An iterator visiting all values mutably in arbitrary order. + /// The iterator element type is `&'a mut V`. + pub fn values_mut(&mut self) -> impl Iterator { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.iter_mut().map(|(_k, v)| v)), + SsoHashMap::Map(map) => EitherIter::Right(map.values_mut()), + } + } + + /// Clears the map, returning all key-value pairs as an iterator. Keeps the + /// allocated memory for reuse. + pub fn drain(&mut self) -> impl Iterator + '_ { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.drain(..)), + SsoHashMap::Map(map) => EitherIter::Right(map.drain()), + } + } +} + +impl SsoHashMap { + /// Changes underlying storage from array to hashmap + /// if array is full. + fn migrate_if_full(&mut self) { + if let SsoHashMap::Array(array) = self { + if array.is_full() { + *self = SsoHashMap::Map(array.drain(..).collect()); + } + } + } + + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the `SsoHashMap`. The collection may reserve more space to avoid + /// frequent reallocations. + pub fn reserve(&mut self, additional: usize) { + match self { + SsoHashMap::Array(array) => { + if array.capacity() < (array.len() + additional) { + let mut map: FxHashMap = array.drain(..).collect(); + map.reserve(additional); + *self = SsoHashMap::Map(map); + } + } + SsoHashMap::Map(map) => map.reserve(additional), + } + } + + /// Shrinks the capacity of the map as much as possible. It will drop + /// down as much as possible while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + pub fn shrink_to_fit(&mut self) { + if let SsoHashMap::Map(map) = self { + let mut array = ArrayVec::new(); + if map.len() <= array.capacity() { + array.extend(map.drain()); + *self = SsoHashMap::Array(array); + } else { + map.shrink_to_fit(); + } + } + } + + /// Retains only the elements specified by the predicate. + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&K, &mut V) -> bool, + { + match self { + SsoHashMap::Array(array) => array.retain(|(k, v)| f(k, v)), + SsoHashMap::Map(map) => map.retain(f), + } + } + + /// Inserts a key-value pair into the map. + /// + /// If the map did not have this key present, [`None`] is returned. + /// + /// If the map did have this key present, the value is updated, and the old + /// value is returned. The key is not updated, though; this matters for + /// types that can be `==` without being identical. See the [module-level + /// documentation] for more. + pub fn insert(&mut self, key: K, value: V) -> Option { match self { SsoHashMap::Array(array) => { - for pair in array.iter_mut() { - if pair.0 == key { - pair.1 = value; - return; + for (k, v) in array.iter_mut() { + if *k == key { + let old_value = std::mem::replace(v, value); + return Some(old_value); } } if let Err(error) = array.try_push((key, value)) { @@ -35,27 +205,325 @@ impl SsoHashMap { map.insert(key, value); *self = SsoHashMap::Map(map); } + None } - SsoHashMap::Map(map) => { - map.insert(key, value); + SsoHashMap::Map(map) => map.insert(key, value), + } + } + + /// Removes a key from the map, returning the value at the key if the key + /// was previously in the map. + pub fn remove(&mut self, key: &Q) -> Option + where + K: Borrow, + Q: Hash + Eq, + { + match self { + SsoHashMap::Array(array) => { + if let Some(index) = array.iter().position(|(k, _v)| k.borrow() == key) { + Some(array.swap_remove(index).1) + } else { + None + } + } + SsoHashMap::Map(map) => map.remove(key), + } + } + + /// Removes a key from the map, returning the stored key and value if the + /// key was previously in the map. + pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> + where + K: Borrow, + Q: Hash + Eq, + { + match self { + SsoHashMap::Array(array) => { + if let Some(index) = array.iter().position(|(k, _v)| k.borrow() == key) { + Some(array.swap_remove(index)) + } else { + None + } } + SsoHashMap::Map(map) => map.remove_entry(key), } } - /// Return value by key if any. - pub fn get(&self, key: &K) -> Option<&V> { + /// Returns a reference to the value corresponding to the key. + pub fn get(&self, key: &Q) -> Option<&V> + where + K: Borrow, + Q: Hash + Eq, + { match self { SsoHashMap::Array(array) => { - for pair in array { - if pair.0 == *key { - return Some(&pair.1); + for (k, v) in array { + if k.borrow() == key { + return Some(v); } } - return None; + None } - SsoHashMap::Map(map) => { - return map.get(key); + SsoHashMap::Map(map) => map.get(key), + } + } + + /// Returns a mutable reference to the value corresponding to the key. + pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> + where + K: Borrow, + Q: Hash + Eq, + { + match self { + SsoHashMap::Array(array) => { + for (k, v) in array { + if (*k).borrow() == key { + return Some(v); + } + } + None } + SsoHashMap::Map(map) => map.get_mut(key), } } + + /// Returns the key-value pair corresponding to the supplied key. + pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> + where + K: Borrow, + Q: Hash + Eq, + { + match self { + SsoHashMap::Array(array) => { + for (k, v) in array { + if k.borrow() == key { + return Some((k, v)); + } + } + None + } + SsoHashMap::Map(map) => map.get_key_value(key), + } + } + + /// Returns `true` if the map contains a value for the specified key. + pub fn contains_key(&self, key: &Q) -> bool + where + K: Borrow, + Q: Hash + Eq, + { + match self { + SsoHashMap::Array(array) => array.iter().any(|(k, _v)| k.borrow() == key), + SsoHashMap::Map(map) => map.contains_key(key), + } + } + + /// Gets the given key's corresponding entry in the map for in-place manipulation. + pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { + Entry { ssomap: self, key } + } +} + +impl Default for SsoHashMap { + fn default() -> Self { + Self::new() + } +} + +impl FromIterator<(K, V)> for SsoHashMap { + fn from_iter>(iter: I) -> SsoHashMap { + let mut map: SsoHashMap = Default::default(); + map.extend(iter); + map + } +} + +impl Extend<(K, V)> for SsoHashMap { + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for (key, value) in iter.into_iter() { + self.insert(key, value); + } + } + + fn extend_one(&mut self, (k, v): (K, V)) { + self.insert(k, v); + } + + fn extend_reserve(&mut self, additional: usize) { + match self { + SsoHashMap::Array(array) => { + if array.capacity() < (array.len() + additional) { + let mut map: FxHashMap = array.drain(..).collect(); + map.extend_reserve(additional); + *self = SsoHashMap::Map(map); + } + } + SsoHashMap::Map(map) => map.extend_reserve(additional), + } + } +} + +impl<'a, K, V> Extend<(&'a K, &'a V)> for SsoHashMap +where + K: Eq + Hash + Copy, + V: Copy, +{ + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().map(|(k, v)| (k.clone(), v.clone()))) + } + + fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) { + self.insert(k, v); + } + + fn extend_reserve(&mut self, additional: usize) { + Extend::<(K, V)>::extend_reserve(self, additional) + } +} + +impl IntoIterator for SsoHashMap { + type IntoIter = EitherIter< + as IntoIterator>::IntoIter, + as IntoIterator>::IntoIter, + >; + type Item = ::Item; + + fn into_iter(self) -> Self::IntoIter { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.into_iter()), + SsoHashMap::Map(map) => EitherIter::Right(map.into_iter()), + } + } +} + +/// adapts Item of array reference iterator to Item of hashmap reference iterator. +fn adapt_array_ref_it(pair: &'a (K, V)) -> (&'a K, &'a V) { + let (a, b) = pair; + (a, b) +} + +/// adapts Item of array mut reference iterator to Item of hashmap mut reference iterator. +fn adapt_array_mut_it(pair: &'a mut (K, V)) -> (&'a K, &'a mut V) { + let (a, b) = pair; + (a, b) +} + +impl<'a, K, V> IntoIterator for &'a SsoHashMap { + type IntoIter = EitherIter< + std::iter::Map< + <&'a ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter, + fn(&'a (K, V)) -> (&'a K, &'a V), + >, + <&'a FxHashMap as IntoIterator>::IntoIter, + >; + type Item = ::Item; + + fn into_iter(self) -> Self::IntoIter { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.into_iter().map(adapt_array_ref_it)), + SsoHashMap::Map(map) => EitherIter::Right(map.into_iter()), + } + } +} + +impl<'a, K, V> IntoIterator for &'a mut SsoHashMap { + type IntoIter = EitherIter< + std::iter::Map< + <&'a mut ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter, + fn(&'a mut (K, V)) -> (&'a K, &'a mut V), + >, + <&'a mut FxHashMap as IntoIterator>::IntoIter, + >; + type Item = ::Item; + + fn into_iter(self) -> Self::IntoIter { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.into_iter().map(adapt_array_mut_it)), + SsoHashMap::Map(map) => EitherIter::Right(map.into_iter()), + } + } +} + +impl fmt::Debug for SsoHashMap +where + K: fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +impl<'a, K, Q: ?Sized, V> Index<&'a Q> for SsoHashMap +where + K: Eq + Hash + Borrow, + Q: Eq + Hash, +{ + type Output = V; + + fn index(&self, key: &Q) -> &V { + self.get(key).expect("no entry found for key") + } +} + +/// A view into a single entry in a map. +pub struct Entry<'a, K, V> { + ssomap: &'a mut SsoHashMap, + key: K, +} + +impl<'a, K: Eq + Hash, V> Entry<'a, K, V> { + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + pub fn and_modify(self, f: F) -> Self + where + F: FnOnce(&mut V), + { + if let Some(value) = self.ssomap.get_mut(&self.key) { + f(value); + } + self + } + + /// Ensures a value is in the entry by inserting the default if empty, and returns + /// a mutable reference to the value in the entry. + pub fn or_insert(self, value: V) -> &'a mut V { + self.or_insert_with(|| value) + } + + /// Ensures a value is in the entry by inserting the result of the default function if empty, + /// and returns a mutable reference to the value in the entry. + pub fn or_insert_with V>(self, default: F) -> &'a mut V { + self.ssomap.migrate_if_full(); + match self.ssomap { + SsoHashMap::Array(array) => { + let key_ref = &self.key; + let found_index = array.iter().position(|(k, _v)| k == key_ref); + let index = if let Some(index) = found_index { + index + } else { + array.try_push((self.key, default())).unwrap(); + array.len() - 1 + }; + &mut array[index].1 + } + SsoHashMap::Map(map) => map.entry(self.key).or_insert_with(default), + } + } + + /// Returns a reference to this entry's key. + pub fn key(&self) -> &K { + &self.key + } +} + +impl<'a, K: Eq + Hash, V: Default> Entry<'a, K, V> { + /// Ensures a value is in the entry by inserting the default value if empty, + /// and returns a mutable reference to the value in the entry. + pub fn or_default(self) -> &'a mut V { + self.or_insert_with(Default::default) + } } diff --git a/compiler/rustc_data_structures/src/sso/mod.rs b/compiler/rustc_data_structures/src/sso/mod.rs index ef634b9adcec3..bcc4240721e61 100644 --- a/compiler/rustc_data_structures/src/sso/mod.rs +++ b/compiler/rustc_data_structures/src/sso/mod.rs @@ -1,3 +1,75 @@ +use std::fmt; +use std::iter::ExactSizeIterator; +use std::iter::FusedIterator; +use std::iter::Iterator; + +/// Iterator which may contain instance of +/// one of two specific implementations. +/// +/// Used by both SsoHashMap and SsoHashSet. +#[derive(Clone)] +pub enum EitherIter { + Left(L), + Right(R), +} + +impl Iterator for EitherIter +where + L: Iterator, + R: Iterator, +{ + type Item = L::Item; + + fn next(&mut self) -> Option { + match self { + EitherIter::Left(l) => l.next(), + EitherIter::Right(r) => r.next(), + } + } + + fn size_hint(&self) -> (usize, Option) { + match self { + EitherIter::Left(l) => l.size_hint(), + EitherIter::Right(r) => r.size_hint(), + } + } +} + +impl ExactSizeIterator for EitherIter +where + L: ExactSizeIterator, + R: ExactSizeIterator, + EitherIter: Iterator, +{ + fn len(&self) -> usize { + match self { + EitherIter::Left(l) => l.len(), + EitherIter::Right(r) => r.len(), + } + } +} + +impl FusedIterator for EitherIter +where + L: FusedIterator, + R: FusedIterator, + EitherIter: Iterator, +{ +} + +impl fmt::Debug for EitherIter +where + L: fmt::Debug, + R: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + EitherIter::Left(l) => l.fmt(f), + EitherIter::Right(r) => r.fmt(f), + } + } +} + mod map; mod set; diff --git a/compiler/rustc_data_structures/src/sso/set.rs b/compiler/rustc_data_structures/src/sso/set.rs index b403c9dcc332e..6ec70524368d9 100644 --- a/compiler/rustc_data_structures/src/sso/set.rs +++ b/compiler/rustc_data_structures/src/sso/set.rs @@ -1,26 +1,197 @@ +use super::EitherIter; use crate::fx::FxHashSet; use arrayvec::ArrayVec; +use std::borrow::Borrow; +use std::fmt; use std::hash::Hash; +use std::iter::FromIterator; + /// Small-storage-optimized implementation of a set. /// /// Stores elements in a small array up to a certain length /// and switches to `HashSet` when that length is exceeded. +/// +/// Implements subset of HashSet API. +/// +/// Missing HashSet API: +/// all hasher-related +/// try_reserve (unstable) +/// shrink_to (unstable) +/// drain_filter (unstable) +/// get_or_insert/get_or_insert_owned/get_or_insert_with (unstable) +/// difference/symmetric_difference/intersection/union +/// is_disjoint/is_subset/is_superset +/// PartialEq/Eq (requires sorting the array) +/// BitOr/BitAnd/BitXor/Sub +#[derive(Clone)] pub enum SsoHashSet { Array(ArrayVec<[T; 8]>), Set(FxHashSet), } -impl SsoHashSet { +impl SsoHashSet { /// Creates an empty `SsoHashSet`. pub fn new() -> Self { SsoHashSet::Array(ArrayVec::new()) } + /// Creates an empty `SsoHashSet` with the specified capacity. + pub fn with_capacity(cap: usize) -> Self { + let array = ArrayVec::new(); + if array.capacity() >= cap { + SsoHashSet::Array(array) + } else { + SsoHashSet::Set(FxHashSet::with_capacity_and_hasher(cap, Default::default())) + } + } + + /// Clears the set, removing all values. + pub fn clear(&mut self) { + match self { + SsoHashSet::Array(array) => array.clear(), + SsoHashSet::Set(set) => set.clear(), + } + } + + /// Returns the number of elements the set can hold without reallocating. + pub fn capacity(&self) -> usize { + match self { + SsoHashSet::Array(array) => array.capacity(), + SsoHashSet::Set(set) => set.capacity(), + } + } + + /// Returns the number of elements in the set. + pub fn len(&self) -> usize { + match self { + SsoHashSet::Array(array) => array.len(), + SsoHashSet::Set(set) => set.len(), + } + } + + /// Returns `true` if the set contains no elements. + pub fn is_empty(&self) -> bool { + match self { + SsoHashSet::Array(array) => array.is_empty(), + SsoHashSet::Set(set) => set.is_empty(), + } + } + + /// An iterator visiting all elements in arbitrary order. + /// The iterator element type is `&'a T`. + pub fn iter(&'a self) -> impl Iterator { + self.into_iter() + } + + /// Clears the set, returning all elements in an iterator. + pub fn drain(&mut self) -> impl Iterator + '_ { + match self { + SsoHashSet::Array(array) => EitherIter::Left(array.drain(..)), + SsoHashSet::Set(set) => EitherIter::Right(set.drain()), + } + } +} + +impl SsoHashSet { + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the `SsoHashSet`. The collection may reserve more space to avoid + /// frequent reallocations. + pub fn reserve(&mut self, additional: usize) { + match self { + SsoHashSet::Array(array) => { + if array.capacity() < (array.len() + additional) { + let mut set: FxHashSet = array.drain(..).collect(); + set.reserve(additional); + *self = SsoHashSet::Set(set); + } + } + SsoHashSet::Set(set) => set.reserve(additional), + } + } + + /// Shrinks the capacity of the set as much as possible. It will drop + /// down as much as possible while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + pub fn shrink_to_fit(&mut self) { + if let SsoHashSet::Set(set) = self { + let mut array = ArrayVec::new(); + if set.len() <= array.capacity() { + array.extend(set.drain()); + *self = SsoHashSet::Array(array); + } else { + set.shrink_to_fit(); + } + } + } + + /// Retains only the elements specified by the predicate. + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&T) -> bool, + { + match self { + SsoHashSet::Array(array) => array.retain(|v| f(v)), + SsoHashSet::Set(set) => set.retain(f), + } + } + + /// Removes and returns the value in the set, if any, that is equal to the given one. + pub fn take(&mut self, value: &Q) -> Option + where + T: Borrow, + Q: Hash + Eq, + { + match self { + SsoHashSet::Array(array) => { + if let Some(index) = array.iter().position(|val| val.borrow() == value) { + Some(array.swap_remove(index)) + } else { + None + } + } + SsoHashSet::Set(set) => set.take(value), + } + } + + /// Adds a value to the set, replacing the existing value, if any, that is equal to the given + /// one. Returns the replaced value. + pub fn replace(&mut self, value: T) -> Option { + match self { + SsoHashSet::Array(array) => { + if let Some(index) = array.iter().position(|val| *val == value) { + let old_value = std::mem::replace(&mut array[index], value); + Some(old_value) + } else { + None + } + } + SsoHashSet::Set(set) => set.replace(value), + } + } + + /// Returns a reference to the value in the set, if any, that is equal to the given value. + pub fn get(&self, value: &Q) -> Option<&T> + where + T: Borrow, + Q: Hash + Eq, + { + match self { + SsoHashSet::Array(array) => { + if let Some(index) = array.iter().position(|val| val.borrow() == value) { + Some(&array[index]) + } else { + None + } + } + SsoHashSet::Set(set) => set.get(value), + } + } + /// Adds a value to the set. /// - /// If the set did not have this value present, true is returned. + /// If the set did not have this value present, `true` is returned. /// - /// If the set did have this value present, false is returned. + /// If the set did have this value present, `false` is returned. pub fn insert(&mut self, elem: T) -> bool { match self { SsoHashSet::Array(array) => { @@ -38,4 +209,134 @@ impl SsoHashSet { SsoHashSet::Set(set) => set.insert(elem), } } + + /// Removes a value from the set. Returns whether the value was + /// present in the set. + pub fn remove(&mut self, value: &Q) -> bool + where + T: Borrow, + Q: Hash + Eq, + { + match self { + SsoHashSet::Array(array) => { + if let Some(index) = array.iter().position(|val| val.borrow() == value) { + array.swap_remove(index); + true + } else { + false + } + } + SsoHashSet::Set(set) => set.remove(value), + } + } + + /// Returns `true` if the set contains a value. + pub fn contains(&self, value: &Q) -> bool + where + T: Borrow, + Q: Hash + Eq, + { + match self { + SsoHashSet::Array(array) => array.iter().any(|v| v.borrow() == value), + SsoHashSet::Set(set) => set.contains(value), + } + } +} + +impl Default for SsoHashSet { + fn default() -> Self { + Self::new() + } +} + +impl FromIterator for SsoHashSet { + fn from_iter>(iter: I) -> SsoHashSet { + let mut set: SsoHashSet = Default::default(); + set.extend(iter); + set + } +} + +impl Extend for SsoHashSet { + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for val in iter.into_iter() { + self.insert(val); + } + } + + fn extend_one(&mut self, item: T) { + self.insert(item); + } + + fn extend_reserve(&mut self, additional: usize) { + match self { + SsoHashSet::Array(array) => { + if array.capacity() < (array.len() + additional) { + let mut set: FxHashSet = array.drain(..).collect(); + set.extend_reserve(additional); + *self = SsoHashSet::Set(set); + } + } + SsoHashSet::Set(set) => set.extend_reserve(additional), + } + } +} + +impl<'a, T> Extend<&'a T> for SsoHashSet +where + T: 'a + Eq + Hash + Copy, +{ + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } + + fn extend_one(&mut self, &item: &'a T) { + self.insert(item); + } + + fn extend_reserve(&mut self, additional: usize) { + Extend::::extend_reserve(self, additional) + } +} + +impl IntoIterator for SsoHashSet { + type IntoIter = EitherIter< + as IntoIterator>::IntoIter, + as IntoIterator>::IntoIter, + >; + type Item = ::Item; + + fn into_iter(self) -> Self::IntoIter { + match self { + SsoHashSet::Array(array) => EitherIter::Left(array.into_iter()), + SsoHashSet::Set(set) => EitherIter::Right(set.into_iter()), + } + } +} + +impl<'a, T> IntoIterator for &'a SsoHashSet { + type IntoIter = EitherIter< + <&'a ArrayVec<[T; 8]> as IntoIterator>::IntoIter, + <&'a FxHashSet as IntoIterator>::IntoIter, + >; + type Item = ::Item; + + fn into_iter(self) -> Self::IntoIter { + match self { + SsoHashSet::Array(array) => EitherIter::Left(array.into_iter()), + SsoHashSet::Set(set) => EitherIter::Right(set.into_iter()), + } + } +} + +impl fmt::Debug for SsoHashSet +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.iter()).finish() + } } From 41942fac7d0711c6b3d0faa69748e22c0eb41388 Mon Sep 17 00:00:00 2001 From: Valerii Lashmanov Date: Sat, 26 Sep 2020 14:28:26 -0500 Subject: [PATCH 3/5] SsoHashSet reimplemented as a wrapper on top of SsoHashMap SsoHashSet::replace had to be removed because it requires missing API from SsoHashMap. It's not a widely used function, so I think it's ok to omit it for now. EitherIter moved into its own file. Also sprinkled code with #[inline] attributes where appropriate. --- .../src/sso/either_iter.rs | 75 ++++++ compiler/rustc_data_structures/src/sso/map.rs | 18 +- compiler/rustc_data_structures/src/sso/mod.rs | 73 +----- compiler/rustc_data_structures/src/sso/set.rs | 220 ++++++------------ 4 files changed, 158 insertions(+), 228 deletions(-) create mode 100644 compiler/rustc_data_structures/src/sso/either_iter.rs diff --git a/compiler/rustc_data_structures/src/sso/either_iter.rs b/compiler/rustc_data_structures/src/sso/either_iter.rs new file mode 100644 index 0000000000000..af8ffcf4c13a5 --- /dev/null +++ b/compiler/rustc_data_structures/src/sso/either_iter.rs @@ -0,0 +1,75 @@ +use std::fmt; +use std::iter::ExactSizeIterator; +use std::iter::FusedIterator; +use std::iter::Iterator; + +/// Iterator which may contain instance of +/// one of two specific implementations. +/// +/// Note: For most methods providing custom +/// implementation may margianlly +/// improve performance by avoiding +/// doing Left/Right match on every step +/// and doing it only once instead. +#[derive(Clone)] +pub enum EitherIter { + Left(L), + Right(R), +} + +impl Iterator for EitherIter +where + L: Iterator, + R: Iterator, +{ + type Item = L::Item; + + fn next(&mut self) -> Option { + match self { + EitherIter::Left(l) => l.next(), + EitherIter::Right(r) => r.next(), + } + } + + fn size_hint(&self) -> (usize, Option) { + match self { + EitherIter::Left(l) => l.size_hint(), + EitherIter::Right(r) => r.size_hint(), + } + } +} + +impl ExactSizeIterator for EitherIter +where + L: ExactSizeIterator, + R: ExactSizeIterator, + EitherIter: Iterator, +{ + fn len(&self) -> usize { + match self { + EitherIter::Left(l) => l.len(), + EitherIter::Right(r) => r.len(), + } + } +} + +impl FusedIterator for EitherIter +where + L: FusedIterator, + R: FusedIterator, + EitherIter: Iterator, +{ +} + +impl fmt::Debug for EitherIter +where + L: fmt::Debug, + R: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + EitherIter::Left(l) => l.fmt(f), + EitherIter::Right(r) => r.fmt(f), + } + } +} diff --git a/compiler/rustc_data_structures/src/sso/map.rs b/compiler/rustc_data_structures/src/sso/map.rs index 258368c8ef322..3089f88784581 100644 --- a/compiler/rustc_data_structures/src/sso/map.rs +++ b/compiler/rustc_data_structures/src/sso/map.rs @@ -1,4 +1,4 @@ -use super::EitherIter; +use super::either_iter::EitherIter; use crate::fx::FxHashMap; use arrayvec::ArrayVec; use std::borrow::Borrow; @@ -32,6 +32,7 @@ pub enum SsoHashMap { impl SsoHashMap { /// Creates an empty `SsoHashMap`. + #[inline] pub fn new() -> Self { SsoHashMap::Array(ArrayVec::new()) } @@ -81,13 +82,15 @@ impl SsoHashMap { /// An iterator visiting all key-value pairs in arbitrary order. /// The iterator element type is `(&'a K, &'a V)`. - pub fn iter(&self) -> impl Iterator { + #[inline] + pub fn iter(&self) -> <&Self as IntoIterator>::IntoIter { self.into_iter() } /// An iterator visiting all key-value pairs in arbitrary order, /// with mutable references to the values. /// The iterator element type is `(&'a K, &'a mut V)`. + #[inline] pub fn iter_mut(&mut self) -> impl Iterator { self.into_iter() } @@ -319,12 +322,14 @@ impl SsoHashMap { } /// Gets the given key's corresponding entry in the map for in-place manipulation. + #[inline] pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { Entry { ssomap: self, key } } } impl Default for SsoHashMap { + #[inline] fn default() -> Self { Self::new() } @@ -348,6 +353,7 @@ impl Extend<(K, V)> for SsoHashMap { } } + #[inline] fn extend_one(&mut self, (k, v): (K, V)) { self.insert(k, v); } @@ -375,10 +381,12 @@ where self.extend(iter.into_iter().map(|(k, v)| (k.clone(), v.clone()))) } + #[inline] fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) { self.insert(k, v); } + #[inline] fn extend_reserve(&mut self, additional: usize) { Extend::<(K, V)>::extend_reserve(self, additional) } @@ -400,12 +408,14 @@ impl IntoIterator for SsoHashMap { } /// adapts Item of array reference iterator to Item of hashmap reference iterator. +#[inline(always)] fn adapt_array_ref_it(pair: &'a (K, V)) -> (&'a K, &'a V) { let (a, b) = pair; (a, b) } /// adapts Item of array mut reference iterator to Item of hashmap mut reference iterator. +#[inline(always)] fn adapt_array_mut_it(pair: &'a mut (K, V)) -> (&'a K, &'a mut V) { let (a, b) = pair; (a, b) @@ -464,6 +474,7 @@ where { type Output = V; + #[inline] fn index(&self, key: &Q) -> &V { self.get(key).expect("no entry found for key") } @@ -490,6 +501,7 @@ impl<'a, K: Eq + Hash, V> Entry<'a, K, V> { /// Ensures a value is in the entry by inserting the default if empty, and returns /// a mutable reference to the value in the entry. + #[inline] pub fn or_insert(self, value: V) -> &'a mut V { self.or_insert_with(|| value) } @@ -515,6 +527,7 @@ impl<'a, K: Eq + Hash, V> Entry<'a, K, V> { } /// Returns a reference to this entry's key. + #[inline] pub fn key(&self) -> &K { &self.key } @@ -523,6 +536,7 @@ impl<'a, K: Eq + Hash, V> Entry<'a, K, V> { impl<'a, K: Eq + Hash, V: Default> Entry<'a, K, V> { /// Ensures a value is in the entry by inserting the default value if empty, /// and returns a mutable reference to the value in the entry. + #[inline] pub fn or_default(self) -> &'a mut V { self.or_insert_with(Default::default) } diff --git a/compiler/rustc_data_structures/src/sso/mod.rs b/compiler/rustc_data_structures/src/sso/mod.rs index bcc4240721e61..dd21bc8e69636 100644 --- a/compiler/rustc_data_structures/src/sso/mod.rs +++ b/compiler/rustc_data_structures/src/sso/mod.rs @@ -1,75 +1,4 @@ -use std::fmt; -use std::iter::ExactSizeIterator; -use std::iter::FusedIterator; -use std::iter::Iterator; - -/// Iterator which may contain instance of -/// one of two specific implementations. -/// -/// Used by both SsoHashMap and SsoHashSet. -#[derive(Clone)] -pub enum EitherIter { - Left(L), - Right(R), -} - -impl Iterator for EitherIter -where - L: Iterator, - R: Iterator, -{ - type Item = L::Item; - - fn next(&mut self) -> Option { - match self { - EitherIter::Left(l) => l.next(), - EitherIter::Right(r) => r.next(), - } - } - - fn size_hint(&self) -> (usize, Option) { - match self { - EitherIter::Left(l) => l.size_hint(), - EitherIter::Right(r) => r.size_hint(), - } - } -} - -impl ExactSizeIterator for EitherIter -where - L: ExactSizeIterator, - R: ExactSizeIterator, - EitherIter: Iterator, -{ - fn len(&self) -> usize { - match self { - EitherIter::Left(l) => l.len(), - EitherIter::Right(r) => r.len(), - } - } -} - -impl FusedIterator for EitherIter -where - L: FusedIterator, - R: FusedIterator, - EitherIter: Iterator, -{ -} - -impl fmt::Debug for EitherIter -where - L: fmt::Debug, - R: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - EitherIter::Left(l) => l.fmt(f), - EitherIter::Right(r) => r.fmt(f), - } - } -} - +mod either_iter; mod map; mod set; diff --git a/compiler/rustc_data_structures/src/sso/set.rs b/compiler/rustc_data_structures/src/sso/set.rs index 6ec70524368d9..353529b059892 100644 --- a/compiler/rustc_data_structures/src/sso/set.rs +++ b/compiler/rustc_data_structures/src/sso/set.rs @@ -1,11 +1,10 @@ -use super::EitherIter; -use crate::fx::FxHashSet; -use arrayvec::ArrayVec; use std::borrow::Borrow; use std::fmt; use std::hash::Hash; use std::iter::FromIterator; +use super::map::SsoHashMap; + /// Small-storage-optimized implementation of a set. /// /// Stores elements in a small array up to a certain length @@ -18,77 +17,73 @@ use std::iter::FromIterator; /// try_reserve (unstable) /// shrink_to (unstable) /// drain_filter (unstable) +/// replace /// get_or_insert/get_or_insert_owned/get_or_insert_with (unstable) /// difference/symmetric_difference/intersection/union /// is_disjoint/is_subset/is_superset -/// PartialEq/Eq (requires sorting the array) +/// PartialEq/Eq (requires SsoHashMap implementation) /// BitOr/BitAnd/BitXor/Sub #[derive(Clone)] -pub enum SsoHashSet { - Array(ArrayVec<[T; 8]>), - Set(FxHashSet), +pub struct SsoHashSet { + map: SsoHashMap, +} + +/// Adapter function used ot return +/// result if SsoHashMap functions into +/// result SsoHashSet should return. +#[inline(always)] +fn entry_to_key((k, _v): (K, V)) -> K { + k } impl SsoHashSet { /// Creates an empty `SsoHashSet`. + #[inline] pub fn new() -> Self { - SsoHashSet::Array(ArrayVec::new()) + Self { map: SsoHashMap::new() } } /// Creates an empty `SsoHashSet` with the specified capacity. + #[inline] pub fn with_capacity(cap: usize) -> Self { - let array = ArrayVec::new(); - if array.capacity() >= cap { - SsoHashSet::Array(array) - } else { - SsoHashSet::Set(FxHashSet::with_capacity_and_hasher(cap, Default::default())) - } + Self { map: SsoHashMap::with_capacity(cap) } } /// Clears the set, removing all values. + #[inline] pub fn clear(&mut self) { - match self { - SsoHashSet::Array(array) => array.clear(), - SsoHashSet::Set(set) => set.clear(), - } + self.map.clear() } /// Returns the number of elements the set can hold without reallocating. + #[inline] pub fn capacity(&self) -> usize { - match self { - SsoHashSet::Array(array) => array.capacity(), - SsoHashSet::Set(set) => set.capacity(), - } + self.map.capacity() } /// Returns the number of elements in the set. + #[inline] pub fn len(&self) -> usize { - match self { - SsoHashSet::Array(array) => array.len(), - SsoHashSet::Set(set) => set.len(), - } + self.map.len() } /// Returns `true` if the set contains no elements. + #[inline] pub fn is_empty(&self) -> bool { - match self { - SsoHashSet::Array(array) => array.is_empty(), - SsoHashSet::Set(set) => set.is_empty(), - } + self.map.is_empty() } /// An iterator visiting all elements in arbitrary order. /// The iterator element type is `&'a T`. + #[inline] pub fn iter(&'a self) -> impl Iterator { self.into_iter() } /// Clears the set, returning all elements in an iterator. + #[inline] pub fn drain(&mut self) -> impl Iterator + '_ { - match self { - SsoHashSet::Array(array) => EitherIter::Left(array.drain(..)), - SsoHashSet::Set(set) => EitherIter::Right(set.drain()), - } + self.map.drain().map(entry_to_key) } } @@ -96,95 +91,46 @@ impl SsoHashSet { /// Reserves capacity for at least `additional` more elements to be inserted /// in the `SsoHashSet`. The collection may reserve more space to avoid /// frequent reallocations. + #[inline] pub fn reserve(&mut self, additional: usize) { - match self { - SsoHashSet::Array(array) => { - if array.capacity() < (array.len() + additional) { - let mut set: FxHashSet = array.drain(..).collect(); - set.reserve(additional); - *self = SsoHashSet::Set(set); - } - } - SsoHashSet::Set(set) => set.reserve(additional), - } + self.map.reserve(additional) } /// Shrinks the capacity of the set as much as possible. It will drop /// down as much as possible while maintaining the internal rules /// and possibly leaving some space in accordance with the resize policy. + #[inline] pub fn shrink_to_fit(&mut self) { - if let SsoHashSet::Set(set) = self { - let mut array = ArrayVec::new(); - if set.len() <= array.capacity() { - array.extend(set.drain()); - *self = SsoHashSet::Array(array); - } else { - set.shrink_to_fit(); - } - } + self.map.shrink_to_fit() } /// Retains only the elements specified by the predicate. + #[inline] pub fn retain(&mut self, mut f: F) where F: FnMut(&T) -> bool, { - match self { - SsoHashSet::Array(array) => array.retain(|v| f(v)), - SsoHashSet::Set(set) => set.retain(f), - } + self.map.retain(|k, _v| f(k)) } /// Removes and returns the value in the set, if any, that is equal to the given one. + #[inline] pub fn take(&mut self, value: &Q) -> Option where T: Borrow, Q: Hash + Eq, { - match self { - SsoHashSet::Array(array) => { - if let Some(index) = array.iter().position(|val| val.borrow() == value) { - Some(array.swap_remove(index)) - } else { - None - } - } - SsoHashSet::Set(set) => set.take(value), - } - } - - /// Adds a value to the set, replacing the existing value, if any, that is equal to the given - /// one. Returns the replaced value. - pub fn replace(&mut self, value: T) -> Option { - match self { - SsoHashSet::Array(array) => { - if let Some(index) = array.iter().position(|val| *val == value) { - let old_value = std::mem::replace(&mut array[index], value); - Some(old_value) - } else { - None - } - } - SsoHashSet::Set(set) => set.replace(value), - } + self.map.remove_entry(value).map(entry_to_key) } /// Returns a reference to the value in the set, if any, that is equal to the given value. + #[inline] pub fn get(&self, value: &Q) -> Option<&T> where T: Borrow, Q: Hash + Eq, { - match self { - SsoHashSet::Array(array) => { - if let Some(index) = array.iter().position(|val| val.borrow() == value) { - Some(&array[index]) - } else { - None - } - } - SsoHashSet::Set(set) => set.get(value), - } + self.map.get_key_value(value).map(entry_to_key) } /// Adds a value to the set. @@ -192,60 +138,30 @@ impl SsoHashSet { /// If the set did not have this value present, `true` is returned. /// /// If the set did have this value present, `false` is returned. + #[inline] pub fn insert(&mut self, elem: T) -> bool { - match self { - SsoHashSet::Array(array) => { - if array.iter().any(|e| *e == elem) { - false - } else { - if let Err(error) = array.try_push(elem) { - let mut set: FxHashSet = array.drain(..).collect(); - set.insert(error.element()); - *self = SsoHashSet::Set(set); - } - true - } - } - SsoHashSet::Set(set) => set.insert(elem), - } + self.map.insert(elem, ()).is_none() } /// Removes a value from the set. Returns whether the value was /// present in the set. + #[inline] pub fn remove(&mut self, value: &Q) -> bool where T: Borrow, Q: Hash + Eq, { - match self { - SsoHashSet::Array(array) => { - if let Some(index) = array.iter().position(|val| val.borrow() == value) { - array.swap_remove(index); - true - } else { - false - } - } - SsoHashSet::Set(set) => set.remove(value), - } + self.map.remove(value).is_some() } /// Returns `true` if the set contains a value. + #[inline] pub fn contains(&self, value: &Q) -> bool where T: Borrow, Q: Hash + Eq, { - match self { - SsoHashSet::Array(array) => array.iter().any(|v| v.borrow() == value), - SsoHashSet::Set(set) => set.contains(value), - } - } -} - -impl Default for SsoHashSet { - fn default() -> Self { - Self::new() + self.map.contains_key(value) } } @@ -257,6 +173,13 @@ impl FromIterator for SsoHashSet { } } +impl Default for SsoHashSet { + #[inline] + fn default() -> Self { + Self::new() + } +} + impl Extend for SsoHashSet { fn extend(&mut self, iter: I) where @@ -267,21 +190,14 @@ impl Extend for SsoHashSet { } } + #[inline] fn extend_one(&mut self, item: T) { self.insert(item); } + #[inline] fn extend_reserve(&mut self, additional: usize) { - match self { - SsoHashSet::Array(array) => { - if array.capacity() < (array.len() + additional) { - let mut set: FxHashSet = array.drain(..).collect(); - set.extend_reserve(additional); - *self = SsoHashSet::Set(set); - } - } - SsoHashSet::Set(set) => set.extend_reserve(additional), - } + self.map.extend_reserve(additional) } } @@ -289,46 +205,42 @@ impl<'a, T> Extend<&'a T> for SsoHashSet where T: 'a + Eq + Hash + Copy, { + #[inline] fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()); } + #[inline] fn extend_one(&mut self, &item: &'a T) { self.insert(item); } + #[inline] fn extend_reserve(&mut self, additional: usize) { Extend::::extend_reserve(self, additional) } } impl IntoIterator for SsoHashSet { - type IntoIter = EitherIter< - as IntoIterator>::IntoIter, - as IntoIterator>::IntoIter, - >; + type IntoIter = std::iter::Map< as IntoIterator>::IntoIter, fn((T, ())) -> T>; type Item = ::Item; + #[inline] fn into_iter(self) -> Self::IntoIter { - match self { - SsoHashSet::Array(array) => EitherIter::Left(array.into_iter()), - SsoHashSet::Set(set) => EitherIter::Right(set.into_iter()), - } + self.map.into_iter().map(entry_to_key) } } impl<'a, T> IntoIterator for &'a SsoHashSet { - type IntoIter = EitherIter< - <&'a ArrayVec<[T; 8]> as IntoIterator>::IntoIter, - <&'a FxHashSet as IntoIterator>::IntoIter, + type IntoIter = std::iter::Map< + <&'a SsoHashMap as IntoIterator>::IntoIter, + fn((&'a T, &'a ())) -> &'a T, >; type Item = ::Item; + #[inline] fn into_iter(self) -> Self::IntoIter { - match self { - SsoHashSet::Array(array) => EitherIter::Left(array.into_iter()), - SsoHashSet::Set(set) => EitherIter::Right(set.into_iter()), - } + self.map.iter().map(entry_to_key) } } From 92a0668c20b8dea00d8739dce2243113f518b427 Mon Sep 17 00:00:00 2001 From: Valerii Lashmanov Date: Sun, 27 Sep 2020 23:48:19 -0500 Subject: [PATCH 4/5] SsoHashMap minor refactoring, SSO_ARRAY_SIZE introduced --- compiler/rustc_data_structures/src/sso/map.rs | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_data_structures/src/sso/map.rs b/compiler/rustc_data_structures/src/sso/map.rs index 3089f88784581..f466796100c47 100644 --- a/compiler/rustc_data_structures/src/sso/map.rs +++ b/compiler/rustc_data_structures/src/sso/map.rs @@ -7,6 +7,25 @@ use std::hash::Hash; use std::iter::FromIterator; use std::ops::Index; +// For pointer-sized arguments arrays +// are faster than set/map for up to 64 +// arguments. +// +// On the other hand such a big array +// hurts cache performance, makes passing +// sso structures around very expensive. +// +// Biggest performance benefit is gained +// for reasonably small arrays that stay +// small in vast majority of cases. +// +// '8' is choosen as a sane default, to be +// reevaluated later. +// +// Note: As of now ArrayVec design prevents +// us from making it user-customizable. +const SSO_ARRAY_SIZE: usize = 8; + /// Small-storage-optimized implementation of a map. /// /// Stores elements in a small array up to a certain length @@ -26,7 +45,7 @@ use std::ops::Index; /// Vacant/Occupied entries and related #[derive(Clone)] pub enum SsoHashMap { - Array(ArrayVec<[(K, V); 8]>), + Array(ArrayVec<[(K, V); SSO_ARRAY_SIZE]>), Map(FxHashMap), } @@ -39,9 +58,8 @@ impl SsoHashMap { /// Creates an empty `SsoHashMap` with the specified capacity. pub fn with_capacity(cap: usize) -> Self { - let array = ArrayVec::new(); - if array.capacity() >= cap { - SsoHashMap::Array(array) + if cap <= SSO_ARRAY_SIZE { + Self::new() } else { SsoHashMap::Map(FxHashMap::with_capacity_and_hasher(cap, Default::default())) } @@ -59,7 +77,7 @@ impl SsoHashMap { /// Returns the number of elements the map can hold without reallocating. pub fn capacity(&self) -> usize { match self { - SsoHashMap::Array(array) => array.capacity(), + SsoHashMap::Array(_) => SSO_ARRAY_SIZE, SsoHashMap::Map(map) => map.capacity(), } } @@ -149,7 +167,7 @@ impl SsoHashMap { pub fn reserve(&mut self, additional: usize) { match self { SsoHashMap::Array(array) => { - if array.capacity() < (array.len() + additional) { + if SSO_ARRAY_SIZE < (array.len() + additional) { let mut map: FxHashMap = array.drain(..).collect(); map.reserve(additional); *self = SsoHashMap::Map(map); @@ -164,10 +182,8 @@ impl SsoHashMap { /// and possibly leaving some space in accordance with the resize policy. pub fn shrink_to_fit(&mut self) { if let SsoHashMap::Map(map) = self { - let mut array = ArrayVec::new(); - if map.len() <= array.capacity() { - array.extend(map.drain()); - *self = SsoHashMap::Array(array); + if map.len() <= SSO_ARRAY_SIZE { + *self = SsoHashMap::Array(map.drain().collect()); } else { map.shrink_to_fit(); } @@ -361,7 +377,7 @@ impl Extend<(K, V)> for SsoHashMap { fn extend_reserve(&mut self, additional: usize) { match self { SsoHashMap::Array(array) => { - if array.capacity() < (array.len() + additional) { + if SSO_ARRAY_SIZE < (array.len() + additional) { let mut map: FxHashMap = array.drain(..).collect(); map.extend_reserve(additional); *self = SsoHashMap::Map(map); @@ -517,8 +533,9 @@ impl<'a, K: Eq + Hash, V> Entry<'a, K, V> { let index = if let Some(index) = found_index { index } else { + let index = array.len(); array.try_push((self.key, default())).unwrap(); - array.len() - 1 + index }; &mut array[index].1 } From d1d2184db407dbdc0a0872c9efb4ff58457e1c9a Mon Sep 17 00:00:00 2001 From: Valerii Lashmanov Date: Fri, 2 Oct 2020 20:13:21 -0500 Subject: [PATCH 5/5] SsoHashSet/Map - genericiy over Q removed Due to performance regression, see SsoHashMap comment. --- compiler/rustc_data_structures/src/sso/map.rs | 108 +++++++++--------- compiler/rustc_data_structures/src/sso/set.rs | 53 +++------ 2 files changed, 72 insertions(+), 89 deletions(-) diff --git a/compiler/rustc_data_structures/src/sso/map.rs b/compiler/rustc_data_structures/src/sso/map.rs index f466796100c47..fa510e58314af 100644 --- a/compiler/rustc_data_structures/src/sso/map.rs +++ b/compiler/rustc_data_structures/src/sso/map.rs @@ -1,7 +1,6 @@ use super::either_iter::EitherIter; use crate::fx::FxHashMap; use arrayvec::ArrayVec; -use std::borrow::Borrow; use std::fmt; use std::hash::Hash; use std::iter::FromIterator; @@ -30,19 +29,45 @@ const SSO_ARRAY_SIZE: usize = 8; /// /// Stores elements in a small array up to a certain length /// and switches to `HashMap` when that length is exceeded. -/// -/// Implements subset of HashMap API. -/// -/// Missing HashMap API: -/// all hasher-related -/// try_reserve (unstable) -/// shrink_to (unstable) -/// drain_filter (unstable) -/// into_keys/into_values (unstable) -/// all raw_entry-related -/// PartialEq/Eq (requires sorting the array) -/// Entry::or_insert_with_key (unstable) -/// Vacant/Occupied entries and related +// +// FIXME: Implements subset of HashMap API. +// +// Missing HashMap API: +// all hasher-related +// try_reserve (unstable) +// shrink_to (unstable) +// drain_filter (unstable) +// into_keys/into_values (unstable) +// all raw_entry-related +// PartialEq/Eq (requires sorting the array) +// Entry::or_insert_with_key (unstable) +// Vacant/Occupied entries and related +// +// FIXME: In HashMap most methods accepting key reference +// accept reference to generic `Q` where `K: Borrow`. +// +// However, using this approach in `HashMap::get` apparently +// breaks inlining and noticeably reduces performance. +// +// Performance *should* be the same given that borrow is +// a NOP in most cases, but in practice that's not the case. +// +// Further investigation is required. +// +// Affected methods: +// SsoHashMap::get +// SsoHashMap::get_mut +// SsoHashMap::get_entry +// SsoHashMap::get_key_value +// SsoHashMap::contains_key +// SsoHashMap::remove +// SsoHashMap::remove_entry +// Index::index +// SsoHashSet::take +// SsoHashSet::get +// SsoHashSet::remove +// SsoHashSet::contains + #[derive(Clone)] pub enum SsoHashMap { Array(ArrayVec<[(K, V); SSO_ARRAY_SIZE]>), @@ -232,14 +257,10 @@ impl SsoHashMap { /// Removes a key from the map, returning the value at the key if the key /// was previously in the map. - pub fn remove(&mut self, key: &Q) -> Option - where - K: Borrow, - Q: Hash + Eq, - { + pub fn remove(&mut self, key: &K) -> Option { match self { SsoHashMap::Array(array) => { - if let Some(index) = array.iter().position(|(k, _v)| k.borrow() == key) { + if let Some(index) = array.iter().position(|(k, _v)| k == key) { Some(array.swap_remove(index).1) } else { None @@ -251,14 +272,10 @@ impl SsoHashMap { /// Removes a key from the map, returning the stored key and value if the /// key was previously in the map. - pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> - where - K: Borrow, - Q: Hash + Eq, - { + pub fn remove_entry(&mut self, key: &K) -> Option<(K, V)> { match self { SsoHashMap::Array(array) => { - if let Some(index) = array.iter().position(|(k, _v)| k.borrow() == key) { + if let Some(index) = array.iter().position(|(k, _v)| k == key) { Some(array.swap_remove(index)) } else { None @@ -269,15 +286,11 @@ impl SsoHashMap { } /// Returns a reference to the value corresponding to the key. - pub fn get(&self, key: &Q) -> Option<&V> - where - K: Borrow, - Q: Hash + Eq, - { + pub fn get(&self, key: &K) -> Option<&V> { match self { SsoHashMap::Array(array) => { for (k, v) in array { - if k.borrow() == key { + if k == key { return Some(v); } } @@ -288,15 +301,11 @@ impl SsoHashMap { } /// Returns a mutable reference to the value corresponding to the key. - pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> - where - K: Borrow, - Q: Hash + Eq, - { + pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { match self { SsoHashMap::Array(array) => { for (k, v) in array { - if (*k).borrow() == key { + if k == key { return Some(v); } } @@ -307,15 +316,11 @@ impl SsoHashMap { } /// Returns the key-value pair corresponding to the supplied key. - pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> - where - K: Borrow, - Q: Hash + Eq, - { + pub fn get_key_value(&self, key: &K) -> Option<(&K, &V)> { match self { SsoHashMap::Array(array) => { for (k, v) in array { - if k.borrow() == key { + if k == key { return Some((k, v)); } } @@ -326,13 +331,9 @@ impl SsoHashMap { } /// Returns `true` if the map contains a value for the specified key. - pub fn contains_key(&self, key: &Q) -> bool - where - K: Borrow, - Q: Hash + Eq, - { + pub fn contains_key(&self, key: &K) -> bool { match self { - SsoHashMap::Array(array) => array.iter().any(|(k, _v)| k.borrow() == key), + SsoHashMap::Array(array) => array.iter().any(|(k, _v)| k == key), SsoHashMap::Map(map) => map.contains_key(key), } } @@ -483,15 +484,14 @@ where } } -impl<'a, K, Q: ?Sized, V> Index<&'a Q> for SsoHashMap +impl<'a, K, V> Index<&'a K> for SsoHashMap where - K: Eq + Hash + Borrow, - Q: Eq + Hash, + K: Eq + Hash, { type Output = V; #[inline] - fn index(&self, key: &Q) -> &V { + fn index(&self, key: &K) -> &V { self.get(key).expect("no entry found for key") } } diff --git a/compiler/rustc_data_structures/src/sso/set.rs b/compiler/rustc_data_structures/src/sso/set.rs index 353529b059892..23cff0206c530 100644 --- a/compiler/rustc_data_structures/src/sso/set.rs +++ b/compiler/rustc_data_structures/src/sso/set.rs @@ -1,4 +1,3 @@ -use std::borrow::Borrow; use std::fmt; use std::hash::Hash; use std::iter::FromIterator; @@ -9,20 +8,20 @@ use super::map::SsoHashMap; /// /// Stores elements in a small array up to a certain length /// and switches to `HashSet` when that length is exceeded. -/// -/// Implements subset of HashSet API. -/// -/// Missing HashSet API: -/// all hasher-related -/// try_reserve (unstable) -/// shrink_to (unstable) -/// drain_filter (unstable) -/// replace -/// get_or_insert/get_or_insert_owned/get_or_insert_with (unstable) -/// difference/symmetric_difference/intersection/union -/// is_disjoint/is_subset/is_superset -/// PartialEq/Eq (requires SsoHashMap implementation) -/// BitOr/BitAnd/BitXor/Sub +// +// FIXME: Implements subset of HashSet API. +// +// Missing HashSet API: +// all hasher-related +// try_reserve (unstable) +// shrink_to (unstable) +// drain_filter (unstable) +// replace +// get_or_insert/get_or_insert_owned/get_or_insert_with (unstable) +// difference/symmetric_difference/intersection/union +// is_disjoint/is_subset/is_superset +// PartialEq/Eq (requires SsoHashMap implementation) +// BitOr/BitAnd/BitXor/Sub #[derive(Clone)] pub struct SsoHashSet { map: SsoHashMap, @@ -115,21 +114,13 @@ impl SsoHashSet { /// Removes and returns the value in the set, if any, that is equal to the given one. #[inline] - pub fn take(&mut self, value: &Q) -> Option - where - T: Borrow, - Q: Hash + Eq, - { + pub fn take(&mut self, value: &T) -> Option { self.map.remove_entry(value).map(entry_to_key) } /// Returns a reference to the value in the set, if any, that is equal to the given value. #[inline] - pub fn get(&self, value: &Q) -> Option<&T> - where - T: Borrow, - Q: Hash + Eq, - { + pub fn get(&self, value: &T) -> Option<&T> { self.map.get_key_value(value).map(entry_to_key) } @@ -146,21 +137,13 @@ impl SsoHashSet { /// Removes a value from the set. Returns whether the value was /// present in the set. #[inline] - pub fn remove(&mut self, value: &Q) -> bool - where - T: Borrow, - Q: Hash + Eq, - { + pub fn remove(&mut self, value: &T) -> bool { self.map.remove(value).is_some() } /// Returns `true` if the set contains a value. #[inline] - pub fn contains(&self, value: &Q) -> bool - where - T: Borrow, - Q: Hash + Eq, - { + pub fn contains(&self, value: &T) -> bool { self.map.contains_key(value) } }