Getting started
This page takes you from an empty source file to a working program, then shows how to build the library into your own project.
Adding the library to your build
dynostatic-buffer is a single translation unit
(library/dynostatic-buffer.c) plus one public header
(library/dynostatic-buffer.h). You can consume it with any of the three
build systems the project ships with.
Important
The configuration macros (DS_BUFFER_MEMORY_SIZE,
DS_MAX_ALLOCATION_COUNT, DS_ALIGNMENT, …) change
sizeof(dynostatic_buffer_t). Every translation unit that includes the
header — the library and all of your own code — must be compiled with the
same values, or the layout will disagree and you will get silent memory
corruption. Set them once, in a place every consumer sees.
CMake (primary)
cmake --preset release
cmake --build --preset release
Make
The top-level Makefile builds a static and shared library. The build
settings also live in a relocatable fragment,
library/dynostatic-buffer.mk, that another project can include to reuse
the exact compile flags:
include third_party/dynostatic-buffer/library/dynostatic-buffer.mk
app.o: app.c
$(CC) $(DYNOSTATIC_BUFFER_CPPFLAGS) -c $< -o $@
app: app.o $(DYNOSTATIC_BUFFER_A)
$(CC) $^ -o $@
Everything is namespaced with the DYNOSTATIC_BUFFER_ / DS_ prefixes to
avoid clashing with the host project.
Bazel
bazel build //...
bazel test //...
bazel run //examples:basic_example
See the top-level README for the full build matrix.
The lifecycle
Every instance follows the same four-phase lifecycle:
Zero-initialize the structure. Static storage duration does this for free and is the recommended placement.
Initialize it once with
ds_initialize_allocation().Use the allocation and query API as much as you like.
Deinitialize with
ds_deinit_allocation(), which zeroes all memory and bookkeeping. The instance can be initialized again afterwards.
Warning
Initialization is detected via an in-band magic marker, so the structure
must be zeroed before the first ds_initialize_allocation().
A stack instance full of garbage may spuriously read as “already
initialized”. Static storage duration satisfies this automatically; for any
other placement, memset it to zero first.
A complete first program
The example below is the minimal lifecycle with real error handling. Each API
call returns a ds_err_code_t; ERROR_DS_OK (zero) means
success. See Error codes for the full list and Usage guide for the
recommended handling pattern.
#include <stdio.h>
#include <stdlib.h>
#include "dynostatic-buffer.h"
int main(void)
{
/* Static storage: zeroed for free (required before first init) and
* keeps the sizeable arena off the stack. */
static dynostatic_buffer_t ds_buffer;
if (ds_initialize_allocation(&ds_buffer) != ERROR_DS_OK) {
return EXIT_FAILURE;
}
/* The pointer MUST be NULL going in: ds_malloc refuses to overwrite a
* pointer that already refers to a live block. */
int *numbers = NULL;
if (ds_malloc(&ds_buffer, (void **)&numbers, 4 * sizeof(int)) != ERROR_DS_OK) {
ds_deinit_allocation(&ds_buffer);
return EXIT_FAILURE;
}
for (int i = 0; i < 4; ++i) {
numbers[i] = (i + 1) * 10;
}
printf("Allocated 4 ints: %d %d %d %d\n",
numbers[0], numbers[1], numbers[2], numbers[3]);
/* On success ds_free sets the pointer back to NULL, so the same
* variable is immediately safe to reuse. */
ds_free(&ds_buffer, (void **)&numbers);
ds_deinit_allocation(&ds_buffer);
return EXIT_SUCCESS;
}
Running the bundled examples
The examples/ directory contains four small programs, each focused on one
facet of the API. After a CMake build they land next to the library:
./build/release/basic_example
./build/release/array_calloc_realloc
./build/release/buffer_introspection
./build/release/safe_memory_ops
They are the same programs the Usage guide guide walks through.