1use core::{
2 cmp,
3 convert::TryFrom,
4 fmt,
5 iter::ExactSizeIterator,
6 mem,
7 num::{NonZeroU32, NonZeroU64},
8 ops::Range,
9 sync::atomic::{AtomicIsize, Ordering},
10};
11#[cfg(feature = "std")]
12use std::error::Error;
13#[allow(unused_imports)]
14use std::vec::Vec;
15
16use gloss_utils::abi_stable_aliases::std_types::RVec;
17#[cfg(not(target_arch = "wasm32"))]
18use gloss_utils::abi_stable_aliases::StableAbi;
19
20#[derive(Clone, Copy, Hash, Eq, Ord, PartialEq, PartialOrd)]
29#[repr(C)]
30#[cfg_attr(not(target_arch = "wasm32"), derive(StableAbi))]
31#[cfg_attr(not(target_arch = "wasm32"), sabi(missing_field(panic)))]
32pub struct Entity {
33 pub(crate) generation: NonZeroU32,
34 pub(crate) id: u32,
35}
36
37impl Entity {
38 pub const DANGLING: Entity = Entity {
44 generation: match NonZeroU32::new(u32::MAX) {
45 Some(x) => x,
46 None => unreachable!(),
47 },
48 id: u32::MAX,
49 };
50
51 pub fn to_bits(self) -> NonZeroU64 {
59 unsafe { NonZeroU64::new_unchecked((u64::from(self.generation.get()) << 32) | u64::from(self.id)) }
60 }
61
62 #[allow(clippy::cast_possible_truncation)]
68 pub fn from_bits(bits: u64) -> Option<Self> {
69 Some(Self {
70 generation: NonZeroU32::new((bits >> 32) as u32)?,
71 id: bits as u32,
72 })
73 }
74
75 pub fn id(self) -> u32 {
84 self.id
85 }
86}
87
88impl fmt::Debug for Entity {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 write!(f, "{}v{}", self.id, self.generation)
91 }
92}
93
94#[cfg(feature = "serde")]
95impl serde::Serialize for Entity {
96 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
97 where
98 S: serde::Serializer,
99 {
100 self.to_bits().serialize(serializer)
101 }
102}
103
104#[cfg(feature = "serde")]
105impl<'de> serde::Deserialize<'de> for Entity {
106 fn deserialize<D>(deserializer: D) -> Result<Entity, D::Error>
107 where
108 D: serde::Deserializer<'de>,
109 {
110 let bits = u64::deserialize(deserializer)?;
111
112 match Entity::from_bits(bits) {
113 Some(ent) => Ok(ent),
114 None => Err(serde::de::Error::invalid_value(
115 serde::de::Unexpected::Unsigned(bits),
116 &"`a valid `Entity` bitpattern",
117 )),
118 }
119 }
120}
121
122pub struct ReserveEntitiesIterator<'a> {
125 meta: &'a [EntityMeta],
127
128 id_iter: core::slice::Iter<'a, u32>,
130
131 id_range: core::ops::Range<u32>,
133}
134
135impl Iterator for ReserveEntitiesIterator<'_> {
136 type Item = Entity;
137
138 fn next(&mut self) -> Option<Self::Item> {
139 self.id_iter
140 .next()
141 .map(|&id| Entity {
142 generation: self.meta[id as usize].generation,
143 id,
144 })
145 .or_else(|| {
146 self.id_range.next().map(|id| Entity {
147 generation: NonZeroU32::new(1).unwrap(),
148 id,
149 })
150 })
151 }
152
153 fn size_hint(&self) -> (usize, Option<usize>) {
154 let len = self.id_iter.len() + self.id_range.len();
155 (len, Some(len))
156 }
157}
158
159impl ExactSizeIterator for ReserveEntitiesIterator<'_> {}
160
161#[derive(Default)]
162#[repr(C)]
163#[cfg_attr(not(target_arch = "wasm32"), derive(StableAbi))]
164pub(crate) struct Entities {
165 pub meta: RVec<EntityMeta>,
166
167 pending: RVec<u32>,
201 free_cursor: AtomicIsize,
202 len: u32,
203}
204
205#[allow(clippy::cast_possible_wrap)]
206#[allow(clippy::cast_sign_loss)]
207#[allow(clippy::cast_possible_truncation)]
208impl Entities {
209 pub fn reserve_entities(&self, count: u32) -> ReserveEntitiesIterator<'_> {
214 let range_end = self.free_cursor.fetch_sub(count as isize, Ordering::Relaxed);
218 let range_start = range_end - count as isize;
219
220 let freelist_range = range_start.max(0) as usize..range_end.max(0) as usize;
221
222 let (new_id_start, new_id_end) = if range_start >= 0 {
223 (0, 0)
225 } else {
226 let base = self.meta.len() as isize;
236
237 let new_id_end = u32::try_from(base - range_start).expect("too many entities");
238
239 let new_id_start = (base - range_end.min(0)) as u32;
241
242 (new_id_start, new_id_end)
243 };
244
245 ReserveEntitiesIterator {
246 meta: &self.meta[..],
247 id_iter: self.pending[freelist_range].iter(),
248 id_range: new_id_start..new_id_end,
249 }
250 }
251
252 pub fn reserve_entity(&self) -> Entity {
257 let n = self.free_cursor.fetch_sub(1, Ordering::Relaxed);
258 if n > 0 {
259 let id = self.pending[(n - 1) as usize];
261 Entity {
262 generation: self.meta[id as usize].generation,
263 id,
264 }
265 } else {
266 Entity {
272 generation: NonZeroU32::new(1).unwrap(),
273 id: u32::try_from(self.meta.len() as isize - n).expect("too many entities"),
274 }
275 }
276 }
277
278 fn verify_flushed(&mut self) {
280 debug_assert!(!self.needs_flush(), "flush() needs to be called before this operation is legal");
281 }
282
283 pub fn alloc(&mut self) -> Entity {
287 self.verify_flushed();
288
289 self.len += 1;
290 if let Some(id) = self.pending.pop() {
291 let new_free_cursor = self.pending.len() as isize;
292 self.free_cursor.store(new_free_cursor, Ordering::Relaxed); Entity {
294 generation: self.meta[id as usize].generation,
295 id,
296 }
297 } else {
298 let id = u32::try_from(self.meta.len()).expect("too many entities");
299 self.meta.push(EntityMeta::EMPTY);
300 Entity {
301 generation: NonZeroU32::new(1).unwrap(),
302 id,
303 }
304 }
305 }
306
307 pub fn alloc_many(&mut self, n: u32, archetype: u32, mut first_index: u32) -> AllocManyState {
312 self.verify_flushed();
313
314 let fresh = (n as usize).saturating_sub(self.pending.len()) as u32;
315 assert!((self.meta.len() + fresh as usize) < u32::MAX as usize, "too many entities");
316 let pending_end = self.pending.len().saturating_sub(n as usize);
317 for &id in &self.pending[pending_end..] {
318 self.meta[id as usize].location = Location {
319 archetype,
320 index: first_index,
321 };
322 first_index += 1;
323 }
324
325 let fresh_start = self.meta.len() as u32;
326 self.meta.extend((first_index..(first_index + fresh)).map(|index| EntityMeta {
327 generation: NonZeroU32::new(1).unwrap(),
328 location: Location { archetype, index },
329 }));
330
331 self.len += n;
332
333 AllocManyState {
334 fresh: fresh_start..(fresh_start + fresh),
335 pending_end,
336 }
337 }
338
339 pub fn finish_alloc_many(&mut self, pending_end: usize) {
344 self.pending.truncate(pending_end);
345 }
346
347 pub fn alloc_at(&mut self, entity: Entity) -> Option<Location> {
352 self.verify_flushed();
353
354 let loc = if entity.id as usize >= self.meta.len() {
355 self.pending.extend((self.meta.len() as u32)..entity.id);
356 let new_free_cursor = self.pending.len() as isize;
357 self.free_cursor.store(new_free_cursor, Ordering::Relaxed); self.meta.resize(entity.id as usize + 1, EntityMeta::EMPTY);
359 self.len += 1;
360 None
361 } else if let Some(index) = self.pending.iter().position(|item| *item == entity.id) {
362 self.pending.swap_remove(index);
363 let new_free_cursor = self.pending.len() as isize;
364 self.free_cursor.store(new_free_cursor, Ordering::Relaxed); self.len += 1;
366 None
367 } else {
368 Some(mem::replace(&mut self.meta[entity.id as usize].location, EntityMeta::EMPTY.location))
369 };
370
371 self.meta[entity.id as usize].generation = entity.generation;
372
373 loc
374 }
375
376 pub fn free(&mut self, entity: Entity) -> Result<Location, NoSuchEntity> {
380 self.verify_flushed();
381
382 let meta = self.meta.get_mut(entity.id as usize).ok_or(NoSuchEntity)?;
383 if meta.generation != entity.generation || meta.location.index == u32::MAX {
384 return Err(NoSuchEntity);
385 }
386
387 meta.generation = NonZeroU32::new(u32::from(meta.generation).wrapping_add(1)).unwrap_or_else(|| NonZeroU32::new(1).unwrap());
388
389 let loc = mem::replace(&mut meta.location, EntityMeta::EMPTY.location);
390
391 self.pending.push(entity.id);
392
393 let new_free_cursor = self.pending.len() as isize;
394 self.free_cursor.store(new_free_cursor, Ordering::Relaxed); self.len -= 1;
396
397 Ok(loc)
398 }
399
400 pub fn reserve(&mut self, additional: u32) {
402 self.verify_flushed();
403
404 let freelist_size = self.free_cursor.load(Ordering::Relaxed);
405 let shortfall = additional as isize - freelist_size;
406 if shortfall > 0 {
407 self.meta.reserve(shortfall as usize);
408 }
409 }
410
411 pub fn contains(&self, entity: Entity) -> bool {
412 if let Some(meta) = self.meta.get(entity.id as usize) {
425 meta.generation == entity.generation
426 && (meta.location.index != u32::MAX || self.pending[self.free_cursor.load(Ordering::Relaxed).max(0) as usize..].contains(&entity.id))
427 } else {
428 let free = self.free_cursor.load(Ordering::Relaxed);
430 entity.generation.get() == 1 && free < 0 && (entity.id as isize) < (free.abs() + self.meta.len() as isize)
431 }
432 }
433
434 pub fn clear(&mut self) {
435 self.meta.clear();
436 self.pending.clear();
437 self.free_cursor.store(0, Ordering::Relaxed); self.len = 0;
439 }
440
441 pub fn get_mut(&mut self, entity: Entity) -> Result<&mut Location, NoSuchEntity> {
445 let meta = self.meta.get_mut(entity.id as usize).ok_or(NoSuchEntity)?;
446 if meta.generation == entity.generation && meta.location.index != u32::MAX {
447 Ok(&mut meta.location)
448 } else {
449 Err(NoSuchEntity)
450 }
451 }
452
453 pub fn get(&self, entity: Entity) -> Result<Location, NoSuchEntity> {
456 if self.meta.len() <= entity.id as usize {
457 let free = self.free_cursor.load(Ordering::Relaxed);
459 if entity.generation.get() == 1 && free < 0 && (entity.id as isize) < (free.abs() + self.meta.len() as isize) {
460 return Ok(Location {
461 archetype: 0,
462 index: u32::MAX,
463 });
464 }
465 return Err(NoSuchEntity);
466 }
467 let meta = &self.meta[entity.id as usize];
468 if meta.generation != entity.generation || meta.location.index == u32::MAX {
469 return Err(NoSuchEntity);
470 }
471 Ok(meta.location)
472 }
473
474 pub unsafe fn resolve_unknown_gen(&self, id: u32) -> Entity {
479 let meta_len = self.meta.len();
480
481 if meta_len > id as usize {
482 let meta = &self.meta[id as usize];
483 Entity {
484 generation: meta.generation,
485 id,
486 }
487 } else {
488 let free_cursor = self.free_cursor.load(Ordering::Relaxed);
490 let num_pending = cmp::max(-free_cursor, 0) as usize;
491
492 if meta_len + num_pending > id as usize {
493 Entity {
495 generation: NonZeroU32::new(1).unwrap(),
496 id,
497 }
498 } else {
499 panic!("entity id is out of range");
500 }
501 }
502 }
503
504 fn needs_flush(&mut self) -> bool {
505 self.free_cursor.load(Ordering::Relaxed) != self.pending.len() as isize
507 }
508
509 pub fn flush(&mut self, mut init: impl FnMut(u32, &mut Location)) {
513 let free_cursor = self.free_cursor.load(Ordering::Relaxed);
515
516 let new_free_cursor = if free_cursor >= 0 {
517 free_cursor as usize
518 } else {
519 let old_meta_len = self.meta.len();
520 let new_meta_len = old_meta_len + -free_cursor as usize;
521 self.meta.resize(new_meta_len, EntityMeta::EMPTY);
522
523 self.len += -free_cursor as u32;
524 for (id, meta) in self.meta.iter_mut().enumerate().skip(old_meta_len) {
525 init(id as u32, &mut meta.location);
526 }
527
528 self.free_cursor.store(0, Ordering::Relaxed);
529 0
530 };
531
532 self.len += (self.pending.len() - new_free_cursor) as u32;
533 for id in self.pending.drain(new_free_cursor..) {
534 init(id, &mut self.meta[id as usize].location);
535 }
536 }
537
538 #[inline]
539 pub fn len(&self) -> u32 {
540 self.len
541 }
542}
543
544#[derive(Copy, Clone)]
545#[repr(C)]
546#[cfg_attr(not(target_arch = "wasm32"), derive(StableAbi))]
547pub(crate) struct EntityMeta {
548 pub generation: NonZeroU32,
549 pub location: Location,
550}
551
552impl EntityMeta {
553 const EMPTY: EntityMeta = EntityMeta {
554 generation: match NonZeroU32::new(1) {
555 Some(x) => x,
556 None => unreachable!(),
557 },
558 location: Location {
559 archetype: 0,
560 index: u32::MAX, },
562 };
563}
564
565#[derive(Copy, Clone)]
566#[repr(C)]
567#[cfg_attr(not(target_arch = "wasm32"), derive(StableAbi))]
568pub(crate) struct Location {
569 pub archetype: u32,
570 pub index: u32,
571}
572
573#[derive(Debug, Clone, Eq, PartialEq)]
575pub struct NoSuchEntity;
576
577impl fmt::Display for NoSuchEntity {
578 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
579 f.pad("no such entity")
580 }
581}
582
583#[cfg(feature = "std")]
584impl Error for NoSuchEntity {}
585
586#[derive(Clone)]
587pub(crate) struct AllocManyState {
588 pub pending_end: usize,
589 fresh: Range<u32>,
590}
591
592impl AllocManyState {
593 pub fn next(&mut self, entities: &Entities) -> Option<u32> {
594 if self.pending_end < entities.pending.len() {
595 let id = entities.pending[self.pending_end];
596 self.pending_end += 1;
597 Some(id)
598 } else {
599 self.fresh.next()
600 }
601 }
602
603 pub fn len(&self, entities: &Entities) -> usize {
604 self.fresh.len() + (entities.pending.len() - self.pending_end)
605 }
606}
607
608#[cfg(test)]
609mod tests {
610 use super::*;
611 use hashbrown::{HashMap, HashSet};
612 use rand::{rngs::StdRng, Rng, SeedableRng};
613
614 #[test]
615 fn entity_bits_roundtrip() {
616 let e = Entity {
617 generation: NonZeroU32::new(0xDEAD_BEEF).unwrap(),
618 id: 0xBAAD_F00D,
619 };
620 assert_eq!(Entity::from_bits(e.to_bits().into()).unwrap(), e);
621 }
622
623 #[test]
624 fn alloc_and_free() {
625 let mut rng = StdRng::seed_from_u64(0xFEED_FACE_DEAD_F00D);
626
627 let mut e = Entities::default();
628 let mut first_unused = 0u32;
629 let mut id_to_gen: HashMap<u32, u32> = HashMap::default();
630 let mut free_set: HashSet<u32> = HashSet::default();
631 let mut len = 0;
632
633 for _ in 0..100 {
634 let alloc = rng.gen_bool(0.7);
635 if alloc || first_unused == 0 {
636 let entity = e.alloc();
637 e.meta[entity.id as usize].location.index = 0;
638 len += 1;
639
640 let id = entity.id;
641 if !free_set.is_empty() {
642 assert!(free_set.remove(&id));
644 } else if id >= first_unused {
645 first_unused = id + 1;
646 }
647
648 e.get_mut(entity).unwrap().index = 37;
649
650 assert!(id_to_gen.insert(id, entity.generation.get()).is_none());
651 } else {
652 let id = rng.gen_range(0..first_unused);
654
655 let generation = id_to_gen.remove(&id);
656 let entity = Entity {
657 id,
658 generation: NonZeroU32::new(generation.unwrap_or_else(|| NonZeroU32::new(1).unwrap().get())).unwrap(),
659 };
660
661 assert_eq!(e.free(entity).is_ok(), generation.is_some());
662 if generation.is_some() {
663 len -= 1;
664 }
665
666 free_set.insert(id);
667 }
668 assert_eq!(e.len(), len);
669 }
670 }
671
672 #[test]
673 fn alloc_at() {
674 let mut e = Entities::default();
675 let mut old = Vec::new();
676
677 for _ in 0..2 {
678 let entity = e.alloc();
679 e.meta[entity.id as usize].location.index = 0;
680 old.push(entity);
681 e.free(entity).unwrap();
682 }
683
684 assert_eq!(e.len(), 0);
685
686 let id = old.first().unwrap().id();
687 assert!(old.iter().all(|entity| entity.id() == id));
688
689 let entity = *old.last().unwrap();
690 assert!(!e.contains(entity));
693 assert!(e.pending.contains(&entity.id()));
694 assert!(e.alloc_at(entity).is_none());
697 e.meta[entity.id as usize].location.index = 0;
698 assert!(e.contains(entity));
699 assert!(!e.pending.contains(&entity.id()));
701 assert_eq!(e.len(), 1);
702 assert!(e.alloc_at(entity).is_some());
705 e.meta[entity.id as usize].location.index = 0;
706 assert!(e.contains(entity));
707 assert_eq!(e.len(), 1);
708
709 assert_eq!(e.meta.len(), 1);
712 assert!(e
713 .alloc_at(Entity {
714 id: 3,
715 generation: NonZeroU32::new(2).unwrap(),
716 })
717 .is_none());
718 e.meta[entity.id as usize].location.index = 0;
719 assert_eq!(e.pending.len(), 2);
720 assert_eq!(&e.pending, &[1, 2]);
721 assert_eq!(e.meta.len(), 4);
722 }
723
724 #[test]
725 fn contains() {
726 let mut e = Entities::default();
727
728 for _ in 0..2 {
729 let entity = e.alloc();
730 e.meta[entity.id as usize].location.index = 0;
731 assert!(e.contains(entity));
732
733 e.free(entity).unwrap();
734 assert!(!e.contains(entity));
735 }
736
737 for _ in 0..3 {
739 let entity = e.reserve_entity();
740 assert!(e.contains(entity));
741 assert!(!e.contains(Entity {
742 id: entity.id,
743 generation: NonZeroU32::new(2).unwrap(),
744 }));
745 assert!(!e.contains(Entity {
746 id: entity.id + 1,
747 generation: NonZeroU32::new(1).unwrap(),
748 }));
749 }
750 }
751
752 #[allow(clippy::cast_possible_truncation)]
754 fn reserve_test_helper(reserve_n: impl FnOnce(&mut Entities, u32) -> Vec<Entity>) {
755 let mut e = Entities::default();
756
757 let mut v1: Vec<Entity> = (0..10).map(|_| e.alloc()).collect();
759 for &entity in &v1 {
760 e.meta[entity.id as usize].location.index = 0;
761 }
762 assert_eq!(v1.iter().map(|e| e.id).max(), Some(9));
763 for &entity in v1.iter() {
764 assert!(e.contains(entity));
765 e.get_mut(entity).unwrap().index = 37;
766 }
767
768 for entity in v1.drain(6..) {
770 e.free(entity).unwrap();
771 }
772 assert_eq!(e.free_cursor.load(Ordering::Relaxed), 4);
773
774 let v2 = reserve_n(&mut e, 10);
777 assert_eq!(v2.iter().map(|e| e.id).max(), Some(15));
778
779 assert!(v2.iter().all(|&entity| e.contains(entity)));
781
782 let mut v3: Vec<Entity> = v1.iter().chain(v2.iter()).copied().collect();
784 assert_eq!(v3.len(), 16);
785 v3.sort_by_key(|entity| entity.id);
786 for (i, entity) in v3.into_iter().enumerate() {
787 assert_eq!(entity.id, i as u32);
788 }
789
790 assert_eq!(e.free_cursor.load(Ordering::Relaxed), -6);
792
793 let mut flushed = Vec::new();
794 e.flush(|id, loc| {
795 loc.index = 0;
796 flushed.push(id);
797 });
798 flushed.sort_unstable();
799
800 assert_eq!(flushed, (6..16).collect::<Vec<_>>());
801 }
802
803 #[test]
804 fn reserve_entity() {
805 reserve_test_helper(|e, n| (0..n).map(|_| e.reserve_entity()).collect());
806 }
807
808 #[test]
809 fn reserve_entities() {
810 reserve_test_helper(|e, n| e.reserve_entities(n).collect());
811 }
812
813 #[test]
814 fn reserve_grows() {
815 let mut e = Entities::default();
816 let _ = e.reserve_entity();
817 e.flush(|_, l| {
818 l.index = 0;
819 });
820 assert_eq!(e.len(), 1);
821 }
822
823 #[test]
824 fn reserve_grows_mixed() {
825 let mut e = Entities::default();
826 let a = e.alloc();
827 e.meta[a.id as usize].location.index = 0;
828 let b = e.alloc();
829 e.meta[b.id as usize].location.index = 0;
830 e.free(a).unwrap();
831 let _ = e.reserve_entities(3);
832 e.flush(|_, l| {
833 l.index = 0;
834 });
835 assert_eq!(e.len(), 4);
836 }
837
838 #[test]
839 fn alloc_at_regression() {
840 let mut e = Entities::default();
841 assert!(e
842 .alloc_at(Entity {
843 generation: NonZeroU32::new(1).unwrap(),
844 id: 1
845 })
846 .is_none());
847 assert!(!e.contains(Entity {
848 generation: NonZeroU32::new(1).unwrap(),
849 id: 0
850 }));
851 }
852}