remove the free function entirely. all allocators are now expected to
implement reset instead. I reallized I never ever want to free an
individual allocation ever again. The forever_allocator is a special
case wherEreset is a no-op. The arena allocator was deleted. It will be
replaced soon.
This commit is contained in:
2025-12-02 20:21:12 +01:00
parent ef8d51f153
commit 01b8150625
11 changed files with 62 additions and 385 deletions
-142
View File
@@ -1,142 +0,0 @@
#include "cig.h"
#include <assert.h>
#include <stdbool.h>
void *arena_allocator_alloc_func(
arena_allocator_t *this,
size_t bytes,
const char *file,
int line
) {
buffer_allocator_t ba = (buffer_allocator_t) {
.size=dyn_array_length(this->bytes),
.capacity=dyn_array_capacity(this->bytes),
.data=this->bytes
};
void *allocated = buffer_allocator_alloc(&ba, bytes);
if (allocated != NULL) {
return allocated;
}
allocated = borrow_allocator_alloc_func(&this->borrow_allocator, bytes, file, line);
if (allocated != NULL) {
this->to_allocate += bytes;
return allocated;
}
return NULL;
}
void arena_allocator_reset_func(arena_allocator_t *this, const char *file, int line) {
borrow_allocator_free_all(&this->borrow_allocator);
if (this->bytes == NULL) {
this->bytes = dyn_array_create_non_crashing_func(
(dyn_array_create_non_crashing_func_args_t) {
.allocator=allocator_stdlib(),
.itemsize=sizeof(uint8_t),
.initial_capacity=this->to_allocate,
.file=file,
.line=line,
}
);
}
if (this->bytes == NULL) {
return;
}
if (dyn_array_capacity(this->bytes) < this->to_allocate) {
size_t needed_bytes = this->to_allocate - dyn_array_length(this->bytes);
uint8_t *new_bytes = dyn_array_grow_non_crashing_func(
this->bytes,
needed_bytes,
file,
line
);
if (new_bytes == NULL) {
dyn_array_shrink_func(
this->bytes,
dyn_array_length(this->bytes),
file,
line
);
return;
} else {
this->bytes = new_bytes;
}
}
if (this->bytes == NULL) {
return;
}
dyn_array_shrink_func(this->bytes, dyn_array_length(this->bytes), file, line);
return;
}
void arena_allocator_destroy(arena_allocator_t *this) {
borrow_allocator_free_all(&this->borrow_allocator);
dyn_array_destroy(this->bytes);
*this = arena_allocator_create();
}
static void *arena_allocator_alloc_impl(void *this, size_t bytes, const char *file, int line) {
return arena_allocator_alloc_func(
(arena_allocator_t*) this,
bytes,
file,
line
);
}
// TODO move to concrete implementation
static void *arena_allocator_resize_impl(void *this, void *old_ptr, size_t bytes, const char *file, int line) {
(void)this;
(void)file;
(void)line;
arena_allocator_t *t = (arena_allocator_t*) this;
if (
(size_t)t->bytes <= (size_t)old_ptr &&
(size_t)old_ptr < (size_t)t->bytes + dyn_array_length(t->bytes)
) {
uint8_t *old_buffer = old_ptr;
uint8_t *new_buffer = arena_allocator_alloc_func(this, bytes, file, line);
if (new_buffer == NULL) return NULL;
// TODO: is this off by 1?
size_t old_ptr_dist = (size_t)t->bytes + dyn_array_length(t->bytes) - (size_t)old_ptr;
size_t bytes_to_copy = bytes < old_ptr_dist ? bytes : old_ptr_dist;
for ( size_t i = 0; i < bytes_to_copy; i++ ) {
new_buffer[i] = old_buffer[i];
}
return new_buffer;
} else {
void *new_buffer = borrow_allocator_resize_func(&t->borrow_allocator, old_ptr, bytes, file, line);
if (new_buffer == NULL) return NULL;
// The bytes added to to_allocate is just an educated guess. If
// calling realloc, you are often allocating twice what you got from the
// last malloc/realloc call.
t->to_allocate += bytes/2;
return new_buffer;
}
return arena_allocator_alloc_func(
(arena_allocator_t*) this,
bytes,
file,
line
);
}
static void arena_allocator_free_impl(void *this, void *ptr, const char *file, int line) {
(void)this;
(void)file;
(void)line;
(void)ptr;
}
static const allocator_vtbl_t arena_allocator_vtbl = {
.alloc = arena_allocator_alloc_impl,
.resize = arena_allocator_resize_impl,
.free = arena_allocator_free_impl,
};
allocator_t arena_allocator_interface(arena_allocator_t *this) {
return (allocator_t) {
.this = this,
.vtbl = &arena_allocator_vtbl,
};
}
+12 -28
View File
@@ -4,11 +4,9 @@
#include <assert.h> #include <assert.h>
#include <stdbool.h> #include <stdbool.h>
void *borrow_allocator_alloc_func(borrow_allocator_t *this, size_t bytes, const char *file, int line) { void *borrow_allocator_alloc_func(borrow_allocator_t *this, size_t bytes) {
linked_allocation_node_t *node = malloc(sizeof(*this->head) + bytes); linked_allocation_node_t *node = malloc(sizeof(*this->head) + bytes);
if (node == NULL) { return NULL; } if (node == NULL) { return NULL; }
node->file = file;
node->line = line;
node->prev = NULL; node->prev = NULL;
node->next = this->head; node->next = this->head;
if (this->head != NULL) { if (this->head != NULL) {
@@ -19,12 +17,10 @@ void *borrow_allocator_alloc_func(borrow_allocator_t *this, size_t bytes, const
return &node->data; return &node->data;
} }
void *borrow_allocator_resize_func(borrow_allocator_t *this, void *old_ptr, size_t bytes, const char *file, int line) { void *borrow_allocator_resize_func(borrow_allocator_t *this, void *old_ptr, size_t bytes) {
linked_allocation_node_t *old_node = PTR_FROM_FIELD_PTR(linked_allocation_node_t, data, old_ptr); linked_allocation_node_t *old_node = PTR_FROM_FIELD_PTR(linked_allocation_node_t, data, old_ptr);
linked_allocation_node_t *new_node = realloc(old_node, sizeof(*old_node) + bytes); linked_allocation_node_t *new_node = realloc(old_node, sizeof(*old_node) + bytes);
if (new_node == NULL) { return NULL; } if (new_node == NULL) { return NULL; }
new_node->file = file;
new_node->line = line;
if (this->head == old_node) { if (this->head == old_node) {
assert(new_node->prev == NULL); assert(new_node->prev == NULL);
this->head = new_node; this->head = new_node;
@@ -68,39 +64,27 @@ size_t borrow_allocator_count_allocations(borrow_allocator_t *this) {
return output; return output;
} }
static void *borrow_alloc(void *this, size_t bytes, const char *file, int line) { static void *borrow_alloc_impl(void *this, size_t bytes) {
return borrow_allocator_alloc_func(this, bytes, file, line); return borrow_allocator_alloc_func(this, bytes);
} }
static void *borrow_resize(void *this, void *old_ptr, size_t bytes, const char *file, int line) { static void *borrow_resize_impl(void *this, void *old_ptr, size_t bytes) {
return borrow_allocator_resize_func(this, old_ptr, bytes, file, line); return borrow_allocator_resize_func(this, old_ptr, bytes);
} }
static void borrow_free(void *this, void *ptr, const char *file, int line) { static void borrow_reset(void *this) {
(void)file; borrow_allocator_free_all(this);
(void)line;
borrow_allocator_free(this, ptr);
} }
static const allocator_vtbl_t borrow_vtbl = { static const allocator_vtbl_t borrow_vtbl = {
.alloc = borrow_alloc, .alloc = borrow_alloc_impl,
.resize = borrow_resize, .resize = borrow_resize_impl,
.free = borrow_free, .reset = borrow_reset,
}; };
allocator_t borrow_allocator_interface(borrow_allocator_t *this) { allocator_t borrow_allocator(borrow_allocator_t *this) {
return (allocator_t) { return (allocator_t) {
.this=this, .this=this,
.vtbl=&borrow_vtbl, .vtbl=&borrow_vtbl,
}; };
} }
void borrow_allocator_assert_all_freed(borrow_allocator_t *this) {
bool should_crash = false;
for (linked_allocation_node_t *curr = this->head; curr != NULL; curr = curr->next) {
fprintf(stderr, "%s:%d: allocation not freed\n", curr->file, curr->line);
}
if (should_crash) {
exit(1);
}
}
+9 -16
View File
@@ -30,32 +30,25 @@ void buffer_allocator_reset(buffer_allocator_t *this) {
this->size = 0; this->size = 0;
} }
static void *buffer_alloc(void *this, size_t bytes, const char *file, int line) { static void *buffer_alloc_impl(void *this, size_t bytes) {
(void)file;
(void)line;
return buffer_allocator_alloc((buffer_allocator_t *)this, bytes); return buffer_allocator_alloc((buffer_allocator_t *)this, bytes);
} }
static void *buffer_resize(void *this, void *old_ptr, size_t bytes, const char *file, int line) { static void *buffer_resize_impl(void *this, void *old_ptr, size_t bytes) {
(void)file; return buffer_allocator_resize((buffer_allocator_t *)this, old_ptr, bytes);
(void)line;
return buffer_allocator_resize(this, old_ptr, bytes);
} }
static void buffer_free(void *this, void *ptr, const char *file, int line) { static void buffer_reset_impl(void *this) {
(void)this; buffer_allocator_reset((buffer_allocator_t *)this);
(void)ptr;
(void)file;
(void)line;
} }
static const allocator_vtbl_t buffer_vtbl = { static const allocator_vtbl_t buffer_vtbl = {
.alloc = buffer_alloc, .alloc = buffer_alloc_impl,
.resize = buffer_resize, .resize = buffer_resize_impl,
.free = buffer_free, .reset = buffer_reset_impl,
}; };
allocator_t buffer_allocator_interface(buffer_allocator_t *this) { allocator_t buffer_allocator(buffer_allocator_t *this) {
return (allocator_t) { return (allocator_t) {
.this=this, .this=this,
.vtbl=&buffer_vtbl, .vtbl=&buffer_vtbl,
+25 -98
View File
@@ -3,6 +3,7 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
typedef union any_align { char c; int i; long l; long long ll; float f; double d; void *p; } any_align_t; typedef union any_align { char c; int i; long l; long long ll; float f; double d; void *p; } any_align_t;
@@ -17,9 +18,9 @@ typedef union any_align { char c; int i; long l; long long ll; float f; double d
// Contains all operations an allocator can do. Similar interface to sdtlibs // Contains all operations an allocator can do. Similar interface to sdtlibs
// malloc, realloc and free. // malloc, realloc and free.
typedef struct allocator_vtbl { typedef struct allocator_vtbl {
void *(*alloc)(void *this, size_t bytes, const char *file, int line); void *(*alloc)(void *this, size_t bytes);
void *(*resize)(void *this, void *old_ptr, size_t bytes, const char *file, int line); void *(*resize)(void *this, void *old_ptr, size_t bytes);
void (*free)(void *this, void *ptr, const char *file, int line); void (*reset)(void *this);
} allocator_vtbl_t; } allocator_vtbl_t;
// An instance of an allocator. // An instance of an allocator.
@@ -32,13 +33,12 @@ typedef struct allocator {
void *allocator_alloc_func(allocator_t this, size_t bytes, const char *file, int line); void *allocator_alloc_func(allocator_t this, size_t bytes, const char *file, int line);
void *allocator_resize_func(allocator_t this, void *old_ptr, size_t bytes, const char *file, int line); void *allocator_resize_func(allocator_t this, void *old_ptr, size_t bytes, const char *file, int line);
void allocator_free_func(allocator_t this, void *ptr, const char *file, int line); void allocator_reset(allocator_t this);
#define allocator_alloc(this, bytes) allocator_alloc_func(this, bytes, __FILE__, __LINE__) #define allocator_alloc(this, bytes) allocator_alloc_func(this, bytes, __FILE__, __LINE__)
#define allocator_resize(this, old_ptr, bytes) allocator_resize_func(this, old_ptr, bytes, __FILE__, __LINE__) #define allocator_resize(this, old_ptr, bytes) allocator_resize_func(this, old_ptr, bytes, __FILE__, __LINE__)
#define allocator_free(this, ptr) allocator_free_func(this, ptr, __FILE__, __LINE__)
// std_allocator /////////////////////////////////////////////////////////////// // std_allocator ///////////////////////////////////////////////////////////////
allocator_t allocator_stdlib(); allocator_t forever_allocator();
// buffer_allocator //////////////////////////////////////////////////////////// // buffer_allocator ////////////////////////////////////////////////////////////
typedef struct buffer_allocator { typedef struct buffer_allocator {
size_t size, capacity; size_t size, capacity;
@@ -49,20 +49,12 @@ typedef struct buffer_allocator {
((buffer_allocator_t){ \ ((buffer_allocator_t){ \
.size = 0, .capacity = CAPACITY, .data = (uint8_t[CAPACITY]){0}}) .size = 0, .capacity = CAPACITY, .data = (uint8_t[CAPACITY]){0}})
allocator_t buffer_allocator_interface(buffer_allocator_t *this); allocator_t buffer_allocator(buffer_allocator_t *this);
void *buffer_allocator_alloc(buffer_allocator_t *this, size_t bytes);
// Very simple resize. Does not free any memory, nor does it keep track of the
// previously allocated size of the old_pointer, just copies over as few bytes
// as it can with the information it has to a new allocation.
void *buffer_allocator_resize(buffer_allocator_t *this, void *old_ptr, size_t bytes);
void buffer_allocator_reset(buffer_allocator_t *this);
// borrow_allocator //////////////////////////////////////////////////////////// // borrow_allocator ////////////////////////////////////////////////////////////
typedef struct linked_allocation_node { typedef struct linked_allocation_node {
struct linked_allocation_node *next; struct linked_allocation_node *next;
struct linked_allocation_node *prev; struct linked_allocation_node *prev;
const char *file;
int line;
any_align_t data[]; any_align_t data[];
} linked_allocation_node_t; } linked_allocation_node_t;
@@ -72,19 +64,7 @@ typedef struct borrow_allocator {
#define borrow_allocator_create() ((borrow_allocator_t){.head=NULL}) #define borrow_allocator_create() ((borrow_allocator_t){.head=NULL})
void *borrow_allocator_alloc_func(borrow_allocator_t *this, size_t bytes, const char *file, int line); allocator_t borrow_allocator(borrow_allocator_t *this);
#define borrow_allocator_alloc(this, bytes) borrow_allocator_alloc_func(this, bytes, __FILE__, __LINE__)
void *borrow_allocator_resize_func(borrow_allocator_t *this, void *old_ptr, size_t bytes, const char *file, int line);
#define borrow_allocator_resize(this, old_ptr, bytes) borrow_allocator_resize_func(this, old_ptr, bytes, __FILE__, __LINE__)
void borrow_allocator_free(borrow_allocator_t *this, void *old_ptr);
// Free all allocations done by this allocator.
void borrow_allocator_free_all(borrow_allocator_t *this);
size_t borrow_allocator_count_allocations(borrow_allocator_t *this);
// Check that all allocations have been freed, or print a list of files and
// lines where made that haven't been freed to stderr and then exit the program
// with 1 as the return value.
void borrow_allocator_assert_all_freed(borrow_allocator_t *this);
allocator_t borrow_allocator_interface(borrow_allocator_t *this);
// Some text that can be used as an identifier (no, not by you), so that I can // Some text that can be used as an identifier (no, not by you), so that I can
// use a variable that won't collide with yours inside macros. // use a variable that won't collide with yours inside macros.
@@ -96,77 +76,16 @@ allocator_t borrow_allocator_interface(borrow_allocator_t *this);
// with_borrow(foos_allocator) {bar = foo(foos_allocator, my_allocator); } Using // with_borrow(foos_allocator) {bar = foo(foos_allocator, my_allocator); } Using
// the return a keyword in the statement following this macro will cause a // the return a keyword in the statement following this macro will cause a
// guaranteed memory leak. // guaranteed memory leak.
// TODO: simplify!
#define with_borrow(NAME) \ #define with_borrow(NAME) \
for (allocator_t NAME = \ for (allocator_t NAME = \
borrow_allocator_interface(&borrow_allocator_create()); \ borrow_allocator(&borrow_allocator_create()); \
!((borrow_allocator_t *)NAME.this)->head; \ !((borrow_allocator_t *)NAME.this)->head; \
((borrow_allocator_t *)NAME.this)->head = \ ((borrow_allocator_t *)NAME.this)->head = \
(borrow_allocator_free_all(((borrow_allocator_t *)NAME.this)), \ (allocator_reset(NAME), \
(linked_allocation_node_t *)1)) \ (linked_allocation_node_t *)1)) \
for (int UNIQUE = 0; UNIQUE < 1; UNIQUE++) for (int UNIQUE = 0; UNIQUE < 1; UNIQUE++)
// arena allocators // BUGGY! //////////////////////////////////////////////////
// The idea of this allocator is to allocate all required memory in a single
// chunk. But it does not require you to know how much memory you need
// beforehand. Instead the number of bytes allocated will be recorded and
// updated, so that the whole thing can be reallocated as a single chunk that is
// as large as it needs to be.
//
// This basically exists for allocations that live for a single 'tick' of an
// application.
// It is intended to eventually stabilize and find the required size of the
// memory chunk that is needed for a single frame.
typedef struct arena_allocator {
borrow_allocator_t borrow_allocator;
// Set to 0 when instantiating. Will be updated as the borrow allocator is
// used. Is used on reset to allocate a continous chunk of memory.
size_t to_allocate;
size_t i;
uint8_t *bytes;
} arena_allocator_t;
#define arena_allocator_create() \
(arena_allocator_t) { \
.borrow_allocator = borrow_allocator_create(), .to_allocate = 0, .i = 0, \
.bytes = NULL \
}
void *arena_allocator_alloc_func(
arena_allocator_t *this,
size_t bytes,
const char *file,
int line
);
#define arena_allocator_alloc(THIS, BYTES) \
arena_allocator_alloc_func(THIS, BYTES, __FILE__, __LINE__)
void arena_allocator_reset_func(arena_allocator_t *this, const char *file, int line);
#define arena_allocator_reset(THIS) \
arena_allocator_reset_func(THIS, __FILE__, __LINE__)
void *arena_allocator_resize_func(
arena_allocator_t *this,
void *old_ptr,
size_t bytes,
const char *file,
int line
);
#define arena_allocator_resize(THIS, OLD_PTR, BYTES) \
arena_allocator_resize_func(THIS, OLD_PTR, BYTES, __FILE__, __LINE__)
void arena_allocator_destroy(arena_allocator_t *this);
allocator_t arena_allocator_interface(arena_allocator_t *this);
// TODO: make the reset happen at the start, not the end. That allows the user
// to specify some starting size of the buffer.
// You cannot use return within this block.
#define with_arena(ARENA, ALLOCATOR_NAME) \
for (allocator_t ALLOCATOR_NAME = arena_allocator_interface(ARENA); \
ALLOCATOR_NAME.vtbl != NULL; \
ALLOCATOR_NAME.vtbl = \
(arena_allocator_reset_func(ARENA, __FILE__, __LINE__), NULL)) \
for (int UNIQUE = 0; UNIQUE < 1; UNIQUE++)
// dynamic arrays ////////////////////////////////////////////////////////////// // dynamic arrays //////////////////////////////////////////////////////////////
typedef struct dyn_array_header { typedef struct dyn_array_header {
@@ -233,7 +152,6 @@ void *dyn_array_grow_non_crashing_func(void *this, size_t n_new_items, const cha
size_t dyn_array_length(void *this); size_t dyn_array_length(void *this);
size_t dyn_array_capacity(void *this); size_t dyn_array_capacity(void *this);
void dyn_array_destroy(void *this);
#define dyn_array_append(THIS, VAL) do { \ #define dyn_array_append(THIS, VAL) do { \
THIS = dyn_array_grow(THIS, 1); \ THIS = dyn_array_grow(THIS, 1); \
@@ -245,20 +163,29 @@ void dyn_array_destroy(void *this);
#ifdef CIG_IMPL #ifdef CIG_IMPL
void *allocator_alloc_func(allocator_t this, size_t bytes, const char *file, int line) { void *allocator_alloc_func(allocator_t this, size_t bytes, const char *file, int line) {
return this.vtbl->alloc(this.this, bytes, file, line); void *ptr = this.vtbl->alloc(this.this, bytes);
if (ptr == NULL) {
fprintf(stderr, "%s:%d: alloc returned NULL\n", file, line);
exit(1);
}
return ptr;
} }
void *allocator_resize_func(allocator_t this, void *old_ptr, size_t bytes, const char *file, int line) { void *allocator_resize_func(allocator_t this, void *old_ptr, size_t bytes, const char *file, int line) {
return this.vtbl->resize(this.this, old_ptr, bytes, file, line); void *ptr = this.vtbl->resize(this.this, old_ptr, bytes);
if (ptr == NULL) {
fprintf(stderr, "%s:%d: alloc returned NULL\n", file, line);
exit(1);
}
return ptr;
} }
void allocator_free_func(allocator_t this, void *ptr, const char *file, int line) { void allocator_reset(allocator_t this) {
this.vtbl->free(this.this, ptr, file, line); this.vtbl->reset(this.this);
} }
#include "std_allocator.c" #include "std_allocator.c"
#include "buffer_allocator.c" #include "buffer_allocator.c"
#include "borrow_allocator.c" #include "borrow_allocator.c"
#include "dyn_array.c" #include "dyn_array.c"
#include "arena_allocator.c"
#endif // CIG_IMPL #endif // CIG_IMPL
#endif // CIG_H #endif // CIG_H
-26
View File
@@ -11,29 +11,11 @@ void *dyn_array_create_func(dyn_array_create_func_args_t args) {
.line=args.line .line=args.line
} }
); );
if (bytes == NULL) {
fprintf(
stderr,
"%s:%d: allocator returned NULL\n",
args.file,
args.line
);
exit(1);
}
return bytes; return bytes;
} }
void *dyn_array_grow_func(void *this, size_t n_new_items, const char *file, int line) { void *dyn_array_grow_func(void *this, size_t n_new_items, const char *file, int line) {
void *bytes = dyn_array_grow_non_crashing_func(this, n_new_items, file, line); void *bytes = dyn_array_grow_non_crashing_func(this, n_new_items, file, line);
if (bytes == NULL) {
fprintf(
stderr,
"%s:%d: allocator returned NULL\n",
file,
line
);
exit(1);
}
return bytes; return bytes;
} }
@@ -44,7 +26,6 @@ void *dyn_array_create_non_crashing_func(dyn_array_create_non_crashing_func_args
args.file, args.file,
args.line args.line
); );
if (header == NULL) { return NULL; }
header->size = 0; header->size = 0;
header->capacity = args.initial_capacity; header->capacity = args.initial_capacity;
header->itemsize = args.itemsize; header->itemsize = args.itemsize;
@@ -69,7 +50,6 @@ void *dyn_array_grow_non_crashing_func(void *this, size_t n_new_items, const cha
file, file,
line line
); );
if (header == NULL) { return NULL; }
header->capacity = new_capacity; header->capacity = new_capacity;
} }
header->size = new_size; header->size = new_size;
@@ -102,9 +82,3 @@ size_t dyn_array_capacity(void *this) {
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this); dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
return header->capacity; return header->capacity;
} }
void dyn_array_destroy(void *this) {
if (this == NULL) { return; }
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
allocator_free(header->allocator, header);
}
+9 -16
View File
@@ -1,34 +1,27 @@
#include "cig.h" #include "cig.h"
static void *stdlib_alloc(void *this, size_t bytes, const char *file, int line) { static void *forever_alloc(void *this, size_t bytes) {
(void)this; (void)this;
(void)file;
(void)line;
return malloc(bytes); return malloc(bytes);
} }
static void *stdlib_resize(void *this, void *old_ptr, size_t bytes, const char *file, int line) { static void *forever_resize(void *this, void *old_ptr, size_t bytes) {
(void)this; (void)this;
(void)file;
(void)line;
return realloc(old_ptr, bytes); return realloc(old_ptr, bytes);
} }
static void stdlib_free(void *this, void *ptr, const char *file, int line) { static void forever_no_op(void *this) {
(void)this; (void)this;
(void)file;
(void)line;
free(ptr);
} }
static const allocator_vtbl_t stdlib_vtbl = { static const allocator_vtbl_t forever_vtbl = {
.alloc = stdlib_alloc, .alloc = forever_alloc,
.resize = stdlib_resize, .resize = forever_resize,
.free = stdlib_free, .reset = forever_no_op,
}; };
allocator_t allocator_stdlib() { allocator_t forever_allocator() {
return (allocator_t) { return (allocator_t) {
.this=NULL, .this=NULL,
.vtbl=&stdlib_vtbl, .vtbl=&forever_vtbl,
}; };
} }
-41
View File
@@ -1,41 +0,0 @@
#include <criterion/criterion.h>
#include "cig.h"
#include <stdio.h>
Test(arena_allocator, repeated_allocations) {
arena_allocator_t aalloc = arena_allocator_create();
for ( int i = 0; i < 10; i++ ) with_arena(&aalloc, allocator) {
allocator_alloc(allocator, 10);
}
cr_assert_eq(aalloc.to_allocate, 10);
cr_assert_eq(borrow_allocator_count_allocations(&aalloc.borrow_allocator), 0);
cr_assert_eq(dyn_array_capacity(aalloc.bytes), 10);
arena_allocator_destroy(&aalloc);
cr_assert_eq(aalloc.to_allocate, 0);
}
Test(arena_allocator, alignment) {
arena_allocator_t aalloc = arena_allocator_create();
for ( int i = 0; i < 10; i++ ) with_arena(&aalloc, allocator) {
allocator_alloc(allocator, 1000);
}
with_arena(&aalloc, allocator) {
size_t prev_addr = 0;
prev_addr = ~prev_addr;
for (int i = 0; i < 1001; i++) {
size_t addr = (size_t)allocator_alloc(allocator, 1);
cr_assert_neq(addr, prev_addr);
cr_assert_eq(addr % MAX_ALIGN, 0);
}
}
arena_allocator_reset(&aalloc);
fprintf(stderr, "%zu\n", aalloc.to_allocate);
cr_assert_eq(aalloc.to_allocate, MAX_ALIGN * 1000 + 1);
}
// TODO somehow test reallocations
+3 -6
View File
@@ -3,14 +3,11 @@
#define EXPECT 5 #define EXPECT 5
Test(borrow_allocator, test) { Test(borrow_allocator, test) {
borrow_allocator_t balloc = borrow_allocator_create(); allocator_t balloc = borrow_allocator(&borrow_allocator_create());
int *ptr = borrow_allocator_alloc(&balloc, sizeof(int)); int *ptr = allocator_alloc(balloc, sizeof(int));
*ptr = EXPECT; *ptr = EXPECT;
cr_assert_eq(*ptr, EXPECT); cr_assert_eq(*ptr, EXPECT);
cr_assert_eq(1, borrow_allocator_count_allocations(&balloc)); allocator_reset(balloc);
borrow_allocator_free_all(&balloc);
cr_assert_eq(0, borrow_allocator_count_allocations(&balloc));
borrow_allocator_assert_all_freed(&balloc);
} }
#define IS_SIZE 900 #define IS_SIZE 900
+2 -7
View File
@@ -2,7 +2,7 @@
#include "cig.h" #include "cig.h"
static void test_buffer_alloc(buffer_allocator_t impl) { static void test_buffer_alloc(buffer_allocator_t impl) {
allocator_t inter = buffer_allocator_interface(&impl); allocator_t inter = buffer_allocator(&impl);
const int n_ints = 100; const int n_ints = 100;
int *ints = allocator_alloc(inter, sizeof(int) * n_ints); int *ints = allocator_alloc(inter, sizeof(int) * n_ints);
for (int i = 0; i < n_ints; i++) { for (int i = 0; i < n_ints; i++) {
@@ -20,15 +20,10 @@ static void test_buffer_alloc(buffer_allocator_t impl) {
cr_assert_eq(ints[i], i); cr_assert_eq(ints[i], i);
} }
cr_assert_eq(impl.size, sizeof(int)*2*n_ints); cr_assert_eq(impl.size, sizeof(int)*2*n_ints);
allocator_free(inter, ints);
size_t remaining_bytes = impl.capacity - impl.size;
size_t double_remaining_bytes = 2 * remaining_bytes;
void *should_be_null = allocator_resize(inter, ints, double_remaining_bytes);
cr_assert_eq(should_be_null, NULL);
size_t double_ints = ((size_t) n_ints) * 2; size_t double_ints = ((size_t) n_ints) * 2;
void *should_be_addr = allocator_resize(inter, ints, double_ints); void *should_be_addr = allocator_resize(inter, ints, double_ints);
cr_assert_neq(should_be_addr, NULL); cr_assert_neq(should_be_addr, NULL);
buffer_allocator_reset(&impl); allocator_reset(inter);
} }
Test(buffer_allocator, test) { Test(buffer_allocator, test) {
-1
View File
@@ -48,6 +48,5 @@ Test(dynamic_arrays, pop) {
int num = dyn_array_pop(numbers); int num = dyn_array_pop(numbers);
cr_assert_eq(num, 50-i); cr_assert_eq(num, 50-i);
} }
dyn_array_destroy(numbers);
} }
} }
+2 -4
View File
@@ -1,14 +1,12 @@
#include <criterion/criterion.h> #include <criterion/criterion.h>
#include "cig.h" #include "cig.h"
// TODO rename std to forever allocator
Test(std_allocator, test) { Test(std_allocator, test) {
allocator_t this = allocator_stdlib(); allocator_t this = forever_allocator();
void *ptr = allocator_alloc(this, 10); void *ptr = allocator_alloc(this, 10);
cr_assert(ptr != ((void *)0), "non null from malloc"); cr_assert(ptr != ((void *)0), "non null from malloc");
void *new_ptr = allocator_resize(this, ptr, 1024*1024*500); void *new_ptr = allocator_resize(this, ptr, 1024*1024*500);
cr_assert(new_ptr != ((void *)0), "non null from realloc"); cr_assert(new_ptr != ((void *)0), "non null from realloc");
cr_assert(new_ptr != ptr, "realloc is not the same ptr as malloc"); cr_assert(new_ptr != ptr, "realloc is not the same ptr as malloc");
allocator_free(this, new_ptr);
} }