Skip to content

Commit 0076b40

Browse files
Shaturmockersf
authored andcommitted
Make Entity construction more ergonomic (#21246)
# Objective Entity serialization is necessary for networking. Entities can exist inside components and events. After deserialization, we simply map remote entities to local entities. To serialize entities efficiently, we split them into index and generation, which benefits from varint serialization. #19121 and #18704 changed the entity layout, and I like the new layout a lot. We can now use the extra bit from the index to store whether the generation is zero or not, avoiding the need to serialize the generation entirely. However, constructing new entities requires relying on the internal layout, which is not very ergonomic. For example, here is how an entity with index = 1 and generation = 1 can be created: ```rust let expected_entity = Entity::from_bits((1 ^ u32::MAX) as u64 | (1 << 32)); ``` ## Solution - Make `Entity::from_raw_and_generation` public. While at it, I also removed outdated comment. - Add `EntityRow::from_raw_u32` to make the initialization nicer. ## Testing - It's a trivial change, but I re-used `EntityRow::from_raw_u32` in unit tests to simplify them. ## Notes I'd probably rename `Entity::from_raw_and_generation` into `Entity::from_row_and_generation` or `Entity::from_index_and_generation`.
1 parent f7b3621 commit 0076b40

File tree

3 files changed

+58
-61
lines changed

3 files changed

+58
-61
lines changed

crates/bevy_ecs/src/entity/mod.rs

Lines changed: 52 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,16 @@ impl EntityRow {
125125
Self(index)
126126
}
127127

128+
/// Equivalent to [`new`](Self::new) except that it takes a `u32` instead of a `NonMaxU32`.
129+
///
130+
/// Returns `None` if the index is `u32::MAX`.
131+
pub const fn from_raw_u32(index: u32) -> Option<Self> {
132+
match NonMaxU32::new(index) {
133+
Some(index) => Some(Self(index)),
134+
None => None,
135+
}
136+
}
137+
128138
/// Gets the index of the entity.
129139
#[inline(always)]
130140
pub const fn index(self) -> u32 {
@@ -445,13 +455,9 @@ impl Hash for Entity {
445455
}
446456

447457
impl Entity {
448-
/// Construct an [`Entity`] from a raw `row` value and a non-zero `generation` value.
449-
/// Ensure that the generation value is never greater than `0x7FFF_FFFF`.
458+
/// Constructs an [`Entity`] from a raw `row` value and a non-zero `generation` value.
450459
#[inline(always)]
451-
pub(crate) const fn from_raw_and_generation(
452-
row: EntityRow,
453-
generation: EntityGeneration,
454-
) -> Entity {
460+
pub const fn from_raw_and_generation(row: EntityRow, generation: EntityGeneration) -> Entity {
455461
Self { row, generation }
456462
}
457463

@@ -1324,12 +1330,11 @@ mod tests {
13241330

13251331
#[test]
13261332
fn entity_bits_roundtrip() {
1327-
let r = EntityRow::new(NonMaxU32::new(0xDEADBEEF).unwrap());
1333+
let r = EntityRow::from_raw_u32(0xDEADBEEF).unwrap();
13281334
assert_eq!(EntityRow::from_bits(r.to_bits()), r);
13291335

1330-
// Generation cannot be greater than 0x7FFF_FFFF else it will be an invalid Entity id
13311336
let e = Entity::from_raw_and_generation(
1332-
EntityRow::new(NonMaxU32::new(0xDEADBEEF).unwrap()),
1337+
EntityRow::from_raw_u32(0xDEADBEEF).unwrap(),
13331338
EntityGeneration::from_bits(0x5AADF00D),
13341339
);
13351340
assert_eq!(Entity::from_bits(e.to_bits()), e);
@@ -1368,15 +1373,15 @@ mod tests {
13681373

13691374
#[test]
13701375
fn entity_const() {
1371-
const C1: Entity = Entity::from_raw(EntityRow::new(NonMaxU32::new(42).unwrap()));
1376+
const C1: Entity = Entity::from_raw(EntityRow::from_raw_u32(42).unwrap());
13721377
assert_eq!(42, C1.index());
13731378
assert_eq!(0, C1.generation().to_bits());
13741379

13751380
const C2: Entity = Entity::from_bits(0x0000_00ff_0000_00cc);
13761381
assert_eq!(!0x0000_00cc, C2.index());
13771382
assert_eq!(0x0000_00ff, C2.generation().to_bits());
13781383

1379-
const C3: u32 = Entity::from_raw(EntityRow::new(NonMaxU32::new(33).unwrap())).index();
1384+
const C3: u32 = Entity::from_raw(EntityRow::from_raw_u32(33).unwrap()).index();
13801385
assert_eq!(33, C3);
13811386

13821387
const C4: u32 = Entity::from_bits(0x00dd_00ff_1111_1111)
@@ -1421,41 +1426,41 @@ mod tests {
14211426
fn entity_comparison() {
14221427
assert_eq!(
14231428
Entity::from_raw_and_generation(
1424-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1429+
EntityRow::from_raw_u32(123).unwrap(),
14251430
EntityGeneration::from_bits(456)
14261431
),
14271432
Entity::from_raw_and_generation(
1428-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1433+
EntityRow::from_raw_u32(123).unwrap(),
14291434
EntityGeneration::from_bits(456)
14301435
)
14311436
);
14321437
assert_ne!(
14331438
Entity::from_raw_and_generation(
1434-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1439+
EntityRow::from_raw_u32(123).unwrap(),
14351440
EntityGeneration::from_bits(789)
14361441
),
14371442
Entity::from_raw_and_generation(
1438-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1443+
EntityRow::from_raw_u32(123).unwrap(),
14391444
EntityGeneration::from_bits(456)
14401445
)
14411446
);
14421447
assert_ne!(
14431448
Entity::from_raw_and_generation(
1444-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1449+
EntityRow::from_raw_u32(123).unwrap(),
14451450
EntityGeneration::from_bits(456)
14461451
),
14471452
Entity::from_raw_and_generation(
1448-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1453+
EntityRow::from_raw_u32(123).unwrap(),
14491454
EntityGeneration::from_bits(789)
14501455
)
14511456
);
14521457
assert_ne!(
14531458
Entity::from_raw_and_generation(
1454-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1459+
EntityRow::from_raw_u32(123).unwrap(),
14551460
EntityGeneration::from_bits(456)
14561461
),
14571462
Entity::from_raw_and_generation(
1458-
EntityRow::new(NonMaxU32::new(456).unwrap()),
1463+
EntityRow::from_raw_u32(456).unwrap(),
14591464
EntityGeneration::from_bits(123)
14601465
)
14611466
);
@@ -1464,93 +1469,93 @@ mod tests {
14641469

14651470
assert!(
14661471
Entity::from_raw_and_generation(
1467-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1472+
EntityRow::from_raw_u32(123).unwrap(),
14681473
EntityGeneration::from_bits(456)
14691474
) >= Entity::from_raw_and_generation(
1470-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1475+
EntityRow::from_raw_u32(123).unwrap(),
14711476
EntityGeneration::from_bits(456)
14721477
)
14731478
);
14741479
assert!(
14751480
Entity::from_raw_and_generation(
1476-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1481+
EntityRow::from_raw_u32(123).unwrap(),
14771482
EntityGeneration::from_bits(456)
14781483
) <= Entity::from_raw_and_generation(
1479-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1484+
EntityRow::from_raw_u32(123).unwrap(),
14801485
EntityGeneration::from_bits(456)
14811486
)
14821487
);
14831488
assert!(
14841489
!(Entity::from_raw_and_generation(
1485-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1490+
EntityRow::from_raw_u32(123).unwrap(),
14861491
EntityGeneration::from_bits(456)
14871492
) < Entity::from_raw_and_generation(
1488-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1493+
EntityRow::from_raw_u32(123).unwrap(),
14891494
EntityGeneration::from_bits(456)
14901495
))
14911496
);
14921497
assert!(
14931498
!(Entity::from_raw_and_generation(
1494-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1499+
EntityRow::from_raw_u32(123).unwrap(),
14951500
EntityGeneration::from_bits(456)
14961501
) > Entity::from_raw_and_generation(
1497-
EntityRow::new(NonMaxU32::new(123).unwrap()),
1502+
EntityRow::from_raw_u32(123).unwrap(),
14981503
EntityGeneration::from_bits(456)
14991504
))
15001505
);
15011506

15021507
assert!(
15031508
Entity::from_raw_and_generation(
1504-
EntityRow::new(NonMaxU32::new(9).unwrap()),
1509+
EntityRow::from_raw_u32(9).unwrap(),
15051510
EntityGeneration::from_bits(1)
15061511
) < Entity::from_raw_and_generation(
1507-
EntityRow::new(NonMaxU32::new(1).unwrap()),
1512+
EntityRow::from_raw_u32(1).unwrap(),
15081513
EntityGeneration::from_bits(9)
15091514
)
15101515
);
15111516
assert!(
15121517
Entity::from_raw_and_generation(
1513-
EntityRow::new(NonMaxU32::new(1).unwrap()),
1518+
EntityRow::from_raw_u32(1).unwrap(),
15141519
EntityGeneration::from_bits(9)
15151520
) > Entity::from_raw_and_generation(
1516-
EntityRow::new(NonMaxU32::new(9).unwrap()),
1521+
EntityRow::from_raw_u32(9).unwrap(),
15171522
EntityGeneration::from_bits(1)
15181523
)
15191524
);
15201525

15211526
assert!(
15221527
Entity::from_raw_and_generation(
1523-
EntityRow::new(NonMaxU32::new(1).unwrap()),
1528+
EntityRow::from_raw_u32(1).unwrap(),
15241529
EntityGeneration::from_bits(1)
15251530
) > Entity::from_raw_and_generation(
1526-
EntityRow::new(NonMaxU32::new(2).unwrap()),
1531+
EntityRow::from_raw_u32(2).unwrap(),
15271532
EntityGeneration::from_bits(1)
15281533
)
15291534
);
15301535
assert!(
15311536
Entity::from_raw_and_generation(
1532-
EntityRow::new(NonMaxU32::new(1).unwrap()),
1537+
EntityRow::from_raw_u32(1).unwrap(),
15331538
EntityGeneration::from_bits(1)
15341539
) >= Entity::from_raw_and_generation(
1535-
EntityRow::new(NonMaxU32::new(2).unwrap()),
1540+
EntityRow::from_raw_u32(2).unwrap(),
15361541
EntityGeneration::from_bits(1)
15371542
)
15381543
);
15391544
assert!(
15401545
Entity::from_raw_and_generation(
1541-
EntityRow::new(NonMaxU32::new(2).unwrap()),
1546+
EntityRow::from_raw_u32(2).unwrap(),
15421547
EntityGeneration::from_bits(2)
15431548
) < Entity::from_raw_and_generation(
1544-
EntityRow::new(NonMaxU32::new(1).unwrap()),
1549+
EntityRow::from_raw_u32(1).unwrap(),
15451550
EntityGeneration::from_bits(2)
15461551
)
15471552
);
15481553
assert!(
15491554
Entity::from_raw_and_generation(
1550-
EntityRow::new(NonMaxU32::new(2).unwrap()),
1555+
EntityRow::from_raw_u32(2).unwrap(),
15511556
EntityGeneration::from_bits(2)
15521557
) <= Entity::from_raw_and_generation(
1553-
EntityRow::new(NonMaxU32::new(1).unwrap()),
1558+
EntityRow::from_raw_u32(1).unwrap(),
15541559
EntityGeneration::from_bits(2)
15551560
)
15561561
);
@@ -1564,15 +1569,12 @@ mod tests {
15641569
let hash = EntityHash;
15651570

15661571
let first_id = 0xC0FFEE << 8;
1567-
let first_hash = hash.hash_one(Entity::from_raw(EntityRow::new(
1568-
NonMaxU32::new(first_id).unwrap(),
1569-
)));
1572+
let first_hash =
1573+
hash.hash_one(Entity::from_raw(EntityRow::from_raw_u32(first_id).unwrap()));
15701574

15711575
for i in 1..=255 {
15721576
let id = first_id + i;
1573-
let hash = hash.hash_one(Entity::from_raw(EntityRow::new(
1574-
NonMaxU32::new(id).unwrap(),
1575-
)));
1577+
let hash = hash.hash_one(Entity::from_raw(EntityRow::from_raw_u32(id).unwrap()));
15761578
assert_eq!(first_hash.wrapping_sub(hash) as u32, i);
15771579
}
15781580
}
@@ -1584,15 +1586,12 @@ mod tests {
15841586
let hash = EntityHash;
15851587

15861588
let first_id = 0xC0FFEE;
1587-
let first_hash = hash.hash_one(Entity::from_raw(EntityRow::new(
1588-
NonMaxU32::new(first_id).unwrap(),
1589-
))) >> 57;
1589+
let first_hash =
1590+
hash.hash_one(Entity::from_raw(EntityRow::from_raw_u32(first_id).unwrap())) >> 57;
15901591

15911592
for bit in 0..u32::BITS {
15921593
let id = first_id ^ (1 << bit);
1593-
let hash = hash.hash_one(Entity::from_raw(EntityRow::new(
1594-
NonMaxU32::new(id).unwrap(),
1595-
))) >> 57;
1594+
let hash = hash.hash_one(Entity::from_raw(EntityRow::from_raw_u32(id).unwrap())) >> 57;
15961595
assert_ne!(hash, first_hash);
15971596
}
15981597
}
@@ -1617,7 +1616,7 @@ mod tests {
16171616

16181617
#[test]
16191618
fn entity_debug() {
1620-
let entity = Entity::from_raw(EntityRow::new(NonMaxU32::new(42).unwrap()));
1619+
let entity = Entity::from_raw(EntityRow::from_raw_u32(42).unwrap());
16211620
let string = format!("{entity:?}");
16221621
assert_eq!(string, "42v0");
16231622

@@ -1628,7 +1627,7 @@ mod tests {
16281627

16291628
#[test]
16301629
fn entity_display() {
1631-
let entity = Entity::from_raw(EntityRow::new(NonMaxU32::new(42).unwrap()));
1630+
let entity = Entity::from_raw(EntityRow::from_raw_u32(42).unwrap());
16321631
let string = format!("{entity}");
16331632
assert_eq!(string, "42v0");
16341633

crates/bevy_ecs/src/storage/sparse_set.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -666,19 +666,18 @@ mod tests {
666666
storage::SparseSet,
667667
};
668668
use alloc::{vec, vec::Vec};
669-
use nonmax::NonMaxU32;
670669

671670
#[derive(Debug, Eq, PartialEq)]
672671
struct Foo(usize);
673672

674673
#[test]
675674
fn sparse_set() {
676675
let mut set = SparseSet::<Entity, Foo>::default();
677-
let e0 = Entity::from_raw(EntityRow::new(NonMaxU32::new(0).unwrap()));
678-
let e1 = Entity::from_raw(EntityRow::new(NonMaxU32::new(1).unwrap()));
679-
let e2 = Entity::from_raw(EntityRow::new(NonMaxU32::new(2).unwrap()));
680-
let e3 = Entity::from_raw(EntityRow::new(NonMaxU32::new(3).unwrap()));
681-
let e4 = Entity::from_raw(EntityRow::new(NonMaxU32::new(4).unwrap()));
676+
let e0 = Entity::from_raw(EntityRow::from_raw_u32(0).unwrap());
677+
let e1 = Entity::from_raw(EntityRow::from_raw_u32(1).unwrap());
678+
let e2 = Entity::from_raw(EntityRow::from_raw_u32(2).unwrap());
679+
let e3 = Entity::from_raw(EntityRow::from_raw_u32(3).unwrap());
680+
let e4 = Entity::from_raw(EntityRow::from_raw_u32(4).unwrap());
682681

683682
set.insert(e1, Foo(1));
684683
set.insert(e2, Foo(2));

crates/bevy_ecs/src/storage/table/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,6 @@ mod tests {
859859
storage::{TableBuilder, TableId, TableRow, Tables},
860860
};
861861
use alloc::vec::Vec;
862-
use nonmax::NonMaxU32;
863862

864863
#[derive(Component)]
865864
struct W<T>(T);
@@ -889,7 +888,7 @@ mod tests {
889888
.add_column(components.get_info(component_id).unwrap())
890889
.build();
891890
let entities = (0..200)
892-
.map(|index| Entity::from_raw(EntityRow::new(NonMaxU32::new(index).unwrap())))
891+
.map(|index| Entity::from_raw(EntityRow::from_raw_u32(index).unwrap()))
893892
.collect::<Vec<_>>();
894893
for entity in &entities {
895894
// SAFETY: we allocate and immediately set data afterwards

0 commit comments

Comments
 (0)