use alloc::alloc::{Layout, alloc, dealloc};
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::cell::{Cell, RefCell};
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ptr::NonNull;
use core::slice;
use crate::hints::likely;
use crate::nexus::effect::EffectMarker;
use crate::nexus::row::Row;
pub const REGION_BIT: u128 = 1 << 36;
#[derive(Copy, Clone, Debug)]
pub struct RegionEffect;
impl EffectMarker for RegionEffect {
const BIT: u128 = REGION_BIT;
const NAME: &'static str = "Region";
}
pub type RegionRow = Row<REGION_BIT>;
struct Chunk {
ptr: NonNull<u8>,
layout: Layout,
}
impl Chunk {
fn new(size: usize) -> Option<Self> {
if size == 0 {
return None;
}
let layout = Layout::from_size_align(size, 16).ok()?;
let ptr = unsafe { alloc(layout) };
NonNull::new(ptr).map(|ptr| Chunk { ptr, layout })
}
fn end(&self) -> *mut u8 {
unsafe { self.ptr.as_ptr().add(self.layout.size()) }
}
}
impl Drop for Chunk {
fn drop(&mut self) {
unsafe {
dealloc(self.ptr.as_ptr(), self.layout);
}
}
}
#[cfg(feature = "alloc")]
use crate::arena::{DEFAULT_CHUNK_SIZE, MIN_CHUNK_SIZE};
#[cfg(not(feature = "alloc"))]
const DEFAULT_CHUNK_SIZE: usize = 64 * 1024;
#[cfg(not(feature = "alloc"))]
const MIN_CHUNK_SIZE: usize = 4 * 1024;
pub struct Region<'r> {
chunks: RefCell<Vec<Chunk>>,
ptr: Cell<*mut u8>,
end: Cell<*mut u8>,
stats: Cell<RegionStats>,
_marker: PhantomData<&'r ()>,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct RegionStats {
pub allocations: usize,
pub bytes_allocated: usize,
pub chunks_used: usize,
}
impl<'r> Region<'r> {
fn new() -> Self {
Self::with_capacity(DEFAULT_CHUNK_SIZE)
}
fn with_capacity(capacity: usize) -> Self {
let chunk_size = capacity.max(MIN_CHUNK_SIZE);
let chunk = Chunk::new(chunk_size).expect("Failed to allocate region chunk");
let ptr = chunk.ptr.as_ptr();
let end = chunk.end();
let stats = RegionStats {
allocations: 0,
bytes_allocated: 0,
chunks_used: 1,
};
Region {
chunks: RefCell::new(alloc::vec![chunk]),
ptr: Cell::new(ptr),
end: Cell::new(end),
stats: Cell::new(stats),
_marker: PhantomData,
}
}
#[inline]
pub fn alloc<T>(&self, value: T) -> &'r mut T {
let ptr = self.alloc_raw(Layout::new::<T>());
let typed_ptr = ptr.as_ptr().cast::<T>();
unsafe {
typed_ptr.write(value);
&mut *typed_ptr
}
}
#[inline]
pub fn alloc_slice<T: Copy>(&self, slice: &[T]) -> &'r mut [T] {
if slice.is_empty() {
return &mut [];
}
let layout = Layout::array::<T>(slice.len()).expect("Invalid layout");
let ptr = self.alloc_raw(layout);
let typed_ptr = ptr.as_ptr().cast::<T>();
unsafe {
core::ptr::copy_nonoverlapping(slice.as_ptr(), typed_ptr, slice.len());
slice::from_raw_parts_mut(typed_ptr, slice.len())
}
}
#[inline]
pub fn alloc_str(&self, s: &str) -> &'r str {
let bytes = self.alloc_slice(s.as_bytes());
unsafe { core::str::from_utf8_unchecked(bytes) }
}
pub fn alloc_uninit<T>(&self) -> &'r mut MaybeUninit<T> {
let ptr = self.alloc_raw(Layout::new::<T>());
let typed_ptr = ptr.as_ptr().cast::<MaybeUninit<T>>();
unsafe { &mut *typed_ptr }
}
pub fn alloc_slice_uninit<T>(&self, len: usize) -> &'r mut [MaybeUninit<T>] {
if len == 0 {
return &mut [];
}
let layout = Layout::array::<T>(len).expect("Invalid layout");
let ptr = self.alloc_raw(layout);
let typed_ptr = ptr.as_ptr().cast::<MaybeUninit<T>>();
unsafe { slice::from_raw_parts_mut(typed_ptr, len) }
}
#[inline]
fn alloc_raw(&self, layout: Layout) -> NonNull<u8> {
let size = layout.size();
let align = layout.align();
let ptr = self.ptr.get();
let aligned = align_up(ptr as usize, align) as *mut u8;
let new_addr = (aligned as usize)
.checked_add(size)
.expect("Address overflow");
if likely(new_addr <= self.end.get() as usize) {
self.ptr.set(new_addr as *mut u8);
self.update_stats(size);
return NonNull::new(aligned).expect("Aligned pointer should be non-null");
}
self.alloc_raw_slow(layout, size, align)
}
#[cold]
#[inline(never)]
fn alloc_raw_slow(&self, _layout: Layout, size: usize, align: usize) -> NonNull<u8> {
let needed = size.checked_add(align - 1).expect("Allocation overflow");
loop {
self.grow(needed.max(MIN_CHUNK_SIZE));
let ptr = self.ptr.get();
let aligned = align_up(ptr as usize, align) as *mut u8;
let new_addr = (aligned as usize)
.checked_add(size)
.expect("Address overflow");
if new_addr <= self.end.get() as usize {
self.ptr.set(new_addr as *mut u8);
self.update_stats(size);
return NonNull::new(aligned).expect("Aligned pointer should be non-null");
}
}
}
fn grow(&self, min_size: usize) {
let chunk_size = min_size.max(DEFAULT_CHUNK_SIZE);
let chunk = Chunk::new(chunk_size).expect("Failed to allocate region chunk");
self.ptr.set(chunk.ptr.as_ptr());
self.end.set(chunk.end());
let mut stats = self.stats.get();
stats.chunks_used += 1;
self.stats.set(stats);
self.chunks.borrow_mut().push(chunk);
}
#[inline(always)]
fn update_stats(&self, bytes: usize) {
let mut stats = self.stats.get();
stats.allocations += 1;
stats.bytes_allocated += bytes;
self.stats.set(stats);
}
pub fn stats(&self) -> RegionStats {
self.stats.get()
}
pub fn allocation_count(&self) -> usize {
self.stats.get().allocations
}
pub fn bytes_allocated(&self) -> usize {
self.stats.get().bytes_allocated
}
pub unsafe fn reset(&self) {
let mut chunks = self.chunks.borrow_mut();
if let Some(chunk) = chunks.first() {
self.ptr.set(chunk.ptr.as_ptr());
self.end.set(chunk.end());
}
chunks.truncate(1);
self.stats.set(RegionStats {
allocations: 0,
bytes_allocated: 0,
chunks_used: 1,
});
}
}
#[inline(always)]
fn align_up(val: usize, align: usize) -> usize {
(val + align - 1) & !(align - 1)
}
pub struct RegionComputation<'r, A> {
run_fn: Box<dyn FnOnce(&Region<'r>) -> A + 'r>,
}
impl<'r, A: 'r> RegionComputation<'r, A> {
pub fn new<F: FnOnce(&Region<'r>) -> A + 'r>(f: F) -> Self {
RegionComputation {
run_fn: Box::new(f),
}
}
pub fn run(self, region: &Region<'r>) -> A {
(self.run_fn)(region)
}
pub fn pure(value: A) -> Self
where
A: Clone,
{
RegionComputation::new(move |_| value)
}
pub fn map<B: 'r, F: FnOnce(A) -> B + 'r>(self, f: F) -> RegionComputation<'r, B> {
RegionComputation::new(move |region| {
let a = (self.run_fn)(region);
f(a)
})
}
pub fn and_then<B: 'r, F: FnOnce(A) -> RegionComputation<'r, B> + 'r>(
self,
f: F,
) -> RegionComputation<'r, B> {
RegionComputation::new(move |region| {
let a = (self.run_fn)(region);
f(a).run(region)
})
}
}
pub fn with_region<A, F>(f: F) -> A
where
F: for<'r> FnOnce(&Region<'r>) -> A,
{
let region = Region::new();
f(®ion)
}
pub fn with_region_capacity<A, F>(capacity: usize, f: F) -> A
where
F: for<'r> FnOnce(&Region<'r>) -> A,
{
let region = Region::with_capacity(capacity);
f(®ion)
}
#[derive(Debug)]
pub struct RegionBox<'r, T: ?Sized> {
ptr: &'r mut T,
}
impl<'r, T> RegionBox<'r, T> {
pub fn new(region: &Region<'r>, value: T) -> Self {
RegionBox {
ptr: region.alloc(value),
}
}
}
impl<T: ?Sized> core::ops::Deref for RegionBox<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.ptr
}
}
impl<T: ?Sized> core::ops::DerefMut for RegionBox<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.ptr
}
}
pub struct RegionVec<'r, T> {
ptr: *mut T,
len: usize,
capacity: usize,
_marker: PhantomData<&'r mut T>,
}
impl<'r, T> RegionVec<'r, T> {
#[inline]
pub fn with_capacity(region: &Region<'r>, capacity: usize) -> Self {
if capacity == 0 {
return RegionVec {
ptr: core::ptr::NonNull::dangling().as_ptr(),
len: 0,
capacity: 0,
_marker: PhantomData,
};
}
let uninit = region.alloc_slice_uninit::<T>(capacity);
RegionVec {
ptr: uninit.as_mut_ptr().cast::<T>(),
len: 0,
capacity,
_marker: PhantomData,
}
}
#[inline]
pub fn push(&mut self, value: T) {
assert!(self.len < self.capacity, "RegionVec at capacity");
unsafe {
self.ptr.add(self.len).write(value);
}
self.len += 1;
}
#[inline]
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 {
None
} else {
self.len -= 1;
Some(unsafe { self.ptr.add(self.len).read() })
}
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn capacity(&self) -> usize {
self.capacity
}
#[inline]
pub fn as_slice(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.ptr, self.len) }
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe { slice::from_raw_parts_mut(self.ptr, self.len) }
}
}
impl<T> core::ops::Deref for RegionVec<'_, T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl<T> core::ops::DerefMut for RegionVec<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_slice()
}
}
impl<T> Drop for RegionVec<'_, T> {
fn drop(&mut self) {
let slice = core::ptr::slice_from_raw_parts_mut(self.ptr, self.len);
unsafe {
core::ptr::drop_in_place(slice);
}
}
}
pub struct ScopedAllocator {
allocation_count: Cell<usize>,
}
impl ScopedAllocator {
pub fn new() -> Self {
ScopedAllocator {
allocation_count: Cell::new(0),
}
}
pub fn scope<A, F>(&self, f: F) -> A
where
F: for<'r> FnOnce(&Region<'r>) -> A,
{
let count_before = self.allocation_count.get();
with_region(|region| {
let r = f(region);
self.allocation_count
.set(count_before + region.allocation_count());
r
})
}
pub fn total_allocations(&self) -> usize {
self.allocation_count.get()
}
}
impl Default for ScopedAllocator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_region_alloc() {
with_region(|region| {
let x = region.alloc(42i32);
let y = region.alloc(100i32);
assert_eq!(*x, 42);
assert_eq!(*y, 100);
*x = 99;
assert_eq!(*x, 99);
});
}
#[test]
fn test_region_alloc_slice() {
with_region(|region| {
let slice = region.alloc_slice(&[1, 2, 3, 4, 5]);
assert_eq!(slice, &[1, 2, 3, 4, 5]);
slice[0] = 10;
assert_eq!(slice[0], 10);
});
}
#[test]
fn test_region_alloc_str() {
with_region(|region| {
let s = region.alloc_str("hello world");
assert_eq!(s, "hello world");
});
}
#[test]
#[allow(clippy::large_stack_arrays)] fn test_region_grow_respects_alignment() {
#[repr(align(128))]
struct Aligned([u8; 65664]);
for _ in 0..32 {
with_region(|region| {
let v = region.alloc(Aligned([7u8; 65664]));
assert_eq!((core::ptr::from_ref(&*v) as usize) % 128, 0);
assert_eq!(v.0[0], 7);
assert_eq!(v.0[65663], 7);
});
}
}
#[test]
fn test_region_stats() {
with_region(|region| {
assert_eq!(region.allocation_count(), 0);
region.alloc(1i32);
region.alloc(2i32);
region.alloc(3i32);
assert_eq!(region.allocation_count(), 3);
assert!(region.bytes_allocated() >= 12); });
}
#[test]
fn test_region_multiple_types() {
with_region(|region| {
let i = region.alloc(42i32);
let f = region.alloc(2.5f64);
let s = region.alloc_str("test");
let arr = region.alloc_slice(&[1u8, 2, 3]);
assert_eq!(*i, 42);
assert_eq!(*f, 2.5);
assert_eq!(s, "test");
assert_eq!(arr, &[1, 2, 3]);
});
}
#[test]
fn test_with_region_capacity() {
let result = with_region_capacity(1024, |region| {
let x = region.alloc(42);
*x * 2
});
assert_eq!(result, 84);
}
#[test]
fn test_region_box() {
with_region(|region| {
let boxed = RegionBox::new(region, 42);
assert_eq!(*boxed, 42);
});
}
#[test]
fn test_region_vec() {
with_region(|region| {
let mut vec = RegionVec::<i32>::with_capacity(region, 10);
vec.push(1);
vec.push(2);
vec.push(3);
assert_eq!(vec.len(), 3);
assert_eq!(vec.as_slice(), &[1, 2, 3]);
assert_eq!(vec.pop(), Some(3));
assert_eq!(vec.len(), 2);
});
}
#[test]
fn test_region_vec_iteration() {
with_region(|region| {
let mut vec = RegionVec::<i32>::with_capacity(region, 5);
vec.push(10);
vec.push(20);
vec.push(30);
let sum: i32 = vec.iter().sum();
assert_eq!(sum, 60);
});
}
#[test]
fn test_region_computation() {
let result = with_region(|region| {
let comp = RegionComputation::new(|r| {
let x = r.alloc(10);
let y = r.alloc(20);
*x + *y
});
comp.run(region)
});
assert_eq!(result, 30);
}
#[test]
fn test_region_computation_map() {
let result = with_region(|region| {
let comp = RegionComputation::new(|r| r.alloc(10)).map(|x| *x * 2);
comp.run(region)
});
assert_eq!(result, 20);
}
#[test]
fn test_scoped_allocator() {
let allocator = ScopedAllocator::new();
allocator.scope(|region| {
region.alloc(1);
region.alloc(2);
});
allocator.scope(|region| {
region.alloc(3);
});
assert_eq!(allocator.total_allocations(), 3);
}
#[test]
#[allow(clippy::large_stack_arrays)] fn test_large_allocation() {
with_region(|region| {
let large = region.alloc_slice(&[0u8; 100_000]);
assert_eq!(large.len(), 100_000);
assert!(region.stats().chunks_used >= 2);
});
}
#[test]
fn test_alignment() {
with_region(|region| {
let b = region.alloc(1u8);
let i = region.alloc(42i32);
let d = region.alloc(2.5f64);
assert_eq!(core::ptr::from_ref::<u8>(b) as usize % align_of::<u8>(), 0);
assert_eq!(
core::ptr::from_ref::<i32>(i) as usize % align_of::<i32>(),
0
);
assert_eq!(
core::ptr::from_ref::<f64>(d) as usize % align_of::<f64>(),
0
);
});
}
#[test]
fn test_region_vec_empty() {
with_region(|region| {
let vec = RegionVec::<i32>::with_capacity(region, 0);
assert!(vec.is_empty());
assert_eq!(vec.capacity(), 0);
});
}
#[test]
fn test_region_computation_pure() {
let result = with_region(|region| {
let comp = RegionComputation::pure(42);
comp.run(region)
});
assert_eq!(result, 42);
}
#[test]
fn test_nested_regions() {
let outer_result = with_region(|outer| {
let x = outer.alloc(10);
let inner_result = with_region(|inner| {
let y = inner.alloc(20);
*y
});
*x + inner_result
});
assert_eq!(outer_result, 30);
}
#[test]
fn test_realistic_usage() {
struct Node<'r> {
value: i32,
children: &'r [&'r Node<'r>],
}
let sum = with_region(|region| {
let leaf1 = region.alloc(Node {
value: 1,
children: &[],
});
let leaf2 = region.alloc(Node {
value: 2,
children: &[],
});
let _leaf3 = region.alloc(Node {
value: 3,
children: &[],
});
let children = region.alloc_slice(&[leaf1 as &Node, leaf2 as &Node]);
let parent = region.alloc(Node {
value: 10,
children,
});
fn sum_tree(node: &Node) -> i32 {
node.value + node.children.iter().map(|c| sum_tree(c)).sum::<i32>()
}
sum_tree(parent)
});
assert_eq!(sum, 13); }
}