# fastarena — Complete Usage Guide

> Zero-dependency bump-pointer arena allocator with RAII transactions, nested savepoints, optional destructor tracking, and `ArenaVec` — built for compilers, storage engines, and high-throughput request-scoped workloads.

---

## Table of Contents

- [Why fastarena?](#why-fastarena)
- [Installation](#installation)
- [Quick Start](#quick-start)
  - [Basic Allocation](#basic-allocation)
  - [Transactions — Auto-Rollback on Failure](#transactions--auto-rollback-on-failure)
  - [Nested Savepoints](#nested-savepoints)
  - [ArenaVec with `finish()`](#arenavec-with-finish--transfer-ownership-to-the-arena)
  - [ArenaVec Macro (`arenavec!`)](#arenavec-macro)
  - [Transaction Budgets](#transaction-budgets--cap-memory-per-request)
  - [Drop-Tracking](#drop-tracking--opt-in-destructor-execution)
- [API Reference](#api-reference)
  - [Arena](#arena)
  - [Transaction](#transaction)
  - [ArenaVec](#arenavec)
  - [ArenaStats](#arenastats)
  - [Checkpoint](#checkpoint)
  - [TxnStatus](#txnstatus)
  - [TxnDiff](#txndiff)
  - [TryReserveError](#tryreserveerror)
  - [ArenaVecIntoIter](#arenavecintoiter)
- [Performance](#performance)
- [Best Practices](#best-practices)
- [Feature Flags](#feature-flags)
- [Safety Considerations](#safety-considerations)
- [MSRV](#minimum-supported-rust-version)

---

## Why fastarena?

| Feature | fastarena | bumpalo | typed-arena |
|---------|-----------|---------|-------------|
| O(1) bump allocation | Yes | Yes | Yes |
| Heterogeneous types | Yes | Yes | No |
| Checkpoint / Rewind | Yes (O(1) snapshot) | No | No |
| RAII Transaction | Yes | No | No |
| Nested Savepoints | Yes | No | No |
| Transaction Budget | Yes | No | No |
| Transaction Metrics | Yes (`TxnDiff`) | No | No |
| `with_transaction` closure API | Yes | No | No |
| `ArenaVec::finish()` ownership transfer | Yes | No | N/A |
| Drop-tracking (opt-in) | Yes | No | Forced |
| Fallible allocation | Yes | Partial | No |
| `alloc_cache_aligned` | Yes | No | No |
| O(1) stats with utilization | Yes | Partial | No |
| `arenavec!` macro | Yes | No | No |
| Zero dependencies | Yes | Yes | Yes |

---

## Installation

```toml
[dependencies]
fastarena = "0.1.3"
```

With destructor tracking:

```toml
[dependencies]
fastarena = { version = "0.1.3", features = ["drop-tracking"] }
```

---

## Quick Start

### Basic Allocation

```rust
use fastarena::Arena;

let mut arena = Arena::new();

let x: &mut u64 = arena.alloc(42);
let squares: &mut [u32] = arena.alloc_slice(0u32..8);
let s: &str = arena.alloc_str("hello");

// Zero-cost reset — pages stay warm, no OS calls
arena.reset();
```

### Transactions — Auto-Rollback on Failure

No other arena allocator gives you RAII transactions. Allocations succeed or roll back as a unit — no leaks, no manual cleanup.

> **Note:** Each `alloc` call returns a `&mut T` that borrows the transaction.
> Don't hold references across subsequent allocations — use the value and let it
> drop before calling `alloc` again.

```rust
use fastarena::Arena;

let mut arena = Arena::new();

// Ok commits, Err rolls back — all allocations are atomic
let result: Result<u32, &str> = arena.with_transaction(|txn| {
    txn.alloc_str("fastarena");
    let score = txn.alloc(100u32);
    Ok(*score)
});
assert_eq!(result, Ok(100));

// Failed transaction — everything allocated inside is gone
arena.with_transaction(|txn| {
    txn.alloc(1u32);
    txn.alloc(2u32);
    Err("abort")  // both u32s rolled back automatically
});

// Infallible variant — commits even through panic
let val = arena.with_transaction_infallible(|txn| {
    *txn.alloc(7u32) * 6
});
assert_eq!(val, 42);
```

### Nested Savepoints

Transactions nest to arbitrary depth. Each savepoint is independently committable — roll back an inner scope without losing outer work.

```rust
use fastarena::Arena;

let mut arena = Arena::new();
let mut outer = arena.transaction();
outer.alloc_str("top-level");

{
    let mut inner = outer.savepoint();
    inner.alloc_str("speculative-opt");
    inner.alloc(999u32);
    // dropped without commit — inner work discarded, outer untouched
}

outer.alloc_str("confirmed");
outer.commit();  // "top-level" + "confirmed" survive
```

### ArenaVec with `finish()` — Transfer Ownership to the Arena

`ArenaVec` is a growable vector backed by arena memory. Call `finish()` to hand ownership to the arena — no destructor run, no copy. The slice lives as long as the arena.

```rust
use fastarena::{Arena, ArenaVec};

let mut arena = Arena::new();

let items: &mut [u32] = {
    let mut v = ArenaVec::new(&mut arena);
    for i in 0..1024 {
        v.push(i);
    }
    v.finish()  // ArenaVec consumed, slice now arena-owned
};

assert_eq!(items.len(), 1024);
assert_eq!(items[512], 512);
```

Or use the `arenavec!` macro for concise initialization:

```rust
use fastarena::{Arena, arenavec};

let mut arena = Arena::new();

let items: &mut [u32] = arenavec![in &mut arena; 0u32; 1024].finish();
assert_eq!(items[512], 0);

let values: &mut [u32] = arenavec![in &mut arena; 1, 2, 3].finish();
assert_eq!(values, &mut [1, 2, 3]);
```

### Transaction Budgets — Cap Memory per Request

Set a byte budget on any transaction. Exceed it and `alloc` panics (or `try_alloc` returns `None`). Zero-cost when unlimited.

> **Note:** `alloc(vec![0u8; N])` stores only the `Vec` struct (24 bytes) in the
> arena — the heap buffer is *not* arena-tracked. To budget actual data bytes,
> use `alloc_slice` or `alloc_slice_copy` instead.

```rust
use fastarena::Arena;

let mut arena = Arena::new();
let mut txn = arena.transaction();
txn.set_limit(4096);  // hard cap

// GOOD — alloc_slice copies data into arena, budget sees all bytes
txn.alloc_slice(vec![0u8; 2048]);  // ok — 2048 arena bytes
// txn.alloc_slice(vec![0u8; 4096]);  // panics: budget exceeded (2048 + 4096 > 4096)

// BAD — alloc(vec![...]) stores only the 24-byte Vec struct, heap is untracked
// txn.alloc(vec![0u8; 9999]);  // would NOT panic — budget sees 24 bytes

let remaining = txn.budget_remaining();  // introspect at any time
txn.commit();
```

### Drop-Tracking — Opt-In Destructor Execution

By default, fastarena never runs destructors (zero overhead). Enable `drop-tracking` to run them in LIFO order on `reset()` / `rewind()`.

```toml
[dependencies]
fastarena = { version = "0.1.3", features = ["drop-tracking"] }
```

```rust
use fastarena::Arena;

let mut arena = Arena::new();
let cp = arena.checkpoint();

arena.alloc(String::from("hello"));
arena.alloc(String::from("world"));

// With drop-tracking: drops fire in LIFO order ("world", then "hello")
// Without drop-tracking: no destructors, memory reclaimed instantly
arena.rewind(cp);
```

---

## API Reference

### Arena

The main bump-pointer allocator.

#### Constructors

| Method | Description |
|--------|-------------|
| `Arena::new()` | Creates arena with 64 KiB initial block |
| `Arena::with_capacity(bytes)` | Creates arena with custom initial block size |

```rust
let arena = Arena::new();
let arena = Arena::with_capacity(1024 * 1024); // 1 MiB initial
```

#### Allocation Methods

| Method | Signature | Description |
|--------|-----------|-------------|
| `alloc` | `fn alloc<T>(&mut self, val: T) -> &mut T` | O(1) single value |
| `alloc_slice` | `fn alloc_slice<T, I>(&mut self, iter: I) -> &mut [T]` | From `ExactSizeIterator` |
| `alloc_slice_copy` | `fn alloc_slice_copy<T: Copy>(&mut self, src: &[T]) -> &mut [T]` | Single `memcpy` for `Copy` types |
| `alloc_str` | `fn alloc_str(&mut self, s: &str) -> &str` | Copy string into arena |
| `alloc_uninit` | `fn alloc_uninit<T>(&mut self) -> &mut MaybeUninit<T>` | Uninitialized slot |
| `alloc_raw` | `fn alloc_raw(&mut self, size: usize, align: usize) -> NonNull<u8>` | Raw bytes with alignment |
| `alloc_zeroed` | `fn alloc_zeroed(&mut self, size: usize, align: usize) -> NonNull<u8>` | Zeroed raw bytes |
| `alloc_cache_aligned` | `fn alloc_cache_aligned(&mut self, size: usize) -> NonNull<u8>` | 64-byte aligned (SIMD-friendly) |

```rust
let mut arena = Arena::new();

let p = arena.alloc(Point { x: 1.0, y: 2.0 });
let slice = arena.alloc_slice(0u32..100);
let s = arena.alloc_str("hello");
let raw = arena.alloc_raw(4096, 4096);   // page-aligned
let buf = arena.alloc_cache_aligned(256); // cache-line aligned
```

#### Fallible Allocation

All allocation methods have `try_*` variants returning `Option`:

| Method | Signature |
|--------|-----------|
| `try_alloc` | `fn try_alloc<T>(&mut self, val: T) -> Option<&mut T>` |
| `try_alloc_slice` | `fn try_alloc_slice<T, I>(&mut self, iter: I) -> Option<&mut [T]>` |
| `try_alloc_slice_copy` | `fn try_alloc_slice_copy<T: Copy>(&mut self, src: &[T]) -> Option<&mut [T]>` |
| `try_alloc_str` | `fn try_alloc_str(&mut self, s: &str) -> Option<&str>` |
| `try_alloc_raw` | `fn try_alloc_raw(&mut self, size: usize, align: usize) -> Option<NonNull<u8>>` |
| `try_alloc_zeroed` | `fn try_alloc_zeroed(&mut self, size: usize, align: usize) -> Option<NonNull<u8>>` |
| `try_alloc_cache_aligned` | `fn try_alloc_cache_aligned(&mut self, size: usize) -> Option<NonNull<u8>>` |

```rust
let mut arena = Arena::with_capacity(64);
match arena.try_alloc(42u64) {
    Some(val) => { /* success */ }
    None => { /* out of memory */ }
}
```

#### Checkpoint / Rewind / Reset

| Method | Signature | Complexity | Description |
|--------|-----------|------------|-------------|
| `checkpoint` | `fn checkpoint(&self) -> Checkpoint` | O(1) | Snapshot current position |
| `rewind` | `fn rewind(&mut self, cp: Checkpoint)` | O(k) | Roll back to checkpoint |
| `reset` | `fn reset(&mut self)` | O(p) | Reset entire arena |

> **`reset()` complexity:** O(p) where p = peak block count since last reset.
> A `high_water_mark` tracks the highest block index ever reached. On reset,
> only blocks 0..=high_water_mark have their offsets zeroed — all their
> capacity is fully reusable with no waste. Single-block arenas pay O(1).
> No memory is freed — OS pages stay mapped and TLB-warm.

```rust
let mut arena = Arena::new();
arena.alloc(1u64);

let cp = arena.checkpoint();   // O(1) — copies 3 integers
arena.alloc(2u64);
arena.alloc(3u64);
arena.rewind(cp);              // 2 and 3 gone; blocks retained for reuse

// reset() — reclaims all memory for reuse, O(peak_blocks)
arena.reset();                 // fast, warm pages, no OS calls
```

#### Transaction Methods

| Method | Signature | Description |
|--------|-----------|-------------|
| `transaction` | `fn transaction(&mut self) -> Transaction<'_>` | Open manual transaction |
| `with_transaction` | `fn with_transaction<F, T, E>(&mut self, f: F) -> Result<T, E>` | Ok=commit, Err=rollback |
| `with_transaction_infallible` | `fn with_transaction_infallible<F, T>(&mut self, f: F) -> T` | Commits even through panic |
| `transaction_depth` | `fn transaction_depth(&self) -> usize` | Current nesting depth |

#### Introspection

| Method | Signature | Complexity |
|--------|-----------|------------|
| `stats` | `fn stats(&self) -> ArenaStats` | O(1) |
| `block_count` | `fn block_count(&self) -> usize` | O(1) |

---

### Transaction

A scoped RAII guard over an `Arena`. Auto-rolls back on drop unless committed.

#### Lifecycle

| Method | Signature | Description |
|--------|-----------|-------------|
| `commit` | `fn commit(self) -> TxnStatus` | Keep all allocations |
| `rollback` | `fn rollback(self) -> TxnStatus` | Explicit rollback |
| `savepoint` | `fn savepoint(&mut self) -> Transaction<'_>` | Open nested transaction |

#### Budget

| Method | Signature | Description |
|--------|-----------|-------------|
| `set_limit` | `fn set_limit(&mut self, bytes: usize)` | Set byte cap |
| `budget_remaining` | `fn budget_remaining(&self) -> Option<usize>` | Remaining budget |

#### Introspection

| Method | Signature | Description |
|--------|-----------|-------------|
| `bytes_used` | `fn bytes_used(&self) -> usize` | Bytes allocated since open |
| `diff` | `fn diff(&self) -> TxnDiff` | Allocation metrics |
| `depth` | `fn depth(&self) -> usize` | Nesting depth (1=top-level) |
| `arena_depth` | `fn arena_depth(&self) -> usize` | Arena's total nesting depth |
| `checkpoint` | `fn checkpoint(&self) -> Checkpoint` | The saved checkpoint |
| `arena_mut` | `fn arena_mut(&mut self) -> &mut Arena` | Direct arena access |
| `is_committed` | `fn is_committed(&self) -> bool` | Whether committed |

#### Allocation

Transaction exposes all the same allocation methods as Arena (`alloc`, `alloc_slice`, `alloc_slice_copy`, `alloc_str`, `alloc_raw`, `try_alloc`, `try_alloc_slice_copy`, `try_alloc_cache_aligned`, etc.) — all budget-checked.

#### Usage Patterns

**Closure API (recommended):**
```rust
let result = arena.with_transaction(|txn| {
    let x = txn.alloc(21u32);
    Ok(*x * 2)
}); // Ok(42) — committed

let result = arena.with_transaction(|txn| {
    txn.alloc(1u32);
    Err("fail")
}); // Err("fail") — rolled back
```

**Manual API:**
```rust
let mut txn = arena.transaction();
txn.alloc(1u32);
txn.alloc(2u32);
txn.commit(); // or drop to rollback
```

**Nested savepoints:**
```rust
let mut outer = arena.transaction();
outer.alloc(1u32);

{
    let mut inner = outer.savepoint();
    inner.alloc(2u32);
    // dropped — only inner rolled back
}

outer.commit(); // 1 survives
```

**Budget enforcement:**
```rust
let mut txn = arena.transaction();
txn.set_limit(1024);
txn.alloc_slice(vec![0u8; 512]);       // ok — 512 arena bytes
// txn.alloc_slice(vec![0u8; 1024]);   // panics: budget exceeded
txn.try_alloc_slice(vec![0u8; 1024]);  // returns None
txn.commit();
```

---

### ArenaVec

An append-only growable vector backed by arena memory.

#### Construction

| Method | Signature | Description |
|--------|-----------|-------------|
| `new` | `fn new(arena: &'arena mut Arena) -> Self` | Empty vector |
| `with_capacity` | `fn with_capacity(arena: &'arena mut Arena, cap: usize) -> Self` | Pre-allocated |

#### Operations

| Method | Signature | Description |
|--------|-----------|-------------|
| `push` | `fn push(&mut self, val: T)` | Amortized O(1) append |
| `try_push` | `fn try_push(&mut self, val: T) -> Result<(), T>` | Fallible push; returns value on OOM |
| `pop` | `fn pop(&mut self) -> Option<T>` | Remove last element |
| `clear` | `fn clear(&mut self)` | Clear, keep capacity |
| `extend_exact` | `fn extend_exact<I>(&mut self, iter: I)` | Batch from `ExactSizeIterator` |
| `extend_from_slice` | `fn extend_from_slice(&mut self, slice: &[T]) where T: Copy` | Single `memcpy` |
| `truncate` | `fn truncate(&mut self, len: usize)` | Shorten, dropping excess |
| `resize` | `fn resize(&mut self, new_len: usize, val: T) where T: Clone` | Grow or shrink |
| `finish` | `fn finish(self) -> &'arena mut [T]` | Transfer to arena |

#### Capacity

| Method | Signature | Description |
|--------|-----------|-------------|
| `reserve` | `fn reserve(&mut self, additional: usize)` | Reserve (may over-allocate) |
| `reserve_exact` | `fn reserve_exact(&mut self, additional: usize)` | Reserve exactly |
| `try_reserve` | `fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>` | Fallible |
| `try_reserve_exact` | `fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError>` | Fallible exact reserve |
| `capacity` | `fn capacity(&self) -> usize` | Total slots |

#### Introspection

| Method | Signature |
|--------|-----------|
| `len` | `fn len(&self) -> usize` |
| `is_empty` | `fn is_empty(&self) -> bool` |
| `as_slice` | `fn as_slice(&self) -> &[T]` |
| `as_mut_slice` | `fn as_mut_slice(&mut self) -> &mut [T]` |

#### Trait Implementations

`ArenaVec` implements `Index<usize>`, `IndexMut<usize>`, `Extend<T>`, `IntoIterator` (owned, `&`, `&mut`), and `ExactSizeIterator`.

#### Truncate / Resize

Shorten or grow the vector in-place:

```rust
let mut v = ArenaVec::new(&mut arena);
v.extend_exact([1u32, 2, 3, 4, 5]);

v.truncate(3);          // [1, 2, 3] — elements 4, 5 dropped
v.resize(6, 0);         // [1, 2, 3, 0, 0, 0] — fills with clone
v.resize(2, 0);         // [1, 2] — excess dropped
```

#### The `finish()` Semantic

This is the key differentiator from `bumpalo::collections::Vec`:

```rust
// finish() — ArenaVec consumed, no destructor runs, arena owns the data
let slice: &mut [u32] = {
    let mut v = ArenaVec::new(&mut arena);
    v.push(1); v.push(2); v.push(3);
    v.finish()
}; // slice lives as long as the arena

// drop without finish() — element destructors run immediately
{
    let mut v = ArenaVec::new(&mut arena);
    v.push(String::from("hello"));
} // String::drop() fires here; arena retains backing memory
```

#### Inside Transactions

Use `txn.arena_mut()` to create an `ArenaVec` within a transaction:

```rust
let mut txn = arena.transaction();
let slice = {
    let mut v = ArenaVec::new(txn.arena_mut());
    v.extend_exact([1u32, 2, 3]);
    v.finish()
};
txn.commit(); // slice survives
```

#### `arenavec!` Macro

The `arenavec!` macro provides concise `ArenaVec` construction. The arena
reference always follows the `in` keyword.

**Syntax:**

```text
arenavec![in &mut arena]            // empty
arenavec![in &mut arena; 1, 2, 3]   // list of elements
arenavec![in &mut arena; 0u32; 10]  // repeated element (requires T: Clone)
```

**Empty:**

```rust
use fastarena::{Arena, ArenaVec, arenavec};

let mut arena = Arena::new();
let v: ArenaVec<u32> = arenavec![in &mut arena];
assert!(v.is_empty());
```

**List of elements:**

```rust
use fastarena::{Arena, arenavec};

let mut arena = Arena::new();
let v = arenavec![in &mut arena; 1u32, 2, 3];
assert_eq!(v.as_slice(), &[1, 2, 3]);
```

**Repeated element** (requires `T: Clone`):

```rust
use fastarena::{Arena, arenavec};

let mut arena = Arena::new();
let v = arenavec![in &mut arena; 0u32; 10];
assert_eq!(v.len(), 10);
assert_eq!(v.as_slice(), &[0; 10]);
```

**With `finish()`:**

```rust
use fastarena::{Arena, arenavec};

let mut arena = Arena::new();
let slice = arenavec![in &mut arena; 1u32, 2, 3].finish();
assert_eq!(slice, &mut [1, 2, 3]);
```

**Inside transactions:**

```rust
use fastarena::{Arena, arenavec};

let mut arena = Arena::new();
let mut txn = arena.transaction();
{
    let v = arenavec![in txn.arena_mut(); 10u32, 20, 30];
    assert_eq!(v.as_slice(), &[10, 20, 30]);
}
txn.commit();
```

---

### ArenaStats

O(1) memory usage snapshot.

```rust
pub struct ArenaStats {
    pub bytes_allocated: usize,   // In-use bytes
    pub bytes_reserved: usize,    // Total reserved across all blocks
    pub block_count: usize,       // Number of blocks
}

impl ArenaStats {
    pub fn utilization(&self) -> f64;  // bytes_allocated / bytes_reserved
    pub fn bytes_idle(&self) -> usize; // bytes_reserved - bytes_allocated
}
```

```rust
let stats = arena.stats();
println!("{:.1}% utilized", stats.utilization() * 100.0);
println!("{} bytes idle", stats.bytes_idle());
```

---

### Checkpoint

Opaque snapshot of arena position. `Copy` — zero-cost to pass around.

```rust
#[derive(Debug, Clone, Copy)]
pub struct Checkpoint { /* opaque */ }
```

Implements `Display` — prints `Checkpoint(block=N, offset=N, bytes=N)`.

**Usage:**

```rust
use fastarena::Arena;

let mut arena = Arena::new();
let x = arena.alloc(10u64);

let cp = arena.checkpoint();   // O(1) snapshot — copies 3 integers
arena.alloc(20u64);
arena.alloc(30u64);
// arena now holds [10, 20, 30]

arena.rewind(cp);              // 20 and 30 gone; blocks retained for reuse
assert_eq!(*x, 10);           // pre-checkpoint data still valid

// Checkpoint is Copy — pass it around freely
let cp2 = arena.checkpoint();
let cp3 = cp2;                 // no cost
arena.alloc(40u64);
arena.rewind(cp3);             // 40 gone

// Print checkpoint info
let cp = arena.checkpoint();
println!("{cp}");              // e.g. "Checkpoint(block=0, offset=8, bytes=8)"
```

**Speculative work pattern:**

```rust
use fastarena::Arena;

let mut arena = Arena::new();
arena.alloc(1u64);

let cp = arena.checkpoint();

// Try some work — may fail
let success = false;
arena.alloc(2u64);
arena.alloc(3u64);

if !success {
    arena.rewind(cp);  // undo speculative allocations, keep original
}
```

---

### TxnStatus

Outcome of `Transaction::commit` or `Transaction::rollback`.

```rust
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TxnStatus {
    Committed,
    RolledBack,
}
```

Implements `Display` — prints `"committed"` or `"rolled back"`.

**Usage:**

```rust
use fastarena::{Arena, TxnStatus};

let mut arena = Arena::new();

let mut txn = arena.transaction();
txn.alloc(42u32);
let status = txn.commit();
assert_eq!(status, TxnStatus::Committed);
println!("{status}");  // "committed"

// Rollback returns RolledBack
let mut txn = arena.transaction();
txn.alloc(1u32);
let status = txn.rollback();
assert_eq!(status, TxnStatus::RolledBack);
println!("{status}");  // "rolled back"
```

---

### TxnDiff

Allocation metrics for a single transaction scope, returned by `Transaction::diff`.

```rust
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct TxnDiff {
    pub bytes_allocated: usize,  // bytes allocated since txn opened
    pub blocks_touched: usize,   // blocks written to during txn
}
```

Implements `Display` — prints `"N bytes in M block(s)"`.

**Usage:**

```rust
use fastarena::Arena;

let mut arena = Arena::new();
let mut txn = arena.transaction();

let diff = txn.diff();
assert_eq!(diff.bytes_allocated, 0);
assert_eq!(diff.blocks_touched, 0);

txn.alloc(100u64);
txn.alloc_slice(vec![0u8; 256]);

let diff = txn.diff();
assert!(diff.bytes_allocated > 0);
assert_eq!(diff.blocks_touched, 1);
println!("{diff}");  // e.g. "264 bytes in 1 block(s)"

txn.commit();
```

---

### TryReserveError

Error returned by `ArenaVec::try_reserve` and `ArenaVec::try_reserve_exact`.

```rust
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TryReserveError {
    CapacityOverflow,  // requested size exceeds usize::MAX
    AllocError,        // arena is out of memory
}
```

Implements `Display` and `std::error::Error`.

**Usage:**

```rust
use fastarena::{Arena, ArenaVec, TryReserveError};

let mut arena = Arena::with_capacity(64);
let mut v = ArenaVec::new(&mut arena);

match v.try_reserve_exact(usize::MAX) {
    Ok(()) => unreachable!(),
    Err(TryReserveError::CapacityOverflow) => println!("overflow"),
    Err(TryReserveError::AllocError) => println!("oom"),
}
```

---

### ArenaVecIntoIter

Owning iterator over an `ArenaVec`. Returned by `ArenaVec::into_iter()` (i.e. `for x in vec`). Drains elements front-to-back; drops remaining elements in LIFO order when the iterator is dropped.

```rust
pub struct ArenaVecIntoIter<'arena, T> { /* opaque */ }

impl<T> Iterator for ArenaVecIntoIter<'_, T> { type Item = T; }
impl<T> ExactSizeIterator for ArenaVecIntoIter<'_, T> {}
```

**Usage:**

```rust
use fastarena::{Arena, ArenaVec};

let mut arena = Arena::new();
let v: ArenaVec<u32> = {
    let mut v = ArenaVec::new(&mut arena);
    v.extend_exact([10, 20, 30, 40, 50]);
    v
};

// for loop consumes the ArenaVec via IntoIterator
let mut collected = Vec::new();
for val in v {
    collected.push(val);
}
assert_eq!(collected, vec![10, 20, 30, 40, 50]);

// size_hint is exact
let mut v2 = ArenaVec::new(&mut arena);
v2.extend_exact([1, 2, 3]);
let iter = v2.into_iter();
assert_eq!(iter.size_hint(), (3, Some(3)));
assert_eq!(iter.len(), 3);
```

---

## Performance

### Head-to-Head: fastarena vs bumpalo vs typed-arena

| Benchmark | fastarena | bumpalo | typed-arena |
|-----------|-----------|---------|-------------|
| alloc 1k items | **894 ns** | 937 ns | 1072 ns |
| alloc_slice n=64 | **10 ns** | 53 ns | 84 ns |
| alloc_slice n=1024 | **64 ns** | 518 ns | — |
| alloc_str (100x) | 202 ns | **190 ns** | — |
| ArenaVec n=16 | **30 ns** | 42 ns | 34 ns |
| ArenaVec n=256 | **263 ns** | 346 ns | 516 ns |
| ArenaVec n=4096 | **3.4 µs** | 8.5 µs | 11.1 µs |
| 10k allocs + reset | **15.0 µs** | 15.1 µs | 2.8 µs† |
| reset (1 block) | **20 ns** | 696 ns | — |
| 128 KB alloc | 63 ns | **27 ns** | — |

† typed-arena drops and re-creates the arena each iteration; not directly comparable.

### Fast Path vs Box/Vec

| Benchmark | fastarena | Box/Vec | Speedup |
|-----------|-----------|---------|---------|
| alloc 1k u64 | **864 ns** | 15732 ns | **18x** |
| alloc_slice n=512 | **59 ns** | 65 ns | ~1x |
| alloc_slice n=4096 | **246 ns** | 236 ns | ~1x |
| 10k allocs + reset | **15.5 µs** | 211.8 µs | **14x** |
| `Arena::new` | **22 ns** | — | — |
| `checkpoint()` | **93 ns** | — | — |
| `reset` 1 block | **20 ns** | — | — |
| `commit` 16 allocs | **1.3 µs** | — | — |

### Why fastarena excels

- **5-8x faster slice allocation** than bumpalo (batch write in tight loop)
- **2-3x faster ArenaVec** than bumpalo/typed-arena for bulk collection building
- **Tied on alloc** — on par with bumpalo for single-item allocation
- **14x faster than Box** for bulk alloc + reclaim cycles
- **35x faster reset** than bumpalo for single-block arenas (O(peak) block reuse)
- **Zero dependencies**: No external crates required

### Complexity

| Operation | Complexity |
|-----------|------------|
| `alloc` (fast path) | O(1) |
| `alloc` (new block) | O(1) amortized |
| `checkpoint` | O(1) |
| `rewind` | O(k) where k ≈ 0–1 |
| `reset` | O(p) where p = peak blocks since last reset |
| `stats` | O(1) |

---

## Best Practices

### 1. Pre-allocate for Known Workloads

```rust
// Avoids early block chaining
let arena = Arena::with_capacity(1024 * 1024);
```

### 2. Reset Over Recreate

```rust
// Good: warm pages, no OS calls, O(peak_blocks) — all capacity reused
let mut arena = Arena::new();
for batch in batches {
    process(&mut arena, batch);
    arena.reset();  // zeros only blocks used since last reset
}
```

`reset()` tracks a `high_water_mark` internally — it only touches blocks
that were actually used, leaving untouched blocks' offsets at zero.
All memory is fully reusable with zero waste.

### 3. Use Transactions for Fallible Operations

```rust
arena.with_transaction(|txn| {
    let parsed = parse(txn, input)?;
    let optimized = optimize(txn, parsed)?;
    Ok(optimized)
}); // auto-rollback on any Err
```

### 4. Use `finish()` When Building Then Freezing

```rust
let slice: &mut [u32] = {
    let mut v = ArenaVec::new(&mut arena);
    for i in 0..n { v.push(compute(i)); }
    v.finish()  // arena owns it now
};
```

### 5. Set Budgets in Server Contexts

```rust
let mut txn = arena.transaction();
txn.set_limit(64 * 1024); // 64 KB per request
handle_request(&mut txn);
txn.commit();
```

---

## Feature Flags

| Flag | Default | Description |
|------|---------|-------------|
| `drop-tracking` | Off | Run destructors in LIFO order on `reset`/`rewind` |

```toml
# Default (zero-cost, no destructor calls)
fastarena = "0.1.3"

# With drop-tracking
fastarena = { version = "0.1.3", features = ["drop-tracking"] }
```

---

## Safety Considerations

### Lifetime Guarantees

Arena-allocated references are invalidated by `reset()` / `rewind()`:

```rust
let mut arena = Arena::new();
let x = arena.alloc(42);
arena.reset();
// x is now dangling — UB if used
```

### No Thread Safety

`Arena` is `!Send` + `!Sync`. For multi-threaded use:

```rust
// Thread-local (recommended for request-scoped workloads)
thread_local! {
    static ARENA: RefCell<Arena> = RefCell::new(Arena::new());
}

// Or wrap in Mutex (adds contention)
let arena = Mutex::new(Arena::new());
```

### `finish()` vs `drop()`

- `finish()` — destructors **not** called; arena owns the data
- `drop()` without `finish()` — destructors run immediately; arena retains memory

---

## Minimum Supported Rust Version

**Rust 1.66.0**
