1use std::alloc::{alloc, dealloc, handle_alloc_error, Layout};
2use std::any::TypeId;
3use std::cmp::Reverse;
4use std::mem::{align_of, needs_drop, transmute};
5use std::ptr::{copy_nonoverlapping, drop_in_place, slice_from_raw_parts_mut};
6
7use crate::{resources::TypeIdMap, world::Entity};
8
9pub struct Archetype {
10 types: Box<[TypeMetadata]>,
11 layout: Layout,
12 len: u32,
13 cap: u32,
14 ptr: *mut u8,
15}
16
17unsafe impl Send for Archetype {}
18
19impl Archetype {
20 pub fn new(types: TypeMetadataSet) -> Self {
21 assert!(types.0.len() <= u16::MAX as usize);
22
23 let max_align = types
24 .0
25 .iter()
26 .map(|ty| ty.layout.align())
27 .max()
28 .unwrap_or(1);
29
30 let layout = Layout::from_size_align(0, max_align).unwrap();
31 let ptr = invalid_ptr(max_align);
32
33 Self {
34 types: types.0.into(),
35 layout,
36 len: 0,
37 cap: 0,
38 ptr,
39 }
40 }
41}
42
43impl Archetype {
44 pub fn len(&self) -> u32 {
45 self.len
46 }
47
48 pub unsafe fn alloc(&mut self) -> u32 {
49 if self.len == self.cap {
50 self.grow();
51 }
52
53 let idx = self.len;
54 self.len += 1;
55 idx
56 }
57
58 #[must_use]
59 pub unsafe fn free<const DROP: bool>(&mut self, idx: u32) -> bool {
60 self.len -= 1;
61
62 let _copy_on_drop;
63
64 let swapped = if idx != self.len {
65 struct CopyOnDrop<'a> {
66 types: &'a [TypeMetadata],
67 len: u32,
68 idx: u32,
69 }
70
71 impl Drop for CopyOnDrop<'_> {
72 fn drop(&mut self) {
73 for ty in self.types {
74 let size = ty.layout.size();
75
76 unsafe {
77 let src_ptr = ty.base_pointer.add(size * self.len as usize);
78 let dst_ptr = ty.base_pointer.add(size * self.idx as usize);
79
80 copy_nonoverlapping(src_ptr, dst_ptr, size);
81 }
82 }
83 }
84 }
85
86 _copy_on_drop = CopyOnDrop {
87 types: &self.types,
88 len: self.len,
89 idx,
90 };
91
92 true
93 } else {
94 false
95 };
96
97 if DROP {
98 for ty in &*self.types {
99 if let Some(drop) = ty.drop {
100 let ptr = ty.base_pointer.add(ty.layout.size() * idx as usize);
101
102 drop(ptr, 1);
103 }
104 }
105 }
106
107 swapped
108 }
109
110 pub fn clear(&mut self) {
111 let len = self.len;
112 self.len = 0;
113
114 for ty in &*self.types {
115 if let Some(drop) = ty.drop {
116 unsafe {
117 drop(ty.base_pointer, len as usize);
118 }
119 }
120 }
121 }
122
123 #[cold]
124 #[inline(never)]
125 fn grow(&mut self) {
126 let new_cap = self.cap.checked_add(self.cap.max(8)).unwrap() as usize;
127
128 struct SortOnDrop<'a>(&'a mut [TypeMetadata]);
129
130 impl Drop for SortOnDrop<'_> {
131 fn drop(&mut self) {
132 self.0.sort_unstable_by_key(|ty| ty.id);
133 }
134 }
135
136 let types = SortOnDrop(&mut self.types);
137
138 types
139 .0
140 .sort_unstable_by_key(|ty| Reverse(ty.layout.align()));
141
142 let mut new_offsets = Vec::<usize>::with_capacity(types.0.len());
143 let mut new_size = 0;
144
145 for ty in types.0.iter() {
146 new_offsets.push(new_size);
147
148 new_size = new_size
149 .checked_add(ty.layout.size().checked_mul(new_cap).unwrap())
150 .unwrap();
151 }
152
153 let new_layout = Layout::from_size_align(new_size, self.layout.align()).unwrap();
154
155 let new_ptr = alloc_ptr(new_layout);
156
157 unsafe {
158 for (ty, new_offset) in types.0.iter_mut().zip(&new_offsets) {
159 let new_base_pointer = new_ptr.add(*new_offset);
160
161 copy_nonoverlapping(
162 ty.base_pointer,
163 new_base_pointer,
164 ty.layout.size() * self.cap as usize,
165 );
166
167 ty.base_pointer = new_base_pointer;
168 }
169
170 if self.layout.size() != 0 {
171 dealloc(self.ptr, self.layout);
172 }
173 }
174
175 self.layout = new_layout;
176 self.ptr = new_ptr;
177
178 self.cap = new_cap as u32;
179 }
180}
181
182impl Drop for Archetype {
183 fn drop(&mut self) {
184 self.clear();
185
186 unsafe {
187 if self.layout.size() != 0 {
188 dealloc(self.ptr, self.layout);
189 }
190 }
191 }
192}
193
194impl Archetype {
195 pub fn types(&self) -> TypeMetadataSet {
196 TypeMetadataSet(self.types.to_vec())
197 }
198
199 pub fn match_(&self, types: &TypeMetadataSet) -> bool {
200 let lhs = self.types.iter().map(|ty| ty.id);
201 let rhs = types.0.iter().map(|ty| ty.id);
202
203 lhs.eq(rhs)
204 }
205
206 pub fn find<C>(&self) -> Option<u16>
207 where
208 C: 'static,
209 {
210 self.find_impl(&TypeId::of::<C>())
211 }
212
213 fn find_impl(&self, key: &TypeId) -> Option<u16> {
214 self.types
215 .binary_search_by_key(key, |ty| ty.id)
216 .map(|idx| idx as u16)
217 .ok()
218 }
219}
220
221impl Archetype {
222 #[allow(clippy::needless_pass_by_ref_mut)]
223 pub unsafe fn move_(src: &mut Self, dst: &mut Self, src_idx: u32, dst_idx: u32) {
224 debug_assert!(src_idx < src.len);
225 debug_assert!(dst_idx < dst.len);
226
227 let mut dst_types = &*dst.types;
228
229 for src_ty in &*src.types {
230 let dst_ty = match dst_types.iter().position(|ty| ty.id == src_ty.id) {
231 Some(dst_ty) => {
232 dst_types = &dst_types[dst_ty..];
233 &dst_types[0]
234 }
235 None => continue,
236 };
237
238 let size = src_ty.layout.size();
239
240 let src_ptr = src_ty.base_pointer.add(size * src_idx as usize);
241 let dst_ptr = dst_ty.base_pointer.add(size * dst_idx as usize);
242
243 copy_nonoverlapping(src_ptr, dst_ptr, size);
244 }
245 }
246}
247
248impl Archetype {
249 #[cfg(debug_assertions)]
250 fn type_metadata<C>(&self, ty: u16) -> &TypeMetadata
251 where
252 C: 'static,
253 {
254 let ty = &self.types[ty as usize];
255 assert_eq!(ty.id, TypeId::of::<C>());
256 ty
257 }
258
259 #[cfg(not(debug_assertions))]
260 unsafe fn type_metadata_unchecked(&self, ty: u16) -> &TypeMetadata {
261 self.types.get_unchecked(ty as usize)
262 }
263
264 pub unsafe fn base_pointer<C>(&self, ty: u16) -> *mut C
265 where
266 C: 'static,
267 {
268 #[cfg(debug_assertions)]
269 let ty = self.type_metadata::<C>(ty);
270 #[cfg(not(debug_assertions))]
271 let ty = self.type_metadata_unchecked(ty);
272
273 ty.base_pointer.cast::<C>()
274 }
275
276 pub unsafe fn pointer<C>(&self, ty: u16, idx: u32) -> *mut C
277 where
278 C: 'static,
279 {
280 let ptr = self.base_pointer::<C>(ty);
281
282 debug_assert!(idx < self.len);
283 ptr.add(idx as usize)
284 }
285}
286
287impl Archetype {
288 pub unsafe fn get<C>(&self, idx: u32) -> *mut C
289 where
290 C: 'static,
291 {
292 let ty = self.find::<C>().unwrap();
293 self.pointer::<C>(ty, idx)
294 }
295
296 pub unsafe fn drop<C>(&mut self, idx: u32)
297 where
298 C: 'static,
299 {
300 if needs_drop::<C>() {
301 if let Some(ty) = self.find::<C>() {
302 let ty = self.types.get_unchecked(ty as usize);
303
304 let ptr = ty.base_pointer.add(ty.layout.size() * idx as usize);
305
306 (ty.drop.unwrap())(ptr, 1);
307 }
308 }
309 }
310}
311
312impl Archetype {
313 pub fn clone(&mut self, cloner: &Cloner) -> Self {
314 let ptr = alloc_ptr(self.layout);
315
316 let mut types = self.types.clone();
317
318 unsafe {
319 for ty in &mut *types {
320 let clone = cloner
321 .clone
322 .get(&ty.id)
323 .expect("Component type missing from cloner");
324
325 let offset = ty.base_pointer.offset_from(self.ptr);
326 let new_base_pointer = ptr.offset(offset);
327
328 clone(ty.base_pointer, new_base_pointer, self.len as usize);
329
330 ty.base_pointer = new_base_pointer;
331 }
332 }
333
334 Self {
335 types,
336 layout: self.layout,
337 len: self.len,
338 cap: self.cap,
339 ptr,
340 }
341 }
342}
343
344#[derive(Clone, Copy)]
345struct TypeMetadata {
346 id: TypeId,
347 layout: Layout,
348 drop: Option<unsafe fn(*mut u8, usize)>,
349 base_pointer: *mut u8,
350}
351
352impl TypeMetadata {
353 fn new<C>() -> Self
354 where
355 C: 'static,
356 {
357 unsafe fn drop<C>(ptr: *mut u8, len: usize) {
358 drop_in_place(slice_from_raw_parts_mut(ptr.cast::<C>(), len))
359 }
360
361 Self {
362 id: TypeId::of::<C>(),
363 layout: Layout::new::<C>(),
364 drop: if needs_drop::<C>() {
365 Some(drop::<C>)
366 } else {
367 None
368 },
369 base_pointer: invalid_ptr(align_of::<C>()),
370 }
371 }
372}
373
374#[derive(Default)]
375pub struct TypeMetadataSet(Vec<TypeMetadata>);
376
377impl TypeMetadataSet {
378 pub fn ids(&self) -> impl ExactSizeIterator<Item = TypeId> + '_ {
379 self.0.iter().map(|ty| ty.id)
380 }
381
382 pub fn insert<C>(&mut self)
383 where
384 C: 'static,
385 {
386 if let Err(ty) = self.0.binary_search_by_key(&TypeId::of::<C>(), |ty| ty.id) {
387 self.0.insert(ty, TypeMetadata::new::<C>());
388 }
389 }
390
391 #[must_use]
392 pub fn remove<C>(&mut self) -> Option<()>
393 where
394 C: 'static,
395 {
396 let ty = self
397 .0
398 .binary_search_by_key(&TypeId::of::<C>(), |ty| ty.id)
399 .ok()?;
400
401 self.0.remove(ty);
402
403 Some(())
404 }
405}
406
407fn alloc_ptr(layout: Layout) -> *mut u8 {
408 if layout.size() == 0 {
409 return invalid_ptr(layout.align());
410 }
411
412 let ptr = unsafe { alloc(layout) };
413 if ptr.is_null() {
414 handle_alloc_error(layout);
415 }
416 ptr
417}
418
419fn invalid_ptr(addr: usize) -> *mut u8 {
420 unsafe {
421 #[allow(integer_to_ptr_transmutes)]
422 transmute(addr)
423 }
424}
425
426pub struct Cloner {
430 clone: TypeIdMap<unsafe fn(*const u8, *mut u8, usize)>,
431}
432
433impl Cloner {
434 pub fn new() -> Self {
436 let mut _self = Self {
437 clone: Default::default(),
438 };
439
440 _self.add_copyable::<Entity>();
441
442 _self
443 }
444}
445
446impl Default for Cloner {
447 fn default() -> Self {
448 Self::new()
449 }
450}
451
452impl Cloner {
453 pub fn add_cloneable<C>(&mut self)
457 where
458 C: Clone + 'static,
459 {
460 unsafe fn clone<C>(src: *const u8, dst: *mut u8, len: usize)
461 where
462 C: Clone,
463 {
464 let src = src.cast::<C>();
465 let dst = dst.cast::<C>();
466
467 for idx in 0..len {
468 let val = (*src.add(idx)).clone();
469 dst.add(idx).write(val);
470 }
471 }
472
473 self.clone.insert(TypeId::of::<C>(), clone::<C>);
474 }
475
476 pub fn add_copyable<C>(&mut self)
478 where
479 C: Copy + 'static,
480 {
481 unsafe fn clone<C>(src: *const u8, dst: *mut u8, len: usize)
482 where
483 C: Copy,
484 {
485 let src = src.cast::<C>();
486 let dst = dst.cast::<C>();
487
488 copy_nonoverlapping(src, dst, len);
489 }
490
491 self.clone.insert(TypeId::of::<C>(), clone::<C>);
492 }
493}
494
495#[cfg(test)]
496mod tests {
497 use super::*;
498
499 use std::cell::Cell;
500 use std::panic::{catch_unwind, AssertUnwindSafe};
501 use std::rc::Rc;
502 use std::thread::panicking;
503
504 #[test]
505 fn reverse_sorting_by_alignment_avoids_padding() {
506 let mut types = TypeMetadataSet::default();
507 types.insert::<u64>();
508 types.insert::<u32>();
509 types.insert::<u16>();
510 types.insert::<u8>();
511
512 let mut archetype = Archetype::new(types);
513 let _ent = unsafe { archetype.alloc() };
514
515 assert_eq!(archetype.layout.size(), 8 * (8 + 4 + 2 + 1));
516 assert_eq!(archetype.layout.align(), 8);
517
518 assert_eq!(archetype.len, 1);
519 assert_eq!(archetype.cap, 8);
520
521 assert_eq!(archetype.types.len(), 4);
522
523 unsafe {
524 let ty = archetype.find::<u64>().unwrap();
525 let base_pointer = archetype.base_pointer::<u64>(ty);
526 assert_eq!(base_pointer, archetype.ptr.add(0).cast());
527
528 let ty = archetype.find::<u32>().unwrap();
529 let base_pointer = archetype.base_pointer::<u32>(ty);
530 assert_eq!(base_pointer, archetype.ptr.add(8 * 8).cast());
531
532 let ty = archetype.find::<u16>().unwrap();
533 let base_pointer = archetype.base_pointer::<u16>(ty);
534 assert_eq!(base_pointer, archetype.ptr.add(8 * (8 + 4)).cast());
535
536 let ty = archetype.find::<u8>().unwrap();
537 let base_pointer = archetype.base_pointer::<u8>(ty);
538 assert_eq!(base_pointer, archetype.ptr.add(8 * (8 + 4 + 2)).cast());
539 }
540 }
541
542 #[test]
543 fn drops_all_component_values() {
544 struct CountDrops(Rc<Cell<usize>>);
545
546 impl Drop for CountDrops {
547 fn drop(&mut self) {
548 let drops = &self.0;
549
550 drops.set(drops.get() + 1);
551 }
552 }
553
554 let drops = Rc::new(Cell::new(0));
555
556 let mut types = TypeMetadataSet::default();
557 types.insert::<CountDrops>();
558
559 let mut archetype = Archetype::new(types);
560
561 unsafe {
562 for _ in 0..32 {
563 let ent = archetype.alloc();
564
565 archetype
566 .get::<CountDrops>(ent)
567 .write(CountDrops(drops.clone()));
568 }
569 }
570
571 drop(archetype);
572
573 assert_eq!(drops.get(), 32);
574 }
575
576 struct MaybePanicOnDrop(bool);
577
578 impl Drop for MaybePanicOnDrop {
579 fn drop(&mut self) {
580 if self.0 && !panicking() {
581 panic!();
582 }
583 }
584 }
585
586 #[test]
587 fn free_does_not_double_drop_on_panic() {
588 let mut types = TypeMetadataSet::default();
589 types.insert::<MaybePanicOnDrop>();
590
591 let mut archetype = Archetype::new(types);
592
593 unsafe {
594 for idx in 0..4 {
595 let ent = archetype.alloc();
596
597 archetype
598 .get::<MaybePanicOnDrop>(ent)
599 .write(MaybePanicOnDrop(idx == 2));
600 }
601 }
602
603 let res = catch_unwind(AssertUnwindSafe(|| unsafe { archetype.free::<true>(2) }));
604 assert!(res.is_err());
605
606 assert_eq!(archetype.len(), 3);
607 }
608
609 #[test]
610 fn clear_does_not_double_drop_on_panic() {
611 let mut types = TypeMetadataSet::default();
612 types.insert::<MaybePanicOnDrop>();
613
614 let mut archetype = Archetype::new(types);
615
616 unsafe {
617 for idx in 0..4 {
618 let ent = archetype.alloc();
619
620 archetype
621 .get::<MaybePanicOnDrop>(ent)
622 .write(MaybePanicOnDrop(idx == 2));
623 }
624 }
625
626 let res = catch_unwind(AssertUnwindSafe(|| archetype.clear()));
627 assert!(res.is_err());
628
629 assert_eq!(archetype.len(), 0);
630 }
631}