mtb_entity_slab/
alloc.rs

1use crate::{
2    EntityAllocConsumeIter, EntityAllocEditIter, EntityAllocReadIter, PtrID,
3    bitalloc::IBitAlloc,
4    chunk::Chunk,
5    id::IndexedID,
6    policy::{AllocPolicy256, IAllocPolicy},
7};
8use std::cell::{BorrowMutError, Cell, RefCell};
9
10pub struct EntityAlloc<E, P: IAllocPolicy = AllocPolicy256> {
11    pub(crate) chunks: RefCell<Vec<Chunk<E, P>>>,
12    pub(crate) free_head: Cell<u32>,
13    pub(crate) num_allocated: Cell<usize>,
14}
15
16impl<E, P: IAllocPolicy> AsRef<EntityAlloc<E, P>> for EntityAlloc<E, P> {
17    fn as_ref(&self) -> &EntityAlloc<E, P> {
18        self
19    }
20}
21impl<E, P: IAllocPolicy> AsMut<EntityAlloc<E, P>> for EntityAlloc<E, P> {
22    fn as_mut(&mut self) -> &mut EntityAlloc<E, P> {
23        self
24    }
25}
26
27impl<E, P: IAllocPolicy> EntityAlloc<E, P> {
28    pub fn new() -> Self {
29        Self {
30            chunks: RefCell::new(Vec::new()),
31            free_head: Cell::new(0),
32            num_allocated: Cell::new(0),
33        }
34    }
35    pub fn with_capacity(cap: usize) -> Self {
36        let chunks = {
37            let nchunks = cap.next_multiple_of(P::CHUNK_SIZE) / P::CHUNK_SIZE;
38            let mut chunks = Vec::with_capacity(nchunks);
39            for chunk_id in 0..(nchunks as u32) {
40                let chunk = Chunk::new(chunk_id, chunk_id + 1);
41                chunks.push(chunk);
42            }
43            RefCell::new(chunks)
44        };
45        Self {
46            chunks,
47            free_head: Cell::new(0),
48            num_allocated: Cell::new(0),
49        }
50    }
51    pub fn allocate(&self, val: E) -> PtrID<E, P> {
52        match self.try_allocate(val) {
53            Ok(ptr) => ptr,
54            Err(_) => panic!(
55                "Cannot borrow EntityAlloc:{self:p} mutably for allocation. Are you traversing it now?"
56            ),
57        }
58    }
59    pub fn try_allocate(&self, val: E) -> Result<PtrID<E, P>, (BorrowMutError, E)> {
60        let mut chunks = match self.chunks.try_borrow_mut() {
61            Ok(c) => c,
62            Err(e) => return Err((e, val)),
63        };
64        let chunk = Self::find_chunk(self.free_head.get(), &mut chunks);
65        let Ok((ptr, _)) = chunk.allocate(val) else {
66            panic!("Allocation error!");
67        };
68        self.num_allocated.set(self.num_allocated.get() + 1);
69        if chunk.is_full() {
70            self.free_head.set(chunk.free_next);
71        }
72        Ok(unsafe { PtrID::from_raw_ptr(ptr as *mut _) })
73    }
74    /// 下一次分配时, 返回下一个可用的索引
75    pub fn next_indexed(&self) -> IndexedID<E, P> {
76        let chunks = self.chunks.borrow();
77        let free_head = self.free_head.get() as usize;
78        let Some(chunk) = chunks.get(free_head) else {
79            return IndexedID::from(free_head << P::CHUNK_SIZE_LOG2);
80        };
81        let unit_id = chunk.next_available().unwrap();
82        IndexedID::from(chunk.indexed_id_of_unit(unit_id))
83    }
84    fn find_chunk(free: u32, chunks: &mut Vec<Chunk<E, P>>) -> &mut Chunk<E, P> {
85        let free = free as usize;
86        while chunks.len() <= free {
87            let id = chunks.len() as u32;
88            chunks.push(Chunk::new(id, id + 1));
89        }
90        &mut chunks[free]
91    }
92    pub fn free_indexed(&mut self, indexed: usize) -> Option<E> {
93        let chunks = self.chunks.get_mut();
94        let chunk_id = P::chunk_of_indexed_id(indexed);
95        let unit_id = P::unit_of_indexed_id(indexed);
96        let chunk = chunks.get_mut(chunk_id as usize)?;
97        let chunk_full = chunk.is_full();
98        let e = chunk.deallocate_unit_id(unit_id)?;
99        // 如果之前是满的,现在有空位了,加入空闲链表
100        if chunk_full {
101            chunk.free_next = self.free_head.get();
102            self.free_head.set(chunk_id);
103        }
104        self.num_allocated.set(self.num_allocated.get() - 1);
105        Some(e)
106    }
107    pub fn free_ptr(&mut self, ptr: PtrID<E, P>) -> Option<E> {
108        let indexed = ptr.get_index(self)?;
109        self.free_indexed(indexed)
110    }
111
112    pub fn remake_free_chain(&mut self) {
113        let chunks = self.chunks.get_mut();
114        let free_chunks = {
115            let mut free_chunks = Vec::with_capacity(chunks.len());
116            for chunk in chunks.iter_mut() {
117                if !chunk.is_full() {
118                    free_chunks.push(chunk.chunk_id);
119                }
120            }
121            free_chunks
122        };
123        for i in 1..free_chunks.len() {
124            let chunk_id = free_chunks[i - 1];
125            let next_chunk_id = free_chunks[i];
126            let chunk = &mut chunks[chunk_id as usize];
127            chunk.free_next = next_chunk_id;
128        }
129        if let Some(&chunk_id) = free_chunks.first() {
130            self.free_head.set(chunk_id);
131        } else {
132            self.free_head.set(chunks.len() as u32);
133        }
134    }
135}
136
137impl<E, P: IAllocPolicy> EntityAlloc<E, P> {
138    pub fn free_if(&mut self, pred: impl FnMut(&E, PtrID<E, P>, IndexedID<E, P>) -> bool) {
139        self.fully_free_if(pred, drop);
140    }
141    pub fn fully_free_if(
142        &mut self,
143        mut should_free: impl FnMut(&E, PtrID<E, P>, IndexedID<E, P>) -> bool,
144        mut consume: impl FnMut(E),
145    ) {
146        let chunks = self.chunks.get_mut();
147        let mut num_last = self.num_allocated.get();
148        for chunk in chunks.iter_mut() {
149            if chunk.is_empty() {
150                continue;
151            }
152            if num_last == 0 {
153                break;
154            }
155            let chunk_full_previously = chunk.is_full();
156            let chunk_num_allocated = chunk.num_allocated as usize;
157            let num_freed = if chunk_num_allocated * 3 >= P::CHUNK_SIZE {
158                Self::densely_free_chunk(&mut should_free, &mut consume, chunk)
159            } else {
160                Self::sparsely_free_chunk(&mut should_free, &mut consume, chunk)
161            };
162
163            num_last -= chunk_num_allocated;
164            // 维护全局已分配计数
165            if num_freed > 0 {
166                self.num_allocated
167                    .set(self.num_allocated.get().saturating_sub(num_freed));
168            }
169            // 如果之前是满的,现在有空位了,加入空闲链表
170            if chunk_full_previously && !chunk.is_full() {
171                chunk.free_next = self.free_head.get();
172                self.free_head.set(chunk.chunk_id);
173            }
174        }
175    }
176
177    fn densely_free_chunk(
178        should_free: &mut impl FnMut(&E, PtrID<E, P>, IndexedID<E, P>) -> bool,
179        consume: &mut impl FnMut(E),
180        chunk: &mut Chunk<E, P>,
181    ) -> usize {
182        let mut num_freed = 0;
183        let mut count_down = chunk.num_allocated;
184        for unit_id in 0..(P::CHUNK_SIZE as u16) {
185            if count_down == 0 {
186                break;
187            }
188            let index = chunk.indexed_id_of_unit(unit_id);
189            if !chunk.allocated.is_allocated(unit_id) {
190                continue;
191            }
192            count_down -= 1;
193
194            let unit = chunk.unit(unit_id);
195            let ptr: PtrID<E, P> = PtrID::from_ref(unit);
196            let elem = unit.as_init_ref().unwrap();
197            if !should_free(elem, ptr, IndexedID::from(index)) {
198                continue;
199            }
200            let Some(e) = chunk.deallocate_unit_id(unit_id) else {
201                unreachable!("Deallocation failed unexpectedly");
202            };
203            consume(e);
204            num_freed += 1;
205        }
206        num_freed
207    }
208    fn sparsely_free_chunk(
209        should_free: &mut impl FnMut(&E, PtrID<E, P>, IndexedID<E, P>) -> bool,
210        consume: &mut impl FnMut(E),
211        chunk: &mut Chunk<E, P>,
212    ) -> usize {
213        // 逐个枚举当前已分配位,避免全表扫描
214        let mut num_freed = 0usize;
215        let mut next = chunk.allocated.next_allocated(0);
216        while let Some(unit_id) = next {
217            let index = chunk.indexed_id_of_unit(unit_id);
218            let unit = chunk.unit(unit_id);
219            let ptr: PtrID<E, P> = PtrID::from_ref(unit);
220            let elem = unit.as_init_ref().unwrap();
221            if should_free(elem, ptr, IndexedID::from(index)) {
222                if let Some(e) = chunk.deallocate_unit_id(unit_id) {
223                    consume(e);
224                    num_freed += 1;
225                } else {
226                    unreachable!("Deallocation failed unexpectedly");
227                }
228                // 删除了当前位后,从 unit_id+1 继续
229                let next_start = unit_id.saturating_add(1);
230                next = chunk.allocated.next_allocated(next_start);
231            } else {
232                // 未删除则从下一个位置开始
233                let next_start = unit_id.saturating_add(1);
234                next = chunk.allocated.next_allocated(next_start);
235            }
236        }
237        num_freed
238    }
239
240    #[inline]
241    pub fn len(&self) -> usize {
242        self.num_allocated.get()
243    }
244    #[inline]
245    pub fn capacity(&self) -> usize {
246        self.chunks.borrow().len() * P::CHUNK_SIZE
247    }
248    #[inline]
249    pub fn is_empty(&self) -> bool {
250        self.len() == 0
251    }
252
253    pub fn clear(&mut self) {
254        self.chunks.get_mut().clear();
255        self.free_head.set(0);
256        self.num_allocated.set(0);
257    }
258
259    #[inline]
260    pub fn iter(&self) -> EntityAllocReadIter<'_, E, P> {
261        EntityAllocReadIter::new(self)
262    }
263    #[inline]
264    pub fn iter_mut(&mut self) -> EntityAllocEditIter<'_, E, P> {
265        EntityAllocEditIter::new(self)
266    }
267}
268
269impl<'alloc, E, P: IAllocPolicy> IntoIterator for &'alloc EntityAlloc<E, P> {
270    type Item = (usize, PtrID<E, P>, &'alloc E);
271    type IntoIter = EntityAllocReadIter<'alloc, E, P>;
272
273    fn into_iter(self) -> Self::IntoIter {
274        self.iter()
275    }
276}
277impl<'alloc, E, P: IAllocPolicy> IntoIterator for &'alloc mut EntityAlloc<E, P> {
278    type Item = (usize, PtrID<E, P>, &'alloc mut E);
279    type IntoIter = EntityAllocEditIter<'alloc, E, P>;
280
281    fn into_iter(self) -> Self::IntoIter {
282        self.iter_mut()
283    }
284}
285impl<E, P: IAllocPolicy> IntoIterator for EntityAlloc<E, P> {
286    type Item = (usize, E);
287    type IntoIter = EntityAllocConsumeIter<E, P>;
288
289    fn into_iter(self) -> Self::IntoIter {
290        EntityAllocConsumeIter::new(self)
291    }
292}