async_ecs/world/
meta.rs

1use std::any::TypeId;
2use std::marker::PhantomData;
3
4use hashbrown::hash_map::{Entry, HashMap};
5use mopa::Any;
6
7use crate::resource::{Resource, ResourceId};
8
9use super::World;
10
11/// The `MetaTable` which allows to store object-safe trait implementations for
12/// resources.
13///
14/// For example, you have a trait `Foo` that is implemented by several
15/// resources. You can register all the implementors using
16/// `MetaTable::register`. Later on, you can iterate over all resources that
17/// implement `Foo` without knowing their specific type.
18///
19/// # Examples
20///
21/// ```
22/// use async_ecs::*;
23///
24/// trait Object {
25///     fn method1(&self) -> i32;
26///
27///     fn method2(&mut self, x: i32);
28/// }
29///
30/// unsafe impl<T> CastFrom<T> for dyn Object
31/// where
32///     T: Object + 'static,
33/// {
34///     fn cast(t: &T) -> &Self {
35///         t
36///     }
37///
38///     fn cast_mut(t: &mut T) -> &mut Self {
39///         t
40///     }
41/// }
42///
43/// struct ImplementorA(i32);
44///
45/// impl Object for ImplementorA {
46///     fn method1(&self) -> i32 {
47///         self.0
48///     }
49///
50///     fn method2(&mut self, x: i32) {
51///         self.0 += x;
52///     }
53/// }
54///
55/// struct ImplementorB(i32);
56///
57/// impl Object for ImplementorB {
58///     fn method1(&self) -> i32 {
59///         self.0
60///     }
61///
62///     fn method2(&mut self, x: i32) {
63///         self.0 *= x;
64///     }
65/// }
66///
67/// let mut world = World::default();
68/// world.insert(ImplementorA(3));
69/// world.insert(ImplementorB(1));
70///
71/// let mut table = MetaTable::<dyn Object>::new();
72/// table.register(&ImplementorA(31415)); // Can just be some instance of type `&ImplementorA`.
73/// table.register(&ImplementorB(27182));
74///
75/// {
76///     let mut iter = table.iter(&mut world);
77///     assert_eq!(iter.next().unwrap().method1(), 3);
78///     assert_eq!(iter.next().unwrap().method1(), 1);
79/// }
80/// ```
81pub struct MetaTable<T: ?Sized> {
82    fat: Vec<FatPtr>,
83    tys: Vec<TypeId>,
84    indices: HashMap<TypeId, usize>,
85    marker: PhantomData<Invariant<T>>,
86}
87
88impl<T: ?Sized> MetaTable<T> {
89    /// Creates a new `MetaTable`.
90    pub fn new() -> Self {
91        assert_unsized::<T>();
92
93        Default::default()
94    }
95
96    /// Registers a resource `R` that implements the trait `T`.
97    /// This just needs some instance of type `R` to retrieve the vtable.
98    /// It doesn't have to be the same object you're calling `get` with later.
99    pub fn register<R>(&mut self, r: &R)
100    where
101        R: Resource,
102        T: CastFrom<R> + 'static,
103    {
104        let thin_ptr = r as *const R as usize;
105        let casted_ptr = <T as CastFrom<R>>::cast(r);
106        let thin_casted_ptr = casted_ptr as *const T as *const () as usize;
107
108        assert_eq!(
109            thin_ptr, thin_casted_ptr,
110            "Bug: `CastFrom` did not cast `self`"
111        );
112
113        let fat = unsafe { FatPtr::from_ptr(casted_ptr) };
114
115        let ty_id = TypeId::of::<R>();
116
117        // Important: ensure no entry exists twice!
118        let len = self.indices.len();
119        match self.indices.entry(ty_id) {
120            Entry::Occupied(occ) => {
121                let ind = *occ.get();
122
123                self.fat[ind] = fat;
124            }
125            Entry::Vacant(vac) => {
126                vac.insert(len);
127
128                self.fat.push(fat);
129                self.tys.push(ty_id);
130            }
131        }
132    }
133
134    /// Tries to convert `world` to a trait object of type `&T`.
135    /// If `world` doesn't have an implementation for `T` (or it wasn't
136    /// registered), this will return `None`.
137    pub fn get<'a>(&self, res: &'a dyn Resource) -> Option<&'a T> {
138        unsafe {
139            self.indices
140                .get(&Any::get_type_id(res))
141                .map(move |&ind| &*self.fat[ind].create_ptr(res as *const _ as *const ()))
142        }
143    }
144
145    /// Tries to convert `world` to a trait object of type `&mut T`.
146    /// If `world` doesn't have an implementation for `T` (or it wasn't
147    /// registered), this will return `None`.
148    pub fn get_mut<'a>(&self, res: &'a dyn Resource) -> Option<&'a mut T> {
149        unsafe {
150            self.indices.get(&Any::get_type_id(res)).map(move |&ind| {
151                &mut *(self.fat[ind].create_ptr::<T>(res as *const _ as *const ()) as *mut T)
152            })
153        }
154    }
155
156    /// Iterates all resources that implement `T` and were registered.
157    pub fn iter<'a>(&'a self, res: &'a World) -> MetaIter<'a, T> {
158        MetaIter {
159            fat: &self.fat,
160            index: 0,
161            world: res,
162            tys: &self.tys,
163            marker: PhantomData,
164        }
165    }
166
167    /// Iterates all resources that implement `T` and were registered mutably.
168    pub fn iter_mut<'a>(&'a self, res: &'a World) -> MetaIterMut<'a, T> {
169        MetaIterMut {
170            fat: &self.fat,
171            index: 0,
172            world: res,
173            tys: &self.tys,
174            marker: PhantomData,
175        }
176    }
177}
178
179impl<T> Default for MetaTable<T>
180where
181    T: ?Sized,
182{
183    fn default() -> Self {
184        MetaTable {
185            fat: Default::default(),
186            indices: Default::default(),
187            tys: Default::default(),
188            marker: Default::default(),
189        }
190    }
191}
192
193struct FatPtr(usize);
194
195impl FatPtr {
196    unsafe fn from_ptr<T: ?Sized>(t: &T) -> Self {
197        use std::ptr::read;
198
199        assert_unsized::<T>();
200
201        let fat_ptr = &t as *const &T as *const usize;
202        // Memory layout:
203        // [object pointer, vtable pointer]
204        //  ^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^
205        //  8 bytes       | 8 bytes
206        // (on 32-bit both have 4 bytes)
207        let vtable = read::<usize>(fat_ptr.offset(1));
208
209        Self(vtable)
210    }
211
212    unsafe fn create_ptr<T: ?Sized>(&self, ptr: *const ()) -> *const T {
213        let fat_ptr: (*const (), usize) = (ptr, self.0);
214
215        *(&fat_ptr as *const (*const (), usize) as *const *const T)
216    }
217}
218
219/// Helper trait for the `MetaTable`.
220/// This trait is required to be implemented for a trait to be compatible with
221/// the meta table.
222///
223/// # Memory safety
224///
225/// Not casting `self` but e.g. a field to the trait object can result in severe
226/// memory safety issues.
227///
228/// # Examples
229///
230/// ```
231/// use async_ecs::*;
232///
233/// trait Foo {
234///     fn foo1(&self);
235///     fn foo2(&mut self, x: i32) -> i32;
236/// }
237///
238/// unsafe impl<T> CastFrom<T> for dyn Foo
239/// where
240///     T: Foo + 'static,
241/// {
242///     fn cast(t: &T) -> &(dyn Foo + 'static) {
243///         t
244///     }
245///
246///     fn cast_mut(t: &mut T) -> &mut (dyn Foo + 'static) {
247///         t
248///     }
249/// }
250/// ```
251pub unsafe trait CastFrom<T> {
252    /// Casts an immutable `T` reference to a trait object.
253    fn cast(t: &T) -> &Self;
254
255    /// Casts a mutable `T` reference to a trait object.
256    fn cast_mut(t: &mut T) -> &mut Self;
257}
258
259/// This implements `Send` and `Sync` unconditionally.
260/// (the trait itself doesn't need to have these bounds and the
261/// resources are already guaranteed to fulfill it).
262struct Invariant<T: ?Sized>(*mut T);
263
264unsafe impl<T> Send for Invariant<T> where T: ?Sized {}
265
266unsafe impl<T> Sync for Invariant<T> where T: ?Sized {}
267
268/// An iterator for the `MetaTable`.
269pub struct MetaIter<'a, T: ?Sized + 'a> {
270    index: usize,
271    fat: &'a [FatPtr],
272    tys: &'a [TypeId],
273    world: &'a World,
274    marker: PhantomData<Invariant<T>>,
275}
276
277impl<'a, T> Iterator for MetaIter<'a, T>
278where
279    T: ?Sized + 'a,
280{
281    type Item = &'a T;
282
283    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
284        let index = self.index;
285        self.index += 1;
286
287        let res_id: ResourceId = match self.tys.get(index) {
288            Some(&x) => x.into(),
289            None => return None,
290        };
291
292        // Ugly hack that works due to `UnsafeCell` and distinct resources.
293        unsafe {
294            self.world
295                .resource_raw(&res_id)
296                .map(|res| {
297                    self.fat[index].create_ptr::<T>(Box::as_ref(&res.borrow())
298                        as *const dyn Resource
299                        as *const ())
300                })
301                .map(|ptr| &*ptr)
302                .or_else(|| self.next())
303        }
304    }
305}
306
307/// A mutable iterator for the `MetaTable`.
308pub struct MetaIterMut<'a, T: ?Sized + 'a> {
309    index: usize,
310    fat: &'a [FatPtr],
311    tys: &'a [TypeId],
312    world: &'a World,
313    marker: PhantomData<Invariant<T>>,
314}
315
316impl<'a, T> Iterator for MetaIterMut<'a, T>
317where
318    T: ?Sized + 'a,
319{
320    type Item = &'a mut T;
321
322    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
323        let index = self.index;
324        self.index += 1;
325
326        let res_id: ResourceId = match self.tys.get(index) {
327            Some(&x) => x.into(),
328            None => return None,
329        };
330
331        // Ugly hack that works due to `UnsafeCell` and distinct resources.
332        unsafe {
333            self.world
334                .resource_raw(&res_id)
335                .map(|res| {
336                    self.fat[index].create_ptr::<T>(Box::as_mut(&mut res.borrow_mut())
337                        as *mut dyn Resource
338                        as *const ()) as *mut T
339                })
340                .map(|ptr| &mut *ptr)
341                .or_else(|| self.next())
342        }
343    }
344}
345
346fn assert_unsized<T: ?Sized>() {
347    use std::mem::size_of;
348
349    assert_eq!(size_of::<&T>(), 2 * size_of::<usize>());
350}
351
352#[cfg(test)]
353mod tests {
354    use super::*;
355    use World;
356
357    trait Object {
358        fn method1(&self) -> i32;
359
360        fn method2(&mut self, x: i32);
361    }
362
363    unsafe impl<T> CastFrom<T> for dyn Object
364    where
365        T: Object + 'static,
366    {
367        fn cast(t: &T) -> &Self {
368            t
369        }
370
371        fn cast_mut(t: &mut T) -> &mut Self {
372            t
373        }
374    }
375
376    struct ImplementorA(i32);
377
378    impl Object for ImplementorA {
379        fn method1(&self) -> i32 {
380            self.0
381        }
382
383        fn method2(&mut self, x: i32) {
384            self.0 += x;
385        }
386    }
387
388    struct ImplementorB(i32);
389
390    impl Object for ImplementorB {
391        fn method1(&self) -> i32 {
392            self.0
393        }
394
395        fn method2(&mut self, x: i32) {
396            self.0 *= x;
397        }
398    }
399
400    #[test]
401    fn test_iter_all() {
402        let mut world = World::default();
403        world.insert(ImplementorA(3));
404        world.insert(ImplementorB(1));
405
406        let mut table = MetaTable::<dyn Object>::new();
407        table.register(&ImplementorA(125));
408        table.register(&ImplementorB(111_111));
409
410        {
411            let mut iter = table.iter(&world);
412            assert_eq!(iter.next().unwrap().method1(), 3);
413            assert_eq!(iter.next().unwrap().method1(), 1);
414        }
415
416        {
417            let mut iter_mut = table.iter_mut(&world);
418            let obj = iter_mut.next().unwrap();
419            obj.method2(3);
420            assert_eq!(obj.method1(), 6);
421            let obj = iter_mut.next().unwrap();
422            obj.method2(4);
423            assert_eq!(obj.method1(), 4);
424        }
425    }
426
427    #[test]
428    fn test_iter_all_after_removal() {
429        let mut world = World::default();
430        world.insert(ImplementorA(3));
431        world.insert(ImplementorB(1));
432
433        let mut table = MetaTable::<dyn Object>::new();
434        table.register(&ImplementorA(125));
435        table.register(&ImplementorB(111_111));
436
437        {
438            let mut iter = table.iter(&world);
439            assert_eq!(iter.next().unwrap().method1(), 3);
440            assert_eq!(iter.next().unwrap().method1(), 1);
441        }
442
443        world.remove::<ImplementorA>().unwrap();
444
445        {
446            let mut iter = table.iter(&world);
447            assert_eq!(iter.next().unwrap().method1(), 1);
448        }
449
450        world.remove::<ImplementorB>().unwrap();
451    }
452
453    struct ImplementorC;
454
455    impl Object for ImplementorC {
456        fn method1(&self) -> i32 {
457            33
458        }
459
460        fn method2(&mut self, _x: i32) {
461            unimplemented!()
462        }
463    }
464
465    struct ImplementorD;
466
467    impl Object for ImplementorD {
468        fn method1(&self) -> i32 {
469            42
470        }
471
472        fn method2(&mut self, _x: i32) {
473            unimplemented!()
474        }
475    }
476
477    #[test]
478    fn get() {
479        let mut world = World::default();
480        world.insert(ImplementorC);
481        world.insert(ImplementorD);
482
483        let mut table = MetaTable::<dyn Object>::new();
484        table.register(&ImplementorC);
485        table.register(&ImplementorD);
486
487        assert_eq!(
488            table
489                .get(&*world.resource::<ImplementorC>())
490                .unwrap()
491                .method1(),
492            33
493        );
494        assert_eq!(
495            table
496                .get(&*world.resource::<ImplementorD>())
497                .unwrap()
498                .method1(),
499            42
500        );
501
502        // Make sure it fulfills `Resource` requirements
503        world.insert(table);
504    }
505}