mirror of
https://bitbucket.org/cosmicvoids/vide_public.git
synced 2025-07-05 15:51:12 +00:00
237 lines
5.9 KiB
C
237 lines
5.9 KiB
C
/* DLL: doubly linked lists.
|
|
|
|
Support:
|
|
insertion either before or after a given element (Above, Below).
|
|
deletion.
|
|
traversal in either direction. (Up, Down)
|
|
|
|
Use a vertical metaphor for mnemonics. You can grow the data
|
|
structure up, down, or outward from the middle. It makes
|
|
absolutely no difference.
|
|
|
|
The implementation uses two 'sentinels'. The one above the topmost
|
|
element is called 'Sup' and the one below the lowest element is
|
|
called 'Inf'. These are good places to start and end 'for' loops, e.g.,
|
|
|
|
for(p=DllTop(dll); p!=DllInf(dll); p = DllDown(dll, p))...
|
|
|
|
or
|
|
|
|
for(p=DllBottom(dll); p!=DllSup(dll); p = DllUp(dll, p))...
|
|
|
|
Space for 'user' data of size 'sz' is allocated with each element.
|
|
This is ideal if you want to allocate associate a fixed size object
|
|
with each list element. If you want more flexibility, there's
|
|
nothing stopping you from making that fixed size object a void*
|
|
that points to something else.
|
|
|
|
*/
|
|
|
|
#include "chn.h"
|
|
#include "dll.h"
|
|
#include "Malloc.h"
|
|
|
|
/* Initializing is a two step process because we have to do some
|
|
funny stuff to get a chain allocating chunks just slightly bigger than
|
|
what the user asks for. The resulting chain can be ChnTerminated
|
|
at the caller's leisure. */
|
|
void DllCreateChn(Chn *chn, int sz, int n){
|
|
n+=2;
|
|
if( sz > sizeof(int) )
|
|
sz -= sizeof(int);
|
|
ChnInit(chn, sizeof(Dll_elmt)+sz, n, Realloc_f);
|
|
}
|
|
|
|
void DllCreate(Dll *dll, Chn *chn){
|
|
dll->chn = chn;
|
|
dll->Sup.down = &dll->Inf;
|
|
dll->Sup.up = NULL;
|
|
dll->Inf.down = NULL;
|
|
dll->Inf.up = &dll->Sup;
|
|
dll->length = 0;
|
|
}
|
|
|
|
/* Terminate a dll */
|
|
void DllTerminate(Dll *dll){
|
|
/* Hmmm. There's really nothing to do since the user is now
|
|
empowered to free the chain. We can't even do a FreeAll because
|
|
there might be other stuff (other DLL's?) using the chain. */
|
|
}
|
|
|
|
/* Insert closer to the Top */
|
|
Dll_elmt *DllInsertAbove(Dll *dll, Dll_elmt *down){
|
|
Dll_elmt *new = ChnAlloc(dll->chn);
|
|
Dll_elmt *up = down->up;
|
|
|
|
if( new == NULL ){
|
|
Shout("ChnAlloc returns null in DllInsertAbove\n");
|
|
return NULL;
|
|
}
|
|
dll->length++;
|
|
new->up = up;
|
|
new->down = down;
|
|
down->up = new;
|
|
up->down = new;
|
|
return new;
|
|
}
|
|
|
|
/* Insert closer to the bottom */
|
|
Dll_elmt *DllInsertBelow(Dll *dll, Dll_elmt *up){
|
|
Dll_elmt *new = ChnAlloc(dll->chn);
|
|
Dll_elmt *down = up->down;
|
|
|
|
if( new == NULL ){
|
|
Shout("ChnAlloc returns null in DllInsertBelow\n");
|
|
return NULL;
|
|
}
|
|
dll->length++;
|
|
new->up = up;
|
|
new->down = down;
|
|
down->up = new;
|
|
up->down = new;
|
|
return new;
|
|
}
|
|
|
|
/* These two should be inlined, with __inline__ ... */
|
|
/* Insert at the bottom */
|
|
Dll_elmt *DllInsertAtBottom(Dll *dll){
|
|
return DllInsertAbove(dll, &(dll->Inf));
|
|
}
|
|
|
|
/* Insert at the top */
|
|
Dll_elmt *DllInsertAtTop(Dll *dll){
|
|
return DllInsertBelow(dll, &(dll->Sup));
|
|
}
|
|
|
|
/* These three are VERY similar, but they are useful for traversals iin
|
|
different directions. Otherwise the caller needs to save the 'up' or
|
|
'down' element */
|
|
/* Delete an entry. Return nothing. */
|
|
void DllDelete(Dll *dll, Dll_elmt *old){
|
|
Dll_elmt *up = old->up;
|
|
Dll_elmt *down = old->down;
|
|
|
|
dll->length--;
|
|
up->down = down;
|
|
down->up = up;
|
|
ChnFree(dll->chn, old);
|
|
}
|
|
|
|
/* Delete an entry. Return the entry that used to be above it. */
|
|
Dll_elmt *DllDeleteUp(Dll *dll, Dll_elmt *old){
|
|
Dll_elmt *up = old->up;
|
|
Dll_elmt *down = old->down;
|
|
|
|
dll->length--;
|
|
up->down = down;
|
|
down->up = up;
|
|
ChnFree(dll->chn, old);
|
|
return up;
|
|
}
|
|
|
|
/* Delete an entry. Return the entry that used to be below it. */
|
|
Dll_elmt *DllDeleteDown(Dll *dll, Dll_elmt *old){
|
|
Dll_elmt *up = old->up;
|
|
Dll_elmt *down = old->down;
|
|
|
|
dll->length--;
|
|
up->down = down;
|
|
down->up = up;
|
|
ChnFree(dll->chn, old);
|
|
return down;
|
|
}
|
|
|
|
/* The next two have two plausible returns: the item directly above or
|
|
below the original position of the mover. Rather than confuse
|
|
things, I won't return either, and leave it up to the caller to keep
|
|
track of whatever he wants. */
|
|
|
|
/* Extract the 'mover' and place it immediately below 'up'.
|
|
Like DllDelete, followed by DllInsertBelow, but preserve the
|
|
data in the object. */
|
|
void DllMoveBelow(Dll *dll, Dll_elmt *mover, Dll_elmt *up){
|
|
Dll_elmt *down;
|
|
/* Extract the mover */
|
|
mover->down->up = mover->up;
|
|
mover->up->down = mover->down;
|
|
/* Now insert it below up */
|
|
down = up->down;
|
|
mover->up = up;
|
|
mover->down = down;
|
|
down->up = mover;
|
|
up->down = mover;
|
|
}
|
|
|
|
/* Extract the 'mover' and place it immediately above 'down'.
|
|
Like DllDelete, followed by DllInsertAbove, but preserve the
|
|
data in the object. */
|
|
void DllMoveAbove(Dll *dll, Dll_elmt *mover, Dll_elmt *down){
|
|
Dll_elmt *up;
|
|
/* Extract the mover */
|
|
mover->down->up = mover->up;
|
|
mover->up->down = mover->down;
|
|
/* Now insert it above down */
|
|
up = down->up;
|
|
mover->up = up;
|
|
mover->down = down;
|
|
down->up = mover;
|
|
up->down = mover;
|
|
}
|
|
|
|
/* These should be inlined... */
|
|
/* Move to bottom */
|
|
void DllMoveToBottom(Dll *dll, Dll_elmt *mover){
|
|
DllMoveAbove(dll, mover, &(dll->Inf));
|
|
}
|
|
|
|
/* Move to top */
|
|
void DllMoveToTop(Dll *dll, Dll_elmt *mover){
|
|
DllMoveBelow(dll, mover, &(dll->Sup));
|
|
}
|
|
|
|
/* These are generally 'inlined' with appropriate #defines in dll.h.
|
|
They're simple enough to allow use of the pre-processor instead of
|
|
__inline__. */
|
|
#undef DllLength
|
|
int DllLength(Dll *dll){
|
|
return dll->length;
|
|
}
|
|
|
|
#undef DllSup
|
|
Dll_elmt *DllSup(Dll *dll){
|
|
return &dll->Sup;
|
|
}
|
|
|
|
#undef DllInf
|
|
Dll_elmt *DllInf(Dll *dll){
|
|
return &dll->Inf;
|
|
}
|
|
|
|
/* The highest 'real' element */
|
|
#undef DllTop
|
|
Dll_elmt *DllTop(Dll *dll){
|
|
return dll->Sup.down;
|
|
}
|
|
|
|
/* The lowest 'real' element */
|
|
#undef DllBottom
|
|
Dll_elmt *DllBottom(Dll *dll){
|
|
return dll->Inf.up;
|
|
}
|
|
|
|
#undef DllData
|
|
void *DllData(Dll_elmt *elmt){
|
|
return &elmt->stuff;
|
|
}
|
|
|
|
#undef DllUp
|
|
Dll_elmt *DllUp(Dll_elmt *elmt){
|
|
return elmt->up;
|
|
}
|
|
|
|
#undef DllDown
|
|
Dll_elmt *DllDown(Dll_elmt *elmt){
|
|
return elmt->down;
|
|
}
|
|
|
|
|