#![no_std]
#![deny(unsafe_code)]
#![allow(clippy::while_let_on_iterator)]
mod chain;
mod entry;
pub mod iterators;
mod misc;
mod ptr;
pub use chain::{ChainArena, Link};
pub(crate) use entry::InternalEntry;
pub use ptr::{Ptr, PtrGen, PtrInx};
extern crate alloc;
use alloc::vec::Vec;
use core::{
borrow::Borrow,
fmt, mem,
ops::{Index, IndexMut},
};
use InternalEntry::*;
pub struct Arena<P: Ptr, T> {
m: Vec<InternalEntry<P, T>>,
len: usize,
freelist_root: Option<P::Inx>,
gen: P::Gen,
}
impl<P: Ptr, T> Arena<P, T> {
#[doc(hidden)]
pub fn _check_invariants(this: &Self) -> Result<(), &'static str> {
if this.gen() < P::Gen::two() {
return Err("bad generation")
}
if this.capacity() != this.m.len() {
return Err("virtual capacity != m_len")
}
let mut n_allocated = 0;
for entry in &this.m {
n_allocated += matches!(entry, Allocated(..)) as usize;
}
let n_free = this.m.len() - n_allocated;
if this.len() != n_allocated {
return Err("len != n_allocated")
}
let mut freelist_len = 0;
if let Some(root) = this.freelist_root {
let mut tmp_inx = root;
for i in 0.. {
let entry = this.m.get(P::Inx::get(tmp_inx)).unwrap();
if let Free(inx) = entry {
freelist_len += 1;
if *inx == tmp_inx {
break
}
tmp_inx = *inx;
} else {
return Err("bad freelist node")
}
if i > this.m.len() {
return Err("endless loop")
}
}
}
if freelist_len != n_free {
return Err("freelist discontinuous")
}
Ok(())
}
pub fn new() -> Arena<P, T> {
Arena {
len: PtrInx::new(0),
m: Vec::new(),
freelist_root: None,
gen: PtrGen::two(),
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
PtrInx::get(self.len) == 0
}
pub fn capacity(&self) -> usize {
self.m.len()
}
#[inline]
pub fn gen(&self) -> P::Gen {
self.gen
}
#[inline]
fn inc_gen(&mut self) {
self.gen = PtrGen::increment(self.gen);
}
pub fn reserve(&mut self, additional: usize) {
let end = self.m.len();
let cap = self.m.capacity();
let target = end.checked_add(additional).unwrap_or(usize::MAX).clamp(
0,
<P::Inx as PtrInx>::max()
.checked_add(1)
.unwrap_or(usize::MAX),
);
let reserve_amt = if target <= cap {
0
} else {
target.wrapping_sub(cap)
};
if reserve_amt > 0 {
self.m.reserve(reserve_amt);
}
let remaining = target.wrapping_sub(end);
if remaining > 0 {
let old_root = self.freelist_root;
self.freelist_root = Some(P::Inx::new(end));
for i in 1..remaining {
self.m.push(Free(P::Inx::new(end.wrapping_add(i))));
}
match old_root {
Some(old_root) => {
self.m.push(Free(old_root));
}
None => {
self.m.push(Free(P::Inx::new(target.wrapping_sub(1))));
}
}
}
}
#[must_use]
fn m_get(&self, inx: P::Inx) -> Option<&InternalEntry<P, T>> {
self.m.get(P::Inx::get(inx))
}
#[must_use]
fn m_get_mut(&mut self, inx: P::Inx) -> Option<&mut InternalEntry<P, T>> {
self.m.get_mut(P::Inx::get(inx))
}
#[inline]
fn unwrap_replace_free(&mut self, inx: P::Inx, gen: P::Gen, t: T) {
let next = self
.m_get_mut(inx)
.unwrap()
.replace_free_with_allocated(gen, t)
.unwrap();
if next == inx {
self.freelist_root = None;
} else {
self.freelist_root = Some(next);
}
}
pub fn try_insert(&mut self, t: T) -> Result<P, T> {
if let Some(inx) = self.freelist_root {
self.unwrap_replace_free(inx, self.gen(), t);
self.len += 1;
Ok(Ptr::_from_raw(inx, self.gen()))
} else {
Err(t)
}
}
pub fn try_insert_with<F: FnOnce(P) -> T>(&mut self, create: F) -> Result<P, F> {
if let Some(inx) = self.freelist_root {
let ptr = P::_from_raw(inx, self.gen());
self.unwrap_replace_free(inx, self.gen(), create(ptr));
self.len += 1;
Ok(ptr)
} else {
Err(create)
}
}
pub fn insert(&mut self, t: T) -> P {
match self.try_insert(t) {
Ok(inx) => inx,
Err(t) => {
let mut additional = self.m.len();
if additional == 0 {
additional = 1;
}
self.reserve(additional);
match self.try_insert(t) {
Ok(p) => p,
Err(_) => panic!(
"called `insert` on an `Arena<P, T>` with maximum length `P::Inx::max() + \
1`"
),
}
}
}
}
pub fn insert_with<F: FnOnce(P) -> T>(&mut self, create: F) -> P {
let inx = if let Some(inx) = self.freelist_root {
inx
} else {
let mut additional = self.m.len();
if additional == 0 {
additional = 1;
}
self.reserve(additional);
self.freelist_root.unwrap()
};
let ptr = P::_from_raw(inx, self.gen());
self.unwrap_replace_free(inx, self.gen(), create(ptr));
self.len += 1;
ptr
}
pub fn contains(&self, p: P) -> bool {
match self.m_get(p.inx()) {
Some(Allocated(gen, _)) => *gen == p.gen(),
_ => false,
}
}
#[must_use]
pub fn get(&self, p: P) -> Option<&T> {
match self.m_get(p.inx()) {
Some(Allocated(gen, t)) => {
if *gen == p.gen() {
Some(t)
} else {
None
}
}
_ => None,
}
}
#[must_use]
pub fn get_mut(&mut self, p: P) -> Option<&mut T> {
let p = *p.borrow();
match self.m_get_mut(p.inx()) {
Some(Allocated(gen, t)) => {
if *gen == p.gen() {
Some(t)
} else {
None
}
}
_ => None,
}
}
#[must_use]
pub fn get2_mut(&mut self, p0: P, p1: P) -> Option<(&mut T, &mut T)> {
if self.contains(p0) && self.contains(p1) && (p0.inx() != p1.inx()) {
if p0.inx() < p1.inx() {
let (lhs, rhs) = self.m.split_at_mut(PtrInx::get(p1.inx()));
if let (Allocated(_, t0), Allocated(_, t1)) =
(&mut lhs[PtrInx::get(p0.inx())], &mut rhs[0])
{
Some((t0, t1))
} else {
unreachable!()
}
} else {
let (lhs, rhs) = self.m.split_at_mut(PtrInx::get(p0.inx()));
if let (Allocated(_, t1), Allocated(_, t0)) =
(&mut lhs[PtrInx::get(p1.inx())], &mut rhs[0])
{
Some((t0, t1))
} else {
unreachable!()
}
}
} else {
None
}
}
#[must_use]
pub fn remove(&mut self, p: P) -> Option<T> {
let freelist_ptr = if let Some(free) = self.freelist_root {
free
} else {
p.inx()
};
let allocation = self.m_get_mut(p.inx())?;
let old = mem::replace(allocation, Free(freelist_ptr));
match old {
Free(old_free) => {
*allocation = Free(old_free);
None
}
Allocated(gen, old_t) => {
if gen != p.gen() {
*allocation = Allocated(gen, old_t);
None
} else {
self.freelist_root = Some(p.inx());
self.len -= 1;
self.inc_gen();
Some(old_t)
}
}
}
}
pub fn remove_by<F: FnMut(P, &mut T) -> bool>(&mut self, mut pred: F) {
for (inx, entry) in self.m.iter_mut().enumerate() {
let inx = P::Inx::new(inx);
if let Allocated(gen, t) = entry {
if pred(P::_from_raw(inx, *gen), t) {
self.len -= 1;
if let Some(free) = self.freelist_root {
*entry = Free(free);
} else {
*entry = Free(inx);
}
self.freelist_root = Some(inx);
}
}
}
self.inc_gen();
}
#[must_use]
pub fn invalidate(&mut self, p: P) -> Option<P> {
match self.m_get(p.inx()) {
Some(Allocated(gen, _)) => {
if *gen != p.gen() {
return None
}
}
_ => return None,
}
self.inc_gen();
let new_gen = self.gen();
match self.m_get_mut(p.inx()) {
Some(Allocated(gen, _)) => {
*gen = new_gen;
Some(P::_from_raw(p.inx(), self.gen()))
}
_ => unreachable!(),
}
}
pub fn replace_and_keep_gen(&mut self, p: P, new: T) -> Result<T, T> {
let old_gen = match self.m_get(p.inx()) {
Some(Allocated(gen, _)) => {
if *gen != p.gen() {
return Err(new)
}
*gen
}
_ => return Err(new),
};
let old = mem::replace(self.m_get_mut(p.inx()).unwrap(), Allocated(old_gen, new));
match old {
Allocated(_, old_gen) => Ok(old_gen),
_ => unreachable!(),
}
}
pub fn replace_and_update_gen(&mut self, p: P, new: T) -> Result<(P, T), T> {
match self.m_get(p.inx()) {
Some(Allocated(gen, _)) => {
if *gen != p.gen() {
return Err(new)
}
}
_ => return Err(new),
}
self.inc_gen();
let new_gen = self.gen();
let old = mem::replace(self.m_get_mut(p.inx()).unwrap(), Allocated(new_gen, new));
match old {
Allocated(_, old) => Ok((P::_from_raw(p.inx(), new_gen), old)),
_ => unreachable!(),
}
}
#[must_use]
pub fn swap(&mut self, p0: P, p1: P) -> Option<()> {
if self.contains(p0) && self.contains(p1) {
if p0 != p1 {
let (p0, p1) = if p0.inx() < p1.inx() {
(p0, p1)
} else {
(p1, p0)
};
let (lhs, rhs) = self.m.split_at_mut(PtrInx::get(p1.inx()));
if let (Allocated(_, t0), Allocated(_, t1)) =
(&mut lhs[PtrInx::get(p0.inx())], &mut rhs[0])
{
mem::swap(t0, t1);
} else {
unreachable!()
}
}
Some(())
} else {
None
}
}
pub fn clear(&mut self) {
for i in 1..self.m.len() {
*self.m_get_mut(P::Inx::new(i.wrapping_sub(1))).unwrap() = Free(P::Inx::new(i));
}
if !self.m.is_empty() {
let last = self.m.len().wrapping_sub(1);
*self.m.get_mut(last).unwrap() = Free(P::Inx::new(last));
self.freelist_root = Some(P::Inx::new(0));
} else {
self.freelist_root = None;
}
self.inc_gen();
self.len = 0;
}
pub fn clear_and_shrink(&mut self) {
self.m.clear();
self.m.shrink_to_fit();
self.freelist_root = None;
self.inc_gen();
self.len = 0;
}
pub fn clone_from_with<U, F: FnMut(P, &U) -> T>(&mut self, source: &Arena<P, U>, mut map: F) {
let old_virtual_capacity = self.m.len();
self.gen = source.gen;
self.len = source.len;
self.m.clear();
if self.m.capacity() < source.m.len() {
self.m
.reserve(source.m.len().wrapping_sub(self.m.capacity()));
}
for i in 0..source.m.len() {
let new = match source.m.get(i).unwrap() {
Free(inx) => Free(*inx),
Allocated(gen, u) => Allocated(*gen, map(P::_from_raw(P::Inx::new(i), *gen), u)),
};
self.m.push(new);
}
for i in self.m.len().wrapping_add(1)..old_virtual_capacity {
self.m.push(Free(P::Inx::new(i)));
}
if self.m.len() < old_virtual_capacity {
self.freelist_root = Some(P::Inx::new(source.m.len()));
self.m.push(match source.freelist_root {
Some(inx) => Free(inx),
None => Free(P::Inx::new(self.m.len())),
});
} else {
self.freelist_root = source.freelist_root;
}
}
}
impl<T, P: Ptr> Default for Arena<P, T> {
fn default() -> Self {
Self::new()
}
}
impl<P: Ptr, T, B: Borrow<P>> Index<B> for Arena<P, T> {
type Output = T;
fn index(&self, inx: B) -> &T {
let p: P = *inx.borrow();
self.get(p).expect("indexed arena with invalidated `Ptr`")
}
}
impl<P: Ptr, T, B: Borrow<P>> IndexMut<B> for Arena<P, T> {
fn index_mut(&mut self, inx: B) -> &mut T {
let p: P = *inx.borrow();
self.get_mut(p)
.expect("indexed arena with invalidated `Ptr`")
}
}
impl<P: Ptr, T: fmt::Debug> fmt::Debug for Arena<P, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self.iter()).finish()
}
}