use std::{
alloc::{alloc, dealloc, Layout},
any::TypeId,
collections::HashMap,
hash::{BuildHasher, BuildHasherDefault, Hasher},
ptr::NonNull,
};
use legion::{
query::{FilterResult, LayoutFilter},
storage::{
ArchetypeSource, ArchetypeWriter, ComponentSource, ComponentTypeId, EntityLayout,
UnknownComponentStorage,
},
Entity,
};
use super::*;
pub struct BuiltEntity {
inner: Common<fn() -> Box<dyn UnknownComponentStorage>>,
}
#[derive(Default)]
pub struct EntityBuilder {
inner: Common<fn() -> Box<dyn UnknownComponentStorage>>,
}
impl EntityBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn build(self) -> BuiltEntity {
BuiltEntity { inner: self.inner }
}
}
impl super::super::EntityBuilder for EntityBuilder {
fn add<T: Component>(&mut self, mut component: T) -> &mut Self {
unsafe {
self.inner.add(
(&mut component as *mut T).cast(),
TypeInfo::of::<T>(),
|| Box::new(T::Storage::default()), );
}
core::mem::forget(component);
self
}
}
impl IntoComponentSource for BuiltEntity
{
type Source = BuiltEntity;
fn into(self) -> Self::Source {
self
}
}
impl IntoComponentSource for EntityBuilder
{
type Source = BuiltEntity;
fn into(self) -> Self::Source {
self.build()
}
}
pub struct ComponentSourceFilter(Vec<ComponentTypeId>);
impl LayoutFilter for ComponentSourceFilter {
fn matches_layout(&self, components: &[ComponentTypeId]) -> FilterResult {
FilterResult::Match(
components.len() == self.0.len() && components.iter().all(|x| self.0.contains(x)),
)
}
}
impl ArchetypeSource for BuiltEntity {
type Filter = ComponentSourceFilter;
fn filter(&self) -> Self::Filter {
let v = self.inner.inner.info.iter().map(|x| x.0.id()).collect();
ComponentSourceFilter(v)
}
fn layout(&mut self) -> EntityLayout {
let mut layout = EntityLayout::default();
for (tid, _offset, meta) in &self.inner.inner.info {
unsafe {
layout.register_component_raw(tid.id(), meta.clone());
}
}
layout
}
}
impl ComponentSource for BuiltEntity {
fn push_components<'a>(
&mut self,
writer: &mut ArchetypeWriter<'a>,
mut entities: impl Iterator<Item = Entity>,
) {
let entity = entities.next().unwrap();
writer.push(entity);
for (ty, offset, _) in &mut self.inner.inner.info {
let mut target = writer.claim_components_unknown(ty.id());
let ptr = unsafe { self.inner.inner.storage.as_ptr().add(*offset) };
let len = 1; unsafe { target.extend_memcopy_raw(ptr, len) };
}
}
}
#[derive(Default)]
pub(crate) struct TypeIdHasher {
hash: u64,
}
impl Hasher for TypeIdHasher {
fn write_u64(&mut self, n: u64) {
debug_assert_eq!(self.hash, 0);
self.hash = n;
}
fn write_u128(&mut self, n: u128) {
debug_assert_eq!(self.hash, 0);
self.hash = n as u64;
}
fn write(&mut self, bytes: &[u8]) {
debug_assert_eq!(self.hash, 0);
let mut hasher = <DefaultHashBuilder as BuildHasher>::Hasher::default();
hasher.write(bytes);
self.hash = hasher.finish();
}
fn finish(&self) -> u64 {
self.hash
}
}
pub(crate) type TypeIdMap<V> = HashMap<TypeId, V, BuildHasherDefault<TypeIdHasher>>;
#[derive(Debug, Copy, Clone)]
pub struct TypeInfo {
id: ComponentTypeId,
layout: Layout,
drop: unsafe fn(*mut u8),
}
impl TypeInfo {
pub fn of<T: 'static + Send + Sync>() -> Self {
unsafe fn drop_ptr<T>(x: *mut u8) {
x.cast::<T>().drop_in_place()
}
Self {
id: ComponentTypeId::of::<T>(),
layout: Layout::new::<T>(),
drop: drop_ptr::<T>,
}
}
pub fn id(&self) -> ComponentTypeId {
self.id
}
pub fn layout(&self) -> Layout {
self.layout
}
pub unsafe fn drop(&self, data: *mut u8) {
(self.drop)(data)
}
pub fn drop_shim(&self) -> unsafe fn(*mut u8) {
self.drop
}
}
pub struct Common<M> {
inner: CommonInner<M>,
indices: TypeIdMap<usize>,
}
pub struct CommonInner<M> {
storage: NonNull<u8>,
layout: Layout,
cursor: usize,
info: Vec<(TypeInfo, usize, M)>,
ids: Vec<TypeId>,
}
#[allow(unused)] impl<M> Common<M> {
fn has<T: Component>(&self) -> bool {
self.indices.contains_key(&TypeId::of::<T>())
}
fn get_by_tid<'a, T>(&'a self, tid: &TypeId) -> Option<T> {
let index = self.indices.get(tid)?;
let (_, offset, _) = self.inner.info[*index];
unsafe {
let storage = self.inner.storage.as_ptr().add(offset).cast::<T>();
Some(todo!())
}
}
fn component_types(&self) -> impl Iterator<Item = ComponentTypeId> + '_ {
self.inner.info.iter().map(|(info, _, _)| info.id())
}
fn clear(&mut self) {
self.inner.ids.clear();
self.indices.clear();
self.inner.cursor = 0;
}
unsafe fn add(&mut self, ptr: *mut u8, ty: TypeInfo, meta: M) {
use std::collections::hash_map::Entry;
match self.indices.entry(ty.id().type_id()) {
Entry::Occupied(occupied) => {
let index = *occupied.get();
let (ty, offset, _) = self.inner.info[index];
let storage = self.inner.storage.as_ptr().add(offset);
ty.drop(storage);
std::ptr::copy_nonoverlapping(ptr, storage, ty.layout().size());
}
Entry::Vacant(vacant) => {
self.inner.fun_name(ty, ptr, vacant, meta);
}
}
}
}
impl<M> CommonInner<M> {
unsafe fn grow(
min_size: usize,
cursor: usize,
align: usize,
storage: NonNull<u8>,
) -> (NonNull<u8>, Layout) {
let layout = Layout::from_size_align(min_size.next_power_of_two().max(64), align).unwrap();
let new_storage = NonNull::new_unchecked(alloc(layout));
std::ptr::copy_nonoverlapping(storage.as_ptr(), new_storage.as_ptr(), cursor);
(new_storage, layout)
}
unsafe fn fun_name(
&mut self,
ty: TypeInfo,
ptr: *mut u8,
vacant: std::collections::hash_map::VacantEntry<'_, TypeId, usize>,
meta: M,
) {
let offset = align(self.cursor, ty.layout().align());
let end = offset + ty.layout().size();
if end > self.layout.size() || ty.layout().align() > self.layout.align() {
let new_align = self.layout.align().max(ty.layout().align());
let (new_storage, new_layout) = Self::grow(end, self.cursor, new_align, self.storage);
if self.layout.size() != 0 {
dealloc(self.storage.as_ptr(), self.layout);
}
self.storage = new_storage;
self.layout = new_layout;
}
if ty.id().type_id() == TypeId::of::<(Vec<usize>,)>() {
let aaa = ptr as *mut (Vec<usize>,);
dbg!(aaa.as_ref());
}
if ty.id().type_id() == TypeId::of::<(Box<[u32]>,)>() {
let aaa = ptr as *mut (Box<[u32]>,);
dbg!(aaa.as_ref());
}
let addr = self.storage.as_ptr().add(offset);
std::ptr::copy_nonoverlapping(ptr, addr, ty.layout().size());
vacant.insert(self.info.len());
self.info.push((ty, offset, meta));
self.cursor = end;
if ty.id().type_id() == TypeId::of::<(Box<[u32]>,)>() {
let aaa = ptr as *mut (Box<[u32]>,);
dbg!(aaa.as_ref());
}
}
}
fn align(x: usize, alignment: usize) -> usize {
debug_assert!(alignment.is_power_of_two());
(x + alignment - 1) & (!alignment + 1)
}
unsafe impl<M> Send for Common<M> {}
unsafe impl<M> Sync for Common<M> {}
impl<M> Drop for Common<M> {
fn drop(&mut self) {
self.clear();
if self.inner.layout.size() != 0 {
unsafe {
dealloc(self.inner.storage.as_ptr(), self.inner.layout);
}
}
}
}
impl<M> Default for Common<M> {
fn default() -> Self {
Self {
inner: CommonInner {
storage: NonNull::dangling(),
layout: Layout::from_size_align(0, 8).unwrap(),
cursor: 0,
info: Vec::new(),
ids: Vec::new(),
},
indices: Default::default(),
}
}
}
#[test]
fn example() {
use crate::store::nodes::EntityBuilder as _;
let mut world = legion::World::new(Default::default());
let mut components = EntityBuilder::new();
components.add(42i32);
components.add(true);
components.add(vec![0, 1, 2, 3]);
components.add("hello");
components.add(0u64);
let components = components.build();
let entity = world.extend(components)[0];
assert_eq!(Ok(&42), world.entry(entity).unwrap().get_component::<i32>());
assert_eq!(
Ok(&vec![0, 1, 2, 3]),
world.entry(entity).unwrap().get_component::<Vec<i32>>()
);
}
#[test]
fn simple() {
use crate::store::nodes::EntityBuilder as _;
let mut world = legion::World::new(Default::default());
let mut components = EntityBuilder::new();
let mut comp0: (Box<[u32]>,) = (vec![0, 0, 0, 0, 0, 1, 4100177920].into_boxed_slice(),); let mut comp0_saved = comp0.clone();
let comp0_ptr = (&mut comp0) as *mut (Box<[u32]>,);
components.add(comp0);
unsafe { (*comp0_ptr).0[4] = 42 };
comp0_saved.0[4] = 42;
let comp1: i32 = 0;
components.add(comp1);
let comp2: bool = true;
components.add(comp2);
let mut comp3: Vec<u64> = vec![0, 1, 2, 3];
let comp3_saved = comp3.clone();
let comp3_ptr = (&mut comp3) as *mut Vec<u64>;
components.add(comp3);
let comp4: String = "ewgwgwsegwesf".into();
components.add(comp4.clone());
let comp5: u64 = 0;
components.add(comp5);
let components = components.build();
dbg!(unsafe { comp0_ptr.as_ref() });
let entity = world.extend(components)[0];
assert_eq!(
Some(&comp0_saved),
unsafe { comp0_ptr.as_ref() },
"slice should not have changed"
);
assert_eq!(
Some(&comp3_saved),
unsafe { comp3_ptr.as_ref() },
"vec should not have changed"
);
if let Some(entry) = world.entry(entity) {
unsafe { (*comp0_ptr).0[5] += 1 };
comp0_saved.0[5] += 1;
dbg!(unsafe { comp0_ptr.as_ref() });
assert_eq!(Ok(&comp0_saved), entry.get_component::<(Box<[u32]>,)>());
assert_eq!(Ok(&comp1), entry.get_component::<i32>());
assert_eq!(Ok(&comp2), entry.get_component::<bool>());
assert_eq!(Ok(&comp3_saved), entry.get_component::<Vec<u64>>());
assert_eq!(Ok(&comp4), entry.get_component::<String>());
}
}