Compare commits

..

2 Commits

Author SHA1 Message Date
Nils O. Selåsdal 24d9994c87 Header only file doesn't need c++ guard 2026-04-20 23:38:21 +02:00
Nils O. Selåsdal d85735add9 Simple slot allocator 2026-04-20 23:36:55 +02:00
3 changed files with 65 additions and 133 deletions
-7
View File
@@ -4,9 +4,6 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <assert.h> #include <assert.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @file /** @file
* Static arena allocator. * Static arena allocator.
@@ -177,8 +174,4 @@ static inline size_t uc_sar_available(const UCSArena *arena)
return uc_sar_size(arena) - uc_sar_used(arena); return uc_sar_size(arena) - uc_sar_used(arena);
} }
#ifdef __cplusplus
}
#endif
#endif #endif
+64 -97
View File
@@ -1,112 +1,79 @@
#ifndef UC_SLOT_ALLOCATOR_H_ #ifndef UC_SLOT_ALLOCATOR_H_
#define UC_SLOT_ALLOCATOR_H_ #define UC_SLOT_ALLOCATOR_H_
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include "ucore/bitvec.h" #include <assert.h>
#ifdef __cplusplus // Repurpose free memory to eep a linked list of free slots
extern "C" { typedef struct UCSlot UCSlot;
#endif struct UCSlot {
/** @file UCSlot *next;
* 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;
}; };
typedef struct UCSlotArena UCSlotArena;
/** Initialize a UCSlotAlloc with an array as the backing memory to allocate slots from. struct UCSlotArena {
* The macro can be used at local scope to allocate memory automatic (stack) storage, UCSlot *free_list;
* or at global scope where the backin array will have static storage duration uint8_t *memory_start;
* size_t memory_len;
* Use as: size_t n_slots;
* @code size_t slot_size;
* 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
*/
#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 \
}; };
/** 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 * Initialize a memory arena for dynamically allocating slots of memory.
* bits is also allocated for the bitmap with this definition * Each allocation returns memory slots of the same size
*/ * Note: For max speed, align @memory and @slot_size to the size of a pointer
#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) \
};
/** Allocate a memory slot
* *
* @param sa The allocator to allocate from * @param arena arena to initialize
* @return A pointer to the memory slot that's sa.slot_size big, or NULL if * @param memory start of the backing memory
* all slots are allocated * @param memory_len length of @memory
* @param slot_size size of each slot allocation
*/ */
void *uc_slot_alloc(UCSlotAlloc *sa); 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;
uint8_t *start = (uint8_t *)memory;
/** Free a memory slot previously returned from uc_slot_alloc() // build free_list in ascending order, anticipating sequential allocation
* // and sequential access as the common case
* @param sa The allocator to free from. for (size_t slot = n_slots; slot > 0; slot--) {
* @param slot The memory slot to free. Passing a NULL pointer is undefined behavior size_t index = slot - 1;
*/ UCSlot *free_slot = (UCSlot *)(start + index * slot_size);
void uc_slot_free(UCSlotAlloc *restrict sa, void *restrict slot); free_slot->next = arena->free_list;
arena->free_list = free_slot;
/** uc_slot_free() where the slot can be a NULL pointer */ }
#define uc_slot_free_safe(sa, slot) \
do { \ arena->n_slots = n_slots;
if ((slot) != NULL) { \ arena->memory_start = (uint8_t*)memory;
uc_slot_free((sa), (slot)); \ arena->memory_len = memory_len;
} \ arena->slot_size = slot_size;
} while (0) }
static inline void *uc_slot_alloc(UCSlotArena *arena)
#ifdef __cplusplus {
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 #endif
-28
View File
@@ -1,28 +0,0 @@
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#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);
}