From ed1d61d976d82ba30a9c5bb59fd36b399a45c5bc Mon Sep 17 00:00:00 2001 From: kamkow1 Date: Wed, 18 Jun 2025 10:38:26 +0200 Subject: [PATCH] Fix leaks, curious case of gebs blog post --- baked.c | 2 ++ baked.h | 1 + blog/curious-case-of-gebs.md | 60 ++++++++++++++++++++++++++++++++++++ build.c | 3 +- gebs | 2 +- main.c | 13 +++++++- 6 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 blog/curious-case-of-gebs.md diff --git a/baked.c b/baked.c index 15ac836..28361ef 100644 --- a/baked.c +++ b/baked.c @@ -19,6 +19,7 @@ INCBIN(hotreload_js, "./etc/hotreload.js"); INCBIN(blog_welcome_md, "./blog/welcome.md"); INCBIN(blog_weird_page_md, "./blog/weird-page.md"); +INCBIN(blog_curious_case_of_gebs_md, "./blog/curious-case-of-gebs.md"); Baked_Resource *baked_resources = NULL; @@ -45,6 +46,7 @@ void init_baked_resources(void) add_baked_resource("hotreload.js", hotreload_js_data, hotreload_js_size); add_baked_resource("blog-welcome.md", blog_welcome_md_data, blog_welcome_md_size); add_baked_resource("blog-weird-page.md", blog_weird_page_md_data, blog_weird_page_md_size); + add_baked_resource("blog-curious-case-of-gebs.md", blog_curious_case_of_gebs_md_data, blog_curious_case_of_gebs_md_size); } void free_baked_resources(void) diff --git a/baked.h b/baked.h index c00513c..fb19af3 100644 --- a/baked.h +++ b/baked.h @@ -16,6 +16,7 @@ INCBIN_EXTERN(hotreload_js); INCBIN_EXTERN(blog_welcome_md); INCBIN_EXTERN(blog_weird_page_md); +INCBIN_EXTERN(blog_curious_case_of_gebs_md); typedef struct { char *key; // path diff --git a/blog/curious-case-of-gebs.md b/blog/curious-case-of-gebs.md new file mode 100644 index 0000000..4f7895a --- /dev/null +++ b/blog/curious-case-of-gebs.md @@ -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. + diff --git a/build.c b/build.c index b84254e..e6a5d3a 100644 --- a/build.c +++ b/build.c @@ -36,7 +36,8 @@ int main(int argc, char ** argv) "./etc/simple.min.css", "./blog/welcome.md", - "./blog/weird-page.md" + "./blog/weird-page.md", + "./blog/curious-case-of-gebs.md" ) { RULE("./mongoose.o", "./mongoose/mongoose.c") { diff --git a/gebs b/gebs index c3be4ce..6db3611 160000 --- a/gebs +++ b/gebs @@ -1 +1 @@ -Subproject commit c3be4ce8ee4632b12db6bccf8b22ec68d4f64773 +Subproject commit 6db36114d55224846c0a7516dc70a4306874c400 diff --git a/main.c b/main.c index c36217b..8cedc23 100644 --- a/main.c +++ b/main.c @@ -108,6 +108,17 @@ void init_route_hashtable(void) baked_resource_each(&put_blogs); } +void free_route_hashtable(void) +{ + for (size_t i = 0; i < shlen(route_hashtable); i++) { + if (strlen(route_hashtable[i].key) >= strlen("blog-") + && strncmp(route_hashtable[i].key, "blog-", strlen("blog-")) == 0) { + free(route_hashtable[i].key); + } + } + shfree(route_hashtable); +} + int main(int argc, char ** argv) { start_timer(); @@ -118,6 +129,7 @@ int main(int argc, char ** argv) struct mg_mgr mgr; mg_mgr_init(&mgr); init_route_hashtable(); + defer { free_route_hashtable(); } mg_wakeup_init(&mgr); mg_http_listen(&mgr, "http://localhost:8080", &event_handler, NULL); @@ -128,7 +140,6 @@ int main(int argc, char ** argv) } mg_mgr_free(&mgr); - shfree(route_hashtable); free_baked_resources();