mod field;
mod flags;
mod iter;
mod multi_src_get;
use core::{ffi::CStr, ffi::c_void, ptr::NonNull};
pub use field::{Field, FieldAt, FieldAtMut, FieldIndex, FieldMut, FieldUntyped, FieldUntypedMut};
pub(crate) use field::{flecs_field, flecs_field_w_size};
pub use multi_src_get::*;
pub use flags::TableFlags;
pub use iter::TableIter;
#[cfg(any(debug_assertions, feature = "flecs_force_enable_ecs_asserts"))]
pub(crate) use iter::{table_lock, table_unlock};
use crate::core::*;
use crate::sys;
#[cfg(feature = "std")]
extern crate std;
extern crate alloc;
use alloc::{string::String, vec::Vec};
#[derive(Debug, Clone, Copy, Eq)]
#[repr(C)]
pub struct Table<'a> {
pub(crate) table: NonNull<sys::ecs_table_t>,
world: WorldRef<'a>,
}
impl PartialEq for Table<'_> {
fn eq(&self, other: &Self) -> bool {
self.table == other.table
}
}
impl<'a> Table<'a> {
pub fn new(world: impl WorldProvider<'a>, table: NonNull<sys::ecs_table_t>) -> Self {
Self {
world: world.world(),
table,
}
}
pub fn raw_table_ptr(&self) -> *mut sys::ecs_table_t {
self.table.as_ptr()
}
}
#[derive(Debug, Clone, Copy)]
pub struct TableRange<'a> {
pub table: Table<'a>,
offset: i32,
count: i32,
}
impl<'a> TableRange<'a> {
pub fn new(table: Table<'a>, offset: i32, count: i32) -> Self {
Self {
table,
offset,
count,
}
}
pub(crate) fn new_raw(
world: impl WorldProvider<'a>,
table: NonNull<sys::ecs_table_t>,
offset: i32,
count: i32,
) -> Self {
Self {
table: Table::new(world, table),
offset,
count,
}
}
}
pub trait TableOperations<'a>: IntoTable {
fn table(&self) -> Table<'a>;
fn offset(&self) -> i32;
fn world(&self) -> WorldRef<'a>;
fn count(&self) -> i32 {
let table = self.table_ptr_mut();
unsafe { sys::ecs_table_count(table) }
}
fn size(&self) -> i32 {
let table = self.table_ptr_mut();
unsafe { sys::ecs_table_size(table) }
}
fn entities(&self) -> &[Entity] {
let table = self.table_ptr_mut();
let entities = unsafe { sys::ecs_table_entities(table) };
if entities.is_null() {
return &[];
}
let count = self.count();
unsafe { core::slice::from_raw_parts(entities as *const Entity, count as usize) }
}
fn clear_entities(&self) {
let world = self.world().world_ptr_mut();
let table = self.table_ptr_mut();
unsafe { sys::ecs_table_clear_entities(world, table) };
}
fn to_string(&self) -> Option<String> {
unsafe {
let raw_ptr = sys::ecs_table_str(self.world().world_ptr(), self.table_ptr_mut());
if raw_ptr.is_null() {
return None;
}
let len = CStr::from_ptr(raw_ptr).to_bytes().len();
Some(String::from_utf8_unchecked(Vec::from_raw_parts(
raw_ptr as *mut u8,
len,
len,
)))
}
}
fn archetype(&self) -> Archetype<'a> {
let type_vec = unsafe { sys::ecs_table_get_type(self.table_ptr_mut()) };
let slice = if unsafe { !(*type_vec).array.is_null() && (*type_vec).count != 0 } {
unsafe {
core::slice::from_raw_parts((*type_vec).array as _, (*type_vec).count as usize)
}
} else {
&[]
};
let world = self.world();
unsafe {
Archetype::new_locked(
world,
slice,
TableLock::new(world, NonNull::new_unchecked(self.table_ptr_mut())),
)
}
}
fn find_type_index(&self, id: impl IntoId) -> Option<i32> {
let index = unsafe {
sys::ecs_table_get_type_index(
self.world().world_ptr(),
self.table_ptr_mut(),
*id.into_id(self.world()),
)
};
if index == -1 { None } else { Some(index) }
}
fn find_column_index(&self, id: impl IntoId) -> Option<i32> {
let index = unsafe {
sys::ecs_table_get_column_index(
self.world().world_ptr(),
self.table_ptr_mut(),
*id.into_id(self.world()),
)
};
if index == -1 { None } else { Some(index) }
}
fn has(&self, id: impl IntoId) -> bool {
self.find_type_index(id).is_some()
}
fn column_untyped(&self, index: i32) -> Option<*mut c_void> {
let ptr = unsafe { sys::ecs_table_get_column(self.table_ptr_mut(), index, self.offset()) };
if ptr.is_null() { None } else { Some(ptr) }
}
fn get_mut<T: ComponentId>(&mut self) -> Option<&mut [T]> {
self.get_mut_untyped(T::entity_id(self.world()))
.map(|ptr| unsafe {
core::slice::from_raw_parts_mut(ptr as *mut T, (self.count()) as usize)
})
}
fn get_mut_untyped(&self, id: sys::ecs_id_t) -> Option<*mut c_void> {
if let Some(index) = self.find_column_index(id) {
self.column_untyped(index)
} else {
None
}
}
fn get_pair_ids_mut_untyped(
&self,
first: impl Into<Entity>,
second: impl Into<Entity>,
) -> Option<*mut c_void> {
self.get_mut_untyped(ecs_pair(*first.into(), *second.into()))
}
fn get_pair_mut_untyped<First: ComponentId, Second: ComponentId>(&self) -> Option<*mut c_void> {
let world = self.world();
self.get_pair_ids_mut_untyped(First::entity_id(world), Second::entity_id(world))
}
fn column_size(&self, index: i32) -> usize {
unsafe { sys::ecs_table_get_column_size(self.table_ptr_mut(), index) }
}
fn depth(&self, rel: impl IntoEntity) -> i32 {
let world = self.world();
unsafe {
sys::ecs_table_get_depth(
world.world_ptr_mut(),
self.table_ptr_mut(),
*rel.into_entity(world),
)
}
}
fn records(&self) -> &[sys::ecs_table_record_t] {
let records = unsafe { sys::flecs_table_records(self.table_ptr_mut()) };
unsafe { core::slice::from_raw_parts(records.array, records.count as usize) }
}
fn id(&self) -> u64 {
unsafe { sys::flecs_table_id(self.table_ptr_mut()) }
}
fn lock(&self) {
unsafe { sys::ecs_table_lock(self.world().world_ptr_mut(), self.table_ptr_mut()) };
}
fn unlock(&self) {
unsafe { sys::ecs_table_unlock(self.world().world_ptr_mut(), self.table_ptr_mut()) };
}
fn has_flags(&self, flags: TableFlags) -> bool {
unsafe { sys::ecs_table_has_flags(self.table_ptr_mut(), flags.bits()) }
}
}
impl<'a> TableOperations<'a> for Table<'a> {
fn table(&self) -> Table<'a> {
*self
}
fn offset(&self) -> i32 {
0
}
fn world(&self) -> WorldRef<'a> {
self.world
}
fn count(&self) -> i32 {
unsafe { sys::ecs_table_count(self.table_ptr_mut()) }
}
}
impl<'a> TableOperations<'a> for TableRange<'a> {
fn table(&self) -> Table<'a> {
self.table
}
fn offset(&self) -> i32 {
self.offset
}
fn world(&self) -> WorldRef<'a> {
self.table.world
}
fn count(&self) -> i32 {
self.count
}
}
pub(crate) struct TableLock<'a> {
world: WorldRef<'a>,
table: NonNull<sys::ecs_table_t>,
}
impl<'a> TableLock<'a> {
pub fn new(world: impl WorldProvider<'a>, table: NonNull<sys::ecs_table_t>) -> Self {
unsafe { sys::ecs_table_lock(world.world_ptr_mut(), table.as_ptr()) };
Self {
world: world.world(),
table,
}
}
}
impl Drop for TableLock<'_> {
fn drop(&mut self) {
if std::thread::panicking() {
return;
}
unsafe {
sys::ecs_table_unlock(self.world.world_ptr_mut(), self.table.as_ptr());
}
}
}