use std::alloc;
use std::alloc::Layout;
use std::cmp::Ordering;
use std::convert::TryFrom;
use std::ops::Deref;
use std::ptr::NonNull;
use std::sync::{Arc, Weak};
use std::sync::atomic::{self, AtomicUsize};
use crossbeam_queue::SegQueue;
use crate::{Component, ComponentTypeID, EntityID, Universe};
use crate::chunk::Chunk;
pub trait ComponentSet {
fn as_slice(&self) -> &[ComponentTypeID];
fn into_owned(self) -> ComponentVecSet;
}
pub trait ComponentSetExt: ComponentSet {
fn len(&self) -> usize { self.as_slice().len() }
fn as_ref(&self) -> ComponentSliceSet {
ComponentSliceSet(self.as_slice())
}
fn to_owned(&self) -> ComponentVecSet {
ComponentVecSet(self.as_slice().to_owned())
}
fn includes(&self, component_type: &ComponentTypeID) -> bool {
self.as_slice().binary_search(component_type).is_ok()
}
fn includes_all<T: Deref<Target=ComponentTypeID>>(&self, component_types: impl IntoIterator<Item=T>) -> bool {
component_types.into_iter().all(|ct| self.includes(&*ct))
}
fn eq(&self, other: &impl ComponentSet) -> bool {
self.as_slice().eq(other.as_slice())
}
fn cmp(&self, other: &impl ComponentSet) -> Ordering {
self.as_slice().cmp(other.as_slice())
}
}
impl<T: ComponentSet + ?Sized> ComponentSetExt for T {}
fn ensure_component_set_valid(component_types: &mut Vec<ComponentTypeID>) {
let entity_component_type = EntityID::type_id();
component_types.dedup();
if !component_types.contains(&entity_component_type) {
component_types.push(entity_component_type);
}
component_types.sort();
}
#[derive(Clone, Debug)]
pub struct ComponentVecSet(Vec<ComponentTypeID>);
impl ComponentVecSet {
pub fn new(mut component_types: Vec<ComponentTypeID>) -> ComponentVecSet {
ensure_component_set_valid(&mut component_types);
ComponentVecSet(component_types)
}
pub fn as_slice(&self) -> &[ComponentTypeID] {
&self.0
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn insert(&mut self, component_type: ComponentTypeID) {
match self.0.binary_search(&component_type) {
Ok(_) => {}
Err(idx) => self.0.insert(idx, component_type),
}
}
pub fn remove(&mut self, component_type: ComponentTypeID) {
match self.0.binary_search(&component_type) {
Ok(idx) => { self.0.remove(idx); }
Err(_) => {}
};
}
}
impl ComponentSet for ComponentVecSet {
fn as_slice(&self) -> &[ComponentTypeID] {
&self.0
}
fn into_owned(self) -> ComponentVecSet {
self
}
}
impl From<Vec<ComponentTypeID>> for ComponentVecSet {
fn from(component_types: Vec<ComponentTypeID>) -> Self {
ComponentVecSet::new(component_types)
}
}
#[derive(Clone, Debug)]
pub struct ComponentSliceSet<'a>(&'a [ComponentTypeID]);
impl<'a> ComponentSliceSet<'a> {
pub fn ensure(component_types: &mut Vec<ComponentTypeID>) -> ComponentSliceSet {
ensure_component_set_valid(component_types);
ComponentSliceSet(component_types)
}
pub fn try_from_slice(component_types: &[ComponentTypeID]) -> Option<ComponentSliceSet> {
let mut has_entity_id = false;
let mut last: Option<ComponentTypeID> = None;
for component_type in component_types.iter().cloned() {
if let Some(l) = last {
if l.cmp(&component_type) != Ordering::Less {
return None;
}
}
last = Some(component_type.clone());
if component_type == EntityID::type_id() {
has_entity_id = true;
}
}
if !has_entity_id {
return None;
}
Some(ComponentSliceSet(component_types))
}
}
impl<'a> ComponentSet for ComponentSliceSet<'a> {
fn as_slice(&self) -> &[ComponentTypeID] {
self.0
}
fn into_owned(self) -> ComponentVecSet {
ComponentSetExt::to_owned(&self)
}
}
impl<'a> TryFrom<&'a [ComponentTypeID]> for ComponentSliceSet<'a> {
type Error = ();
fn try_from(component_types: &'a [ComponentTypeID]) -> Result<Self, Self::Error> {
ComponentSliceSet::try_from_slice(component_types).ok_or(())
}
}
pub struct Archetype {
universe: Weak<Universe>,
id: usize,
component_types: ComponentVecSet,
chunk_capacity: usize,
chunk_layout: Layout,
component_offsets: Vec<usize>,
allocated_chunks: AtomicUsize,
free_list: SegQueue<NonNull<u8>>,
}
unsafe impl Send for Archetype {}
unsafe impl Sync for Archetype {}
impl Archetype {
pub(crate) fn new(universe: &Arc<Universe>, component_types: impl ComponentSet, id: usize) -> Archetype {
let chunk_capacity = universe.chunk_size();
let component_types = component_types.into_owned();
let (component_offsets, chunk_layout) = Archetype::calculate_layout(&component_types, chunk_capacity);
let universe = Arc::downgrade(&universe);
Archetype {
universe,
id,
component_types,
chunk_capacity,
chunk_layout,
component_offsets,
allocated_chunks: AtomicUsize::new(0),
free_list: SegQueue::new(),
}
}
pub fn universe(&self) -> &Weak<Universe> { &self.universe }
pub fn id(&self) -> usize { return self.id; }
pub fn component_types(&self) -> &ComponentVecSet {
&self.component_types
}
pub fn chunk_capacity(&self) -> usize {
self.chunk_capacity
}
pub fn chunk_layout(&self) -> Layout {
self.chunk_layout
}
pub fn has_component_type(&self, component_type: &ComponentTypeID) -> bool {
self.component_types.includes(component_type)
}
pub fn has_all_component_types<T: Deref<Target=ComponentTypeID>>(&self, component_types: impl IntoIterator<Item=T>) -> bool {
self.component_types.includes_all(component_types)
}
pub fn allocated_chunks(&self) -> usize {
self.allocated_chunks.load(atomic::Ordering::Relaxed)
}
pub fn component_offsets(&self) -> &[usize] {
&self.component_offsets
}
pub fn component_offset(&self, component_type: ComponentTypeID) -> Option<usize> {
let component_offsets = self.component_offsets();
self.component_types()
.as_slice()
.binary_search_by(|c| c.cmp(&component_type))
.ok()
.map(|idx| component_offsets[idx])
}
pub fn new_chunk(self: Arc<Self>) -> Chunk {
let zone = self.clone();
let ptr = self.allocate_page();
unsafe { Chunk::from_raw(zone, ptr, 0) }
}
pub fn flush(&self) {
while let Some(p) = self.free_list.pop() {
unsafe { alloc::dealloc(p.as_ptr(), self.chunk_layout) };
self.allocated_chunks.fetch_sub(1, atomic::Ordering::Relaxed);
}
}
pub fn allocate_page(&self) -> NonNull<u8> {
if self.chunk_layout.size() == 0 {
return NonNull::dangling();
}
match self.free_list.pop() {
Some(ptr) => ptr,
None => {
self.allocated_chunks.fetch_add(1, atomic::Ordering::Relaxed);
let raw_ptr = unsafe { alloc::alloc(self.chunk_layout) };
NonNull::new(raw_ptr).unwrap()
}
}
}
pub unsafe fn free_page(&self, p: NonNull<u8>) {
if self.chunk_capacity > 0 {
self.free_list.push(p)
}
}
fn calculate_layout(component_types: &ComponentVecSet, capacity: usize) -> (Vec<usize>, Layout) {
let mut offset = 0;
let mut align = 1;
let mut offsets = Vec::with_capacity(component_types.as_slice().len());
for ty in component_types.as_slice().iter().cloned() {
offsets.push(offset);
let layout = ty.layout();
let ty_align = layout.align();
let size = layout.size();
let misalignment = offset % align;
if misalignment != 0 {
offset += align - misalignment;
}
if ty_align > align {
align = ty_align;
}
offset += capacity * size;
}
let layout = Layout::from_size_align(offset, align).unwrap();
(offsets, layout)
}
}
impl Drop for Archetype {
fn drop(&mut self) {
self.flush()
}
}
#[macro_export]
macro_rules! component_set {
() => { $crate::archetype::ComponentVecSet::new(Vec::new()) };
($x:expr, $($y:expr),*) => {
$crate::archetype::ComponentVecSet::new(vec![$x, $($y),*])
};
}