v1.2.4 - Major rewrite
This commit is contained in:
16
.clangd
Normal file
16
.clangd
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
CompileFlags:
|
||||||
|
Add:
|
||||||
|
- "-Wall"
|
||||||
|
- "-Wextra"
|
||||||
|
- "-pedantic"
|
||||||
|
- "-std=c11"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
If:
|
||||||
|
PathMatch: arena\.h
|
||||||
|
|
||||||
|
CompileFlags:
|
||||||
|
Add:
|
||||||
|
- "-xc-header"
|
||||||
|
- "-DARENA_IMPLEMENTATION"
|
||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2025 seajee
|
Copyright (c) 2026 Cristian Gora
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
test: test.c arena.h
|
test: test.c arena.h
|
||||||
cc -Wall -Wextra -DDEBUG -o test test.c
|
clang -Wall -Wextra -pedantic -std=c11 -o test test.c
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf test
|
rm -rf test
|
||||||
|
|||||||
550
arena.h
550
arena.h
@@ -1,10 +1,42 @@
|
|||||||
// arena.h - v1.2.3 - MIT License
|
// arena.h - v1.2.4 - MIT License
|
||||||
// single header library for region-based memory management.
|
// single header library for region-based memory management.
|
||||||
//
|
//
|
||||||
// [License and changelog]
|
// [License and changelog]
|
||||||
//
|
//
|
||||||
// See end of file.
|
// See end of file.
|
||||||
//
|
//
|
||||||
|
// [About this library]
|
||||||
|
//
|
||||||
|
// This is a header only, C11 compliant, library for region-based memory
|
||||||
|
// management.
|
||||||
|
//
|
||||||
|
// In this library, arenas are implemented as linked lists of regions. Each
|
||||||
|
// region will contain all the allocated user buffers in a single block of
|
||||||
|
// memory. Every time an arena needs a new region to satisfy a new allocation,
|
||||||
|
// it will append a new one to its internal linked list with an increased
|
||||||
|
// capacity.
|
||||||
|
//
|
||||||
|
// This library also exposes some other utilities, such as snapshot management
|
||||||
|
// of an arena and common functions used in situations where dynamic memory
|
||||||
|
// can get in the way of performance and simplicity.
|
||||||
|
//
|
||||||
|
// [Quick start example]
|
||||||
|
//
|
||||||
|
// #define ARENA_IMPLEMENTATION
|
||||||
|
// #include "arena.h"
|
||||||
|
//
|
||||||
|
// int main(void)
|
||||||
|
// {
|
||||||
|
// Arena a = {0}; // or arena_create(...) to specify a custom region
|
||||||
|
// // capacity just for this arena
|
||||||
|
//
|
||||||
|
// int *x = arena_alloc(&a, sizeof(*x) * 32);
|
||||||
|
// float *y = arena_alloc(&a, sizeof(*y) * 512);
|
||||||
|
//
|
||||||
|
// arena_free(&a); // The arena can still be reused
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
//
|
||||||
// [Single header library usage]
|
// [Single header library usage]
|
||||||
//
|
//
|
||||||
// Including this header file as such does what you normally expect.
|
// Including this header file as such does what you normally expect.
|
||||||
@@ -16,10 +48,28 @@
|
|||||||
// #define ARENA_IMPLEMENTATION
|
// #define ARENA_IMPLEMENTATION
|
||||||
// #include "arena.h"
|
// #include "arena.h"
|
||||||
//
|
//
|
||||||
|
// [Error handling]
|
||||||
|
//
|
||||||
|
// By default this library handles errors with assertions, thus terminating
|
||||||
|
// the program with an error message. This happens whenever a memory
|
||||||
|
// allocation or a critical condition fails.
|
||||||
|
//
|
||||||
|
// This behaviour has been chosen as the default one for development
|
||||||
|
// convenience, since the type of errors this library handles are rare in
|
||||||
|
// common scenarios.
|
||||||
|
//
|
||||||
|
// To disable this behaviour, see ARENA_NO_ASSERT in [Compile-time options]
|
||||||
|
// below.
|
||||||
|
//
|
||||||
// [Compile-time options]
|
// [Compile-time options]
|
||||||
//
|
//
|
||||||
// Note: every compile-time option listed here should be configured before
|
// Note: every compile-time option listed here should be configured before
|
||||||
// the #include "arena.h"
|
// including the header file.
|
||||||
|
//
|
||||||
|
// #define ARENA_ALIGNMENT alignment (alignof(max_align_t))
|
||||||
|
//
|
||||||
|
// This macro defines the memory alignment used for allocations.
|
||||||
|
// Must be a power of two.
|
||||||
//
|
//
|
||||||
// #define ARENA_REGION_CAPACITY new_region_capacity_in_bytes (8*1024)
|
// #define ARENA_REGION_CAPACITY new_region_capacity_in_bytes (8*1024)
|
||||||
//
|
//
|
||||||
@@ -30,17 +80,12 @@
|
|||||||
// This macro defines the growth factor of the capacity for every
|
// This macro defines the growth factor of the capacity for every
|
||||||
// subsequent region in all arenas.
|
// subsequent region in all arenas.
|
||||||
//
|
//
|
||||||
// #define ARENA_ALIGNMENT alignment (alignof(max_align_t))
|
|
||||||
//
|
|
||||||
// This macro defines the memory alignment used for allocations.
|
|
||||||
// Must be a power of two.
|
|
||||||
//
|
|
||||||
// #define ARENA_NO_ASSERT
|
// #define ARENA_NO_ASSERT
|
||||||
//
|
//
|
||||||
// This macro disables assertions that happen when allocations fail.
|
// This macro disables all assertions that happen when critical
|
||||||
// The disabled assertions will be replaced with a simple condition
|
// operations fail. This is useful for checking errors manually with
|
||||||
// that returns NULL when it fails. This macro affects every function
|
// return values instead of letting the library assert and terminate
|
||||||
// that returns a pointer.
|
// the program.
|
||||||
//
|
//
|
||||||
// #define ARENA_ASSERT my_assert
|
// #define ARENA_ASSERT my_assert
|
||||||
//
|
//
|
||||||
@@ -51,116 +96,106 @@
|
|||||||
// #define ARENA_FREE my_free
|
// #define ARENA_FREE my_free
|
||||||
//
|
//
|
||||||
// These macros define alternative functions for dynamic allocation
|
// These macros define alternative functions for dynamic allocation
|
||||||
// and deallocation. They are only used for managing Arena_Region
|
// and deallocation. They are only used for managing Arena__Region
|
||||||
// structures. If redefined ensure that your functions consider memory
|
// structures. If redefined ensure that your functions consider memory
|
||||||
// alignment.
|
// alignment.
|
||||||
//
|
//
|
||||||
// [Function documentation]
|
// [Function documentation]
|
||||||
//
|
//
|
||||||
// Note: every function that starts with "arena__" is considered a private
|
// Note: every function that starts with the prefix "arena__" is considered a
|
||||||
// function of this library.
|
// 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
|
|
||||||
//
|
//
|
||||||
// Arena arena_create(size_t region_capacity)
|
// Arena arena_create(size_t region_capacity)
|
||||||
//
|
//
|
||||||
// This function is optional (see [Example] section): initializes an arena
|
// This function is optional (see [Quick start example] section): it
|
||||||
// with a specified region capacity. It will override the default
|
// initializes an arena with a specified region capacity. It will override the
|
||||||
// ARENA_REGION_CAPACITY.
|
// default ARENA_REGION_CAPACITY just for this arena.
|
||||||
//
|
//
|
||||||
// void arena_free(Arena *a)
|
// void arena_free(Arena *arena)
|
||||||
//
|
//
|
||||||
// This function frees all of the regions allocated in the specified arena
|
// This function frees all of the regions allocated in the specified arena
|
||||||
// which invalidates all the pointers associated with the arena. The arena can
|
// which invalidates all the pointers associated with it. The arena can still
|
||||||
// still be reused. If the arena was created with arena_create() the
|
// be reused. If the arena was created with arena_create() the configured
|
||||||
// configured region_capacity will be retained.
|
// region_capacity will be retained.
|
||||||
//
|
//
|
||||||
// void arena_reset(Arena *a)
|
// void arena_reset(Arena *arena)
|
||||||
//
|
//
|
||||||
// This function resets an arena by keeping all of the regions allocated but
|
// This function resets an arena by keeping all of the regions allocated but
|
||||||
// invalidates all of the pointers associated with the specified arena.
|
// invalidates all of the pointers associated with the specified arena.
|
||||||
// Warning: this functions may cause fragmentation, consider setting an
|
|
||||||
// appropriate region capacity.
|
|
||||||
//
|
//
|
||||||
// void arena_trim(Arena *a)
|
// Warning: this function may cause fragmentation with future allocations.
|
||||||
|
// Consider setting an appropriate region capacity.
|
||||||
|
//
|
||||||
|
// void arena_trim(Arena *arena)
|
||||||
|
//
|
||||||
|
// This function frees all of the trailing regions where no buffers are
|
||||||
|
// allocated. This is useful after an arena_rewind() was performed to save
|
||||||
|
// memory.
|
||||||
//
|
//
|
||||||
// This functions frees all of the regions where no buffers are allocated.
|
|
||||||
// This is useful after an arena_rewind() was performed to preserve memory.
|
|
||||||
// Note: this function always keeps the first region allocated to preserve an
|
// Note: this function always keeps the first region allocated to preserve an
|
||||||
// ideal behaviour.
|
// ideal behaviour.
|
||||||
//
|
//
|
||||||
// void *arena_alloc(Arena *a, size_t size)
|
// void *arena_alloc(Arena *arena, size_t size)
|
||||||
//
|
//
|
||||||
// This function allocates a buffer of <size> bytes into the specified arena
|
// This function allocates a buffer of <size> bytes into the specified arena
|
||||||
// and returns it's pointer.
|
// and returns its pointer.
|
||||||
//
|
//
|
||||||
// void *arena_calloc(Arena *a, size_t count, size_t size)
|
// void *arena_calloc(Arena *arena, size_t count, size_t size)
|
||||||
//
|
//
|
||||||
// This function allocates a buffer of <count> * <size> bytes into the
|
// This function allocates a buffer of <count> * <size> bytes into the
|
||||||
// specified arena and returns it's pointer. The resulting buffer will be
|
// specified arena and returns its pointer. The resulting buffer will be
|
||||||
// zero-initialized just like the standard calloc() function.
|
// zero-initialized just like the standard calloc() function.
|
||||||
//
|
//
|
||||||
// void *arena_copy(Arena *a, const void *src, size_t size);
|
// void *arena_copy(Arena *arena, const void *src, size_t size)
|
||||||
//
|
//
|
||||||
// This function allocates a buffer of <size> bytes into the arena and copies
|
// This function allocates a buffer of <size> bytes into the arena and copies
|
||||||
// the contents of <src>. Returns the pointer of the new copy.
|
// the contents of <src>. Returns the pointer of the new copy.
|
||||||
//
|
//
|
||||||
// char *arena_strdup(Arena *a, const char *s);
|
// char *arena_strdup(Arena *arena, const char *s)
|
||||||
//
|
//
|
||||||
// This functions acts like the strdup() function but it instead allocates on
|
// This function acts like the standard strdup() function but it instead
|
||||||
// the specified arena. It duplicates a string in the arena.
|
// allocates on the specified arena. It duplicates a NULL terminated string in
|
||||||
|
// the arena.
|
||||||
//
|
//
|
||||||
// char *arena_sprintf(Arena *a, const char *fmt, ...)
|
// char *arena_sprintf(Arena *arena, const char *fmt, ...)
|
||||||
//
|
//
|
||||||
// This function internally uses vsnprintf() to store the resulting formatted
|
// This function uses standard printf style formatting to construct the
|
||||||
// string in the specified arena. Returns NULL on error.
|
// resulting string. It internally uses vsnprintf() to store the resulting
|
||||||
|
// formatted string in the specified arena.
|
||||||
//
|
//
|
||||||
// Arena_Marker arena_snapshot(const Arena *a)
|
// ArenaMarker arena_snapshot(const Arena *arena)
|
||||||
//
|
//
|
||||||
// This function creates a snapshot of the current state of the arena and
|
// 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
|
// returns it. This is useful when you only need to allocate memory on the arena
|
||||||
// temporarily. To restore the returned state, see arena_rewind().
|
// temporarily. To restore the returned state, see arena_rewind().
|
||||||
//
|
//
|
||||||
// void arena_rewind(Arena *a, Arena_Marker marker)
|
// void arena_rewind(Arena *arena, ArenaMarker marker)
|
||||||
//
|
//
|
||||||
// This function restores the arena to the specified state, invalidating all
|
// This function restores the arena to the specified state, invalidating all
|
||||||
// previously allocated buffers since the creation of the snapshot.
|
// previously allocated buffers since the creation of the snapshot.
|
||||||
//
|
//
|
||||||
|
// Note: this function does not free any newly allocated regions internally.
|
||||||
|
// See arena_trim()
|
||||||
|
//
|
||||||
// [Notes]
|
// [Notes]
|
||||||
//
|
//
|
||||||
// This library is not Thread-safe.
|
// This library does not account for thread safety.
|
||||||
//
|
|
||||||
// [Example]
|
|
||||||
//
|
|
||||||
#if 0
|
|
||||||
#define ARENA_IMPLEMENTATION
|
|
||||||
#include "arena.h"
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
Arena a = {0}; // or arena_create(...) to specify a custom region
|
|
||||||
// capacity just for this arena
|
|
||||||
|
|
||||||
int *x = arena_alloc(&a, sizeof(*x) * 32);
|
|
||||||
float *y = arena_alloc(&a, sizeof(*y) * 512);
|
|
||||||
|
|
||||||
arena_free(&a); // The arena can still be reused
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif // [Example]
|
|
||||||
|
|
||||||
#ifndef ARENA_H_
|
#ifndef ARENA_H_
|
||||||
#define ARENA_H_
|
#define ARENA_H_
|
||||||
|
|
||||||
#include <stdalign.h>
|
#include <stdalign.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifndef ARENA_ALIGNMENT
|
||||||
|
# define ARENA_ALIGNMENT alignof(max_align_t)
|
||||||
|
#endif // ARENA_ALIGNMENT
|
||||||
|
|
||||||
#ifndef ARENA_REGION_CAPACITY
|
#ifndef ARENA_REGION_CAPACITY
|
||||||
# define ARENA_REGION_CAPACITY (8*1024)
|
# define ARENA_REGION_CAPACITY (8*1024)
|
||||||
#endif // ARENA_REGION_CAPACITY
|
#endif // ARENA_REGION_CAPACITY
|
||||||
@@ -169,10 +204,6 @@ int main(void)
|
|||||||
# define ARENA_REGION_GROWTH_FACTOR 2
|
# define ARENA_REGION_GROWTH_FACTOR 2
|
||||||
#endif // ARENA_REGION_GROWTH_FACTOR
|
#endif // ARENA_REGION_GROWTH_FACTOR
|
||||||
|
|
||||||
#ifndef ARENA_ALIGNMENT
|
|
||||||
# define ARENA_ALIGNMENT alignof(max_align_t)
|
|
||||||
#endif // ARENA_ALIGNMENT
|
|
||||||
|
|
||||||
#ifndef ARENA_NO_ASSERT
|
#ifndef ARENA_NO_ASSERT
|
||||||
# ifndef ARENA_ASSERT
|
# ifndef ARENA_ASSERT
|
||||||
# include <assert.h>
|
# include <assert.h>
|
||||||
@@ -192,49 +223,44 @@ int main(void)
|
|||||||
# define ARENA_FREE free
|
# define ARENA_FREE free
|
||||||
#endif // ARENA_FREE
|
#endif // ARENA_FREE
|
||||||
|
|
||||||
#ifdef __cplusplus
|
typedef struct Arena_Region {
|
||||||
extern "C" { // Prevent name mangling of functions
|
struct Arena_Region *next;
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
typedef struct Arena_Region Arena_Region;
|
|
||||||
|
|
||||||
struct Arena_Region {
|
|
||||||
Arena_Region *next;
|
|
||||||
size_t count;
|
size_t count;
|
||||||
size_t capacity;
|
size_t capacity;
|
||||||
alignas(ARENA_ALIGNMENT) uint8_t data[];
|
alignas(ARENA_ALIGNMENT) uint8_t data[];
|
||||||
};
|
} Arena__Region;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Arena_Region *region;
|
Arena__Region *head;
|
||||||
size_t count;
|
Arena__Region *tail;
|
||||||
} Arena_Marker;
|
|
||||||
|
|
||||||
typedef struct Arena {
|
|
||||||
Arena_Region *head;
|
|
||||||
Arena_Region *tail;
|
|
||||||
size_t region_capacity;
|
size_t region_capacity;
|
||||||
} Arena;
|
} Arena;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Arena__Region *region;
|
||||||
|
size_t count;
|
||||||
|
} ArenaMarker;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" { // Prevent name mangling
|
||||||
|
#endif // __cplusplus
|
||||||
|
|
||||||
// Arena management
|
// Arena management
|
||||||
Arena arena_create(size_t region_capacity);
|
Arena arena_create(size_t region_capacity);
|
||||||
void arena_free(Arena *a);
|
void arena_free(Arena *arena);
|
||||||
void arena_reset(Arena *a);
|
void arena_reset(Arena *arena);
|
||||||
void arena_trim(Arena *a);
|
void arena_trim(Arena *arena);
|
||||||
|
|
||||||
// Allocation and other utilities
|
// Allocation and other utilities
|
||||||
void *arena_alloc(Arena *a, size_t size);
|
void *arena_alloc(Arena *arena, size_t size);
|
||||||
void *arena_calloc(Arena *a, size_t count, size_t size);
|
void *arena_calloc(Arena *arena, size_t count, size_t size);
|
||||||
void *arena_copy(Arena *a, const void *src, size_t size);
|
void *arena_copy(Arena *arena, const void *src, size_t size);
|
||||||
char *arena_strdup(Arena *a, const char *s);
|
char *arena_strdup(Arena *arena, const char *s);
|
||||||
char *arena_sprintf(Arena *a, const char *fmt, ...);
|
char *arena_sprintf(Arena *arena, const char *fmt, ...);
|
||||||
|
|
||||||
// Snapshot management
|
// Snapshot management
|
||||||
Arena_Marker arena_snapshot(const Arena *a);
|
ArenaMarker arena_snapshot(const Arena *arena);
|
||||||
void arena_rewind(Arena *a, Arena_Marker marker);
|
void arena_rewind(Arena *arena, ArenaMarker marker);
|
||||||
|
|
||||||
// Private functions
|
|
||||||
size_t arena__align(size_t n);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
@@ -244,197 +270,203 @@ size_t arena__align(size_t n);
|
|||||||
|
|
||||||
#ifdef ARENA_IMPLEMENTATION
|
#ifdef ARENA_IMPLEMENTATION
|
||||||
|
|
||||||
#ifdef __cplusplus
|
// Private functions
|
||||||
extern "C" { // Prevent name mangling of functions
|
static size_t arena__align(size_t n);
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
Arena arena_create(size_t region_capacity)
|
Arena arena_create(size_t region_capacity)
|
||||||
{
|
{
|
||||||
Arena a;
|
Arena arena;
|
||||||
memset(&a, 0, sizeof(a));
|
memset(&arena, 0, sizeof(arena));
|
||||||
a.region_capacity = region_capacity;
|
|
||||||
return a;
|
arena.region_capacity = region_capacity;
|
||||||
|
return arena;
|
||||||
}
|
}
|
||||||
|
|
||||||
void arena_free(Arena *a)
|
void arena_free(Arena *arena)
|
||||||
{
|
{
|
||||||
if (a == NULL) {
|
if (arena == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Arena_Region *cur = a->head;
|
Arena__Region *current = arena->head;
|
||||||
while (cur != NULL) {
|
|
||||||
Arena_Region *next = cur->next;
|
while (current != NULL) {
|
||||||
ARENA_FREE(cur);
|
Arena__Region *next = current->next;
|
||||||
cur = next;
|
ARENA_FREE(current);
|
||||||
|
current = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
a->head = NULL;
|
arena->head = NULL;
|
||||||
a->tail = NULL;
|
arena->tail = NULL;
|
||||||
|
// Keep the arena region_capacity saved
|
||||||
}
|
}
|
||||||
|
|
||||||
void arena_reset(Arena *a)
|
void arena_reset(Arena *arena)
|
||||||
{
|
{
|
||||||
if (a == NULL) {
|
if (arena == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Arena_Region *cur = a->head; cur != NULL; cur = cur->next) {
|
for (Arena__Region *r = arena->head; r != NULL; r = r->next) {
|
||||||
cur->count = 0;
|
r->count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
a->tail = a->head;
|
arena->tail = arena->head;
|
||||||
}
|
}
|
||||||
|
|
||||||
void arena_trim(Arena *a)
|
void arena_trim(Arena *arena)
|
||||||
{
|
{
|
||||||
if (a == NULL) {
|
if (arena == NULL || arena->tail == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Arena_Region *to_free = a->tail->next;
|
Arena__Region *to_free = arena->tail->next;
|
||||||
a->tail->next = NULL;
|
arena->tail->next = NULL;
|
||||||
while (to_free != NULL) {
|
while (to_free != NULL) {
|
||||||
Arena_Region *next = to_free->next;
|
Arena__Region *next = to_free->next;
|
||||||
ARENA_FREE(to_free);
|
ARENA_FREE(to_free);
|
||||||
to_free = next;
|
to_free = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *arena_alloc(Arena *a, size_t size)
|
void *arena_alloc(Arena *arena, size_t size)
|
||||||
{
|
{
|
||||||
if (a == NULL || size == 0) {
|
if (arena == NULL || size == 0) {
|
||||||
ARENA_ASSERT(false && "Invalid parameters");
|
ARENA_ASSERT(false && "Invalid parameters");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for overflow
|
size_t aligned_size = arena__align(size);
|
||||||
if (size > SIZE_MAX - sizeof(Arena_Region)) {
|
if (aligned_size < size) {
|
||||||
ARENA_ASSERT(false && "Requested size too large");
|
ARENA_ASSERT(false && "Allocation size overflow after alignment");
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure memory alignment of requested size
|
|
||||||
size = arena__align(size);
|
|
||||||
|
|
||||||
// Get the next region capacity multiplied by the growth factor
|
|
||||||
size_t region_capacity = (a->region_capacity == 0
|
|
||||||
? ARENA_REGION_CAPACITY : a->region_capacity);
|
|
||||||
if (a->tail != NULL) {
|
|
||||||
size_t max_cap = (SIZE_MAX / 2) - sizeof(Arena_Region);
|
|
||||||
|
|
||||||
if (a->tail->capacity >= max_cap / (ARENA_REGION_GROWTH_FACTOR)) {
|
|
||||||
region_capacity = max_cap;
|
|
||||||
} else {
|
|
||||||
region_capacity = a->tail->capacity * (ARENA_REGION_GROWTH_FACTOR);
|
|
||||||
// Edge case for small growth factors
|
|
||||||
if (region_capacity <= a->tail->capacity) {
|
|
||||||
region_capacity = a->tail->capacity + (4 * 1024);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate and check region allocation size
|
|
||||||
size_t alloc_size = (size > region_capacity ? size : region_capacity);
|
|
||||||
if (alloc_size > SIZE_MAX - sizeof(Arena_Region)) {
|
|
||||||
ARENA_ASSERT(false && "Region size overflow");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty arena: allocate first region
|
// Empty arena: allocate first region
|
||||||
if (a->head == NULL) {
|
if (arena->head == NULL) {
|
||||||
a->head = (Arena_Region*)ARENA_MALLOC(sizeof(*a->head) + alloc_size);
|
size_t default_capacity = (arena->region_capacity == 0
|
||||||
if (a->head == NULL) {
|
? ARENA_REGION_CAPACITY
|
||||||
ARENA_ASSERT(false && "Allocation failed");
|
: arena->region_capacity);
|
||||||
|
size_t alloc_capacity = (aligned_size > default_capacity
|
||||||
|
? aligned_size
|
||||||
|
: default_capacity);
|
||||||
|
|
||||||
|
if (alloc_capacity > SIZE_MAX - sizeof(Arena__Region)) {
|
||||||
|
ARENA_ASSERT(false && "Region allocation size overflow");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
a->head->next = NULL;
|
arena->head = arena->tail = (Arena__Region *)ARENA_MALLOC(
|
||||||
a->head->count = size;
|
sizeof(*arena->head) + alloc_capacity);
|
||||||
a->head->capacity = alloc_size;
|
if (arena->head == NULL) {
|
||||||
a->tail = a->head;
|
ARENA_ASSERT(false && "Memory allocation failed");
|
||||||
return a->head->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find first suitable region
|
|
||||||
Arena_Region *r = a->tail;
|
|
||||||
Arena_Region *last = r;
|
|
||||||
while (r != NULL && size > r->capacity - r->count) {
|
|
||||||
last = r;
|
|
||||||
r = r->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If found allocate on it
|
|
||||||
if (r != NULL) {
|
|
||||||
if (r->count > SIZE_MAX - size) {
|
|
||||||
ARENA_ASSERT(false && "Region count overflow");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
a->tail = r;
|
|
||||||
r->count += size;
|
|
||||||
return r->data + r->count - size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not found append a new region
|
|
||||||
last->next = (Arena_Region*)ARENA_MALLOC(sizeof(*a->tail) + alloc_size);
|
|
||||||
if (last->next == NULL) {
|
|
||||||
ARENA_ASSERT(false && "Allocation failed");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
a->tail = last->next;
|
arena->head->next = NULL;
|
||||||
a->tail->next = NULL;
|
arena->head->count = aligned_size;
|
||||||
a->tail->count = size;
|
arena->head->capacity = alloc_capacity;
|
||||||
a->tail->capacity = alloc_size;
|
return arena->head->data;
|
||||||
return a->tail->data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *arena_calloc(Arena *a, size_t count, size_t size)
|
// Non-empty arena: search existing regions for sufficient capacity
|
||||||
|
Arena__Region *region = arena->tail;
|
||||||
|
Arena__Region *last_region = region;
|
||||||
|
while (region != NULL && aligned_size > region->capacity - region->count) {
|
||||||
|
last_region = region;
|
||||||
|
region = region->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region != NULL) {
|
||||||
|
void *ptr = region->data + region->count;
|
||||||
|
arena->tail = region;
|
||||||
|
region->count += aligned_size;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No fitting region found: append a new region
|
||||||
|
size_t next_capacity = last_region->capacity;
|
||||||
|
if (next_capacity <= SIZE_MAX / ARENA_REGION_GROWTH_FACTOR) {
|
||||||
|
next_capacity *= ARENA_REGION_GROWTH_FACTOR;
|
||||||
|
} else {
|
||||||
|
next_capacity = SIZE_MAX - sizeof(Arena__Region);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t alloc_capacity = (aligned_size > next_capacity
|
||||||
|
? aligned_size
|
||||||
|
: next_capacity);
|
||||||
|
|
||||||
|
if (alloc_capacity > SIZE_MAX - sizeof(Arena__Region)) {
|
||||||
|
ARENA_ASSERT(false && "Region allocation size overflow");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_region->next = (Arena__Region *)ARENA_MALLOC(
|
||||||
|
sizeof(*last_region->next) + alloc_capacity);
|
||||||
|
if (last_region->next == NULL) {
|
||||||
|
ARENA_ASSERT(false && "Memory allocation failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
arena->tail = last_region->next;
|
||||||
|
arena->tail->next = NULL;
|
||||||
|
arena->tail->count = aligned_size;
|
||||||
|
arena->tail->capacity = alloc_capacity;
|
||||||
|
return arena->tail->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *arena_calloc(Arena *arena, size_t count, size_t size)
|
||||||
{
|
{
|
||||||
if (a == NULL) {
|
if (arena == NULL) {
|
||||||
ARENA_ASSERT(false && "Invalid parameters");
|
ARENA_ASSERT(false && "Invalid parameters");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *result = arena_alloc(a, count * size);
|
// Prevent size_t overflow during arena_alloc size calculation
|
||||||
|
if (size > 0 && count > SIZE_MAX / size) {
|
||||||
if (result != NULL) {
|
ARENA_ASSERT(false && "Allocation size overflow");
|
||||||
memset(result, 0, count * size);
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
size_t alloc_size = count * size;
|
||||||
|
|
||||||
|
void *ptr = arena_alloc(arena, alloc_size);
|
||||||
|
if (ptr == NULL) { return NULL; }
|
||||||
|
|
||||||
|
memset(ptr, 0, alloc_size);
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *arena_copy(Arena *a, const void *src, size_t size)
|
void *arena_copy(Arena *arena, const void *src, size_t size)
|
||||||
{
|
{
|
||||||
if (a == NULL || src == NULL) {
|
if (arena == NULL || src == NULL) {
|
||||||
ARENA_ASSERT(false && "Invalid parameters");
|
ARENA_ASSERT(false && "Invalid parameters");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *copy = arena_alloc(a, size);
|
void *copy = arena_alloc(arena, size);
|
||||||
if (copy == NULL) {
|
if (copy == NULL) { return NULL; }
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(copy, src, size);
|
memcpy(copy, src, size);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *arena_strdup(Arena *arena, const char *s)
|
||||||
|
{
|
||||||
|
if (arena == NULL || s == NULL) {
|
||||||
|
ARENA_ASSERT(false && "Invalid parameters");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *copy = (char *)arena_copy(arena, s, strlen(s) + 1);
|
||||||
|
if (copy == NULL) { return NULL; }
|
||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *arena_strdup(Arena *a, const char *s)
|
char *arena_sprintf(Arena *arena, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
if (a == NULL || s == NULL) {
|
if (arena == NULL || fmt == NULL) {
|
||||||
ARENA_ASSERT(false && "Invalid parameters");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
ARENA_ASSERT(false && "Invalid parameters");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -442,105 +474,83 @@ char *arena_sprintf(Arena *a, const char *fmt, ...)
|
|||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
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_list args_copy;
|
||||||
va_copy(args_copy, args);
|
va_copy(args_copy, args);
|
||||||
int n = vsnprintf(result, available, fmt, args_copy);
|
int length = vsnprintf(NULL, 0, fmt, args_copy);
|
||||||
va_end(args_copy);
|
va_end(args_copy);
|
||||||
|
if (length < 0) {
|
||||||
if (n < 0) {
|
ARENA_ASSERT(false && "Format string length calculation failed");
|
||||||
va_end(args);
|
va_end(args);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t needed = (size_t)n + 1;
|
size_t size = (size_t)length + 1;
|
||||||
|
char *str = (char *)arena_alloc(arena, size);
|
||||||
// If it fits commit and return
|
if (str == NULL) {
|
||||||
if (arena__align(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);
|
va_end(args);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = vsnprintf(result, needed, fmt, args);
|
int written = vsnprintf(str, size, fmt, args);
|
||||||
|
ARENA_ASSERT(written == length && "Mismatched vsnprintf on second pass");
|
||||||
|
if (written < 0) {
|
||||||
|
ARENA_ASSERT(false && "Failed vsnprintf on second pass");
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
if (n < 0) {
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
va_end(args);
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
Arena_Marker arena_snapshot(const Arena *a)
|
ArenaMarker arena_snapshot(const Arena *arena)
|
||||||
{
|
{
|
||||||
Arena_Marker marker = {0};
|
ArenaMarker marker;
|
||||||
|
memset(&marker, 0, sizeof(marker));
|
||||||
|
|
||||||
if (a == NULL) {
|
if (arena == NULL) {
|
||||||
ARENA_ASSERT(false && "Invalid parameters");
|
ARENA_ASSERT(false && "Invalid parameters");
|
||||||
return marker;
|
return marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
marker.region = a->tail;
|
marker.region = arena->tail;
|
||||||
marker.count = (a->tail != NULL ? a->tail->count : 0);
|
marker.count = (arena->tail != NULL ? arena->tail->count : 0);
|
||||||
|
|
||||||
return marker;
|
return marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
void arena_rewind(Arena *a, Arena_Marker marker)
|
void arena_rewind(Arena *arena, ArenaMarker marker)
|
||||||
{
|
{
|
||||||
if (a == NULL) {
|
if (arena == NULL) {
|
||||||
ARENA_ASSERT(false && "Invalid parameters");
|
ARENA_ASSERT(false && "Invalid parameters");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If arena was empty reset it
|
// If arena was empty reset it
|
||||||
if (marker.region == NULL) {
|
if (marker.region == NULL) {
|
||||||
arena_reset(a);
|
arena_reset(arena);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
a->tail = marker.region;
|
arena->tail = marker.region;
|
||||||
a->tail->count = marker.count;
|
arena->tail->count = marker.count;
|
||||||
|
|
||||||
// Clear new regions in case they were appended
|
// Reset any new regions that were appended
|
||||||
for (Arena_Region *cur = a->tail->next; cur != NULL; cur = cur->next) {
|
for (Arena__Region *r = arena->tail->next; r != NULL; r = r->next) {
|
||||||
cur->count = 0;
|
r->count = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t arena__align(size_t n)
|
static size_t arena__align(size_t n)
|
||||||
{
|
{
|
||||||
return (n + (ARENA_ALIGNMENT - 1)) & ~(ARENA_ALIGNMENT - 1);
|
return (n + (ARENA_ALIGNMENT - 1)) & ~(ARENA_ALIGNMENT - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
#endif // ARENA_IMPLEMENTATION
|
#endif // ARENA_IMPLEMENTATION
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Revision history:
|
* Revision history:
|
||||||
*
|
*
|
||||||
|
* 1.2.4 (2026-09-20) Major rewrite
|
||||||
* 1.2.3 (2026-07-07) New function: arena_calloc()
|
* 1.2.3 (2026-07-07) New function: arena_calloc()
|
||||||
* 1.2.2 (2026-07-07) Fix various memory problems;
|
* 1.2.2 (2026-07-07) Fix various memory problems;
|
||||||
* make ARENA_REGION_GROWTH_FACTOR an integer
|
* make ARENA_REGION_GROWTH_FACTOR an integer
|
||||||
@@ -562,7 +572,7 @@ size_t arena__align(size_t n)
|
|||||||
/*
|
/*
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2025 seajee
|
* Copyright (c) 2026 Cristian Gora
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
|||||||
249
test.c
249
test.c
@@ -1,64 +1,211 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
#define malloc(s) (printf("%s:%d:%s: malloc(%lu)\n",\
|
|
||||||
__FILE__, __LINE__, __func__, (s)), malloc((s)));
|
|
||||||
#define free(p) (printf("%s:%d:%s: free(%p)\n",\
|
|
||||||
__FILE__, __LINE__, __func__, (p)), free((p)));
|
|
||||||
#endif // DEBUG
|
|
||||||
|
|
||||||
#define ARENA_IMPLEMENTATION
|
#define ARENA_IMPLEMENTATION
|
||||||
#define ARENA_REGION_GROWTH_FACTOR 2
|
#define ARENA_NO_ASSERT
|
||||||
#include "arena.h"
|
#include "arena.h"
|
||||||
|
|
||||||
void arena_print(Arena arena)
|
// Helper to count active regions in the linked list
|
||||||
|
static int count_regions(const Arena *arena)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int count = 0;
|
||||||
for (Arena_Region *cur = arena.head; cur != NULL; cur = cur->next) {
|
for (Arena__Region *r = arena->head; r != NULL; r = r->next) {
|
||||||
printf("Arena_Region #%d:\n", ++i);
|
count++;
|
||||||
printf(" count = %ld\n", cur->count);
|
|
||||||
printf(" capacity = %ld\n", cur->capacity);
|
|
||||||
printf(" next = %p\n", cur->next);
|
|
||||||
printf(" data = %p\n", cur->data);
|
|
||||||
}
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to verify pointer alignment
|
||||||
|
static bool is_aligned(const void *ptr)
|
||||||
|
{
|
||||||
|
return ((uintptr_t)ptr % ARENA_ALIGNMENT) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_edge_case_inputs(void)
|
||||||
|
{
|
||||||
|
printf("Running edge case and invalid input tests...\n");
|
||||||
|
Arena arena = arena_create(1024);
|
||||||
|
|
||||||
|
// 0-byte allocations
|
||||||
|
assert(arena_alloc(&arena, 0) == NULL);
|
||||||
|
assert(arena_calloc(&arena, 0, 10) == NULL);
|
||||||
|
assert(arena_calloc(&arena, 10, 0) == NULL);
|
||||||
|
|
||||||
|
// NULL pointer handling
|
||||||
|
assert(arena_alloc(NULL, 100) == NULL);
|
||||||
|
assert(arena_calloc(NULL, 1, 1) == NULL);
|
||||||
|
assert(arena_copy(NULL, "data", 4) == NULL);
|
||||||
|
assert(arena_copy(&arena, NULL, 4) == NULL);
|
||||||
|
assert(arena_strdup(NULL, "test") == NULL);
|
||||||
|
assert(arena_strdup(&arena, NULL) == NULL);
|
||||||
|
assert(arena_sprintf(NULL, "test") == NULL);
|
||||||
|
assert(arena_sprintf(&arena, NULL) == NULL);
|
||||||
|
|
||||||
|
// Snapshot/Rewind/Trim edge cases on NULL or empty
|
||||||
|
arena_trim(NULL); // Should not crash
|
||||||
|
arena_reset(NULL); // Should not crash
|
||||||
|
arena_free(NULL); // Should not crash
|
||||||
|
|
||||||
|
ArenaMarker null_marker = arena_snapshot(NULL);
|
||||||
|
assert(null_marker.region == NULL && null_marker.count == 0);
|
||||||
|
arena_rewind(NULL, null_marker); // Should not crash
|
||||||
|
|
||||||
|
arena_free(&arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_overflows(void)
|
||||||
|
{
|
||||||
|
printf("Running integer overflow tests...\n");
|
||||||
|
Arena arena = arena_create(1024);
|
||||||
|
|
||||||
|
// calloc multiplication overflow (count * size > SIZE_MAX)
|
||||||
|
size_t massive_count = SIZE_MAX / 2;
|
||||||
|
size_t massive_size = 4;
|
||||||
|
assert(arena_calloc(&arena, massive_count, massive_size) == NULL);
|
||||||
|
|
||||||
|
// arena_alloc size wrap-around on alignment
|
||||||
|
// arena__align(SIZE_MAX) will overflow back to a small number
|
||||||
|
size_t overflow_align_size = SIZE_MAX - (ARENA_ALIGNMENT / 2);
|
||||||
|
assert(arena_alloc(&arena, overflow_align_size) == NULL);
|
||||||
|
|
||||||
|
arena_free(&arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_geometric_growth_and_stress(void)
|
||||||
|
{
|
||||||
|
printf("Running geometric growth and stress tests...\n");
|
||||||
|
|
||||||
|
size_t initial_capacity = 64;
|
||||||
|
Arena arena = arena_create(initial_capacity);
|
||||||
|
|
||||||
|
// Exhaust the first region to force geometric growth
|
||||||
|
assert(arena_alloc(&arena, 32) != NULL); // Head allocated with capacity 64
|
||||||
|
assert(arena.head->capacity == 64);
|
||||||
|
|
||||||
|
assert(arena_alloc(&arena, 32) != NULL); // Head is now full
|
||||||
|
|
||||||
|
// Trigger next region (should be 64 * 2 = 128)
|
||||||
|
assert(arena_alloc(&arena, 32) != NULL);
|
||||||
|
assert(arena.tail->capacity == 128);
|
||||||
|
|
||||||
|
// Trigger next region (should be 128 * 2 = 256)
|
||||||
|
assert(arena_alloc(&arena, 128) != NULL);
|
||||||
|
assert(arena.tail->capacity == 256);
|
||||||
|
|
||||||
|
// Stress test: Allocate 10,000 small blocks
|
||||||
|
for (int i = 0; i < 10000; i++) {
|
||||||
|
void *ptr = arena_alloc(&arena, 7); // Odd size to test alignment scaling
|
||||||
|
assert(ptr != NULL);
|
||||||
|
assert(is_aligned(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify regions were created
|
||||||
|
int regions = count_regions(&arena);
|
||||||
|
assert(regions > 3);
|
||||||
|
|
||||||
|
// Huge allocation bypassing standard growth
|
||||||
|
// Requesting 1MB when the next capacity might only be ~4KB
|
||||||
|
void *huge = arena_alloc(&arena, 1024 * 1024);
|
||||||
|
assert(huge != NULL);
|
||||||
|
assert(arena.tail->capacity >= 1024 * 1024);
|
||||||
|
|
||||||
|
arena_free(&arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_snapshot_rewind_trim_logic(void)
|
||||||
|
{
|
||||||
|
printf("Running snapshot, rewind, and trim logic tests...\n");
|
||||||
|
Arena arena = arena_create(128);
|
||||||
|
|
||||||
|
// 1. Snapshot empty
|
||||||
|
ArenaMarker empty_marker = arena_snapshot(&arena);
|
||||||
|
assert(empty_marker.region == NULL);
|
||||||
|
|
||||||
|
// 2. Allocate across 3 regions
|
||||||
|
arena_alloc(&arena, 100); // Region 1 (Cap: 128)
|
||||||
|
arena_snapshot(&arena);
|
||||||
|
|
||||||
|
arena_alloc(&arena, 200); // Region 2 (Cap: 256)
|
||||||
|
ArenaMarker mark_r2 = arena_snapshot(&arena);
|
||||||
|
|
||||||
|
arena_alloc(&arena, 400); // Region 3 (Cap: 512)
|
||||||
|
assert(count_regions(&arena) == 3);
|
||||||
|
|
||||||
|
// 3. Rewind to Region 2
|
||||||
|
arena_rewind(&arena, mark_r2);
|
||||||
|
assert(arena.tail == mark_r2.region);
|
||||||
|
assert(arena.tail->count == mark_r2.count);
|
||||||
|
|
||||||
|
// Check that Region 3's count was reset to 0
|
||||||
|
assert(arena.tail->next != NULL);
|
||||||
|
assert(arena.tail->next->count == 0);
|
||||||
|
|
||||||
|
// 4. Trim trailing regions (Should drop Region 3)
|
||||||
|
arena_trim(&arena);
|
||||||
|
assert(arena.tail->next == NULL);
|
||||||
|
assert(count_regions(&arena) == 2);
|
||||||
|
|
||||||
|
// 5. Rewind to completely empty
|
||||||
|
arena_rewind(&arena, empty_marker);
|
||||||
|
assert(arena.tail == NULL || (arena.tail == arena.head && arena.tail->count == 0));
|
||||||
|
|
||||||
|
// Trim when virtually empty (should retain the head region if it exists)
|
||||||
|
arena_trim(&arena);
|
||||||
|
if (arena.head != NULL) {
|
||||||
|
assert(arena.head->next == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
arena_free(&arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_massive_string_formatting(void)
|
||||||
|
{
|
||||||
|
printf("Running massive string formatting tests...\n");
|
||||||
|
Arena arena = arena_create(1024);
|
||||||
|
|
||||||
|
// Create a 50,000 character string requirement
|
||||||
|
size_t massive_len = 50000;
|
||||||
|
char *massive_input = malloc(massive_len + 1);
|
||||||
|
assert(massive_input != NULL);
|
||||||
|
memset(massive_input, 'X', massive_len);
|
||||||
|
massive_input[massive_len] = '\0';
|
||||||
|
|
||||||
|
char *result = arena_sprintf(&arena, "PREFIX_%s_SUFFIX", massive_input);
|
||||||
|
assert(result != NULL);
|
||||||
|
|
||||||
|
size_t expected_length = 7 + massive_len + 7;
|
||||||
|
assert(strlen(result) == expected_length);
|
||||||
|
assert(strncmp(result, "PREFIX_XXX", 10) == 0);
|
||||||
|
assert(strcmp(result + expected_length - 7, "_SUFFIX") == 0);
|
||||||
|
|
||||||
|
free(massive_input);
|
||||||
|
arena_free(&arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_zero_capacity_fallback(void)
|
||||||
|
{
|
||||||
|
printf("Running zero-capacity fallback tests...\n");
|
||||||
|
|
||||||
|
// Passing 0 should fall back to ARENA_REGION_CAPACITY implicitly on first alloc
|
||||||
|
Arena arena = arena_create(0);
|
||||||
|
|
||||||
|
void *ptr = arena_alloc(&arena, 10);
|
||||||
|
assert(ptr != NULL);
|
||||||
|
assert(arena.head->capacity >= ARENA_REGION_CAPACITY);
|
||||||
|
|
||||||
|
arena_free(&arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
printf("struct Arena_Region {\n");
|
test_edge_case_inputs();
|
||||||
printf(" Arena_Region *next; (%zu)\n", offsetof(Arena_Region, next));
|
test_overflows();
|
||||||
printf(" size_t count; (%zu)\n", offsetof(Arena_Region, count));
|
test_geometric_growth_and_stress();
|
||||||
printf(" size_t capacity; (%zu)\n", offsetof(Arena_Region, capacity));
|
test_snapshot_rewind_trim_logic();
|
||||||
printf(" uint8_t data[]; (%zu)\n", offsetof(Arena_Region, data));
|
test_massive_string_formatting();
|
||||||
printf("};\n");
|
test_zero_capacity_fallback();
|
||||||
|
|
||||||
printf("Alignment: %zu\n", ARENA_ALIGNMENT);
|
return EXIT_SUCCESS;
|
||||||
printf("================================================================\n");
|
|
||||||
|
|
||||||
Arena a = {0};
|
|
||||||
|
|
||||||
arena_alloc(&a, 4012);
|
|
||||||
arena_alloc(&a, 100);
|
|
||||||
arena_alloc(&a, 2301);
|
|
||||||
arena_alloc(&a, 4015);
|
|
||||||
arena_alloc(&a, 10000);
|
|
||||||
|
|
||||||
arena_reset(&a);
|
|
||||||
|
|
||||||
arena_alloc(&a, 1000);
|
|
||||||
|
|
||||||
arena_print(a);
|
|
||||||
|
|
||||||
char *str = arena_sprintf(&a, "Formatted string %x", 0xCAFE);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 1000; ++i) {
|
|
||||||
str = arena_sprintf(&a, "Formatted string %d", i);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("%s\n", str);
|
|
||||||
|
|
||||||
arena_print(a);
|
|
||||||
arena_free(&a);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user