Skip to main content

mtb_entity_slab/
iter.rs

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        // 先定位到包含下一个元素的有效 chunk,再基于最新的 indexed_id 计算 unit_id
54        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
87/// Immutable iterator over allocated entities in an `EntityAlloc`.
88///
89/// # Yields
90///
91/// Yields tuples of `(IndexedID<E, P>, PtrID<E, P>, &E)`, where:
92/// - `IndexedID<E, P>` is the indexed ID of the entity.
93/// - `PtrID<E, P>` is the pointer ID of the entity.
94/// - `&E` is a reference to the allocated entity itself.
95///
96/// # Borrowing
97///
98/// This iterator borrows the `EntityAlloc` immutably for its entire lifetime.
99/// Attempting to mutate the allocator while this iterator is active will result
100/// in a runtime borrow error.
101///
102/// 不可变迭代器:遍历 `EntityAlloc` 中已分配的实体。
103pub 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        // 准备下一个
119        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    /// Create a new iterator over the given `EntityAlloc`.
136    ///
137    /// # Errors
138    ///
139    /// Returns a `BorrowError` if the `EntityAlloc` is already mutably borrowed.
140    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    /// Create a new iterator over the given `EntityAlloc`.
151    ///
152    /// # Panics
153    ///
154    /// Panics if the `EntityAlloc` is already mutably borrowed.
155    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    /// Get the current `IndexedID` at the iterator's position, if not at end.
161    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    /// Get the current chunk ID at the iterator's position, if not at end.
167    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    /// Get the current unit ID at the iterator's position, if not at end.
175    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
184/// Mutable iterator over allocated entities in an `EntityAlloc`.
185///
186/// # Yields
187///
188/// Yields tuples of `(IndexedID<E, P>, PtrID<E, P>, &mut E)`, where:
189/// - `IndexedID<E, P>` is the indexed ID of the entity.
190/// - `PtrID<E, P>` is the pointer ID of the entity.
191/// - `&mut E` is a mutable reference to the allocated entity itself.
192///
193/// # Borrowing
194///
195/// This iterator borrows the `EntityAlloc` mutably for its entire lifetime.
196/// The entity allocator cannot be accessed in any other way while this
197/// iterator is active. (guarenteed by Rust's mutable borrow rules)
198///
199/// 可变迭代器:遍历 `EntityAlloc` 中已分配的实体。
200pub 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        // 准备下一个
216        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    /// Create a new iterator over the given `EntityAlloc`.
232    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
241/// Consuming iterator over allocated entities in an `EntityAlloc`.
242///
243/// # Yields
244///
245/// Yields tuples of `(GenIndex, E)`, where:
246/// - `GenIndex` is the original generation-indexed ID of the entity.
247/// - `E` is the owned value of the allocated entity itself.
248///
249/// The pointer ID is not yielded since the entity is being consumed.
250///
251/// # Ownership
252///
253/// This iterator takes ownership of the `EntityAlloc` and consumes its entities.
254/// After the iterator is dropped or fully consumed, the allocator can be
255/// retrieved back using the `release()` method.
256pub 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        // 准备下一个
270        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    /// Create a new iterator over the given `EntityAlloc`.
294    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    /// Release the owned `EntityAlloc` after iteration.
302    pub fn release(self) -> EntityAlloc<E, P> {
303        self.alloc
304    }
305}