Skip to main content

Crate vecpool

Crate vecpool 

Source
Expand description

Thread-local pool of reusable Vec buffers with cross-type reuse.

vecpool eliminates repeated heap allocations by recycling Vec buffers. When a PoolVec<T> is dropped, its underlying allocation is returned to a thread-local pool. The next call to get or with_capacity can reuse that buffer instead of asking the allocator for new memory.

§Key Features

  • Cross-type reuse: Buffers are keyed by (size_of::<T>(), align_of::<T>()), so a Vec<i32> buffer can be reused as a Vec<u32> (same size and alignment).
  • Zero synchronization: Each thread has its own pool. No mutexes, no atomics.
  • Transparent: PoolVec<T> dereferences to Vec<T> — use all Vec methods directly.
  • Fast path: Common power-of-two sizes (1–256 bytes) use a direct array lookup instead of hashing.

§Quick Start

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

// Get a vec from the pool (or a fresh one if the pool is empty)
let mut v = get::<u32>();
v.push(1);
v.push(2);
v.push(3);
assert_eq!(v.len(), 3);

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

// The next request reuses the same buffer — no allocation!
let v2 = get::<u32>();
assert!(v2.capacity() >= 3);

§Cross-Type Reuse

Types with the same memory layout share a pool lane:

use vecpool::get;

let mut v = get::<i32>();
v.push(1);
v.push(2);
let cap = v.capacity();
drop(v);

// u32 has the same size (4) and alignment (4) as i32 — reuses the buffer
let v2 = get::<u32>();
assert_eq!(v2.capacity(), cap);

§Pre-allocating Capacity

use vecpool::with_capacity;

// If the pool has a buffer with >= 1000 capacity, reuse it.
// Otherwise, allocate fresh with Vec::with_capacity(1000).
let v = with_capacity::<u64>(1000);
assert!(v.capacity() >= 1000);

§Wrapping Existing Vecs

use vecpool::PoolVec;

let data = vec![1u32, 2, 3, 4, 5];
let pooled = PoolVec::from(data);
// When `pooled` is dropped, its buffer enters the pool for future reuse.

§Escaping the Pool

use vecpool::get;

let mut v = get::<u32>();
v.push(42);

// Extract the inner Vec — buffer will NOT be returned to the pool
let owned: Vec<u32> = v.into_vec();
assert_eq!(owned, [42]);

Calling .into_iter() also escapes the pool (it calls into_vec() internally).

§Collecting Iterators

PoolVec<T> implements FromIterator, so .collect() works directly:

use vecpool::PoolVec;

let v: PoolVec<i32> = (0..100).map(|x| x * 2).collect();
assert_eq!(v.len(), 100);

§Memory Management

Pooled buffers persist for the lifetime of the thread. To reclaim memory:

use vecpool::{get, clear_pool};

let mut v = get::<u32>();
v.push(1);
drop(v);

// Free all pooled buffers on this thread
clear_pool();

§Thread Safety

PoolVec<T> is Send when T: Send. If moved to another thread and dropped there, the buffer is returned to that thread’s pool. There is no cross-thread synchronization.

§Performance

The pool uses a two-tier lookup:

  • Fast path: Types with power-of-two size ≤ 256 and natural alignment (e.g., u8, u32, u64, usize) use a direct array index via trailing_zeros() — a single CPU instruction.
  • Slow path: All other layouts fall back to an FxHashMap.

Structs§

PoolVec
A wrapper around Vec<T> that returns its buffer to the thread-local pool on drop.

Functions§

clear_pool
Drain all buffers from the thread-local pool, freeing their memory.
get
Get an empty vec from the thread-local pool.
with_capacity
Get a vec from the pool with at least capacity elements of space.