/* * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * stdlib/realloc.c * Reallocates a chunk of memory from the dynamic memory heap. */ #include #include #include #include #include #if defined(HEAP_NO_ASSERT) #define __heap_verify() ((void) 0) #undef assert #define assert(x) do { ((void) 0); } while ( 0 ) #endif #if !defined(HEAP_GUARD_DEBUG) #ifdef __TRACE_ALLOCATION_SITES void* realloc_trace(struct __allocation_site* new_allocation_site, void* ptr, size_t requested_size) #else void* realloc(void* ptr, size_t requested_size) #endif { if ( !ptr ) #ifdef __TRACE_ALLOCATION_SITES return malloc_trace(new_allocation_site, requested_size); #else return malloc(requested_size); #endif if ( !heap_size_has_bin(requested_size) ) return errno = ENOMEM, (void*) NULL; // Decide how big an allocation we would like to make. size_t requested_chunk_outer_size = sizeof(struct heap_chunk) + sizeof(struct heap_chunk_post); size_t requested_chunk_inner_size = heap_align(requested_size); size_t requested_chunk_size = requested_chunk_outer_size + requested_chunk_inner_size; if ( !heap_size_has_bin(requested_chunk_size) ) return errno = ENOMEM, (void*) NULL; __heap_lock(); __heap_verify(); // Retrieve the chunk that contains this allocation. struct heap_chunk* chunk = heap_data_to_chunk((uint8_t*) ptr); #ifdef __TRACE_ALLOCATION_SITES assert(MAGIC_IS_ALLOCATION_SITE(chunk->chunk_magic)); struct __allocation_site* allocation_site = ALLOCATION_SITE_OF_MAGIC(chunk->chunk_magic); #endif assert(chunk->chunk_magic == HEAP_CHUNK_MAGIC || MAGIC_IS_ALLOCATION_SITE(chunk->chunk_magic)); assert(heap_chunk_to_post(chunk)->chunk_magic == HEAP_CHUNK_MAGIC); assert(heap_chunk_to_post(chunk)->chunk_size == chunk->chunk_size); // Do nothing if the chunk already has the ideal size. if ( chunk->chunk_size == requested_chunk_size ) { __heap_verify(); __heap_unlock(); return heap_chunk_to_data(chunk); } // If the ideal size is smaller than the current, attempt the shrink the // allocation if a new chunk can be created. if ( requested_chunk_size < chunk->chunk_size ) { assert(requested_chunk_size <= chunk->chunk_size); #ifdef __TRACE_ALLOCATION_SITES allocation_site->current_size -= chunk->chunk_size; allocation_site->allocations--; #endif if ( heap_can_split_chunk(chunk, requested_chunk_size) ) heap_split_chunk(chunk, requested_chunk_size); #ifdef __TRACE_ALLOCATION_SITES allocation_site->current_size += chunk->chunk_size; allocation_site->allocations++; chunk->chunk_magic = MAGIC_OF_ALLOCATION_SITE(allocation_site); #endif __heap_verify(); __heap_unlock(); return heap_chunk_to_data(chunk); } // TODO: What if the right neighbor is the part edge? // If we need to expand the chunk, attempt to combine it with its right // neighbor if it is large enough. struct heap_chunk* right; if ( (right = heap_chunk_right(chunk)) && !heap_chunk_is_used(right) && requested_chunk_size <= chunk->chunk_size + right->chunk_size ) { #ifdef __TRACE_ALLOCATION_SITES allocation_site->current_size -= chunk->chunk_size; allocation_site->allocations--; #endif heap_remove_chunk(right); heap_chunk_format((uint8_t*) chunk, chunk->chunk_size + right->chunk_size); assert(requested_chunk_size <= chunk->chunk_size); if ( heap_can_split_chunk(chunk, requested_chunk_size) ) heap_split_chunk(chunk, requested_chunk_size); #ifdef __TRACE_ALLOCATION_SITES allocation_site->current_size += chunk->chunk_size; allocation_site->allocations++; chunk->chunk_magic = MAGIC_OF_ALLOCATION_SITE(allocation_site); #endif __heap_verify(); __heap_unlock(); return heap_chunk_to_data(chunk); } // It appears that we cannot retain the orignal allocation location and we // will have to relocate the allocation elsewhere to expand it. size_t orignal_ptr_size = heap_chunk_data_size(chunk); __heap_verify(); __heap_unlock(); assert(orignal_ptr_size < requested_size); #ifdef __TRACE_ALLOCATION_SITES void* result = malloc_trace(allocation_site, requested_size); #else void* result = malloc(requested_size); #endif if ( !result ) return (void*) NULL; memcpy(result, ptr, orignal_ptr_size); free(ptr); return result; } #else void* realloc(void* ptr, size_t requested_size) { if ( !ptr ) return malloc(requested_size); struct heap_alloc* alloc_ptr = (struct heap_alloc*) HEAP_ALIGN_PAGEDOWN((uintptr_t) ptr - 16); assert(alloc_ptr->from == (uintptr_t) alloc_ptr); struct heap_alloc alloc = *alloc_ptr; size_t size = (alloc.from + alloc.size - HEAP_PAGE_SIZE) - (uintptr_t) ptr; if ( requested_size <= size ) return ptr; void* replacement = malloc(requested_size); if ( !replacement ) return NULL; memcpy(replacement, ptr, size); free(ptr); return replacement; } #endif