#ifndef UC_SARENA_H_ #define UC_SARENA_H_ #include #include #ifdef __cplusplus extern "C" { #endif /** @file * Static arena allocator. * * An UCSArena allocates memory from a provided buffer. * * The backing buffer 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 buffer. * * 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 { unsigned char *start; // start of user buffer unsigned char *curr; // next alloc point unsigned char *end; // one past end of user buffer int tmp_allocs; // no of uc_sar_tmp_begin calls }; typedef struct UCSTmpAlloc UCSTmpAlloc; struct UCSTmpAlloc { unsigned char *alloc_point; UCSArena *arena; }; /** Initialize a UCSArena with predefined backing array. * * @data must be an array type * Use as * @code * _Alignas(16) char v[128]; * UCSArena a = UC_BV_STATIC_INITIALIZER(v); * @endcode */ #define UC_SAR_STATIC_INITIALIZER(data) UC_SAR_INITIALIZER((data), sizeof(data)) /** Initializer for UCSArena with predefined memory * Use as * @code * char v[128]; * UCSArena a = UC_BV_STATIC_INITIALIZER(v, sizeof v); * @endcode * or * @code * char *m = malloc(128); * UCSArena a = UC_BV_INIT(m, 128); * @endcode */ #define UC_SAR_INITIALIZER(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 on the stack, * 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 buffer 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_INITIALIZER(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: * struct Foo *str = UC_SAR_ALLOC_ALIGNED(&my_allocator, struct Foo[32]); * @endcode */ #define UC_SAR_ALLOC_ALIGNED(allocator, type_) uc_sar_alloc_aligned((allocator), sizeof(type_), _Alignof(type_)) // Initialize an UCSArena to allocate from @mem which is @sz bytes static inline void uc_sar_init(UCSArena *a, void *mem, size_t sz) { a->start = a->curr = mem; a->end = a->start + sz; } // begin temporary allocation. All allocations afterwards will be released in uc_sar_tmp_end UCSTmpAlloc uc_sar_tmp_begin(UCSArena *a); // end temporary allocations, releaseing all allocations since the most recent uc_sar_tmp_begin void uc_sar_tmp_end(const UCSTmpAlloc *state); // 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) [[gnu::alloc_align(2)]]; static inline void uc_sar_reset(UCSArena *a) { a->curr = a->start; } #ifdef __cplusplus } #endif #endif