metal-cos/lib/libc/malloc.c
2023-08-19 22:14:06 -04:00

185 lines
3.6 KiB
C

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
typedef struct Header {
uint16_t magic;
uint16_t status;
uint32_t size;
struct Header *next;
} Header;
#define HEAP_MAGIC 0x8051
#define HEAP_MIN_POOLSIZE (64 - sizeof(Header))
#define HEAP_MAX_POOLSIZE (128*1024 - sizeof(Header))
#define HEAP_POOLS (12)
#define PGSIZE 4096
#define HEAP_INCREMENT (PGSIZE / 64)
typedef struct HeapPool {
uint32_t blocksInuse;
uint32_t blocksFree;
Header *free;
uint64_t base;
uint64_t top;
} HeapPool;
#define ROUNDUP(_x, _n) (((_x) + (_n) - 1) & ~((_n) - 1))
static HeapPool pool[HEAP_POOLS] = {
{ 0, 0, (Header *)0, 0x400000000, 0x400000000 }, // 64B
{ 0, 0, (Header *)0, 0x410000000, 0x410000000 }, // 128B
{ 0, 0, (Header *)0, 0x420000000, 0x420000000 }, // 256B
{ 0, 0, (Header *)0, 0x430000000, 0x430000000 }, // 512B
{ 0, 0, (Header *)0, 0x440000000, 0x440000000 }, // 1KB
{ 0, 0, (Header *)0, 0x450000000, 0x450000000 }, // 2KB
{ 0, 0, (Header *)0, 0x460000000, 0x460000000 }, // 4KB
{ 0, 0, (Header *)0, 0x470000000, 0x470000000 }, // 8KB
{ 0, 0, (Header *)0, 0x480000000, 0x480000000 }, // 16KB
{ 0, 0, (Header *)0, 0x490000000, 0x490000000 }, // 32KB
{ 0, 0, (Header *)0, 0x4A0000000, 0x4A0000000 }, // 64KB
{ 0, 0, (Header *)0, 0x4B0000000, 0x4B0000000 }, // 128KB
};
static HeapPool largePool = { 0, 0, (Header *)0, 0x4C000000, 0x4C000000 }; // 128KB+
static int
size_to_idx(size_t sz)
{
int i;
size_t bucketSz = 64;
for (i = 0; ; i++) {
if ((bucketSz - sizeof(Header)) >= sz) {
return i;
}
bucketSz *= 2;
}
}
static void
grow_small(int idx)
{
int i;
size_t bucketSz = (1 << idx) * 64; // Compute bucket size
char *addr = (char *)pool[idx].top;
addr = (char *)mmap(addr, bucketSz * HEAP_INCREMENT,
PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED,
-1, 0);
if (addr == NULL) {
return;
}
// Update top pointer
pool[idx].top += bucketSz * HEAP_INCREMENT;
for (i = 0; i < HEAP_INCREMENT; i++) {
Header *obj = (Header *)(addr + i * bucketSz);
obj->magic = HEAP_MAGIC;
obj->status = 0;
obj->size = bucketSz - sizeof(Header);
obj->next = pool[idx].free;
pool[idx].free = obj;
}
}
static void *
malloc_small(size_t sz)
{
int idx = size_to_idx(sz);
Header *hdr;
// Grow pool if freelist is empty
if (pool[idx].free == 0)
grow_small(idx);
// Malloc failed
if (pool[idx].free == 0)
return NULL;
hdr = pool[idx].free;
pool[idx].free = hdr->next;
return (void *)(hdr + 1);
}
static void
free_small(Header *mem)
{
int idx = size_to_idx(mem->size);
mem->next = pool[idx].free;
pool[idx].free = mem;
}
static void *
malloc_large(size_t sz)
{
uintptr_t ptr = largePool.top;
uintptr_t realSz = ROUNDUP(sz, 4096);
Header *addr;
addr = (Header *)mmap((void *)ptr, realSz,
PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED,
-1, 0);
if (addr == NULL) {
return NULL;
}
addr->magic = HEAP_MAGIC;
addr->status = 0;
addr->size = realSz;
addr->next = 0;
largePool.top += realSz;
return (void *)(addr + 1);
}
static void
free_large(Header *mem)
{
munmap(mem, mem->size);
}
void *
calloc(size_t num, size_t sz)
{
return malloc(num*sz);
}
void *
malloc(size_t sz)
{
if (sz > HEAP_MAX_POOLSIZE)
return malloc_large(sz);
else
return malloc_small(sz);
}
void
free(void *mem)
{
Header *hdr = (Header *)mem;
hdr--;
if (mem == NULL)
return;
assert(hdr->magic == HEAP_MAGIC);
if (hdr->size > HEAP_MAX_POOLSIZE)
free_large(hdr);
else
free_small(hdr);
}