#include #include #include #include "wv_mem.h" #include "wv_err.h" /* Detect alignment pre-C11 */ union wv_max_align { long l; double d; void *p; }; #define WV_ALIGNOF(type) offsetof(struct { char c; type member; }, member) #define WV_ALIGNMENT WV_ALIGNOF(union wv_max_align) #define WV_ALIGN_MASK (WV_ALIGNMENT - 1) #define WV_ARENA_MIN_SIZE (256 * 1024) #define WV_ARENA_RESERVED WV_ALIGNMENT /* Reserve 0 as the sentinel "null" ref */ struct wv_arena *wv_arena_create(size_t n) { struct wv_arena *arena; if (n < WV_ARENA_MIN_SIZE) n = WV_ARENA_MIN_SIZE; arena = malloc(sizeof(struct wv_arena)); if (arena == NULL) err(1, "malloc arena struct"); arena->buf = malloc(n); if (arena->buf == NULL) err(1, "malloc arena buffer"); arena->size = n; arena->offset = WV_ARENA_RESERVED; return arena; } wv_ref wv_alloc(struct wv_arena *arena, size_t n) { wv_ref ref; unsigned char *new_buf; size_t aligned_n, new_size; if (n == 0) errx(1, "wv_alloc: zero-size allocation"); if (n > SIZE_MAX - WV_ALIGN_MASK) errx(1, "wv_alloc: allocation size overflow"); aligned_n = (n + WV_ALIGN_MASK) & ~WV_ALIGN_MASK; if (aligned_n > SIZE_MAX - arena->offset) errx(1, "wv_alloc: arena offset overflow"); if (arena->offset + aligned_n > arena->size) { new_size = arena->size; while (new_size < arena->offset + aligned_n) { if (new_size > SIZE_MAX / 2) errx(1, "wv_alloc: arena growth overflow"); new_size *= 2; } new_buf = realloc(arena->buf, new_size); if (new_buf == NULL) err(1, "realloc arena failed at %zu bytes", new_size); arena->buf = new_buf; arena->size = new_size; } ref = (wv_ref)arena->offset; arena->offset += aligned_n; return ref; } void wv_arena_reset(struct wv_arena *arena) { if (arena == NULL) return; memset(arena->buf, 0, arena->offset); arena->offset = WV_ARENA_RESERVED; } void wv_arena_destroy(struct wv_arena *arena) { if (arena == NULL) return; free(arena->buf); free(arena); }