feat: initial commit, basic arena

This commit is contained in:
2026-01-09 00:35:24 +02:00
parent ed1fa16625
commit 1dd4a788a8
8 changed files with 289 additions and 1 deletions

22
.clang-format Normal file
View File

@@ -0,0 +1,22 @@
AlignAfterOpenBracket: DontAlign
AlignEscapedNewlines: DontAlign
AlignOperands: DontAlign
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: TopLevel
BreakBeforeTernaryOperators: false
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: WebKit
IndentCaseLabels: false
TabWidth: 4
IndentWidth: 4
ContinuationIndentWidth: 4
UseTab: ForContinuationAndIndentation
ColumnLimit: 0
ReflowComments: false
SortIncludes: false
SpaceBeforeParens: false

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
bin/

View File

@@ -1,3 +1,7 @@
# ni-arena
Arena Allocator of the NI-C system
Arena Allocator of the NI-C system
## Dependencies
- [ni-const](https://codecave.studiosi.es/studiosi/ni-const) constants library of the NI-C system

27
makefile Normal file
View File

@@ -0,0 +1,27 @@
RELEASE_flags=-DNDEBUG -O2 -g0
DEBUG_flags=-std=c23 -DDEBUG -Wall -Wno-unknown-pragmas -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og
clean:
rm -fr bin
format:
clang-format -i test/niarena_test.c
clang-format -i test/niarena_test_alloc_arena.c
clang-format -i test/niarena_test_alloc_buffer.c
clang-format -i niarena.h
test: bin/test
bin/niarena_test
bin/niarena_test_alloc_arena
bin/niarena_test_alloc_buffer
leaks: bin/test
leaks --atExit -- bin/niarena_test
.PHONY: clean format test
bin/test: test/niarena_test.c
mkdir -p bin/test
cc ${DEBUG_flags} test/niarena_test.c -o bin/niarena_test
cc ${DEBUG_flags} test/niarena_test_alloc_buffer.c -o bin/niarena_test_alloc_buffer
cc ${DEBUG_flags} test/niarena_test_alloc_buffer.c -o bin/niarena_test_alloc_arena

94
niarena.h Normal file
View File

@@ -0,0 +1,94 @@
#pragma once
#include "../ni-const/niconst.h"
// If custom memory management is provided, including this
// header can be skipped
#ifndef NIAREA_NO_STDLIB
#include <stdlib.h>
#endif
// The size can be defined by overriding this constant
#ifndef NIARENA_ARENA_SIZE
#define NIARENA_ARENA_SIZE NICONST_MIB_B(4)
#endif
// Custom memory management can be defined as well.
// Either both or none must be defined
#if (!defined(NIARENA_MALLOC) && defined(NIARENA_FREE)) || (defined(NIARENA_MALLOC) && !defined(NIARENA_FREE))
#error "Either both NIARENA_MALLOC and NIARENA_FREE have to be defined or neither of them"
#endif
#ifndef NIARENA_MALLOC
#define NIARENA_MALLOC(sz) malloc(sz)
#endif
#ifndef NIARENA_FREE
#define NIARENA_FREE(p) free(p)
#endif
// Definitions
typedef struct _NIArena {
void *buffer;
size_t capacity;
size_t offset;
} NIArena;
const char *niarena_get_error(void);
NIArena *niarena_new(void);
void *niarena_alloc(NIArena *arena, size_t size);
void niarena_delete(NIArena *arena);
#ifdef NIARENA_IMPLEMENTATION
static const char *niarena__error_reason = "";
static void
niarena_error(const char *reason)
{
niarena__error_reason = reason;
}
const char *
niarena_get_error(void)
{
return niarena__error_reason;
}
NIArena *
niarena_new(void)
{
NIArena *arena = NIARENA_MALLOC(sizeof(NIArena));
if(arena == NULL) {
niarena_error("niarena_new: out of memory allocating arena");
return NULL;
}
arena->buffer = NIARENA_MALLOC(NIARENA_ARENA_SIZE);
if(arena->buffer == NULL) {
NIARENA_FREE(arena);
niarena_error("niarena_new: out of memory allocating buffer");
return NULL;
}
arena->capacity = NIARENA_ARENA_SIZE;
return arena;
}
void *
niarena_alloc(NIArena *arena, size_t size)
{
if((arena->capacity - arena->offset) < size) {
// The allocation is invalid, but the contents of the arena
// can still be used (previous allocations).
niarena_error("niarena_alloc: out of memory inside of the arena");
return NULL;
}
arena->offset += size;
return (void *)((char *)arena->buffer + arena->offset);
}
void
niarena_delete(NIArena *arena)
{
NIARENA_FREE(arena->buffer);
NIARENA_FREE(arena);
}
#endif

52
test/niarena_test.c Normal file
View File

@@ -0,0 +1,52 @@
#define N_INTS_TEST 4
#define NIARENA_ARENA_SIZE (N_INTS_TEST * sizeof(int))
#define NIARENA_IMPLEMENTATION
#include "../niarena.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
void
test_arena_new()
{
NIArena *arena = niarena_new();
assert(arena->offset == 0);
assert(arena->capacity == (N_INTS_TEST * sizeof(int)));
niarena_delete(arena);
}
void
test_arena_allocations()
{
NIArena *arena = niarena_new();
int *ns[N_INTS_TEST];
for(int i = 1; i <= N_INTS_TEST + 1; i++) {
int *p = (int *)niarena_alloc(arena, sizeof(int));
if(i == N_INTS_TEST + 1) {
// Invalid allocation does not invalidate the rest
assert(p == NULL);
const char *err = niarena_get_error();
assert(strcmp(err, "niarena_alloc: out of memory inside of the arena") == 0);
} else {
assert(arena->offset == i * sizeof(int));
*p = 1234 + i;
ns[i - 1] = p;
}
}
for(int i = 1; i <= N_INTS_TEST; i++) {
assert(*ns[i - 1] == 1234 + i);
}
niarena_delete(arena);
}
int
main()
{
test_arena_new();
test_arena_allocations();
printf("== TESTS SUCCESSFUL ==\n");
return 0;
}

View File

@@ -0,0 +1,42 @@
#include <stdio.h>
#include <stdlib.h>
void *
bad_malloc(size_t sz)
{
(void)sz;
return NULL;
}
#define NIARENA_MALLOC(sz) bad_malloc(sz)
void
mock_free(void *p)
{
free(p);
}
#define NIARENA_FREE(p) mock_free(p)
#define N_INTS_TEST 4
#define NIARENA_ARENA_SIZE (N_INTS_TEST * sizeof(int))
#define NIARENA_IMPLEMENTATION
#include "../niarena.h"
#include <assert.h>
#include <string.h>
void
niarena_test_malloc_fail_arena()
{
NIArena *arena = niarena_new();
assert(arena == NULL);
const char *err = niarena_get_error();
assert(strcmp(err, "niarena_new: out of memory allocating arena") == 0);
}
int
main()
{
niarena_test_malloc_fail_arena();
printf("== TESTS SUCCESSFUL ==\n");
return 0;
}

View File

@@ -0,0 +1,46 @@
#include <stdio.h>
#include <stdlib.h>
int call_count = 0;
void *
bad_malloc(size_t sz)
{
call_count++;
if(call_count != 2) {
return malloc(sz);
}
return NULL;
}
#define NIARENA_MALLOC(sz) bad_malloc(sz)
void
mock_free(void *p)
{
free(p);
}
#define NIARENA_FREE(p) mock_free(p)
#define N_INTS_TEST 4
#define NIARENA_ARENA_SIZE (N_INTS_TEST * sizeof(int))
#define NIARENA_IMPLEMENTATION
#include "../niarena.h"
#include <assert.h>
#include <string.h>
void
niarena_test_malloc_fail_buffer()
{
NIArena *arena = niarena_new();
assert(arena == NULL);
const char *err = niarena_get_error();
assert(strcmp(err, "niarena_new: out of memory allocating buffer") == 0);
}
int
main()
{
niarena_test_malloc_fail_buffer();
printf("== TESTS SUCCESSFUL ==\n");
return 0;
}