diff --git a/ya_block.c b/ya_block.c index d84c1fe..a518aef 100644 --- a/ya_block.c +++ b/ya_block.c @@ -66,8 +66,8 @@ void block_print_range(intptr_t *start, intptr_t *end) { intptr_t size; for (block = start; block < end; block += size) { size = block_size(block); - printf("Block at %p size = %ld alloc = %d\n", - block, size, block_is_alloc(block)); + ya_debug("%d %p:%ld\n", + block_is_alloc(block), block, size); } } #endif @@ -164,8 +164,8 @@ intptr_t *block_split(intptr_t *block, intptr_t size) { return block + size; } -/* Try to find a free block at least size words big by walking the boundary - * tags. If no block is found the heap is grown adequately. +/* Try to find a free block at least size min_size words large by walking the + * boundary tags. Does not grow the heap. * Returns a pointer to the block or NULL in case of failure. */ intptr_t *block_find(intptr_t min_size) { intptr_t *block; @@ -176,8 +176,8 @@ intptr_t *block_find(intptr_t min_size) { return block; } } - // could not find block, extend heap - return heap_extend(min_size); + // could not find block + return NULL; } /* Initializes the heap by calling sbrk to allocate some starter memory. @@ -195,26 +195,29 @@ intptr_t *heap_init() { heap_start += 2; // space for the first block's tags + dword alignment heap_end = heap_start + size; block_init(heap_start, size); - fl_set_prev(heap_start, NULL); - fl_set_next(heap_start, NULL); + fl_free(heap_start); ya_debug("heap_init: start = %p, end = %p, size = %ld\n", heap_start, heap_end, size); return heap_start; } -/* Extends the heap by at least size_w words by calling sbrk. +/* Extends the heap by at least n_bytes bytes by calling sbrk. * Returns a pointer to the last (free) block or NULL in case of failure. */ -intptr_t *heap_extend(intptr_t size) { - size = block_fit(round_to(WORD_SIZE * size, CHUNK_SIZE)); +intptr_t *heap_extend(size_t n_bytes) { + // request an integer number of blocks + intptr_t size = block_fit(round_to(n_bytes, CHUNK_SIZE)); void *ptr = sbrk(WORD_SIZE * size); - if (ptr == (void *) - 1) { + if (ptr == (void *) -1) { return NULL; } intptr_t *block = ptr; // == old heap_end heap_end = block + size; block_init(block, size); + fl_free(block); + fl_join(block); + block = block_join(block); ya_debug("heap_extend: old end = %p, new end = %p, size = %ld\n", block, heap_end, size); ya_print_blocks(); - return block_join(ptr); + return block; } diff --git a/ya_block.h b/ya_block.h index 5a45687..e5f3d6b 100644 --- a/ya_block.h +++ b/ya_block.h @@ -63,9 +63,9 @@ static inline intptr_t block_size(intptr_t *block) { * Returns the pointer to the start of the heap or NULL in case of failure. */ intptr_t *heap_init(); -/* Extends the heap by at least size_w words by calling sbrk. +/* Extends the heap by at least n_bytes bytes by calling sbrk. * Returns a pointer to the last (free) block or NULL in case of failure. */ -intptr_t *heap_extend(intptr_t size_w); +intptr_t *heap_extend(size_t n_bytes); #ifdef YA_DEBUG /* Prints each block in the range from the block at start to the one at end */ @@ -104,8 +104,8 @@ intptr_t *block_join(intptr_t *block); * Returns a pointer to the second block or NULL if no split occurred. */ intptr_t *block_split(intptr_t *block, intptr_t size); -/* Try to find a free block at least min_size words big by walking the - * boundary tags. If no block is found the heap is grown adequately. +/* Try to find a free block at least min_size words large by walking the + * boundary tags. Does not grow the heap. * Returns a pointer to the block or NULL in case of failure. */ intptr_t *block_find(intptr_t min_size); diff --git a/ya_freelist.c b/ya_freelist.c index b551799..784265b 100644 --- a/ya_freelist.c +++ b/ya_freelist.c @@ -3,15 +3,6 @@ * ya_freelist.c */ -/* Block layout: - * - * -2 -1 0 size-4 size-3 - * +------+------+-------- - - - - - - - --------+------+------+ - * | prev | size | data... | size | next | - * +------+------+-------- - - - - - - - --------+------+------+ - * - */ - /*----------*/ /* Includes */ /*----------*/ @@ -35,7 +26,7 @@ intptr_t *fl_end = NULL; // sorted decreasing #ifdef YA_DEBUG void fl_debug_print() { for (intptr_t *block = fl_start; block != NULL; block = fl_next(block)) { - printf("Free block %p:%ld\n", block, block_size(block)); + ya_debug("%p:%ld\n", block, block_size(block)); } } #endif @@ -62,11 +53,15 @@ void fl_alloc(intptr_t *block) { /* Adds the freed block to the appropriate in the free list, which is kept * in sorted increasing order. */ void fl_free(intptr_t *block) { - intptr_t *next; - for (next = fl_start; next && block < next; next = fl_next(next)) { - // *whistle* + if (!fl_start && !fl_end) { + // add to empty list + fl_set_prev(block, NULL); + fl_set_next(block, NULL); + fl_start = block; + fl_end = block; + return; } - if (next == fl_start) { + if (block < fl_start) { // preprend block to the free list fl_set_prev(block, NULL); fl_set_next(block, fl_start); @@ -74,7 +69,7 @@ void fl_free(intptr_t *block) { fl_start = block; return; } - if (!next) { + if (block > fl_end) { // append block to the free list fl_set_prev(block, fl_end); fl_set_next(block, NULL); @@ -83,6 +78,10 @@ void fl_free(intptr_t *block) { return; } // splice the block in the middle of the list + intptr_t *next; + for (next = fl_start; next && next < block; next = fl_next(next)) { + // *whistle* + } intptr_t *prev = fl_prev(next); fl_set_prev(block, prev); fl_set_next(block, next); @@ -104,6 +103,47 @@ intptr_t *fl_find(intptr_t min_size) { /* Force split of free block [block_size] into [size, block_size - size]. */ void fl_split(intptr_t *block, intptr_t size) { fl_set_next(block, block+size); + if (block == fl_end) { + fl_end = block+size; + } fl_set_prev(block+size, block); } +void fl_join_next(intptr_t *block) { + intptr_t *next = block + block_size(block); + if (fl_next(block) == next) { + // skip a block + ya_debug("fl_join_next: %p:%ld + %p:%ld -> %p:%ld\n", + block, block_size(block), next, block_size(next), + block, block_size(block) + block_size(next)); + fl_set_next(block, fl_next(next)); + fl_set_prev(next, fl_prev(block)); + if (fl_next(next) == NULL) { + fl_end = block; + } + } +} + +void fl_join_prev(intptr_t *block) { + intptr_t *free_prev = fl_prev(block); + if (!free_prev) { + return; + } + // will not segfault because there is at least one block preceding + intptr_t *prev = block - block[-4]; + if (prev == free_prev) { + ya_debug("fl_join_prev: %p:%ld + %p:%ld -> %p:%ld\n", + block, block_size(block), prev, block_size(prev), + prev, block_size(block) + block_size(prev)); + fl_set_prev(block, fl_prev(prev)); + fl_set_next(prev, fl_next(block)); + if (fl_next(block) == NULL) { + fl_end = prev; + } + } +} + +void fl_join(intptr_t *block) { + fl_join_next(block); + fl_join_prev(block); +} diff --git a/ya_freelist.h b/ya_freelist.h index a02576d..dd7563d 100644 --- a/ya_freelist.h +++ b/ya_freelist.h @@ -3,6 +3,15 @@ * ya_freelist.h */ +/* Block layout: + * + * -2 -1 0 size-4 size-3 + * +------+------+-------- - - - - - - - --------+------+------+ + * | prev | size | data... | size | next | + * +------+------+-------- - - - - - - - --------+------+------+ + * + */ + #ifndef YA_FREELIST_H #define YA_FREELIST_H @@ -13,6 +22,7 @@ #include // for intptr_t #include "ya_block.h" +#include "ya_debug.h" /*---------*/ /* Inlines */ @@ -45,7 +55,7 @@ void fl_debug_print(); /* Splices the allocated block out of the free list. */ void fl_alloc(intptr_t *block); -/* Adds the freed block to the start of the free list. */ +/* Adds the freed block to the appropriate place in free list. */ void fl_free(intptr_t *block); /* Returns the first block in the free list at min_size words long. @@ -55,6 +65,10 @@ intptr_t *fl_find(intptr_t min_size); /* Force split of block [block_size] into [size, block_size - size]. */ void fl_split(intptr_t *block, intptr_t size); -/* Joins are unnecessary because the pointers will still be ok after a join. */ +void fl_join_prev(intptr_t *block); + +void fl_join_next(intptr_t *block); + +void fl_join(intptr_t *block); #endif diff --git a/yamalloc.c b/yamalloc.c index 830b4e5..bc3a6d0 100644 --- a/yamalloc.c +++ b/yamalloc.c @@ -37,10 +37,17 @@ void *malloc(size_t n_bytes) { return NULL; } } - intptr_t min_size = block_fit(n_bytes); - intptr_t *block = block_find(min_size); - block_split(block, min_size); + intptr_t size = block_fit(n_bytes); + intptr_t *block = block_find(size); + if (!block) { + block = heap_extend(n_bytes); + } + if (block_split(block, size)) { + // block was indeed split, so splt it in the free list as well + fl_split(block, size); + } block_alloc(block); + fl_alloc(block); return block; } @@ -53,6 +60,8 @@ void free(void *ptr) { return; // TODO: provoke segfault } block_free(block); + fl_free(block); + fl_join(block); block_join(block); } @@ -78,6 +87,7 @@ void *realloc(void *ptr, size_t n_bytes) { } if (n_bytes == 0) { free(ptr); + return NULL; } intptr_t *block = ptr; if (block < heap_start) { @@ -91,26 +101,46 @@ void *realloc(void *ptr, size_t n_bytes) { if (new_size < size) { intptr_t *next = block_split(block, new_size); if (next) { + fl_free(next); // add the next from the free list + fl_join_next(next); block_join_next(next); // coalesce the leftovers } block_alloc(block); + // no need to remove it from the free list, it wasn't in it return block; } intptr_t *next = block + size; + if (next == heap_end || + (!block_is_alloc(next) && next + block_size(next) == heap_end)) { + // grow the heap and extend block + next = heap_extend(n_bytes); + fl_free(block); + fl_join_next(block); + block_join_next(block); + if (block_split(block, new_size)) { + fl_split(block, new_size); + } + block_alloc(block); + fl_alloc(block); + return block; + } // try to use next free block if (next < heap_end) { intptr_t next_size = block_size(next); if (!block_is_alloc(next) && new_size <= size + next_size) { + // try to split the next block at the right size + if (block_split(next, new_size - size)) { + // split successful, must split in the free list too + fl_split(block, new_size); + } + fl_alloc(next); // remove the next block from the free list + fl_join_next(block); block_join_next(block); // coalesce - block_split(block, new_size); // split if possible - // no need to coalesce block_alloc(block); // mark block as allocated return block; } } // resizing failed, so allocate a whole new block and copy - // could handle the case when the block is the last in the heap - // a bit more gracefully and just grow the heap instead of copying intptr_t *new_block = malloc(n_bytes); for (int i = 0; i < size; i++) { new_block[i] = block[i];