Struct ObjectStack

Source
pub struct ObjectStack { /* private fields */ }
Expand description

A high-level stack-based object builder that uses StackArena for memory management.

ObjectStack provides a more user-friendly interface on top of StackArena for building and managing objects in a stack-like fashion. It supports incremental object construction through the extend and finish methods, as well as direct formatting through the std::fmt::Write trait.

§Features

  • Push objects onto the stack with move or copy semantics
  • Push byte slices onto the stack
  • Build objects incrementally using extend and finish methods
  • Implements std::fmt::Write for string formatting directly into objects
  • Stack-like (LIFO) allocation and deallocation pattern
  • Memory-efficient with minimal overhead
  • Automatic memory management with chunk reuse

§Use Cases

ObjectStack is particularly useful for:

  • String building and text processing
  • Constructing complex objects incrementally
  • Serialization operations
  • Any scenario where objects need to be built in stages

§Examples

use stack_arena::ObjectStack;
use std::fmt::Write;

let mut stack = ObjectStack::new();

// Push bytes onto the stack
stack.push_bytes(b"hello");

// Push a value with move semantics
let string = String::from("world");
stack.push(string); // string is moved

// Push a value with copy semantics
let value = 42;
stack.push_copy(&value);

// Build an object incrementally
stack.extend("incremental ");
write!(&mut stack, "from {}", "Rust").unwrap();
let ptr = stack.finish();

// Access the object
let message = unsafe { std::str::from_utf8_unchecked(ptr.as_ref()) };
assert_eq!(message, "incremental from Rust");

// Pop the object when done
stack.pop();

Implementations§

Source§

impl ObjectStack

Source

pub fn new() -> Self

Creates a new empty ObjectStack with a default initial capacity.

The initial chunk size is 1024 bytes, managed by the underlying StackArena.

§Examples
use stack_arena::ObjectStack;

let stack = ObjectStack::new();
assert!(stack.is_empty());
Source

pub fn len(&self) -> usize

Returns the number of objects currently on the stack.

§Examples
use stack_arena::ObjectStack;

let mut stack = ObjectStack::new();
assert_eq!(stack.len(), 0);

stack.push_bytes(b"hello");
assert_eq!(stack.len(), 1);
Source

pub fn is_empty(&self) -> bool

Returns true if the stack contains no objects.

§Examples
use stack_arena::ObjectStack;

let mut stack = ObjectStack::new();
assert!(stack.is_empty());

stack.push_bytes(b"hello");
assert!(!stack.is_empty());
Source

pub fn push_bytes<P: AsRef<[u8]>>(&mut self, object: P) -> NonNull<[u8]>

Pushes a byte slice onto the stack.

This method allocates memory for the byte slice, copies the data, and returns a pointer to the allocated memory.

§Parameters
  • object - The data to push onto the stack, which can be any type that can be converted to a byte slice.
§Returns

A non-null pointer to the allocated memory containing the data.

§Examples
use stack_arena::ObjectStack;

let mut stack = ObjectStack::new();
let ptr = stack.push_bytes(b"hello world");
// The pointer is valid until the object is popped or freed
Source

pub fn push_copy<T>(&mut self, object: &T) -> NonNull<T>
where T: Copy + ?Sized,

Pushes a copy of a value onto the stack.

This method takes a reference to a value that implements the Copy trait, makes a bitwise copy of it, and pushes it onto the stack.

§Parameters
  • object - A reference to the value to copy onto the stack.
§Returns

A non-null pointer to the allocated memory containing the copied value.

§Examples
use stack_arena::ObjectStack;

let mut stack = ObjectStack::new();

// Copy a primitive value
let value = 42;
let ptr = stack.push_copy(&value);

// Copy a struct that implements Copy
#[derive(Copy, Clone)]
struct Point { x: i32, y: i32 }

let point = Point { x: 10, y: 20 };
let point_ptr = stack.push_copy(&point);
Source

pub fn push<T>(&mut self, object: T) -> NonNull<T>
where T: Sized,

Pushes a value onto the stack with move semantics.

This method takes ownership of the value, moves it onto the stack, and returns a pointer to the allocated memory.

§Important Safety Notes
  • For types that implement Drop, the destructor will not be called automatically when the object is popped or the stack is dropped. You must call drop_in_place explicitly when you’re done with the object.
  • Modifying the object through the returned pointer (e.g., pushing more bytes onto a String) is undefined behavior if it would change the object’s size or layout.
  • The memory layout is fixed after allocation and cannot be resized.
§Parameters
  • object - The value to move onto the stack.
