# Understanding Buffer Modes in mulle-buffer: Core Types, Access Controls, and Seek Behavior

> Explore mulle-buffer modes: flexible, read-only, and flushable cover memory, I/O, and navigation for C applications. Optimize your buffer management.

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

---

**mulle-buffer provides three orthogonal buffer mode families—core type modes (flexible, inflexible, flushable), access control modes (read-only, write-only, text/binary), and seek positioning modes—that determine memory allocation strategy, I/O permissions, and navigation behavior in C applications.**

The mulle-c/mulle-buffer library implements a dynamic memory buffer system where **buffer modes in mulle-buffer** control every aspect of data storage and retrieval. These modes are defined across multiple header files and stored in a compact bitfield format, allowing developers to configure fixed-size stack buffers, growable heap storage, or flushable I/O sinks with precise access controls.

## Core Buffer Type Modes

The fundamental buffer behavior is determined by the core type enumeration defined in [`src/mulle--buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.h). These values occupy the low two bits of the buffer's internal `_type` field and control memory allocation flexibility.

### Flexible Mode (MULLE_BUFFER_IS_FLEXIBLE)

**Flexible mode** (value `0`) represents the default behavior where the buffer dynamically allocates and reallocates memory as needed to accommodate writes. When using `MULLE_BUFFER_DATA(NULL)`, you create a flexible buffer that grows automatically with the default allocator. This mode is ideal for general-purpose string building or data accumulation where the final size is unknown.

### Inflexible Mode (MULLE_BUFFER_IS_INFLEXIBLE)

**Inflexible mode** (value `1`) creates a fixed-size buffer backed by pre-allocated memory, typically stack-allocated arrays. Declared using `MULLE_BUFFER_INFLEXIBLE_DATA(stack, sizeof stack)`, this mode will trigger assertions if write operations exceed the allocated capacity. Use this for performance-critical scenarios where dynamic allocation must be avoided.

### Flushable Mode (MULLE_BUFFER_IS_FLUSHABLE)

**Flushable mode** (value `2`) enables buffers that can write their contents to an underlying sink through the flush operation. Defined in [`src/mulle-flushablebuffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-flushablebuffer.h), this mode is used with `struct mulle_flushablebuffer` to implement streaming I/O where accumulated data periodically empties into files or network sockets rather than remaining in memory indefinitely.

### Sprintf Inflexible Mode (MULLE_BUFFER_IS_SPRINTF_INFLEXIBLE)

**Sprintf inflexible mode** (value `3`) provides a specialized internal buffer type used specifically for sprintf operations within the library, offering optimized behavior for formatted string output into fixed-size targets.

## Access Control and Data Kind Modes

Beyond core types, [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) defines higher-order bit flags (stored in bits 6-8 of the `_type` field) that restrict access patterns and specify data interpretation for stdio helpers.

### Read-Only and Write-Only Modes

The library enforces access restrictions through bitwise flags:

- **`MULLE_BUFFER_IS_READONLY`** (`0x40`): Marks the buffer as read-only, causing assertions on any write attempt
- **`MULLE_BUFFER_IS_WRITEONLY`** (`0x80`): Marks the buffer as write-only, causing assertions on any read attempt

Use the inline helpers `mulle_buffer_set_readonly()` and `mulle_buffer_set_writeonly()` to apply these restrictions after initialization. The helper `mulle_buffer_is_readonly()` tests these bits before allowing write operations.

### Text vs Binary Data Interpretation

The **`MULLE_BUFFER_IS_TEXT`** (`0x100`) and **`MULLE_BUFFER_IS_BINARY`** (`0x0`) flags control how the stdio integration layer handles line endings and data transformation. Text mode enables newline conversion behaviors, while binary mode (the default) preserves exact byte sequences.

## Seek Modes for Buffer Positioning

