v1.2.0 Release

This commit is contained in:
2026-04-08 20:59:58 +02:00
parent 8ae06f5a95
commit d84e7bf067
2 changed files with 142 additions and 3 deletions

142
arena.h
View File

@@ -1,4 +1,4 @@
// arena.h - v1.1.3 - MIT License
// arena.h - v1.2.0 - MIT License
// single header library for region-based memory management.
//
// [License and changelog]
@@ -51,6 +51,9 @@
//
// [Function documentation]
//
// Note: every function that starts with "arena__" is considered a private
// function of this library.
//
// In this library, arenas are implemented as linked lists of regions. Each
// region will contain the allocated buffers. The following are all of the
// functions
@@ -83,13 +86,29 @@
// void *arena_copy(Arena *a, const void *src, size_t size);
//
// This function allocates a buffer of <size> bytes into the arena and copies
// the contents of <src>.
// the contents of <src>. Returns the pointer of the new copy.
//
// char *arena_strdup(Arena *a, const char *s);
//
// This functions acts like the strdup() function but it instead allocates on
// the specified arena. It duplicates a string in the arena.
//
// char *arena_sprintf(Arena *a, const char *fmt, ...)
//
// This function internally uses vsnprintf() to store the resulting formatted
// string in the specified arena. Returns NULL on error.
//
// Arena_Marker arena_snapshot(const Arena *a)
//
// This function creates a snapshot of the current state of the arena and
// returns it. This is useful when you only need to allocate memory on the arena
// temporarily. To restore the returned state, see arena_rewind().
//
// void arena_rewind(Arena *a, Arena_Marker marker)
//
// This function restores the arena to the specified state, invalidating all
// previously allocated buffers since the creation of the snapshot.
//
// [Notes]
//
// This library is not Thread-safe.
@@ -117,8 +136,10 @@ int main(void)
#define ARENA_H_
#include <stdalign.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifndef ARENA_REGION_CAPACITY
@@ -161,6 +182,11 @@ struct Arena_Region {
alignas(ARENA_ALIGNMENT) uint8_t data[];
};
typedef struct {
Arena_Region *region;
size_t count;
} Arena_Marker;
typedef struct Arena {
Arena_Region *head;
Arena_Region *tail;
@@ -176,6 +202,14 @@ void arena_reset(Arena *a);
void *arena_alloc(Arena *a, size_t size);
void *arena_copy(Arena *a, const void *src, size_t size);
char *arena_strdup(Arena *a, const char *s);
char *arena_sprintf(Arena *a, const char *fmt, ...);
// Snapshot management
Arena_Marker arena_snapshot(const Arena *a);
void arena_rewind(Arena *a, Arena_Marker marker);
// Private functions
size_t arena__align(size_t n);
#ifdef __cplusplus
}
@@ -236,7 +270,7 @@ void *arena_alloc(Arena *a, size_t size)
}
// Ensure memory alignment of requested size
size = (size + (ARENA_ALIGNMENT - 1)) & ~(ARENA_ALIGNMENT - 1);
size = arena__align(size);
// Check for overflow
if (size > SIZE_MAX - sizeof(Arena_Region)) {
@@ -326,6 +360,106 @@ char *arena_strdup(Arena *a, const char *s)
return (char*)arena_copy(a, s, strlen(s) + 1);
}
char *arena_sprintf(Arena *a, const char *fmt, ...)
{
if (a == NULL || fmt == NULL) {
ARENA_ASSERT(false && "Invalid parameters");
return NULL;
}
va_list args;
va_start(args, fmt);
Arena_Region *r = a->tail;
char *result = NULL;
size_t available = 0;
if (r != NULL) {
available = r->capacity - r->count;
if (available > 0) {
result = (char*)r->data + r->count;
}
}
// Check if result fits inside tail's region available space
va_list args_copy;
va_copy(args_copy, args);
int n = vsnprintf(result, available, fmt, args_copy);
va_end(args_copy);
if (n < 0) {
va_end(args);
return NULL;
}
size_t needed = (size_t)n + 1;
// If it fits commit and return
if (needed <= available) {
r->count += arena__align(needed);
va_end(args);
return result;
}
// Fallback: if it didn't fit, allocate and printf
result = (char*)arena_alloc(a, needed);
if (result == NULL) {
va_end(args);
return NULL;
}
n = vsnprintf(result, needed, fmt, args);
va_end(args);
if (n < 0) {
return NULL;
}
return result;
}
Arena_Marker arena_snapshot(const Arena *a)
{
Arena_Marker marker = {0};
if (a == NULL) {
ARENA_ASSERT(false && "Invalid parameters");
return marker;
}
marker.region = a->tail;
marker.count = (a->tail != NULL ? a->tail->count : 0);
return marker;
}
void arena_rewind(Arena *a, Arena_Marker marker)
{
if (a == NULL) {
ARENA_ASSERT(false && "Invalid parameters");
return;
}
// If arena was empty reset it
if (marker.region == NULL) {
arena_reset(a);
return;
}
a->tail = marker.region;
a->tail->count = marker.count;
// Clear new regions in case they were appended
for (Arena_Region *cur = a->tail->next; cur != NULL; cur = cur->next) {
cur->count = 0;
}
}
size_t arena__align(size_t n)
{
return (n + (ARENA_ALIGNMENT - 1)) & ~(ARENA_ALIGNMENT - 1);
}
#ifdef __cplusplus
}
#endif // __cplusplus
@@ -335,6 +469,8 @@ char *arena_strdup(Arena *a, const char *s)
/*
* Revision history:
*
* 1.2.0 (2026-04-08) New helper functions: arena_sprintf(),
* arena_snapshot() and arena_rewind()
* 1.1.3 (2026-01-14) Align Arena_Region data flexible array member
* 1.1.2 (2026-01-10) Minor changes; check result of alloc in arena_copy()
* 1.1.1 (2025-11-18) Implement memory alignment; improve docs

3
test.c
View File

@@ -49,6 +49,9 @@ int main(void)
arena_print(a);
char *str = arena_sprintf(&a, "Formatted string %x", 0xCAFE);
printf("%s\n", str);
arena_free(&a);
return 0;
}