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}