diff options
| author | Karsten Schmidt <k@postspectacular.com> | 2017-07-14 18:17:27 +0100 |
|---|---|---|
| committer | Karsten Schmidt <k@postspectacular.com> | 2017-07-14 18:17:27 +0100 |
| commit | e46468047ce0fb4c75d31c7797e8e3ef14219903 (patch) | |
| tree | 3417df1402d44a606432a78501195d18edd35ae5 /tinyalloc.c | |
| parent | cebcee692db504907901ddad4ac8342d296fc76e (diff) | |
rename fns again, add ta_calloc() & memset
Diffstat (limited to 'tinyalloc.c')
| -rw-r--r-- | tinyalloc.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/tinyalloc.c b/tinyalloc.c new file mode 100644 index 0000000..b48f85c --- /dev/null +++ b/tinyalloc.c @@ -0,0 +1,249 @@ +#include "tinyalloc.h" + +#ifdef NDEBUG +#define print_s(X) +#define print_i(X) +#else +extern void print_s(char *); +extern void print_i(size_t); +#endif + +typedef struct Block Block; + +struct Block { + void *addr; + Block *next; + size_t size; +}; + +typedef struct { + Block *free; // first free block + Block *used; // first used block + Block *avail; // first available blank block + size_t top; // top free addr + Block blocks[TA_HEAP_BLOCKS]; +} Heap; + +static Heap *heap = (Heap *)TA_BASE; + +/** + * Insert block into free list, sorted by addr. + */ +static void insert_block(Block *block) { + Block *ptr = heap->free; + Block *prev = NULL; + while (ptr != NULL) { + if ((size_t)block->addr <= (size_t)ptr->addr) { + print_s("insert"); + print_i((size_t)ptr); + break; + } + prev = ptr; + ptr = ptr->next; + } + if (prev != NULL) { + if (ptr == NULL) { + print_s("new tail"); + } + prev->next = block; + } else { + print_s("new head"); + heap->free = block; + } + block->next = ptr; +} + +static void release_blocks(Block *scan, Block *to) { + Block *scan_next; + while (scan != to) { + print_s("release"); + print_i((size_t)scan); + scan_next = scan->next; + scan->next = heap->avail; + scan->addr = 0; + scan->size = 0; + heap->avail = scan; + scan = scan_next; + } +} + +static void compact() { + Block *ptr = heap->free; + Block *prev = NULL; + Block *scan; + while (ptr != NULL) { + prev = ptr; + scan = ptr->next; + size_t base = (size_t)ptr->addr; + while (scan != NULL && + (size_t)prev->addr + prev->size == (size_t)scan->addr) { + print_s("merge"); + print_i((size_t)scan); + prev = scan; + scan = scan->next; + } + if (prev != ptr) { + size_t new_size = prev->addr + prev->size - ptr->addr; + print_s("new size"); + print_i(new_size); + ptr->size = new_size; + Block *next = prev->next; + // make merged blocks available + release_blocks(ptr->next, prev->next); + // relink + ptr->next = next; + ptr = next; + } else { + ptr = ptr->next; + } + } +} + +bool ta_init() { + heap->free = NULL; + heap->used = NULL; + heap->avail = heap->blocks; + heap->top = TA_HEAP_START; + Block *block = heap->blocks; + size_t i = TA_HEAP_BLOCKS - 1; + while(i--) { + block->next = block + 1; + block++; + } + return true; +} + +bool ta_free(void *free) { + Block *block = heap->used; + Block *prev = NULL; + while (block != NULL) { + if (free == block->addr) { + if (prev) { + prev->next = block->next; + } else { + heap->used = block->next; + } + insert_block(block); + compact(); + return true; + } + prev = block; + block = block->next; + } + return false; +} + +static Block *alloc_block(size_t num) { + Block *ptr = heap->free; + Block *prev = NULL; + size_t top = heap->top; + num = (num + TA_ALIGN - 1) & -TA_ALIGN; + while (ptr != NULL) { + const int is_top = (size_t)ptr->addr + ptr->size >= top; + if (is_top || ptr->size >= num) { + if (prev != NULL) { + prev->next = ptr->next; + } else { + heap->free = ptr->next; + } + ptr->next = heap->used; + heap->used = ptr; + if (is_top) { + print_s("resize top block"); + ptr->size = num; + heap->top = (size_t)ptr->addr + num; + } else if (heap->avail != NULL) { + size_t excess = ptr->size - num; + if (excess >= TA_SIZE_THRESHOLD) { + ptr->size = num; + Block *split = heap->avail; + heap->avail = split->next; + split->addr = ptr->addr + num; + print_s("split"); + print_i((size_t)split->addr); + split->size = excess; + insert_block(split); + compact(); + } + } + return ptr; + } + prev = ptr; + ptr = ptr->next; + } + // no matching free blocks + // see if any other blocks available + size_t new_top = top + num; + if (heap->avail != NULL && new_top <= TA_HEAP_LIMIT) { + ptr = heap->avail; + heap->avail = ptr->next; + ptr->addr = (void *)top; + ptr->next = heap->used; + ptr->size = num; + heap->used = ptr; + heap->top = new_top; + return ptr; + } + return NULL; +} + +void *ta_alloc(size_t num) { + Block *block = alloc_block(num); + if (block != NULL) { + return block->addr; + } + return NULL; +} + +static void memset(void *ptr, uint8_t c, size_t num) { + size_t *ptrw = (size_t *)ptr; + size_t numw = (num & -sizeof(size_t)) / sizeof(size_t); + size_t cw = c; + cw = (cw << 24) | (cw << 16) | (cw << 8) | cw; + #ifdef __LP64__ + cw |= (cw << 32); + #endif + while (numw--) { + *ptrw++ = cw; + } + num &= (sizeof(size_t) - 1); + uint8_t *ptrb = (uint8_t *)ptrw; + while (num--) { + *ptrb++ = c; + } +} + +void *ta_calloc(size_t num, size_t size) { + num *= size; + Block *block = alloc_block(num); + if (block != NULL) { + memset(block->addr, 0, num); + return block->addr; + } + return NULL; +} + +static size_t count_blocks(Block *ptr) { + size_t num = 0; + while (ptr != NULL) { + num++; + ptr = ptr->next; + } + return num; +} + +size_t ta_num_free() { + return count_blocks(heap->free); +} + +size_t ta_num_used() { + return count_blocks(heap->used); +} + +size_t ta_num_avail() { + return count_blocks(heap->avail); +} + +bool ta_check() { + return TA_HEAP_BLOCKS == ta_num_free() + ta_num_used() + ta_num_avail(); +} |
