checs/
entity.rs

1//! Entities and their allocation.
2
3use std::collections::BTreeSet;
4use std::error;
5use std::fmt;
6
7/// The underlying integer type that represents an entity.
8///
9/// > 4 billion entities ought to be enough for anybody.
10/// >
11/// > -- <cite>Someone, somewhere presumably</cite>
12#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
13pub(crate) struct Index(u32);
14
15impl Index {
16    pub const MAX: Index = Index(u32::MAX - 1);
17    pub const UNINIT: Index = Index(u32::MAX);
18
19    #[inline]
20    pub fn as_usize(self) -> usize {
21        self.0 as usize
22    }
23
24    #[inline]
25    pub fn from_usize_truncate(value: usize) -> Index {
26        #![allow(clippy::cast_possible_truncation)]
27
28        debug_assert!(u32::try_from(value).is_ok());
29        Index(value as u32)
30    }
31}
32
33/// A thing that can be associated with components.
34///
35/// An entity can be associated with components by inserting them in a [`ComponentVec`].
36///
37/// [`ComponentVec`]: crate::ComponentVec
38#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
39pub struct Entity(Index);
40
41impl Entity {
42    #[inline]
43    pub(crate) fn as_usize(self) -> usize {
44        self.0.as_usize()
45    }
46
47    /// Creates a new entity, without using an [`Allocator`].
48    ///
49    /// This is used for crate internal unit tests only.
50    #[allow(dead_code)]
51    #[inline]
52    pub(crate) fn from(value: u32) -> Entity {
53        Entity(Index(value))
54    }
55}
56
57/// An allocator that can create new entities and free old entities.
58///
59/// This is not necessarily meant to be used directly. Instead, a [`World`] can be used to manage
60/// entities and components, which maintains its own `Allocator`.
61///
62/// [`World`]: crate::World
63///
64/// # Examples
65///
66/// ```
67/// use checs::entity::{Allocator, Error};
68///
69/// let mut entities = Allocator::new();
70///
71/// let entity = entities.alloc();
72///
73/// assert_eq!(entities.free(entity), Ok(()));
74/// assert_eq!(entities.free(entity), Err(Error::DoubleFree));
75/// ```
76///
77/// Constructing an `Entity` without using an `Allocator` is currently not supported:
78///
79/// ```compile_fail
80/// let impossible = checs::Entity(99);
81///
82/// assert_eq!(entities.free(impossible), Err(Error::Unallocated));
83/// ```
84pub struct Allocator {
85    count: Index,
86    freed: BTreeSet<Entity>,
87}
88
89impl Allocator {
90    /// Constructs a new `Allocator`.
91    #[must_use]
92    #[inline]
93    pub fn new() -> Self {
94        Self {
95            count: Index(0),
96            freed: BTreeSet::new(),
97        }
98    }
99
100    /// Creates a new entity.
101    ///
102    /// # Panics
103    ///
104    /// Panics if the maximum number of entities has been reached. The current maximum is at
105    /// [`u32::MAX - 1`](u32::MAX).
106    #[must_use]
107    #[inline]
108    pub fn alloc(&mut self) -> Entity {
109        if let Some(entity) = self.freed.pop_first() {
110            entity
111        } else {
112            assert!(self.count < Index::MAX, "out of entities");
113
114            let entity = self.count;
115            self.count = Index(self.count.0 + 1);
116            Entity(entity)
117        }
118    }
119
120    /// Frees an entity.
121    ///
122    /// # Errors
123    ///
124    /// Returns an [`Error::Unallocated`] if the `entity` has never been allocated.
125    /// Returns an [`Error::DoubleFree`] if the `entity` has already been freed.
126    ///
127    /// # Note
128    ///
129    /// This methods does not and can not remove any components from the `entity`. To do this use a
130    /// higher-level collection like [`World`] and its [`remove`] method, which both frees an
131    /// entity and removes all of its components.
132    ///
133    /// [`remove`]: crate::World::remove
134    /// [`World`]: crate::World
135    #[inline]
136    pub fn free(&mut self, entity: Entity) -> Result<(), Error> {
137        if entity.0 >= self.count {
138            Err(Error::Unallocated)
139        } else if self.freed.insert(entity) {
140            Ok(())
141        } else {
142            Err(Error::DoubleFree)
143        }
144    }
145}
146
147impl Default for Allocator {
148    #[inline]
149    fn default() -> Allocator {
150        Allocator::new()
151    }
152}
153
154/// The error type which is returned from the methods of an [`Allocator`].
155#[derive(Debug, PartialEq)]
156pub enum Error {
157    /// An unallocated entity was freed.
158    Unallocated,
159    /// An entity was freed multiple times.
160    DoubleFree,
161}
162
163impl fmt::Display for Error {
164    #[inline]
165    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166        match *self {
167            Error::Unallocated => f.write_str("an unallocated entity cannot be freed"),
168            Error::DoubleFree => f.write_str("an entity cannot be freed twice"),
169        }
170    }
171}
172
173impl error::Error for Error {}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178
179    #[test]
180    fn alloc_free_and_reuse() {
181        let mut entity_allocator = Allocator::new();
182
183        let e0 = entity_allocator.alloc();
184        let e1 = entity_allocator.alloc();
185
186        assert_eq!(e0, Entity::from(0));
187        assert_eq!(e1, Entity::from(1));
188
189        entity_allocator.free(e0).unwrap();
190
191        let e2 = entity_allocator.alloc();
192        let e3 = entity_allocator.alloc();
193
194        assert_eq!(e2, Entity::from(0));
195        assert_eq!(e3, Entity::from(2));
196
197        // Double free an entity.
198        entity_allocator.free(e3).unwrap();
199        let err = entity_allocator.free(e3).unwrap_err();
200
201        assert_eq!(err, Error::DoubleFree);
202
203        // Free an unallocated entity.
204        let err = entity_allocator.free(Entity::from(99)).unwrap_err();
205
206        assert_eq!(err, Error::Unallocated);
207    }
208}