diff --git a/crates/bevy_app/src/plugin_group.rs b/crates/bevy_app/src/plugin_group.rs index 257f869d12b3d7..22d174df9e67ad 100644 --- a/crates/bevy_app/src/plugin_group.rs +++ b/crates/bevy_app/src/plugin_group.rs @@ -1,5 +1,5 @@ use crate::{App, AppError, Plugin}; -use bevy_utils::{tracing::debug, tracing::warn, HashMap}; +use bevy_utils::{tracing::debug, tracing::warn, TypeIdMap}; use std::any::TypeId; /// Combines multiple [`Plugin`]s into a single unit. @@ -33,7 +33,7 @@ impl PluginGroup for PluginGroupBuilder { /// can be disabled, enabled or reordered. pub struct PluginGroupBuilder { group_name: String, - plugins: HashMap, + plugins: TypeIdMap, order: Vec, } diff --git a/crates/bevy_asset/src/server/info.rs b/crates/bevy_asset/src/server/info.rs index e08dbed7c33067..22c03efbd07605 100644 --- a/crates/bevy_asset/src/server/info.rs +++ b/crates/bevy_asset/src/server/info.rs @@ -6,7 +6,7 @@ use crate::{ }; use bevy_ecs::world::World; use bevy_log::warn; -use bevy_utils::{Entry, HashMap, HashSet}; +use bevy_utils::{Entry, HashMap, HashSet, TypeIdMap}; use crossbeam_channel::Sender; use std::{ any::TypeId, @@ -61,7 +61,7 @@ impl AssetInfo { #[derive(Default)] pub(crate) struct AssetInfos { - path_to_id: HashMap, HashMap>, + path_to_id: HashMap, TypeIdMap>, infos: HashMap, /// If set to `true`, this informs [`AssetInfos`] to track data relevant to watching for changes (such as `load_dependants`) /// This should only be set at startup. @@ -72,10 +72,10 @@ pub(crate) struct AssetInfos { /// Tracks living labeled assets for a given source asset. /// This should only be set when watching for changes to avoid unnecessary work. pub(crate) living_labeled_assets: HashMap, HashSet>, - pub(crate) handle_providers: HashMap, - pub(crate) dependency_loaded_event_sender: HashMap, + pub(crate) handle_providers: TypeIdMap, + pub(crate) dependency_loaded_event_sender: TypeIdMap, pub(crate) dependency_failed_event_sender: - HashMap, AssetLoadError)>, + TypeIdMap, AssetLoadError)>, } impl std::fmt::Debug for AssetInfos { @@ -112,7 +112,7 @@ impl AssetInfos { #[allow(clippy::too_many_arguments)] fn create_handle_internal( infos: &mut HashMap, - handle_providers: &HashMap, + handle_providers: &TypeIdMap, living_labeled_assets: &mut HashMap, HashSet>, watching_for_changes: bool, type_id: TypeId, @@ -205,7 +205,7 @@ impl AssetInfos { .ok_or(GetOrCreateHandleInternalError::HandleMissingButTypeIdNotSpecified)?; match handles.entry(type_id) { - Entry::Occupied(entry) => { + bevy_utils::hashbrown::hash_map::Entry::Occupied(entry) => { let id = *entry.get(); // if there is a path_to_id entry, info always exists let info = self.infos.get_mut(&id).unwrap(); @@ -246,7 +246,7 @@ impl AssetInfos { } } // The entry does not exist, so this is a "fresh" asset load. We must create a new handle - Entry::Vacant(entry) => { + bevy_utils::hashbrown::hash_map::Entry::Vacant(entry) => { let should_load = match loading_mode { HandleLoadingMode::NotLoading => false, HandleLoadingMode::Request | HandleLoadingMode::Force => true, @@ -640,7 +640,7 @@ impl AssetInfos { fn process_handle_drop_internal( infos: &mut HashMap, - path_to_id: &mut HashMap, HashMap>, + path_to_id: &mut HashMap, TypeIdMap>, loader_dependants: &mut HashMap, HashSet>>, living_labeled_assets: &mut HashMap, HashSet>, watching_for_changes: bool, diff --git a/crates/bevy_asset/src/server/mod.rs b/crates/bevy_asset/src/server/mod.rs index 9863c87130241d..298c209a98a4ba 100644 --- a/crates/bevy_asset/src/server/mod.rs +++ b/crates/bevy_asset/src/server/mod.rs @@ -19,7 +19,7 @@ use crate::{ use bevy_ecs::prelude::*; use bevy_log::{error, info, warn}; use bevy_tasks::IoTaskPool; -use bevy_utils::{CowArc, HashMap, HashSet}; +use bevy_utils::{CowArc, HashMap, HashSet, TypeIdMap}; use crossbeam_channel::{Receiver, Sender}; use futures_lite::StreamExt; use info::*; @@ -1238,7 +1238,7 @@ pub fn handle_internal_asset_events(world: &mut World) { #[derive(Default)] pub(crate) struct AssetLoaders { - type_id_to_loader: HashMap, + type_id_to_loader: TypeIdMap, extension_to_type_id: HashMap, type_name_to_type_id: HashMap<&'static str, TypeId>, preregistered_loaders: HashMap<&'static str, TypeId>, diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 361689e5793ad3..35ba1ca9ddbb2c 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -3,7 +3,7 @@ //! This module contains the [`Bundle`] trait and some other helper types. pub use bevy_ecs_macros::Bundle; -use bevy_utils::{HashMap, HashSet}; +use bevy_utils::{HashMap, HashSet, TypeIdMap}; use crate::{ archetype::{ @@ -14,7 +14,6 @@ use crate::{ entity::{Entities, Entity, EntityLocation}, query::DebugCheckedUnwrap, storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow}, - TypeIdMap, }; use bevy_ptr::OwningPtr; use bevy_utils::all_tuples; diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index cae48be957bd0c..dcf76efd24fe38 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -6,12 +6,12 @@ use crate::{ storage::{SparseSetIndex, Storages}, system::{Local, Resource, SystemParam}, world::{FromWorld, World}, - TypeIdMap, }; pub use bevy_ecs_macros::Component; use bevy_ptr::{OwningPtr, UnsafeCellDeref}; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; +use bevy_utils::TypeIdMap; use std::cell::UnsafeCell; use std::{ alloc::Layout, diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 6b63156d48c655..3cd89b77e2b933 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -21,8 +21,6 @@ pub mod storage; pub mod system; pub mod world; -use std::any::TypeId; - pub use bevy_ptr as ptr; /// Most commonly used re-exported types. @@ -56,34 +54,6 @@ pub mod prelude { pub use bevy_utils::all_tuples; -/// A specialized hashmap type with Key of [`TypeId`] -type TypeIdMap = - std::collections::HashMap>; - -#[doc(hidden)] -#[derive(Default)] -struct NoOpTypeIdHasher(u64); - -// TypeId already contains a high-quality hash, so skip re-hashing that hash. -impl std::hash::Hasher for NoOpTypeIdHasher { - fn finish(&self) -> u64 { - self.0 - } - - fn write(&mut self, bytes: &[u8]) { - // This will never be called: TypeId always just calls write_u64 once! - // This is a known trick and unlikely to change, but isn't officially guaranteed. - // Don't break applications (slower fallback, just check in test): - self.0 = bytes.iter().fold(self.0, |hash, b| { - hash.rotate_left(8).wrapping_add(*b as u64) - }); - } - - fn write_u64(&mut self, i: u64) { - self.0 = i; - } -} - #[cfg(test)] mod tests { use crate as bevy_ecs; @@ -1755,23 +1725,6 @@ mod tests { ); } - #[test] - fn fast_typeid_hash() { - struct Hasher; - - impl std::hash::Hasher for Hasher { - fn finish(&self) -> u64 { - 0 - } - fn write(&mut self, _: &[u8]) { - panic!("Hashing of std::any::TypeId changed"); - } - fn write_u64(&mut self, _: u64) {} - } - - std::hash::Hash::hash(&TypeId::of::<()>(), &mut Hasher); - } - #[derive(Component)] struct ComponentA(u32); diff --git a/crates/bevy_ecs/src/schedule/stepping.rs b/crates/bevy_ecs/src/schedule/stepping.rs index 8a91f359713dbb..0a5348b126dec0 100644 --- a/crates/bevy_ecs/src/schedule/stepping.rs +++ b/crates/bevy_ecs/src/schedule/stepping.rs @@ -9,6 +9,7 @@ use crate::{ use bevy_utils::{ thiserror::Error, tracing::{error, info, warn}, + TypeIdMap, }; #[cfg(test)] @@ -617,7 +618,7 @@ struct ScheduleState { /// changes to system behavior that should be applied the next time /// [`ScheduleState::skipped_systems()`] is called - behavior_updates: HashMap>, + behavior_updates: TypeIdMap>, /// This field contains the first steppable system in the schedule. first: Option, diff --git a/crates/bevy_gizmos/src/config.rs b/crates/bevy_gizmos/src/config.rs index c2656268c23429..a8bf73b5c27648 100644 --- a/crates/bevy_gizmos/src/config.rs +++ b/crates/bevy_gizmos/src/config.rs @@ -6,7 +6,7 @@ pub use bevy_gizmos_macros::GizmoConfigGroup; use bevy_ecs::{component::Component, system::Resource}; use bevy_reflect::{Reflect, TypePath}; use bevy_render::view::RenderLayers; -use bevy_utils::HashMap; +use bevy_utils::TypeIdMap; use core::panic; use std::{ any::TypeId, @@ -30,7 +30,7 @@ pub struct DefaultGizmoConfigGroup; #[derive(Resource, Default)] pub struct GizmoConfigStore { // INVARIANT: must map TypeId::of::() to correct type T - store: HashMap)>, + store: TypeIdMap<(GizmoConfig, Box)>, } impl GizmoConfigStore { diff --git a/crates/bevy_gizmos/src/lib.rs b/crates/bevy_gizmos/src/lib.rs index 70e3afd11dd8e9..e194701e76dcb9 100644 --- a/crates/bevy_gizmos/src/lib.rs +++ b/crates/bevy_gizmos/src/lib.rs @@ -77,7 +77,7 @@ use bevy_render::{ renderer::RenderDevice, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; -use bevy_utils::HashMap; +use bevy_utils::TypeIdMap; use config::{ DefaultGizmoConfigGroup, GizmoConfig, GizmoConfigGroup, GizmoConfigStore, GizmoMeshConfig, }; @@ -206,8 +206,8 @@ impl AppGizmoBuilder for App { #[derive(Resource, Default)] struct LineGizmoHandles { - list: HashMap>, - strip: HashMap>, + list: TypeIdMap>, + strip: TypeIdMap>, } fn update_gizmo_meshes( diff --git a/crates/bevy_reflect/src/type_registry.rs b/crates/bevy_reflect/src/type_registry.rs index 36ebbfc99217fe..8118a7824ee72f 100644 --- a/crates/bevy_reflect/src/type_registry.rs +++ b/crates/bevy_reflect/src/type_registry.rs @@ -1,6 +1,6 @@ use crate::{serde::Serializable, Reflect, TypeInfo, TypePath, Typed}; use bevy_ptr::{Ptr, PtrMut}; -use bevy_utils::{HashMap, HashSet}; +use bevy_utils::{HashMap, HashSet, TypeIdMap}; use downcast_rs::{impl_downcast, Downcast}; use serde::Deserialize; use std::{ @@ -22,7 +22,7 @@ use std::{ /// [Registering]: TypeRegistry::register /// [crate-level documentation]: crate pub struct TypeRegistry { - registrations: HashMap, + registrations: TypeIdMap, short_path_to_id: HashMap<&'static str, TypeId>, type_path_to_id: HashMap<&'static str, TypeId>, ambiguous_names: HashSet<&'static str>, @@ -318,7 +318,7 @@ impl TypeRegistryArc { /// /// [crate-level documentation]: crate pub struct TypeRegistration { - data: HashMap>, + data: TypeIdMap>, type_info: &'static TypeInfo, } @@ -373,7 +373,7 @@ impl TypeRegistration { /// Creates type registration information for `T`. pub fn of() -> Self { Self { - data: HashMap::default(), + data: Default::default(), type_info: T::type_info(), } } @@ -381,7 +381,7 @@ impl TypeRegistration { impl Clone for TypeRegistration { fn clone(&self) -> Self { - let mut data = HashMap::default(); + let mut data = TypeIdMap::default(); for (id, type_data) in &self.data { data.insert(*id, (*type_data).clone_type_data()); } diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index b293ca620c9481..f67316d68a9c5b 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -6,7 +6,7 @@ use bevy_ecs::{ system::{ReadOnlySystemParam, Resource, SystemParam, SystemParamItem, SystemState}, world::World, }; -use bevy_utils::{all_tuples, HashMap}; +use bevy_utils::{all_tuples, TypeIdMap}; use std::{ any::TypeId, fmt::Debug, @@ -47,7 +47,7 @@ pub struct DrawFunctionId(u32); /// For retrieval, the [`Draw`] functions are mapped to their respective [`TypeId`]s. pub struct DrawFunctionsInternal { pub draw_functions: Vec>>, - pub indices: HashMap, + pub indices: TypeIdMap, } impl DrawFunctionsInternal

