# How to Create a Persistent String Using mulle-buffer

> Learn to create a persistent string with mulle-buffer. Use mulle_buffer_make_string and mulle_buffer_extract_string for a heap-allocated C string that survives buffer destruction.

- Repository: [mulle-c/mulle-buffer](https://github.com/mulle-c/mulle-buffer)
- Tags: how-to-guide
- Published: 2026-03-07

---

**To create a persistent string using mulle-buffer, first ensure the buffer is null-terminated with `mulle_buffer_make_string()`, then call `mulle_buffer_extract_string()` to obtain a heap-allocated C string that remains valid after the buffer itself is destroyed.**

The `mulle-c/mulle-buffer` repository provides a dynamic byte array implementation for C that can be safely converted into independent, null-terminated strings. Unlike temporary buffer contents that become invalid when the buffer scope ends, properly extracted strings persist on the heap until explicitly freed. This guide demonstrates how to create a persistent string using mulle-buffer through the exact API calls and macro-based workflows defined in the source code.

## The Two-Step Process for Persistent Strings

Creating a persistent string requires two distinct operations: ensuring null termination and detaching the data from the buffer structure. According to the implementation in [`src/mulle--buffer.c`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.c), these steps guarantee that the returned pointer remains valid independent of the original buffer's lifetime.

### Step 1: Null-Terminate with mulle_buffer_make_string

Before extraction, the buffer must contain a terminating `'\0'` byte. The function `mulle_buffer_make_string()` (backed by `_mulle__buffer_make_string` in [`src/mulle--buffer.c`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.c) at lines 196-215) appends a null terminator if one is not already present. This function returns status codes indicating whether the buffer required modification, ensuring the internal storage is properly prepared for C string operations.

### Step 2: Extract with mulle_buffer_extract_string

The `mulle_buffer_extract_string()` function, declared as an inline wrapper in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) at lines 702-710, performs the actual extraction:

```c
static inline void *mulle_buffer_extract_string( struct mulle_buffer *buffer )
{
    return( _mulle__buffer_extract_string( (struct mulle__buffer *) buffer,
                                           buffer->_allocator));
}

```

Internally, the implementation in [`src/mulle--buffer.c`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.c) (lines 218-227) first invokes the make_string logic, then shrinks the allocation to the exact size via `_mulle__buffer_size_to_fit`, and finally returns the pointer. The returned string is allocated using the buffer's configured allocator and **must be freed with `mulle_free()`** when no longer needed.

## Manual API Implementation

For explicit control over buffer lifecycle and string extraction, use the manual API. This approach is defined in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) and allows you to manage each allocation step discretely.

```c
#include "mulle-buffer.h"

int main(void)
{
    struct mulle_buffer *buf;
    char               *persistent;

    /* 1️⃣  Create a flexible buffer that will grow as needed */
    buf = mulle_buffer_create( NULL );                     /* uses default allocator */

    /* 2️⃣  Build the string */
    mulle_buffer_add_string( buf, "Hello, " );
    mulle_buffer_add_string( buf, "world!" );

    /* 3️⃣  Ensure null‑termination (optional – extract does it anyway) */
    mulle_buffer_make_string( buf );

    /* 4️⃣  Extract a persistent copy */
    persistent = mulle_buffer_extract_string( buf );

    /* 5️⃣  The original buffer can now be destroyed safely */
    mulle_buffer_destroy( buf );

    /* 6️⃣  Use the string */
    printf("%s\n", persistent);

    /* 7️⃣  Free the persistent copy when done */
    mulle_free( persistent );
    return 0;
}

```

Key API references from the source:
- `mulle_buffer_create` → defined in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) at line 258
- `mulle_buffer_add_string` → defined in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) at line 1354
- `mulle_buffer_make_string` → defined in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) at lines 1644-1655
- `mulle_buffer_extract_string` → defined in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) at lines 702-710

## Automated Extraction with mulle_buffer_do_string

