How to Use Custom Allocators with mulle-buffer

Yes, mulle-buffer fully supports custom allocators through the struct mulle_allocator interface, allowing you to specify custom memory management strategies for buffer creation, growth, and destruction operations.

The mulle-c/mulle-buffer library provides comprehensive support for custom allocators, enabling you to inject specialized memory allocation logic while maintaining automatic fallback to mulle_default_allocator when NULL is provided.

The mulle_allocator Interface

Custom allocators must conform to the struct mulle_allocator interface, which defines function pointers for malloc, realloc, and free operations. The buffer stores this allocator in its internal _allocator field and invokes it for all dynamic memory operations.

When you pass NULL as the allocator argument to any buffer function, the library automatically substitutes mulle_default_allocator. This ensures that buffers always have a valid allocator while remaining agnostic to whether the allocation strategy is custom or standard.

Setting Allocators During Buffer Creation

You can specify a custom allocator when creating heap-allocated buffers or initializing stack-based buffer structures.

Dynamic Buffer Creation

The mulle_buffer_create function defined in src/mulle-buffer.c accepts an allocator parameter and stores it in the buffer's metadata. According to the implementation in src/mulle-buffer.c:28-40, the function forwards the allocator to internal initialization helpers.

#include <mulle-buffer/mulle-buffer.h>

int main(void)
{
    struct mulle_allocator *my_allocator = &custom_allocator;
    
    /* Create buffer using custom allocator for all memory operations */
    struct mulle_buffer *buf = mulle_buffer_create(my_allocator);
    if (!buf)
        return 1;
    
    mulle_buffer_add_string(buf, "Hello, custom allocator!");
    mulle_buffer_destroy(buf);
    return 0;
}

Stack-Based Initialization

For stack-allocated struct mulle_buffer instances, use mulle_buffer_init with the same allocator parameter pattern. This stores the allocator pointer locally for subsequent growth operations.

Pre-Allocated Memory Patterns

mulle-buffer supports initializing buffers with existing memory blocks while specifying an allocator for future growth. This is useful for stack-backed buffers that may need to spill to heap storage.

Static Stack Storage

The mulle_buffer_init_with_static_bytes function, defined in src/mulle-buffer.h:146-156, accepts a storage buffer and an allocator. If you pass NULL as the allocator, the buffer operates in non-growable mode.

char stack_storage[256];
struct mulle_buffer buf;

/* Use stack storage initially, allow heap growth via default allocator */
mulle_buffer_init_with_static_bytes(&buf, 
                                   stack_storage, 
                                   sizeof(stack_storage), 
                                   NULL);

External Memory Blocks

Similarly, mulle_buffer_init_with_allocated_bytes lets you provide pre-allocated heap memory along with a custom allocator for future reallocations. Both initializers store the allocator after setting up the static storage, as shown in the header implementation.

Runtime Allocator Queries and Updates

You can inspect or modify the allocator associated with an existing buffer at runtime. The mulle_buffer_set_allocator function, implemented in src/mulle-buffer.h:86-90, updates the _allocator field or falls back to the default if you pass NULL.

/* Query current allocator */
struct mulle_allocator *alloc = mulle_buffer_get_allocator(buf);
if (alloc == &mulle_default_allocator) {
    /* Using default allocator */
}

/* Switch to custom allocator for remaining operations */
mulle_buffer_set_allocator(buf, &my_custom_allocator);

Allocator Usage in Growth Operations

All dynamic memory operations retrieve the stored allocator before invoking memory functions. Functions like mulle_buffer_grow, mulle_buffer_guarantee, and mulle_buffer_add_bytes obtain the allocator via mulle_buffer_get_allocator and pass it to the underlying _mulle__buffer_* helpers.

As shown in src/mulle-buffer.h:572-582, the growth logic uses the stored allocator for reallocation operations, ensuring consistent memory management throughout the buffer's lifecycle.

The test suite in [test/buffer/allocator.c](https://github.com/mulle-c/mulle-buffer/blob/master/test/buffer/allocator.c) validates this behavior, confirming that NULL allocators are correctly replaced by the default and that custom allocators propagate through all growth operations.

Summary

  • Full custom allocator support via the struct mulle_allocator interface, with automatic fallback to mulle_default_allocator when NULL is specified.
  • Allocator storage occurs in the buffer's _allocator field during creation via mulle_buffer_create or initialization functions (see src/mulle-buffer.c:28-40).
  • Flexible initialization through mulle_buffer_init_with_static_bytes and mulle_buffer_init_with_allocated_bytes, allowing pre-allocated memory with custom growth allocators (see src/mulle-buffer.h:146-L156).
  • Runtime modification via mulle_buffer_set_allocator, which stores the allocator or falls back to default if NULL is passed (see src/mulle-buffer.h:86-90).
  • Consistent internal usage ensures all growth operations use the stored allocator retrieved via mulle_buffer_get_allocator (see src/mulle-buffer.h:572-582).

Frequently Asked Questions

Can I use a stack-only buffer without any heap allocator?

Yes. Pass NULL as the allocator to mulle_buffer_init_with_static_bytes. This creates a non-growable buffer that operates strictly within the provided stack storage. If you attempt to add data beyond the static buffer's capacity, the operation will fail rather than allocate heap memory.

What happens if I pass NULL as the allocator to mulle_buffer_create?

The function automatically substitutes mulle_default_allocator before storing the pointer in the buffer structure. According to the implementation in src/mulle-buffer.c, all creation and initialization functions perform this NULL-check fallback to ensure the buffer always has a valid allocator for memory operations.

Can I change the allocator after the buffer is already in use?

Yes. Use mulle_buffer_set_allocator to update the allocator pointer at any time. Subsequent growth operations will use the new allocator for reallocations, though existing allocated memory remains managed by the previous allocator until freed. You should ensure allocator consistency when the buffer frees its memory during destruction.

Does mulle-buffer support arena allocators or memory pools?

Yes. Any allocator conforming to the struct mulle_allocator interface—including arena allocators, memory pools, or tracing allocators—can be used with mulle-buffer. The library treats the allocator as an opaque interface, invoking only the standard malloc, realloc, and free function pointers defined in the structure.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →