-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Nonmax all rows #19132
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
Nonmax all rows #19132
Changes from all commits
b3561e2
984b99e
f2bdd9d
67a468b
7fd04e5
52cc0bb
42e3196
a8bd757
a5e226b
f488f85
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 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -32,6 +32,7 @@ use core::{ | |||||||||||
hash::Hash, | ||||||||||||
ops::{Index, IndexMut, RangeFrom}, | ||||||||||||
}; | ||||||||||||
use nonmax::NonMaxU32; | ||||||||||||
|
||||||||||||
/// An opaque location within a [`Archetype`]. | ||||||||||||
/// | ||||||||||||
|
@@ -44,23 +45,30 @@ use core::{ | |||||||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||||||||||||
// SAFETY: Must be repr(transparent) due to the safety requirements on EntityLocation | ||||||||||||
#[repr(transparent)] | ||||||||||||
pub struct ArchetypeRow(u32); | ||||||||||||
pub struct ArchetypeRow(NonMaxU32); | ||||||||||||
|
||||||||||||
impl ArchetypeRow { | ||||||||||||
/// Index indicating an invalid archetype row. | ||||||||||||
/// This is meant to be used as a placeholder. | ||||||||||||
pub const INVALID: ArchetypeRow = ArchetypeRow(u32::MAX); | ||||||||||||
// TODO: Deprecate in favor of options, since `INVALID` is, technically, valid. | ||||||||||||
pub const INVALID: ArchetypeRow = ArchetypeRow(NonMaxU32::MAX); | ||||||||||||
|
||||||||||||
/// Creates a `ArchetypeRow`. | ||||||||||||
#[inline] | ||||||||||||
pub const fn new(index: usize) -> Self { | ||||||||||||
Self(index as u32) | ||||||||||||
pub const fn new(index: NonMaxU32) -> Self { | ||||||||||||
Self(index) | ||||||||||||
} | ||||||||||||
|
||||||||||||
/// Gets the index of the row. | ||||||||||||
#[inline] | ||||||||||||
pub const fn index(self) -> usize { | ||||||||||||
self.0 as usize | ||||||||||||
self.0.get() as usize | ||||||||||||
} | ||||||||||||
|
||||||||||||
/// Gets the index of the row. | ||||||||||||
#[inline] | ||||||||||||
pub const fn index_u32(self) -> u32 { | ||||||||||||
self.0.get() | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
|
@@ -467,6 +475,27 @@ impl Archetype { | |||||||||||
&self.entities | ||||||||||||
} | ||||||||||||
|
||||||||||||
/// Fetches the entities contained in this archetype. | ||||||||||||
#[inline] | ||||||||||||
pub fn entities_with_location(&self) -> impl Iterator<Item = (Entity, EntityLocation)> { | ||||||||||||
self.entities.iter().enumerate().map( | ||||||||||||
|(archetype_row, &ArchetypeEntity { entity, table_row })| { | ||||||||||||
( | ||||||||||||
entity, | ||||||||||||
EntityLocation { | ||||||||||||
archetype_id: self.id, | ||||||||||||
// SAFETY: The entities in the archetype must be unique and there are never more than u32::MAX entities. | ||||||||||||
archetype_row: unsafe { | ||||||||||||
ArchetypeRow::new(NonMaxU32::new_unchecked(archetype_row as u32)) | ||||||||||||
}, | ||||||||||||
table_id: self.table_id, | ||||||||||||
table_row, | ||||||||||||
}, | ||||||||||||
) | ||||||||||||
}, | ||||||||||||
) | ||||||||||||
} | ||||||||||||
|
||||||||||||
/// Gets an iterator of all of the components stored in [`Table`]s. | ||||||||||||
/// | ||||||||||||
/// All of the IDs are unique. | ||||||||||||
|
@@ -569,7 +598,8 @@ impl Archetype { | |||||||||||
entity: Entity, | ||||||||||||
table_row: TableRow, | ||||||||||||
) -> EntityLocation { | ||||||||||||
let archetype_row = ArchetypeRow::new(self.entities.len()); | ||||||||||||
// SAFETY: An entity can not have multiple archetype rows and there can not be more than u32::MAX entities. | ||||||||||||
let archetype_row = unsafe { ArchetypeRow::new(NonMaxU32::new_unchecked(self.len())) }; | ||||||||||||
Comment on lines
+601
to
+602
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. Something is off here. "there can not be more than u32::MAX entities" semantically means "there can be up to u32::MAX entities". That would mean this can be unsound if there really end up being If I understand #18704 correctly, then this is not a incorrectly worded comment: There can be 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. This is admittedly tricky to reason about. The index of the So the safety comment is correct everywhere. It's just a bit hard to get one's head around it unless you're familiar with the entity id scheme. If you can think of clearer wording, I'm open to suggestions. But off by one errors are so common, I don't think we need to bend over backwards to explain it at each safety comment. I think it's fine as it is. 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. So the idea is this:
If I am correct then this is safe. I am just not sure if the method safety docs need to note that one should not make an Maybe add to the comment I marked here that at the time of the call there can not be u32::MAX entities inside this archetype. That would more reflect on the 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.
Suggested change
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.
That's a good summary.
If I'm understanding your concerns correctly, we're on the same page that the only logical problem here is if the caller is inventing these values outside of an entity allocator. As long as these values come from an allocator, and the relevant The only danger is creating rows and entities "out of thin air". Nobody is doing that. Further, this is already expressly unsafe. Safety comments usually require these ids to be valid, etc. I don't disagree that this safety issue would be nice to address, but it should be somewhat obvious that the At the end of the day, I'm not opposed to this; I just don't think that should be grouped into this PR. |
||||||||||||
self.entities.push(ArchetypeEntity { entity, table_row }); | ||||||||||||
|
||||||||||||
EntityLocation { | ||||||||||||
|
@@ -606,8 +636,10 @@ impl Archetype { | |||||||||||
|
||||||||||||
/// Gets the total number of entities that belong to the archetype. | ||||||||||||
#[inline] | ||||||||||||
pub fn len(&self) -> usize { | ||||||||||||
self.entities.len() | ||||||||||||
pub fn len(&self) -> u32 { | ||||||||||||
// No entity may have more than one archetype row, so there are no duplicates, | ||||||||||||
// and there may only ever be u32::MAX entities, so the length never exceeds u32's cappacity. | ||||||||||||
self.entities.len() as u32 | ||||||||||||
} | ||||||||||||
|
||||||||||||
/// Checks if the archetype has any entities. | ||||||||||||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding my earlier comment, even if there are not
u32::MAX
entities butNonMaxU32::MAX
entities, that last one would have a row equal toINVALID
? If they all end up in the same archetype.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. It was that way before though. This will let us trade
INVALID
forNone
though in the future!Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not so happy with this reasoning when stumbling over a potential bug. 😅
If I am not mistaken this is only used in
EntityLocation::INVALID
and these are checked upon by looking at theArchetypeId
being invalid or not, so this seems to be no concern. Maybe this constant could be renamed toPLACEHOLDER
and warn in the docs that this may be a valid row in a full archetype.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're on the same page here. It's not good that
INVALID
is, in fact, valid.In the past, it wrapped
u32
, so we were forced to useu32::MAX
asNone
(Option
would take 64 bits!). In the future, making it non-max will let us removeINVALID
and just useOption
.Also note that this is only ever used in coordination with
ArchetypeId
, which has the same issue.We're in agreement that this would be better called
PLACEHOLDER
or something or removed and replaced with a properOption
. But I think changing names/semantics like this is better left for a different PR. For now, I just want to tighten up the type, now that entity rows are themselvesNonMax
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you be more comfortable with adding a "todo: deprecate because this row can indeed be valid" comment on the constant? That could be addressed in a future PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea! I've added a todo item. I suspect I can fulfill it in a future PR in the near future. I'd just like remote reservation to be merged first for convenience.