From 1cded6ac60a4a676664530b7464aca81209d5ba7 Mon Sep 17 00:00:00 2001 From: Tristan Guichaoua <33934311+tguichaoua@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:27:40 +0100 Subject: [PATCH] Use immutable key for `HashMap` and `HashSet` (#12086) # Objective Memory usage optimisation ## Solution `HashMap` and `HashSet`'s keys are immutable. So using mutable types like `String`, `Vec`, or `PathBuf` as a key is a waste of memory: they have an extra `usize` for their capacity and may have spare capacity. This PR replaces these types by their immutable equivalents `Box`, `Box<[T]>`, and `Box`. For more context, I recommend watching the [Use Arc Instead of Vec](https://www.youtube.com/watch?v=A4cKi7PTJSs) video. --------- Co-authored-by: James Liu --- crates/bevy_app/src/app.rs | 4 ++-- .../src/io/embedded/embedded_watcher.rs | 6 +++--- crates/bevy_asset/src/io/embedded/mod.rs | 6 +++--- crates/bevy_asset/src/io/gated.rs | 13 +++++-------- crates/bevy_asset/src/io/memory.rs | 19 ++++++++++++------- crates/bevy_asset/src/lib.rs | 12 ++++-------- crates/bevy_asset/src/processor/mod.rs | 4 ++-- crates/bevy_asset/src/server/info.rs | 10 +++++----- crates/bevy_asset/src/server/loaders.rs | 6 +++--- crates/bevy_ecs/src/bundle.rs | 4 ++-- crates/bevy_ecs/src/storage/table.rs | 7 ++----- crates/bevy_gltf/src/lib.rs | 15 +++++++-------- crates/bevy_gltf/src/loader.rs | 16 ++++++---------- crates/bevy_gltf/src/vertex_attributes.rs | 4 ++-- .../src/render_resource/pipeline_cache.rs | 11 ++++++----- tools/build-templated-pages/Cargo.toml | 1 + tools/build-templated-pages/src/examples.rs | 13 +++++++------ 17 files changed, 72 insertions(+), 79 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 5605d0c335..f08a3e73bd 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -78,7 +78,7 @@ pub struct App { pub main_schedule_label: InternedScheduleLabel, sub_apps: HashMap, plugin_registry: Vec>, - plugin_name_added: HashSet, + plugin_name_added: HashSet>, /// A private counter to prevent incorrect calls to `App::run()` from `Plugin::build()` building_plugin_depth: usize, plugins_state: PluginsState, @@ -642,7 +642,7 @@ impl App { plugin: Box, ) -> Result<&mut Self, AppError> { debug!("added plugin: {}", plugin.name()); - if plugin.is_unique() && !self.plugin_name_added.insert(plugin.name().to_string()) { + if plugin.is_unique() && !self.plugin_name_added.insert(plugin.name().into()) { Err(AppError::DuplicatePlugin { plugin_name: plugin.name().to_string(), })?; diff --git a/crates/bevy_asset/src/io/embedded/embedded_watcher.rs b/crates/bevy_asset/src/io/embedded/embedded_watcher.rs index 6e92caa5d3..1e7f3057bb 100644 --- a/crates/bevy_asset/src/io/embedded/embedded_watcher.rs +++ b/crates/bevy_asset/src/io/embedded/embedded_watcher.rs @@ -25,7 +25,7 @@ pub struct EmbeddedWatcher { impl EmbeddedWatcher { pub fn new( dir: Dir, - root_paths: Arc>>, + root_paths: Arc, PathBuf>>>, sender: crossbeam_channel::Sender, debounce_wait_time: Duration, ) -> Self { @@ -49,7 +49,7 @@ impl AssetWatcher for EmbeddedWatcher {} /// the initial static bytes from the file embedded in the binary. pub(crate) struct EmbeddedEventHandler { sender: crossbeam_channel::Sender, - root_paths: Arc>>, + root_paths: Arc, PathBuf>>>, root: PathBuf, dir: Dir, last_event: Option, @@ -61,7 +61,7 @@ impl FilesystemEventHandler for EmbeddedEventHandler { fn get_path(&self, absolute_path: &Path) -> Option<(PathBuf, bool)> { let (local_path, is_meta) = get_asset_path(&self.root, absolute_path); - let final_path = self.root_paths.read().get(&local_path)?.clone(); + let final_path = self.root_paths.read().get(local_path.as_path())?.clone(); if is_meta { warn!("Meta file asset hot-reloading is not supported yet: {final_path:?}"); } diff --git a/crates/bevy_asset/src/io/embedded/mod.rs b/crates/bevy_asset/src/io/embedded/mod.rs index bf22602436..b953e1e669 100644 --- a/crates/bevy_asset/src/io/embedded/mod.rs +++ b/crates/bevy_asset/src/io/embedded/mod.rs @@ -22,7 +22,7 @@ pub const EMBEDDED: &str = "embedded"; pub struct EmbeddedAssetRegistry { dir: Dir, #[cfg(feature = "embedded_watcher")] - root_paths: std::sync::Arc>>, + root_paths: std::sync::Arc, PathBuf>>>, } impl EmbeddedAssetRegistry { @@ -35,7 +35,7 @@ impl EmbeddedAssetRegistry { #[cfg(feature = "embedded_watcher")] self.root_paths .write() - .insert(full_path.to_owned(), asset_path.to_owned()); + .insert(full_path.into(), asset_path.to_owned()); self.dir.insert_asset(asset_path, value); } @@ -48,7 +48,7 @@ impl EmbeddedAssetRegistry { #[cfg(feature = "embedded_watcher")] self.root_paths .write() - .insert(full_path.to_owned(), asset_path.to_owned()); + .insert(full_path.into(), asset_path.to_owned()); self.dir.insert_meta(asset_path, value); } diff --git a/crates/bevy_asset/src/io/gated.rs b/crates/bevy_asset/src/io/gated.rs index 8a049263da..76f531a04c 100644 --- a/crates/bevy_asset/src/io/gated.rs +++ b/crates/bevy_asset/src/io/gated.rs @@ -2,10 +2,7 @@ use crate::io::{AssetReader, AssetReaderError, PathStream, Reader}; use bevy_utils::{BoxedFuture, HashMap}; use crossbeam_channel::{Receiver, Sender}; use parking_lot::RwLock; -use std::{ - path::{Path, PathBuf}, - sync::Arc, -}; +use std::{path::Path, sync::Arc}; /// A "gated" reader that will prevent asset reads from returning until /// a given path has been "opened" using [`GateOpener`]. @@ -13,7 +10,7 @@ use std::{ /// This is built primarily for unit tests. pub struct GatedReader { reader: R, - gates: Arc, Receiver<()>)>>>, + gates: Arc, (Sender<()>, Receiver<()>)>>>, } impl Clone for GatedReader { @@ -27,7 +24,7 @@ impl Clone for GatedReader { /// Opens path "gates" for a [`GatedReader`]. pub struct GateOpener { - gates: Arc, Receiver<()>)>>>, + gates: Arc, (Sender<()>, Receiver<()>)>>>, } impl GateOpener { @@ -36,7 +33,7 @@ impl GateOpener { pub fn open>(&self, path: P) { let mut gates = self.gates.write(); let gates = gates - .entry(path.as_ref().to_path_buf()) + .entry_ref(path.as_ref()) .or_insert_with(crossbeam_channel::unbounded); gates.0.send(()).unwrap(); } @@ -65,7 +62,7 @@ impl AssetReader for GatedReader { let receiver = { let mut gates = self.gates.write(); let gates = gates - .entry(path.to_path_buf()) + .entry_ref(path.as_ref()) .or_insert_with(crossbeam_channel::unbounded); gates.1.clone() }; diff --git a/crates/bevy_asset/src/io/memory.rs b/crates/bevy_asset/src/io/memory.rs index 94f31928a7..cc13d04820 100644 --- a/crates/bevy_asset/src/io/memory.rs +++ b/crates/bevy_asset/src/io/memory.rs @@ -12,9 +12,9 @@ use std::{ #[derive(Default, Debug)] struct DirInternal { - assets: HashMap, - metadata: HashMap, - dirs: HashMap, + assets: HashMap, Data>, + metadata: HashMap, Data>, + dirs: HashMap, Dir>, path: PathBuf, } @@ -46,7 +46,7 @@ impl Dir { dir = self.get_or_insert_dir(parent); } dir.0.write().assets.insert( - path.file_name().unwrap().to_string_lossy().to_string(), + path.file_name().unwrap().to_string_lossy().into(), Data { value: value.into(), path: path.to_owned(), @@ -60,7 +60,7 @@ impl Dir { dir = self.get_or_insert_dir(parent); } dir.0.write().metadata.insert( - path.file_name().unwrap().to_string_lossy().to_string(), + path.file_name().unwrap().to_string_lossy().into(), Data { value: value.into(), path: path.to_owned(), @@ -73,7 +73,7 @@ impl Dir { let mut full_path = PathBuf::new(); for c in path.components() { full_path.push(c); - let name = c.as_os_str().to_string_lossy().to_string(); + let name = c.as_os_str().to_string_lossy().into(); dir = { let dirs = &mut dir.0.write().dirs; dirs.entry(name) @@ -147,7 +147,12 @@ impl Stream for DirStream { let dir = this.dir.0.read(); let dir_index = this.dir_index; - if let Some(dir_path) = dir.dirs.keys().nth(dir_index).map(|d| dir.path.join(d)) { + if let Some(dir_path) = dir + .dirs + .keys() + .nth(dir_index) + .map(|d| dir.path.join(d.as_ref())) + { this.dir_index += 1; Poll::Ready(Some(dir_path)) } else { diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index ef6b35e993..a0b38af09f 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -451,10 +451,7 @@ mod tests { use bevy_utils::{BoxedFuture, Duration, HashMap}; use futures_lite::AsyncReadExt; use serde::{Deserialize, Serialize}; - use std::{ - path::{Path, PathBuf}, - sync::Arc, - }; + use std::{path::Path, sync::Arc}; use thiserror::Error; #[derive(Asset, TypePath, Debug, Default)] @@ -545,7 +542,7 @@ mod tests { /// A dummy [`CoolText`] asset reader that only succeeds after `failure_count` times it's read from for each asset. #[derive(Default, Clone)] pub struct UnstableMemoryAssetReader { - pub attempt_counters: Arc>>, + pub attempt_counters: Arc, usize>>>, pub load_delay: Duration, memory_reader: MemoryAssetReader, failure_count: usize, @@ -589,13 +586,12 @@ mod tests { Result>, bevy_asset::io::AssetReaderError>, > { let attempt_number = { - let key = PathBuf::from(path); let mut attempt_counters = self.attempt_counters.lock().unwrap(); - if let Some(existing) = attempt_counters.get_mut(&key) { + if let Some(existing) = attempt_counters.get_mut(path) { *existing += 1; *existing } else { - attempt_counters.insert(key, 1); + attempt_counters.insert(path.into(), 1); 1 } }; diff --git a/crates/bevy_asset/src/processor/mod.rs b/crates/bevy_asset/src/processor/mod.rs index b5ef275103..625a484330 100644 --- a/crates/bevy_asset/src/processor/mod.rs +++ b/crates/bevy_asset/src/processor/mod.rs @@ -56,7 +56,7 @@ pub struct AssetProcessorData { log: async_lock::RwLock>, processors: RwLock>>, /// Default processors for file extensions - default_processors: RwLock>, + default_processors: RwLock, &'static str>>, state: async_lock::RwLock, sources: AssetSources, initialized_sender: async_broadcast::Sender<()>, @@ -482,7 +482,7 @@ impl AssetProcessor { /// Set the default processor for the given `extension`. Make sure `P` is registered with [`AssetProcessor::register_processor`]. pub fn set_default_processor(&self, extension: &str) { let mut default_processors = self.data.default_processors.write(); - default_processors.insert(extension.to_string(), std::any::type_name::

()); + default_processors.insert(extension.into(), std::any::type_name::

()); } /// Returns the default processor for the given `extension`, if it exists. diff --git a/crates/bevy_asset/src/server/info.rs b/crates/bevy_asset/src/server/info.rs index 8ce639d7ca..6e9b758fa7 100644 --- a/crates/bevy_asset/src/server/info.rs +++ b/crates/bevy_asset/src/server/info.rs @@ -71,7 +71,7 @@ pub(crate) struct AssetInfos { pub(crate) loader_dependants: HashMap, HashSet>>, /// 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) living_labeled_assets: HashMap, HashSet>>, pub(crate) handle_providers: TypeIdMap, pub(crate) dependency_loaded_event_sender: TypeIdMap, pub(crate) dependency_failed_event_sender: @@ -113,7 +113,7 @@ impl AssetInfos { fn create_handle_internal( infos: &mut HashMap, handle_providers: &TypeIdMap, - living_labeled_assets: &mut HashMap, HashSet>, + living_labeled_assets: &mut HashMap, HashSet>>, watching_for_changes: bool, type_id: TypeId, path: Option>, @@ -129,7 +129,7 @@ impl AssetInfos { let mut without_label = path.to_owned(); if let Some(label) = without_label.take_label() { let labels = living_labeled_assets.entry(without_label).or_default(); - labels.insert(label.to_string()); + labels.insert(label.as_ref().into()); } } } @@ -613,7 +613,7 @@ impl AssetInfos { info: &AssetInfo, loader_dependants: &mut HashMap, HashSet>>, path: &AssetPath<'static>, - living_labeled_assets: &mut HashMap, HashSet>, + living_labeled_assets: &mut HashMap, HashSet>>, ) { for loader_dependency in info.loader_dependencies.keys() { if let Some(dependants) = loader_dependants.get_mut(loader_dependency) { @@ -642,7 +642,7 @@ impl AssetInfos { infos: &mut HashMap, path_to_id: &mut HashMap, TypeIdMap>, loader_dependants: &mut HashMap, HashSet>>, - living_labeled_assets: &mut HashMap, HashSet>, + living_labeled_assets: &mut HashMap, HashSet>>, watching_for_changes: bool, id: UntypedAssetId, ) -> bool { diff --git a/crates/bevy_asset/src/server/loaders.rs b/crates/bevy_asset/src/server/loaders.rs index 671064b31a..65f21d6b9b 100644 --- a/crates/bevy_asset/src/server/loaders.rs +++ b/crates/bevy_asset/src/server/loaders.rs @@ -13,7 +13,7 @@ use thiserror::Error; pub(crate) struct AssetLoaders { loaders: Vec, type_id_to_loaders: TypeIdMap>, - extension_to_loaders: HashMap>, + extension_to_loaders: HashMap, Vec>, type_name_to_loader: HashMap<&'static str, usize>, preregistered_loaders: HashMap<&'static str, usize>, } @@ -44,7 +44,7 @@ impl AssetLoaders { for extension in loader.extensions() { let list = self .extension_to_loaders - .entry(extension.to_string()) + .entry((*extension).into()) .or_default(); if !list.is_empty() { @@ -105,7 +105,7 @@ impl AssetLoaders { for extension in extensions { let list = self .extension_to_loaders - .entry(extension.to_string()) + .entry((*extension).into()) .or_default(); if !list.is_empty() { diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index f0266f6a67..81f025d102 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -806,7 +806,7 @@ pub struct Bundles { /// Cache static [`BundleId`] bundle_ids: TypeIdMap, /// Cache dynamic [`BundleId`] with multiple components - dynamic_bundle_ids: HashMap, (BundleId, Vec)>, + dynamic_bundle_ids: HashMap, (BundleId, Vec)>, /// Cache optimized dynamic [`BundleId`] with single component dynamic_component_bundle_ids: HashMap, } @@ -871,7 +871,7 @@ impl Bundles { .from_key(component_ids) .or_insert_with(|| { ( - Vec::from(component_ids), + component_ids.into(), initialize_dynamic_bundle(bundle_infos, components, Vec::from(component_ids)), ) }); diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index 5b05e43c29..8d6b03d1da 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -796,7 +796,7 @@ impl Table { /// Can be accessed via [`Storages`](crate::storage::Storages) pub struct Tables { tables: Vec, - table_ids: HashMap, TableId>, + table_ids: HashMap, TableId>, } impl Default for Tables { @@ -872,10 +872,7 @@ impl Tables { table = table.add_column(components.get_info_unchecked(*component_id)); } tables.push(table.build()); - ( - component_ids.to_vec(), - TableId::from_usize(tables.len() - 1), - ) + (component_ids.into(), TableId::from_usize(tables.len() - 1)) }); *value diff --git a/crates/bevy_gltf/src/lib.rs b/crates/bevy_gltf/src/lib.rs index 5e8d8e3f1c..9cd59d674a 100644 --- a/crates/bevy_gltf/src/lib.rs +++ b/crates/bevy_gltf/src/lib.rs @@ -26,7 +26,7 @@ use bevy_scene::Scene; /// Adds support for glTF file loading to the app. #[derive(Default)] pub struct GltfPlugin { - custom_vertex_attributes: HashMap, + custom_vertex_attributes: HashMap, MeshVertexAttribute>, } impl GltfPlugin { @@ -40,8 +40,7 @@ impl GltfPlugin { name: &str, attribute: MeshVertexAttribute, ) -> Self { - self.custom_vertex_attributes - .insert(name.to_string(), attribute); + self.custom_vertex_attributes.insert(name.into(), attribute); self } } @@ -75,19 +74,19 @@ pub struct Gltf { /// All scenes loaded from the glTF file. pub scenes: Vec>, /// Named scenes loaded from the glTF file. - pub named_scenes: HashMap>, + pub named_scenes: HashMap, Handle>, /// All meshes loaded from the glTF file. pub meshes: Vec>, /// Named meshes loaded from the glTF file. - pub named_meshes: HashMap>, + pub named_meshes: HashMap, Handle>, /// All materials loaded from the glTF file. pub materials: Vec>, /// Named materials loaded from the glTF file. - pub named_materials: HashMap>, + pub named_materials: HashMap, Handle>, /// All nodes loaded from the glTF file. pub nodes: Vec>, /// Named nodes loaded from the glTF file. - pub named_nodes: HashMap>, + pub named_nodes: HashMap, Handle>, /// Default scene to be displayed. pub default_scene: Option>, /// All animations loaded from the glTF file. @@ -95,7 +94,7 @@ pub struct Gltf { pub animations: Vec>, /// Named animations loaded from the glTF file. #[cfg(feature = "bevy_animation")] - pub named_animations: HashMap>, + pub named_animations: HashMap, Handle>, /// The gltf root of the gltf asset, see . Only has a value when `GltfLoaderSettings::include_source` is true. pub source: Option, } diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 63336cfdfc..c6200a6339 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -110,7 +110,7 @@ pub struct GltfLoader { /// Keys must be the attribute names as found in the glTF data, which must start with an underscore. /// See [this section of the glTF specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview) /// for additional details on custom attributes. - pub custom_vertex_attributes: HashMap, + pub custom_vertex_attributes: HashMap, MeshVertexAttribute>, } /// Specifies optional settings for processing gltfs at load time. By default, all recognized contents of @@ -293,7 +293,7 @@ async fn load_gltf<'a, 'b, 'c>( let handle = load_context .add_labeled_asset(format!("Animation{}", animation.index()), animation_clip); if let Some(name) = animation.name() { - named_animations.insert(name.to_string(), handle.clone()); + named_animations.insert(name.into(), handle.clone()); } animations.push(handle); } @@ -383,7 +383,7 @@ async fn load_gltf<'a, 'b, 'c>( for material in gltf.materials() { let handle = load_material(&material, load_context, false); if let Some(name) = material.name() { - named_materials.insert(name.to_string(), handle.clone()); + named_materials.insert(name.into(), handle.clone()); } materials.push(handle); } @@ -526,7 +526,7 @@ async fn load_gltf<'a, 'b, 'c>( }, ); if let Some(name) = gltf_mesh.name() { - named_meshes.insert(name.to_string(), handle.clone()); + named_meshes.insert(name.into(), handle.clone()); } meshes.push(handle); } @@ -560,11 +560,7 @@ async fn load_gltf<'a, 'b, 'c>( .collect::>>(); let named_nodes = named_nodes_intermediate .into_iter() - .filter_map(|(name, index)| { - nodes - .get(index) - .map(|handle| (name.to_string(), handle.clone())) - }) + .filter_map(|(name, index)| nodes.get(index).map(|handle| (name.into(), handle.clone()))) .collect(); let skinned_mesh_inverse_bindposes: Vec<_> = gltf @@ -661,7 +657,7 @@ async fn load_gltf<'a, 'b, 'c>( let scene_handle = load_context.add_loaded_labeled_asset(scene_label(&scene), loaded_scene); if let Some(name) = scene.name() { - named_scenes.insert(name.to_string(), scene_handle.clone()); + named_scenes.insert(name.into(), scene_handle.clone()); } scenes.push(scene_handle); } diff --git a/crates/bevy_gltf/src/vertex_attributes.rs b/crates/bevy_gltf/src/vertex_attributes.rs index 49f9924187..347b4e8ab5 100644 --- a/crates/bevy_gltf/src/vertex_attributes.rs +++ b/crates/bevy_gltf/src/vertex_attributes.rs @@ -255,7 +255,7 @@ pub(crate) fn convert_attribute( semantic: gltf::Semantic, accessor: gltf::Accessor, buffer_data: &Vec>, - custom_vertex_attributes: &HashMap, + custom_vertex_attributes: &HashMap, MeshVertexAttribute>, ) -> Result<(MeshVertexAttribute, Values), ConvertAttributeError> { if let Some((attribute, conversion)) = match &semantic { gltf::Semantic::Positions => Some((Mesh::ATTRIBUTE_POSITION, ConversionMode::Any)), @@ -271,7 +271,7 @@ pub(crate) fn convert_attribute( Some((Mesh::ATTRIBUTE_JOINT_WEIGHT, ConversionMode::JointWeight)) } gltf::Semantic::Extras(name) => custom_vertex_attributes - .get(name) + .get(name.as_str()) .map(|attr| (attr.clone(), ConversionMode::Any)), _ => None, } { diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 23822e5659..2e2f2eeafa 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -4,10 +4,11 @@ use bevy_asset::{AssetEvent, AssetId, Assets}; use bevy_ecs::system::{Res, ResMut}; use bevy_ecs::{event::EventReader, system::Resource}; use bevy_tasks::Task; +use bevy_utils::hashbrown::hash_map::EntryRef; use bevy_utils::{ default, tracing::{debug, error}, - Entry, HashMap, HashSet, + HashMap, HashSet, }; use naga::valid::Capabilities; use std::{ @@ -122,7 +123,7 @@ impl CachedPipelineState { #[derive(Default)] struct ShaderData { pipelines: HashSet, - processed_shaders: HashMap, ErasedShaderModule>, + processed_shaders: HashMap, ErasedShaderModule>, resolved_imports: HashMap>, dependents: HashSet>, } @@ -274,9 +275,9 @@ impl ShaderCache { data.pipelines.insert(pipeline); // PERF: this shader_defs clone isn't great. use raw_entry_mut when it stabilizes - let module = match data.processed_shaders.entry(shader_defs.to_vec()) { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => { + let module = match data.processed_shaders.entry_ref(shader_defs) { + EntryRef::Occupied(entry) => entry.into_mut(), + EntryRef::Vacant(entry) => { let mut shader_defs = shader_defs.to_vec(); #[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))] { diff --git a/tools/build-templated-pages/Cargo.toml b/tools/build-templated-pages/Cargo.toml index c62a0e23ab..d98a6084d7 100644 --- a/tools/build-templated-pages/Cargo.toml +++ b/tools/build-templated-pages/Cargo.toml @@ -14,3 +14,4 @@ toml_edit = { version = "0.22", default-features = false, features = ["parse"] } tera = "1.15" serde = { version = "1.0", features = ["derive"] } bitflags = "2.3" +hashbrown = { version = "0.14", features = ["serde"] } diff --git a/tools/build-templated-pages/src/examples.rs b/tools/build-templated-pages/src/examples.rs index b1cdc3ad32..db942da289 100644 --- a/tools/build-templated-pages/src/examples.rs +++ b/tools/build-templated-pages/src/examples.rs @@ -1,5 +1,6 @@ -use std::{cmp::Ordering, collections::HashMap, fs::File}; +use std::{cmp::Ordering, fs::File}; +use hashbrown::HashMap; use serde::Serialize; use tera::{Context, Tera}; use toml_edit::Document; @@ -80,7 +81,7 @@ fn parse_examples(panic_on_missing: bool) -> Vec { .collect() } -fn parse_categories() -> HashMap { +fn parse_categories() -> HashMap, String> { let manifest_file = std::fs::read_to_string("Cargo.toml").unwrap(); let manifest = manifest_file.parse::().unwrap(); manifest @@ -95,7 +96,7 @@ fn parse_categories() -> HashMap { .iter() .map(|v| { ( - v.get("name").unwrap().as_str().unwrap().to_string(), + v.get("name").unwrap().as_str().unwrap().into(), v.get("description").unwrap().as_str().unwrap().to_string(), ) }) @@ -107,10 +108,10 @@ pub(crate) fn check(what_to_run: Command) { if what_to_run.contains(Command::UPDATE) { let categories = parse_categories(); - let examples_by_category: HashMap = examples + let examples_by_category: HashMap, Category> = examples .into_iter() - .fold(HashMap::>::new(), |mut v, ex| { - v.entry(ex.category.clone()).or_default().push(ex); + .fold(HashMap::, Vec>::new(), |mut v, ex| { + v.entry_ref(ex.category.as_str()).or_default().push(ex); v }) .into_iter()