mirror of https://github.com/smol-rs/vec-arena
Remove bitmap
This commit is contained in:
parent
fcdbf80000
commit
821eac2764
411
src/bitmap.rs
411
src/bitmap.rs
|
@ -1,411 +0,0 @@
|
|||
//! The internals of `VecArena`.
|
||||
//!
|
||||
//! `Bitmap` tracks occupied and vacant slots in `VecArena` and provides useful methods for e.g.
|
||||
//! iterating over all occupied slots, or reserving additional slots.
|
||||
|
||||
use std::cmp;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
/// Returns the number of bits in one `usize` integer.
|
||||
#[inline]
|
||||
fn bits() -> usize {
|
||||
mem::size_of::<usize>() * 8
|
||||
}
|
||||
|
||||
/// Returns the number of `usize` integers required to store `len` bits.
|
||||
#[inline]
|
||||
fn blocks_for(len: usize) -> usize {
|
||||
// Divide `len` by `bits()` and round up.
|
||||
len / bits() + ((len % bits() > 0) as usize)
|
||||
}
|
||||
|
||||
/// Given a valid `block` index and `offset` within it, returns the index of that slot as within
|
||||
/// the whole bitmap.
|
||||
#[inline]
|
||||
fn slot_index(block: usize, offset: usize) -> usize {
|
||||
block * bits() + offset
|
||||
}
|
||||
|
||||
/// Given a slot `index` in the bitmap, returns it's block index and offset within the block.
|
||||
#[inline]
|
||||
fn block_and_offset(index: usize) -> (usize, usize) {
|
||||
(index / bits(), index % bits())
|
||||
}
|
||||
|
||||
/// Keeps track of occupied and vacant slots in `VecArena`.
|
||||
///
|
||||
/// It's implemented as an array of blocks, where every block tracks only a small contiguous chunk
|
||||
/// of slots. More precisely: as many slots as there are bits in one `usize`.
|
||||
///
|
||||
/// All blocks which are not fully occupied, except the last one, are linked together to form a
|
||||
/// doubly-linked list. This list allows finding a vacant slot to acquire in `O(1)` and releasing
|
||||
/// an occupied slot in `O(1)`, assuming the bitmap doesn't grow to accommodate more slots.
|
||||
///
|
||||
/// A block consists of:
|
||||
///
|
||||
/// * a bit mask (one `usize`), in which zeros are for vacant slots and ones for occupied slots
|
||||
/// * index of the successor block in the linked list
|
||||
/// * index of the predecessor block in the linked list
|
||||
///
|
||||
/// The last block is tricky to handle because it might not have the same number of slots as other
|
||||
/// blocks, so it gets special treatment in the implementation.
|
||||
pub struct Bitmap {
|
||||
/// Storage for the following sequences, in this order:
|
||||
///
|
||||
/// * bit masks
|
||||
/// * indices to successor nodes
|
||||
/// * indices to predecessor nodes
|
||||
///
|
||||
/// All three sequences are stored in this single contiguous array.
|
||||
///
|
||||
/// The most common and costly operation during the lifetime of a bitmap is testing
|
||||
/// whether a slot is occupied. Storing bit masks close together improves cache
|
||||
/// performance.
|
||||
data: *mut usize,
|
||||
|
||||
/// Number of reserved slots.
|
||||
len: usize,
|
||||
|
||||
/// Number of occupied slots.
|
||||
occupied: usize,
|
||||
|
||||
/// Index of the first block in the linked list, or `!0` if the list is empty.
|
||||
head: usize,
|
||||
}
|
||||
|
||||
impl Bitmap {
|
||||
/// Constructs a new `Bitmap` with zero slots.
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
let data = {
|
||||
let mut vec = Vec::with_capacity(0);
|
||||
let ptr = vec.as_mut_ptr();
|
||||
mem::forget(vec);
|
||||
ptr
|
||||
};
|
||||
Bitmap {
|
||||
data: data,
|
||||
len: 0,
|
||||
occupied: 0,
|
||||
head: !0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of reserved slots in the bitmap.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
/// Returns the number of occupied slots in the bitmap.
|
||||
#[inline]
|
||||
pub fn occupied(&self) -> usize {
|
||||
self.occupied
|
||||
}
|
||||
|
||||
/// Finds a vacant slot, marks as occupied, and returns its index.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if all slots are occupied.
|
||||
pub fn acquire(&mut self) -> usize {
|
||||
assert!(self.occupied < self.len, "no vacant slots to acquire, len = {}", self.len);
|
||||
|
||||
let block = if self.head == !0 {
|
||||
// The list is empty, so try the last block.
|
||||
blocks_for(self.len) - 1
|
||||
} else {
|
||||
// The list has a head. Take a vacant slot from the head block.
|
||||
self.head
|
||||
};
|
||||
|
||||
let index = unsafe {
|
||||
// Find the rightmost zero bit in the mask. Taking the rightmost zero is always ok,
|
||||
// even if this is the last block.
|
||||
let offset = (!*self.mask(block)).trailing_zeros() as usize;
|
||||
debug_assert!(offset < bits());
|
||||
|
||||
slot_index(block, offset)
|
||||
};
|
||||
self.acquire_at(index);
|
||||
index
|
||||
}
|
||||
|
||||
/// Marks the vacant slot at `index` as occupied.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the slot is already occupied.
|
||||
pub fn acquire_at(&mut self, index: usize) {
|
||||
assert!(index < self.len, "`index` out of bounds");
|
||||
|
||||
let (block, offset) = block_and_offset(index);
|
||||
unsafe {
|
||||
// Mark the slot as occupied in the block's bit mask.
|
||||
assert!(*self.mask(block) >> offset & 1 == 0, "occupied slot at `index`");
|
||||
*self.mask(block) ^= 1 << offset;
|
||||
|
||||
// If the block has just become fully occupied, remove it from the list.
|
||||
if block == self.head && *self.mask(block) == !0 {
|
||||
let succ = *self.successor(block);
|
||||
self.link_blocks(!0, succ);
|
||||
self.head = succ;
|
||||
}
|
||||
|
||||
self.occupied += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Releases the occupied slot at `index`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the `index` is out of bounds or the slot is vacant.
|
||||
pub fn release(&mut self, index: usize) {
|
||||
assert!(index < self.len, "`index` out of bounds");
|
||||
|
||||
let (block, offset) = block_and_offset(index);
|
||||
unsafe {
|
||||
// Make sure we're not releasing a vacant slot.
|
||||
assert!(*self.mask(block) >> offset & 1 == 1, "releasing a vacant slot");
|
||||
|
||||
self.occupied -= 1;
|
||||
|
||||
// If the block is fully occupied, insert it back into the list.
|
||||
if *self.mask(block) == !0 {
|
||||
let head = self.head;
|
||||
self.link_blocks(!0, block);
|
||||
self.link_blocks(block, head);
|
||||
self.head = block;
|
||||
}
|
||||
|
||||
// Mark the slot as vacant in the block's bit mask.
|
||||
*self.mask(block) ^= 1 << offset;
|
||||
}
|
||||
}
|
||||
|
||||
/// Given the required minimal additional number of slots to reserve, returns the number of
|
||||
/// slots to reserve in order to keep amortized time complexity.
|
||||
fn amortized_reserve(&self, additional: usize) -> usize {
|
||||
let len = self.len();
|
||||
let required_len = len.checked_add(additional).expect("len overflow");
|
||||
let double_len = len.checked_mul(2).expect("len overflow");
|
||||
cmp::max(required_len, double_len) - len
|
||||
}
|
||||
|
||||
/// Reserves at least `additional` more slots for new elements to be inserted. The arena may
|
||||
/// reserve more space to avoid frequent reallocations.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the new number of slots overflows `usize`.
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
let amortized = self.amortized_reserve(additional);
|
||||
self.reserve_exact(amortized);
|
||||
}
|
||||
|
||||
/// Reserves exactly `additional` more slots for new elements to be inserted.
|
||||
///
|
||||
/// Note that the allocator may give the bitmap more space than it requests.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the new number of slots overflows `usize`.
|
||||
pub fn reserve_exact(&mut self, additional: usize) {
|
||||
let old_blocks = blocks_for(self.len);
|
||||
self.len = self.len.checked_add(additional).expect("len overflow");
|
||||
let new_blocks = blocks_for(self.len);
|
||||
|
||||
if new_blocks == old_blocks {
|
||||
// We can simply return.
|
||||
// The higher unused bits of the last block are always zero anyway.
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// If the last block had some unused or vacant slots, push it into the linked list.
|
||||
if old_blocks > 0 && *self.mask(old_blocks - 1) != !0 {
|
||||
let head = self.head;
|
||||
let last = old_blocks - 1;
|
||||
self.link_blocks(last, head);
|
||||
self.link_blocks(!0, last);
|
||||
self.head = last;
|
||||
}
|
||||
|
||||
// Allocate a new array.
|
||||
let new_data = {
|
||||
// Every block contains three `usize` integers, so we need `3 * new_blocks` of
|
||||
// space.
|
||||
let mut vec = Vec::with_capacity(3 * new_blocks);
|
||||
let ptr = vec.as_mut_ptr();
|
||||
mem::forget(vec);
|
||||
ptr
|
||||
};
|
||||
|
||||
// Copy the three old subarrays (bit masks, successor indices, predecessor indices)
|
||||
// into the new array.
|
||||
for i in 0..3 {
|
||||
ptr::copy_nonoverlapping(
|
||||
self.data.offset((old_blocks * i) as isize),
|
||||
new_data.offset((new_blocks * i) as isize),
|
||||
old_blocks);
|
||||
}
|
||||
|
||||
// Deallocate the old array.
|
||||
Vec::from_raw_parts(self.data, 0, 3 * old_blocks);
|
||||
|
||||
// Set the new bit masks to zero.
|
||||
ptr::write_bytes(new_data.offset(old_blocks as isize), 0, new_blocks - old_blocks);
|
||||
|
||||
// Set the new data now because we're about to push blocks into the linked list.
|
||||
self.data = new_data;
|
||||
|
||||
// If there are at least two new blocks, that means there is at least one new block
|
||||
// that must be pushed into the linked list.
|
||||
if old_blocks + 2 <= new_blocks {
|
||||
let head = self.head;
|
||||
self.link_blocks(new_blocks - 2, head);
|
||||
self.link_blocks(!0, old_blocks);
|
||||
self.head = old_blocks;
|
||||
|
||||
for block in old_blocks .. new_blocks - 2 {
|
||||
self.link_blocks(block, block + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the slot at `index` is occupied.
|
||||
#[inline]
|
||||
pub fn is_occupied(&self, index: usize) -> bool {
|
||||
assert!(index < self.len, "`index` out of bounds");
|
||||
|
||||
let (block, offset) = block_and_offset(index);
|
||||
unsafe {
|
||||
*self.mask(block) >> offset & 1 != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over occupied slots.
|
||||
#[inline]
|
||||
pub fn iter(&self) -> Iter {
|
||||
Iter {
|
||||
bitmap: self,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn check_invariants(&self) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/// Links together blocks `a` and `b` so that `a` comes before `b` in the linked list.
|
||||
#[inline]
|
||||
unsafe fn link_blocks(&mut self, a: usize, b: usize) {
|
||||
if a != !0 { *self.successor(a) = b; }
|
||||
if b != !0 { *self.predecessor(b) = a; }
|
||||
}
|
||||
|
||||
/// Returns the pointer to the bit mask of `block`.
|
||||
#[inline]
|
||||
unsafe fn mask(&self, block: usize) -> *mut usize {
|
||||
self.data.offset(block as isize)
|
||||
}
|
||||
|
||||
/// Returns the pointer to the index of the successor of `block`.
|
||||
#[inline]
|
||||
unsafe fn successor(&self, block: usize) -> *mut usize {
|
||||
self.data.offset((blocks_for(self.len) + block) as isize)
|
||||
}
|
||||
|
||||
/// Returns the pointer to the index of the predecessor of `block`.
|
||||
#[inline]
|
||||
unsafe fn predecessor(&self, block: usize) -> *mut usize {
|
||||
self.data.offset((2 * blocks_for(self.len) + block) as isize)
|
||||
}
|
||||
|
||||
/// Returns the next occupied slot starting from (and including) the specified `index`.
|
||||
pub fn next_occupied(&self, mut index: usize) -> Option<usize> {
|
||||
while index < self.len() {
|
||||
let (block, offset) = block_and_offset(index);
|
||||
let mask = unsafe { *self.mask(block) };
|
||||
|
||||
if mask != 0 {
|
||||
for off in offset .. bits() {
|
||||
if mask >> off & 1 == 1 {
|
||||
return Some(slot_index(block, off));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
index = slot_index(block + 1, 0);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the previous occupied slot before the specified `index`.
|
||||
pub fn previous_occupied(&self, mut index: usize) -> Option<usize> {
|
||||
index = cmp::min(index, self.len());
|
||||
loop {
|
||||
let (block, offset) = block_and_offset(index);
|
||||
let mask = unsafe { *self.mask(block) };
|
||||
|
||||
if mask != 0 {
|
||||
for off in (0 .. offset).rev() {
|
||||
if mask >> off & 1 == 1 {
|
||||
return Some(slot_index(block, off));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if block == 0 {
|
||||
break;
|
||||
}
|
||||
index = slot_index(block - 1, 0);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Bitmap {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
Vec::from_raw_parts(self.data, 0, 3 * blocks_for(self.len));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates over all occupied slots.
|
||||
pub struct Iter<'a> {
|
||||
/// The bitmap to iterate over.
|
||||
bitmap: &'a Bitmap,
|
||||
|
||||
/// Index of the current slot.
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = usize;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.bitmap.next_occupied(self.index).map(|index| {
|
||||
self.index = index + 1;
|
||||
index
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let low = 0;
|
||||
let high = self.bitmap.len() - self.index;
|
||||
(low, Some(high))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn count(self) -> usize {
|
||||
self.bitmap.occupied
|
||||
}
|
||||
}
|
673
src/lib.rs
673
src/lib.rs
|
@ -1,54 +1,110 @@
|
|||
//! A fast general-purpose object arena.
|
||||
//!
|
||||
//! This crate contains `VecArena`, which is an allocator that can hold objects of only one type.
|
||||
//! It can allocate space for new objects and reclaim space upon removal of objects. The amount
|
||||
//! of space to hold objects is dynamically expanded as needed. Inserting objects into an arena
|
||||
//! is amortized `O(1)`, and removing is `O(1)`.
|
||||
// TODO: An example of a doubly linked list?
|
||||
//! `Arena<T>` is basically just a `Vec<Option<T>>`, which allows you to:
|
||||
//!
|
||||
//! * Insert an object (reuse an existing `None` element, or append to the end)
|
||||
//! * Remove object at a specified index
|
||||
//! * Access object at a specified index
|
||||
//!
|
||||
//! It's useful in situations where you need to create complex graphs. For example:
|
||||
//!
|
||||
//! * Doubly linked lists
|
||||
//! * Bidirectional trees
|
||||
//! * Widget hierarchies in GUIs
|
||||
//! * Graphs with circular references
|
||||
//!
|
||||
//! As a rule of thumb, if building a data structure using `Rc` and `RefCell` gets too messy or
|
||||
//! costly, `Arena` might be a better choice.
|
||||
|
||||
use bitmap::Bitmap;
|
||||
use std::iter;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::vec;
|
||||
|
||||
mod bitmap;
|
||||
|
||||
/// An arena that can insert and remove objects of a single type.
|
||||
///
|
||||
/// `VecArena<T>` resembles `Vec<Option<T>>` a lot because it holds an array of slots for
|
||||
/// storing objects. A slot can be either occupied or vacant. Inserting a new object into an arena
|
||||
/// involves finding a vacant slot and placing the object into the slot. Removing an object means
|
||||
/// taking it out of the slot and marking it as vacant.
|
||||
///
|
||||
/// Internally, a bitmap is used instead of `Option`s to conserve space and improve cache
|
||||
/// performance. Every indexing operation checks that an object really exists at the specified
|
||||
/// index, otherwise it panics.
|
||||
// TODO: Explain that arena[index] is equivalent to arena.get(index).unwrap() or
|
||||
// arena.get_mut(index).unwrap()
|
||||
// TODO: a bunch of examples, see the docs for Vec for inspiration.
|
||||
pub struct VecArena<T> {
|
||||
// TODO: Docs for these fields
|
||||
slots: *const T,
|
||||
bitmap: Bitmap,
|
||||
marker: PhantomData<T>,
|
||||
#[derive(Clone)]
|
||||
enum Slot<T> {
|
||||
Vacant(usize),
|
||||
Occupied(T),
|
||||
}
|
||||
|
||||
impl<T> VecArena<T> {
|
||||
/// An object arena.
|
||||
///
|
||||
/// `Arena<T>` holds an array of slots for storing objects.
|
||||
/// Every slot is always in one of two states: occupied or vacant.
|
||||
///
|
||||
/// Essentially, this is equivalent to `Vec<Option<T>>`.
|
||||
///
|
||||
/// # Insert and remove
|
||||
///
|
||||
/// When inserting a new object into arena, a vacant slot is found and then the object is placed
|
||||
/// into the slot. If there are no vacant slots, the array is reallocated with bigger capacity.
|
||||
/// The cost of insertion is amortized `O(1)`.
|
||||
///
|
||||
/// When removing an object, the slot containing it is marked as vacant and the object is returned.
|
||||
/// The cost of removal is `O(1)`.
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::new();
|
||||
/// let a = arena.insert(10);
|
||||
/// let b = arena.insert(20);
|
||||
///
|
||||
/// assert_eq!(a, 0); // 10 was placed at index 0
|
||||
/// assert_eq!(b, 1); // 20 was placed at index 1
|
||||
///
|
||||
/// assert_eq!(arena.remove(a), Some(10));
|
||||
/// assert_eq!(arena.get(a), None); // slot at index 0 is now vacant
|
||||
///
|
||||
/// assert_eq!(arena.insert(30), 0); // slot at index 0 is reused
|
||||
/// ```
|
||||
///
|
||||
/// # Indexing
|
||||
///
|
||||
/// You can also access objects in an arena by index, just like you would in a `Vec`.
|
||||
/// However, accessing a vacant slot by index or using an out-of-bounds index will result in panic.
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::new();
|
||||
/// let a = arena.insert(10);
|
||||
/// let b = arena.insert(20);
|
||||
///
|
||||
/// assert_eq!(arena[a], 10);
|
||||
/// assert_eq!(arena[b], 20);
|
||||
///
|
||||
/// arena[a] += arena[b];
|
||||
/// assert_eq!(arena[a], 30);
|
||||
/// ```
|
||||
///
|
||||
/// To access slots without fear of panicking, use `get` and `get_mut`, which return `Option`s.
|
||||
pub struct Arena<T> {
|
||||
slots: Vec<Slot<T>>,
|
||||
len: usize,
|
||||
head: usize,
|
||||
}
|
||||
|
||||
impl<T> Arena<T> {
|
||||
/// Constructs a new, empty arena.
|
||||
///
|
||||
/// The arena will not allocate until objects are inserted into it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena: Arena<i32> = Arena::new();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
let slots = {
|
||||
let mut vec = Vec::with_capacity(0);
|
||||
let ptr = vec.as_mut_ptr();
|
||||
mem::forget(vec);
|
||||
ptr
|
||||
};
|
||||
VecArena {
|
||||
slots: slots,
|
||||
bitmap: Bitmap::new(),
|
||||
marker: PhantomData,
|
||||
Arena {
|
||||
slots: Vec::new(),
|
||||
len: 0,
|
||||
head: !0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,231 +112,330 @@ impl<T> VecArena<T> {
|
|||
///
|
||||
/// The arena will be able to hold exactly `capacity` objects without reallocating.
|
||||
/// If `capacity` is 0, the arena will not allocate.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::with_capacity(10);
|
||||
///
|
||||
/// assert_eq!(arena.len(), 0);
|
||||
/// assert_eq!(arena.capacity(), 10);
|
||||
///
|
||||
/// // These inserts are done without reallocating...
|
||||
/// for i in 0..10 {
|
||||
/// arena.insert(i);
|
||||
/// }
|
||||
/// assert!(arena.capacity() == 10);
|
||||
///
|
||||
/// // ... but this one will reallocate.
|
||||
/// arena.insert(11);
|
||||
/// assert!(arena.capacity() > 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
let mut arena = Self::new();
|
||||
arena.reserve_exact(cap);
|
||||
arena
|
||||
Arena {
|
||||
slots: Vec::with_capacity(cap),
|
||||
len: 0,
|
||||
head: !0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of objects the arena can hold without reallocating.
|
||||
/// In other words, this is the number of slots.
|
||||
/// Returns the number of slots in the arena.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let arena: Arena<i32> = Arena::with_capacity(10);
|
||||
/// assert_eq!(arena.capacity(), 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.bitmap.len()
|
||||
self.slots.capacity()
|
||||
}
|
||||
|
||||
/// Returns the number of objects in the arena.
|
||||
/// In other words, this is the number of occupied slots.
|
||||
/// Returns the number of occupied slots in the arena.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::new();
|
||||
/// assert_eq!(arena.len(), 0);
|
||||
///
|
||||
/// for i in 0..10 {
|
||||
/// arena.insert(());
|
||||
/// assert_eq!(arena.len(), i + 1);
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.bitmap.occupied()
|
||||
self.len
|
||||
}
|
||||
|
||||
/// Returns `true` if the arena holds no objects.
|
||||
/// Returns `true` if all slots are vacant.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::new();
|
||||
/// assert!(arena.is_empty());
|
||||
///
|
||||
/// arena.insert(1);
|
||||
/// assert!(!arena.is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
self.len == 0
|
||||
}
|
||||
|
||||
/// Inserts an object into the arena and returns the slot index in which it was stored.
|
||||
/// Inserts an object into the arena and returns the slot index it was stored in.
|
||||
/// The arena will reallocate if it's full.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::new();
|
||||
///
|
||||
/// let a = arena.insert(1);
|
||||
/// let b = arena.insert(2);
|
||||
/// assert!(a != b);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn insert(&mut self, object: T) -> usize {
|
||||
if self.bitmap.occupied() == self.bitmap.len() {
|
||||
self.double();
|
||||
if self.head == !0 {
|
||||
let index = self.len;
|
||||
self.slots.push(Slot::Occupied(object));
|
||||
self.len += 1;
|
||||
index
|
||||
} else {
|
||||
let index = self.head;
|
||||
match self.slots[index] {
|
||||
Slot::Vacant(next) => {
|
||||
self.head = next;
|
||||
self.slots[index] = Slot::Occupied(object);
|
||||
},
|
||||
Slot::Occupied(_) => unreachable!(),
|
||||
}
|
||||
index
|
||||
}
|
||||
|
||||
let index = self.bitmap.acquire();
|
||||
unsafe {
|
||||
ptr::write(self.slots.offset(index as isize) as *mut T, object);
|
||||
}
|
||||
index
|
||||
}
|
||||
|
||||
/// Inserts an object into the vacant slot at `index`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `index` is out of bounds or the slot is already occupied.
|
||||
pub fn insert_at(&mut self, index: usize, object: T) -> usize {
|
||||
self.bitmap.acquire_at(index);
|
||||
unsafe {
|
||||
ptr::write(self.slots.offset(index as isize) as *mut T, object);
|
||||
}
|
||||
index
|
||||
}
|
||||
|
||||
/// Removes the object stored at `index` from the arena and returns it.
|
||||
///
|
||||
/// # Panics
|
||||
/// `None` is returned in case the slot is vacant, or `index` is out of bounds.
|
||||
///
|
||||
/// Panics if `index` is out of bounds or the slot is already vacant.
|
||||
pub fn remove(&mut self, index: usize) -> T {
|
||||
// `release` will panic if the index is out of bounds or the slot is already vacant.
|
||||
self.bitmap.release(index);
|
||||
|
||||
unsafe {
|
||||
ptr::read(self.slots.offset(index as isize) as *mut T)
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::new();
|
||||
/// let a = arena.insert("hello");
|
||||
///
|
||||
/// assert_eq!(arena.len(), 1);
|
||||
/// assert_eq!(arena.remove(a), Some("hello"));
|
||||
///
|
||||
/// assert_eq!(arena.len(), 0);
|
||||
/// assert_eq!(arena.remove(a), None);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn remove(&mut self, index: usize) -> Option<T> {
|
||||
match self.slots.get_mut(index) {
|
||||
None => None,
|
||||
Some(&mut Slot::Vacant(_)) => None,
|
||||
Some(slot @ &mut Slot::Occupied(_)) => {
|
||||
if let Slot::Occupied(object) = mem::replace(slot, Slot::Vacant(self.head)) {
|
||||
self.head = index;
|
||||
self.len -= 1;
|
||||
Some(object)
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears the arena, removing and dropping all objects it holds. Keeps the allocated memory
|
||||
/// for reuse.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::new();
|
||||
/// for i in 0..10 {
|
||||
/// arena.insert(i);
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(arena.len(), 10);
|
||||
/// arena.clear();
|
||||
/// assert_eq!(arena.len(), 0);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
let mut arena = VecArena::with_capacity(self.capacity());
|
||||
mem::swap(self, &mut arena);
|
||||
self.slots.clear();
|
||||
self.len = 0;
|
||||
self.head = !0;
|
||||
}
|
||||
|
||||
/// Returns a reference to the object stored at `index`.
|
||||
/// Returns a reference to the object stored at `index`, or `None` if it's out of bounds.
|
||||
///
|
||||
/// # Panics
|
||||
/// # Examples
|
||||
///
|
||||
/// Panics if `index` is out of bounds.
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::new();
|
||||
/// let index = arena.insert("hello");
|
||||
///
|
||||
/// assert_eq!(arena.get(index), Some(&"hello"));
|
||||
/// arena.remove(index);
|
||||
/// assert_eq!(arena.get(index), None);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<&T> {
|
||||
if self.bitmap.is_occupied(index) {
|
||||
unsafe { Some(self.get_unchecked(index)) }
|
||||
} else {
|
||||
None
|
||||
match self.slots.get(index) {
|
||||
None => None,
|
||||
Some(&Slot::Vacant(_)) => None,
|
||||
Some(&Slot::Occupied(ref object)) => Some(object),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the object stored at `index`.
|
||||
/// Returns a mutable reference to the object stored at `index`, or `None` if it's out of
|
||||
/// bounds.
|
||||
///
|
||||
/// # Panics
|
||||
/// # Examples
|
||||
///
|
||||
/// Panics if `index` is out of bounds.
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::new();
|
||||
/// let index = arena.insert(7);
|
||||
///
|
||||
/// assert_eq!(arena.get_mut(index), Some(&mut 7));
|
||||
/// *arena.get_mut(index).unwrap() *= 10;
|
||||
/// assert_eq!(arena.get_mut(index), Some(&mut 70));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
|
||||
if self.bitmap.is_occupied(index) {
|
||||
unsafe { Some(self.get_unchecked_mut(index)) }
|
||||
} else {
|
||||
None
|
||||
match self.slots.get_mut(index) {
|
||||
None => None,
|
||||
Some(&mut Slot::Vacant(_)) => None,
|
||||
Some(&mut Slot::Occupied(ref mut object)) => Some(object),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reserves capacity for at least `additional` more elements to be inserted. The arena may
|
||||
/// Reserves capacity for at least `additional` more objects to be inserted. The arena may
|
||||
/// reserve more space to avoid frequent reallocations.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the new capacity overflows `usize`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::new();
|
||||
/// arena.insert("hello");
|
||||
///
|
||||
/// arena.reserve(10);
|
||||
/// assert!(arena.capacity() >= 11);
|
||||
/// ```
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
let len = self.bitmap.len();
|
||||
self.bitmap.reserve(additional);
|
||||
unsafe {
|
||||
self.reallocate(len);
|
||||
let vacant = self.slots.len() - self.len;
|
||||
if additional > vacant {
|
||||
self.slots.reserve(additional - vacant);
|
||||
}
|
||||
}
|
||||
|
||||
/// Reserves the minimum capacity for exactly `additional` more elements to be inserted.
|
||||
/// Reserves the minimum capacity for exactly `additional` more objects to be inserted.
|
||||
///
|
||||
/// Note that the allocator may give the arena more space than it requests.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the new capacity overflows `usize`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::new();
|
||||
/// arena.insert("hello");
|
||||
///
|
||||
/// arena.reserve_exact(10);
|
||||
/// assert!(arena.capacity() >= 11);
|
||||
/// ```
|
||||
pub fn reserve_exact(&mut self, additional: usize) {
|
||||
let len = self.bitmap.len();
|
||||
self.bitmap.reserve_exact(additional);
|
||||
unsafe {
|
||||
self.reallocate(len);
|
||||
let vacant = self.slots.len() - self.len;
|
||||
if additional > vacant {
|
||||
self.slots.reserve_exact(additional - vacant);
|
||||
}
|
||||
}
|
||||
|
||||
/// Reallocates the object array because the bitmap was resized.
|
||||
unsafe fn reallocate(&mut self, old_len: usize) {
|
||||
let new_len = self.bitmap.len();
|
||||
|
||||
// Allocate a new array.
|
||||
let mut vec = Vec::with_capacity(new_len);
|
||||
let ptr = vec.as_mut_ptr();
|
||||
mem::forget(vec);
|
||||
|
||||
// Copy data into the new array.
|
||||
ptr::copy_nonoverlapping(self.slots, ptr, old_len);
|
||||
|
||||
// Deallocate the old array.
|
||||
Vec::from_raw_parts(self.slots as *mut T, 0, old_len);
|
||||
|
||||
self.slots = ptr;
|
||||
}
|
||||
|
||||
/// Doubles the capacity of the arena.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn double(&mut self) {
|
||||
let len = self.bitmap.len();
|
||||
let elem_size = mem::size_of::<T>();
|
||||
|
||||
let new_len = if len == 0 {
|
||||
if elem_size.checked_mul(4).is_some() {
|
||||
4
|
||||
} else {
|
||||
1
|
||||
}
|
||||
} else {
|
||||
len.checked_mul(2).expect("len overflow")
|
||||
};
|
||||
|
||||
self.reserve_exact(new_len - len);
|
||||
}
|
||||
|
||||
/// Returns an iterator over occupied slots.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::new();
|
||||
/// arena.insert(1);
|
||||
/// arena.insert(2);
|
||||
/// arena.insert(4);
|
||||
///
|
||||
/// let mut iterator = arena.iter();
|
||||
/// assert_eq!(iterator.next(), Some((0, &1)));
|
||||
/// assert_eq!(iterator.next(), Some((1, &2)));
|
||||
/// assert_eq!(iterator.next(), Some((2, &4)));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn iter(&self) -> Iter<T> {
|
||||
Iter {
|
||||
arena: self,
|
||||
index: 0,
|
||||
}
|
||||
Iter { slots: self.slots.iter().enumerate() }
|
||||
}
|
||||
|
||||
/// Returns an iterator that returns mutable references to objects.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use vec_arena::Arena;
|
||||
///
|
||||
/// let mut arena = Arena::new();
|
||||
/// arena.insert("zero".to_string());
|
||||
/// arena.insert("one".to_string());
|
||||
/// arena.insert("two".to_string());
|
||||
///
|
||||
/// for (index, object) in arena.iter_mut() {
|
||||
/// *object = index.to_string() + " " + object;
|
||||
/// }
|
||||
///
|
||||
/// let mut iterator = arena.iter();
|
||||
/// assert_eq!(iterator.next(), Some((0, &"0 zero".to_string())));
|
||||
/// assert_eq!(iterator.next(), Some((1, &"1 one".to_string())));
|
||||
/// assert_eq!(iterator.next(), Some((2, &"2 two".to_string())));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn iter_mut(&mut self) -> IterMut<T> {
|
||||
IterMut {
|
||||
slots: self.slots as *mut T,
|
||||
bitmap: &self.bitmap,
|
||||
index: 0,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn drain(&mut self) -> Drain<T> {
|
||||
Drain {
|
||||
arena: self,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the object at `index`, without bounds checking nor checking that
|
||||
/// the object exists.
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked(&self, index: usize) -> &T {
|
||||
&*self.slots.offset(index as isize)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the object at `index`, without bounds checking nor checking
|
||||
/// that the object exists.
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T {
|
||||
&mut *(self.slots.offset(index as isize) as *mut T)
|
||||
IterMut { slots: self.slots.iter_mut().enumerate() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for VecArena<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// Drop all objects in the arena.
|
||||
for index in self.bitmap.iter() {
|
||||
ptr::drop_in_place(self.slots.offset(index as isize) as *mut T);
|
||||
}
|
||||
|
||||
// Deallocate the old array.
|
||||
Vec::from_raw_parts(self.slots as *mut T, 0, self.bitmap.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<usize> for VecArena<T> {
|
||||
impl<T> Index<usize> for Arena<T> {
|
||||
type Output = T;
|
||||
|
||||
#[inline]
|
||||
|
@ -289,43 +444,32 @@ impl<T> Index<usize> for VecArena<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<usize> for VecArena<T> {
|
||||
impl<T> IndexMut<usize> for Arena<T> {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: usize) -> &mut T {
|
||||
self.get_mut(index).expect("vacant slot at `index`")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for VecArena<T> {
|
||||
impl<T> Default for Arena<T> {
|
||||
fn default() -> Self {
|
||||
VecArena::new()
|
||||
Arena::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for VecArena<T> {
|
||||
impl<T: Clone> Clone for Arena<T> {
|
||||
fn clone(&self) -> Self {
|
||||
let mut arena = VecArena::with_capacity(self.capacity());
|
||||
for index in self.bitmap.iter() {
|
||||
let clone = unsafe {
|
||||
self.get_unchecked(index).clone()
|
||||
};
|
||||
arena.insert_at(index, clone);
|
||||
Arena {
|
||||
slots: self.slots.clone(),
|
||||
len: self.len,
|
||||
head: self.head,
|
||||
}
|
||||
arena
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the occupied slots in a `Arena`.
|
||||
pub struct IntoIter<T> {
|
||||
slots: *const T,
|
||||
bitmap: Bitmap,
|
||||
index: usize,
|
||||
marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Drop for IntoIter<T> {
|
||||
fn drop(&mut self) {
|
||||
while self.next().is_some() {}
|
||||
}
|
||||
slots: iter::Enumerate<vec::IntoIter<Slot<T>>>,
|
||||
}
|
||||
|
||||
impl<T> Iterator for IntoIter<T> {
|
||||
|
@ -333,42 +477,28 @@ impl<T> Iterator for IntoIter<T> {
|
|||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.bitmap.next_occupied(self.index).map(|index| {
|
||||
self.index = index + 1;
|
||||
unsafe {
|
||||
self.bitmap.release(index);
|
||||
(index, ptr::read(self.slots.offset(index as isize)))
|
||||
while let Some((index, slot)) = self.slots.next() {
|
||||
if let Slot::Occupied(object) = slot {
|
||||
return Some((index, object));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let lower = 0;
|
||||
let upper = self.bitmap.len() - self.index;
|
||||
(lower, Some(upper))
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for VecArena<T> {
|
||||
impl<T> IntoIterator for Arena<T> {
|
||||
type Item = (usize, T);
|
||||
type IntoIter = IntoIter<T>;
|
||||
|
||||
fn into_iter(mut self) -> Self::IntoIter {
|
||||
let iter = IntoIter {
|
||||
slots: self.slots,
|
||||
bitmap: mem::replace(&mut self.bitmap, Bitmap::new()),
|
||||
index: 0,
|
||||
marker: PhantomData,
|
||||
};
|
||||
mem::forget(self);
|
||||
iter
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter { slots: self.slots.into_iter().enumerate() }
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over references to the occupied slots in a `Arena`.
|
||||
pub struct Iter<'a, T: 'a> {
|
||||
arena: &'a VecArena<T>,
|
||||
index: usize,
|
||||
slots: iter::Enumerate<slice::Iter<'a, Slot<T>>>,
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for Iter<'a, T> {
|
||||
|
@ -376,27 +506,18 @@ impl<'a, T> Iterator for Iter<'a, T> {
|
|||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.arena.bitmap.next_occupied(self.index).map(|index| {
|
||||
self.index = index + 1;
|
||||
unsafe {
|
||||
(index, self.arena.get_unchecked(index))
|
||||
while let Some((index, slot)) = self.slots.next() {
|
||||
if let Slot::Occupied(ref object) = *slot {
|
||||
return Some((index, object));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let lower = 0;
|
||||
let upper = self.arena.capacity() - self.index;
|
||||
(lower, Some(upper))
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over mutable references to the occupied slots in a `Arena`.
|
||||
pub struct IterMut<'a, T: 'a> {
|
||||
slots: *mut T,
|
||||
bitmap: &'a Bitmap,
|
||||
index: usize,
|
||||
marker: PhantomData<&'a mut T>,
|
||||
slots: iter::Enumerate<slice::IterMut<'a, Slot<T>>>,
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for IterMut<'a, T> {
|
||||
|
@ -404,46 +525,12 @@ impl<'a, T> Iterator for IterMut<'a, T> {
|
|||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.bitmap.next_occupied(self.index).map(|index| {
|
||||
self.index = index + 1;
|
||||
unsafe {
|
||||
(index, &mut *self.slots.offset(index as isize))
|
||||
while let Some((index, slot)) = self.slots.next() {
|
||||
if let Slot::Occupied(ref mut object) = *slot {
|
||||
return Some((index, object));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let lower = 0;
|
||||
let upper = self.bitmap.len() - self.index;
|
||||
(lower, Some(upper))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Drain<'a, T: 'a> {
|
||||
arena: &'a mut VecArena<T>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for Drain<'a, T> {
|
||||
type Item = (usize, T);
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.arena.bitmap.next_occupied(self.index).map(|index| {
|
||||
self.index = index + 1;
|
||||
unsafe {
|
||||
self.arena.bitmap.release(index);
|
||||
(index, ptr::read(self.arena.slots.offset(index as isize)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let lower = 0;
|
||||
let upper = self.arena.capacity() - self.index;
|
||||
(lower, Some(upper))
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -451,11 +538,9 @@ impl<'a, T> Iterator for Drain<'a, T> {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// TODO: test variance: https://github.com/rust-lang/rust/pull/30998/files
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let mut arena = VecArena::new();
|
||||
let mut arena = Arena::new();
|
||||
for i in 0..10 {
|
||||
assert_eq!(arena.insert(()), i);
|
||||
assert!(arena[i] == ());
|
||||
|
|
Loading…
Reference in New Issue