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)`.
This commit is contained in:
parent
3d17865c2e
commit
f3f4e80d87
@ -131,9 +131,7 @@ fn build_over_map(
|
|||||||
.filter(|e| !cancelled_pointers.contains(&e.pointer))
|
.filter(|e| !cancelled_pointers.contains(&e.pointer))
|
||||||
{
|
{
|
||||||
let pointer = entities_under_pointer.pointer;
|
let pointer = entities_under_pointer.pointer;
|
||||||
let layer_map = pointer_over_map
|
let layer_map = pointer_over_map.entry(pointer).or_default();
|
||||||
.entry(pointer)
|
|
||||||
.or_insert_with(BTreeMap::new);
|
|
||||||
for (entity, pick_data) in entities_under_pointer.picks.iter() {
|
for (entity, pick_data) in entities_under_pointer.picks.iter() {
|
||||||
let layer = entities_under_pointer.order;
|
let layer = entities_under_pointer.order;
|
||||||
let hits = layer_map.entry(FloatOrd(layer)).or_default();
|
let hits = layer_map.entry(FloatOrd(layer)).or_default();
|
||||||
|
@ -14,7 +14,10 @@ default = ["std"]
|
|||||||
# Functionality
|
# Functionality
|
||||||
|
|
||||||
## Adds serialization support through `serde`.
|
## Adds serialization support through `serde`.
|
||||||
serialize = ["hashbrown/serde"]
|
serialize = ["dep:serde", "hashbrown/serde"]
|
||||||
|
|
||||||
|
## Adds integration with Rayon.
|
||||||
|
rayon = ["dep:rayon", "hashbrown/rayon"]
|
||||||
|
|
||||||
# Platform Compatibility
|
# Platform Compatibility
|
||||||
|
|
||||||
@ -28,10 +31,11 @@ std = [
|
|||||||
"portable-atomic-util/std",
|
"portable-atomic-util/std",
|
||||||
"spin/std",
|
"spin/std",
|
||||||
"foldhash/std",
|
"foldhash/std",
|
||||||
|
"serde?/std",
|
||||||
]
|
]
|
||||||
|
|
||||||
## Allows access to the `alloc` crate.
|
## 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
|
## `critical-section` provides the building blocks for synchronization primitives
|
||||||
## on all platforms, including `no_std`.
|
## on all platforms, including `no_std`.
|
||||||
@ -57,6 +61,8 @@ hashbrown = { version = "0.15.1", features = [
|
|||||||
"equivalent",
|
"equivalent",
|
||||||
"raw-entry",
|
"raw-entry",
|
||||||
], optional = true, default-features = false }
|
], 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]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
web-time = { version = "1.1", default-features = false, optional = true }
|
web-time = { version = "1.1", default-features = false, optional = true }
|
||||||
|
@ -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<K, V, RandomState>`.
|
|
||||||
//! Whereas, Bevy exports `HashMap<K, V, FixedHasher>` 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<K, V, S = FixedHasher> = hb::HashMap<K, V, S>;
|
|
||||||
|
|
||||||
/// 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<T, S = FixedHasher> = hb::HashSet<T, S>;
|
|
||||||
|
|
||||||
/// 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,
|
|
||||||
};
|
|
||||||
}
|
|
1287
crates/bevy_platform_support/src/collections/hash_map.rs
Normal file
1287
crates/bevy_platform_support/src/collections/hash_map.rs
Normal file
File diff suppressed because it is too large
Load Diff
1078
crates/bevy_platform_support/src/collections/hash_set.rs
Normal file
1078
crates/bevy_platform_support/src/collections/hash_set.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,6 @@
|
|||||||
|
//! Provides [`HashTable`]
|
||||||
|
|
||||||
|
pub use hashbrown::hash_table::{
|
||||||
|
AbsentEntry, Drain, Entry, ExtractIf, HashTable, IntoIter, Iter, IterHash, IterHashMut,
|
||||||
|
IterMut, OccupiedEntry, VacantEntry,
|
||||||
|
};
|
12
crates/bevy_platform_support/src/collections/mod.rs
Normal file
12
crates/bevy_platform_support/src/collections/mod.rs
Normal file
@ -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;
|
@ -331,10 +331,7 @@ impl SceneSpawner {
|
|||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.spawned_instances
|
self.spawned_instances
|
||||||
.insert(instance_id, InstanceInfo { entity_map });
|
.insert(instance_id, InstanceInfo { entity_map });
|
||||||
let spawned = self
|
let spawned = self.spawned_dynamic_scenes.entry(handle.id()).or_default();
|
||||||
.spawned_dynamic_scenes
|
|
||||||
.entry(handle.id())
|
|
||||||
.or_insert_with(HashSet::default);
|
|
||||||
spawned.insert(instance_id);
|
spawned.insert(instance_id);
|
||||||
|
|
||||||
// Scenes with parents need more setup before they are ready.
|
// Scenes with parents need more setup before they are ready.
|
||||||
|
Loading…
Reference in New Issue
Block a user