/* * No free per allocation; freearena releases the whole chain. * Aligned to 16 so structs with 8-byte fields and doubles are happy. * The chunk size doubles up to a cap so we don't fragment on huge * inputs. */ #include "ww.h" #include #include #define ALIGN 16 #define INIT_CHUNK (64 * 1024) #define MAX_CHUNK (4 * 1024 * 1024) static u64 roundup(u64 n, u64 a) { return (n + a - 1) & ~(a - 1); } Arena * newarena(void) { Arena *a = calloc(1, sizeof *a); if (a == NULL) fatal("newarena: out of memory"); a->buf = malloc(INIT_CHUNK); if (a->buf == NULL) fatal("newarena: out of memory"); a->cap = INIT_CHUNK; return a; } static void grow(Arena *a, u64 need) { u64 ncap = a->cap * 2; if (ncap > MAX_CHUNK) ncap = MAX_CHUNK; if (ncap < need) ncap = roundup(need, ALIGN); Arena *old = malloc(sizeof *old); if (old == NULL) fatal("arena: oom"); *old = *a; a->next = old; a->buf = malloc(ncap); if (a->buf == NULL) fatal("arena: oom (chunk=%llu)", (unsigned long long)ncap); a->off = 0; a->cap = ncap; } void * amalloc(Arena *a, u64 n) { n = roundup(n, ALIGN); if (n > a->cap - a->off) grow(a, n); void *p = a->buf + a->off; a->off += n; a->total += n; memset(p, 0, n); return p; } char * astrdup(Arena *a, const char *s) { u64 n = strlen(s); char *p = amalloc(a, n + 1); memcpy(p, s, n); return p; } char * astrndup(Arena *a, const char *s, u64 n) { char *p = amalloc(a, n + 1); memcpy(p, s, n); return p; } char * aprintf(Arena *a, const char *fmt, ...) { va_list ap; va_start(ap, fmt); int n = vsnprintf(NULL, 0, fmt, ap); va_end(ap); if (n < 0) fatal("aprintf: vsnprintf failed"); char *p = amalloc(a, (u64)n + 1); va_start(ap, fmt); vsnprintf(p, (size_t)n + 1, fmt, ap); va_end(ap); return p; } void freearena(Arena *a) { while (a) { Arena *next = a->next; free(a->buf); /* The head Arena was returned by newarena() and is the only * one we should free as a struct; the linked older ones were * allocated by grow() and are also freeable. */ free(a); a = next; } }