@@ -12,6 +12,7 @@ use crate::{
12
12
} ,
13
13
component:: { Component , ComponentId , ComponentStorage , Components , StorageType , Tick } ,
14
14
entity:: { Entities , Entity , EntityLocation } ,
15
+ query:: DebugCheckedUnwrap ,
15
16
storage:: { SparseSetIndex , SparseSets , Storages , Table , TableRow } ,
16
17
TypeIdMap ,
17
18
} ;
@@ -269,10 +270,59 @@ impl SparseSetIndex for BundleId {
269
270
270
271
pub struct BundleInfo {
271
272
id : BundleId ,
273
+ // SAFETY: Every ID in this list must be valid within the World that owns the BundleInfo,
274
+ // must have its storage initialized (i.e. columns created in tables, sparse set created),
275
+ // and must be in the same order as the source bundle type writes its components in.
272
276
component_ids : Vec < ComponentId > ,
273
277
}
274
278
275
279
impl BundleInfo {
280
+ /// Create a new [`BundleInfo`].
281
+ ///
282
+ /// # Safety
283
+ ///
284
+ // Every ID in `component_ids` must be valid within the World that owns the BundleInfo,
285
+ // must have its storage initialized (i.e. columns created in tables, sparse set created),
286
+ // and must be in the same order as the source bundle type writes its components in.
287
+ unsafe fn new (
288
+ bundle_type_name : & ' static str ,
289
+ components : & Components ,
290
+ component_ids : Vec < ComponentId > ,
291
+ id : BundleId ,
292
+ ) -> BundleInfo {
293
+ let mut deduped = component_ids. clone ( ) ;
294
+ deduped. sort ( ) ;
295
+ deduped. dedup ( ) ;
296
+
297
+ if deduped. len ( ) != component_ids. len ( ) {
298
+ // TODO: Replace with `Vec::partition_dedup` once https://github.com/rust-lang/rust/issues/54279 is stabilized
299
+ let mut seen = HashSet :: new ( ) ;
300
+ let mut dups = Vec :: new ( ) ;
301
+ for id in component_ids {
302
+ if !seen. insert ( id) {
303
+ dups. push ( id) ;
304
+ }
305
+ }
306
+
307
+ let names = dups
308
+ . into_iter ( )
309
+ . map ( |id| {
310
+ // SAFETY: the caller ensures component_id is valid.
311
+ unsafe { components. get_info_unchecked ( id) . name ( ) }
312
+ } )
313
+ . collect :: < Vec < _ > > ( )
314
+ . join ( ", " ) ;
315
+
316
+ panic ! ( "Bundle {bundle_type_name} has duplicate components: {names}" ) ;
317
+ }
318
+
319
+ // SAFETY: The caller ensures that component_ids:
320
+ // - is valid for the associated world
321
+ // - has had its storage initialized
322
+ // - is in the same order as the source bundle type
323
+ BundleInfo { id, component_ids }
324
+ }
325
+
276
326
#[ inline]
277
327
pub const fn id ( & self ) -> BundleId {
278
328
self . id
@@ -400,7 +450,10 @@ impl BundleInfo {
400
450
let component_id = * self . component_ids . get_unchecked ( bundle_component) ;
401
451
match storage_type {
402
452
StorageType :: Table => {
403
- let column = table. get_column_mut ( component_id) . unwrap ( ) ;
453
+ let column =
454
+ // SAFETY: If component_id is in self.component_ids, BundleInfo::new requires that
455
+ // the target table contains the component.
456
+ unsafe { table. get_column_mut ( component_id) . debug_checked_unwrap ( ) } ;
404
457
// SAFETY: bundle_component is a valid index for this bundle
405
458
match bundle_component_status. get_status ( bundle_component) {
406
459
ComponentStatus :: Added => {
@@ -412,11 +465,11 @@ impl BundleInfo {
412
465
}
413
466
}
414
467
StorageType :: SparseSet => {
415
- sparse_sets . get_mut ( component_id ) . unwrap ( ) . insert (
416
- entity ,
417
- component_ptr ,
418
- change_tick ,
419
- ) ;
468
+ let sparse_set =
469
+ // SAFETY: If component_id is in self.component_ids, BundleInfo::new requires that
470
+ // a sparse set exists for the component.
471
+ unsafe { sparse_sets . get_mut ( component_id ) . debug_checked_unwrap ( ) } ;
472
+ sparse_set . insert ( entity , component_ptr , change_tick ) ;
420
473
}
421
474
}
422
475
bundle_component += 1 ;
@@ -543,11 +596,13 @@ impl<'a, 'b> BundleInserter<'a, 'b> {
543
596
match & mut self . result {
544
597
InsertBundleResult :: SameArchetype => {
545
598
// PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty)
546
- let add_bundle = self
547
- . archetype
548
- . edges ( )
549
- . get_add_bundle_internal ( self . bundle_info . id )
550
- . unwrap ( ) ;
599
+ // SAFETY: The edge is assured to be initialized when creating the BundleInserter
600
+ let add_bundle = unsafe {
601
+ self . archetype
602
+ . edges ( )
603
+ . get_add_bundle_internal ( self . bundle_info . id )
604
+ . debug_checked_unwrap ( )
605
+ } ;
551
606
self . bundle_info . write_components (
552
607
self . table ,
553
608
self . sparse_sets ,
@@ -562,7 +617,9 @@ impl<'a, 'b> BundleInserter<'a, 'b> {
562
617
InsertBundleResult :: NewArchetypeSameTable { new_archetype } => {
563
618
let result = self . archetype . swap_remove ( location. archetype_row ) ;
564
619
if let Some ( swapped_entity) = result. swapped_entity {
565
- let swapped_location = self . entities . get ( swapped_entity) . unwrap ( ) ;
620
+ let swapped_location =
621
+ // SAFETY: If the swap was successful, swapped_entity must be valid.
622
+ unsafe { self . entities . get ( swapped_entity) . debug_checked_unwrap ( ) } ;
566
623
self . entities . set (
567
624
swapped_entity. index ( ) ,
568
625
EntityLocation {
@@ -577,11 +634,13 @@ impl<'a, 'b> BundleInserter<'a, 'b> {
577
634
self . entities . set ( entity. index ( ) , new_location) ;
578
635
579
636
// PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty)
580
- let add_bundle = self
581
- . archetype
582
- . edges ( )
583
- . get_add_bundle_internal ( self . bundle_info . id )
584
- . unwrap ( ) ;
637
+ // SAFETY: The edge is assured to be initialized when creating the BundleInserter
638
+ let add_bundle = unsafe {
639
+ self . archetype
640
+ . edges ( )
641
+ . get_add_bundle_internal ( self . bundle_info . id )
642
+ . debug_checked_unwrap ( )
643
+ } ;
585
644
self . bundle_info . write_components (
586
645
self . table ,
587
646
self . sparse_sets ,
@@ -599,7 +658,9 @@ impl<'a, 'b> BundleInserter<'a, 'b> {
599
658
} => {
600
659
let result = self . archetype . swap_remove ( location. archetype_row ) ;
601
660
if let Some ( swapped_entity) = result. swapped_entity {
602
- let swapped_location = self . entities . get ( swapped_entity) . unwrap ( ) ;
661
+ let swapped_location =
662
+ // SAFETY: If the swap was successful, swapped_entity must be valid.
663
+ unsafe { self . entities . get ( swapped_entity) . debug_checked_unwrap ( ) } ;
603
664
self . entities . set (
604
665
swapped_entity. index ( ) ,
605
666
EntityLocation {
@@ -620,7 +681,9 @@ impl<'a, 'b> BundleInserter<'a, 'b> {
620
681
621
682
// if an entity was moved into this entity's table spot, update its table row
622
683
if let Some ( swapped_entity) = move_result. swapped_entity {
623
- let swapped_location = self . entities . get ( swapped_entity) . unwrap ( ) ;
684
+ let swapped_location =
685
+ // SAFETY: If the swap was successful, swapped_entity must be valid.
686
+ unsafe { self . entities . get ( swapped_entity) . debug_checked_unwrap ( ) } ;
624
687
let swapped_archetype = if self . archetype . id ( ) == swapped_location. archetype_id
625
688
{
626
689
& mut * self . archetype
@@ -647,11 +710,13 @@ impl<'a, 'b> BundleInserter<'a, 'b> {
647
710
}
648
711
649
712
// PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty)
650
- let add_bundle = self
651
- . archetype
652
- . edges ( )
653
- . get_add_bundle_internal ( self . bundle_info . id )
654
- . unwrap ( ) ;
713
+ // SAFETY: The edge is assured to be initialized when creating the BundleInserter
714
+ let add_bundle = unsafe {
715
+ self . archetype
716
+ . edges ( )
717
+ . get_add_bundle_internal ( self . bundle_info . id )
718
+ . debug_checked_unwrap ( )
719
+ } ;
655
720
self . bundle_info . write_components (
656
721
new_table,
657
722
self . sparse_sets ,
@@ -750,8 +815,11 @@ impl Bundles {
750
815
T :: component_ids ( components, storages, & mut |id| component_ids. push ( id) ) ;
751
816
let id = BundleId ( bundle_infos. len ( ) ) ;
752
817
let bundle_info =
753
- // SAFETY: T::component_id ensures info was created
754
- unsafe { initialize_bundle ( std:: any:: type_name :: < T > ( ) , components, component_ids, id) } ;
818
+ // SAFETY: T::component_id ensures its:
819
+ // - info was created
820
+ // - appropriate storage for it has been initialized.
821
+ // - was created in the same order as the components in T
822
+ unsafe { BundleInfo :: new ( std:: any:: type_name :: < T > ( ) , components, component_ids, id) } ;
755
823
bundle_infos. push ( bundle_info) ;
756
824
id
757
825
} ) ;
@@ -818,44 +886,6 @@ impl Bundles {
818
886
}
819
887
}
820
888
821
- /// # Safety
822
- ///
823
- /// `component_id` must be valid [`ComponentId`]'s
824
- unsafe fn initialize_bundle (
825
- bundle_type_name : & ' static str ,
826
- components : & Components ,
827
- component_ids : Vec < ComponentId > ,
828
- id : BundleId ,
829
- ) -> BundleInfo {
830
- let mut deduped = component_ids. clone ( ) ;
831
- deduped. sort ( ) ;
832
- deduped. dedup ( ) ;
833
-
834
- if deduped. len ( ) != component_ids. len ( ) {
835
- // TODO: Replace with `Vec::partition_dedup` once https://github.com/rust-lang/rust/issues/54279 is stabilized
836
- let mut seen = HashSet :: new ( ) ;
837
- let mut dups = Vec :: new ( ) ;
838
- for id in component_ids {
839
- if !seen. insert ( id) {
840
- dups. push ( id) ;
841
- }
842
- }
843
-
844
- let names = dups
845
- . into_iter ( )
846
- . map ( |id| {
847
- // SAFETY: component_id exists and is therefore valid
848
- unsafe { components. get_info_unchecked ( id) . name ( ) }
849
- } )
850
- . collect :: < Vec < _ > > ( )
851
- . join ( ", " ) ;
852
-
853
- panic ! ( "Bundle {bundle_type_name} has duplicate components: {names}" ) ;
854
- }
855
-
856
- BundleInfo { id, component_ids }
857
- }
858
-
859
889
/// Asserts that all components are part of [`Components`]
860
890
/// and initializes a [`BundleInfo`].
861
891
fn initialize_dynamic_bundle (
@@ -875,7 +905,7 @@ fn initialize_dynamic_bundle(
875
905
let id = BundleId ( bundle_infos. len ( ) ) ;
876
906
let bundle_info =
877
907
// SAFETY: `component_ids` are valid as they were just checked
878
- unsafe { initialize_bundle ( "<dynamic bundle>" , components, component_ids, id) } ;
908
+ unsafe { BundleInfo :: new ( "<dynamic bundle>" , components, component_ids, id) } ;
879
909
bundle_infos. push ( bundle_info) ;
880
910
881
911
( id, storage_types)
0 commit comments