From f729b102fca98beebb281e409acd394d4223e63a Mon Sep 17 00:00:00 2001 From: Matthias Wuketich Date: Tue, 3 Jun 2025 22:16:53 +0200 Subject: [PATCH 1/8] added get_many and get_many_mut for Assets --- crates/bevy_asset/src/assets.rs | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 9fa8eb4381..4a1d51a6ec 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -422,6 +422,11 @@ impl Assets { } } + #[inline] + pub fn get_many(&self, ids: [AssetId; N]) -> [Option<&A>; N] { + ids.map(|id| self.get(id)) + } + /// Retrieves a mutable reference to the [`Asset`] with the given `id`, if it exists. /// Note that this supports anything that implements `Into>`, which includes [`Handle`] and [`AssetId`]. #[inline] @@ -437,6 +442,39 @@ impl Assets { result } + #[inline] + #[allow(unsafe_code)] + pub fn get_many_mut( + &mut self, + ids: [AssetId; N], + ) -> Option<[Option<&mut A>; N]> { + use std::mem::MaybeUninit; + + // SAFETY: Verify that all entities are unique + for i in 0..N { + for j in 0..i { + if ids[i] == ids[j] { + return None; + } + } + } + + let mut values = [(); N].map(|_| MaybeUninit::uninit()); + + for (value, asset) in core::iter::zip(&mut values, ids) { + let item: Option<*mut _> = match self.get_mut(asset) { + Some(asset) => Some(asset), + None => None, + }; + + *value = MaybeUninit::new(item); + } + + // SAFETY: Each value has been fully initialized. + let values = values.map(|x| unsafe { x.assume_init().map(|raw| std::mem::transmute(raw)) }); + Some(values) + } + /// Removes (and returns) the [`Asset`] with the given `id`, if it exists. /// Note that this supports anything that implements `Into>`, which includes [`Handle`] and [`AssetId`]. pub fn remove(&mut self, id: impl Into>) -> Option { From 6a3dbc2d6324c010c3ce025c6e091dffb2526e5d Mon Sep 17 00:00:00 2001 From: Matthias Wuketich Date: Tue, 3 Jun 2025 23:04:17 +0200 Subject: [PATCH 2/8] added unit tests --- crates/bevy_asset/src/assets.rs | 43 +++++++++++++++++++++++++++------ crates/bevy_asset/src/lib.rs | 2 +- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 4a1d51a6ec..5d0a5796a9 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -460,18 +460,13 @@ impl Assets { } let mut values = [(); N].map(|_| MaybeUninit::uninit()); - for (value, asset) in core::iter::zip(&mut values, ids) { - let item: Option<*mut _> = match self.get_mut(asset) { - Some(asset) => Some(asset), - None => None, - }; - - *value = MaybeUninit::new(item); + let asset = self.get_mut(asset).map(|asset| asset as *mut _); + *value = MaybeUninit::new(asset); } // SAFETY: Each value has been fully initialized. - let values = values.map(|x| unsafe { x.assume_init().map(|raw| std::mem::transmute(raw)) }); + let values = values.map(|x| unsafe { x.assume_init().map(|raw| &mut *raw) }); Some(values) } @@ -685,6 +680,8 @@ pub struct InvalidGenerationError { #[cfg(test)] mod test { + use crate::prelude::*; + use crate::tests::{SubText, TestAsset}; use crate::AssetIndex; #[test] @@ -696,4 +693,34 @@ mod test { let roundtripped = AssetIndex::from_bits(asset_index.to_bits()); assert_eq!(asset_index, roundtripped); } + + #[test] + fn get_many_mut_no_aliases() { + let mut assets = Assets::default(); + + let one = "One"; + let two = "Two"; + + let v = AssetIndex::from_bits(1); + let w = AssetIndex::from_bits(3); + + assets.insert(v, SubText { text: one.into() }); + assets.insert(w, SubText { text: two.into() }); + + let [x, y] = assets.get_many_mut([v.into(), w.into()]).unwrap(); + assert_eq!(x.unwrap().text, one); + assert_eq!(y.unwrap().text, two); + } + + #[test] + fn get_many_mut_aliases() { + let mut assets = Assets::default(); + + let v = AssetIndex::from_bits(1); + assets.insert(v, TestAsset); + let v = v.into(); + + let result = assets.get_many_mut([v, v]); + assert!(result.is_none()); + } } diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 5b680eb191..5d9136b6e7 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -712,7 +712,7 @@ mod tests { #[derive(Asset, TypePath, Debug)] pub struct SubText { - text: String, + pub text: String, } #[derive(Serialize, Deserialize)] From ced557f81a9ef566ff7a3bf677a1b050fc097185 Mon Sep 17 00:00:00 2001 From: Matthias Wuketich Date: Tue, 3 Jun 2025 23:21:39 +0200 Subject: [PATCH 3/8] added documentation --- crates/bevy_asset/src/assets.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 5d0a5796a9..526ba4dc88 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -423,6 +423,7 @@ impl Assets { } #[inline] + /// Retrieves many references to the [`Asset`] with the given `id`s, if they exists. pub fn get_many(&self, ids: [AssetId; N]) -> [Option<&A>; N] { ids.map(|id| self.get(id)) } @@ -444,13 +445,15 @@ impl Assets { #[inline] #[allow(unsafe_code)] + /// Retrieves may mutable references to the [`Asset`]s with the given `id`s, if they exists. + /// Will return `None` if any `id`s alias. pub fn get_many_mut( &mut self, ids: [AssetId; N], ) -> Option<[Option<&mut A>; N]> { use std::mem::MaybeUninit; - // SAFETY: Verify that all entities are unique + // SAFETY: Verify that all indices are unique for i in 0..N { for j in 0..i { if ids[i] == ids[j] { From 9de6310e88217a940eb2acad69b09fe53bc5ad56 Mon Sep 17 00:00:00 2001 From: Matthias Wuketich Date: Tue, 3 Jun 2025 23:52:05 +0200 Subject: [PATCH 4/8] fixed ci issues --- crates/bevy_asset/src/assets.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 526ba4dc88..dad1f379bb 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -451,7 +451,7 @@ impl Assets { &mut self, ids: [AssetId; N], ) -> Option<[Option<&mut A>; N]> { - use std::mem::MaybeUninit; + use core::mem::MaybeUninit; // SAFETY: Verify that all indices are unique for i in 0..N { @@ -464,7 +464,7 @@ impl Assets { let mut values = [(); N].map(|_| MaybeUninit::uninit()); for (value, asset) in core::iter::zip(&mut values, ids) { - let asset = self.get_mut(asset).map(|asset| asset as *mut _); + let asset = self.get_mut(asset).map(|asset| core::ptr::from_mut(asset)); *value = MaybeUninit::new(asset); } From a05f9e1a5cb6fdf467d70910179a772dba56ffa4 Mon Sep 17 00:00:00 2001 From: Matthias Wuketich Date: Wed, 4 Jun 2025 00:18:48 +0200 Subject: [PATCH 5/8] fixed clippy issue --- crates/bevy_asset/src/assets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index dad1f379bb..59a13a3da0 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -444,7 +444,7 @@ impl Assets { } #[inline] - #[allow(unsafe_code)] + #[expect(unsafe_code, reason = "Required to get several mutable references")] /// Retrieves may mutable references to the [`Asset`]s with the given `id`s, if they exists. /// Will return `None` if any `id`s alias. pub fn get_many_mut( From 2d83ec9926773f6724bc32a66e6f129ca680cb94 Mon Sep 17 00:00:00 2001 From: Matthias Wuketich Date: Wed, 4 Jun 2025 01:06:17 +0200 Subject: [PATCH 6/8] fixed unit tests --- crates/bevy_asset/src/assets.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 59a13a3da0..8605e287f8 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -699,16 +699,13 @@ mod test { #[test] fn get_many_mut_no_aliases() { - let mut assets = Assets::default(); + let mut assets: Assets = Assets::default(); let one = "One"; let two = "Two"; - let v = AssetIndex::from_bits(1); - let w = AssetIndex::from_bits(3); - - assets.insert(v, SubText { text: one.into() }); - assets.insert(w, SubText { text: two.into() }); + let v = assets.add(SubText { text: one.into() }).id(); + let w = assets.add(SubText { text: two.into() }).id(); let [x, y] = assets.get_many_mut([v.into(), w.into()]).unwrap(); assert_eq!(x.unwrap().text, one); @@ -717,13 +714,10 @@ mod test { #[test] fn get_many_mut_aliases() { - let mut assets = Assets::default(); + let mut assets: Assets = Assets::default(); - let v = AssetIndex::from_bits(1); - assets.insert(v, TestAsset); - let v = v.into(); - - let result = assets.get_many_mut([v, v]); + let id = assets.add(TestAsset).id(); + let result = assets.get_many_mut([id, id]); assert!(result.is_none()); } } From 66cdcd71883d08b2a09c248ef36d042db2149a76 Mon Sep 17 00:00:00 2001 From: Matthias Wuketich Date: Wed, 4 Jun 2025 01:11:58 +0200 Subject: [PATCH 7/8] fixed ci issue yet again --- crates/bevy_asset/src/assets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 8605e287f8..cef10e1ecb 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -707,7 +707,7 @@ mod test { let v = assets.add(SubText { text: one.into() }).id(); let w = assets.add(SubText { text: two.into() }).id(); - let [x, y] = assets.get_many_mut([v.into(), w.into()]).unwrap(); + let [x, y] = assets.get_many_mut([v, w]).unwrap(); assert_eq!(x.unwrap().text, one); assert_eq!(y.unwrap().text, two); } From d277d1ce0db2091327b922d4ff7ae0bcdc685cab Mon Sep 17 00:00:00 2001 From: Wuketuke <59253838+Wuketuke@users.noreply.github.com> Date: Tue, 10 Jun 2025 08:59:33 +0000 Subject: [PATCH 8/8] Oops, forgot a closing delimiter in assets.rs --- crates/bevy_asset/src/assets.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 50a63b0e29..3e6dfe39ab 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -471,6 +471,7 @@ impl Assets { // SAFETY: Each value has been fully initialized. let values = values.map(|x| unsafe { x.assume_init().map(|raw| &mut *raw) }); Some(values) + } /// Retrieves a mutable reference to the [`Asset`] with the given `id`, if it exists. ///