/* =========================================================================== 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; void * Mem_Alloc16( const int size, const memTag_t tag ); void Mem_Free16( void *ptr ); ID_INLINE void * Mem_Alloc( const int size, const memTag_t tag ) { return Mem_Alloc16( size, tag ); } ID_INLINE void Mem_Free( void *ptr ) { Mem_Free16( ptr ); } void * Mem_ClearedAlloc( const int size, const memTag_t tag ); char * Mem_CopyString( const char *in ); 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]; assert( ( ( (UINT_PTR)free ) & ( BLOCK_ALLOC_ALIGNMENT - 1 ) ) == 0 ); } 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__ */