diff options
author | Thomas Voss <mail@thomasvoss.com> | 2024-04-16 02:03:12 +0200 |
---|---|---|
committer | Thomas Voss <mail@thomasvoss.com> | 2024-04-16 02:03:12 +0200 |
commit | 43b04a1bfaecfabe8b3460575db7be5d592f896b (patch) | |
tree | 86c337e90f4b4238ab73dd698f470cf3482a0de8 /lib/alloc | |
parent | bbbeab43e067a7f009b74df72ed9b083f3ecef58 (diff) |
implement arena allocators
Diffstat (limited to 'lib/alloc')
-rw-r--r-- | lib/alloc/arena_alloc.c | 77 | ||||
-rw-r--r-- | lib/alloc/arena_free.c | 20 | ||||
-rw-r--r-- | lib/alloc/arena_zero.c | 14 | ||||
-rw-r--r-- | lib/alloc/bufalloc_noterm.c | 14 |
4 files changed, 112 insertions, 13 deletions
diff --git a/lib/alloc/arena_alloc.c b/lib/alloc/arena_alloc.c new file mode 100644 index 0000000..e5b99b7 --- /dev/null +++ b/lib/alloc/arena_alloc.c @@ -0,0 +1,77 @@ +#include <sys/mman.h> + +#include <errno.h> +#include <stdckdint.h> +#include <stdlib.h> + +#include "_attrs.h" +#include "alloc.h" +#include "macros.h" + +#define IS_POW_2(n) ((n) != 0 && ((n) & ((n) - 1)) == 0) + +[[_mlib_pure, _mlib_inline]] static size_t pad(size_t, size_t); +static struct _region *mkregion(size_t); + +size_t +pad(size_t len, size_t align) +{ + return (len + align - 1) & ~(align - 1); +} + +struct _region * +mkregion(size_t cap) +{ + struct _region *r = malloc(sizeof(struct _region)); + if (r == nullptr) + return nullptr; + *r = (struct _region){ + .cap = cap, + .data = mmap(nullptr, cap, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), + }; + if (r->data == MAP_FAILED) { + free(r); + return nullptr; + } + return r; +} + +void * +arena_alloc(arena *a, size_t sz, size_t n, size_t align) +{ + ASSUME(a != nullptr); + ASSUME(IS_POW_2(align)); + + if (ckd_mul(&sz, sz, n)) { + errno = EOVERFLOW; + return nullptr; + } + + for (struct _region *r = a->_head; r != nullptr; r = r->next) { + size_t off = pad(r->len, align); + + /* Technically there are other ways to solve this… but at this point you + might as well just fail */ + size_t padded_sz; + if (ckd_add(&padded_sz, off, sz)) { + errno = EOVERFLOW; + return nullptr; + } + + if (padded_sz <= r->cap) { + void *ret = (char *)r->data + off; + r->len = off + sz; + return ret; + } + } + + /* No page exists with enough space */ + struct _region *r = mkregion(MAX(sz, a->_init)); + if (r == nullptr) + return nullptr; + r->next = a->_head; + r->len = sz; + a->_head = r; + return r->data; +} diff --git a/lib/alloc/arena_free.c b/lib/alloc/arena_free.c new file mode 100644 index 0000000..dc2b45e --- /dev/null +++ b/lib/alloc/arena_free.c @@ -0,0 +1,20 @@ +#include <sys/mman.h> + +#include <stdlib.h> + +#include "alloc.h" +#include "macros.h" + +void +arena_free(arena *a) +{ + ASSUME(a != nullptr); + + struct _region *cur, *next; + for (cur = a->_head; cur != nullptr; cur = next) { + next = cur->next; + munmap(cur->data, cur->cap); + free(cur); + } + a->_head = nullptr; +} diff --git a/lib/alloc/arena_zero.c b/lib/alloc/arena_zero.c new file mode 100644 index 0000000..b0e3fbe --- /dev/null +++ b/lib/alloc/arena_zero.c @@ -0,0 +1,14 @@ +#include "alloc.h" +#include "macros.h" + +void +arena_zero(arena *a) +{ + ASSUME(a != nullptr); + + struct _region *cur = a->_head; + while (cur != nullptr) { + cur->len = 0; + cur = cur->next; + } +} diff --git a/lib/alloc/bufalloc_noterm.c b/lib/alloc/bufalloc_noterm.c index 8258521..e46d2a1 100644 --- a/lib/alloc/bufalloc_noterm.c +++ b/lib/alloc/bufalloc_noterm.c @@ -1,17 +1,5 @@ #include <errno.h> -#if __has_include(<stdckdint.h>) -# include <stdckdint.h> -# ifdef __APPLE__ -# warning "stdckdint.h now available on Mac; remove manual ckd_*() code" -# endif -#elifdef __GNUC__ -# define ckd_add(r, a, b) ((bool)__builtin_add_overflow(a, b, r)) -# define ckd_mul(r, a, b) ((bool)__builtin_mul_overflow(a, b, r)) -#else -# define ckd_add(r, a, b) (*(r) = (a) + (b)) -# define ckd_mul(r, a, b) (*(r) = (a) * (b)) -# warning "ckd_*() not supported on the current platform" -#endif +#include <stdckdint.h> #include <stdlib.h> #include "alloc.h" |