use std::collections::BTreeSet;
use std::error;
use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct Index(u32);
impl Index {
pub const MAX: Index = Index(u32::MAX - 1);
pub const UNINIT: Index = Index(u32::MAX);
#[inline]
pub fn as_usize(self) -> usize {
self.0 as usize
}
#[inline]
pub fn from_usize_truncate(value: usize) -> Index {
#![allow(clippy::cast_possible_truncation)]
debug_assert!(u32::try_from(value).is_ok());
Index(value as u32)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Entity(Index);
impl Entity {
#[inline]
pub(crate) fn as_usize(self) -> usize {
self.0.as_usize()
}
#[allow(dead_code)]
#[inline]
pub(crate) fn from(value: u32) -> Entity {
Entity(Index(value))
}
}
pub struct Allocator {
count: Index,
freed: BTreeSet<Entity>,
}
impl Allocator {
#[must_use]
#[inline]
pub fn new() -> Self {
Self {
count: Index(0),
freed: BTreeSet::new(),
}
}
#[must_use]
#[inline]
pub fn alloc(&mut self) -> Entity {
if let Some(entity) = self.freed.pop_first() {
entity
} else {
assert!(self.count < Index::MAX, "out of entities");
let entity = self.count;
self.count = Index(self.count.0 + 1);
Entity(entity)
}
}
#[inline]
pub fn free(&mut self, entity: Entity) -> Result<(), Error> {
if entity.0 >= self.count {
Err(Error::Unallocated)
} else if self.freed.insert(entity) {
Ok(())
} else {
Err(Error::DoubleFree)
}
}
}
impl Default for Allocator {
#[inline]
fn default() -> Allocator {
Allocator::new()
}
}
#[derive(Debug, PartialEq)]
pub enum Error {
Unallocated,
DoubleFree,
}
impl fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Error::Unallocated => f.write_str("an unallocated entity cannot be freed"),
Error::DoubleFree => f.write_str("an entity cannot be freed twice"),
}
}
}
impl error::Error for Error {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alloc_free_and_reuse() {
let mut entity_allocator = Allocator::new();
let e0 = entity_allocator.alloc();
let e1 = entity_allocator.alloc();
assert_eq!(e0, Entity::from(0));
assert_eq!(e1, Entity::from(1));
entity_allocator.free(e0).unwrap();
let e2 = entity_allocator.alloc();
let e3 = entity_allocator.alloc();
assert_eq!(e2, Entity::from(0));
assert_eq!(e3, Entity::from(2));
entity_allocator.free(e3).unwrap();
let err = entity_allocator.free(e3).unwrap_err();
assert_eq!(err, Error::DoubleFree);
let err = entity_allocator.free(Entity::from(99)).unwrap_err();
assert_eq!(err, Error::Unallocated);
}
}