santh-bufpool
Fast, lock-free buffer recycling for Rust with fixed size classes and thread-local caching.
Overview
santh-bufpool provides a typed buffer pool that eliminates allocator churn in hot paths. It pre-allocates buffers in four size classes—4 KiB, 64 KiB, 256 KiB, and 1 MiB—and services checkout requests via lock-free queues (crossbeam-queue). When a class is exhausted, the pool falls back to a fresh heap allocation rather than blocking.
Buffers are automatically zeroed before reuse, making the pool suitable for security-sensitive workloads. A small thread-local cache (up to 4 buffers per thread) further reduces cross-thread queue contention.
Features
- Lock-free checkout/return —
ArrayQueueper size class, no mutexes. - Zero-allocation fast path — thread-local cache bypasses the global queue entirely.
- Automatic zeroing — every buffer is wiped before the next checkout.
- NUMA-aware placement — optional best-effort allocation on a specific NUMA node (requires the
numafeature andkernelkit). - Immutable sharing — freeze a
PoolBufferinto aSend + SyncFrozenBuffer.
Quick Start
use ;
let pool = new;
let mut buf = pool.checkout.unwrap;
buf.copy_from_slice;
assert_eq!;
// buffer returns to the pool (or TLS cache) on drop
Size Classes
| Requested bytes | Allocated capacity |
|---|---|
1..=4096 |
4 KiB |
4097..=65536 |
64 KiB |
65537..=262144 |
256 KiB |
262145..=1048576 |
1 MiB |
> 1048576 |
exact requested size (fallback) |
Thread-Local Caching
Each thread keeps a small private stash of recently returned buffers. When you check out a buffer, the pool first checks the thread-local cache before hitting the shared queue. This means repeated checkout/return cycles on the same thread are typically contention-free.
The cache is drained automatically when the thread exits, returning buffers to their original pools.
NUMA Placement
Enable the numa feature to request best-effort allocation on a specific NUMA node:
[]
= { = "0.1", = ["numa"] }
use ;
let pool = new;
If the node-specific allocation fails, the pool transparently falls back to the default heap allocator.
Safety & Security
- Buffers are zeroed on every return path (
PoolBuffer::drop,FrozenBuffer::drop, and TLS cache drain). - The pool rejects requests larger than
isize::MAXto avoid undefined behavior in slice construction. PoolBufferexposesDerefMutonly while uniquely owned;freeze()converts it into an immutableFrozenBufferthat can cross thread boundaries safely.
License
MIT