From: H. Peter Anvin Date: Fri, 22 Feb 2008 01:59:56 +0000 (-0800) Subject: realloc(): better implementation allowing in-place growth X-Git-Tag: syslinux-3.62-pre13~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ce6265e0c4577aa7871e337cec549b0d844706ce;p=profile%2Fivi%2Fsyslinux.git realloc(): better implementation allowing in-place growth Change the realloc() implementation to allow in-place growth. This is an important step in handling files without knowing their sizes a priori. --- diff --git a/com32/lib/realloc.c b/com32/lib/realloc.c index cfbdc76..802d973 100644 --- a/com32/lib/realloc.c +++ b/com32/lib/realloc.c @@ -4,16 +4,15 @@ #include #include +#include #include "malloc.h" -/* FIXME: This is cheesy, it should be fixed later */ - void *realloc(void *ptr, size_t size) { - struct free_arena_header *ah; + struct free_arena_header *ah, *nah; void *newptr; - size_t oldsize; + size_t newsize, oldsize, xsize; if ( !ptr ) return malloc(size); @@ -23,26 +22,65 @@ void *realloc(void *ptr, size_t size) return NULL; } - /* Add the obligatory arena header, and round up */ - size = (size+2*sizeof(struct arena_header)-1) & ARENA_SIZE_MASK; - ah = (struct free_arena_header *) ((struct arena_header *)ptr - 1); - if ( ah->a.size >= size && size >= (ah->a.size >> 2) ) { - /* This field is a good size already. */ + /* Actual size of the old block */ + oldsize = ah->a.size; + + /* Add the obligatory arena header, and round up */ + newsize = (size+2*sizeof(struct arena_header)-1) & ARENA_SIZE_MASK; + + if ( oldsize >= newsize && newsize >= (oldsize >> 2) ) { + /* This allocation is close enough already. */ return ptr; } else { - /* Make me a new block. This is kind of bogus; we should - be checking the adjacent blocks to see if we can do an - in-place adjustment... fix that later. */ + xsize = oldsize; - oldsize = ah->a.size - sizeof(struct arena_header); + nah = ah->a.next; + if ((char *)nah == (char *)ah + ah->a.size && + nah->a.type == ARENA_TYPE_FREE && + oldsize + nah->a.size >= newsize) { + /* Merge in subsequent free block */ + ah->a.next = nah->a.next; + ah->a.next->a.prev = ah; + nah->next_free->prev_free = nah->prev_free; + nah->prev_free->next_free = nah->next_free; + xsize = (ah->a.size += nah->a.size); + } - newptr = malloc(size); - memcpy(newptr, ptr, (size < oldsize) ? size : oldsize); - free(ptr); + if (xsize >= newsize) { + /* We can reallocate in place */ + if (xsize >= newsize + 2*sizeof(struct arena_header)) { + /* Residual free block at end */ + nah = (struct free_arena_header *)((char *)ah + newsize); + nah->a.type = ARENA_TYPE_FREE; + nah->a.size = xsize - newsize; + ah->a.size = newsize; + + /* Insert into block list */ + nah->a.next = ah->a.next; + ah->a.next = nah; + nah->a.next->a.prev = nah; + nah->a.prev = ah; - return newptr; + /* Insert into free list */ + nah->next_free = __malloc_head.next_free; + nah->prev_free = &__malloc_head; + __malloc_head.next_free = nah; + nah->next_free->prev_free = nah; + } + /* otherwise, use up the whole block */ + return ptr; + } else { + /* Last resort: need to allocate a new block and copy */ + oldsize -= sizeof(struct arena_header); + newptr = malloc(size); + if (newptr) { + memcpy(newptr, ptr, min(size,oldsize)); + free(ptr); + } + return newptr; + } } }