use core::fmt::*;
use std::mem::replace;
use fixedbitset::FixedBitSet;
use pi_append_vec::AppendVec;
use pi_null::Null;
use crate::archetype::ComponentInfo;
use crate::archetype::{ColumnIndex, Row};
use crate::column::Column;
use crate::world::{Entity, World};
pub struct Table {
entities: AppendVec<Entity>, pub(crate) columns: Vec<Column>, removes: AppendVec<Row>, }
impl Table {
pub fn new(infos: Vec<ComponentInfo>) -> Self {
Self {
entities: AppendVec::default(),
columns: infos.into_iter().map(|info| Column::new(info)).collect(),
removes: AppendVec::default(),
}
}
#[inline(always)]
pub fn len(&self) -> Row {
self.entities.len() as Row
}
#[inline(always)]
pub fn get(&self, row: Row) -> Entity {
*self.entities.load_alloc(row as usize)
}
#[inline(always)]
pub fn set(&self, row: Row, e: Entity) {
let a = self.entities.load_alloc(row as usize);
*a = e;
}
#[inline(always)]
pub(crate) fn get_column_unchecked(&self, index: ColumnIndex) -> &Column {
unsafe { self.columns.get_unchecked(index as usize) }
}
pub fn reserve(&mut self, additional: usize) {
let len = self.entities.len();
self.entities.reserve(additional);
for c in self.columns.iter_mut() {
c.reserve(len, additional);
}
}
#[inline(always)]
pub fn alloc(&self) -> Row {
self.entities.alloc_index(1) as Row
}
#[inline(always)]
pub(crate) fn mark_remove(&self, row: Row) -> Entity {
let e = self.entities.load_alloc(row as usize);
if e.is_null() {
return *e;
}
self.removes.insert(row);
replace(e, Entity::null())
}
#[inline(always)]
pub(crate) fn drop_row(&self, row: Row) {
for t in self.columns.iter() {
t.drop_row(row);
}
}
pub(crate) fn removes_action(
removes: &AppendVec<Row>,
remove_len: usize,
entity_len: usize,
action: &mut Vec<(Row, Row)>,
set: &mut FixedBitSet,
) -> usize {
action.clear();
if remove_len >= entity_len {
return 0;
}
if remove_len == 1 {
let remove_row = unsafe { removes.get_unchecked(0) };
if (*remove_row) as usize + 1 < entity_len {
action.push((entity_len as u32 - 1, *remove_row));
}
return entity_len - 1;
}
let r = remove_len as f64;
if r * r.log2() < (entity_len - remove_len) as f64 {
for row in removes.iter() {
action.push((*row, *row));
}
action.sort();
let mut start = 0;
let mut end = action.len();
let mut index = entity_len;
while start < end {
index -= 1;
let remove_row = unsafe { action.get_unchecked(end - 1) };
if remove_row.0 as usize == index {
end -= 1;
continue;
}
let r = unsafe { action.get_unchecked_mut(start) };
r.0 = index as u32;
start += 1;
}
action.truncate(end);
return index;
}
set.clear();
set.grow(entity_len);
for row in removes.iter() {
set.set(*row as usize, true);
}
let ones = set.ones();
let mut end = entity_len;
for row in ones {
loop {
end -= 1;
if row >= end {
return end + 1;
}
if !set.contains(end) {
action.push((end as u32, row as u32));
break;
}
}
}
end
}
pub(crate) fn collect(
&mut self,
world: &World,
action: &mut Vec<(Row, Row)>,
set: &mut FixedBitSet,
) -> bool {
let mut r = true;
for c in self.columns.iter_mut() {
r &= c.collect_dirty();
}
if !r {
return false;
}
let remove_len = self.removes.len();
if remove_len == 0 {
return true;
}
let new_entity_len =
Self::removes_action(&self.removes, remove_len, self.entities.len(), action, set);
self.removes.clear();
if self.removes.vec_capacity() < remove_len {
unsafe {
self.removes
.vec_reserve(remove_len - self.removes.vec_capacity())
};
}
for c in self.columns.iter_mut() {
c.collect(new_entity_len, &action);
}
for (src, dst) in action.iter() {
let e = unsafe {
replace(
self.entities.get_unchecked_mut(*src as usize),
Entity::null(),
)
};
*unsafe { self.entities.get_unchecked_mut(*dst as usize) } = e;
world.replace_row(e, *dst);
}
unsafe {
self.entities.set_len(new_entity_len);
};
self.entities.collect();
true
}
}
impl Drop for Table {
fn drop(&mut self) {
let len = self.len() as usize;
if len == 0 {
return;
}
for c in self.columns.iter_mut() {
if !c.needs_drop() {
continue;
}
for (row, e) in self.entities.iter().enumerate() {
if !e.is_null() {
c.drop_row_unchecked(row as Row);
}
}
}
}
}
impl Debug for Table {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("Table")
.field("entitys", &self.entities)
.field("columns", &self.columns)
.field("removes", &self.removes)
.finish()
}
}