diff --git a/.vscode/settings.json b/.vscode/settings.json index f0ce8de..571ae2e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,14 +1,11 @@ { "files.associations": { - "ringbuf.h": "c", - "seq.h": "c", - "stdint.h": "c", - "stdio.h": "c" + "*.h": "c", }, "C_Cpp.default.includePath": [ "/opt/homebrew/include/", "$(workspaceFolder)/include/", - - ] + ], + "C_Cpp.default.cStandard": "gnu99" } diff --git a/include/ucore/bitvec.h b/include/ucore/bitvec.h index a141bdc..493236e 100644 --- a/include/ucore/bitvec.h +++ b/include/ucore/bitvec.h @@ -27,7 +27,7 @@ struct UCBitVec { * Use as * @code * uc_bv_integer v[10]; - * struct UCBitVec v = BITVEC_STATIC_INIT(v); + * struct UCBitVec v = UC_BV_STATIC_INIT(v); * @endcode */ #define UC_BV_STATIC_INIT(vec_data)\ diff --git a/include/ucore/slot_allocator.h b/include/ucore/slot_allocator.h new file mode 100644 index 0000000..ddc13e1 --- /dev/null +++ b/include/ucore/slot_allocator.h @@ -0,0 +1,112 @@ +#ifndef UC_SLOT_ALLOCATOR_H_ +#define UC_SLOT_ALLOCATOR_H_ + +#include +#include +#include "ucore/bitvec.h" + +#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; +}; + + +/** 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 +*/ +#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 + * 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 \ +}; + +/** 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 +} +#endif + +#endif diff --git a/include/ucore/utils.h b/include/ucore/utils.h index 614b6bc..1a0b7f7 100644 --- a/include/ucore/utils.h +++ b/include/ucore/utils.h @@ -30,7 +30,7 @@ // int i; // struct bar zap; //}; -// ... +// ... //struct bar *p = ..; //the local p is a pointer to //a 'zap' member inside a struct foo. //give us the struct foo*: @@ -55,7 +55,7 @@ const typeof( ((const type *)0)->member ) *__mptr = (ptr); \ /** * Test if x is a power of 2 - * + * * @param x value, must be an unsigned type * @return 1 if x is a power of 20, 0 if it is not */ @@ -90,7 +90,7 @@ const typeof( ((const type *)0)->member ) *__mptr = (ptr); \ * * @param x numerator, must be positive * @param y denominator - * @return x/y rounded up + * @return x/y rounded up */ #define UC_DIV_ROUND_UP(x, y) (((x) + ((y) - 1))/(y)) @@ -103,7 +103,7 @@ const typeof( ((const type *)0)->member ) *__mptr = (ptr); \ * @param y denominator * @return x rounded up to nearest multiple of y */ -#define UC_ROUND_UP(x, y) (UC_DIV_ROUND_UP((x), (y)) * (y)) +#define UC_ROUND_UP(x, y) (UC_DIV_ROUND_UP((x), (y)) * (y)) /** * Round x down to nearest multiple of y diff --git a/src/bitvec.c b/src/bitvec.c index d168f84..9644f58 100644 --- a/src/bitvec.c +++ b/src/bitvec.c @@ -44,7 +44,7 @@ struct UCBitVec *uc_bv_new(size_t nbits) void uc_bv_free(struct UCBitVec *v) { - if (v) { + if (likely(v != NULL)) { free(v); } } diff --git a/src/slot_allocator.c b/src/slot_allocator.c new file mode 100644 index 0000000..cd5fd0a --- /dev/null +++ b/src/slot_allocator.c @@ -0,0 +1,67 @@ + +#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); +} + +int main() +{ + + UC_SLOT_ALLOC_DEF(sa, 24, 1) + + void *p1 = uc_slot_alloc(&sa); + printf("%p\n", &sa_memory.memory[0]); + printf("%p\n", p1); + + void *p2 = uc_slot_alloc(&sa); + printf("%p\n", p2); + uc_slot_free(&sa, p1); + + p2 = uc_slot_alloc(&sa); + printf("%p\n", p2); + + printf("__malloc__\n"); + void *v = malloc(24 * (1<<16)); + UC_SLOT_ALLOC_DEF_EX(sa2, 24, (1<<16), v) + char *v2 = NULL; + for (int i = 0; i < 1<<16; i++) { + char *c = uc_slot_alloc(&sa2); + if (c == NULL) { + printf("uc_slot_alloc failed at %d\n", i); + break; + } + *c = i; + if (i == 0) { + v2 = c; + } + } + for (int i = (1<<16) -1 ; i >= 0; i--) { + uc_slot_free(&sa2, v2 + 24*i); + } + free(v); + return 0; +} +