# mulle_buffer vs mulle_flushablebuffer: Key Differences in the mulle-buffer Library

> Discover the key differences between mulle_buffer and mulle_flushablebuffer. Learn how mulle_buffer handles flexible data and mulle_flushablebuffer manages fixed-size stream data with automatic flushing.

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

---

**mulle_buffer is a flexible, growable memory container for general-purpose data construction, while mulle_flushablebuffer is a fixed-size, stream-oriented extension that automatically flushes contents to an external sink via a callback function when the buffer fills.**

The **mulle-buffer** library provides two primary data structures for memory management in C: `mulle_buffer` and `mulle_flushablebuffer`. While both serve as containers for binary or text data, they target fundamentally different use cases and exhibit distinct architectural behaviors. Understanding the difference between mulle_buffer and mulle_flushablebuffer is essential for selecting the appropriate tool for string construction versus stream-oriented I/O operations.

## Core Architectural Differences

### Structure and Inheritance

In [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) (lines 70-77), `struct mulle_buffer` is defined as a thin wrapper around the internal `mulle__buffer` structure plus an allocator field. This design provides the foundation for dynamic memory management operations.

In contrast, `struct mulle_flushablebuffer` defined in [`src/mulle-flushablebuffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-flushablebuffer.h) (lines 73-77) embeds the entire `mulle_buffer` layout via the `MULLE_BUFFER_BASE` macro. It extends the base structure with three additional fields: a **flusher callback**, a **user-data pointer**, and a **flushed-byte counter**.

### Memory Growth Models

**mulle_buffer** operates as a **flexible** container. It can grow or shrink on demand through functions like `mulle_buffer_grow()` and `mulle_buffer_set_length()`, automatically reallocating memory as needed to accommodate incoming data.

**mulle_flushablebuffer** is **inflexible** by design. The underlying storage has a fixed size established at creation. When the buffer reaches capacity, it does not reallocate; instead, it invokes the user-supplied flusher function to drain contents to an external sink.

## Functional Characteristics

### API Flags and Access Modes

`mulle_buffer` supports multiple operational modes via bitwise flags: `MULLE_BUFFER_IS_FLEXIBLE` (default behavior), optionally combined with `MULLE_BUFFER_IS_READONLY`, `MULLE_BUFFER_IS_WRITEONLY`, or `MULLE_BUFFER_IS_TEXT`.

`mulle_flushablebuffer` initializes with a constrained flag set defined by the `MULLE_FLUSHABLEBUFFER_TYPE` macro: `MULLE_BUFFER_IS_INFLEXIBLE | MULLE_BUFFER_IS_FLUSHABLE | MULLE_BUFFER_IS_WRITEONLY`. This enforces write-only, stream-oriented semantics suitable for output streams like files or sockets.

### Data Retrieval vs. Automatic Flushing

With `mulle_buffer`, data retrieval happens explicitly through extraction functions like `mulle_buffer_extract_data()` or `mulle_buffer_get_string()`. The caller manually manages when and how to access the buffered contents.

`mulle_flushablebuffer` automates output through its **flushing mechanism**. The flusher callback conforms to the signature `size_t (*)(void *buf, size_t one, size_t len, void *userinfo)`. Explicit flushing occurs via `mulle_flushablebuffer_flush()`, while `mulle_flushablebuffer_done()` performs an implicit flush during destruction. This design eliminates the need to hold large datasets in memory before writing.

## Practical Usage Examples

### Building Strings with mulle_buffer

Use `mulle_buffer` when assembling C-strings or binary blobs that require dynamic resizing. The `mulle_buffer_create_default()` function initializes the buffer with the default allocator.

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

int main(void)
{
    struct mulle_buffer *buf = mulle_buffer_create_default();
    mulle_buffer_add_string(buf, "Hello, ");
    mulle_buffer_add_string(buf, "world!");
    printf("%s\n", mulle_buffer_get_string(buf));
    mulle_buffer_destroy(buf);
    return 0;
}

```

The implementation of `mulle_buffer_add_string()` resides in [`src/mulle-buffer.c`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.c), handling automatic growth when the internal storage fills.

### Streaming to Files with mulle_flushablebuffer

For stream-oriented output, use the `mulle_flushablebuffer_do_FILE` macro defined in [`src/mulle-flushablebuffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-flushablebuffer.h) (lines 104-124). This creates a stack-allocated buffer that flushes to a `FILE*` when full.

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

int main(void)
{
    mulle_flushablebuffer_do_FILE(buf, stdout)
    {
        struct mulle_buffer *b = buf;
        mulle_buffer_add_string(b, "Line 1\n");
        mulle_buffer_add_string(b, "Line 2\n");
    }
    return 0;
}

```

When the scope ends, the macro invokes `mulle_flushablebuffer_done()`, which flushes remaining data to `stdout` via `fwrite`.

### Implementing Custom Flushers

Define a custom flusher to direct output to arbitrary sinks such as network sockets. The creation function `mulle_flushablebuffer_create()` (declared at lines 80-86 in [`src/mulle-flushablebuffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-flushablebuffer.h)) accepts your callback and userinfo pointer.

```c
#include "mulle-flushablebuffer.h"
#include <sys/socket.h>

static size_t socket_flusher(void *buf, size_t one, size_t len, void *userinfo)
{
    int fd = *(int *)userinfo;
    return send(fd, buf, len, 0);
}

int main(void)
{
    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
    struct mulle_flushablebuffer *fb =
        mulle_flushablebuffer_create(1024, socket_flusher, &sock, NULL);

    struct mulle_buffer *b = mulle_flushablebuffer_as_buffer(fb);
    mulle_buffer_add_string(b, "Hello socket!\n");
    
    mulle_flushablebuffer_done(fb);
    close(sock);
    return 0;
}

```

The conversion macro `mulle_flushablebuffer_as_buffer()` (lines 43-48) allows treating the flushable buffer as a standard `mulle_buffer` for writing operations.

## Summary

- **mulle_buffer** provides flexible, growable storage for general-purpose data construction with manual extraction.
- **mulle_flushablebuffer** provides fixed-size, write-only storage that automatically flushes to external sinks via callbacks.
- **mulle_flushablebuffer** inherits from **mulle_buffer** using `MULLE_BUFFER_BASE`, extending it with flusher machinery rather than duplicating functionality.
- Use **mulle_buffer** for string building and data assembly; use **mulle_flushablebuffer** for streaming I/O to files, sockets, or custom output devices.

## Frequently Asked Questions

### Can I read from a mulle_flushablebuffer like I do with mulle_buffer?

No. `mulle_flushablebuffer` is strictly **write-only** (`MULLE_BUFFER_IS_WRITEONLY`). It is designed exclusively for output streams. If you need to read data back, use `mulle_buffer` which supports read-only, write-only, or read/write modes.

### How do I retrieve data from a mulle_flushablebuffer after writing?

You do not extract data directly. Instead, the buffer pushes content to your **flusher callback** automatically when full or explicitly via `mulle_flushablebuffer_flush()`. For the final remaining bytes, call `mulle_flushablebuffer_done()` to trigger the last flush operation.

### Can I convert a mulle_buffer into a mulle_flushablebuffer?

No safe conversion exists from `mulle_buffer` to `mulle_flushablebuffer`. However, you can safely cast a `mulle_flushablebuffer` pointer to a `mulle_buffer` pointer using `mulle_flushablebuffer_as_buffer()` (defined in [`src/mulle-flushablebuffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-flushablebuffer.h)). This allows reuse of the standard buffer API for write operations while maintaining the flushable behavior.

### Which type should I use for logging large amounts of data?

Use **mulle_flushablebuffer** for large-scale logging. Its fixed-size design prevents memory exhaustion by writing chunks to disk or stdout as the buffer fills, whereas `mulle_buffer` would continue allocating memory until the system runs out of RAM or the process terminates.