use std::fmt;
use std::num::NonZeroU32;
pub type EntityId = u64;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Entity {
pub(crate) id: u32,
pub(crate) generation: u32,
}
impl Entity {
pub const NULL: Entity = Entity {
id: u32::MAX,
generation: 0,
};
#[inline]
pub fn new(id: u32, generation: u32) -> Self {
Self { id, generation }
}
#[inline]
pub fn id(&self) -> u32 {
self.id
}
#[inline]
pub fn generation(&self) -> u32 {
self.generation
}
#[inline]
pub fn to_bits(self) -> u64 {
((self.generation as u64) << 32) | (self.id as u64)
}
#[inline]
pub fn from_bits(bits: u64) -> Self {
Self {
id: bits as u32,
generation: (bits >> 32) as u32,
}
}
#[inline]
pub fn is_null(self) -> bool {
self == Self::NULL
}
pub fn display_string(self) -> String {
if self.is_null() {
"Entity(NULL)".to_owned()
} else {
format!("Entity({}:{})", self.id, self.generation)
}
}
}
impl fmt::Debug for Entity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_null() {
write!(f, "Entity::NULL")
} else {
write!(f, "Entity(id={}, gen={})", self.id, self.generation)
}
}
}
impl fmt::Display for Entity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.display_string())
}
}
impl Default for Entity {
fn default() -> Self {
Self::NULL
}
}
impl From<Entity> for u64 {
fn from(e: Entity) -> u64 {
e.to_bits()
}
}
impl From<u64> for Entity {
fn from(bits: u64) -> Entity {
Entity::from_bits(bits)
}
}
impl PartialOrd for Entity {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Entity {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.to_bits().cmp(&other.to_bits())
}
}
#[derive(Debug, Clone)]
enum SlotState {
Alive { generation: u32 },
Free { generation: u32, next_free: u32 },
}
impl SlotState {
fn generation(&self) -> u32 {
match self {
SlotState::Alive { generation } => *generation,
SlotState::Free { generation, .. } => *generation,
}
}
fn is_alive(&self) -> bool {
matches!(self, SlotState::Alive { .. })
}
}
#[derive(Debug)]
pub struct EntityAllocator {
slots: Vec<SlotState>,
free_head: u32,
alive: usize,
total_allocated: u64,
}
impl EntityAllocator {
pub fn new() -> Self {
Self {
slots: Vec::new(),
free_head: u32::MAX,
alive: 0,
total_allocated: 0,
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
slots: Vec::with_capacity(capacity),
free_head: u32::MAX,
alive: 0,
total_allocated: 0,
}
}
pub fn allocate(&mut self) -> Entity {
self.total_allocated += 1;
self.alive += 1;
if self.free_head != u32::MAX {
let idx = self.free_head;
let slot = &self.slots[idx as usize];
let (generation, next_free) = match slot {
SlotState::Free { generation, next_free } => (*generation, *next_free),
SlotState::Alive { .. } => panic!("EntityAllocator: free list pointed to alive slot"),
};
self.free_head = next_free;
self.slots[idx as usize] = SlotState::Alive { generation };
Entity::new(idx, generation)
} else {
let idx = self.slots.len() as u32;
self.slots.push(SlotState::Alive { generation: 0 });
Entity::new(idx, 0)
}
}
pub fn allocate_many(&mut self, count: usize) -> Vec<Entity> {
(0..count).map(|_| self.allocate()).collect()
}
pub fn free(&mut self, entity: Entity) -> bool {
if entity.is_null() {
return false;
}
let idx = entity.id as usize;
if idx >= self.slots.len() {
return false;
}
match self.slots[idx] {
SlotState::Alive { generation } if generation == entity.generation => {
let new_gen = generation.wrapping_add(1);
self.slots[idx] = SlotState::Free {
generation: new_gen,
next_free: self.free_head,
};
self.free_head = idx as u32;
self.alive -= 1;
true
}
_ => false,
}
}
#[inline]
pub fn is_alive(&self, entity: Entity) -> bool {
if entity.is_null() {
return false;
}
let idx = entity.id as usize;
if idx >= self.slots.len() {
return false;
}
match self.slots[idx] {
SlotState::Alive { generation } => generation == entity.generation,
SlotState::Free { .. } => false,
}
}
#[inline]
pub fn len(&self) -> usize {
self.alive
}
#[inline]
pub fn is_empty(&self) -> bool {
self.alive == 0
}
#[inline]
pub fn capacity(&self) -> usize {
self.slots.len()
}
#[inline]
pub fn total_allocated(&self) -> u64 {
self.total_allocated
}
pub fn clear(&mut self) {
for slot in &mut self.slots {
if let SlotState::Alive { generation } = slot {
*generation = generation.wrapping_add(1);
}
}
self.slots.clear();
self.free_head = u32::MAX;
self.alive = 0;
}
pub fn iter_alive(&self) -> impl Iterator<Item = Entity> + '_ {
self.slots
.iter()
.enumerate()
.filter_map(|(idx, slot)| match slot {
SlotState::Alive { generation } => Some(Entity::new(idx as u32, *generation)),
SlotState::Free { .. } => None,
})
}
pub fn alive_entities(&self) -> Vec<Entity> {
self.iter_alive().collect()
}
pub fn generation_of(&self, id: u32) -> Option<u32> {
self.slots.get(id as usize).map(|s| s.generation())
}
#[cfg(debug_assertions)]
pub fn verify_invariants(&self) {
let mut free_count = 0usize;
let mut cursor = self.free_head;
let mut visited = std::collections::HashSet::new();
while cursor != u32::MAX {
assert!(visited.insert(cursor), "free list cycle detected");
match &self.slots[cursor as usize] {
SlotState::Free { next_free, .. } => {
free_count += 1;
cursor = *next_free;
}
SlotState::Alive { .. } => panic!("free list contained alive slot"),
}
}
let alive_count = self.slots.iter().filter(|s| s.is_alive()).count();
assert_eq!(alive_count, self.alive, "alive count mismatch");
assert_eq!(alive_count + free_count, self.slots.len(), "slot count mismatch");
}
}
impl Default for EntityAllocator {
fn default() -> Self {
Self::new()
}
}
impl Clone for EntityAllocator {
fn clone(&self) -> Self {
Self {
slots: self.slots.clone(),
free_head: self.free_head,
alive: self.alive,
total_allocated: self.total_allocated,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct EntitySet {
entities: Vec<Entity>,
}
impl EntitySet {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(cap: usize) -> Self {
Self { entities: Vec::with_capacity(cap) }
}
pub fn insert(&mut self, entity: Entity) -> bool {
match self.entities.binary_search(&entity) {
Ok(_) => false,
Err(pos) => {
self.entities.insert(pos, entity);
true
}
}
}
pub fn remove(&mut self, entity: Entity) -> bool {
match self.entities.binary_search(&entity) {
Ok(pos) => {
self.entities.remove(pos);
true
}
Err(_) => false,
}
}
pub fn contains(&self, entity: Entity) -> bool {
self.entities.binary_search(&entity).is_ok()
}
pub fn len(&self) -> usize {
self.entities.len()
}
pub fn is_empty(&self) -> bool {
self.entities.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = &Entity> {
self.entities.iter()
}
pub fn clear(&mut self) {
self.entities.clear();
}
pub fn retain_alive(&mut self, alloc: &EntityAllocator) {
self.entities.retain(|e| alloc.is_alive(*e));
}
pub fn intersection<'a>(&'a self, other: &'a EntitySet) -> impl Iterator<Item = &'a Entity> {
self.entities.iter().filter(move |e| other.contains(**e))
}
pub fn union(&self, other: &EntitySet) -> EntitySet {
let mut result = self.clone();
for &e in &other.entities {
result.insert(e);
}
result
}
}
impl FromIterator<Entity> for EntitySet {
fn from_iter<I: IntoIterator<Item = Entity>>(iter: I) -> Self {
let mut set = EntitySet::new();
for e in iter {
set.insert(e);
}
set
}
}
#[derive(Debug, Clone)]
pub struct EntityMap<V> {
inner: std::collections::HashMap<Entity, V>,
}
impl<V> EntityMap<V> {
pub fn new() -> Self {
Self { inner: std::collections::HashMap::new() }
}
pub fn insert(&mut self, entity: Entity, value: V) -> Option<V> {
self.inner.insert(entity, value)
}
pub fn remove(&mut self, entity: Entity) -> Option<V> {
self.inner.remove(&entity)
}
pub fn get(&self, entity: Entity) -> Option<&V> {
self.inner.get(&entity)
}
pub fn get_mut(&mut self, entity: Entity) -> Option<&mut V> {
self.inner.get_mut(&entity)
}
pub fn contains(&self, entity: Entity) -> bool {
self.inner.contains_key(&entity)
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&Entity, &V)> {
self.inner.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Entity, &mut V)> {
self.inner.iter_mut()
}
pub fn clear(&mut self) {
self.inner.clear();
}
pub fn retain_alive(&mut self, alloc: &EntityAllocator) {
self.inner.retain(|e, _| alloc.is_alive(*e));
}
}
impl<V> Default for EntityMap<V> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_entity_packing() {
let e = Entity::new(42, 7);
assert_eq!(e.id(), 42);
assert_eq!(e.generation(), 7);
let bits = e.to_bits();
let e2 = Entity::from_bits(bits);
assert_eq!(e, e2);
}
#[test]
fn test_null_entity() {
assert!(Entity::NULL.is_null());
assert!(!Entity::new(0, 0).is_null());
}
#[test]
fn test_allocate_and_free() {
let mut alloc = EntityAllocator::new();
let e1 = alloc.allocate();
let e2 = alloc.allocate();
assert_ne!(e1, e2);
assert!(alloc.is_alive(e1));
assert!(alloc.is_alive(e2));
assert_eq!(alloc.len(), 2);
assert!(alloc.free(e1));
assert!(!alloc.is_alive(e1));
assert_eq!(alloc.len(), 1);
assert!(!alloc.free(e1));
}
#[test]
fn test_generation_reuse() {
let mut alloc = EntityAllocator::new();
let e1 = alloc.allocate();
alloc.free(e1);
let e2 = alloc.allocate(); assert_eq!(e1.id(), e2.id());
assert_ne!(e1.generation(), e2.generation());
assert!(!alloc.is_alive(e1)); assert!(alloc.is_alive(e2)); }
#[test]
fn test_allocate_many() {
let mut alloc = EntityAllocator::new();
let entities = alloc.allocate_many(100);
assert_eq!(entities.len(), 100);
assert_eq!(alloc.len(), 100);
for e in &entities {
assert!(alloc.is_alive(*e));
}
}
#[test]
fn test_iter_alive() {
let mut alloc = EntityAllocator::new();
let e1 = alloc.allocate();
let e2 = alloc.allocate();
let e3 = alloc.allocate();
alloc.free(e2);
let alive: Vec<_> = alloc.iter_alive().collect();
assert_eq!(alive.len(), 2);
assert!(alive.contains(&e1));
assert!(!alive.contains(&e2));
assert!(alive.contains(&e3));
}
#[test]
fn test_entity_set() {
let mut set = EntitySet::new();
let e1 = Entity::new(1, 0);
let e2 = Entity::new(2, 0);
assert!(set.insert(e1));
assert!(!set.insert(e1)); assert!(set.insert(e2));
assert_eq!(set.len(), 2);
assert!(set.contains(e1));
assert!(set.remove(e1));
assert!(!set.contains(e1));
assert_eq!(set.len(), 1);
}
#[test]
fn test_entity_map() {
let mut map: EntityMap<i32> = EntityMap::new();
let e1 = Entity::new(1, 0);
map.insert(e1, 42);
assert_eq!(map.get(e1), Some(&42));
*map.get_mut(e1).unwrap() = 99;
assert_eq!(map.get(e1), Some(&99));
assert_eq!(map.remove(e1), Some(99));
assert_eq!(map.get(e1), None);
}
#[test]
fn test_clear() {
let mut alloc = EntityAllocator::new();
let entities = alloc.allocate_many(10);
alloc.clear();
assert_eq!(alloc.len(), 0);
for e in entities {
assert!(!alloc.is_alive(e));
}
}
#[test]
#[cfg(debug_assertions)]
fn test_invariants() {
let mut alloc = EntityAllocator::new();
let _e1 = alloc.allocate();
let e2 = alloc.allocate();
let _e3 = alloc.allocate();
alloc.free(e2);
alloc.verify_invariants();
}
#[test]
fn test_entity_ordering() {
let e1 = Entity::new(0, 0);
let e2 = Entity::new(1, 0);
let e3 = Entity::new(0, 1);
assert!(e1 < e2);
assert!(e1 < e3); }
#[test]
fn test_display() {
let e = Entity::new(5, 3);
assert_eq!(format!("{}", e), "Entity(5:3)");
assert_eq!(format!("{}", Entity::NULL), "Entity(NULL)");
}
}