pub struct StackArena { /* private fields */ }
Expand description
A stack-like arena allocator that efficiently manages memory in chunks.
StackArena
provides a low-level memory allocation strategy where objects are allocated
in a stack-like fashion (Last-In-First-Out). It manages memory in chunks, automatically
allocating new chunks when needed, which reduces allocation overhead and improves performance.
§Memory Management
- Memory is organized in chunks, with a current active chunk for allocations
- When the current chunk is full, a new larger chunk is allocated
- Old chunks are stored to keep previous allocations valid until explicitly deallocated
- Objects are allocated contiguously within chunks for better cache locality
- Follows LIFO (Last-In-First-Out) allocation/deallocation pattern
§Use Cases
StackArena
is particularly well-suited for:
- Parsers and compilers that need to allocate many temporary objects
- Interpreters that need efficient memory management for short-lived objects
- Data processing pipelines with predictable allocation patterns
- Any scenario where many small allocations are made in sequence
§Thread Safety
StackArena
is not thread-safe and should not be shared between threads
without external synchronization.
§Performance
This allocator is optimized for scenarios with many small allocations that follow a stack-like pattern. Benchmarks show it significantly outperforms the system allocator:
- Up to 10x faster for consecutive small allocations
- Minimal overhead for allocation and deallocation
- Efficient memory reuse with the LIFO pattern
Implementations§
Source§impl StackArena
impl StackArena
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates a new empty StackArena
with a default initial capacity.
The initial chunk size is 1024 bytes.
§Examples
use stack_arena::StackArena;
let arena = StackArena::new();
assert!(arena.is_empty());
Sourcepub fn with_chunk_size(chunk_size: usize) -> Self
pub fn with_chunk_size(chunk_size: usize) -> Self
Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns the number of objects currently on the stack.
§Examples
use stack_arena::{StackArena, Allocator};
use std::alloc::Layout;
let mut arena = StackArena::new();
assert_eq!(arena.len(), 0);
// Allocate memory for an object
let layout = Layout::from_size_align(5, 1).unwrap();
let ptr = unsafe { arena.allocate(layout).unwrap() };
assert_eq!(arena.len(), 1);
Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Returns true
if the arena contains no objects.
§Examples
use stack_arena::{StackArena, Allocator};
use std::alloc::Layout;
let mut arena = StackArena::new();
assert!(arena.is_empty());
// Allocate memory for an object
let layout = Layout::from_size_align(5, 1).unwrap();
let ptr = unsafe { arena.allocate(layout).unwrap() };
assert!(!arena.is_empty());
Sourcepub fn pop(&mut self) -> Option<NonNull<[u8]>>
pub fn pop(&mut self) -> Option<NonNull<[u8]>>
Removes the most recently pushed object from the stack.
This method follows the LIFO (Last-In-First-Out) principle. After popping, any pointers to the popped object become invalid.
§Panics
Panics if the stack is empty or if there is a partial object
being built (i.e., if extend
has been called but finish
has not).
§Examples
use stack_arena::{StackArena, Allocator};
use std::alloc::Layout;
let mut arena = StackArena::new();
// Allocate first object
let layout1 = Layout::from_size_align(5, 1).unwrap();
let ptr1 = unsafe { arena.allocate(layout1).unwrap() };
// Allocate second object
let layout2 = Layout::from_size_align(5, 1).unwrap();
let ptr2 = unsafe { arena.allocate(layout2).unwrap() };
assert_eq!(arena.len(), 2);
arena.pop();
assert_eq!(arena.len(), 1);
Sourcepub fn top(&mut self) -> Option<NonNull<[u8]>>
pub fn top(&mut self) -> Option<NonNull<[u8]>>
Returns the top object on the stack without removing it.
This method returns a reference to the most recently allocated object on the stack.
§Returns
An optional non-null pointer to the top object, or None if the stack is empty.
§Examples
use stack_arena::{StackArena, Allocator};
use std::alloc::Layout;
let mut arena = StackArena::new();
let layout = Layout::from_size_align(8, 1).unwrap();
let ptr = unsafe { arena.allocate(layout).unwrap() };
// Get the top object
let top = arena.top();
assert!(top.is_some());
Sourcepub fn rollback(&mut self, data: &[u8])
pub fn rollback(&mut self, data: &[u8])
Rolls back to a specific object, freeing it and all objects allocated after it.
This method allows for rolling back to a specific point in the allocation history by providing a reference to an object on the stack.
§Parameters
data
- A reference to the object to free, along with all objects allocated after it.
§Examples
use stack_arena::{StackArena, Allocator};
use std::alloc::Layout;
let mut arena = StackArena::new();
// Allocate some objects
let layout1 = Layout::from_size_align(8, 1).unwrap();
let ptr1 = unsafe { arena.allocate(layout1).unwrap() };
let layout2 = Layout::from_size_align(16, 1).unwrap();
let ptr2 = unsafe { arena.allocate(layout2).unwrap() };
// Roll back to the first object
unsafe {
arena.rollback(ptr1.as_ref());
}
assert_eq!(arena.len(), 0); // All objects are freed
Trait Implementations§
Source§impl Allocator for StackArena
Implementation of the Allocator
trait for StackArena
.
impl Allocator for StackArena
Implementation of the Allocator
trait for StackArena
.
This implementation allows StackArena
to be used with APIs that require
an allocator. The implementation follows the stack-like allocation pattern,
where memory is allocated in chunks and objects are allocated contiguously.
§Safety
The implementation uses unsafe code internally to manage memory efficiently. Users should follow the LIFO (Last-In-First-Out) pattern when deallocating memory to ensure proper behavior.
§Performance
This allocator is optimized for scenarios with many small allocations that follow a stack-like pattern. It significantly outperforms the system allocator in these cases as demonstrated in the benchmarks.
Source§unsafe fn allocate(
&mut self,
layout: Layout,
) -> Result<NonNull<[u8]>, AllocError>
unsafe fn allocate( &mut self, layout: Layout, ) -> Result<NonNull<[u8]>, AllocError>
Allocates memory with the specified layout.
This method allocates a new object with the given layout in the current chunk. If the current chunk doesn’t have enough space, a new chunk is allocated.
§Safety
This method is unsafe because it returns a raw pointer that must be used carefully to avoid memory safety issues.
Source§unsafe fn deallocate(&mut self, ptr: NonNull<u8>, layout: Layout)
unsafe fn deallocate(&mut self, ptr: NonNull<u8>, layout: Layout)
Deallocates memory previously allocated by allocate
.
This method frees the specified object and all objects allocated after it, following the stack-like (LIFO) deallocation pattern.
§Safety
This method is unsafe because it must be called with a pointer returned
by allocate
and the same layout that was used to allocate it.
Source§unsafe fn grow(
&mut self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError>
unsafe fn grow( &mut self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError>
Grows a previously allocated memory block to a larger size.
This method is used to extend an existing allocation to a larger size.
It’s used internally by the extend
method. It only supports growing
the last allocation (following LIFO pattern). Attempting to grow any
other allocation will trigger a debug assertion failure.
§Safety
This method is unsafe because it must be called with a pointer returned
by allocate
and the same layout that was used to allocate it.
Source§unsafe fn shrink(
&mut self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError>
unsafe fn shrink( &mut self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError>
Shrinks a previously allocated memory block to a smaller size.
This method is used to reduce the size of an existing allocation.
§Safety
This method is unsafe because it must be called with a pointer returned
by allocate
and the same layout that was used to allocate it.