685 lines
14 KiB
C
685 lines
14 KiB
C
#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; // pointer to the owner cmDList
|
|
cmDListIndex_t* x; // pointer to the index this iterator traverses
|
|
cmDListIndexRecd_t* s; // current record
|
|
struct cmDListIter_str* link; // p->iters list 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;
|
|
}
|
|
|
|
// Given an indexId return the associated index.
|
|
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;
|
|
}
|
|
|
|
// Allocate 'n' new index records for the index 'x'.
|
|
void _cmDListIndexAllocRecds( cmDListIndex_t* x, unsigned n )
|
|
{
|
|
unsigned i;
|
|
for(i=0; i<n; ++i)
|
|
{
|
|
cmDListIndexRecd_t* s = cmMemAllocZ(cmDListIndexRecd_t,1);
|
|
|
|
s->prev = 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 _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(e1);
|
|
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;
|
|
}
|
|
|
|
|
|
void _cmDListIndexUpdate( cmDList_t* p, cmDListIndex_t* x )
|
|
{
|
|
cmDListIndexRecd_t* first = NULL;
|
|
cmDListIndexRecd_t* last = NULL;
|
|
|
|
assert( x->recdN >= p->recdN );
|
|
|
|
// for each data recd
|
|
cmDListRecd_t* r = p->first;
|
|
for(; r!=NULL; r=r->next)
|
|
{
|
|
// get the next available index record
|
|
cmDListIndexRecd_t* a = x->first;
|
|
assert(a!=NULL);
|
|
x->first = x->first->next;
|
|
if( x->first != NULL )
|
|
x->first->prev = NULL;
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// release any index records that are not in use
|
|
while(x->first!=NULL)
|
|
{
|
|
_cmDListIndexRecdFree(x,x->first);
|
|
x->recdN -= 1;
|
|
}
|
|
|
|
assert( x->recdN == p->recdN );
|
|
|
|
// Invalidate all iterators which use index x.
|
|
cmDListIter_t* e = p->iters;
|
|
for(; e!=NULL; e=e->link)
|
|
if( e->x == x )
|
|
e->s = NULL;
|
|
|
|
x->first = first;
|
|
x->last = last;
|
|
}
|
|
|
|
|
|
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, bool resyncFl )
|
|
{
|
|
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 data record 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;
|
|
|
|
// add a record to each index
|
|
cmDListIndex_t* x = p->indexes;
|
|
for(; x!=NULL; x=x->link)
|
|
{
|
|
if( x->recdN < p->recdN )
|
|
_cmDListIndexAllocRecds(x,1);
|
|
|
|
assert( x->recdN >= p->recdN );
|
|
|
|
if( resyncFl )
|
|
_cmDListIndexUpdate(p,x);
|
|
}
|
|
|
|
|
|
return kOkDlRC;
|
|
}
|
|
|
|
cmDlRC_t cmDListDelete( cmDListH_t h, const void* recd, bool resyncFl )
|
|
{
|
|
cmDList_t* p = _cmDListHandleToPtr(h);
|
|
cmDListIndex_t* x = p->indexes;
|
|
cmDListIndexRecd_t* s = NULL;
|
|
cmDListRecd_t* r = NULL;
|
|
|
|
if( resyncFl==false )
|
|
{
|
|
r = p->first;
|
|
for(; r!=NULL; r=r->next)
|
|
if( r->dV == recd )
|
|
{
|
|
_cmDListRecdFree(p,r);
|
|
break;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// for each index
|
|
for(; x!=NULL; x=x->link)
|
|
{
|
|
// for each index recd
|
|
for(s=x->first; s!=NULL; s=s->next)
|
|
if( s->r->dV == recd ) // if this index recd points to the deletion target
|
|
{
|
|
// store a ptr to the data recd to be deleted
|
|
if( r == NULL )
|
|
r = s->r;
|
|
else
|
|
{
|
|
// the same data record should be found for all indexes
|
|
assert( s->r == r );
|
|
}
|
|
|
|
// free the index record
|
|
_cmDListIndexRecdFree(x,s);
|
|
|
|
break;
|
|
}
|
|
|
|
// if the deletion target was not found
|
|
if( r == NULL )
|
|
goto errLabel;
|
|
|
|
}
|
|
|
|
// advance any iterators that are pointing to the deleted record
|
|
cmDListIter_t* e = p->iters;
|
|
for(; e!=NULL; e=e->link)
|
|
if( e->s != NULL && e->s->r != NULL && e->s->r == r )
|
|
e->s = e->s->next;
|
|
|
|
// release the data record
|
|
_cmDListRecdFree(p,r);
|
|
|
|
}
|
|
|
|
errLabel:
|
|
if( r == NULL )
|
|
return cmErrMsg(&p->err,kDataRecdNotFoundDlRC,"The deletion target record could not be found.");
|
|
|
|
assert( p->recdN > 0 );
|
|
p->recdN -= 1;
|
|
|
|
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;
|
|
}
|
|
|
|
cmDlRC_t cmDListIndexUpdateAll( cmDListH_t h )
|
|
{
|
|
cmDList_t* p = _cmDListHandleToPtr(h);
|
|
cmDListIndex_t* x = p->indexes;
|
|
|
|
for(; x!=NULL; x=x->link)
|
|
_cmDListIndexUpdate(p,x);
|
|
|
|
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;
|
|
}
|
|
|
|
assert( e->s->r != 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;
|
|
}
|