ClassiCube/third_party/tinyalloc/tinyalloc.c
2024-09-10 18:39:20 +10:00

270 lines
6.6 KiB
C

#include "tinyalloc.h"
#include <stdint.h>
#ifdef TA_DEBUG
extern void print_s(char *);
extern void print_i(size_t);
#else
#define print_s(X)
#define print_i(X)
#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 *fresh; // first available blank block
size_t top; // top free addr
} Heap;
static Heap *heap = NULL;
static const void *heap_limit = NULL;
static size_t heap_split_thresh;
static size_t heap_alignment;
static size_t heap_max_blocks;
/**
* If compaction is enabled, inserts block
* into free list, sorted by addr.
* If disabled, add block has new head of
* the free list.
*/
static void insert_block(Block *block) {
#ifndef TA_DISABLE_COMPACT
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;
#else
block->next = heap->free;
heap->free = block;
#endif
}
#ifndef TA_DISABLE_COMPACT
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->fresh;
heap->fresh = scan;
scan->addr = 0;
scan->size = 0;
scan = scan_next;
}
}
static void compact() {
Block *ptr = heap->free;
Block *prev;
Block *scan;
while (ptr != NULL) {
prev = ptr;
scan = ptr->next;
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 =
(size_t)prev->addr - (size_t)ptr->addr + prev->size;
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 = ptr->next;
}
}
#endif
bool ta_init(const void *base, const void *limit, const size_t heap_blocks, const size_t split_thresh, const size_t alignment) {
heap = (Heap *)base;
heap_limit = limit;
heap_split_thresh = split_thresh;
heap_alignment = alignment;
heap_max_blocks = heap_blocks;
heap->free = NULL;
heap->used = NULL;
heap->fresh = (Block *)(heap + 1);
heap->top = (size_t)(heap->fresh + heap_blocks);
Block *block = heap->fresh;
size_t i = heap_max_blocks - 1;
while (i--) {
block->next = block + 1;
block++;
}
block->next = NULL;
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);
#ifndef TA_DISABLE_COMPACT
compact();
#endif
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 + heap_alignment - 1) & -heap_alignment;
while (ptr != NULL) {
const int is_top = ((size_t)ptr->addr + ptr->size >= top) && ((size_t)ptr->addr + num <= (size_t)heap_limit);
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;
#ifndef TA_DISABLE_SPLIT
} else if (heap->fresh != NULL) {
size_t excess = ptr->size - num;
if (excess >= heap_split_thresh) {
ptr->size = num;
Block *split = heap->fresh;
heap->fresh = split->next;
split->addr = (void *)((size_t)ptr->addr + num);
print_s("split");
print_i((size_t)split->addr);
split->size = excess;
insert_block(split);
#ifndef TA_DISABLE_COMPACT
compact();
#endif
}
#endif
}
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->fresh != NULL && new_top <= (size_t)heap_limit) {
ptr = heap->fresh;
heap->fresh = 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 memclear(void *ptr, size_t num) {
size_t *ptrw = (size_t *)ptr;
size_t numw = (num & -sizeof(size_t)) / sizeof(size_t);
while (numw--) {
*ptrw++ = 0;
}
num &= (sizeof(size_t) - 1);
uint8_t *ptrb = (uint8_t *)ptrw;
while (num--) {
*ptrb++ = 0;
}
}
void *ta_calloc(size_t num, size_t size) {
num *= size;
Block *block = alloc_block(num);
if (block != NULL) {
memclear(block->addr, 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_fresh() {
return count_blocks(heap->fresh);
}
bool ta_check() {
return heap_max_blocks == ta_num_free() + ta_num_used() + ta_num_fresh();
}