use std::collections::BTreeSet;
use std::error;
use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Entity(pub u32);
impl Entity {
#[inline]
pub fn index(&self) -> usize {
self.0 as usize
}
}
impl From<u32> for Entity {
#[inline]
fn from(value: u32) -> Self {
Entity(value)
}
}
pub struct Allocator {
count: u32,
freed: BTreeSet<Entity>,
}
impl Allocator {
#[inline]
pub fn new() -> Self {
Self {
count: 0,
freed: BTreeSet::new(),
}
}
#[inline]
pub fn alloc(&mut self) -> Entity {
if let Some(entity) = self.freed.pop_first() {
entity
} else {
assert!(self.count < u32::MAX, "out of entities");
let entity = self.count;
self.count += 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)
}
}
}
#[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(0));
assert_eq!(e1, Entity(1));
entity_allocator.free(e0).unwrap();
let e2 = entity_allocator.alloc();
let e3 = entity_allocator.alloc();
assert_eq!(e2, Entity(0));
assert_eq!(e3, Entity(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(99)).unwrap_err();
assert_eq!(err, Error::Unallocated);
}
}