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}