§Returns

A non-null pointer to the allocated memory containing the moved value.

§Examples
use stack_arena::ObjectStack;

let mut stack = ObjectStack::new();

// Move a String onto the stack
let string = String::from("hello world");
let ptr = stack.push(string);
// string is moved and no longer accessible

// Use the string...
unsafe {
    let s = ptr.as_ref();
    assert_eq!(s, "hello world");
}

// When done, explicitly drop it
unsafe {
    stack.drop_in_place(ptr);
}

// Move a custom struct onto the stack
struct Person {
    name: String,
    age: u32,
}

let person = Person {
    name: String::from("Alice"),
    age: 30,
};

let person_ptr = stack.push(person);
// person is moved and no longer accessible

// Don't forget to drop it when done
unsafe {
    stack.drop_in_place(person_ptr);
}
Source

pub unsafe fn drop_in_place<T>(&mut self, ptr: NonNull<T>)

Drops a value in place without deallocating its memory.

This method calls the destructor for a value previously pushed onto the stack with push. It’s useful for types that implement Drop and need cleanup. The memory itself is not freed until the object is popped or the stack is dropped.

§User Responsibility

It is the user’s responsibility to call this method for any values that implement Drop. Failure to do so may result in resource leaks (e.g., unclosed files, unfreed memory from heap allocations inside the object).

§Safety

This method is unsafe because:

  • The pointer must have been obtained from push<T> with the same type T
  • The value must not have been previously dropped
  • The memory must not be accessed after calling this method
§Examples
use stack_arena::ObjectStack;
use std::fs::File;

let mut stack = ObjectStack::new();

// Push a value that needs dropping
let string = String::from("hello world");
let ptr = stack.push(string);

// Use the string...

// When done, drop it in place
unsafe {
    stack.drop_in_place(ptr);
}

// The memory is still allocated but the string is dropped
Source

pub fn pop(&mut self)

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::ObjectStack;

let mut stack = ObjectStack::new();
stack.push_bytes(b"hello");
stack.push_bytes(b"world");
assert_eq!(stack.len(), 2);

stack.pop();
assert_eq!(stack.len(), 1);
Source

pub fn extend<P: AsRef<[u8]>>(&mut self, value: P)

Extends the current object being built with additional data.

This method is used for incrementally building objects. Multiple calls to extend can be made before finalizing the object with finish. This method only supports extending the last allocation (following LIFO pattern), as it uses the underlying arena’s grow functionality.

§Parameters
  • value - The data to append to the current object, which can be any type that can be converted to a byte slice.
§Examples
use stack_arena::ObjectStack;

let mut stack = ObjectStack::new();
stack.extend("Hello, ");
stack.extend("world!");
let ptr = stack.finish();
// ptr now points to "Hello, world!"
Source

pub fn finish(&mut self) -> NonNull<[u8]>

Finalizes the current object being built and adds it to the stack.

This method should be called after one or more calls to extend to finalize the object and make it available on the stack.

§Returns

A non-null pointer to the finalized object.

§Examples
use stack_arena::ObjectStack;

let mut stack = ObjectStack::new();
stack.extend("Hello");
stack.extend(" world");
let ptr = stack.finish();
// ptr now points to "Hello world"
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::ObjectStack;

let mut stack = ObjectStack::new();
stack.push_bytes(b"first");
let second = stack.push_bytes(b"second");
stack.push_bytes(b"third");

// Free "second" and "third"
stack.rollback(unsafe { second.as_ref() });
assert_eq!(stack.len(), 1); // Only "first" remains

Trait Implementations§

Source§

impl Debug for ObjectStack

Source§

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

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

impl Write for ObjectStack

Implementation of the std::fmt::Write trait for ObjectStack.

This allows using the write! macro and other formatting utilities to write formatted text directly into the object being built.

§Examples

use std::fmt::Write;
use stack_arena::ObjectStack;

let mut stack = ObjectStack::new();
write!(&mut stack, "Hello, {}!", "world").unwrap();
let formatted = stack.finish();
// formatted now points to "Hello, world!"
Source§

fn write_str(&mut self, s: &str) -> Result

Writes a string into the arena.

This method extends the current object with the given string.

1.1.0 · Source§

fn write_char(&mut self, c: char) -> Result<(), Error>

Writes a char into this writer, returning whether the write succeeded. Read more
1.0.0 · Source§

fn write_fmt(&mut self, args: Arguments<'_>) -> Result<(), Error>

Glue for usage of the write! macro with implementors of this trait. 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.