# mulle_buffer_guarantee: Ensuring Writable Space in Dynamic Buffers

> Discover mulle_buffer_guarantee for ensuring writable space in dynamic buffers. This utility guarantees free capacity for writing, returning a pointer to the space or NULL if expansion fails.

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

---

**`mulle_buffer_guarantee` is a core utility in the mulle-buffer library that ensures a buffer has at least a specified amount of free capacity available for writing, returning a pointer to the guaranteed space or NULL if the buffer cannot be expanded.**

The **mulle_buffer_guarantee** function serves as a foundational memory management primitive in the [mulle-c/mulle-buffer](https://github.com/mulle-c/mulle-buffer) repository. It abstracts the complexities of dynamic buffer growth, allowing developers to safely reserve contiguous writable memory without manual capacity checks or reallocation logic.

## Core Functionality and Purpose

The primary role of `mulle_buffer_guarantee` is to validate and ensure that a `struct mulle_buffer` maintains sufficient unused capacity for an impending write operation. When invoked, the function inspects the buffer's current state to determine if at least `length` bytes of free space are available.

If the existing capacity is inadequate, the function automatically triggers a growth operation using the buffer's configured allocator. Upon successful expansion, it returns a direct pointer to the beginning of the newly guaranteed writable region. This pointer serves as a safe destination for raw memory operations, string copies, or file read operations. If the buffer is marked as overflown or is inflexible and cannot grow, the function returns `NULL`, providing a clear failure signal that prevents buffer overflows.

## Internal Implementation Details

The guarantee mechanism operates through a layered architecture separating public interface convenience from internal growth logic.

### Public Inline Wrapper (src/mulle-buffer.h)

The public-facing `mulle_buffer_guarantee` is implemented as an inline function in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h). This wrapper performs preliminary validation, asserting that the provided buffer pointer is valid and that the buffer is writable. It then forwards the request to the internal implementation `_mulle__buffer_guarantee`, passing the buffer structure and the requested length.

### Core Growth Logic (src/mulle--buffer.c)

The internal function `_mulle__buffer_guarantee`, defined in [`src/mulle--buffer.c`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.c), executes the capacity check and potential reallocation. The routine first inspects the buffer's overflow flag; if set, it immediately returns `NULL` to indicate an unrecoverable state.

For valid buffers, it calculates the deficit by computing `missing = &buffer->_curr[length] - buffer->_sentinel`. If this value is positive, indicating insufficient space, the function invokes `_mulle__buffer_grow` to expand the storage. Following a successful growth operation—or if no growth was required—the function returns `buffer->_curr`, the cursor pointing to the start of the guaranteed writable area.

## Practical Usage Patterns

The utility excels in scenarios requiring direct memory manipulation where standard append operations are insufficient or inefficient.

### Reading Files Into a Buffer

When streaming data from a file system, `mulle_buffer_guarantee` provides a safe destination pointer for `fread` operations without pre-allocating excessive memory.

```c
struct mulle_data read_file( FILE *fp)
{
   struct mulle_buffer   buffer;
   struct mulle_data     data;
   void                 *ptr;
   size_t                length, size;

   mulle_buffer_init( &buffer, NULL);
   while( !feof( fp))
   {
      /* Ensure at least 0x1000 bytes are available */
      ptr = mulle_buffer_guarantee( &buffer, 0x1000);
      assert( ptr);                     // never NULL for a growable buffer
      size = mulle_buffer_guaranteed_size( &buffer);
      length = fread( ptr, 1, size, fp);
      mulle_buffer_advance( &buffer, length);
   }
   mulle_buffer_shrink_to_fit( &buffer);
   data = mulle_buffer_extract_data( &buffer);
   mulle_buffer_done( &buffer);
   return data;
}

```

The guarantee call supplies a writable region that can safely receive `fread` output, with `mulle_buffer_advance` updating the cursor by the actual bytes read.

