From 042b1173f508ff84373ecd5a50b17b10db5a759b Mon Sep 17 00:00:00 2001 From: Kevin Larke Date: Sun, 2 Aug 2015 15:07:59 -0700 Subject: [PATCH] cmDList.h/c/Tpl.h : Initial commit. --- cmDList.c | 612 +++++++++++++++++++++++++++++++++++++++++++++++++++ cmDList.h | 61 +++++ cmDListTpl.h | 122 ++++++++++ 3 files changed, 795 insertions(+) create mode 100644 cmDList.c create mode 100644 cmDList.h create mode 100644 cmDListTpl.h diff --git a/cmDList.c b/cmDList.c new file mode 100644 index 0000000..5795133 --- /dev/null +++ b/cmDList.c @@ -0,0 +1,612 @@ +#include "cmGlobal.h" +#include "cmRpt.h" +#include "cmErr.h" +#include "cmCtx.h" +#include "cmMem.h" +#include "cmMallocDebug.h" +#include "cmDList.h" + + +typedef struct cmDListRecd_str +{ + void* dV; + unsigned dN; + struct cmDListRecd_str* prev; + struct cmDListRecd_str* next; +} cmDListRecd_t; + +typedef struct cmDListIndexRecd_str +{ + cmDListRecd_t* r; + struct cmDListIndexRecd_str* prev; + struct cmDListIndexRecd_str* next; +} cmDListIndexRecd_t; + +typedef struct cmDListIndex_str +{ + unsigned id; + cmDListCmpFunc_t cmpFunc; + void* funcArg; + cmDListIndexFreeFunc_t freeFunc; + cmDListIndexRecd_t* first; + cmDListIndexRecd_t* last; + unsigned recdN; + struct cmDListIndex_str* link; +} cmDListIndex_t; + + +struct cmDList_str; + +typedef struct cmDListIter_str +{ + struct cmDList_str* p; + cmDListIndex_t* x; + cmDListIndexRecd_t* s; + struct cmDListIter_str* link; +} cmDListIter_t; + +typedef struct cmDList_str +{ + cmErr_t err; + cmDListRecd_t* first; + cmDListRecd_t* last; + unsigned recdN; + + cmDListIndex_t* indexes; + cmDListIter_t* iters; +} cmDList_t; + +cmDListH_t cmDListNullHandle = cmSTATIC_NULL_HANDLE; +cmDListIterH_t cmDListIterNullHandle = cmSTATIC_NULL_HANDLE; + +cmDList_t* _cmDListHandleToPtr( cmDListH_t h ) +{ + cmDList_t* p = (cmDList_t*)h.h; + assert( p!=NULL ); + return p; +} + +cmDListIndex_t* _cmDListIdToIndex( cmDList_t* p, unsigned indexId ) +{ + cmDListIndex_t* x = p->indexes; + for(; x!=NULL; x=x->link) + if( x->id == indexId ) + return x; + + return NULL; +} + +void _cmDListIndexAllocRecds( cmDListIndex_t* x, unsigned n ) +{ + unsigned i; + for(i=0; iprev = x->last; + + if( x->last != NULL ) + x->last->next = s; + else + { + assert( x->first == NULL ); + x->first = s; + } + + x->last = s; + } + + x->recdN += n; + +} + +void _cmDListIndexUpdate( cmDList_t* p, cmDListIndex_t* x ) +{ + cmDListIndexRecd_t* first = NULL; + cmDListIndexRecd_t* last = NULL; + cmDListIndexRecd_t* avail = x->first; + + // for each data recd + cmDListRecd_t* r = p->first; + for(; r!=NULL; r=r->next) + { + // get the next available index record + cmDListIndexRecd_t* a = avail; + + avail = avail->next; + + // The count of index records and data records should always be the same. + assert( a != NULL ); + a->r = r; + + cmDListIndexRecd_t* s = first; + + // for each index recd that has already been sorted + for(; s!=NULL; s=s->next) + if( x->cmpFunc( x->funcArg, r->dV, r->dN, s->r->dV, s->r->dN ) < 0 ) + { + // r is less than s->r + // insert 'a' prior to 's' in the index + + a->next = s; + a->prev = s->prev; + + // if 's' is not first + if( s->prev != NULL ) + s->prev->next = a; + else + { // 's' was first - now 'a' is first + assert( s == first ); + first = a; + } + + s->prev = a; + + break; + } + + // No records are greater than r or the index is empty - 'a' is last in the index. + if( s == NULL ) + { + // insert 'a' after 'last' + + // if the index is empty + if( last == NULL ) + { + first = a; + a->prev = NULL; + } + else // make 'a' last in the index + { + a->prev = last; + a->next = NULL; + assert( last->next == NULL ); + last->next = a; + } + + a->next = NULL; + last = a; + } + + + + } + + x->first = first; + x->last = last; +} + +void _cmDListRecdFree( cmDList_t* p, cmDListRecd_t* r ) +{ + if( r->prev != NULL ) + r->prev->next = r->next; + else + p->first = r->next; + + if( r->next != NULL ) + r->next->prev = r->prev; + else + p->last = r->prev; + + cmMemFree(r); +} + +// Unlink and free the index record s; +void _cmDListIndexRecdFree( cmDListIndex_t* x, cmDListIndexRecd_t* s ) +{ + if( s->prev != NULL ) + s->prev->next = s->next; + else + x->first = s->next; + + if( s->next != NULL ) + s->next->prev = s->prev; + else + x->last = s->prev; + + cmMemFree(s); +} + +// Unlink and release an index. +void _cmDListIndexFree( cmDList_t* p, cmDListIndex_t* x ) +{ + if( x == NULL ) + return; + + cmDListIndex_t* x0 = NULL; + cmDListIndex_t* x1 = p->indexes; + + // unlink 'x' from p->indexes + while( x1 != NULL ) + { + if( x1 == x ) + { + // x is the first index + if( x0 == NULL ) + { + assert( x1 = p->indexes ); + p->indexes = x->link; + } + else + { + x0->link = x1->link; + } + + break; + } + + x0 = x1; + x1 = x1->link; + } + + // 'x' must have been found + assert( x1 == x ); + + + // release each index record in 'x' + cmDListIndexRecd_t* s = x->first; + while( s != NULL ) + { + cmDListIndexRecd_t* ns = s->next; + cmMemFree(s); + s = ns; + } + + if( x->freeFunc != NULL ) + x->freeFunc(x->id,x->funcArg); + + cmMemFree(x); + +} + +// Unlink and release the iterator 'e'. +cmDlRC_t _cmDListIterFree( cmDListIter_t* e ) +{ + cmDList_t* p = e->p; + cmDListIter_t* e0 = NULL; + cmDListIter_t* e1 = p->iters; + + while( e1 != NULL ) + { + if( e1 == e ) + { + if( e0 == NULL ) + p->iters = e1->link; + else + e0->link = e1->link; + + cmMemFree(e0); + break; + } + + e0 = e1; + e1 = e1->link; + } + + if( e1 == NULL ) + return cmErrMsg(&p->err,kIterNotFoundDlRC,"The delete target iterator could not be found."); + + return kOkDlRC; +} + +cmDlRC_t _cmDListFree( cmDList_t* p ) +{ + cmDlRC_t rc = kOkDlRC; + + // release all indexes + while( p->indexes != NULL ) + _cmDListIndexFree(p,p->indexes); + + while( p->iters != NULL ) + _cmDListIterFree(p->iters); + + // release all data records + while( p->first != NULL ) + _cmDListRecdFree(p,p->first); + + cmMemFree(p); + return rc; +} + +cmDlRC_t _cmDListIndexAlloc( cmDList_t* p, unsigned indexId, cmDListCmpFunc_t func, void* funcArg ) +{ + cmDListIndex_t* x; + + if((x =_cmDListIdToIndex(p, indexId )) != NULL ) + return cmErrMsg(&p->err,kDuplicateIndexIdDlRC,"The indexId '%i' has already been used.",indexId); + + x = cmMemAllocZ(cmDListIndex_t,1); + + x->id = indexId; + x->cmpFunc = func; + x->funcArg = funcArg; + x->link = p->indexes; + p->indexes = x; + + + _cmDListIndexAllocRecds(x,p->recdN); + _cmDListIndexUpdate(p,x); + + return kOkDlRC; + +} + +cmDlRC_t cmDListAlloc( cmCtx_t* ctx, cmDListH_t* hp, cmDListCmpFunc_t func, void* funcArg ) +{ + cmDlRC_t rc = kOkDlRC; + + if((rc = cmDListFree(hp)) != kOkDlRC ) + return rc; + + cmDList_t* p = cmMemAllocZ(cmDList_t,1); + cmErrSetup(&p->err,&ctx->rpt,"cmDList"); + + if( func!=NULL ) + if((rc = _cmDListIndexAlloc(p,0,func,funcArg)) != kOkDlRC ) + goto errLabel; + + hp->h = p; + + errLabel: + if( rc != kOkDlRC ) + _cmDListFree(p); + + return rc; +} + +cmDlRC_t cmDListFree( cmDListH_t* hp ) +{ + cmDlRC_t rc; + + if( hp==NULL || cmDListIsValid(*hp)==false) + return kOkDlRC; + + cmDList_t* p = _cmDListHandleToPtr(*hp); + + if((rc = _cmDListFree(p)) != kOkDlRC ) + return rc; + + hp->h = NULL; + + return rc; +} + +bool cmDListIsValid( cmDListH_t h ) +{ return h.h != NULL; } + +cmDlRC_t cmDListInsert( cmDListH_t h, const void* recd, unsigned recdByteN ) +{ + cmDList_t* p = _cmDListHandleToPtr(h); + char* vp = cmMemAllocZ(char,sizeof(cmDListRecd_t) + recdByteN ); + cmDListRecd_t* r = (cmDListRecd_t*)vp; + + r->dV = r + 1; + r->dN = recdByteN; + memcpy( r->dV, recd, recdByteN ); + + // Add records at the end of the list. + + // If the list is not empty + if( p->last != NULL ) + p->last->next = r; + else + { + // The list was empty + assert( p->first == NULL ); + p->first = r; + } + + r->prev = p->last; + r->next = NULL; + p->last = r; + p->recdN += 1; + + // update the indexes + cmDListIndex_t* x = p->indexes; + for(; x!=NULL; x=x->link) + { + _cmDListIndexAllocRecds(x,1); + _cmDListIndexUpdate(p,x); + } + + return kOkDlRC; +} + +cmDlRC_t cmDListDelete( cmDListH_t h, const void* recd ) +{ + cmDList_t* p = _cmDListHandleToPtr(h); + cmDListIndex_t* x = p->indexes; + cmDListIndexRecd_t* s = NULL; + cmDListRecd_t* r = NULL; + + for(; x!=NULL; x=x->link) + { + for(s=x->first; s!=NULL; s=s->next) + if( s->r->dV == recd ) + { + if( r == NULL ) + r = s->r; + else + { + // the same data record should be found for all indexes + assert( s->r == r ); + } + + _cmDListIndexRecdFree(x,s); + + break; + } + + if( r == NULL ) + return cmErrMsg(&p->err,kDataRecdNotFoundDlRC,"The delete target data record could not be found."); + + // if the indexes are valid then the recd should always be found + assert( s!=NULL ); + } + + // release the data record + _cmDListRecdFree(p,r); + + return kOkDlRC; + +} + +cmDlRC_t cmDListIndexAlloc( cmDListH_t h, unsigned indexId, cmDListCmpFunc_t func, void* funcArg ) +{ + cmDList_t* p = _cmDListHandleToPtr(h); + return _cmDListIndexAlloc(p,indexId,func,funcArg); +} + +cmDlRC_t cmDListIndexFree( cmDListH_t h, unsigned indexId ) +{ + cmDList_t* p = _cmDListHandleToPtr(h); + cmDListIndex_t* x; + if((x = _cmDListIdToIndex(p,indexId)) == NULL ) + return cmErrMsg(&p->err,kInvalidIndexDlRC,"The indexId '%i' could not be found.",indexId); + + _cmDListIndexFree(p,x); + + return kOkDlRC; +} + +cmDlRC_t cmDListIndexSetFreeFunc(cmDListH_t h, unsigned indexId, cmDListIndexFreeFunc_t func ) +{ + cmDList_t* p = _cmDListHandleToPtr(h); + cmDListIndex_t* x; + if((x = _cmDListIdToIndex(p,indexId)) == NULL ) + return cmErrMsg(&p->err,kInvalidIndexDlRC,"The indexId '%i' could not be found.",indexId); + + x->freeFunc = func; + return kOkDlRC; +} + + +cmDListIter_t* _cmDListIterHandleToPtr( cmDListIterH_t h ) +{ + cmDListIter_t* e = (cmDListIter_t*)h.h; + assert(e != NULL ); + return e; +} + +cmDlRC_t cmDListIterAlloc( cmDListH_t h, cmDListIterH_t* iHp, unsigned indexId ) +{ + cmDlRC_t rc = kOkDlRC; + cmDList_t* p = _cmDListHandleToPtr(h); + cmDListIndex_t* x; + + if((rc = cmDListIterFree(iHp)) != kOkDlRC ) + return rc; + + if((x = _cmDListIdToIndex(p, indexId)) == NULL ) + return cmErrMsg(&p->err,kInvalidIndexDlRC,"The indexId '%i' could not be found.",indexId); + + cmDListIter_t* e = cmMemAllocZ(cmDListIter_t,1); + + e->p = p; + e->x = x; + e->s = x->first; + e->link = p->iters; + p->iters = e; + + iHp->h = e; + + return rc; + +} + +cmDlRC_t cmDListIterFree( cmDListIterH_t* iHp ) +{ + cmDlRC_t rc; + + if( iHp==NULL || cmDListIterIsValid(*iHp)==false ) + return kOkDlRC; + + cmDListIter_t* e = _cmDListIterHandleToPtr(*iHp); + + if(( rc = _cmDListIterFree( e )) != kOkDlRC ) + return rc; + + iHp->h = NULL; + + return rc; +} + +bool cmDListIterIsValid( cmDListIterH_t iH ) +{ return iH.h != NULL; } + + +cmDlRC_t cmDListIterSeekBegin( cmDListIterH_t iH ) +{ + cmDListIter_t* e = _cmDListIterHandleToPtr(iH); + e->s = e->x->first; + return kOkDlRC; +} + +cmDlRC_t cmDListIterSeekLast( cmDListIterH_t iH ) +{ + cmDListIter_t* e = _cmDListIterHandleToPtr(iH); + e->s = e->x->last; + return kOkDlRC; +} + +const void* _cmDListIterGet( cmDListIter_t* e, unsigned* recdByteNRef ) +{ + if( e->s == NULL ) + { + if( recdByteNRef != NULL ) + *recdByteNRef = 0; + return NULL; + } + + if( recdByteNRef != NULL ) + *recdByteNRef = e->s->r->dN; + + return e->s->r->dN==0 ? NULL : e->s->r->dV; +} + +const void* cmDListIterGet( cmDListIterH_t iH, unsigned* recdByteNRef ) +{ + cmDListIter_t* e = _cmDListIterHandleToPtr(iH); + + return _cmDListIterGet(e,recdByteNRef); +} + +const void* cmDListIterPrev( cmDListIterH_t iH, unsigned* recdByteNRef ) +{ + cmDListIter_t* e = _cmDListIterHandleToPtr(iH); + const void* rv = _cmDListIterGet(e,recdByteNRef); + + if( e->s != NULL ) + e->s = e->s->prev; + + return rv; +} + +const void* cmDListIterNext( cmDListIterH_t iH, unsigned* recdByteNRef ) +{ + cmDListIter_t* e = _cmDListIterHandleToPtr(iH); + const void* rv = _cmDListIterGet(e,recdByteNRef); + + if( e->s != NULL ) + e->s = e->s->next; + + return rv; +} + +const void* cmDListIterFind( cmDListIterH_t iH, const void* key, unsigned keyN, unsigned* recdByteNRef) +{ + cmDListIter_t* e = _cmDListIterHandleToPtr(iH); + + cmDListIndexRecd_t* s = e->s; + + for(; s!=NULL; s=s->next) + if( e->x->cmpFunc( e->x->funcArg, s->r->dV, s->r->dN, key, keyN ) == 0 ) + { + e->s = s; + return _cmDListIterGet(e,recdByteNRef); + } + + if( recdByteNRef != NULL ) + *recdByteNRef = 0; + + return NULL; +} diff --git a/cmDList.h b/cmDList.h new file mode 100644 index 0000000..97f22d1 --- /dev/null +++ b/cmDList.h @@ -0,0 +1,61 @@ + +#ifndef cmDList_h +#define cmDList_h + +#ifdef __cplusplus +extern "C" { +#endif + + enum + { + kOkDlRC = cmOkRC, + kDuplicateIndexIdDlRC, + kInvalidIndexDlRC, + kIterNotFoundDlRC, + kDataRecdNotFoundDlRC, + + }; + + typedef unsigned cmDlRC_t; + typedef cmHandle_t cmDListH_t; + typedef cmHandle_t cmDListIterH_t; + + extern cmDListH_t cmDListNullHandle; + extern cmDListIterH_t cmDListIterNullHandle; + + // Return < 0 if v0 < v1 + // == 0 if v0 == v1 + // > 0 if v0 > v1 + typedef int (*cmDListCmpFunc_t)( void* arg, const void* v0, unsigned v0N, const void* v1, unsigned v1N ); + + typedef void (*cmDListIndexFreeFunc_t)( unsigned indexId, void* arg ); + + // If 'f' is not NULL then a default index with an indexId==0 will be automatically created. + cmDlRC_t cmDListAlloc( cmCtx_t* ctx, cmDListH_t* hp, cmDListCmpFunc_t f, void* farg ); + cmDlRC_t cmDListFree( cmDListH_t* hp ); + bool cmDListIsValid( cmDListH_t h ); + cmDlRC_t cmDListInsert( cmDListH_t h, const void* recd, unsigned recdByteN ); + cmDlRC_t cmDListDelete( cmDListH_t h, const void* recd ); + + + cmDlRC_t cmDListIndexAlloc( cmDListH_t h, unsigned indexId, cmDListCmpFunc_t f, void* farg ); + cmDlRC_t cmDListIndexFree( cmDListH_t h, unsigned indexId ); + cmDlRC_t cmDListIndexSetFreeFunc(cmDListH_t h, unsigned indexId, cmDListIndexFreeFunc_t func ); + + + cmDlRC_t cmDListIterAlloc( cmDListH_t h, cmDListIterH_t* iHp, unsigned indexId ); + cmDlRC_t cmDListIterFree( cmDListIterH_t* iHp ); + bool cmDListIterIsValid( cmDListIterH_t iH ); + cmDlRC_t cmDListIterSeekBegin( cmDListIterH_t iH ); + cmDlRC_t cmDListIterSeekEnd( cmDListIterH_t iH ); + const void* cmDListIterGet( cmDListIterH_t iH, unsigned* recdByteNRef ); + const void* cmDListIterPrev( cmDListIterH_t iH, unsigned* recdByteNRef ); + const void* cmDListIterNext( cmDListIterH_t iH, unsigned* recdByteNRef ); + const void* cmDListIterFind( cmDListIterH_t iH, const void* key, unsigned keyN, unsigned* recdByteNRef); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cmDListTpl.h b/cmDListTpl.h new file mode 100644 index 0000000..a87ad93 --- /dev/null +++ b/cmDListTpl.h @@ -0,0 +1,122 @@ + +// The following two macros must be defined prior to including this code: +// #define cmSFX(a) a##_MySuffix +// #define cmTYPE My_Type + +// Also define cmGEN_HDR to generate the .h code and/or +// cmGEN_CODE to generate the .c code + +#ifdef cmGEN_HDR + +typedef int (*cmSFX(cmDListFunc))( void* arg, const cmTYPE* v0, const cmTYPE* v1 ); + +cmDlRC_t cmSFX(cmDListAlloc)( cmCtx_t* ctx, cmDListH_t* hp, cmSFX(cmDListFunc) func, void* funcArg ); + +cmDlRC_t cmSFX(cmDListInsert)( cmDListH_t h, const cmTYPE* recd ); +cmDlRC_t cmSFX(cmDListDelete)( cmDListH_t h, const cmTYPE* recd ); + +cmDlRC_t cmSFX(cmDListAllocIndex)( cmDListH_t h, unsigned indexId, cmSFX(cmDListFunc) f, void* farg ); + +const cmTYPE* cmSFX(cmDListIterGet)( cmDListIterH_t iH ); +const cmTYPE* cmSFX(cmDListIterPrev)( cmDListIterH_t iH ); +const cmTYPE* cmSFX(cmDListIterNext)( cmDListIterH_t iH ); +const cmTYPE* cmSFX(cmDListIterFind)( cmDListIterH_t iH, const cmTYPE* key); + +#endif // cmGEN_HDR + + +#ifdef cmGEN_CODE + +typedef struct +{ + cmSFX(cmDListFunc) func; + void* funcArg; +} cmSFX(_cmDListArg); + +// This function is called when the index identified by indexId is about to be deleted. +// It is used to cleanup the arg record created by cmSFX(cmDListIndexAlloc()). +void cmSFX(_cmDListIndexOnFree)( unsigned indexId, void* arg ) +{ + cmMemFree(arg); +} + +// Proxy function used to cast generic compare function to the user defined compare function. +int cmSFX(_cmDListCmp)( void* arg, const void* v0, unsigned vn0, const void* v1, unsigned vn1 ) +{ + assert(vn0==vn1 && sizeof(cmTYPE)==vn0); + cmSFX(_cmDListArg)* a = (cmSFX(_cmDListArg)*)arg; + return a->func(a->funcArg,(const cmTYPE*)v0,(const cmTYPE*)v1); +} + +cmDlRC_t cmSFX(cmDListAlloc)( cmCtx_t* ctx, cmDListH_t* hp, cmSFX(cmDListFunc) func, void* funcArg ) +{ + cmDlRC_t rc; + cmSFX(_cmDListArg)* a = NULL; + + if( func != NULL ) + { + // allocate a record to redirect the compare function callback + a = cmMemAllocZ(cmSFX(_cmDListArg),1); + a->func = func; + a->funcArg = funcArg; + } + + if((rc = cmDListAlloc(ctx,hp,cmSFX(_cmDListCmp),a)) != kOkDlRC ) + return rc; + + if( func != NULL ) + if((rc = cmDListIndexSetFreeFunc(*hp,0,cmSFX(_cmDListIndexOnFree))) != kOkDlRC ) + cmDListFree(hp); + + return rc; +} + + +cmDlRC_t cmSFX(cmDListIndexAlloc)( cmDListH_t h, unsigned indexId, cmSFX(cmDListFunc) func, void* funcArg ) +{ + cmDlRC_t rc; + + // allocate a record to redirect the compare function callback + cmSFX(_cmDListArg)* a = cmMemAllocZ(cmSFX(_cmDListArg),1); + a->func = func; + a->funcArg = funcArg; + + // allocate the index + if((rc = cmDListIndexAlloc(h,indexId,cmSFX(_cmDListCmp),a)) != kOkDlRC ) + { + cmMemFree(a); + goto errLabel; + } + + // set the index clean up handler + if((rc = cmDListIndexSetFreeFunc(h,indexId,cmSFX(_cmDListIndexOnFree))) != kOkDlRC ) + cmDListIndexFree(h,indexId); + + errLabel: + return rc; +} + +cmDlRC_t cmSFX(cmDListInsert)( cmDListH_t h, const cmTYPE* recd ) +{ return cmDListInsert(h,recd,sizeof(recd)); } + +cmDlRC_t cmSFX(cmDListDelete)( cmDListH_t h, const cmTYPE* recd ) +{ return cmDListDelete(h,recd); } + + +const cmTYPE* cmSFX(cmDListIterGet)( cmDListIterH_t iH ) +{ return (const cmTYPE*)cmDListIterGet(iH,NULL);} + +const cmTYPE* cmSFX(cmDListIterPrev)( cmDListIterH_t iH ) +{ return (const cmTYPE*)cmDListIterPrev(iH,NULL); } + +const cmTYPE* cmSFX(cmDListIterNext)( cmDListIterH_t iH ) +{ return (const cmTYPE*)cmDListIterNext(iH,NULL); } + +const cmTYPE* cmSFX(cmDListIterFind)( cmDListIterH_t iH, const cmTYPE* key) +{ return (const cmTYPE*)cmDListIterFind(iH,key,sizeof(cmTYPE),NULL); } + +#endif // cmGEN_CODE + + +#undef cmSFX +#undef cmTYPE