use crate::buffers::RUMBuffer;
use crate::constants::KB;
use crate::mem::{as_slice, as_slice_mut, cast_to_nonnull, direct_alloc, AsPtr, AsSlice, SizedType};
use crate::rumtk_layout;
use std::alloc::AllocError;
use std::ops::Index;
use std::ops::{Range, RangeFrom, RangeFull, RangeTo, RangeToInclusive};
use std::ptr::NonNull;
pub const DEFAULT_ARENA_MEMORY_ALLOCATION: usize = 4 * KB;
pub type ArenaResult<T> = Result<T, AllocError>;
pub type ArenaBaseAddress = *const u8;
#[derive(Debug)]
pub struct Arena {
memory: RUMBuffer,
remaining: usize,
capacity: usize,
}
impl Arena {
pub fn new() -> Self {
Self::with_capacity(DEFAULT_ARENA_MEMORY_ALLOCATION)
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
memory: RUMBuffer::from_parts(unsafe { direct_alloc(rumtk_layout!(capacity)) }, capacity, true),
remaining: capacity,
capacity,
}
}
#[inline]
pub const fn null() -> Self {
Self {
memory: RUMBuffer::new(),
remaining: 0,
capacity: 0,
}
}
#[inline]
pub fn from_parts(ptr: *mut u8, capacity: usize, dealloc: bool) -> Self {
Self {
memory: RUMBuffer::from_parts(ptr, capacity, dealloc),
remaining: capacity,
capacity,
}
}
#[inline]
pub fn split_to(&mut self, len: usize) -> Self {
match self.memory.split_to(len) {
Some(new_buffer) => {
self.remaining -= len;
self.capacity -= len;
Self {
memory: new_buffer,
remaining: len,
capacity: len,
}
},
None => {
Self {
memory: RUMBuffer::new(),
remaining: 0,
capacity: 0,
}
}
}
}
#[inline]
pub fn freeze(&mut self) -> Self {
Self {
memory: self.memory.freeze(),
remaining: self.remaining,
capacity: self.capacity,
}
}
#[inline(always)]
pub fn remaining(&self) -> usize {
self.remaining
}
#[inline(always)]
pub fn capacity(&self) -> usize {
self.capacity
}
#[inline(always)]
pub fn can_allocate(&self, size: usize) -> bool {
let remaining = self.remaining();
remaining >= size
}
#[inline(always)]
pub fn commit(&mut self, size: usize) -> ArenaResult<*mut [u8]> {
if self.can_allocate(size) {
let lower_bound = self.capacity - self.remaining;
let upper_bound = lower_bound + size;
let slice = &mut self.memory[lower_bound..upper_bound];
self.remaining -= size;
Ok(slice)
} else {
eprintln!("Cannot allocate {} bytes due to lack of space!", size);
Err(AllocError)
}
}
pub fn write_bytes(&mut self, src: *const u8, data_length: usize) -> ArenaResult<*mut [u8]> {
let dst = self.commit(data_length)?;
unsafe {
std::ptr::copy_nonoverlapping(
src,
dst.as_mut_ptr(),
data_length,
);
}
Ok(dst)
}
pub fn write<T>(&mut self, data: T) -> ArenaResult<NonNull<T>> {
let data_length = size_of::<T>();
let src = std::ptr::addr_of!(data).cast::<u8>();
let mem = cast_to_nonnull(self.write_bytes(src, data_length)?);
Ok(mem.cast())
}
#[inline(always)]
pub fn uncommit(&mut self, length: usize) {
let new_lower_bound = self.remaining() - (length % self.len());
self.remaining = new_lower_bound;
}
#[inline(always)]
pub fn reset(&mut self) {
self.remaining = self.capacity;
}
#[inline(always)]
pub fn address(&self) -> ArenaBaseAddress {
self.as_ptr()
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.remaining() == 0
}
#[inline(always)]
pub fn len(&self) -> usize {
self.capacity()
}
}
impl AsSlice for Arena {
#[inline(always)]
fn as_slice(&self) -> &'static [u8] { as_slice(self.as_ptr(), self.size()) }
#[inline(always)]
fn as_slice_mut(&mut self) -> &'static mut [u8] { as_slice_mut(self.as_mut_ptr(), self.size()) }
#[inline(always)]
fn contains(&self, x: &u8) -> bool {
self.as_slice().contains(x)
}
}
impl AsPtr for Arena {
#[inline(always)]
fn as_ptr(&self) -> *const u8 {
self.memory.as_ptr()
}
#[inline(always)]
fn as_mut_ptr(&mut self) -> *mut u8 {
self.memory.as_mut_ptr()
}
}
impl SizedType for Arena {
#[inline(always)]
fn size(&self) -> usize {
self.capacity
}
}
impl Default for Arena {
fn default() -> Self {
Self::new()
}
}
unsafe impl Send for Arena {}
unsafe impl Sync for Arena {}
impl Index<usize> for Arena {
type Output = u8;
#[inline]
fn index(&self, i: usize) -> & Self::Output {
&self.as_slice()[i]
}
}
impl Index<Range<usize>> for Arena {
type Output = [u8];
#[inline]
fn index(&self, i: Range<usize>) -> & Self::Output {
&self.as_slice()[i.start..i.end]
}
}
impl Index<RangeTo<usize>> for Arena {
type Output = [u8];
#[inline]
fn index(&self, i: RangeTo<usize>) -> & Self::Output {
&self.as_slice()[..i.end]
}
}
impl Index<RangeFrom<usize>> for Arena {
type Output = [u8];
#[inline]
fn index(&self, i: RangeFrom<usize>) -> & Self::Output {
&self.as_slice()[i.start..]
}
}
impl Index<RangeToInclusive<usize>> for Arena {
type Output = [u8];
#[inline]
fn index(&self, i: RangeToInclusive<usize>) -> & Self::Output {
&self.as_slice()[..=i.end]
}
}
impl Index<RangeFull> for Arena {
type Output = [u8];
#[inline]
fn index(&self, i: RangeFull) -> & Self::Output {
self.as_slice()
}
}
#[macro_export]
macro_rules! rumtk_arena_new {
( ) => {{
use $crate::arena::Arena;
Arena::new()
}};
( $capacity:expr ) => {{
use $crate::arena::Arena;
Arena::with_capacity($capacity)
}};
( $ptr:expr, $capacity:expr, $dealloc:expr ) => {{
use $crate::arena::Arena;
Arena::from_parts($ptr, $capacity, $dealloc)
}};
}