-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
General entity set cleanup #21498
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
General entity set cleanup #21498
Changes from all commits
513da58
4e08725
bb95355
dceec17
68546a9
1f1d205
a1530b0
3c0c67a
ac73915
f390dd3
a77aaaa
4c5ccaa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,7 @@ use core::{ | |
| fmt::{Debug, Formatter}, | ||
| hash::{BuildHasher, Hash}, | ||
| iter::{self, FusedIterator}, | ||
| option, result, | ||
| option, ptr, result, | ||
| }; | ||
|
|
||
| use super::{Entity, UniqueEntityEquivalentSlice}; | ||
|
|
@@ -58,7 +58,7 @@ pub trait ContainsEntity { | |
| /// To obtain hash values forming the same total order as [`Entity`], any [`Hasher`] used must be | ||
| /// deterministic and concerning [`Entity`], collisionless. | ||
| /// Standard library hash collections handle collisions with an [`Eq`] fallback, but do not account for | ||
| /// determinism when [`BuildHasher`] is unspecified,. | ||
| /// determinism when [`BuildHasher`] is unspecified. | ||
| /// | ||
| /// [`Hash`]: core::hash::Hash | ||
| /// [`Hasher`]: core::hash::Hasher | ||
|
|
@@ -143,12 +143,12 @@ unsafe impl<T: EntityEquivalent> EntityEquivalent for Arc<T> {} | |
| /// As a consequence, [`into_iter()`] on `EntitySet` will always produce another `EntitySet`. | ||
| /// | ||
| /// Implementing this trait allows for unique query iteration over a list of entities. | ||
| /// See [`iter_many_unique`] and [`iter_many_unique_mut`] | ||
| /// See [`iter_many_unique`] and [`iter_many_unique_mut`]. | ||
| /// | ||
| /// Note that there is no guarantee of the [`IntoIterator`] impl being deterministic, | ||
| /// it might return different iterators when called multiple times. | ||
| /// Neither is there a guarantee that the comparison trait impls of `EntitySet` match that | ||
| /// of the respective [`EntitySetIterator`] (or of a [`Vec`] collected from its elements) | ||
| /// of the respective [`EntitySetIterator`] (or of a [`Vec`] collected from its elements). | ||
| /// | ||
| /// [`Self::IntoIter`]: IntoIterator::IntoIter | ||
| /// [`into_iter()`]: IntoIterator::into_iter | ||
|
|
@@ -342,9 +342,10 @@ pub trait FromEntitySetIterator<A: EntityEquivalent>: FromIterator<A> { | |
| impl<T: EntityEquivalent + Hash, S: BuildHasher + Default> FromEntitySetIterator<T> | ||
| for HashSet<T, S> | ||
| { | ||
| #[inline] | ||
| fn from_entity_set_iter<I: EntitySet<Item = T>>(set_iter: I) -> Self { | ||
| let iter = set_iter.into_iter(); | ||
| let set = HashSet::<T, S>::with_capacity_and_hasher(iter.size_hint().0, S::default()); | ||
| let set = HashSet::with_capacity_and_hasher(iter.size_hint().0, S::default()); | ||
| iter.fold(set, |mut set, e| { | ||
| // SAFETY: Every element in self is unique. | ||
| unsafe { | ||
|
|
@@ -358,14 +359,17 @@ impl<T: EntityEquivalent + Hash, S: BuildHasher + Default> FromEntitySetIterator | |
| /// An iterator that yields unique entities. | ||
| /// | ||
| /// This wrapper can provide an [`EntitySetIterator`] implementation when an instance of `I` is known to uphold uniqueness. | ||
| #[repr(transparent)] | ||
| pub struct UniqueEntityIter<I: Iterator<Item: EntityEquivalent>> { | ||
| iter: I, | ||
| } | ||
|
|
||
| impl<I: EntitySetIterator> UniqueEntityIter<I> { | ||
| /// Constructs a `UniqueEntityIter` from an [`EntitySetIterator`]. | ||
| pub fn from_entity_set_iterator<S>(iter: I) -> Self { | ||
| Self { iter } | ||
| #[inline] | ||
| pub const fn from_entity_set_iter(iter: I) -> Self { | ||
| // SAFETY: iter implements `EntitySetIterator`. | ||
| unsafe { Self::from_iter_unchecked(iter) } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -375,17 +379,40 @@ impl<I: Iterator<Item: EntityEquivalent>> UniqueEntityIter<I> { | |
| /// # Safety | ||
| /// `iter` must only yield unique elements. | ||
| /// As in, the resulting iterator must adhere to the safety contract of [`EntitySetIterator`]. | ||
| pub unsafe fn from_iterator_unchecked(iter: I) -> Self { | ||
| #[inline] | ||
| pub const unsafe fn from_iter_unchecked(iter: I) -> Self { | ||
| Self { iter } | ||
| } | ||
|
|
||
| /// Constructs a [`UniqueEntityIter`] from an iterator unsafely. | ||
| /// | ||
| /// # Safety | ||
| /// `iter` must only yield unique elements. | ||
| /// As in, the resulting iterator must adhere to the safety contract of [`EntitySetIterator`]. | ||
| #[inline] | ||
| pub const unsafe fn from_iter_ref_unchecked(iter: &I) -> &Self { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this makes sense for completeness, but there's not much you can do with an Same thing for
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For iterators, remember that is a For Right now, mutable |
||
| // SAFETY: UniqueEntityIter is a transparent wrapper around I. | ||
| unsafe { &*ptr::from_ref(iter).cast() } | ||
| } | ||
|
|
||
| /// Constructs a [`UniqueEntityIter`] from an iterator unsafely. | ||
| /// | ||
| /// # Safety | ||
| /// `iter` must only yield unique elements. | ||
| /// As in, the resulting iterator must adhere to the safety contract of [`EntitySetIterator`]. | ||
| #[inline] | ||
| pub const unsafe fn from_iter_mut_unchecked(iter: &mut I) -> &mut Self { | ||
| // SAFETY: UniqueEntityIter is a transparent wrapper around I. | ||
| unsafe { &mut *ptr::from_mut(iter).cast() } | ||
| } | ||
|
|
||
| /// Returns the inner `I`. | ||
| pub fn into_inner(self) -> I { | ||
| self.iter | ||
| } | ||
|
|
||
| /// Returns a reference to the inner `I`. | ||
| pub fn as_inner(&self) -> &I { | ||
| pub const fn as_inner(&self) -> &I { | ||
| &self.iter | ||
| } | ||
|
|
||
|
|
@@ -395,7 +422,7 @@ impl<I: Iterator<Item: EntityEquivalent>> UniqueEntityIter<I> { | |
| /// | ||
| /// `self` must always contain an iterator that yields unique elements, | ||
| /// even while this reference is live. | ||
| pub unsafe fn as_mut_inner(&mut self) -> &mut I { | ||
| pub const unsafe fn as_mut_inner(&mut self) -> &mut I { | ||
| &mut self.iter | ||
| } | ||
| } | ||
|
|
@@ -415,6 +442,7 @@ impl<I: Iterator<Item: EntityEquivalent>> Iterator for UniqueEntityIter<I> { | |
| impl<I: ExactSizeIterator<Item: EntityEquivalent>> ExactSizeIterator for UniqueEntityIter<I> {} | ||
|
|
||
| impl<I: DoubleEndedIterator<Item: EntityEquivalent>> DoubleEndedIterator for UniqueEntityIter<I> { | ||
| #[inline] | ||
| fn next_back(&mut self) -> Option<Self::Item> { | ||
| self.iter.next_back() | ||
| } | ||
|
|
@@ -506,7 +534,7 @@ mod tests { | |
|
|
||
| // SAFETY: SpawnBatchIter is `EntitySetIterator`, | ||
| let mut unique_entity_iter = | ||
| unsafe { UniqueEntityIter::from_iterator_unchecked(spawn_batch.iter()) }; | ||
| unsafe { UniqueEntityIter::from_iter_unchecked(spawn_batch.iter()) }; | ||
|
|
||
| let entity_set = unique_entity_iter | ||
| .by_ref() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,7 @@ use super::{Entity, EntityEquivalent, EntityHash, EntitySetIterator}; | |
| #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] | ||
| #[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))] | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| pub struct EntityHashMap<V>(pub(crate) HashMap<Entity, V, EntityHash>); | ||
| pub struct EntityHashMap<V>(HashMap<Entity, V, EntityHash>); | ||
|
|
||
| impl<V> EntityHashMap<V> { | ||
| /// Creates an empty `EntityHashMap`. | ||
|
|
@@ -35,11 +35,16 @@ impl<V> EntityHashMap<V> { | |
| /// | ||
| /// Equivalent to [`HashMap::with_capacity_and_hasher(n, EntityHash)`]. | ||
| /// | ||
| /// [`HashMap:with_capacity_and_hasher(n, EntityHash)`]: HashMap::with_capacity_and_hasher | ||
| /// [`HashMap::with_capacity_and_hasher(n, EntityHash)`]: HashMap::with_capacity_and_hasher | ||
| pub fn with_capacity(n: usize) -> Self { | ||
| Self(HashMap::with_capacity_and_hasher(n, EntityHash)) | ||
| } | ||
|
|
||
| /// Constructs an `EntityHashMap` from an [`HashMap`]. | ||
| pub const fn from_index_map(set: HashMap<Entity, V, EntityHash>) -> Self { | ||
| Self(set) | ||
| } | ||
|
|
||
| /// Returns the inner [`HashMap`]. | ||
| pub fn into_inner(self) -> HashMap<Entity, V, EntityHash> { | ||
| self.0 | ||
|
|
@@ -113,8 +118,15 @@ impl<V> FromIterator<(Entity, V)> for EntityHashMap<V> { | |
| } | ||
| } | ||
|
|
||
| impl<V> From<HashMap<Entity, V, EntityHash>> for EntityHashMap<V> { | ||
| fn from(value: HashMap<Entity, V, EntityHash>) -> Self { | ||
| Self(value) | ||
| } | ||
| } | ||
|
|
||
| impl<V, Q: EntityEquivalent + ?Sized> Index<&Q> for EntityHashMap<V> { | ||
| type Output = V; | ||
|
|
||
| fn index(&self, key: &Q) -> &V { | ||
| self.0.index(&key.entity()) | ||
| } | ||
|
|
@@ -156,8 +168,20 @@ impl<V> IntoIterator for EntityHashMap<V> { | |
| pub struct Keys<'a, V, S = EntityHash>(hash_map::Keys<'a, Entity, V>, PhantomData<S>); | ||
|
|
||
| impl<'a, V> Keys<'a, V> { | ||
| /// Constructs a [`Keys<'a, V, S>`] from a [`hash_map::Keys<'a, V>`] unsafely. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// `keys` must either be empty, or have been obtained from a | ||
| /// [`hash_map::HashMap`] using the `S` hasher. | ||
| pub const unsafe fn from_keys_unchecked<S>( | ||
| keys: hash_map::Keys<'a, Entity, V>, | ||
| ) -> Keys<'a, V, S> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might make sense to put this in a separate Oh, or is the point to be able to call it as
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup, if either
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice to see you active again!!
Right, I meant to have impl<'a, V, S> Keys<'a, V, S> {
pub const unsafe fn from_keys_unchecked(keys: map::Keys<'a, Entity, V>) -> Self {
Self(keys, PhantomData)
}
}That seems like the more idiomatic way to write a constructor function, and then ... Hmm, for that matter, why is
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Thank you!!
Hmm, I think I wasn't clear here.
Because all of this infrastructure is only for
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Maybe I'm just not entirely clear on what the expected use case of this method looks like. It's certainly possible for Rust to infer generic parameters on both types and methods! So something like fn foo<V, S>(map: &HashMap<Entity, V>) -> Keys<'_, V, S> {
unsafe { Keys::from_keys_unchecked(map.keys()) }
}where Anyway, this isn't a big deal either way! It seems odd to have an associated function that doesn't make any use of
I'm not sure I follow. If we always want
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We do currently always want As soon as you see the |
||
| Keys(keys, PhantomData) | ||
| } | ||
|
|
||
| /// Returns the inner [`Keys`](hash_map::Keys). | ||
| pub fn into_inner(self) -> hash_map::Keys<'a, Entity, V> { | ||
| pub const fn into_inner(self) -> hash_map::Keys<'a, Entity, V> { | ||
| self.0 | ||
| } | ||
| } | ||
|
|
@@ -188,7 +212,8 @@ impl<V> FusedIterator for Keys<'_, V> {} | |
|
|
||
| impl<V> Clone for Keys<'_, V> { | ||
| fn clone(&self) -> Self { | ||
| Self(self.0.clone(), PhantomData) | ||
| // SAFETY: We are cloning an already valid `Keys`. | ||
| unsafe { Self::from_keys_unchecked(self.0.clone()) } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -200,7 +225,8 @@ impl<V: Debug> Debug for Keys<'_, V> { | |
|
|
||
| impl<V> Default for Keys<'_, V> { | ||
| fn default() -> Self { | ||
| Self(Default::default(), PhantomData) | ||
| // SAFETY: `Keys` is empty. | ||
| unsafe { Self::from_keys_unchecked(Default::default()) } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -218,6 +244,18 @@ unsafe impl<V> EntitySetIterator for Keys<'_, V> {} | |
| pub struct IntoKeys<V, S = EntityHash>(hash_map::IntoKeys<Entity, V>, PhantomData<S>); | ||
|
|
||
| impl<V> IntoKeys<V> { | ||
| /// Constructs a [`IntoKeys<V, S>`] from a [`hash_map::IntoKeys<V>`] unsafely. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// `into_keys` must either be empty, or have been obtained from a | ||
| /// [`hash_map::HashMap`] using the `S` hasher. | ||
| pub const unsafe fn from_into_keys_unchecked<S>( | ||
| into_keys: hash_map::IntoKeys<Entity, V>, | ||
| ) -> IntoKeys<V, S> { | ||
| IntoKeys(into_keys, PhantomData) | ||
| } | ||
|
|
||
| /// Returns the inner [`IntoKeys`](hash_map::IntoKeys). | ||
| pub fn into_inner(self) -> hash_map::IntoKeys<Entity, V> { | ||
| self.0 | ||
|
|
@@ -259,7 +297,8 @@ impl<V: Debug> Debug for IntoKeys<V> { | |
|
|
||
| impl<V> Default for IntoKeys<V> { | ||
| fn default() -> Self { | ||
| Self(Default::default(), PhantomData) | ||
| // SAFETY: `IntoKeys` is empty. | ||
| unsafe { Self::from_into_keys_unchecked(Default::default()) } | ||
| } | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.