* Reimplemented realloc() to reuse the previous buffer if possible and useful.

* Cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34409 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2009-12-01 14:50:33 +00:00
parent 2920b81793
commit 3c627a2751
+52 -34
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2003-2007, Axel Dörfler, [email protected]. All rights reserved. * Copyright 2003-2009, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -23,20 +23,20 @@
#endif #endif
/* This is a very simple malloc()/free() implementation - it only /*! This is a very simple malloc()/free() implementation - it only
* manages a free list. manages a free list.
* After heap_init() is called, all free memory is contained in one After heap_init() is called, all free memory is contained in one
* big chunk, the only entry in the free link list (which is a single big chunk, the only entry in the free link list (which is a single
* linked list). linked list).
* When memory is allocated, the smallest free chunk that contains When memory is allocated, the smallest free chunk that contains
* the requested size is split (or taken as a whole if it can't be the requested size is split (or taken as a whole if it can't be
* splitted anymore), and it's lower half will be removed from the splitted anymore), and it's lower half will be removed from the
* free list. free list.
* The free list is ordered by size, starting with the smallest The free list is ordered by size, starting with the smallest
* free chunk available. When a chunk is freed, it will be joint free chunk available. When a chunk is freed, it will be joint
* with its predecessor or successor, if possible. with its predecessor or successor, if possible.
* To ease list handling, the list anchor itself is a free chunk with To ease list handling, the list anchor itself is a free chunk with
* size 0 that can't be allocated. size 0 that can't be allocated.
*/ */
#define DEBUG_ALLOCATIONS #define DEBUG_ALLOCATIONS
@@ -58,6 +58,9 @@ struct free_chunk {
}; };
const static uint32 kAlignment = 4;
// all memory chunks will be a multiple of this
static void* sHeapBase; static void* sHeapBase;
static uint32 /*sHeapSize,*/ sMaxHeapSize, sAvailable; static uint32 /*sHeapSize,*/ sMaxHeapSize, sAvailable;
static free_chunk sFreeAnchor; static free_chunk sFreeAnchor;
@@ -149,7 +152,8 @@ free_chunk::Remove(free_chunk *previous)
void void
free_chunk::Enqueue() free_chunk::Enqueue()
{ {
free_chunk *chunk = sFreeAnchor.next, *last = &sFreeAnchor; free_chunk* chunk = sFreeAnchor.next;
free_chunk* last = &sFreeAnchor;
while (chunk && chunk->Size() < size) { while (chunk && chunk->Size() < size) {
last = chunk; last = chunk;
chunk = chunk->next; chunk = chunk->next;
@@ -191,7 +195,8 @@ heap_release(stage2_args *args)
status_t status_t
heap_init(stage2_args* args) heap_init(stage2_args* args)
{ {
void *base, *top; void* base;
void* top;
if (platform_init_heap(args, &base, &top) < B_OK) if (platform_init_heap(args, &base, &top) < B_OK)
return B_ERROR; return B_ERROR;
@@ -234,11 +239,13 @@ grow_heap(uint32 bytes)
void void
dump_chunks(void) dump_chunks(void)
{ {
free_chunk *chunk = sFreeAnchor.next, *last = &sFreeAnchor; free_chunk* chunk = sFreeAnchor.next;
free_chunk* last = &sFreeAnchor;
while (chunk != NULL) { while (chunk != NULL) {
last = chunk; last = chunk;
printf("\t%p: chunk size = %ld, end = %p, next = %p\n", chunk, chunk->size, (uint8 *)chunk + chunk->size, chunk->next); printf("\t%p: chunk size = %ld, end = %p, next = %p\n", chunk,
chunk->size, (uint8*)chunk + chunk->size, chunk->next);
chunk = chunk->next; chunk = chunk->next;
} }
} }
@@ -257,15 +264,16 @@ malloc(size_t size)
if (sHeapBase == NULL || size == 0) if (sHeapBase == NULL || size == 0)
return NULL; return NULL;
// align the size requirement to a 4 bytes boundary // align the size requirement to a kAlignment bytes boundary
size = (size + 3) & 0xfffffffc; size = (size - 1 + kAlignment) & ~(size_t)(kAlignment - 1);
if (size > sAvailable) { if (size > sAvailable) {
dprintf("malloc(): Out of memory!\n"); dprintf("malloc(): Out of memory!\n");
return NULL; return NULL;
} }
free_chunk *chunk = sFreeAnchor.next, *last = &sFreeAnchor; free_chunk* chunk = sFreeAnchor.next;
free_chunk* last = &sFreeAnchor;
while (chunk && chunk->Size() < size) { while (chunk && chunk->Size() < size) {
last = chunk; last = chunk;
chunk = chunk->next; chunk = chunk->next;
@@ -277,10 +285,10 @@ malloc(size_t size)
return NULL; return NULL;
} }
if (chunk->Size() > size + sizeof(free_chunk) + 4) { if (chunk->Size() > size + sizeof(free_chunk) + kAlignment) {
// if this chunk is bigger than the requested size, // if this chunk is bigger than the requested size,
// we split it to form two chunks (with a minimal // we split it to form two chunks (with a minimal
// size of 4 allocatable bytes). // size of kAlignment allocatable bytes).
free_chunk* freeChunk = chunk->Split(size); free_chunk* freeChunk = chunk->Split(size);
last->next = freeChunk; last->next = freeChunk;
@@ -304,25 +312,34 @@ malloc(size_t size)
void* void*
realloc(void* oldBuffer, size_t newSize) realloc(void* oldBuffer, size_t newSize)
{ {
// ToDo: improve this implementation!
if (newSize == 0) { if (newSize == 0) {
TRACE("realloc(%p, %lu) -> NULL\n", oldBuffer, newSize); TRACE("realloc(%p, %lu) -> NULL\n", oldBuffer, newSize);
free(oldBuffer); free(oldBuffer);
return NULL; return NULL;
} }
size_t copySize = newSize;
if (oldBuffer != NULL) {
free_chunk* oldChunk = free_chunk::SetToAllocated(oldBuffer);
// Check if the old buffer still fits, and if it makes sense to keep it
if (oldChunk->size >= newSize
&& (oldChunk->size < 128 || newSize > oldChunk->size / 3)) {
TRACE("realloc(%p, %lu) old buffer is large enough\n",
oldBuffer, newSize);
return oldChunk->AllocatedAddress();
}
if (copySize > oldChunk->size)
copySize = oldChunk->size;
}
void* newBuffer = malloc(newSize); void* newBuffer = malloc(newSize);
if (newBuffer == NULL) if (newBuffer == NULL)
return NULL; return NULL;
if (oldBuffer) { if (oldBuffer != NULL) {
free_chunk *oldChunk = free_chunk::SetToAllocated(oldBuffer); memcpy(newBuffer, oldBuffer, copySize);
if (newSize > oldChunk->size)
newSize = oldChunk->size;
memcpy(newBuffer, oldBuffer, newSize);
free(oldBuffer); free(oldBuffer);
} }
@@ -359,7 +376,8 @@ free(void *allocated)
// try to join the new free chunk with an existing one // try to join the new free chunk with an existing one
// it may be joined with up to two chunks // it may be joined with up to two chunks
free_chunk *chunk = sFreeAnchor.next, *last = &sFreeAnchor; free_chunk* chunk = sFreeAnchor.next;
free_chunk* last = &sFreeAnchor;
int32 joinCount = 0; int32 joinCount = 0;
while (chunk) { while (chunk) {