1use crate::{
2 EntityAlloc, IndexedID, PtrID, bitalloc::IBitAlloc, chunk::Chunk, gen_index::GenIndex,
3 policy::IAllocPolicy,
4};
5use std::{
6 cell::{BorrowError, Ref},
7 marker::PhantomData,
8};
9
10pub(super) struct IterPos<E, P: IAllocPolicy> {
11 index: u64,
12 nelems_last: usize,
13 nunits_last: usize,
14 _mark: PhantomData<(E, P)>,
15}
16impl<E, P: IAllocPolicy> IterPos<E, P> {
17 pub fn new(nelems_last: usize) -> Self {
18 Self {
19 index: 0,
20 nelems_last,
21 nunits_last: 0,
22 _mark: PhantomData,
23 }
24 }
25
26 pub fn at_end(&self) -> bool {
27 self.nelems_last == 0
28 }
29
30 fn chunk_id(&self) -> u32 {
31 P::chunk(GenIndex(self.index))
32 }
33 fn unit_id(&self) -> u16 {
34 P::unit(GenIndex(self.index))
35 }
36 fn find_valid_chunk<'alloc>(
37 &mut self,
38 chunks: &'alloc [Chunk<E, P>],
39 ) -> Option<&'alloc Chunk<E, P>> {
40 while self.nunits_last == 0 {
41 self.index = self.index.next_multiple_of(P::CHUNK_SIZE as u64);
42 let chunk_id = self.chunk_id() as usize;
43 if chunk_id >= chunks.len() {
44 return None;
45 }
46 let chunk = &chunks[chunk_id];
47 self.nunits_last = chunk.num_allocated as usize;
48 debug_assert_eq!(self.nunits_last, chunk.allocated.count_allocated());
49 }
50 Some(&chunks[self.chunk_id() as usize])
51 }
52 pub fn find_valid_unit(&mut self, chunks: &[Chunk<E, P>]) {
53 let Some(chunk) = self.find_valid_chunk(chunks) else {
55 return;
56 };
57 let unit_id = self.unit_id();
58 let Some(next) = chunk.next_allocated(unit_id) else {
59 unreachable!("find_valid_chunk ensures there is a valid unit");
60 };
61 self.index = {
62 let chunk = chunk.chunk_id as u64;
63 let unit = next as u64;
64 chunk << P::CHUNK_SIZE_LOG2 | unit
65 };
66 }
67 pub fn advance(&mut self, chunks: &[Chunk<E, P>]) {
68 self.index += 1;
69 self.nunits_last -= 1;
70 self.nelems_last -= 1;
71 self.find_valid_unit(chunks);
72 }
73
74 pub fn get_ids(&self, chunks: &[Chunk<E, P>]) -> Option<(IndexedID<E, P>, PtrID<E, P>)> {
75 if self.at_end() {
76 return None;
77 }
78 let chunk_id = self.chunk_id() as usize;
79 let unit_id = self.unit_id() as usize;
80 let chunk = chunks.get(chunk_id)?;
81 let indexed_id = chunk.indexed_id_of_unit(unit_id as u16);
82 let ptr = PtrID::from(&chunk.units[unit_id]);
83 Some((IndexedID::from(indexed_id), ptr))
84 }
85}
86
87pub struct EntityAllocReadIter<'alloc, E, P: IAllocPolicy> {
104 chunks: Ref<'alloc, [Chunk<E, P>]>,
105 pos: IterPos<E, P>,
106}
107impl<'alloc, E, P: IAllocPolicy> Iterator for EntityAllocReadIter<'alloc, E, P> {
108 type Item = (IndexedID<E, P>, PtrID<E, P>, &'alloc E);
109
110 fn next(&mut self) -> Option<Self::Item> {
111 if self.pos.at_end() {
112 return None;
113 }
114 let Some((index, ptr)) = self.pos.get_ids(&self.chunks) else {
115 panic!("IterPos ensures valid pointer when not at end")
116 };
117
118 self.pos.advance(&self.chunks);
120 let e = unsafe { ptr.direct_deref().unwrap() };
121 Some((index, ptr, e))
122 }
123
124 fn size_hint(&self) -> (usize, Option<usize>) {
125 let len = self.pos.nelems_last;
126 (len, Some(len))
127 }
128}
129impl<'alloc, E, P: IAllocPolicy> ExactSizeIterator for EntityAllocReadIter<'alloc, E, P> {
130 fn len(&self) -> usize {
131 self.pos.nelems_last
132 }
133}
134impl<'alloc, E, P: IAllocPolicy> EntityAllocReadIter<'alloc, E, P> {
135 pub fn try_new(alloc: &'alloc EntityAlloc<E, P>) -> Result<Self, BorrowError> {
141 let chunks = alloc.chunks.try_borrow()?;
142 let nelems_last = chunks.iter().map(|c| c.num_allocated as usize).sum();
143 let mut pos = IterPos::new(nelems_last);
144 pos.find_valid_unit(&chunks);
145 Ok(Self {
146 chunks: Ref::map(chunks, |x| x.as_slice()),
147 pos,
148 })
149 }
150 pub fn new(alloc: &'alloc EntityAlloc<E, P>) -> Self {
156 Self::try_new(alloc).expect(
157 "Cannot borrow EntityAlloc for iteration: are you allocating elements while iterating?",
158 )
159 }
160 pub fn current_indexed_id(&self) -> Option<IndexedID<E, P>> {
162 self.pos
163 .get_ids(&self.chunks)
164 .map(|(indexed_id, _)| indexed_id)
165 }
166 pub fn current_chunk_id(&self) -> Option<u32> {
168 if self.pos.at_end() {
169 None
170 } else {
171 Some(self.pos.chunk_id())
172 }
173 }
174 pub fn current_unit_id(&self) -> Option<u16> {
176 if self.pos.at_end() {
177 None
178 } else {
179 Some(self.pos.unit_id())
180 }
181 }
182}
183
184pub struct EntityAllocEditIter<'alloc, E, P: IAllocPolicy> {
201 chunks: &'alloc mut [Chunk<E, P>],
202 pos: IterPos<E, P>,
203}
204impl<'alloc, E, P: IAllocPolicy> Iterator for EntityAllocEditIter<'alloc, E, P> {
205 type Item = (IndexedID<E, P>, PtrID<E, P>, &'alloc mut E);
206
207 fn next(&mut self) -> Option<Self::Item> {
208 if self.pos.at_end() {
209 return None;
210 }
211 let Some((indexed_id, ptr)) = self.pos.get_ids(self.chunks) else {
212 panic!("IterPos ensures valid pointer when not at end")
213 };
214
215 self.pos.advance(self.chunks);
217 let e = unsafe { ptr.direct_deref_mut().unwrap() };
218 Some((indexed_id, ptr, e))
219 }
220 fn size_hint(&self) -> (usize, Option<usize>) {
221 let len = self.pos.nelems_last;
222 (len, Some(len))
223 }
224}
225impl<'alloc, E, P: IAllocPolicy> ExactSizeIterator for EntityAllocEditIter<'alloc, E, P> {
226 fn len(&self) -> usize {
227 self.pos.nelems_last
228 }
229}
230impl<'alloc, E, P: IAllocPolicy> EntityAllocEditIter<'alloc, E, P> {
231 pub fn new(alloc: &'alloc mut EntityAlloc<E, P>) -> Self {
233 let chunks = alloc.chunks.get_mut();
234 let nelems_last = chunks.iter().map(|c| c.num_allocated as usize).sum();
235 let mut pos = IterPos::new(nelems_last);
236 pos.find_valid_unit(chunks);
237 Self { chunks, pos }
238 }
239}
240
241pub struct EntityAllocConsumeIter<E, P: IAllocPolicy> {
257 alloc: EntityAlloc<E, P>,
258 pos: IterPos<E, P>,
259}
260
261impl<E, P: IAllocPolicy> Iterator for EntityAllocConsumeIter<E, P> {
262 type Item = (GenIndex, E);
263
264 fn next(&mut self) -> Option<Self::Item> {
265 let Self { alloc, pos } = self;
266 if pos.at_end() {
267 return None;
268 }
269 let chunks = alloc.chunks.get_mut();
271 pos.advance(chunks);
272 let indexed = match self.pos.get_ids(chunks) {
273 Some((indexed, _)) => indexed.indexed,
274 None => panic!("IterPos ensures valid pointer when not at end"),
275 };
276 let e = alloc
277 .free_gen_index(indexed)
278 .expect("UAF detected: pointer should be valid during consume iteration");
279 Some((indexed, e))
280 }
281
282 fn size_hint(&self) -> (usize, Option<usize>) {
283 let len = self.pos.nelems_last;
284 (len, Some(len))
285 }
286}
287impl<E, P: IAllocPolicy> ExactSizeIterator for EntityAllocConsumeIter<E, P> {
288 fn len(&self) -> usize {
289 self.pos.nelems_last
290 }
291}
292impl<E, P: IAllocPolicy> EntityAllocConsumeIter<E, P> {
293 pub fn new(mut alloc: EntityAlloc<E, P>) -> Self {
295 let nelems_last = alloc.num_allocated.get();
296 let mut pos = IterPos::new(nelems_last);
297 pos.find_valid_unit(alloc.chunks.get_mut());
298 Self { alloc, pos }
299 }
300
301 pub fn release(self) -> EntityAlloc<E, P> {
303 self.alloc
304 }
305}