flax/
vtable.rs

1use core::{alloc::Layout, any::TypeId, marker::PhantomData, mem, ptr::NonNull};
2
3use once_cell::sync::OnceCell;
4
5use crate::{
6    buffer::ComponentBuffer,
7    component::{ComponentDesc, ComponentValue},
8};
9
10#[doc(hidden)]
11pub struct LazyComponentBuffer {
12    value: OnceCell<ComponentBuffer>,
13    init: fn(ComponentDesc) -> ComponentBuffer,
14}
15
16impl LazyComponentBuffer {
17    /// Creates a new component buffer which can also be recreated
18    pub const fn new(init: fn(ComponentDesc) -> ComponentBuffer) -> Self {
19        Self {
20            value: OnceCell::new(),
21            init,
22        }
23    }
24
25    pub(crate) fn get_ref(&self, desc: ComponentDesc) -> &ComponentBuffer {
26        self.value.get_or_init(|| (self.init)(desc))
27    }
28
29    pub(crate) fn get(&self, desc: ComponentDesc) -> ComponentBuffer {
30        (self.init)(desc)
31    }
32}
33
34/// Describes a components dynamic functionality, such as name, metadata, and type layout.
35pub struct UntypedVTable {
36    pub(crate) name: &'static str,
37    pub(crate) drop: unsafe fn(*mut u8),
38    pub(crate) layout: Layout,
39    pub(crate) type_id: fn() -> TypeId,
40    pub(crate) type_name: fn() -> &'static str,
41    // Dangling pointer with proper alignment
42    // See: https://github.com/rust-lang/rust/issues/55724
43    pub(crate) dangling: fn() -> NonNull<u8>,
44    /// A metadata is a component which is attached to the component, such as
45    /// metadata or name
46    pub(crate) meta: LazyComponentBuffer,
47}
48
49impl UntypedVTable {
50    /// Returns true if the vtable is of type `T`
51    pub fn is<T: ComponentValue>(&self) -> bool {
52        (self.type_id)() == TypeId::of::<T>()
53    }
54
55    /// Creates a new vtable of type `T`
56    pub(crate) const fn new<T: ComponentValue>(
57        name: &'static str,
58        meta: LazyComponentBuffer,
59    ) -> Self {
60        unsafe fn drop_ptr<T>(x: *mut u8) {
61            x.cast::<T>().drop_in_place()
62        }
63
64        UntypedVTable {
65            name,
66            drop: drop_ptr::<T>,
67            layout: Layout::new::<T>(),
68            type_id: || TypeId::of::<T>(),
69            type_name: || core::any::type_name::<T>(),
70            meta,
71            dangling: || NonNull::<T>::dangling().cast(),
72        }
73    }
74}
75
76/// Represents a strongly typed vtable
77#[repr(transparent)]
78pub struct ComponentVTable<T> {
79    inner: UntypedVTable,
80    marker: PhantomData<T>,
81}
82
83impl<T> core::fmt::Debug for ComponentVTable<T> {
84    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
85        f.debug_struct("ComponentVTable")
86            .field("name", &self.inner.name)
87            .field("type_name", &self.inner.type_name)
88            .finish()
89    }
90}
91
92impl<T> core::ops::Deref for ComponentVTable<T> {
93    type Target = UntypedVTable;
94
95    fn deref(&self) -> &Self::Target {
96        &self.inner
97    }
98}
99
100impl<T: ComponentValue> ComponentVTable<T> {
101    /// Creates a new *typed* vtable of `T`
102    pub const fn new(name: &'static str, meta: LazyComponentBuffer) -> Self {
103        Self {
104            inner: UntypedVTable::new::<T>(name, meta),
105            marker: PhantomData,
106        }
107    }
108
109    pub(crate) fn erase(&self) -> &'static UntypedVTable {
110        unsafe { mem::transmute(self) }
111    }
112}