From f3f4e80d87b5423eb3e6b5dabe755f17bcb5574e Mon Sep 17 00:00:00 2001 From: Zachary Harrold Date: Mon, 7 Apr 2025 03:52:49 +1000 Subject: [PATCH] Newtype `hashbrown` (#18694) # Objective - Fixes #18690 - Closes [#2065](https://github.com/bevyengine/bevy-website/pull/2065) - Alternative to #18691 The changes to the Hash made in #15801 to the [BuildHasher](https://doc.rust-lang.org/std/hash/trait.BuildHasher.html) resulted in serious migration problems and downgraded UX for users of Bevy's re-exported hashmaps. Once merged, we need to go in and remove the migration guide added as part of #15801. ## Solution - Newtype `HashMap` and `HashSet` instead of type aliases - Added `Deref/Mut` to allow accessing future `hashbrown` methods without maintenance from Bevy - Added bidirectional `From` implementations to provide escape hatch for API incompatibility - Added inlinable re-exports of all methods directly to Bevy's types. This ensures `HashMap::new()` works (since the `Deref` implementation wont cover these kinds of invocations). ## Testing - CI --- ## Migration Guide - If you relied on Bevy's `HashMap` and/or `HashSet` types to be identical to `hashbrown`, consider using `From` and `Into` to convert between the `hashbrown` and Bevy types as required. - If you relied on `hashbrown/serde` or `hashbrown/rayon` features, you may need to enable `bevy_platform_support/serialize` and/or `bevy_platform_support/rayon` respectively. --- ## Notes - Did not replicate the Rayon traits, users will need to rely on the `Deref/Mut` or `From` implementations for those methods. - Did not re-expose the `unsafe` methods from `hashbrown`. In most cases users will still have access via `Deref/Mut` anyway. - I have added `inline` to all methods as they are trivial wrappings of existing methods. - I chose to make `HashMap::new` and `HashSet::new` const, which is different to `hashbrown`. We can do this because we default to a fixed-state build-hasher. Mild ergonomic win over using `HashMap::with_hasher(FixedHasher)`. --- crates/bevy_picking/src/hover.rs | 4 +- crates/bevy_platform_support/Cargo.toml | 10 +- .../bevy_platform_support/src/collections.rs | 68 - .../src/collections/hash_map.rs | 1287 +++++++++++++++++ .../src/collections/hash_set.rs | 1078 ++++++++++++++ .../src/collections/hash_table.rs | 6 + .../src/collections/mod.rs | 12 + crates/bevy_scene/src/scene_spawner.rs | 5 +- 8 files changed, 2393 insertions(+), 77 deletions(-) delete mode 100644 crates/bevy_platform_support/src/collections.rs create mode 100644 crates/bevy_platform_support/src/collections/hash_map.rs create mode 100644 crates/bevy_platform_support/src/collections/hash_set.rs create mode 100644 crates/bevy_platform_support/src/collections/hash_table.rs create mode 100644 crates/bevy_platform_support/src/collections/mod.rs diff --git a/crates/bevy_picking/src/hover.rs b/crates/bevy_picking/src/hover.rs index 5e5e715890..b88d0dbebd 100644 --- a/crates/bevy_picking/src/hover.rs +++ b/crates/bevy_picking/src/hover.rs @@ -131,9 +131,7 @@ fn build_over_map( .filter(|e| !cancelled_pointers.contains(&e.pointer)) { let pointer = entities_under_pointer.pointer; - let layer_map = pointer_over_map - .entry(pointer) - .or_insert_with(BTreeMap::new); + let layer_map = pointer_over_map.entry(pointer).or_default(); for (entity, pick_data) in entities_under_pointer.picks.iter() { let layer = entities_under_pointer.order; let hits = layer_map.entry(FloatOrd(layer)).or_default(); diff --git a/crates/bevy_platform_support/Cargo.toml b/crates/bevy_platform_support/Cargo.toml index 5efadec74d..cfcee2f243 100644 --- a/crates/bevy_platform_support/Cargo.toml +++ b/crates/bevy_platform_support/Cargo.toml @@ -14,7 +14,10 @@ default = ["std"] # Functionality ## Adds serialization support through `serde`. -serialize = ["hashbrown/serde"] +serialize = ["dep:serde", "hashbrown/serde"] + +## Adds integration with Rayon. +rayon = ["dep:rayon", "hashbrown/rayon"] # Platform Compatibility @@ -28,10 +31,11 @@ std = [ "portable-atomic-util/std", "spin/std", "foldhash/std", + "serde?/std", ] ## Allows access to the `alloc` crate. -alloc = ["portable-atomic-util/alloc", "dep:hashbrown"] +alloc = ["portable-atomic-util/alloc", "dep:hashbrown", "serde?/alloc"] ## `critical-section` provides the building blocks for synchronization primitives ## on all platforms, including `no_std`. @@ -57,6 +61,8 @@ hashbrown = { version = "0.15.1", features = [ "equivalent", "raw-entry", ], optional = true, default-features = false } +serde = { version = "1", default-features = false, optional = true } +rayon = { version = "1", default-features = false, optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] web-time = { version = "1.1", default-features = false, optional = true } diff --git a/crates/bevy_platform_support/src/collections.rs b/crates/bevy_platform_support/src/collections.rs deleted file mode 100644 index d3f11431e4..0000000000 --- a/crates/bevy_platform_support/src/collections.rs +++ /dev/null @@ -1,68 +0,0 @@ -//! Provides [`HashMap`] and [`HashSet`] from [`hashbrown`] with some customized defaults. -//! -//! Also provides the [`HashTable`] type, which is specific to [`hashbrown`]. -//! -//! Note that due to the implementation details of [`hashbrown`], [`HashMap::new`] is only implemented for `HashMap`. -//! Whereas, Bevy exports `HashMap` as its default [`HashMap`] type, meaning [`HashMap::new`] will typically fail. -//! To bypass this issue, use [`HashMap::default`] instead. - -pub use hash_map::HashMap; -pub use hash_set::HashSet; -pub use hash_table::HashTable; -pub use hashbrown::Equivalent; - -pub mod hash_map { - //! Provides [`HashMap`] - - use crate::hash::FixedHasher; - use hashbrown::hash_map as hb; - - // Re-exports to match `std::collections::hash_map` - pub use { - crate::hash::{DefaultHasher, RandomState}, - hb::{ - Drain, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, OccupiedEntry, VacantEntry, - Values, ValuesMut, - }, - }; - - // Additional items from `hashbrown` - pub use hb::{ - EntryRef, ExtractIf, OccupiedError, RawEntryBuilder, RawEntryBuilderMut, RawEntryMut, - RawOccupiedEntryMut, - }; - - /// Shortcut for [`HashMap`](hb::HashMap) with [`FixedHasher`] as the default hashing provider. - pub type HashMap = hb::HashMap; - - /// Shortcut for [`Entry`](hb::Entry) with [`FixedHasher`] as the default hashing provider. - pub type Entry<'a, K, V, S = FixedHasher> = hb::Entry<'a, K, V, S>; -} - -pub mod hash_set { - //! Provides [`HashSet`] - - use crate::hash::FixedHasher; - use hashbrown::hash_set as hb; - - // Re-exports to match `std::collections::hash_set` - pub use hb::{Difference, Drain, Intersection, IntoIter, Iter, SymmetricDifference, Union}; - - // Additional items from `hashbrown` - pub use hb::{ExtractIf, OccupiedEntry, VacantEntry}; - - /// Shortcut for [`HashSet`](hb::HashSet) with [`FixedHasher`] as the default hashing provider. - pub type HashSet = hb::HashSet; - - /// Shortcut for [`Entry`](hb::Entry) with [`FixedHasher`] as the default hashing provider. - pub type Entry<'a, T, S = FixedHasher> = hb::Entry<'a, T, S>; -} - -pub mod hash_table { - //! Provides [`HashTable`] - - pub use hashbrown::hash_table::{ - AbsentEntry, Drain, Entry, ExtractIf, HashTable, IntoIter, Iter, IterHash, IterHashMut, - IterMut, OccupiedEntry, VacantEntry, - }; -} diff --git a/crates/bevy_platform_support/src/collections/hash_map.rs b/crates/bevy_platform_support/src/collections/hash_map.rs new file mode 100644 index 0000000000..fe9c09d69c --- /dev/null +++ b/crates/bevy_platform_support/src/collections/hash_map.rs @@ -0,0 +1,1287 @@ +//! Provides [`HashMap`] based on [hashbrown]'s implementation. +//! Unlike [`hashbrown::HashMap`], [`HashMap`] defaults to [`FixedHasher`] +//! instead of [`RandomState`]. +//! This provides determinism by default with an acceptable compromise to denial +//! of service resistance in the context of a game engine. + +use core::{ + fmt::Debug, + hash::{BuildHasher, Hash}, + ops::{Deref, DerefMut, Index}, +}; + +use hashbrown::{hash_map as hb, Equivalent}; + +use crate::hash::FixedHasher; + +#[cfg(feature = "rayon")] +use rayon::prelude::{FromParallelIterator, IntoParallelIterator, ParallelExtend}; + +// Re-exports to match `std::collections::hash_map` +pub use { + crate::hash::{DefaultHasher, RandomState}, + hb::{ + Drain, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, OccupiedEntry, VacantEntry, + Values, ValuesMut, + }, +}; + +// Additional items from `hashbrown` +pub use hb::{ + EntryRef, ExtractIf, OccupiedError, RawEntryBuilder, RawEntryBuilderMut, RawEntryMut, + RawOccupiedEntryMut, +}; + +/// Shortcut for [`Entry`](hb::Entry) with [`FixedHasher`] as the default hashing provider. +pub type Entry<'a, K, V, S = FixedHasher> = hb::Entry<'a, K, V, S>; + +/// New-type for [`HashMap`](hb::HashMap) with [`FixedHasher`] as the default hashing provider. +/// Can be trivially converted to and from a [hashbrown] [`HashMap`](hb::HashMap) using [`From`]. +/// +/// A new-type is used instead of a type alias due to critical methods like [`new`](hb::HashMap::new) +/// being incompatible with Bevy's choice of default hasher. +#[repr(transparent)] +pub struct HashMap(hb::HashMap); + +impl Clone for HashMap +where + hb::HashMap: Clone, +{ + #[inline] + fn clone(&self) -> Self { + Self(self.0.clone()) + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + self.0.clone_from(&source.0); + } +} + +impl Debug for HashMap +where + hb::HashMap: Debug, +{ + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + as Debug>::fmt(&self.0, f) + } +} + +impl Default for HashMap +where + hb::HashMap: Default, +{ + #[inline] + fn default() -> Self { + Self(Default::default()) + } +} + +impl PartialEq for HashMap +where + hb::HashMap: PartialEq, +{ + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + +impl Eq for HashMap where hb::HashMap: Eq {} + +impl FromIterator for HashMap +where + hb::HashMap: FromIterator, +{ + #[inline] + fn from_iter>(iter: U) -> Self { + Self(FromIterator::from_iter(iter)) + } +} + +impl Index for HashMap +where + hb::HashMap: Index, +{ + type Output = as Index>::Output; + + #[inline] + fn index(&self, index: T) -> &Self::Output { + self.0.index(index) + } +} + +impl IntoIterator for HashMap +where + hb::HashMap: IntoIterator, +{ + type Item = as IntoIterator>::Item; + + type IntoIter = as IntoIterator>::IntoIter; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a, K, V, S> IntoIterator for &'a HashMap +where + &'a hb::HashMap: IntoIterator, +{ + type Item = <&'a hb::HashMap as IntoIterator>::Item; + + type IntoIter = <&'a hb::HashMap as IntoIterator>::IntoIter; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + (&self.0).into_iter() + } +} + +impl<'a, K, V, S> IntoIterator for &'a mut HashMap +where + &'a mut hb::HashMap: IntoIterator, +{ + type Item = <&'a mut hb::HashMap as IntoIterator>::Item; + + type IntoIter = <&'a mut hb::HashMap as IntoIterator>::IntoIter; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + (&mut self.0).into_iter() + } +} + +impl Extend for HashMap +where + hb::HashMap: Extend, +{ + #[inline] + fn extend>(&mut self, iter: U) { + self.0.extend(iter); + } +} + +impl From<[(K, V); N]> for HashMap +where + K: Eq + Hash, +{ + fn from(arr: [(K, V); N]) -> Self { + arr.into_iter().collect() + } +} + +impl From> for HashMap { + #[inline] + fn from(value: hb::HashMap) -> Self { + Self(value) + } +} + +impl From> for hb::HashMap { + #[inline] + fn from(value: HashMap) -> Self { + value.0 + } +} + +impl Deref for HashMap { + type Target = hb::HashMap; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for HashMap { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for HashMap +where + hb::HashMap: serde::Serialize, +{ + #[inline] + fn serialize(&self, serializer: T) -> Result + where + T: serde::Serializer, + { + self.0.serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de, K, V, S> serde::Deserialize<'de> for HashMap +where + hb::HashMap: serde::Deserialize<'de>, +{ + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(Self(serde::Deserialize::deserialize(deserializer)?)) + } +} + +#[cfg(feature = "rayon")] +impl FromParallelIterator for HashMap +where + hb::HashMap: FromParallelIterator, + T: Send, +{ + fn from_par_iter

(par_iter: P) -> Self + where + P: IntoParallelIterator, + { + Self( as FromParallelIterator>::from_par_iter(par_iter)) + } +} + +#[cfg(feature = "rayon")] +impl IntoParallelIterator for HashMap +where + hb::HashMap: IntoParallelIterator, +{ + type Item = as IntoParallelIterator>::Item; + type Iter = as IntoParallelIterator>::Iter; + + fn into_par_iter(self) -> Self::Iter { + self.0.into_par_iter() + } +} + +#[cfg(feature = "rayon")] +impl<'a, K: Sync, V: Sync, S> IntoParallelIterator for &'a HashMap +where + &'a hb::HashMap: IntoParallelIterator, +{ + type Item = <&'a hb::HashMap as IntoParallelIterator>::Item; + type Iter = <&'a hb::HashMap as IntoParallelIterator>::Iter; + + fn into_par_iter(self) -> Self::Iter { + (&self.0).into_par_iter() + } +} + +#[cfg(feature = "rayon")] +impl<'a, K: Sync, V: Sync, S> IntoParallelIterator for &'a mut HashMap +where + &'a mut hb::HashMap: IntoParallelIterator, +{ + type Item = <&'a mut hb::HashMap as IntoParallelIterator>::Item; + type Iter = <&'a mut hb::HashMap as IntoParallelIterator>::Iter; + + fn into_par_iter(self) -> Self::Iter { + (&mut self.0).into_par_iter() + } +} + +#[cfg(feature = "rayon")] +impl ParallelExtend for HashMap +where + hb::HashMap: ParallelExtend, + T: Send, +{ + fn par_extend(&mut self, par_iter: I) + where + I: IntoParallelIterator, + { + as ParallelExtend>::par_extend(&mut self.0, par_iter); + } +} + +impl HashMap { + /// Creates an empty [`HashMap`]. + /// + /// Refer to [`new`](hb::HashMap::new) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # + /// // Creates a HashMap with zero capacity. + /// let map = HashMap::new(); + /// # + /// # let mut map = map; + /// # map.insert(0usize, "foo"); + /// # assert_eq!(map.get(&0), Some("foo").as_ref()); + /// ``` + #[inline] + pub const fn new() -> Self { + Self::with_hasher(FixedHasher) + } + + /// Creates an empty [`HashMap`] with the specified capacity. + /// + /// Refer to [`with_capacity`](hb::HashMap::with_capacity) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # + /// // Creates a HashMap with capacity for at least 5 entries. + /// let map = HashMap::with_capacity(5); + /// # + /// # let mut map = map; + /// # map.insert(0usize, "foo"); + /// # assert_eq!(map.get(&0), Some("foo").as_ref()); + /// ``` + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + Self::with_capacity_and_hasher(capacity, FixedHasher) + } +} + +impl HashMap { + /// Creates an empty [`HashMap`] which will use the given hash builder to hash + /// keys. + /// + /// Refer to [`with_hasher`](hb::HashMap::with_hasher) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # use bevy_platform_support::hash::FixedHasher as SomeHasher; + /// // Creates a HashMap with the provided hasher. + /// let map = HashMap::with_hasher(SomeHasher); + /// # + /// # let mut map = map; + /// # map.insert(0usize, "foo"); + /// # assert_eq!(map.get(&0), Some("foo").as_ref()); + /// ``` + #[inline] + pub const fn with_hasher(hash_builder: S) -> Self { + Self(hb::HashMap::with_hasher(hash_builder)) + } + + /// Creates an empty [`HashMap`] with the specified capacity, using `hash_builder` + /// to hash the keys. + /// + /// Refer to [`with_capacity_and_hasher`](hb::HashMap::with_capacity_and_hasher) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # use bevy_platform_support::hash::FixedHasher as SomeHasher; + /// // Creates a HashMap with capacity for 5 entries and the provided hasher. + /// let map = HashMap::with_capacity_and_hasher(5, SomeHasher); + /// # + /// # let mut map = map; + /// # map.insert(0usize, "foo"); + /// # assert_eq!(map.get(&0), Some("foo").as_ref()); + /// ``` + #[inline] + pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> Self { + Self(hb::HashMap::with_capacity_and_hasher( + capacity, + hash_builder, + )) + } + + /// Returns a reference to the map's [`BuildHasher`], or `S` parameter. + /// + /// Refer to [`hasher`](hb::HashMap::hasher) for further details. + #[inline] + pub fn hasher(&self) -> &S { + self.0.hasher() + } + + /// Returns the number of elements the map can hold without reallocating. + /// + /// Refer to [`capacity`](hb::HashMap::capacity) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let map = HashMap::with_capacity(5); + /// + /// # let map: HashMap<(), ()> = map; + /// # + /// assert!(map.capacity() >= 5); + /// ``` + #[inline] + pub fn capacity(&self) -> usize { + self.0.capacity() + } + + /// An iterator visiting all keys in arbitrary order. + /// The iterator element type is `&'a K`. + /// + /// Refer to [`keys`](hb::HashMap::keys) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// for key in map.keys() { + /// // foo, bar, baz + /// // Note that the above order is not guaranteed + /// } + /// # + /// # assert_eq!(map.keys().count(), 3); + /// ``` + #[inline] + pub fn keys(&self) -> Keys<'_, K, V> { + self.0.keys() + } + + /// An iterator visiting all values in arbitrary order. + /// The iterator element type is `&'a V`. + /// + /// Refer to [`values`](hb::HashMap::values) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// for key in map.values() { + /// // 0, 1, 2 + /// // Note that the above order is not guaranteed + /// } + /// # + /// # assert_eq!(map.values().count(), 3); + /// ``` + #[inline] + pub fn values(&self) -> Values<'_, K, V> { + self.0.values() + } + + /// An iterator visiting all values mutably in arbitrary order. + /// The iterator element type is `&'a mut V`. + /// + /// Refer to [`values`](hb::HashMap::values) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// for key in map.values_mut() { + /// // 0, 1, 2 + /// // Note that the above order is not guaranteed + /// } + /// # + /// # assert_eq!(map.values_mut().count(), 3); + /// ``` + #[inline] + pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> { + self.0.values_mut() + } + + /// An iterator visiting all key-value pairs in arbitrary order. + /// The iterator element type is `(&'a K, &'a V)`. + /// + /// Refer to [`iter`](hb::HashMap::iter) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// for (key, value) in map.iter() { + /// // ("foo", 0), ("bar", 1), ("baz", 2) + /// // Note that the above order is not guaranteed + /// } + /// # + /// # assert_eq!(map.iter().count(), 3); + /// ``` + #[inline] + pub fn iter(&self) -> Iter<'_, K, V> { + self.0.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)`. + /// + /// Refer to [`iter_mut`](hb::HashMap::iter_mut) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// for (key, value) in map.iter_mut() { + /// // ("foo", 0), ("bar", 1), ("baz", 2) + /// // Note that the above order is not guaranteed + /// } + /// # + /// # assert_eq!(map.iter_mut().count(), 3); + /// ``` + #[inline] + pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { + self.0.iter_mut() + } + + /// Returns the number of elements in the map. + /// + /// Refer to [`len`](hb::HashMap::len) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// assert_eq!(map.len(), 0); + /// + /// map.insert("foo", 0); + /// + /// assert_eq!(map.len(), 1); + /// ``` + #[inline] + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns `true` if the map contains no elements. + /// + /// Refer to [`is_empty`](hb::HashMap::is_empty) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// assert!(map.is_empty()); + /// + /// map.insert("foo", 0); + /// + /// assert!(!map.is_empty()); + /// ``` + #[inline] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Clears the map, returning all key-value pairs as an iterator. Keeps the + /// allocated memory for reuse. + /// + /// Refer to [`drain`](hb::HashMap::drain) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// for (key, value) in map.drain() { + /// // ("foo", 0), ("bar", 1), ("baz", 2) + /// // Note that the above order is not guaranteed + /// } + /// + /// assert!(map.is_empty()); + /// ``` + #[inline] + pub fn drain(&mut self) -> Drain<'_, K, V> { + self.0.drain() + } + + /// Retains only the elements specified by the predicate. Keeps the + /// allocated memory for reuse. + /// + /// Refer to [`retain`](hb::HashMap::retain) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// map.retain(|key, value| *value == 2); + /// + /// assert_eq!(map.len(), 1); + /// ``` + #[inline] + pub fn retain(&mut self, f: F) + where + F: FnMut(&K, &mut V) -> bool, + { + self.0.retain(f); + } + + /// Drains elements which are true under the given predicate, + /// and returns an iterator over the removed items. + /// + /// Refer to [`extract_if`](hb::HashMap::extract_if) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// let extracted = map + /// .extract_if(|key, value| *value == 2) + /// .collect::>(); + /// + /// assert_eq!(map.len(), 2); + /// assert_eq!(extracted.len(), 1); + /// ``` + #[inline] + pub fn extract_if(&mut self, f: F) -> ExtractIf<'_, K, V, F> + where + F: FnMut(&K, &mut V) -> bool, + { + self.0.extract_if(f) + } + + /// Clears the map, removing all key-value pairs. Keeps the allocated memory + /// for reuse. + /// + /// Refer to [`clear`](hb::HashMap::clear) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// map.clear(); + /// + /// assert!(map.is_empty()); + /// ``` + #[inline] + pub fn clear(&mut self) { + self.0.clear(); + } + + /// Creates a consuming iterator visiting all the keys in arbitrary order. + /// The map cannot be used after calling this. + /// The iterator element type is `K`. + /// + /// Refer to [`into_keys`](hb::HashMap::into_keys) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// for key in map.into_keys() { + /// // "foo", "bar", "baz" + /// // Note that the above order is not guaranteed + /// } + /// ``` + #[inline] + pub fn into_keys(self) -> IntoKeys { + self.0.into_keys() + } + + /// Creates a consuming iterator visiting all the values in arbitrary order. + /// The map cannot be used after calling this. + /// The iterator element type is `V`. + /// + /// Refer to [`into_values`](hb::HashMap::into_values) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// # + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// for key in map.into_values() { + /// // 0, 1, 2 + /// // Note that the above order is not guaranteed + /// } + /// ``` + #[inline] + pub fn into_values(self) -> IntoValues { + self.0.into_values() + } + + /// Takes the inner [`HashMap`](hb::HashMap) out of this wrapper. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let map: HashMap<&'static str, usize> = HashMap::new(); + /// let map: hashbrown::HashMap<&'static str, usize, _> = map.into_inner(); + /// ``` + #[inline] + pub fn into_inner(self) -> hb::HashMap { + self.0 + } +} + +impl HashMap +where + K: Eq + Hash, + S: BuildHasher, +{ + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the [`HashMap`]. The collection may reserve more space to avoid + /// frequent reallocations. + /// + /// Refer to [`reserve`](hb::HashMap::reserve) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::with_capacity(5); + /// + /// # let mut map: HashMap<(), ()> = map; + /// # + /// assert!(map.capacity() >= 5); + /// + /// map.reserve(10); + /// + /// assert!(map.capacity() - map.len() >= 10); + /// ``` + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.0.reserve(additional); + } + + /// Tries to reserve capacity for at least `additional` more elements to be inserted + /// in the given `HashMap`. The collection may reserve more space to avoid + /// frequent reallocations. + /// + /// Refer to [`try_reserve`](hb::HashMap::try_reserve) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::with_capacity(5); + /// + /// # let mut map: HashMap<(), ()> = map; + /// # + /// assert!(map.capacity() >= 5); + /// + /// map.try_reserve(10).expect("Out of Memory!"); + /// + /// assert!(map.capacity() - map.len() >= 10); + /// ``` + #[inline] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), hashbrown::TryReserveError> { + self.0.try_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. + /// + /// Refer to [`shrink_to_fit`](hb::HashMap::shrink_to_fit) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::with_capacity(5); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// assert!(map.capacity() >= 5); + /// + /// map.shrink_to_fit(); + /// + /// assert_eq!(map.capacity(), 3); + /// ``` + #[inline] + pub fn shrink_to_fit(&mut self) { + self.0.shrink_to_fit(); + } + + /// Shrinks the capacity of the map with a lower limit. It will drop + /// down no lower than the supplied limit while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + /// + /// Refer to [`shrink_to`](hb::HashMap::shrink_to) for further details. + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.0.shrink_to(min_capacity); + } + + /// Gets the given key's corresponding entry in the map for in-place manipulation. + /// + /// Refer to [`entry`](hb::HashMap::entry) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// let value = map.entry("foo").or_insert(0); + /// # + /// # assert_eq!(*value, 0); + /// ``` + #[inline] + pub fn entry(&mut self, key: K) -> Entry<'_, K, V, S> { + self.0.entry(key) + } + + /// Gets the given key's corresponding entry by reference in the map for in-place manipulation. + /// + /// Refer to [`entry_ref`](hb::HashMap::entry_ref) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// # let mut map: HashMap<&'static str, usize> = map; + /// + /// let value = map.entry_ref("foo").or_insert(0); + /// # + /// # assert_eq!(*value, 0); + /// ``` + #[inline] + pub fn entry_ref<'a, 'b, Q>(&'a mut self, key: &'b Q) -> EntryRef<'a, 'b, K, Q, V, S> + where + Q: Hash + Equivalent + ?Sized, + { + self.0.entry_ref(key) + } + + /// Returns a reference to the value corresponding to the key. + /// + /// Refer to [`get`](hb::HashMap::get) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// + /// assert_eq!(map.get("foo"), Some(&0)); + /// ``` + #[inline] + pub fn get(&self, k: &Q) -> Option<&V> + where + Q: Hash + Equivalent + ?Sized, + { + self.0.get(k) + } + + /// Returns the key-value pair corresponding to the supplied key. + /// + /// Refer to [`get_key_value`](hb::HashMap::get_key_value) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// + /// assert_eq!(map.get_key_value("foo"), Some((&"foo", &0))); + /// ``` + #[inline] + pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> + where + Q: Hash + Equivalent + ?Sized, + { + self.0.get_key_value(k) + } + + /// Returns the key-value pair corresponding to the supplied key, with a mutable reference to value. + /// + /// Refer to [`get_key_value_mut`](hb::HashMap::get_key_value_mut) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// + /// assert_eq!(map.get_key_value_mut("foo"), Some((&"foo", &mut 0))); + /// ``` + #[inline] + pub fn get_key_value_mut(&mut self, k: &Q) -> Option<(&K, &mut V)> + where + Q: Hash + Equivalent + ?Sized, + { + self.0.get_key_value_mut(k) + } + + /// Returns `true` if the map contains a value for the specified key. + /// + /// Refer to [`contains_key`](hb::HashMap::contains_key) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// + /// assert!(map.contains_key("foo")); + /// ``` + #[inline] + pub fn contains_key(&self, k: &Q) -> bool + where + Q: Hash + Equivalent + ?Sized, + { + self.0.contains_key(k) + } + + /// Returns a mutable reference to the value corresponding to the key. + /// + /// Refer to [`get_mut`](hb::HashMap::get_mut) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// + /// assert_eq!(map.get_mut("foo"), Some(&mut 0)); + /// ``` + #[inline] + pub fn get_mut(&mut self, k: &Q) -> Option<&mut V> + where + Q: Hash + Equivalent + ?Sized, + { + self.0.get_mut(k) + } + + /// Attempts to get mutable references to `N` values in the map at once. + /// + /// Refer to [`get_many_mut`](hb::HashMap::get_many_mut) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// let result = map.get_many_mut(["foo", "bar"]); + /// + /// assert_eq!(result, [Some(&mut 0), Some(&mut 1)]); + /// ``` + #[inline] + pub fn get_many_mut(&mut self, ks: [&Q; N]) -> [Option<&'_ mut V>; N] + where + Q: Hash + Equivalent + ?Sized, + { + self.0.get_many_mut(ks) + } + + /// Attempts to get mutable references to `N` values in the map at once, with immutable + /// references to the corresponding keys. + /// + /// Refer to [`get_many_key_value_mut`](hb::HashMap::get_many_key_value_mut) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// map.insert("bar", 1); + /// map.insert("baz", 2); + /// + /// let result = map.get_many_key_value_mut(["foo", "bar"]); + /// + /// assert_eq!(result, [Some((&"foo", &mut 0)), Some((&"bar", &mut 1))]); + /// ``` + #[inline] + pub fn get_many_key_value_mut( + &mut self, + ks: [&Q; N], + ) -> [Option<(&'_ K, &'_ mut V)>; N] + where + Q: Hash + Equivalent + ?Sized, + { + self.0.get_many_key_value_mut(ks) + } + + /// Inserts a key-value pair into the map. + /// + /// Refer to [`insert`](hb::HashMap::insert) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// + /// assert_eq!(map.get("foo"), Some(&0)); + /// ``` + #[inline] + pub fn insert(&mut self, k: K, v: V) -> Option { + self.0.insert(k, v) + } + + /// Tries to insert a key-value pair into the map, and returns + /// a mutable reference to the value in the entry. + /// + /// Refer to [`try_insert`](hb::HashMap::try_insert) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// map.try_insert("foo", 0).unwrap(); + /// + /// assert!(map.try_insert("foo", 1).is_err()); + /// ``` + #[inline] + pub fn try_insert(&mut self, key: K, value: V) -> Result<&mut V, OccupiedError<'_, K, V, S>> { + self.0.try_insert(key, value) + } + + /// Removes a key from the map, returning the value at the key if the key + /// was previously in the map. Keeps the allocated memory for reuse. + /// + /// Refer to [`remove`](hb::HashMap::remove) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// + /// assert_eq!(map.remove("foo"), Some(0)); + /// + /// assert!(map.is_empty()); + /// ``` + #[inline] + pub fn remove(&mut self, k: &Q) -> Option + where + Q: Hash + Equivalent + ?Sized, + { + self.0.remove(k) + } + + /// Removes a key from the map, returning the stored key and value if the + /// key was previously in the map. Keeps the allocated memory for reuse. + /// + /// Refer to [`remove_entry`](hb::HashMap::remove_entry) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// map.insert("foo", 0); + /// + /// assert_eq!(map.remove_entry("foo"), Some(("foo", 0))); + /// + /// assert!(map.is_empty()); + /// ``` + #[inline] + pub fn remove_entry(&mut self, k: &Q) -> Option<(K, V)> + where + Q: Hash + Equivalent + ?Sized, + { + self.0.remove_entry(k) + } + + /// Returns the total amount of memory allocated internally by the hash + /// set, in bytes. + /// + /// Refer to [`allocation_size`](hb::HashMap::allocation_size) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashMap; + /// let mut map = HashMap::new(); + /// + /// assert_eq!(map.allocation_size(), 0); + /// + /// map.insert("foo", 0u32); + /// + /// assert!(map.allocation_size() >= size_of::<&'static str>() + size_of::()); + /// ``` + #[inline] + pub fn allocation_size(&self) -> usize { + self.0.allocation_size() + } + + /// Insert a key-value pair into the map without checking + /// if the key already exists in the map. + /// + /// Refer to [`insert_unique_unchecked`](hb::HashMap::insert_unique_unchecked) for further details. + /// + /// # Safety + /// + /// This operation is safe if a key does not exist in the map. + /// + /// However, if a key exists in the map already, the behavior is unspecified: + /// this operation may panic, loop forever, or any following operation with the map + /// may panic, loop forever or return arbitrary result. + /// + /// That said, this operation (and following operations) are guaranteed to + /// not violate memory safety. + /// + /// However this operation is still unsafe because the resulting `HashMap` + /// may be passed to unsafe code which does expect the map to behave + /// correctly, and would cause unsoundness as a result. + #[expect( + unsafe_code, + reason = "re-exporting unsafe method from Hashbrown requires unsafe code" + )] + #[inline] + pub unsafe fn insert_unique_unchecked(&mut self, key: K, value: V) -> (&K, &mut V) { + // SAFETY: safety contract is ensured by the caller. + unsafe { self.0.insert_unique_unchecked(key, value) } + } + + /// Attempts to get mutable references to `N` values in the map at once, without validating that + /// the values are unique. + /// + /// Refer to [`get_many_unchecked_mut`](hb::HashMap::get_many_unchecked_mut) for further details. + /// + /// Returns an array of length `N` with the results of each query. `None` will be used if + /// the key is missing. + /// + /// For a safe alternative see [`get_many_mut`](`HashMap::get_many_mut`). + /// + /// # Safety + /// + /// Calling this method with overlapping keys is *[undefined behavior]* even if the resulting + /// references are not used. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + #[expect( + unsafe_code, + reason = "re-exporting unsafe method from Hashbrown requires unsafe code" + )] + #[inline] + pub unsafe fn get_many_unchecked_mut( + &mut self, + keys: [&Q; N], + ) -> [Option<&'_ mut V>; N] + where + Q: Hash + Equivalent + ?Sized, + { + // SAFETY: safety contract is ensured by the caller. + unsafe { self.0.get_many_unchecked_mut(keys) } + } + + /// Attempts to get mutable references to `N` values in the map at once, with immutable + /// references to the corresponding keys, without validating that the values are unique. + /// + /// Refer to [`get_many_key_value_unchecked_mut`](hb::HashMap::get_many_key_value_unchecked_mut) for further details. + /// + /// Returns an array of length `N` with the results of each query. `None` will be returned if + /// any of the keys are missing. + /// + /// For a safe alternative see [`get_many_key_value_mut`](`HashMap::get_many_key_value_mut`). + /// + /// # Safety + /// + /// Calling this method with overlapping keys is *[undefined behavior]* even if the resulting + /// references are not used. + /// + /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html + #[expect( + unsafe_code, + reason = "re-exporting unsafe method from Hashbrown requires unsafe code" + )] + #[inline] + pub unsafe fn get_many_key_value_unchecked_mut( + &mut self, + keys: [&Q; N], + ) -> [Option<(&'_ K, &'_ mut V)>; N] + where + Q: Hash + Equivalent + ?Sized, + { + // SAFETY: safety contract is ensured by the caller. + unsafe { self.0.get_many_key_value_unchecked_mut(keys) } + } +} diff --git a/crates/bevy_platform_support/src/collections/hash_set.rs b/crates/bevy_platform_support/src/collections/hash_set.rs new file mode 100644 index 0000000000..b29b78fae5 --- /dev/null +++ b/crates/bevy_platform_support/src/collections/hash_set.rs @@ -0,0 +1,1078 @@ +//! Provides [`HashSet`] based on [hashbrown]'s implementation. +//! Unlike [`hashbrown::HashSet`], [`HashSet`] defaults to [`FixedHasher`] +//! instead of [`RandomState`](crate::hash::RandomState). +//! This provides determinism by default with an acceptable compromise to denial +//! of service resistance in the context of a game engine. + +use core::{ + fmt::Debug, + hash::{BuildHasher, Hash}, + ops::{ + BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Deref, DerefMut, Sub, + SubAssign, + }, +}; + +use hashbrown::{hash_set as hb, Equivalent}; + +use crate::hash::FixedHasher; + +#[cfg(feature = "rayon")] +use rayon::prelude::{FromParallelIterator, IntoParallelIterator, ParallelExtend}; + +// Re-exports to match `std::collections::hash_set` +pub use hb::{Difference, Drain, Intersection, IntoIter, Iter, SymmetricDifference, Union}; + +// Additional items from `hashbrown` +pub use hb::{ExtractIf, OccupiedEntry, VacantEntry}; + +/// Shortcut for [`Entry`](hb::Entry) with [`FixedHasher`] as the default hashing provider. +pub type Entry<'a, T, S = FixedHasher> = hb::Entry<'a, T, S>; + +/// New-type for [`HashSet`](hb::HashSet) with [`FixedHasher`] as the default hashing provider. +/// Can be trivially converted to and from a [hashbrown] [`HashSet`](hb::HashSet) using [`From`]. +/// +/// A new-type is used instead of a type alias due to critical methods like [`new`](hb::HashSet::new) +/// being incompatible with Bevy's choice of default hasher. +#[repr(transparent)] +pub struct HashSet(hb::HashSet); + +impl Clone for HashSet +where + hb::HashSet: Clone, +{ + #[inline] + fn clone(&self) -> Self { + Self(self.0.clone()) + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + self.0.clone_from(&source.0); + } +} + +impl Debug for HashSet +where + hb::HashSet: Debug, +{ + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + as Debug>::fmt(&self.0, f) + } +} + +impl Default for HashSet +where + hb::HashSet: Default, +{ + #[inline] + fn default() -> Self { + Self(Default::default()) + } +} + +impl PartialEq for HashSet +where + hb::HashSet: PartialEq, +{ + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + +impl Eq for HashSet where hb::HashSet: Eq {} + +impl FromIterator for HashSet +where + hb::HashSet: FromIterator, +{ + #[inline] + fn from_iter>(iter: U) -> Self { + Self(FromIterator::from_iter(iter)) + } +} + +impl IntoIterator for HashSet +where + hb::HashSet: IntoIterator, +{ + type Item = as IntoIterator>::Item; + + type IntoIter = as IntoIterator>::IntoIter; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a, T, S> IntoIterator for &'a HashSet +where + &'a hb::HashSet: IntoIterator, +{ + type Item = <&'a hb::HashSet as IntoIterator>::Item; + + type IntoIter = <&'a hb::HashSet as IntoIterator>::IntoIter; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + (&self.0).into_iter() + } +} + +impl<'a, T, S> IntoIterator for &'a mut HashSet +where + &'a mut hb::HashSet: IntoIterator, +{ + type Item = <&'a mut hb::HashSet as IntoIterator>::Item; + + type IntoIter = <&'a mut hb::HashSet as IntoIterator>::IntoIter; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + (&mut self.0).into_iter() + } +} + +impl Extend for HashSet +where + hb::HashSet: Extend, +{ + #[inline] + fn extend>(&mut self, iter: U) { + self.0.extend(iter); + } +} + +impl From<[T; N]> for HashSet +where + T: Eq + Hash, +{ + fn from(value: [T; N]) -> Self { + value.into_iter().collect() + } +} + +impl From> for HashSet { + #[inline] + fn from(value: crate::collections::HashMap) -> Self { + Self(hb::HashSet::from(hashbrown::HashMap::from(value))) + } +} + +impl From> for HashSet { + #[inline] + fn from(value: hb::HashSet) -> Self { + Self(value) + } +} + +impl From> for hb::HashSet { + #[inline] + fn from(value: HashSet) -> Self { + value.0 + } +} + +impl Deref for HashSet { + type Target = hb::HashSet; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for HashSet { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[cfg(feature = "serialize")] +impl serde::Serialize for HashSet +where + hb::HashSet: serde::Serialize, +{ + #[inline] + fn serialize(&self, serializer: U) -> Result + where + U: serde::Serializer, + { + self.0.serialize(serializer) + } +} + +#[cfg(feature = "serialize")] +impl<'de, T, S> serde::Deserialize<'de> for HashSet +where + hb::HashSet: serde::Deserialize<'de>, +{ + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(Self(serde::Deserialize::deserialize(deserializer)?)) + } +} + +#[cfg(feature = "rayon")] +impl FromParallelIterator for HashSet +where + hb::HashSet: FromParallelIterator, + U: Send, +{ + fn from_par_iter