Buffer navigation uses standard seek constants defined in [`src/mulle--buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.h), mirroring the classic C stdio seek behavior:

- **`MULLE_BUFFER_SEEK_SET`** (`0`): Position relative to the start of the buffer
- **`MULLE_BUFFER_SEEK_CUR`** (`1`): Position relative to the current offset
- **`MULLE_BUFFER_SEEK_END`** (`2`): Position relative to the end of the buffer

The functions `mulle_buffer_set_seek()` and `mulle_buffer_get_seek()` manipulate the cursor position using these constants, while `mulle_buffer_advance()` moves the cursor forward by a specified number of bytes.

## How Buffer Modes Are Stored Internally

All mode flags coexist in the buffer structure's **`_type`** field, a 32-bit unsigned integer. The bit layout follows this architecture:

- **Bits 0-1**: Core buffer type (flexible, inflexible, flushable, sprintf-inflexible)
- **Bits 6-8**: Access and data kind flags (read-only, write-only, text/binary)
- **Remaining bits**: Reserved for future expansion

This compact representation allows the library to test multiple mode properties simultaneously using bitwise operations, minimizing overhead during high-performance buffer operations.

## Practical Examples of Buffer Mode Combinations

The following examples demonstrate how to initialize and configure different **buffer modes in mulle-buffer**:

```c
/* 1. Flexible, read-write buffer (default) */
struct mulle_buffer buf = MULLE_BUFFER_DATA(NULL);
mulle_buffer_write(&buf, "hello", 5);

/* 2. Inflexible (fixed-size) buffer on the stack */
unsigned char stack[64];
struct mulle_buffer fixed = MULLE_BUFFER_INFLEXIBLE_DATA(stack, sizeof(stack));
mulle_buffer_write(&fixed, "fixed", 5);   // asserts if overflow occurs

/* 3. Mark a buffer as read-only – further writes will assert */
mulle_buffer_set_readonly(&buf);

/* 4. Seek within a buffer */
mulle_buffer_set_seek(&buf, 2, MULLE_BUFFER_SEEK_SET);
char *ptr = mulle_buffer_advance(&buf, 1);

/* 5. Flushable, write-only buffer for streaming output */
struct mulle_flushablebuffer fbuf;
mulle_flushablebuffer_init(&fbuf, NULL);
mulle_buffer_set_writeonly(&fbuf.buffer);
mulle_buffer_write(&fbuf.buffer, "data", 4);
mulle_flushablebuffer_flush(&fbuf);

```

## Summary

- **mulle-buffer** implements three orthogonal mode families defined across [`src/mulle--buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.h) and [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h)
- **Core types** control memory allocation: `MULLE_BUFFER_IS_FLEXIBLE` (0) for dynamic growth, `MULLE_BUFFER_IS_INFLEXIBLE` (1) for fixed-size, `MULLE_BUFFER_IS_FLUSHABLE` (2) for streamable sinks
- **Access flags** enforce I/O restrictions: `MULLE_BUFFER_IS_READONLY` (0x40) and `MULLE_BUFFER_IS_WRITEONLY` (0x80) prevent unauthorized operations
- **Data kind flags** specify interpretation: `MULLE_BUFFER_IS_TEXT` (0x100) versus binary mode (0x0) for stdio handling
- **Seek modes** provide navigation: `MULLE_BUFFER_SEEK_SET` (0), `MULLE_BUFFER_SEEK_CUR` (1), and `MULLE_BUFFER_SEEK_END` (2)
- All modes compress into the `_type` field using specific bitmasks for efficient runtime testing

## Frequently Asked Questions

### What is the difference between flexible and inflexible buffer modes in mulle-buffer?

**Flexible mode** (`MULLE_BUFFER_IS_FLEXIBLE`) allows the buffer to dynamically allocate and reallocate memory as data grows, suitable for unknown or variable data sizes. **Inflexible mode** (`MULLE_BUFFER_IS_INFLEXIBLE`) operates on fixed pre-allocated storage (typically stack memory) and triggers assertions when writes exceed capacity, offering predictable memory usage for safety-critical applications.

### How do I make a mulle-buffer read-only?

Call `mulle_buffer_set_readonly()` on an initialized buffer to set the `MULLE_BUFFER_IS_READONLY` flag (0x40). According to the implementation in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h), subsequent write attempts will assert or fail silently depending on build configuration, protecting the buffer content from modification while allowing reads to proceed normally.

### What does the flushable buffer mode do?

**Flushable mode** (`MULLE_BUFFER_IS_FLUSHABLE`) enables buffers to transfer accumulated data to an underlying sink (such as a file descriptor or network socket) rather than retaining it in memory. As implemented in [`src/mulle-flushablebuffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-flushablebuffer.h), calling `mulle_flushablebuffer_flush()` writes the current contents to the configured destination and resets the buffer cursor, making this mode ideal for logging systems or streaming data processors.

### How are buffer modes stored internally in mulle-buffer?

All modes are stored in the **`_type`** field of the `struct mulle_buffer` as a 32-bit integer bitmask. The core buffer type occupies bits 0-1 (values 0-3), while access control and data kind flags occupy higher-order bits (e.g., bit 6 for read-only, bit 7 for write-only, bit 8 for text mode). Inline helper functions like `mulle_buffer_is_readonly()` perform bitwise AND operations against these masks to test current modes efficiently.