#![no_std]
use core::alloc::Layout;
use core::fmt::Debug;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ops::{Index, IndexMut};
use core::ptr;
use utils::{assert_const, pad_to_align};
use crate::utils::MaybeUninitExt;
mod id;
mod storage;
mod utils;
pub use id::{Id, RawId, SupportedType};
pub use storage::{SliceStorage, SliceStorageError, Storage};
#[cfg(feature = "alloc")]
pub use storage::VecStorage;
pub struct Arena<M, S> {
storage: S,
_tag: PhantomData<M>,
}
impl<M, S> Arena<M, S> {
#[inline]
pub unsafe fn with_storage(storage: S) -> Self {
Arena {
storage,
_tag: PhantomData,
}
}
}
impl<M, S: Default> Arena<M, S> {
#[inline]
pub unsafe fn new() -> Self {
unsafe { Arena::with_storage(S::default()) }
}
}
impl<M, S: Storage> Arena<M, S> {
#[inline]
pub fn get<T: ?Sized + SupportedType>(&self, id: Id<T, M>) -> &T {
unsafe { id.get(self.storage.view()) }
}
#[inline]
pub fn get_mut<T: ?Sized + SupportedType>(&mut self, id: Id<T, M>) -> &mut T {
unsafe { id.get_mut(self.storage.view_mut()) }
}
#[inline]
pub fn try_alloc<T>(&mut self, item: T) -> Result<Id<T, M>, S::AllocError> {
let uninit_id = self.try_alloc_uninit::<T>()?;
unsafe {
ptr::write(self.get_mut(uninit_id).as_mut_ptr(), item);
}
Ok(unsafe { uninit_id.assume_init() })
}
#[inline]
pub fn try_alloc_slice<T: Clone>(&mut self, slice: &[T]) -> Result<Id<[T], M>, S::AllocError> {
let uninit_id = self.try_alloc_slice_uninit(slice.len())?;
<MaybeUninit<T> as MaybeUninitExt<T>>::clone_from_slice(self.get_mut(uninit_id), slice);
Ok(unsafe { uninit_id.assume_init() })
}
#[inline]
pub fn try_alloc_str(&mut self, str: &str) -> Result<Id<str, M>, S::AllocError> {
let slice_id = self.try_alloc_slice(str.as_bytes())?;
Ok(unsafe { Id::new_str(slice_id) })
}
#[inline]
fn try_alloc_uninit<T>(&mut self) -> Result<Id<MaybeUninit<T>, M>, S::AllocError> {
assert_const!(align_of::<T>() <= S::ALIGN);
if const { size_of::<T>() == 0 } {
return unsafe { Ok(Id::new_sized(0)) };
}
let byte_offset = unsafe { self.try_alloc_layout(Layout::new::<T>())? };
Ok(unsafe { Id::new_sized(byte_offset) })
}
#[inline]
fn try_alloc_slice_uninit<T>(
&mut self,
len: usize,
) -> Result<Id<[MaybeUninit<T>], M>, S::AllocError> {
assert_const!(align_of::<T>() <= S::ALIGN);
if const { size_of::<T>() == 0 } {
return unsafe { Ok(Id::new_slice(0, len)) };
}
let layout = Layout::array::<T>(len).unwrap();
let byte_offset = unsafe { self.try_alloc_layout(layout)? };
Ok(unsafe { Id::new_slice(byte_offset, len) })
}
#[inline]
unsafe fn try_alloc_layout(&mut self, layout: Layout) -> Result<usize, S::AllocError> {
let old_size = self.storage.view().len();
let padding = unsafe { pad_to_align(old_size, layout.align()) };
let padded_size = unsafe { layout.size().unchecked_add(padding) };
self.storage.try_grow_by(padded_size)?;
Ok(unsafe { old_size.unchecked_add(padding) })
}
}
impl<M, S> Arena<M, S>
where
S: Storage,
S::AllocError: Debug,
{
#[inline]
pub fn alloc<T>(&mut self, item: T) -> Id<T, M> {
self.try_alloc(item).unwrap()
}
#[inline]
pub fn alloc_slice<T: Clone>(&mut self, slice: &[T]) -> Id<[T], M> {
self.try_alloc_slice(slice).unwrap()
}
#[inline]
pub fn alloc_str(&mut self, str: &str) -> Id<str, M> {
self.try_alloc_str(str).unwrap()
}
}
impl<T, M, S> Index<Id<T, M>> for Arena<M, S>
where
T: ?Sized + SupportedType,
S: Storage,
{
type Output = T;
#[inline]
fn index(&self, id: Id<T, M>) -> &Self::Output {
self.get(id)
}
}
impl<T, M, S> IndexMut<Id<T, M>> for Arena<M, S>
where
T: ?Sized + SupportedType,
S: Storage,
{
#[inline]
fn index_mut(&mut self, id: Id<T, M>) -> &mut Self::Output {
self.get_mut(id)
}
}
#[doc(hidden)]
pub const DEFAULT_STORAGE_ALIGN: usize = 128;
#[macro_export]
macro_rules! new_arena {
() => {{
const ALIGN: usize = $crate::DEFAULT_STORAGE_ALIGN;
let storage = $crate::VecStorage::<ALIGN>::new();
$crate::new_arena!(storage = storage)
}};
(storage = $storage:expr) => {{
struct M;
let storage = $storage;
unsafe { $crate::Arena::<M, _>::with_storage(storage) }
}};
}
#[cfg(test)]
mod test {
use super::*;
use crate::storage::SliceStorage;
#[test]
fn arena_alloc_one_u8() {
let mut arena = new_arena!();
let id = arena.alloc(123u8);
assert_eq!(arena.get(id), &123u8);
}
#[test]
fn arena_alloc_one_u64() {
let mut arena = new_arena!();
let id = arena.alloc(u64::MAX);
assert_eq!(arena.get(id), &u64::MAX);
}
#[test]
fn arena_alloc_mut() {
let mut arena = new_arena!();
let id = arena.alloc(456u32);
assert_eq!(arena.get(id), &456u32);
*arena.get_mut(id) += 234;
assert_eq!(*arena.get(id), 456u32 + 234);
}
#[test]
fn arena_alloc_multiple_sized() {
let mut arena = new_arena!();
let a_id = arena.alloc(12u16);
let b_id = arena.alloc("hell");
let c_id = arena.alloc(131u128);
assert_eq!(arena.get(b_id), &"hell");
assert_eq!(arena.get(a_id), &12u16);
assert_eq!(arena.get(c_id), &131u128);
}
#[test]
fn arena_alloc_multiple_sized_mut() {
let mut arena = new_arena!();
let a_id = arena.alloc(12u16);
let b_id = arena.alloc("hell");
let c_id = arena.alloc(131u128);
assert_eq!(arena[b_id], "hell");
assert_eq!(arena[a_id], 12u16);
assert_eq!(arena[c_id], 131u128);
*arena.get_mut(c_id) = 1;
assert_eq!(arena.get(c_id), &1);
*arena.get_mut(b_id) = "heaven";
*arena.get_mut(a_id) *= 3;
assert_eq!(arena.get(b_id), &"heaven");
assert_eq!(*arena.get(a_id), 12u16 * 3);
}
#[test]
fn arena_alloc_slice() {
let mut arena = new_arena!();
let fruits = ["banana", "orange", "apple"];
let id = arena.alloc_slice(&fruits);
assert_eq!(arena[id][1], "orange");
}
#[test]
fn arena_alloc_slice_mut() {
let mut arena = new_arena!();
let numbers = [12i64, -451i64, 0i64];
let id = arena.alloc_slice(&numbers);
assert_eq!(arena[id][1], -451i64);
arena[id][1] = 3i64;
assert_eq!(arena[id][1], 3i64);
}
#[test]
fn arena_alloc_multiple() {
let mut arena = new_arena!();
let counter = arena.alloc(123);
let fruits = arena.alloc_slice(&["banana", "orange", "apple"]);
assert_eq!(arena[counter], 123);
assert_eq!(arena[fruits], ["banana", "orange", "apple"]);
}
#[test]
fn arena_alloc_multiple_mut() {
let mut arena = new_arena!();
let counter = arena.alloc(123);
let fruits = arena.alloc_slice(&["banana", "orange", "apple"]);
assert_eq!(arena[counter], 123);
assert_eq!(arena[fruits], ["banana", "orange", "apple"]);
arena[counter] = 43;
assert_eq!(arena[counter], 43);
arena[fruits][0] = "pineapple";
assert_eq!(arena[fruits][0], "pineapple");
}
#[test]
fn arena_alloc_str() {
let mut arena = new_arena!();
let hello = arena.alloc_str("Hello!");
assert_eq!(&arena[hello], "Hello!");
}
#[test]
fn arena_alloc_slice_storage() {
let mut buf = [MaybeUninit::uninit(); 1024];
let storage = SliceStorage::from_unaligned_bytes(&mut buf).unwrap();
let mut arena = new_arena!(storage = storage);
let counter = arena.alloc(123);
let fruits = arena.alloc_slice(&["banana", "orange", "apple"]);
assert_eq!(arena[counter], 123);
assert_eq!(arena[fruits], ["banana", "orange", "apple"]);
arena[counter] = 43;
assert_eq!(arena[counter], 43);
arena[fruits][0] = "pineapple";
assert_eq!(arena[fruits][0], "pineapple");
let hello = arena.alloc_str("Hello!");
assert_eq!(&arena[hello], "Hello!");
}
#[test]
#[should_panic]
fn arena_alloc_slice_storage_out_of_memory() {
#[repr(align(128))]
struct AlignedSlice([MaybeUninit<u8>; 128]);
assert!(align_of::<AlignedSlice>() == 128);
let mut buf: AlignedSlice = AlignedSlice([MaybeUninit::uninit(); 128]);
let storage = SliceStorage::from_unaligned_bytes(&mut buf.0).unwrap();
let mut arena = new_arena!(storage = storage);
arena.alloc_slice(&[0; 256]);
}
#[test]
fn arena_alloc_slice_storage_out_of_memory_fallible() {
#[repr(align(128))]
struct AlignedSlice([MaybeUninit<u8>; 128]);
assert!(align_of::<AlignedSlice>() == 128);
let mut buf: AlignedSlice = AlignedSlice([MaybeUninit::uninit(); 128]);
let storage = SliceStorage::from_unaligned_bytes(&mut buf.0).unwrap();
let mut arena = new_arena!(storage = storage);
assert!(arena.try_alloc_slice(&[0; 256]).is_err());
}
#[test]
fn arena_alloc_zst() {
let mut arena = new_arena!();
let a = arena.alloc(());
let _ = arena.get(a);
let b = arena.alloc_slice(&[(); 1024]);
let _ = arena.get(b);
}
}