mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-22 07:28:16 +01:00
tweak dllist to allow deletion mid-iteration
Eventually comms will use this thing I think
This commit is contained in:
parent
b5090bb825
commit
9437cc4ef9
5 changed files with 193 additions and 100 deletions
|
@ -35,6 +35,7 @@
|
|||
#include "knownplyr.h"
|
||||
#include "device.h"
|
||||
#include "nli.h"
|
||||
#include "dllist.h"
|
||||
|
||||
#define HEARTBEAT_NONE 0
|
||||
|
||||
|
@ -272,7 +273,6 @@ static void sendConnect( CommsCtxt* comms, XWEnv xwe
|
|||
#endif
|
||||
);
|
||||
|
||||
typedef enum {FEA_OK = 0x00, FEA_REMOVE = 0x01, FEA_EXIT = 0x02} ForEachAct;
|
||||
typedef ForEachAct (*EachMsgProc)( MsgQueueElem* elem, void* closure );
|
||||
static void forEachElem( CommsCtxt* comms, EachMsgProc proc, void* closure );
|
||||
|
||||
|
|
|
@ -21,38 +21,30 @@
|
|||
#include "dllist.h"
|
||||
#include "dutil.h" /* for NULL ??? */
|
||||
|
||||
void
|
||||
dll_map( DLHead* list, DLMapProc proc, void* closure )
|
||||
{
|
||||
for ( ; !!list; list = list->next ) {
|
||||
(*proc)( list, closure );
|
||||
}
|
||||
}
|
||||
|
||||
DLHead*
|
||||
dll_insert( DLHead* head, DLHead* node, DLCompProc proc )
|
||||
{
|
||||
DLHead* next;
|
||||
DLHead* prev = NULL;
|
||||
for ( next = head; !!next; next = next->next ) {
|
||||
for ( next = head; !!next; next = next->_next ) {
|
||||
if ( 0 <= (*proc)( next, node ) ) {
|
||||
break;
|
||||
}
|
||||
prev = next;
|
||||
}
|
||||
|
||||
node->prev = prev;
|
||||
node->next = next;
|
||||
node->_prev = prev;
|
||||
node->_next = next;
|
||||
|
||||
DLHead* newHead;
|
||||
if ( !!prev ) {
|
||||
newHead = head;
|
||||
prev->next = node;
|
||||
prev->_next = node;
|
||||
} else {
|
||||
newHead = node;
|
||||
}
|
||||
if ( !!next ) {
|
||||
next->prev = node;
|
||||
next->_prev = node;
|
||||
}
|
||||
XP_ASSERT( !!newHead );
|
||||
return newHead;
|
||||
|
@ -61,23 +53,61 @@ dll_insert( DLHead* head, DLHead* node, DLCompProc proc )
|
|||
DLHead*
|
||||
dll_remove( DLHead* list, DLHead* node )
|
||||
{
|
||||
XP_ASSERT( !list->_prev );
|
||||
DLHead* newHead = list;
|
||||
if ( list == node ) {
|
||||
newHead = list->next;
|
||||
newHead = list->_next;
|
||||
if ( !!newHead ) {
|
||||
newHead->prev = NULL;
|
||||
newHead->_prev = NULL;
|
||||
}
|
||||
} else {
|
||||
if ( !!node->prev ) {
|
||||
node->prev->next = node->next;
|
||||
if ( !!node->_prev ) {
|
||||
node->_prev->_next = node->_next;
|
||||
}
|
||||
if ( !!node->next ) {
|
||||
node->next->prev = node->prev;
|
||||
if ( !!node->_next ) {
|
||||
node->_next->_prev = node->_prev;
|
||||
}
|
||||
}
|
||||
return newHead;
|
||||
}
|
||||
|
||||
DLHead*
|
||||
dll_map( DLHead* list, DLMapProc mapProc, DLDisposeProc dispProc,
|
||||
void* closure )
|
||||
{
|
||||
DLHead* newHead = list;
|
||||
while ( !!list ) {
|
||||
DLHead* next = list->_next;
|
||||
ForEachAct fea = (*mapProc)( list, closure );
|
||||
if ( 0 != (FEA_REMOVE & fea) ) {
|
||||
DLHead* victim = list;
|
||||
next = victim->_prev;
|
||||
|
||||
if ( victim == newHead ) {
|
||||
newHead = next;
|
||||
}
|
||||
|
||||
if ( !!victim->_prev ) {
|
||||
victim->_prev->_next = victim->_next;
|
||||
}
|
||||
if ( !!victim->_next ) {
|
||||
victim->_next->_prev = victim->_prev;
|
||||
}
|
||||
|
||||
if ( !!dispProc ) {
|
||||
(*dispProc)( list, closure );
|
||||
}
|
||||
}
|
||||
if ( 0 != (FEA_EXIT & fea) ) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
list = next;
|
||||
}
|
||||
done:
|
||||
return newHead;
|
||||
}
|
||||
|
||||
DLHead*
|
||||
dll_sort( DLHead* list, DLCompProc proc )
|
||||
{
|
||||
|
|
|
@ -25,18 +25,22 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
typedef struct DLHead {
|
||||
struct DLHead* next;
|
||||
struct DLHead* prev;
|
||||
struct DLHead* _next;
|
||||
struct DLHead* _prev;
|
||||
} DLHead;
|
||||
|
||||
typedef enum {FEA_OK = 0x00, FEA_REMOVE = 0x01, FEA_EXIT = 0x02} ForEachAct;
|
||||
|
||||
typedef int (*DLCompProc)(const DLHead* dl1, const DLHead* dl2);
|
||||
|
||||
DLHead* dll_insert( DLHead* list, DLHead* node, DLCompProc proc );
|
||||
DLHead* dll_remove( DLHead* list, DLHead* node );
|
||||
DLHead* dll_sort( DLHead* list, DLCompProc proc );
|
||||
|
||||
typedef void (*DLMapProc)(const DLHead* dl1, void* closure);
|
||||
void dll_map( DLHead* list, DLMapProc proc, void* closure );
|
||||
typedef ForEachAct (*DLMapProc)(const DLHead* dl1, void* closure);
|
||||
typedef void (*DLDisposeProc)(DLHead* elem, void* closure);
|
||||
DLHead* dll_map( DLHead* list, DLMapProc mapProc, DLDisposeProc dispProc,
|
||||
void* closure );
|
||||
|
||||
#ifdef CPLUS
|
||||
}
|
||||
|
|
|
@ -88,15 +88,15 @@ loadState( XW_DUtilCtxt* dutil, XWEnv xwe )
|
|||
return state;
|
||||
}
|
||||
|
||||
static void
|
||||
static ForEachAct
|
||||
saveProc( const DLHead* dl, void* closure )
|
||||
{
|
||||
XWStreamCtxt* stream = (XWStreamCtxt*)closure;
|
||||
KnownPlayer* kp = (KnownPlayer*)dl;
|
||||
stream_putU32( stream, kp->newestMod );
|
||||
XP_LOGFF( "wrote newestMod: %d for player %s", kp->newestMod, kp->name );
|
||||
stringToStream( stream, kp->name );
|
||||
addrToStream( stream, &kp->addr );
|
||||
return FEA_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -108,7 +108,7 @@ saveState( XW_DUtilCtxt* dutil, XWEnv xwe, KPState* state )
|
|||
stream_setVersion( stream, CUR_STREAM_VERS );
|
||||
stream_putU8( stream, CUR_STREAM_VERS );
|
||||
|
||||
dll_map( &state->players->links, saveProc, stream );
|
||||
dll_map( &state->players->links, saveProc, NULL, stream );
|
||||
|
||||
const XP_UCHAR* keys[] = { KNOWN_PLAYERS_KEY, NULL };
|
||||
dutil_storeStream( dutil, xwe, keys, stream );
|
||||
|
@ -198,41 +198,57 @@ compByDate(const DLHead* dl1, const DLHead* dl2)
|
|||
*
|
||||
* For early testing, however, just make a new name.
|
||||
*/
|
||||
|
||||
typedef struct _AddData {
|
||||
const XP_UCHAR* name;
|
||||
const CommsAddrRec* addr;
|
||||
KnownPlayer* withSameDevID;
|
||||
KnownPlayer* withSameName;
|
||||
} AddData;
|
||||
|
||||
static ForEachAct
|
||||
addProc( const DLHead* dl, void* closure )
|
||||
{
|
||||
AddData* adp = (AddData*)closure;
|
||||
KnownPlayer* kp = (KnownPlayer*)dl;
|
||||
if ( 0 == XP_STRCMP( kp->name, adp->name ) ) {
|
||||
adp->withSameName = kp;
|
||||
}
|
||||
if ( adp->addr->u.mqtt.devID == kp->addr.u.mqtt.devID ) {
|
||||
adp->withSameDevID = kp;
|
||||
}
|
||||
ForEachAct result = !!adp->withSameName && !!adp->withSameDevID
|
||||
? FEA_EXIT : FEA_OK;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
addPlayer( XW_DUtilCtxt* XP_UNUSED_DBG(dutil), KPState* state, const XP_UCHAR* name,
|
||||
const CommsAddrRec* addr, XP_U32 newestMod )
|
||||
addPlayer( XW_DUtilCtxt* XP_UNUSED_DBG(dutil), KPState* state,
|
||||
const XP_UCHAR* name, const CommsAddrRec* addr, XP_U32 newestMod )
|
||||
{
|
||||
XP_LOGFF( "(name=%s, newestMod: %d)", name, newestMod );
|
||||
KnownPlayer* withSameDevID = NULL;
|
||||
KnownPlayer* withSameName = NULL;
|
||||
AddData ad = {.name = name, .addr = addr, };
|
||||
dll_map( &state->players->links, addProc, NULL, &ad );
|
||||
|
||||
for ( KnownPlayer* kp = state->players;
|
||||
!!kp && (!withSameDevID || !withSameName);
|
||||
kp = (KnownPlayer*)kp->links.next ) {
|
||||
if ( 0 == XP_STRCMP( kp->name, name ) ) {
|
||||
withSameName = kp;
|
||||
}
|
||||
if ( addr->u.mqtt.devID == kp->addr.u.mqtt.devID ) {
|
||||
withSameDevID = kp;
|
||||
}
|
||||
}
|
||||
|
||||
XP_LOGFF( "withSameDevID: %p; withSameName: %p", withSameDevID, withSameName );
|
||||
XP_LOGFF( "withSameDevID: %p; withSameName: %p",
|
||||
ad.withSameDevID, ad.withSameName );
|
||||
|
||||
XP_UCHAR tmpName[64];
|
||||
if ( !!withSameDevID ) { /* only one allowed */
|
||||
XP_Bool isNewer = newestMod > withSameDevID->newestMod;
|
||||
XP_Bool changed = augmentAddr( &withSameDevID->addr, addr, isNewer );
|
||||
if ( !!ad.withSameDevID ) { /* only one allowed */
|
||||
XP_Bool isNewer = newestMod > ad.withSameDevID->newestMod;
|
||||
XP_Bool changed = augmentAddr( &ad.withSameDevID->addr,
|
||||
addr, isNewer );
|
||||
if ( isNewer ) {
|
||||
XP_LOGFF( "updating newestMod from %d to %d", withSameDevID->newestMod, newestMod );
|
||||
withSameDevID->newestMod = newestMod;
|
||||
XP_LOGFF( "updating newestMod from %d to %d",
|
||||
ad.withSameDevID->newestMod, newestMod );
|
||||
ad.withSameDevID->newestMod = newestMod;
|
||||
changed = XP_TRUE;
|
||||
} else {
|
||||
XP_LOGFF( "not newer" );
|
||||
}
|
||||
state->dirty = changed || state->dirty;
|
||||
} else {
|
||||
if ( !!withSameName ) {
|
||||
if ( !!ad.withSameName ) {
|
||||
/* Same name but different devID? Create a unique name */
|
||||
makeUniqueName( state, name, tmpName, VSIZE(tmpName) );
|
||||
name = tmpName;
|
||||
|
@ -298,12 +314,13 @@ typedef struct _GetState {
|
|||
} GetState;
|
||||
|
||||
|
||||
static void
|
||||
static ForEachAct
|
||||
getProc( const DLHead* dl, void* closure )
|
||||
{
|
||||
GetState* gsp = (GetState*)closure;
|
||||
const KnownPlayer* kp = (KnownPlayer*)dl;
|
||||
gsp->players[gsp->indx++] = kp->name;
|
||||
return FEA_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -311,10 +328,9 @@ getPlayersImpl( const KPState* state, const XP_UCHAR** players,
|
|||
XP_U16* nFound )
|
||||
{
|
||||
if ( state->nPlayers <= *nFound && !!players ) {
|
||||
GetState gs = { .players = players,
|
||||
.indx = 0,
|
||||
};
|
||||
dll_map( &state->players->links, getProc, &gs );
|
||||
GetState gs = { .players = players, .indx = 0, };
|
||||
DLHead* head = dll_map( &state->players->links, getProc, NULL, &gs );
|
||||
XP_ASSERT( head == &state->players->links );
|
||||
}
|
||||
*nFound = state->nPlayers;
|
||||
}
|
||||
|
@ -336,17 +352,31 @@ kplr_getNames( XW_DUtilCtxt* dutil, XWEnv xwe, XP_Bool byDate,
|
|||
releaseState( dutil, xwe, state );
|
||||
}
|
||||
|
||||
typedef struct _FindState {
|
||||
const XP_UCHAR* name;
|
||||
const KnownPlayer* result;
|
||||
} FindState;
|
||||
|
||||
static ForEachAct
|
||||
findProc( const DLHead* dl, void* closure )
|
||||
{
|
||||
ForEachAct result = FEA_OK;
|
||||
FindState* fsp = (FindState*)closure;
|
||||
const KnownPlayer* kp = (KnownPlayer*)dl;
|
||||
if ( 0 == XP_STRCMP( kp->name, fsp->name ) ) {
|
||||
fsp->result = kp;
|
||||
result = FEA_EXIT;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static KnownPlayer*
|
||||
findByName( KPState* state, const XP_UCHAR* name )
|
||||
{
|
||||
KnownPlayer* result = NULL;
|
||||
for ( KnownPlayer* kp = state->players; !!kp && !result;
|
||||
kp = (KnownPlayer*)kp->links.next ) {
|
||||
if ( 0 == XP_STRCMP( kp->name, name ) ) {
|
||||
result = kp;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
FindState fs = { .name = name, };
|
||||
DLHead* head = dll_map( &state->players->links, findProc, NULL, &fs );
|
||||
XP_ASSERT( head == &state->players->links );
|
||||
return (KnownPlayer*)fs.result;
|
||||
}
|
||||
|
||||
XP_Bool
|
||||
|
@ -368,26 +398,40 @@ kplr_getAddr( XW_DUtilCtxt* dutil, XWEnv xwe, const XP_UCHAR* name,
|
|||
return found;
|
||||
}
|
||||
|
||||
const XP_UCHAR*
|
||||
kplr_nameForMqttDev( XW_DUtilCtxt* dutil, XWEnv xwe, const XP_UCHAR* mqttDevID )
|
||||
{
|
||||
const XP_UCHAR* name = NULL;
|
||||
typedef struct _MDevState {
|
||||
MQTTDevID devID;
|
||||
if ( strToMQTTCDevID( mqttDevID, &devID ) ) {
|
||||
const XP_UCHAR* name;
|
||||
} MDevState;
|
||||
|
||||
static ForEachAct
|
||||
mqttProc( const DLHead* dl, void* closure )
|
||||
{
|
||||
ForEachAct result = FEA_OK;
|
||||
const KnownPlayer* kp = (KnownPlayer*)dl;
|
||||
MDevState* msp = (MDevState*)closure;
|
||||
const CommsAddrRec* addr = &kp->addr;
|
||||
if ( addr_hasType( addr, COMMS_CONN_MQTT )
|
||||
&& 0 == XP_MEMCMP( &addr->u.mqtt.devID, &msp->devID,
|
||||
sizeof(msp->devID) ) ) {
|
||||
msp->name = kp->name;
|
||||
result = FEA_EXIT;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const XP_UCHAR*
|
||||
kplr_nameForMqttDev( XW_DUtilCtxt* dutil, XWEnv xwe,
|
||||
const XP_UCHAR* mqttDevID )
|
||||
{
|
||||
MDevState ms = {0};
|
||||
if ( strToMQTTCDevID( mqttDevID, &ms.devID ) ) {
|
||||
KPState* state = loadState( dutil, xwe );
|
||||
for ( KnownPlayer* kp = state->players; !!kp && !name;
|
||||
kp = (KnownPlayer*)kp->links.next ) {
|
||||
const CommsAddrRec* addr = &kp->addr;
|
||||
if ( addr_hasType( addr, COMMS_CONN_MQTT ) ) {
|
||||
if ( 0 == XP_MEMCMP( &addr->u.mqtt.devID, &devID, sizeof(devID) ) ) {
|
||||
name = kp->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
DLHead* head = dll_map( &state->players->links, mqttProc, NULL, &ms );
|
||||
XP_ASSERT( head == &state->players->links );
|
||||
releaseState( dutil, xwe, state );
|
||||
}
|
||||
LOG_RETURNF( "%s", name );
|
||||
return name;
|
||||
LOG_RETURNF( "%s", ms.name );
|
||||
return ms.name;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -420,31 +464,45 @@ kplr_renamePlayer( XW_DUtilCtxt* dutil, XWEnv xwe, const XP_UCHAR* oldName,
|
|||
return result;
|
||||
}
|
||||
|
||||
typedef struct _DelState {
|
||||
XW_DUtilCtxt* dutil;
|
||||
const XP_UCHAR* name;
|
||||
KPState* state;
|
||||
} DelState;
|
||||
|
||||
static ForEachAct
|
||||
delMapProc( const DLHead* dl, void* closure )
|
||||
{
|
||||
ForEachAct result = FEA_OK;
|
||||
|
||||
const KnownPlayer* kp = (KnownPlayer*)dl;
|
||||
DelState* dsp = (DelState*)closure;
|
||||
if ( 0 == XP_STRCMP( kp->name, dsp->name ) ) {
|
||||
result = FEA_REMOVE | FEA_EXIT;
|
||||
--dsp->state->nPlayers;
|
||||
dsp->state->dirty = XP_TRUE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
delFreeProc( DLHead* elem, void* closure )
|
||||
{
|
||||
DelState* dsp = (DelState*)closure;
|
||||
freeKP( dsp->dutil, (KnownPlayer*)elem );
|
||||
}
|
||||
|
||||
KP_Rslt
|
||||
kplr_deletePlayer( XW_DUtilCtxt* dutil, XWEnv xwe, const XP_UCHAR* name )
|
||||
{
|
||||
KP_Rslt result = KP_NAME_NOT_FOUND;
|
||||
KnownPlayer* doomed = NULL;
|
||||
KP_Rslt result = KP_OK;
|
||||
KPState* state = loadState( dutil, xwe );
|
||||
|
||||
for ( KnownPlayer* kp = state->players; !!kp;
|
||||
kp = (KnownPlayer*)kp->links.next ) {
|
||||
if ( 0 == XP_STRCMP( kp->name, name ) ) {
|
||||
doomed = kp;
|
||||
state->players = (KnownPlayer*)
|
||||
dll_remove( &state->players->links, &doomed->links );
|
||||
--state->nPlayers;
|
||||
state->dirty = XP_TRUE;
|
||||
result = KP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
DelState ds = { .name = name, .state = state, .dutil = dutil,};
|
||||
state->players = (KnownPlayer*)
|
||||
dll_map( &state->players->links, delMapProc, delFreeProc, &ds );
|
||||
releaseState( dutil, xwe, state );
|
||||
|
||||
XP_ASSERT( !!doomed );
|
||||
if ( !!doomed ) {
|
||||
freeKP( dutil, doomed );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -2598,10 +2598,11 @@ compByNameRev( const DLHead* dl1, const DLHead* dl2 )
|
|||
return strcmp( ((TestThing*)dl2)->name, ((TestThing*)dl1)->name );
|
||||
}
|
||||
|
||||
static void
|
||||
static ForEachAct
|
||||
mapProc( const DLHead* dl, void* XP_UNUSED(closure))
|
||||
{
|
||||
XP_LOGFF( "name: %s", ((TestThing*)dl)->name );
|
||||
return FEA_OK;
|
||||
}
|
||||
|
||||
static TestThing*
|
||||
|
@ -2609,7 +2610,7 @@ removeAndMap( TestThing* list, TestThing* node )
|
|||
{
|
||||
XP_LOGFF( "removing %s", node->name );
|
||||
list = (TestThing*)dll_remove( &list->links, &node->links );
|
||||
dll_map( &list->links, mapProc, NULL );
|
||||
dll_map( &list->links, mapProc, NULL, NULL );
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -2627,10 +2628,10 @@ testDLL()
|
|||
for ( int ii = 0; ii < VSIZE(tss); ++ii ) {
|
||||
list = (TestThing*)dll_insert( &list->links, &tss[ii].links, compByName );
|
||||
}
|
||||
dll_map( &list->links, mapProc, NULL );
|
||||
dll_map( &list->links, mapProc, NULL, NULL );
|
||||
|
||||
list = (TestThing*)dll_sort( &list->links, compByNameRev );
|
||||
dll_map( &list->links, mapProc, NULL );
|
||||
dll_map( &list->links, mapProc, NULL, NULL );
|
||||
|
||||
list = removeAndMap( list, &tss[0] );
|
||||
list = removeAndMap( list, &tss[2] );
|
||||
|
|
Loading…
Reference in a new issue