THE FOUNDATION

fwAlloc — Bump Allocator

O(1) bump allocation from a stack-allocated buffer. Bulk free of entire request contexts. No GC, no fragmentation, no overhead.

What It Does

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.

Trace Levels

fwAlloc uses trace levels 20–39. See the full trace level table for all assignments across the fw-lib stack.

Functions

faBufferInit

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.

faAlloc

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.

faStrdup

char* faStrdup(FaAlloc* faP, const char* s);

String duplication from the fwAlloc pool — like strdup() but backed by the arena rather than malloc.

faRealloc

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.

faBufferReset

void faBufferReset(FaAlloc* faP, bool reuse);

Releases all allocations at once:

Performance Characteristics

OperationTime ComplexityNotes
faAlloc (in-buffer)O(1)Simple pointer arithmetic
faAlloc (new buffer)O(1)**Excludes calloc overhead; rare event
faStrdupO(n)n = string length
faReallocO(n)n = data size to copy
faBufferResetO(m)m = number of overflow buffers allocated

Memory Layout

[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().

Usage Example

#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);
}