Source code

Revision control

Copy as Markdown

Other Tools

//! The arena, a fast but limited type of allocator.
//!
//! **A fast (but limited) allocation arena for values of a single type.**
//!
//! Allocated objects are destroyed all at once, when the arena itself is
//! destroyed. There is no deallocation of individual objects while the arena
//! itself is still alive. The flipside is that allocation is fast: typically
//! just a vector push.
//!
//! There is also a method `into_vec()` to recover ownership of allocated
//! objects when the arena is no longer required, instead of destroying
//! everything.
//!
//! ## Example
//!
//! ```
//! use typed_arena_nomut::Arena;
//!
//! struct Monster {
//! level: u32,
//! }
//!
//! let monsters = Arena::new();
//!
//! let goku = monsters.alloc(Monster { level: 9001 });
//! assert!(goku.level > 9000);
//! ```
//!
//! ## Safe Cycles
//!
//! All allocated objects get the same lifetime, so you can safely create cycles
//! between them. This can be useful for certain data structures, such as graphs
//! and trees with parent pointers.
//!
//! ```
//! use std::cell::Cell;
//! use typed_arena_nomut::Arena;
//!
//! struct CycleParticipant<'a> {
//! other: Cell<Option<&'a CycleParticipant<'a>>>,
//! }
//!
//! let arena = Arena::new();
//!
//! let a = arena.alloc(CycleParticipant { other: Cell::new(None) });
//! let b = arena.alloc(CycleParticipant { other: Cell::new(None) });
//!
//! a.other.set(Some(b));
//! b.other.set(Some(a));
//! ```
// Potential optimizations:
// 1) add and stabilize a method for in-place reallocation of vecs.
// 2) add and stabilize placement new.
// 3) use an iterator. This may add far too much unsafe code.
#![deny(missing_docs)]
#![cfg_attr(not(any(feature = "std", test)), no_std)]
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(any(feature = "std", test))]
extern crate core;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::cell::RefCell;
use core::cmp;
use core::iter;
use core::mem;
use core::slice;
use core::str;
use std::cell::Ref;
use mem::MaybeUninit;
#[cfg(test)]
mod test;
// Initial size in bytes.
const INITIAL_SIZE: usize = 1024;
// Minimum capacity. Must be larger than 0.
const MIN_CAPACITY: usize = 1;
/// An arena of objects of type `T`.
///
/// ## Example
///
/// ```
/// use typed_arena_nomut::Arena;
///
/// struct Monster {
/// level: u32,
/// }
///
/// let monsters = Arena::new();
///
/// let vegeta = monsters.alloc(Monster { level: 9001 });
/// assert!(vegeta.level > 9000);
/// ```
pub struct Arena<T> {
chunks: RefCell<ChunkList<T>>,
}
struct ChunkList<T> {
current: Vec<T>,
rest: Vec<Vec<T>>,
}
impl<T> Arena<T> {
/// Construct a new arena.
///
/// ## Example
///
/// ```
/// use typed_arena_nomut::Arena;
///
/// let arena = Arena::new();
/// # arena.alloc(1);
/// ```
pub fn new() -> Arena<T> {
let size = cmp::max(1, mem::size_of::<T>());
Arena::with_capacity(INITIAL_SIZE / size)
}
/// Construct a new arena with capacity for `n` values pre-allocated.
///
/// ## Example
///
/// ```
/// use typed_arena_nomut::Arena;
///
/// let arena = Arena::with_capacity(1337);
/// # arena.alloc(1);
/// ```
pub fn with_capacity(n: usize) -> Arena<T> {
let n = cmp::max(MIN_CAPACITY, n);
Arena {
chunks: RefCell::new(ChunkList {
current: Vec::with_capacity(n),
rest: Vec::new(),
}),
}
}
/// Return the size of the arena
///
/// This is useful for using the size of previous typed arenas to build new typed arenas with large enough spaces.
///
/// ## Example
///
/// ```
/// use typed_arena_nomut::Arena;
///
/// let arena = Arena::with_capacity(0);
/// let a = arena.alloc(1);
/// let b = arena.alloc(2);
///
/// assert_eq!(arena.len(), 2);
/// ```
pub fn len(&self) -> usize {
let chunks = self.chunks.borrow();
let mut res = 0;
for vec in chunks.rest.iter() {
res += vec.len()
}
res + chunks.current.len()
}
/// Allocates a value in the arena, and returns a mutable reference
/// to that value.
///
/// ## Example
///
/// ```
/// use typed_arena_nomut::Arena;
///
/// let arena = Arena::new();
/// let x = arena.alloc(42);
/// assert_eq!(*x, 42);
/// ```
#[inline]
pub fn alloc(&self, value: T) -> &T {
self.alloc_fast_path(value)
.unwrap_or_else(|value| self.alloc_slow_path(value))
}
#[inline]
fn alloc_fast_path(&self, value: T) -> Result<&T, T> {
let mut chunks = self.chunks.borrow_mut();
let len = chunks.current.len();
if len < chunks.current.capacity() {
chunks.current.push(value);
// Avoid going through `Vec::deref_mut`, which overlaps
// other references we have already handed out!
debug_assert!(len < chunks.current.len()); // bounds check
Ok(unsafe { &mut *chunks.current.as_mut_ptr().add(len) })
} else {
Err(value)
}
}
fn alloc_slow_path(&self, value: T) -> &T {
&self.alloc_extend(iter::once(value))[0]
}
/// Uses the contents of an iterator to allocate values in the arena.
/// Returns a mutable slice that contains these values.
///
/// ## Example
///
/// ```
/// use typed_arena_nomut::Arena;
///
/// let arena = Arena::new();
/// let abc = arena.alloc_extend("abcdefg".chars().take(3));
/// assert_eq!(abc, ['a', 'b', 'c']);
/// ```
pub fn alloc_extend<I>(&self, iterable: I) -> &[T]
where
I: IntoIterator<Item = T>,
{
let mut iter = iterable.into_iter();
let mut chunks = self.chunks.borrow_mut();
let iter_min_len = iter.size_hint().0;
let mut next_item_index;
debug_assert!(
chunks.current.capacity() >= chunks.current.len(),
"capacity is always greater than or equal to len, so we don't need to worry about underflow"
);
if iter_min_len > chunks.current.capacity() - chunks.current.len() {
chunks.reserve(iter_min_len);
chunks.current.extend(iter);
next_item_index = 0;
} else {
next_item_index = chunks.current.len();
let mut i = 0;
while let Some(elem) = iter.next() {
if chunks.current.len() == chunks.current.capacity() {
// The iterator was larger than we could fit into the current chunk.
let chunks = &mut *chunks;
// Create a new chunk into which we can freely push the entire iterator into
chunks.reserve(i + 1);
let previous_chunk = chunks.rest.last_mut().unwrap();
let previous_chunk_len = previous_chunk.len();
// Move any elements we put into the previous chunk into this new chunk
chunks
.current
.extend(previous_chunk.drain(previous_chunk_len - i..));
chunks.current.push(elem);
// And the remaining elements in the iterator
chunks.current.extend(iter);
next_item_index = 0;
break;
} else {
chunks.current.push(elem);
}
i += 1;
}
}
let new_slice_ref = &mut chunks.current[next_item_index..];
// Extend the lifetime from that of `chunks_borrow` to that of `self`.
// This is OK because we’re careful to never move items
// by never pushing to inner `Vec`s beyond their initial capacity.
// The returned reference is unique (`&mut`):
// the `Arena` never gives away references to existing items.
unsafe { mem::transmute::<&mut [T], &mut [T]>(new_slice_ref) }
}
/// Allocates space for a given number of values, but doesn't initialize it.
///
/// ## Safety
///
/// After calling this method, the arena considers the elements initialized. If you fail to
/// initialize them (which includes because of panicking during the initialization), the arena
/// will run destructors on the uninitialized memory. Therefore, you must initialize them.
///
/// Considering how easy it is to cause undefined behaviour using this, you're advised to
/// prefer the other (safe) methods, like [`alloc_extend`][Arena::alloc_extend].
///
/// ## Example
///
/// ```rust
/// use std::mem::{self, MaybeUninit};
/// use std::ptr;
/// use typed_arena_nomut::Arena;
///
/// // Transmute from MaybeUninit slice to slice of initialized T.
/// // It is a separate function to preserve the lifetime of the reference.
/// unsafe fn transmute_uninit<A>(r: &mut [MaybeUninit<A>]) -> &mut [A] {
/// mem::transmute(r)
/// }
///
/// let arena: Arena<bool> = Arena::new();
/// let slice: &mut [bool];
/// unsafe {
/// let uninitialized = arena.alloc_uninitialized(10);
/// for elem in uninitialized.iter_mut() {
/// ptr::write(elem.as_mut_ptr(), true);
/// }
/// slice = transmute_uninit(uninitialized);
/// }
/// ```
///
/// ## Alternative allocation pattern
///
/// To avoid the problem of dropping assumed to be initialized elements on panic, it is also
/// possible to combine the [`reserve_extend`][Arena::reserve_extend] with
/// [`uninitialized_array`][Arena::uninitialized_array], initialize the elements and confirm
/// them by this method. In such case, when there's a panic during initialization, the already
/// initialized elements would leak but it wouldn't cause UB.
///
/// ```rust
/// use std::mem::{self, MaybeUninit};
/// use std::ptr;
/// use typed_arena_nomut::Arena;
///
/// unsafe fn transmute_uninit<A>(r: &mut [MaybeUninit<A>]) -> &mut [A] {
/// mem::transmute(r)
/// }
///
/// const COUNT: usize = 2;
///
/// let arena: Arena<String> = Arena::new();
///
/// arena.reserve_extend(COUNT);
/// let slice: &mut [String];
/// unsafe {
/// // Perform initialization before we claim the memory.
/// let uninitialized = arena.uninitialized_array();
/// assert!((*uninitialized).len() >= COUNT); // Ensured by the reserve_extend
/// for elem in &mut (*uninitialized)[..COUNT] {
/// ptr::write(elem.as_mut_ptr(), "Hello".to_owned());
/// }
/// let addr = (*uninitialized).as_ptr() as usize;
///
/// // The alloc_uninitialized returns the same memory, but "confirms" its allocation.
/// slice = transmute_uninit(arena.alloc_uninitialized(COUNT));
/// assert_eq!(addr, slice.as_ptr() as usize);
/// assert_eq!(slice, &["Hello".to_owned(), "Hello".to_owned()]);
/// }
/// ```
pub unsafe fn alloc_uninitialized(&self, num: usize) -> &mut [MaybeUninit<T>] {
let mut chunks = self.chunks.borrow_mut();
debug_assert!(
chunks.current.capacity() >= chunks.current.len(),
"capacity is always greater than or equal to len, so we don't need to worry about underflow"
);
if num > chunks.current.capacity() - chunks.current.len() {
chunks.reserve(num);
}
// At this point, the current chunk must have free capacity.
let next_item_index = chunks.current.len();
chunks.current.set_len(next_item_index + num);
// Go through pointers, to make sure we never create a reference to uninitialized T.
let start = chunks.current.as_mut_ptr().offset(next_item_index as isize);
let start_uninit = start as *mut MaybeUninit<T>;
slice::from_raw_parts_mut(start_uninit, num)
}
/// Makes sure there's enough continuous space for at least `num` elements.
///
/// This may save some work if called before [`alloc_extend`][Arena::alloc_extend]. It also
/// allows somewhat safer use pattern of [`alloc_uninitialized`][Arena::alloc_uninitialized].
/// On the other hand this might waste up to `n - 1` elements of space. In case new allocation
/// is needed, the unused ones in current chunk are never used.
pub fn reserve_extend(&self, num: usize) {
let mut chunks = self.chunks.borrow_mut();
debug_assert!(
chunks.current.capacity() >= chunks.current.len(),
"capacity is always greater than or equal to len, so we don't need to worry about underflow"
);
if num > chunks.current.capacity() - chunks.current.len() {
chunks.reserve(num);
}
}
/// Returns unused space.
///
/// *This unused space is still not considered "allocated".* Therefore, it
/// won't be dropped unless there are further calls to `alloc`,
/// [`alloc_uninitialized`][Arena::alloc_uninitialized], or
/// [`alloc_extend`][Arena::alloc_extend] which is why the method is safe.
///
/// It returns a raw pointer to avoid creating multiple mutable references to the same place.
/// It is up to the caller not to dereference it after any of the `alloc_` methods are called.
pub fn uninitialized_array(&self) -> *mut [MaybeUninit<T>] {
let mut chunks = self.chunks.borrow_mut();
let len = chunks.current.capacity() - chunks.current.len();
let next_item_index = chunks.current.len();
unsafe {
// Go through pointers, to make sure we never create a reference to uninitialized T.
let start = chunks.current.as_mut_ptr().offset(next_item_index as isize);
let start_uninit = start as *mut MaybeUninit<T>;
slice::from_raw_parts_mut(start_uninit, len) as *mut _
}
}
/// Convert this `Arena` into a `Vec<T>`.
///
/// Items in the resulting `Vec<T>` appear in the order that they were
/// allocated in.
///
/// ## Example
///
/// ```
/// use typed_arena_nomut::Arena;
///
/// let arena = Arena::new();
///
/// arena.alloc("a");
/// arena.alloc("b");
/// arena.alloc("c");
///
/// let easy_as_123 = arena.into_vec();
///
/// assert_eq!(easy_as_123, vec!["a", "b", "c"]);
/// ```
pub fn into_vec(self) -> Vec<T> {
let mut chunks = self.chunks.into_inner();
// keep order of allocation in the resulting Vec
let n = chunks
.rest
.iter()
.fold(chunks.current.len(), |a, v| a + v.len());
let mut result = Vec::with_capacity(n);
for mut vec in chunks.rest {
result.append(&mut vec);
}
result.append(&mut chunks.current);
result
}
/// Returns an iterator that allows modifying each value.
///
/// Items are yielded in the order that they were allocated.
///
/// ## Example
///
/// ```
/// use typed_arena_nomut::Arena;
/// use std::cell::Cell;
///
/// #[derive(Debug, PartialEq, Eq)]
/// struct Point { x: Cell<i32>, y: i32 };
///
/// let mut arena = Arena::new();
///
/// arena.alloc(Point { x: Cell::new(0), y: 0 });
/// arena.alloc(Point { x: Cell::new(1), y: 1 });
///
/// for point in arena.iter() {
/// point.x.set(point.x.get() + 10);
/// }
///
/// let points = arena.into_vec();
///
/// assert_eq!(points, vec![Point { x: Cell::new(10), y: 0 }, Point { x: Cell::new(11), y: 1 }]);
///
/// ```
///
/// ## Immutable Iteration
///
/// Note that there is no corresponding `iter` method. Access to the arena's contents
/// requries mutable access to the arena itself.
///
/// ```
/// use typed_arena_nomut::Arena;
/// use std::cell::Cell;
///
/// let mut arena = Arena::new();
/// let x = arena.alloc(Cell::new(1));
///
/// for i in arena.iter() {
/// println!("i: {}", i.get());
/// }
///
/// x.set(x.get() * 2);
/// ```
#[inline]
pub fn iter(&self) -> Iter<T> {
let chunks = self.chunks.borrow();
let position = if !chunks.rest.is_empty() {
let index = 0;
let inner_iter = chunks.rest[index].iter();
// Extend the lifetime of the individual elements to that of the arena.
// This is OK because we borrow the arena mutably to prevent new allocations
// and we take care here to never move items inside the arena while the
// iterator is alive.
let inner_iter = unsafe { mem::transmute(inner_iter) };
IterState::ChunkListRest { index, inner_iter }
} else {
// Extend the lifetime of the individual elements to that of the arena.
let iter = unsafe { mem::transmute(chunks.current.iter()) };
IterState::ChunkListCurrent { iter }
};
Iter {
chunks,
state: position,
}
}
}
impl Arena<u8> {
/// Allocates a string slice and returns a mutable reference to it.
///
/// This is on `Arena<u8>`, because string slices use byte slices (`[u8]`) as their backing
/// storage.
///
/// # Example
///
/// ```
/// use typed_arena_nomut::Arena;
///
/// let arena: Arena<u8> = Arena::new();
/// let hello = arena.alloc_str("Hello world");
/// assert_eq!("Hello world", hello);
/// ```
#[inline]
pub fn alloc_str(&self, s: &str) -> & str {
let buffer = self.alloc_extend(s.bytes());
// Can't fail the utf8 validation, it already came in as utf8
unsafe { str::from_utf8_unchecked(buffer) }
}
}
impl<T> Default for Arena<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> ChunkList<T> {
#[inline(never)]
#[cold]
fn reserve(&mut self, additional: usize) {
let double_cap = self
.current
.capacity()
.checked_mul(2)
.expect("capacity overflow");
let required_cap = additional
.checked_next_power_of_two()
.expect("capacity overflow");
let new_capacity = cmp::max(double_cap, required_cap);
let chunk = mem::replace(&mut self.current, Vec::with_capacity(new_capacity));
self.rest.push(chunk);
}
}
enum IterState<'a, T> {
ChunkListRest {
index: usize,
inner_iter: slice::Iter<'a, T>,
},
ChunkListCurrent {
iter: slice::Iter<'a, T>,
},
}
/// Mutable arena iterator.
///
/// This struct is created by the [`iter_mut`](struct.Arena.html#method.iter_mut) method on [Arenas](struct.Arena.html).
pub struct Iter<'a, T: 'a> {
chunks: Ref<'a, ChunkList<T>>,
state: IterState<'a, T>,
}
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
loop {
self.state = match self.state {
IterState::ChunkListRest {
mut index,
ref mut inner_iter,
} => {
match inner_iter.next() {
Some(item) => return Some(item),
None => {
index += 1;
if index < self.chunks.rest.len() {
let inner_iter = self.chunks.rest[index].iter();
// Extend the lifetime of the individual elements to that of the arena.
let inner_iter = unsafe { mem::transmute(inner_iter) };
IterState::ChunkListRest { index, inner_iter }
} else {
let iter = self.chunks.current.iter();
// Extend the lifetime of the individual elements to that of the arena.
let iter = unsafe { mem::transmute(iter) };
IterState::ChunkListCurrent { iter }
}
}
}
}
IterState::ChunkListCurrent { ref mut iter } => return iter.next(),
};
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let current_len = self.chunks.current.len();
let current_cap = self.chunks.current.capacity();
if self.chunks.rest.is_empty() {
(current_len, Some(current_len))
} else {
let rest_len = self.chunks.rest.len();
let last_chunk_len = self
.chunks
.rest
.last()
.map(|chunk| chunk.len())
.unwrap_or(0);
let min = current_len + last_chunk_len;
let max = min + (rest_len * current_cap / rest_len);
(min, Some(max))
}
}
}