use memmap2::MmapMut;
use std::alloc::{AllocError, Allocator};
use std::alloc::{GlobalAlloc, Layout};
use std::io::{Read, Write};
use std::ptr::NonNull;
use std::sync::{Arc, RwLock};
pub const ONE_KB: usize = 1024;
pub const ONE_MB: usize = 1024 * ONE_KB;
pub const ONE_GB: usize = 1024 * ONE_MB;
pub const DEFAULT_ARENA_MEMORY_ALLOCATION: usize = 4 * ONE_KB;
#[inline(always)]
pub fn cast_to_nonnull<T: ?Sized>(dst: *mut T) -> NonNull<T> {
match NonNull::new(dst) {
Some(ptr) => ptr,
None => panic!("Failed to allocate memory"),
}
}
#[inline(always)]
pub fn cast_data_to_ptr<T>(data: &T) -> *const u8 {
std::ptr::addr_of!(*data).cast::<u8>()
}
#[inline(always)]
pub fn get_data_length<T>(data: &T) -> usize {
size_of::<T>()
}
#[inline(always)]
pub fn zero_memory(data: *mut [u8], offset: usize, length: usize) -> *mut [u8] {
let chunk = unsafe { &mut *data };
for i in offset..offset + length {
chunk[i] = 0;
}
data
}
pub type ArenaResult<T> = Result<T, AllocError>;
pub type ArenaBaseAddress = *const u8;
#[derive(Debug)]
pub struct ArenaAlloc {
memory: MmapMut,
capacity: usize,
used: usize,
}
impl ArenaAlloc {
pub fn new() -> Self {
Self::with_capacity(DEFAULT_ARENA_MEMORY_ALLOCATION)
}
pub fn with_capacity(capacity: usize) -> Self {
let memory = match MmapMut::map_anon(capacity) {
Ok(m) => m,
Err(_) => panic!("Failed to map memory"),
};
Self {
memory,
capacity,
used: 0,
}
}
#[inline(always)]
pub fn remaining(&self) -> usize {
self.capacity - self.used
}
#[inline(always)]
pub fn capacity(&self) -> usize {
self.capacity
}
#[inline(always)]
pub fn can_allocate(&self, size: usize) -> ArenaResult<bool> {
let remaining = self.remaining();
let can_allocate = remaining >= size;
if can_allocate {
Ok(can_allocate)
} else {
Err(AllocError)
}
}
#[inline(always)]
pub fn commit(&mut self, size: usize) -> ArenaResult<*mut [u8]> {
self.can_allocate(size)?;
let ptr = &mut self.memory[self.used..self.used+size];
self.used += size;
Ok(ptr)
}
#[inline(always)]
pub fn grow(&mut self, old_size: usize, new_size: usize) -> ArenaResult<*mut [u8]> {
self.uncommit(old_size);
self.commit(new_size)
}
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) {
self.used -= length;
}
#[inline(always)]
pub fn reset(&mut self) {
self.used = 0;
}
pub fn address(&self) -> ArenaBaseAddress {
self.memory.as_ptr()
}
pub fn is_empty(&self) -> bool {
self.used == 0
}
}
impl Default for ArenaAlloc {
fn default() -> Self {
Self::new()
}
}
type ArenaRef = Arc<RwLock<ArenaAlloc>>;
#[derive(Debug, Default)]
pub struct Arena {
memory: ArenaRef
}
impl Arena {
pub fn new() -> Self {
Self {
memory: ArenaRef::new(RwLock::new(ArenaAlloc::new()))
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
memory: ArenaRef::new(RwLock::new(ArenaAlloc::with_capacity(capacity)))
}
}
#[inline(always)]
pub fn commit(&self, size: usize) -> ArenaResult<*mut [u8]> {
self.memory.write().unwrap().commit(size)
}
#[inline(always)]
pub fn grow_block(&self, old_size: usize, new_size: usize) -> ArenaResult<*mut [u8]> {
self.memory.write().unwrap().grow(old_size, new_size)
}
#[inline(always)]
pub fn write<T>(&self, data: T) -> ArenaResult<NonNull<T>> {
self.memory.write().unwrap().write(data)
}
#[inline(always)]
pub fn uncommit(&self, length: usize) {
self.memory.write().unwrap().uncommit(length)
}
#[inline(always)]
pub fn reset(&self) {
self.memory.write().unwrap().reset()
}
#[inline(always)]
pub fn remaining(&self) -> usize {
self.memory.read().unwrap().remaining()
}
#[inline(always)]
pub fn capacity(&self) -> usize {
self.memory.read().unwrap().capacity()
}
#[inline(always)]
pub fn address(&self) -> ArenaBaseAddress {
self.memory.read().unwrap().address()
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.memory.read().unwrap().is_empty()
}
}
impl PartialEq for Arena {
fn eq(&self, other: &Self) -> bool {
self.address() == other.address() && self.capacity() == other.capacity()
}
}
unsafe impl Allocator for Arena {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let r = self.commit(layout.size())?;
let nz_r = cast_to_nonnull(r);
Ok(nz_r)
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
self.uncommit(layout.size());
}
fn allocate_zeroed(
&self,
layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
let length = layout.size();
let allocated = self.commit(length)?;
zero_memory(allocated, 0, length);
Ok(cast_to_nonnull(allocated))
}
unsafe fn grow(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
let new_ptr = self.grow_block(old_layout.size(), new_layout.size())?;
let nz_new_ptr = cast_to_nonnull(new_ptr);
Ok(nz_new_ptr)
}
unsafe fn grow_zeroed(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
let new_ptr = zero_memory(self.grow_block(old_layout.size(), new_layout.size())?, old_layout.size(), new_layout.size());
Ok(cast_to_nonnull(new_ptr))
}
unsafe fn shrink(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
self.uncommit(old_layout.size());
Ok(cast_to_nonnull(self.commit(new_layout.size())?))
}
fn by_ref(&self) -> &Self
where Self: Sized { &self }
}
unsafe impl GlobalAlloc for Arena {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
self.commit(layout.size()).unwrap().as_mut_ptr()
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.uncommit(layout.size());
}
}
#[macro_export]
macro_rules! rumtk_arena_new {
( ) => {{
use $crate::arena::Arena;
Arena::new()
}};
( $capacity:expr ) => {{
use $crate::arena::Arena;
Arena::with_capacity($capacity)
}};
}