Struct StackArena

Source
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

Source

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());
Source

pub fn with_chunk_size(chunk_size: usize) -> Self

Creates a new empty StackArena with a specified chunk size.

§Parameters
  • chunk_size - The size in bytes to use for new chunks
§Examples
use stack_arena::StackArena;

// Create an arena with 4096-byte chunks
let arena = StackArena::with_chunk_size(4096);
assert!(arena.is_empty());
Source

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);
Source

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());
Source

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);
Source

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());
Source

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.

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>

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)

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>

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>

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.

Source§

unsafe fn allocate_zeroed( &mut self, layout: Layout, ) -> Result<NonNull<[u8]>, AllocError>

Allocates zeroed memory with the specified layout. Read more
Source§

unsafe fn grow_zeroed( &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 and zeroes the new memory. Read more
Source§

fn by_ref(&self) -> &Self
where Self: Sized,

Returns a reference to this allocator. Read more
Source§

impl Debug for StackArena

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for StackArena

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.