diff --git a/include/ucore/spool.h b/include/ucore/spool.h index 17ae856..81395c7 100644 --- a/include/ucore/spool.h +++ b/include/ucore/spool.h @@ -9,8 +9,8 @@ extern "C" { typedef union UCPoolBlock UCPoolBlock; union UCPoolBlock { - UCPoolBlock *next; // next free block (when not allocated) - unsigned char data[]; // data when allocated + UCPoolBlock *next; // next free block (when not allocated) + unsigned char data[0]; // data when allocated }; /** @file * Static pool allocator. @@ -25,15 +25,15 @@ union UCPoolBlock { * @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 + 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. @@ -45,11 +45,7 @@ struct UCPool { * @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); +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))]] diff --git a/src/small_allocator.c b/src/small_allocator.c index 295f0ca..0d63943 100644 --- a/src/small_allocator.c +++ b/src/small_allocator.c @@ -15,44 +15,37 @@ * 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 16 +#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) - -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. - // Remove padding & set ALIGNMENT to 8 to get 8 byte alignmet - // (for 64 bit systems, 32 bit systems might need other adjustments) - uint8_t pad[4]; -} BlockHeader; - - -_Static_assert(sizeof(BlockHeader) % ALIGNMENT == 0, - "BlockHeader size must be a multiple of ALIGNMENT"); - +#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* last_alloc; // next-fit pointer + uint8_t* base; // backing store + uint32_t size; // total size + BlockHeader* heap_start; // first block + BlockHeader* last_alloc; // next-fit pointer } SmallAllocator; /* --- Header helpers --- */ -static inline uint32_t block_size(BlockHeader* b) +static inline uint32_t block_size(const BlockHeader* b) { return b->size_and_flags & ~BLOCK_FREE; } -static inline int block_is_free(BlockHeader* b) +static inline int block_is_free(const BlockHeader* b) { return (b->size_and_flags & BLOCK_FREE) != 0; } @@ -67,23 +60,22 @@ 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 * bh_from_offset(const SmallAllocator *a, uint32_t off) +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); + return (BlockHeader*)((uintptr_t)a->base + off); } -static inline uint32_t bh_offset(SmallAllocator *a, BlockHeader *bh) +static inline uint32_t bh_offset(SmallAllocator* a, BlockHeader* bh) { if (bh == NULL) { return NULL_BLOCK; @@ -113,17 +105,15 @@ void small_allocator_init(SmallAllocator* a, void* backing_store, uint32_t size) a->last_alloc = first; } -static void split_block(SmallAllocator *a, BlockHeader* block, uint32_t size) +static void maybe_split_block(SmallAllocator* a, BlockHeader* block, uint32_t size) { - size = ALIGN_UP(size); + // assumes size is already aligned uint32_t bsize = block_size(block); - if (bsize < size + sizeof(BlockHeader) + ALIGNMENT) - return; + if (bsize < size + sizeof(BlockHeader) + ALIGNMENT) return; uint8_t* base = (uint8_t*)block; - BlockHeader* new_block = - (BlockHeader*)(base + sizeof(BlockHeader) + size); + BlockHeader* new_block = (BlockHeader*)(base + sizeof(BlockHeader) + size); uint32_t new_size = ALIGN_UP(bsize - size - sizeof(BlockHeader)); @@ -134,30 +124,25 @@ static void split_block(SmallAllocator *a, BlockHeader* block, uint32_t size) new_block->next = block->next; new_block->prev = bh_offset(a, block); - BlockHeader *next = bh_from_offset(a, new_block->next); + BlockHeader* next = block_from_offset(a, new_block->next); if (next) { next->prev = bh_offset(a, new_block); - //block->next->prev = new_block; } block->next = bh_offset(a, new_block); } -static void merge_with_next(SmallAllocator *a, BlockHeader* block) +static void merge_with_next(SmallAllocator* a, BlockHeader* block) { - BlockHeader* next = bh_from_offset(a, block->next); - if (!next || !block_is_free(next)) - return; + BlockHeader* next = block_from_offset(a, block->next); + if (next == NULL || !block_is_free(next)) return; - uint32_t new_size = ALIGN_UP(block_size(block) + - sizeof(BlockHeader) + - block_size(next)); + uint32_t new_size = ALIGN_UP(block_size(block) + sizeof(BlockHeader) + block_size(next)); block_set_size(block, new_size); block->next = next->next; - BlockHeader *nextnext = bh_from_offset(a, next->next); - if (nextnext) - nextnext->prev = bh_offset(a, block); + BlockHeader* nextnext = block_from_offset(a, next->next); + if (nextnext) nextnext->prev = bh_offset(a, block); } void* small_alloc(SmallAllocator* a, uint32_t size) @@ -169,12 +154,12 @@ void* small_alloc(SmallAllocator* a, uint32_t size) do { if (block_is_free(curr) && block_size(curr) >= size) { - split_block(a, curr, size); + maybe_split_block(a, curr, size); block_set_used(curr); a->last_alloc = curr; return (uint8_t*)curr + sizeof(BlockHeader); } - curr = bh_from_offset(a, curr->next) ? bh_from_offset(a, curr->next) : a->heap_start; + curr = block_from_offset(a, curr->next) ? block_from_offset(a, curr->next) : a->heap_start; } while (curr != start); return NULL; @@ -197,7 +182,7 @@ void small_free(SmallAllocator* a, void* ptr) block_set_free(block); merge_with_next(a, block); - BlockHeader* prev = bh_from_offset(a, block->prev); + BlockHeader* prev = block_from_offset(a, block->prev); if (prev != NULL && block_is_free(prev)) { merge_with_next(a, prev); @@ -212,36 +197,46 @@ void small_free(SmallAllocator* a, void* ptr) #include #include -void debug_alloc(const SmallAllocator *a) +void debug_alloc(const SmallAllocator* a) { puts("--debug start--"); - for (BlockHeader *b = a->heap_start ; b ; b = bh_from_offset(a, b->next)) { + 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--"); } -char buff[1023*1023]; +[[gnu::aligned(16)]] char buff[1023 * 1023]; int main(void) { printf("bh size %zu\n", sizeof(BlockHeader)); - #define SZ 64 +#define SZ 64 SmallAllocator a; - char *ptrs[SZ]; + char* ptrs[SZ]; small_allocator_init(&a, buff, sizeof buff); - ptrs[0] = small_alloc(&a, 16); debug_alloc(&a); + ptrs[0] = small_alloc(&a, 32); + ptrs[1] = small_alloc(&a, 80); + ptrs[2] = small_alloc(&a, 32); + small_free(&a, ptrs[1]); + ptrs[3] = small_alloc(&a, 12); + ptrs[4] = small_alloc(&a, 12); + debug_alloc(&a); + small_free(&a, ptrs[0]); + 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]); - // } + // if (rand() % 2 == 0) { + small_free(&a, ptrs[i]); + // } } debug_alloc(&a); }