1use crate::{
6 IndexedID, PtrID, SlicePtrErr, SlicePtrRes,
7 bitalloc::IBitAlloc,
8 chunk::{Chunk, Unit},
9 gen_index::GenIndex,
10 iter::{EntityAllocConsumeIter, EntityAllocEditIter, EntityAllocReadIter},
11 policy::{AllocPolicy256, IAllocPolicy},
12};
13use std::{
14 cell::{BorrowMutError, Cell, RefCell},
15 ptr::NonNull,
16};
17
18#[derive(Debug)]
22pub enum EntityAccessErr {
23 UseAfterFree,
27
28 InvalidReference,
32}
33impl std::fmt::Display for EntityAccessErr {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 let msg = match self {
36 EntityAccessErr::UseAfterFree => "Entity has been freed (use-after-free).",
37 EntityAccessErr::InvalidReference => "Index is invalid (no entity referenced).",
38 };
39 f.write_str(msg)
40 }
41}
42impl std::error::Error for EntityAccessErr {}
43
44pub type EntityAccessRes<T = ()> = Result<T, EntityAccessErr>;
48
49pub struct EntityAlloc<E, P: IAllocPolicy = AllocPolicy256> {
103 pub(crate) chunks: RefCell<Vec<Chunk<E, P>>>,
104 pub(crate) free_head: Cell<u32>,
105 pub(crate) num_allocated: Cell<usize>,
106}
107impl<E, P: IAllocPolicy> Default for EntityAlloc<E, P> {
108 fn default() -> Self {
109 Self {
110 chunks: RefCell::new(Vec::new()),
111 free_head: Cell::new(0),
112 num_allocated: Cell::new(0),
113 }
114 }
115}
116impl<E, P: IAllocPolicy> EntityAlloc<E, P> {
117 pub fn new() -> Self {
119 Self::default()
120 }
121
122 pub fn with_capacity(cap: usize) -> Self {
124 let chunks = {
125 let nchunks = cap.div_ceil(P::CHUNK_SIZE);
126 let mut chunks = Vec::with_capacity(nchunks);
127 for chunk_id in 0..(nchunks as u32) {
128 let chunk = Chunk::new(chunk_id, chunk_id + 1);
129 chunks.push(chunk);
130 }
131 RefCell::new(chunks)
132 };
133 Self {
134 chunks,
135 free_head: Cell::new(0),
136 num_allocated: Cell::new(0),
137 }
138 }
139
140 pub(crate) fn try_allocate_unit(&self, val: E) -> Result<*const Unit<E>, (BorrowMutError, E)> {
141 let mut chunks = match self.chunks.try_borrow_mut() {
142 Ok(c) => c,
143 Err(e) => return Err((e, val)),
144 };
145 let chunk = Self::find_chunk(self.free_head.get(), &mut chunks);
146 let Ok(ptr) = chunk.allocate(val) else {
147 unreachable!("Internal error: Cannot allocate inside a non-full chunk");
148 };
149 self.num_allocated.set(self.num_allocated.get() + 1);
150 if chunk.is_full() {
151 self.free_head.set(chunk.free_next);
152 }
153 Ok(ptr)
154 }
155 fn find_chunk(free: u32, chunks: &mut Vec<Chunk<E, P>>) -> &mut Chunk<E, P> {
156 assert_ne!(free, u32::MAX, "OOM detected");
157 let free = free as usize;
158 while chunks.len() <= free {
159 let id = chunks.len() as u32;
160 chunks.push(Chunk::new(id, id + 1));
161 }
162 &mut chunks[free]
163 }
164 pub(crate) fn next_index(&self) -> GenIndex {
165 let chunks = self.chunks.borrow();
166 let free = self.free_head.get() as usize;
167 let Some(chunk) = chunks.get(free) else {
168 return GenIndex::compose(free << P::CHUNK_SIZE_LOG2, GenIndex::GEN_1);
169 };
170 let unit_id = chunk.next_available().unwrap();
171 chunk.indexed_id_of_unit(unit_id)
172 }
173
174 pub(crate) fn free_gen_index(&mut self, index: GenIndex) -> Option<E> {
175 let (chunk_id, unit_id, gene) = P::tear(index);
176 let chunks = self.chunks.get_mut();
177 let chunk = chunks.get_mut(chunk_id as usize)?;
178 let chunk_full = chunk.is_full();
179 let val = chunk.deallocate_checked(unit_id, gene)?;
180 self.num_allocated.set(self.num_allocated.get() - 1);
181 if chunk_full {
182 chunk.free_next = self.free_head.get();
183 self.free_head.set(chunk_id);
184 }
185 Some(val)
186 }
187 pub(crate) fn free_unit_ptr(&mut self, ptr: *const Unit<E>) -> Option<E> {
188 let indexed = self.index_of_unit_ptr(ptr).ok()?;
189 self.free_gen_index(indexed)
190 }
191 pub(crate) fn check_unit_validity(&self, ptr: *const Unit<E>) -> SlicePtrRes<GenIndex> {
192 let chunks = self.chunks.borrow();
193 for chunk in chunks.iter() {
194 let unit_id = match chunk.unit_of_ptr(ptr) {
195 Ok(id) => id,
196 Err(SlicePtrErr::OutOfRange) => continue,
197 Err(SlicePtrErr::NotAlignedWith) => return Err(SlicePtrErr::NotAlignedWith),
198 };
199 let unit = chunk.unit(unit_id);
200 return if unit.indexed.is_invalid() {
201 Err(SlicePtrErr::OutOfRange)
202 } else {
203 Ok(unit.indexed)
204 };
205 }
206 Err(SlicePtrErr::OutOfRange)
207 }
208 pub(crate) fn index_of_unit_ptr(&self, ptr: *const Unit<E>) -> SlicePtrRes<GenIndex> {
209 if cfg!(debug_assertions) {
210 self.check_unit_validity(ptr)
211 } else {
212 let indexed = unsafe { ptr.as_ref().ok_or(SlicePtrErr::OutOfRange)?.indexed };
213 if indexed.is_invalid() {
214 Err(SlicePtrErr::OutOfRange)
215 } else {
216 Ok(indexed)
217 }
218 }
219 }
220 pub(crate) fn unit_ptr_of_indexed(
221 &self,
222 indexed: GenIndex,
223 ) -> EntityAccessRes<NonNull<Unit<E>>> {
224 if indexed.is_invalid() {
225 return Err(EntityAccessErr::InvalidReference);
226 }
227 let (chunk_id, unit_id, _) = P::tear(indexed);
228 let chunks = self.chunks.borrow();
229 let Some(chunk) = chunks.get(chunk_id as usize) else {
230 return Err(EntityAccessErr::UseAfterFree);
231 };
232 let unit = chunk.unit(unit_id);
233 if unit.indexed != indexed {
234 return Err(EntityAccessErr::UseAfterFree);
235 }
236 Ok(NonNull::new(unit as *const _ as *mut Unit<E>).unwrap())
237 }
238 pub(crate) fn unit_mut_of_indexed(
239 &mut self,
240 indexed: GenIndex,
241 ) -> EntityAccessRes<&mut Unit<E>> {
242 if indexed.is_invalid() {
243 return Err(EntityAccessErr::InvalidReference);
244 }
245 let (chunk_id, unit_id, _) = P::tear(indexed);
246 let chunks = self.chunks.get_mut();
247 let Some(chunk) = chunks.get_mut(chunk_id as usize) else {
248 return Err(EntityAccessErr::UseAfterFree);
249 };
250 let unit = chunk.unit_mut(unit_id);
251 if unit.indexed != indexed {
252 return Err(EntityAccessErr::UseAfterFree);
253 }
254 Ok(unit)
255 }
256
257 pub fn remake_free_chain(&mut self) {
259 let chunks = self.chunks.get_mut();
260 let free_chunks = {
261 let mut free_chunks = Vec::with_capacity(chunks.len());
262 for chunk in chunks.iter_mut() {
263 if !chunk.is_full() {
264 free_chunks.push(chunk.chunk_id);
265 }
266 }
267 free_chunks
268 };
269 for i in 1..free_chunks.len() {
270 let chunk_id = free_chunks[i - 1];
271 let next_chunk_id = free_chunks[i];
272 let chunk = &mut chunks[chunk_id as usize];
273 chunk.free_next = next_chunk_id;
274 }
275 if let Some(&chunk_id) = free_chunks.first() {
276 self.free_head.set(chunk_id);
277 } else {
278 self.free_head.set(chunks.len() as u32);
279 }
280 }
281}
282
283impl<E, P: IAllocPolicy> EntityAlloc<E, P> {
284 pub fn free_if(&mut self, pred: impl FnMut(&E, PtrID<E, P>, IndexedID<E, P>) -> bool) {
286 self.fully_free_if(pred, drop);
287 }
288
289 pub fn fully_free_if(
294 &mut self,
295 mut should_free: impl FnMut(&E, PtrID<E, P>, IndexedID<E, P>) -> bool,
296 mut consume: impl FnMut(E),
297 ) {
298 let chunks = self.chunks.get_mut();
299 let mut num_last = self.num_allocated.get();
300 for chunk in chunks.iter_mut() {
301 if chunk.is_empty() {
302 continue;
303 }
304 if num_last == 0 {
305 break;
306 }
307 let chunk_full_previously = chunk.is_full();
308 let chunk_num_allocated = chunk.num_allocated as usize;
309 let num_freed = if chunk_num_allocated * 3 >= P::CHUNK_SIZE {
310 Self::densely_free_chunk(&mut should_free, &mut consume, chunk)
311 } else {
312 Self::sparsely_free_chunk(&mut should_free, &mut consume, chunk)
313 };
314
315 num_last -= chunk_num_allocated;
316 if num_freed > 0 {
318 self.num_allocated
319 .set(self.num_allocated.get().saturating_sub(num_freed));
320 }
321 if chunk_full_previously && !chunk.is_full() {
323 chunk.free_next = self.free_head.get();
324 self.free_head.set(chunk.chunk_id);
325 }
326 }
327 }
328
329 fn densely_free_chunk(
330 should_free: &mut impl FnMut(&E, PtrID<E, P>, IndexedID<E, P>) -> bool,
331 consume: &mut impl FnMut(E),
332 chunk: &mut Chunk<E, P>,
333 ) -> usize {
334 let mut num_freed = 0;
335 let mut count_down = chunk.num_allocated;
336 for unit_id in 0..(P::CHUNK_SIZE as u16) {
337 if count_down == 0 {
338 break;
339 }
340 let index = chunk.indexed_id_of_unit(unit_id);
341 if !chunk.allocated.is_allocated(unit_id) {
342 continue;
343 }
344 count_down -= 1;
345
346 let unit = chunk.unit_ptr_mut(unit_id);
347 let ptr = PtrID::from(unit);
348 let elem = unsafe { &*unit }.as_init_ref().unwrap();
349 if !should_free(elem, ptr, IndexedID::from(index)) {
350 continue;
351 }
352 let Some(e) = chunk.deallocate_unchecked(unit_id) else {
353 unreachable!("Deallocation failed unexpectedly");
354 };
355 consume(e);
356 num_freed += 1;
357 }
358 num_freed
359 }
360 fn sparsely_free_chunk(
361 should_free: &mut impl FnMut(&E, PtrID<E, P>, IndexedID<E, P>) -> bool,
362 consume: &mut impl FnMut(E),
363 chunk: &mut Chunk<E, P>,
364 ) -> usize {
365 let mut num_freed = 0usize;
367 let mut next = chunk.allocated.next_allocated(0);
368 while let Some(unit_id) = next {
369 let index = chunk.indexed_id_of_unit(unit_id);
370 let unit = chunk.unit_ptr_mut(unit_id);
371 let ptr = PtrID::from(unit);
372 let elem = unsafe { &*unit }.as_init_ref().unwrap();
373 if should_free(elem, ptr, IndexedID::from(index)) {
374 if let Some(e) = chunk.deallocate_unchecked(unit_id) {
375 consume(e);
376 num_freed += 1;
377 } else {
378 unreachable!("Deallocation failed unexpectedly");
379 }
380 let next_start = unit_id.saturating_add(1);
382 next = chunk.allocated.next_allocated(next_start);
383 } else {
384 let next_start = unit_id.saturating_add(1);
386 next = chunk.allocated.next_allocated(next_start);
387 }
388 }
389 num_freed
390 }
391
392 #[inline]
397 pub fn len(&self) -> usize {
398 self.num_allocated.get()
399 }
400
401 #[inline]
403 pub fn capacity(&self) -> usize {
404 self.chunks.borrow().len() * P::CHUNK_SIZE
405 }
406
407 #[inline]
409 pub fn is_empty(&self) -> bool {
410 self.len() == 0
411 }
412
413 pub fn clear(&mut self) {
415 self.chunks.get_mut().clear();
416 self.free_head.set(0);
417 self.num_allocated.set(0);
418 }
419
420 #[inline]
424 pub fn iter(&self) -> EntityAllocReadIter<'_, E, P> {
425 EntityAllocReadIter::new(self)
426 }
427 #[inline]
431 pub fn iter_mut(&mut self) -> EntityAllocEditIter<'_, E, P> {
432 EntityAllocEditIter::new(self)
433 }
434}
435
436impl<'alloc, E, P: IAllocPolicy> IntoIterator for &'alloc EntityAlloc<E, P> {
437 type Item = (IndexedID<E, P>, PtrID<E, P>, &'alloc E);
438 type IntoIter = EntityAllocReadIter<'alloc, E, P>;
439
440 fn into_iter(self) -> Self::IntoIter {
441 self.iter()
442 }
443}
444impl<'alloc, E, P: IAllocPolicy> IntoIterator for &'alloc mut EntityAlloc<E, P> {
445 type Item = (IndexedID<E, P>, PtrID<E, P>, &'alloc mut E);
446 type IntoIter = EntityAllocEditIter<'alloc, E, P>;
447
448 fn into_iter(self) -> Self::IntoIter {
449 self.iter_mut()
450 }
451}
452impl<E, P: IAllocPolicy> IntoIterator for EntityAlloc<E, P> {
453 type Item = (GenIndex, E);
454 type IntoIter = EntityAllocConsumeIter<E, P>;
455
456 fn into_iter(self) -> Self::IntoIter {
457 EntityAllocConsumeIter::new(self)
458 }
459}