my_ecs/ecs/
resource.rs

1use crate::{
2    ds::{
3        ATypeId, BorrowError, BorrowResult, ManagedConstPtr, ManagedMutPtr, NonNullExt, OptVec,
4        SimpleHolder,
5    },
6    ecs::EcsError,
7    util::{macros::debug_format, Or, With},
8};
9use std::{
10    any::Any,
11    collections::HashMap,
12    fmt,
13    hash::{BuildHasher, Hash},
14    ptr::NonNull,
15};
16
17pub mod prelude {
18    pub use super::{Resource, ResourceDesc, ResourceId, ResourceIndex};
19}
20
21/// Unique data in the entire ecs instance.
22#[allow(private_interfaces)]
23pub trait Resource: Send + 'static {
24    #[doc(hidden)]
25    fn key() -> ResourceKey {
26        ResourceKey::of::<Self>()
27    }
28}
29
30/// There are two types of resources.
31/// First one is static resource which is defined internally.
32/// The other one is user resource which is defined by users.
33/// This struct has pointers to those resources and doesn't update it once it's set.
34/// Because, resource is a kind of unique data storage, so it makes sense.
35#[derive(Debug)]
36pub(super) struct ResourceStorage<S> {
37    /// Owned resources.
38    owned: HashMap<ResourceKey, Box<dyn Any>, S>,
39
40    /// Raw pointers to resources.
41    /// Pointers to owned resources are guaranteed to be valid by the struct.
42    /// Other pointers must be kept to be valid by client code.
43    /// They must be well aligned, not aliased, and alive.
44    ptrs: OptVec<SimpleHolder<NonNullExt<u8>>, S>,
45
46    /// [`ResourceKey`] -> index in `Self::ptrs`.
47    imap: HashMap<ResourceKey, ResourceIndex, S>,
48
49    /// Dedicated resources, which are not allowed to be sent to other workers.
50    /// So they must be handled by main worker.
51    /// For example, in web environment, we must send JS objects through postMessage().
52    /// That means objects that are not posted can't be accessed from other workers.
53    /// Plus, ecs objects will be dedicated resource in most cases.
54    is_dedi: Vec<bool>,
55
56    /// Generation of each resource. The generation is when the resource is
57    /// registered to this storage.
58    res_gens: Vec<u64>,
59
60    /// Generation that will be assigned to the next registered resource.
61    generation: u64,
62}
63
64impl<S> ResourceStorage<S>
65where
66    S: Default,
67{
68    pub(super) fn new() -> Self {
69        Self {
70            owned: HashMap::default(),
71            ptrs: OptVec::new(),
72            imap: HashMap::default(),
73            is_dedi: Vec::new(),
74            res_gens: Vec::new(),
75            generation: 1,
76        }
77    }
78}
79
80impl<S> ResourceStorage<S>
81where
82    S: BuildHasher + Default,
83{
84    /// Adds a resource.
85    ///
86    /// If it succeeded, returns resource index for the resource. Otherwise,
87    /// nothing takes place and returns error with the descriptor.
88    pub(super) fn add(
89        &mut self,
90        desc: ResourceDesc,
91    ) -> Result<ResourceIndex, EcsError<ResourceDesc>> {
92        if self.imap.contains_key(&desc.key) {
93            let reason = debug_format!("detected duplicated resource `{:?}`", desc.key);
94            return Err(EcsError::DupResource(reason, desc));
95        }
96
97        let ResourceDesc {
98            dedicated,
99            key,
100            data,
101        } = desc;
102
103        let ptr = match data {
104            Or::A(mut owned) => {
105                // Safety: Infallible.
106                let ptr = unsafe { NonNull::new_unchecked(&mut *owned as *mut dyn Any as *mut u8) };
107                let must_none = self.owned.insert(key, owned);
108                debug_assert!(must_none.is_none());
109                ptr
110            }
111            Or::B(ptr) => ptr,
112        };
113
114        // Attaches ResourceKey's type info to the pointer for the sake of
115        // debugging.
116        let ptr = NonNullExt::from_nonnull(ptr).with_type(*key.get_inner());
117
118        // Adds the pointer.
119        let holder = SimpleHolder::new(ptr);
120        let index = self.ptrs.add(holder);
121        let ri = ResourceIndex::new(index, self.generation);
122        self.generation += 1;
123        while self.res_gens.len() <= index {
124            self.res_gens.push(0);
125        }
126        self.res_gens[index] = ri.generation();
127
128        // Adds the index to the pointer list.
129        self.imap.insert(key, ri);
130
131        // Adds dedicated mapping.
132        if self.is_dedi.len() < index + 1 {
133            self.is_dedi.resize(index + 1, false);
134        }
135        self.is_dedi[index] = dedicated;
136
137        Ok(ri)
138    }
139
140    pub(super) fn remove(&mut self, rkey: &ResourceKey) -> Option<Or<Box<dyn Any>, NonNull<u8>>> {
141        // Removes the resource from `self.owned`, `self.ptrs`, and `self.imap`.
142        // But we don't have to remove `self.is_dedi`.
143        if let Some(ri) = self.imap.remove(rkey) {
144            let data = self.owned.remove(rkey);
145            let ptr = self.ptrs.take(ri.index());
146
147            // Safety: Pointer must exist.
148            debug_assert!(ptr.is_some());
149            let holder = unsafe { ptr.unwrap_unchecked() };
150            let ptr = *holder.into_value();
151
152            Some(if let Some(data) = data {
153                Or::A(data)
154            } else {
155                Or::B(ptr)
156            })
157        } else {
158            None
159        }
160    }
161
162    pub(super) fn contains<Q>(&self, key: &Q) -> bool
163    where
164        ResourceKey: std::borrow::Borrow<Q>,
165        Q: Hash + Eq + ?Sized,
166    {
167        self.imap.contains_key(key)
168    }
169
170    pub(super) fn index<Q>(&self, key: &Q) -> Option<ResourceIndex>
171    where
172        ResourceKey: std::borrow::Borrow<Q>,
173        Q: Hash + Eq + ?Sized,
174    {
175        self.imap.get(key).cloned()
176    }
177
178    // For consistency
179    #[allow(dead_code)]
180    pub(super) fn is_dedicated(&self, ri: ResourceIndex) -> Option<bool> {
181        if self.is_valid_index(&ri) {
182            Some(self.is_dedi[ri.index()])
183        } else {
184            None
185        }
186    }
187
188    pub(super) fn is_dedicated2<Q>(&self, key: &Q) -> Option<bool>
189    where
190        ResourceKey: std::borrow::Borrow<Q>,
191        Q: Hash + Eq + ?Sized,
192    {
193        self.imap.get(key).map(|ri| self.is_dedi[ri.index()])
194    }
195
196    pub(super) fn borrow(&self, ri: ResourceIndex) -> BorrowResult<ManagedConstPtr<u8>> {
197        if self.is_valid_index(&ri) {
198            if let Some(holder) = self.ptrs.get(ri.index()) {
199                return holder
200                    .borrow()
201                    .map(|borrowed| borrowed.map(|ptr| unsafe { ManagedConstPtr::new(ptr) }));
202            }
203        }
204        Err(BorrowError::OutOfBound)
205    }
206
207    pub(super) fn borrow2<Q>(&self, key: &Q) -> BorrowResult<ManagedConstPtr<u8>>
208    where
209        ResourceKey: std::borrow::Borrow<Q>,
210        Q: Hash + Eq + ?Sized,
211    {
212        if let Some(index) = self.index(key) {
213            self.borrow(index)
214        } else {
215            Err(BorrowError::NotFound)
216        }
217    }
218
219    pub(super) fn borrow_mut(&mut self, ri: ResourceIndex) -> BorrowResult<ManagedMutPtr<u8>> {
220        if self.is_valid_index(&ri) {
221            if let Some(holder) = self.ptrs.get_mut(ri.index()) {
222                return holder
223                    .borrow_mut()
224                    .map(|borrowed| borrowed.map(|ptr| unsafe { ManagedMutPtr::new(ptr) }));
225            }
226        }
227        Err(BorrowError::OutOfBound)
228    }
229
230    pub(super) fn borrow_mut2<Q>(&mut self, key: &Q) -> BorrowResult<ManagedMutPtr<u8>>
231    where
232        ResourceKey: std::borrow::Borrow<Q>,
233        Q: Hash + Eq + ?Sized,
234    {
235        if let Some(index) = self.index(key) {
236            self.borrow_mut(index)
237        } else {
238            Err(BorrowError::NotFound)
239        }
240    }
241
242    /// # Safety
243    ///
244    /// Undefined behavior if exclusive borrow happened before.
245    //
246    // Allows dead_code for test.
247    #[cfg(test)]
248    pub(super) unsafe fn get_ptr(&self, ri: ResourceIndex) -> Option<NonNullExt<u8>> {
249        if self.is_valid_index(&ri) {
250            self.ptrs
251                .get(ri.index())
252                .map(|holder| *holder.get_unchecked())
253        } else {
254            None
255        }
256    }
257
258    fn is_valid_index(&self, ri: &ResourceIndex) -> bool {
259        if let Some(generation) = self.res_gens.get(ri.index()).cloned() {
260            generation == ri.generation()
261        } else {
262            false
263        }
264    }
265}
266
267impl<S> Default for ResourceStorage<S>
268where
269    S: Default,
270{
271    fn default() -> Self {
272        Self::new()
273    }
274}
275
276/// A descriptor for registration of a resource.
277///
278/// Normally, resource is owned by an ECS instance, but type-erased raw pointer
279/// can also be considered as a resource. In that case, clients must guarantee
280/// safety about the pointer. [`ResourceDesc::with_owned`] and
281/// [`ResourceDesc::with_ptr`] are methods about the ownership.
282#[derive(Debug)]
283pub struct ResourceDesc {
284    pub dedicated: bool,
285    pub(crate) key: ResourceKey,
286    pub data: Or<Box<dyn Any>, NonNull<u8>>,
287}
288
289impl ResourceDesc {
290    /// Creates a new empty [`ResourceDesc`].
291    ///
292    /// # Examples
293    ///
294    /// ```
295    /// use my_ecs::prelude::ResourceDesc;
296    ///
297    /// let desc = ResourceDesc::new();
298    /// ```
299    pub fn new() -> Self {
300        struct Dummy;
301        impl Resource for Dummy {}
302
303        Self {
304            dedicated: false,
305            key: Dummy::key(),
306            data: Or::B(NonNull::dangling()),
307        }
308    }
309
310    /// Sets whether the resource is dedicated to the descriptor then returns
311    /// the result.
312    ///
313    /// Dedicated resource is only accessable from main worker for now.
314    ///
315    /// # Examples
316    ///
317    /// ```
318    /// use my_ecs::prelude::ResourceDesc;
319    ///
320    /// let desc = ResourceDesc::new().with_dedicated(true);
321    /// ```
322    pub fn with_dedicated(mut self, is_dedicated: bool) -> Self {
323        self.dedicated = is_dedicated;
324        self
325    }
326
327    /// Sets the given owned resource to the descriptor then returns the result.
328    ///
329    /// # Examples
330    ///
331    /// ```
332    /// use my_ecs::prelude::*;
333    ///
334    /// #[derive(Resource)] struct R(i32);
335    ///
336    /// let desc = ResourceDesc::new().with_owned(R(0));
337    /// ```
338    pub fn with_owned<R: Resource>(mut self, data: R) -> Self {
339        self.key = R::key();
340        self.data = Or::A(Box::new(data));
341        self
342    }
343
344    /// Sets the given pointer as a resource to the descriptor then returns the
345    /// result.
346    ///
347    /// # Safety
348    ///
349    /// After registration the descriptor to an ECS instance, owner of the data
350    /// must not access the data while the ECS instance is running because the
351    /// ECS instance may read or write something on the data.
352    ///
353    /// # Examples
354    ///
355    /// ```
356    /// use my_ecs::prelude::*;
357    ///
358    /// #[derive(Resource)] struct R(i32);
359    ///
360    /// let mut r = R(0);
361    /// let desc = unsafe { ResourceDesc::new().with_ptr(&mut r as *mut R) };
362    /// ```
363    pub unsafe fn with_ptr<R: Resource>(mut self, data: *mut R) -> Self {
364        self.key = R::key();
365        self.data = Or::B(NonNull::new(data as *mut u8).unwrap());
366        self
367    }
368}
369
370impl Default for ResourceDesc {
371    fn default() -> Self {
372        Self::new()
373    }
374}
375
376impl<R: Resource> From<R> for ResourceDesc {
377    fn from(value: R) -> Self {
378        ResourceDesc::new().with_owned(value)
379    }
380}
381
382/// Unique identifier for a type implementing [`Resource`].
383pub(crate) type ResourceKey = ATypeId<ResourceKey_>;
384pub(crate) struct ResourceKey_;
385
386/// A unique identifier for a resource **item**.
387///
388/// A unique resource is usually identified by [`ResourceIndex`], but if you
389/// need to have a resource container for a resource type then identify each
390/// item in the container, this would be useful. This resource item identifier
391/// is composed of the `ResourceIndex` and **item index** as well. The item
392/// index is a pair of index(usize) and generation(u64) so that you can use it
393/// for most cases.
394#[derive(Clone, Copy, PartialEq, Eq, Hash)]
395pub struct ResourceId {
396    /// Index to a specific resource container.
397    ///
398    /// Resource container is just a resource but a container type like vector.
399    ri: ResourceIndex,
400
401    /// Pair of index and generation for an item in the resource container.
402    ii: With<usize, u64>,
403}
404
405impl ResourceId {
406    /// Creates a new [`ResourceId`] with the given resource index and item
407    /// index.
408    pub const fn new(ri: ResourceIndex, ii: With<usize, u64>) -> Self {
409        Self { ri, ii }
410    }
411
412    /// Returns resource index.
413    pub const fn resource_index(&self) -> ResourceIndex {
414        self.ri
415    }
416
417    /// Returns item index.
418    ///
419    /// Item index consists of an index(usize) and a generation(u64), but
420    /// the generation may not be used. It depends.
421    pub const fn item_index(&self) -> With<usize, u64> {
422        self.ii
423    }
424}
425
426/// A unique resource identifier.
427///
428/// Resource index is composed of index(usize) and generation(u64). The
429/// generation is determined when the resource is registered to an ECS instance.
430/// The generation help us detect stale resource identifiers.
431#[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
432#[repr(transparent)]
433pub struct ResourceIndex(With<usize, u64>);
434
435impl ResourceIndex {
436    const DUMMY: Self = Self(With::new(usize::MAX, u64::MAX));
437
438    /// Creates a new [`ResourceIndex`] with the given index and generation.
439    pub const fn new(index: usize, generation: u64) -> Self {
440        Self(With::new(index, generation))
441    }
442
443    /// Creates a dummy [`ResourceIndex`].
444    pub const fn dummy() -> Self {
445        Self::DUMMY
446    }
447
448    /// Returns true if the resource index is dummy.
449    pub fn is_dummy(&self) -> bool {
450        *self == Self::dummy()
451    }
452
453    /// Returns inner index.
454    pub fn index(&self) -> usize {
455        self.0.value
456    }
457
458    /// Returns inner generation.
459    pub fn generation(&self) -> u64 {
460        self.0.with
461    }
462}
463
464impl Default for ResourceIndex {
465    fn default() -> Self {
466        Self::dummy()
467    }
468}
469
470impl fmt::Display for ResourceIndex {
471    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472        self.0.fmt(f)
473    }
474}