#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; } /*--------------------------------------------------------- */