Fix leaks, curious case of gebs blog post

This commit is contained in:
kamkow1
2025-06-18 10:38:26 +02:00
parent e9a95c2c54
commit ed1d61d976
6 changed files with 78 additions and 3 deletions

View File

@ -0,0 +1,60 @@
# GEBS - the Good Enough Build System
GEBS is a reiteration of my previous build system "MIBS" (or MIni Build System). It takes some inspiration from
[Tsoding's](https://twitch.tv/tsoding) [nobuild](https://github.com/tsoding/nobuild) and later on [nob.h](https://github.com/tsoding/nob.h).
The key difference is the way GEBS is implemented on the inside, which makes it more powerful and extensible than nob.h.
GEBS also includes a bunch of extra helper macros, which turn C into a language more akin to Go or Zig, but more on that later.
## So what makes GEBS different?
### Allocators
So one thing I've noticed is that nob.h is used alongside of [arena](https://github.com/tsoding/arena). If you look into the implementation
you can see some things, which are somewhat redundant like \`arena_sprintf()\` or \`arena_da_append()\`, \`arena_sb_append_cstr()\` and so on...
First of all, why is an arena library managing string builders and dynamic arrays? In my opinion it should be the other way around.
A string builder should rather accept a generic allocator interface, which it can then utilize to get it's memory. In GEBS this is done
via a \`Gebs_Allocator\` interface.
\`\`\`c
typedef struct {
void *(*malloc)(void *self, size_t size);
void (*free)(void *self, void *memory);
void *(*realloc)(void *self, void *memory, size_t prev_size, size_t new_size);
} Gebs_Allocator;
// Wrapper macros
#define gebs_malloc(alloc, size) ((alloc)->malloc((void *)(alloc), (size)))
#define gebs_free(alloc, memory) ((alloc)->free((void *)(alloc), (memory)))
#define gebs_realloc(alloc, memory, prev_size, new_size) \
((alloc)->realloc((void *)(alloc), (memory), (prev_size), (new_size)))
\`\`\`
We then can implement an allocator that conforms to this interface and it will work with any dynamic structure.
This is my version of the \`XXX_da_append()\` macro:
\`\`\`c
#define gebs_list_append_alloc(alloc, list, item) \\
do { \\
if ((list)->items == nil) { \\
(list)->capacity = 1; \\
(list)->items = gebs_malloc((alloc), \\
sizeof(*(list)->items) * (list)->capacity); \\
} else { \\
if ((list)->count == (list)->capacity) { \\
size_t __prev_capacity = (list)->capacity; \\
(list)->capacity *= 2; \\
(list)->items = gebs_realloc((alloc), (list)->items, \\
sizeof(*(list)->items) * __prev_capacity, \\
sizeof(*(list)->items) * (list)->capacity); \\
} \\
} \\
(list)->items[(list)->count++] = (item); \\
} while(0)
#define gebs_list_append(list, item) \\
gebs_list_append_alloc(&gebs_default_allocator, (list), (item))
\`\`\`
This way a dynamic list can work with any kind of allocator - the default libc allocator, an arena or literally anything else.
We're not tied to the libc allocator and then have to implement the same macro of all other allocators.