From d85735add9c250b0e1a60d21267595dd5f62e3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Mon, 20 Apr 2026 23:36:55 +0200 Subject: [PATCH] Simple slot allocator --- include/ucore/slot_allocator.h | 163 +++++++++++++-------------------- src/slot_allocator.c | 28 ------ 2 files changed, 65 insertions(+), 126 deletions(-) delete mode 100644 src/slot_allocator.c diff --git a/include/ucore/slot_allocator.h b/include/ucore/slot_allocator.h index d0cdc7f..b6d88da 100644 --- a/include/ucore/slot_allocator.h +++ b/include/ucore/slot_allocator.h @@ -1,112 +1,79 @@ #ifndef UC_SLOT_ALLOCATOR_H_ #define UC_SLOT_ALLOCATOR_H_ - #include #include -#include "ucore/bitvec.h" +#include -#ifdef __cplusplus -extern "C" { -#endif -/** @file - * Fixed size slot memory allocator - * - * UCSlotAlloc is used to allocate fixed slots of memory from a pre-allocated - * piece of memory. - * - * The main use case is to keep allocated memory in a contiguous block for use - * maximum number of objects(slots) is known and relatively small. More than 131072 slots - * is not recommended. - * - * Note that this allocator is not thread safe. - * - * A bitmap is used to track which slots are allocated. - */ - - - -typedef struct UCSlotAlloc UCSlotAlloc; -struct UCSlotAlloc { - const uint32_t slot_size; - const uint32_t num_slots; - struct UCBitVec bitmap; - uint8_t *slots; +// Repurpose free memory to eep a linked list of free slots +typedef struct UCSlot UCSlot; +struct UCSlot { + UCSlot *next; }; +typedef struct UCSlotArena UCSlotArena; +struct UCSlotArena { + UCSlot *free_list; + uint8_t *memory_start; + size_t memory_len; + size_t n_slots; + size_t slot_size; +}; -/** Initialize a UCSlotAlloc with an array as the backing memory to allocate slots from. - * The macro can be used at local scope to allocate memory automatic (stack) storage, - * or at global scope where the backin array will have static storage duration - * - * Use as: - * @code - * UC_SLOT_ALLOC_DEF(my_allocator, sizeof foo, 1024) - * struct foo *foo = uc_slot_alloc(&my_allocator); - * if (foo == NULL) { - * // out of memory - * } else { - * .. use foo - * uc_slot_free(&my_allocator, foo); - * } - * @endcode - * - * @param var_name Variable name for the allocator - * @param v slot_size Memory size allocated for each slot - * @param v num_slots Number of slots to reserve memory for +/** +* Initialize a memory arena for dynamically allocating slots of memory. +* Each allocation returns memory slots of the same size +* Note: For max speed, align @memory and @slot_size to the size of a pointer +* +* @param arena arena to initialize +* @param memory start of the backing memory +* @param memory_len length of @memory +* @param slot_size size of each slot allocation */ -#define UC_SLOT_ALLOC_DEF(var_name, slot_size_, num_slots_) \ -struct {\ - uc_bv_integer bitmap[UC_BV_LEN((num_slots_))]; \ - uint8_t memory[(num_slots_) * (slot_size_)] __attribute__((aligned)); \ -} var_name ## _memory = { \ -}; \ -UCSlotAlloc var_name = { \ - .slot_size = (slot_size_), \ - .num_slots = (num_slots_), \ - .bitmap = UC_BV_STATIC_INIT(var_name ## _memory.bitmap), \ - .slots = var_name ## _memory.memory \ -}; +static inline void uc_slot_arena_init(UCSlotArena *arena, void *memory, size_t memory_len, size_t slot_size) +{ + // Optionally - align to a pointer + //slot_size = (slot_size + _Alignof(UCSlot) - 1) & (~(_Alignof(UCSlot) - 1)); + assert(slot_size >= sizeof(UCSlot)); + size_t n_slots = memory_len / slot_size; + arena->free_list = NULL; -/** Similar to UC_SLOT_ALLOC_DEF but allows you to specify the backing memory that must - * be atleast as large as num_slots * slot_size. Note that an array of num_slots * slot_size - * bits is also allocated for the bitmap with this definition - */ -#define UC_SLOT_ALLOC_DEF_EX(var_name, slot_size_, num_slots_, memory) \ -uc_bv_integer var_name ## _bitmap[UC_BV_LEN((num_slots_))] = {}; \ -UCSlotAlloc var_name = { \ - .slot_size = (slot_size_), \ - .num_slots = (num_slots_), \ - .bitmap = UC_BV_STATIC_INIT(var_name ## _bitmap), \ - .slots = (memory) \ -}; + uint8_t *start = (uint8_t *)memory; + // build free_list in ascending order, anticipating sequential allocation + // and sequential access as the common case + for (size_t slot = n_slots; slot > 0; slot--) { + size_t index = slot - 1; + UCSlot *free_slot = (UCSlot *)(start + index * slot_size); + free_slot->next = arena->free_list; + arena->free_list = free_slot; + } -/** Allocate a memory slot - * - * @param sa The allocator to allocate from - * @return A pointer to the memory slot that's sa.slot_size big, or NULL if - * all slots are allocated - */ -void *uc_slot_alloc(UCSlotAlloc *sa); - - -/** Free a memory slot previously returned from uc_slot_alloc() - * - * @param sa The allocator to free from. - * @param slot The memory slot to free. Passing a NULL pointer is undefined behavior - */ -void uc_slot_free(UCSlotAlloc *restrict sa, void *restrict slot); - -/** uc_slot_free() where the slot can be a NULL pointer */ -#define uc_slot_free_safe(sa, slot) \ -do { \ - if ((slot) != NULL) { \ - uc_slot_free((sa), (slot)); \ - } \ -} while (0) - - -#ifdef __cplusplus + arena->n_slots = n_slots; + arena->memory_start = (uint8_t*)memory; + arena->memory_len = memory_len; + arena->slot_size = slot_size; +} + +static inline void *uc_slot_alloc(UCSlotArena *arena) +{ + UCSlot *slot = arena->free_list; + + if (slot != NULL) { + arena->free_list = slot->next; + } + + return slot; +} + +static inline void uc_slot_free(UCSlotArena *arena, void *mem) +{ + if (mem != NULL) { + assert((uint8_t *)mem >= arena->memory_start); + assert((uint8_t *)mem < arena->memory_start + arena->memory_len); + + UCSlot *slot = (UCSlot *)mem; + slot->next = arena->free_list; + arena->free_list = slot; + } } -#endif #endif diff --git a/src/slot_allocator.c b/src/slot_allocator.c deleted file mode 100644 index dbb2091..0000000 --- a/src/slot_allocator.c +++ /dev/null @@ -1,28 +0,0 @@ - -#include -#include -#include -#include "ucore/bitvec.h" -#include "ucore/slot_allocator.h" - -void *uc_slot_alloc(UCSlotAlloc *sa) -{ - int free_slot = uc_bv_find_first_zero(&sa->bitmap); - if (free_slot < 0) { - return NULL; - } else if ((size_t)free_slot >= sa->num_slots) { // bit vector may have more bits than slots - return NULL; - } - - uc_bv_set_bit(&sa->bitmap, free_slot); - - return sa->slots + free_slot * sa->slot_size; -} - -void uc_slot_free(UCSlotAlloc *restrict sa, void *restrict slot) -{ - assert((uint8_t *)slot >= sa->slots); - int slot_index = ((uint8_t *)slot - sa->slots) / sa->slot_size; - assert((size_t)slot_index < sa->num_slots); - uc_bv_clr_bit(&sa->bitmap, slot_index); -}