diff --git a/kernel/memory/heap.c b/kernel/memory/heap.c index 3e755bf..b42a22b 100644 --- a/kernel/memory/heap.c +++ b/kernel/memory/heap.c @@ -7,6 +7,7 @@ #include "../memory/paging.h" #include "../lib/logging.h" #include "../lib/assert.h" +#include "../lib/string.h" #define MIN_EXPAND_SIZE 1024 @@ -151,6 +152,20 @@ static void defragment(void) { } } +// return the node preceding the argument +// in the linked list +// O(n) sequential research +static seg_header* find_pred(seg_header* node) { + for(seg_header* seg = current_segment; + seg != NULL; + seg = seg->next) { + if(seg->next == node) + return node; + } + assert(0); + __builtin_unreachable(); +} + // try to merge the node with the next one // we suppose that the argument is free static inline void try_merge(seg_header* free_node) { @@ -191,28 +206,17 @@ static inline void try_merge(seg_header* free_node) { // if not, we must find the preceding // node in the linked list + seg_header* seg = find_pred(free_node); - for(seg_header* seg = current_segment; - seg != NULL; - seg = seg->next) { - if(seg->next == free_node) { - // we found it! - - // now make it point on the - // new merged node which is - // the 'next' node - seg->next = next; - - // we are done merging! - return; - } - } - // oh no! wtf - // the argument wasn't in the list - assert(0); + // now make it point on the + // new merged node which is + // the 'next' node + seg->next = next; } } + + /* * insert a new segment between to_split and pred * return the new segment @@ -253,7 +257,7 @@ void heap_init(void) { } -void* __attribute__((noinline)) malloc(size_t size) { +void* malloc(size_t size) { // align the size to assure that // the whole structure is alligned size = ((size + 7 ) / 8) * 8; @@ -324,6 +328,63 @@ void* __attribute__((noinline)) malloc(size_t size) { return malloc(size); } +static void realloc_shrink(seg_header* header, size_t size) { + // check if we can actually free some memory + if(header->size - size >= sizeof(seg_header) + MIN_SEGMENT_SIZE) { + split_segment( + find_pred(header), // we need to find the preceding + // node in order to split it + header, + size + ); + } + else { + // the difference is not big enough + // to actually free anything + // let's not even change the size + // in the structure + // so that if we do: + // realloc(ptr, size-X); + // realloc(ptr, size); + // no reallocation will be done + } +} + +// O(n) in case of freeing (size < oldsize) +void* realloc(void* ptr, size_t size) { + if(ptr == NULL) + return malloc(size); + + seg_header* header = ptr - sizeof(seg_header); + if(size <= header->size) {// no need to move + realloc_shrink(header, size); + } + else { + // maybe need reallocation + + // mark the node free + header->free = 1; + unsigned old_size = header->size; + + // hope for the node to merge with another + defragment(); + + + // check the size again + if(header->size >= size) { + // we have enough space now! + header->free = 0; + realloc_shrink(header, size); + } + else { + // still not enough space + // we have to copy the whole thing + void* new_ptr = malloc(size); + memcpy(new_ptr, ptr, old_size); + } + } +} + // O(1) free void free(void *ptr) { diff --git a/kernel/memory/heap.h b/kernel/memory/heap.h index bed7eec..7ee2e72 100644 --- a/kernel/memory/heap.h +++ b/kernel/memory/heap.h @@ -6,6 +6,7 @@ // free memory void* malloc(size_t size); void free(void* p); +void* realloc(void* ptr, size_t size); void heap_init(void); #ifndef NDEBUG