1use std::{
2 cell::{BorrowError, Ref},
3 marker::PhantomData,
4};
5
6use crate::{EntityAlloc, PtrID, bitalloc::IBitAlloc, chunk::Chunk, policy::IAllocPolicy};
7
8pub(super) struct IterPos<E, P: IAllocPolicy> {
9 pub indexed_id: usize,
10 nelems_last: usize,
11 nunits_last: usize,
12 _mark: PhantomData<(E, P)>,
13}
14impl<E, P: IAllocPolicy> IterPos<E, P> {
15 pub fn new(nelems_last: usize) -> Self {
16 Self {
17 indexed_id: 0,
18 nelems_last,
19 nunits_last: 0,
20 _mark: PhantomData::<(E, P)>,
21 }
22 }
23
24 pub fn at_end(&self) -> bool {
25 self.nelems_last == 0
26 }
27
28 fn chunk_id(&self) -> u32 {
29 (self.indexed_id / P::CHUNK_SIZE) as u32
30 }
31 fn unit_id(&self) -> u16 {
32 (self.indexed_id % P::CHUNK_SIZE) as u16
33 }
34 fn find_valid_chunk<'alloc>(
35 &mut self,
36 chunks: &'alloc [Chunk<E, P>],
37 ) -> Option<&'alloc Chunk<E, P>> {
38 while self.nunits_last == 0 {
39 self.indexed_id = self.indexed_id.next_multiple_of(P::CHUNK_SIZE);
40 let chunk_id = self.chunk_id() as usize;
41 if chunk_id >= chunks.len() {
42 return None;
43 }
44 self.nunits_last = chunks[chunk_id].num_allocated as usize;
45 assert_eq!(
46 self.nunits_last,
47 chunks[chunk_id].allocated.count_allocated()
48 );
49 }
50 Some(&chunks[self.chunk_id() as usize])
51 }
52 pub fn find_valid_unit<'alloc>(&mut self, chunks: &'alloc [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.indexed_id = chunk.indexed_id_of_unit(next);
62 }
63 pub fn advance<'alloc>(&mut self, chunks: &'alloc [Chunk<E, P>]) {
64 self.indexed_id += 1;
65 self.nunits_last -= 1;
66 self.nelems_last -= 1;
67 self.find_valid_unit(chunks);
68 }
69
70 pub fn get_ptr(&self, chunks: &[Chunk<E, P>]) -> Option<PtrID<E, P>> {
71 if self.at_end() {
72 return None;
73 }
74 let chunk_id = self.chunk_id() as usize;
75 let unit_id = self.unit_id() as usize;
76 let chunk = chunks.get(chunk_id)?;
77 let ptr = PtrID::from_ref(&chunk.units[unit_id]);
78 Some(ptr)
79 }
80}
81
82pub struct EntityAllocReadIter<'alloc, E, P: IAllocPolicy> {
83 chunks: Ref<'alloc, [Chunk<E, P>]>,
84 pos: IterPos<E, P>,
85}
86impl<'alloc, E, P: IAllocPolicy> Iterator for EntityAllocReadIter<'alloc, E, P> {
87 type Item = (usize, PtrID<E, P>, &'alloc E);
88
89 fn next(&mut self) -> Option<Self::Item> {
90 if self.pos.at_end() {
91 return None;
92 }
93 let ptr = self
94 .pos
95 .get_ptr(&self.chunks)
96 .expect("IterPos ensures valid pointer when not at end");
97 let indexed_id = self.pos.indexed_id;
98
99 self.pos.advance(&self.chunks);
101 let e = unsafe { ptr.direct_deref() };
102 Some((indexed_id, ptr, e))
103 }
104
105 fn size_hint(&self) -> (usize, Option<usize>) {
106 let len = self.pos.nelems_last;
107 (len, Some(len))
108 }
109}
110impl<'alloc, E, P: IAllocPolicy> ExactSizeIterator for EntityAllocReadIter<'alloc, E, P> {
111 fn len(&self) -> usize {
112 self.pos.nelems_last
113 }
114}
115impl<'alloc, E, P: IAllocPolicy> EntityAllocReadIter<'alloc, E, P> {
116 pub fn try_new(alloc: &'alloc EntityAlloc<E, P>) -> Result<Self, BorrowError> {
117 let chunks = alloc.chunks.try_borrow()?;
118 let nelems_last = chunks.iter().map(|c| c.num_allocated as usize).sum();
119 let mut pos = IterPos::new(nelems_last);
120 pos.find_valid_unit(&chunks);
121 Ok(Self {
122 chunks: Ref::map(chunks, |x| x.as_slice()),
123 pos,
124 })
125 }
126 pub fn new(alloc: &'alloc EntityAlloc<E, P>) -> Self {
127 Self::try_new(alloc).expect(
128 "Cannot borrow EntityAlloc for iteration: are you allocating elements while iterating?",
129 )
130 }
131 pub fn current_indexed_id(&self) -> Option<usize> {
132 if self.pos.at_end() {
133 None
134 } else {
135 Some(self.pos.indexed_id)
136 }
137 }
138 pub fn current_chunk_id(&self) -> Option<u32> {
139 if self.pos.at_end() {
140 None
141 } else {
142 Some(self.pos.chunk_id())
143 }
144 }
145 pub fn current_unit_id(&self) -> Option<u16> {
146 if self.pos.at_end() {
147 None
148 } else {
149 Some(self.pos.unit_id())
150 }
151 }
152}
153
154pub struct EntityAllocEditIter<'alloc, E, P: IAllocPolicy> {
155 chunks: &'alloc mut [Chunk<E, P>],
156 pos: IterPos<E, P>,
157}
158impl<'alloc, E, P: IAllocPolicy> Iterator for EntityAllocEditIter<'alloc, E, P> {
159 type Item = (usize, PtrID<E, P>, &'alloc mut E);
160
161 fn next(&mut self) -> Option<Self::Item> {
162 if self.pos.at_end() {
163 return None;
164 }
165 let ptr = self
166 .pos
167 .get_ptr(&self.chunks)
168 .expect("IterPos ensures valid pointer when not at end");
169 let indexed_id = self.pos.indexed_id;
170
171 self.pos.advance(&self.chunks);
173 let e = unsafe { ptr.direct_deref_mut() };
174 Some((indexed_id, ptr, e))
175 }
176 fn size_hint(&self) -> (usize, Option<usize>) {
177 let len = self.pos.nelems_last;
178 (len, Some(len))
179 }
180}
181impl<'alloc, E, P: IAllocPolicy> ExactSizeIterator for EntityAllocEditIter<'alloc, E, P> {
182 fn len(&self) -> usize {
183 self.pos.nelems_last
184 }
185}
186impl<'alloc, E, P: IAllocPolicy> EntityAllocEditIter<'alloc, E, P> {
187 pub fn new(alloc: &'alloc mut EntityAlloc<E, P>) -> Self {
188 let chunks = alloc.chunks.get_mut();
189 let nelems_last = chunks.iter().map(|c| c.num_allocated as usize).sum();
190 let mut pos = IterPos::new(nelems_last);
191 pos.find_valid_unit(&chunks);
192 Self { chunks, pos }
193 }
194}
195
196pub struct EntityAllocConsumeIter<E, P: IAllocPolicy> {
197 alloc: EntityAlloc<E, P>,
198 pos: IterPos<E, P>,
199}
200
201impl<E, P: IAllocPolicy> Iterator for EntityAllocConsumeIter<E, P> {
202 type Item = (usize, E);
203
204 fn next(&mut self) -> Option<Self::Item> {
205 let Self { alloc, pos } = self;
206 if pos.at_end() {
207 return None;
208 }
209 let indexed_id = pos.indexed_id;
210 pos.advance(alloc.chunks.get_mut());
212 let e = alloc
213 .free_indexed(indexed_id)
214 .expect("UAF detected: pointer should be valid during consume iteration");
215 Some((indexed_id, e))
216 }
217
218 fn size_hint(&self) -> (usize, Option<usize>) {
219 let len = self.pos.nelems_last;
220 (len, Some(len))
221 }
222}
223impl<E, P: IAllocPolicy> ExactSizeIterator for EntityAllocConsumeIter<E, P> {
224 fn len(&self) -> usize {
225 self.pos.nelems_last
226 }
227}
228impl<E, P: IAllocPolicy> EntityAllocConsumeIter<E, P> {
229 pub fn new(mut alloc: EntityAlloc<E, P>) -> Self {
230 let nelems_last = alloc.num_allocated.get();
231 let mut pos = IterPos::new(nelems_last);
232 pos.find_valid_unit(alloc.chunks.get_mut());
233 Self { alloc, pos }
234 }
235
236 pub fn release(self) -> EntityAlloc<E, P> {
237 self.alloc
238 }
239}