(par_iter: P) -> Self + where + P: IntoParallelIterator, + { + Self( as FromParallelIterator>::from_par_iter(par_iter)) + } +} + +#[cfg(feature = "rayon")] +impl IntoParallelIterator for HashSet +where + hb::HashSet: IntoParallelIterator, +{ + type Item = as IntoParallelIterator>::Item; + type Iter = as IntoParallelIterator>::Iter; + + fn into_par_iter(self) -> Self::Iter { + self.0.into_par_iter() + } +} + +#[cfg(feature = "rayon")] +impl<'a, T: Sync, S> IntoParallelIterator for &'a HashSet +where + &'a hb::HashSet: IntoParallelIterator, +{ + type Item = <&'a hb::HashSet as IntoParallelIterator>::Item; + type Iter = <&'a hb::HashSet as IntoParallelIterator>::Iter; + + fn into_par_iter(self) -> Self::Iter { + (&self.0).into_par_iter() + } +} + +#[cfg(feature = "rayon")] +impl ParallelExtend for HashSet +where + hb::HashSet: ParallelExtend, + U: Send, +{ + fn par_extend(&mut self, par_iter: I) + where + I: IntoParallelIterator, + { + as ParallelExtend>::par_extend(&mut self.0, par_iter); + } +} + +impl HashSet { + /// Creates an empty [`HashSet`]. + /// + /// Refer to [`new`](hb::HashSet::new) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// # + /// // Creates a HashSet with zero capacity. + /// let map = HashSet::new(); + /// # + /// # let mut map = map; + /// # map.insert("foo"); + /// # assert_eq!(map.get("foo"), Some("foo").as_ref()); + /// ``` + #[inline] + pub const fn new() -> Self { + Self::with_hasher(FixedHasher) + } + + /// Creates an empty [`HashSet`] with the specified capacity. + /// + /// Refer to [`with_capacity`](hb::HashSet::with_capacity) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// # + /// // Creates a HashSet with capacity for at least 5 entries. + /// let map = HashSet::with_capacity(5); + /// # + /// # let mut map = map; + /// # map.insert("foo"); + /// # assert_eq!(map.get("foo"), Some("foo").as_ref()); + /// ``` + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + Self::with_capacity_and_hasher(capacity, FixedHasher) + } +} + +impl HashSet { + /// Returns the number of elements the set can hold without reallocating. + /// + /// Refer to [`capacity`](hb::HashSet::capacity) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let map = HashSet::with_capacity(5); + /// + /// # let map: HashSet<()> = map; + /// # + /// assert!(map.capacity() >= 5); + /// ``` + #[inline] + pub fn capacity(&self) -> usize { + self.0.capacity() + } + + /// An iterator visiting all elements in arbitrary order. + /// The iterator element type is `&'a T`. + /// + /// Refer to [`iter`](hb::HashSet::iter) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// # + /// let mut map = HashSet::new(); + /// + /// map.insert("foo"); + /// map.insert("bar"); + /// map.insert("baz"); + /// + /// for value in map.iter() { + /// // "foo", "bar", "baz" + /// // Note that the above order is not guaranteed + /// } + /// # + /// # assert_eq!(map.iter().count(), 3); + /// ``` + #[inline] + pub fn iter(&self) -> Iter<'_, T> { + self.0.iter() + } + + /// Returns the number of elements in the set. + /// + /// Refer to [`len`](hb::HashSet::len) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::new(); + /// + /// assert_eq!(map.len(), 0); + /// + /// map.insert("foo"); + /// + /// assert_eq!(map.len(), 1); + /// ``` + #[inline] + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns `true` if the set contains no elements. + /// + /// Refer to [`is_empty`](hb::HashSet::is_empty) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::new(); + /// + /// assert!(map.is_empty()); + /// + /// map.insert("foo"); + /// + /// assert!(!map.is_empty()); + /// ``` + #[inline] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Clears the set, returning all elements in an iterator. + /// + /// Refer to [`drain`](hb::HashSet::drain) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// # + /// let mut map = HashSet::new(); + /// + /// map.insert("foo"); + /// map.insert("bar"); + /// map.insert("baz"); + /// + /// for value in map.drain() { + /// // "foo", "bar", "baz" + /// // Note that the above order is not guaranteed + /// } + /// + /// assert!(map.is_empty()); + /// ``` + #[inline] + pub fn drain(&mut self) -> Drain<'_, T> { + self.0.drain() + } + + /// Retains only the elements specified by the predicate. + /// + /// Refer to [`retain`](hb::HashSet::retain) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// # + /// let mut map = HashSet::new(); + /// + /// map.insert("foo"); + /// map.insert("bar"); + /// map.insert("baz"); + /// + /// map.retain(|value| *value == "baz"); + /// + /// assert_eq!(map.len(), 1); + /// ``` + #[inline] + pub fn retain(&mut self, f: F) + where + F: FnMut(&T) -> bool, + { + self.0.retain(f); + } + + /// Drains elements which are true under the given predicate, + /// and returns an iterator over the removed items. + /// + /// Refer to [`extract_if`](hb::HashSet::extract_if) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// # + /// let mut map = HashSet::new(); + /// + /// map.insert("foo"); + /// map.insert("bar"); + /// map.insert("baz"); + /// + /// let extracted = map + /// .extract_if(|value| *value == "baz") + /// .collect::>(); + /// + /// assert_eq!(map.len(), 2); + /// assert_eq!(extracted.len(), 1); + /// ``` + #[inline] + pub fn extract_if(&mut self, f: F) -> ExtractIf<'_, T, F> + where + F: FnMut(&T) -> bool, + { + self.0.extract_if(f) + } + + /// Clears the set, removing all values. + /// + /// Refer to [`clear`](hb::HashSet::clear) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// # + /// let mut map = HashSet::new(); + /// + /// map.insert("foo"); + /// map.insert("bar"); + /// map.insert("baz"); + /// + /// map.clear(); + /// + /// assert!(map.is_empty()); + /// ``` + #[inline] + pub fn clear(&mut self) { + self.0.clear(); + } + + /// Creates a new empty hash set which will use the given hasher to hash + /// keys. + /// + /// Refer to [`with_hasher`](hb::HashSet::with_hasher) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// # use bevy_platform_support::hash::FixedHasher as SomeHasher; + /// // Creates a HashSet with the provided hasher. + /// let map = HashSet::with_hasher(SomeHasher); + /// # + /// # let mut map = map; + /// # map.insert("foo"); + /// # assert_eq!(map.get("foo"), Some("foo").as_ref()); + /// ``` + #[inline] + pub const fn with_hasher(hasher: S) -> Self { + Self(hb::HashSet::with_hasher(hasher)) + } + + /// Creates an empty [`HashSet`] with the specified capacity, using + /// `hasher` to hash the keys. + /// + /// Refer to [`with_capacity_and_hasher`](hb::HashSet::with_capacity_and_hasher) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// # use bevy_platform_support::hash::FixedHasher as SomeHasher; + /// // Creates a HashSet with capacity for 5 entries and the provided hasher. + /// let map = HashSet::with_capacity_and_hasher(5, SomeHasher); + /// # + /// # let mut map = map; + /// # map.insert("foo"); + /// # assert_eq!(map.get("foo"), Some("foo").as_ref()); + /// ``` + #[inline] + pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> Self { + Self(hb::HashSet::with_capacity_and_hasher(capacity, hasher)) + } + + /// Returns a reference to the set's [`BuildHasher`]. + /// + /// Refer to [`hasher`](hb::HashSet::hasher) for further details. + #[inline] + pub fn hasher(&self) -> &S { + self.0.hasher() + } + + /// Takes the inner [`HashSet`](hb::HashSet) out of this wrapper. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let map: HashSet<&'static str> = HashSet::new(); + /// let map: hashbrown::HashSet<&'static str, _> = map.into_inner(); + /// ``` + #[inline] + pub fn into_inner(self) -> hb::HashSet { + self.0 + } +} + +impl HashSet +where + T: Eq + Hash, + S: BuildHasher, +{ + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the [`HashSet`]. The collection may reserve more space to avoid + /// frequent reallocations. + /// + /// Refer to [`reserve`](hb::HashSet::reserve) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::with_capacity(5); + /// + /// # let mut map: HashSet<()> = map; + /// # + /// assert!(map.capacity() >= 5); + /// + /// map.reserve(10); + /// + /// assert!(map.capacity() - map.len() >= 10); + /// ``` + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.0.reserve(additional); + } + + /// Tries to reserve capacity for at least `additional` more elements to be inserted + /// in the given `HashSet`. The collection may reserve more space to avoid + /// frequent reallocations. + /// + /// Refer to [`try_reserve`](hb::HashSet::try_reserve) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::with_capacity(5); + /// + /// # let mut map: HashSet<()> = map; + /// # + /// assert!(map.capacity() >= 5); + /// + /// map.try_reserve(10).expect("Out of Memory!"); + /// + /// assert!(map.capacity() - map.len() >= 10); + /// ``` + #[inline] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), hashbrown::TryReserveError> { + self.0.try_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. + /// + /// Refer to [`shrink_to_fit`](hb::HashSet::shrink_to_fit) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::with_capacity(5); + /// + /// map.insert("foo"); + /// map.insert("bar"); + /// map.insert("baz"); + /// + /// assert!(map.capacity() >= 5); + /// + /// map.shrink_to_fit(); + /// + /// assert_eq!(map.capacity(), 3); + /// ``` + #[inline] + pub fn shrink_to_fit(&mut self) { + self.0.shrink_to_fit(); + } + + /// Shrinks the capacity of the set with a lower limit. It will drop + /// down no lower than the supplied limit while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + /// + /// Refer to [`shrink_to`](hb::HashSet::shrink_to) for further details. + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.0.shrink_to(min_capacity); + } + + /// Visits the values representing the difference, + /// i.e., the values that are in `self` but not in `other`. + /// + /// Refer to [`difference`](hb::HashSet::difference) for further details. + #[inline] + pub fn difference<'a>(&'a self, other: &'a Self) -> Difference<'a, T, S> { + self.0.difference(other) + } + + /// Visits the values representing the symmetric difference, + /// i.e., the values that are in `self` or in `other` but not in both. + /// + /// Refer to [`symmetric_difference`](hb::HashSet::symmetric_difference) for further details. + #[inline] + pub fn symmetric_difference<'a>(&'a self, other: &'a Self) -> SymmetricDifference<'a, T, S> { + self.0.symmetric_difference(other) + } + + /// Visits the values representing the intersection, + /// i.e., the values that are both in `self` and `other`. + /// + /// Refer to [`intersection`](hb::HashSet::intersection) for further details. + #[inline] + pub fn intersection<'a>(&'a self, other: &'a Self) -> Intersection<'a, T, S> { + self.0.intersection(other) + } + + /// Visits the values representing the union, + /// i.e., all the values in `self` or `other`, without duplicates. + /// + /// Refer to [`union`](hb::HashSet::union) for further details. + #[inline] + pub fn union<'a>(&'a self, other: &'a Self) -> Union<'a, T, S> { + self.0.union(other) + } + + /// Returns `true` if the set contains a value. + /// + /// Refer to [`contains`](hb::HashSet::contains) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::new(); + /// + /// map.insert("foo"); + /// + /// assert!(map.contains("foo")); + /// ``` + #[inline] + pub fn contains(&self, value: &Q) -> bool + where + Q: Hash + Equivalent + ?Sized, + { + self.0.contains(value) + } + + /// Returns a reference to the value in the set, if any, that is equal to the given value. + /// + /// Refer to [`get`](hb::HashSet::get) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::new(); + /// + /// map.insert("foo"); + /// + /// assert_eq!(map.get("foo"), Some(&"foo")); + /// ``` + #[inline] + pub fn get(&self, value: &Q) -> Option<&T> + where + Q: Hash + Equivalent + ?Sized, + { + self.0.get(value) + } + + /// Inserts the given `value` into the set if it is not present, then + /// returns a reference to the value in the set. + /// + /// Refer to [`get_or_insert`](hb::HashSet::get_or_insert) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::new(); + /// + /// assert_eq!(map.get_or_insert("foo"), &"foo"); + /// ``` + #[inline] + pub fn get_or_insert(&mut self, value: T) -> &T { + self.0.get_or_insert(value) + } + + /// Inserts a value computed from `f` into the set if the given `value` is + /// not present, then returns a reference to the value in the set. + /// + /// Refer to [`get_or_insert_with`](hb::HashSet::get_or_insert_with) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::new(); + /// + /// assert_eq!(map.get_or_insert_with(&"foo", |_| "foo"), &"foo"); + /// ``` + #[inline] + pub fn get_or_insert_with(&mut self, value: &Q, f: F) -> &T + where + Q: Hash + Equivalent + ?Sized, + F: FnOnce(&Q) -> T, + { + self.0.get_or_insert_with(value, f) + } + + /// Gets the given value's corresponding entry in the set for in-place manipulation. + /// + /// Refer to [`entry`](hb::HashSet::entry) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::new(); + /// + /// let value = map.entry("foo").or_insert(); + /// # + /// # assert_eq!(value, ()); + /// ``` + #[inline] + pub fn entry(&mut self, value: T) -> Entry<'_, T, S> { + self.0.entry(value) + } + + /// Returns `true` if `self` has no elements in common with `other`. + /// This is equivalent to checking for an empty intersection. + /// + /// Refer to [`is_disjoint`](hb::HashSet::is_disjoint) for further details. + #[inline] + pub fn is_disjoint(&self, other: &Self) -> bool { + self.0.is_disjoint(other) + } + + /// Returns `true` if the set is a subset of another, + /// i.e., `other` contains at least all the values in `self`. + /// + /// Refer to [`is_subset`](hb::HashSet::is_subset) for further details. + #[inline] + pub fn is_subset(&self, other: &Self) -> bool { + self.0.is_subset(other) + } + + /// Returns `true` if the set is a superset of another, + /// i.e., `self` contains at least all the values in `other`. + /// + /// Refer to [`is_superset`](hb::HashSet::is_superset) for further details. + #[inline] + pub fn is_superset(&self, other: &Self) -> bool { + self.0.is_superset(other) + } + + /// Adds a value to the set. + /// + /// Refer to [`insert`](hb::HashSet::insert) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::new(); + /// + /// map.insert("foo"); + /// + /// assert!(map.contains("foo")); + /// ``` + #[inline] + pub fn insert(&mut self, value: T) -> bool { + self.0.insert(value) + } + + /// Adds a value to the set, replacing the existing value, if any, that is equal to the given + /// one. Returns the replaced value. + /// + /// Refer to [`replace`](hb::HashSet::replace) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::new(); + /// + /// map.insert("foo"); + /// + /// assert_eq!(map.replace("foo"), Some("foo")); + /// ``` + #[inline] + pub fn replace(&mut self, value: T) -> Option { + self.0.replace(value) + } + + /// Removes a value from the set. Returns whether the value was + /// present in the set. + /// + /// Refer to [`remove`](hb::HashSet::remove) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::new(); + /// + /// map.insert("foo"); + /// + /// assert!(map.remove("foo")); + /// + /// assert!(map.is_empty()); + /// ``` + #[inline] + pub fn remove(&mut self, value: &Q) -> bool + where + Q: Hash + Equivalent + ?Sized, + { + self.0.remove(value) + } + + /// Removes and returns the value in the set, if any, that is equal to the given one. + /// + /// Refer to [`take`](hb::HashSet::take) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::new(); + /// + /// map.insert("foo"); + /// + /// assert_eq!(map.take("foo"), Some("foo")); + /// + /// assert!(map.is_empty()); + /// ``` + #[inline] + pub fn take(&mut self, value: &Q) -> Option + where + Q: Hash + Equivalent + ?Sized, + { + self.0.take(value) + } + + /// Returns the total amount of memory allocated internally by the hash + /// set, in bytes. + /// + /// Refer to [`allocation_size`](hb::HashSet::allocation_size) for further details. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_platform_support::collections::HashSet; + /// let mut map = HashSet::new(); + /// + /// assert_eq!(map.allocation_size(), 0); + /// + /// map.insert("foo"); + /// + /// assert!(map.allocation_size() >= size_of::<&'static str>()); + /// ``` + #[inline] + pub fn allocation_size(&self) -> usize { + self.0.allocation_size() + } + + /// Insert a value the set without checking if the value already exists in the set. + /// + /// Refer to [`insert_unique_unchecked`](hb::HashSet::insert_unique_unchecked) for further details. + /// + /// # Safety + /// + /// This operation is safe if a value does not exist in the set. + /// + /// However, if a value exists in the set already, the behavior is unspecified: + /// this operation may panic, loop forever, or any following operation with the set + /// may panic, loop forever or return arbitrary result. + /// + /// That said, this operation (and following operations) are guaranteed to + /// not violate memory safety. + /// + /// However this operation is still unsafe because the resulting `HashSet` + /// may be passed to unsafe code which does expect the set to behave + /// correctly, and would cause unsoundness as a result. + #[expect( + unsafe_code, + reason = "re-exporting unsafe method from Hashbrown requires unsafe code" + )] + #[inline] + pub unsafe fn insert_unique_unchecked(&mut self, value: T) -> &T { + // SAFETY: safety contract is ensured by the caller. + unsafe { self.0.insert_unique_unchecked(value) } + } +} + +impl BitOr<&HashSet> for &HashSet +where + for<'a> &'a hb::HashSet: BitOr<&'a hb::HashSet, Output = hb::HashSet>, +{ + type Output = HashSet; + + /// Returns the union of `self` and `rhs` as a new `HashSet`. + #[inline] + fn bitor(self, rhs: &HashSet) -> HashSet { + HashSet(self.0.bitor(&rhs.0)) + } +} + +impl BitAnd<&HashSet> for &HashSet +where + for<'a> &'a hb::HashSet: BitAnd<&'a hb::HashSet, Output = hb::HashSet>, +{ + type Output = HashSet; + + /// Returns the intersection of `self` and `rhs` as a new `HashSet`. + #[inline] + fn bitand(self, rhs: &HashSet) -> HashSet { + HashSet(self.0.bitand(&rhs.0)) + } +} + +impl BitXor<&HashSet> for &HashSet +where + for<'a> &'a hb::HashSet: BitXor<&'a hb::HashSet, Output = hb::HashSet>, +{ + type Output = HashSet; + + /// Returns the symmetric difference of `self` and `rhs` as a new `HashSet`. + #[inline] + fn bitxor(self, rhs: &HashSet) -> HashSet { + HashSet(self.0.bitxor(&rhs.0)) + } +} + +impl Sub<&HashSet> for &HashSet +where + for<'a> &'a hb::HashSet: Sub<&'a hb::HashSet, Output = hb::HashSet>, +{ + type Output = HashSet; + + /// Returns the difference of `self` and `rhs` as a new `HashSet`. + #[inline] + fn sub(self, rhs: &HashSet) -> HashSet { + HashSet(self.0.sub(&rhs.0)) + } +} + +impl BitOrAssign<&HashSet> for HashSet +where + hb::HashSet: for<'a> BitOrAssign<&'a hb::HashSet>, +{ + /// Modifies this set to contain the union of `self` and `rhs`. + #[inline] + fn bitor_assign(&mut self, rhs: &HashSet) { + self.0.bitor_assign(&rhs.0); + } +} + +impl BitAndAssign<&HashSet> for HashSet +where + hb::HashSet: for<'a> BitAndAssign<&'a hb::HashSet>, +{ + /// Modifies this set to contain the intersection of `self` and `rhs`. + #[inline] + fn bitand_assign(&mut self, rhs: &HashSet) { + self.0.bitand_assign(&rhs.0); + } +} + +impl BitXorAssign<&HashSet> for HashSet +where + hb::HashSet: for<'a> BitXorAssign<&'a hb::HashSet>, +{ + /// Modifies this set to contain the symmetric difference of `self` and `rhs`. + #[inline] + fn bitxor_assign(&mut self, rhs: &HashSet) { + self.0.bitxor_assign(&rhs.0); + } +} + +impl SubAssign<&HashSet> for HashSet +where + hb::HashSet: for<'a> SubAssign<&'a hb::HashSet>, +{ + /// Modifies this set to contain the difference of `self` and `rhs`. + #[inline] + fn sub_assign(&mut self, rhs: &HashSet) { + self.0.sub_assign(&rhs.0); + } +} diff --git a/crates/bevy_platform_support/src/collections/hash_table.rs b/crates/bevy_platform_support/src/collections/hash_table.rs new file mode 100644 index 0000000000..5d6a265679 --- /dev/null +++ b/crates/bevy_platform_support/src/collections/hash_table.rs @@ -0,0 +1,6 @@ +//! Provides [`HashTable`] + +pub use hashbrown::hash_table::{ + AbsentEntry, Drain, Entry, ExtractIf, HashTable, IntoIter, Iter, IterHash, IterHashMut, + IterMut, OccupiedEntry, VacantEntry, +}; diff --git a/crates/bevy_platform_support/src/collections/mod.rs b/crates/bevy_platform_support/src/collections/mod.rs new file mode 100644 index 0000000000..3622165b65 --- /dev/null +++ b/crates/bevy_platform_support/src/collections/mod.rs @@ -0,0 +1,12 @@ +//! Provides [`HashMap`] and [`HashSet`] from [`hashbrown`] with some customized defaults. +//! +//! Also provides the [`HashTable`] type, which is specific to [`hashbrown`]. + +pub use hash_map::HashMap; +pub use hash_set::HashSet; +pub use hash_table::HashTable; +pub use hashbrown::Equivalent; + +pub mod hash_map; +pub mod hash_set; +pub mod hash_table; diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index e9f32ae9e0..91c89e96ac 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -331,10 +331,7 @@ impl SceneSpawner { Ok(_) => { self.spawned_instances .insert(instance_id, InstanceInfo { entity_map }); - let spawned = self - .spawned_dynamic_scenes - .entry(handle.id()) - .or_insert_with(HashSet::default); + let spawned = self.spawned_dynamic_scenes.entry(handle.id()).or_default(); spawned.insert(instance_id); // Scenes with parents need more setup before they are ready.