use std::alloc::{self, Layout};
use std::any::TypeId;
use std::collections::HashMap;
use std::hash::{BuildHasherDefault, Hasher};
use std::ptr::NonNull;
use crate::entity::Entity;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, Default)]
pub struct ComponentId(pub(crate) u32);
impl ComponentId {
#[must_use]
pub fn index(self) -> usize {
self.0 as usize
}
}
pub type DropFn = unsafe extern "C-unwind" fn(*mut u8);
pub(crate) unsafe extern "C-unwind" fn drop_ptr<T>(p: *mut u8) {
unsafe { p.cast::<T>().drop_in_place() }
}
#[derive(Clone, Debug)]
pub struct ComponentInfo {
pub(crate) name: String,
pub(crate) layout: Layout,
pub(crate) drop: Option<DropFn>,
pub(crate) type_id: Option<TypeId>,
}
impl ComponentInfo {
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn layout(&self) -> Layout {
self.layout
}
#[must_use]
pub fn type_id(&self) -> Option<TypeId> {
self.type_id
}
#[must_use]
pub fn needs_drop(&self) -> bool {
self.drop.is_some()
}
}
pub(crate) struct Column {
data: NonNull<u8>,
item: Layout,
drop: Option<DropFn>,
}
impl Column {
fn new(item: Layout, drop: Option<DropFn>) -> Self {
let data = NonNull::new(std::ptr::without_provenance_mut(item.align())).unwrap();
Column { data, item, drop }
}
#[inline(always)]
pub(crate) fn base(&self) -> *mut u8 {
self.data.as_ptr()
}
#[inline(always)]
pub(crate) fn ptr(&self, row: usize) -> *mut u8 {
unsafe { self.data.as_ptr().add(row * self.item.size()) }
}
#[inline]
pub(crate) fn size(&self) -> usize {
self.item.size()
}
pub(crate) fn drop_fn(&self) -> Option<DropFn> {
self.drop
}
fn array(&self, cap: usize) -> Layout {
Layout::from_size_align(
self.item.size().checked_mul(cap).expect("column too large"),
self.item.align(),
)
.expect("column too large")
}
unsafe fn grow(&mut self, old_cap: usize, new_cap: usize) {
if self.item.size() == 0 {
return;
}
let new = self.array(new_cap);
let p = unsafe {
if old_cap == 0 {
alloc::alloc(new)
} else {
alloc::realloc(self.data.as_ptr(), self.array(old_cap), new.size())
}
};
self.data = NonNull::new(p).unwrap_or_else(|| alloc::handle_alloc_error(new));
}
#[inline]
pub(crate) unsafe fn swap_remove_forget(&mut self, row: usize, last: usize) {
if row != last {
unsafe { std::ptr::copy_nonoverlapping(self.ptr(last), self.ptr(row), self.item.size()) }
}
}
unsafe fn permute(&mut self, perm: &[u32], cap: usize) {
let size = self.item.size();
if size == 0 || perm.is_empty() {
return;
}
let layout = self.array(cap);
let new = unsafe { alloc::alloc(layout) };
let new = NonNull::new(new).unwrap_or_else(|| alloc::handle_alloc_error(layout));
for (i, &old) in perm.iter().enumerate() {
unsafe { std::ptr::copy_nonoverlapping(self.ptr(old as usize), new.as_ptr().add(i * size), size) };
}
unsafe { alloc::dealloc(self.data.as_ptr(), layout) };
self.data = new;
}
unsafe fn dealloc(&mut self, cap: usize) {
if self.item.size() != 0 && cap != 0 {
unsafe { alloc::dealloc(self.data.as_ptr(), self.array(cap)) }
}
}
}
pub struct Archetype {
pub(crate) ids: Box<[ComponentId]>,
pub(crate) columns: Box<[Column]>,
pub(crate) entities: Vec<Entity>,
cap: usize,
pub(crate) add_edges: HashMap<ComponentId, usize>,
pub(crate) remove_edges: HashMap<ComponentId, usize>,
}
unsafe impl Send for Archetype {}
unsafe impl Sync for Archetype {}
impl Archetype {
pub(crate) fn new(ids: Box<[ComponentId]>, infos: &[ComponentInfo]) -> Self {
let columns = ids
.iter()
.map(|id| {
let info = &infos[id.index()];
Column::new(info.layout, info.drop)
})
.collect();
Archetype {
ids,
columns,
entities: Vec::new(),
cap: 0,
add_edges: HashMap::new(),
remove_edges: HashMap::new(),
}
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.entities.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.entities.is_empty()
}
pub fn bytes_per_entity(&self) -> usize {
self.columns.iter().map(Column::size).sum::<usize>() + std::mem::size_of::<Entity>()
}
#[must_use]
pub fn allocated_bytes(&self) -> usize {
self.columns.iter().map(|c| c.size() * self.cap).sum::<usize>()
+ self.entities.capacity() * std::mem::size_of::<Entity>()
}
#[must_use]
pub fn component_ids(&self) -> &[ComponentId] {
&self.ids
}
#[must_use]
pub fn entities(&self) -> &[Entity] {
&self.entities
}
#[inline]
#[must_use]
pub fn has(&self, id: ComponentId) -> bool {
self.ids.contains(&id)
}
#[inline]
pub(crate) fn column_index(&self, id: ComponentId) -> Option<usize> {
self.ids.iter().position(|&c| c == id)
}
#[inline]
#[must_use]
pub fn column_ptr(&self, id: ComponentId) -> Option<*mut u8> {
self.column_index(id).map(|i| self.columns[i].base())
}
pub(crate) fn permute(&mut self, perm: &[u32]) {
assert_eq!(perm.len(), self.len());
debug_assert!({
let mut seen = vec![false; perm.len()];
perm.iter().all(|&p| !std::mem::replace(&mut seen[p as usize], true))
});
for c in self.columns.iter_mut() {
unsafe { c.permute(perm, self.cap) };
}
let old = std::mem::take(&mut self.entities);
self.entities = perm.iter().map(|&p| old[p as usize]).collect();
}
pub(crate) fn reserve(&mut self, additional: usize) {
let needed = self.len().checked_add(additional).expect("too many entities");
if needed <= self.cap {
return;
}
let new_cap = needed.max(self.cap * 2).max(8);
for c in self.columns.iter_mut() {
unsafe { c.grow(self.cap, new_cap) }
}
self.cap = new_cap;
self.entities.reserve(new_cap - self.entities.len());
}
pub(crate) unsafe fn swap_remove_drop(&mut self, row: usize) -> Option<Entity> {
let last = self.len() - 1;
for c in self.columns.iter_mut() {
if let Some(d) = c.drop {
unsafe { d(c.ptr(row)) }
}
unsafe { c.swap_remove_forget(row, last) }
}
self.entities.swap_remove(row);
(row != last).then(|| self.entities[row])
}
}
impl Drop for Archetype {
fn drop(&mut self) {
let len = self.len();
for c in self.columns.iter_mut() {
if let Some(d) = c.drop {
for row in 0..len {
unsafe { d(c.ptr(row)) }
}
}
unsafe { c.dealloc(self.cap) }
}
}
}
#[derive(Default)]
pub(crate) struct TypeIdHasher(u64);
impl Hasher for TypeIdHasher {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
for &b in bytes {
self.0 = (self.0.rotate_left(5) ^ b as u64).wrapping_mul(0x517c_c1b7_2722_0a95);
}
}
fn write_u64(&mut self, n: u64) {
self.0 ^= n;
}
}
pub(crate) type TypeMap<V> = HashMap<TypeId, V, BuildHasherDefault<TypeIdHasher>>;