#include "cmPrefix.h" #include "cmGlobal.h" #include "cmRpt.h" #include "cmErr.h" #include "cmCtx.h" #include "cmMem.h" #include "cmMallocDebug.h" #include "cmStack.h" typedef struct cmStkBlk_str { char* base; unsigned curEleCnt; unsigned maxEleCnt; struct cmStkBlk_str* link; } cmStkBlk_t; typedef struct { cmErr_t err; unsigned expandBlkEleCnt; // count of ele's in all blocks after the first block unsigned cnt; // cur total count of elements in all blocks cmStkBlk_t* baseBlkPtr; // first block cmStkBlk_t* curBlkPtr; // current block - all blocks are always full except for the current block unsigned eleByteCnt; // bytes per element } cmStack_t; cmStackH_t cmStackNullHandle = cmSTATIC_NULL_HANDLE; cmStack_t* _cmStHandleToPtr( cmStackH_t h ) { cmStack_t* p = (cmStack_t*)h.h; assert( p != NULL ); return p; } cmStkBlk_t* _cmStkAllocBlk( cmStack_t* p, unsigned blkEleCnt ) { cmStkBlk_t* blkPtr; // if a next block has already been created (because an earlier stack was cleared (w/o release) if( p->curBlkPtr != NULL && p->curBlkPtr->link != NULL ) blkPtr = p->curBlkPtr->link; else { // ... otherwise allocate a new next block char* cp = cmMemAllocZ(char,sizeof(cmStkBlk_t) + p->eleByteCnt * blkEleCnt ); blkPtr = (cmStkBlk_t*)cp; blkPtr->base = cp + sizeof(cmStkBlk_t); blkPtr->curEleCnt = 0; blkPtr->maxEleCnt = blkEleCnt; if( p->curBlkPtr != NULL ) p->curBlkPtr->link = blkPtr; } if( p->baseBlkPtr == NULL ) p->baseBlkPtr = blkPtr; p->curBlkPtr = blkPtr; return blkPtr; } cmStkBlk_t* _cmStackClear( cmStack_t* p, bool releaseFl ) { cmStkBlk_t* bp = p->baseBlkPtr; while( bp != NULL ) { cmStkBlk_t* nbp = bp->link; bp->curEleCnt = 0; if( releaseFl ) cmMemFree(bp); bp = nbp; } p->cnt = 0; if( releaseFl ) { p->curBlkPtr = NULL; p->baseBlkPtr = NULL; } else { p->curBlkPtr = p->baseBlkPtr; } return bp; } cmStRC_t _cmStackFree( cmStack_t* p ) { cmStRC_t rc = kOkStRC; _cmStackClear(p,true); cmMemFree(p); return rc; } cmStRC_t cmStackAlloc( cmCtx_t* ctx, cmStackH_t* hp, unsigned initCnt, unsigned expandCnt, unsigned eleByteCnt ) { cmStRC_t rc = kOkStRC; if((rc = cmStackFree(hp)) != kOkStRC ) return rc; cmStack_t* p = cmMemAllocZ(cmStack_t,1); cmErrSetup(&p->err,&ctx->rpt,"Stack"); p->expandBlkEleCnt = expandCnt; p->eleByteCnt = eleByteCnt; _cmStkAllocBlk(p,initCnt); // allocate the initial block hp->h = p; return rc; } cmStRC_t cmStackFree( cmStackH_t* hp ) { cmStRC_t rc = kOkStRC; if( hp==NULL || cmStackIsValid(*hp) == false ) return kOkStRC; cmStack_t* p = _cmStHandleToPtr(*hp); if((rc = _cmStackFree(p)) != kOkStRC ) goto errLabel; hp->h = NULL; errLabel: return rc; } cmStRC_t cmStackIsValid( cmStackH_t h ) { return h.h != NULL; } unsigned cmStackCount( cmStackH_t h ) { cmStack_t* p = _cmStHandleToPtr(h); return p->cnt; } void cmStackClear( cmStackH_t h, bool releaseFl ) { if( cmStackIsValid(h) == false ) return; cmStack_t* p = _cmStHandleToPtr(h); _cmStackClear(p,releaseFl); } cmStRC_t cmStackPush( cmStackH_t h, const void* data, unsigned dataEleCnt ) { cmStRC_t rc = kOkStRC; cmStack_t* p = _cmStHandleToPtr(h); while( dataEleCnt ) { if( p->curBlkPtr == NULL || p->curBlkPtr->curEleCnt >= p->curBlkPtr->maxEleCnt ) _cmStkAllocBlk(p, p->expandBlkEleCnt ); // calc the count of ele's to copy into this block unsigned en = cmMin(dataEleCnt, p->curBlkPtr->maxEleCnt - p->curBlkPtr->curEleCnt ); // calc the count of source bytes to copy into this block unsigned bn = en * p->eleByteCnt; // copy the source bytes into the block memcpy( p->curBlkPtr->base + (p->curBlkPtr->curEleCnt * p->eleByteCnt), data, bn ); p->curBlkPtr->curEleCnt += en; // incr block element count p->cnt += en; // incr the total element count data = ((char*)data) + bn; // incr the source pointer dataEleCnt -= en; // decr the source ele count } return rc; } // Return the block and local index of the element at index *idxPtr. cmStkBlk_t* _cmStBlockIndex( cmStack_t* p, unsigned* idxPtr ) { cmStkBlk_t* blkPtr; assert( p->baseBlkPtr != NULL); if( *idxPtr < p->baseBlkPtr->maxEleCnt ) return p->baseBlkPtr; *idxPtr -= p->baseBlkPtr->maxEleCnt; unsigned bi = *idxPtr / p->expandBlkEleCnt; unsigned i; blkPtr = p->baseBlkPtr->link; for(i=0; ilink; *idxPtr = *idxPtr - bi * p->expandBlkEleCnt; return blkPtr; } cmStRC_t cmStackPop( cmStackH_t h, unsigned eleCnt ) { cmStRC_t rc = kOkStRC; cmStack_t* p = _cmStHandleToPtr(h); if( p->cnt < eleCnt ) return cmErrMsg(&p->err,kUnderflowStRC,"Stack underflow. Cannot pop %i elements off stack with %i elements.", eleCnt, p->cnt ); unsigned idx = p->cnt - eleCnt; // index of the first element to remove cmStkBlk_t* blkPtr = _cmStBlockIndex(p, &idx ); // get blk and local index of new curBlkPtr blkPtr->curEleCnt = idx; // update the element cnt for the new curBlkPtr p->curBlkPtr = blkPtr; // set the new curBlkPtr blkPtr = blkPtr->link; // for each blk after the current block ... while( blkPtr != NULL ) { blkPtr->curEleCnt = 0; // ... set the block empty blkPtr = blkPtr->link; } p->cnt -= eleCnt; return rc; } cmStRC_t _cmStackSetGet( cmStack_t* p, unsigned index, char* data, unsigned dataEleCnt, bool setFl ) { cmStRC_t rc = kOkStRC; if( index + dataEleCnt > p->cnt ) return cmErrMsg(&p->err,kInvalidIdxStRC,"The element index range:%i to %i falls outside the current index range:%i to %i of the stack.",index,index+dataEleCnt-1,0,p->cnt-1); cmStkBlk_t* blkPtr = _cmStBlockIndex(p,&index); assert( blkPtr != NULL ); while( dataEleCnt ) { // calc the count of ele's to copy into this block unsigned en = cmMin(dataEleCnt, blkPtr->curEleCnt - index ); // calc the count of bytes in en elements unsigned bn = en * p->eleByteCnt; char* bp = blkPtr->base + (index * p->eleByteCnt); // copy the source bytes into the block memcpy( setFl ? bp:data, setFl ? data:bp, bn ); data = data + bn; // incr the source pointer dataEleCnt -= en; // decr the source ele count index = 0; // all blocks after the first use index=0 blkPtr = blkPtr->link; } return rc; } cmStRC_t cmStackSet( cmStackH_t h, unsigned index, const void* data, unsigned dataEleCnt ) { return _cmStackSetGet( _cmStHandleToPtr(h), index, (char*) data, dataEleCnt,true ); } cmStRC_t cmStackGetN( cmStackH_t h, unsigned index, void* data, unsigned dataEleCnt ) { return _cmStackSetGet( _cmStHandleToPtr(h), index, (char*)data, dataEleCnt, false ); } const void* cmStackGet( cmStackH_t h, unsigned index ) { cmStack_t* p = _cmStHandleToPtr(h); if( index >= p->cnt ) { cmErrMsg(&p->err,kInvalidIdxStRC,"The index %i is outside the valid range 0:%i.",index,p->cnt-1); return NULL; } cmStkBlk_t* blkPtr = _cmStBlockIndex(p,&index); assert( blkPtr != NULL ); return blkPtr->base + (index * p->eleByteCnt); } void* cmStackFlatten( cmStackH_t h ) { cmStack_t* p = _cmStHandleToPtr(h); cmStkBlk_t* bbp = p->baseBlkPtr; cmStkBlk_t* cbp = p->curBlkPtr; unsigned cnt = p->cnt; // pretend the stack is unallocated p->baseBlkPtr = NULL; p->curBlkPtr = NULL; // allocate a new initial block large enough to hold all the data cmStkBlk_t* nbp = _cmStkAllocBlk(p,cnt); cmStkBlk_t* bp = bbp; unsigned n = 0; // copy the existing stack values into the new initial block while( bp != NULL && bp->curEleCnt > 0 ) { unsigned bn = bp->curEleCnt * p->eleByteCnt; assert( n < cnt*p->eleByteCnt ); memcpy( nbp->base + n, bp->base, bn ); n += bn; bp = bp->link; } // restore the stack to it's original state, clear and release p->baseBlkPtr = bbp; p->curBlkPtr = cbp; _cmStackClear(p,true); // setup new stack with the new initial block // (the data is now linearly arranged in nbp->base) p->baseBlkPtr = nbp; p->curBlkPtr = nbp; p->cnt = cnt; return p->baseBlkPtr->base; } void cmStackTest( cmCtx_t* ctx ) { cmStackH_t h = cmStackNullHandle; int i; int v[20]; if( cmStackAlloc(ctx,&h,10,5,sizeof(int)) != kOkStRC ) goto errLabel; for(i=0; i<9; ++i) v[i] = i; cmStackPush(h,v,i); cmRptPrintf(&ctx->rpt,"count:%i %i\n",cmStackCount(h),i); for(; i<18; ++i) v[i] = i; cmStackPush(h,v,i); cmRptPrintf(&ctx->rpt,"count:%i %i\n",cmStackCount(h),i); cmStackPop(h,10); cmRptPrintf(&ctx->rpt,"count:%i %i\n",cmStackCount(h),i); cmStackPop(h,10); cmRptPrintf(&ctx->rpt,"count:%i %i\n",cmStackCount(h),i); cmStackPush(h,v,10); cmRptPrintf(&ctx->rpt,"count:%i %i\n",cmStackCount(h),i); for(i=0; irpt,"%i ",cmStackEle(h,int,i)); cmRptPrintf(&ctx->rpt,"\n"); int* ip = (int*)cmStackFlatten(h); for(i=0; irpt,"%i ",ip[i]); cmRptPrintf(&ctx->rpt,"\n"); errLabel: cmStackFree(&h); }