deargui-vpl/ref/virtools/Includes/VxAllocator.h

791 lines
18 KiB
C++

#ifndef VXALLOCATOR_H
#define VXALLOCATOR_H
#include "XArray.h"
#include "XClassArray.h"
#include "XHashTable.h"
#include "VxMutex.h"
#if defined(_XBOX_VER)
#include <windows.h>
#endif
// Visual Studio compiler was complainiog about a missing
// delete operator on VxPoolObject structure
#ifdef _MSC_VER
#pragma warning(disable:4291)
#endif
class XString;
class XFixedSizeAllocator;
//---- Aligned memory allocation
VX_EXPORT void* VxNewAligned(int size,int align);
VX_EXPORT void VxDeleteAligned(void* ptr);
VX_EXPORT BOOL VxIsAllocatedByNewAligned(void* ptr,int alignement);
#undef new
#undef delete
//--------------------------------------------------------
// To quickly identify the kind of allocation that is done :
#define def_new new
// this is just for information, the default new could be used
#define pool_new new
#define fixeds_new(x) VxAllocator::Instance().AllocatePoolObject((x*)NULL);
// this is the standard delete
#define def_delete delete
// this is just for information, the default new could be used
#define pool_delete delete
#define fixeds_delete(x) VxAllocator::Instance().ReleasePoolObject(x);
/****************************************************************
Summary: Memory buffer class.
Remarks:
o This class is designed to be used as a local or global variable that
handles a memory buffer that will be automatically deleted on destruction.
o The memory buffer is aligned on a 16 bytes boundary (64 on a PS2 system)
o Do not call delete on the buffer returned by Buffer() method.
See Also: VxNewAligned,VxDeleteAligned
****************************************************************/
class VxMemoryPool {
public:
~VxMemoryPool() {
VxDeleteAligned(m_Memory);
m_Memory = NULL;
}
// Constructs the class allocating the buffer to ByteCount bytes.
VxMemoryPool(size_t ByteCount=0) {
m_Memory = NULL;
Allocated= 0;
Allocate(ByteCount);
}
// Returns access to the memory buffer.
void* Buffer() const {
return m_Memory;
}
// Returns allocated size of this memory pool.
size_t AllocatedSize() const {
return Allocated;
}
// Allocates the number of bytes if not yet allocated.
void Allocate(size_t ByteCount) {
if (Allocated<ByteCount) {
VxDeleteAligned(m_Memory);
#ifdef PSX2
m_Memory = (DWORD *)VxNewAligned(ByteCount,64);
#else
m_Memory = (DWORD *)VxNewAligned(ByteCount,16);
#endif
Allocated = ByteCount;
}
}
protected:
DWORD* m_Memory;
size_t Allocated;
};
class VxFreeSizeAllocator
{
friend class VxAllocator;
public:
VxFreeSizeAllocator(const unsigned int iPageSize = 1048576); // Default to 1 Mo Pages
~VxFreeSizeAllocator();
// Methods
void* Allocate(const size_t iSize);
void Release(void* iPtr);
#ifdef PSX2
void* GetPageDataPointer();
void ResetPage();
#endif
// Profiling functions
int FreeQueueSize(int Page = -1);
void DumpFreeQueue();
int DumpPage(int iPage);
void SetMaxPageCount(int iMaxPageCount) { m_MaxPageCount = iMaxPageCount; }
private:
// Memory Block Size : can be more if needed
const unsigned int m_PageSize;
enum {
PAGECOUNT = 256
};
// Types
struct CPage {
CPage():m_Begin(0),m_End(0) {}
unsigned int* Begin() const {return m_Begin;}
unsigned int* End() const {return m_End;}
unsigned int Size() const {return m_End - m_Begin;}
unsigned int* m_Begin;
unsigned int* m_End;
};
// Class to repressent the starting data of an allocation
struct CMemoryUnit
{
unsigned int* End() {return (unsigned int*)this + m_Size;}
#ifdef PSX2
enum {
SIZE = 4
};
// 16 Bytes long
unsigned int m_Size; // Size of the element (and offset to the next)
int m_PrevOffset;
int m_Free;
int m_Page;
#else
enum {
SIZE = 2
};
unsigned int m_Size; // Size of the element (and offset to the next)
int m_PrevOffset:23;
int m_Free:1;
int m_Page:8;
#endif
}; // 2 dword of size
struct CFreeMemoryUnit : public CMemoryUnit
{
CFreeMemoryUnit* m_Next; // Next free element
CFreeMemoryUnit* m_Prev; // Previous free element
}; // 4 dword of size
///
// Members
// Array of allocated pages
CPage m_Pages[PAGECOUNT];
int m_PageCount;
int m_MaxPageCount;
// Array of free memory pointers
CFreeMemoryUnit* m_FreeQueue;
///
// Methods
CPage* AllocateNewPage(const unsigned int iSize);
void UseUnit(CMemoryUnit* iUnit,const unsigned int iSize);
void FreeUnit(CMemoryUnit* iUnit,const CPage& iPage);
///
// Free Queue operations
CFreeMemoryUnit* Begin() {return m_FreeQueue->m_Next;}
CFreeMemoryUnit* End() {return m_FreeQueue;}
inline void InsertFreeUnit(CFreeMemoryUnit* iUnit);
inline void RemoveFreeUnit(CFreeMemoryUnit* iUnit);
inline CFreeMemoryUnit* FindFreeUnit(const unsigned int iDwordSize);
// Profiling
void _DumpNodePost(CFreeMemoryUnit* iNode);
int m_FreeIndex;
int m_FreeCount;
};
class VxSmallSizeAllocator
{
friend class VxAllocator;
friend class RCKRenderManager;
public:
VxSmallSizeAllocator(); // Default to 512 Ko Pages
~VxSmallSizeAllocator();
// Methods
void* Allocate(const size_t iSize);
void Release(void* iPtr);
XBOOL IsOwner(void* iPtr);
// Profiling/Stats functions
int FreeQueueSize();
void FreeQueueStats(int& oCount, int& oSize) const;
void DumpFreeQueue();
void DumpPage(int iPage);
int GetPageCount() {
return m_PageCount;
}
#define MUPREVSIZE 18
#define MUSIZE (24-MUPREVSIZE)
enum {
PAGESIZE = 1 << (MUPREVSIZE-1), // Pages size : directly related to the :19 in the MemoryUnit
PAGEBITS = 7,
PAGECOUNT = 1<<PAGEBITS, // Pages count : directly related to the :8 in the MemoryUnit
MAXSIZE = 1<<(MUSIZE+1),
FREEQUEUESCOUNT = MAXSIZE>>2
};
private:
///
// Types
// Class to repressent the starting data of an allocation
struct CMemoryUnit
{
XDWORD* UsedEnd() {
return (unsigned int*)this + m_Size;
}
XDWORD* FreeEnd() {
return (unsigned int*)this + m_PrevSize;
}
XDWORD* End() {
if (m_Free)
return (unsigned int*)this + m_PrevSize;
else
return (unsigned int*)this + m_Size;
}
XDWORD GetSize() {
if (m_Free)
return m_PrevSize;
else
return m_Size;
}
XDWORD GetPreviousSize() {
if (m_Free)
return m_Size;
else
return m_PrevSize;
}
XDWORD m_PrevSize:MUPREVSIZE; // Size of previous element or size of the free block
XDWORD m_Size:MUSIZE; // Size of the element in DWORD so limited to 64 (and offset to the next) or size of the previous allocated unit
XDWORD m_Free:1; // Marker free or not
XDWORD m_Page:PAGEBITS; // Page index (128 pages Max)
}; // 1 dword of size
// class to represent a freed unit allocation
struct CFreeMemoryUnit : public CMemoryUnit
{
CFreeMemoryUnit* m_Next; // Next free element
}; // 2 dword of size
// Page structure
struct CPage {
CPage():m_Begin(0),m_End(0) {
// Inits the queues
for (int i=0; i<FREEQUEUESCOUNT+1; ++i)
m_FreeUnits[i].m_Next = &m_FreeUnits[i];
}
unsigned int* Begin() const {return m_Begin;}
unsigned int* End() const {return m_End;}
unsigned int Size() const {return m_End - m_Begin;}
unsigned int* m_Begin;
unsigned int* m_End;
unsigned int m_Occupation; // Size occupied of the page
// Single linked of free memory pointers
CFreeMemoryUnit m_FreeUnits[FREEQUEUESCOUNT+1];
};
///
// Members
// Array of allocated pages
CPage m_Pages[PAGECOUNT];
// Range of used pages (not necessary means that all the pages
// in this range are allocated)
XDWORD m_PageCount;
// array of pages containing the page index by occupation
// (most occupied comes first, less occupied comes last)
XDWORD m_PageMostOccupied[PAGECOUNT];
//we need a count because we do not store fully occupied or deleted pages
XDWORD m_PageMostOccupiedCount;
///
// Methods
CPage* AllocateNewPage(const unsigned int iSize);
void UseUnit(CMemoryUnit* iUnit,const unsigned int iSize);
void FreeUnit(CMemoryUnit* iUnit,const CPage& iPage);
void ValidatePage(const unsigned int iPage);
void Check();
void SortMostOccupied(XBOOL iAfterAllocation);
void AddFullPageToMostOccupied(XDWORD iPageIndex);
void RemovePageFromMostOccupied(XDWORD iPageIndex);
///
// Free Queue operations
// Begin
CFreeMemoryUnit* Begin(CPage& iPage, const unsigned int iSize) {
//XASSERT(iSize < FREEQUEUESCOUNT);
// we normally don't need to test because we always know when we call for size 0
// except in the removefreeunit !!! to optimize !
if (iSize > FREEQUEUESCOUNT)
return iPage.m_FreeUnits[0].m_Next;
else
return iPage.m_FreeUnits[iSize].m_Next;
}
// End
CFreeMemoryUnit* End(CPage& iPage, const unsigned int iSize) {
// XASSERT(iSize < FREEQUEUESCOUNT);
// we normally don't need to test because we always know when we call for size 0
// except in the removefreeunit !!! to optimize !
if (iSize > FREEQUEUESCOUNT)
return &(iPage.m_FreeUnits[0]);
else
return &(iPage.m_FreeUnits[iSize]);
}
// InsertFreeUnit
inline void InsertFreeUnit(CPage& iPage, CFreeMemoryUnit* iUnit);
// RemoveFreeUnit
void RemoveFreeUnit(CFreeMemoryUnit* iUnit) {
XASSERT(iUnit->m_Page < m_PageCount);
CPage& page = m_Pages[iUnit->m_Page];
CFreeMemoryUnit* it = Begin(page, iUnit->m_PrevSize);
CFreeMemoryUnit* itend = End(page, iUnit->m_PrevSize);
CFreeMemoryUnit* prev = itend;
while (it != itend) {
if (it == iUnit) { // found !
prev->m_Next = it->m_Next;
return;
}
prev = it;
it = it->m_Next;
}
}
// FindFreeUnit
inline CFreeMemoryUnit* FindAndRemoveFreeUnit(const unsigned int iDwordSize);
};
/***************************************************
Summary:Memory management
****************************************************/
class VxAllocator
{
friend class VxScratch;
friend class RCKRenderManager;
friend class RCKTriangles;
// Allocation Mutex
VxMutex m_Mutex;
public:
// CTor
VxAllocator();
// Dtor
~VxAllocator();
// Standard Allocation
VX_EXPORT void* Allocate(size_t iSize);
// Standard Deallocation
VX_EXPORT void Release(void* iMem);
VX_EXPORT void DeleteAllAllocatedDatas();
// Gets an instance on the allocator
VX_EXPORT static VxAllocator& Instance();
// Updates the debug symbols
void UpdateSymbols(const char* iModule);
///
// String Public Part
// allocate a string
VX_EXPORT char* AllocateString(const char* iString);
// allocate a string
VX_EXPORT char* AllocateString(unsigned int iSize);
// Release a string
VX_EXPORT void ReleaseString(char* iString);
///
// Scratch Public Part
// allocate a scratch (temporary usage) memory
VX_EXPORT void* AllocateScratch(size_t iSize);
// Release a scratch memory
VX_EXPORT void ReleaseScratch(void* iMemory);
// Returns the total memory allocated for scratch
VX_EXPORT size_t GetAllocatedScratchMemory();
// Returns the count of scratch pool
VX_EXPORT int GetAllocatedScratchCount();
// Free the memory allocated by unused scratch pools
VX_EXPORT void ReleaseUnusedScratch();
///
// Scratch Strings
// allocate a scratch (temporary usage) string
VX_EXPORT XString* AllocateScratchString();
// Release the scratch string
VX_EXPORT void ReleaseScratchString(XString* iString);
///
// Generic Allocation Part
// allocate the general memory
VX_EXPORT void* AllocateMemory(size_t iSize);
// Release the general memory
VX_EXPORT void ReleaseMemory(void* iMem);
///
// Pool Allocation Part
// allocate an object from a fixed size pool
template <class T>
T* AllocatePoolObject(T* iDummy)
{
return new (AllocatePool(sizeof(T))) T;
}
// Release an object from a fixed size pool
template <class T>
void ReleasePoolObject(T* iMem)
{
iMem->~T();
ReleasePool(sizeof(T),iMem);
}
// allocate memory in a fixed size pool
VX_EXPORT void* AllocatePool(size_t iSize);
// Release memory allocated by a fixed size pool
VX_EXPORT void ReleasePool(size_t iSize, void* iMem);
///
// Stats
// dump the stats into a file
VX_EXPORT void DumpStats(const char* iFile);
// Types
struct Stats
{
Stats():total(0),peak(0),accumulated(0) {}
int total;
int peak;
int accumulated;
int size;
int operator < (const Stats& iS) const
{
return accumulated*size > iS.accumulated*iS.size;
}
void Add(int i=1)
{
accumulated += i;
total += i;
if (total > peak) {
peak = total;
}
}
void Remove(int i=1)
{
total -= i;
if (total < 0)
total = 0;
}
};
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
VX_EXPORT const Stats& GetGlobalStats() const;
private:
///
// Generic Allocation Part
typedef XHashTable<size_t,XString> HashCallStack2Size;
typedef XHashTable<Stats,size_t> HashSize2Stats;
typedef XHashTable<size_t,void*> HashAddress2Size;
static int CompareAllocated(const void *elem1, const void *elem2 );
// Hash table of memory stats
#if defined(_XBOX_VER)
struct AllocationSentinel
{
AllocationSentinel() {
InterlockedIncrement(&count);
}
~AllocationSentinel() {
InterlockedDecrement(&count);
}
static volatile LONG count;
};
#else
struct AllocationSentinel
{
AllocationSentinel() {
count++;
}
~AllocationSentinel() {
--count;
}
static volatile int count;
};
#endif
HashSize2Stats m_MemoryStats;
HashAddress2Size m_MemorySize;
HashCallStack2Size m_MemoryCallStacks;
XString m_TempCallStack;
///
// Scratch Part
// allocate the scratch memory
VX_EXPORT VxMemoryPool* XAllocateScratch(size_t iSize);
typedef XArray<VxMemoryPool*> ScratchPoolArray;
// Used scratch pool
ScratchPoolArray m_UsedScratch;
// Free scratch pool
ScratchPoolArray m_FreeScratch;
///
// Scratch String Part
typedef XArray<XString*> ScratchStringPoolArray;
// Used scratch pool
ScratchStringPoolArray m_UsedScratchStrings;
// Free scratch pool
ScratchStringPoolArray m_FreeScratchStrings;
///
// String part
// strings count statistics
Stats m_StringsCount;
// strings allocated size statistics
Stats m_StringsSize;
// Storage For global allocation stats
mutable Stats m_GlobalAllocation;
public:
int m_SharedStringsCount;
int m_UnSharedStringsCount;
private:
// temp array
XClassArray<XString> m_AllocatedStrings;
XArray<const char*> m_AllocatedStrings2;
///
// Pool Part
enum {
POOLMAXOBJECTSIZE = 1024 // max size of objects in pool
// BIGMARKER = 0xbee9bee9 // Marker preceding the allocation not related to the SmallSizeAllocator
};
// hash table size -> pool of fixed size
XFixedSizeAllocator* m_Pools[POOLMAXOBJECTSIZE>>2];
// Small Size (<64) Allocator
VxSmallSizeAllocator m_SmallAllocator;
// Free Size allocator in replacement for malloc/free
VxFreeSizeAllocator m_FreeAllocator;
};
//-------------------------------
// Summary: Ensures a virtual class is allocated inside a fixed size memory pool.
// Remarks:
// Adding this macro to the class definition ensure
// the memory allocation will be done in a fixed size
// pool.
// This macro only works for class with a virtual table
// use IMPLEMENT_POOL_CLASS otherwise
#define IMPLEMENT_POOL()\
void* operator new(size_t st) {\
return VxAllocator::Instance().AllocatePool(st);\
}\
void operator delete(void* mem,size_t st) {\
VxAllocator::Instance().ReleasePool(st,mem);\
}\
//------------------------------------
// Summary: Ensures a class is allocated inside a fixed size memory pool.
// Adding this macro to the class definition ensure
// the memory allocation will be done in a fixed size
// pool
// For class that do not have a virtual table
// use this macro with the name of the class
// inside the class declaration
#define IMPLEMENT_POOL_CLASS(classname)\
void* operator new(size_t st) {\
return VxAllocator::Instance().AllocatePool(st);\
}\
void operator delete(void* mem,size_t st) {\
VxAllocator::Instance().ReleasePool(sizeof(classname),mem);\
}\
/*************************************************************
Summary: A base class for fixed sized object that should be placed in fixed size pools.
Remarks:
This class force the usage of a virtual destructor so it should not be used with
small structures of known size that does not need a virtual table. Instead for a class
to be automatically placed in a fixed size pool use the IMPLEMENT_POOL macro that will
redefines the new and delete operator for this class
*************************************************************/
class VxPoolObject {
public:
IMPLEMENT_POOL()
public:
virtual ~VxPoolObject() {}
};
/*************************************************************
Summary: Class to allocate some scratch memory.
Remarks
Allocates a scratch for temporary needs.
This class release the memory when it has finished
*************************************************************/
class VxScratch
{
public:
VxScratch(size_t iSize=0)
{
m_Memory = VxAllocator::Instance().XAllocateScratch(iSize);
}
~VxScratch()
{
if (m_Memory)
VxAllocator::Instance().ReleaseScratch(m_Memory->Buffer());
}
void* Check(size_t iSize)
{
if (!m_Memory || (iSize > m_Memory->AllocatedSize())) { // we need to reallocate
if (m_Memory)
VxAllocator::Instance().ReleaseScratch(m_Memory->Buffer());
m_Memory = VxAllocator::Instance().XAllocateScratch(iSize);
}
return Mem();
}
void* Mem()
{
if (m_Memory)
return m_Memory->Buffer();
return 0;
}
protected:
// the memory
VxMemoryPool* m_Memory;
private:
VxScratch(const VxScratch&);
VxScratch& operator=(const VxScratch&);
};
/*************************************************************
Summary: Class to allocate some scratch memory for a string.
Remarks
Allocates a scratch for temporary needs.
This class release the memory when it has finished
*************************************************************/
class VxScratchString
{
public:
VxScratchString(const char* iString = "")
{
m_String = VxAllocator::Instance().AllocateScratchString();
*m_String = iString;
}
~VxScratchString()
{
VxAllocator::Instance().ReleaseScratchString(m_String);
}
XString& operator* () const
{
return *m_String;
}
XString* operator-> () const
{
return m_String;
}
protected:
// the string
XString* m_String;
private:
VxScratchString(const VxScratchString&);
VxScratchString& operator=(const VxScratchString&);
};
/*************************************************************
Summary: Tokenize a string.
Remarks
*************************************************************/
class XStringTokenizer
{
public:
XStringTokenizer(const char* iString, const char* iSeparators):m_String(iString),m_Separators(iSeparators) {}
VX_EXPORT const char* NextToken(const char* iPrevToken);
private:
VxScratchString m_String;
const char* m_Separators;
};
#endif