/* * Yet Another Malloc * ya_freelist.c */ /*----------*/ /* Includes */ /*----------*/ #include #include "ya_freelist.h" #include "ya_block.h" /*---------*/ /* Globals */ /*---------*/ intptr_t *fl_start = NULL; // sorted increasing intptr_t *fl_end = NULL; // sorted decreasing /*-----------*/ /* Functions */ /*-----------*/ /* Splices the allocated block out of the free list. */ void fl_alloc(intptr_t *block) { intptr_t size = block_size(block); intptr_t *prev = fl_prev(block); intptr_t *next = fl_next(block); fl_set_prev(block, NULL); fl_set_next(block, NULL); if (prev) { fl_set_next(prev, next); } else { fl_start = next; } if (next) { fl_set_prev(next, prev); } else { fl_end = prev; } } /* Adds the freed block to the appropriate in the free list, which is kept * in sorted increasing order. */ void fl_free(intptr_t *block) { 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 (block < fl_start) { // preprend block to the free list fl_set_prev(block, NULL); fl_set_next(block, fl_start); fl_set_prev(fl_start, block); fl_start = block; return; } if (block > fl_end) { // append block to the free list fl_set_prev(block, fl_end); fl_set_next(block, NULL); fl_set_next(fl_end, block); fl_end = 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); fl_set_prev(next, block); fl_set_next(prev, block); } /* Returns the first block in the free list at min_size words long. * Returns NULL if no adequate block is found. Does not grow the heap. */ intptr_t *fl_find(intptr_t min_size) { for (intptr_t *block = fl_start; block != NULL; block = fl_next(block)) { if (min_size <= block_size(block)) { return block; } } return NULL; } /* Mends the free list after a free block has just been split into two blocks, * block and new_next. */ void fl_mend_split(intptr_t *block, intptr_t *new_next) { if (!new_next) { return; // block was not split } intptr_t *old_next = fl_next(new_next); fl_set_next(block, new_next); fl_set_prev(new_next, block); if (old_next) { fl_set_prev(old_next, new_next); } else { fl_end = new_next; } } 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); } /* Returns a pointer to the first free block. */ intptr_t *fl_get_start() { return fl_start; } /* Returns a pointer to the last free block. */ intptr_t *fl_get_end() { return fl_end; } #ifdef YA_DEBUG void fl_debug_print() { for (intptr_t *block = fl_start; block != NULL; block = fl_next(block)) { ya_debug("%p:%ld\n", block, block_size(block)); } } /* Checks that block's information is consistent. The previous pointer should * point to correct_prev, which was the previous free block during free list * iteration. * Returns -1 on error, 0 otherwise. */ int fl_check_one(intptr_t *block, intptr_t *correct_prev) { if (block < heap_start || block >= heap_end) { ya_debug("fl_check_one: block %p out of bounds\n", block); return -1; } intptr_t *prev = fl_prev(block); if (prev && (prev < heap_start || prev >= heap_end)) { ya_debug("fl_check_one: previous pointer %p out of bounds [%p,%p[\n", prev, heap_start, heap_end); return -1; } if (correct_prev != prev) { ya_debug("fl_check_one(%p): previous pointer mismatch, " "should be %p, not %p\n", block, correct_prev, prev); return -1; } intptr_t *next = fl_next(block); if (next && (next < heap_start || next >= heap_end)) { ya_debug("fl_check_one: next pointer %p out of bounds [%p,%p[\n", next, heap_start, heap_end); return -1; } return 0; } /* Checks the free list for consistency. * Returns -1 on error, the total number of free blocks otherwise. */ int fl_check() { if (!fl_start) { if (fl_end) { ya_debug("fl_check: fl_start == NULL but fl_end == %p\n", fl_end); return -1; } return 0; } if (!fl_end) { ya_debug("fl_check: fl_end == NULL but fl_start == %p\n", fl_start); return -1; } if (fl_start < heap_start || fl_start >= heap_end) { ya_debug("fl_check: fl_start %p out of bounds\n", fl_start); return -1; } if (fl_end < heap_start || fl_end >= heap_end) { ya_debug("fl_check: fl_ebd %p out of bounds\n", fl_end); return -1; } int num_free = 0; intptr_t *prev = NULL; intptr_t *block; for (block = fl_start; block; block = fl_next(block)) { num_free++; if (fl_check_one(block, prev)) { return -1; } prev = block; } return num_free; } #endif // def YA_DEBUG