diff --git a/src/heap.c b/src/heap.c index c36013f..821d6b9 100644 --- a/src/heap.c +++ b/src/heap.c @@ -1,25 +1,27 @@ -// https://github.com/YeonwooSung/MemoryAllocation - #include +#include #include -#include +#include #include +#include +#include +#include #include "sdb/sdb.h" -#include "sdb/heap.h" static SdbGlobalHeap Gheap = { NULL, NULL, NULL }; SDB_API SdbGlobalHeap *sdb_gh(void) { - return &Gheap; + return &Gheap; } + SDB_API char *sdb_strdup(const char *s) { - size_t sl = strlen (s) + 1; - char *p = (char *)sdb_gh_malloc (sl); - if (p) { - memcpy (p, s, sl); - } - return p; + size_t sl = strlen (s) + 1; + char *p = (char *)sdb_gh_malloc (sl); + if (p) { + memcpy (p, s, sl); + } + return p; } #if USE_MMAN @@ -32,396 +34,416 @@ SDB_API char *sdb_strdup(const char *s) { #define MAP_ANONYMOUS 0x20 #endif #endif +#endif -#if __SDB_WINDOWS__ -#include -#else -#include -#include -#include +// Align pointers and sizes to 8 bytes (suitable for 64-bit alignment) +#define ALIGNMENT 8 +#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1)) +#define SDB_PAGE_SIZE 131072 // 128 KB pages +#define MIN_SIZE ALIGN(sizeof(free_list) + META_SIZE) // Minimum block size to hold free_list node + metadata + +// Macros for page calculations +#define CEIL_DIV(x,y) (((x) + (y) - 1) / (y)) +#define PAGES(size) CEIL_DIV((size), SDB_PAGE_SIZE) -// Size 16 +// Structure for free list node (embedded in free block's payload) typedef struct free_list { struct free_list *next; struct free_list *prev; } free_list; -typedef struct sdb_heap_t { - // Globals - int *last_address; - free_list *free_list_start; - // To reduce number of mmap calls. - int last_mapped_size; // 1; -} SdbHeap; - -SDB_API void sdb_heap_fini(SdbHeap *heap); -SDB_API void *sdb_heap_realloc(SdbHeap *heap, void *ptr, int size); - -static SdbHeap sdb_gh_custom_data = { NULL, NULL, 1}; -const SdbGlobalHeap sdb_gh_custom = { - (SdbHeapRealloc)sdb_heap_realloc, - (SdbHeapFini)sdb_heap_fini, - &sdb_gh_custom_data -}; - -#define USED false -#define FREE true - +// Header and Footer definitions typedef struct Header { - int size; - bool free : 1; - bool has_prev : 1; - bool has_next : 1; + int size; // total size of this block (including header+footer+payload) + bool free : 1; // 1-bit flag: true if block is free + bool has_prev : 1; // true if a previous contiguous block exists + bool has_next : 1; // true if a next contiguous block exists + // (Remaining bits of the byte/word are unused padding due to bit-fields) } Header; -// Size field is not necessary in used blocks. typedef struct Footer { int size; bool free : 1; + // (Padding bits unused) } Footer; - -#define ALIGNMENT 8 -#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1)) -#define SDB_PAGE_SIZE 131072 // 96096*2 //sysconf(_SC_PAGESIZE) -#define CEIL(X) ((X - (int)(X)) > 0 ? (int)(X + 1) : (int)(X)) -#define PAGES(size) (CEIL(size / (double)SDB_PAGE_SIZE)) -#define MIN_SIZE (ALIGN(sizeof(free_list) + META_SIZE)) -#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) - -// Meta sizes. -#define META_SIZE ALIGN(sizeof(Header) + sizeof(Footer)) +// Compute aligned metadata sizes #define HEADER_SIZE ALIGN(sizeof(Header)) #define FOOTER_SIZE ALIGN(sizeof(Footer)) +#define META_SIZE ALIGN(sizeof(Header) + sizeof(Footer)) -// Get pointer to the payload (passing the pointer to the header). -static void *add_offset(void *ptr) { - return (void *)((const ut8*)ptr + HEADER_SIZE); -} +// Global heap state structure +typedef struct sdb_heap_t { + uint8_t *last_address; // End address of the last allocated region (for contiguous mmap hint) + free_list *free_list_start; // Head of the free list linked list + size_t last_mapped_size; // Size (in pages) of the last mmap request (doubling strategy) +} SdbHeap; -// Get poiner to the header (passing pointer to the payload). -static void *remove_offset(void *ptr) { - return (void *)((const ut8*)ptr - HEADER_SIZE); -} +SDB_API void *sdb_heap_realloc(SdbHeap *heap, void *ptr, int new_size); +SDB_API void sdb_heap_fini(SdbHeap *heap); +static SdbHeap sdb_gh_custom_data = { NULL, NULL, 1}; -static Footer *getFooter(void *header_ptr) { - return (Footer*)((ut8*)header_ptr + ((Header *)header_ptr)->size - FOOTER_SIZE); -} +const SdbGlobalHeap sdb_gh_custom = { + (SdbHeapRealloc)sdb_heap_realloc, + (SdbHeapFini)sdb_heap_fini, + &sdb_gh_custom_data +}; -static void setFree(void *ptr, int val) { - ((Header *)ptr)->free = val; - Footer *footer = (Footer *)getFooter(ptr); - footer->free = val; - // Copy size to footer size field. - footer->size = ((Header *)ptr)->size; +// Utility functions to get pointers to header, payload, and footer +static inline void *add_offset(void *header_ptr) { + // From a Header pointer, get the address of the payload + return (uint8_t*)header_ptr + HEADER_SIZE; } -// Set size in the header. -static inline void setSizeHeader(void *ptr, int size) { - ((Header *)ptr)->size = size; +static inline void *remove_offset(void *payload_ptr) { + // From a payload pointer, get the address of the Header + return (uint8_t*)payload_ptr - HEADER_SIZE; } -#if 0 -// Set size in the header. -static inline void setSizeFooter(void *ptr, int size) { - ((Footer *)getFooter(ptr))->size = size; +static inline Footer *getFooter(void *header_ptr) { + Header *h = (Header*)header_ptr; + return (Footer*)((uint8_t*)header_ptr + h->size - FOOTER_SIZE); } -#endif -// Get size of the free list item. -static inline int getSize(void *ptr) { - return ((Header *)remove_offset (ptr))->size; +// Functions to set and get block attributes +static inline void setFreeFlag(void *header_ptr, bool is_free) { + Header *h = (Header*)header_ptr; + h->free = is_free; + // Set the footer's free flag and size to match header + Footer *f = getFooter(header_ptr); + f->free = is_free; + f->size = h->size; } -static void remove_from_free_list(SdbHeap *heap, void *block) { - setFree (block, USED); - - free_list *free_block = (free_list *)add_offset(block); - free_list *next = free_block->next; - free_list *prev = free_block->prev; - if (!prev) { - if (!next) { - // free_block is the only block in the free list. - heap->free_list_start = NULL; - } else { - // Remove first element in the free list. - heap->free_list_start = next; - next->prev = NULL; - } - } else { - if (!next) { - // Remove last element of the free list. - prev->next = NULL; - } else { - // Remove element in the middle. - prev->next = next; - next->prev = prev; - } - } +static inline int getBlockSize(void *payload_ptr) { + // Get total block size given a pointer to the payload + Header *h = (Header*)remove_offset(payload_ptr); + return h->size; } -static void append_to_free_list(SdbHeap *heap, void *ptr) { - setFree (ptr, FREE); - - free_list eew = {}; - free_list *new_ptr = (free_list *)add_offset (ptr); - *new_ptr = eew; +// Free list management functions +static void remove_from_free_list(SdbHeap *heap, void *header_ptr) { + // Remove the free block with this header from the free list + free_list *node = (free_list*)add_offset(header_ptr); + free_list *prev = node->prev; + free_list *next = node->next; + if (prev) prev->next = next; + else heap->free_list_start = next; // Removing head + if (next) next->prev = prev; + // Mark block as used (not free) since it's no longer in free list + setFreeFlag(header_ptr, false); +} +static void append_to_free_list(SdbHeap *heap, void *header_ptr) { + // Add this free block (by header pointer) to the free list (LIFO insertion at head) + Header *h = (Header*)header_ptr; + h->free = true; + Footer *f = getFooter(header_ptr); + f->free = true; + f->size = h->size; + // Initialize a free_list node in the payload area + free_list *node = (free_list*)add_offset(header_ptr); + node->prev = NULL; + node->next = heap->free_list_start; if (heap->free_list_start) { - // Insert in the beginning. - new_ptr->next = heap->free_list_start; - new_ptr->prev = NULL; - heap->free_list_start->prev = new_ptr; - heap->free_list_start = new_ptr; - } else { - // No elements in the free list - heap->free_list_start = new_ptr; - new_ptr->prev = NULL; - new_ptr->next = NULL; + heap->free_list_start->prev = node; } + heap->free_list_start = node; } -// Find a free block that is large enough to store 'size' bytes. -// Returns NULL if not found. static free_list *find_free_block(SdbHeap *heap, int size) { + // First-fit search for a free block with at least 'size' bytes free_list *current = heap->free_list_start; while (current) { - if (getSize (current) >= size) { - // Return a pointer to the free block. - return current; + Header *h = (Header*)remove_offset(current); + if (h->size >= size) { + return current; // found a large enough free block } current = current->next; } - return NULL; + return NULL; // no suitable block found } -// Split memory into multiple blocks after some part of it was requested -// (requested + the rest). -static void split(SdbHeap *heap, void *start_ptr, int total, int requested) { - void *new_block_ptr = (void*)((ut8*)start_ptr + requested); - int block_size = total - requested; - - // Size that was left after allocating memory. - // Needs to be large enough to store another block (min size is needed in order - // to store free list element there after it is freed). - if (block_size < (int)MIN_SIZE) { - // Not enough size to split. - return; +// Split a block into an allocated part and a free remainder +static void split_block(SdbHeap *heap, void *header_ptr, int total_size, int alloc_size) { + // Only split if the remainder is large enough to form a new free block + int remainder_size = total_size - alloc_size; + if (remainder_size < (int)MIN_SIZE) { + return; // Not enough space to create another block; skip splitting + } + Header *orig_header = (Header*)header_ptr; + // Create the new free block at the end of the allocated portion + uint8_t *new_block_addr = (uint8_t*)header_ptr + alloc_size; + Header *new_header = (Header*)new_block_addr; + // Set up the allocated block (orig_header) metadata + orig_header->size = alloc_size; + orig_header->has_next = true; + // Set up the new free block's header + new_header->size = remainder_size; + new_header->free = true; + new_header->has_prev = true; + new_header->has_next = orig_header->has_next; // inherit whether a block existed beyond the original + // Update orig_header's next flag to true (it now has a neighbor) + // (orig_header->has_next is already true from above) + // Update the footer of the new free block + Footer *new_footer = getFooter(new_header); + new_footer->size = remainder_size; + new_footer->free = true; + // Link the new free block into the free list + append_to_free_list(heap, new_header); + // Update the *next* block (if it exists) to have a prev neighbor + if (new_header->has_next) { + // The block after the new free block (if any) should now have a prev + Header *next_header = (Header*)((uint8_t*)new_block_addr + new_header->size); + next_header->has_prev = true; } - // Change size of the prev (recently allocated) block. - setSizeHeader(start_ptr, requested); - ((Header *)start_ptr)->has_next = true; - - // Add a header for newly created block (right block). - Header header = {block_size, FREE, true, ((Header *)start_ptr)->has_next}; - Header *new_block_header = (Header *)new_block_ptr; - *new_block_header = header; - Footer footer = {block_size, FREE}; - *((Footer *)getFooter(new_block_header)) = footer; - append_to_free_list (heap, new_block_header); } -static void *sdb_heap_malloc(SdbHeap *heap, int size) { - if (size <= 0) { - return NULL; - } - // Size of the block can't be smaller than MIN_SIZE, as we need to store - // free list in the body + header and footer on each side respectively. - int required_size = MAX (ALIGN (size + META_SIZE), MIN_SIZE); - // Try to find a block big enough in already allocated memory. - free_list *free_block = find_free_block (heap, required_size); +// Initialize and finalize heap (for completeness) +SDB_API void sdb_heap_init(SdbHeap *heap) { + heap->last_address = NULL; + heap->free_list_start = NULL; + heap->last_mapped_size = 1; // start with 1 page on first allocation +} - if (free_block) { - // Header ptr - void *address = remove_offset (free_block); - // Mark block as used. - setFree (address, USED); - // Split the block into two, where the second is free. - split (heap, address, ((Header *)address)->size, required_size); - remove_from_free_list (heap, address); - return add_offset (address); +SDB_API void sdb_heap_fini(SdbHeap *heap) { + // Free all free blocks (release to OS) + free_list *current = heap->free_list_start; + while (current) { + Header *h = (Header*)remove_offset(current); + // Unmap free block regions (only if they were allocated via mmap) + // Here we simply iterate and unmap all free blocks. + munmap(h, h->size); + current = current->next; } + heap->free_list_start = NULL; +} - // No free block was found. Allocate size requested + header (in full pages). - // Each next allocation will be doubled in size from the previous one - // (to decrease the number of mmap sys calls we make). - // int bytes = MAX (PAGES (required_size), heap->last_mapped_size) * SDB_PAGE_SIZE; - size_t bytes = PAGES (MAX (PAGES (required_size), heap->last_mapped_size)) * SDB_PAGE_SIZE; - heap->last_mapped_size *= 2; - - // last_address my not be returned by mmap, but makes it more efficient if it happens. - void *new_region = mmap (NULL, bytes, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - if (new_region == MAP_FAILED) { - perror ("mmap"); +// Allocate memory +SDB_API void *sdb_heap_malloc(SdbHeap *heap, int size) { + if (size <= 0) { return NULL; } - // Create a header/footer for new block. - Header header = {(int)bytes, USED, false, false}; - Header *header_ptr = (Header *)new_region; - *header_ptr = header; - Footer footer = {}; - footer.free = USED; - *((Footer *)getFooter (new_region)) = footer; - - if (new_region == heap->last_address && heap->last_address != 0) { - // if we got a block of memory after the last block, as we requested. - header_ptr->has_prev = true; - // change has_next of the prev block - Footer *prev_footer = (Footer *)(header_ptr - FOOTER_SIZE); - ((Header *)header_ptr - (prev_footer->size))->has_next = true; + // Calculate required total block size (include header+footer, aligned) + int required_size = ALIGN(size + META_SIZE); + if (required_size < (int)MIN_SIZE) { + required_size = MIN_SIZE; // ensure minimum block size } - // Split new region. - split (heap, new_region, bytes, required_size); - // Update last_address for the next allocation. - heap->last_address = (int*)((ut8*)new_region + bytes); - // Return address behind the header (i.e. header is hidden). - return add_offset (new_region); -} - -static void coalesce(SdbHeap *heap, void *ptr) { - Header *current_header = (Header *)ptr; - Footer *current_footer = getFooter (ptr); - if (current_header->has_prev && ((Footer *)((ut8*)ptr - FOOTER_SIZE))->free) { - int prev_size = ((Footer *)((ut8*)ptr - FOOTER_SIZE))->size; - Header *prev_header = (Header *)((ut8*)ptr - prev_size); - Footer *prev_footer = (Footer *)((ut8*)ptr - FOOTER_SIZE); - - // Merge with previous block. - remove_from_free_list (heap, current_header); - // Add size of prev block to the size of current block - prev_header->size += current_header->size; - prev_footer->size = prev_header->size; - current_header = prev_header; + // Try to find a free block in the existing heap + free_list *free_node = find_free_block(heap, required_size); + if (free_node) { + // Reuse this free block + Header *header = (Header*)remove_offset(free_node); + remove_from_free_list(heap, header); + // Mark as used and possibly split if it's larger than needed + setFreeFlag(header, false); + split_block(heap, header, header->size, required_size); + // Return pointer to payload + return add_offset(header); } - void *next = (void*)((ut8*)ptr + current_header->size); - if (current_header->has_next && ((Header *)next)->free) { - int size = ((Header *)next)->size; - // merge with next block. - remove_from_free_list (heap, (ut8*)ptr + current_header->size); - // Add size of next block to the size of current block. - current_header->size += size; - current_footer->size = current_header->size; + // No suitable free block found; map a new region from OS + size_t pages_needed = PAGES(required_size); + size_t pages_to_map = heap->last_mapped_size; + if (pages_needed > pages_to_map) { + pages_to_map = pages_needed; } -} - -static int unmap(SdbHeap *heap, void *start_address, int size) { - remove_from_free_list (heap, start_address); - // Reset has_next, has_prev of neighbours. - Header *header = (Header *)start_address; - if (header->has_prev) { - // Get prev header, set has_next to false. - int prev_size = ((Footer *)((ut8*)start_address - FOOTER_SIZE))->size; - Header *prev_header = (Header *)((ut8*)start_address - prev_size); - prev_header->has_next = false; - } - if (header->has_next) { - // Get next header, set has_prev to false. - int this_size = header->size; - Header *next_header = (Header *)((ut8*)start_address + this_size); - next_header->has_prev = false; + size_t bytes = pages_to_map * SDB_PAGE_SIZE; + // Double the last_mapped_size for next time (to exponential grow) + heap->last_mapped_size = (heap->last_mapped_size < pages_needed) + ? heap->last_mapped_size * 2 + : heap->last_mapped_size * 2; + void *new_region = mmap(NULL, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (new_region == MAP_FAILED) { + perror("mmap"); + return NULL; } - - // If this is the last block we've allocated using mmap, need to change last_address. - if (heap->last_address == start_address) { - heap->last_address = (int *)((ut8*)start_address - size); + // Set up the new region as a single allocated block for now + Header *header = (Header*)new_region; + header->size = (int)bytes; + header->free = false; + header->has_prev = false; + header->has_next = false; + // If this new region is contiguous with the last allocated region, link them + if (heap->last_address != NULL && new_region == heap->last_address) { + // The new region starts exactly where the previous one ended + header->has_prev = true; + // Update previous block's has_next flag + // Find the previous block's header using the footer just before this region + Footer *prev_footer = (Footer*)((uint8_t*)new_region - FOOTER_SIZE); + Header *prev_header = (Header*)((uint8_t*)new_region - prev_footer->size); + prev_header->has_next = true; } - return munmap (start_address, (size_t)size); + // Possibly split off the portion needed by the user from this new region + // We treat the portion to return as allocated and the remainder as free. + split_block(heap, header, (int)bytes, required_size); + // Update the last_address to the end of this region for future contiguous allocations + heap->last_address = (uint8_t*)new_region + bytes; + // Return pointer to payload of allocated block + return add_offset(header); } -static void sdb_heap_free(SdbHeap *heap, void *ptr) { - if (!ptr) { +// Free memory +SDB_API void sdb_heap_free(SdbHeap *heap, void *ptr) { + if (!ptr) return; + Header *header = (Header*)remove_offset(ptr); + if (header->free) { + // Block is already free – likely a double free – ignore to avoid corruption return; } - void *start_address = remove_offset (ptr); - - // Check if it has already been freed. - // Does not handle case when start_address passed was never allocated. - if (((Header *)start_address)->free) { - return; - } - - Header *header = (Header *)start_address; int size = header->size; uintptr_t addr = (uintptr_t)header; - if (size % SDB_PAGE_SIZE == 0 && (addr % SDB_PAGE_SIZE) == 0) { - // if: full page is free (or multiple consecutive pages), page-aligned -> can munmap it. - unmap (heap, start_address, size); - } else { - append_to_free_list (heap, start_address); - coalesce (heap, start_address); - // if we are left with a free block of size bigger than PAGE_SIZE that is - // page-aligned, munmap that part. - if (size >= SDB_PAGE_SIZE && (addr % SDB_PAGE_SIZE) == 0) { - split (heap, start_address, size, (size / SDB_PAGE_SIZE) * SDB_PAGE_SIZE); - unmap (heap, start_address, (size / SDB_PAGE_SIZE) * SDB_PAGE_SIZE); + // If the block is a whole number of pages and page-aligned, free it back to OS + if ((size % SDB_PAGE_SIZE) == 0 && (addr % SDB_PAGE_SIZE) == 0) { + // Unlink neighbor relationships before unmapping + if (header->has_prev) { + // Previous block exists: set its has_next to false + Footer *prev_footer = (Footer*)((uint8_t*)header - FOOTER_SIZE); + Header *prev_header = (Header*)((uint8_t*)header - prev_footer->size); + prev_header->has_next = false; + } + if (header->has_next) { + // Next block exists: set its has_prev to false + Header *next_header = (Header*)((uint8_t*)header + header->size); + next_header->has_prev = false; } + // Update heap->last_address if this was the last region + if (heap->last_address == (uint8_t*)header + header->size) { + heap->last_address = (uint8_t*)header; // move last_address backward + } + // Return the memory to the OS + munmap(header, header->size); + return; } -} - -SDB_API void sdb_heap_init(SdbHeap *heap) { - heap->last_address = NULL; - heap->free_list_start = NULL; - heap->last_mapped_size = 1; -} - -SDB_API void sdb_heap_fini(SdbHeap *heap) { -#if 1 - free_list *current = heap->free_list_start; - while (current) { - free_list *next = current->next; - sdb_heap_free (heap, current); - current = next; + // Otherwise, mark the block free and add to free list + append_to_free_list(heap, header); + // Coalesce with adjacent free blocks if possible + // Coalesce backward + Header *current_header = header; + //bool merged_prev = false; + if (current_header->has_prev) { + // Check the previous block's footer to see if the previous block is free + Footer *prev_footer = (Footer*)((uint8_t*)current_header - FOOTER_SIZE); + if (prev_footer->free) { + // Previous block is free: merge with it + Header *prev_header = (Header*)((uint8_t*)current_header - prev_footer->size); + // Remove the current block from free list (prev is already free and in list) + remove_from_free_list(heap, current_header); + // Combine sizes + prev_header->size += current_header->size; + // Use current block's footer as the new combined footer + Footer *curr_footer = getFooter(current_header); + // Now prev_header's block spans both, so update its footer (which is curr_footer's position) + Footer *new_footer = curr_footer; + new_footer->size = prev_header->size; + new_footer->free = true; + // Update flags: prev_header is still free, has_prev stays same, has_next should reflect current's has_next + bool had_next = current_header->has_next; + prev_header->has_next = had_next; + current_header = prev_header; // current_header now points to the combined block's header (prev block) + // merged_prev = true; + } + } + // Coalesce forward + if (current_header->has_next) { + Header *next_header = (Header*)((uint8_t*)current_header + current_header->size); + if (next_header->free) { + // Next block is free: merge with it + remove_from_free_list(heap, next_header); + // Combine sizes + current_header->size += next_header->size; + // Update footer at end of combined block + Footer *end_footer = getFooter(next_header); + // end_footer currently is next_header's footer + end_footer->size = current_header->size; + end_footer->free = true; + // Update flags for combined block + current_header->has_next = next_header->has_next; + } + } + // After coalescing, current_header is the start of the free region + // If the resulting free block contains whole pages and is page-aligned, release those pages + Header *free_block = current_header; + size = free_block->size; + addr = (uintptr_t)free_block; + if (size >= SDB_PAGE_SIZE && (addr % SDB_PAGE_SIZE) == 0) { + // Calculate how many whole pages can be freed from this block + int whole_pages = (size / SDB_PAGE_SIZE); + int bytes_to_free = whole_pages * SDB_PAGE_SIZE; + // Split off the page-aligned chunk to free + // We reuse split_block to carve out 'bytes_to_free' from the beginning of this free block + remove_from_free_list(heap, free_block); // remove combined block from list temporarily + setFreeFlag(free_block, false); // mark as used while we carve it (avoid confusion) + split_block(heap, free_block, size, bytes_to_free); + // Now the first part of free_block is of size bytes_to_free and marked used (not in free list). + // Unmap that first part + munmap(free_block, bytes_to_free); + // The second part (if any) is already in the free list from split_block. + // Update heap->last_address if needed + if (heap->last_address == (uint8_t*)free_block + size) { + heap->last_address -= bytes_to_free; + } + // If the entire block was freed (no remainder), do nothing else. + // If there is a remainder free block, it’s already in the free list. } -#endif } -SDB_API void *sdb_heap_realloc(SdbHeap *heap, void *ptr, int size) { - // If ptr is NULL, realloc() is identical to a call to malloc() for size bytes. +// Reallocate memory (resize block) +SDB_API void *sdb_heap_realloc(SdbHeap *heap, void *ptr, int new_size) { if (!ptr) { - return sdb_heap_malloc (heap, size); + // Equivalent to malloc + return sdb_heap_malloc(heap, new_size); } - // If size is zero and ptr is not NULL, a new, minimum sized object (MIN_SIZE) is - // allocated and the original object is freed. - if (size == 0 && ptr) { - sdb_heap_free (heap, ptr); - return sdb_heap_malloc (heap, 1); + if (new_size <= 0) { + // Equivalent to free (with special case: allocate minimum object of size 1 as per code) + sdb_heap_free(heap, ptr); + return sdb_heap_malloc(heap, 1); } - - int required_size = META_SIZE + size; - // If there is enough space, expand the block. - int current_size = getSize (ptr); - - // if user requests to shorten the block. - if (size < current_size) { + Header *current_header = (Header*)remove_offset(ptr); + int old_total_size = current_header->size; + int old_payload_size = old_total_size - (int)META_SIZE; + int required_size = ALIGN(new_size + META_SIZE); + if (required_size < (int)MIN_SIZE) { + required_size = MIN_SIZE; + } + // If the current block is already large enough to accommodate new_size, reuse it + if (required_size <= old_total_size) { + // Optionally, we could shrink the block if the new request is much smaller, + // but for now we simply return the same pointer (no move). return ptr; } - Header *current_header = (Header *)ptr; - Footer *current_footer = (Footer *)getFooter(ptr); - // Next block exists and is free. - if (current_header->has_next && ((Header *)ptr + current_size)->free) { - int available_size = current_size + getSize ((ut8*)ptr + current_size); - // Size is enough. - if (available_size >= required_size) { - Header *next_header = (Header *)((ut8*)ptr + current_size); - remove_from_free_list (heap, next_header); - // Add size of next block to the size of current block. - current_header->size += size; - current_footer->size = current_header->size; - - // split if possible. - split (heap, current_header, available_size, required_size); - return ptr; + // Try to expand into the next block if it's free + if (current_header->has_next) { + Header *next_header = (Header*)((uint8_t*)current_header + current_header->size); + if (next_header->free) { + int combined_size = current_header->size + next_header->size; + if (combined_size >= required_size) { + // We can merge with next block to satisfy the request + // Remove next block from free list and merge + remove_from_free_list(heap, next_header); + current_header->size = combined_size; + current_header->has_next = next_header->has_next; + // Update footer to combined block + Footer *combined_footer = getFooter(current_header); + combined_footer->size = current_header->size; + combined_footer->free = false; // block will be in-use after realloc + // If there's still extra space beyond required_size, split the excess into a free block + split_block(heap, current_header, current_header->size, required_size); + return ptr; + } } } - - // Not enough room to enlarge -> allocate new region. - void *new_ptr = sdb_heap_malloc (heap, size); - memcpy (new_ptr, ptr, current_size); - - // Free old location. - sdb_heap_free (heap, ptr); + // Otherwise, allocate a new block and copy the data + void *new_ptr = sdb_heap_malloc(heap, new_size); + if (!new_ptr) { + return NULL; // allocation failed + } + // Copy old data to new block (up to the smaller of old data size or new size) + int bytes_to_copy = old_payload_size; + if (bytes_to_copy > new_size) { + bytes_to_copy = new_size; + } + memcpy(new_ptr, ptr, bytes_to_copy); + // Free the old block + sdb_heap_free(heap, ptr); return new_ptr; } - -#endif -#endif