vecpool 0.1.0

Thread-local pool of reusable Vec buffers with cross-type reuse
Documentation
# vecpool

[![Crates.io](https://img.shields.io/crates/v/vecpool.svg)](https://crates.io/crates/vecpool)
[![Documentation](https://img.shields.io/docsrs/vecpool)](https://docs.rs/vecpool)
[![License](https://img.shields.io/crates/l/vecpool.svg)](LICENSE)

A thread-local pool of reusable `Vec` buffers with cross-type reuse.

## Why?

Allocating and deallocating `Vec`s in a loop is expensive. `vecpool` recycles the underlying heap buffers so subsequent allocations are free — just a pointer pop from a thread-local stack.

Unlike per-type pools, `vecpool` keys buffers by *memory layout* `(size, align)`, so a buffer originally used for `Vec<i32>` can be reused for `Vec<u32>`, `Vec<f32>`, or any other 4-byte aligned type.

## Usage

```toml
[dependencies]
vecpool = "0.1"
```

```rust
use vecpool::{get, with_capacity, PoolVec};

// Get a vec from the pool
let mut v = get::<u32>();
v.push(1);
v.push(2);
v.push(3);

// Use it exactly like a Vec<u32>
assert_eq!(v.len(), 3);
assert_eq!(&*v, &[1, 2, 3]);

// On drop, the buffer is returned to the pool
drop(v);

// Next request reuses the buffer — zero allocation cost
let v2 = get::<u32>();
assert!(v2.capacity() >= 3);
```

## Features

- **Cross-type reuse**`Vec<i32>``Vec<u32>``Vec<f32>` share the same pool lane
- **Zero synchronization** — one pool per thread, no mutexes or atomics
- **Transparent**`PoolVec<T>` derefs to `Vec<T>`, all Vec methods work
- **Fast** — power-of-two sizes use a direct array lookup (`trailing_zeros`), no hashing
- **Safe** — all unsafe code is Miri-tested, no undefined behavior

## API Overview

| Function / Method | Description |
|---|---|
| `get::<T>()` | Get an empty pooled vec |
| `with_capacity::<T>(n)` | Get a pooled vec with at least `n` capacity |
| `PoolVec::new()` | Alias for `get::<T>()` |
| `PoolVec::from(vec)` | Wrap an existing `Vec<T>` for pool return on drop |
| `pool_vec.into_vec()` | Extract the inner `Vec<T>` (skips pool return) |
| `.collect::<PoolVec<T>>()` | Collect an iterator into a pooled vec |
| `pool_vec.into_iter()` | Iterate elements (buffer escapes the pool) |
| `clear_pool()` | Free all pooled buffers on the current thread |

## How It Works

1. Each thread has a local pool with "lanes" keyed by `(size_of::<T>(), align_of::<T>())`
2. Each lane is a LIFO stack of raw buffers (pointer + byte capacity)
3. `get::<T>()` pops a buffer from the matching lane and reconstructs a `Vec<T>` with `len = 0`
4. On drop, `PoolVec<T>` clears elements, extracts the raw buffer, and pushes it back
5. If no buffer is available, a fresh `Vec` is allocated normally

### Fast Path

For types where `size == align` and size is a power of two ≤ 256 (covers all primitives, pointers, and most small types), the pool uses a fixed-size array indexed by `size.trailing_zeros()`. This avoids hashing entirely.

### Thread Safety

`PoolVec<T>: Send` where `T: Send`. If a `PoolVec` is moved to another thread and dropped there, the buffer goes to that thread's pool. No synchronization is needed.

If the thread-local pool is being destroyed (thread exit), the buffer is freed normally instead of being pooled.

## Benchmarks

Run benchmarks with:

```bash
cargo bench
```

The benchmark suite compares pooled vs unpooled allocation across different element sizes, pool depths, and usage patterns.

## Minimum Supported Rust Version

Rust 2024 edition (1.85+).

## License

MIT