/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see .
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef __HEAP_H__
#define __HEAP_H__
/*
===============================================================================
Memory Management
===============================================================================
*/
// memory tag names are used to sort allocations for sys_dumpMemory and other reporting functions
enum memTag_t
{
#define MEM_TAG( x ) TAG_##x,
#include "sys/sys_alloc_tags.h"
TAG_NUM_TAGS,
};
static const int MAX_TAGS = 256;
// RB: 64 bit fixes, changed int to size_t
void* Mem_Alloc16( const size_t size, const memTag_t tag );
void Mem_Free16( void* ptr );
ID_INLINE void* Mem_Alloc( const size_t size, const memTag_t tag )
{
return Mem_Alloc16( size, tag );
}
ID_INLINE void Mem_Free( void* ptr )
{
Mem_Free16( ptr );
}
void* Mem_ClearedAlloc( const size_t size, const memTag_t tag );
char* Mem_CopyString( const char* in );
// RB end
ID_INLINE void* operator new( size_t s )
{
return Mem_Alloc( s, TAG_NEW );
}
ID_INLINE void operator delete( void* p )
{
Mem_Free( p );
}
ID_INLINE void* operator new[]( size_t s )
{
return Mem_Alloc( s, TAG_NEW );
}
ID_INLINE void operator delete[]( void* p )
{
Mem_Free( p );
}
ID_INLINE void* operator new( size_t s, memTag_t tag )
{
return Mem_Alloc( s, tag );
}
ID_INLINE void operator delete( void* p, memTag_t tag )
{
Mem_Free( p );
}
ID_INLINE void* operator new[]( size_t s, memTag_t tag )
{
return Mem_Alloc( s, tag );
}
ID_INLINE void operator delete[]( void* p, memTag_t tag )
{
Mem_Free( p );
}
// Define replacements for the PS3 library's aligned new operator.
// Without these, allocations of objects with 32 byte or greater alignment
// may not go through our memory system.
/*
================================================
idTempArray is an array that is automatically free'd when it goes out of scope.
There is no "cast" operator because these are very unsafe.
The template parameter MUST BE POD!
Compile time asserting POD-ness of the template parameter is complicated due
to our vector classes that need a default constructor but are otherwise
considered POD.
================================================
*/
template < class T >
class idTempArray
{
public:
idTempArray( idTempArray& other );
idTempArray( unsigned int num );
~idTempArray();
T& operator []( unsigned int i )
{
assert( i < num );
return buffer[i];
}
const T& operator []( unsigned int i ) const
{
assert( i < num );
return buffer[i];
}
T* Ptr()
{
return buffer;
}
const T* Ptr() const
{
return buffer;
}
size_t Size( ) const
{
return num * sizeof( T );
}
unsigned int Num( ) const
{
return num;
}
void Zero()
{
memset( Ptr(), 0, Size() );
}
private:
T* buffer; // Ensure this buffer comes first, so this == &this->buffer
unsigned int num;
};
/*
========================
idTempArray::idTempArray
========================
*/
template < class T >
ID_INLINE idTempArray::idTempArray( idTempArray& other )
{
this->num = other.num;
this->buffer = other.buffer;
other.num = 0;
other.buffer = NULL;
}
/*
========================
idTempArray::idTempArray
========================
*/
template < class T >
ID_INLINE idTempArray::idTempArray( unsigned int num )
{
this->num = num;
buffer = ( T* )Mem_Alloc( num * sizeof( T ), TAG_TEMP );
}
/*
========================
idTempArray::~idTempArray
========================
*/
template < class T >
ID_INLINE idTempArray::~idTempArray()
{
Mem_Free( buffer );
}
/*
===============================================================================
Block based allocator for fixed size objects.
All objects of the 'type' are properly constructed and destructed when reused.
===============================================================================
*/
#define BLOCK_ALLOC_ALIGNMENT 16
// Define this to force all block allocators to act like normal new/delete allocation
// for tool checking.
//#define FORCE_DISCRETE_BLOCK_ALLOCS
/*
================================================
idBlockAlloc is a block-based allocator for fixed-size objects.
All objects are properly constructed and destructed.
================================================
*/
template
class idBlockAlloc
{
public:
ID_INLINE idBlockAlloc( bool clear = false );
ID_INLINE ~idBlockAlloc();
// returns total size of allocated memory
size_t Allocated() const
{
return total * sizeof( _type_ );
}
// returns total size of allocated memory including size of (*this)
size_t Size() const
{
return sizeof( *this ) + Allocated();
}
ID_INLINE void Shutdown();
ID_INLINE void SetFixedBlocks( int numBlocks );
ID_INLINE void FreeEmptyBlocks();
ID_INLINE _type_* Alloc();
ID_INLINE void Free( _type_ *element );
int GetTotalCount() const
{
return total;
}
int GetAllocCount() const
{
return active;
}
int GetFreeCount() const
{
return total - active;
}
private:
union element_t
{
_type_* data; // this is a hack to make sure the save game system marks _type_ as saveable
element_t* next;
byte buffer[( CONST_MAX( sizeof( _type_ ), sizeof( element_t* ) ) + ( BLOCK_ALLOC_ALIGNMENT - 1 ) ) & ~( BLOCK_ALLOC_ALIGNMENT - 1 )];
};
class idBlock
{
public:
element_t elements[_blockSize_];
idBlock* next;
element_t* free; // list with free elements in this block (temp used only by FreeEmptyBlocks)
int freeCount; // number of free elements in this block (temp used only by FreeEmptyBlocks)
};
idBlock* blocks;
element_t* free;
int total;
int active;
bool allowAllocs;
bool clearAllocs;
ID_INLINE void AllocNewBlock();
};
/*
========================
idBlockAlloc<_type_,_blockSize_,align_t>::idBlockAlloc
========================
*/
template
ID_INLINE idBlockAlloc<_type_, _blockSize_, memTag>::idBlockAlloc( bool clear ) :
blocks( NULL ),
free( NULL ),
total( 0 ),
active( 0 ),
allowAllocs( true ),
clearAllocs( clear )
{
}
/*
========================
idBlockAlloc<_type_,_blockSize__,align_t>::~idBlockAlloc
========================
*/
template
ID_INLINE idBlockAlloc<_type_, _blockSize_, memTag>::~idBlockAlloc()
{
Shutdown();
}
/*
========================
idBlockAlloc<_type_,_blockSize_,align_t>::Alloc
========================
*/
template
ID_INLINE _type_* idBlockAlloc<_type_, _blockSize_, memTag>::Alloc()
{
#ifdef FORCE_DISCRETE_BLOCK_ALLOCS
// for debugging tools
return new _type_;
#else
if( free == NULL )
{
if( !allowAllocs )
{
return NULL;
}
AllocNewBlock();
}
active++;
element_t* element = free;
free = free->next;
element->next = NULL;
_type_ * t = ( _type_* ) element->buffer;
if( clearAllocs )
{
memset( t, 0, sizeof( _type_ ) );
}
new( t ) _type_;
return t;
#endif
}
/*
========================
idBlockAlloc<_type_,_blockSize_,align_t>::Free
========================
*/
template
ID_INLINE void idBlockAlloc<_type_, _blockSize_, memTag>::Free( _type_ * t )
{
#ifdef FORCE_DISCRETE_BLOCK_ALLOCS
// for debugging tools
delete t;
#else
if( t == NULL )
{
return;
}
t->~_type_();
element_t* element = ( element_t* )( t );
element->next = free;
free = element;
active--;
#endif
}
/*
========================
idBlockAlloc<_type_,_blockSize_,align_t>::Shutdown
========================
*/
template
ID_INLINE void idBlockAlloc<_type_, _blockSize_, memTag>::Shutdown()
{
while( blocks != NULL )
{
idBlock* block = blocks;
blocks = blocks->next;
Mem_Free( block );
}
blocks = NULL;
free = NULL;
total = active = 0;
}
/*
========================
idBlockAlloc<_type_,_blockSize_,align_t>::SetFixedBlocks
========================
*/
template
ID_INLINE void idBlockAlloc<_type_, _blockSize_, memTag>::SetFixedBlocks( int numBlocks )
{
int currentNumBlocks = 0;
for( idBlock* block = blocks; block != NULL; block = block->next )
{
currentNumBlocks++;
}
for( int i = currentNumBlocks; i < numBlocks; i++ )
{
AllocNewBlock();
}
allowAllocs = false;
}
/*
========================
idBlockAlloc<_type_,_blockSize_,align_t>::AllocNewBlock
========================
*/
template
ID_INLINE void idBlockAlloc<_type_, _blockSize_, memTag>::AllocNewBlock()
{
idBlock* block = ( idBlock* )Mem_Alloc( sizeof( idBlock ), memTag );
block->next = blocks;
blocks = block;
for( int i = 0; i < _blockSize_; i++ )
{
block->elements[i].next = free;
free = &block->elements[i];
// RB: changed UINT_PTR to uintptr_t
assert( ( ( ( uintptr_t )free ) & ( BLOCK_ALLOC_ALIGNMENT - 1 ) ) == 0 );
// RB end
}
total += _blockSize_;
}
/*
========================
idBlockAlloc<_type_,_blockSize_,align_t>::FreeEmptyBlocks
========================
*/
template
ID_INLINE void idBlockAlloc<_type_, _blockSize_, memTag>::FreeEmptyBlocks()
{
// first count how many free elements are in each block
// and build up a free chain per block
for( idBlock* block = blocks; block != NULL; block = block->next )
{
block->free = NULL;
block->freeCount = 0;
}
for( element_t* element = free; element != NULL; )
{
element_t* next = element->next;
for( idBlock* block = blocks; block != NULL; block = block->next )
{
if( element >= block->elements && element < block->elements + _blockSize_ )
{
element->next = block->free;
block->free = element;
block->freeCount++;
break;
}
}
// if this assert fires, we couldn't find the element in any block
assert( element->next != next );
element = next;
}
// now free all blocks whose free count == _blockSize_
idBlock* prevBlock = NULL;
for( idBlock* block = blocks; block != NULL; )
{
idBlock* next = block->next;
if( block->freeCount == _blockSize_ )
{
if( prevBlock == NULL )
{
assert( blocks == block );
blocks = block->next;
}
else
{
assert( prevBlock->next == block );
prevBlock->next = block->next;
}
Mem_Free( block );
total -= _blockSize_;
}
else
{
prevBlock = block;
}
block = next;
}
// now rebuild the free chain
free = NULL;
for( idBlock* block = blocks; block != NULL; block = block->next )
{
for( element_t* element = block->free; element != NULL; )
{
element_t* next = element->next;
element->next = free;
free = element;
element = next;
}
}
}
/*
==============================================================================
Dynamic allocator, simple wrapper for normal allocations which can
be interchanged with idDynamicBlockAlloc.
No constructor is called for the 'type'.
Allocated blocks are always 16 byte aligned.
==============================================================================
*/
template
class idDynamicAlloc
{
public:
idDynamicAlloc();
~idDynamicAlloc();
void Init();
void Shutdown();
void SetFixedBlocks( int numBlocks ) {}
void SetLockMemory( bool lock ) {}
void FreeEmptyBaseBlocks() {}
type* Alloc( const int num );
type* Resize( type* ptr, const int num );
void Free( type* ptr );
const char* CheckMemory( const type* ptr ) const;
int GetNumBaseBlocks() const
{
return 0;
}
int GetBaseBlockMemory() const
{
return 0;
}
int GetNumUsedBlocks() const
{
return numUsedBlocks;
}
int GetUsedBlockMemory() const
{
return usedBlockMemory;
}
int GetNumFreeBlocks() const
{
return 0;
}
int GetFreeBlockMemory() const
{
return 0;
}
int GetNumEmptyBaseBlocks() const
{
return 0;
}
private:
int numUsedBlocks; // number of used blocks
int usedBlockMemory; // total memory in used blocks
int numAllocs;
int numResizes;
int numFrees;
void Clear();
};
template
idDynamicAlloc::idDynamicAlloc()
{
Clear();
}
template
idDynamicAlloc::~idDynamicAlloc()
{
Shutdown();
}
template
void idDynamicAlloc::Init()
{
}
template
void idDynamicAlloc::Shutdown()
{
Clear();
}
template
type* idDynamicAlloc::Alloc( const int num )
{
numAllocs++;
if( num <= 0 )
{
return NULL;
}
numUsedBlocks++;
usedBlockMemory += num * sizeof( type );
return Mem_Alloc16( num * sizeof( type ), TAG_BLOCKALLOC );
}
template
type* idDynamicAlloc::Resize( type* ptr, const int num )
{
numResizes++;
if( ptr == NULL )
{
return Alloc( num );
}
if( num <= 0 )
{
Free( ptr );
return NULL;
}
assert( 0 );
return ptr;
}
template
void idDynamicAlloc::Free( type* ptr )
{
numFrees++;
if( ptr == NULL )
{
return;
}
Mem_Free16( ptr );
}
template
const char* idDynamicAlloc::CheckMemory( const type* ptr ) const
{
return NULL;
}
template
void idDynamicAlloc::Clear()
{
numUsedBlocks = 0;
usedBlockMemory = 0;
numAllocs = 0;
numResizes = 0;
numFrees = 0;
}
/*
==============================================================================
Fast dynamic block allocator.
No constructor is called for the 'type'.
Allocated blocks are always 16 byte aligned.
==============================================================================
*/
#include "containers/BTree.h"
//#define DYNAMIC_BLOCK_ALLOC_CHECK
template
class idDynamicBlock
{
public:
type* GetMemory() const
{
return ( type* )( ( ( byte* ) this ) + sizeof( idDynamicBlock ) );
}
int GetSize() const
{
return abs( size );
}
void SetSize( int s, bool isBaseBlock )
{
size = isBaseBlock ? -s : s;
}
bool IsBaseBlock() const
{
return ( size < 0 );
}
#ifdef DYNAMIC_BLOCK_ALLOC_CHECK
int id[3];
void* allocator;
#endif
int size; // size in bytes of the block
idDynamicBlock* prev; // previous memory block
idDynamicBlock* next; // next memory block
idBTreeNode, int>* node; // node in the B-Tree with free blocks
};
template
class idDynamicBlockAlloc
{
public:
idDynamicBlockAlloc();
~idDynamicBlockAlloc();
void Init();
void Shutdown();
void SetFixedBlocks( int numBlocks );
void SetLockMemory( bool lock );
void FreeEmptyBaseBlocks();
type* Alloc( const int num );
type* Resize( type* ptr, const int num );
void Free( type* ptr );
const char* CheckMemory( const type* ptr ) const;
int GetNumBaseBlocks() const
{
return numBaseBlocks;
}
int GetBaseBlockMemory() const
{
return baseBlockMemory;
}
int GetNumUsedBlocks() const
{
return numUsedBlocks;
}
int GetUsedBlockMemory() const
{
return usedBlockMemory;
}
int GetNumFreeBlocks() const
{
return numFreeBlocks;
}
int GetFreeBlockMemory() const
{
return freeBlockMemory;
}
int GetNumEmptyBaseBlocks() const;
private:
idDynamicBlock* firstBlock; // first block in list in order of increasing address
idDynamicBlock* lastBlock; // last block in list in order of increasing address
idBTree, int, 4>freeTree; // B-Tree with free memory blocks
bool allowAllocs; // allow base block allocations
bool lockMemory; // lock memory so it cannot get swapped out
#ifdef DYNAMIC_BLOCK_ALLOC_CHECK
int blockId[3];
#endif
int numBaseBlocks; // number of base blocks
int baseBlockMemory; // total memory in base blocks
int numUsedBlocks; // number of used blocks
int usedBlockMemory; // total memory in used blocks
int numFreeBlocks; // number of free blocks
int freeBlockMemory; // total memory in free blocks
int numAllocs;
int numResizes;
int numFrees;
memTag_t tag;
void Clear();
idDynamicBlock* AllocInternal( const int num );
idDynamicBlock* ResizeInternal( idDynamicBlock* block, const int num );
void FreeInternal( idDynamicBlock* block );
void LinkFreeInternal( idDynamicBlock* block );
void UnlinkFreeInternal( idDynamicBlock* block );
void CheckMemory() const;
};
template
idDynamicBlockAlloc::idDynamicBlockAlloc()
{
tag = _tag_;
Clear();
}
template
idDynamicBlockAlloc::~idDynamicBlockAlloc()
{
Shutdown();
}
template
void idDynamicBlockAlloc::Init()
{
freeTree.Init();
}
template
void idDynamicBlockAlloc::Shutdown()
{
idDynamicBlock* block;
for( block = firstBlock; block != NULL; block = block->next )
{
if( block->node == NULL )
{
FreeInternal( block );
}
}
for( block = firstBlock; block != NULL; block = firstBlock )
{
firstBlock = block->next;
assert( block->IsBaseBlock() );
if( lockMemory )
{
//idLib::sys->UnlockMemory( block, block->GetSize() + (int)sizeof( idDynamicBlock ) );
}
Mem_Free16( block );
}
freeTree.Shutdown();
Clear();
}
template
void idDynamicBlockAlloc::SetFixedBlocks( int numBlocks )
{
idDynamicBlock* block;
for( int i = numBaseBlocks; i < numBlocks; i++ )
{
block = ( idDynamicBlock* ) Mem_Alloc16( baseBlockSize, _tag_ );
if( lockMemory )
{
//idLib::sys->LockMemory( block, baseBlockSize );
}
#ifdef DYNAMIC_BLOCK_ALLOC_CHECK
memcpy( block->id, blockId, sizeof( block->id ) );
block->allocator = ( void* )this;
#endif
block->SetSize( baseBlockSize - ( int )sizeof( idDynamicBlock ), true );
block->next = NULL;
block->prev = lastBlock;
if( lastBlock )
{
lastBlock->next = block;
}
else
{
firstBlock = block;
}
lastBlock = block;
block->node = NULL;
FreeInternal( block );
numBaseBlocks++;
baseBlockMemory += baseBlockSize;
}
allowAllocs = false;
}
template
void idDynamicBlockAlloc::SetLockMemory( bool lock )
{
lockMemory = lock;
}
template
void idDynamicBlockAlloc::FreeEmptyBaseBlocks()
{
idDynamicBlock* block, *next;
for( block = firstBlock; block != NULL; block = next )
{
next = block->next;
if( block->IsBaseBlock() && block->node != NULL && ( next == NULL || next->IsBaseBlock() ) )
{
UnlinkFreeInternal( block );
if( block->prev )
{
block->prev->next = block->next;
}
else
{
firstBlock = block->next;
}
if( block->next )
{
block->next->prev = block->prev;
}
else
{
lastBlock = block->prev;
}
if( lockMemory )
{
//idLib::sys->UnlockMemory( block, block->GetSize() + (int)sizeof( idDynamicBlock ) );
}
numBaseBlocks--;
baseBlockMemory -= block->GetSize() + ( int )sizeof( idDynamicBlock );
Mem_Free16( block );
}
}
#ifdef DYNAMIC_BLOCK_ALLOC_CHECK
CheckMemory();
#endif
}
template
int idDynamicBlockAlloc::GetNumEmptyBaseBlocks() const
{
int numEmptyBaseBlocks;
idDynamicBlock* block;
numEmptyBaseBlocks = 0;
for( block = firstBlock; block != NULL; block = block->next )
{
if( block->IsBaseBlock() && block->node != NULL && ( block->next == NULL || block->next->IsBaseBlock() ) )
{
numEmptyBaseBlocks++;
}
}
return numEmptyBaseBlocks;
}
template
type* idDynamicBlockAlloc::Alloc( const int num )
{
idDynamicBlock* block;
numAllocs++;
if( num <= 0 )
{
return NULL;
}
block = AllocInternal( num );
if( block == NULL )
{
return NULL;
}
block = ResizeInternal( block, num );
if( block == NULL )
{
return NULL;
}
#ifdef DYNAMIC_BLOCK_ALLOC_CHECK
CheckMemory();
#endif
numUsedBlocks++;
usedBlockMemory += block->GetSize();
return block->GetMemory();
}
template
type* idDynamicBlockAlloc::Resize( type* ptr, const int num )
{
numResizes++;
if( ptr == NULL )
{
return Alloc( num );
}
if( num <= 0 )
{
Free( ptr );
return NULL;
}
idDynamicBlock* block = ( idDynamicBlock* )( ( ( byte* ) ptr ) - ( int )sizeof( idDynamicBlock ) );
usedBlockMemory -= block->GetSize();
block = ResizeInternal( block, num );
if( block == NULL )
{
return NULL;
}
#ifdef DYNAMIC_BLOCK_ALLOC_CHECK
CheckMemory();
#endif
usedBlockMemory += block->GetSize();
return block->GetMemory();
}
template
void idDynamicBlockAlloc::Free( type* ptr )
{
numFrees++;
if( ptr == NULL )
{
return;
}
idDynamicBlock* block = ( idDynamicBlock* )( ( ( byte* ) ptr ) - ( int )sizeof( idDynamicBlock ) );
numUsedBlocks--;
usedBlockMemory -= block->GetSize();
FreeInternal( block );
#ifdef DYNAMIC_BLOCK_ALLOC_CHECK
CheckMemory();
#endif
}
template
const char* idDynamicBlockAlloc::CheckMemory( const type* ptr ) const
{
idDynamicBlock* block;
if( ptr == NULL )
{
return NULL;
}
block = ( idDynamicBlock* )( ( ( byte* ) ptr ) - ( int )sizeof( idDynamicBlock ) );
if( block->node != NULL )
{
return "memory has been freed";
}
#ifdef DYNAMIC_BLOCK_ALLOC_CHECK
if( block->id[0] != 0x11111111 || block->id[1] != 0x22222222 || block->id[2] != 0x33333333 )
{
return "memory has invalid id";
}
if( block->allocator != ( void* )this )
{
return "memory was allocated with different allocator";
}
#endif
/* base blocks can be larger than baseBlockSize which can cause this code to fail
idDynamicBlock *base;
for ( base = firstBlock; base != NULL; base = base->next ) {
if ( base->IsBaseBlock() ) {
if ( ((int)block) >= ((int)base) && ((int)block) < ((int)base) + baseBlockSize ) {
break;
}
}
}
if ( base == NULL ) {
return "no base block found for memory";
}
*/
return NULL;
}
template
void idDynamicBlockAlloc::Clear()
{
firstBlock = lastBlock = NULL;
allowAllocs = true;
lockMemory = false;
numBaseBlocks = 0;
baseBlockMemory = 0;
numUsedBlocks = 0;
usedBlockMemory = 0;
numFreeBlocks = 0;
freeBlockMemory = 0;
numAllocs = 0;
numResizes = 0;
numFrees = 0;
#ifdef DYNAMIC_BLOCK_ALLOC_CHECK
blockId[0] = 0x11111111;
blockId[1] = 0x22222222;
blockId[2] = 0x33333333;
#endif
}
template
idDynamicBlock* idDynamicBlockAlloc::AllocInternal( const int num )
{
idDynamicBlock* block;
int alignedBytes = ( num * sizeof( type ) + 15 ) & ~15;
block = freeTree.FindSmallestLargerEqual( alignedBytes );
if( block != NULL )
{
UnlinkFreeInternal( block );
}
else if( allowAllocs )
{
int allocSize = Max( baseBlockSize, alignedBytes + ( int )sizeof( idDynamicBlock ) );
block = ( idDynamicBlock* ) Mem_Alloc16( allocSize, _tag_ );
if( lockMemory )
{
//idLib::sys->LockMemory( block, baseBlockSize );
}
#ifdef DYNAMIC_BLOCK_ALLOC_CHECK
memcpy( block->id, blockId, sizeof( block->id ) );
block->allocator = ( void* )this;
#endif
block->SetSize( allocSize - ( int )sizeof( idDynamicBlock ), true );
block->next = NULL;
block->prev = lastBlock;
if( lastBlock )
{
lastBlock->next = block;
}
else
{
firstBlock = block;
}
lastBlock = block;
block->node = NULL;
numBaseBlocks++;
baseBlockMemory += allocSize;
}
return block;
}
template
idDynamicBlock* idDynamicBlockAlloc::ResizeInternal( idDynamicBlock* block, const int num )
{
int alignedBytes = ( num * sizeof( type ) + 15 ) & ~15;
#ifdef DYNAMIC_BLOCK_ALLOC_CHECK
assert( block->id[0] == 0x11111111 && block->id[1] == 0x22222222 && block->id[2] == 0x33333333 && block->allocator == ( void* )this );
#endif
// if the new size is larger
if( alignedBytes > block->GetSize() )
{
idDynamicBlock* nextBlock = block->next;
// try to annexate the next block if it's free
if( nextBlock && !nextBlock->IsBaseBlock() && nextBlock->node != NULL &&
block->GetSize() + ( int )sizeof( idDynamicBlock ) + nextBlock->GetSize() >= alignedBytes )
{
UnlinkFreeInternal( nextBlock );
block->SetSize( block->GetSize() + ( int )sizeof( idDynamicBlock ) + nextBlock->GetSize(), block->IsBaseBlock() );
block->next = nextBlock->next;
if( nextBlock->next )
{
nextBlock->next->prev = block;
}
else
{
lastBlock = block;
}
}
else
{
// allocate a new block and copy
idDynamicBlock* oldBlock = block;
block = AllocInternal( num );
if( block == NULL )
{
return NULL;
}
memcpy( block->GetMemory(), oldBlock->GetMemory(), oldBlock->GetSize() );
FreeInternal( oldBlock );
}
}
// if the unused space at the end of this block is large enough to hold a block with at least one element
if( block->GetSize() - alignedBytes - ( int )sizeof( idDynamicBlock ) < Max( minBlockSize, ( int )sizeof( type ) ) )
{
return block;
}
idDynamicBlock* newBlock;
newBlock = ( idDynamicBlock* )( ( ( byte* ) block ) + ( int )sizeof( idDynamicBlock ) + alignedBytes );
#ifdef DYNAMIC_BLOCK_ALLOC_CHECK
memcpy( newBlock->id, blockId, sizeof( newBlock->id ) );
newBlock->allocator = ( void* )this;
#endif
newBlock->SetSize( block->GetSize() - alignedBytes - ( int )sizeof( idDynamicBlock ), false );
newBlock->next = block->next;
newBlock->prev = block;
if( newBlock->next )
{
newBlock->next->prev = newBlock;
}
else
{
lastBlock = newBlock;
}
newBlock->node = NULL;
block->next = newBlock;
block->SetSize( alignedBytes, block->IsBaseBlock() );
FreeInternal( newBlock );
return block;
}
template
void idDynamicBlockAlloc::FreeInternal( idDynamicBlock* block )
{
assert( block->node == NULL );
#ifdef DYNAMIC_BLOCK_ALLOC_CHECK
assert( block->id[0] == 0x11111111 && block->id[1] == 0x22222222 && block->id[2] == 0x33333333 && block->allocator == ( void* )this );
#endif
// try to merge with a next free block
idDynamicBlock* nextBlock = block->next;
if( nextBlock && !nextBlock->IsBaseBlock() && nextBlock->node != NULL )
{
UnlinkFreeInternal( nextBlock );
block->SetSize( block->GetSize() + ( int )sizeof( idDynamicBlock ) + nextBlock->GetSize(), block->IsBaseBlock() );
block->next = nextBlock->next;
if( nextBlock->next )
{
nextBlock->next->prev = block;
}
else
{
lastBlock = block;
}
}
// try to merge with a previous free block
idDynamicBlock* prevBlock = block->prev;
if( prevBlock && !block->IsBaseBlock() && prevBlock->node != NULL )
{
UnlinkFreeInternal( prevBlock );
prevBlock->SetSize( prevBlock->GetSize() + ( int )sizeof( idDynamicBlock ) + block->GetSize(), prevBlock->IsBaseBlock() );
prevBlock->next = block->next;
if( block->next )
{
block->next->prev = prevBlock;
}
else
{
lastBlock = prevBlock;
}
LinkFreeInternal( prevBlock );
}
else
{
LinkFreeInternal( block );
}
}
template
ID_INLINE void idDynamicBlockAlloc::LinkFreeInternal( idDynamicBlock* block )
{
block->node = freeTree.Add( block, block->GetSize() );
numFreeBlocks++;
freeBlockMemory += block->GetSize();
}
template
ID_INLINE void idDynamicBlockAlloc::UnlinkFreeInternal( idDynamicBlock* block )
{
freeTree.Remove( block->node );
block->node = NULL;
numFreeBlocks--;
freeBlockMemory -= block->GetSize();
}
template
void idDynamicBlockAlloc::CheckMemory() const
{
idDynamicBlock* block;
for( block = firstBlock; block != NULL; block = block->next )
{
// make sure the block is properly linked
if( block->prev == NULL )
{
assert( firstBlock == block );
}
else
{
assert( block->prev->next == block );
}
if( block->next == NULL )
{
assert( lastBlock == block );
}
else
{
assert( block->next->prev == block );
}
}
}
#endif /* !__HEAP_H__ */