summary refs log tree commit diff
path: root/zone.h
diff options
context:
space:
mode:
Diffstat (limited to 'zone.h')
-rw-r--r--zone.h291
1 files changed, 291 insertions, 0 deletions
diff --git a/zone.h b/zone.h
new file mode 100644
index 0000000..d10ef6e
--- /dev/null
+++ b/zone.h
@@ -0,0 +1,291 @@
+#ifndef ZONE_H
+#define ZONE_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#define ZONE_USE_MALLOC         0
+#define ZONE_USE_MMAP           1
+#define ZONE_USE_WASM_BULKMEM   2
+
+#ifndef ZONE_BACKEND
+	#ifdef __unix__
+		#define ZONE_BACKEND ZONE_USE_MMAP
+	#else
+		#define ZONE_BACKEND ZONE_USE_MALLOC
+	#endif
+#endif
+
+#if !defined(ZONE_NOSTDLIB) && (ZONE_BACKEND != ZONE_USE_MMAP && ZONE_BACKEND != ZONE_USE_MALLOC)
+	#define ZONE_NOSTDLIB
+#endif
+
+typedef struct ZoneFrame ZoneFrame;
+struct ZoneFrame {
+	ZoneFrame *prev, *next;
+	uint8_t *beg, *end;
+	uint8_t data[];
+};
+
+typedef struct ZoneState ZoneState;
+
+struct ZoneState {
+	ZoneFrame *zf;
+	ZoneState *last;
+	uint8_t *beg;
+};
+
+typedef struct {
+	ZoneFrame *cur, *tail;
+	ZoneState *last;
+} Zone;
+
+void zn_free(Zone *z);
+void zn_reset(Zone *z);
+
+void zn_begin(Zone *z);
+void zn_end(Zone *z);
+void zn_save(Zone *z, ZoneState *m);
+void zn_load(Zone *z, ZoneState *m);
+
+void *zn_alloc(Zone *z, ptrdiff_t n);
+void *zn_alloc_align(Zone *z, ptrdiff_t n, size_t align);
+void *zn_realloc(Zone *z, void *ptr, ptrdiff_t oldsz, ptrdiff_t newsz);
+void *zn_realloc_align(Zone *z, void *ptr, ptrdiff_t oldsz, ptrdiff_t newsz, size_t align);
+
+char *zn_strdup(Zone *z, const char *s);
+void *zn_zeroed(void *, size_t);
+
+#define zn_new(z, t) (t*)zn_zeroed(zn_alloc_align(z, sizeof(t), _Alignof(t)), sizeof(t))
+
+#ifdef ZONE_IMPL
+
+#ifndef ZONE_PAGE_MULT
+#define ZONE_PAGE_MULT 2
+#endif
+
+#define UPTR_ALIGN(x, align) ((x)+((-x)&((align)-1)))
+#define PTR_ALIGN(ptr, align) (typeof(ptr)) (((uintptr_t)ptr + (align - 1)) & -align)
+
+#if ZONE_BACKEND == ZONE_USE_MMAP || ZONE_BACKEND == ZONE_USE_MALLOC
+	#include <stdio.h>
+	#include <stdlib.h>
+	[[noreturn]] void zn_abort(const char *msg) {
+		fprintf(stderr, "%s\n", msg);
+		abort();
+	}
+#elif ZONE_BACKEND == ZONE_USE_WASM_BULKMEM
+	__attribute((import_name("alert"))) void js_alert(const char *msg);
+	__attribute((import_name("abort"))) [[noreturn]] void js_abort(void);
+	[[noreturn]] void zn_abort(const char *msg) {
+		js_alert(msg);
+		js_abort();
+	}
+#else
+	#error "zn_abort not implemented for the current platform -- using __builtin_trap() instead"
+	[[noreturn]] void zn_abort(cont char *msg) {
+		(void)msg;
+		__builtin_trap();
+	}
+#endif
+
+#define ZONE_MEMSET __builtin_memset
+#define ZONE_MEMCPY __builtin_memcpy
+
+#if ZONE_BACKEND == ZONE_USE_MMAP
+
+	#include <sys/mman.h>
+	#include <unistd.h>
+
+	#define ZONE_PAGE_SIZE (ZONE_PAGE_MULT * sysconf(_SC_PAGE_SIZE))
+	#define ZONE_PAGE_FAIL MAP_FAILED
+
+	static void *zn_pg_alloc(size_t n) {
+		return mmap(NULL, n * ZONE_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+	}
+	static void zn_pg_free(void *p, size_t n) {
+		munmap(p, n);
+	}
+
+#elif ZONE_BACKEND == ZONE_USE_MALLOC
+
+	#define ZONE_PAGE_SIZE (ZONE_PAGE_MULT * 4 * 1024)
+	#define ZONE_PAGE_FAIL NULL
+
+	static void *zn_pg_alloc(size_t n) {
+		return malloc(n * ZONE_PAGE_SIZE);
+	}
+	static void zn_pg_free(void *p, size_t n) {
+		free(p);
+	}
+
+#elif ZONE_BACKEND == ZONE_USE_WASM_BULKMEM
+
+	#define ZONE_PAGE_SIZE 65536
+	#define ZONE_PAGE_FAIL (void*)(uintptr_t)-1
+	extern uint8_t __heap_base;
+	static void *zn_pg_free_adr = 0;
+	static void *zn_pg_alloc(size_t n) {
+		if (zn_pg_free_adr) {
+			void *p = zn_pg_free_adr;
+			zn_pg_free_adr = *(void **)zn_pg_free_adr;
+			return p;
+		}
+		void *p = &__heap_base + __builtin_wasm_memory_size(0);
+		__builtin_wasm_memory_grow(0, 1);
+		return p;
+	}
+	static void zn_pg_free(void *p, size_t n) {
+		uintptr_t adr = ((uintptr_t)p & ~0xFFFF);
+		n = (n + 0xFFFF) >> 16;
+		while (n--) {
+			void *pg = (void *)(adr + (n << 16));
+			*(void **)pg = zn_pg_free_adr;
+			zn_pg_free_adr = pg;
+		}
+	}
+
+#else
+
+	#error "unknown or unsupported zone backend"
+
+#endif
+
+static inline size_t zn_pg_fit(size_t cap) {
+	return cap + (-cap & (ZONE_PAGE_SIZE - 1));
+}
+
+static ZoneFrame *zn_zf_new(size_t capacity) {
+	ZoneFrame *zf = zn_pg_alloc(capacity / ZONE_PAGE_SIZE);
+	if (zf == ZONE_PAGE_FAIL) zn_abort("failed to allocate zone frame\n");
+	zf->prev = NULL;
+	zf->next = NULL;
+	zf->beg = zf->data;
+	zf->end = (uint8_t*)zf + capacity;
+	return zf;
+}
+
+static void zn_zf_free(ZoneFrame *z) {
+	zn_pg_free(z, (uintptr_t)z->end - (uintptr_t)z->beg);
+}
+
+void *zn_alloc_align(Zone *z, ptrdiff_t n, size_t align) {
+	ZoneFrame *zf = z->cur;
+	uint8_t *aligned;
+	for (;;) {
+		if (!zf) {
+			zf = zn_zf_new(zn_pg_fit(n + sizeof(ZoneFrame) + align - 1));
+			zf->prev = z->tail;
+			if (z->tail) z->tail->next = zf;
+			z->tail = zf;
+			z->cur = zf;
+		}
+		aligned = PTR_ALIGN(zf->beg, align);
+		if (aligned + n < zf->end) break;
+		zf = zf->next;
+	}
+	zf->beg = aligned + n;
+	return aligned;
+}
+
+void *zn_realloc_align(Zone *z, void *ptr, ptrdiff_t oldsz, ptrdiff_t newsz, size_t align) {
+	if (!ptr || !oldsz) return zn_alloc_align(z, newsz, align);
+	if (z->cur && ptr == z->cur->beg - oldsz && z->cur->beg - oldsz + newsz < z->cur->end) {
+		z->cur->beg -= oldsz;
+		z->cur->beg += newsz;
+		return ptr;
+	} else {
+		void *p = zn_alloc_align(z, newsz, align);
+		ZONE_MEMCPY(p, ptr, oldsz);
+		return p;
+	}
+}
+
+static inline size_t zn_align_for(size_t n) {
+	size_t a = 1;
+	while (n > a) a <<= 1;
+	return a;
+}
+
+void *zn_alloc(Zone *z, ptrdiff_t n) {
+	return zn_alloc_align(z, n, zn_align_for(n));
+}
+
+void *zn_realloc(Zone *z, void *ptr, ptrdiff_t oldsz, ptrdiff_t newsz) {
+	return zn_realloc_align(z, ptr, oldsz, newsz, zn_align_for(newsz));
+}
+
+void *zn_calloc(Zone *z, size_t n, size_t size) {
+	void *p = zn_alloc_align(z, n * size, zn_align_for(size));
+	ZONE_MEMSET(p, 0, n * size);
+	return p;
+}
+
+void zn_free(Zone *z) {
+	ZoneFrame *a = z->tail, *b;
+	while (a) {
+		b = a->prev;
+		zn_zf_free(a);
+		a = b;
+	}
+}
+
+void zn_reset(Zone *z) {
+	ZoneFrame *zf = z->tail;
+	while (zf) {
+		zf->beg = zf->data;
+		if (!zf->prev) break;
+		zf = zf->prev;
+	}
+	if (zf) z->cur = zf;
+}
+
+void zn_save(Zone *z, ZoneState *m) {
+	m->zf = z->cur;
+	m->beg = z->cur ? z->cur->beg : 0;
+	m->last = z->last;
+}
+
+void zn_load(Zone *z, ZoneState *m) {
+	if (m->zf) {
+		while (z->cur != m->zf) {
+			z->cur->beg = z->cur->data;
+			z->cur = z->cur->prev;
+		}
+		m->zf->beg = m->beg;
+	} else {
+		zn_reset(z);
+	}
+	z->last = m->last;
+}
+
+/* utils */
+
+#ifndef ZONE_NOSTDLIB
+char *zn_strdup(Zone *z, const char *s) {
+	size_t n = strlen(s) + 1;
+	char *d = zn_alloc_align(z, n, 1);
+	ZONE_MEMCPY(d, s, n);
+	return d;
+}
+#endif
+
+void *zn_zeroed(void *ptr, size_t n) {
+	ZONE_MEMSET(ptr, 0, n);
+	return ptr;
+}
+
+void zn_begin(Zone *z) {
+	ZoneState m = { 0 };
+	zn_save(z, &m);
+	ZoneState *p = zn_new(z, ZoneState);
+	*p = m;
+	z->last = p;
+}
+
+void zn_end(Zone *z) {
+	zn_load(z, z->last);
+}
+
+#endif
+#endif