From 92d2725ea4c58496cea4cb17065dc834085f351a Mon Sep 17 00:00:00 2001 From: Pablo Emilio Escobar Gaviria Date: Tue, 24 Mar 2020 22:46:39 -0300 Subject: [PATCH 01/13] Created a very basic `HashSetRef` implementation The documentation hasn't beeng updated yet. --- src/lib.rs | 18 ++++ src/map_ref.rs | 20 +---- src/set_ref.rs | 229 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 249 insertions(+), 18 deletions(-) create mode 100644 src/set_ref.rs diff --git a/src/lib.rs b/src/lib.rs index 9f1bc685..1f9800e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -213,6 +213,8 @@ intra_doc_link_resolution_failure )] #![warn(rust_2018_idioms)] +use crossbeam_epoch::Guard; +use std::ops::Deref; mod map; mod map_ref; @@ -234,3 +236,19 @@ pub type DefaultHashBuilder = ahash::RandomState; pub mod epoch { pub use crossbeam_epoch::{pin, Guard}; } + +pub(crate) enum GuardRef<'g> { + Owned(Guard), + Ref(&'g Guard), +} + +impl Deref for GuardRef<'_> { + type Target = Guard; + + #[inline] + fn deref(&self) -> &Guard { + match *self { + GuardRef::Owned(ref guard) | GuardRef::Ref(&ref guard) => guard, + } + } +} diff --git a/src/map_ref.rs b/src/map_ref.rs index c2a8964d..82428090 100644 --- a/src/map_ref.rs +++ b/src/map_ref.rs @@ -1,10 +1,10 @@ use crate::iter::*; -use crate::{HashMap, TryInsertError}; +use crate::{GuardRef, HashMap, TryInsertError}; use crossbeam_epoch::Guard; use std::borrow::Borrow; use std::fmt::{self, Debug, Formatter}; use std::hash::{BuildHasher, Hash}; -use std::ops::{Deref, Index}; +use std::ops::Index; /// A reference to a [`HashMap`], constructed with [`HashMap::pin`] or [`HashMap::with_guard`]. /// @@ -15,22 +15,6 @@ pub struct HashMapRef<'map, K, V, S = crate::DefaultHashBuilder> { guard: GuardRef<'map>, } -enum GuardRef<'g> { - Owned(Guard), - Ref(&'g Guard), -} - -impl Deref for GuardRef<'_> { - type Target = Guard; - - #[inline] - fn deref(&self) -> &Guard { - match *self { - GuardRef::Owned(ref guard) | GuardRef::Ref(&ref guard) => guard, - } - } -} - impl HashMap { /// Get a reference to this map with the current thread pinned. /// diff --git a/src/set_ref.rs b/src/set_ref.rs new file mode 100644 index 00000000..400abc47 --- /dev/null +++ b/src/set_ref.rs @@ -0,0 +1,229 @@ +use crate::iter::*; +use crate::{HashSet, GuardRef, TryInsertError}; +use crossbeam_epoch::Guard; +use std::borrow::Borrow; +use std::fmt::{self, Debug, Formatter}; +use std::hash::{BuildHasher, Hash}; +use std::ops::Index; + +/// A reference to a [`HashSet`], constructed with [`HashSet::pin`] or [`HashSet::with_guard`]. +/// +/// The current thread will be pinned for the duration of this reference. +/// Keep in mind that this prevents the collection of garbage generated by the set. +pub struct HashSetRef<'set, T, S = crate::DefaultHashBuilder> { + set: &'set HashSet, + guard: GuardRef<'set>, +} + +impl HashSet { + /// Get a reference to this set with the current thread pinned. + /// + /// Keep in mind that for as long as you hold onto this, you are preventing the collection of + /// garbage generated by the set. + pub fn pin(&self) -> HashSetRef<'_, T, S> { + HashSetRef { + guard: GuardRef::Owned(self.guard()), + set: &self, + } + } + + /// Get a reference to this set with the given guard. + pub fn with_guard<'g>(&'g self, guard: &'g Guard) -> HashSetRef<'g, T, S> { + HashSetRef { + set: &self, + guard: GuardRef::Ref(guard), + } + } +} + +impl HashSetRef<'_, T, S> { + /// An iterator visiting all key-value pairs in arbitrary order. + /// The iterator element type is `(&'g K, &'g V)`. + /// See also [`HashSet::iter`]. + pub fn iter(&self) -> Keys<'_, T, ()> { + self.set.iter(&self.guard) + } + + /// Returns the number of entries in the set. + /// See also [`HashSet::len`]. + pub fn len(&self) -> usize { + self.set.len() + } + + /// Returns `true` if the set is empty. Otherwise returns `false`. + /// See also [`HashSet::is_empty`]. + pub fn is_empty(&self) -> bool { + self.set.is_empty() + } +} + +impl HashSetRef<'_, K, V, S> +where + K: Clone, +{ + /// Tries to reserve capacity for at least additional more elements. + /// See also [`HashSet::reserve`]. + pub fn reserve(&self, additional: usize) { + self.set.reserve(additional, &self.guard) + } + + /// Removes all entries from this set. + /// See also [`HashSet::clear`]. + pub fn clear(&self) { + self.set.clear(&self.guard); + } +} + +impl HashSetRef<'_, K, V, S> +where + K: Hash + Eq, + S: BuildHasher, +{ + /// Tests if `key` is a key in this table. + /// See also [`HashSet::contains_key`]. + pub fn contains(&self, value: &Q) -> bool + where + T: Borrow, + Q: ?Sized + Hash + Eq, + { + self.set.contains(value, &self.guard) + } + + /// Returns the value to which `key` is setped. + /// See also [`HashSet::get`]. + #[inline] + pub fn get<'g, Q>(&'g self, value: &Q) -> Option<&'g V> + where + T: Borrow, + Q: ?Sized + Hash + Eq, + { + self.set.get(value, &self.guard) + } +} + +impl HashSetRef<'_, T, S> +where + T: 'static + Sync + Send + Clone + Hash + Eq, + S: BuildHasher, +{ + /// Inserts a key-value pair into the set. + /// + /// See also [`HashSet::insert`]. + pub fn insert(&self, value: T) -> bool { + self.set.insert(key, value, &self.guard) + } + + /// If the value for the specified `key` is present, attempts to + /// compute a new setping given the key and its current setped value. + /// See also [`HashSet::compute_if_present`]. + pub fn compute_if_present<'g, Q, F>(&'g self, key: &Q, resetping_function: F) -> Option<&'g V> + where + K: Borrow, + Q: ?Sized + Hash + Eq, + F: FnOnce(&K, &V) -> Option, + { + self.set + .compute_if_present(key, resetping_function, &self.guard) + } + + /// Removes the key (and its corresponding value) from this set. + /// See also [`HashSet::remove`]. + pub fn remove<'g, Q>(&'g self, value: &Q) -> bool + where + T: Borrow, + Q: ?Sized + Hash + Eq, + { + self.set.remove(value, &self.guard) + } + + pub fn take<'g, Q>(&self, value: &Q) -> Option<&'g T> + where + T: Borrow, + Q:?Sized + Hash + Eq + { + self.set.take(value, self.guard) + } + + /// Retains only the elements specified by the predicate. + /// See also [`HashSet::retain`]. + pub fn retain(&self, f: F) + where + F: FnMut(&K, &V) -> bool, + { + self.set.retain(f, &self.guard); + } + + /// Retains only the elements specified by the predicate. + /// See also [`HashSet::retain_force`]. + pub fn retain_force(&self, f: F) + where + F: FnMut(&K, &V) -> bool, + { + self.set.retain_force(f, &self.guard); + } +} + +impl<'g, T, S> IntoIterator for &'g HashSetRef<'_, T, S> { + type IntoIter = Keys<'g, T, ()>; + type Item = &'g T; + + fn into_iter(self) -> Self::IntoIter { + self.set.iter(&self.guard) + } +} + +impl Debug for HashSetRef<'_, T, S> +where + T: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self).finish() + } +} + +impl Clone for HashSetRef<'_, T, S> { + fn clone(&self) -> Self { + Self { + set: self.set.clone(), + guard: self.guard.clone() + } + } +} + +impl PartialEq for HashSetRef<'_, T, S> +where + T: Hash + Eq, + S: BuildHasher, +{ + fn eq(&self, other: &Self) -> bool { + self.set == other.set + } +} + +impl PartialEq> for HashSetRef<'_, T, S> +where + K: Hash + Eq, + V: PartialEq, + S: BuildHasher, +{ + fn eq(&self, other: &HashSet) -> bool { + self.set.guarded_eq(&other, &self.guard, &other.guard()) + } +} + +impl PartialEq> for HashSet +where + T: Hash + Eq, + S: BuildHasher, +{ + fn eq(&self, other: &HashSetRef<'_, K, V, S>) -> bool { + self.guarded_eq(&other.set, &self.guard(), &other.guard) + } +} + +impl Eq for HashSetRef<'_, T, S> +where + K: Hash + Eq, + S: BuildHasher, +{ +} From b3cd90b587e761050272baaa94d57527a2558799 Mon Sep 17 00:00:00 2001 From: Pablo Emilio Escobar Gaviria Date: Wed, 25 Mar 2020 22:11:14 -0300 Subject: [PATCH 02/13] Worked on fixing compile-time errors on `crate::set_ref` --- src/lib.rs | 1 + src/set.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++ src/set_ref.rs | 59 ++++++++++++++------------------------------------ 3 files changed, 73 insertions(+), 43 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1f9800e5..1e97e8fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -221,6 +221,7 @@ mod map_ref; mod node; mod raw; mod set; +mod set_ref; /// Iterator types. pub mod iter; diff --git a/src/set.rs b/src/set.rs index 27fa9683..7ad9ad51 100644 --- a/src/set.rs +++ b/src/set.rs @@ -367,6 +367,62 @@ where { self.map.remove_entry(value, guard).map(|(k, _)| k) } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` such that `f(&e)` returns `false`. + /// + /// # Examples + /// + /// ``` + /// use flurry::HashSet; + /// + /// let set = HashSet::new(); + /// + /// for i in 0..8 { + /// set.pin().insert(i); + /// } + /// set.pin().retain(|&e| e % 2 == 0); + /// assert_eq!(set.pin().len(), 4); + /// ``` + #[inline] + pub fn retain(&self, mut f: F, guard: &Guard) + where + F: FnMut(&T) -> bool, + { + self.map.retain(|value, ()| f(value), guard) + } +} + +impl HashSet + where + T: Clone, +{ + /// Clears the set, removing all elements. + /// + /// # Examples + /// + /// ``` + /// use flurry::HashSet; + /// + /// let set = HashSet::new(); + /// + /// set.pin().insert("a"); + /// set.pin().clear(); + /// assert!(set.pin().is_empty()); + /// ``` + #[inline] + pub fn clear(&self, Guard: &Guard) { + self.map.clear(guard) + } + + /// Tries to reserve capacity for at least `additional` more elements to + /// be inserted in the `HashSet`. The collection may reserve more space to + /// avoid frequent reallocations. + #[inline] + pub fn reserve(&self, additional: usize, guard: &Guard) { + self.map.reserve(additional, guard) + } } impl PartialEq for HashSet diff --git a/src/set_ref.rs b/src/set_ref.rs index 400abc47..a2aedf0c 100644 --- a/src/set_ref.rs +++ b/src/set_ref.rs @@ -1,10 +1,9 @@ use crate::iter::*; -use crate::{HashSet, GuardRef, TryInsertError}; +use crate::{HashSet, GuardRef}; use crossbeam_epoch::Guard; use std::borrow::Borrow; use std::fmt::{self, Debug, Formatter}; use std::hash::{BuildHasher, Hash}; -use std::ops::Index; /// A reference to a [`HashSet`], constructed with [`HashSet::pin`] or [`HashSet::with_guard`]. /// @@ -15,7 +14,7 @@ pub struct HashSetRef<'set, T, S = crate::DefaultHashBuilder> { guard: GuardRef<'set>, } -impl HashSet { +impl HashSet { /// Get a reference to this set with the current thread pinned. /// /// Keep in mind that for as long as you hold onto this, you are preventing the collection of @@ -57,9 +56,9 @@ impl HashSetRef<'_, T, S> { } } -impl HashSetRef<'_, K, V, S> +impl HashSetRef<'_, T, S> where - K: Clone, + T: Clone, { /// Tries to reserve capacity for at least additional more elements. /// See also [`HashSet::reserve`]. @@ -74,9 +73,9 @@ where } } -impl HashSetRef<'_, K, V, S> +impl HashSetRef<'_, T, S> where - K: Hash + Eq, + T: Hash + Eq, S: BuildHasher, { /// Tests if `key` is a key in this table. @@ -92,7 +91,7 @@ where /// Returns the value to which `key` is setped. /// See also [`HashSet::get`]. #[inline] - pub fn get<'g, Q>(&'g self, value: &Q) -> Option<&'g V> + pub fn get<'g, Q>(&'g self, value: &Q) -> Option<&'g T> where T: Borrow, Q: ?Sized + Hash + Eq, @@ -110,20 +109,7 @@ where /// /// See also [`HashSet::insert`]. pub fn insert(&self, value: T) -> bool { - self.set.insert(key, value, &self.guard) - } - - /// If the value for the specified `key` is present, attempts to - /// compute a new setping given the key and its current setped value. - /// See also [`HashSet::compute_if_present`]. - pub fn compute_if_present<'g, Q, F>(&'g self, key: &Q, resetping_function: F) -> Option<&'g V> - where - K: Borrow, - Q: ?Sized + Hash + Eq, - F: FnOnce(&K, &V) -> Option, - { - self.set - .compute_if_present(key, resetping_function, &self.guard) + self.set.insert(value, &self.guard) } /// Removes the key (and its corresponding value) from this set. @@ -136,31 +122,22 @@ where self.set.remove(value, &self.guard) } - pub fn take<'g, Q>(&self, value: &Q) -> Option<&'g T> + pub fn take(&self, value: &Q) -> Option<&'_ T> where T: Borrow, Q:?Sized + Hash + Eq { - self.set.take(value, self.guard) + self.set.take(value, &self.guard) } /// Retains only the elements specified by the predicate. /// See also [`HashSet::retain`]. pub fn retain(&self, f: F) where - F: FnMut(&K, &V) -> bool, + F: FnMut(&T) -> bool, { self.set.retain(f, &self.guard); } - - /// Retains only the elements specified by the predicate. - /// See also [`HashSet::retain_force`]. - pub fn retain_force(&self, f: F) - where - F: FnMut(&K, &V) -> bool, - { - self.set.retain_force(f, &self.guard); - } } impl<'g, T, S> IntoIterator for &'g HashSetRef<'_, T, S> { @@ -183,10 +160,7 @@ where impl Clone for HashSetRef<'_, T, S> { fn clone(&self) -> Self { - Self { - set: self.set.clone(), - guard: self.guard.clone() - } + self.set.pin() } } @@ -202,11 +176,10 @@ where impl PartialEq> for HashSetRef<'_, T, S> where - K: Hash + Eq, - V: PartialEq, + T: Hash + Eq, S: BuildHasher, { - fn eq(&self, other: &HashSet) -> bool { + fn eq(&self, other: &HashSet) -> bool { self.set.guarded_eq(&other, &self.guard, &other.guard()) } } @@ -216,14 +189,14 @@ where T: Hash + Eq, S: BuildHasher, { - fn eq(&self, other: &HashSetRef<'_, K, V, S>) -> bool { + fn eq(&self, other: &HashSetRef<'_, T, S>) -> bool { self.guarded_eq(&other.set, &self.guard(), &other.guard) } } impl Eq for HashSetRef<'_, T, S> where - K: Hash + Eq, + T: Hash + Eq, S: BuildHasher, { } From 7c855df26066bf555830bb86bc811dfe817fe0a4 Mon Sep 17 00:00:00 2001 From: Pablo Emilio Escobar Gaviria Date: Thu, 26 Mar 2020 10:51:48 -0300 Subject: [PATCH 03/13] Finished fixing the compile-time errors The documentation still needs work --- src/lib.rs | 1 + src/set.rs | 18 +++++++++++------- src/set_ref.rs | 11 +++++++---- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1e97e8fc..7340f053 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -229,6 +229,7 @@ pub mod iter; pub use map::{HashMap, TryInsertError}; pub use map_ref::HashMapRef; pub use set::HashSet; +pub use set_ref::HashSetRef; /// Default hasher for [`HashMap`]. pub type DefaultHashBuilder = ahash::RandomState; diff --git a/src/set.rs b/src/set.rs index 7ad9ad51..af594ce1 100644 --- a/src/set.rs +++ b/src/set.rs @@ -276,6 +276,10 @@ where { self.map.get_key_value(value, guard).map(|(k, _)| k) } + + pub(crate) fn guarded_eq(&self, other: &Self, our_guard: &Guard, their_guard: &Guard) -> bool { + self.map.guarded_eq(&other.map, our_guard, their_guard) + } } impl HashSet @@ -387,16 +391,16 @@ where /// ``` #[inline] pub fn retain(&self, mut f: F, guard: &Guard) - where - F: FnMut(&T) -> bool, + where + F: FnMut(&T) -> bool, { self.map.retain(|value, ()| f(value), guard) } } impl HashSet - where - T: Clone, +where + T: Clone, { /// Clears the set, removing all elements. /// @@ -412,12 +416,12 @@ impl HashSet /// assert!(set.pin().is_empty()); /// ``` #[inline] - pub fn clear(&self, Guard: &Guard) { + pub fn clear(&self, guard: &Guard) { self.map.clear(guard) } - /// Tries to reserve capacity for at least `additional` more elements to - /// be inserted in the `HashSet`. The collection may reserve more space to + /// Tries to reserve capacity for at least `additional` more elements to + /// be inserted in the `HashSet`. The collection may reserve more space to /// avoid frequent reallocations. #[inline] pub fn reserve(&self, additional: usize, guard: &Guard) { diff --git a/src/set_ref.rs b/src/set_ref.rs index a2aedf0c..90ecf915 100644 --- a/src/set_ref.rs +++ b/src/set_ref.rs @@ -1,5 +1,5 @@ use crate::iter::*; -use crate::{HashSet, GuardRef}; +use crate::{GuardRef, HashSet}; use crossbeam_epoch::Guard; use std::borrow::Borrow; use std::fmt::{self, Debug, Formatter}; @@ -122,10 +122,13 @@ where self.set.remove(value, &self.guard) } + /// Removes and returns the value in the set, if any, that is equal to the given one. + /// + /// See also [`HashSet::take`]. pub fn take(&self, value: &Q) -> Option<&'_ T> - where - T: Borrow, - Q:?Sized + Hash + Eq + where + T: Borrow, + Q: ?Sized + Hash + Eq, { self.set.take(value, &self.guard) } From 7bc63b04fe718900ff124086d417809c57439301 Mon Sep 17 00:00:00 2001 From: Pablo Emilio Escobar Gaviria Date: Thu, 26 Mar 2020 10:58:45 -0300 Subject: [PATCH 04/13] Fixed the documentation of `HashSetRef` --- src/set_ref.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/set_ref.rs b/src/set_ref.rs index 90ecf915..05ed519b 100644 --- a/src/set_ref.rs +++ b/src/set_ref.rs @@ -36,14 +36,14 @@ impl HashSet { } impl HashSetRef<'_, T, S> { - /// An iterator visiting all key-value pairs in arbitrary order. - /// The iterator element type is `(&'g K, &'g V)`. + /// An iterator visiting all elements in arbitrary order. + /// The iterator item type is `&'g T`. /// See also [`HashSet::iter`]. pub fn iter(&self) -> Keys<'_, T, ()> { self.set.iter(&self.guard) } - /// Returns the number of entries in the set. + /// Returns the number of elements in the set. /// See also [`HashSet::len`]. pub fn len(&self) -> usize { self.set.len() @@ -60,13 +60,13 @@ impl HashSetRef<'_, T, S> where T: Clone, { - /// Tries to reserve capacity for at least additional more elements. + /// Tries to reserve capacity for at least `additional` more elements. /// See also [`HashSet::reserve`]. pub fn reserve(&self, additional: usize) { self.set.reserve(additional, &self.guard) } - /// Removes all entries from this set. + /// Removes all elements from this set. /// See also [`HashSet::clear`]. pub fn clear(&self) { self.set.clear(&self.guard); @@ -78,8 +78,8 @@ where T: Hash + Eq, S: BuildHasher, { - /// Tests if `key` is a key in this table. - /// See also [`HashSet::contains_key`]. + /// Tests if `value` is an element of this set. + /// See also [`HashSet::contains`]. pub fn contains(&self, value: &Q) -> bool where T: Borrow, @@ -88,7 +88,8 @@ where self.set.contains(value, &self.guard) } - /// Returns the value to which `key` is setped. + /// Returns a reference to the value in the set, if any, that is equal to the given value. + /// /// See also [`HashSet::get`]. #[inline] pub fn get<'g, Q>(&'g self, value: &Q) -> Option<&'g T> @@ -105,14 +106,15 @@ where T: 'static + Sync + Send + Clone + Hash + Eq, S: BuildHasher, { - /// Inserts a key-value pair into the set. + /// Inserts an element into the set. /// /// See also [`HashSet::insert`]. pub fn insert(&self, value: T) -> bool { self.set.insert(value, &self.guard) } - /// Removes the key (and its corresponding value) from this set. + /// Removes the element from this set. + /// /// See also [`HashSet::remove`]. pub fn remove<'g, Q>(&'g self, value: &Q) -> bool where @@ -134,6 +136,7 @@ where } /// Retains only the elements specified by the predicate. + /// /// See also [`HashSet::retain`]. pub fn retain(&self, f: F) where From 6322a471cfac14c3e0c48d823d135b32630f6f49 Mon Sep 17 00:00:00 2001 From: Pablo Emilio Escobar Gaviria Date: Thu, 26 Mar 2020 11:06:28 -0300 Subject: [PATCH 05/13] Removed incorrect #[inline] annotations --- src/set.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/set.rs b/src/set.rs index af594ce1..986a7759 100644 --- a/src/set.rs +++ b/src/set.rs @@ -389,7 +389,6 @@ where /// set.pin().retain(|&e| e % 2 == 0); /// assert_eq!(set.pin().len(), 4); /// ``` - #[inline] pub fn retain(&self, mut f: F, guard: &Guard) where F: FnMut(&T) -> bool, @@ -415,15 +414,15 @@ where /// set.pin().clear(); /// assert!(set.pin().is_empty()); /// ``` - #[inline] pub fn clear(&self, guard: &Guard) { self.map.clear(guard) } /// Tries to reserve capacity for at least `additional` more elements to - /// be inserted in the `HashSet`. The collection may reserve more space to + /// be inserted in the `HashSet`. + /// + /// The collection may reserve more space to /// avoid frequent reallocations. - #[inline] pub fn reserve(&self, additional: usize, guard: &Guard) { self.map.reserve(additional, guard) } From 29803a0f1f2bf63defa3c43d14d16ddf0f61f7b5 Mon Sep 17 00:00:00 2001 From: Pablo Emilio Escobar Gaviria Date: Thu, 26 Mar 2020 12:04:23 -0300 Subject: [PATCH 06/13] Implemented `HashSet::is_disjoint`, `HashSet::is_subset` and `HashSet::is_superset` --- src/set.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/set_ref.rs | 21 +++++++++++++ 2 files changed, 104 insertions(+) diff --git a/src/set.rs b/src/set.rs index 986a7759..e55d42d1 100644 --- a/src/set.rs +++ b/src/set.rs @@ -277,6 +277,89 @@ where self.map.get_key_value(value, guard).map(|(k, _)| k) } + /// Returns `true` if `self` has no elements in common with `other`. + /// This is equivalent to checking for an empty intersection. + /// + /// # Examples + /// + /// ``` + /// use std::iter::FromIterator; + /// use flurry::HashSet; + /// + /// let a = HashSet::from_iter(&[1, 2, 3]); + /// let b = HashSet::new(); + /// let guard = a.guard(); + /// + /// assert_eq!(a.is_disjoint(&b, &guard), true); + /// b.insert(4, &guard); + /// assert_eq!(a.is_disjoint(&b, &guard), true); + /// b.insert(1, &guard); + /// assert_eq!(a.is_disjoint(&b, &guard), false); + /// + /// ``` + pub fn is_disjoint(&self, other: &HashSet, guard: &Guard) -> bool { + for value in self.iter(guard) { + if other.contains(&value, guard) { + return false; + } + } + + true + } + + /// Returns `true` if the set is a subset of another, i.e., `other` contains at least all the values in `self`. + /// + /// # Examples + /// + /// ``` + /// use std::iter::FromIterator; + /// use flurry::HashSet; + /// + /// let sup = HashSet::from_iter(&[1, 2, 3]); + /// let set = HashSet::new(); + /// let guard = sup.guard(); + /// + /// assert_eq!(set.is_subset(&sup, &guard), true); + /// set.insert(2, &guard); + /// assert_eq!(set.is_subset(&sup, &guard), true); + /// set.insert(4, &guard); + /// assert_eq!(set.is_subset(&sup, &guard), false); + /// ``` + pub fn is_subset(&self, other: &HashSet, guard: &Guard) -> bool { + for value in self.iter(guard) { + if !other.contains(&value, guard) { + return false; + } + } + + true + } + + /// Returns `true` if the set is a superset of another, i.e., `self` contains at least all the values in `other`. + /// + /// # Examples + /// + /// ``` + /// use std::iter::FromIterator; + /// use flurry::HashSet; + /// + /// let sub = HashSet::from_iter(&[1, 2]); + /// let set = HashSet::new(); + /// let guard = sub.guard(); + /// + /// assert_eq!(set.is_superset(&sub, &guard), false); + /// + /// set.insert(0, &guard); + /// set.insert(1, &guard); + /// assert_eq!(set.is_superset(&sub, &guard), false); + /// + /// set.insert(2, &guard); + /// assert_eq!(set.is_superset(&sub, &guard), true); + /// ``` + pub fn is_superset(&self, other: &HashSet, guard: &Guard) -> bool { + other.is_subset(self, guard) + } + pub(crate) fn guarded_eq(&self, other: &Self, our_guard: &Guard, their_guard: &Guard) -> bool { self.map.guarded_eq(&other.map, our_guard, their_guard) } diff --git a/src/set_ref.rs b/src/set_ref.rs index 05ed519b..b16a6337 100644 --- a/src/set_ref.rs +++ b/src/set_ref.rs @@ -99,6 +99,27 @@ where { self.set.get(value, &self.guard) } + + /// Returns `true` if `self` has no elements in common with `other`. + /// + /// See also [`HashSet::is_disjoint`]. + pub fn is_disjoint(&self, other: &HashSet) -> bool { + self.set.is_disjoint(other, &self.guard) + } + + /// Returns `true` if the set is a subset of another, i.e., `other` contains at least all the values in `self`. + /// + /// See also [`HashSet::is_subset`]. + pub fn is_subset(&self, other: &HashSet) -> bool { + self.set.is_subset(other, &self.guard) + } + + /// Returns `true` if the set is a superset of another, i.e., `self` contains at least all the values in `other`. + /// + /// See also [`HashSet::is_superset`]. + pub fn is_superset(&self, other: &HashSet) -> bool { + self.set.is_superset(other, &self.guard) + } } impl HashSetRef<'_, T, S> From fc704da8b40903a757e82071f913f336df349e83 Mon Sep 17 00:00:00 2001 From: Pablo Emilio Escobar Gaviria Date: Thu, 26 Mar 2020 12:19:42 -0300 Subject: [PATCH 07/13] Made the `HashMapRef::is_disjoint`, `HashMapRef::is_subset` and `HashMapRef::is_superset` methods generic --- src/set_ref.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/set_ref.rs b/src/set_ref.rs index b16a6337..eaf425ce 100644 --- a/src/set_ref.rs +++ b/src/set_ref.rs @@ -103,22 +103,31 @@ where /// Returns `true` if `self` has no elements in common with `other`. /// /// See also [`HashSet::is_disjoint`]. - pub fn is_disjoint(&self, other: &HashSet) -> bool { - self.set.is_disjoint(other, &self.guard) + pub fn is_disjoint(&self, other: &Q) -> bool + where + Q: AsRef>, + { + self.set.is_disjoint(other.as_ref(), &self.guard) } /// Returns `true` if the set is a subset of another, i.e., `other` contains at least all the values in `self`. /// /// See also [`HashSet::is_subset`]. - pub fn is_subset(&self, other: &HashSet) -> bool { - self.set.is_subset(other, &self.guard) + pub fn is_subset(&self, other: &Q) -> bool + where + Q: AsRef>, + { + self.set.is_subset(other.as_ref(), &self.guard) } /// Returns `true` if the set is a superset of another, i.e., `self` contains at least all the values in `other`. /// /// See also [`HashSet::is_superset`]. - pub fn is_superset(&self, other: &HashSet) -> bool { - self.set.is_superset(other, &self.guard) + pub fn is_superset(&self, other: &Q) -> bool + where + Q: AsRef>, + { + self.set.is_superset(other.as_ref(), &self.guard) } } From a4d30b87b8431f5956e26b27c06e2a35635c9a0e Mon Sep 17 00:00:00 2001 From: Pablo Emilio Escobar Gaviria Date: Thu, 26 Mar 2020 17:52:08 -0300 Subject: [PATCH 08/13] Made it so that `HashSet::is_disjoint`, `HashSet::is_subset` and `HashSet::is_superset` take two different guards (one for `self` and another one for `other`) --- src/set.rs | 65 ++++++++++++++++++++++++++++---------------------- src/set_ref.rs | 21 +++++----------- 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/src/set.rs b/src/set.rs index e55d42d1..1a721594 100644 --- a/src/set.rs +++ b/src/set.rs @@ -288,18 +288,22 @@ where /// /// let a = HashSet::from_iter(&[1, 2, 3]); /// let b = HashSet::new(); - /// let guard = a.guard(); /// - /// assert_eq!(a.is_disjoint(&b, &guard), true); - /// b.insert(4, &guard); - /// assert_eq!(a.is_disjoint(&b, &guard), true); - /// b.insert(1, &guard); - /// assert_eq!(a.is_disjoint(&b, &guard), false); - /// - /// ``` - pub fn is_disjoint(&self, other: &HashSet, guard: &Guard) -> bool { - for value in self.iter(guard) { - if other.contains(&value, guard) { + /// assert_eq!(a.pin().is_disjoint(&b.pin()), true); + /// b.pin().insert(4); + /// assert_eq!(a.pin().is_disjoint(&b.pin()), true); + /// b.pin().insert(1); + /// assert_eq!(a.pin().is_disjoint(&b.pin()), false); + /// + /// ``` + pub fn is_disjoint( + &self, + other: &HashSet, + our_guard: &Guard, + their_guard: &Guard, + ) -> bool { + for value in self.iter(our_guard) { + if other.contains(&value, their_guard) { return false; } } @@ -317,17 +321,16 @@ where /// /// let sup = HashSet::from_iter(&[1, 2, 3]); /// let set = HashSet::new(); - /// let guard = sup.guard(); /// - /// assert_eq!(set.is_subset(&sup, &guard), true); - /// set.insert(2, &guard); - /// assert_eq!(set.is_subset(&sup, &guard), true); - /// set.insert(4, &guard); - /// assert_eq!(set.is_subset(&sup, &guard), false); + /// assert_eq!(set.pin().is_subset(&sup.pin()), true); + /// set.pin().insert(2); + /// assert_eq!(set.pin().is_subset(&sup.pin()), true); + /// set.pin().insert(4); + /// assert_eq!(set.pin().is_subset(&sup.pin()), false); /// ``` - pub fn is_subset(&self, other: &HashSet, guard: &Guard) -> bool { - for value in self.iter(guard) { - if !other.contains(&value, guard) { + pub fn is_subset(&self, other: &HashSet, our_guard: &Guard, their_guard: &Guard) -> bool { + for value in self.iter(our_guard) { + if !other.contains(&value, their_guard) { return false; } } @@ -345,19 +348,23 @@ where /// /// let sub = HashSet::from_iter(&[1, 2]); /// let set = HashSet::new(); - /// let guard = sub.guard(); /// - /// assert_eq!(set.is_superset(&sub, &guard), false); + /// assert_eq!(set.pin().is_superset(&sub.pin()), false); /// - /// set.insert(0, &guard); - /// set.insert(1, &guard); - /// assert_eq!(set.is_superset(&sub, &guard), false); + /// set.pin().insert(0); + /// set.pin().insert(1); + /// assert_eq!(set.pin().is_superset(&sub.pin()), false); /// - /// set.insert(2, &guard); - /// assert_eq!(set.is_superset(&sub, &guard), true); + /// set.pin().insert(2); + /// assert_eq!(set.pin().is_superset(&sub.pin()), true); /// ``` - pub fn is_superset(&self, other: &HashSet, guard: &Guard) -> bool { - other.is_subset(self, guard) + pub fn is_superset( + &self, + other: &HashSet, + our_guard: &Guard, + their_guard: &Guard, + ) -> bool { + other.is_subset(self, their_guard, our_guard) } pub(crate) fn guarded_eq(&self, other: &Self, our_guard: &Guard, their_guard: &Guard) -> bool { diff --git a/src/set_ref.rs b/src/set_ref.rs index eaf425ce..5ee0de0b 100644 --- a/src/set_ref.rs +++ b/src/set_ref.rs @@ -103,31 +103,22 @@ where /// Returns `true` if `self` has no elements in common with `other`. /// /// See also [`HashSet::is_disjoint`]. - pub fn is_disjoint(&self, other: &Q) -> bool - where - Q: AsRef>, - { - self.set.is_disjoint(other.as_ref(), &self.guard) + pub fn is_disjoint<'other>(&self, other: &HashSetRef<'other, T, S>) -> bool { + self.set.is_disjoint(other.set, &self.guard, &other.guard) } /// Returns `true` if the set is a subset of another, i.e., `other` contains at least all the values in `self`. /// /// See also [`HashSet::is_subset`]. - pub fn is_subset(&self, other: &Q) -> bool - where - Q: AsRef>, - { - self.set.is_subset(other.as_ref(), &self.guard) + pub fn is_subset<'other>(&self, other: &HashSetRef<'other, T, S>) -> bool { + self.set.is_subset(other.set, &self.guard, &other.guard) } /// Returns `true` if the set is a superset of another, i.e., `self` contains at least all the values in `other`. /// /// See also [`HashSet::is_superset`]. - pub fn is_superset(&self, other: &Q) -> bool - where - Q: AsRef>, - { - self.set.is_superset(other.as_ref(), &self.guard) + pub fn is_superset<'other>(&self, other: &HashSetRef<'other, T, S>) -> bool { + self.set.is_superset(other.set, &self.guard, &other.guard) } } From aac8a9b9d092e9b9bcff667b92b6d3aae6799fb0 Mon Sep 17 00:00:00 2001 From: Jon Gjengset Date: Fri, 27 Mar 2020 09:22:48 -0400 Subject: [PATCH 09/13] set: Remove some unnecessary lifetime bounds The return type does not have a lifetime, so there's no concern here about capturing the lifetime of `&Q`. --- src/set.rs | 4 ++-- src/set_ref.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/set.rs b/src/set.rs index 1a721594..1f821976 100644 --- a/src/set.rs +++ b/src/set.rs @@ -395,7 +395,7 @@ where /// assert_eq!(set.insert(2, &guard), false); /// assert!(set.contains(&2, &guard)); /// ``` - pub fn insert<'g>(&'g self, value: T, guard: &'g Guard) -> bool { + pub fn insert(&self, value: T, guard: &Guard) -> bool { let old = self.map.insert(value, (), guard); old.is_none() } @@ -426,7 +426,7 @@ where /// assert!(!set.contains(&2, &guard)); /// assert_eq!(set.remove(&2, &guard), false); /// ``` - pub fn remove<'g, Q>(&'g self, value: &Q, guard: &'g Guard) -> bool + pub fn remove(&self, value: &Q, guard: &Guard) -> bool where T: Borrow, Q: ?Sized + Hash + Eq, diff --git a/src/set_ref.rs b/src/set_ref.rs index 5ee0de0b..85a24648 100644 --- a/src/set_ref.rs +++ b/src/set_ref.rs @@ -103,14 +103,14 @@ where /// Returns `true` if `self` has no elements in common with `other`. /// /// See also [`HashSet::is_disjoint`]. - pub fn is_disjoint<'other>(&self, other: &HashSetRef<'other, T, S>) -> bool { + pub fn is_disjoint(&self, other: &HashSetRef<'_, T, S>) -> bool { self.set.is_disjoint(other.set, &self.guard, &other.guard) } /// Returns `true` if the set is a subset of another, i.e., `other` contains at least all the values in `self`. /// /// See also [`HashSet::is_subset`]. - pub fn is_subset<'other>(&self, other: &HashSetRef<'other, T, S>) -> bool { + pub fn is_subset(&self, other: &HashSetRef<'_, T, S>) -> bool { self.set.is_subset(other.set, &self.guard, &other.guard) } @@ -137,7 +137,7 @@ where /// Removes the element from this set. /// /// See also [`HashSet::remove`]. - pub fn remove<'g, Q>(&'g self, value: &Q) -> bool + pub fn remove(&self, value: &Q) -> bool where T: Borrow, Q: ?Sized + Hash + Eq, From cdffd9784d4d673b366ee53e4ef8b6ee45871eb4 Mon Sep 17 00:00:00 2001 From: Jon Gjengset Date: Fri, 27 Mar 2020 09:25:13 -0400 Subject: [PATCH 10/13] set: inline contains() over get() --- src/set_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/set_ref.rs b/src/set_ref.rs index 85a24648..f567be48 100644 --- a/src/set_ref.rs +++ b/src/set_ref.rs @@ -80,6 +80,7 @@ where { /// Tests if `value` is an element of this set. /// See also [`HashSet::contains`]. + #[inline] pub fn contains(&self, value: &Q) -> bool where T: Borrow, @@ -91,7 +92,6 @@ where /// Returns a reference to the value in the set, if any, that is equal to the given value. /// /// See also [`HashSet::get`]. - #[inline] pub fn get<'g, Q>(&'g self, value: &Q) -> Option<&'g T> where T: Borrow, From 13e10476c27261cde5173d3c2a5bcde203a15869 Mon Sep 17 00:00:00 2001 From: Jon Gjengset Date: Fri, 27 Mar 2020 09:26:00 -0400 Subject: [PATCH 11/13] set: take return value should not be tied to &Q --- src/set_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/set_ref.rs b/src/set_ref.rs index f567be48..c18349b7 100644 --- a/src/set_ref.rs +++ b/src/set_ref.rs @@ -148,7 +148,7 @@ where /// Removes and returns the value in the set, if any, that is equal to the given one. /// /// See also [`HashSet::take`]. - pub fn take(&self, value: &Q) -> Option<&'_ T> + pub fn take<'g, Q>(&'g self, value: &Q) -> Option<&'g T> where T: Borrow, Q: ?Sized + Hash + Eq, From 9e21c55bbb20d519a87d1a5638bbb3f786bdf72a Mon Sep 17 00:00:00 2001 From: Jon Gjengset Date: Fri, 27 Mar 2020 09:26:20 -0400 Subject: [PATCH 12/13] Align HashSetRef impls with HashSet impls --- src/set.rs | 14 ++++++------ src/set_ref.rs | 60 ++++++++++++++++++++++++++++---------------------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/set.rs b/src/set.rs index 1f821976..f6bd9003 100644 --- a/src/set.rs +++ b/src/set.rs @@ -156,7 +156,7 @@ impl HashSet { self.map.guard() } - /// Returns the number of entries in the set. + /// Returns the number of elements in the set. /// /// # Examples /// @@ -190,9 +190,9 @@ impl HashSet { self.len() == 0 } - /// An iterator visiting all values in arbitrary order. + /// An iterator visiting all elements in arbitrary order. /// - /// The iterator element type is `(&'g K, &'g V)`. + /// The iterator element type is `&'g T`. /// /// See [`HashMap::keys`] for details. /// @@ -220,7 +220,7 @@ where T: Hash + Eq, S: BuildHasher, { - /// Returns `true` if the set contains the specified value. + /// Returns `true` if the given value is an element of this set. /// /// The value may be any borrowed form of the set's value type, but /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for @@ -250,7 +250,7 @@ where self.map.contains_key(value, guard) } - /// Returns a reference to the value in the set, if any, that is equal to the given value. + /// Returns a reference to the element in the set, if any, that is equal to the given value. /// /// The value may be any borrowed form of the set's value type, but /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for @@ -278,6 +278,7 @@ where } /// Returns `true` if `self` has no elements in common with `other`. + /// /// This is equivalent to checking for an empty intersection. /// /// # Examples @@ -511,8 +512,7 @@ where /// Tries to reserve capacity for at least `additional` more elements to /// be inserted in the `HashSet`. /// - /// The collection may reserve more space to - /// avoid frequent reallocations. + /// The collection may reserve more space to avoid frequent reallocations. pub fn reserve(&self, additional: usize, guard: &Guard) { self.map.reserve(additional, guard) } diff --git a/src/set_ref.rs b/src/set_ref.rs index c18349b7..80c9fe0a 100644 --- a/src/set_ref.rs +++ b/src/set_ref.rs @@ -36,40 +36,27 @@ impl HashSet { } impl HashSetRef<'_, T, S> { - /// An iterator visiting all elements in arbitrary order. - /// The iterator item type is `&'g T`. - /// See also [`HashSet::iter`]. - pub fn iter(&self) -> Keys<'_, T, ()> { - self.set.iter(&self.guard) - } - /// Returns the number of elements in the set. + /// /// See also [`HashSet::len`]. pub fn len(&self) -> usize { self.set.len() } /// Returns `true` if the set is empty. Otherwise returns `false`. + /// /// See also [`HashSet::is_empty`]. pub fn is_empty(&self) -> bool { self.set.is_empty() } -} -impl HashSetRef<'_, T, S> -where - T: Clone, -{ - /// Tries to reserve capacity for at least `additional` more elements. - /// See also [`HashSet::reserve`]. - pub fn reserve(&self, additional: usize) { - self.set.reserve(additional, &self.guard) - } - - /// Removes all elements from this set. - /// See also [`HashSet::clear`]. - pub fn clear(&self) { - self.set.clear(&self.guard); + /// An iterator visiting all elements in arbitrary order. + /// + /// The iterator element type is `&'g T`. + /// + /// See also [`HashSet::iter`]. + pub fn iter(&self) -> Keys<'_, T, ()> { + self.set.iter(&self.guard) } } @@ -78,7 +65,8 @@ where T: Hash + Eq, S: BuildHasher, { - /// Tests if `value` is an element of this set. + /// Returns `true` if the given value is an element of this set. + /// /// See also [`HashSet::contains`]. #[inline] pub fn contains(&self, value: &Q) -> bool @@ -89,7 +77,7 @@ where self.set.contains(value, &self.guard) } - /// Returns a reference to the value in the set, if any, that is equal to the given value. + /// Returns a reference to the element in the set, if any, that is equal to the given value. /// /// See also [`HashSet::get`]. pub fn get<'g, Q>(&'g self, value: &Q) -> Option<&'g T> @@ -127,14 +115,14 @@ where T: 'static + Sync + Send + Clone + Hash + Eq, S: BuildHasher, { - /// Inserts an element into the set. + /// Adds a value to the set. /// /// See also [`HashSet::insert`]. pub fn insert(&self, value: T) -> bool { self.set.insert(value, &self.guard) } - /// Removes the element from this set. + /// Removes a value from the set. /// /// See also [`HashSet::remove`]. pub fn remove(&self, value: &Q) -> bool @@ -167,6 +155,26 @@ where } } +impl HashSetRef<'_, T, S> +where + T: Clone, +{ + /// Clears the set, removing all elements. + /// + /// See also [`HashSet::clear`]. + pub fn clear(&self) { + self.set.clear(&self.guard); + } + + /// Tries to reserve capacity for at least `additional` more elements to + /// be inserted into the underlying `HashSet`. + /// + /// See also [`HashSet::reserve`]. + pub fn reserve(&self, additional: usize) { + self.set.reserve(additional, &self.guard) + } +} + impl<'g, T, S> IntoIterator for &'g HashSetRef<'_, T, S> { type IntoIter = Keys<'g, T, ()>; type Item = &'g T; From e24daf085acf6181e365bc2195c7e10cceaa39f4 Mon Sep 17 00:00:00 2001 From: Pablo Emilio Escobar Gaviria Date: Fri, 27 Mar 2020 10:27:42 -0300 Subject: [PATCH 13/13] Replaced `assert_eq` by `assert` in examples of the documentation of `HashSet` predicates --- src/set.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/set.rs b/src/set.rs index 1a721594..7eb8d2d1 100644 --- a/src/set.rs +++ b/src/set.rs @@ -289,11 +289,11 @@ where /// let a = HashSet::from_iter(&[1, 2, 3]); /// let b = HashSet::new(); /// - /// assert_eq!(a.pin().is_disjoint(&b.pin()), true); + /// assert!(a.pin().is_disjoint(&b.pin())); /// b.pin().insert(4); - /// assert_eq!(a.pin().is_disjoint(&b.pin()), true); + /// assert!(a.pin().is_disjoint(&b.pin())); /// b.pin().insert(1); - /// assert_eq!(a.pin().is_disjoint(&b.pin()), false); + /// assert!(!a.pin().is_disjoint(&b.pin())); /// /// ``` pub fn is_disjoint( @@ -322,11 +322,11 @@ where /// let sup = HashSet::from_iter(&[1, 2, 3]); /// let set = HashSet::new(); /// - /// assert_eq!(set.pin().is_subset(&sup.pin()), true); + /// assert!(set.pin().is_subset(&sup.pin())); /// set.pin().insert(2); - /// assert_eq!(set.pin().is_subset(&sup.pin()), true); + /// assert!(set.pin().is_subset(&sup.pin())); /// set.pin().insert(4); - /// assert_eq!(set.pin().is_subset(&sup.pin()), false); + /// assert!(!set.pin().is_subset(&sup.pin())); /// ``` pub fn is_subset(&self, other: &HashSet, our_guard: &Guard, their_guard: &Guard) -> bool { for value in self.iter(our_guard) { @@ -349,14 +349,14 @@ where /// let sub = HashSet::from_iter(&[1, 2]); /// let set = HashSet::new(); /// - /// assert_eq!(set.pin().is_superset(&sub.pin()), false); + /// assert!(!set.pin().is_superset(&sub.pin())); /// /// set.pin().insert(0); /// set.pin().insert(1); - /// assert_eq!(set.pin().is_superset(&sub.pin()), false); + /// assert!(!set.pin().is_superset(&sub.pin())); /// /// set.pin().insert(2); - /// assert_eq!(set.pin().is_superset(&sub.pin()), true); + /// assert!(set.pin().is_superset(&sub.pin())); /// ``` pub fn is_superset( &self,