generational_cache/arena/
mod.rs

1//! Module providing abstractions for a generational arena implemenation.
2//!
3//! ## Usage
4//! ```
5//! #[no_std]
6//!
7//! use generational_cache::prelude::*;
8//!
9//! const CAPACITY: usize = 5;
10//!
11//! let mut arena = Arena::<_, i32>::with_vector(Array::<_, CAPACITY>::new());
12//! let index = arena.insert(78).unwrap(); // allocate new element in arena
13//! let i_ref = arena.get(&index);
14//! assert_eq!(i_ref, Some(&78));
15//! let i_m_ref = arena.get_mut(&index).unwrap();
16//! *i_m_ref = -68418;
17//! assert_eq!(arena.get(&index), Some(&-68418));
18//!
19//! arena.remove(&index).unwrap();
20//!
21//! assert!(arena.get(&index).is_none());
22//! ```
23use crate::vector::Vector;
24use core::{
25    fmt::{self, Debug, Display},
26    marker::PhantomData,
27};
28
29/// A generational counter augemented index to track arena allocation entries.
30#[derive(Clone, Copy, PartialEq, Eq, Debug)]
31pub struct Index {
32    /// Generation counter.
33    pub generation: u64,
34
35    /// Index to the [`Vector`] impl. instance underlying an [`Arena`].
36    pub idx: usize,
37}
38
39/// An allocation entry in a generational arena.
40#[derive(Clone, Copy, PartialEq, Eq, Debug)]
41pub enum Entry<T> {
42    /// An occupied entry containing an allocated value and the associated generation counter.
43    Occupied { value: T, generation: u64 },
44
45    /// Free entry pointing to next free entry in the free list.
46    Free { next_free_idx: Option<usize> },
47
48    /// An unmapped arena entry.
49    Unmapped,
50}
51
52impl<T> Default for Entry<T> {
53    fn default() -> Self {
54        Self::Unmapped
55    }
56}
57
58/// A generational arena for allocating memory based off a vector. Every
59/// entry is associated with a generation counter to uniquely identify
60/// newer allocations from older reclaimed allocations at the same
61/// position in the vector.
62///
63/// This is inspired from the crate
64/// ["generational-arena"](https://docs.rs/generational-arena)
65///
66/// ## Usage
67/// ```
68/// #[no_std]
69///
70/// use generational_cache::prelude::*;
71///
72/// const CAPACITY: usize = 5;
73///
74/// let mut arena = Arena::<_, i32>::with_vector(Array::<_, CAPACITY>::new());
75/// let index = arena.insert(78).unwrap(); // allocate new element in arena
76/// let i_ref = arena.get(&index);
77/// assert_eq!(i_ref, Some(&78));
78/// let i_m_ref = arena.get_mut(&index).unwrap();
79/// *i_m_ref = -68418;
80/// assert_eq!(arena.get(&index), Some(&-68418));
81///
82/// arena.remove(&index).unwrap();
83///
84/// assert!(arena.get(&index).is_none());
85/// ```
86pub struct Arena<V, T> {
87    entries_vec: V,
88    generation: u64,
89    free_list_head: Option<usize>,
90
91    len: usize,
92    capacity: usize,
93
94    _phantom_type: PhantomData<T>,
95}
96
97/// Error type associated with arena operations.
98#[derive(Debug)]
99pub enum ArenaError<VE> {
100    /// Used on inserts on a maxed out [`Arena`] which is out of memory.
101    OutOfMemory,
102
103    /// Used when referencing items in an [`Arena`] with an invalid [`Index`].
104    InvalidIdx,
105
106    /// Used when there is an error in the underlying [`Vector`]
107    /// implemenation instance.
108    VectorError(VE),
109}
110
111impl<VE> Display for ArenaError<VE>
112where
113    VE: Debug,
114{
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        write!(f, "{self:?}")
117    }
118}
119
120/// A generational arena.
121impl<V, T> Arena<V, T>
122where
123    V: Vector<Entry<T>>,
124{
125    /// Reserves space for the given number of additional items in this arena.
126    pub fn reserve(&mut self, additional: usize) -> Result<(), ArenaError<V::Error>> {
127        let reserve_start = self.entries_vec.len();
128        let old_head = self.free_list_head;
129
130        self.entries_vec
131            .reserve(additional)
132            .map_err(ArenaError::VectorError)?;
133
134        for i in 0..additional {
135            let free_idx = i + reserve_start;
136
137            let next_free_idx = if i < additional - 1 {
138                Some(free_idx + 1)
139            } else {
140                old_head
141            };
142
143            let free_entry = Entry::Free { next_free_idx };
144            self.entries_vec
145                .push(free_entry)
146                .map_err(ArenaError::VectorError)?;
147        }
148
149        self.free_list_head = Some(reserve_start);
150
151        self.capacity += additional;
152
153        Ok(())
154    }
155
156    /// Removes all items from this arena and reclaims all allocated memory.
157    pub fn clear(&mut self) -> Result<(), ArenaError<V::Error>> {
158        self.free_list_head = Some(0);
159        self.generation = 0;
160        self.len = 0;
161
162        self.entries_vec.clear();
163
164        let capacity = self.capacity();
165
166        for i in 0..capacity {
167            let next_free_idx = i + 1;
168            let next_free_idx = if next_free_idx < capacity {
169                Some(next_free_idx)
170            } else {
171                None
172            };
173
174            let free_entry = Entry::Free { next_free_idx };
175            self.entries_vec
176                .push(free_entry)
177                .map_err(ArenaError::VectorError)?;
178        }
179
180        Ok(())
181    }
182
183    /// Creates an [`Arena`] instance with the given [`Vector`]
184    /// implemenation instance as the backing memory.
185    pub fn with_vector(vector: V) -> Self {
186        let capacity = vector.capacity();
187
188        let mut arena = Self {
189            entries_vec: vector,
190            generation: 0,
191            free_list_head: Some(0),
192            len: 0,
193            capacity,
194            _phantom_type: PhantomData,
195        };
196
197        arena.clear().unwrap();
198
199        arena
200    }
201
202    /// Allocates space for the given items and inserts it into this arena.
203    pub fn insert(&mut self, item: T) -> Result<Index, ArenaError<V::Error>> {
204        let old_free = self.free_list_head.ok_or(ArenaError::OutOfMemory)?;
205
206        self.free_list_head = self
207            .entries_vec
208            .get(old_free)
209            .map(|x| match x {
210                Entry::Free { next_free_idx } => *next_free_idx,
211                _ => None,
212            })
213            .ok_or(ArenaError::InvalidIdx)?;
214
215        let entry = Entry::Occupied {
216            value: item,
217            generation: self.generation,
218        };
219
220        *self
221            .entries_vec
222            .get_mut(old_free)
223            .ok_or(ArenaError::InvalidIdx)? = entry;
224        self.generation += 1;
225
226        self.len += 1;
227
228        Ok(Index {
229            generation: self.generation - 1,
230            idx: old_free,
231        })
232    }
233
234    /// Reclaims the allocated space for the item at the given index and
235    /// removes it from the arena.
236    pub fn remove(&mut self, index: &Index) -> Option<T> {
237        match self.entries_vec.get(index.idx) {
238            Some(Entry::Occupied {
239                value: _,
240                generation,
241            }) if &index.generation == generation => {
242                let new_free_list_head_entry = Entry::<T>::Free {
243                    next_free_idx: self.free_list_head,
244                };
245
246                let old_entry = core::mem::replace(
247                    self.entries_vec.get_mut(index.idx)?,
248                    new_free_list_head_entry,
249                );
250
251                self.free_list_head = Some(index.idx);
252
253                self.len -= 1;
254
255                Some(old_entry)
256            }
257            _ => None,
258        }
259        .and_then(|x| match x {
260            Entry::Occupied {
261                value,
262                generation: _,
263            } => Some(value),
264            _ => None,
265        })
266    }
267
268    /// Returns a mutable reference to the allocated items referenced by the given [`Index`].
269    pub fn get_mut(&mut self, index: &Index) -> Option<&mut T> {
270        match self.entries_vec.get_mut(index.idx) {
271            Some(Entry::Occupied { value, generation }) if &index.generation == generation => {
272                Some(value)
273            }
274            _ => None,
275        }
276    }
277
278    /// Returns an immutable reference to the allocated items referenced by the given [`Index`].
279    pub fn get(&self, index: &Index) -> Option<&T> {
280        match self.entries_vec.get(index.idx) {
281            Some(Entry::Occupied { value, generation }) if &index.generation == generation => {
282                Some(value)
283            }
284            _ => None,
285        }
286    }
287
288    /// Returns the number of elements this [`Arena`] is capable of allocating.
289    pub fn capacity(&self) -> usize {
290        self.capacity
291    }
292
293    /// Returns whether this [`Arena`] is empty.
294    pub fn is_empty(&self) -> bool {
295        self.len == 0
296    }
297
298    /// Returns the number of elements allocated in this [`Arena`].
299    pub fn len(&self) -> usize {
300        self.len
301    }
302}
303
304#[doc(hidden)]
305pub mod tests {
306    use super::{Arena, Entry, Index, Vector};
307    use core::{cmp::PartialEq, fmt::Debug};
308
309    pub fn _test_arena_free_entries_init<T, V>(mut arena: Arena<V, T>)
310    where
311        V: Vector<Entry<T>>,
312        T: Debug + PartialEq,
313    {
314        arena.clear().unwrap();
315
316        assert_eq!(arena.free_list_head, Some(0));
317
318        let capacity = arena.capacity();
319
320        for i in 0..capacity {
321            let entry = &arena.entries_vec[i];
322
323            if i == capacity - 1 {
324                assert_eq!(
325                    entry,
326                    &Entry::Free {
327                        next_free_idx: None
328                    }
329                )
330            } else {
331                assert_eq!(
332                    entry,
333                    &Entry::Free {
334                        next_free_idx: Some(i + 1)
335                    }
336                )
337            };
338        }
339    }
340
341    pub fn _test_arena_reserve<T, V>(mut arena: Arena<V, T>)
342    where
343        V: Vector<Entry<T>>,
344        T: Debug + PartialEq,
345    {
346        arena.clear().unwrap();
347
348        let old_capacity = arena.capacity();
349
350        const ADDITIONAL: usize = 5;
351
352        let result = arena.reserve(ADDITIONAL);
353
354        if result.is_err() {
355            return;
356        }
357
358        assert_eq!(arena.free_list_head, Some(old_capacity));
359
360        let capacity = arena.capacity();
361
362        for i in 0..capacity {
363            let entry = &arena.entries_vec[i];
364
365            if i == capacity - 1 {
366                assert_eq!(
367                    entry,
368                    &Entry::Free {
369                        next_free_idx: Some(0)
370                    }
371                )
372            } else if i == old_capacity - 1 {
373                assert_eq!(
374                    entry,
375                    &Entry::Free {
376                        next_free_idx: None
377                    }
378                )
379            } else {
380                assert_eq!(
381                    entry,
382                    &Entry::Free {
383                        next_free_idx: Some(i + 1)
384                    }
385                )
386            };
387        }
388    }
389
390    pub fn _test_arena_insert<V>(mut arena: Arena<V, i32>)
391    where
392        V: Vector<Entry<i32>>,
393    {
394        assert!(
395            arena.capacity() >= 2,
396            "Test not valid for arena with capacity < 2"
397        );
398
399        arena.clear().unwrap();
400
401        let index_0 = arena.insert(0);
402        assert_eq!(
403            index_0.as_ref().unwrap(),
404            &Index {
405                generation: 0,
406                idx: 0
407            }
408        );
409
410        let index_1 = arena.insert(1);
411        assert_eq!(
412            index_1.as_ref().unwrap(),
413            &Index {
414                generation: 1,
415                idx: 1
416            }
417        );
418
419        let index_0_val = index_0.as_ref().unwrap();
420        let item_0 = arena.get(index_0_val);
421        assert_eq!(item_0, Some(&0));
422
423        let index_1_val = index_1.unwrap();
424        let item_1 = arena.get(&index_1_val);
425        assert_eq!(item_1, Some(&1));
426
427        let item_0 = arena.get_mut(index_0_val);
428        assert_eq!(item_0, Some(&mut 0));
429        let item_0 = item_0.unwrap();
430        *item_0 = 25;
431
432        let item_0 = arena.get(index_0_val);
433        assert_eq!(item_0, Some(&25));
434
435        let item_1 = arena.get_mut(&index_1_val);
436        assert_eq!(item_1, Some(&mut 1));
437        let item_1 = item_1.unwrap();
438        *item_1 = -78;
439
440        let item_1 = arena.get(&index_1_val);
441        assert_eq!(item_1, Some(&-78));
442
443        let last_len = arena.len();
444
445        let remaining = arena.capacity() - last_len;
446
447        for i in 0..remaining {
448            let possible_idx = last_len + i;
449
450            assert_eq!(
451                arena.insert(0).unwrap(),
452                Index {
453                    generation: possible_idx as u64,
454                    idx: possible_idx
455                }
456            )
457        }
458
459        const ADDITIONAL: usize = 5;
460
461        let result = arena.reserve(ADDITIONAL);
462
463        if result.is_ok() {
464            for _ in 0..ADDITIONAL {
465                arena.insert(0).unwrap();
466            }
467        }
468
469        arena.clear().unwrap();
470
471        assert!(arena.is_empty());
472    }
473
474    pub fn _test_arena_remove<V>(mut arena: Arena<V, i32>)
475    where
476        V: Vector<Entry<i32>>,
477    {
478        assert!(
479            arena.capacity() >= 2,
480            "Test not valid for arena with capacity < 2"
481        );
482
483        arena.clear().unwrap();
484
485        assert_eq!(arena.free_list_head.unwrap(), 0);
486
487        let index = arena.insert(0).unwrap();
488        assert_eq!(arena.get(&index), Some(&0));
489        assert_eq!(
490            index,
491            Index {
492                generation: 0,
493                idx: 0
494            }
495        );
496
497        assert_eq!(arena.free_list_head.unwrap(), 1);
498
499        assert_eq!(arena.remove(&index).unwrap(), 0);
500        assert_eq!(arena.get(&index), None);
501
502        assert_eq!(arena.free_list_head.unwrap(), 0);
503
504        let index = arena.insert(0).unwrap();
505        assert_eq!(arena.get(&index), Some(&0));
506        assert_eq!(
507            index,
508            Index {
509                generation: 1,
510                idx: 0
511            }
512        );
513
514        assert_eq!(arena.free_list_head.unwrap(), 1);
515
516        let last_arena_len = arena.len();
517        let remaining = arena.capacity() - last_arena_len;
518
519        let current_generation = index.generation + 1;
520
521        for i in 0..remaining {
522            let index = arena.insert(i as i32).unwrap();
523            assert_eq!(
524                index,
525                Index {
526                    generation: current_generation + i as u64,
527                    idx: last_arena_len + i
528                }
529            );
530        }
531
532        // remove elements at odd indices
533        let mut i = 1;
534        let mut removed_count = 0;
535        while i < arena.capacity() {
536            arena
537                .remove(&Index {
538                    generation: i as u64 + 1,
539                    idx: i,
540                })
541                .unwrap();
542
543            i += 2;
544            removed_count += 1;
545        }
546
547        // iterate through free list
548        let mut free_position_count = 0;
549        let mut free_idx = arena.free_list_head;
550
551        while let Some(next_free) = free_idx {
552            assert_eq!(next_free & 1, 1);
553            free_idx = match arena.entries_vec[next_free] {
554                Entry::Free { next_free_idx } => next_free_idx,
555                _ => None,
556            };
557            free_position_count += 1;
558        }
559
560        assert_eq!(removed_count, free_position_count);
561
562        arena.clear().unwrap();
563
564        assert!(arena.is_empty());
565    }
566}