# How to Get the String Representation of a mulle_buffer in C

> Learn how to get the string representation of a mulle_buffer in C. Use mulle_buffer_get_string for a view or mulle_buffer_extract_string for a copy.

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

---

**Call `mulle_buffer_get_string()` to obtain a NUL-terminated C string view of the buffer's contents, or use `mulle_buffer_extract_string()` to create an independent heap-allocated copy that survives buffer modification.**

The `mulle_buffer` library from the [mulle-c](https://github.com/mulle-c) ecosystem provides a dynamic, resizable binary data container for C applications. When you need to convert accumulated bytes into a standard C string representation, the library offers specific APIs that handle NUL-termination and memory safety automatically without manual byte manipulation.

## Using mulle_buffer_get_string for Zero-Copy Access

The most efficient way to retrieve a string representation is through the public inline helper defined in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h).

### Function Signature and Location

The implementation at **line 804** of [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) declares:

```c
char *mulle_buffer_get_string( struct mulle_buffer *buffer );

```

This function performs three critical validations before returning data:

1. **Null check** – Returns `NULL` if the buffer pointer is invalid
2. **Write-ability assertion** – Aborts if the buffer lacks write permissions (read-only views are rejected)
3. **Allocator forwarding** – Passes the buffer's allocator to internal routines

### Internal String Termination Logic

The public wrapper delegates to `_mulle__buffer_get_string` located in **src/mulle--buffer.c** (line 132). This internal routine first invokes `_mulle__buffer_make_string` (line 135) to guarantee NUL-termination:

- If the buffer is **flexible**, it grows the allocation by one byte to accommodate the terminator
- If the buffer is **inflexible** (fixed capacity) and already full, it truncates the last data byte to make room for the NUL character
- Handles overflow checks to prevent buffer overruns

### Pointer Validity Warning

The returned `char*` points directly to the buffer's internal storage. **This pointer remains valid only as long as the buffer is not reallocated, resized, or destroyed.** Any subsequent call to `mulle_buffer_add_bytes`, `mulle_buffer_set_length`, or similar mutating operations may invalidate the pointer.

## Extracting an Independent String Copy

When you require a string that persists beyond the buffer's lifetime or need to modify the buffer while retaining the current contents, use the extracting helper.

### mulle_buffer_extract_string

Defined in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) at **line 702**, this function:

- Allocates a new memory block using the buffer's associated allocator
- Copies all current data plus NUL terminator to the new location
- Resets the buffer length to zero (effectively clearing it)
- Returns the allocated copy which the caller must eventually free

This approach eliminates dangling pointer risks but transfers ownership responsibility to your application.

### Comparison of Approaches

**`mulle_buffer_get_string`** – Zero-copy view; fastest option; pointer becomes invalid on buffer modification; leaves buffer contents intact.

**`mulle_buffer_extract_string`** – Allocates independent copy; survives buffer destruction; empties the source buffer; requires explicit memory management.

## Complete Implementation Example

The following example demonstrates both retrieval methods using the actual API surface from [`mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/mulle-buffer.h):

```c
#include "mulle-buffer.h"
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    /* Allocate a flexible buffer with the default allocator */
    struct mulle_buffer *buf = mulle_buffer_create_default();

    /* Fill it with text (src/mulle-buffer.h line 862) */
    mulle_buffer_add_string(buf, "Hello, ");
    mulle_buffer_add_string(buf, "mulle-buffer!");

    /* -------------------------------------------------
     *  Method 1: Get NUL-terminated view (no copy)
     * ------------------------------------------------- */
    char *view = mulle_buffer_get_string(buf);
    if (view)
        printf("Zero-copy view: %s\n", view);

    /* -------------------------------------------------
     *  Method 2: Extract owned copy (buffer becomes empty)
     * ------------------------------------------------- */
    char *owned = mulle_buffer_extract_string(buf);
    if (owned) {
        printf("Extracted copy: %s\n", owned);
        printf("Buffer length after extract: %zu\n", 
               mulle_buffer_get_length(buf)); /* Outputs 0 */
        
        /* Must free using the same allocator (default here) */
        free(owned);
    }

    /* Buffer is empty but reusable */
    mulle_buffer_add_string(buf, "New data");
    printf("Reused buffer: %s\n", mulle_buffer_get_string(buf));

    mulle_buffer_destroy(buf);
    return 0;
}

```

## API Selection Guide

Choose the appropriate function based on your memory management requirements:

- **Read-only access needed** – Use `mulle_buffer_get_string` for immediate consumption (e.g., passing to `printf` or string comparison functions) without allocation overhead.

- **Long-term storage required** – Use `mulle_buffer_extract_string` when storing the result in a struct field or returning it from a function after the buffer goes out of scope.

- **Binary data inspection** – For non-string binary content, use `mulle_buffer_get_bytes` or `mulle_buffer_extract_data` which return `void*` or `mulle_data` structures instead of `char*`.

- **Read-only buffer views** – If you created the buffer with read-only constraints, `mulle_buffer_get_string` will assert; instead, access raw bytes via `mulle_buffer_get_bytes` and manually ensure NUL-termination in your application logic.

## Summary

- **`mulle_buffer_get_string`** provides a zero-copy, NUL-terminated view of internal storage, but asserts the buffer is writable and invalidates on resize
- **String termination** automatically handles edge cases, including truncating the final byte in inflexible full buffers to accommodate the NUL terminator
- **`mulle_buffer_extract_string`** creates an allocator-aware heap copy at line 702 of [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h), clearing the source buffer but ensuring pointer stability
- **Source files** [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) (public API) and [`src/mulle--buffer.c`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.c) (internal logic at lines 132-135) implement these mechanisms

## Frequently Asked Questions

### Does mulle_buffer_get_string return a copy of the data?

No, it returns a pointer to the internal byte array. According to the implementation in [`src/mulle--buffer.c`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.c) line 132, the function simply ensures NUL-termination and returns the internal storage address. If you need a copy, use `mulle_buffer_extract_string` or manually duplicate the result with `strdup`.

### What happens if the buffer is completely full when calling mulle_buffer_get_string?

The internal routine `_mulle__buffer_make_string` at line 135 of [`src/mulle--buffer.c`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.c) handles this edge case by truncating the last byte of data to make room for the NUL terminator in inflexible buffers. Flexible buffers grow automatically to accommodate the additional byte.

### Why does mulle_buffer_get_string require a writable buffer?

The function asserts write-ability because string termination may require modifying the buffer contents—either by adding a NUL byte or truncating the last character in full buffers. Read-only buffer views must use `mulle_buffer_get_bytes` and manage string conversion externally.

### How do I preserve the string after destroying the buffer?

You must extract an independent copy before destruction using `mulle_buffer_extract_string`, which allocates new memory using the buffer's allocator and returns ownership to you. Alternatively, call `mulle_buffer_get_string` followed by `mulle_allocator_strdup` on the result, then destroy the buffer separately.