structecs/
entity.rs

1use std::{
2    ptr::NonNull,
3    sync::{
4        Arc,
5        atomic::{AtomicUsize, Ordering},
6    },
7};
8
9use crate::extractor::Extractor;
10
11/// Unique identifier for an entity in the World.
12#[derive(Hash, Eq, PartialEq, Debug, Clone, Copy)]
13pub struct EntityId {
14    pub(crate) id: u32,
15}
16
17impl EntityId {
18    pub(crate) fn new(id: u32) -> Self {
19        Self { id }
20    }
21
22    /// Create an EntityId from a raw u32 value.
23    ///
24    /// # Safety
25    /// The caller must ensure that the provided `id` is valid and unique within the context
26    /// of the World. Using an invalid or duplicate ID may lead to undefined behavior.
27    pub fn from_raw(id: u32) -> Self {
28        Self { id }
29    }
30}
31
32impl std::fmt::Display for EntityId {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        write!(f, "Entity({})", self.id)
35    }
36}
37
38/// Internal reference-counted data for an entity.
39///
40/// Memory layout is optimized to reduce false sharing in concurrent scenarios.
41/// The counter is placed in its own cache line to avoid contention with other fields.
42#[repr(C)]
43pub(crate) struct EntityDataInner {
44    /// Reference counter - placed first and aligned to cache line for optimal concurrent access
45    pub(crate) counter: AtomicUsize,
46
47    /// Pointer to the entity data
48    pub(crate) data: NonNull<u8>,
49
50    /// Extractor for component access
51    pub(crate) extractor: Arc<Extractor>,
52}
53
54pub struct EntityData {
55    inner: NonNull<EntityDataInner>,
56}
57
58// SAFETY: EntityData uses atomic reference counting and all internal data
59// is properly synchronized with Arc. Safe to send across threads.
60unsafe impl Send for EntityData {}
61// SAFETY: EntityData uses Arc for extractor, providing safe concurrent access.
62// Safe to share across threads.
63unsafe impl Sync for EntityData {}
64
65impl EntityData {
66    pub(crate) fn inner(&self) -> &EntityDataInner {
67        // SAFETY: inner is always valid and points to a properly initialized EntityDataInner
68        // that is kept alive by reference counting.
69        unsafe { self.inner.as_ref() }
70    }
71
72    pub(crate) fn new<E: crate::Extractable>(entity: E, extractor: Arc<Extractor>) -> Self {
73        let ptr = Box::into_raw(Box::new(entity)) as *mut u8;
74        let inner = EntityDataInner {
75            counter: AtomicUsize::new(1),
76            // SAFETY: Box::into_raw never returns null
77            data: unsafe { NonNull::new_unchecked(ptr) },
78            extractor,
79        };
80        Self {
81            // SAFETY: Box::into_raw never returns null
82            inner: unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(inner))) },
83        }
84    }
85
86    pub(crate) unsafe fn extract_by_offset<T: 'static>(
87        &self,
88        offset: usize,
89    ) -> crate::Acquirable<T> {
90        let data_ptr = self.inner().data;
91        // SAFETY: The caller guarantees that offset is valid for type T within the entity data.
92        // The offset comes from the Extractor which validates it during creation.
93        let extracted = unsafe { data_ptr.add(offset).cast::<T>() };
94        crate::Acquirable::new(extracted, self.clone())
95    }
96
97    pub(crate) fn data_ptr(&self) -> NonNull<u8> {
98        self.inner().data
99    }
100
101    pub(crate) fn extract<T: 'static>(&self) -> Option<crate::Acquirable<T>> {
102        // SAFETY: extract_ptr validates the type through the Extractor
103        let extracted = unsafe { self.extract_ptr::<T>()? };
104        Some(crate::Acquirable::new(extracted, self.clone()))
105    }
106
107    pub(crate) unsafe fn extract_ptr<T: 'static>(&self) -> Option<NonNull<T>> {
108        // SAFETY: The caller must ensure proper synchronization. The extractor validates
109        // that type T exists in the entity data and returns None if not present.
110        unsafe { self.inner().extractor.extract_ptr::<T>(self.inner().data) }
111    }
112}
113
114impl Drop for EntityData {
115    fn drop(&mut self) {
116        let inner = self.inner();
117        if inner.counter.fetch_sub(1, Ordering::Release) > 1 {
118            return;
119        }
120
121        std::sync::atomic::fence(Ordering::Acquire);
122
123        // Drop the main entity data
124        // SAFETY: The dropper was created for the entity type when it was constructed.
125        // The data pointer is valid and points to properly allocated data of the correct type.
126        unsafe { (inner.extractor.dropper)(inner.data) };
127
128        // SAFETY: We are the last reference (counter reached 0), so we can safely
129        // deallocate the inner data. The inner pointer is valid and was allocated via Box.
130        unsafe {
131            let inner = Box::from_raw(self.inner.as_ptr());
132            drop(inner);
133        }
134    }
135}
136
137impl Clone for EntityData {
138    fn clone(&self) -> Self {
139        self.inner().counter.fetch_add(1, Ordering::Relaxed);
140
141        Self { inner: self.inner }
142    }
143}