Heap.h (31672B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 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. 23 24 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. 25 26 =========================================================================== 27 */ 28 29 #ifndef __HEAP_H__ 30 #define __HEAP_H__ 31 32 /* 33 =============================================================================== 34 35 Memory Management 36 37 =============================================================================== 38 */ 39 40 // memory tag names are used to sort allocations for sys_dumpMemory and other reporting functions 41 enum memTag_t { 42 #define MEM_TAG( x ) TAG_##x, 43 #include "sys/sys_alloc_tags.h" 44 TAG_NUM_TAGS, 45 }; 46 47 static const int MAX_TAGS = 256; 48 49 50 51 52 void * Mem_Alloc16( const int size, const memTag_t tag ); 53 void Mem_Free16( void *ptr ); 54 55 ID_INLINE void * Mem_Alloc( const int size, const memTag_t tag ) { return Mem_Alloc16( size, tag ); } 56 ID_INLINE void Mem_Free( void *ptr ) { Mem_Free16( ptr ); } 57 58 void * Mem_ClearedAlloc( const int size, const memTag_t tag ); 59 char * Mem_CopyString( const char *in ); 60 61 ID_INLINE void *operator new( size_t s ) { 62 return Mem_Alloc( s, TAG_NEW ); 63 } 64 ID_INLINE void operator delete( void *p ) { 65 Mem_Free( p ); 66 } 67 ID_INLINE void *operator new[]( size_t s ) { 68 return Mem_Alloc( s, TAG_NEW ); 69 } 70 ID_INLINE void operator delete[]( void *p ) { 71 Mem_Free( p ); 72 } 73 ID_INLINE void *operator new( size_t s, memTag_t tag ) { 74 return Mem_Alloc( s, tag ); 75 } 76 ID_INLINE void operator delete( void *p, memTag_t tag ) { 77 Mem_Free( p ); 78 } 79 ID_INLINE void *operator new[]( size_t s, memTag_t tag ) { 80 return Mem_Alloc( s, tag ); 81 } 82 ID_INLINE void operator delete[]( void *p, memTag_t tag ) { 83 Mem_Free( p ); 84 } 85 86 // Define replacements for the PS3 library's aligned new operator. 87 // Without these, allocations of objects with 32 byte or greater alignment 88 // may not go through our memory system. 89 90 /* 91 ================================================ 92 idTempArray is an array that is automatically free'd when it goes out of scope. 93 There is no "cast" operator because these are very unsafe. 94 95 The template parameter MUST BE POD! 96 97 Compile time asserting POD-ness of the template parameter is complicated due 98 to our vector classes that need a default constructor but are otherwise 99 considered POD. 100 ================================================ 101 */ 102 template < class T > 103 class idTempArray { 104 public: 105 idTempArray( idTempArray<T> & other ); 106 idTempArray( unsigned int num ); 107 108 ~idTempArray(); 109 110 T & operator []( unsigned int i ) { assert( i < num ); return buffer[i]; } 111 const T & operator []( unsigned int i ) const { assert( i < num ); return buffer[i]; } 112 113 T * Ptr() { return buffer; } 114 const T* Ptr() const { return buffer; } 115 116 size_t Size( ) const { return num * sizeof( T ); } 117 unsigned int Num( ) const { return num; } 118 119 void Zero() { memset( Ptr(), 0, Size() ); } 120 121 private: 122 T * buffer; // Ensure this buffer comes first, so this == &this->buffer 123 unsigned int num; 124 }; 125 126 /* 127 ======================== 128 idTempArray::idTempArray 129 ======================== 130 */ 131 template < class T > 132 ID_INLINE idTempArray<T>::idTempArray( idTempArray<T> & other ) { 133 this->num = other.num; 134 this->buffer = other.buffer; 135 other.num = 0; 136 other.buffer = NULL; 137 } 138 139 /* 140 ======================== 141 idTempArray::idTempArray 142 ======================== 143 */ 144 template < class T > 145 ID_INLINE idTempArray<T>::idTempArray( unsigned int num ) { 146 this->num = num; 147 buffer = (T*)Mem_Alloc( num * sizeof( T ), TAG_TEMP ); 148 } 149 150 /* 151 ======================== 152 idTempArray::~idTempArray 153 ======================== 154 */ 155 template < class T > 156 ID_INLINE idTempArray<T>::~idTempArray() { 157 Mem_Free( buffer ); 158 } 159 160 /* 161 =============================================================================== 162 163 Block based allocator for fixed size objects. 164 165 All objects of the 'type' are properly constructed and destructed when reused. 166 167 =============================================================================== 168 */ 169 170 #define BLOCK_ALLOC_ALIGNMENT 16 171 172 // Define this to force all block allocators to act like normal new/delete allocation 173 // for tool checking. 174 //#define FORCE_DISCRETE_BLOCK_ALLOCS 175 176 /* 177 ================================================ 178 idBlockAlloc is a block-based allocator for fixed-size objects. 179 180 All objects are properly constructed and destructed. 181 ================================================ 182 */ 183 template<class _type_, int _blockSize_, memTag_t memTag = TAG_BLOCKALLOC> 184 class idBlockAlloc { 185 public: 186 ID_INLINE idBlockAlloc( bool clear = false ); 187 ID_INLINE ~idBlockAlloc(); 188 189 // returns total size of allocated memory 190 size_t Allocated() const { return total * sizeof( _type_ ); } 191 192 // returns total size of allocated memory including size of (*this) 193 size_t Size() const { return sizeof( *this ) + Allocated(); } 194 195 ID_INLINE void Shutdown(); 196 ID_INLINE void SetFixedBlocks( int numBlocks ); 197 ID_INLINE void FreeEmptyBlocks(); 198 199 ID_INLINE _type_ * Alloc(); 200 ID_INLINE void Free( _type_ *element ); 201 202 int GetTotalCount() const { return total; } 203 int GetAllocCount() const { return active; } 204 int GetFreeCount() const { return total - active; } 205 206 private: 207 union element_t { 208 _type_ * data; // this is a hack to make sure the save game system marks _type_ as saveable 209 element_t * next; 210 byte buffer[( CONST_MAX( sizeof( _type_ ), sizeof( element_t * ) ) + ( BLOCK_ALLOC_ALIGNMENT - 1 ) ) & ~( BLOCK_ALLOC_ALIGNMENT - 1 )]; 211 }; 212 213 class idBlock { 214 public: 215 element_t elements[_blockSize_]; 216 idBlock * next; 217 element_t * free; // list with free elements in this block (temp used only by FreeEmptyBlocks) 218 int freeCount; // number of free elements in this block (temp used only by FreeEmptyBlocks) 219 }; 220 221 idBlock * blocks; 222 element_t * free; 223 int total; 224 int active; 225 bool allowAllocs; 226 bool clearAllocs; 227 228 ID_INLINE void AllocNewBlock(); 229 }; 230 231 /* 232 ======================== 233 idBlockAlloc<_type_,_blockSize_,align_t>::idBlockAlloc 234 ======================== 235 */ 236 template<class _type_, int _blockSize_, memTag_t memTag> 237 ID_INLINE idBlockAlloc<_type_,_blockSize_,memTag>::idBlockAlloc( bool clear ) : 238 blocks( NULL ), 239 free( NULL ), 240 total( 0 ), 241 active( 0 ), 242 allowAllocs( true ), 243 clearAllocs( clear ) 244 { 245 } 246 247 /* 248 ======================== 249 idBlockAlloc<_type_,_blockSize__,align_t>::~idBlockAlloc 250 ======================== 251 */ 252 template<class _type_, int _blockSize_, memTag_t memTag> 253 ID_INLINE idBlockAlloc<_type_,_blockSize_,memTag>::~idBlockAlloc() { 254 Shutdown(); 255 } 256 257 /* 258 ======================== 259 idBlockAlloc<_type_,_blockSize_,align_t>::Alloc 260 ======================== 261 */ 262 template<class _type_, int _blockSize_, memTag_t memTag> 263 ID_INLINE _type_ * idBlockAlloc<_type_,_blockSize_,memTag>::Alloc() { 264 #ifdef FORCE_DISCRETE_BLOCK_ALLOCS 265 // for debugging tools 266 return new _type_; 267 #else 268 if ( free == NULL ) { 269 if ( !allowAllocs ) { 270 return NULL; 271 } 272 AllocNewBlock(); 273 } 274 275 active++; 276 element_t * element = free; 277 free = free->next; 278 element->next = NULL; 279 280 _type_ * t = (_type_ *) element->buffer; 281 if ( clearAllocs ) { 282 memset( t, 0, sizeof( _type_ ) ); 283 } 284 new ( t ) _type_; 285 return t; 286 #endif 287 } 288 289 /* 290 ======================== 291 idBlockAlloc<_type_,_blockSize_,align_t>::Free 292 ======================== 293 */ 294 template<class _type_, int _blockSize_, memTag_t memTag> 295 ID_INLINE void idBlockAlloc<_type_,_blockSize_,memTag>::Free( _type_ * t ) { 296 #ifdef FORCE_DISCRETE_BLOCK_ALLOCS 297 // for debugging tools 298 delete t; 299 #else 300 if ( t == NULL ) { 301 return; 302 } 303 304 t->~_type_(); 305 306 element_t * element = (element_t *)( t ); 307 element->next = free; 308 free = element; 309 active--; 310 #endif 311 } 312 313 /* 314 ======================== 315 idBlockAlloc<_type_,_blockSize_,align_t>::Shutdown 316 ======================== 317 */ 318 template<class _type_, int _blockSize_, memTag_t memTag> 319 ID_INLINE void idBlockAlloc<_type_,_blockSize_,memTag>::Shutdown() { 320 while( blocks != NULL ) { 321 idBlock * block = blocks; 322 blocks = blocks->next; 323 Mem_Free( block ); 324 } 325 blocks = NULL; 326 free = NULL; 327 total = active = 0; 328 } 329 330 /* 331 ======================== 332 idBlockAlloc<_type_,_blockSize_,align_t>::SetFixedBlocks 333 ======================== 334 */ 335 template<class _type_, int _blockSize_, memTag_t memTag> 336 ID_INLINE void idBlockAlloc<_type_,_blockSize_,memTag>::SetFixedBlocks( int numBlocks ) { 337 int currentNumBlocks = 0; 338 for ( idBlock * block = blocks; block != NULL; block = block->next ) { 339 currentNumBlocks++; 340 } 341 for ( int i = currentNumBlocks; i < numBlocks; i++ ) { 342 AllocNewBlock(); 343 } 344 allowAllocs = false; 345 } 346 347 /* 348 ======================== 349 idBlockAlloc<_type_,_blockSize_,align_t>::AllocNewBlock 350 ======================== 351 */ 352 template<class _type_, int _blockSize_, memTag_t memTag> 353 ID_INLINE void idBlockAlloc<_type_,_blockSize_,memTag>::AllocNewBlock() { 354 idBlock * block = (idBlock *)Mem_Alloc( sizeof( idBlock ), memTag ); 355 block->next = blocks; 356 blocks = block; 357 for ( int i = 0; i < _blockSize_; i++ ) { 358 block->elements[i].next = free; 359 free = &block->elements[i]; 360 assert( ( ( (UINT_PTR)free ) & ( BLOCK_ALLOC_ALIGNMENT - 1 ) ) == 0 ); 361 } 362 total += _blockSize_; 363 } 364 365 /* 366 ======================== 367 idBlockAlloc<_type_,_blockSize_,align_t>::FreeEmptyBlocks 368 ======================== 369 */ 370 template<class _type_, int _blockSize_, memTag_t memTag> 371 ID_INLINE void idBlockAlloc<_type_,_blockSize_,memTag>::FreeEmptyBlocks() { 372 // first count how many free elements are in each block 373 // and build up a free chain per block 374 for ( idBlock * block = blocks; block != NULL; block = block->next ) { 375 block->free = NULL; 376 block->freeCount = 0; 377 } 378 for ( element_t * element = free; element != NULL; ) { 379 element_t * next = element->next; 380 for ( idBlock * block = blocks; block != NULL; block = block->next ) { 381 if ( element >= block->elements && element < block->elements + _blockSize_ ) { 382 element->next = block->free; 383 block->free = element; 384 block->freeCount++; 385 break; 386 } 387 } 388 // if this assert fires, we couldn't find the element in any block 389 assert( element->next != next ); 390 element = next; 391 } 392 // now free all blocks whose free count == _blockSize_ 393 idBlock * prevBlock = NULL; 394 for ( idBlock * block = blocks; block != NULL; ) { 395 idBlock * next = block->next; 396 if ( block->freeCount == _blockSize_ ) { 397 if ( prevBlock == NULL ) { 398 assert( blocks == block ); 399 blocks = block->next; 400 } else { 401 assert( prevBlock->next == block ); 402 prevBlock->next = block->next; 403 } 404 Mem_Free( block ); 405 total -= _blockSize_; 406 } else { 407 prevBlock = block; 408 } 409 block = next; 410 } 411 // now rebuild the free chain 412 free = NULL; 413 for ( idBlock * block = blocks; block != NULL; block = block->next ) { 414 for ( element_t * element = block->free; element != NULL; ) { 415 element_t * next = element->next; 416 element->next = free; 417 free = element; 418 element = next; 419 } 420 } 421 } 422 423 /* 424 ============================================================================== 425 426 Dynamic allocator, simple wrapper for normal allocations which can 427 be interchanged with idDynamicBlockAlloc. 428 429 No constructor is called for the 'type'. 430 Allocated blocks are always 16 byte aligned. 431 432 ============================================================================== 433 */ 434 435 template<class type, int baseBlockSize, int minBlockSize> 436 class idDynamicAlloc { 437 public: 438 idDynamicAlloc(); 439 ~idDynamicAlloc(); 440 441 void Init(); 442 void Shutdown(); 443 void SetFixedBlocks( int numBlocks ) {} 444 void SetLockMemory( bool lock ) {} 445 void FreeEmptyBaseBlocks() {} 446 447 type * Alloc( const int num ); 448 type * Resize( type *ptr, const int num ); 449 void Free( type *ptr ); 450 const char * CheckMemory( const type *ptr ) const; 451 452 int GetNumBaseBlocks() const { return 0; } 453 int GetBaseBlockMemory() const { return 0; } 454 int GetNumUsedBlocks() const { return numUsedBlocks; } 455 int GetUsedBlockMemory() const { return usedBlockMemory; } 456 int GetNumFreeBlocks() const { return 0; } 457 int GetFreeBlockMemory() const { return 0; } 458 int GetNumEmptyBaseBlocks() const { return 0; } 459 460 private: 461 int numUsedBlocks; // number of used blocks 462 int usedBlockMemory; // total memory in used blocks 463 464 int numAllocs; 465 int numResizes; 466 int numFrees; 467 468 void Clear(); 469 }; 470 471 template<class type, int baseBlockSize, int minBlockSize> 472 idDynamicAlloc<type, baseBlockSize, minBlockSize>::idDynamicAlloc() { 473 Clear(); 474 } 475 476 template<class type, int baseBlockSize, int minBlockSize> 477 idDynamicAlloc<type, baseBlockSize, minBlockSize>::~idDynamicAlloc() { 478 Shutdown(); 479 } 480 481 template<class type, int baseBlockSize, int minBlockSize> 482 void idDynamicAlloc<type, baseBlockSize, minBlockSize>::Init() { 483 } 484 485 template<class type, int baseBlockSize, int minBlockSize> 486 void idDynamicAlloc<type, baseBlockSize, minBlockSize>::Shutdown() { 487 Clear(); 488 } 489 490 template<class type, int baseBlockSize, int minBlockSize> 491 type *idDynamicAlloc<type, baseBlockSize, minBlockSize>::Alloc( const int num ) { 492 numAllocs++; 493 if ( num <= 0 ) { 494 return NULL; 495 } 496 numUsedBlocks++; 497 usedBlockMemory += num * sizeof( type ); 498 return Mem_Alloc16( num * sizeof( type ), TAG_BLOCKALLOC ); 499 } 500 501 template<class type, int baseBlockSize, int minBlockSize> 502 type *idDynamicAlloc<type, baseBlockSize, minBlockSize>::Resize( type *ptr, const int num ) { 503 504 numResizes++; 505 506 if ( ptr == NULL ) { 507 return Alloc( num ); 508 } 509 510 if ( num <= 0 ) { 511 Free( ptr ); 512 return NULL; 513 } 514 515 assert( 0 ); 516 return ptr; 517 } 518 519 template<class type, int baseBlockSize, int minBlockSize> 520 void idDynamicAlloc<type, baseBlockSize, minBlockSize>::Free( type *ptr ) { 521 numFrees++; 522 if ( ptr == NULL ) { 523 return; 524 } 525 Mem_Free16( ptr ); 526 } 527 528 template<class type, int baseBlockSize, int minBlockSize> 529 const char *idDynamicAlloc<type, baseBlockSize, minBlockSize>::CheckMemory( const type *ptr ) const { 530 return NULL; 531 } 532 533 template<class type, int baseBlockSize, int minBlockSize> 534 void idDynamicAlloc<type, baseBlockSize, minBlockSize>::Clear() { 535 numUsedBlocks = 0; 536 usedBlockMemory = 0; 537 numAllocs = 0; 538 numResizes = 0; 539 numFrees = 0; 540 } 541 542 543 /* 544 ============================================================================== 545 546 Fast dynamic block allocator. 547 548 No constructor is called for the 'type'. 549 Allocated blocks are always 16 byte aligned. 550 551 ============================================================================== 552 */ 553 554 #include "containers/BTree.h" 555 556 //#define DYNAMIC_BLOCK_ALLOC_CHECK 557 558 template<class type> 559 class idDynamicBlock { 560 public: 561 type * GetMemory() const { return (type *)( ( (byte *) this ) + sizeof( idDynamicBlock<type> ) ); } 562 int GetSize() const { return abs( size ); } 563 void SetSize( int s, bool isBaseBlock ) { size = isBaseBlock ? -s : s; } 564 bool IsBaseBlock() const { return ( size < 0 ); } 565 566 #ifdef DYNAMIC_BLOCK_ALLOC_CHECK 567 int id[3]; 568 void * allocator; 569 #endif 570 571 int size; // size in bytes of the block 572 idDynamicBlock<type> * prev; // previous memory block 573 idDynamicBlock<type> * next; // next memory block 574 idBTreeNode<idDynamicBlock<type>,int> *node; // node in the B-Tree with free blocks 575 }; 576 577 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_ = TAG_BLOCKALLOC> 578 class idDynamicBlockAlloc { 579 public: 580 idDynamicBlockAlloc(); 581 ~idDynamicBlockAlloc(); 582 583 void Init(); 584 void Shutdown(); 585 void SetFixedBlocks( int numBlocks ); 586 void SetLockMemory( bool lock ); 587 void FreeEmptyBaseBlocks(); 588 589 type * Alloc( const int num ); 590 type * Resize( type *ptr, const int num ); 591 void Free( type *ptr ); 592 const char * CheckMemory( const type *ptr ) const; 593 594 int GetNumBaseBlocks() const { return numBaseBlocks; } 595 int GetBaseBlockMemory() const { return baseBlockMemory; } 596 int GetNumUsedBlocks() const { return numUsedBlocks; } 597 int GetUsedBlockMemory() const { return usedBlockMemory; } 598 int GetNumFreeBlocks() const { return numFreeBlocks; } 599 int GetFreeBlockMemory() const { return freeBlockMemory; } 600 int GetNumEmptyBaseBlocks() const; 601 602 private: 603 idDynamicBlock<type> * firstBlock; // first block in list in order of increasing address 604 idDynamicBlock<type> * lastBlock; // last block in list in order of increasing address 605 idBTree<idDynamicBlock<type>,int,4>freeTree; // B-Tree with free memory blocks 606 bool allowAllocs; // allow base block allocations 607 bool lockMemory; // lock memory so it cannot get swapped out 608 609 #ifdef DYNAMIC_BLOCK_ALLOC_CHECK 610 int blockId[3]; 611 #endif 612 613 int numBaseBlocks; // number of base blocks 614 int baseBlockMemory; // total memory in base blocks 615 int numUsedBlocks; // number of used blocks 616 int usedBlockMemory; // total memory in used blocks 617 int numFreeBlocks; // number of free blocks 618 int freeBlockMemory; // total memory in free blocks 619 620 int numAllocs; 621 int numResizes; 622 int numFrees; 623 624 memTag_t tag; 625 626 void Clear(); 627 idDynamicBlock<type> * AllocInternal( const int num ); 628 idDynamicBlock<type> * ResizeInternal( idDynamicBlock<type> *block, const int num ); 629 void FreeInternal( idDynamicBlock<type> *block ); 630 void LinkFreeInternal( idDynamicBlock<type> *block ); 631 void UnlinkFreeInternal( idDynamicBlock<type> *block ); 632 void CheckMemory() const; 633 }; 634 635 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 636 idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::idDynamicBlockAlloc() { 637 tag = _tag_; 638 Clear(); 639 } 640 641 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 642 idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::~idDynamicBlockAlloc() { 643 Shutdown(); 644 } 645 646 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 647 void idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::Init() { 648 freeTree.Init(); 649 } 650 651 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 652 void idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::Shutdown() { 653 idDynamicBlock<type> *block; 654 655 for ( block = firstBlock; block != NULL; block = block->next ) { 656 if ( block->node == NULL ) { 657 FreeInternal( block ); 658 } 659 } 660 661 for ( block = firstBlock; block != NULL; block = firstBlock ) { 662 firstBlock = block->next; 663 assert( block->IsBaseBlock() ); 664 if ( lockMemory ) { 665 //idLib::sys->UnlockMemory( block, block->GetSize() + (int)sizeof( idDynamicBlock<type> ) ); 666 } 667 Mem_Free16( block ); 668 } 669 670 freeTree.Shutdown(); 671 672 Clear(); 673 } 674 675 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 676 void idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::SetFixedBlocks( int numBlocks ) { 677 idDynamicBlock<type> *block; 678 679 for ( int i = numBaseBlocks; i < numBlocks; i++ ) { 680 block = ( idDynamicBlock<type> * ) Mem_Alloc16( baseBlockSize, _tag_ ); 681 if ( lockMemory ) { 682 //idLib::sys->LockMemory( block, baseBlockSize ); 683 } 684 #ifdef DYNAMIC_BLOCK_ALLOC_CHECK 685 memcpy( block->id, blockId, sizeof( block->id ) ); 686 block->allocator = (void*)this; 687 #endif 688 block->SetSize( baseBlockSize - (int)sizeof( idDynamicBlock<type> ), true ); 689 block->next = NULL; 690 block->prev = lastBlock; 691 if ( lastBlock ) { 692 lastBlock->next = block; 693 } else { 694 firstBlock = block; 695 } 696 lastBlock = block; 697 block->node = NULL; 698 699 FreeInternal( block ); 700 701 numBaseBlocks++; 702 baseBlockMemory += baseBlockSize; 703 } 704 705 allowAllocs = false; 706 } 707 708 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 709 void idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::SetLockMemory( bool lock ) { 710 lockMemory = lock; 711 } 712 713 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 714 void idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::FreeEmptyBaseBlocks() { 715 idDynamicBlock<type> *block, *next; 716 717 for ( block = firstBlock; block != NULL; block = next ) { 718 next = block->next; 719 720 if ( block->IsBaseBlock() && block->node != NULL && ( next == NULL || next->IsBaseBlock() ) ) { 721 UnlinkFreeInternal( block ); 722 if ( block->prev ) { 723 block->prev->next = block->next; 724 } else { 725 firstBlock = block->next; 726 } 727 if ( block->next ) { 728 block->next->prev = block->prev; 729 } else { 730 lastBlock = block->prev; 731 } 732 if ( lockMemory ) { 733 //idLib::sys->UnlockMemory( block, block->GetSize() + (int)sizeof( idDynamicBlock<type> ) ); 734 } 735 numBaseBlocks--; 736 baseBlockMemory -= block->GetSize() + (int)sizeof( idDynamicBlock<type> ); 737 Mem_Free16( block ); 738 } 739 } 740 741 #ifdef DYNAMIC_BLOCK_ALLOC_CHECK 742 CheckMemory(); 743 #endif 744 } 745 746 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 747 int idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::GetNumEmptyBaseBlocks() const { 748 int numEmptyBaseBlocks; 749 idDynamicBlock<type> *block; 750 751 numEmptyBaseBlocks = 0; 752 for ( block = firstBlock; block != NULL; block = block->next ) { 753 if ( block->IsBaseBlock() && block->node != NULL && ( block->next == NULL || block->next->IsBaseBlock() ) ) { 754 numEmptyBaseBlocks++; 755 } 756 } 757 return numEmptyBaseBlocks; 758 } 759 760 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 761 type *idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::Alloc( const int num ) { 762 idDynamicBlock<type> *block; 763 764 numAllocs++; 765 766 if ( num <= 0 ) { 767 return NULL; 768 } 769 770 block = AllocInternal( num ); 771 if ( block == NULL ) { 772 return NULL; 773 } 774 block = ResizeInternal( block, num ); 775 if ( block == NULL ) { 776 return NULL; 777 } 778 779 #ifdef DYNAMIC_BLOCK_ALLOC_CHECK 780 CheckMemory(); 781 #endif 782 783 numUsedBlocks++; 784 usedBlockMemory += block->GetSize(); 785 786 return block->GetMemory(); 787 } 788 789 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 790 type *idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::Resize( type *ptr, const int num ) { 791 792 numResizes++; 793 794 if ( ptr == NULL ) { 795 return Alloc( num ); 796 } 797 798 if ( num <= 0 ) { 799 Free( ptr ); 800 return NULL; 801 } 802 803 idDynamicBlock<type> *block = ( idDynamicBlock<type> * ) ( ( (byte *) ptr ) - (int)sizeof( idDynamicBlock<type> ) ); 804 805 usedBlockMemory -= block->GetSize(); 806 807 block = ResizeInternal( block, num ); 808 if ( block == NULL ) { 809 return NULL; 810 } 811 812 #ifdef DYNAMIC_BLOCK_ALLOC_CHECK 813 CheckMemory(); 814 #endif 815 816 usedBlockMemory += block->GetSize(); 817 818 return block->GetMemory(); 819 } 820 821 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 822 void idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::Free( type *ptr ) { 823 824 numFrees++; 825 826 if ( ptr == NULL ) { 827 return; 828 } 829 830 idDynamicBlock<type> *block = ( idDynamicBlock<type> * ) ( ( (byte *) ptr ) - (int)sizeof( idDynamicBlock<type> ) ); 831 832 numUsedBlocks--; 833 usedBlockMemory -= block->GetSize(); 834 835 FreeInternal( block ); 836 837 #ifdef DYNAMIC_BLOCK_ALLOC_CHECK 838 CheckMemory(); 839 #endif 840 } 841 842 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 843 const char *idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::CheckMemory( const type *ptr ) const { 844 idDynamicBlock<type> *block; 845 846 if ( ptr == NULL ) { 847 return NULL; 848 } 849 850 block = ( idDynamicBlock<type> * ) ( ( (byte *) ptr ) - (int)sizeof( idDynamicBlock<type> ) ); 851 852 if ( block->node != NULL ) { 853 return "memory has been freed"; 854 } 855 856 #ifdef DYNAMIC_BLOCK_ALLOC_CHECK 857 if ( block->id[0] != 0x11111111 || block->id[1] != 0x22222222 || block->id[2] != 0x33333333 ) { 858 return "memory has invalid id"; 859 } 860 if ( block->allocator != (void*)this ) { 861 return "memory was allocated with different allocator"; 862 } 863 #endif 864 865 /* base blocks can be larger than baseBlockSize which can cause this code to fail 866 idDynamicBlock<type> *base; 867 for ( base = firstBlock; base != NULL; base = base->next ) { 868 if ( base->IsBaseBlock() ) { 869 if ( ((int)block) >= ((int)base) && ((int)block) < ((int)base) + baseBlockSize ) { 870 break; 871 } 872 } 873 } 874 if ( base == NULL ) { 875 return "no base block found for memory"; 876 } 877 */ 878 879 return NULL; 880 } 881 882 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 883 void idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::Clear() { 884 firstBlock = lastBlock = NULL; 885 allowAllocs = true; 886 lockMemory = false; 887 numBaseBlocks = 0; 888 baseBlockMemory = 0; 889 numUsedBlocks = 0; 890 usedBlockMemory = 0; 891 numFreeBlocks = 0; 892 freeBlockMemory = 0; 893 numAllocs = 0; 894 numResizes = 0; 895 numFrees = 0; 896 897 #ifdef DYNAMIC_BLOCK_ALLOC_CHECK 898 blockId[0] = 0x11111111; 899 blockId[1] = 0x22222222; 900 blockId[2] = 0x33333333; 901 #endif 902 } 903 904 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 905 idDynamicBlock<type> *idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::AllocInternal( const int num ) { 906 idDynamicBlock<type> *block; 907 int alignedBytes = ( num * sizeof( type ) + 15 ) & ~15; 908 909 block = freeTree.FindSmallestLargerEqual( alignedBytes ); 910 if ( block != NULL ) { 911 UnlinkFreeInternal( block ); 912 } else if ( allowAllocs ) { 913 int allocSize = Max( baseBlockSize, alignedBytes + (int)sizeof( idDynamicBlock<type> ) ); 914 block = ( idDynamicBlock<type> * ) Mem_Alloc16( allocSize, _tag_ ); 915 if ( lockMemory ) { 916 //idLib::sys->LockMemory( block, baseBlockSize ); 917 } 918 #ifdef DYNAMIC_BLOCK_ALLOC_CHECK 919 memcpy( block->id, blockId, sizeof( block->id ) ); 920 block->allocator = (void*)this; 921 #endif 922 block->SetSize( allocSize - (int)sizeof( idDynamicBlock<type> ), true ); 923 block->next = NULL; 924 block->prev = lastBlock; 925 if ( lastBlock ) { 926 lastBlock->next = block; 927 } else { 928 firstBlock = block; 929 } 930 lastBlock = block; 931 block->node = NULL; 932 933 numBaseBlocks++; 934 baseBlockMemory += allocSize; 935 } 936 937 return block; 938 } 939 940 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 941 idDynamicBlock<type> *idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::ResizeInternal( idDynamicBlock<type> *block, const int num ) { 942 int alignedBytes = ( num * sizeof( type ) + 15 ) & ~15; 943 944 #ifdef DYNAMIC_BLOCK_ALLOC_CHECK 945 assert( block->id[0] == 0x11111111 && block->id[1] == 0x22222222 && block->id[2] == 0x33333333 && block->allocator == (void*)this ); 946 #endif 947 948 // if the new size is larger 949 if ( alignedBytes > block->GetSize() ) { 950 951 idDynamicBlock<type> *nextBlock = block->next; 952 953 // try to annexate the next block if it's free 954 if ( nextBlock && !nextBlock->IsBaseBlock() && nextBlock->node != NULL && 955 block->GetSize() + (int)sizeof( idDynamicBlock<type> ) + nextBlock->GetSize() >= alignedBytes ) { 956 957 UnlinkFreeInternal( nextBlock ); 958 block->SetSize( block->GetSize() + (int)sizeof( idDynamicBlock<type> ) + nextBlock->GetSize(), block->IsBaseBlock() ); 959 block->next = nextBlock->next; 960 if ( nextBlock->next ) { 961 nextBlock->next->prev = block; 962 } else { 963 lastBlock = block; 964 } 965 } else { 966 // allocate a new block and copy 967 idDynamicBlock<type> *oldBlock = block; 968 block = AllocInternal( num ); 969 if ( block == NULL ) { 970 return NULL; 971 } 972 memcpy( block->GetMemory(), oldBlock->GetMemory(), oldBlock->GetSize() ); 973 FreeInternal( oldBlock ); 974 } 975 } 976 977 // if the unused space at the end of this block is large enough to hold a block with at least one element 978 if ( block->GetSize() - alignedBytes - (int)sizeof( idDynamicBlock<type> ) < Max( minBlockSize, (int)sizeof( type ) ) ) { 979 return block; 980 } 981 982 idDynamicBlock<type> *newBlock; 983 984 newBlock = ( idDynamicBlock<type> * ) ( ( (byte *) block ) + (int)sizeof( idDynamicBlock<type> ) + alignedBytes ); 985 #ifdef DYNAMIC_BLOCK_ALLOC_CHECK 986 memcpy( newBlock->id, blockId, sizeof( newBlock->id ) ); 987 newBlock->allocator = (void*)this; 988 #endif 989 newBlock->SetSize( block->GetSize() - alignedBytes - (int)sizeof( idDynamicBlock<type> ), false ); 990 newBlock->next = block->next; 991 newBlock->prev = block; 992 if ( newBlock->next ) { 993 newBlock->next->prev = newBlock; 994 } else { 995 lastBlock = newBlock; 996 } 997 newBlock->node = NULL; 998 block->next = newBlock; 999 block->SetSize( alignedBytes, block->IsBaseBlock() ); 1000 1001 FreeInternal( newBlock ); 1002 1003 return block; 1004 } 1005 1006 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 1007 void idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::FreeInternal( idDynamicBlock<type> *block ) { 1008 1009 assert( block->node == NULL ); 1010 1011 #ifdef DYNAMIC_BLOCK_ALLOC_CHECK 1012 assert( block->id[0] == 0x11111111 && block->id[1] == 0x22222222 && block->id[2] == 0x33333333 && block->allocator == (void*)this ); 1013 #endif 1014 1015 // try to merge with a next free block 1016 idDynamicBlock<type> *nextBlock = block->next; 1017 if ( nextBlock && !nextBlock->IsBaseBlock() && nextBlock->node != NULL ) { 1018 UnlinkFreeInternal( nextBlock ); 1019 block->SetSize( block->GetSize() + (int)sizeof( idDynamicBlock<type> ) + nextBlock->GetSize(), block->IsBaseBlock() ); 1020 block->next = nextBlock->next; 1021 if ( nextBlock->next ) { 1022 nextBlock->next->prev = block; 1023 } else { 1024 lastBlock = block; 1025 } 1026 } 1027 1028 // try to merge with a previous free block 1029 idDynamicBlock<type> *prevBlock = block->prev; 1030 if ( prevBlock && !block->IsBaseBlock() && prevBlock->node != NULL ) { 1031 UnlinkFreeInternal( prevBlock ); 1032 prevBlock->SetSize( prevBlock->GetSize() + (int)sizeof( idDynamicBlock<type> ) + block->GetSize(), prevBlock->IsBaseBlock() ); 1033 prevBlock->next = block->next; 1034 if ( block->next ) { 1035 block->next->prev = prevBlock; 1036 } else { 1037 lastBlock = prevBlock; 1038 } 1039 LinkFreeInternal( prevBlock ); 1040 } else { 1041 LinkFreeInternal( block ); 1042 } 1043 } 1044 1045 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 1046 ID_INLINE void idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::LinkFreeInternal( idDynamicBlock<type> *block ) { 1047 block->node = freeTree.Add( block, block->GetSize() ); 1048 numFreeBlocks++; 1049 freeBlockMemory += block->GetSize(); 1050 } 1051 1052 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 1053 ID_INLINE void idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::UnlinkFreeInternal( idDynamicBlock<type> *block ) { 1054 freeTree.Remove( block->node ); 1055 block->node = NULL; 1056 numFreeBlocks--; 1057 freeBlockMemory -= block->GetSize(); 1058 } 1059 1060 template<class type, int baseBlockSize, int minBlockSize, memTag_t _tag_> 1061 void idDynamicBlockAlloc<type, baseBlockSize, minBlockSize, _tag_>::CheckMemory() const { 1062 idDynamicBlock<type> *block; 1063 1064 for ( block = firstBlock; block != NULL; block = block->next ) { 1065 // make sure the block is properly linked 1066 if ( block->prev == NULL ) { 1067 assert( firstBlock == block ); 1068 } else { 1069 assert( block->prev->next == block ); 1070 } 1071 if ( block->next == NULL ) { 1072 assert( lastBlock == block ); 1073 } else { 1074 assert( block->next->prev == block ); 1075 } 1076 } 1077 } 1078 1079 #endif /* !__HEAP_H__ */