### Building Dynamic Strings

For string construction involving multiple append operations, guaranteeing sufficient space upfront eliminates intermediate reallocations.

```c
void make_string( void)
{
   struct mulle_buffer   buffer;
   char                 *s;

   mulle_buffer_init( &buffer, NULL);
   /* Reserve space for up to 256 bytes */
   if( mulle_buffer_guarantee( &buffer, 256))
   {
      mulle_buffer_add_string( &buffer, "Hello, ");
      mulle_buffer_add_string( &buffer, "world!");
   }
   /* Extract a NUL‑terminated C string */
   s = mulle_buffer_extract_string( &buffer);
   printf("%s\n", s);
   mulle_free( s);
   mulle_buffer_done( &buffer);
}

```

This pattern ensures the buffer can accommodate the concatenated strings without repeated growth operations.

### Direct Binary Data Writing

For structured binary data, the function provides a raw pointer suitable for `memcpy` operations.

```c
struct my_hdr { uint32_t id; uint16_t flags; };

void write_header( struct mulle_buffer *buf, struct my_hdr const *hdr)
{
   void *dst = mulle_buffer_guarantee( buf, sizeof *hdr);
   if( dst)
   {
      memcpy( dst, hdr, sizeof *hdr);
      mulle_buffer_advance( buf, sizeof *hdr);
   }
}

```

Here, the caller obtains a raw pointer, copies a structure directly, and then advances the buffer cursor by the structure size.

## Summary

- **`mulle_buffer_guarantee`** ensures a `struct mulle_buffer` has at least `length` bytes of free capacity, automatically growing the buffer if necessary.
- The function returns a pointer to the writable area (`buffer->_curr`) on success, or `NULL` if the buffer is overflown or inflexible.
- Implemented in [`src/mulle-buffer.h`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle-buffer.h) (public wrapper) and [`src/mulle--buffer.c`](https://github.com/mulle-c/mulle-buffer/blob/main/src/mulle--buffer.c) (internal logic `_mulle__buffer_guarantee`), it forms the backbone of safe buffer operations.
- It enables efficient file reading, string building, and binary serialization by providing direct memory access without manual capacity management.
- Always pair with **`mulle_buffer_advance`** after writing to update the cursor position, or use **`mulle_buffer_guaranteed_size`** to determine the actual available space.

## Frequently Asked Questions

### What happens if mulle_buffer_guarantee cannot allocate enough memory?

If the buffer lacks sufficient space and cannot grow—either because it is inflexible or the allocator fails—the function returns `NULL`. For growable buffers initialized with `NULL` allocators, this typically indicates memory exhaustion. The caller must check for this return value to prevent null pointer dereferences during subsequent write operations.

### How does mulle_buffer_guarantee differ from manually calling realloc?

Unlike manual `realloc`, `mulle_buffer_guarantee` integrates with the buffer's internal state management, preserving the cursor (`_curr`) and sentinel (`_sentinel`) relationships automatically. It calculates the exact growth required based on the current cursor position and requested length, ensuring the returned pointer aligns with the buffer's logical write position. This abstraction prevents common errors such as pointer invalidation and offset miscalculations.

### When should mulle_buffer_advance be used with guarantee?

You must call **`mulle_buffer_advance`** immediately after writing data to the pointer returned by `mulle_buffer_guarantee`. The guarantee function only reserves space; it does not update the cursor. Advancement moves `buffer->_curr` forward by the number of bytes actually written, maintaining the buffer's internal consistency for subsequent operations.

### Is the memory returned by mulle_buffer_guarantee always contiguous?

Yes, the function guarantees a single contiguous block of at least `length` bytes. This is essential for operations like `fread` or `memcpy` that require linear memory addresses. The contiguous guarantee holds because the buffer growth logic in `_mulle__buffer_grow` ensures reallocated memory remains linear, or the function returns `NULL` if such a block cannot be provided.