Skip to content

Instantly share code, notes, and snippets.

@skeeto
Created December 28, 2025 01:19
Show Gist options
  • Select an option

  • Save skeeto/e2c751b66f96841df6281a1c6b3496bb to your computer and use it in GitHub Desktop.

Select an option

Save skeeto/e2c751b66f96841df6281a1c6b3496bb to your computer and use it in GitHub Desktop.
// 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