windows-nt/Source/XPSP1/NT/windows/richedit/lssrc/qheap.c
2020-09-26 16:20:57 +08:00

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