static void test_check_free_block(void) { Heap_Block *block = NULL; Heap_Block *next_block = NULL; Heap_Block *first_free_block = NULL; Heap_Block *secound_free_block = NULL; void *p1 = NULL; puts( "test the _Heap_Walk_check_free_block() function" ); puts( "\tset a previous size for the next block which is not equal to the size of the actual block" ); test_heap_init_default(); block = _Heap_Free_list_first( &TestHeap ); next_block = _Heap_Block_at( block, _Heap_Block_size( block ) ); next_block->prev_size = _Heap_Block_size( block ) - 1; test_call_heap_walk( false ); puts( "\tclear the previous_used flag of the first free block after an used block" ); test_heap_init_default(); p1 = test_allocate_block(); block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size ); first_free_block = _Heap_Free_list_first( &TestHeap ); first_free_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED; first_free_block->prev_size = _Heap_Block_size( block ); _Heap_Free_list_insert_after( first_free_block, block ); test_call_heap_walk( false ); puts( "\ttake a free block out of the free list" ); test_heap_init_custom(); test_create_heap_with_gaps(); first_free_block = _Heap_Free_list_first( &TestHeap ); secound_free_block = first_free_block->next; _Heap_Free_list_remove( secound_free_block ); test_call_heap_walk( false ); }
static Heap_Block *_Heap_Block_allocate_from_end( Heap_Control *heap, Heap_Block *block, Heap_Block *free_list_anchor, uintptr_t alloc_begin, uintptr_t alloc_size ) { Heap_Statistics *const stats = &heap->stats; uintptr_t block_begin = (uintptr_t) block; uintptr_t block_size = _Heap_Block_size( block ); uintptr_t block_end = block_begin + block_size; Heap_Block *const new_block = _Heap_Block_of_alloc_area( alloc_begin, heap->page_size ); uintptr_t const new_block_begin = (uintptr_t) new_block; uintptr_t const new_block_size = block_end - new_block_begin; block_end = new_block_begin; block_size = block_end - block_begin; _HAssert( block_size >= heap->min_block_size ); _HAssert( new_block_size >= heap->min_block_size ); /* Statistics */ stats->free_size += block_size; if ( _Heap_Is_prev_used( block ) ) { _Heap_Free_list_insert_after( free_list_anchor, block ); free_list_anchor = block; /* Statistics */ ++stats->free_blocks; } else { Heap_Block *const prev_block = _Heap_Prev_block( block ); uintptr_t const prev_block_size = _Heap_Block_size( prev_block ); block = prev_block; block_begin = (uintptr_t) block; block_size += prev_block_size; } block->size_and_flag = block_size | HEAP_PREV_BLOCK_USED; new_block->prev_size = block_size; new_block->size_and_flag = new_block_size; _Heap_Block_split( heap, new_block, free_list_anchor, alloc_size ); return new_block; }
static void test_check_free_list(void) { void *p1 = NULL; Heap_Block *first_free_block = NULL; Heap_Block *secound_free_block = NULL; Heap_Block *third_free_block = NULL; Heap_Block *used_block = NULL; puts( "testing the _Heap_Walk_check_free_list() function" ); puts( "\tno free blocks" ); test_heap_init_custom(); test_fill_heap(); test_call_heap_walk( true ); puts( "\tcreate a loop in the free list" ); test_heap_init_default(); test_create_heap_with_gaps(); /* find free blocks */ first_free_block = _Heap_Free_list_first( &TestHeap ); secound_free_block = first_free_block->next; third_free_block = secound_free_block->next; /* create a loop */ third_free_block->next = secound_free_block; secound_free_block->prev = third_free_block; test_call_heap_walk( false ); puts( "\tput a block outside the heap to the free list" ); test_heap_init_default(); first_free_block = _Heap_Free_list_first( &TestHeap ); first_free_block->next = TestHeap.first_block - 1; test_call_heap_walk( false ); puts( "\tput a block on the free list, which is not page-aligned" ); test_heap_init_custom(); test_create_heap_with_gaps(); first_free_block = _Heap_Free_list_first( &TestHeap ); first_free_block->next = (Heap_Block *) ((uintptr_t) first_free_block->next + CPU_ALIGNMENT); first_free_block->next->prev = first_free_block; test_call_heap_walk( false ); puts( "\tput a used block on the free list" ); test_heap_init_custom(); test_create_heap_with_gaps(); p1 = test_allocate_block(); first_free_block = _Heap_Free_list_first( &TestHeap ); used_block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size ); _Heap_Free_list_insert_after( first_free_block, used_block ); test_call_heap_walk( false ); }
bool _Heap_Free( Heap_Control *heap, void *alloc_begin_ptr ) { Heap_Statistics *const stats = &heap->stats; uintptr_t alloc_begin = (uintptr_t) alloc_begin_ptr; Heap_Block *block = _Heap_Block_of_alloc_area( alloc_begin, heap->page_size ); Heap_Block *next_block = NULL; uintptr_t block_size = 0; uintptr_t next_block_size = 0; bool next_is_free = false; if ( !_Heap_Is_block_in_heap( heap, block ) ) { return false; } block_size = _Heap_Block_size( block ); next_block = _Heap_Block_at( block, block_size ); if ( !_Heap_Is_block_in_heap( heap, next_block ) ) { _HAssert( false ); return false; } if ( !_Heap_Is_prev_used( next_block ) ) { _HAssert( false ); return false; } next_block_size = _Heap_Block_size( next_block ); next_is_free = next_block != heap->last_block && !_Heap_Is_prev_used( _Heap_Block_at( next_block, next_block_size )); if ( !_Heap_Is_prev_used( block ) ) { uintptr_t const prev_size = block->prev_size; Heap_Block * const prev_block = _Heap_Block_at( block, -prev_size ); if ( !_Heap_Is_block_in_heap( heap, prev_block ) ) { _HAssert( false ); return( false ); } /* As we always coalesce free blocks, the block that preceedes prev_block must have been used. */ if ( !_Heap_Is_prev_used ( prev_block) ) { _HAssert( false ); return( false ); } if ( next_is_free ) { /* coalesce both */ uintptr_t const size = block_size + prev_size + next_block_size; _Heap_Free_list_remove( next_block ); stats->free_blocks -= 1; prev_block->size_and_flag = size | HEAP_PREV_BLOCK_USED; next_block = _Heap_Block_at( prev_block, size ); _HAssert(!_Heap_Is_prev_used( next_block)); next_block->prev_size = size; } else { /* coalesce prev */ uintptr_t const size = block_size + prev_size; prev_block->size_and_flag = size | HEAP_PREV_BLOCK_USED; next_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED; next_block->prev_size = size; } } else if ( next_is_free ) { /* coalesce next */ uintptr_t const size = block_size + next_block_size; _Heap_Free_list_replace( next_block, block ); block->size_and_flag = size | HEAP_PREV_BLOCK_USED; next_block = _Heap_Block_at( block, size ); next_block->prev_size = size; } else { /* no coalesce */ /* Add 'block' to the head of the free blocks list as it tends to produce less fragmentation than adding to the tail. */ _Heap_Free_list_insert_after( _Heap_Free_list_head( heap), block ); block->size_and_flag = block_size | HEAP_PREV_BLOCK_USED; next_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED; next_block->prev_size = block_size; /* Statistics */ ++stats->free_blocks; if ( stats->max_free_blocks < stats->free_blocks ) { stats->max_free_blocks = stats->free_blocks; } } /* Statistics */ --stats->used_blocks; ++stats->frees; stats->free_size += block_size; return( true ); }
bool _Heap_Free( Heap_Control *heap, void *alloc_begin_ptr ) { Heap_Statistics *const stats = &heap->stats; uintptr_t alloc_begin; Heap_Block *block; Heap_Block *next_block = NULL; uintptr_t block_size = 0; uintptr_t next_block_size = 0; bool next_is_free = false; /* * If NULL return true so a free on NULL is considered a valid release. This * is a special case that could be handled by the in heap check how-ever that * would result in false being returned which is wrong. */ if ( alloc_begin_ptr == NULL ) { return true; } alloc_begin = (uintptr_t) alloc_begin_ptr; block = _Heap_Block_of_alloc_area( alloc_begin, heap->page_size ); if ( !_Heap_Is_block_in_heap( heap, block ) ) { return false; } _Heap_Protection_block_check( heap, block ); block_size = _Heap_Block_size( block ); next_block = _Heap_Block_at( block, block_size ); if ( !_Heap_Is_block_in_heap( heap, next_block ) ) { return false; } _Heap_Protection_block_check( heap, next_block ); if ( !_Heap_Is_prev_used( next_block ) ) { _Heap_Protection_block_error( heap, block ); return false; } if ( !_Heap_Protection_determine_block_free( heap, block ) ) { return true; } next_block_size = _Heap_Block_size( next_block ); next_is_free = next_block != heap->last_block && !_Heap_Is_prev_used( _Heap_Block_at( next_block, next_block_size )); if ( !_Heap_Is_prev_used( block ) ) { uintptr_t const prev_size = block->prev_size; Heap_Block * const prev_block = _Heap_Block_at( block, -prev_size ); if ( !_Heap_Is_block_in_heap( heap, prev_block ) ) { _HAssert( false ); return( false ); } /* As we always coalesce free blocks, the block that preceedes prev_block must have been used. */ if ( !_Heap_Is_prev_used ( prev_block) ) { _HAssert( false ); return( false ); } if ( next_is_free ) { /* coalesce both */ uintptr_t const size = block_size + prev_size + next_block_size; _Heap_Free_list_remove( next_block ); stats->free_blocks -= 1; prev_block->size_and_flag = size | HEAP_PREV_BLOCK_USED; next_block = _Heap_Block_at( prev_block, size ); _HAssert(!_Heap_Is_prev_used( next_block)); next_block->prev_size = size; } else { /* coalesce prev */ uintptr_t const size = block_size + prev_size; prev_block->size_and_flag = size | HEAP_PREV_BLOCK_USED; next_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED; next_block->prev_size = size; } } else if ( next_is_free ) { /* coalesce next */ uintptr_t const size = block_size + next_block_size; _Heap_Free_list_replace( next_block, block ); block->size_and_flag = size | HEAP_PREV_BLOCK_USED; next_block = _Heap_Block_at( block, size ); next_block->prev_size = size; } else { /* no coalesce */ /* Add 'block' to the head of the free blocks list as it tends to produce less fragmentation than adding to the tail. */ _Heap_Free_list_insert_after( _Heap_Free_list_head( heap), block ); block->size_and_flag = block_size | HEAP_PREV_BLOCK_USED; next_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED; next_block->prev_size = block_size; /* Statistics */ ++stats->free_blocks; if ( stats->max_free_blocks < stats->free_blocks ) { stats->max_free_blocks = stats->free_blocks; } } /* Statistics */ --stats->used_blocks; ++stats->frees; stats->free_size += block_size; return( true ); }
void _Heap_Block_split( Heap_Control *heap, Heap_Block *block, Heap_Block *free_list_anchor, uintptr_t alloc_size ) { Heap_Statistics *const stats = &heap->stats; uintptr_t const page_size = heap->page_size; uintptr_t const min_block_size = heap->min_block_size; uintptr_t const min_alloc_size = min_block_size - HEAP_BLOCK_HEADER_SIZE; uintptr_t const block_size = _Heap_Block_size( block ); uintptr_t const used_size = _Heap_Max( alloc_size, min_alloc_size ) + HEAP_BLOCK_HEADER_SIZE; uintptr_t const used_block_size = _Heap_Align_up( used_size, page_size ); uintptr_t const free_size = block_size + HEAP_BLOCK_SIZE_OFFSET - used_size; uintptr_t const free_size_limit = min_block_size + HEAP_BLOCK_SIZE_OFFSET; Heap_Block *next_block = _Heap_Block_at( block, block_size ); _HAssert( used_size <= block_size + HEAP_BLOCK_SIZE_OFFSET ); _HAssert( used_size + free_size == block_size + HEAP_BLOCK_SIZE_OFFSET ); if ( free_size >= free_size_limit ) { Heap_Block *const free_block = _Heap_Block_at( block, used_block_size ); uintptr_t free_block_size = block_size - used_block_size; _HAssert( used_block_size + free_block_size == block_size ); _Heap_Block_set_size( block, used_block_size ); /* Statistics */ stats->free_size += free_block_size; if ( _Heap_Is_used( next_block ) ) { _Heap_Free_list_insert_after( free_list_anchor, free_block ); /* Statistics */ ++stats->free_blocks; } else { uintptr_t const next_block_size = _Heap_Block_size( next_block ); _Heap_Free_list_replace( next_block, free_block ); free_block_size += next_block_size; next_block = _Heap_Block_at( free_block, free_block_size ); } free_block->size_and_flag = free_block_size | HEAP_PREV_BLOCK_USED; next_block->prev_size = free_block_size; next_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED; } else { next_block->size_and_flag |= HEAP_PREV_BLOCK_USED; } }