use std::{
cell::RefCell,
collections::HashMap,
fmt::Debug,
hash::BuildHasherDefault,
num::NonZeroU64,
sync::atomic::{AtomicU64, Ordering},
};
use super::{
hash::U64Hasher,
storage::{archetype::ArchetypeIndex, ComponentIndex},
};
#[derive(Debug, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Entity(NonZeroU64);
thread_local! {
pub static ID_CLONE_MAPPINGS: RefCell<HashMap<Entity, Entity, EntityHasher>> = RefCell::new(HashMap::default());
}
impl Clone for Entity {
fn clone(&self) -> Self {
ID_CLONE_MAPPINGS.with(|cell| {
let map = cell.borrow();
*map.get(self).unwrap_or(self)
})
}
}
const BLOCK_SIZE: u64 = 16;
const BLOCK_SIZE_USIZE: usize = BLOCK_SIZE as usize;
static NEXT_ENTITY: AtomicU64 = AtomicU64::new(BLOCK_SIZE);
#[derive(Debug)]
pub struct Allocate {
next: u64,
}
impl Allocate {
pub fn new() -> Self {
Self { next: 0 }
}
}
impl Default for Allocate {
fn default() -> Self {
Self::new()
}
}
impl<'a> Iterator for Allocate {
type Item = Entity;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
if self.next % BLOCK_SIZE == 0 {
self.next = NEXT_ENTITY.fetch_add(BLOCK_SIZE, Ordering::Relaxed);
debug_assert_eq!(self.next % BLOCK_SIZE, 0);
}
let entity = unsafe {
debug_assert_ne!(self.next, 0);
Entity(NonZeroU64::new_unchecked(self.next))
};
self.next += 1;
Some(entity)
}
}
#[derive(Debug, Copy, Clone)]
pub struct EntityLocation(pub(crate) ArchetypeIndex, pub(crate) ComponentIndex);
impl EntityLocation {
pub fn new(archetype: ArchetypeIndex, component: ComponentIndex) -> Self {
EntityLocation(archetype, component)
}
pub fn archetype(&self) -> ArchetypeIndex {
self.0
}
pub fn component(&self) -> ComponentIndex {
self.1
}
}
pub type EntityHasher = BuildHasherDefault<U64Hasher>;
#[derive(Clone, Default)]
pub struct LocationMap {
len: usize,
blocks: HashMap<u64, Box<[Option<EntityLocation>; BLOCK_SIZE_USIZE]>, EntityHasher>,
}
impl Debug for LocationMap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let entries = self.blocks.iter().flat_map(|(base, locs)| {
locs.iter().enumerate().filter_map(move |(i, loc)| {
let entity = unsafe {
let id = *base + i as u64;
debug_assert_ne!(id, 0);
Entity(NonZeroU64::new_unchecked(id))
};
loc.map(|loc| (entity, loc))
})
});
f.debug_map().entries(entries).finish()
}
}
impl LocationMap {
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn contains(&self, entity: Entity) -> bool {
self.get(entity).is_some()
}
pub fn insert(
&mut self,
ids: &[Entity],
arch: ArchetypeIndex,
ComponentIndex(base): ComponentIndex,
) -> Vec<EntityLocation> {
let mut current_block = u64::MAX;
let mut block_vec = None;
let mut removed = Vec::new();
for (i, entity) in ids.iter().enumerate() {
let block = entity.0.get() / BLOCK_SIZE;
if current_block != block {
block_vec = Some(
self.blocks
.entry(block)
.or_insert_with(|| Box::new([None; BLOCK_SIZE_USIZE])),
);
current_block = block;
}
if let Some(ref mut vec) = block_vec {
let idx = (entity.0.get() % BLOCK_SIZE) as usize;
let loc = EntityLocation(arch, ComponentIndex(base + i));
if let Some(previous) = vec[idx].replace(loc) {
removed.push(previous);
}
}
}
self.len += ids.len() - removed.len();
removed
}
pub fn set(&mut self, entity: Entity, location: EntityLocation) {
self.insert(&[entity], location.archetype(), location.component());
}
pub fn get(&self, entity: Entity) -> Option<EntityLocation> {
let block = entity.0.get() / BLOCK_SIZE;
let idx = (entity.0.get() % BLOCK_SIZE) as usize;
if let Some(&result) = self.blocks.get(&block).and_then(|v| v.get(idx)) {
result
} else {
None
}
}
pub fn remove(&mut self, entity: Entity) -> Option<EntityLocation> {
let block = entity.0.get() / BLOCK_SIZE;
let idx = (entity.0.get() % BLOCK_SIZE) as usize;
if let Some(loc) = self.blocks.get_mut(&block).and_then(|v| v.get_mut(idx)) {
let original = loc.take();
if original.is_some() {
self.len -= 1;
}
original
} else {
None
}
}
}