1use crate::alloc::collections::BinaryHeap;
2use core::{any::TypeId, fmt, mem::MaybeUninit, slice};
3
4use crate::{
5 archetype::{TypeIdMap, TypeInfo},
6 Archetype, Component,
7};
8
9#[derive(Debug, Clone, Default)]
11pub struct ColumnBatchType {
12 types: BinaryHeap<TypeInfo>,
13}
14
15impl ColumnBatchType {
16 pub fn new() -> Self {
18 Self::default()
19 }
20
21 pub fn add<T: Component>(&mut self) -> &mut Self {
23 self.types.push(TypeInfo::of::<T>());
24 self
25 }
26
27 pub fn into_batch(self, size: u32) -> ColumnBatchBuilder {
29 let mut types = self.types.into_sorted_vec();
30 types.dedup();
31 let fill = TypeIdMap::with_capacity_and_hasher(types.len(), Default::default());
32 let mut arch = Archetype::new(types);
33 arch.reserve(size);
34 ColumnBatchBuilder {
35 fill,
36 target_fill: size,
37 archetype: Some(arch),
38 }
39 }
40}
41
42pub struct ColumnBatchBuilder {
44 fill: TypeIdMap<u32>,
46 target_fill: u32,
47 pub(crate) archetype: Option<Archetype>,
48}
49
50unsafe impl Send for ColumnBatchBuilder {}
51unsafe impl Sync for ColumnBatchBuilder {}
52
53impl ColumnBatchBuilder {
54 pub fn new(ty: ColumnBatchType, size: u32) -> Self {
56 ty.into_batch(size)
57 }
58
59 pub fn writer<T: Component>(&mut self) -> Option<BatchWriter<'_, T>> {
61 let archetype = self.archetype.as_mut().unwrap();
62 let state = archetype.get_state::<T>()?;
63 let base = archetype.get_base::<T>(state);
64 Some(BatchWriter {
65 fill: self.fill.entry(TypeId::of::<T>()).or_insert(0),
66 storage: unsafe {
67 slice::from_raw_parts_mut(base.as_ptr().cast(), self.target_fill as usize)
68 .iter_mut()
69 },
70 })
71 }
72
73 pub fn build(mut self) -> Result<ColumnBatch, BatchIncomplete> {
75 let mut archetype = self.archetype.take().unwrap();
76 if archetype
77 .types()
78 .iter()
79 .any(|ty| self.fill.get(&ty.id()).copied().unwrap_or(0) != self.target_fill)
80 {
81 return Err(BatchIncomplete { _opaque: () });
82 }
83 unsafe {
84 archetype.set_len(self.target_fill);
85 }
86 Ok(ColumnBatch(archetype))
87 }
88}
89
90impl Drop for ColumnBatchBuilder {
91 fn drop(&mut self) {
92 if let Some(archetype) = self.archetype.take() {
93 for ty in archetype.types() {
94 let fill = self.fill.get(&ty.id()).copied().unwrap_or(0);
95 unsafe {
96 let base = archetype.get_dynamic(ty.id(), 0, 0).unwrap();
97 for i in 0..fill {
98 base.as_ptr().add(i as usize).drop_in_place()
99 }
100 }
101 }
102 }
103 }
104}
105
106pub struct ColumnBatch(pub(crate) Archetype);
108
109pub struct BatchWriter<'a, T> {
111 fill: &'a mut u32,
112 storage: core::slice::IterMut<'a, MaybeUninit<T>>,
113}
114
115impl<T> BatchWriter<'_, T> {
116 pub fn push(&mut self, x: T) -> Result<(), T> {
118 match self.storage.next() {
119 None => Err(x),
120 Some(slot) => {
121 *slot = MaybeUninit::new(x);
122 *self.fill += 1;
123 Ok(())
124 }
125 }
126 }
127
128 pub fn fill(&self) -> u32 {
130 *self.fill
131 }
132}
133
134#[derive(Debug, Clone, Eq, PartialEq, Hash)]
136pub struct BatchIncomplete {
137 _opaque: (),
138}
139
140#[cfg(feature = "std")]
141impl std::error::Error for BatchIncomplete {}
142
143impl fmt::Display for BatchIncomplete {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 f.write_str("batch incomplete")
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn empty_batch() {
155 let mut types = ColumnBatchType::new();
156 types.add::<usize>();
157 let mut builder = types.into_batch(0);
158 let mut writer = builder.writer::<usize>().unwrap();
159 assert!(writer.push(42).is_err());
160 }
161}