327 lines
7.2 KiB
C
327 lines
7.2 KiB
C
|
//
|
||
|
// Lists have internal serialization so that most functions operate as atomic
|
||
|
// operations. However, the results of some functions are transient (i.e.
|
||
|
// immediately out-of-date) unless the caller explicitly locks the list using
|
||
|
// the Lock/Unlock methods. These functions are GetCount, GetHeadPosition,
|
||
|
// GetNext. Also, it is the caller's responsibility to serialize access to a
|
||
|
// POSITION. The internal serialization only ensures that two simultaneus
|
||
|
// AddTail calls will not make the list internally inconsistent, and nor will
|
||
|
// two simultaneous RemoveAt calls on _different_ POSITIONs.
|
||
|
//
|
||
|
// List elements with NULL object pointers are skipped by list iteration
|
||
|
// functions GetHeadPosition and GetNext.
|
||
|
//
|
||
|
|
||
|
typedef struct _CListElement {
|
||
|
struct _CListElement *Next;
|
||
|
struct _CListElement *Prev;
|
||
|
const void *Object;
|
||
|
} CListElement, *POSITION;
|
||
|
|
||
|
template <class TYPE, class ARG_TYPE> class CList {
|
||
|
public:
|
||
|
CList();
|
||
|
~CList();
|
||
|
|
||
|
POSITION AddHead(ARG_TYPE newElement);
|
||
|
POSITION AddTail(ARG_TYPE newElement);
|
||
|
TYPE GetAt(POSITION position);
|
||
|
int GetCount(void) const;
|
||
|
POSITION GetHeadPosition(void) const;
|
||
|
TYPE GetNext(POSITION& rPosition); // return *Position++
|
||
|
LONG Initialize(void);
|
||
|
POSITION InsertAfter(POSITION position, ARG_TYPE newElement);
|
||
|
POSITION InsertBefore(POSITION position, ARG_TYPE newElement);
|
||
|
BOOL IsEmpty(void) const;
|
||
|
void Lock(void);
|
||
|
void MoveBefore(POSITION posTarget, POSITION posMove);
|
||
|
void RemoveAll(void);
|
||
|
void RemoveAt(POSITION position);
|
||
|
void SetAt(POSITION pos, ARG_TYPE newElement);
|
||
|
void Unlock(void);
|
||
|
|
||
|
private:
|
||
|
BOOL m_Initialized;
|
||
|
CRITICAL_SECTION m_CriticalSection;
|
||
|
int m_Count;
|
||
|
CListElement m_Sentinel;
|
||
|
};
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
CList<TYPE, ARG_TYPE>::CList(void)
|
||
|
{
|
||
|
m_Initialized = FALSE;
|
||
|
m_Count = 0;
|
||
|
m_Sentinel.Next = &m_Sentinel;
|
||
|
m_Sentinel.Prev = &m_Sentinel;
|
||
|
m_Sentinel.Object = &m_Sentinel;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
CList<TYPE, ARG_TYPE>::~CList(void)
|
||
|
{
|
||
|
if (m_Initialized) {
|
||
|
RemoveAll();
|
||
|
ASSERT(m_Sentinel.Next = &m_Sentinel);
|
||
|
ASSERT(m_Sentinel.Prev = &m_Sentinel);
|
||
|
DeleteCriticalSection(&m_CriticalSection);
|
||
|
m_Initialized = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
POSITION CList<TYPE, ARG_TYPE>::AddHead(ARG_TYPE newElement)
|
||
|
{
|
||
|
POSITION pos;
|
||
|
|
||
|
ASSERT(m_Initialized);
|
||
|
|
||
|
pos = new CListElement;
|
||
|
if (pos) {
|
||
|
Lock();
|
||
|
|
||
|
pos->Next = m_Sentinel.Next;
|
||
|
pos->Prev = &m_Sentinel;
|
||
|
|
||
|
pos->Prev->Next = pos;
|
||
|
pos->Next->Prev = pos;
|
||
|
|
||
|
pos->Object = newElement;
|
||
|
|
||
|
m_Count++;
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
return pos;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
POSITION CList<TYPE, ARG_TYPE>::AddTail(ARG_TYPE newElement)
|
||
|
{
|
||
|
POSITION pos;
|
||
|
|
||
|
ASSERT(m_Initialized);
|
||
|
|
||
|
pos = new CListElement;
|
||
|
if (pos) {
|
||
|
Lock();
|
||
|
|
||
|
pos->Next = &m_Sentinel;
|
||
|
pos->Prev = m_Sentinel.Prev;
|
||
|
|
||
|
pos->Prev->Next = pos;
|
||
|
pos->Next->Prev = pos;
|
||
|
|
||
|
pos->Object = newElement;
|
||
|
|
||
|
m_Count++;
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
return pos;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
TYPE CList<TYPE, ARG_TYPE>::GetAt(POSITION pos)
|
||
|
{
|
||
|
ASSERT(m_Initialized);
|
||
|
return (TYPE)pos->Object;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
int CList<TYPE, ARG_TYPE>::GetCount(void) const
|
||
|
{
|
||
|
ASSERT(m_Initialized);
|
||
|
return m_Count;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
POSITION CList<TYPE, ARG_TYPE>::GetHeadPosition(void) const
|
||
|
{
|
||
|
POSITION pos;
|
||
|
ASSERT(m_Initialized);
|
||
|
pos = m_Sentinel.Next;
|
||
|
// Skip NULL elements
|
||
|
while (NULL == pos->Object) pos = pos->Next;
|
||
|
if (pos == &m_Sentinel) return NULL;
|
||
|
return pos;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
TYPE CList<TYPE, ARG_TYPE>::GetNext(POSITION& rPos)
|
||
|
{
|
||
|
ASSERT(m_Initialized);
|
||
|
|
||
|
TYPE Object = (TYPE)rPos->Object;
|
||
|
|
||
|
rPos = rPos->Next;
|
||
|
// Skip NULL elements.
|
||
|
while (NULL == rPos->Object) rPos = rPos->Next;
|
||
|
if (rPos == &m_Sentinel) rPos = NULL;
|
||
|
|
||
|
return Object;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
LONG CList<TYPE, ARG_TYPE>::Initialize(void)
|
||
|
{
|
||
|
LONG result;
|
||
|
|
||
|
ASSERT(!m_Initialized);
|
||
|
|
||
|
__try {
|
||
|
InitializeCriticalSection(&m_CriticalSection);
|
||
|
result = NO_ERROR;
|
||
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
||
|
result = ERROR_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
m_Initialized = (NO_ERROR == result);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
POSITION CList<TYPE, ARG_TYPE>::InsertAfter(POSITION position, ARG_TYPE newElement)
|
||
|
{
|
||
|
ASSERT(m_Initialized);
|
||
|
|
||
|
if (position == NULL)
|
||
|
{
|
||
|
// insert after nothing -> head of the list
|
||
|
position = &m_Sentinel;
|
||
|
}
|
||
|
|
||
|
// Insert it after position
|
||
|
CListElement* newPos = new CListElement;
|
||
|
if (newPos)
|
||
|
{
|
||
|
Lock();
|
||
|
|
||
|
newPos->Next = position->Next;
|
||
|
newPos->Prev = position;
|
||
|
|
||
|
newPos->Next->Prev = newPos;
|
||
|
newPos->Prev->Next = newPos;
|
||
|
|
||
|
newPos->Object = newElement;
|
||
|
|
||
|
m_Count++;
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
return newPos;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
POSITION CList<TYPE, ARG_TYPE>::InsertBefore(POSITION position, ARG_TYPE newElement)
|
||
|
{
|
||
|
ASSERT(m_Initialized);
|
||
|
|
||
|
if (position == NULL)
|
||
|
{
|
||
|
// insert before nothing -> tail of list
|
||
|
position = &m_Sentinel;
|
||
|
}
|
||
|
|
||
|
// Insert it before position
|
||
|
CListElement* newPos = new CListElement;
|
||
|
if (newPos)
|
||
|
{
|
||
|
Lock();
|
||
|
|
||
|
newPos->Next = position;
|
||
|
newPos->Prev = position->Prev;
|
||
|
|
||
|
newPos->Next->Prev = newPos;
|
||
|
newPos->Prev->Next = newPos;
|
||
|
|
||
|
newPos->Object = newElement;
|
||
|
|
||
|
m_Count++;
|
||
|
|
||
|
Unlock();
|
||
|
}
|
||
|
return newPos;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
BOOL CList<TYPE, ARG_TYPE>::IsEmpty(void) const
|
||
|
{
|
||
|
ASSERT(m_Initialized);
|
||
|
return (0 == m_Count);
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
void CList<TYPE, ARG_TYPE>::Lock(void)
|
||
|
{
|
||
|
EnterCriticalSection(&m_CriticalSection);
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
void CList<TYPE, ARG_TYPE>::MoveBefore(POSITION posTarget, POSITION posMove)
|
||
|
{
|
||
|
ASSERT(m_Initialized);
|
||
|
|
||
|
if (posTarget == posMove) return;
|
||
|
|
||
|
if (posTarget == NULL)
|
||
|
{
|
||
|
// Move before nothing -> tail of list
|
||
|
posTarget = &m_Sentinel;
|
||
|
}
|
||
|
|
||
|
// first remove from list
|
||
|
posMove->Prev->Next = posMove->Next;
|
||
|
posMove->Next->Prev = posMove->Prev;
|
||
|
|
||
|
// Move it before posTarget
|
||
|
posMove->Next = posTarget;
|
||
|
posMove->Prev = posTarget->Prev;
|
||
|
|
||
|
posMove->Next->Prev = posMove;
|
||
|
posMove->Prev->Next = posMove;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
void CList<TYPE, ARG_TYPE>::RemoveAll(void)
|
||
|
{
|
||
|
POSITION pos;
|
||
|
ASSERT(m_Initialized);
|
||
|
while (pos = GetHeadPosition()) RemoveAt(pos);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
void CList<TYPE, ARG_TYPE>::RemoveAt(POSITION pos)
|
||
|
{
|
||
|
ASSERT(m_Initialized);
|
||
|
ASSERT(m_Count > 0);
|
||
|
Lock();
|
||
|
pos->Prev->Next = pos->Next;
|
||
|
pos->Next->Prev = pos->Prev;
|
||
|
m_Count--;
|
||
|
Unlock();
|
||
|
delete pos;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
void CList<TYPE, ARG_TYPE>::SetAt(POSITION pos, ARG_TYPE newElement)
|
||
|
{
|
||
|
ASSERT(m_Initialized);
|
||
|
ASSERT(m_Count > 0);
|
||
|
Lock();
|
||
|
pos->Object = newElement;
|
||
|
Unlock();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
template<class TYPE, class ARG_TYPE>
|
||
|
void CList<TYPE, ARG_TYPE>::Unlock(void)
|
||
|
{
|
||
|
LeaveCriticalSection(&m_CriticalSection);
|
||
|
}
|
||
|
|