diff --git a/include/ucore/sarena.h b/include/ucore/sarena.h deleted file mode 100644 index a712de8..0000000 --- a/include/ucore/sarena.h +++ /dev/null @@ -1,185 +0,0 @@ -#ifndef UC_SARENA_H_ -#define UC_SARENA_H_ - -#include -#include -#include - -/** @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_SAR_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_SAR_INITIALIZER(v, sizeof v); - * @endcode - * or - * @code - * char *m = malloc(128); - * UCSArena a = UC_SAR_INITIALIZER(m, 128); - * @endcode - */ -#define UC_SAR_INITIALIZER(data, sz) {\ - .start = (data),\ - .curr = (data),\ - .end=(data) + sz,\ - .tmp_allocs = 0\ -} - -/** 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##_arena_memory; \ - UCSArena var_name = UC_SAR_STATIC_INITIALIZER(var_name##_arena_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 *arena, void *memory, size_t sz) -{ - arena->start = arena->curr = (unsigned char *)memory; - arena->end = arena->start + sz; -} - -// begin temporary allocation. All allocations afterwards will be released in uc_sar_tmp_end -static inline UCSTmpAlloc uc_sar_tmp_begin(UCSArena *arena) -{ - UCSTmpAlloc state = {.alloc_point = arena->curr, .arena = arena}; - arena->tmp_allocs++; - return state; -} - -// end temporary allocations, releasing all allocations since the most recent uc_sar_tmp_begin -static inline void uc_sar_tmp_end(const UCSTmpAlloc *state) -{ - UCSArena *arena = state->arena; - assert(arena->tmp_allocs > 0); - arena->curr = state->alloc_point; - arena->tmp_allocs--; -} - -// Allocate @sz bytes from the UCSArena. Returns NULL if not enough space -static inline void *uc_sar_alloc(UCSArena *arena, size_t sz) -{ - if (sz > (arena->curr - arena->end)) { - return NULL; - } - void *start = arena->curr; - arena->curr += sz; - return start; -} - -// Allocate @sz bytes from the UCSArena, returned pointer is aligned to @align -// Returns NULL if not enough space -static inline void *uc_sar_alloc_aligned(UCSArena *arena, size_t sz, unsigned int align) [[gnu::alloc_align(3)]] -{ - assert(align != 0); - assert((align & (align - 1)) == 0); // power of 2 - - uintptr_t curr = (uintptr_t)arena->curr; - uintptr_t aligned = (curr + (align - (uintptr_t)1)) & ~(align - (uintptr_t)1); - unsigned char *start = (unsigned char *)aligned; - - if (sz > (arena->curr - arena->end)) { - - return NULL; - } - - arena->curr = start + sz; - - return start; -} - -// reset arena, making all memory available for allocation -// also invalidates any UCSTmpAlloc instances. -static inline void uc_sar_reset(UCSArena *arena) -{ - arena->curr = arena->start; -} - -// Arena size in bytes -static inline size_t uc_sar_size(const UCSArena *arena) -{ - return (size_t)(arena->end - arena->start); -} - -// Used bytes of the arena -static inline size_t uc_sar_used(const UCSArena *arena) -{ - return (size_t)(arena->curr - arena->start); -} - -// Available unused bytes in the arena -static inline size_t uc_sar_available(const UCSArena *arena) -{ - return uc_sar_size(arena) - uc_sar_used(arena); -} - -#endif diff --git a/include/ucore/slot_allocator.h b/include/ucore/slot_allocator.h deleted file mode 100644 index b7ca13f..0000000 --- a/include/ucore/slot_allocator.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef UC_SLOT_ALLOCATOR_H_ -#define UC_SLOT_ALLOCATOR_H_ -#include -#include -#include - -//Free memory is reused to build a linked list of slots -typedef union UCSlot UCSlot; -union UCSlot { - UCSlot *next; // next free block (when not allocated) - uint8_t data[0]; // data when allocated -}; - -typedef struct UCSlotArena UCSlotArena; -struct UCSlotArena { - UCSlot *free_list; - uint8_t *memory_start; - size_t memory_len; - size_t n_slots; - size_t used_slots; - size_t slot_size; -}; - -/** -* 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 -*/ -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; - // 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; - } - - arena->n_slots = n_slots; - arena->memory_start = (uint8_t*)memory; - arena->memory_len = memory_len; - arena->slot_size = slot_size; -} - -/** - * Allocate a slot. - * - * @return pointer to a slot, NULL if all slots are already allocated - */ -static inline void *uc_slot_alloc(UCSlotArena *arena) -{ - UCSlot *slot = arena->free_list; - - if (slot != NULL) { - arena->free_list = slot->next; - arena->used_slots++; -#ifdef DEBUG - memset(slot, 0xCB, arena->slot_size); -#endif - } - - return &slot->data[0]; -} -/** - * Free a slot. - * - * @param mem pointer to a slot that previously was allocated with uc_slot_alloc - */ -static inline void uc_slot_free(UCSlotArena *arena, void *memory) -{ - if (memory != NULL) { -#ifdef DEBUG - for (const UCSlot *slot = arena->free_list ; slot = slot->next) { - if (slot == mem) { - assert(0 && "Double free detected"); - } - } -#endif - assert((uint8_t *)memory >= arena->memory_start); - assert((uint8_t *)memory < arena->memory_start + arena->memory_len); - assert(arena->used_slots > 0); - UCSlot *slot = (UCSlot *)memory; - slot->next = arena->free_list; - arena->free_list = slot; - arena->used_slots--; - } -} - -#endif diff --git a/include/ucore/spool.h b/include/ucore/spool.h deleted file mode 100644 index 81395c7..0000000 --- a/include/ucore/spool.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef UC_SPOOL_H_ -#define UC_SPOOL_H_ - -#include -#include -#ifdef __cplusplus -extern "C" { -#endif - -typedef union UCPoolBlock UCPoolBlock; -union UCPoolBlock { - UCPoolBlock *next; // next free block (when not allocated) - unsigned char data[0]; // data when allocated -}; -/** @file - * Static pool allocator. - * - * An UCPool allocates fixed size memory chunks from a provided buffer, - * and memory allocations can be free'd in any order. - * - * 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 buffer. - * - */ -typedef struct UCPool UCPool; -struct UCPool { - UCPoolBlock *free_list; // free list - size_t block_size; // aligned size of each block - size_t capacity; // number of blocks - size_t align; // alignment - UCPoolBlock *start; // start of user buffer - UCPoolBlock *end; // one past end of user buffer -}; - -/** Initialize the pool. - * - * Block size will be rounded up by alignment, or to size of a pointer - * - * @param buffer backing buffer for the pool - * @param buffer_size size of @buffer - * @param block_size size of each allocation. - * @param alignment alignment of each block (must be power of 2) - */ -void uc_pool_init(UCPool *pool, void *buffer, size_t buffer_size, size_t block_size, size_t alignment); - -// Allocate a block from the pool. Returns NULL if no more space -[[gnu::assume_aligned(_Alignof(UCPoolBlock))]] -void *uc_pool_alloc(UCPool *pool); - -// Free @p . Returns memory to the pool -void uc_pool_free(UCPool *pool, void *p); -void uc_pool_reset(UCPool *pool); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/small_allocator.c b/src/small_allocator.c deleted file mode 100644 index cc532f1..0000000 --- a/src/small_allocator.c +++ /dev/null @@ -1,252 +0,0 @@ -#include -#include -#include -#include -#include -/** - * General purpose allocator. - * - Allocated pointers are aligned to 16 bytes. - * - Backing buffer must be aligned to 16 bytes start - * - 4Gb - 16 is max for the allocator backing buffer. - * - Allocation of > 0 bytes returns a size aligned up to 16 bytes - * - Allocation of 0 bytes returns a valid pointer with 0 usable space - * The allocator maintains a doubly linked list of blocks, - * when blocks are free'd, adjacent blocks are merged to keep - * fragmentation to a minimum - */ - -typedef struct BlockHeader { - uint32_t next; // offset from base - uint32_t prev; // offset from base - uint32_t size_and_flags; // size | free_bit - // Padding to ensure we always return 16 byte aligned pointers. - uint8_t pad[4]; -} BlockHeader; - -#define ALIGNMENT sizeof(BlockHeader) -#define ALIGN_UP(n) (((n) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1)) -#define ALIGN_DOWN(n) ((n) & ~(ALIGNMENT - 1)) - -#define NULL_BLOCK (0xffffffff) -#define BLOCK_FREE ((uint32_t)1) -_Static_assert(ALIGNMENT == 16, "sizeof(BlockHeader) is not 16"); - -typedef struct { - uint8_t* base; // backing store - uint32_t size; // total size - BlockHeader* heap_start; // first block - BlockHeader* next_alloc; // start of free block search -} SmallAllocator; - -/* --- Header helpers --- */ - -static inline uint32_t block_size(const BlockHeader* b) -{ - return b->size_and_flags & ~BLOCK_FREE; -} - -static inline int block_is_free(const BlockHeader* b) -{ - return (b->size_and_flags & BLOCK_FREE) != 0; -} - -static inline void block_set_free(BlockHeader* b) -{ - b->size_and_flags |= BLOCK_FREE; -} - -static inline void block_set_used(BlockHeader* b) -{ - b->size_and_flags &= ~BLOCK_FREE; -} - -static inline void block_set_size(BlockHeader* b, uint32_t size) -{ - uint32_t flags = b->size_and_flags & BLOCK_FREE; - b->size_and_flags = size | flags; -} - -static inline BlockHeader* block_from_offset(const SmallAllocator* a, uint32_t off) -{ - if (off == NULL_BLOCK) { - return NULL; - } - - return (BlockHeader*)((uintptr_t)a->base + off); -} - -static inline uint32_t bh_offset(SmallAllocator* a, BlockHeader* bh) -{ - if (bh == NULL) { - return NULL_BLOCK; - } - - return (uint32_t)((uintptr_t)bh - (uintptr_t)a->base); -} - -void small_allocator_init(SmallAllocator* a, void* backing_store, uint32_t size) -{ - // backing store must be aligned, otherwise all alignment guarantees are lies - assert(((uintptr_t)backing_store & (ALIGNMENT - 1)) == 0); - assert(size >= sizeof(BlockHeader)); - assert(size < 0xfffffff0); - - a->base = backing_store; - a->size = size; - - BlockHeader* first = backing_store; - // allocator logic requires the first block to be properly aligned - // as well as size being eligned. - uint32_t usable = ALIGN_DOWN(size) - sizeof(BlockHeader); - block_set_free(first); - block_set_size(first, usable); - first->next = NULL_BLOCK; - first->prev = NULL_BLOCK; - - a->heap_start = first; - a->next_alloc = first; -} - -static void maybe_split_block(SmallAllocator* a, BlockHeader* block, uint32_t size) -{ - // assumes size is already aligned - uint32_t bsize = block_size(block); - - if (bsize < size + sizeof(BlockHeader) + ALIGNMENT) { - return; - } - - uint8_t* base = (uint8_t*)block; - BlockHeader* new_block = (BlockHeader*)(base + sizeof(BlockHeader) + size); - - uint32_t new_size = bsize - size - sizeof(BlockHeader); - - block_set_size(block, size); - block_set_used(block); - - new_block->size_and_flags = new_size | BLOCK_FREE; - new_block->next = block->next; - new_block->prev = bh_offset(a, block); - - BlockHeader* next = block_from_offset(a, new_block->next); - if (next) { - next->prev = bh_offset(a, new_block); - } - - block->next = bh_offset(a, new_block); -} - -static void merge_with_next(SmallAllocator* a, BlockHeader* block) -{ - BlockHeader* next = block_from_offset(a, block->next); - if (next == NULL || !block_is_free(next)) return; - - uint32_t new_size = block_size(block) + sizeof(BlockHeader) + block_size(next); - - block_set_size(block, new_size); - block->next = next->next; - BlockHeader* nextnext = block_from_offset(a, next->next); - if (nextnext) nextnext->prev = bh_offset(a, block); - // note - caller have to adjust next_alloc on return -} - -void* small_alloc(SmallAllocator* a, uint32_t size) -{ - if (size > UINT32_MAX - (ALIGNMENT - 1)) { - return NULL; - } - size = ALIGN_UP(size); - - BlockHeader* start = a->next_alloc ? a->next_alloc : a->heap_start; - BlockHeader* curr = start; - - do { - if (block_is_free(curr) && block_size(curr) >= size) { - maybe_split_block(a, curr, size); - block_set_used(curr); - BlockHeader *next = block_from_offset(a, curr->next); - a->next_alloc = next ? next : a->heap_start;; - return (uint8_t*)curr + sizeof(BlockHeader); - } - BlockHeader *next = block_from_offset(a, curr->next); - curr = next ? next : a->heap_start; - } while (curr != start); - - return NULL; -} - -/* Free */ - -void small_free(SmallAllocator* a, void* ptr) -{ - if (!ptr) { - return; - } - - BlockHeader* block = (BlockHeader*)((uint8_t*)ptr - sizeof(BlockHeader)); - - assert((uint8_t*)ptr - sizeof(BlockHeader) >= a->base && (uint8_t*)ptr < a->base + a->size); - // double free detection - assert(!block_is_free(block)); - - block_set_free(block); - merge_with_next(a, block); - - BlockHeader* prev = block_from_offset(a, block->prev); - if (prev != NULL && block_is_free(prev)) { - merge_with_next(a, prev); - a->next_alloc = prev; - } else { - a->next_alloc = block; - } -} - -#include -#include -#include - -void debug_alloc(const SmallAllocator* a) -{ - puts("--debug start--"); - for (BlockHeader* b = a->heap_start; b; b = block_from_offset(a, b->next)) { - printf("Block size %u free %d prev %u next %u \n", block_size(b), block_is_free(b), b->prev, b->next); - } - puts("--debug end--"); -} - -[[gnu::aligned(16)]] char buff[1023]; -int main(void) -{ - printf("bh size %zu\n", sizeof(BlockHeader)); -#define SZ 64 - SmallAllocator a; - char* ptrs[SZ]; - - small_allocator_init(&a, buff, sizeof(buff)); - debug_alloc(&a); - ptrs[0] = small_alloc(&a, 1023-16*2 - 144); - ptrs[1] = small_alloc(&a, 80); - ptrs[2] = small_alloc(&a, 32); - debug_alloc(&a); - small_free(&a, ptrs[0]); - ptrs[3] = small_alloc(&a, 12); - ptrs[4] = small_alloc(&a, 12); - debug_alloc(&a); - - small_free(&a, ptrs[1]); - small_free(&a, ptrs[2]); - small_free(&a, ptrs[3]); - small_free(&a, ptrs[4]); - - for (int i = 0; i < SZ; i++) { - ptrs[i] = small_alloc(&a, rand() % 256 + 1); - } - - for (int i = 0; i < SZ; i++) { - // if (rand() % 2 == 0) { - small_free(&a, ptrs[i]); - // } - } - debug_alloc(&a); -} - diff --git a/src/spool.c b/src/spool.c deleted file mode 100644 index decb58f..0000000 --- a/src/spool.c +++ /dev/null @@ -1,115 +0,0 @@ -#include -#include "ucore/spool.h" -#include "ucore/utils.h" -#include - -void uc_pool_init(UCPool *pool, void *buffer, size_t buffer_size, size_t block_size, size_t align) -{ - assert(align > 0); - assert((align & (align - 1)) == 0); // power of 2 - - - // enforce minimum for storing free list pointers - - if (align < _Alignof(UCPoolBlock)) { - align = _Alignof(UCPoolBlock); - } - - if (block_size < sizeof(UCPoolBlock)) { - block_size = sizeof(UCPoolBlock); - } - // ensure initial alignment - size_t stride = UC_ALIGN(block_size, align); - - // align first usable block - uintptr_t start_addr = (uintptr_t)buffer; - uintptr_t first_addr = UC_ALIGN(start_addr, align); - size_t padding = first_addr - start_addr; - - if (padding + stride > buffer_size) { - pool->capacity = 0; - pool->free_list = NULL; - } else { - size_t usable = buffer_size - padding; - pool->capacity = usable / stride; - - // build intrusive free list - UCPoolBlock *p = (UCPoolBlock*)first_addr; - for (size_t i = 0; i < pool->capacity - 1; ++i) { - void *punt = __builtin_assume_aligned(&p->data[0] + pool->block_size, _Alignof(UCPoolBlock)); - UCPoolBlock *next = punt; - p->next = next; - p = next; - } - p->next = NULL; // last element - pool->free_list = (UCPoolBlock *)first_addr; - } - - pool->block_size = stride; - pool->align = align; - pool->start = buffer; - pool->end = buffer + buffer_size; -} - - -void *uc_pool_alloc(UCPool *pool) -{ - if (!pool->free_list) { - return NULL; - } - UCPoolBlock *block = pool->free_list; - pool->free_list = block->next; - -#ifdef DEBUG - memset(block, 0xCB, pool->block_size); -#endif - return block->data; -} -void uc_pool_free(UCPool *pool, void *p) -{ - if (p == NULL) { - return; - } - UCPoolBlock *block = p; -#ifdef DEBUG - assert(block >= pool->start && block < pool->end); - assert(((uintptr_t)p - (uintptr_t)pool->start) % pool->block_size == 0); - - // double-free detection - for (UCPoolBlock *f = pool->free_list; f; f = f->next) { - if (f == p) { - assert(0 && "Double free detected in pool!"); - return; - } - } - - memset(p, 0xDD, pool->block_size); -#endif - // When not in use, store pointer to next free element inline in the block - block->next = pool->free_list; - pool->free_list = p; -} - -void uc_pool_reset(UCPool *pool) -{ - if (pool->capacity == 0) - return; - - uintptr_t start_addr = (uintptr_t)pool->start; - uintptr_t first_addr = UC_ALIGN(start_addr, pool->align); - - // build intrusive free list - UCPoolBlock *p = (UCPoolBlock*)first_addr; - for (size_t i = 0; i < pool->capacity - 1; ++i) { - void *punt = __builtin_assume_aligned(&p->data[0] + pool->block_size, _Alignof(UCPoolBlock)); - UCPoolBlock *next = punt; - p->next = next; - p = next; - - } - - p->next = NULL; // last element - - pool->free_list = (UCPoolBlock *)first_addr; -} -