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