O(1) bump allocation from a stack-allocated buffer. Bulk free of entire request contexts. No GC, no fragmentation, no overhead.
fwAlloc is a bump (arena) allocator designed for the request-response lifecycle of a GE. You pass faBufferInit a char[] array — typically embedded in a __thread or static per-connection struct (e.g. a char buf[8*1024] inside the connection state). The buffer is just memory that already exists; there is no runtime allocation cost. Each allocation from that buffer is an O(1) pointer increment with no locking, no fragmentation, and no per-object bookkeeping. If the initial buffer is large enough for the entire REST request — and a few KB is usually more than enough — the result is zero calls to malloc/free for the entire request cycle. At the end of the request, the arena is reset in a single operation.
When the initial buffer is exhausted, fwAlloc chains additional buffers allocated via calloc, keeping allocation alive without reallocation or copying. The chain is walked and released at bulk-free time. But the design goal is to avoid this path entirely for typical requests.
calloc, transparently linked to the current chainfwAlloc uses trace levels 20–39. See the full trace level table for all assignments across the fw-lib stack.
void faBufferInit(
FaAlloc* faP, // Pointer to FaAlloc structure
char* buf, // Initial buffer (can be stack-allocated)
unsigned long long bufSize, // Size of initial buffer
unsigned long long allocSize, // Size for subsequent allocations
FaErrorHook errorHook, // Optional error callback (NULL if not needed)
const char* name // Debug name for this allocator
);
Initializes the allocator. Must be called before any allocations. The initial buffer can be a stack array, avoiding even the first malloc.
char* faAlloc(FaAlloc* faP, unsigned long long size);
Allocates size bytes from the pool. Fast path: if the current buffer has space, this is a pointer increment — O(1). Slow path: if the buffer is exhausted, a new buffer is allocated via calloc and chained in.
char* faStrdup(FaAlloc* faP, const char* s);
String duplication from the fwAlloc pool — like strdup() but backed by the arena rather than malloc.
char* faRealloc(FaAlloc* faP, char* origBuf, unsigned long long origSize,
unsigned long long newSize);
Resizes an allocation by allocating a new block and copying the data. The original space is not reclaimed individually — it is released along with everything else at faBufferReset() time.
void faBufferReset(FaAlloc* faP, bool reuse);
Releases all allocations at once:
reuse = true — reset and reinitialize with the original initial buffer; ready for the next request cyclereuse = false — free all dynamically allocated buffers; use at final shutdown| Operation | Time Complexity | Notes |
|---|---|---|
faAlloc (in-buffer) | O(1) | Simple pointer arithmetic |
faAlloc (new buffer) | O(1)* | *Excludes calloc overhead; rare event |
faStrdup | O(n) | n = string length |
faRealloc | O(n) | n = data size to copy |
faBufferReset | O(m) | m = number of overflow buffers allocated |
[Initial Buffer (stack or pre-allocated)]
├─ [alloc 1] [alloc 2] [alloc 3] [unused...]
[Dynamic Buffer 1] (allocated when initial runs out)
├─ [metadata] [alloc 4] [alloc 5] [unused...]
[Dynamic Buffer 2]
└─ [metadata] [alloc 6] [unused...]
All dynamic buffers are linked together and released in one pass by faBufferReset().
#include "fwAlloc/fwAlloc.h"
// Per-thread connection state with embedded allocator buffer
typedef struct {
FaAlloc alloc;
char allocBuffer[8 * 1024];
// ... other per-connection fields ...
} ConnectionState;
__thread ConnectionState connState;
void handleRequest(void)
{
// Initialize with the 8KB buffer embedded in thread-local state
faBufferInit(&connState.alloc, connState.allocBuffer,
sizeof(connState.allocBuffer), 8192, NULL, "request");
// Fast O(1) allocations — no malloc involved
char* buf1 = faAlloc(&connState.alloc, 256);
char* str = faStrdup(&connState.alloc, "hello world");
char* buf2 = faAlloc(&connState.alloc, 1024);
// ... process request ...
// Reset for next request (reuse the same buffer — zero malloc/free)
faBufferReset(&connState.alloc, true);
}