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}