extern crate alloc;
use alloc::alloc::{alloc, dealloc, Layout};
use alloc::vec::Vec;
use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::mem::{self, MaybeUninit};
use core::ptr::{self, NonNull};
use core::slice;
use core::sync::atomic::{AtomicUsize, Ordering};
use core::usize;
use std::sync::Mutex;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
const MIN_CHUNK_SIZE: usize = 64;
const MAX_CHUNK_SIZE: usize = 16 * 1024 * 1024; const ALIGNMENT_MASK: usize = 63; const PREFETCH_DISTANCE: usize = 8;
const PREFETCH_WARMUP_SIZE: usize = 64;
pub struct MemoryPool {
slots: Vec<Option<NonNull<u8>>>,
size_class: usize,
capacity: usize,
}
impl MemoryPool {
pub fn new(size_class: usize, capacity: usize) -> Self {
Self {
slots: Vec::with_capacity(capacity),
size_class,
capacity,
}
}
pub fn alloc(&mut self) -> Option<NonNull<u8>> {
self.slots.pop().flatten()
}
pub fn dealloc(&mut self, ptr: NonNull<u8>) {
if self.slots.len() < self.capacity {
self.slots.push(Some(ptr));
}
}
pub fn size_class(&self) -> usize {
self.size_class
}
}
pub struct AtomicCounter {
value: AtomicUsize,
}
impl std::fmt::Debug for AtomicCounter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"AtomicCounter({})",
self.value.load(std::sync::atomic::Ordering::Relaxed)
)
}
}
impl AtomicCounter {
pub fn new(value: usize) -> Self {
Self {
value: AtomicUsize::new(value),
}
}
pub fn load(&self, ordering: Ordering) -> usize {
self.value.load(ordering)
}
pub fn store(&self, value: usize, ordering: Ordering) {
self.value.store(value, ordering);
}
pub fn fetch_add(&self, value: usize, ordering: Ordering) -> usize {
self.value.fetch_add(value, ordering)
}
}
impl PartialEq<usize> for AtomicCounter {
fn eq(&self, other: &usize) -> bool {
self.load(Ordering::Acquire) == *other
}
}
impl PartialEq for AtomicCounter {
fn eq(&self, other: &Self) -> bool {
self.load(Ordering::Acquire) == other.load(Ordering::Acquire)
}
}
impl PartialOrd<usize> for AtomicCounter {
fn partial_cmp(&self, other: &usize) -> Option<std::cmp::Ordering> {
self.load(Ordering::Acquire).partial_cmp(other)
}
}
impl std::fmt::Display for AtomicCounter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.load(Ordering::Acquire))
}
}
#[repr(C)]
pub struct Chunk {
ptr: NonNull<u8>,
capacity: usize,
used: AtomicUsize,
}
impl Chunk {
pub fn new(capacity: usize) -> Result<Self, &'static str> {
if capacity == 0 {
return Err("Capacity must be nonzero");
}
let capacity = (capacity + 63) & !63;
eprintln!(
"Attempting allocation: requested_capacity={} aligned_capacity={} alignment=64",
capacity, capacity
);
let layout = Layout::from_size_align(capacity, 64).map_err(|_| "Invalid layout")?;
let ptr = unsafe { alloc(layout) };
if ptr.is_null() {
return Err("Failed to allocate memory");
}
Ok(Self {
ptr: unsafe { NonNull::new_unchecked(ptr) },
capacity,
used: AtomicUsize::new(0),
})
}
pub fn allocate(&self, layout: Layout) -> Option<*mut u8> {
let size = layout.size();
let align = layout.align();
let current_used = self.used.load(Ordering::Acquire);
let start = (current_used + align - 1) & !(align - 1);
let end = start + size;
if end > self.capacity {
return None;
}
if self
.used
.compare_exchange_weak(current_used, end, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
unsafe {
let ptr = self.ptr.as_ptr().add(start);
#[cfg(target_arch = "x86_64")]
if size >= PREFETCH_WARMUP_SIZE {
_mm_prefetch(ptr as *const i8, _MM_HINT_T0);
}
return Some(ptr);
}
}
None
}
pub fn reset(&self) {
self.used.store(0, Ordering::Release);
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn ptr(&self) -> NonNull<u8> {
self.ptr
}
pub fn used(&self) -> usize {
self.used.load(Ordering::Acquire)
}
pub fn set_used(&self, value: usize) {
self.used.store(value, Ordering::Release);
}
}
impl Drop for Chunk {
fn drop(&mut self) {
unsafe {
if self.capacity > 0 {
if let Ok(layout) = Layout::from_size_align(self.capacity, 64) {
eprintln!(
"[Chunk::drop] dealloc ptr={:p} capacity={}",
self.ptr.as_ptr(),
self.capacity
);
eprintln!(
"[Chunk::drop] backtrace:\n{}",
std::backtrace::Backtrace::capture()
);
dealloc(self.ptr.as_ptr(), layout);
} else {
let fallback = Layout::from_size_align(64, 64).unwrap_or_else(|_| {
eprintln!("[Chunk::drop] Layout::from_size_align(fallback) failed, using minimal layout");
Layout::from_size_align(8, 8).expect("fallback layout invalid")
});
eprintln!(
"[Chunk::drop] dealloc ptr={:p} capacity=fallback(64)",
self.ptr.as_ptr()
);
eprintln!(
"[Chunk::drop] backtrace:\n{}",
std::backtrace::Backtrace::capture()
);
dealloc(self.ptr.as_ptr(), fallback);
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ArenaCheckpoint {
pub chunk_index: usize,
pub chunk_offset: usize,
pub checkpoint_id: usize,
pub allocation_count: usize,
}
#[derive(Debug, Default, Clone)]
pub struct DebugStats {
pub total_allocations: usize,
pub active_checkpoints: usize,
pub current_checkpoint_id: usize,
pub corrupted_allocations: usize,
pub leak_reports: usize,
}
pub struct ArenaStats {
pub bytes_used: AtomicCounter,
pub bytes_allocated: AtomicCounter,
pub allocation_count: AtomicCounter,
pub chunk_count: usize,
}
impl std::fmt::Debug for ArenaStats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ArenaStats")
.field("bytes_used", &self.bytes_used)
.field("allocation_count", &self.allocation_count)
.field("chunk_count", &self.chunk_count)
.finish()
}
}
impl ArenaStats {
pub fn new() -> Self {
Self {
bytes_used: AtomicCounter::new(0),
bytes_allocated: AtomicCounter::new(0),
allocation_count: AtomicCounter::new(0),
chunk_count: 0,
}
}
pub fn bytes_used(&self) -> usize {
self.bytes_used.load(Ordering::Acquire)
}
pub fn allocation_count(&self) -> usize {
self.allocation_count.load(Ordering::Acquire)
}
}
impl Default for ArenaStats {
fn default() -> Self {
Self::new()
}
}
pub struct ArenaInner {
pub chunks: Vec<Chunk>,
pub current_chunk: AtomicUsize,
pub checkpoints: Vec<ArenaCheckpoint>,
pub current_checkpoint_id: usize,
pub pools: Vec<MemoryPool>,
#[cfg(feature = "stats")]
pub stats: ArenaStats,
#[cfg(feature = "virtual_memory")]
pub virtual_region: Option<crate::virtual_memory::VirtualMemoryRegion>,
#[cfg(feature = "thread_local")]
pub thread_cache_active: bool,
#[cfg(feature = "lockfree")]
pub lockfree_buffer: Option<crate::lockfree::LockFreeBuffer>,
#[cfg(feature = "lockfree")]
pub lockfree_stats: crate::lockfree::LockFreeStats,
}
impl ArenaInner {
pub fn new(initial_capacity: usize) -> Result<Self, &'static str> {
let chunk = Chunk::new(initial_capacity)?;
let mut pools = Vec::new();
let mut size = 8;
while size <= 4096 {
pools.push(MemoryPool::new(size, 64));
size *= 2;
}
let mut inner = Self {
chunks: vec![chunk],
current_chunk: AtomicUsize::new(0),
checkpoints: Vec::new(),
current_checkpoint_id: 0,
pools,
#[cfg(feature = "stats")]
stats: ArenaStats::new(),
#[cfg(feature = "virtual_memory")]
virtual_region: None,
#[cfg(feature = "thread_local")]
thread_cache_active: true,
#[cfg(feature = "lockfree")]
lockfree_buffer: Some(crate::lockfree::LockFreeBuffer::new()),
#[cfg(feature = "lockfree")]
lockfree_stats: crate::lockfree::LockFreeStats::new(),
};
#[cfg(feature = "stats")]
{
inner
.stats
.bytes_allocated
.store(initial_capacity, Ordering::Relaxed);
inner.stats.chunk_count = inner.chunks.len();
}
Ok(inner)
}
pub fn allocate(&mut self, layout: Layout) -> Option<*mut u8> {
let size = layout.size();
if size <= 4096 {
if let Some(_pool) = self.pools.iter().find(|p| p.size_class() >= size) {
if let Some(ptr) = self
.pools
.iter_mut()
.find(|p| p.size_class() >= size)
.and_then(|p| p.alloc())
{
return Some(ptr.as_ptr());
}
}
}
let current_chunk_idx = self.current_chunk.load(Ordering::Acquire);
let chunk_idx = if current_chunk_idx >= self.chunks.len() {
self.chunks.len().saturating_sub(1)
} else {
current_chunk_idx
};
if let Some(chunk) = self.chunks.get(chunk_idx) {
if let Some(ptr) = chunk.allocate(layout) {
#[cfg(feature = "stats")]
{
self.stats.bytes_used.fetch_add(size, Ordering::Relaxed);
self.stats.allocation_count.fetch_add(1, Ordering::Relaxed);
}
return Some(ptr);
}
}
None
}
pub fn add_chunk(&mut self, capacity: usize) -> Result<usize, &'static str> {
let chunk = Chunk::new(capacity)?;
let chunk_index = self.chunks.len();
self.chunks.push(chunk);
self.current_chunk.store(chunk_index, Ordering::Release);
#[cfg(feature = "stats")]
{
self.stats.chunk_count = self.chunks.len();
self.stats
.bytes_allocated
.fetch_add(capacity, Ordering::Relaxed);
}
Ok(chunk_index)
}
pub fn reset(&mut self) {
for chunk in &mut self.chunks {
chunk.reset();
}
self.current_chunk.store(0, Ordering::Release);
self.checkpoints.clear();
self.current_checkpoint_id = 0;
#[cfg(feature = "stats")]
{
self.stats.bytes_used.store(0, Ordering::Release);
self.stats.allocation_count.store(0, Ordering::Release);
}
#[cfg(feature = "thread_local")]
{
crate::thread_local::reset_thread_cache();
}
#[cfg(feature = "lockfree")]
{
if let Some(ref buffer) = self.lockfree_buffer {
buffer.reset();
}
}
}
pub fn checkpoint(&mut self) -> ArenaCheckpoint {
let current_chunk_idx = self.current_chunk.load(Ordering::Acquire);
let current_chunk_idx = if current_chunk_idx >= self.chunks.len() {
self.chunks.len().saturating_sub(1)
} else {
current_chunk_idx
};
let current_chunk = &self.chunks[current_chunk_idx];
let chunk_offset = current_chunk.used();
#[cfg(feature = "stats")]
let alloc_count = self.stats.allocation_count.load(Ordering::Acquire);
#[cfg(not(feature = "stats"))]
let alloc_count = 0;
let checkpoint = ArenaCheckpoint {
chunk_index: current_chunk_idx,
chunk_offset,
checkpoint_id: self.current_checkpoint_id,
allocation_count: alloc_count,
};
self.checkpoints.push(checkpoint);
self.current_checkpoint_id += 1;
checkpoint
}
pub fn rewind_to_checkpoint(&mut self, checkpoint: ArenaCheckpoint) {
assert!(
checkpoint.chunk_index < self.chunks.len(),
"Invalid checkpoint: chunk index out of bounds"
);
assert!(
checkpoint.chunk_offset <= self.chunks[checkpoint.chunk_index].capacity(),
"Invalid checkpoint: offset exceeds chunk capacity"
);
self.current_chunk
.store(checkpoint.chunk_index, Ordering::Release);
for (idx, chunk) in self.chunks.iter_mut().enumerate() {
if idx < checkpoint.chunk_index {
continue;
}
if idx == checkpoint.chunk_index {
unsafe {
let used_ptr = &mut chunk.used as *mut AtomicUsize;
(*used_ptr).store(checkpoint.chunk_offset, Ordering::Release);
}
} else {
chunk.reset();
}
}
self.checkpoints.retain(|cp| {
cp.chunk_index < checkpoint.chunk_index
|| (cp.chunk_index == checkpoint.chunk_index
&& cp.chunk_offset <= checkpoint.chunk_offset)
});
#[cfg(feature = "debug")]
{
crate::debug::rewind_to_checkpoint(checkpoint.checkpoint_id);
self.current_checkpoint_id = checkpoint.checkpoint_id + 1;
}
#[cfg(feature = "thread_local")]
{
crate::thread_local::reset_thread_cache();
}
#[cfg(feature = "lockfree")]
{
if let Some(ref buffer) = self.lockfree_buffer {
buffer.reset();
}
}
#[cfg(feature = "stats")]
{
let mut bytes_used = 0;
for chunk in self.chunks.iter().take(checkpoint.chunk_index + 1) {
bytes_used += chunk.used();
}
self.stats.bytes_used.store(bytes_used, Ordering::Release);
self.stats
.allocation_count
.store(checkpoint.allocation_count, Ordering::Release);
}
}
pub fn push_checkpoint(&mut self) -> ArenaCheckpoint {
self.checkpoint()
}
pub fn pop_and_rewind(&mut self) -> Result<(), &'static str> {
if let Some(checkpoint) = self.checkpoints.pop() {
self.rewind_to_checkpoint(checkpoint);
Ok(())
} else {
Err("No checkpoint to pop")
}
}
#[cfg(feature = "stats")]
pub fn stats(&self) -> &ArenaStats {
&self.stats
}
#[cfg(feature = "debug")]
pub fn debug_stats(&self) -> DebugStats {
crate::debug::get_debug_stats()
}
#[cfg(feature = "lockfree")]
pub fn lockfree_stats(&self) -> (usize, usize, usize, usize) {
self.lockfree_stats.get()
}
}
pub struct ArenaBuilder {
initial_capacity: usize,
}
impl ArenaBuilder {
pub fn new() -> Self {
Self {
initial_capacity: 4096,
}
}
pub fn initial_capacity(mut self, capacity: usize) -> Self {
self.initial_capacity = capacity;
self
}
pub fn build(self) -> crate::Arena {
crate::Arena::with_capacity(self.initial_capacity)
}
}
impl Default for ArenaBuilder {
fn default() -> Self {
Self::new()
}
}
pub struct Scope<'scope, 'arena> {
arena: &'arena mut crate::Arena,
_phantom: PhantomData<&'scope ()>,
}
impl<'scope, 'arena> Scope<'scope, 'arena> {
pub fn new(arena: &'arena mut crate::Arena) -> Self {
Self {
arena,
_phantom: PhantomData,
}
}
}
impl<'scope, 'arena> Drop for Scope<'scope, 'arena> {
fn drop(&mut self) {
unsafe {
self.arena.reset();
}
}
}