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