thug/Code/Sys/Mem/heap.cpp
2016-02-14 08:39:12 +11:00

1156 lines
34 KiB
C++

/*****************************************************************************
** **
** Neversoft Entertainment **
** **
** Copyright (C) 1999 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: System Library **
** **
** Module: Memory (Mem) **
** **
** File name: heap.cpp **
** **
** Maintained by: Mick **
** **
** Description: Heap class **
** **
*****************************************************************************/
// (Mick) undefine this line if you want the full PS2 callstack reporting on allocations
// and the trashing of memory
// Also you might want to set gHeapPools in Poolable.cpp
// which makes the pools use the debug heap, so this debuggin applies to them.
#define __PURE_HEAP__ // No debugging
//////////////////////////////////////////////////////
// Memory Debugging Trick:
// dump the call stack for allocations and de-allocation of
// a particular block
// (maybe extend this to any block that encompasses this address????)
#define REPORT_ON 0 // would be somethng like 0x3a2b50, if you want it to work
/*****************************************************************************
** Includes **
*****************************************************************************/
#include <core/defines.h>
#include "memman.h"
#include "heap.h"
#include <sys\mem\region.h>
#define DUMP_HEAP 0 // Change to 1 to have a handy text dump in memory of the heap status.
// Use the debugger to see it - look at gMemStatus, 64 bytes per line.
#define NUM_ADDRESS 0 // Change this to 8 to have a handy list of addresses to check.
// If set to 0, no extra code/data will be generated.
// Remember to change to 8, compile, then get the addresses in question
// as the act of adding this code/data will change your addresses.
#if NUM_ADDRESS > 0
uint32 gAddress[NUM_ADDRESS] =
{
0x81828561,
0x818284c3,
0x81827621,
0x818275e3,
0x81827541,
0x81826523,
0x818264e1,
0x81826443
};
int g128 = 0;
int g32 = 0;
static void check_address( void * p, int size )
{
for ( int lp = 0; lp < 8; lp++ )
{
if ( gAddress[lp] == ((uint32)p) ) {
Dbg_Message( "We found the address we're looking for: 0x%08x (%d)\n", (uint32)p, size );
}
}
if ( size == 32 )
{
g32++;
}
if ( size == 128 )
{
g128++;
}
}
#else
#define check_address(a,b)
#endif
#if DUMP_HEAP == 1
#ifdef __PLAT_XBOX__
static SIZE_T peakUsage = 64 * 1024 * 1024; // 64mb.
char gMemStatus[64*28];
#else
char gMemStatus[64*16];
#endif
void dump_heap( void )
{
Mem::Manager& mem_man = Mem::Manager::sHandle();
sprintf(&gMemStatus[64*0], "Name Used Frag Free Blocks \n");
sprintf(&gMemStatus[64*1], "--------------- ------ ------ ------ ------ \n");
Mem::Heap* heap;
int line = 2;
for (heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap))
{
Mem::Region* region = heap->ParentRegion();
sprintf( &gMemStatus[64*line], "%12s: %5dK %5dK %5dK %5d \n",
heap->GetName(),
heap->mUsedMem.m_count / 1024,
heap->mFreeMem.m_count / 1024,
region->MemAvailable() / 1024,
heap->mUsedBlocks.m_count
);
line++;
}
# ifdef __PLAT_XBOX__
MEMORYSTATUS stat;
GlobalMemoryStatus( &stat );
sprintf( &gMemStatus[64 * line++], "%4d free kb of physical memory.\n", stat.dwAvailPhys / 1024 );
if( stat.dwAvailPhys < peakUsage )
peakUsage = stat.dwAvailPhys;
sprintf( &gMemStatus[64 * line++], "%4d free kb at peak physical memory usage.\n", peakUsage / 1024 );
# endif
}
#endif // DUMP_HEAP == 1
namespace Script
{
class CStruct;
class CScript;
}
namespace CFuncs
{
bool ScriptDumpHeaps( Script::CStruct *pParams, Script::CScript *pScript );
}
#ifdef __PLAT_NGPS__
#include <gfx/ngps/p_memview.h>
#elif defined( __PLAT_NGC__ )
#include <gfx/ngc/p_memview.h>
#endif
/*****************************************************************************
** DBG Information **
*****************************************************************************/
namespace Mem
{
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
#ifndef __NOPT_ASSERT__
#define __PURE_HEAP__
#endif
#ifndef __PURE_HEAP__
#define __TRASH_BLOCKS__ // define this to trash contents of alloc and free, regardless of build
#endif
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
inline Mem::Allocator::BlockHeader* Heap::next_addr( Allocator::BlockHeader* pHeader )
{
Dbg_AssertType( pHeader, BlockHeader );
return (BlockHeader*)( (uint)pHeader + BlockHeader::sSize + pHeader->mSize );
}
#ifdef __TRASH_BLOCKS__
//static const uint64 vTRASH_ALLOCATED_BLOCK = 0x0101010101010101;
static const uint64 vTRASH_ALLOCATED_BLOCK = 0x5555555555555555LL; // grey
static const uint64 vTRASH_FREE_BLOCK = 0xbbbbbbbbbbbbbbbbLL; // white
//static const uint64 vTRASH_FREE_BLOCK = 0x3f3f003f3f003f3f; // ??? ??? (with 0 so string are terminated)
inline void Trash_AllocateBlock( Mem::Allocator::BlockHeader* pBlock )
{
#ifdef __EFFICIENT__
memset( (void*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize ), vTRASH_ALLOCATED_BLOCK & 0xff, pBlock->mSize );
#else
uint64* ptr = (uint64*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize );
for ( uint i = 0; i < pBlock->mSize; i += 8 )
{
*ptr++ = vTRASH_ALLOCATED_BLOCK;
}
#endif // __EFFICIENT__
}
/******************************************************************/
/* */
/* */
/******************************************************************/
inline void Trash_FreeBlock( Mem::Allocator::BlockHeader* pBlock )
{
#ifdef __EFFICIENT__
memset( (void*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize ), vTRASH_FREE_BLOCK & 0xff, pBlock->mSize );
#else
uint64* ptr = (uint64*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize );
for ( uint i = 0; i < pBlock->mSize; i += 8 )
{
*ptr++ = vTRASH_FREE_BLOCK;
}
#endif // __EFFICIENT__
}
inline void Trash_FreeBlockHeader( Mem::Allocator::BlockHeader* pBlock )
{
#ifdef __EFFICIENT__
memset( (void*)((uint)pBlock), vTRASH_FREE_BLOCK & 0xff, Mem::Allocator::BlockHeader::sSize );
#else
uint64* ptr = (uint64*)((uint)pBlock);
for ( uint i = 0; i < Mem::Allocator::BlockHeader::sSize; i += 8 )
{
*ptr++ = vTRASH_FREE_BLOCK;
}
#endif // __EFFICIENT__
}
#endif
/******************************************************************/
/* */
/* */
/******************************************************************/
void Heap::free ( BlockHeader* pFreeBlock )
{
#if DUMP_HEAP == 1
dump_heap();
#endif // DUMP_HEAP == 1
#ifdef __EFFICIENT__
pFreeBlock->mSize += pFreeBlock->mPadBytes;
pFreeBlock->mPadBytes = 0;
#endif // __EFFICIENT__
Dbg_AssertType ( pFreeBlock, BlockHeader );
#ifdef __NOPT_ASSERT__
#ifdef __PLAT_NGPS__
// find what feeded a particular block
if ( ((uint)pFreeBlock + BlockHeader::sSize) == REPORT_ON)
{
printf ("Freeing 0x%x",REPORT_ON);
DumpUnwindStack(20,NULL);
}
#endif
#endif
BlockHeader* p_before = NULL;
BlockHeader* p_2before = NULL;
BlockHeader* p_after = mp_context->mp_free_list;
#ifdef __LINKED_LIST_HEAP__
#ifdef __PLAT_NGPS__
#ifndef __PURE_HEAP__
MemView_Free((void*)pFreeBlock);
#endif
#endif
FreeMemProfile(pFreeBlock);
// mp_next_used and mp_prev_used are a doble linked null ternimated list
// unlink next and prev
if ( pFreeBlock->mp_next_used )
{
pFreeBlock->mp_next_used->mp_prev_used = pFreeBlock->mp_prev_used;
}
if ( pFreeBlock->mp_prev_used )
{
pFreeBlock->mp_prev_used->mp_next_used = pFreeBlock->mp_next_used;
}
// unlink if it's the head of the list
if ( mp_context->mp_used_list == pFreeBlock )
{
mp_context->mp_used_list = pFreeBlock->mp_next_used;
}
pFreeBlock->mp_prev_used = NULL;
pFreeBlock->mp_next_used = NULL;
#endif
// we trash the free block before we merge it with anything
// otherwise we end us trashing multi-megabytes
// when memory is fragmented (which it often is, in the middle of things)
#ifdef __TRASH_BLOCKS__
Trash_FreeBlock ( pFreeBlock );
#endif
mFreeBlocks++;
mFreeMem += pFreeBlock->mSize;
mUsedBlocks--;
mUsedMem -= pFreeBlock->mSize;
// p_after starts at the head of the free list
// and traverses it until p_after is the block after this
// block
while ( p_after && ( (uint)p_after < (uint)pFreeBlock ))
{
p_2before = p_before;
p_before = p_after;
p_after = p_after->mpNext;
}
pFreeBlock->mpNext = p_after; // insert pFreeBlock before p_after
if ( p_after )
{
if ( next_addr( pFreeBlock ) == p_after ) // p_after joins pFreeBlock
{
pFreeBlock->mpNext = p_after->mpNext;
pFreeBlock->mSize += p_after->mSize + BlockHeader::sSize;
p_after->~BlockHeader();
#ifdef __TRASH_BLOCKS__
Trash_FreeBlockHeader ( p_after );
#endif
mFreeBlocks--;
mFreeMem += BlockHeader::sSize;
mUsedMem -= BlockHeader::sSize;
}
}
if ( p_before )
{
if ( next_addr( p_before ) == pFreeBlock ) // pFreeBlock joins p_before
{
p_before->mSize += pFreeBlock->mSize + BlockHeader::sSize;
p_before->mpNext = pFreeBlock->mpNext;
pFreeBlock->~BlockHeader();
#ifdef __TRASH_BLOCKS__
Trash_FreeBlockHeader ( pFreeBlock );
#endif
mFreeBlocks--;
mFreeMem += BlockHeader::sSize;
mUsedMem -= BlockHeader::sSize;
pFreeBlock = p_before;
p_before = p_2before;
}
else // insert pFreeBlock after p_before
{
p_before->mpNext = pFreeBlock;
}
}
else
{
mp_context->mp_free_list = pFreeBlock;
}
MemDbg_FreeBlock ( pFreeBlock );
// reclaim free space in region
if ( ( m_dir == vBOTTOM_UP ) && ( !p_after ) &&
( mp_top == (void*)((uint)pFreeBlock + pFreeBlock->mSize + BlockHeader::sSize )))
{
if( p_before )
{
p_before->mpNext = NULL;
}
else
{
mp_context->mp_free_list = NULL;
}
mp_top = (void*)((uint)mp_top - ( pFreeBlock->mSize + BlockHeader::sSize) * m_dir );
pFreeBlock->~BlockHeader();
mFreeBlocks--;
mFreeMem -= pFreeBlock->mSize;
mUsedMem -= BlockHeader::sSize;
#ifdef __TRASH_BLOCKS__
Trash_FreeBlockHeader ( pFreeBlock );
#endif
}
else if (( m_dir == vTOP_DOWN ) && ( !p_before ) &&
( mp_top == (void*)((uint)pFreeBlock )))
{
mp_top = (void*)((uint)mp_top - ( pFreeBlock->mSize + BlockHeader::sSize) * m_dir );
mp_context->mp_free_list = pFreeBlock->mpNext;
pFreeBlock->~BlockHeader();
mFreeBlocks--;
mFreeMem -= pFreeBlock->mSize;
mUsedMem -= BlockHeader::sSize;
#ifdef __TRASH_BLOCKS__
Trash_FreeBlockHeader ( pFreeBlock );
#endif
}
#if 0
Mem::Manager& mem_man = Mem::Manager::sHandle();
Mem::Heap* heap = mem_man.BottomUpHeap();
Mem::Region* region = heap->ParentRegion();
if (heap->mFreeMem.m_count > 3200*1000)
{
printf ("\nBottomUp Fragmentation %7dK, in %5d Blocks\n",heap->mFreeMem.m_count / 1024, heap->mFreeBlocks.m_count);
DumpUnwindStack(20,NULL);
MemView_DumpFragments(heap);
Dbg_MsgAssert(0,("Frag"));
}
#endif
}
// Returns the size in bytes of either the largest free fragmented block,
// or the free space in the region whichever is larger
int Heap::LargestFreeBlock()
{
uint32 size = 0;
BlockHeader* p_header = mp_context->mp_free_list;
while ( p_header )
{
if ( p_header->mSize >= size )
{
size = p_header->mSize;
}
p_header = p_header->mpNext;
}
uint32 region_size = mp_region->MemAvailable();
if (region_size > size)
{
size = region_size;
}
return size;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// Note the block returned might be bigger than asked for
// by 16 or 32 bytes.
// memory system calls MUST account for this
void* Heap::allocate( size_t size, bool assert_on_fail )
{
#ifdef __PLAT_NGPS__
// if (size > 10*1024)
// {
// printf ("\nAllocating %d\n",size);
// printf ("%s\n",Mem::Manager::sHandle().GetContextName());
// }
#endif
Dbg_MsgAssert(size >0, ("Trying to allocate Zero bytes\n"));
/*
if (size == 0)
{
static int zeros=0;
printf("(%d) Trying to allocate Zero bytes\n", zeros++);
DumpUnwindStack(20,NULL);
}
*/
#if DUMP_HEAP == 1
dump_heap();
#endif // DUMP_HEAP == 1
BlockHeader* p_header = mp_context->mp_free_list;
BlockHeader* p_freeblock = NULL;
BlockHeader* p_last = NULL;
BlockHeader* p_prev = NULL;
// printf ("Trying to allocate %d bytes, current free list = %p\n",size,p_header);
// if (size == 8688) while (1);
#ifdef __EFFICIENT__
int align = 1 << GetAlign();
int offset_bytes;
size = (uint)nAlignUpBy( size, 2 ); // all allocations aligned by 4 bytes
#else
#ifdef __PLAT_NGC__
size = (uint)nAlignUpBy( size, 5 ); // all allocations aligned by 16 bytes
#else
size = (uint)nAlignUpBy( size, 4 ); // all allocations aligned by 16 bytes
#endif
#endif
while ( p_header ) // find smallest free block large enough to fulfill request
{
Dbg_AssertType ( p_header, BlockHeader );
#ifdef __EFFICIENT__
int off = ( ( ( (int)p_header + BlockHeader::sSize ) + ( align - 1 ) ) & -align ) - ( (int)p_header + BlockHeader::sSize );
uint adjusted_size = size + off;
if ( p_header->mSize >= adjusted_size )
#else
if ( p_header->mSize >= size )
#endif
{
if (( p_freeblock == NULL ) || ( p_freeblock->mSize > p_header->mSize ))
{
p_prev = p_last;
p_freeblock = p_header;
}
}
p_last = p_header;
p_header = p_header->mpNext;
}
if ( p_freeblock ) // found a free block large enough
{
#ifdef __EFFICIENT__
offset_bytes = ( ( ( (int)p_freeblock + BlockHeader::sSize ) + ( align - 1 ) ) & -align ) - ( (int)p_freeblock + BlockHeader::sSize );
#endif
if ( p_prev )
{
p_prev->mpNext = p_freeblock->mpNext;
}
else
{
mp_context->mp_free_list = p_freeblock->mpNext;
}
p_freeblock->mpAlloc = this;
mFreeBlocks--;
mUsedBlocks++;
mFreeMem -= p_freeblock->mSize;
mUsedMem += p_freeblock->mSize;
#ifdef __EFFICIENT__
BlockHeader* p_leftover = (BlockHeader*)((uint)p_freeblock + BlockHeader::sSize + size + offset_bytes );
int new_size = p_freeblock->mSize - size - BlockHeader::sSize - offset_bytes;
#else
BlockHeader* p_leftover = (BlockHeader*)((uint)p_freeblock + BlockHeader::sSize + size );
int new_size = p_freeblock->mSize - size - BlockHeader::sSize;
#endif // __EFFICIENT__
if ( new_size > 0 ) // create new free node for left-over memory
{
p_freeblock->mSize = size;
new ((void*)p_leftover) BlockHeader( this, new_size );
mUsedBlocks++;
free( p_leftover );
}
else
{
// new_size is 0, which implies that this block is (size) ... (size + BlockHeader::Size)
// which means we'll be allocating a bit more than we need
// but as (size) is not reference again, then that's not a problem
#ifdef __EFFICIENT__
p_freeblock->mSize -= offset_bytes;
#endif // __EFFICIENT__
}
}
else // request extra space from region
{
p_freeblock = (BlockHeader*)mp_region->Allocate( this, size + BlockHeader::sSize, assert_on_fail );
#ifdef __EFFICIENT__
if ( m_dir == vBOTTOM_UP )
{
offset_bytes = ( ( ( (int)p_freeblock + BlockHeader::sSize ) + ( align - 1 ) ) & -align ) - ( (int)p_freeblock + BlockHeader::sSize );
// Allocate, but keep the original pointer.
mp_region->Allocate( this, offset_bytes, assert_on_fail );
}
else
{
offset_bytes = ( (int)p_freeblock + BlockHeader::sSize ) - ( ( (int)p_freeblock + BlockHeader::sSize ) & -align );
// Allocate, increase size, not offset bytes.
if ( offset_bytes )
{
p_freeblock = (BlockHeader*)mp_region->Allocate( this, offset_bytes, assert_on_fail );
size += offset_bytes;
offset_bytes = 0;
}
}
#endif
if ( p_freeblock )
{
new ((void*)p_freeblock) BlockHeader( this, size );
mUsedBlocks++;
#ifdef __EFFICIENT__
mUsedMem += size + BlockHeader::sSize + offset_bytes;
#else
mUsedMem += size + BlockHeader::sSize;
#endif // __EFFICIENT__
}
}
if ( p_freeblock )
{
MemDbg_AllocateBlock ( p_freeblock );
#ifdef __TRASH_BLOCKS__
Trash_AllocateBlock ( p_freeblock );
#endif
#ifdef __LINKED_LIST_HEAP__
if( mp_context->mp_used_list )
{
p_freeblock->mp_next_used = mp_context->mp_used_list;
mp_context->mp_used_list->mp_prev_used = p_freeblock;
}
else
{
p_freeblock->mp_next_used = NULL;
}
p_freeblock->mp_prev_used = NULL;
#ifdef __PLAT_NGPS__
#ifndef __PURE_HEAP__
MemView_Alloc((void*)p_freeblock);
#endif
#endif
mp_context->mp_used_list = p_freeblock;
#endif
AllocMemProfile(p_freeblock);
#ifdef __NOPT_ASSERT__
#ifdef __PLAT_NGPS__
// find what allocated a particular block
if ( ((uint)p_freeblock + BlockHeader::sSize) == REPORT_ON)
{
printf ("allocating 0x%x",REPORT_ON);
DumpUnwindStack(20,NULL);
}
#endif
#endif
#ifdef __EFFICIENT__
check_address( (void*)((uint)p_freeblock + BlockHeader::sSize + offset_bytes), size );
// Fill padding bytes with bytes offset value.
p_freeblock->mPadBytes = offset_bytes;
uint8 * p8 = (uint8*)((uint)p_freeblock + BlockHeader::sSize);
for ( int lp = 0; lp < offset_bytes; lp++ )
{
*p8++ = offset_bytes;
}
return (void*)((uint)p_freeblock + BlockHeader::sSize + offset_bytes);
#else
check_address( (void*)((uint)p_freeblock + BlockHeader::sSize), size );
return (void*)((uint)p_freeblock + BlockHeader::sSize);
#endif // __EFFICIENT__
}
// Mick: heap allocations currently coded to ALWAYS assert on fail
// if ( assert_on_fail )
{
Dbg_MsgAssert ( false,( "Failed to allocate %d bytes", size ));
}
// Mem::Heap* heap = Mem::Manager::sHandle().BottomUpHeap();
// printf ("Dumping Fragments.....\n");
// MemView_DumpFragments(heap);
printf ("----------------------------------\n");
printf ("failed to allocate %d bytes\n", size);
#ifdef __PLAT_NGC__
#ifndef __NOPT_FINAL__
{
Mem::Manager& mem_man = Mem::Manager::sHandle();
printf ("MEM CONTEXT: %s\n",Mem::Manager::sHandle().GetContextName());
printf("Name Used Frag Free Blocks\n");
printf("--------------- ----- ----- ------ ------\n");
Mem::Heap* heap;
for (heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap))
{
Mem::Region* region = heap->ParentRegion();
printf( "%12s: %6dK %6dK %6dK %6d \n",
heap->GetName(),
heap->mUsedMem.m_count / 1024,
heap->mFreeMem.m_count / 1024,
region->MemAvailable() / 1024,
heap->mUsedBlocks.m_count
);
}
}
#endif
#endif // __PLAT_NGC__
#ifndef __PLAT_WN32__
CFuncs::ScriptDumpHeaps(NULL,NULL);
#endif
return NULL;
}
int Heap::available()
{
return LargestFreeBlock();
}
// Ken addition, for use by pip.cpp when it needs to reallocate the block of memory used
// by a pre file that has just been loaded in, so that the pre can be decompressed.
//
// This function will return a pointer to a new block of memory which includes the original
// block, but such that the returned pointer is lower down in memory than the original.
// The original contents of the memory block will be unchanged.
// This function is most likely to work if used on the top-down heap.
// It will assert if it does not work.
void* Heap::reallocate_down( size_t newSize, void *pOld )
{
// What this function is going to do is allocate a new block with size equal to
// the difference between newSize and the size of pOld, then see if the new block
// is directly below the passed pOld.
// If it is, then it will merge the two blocks together.
// If it is not, it will assert.
Dbg_MsgAssert(pOld,("NULL pOld sent to reallocate_down !"));
BlockHeader* p_old_block = BlockHeader::sRead( pOld );
#ifdef __EFFICIENT__
newSize = (uint)nAlignUpBy( newSize, 2 ); // all allocations aligned by 4 bytes
#else
#ifdef __PLAT_NGC__
newSize = (uint)nAlignUpBy( newSize, 5 ); // all allocations aligned by 32 bytes
#else
newSize = (uint)nAlignUpBy( newSize, 4 ); // all allocations aligned by 16 bytes
#endif
#endif // __EFFICIENT__
Dbg_MsgAssert(p_old_block->mSize < newSize,("Tried to reallocate a block that was already big enough, old size=%d, requested size=%d",p_old_block->mSize,newSize));
// When bobbling together the new and old blocks, we will gain the space used by the old
// block header, so we don't really need to include that space in the new allocation request.
// But, if newSize is exactly a blockheader size bigger than the old, then that would cause
// allocate to be called on a size of zero.
// So allocate a blockheader size more than necessary to avoid this.
void *p_new=allocate(newSize-p_old_block->mSize,true);
Dbg_MsgAssert(p_new,("allocate failed!"));
// Got the new block, so now check that it is directly below the old.
BlockHeader* p_new_block=BlockHeader::sRead( p_new );
Dbg_MsgAssert( (BlockHeader*)(((uint)p_new)+p_new_block->mSize) == p_old_block,("reallocate_down failed! New block is not directly below the old."));
// Got this far, so the new block is right below the old.
// Now we have to remove the old block header so as to bobble the two blocks together.
#ifdef __LINKED_LIST_HEAP__
#ifdef __PLAT_NGPS__
#ifndef __PURE_HEAP__
// Both blocks need to be freed from the memory tracker
// as together they take up all the space of the new block, whcih will be added below
MemView_Free((void*)p_old_block);
MemView_Free((void*)p_new_block);
#endif
#endif
// mp_next_used and mp_prev_used are a doble linked null ternimated list
// unlink next and prev
if ( p_old_block->mp_next_used )
{
p_old_block->mp_next_used->mp_prev_used = p_old_block->mp_prev_used;
}
if ( p_old_block->mp_prev_used )
{
p_old_block->mp_prev_used->mp_next_used = p_old_block->mp_next_used;
}
// unlink if it's the head of the list
if ( mp_context->mp_used_list == p_old_block )
{
mp_context->mp_used_list = p_old_block->mp_next_used;
}
p_old_block->mp_prev_used = NULL;
p_old_block->mp_next_used = NULL;
#endif
// Free both blocks, so we can just add one later
FreeMemProfile(p_old_block);
FreeMemProfile(p_new_block);
#ifdef __EFFICIENT__
newSize += p_old_block->mPadBytes;
#endif // __EFFICIENT__
p_old_block->~BlockHeader();
#ifdef __TRASH_BLOCKS__
Trash_FreeBlockHeader ( p_old_block );
#endif
// The memory occupied by the old block header has gone from being used as a BlockHeader,
// to being used as part of a used memory block, so mUsedMem does not need to change.
// The number of used blocks has changed though, since two are being merged into one.
--mUsedBlocks;
// Nothing has changed regarding free blocks, so mFreeBlocks and mFreeMem stay the same.
// That's p_old_block cleaned up, so now modify p_new_block's size.
p_new_block->mSize=newSize+BlockHeader::sSize;
#ifdef __PLAT_NGPS__
#ifndef __PURE_HEAP__
MemView_Alloc((void*)p_new_block);
#endif
#endif
AllocMemProfile(p_new_block);
#ifdef __EFFICIENT__
return (void*)((uint)p_new_block + BlockHeader::sSize + p_new_block->mPadBytes);
#else
return (void*)((uint)p_new_block + BlockHeader::sSize);
#endif // __EFFICIENT__
}
// This will make the passed memory block bigger.
// The original contents of the memory block will be unchanged.
// This function is most likely to work if used on the bottom-up heap.
// It will assert if it does not work.
void *Heap::reallocate_up( size_t newSize, void *pOld )
{
// What this function is going to do is allocate a new block with size equal to
// the difference between newSize and the size of pOld, then see if the new block
// is directly above the passed pOld.
// If it is, then it will merge the two blocks together.
// If it is not, it will assert.
Dbg_MsgAssert(pOld,("NULL pOld sent to reallocate_up !"));
BlockHeader* p_old_block = BlockHeader::sRead( pOld );
#ifdef __EFFICIENT__
newSize = (uint)nAlignUpBy( newSize, 2 ); // all allocations aligned by 4 bytes
#else
#ifdef __PLAT_NGC__
newSize = (uint)nAlignUpBy( newSize, 5 ); // all allocations aligned by 32 bytes
#else
newSize = (uint)nAlignUpBy( newSize, 4 ); // all allocations aligned by 16 bytes
#endif
#endif // __EFFICIENT__
if (p_old_block->mSize >= newSize)
{
// The current block is already big enough, so nothing to do.
return pOld;
}
// When bobbling together the new and old blocks, we will gain the space used by the old
// block header, so we don't really need to include that space in the new allocation request.
// But, if newSize is exactly a blockheader size bigger than the old, then that would cause
// allocate to be called on a size of zero.
// So allocate a blockheader size more than necessary to avoid this.
void *p_new=allocate(newSize-p_old_block->mSize,true);
Dbg_MsgAssert(p_new,("allocate failed!"));
// Got the new block, so now check that it is directly above the old.
BlockHeader* p_new_block=BlockHeader::sRead( p_new );
if ( (BlockHeader*)(((uint)pOld)+p_old_block->mSize) != p_new_block)
{
// It isn't!
free(p_new_block);
return NULL;
}
// Got this far, so the new block is right above the old.
// Now we have to remove the new block header so as to bobble the two blocks together.
#ifdef __LINKED_LIST_HEAP__
// remove both blocks from memory tracking, ready to add the new block
// that encompasses both of them
#ifdef __PLAT_NGPS__
#ifndef __PURE_HEAP__
MemView_Free((void*)p_old_block);
MemView_Free((void*)p_new_block);
#endif
#endif
// mp_next_used and mp_prev_used are a doble linked null ternimated list
// unlink next and prev
if ( p_new_block->mp_next_used )
{
p_new_block->mp_next_used->mp_prev_used = p_new_block->mp_prev_used;
}
if ( p_new_block->mp_prev_used )
{
p_new_block->mp_prev_used->mp_next_used = p_new_block->mp_next_used;
}
// unlink if it's the head of the list
if ( mp_context->mp_used_list == p_new_block )
{
mp_context->mp_used_list = p_new_block->mp_next_used;
}
p_new_block->mp_prev_used = NULL;
p_new_block->mp_next_used = NULL;
#endif
FreeMemProfile(p_old_block);
FreeMemProfile(p_new_block);
// (Mick) Since the alloc function might return a bigger block than was asked for
// we need to account for this in calculating the size of the new block
// we can get the size of the new block before we trash the header
int new_block_size = p_new_block->mSize;
p_new_block->~BlockHeader();
#ifdef __TRASH_BLOCKS__
Trash_FreeBlockHeader ( p_new_block );
#endif
// The memory occupied by the new block header has gone from being used as a BlockHeader,
// to being used as part of a used memory block, so mUsedMem does not need to change.
// The number of used blocks has changed though, since two are being merged into one.
--mUsedBlocks;
// Nothing has changed regarding free blocks, so mFreeBlocks and mFreeMem stay the same.
#ifdef __NOPT_ASSERT__
if (pOld == (void*)REPORT_ON)
{
printf ("%p: realloced up from %d to %d (actually %d) bytes\n", p_old_block, p_old_block->mSize, newSize, new_block_size + BlockHeader::sSize);
}
#endif
// That's p_new_block cleaned up, so now modify p_old_block's size.
// p_old_block->mSize=newSize+BlockHeader::sSize; // Old method, did not account for alloc returning oversized blocks
p_old_block->mSize += new_block_size + BlockHeader::sSize; // (Mick) New method, uses the actual size of the new block (plus blockheader)
#ifdef __PLAT_NGPS__
#ifndef __PURE_HEAP__
MemView_Alloc((void*)p_old_block);
#endif
#endif
AllocMemProfile(p_old_block);
return pOld;
}
void *Heap::reallocate_shrink( size_t newSize, void *pOld )
{
// This will shrink the passed block by creating a new free block out of the remaining
// space, if there is enough room to make a new free block. (at least 32 bytes needed)
// It creates the new free block by first creating a new used block, then freeing that.
// That way, all the merging of free blocks will be taken care of.
Dbg_MsgAssert(pOld,("NULL pOld sent to reallocate_up !"));
BlockHeader* p_old_block = BlockHeader::sRead( pOld );
Dbg_MsgAssert(p_old_block->mpAlloc == this,("Shrinking block in wrong context"));
//printf("Block size before shrinking = %d\n",p_old_block->mSize);
Dbg_MsgAssert(newSize<=p_old_block->mSize,("Larger size sent to reallocate_shrink:\nold size=%d requested new size=%d",p_old_block->mSize,newSize));
uint32 size_diff=p_old_block->mSize-newSize;
// We can only shrink down by a number of bytes that is divisible be 16 (4 on ngc), so
// that the resulting block size for the old block remains a multiple of 16 (4)
#ifdef __EFFICIENT__
size_diff&=~((1<<2)-1);
#else
#ifdef __PLAT_NGC__
size_diff&=~((1<<5)-1);
#else
size_diff&=~((1<<4)-1);
#endif
#endif // __EFFICIENT__
// If the amount the block is being shrunk by is not enough to hold a block header
// with a bit left over, then we can't make a new free block with it, so return without
// doing anything.
if (size_diff<=BlockHeader::sSize)
{
return pOld;
}
// Calculate a pointer to the new block.
BlockHeader *p_new_block=(BlockHeader*)((uint32)pOld+p_old_block->mSize-size_diff);
#ifdef __PLAT_NGPS__
Dbg_MsgAssert(((int)p_new_block&0xf) == 0,("p_new_block odd (%p), pOld = %p, p_old_block = %p, p_old_block->mSize = %d, size_diff = %d",
p_new_block, pOld, p_old_block->mSize, size_diff));
#endif
// Fill in new block header
p_new_block->mSize=size_diff-BlockHeader::sSize;
#ifdef __EFFICIENT__
p_new_block->mPadBytes = 0;
#endif // __EFFICIENT__
/////////////////////////////////////////////////////////////////////
// Do all the stuff one needs to do when creating a new used block.
// I just happily cut-and-pasted this lot from ::allocate
p_new_block->mpAlloc = this;
MemDbg_AllocateBlock ( p_new_block );
#ifdef __TRASH_BLOCKS__
Trash_AllocateBlock ( p_new_block );
#endif
#ifdef __LINKED_LIST_HEAP__
if( mp_context->mp_used_list )
{
p_new_block->mp_next_used = mp_context->mp_used_list;
mp_context->mp_used_list->mp_prev_used = p_new_block;
}
else
{
p_new_block->mp_next_used = NULL;
}
p_new_block->mp_prev_used = NULL;
#ifdef __PLAT_NGPS__
#ifndef __PURE_HEAP__
MemView_Alloc((void*)p_new_block);
#endif
#endif
mp_context->mp_used_list = p_new_block;
#endif
AllocMemProfile(p_new_block);
/////////////////////////////////////////////////////////////////////
// mUsedMem does not change, since we've made a new used block out of memory
// that was already being used.
// The number of used blocks has increased by one though.
++mUsedBlocks;
// Nothing has changed regarding mFreeBlocks or mFreeMem yet. That will all be handled
// when the block gets freed.
// Free the block just created.
free(p_new_block);
#ifdef __PLAT_NGPS__
#ifndef __PURE_HEAP__
MemView_Free((void*)p_old_block); // free at old size
#endif
#endif
FreeMemProfile(p_old_block);
// Update the size of the old block.
p_old_block->mSize-=size_diff;
#ifdef __PLAT_NGPS__
#ifndef __PURE_HEAP__
MemView_Alloc((void*)p_old_block); // re-register at new size
#endif
#endif
AllocMemProfile(p_old_block);
//printf("New block size after shrinking = %d\n",p_old_block->mSize);
return pOld;
}
/****************************************************************************
** Public Functions **
*****************************************************************************/
Heap::Heap( Region* region, Direction dir, char *p_name )
: Allocator( region, dir, p_name )
{
}
/******************************************************************/
/* */
/* */
/******************************************************************/
} // namespace Mem