summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common.h29
-rw-r--r--src/ez_arena.c58
-rw-r--r--src/ez_arena.h18
-rw-r--r--src/smrt_arena.c197
-rw-r--r--src/smrt_arena.h35
-rw-r--r--src/string.c25
-rw-r--r--src/string.h18
-rw-r--r--tests/ez_arena.c109
-rw-r--r--tests/smrt_arena.c367
9 files changed, 856 insertions, 0 deletions
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..1add4db
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+
+#define KiB(n) ((u64)n<<10)
+#define MiB(n) ((u64)n<<20)
+#define GiB(n) ((u64)n<<30)
+
+#define MAX(n, m) ((n > m) ? (n) : (m))
+#define MIN(n, m) ((n < m) ? (n) : (m))
+#define ALIGN_UP_POW2(n, m) (((u64)(n) + (u64)(m) - 1) & (~((u64)(m) - 1)))
+
+typedef int8_t i8;
+typedef int16_t i16;
+typedef int32_t i32;
+typedef int64_t i64;
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+typedef i8 b8;
+typedef i32 b32;
+
+typedef float f32;
+typedef double f64;
+
+#define ARENA_ALIGN (sizeof(void*))
diff --git a/src/ez_arena.c b/src/ez_arena.c
new file mode 100644
index 0000000..e37766e
--- /dev/null
+++ b/src/ez_arena.c
@@ -0,0 +1,58 @@
+#include "ez_arena.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+ez_arena_t *ez_arena_create(u64 size, b32 zero_out) {
+ ez_arena_t *alloc = malloc(size + sizeof(ez_arena_t));
+
+ if (!alloc) {
+ return NULL;
+ }
+
+ if (zero_out) {
+ memset((u8*)alloc+EZ_ARENA_BASE_POS, 0, size);
+ }
+
+ *alloc = (ez_arena_t){ .alloc_size = size, .pos = EZ_ARENA_BASE_POS };
+
+ return alloc;
+}
+
+void *ez_arena_alloc(ez_arena_t *arena, u64 alloc_amount) {
+ u64 pos_aligned = ALIGN_UP_POW2(arena->pos, ARENA_ALIGN);
+ u64 new_pos = pos_aligned + alloc_amount;
+
+ if (new_pos > arena->alloc_size + sizeof(ez_arena_t)) { return NULL; }
+
+ arena->pos = new_pos;
+
+ u8* out = (u8*)arena + pos_aligned;
+
+ memset(out, 0, alloc_amount);
+
+ return out;
+}
+
+void *ez_arena_alloc_nz(ez_arena_t *arena, u64 alloc_amount) {
+ u64 pos_aligned = ALIGN_UP_POW2(arena->pos, ARENA_ALIGN);
+ u64 new_pos = pos_aligned + alloc_amount;
+
+ if (new_pos > arena->alloc_size + sizeof(ez_arena_t)) { return NULL; }
+
+ arena->pos = new_pos;
+
+ return (u8*)arena + pos_aligned;
+}
+
+void ez_arena_clear(ez_arena_t *arena, b32 zero_out) {
+ if (zero_out) {
+ memset((u8*)arena+EZ_ARENA_BASE_POS, 0, arena->alloc_size);
+ }
+
+ arena->pos = EZ_ARENA_BASE_POS;
+}
+
+void ez_arena_destroy(ez_arena_t *arena) {
+ free(arena);
+}
diff --git a/src/ez_arena.h b/src/ez_arena.h
new file mode 100644
index 0000000..19def60
--- /dev/null
+++ b/src/ez_arena.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "common.h"
+
+#define EZ_ARENA_BASE_POS (sizeof(ez_arena_t))
+
+typedef struct {
+ u64 alloc_size;
+ u64 pos;
+} ez_arena_t;
+
+ez_arena_t *ez_arena_create(u64 size, b32 zero_out);
+ void *ez_arena_alloc(ez_arena_t *arena, u64 alloc_amount);
+ void *ez_arena_alloc_nz(ez_arena_t *arena, u64 alloc_amount);
+ void ez_arena_clear(ez_arena_t *arena, b32 zero_out);
+ void ez_arena_destroy(ez_arena_t *arena);
+
+#define EZA_ALLOC_ARRAY(arena, T, n) (T*)ez_arena_alloc(arena, sizeof(T) * (n))
diff --git a/src/smrt_arena.c b/src/smrt_arena.c
new file mode 100644
index 0000000..683afcd
--- /dev/null
+++ b/src/smrt_arena.c
@@ -0,0 +1,197 @@
+#if defined(__linux__)
+#define _DEFAULT_SOURCE
+#endif
+
+#include "common.h"
+#include "smrt_arena.h"
+
+#include <string.h>
+
+smrt_arena_t *smrt_arena_create(u64 reserve_size, u64 commit_size, b32 auto_decommit) {
+ u32 pagesize = plat_get_pagesize();
+
+ reserve_size = ALIGN_UP_POW2(reserve_size + sizeof(smrt_arena_t), pagesize);
+ commit_size = ALIGN_UP_POW2( commit_size, pagesize);
+
+ smrt_arena_t *arena = plat_mem_reserve(reserve_size);
+
+ if (!plat_mem_commit(arena, commit_size)) {
+ return NULL;
+ }
+
+ arena->reserve_size = reserve_size;
+ arena->commit_size = commit_size;
+ arena->commit_pos = commit_size;
+ arena->pos = SMRT_ARENA_BASE_POS;
+ arena->mark_pos = 0;
+ arena->auto_decommit = auto_decommit;
+
+ return arena;
+
+}
+
+static void smrt_arena__shrink_commit(smrt_arena_t *arena) {
+ if (!arena->auto_decommit) { return; }
+
+ u64 target_commit_pos = arena->pos + arena->commit_size - 1;
+ target_commit_pos -= target_commit_pos % arena->commit_size;
+
+ if (target_commit_pos >= arena->commit_pos) { return; }
+
+ u64 decommit_size = arena->commit_pos - target_commit_pos;
+ u8 *decommit_start = (u8*)arena + target_commit_pos;
+
+ if (plat_mem_decommit(decommit_start, decommit_size)) {
+ arena->commit_pos = target_commit_pos;
+
+ if (arena->mark_pos > arena->commit_pos) {
+ arena->mark_pos = 0;
+ }
+ }
+}
+
+void *smrt_arena_push(smrt_arena_t *arena, u64 alloc_amount, b32 zero_out) {
+ u64 pos_aligned = ALIGN_UP_POW2(arena->pos, ARENA_ALIGN);
+ u64 new_pos = pos_aligned + alloc_amount;
+
+ if (new_pos > arena->reserve_size) { return NULL; }
+
+ if (new_pos > arena->commit_pos) {
+ u64 new_commit_pos = new_pos;
+ new_commit_pos += arena->commit_size - 1;
+ new_commit_pos -= new_commit_pos % arena->commit_size;
+ new_commit_pos = MIN(new_commit_pos, arena->reserve_size);
+
+ u8 *mem = (u8*)arena + arena->commit_pos;
+ u64 commit_size = new_commit_pos - arena->commit_pos;
+
+ if (!plat_mem_commit(mem, commit_size)) {
+ return NULL;
+ }
+
+ arena->commit_pos = new_commit_pos;
+ }
+
+ arena->pos = new_pos;
+
+ u8 *out = (u8*)arena + pos_aligned;
+
+ if (zero_out) {
+ memset(out, 0, alloc_amount);
+ }
+
+ return out;
+}
+
+void *smrt_arena_mark_push(smrt_arena_t *arena, u64 alloc_amount, b32 zero_out) {
+ u64 orig_pos = arena->pos;
+
+ void *mem = smrt_arena_push(arena, alloc_amount, zero_out);
+
+ if (mem) {
+ arena->mark_pos = orig_pos;
+ }
+
+ return mem;
+}
+
+void smrt_arena_pop(smrt_arena_t *arena, u64 pop_amount) {
+ pop_amount = MIN(pop_amount, arena->pos - SMRT_ARENA_BASE_POS);
+ arena->pos -= pop_amount;
+
+ smrt_arena__shrink_commit(arena);
+}
+
+void smrt_arena_pop_to(smrt_arena_t *arena, u64 pos) {
+ u64 size = pos < arena->pos ? arena->pos - pos : 0;
+ smrt_arena_pop(arena, size);
+}
+
+b32 smrt_arena_pop_to_mark(smrt_arena_t *arena) {
+ if (arena->mark_pos == 0) {
+ return false;
+ }
+
+ smrt_arena_pop_to(arena, arena->mark_pos);
+
+ return true;
+}
+
+void smrt_arena_clear(smrt_arena_t *arena, b32 zero_out) {
+ arena->pos = SMRT_ARENA_BASE_POS;
+
+ if (zero_out) {
+ memset((u8*)arena + SMRT_ARENA_BASE_POS, 0, arena->commit_size - SMRT_ARENA_BASE_POS);
+ }
+
+ smrt_arena__shrink_commit(arena);
+}
+
+void smrt_arena_destroy(smrt_arena_t *arena) {
+ plat_mem_release(arena, arena->reserve_size);
+}
+
+#if defined(_WIN32)
+
+#include <windows.h>
+
+u32 plat_get_pagesize(void) {
+ SYSTEM_INFO sysinfo = { 0 };
+ GetSystemInfo(&sysinfo);
+
+ return sysinfo.dwPageSize;
+}
+
+void* plat_mem_reserve(u64 size) {
+ return VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_READWRITE);
+}
+
+b32 plat_mem_commit(void* ptr, u64 size) {
+ void* ret = VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE);
+ return ret != NULL;
+}
+
+b32 plat_mem_decommit(void* ptr, u64 size) {
+ return VirtualFree(ptr, size, MEM_DECOMMIT);
+}
+
+b32 plat_mem_release(void* ptr, u64 size) {
+ return VirtualFree(ptr, size, MEM_RELEASE);
+}
+
+
+#elif defined(__linux__)
+
+#include <unistd.h>
+#include <sys/mman.h>
+
+u32 plat_get_pagesize(void) {
+ return (u32)sysconf(_SC_PAGESIZE);
+}
+
+void* plat_mem_reserve(u64 size) {
+ void* out = mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (out == MAP_FAILED) {
+ return NULL;
+ }
+ return out;
+}
+
+b32 plat_mem_commit(void* ptr, u64 size) {
+ i32 ret = mprotect(ptr, size, PROT_READ | PROT_WRITE);
+ return ret == 0;
+}
+
+b32 plat_mem_decommit(void* ptr, u64 size) {
+ i32 ret = mprotect(ptr, size, PROT_NONE);
+ if (ret != 0) return false;
+ ret = madvise(ptr, size, MADV_DONTNEED);
+ return ret == 0;
+}
+
+b32 plat_mem_release(void* ptr, u64 size) {
+ i32 ret = munmap(ptr, size);
+ return ret == 0;
+}
+
+#endif
diff --git a/src/smrt_arena.h b/src/smrt_arena.h
new file mode 100644
index 0000000..4e6f592
--- /dev/null
+++ b/src/smrt_arena.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "common.h"
+
+#define SMRT_ARENA_BASE_POS (sizeof(smrt_arena_t))
+
+typedef struct {
+ u64 reserve_size;
+ u64 commit_size;
+
+ u64 mark_pos;
+
+ u64 pos;
+ u64 commit_pos;
+
+ b32 auto_decommit;
+} smrt_arena_t;
+
+smrt_arena_t *smrt_arena_create(u64 reserve_size, u64 commit_size, b32 auto_decommit);
+ void *smrt_arena_push(smrt_arena_t *arena, u64 alloc_amount, b32 zero_out);
+ void *smrt_arena_mark_push(smrt_arena_t *arena, u64 alloc_amount, b32 zero_out);
+ void smrt_arena_pop(smrt_arena_t *arena, u64 pop_amount);
+ void smrt_arena_pop_to(smrt_arena_t *arena, u64 location);
+ b32 smrt_arena_pop_to_mark(smrt_arena_t *arena);
+ void smrt_arena_clear(smrt_arena_t *arena, b32 zero_out);
+ void smrt_arena_destroy(smrt_arena_t *arena);
+
+#define SMRTA_ALLOC_ARRAY(arena, T, n) (T*)smrt_arena_push(arena, sizeof(T) * (n), true)
+
+u32 plat_get_pagesize(void);
+
+void *plat_mem_reserve(u64 size);
+ b32 plat_mem_commit(void *ptr, u64 size);
+ b32 plat_mem_decommit(void *ptr, u64 size);
+ b32 plat_mem_release(void *ptr, u64 size);
diff --git a/src/string.c b/src/string.c
new file mode 100644
index 0000000..be96c63
--- /dev/null
+++ b/src/string.c
@@ -0,0 +1,25 @@
+#include "common.h"
+#include "smrt_arena.h"
+#include <string.h>
+
+#include "string.h"
+
+strng_t *strng_new(smrt_arena_t *arena, u64 size) {
+ strng_t *s = smrt_arena_push(arena, sizeof(strng_t) + size, true);
+
+ if (!s) {
+ return NULL;
+ }
+
+ s->alloc_size = size;
+ return s;
+}
+
+strng_t *strng_from(smrt_arena_t *arena, char *c) {
+ u64 size = strlen(c);
+
+ return strng_new(arena, size);
+}
+
+ b32 strng_set(strng_t *string, char *c);
+ void strng_clear(strng_t *string);
diff --git a/src/string.h b/src/string.h
new file mode 100644
index 0000000..1b5f136
--- /dev/null
+++ b/src/string.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "common.h"
+#include "smrt_arena.h"
+
+#define STRNG_BASE_POS (sizeof(ez_arena_t))
+
+typedef struct {
+ u64 alloc_size;
+ u64 len;
+} strng_t;
+
+strng_t *strng_new(smrt_arena_t *arena, u64 size);
+strng_t *strng_from(smrt_arena_t *arena, char *c);
+ b32 strng_set(strng_t *string, char *c);
+ void strng_clear(strng_t *string);
+
+#define STRNG_FMT(s) (int)s->len, (s+STRNG_BASE_POS)
diff --git a/tests/ez_arena.c b/tests/ez_arena.c
new file mode 100644
index 0000000..d5bcced
--- /dev/null
+++ b/tests/ez_arena.c
@@ -0,0 +1,109 @@
+#include "../src/ez_arena.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <criterion/criterion.h>
+#include <criterion/internal/assert.h>
+#include <criterion/internal/test.h>
+
+Test(ez_arena, create_arena) {
+ ez_arena_t *arena = ez_arena_create(KiB(1), true);
+
+ cr_expect(arena->alloc_size == 1024);
+ cr_expect(arena->pos == EZ_ARENA_BASE_POS);
+
+ ez_arena_destroy(arena);
+}
+
+Test(ez_arena, alloc_arena) {
+ ez_arena_t *arena = ez_arena_create(sizeof(uint32_t), true);
+
+ uint32_t *i = ez_arena_alloc(arena, sizeof(uint32_t));
+
+ *i = UINT32_MAX;
+
+ cr_assert_eq(*i, UINT32_MAX);
+
+ ez_arena_destroy(arena);
+}
+
+Test(ez_arena, alloc_zero_arena) {
+ ez_arena_t *arena = ez_arena_create(sizeof(uint32_t), true);
+
+ uint32_t *i = ez_arena_alloc(arena, sizeof(uint32_t));
+
+ cr_assert_eq(*i, 0);
+
+ ez_arena_destroy(arena);
+}
+
+Test(ez_arena, alloc_array_arena) {
+ const size_t len = 24;
+ cr_expect(len != 0);
+
+ ez_arena_t *arena = ez_arena_create(sizeof(uint8_t) * len, true);
+
+ uint8_t *arr = EZA_ALLOC_ARRAY(arena, uint8_t, len);
+
+ for (size_t i = 0; i < len; i++) {
+ arr[i] = UINT8_MAX / (i+1);
+ }
+
+ cr_expect_eq(arr[0], UINT8_MAX);
+ cr_expect_eq(arr[len-1], UINT8_MAX / len);
+
+ ez_arena_destroy(arena);
+}
+
+Test(ez_arena, alloc_clear_arena) {
+ ez_arena_t *arena = ez_arena_create(sizeof(uint32_t), true);
+
+ uint32_t *i = ez_arena_alloc(arena, sizeof(uint32_t));
+
+ ez_arena_clear(arena, true);
+
+ uint32_t *j = ez_arena_alloc(arena, sizeof(uint32_t));
+
+ cr_assert_eq(i, j);
+
+ ez_arena_destroy(arena);
+}
+
+Test(ez_arena, alloc_clear_zero_arena) {
+ ez_arena_t *arena = ez_arena_create(sizeof(uint32_t), true);
+
+ uint32_t *i = ez_arena_alloc(arena, sizeof(uint32_t));
+
+ *i = UINT32_MAX;
+
+ ez_arena_clear(arena, true);
+
+ cr_expect_eq(*i, 0);
+
+ uint32_t *j = ez_arena_alloc(arena, sizeof(uint32_t));
+
+ cr_assert_eq(*j, 0);
+
+ *j = UINT32_MAX;
+
+ cr_expect_eq(*j, UINT32_MAX);
+ cr_expect_eq(*i, UINT32_MAX);
+
+ ez_arena_destroy(arena);
+}
+
+Test(ez_arena, alloc_clear_dont_zero_arena) {
+ ez_arena_t *arena = ez_arena_create(sizeof(uint32_t), true);
+
+ uint32_t *i = ez_arena_alloc(arena, sizeof(uint32_t));
+
+ *i = UINT32_MAX;
+
+ ez_arena_clear(arena, false);
+
+ cr_expect_eq(*i, UINT32_MAX);
+
+ ez_arena_destroy(arena);
+}
diff --git a/tests/smrt_arena.c b/tests/smrt_arena.c
new file mode 100644
index 0000000..87230d3
--- /dev/null
+++ b/tests/smrt_arena.c
@@ -0,0 +1,367 @@
+#include "../src/smrt_arena.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <criterion/criterion.h>
+#include <criterion/internal/assert.h>
+#include <criterion/internal/test.h>
+
+Test(smrt_arena, create_arena) {
+ smrt_arena_t *arena = smrt_arena_create(KiB(1), KiB(4), false);
+
+ cr_expect(arena->reserve_size == MAX(KiB(1), plat_get_pagesize()));
+ cr_expect(arena->pos == SMRT_ARENA_BASE_POS);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, alloc_arena) {
+ smrt_arena_t *arena = smrt_arena_create(sizeof(uint32_t), true, false);
+
+ uint32_t *i = smrt_arena_push(arena, sizeof(uint32_t), true);
+
+ *i = UINT32_MAX;
+
+ cr_assert_eq(*i, UINT32_MAX);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, alloc_zero_arena) {
+ smrt_arena_t *arena = smrt_arena_create(sizeof(uint32_t), true, false);
+
+ uint32_t *i = smrt_arena_push(arena, sizeof(uint32_t), true);
+
+ cr_assert_eq(*i, 0);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, alloc_array_arena) {
+ const size_t len = 24;
+ cr_expect(len != 0);
+
+ smrt_arena_t *arena = smrt_arena_create(sizeof(uint8_t) * len, true, false);
+
+ uint8_t *arr = SMRTA_ALLOC_ARRAY(arena, uint8_t, len);
+
+ for (size_t i = 0; i < len; i++) {
+ arr[i] = UINT8_MAX / (i+1);
+ }
+
+ cr_expect_eq(arr[0], UINT8_MAX);
+ cr_expect_eq(arr[len-1], UINT8_MAX / len);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, alloc_clear_arena) {
+ smrt_arena_t *arena = smrt_arena_create(sizeof(uint32_t), true, false);
+
+ uint32_t *i = smrt_arena_push(arena, sizeof(uint32_t), true);
+
+ smrt_arena_clear(arena, true);
+
+ uint32_t *j = smrt_arena_push(arena, sizeof(uint32_t), true);
+
+ cr_assert_eq(i, j);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, alloc_clear_zero_arena) {
+ smrt_arena_t *arena = smrt_arena_create(sizeof(uint32_t), true, false);
+
+ uint32_t *i = smrt_arena_push(arena, sizeof(uint32_t), true);
+
+ *i = UINT32_MAX;
+
+ smrt_arena_clear(arena, true);
+
+ cr_expect_eq(*i, 0);
+
+ uint32_t *j = smrt_arena_push(arena, sizeof(uint32_t), true);
+
+ cr_assert_eq(*j, 0);
+
+ *j = UINT32_MAX;
+
+ cr_expect_eq(*j, UINT32_MAX);
+ cr_expect_eq(*i, UINT32_MAX);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, alloc_clear_dont_zero_arena) {
+ smrt_arena_t *arena = smrt_arena_create(sizeof(uint32_t), true, false);
+
+ uint32_t *i = smrt_arena_push(arena, sizeof(uint32_t), true);
+
+ *i = UINT32_MAX;
+
+ smrt_arena_clear(arena, false);
+
+ cr_expect_eq(*i, UINT32_MAX);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, pop_arena) {
+ smrt_arena_t *arena = smrt_arena_create(KiB(1), true, false);
+
+ uint64_t mark = arena->pos;
+ uint32_t *i = smrt_arena_push(arena, sizeof(uint32_t), true);
+ smrt_arena_push(arena, sizeof(uint32_t), true);
+
+ smrt_arena_pop(arena, arena->pos - mark);
+ uint32_t *j = smrt_arena_push(arena, sizeof(uint32_t), true);
+
+ cr_assert_eq(i, j);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, pop_to_arena) {
+ smrt_arena_t *arena = smrt_arena_create(KiB(1), true, false);
+
+ smrt_arena_push(arena, sizeof(uint32_t), true);
+ uint64_t mark = arena->pos;
+ smrt_arena_push(arena, sizeof(uint32_t), true);
+ smrt_arena_push(arena, sizeof(uint32_t), true);
+
+ smrt_arena_pop_to(arena, mark);
+
+ cr_expect_eq(arena->pos, mark);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, pop_clamped_arena) {
+ smrt_arena_t *arena = smrt_arena_create(KiB(1), true, false);
+
+ smrt_arena_push(arena, sizeof(uint32_t), true);
+ smrt_arena_pop(arena, KiB(1));
+
+ cr_expect_eq(arena->pos, SMRT_ARENA_BASE_POS);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, push_grows_commit_arena) {
+ u32 pagesize = plat_get_pagesize();
+ smrt_arena_t *arena = smrt_arena_create(KiB(64), pagesize, false);
+
+ uint64_t initial_commit_pos = arena->commit_pos;
+ uint8_t *arr = SMRTA_ALLOC_ARRAY(arena, uint8_t, pagesize * 2);
+ arr[pagesize * 2 - 1] = UINT8_MAX;
+
+ cr_expect_gt(arena->commit_pos, initial_commit_pos);
+ cr_expect_eq(arr[pagesize * 2 - 1], UINT8_MAX);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, push_exceeds_reserve_arena) {
+ smrt_arena_t *arena = smrt_arena_create(KiB(1), true, false);
+
+ void *p = smrt_arena_push(arena, KiB(64), true);
+
+ cr_expect_eq(p, NULL);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, push_alignment_arena) {
+ smrt_arena_t *arena = smrt_arena_create(KiB(1), true, false);
+
+ smrt_arena_push(arena, sizeof(uint8_t), true);
+ void *p = smrt_arena_push(arena, sizeof(uint64_t), true);
+
+ cr_expect_eq((uintptr_t)p % ARENA_ALIGN, 0);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, create_fails_when_reserve_exceeds_address_space) {
+ smrt_arena_t *arena = smrt_arena_create((u64)1 << 48, true, false);
+
+ cr_expect_eq(arena, NULL);
+}
+
+Test(smrt_arena, mark_push_sets_mark) {
+ smrt_arena_t *arena = smrt_arena_create(KiB(1), true, false);
+
+ uint64_t pre_mark_pos = arena->pos;
+ smrt_arena_mark_push(arena, sizeof(uint32_t), true);
+
+ cr_expect_eq(arena->mark_pos, pre_mark_pos);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, mark_push_failure_leaves_mark_unset) {
+ smrt_arena_t *arena = smrt_arena_create(KiB(1), true, false);
+
+ void *p = smrt_arena_mark_push(arena, KiB(64), true);
+
+ cr_expect_eq(p, NULL);
+ cr_expect_eq(arena->mark_pos, 0);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, pop_to_mark_restores_position) {
+ smrt_arena_t *arena = smrt_arena_create(KiB(1), true, false);
+
+ uint32_t *i = smrt_arena_mark_push(arena, sizeof(uint32_t), true);
+ smrt_arena_push(arena, sizeof(uint32_t), true);
+ smrt_arena_push(arena, sizeof(uint32_t), true);
+
+ b32 popped = smrt_arena_pop_to_mark(arena);
+ uint32_t *j = smrt_arena_push(arena, sizeof(uint32_t), true);
+
+ cr_expect_eq(popped, true);
+ cr_assert_eq(i, j);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, mark_at_base_position_is_valid) {
+ smrt_arena_t *arena = smrt_arena_create(KiB(1), true, false);
+
+ cr_assert_eq(arena->pos, SMRT_ARENA_BASE_POS);
+
+ smrt_arena_mark_push(arena, sizeof(uint32_t), true);
+
+ cr_expect_eq(arena->mark_pos, SMRT_ARENA_BASE_POS);
+
+ b32 popped = smrt_arena_pop_to_mark(arena);
+
+ cr_expect_eq(popped, true);
+ cr_expect_eq(arena->pos, SMRT_ARENA_BASE_POS);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, pop_to_mark_without_mark_fails) {
+ smrt_arena_t *arena = smrt_arena_create(KiB(1), true, false);
+
+ smrt_arena_push(arena, sizeof(uint32_t), true);
+
+ b32 popped = smrt_arena_pop_to_mark(arena);
+
+ cr_expect_eq(popped, false);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, pop_to_ahead_of_pos_is_noop) {
+ smrt_arena_t *arena = smrt_arena_create(KiB(1), true, false);
+
+ smrt_arena_push(arena, sizeof(uint32_t), true);
+ uint64_t pos = arena->pos;
+
+ smrt_arena_pop_to(arena, pos + KiB(1));
+
+ cr_expect_eq(arena->pos, pos);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, auto_decommit_shrinks_on_pop) {
+ u32 pagesize = plat_get_pagesize();
+ smrt_arena_t *arena = smrt_arena_create(KiB(64), pagesize, true);
+
+ SMRTA_ALLOC_ARRAY(arena, uint8_t, pagesize * 3);
+ cr_assert_eq(arena->commit_pos, pagesize * 4);
+
+ smrt_arena_pop(arena, pagesize * 2);
+
+ cr_expect_eq(arena->commit_pos, pagesize * 2);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, auto_decommit_noop_within_same_chunk) {
+ u32 pagesize = plat_get_pagesize();
+ smrt_arena_t *arena = smrt_arena_create(KiB(64), pagesize, true);
+
+ SMRTA_ALLOC_ARRAY(arena, uint8_t, pagesize / 2);
+ uint64_t commit_pos_before = arena->commit_pos;
+
+ smrt_arena_pop(arena, sizeof(uint8_t));
+
+ cr_expect_eq(arena->commit_pos, commit_pos_before);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, auto_decommit_clear_leaves_one_chunk) {
+ u32 pagesize = plat_get_pagesize();
+ smrt_arena_t *arena = smrt_arena_create(KiB(64), pagesize, true);
+
+ SMRTA_ALLOC_ARRAY(arena, uint8_t, pagesize * 3);
+ cr_assert_gt(arena->commit_pos, (u64)pagesize);
+
+ smrt_arena_clear(arena, false);
+
+ cr_expect_eq(arena->commit_pos, pagesize);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, auto_decommit_off_by_default_preserves_commit) {
+ u32 pagesize = plat_get_pagesize();
+ smrt_arena_t *arena = smrt_arena_create(KiB(64), pagesize, false);
+
+ SMRTA_ALLOC_ARRAY(arena, uint8_t, pagesize * 3);
+ uint64_t commit_pos_before = arena->commit_pos;
+
+ smrt_arena_pop(arena, pagesize * 2);
+
+ cr_expect_eq(arena->commit_pos, commit_pos_before);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, auto_decommit_pop_past_mark_resets_mark) {
+ u32 pagesize = plat_get_pagesize();
+ smrt_arena_t *arena = smrt_arena_create(KiB(64), pagesize, true);
+
+ SMRTA_ALLOC_ARRAY(arena, uint8_t, pagesize);
+ smrt_arena_mark_push(arena, pagesize, true);
+ SMRTA_ALLOC_ARRAY(arena, uint8_t, pagesize);
+
+ uint64_t mark_pos = arena->mark_pos;
+ cr_assert_gt(mark_pos, (u64)0);
+
+ smrt_arena_pop(arena, pagesize * 3);
+
+ cr_expect_lt(arena->commit_pos, mark_pos);
+ cr_expect_eq(arena->mark_pos, 0);
+
+ b32 popped = smrt_arena_pop_to_mark(arena);
+ cr_expect_eq(popped, false);
+
+ smrt_arena_destroy(arena);
+}
+
+Test(smrt_arena, decommit_then_recommit_pages) {
+ u32 pagesize = plat_get_pagesize();
+ void *mem = plat_mem_reserve(pagesize);
+ cr_assert_neq(mem, NULL);
+
+ cr_assert_eq(plat_mem_commit(mem, pagesize), true);
+
+ uint8_t *bytes = (uint8_t*)mem;
+ bytes[0] = UINT8_MAX;
+
+ cr_expect_eq(plat_mem_decommit(mem, pagesize), true);
+ cr_expect_eq(plat_mem_commit(mem, pagesize), true);
+
+ cr_expect_eq(bytes[0], 0);
+
+ plat_mem_release(mem, pagesize);
+}