windows-nt/Source/XPSP1/NT/multimedia/directx/dmusic/dmusic32/tpool.h
2020-09-26 16:20:57 +08:00

280 lines
6.8 KiB
C++

// Copyright (c) 1998 Microsoft Corporation
//
// TPool.h
//
// Template pool memory manager. Efficiently manage requests for many of the same (small) object.
// Named after t'Pool, the Vulcan programmer who invented the technique.
//
#ifndef _TPOOL_H_
#define _TPOOL_H_
#include "debug.h"
#define POOL_DEFAULT_BYTE_PER_BLOCK 4096
#define MIN_ITEMS_PER_BLOCK 4
///////////////////////////////////////////////////////////////////////////////
//
// CPool
//
// A simple memory manager that efficiently handles many objects of the same
// size by allocating blocks containing multiple objects at once.
//
//
template<class contained> class CPool
{
public:
CPool(int nApproxBytesPerBlock = POOL_DEFAULT_BYTE_PER_BLOCK);
~CPool();
contained *Alloc();
void Free(contained* pToFree);
private:
union CPoolNode
{
CPoolNode *pNext;
contained c;
};
class CPoolBlock
{
public:
CPoolBlock *pNext;
CPoolNode *pObjects;
};
int nItemsPerBlock; // Based on bytes per block
int nAllocatedBlocks; // # allocated blocks
CPoolBlock *pAllocatedBlocks; // list of allocated blocks
int nFreeList; // # nodes in free list
CPoolNode *pFreeList; // free list
private:
bool RefillFreeList();
#ifdef DBG
bool IsPoolNode(CPoolNode *pNode);
bool IsInFreeList(CPoolNode *pNode);
#endif
};
///////////////////////////////////////////////////////////////////////////////
//
// CPool::CPool
//
// Figure out the number of contained objects per block based on the requested
// approximate block size. Initialize the free list to contain one block's
// worth of objects.
//
//
template<class contained> CPool<contained>::CPool(int nApproxBytesPerBlock)
{
// Figure out how many items per block and cheat if too small
//
nItemsPerBlock = nApproxBytesPerBlock / sizeof(CPoolNode);
if (nItemsPerBlock < MIN_ITEMS_PER_BLOCK)
{
nItemsPerBlock = MIN_ITEMS_PER_BLOCK;
}
nAllocatedBlocks = 0;
pAllocatedBlocks = NULL;
nFreeList = 0;
pFreeList = NULL;
// Fill up with some items ahead of time
//
RefillFreeList();
}
///////////////////////////////////////////////////////////////////////////////
//
// CPool::~CPool
//
// Free up all allocated blocks. There should be no outstanding blocks
// allocated at this point.
//
//
template<class contained> CPool<contained>::~CPool()
{
#ifdef DBG
if (nFreeList < nAllocatedBlocks * nItemsPerBlock)
{
Trace(0, "CPool::~Cpool: Warning: free'ing with outstanding objects allocated.\n");
}
#endif
// Clean up all allocated blocks and contained objects.
//
while (pAllocatedBlocks)
{
CPoolBlock *pNext = pAllocatedBlocks->pNext;
delete[] pAllocatedBlocks->pObjects;
delete pAllocatedBlocks;
pAllocatedBlocks = pNext;
}
}
///////////////////////////////////////////////////////////////////////////////
//
// CPool::Alloc
//
// Attempt to allocate a contained object and return NULL if out of memory.
// If the free list is empty then allocate another block.
//
//
template<class contained> contained *CPool<contained>::Alloc()
{
if (pFreeList == NULL)
{
if (!RefillFreeList())
{
return false;
}
}
nFreeList--;
contained *pAlloc = (contained*)pFreeList;
pFreeList = pFreeList->pNext;
return pAlloc;
}
///////////////////////////////////////////////////////////////////////////////
//
// CPool::Free
//
// Return a contained object to the free list. In the debug version make sure
// the object was in fact allocated from this pool in the first place and that
// it isn't already in the free list.
//
//
template<class contained> void CPool<contained>::Free(contained *pToFree)
{
CPoolNode *pNode = (CPoolNode*)pToFree;
#ifdef DBG
if (!IsPoolNode(pNode))
{
Trace(0, "CPool::Free() Object %p is not a pool node; ignored.\n", pToFree);
return;
}
if (IsInFreeList(pNode))
{
Trace(0, "CPool::Free() Object %p is already in the free list; ignored.\n", pToFree);
return;
}
#endif
nFreeList++;
pNode->pNext = pFreeList;
pFreeList = pNode;
}
///////////////////////////////////////////////////////////////////////////////
//
// CPool::RefillFreeList
//
// Add one block's worth of contained objects to the free list, tracking the
// allocated memory so we can free it later.
//
//
template<class contained> bool CPool<contained>::RefillFreeList()
{
// Allocate a new block and the actual block of objects
//
CPoolBlock *pNewBlock = new CPoolBlock;
if (pNewBlock == NULL)
{
return false;
}
pNewBlock->pObjects = new CPoolNode[nItemsPerBlock];
if (pNewBlock->pObjects == NULL)
{
delete pNewBlock;
return false;
}
// Link the block and objects into the right places. First link the new block
// into the list of allocated blocks.
//
pNewBlock->pNext = pAllocatedBlocks;
pAllocatedBlocks = pNewBlock;
// Link all the contained object nodes into the free list.
//
CPoolNode *pFirstNode = &pNewBlock->pObjects[0];
CPoolNode *pLastNode = &pNewBlock->pObjects[nItemsPerBlock - 1];
for (CPoolNode *pNode = pFirstNode; pNode < pLastNode; pNode++)
{
pNode->pNext = pNode + 1;
}
pLastNode->pNext = pFreeList;
pFreeList = pFirstNode;
nFreeList += nItemsPerBlock;
nAllocatedBlocks++;
return true;
}
#ifdef DBG
///////////////////////////////////////////////////////////////////////////////
//
// CPool::IsPoolNode (debug)
//
// Verify that the passed pointer is a pointer to a pool node by walking the list
// of allocated blocks.
//
//
template<class contained> bool CPool<contained>::IsPoolNode(CPoolNode *pTest)
{
for (CPoolBlock *pBlock = pAllocatedBlocks; pBlock; pBlock = pBlock->pNext)
{
CPoolNode *pFirstNode = &pBlock->pObjects[0];
CPoolNode *pLastNode = &pBlock->pObjects[nItemsPerBlock - 1];
for (CPoolNode *pNode = pFirstNode; pNode <= pLastNode; pNode++)
{
if (pNode == pTest)
{
return true;
}
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
//
// CPool::IsInFreeList (debug)
//
// Verify that the passed pointer points to a node that is already in the free
// list.
//
//
template<class contained> bool CPool<contained>::IsInFreeList(CPoolNode *pTest)
{
for (CPoolNode *pNode = pFreeList; pNode; pNode = pNode->pNext)
{
if (pTest == pNode)
{
return true;
}
}
return false;
}
#endif // DBG
#endif // _TPOOL_H_