diff options
Diffstat (limited to 'wv_mem.c')
| -rw-r--r-- | wv_mem.c | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/wv_mem.c b/wv_mem.c new file mode 100644 index 0000000..0bb32dc --- /dev/null +++ b/wv_mem.c @@ -0,0 +1,94 @@ +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#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); +} + |
