Skip to main content

gloss_hecs/
bundle.rs

1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8#![allow(clippy::zero_repeat_side_effects)]
9#![allow(clippy::ref_as_ptr)]
10
11use crate::{alloc::vec::Vec, stabletypeid::StableTypeId};
12use core::{any::type_name, fmt, mem, ptr::NonNull};
13
14use crate::{archetype::TypeInfo, Component};
15
16/// A dynamically typed collection of components
17///
18/// Bundles composed of exactly the same types are semantically equivalent,
19/// regardless of order. The interface of this trait is a private implementation
20/// detail.
21#[allow(clippy::missing_safety_doc)]
22pub unsafe trait DynamicBundle {
23    /// Returns a `StableTypeId` uniquely identifying the set of components, if
24    /// known
25    #[doc(hidden)]
26    fn key(&self) -> Option<StableTypeId> {
27        None
28    }
29
30    /// Invoke a callback on the fields' type IDs, sorted by descending
31    /// alignment then id
32    #[doc(hidden)]
33    fn with_ids<T>(&self, f: impl FnOnce(&[StableTypeId]) -> T) -> T;
34
35    /// Obtain the fields' TypeInfos, sorted by descending alignment then id
36    #[doc(hidden)]
37    fn type_info(&self) -> Vec<TypeInfo>;
38    /// Allow a callback to move all components out of the bundle
39    ///
40    /// Must invoke `f` only with a valid pointer and the pointee's type and
41    /// size.
42    #[doc(hidden)]
43    unsafe fn put(self, f: impl FnMut(*mut u8, TypeInfo));
44}
45
46/// A statically typed collection of components
47///
48/// Bundles composed of exactly the same types are semantically equivalent,
49/// regardless of order. The interface of this trait is a private implementation
50/// detail.
51#[allow(clippy::missing_safety_doc)]
52pub unsafe trait Bundle: DynamicBundle {
53    #[doc(hidden)]
54    fn with_static_ids<T>(f: impl FnOnce(&[StableTypeId]) -> T) -> T;
55
56    /// Obtain the fields' TypeInfos, sorted by descending alignment then id
57    #[doc(hidden)]
58    fn with_static_type_info<T>(f: impl FnOnce(&[TypeInfo]) -> T) -> T;
59
60    /// Construct `Self` by moving components out of pointers fetched by `f`
61    ///
62    /// # Safety
63    ///
64    /// `f` must produce pointers to the expected fields. The implementation
65    /// must not read from any pointers if any call to `f` returns `None`.
66    #[doc(hidden)]
67    unsafe fn get(f: impl FnMut(TypeInfo) -> Option<NonNull<u8>>) -> Result<Self, MissingComponent>
68    where
69        Self: Sized;
70}
71
72/// A dynamically typed collection of cloneable components
73#[allow(clippy::missing_safety_doc)]
74pub unsafe trait DynamicBundleClone: DynamicBundle {
75    /// Allow a callback to move all components out of the bundle
76    ///
77    /// Must invoke `f` only with a valid pointer, the pointee's type and size,
78    /// and a `DynamicClone` constructed for the pointee's type.
79    #[doc(hidden)]
80    unsafe fn put_with_clone(self, f: impl FnMut(*mut u8, TypeInfo, DynamicClone));
81}
82
83#[derive(Copy, Clone)]
84/// Type-erased [`Clone`] implementation
85pub struct DynamicClone {
86    pub(crate) func: unsafe fn(*const u8, &mut dyn FnMut(*mut u8, TypeInfo)),
87}
88
89impl DynamicClone {
90    /// Create a new type ereased cloner for the type T
91    #[allow(clippy::borrow_as_ptr)]
92    pub fn new<T: Component + Clone>() -> Self {
93        Self {
94            func: |src, f| {
95                let mut tmp = unsafe { (*src.cast::<T>()).clone() };
96                f((&mut tmp as *mut T).cast(), TypeInfo::of::<T>());
97                core::mem::forget(tmp);
98            },
99        }
100    }
101}
102
103/// Error indicating that an entity did not have a required component
104#[derive(Debug, Clone, Eq, PartialEq, Hash)]
105pub struct MissingComponent(&'static str);
106
107impl MissingComponent {
108    /// Construct an error representing a missing `T`
109    pub fn new<T: Component>() -> Self {
110        Self(type_name::<T>())
111    }
112}
113
114impl fmt::Display for MissingComponent {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        write!(f, "missing {} component", self.0)
117    }
118}
119
120#[cfg(feature = "std")]
121impl std::error::Error for MissingComponent {}
122#[allow(clippy::borrow_as_ptr)]
123macro_rules! tuple_impl {
124    ($($name: ident),*) => {
125        unsafe impl<$($name: Component),*> DynamicBundle for ($($name,)*) {
126            fn key(&self) -> Option<StableTypeId> {
127                Some(StableTypeId::of::<Self>())
128            }
129
130            fn with_ids<T>(&self, f: impl FnOnce(&[StableTypeId]) -> T) -> T {
131                Self::with_static_ids(f)
132            }
133
134            fn type_info(&self) -> Vec<TypeInfo> {
135                Self::with_static_type_info(|info| info.to_vec())
136            }
137
138            #[allow(unused_variables, unused_mut, clippy::borrow_as_ptr)]
139            unsafe fn put(self, mut f: impl FnMut(*mut u8, TypeInfo)) {
140                #[allow(non_snake_case)]
141                let ($(mut $name,)*) = self;
142                $(
143                    f(
144                        (&mut $name as *mut $name).cast::<u8>(),
145                        TypeInfo::of::<$name>()
146                    );
147                    mem::forget($name);
148                )*
149            }
150        }
151
152        unsafe impl<$($name: Component + Clone),*> DynamicBundleClone for ($($name,)*) {
153            // Compiler false positive warnings
154            #[allow(unused_variables, unused_mut, clippy::borrow_as_ptr)]
155            unsafe fn put_with_clone(self, mut f: impl FnMut(*mut u8, TypeInfo, DynamicClone)) {
156                #[allow(non_snake_case)]
157                let ($(mut $name,)*) = self;
158                $(
159                    f(
160                        (&mut $name as *mut $name).cast::<u8>(),
161                        TypeInfo::of::<$name>(),
162                        DynamicClone::new::<$name>()
163                    );
164                    mem::forget($name);
165                )*
166            }
167        }
168
169        unsafe impl<$($name: Component),*> Bundle for ($($name,)*) {
170            fn with_static_ids<T>(f: impl FnOnce(&[StableTypeId]) -> T) -> T {
171                const N: usize = count!($($name),*);
172                let mut xs: [(usize, StableTypeId); N] = [$((mem::align_of::<$name>(), StableTypeId::of::<$name>())),*];
173                xs.sort_unstable_by(|x, y| x.0.cmp(&y.0).reverse().then(x.1.cmp(&y.1)));
174                let mut ids = [StableTypeId::of::<()>(); N];
175                for (slot, &(_, id)) in ids.iter_mut().zip(xs.iter()) {
176                    *slot = id;
177                }
178                f(&ids)
179            }
180
181            fn with_static_type_info<T>(f: impl FnOnce(&[TypeInfo]) -> T) -> T {
182                const N: usize = count!($($name),*);
183                let mut xs: [TypeInfo; N] = [$(TypeInfo::of::<$name>()),*];
184                xs.sort_unstable();
185                f(&xs)
186            }
187
188            #[allow(unused_variables, unused_mut)]
189            unsafe fn get(mut f: impl FnMut(TypeInfo) -> Option<NonNull<u8>>) -> Result<Self, MissingComponent> {
190                #[allow(non_snake_case)]
191                let ($(mut $name,)*) = ($(
192                    f(TypeInfo::of::<$name>()).ok_or_else(MissingComponent::new::<$name>)?
193                        .as_ptr()
194                        .cast::<$name>(),)*
195                );
196                Ok(($($name.read(),)*))
197            }
198        }
199    }
200}
201
202macro_rules! count {
203    () => { 0 };
204    ($x: ident $(, $rest: ident)*) => { 1 + count!($($rest),*) };
205}
206
207smaller_tuples_too!(tuple_impl, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A);