383 wiersze
10 KiB
C
383 wiersze
10 KiB
C
#include "cmPrefix.h"
|
|
#include "cmGlobal.h"
|
|
#include "cmRpt.h"
|
|
#include "cmErr.h"
|
|
#include "cmCtx.h"
|
|
#include "cmMem.h"
|
|
#include "cmLinkedHeap.h"
|
|
#include "cmMallocDebug.h"
|
|
|
|
typedef struct cmLhBlock_str
|
|
{
|
|
char* basePtr; // base of block
|
|
char* nextPtr; // next avail location in block
|
|
char* endPtr; // one past end of block
|
|
struct cmLhBlock_str* prevBlkPtr; // backward block link
|
|
struct cmLhBlock_str* nextBlkPtr; // forward block link
|
|
unsigned freeCnt; // track orphaned space that is unavailable for reuse
|
|
} cmLhBlock_t;
|
|
|
|
typedef struct
|
|
{
|
|
unsigned dfltBlockByteCnt; // size of each block in chain
|
|
cmLhBlock_t* first; // first block in chain
|
|
cmLhBlock_t* last; // last block in chain
|
|
cmMmH_t mmH;
|
|
} cmLHeap_t;
|
|
|
|
cmLHeapH_t cmLHeapNullHandle = { NULL };
|
|
|
|
cmLHeap_t* _cmLHeapHandleToPtr( cmLHeapH_t h )
|
|
{
|
|
cmLHeap_t* lhp = (cmLHeap_t*)h.h;
|
|
assert( lhp != NULL);
|
|
return lhp;
|
|
}
|
|
|
|
cmLhBlock_t* _cmLHeapAllocBlock( cmLHeap_t* lhp, unsigned blockByteCnt )
|
|
{
|
|
// note: the entire block (control record and data space) is allocated
|
|
// as one contiguous chunk of memory.
|
|
|
|
cmLhBlock_t* lbp = (cmLhBlock_t*)cmMemMallocZ( sizeof(cmLhBlock_t) + blockByteCnt );
|
|
|
|
// setup the new block
|
|
lbp->basePtr = (char*)(lbp+1);
|
|
lbp->nextPtr = lbp->basePtr;
|
|
lbp->endPtr = lbp->basePtr + blockByteCnt;
|
|
lbp->prevBlkPtr = lhp->last;
|
|
lbp->nextBlkPtr = NULL;
|
|
|
|
// link the new block into the chain
|
|
if( lhp->last != NULL )
|
|
lhp->last->nextBlkPtr = lbp;
|
|
else
|
|
{
|
|
assert( lhp->first == NULL );
|
|
lhp->first = lbp;
|
|
}
|
|
|
|
lhp->last = lbp;
|
|
|
|
return lbp;
|
|
}
|
|
|
|
void* _cmLHeapAlloc( cmLHeap_t* lhp, unsigned dataByteCnt )
|
|
{
|
|
void* retPtr = NULL;
|
|
cmLhBlock_t* lbp = lhp->last;
|
|
unsigned allocByteCnt = dataByteCnt + sizeof(unsigned);
|
|
|
|
// go backwards down the chain looking for the first block with enough
|
|
// free space to hold the allocation (we go backward under the assumption
|
|
// that the last block is most likely to have available space)
|
|
while( (lbp != NULL) && ((lbp->endPtr - lbp->nextPtr) < allocByteCnt) )
|
|
lbp = lbp->prevBlkPtr;
|
|
|
|
// no space large enough to provide the requested memory - allocate a new block
|
|
if( lbp == NULL )
|
|
lbp = _cmLHeapAllocBlock(lhp, cmMax( lhp->dfltBlockByteCnt, allocByteCnt ));
|
|
|
|
assert( lbp != NULL );
|
|
|
|
// store the sizeof the allocation at the beginning of the allocated block
|
|
*(unsigned*)lbp->nextPtr = allocByteCnt;
|
|
|
|
// the returned block ptr begins just after the block size
|
|
retPtr = lbp->nextPtr + sizeof(unsigned);
|
|
|
|
|
|
lbp->nextPtr += allocByteCnt;
|
|
|
|
return retPtr;
|
|
}
|
|
|
|
|
|
void* _cmLHeapAllocCb(void* funcArgPtr, unsigned byteCnt)
|
|
{
|
|
cmLHeap_t* p = (cmLHeap_t*)funcArgPtr;
|
|
assert( p != NULL );
|
|
return _cmLHeapAlloc(p,byteCnt);
|
|
}
|
|
|
|
cmLhBlock_t* _cmLHeapPtrToBlock( cmLHeap_t* lhp, const void* dataPtr )
|
|
{
|
|
if( dataPtr == NULL )
|
|
return NULL;
|
|
|
|
cmLhBlock_t* lbp = lhp->first;
|
|
|
|
// locate the block containing the area to free
|
|
while( (lbp != NULL ) && (((char*)dataPtr < lbp->basePtr) || ((char*)dataPtr >= lbp->endPtr)))
|
|
lbp = lbp->nextBlkPtr;
|
|
|
|
return lbp;
|
|
}
|
|
|
|
bool _cmLHeapFree( cmLHeap_t* lhp, void* dataPtr )
|
|
{
|
|
if( dataPtr == NULL )
|
|
return true;
|
|
|
|
/*
|
|
cmLhBlock_t* lbp = lhp->first;
|
|
|
|
// locate the block containing the area to free
|
|
while( (lbp != NULL ) && (((char*)dataPtr < lbp->basePtr) || ((char*)dataPtr >= lbp->endPtr)))
|
|
lbp = lbp->nextBlkPtr;
|
|
*/
|
|
|
|
cmLhBlock_t* lbp = _cmLHeapPtrToBlock(lhp,dataPtr);
|
|
|
|
// the pointer must be in one of the blocks
|
|
if( lbp == NULL )
|
|
return false;
|
|
|
|
unsigned* allocPtr = ((unsigned*)dataPtr)-1;
|
|
unsigned dataByteCnt = *allocPtr - sizeof(unsigned);
|
|
|
|
// the data to be freed is at the end of the blocks space ...
|
|
if( dataPtr == lbp->nextPtr - dataByteCnt )
|
|
lbp->nextPtr = (char*)allocPtr; // ... then make it the space to alloc
|
|
else
|
|
lbp->freeCnt += *allocPtr; // ... otherwise increase the free count
|
|
// freeCnt tracks unused space that is not at the end of the block and therefore cannot be reused.
|
|
|
|
// if all the space for this block has been freed then the
|
|
// next space to allocate must be at the base
|
|
if( lbp->freeCnt == lbp->endPtr - lbp->basePtr )
|
|
lbp->nextPtr = lbp->basePtr;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
bool _cmLHeapFreeCb(void* funcArgPtr, void* ptr)
|
|
{
|
|
cmLHeap_t* lhp = (cmLHeap_t*)funcArgPtr;
|
|
return _cmLHeapFree(lhp,ptr);
|
|
}
|
|
|
|
|
|
cmLHeapH_t cmLHeapCreate( unsigned dfltBlockByteCnt, cmCtx_t* ctx )
|
|
{
|
|
cmLHeapH_t h;
|
|
cmLHeap_t* lhp = cmMemAllocZ( cmLHeap_t, 1 );
|
|
|
|
// We are not going to defer freeing each allocation because it will result in using
|
|
// a lot of memory. Commenting out this line however would result in
|
|
// checking all allocations for corruption during cmLHeapDestroy().
|
|
// This may be a good way to hunt down subtle memory corruption problems.
|
|
// See cmLHeapDestroy() and cmLHeapClear() for kDeferFreeMMFl related
|
|
// items.
|
|
unsigned mmFlags = cmClrFlag(ctx->mmFlags,kDeferFreeMmFl);
|
|
|
|
if( cmMmInitialize(&lhp->mmH,_cmLHeapAllocCb,_cmLHeapFreeCb,lhp,ctx->guardByteCnt,ctx->alignByteCnt,mmFlags,&ctx->rpt) != kOkMmRC )
|
|
return cmLHeapNullHandle;
|
|
|
|
lhp->dfltBlockByteCnt = dfltBlockByteCnt;
|
|
|
|
_cmLHeapAllocBlock( lhp, lhp->dfltBlockByteCnt );
|
|
|
|
h.h = lhp;
|
|
return h;
|
|
}
|
|
|
|
void cmLHeapDestroy( cmLHeapH_t* hp )
|
|
{
|
|
if( hp==NULL || hp->h == NULL )
|
|
return;
|
|
|
|
cmLHeap_t* lhp = _cmLHeapHandleToPtr(*hp);
|
|
|
|
// check for corruption
|
|
if( cmIsFlag(cmMmInitializeFlags(lhp->mmH),kDeferFreeMmFl))
|
|
cmMmReport(lhp->mmH, kSuppressSummaryMmFl | kIgnoreLeaksMmFl | kIgnoreNormalMmFl );
|
|
|
|
cmMmFinalize(&lhp->mmH);
|
|
|
|
cmLhBlock_t* lbp = lhp->first;
|
|
while( lbp != NULL )
|
|
{
|
|
cmLhBlock_t* t = lbp;
|
|
|
|
lbp = lbp->nextBlkPtr;
|
|
|
|
cmMemFree(t);
|
|
}
|
|
|
|
cmMemPtrFree(&hp->h);
|
|
|
|
}
|
|
|
|
void* cmLHeapAllocate(cmLHeapH_t h, void* orgDataPtr, unsigned eleCnt, unsigned eleByteCnt, unsigned flags, const char* fileStr, const char* funcStr, unsigned fileLine )
|
|
{ return cmMmAllocate(_cmLHeapHandleToPtr(h)->mmH,orgDataPtr,eleCnt,eleByteCnt,flags,fileStr,funcStr,fileLine); }
|
|
|
|
cmChar_t* cmLHeapAllocStr(cmLHeapH_t h, void* orgDataPtr, const cmChar_t* str, unsigned charCnt, unsigned flags, const char* fileStr, const char* funcStr, unsigned fileLine )
|
|
{
|
|
if( str == NULL )
|
|
return NULL;
|
|
|
|
unsigned n = charCnt + 1;
|
|
cmChar_t* cp = cmLHeapAllocate(h, orgDataPtr, n, sizeof(cmChar_t), flags, fileStr, funcStr, fileLine );
|
|
strncpy(cp,str,n);
|
|
cp[n-1] = 0;
|
|
return cp;
|
|
}
|
|
|
|
void cmLHeapFree( cmLHeapH_t h, void* dataPtr )
|
|
{
|
|
cmLHeap_t* lhp = _cmLHeapHandleToPtr(h);
|
|
cmMmFree(lhp->mmH,dataPtr);
|
|
}
|
|
|
|
void cmLHeapFreeDebug( cmLHeapH_t h, void* dataPtr, const char* fileName, const char* funcName, unsigned fileLine )
|
|
{
|
|
cmLHeap_t* lhp = _cmLHeapHandleToPtr(h);
|
|
cmMmFreeDebug(lhp->mmH,dataPtr,fileName,funcName,fileLine);
|
|
}
|
|
|
|
|
|
void cmLHeapFreePtr( cmLHeapH_t h, void** ptrPtr )
|
|
{
|
|
assert( ptrPtr != NULL );
|
|
cmLHeapFree(h,*ptrPtr);
|
|
*ptrPtr = NULL;
|
|
}
|
|
|
|
void cmLHeapFreePtrDebug( cmLHeapH_t h, void** ptrPtr, const char* fileName, const char* funcName, unsigned fileLine )
|
|
{
|
|
assert( ptrPtr != NULL );
|
|
cmLHeapFreeDebug(h,*ptrPtr,fileName,funcName,fileLine);
|
|
*ptrPtr = NULL;
|
|
}
|
|
|
|
|
|
unsigned cmLHeapDefaultBlockByteCount( cmLHeapH_t h )
|
|
{
|
|
cmLHeap_t* p = _cmLHeapHandleToPtr(h);
|
|
return p->dfltBlockByteCnt;
|
|
}
|
|
|
|
unsigned cmLHeapGuardByteCount( cmLHeapH_t h )
|
|
{
|
|
cmLHeap_t* p = _cmLHeapHandleToPtr(h);
|
|
return cmMmGuardByteCount( p->mmH );
|
|
}
|
|
|
|
unsigned cmLHeapAlignByteCount( cmLHeapH_t h )
|
|
{
|
|
cmLHeap_t* p = _cmLHeapHandleToPtr(h);
|
|
return cmMmAlignByteCount( p->mmH );
|
|
}
|
|
|
|
unsigned cmLHeapInitializeFlags( cmLHeapH_t h )
|
|
{
|
|
cmLHeap_t* p = _cmLHeapHandleToPtr(h);
|
|
return cmMmInitializeFlags( p->mmH );
|
|
}
|
|
|
|
bool cmLHeapIsValid( cmLHeapH_t h )
|
|
{ return h.h != NULL; }
|
|
|
|
void cmLHeapClear( cmLHeapH_t h, bool releaseFl )
|
|
{
|
|
cmLHeap_t* p = _cmLHeapHandleToPtr(h);
|
|
|
|
// If we are deferring freeing memory until the heap is destroyed
|
|
// then we cannot clear the block list in this function.
|
|
// If the block list was cleared here then later calls to _cmLHeapFreeCb()
|
|
// would fail because the pointers to the deferred allocations
|
|
// would no longer exist in any of the blocks.
|
|
if( cmIsFlag(cmMmInitializeFlags(p->mmH),kDeferFreeMmFl))
|
|
return;
|
|
|
|
cmLhBlock_t* bp = p->first;
|
|
while( bp != NULL )
|
|
{
|
|
bp->nextPtr = bp->basePtr;
|
|
bp->freeCnt = bp->endPtr - bp->basePtr;
|
|
|
|
cmLhBlock_t* nbp = bp->nextBlkPtr;
|
|
|
|
if( releaseFl )
|
|
cmMemFree(bp);
|
|
|
|
bp = nbp;
|
|
}
|
|
|
|
if( releaseFl )
|
|
{
|
|
p->first = NULL;
|
|
p->last = NULL;
|
|
}
|
|
}
|
|
|
|
bool cmLHeapIsPtrInHeap( cmLHeapH_t h, const void* ptr )
|
|
{
|
|
cmLHeap_t* lhp = _cmLHeapHandleToPtr(h);
|
|
return _cmLHeapPtrToBlock(lhp,ptr) != NULL;
|
|
}
|
|
|
|
cmMmRC_t cmLHeapReportErrors( cmLHeapH_t h, unsigned mmFlags )
|
|
{
|
|
cmLHeap_t* lhp = _cmLHeapHandleToPtr(h);
|
|
return cmMmReport(lhp->mmH, mmFlags );
|
|
}
|
|
|
|
void cmLHeapReport( cmLHeapH_t h )
|
|
{
|
|
cmLHeap_t* lhp = _cmLHeapHandleToPtr(h);
|
|
cmLhBlock_t* lbp = lhp->first;
|
|
unsigned i;
|
|
for(i=0; lbp != NULL; ++i )
|
|
{
|
|
printf("%u : %li available %i free\n", i, lbp->endPtr-lbp->nextPtr, lbp->freeCnt );
|
|
lbp = lbp->nextBlkPtr;
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
void cmLHeapTestPrint( void* userPtr, const char* text )
|
|
{
|
|
fputs(text,stdin);
|
|
}
|
|
|
|
void cmLHeapTest()
|
|
{
|
|
int i = 0;
|
|
int n = 5;
|
|
unsigned guardByteCnt = 8;
|
|
unsigned alignByteCnt = 16;
|
|
unsigned mmFlags = kTrackMmFl; // | kDeferFreeMmFl;
|
|
cmCtx_t ctx;
|
|
cmCtxSetup(&ctx,"Heap Test",cmLHeapTestPrint,cmLHeapTestPrint,NULL,guardByteCnt,alignByteCnt,mmFlags);
|
|
|
|
cmLHeapH_t h = cmLHeapCreate( 16, &ctx );
|
|
|
|
void* pArray[ n ];
|
|
|
|
cmLHeapReport(h);
|
|
|
|
for(i=0; i<n; ++i)
|
|
{
|
|
unsigned byteCnt = (i+1) * 2;
|
|
printf("Allocating:%li\n",byteCnt+sizeof(unsigned));
|
|
pArray[i] = cmLHeapAlloc(h,byteCnt);
|
|
|
|
cmLHeapReport(h);
|
|
}
|
|
|
|
for(i=n-1; i>=0; i-=2)
|
|
{
|
|
printf("Freeing:%li\n",((i+1) * 2) + sizeof(unsigned));
|
|
cmLHeapFree(h,pArray[i]);
|
|
cmLHeapReport(h);
|
|
}
|
|
|
|
cmLHeapReportErrors(h,0);
|
|
|
|
cmLHeapDestroy(&h);
|
|
}
|