home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2006 March / Gamestar_82_2006-03_dvd.iso / DVDStar / Editace / quake4_sdkv10.exe / source / idlib / rvHeap.cpp < prev    next >
C/C++ Source or Header  |  2005-11-14  |  48KB  |  1,557 lines

  1. //
  2. // rvHeap.cpp - Heap object 
  3. // Date: 12/13/04
  4. // Created by: Dwight Luetscher
  5. //
  6.  
  7. #include "../idlib/precompiled.h"
  8. #pragma hdrstop
  9.  
  10. #ifdef _RV_MEM_SYS_SUPPORT
  11.  
  12. //#define DETECT_MEM_OVERWRITES
  13.  
  14. // Define some structures used by each heap
  15. struct rvMemoryBlock_s 
  16. {
  17.     rvMemoryBlock_s* m_prev;                                    // previous block in memory (next is implied by address of memory block plus distToNextBlock)
  18.     dword m_dwordDistToNextBlock : NUM_MEMORY_BLOCK_SIZE_BITS;    // distance, in double words (4 bytes), from the first byte of this rvMemoryBlock_s header to the first byte of the next rvMemoryBlock_s header
  19.     dword m_tag : NUM_TAG_BITS;                                    // bits used to tag the type of allocation and state of memory block (zero for free)
  20.  
  21.     // GetDistToNextBlock
  22.     //
  23.     // returns: the distance to the next block, in bytes
  24.     ID_INLINE dword GetDistToNextBlock() const
  25.     {
  26.         return m_dwordDistToNextBlock << 2;
  27.     }
  28.  
  29.     // SetDistToNextBlock
  30.     //
  31.     // sets the distance to the next block, in bytes
  32.     ID_INLINE void SetDistToNextBlock( dword distBytes )
  33.     {
  34.         assert( !(distBytes & 0x03) );
  35.         m_dwordDistToNextBlock = distBytes >> 2;
  36.     }
  37. };
  38.  
  39. struct rvFreeMemoryBlock_s : rvMemoryBlock_s 
  40. {
  41.     rvFreeMemoryBlock_s* m_prevFree;
  42.     rvFreeMemoryBlock_s* m_nextFree;
  43. };
  44.  
  45. static const dword rvHeapAlignmentPadding    = 0xFFFFFFFF;    // fixed value used for padding an allocation for alignment
  46.  
  47. #ifdef DETECT_MEM_OVERWRITES
  48. // a header and a trailer is stored at the front and rear of each allocation for 
  49. // the purposes of detecting if an allocation has been written past its bounds
  50. static const uint OVERWRITE_HEADER_SIZE            = 4;
  51. static const uint OVERWRITE_TRAILER_SIZE        = 4;
  52. static const char rvHeapAllocHeader[OVERWRITE_HEADER_SIZE]        = { '{', 'R', 'a', 'V' };
  53. static const char rvHeapAllocTrailer[OVERWRITE_TRAILER_SIZE]    = { 'e', 'N', '1', '}' };
  54. #else
  55. static const uint OVERWRITE_HEADER_SIZE            = 0;
  56. static const uint OVERWRITE_TRAILER_SIZE        = 0;
  57. #endif
  58.  
  59. #define SIZE_TO_SMALL_TABLE_OFFSET(sizeBytes)    ((((sizeBytes) + SMALL_BLOCK_ALIGN_SIZE_MASK) >> SMALL_BLOCK_ALIGN_SIZE_SHIFT) - 1)
  60. #define SMALL_TABLE_OFFSET_TO_SIZE(tableOffset)    (((tableOffset) + 1) << SMALL_BLOCK_ALIGN_SIZE_SHIFT)
  61.  
  62. // rvHeap
  63. //
  64. // constructor
  65. rvHeap::rvHeap( ) :
  66.     mDebugID(-1)
  67. {
  68.     // ResetValues();    do this in the Init() call instead (due to the fact that other constructors could call rvHeap::Init() before this constructor is called)
  69. }
  70.  
  71. // ~rvHeap
  72. //
  73. // destructor
  74. rvHeap::~rvHeap( )
  75. {
  76.     Shutdown();
  77. }
  78.  
  79. // Init
  80. // 
  81. // initializes this heap for use within the given arena, and with the given size limit that it can grow to 
  82. //
  83. // heapArena            heap arena that this heap is associated with
  84. // maxHeapSizeBytes        maximum number of bytes this heap can grow to
  85. // flags                flags describing the capabilities of this heap
  86. void rvHeap::Init( rvHeapArena &heapArena, uint maxHeapSizeBytes, uint flags )
  87. {
  88.     DWORD protection = PAGE_READWRITE;
  89.     DWORD allocType;
  90.     uint numPageTableBytes, numTablePages;
  91.     uint largeFreeRangeByteSize;
  92.     byte* pageTableData;
  93.  
  94.     ResetValues();
  95.  
  96.     // create the critical section used by this heap
  97.     InitializeCriticalSection( &m_criticalSection );
  98.  
  99.     // determine how many pages the virtual address range will consume
  100.     m_numPages = (maxHeapSizeBytes + PAGE_SIZE - 1) >> PAGE_SIZE_SHIFT;
  101.     m_maxHeapSizeBytes = m_numPages << PAGE_SIZE_SHIFT;
  102.     
  103.     m_heapRangeBytes = m_maxHeapSizeBytes + PAGE_SIZE;    // add a page at the beginning for zero-length allocations (not part of page table and is never committed)
  104.  
  105.     assert(sizeof(dword) == 4);    // assumed by following code
  106.     m_pageCommitStateArrayByteSize = ((m_numPages + 31) >> 5)*sizeof(dword);
  107.  
  108.     m_smallFreePageRangeByteSize = m_numPages*sizeof(freePageRange_t);
  109.     m_largeFreeRangeStorageSize = m_numPages/MAX_SMALL_PAGE_RANGE;
  110.     if ( m_largeFreeRangeStorageSize < 1 )
  111.     {
  112.         m_largeFreeRangeStorageSize = 1;
  113.     }
  114.     largeFreeRangeByteSize = m_largeFreeRangeStorageSize*sizeof(freeLargePageRange_t);
  115.  
  116.     numPageTableBytes = m_pageCommitStateArrayByteSize;    // allocate storage for the m_pageCommitStateTable array 
  117.     numPageTableBytes += m_smallFreePageRangeByteSize;    // allocate storage for the m_smallFreePageRangeTable array
  118.     numPageTableBytes += largeFreeRangeByteSize;        // allocate storage for the m_largeFreeRangeStorage array
  119.  
  120.     numTablePages = (numPageTableBytes + PAGE_SIZE - 1) >> PAGE_SIZE_SHIFT;
  121.     numPageTableBytes = numTablePages << PAGE_SIZE_SHIFT;
  122.  
  123.     // reserve the range of virtual memory needed by this heap
  124.     if ( (flags & rvHeapFlagWriteCombine) != 0 )
  125.     {
  126.         protection |= PAGE_WRITECOMBINE;
  127.     }
  128.     if ( (flags & rvHeapFlagNoCache) != 0 )
  129.     {
  130.         protection |= PAGE_NOCACHE;
  131.     }
  132.     allocType = MEM_RESERVE;
  133. #ifdef _XENON
  134.     if ( PAGE_SIZE_SHIFT == 16 )
  135.     {
  136.         allocType |= MEM_LARGE_PAGES;
  137.     }
  138.     allocType |= MEM_NOZERO;
  139. #endif
  140.     m_baseAddress = (byte *) VirtualAlloc( NULL, numPageTableBytes+m_heapRangeBytes, allocType, protection );
  141.     if ( NULL == m_baseAddress )
  142.     {
  143.         common->FatalError( "Unable to reserve desired size for heap.\n" );
  144.         return;
  145.     }
  146.     m_zeroLengthAllocPage = m_baseAddress + numPageTableBytes;
  147.     m_heapStorageStart = m_zeroLengthAllocPage + PAGE_SIZE;
  148.  
  149.     // commit the initial block of that range for use by the page table
  150.     // Note: We want this to zero the memory for the page table init!
  151.     allocType = MEM_COMMIT;
  152. #ifdef _XENON
  153.     if ( PAGE_SIZE_SHIFT == 16 )
  154.     {
  155.         allocType |= MEM_LARGE_PAGES;
  156.     }
  157. #endif
  158.     pageTableData = (byte *) VirtualAlloc( m_baseAddress, numPageTableBytes, allocType, PAGE_READWRITE );
  159.     if ( NULL == pageTableData )
  160.     {
  161.         common->FatalError( "Unable to commit pages needed for heap's page table.\n" );
  162.         VirtualFree( m_baseAddress, 0, MEM_RELEASE );
  163.         return;
  164.     }
  165.     m_committedSizeBytes = numPageTableBytes;
  166.  
  167.     m_pageCommitStateTable = (dword*) pageTableData;
  168.     pageTableData += m_pageCommitStateArrayByteSize;
  169.  
  170.     m_smallFreePageRangeTable = (freePageRange_t *) pageTableData;
  171.     pageTableData += m_smallFreePageRangeByteSize;
  172.  
  173.     m_largeFreeRangeStorage = (freeLargePageRange_t *) pageTableData;
  174.     BuildLargeFreeRangeReuseList();
  175.  
  176.     // set up the information members
  177.     m_flags = flags;
  178.  
  179.     heapArena.InitHeap( *this );
  180. }
  181.  
  182. // Shutdown
  183. //
  184. // releases this heap from use
  185. void rvHeap::Shutdown( )
  186. {
  187.     if ( m_arena != NULL ) 
  188.     {
  189.         VirtualFree( m_baseAddress, 0, MEM_RELEASE );
  190.         m_arena->ShutdownHeap( *this );
  191.  
  192.         DeleteCriticalSection( &m_criticalSection );
  193.     }
  194.     ResetValues();
  195. }
  196.  
  197. // ResetValues
  198. //
  199. // resets the data members to their pre-initialized state
  200. void rvHeap::ResetValues( )
  201. {
  202.     m_arena = NULL;
  203.     m_next = NULL;
  204.     memset( &m_criticalSection, 0, sizeof(m_criticalSection) );
  205.     m_baseAddress = NULL;
  206.     m_zeroLengthAllocPage = NULL;
  207.     m_heapStorageStart = NULL;
  208.     memset( m_smallFreeBlocks, 0, sizeof(m_smallFreeBlocks) );
  209.     m_largeFreeBlocks = NULL;
  210.     m_pageCommitStateTable = NULL;
  211.     m_smallFreePageRangeTable = NULL;
  212.     memset( m_smallFreePageRangeLists, 0, sizeof(m_smallFreePageRangeLists) );
  213.     m_largeFreePageRangeList = NULL;
  214.     m_largeFreeRangeStorage = NULL;
  215.     m_largeFreeRangeReuseList = NULL;
  216.     m_largeFreeRangeStorageSize = 0;
  217.     m_numPages = 0;
  218.     m_heapRangeBytes = 0;
  219.     m_committedSizeBytes = 0;
  220.     m_maxHeapSizeBytes = 0;
  221.     m_numBytesRequested = 0;
  222.     m_numBytesAllocated = 0;
  223.     memset(m_allocatedBytesByTag, 0, sizeof(m_allocatedBytesByTag));
  224.     memset(m_peekAllocatedBytesByTag, 0, sizeof(m_peekAllocatedBytesByTag));
  225.     memset(m_numAllocationsByTag, 0, sizeof(m_numAllocationsByTag));
  226.     m_numAllocations = 0;
  227.     m_flags = 0;
  228.     m_numZeroLengthAllocations = 0;
  229.     memset(m_name, 0, sizeof(m_name));
  230. }
  231.  
  232. // BuildLargeFreeRangeReuseList
  233. //
  234. // Builds the m_largeFreeRangeReuseList linked-list from the m_largeFreeRangeStorage[] array.
  235. void rvHeap::BuildLargeFreeRangeReuseList( )
  236. {
  237.     uint nOffset;
  238.  
  239.     m_largeFreeRangeReuseList = m_largeFreeRangeStorage + 1;
  240.     for ( nOffset = 1; nOffset < m_largeFreeRangeStorageSize-1; nOffset++ )
  241.     {
  242.         m_largeFreeRangeStorage[ nOffset ].m_nextFree = &m_largeFreeRangeStorage[ nOffset + 1 ];
  243.     }
  244.     m_largeFreeRangeStorage[ m_largeFreeRangeStorageSize-1 ].m_nextFree = NULL;
  245.  
  246.     m_largeFreePageRangeList = m_largeFreeRangeStorage;
  247.     m_largeFreePageRangeList->m_nextFree = NULL;
  248.     m_largeFreePageRangeList->m_firstPageOffset = 0;
  249.     m_largeFreePageRangeList->m_numContiguousPages = m_numPages;
  250. }
  251.  
  252. // AllocateMemory
  253. //
  254. // allocates memory from this heap.
  255. //
  256. // sizeBytes        Size, in bytes, of desired allocation.
  257. // allocationTag    Tag stored with this allocation (describing type of allocation)
  258. // align16Flag        true if allocation must be aligned on a 16-byte boundary, false otherwise.
  259. //
  260. // returns: a pointer to the allocated memory, NULL if enough memory does not exist for allocation
  261. byte *rvHeap::AllocateMemory( uint sizeBytes, int allocationTag, bool align16Flag )
  262. {
  263. //    align16Flag=true;
  264.     byte *allocation;
  265.     rvMemoryBlock_s *allocatedBlock;
  266.     uint actualSizeBytes, extraBytes, distToNextBlock;
  267.  
  268.     if ( allocationTag == MA_NONE || allocationTag >= MA_DO_NOT_USE )
  269.     {
  270.         allocationTag = MA_DEFAULT;
  271.     }
  272.  
  273.     EnterHeapCriticalSection( );
  274.  
  275.     // check for a zero-length allocation - returns a valid virtual address that will page fault should
  276.     // it be written to, or read from (uncommitted page)
  277.     if ( !sizeBytes )
  278.     {
  279.         // NOTE: The following is an attempt to provide an address within the range of this heap
  280.         //         that takes up no space (with the exception of a single virtual, uncommitted page).
  281.         //         The uniqueness of the address returned here is not guaranteed and is not aligned.
  282.         m_numZeroLengthAllocations++;
  283.         m_numAllocations++;
  284.         allocation = (byte *) (m_zeroLengthAllocPage + (m_numZeroLengthAllocations & PAGE_SIZE_MASK));
  285.  
  286.         ExitHeapCriticalSection( );
  287.         return allocation;
  288.     }
  289.  
  290.     // determine how big the allocation is really going to need to be
  291.     actualSizeBytes = sizeof(rvMemoryBlock_s) + sizeBytes + OVERWRITE_HEADER_SIZE + OVERWRITE_TRAILER_SIZE;
  292.     extraBytes = OVERWRITE_HEADER_SIZE + OVERWRITE_TRAILER_SIZE;
  293.     if ( align16Flag )
  294.     {
  295.         actualSizeBytes += 3*sizeof(rvHeapAlignmentPadding);    // for alignment
  296.         extraBytes += 3*sizeof(rvHeapAlignmentPadding);
  297.     }
  298.  
  299.     if ( actualSizeBytes > PAGE_SIZE )
  300.     {
  301.         // if greater than a page, and the tail just barely crosses a page boundary,
  302.         // ask for a size that is a multiple of free memory block structure (so there is room for such a structure at the end)
  303.         if ( (actualSizeBytes & PAGE_SIZE_MASK) < sizeof(rvFreeMemoryBlock_s) )
  304.         {
  305.             actualSizeBytes = (actualSizeBytes + (uint) sizeof(rvFreeMemoryBlock_s) - 1) & ~((uint) sizeof(rvFreeMemoryBlock_s) - 1);
  306.         }
  307.     }
  308.     else if ( actualSizeBytes < sizeof(rvFreeMemoryBlock_s) )
  309.     {
  310.         actualSizeBytes = sizeof(rvFreeMemoryBlock_s);
  311.  
  312.         // make sure that the size is 4-byte aligned
  313.         actualSizeBytes = ( actualSizeBytes + 3 ) & ~3;
  314.     }
  315.  
  316.     // make sure that the size is 4-byte aligned
  317.     actualSizeBytes = ( actualSizeBytes + 3 ) & ~3;
  318.  
  319.     if ( actualSizeBytes > MAX_SINGLE_ALLOCATION_SIZE )
  320.     {
  321.         // beyond what any heap can handle
  322.         ExitHeapCriticalSection( );
  323.         return NULL;
  324.     }
  325.  
  326.     if ( actualSizeBytes <= MAX_SMALL_BLOCK_SIZE )
  327.     {
  328.         // perform a small block allocation
  329.         allocatedBlock = AllocateMemorySmallBlock( actualSizeBytes );
  330.     }
  331.     else
  332.     {
  333.         // perform a large block allocation
  334.         allocatedBlock = AllocateMemoryLargeBlock( actualSizeBytes );
  335.     }
  336.  
  337.     if ( NULL == allocatedBlock ) 
  338.     {
  339.         ExitHeapCriticalSection( );
  340.         return NULL;
  341.     }
  342.  
  343.     distToNextBlock = allocatedBlock->GetDistToNextBlock();
  344.  
  345.     assert( allocationTag < MA_MAX );
  346.     allocatedBlock->m_tag = (dword) allocationTag;
  347.     m_allocatedBytesByTag[ allocationTag ] += distToNextBlock;
  348.     if ( m_allocatedBytesByTag[ allocationTag ] > m_peekAllocatedBytesByTag[ allocationTag ] )
  349.     {
  350.         m_peekAllocatedBytesByTag[ allocationTag ] = m_allocatedBytesByTag[ allocationTag ];
  351.     }
  352.     m_numAllocationsByTag[ allocationTag ]++;
  353.  
  354.     m_numBytesAllocated += distToNextBlock;
  355.     m_numBytesRequested += (distToNextBlock - sizeof(rvMemoryBlock_s) - extraBytes);
  356.     m_numAllocations++;
  357.  
  358.     allocation = (byte *) allocatedBlock + sizeof(rvMemoryBlock_s);
  359.  
  360. #ifdef DETECT_MEM_OVERWRITES
  361.     memcpy( allocation, rvHeapAllocHeader, OVERWRITE_HEADER_SIZE );
  362.     allocation += OVERWRITE_HEADER_SIZE;
  363.     byte *nextBlockAddress = (byte*) allocatedBlock + distToNextBlock;
  364.     nextBlockAddress -= OVERWRITE_TRAILER_SIZE;
  365.     memcpy( nextBlockAddress, rvHeapAllocTrailer, OVERWRITE_TRAILER_SIZE );
  366. #endif
  367.  
  368.     if ( align16Flag )
  369.     {
  370.         while ( ((ulong) allocation & 0x0F) != 0 )
  371.         {
  372.             *(dword*)allocation = rvHeapAlignmentPadding;
  373.             allocation += sizeof(rvHeapAlignmentPadding);
  374.         }
  375.     }
  376.  
  377.     ExitHeapCriticalSection( );
  378.     return allocation;
  379. }
  380.  
  381. // AllocateMemorySmallBlock
  382. //
  383. // perform a small block allocation - try to use an existing free block pointed to
  384. // by the table.
  385. //
  386. // returns: a pointer to the allocation header in front of the newly allocated block of memory,
  387. //            NULL if enough memory does not exist for allocation
  388. rvMemoryBlock_s *rvHeap::AllocateMemorySmallBlock( uint sizeBytes )
  389. {
  390.     uint smallBlockTableOffset, stopOffset;    //, largerBlockOffset;
  391.  
  392.     smallBlockTableOffset = SIZE_TO_SMALL_TABLE_OFFSET(sizeBytes);
  393.     assert(smallBlockTableOffset < NUM_SMALL_BLOCK_TABLE_ENTRIES);
  394.  
  395. #if 1
  396.     stopOffset = smallBlockTableOffset + NUM_SMALL_BLOCK_TABLE_ENTRIES;
  397. #else
  398.     stopOffset = smallBlockTableOffset << 1;
  399. #endif
  400.     if ( stopOffset > m_largestFreeSmallBlockOffset )
  401.     {
  402.         stopOffset = m_largestFreeSmallBlockOffset;
  403.     }
  404.  
  405.     while ( smallBlockTableOffset < stopOffset )
  406.     {
  407.         // check to see if there is a block that matches exactly
  408.         if ( m_smallFreeBlocks[smallBlockTableOffset] != NULL ) 
  409.         {
  410.             // there is a block that is just the right size, remove from free list and return it.
  411.             return AllocateBlockFromTable( smallBlockTableOffset, sizeBytes );
  412.         }
  413.  
  414. /*
  415.         NOTE: The following code is experimental and seems to result in greater fragmentation.
  416.  
  417.         // check to see if there is a block that matches at some multiple of the exact size
  418.         largerBlockOffset = smallBlockTableOffset << 1;
  419.         while ( largerBlockOffset <= m_largestFreeSmallBlockOffset ) 
  420.         {
  421.             if ( m_smallFreeBlocks[largerBlockOffset] != NULL ) 
  422.             {
  423.                 // found a larger block to allocate from
  424.                 return AllocateBlockFromTable( largerBlockOffset, sizeBytes );
  425.             }
  426.             largerBlockOffset = largerBlockOffset << 1;
  427.         }
  428. */
  429.  
  430.         smallBlockTableOffset++;
  431.     }
  432.  
  433.     // an existing small block could not be found, try a larger block
  434.     return AllocateMemoryLargeBlock( sizeBytes );
  435. }
  436.  
  437. // AllocateBlockFromTable
  438. //
  439. // Allocates the a small memory block from the free block table
  440. // and partitions it, if necessary, into an allocated chunk
  441. // and a free chunk (which remains in the m_smallFreeBlocks[] table).
  442. //
  443. // returns: a pointer to the allocation header in front of the newly allocated block of memory,
  444. //            NULL if enough memory does not exist for allocation
  445. rvMemoryBlock_s *rvHeap::AllocateBlockFromTable( uint smallBlockTableOffset, uint sizeBytes )
  446. {
  447.     rvFreeMemoryBlock_s* allocatedBlockHeader, *newFreeBlock;
  448.     uint originalBlockSize, newBlockSize, newBlockTableOffset;
  449.  
  450.     allocatedBlockHeader = m_smallFreeBlocks[smallBlockTableOffset];
  451.     assert( sizeBytes <= allocatedBlockHeader->GetDistToNextBlock() );
  452.  
  453.     originalBlockSize = allocatedBlockHeader->GetDistToNextBlock();
  454.     assert( originalBlockSize == SMALL_TABLE_OFFSET_TO_SIZE(smallBlockTableOffset) );
  455.  
  456.     // remove the free block from the table 
  457.     m_smallFreeBlocks[smallBlockTableOffset] = m_smallFreeBlocks[smallBlockTableOffset]->m_nextFree;
  458.     if ( m_smallFreeBlocks[smallBlockTableOffset] != NULL )
  459.     {
  460.         m_smallFreeBlocks[smallBlockTableOffset]->m_prevFree = NULL;
  461.     }
  462.  
  463.     // see if the requested size essentially covers the entire free block 
  464.     if ( sizeBytes + sizeof(rvFreeMemoryBlock_s) <= allocatedBlockHeader->GetDistToNextBlock() )
  465.     {
  466.         // this block is large enough to be split into two - an allocated chunk and a free chunk.
  467.         // re-add the now smaller free chunk back into smallFreeBlocks[] table
  468.         newBlockSize = allocatedBlockHeader->GetDistToNextBlock() - sizeBytes;
  469.  
  470.         newFreeBlock = (rvFreeMemoryBlock_s*) ((byte *) allocatedBlockHeader + sizeBytes);
  471.  
  472.         newFreeBlock->m_prev = allocatedBlockHeader;
  473.         newFreeBlock->SetDistToNextBlock( newBlockSize );
  474.         newFreeBlock->m_tag = MA_NONE;
  475.  
  476.         newBlockTableOffset = SIZE_TO_SMALL_TABLE_OFFSET(newBlockSize);
  477.         if ( m_smallFreeBlocks[newBlockTableOffset] != NULL )
  478.         {
  479.             m_smallFreeBlocks[newBlockTableOffset]->m_prevFree = newFreeBlock;
  480.         }
  481.  
  482.         newFreeBlock->m_prevFree = NULL;
  483.         newFreeBlock->m_nextFree = m_smallFreeBlocks[newBlockTableOffset];
  484.         m_smallFreeBlocks[newBlockTableOffset] = newFreeBlock;
  485.  
  486.         FixUpNextBlocksPrev( (byte *) newFreeBlock, newBlockSize );
  487.  
  488.         allocatedBlockHeader->SetDistToNextBlock( sizeBytes );
  489.     }
  490.  
  491.     allocatedBlockHeader->m_tag = MA_DEFAULT;
  492.  
  493.     // check to see if the largest free small block has moved down
  494.     TestLargestFreeSmallBlockOffset( smallBlockTableOffset );
  495.  
  496.     return allocatedBlockHeader;
  497. }
  498.  
  499. // TestLargestFreeSmallBlockOffset
  500. //
  501. // checks to see if the largest free small block has moved down
  502. void rvHeap::TestLargestFreeSmallBlockOffset( uint smallBlockTableOffset )
  503. {
  504.     if ( smallBlockTableOffset >= m_largestFreeSmallBlockOffset ) {
  505.  
  506.         // find the next largest available small block (if table entry is NULL)
  507.         uint nextAvailableBlock = smallBlockTableOffset;
  508.         while ( nextAvailableBlock > 0 && NULL == m_smallFreeBlocks[nextAvailableBlock] )
  509.         {
  510.             nextAvailableBlock--;
  511.         }
  512.         m_largestFreeSmallBlockOffset = nextAvailableBlock;
  513.     }
  514. }
  515.  
  516. // AllocateMemoryLargeBlock
  517. //
  518. // perform a large block allocation - try to use an existing free block from within
  519. // the large free block linked-list.
  520. //
  521. // returns: a pointer to the allocation header in front of the newly allocated block of memory,
  522. //            NULL if enough memory does not exist for allocation
  523. rvMemoryBlock_s *rvHeap::AllocateMemoryLargeBlock( uint sizeBytes )
  524. {
  525.     rvFreeMemoryBlock_s **prevNextBlock, *curFreeBlock, *freeBlock;
  526.     rvMemoryBlock_s *block = NULL;
  527.     byte *newPageStart;
  528.     uint numPages, totalAllocated = 0, remainingSize;
  529.  
  530.     prevNextBlock = &m_largeFreeBlocks;
  531.     curFreeBlock = m_largeFreeBlocks;
  532.     while ( curFreeBlock != NULL ) 
  533.     {
  534.         if ( sizeBytes <= curFreeBlock->GetDistToNextBlock() )
  535.         {
  536.             // we found a block that is big enough, remove it from the large free block list
  537.             *prevNextBlock = curFreeBlock->m_nextFree;
  538.             if ( curFreeBlock->m_nextFree != NULL )
  539.             {
  540.                 curFreeBlock->m_nextFree->m_prevFree = curFreeBlock->m_prevFree;
  541.             }
  542.             totalAllocated = curFreeBlock->GetDistToNextBlock();
  543.             block = (rvMemoryBlock_s *) curFreeBlock;
  544.             break;
  545.         }
  546.  
  547.         prevNextBlock = &curFreeBlock->m_nextFree;
  548.         curFreeBlock = curFreeBlock->m_nextFree;
  549.     }
  550.  
  551.     if ( NULL == block )
  552.     {
  553.         // an existing block could not be found, commit a new page range for use 
  554.         numPages = (sizeBytes + PAGE_SIZE - 1) >> PAGE_SIZE_SHIFT;
  555.         newPageStart = (byte *) PageCommit( numPages );
  556.         if ( NULL == newPageStart )
  557.         {
  558.             // out of memory error
  559.             return NULL;
  560.         }
  561.  
  562.         // break the new page up into an allocated chunk (returned) and a free chunk (added to m_smallFreeBlocks[] or m_largeFreeBlocks)
  563.         block = (rvMemoryBlock_s *) newPageStart;
  564.         block->m_prev = NULL;
  565.  
  566.         totalAllocated = numPages << PAGE_SIZE_SHIFT;
  567.     }
  568.  
  569.     block->m_tag = MA_DEFAULT;
  570.  
  571.     remainingSize = totalAllocated - sizeBytes;
  572.     if ( remainingSize < sizeof(rvFreeMemoryBlock_s) )
  573.     {
  574.         // it is not worth creating a free block for what remains of last page
  575.         block->SetDistToNextBlock( totalAllocated );
  576.     }
  577.     else 
  578.     {
  579.         // there is a enough space left at the end of the last page to generate
  580.         // a free block
  581.         block->SetDistToNextBlock( sizeBytes );
  582.         freeBlock = (rvFreeMemoryBlock_s *) ((byte *) block + sizeBytes);
  583.         assert( !((uint) freeBlock & 0x03) );    
  584.  
  585.         if ( ((uint) block >> PAGE_SIZE_SHIFT) == (((uint) block + sizeBytes) >> PAGE_SIZE_SHIFT) )
  586.         {
  587.             // new free block is on the same page
  588.             freeBlock->m_prev = block;
  589.         }
  590.         else
  591.         {
  592.             // new free block is on a different page
  593.             freeBlock->m_prev = NULL;
  594.         }
  595.         freeBlock->SetDistToNextBlock( remainingSize );
  596.         freeBlock->m_tag = MA_NONE;
  597.         
  598.         FixUpNextBlocksPrev( (byte *) freeBlock, remainingSize );
  599.  
  600.         AddFreeBlock( freeBlock );
  601.     }
  602.     return block;
  603. }
  604.  
  605. // FixUpNextBlocksPrev
  606. //
  607. // Fixes up the previous pointer of the memory block that lies immediately
  608. // past the given newBlock, if it remains on the same page.
  609. void rvHeap::FixUpNextBlocksPrev( byte *newBlock, uint blockSize )
  610. {
  611.     rvMemoryBlock_s* nextBlockPastNew;
  612.  
  613.     if ( ((uint) newBlock >> PAGE_SIZE_SHIFT) == ((uint) (newBlock + blockSize) >> PAGE_SIZE_SHIFT) )
  614.     {
  615.         // the next block remains on the same page, the previous pointer must be fixed up
  616.         nextBlockPastNew = (rvMemoryBlock_s*) (newBlock + blockSize);
  617.         nextBlockPastNew->m_prev = (rvMemoryBlock_s*) newBlock;
  618.     }
  619. }
  620.  
  621. // AddFreeBlock
  622. // 
  623. // Adds the given free block to the small allocation table or large allocation list.
  624. void rvHeap::AddFreeBlock( rvFreeMemoryBlock_s *freeBlock )
  625. {
  626.     uint smallBlockTableOffset;
  627.  
  628.     if ( freeBlock->GetDistToNextBlock() <= MAX_SMALL_BLOCK_SIZE )
  629.     {
  630.         // add the block to the small free block table
  631.         smallBlockTableOffset = SIZE_TO_SMALL_TABLE_OFFSET(freeBlock->GetDistToNextBlock());
  632.         assert(smallBlockTableOffset < NUM_SMALL_BLOCK_TABLE_ENTRIES);
  633.  
  634.         if ( m_smallFreeBlocks[smallBlockTableOffset] != NULL ) 
  635.         {
  636.             m_smallFreeBlocks[smallBlockTableOffset]->m_prevFree = freeBlock;
  637.         }
  638.         else if ( smallBlockTableOffset > m_largestFreeSmallBlockOffset )
  639.         {
  640.             m_largestFreeSmallBlockOffset = smallBlockTableOffset;
  641.         }
  642.  
  643.         freeBlock->m_prevFree = NULL;
  644.         freeBlock->m_nextFree = m_smallFreeBlocks[smallBlockTableOffset];
  645.         m_smallFreeBlocks[smallBlockTableOffset] = freeBlock;
  646.     }
  647.     else 
  648.     {
  649.         if ( m_largeFreeBlocks != NULL )
  650.         {
  651.             m_largeFreeBlocks->m_prevFree = freeBlock;
  652.         }
  653.         freeBlock->m_prevFree = NULL;
  654.         freeBlock->m_nextFree = m_largeFreeBlocks;
  655.         m_largeFreeBlocks = freeBlock;
  656.     }
  657. }
  658.  
  659. // RemoveFreeBlock
  660. //
  661. // Removes the given block from the small allocation table or large allocation list.
  662. void rvHeap::RemoveFreeBlock( rvFreeMemoryBlock_s *freeBlock )
  663. {
  664.     uint smallBlockTableOffset;
  665.  
  666.     if ( freeBlock->GetDistToNextBlock() <= MAX_SMALL_BLOCK_SIZE )
  667.     {
  668.         if ( NULL == freeBlock->m_prevFree )
  669.         {
  670.             smallBlockTableOffset = SIZE_TO_SMALL_TABLE_OFFSET(freeBlock->GetDistToNextBlock());
  671.  
  672.             assert( smallBlockTableOffset < NUM_SMALL_BLOCK_TABLE_ENTRIES );
  673.             assert( m_smallFreeBlocks[smallBlockTableOffset] == freeBlock );
  674.         
  675.             m_smallFreeBlocks[smallBlockTableOffset] = freeBlock->m_nextFree;
  676.  
  677.             // check to see if the largest free small block has moved down
  678.             TestLargestFreeSmallBlockOffset( smallBlockTableOffset );
  679.         }
  680.         else
  681.         {
  682.             freeBlock->m_prevFree->m_nextFree = freeBlock->m_nextFree;
  683.         }
  684.     }
  685.     else 
  686.     {
  687.         if ( NULL == freeBlock->m_prevFree )
  688.         {
  689.             assert( m_largeFreeBlocks == freeBlock );
  690.             
  691.             m_largeFreeBlocks = freeBlock->m_nextFree;
  692.         }
  693.         else 
  694.         {
  695.             freeBlock->m_prevFree->m_nextFree = freeBlock->m_nextFree;
  696.         }
  697.     }
  698.  
  699.     if ( freeBlock->m_nextFree != NULL )
  700.     {
  701.         freeBlock->m_nextFree->m_prevFree = freeBlock->m_prevFree;
  702.     }
  703. }
  704.  
  705. // Free
  706. //
  707. // free memory 
  708. void rvHeap::Free( void *p )
  709. {
  710.     rvMemoryBlock_s *block, *prevBlock, *nextBlock;    
  711.     rvFreeMemoryBlock_s *freeBlock;
  712.     byte *nextBlockAddress, *allocation;
  713.     byte *pageAddress;
  714.     uint numPages, remainingSize, extraBytes, allocationTag, distToNextBlock;
  715.  
  716.     EnterHeapCriticalSection( );
  717.  
  718.     assert( m_numAllocations > 0 );
  719.     assert( p != NULL );
  720.     assert( DoesAllocBelong( p ) );
  721.  
  722.     m_numAllocations--;
  723.  
  724.     if ( p < m_zeroLengthAllocPage+PAGE_SIZE )
  725.     {
  726.         // this is a zero-length allocation
  727.         assert(m_numZeroLengthAllocations>0);
  728.         m_numZeroLengthAllocations--;
  729. #ifdef _DETECT_BLOCK_MERGE_PROBLEMS
  730.         TestFreeBlockValidity();
  731. #endif
  732.         ExitHeapCriticalSection( );
  733.         return;
  734.     }
  735.  
  736.     assert( !((uint) p & 0x03) );
  737.  
  738.     // back up over any heap alignment padding
  739.     allocation = (byte *) p;
  740.     extraBytes = 0;
  741.     if ( *((dword*) allocation - 1) == rvHeapAlignmentPadding )
  742.     {
  743.         allocation -= sizeof(rvHeapAlignmentPadding);
  744.         if ( *((dword*) allocation - 1) == rvHeapAlignmentPadding )
  745.         {
  746.             allocation -= sizeof(rvHeapAlignmentPadding);
  747.             if ( *((dword*) allocation - 1) == rvHeapAlignmentPadding )
  748.             {
  749.                 allocation -= sizeof(rvHeapAlignmentPadding);
  750.             }
  751.         }
  752.         extraBytes += 3*sizeof(rvHeapAlignmentPadding);
  753.     }
  754.  
  755.     // make sure that the overwrite header and trailer are intact
  756. #ifdef DETECT_MEM_OVERWRITES
  757.     allocation -= OVERWRITE_HEADER_SIZE;
  758.     extraBytes += OVERWRITE_HEADER_SIZE + OVERWRITE_TRAILER_SIZE;
  759.     if ( memcmp( allocation, rvHeapAllocHeader, OVERWRITE_HEADER_SIZE ) )
  760.     {
  761.         idLib::common->FatalError( "rvHeap::Free: memory block header overwrite (0x%x)", (dword) allocation );
  762.     }
  763. #endif
  764.  
  765.     // restore the memory block pointer
  766.     block = (rvMemoryBlock_s *) (allocation - sizeof(rvMemoryBlock_s));
  767.  
  768. #ifdef DETECT_MEM_OVERWRITES
  769.     nextBlockAddress = (byte*) block + block->GetDistToNextBlock();
  770.     nextBlockAddress -= OVERWRITE_TRAILER_SIZE;
  771.     if ( memcmp( nextBlockAddress, rvHeapAllocTrailer, OVERWRITE_TRAILER_SIZE ) )
  772.     {
  773.         idLib::common->FatalError( "rvHeap::Free: memory block trailer overwrite (0x%x)", (dword) nextBlockAddress );
  774.     }
  775. #endif
  776.  
  777.     pageAddress = (byte *) (((dword) allocation) & ~PAGE_SIZE_MASK);
  778.  
  779.     if ( ( block->m_prev != NULL && (byte *) block->m_prev < pageAddress ) ) 
  780.     {
  781.         idLib::common->FatalError( "rvHeap::Free: memory block header corrupted (0x%x)", (dword) allocation );
  782.     }
  783.  
  784.     if ( block->m_tag == MA_NONE )
  785.     {
  786.         idLib::common->FatalError( "rvHeap::Free: attempt to free allocation that is already freed (0x%x)", (dword) allocation );
  787.     }
  788.  
  789.     assert( (byte*) block >= m_heapStorageStart && ((byte*) block + block->GetDistToNextBlock()) <= (m_heapStorageStart + m_maxHeapSizeBytes) );
  790.  
  791.     distToNextBlock = block->GetDistToNextBlock();
  792.     assert( distToNextBlock <= m_numBytesAllocated );
  793.     m_numBytesAllocated -= distToNextBlock;
  794.     m_numBytesRequested -= (distToNextBlock - sizeof(rvMemoryBlock_s) - extraBytes);
  795.  
  796.     allocationTag = block->m_tag;
  797.     assert( distToNextBlock <= m_allocatedBytesByTag[ allocationTag ] && m_numAllocationsByTag[ allocationTag ] > 0 );
  798.     m_allocatedBytesByTag[ allocationTag ] -= distToNextBlock;
  799.     m_numAllocationsByTag[ allocationTag ]--;
  800.  
  801.     if ( block->GetDistToNextBlock() > PAGE_SIZE )
  802.     {
  803.         // this allocation that is being freed spans multiple pages - uncommit the ones 
  804.         // before the last one
  805.         numPages = block->GetDistToNextBlock() >> PAGE_SIZE_SHIFT;
  806.  
  807.         remainingSize = block->GetDistToNextBlock() - (numPages << PAGE_SIZE_SHIFT);
  808.  
  809.         PageUncommit( pageAddress, numPages );
  810.  
  811.         if ( !remainingSize )
  812.         {
  813.             // there are no remaining blocks
  814. #ifdef _DETECT_BLOCK_MERGE_PROBLEMS
  815.             TestFreeBlockValidity();
  816. #endif
  817.             ExitHeapCriticalSection( );
  818.             return;
  819.         }
  820.  
  821.         pageAddress += (numPages << PAGE_SIZE_SHIFT);
  822.  
  823.         block = (rvMemoryBlock_s *) pageAddress;
  824.         block->m_prev = NULL;
  825.         block->SetDistToNextBlock( remainingSize );
  826.         block->m_tag = MA_NONE;
  827.  
  828.         FixUpNextBlocksPrev( (byte *) block, remainingSize );
  829.     }
  830.  
  831.     nextBlockAddress = (byte*) block + block->GetDistToNextBlock();
  832.  
  833.     // mark the block as free
  834.     block->m_tag = MA_NONE;
  835.  
  836.     // determine if the block we are freeing should be merged with the previous block
  837.     prevBlock = block->m_prev;
  838.     if ( prevBlock != NULL )
  839.     {
  840.         if ( prevBlock->m_tag == MA_NONE )
  841.         {
  842.             // the previous block is free, merge the block we are freeing to its previous neighbor
  843.             RemoveFreeBlock( (rvFreeMemoryBlock_s *) prevBlock );
  844.             prevBlock->SetDistToNextBlock( prevBlock->GetDistToNextBlock() + block->GetDistToNextBlock() );
  845.             block = prevBlock;
  846.  
  847.             FixUpNextBlocksPrev( (byte *) prevBlock, prevBlock->GetDistToNextBlock() );
  848.         }
  849.     }
  850.  
  851.     // determine if the block we are freeing should be merged with the next block
  852.     if ( nextBlockAddress < pageAddress+PAGE_SIZE )
  853.     {
  854.         nextBlock = (rvMemoryBlock_s *) nextBlockAddress;
  855.         if ( nextBlock->m_tag == MA_NONE )
  856.         {
  857.             // the next block is free, merge the block we are freeing to its next neighbor
  858.             RemoveFreeBlock( (rvFreeMemoryBlock_s *) nextBlock );
  859.  
  860.             block->SetDistToNextBlock( block->GetDistToNextBlock() + nextBlock->GetDistToNextBlock() );
  861.  
  862.             FixUpNextBlocksPrev( (byte *) block, block->GetDistToNextBlock() );
  863.         }
  864.     }
  865.  
  866.     // determine if block being freed should be added to small allocation table, large 
  867.     // allocation list, or if the page itself should be uncommitted
  868.     freeBlock = (rvFreeMemoryBlock_s *) block;
  869.     if ( freeBlock->GetDistToNextBlock() < PAGE_SIZE )
  870.     {
  871.         AddFreeBlock( freeBlock );
  872.     }
  873.     else 
  874.     {
  875.         // this free block has reached the size of an entire page, uncommit it
  876.         PageUncommit( pageAddress, 1 );
  877.     }
  878.  
  879. #ifdef _DETECT_BLOCK_MERGE_PROBLEMS
  880.     TestFreeBlockValidity();
  881. #endif
  882.  
  883.     ExitHeapCriticalSection( );
  884. }
  885.  
  886. // Msize
  887. //
  888. // returns: the size, in bytes, of the allocation at the given address (including header, alignment bytes, etc).
  889. int rvHeap::Msize( void *p )
  890. {
  891.     rvMemoryBlock_s *block;    
  892.     byte *allocation;
  893.     dword size;
  894.  
  895.     EnterHeapCriticalSection( );
  896.  
  897.     assert( m_numAllocations > 0 );
  898.     assert( p != NULL );
  899.     assert( DoesAllocBelong( p ) );
  900.  
  901.     if ( p < m_zeroLengthAllocPage+PAGE_SIZE )
  902.     {
  903.         // this is a zero-length allocation
  904.         ExitHeapCriticalSection( );
  905.         return 0;
  906.     }
  907.  
  908.     assert( !((uint) p & 0x03) );
  909.  
  910.     // back up over any heap alignment padding
  911.     allocation = (byte *) p;
  912.     if ( *((dword*) allocation - 1) == rvHeapAlignmentPadding )
  913.     {
  914.         allocation -= sizeof(rvHeapAlignmentPadding);
  915.         if ( *((dword*) allocation - 1) == rvHeapAlignmentPadding )
  916.         {
  917.             allocation -= sizeof(rvHeapAlignmentPadding);
  918.             if ( *((dword*) allocation - 1) == rvHeapAlignmentPadding )
  919.             {
  920.                 allocation -= sizeof(rvHeapAlignmentPadding);
  921.             }
  922.         }
  923.     }
  924.  
  925.     // make sure that the overwrite header and trailer are intact
  926. #ifdef DETECT_MEM_OVERWRITES
  927.     allocation -= OVERWRITE_HEADER_SIZE;
  928.     if ( memcmp( allocation, rvHeapAllocHeader, OVERWRITE_HEADER_SIZE ) )
  929.     {
  930.         idLib::common->FatalError( "rvHeap::Free: memory block header overwrite (0x%x)", (dword) allocation );
  931.     }
  932. #endif
  933.  
  934.     // restore the memory block pointer
  935.     block = (rvMemoryBlock_s *) (allocation - sizeof(rvMemoryBlock_s));
  936.  
  937.     size = block->GetDistToNextBlock();
  938.     size -= ((dword)p-(dword)block) + OVERWRITE_TRAILER_SIZE;
  939.  
  940.     ExitHeapCriticalSection( );
  941.  
  942.     return size;
  943. }
  944.  
  945. // FreeAll
  946. //
  947. // frees all the allocations from this heap (and decommits all pages)
  948. void rvHeap::FreeAll( )
  949. {
  950.     if ( NULL == m_baseAddress )
  951.     {
  952.         return;
  953.     }
  954.  
  955.     EnterHeapCriticalSection( );
  956.  
  957.     VirtualFree( m_heapStorageStart, m_maxHeapSizeBytes, MEM_DECOMMIT );    // this decommits all of the physical pages (frees physical memory), but leaves the virtual address range reserved to this heap
  958.  
  959.     memset( m_smallFreeBlocks, 0, sizeof(rvFreeMemoryBlock_s*)*NUM_SMALL_BLOCK_TABLE_ENTRIES );
  960.     m_largeFreeBlocks = NULL;
  961.  
  962.     m_committedSizeBytes = 0;
  963.     m_numBytesAllocated = 0;
  964.     m_numBytesRequested = 0;
  965.  
  966.     m_numAllocations = 0;
  967.     m_numZeroLengthAllocations = 0;
  968.  
  969.     m_largestFreeSmallBlockOffset = 0;
  970.  
  971.     memset( m_pageCommitStateTable, 0, m_pageCommitStateArrayByteSize );
  972.     memset( m_smallFreePageRangeTable, 0, m_smallFreePageRangeByteSize );
  973.     BuildLargeFreeRangeReuseList( );
  974.  
  975.     ExitHeapCriticalSection( );
  976. }
  977.  
  978. // PageCommit
  979. //
  980. // commits the given number of contiguous pages to physical memory 
  981. // (actually allocates the physical pages).
  982. //
  983. // returns: pointer to the first virtual address of the first committed page, 
  984. //            NULL if commit failed.
  985. void *rvHeap::PageCommit( uint numDesiredPages )
  986. {
  987.     freePageRange_t *freePageRange;
  988.     freeLargePageRange_t *freeLargePageRange, **prevNextLargePageRange;
  989.     uint listOffset, tableOffset, numRemainingPages, remainingPagesOffset, remainingListOffset;
  990.  
  991.     if ( numDesiredPages <= MAX_SMALL_PAGE_RANGE )
  992.     {
  993.         // the desired range of contiguous pages is considered "small" and the retrieval
  994.         // of such a range is accelerated by the m_smallFreePageRangeLists[] table
  995.         listOffset = numDesiredPages - 1;
  996.         do
  997.         {
  998.             if ( m_smallFreePageRangeLists[listOffset] != NULL )
  999.             {
  1000.                 // we have found a contiguous range of pages that will hold the desired amount
  1001.                 freePageRange = m_smallFreePageRangeLists[listOffset];    
  1002.                 m_smallFreePageRangeLists[listOffset] =  m_smallFreePageRangeLists[listOffset]->m_nextFree;
  1003.  
  1004.                 // determine the starting table offset of the page range (starting page number)
  1005.                 tableOffset = freePageRange - m_smallFreePageRangeTable;
  1006.  
  1007.                 // re-add any remainder back into the m_smallFreePageRangeTable[]
  1008.                 numRemainingPages = listOffset + 1 - numDesiredPages;
  1009.                 if ( numRemainingPages > 0 )
  1010.                 {
  1011.                     remainingListOffset = numRemainingPages - 1;
  1012.                     remainingPagesOffset = tableOffset + numDesiredPages;
  1013.                     m_smallFreePageRangeTable[ remainingPagesOffset ].m_nextFree = m_smallFreePageRangeLists[ remainingListOffset  ];
  1014.                     m_smallFreePageRangeLists[ remainingListOffset  ] = &m_smallFreePageRangeTable[ remainingPagesOffset ];
  1015.                 }
  1016.  
  1017.                 // actually commit the pages to physical memory and mark the pages as committed within the m_pageCommitStateTable[]
  1018.                 return CommitPageRange( tableOffset, numDesiredPages );
  1019.             }
  1020.  
  1021.             listOffset++;
  1022.         }
  1023.         while ( listOffset < MAX_SMALL_PAGE_RANGE );
  1024.     }
  1025.  
  1026.     // try find a suitable contiguous range of pages within the m_largeFreePageRangeList linked list
  1027.     freeLargePageRange = m_largeFreePageRangeList;
  1028.     prevNextLargePageRange = &m_largeFreePageRangeList;
  1029.     while ( freeLargePageRange != NULL )
  1030.     {
  1031.         if ( freeLargePageRange->m_numContiguousPages >= numDesiredPages )
  1032.         {
  1033.             // found a block that is big enough - split it if necessary
  1034.             tableOffset = freeLargePageRange->m_firstPageOffset;
  1035.             numRemainingPages = freeLargePageRange->m_numContiguousPages - numDesiredPages;
  1036.             remainingPagesOffset = tableOffset + numDesiredPages;
  1037.  
  1038.             if ( numRemainingPages <= MAX_SMALL_PAGE_RANGE )
  1039.             {
  1040.                 // the freeLargePageRange_t structure is no longer needed in the m_largeFreePageRangeList list, remove it
  1041.                 *prevNextLargePageRange = freeLargePageRange->m_nextFree;
  1042.                 freeLargePageRange->m_nextFree = m_largeFreeRangeReuseList;
  1043.                 m_largeFreeRangeReuseList = freeLargePageRange;
  1044.  
  1045.                 if ( numRemainingPages > 0 )
  1046.                 {
  1047.                     // add the remainder into the m_smallFreePageRangeTable[]
  1048.                     remainingListOffset = numRemainingPages - 1;
  1049.                     m_smallFreePageRangeTable[ remainingPagesOffset ].m_nextFree = m_smallFreePageRangeLists[ remainingListOffset  ];
  1050.                     m_smallFreePageRangeLists[ remainingListOffset  ] = &m_smallFreePageRangeTable[ remainingPagesOffset ];
  1051.                 }
  1052.             }
  1053.             else 
  1054.             {
  1055.                 // add the remainder back into the m_largeFreePageRangeList linked-list (NOTE: structure is still part of list)
  1056.                 freeLargePageRange->m_firstPageOffset = remainingPagesOffset;
  1057.                 freeLargePageRange->m_numContiguousPages = numRemainingPages;
  1058.             }
  1059.  
  1060.             return CommitPageRange( tableOffset, numDesiredPages );
  1061.         }
  1062.  
  1063.         prevNextLargePageRange = &freeLargePageRange->m_nextFree;
  1064.         freeLargePageRange = freeLargePageRange->m_nextFree;
  1065.     }
  1066.  
  1067.     return NULL;
  1068. }
  1069.  
  1070. // PageUncommit
  1071. //
  1072. // uncommits the given range of contiguous pages back to physical memory 
  1073. // (actually frees the physical pages).
  1074. void rvHeap::PageUncommit( byte *pageAddress, uint numPages )
  1075. {
  1076.     freeLargePageRange_t *freeLargePageRange = NULL;
  1077.     uint startPageOffset, prevPageOffset, nextPageOffset, listOffset;
  1078.     uint curDWordOffset, freeBlockPageCount, largeRangeCount, orgStartPageOffset, orgNumPages;
  1079.  
  1080.     pageAddress = (byte *) (((dword) pageAddress) & ~PAGE_SIZE_MASK);
  1081.  
  1082.     startPageOffset = (uint) (pageAddress - m_heapStorageStart) >> PAGE_SIZE_SHIFT;
  1083.  
  1084.     orgStartPageOffset = startPageOffset;
  1085.     orgNumPages = numPages;
  1086.  
  1087.     // determine if there is a free block of pages immediately previous and/or immediately following
  1088.     // this newly freed block of pages - merge into one big block if we can.
  1089.     if ( startPageOffset > 0 )
  1090.     {
  1091.         prevPageOffset = startPageOffset - 1;
  1092.         curDWordOffset = prevPageOffset >> 5;
  1093.         if ( !(m_pageCommitStateTable[ curDWordOffset ] & (1 << (prevPageOffset & 0x1F))) )
  1094.         {
  1095.             // the previous block is free, so now we need to determine what linked-list it
  1096.             // is on (how big is the continuous block of pages)
  1097.             freeBlockPageCount = 1;
  1098.             while ( prevPageOffset > 0 && freeBlockPageCount <= MAX_SMALL_PAGE_RANGE )
  1099.             {
  1100.                 curDWordOffset = (prevPageOffset - 1) >> 5;
  1101.                 if ( (m_pageCommitStateTable[ curDWordOffset ] & (1 << ((prevPageOffset - 1) & 0x1F))) != 0 )
  1102.                 {
  1103.                     break;
  1104.                 }
  1105.                 prevPageOffset--;
  1106.                 freeBlockPageCount++;
  1107.             }
  1108.  
  1109.             if ( freeBlockPageCount <= MAX_SMALL_PAGE_RANGE )
  1110.             {
  1111.                 RemoveFromSmallFreeRangeList( prevPageOffset, freeBlockPageCount );
  1112.                 startPageOffset = prevPageOffset;
  1113.                 numPages += freeBlockPageCount;
  1114.             }
  1115.             else
  1116.             {
  1117.                 RemoveFromLargeFreeRangeList( prevPageOffset, startPageOffset, largeRangeCount );
  1118.                 numPages += largeRangeCount;
  1119.             }
  1120.         }
  1121.     }
  1122.  
  1123.     if ( startPageOffset + numPages < m_numPages )
  1124.     {
  1125.         nextPageOffset = startPageOffset + numPages;
  1126.         curDWordOffset = nextPageOffset >> 5;
  1127.         if ( !(m_pageCommitStateTable[ curDWordOffset ] & (1 << (nextPageOffset & 0x1F))) )
  1128.         {
  1129.             // the next block is free, so now we need to determine what linked-list it
  1130.             // is on (how big is the continuous block of pages)
  1131.             freeBlockPageCount = 1;
  1132.             while ( nextPageOffset < m_numPages && freeBlockPageCount <= MAX_SMALL_PAGE_RANGE )
  1133.             {
  1134.                 nextPageOffset++;
  1135.                 curDWordOffset = nextPageOffset >> 5;
  1136.                 if ( (m_pageCommitStateTable[ curDWordOffset ] & (1 << (nextPageOffset & 0x1F))) != 0 )
  1137.                 {
  1138.                     break;
  1139.                 }
  1140.                 freeBlockPageCount++;
  1141.             }
  1142.  
  1143.             if ( freeBlockPageCount <= MAX_SMALL_PAGE_RANGE )
  1144.             {
  1145.                 RemoveFromSmallFreeRangeList( startPageOffset + numPages, freeBlockPageCount );
  1146.                 numPages += freeBlockPageCount;
  1147.             }
  1148.             else
  1149.             {
  1150.                 RemoveFromLargeFreeRangeList( startPageOffset + numPages, nextPageOffset, largeRangeCount );
  1151.                 numPages += largeRangeCount;
  1152.             }
  1153.         }
  1154.     }
  1155.  
  1156.     if ( numPages <= MAX_SMALL_PAGE_RANGE ) 
  1157.     {
  1158.         // add the uncommitted page block into the m_smallFreePageRangeTable[]
  1159.         listOffset = numPages - 1;
  1160.         m_smallFreePageRangeTable[ startPageOffset ].m_nextFree = m_smallFreePageRangeLists[ listOffset  ];
  1161.         m_smallFreePageRangeLists[ listOffset ] = &m_smallFreePageRangeTable[ startPageOffset ];
  1162.     }
  1163.     else
  1164.     {
  1165.         // add the uncommitted page block onto the m_largeFreePageRangeList linked-list
  1166.         assert( m_largeFreeRangeReuseList != NULL );
  1167.         freeLargePageRange = m_largeFreeRangeReuseList;
  1168.         m_largeFreeRangeReuseList = m_largeFreeRangeReuseList->m_nextFree;
  1169.  
  1170.         freeLargePageRange->m_nextFree = m_largeFreePageRangeList;
  1171.         m_largeFreePageRangeList = freeLargePageRange;
  1172.  
  1173.         freeLargePageRange->m_firstPageOffset = startPageOffset;
  1174.         freeLargePageRange->m_numContiguousPages = numPages;
  1175.     }
  1176.  
  1177.     // actually perform the decommit of the pages and clear the bits in the page table
  1178.     UncommitPageRange( orgStartPageOffset, orgNumPages );
  1179. }
  1180.  
  1181. // RemoveFromSmallFreeRangeList
  1182. //
  1183. // Removes the given page range from the m_smallFreePageRangeLists[]
  1184. void rvHeap::RemoveFromSmallFreeRangeList( uint pageOffset, uint freeBlockPageCount )
  1185. {
  1186.     freePageRange_t *freePageRange, **prevNextFreePageRange;
  1187.     uint curOffset;
  1188.  
  1189.     assert( freeBlockPageCount <= MAX_SMALL_PAGE_RANGE && freeBlockPageCount > 0 );
  1190.  
  1191.     prevNextFreePageRange = &m_smallFreePageRangeLists[ freeBlockPageCount - 1 ];
  1192.     freePageRange = m_smallFreePageRangeLists[ freeBlockPageCount - 1 ];
  1193.     while ( freePageRange != NULL )
  1194.     {
  1195.         curOffset = (uint) (freePageRange - m_smallFreePageRangeTable);
  1196.         if ( curOffset == pageOffset )
  1197.         {
  1198.             *prevNextFreePageRange = freePageRange->m_nextFree;
  1199.             return;
  1200.         }
  1201.         prevNextFreePageRange = &freePageRange->m_nextFree;
  1202.         freePageRange = freePageRange->m_nextFree;
  1203.     }
  1204.     assert(0);    // we should not reach this
  1205. }
  1206.  
  1207. // RemoveFromLargeFreeRangeList
  1208. //
  1209. // Removes the given page range from the m_largeFreePageRangeList
  1210. void rvHeap::RemoveFromLargeFreeRangeList( uint pageOffset, uint &startPageOffset, uint &pageCount )
  1211. {
  1212.     freeLargePageRange_t *freeLargePageRange, ** prevNextFreeLargePageRange;
  1213.  
  1214.     prevNextFreeLargePageRange = &m_largeFreePageRangeList;
  1215.     freeLargePageRange = m_largeFreePageRangeList;
  1216.     while ( freeLargePageRange != NULL )
  1217.     {
  1218.         if ( pageOffset >= freeLargePageRange->m_firstPageOffset &&
  1219.              pageOffset < freeLargePageRange->m_firstPageOffset + freeLargePageRange->m_numContiguousPages )
  1220.         {
  1221.             // found it!
  1222.             *prevNextFreeLargePageRange = freeLargePageRange->m_nextFree;
  1223.             startPageOffset = freeLargePageRange->m_firstPageOffset;
  1224.             pageCount = freeLargePageRange->m_numContiguousPages;
  1225.  
  1226.             freeLargePageRange->m_nextFree = m_largeFreeRangeReuseList;
  1227.             m_largeFreeRangeReuseList = freeLargePageRange;
  1228.             return;
  1229.         }
  1230.  
  1231.         prevNextFreeLargePageRange = &freeLargePageRange->m_nextFree;
  1232.         freeLargePageRange = freeLargePageRange->m_nextFree;
  1233.     }
  1234.     assert(0);    // we should not reach this
  1235. }
  1236.  
  1237. // CommitPageRange
  1238. //
  1239. // Commits the given range of pages and flags them as committed within the m_pageCommitStateTable array
  1240. void *rvHeap::CommitPageRange( uint startPageOffset, uint numPages )
  1241. {
  1242.     void* committedBaseAddress;
  1243.     uint curDWordOffset, endDWordOffset, commitSize, tailShift;
  1244.     dword mask;
  1245.     DWORD allocType;
  1246.  
  1247.     assert( startPageOffset + numPages <= m_numPages );
  1248.  
  1249.     // implement the commit
  1250.     commitSize = numPages << PAGE_SIZE_SHIFT;
  1251.     m_committedSizeBytes += commitSize;
  1252.  
  1253.     allocType = MEM_COMMIT;
  1254. #ifdef _XENON
  1255.     if ( PAGE_SIZE_SHIFT == 16 )
  1256.     {
  1257.         allocType |= MEM_LARGE_PAGES;
  1258.     }
  1259.     allocType |= MEM_NOZERO;
  1260. #endif
  1261.     committedBaseAddress = VirtualAlloc( m_heapStorageStart + (startPageOffset << PAGE_SIZE_SHIFT), commitSize, allocType, PAGE_READWRITE );
  1262.     if ( NULL == committedBaseAddress)
  1263.     {
  1264.         common->Warning( "Out of physical memory - unable to commit requested page range.\n" );
  1265.         return NULL;
  1266.     }
  1267.  
  1268.     assert(sizeof(dword) == 4);    // the following code assumes that a dword is 4 bytes
  1269.     
  1270.     // enable the bits that correspond to the pages being committed within the m_pageCommitStateTable array
  1271.     curDWordOffset = startPageOffset >> 5;
  1272.     endDWordOffset = (startPageOffset + numPages - 1) >> 5;
  1273.  
  1274.     if ( curDWordOffset == endDWordOffset )
  1275.     {
  1276.         mask = 0xFFFFFFFF << (startPageOffset & 0x1F);
  1277.         tailShift = (startPageOffset + numPages) & 0x1F;
  1278.         if ( tailShift != 0 )
  1279.         {
  1280.             mask ^= (0xFFFFFFFF << tailShift);
  1281.         }
  1282.         m_pageCommitStateTable[ curDWordOffset ] |= mask;
  1283.     }
  1284.     else 
  1285.     {
  1286.         mask = 0xFFFFFFFF << (startPageOffset & 0x1F);
  1287.         m_pageCommitStateTable[ curDWordOffset++ ] |= mask;
  1288.  
  1289.         while ( curDWordOffset < endDWordOffset )
  1290.         {
  1291.             m_pageCommitStateTable[ curDWordOffset ] |= 0xFFFFFFFF;
  1292.             curDWordOffset++;
  1293.         }
  1294.  
  1295.         mask = 0xFFFFFFFF >> (32 - (startPageOffset + numPages - (endDWordOffset << 5)));
  1296.         m_pageCommitStateTable[ curDWordOffset ] |= mask;
  1297.     }
  1298.     return committedBaseAddress;
  1299. }
  1300.  
  1301. // UncommitPageRange
  1302. //
  1303. // Uncommits the given range of pages and clears their flags within the m_pageCommitStateTable array
  1304. void rvHeap::UncommitPageRange( uint startPageOffset, uint numPages )
  1305. {
  1306.     BOOL rtnValue;
  1307.     uint curDWordOffset, endDWordOffset, decommitSize, tailShift;
  1308.     dword mask;
  1309.  
  1310.     assert( startPageOffset + numPages <= m_numPages );
  1311.  
  1312.     // implement the free
  1313.     decommitSize = numPages << PAGE_SIZE_SHIFT;
  1314.     assert( decommitSize <= m_committedSizeBytes );
  1315.     m_committedSizeBytes -= decommitSize;
  1316.  
  1317.     rtnValue = VirtualFree( m_heapStorageStart + (startPageOffset << PAGE_SIZE_SHIFT), 
  1318.                             decommitSize, 
  1319.                             MEM_DECOMMIT );
  1320.     assert( rtnValue );
  1321.     assert(sizeof(dword) == 4);    // the following code assumes that a dword is 4 bytes
  1322.     
  1323.     // disable the bits that correspond to the pages being decommitted within the m_pageCommitStateTable array
  1324.     curDWordOffset = startPageOffset >> 5;
  1325.     endDWordOffset = (startPageOffset + numPages - 1) >> 5;
  1326.  
  1327.     if ( curDWordOffset == endDWordOffset )
  1328.     {
  1329.         mask = 0xFFFFFFFF << (startPageOffset & 0x1F);
  1330.         tailShift = (startPageOffset + numPages) & 0x1F;
  1331.         if ( tailShift != 0 )
  1332.         {
  1333.             mask ^= (0xFFFFFFFF << tailShift);
  1334.         }
  1335.         m_pageCommitStateTable[ curDWordOffset ] &= ~mask;
  1336.     }
  1337.     else 
  1338.     {
  1339.         mask = 0xFFFFFFFF << (startPageOffset & 0x1F);
  1340.         m_pageCommitStateTable[ curDWordOffset++ ] &= ~mask;
  1341.  
  1342.         while ( curDWordOffset < endDWordOffset )
  1343.         {
  1344.             m_pageCommitStateTable[ curDWordOffset++ ] = 0;
  1345.         }
  1346.  
  1347.         mask = 0xFFFFFFFF >> (32 - (startPageOffset + numPages - (endDWordOffset << 5)));
  1348.         m_pageCommitStateTable[ curDWordOffset ] &= ~mask;
  1349.     }
  1350. }
  1351.  
  1352. // EnterHeapCriticalSection
  1353. //
  1354. // enters this heap's critical section
  1355. void rvHeap::EnterHeapCriticalSection()
  1356. {
  1357.     ::EnterCriticalSection( &m_criticalSection );
  1358. }
  1359.  
  1360. // ExitHeapCriticalSection
  1361. //
  1362. // exits this heap's critical section
  1363. void rvHeap::ExitHeapCriticalSection()
  1364. {
  1365.     ::LeaveCriticalSection( &m_criticalSection );
  1366. }
  1367.  
  1368. // GetSmallBlockFreeCount
  1369. //
  1370. // Get number of free blocks for this block type
  1371. // If there's a lot of free blocks, then it may be bad.
  1372. int rvHeap::GetSmallBlockFreeCount( int block ) const
  1373. {
  1374.     return GetBlockFreeCount( m_smallFreeBlocks[block] );
  1375. }
  1376.  
  1377.  
  1378. // GetSmallBlockFreeSize
  1379. //
  1380. // Get the actual physical storage committed for empty space in this block
  1381. dword rvHeap::GetSmallBlockFreeSize( int block ) const
  1382. {
  1383.     return GetBlockFreeSize( m_smallFreeBlocks[block] );
  1384. }
  1385.  
  1386. void rvHeap::SetName(const char* name)
  1387. {
  1388.     if(name)
  1389.     {
  1390.         idStr::Copynz(m_name, name, sizeof(m_name));
  1391.     }
  1392. }
  1393.  
  1394. const char* rvHeap::GetName(void) const
  1395. {
  1396.     if(!m_name[0])
  1397.     {
  1398.         return "UN-NAMED";
  1399.     }
  1400.     return m_name;
  1401. }
  1402.  
  1403. // GetLargeBlockFreeCount
  1404. //
  1405. int rvHeap::GetLargeBlockFreeCount() const 
  1406. {
  1407.     return GetBlockFreeCount( m_largeFreeBlocks );
  1408. }
  1409.  
  1410. // GetLargeBlockFreeSize
  1411. //
  1412. dword rvHeap::GetLargeBlockFreeSize() const 
  1413. {
  1414.     return GetBlockFreeSize( m_largeFreeBlocks );
  1415. }
  1416.  
  1417. // GetBlockFreeCount
  1418. //
  1419. int rvHeap::GetBlockFreeCount( rvFreeMemoryBlock_s* currentBlock ) const 
  1420. {
  1421.     dword freeCount = 0;
  1422.     while ( currentBlock ) 
  1423.     {
  1424.         currentBlock = currentBlock->m_nextFree;
  1425.         ++freeCount;
  1426.     }
  1427.     return freeCount;
  1428. }
  1429.  
  1430. // GetBlockFreeSize
  1431. //
  1432. dword rvHeap::GetBlockFreeSize( rvFreeMemoryBlock_s* currentBlock ) const 
  1433. {
  1434.     dword freeSize = 0;
  1435.     while (currentBlock) 
  1436.     {
  1437.         dword blockSize = currentBlock->m_dwordDistToNextBlock << 2;
  1438.         
  1439.         if ( blockSize > PAGE_SIZE )
  1440.         {
  1441. #ifdef _DEBUG
  1442.                dword initialBlockSize = blockSize;
  1443. #endif            
  1444.             // Round up to the next block boundary
  1445.             dword rem = (dword)currentBlock & ~PAGE_SIZE_MASK;
  1446.             blockSize-=rem;
  1447.             
  1448.             // Subtract off the pages that should be decomitted
  1449.             while ( blockSize > PAGE_SIZE ) 
  1450.             {
  1451.                 blockSize-=PAGE_SIZE;
  1452.             }
  1453.             
  1454.             // Make sure we didn't wrap
  1455. #ifdef _DEBUG
  1456.                assert( initialBlockSize > blockSize );
  1457. #endif            
  1458.             freeSize+=blockSize;
  1459.         } 
  1460.         else
  1461.         {
  1462.             // Does not span blocks, so just add
  1463.             freeSize+=blockSize;
  1464.         }
  1465.         
  1466.         currentBlock = currentBlock->m_nextFree;
  1467.     }
  1468.     
  1469.     return freeSize;
  1470. }
  1471.  
  1472. // GetSmallBlockFreeSize
  1473. //
  1474. void Mem_FragmentationStats_f( const idCmdArgs &args ) 
  1475. {
  1476.     rvHeap *heapArray[MAX_SYSTEM_HEAPS];
  1477.     rvGetAllSysHeaps( heapArray );
  1478.     
  1479.     dword unusedMem = 0;
  1480.     dword totalFragments = 0;
  1481.     
  1482.     for ( int i = 0; i < MAX_SYSTEM_HEAPS; ++i ) 
  1483.     {
  1484.         rvHeap *curr = heapArray[i];
  1485.         if ( curr ) 
  1486.         {
  1487.             for ( int j = 0; j < curr->SmallBlockCount(); ++j ) 
  1488.             {
  1489.                 int freeCount = curr->GetSmallBlockFreeCount( j );
  1490.                 if ( freeCount ) 
  1491.                 {
  1492.                     dword unused = curr->GetSmallBlockFreeSize(j);
  1493.                     idLib::common->Printf( "i:%d c:%d t:%d - ", j, freeCount, unused );
  1494.                     unusedMem+=unused;
  1495.                     totalFragments+=freeCount;
  1496.                 }
  1497.             }
  1498.             
  1499.             dword unused = curr->GetLargeBlockFreeSize();
  1500.             dword freeCount = curr->GetLargeBlockFreeCount();
  1501.             unusedMem+=unused;
  1502.             totalFragments+=freeCount;
  1503.             idLib::common->Printf( "i:large c:%d t:%d\n", freeCount, unused );
  1504.             
  1505. //            dword comSize = curr->GetCommittedSize();
  1506.             dword bytesAl = curr->GetBytesAllocated();
  1507.             
  1508.             idLib::common->Printf( "Total fragments: %d Fragment memory: %d\n", totalFragments, unusedMem );
  1509.             idLib::common->Printf( "Fragmentation: %f\n", float(unusedMem) / float(bytesAl) );
  1510.             
  1511.             // We only want the first one, since they all appear to be the same heap right now.
  1512.             break;
  1513.         }
  1514.     }
  1515. }
  1516.  
  1517. #ifdef _DETECT_BLOCK_MERGE_PROBLEMS
  1518. // TestFreeBlockValidity
  1519. //
  1520. // Tests for free memory block merge problems
  1521. void rvHeap::TestFreeBlockValidity()
  1522. {
  1523.     byte *pageAddress;
  1524.     rvMemoryBlock_s *nextBlock;
  1525.     rvFreeMemoryBlock_s *freeBlock;
  1526.     int smallBlockTableOffset;
  1527.  
  1528.     for ( smallBlockTableOffset = 0; 
  1529.           smallBlockTableOffset < NUM_SMALL_BLOCK_TABLE_ENTRIES; 
  1530.           smallBlockTableOffset++ )
  1531.     {
  1532.         // check to see if there is a block that matches exactly
  1533.         freeBlock = m_smallFreeBlocks[smallBlockTableOffset];
  1534.         while ( freeBlock != NULL ) 
  1535.         {
  1536.             // check the blocks around this block
  1537.             if ( freeBlock->m_prev != NULL && freeBlock->m_prev->m_tag == MA_NONE )
  1538.             {
  1539.                 common->Warning( "Previous block failed to merge" );
  1540.             }
  1541.  
  1542.             nextBlock = (rvMemoryBlock_s *) ((byte*) freeBlock + freeBlock->GetDistToNextBlock());
  1543.             pageAddress = (byte *) (((dword) freeBlock) & ~PAGE_SIZE_MASK);
  1544.             
  1545.             if ( (byte*) nextBlock < pageAddress+PAGE_SIZE && nextBlock->m_tag == MA_NONE )
  1546.             {
  1547.                 common->Warning( "Next block failed to merge" );
  1548.             }
  1549.                         
  1550.             freeBlock = freeBlock->m_nextFree;
  1551.         }
  1552.     }
  1553. }
  1554. #endif
  1555.  
  1556. #endif    // #ifdef _RV_MEM_SYS_SUPPORT
  1557.