quake4-sdk/source/idlib/rvHeap.h

378 lines
18 KiB
C
Raw Normal View History

2007-06-15 00:00:00 +00:00
//
// rvHeap.h - Heap object (replacing the idHeap class)
// Date: 12/13/04
// Created by: Dwight Luetscher
//
#ifndef __RV_HEAP_H__
#define __RV_HEAP_H__
class rvHeapArena;
struct rvMemoryBlock_s;
struct rvFreeMemoryBlock_s;
//#define _DETECT_BLOCK_MERGE_PROBLEMS
// Define some flags used in describing various properties of allocations
// coming from this heap (these flags are passed into rvHeap::Init()).
static const uint rvHeapFlagDefault = 0;
static const uint rvHeapFlagWriteCombine = 0x0001; // flag specified if the memory allocated from this heap is intended to have a write-combine cache policy
static const uint rvHeapFlagNoCache = 0x0002; // flag specified if the memory allocated from this heap is intended to have a no cache policy
// Define some limits used by each heap
static const uint NUM_MEMORY_BLOCK_SIZE_BITS = 27;
static const uint NUM_TAG_BITS = 5;
static const uint MAX_SINGLE_ALLOCATION_SIZE = ((1 << (NUM_MEMORY_BLOCK_SIZE_BITS+2)) - 1); // maximum number of bytes that can be allocated in a single allocation
static const uint NUM_SMALL_BLOCK_TABLE_ENTRIES = 2048; // number of entries within each heap's small free memory block table
static const uint SMALL_BLOCK_ALIGN_SIZE_SHIFT = 2; // left shift of the size that all allocations are rounded to
static const uint SMALL_BLOCK_ALIGN_SIZE = 1 << SMALL_BLOCK_ALIGN_SIZE_SHIFT; // size that all allocations are rounded to
static const uint SMALL_BLOCK_ALIGN_SIZE_MASK = SMALL_BLOCK_ALIGN_SIZE-1; // mask of the bits used for the size that all allocations are rounded to
static const uint MAX_SMALL_BLOCK_SIZE = (NUM_SMALL_BLOCK_TABLE_ENTRIES << SMALL_BLOCK_ALIGN_SIZE_SHIFT) - 1; // maximum size of an allocation to be considered a "small block" allocation
static const uint PAGE_SIZE_SHIFT = 16; // left shift of the page size used by this heap object
static const uint PAGE_SIZE = 1 << PAGE_SIZE_SHIFT; // page size used by this heap object
static const uint PAGE_SIZE_MASK = PAGE_SIZE - 1;
static const uint MAX_SMALL_PAGE_RANGE = 128; // maximum number of contiguous pages for a single range to be considered "small"
static const uint MIN_LARGE_PAGE_RANGE = MAX_SMALL_PAGE_RANGE+1; // minimum number of contiguous pages for a single range to be considered "large"
// Define the freePageRange_t structure used by this rvHeap used to maintain linked-lists of contiguous page ranges below LARGE_PAGE_RANGE
typedef struct freePageRange_s
{
freePageRange_s* m_nextFree; // next range of free pages in a linked-list
}
freePageRange_t;
// Define the freePageRange_t structure used by this rvHeap used to maintain a linked-list of contiguous page ranges above LARGE_PAGE_RANGE
typedef struct freeLargePageRange_s
{
freeLargePageRange_s* m_nextFree; // next large range of free pages in a linked-list
uint m_firstPageOffset; // offset of the first page in this range
uint m_numContiguousPages; // count of the number of contiguous pages in this range
}
freeLargePageRange_t;
class rvHeap
{
public:
rvHeap( ); // constructor
~rvHeap( ); // destructor
void Init( rvHeapArena &heapArena, uint maxHeapSizeBytes, uint flags = rvHeapFlagDefault ); // initializes this heap for use within the given arena, and with the given size limit that it can grow to
void Shutdown( ); // releases this heap from use
ID_INLINE bool IsInitialized( ) const; // returns true if this heap is currently initialized, and not shutdown
ID_INLINE void *Allocate( uint sizeBytes, int debugTag = 0 ); // allocate memory
ID_INLINE void *Allocate16( uint sizeBytes, int debugTag = 0 ); // allocate memory aligned on a 16-byte boundary
void Free( void *allocation ); // free memory
int Msize( void *allocation ); // returns the size, in bytes, of the allocation at the given address (including header, alignment bytes, etc).
void FreeAll( ); // frees all the allocations from this heap (and decommits all pages)
ID_INLINE bool DoesAllocBelong( void *allocBase ); // returns true if the given address was allocated from this heap, false otherwise
ID_INLINE rvHeapArena *GetArena( ); // returns the arena this heap is associated with
ID_INLINE void PushCurrent( ); // makes this heap the new top of stack for its associated arena, thus making it current
ID_INLINE void PopCurrent( ); // pops this heap, or any other heap, from the top of stack of this heap's associated arena, thus making its predecessor current
ID_INLINE uint GetSize( ) const; // returns the current size that this heap has grown to, in bytes
ID_INLINE uint GetMaxSize( ) const; // returns the maximum size that this heap can grow to, in bytes
ID_INLINE uint GetCommittedSize( ) const; // returns the number of physical bytes this heap is currently using (total bytes of all committed pages)
ID_INLINE uint GetBytesAllocated( ) const; // returns the number of bytes currently allocated from this heap (actual memory footprint of all active allocations including internal headers and alignment bytes)
ID_INLINE uint GetBytesRequested( ) const;
ID_INLINE int GetNumAllocations( ) const; // returns the number of allocations made from this heap that are still active (have not been freed)
ID_INLINE uint GetBytesAllocatedByTag( Mem_Alloc_Types_t allocType ) const; // returns the number of bytes currently allocated from this heap with the given allocation tag (actual memory footprint of all active allocations including internal headers and alignment bytes)
ID_INLINE uint GetPeekBytesAllocatedByTag( Mem_Alloc_Types_t allocType ) const; // returns the peek number of bytes allocated from this heap with the given allocation tag (actual memory footprint of all active allocations including internal headers and alignment bytes)
ID_INLINE int GetNumAllocationsByTag( Mem_Alloc_Types_t allocType ) const; // returns the current number of allocation from this heap with the given allocation tag
ID_INLINE bool IsWriteCombine( ) const; // returns true if memory allocated from this heap has a write-combine cache policy, false otherwise
ID_INLINE bool IsNoCache( ) const; // returns true if memory allocated from this heap has a no-cache policy, false otherwise
int SmallBlockCount() const { return NUM_SMALL_BLOCK_TABLE_ENTRIES; }
int GetSmallBlockFreeCount( int block ) const;
dword GetSmallBlockFreeSize( int block ) const;
int GetLargeBlockFreeCount() const;
dword GetLargeBlockFreeSize() const;
int GetBlockFreeCount( rvFreeMemoryBlock_s* currentBlock ) const;
dword GetBlockFreeSize( rvFreeMemoryBlock_s* currentBlock ) const;
void SetName(const char* name);
const char* GetName(void) const;
void SetDebugID(byte debugID) {mDebugID=debugID;}
byte DebugID(void) const {return mDebugID;}
protected:
rvHeapArena *m_arena; // arena associated with this heap
rvHeap *m_next; // next heap belonging to the same arena
CRITICAL_SECTION m_criticalSection; // critical section associated with this heap
byte *m_baseAddress; // base address of this heap (virtual)
byte *m_zeroLengthAllocPage; // special page for zero-length allocations (remains uncommitted)
byte *m_heapStorageStart; // address of the start of this heap's storage (just past page table)
rvFreeMemoryBlock_s *m_smallFreeBlocks[NUM_SMALL_BLOCK_TABLE_ENTRIES]; // table used to manage linked-lists of small free memory blocks
rvFreeMemoryBlock_s *m_largeFreeBlocks; // linked-list of all of the large free memory blocks
dword *m_pageCommitStateTable; // a very long bit mask (1-bit per page) that describes whether the page has been committed or not to physical memory
freePageRange_t *m_smallFreePageRangeTable; // array of structures that each correspond to a particular page - the structures representing the pages at the start of a small range are added to lists within the m_smallFreePageRangeLists[] array below
freePageRange_t *m_smallFreePageRangeLists[MAX_SMALL_PAGE_RANGE]; // array where each entry is a linked-list of structures that individually describe a contiguous range of pages (where the range count matches the index into this array plus 1)
freeLargePageRange_t *m_largeFreePageRangeList; // linked-list of structures that individually describe a large contiguous range of pages
// the following two data members are just used to manage the storage of the freeLargePageRange_t structures
freeLargePageRange_t *m_largeFreeRangeReuseList; // linked-list of freeLargePageRange_t that are currently not part of the m_largeFreePageRangeList linked-list and are available for use
freeLargePageRange_t *m_largeFreeRangeStorage; // array that acts as a store of the freeLargePageRange_t structures
uint m_largeFreeRangeStorageSize; // number of entries in the m_largeFreeRangeStorage[] array
uint m_pageCommitStateArrayByteSize; // size of the m_pageCommitStateTable[] array in bytes
uint m_smallFreePageRangeByteSize; // size of the m_smallFreePageRangeTable[] array in bytes
uint m_numPages; // number of pages that can span this heap entirely
uint m_heapRangeBytes; // the number of bytes in the virtual address space of this heap (including zero-length allocation page)
uint m_committedSizeBytes; // the number of physical bytes this heap is currently using (total bytes of all committed pages)
uint m_maxHeapSizeBytes; // the maximum size that this heap can grow to, in bytes (allocatable space)
uint m_numBytesRequested; // the number of bytes currently requested for allocation from this heap (memory footprint without the overhaed of internal headers and alignment bytes)
uint m_numBytesAllocated; // the number of bytes currently allocated from this heap (actual memory footprint of all active allocations including internal headers and alignment bytes)
uint m_allocatedBytesByTag[MA_MAX]; // the number of bytes allocated from this heap - organized by usage tag
uint m_peekAllocatedBytesByTag[MA_MAX]; // the peek number of bytes allocated from this heap - organized by usage tag
int m_numAllocationsByTag[MA_MAX]; // the number of allocations - organized by usage tag
int m_numAllocations; // the number of allocations made from this heap that are still active (have not been freed)
int m_numZeroLengthAllocations; // the number of zero length allocations made from this heap
uint m_largestFreeSmallBlockOffset; // small block table offset of the largest free small block available (block that is part of m_smallFreeBlocks[] table)
uint m_flags; // flags that describe various properties of this heap
byte mDebugID; // ID that identifies heap. Needed to absolutely identify a heap.
char m_name[20]; // for debug display
void ResetValues( ); // resets the data members to their pre-initialized state
void BuildLargeFreeRangeReuseList( ); // builds the m_largeFreeRangeReuseList linked-list from the m_largeFreeRangeStorage[] array.
byte *AllocateMemory( uint sizeBytes, int debugTag, bool align16Flag ); // performs memory allocation for the given number of bytes from this heap
rvMemoryBlock_s *AllocateMemorySmallBlock( uint sizeBytes ); // perform a small block allocation - try to use an existing free block pointed to by the table.
rvMemoryBlock_s *AllocateBlockFromTable( uint smallBlockTableOffset, uint sizeBytes ); // allocates the a small memory block from the free block table and partitions it, if necessary, into an allocated chunk and a free chunk (which remains in the m_smallFreeBlocks[] table).
rvMemoryBlock_s *AllocateMemoryLargeBlock( uint sizeBytes ); // perform a large block allocation - try to use an existing free block from within the large free block linked-list.
void TestLargestFreeSmallBlockOffset( uint smallBlockTableOffset ); // checks to see if the largest free small block has moved down
void FixUpNextBlocksPrev( byte *newBlock, uint blockSize ); // fixes up the previous pointer of the memory block that lies immediately past the given newBlock, if it remains on the same page.
void AddFreeBlock( rvFreeMemoryBlock_s *freeBlock ); // adds the given free block to the small allocation table or large allocation list.
void RemoveFreeBlock( rvFreeMemoryBlock_s *freeBlock ); // removes the given block from the small allocation table or large allocation list.
void *PageCommit( uint numDesiredPages ); // commits the given number of contiguous pages to physical memory
void PageUncommit( byte *pageAddress, uint numPages ); // uncommits the given range of contiguous pages back to physical memory
void RemoveFromSmallFreeRangeList( uint pageOffset, uint freeBlockPageCount ); // removes the given page range from the m_smallFreePageRangeLists[]
void RemoveFromLargeFreeRangeList( uint pageOffset, uint &startPageOffset, uint &pageCount ); // removes the given page range from the m_largeFreePageRangeList
void *CommitPageRange( uint startPageOffset, uint numPages ); // commits the given range of pages and flags them as committed within the m_pageCommitStateTable array
void UncommitPageRange( uint startPageOffset, uint numPages ); // uncommits the given range of pages and clears their flags within the m_pageCommitStateTable array
void EnterHeapCriticalSection(); // enters this heap's critical section
void ExitHeapCriticalSection(); // exits this heap's critical section
#ifdef _DETECT_BLOCK_MERGE_PROBLEMS
void TestFreeBlockValidity(); // test for free memory block merge problems
#endif
friend class rvHeapArena; // give the rvHeapArena class access to the following methods
// {
ID_INLINE void SetArena( rvHeapArena *arena ); // sets the arena this heap is associated with
ID_INLINE void SetNext( rvHeap *next ); // sets the next heap associated with the same arena
ID_INLINE rvHeap *GetNext( ); // returns the next heap associated with the same arena
// }
};
// IsInitialized
//
// returns: true if this heap is currently initialized, and not shutdown
ID_INLINE bool rvHeap::IsInitialized( ) const
{
return m_arena != NULL;
}
// Allocate
//
// allocate memory
void *rvHeap::Allocate( uint sizeBytes, int debugTag )
{
return AllocateMemory( sizeBytes, debugTag, false );
}
// Allocate16
//
// allocate memory aligned on a 16-byte boundary
void *rvHeap::Allocate16( uint sizeBytes, int debugTag )
{
return AllocateMemory( sizeBytes, debugTag, true );
}
// DoesAllocBelong
//
// returns: true if the given address was allocated from this heap, false otherwise
ID_INLINE bool rvHeap::DoesAllocBelong( void *allocBase )
{
return allocBase >= m_zeroLengthAllocPage && allocBase < m_heapStorageStart + m_heapRangeBytes;
}
// GetArena
//
// returns: the arena this heap is associated with
ID_INLINE rvHeapArena *rvHeap::GetArena( )
{
return m_arena;
}
// GetNext
//
// returns: the next heap belonging to the same arena
ID_INLINE rvHeap *rvHeap::GetNext( )
{
return m_next;
}
// PushCurrent
//
// makes this heap the new top of stack for its associated arena, thus making it current.
ID_INLINE void rvHeap::PushCurrent( )
{
m_arena->Push( *this );
}
// PopCurrent
//
// pops this heap, or any other heap, from the top of stack of this heap's associated arena,
// thus making its predecessor current
ID_INLINE void rvHeap::PopCurrent( )
{
m_arena->Pop( );
}
// GetCommittedSize
//
// returns: the number of physical bytes this heap is currently using (total bytes of all committed pages)
ID_INLINE uint rvHeap::GetCommittedSize( ) const
{
return m_committedSizeBytes;
}
// GetMaxSize
//
// returns the maximum size that this heap can grow to, in bytes
ID_INLINE uint rvHeap::GetMaxSize( ) const
{
return m_maxHeapSizeBytes;
}
// GetBytesAllocated
//
// returns: the number of bytes currently allocated from this heap
// (actual memory footprint of all active allocations
// including internal headers and alignment bytes)
//
ID_INLINE uint rvHeap::GetBytesAllocated( ) const
{
return m_numBytesAllocated;
}
// GetBytesRequested
//
// returns: the number of bytes currently requested from this heap
// (i.e. without overhead)
//
ID_INLINE uint rvHeap::GetBytesRequested( ) const
{
return m_numBytesRequested;
}
// GetNumAllocations
//
// returns: the number of allocations made from this heap that are still active (have not been freed)
ID_INLINE int rvHeap::GetNumAllocations( ) const
{
return m_numAllocations;
}
// GetBytesAllocatedByTag
//
// returns: the number of bytes currently allocated from this heap with the given allocation tag
// (actual memory footprint of all active allocations including internal headers and
// alignment bytes).
ID_INLINE uint rvHeap::GetBytesAllocatedByTag( Mem_Alloc_Types_t allocType ) const
{
assert( allocType < MA_MAX );
return m_allocatedBytesByTag[(uint) allocType];
}
// GetPeekBytesAllocatedByTag
//
// returns: the peek number of bytes allocated from this heap with the given allocation tag (actual
// memory footprint of all active allocations including internal headers and alignment bytes)
ID_INLINE uint rvHeap::GetPeekBytesAllocatedByTag( Mem_Alloc_Types_t allocType ) const
{
assert( allocType < MA_MAX );
return m_peekAllocatedBytesByTag[(uint) allocType];
}
// GetNumAllocationsByTag
//
// returns: the current number of allocation from this heap with the given allocation tag
ID_INLINE int rvHeap::GetNumAllocationsByTag( Mem_Alloc_Types_t allocType ) const
{
assert( allocType < MA_MAX );
return m_numAllocationsByTag[(uint) allocType];
}
// IsWriteCombine
//
// returns: true if memory allocated from this heap has a write-combine cache policy, false otherwise
ID_INLINE bool rvHeap::IsWriteCombine( ) const
{
return (m_flags & rvHeapFlagWriteCombine) != 0;
}
// IsNoCache
//
// returns: true if memory allocated from this heap has a no-cache policy, false otherwise
ID_INLINE bool rvHeap::IsNoCache( ) const
{
return (m_flags & rvHeapFlagNoCache) != 0;
}
// SetArena
//
// sets the arena this heap is associated with
ID_INLINE void rvHeap::SetArena( rvHeapArena *arena )
{
m_arena = arena;
}
// SetNext
//
// sets the next heap associated with the same arena
ID_INLINE void rvHeap::SetNext( rvHeap *next )
{
m_next = next;
}
extern void Mem_FragmentationStats_f( const class idCmdArgs &args );
#endif // #ifndef __RV_HEAP_H__