For most use cases, the `mulle_buffer_do_string` macro automates buffer creation, string extraction, and cleanup. Defined in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) at lines 640-679, this macro uses a complex `for`-loop construct to manage scope automatically.

The macro expansion creates a temporary `mulle_buffer` on the stack (or via the provided allocator), executes your code block, automatically calls `mulle_buffer_extract_string`, and cleans the temporary buffer when exiting the scope.

```c
#include "mulle-buffer.h"

int main(void)
{
    char *s;

    /* The macro handles creation, extraction and cleanup */
    mulle_buffer_do_string( buf, NULL, s )
    {
        mulle_buffer_add_string( buf, "Persistent " );
        mulle_buffer_add_string( buf, "string created with a macro" );
        break;               /* break out of the block – macro will still extract */
    }

    /* `s` now points to a heap‑allocated, null‑terminated string */
    printf("%s\n", s);
    mulle_free( s );         /* remember to free it */
    return 0;
}

```

The macro definition itself (lines 640-679) implements a state machine using pointer arithmetic to ensure extraction happens exactly once after the block executes, even if you exit early with a `break` statement.

## Custom Allocator Support

When working with non-standard memory management, use `mulle_buffer_do_allocator` (defined in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) at lines 731-764) to inject a custom allocator for both the temporary buffer and the final extracted string.

```c
#include "mulle-buffer.h"
#include "mulle-allocator.h"

int main(void)
{
    struct mulle_allocator *alloc = mulle_allocator_create();
    char *s;

    mulle_buffer_do_allocator( buf, alloc, s )
    {
        mulle_buffer_add_string( buf, "Allocated with custom allocator" );
    }

    printf("%s\n", s);
    mulle_free( s );
    mulle_allocator_destroy( alloc );
    return 0;
}

```

This pattern ensures that `mulle_buffer_extract_string` uses your specified allocator when shrinking the allocation to fit and returning the persistent pointer.

## Summary

- **Persistent strings** require explicit extraction from a `mulle_buffer` using `mulle_buffer_extract_string()`, which internally calls `_mulle__buffer_extract_string` (lines 218-227 in [`src/mulle--buffer.c`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.c)).
- **Null-termination** is guaranteed by `mulle_buffer_make_string()` (backed by lines 196-215 in [`src/mulle--buffer.c`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.c)), though extraction functions typically invoke this automatically.
- **Memory ownership** transfers to the caller upon extraction; you must release the string with `mulle_free()` to avoid leaks.
- **The `mulle_buffer_do_string` macro** (lines 640-679 in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h)) provides a safe, scope-bound alternative that automates cleanup.
- **Custom allocators** are supported via `mulle_buffer_do_allocator` for specialized memory management requirements.

## Frequently Asked Questions

### What is the difference between mulle_buffer_make_string and mulle_buffer_extract_string?

`mulle_buffer_make_string()` ensures the buffer contains a null terminator but keeps the data attached to the buffer structure, whereas `mulle_buffer_extract_string()` first ensures null termination, then shrinks the allocation to fit exactly, detaches it from the buffer, and returns an independent pointer. Only the extracted pointer represents a persistent string that survives buffer destruction.

### Do I need to call mulle_buffer_make_string before mulle_buffer_extract_string?

No, calling `mulle_buffer_make_string()` explicitly is optional. According to the implementation in [`src/mulle--buffer.c`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.c) at lines 218-227, `mulle_buffer_extract_string()` internally invokes the make_string logic to guarantee null termination before performing the extraction and allocation shrink.

### How do I free a persistent string created with mulle-buffer?

Strings returned by `mulle_buffer_extract_string()` are allocated using the buffer's allocator (typically the default allocator). You must release this memory using `mulle_free()` when the string is no longer needed to prevent memory leaks.

### Can I create persistent strings with custom memory allocators?

Yes. Use the `mulle_buffer_do_allocator` macro (lines 731-764 in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h)) to specify a custom `struct mulle_allocator`. The extraction process will use your allocator for both the temporary buffer operations and the final persistent string allocation, ensuring consistent memory management throughout the lifecycle.