1use crate::{
2 EntityAlloc, IDProxy, IEntityAllocID, IEntityAllocPolicy, PtrID, alloc::IEntityAllocatable,
3 chunk::Chunk,
4};
5use std::{
6 cell::{BorrowError, Ref},
7 marker::PhantomData,
8 ptr::NonNull,
9};
10
11struct IterPos<E: IEntityAllocatable> {
12 indexed_id: usize,
13 nelems_last: usize,
14 nunits_last: usize,
15 _mark: PhantomData<E>,
16}
17impl<E: IEntityAllocatable> IterPos<E> {
18 fn new(nelems_last: usize) -> Self {
19 Self {
20 indexed_id: 0,
21 nelems_last,
22 nunits_last: 0,
23 _mark: PhantomData::<E>,
24 }
25 }
26}
27
28impl<E: IEntityAllocatable> IterPos<E> {
29 fn chunk_id(&self) -> u32 {
30 (self.indexed_id / E::AllocatePolicyT::CHUNK_SIZE) as u32
31 }
32 fn unit_id(&self) -> u16 {
33 (self.indexed_id % E::AllocatePolicyT::CHUNK_SIZE) as u16
34 }
35
36 fn at_end(&self) -> bool {
37 self.nelems_last == 0
38 }
39 fn find_valid_chunk<'alloc>(
40 &mut self,
41 chunks: &'alloc [Chunk<E, E::AllocatePolicyT>],
42 ) -> Option<&'alloc Chunk<E, E::AllocatePolicyT>> {
43 while self.nunits_last == 0 {
44 self.indexed_id = self
45 .indexed_id
46 .next_multiple_of(E::AllocatePolicyT::CHUNK_SIZE);
47 let chunk_id = self.chunk_id() as usize;
48 if chunk_id >= chunks.len() {
49 return None;
50 }
51 self.nunits_last = chunks[chunk_id].num_allocated as usize;
52 }
53 Some(&chunks[self.chunk_id() as usize])
54 }
55 fn find_valid_unit<'alloc>(&mut self, chunks: &'alloc [Chunk<E, E::AllocatePolicyT>]) {
56 let unit_id = self.unit_id();
57 let Some(chunk) = self.find_valid_chunk(chunks) else {
58 return;
59 };
60 let Some(next) = chunk.next_allocated(unit_id) else {
61 unreachable!("find_valid_chunk ensures there is a valid unit");
62 };
63 self.indexed_id = chunk.indexed_id_of_unit(next);
64 }
65 fn advance<'alloc>(&mut self, chunks: &'alloc [Chunk<E, E::AllocatePolicyT>]) {
66 self.indexed_id += 1;
67 self.nunits_last -= 1;
68 self.nelems_last -= 1;
69 self.find_valid_unit(chunks);
70 }
71
72 fn get_ptr(&self, chunks: &[Chunk<E, E::AllocatePolicyT>]) -> Option<PtrID<E>> {
73 if self.at_end() {
74 return None;
75 }
76 let chunk_id = self.chunk_id() as usize;
77 let unit_id = self.unit_id() as usize;
78 let chunk = chunks.get(chunk_id)?;
79 let ptr = PtrID(NonNull::from_ref(chunk.unit(unit_id as u16)));
80 Some(ptr)
81 }
82}
83
84pub struct EntityAllocReadIter<'alloc, E: IEntityAllocatable> {
85 chunks: Ref<'alloc, [Chunk<E, E::AllocatePolicyT>]>,
86 pos: IterPos<E>,
87}
88
89impl<'alloc, E: IEntityAllocatable> Iterator for EntityAllocReadIter<'alloc, E> {
90 type Item = (usize, E::PtrID, &'alloc E);
91
92 fn next(&mut self) -> Option<Self::Item> {
93 if self.pos.at_end() {
94 return None;
95 }
96 let ptr = self
97 .pos
98 .get_ptr(&self.chunks)
99 .expect("IterPos ensures valid pointer when not at end");
100 let indexed_id = self.pos.indexed_id;
101
102 self.pos.advance(&self.chunks);
104 let e = unsafe { ptr.direct_deref() };
105 Some((indexed_id, E::id_of_ptr(ptr), e))
106 }
107
108 fn size_hint(&self) -> (usize, Option<usize>) {
109 let len = self.pos.nelems_last;
110 (len, Some(len))
111 }
112}
113
114impl<'alloc, E: IEntityAllocatable> ExactSizeIterator for EntityAllocReadIter<'alloc, E> {
115 fn len(&self) -> usize {
116 self.pos.nelems_last
117 }
118}
119
120impl<'alloc, E: IEntityAllocatable> EntityAllocReadIter<'alloc, E> {
121 pub fn try_new(alloc: &'alloc EntityAlloc<E>) -> Result<Self, BorrowError> {
122 let chunks = alloc.chunks.try_borrow()?;
123 let nelems_last = chunks.iter().map(|c| c.num_allocated as usize).sum();
124 let mut pos = IterPos::new(nelems_last);
125 pos.find_valid_unit(&chunks);
126 Ok(Self {
127 chunks: Ref::map(chunks, |x| x.as_slice()),
128 pos,
129 })
130 }
131
132 pub fn new(alloc: &'alloc EntityAlloc<E>) -> Self {
133 Self::try_new(alloc).expect(
134 "Cannot borrow EntityAlloc for iteration: are you allocating elements while iterating?",
135 )
136 }
137
138 pub fn current_indexed_id(&self) -> Option<usize> {
139 if self.pos.at_end() {
140 None
141 } else {
142 Some(self.pos.indexed_id)
143 }
144 }
145 pub fn current_chunk_id(&self) -> Option<u32> {
146 if self.pos.at_end() {
147 None
148 } else {
149 Some(self.pos.chunk_id())
150 }
151 }
152 pub fn current_unit_id(&self) -> Option<u16> {
153 if self.pos.at_end() {
154 None
155 } else {
156 Some(self.pos.unit_id())
157 }
158 }
159}
160
161pub struct EntityAllocEditIter<'alloc, E: IEntityAllocatable> {
162 chunks: &'alloc mut [Chunk<E, E::AllocatePolicyT>],
163 pos: IterPos<E>,
164}
165
166impl<'alloc, E: IEntityAllocatable> Iterator for EntityAllocEditIter<'alloc, E> {
167 type Item = (usize, E::PtrID, &'alloc mut E);
168
169 fn next(&mut self) -> Option<Self::Item> {
170 if self.pos.at_end() {
171 return None;
172 }
173 let ptr = self
174 .pos
175 .get_ptr(&self.chunks)
176 .expect("IterPos ensures valid pointer when not at end");
177 let indexed_id = self.pos.indexed_id;
178 self.pos.advance(&self.chunks);
180 let e = unsafe { ptr.direct_deref_mut() };
181 Some((indexed_id, E::id_of_ptr(ptr), e))
182 }
183
184 fn size_hint(&self) -> (usize, Option<usize>) {
185 let len = self.pos.nelems_last;
186 (len, Some(len))
187 }
188}
189impl<'alloc, E: IEntityAllocatable> ExactSizeIterator for EntityAllocEditIter<'alloc, E> {
190 fn len(&self) -> usize {
191 self.pos.nelems_last
192 }
193}
194impl<'alloc, E: IEntityAllocatable> EntityAllocEditIter<'alloc, E> {
195 pub fn new(alloc: &'alloc mut EntityAlloc<E>) -> Self {
196 let chunks = alloc.chunks.get_mut();
197 let nelems_last = chunks.iter().map(|c| c.num_allocated as usize).sum();
198 let mut pos = IterPos::new(nelems_last);
199 pos.find_valid_unit(&chunks);
200 Self { chunks, pos }
201 }
202}
203
204pub struct EntityAllocConsumeIter<E: IEntityAllocatable> {
205 alloc: EntityAlloc<E>,
206 pos: IterPos<E>,
207}
208
209impl<E: IEntityAllocatable> Iterator for EntityAllocConsumeIter<E> {
210 type Item = (usize, E);
211
212 fn next(&mut self) -> Option<Self::Item> {
213 let Self { alloc, pos } = self;
214 let chunks = alloc.chunks.get_mut();
215 if pos.at_end() {
216 return None;
217 }
218 let ptr = pos
219 .get_ptr(chunks)
220 .expect("IterPos ensures valid pointer when not at end");
221 let indexed_id = pos.indexed_id;
222
223 pos.advance(alloc.chunks.get_mut());
225 let e = ptr
226 .free(alloc)
227 .expect("UAF detected: pointer should be valid during consume iteration");
228 Some((indexed_id, e))
229 }
230
231 fn size_hint(&self) -> (usize, Option<usize>) {
232 let len = self.pos.nelems_last;
233 (len, Some(len))
234 }
235}
236impl<E: IEntityAllocatable> ExactSizeIterator for EntityAllocConsumeIter<E> {
237 fn len(&self) -> usize {
238 self.pos.nelems_last
239 }
240}
241impl<E: IEntityAllocatable> EntityAllocConsumeIter<E> {
242 pub fn new(mut alloc: EntityAlloc<E>) -> Self {
243 let nelems_last = alloc.num_allocated.get();
244 let mut pos = IterPos::new(nelems_last);
245 pos.find_valid_unit(alloc.chunks.get_mut());
246 Self { alloc, pos }
247 }
248
249 pub fn into_alloc(self) -> EntityAlloc<E> {
250 self.alloc
251 }
252}
253
254pub(crate) struct EntitySlotIter<'alloc, E: IEntityAllocatable> {
256 chunks: &'alloc mut [Chunk<E, E::AllocatePolicyT>],
257 pos: IterPos<E>,
258}
259impl<'alloc, E: IEntityAllocatable> Iterator for EntitySlotIter<'alloc, E> {
260 type Item = IDProxy<'alloc, E>;
261
262 fn next(&mut self) -> Option<Self::Item> {
263 if self.pos.at_end() {
264 return None;
265 }
266 let chunk_id = self.pos.chunk_id() as usize;
267 let unit_id = self.pos.unit_id() as usize;
268 self.pos.advance(&self.chunks);
270 let chunk = &mut self.chunks[chunk_id];
271
272 let unit = chunk.unit_mut(unit_id as u16);
273 let unit = unsafe { &mut *(unit as *mut _) };
274 Some(IDProxy { unit })
275 }
276}
277impl<'alloc, E: IEntityAllocatable> EntitySlotIter<'alloc, E> {
278 pub fn new(alloc: &'alloc mut EntityAlloc<E>) -> Self {
279 let chunks = alloc.chunks.get_mut();
280 let nelems_last = chunks.iter().map(|c| c.num_allocated as usize).sum();
281 let mut pos = IterPos::new(nelems_last);
282 pos.find_valid_unit(&chunks);
283 Self { chunks, pos }
284 }
285}