* 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:
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user