From a20833f4ee0599a34e044e5ec5c228f919cb45fb Mon Sep 17 00:00:00 2001 From: Ivar Fatland Date: Wed, 8 Oct 2025 22:16:31 +0200 Subject: [PATCH] borrow allocator implementation --- allocator.h | 18 +++++-- borrow_allocator.c | 101 ++++++++++++++++++++++++++++++++++++---- test_borrow_allocator.c | 14 ++++++ 3 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 test_borrow_allocator.c diff --git a/allocator.h b/allocator.h index d72637d..cfa84ad 100644 --- a/allocator.h +++ b/allocator.h @@ -61,6 +61,8 @@ void buffer_allocator_reset(buffer_allocator_t *this); typedef struct linked_allocation_node { struct linked_allocation_node *next; struct linked_allocation_node *prev; + const char *file; + int line; any_align_t data[]; } linked_allocation_node_t; @@ -70,9 +72,19 @@ typedef struct borrow_allocator { #define borrow_allocator_create() ((borrow_allocator_t){.head=NULL}) -void *borrow_allcoator_alloc(borrow_allocator_t *this, size_t bytes); -void *borrow_allcoator_resize(borrow_allocator_t *this, void *old_ptr, size_t bytes); -void borrow_allcoator_free(borrow_allocator_t *this, void *old_ptr); +void *borrow_allocator_alloc_func(borrow_allocator_t *this, size_t bytes, const char *file, int line); +#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_reset(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); #ifdef ALLOCATOR_IMPLEMENTATION diff --git a/borrow_allocator.c b/borrow_allocator.c index affd88a..297fb40 100644 --- a/borrow_allocator.c +++ b/borrow_allocator.c @@ -1,25 +1,106 @@ #include "allocator.h" +#include +#include +#include +#include -void *borrow_allcoator_alloc(borrow_allocator_t *this, size_t bytes) { +void *borrow_allocator_alloc_func(borrow_allocator_t *this, size_t bytes, const char *file, int line) { linked_allocation_node_t *node = malloc(sizeof(*this->head) + bytes); if (node == NULL) { return NULL; } + node->file = file; + node->line = line; node->prev = NULL; node->next = this->head; if (this->head != NULL) { + assert(this->head->prev == NULL); this->head->prev = node; } this->head = node; + return &node->data; } -// WARNING: it is important to consider the case where the old_node is the head -// node. +void *borrow_allocator_resize_func(borrow_allocator_t *this, void *old_ptr, size_t bytes, const char *file, int line) { + 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); + if (new_node == NULL) { return NULL; } + new_node->file = file; + new_node->line = line; + if (this->head == old_node) { + assert(new_node->prev == NULL); + this->head = new_node; + } + if (new_node->prev != NULL) { new_node->prev->next = new_node; } + if (new_node->next != NULL) { new_node->next->prev = new_node; } + return &new_node->data; +} -void *borrow_allcoator_resize(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); - // TODO: update the links +void borrow_allocator_free(borrow_allocator_t *this, void *old_ptr) { + linked_allocation_node_t *node = PTR_FROM_FIELD_PTR(linked_allocation_node_t, data, old_ptr); + if (node->prev != NULL) { node->prev->next = node->next; } + if (node->next != NULL) { node->next->prev = node->prev; } + if (this->head == node) { + assert(node->prev == NULL); + if (node->next != NULL) { assert(node->next->prev == NULL); } + this->head = node->next; + } + free(node); } -void borrow_allcoator_free(borrow_allocator_t *this, void *old_ptr) { - linked_allocation_node_t *old_node = PTR_FROM_FIELD_PTR(linked_allocation_node_t, data, old_ptr); - // TODO: update the links - free(old_node); + +void borrow_allocator_reset(borrow_allocator_t *this) { + if (this->head == NULL) { + return; + } + assert(this->head->prev == NULL); + void *prev = NULL; + for (linked_allocation_node_t *node = this->head; node != NULL; node = node->next) { + free(prev); + prev = node; + } + free(prev); + this->head = NULL; +} + +size_t borrow_allocator_count_allocations(borrow_allocator_t *this) { + size_t output = 0; + for (linked_allocation_node_t *node = this->head; node != NULL; node = node->next) { + output++; + } + return output; +} + +static void *borrow_alloc(void *this, size_t bytes, const char *file, int line) { + return borrow_allocator_alloc_func(this, bytes, file, line); +} + +static void *borrow_resize(void *this, void *old_ptr, size_t bytes, const char *file, int line) { + return borrow_allocator_resize_func(this, old_ptr, bytes, file, line); +} + +static void borrow_free(void *this, void *ptr, const char *file, int line) { + (void)file; + (void)line; + borrow_allocator_free(this, ptr); +} + +static const allocator_vtbl_t borrow_vtbl = { + .alloc = borrow_alloc, + .resize = borrow_resize, + .free = borrow_free, +}; + +allocator_t borrow_allocator_interface(borrow_allocator_t *this) { + return (allocator_t) { + .this=this, + .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); + } } diff --git a/test_borrow_allocator.c b/test_borrow_allocator.c new file mode 100644 index 0000000..e04e973 --- /dev/null +++ b/test_borrow_allocator.c @@ -0,0 +1,14 @@ +#include +#include "allocator.h" + +#define EXPECT 5 +Test(borrow_allocator, test) { + borrow_allocator_t balloc = borrow_allocator_create(); + int *ptr = borrow_allocator_alloc(&balloc, sizeof(int)); + *ptr = EXPECT; + cr_assert_eq(*ptr, EXPECT); + cr_assert_eq(1, borrow_allocator_count_allocations(&balloc)); + borrow_allocator_reset(&balloc); + cr_assert_eq(0, borrow_allocator_count_allocations(&balloc)); + borrow_allocator_assert_all_freed(&balloc); +}