384 lines
6.7 KiB
C
384 lines
6.7 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;
|
|
|
|
extern uint8_t __heap_base;
|
|
extern uint8_t __data_end;
|
|
|
|
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;
|
|
static size_t current_pages;
|
|
|
|
/**
|
|
* 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
|
|
|
|
uint32_t ta_grow(uint32_t pages)
|
|
{
|
|
return __builtin_wasm_memory_grow(0, pages);
|
|
}
|
|
|
|
uint32_t ta_page_count()
|
|
{
|
|
return __builtin_wasm_memory_size(0);
|
|
}
|
|
|
|
void ta_init(const size_t heap_blocks, const size_t split_thresh, const size_t alignment)
|
|
{
|
|
current_pages = ta_page_count();
|
|
void *limit = (void *)ta_PAGE_SIZE(current_pages);
|
|
|
|
heap = (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;
|
|
}
|
|
|
|
void free(void *ptr)
|
|
{
|
|
Block *block = heap->used;
|
|
Block *prev = NULL;
|
|
while (block != NULL)
|
|
{
|
|
if (ptr == block->addr)
|
|
{
|
|
if (prev)
|
|
{
|
|
prev->next = block->next;
|
|
}
|
|
else
|
|
{
|
|
heap->used = block->next;
|
|
}
|
|
insert_block(block);
|
|
#ifndef TA_DISABLE_COMPACT
|
|
compact();
|
|
#endif
|
|
}
|
|
prev = block;
|
|
block = block->next;
|
|
}
|
|
}
|
|
|
|
size_t ta_ptr_size(void *ptr)
|
|
{
|
|
size_t result = 0;
|
|
|
|
Block *block = heap->used;
|
|
while(block != NULL)
|
|
{
|
|
if(ptr == block->addr)
|
|
{
|
|
result = block->size;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static size_t ta_bytes_to_pages(size_t count)
|
|
{
|
|
size_t pages = count/(1024*64);
|
|
return pages > 0 ? pages : 1;
|
|
}
|
|
|
|
void *malloc(size_t num)
|
|
{
|
|
Block *block = alloc_block(num);
|
|
|
|
if(block == NULL)
|
|
{
|
|
ta_grow(ta_bytes_to_pages(num));
|
|
size_t new_pages = ta_page_count();
|
|
size_t page_diff = new_pages - current_pages;
|
|
if(page_diff > 0)
|
|
{
|
|
current_pages = new_pages;
|
|
heap_limit = (void *)ta_PAGE_SIZE(current_pages);
|
|
|
|
block = alloc_block(num);
|
|
}
|
|
}
|
|
|
|
return block != NULL ? block->addr : 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 *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;
|
|
}
|
|
|
|
void *realloc(void *ptr, size_t size)
|
|
{
|
|
void *new_ptr = malloc(size);
|
|
size_t prev_size = ta_ptr_size(ptr);
|
|
|
|
__builtin_memcpy(new_ptr, ptr, prev_size);
|
|
|
|
free(ptr);
|
|
|
|
return new_ptr;
|
|
}
|
|
|
|
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();
|
|
}
|