You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
329 lines
7.5 KiB
329 lines
7.5 KiB
#include <stdlib.h> |
|
#include <math.h> |
|
#include <stdio.h> |
|
#include <stdint.h> |
|
#include "lisp.h" |
|
#include <assert.h> |
|
|
|
static Memory MEMORY; |
|
|
|
static size_t alignToPow2(size_t n) { |
|
return pow(2, ceil(log2(n))); |
|
} |
|
|
|
static size_t alignToBlock(size_t n) { |
|
return alignToPow2(MAX(((n + sizeof(Block) - 1) / sizeof(Block)) + 1, 2)); |
|
} |
|
|
|
void memory_init(size_t size) { |
|
MEMORY.used = 0; |
|
MEMORY.size = alignToPow2(MAX(size, 2)); |
|
MEMORY.buffer = malloc(MEMORY.size * sizeof(Block)); |
|
MEMORY.freelist = 0; |
|
MEMORY.buffer[MEMORY.freelist] = (Block) { |
|
.garbage = true, |
|
.size = MEMORY.size, |
|
}; |
|
MEMORY.buffer[MEMORY.freelist].data->next = -1u; |
|
} |
|
|
|
void memory_free(void) { |
|
free(MEMORY.buffer); |
|
} |
|
|
|
Block* memory_get(Pointer data) { |
|
assert(data < MEMORY.size); |
|
return &MEMORY.buffer[data]; |
|
} |
|
|
|
static void iterate_freelist(size_t free_size, |
|
Pointer* free, |
|
Block** free_previous, |
|
bool free_break, |
|
Pointer after_start, |
|
size_t after_size, |
|
Pointer* after, |
|
Block** after_previous, |
|
bool after_break, |
|
Pointer* last, |
|
Block** last_previous) { |
|
Pointer freelist = MEMORY.freelist; |
|
Block* previous = NULL; |
|
Block* block = NULL; |
|
while (freelist != -1u) { |
|
block = memory_get(freelist); |
|
if (block->size >= free_size) { |
|
if (free) *free = freelist; |
|
if (free_previous) *free_previous = previous; |
|
if (free_break) break; |
|
} |
|
|
|
if (freelist + block->size == MEMORY.size) { |
|
if (last) *last = freelist; |
|
if (last_previous) *last_previous = previous; |
|
} |
|
|
|
if (freelist == after_start && |
|
block->size >= after_size) { |
|
if (after) *after = freelist; |
|
if (after_previous) *after_previous = previous; |
|
if (after_break) break; |
|
} |
|
|
|
previous = block; |
|
freelist = block->data->next; |
|
} |
|
} |
|
|
|
static Pointer use_free_slot(Type type, size_t size, Pointer free, Block* previous) { |
|
Block* block = memory_get(free); |
|
if (block->size == size) { |
|
if (previous != NULL) { |
|
previous->data->next = block->data->next; |
|
} else { |
|
MEMORY.freelist = block->data->next; |
|
} |
|
|
|
block->type = type; |
|
block->garbage = false; |
|
return free; |
|
} |
|
|
|
size_t offset = size; |
|
size_t idx = free + offset; |
|
Block* new = block + offset; |
|
new->data->next = block->data->next; |
|
new->size = block->size - size; |
|
new->garbage = true; |
|
|
|
if (previous != NULL) { |
|
previous->data->next = idx; |
|
} else { |
|
MEMORY.freelist = idx; |
|
} |
|
|
|
block->type = type; |
|
block->garbage = false; |
|
block->size = size; |
|
|
|
return free; |
|
} |
|
|
|
Pointer alloc_free_slot(Type type, size_t size, Pointer last, Block* last_previous) { |
|
// Memory_Get last free slot in buffer |
|
Pointer freelist = last; |
|
size_t requiredSize = MEMORY.size; |
|
if (last == -1u) { |
|
freelist = MEMORY.size; |
|
requiredSize += size; |
|
} else { |
|
if (last_previous != NULL) { |
|
last_previous->data->next = NEXT(last); |
|
} else { |
|
MEMORY.freelist = NEXT(last); |
|
} |
|
|
|
requiredSize += size - SIZE(last); |
|
} |
|
|
|
while (MEMORY.size < requiredSize) { |
|
MEMORY.size *= 2; |
|
} |
|
MEMORY.buffer = realloc(MEMORY.buffer, MEMORY.size * sizeof(Block)); |
|
|
|
Block* block = memory_get(freelist); |
|
block->type = type; |
|
block->garbage = false; |
|
block->size = size; |
|
|
|
if (freelist + size < MEMORY.size) { |
|
block += size; |
|
block->size = MEMORY.size - freelist - size; |
|
block->garbage = true; |
|
block->data->next = MEMORY.freelist; |
|
MEMORY.freelist = freelist + size; |
|
} else { |
|
MEMORY.freelist = -1u; |
|
} |
|
|
|
return freelist; |
|
} |
|
|
|
Pointer memory_new(Type type, size_t size) { |
|
/* if (size == 0) { */ |
|
/* return -1u; // Why alloc an element of zero size? */ |
|
/* } */ |
|
|
|
size = alignToBlock(size); |
|
|
|
Pointer free = -1u; |
|
Block* free_previous = NULL; |
|
Pointer last = -1u; |
|
Block* last_previous = NULL; |
|
iterate_freelist(size, |
|
&free, |
|
&free_previous, |
|
true, |
|
0, |
|
0, |
|
NULL, |
|
NULL, |
|
false, |
|
&last, |
|
&last_previous); |
|
|
|
if (free != -1u) { |
|
return use_free_slot(type, size, free, free_previous); |
|
} |
|
|
|
/* return alloc_free_slot(type, size, last, last_previous); */ |
|
return alloc_free_slot(type, size, last, last_previous); |
|
} |
|
|
|
void memory_destroy(Pointer ptr) { |
|
Block* block = memory_get(ptr); |
|
Block* last = NULL; |
|
Block* beforeNext = NULL; |
|
|
|
Pointer freelist = MEMORY.freelist; |
|
Pointer previous = -1u; |
|
Pointer next = -1u; |
|
while (freelist != -1u && |
|
(previous == -1u || next == -1u)) { |
|
Block* free = memory_get(freelist); |
|
if (freelist + free->size == ptr) { |
|
previous = freelist; |
|
} |
|
|
|
if (freelist == ptr + block->size) { |
|
next = freelist; |
|
beforeNext = last; |
|
} |
|
last = free; |
|
freelist = free->data->next; |
|
} |
|
|
|
if (next != -1u) { |
|
Block* nblk = memory_get(next); |
|
block->size += nblk->size; |
|
if (beforeNext != NULL) { |
|
last->data->next = nblk->data->next; |
|
} else { |
|
MEMORY.freelist = nblk->data->next; |
|
} |
|
} |
|
|
|
if (previous != -1u) { |
|
Block* pblk = memory_get(previous); |
|
pblk->size += block->size; |
|
} else { |
|
block->data->next = MEMORY.freelist; |
|
MEMORY.freelist = ptr; |
|
} |
|
} |
|
|
|
Pointer memory_resize(Pointer ptr, size_t new_size) { |
|
Block* pblock = memory_get(ptr); |
|
Block* block; |
|
size_t prev_size = pblock->size; |
|
new_size = MAX(alignToBlock(new_size), 2); |
|
|
|
size_t remaining = new_size - prev_size; |
|
|
|
if (new_size == prev_size) { |
|
return ptr; |
|
} |
|
|
|
if (new_size < prev_size) { |
|
// Shrink |
|
Pointer rest = ptr + new_size; |
|
block = memory_get(rest); |
|
block->size = prev_size - new_size; |
|
block->data->next = MEMORY.freelist; |
|
block->garbage = true; |
|
MEMORY.freelist = rest; |
|
pblock->size = new_size; |
|
return ptr; |
|
} |
|
|
|
Pointer free = -1u; |
|
Block* free_previous = NULL; |
|
Pointer after = -1u; |
|
Block* after_previous = NULL; |
|
Pointer last = -1u; |
|
Block* last_previous = NULL; |
|
iterate_freelist(new_size, |
|
&free, |
|
&free_previous, |
|
false, |
|
ptr + prev_size, |
|
remaining, |
|
&after, |
|
&after_previous, |
|
true, |
|
&last, |
|
&last_previous); |
|
|
|
if (after != -1u) { |
|
block = memory_get(after); |
|
if (block->size > remaining) { |
|
Pointer restPtr = after + remaining; |
|
Block* rest = memory_get(restPtr); |
|
rest->size = block->size - remaining; |
|
NEXT(restPtr) = NEXT(after); |
|
if (after_previous != NULL) { |
|
after_previous->data->next = restPtr; |
|
} else { |
|
MEMORY.freelist = restPtr; |
|
} |
|
} else { |
|
if (after_previous != NULL) { |
|
after_previous->data->next = block->data->next; |
|
} else { |
|
MEMORY.freelist = block->data->next; |
|
} |
|
} |
|
block->garbage = false; |
|
pblock->size = new_size; |
|
return ptr; |
|
} |
|
|
|
|
|
if (free != -1u || |
|
(ptr + pblock->size != last && |
|
ptr + pblock->size != MEMORY.size)) { |
|
if (free == -1u) { |
|
free = alloc_free_slot(pblock->type, new_size, last, last_previous); |
|
} else { |
|
free = use_free_slot(pblock->type, new_size, free, free_previous); |
|
} |
|
|
|
Pointer offset = free - ptr; |
|
for (size_t i = 0; i < MEMORY.size; ) { |
|
block = memory_get(i); |
|
switch (block->type) { |
|
case CONS: |
|
if (CONS(i).car == ptr) CONS(i).car += offset; |
|
if (CONS(i).cdr == ptr) CONS(i).cdr += offset; |
|
break; |
|
case TABLE: // TODO: handle for Table |
|
case ARRAY: // TODO: handle for Vector |
|
default: break; |
|
} |
|
i += block->size; |
|
} |
|
|
|
// Copy old memory to new memory |
|
for (size_t i = 1; i <= prev_size; i++) { |
|
MEMORY.buffer[free + i] = MEMORY.buffer[ptr + i]; |
|
} |
|
|
|
// Free old memory |
|
memory_destroy(ptr); |
|
|
|
return free; |
|
} |
|
|
|
alloc_free_slot(pblock->type, remaining, last, last_previous); |
|
return ptr; |
|
}
|
|
|