1use std::mem;
12
13use super::gc::Room;
14
15pub trait Record {
17 fn vacant() -> Self;
19
20 fn extra_bytes(&self) -> usize {
23 0
24 }
25}
26
27const CHECK_LIVE: bool = cfg!(any(debug_assertions, feature = "gc-stress"));
28
29pub const DOUBLING_LIMIT: usize = 32 << 20;
35pub const STEP: usize = 2 << 20;
36
37#[inline]
39pub fn step_bytes(capacity: usize, unit: usize) -> usize {
40 if capacity < DOUBLING_LIMIT {
41 capacity.max(4 * unit)
42 } else {
43 STEP
44 }
45}
46
47pub fn grown_capacity(mut capacity: usize, needed: usize, unit: usize) -> usize {
50 while capacity < needed {
51 capacity += step_bytes(capacity * unit, unit).div_ceil(unit);
52 }
53 capacity
54}
55
56pub struct Arena<T> {
57 records: Vec<T>,
58 live: Vec<u64>,
59 marks: Vec<u64>,
60 free_count: usize,
62 cursor: usize,
64 #[cfg(feature = "gc-stress")]
68 quarantine: Vec<u64>,
69 live_count: usize,
70 extra: usize,
71}
72
73#[inline]
74fn bit(index: u32) -> (usize, u64) {
75 ((index / 64) as usize, 1u64 << (index % 64))
76}
77
78impl<T: Record> Default for Arena<T> {
79 fn default() -> Self {
80 Self::new()
81 }
82}
83
84impl<T: Record> Arena<T> {
85 pub fn new() -> Self {
86 Arena {
87 records: Vec::new(),
88 live: Vec::new(),
89 marks: Vec::new(),
90 free_count: 0,
91 cursor: 0,
92 #[cfg(feature = "gc-stress")]
93 quarantine: Vec::new(),
94 live_count: 0,
95 extra: 0,
96 }
97 }
98
99 #[inline(always)]
102 pub fn alloc(&mut self, value: T, room: &mut Room) -> u32 {
103 self.extra += value.extra_bytes();
104 self.live_count += 1;
105 if self.free_count != 0 {
106 let index = self.take_free();
107 self.records[index as usize] = value;
108 return index;
109 }
110 self.push(value, room)
111 }
112
113 #[inline(always)]
115 fn taken(&self, word: usize) -> u64 {
116 #[cfg(feature = "gc-stress")]
117 return self.live[word] | self.quarantine[word];
118 #[cfg(not(feature = "gc-stress"))]
119 self.live[word]
120 }
121
122 #[inline(always)]
124 fn take_free(&mut self) -> u32 {
125 let mut word = self.cursor;
126 loop {
127 let taken = self.taken(word);
128 if taken != u64::MAX {
129 let low = (!taken).trailing_zeros();
130 self.live[word] |= 1 << low;
131 self.cursor = word;
132 self.free_count -= 1;
133 let index = word as u32 * 64 + low;
134 debug_assert!((index as usize) < self.records.len());
135 return index;
136 }
137 word += 1;
138 }
139 }
140
141 #[inline(never)]
142 fn push(&mut self, value: T, room: &mut Room) -> u32 {
143 let index = u32::try_from(self.records.len()).expect("arena index overflow");
144 if self.records.len() == self.records.capacity() {
145 let before = self.bytes_without_extra();
146 let unit = mem::size_of::<T>().max(1);
147 let old = self.records.capacity();
148 let capacity = grown_capacity(old, old + 1, unit);
149 self.records.reserve_exact(capacity - old);
150 let words = self.records.capacity().div_ceil(64);
151 self.live.reserve_exact(words - self.live.len());
152 self.marks.reserve_exact(words - self.marks.len());
153 #[cfg(feature = "gc-stress")]
154 self.quarantine.reserve_exact(words - self.quarantine.len());
155 room.take(self.bytes_without_extra() - before);
156 }
157 self.records.push(value);
158 let (word, mask) = bit(index);
159 if word == self.live.len() {
160 self.live.push(0);
161 self.marks.push(0);
162 #[cfg(feature = "gc-stress")]
163 self.quarantine.push(0);
164 }
165 self.live[word] |= mask;
166 index
167 }
168
169 pub fn growth_bytes(&self, count: usize) -> usize {
172 let needed = self.records.len() + count.saturating_sub(self.free_count);
174 let old = self.records.capacity();
175 let unit = mem::size_of::<T>().max(1);
176 (grown_capacity(old, needed, unit) - old) * unit
177 }
178
179 #[inline]
180 pub fn is_live(&self, index: u32) -> bool {
181 let (word, mask) = bit(index);
182 self.live.get(word).is_some_and(|bits| bits & mask != 0)
183 }
184
185 #[inline]
186 #[track_caller]
187 fn check(&self, index: u32) {
188 if CHECK_LIVE {
189 assert!(
190 self.is_live(index),
191 "stale {} index {index}",
192 std::any::type_name::<T>()
193 );
194 }
195 }
196
197 #[inline]
198 #[track_caller]
199 pub fn get(&self, index: u32) -> &T {
200 self.check(index);
201 &self.records[index as usize]
202 }
203
204 #[inline]
205 #[track_caller]
206 pub fn get_mut(&mut self, index: u32) -> &mut T {
207 self.check(index);
208 &mut self.records[index as usize]
209 }
210
211 #[track_caller]
213 pub fn free(&mut self, index: u32) -> T {
214 assert!(self.is_live(index), "double free of index {index}");
215 let record = mem::replace(&mut self.records[index as usize], T::vacant());
216 self.extra -= record.extra_bytes();
217 self.live_count -= 1;
218 let (word, mask) = bit(index);
219 self.live[word] &= !mask;
220 self.free_count += 1;
221 self.cursor = self.cursor.min(word);
222 record
223 }
224
225 pub fn adjust_extra(&mut self, old: usize, new: usize) {
227 self.extra = self.extra - old + new;
228 }
229
230 pub fn live_count(&self) -> usize {
231 self.live_count
232 }
233
234 pub fn slot_count(&self) -> usize {
236 self.records.len()
237 }
238
239 fn bytes_without_extra(&self) -> usize {
241 #[allow(unused_mut)]
242 let mut words = self.live.capacity() + self.marks.capacity();
243 #[cfg(feature = "gc-stress")]
244 {
245 words += self.quarantine.capacity();
246 }
247 self.records.capacity() * mem::size_of::<T>() + words * 8
248 }
249
250 pub fn bytes(&self) -> usize {
253 self.bytes_without_extra() + self.extra
254 }
255
256 pub fn used_bytes(&self) -> usize {
258 self.live_count * mem::size_of::<T>() + self.extra
259 }
260
261 #[inline]
263 #[track_caller]
264 pub fn mark(&mut self, index: u32) -> bool {
265 self.check(index);
266 let (word, mask) = bit(index);
267 let bits = &mut self.marks[word];
268 let fresh = *bits & mask == 0;
269 *bits |= mask;
270 fresh
271 }
272
273 #[inline]
274 pub fn is_marked(&self, index: u32) -> bool {
275 let (word, mask) = bit(index);
276 self.marks.get(word).is_some_and(|bits| bits & mask != 0)
277 }
278
279 pub fn clear_marks(&mut self) {
280 self.marks.fill(0);
281 }
282
283 pub fn sweep(&mut self, mut dead: impl FnMut(u32, T)) -> usize {
287 #[cfg(feature = "gc-stress")]
288 self.release_quarantine();
289 let mut freed = 0;
290 let mut lowest = self.live.len();
291 for word in (0..self.live.len()).rev() {
292 let mut garbage = self.live[word] & !self.marks[word];
293 if garbage == 0 {
294 continue;
295 }
296 lowest = word;
297 self.live[word] &= !garbage;
298 #[cfg(feature = "gc-stress")]
299 {
300 self.quarantine[word] = garbage;
301 }
302 while garbage != 0 {
303 let high = 63 - garbage.leading_zeros();
304 garbage &= !(1u64 << high);
305 let index = word as u32 * 64 + high;
306 let record = mem::replace(&mut self.records[index as usize], T::vacant());
307 self.extra -= record.extra_bytes();
308 freed += 1;
309 dead(index, record);
310 }
311 }
312 self.live_count -= freed;
313 if !cfg!(feature = "gc-stress") {
314 self.free_count += freed;
315 self.cursor = self.cursor.min(lowest);
316 }
317 self.shrink();
318 freed
319 }
320
321 #[cfg(feature = "gc-stress")]
323 fn release_quarantine(&mut self) {
324 for (word, bits) in self.quarantine.iter_mut().enumerate() {
325 if *bits != 0 {
326 self.free_count += bits.count_ones() as usize;
327 self.cursor = self.cursor.min(word);
328 *bits = 0;
329 }
330 }
331 }
332
333 fn shrink(&mut self) {
337 let capacity = self.records.capacity();
338 let end = match (0..self.live.len())
339 .rev()
340 .find(|&word| self.taken(word) != 0)
341 {
342 Some(word) => word * 64 + 64 - self.taken(word).leading_zeros() as usize,
343 None => 0,
344 };
345 if (capacity - end) * 4 <= capacity {
346 return;
347 }
348 self.free_count -= self.records.len() - end;
350 let words = end.div_ceil(64);
351 self.records.truncate(end);
352 self.live.truncate(words);
353 self.marks.truncate(words);
354 #[cfg(feature = "gc-stress")]
355 self.quarantine.truncate(words);
356 self.cursor = self.cursor.min(words);
357 let unit = mem::size_of::<T>().max(1);
358 self.records
359 .shrink_to(end + step_bytes(end * unit, unit).div_ceil(unit));
360 let words = self.records.capacity().div_ceil(64);
361 self.live.shrink_to(words);
362 self.marks.shrink_to(words);
363 #[cfg(feature = "gc-stress")]
364 self.quarantine.shrink_to(words);
365 }
366
367 pub fn live_indices(&self) -> impl Iterator<Item = u32> + '_ {
369 self.live.iter().enumerate().flat_map(|(word, &bits)| {
370 let mut bits = bits;
371 std::iter::from_fn(move || {
372 if bits == 0 {
373 return None;
374 }
375 let low = bits.trailing_zeros();
376 bits &= bits - 1;
377 Some(word as u32 * 64 + low)
378 })
379 })
380 }
381
382 pub fn free_indices(&self) -> impl Iterator<Item = u32> + '_ {
384 (0..self.records.len() as u32).filter(|&index| !self.is_live(index))
385 }
386
387 pub fn free_count_matches(&self) -> bool {
389 #[allow(unused_mut)]
390 let mut reusable = self.records.len() - self.live_count;
391 #[cfg(feature = "gc-stress")]
392 {
393 reusable -= self
394 .quarantine
395 .iter()
396 .map(|bits| bits.count_ones() as usize)
397 .sum::<usize>();
398 }
399 let below_cursor = (0..(self.cursor * 64).min(self.records.len()) as u32)
400 .filter(|&index| !self.is_live(index))
401 .count();
402 #[cfg(feature = "gc-stress")]
403 let below_cursor = below_cursor
404 - (0..self.cursor.min(self.quarantine.len()))
405 .map(|word| self.quarantine[word].count_ones() as usize)
406 .sum::<usize>();
407 reusable == self.free_count && below_cursor == 0
408 }
409
410 pub fn marks_equal_live(&self) -> Option<u32> {
412 self.live
413 .iter()
414 .zip(&self.marks)
415 .enumerate()
416 .find_map(|(word, (live, marks))| {
417 let diff = live ^ marks;
418 (diff != 0).then(|| word as u32 * 64 + diff.trailing_zeros())
419 })
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426
427 #[derive(Debug, PartialEq)]
428 struct Item(u32);
429
430 impl Record for Item {
431 fn vacant() -> Self {
432 Item(u32::MAX)
433 }
434 }
435
436 #[test]
437 fn reuse_is_lowest_first_after_sweep() {
438 let mut room = Room::unbounded();
439 let mut arena = Arena::new();
440 for value in 0..10 {
441 assert_eq!(arena.alloc(Item(value), &mut room), value);
442 }
443 for keep in [0, 3, 9] {
444 arena.mark(keep);
445 }
446 let mut dead = Vec::new();
447 assert_eq!(arena.sweep(|index, item| dead.push((index, item.0))), 7);
448 assert_eq!(dead.first(), Some(&(8, 8)));
449 assert_eq!(arena.live_count(), 3);
450 assert_eq!(arena.live_indices().collect::<Vec<_>>(), [0, 3, 9]);
451 arena.clear_marks();
452 #[cfg(feature = "gc-stress")]
453 {
454 for keep in [0, 3, 9] {
456 arena.mark(keep);
457 }
458 assert_eq!(arena.sweep(|_, _| {}), 0);
459 }
460 assert!(arena.free_count_matches());
461 let reused = (0..7)
462 .map(|value| arena.alloc(Item(100 + value), &mut room))
463 .collect::<Vec<_>>();
464 assert_eq!(reused, [1, 2, 4, 5, 6, 7, 8]);
465 assert_eq!(arena.alloc(Item(200), &mut room), 10);
466 arena.free(4);
467 arena.free(2);
468 assert_eq!(arena.alloc(Item(300), &mut room), 2);
469 assert_eq!(arena.alloc(Item(301), &mut room), 4);
470 assert!(arena.free_count_matches());
471 }
472
473 #[test]
474 fn free_poisons_and_growth_is_reported() {
475 let mut room = Room::new(1 << 20, 1 << 20);
476 let mut arena = Arena::new();
477 assert_eq!(arena.growth_bytes(1), 4 * size_of::<Item>());
478 let index = arena.alloc(Item(7), &mut room);
479 assert_eq!(room.threshold(), 1 << 20);
480 assert_eq!(arena.growth_bytes(3), 0);
481 assert_eq!(arena.free(index), Item(7));
482 assert!(!arena.is_live(index));
483 assert_eq!(arena.growth_bytes(4), 0);
484 assert_eq!(arena.growth_bytes(5), 4 * size_of::<Item>());
485 }
486
487 #[test]
488 fn growth_steps_are_bounded() {
489 assert_eq!(step_bytes(0, 24), 96);
490 assert_eq!(step_bytes(4 << 20, 24), 4 << 20);
491 assert_eq!(step_bytes(64 << 20, 24), STEP);
492 let mut room = Room::unbounded();
493 let mut arena = Arena::new();
494 for value in 0..10_000_000 {
495 arena.alloc(Item(value), &mut room);
496 }
497 let capacity = arena.records.capacity() * size_of::<Item>();
498 let used = arena.records.len() * size_of::<Item>();
499 assert!(capacity - used < STEP, "{capacity} for {used}");
500 }
501
502 #[test]
503 fn a_trailing_free_run_is_given_back() {
504 let mut room = Room::unbounded();
505 let mut arena = Arena::new();
506 for value in 0..100_000 {
507 arena.alloc(Item(value), &mut room);
508 }
509 let before = arena.bytes();
510 for keep in 0..1000 {
511 arena.mark(keep);
512 }
513 arena.sweep(|_, _| {});
514 #[cfg(feature = "gc-stress")]
515 {
516 assert_eq!(arena.slot_count(), 100_000);
518 arena.clear_marks();
519 for keep in 0..1000 {
520 arena.mark(keep);
521 }
522 arena.sweep(|_, _| {});
523 }
524 assert_eq!(arena.slot_count(), 1000);
525 assert!(arena.bytes() < before / 4);
526 assert_eq!(arena.alloc(Item(5), &mut room), 1000);
527 }
528
529 #[test]
530 fn a_step_past_the_budget_zeroes_the_threshold() {
531 let mut room = Room::new(1 << 10, usize::MAX);
532 let mut arena = Arena::new();
533 let mut value = 0;
534 while room.threshold() != 0 {
535 assert!(arena.bytes() <= 1 << 10);
536 arena.alloc(Item(value), &mut room);
537 value += 1;
538 }
539 assert!(arena.bytes() > 1 << 10);
540 }
541
542 #[test]
543 #[cfg(any(debug_assertions, feature = "gc-stress"))]
544 #[should_panic(expected = "stale")]
545 fn stale_access_panics() {
546 let mut arena = Arena::new();
547 let index = arena.alloc(Item(1), &mut Room::unbounded());
548 arena.free(index);
549 let _ = arena.get(index);
550 }
551}