libcm/cmDList.c

685 行
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;
}