* 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
+92 -74
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,42 +23,45 @@
#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
// if defined, freed memory is filled with 0xcc // if defined, freed memory is filled with 0xcc
struct free_chunk { struct free_chunk {
uint32 size; uint32 size;
free_chunk *next; free_chunk* next;
uint32 Size() const; uint32 Size() const;
free_chunk *Split(uint32 splitSize); free_chunk* Split(uint32 splitSize);
bool IsTouching(free_chunk *link); bool IsTouching(free_chunk* link);
free_chunk *Join(free_chunk *link); free_chunk* Join(free_chunk* link);
void Remove(free_chunk *previous = NULL); void Remove(free_chunk* previous = NULL);
void Enqueue(); void Enqueue();
void *AllocatedAddress() const; void* AllocatedAddress() const;
static free_chunk *SetToAllocated(void *allocated); static free_chunk* SetToAllocated(void* allocated);
}; };
static void *sHeapBase; const static uint32 kAlignment = 4;
// all memory chunks will be a multiple of this
static void* sHeapBase;
static uint32 /*sHeapSize,*/ sMaxHeapSize, sAvailable; static uint32 /*sHeapSize,*/ sMaxHeapSize, sAvailable;
static free_chunk sFreeAnchor; static free_chunk sFreeAnchor;
@@ -76,10 +79,10 @@ free_chunk::Size() const
/*! Splits the upper half at the requested location /*! Splits the upper half at the requested location
and returns it. and returns it.
*/ */
free_chunk * free_chunk*
free_chunk::Split(uint32 splitSize) free_chunk::Split(uint32 splitSize)
{ {
free_chunk *chunk = (free_chunk *)((uint8 *)this + sizeof(uint32) + splitSize); free_chunk* chunk = (free_chunk*)((uint8*)this + sizeof(uint32) + splitSize);
chunk->size = size - splitSize - sizeof(uint32); chunk->size = size - splitSize - sizeof(uint32);
chunk->next = next; chunk->next = next;
@@ -93,11 +96,11 @@ free_chunk::Split(uint32 splitSize)
that they could be joined. that they could be joined.
*/ */
bool bool
free_chunk::IsTouching(free_chunk *chunk) free_chunk::IsTouching(free_chunk* chunk)
{ {
return chunk return chunk
&& (((uint8 *)this + size == (uint8 *)chunk) && (((uint8*)this + size == (uint8*)chunk)
|| (uint8 *)chunk + chunk->size == (uint8 *)this); || (uint8*)chunk + chunk->size == (uint8*)this);
} }
@@ -108,8 +111,8 @@ free_chunk::IsTouching(free_chunk *chunk)
doesn't work correctly. Use free_chunk::IsTouching() doesn't work correctly. Use free_chunk::IsTouching()
to check if this method can be applied. to check if this method can be applied.
*/ */
free_chunk * free_chunk*
free_chunk::Join(free_chunk *chunk) free_chunk::Join(free_chunk* chunk)
{ {
if (chunk < this) { if (chunk < this) {
chunk->size += size; chunk->size += size;
@@ -126,11 +129,11 @@ free_chunk::Join(free_chunk *chunk)
void void
free_chunk::Remove(free_chunk *previous) free_chunk::Remove(free_chunk* previous)
{ {
if (previous == NULL) { if (previous == NULL) {
// find the previous chunk in the list // find the previous chunk in the list
free_chunk *chunk = sFreeAnchor.next; free_chunk* chunk = sFreeAnchor.next;
while (chunk != NULL && chunk != this) { while (chunk != NULL && chunk != this) {
previous = chunk; previous = chunk;
@@ -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;
@@ -164,17 +168,17 @@ free_chunk::Enqueue()
} }
void * void*
free_chunk::AllocatedAddress() const free_chunk::AllocatedAddress() const
{ {
return (void *)&next; return (void*)&next;
} }
free_chunk * free_chunk*
free_chunk::SetToAllocated(void *allocated) free_chunk::SetToAllocated(void* allocated)
{ {
return (free_chunk *)((uint8 *)allocated - sizeof(uint32)); return (free_chunk*)((uint8*)allocated - sizeof(uint32));
} }
@@ -182,27 +186,28 @@ free_chunk::SetToAllocated(void *allocated)
void void
heap_release(stage2_args *args) heap_release(stage2_args* args)
{ {
platform_release_heap(args, sHeapBase); platform_release_heap(args, sHeapBase);
} }
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;
sHeapBase = base; sHeapBase = base;
sMaxHeapSize = (uint8 *)top - (uint8 *)base; sMaxHeapSize = (uint8*)top - (uint8*)base;
sAvailable = sMaxHeapSize - sizeof(uint32); sAvailable = sMaxHeapSize - sizeof(uint32);
// declare the whole heap as one chunk, and add it // declare the whole heap as one chunk, and add it
// to the free list // to the free list
free_chunk *chunk = (free_chunk *)base; free_chunk* chunk = (free_chunk*)base;
chunk->size = sMaxHeapSize; chunk->size = sMaxHeapSize;
chunk->next = NULL; chunk->next = NULL;
@@ -214,15 +219,15 @@ heap_init(stage2_args *args)
#if 0 #if 0
char * char*
grow_heap(uint32 bytes) grow_heap(uint32 bytes)
{ {
char *start; char* start;
if (sHeapSize + bytes > sMaxHeapSize) if (sHeapSize + bytes > sMaxHeapSize)
return NULL; return NULL;
start = (char *)sHeapBase + sHeapSize; start = (char*)sHeapBase + sHeapSize;
memset(start, 0, bytes); memset(start, 0, bytes);
sHeapSize += bytes; sHeapSize += bytes;
@@ -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;
} }
} }
@@ -251,21 +258,22 @@ heap_available(void)
} }
void * void*
malloc(size_t size) 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,12 +285,12 @@ 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;
// re-enqueue the free chunk at the correct position // re-enqueue the free chunk at the correct position
@@ -301,28 +309,37 @@ 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;
} }
void *newBuffer = malloc(newSize); 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);
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);
} }
@@ -332,20 +349,20 @@ realloc(void *oldBuffer, size_t newSize)
void void
free(void *allocated) free(void* allocated)
{ {
if (allocated == NULL) if (allocated == NULL)
return; return;
TRACE("free(%p)\n", allocated); TRACE("free(%p)\n", allocated);
free_chunk *freedChunk = free_chunk::SetToAllocated(allocated); free_chunk* freedChunk = free_chunk::SetToAllocated(allocated);
#ifdef DEBUG_ALLOCATIONS #ifdef DEBUG_ALLOCATIONS
if (freedChunk->size > sMaxHeapSize) if (freedChunk->size > sMaxHeapSize)
panic("freed chunk %p clobbered (%lx)!\n", freedChunk, freedChunk->size); panic("freed chunk %p clobbered (%lx)!\n", freedChunk, freedChunk->size);
{ {
free_chunk *chunk = sFreeAnchor.next; free_chunk* chunk = sFreeAnchor.next;
while (chunk) { while (chunk) {
if (chunk->size > sMaxHeapSize || freedChunk == chunk) if (chunk->size > sMaxHeapSize || freedChunk == chunk)
panic("invalid chunk in free list, or double free\n"); panic("invalid chunk in free list, or double free\n");
@@ -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) {