diff --git a/mem.c b/mem.c index 9eee455..695a8bc 100644 --- a/mem.c +++ b/mem.c @@ -1,4 +1,5 @@ #include "gmqcc.h" +#include /* * GMQCC does a lot of allocations on shortly lived objects all of which * call down to malloc/free internally. The overhead involved with these @@ -7,11 +8,24 @@ * that sits ontop of malloc/free. I'd like to thank Lee Salzman for * guiding me in the right direction for designing this. */ -#define GMQCC_MEM_USED 0xEDCA10A1EDCA10A1 -#define GMQCC_MEM_FREE 0xEEF8EEF8EEF8EEF8 -#define GMQCC_MEM_CORE 0x00000000000000AA -#define GMQCC_MEM_BSL -1 -#define GMQCC_MEM_BSR 1 +#define GMQCC_MEM_USED 0xEDCA10A1EDCA10A1 +#define GMQCC_MEM_FREE 0xEEF8EEF8EEF8EEF8 +#define GMQCC_MEM_CORE 0x00000000000000AA +#define GMQCC_MEM_BSL -1 +#define GMQCC_MEM_BSR 1 +#define GMQCC_MEM_DEBUG 1 + +#ifdef GMQCC_MEM_DEBUG +# include +# define GMQCC_MEM_TRACE(TAG, ...) \ + do { \ + printf("[mem:%s]: %s ", TAG, __func__); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + } while (0) +#else +# define GMQCC_MEM_TRACE(TAG, ...) +#endif typedef unsigned long int mem_addr; @@ -21,11 +35,11 @@ static size_t mem_size = 0; /* heap size */ /* read or write to heap */ #define GMQCC_MEM_WRITEHEAP(OFFSET, TYPE, VALUE) *((TYPE *) ((unsigned char*)mem_heap + (OFFSET))) = (VALUE) -#define GMQCC_MEM_READHEAP (OFFSET, TYPE) ((TYPE)*((TYPE *)(((unsigned char*)mem_heap + (OFFSET))))) +#define GMQCC_MEM_READHEAP(OFFSET, TYPE) ((TYPE)*((TYPE *)(((unsigned char*)mem_heap + (OFFSET))))) /* read of write first block to heap */ #define GMQCC_MEM_WRITEFBA(SIZE, ADDR) GMQCC_MEM_WRITEHEAP(mem_look + (SIZE) * sizeof(mem_addr), mem_addr, ADDR) -#define GMQCC_MEM_READFBA(SIZE) GMQCC_MEM_READHEAP (mem_look + (SIZE) * sizeof(mem_addr), ADDR) +#define GMQCC_MEM_READFBA(SIZE) GMQCC_MEM_READHEAP (mem_look + (SIZE) * sizeof(mem_addr), mem_addr) /* read and write block sizes from heap */ #define GMQCC_MEM_WRITEBS(ADDR, SIZE) GMQCC_MEM_WRITEHEAP(ADDR, mem_addr, (SIZE)) @@ -44,7 +58,11 @@ static size_t mem_size = 0; /* heap size */ #define GMQCC_MEM_MARKUSED(ADDR) GMQCC_MEM_WRITEHEAP((ADDR) + 1 * sizeof(mem_addr), mem_addr, GMQCC_MEM_USED) #define GMQCC_MEM_MARKFREE(ADDR) GMQCC_MEM_WRITEHEAP((ADDR) + 1 * sizeof(mem_addr), mem_addr, GMQCC_MEM_FREE) +/* Has block? */ +#define GMQCC_MEM_HASBLOCK(SIZE) (GMQCC_MEM_READFBA(size) != 0) + static void mem_init_table(size_t size) { + GMQCC_MEM_TRACE("flow", "(%lu)", size); size_t i; mem_look = 8 * ((mem_addr)1 << (size - 1)) + sizeof(mem_addr); @@ -58,7 +76,7 @@ static void mem_init_table(size_t size) { GMQCC_MEM_WRITEHEAP(mem_look + sizeof(mem_addr) * size, mem_addr, sizeof(mem_addr)); GMQCC_MEM_WRITEHEAP(sizeof(mem_addr), mem_addr, size); - GMQCC_MEM_MARKFREE (sizeof(mem_addr) << 1); + GMQCC_MEM_MARKFREE (sizeof(mem_addr) * 2); GMQCC_MEM_WRITEHEAP(sizeof(mem_addr) * 3, mem_addr, 0); GMQCC_MEM_WRITEHEAP(sizeof(mem_addr) * 4, mem_addr, 0); } @@ -69,33 +87,171 @@ static size_t mem_getnbs(const size_t need) { size_t s = 1; while (need > b) { - b >>= 1; + b *= 2; s ++; } return s; } +static void mem_removeblock(mem_addr a, size_t size) { + mem_addr p = GMQCC_MEM_GETADDROFPS(a); + mem_addr n = GMQCC_MEM_GETADDROFFS(a); + + GMQCC_MEM_SETADDROFPS(a, ~((mem_addr)0)); + GMQCC_MEM_SETADDROFFS(a, ~((mem_addr)0)); + + /* handle singles in list */ + if ((p == 0) && (n == 0)) { + GMQCC_MEM_WRITEFBA(size, 0); + return; + } + + /* first in list has different sibling semantics */ + if (p == 0) { + GMQCC_MEM_WRITEFBA (size, n); + GMQCC_MEM_SETADDROFPS(n, 0); + return; + } + + /* last item also has special meaning :) */ + if (n == 0) { + GMQCC_MEM_SETADDROFFS(p, 0); + return; + } + + /* middle of list */ + GMQCC_MEM_SETADDROFPS(n, p); + GMQCC_MEM_SETADDROFFS(p, n); +} + +static int mem_createblock(const size_t size) { + mem_addr parent; + int test; + + GMQCC_MEM_TRACE("flow", "(%lu)", size); + if (GMQCC_MEM_HASBLOCK(size)) + return 0; + + if (size > GMQCC_MEM_READHEAP(mem_look, mem_addr)) + abort(); + + /* recrusive ... */ + if ((test = mem_createblock(size + 1)) != 0) + return test; + + /* safe splits assured */ + parent = GMQCC_MEM_READFBA(size + 1); + mem_removeblock(parent, size + 1); + + /* split it */ + GMQCC_MEM_WRITEFBA(size, parent); + { + /* find center and split */ + mem_addr block = parent + 8 * ((mem_addr)1 << (size - 1)); + mem_addr left = parent; + mem_addr right = block; + + GMQCC_MEM_TRACE( + "dump", + "block info:\n left addr: %lu\n right addr: %lu\n prev addr: %lu", + left, right, parent + ); + + /* left half */ + GMQCC_MEM_WRITEHEAP (left, mem_addr, size); + GMQCC_MEM_MARKFREE (left); + GMQCC_MEM_SETADDROFPS(left, 0); + GMQCC_MEM_SETADDROFFS(left, right); + /* right half */ + GMQCC_MEM_WRITEHEAP (right, mem_addr, size); + GMQCC_MEM_MARKFREE (right); + GMQCC_MEM_SETADDROFPS(right, left); + GMQCC_MEM_SETADDROFPS(right, 0); + } + return 0; +} + +static mem_addr mem_allocblock(const size_t size) { + GMQCC_MEM_TRACE("flow", "(%lu)", size); + int test = mem_createblock(size); + mem_addr first; + mem_addr next; + + if (test != 0) + return 0; + + /* first free one */ + first = GMQCC_MEM_READFBA (size); + next = GMQCC_MEM_GETADDROFFS(first); + + mem_removeblock(first, size); + + GMQCC_MEM_WRITEFBA(next, size); + GMQCC_MEM_MARKUSED(first); + + return first; +} + +static int mem_getside(mem_addr addr, const size_t size) { + size_t real = addr - sizeof(mem_addr); + size_t next = ((mem_addr)1 << (size)); + assert((real % 8) == 0); /* blow up */ + real /= 8; + + return ((real % next) == 0)? GMQCC_MEM_BSL : GMQCC_MEM_BSR; +} + +static mem_addr mem_getaddr(mem_addr start, const size_t size) { + size_t length = (((mem_addr)1 << (size - 1)) * 8); + + switch (mem_getside(start, size)) { + case GMQCC_MEM_BSL: return start + length; + case GMQCC_MEM_BSR: return start - length; + } + /* if reached blow up */ + return (abort(), 1); +} + +static void mem_addblock(mem_addr a, size_t s) { + mem_addr first = GMQCC_MEM_READFBA(s); + if (first == 0) { + /* only block */ + GMQCC_MEM_WRITEFBA (s, a); + GMQCC_MEM_SETADDROFPS(a, 0); + GMQCC_MEM_SETADDROFFS(a, 0); + } else { + /* add to front */ + GMQCC_MEM_WRITEFBA (s, a); + GMQCC_MEM_SETADDROFPS(a, 0); + GMQCC_MEM_SETADDROFFS(a, first); + GMQCC_MEM_SETADDROFPS(first, a); + } +} + void mem_init(size_t size) { - size_t alloc = 0; + size_t alloc = size; size_t count = 1; size_t block = 1; + /* blow up if too small */ + assert (sizeof(void*) == sizeof(mem_addr)); + if (!(mem_heap = malloc(size))) abort(); memset(mem_heap, GMQCC_MEM_CORE, size); mem_size = size; - alloc = size - (2 * sizeof(mem_addr)); + alloc -= 2 * sizeof(mem_addr); while (alloc + sizeof(mem_addr) > 8 * block) { - alloc -= sizeof(mem_addr); - block <<= 1; - count ++; + alloc -= sizeof(mem_addr); + block *= 2; + count ++; } /* over shot ? */ - block >>= 1; + block /= 2; count --; mem_init_table(count); @@ -108,10 +264,58 @@ void mem_destroy() { } void *mem_alloc(size_t amount) { + GMQCC_MEM_TRACE("flow", "(%lu)", amount); size_t need = amount + 4 * sizeof(mem_addr); size_t size = mem_getnbs (need); mem_addr block = mem_allocblock(size); - if (!block) return NULL; + GMQCC_MEM_TRACE("dump", "will allocate %lu size block", size); + /* standard behaviour */ + if (block == 0) + return NULL; + GMQCC_MEM_TRACE("dump", "returning offset %lu", block); return mem_heap + block + 4 * sizeof(mem_addr); } + +void mem_free(void *ptr) { + mem_addr start = (mem_addr)(ptr - mem_heap) - 4 * sizeof(mem_addr); + size_t size = GMQCC_MEM_READHEAP(start, mem_addr); + mem_addr addr = mem_getaddr(start, size); + int side = mem_getside(start, size); + + + GMQCC_MEM_TRACE ( + "dump", + "deallocating %s buddy (neighbour at %lu)", + (side == GMQCC_MEM_BSL) ? "left" : "right", + addr + ); + GMQCC_MEM_MARKFREE(start); + + /* while free block merge */ + while ((GMQCC_MEM_READHEAP(addr + 1 * sizeof(mem_addr), mem_addr)) == (mem_addr)GMQCC_MEM_FREE) { + GMQCC_MEM_TRACE("dump", "merging ..."); + mem_removeblock(addr, size); + + /* find new start */ + start = addr < start ? addr : start; + size ++; + + if (size == GMQCC_MEM_READHEAP(mem_look, mem_addr)) + break; /* blow up */ + + addr = mem_getaddr(start, size); + GMQCC_MEM_TRACE("dump", "new block start is %lu, buddy at %lu", start, addr); + } + + /* add it */ + GMQCC_MEM_WRITEBS(start, size); + mem_addblock (start, size); +} + +#include +int main() { + mem_init(1330); + char *p = mem_alloc(sizeof(char) * 5); + /* blows up on second alloc, why? char *x = mem_alloc(200); */ +}