{ @@ -111,7 +111,7 @@ impl Default for DrawFunctions

{ Self { internal: RwLock::new(DrawFunctionsInternal { draw_functions: Vec::new(), - indices: HashMap::default(), + indices: Default::default(), }), } } diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 676fb9fdcd8347..2d01a02cfdcdf4 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -5,8 +5,7 @@ use bevy_ecs::{ world::World, }; use bevy_reflect::{Reflect, TypePath, TypeRegistryArc}; -use bevy_utils::{EntityHashMap, HashMap}; -use std::any::TypeId; +use bevy_utils::{EntityHashMap, TypeIdMap}; #[cfg(feature = "serialize")] use crate::serde::SceneSerializer; @@ -97,7 +96,7 @@ impl DynamicScene { // of which entities in the scene use that component. // This is so we can update the scene-internal references to references // of the actual entities in the world. - let mut scene_mappings: HashMap> = HashMap::default(); + let mut scene_mappings: TypeIdMap> = Default::default(); for scene_entity in &self.entities { // Fetch the entity with the given entity id from the `entity_map` @@ -132,7 +131,7 @@ impl DynamicScene { if registration.data::().is_some() { scene_mappings .entry(registration.type_id()) - .or_insert(Vec::new()) + .or_default() .push(entity); } diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index b70ee02ebe4bc6..49bcc8e09e0ed3 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -43,6 +43,7 @@ pub mod nonmax { use hashbrown::hash_map::RawEntryMut; use std::{ + any::TypeId, fmt::Debug, future::Future, hash::{BuildHasher, BuildHasherDefault, Hash, Hasher}, @@ -326,6 +327,34 @@ pub type EntityHashMap = hashbrown::HashMap; /// A [`HashSet`] pre-configured to use [`EntityHash`] hashing. pub type EntityHashSet = hashbrown::HashSet; +/// A specialized hashmap type with Key of [`TypeId`] +pub type TypeIdMap = + hashbrown::HashMap>; + +#[doc(hidden)] +#[derive(Default)] +pub struct NoOpTypeIdHasher(u64); + +// TypeId already contains a high-quality hash, so skip re-hashing that hash. +impl std::hash::Hasher for NoOpTypeIdHasher { + fn finish(&self) -> u64 { + self.0 + } + + fn write(&mut self, bytes: &[u8]) { + // This will never be called: TypeId always just calls write_u64 once! + // This is a known trick and unlikely to change, but isn't officially guaranteed. + // Don't break applications (slower fallback, just check in test): + self.0 = bytes.iter().fold(self.0, |hash, b| { + hash.rotate_left(8).wrapping_add(*b as u64) + }); + } + + fn write_u64(&mut self, i: u64) { + self.0 = i; + } +} + /// A type which calls a function when dropped. /// This can be used to ensure that cleanup code is run even in case of a panic. /// @@ -423,4 +452,21 @@ mod tests { // Check that the HashMaps are Clone if the key/values are Clone assert_impl_all!(EntityHashMap::: Clone); assert_impl_all!(PreHashMap::: Clone); + + #[test] + fn fast_typeid_hash() { + struct Hasher; + + impl std::hash::Hasher for Hasher { + fn finish(&self) -> u64 { + 0 + } + fn write(&mut self, _: &[u8]) { + panic!("Hashing of std::any::TypeId changed"); + } + fn write_u64(&mut self, _: u64) {} + } + + std::hash::Hash::hash(&TypeId::of::<()>(), &mut Hasher); + } }