-
Notifications
You must be signed in to change notification settings - Fork 142
Expand file tree
/
Copy pathentity_builder.rs
More file actions
160 lines (134 loc) · 4.52 KB
/
Copy pathentity_builder.rs
File metadata and controls
160 lines (134 loc) · 4.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use std::{
alloc::{alloc, dealloc},
any::TypeId,
mem::{size_of, MaybeUninit},
ptr::{self, NonNull},
};
use crate::{component::ComponentMeta, Component, Entities, Entity};
/// A utility to build an entity's components.
///
/// An `EntityBuilder` can be reused to avoid repeated allocations.
#[derive(Default)]
pub struct EntityBuilder {
/// Packed vector containing component data.
components: Vec<MaybeUninit<u8>>,
entries: Vec<Entry>,
}
impl EntityBuilder {
pub fn new() -> Self {
Self::default()
}
/// Inserts a new component for the entity.
///
/// If the entity builder already contains the component,
/// then the previous value is overriden.
pub fn add<T: Component>(&mut self, component: T) -> &mut Self {
let component = MaybeUninit::new(component);
self.components.reserve(size_of::<T>());
let offset = self.components.len();
unsafe {
ptr::copy_nonoverlapping(
component.as_ptr().cast::<MaybeUninit<u8>>(),
self.components.as_mut_ptr().add(offset),
size_of::<T>(),
);
self.components
.set_len(self.components.len() + size_of::<T>());
}
self.entries.push(Entry {
component_meta: ComponentMeta::of::<T>(),
offset,
});
self
}
/// Determines whether the builder has a component
/// of type T.
pub fn has<T: Component>(&self) -> bool {
self.entries
.iter()
.any(|entry| entry.component_meta.type_id == TypeId::of::<T>())
}
/// Gets the given component from the builder.
///
/// For soundness reasons, `T` must be `Copy`.
///
/// Time complexity: O(n) with respect to the number of components.
pub fn get<T: Copy + Component>(&self) -> Option<T> {
let entry = self
.entries
.iter()
.find(|e| e.component_meta.type_id == TypeId::of::<T>());
entry.map(|entry| unsafe {
ptr::read_unaligned(self.components.as_ptr().add(entry.offset).cast::<T>())
})
}
/// Spawns the entity builder into an `Ecs`.
pub fn spawn_into(&mut self, ecs: &mut Entities) -> Entity {
ecs.spawn_builder(self)
}
/// Drains the builder, returning tuples of
/// the component meta and a pointer
/// to the component data.
///
/// NB: component data is not necessarily aligned.
pub(crate) fn drain(&mut self) -> impl Iterator<Item = (ComponentMeta, NonNull<u8>)> + '_ {
let components = &mut self.components;
self.entries.drain(..).map(move |entry| {
let component = unsafe {
NonNull::new_unchecked(components.as_mut_ptr().add(entry.offset).cast::<u8>())
};
(entry.component_meta, component)
})
}
/// Resets the builder, clearing all components.
///
/// Does not invoke component drop functions.
pub(crate) fn reset(&mut self) {
self.entries.clear();
self.components.clear();
}
}
impl Drop for EntityBuilder {
fn drop(&mut self) {
for entry in self.entries.drain(..) {
unsafe {
let src_ptr = self.components.as_ptr().add(entry.offset).cast::<u8>();
// Pointers in the entity builder are unaligned, so a
// separate, aligned buffer is needed to store the component for dropping.
let buffer = alloc(entry.component_meta.layout);
std::ptr::copy_nonoverlapping(src_ptr, buffer, entry.component_meta.layout.size());
(entry.component_meta.drop_fn)(buffer);
dealloc(buffer, entry.component_meta.layout);
}
}
}
}
struct Entry {
component_meta: ComponentMeta,
offset: usize,
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Copy, Clone, Debug, PartialEq)]
struct Comp<T>(T);
impl<T: 'static> Component for Comp<T> {}
#[test]
fn build_entity() {
let mut builder = EntityBuilder::new();
builder
.add(Comp(10i32))
.add(Comp("a string".to_owned()))
.add(Comp(50usize));
assert_eq!(builder.get::<Comp<i32>>(), Some(Comp(10)));
builder.reset();
assert_eq!(builder.drain().count(), 0);
}
#[test]
fn drops_components_on_drop() {
let mut builder = EntityBuilder::new();
builder.add(Comp(vec![1, 2, 3]));
drop(builder);
// A memory leak is detected by Miri if this fails
}
}