#include #include #include "private.h" /*--------------------------------------------------------- */ lub_heap_status_t lub_heap_raw_realloc(lub_heap_t *this, char **ptr, size_t requested_size, lub_heap_align_t alignment) { lub_heap_status_t result = LUB_HEAP_FAILED; lub_heap_block_t *block = NULL; words_t words; char *new_ptr = NULL; const size_t MIN_SIZE = sizeof(lub_heap_free_block_t); /* make the minimum alignment leave enough space for * (sizeof(lub_heap_free_block_t) + sizeof(lub_heap_alloc_block_t) + sizeof(lub_heap_node_t)) * approx 72 bytes on 64-bit architecture */ const lub_heap_align_t MIN_ALIGN_NON_NATIVE = LUB_HEAP_ALIGN_2_POWER_7; size_t size = requested_size; do { if(NULL == ptr) break; /* The client MUST give us a pointer to play with */ if(LUB_HEAP_ZERO_ALLOC == *ptr) { *ptr = NULL; } if(*ptr && (BOOL_FALSE == lub_heap_validate_pointer(this,*ptr))) { /* This is not a valid pointer */ result = LUB_HEAP_INVALID_POINTER; break; } /* check the heap integrity */ if(BOOL_FALSE == lub_heap_check_memory(this)) { result = LUB_HEAP_CORRUPTED; break; } if(size) { /* make sure we get enough space for the header/tail */ size += sizeof(lub_heap_alloc_block_t); /* make sure we are at least natively aligned */ size += (LUB_HEAP_ALIGN_NATIVE-1); size &= ~(LUB_HEAP_ALIGN_NATIVE-1); if(size < MIN_SIZE) { /* * we must ensure that any allocated block is at least * large enough to hold a free block node when it is released */ size = MIN_SIZE; } if(LUB_HEAP_ALIGN_NATIVE != alignment) { if(alignment < MIN_ALIGN_NON_NATIVE) { /* * ensure that we always leave enough space * to be able to collapse the block to the * right size */ alignment = MIN_ALIGN_NON_NATIVE; } /* add twice the alignment */ size += (alignment << 1); } } words = (size >> 2); if(requested_size > size) { /* the size has wrapped when accounting for overheads */ break; } if(NULL != *ptr) { /* get reference to the current block */ block = lub_heap_block_from_ptr(*ptr); /* first of all check this is an allocated block */ if(1 == block->alloc.tag.free) { result = LUB_HEAP_DOUBLE_FREE; break; } /* first of all check this is an allocated block */ if(BOOL_FALSE == lub_heap_block_check(block)) { result = LUB_HEAP_CORRUPTED; break; } /* is the current block large enough for the request */ if(words && (block->alloc.tag.words >= words)) { lub_heap_block_t *next_block = lub_heap_block_getnext(block); words_t delta = (block->alloc.tag.words - words); lub_heap_tag_t *tail; result = LUB_HEAP_OK; new_ptr = *ptr; if(delta && (NULL != next_block) ) { /* can we graft this spare memory to a following free block? */ if(1 == next_block->free.tag.free) { block->alloc.tag.words = words; tail = lub_heap_block__get_tail(block); tail->words = words; tail->free = 0; tail->segment = 0; lub_heap_graft_to_bottom(this, next_block, &tail[1], delta, BOOL_FALSE, BOOL_FALSE); this->stats.alloc_bytes -= (delta << 2); break; } } /* Is there enough space to turn the spare memory into a free block? */ if(delta >= (sizeof(lub_heap_free_block_t) >> 2)) { tail = lub_heap_block__get_tail(block); lub_heap_init_free_block(this, &tail[1-delta], (delta << 2), BOOL_FALSE, tail->segment); block->alloc.tag.words = words; tail = lub_heap_block__get_tail(block); tail->words = words; tail->free = 0; tail->segment = 0; this->stats.alloc_bytes -= (delta << 2); } break; } else if(words) { /* can we simple extend the current allocated block? */ if( (BOOL_TRUE == lub_heap_extend_upwards (this,&block,words)) || (BOOL_TRUE == lub_heap_extend_downwards(this,&block,words)) || (BOOL_TRUE == lub_heap_extend_both_ways(this,&block,words)) ) { result = LUB_HEAP_OK; new_ptr = (char*)block->alloc.memory; break; } } } if(words && (LUB_HEAP_FAILED == result)) { /* need to reallocate and copy the data */ new_ptr = lub_heap_new_alloc_block(this,words); if(NULL != new_ptr) { result = LUB_HEAP_OK; if(NULL != block) { /* now copy the old contents across */ memcpy(new_ptr, (const char*)block->alloc.memory, (block->alloc.tag.words << 2) - sizeof(lub_heap_alloc_block_t)); } } else { /* couldn't find the space for the request */ break; } } /* now it's time to release the memory of the old block */ if(NULL != block) { words_t old_words = block->alloc.tag.words; bool_t done = BOOL_FALSE; /* combine with the next block */ if(LUB_HEAP_OK == lub_heap_merge_with_next(this,block)) { done = BOOL_TRUE; } /* combine with the previous block */ if(LUB_HEAP_OK == lub_heap_merge_with_previous(this,block)) { done = BOOL_TRUE; } if(BOOL_FALSE == done) { /* create a new free block */ lub_heap_new_free_block(this,block); } result = LUB_HEAP_OK; --this->stats.alloc_blocks; this->stats.alloc_bytes -= (old_words << 2); this->stats.alloc_bytes += sizeof(lub_heap_alloc_block_t); this->stats.alloc_overhead -= sizeof(lub_heap_alloc_block_t); } } while(0); if(LUB_HEAP_OK == result) { unsigned delta; /* align the block as required */ if(LUB_HEAP_ALIGN_NATIVE != alignment) { lub_heap_align_block(this, alignment, &new_ptr, &size); } /* revert to client space available */ size -= sizeof(lub_heap_alloc_block_t); *ptr = new_ptr; delta = (size - requested_size); if(*ptr && requested_size && (0 < delta)) { /* make sure that any excess memory is tainted */ lub_heap_taint_memory(*ptr + requested_size,LUB_HEAP_TAINT_ALLOC,delta); } } else if( (LUB_HEAP_FAILED == result) && (0 == requested_size) && (NULL == *ptr)) { /* freeing zero is always OK */ result = LUB_HEAP_OK; } return result; } /*--------------------------------------------------------- */