Created
December 28, 2025 01:19
-
-
Save skeeto/e2c751b66f96841df6281a1c6b3496bb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Ref: https://old.reddit.com/r/C_Programming/comments/1pxat1v | |
| #include <assert.h> | |
| #include <stdarg.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #define new(a, n, t) (t *)alloc(a, n, sizeof(t), _Alignof(t)) | |
| typedef struct Dtor Dtor; | |
| struct Dtor { | |
| Dtor *next; | |
| void *object; | |
| void (*dtor)(void *); | |
| }; | |
| typedef struct { | |
| char *beg; | |
| char *end; | |
| Dtor *dtors; | |
| } Arena; | |
| char *alloc(Arena *a, ptrdiff_t count, int size, int align) | |
| { | |
| ptrdiff_t pad = (ptrdiff_t)-(size_t)a->beg & (align - 1); | |
| assert(count < (a->end - a->beg - pad)/size); // TODO: oom policy | |
| char *r = a->beg + pad; | |
| a->beg += pad + count*size; | |
| return memset(r, 0, (size_t)(count*size)); | |
| } | |
| // Register a destructor. If using longjmp on OOM, registration should | |
| // occur before non-memory resources are allocated, and the destructor | |
| // should tolerate objects do not referencing a non-memory resource. | |
| void register_dtor(Arena *a, void *object, void (*dtor)(void *)) | |
| { | |
| Dtor *node = new(a, 1, Dtor); | |
| node->dtor = dtor; | |
| node->next = a->dtors; | |
| node->object = object; | |
| a->dtors = node; | |
| } | |
| // Destroy non-trivial objects allocated in this arena. | |
| void destroy(Arena *a) | |
| { | |
| for (Dtor *dtors = a->dtors; dtors; dtors = dtors->next) { | |
| dtors->dtor(dtors->object); | |
| } | |
| } | |
| // Create a scratch arena with an empty dtor list. The destroy() | |
| // function will not destroy objects from before this checkpoint. | |
| Arena make_scratch(Arena *a) | |
| { | |
| Arena r = *a; | |
| r.dtors = 0; | |
| return r; | |
| } | |
| typedef struct { | |
| char *name; | |
| } Thing; | |
| void destroy_thing(void *p) | |
| { | |
| Thing *t = p; | |
| if (t->name) { | |
| printf("destroy Thing(%s)\n", t->name); | |
| } | |
| } | |
| char *aprintf(Arena *a, char *fmt, ...) | |
| { | |
| va_list ap; | |
| va_start(ap, fmt); | |
| int len = vsnprintf(0, 0, fmt, ap); | |
| va_end(ap); | |
| if (len<0 || len==PTRDIFF_MAX) return 0; | |
| ptrdiff_t cap = (ptrdiff_t)len + 1; | |
| char *r = new(a, cap, char); | |
| va_start(ap, fmt); | |
| vsnprintf(r, (size_t)cap, fmt, ap); | |
| va_end(ap); | |
| return r; | |
| } | |
| Thing *make_thing(Arena *a, int id) | |
| { | |
| Thing *thing = new(a, 1, Thing); | |
| thing->name = aprintf(a, "thing-%d", id); | |
| register_dtor(a, thing, destroy_thing); | |
| return thing; | |
| } | |
| typedef struct { | |
| Thing **data; | |
| ptrdiff_t len; | |
| } Things; | |
| Things make_things(Arena *a, int n) | |
| { | |
| Things things = {}; | |
| things.len = n; | |
| things.data = new(a, n, Thing *); | |
| for (int i = 0; i < n; i++) { | |
| things.data[i] = make_thing(a, i); | |
| } | |
| return things; | |
| } | |
| int main() | |
| { | |
| static char mem[1<<21]; | |
| Arena a = {mem, mem+sizeof(mem)}; | |
| { | |
| Arena scratch = make_scratch(&a); | |
| _Defer { destroy(&scratch); }; | |
| Things things = make_things(&scratch, 3); | |
| (void)things; | |
| // ... | |
| } | |
| { | |
| Arena scratch = make_scratch(&a); | |
| _Defer { destroy(&scratch); }; | |
| Things things = make_things(&scratch, 5); | |
| (void)things; | |
| // ... | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment