Skip to main content

steel_core/entity/manager/
entries.rs

1//! Slot storage for entity entries, so queries index a `Vec` instead of hashing each candidate.
2
3use rustc_hash::FxHashMap;
4
5use super::EntityEntry;
6
7/// Index into [`EntityEntries`]. A slot is not reused while a query borrows it under the manager lock.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub(super) struct EntitySlot(usize);
10
11#[derive(Default)]
12pub(super) struct EntityEntries {
13    by_id: FxHashMap<i32, EntitySlot>,
14    slots: Vec<Option<EntityEntry>>,
15    free: Vec<EntitySlot>,
16}
17
18impl EntityEntries {
19    pub(super) fn slot(&self, id: i32) -> Option<EntitySlot> {
20        self.by_id.get(&id).copied()
21    }
22
23    pub(super) fn entry(&self, slot: EntitySlot) -> &EntityEntry {
24        match &self.slots[slot.0] {
25            Some(entry) => entry,
26            None => unreachable!("spatial index refers to a vacant entity slot"),
27        }
28    }
29
30    pub(super) fn entry_mut(&mut self, slot: EntitySlot) -> &mut EntityEntry {
31        match &mut self.slots[slot.0] {
32            Some(entry) => entry,
33            None => unreachable!("spatial index refers to a vacant entity slot"),
34        }
35    }
36
37    pub(super) fn get(&self, id: i32) -> Option<&EntityEntry> {
38        self.slot(id).map(|slot| self.entry(slot))
39    }
40
41    pub(super) fn get_mut(&mut self, id: i32) -> Option<&mut EntityEntry> {
42        let slot = self.slot(id)?;
43
44        Some(self.entry_mut(slot))
45    }
46
47    pub(super) fn contains_key(&self, id: i32) -> bool {
48        self.by_id.contains_key(&id)
49    }
50
51    pub(super) fn len(&self) -> usize {
52        self.by_id.len()
53    }
54
55    pub(super) fn values(&self) -> impl Iterator<Item = &EntityEntry> {
56        self.slots.iter().filter_map(Option::as_ref)
57    }
58
59    pub(super) fn insert(&mut self, id: i32, entry: EntityEntry) -> EntitySlot {
60        let slot = if let Some(slot) = self.free.pop() {
61            self.slots[slot.0] = Some(entry);
62            slot
63        } else {
64            let slot = EntitySlot(self.slots.len());
65            self.slots.push(Some(entry));
66            slot
67        };
68
69        assert!(self.by_id.insert(id, slot).is_none(), "duplicate entity ID");
70
71        slot
72    }
73
74    pub(super) fn remove(&mut self, id: i32) -> Option<(EntitySlot, EntityEntry)> {
75        let slot = self.by_id.remove(&id)?;
76
77        let Some(entry) = self.slots[slot.0].take() else {
78            unreachable!("entity ID refers to a vacant entity slot");
79        };
80
81        self.free.push(slot);
82
83        Some((slot, entry))
84    }
85}