use std::mem;
use std::ops::{Index, IndexMut};
use std::vec::Vec;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Entry {
chunk_index: usize,
block_index: usize,
}
enum Block<T> {
Occupied(T),
Vacant(Option<Entry>),
}
pub struct TypedArena<T> {
head: Option<Entry>,
chunks: Vec<Vec<Block<T>>>,
chunk_size: usize,
size: usize,
capacity: usize,
}
impl<T> TypedArena<T> {
fn is_valid_entry(&self, entry: &Entry) -> bool {
entry.chunk_index < self.chunks.len() && entry.block_index < self.chunks[entry.chunk_index].len()
}
pub fn new(chunk_size: usize) -> Self {
TypedArena {
head: None,
chunks: Vec::new(),
chunk_size,
size: 0,
capacity: 0,
}
}
pub fn allocate(&mut self, value: T) -> Entry {
if self.size == self.capacity {
self.chunks.push(Vec::with_capacity(self.chunk_size));
self.capacity += self.chunk_size;
}
self.size += 1;
match self.head.take() {
None => {
let chunk_count = self.chunks.len();
let mut last_chunk = &mut self.chunks[chunk_count - 1];
last_chunk.push(Block::Occupied(value));
Entry {
chunk_index: chunk_count - 1,
block_index: last_chunk.len() - 1,
}
},
Some(entry) => {
let vacant_block = mem::replace(
&mut self.chunks[entry.chunk_index][entry.block_index],
Block::Occupied(value),
);
match vacant_block {
Block::Vacant(next_entry) => {
let ret = entry;
self.head = next_entry;
ret
},
Block::Occupied(_) => unreachable!(),
}
},
}
}
pub fn free(&mut self, entry: &Entry) -> T {
if !self.is_valid_entry(entry) {
panic!("Attempting to free invalid block.");
}
let old_block = mem::replace(
&mut self.chunks[entry.chunk_index][entry.block_index],
Block::Vacant(self.head.take()),
);
match old_block {
Block::Vacant(_) => panic!("Attempting to free vacant block."),
Block::Occupied(value) => {
self.size -= 1;
self.head = Some(Entry {
chunk_index: entry.chunk_index,
block_index: entry.block_index,
});
value
},
}
}
pub fn get(&self, entry: &Entry) -> Option<&T> {
if !self.is_valid_entry(entry) {
return None;
}
match self.chunks[entry.chunk_index][entry.block_index] {
Block::Occupied(ref value) => Some(value),
Block::Vacant(_) => None,
}
}
pub fn get_mut(&mut self, entry: &Entry) -> Option<&mut T> {
if !self.is_valid_entry(entry) {
return None;
}
match self.chunks[entry.chunk_index][entry.block_index] {
Block::Occupied(ref mut value) => Some(value),
Block::Vacant(_) => None,
}
}
}
impl<T> Index<Entry> for TypedArena<T> {
type Output = T;
fn index(&self, entry: Entry) -> &Self::Output {
self.get(&entry).expect("Entry out of bounds.")
}
}
impl<T> IndexMut<Entry> for TypedArena<T> {
fn index_mut(&mut self, entry: Entry) -> &mut Self::Output {
self.get_mut(&entry).expect("Entry out of bounds.")
}
}
#[cfg(test)]
mod tests {
use super::Entry;
use super::TypedArena;
#[test]
#[should_panic]
fn test_free_invalid_block() {
let mut arena: TypedArena<u32> = TypedArena::new(1024);
arena.free(&Entry { chunk_index: 0, block_index: 0 });
}
#[test]
#[should_panic]
fn test_free_vacant_block() {
let mut arena = TypedArena::new(1024);
arena.allocate(0);
arena.free(&Entry { chunk_index: 0, block_index: 1 });
}
#[test]
fn test_insert() {
let mut pool = TypedArena::new(1024);
assert_eq!(pool.allocate(0), Entry { chunk_index: 0, block_index: 0 });
assert_eq!(pool.allocate(0), Entry { chunk_index: 0, block_index: 1 });
assert_eq!(pool.allocate(0), Entry { chunk_index: 0, block_index: 2 });
}
#[test]
fn test_insert_multiple_chunks() {
let mut pool = TypedArena::new(2);
assert_eq!(pool.allocate(0), Entry { chunk_index: 0, block_index: 0 });
assert_eq!(pool.allocate(0), Entry { chunk_index: 0, block_index: 1 });
assert_eq!(pool.allocate(0), Entry { chunk_index: 1, block_index: 0 });
}
#[test]
fn test_free() {
let mut pool = TypedArena::new(1024);
let entry = pool.allocate(0);
assert_eq!(entry, Entry { chunk_index: 0, block_index: 0 });
assert_eq!(pool.free(&entry), 0);
assert_eq!(pool.allocate(0), entry);
}
#[test]
fn test_get() {
let mut pool = TypedArena::new(1024);
let entry = pool.allocate(0);
assert_eq!(pool.get(&entry), Some(&0));
}
#[test]
fn test_get_invalid_block() {
let pool: TypedArena<u32> = TypedArena::new(1024);
assert_eq!(pool.get(&Entry { chunk_index: 0, block_index: 0 }), None);
}
#[test]
fn test_get_vacant_block() {
let mut pool = TypedArena::new(1024);
pool.allocate(0);
assert_eq!(pool.get(&Entry { chunk_index: 0, block_index: 1 }), None);
}
#[test]
fn test_get_mut() {
let mut pool = TypedArena::new(1024);
let entry = pool.allocate(0);
*pool.get_mut(&entry).unwrap() = 1;
assert_eq!(pool.get(&entry), Some(&1));
}
#[test]
fn test_get_mut_invalid_block() {
let mut pool: TypedArena<u32> = TypedArena::new(1024);
assert_eq!(pool.get_mut(&Entry { chunk_index: 0, block_index: 0 }), None);
}
#[test]
fn test_get_mut_vacant_block() {
let mut pool = TypedArena::new(1024);
pool.allocate(0);
assert_eq!(pool.get_mut(&Entry { chunk_index: 0, block_index: 1 }), None);
}
}