324 lines
8.2 KiB
C
324 lines
8.2 KiB
C
|
|
#include "lsmem.h"
|
|
#include "lsidefs.h"
|
|
#include "lsc.h"
|
|
#include "qheap.h"
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
struct qheap
|
|
{
|
|
#ifdef DEBUG
|
|
DWORD tag;
|
|
#endif
|
|
|
|
BYTE* pbFreeObj; /* List of free objects in storage */
|
|
BYTE** ppbAdditionalStorageList; /* List of additional storage (chunk)*/
|
|
|
|
POLS pols;
|
|
void* (WINAPI* pfnNewPtr)(POLS, DWORD);
|
|
void (WINAPI* pfnDisposePtr)(POLS, void*);
|
|
|
|
DWORD cbObj;
|
|
DWORD cbObjNoLink;
|
|
DWORD iobjChunk; /* number of elements in chunk */
|
|
|
|
BOOL fFlush; /* use flush and don't use destroy */
|
|
};
|
|
|
|
#define tagQHEAP Tag('Q','H','E','A')
|
|
#define FIsQHEAP(p) FHasTag(p,tagQHEAP)
|
|
|
|
|
|
#define SetNextPb(pb,pbNext) ( (*(BYTE**)(pb)) = (pbNext) )
|
|
#define PbGetNext(pbObj) ( *(BYTE**)(pbObj) )
|
|
|
|
#define PLinkFromClientMemory(pClient) (BYTE *) ((BYTE**)pClient - 1)
|
|
#define ClientMemoryFromPLink(pLink) (void *) ((BYTE**)pLink + 1)
|
|
|
|
|
|
#ifdef DEBUG
|
|
#define DebugMemset(a,b,c) if ((a) != NULL) memset(a,b,c); else
|
|
#else
|
|
#define DebugMemset(a,b,c) (void)(0)
|
|
#endif
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/* C R E A T E Q U I C K H E A P */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: CreateQuickHeap
|
|
%%Contact: igorzv
|
|
|
|
Creates a block of fixed-size objects which can be allocated and
|
|
deallocated with very little overhead. Once the heap is created,
|
|
allocation of up to the specified number of objects will not require
|
|
using the application's callback function.
|
|
----------------------------------------------------------------------------*/
|
|
PQHEAP CreateQuickHeap(PLSC plsc, DWORD iobjChunk, DWORD cbObj, BOOL fFlush)
|
|
{
|
|
DWORD cbStorage;
|
|
PQHEAP pqh;
|
|
BYTE* pbObj;
|
|
BYTE* pbNextObj;
|
|
DWORD iobj;
|
|
DWORD cbObjNoLink = cbObj;
|
|
BYTE** ppbChunk;
|
|
|
|
Assert(iobjChunk != 0 && cbObj != 0);
|
|
|
|
cbObj += sizeof(BYTE*);
|
|
cbStorage = cbObj * iobjChunk;
|
|
pqh = plsc->lscbk.pfnNewPtr(plsc->pols, sizeof(*pqh));
|
|
if (pqh == NULL)
|
|
return NULL;
|
|
|
|
ppbChunk = plsc->lscbk.pfnNewPtr(plsc->pols, sizeof(BYTE*) + cbStorage);
|
|
if (ppbChunk == NULL)
|
|
{
|
|
plsc->lscbk.pfnDisposePtr(plsc->pols, pqh);
|
|
return NULL;
|
|
}
|
|
pbObj = (BYTE*) (ppbChunk + 1);
|
|
|
|
#ifdef DEBUG
|
|
pqh->tag = tagQHEAP;
|
|
#endif
|
|
|
|
pqh->pbFreeObj = pbObj;
|
|
pqh->ppbAdditionalStorageList = ppbChunk;
|
|
pqh->pols = plsc->pols;
|
|
pqh->pfnNewPtr = plsc->lscbk.pfnNewPtr;
|
|
pqh->pfnDisposePtr = plsc->lscbk.pfnDisposePtr;
|
|
pqh->cbObj = cbObj;
|
|
pqh->cbObjNoLink = cbObjNoLink;
|
|
pqh->iobjChunk = iobjChunk;
|
|
pqh->fFlush = fFlush;
|
|
|
|
/* Loop iobjChunk-1 times to chain the nodes together, then terminate
|
|
* the chain outside the loop.
|
|
*/
|
|
for (iobj = 1; iobj < iobjChunk; iobj++)
|
|
{
|
|
pbNextObj = pbObj + cbObj;
|
|
SetNextPb(pbObj,pbNextObj);
|
|
pbObj = pbNextObj;
|
|
}
|
|
SetNextPb(pbObj,NULL);
|
|
|
|
/* terminate chain of chunks */
|
|
*ppbChunk = NULL;
|
|
return pqh;
|
|
}
|
|
|
|
|
|
/* D E S T R O Y Q U I C K H E A P */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: DestroyQuickHeap
|
|
%%Contact: igorzv
|
|
|
|
Destroys one of the blocks of fixed-size objects which was created by
|
|
CreateQuickHeap().
|
|
----------------------------------------------------------------------------*/
|
|
void DestroyQuickHeap(PQHEAP pqh)
|
|
{
|
|
BYTE** ppbChunk;
|
|
BYTE** ppbChunkPrev = NULL;
|
|
|
|
if (pqh)
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
BYTE* pbObj;
|
|
BYTE* pbNext;
|
|
DWORD cbStorage;
|
|
DWORD i;
|
|
|
|
|
|
Assert(FIsQHEAP(pqh));
|
|
|
|
/* check that everything is free */
|
|
/* mark free objects*/
|
|
for (pbObj = pqh->pbFreeObj; pbObj != NULL; pbObj = pbNext)
|
|
{
|
|
pbNext = PbGetNext(pbObj);
|
|
|
|
DebugMemset(pbObj, 0xe4, pqh->cbObj);
|
|
}
|
|
|
|
/* check that all objects are marked */
|
|
ppbChunk = pqh->ppbAdditionalStorageList;
|
|
Assert(ppbChunk != NULL);
|
|
cbStorage = pqh->cbObj * pqh->iobjChunk;
|
|
while (ppbChunk != NULL)
|
|
{
|
|
for (pbObj = (BYTE *)(ppbChunk + 1), i=0; i < cbStorage; pbObj++, i++)
|
|
{
|
|
AssertSz(*pbObj == 0xe4, "Heap object not freed");
|
|
}
|
|
ppbChunk = (BYTE**) *ppbChunk;
|
|
}
|
|
#endif
|
|
/* free all chunks */
|
|
ppbChunk = pqh->ppbAdditionalStorageList;
|
|
Assert(ppbChunk != NULL);
|
|
while (ppbChunk != NULL)
|
|
{
|
|
ppbChunkPrev = ppbChunk;
|
|
ppbChunk = (BYTE**) *ppbChunk;
|
|
pqh->pfnDisposePtr(pqh->pols, ppbChunkPrev);
|
|
}
|
|
/* free header */
|
|
pqh->pfnDisposePtr(pqh->pols, pqh);
|
|
}
|
|
}
|
|
|
|
|
|
/* P V N E W Q U I C K P R O C */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: PvNewQuickProc
|
|
%%Contact: igorzv
|
|
|
|
Allocates an object from one of the blocks of fixed-size objects which
|
|
was created by CreateQuickHeap(). If no preallocated objects are
|
|
available, the callback function memory management function will be
|
|
used to attempt to allocate additional memory.
|
|
|
|
This function should not be called directly. Instead, the PvNewQuick()
|
|
macro should be used in order to allow debug code to validate that the
|
|
heap contains objects of the expected size.
|
|
----------------------------------------------------------------------------*/
|
|
void* PvNewQuickProc(PQHEAP pqh)
|
|
{
|
|
BYTE* pbObj;
|
|
BYTE* pbNextObj;
|
|
BYTE** ppbChunk;
|
|
BYTE** ppbChunkPrev = NULL;
|
|
DWORD cbStorage;
|
|
DWORD iobj;
|
|
BYTE* pbObjLast = NULL;
|
|
|
|
|
|
Assert(FIsQHEAP(pqh));
|
|
|
|
if (pqh->pbFreeObj == NULL)
|
|
{
|
|
cbStorage = pqh->cbObj * pqh->iobjChunk;
|
|
ppbChunk = pqh->ppbAdditionalStorageList;
|
|
Assert(ppbChunk != NULL);
|
|
/* find last chunk in the list */
|
|
while (ppbChunk != NULL)
|
|
{
|
|
ppbChunkPrev = ppbChunk;
|
|
ppbChunk = (BYTE**) *ppbChunk;
|
|
}
|
|
|
|
/* allocate memory */
|
|
ppbChunk = pqh->pfnNewPtr(pqh->pols, sizeof(BYTE*) + cbStorage);
|
|
if (ppbChunk == NULL)
|
|
return NULL;
|
|
pbObj = (BYTE*) (ppbChunk + 1);
|
|
/* add chunk to the list */
|
|
*ppbChunkPrev = (BYTE *) ppbChunk;
|
|
|
|
/* terminate chain of chunks */
|
|
*ppbChunk = NULL;
|
|
|
|
/* add new objects to free list */
|
|
pqh->pbFreeObj = pbObj;
|
|
|
|
if (pqh->fFlush) /* to link all objects into a chain */
|
|
{
|
|
/* find last object in chain */
|
|
pbObjLast = (BYTE*) (ppbChunkPrev + 1);
|
|
pbObjLast += (pqh->iobjChunk - 1) * pqh->cbObj;
|
|
SetNextPb(pbObjLast,pbObj);
|
|
}
|
|
|
|
/* Loop iobjChunk-1 times to chain the nodes together, then terminate
|
|
* the chain outside the loop.
|
|
*/
|
|
for (iobj = 1; iobj < pqh->iobjChunk; iobj++)
|
|
{
|
|
pbNextObj = pbObj + pqh->cbObj;
|
|
SetNextPb(pbObj,pbNextObj);
|
|
pbObj = pbNextObj;
|
|
}
|
|
SetNextPb(pbObj,NULL);
|
|
}
|
|
|
|
pbObj = pqh->pbFreeObj;
|
|
Assert(pbObj != NULL);
|
|
pqh->pbFreeObj = PbGetNext(pbObj);
|
|
DebugMemset(ClientMemoryFromPLink(pbObj), 0xE8, pqh->cbObjNoLink);
|
|
|
|
return ClientMemoryFromPLink(pbObj);
|
|
}
|
|
|
|
|
|
/* D I S P O S E Q U I C K P V P R O C */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: DisposeQuickPvProc
|
|
%%Contact: igorzv
|
|
|
|
De-allocates an object which was allocated by PvNewQuickProc().
|
|
|
|
This function should not be called directly. Instead, the PvDisposeQuick
|
|
macro should be used in order to allow debug code to validate that the
|
|
heap contains objects of the expected size.
|
|
----------------------------------------------------------------------------*/
|
|
void DisposeQuickPvProc(PQHEAP pqh, void* pv)
|
|
{
|
|
BYTE* pbObj = PLinkFromClientMemory(pv);
|
|
|
|
Assert(FIsQHEAP(pqh));
|
|
Assert(!pqh->fFlush);
|
|
|
|
if (pbObj != NULL)
|
|
{
|
|
DebugMemset(pbObj, 0xE9, pqh->cbObjNoLink);
|
|
|
|
SetNextPb(pbObj, pqh->pbFreeObj);
|
|
pqh->pbFreeObj = pbObj;
|
|
}
|
|
}
|
|
|
|
/* F L U S H Q U I C K H E A P */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: FlushQuickHeap
|
|
%%Contact: igorzv
|
|
|
|
For a quck heap with a flush flag, returns all objects to the list of
|
|
free objects.
|
|
----------------------------------------------------------------------------*/
|
|
void FlushQuickHeap(PQHEAP pqh)
|
|
{
|
|
|
|
Assert(FIsQHEAP(pqh));
|
|
Assert(pqh->fFlush);
|
|
|
|
pqh->pbFreeObj = (BYTE*) (pqh->ppbAdditionalStorageList + 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
/* C B O B J Q U I C K */
|
|
/*----------------------------------------------------------------------------
|
|
%%Function: CbObjQuick
|
|
%%Contact: igorzv
|
|
|
|
Returns the size of the objects in this quick heap. Used by the
|
|
PvNewQuick() and PvDisposeQuick() macros to validate that the
|
|
heap contains objects of the expected size.
|
|
----------------------------------------------------------------------------*/
|
|
DWORD CbObjQuick(PQHEAP pqh)
|
|
{
|
|
Assert(FIsQHEAP(pqh));
|
|
return pqh->cbObjNoLink;
|
|
}
|
|
#endif
|