#ifndef ZONE_H #define ZONE_H #include #ifndef ZONE_MIN_CAPACITY #define ZONE_MIN_CAPACITY 1024 #endif #define ZONE_USE_MALLOC 0 #define ZONE_USE_MMAP 1 typedef struct ZoneFrame ZoneFrame; struct ZoneFrame { ZoneFrame *prev, *next; size_t length, capacity; uintptr_t data[]; }; typedef struct { ZoneFrame *tail, *here; } Zone; typedef struct { ZoneFrame *zf; size_t length; } ZoneState; void zn_free(Zone *z); void zn_reset(Zone *z); void zn_save(Zone *z, ZoneState *m); void zn_load(Zone *z, ZoneState *m); void *zn_alloc(Zone *z, size_t n); void *zn_zf_alloc(Zone *z, ZoneFrame **zf, size_t n); void *zn_zf_realloc(Zone *z, ZoneFrame **zf, void *ptr, size_t oldsz, size_t newsz); char *zn_strdup(Zone *z, const char *s); #ifdef ZONE_IMPL #include #include #include #include "zone.h" #ifndef ZONE_BACKEND #ifdef __unix__ #define ZONE_BACKEND ZONE_USE_MMAP #else #define ZONE_BACKEND ZONE_USE_MALLOC #endif #endif #if ZONE_BACKEND == ZONE_USE_MMAP #include #include static ZoneFrame *zn_zf_allocate(size_t capacity) { ZoneFrame *zf = mmap(NULL, sizeof *zf + capacity * sizeof(uintptr_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (!zf) { fprintf(stderr, "failed to allocate memory zone frame\n"); abort(); } return zf; } static void zn_zf_free(ZoneFrame *zf) { munmap(zf, sizeof *zf + zf->capacity * sizeof(uintptr_t)); } static size_t zn_zf_capacity(size_t cap) { if (cap < ZONE_MIN_CAPACITY) cap = ZONE_MIN_CAPACITY; size_t pg_size = sysconf(_SC_PAGE_SIZE) / sizeof(uintptr_t); size_t hdr_size = sizeof(ZoneFrame) / sizeof(uintptr_t); return cap + (-cap & (pg_size - 1)) - hdr_size; } #elif ZONE_BACKEND == ZONE_USE_MALLOC static ZoneFrame *zn_zf_allocate(size_t capacity) { ZoneFrame *zf = malloc(sizeof *zf + capacity * sizeof(uintptr_t)); if (!zf) { fprintf(stderr, "failed to allocate memory zone frame\n"); abort(); } return zf; } static void zn_zf_free(ZoneFrame *zf) { free(zf); } static size_t zn_zf_capacity(size_t cap) { if (cap < ZONE_MIN_CAPACITY) cap = ZONE_MIN_CAPACITY; return cap; } #else #error "unknown or unsupported zone backend" #endif static ZoneFrame *zn_zf_new(size_t capacity) { ZoneFrame *zf = zn_zf_allocate(capacity); zf->prev = NULL; zf->next = NULL; zf->length = 0; zf->capacity = capacity; return zf; } void *zn_alloc(Zone *z, size_t n) { return zn_zf_alloc(z, NULL, n); } void *zn_zf_alloc(Zone *z, ZoneFrame **zfo, size_t n) { ZoneFrame *zf = NULL; size_t wordsz = (n + sizeof(uintptr_t) - 1) / sizeof(uintptr_t); for (ZoneFrame *f = z->here; f; f = f->next) { if (f->capacity - f->length >= wordsz) { zf = f; break; } } if (!zf) { zf = zn_zf_new(zn_zf_capacity(wordsz)); zf->prev = z->tail; if (z->tail) z->tail->next = zf; z->tail = zf; } void *ptr = &zf->data[zf->length]; zf->length += wordsz; z->here = zf; if (zfo) *zfo = zf; return ptr; } void *zn_zf_realloc(Zone *z, ZoneFrame **zfo, void *old, size_t oldsz, size_t newsz) { ZoneFrame *zf = *zfo; size_t old_wordsz = (oldsz + sizeof(uintptr_t) - 1) / sizeof(uintptr_t); size_t new_wordsz = (newsz + sizeof(uintptr_t) - 1) / sizeof(uintptr_t); if (&zf->data[zf->length - old_wordsz] == old && zf->length + new_wordsz - old_wordsz <= zf->capacity) { zf->length = zf->length + new_wordsz - old_wordsz; return old; } else { void *new = zn_zf_alloc(z, zfo, newsz); zf->length -= old_wordsz; memcpy(new, old, oldsz); return new; } } void zn_free(Zone *z) { ZoneFrame *p, *t; t = z->tail; while (t) { p = t->prev; zn_zf_free(t); t = p; } } void zn_reset(Zone *z) { ZoneFrame *zf = z->here; if (!zf) return; for (;;) { zf->length = 0; if (zf->prev) zf = zf->prev; else break; } z->tail = zf; } char *zn_strdup(Zone *z, const char *s) { size_t n = strlen(s) + 1; char *d = zn_alloc(z, n); memcpy(d, s, n); return d; } void zn_save(Zone *z, ZoneState *m) { m->zf = z->here; m->length = z->here ? z->tail->length : 0; } void zn_load(Zone *z, ZoneState *m) { if (m->zf) { while (z->here != m->zf) { z->here->length = 0; z->here = z->here->prev; } m->zf->length = m->length; } else { zn_reset(z); } } #endif #endif