From d84e7bf0679e3d1e3bba7871cd7c8de46e306fb8 Mon Sep 17 00:00:00 2001 From: Cristian Gora Date: Wed, 8 Apr 2026 20:59:58 +0200 Subject: [PATCH] v1.2.0 Release --- arena.h | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- test.c | 3 ++ 2 files changed, 142 insertions(+), 3 deletions(-) diff --git a/arena.h b/arena.h index b1d7e63..908a27f 100644 --- a/arena.h +++ b/arena.h @@ -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 bytes into the arena and copies -// the contents of . +// the contents of . 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 +#include #include #include +#include #include #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 diff --git a/test.c b/test.c index ae69aea..b2c0f1f 100644 --- a/test.c +++ b/test.c @@ -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; }