bones_ecs/components/
untyped.rs

1use crate::prelude::*;
2
3use bones_schema::alloc::ResizableAlloc;
4use std::{
5    ffi::c_void,
6    mem::MaybeUninit,
7    ptr::{self},
8    rc::Rc,
9};
10
11/// Holds components of a given type indexed by `Entity`.
12///
13/// We do not check if the given entity is alive here, this should be done using `Entities`.
14pub struct UntypedComponentStore {
15    pub(crate) bitset: BitSetVec,
16    pub(crate) storage: ResizableAlloc,
17    pub(crate) max_id: usize,
18    pub(crate) schema: &'static Schema,
19}
20
21unsafe impl Sync for UntypedComponentStore {}
22unsafe impl Send for UntypedComponentStore {}
23
24impl Clone for UntypedComponentStore {
25    fn clone(&self) -> Self {
26        let new_storage = self.storage.clone();
27
28        for i in 0..self.max_id {
29            if self.bitset.bit_test(i) {
30                // SAFE: constructing an UntypedComponent store is unsafe, and the user affirms that
31                // clone_fn will not do anything unsound.
32                //
33                // - And our previous pointer is a valid pointer to component data
34                // - And our new pointer is a writable pointer with the same layout
35                unsafe {
36                    let prev_ptr = self.storage.unchecked_idx(i);
37                    let new_ptr = new_storage.unchecked_idx(i);
38                    (self
39                        .schema
40                        .clone_fn
41                        .as_ref()
42                        .expect("Cannot clone component")
43                        .get())(prev_ptr, new_ptr);
44                }
45            }
46        }
47
48        Self {
49            bitset: self.bitset.clone(),
50            storage: new_storage,
51            max_id: self.max_id,
52            schema: self.schema,
53        }
54    }
55}
56
57impl Drop for UntypedComponentStore {
58    fn drop(&mut self) {
59        if let Some(drop_fn) = &self.schema.drop_fn {
60            for i in 0..self.storage.capacity() {
61                if self.bitset.bit_test(i) {
62                    // SAFE: constructing an UntypedComponent store is unsafe, and the user affirms
63                    // that drop_fn will not do anything unsound.
64                    //
65                    // And our pointer is valid.
66                    unsafe {
67                        let ptr = self.storage.unchecked_idx(i);
68                        drop_fn.get()(ptr);
69                    }
70                }
71            }
72        }
73    }
74}
75
76impl UntypedComponentStore {
77    /// Create a arbitrary [`UntypedComponentStore`].
78    ///
79    /// In Rust, you will usually not use [`UntypedComponentStore`] and will use the statically
80    /// typed [`ComponentStore<T>`] instead.
81    pub fn new(schema: &'static Schema) -> Self {
82        Self {
83            bitset: create_bitset(),
84            storage: ResizableAlloc::new(schema.layout()),
85            max_id: 0,
86            schema,
87        }
88    }
89
90    /// Create an [`UntypedComponentStore`] that is valid for the given type `T`.
91    pub fn for_type<T: HasSchema>() -> Self {
92        Self {
93            bitset: create_bitset(),
94            storage: ResizableAlloc::new(T::schema().layout()),
95            max_id: 0,
96            schema: T::schema(),
97        }
98    }
99
100    /// Get the schema of the components stored.
101    pub fn schema(&self) -> &'static Schema {
102        self.schema
103    }
104
105    /// Insert component data for the given entity and get the previous component data if present.
106    /// # Panics
107    /// Panics if the schema of `T` doesn't match the store.
108    #[inline]
109    #[track_caller]
110    pub fn insert_box(&mut self, entity: Entity, data: SchemaBox) -> Option<SchemaBox> {
111        self.try_insert_box(entity, data).unwrap()
112    }
113
114    /// Insert component data for the given entity and get the previous component data if present.
115    /// # Errors
116    /// Errors if the schema of `T` doesn't match the store.
117    pub fn try_insert_box(
118        &mut self,
119        entity: Entity,
120        data: SchemaBox,
121    ) -> Result<Option<SchemaBox>, SchemaMismatchError> {
122        if self.schema != data.schema() {
123            Err(SchemaMismatchError)
124        } else {
125            let ptr = data.as_ptr();
126            // SOUND: we validated schema matches
127            let already_had_component = unsafe { self.insert_raw(entity, ptr) };
128            if already_had_component {
129                // Previous component data will be written to data pointer
130                Ok(Some(data))
131            } else {
132                // Don't run the data's destructor, it has been moved into the storage.
133                std::mem::forget(data);
134                Ok(None)
135            }
136        }
137    }
138
139    /// Insert component data for the given entity and get the previous component data if present.
140    /// # Panics
141    /// Panics if the schema of `T` doesn't match the store.
142    #[inline]
143    #[track_caller]
144    pub fn insert<T: HasSchema>(&mut self, entity: Entity, data: T) -> Option<T> {
145        self.try_insert(entity, data).unwrap()
146    }
147
148    /// Insert component data for the given entity and get the previous component data if present.
149    /// # Errors
150    /// Errors if the schema of `T` doesn't match the store.
151    pub fn try_insert<T: HasSchema>(
152        &mut self,
153        entity: Entity,
154        mut data: T,
155    ) -> Result<Option<T>, SchemaMismatchError> {
156        if self.schema != T::schema() {
157            Err(SchemaMismatchError)
158        } else {
159            let ptr = &mut data as *mut T as *mut c_void;
160            // SOUND: we validated schema matches
161            let already_had_component = unsafe { self.insert_raw(entity, ptr) };
162            if already_had_component {
163                // Previous component data will be written to data pointer
164                Ok(Some(data))
165            } else {
166                // Don't run the data's destructor, it has been moved into the storage.
167                std::mem::forget(data);
168                Ok(None)
169            }
170        }
171    }
172
173    /// Returns true if the entity already had a component of this type.
174    ///
175    /// If true is returned, the previous value of the pointer will be written to `data`.
176    ///
177    /// # Safety
178    /// - The data must be a pointer to memory with the same schema.
179    /// - If `false` is returned you must ensure the `data` pointer is not used after pushing.
180    pub unsafe fn insert_raw(&mut self, entity: Entity, data: *mut c_void) -> bool {
181        let index = entity.index() as usize;
182        let size = self.schema.layout().size();
183
184        // If the component already exists on the entity
185        if self.bitset.bit_test(entity.index() as usize) {
186            let ptr = self.storage.unchecked_idx(index);
187
188            // Swap the data with the data already there
189            ptr::swap_nonoverlapping(ptr, data, size);
190
191            // There was already a component of this type
192            true
193
194        // If the component does not already exist for this entity.
195        } else {
196            // Update our maximum enitity id.
197            self.max_id = self.max_id.max(index + 1);
198
199            // Make sure we have enough memory allocated for storage.
200            self.allocate_enough(index);
201
202            // Set the bit indicating that this entity has this component data stored.
203            self.bitset.bit_set(index);
204
205            // Copy the data from the data pointer into our storage
206            self.storage
207                .unchecked_idx(index)
208                .copy_from_nonoverlapping(data, size);
209
210            // There was not already a component of this type
211            false
212        }
213    }
214
215    /// Ensures that we have the storage filled at least until the `until` variable.
216    ///
217    /// Usually, set this to `entity.index`.
218    fn allocate_enough(&mut self, until: usize) {
219        if self.storage.capacity() <= until {
220            self.storage
221                // TODO: Determine a better policy for resizing and pre-allocating component storage.
222                // Right now we double the size of the storage every time we run out. It seems like we
223                // might be able to come up with a smarter policy. On top of that we should
224                // be able to create a type data for components ( see
225                // `bones_framework::metadata_asset()` for example ) that lets you customize the resize
226                // and also pre-allocation strategy for the component. Right now we don't pre-allocate
227                // any memory, but that could be useful for components that know there will be a lot of
228                // them, such as bullets.
229                .resize((until + 1) * 2)
230                .unwrap();
231        }
232    }
233
234    /// Get a reference to the component storage for the given [`Entity`].
235    /// # Panics
236    /// Panics if the schema of `T` doesn't match.
237    #[track_caller]
238    #[inline]
239    pub fn get<T: HasSchema>(&self, entity: Entity) -> Option<&T> {
240        self.try_get(entity).unwrap()
241    }
242
243    /// Get a reference to the component storage for the given [`Entity`].
244    /// # Errors
245    /// Errors if the schema of `T` doesn't match.
246    pub fn try_get<T: HasSchema>(&self, entity: Entity) -> Result<Option<&T>, SchemaMismatchError> {
247        self.get_ref(entity).map(|x| x.try_cast()).transpose()
248    }
249
250    /// Get a [`SchemaRef`] to the component for the given [`Entity`] if the entity has this
251    /// component.
252    #[inline]
253    pub fn get_ref(&self, entity: Entity) -> Option<SchemaRef<'_>> {
254        let idx = entity.index() as usize;
255        self.get_idx(idx)
256    }
257
258    fn get_idx(&self, idx: usize) -> Option<SchemaRef<'_>> {
259        if self.bitset.bit_test(idx) {
260            // SOUND: we ensure that there is allocated storge for entities that have their bit set.
261            let ptr = unsafe { self.storage.unchecked_idx(idx) };
262            // SOUND: we know that the pointer has our schema.
263            Some(unsafe { SchemaRef::from_ptr_schema(ptr, self.schema) })
264        } else {
265            None
266        }
267    }
268
269    /// Get a mutable reference to the component storage for the given [`Entity`].
270    /// # Panics
271    /// Panics if the schema of `T` doesn't match.
272    #[track_caller]
273    #[inline]
274    pub fn get_mut<T: HasSchema>(&mut self, entity: Entity) -> Option<&mut T> {
275        self.try_get_mut(entity).unwrap()
276    }
277
278    /// Get a mutable reference to the component storage for the given [`Entity`].
279    /// # Errors
280    /// Errors if the schema of `T` doesn't match.
281    pub fn try_get_mut<T: HasSchema>(
282        &mut self,
283        entity: Entity,
284    ) -> Result<Option<&mut T>, SchemaMismatchError> {
285        self.get_ref_mut(entity)
286            .map(|x| x.try_cast_into_mut())
287            .transpose()
288    }
289
290    /// Get a mutable reference to component storage for the given [`Entity`]
291    /// if it exists. Otherwise inserts `T` generated by calling parameter: `f`.
292    #[inline]
293    pub fn get_mut_or_insert<T: HasSchema>(
294        &mut self,
295        entity: Entity,
296        f: impl FnOnce() -> T,
297    ) -> &mut T {
298        if !self.bitset.bit_test(entity.index() as usize) {
299            self.insert(entity, f());
300        }
301        self.get_mut(entity).unwrap()
302    }
303
304    /// Get a [`SchemaRefMut`] to the component for the given [`Entity`]
305    #[inline]
306    pub fn get_ref_mut<'a>(&mut self, entity: Entity) -> Option<SchemaRefMut<'a>> {
307        let idx = entity.index() as usize;
308        self.get_idx_mut(idx)
309    }
310
311    fn get_idx_mut<'a>(&mut self, idx: usize) -> Option<SchemaRefMut<'a>> {
312        if self.bitset.bit_test(idx) {
313            // SOUND: we ensure that there is allocated storage for entities that have their bit
314            // set.
315            let ptr = unsafe { self.storage.unchecked_idx(idx) };
316            // SOUND: we know that the pointer has our schema.
317            Some(unsafe { SchemaRefMut::from_ptr_schema(ptr, self.schema) })
318        } else {
319            None
320        }
321    }
322
323    /// Get mutable references s to the component data for multiple entities at the same time.
324    ///
325    /// # Panics
326    ///
327    /// This will panic if the same entity is specified multiple times. This is invalid because it
328    /// would mean you would have two mutable references to the same component data at the same
329    /// time.
330    ///
331    /// This will also panic if there is a schema mismatch.
332    #[inline]
333    #[track_caller]
334    pub fn get_many_mut<const N: usize, T: HasSchema>(
335        &mut self,
336        entities: [Entity; N],
337    ) -> [Option<&mut T>; N] {
338        self.try_get_many_mut(entities).unwrap()
339    }
340
341    /// Get mutable references s to the component data for multiple entities at the same time.
342    ///
343    /// # Panics
344    ///
345    /// This will panic if the same entity is specified multiple times. This is invalid because it
346    /// would mean you would have two mutable references to the same component data at the same
347    /// time.
348    ///
349    /// # Errors
350    ///
351    /// This will error if there is a schema mismatch.
352    pub fn try_get_many_mut<const N: usize, T: HasSchema>(
353        &mut self,
354        entities: [Entity; N],
355    ) -> Result<[Option<&mut T>; N], SchemaMismatchError> {
356        if self.schema != T::schema() {
357            Err(SchemaMismatchError)
358        } else {
359            let mut refs = self.get_many_ref_mut(entities);
360            let refs = std::array::from_fn(|i| {
361                let r = refs[i].take();
362                // SOUND: we've validated the schema matches.
363                r.map(|r| unsafe { r.cast_into_mut_unchecked() })
364            });
365
366            Ok(refs)
367        }
368    }
369
370    /// Get [`SchemaRefMut`]s to the component data for multiple entities at the same time.
371    ///
372    /// # Panics
373    ///
374    /// This will panic if the same entity is specified multiple times. This is invalid because it
375    /// would mean you would have two mutable references to the same component data at the same
376    /// time.
377    pub fn get_many_ref_mut<const N: usize>(
378        &mut self,
379        entities: [Entity; N],
380    ) -> [Option<SchemaRefMut<'_>>; N] {
381        // Sort a copy of the passed in entities list.
382        let mut sorted = entities;
383        sorted.sort_unstable();
384        // Detect duplicates.
385        //
386        // Since we have sorted the slice, any duplicates will be adjacent to each-other, and we
387        // only have to make sure that for every item in the slice, the one after it is not the same
388        // as it.
389        for i in 0..(N - 1) {
390            if sorted[i] == sorted[i + 1] {
391                panic!("All entities passed to `get_multiple_mut()` must be unique.");
392            }
393        }
394
395        std::array::from_fn(|i| {
396            let index = entities[i].index() as usize;
397
398            if self.bitset.bit_test(index) {
399                // SOUND: we've already validated that the contents of storage is valid for type T.
400                // The new lifetime is sound because we validate that all of these borrows don't
401                // overlap and their lifetimes are that of the &mut self borrow.
402                unsafe {
403                    let ptr = self.storage.unchecked_idx(index);
404                    Some(SchemaRefMut::from_ptr_schema(ptr, self.schema))
405                }
406            } else {
407                None
408            }
409        })
410    }
411
412    /// Remove the component data for the entity if it exists.
413    /// # Errors
414    /// Errors if the schema doesn't match.
415    #[inline]
416    #[track_caller]
417    pub fn remove<T: HasSchema>(&mut self, entity: Entity) -> Option<T> {
418        self.try_remove(entity).unwrap()
419    }
420
421    /// Remove the component data for the entity if it exists.
422    /// # Errors
423    /// Errors if the schema doesn't match.
424    pub fn try_remove<T: HasSchema>(
425        &mut self,
426        entity: Entity,
427    ) -> Result<Option<T>, SchemaMismatchError> {
428        if self.schema != T::schema() {
429            Err(SchemaMismatchError)
430        } else if self.bitset.contains(entity) {
431            let mut data = MaybeUninit::<T>::uninit();
432            // SOUND: the data doesn't overlap the storage.
433            unsafe { self.remove_raw(entity, Some(data.as_mut_ptr() as *mut c_void)) };
434
435            // SOUND: we've initialized the data.
436            Ok(Some(unsafe { data.assume_init() }))
437        } else {
438            // SOUND: we don't use the out pointer.
439            unsafe { self.remove_raw(entity, None) };
440            Ok(None)
441        }
442    }
443
444    /// Remove the component data for the entity if it exists.
445    pub fn remove_box(&mut self, entity: Entity) -> Option<SchemaBox> {
446        if self.bitset.contains(entity) {
447            // SOUND: we will immediately initialize the schema box with data matching the schema.
448            let b = unsafe { SchemaBox::uninitialized(self.schema) };
449            // SOUND: the box data doesn't overlap the storage.
450            unsafe { self.remove_raw(entity, Some(b.as_ptr())) };
451            Some(b)
452        } else {
453            // SOUND: we don't use the out pointer.
454            unsafe { self.remove_raw(entity, None) };
455            None
456        }
457    }
458
459    /// If there is a previous value, `true` will be returned.
460    ///
461    /// If `out` is set and true is returned, the previous value will be written to it.
462    ///
463    /// # Safety
464    ///
465    /// If set, the `out` pointer, must not overlap the internal component storage.
466    pub unsafe fn remove_raw(&mut self, entity: Entity, out: Option<*mut c_void>) -> bool {
467        let index = entity.index() as usize;
468        let size = self.schema.layout().size();
469
470        if self.bitset.bit_test(index) {
471            self.bitset.bit_reset(index);
472
473            let ptr = self.storage.unchecked_idx(index);
474
475            if let Some(out) = out {
476                // SAFE: user asserts `out` is non-overlapping
477                out.copy_from_nonoverlapping(ptr, size);
478            } else if let Some(drop_fn) = &self.schema.drop_fn {
479                // SAFE: construcing `UntypedComponentStore` asserts the soundess of the drop_fn
480                //
481                // And ptr is a valid pointer to the component type.
482                drop_fn.get()(ptr);
483            }
484
485            // Found previous component
486            true
487        } else {
488            // No previous component
489            false
490        }
491    }
492
493    /// Get a reference to the component store if there is exactly one instance of the component.
494    pub fn get_single_with_bitset(
495        &self,
496        bitset: Rc<BitSetVec>,
497    ) -> Result<SchemaRef<'_>, QuerySingleError> {
498        if self.bitset().bit_count() == 0 || bitset.bit_count() == 0 {
499            // Both bitsets are empty so there are no matches
500            return Err(QuerySingleError::NoEntities);
501        }
502
503        let len = self.bitset().bit_len();
504        let mut iter = (0..len).filter(|&i| bitset.bit_test(i) && self.bitset().bit_test(i));
505
506        // Try to find the first match
507        let i = iter.next().ok_or(QuerySingleError::NoEntities)?;
508
509        if iter.next().is_some() {
510            // Found an unexpected second match in both bitsets
511            return Err(QuerySingleError::MultipleEntities);
512        }
513
514        // TODO: add unchecked variant to avoid redundant validation
515        self.get_idx(i).ok_or(QuerySingleError::NoEntities)
516    }
517
518    /// Get a mutable reference to the component store if there is exactly one instance of the
519    /// component.
520    pub fn get_single_with_bitset_mut(
521        &mut self,
522        bitset: Rc<BitSetVec>,
523    ) -> Result<SchemaRefMut<'_>, QuerySingleError> {
524        if self.bitset().bit_count() == 0 || bitset.bit_count() == 0 {
525            // Both bitsets are empty so there are no matches
526            return Err(QuerySingleError::NoEntities);
527        }
528
529        let len = self.bitset().bit_len();
530        let mut iter = (0..len).filter(|&i| bitset.bit_test(i) && self.bitset().bit_test(i));
531
532        // Try to find the first match
533        let i = iter.next().ok_or(QuerySingleError::NoEntities)?;
534
535        if iter.next().is_some() {
536            // Found an unexpected second match in both bitsets
537            return Err(QuerySingleError::MultipleEntities);
538        }
539
540        // TODO: add unchecked variant to avoid redundant validation
541        self.get_idx_mut(i).ok_or(QuerySingleError::NoEntities)
542    }
543
544    /// Iterates immutably over all components of this type.
545    ///
546    /// Very fast but doesn't allow joining with other component types.
547    pub fn iter(&self) -> UntypedComponentStoreIter<'_> {
548        UntypedComponentStoreIter {
549            store: self,
550            idx: 0,
551        }
552    }
553
554    /// Iterates mutably over all components of this type.
555    ///
556    /// Very fast but doesn't allow joining with other component types.
557    pub fn iter_mut(&mut self) -> UntypedComponentStoreIterMut<'_> {
558        UntypedComponentStoreIterMut {
559            store: self,
560            idx: 0,
561        }
562    }
563
564    /// Iterates immutably over the components of this type where `bitset` indicates the indices of
565    /// entities.
566    ///
567    /// Slower than `iter()` but allows joining between multiple component types.
568    pub fn iter_with_bitset(&self, bitset: Rc<BitSetVec>) -> UntypedComponentBitsetIterator<'_> {
569        UntypedComponentBitsetIterator {
570            current_id: 0,
571            components: self,
572            bitset,
573        }
574    }
575
576    /// Iterates mutable over the components of this type where `bitset` indicates the indices of
577    /// entities.
578    ///
579    /// Slower than `iter()` but allows joining between multiple component types.
580    pub fn iter_mut_with_bitset(
581        &mut self,
582        bitset: Rc<BitSetVec>,
583    ) -> UntypedComponentBitsetIteratorMut<'_> {
584        UntypedComponentBitsetIteratorMut {
585            current_id: 0,
586            components: self,
587            bitset,
588        }
589    }
590
591    /// Iterates immutably over the components of this type where `bitset` indicates the indices of
592    /// entities. Iterator provides Option, returning None if there is no component for entity in bitset.
593    pub fn iter_with_bitset_optional(
594        &self,
595        bitset: Rc<BitSetVec>,
596    ) -> UntypedComponentOptionalBitsetIterator<'_> {
597        let components_count = self.bitset.bit_count();
598        let query_count = bitset.bit_count();
599        UntypedComponentOptionalBitsetIterator {
600            inner: UntypedComponentBitsetIterator {
601                current_id: 0,
602                components: self,
603                bitset,
604            },
605            components_count,
606            query_count,
607            found: 0,
608        }
609    }
610
611    /// Iterates mutably over the components of this type where `bitset` indicates the indices of
612    /// entities. Iterator provides Option, returning None if there is no component for entity in bitset.
613    pub fn iter_mut_with_bitset_optional(
614        &mut self,
615        bitset: Rc<BitSetVec>,
616    ) -> UntypedComponentOptionalBitsetIteratorMut<'_> {
617        let components_count = self.bitset.bit_count();
618        let query_count = bitset.bit_count();
619        UntypedComponentOptionalBitsetIteratorMut {
620            inner: UntypedComponentBitsetIteratorMut {
621                current_id: 0,
622                components: self,
623                bitset,
624            },
625            components_count,
626            query_count,
627            found: 0,
628        }
629    }
630
631    /// Returns the bitset indicating which entity indices have a component associated to them.
632    ///
633    /// Useful to build conditions between multiple `Components`' bitsets.
634    ///
635    /// For example, take two bitsets from two different `Components` types. Then,
636    /// bitset1.clone().bit_and(bitset2); And finally, you can use bitset1 in `iter_with_bitset` and
637    /// `iter_mut_with_bitset`. This will iterate over the components of the entity only for
638    /// entities that have both components.
639    pub fn bitset(&self) -> &BitSetVec {
640        &self.bitset
641    }
642
643    /// Convert into a typed [`ComponentStore`].
644    /// # Panics
645    /// Panics if the schema doesn't match.
646    #[inline]
647    #[track_caller]
648    pub fn into_typed<T: HasSchema>(self) -> ComponentStore<T> {
649        self.try_into().unwrap()
650    }
651}
652
653/// Mutable iterator over pointers in an untyped component store.
654pub struct UntypedComponentStoreIter<'a> {
655    store: &'a UntypedComponentStore,
656    idx: usize,
657}
658impl<'a> Iterator for UntypedComponentStoreIter<'a> {
659    type Item = SchemaRef<'a>;
660    fn next(&mut self) -> Option<Self::Item> {
661        loop {
662            if self.idx < self.store.max_id {
663                if let Some(ptr) = self.store.get_idx(self.idx) {
664                    self.idx += 1;
665                    break Some(ptr);
666                }
667                self.idx += 1;
668            } else {
669                break None;
670            }
671        }
672    }
673}
674
675/// Mutable iterator over pointers in an untyped component store.
676pub struct UntypedComponentStoreIterMut<'a> {
677    store: &'a mut UntypedComponentStore,
678    idx: usize,
679}
680impl<'a> Iterator for UntypedComponentStoreIterMut<'a> {
681    type Item = SchemaRefMut<'a>;
682    fn next(&mut self) -> Option<Self::Item> {
683        loop {
684            if self.idx < self.store.max_id {
685                if let Some(ptr) = self.store.get_idx_mut(self.idx) {
686                    self.idx += 1;
687                    // Re-create the ref to extend the lifetime.
688                    // SOUND: We know the pointer will be valid for the lifetime of the store.
689                    break Some(unsafe {
690                        SchemaRefMut::from_ptr_schema(ptr.as_ptr(), ptr.schema())
691                    });
692                }
693                self.idx += 1;
694            } else {
695                break None;
696            }
697        }
698    }
699}