From 3e4826cf36779f9650f1a73f470c92ae92612afc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Mon, 6 Apr 2026 14:37:00 +0200 Subject: [PATCH] Add simple static arena allocator --- include/ucore/sarena.h | 116 +++++++++++++++++++++++++++++++++++++++++ src/sarena.c | 30 +++++++++++ 2 files changed, 146 insertions(+) create mode 100644 include/ucore/sarena.h create mode 100644 src/sarena.c diff --git a/include/ucore/sarena.h b/include/ucore/sarena.h new file mode 100644 index 0000000..222039b --- /dev/null +++ b/include/ucore/sarena.h @@ -0,0 +1,116 @@ +#ifndef UC_SARENA_H_ +#define UC_SARENA_H_ + +#include +#include +#ifdef __cplusplus +extern "C" { +#endif + +/** @file + * Static arena allocator. + * + * An UCSArena allocates memory from pre-existing array + * + * The backing array should be suitable aligned to the types + * that will be allocated from it. Use .e.g + * @code + * _Alignas(16) char mem[1024]; + * @endcode + * as the backing array. + * + * Once allocated, a piece of memory obtained from the + * allocator cannot be free'd individually, only the entire SArena can + * be free'd/reset +*/ +typedef struct UCSArena UCSArena; +struct UCSArena { + char *start; // start of user buffer + char *curr; // next alloc point + char *end; // one past end of user buffer +}; + +/** Initialize a UCSArena with predefined backing array. + * @data must be an array type + * Use as + * @code + * _Alignas(16) char v[128]; + * SArana a = UC_BV_STATIC_INIT(v); + * @endcode + */ +#define UC_SAR_STATIC_INIT(data)\ + UC_SAR_INIT((data), sizeof(data)) + +/** Initialize a UCSArena with predefined memory + * Use as + * @code + * char v[128]; + * SArana a = UC_BV_STATIC_INIT(v, sizeof v); + * @endcode + * or + * @code + * char *m = malloc(128); + * SArana a = UC_BV_INIT(m, 128); + * @endcode + */ +#define UC_SAR_INIT(data, sz)\ + {\ + (data),\ + (data),\ + (data) + sz\ + } + +/** Initialize a UCSArena with an array as the backing memory to allocate from. + * The macro can be used at local scope to allocate memory automatic (stack) storage, + * or at global scope where the backing array will have static storage duration + * + * Use as: + * @code + * UC_SAR_ALLOC_DEF(my_allocator, 1024) + * struct foo *foo = uc_sar_alloc(&my_allocator); + * if (foo == NULL) { + * // out of memory + * } + * @endcode + * + * @note, the backing store has alignment of 16. Use manual init if a different alignment + * is needed. + * @param var_name Variable name for the allocator + * @param size_ byte size of the backing array +*/ +#define UC_SAR_ALLOC_DEF(var_name, size_) \ + struct {\ + _Alignas(16) char memory[size_]; \ + } var_name ## _memory;\ + UCSArena var_name = UC_SAR_STATIC_INIT(var_name ## _memory.memory) + +/** Allocate space for @type_ , returned memory is aligned to requirements + * of @type. + * + * Use as: + * @code + * UC_SAR_ALLOC_DEF(my_allocator, 1024) + * struct foo *foo = UC_SAR_ALLOC_ALIGNED(&my_allocator, struct foo); + * // Array types can be used too, room for null terminated string of lengh 10: + * char *str = UC_SAR_ALLOC_ALIGNED(&my_allocator, char[11]); + * @endcode + */ +#define UC_SAR_ALLOC_ALIGNED(allocator, type_) \ + uc_sar_alloc_aligned((allocator), sizeof (type_), _Alignof(type_)) + +// Allocate @sz bytes from the UCSArena. Returns NULL if not enough space +void *uc_sar_alloc(UCSArena *a, size_t sz); + +// Allocate @sz bytes from the UCSArena, returned pointer is aligned to @align +// Returns NULL if not enough space +void *uc_sar_alloc_aligned(UCSArena *a, size_t sz, unsigned int align); + +static inline void uc_sar_reset(UCSArena *a) +{ + a->curr = a->start; +} +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/sarena.c b/src/sarena.c new file mode 100644 index 0000000..45e87c9 --- /dev/null +++ b/src/sarena.c @@ -0,0 +1,30 @@ +#include +#include "ucore/sarena.h" + +void *uc_sar_alloc(UCSArena *a, size_t sz) +{ + if (a->curr + sz > a->end) { + return NULL; + } + void *start = a->curr; + a->curr += sz; + return start; +} + +void *uc_sar_alloc_aligned(UCSArena *a, size_t sz, unsigned int align) +{ + assert(align != 0); + assert((align & (align - 1)) == 0); //power of 2 + + uintptr_t curr = (uintptr_t)a->curr; + uintptr_t aligned = (curr + (align - (uintptr_t)1)) & ~(align - (uintptr_t)1); + char *start = (char *)aligned; + + if (start + sz > a->end) { + return NULL; + } + + a->curr = start + sz; + + return start; +}