ref_arena/
lib.rs

1#![no_std]
2//! A `no_std` (alloc) arena that acts similarly to a `Slab<Rc<T>>` but with
3//! better performance and less features.
4//!
5//! This arena stores reference counts with the objects in
6//! a continuous buffer, which decreases the need for
7//! multiple small memory allocations. References can
8//! be cloned and act like normal `Rc`s. When all references
9//! to an object is dropped, the space is made available
10//! for a new object, and if the arena is dropped, the
11//! underlying buffer may also be dropped.
12//!
13//! This arena features constant time inserts, derefs,
14//! and drops while allocating less often, decreasing
15//! memory fragmentation, having increased performance
16//! (depending on the allocator), and potentially using
17//! less memory.
18//!
19//! `RefArena` does not support Weak references and probably
20//! will not in the indefinite future.
21//!
22//! This library uses a decent amount of unsafe code, and is
23//! partially tested with miri. It should be safe since the code
24//! isn't too complex, but beware of bugs.
25//!
26//! # Example
27//!
28//! ```
29//! use ref_arena::{RefArena, RcRef};
30//!
31//! let mut arena: RefArena<i32> = RefArena::new();
32//!
33//! // Create a reference
34//! let reference: RcRef<i32> = arena.insert(5);
35//!
36//! // Deref to get the inner value
37//! let inner = *reference;
38//! assert_eq!(inner, 5);
39//!
40//! // We can create clones of the reference just like an Rc!
41//! let clone = reference.clone();
42//! assert_eq!(*clone, 5);
43//!
44//! // References can be held even after the arena is dropped.
45//! drop(arena);
46//! assert_eq!(*reference, 5);
47//!
48//! // Objects (and internal buffers) are dropped when
49//! // all references are dropped.
50//! drop(reference);
51//! drop(clone);
52//! ```
53//!
54//! # Benchmarks
55//!
56//! Note: These benchmarks are very specific and
57//! cater to the creation of lots of small objects.
58//!
59//! Additionally these benchmarks may vary
60//! system-to-system and allocator-to-allocator.
61//!
62//! Allocating 10k `Rc`s:
63//! ```text
64//! std::rc::Rc   allocate 10_000  247.59 μs
65//! RefArena      allocate 10_000  48.57 μs
66//!
67//! ~5x speedup
68//! ```
69//!
70//! Dereferencing 10k `Rc`s:
71//! ```text
72//! std::rc::Rc   deref 10_000     4.97 μs
73//! RefArena      deref 10_000     4.86 μs
74//!
75//! no speedup
76//! ```
77//!
78//! Dereferencing should be about the same within both since
79//! it's a simple pointer dereference.
80//!
81//! Dropping 10k `Rc`s:
82//! ```text
83//! std::rc::Rc   drop 10_000      134.35 μs
84//! RefArena      drop 10_000      29.06 μs
85//!
86//! ~4.62x speedup
87//! ```
88//!
89//! Reallocating 10k `Rc`s:
90//! ```text
91//! RefArena      realloc 10_000   45.62 μs
92//! ```
93//!
94//! In this case 10k `RcRef`s were allocated and dropped, and we measured
95//! the time it took to put 10k objects back onto the arena.
96//! (Compare to allocate)
97//!
98//! # Comparison to `rc_arena`
99//!
100//! [`rc_arena`](https://github.com/ebfull/rc_arena) is similar to `ref_arena`
101//! in that they are arenas that return reference counted objects.
102//! Both contain inner buffers that hold contiguous lists of objects.
103//!
104//! The main difference between the two is that `rc_arena` does not
105//! individually count objects. When all references of an object are
106//! dropped in `ref_arena`, the inner object is dropped and the space
107//! is made available for a new insertion (similar to `slab` and
108//! `stable-vec`), whereas in `rc_arena` the space is never made available again.
109//!
110//! `rc_arena` is useful if you have a determinite amount of objects
111//! that need to be reference counted, where `ref_arena` is useful
112//! when you frequently create and drop objects.
113//!
114//! Note this comparison might not be 100% correct as it's just
115//! what I could tell from looking at the code and documentation.
116//! Additionally this crate was not made with `rc_arena` in mind.
117//!
118
119extern crate alloc;
120
121use alloc::{
122    boxed::Box,
123    rc::{Rc, Weak},
124    vec::Vec,
125};
126use core::{
127    cell::UnsafeCell,
128    fmt::{Debug, Display},
129    mem::MaybeUninit, ptr::NonNull,
130};
131use core::{
132    cmp::Ordering,
133    fmt,
134    hash::{Hash, Hasher},
135    ops,
136};
137
138type RcItem<T> = (UnsafeCell<MaybeUninit<T>>, UnsafeCell<usize>);
139
140// Starts at 8 and doubles every time.
141fn get_buffer_size(buffer_num: u32) -> usize {
142    2_usize.pow(3 + buffer_num)
143}
144
145struct InnerArena<T> {
146    // Keep weak reference to vacant so RcRefs can push
147    // when dropped. Weak since if the RefArena is dropped
148    // we won't be allocating any more.
149    vacant: Weak<UnsafeCell<Vec<(usize, usize)>>>,
150    // The index of the buffer in RefArena.inner
151    // Used for inserting dropped indexes into vacant.
152    buffer_index: usize,
153    // Wish I could get rid of the arena since InnerArena is
154    // already allocated in an Rc.
155    arena: Box<[RcItem<T>]>,
156}
157
158/// An arena that holds reference counted values.
159///
160/// A `RefArena` essentially acts as a `Slab<Rc<T>>` but
161/// is much more faster and memory efficient since reference
162/// counting is stored inline with the objects rather than
163/// in separate allocations.
164///
165/// # Internals
166///
167/// Internally the objects are stored in buffers that get
168/// bigger as you store more objects, similar to a `Vec`. The
169/// key different here is that the `Vec`s are never reallocated,
170/// which allows stable references to be held for the [`RcRef`]s
171///
172/// Buffers start at 8 objects and exponentially increase by
173/// powers of two depending on how many objects are in the arena.
174///
175/// # Notes
176///
177/// Objects can't be removed or indexed once they are in the arena.
178/// Instead, they are dropped when all references are dropped, similar
179/// to an Rc, and are accessed through the reference.
180///
181/// # Example
182///
183/// ```
184/// use ref_arena::{RefArena, RcRef};
185///
186/// let mut arena: RefArena<i32> = RefArena::new();
187///
188/// // Create a reference
189/// let reference: RcRef<i32> = arena.insert(5);
190///
191/// // Deref to get the inner value
192/// let inner = *reference;
193/// assert_eq!(inner, 5);
194///
195/// // References can be held even after the arena is dropped.
196/// drop(arena);
197///
198/// assert_eq!(*reference, 5);
199///
200/// // Objects (and internal buffers) are dropped when
201/// // all references are dropped.
202/// drop(reference);
203/// ```
204pub struct RefArena<T> {
205    // Use MaybeUninit and track Someness with references
206    // (0 references == None)
207    // Using Option requires T to have clone in some cases.
208    inner: Vec<Rc<InnerArena<T>>>,
209    // Stores indexes for indexes previously used by an RcRef,
210    // but since dropped and "removed".
211    vacant: Rc<UnsafeCell<Vec<(usize, usize)>>>,
212    // The "length" of the last allocated buffer. We use this
213    // so we don't need to populate vacant when we allocate.
214    last_len: usize,
215}
216
217impl<T> RefArena<T> {
218    /// Creates a new `RefArena`
219    pub fn new() -> RefArena<T> {
220        RefArena {
221            inner: Vec::new(),
222            vacant: Rc::new(UnsafeCell::new(Vec::new())),
223            last_len: 0,
224        }
225    }
226
227    fn allocate_new_buffer(&mut self) {
228        let size = get_buffer_size(self.inner.len() as u32);
229
230        // Eliminate some checks in Vec::with_capacity()
231        // size_of(RcItem::<T>) can never be 0 since RcItem contains a usize.
232        // checked at compile time, optimizations remove the assertion.
233        assert!(core::mem::size_of::<RcItem::<T>>() != 0);
234
235        // Can never be false unless it overflows usize, at which point we're screwed anyways
236        debug_assert!(size > 0);
237        match size > 0 {
238            true => (),
239            false => unsafe { core::hint::unreachable_unchecked() }
240        }
241
242        let mut v = Vec::<RcItem<T>>::with_capacity(size);
243        unsafe {
244            for i in 0..size {
245                v.as_mut_ptr()
246                    .add(i)
247                    .write((UnsafeCell::new(MaybeUninit::uninit()), UnsafeCell::new(0)));
248            }
249            v.set_len(size);
250        }
251
252        let inner = InnerArena {
253            vacant: Rc::downgrade(&self.vacant),
254            buffer_index: self.inner.len(),
255            arena: v.into_boxed_slice(),
256        };
257
258        self.inner.push(Rc::new(inner));
259    }
260
261    /// Inserts an item `T` into the `RefArena`, and returns an [`RcRef`] to it.
262    ///
263    /// # Example
264    /// ```
265    /// use ref_arena::{RefArena, RcRef};
266    ///
267    /// let mut arena: RefArena<i32> = RefArena::new();
268    ///
269    /// let rc: RcRef<i32> = arena.insert(5);
270    /// assert_eq!(*rc, 5);
271    /// ```
272    pub fn insert(&mut self, item: T) -> RcRef<T> {
273        let (buffer_index, index) = match unsafe { &mut *self.vacant.get() }.pop() {
274            // Found vacant spot!
275            Some(vacant) => vacant,
276
277            // No vacant spots found
278            None => {
279                match self.inner.last() {
280                    Some(last) => {
281                        // We have a buffer allocated
282                        if self.last_len == last.arena.len() {
283                            // Buffer is full, allocate a new one.
284                            self.allocate_new_buffer();
285                            self.last_len = 0;
286                        }
287                        // Insert into buffer now.
288                        self.last_len += 1;
289
290                        (self.inner.len() - 1, self.last_len - 1)
291                    }
292                    None => {
293                        // Allocate initial buffer
294                        self.allocate_new_buffer();
295                        self.last_len += 1;
296
297                        (0, 0)
298                    }
299                }
300            }
301        };
302
303        let buffer = unsafe { self.inner.get_unchecked(buffer_index) };
304        unsafe {
305            let cell = buffer.arena.get_unchecked(index);
306            // Refcount is 1 (the ref we are about to return)
307            (*cell.1.get()) = 1;
308            (*cell.0.get()).write(item);
309        }
310
311        RcRef {
312            buffer: buffer.clone(),
313            ptr: NonNull::from(unsafe { buffer.arena.get_unchecked(index) }),
314        }
315    }
316}
317
318impl<T> Default for RefArena<T> {
319    fn default() -> Self {
320        Self::new()
321    }
322}
323
324/// A reference to an item within an [`RefArena`].
325///
326/// This reference has no lifetime and acts like a normal [`Rc`].
327/// Internally this Rc is an index within a bigger buffer
328/// in the `RefArena`.
329///
330/// Note that keeping an Rc held after the owning arena has
331/// been dropped will cause the holding buffer to stay alive.
332/// The holding buffer is an array of `T`'s, so keeping the
333/// buffer alive means that lots of memory (depending on how
334/// many objects are in the holding buffer) will not be freed
335/// until the last `RcRef` in the buffer has been dropped.
336
337pub struct RcRef<T> {
338    buffer: Rc<InnerArena<T>>,
339    ptr: NonNull<RcItem<T>>,
340}
341
342impl<T> RcRef<T> {
343    /// Gets the reference count.
344    pub fn ref_count(&self) -> usize {
345        unsafe { *self.get_inner().1.get() }
346    }
347
348    const unsafe fn get_inner(&self) -> &RcItem<T> {
349        // Ptr is valid while inner buffer is valid, held by Rc.
350        unsafe { &*self.ptr.as_ref() }
351    }
352
353    /// Gets the inner data and refcount immutably
354    /// Safe since it gives an immut ref.
355    fn get_data(&self) -> &T {
356        unsafe { (*self.get_inner().0.get()).assume_init_ref() }
357    }
358}
359
360impl<T> Clone for RcRef<T> {
361    #[inline]
362    fn clone(&self) -> Self {
363        // Should be safe since refcount is private and not mutably borrowed elsewhere
364        let count = self.ref_count();
365        // Refcount is initted on creation.
366        // Increment refcount
367        unsafe {
368            (*self.get_inner().1.get()) = count + 1;
369        }
370
371        RcRef {
372            buffer: self.buffer.clone(),
373            ptr: self.ptr,
374        }
375    }
376}
377
378impl<T> ops::Drop for RcRef<T> {
379    fn drop(&mut self) {
380        // Data is unborrowed while dropping
381        unsafe {
382            let inner = self.get_inner();
383            let refcount = &mut *inner.1.get();
384            *refcount -= 1;
385
386            if *refcount == 0 {
387                // No other rcs are alive
388                let data = &mut *inner.0.get();
389                data.assume_init_drop();
390                if self.buffer.vacant.strong_count() != 0 {
391                    let vacant = self.buffer.vacant.as_ptr();
392                    // Calculate index from ptr offset, so Deref impl doesn't
393                    // calculate offset and just derefs ptr directly.
394                    let index = self.ptr.as_ptr().offset_from(self.buffer.arena.as_ptr());
395                    (*(*vacant).get()).push((self.buffer.buffer_index, index as usize));
396                }
397            }
398        }
399    }
400}
401
402impl<T> ops::Deref for RcRef<T> {
403    type Target = T;
404
405    #[inline]
406    fn deref(&self) -> &Self::Target {
407        // Data is initted on creation
408        self.get_data()
409    }
410}
411
412impl<U, T: AsRef<U>> AsRef<U> for RcRef<T> {
413    #[inline]
414    fn as_ref(&self) -> &U {
415        (**self).as_ref()
416    }
417}
418
419impl<T: Debug> Debug for RcRef<T> {
420    #[inline]
421    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422        Debug::fmt(&**self, f)
423    }
424}
425
426impl<T: Display> Display for RcRef<T> {
427    #[inline]
428    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
429        Display::fmt(&**self, f)
430    }
431}
432
433impl<T: PartialEq> PartialEq for RcRef<T> {
434    #[inline]
435    fn eq(&self, other: &Self) -> bool {
436        (**self).eq(&**other)
437    }
438}
439
440impl<T: Eq> Eq for RcRef<T> {}
441
442impl<T: PartialOrd> PartialOrd for RcRef<T> {
443    #[inline]
444    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
445        (**self).partial_cmp(&**other)
446    }
447}
448
449impl<T: Ord> Ord for RcRef<T> {
450    #[inline]
451    fn cmp(&self, other: &Self) -> Ordering {
452        (**self).cmp(&**other)
453    }
454}
455
456impl<T: Hash> Hash for RcRef<T> {
457    #[inline]
458    fn hash<H: Hasher>(&self, state: &mut H) {
459        (**self).hash(state);
460    }
461}
462
463#[cfg(test)]
464mod test {
465    use crate::{get_buffer_size, RefArena};
466    use alloc::vec::Vec;
467
468    #[test]
469    fn initial_buffer_size() {
470        assert_eq!(get_buffer_size(0), 8);
471    }
472
473    #[test]
474    fn live_after() {
475        let mut arena = RefArena::new();
476        let rc = arena.insert(5);
477        drop(arena);
478        assert_eq!(*rc, 5);
479        assert!(rc.buffer.vacant.upgrade().is_none());
480    }
481
482    #[test]
483    fn buffer_allocate() {
484        // first buffer size
485        let to_allocate = get_buffer_size(0) + 1;
486
487        let mut arena = RefArena::new();
488        let mut rcs = Vec::new();
489
490        for i in 0..to_allocate {
491            rcs.push(arena.insert(i));
492        }
493
494        assert_eq!(arena.inner.len(), 2);
495    }
496
497    #[test]
498    fn ref_clone() {
499        let mut arena = RefArena::new();
500
501        let rc = arena.insert(5);
502        let rcclone = rc.clone();
503
504        assert_eq!(*rc, 5);
505        assert_eq!(*rcclone, 5);
506        assert_eq!(rc.ref_count(), 2);
507        drop(rc);
508        assert_eq!(*rcclone, 5);
509        assert_eq!(rcclone.ref_count(), 1);
510        assert_eq!(unsafe { &*arena.vacant.get() }.len(), 0);
511        drop(rcclone);
512        assert_eq!(unsafe { &*arena.vacant.get() }.len(), 1);
513    }
514
515    #[test]
516    fn cell_check() {
517        let mut arena = RefArena::new();
518
519        let rc = arena.insert(5);
520        let r: &i32 = &rc;
521        // Shouldn't cause UB, check with miri.
522        let count = rc.ref_count();
523
524        assert_eq!(*r, 5);
525        assert_eq!(count, 1);
526    }
527
528    #[test]
529    fn test_zst() {
530        struct Z;
531
532        let mut arena = RefArena::new();
533        let r = arena.insert(Z);
534        let _ = r.clone();
535    }
536}