2022-08-13 19:41:45 +02:00
|
|
|
|
//======= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ======//
|
|
|
|
|
//
|
|
|
|
|
// Purpose: Linked list container class
|
|
|
|
|
//
|
|
|
|
|
// $Revision: $
|
|
|
|
|
// $NoKeywords: $
|
|
|
|
|
//=============================================================================//
|
|
|
|
|
|
|
|
|
|
#ifndef UTLLINKEDLIST_H
|
|
|
|
|
#define UTLLINKEDLIST_H
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
#pragma once
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "tier0/basetypes.h"
|
|
|
|
|
#include "utlmemory.h"
|
|
|
|
|
#include "utlfixedmemory.h"
|
|
|
|
|
#include "utlblockmemory.h"
|
|
|
|
|
#include "tier0/dbg.h"
|
|
|
|
|
|
|
|
|
|
// define to enable asserts griping about things you shouldn't be doing with multilists
|
|
|
|
|
// #define MULTILIST_PEDANTIC_ASSERTS 1
|
|
|
|
|
|
|
|
|
|
// This is a useful macro to iterate from head to tail in a linked list.
|
|
|
|
|
#define FOR_EACH_LL( listName, iteratorName ) \
|
|
|
|
|
for( int iteratorName=listName.Head(); iteratorName != listName.InvalidIndex(); iteratorName = listName.Next( iteratorName ) )
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// class CUtlLinkedList:
|
|
|
|
|
// description:
|
|
|
|
|
// A lovely index-based linked list! T is the class type, I is the index
|
|
|
|
|
// type, which usually should be an unsigned short or smaller. However,
|
|
|
|
|
// you must avoid using 16- or 8-bit arithmetic on PowerPC architectures;
|
|
|
|
|
// therefore you should not use UtlLinkedListElem_t::I as the type of
|
|
|
|
|
// a local variable... ever. PowerPC integer arithmetic must be 32- or
|
|
|
|
|
// 64-bit only; otherwise performance plummets.
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
template <class T, class I>
|
|
|
|
|
struct UtlLinkedListElem_t
|
|
|
|
|
{
|
|
|
|
|
T m_Element;
|
|
|
|
|
I m_Previous;
|
|
|
|
|
I m_Next;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// No copy constructor for these...
|
|
|
|
|
UtlLinkedListElem_t(const UtlLinkedListElem_t&);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Class S is the storage type; the type you can use to save off indices in
|
|
|
|
|
// persistent memory. Class I is the iterator type, which is what should be used
|
|
|
|
|
// in local scopes. I defaults to be S, but be aware that on the 360, 16-bit
|
|
|
|
|
// arithmetic is catastrophically slow. Therefore you should try to save shorts
|
|
|
|
|
// in memory, but always operate on 32's or 64's in local scope.
|
|
|
|
|
// The ideal parameter order would be TSMI (you are more likely to override M than I)
|
|
|
|
|
// but since M depends on I we can't have the defaults in that order, alas.
|
|
|
|
|
template <class T, class S = unsigned short, bool ML = false, class I = S, class M = CUtlMemory< UtlLinkedListElem_t<T, S>, I > >
|
|
|
|
|
class CUtlLinkedList
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
typedef T ElemType_t;
|
|
|
|
|
typedef S IndexType_t; // should really be called IndexStorageType_t, but that would be a huge change
|
|
|
|
|
typedef I IndexLocalType_t;
|
|
|
|
|
typedef M MemoryAllocator_t;
|
|
|
|
|
|
|
|
|
|
// constructor, destructor
|
|
|
|
|
CUtlLinkedList(ssize_t growSize = 0, ssize_t initSize = 0);
|
|
|
|
|
~CUtlLinkedList();
|
|
|
|
|
|
|
|
|
|
// gets particular elements
|
|
|
|
|
T& Element(I i);
|
|
|
|
|
T const& Element(I i) const;
|
|
|
|
|
T& operator[](I i);
|
|
|
|
|
T const& operator[](I i) const;
|
|
|
|
|
|
|
|
|
|
// Make sure we have a particular amount of memory
|
|
|
|
|
void EnsureCapacity(int num);
|
|
|
|
|
|
|
|
|
|
void SetGrowSize(int growSize);
|
|
|
|
|
|
|
|
|
|
// Memory deallocation
|
|
|
|
|
void Purge();
|
|
|
|
|
|
|
|
|
|
// Delete all the elements then call Purge.
|
|
|
|
|
void PurgeAndDeleteElements();
|
|
|
|
|
|
|
|
|
|
// Insertion methods....
|
|
|
|
|
I InsertBefore(I before);
|
|
|
|
|
I InsertAfter(I after);
|
|
|
|
|
I AddToHead();
|
|
|
|
|
I AddToTail();
|
|
|
|
|
|
|
|
|
|
I InsertBefore(I before, T const& src);
|
|
|
|
|
I InsertAfter(I after, T const& src);
|
|
|
|
|
I AddToHead(T const& src);
|
|
|
|
|
I AddToTail(T const& src);
|
|
|
|
|
|
|
|
|
|
// Find an element and return its index or InvalidIndex() if it couldn't be found.
|
|
|
|
|
I Find(const T& src) const;
|
|
|
|
|
|
|
|
|
|
// Look for the element. If it exists, remove it and return true. Otherwise, return false.
|
|
|
|
|
bool FindAndRemove(const T& src);
|
|
|
|
|
|
|
|
|
|
// Removal methods
|
|
|
|
|
void Remove(I elem);
|
|
|
|
|
void RemoveAll();
|
|
|
|
|
|
|
|
|
|
// Allocation/deallocation methods
|
|
|
|
|
// If multilist == true, then list list may contain many
|
|
|
|
|
// non-connected lists, and IsInList and Head + Tail are meaningless...
|
|
|
|
|
I Alloc(bool multilist = false);
|
|
|
|
|
void Free(I elem);
|
|
|
|
|
|
|
|
|
|
// Identify the owner of this linked list's memory:
|
|
|
|
|
void SetAllocOwner(const char* pszAllocOwner);
|
|
|
|
|
|
|
|
|
|
// list modification
|
|
|
|
|
void LinkBefore(I before, I elem);
|
|
|
|
|
void LinkAfter(I after, I elem);
|
|
|
|
|
void Unlink(I elem);
|
|
|
|
|
void LinkToHead(I elem);
|
|
|
|
|
void LinkToTail(I elem);
|
|
|
|
|
|
|
|
|
|
// invalid index (M will never allocate an element at this index)
|
|
|
|
|
inline static S InvalidIndex() { return (S)M::InvalidIndex(); }
|
|
|
|
|
|
2022-09-09 19:47:31 +02:00
|
|
|
|
// Is a given index valid to use? (representable by S and not the invalid index)
|
2022-08-13 19:41:45 +02:00
|
|
|
|
static bool IndexInRange(I index);
|
|
|
|
|
|
|
|
|
|
inline static size_t ElementSize() { return sizeof(ListElem_t); }
|
|
|
|
|
|
|
|
|
|
// list statistics
|
|
|
|
|
int Count() const;
|
|
|
|
|
I MaxElementIndex() const;
|
|
|
|
|
I NumAllocated(void) const { return m_NumAlloced; }
|
|
|
|
|
|
|
|
|
|
// Traversing the list
|
|
|
|
|
I Head() const;
|
|
|
|
|
I Tail() const;
|
|
|
|
|
I Previous(I i) const;
|
|
|
|
|
I Next(I i) const;
|
|
|
|
|
|
|
|
|
|
// Are nodes in the list or valid?
|
|
|
|
|
bool IsValidIndex(I i) const;
|
|
|
|
|
bool IsInList(I i) const;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
|
|
|
|
|
// What the linked list element looks like
|
|
|
|
|
typedef UtlLinkedListElem_t<T, S> ListElem_t;
|
|
|
|
|
|
|
|
|
|
// constructs the class
|
|
|
|
|
I AllocInternal(bool multilist = false);
|
|
|
|
|
void ConstructList();
|
|
|
|
|
|
|
|
|
|
// Gets at the list element....
|
|
|
|
|
ListElem_t& InternalElement(I i) { return m_Memory[i]; }
|
|
|
|
|
ListElem_t const& InternalElement(I i) const { return m_Memory[i]; }
|
|
|
|
|
|
|
|
|
|
// copy constructors not allowed
|
|
|
|
|
CUtlLinkedList(CUtlLinkedList<T, S, ML, I, M> const& list) { Assert(0); }
|
|
|
|
|
|
|
|
|
|
M m_Memory;
|
|
|
|
|
I m_Head;
|
|
|
|
|
I m_Tail;
|
|
|
|
|
I m_FirstFree;
|
|
|
|
|
I m_ElementCount; // The number actually in the list
|
|
|
|
|
I m_NumAlloced; // The number of allocated elements
|
|
|
|
|
typename M::Iterator_t m_LastAlloc; // the last index allocated
|
|
|
|
|
|
|
|
|
|
// For debugging purposes;
|
|
|
|
|
// it's in release builds so this can be used in libraries correctly
|
|
|
|
|
ListElem_t* m_pElements;
|
|
|
|
|
|
|
|
|
|
FORCEINLINE M const& Memory(void) const
|
|
|
|
|
{
|
|
|
|
|
return m_Memory;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ResetDbgInfo()
|
|
|
|
|
{
|
|
|
|
|
m_pElements = m_Memory.Base();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice
|
|
|
|
|
template < class T >
|
|
|
|
|
class CUtlFixedLinkedList : public CUtlLinkedList< T, intptr_t, true, intptr_t, CUtlFixedMemory< UtlLinkedListElem_t< T, intptr_t > > >
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CUtlFixedLinkedList(ssize_t growSize = 0, ssize_t initSize = 0)
|
|
|
|
|
: CUtlLinkedList< T, intptr_t, true, intptr_t, CUtlFixedMemory< UtlLinkedListElem_t< T, intptr_t > > >(growSize, initSize) {}
|
|
|
|
|
|
|
|
|
|
bool IsValidIndex(intptr_t i) const
|
|
|
|
|
{
|
|
|
|
|
if (!this->Memory().IsIdxValid(i))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
#ifdef _DEBUG // it's safe to skip this here, since the only way to get indices after m_LastAlloc is to use MaxElementIndex
|
|
|
|
|
if (this->Memory().IsIdxAfter(i, this->m_LastAlloc))
|
|
|
|
|
{
|
|
|
|
|
Assert(0);
|
|
|
|
|
return false; // don't read values that have been allocated, but not constructed
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return (this->Memory()[i].m_Previous != i) || (this->Memory()[i].m_Next == i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
int MaxElementIndex() const { Assert(0); return this->InvalidIndex(); } // fixedmemory containers don't support iteration from 0..maxelements-1
|
|
|
|
|
void ResetDbgInfo() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice
|
|
|
|
|
template < class T, class I = unsigned short >
|
|
|
|
|
class CUtlBlockLinkedList : public CUtlLinkedList< T, I, true, I, CUtlBlockMemory< UtlLinkedListElem_t< T, I >, I > >
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CUtlBlockLinkedList(int growSize = 0, int initSize = 0)
|
|
|
|
|
: CUtlLinkedList< T, I, true, I, CUtlBlockMemory< UtlLinkedListElem_t< T, I >, I > >(growSize, initSize) {}
|
|
|
|
|
protected:
|
|
|
|
|
void ResetDbgInfo() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// constructor, destructor
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
CUtlLinkedList<T, S, ML, I, M>::CUtlLinkedList(ssize_t growSize, ssize_t initSize) :
|
|
|
|
|
m_Memory(growSize, initSize), m_LastAlloc(m_Memory.InvalidIterator())
|
|
|
|
|
{
|
|
|
|
|
// Prevent signed non-int datatypes
|
|
|
|
|
COMPILE_TIME_ASSERT(sizeof(S) == 8 || (((S)-1) > 0));
|
|
|
|
|
ConstructList();
|
|
|
|
|
ResetDbgInfo();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
CUtlLinkedList<T, S, ML, I, M>::~CUtlLinkedList()
|
|
|
|
|
{
|
|
|
|
|
RemoveAll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
void CUtlLinkedList<T, S, ML, I, M>::ConstructList()
|
|
|
|
|
{
|
|
|
|
|
m_Head = InvalidIndex();
|
|
|
|
|
m_Tail = InvalidIndex();
|
|
|
|
|
m_FirstFree = InvalidIndex();
|
|
|
|
|
m_ElementCount = 0;
|
|
|
|
|
m_NumAlloced = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// gets particular elements
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline T& CUtlLinkedList<T, S, ML, I, M>::Element(I i)
|
|
|
|
|
{
|
|
|
|
|
return m_Memory[i].m_Element;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline T const& CUtlLinkedList<T, S, ML, I, M>::Element(I i) const
|
|
|
|
|
{
|
|
|
|
|
return m_Memory[i].m_Element;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline T& CUtlLinkedList<T, S, ML, I, M>::operator[](I i)
|
|
|
|
|
{
|
|
|
|
|
return m_Memory[i].m_Element;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline T const& CUtlLinkedList<T, S, ML, I, M>::operator[](I i) const
|
|
|
|
|
{
|
|
|
|
|
return m_Memory[i].m_Element;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// list statistics
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline int CUtlLinkedList<T, S, ML, I, M>::Count() const
|
|
|
|
|
{
|
|
|
|
|
#ifdef MULTILIST_PEDANTIC_ASSERTS
|
|
|
|
|
AssertMsg(!ML, "CUtlLinkedList::Count() is meaningless for linked lists.");
|
|
|
|
|
#endif
|
|
|
|
|
return m_ElementCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline I CUtlLinkedList<T, S, ML, I, M>::MaxElementIndex() const
|
|
|
|
|
{
|
|
|
|
|
return m_Memory.NumAllocated();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Traversing the list
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline I CUtlLinkedList<T, S, ML, I, M>::Head() const
|
|
|
|
|
{
|
|
|
|
|
return m_Head;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline I CUtlLinkedList<T, S, ML, I, M>::Tail() const
|
|
|
|
|
{
|
|
|
|
|
return m_Tail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline I CUtlLinkedList<T, S, ML, I, M>::Previous(I i) const
|
|
|
|
|
{
|
|
|
|
|
Assert(IsValidIndex(i));
|
|
|
|
|
return InternalElement(i).m_Previous;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline I CUtlLinkedList<T, S, ML, I, M>::Next(I i) const
|
|
|
|
|
{
|
|
|
|
|
Assert(IsValidIndex(i));
|
|
|
|
|
return InternalElement(i).m_Next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Are nodes in the list or valid?
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
#pragma warning(push)
|
|
|
|
|
#pragma warning( disable: 4310 ) // Allows "(I)(S)M::INVALID_INDEX" below
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline bool CUtlLinkedList<T, S, ML, I, M>::IndexInRange(I index) // Static method
|
|
|
|
|
{
|
|
|
|
|
// Since S is not necessarily the type returned by M, we need to check that M returns indices
|
|
|
|
|
// which are representable by S. A common case is 'S === unsigned short', 'I == int', in which
|
|
|
|
|
// case CUtlMemory will have 'InvalidIndex == (int)-1' (which casts to 65535 in S), and will
|
|
|
|
|
// happily return elements at index 65535 and above.
|
|
|
|
|
|
|
|
|
|
// Do some static checks here:
|
|
|
|
|
// 'I' needs to be able to store 'S'
|
|
|
|
|
COMPILE_TIME_ASSERT(sizeof(I) >= sizeof(S));
|
|
|
|
|
// 'S' should be unsigned (to avoid signed arithmetic errors for plausibly exhaustible ranges)
|
|
|
|
|
COMPILE_TIME_ASSERT((sizeof(S) > 2) || (((S)-1) > 0));
|
|
|
|
|
// M::INVALID_INDEX should be storable in S to avoid ambiguities (e.g. with 65536)
|
|
|
|
|
COMPILE_TIME_ASSERT((M::INVALID_INDEX == -1) || (M::INVALID_INDEX == (S)M::INVALID_INDEX));
|
|
|
|
|
|
|
|
|
|
return (((S)index == index) && ((S)index != InvalidIndex()));
|
|
|
|
|
}
|
|
|
|
|
#pragma warning(pop)
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline bool CUtlLinkedList<T, S, ML, I, M>::IsValidIndex(I i) const
|
|
|
|
|
{
|
|
|
|
|
if (!m_Memory.IsIdxValid(i))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (m_Memory.IsIdxAfter(i, m_LastAlloc))
|
|
|
|
|
return false; // don't read values that have been allocated, but not constructed
|
|
|
|
|
|
|
|
|
|
return (m_Memory[i].m_Previous != i) || (m_Memory[i].m_Next == i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline bool CUtlLinkedList<T, S, ML, I, M>::IsInList(I i) const
|
|
|
|
|
{
|
|
|
|
|
if (!m_Memory.IsIdxValid(i) || m_Memory.IsIdxAfter(i, m_LastAlloc))
|
|
|
|
|
return false; // don't read values that have been allocated, but not constructed
|
|
|
|
|
|
|
|
|
|
return Previous(i) != i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
template <class T>
|
|
|
|
|
inline bool CUtlFixedLinkedList<T>::IsInList( int i ) const
|
|
|
|
|
{
|
|
|
|
|
return m_Memory.IsIdxValid( i ) && (Previous( i ) != i);
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Makes sure we have enough memory allocated to store a requested # of elements
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
template< class T, class S, bool ML, class I, class M >
|
|
|
|
|
void CUtlLinkedList<T, S, ML, I, M>::EnsureCapacity(int num)
|
|
|
|
|
{
|
|
|
|
|
MEM_ALLOC_CREDIT_CLASS();
|
|
|
|
|
m_Memory.EnsureCapacity(num);
|
|
|
|
|
ResetDbgInfo();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template< class T, class S, bool ML, class I, class M >
|
|
|
|
|
void CUtlLinkedList<T, S, ML, I, M>::SetGrowSize(int growSize)
|
|
|
|
|
{
|
|
|
|
|
RemoveAll();
|
|
|
|
|
m_Memory.Init(growSize);
|
|
|
|
|
ResetDbgInfo();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template< class T, class S, bool ML, class I, class M >
|
|
|
|
|
void CUtlLinkedList<T, S, ML, I, M>::SetAllocOwner(const char* pszAllocOwner)
|
|
|
|
|
{
|
|
|
|
|
m_Memory.SetAllocOwner(pszAllocOwner);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Deallocate memory
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
void CUtlLinkedList<T, S, ML, I, M>::Purge()
|
|
|
|
|
{
|
|
|
|
|
RemoveAll();
|
|
|
|
|
|
|
|
|
|
m_Memory.Purge();
|
|
|
|
|
m_FirstFree = InvalidIndex();
|
|
|
|
|
m_NumAlloced = 0;
|
|
|
|
|
|
|
|
|
|
//Routing "m_LastAlloc = m_Memory.InvalidIterator();" through a local const to sidestep an internal compiler error on 360 builds
|
|
|
|
|
const typename M::Iterator_t scInvalidIterator = m_Memory.InvalidIterator();
|
|
|
|
|
m_LastAlloc = scInvalidIterator;
|
|
|
|
|
ResetDbgInfo();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class T, class S, bool ML, class I, class M>
|
|
|
|
|
void CUtlLinkedList<T, S, ML, I, M>::PurgeAndDeleteElements()
|
|
|
|
|
{
|
|
|
|
|
I iNext;
|
|
|
|
|
for (I i = Head(); i != InvalidIndex(); i = iNext)
|
|
|
|
|
{
|
|
|
|
|
iNext = Next(i);
|
|
|
|
|
MemAllocSingleton()->Free(Element(i));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Purge();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Node allocation/deallocation
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
I CUtlLinkedList<T, S, ML, I, M>::AllocInternal(bool multilist)
|
|
|
|
|
{
|
|
|
|
|
Assert(!multilist || ML);
|
|
|
|
|
#ifdef MULTILIST_PEDANTIC_ASSERTS
|
|
|
|
|
Assert(multilist == ML);
|
|
|
|
|
#endif
|
|
|
|
|
I elem;
|
|
|
|
|
if (m_FirstFree == InvalidIndex())
|
|
|
|
|
{
|
|
|
|
|
Assert(m_Memory.IsValidIterator(m_LastAlloc) || m_ElementCount == 0);
|
|
|
|
|
|
|
|
|
|
typename M::Iterator_t it = m_Memory.IsValidIterator(m_LastAlloc) ? m_Memory.Next(m_LastAlloc) : m_Memory.First();
|
|
|
|
|
|
|
|
|
|
if (!m_Memory.IsValidIterator(it))
|
|
|
|
|
{
|
|
|
|
|
MEM_ALLOC_CREDIT_CLASS();
|
|
|
|
|
m_Memory.Grow();
|
|
|
|
|
ResetDbgInfo();
|
|
|
|
|
|
|
|
|
|
it = m_Memory.IsValidIterator(m_LastAlloc) ? m_Memory.Next(m_LastAlloc) : m_Memory.First();
|
|
|
|
|
|
|
|
|
|
Assert(m_Memory.IsValidIterator(it));
|
|
|
|
|
if (!m_Memory.IsValidIterator(it))
|
|
|
|
|
{
|
2022-11-25 23:03:56 +01:00
|
|
|
|
ExecuteNTimes(10, Warning(eDLL_T::COMMON, "CUtlLinkedList overflow! (exhausted memory allocator)\n"));
|
2022-08-13 19:41:45 +02:00
|
|
|
|
return InvalidIndex();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We can overflow before the utlmemory overflows, since S != I
|
|
|
|
|
if (!IndexInRange(m_Memory.GetIndex(it)))
|
|
|
|
|
{
|
2022-11-25 23:03:56 +01:00
|
|
|
|
ExecuteNTimes(10, Warning(eDLL_T::COMMON, "CUtlLinkedList overflow! (exhausted index range)\n"));
|
2022-08-13 19:41:45 +02:00
|
|
|
|
return InvalidIndex();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_LastAlloc = it;
|
|
|
|
|
elem = m_Memory.GetIndex(m_LastAlloc);
|
|
|
|
|
m_NumAlloced++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
elem = m_FirstFree;
|
|
|
|
|
m_FirstFree = InternalElement(m_FirstFree).m_Next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!multilist)
|
|
|
|
|
{
|
|
|
|
|
InternalElement(elem).m_Next = elem;
|
|
|
|
|
InternalElement(elem).m_Previous = elem;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
InternalElement(elem).m_Next = InvalidIndex();
|
|
|
|
|
InternalElement(elem).m_Previous = InvalidIndex();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return elem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
I CUtlLinkedList<T, S, ML, I, M>::Alloc(bool multilist)
|
|
|
|
|
{
|
|
|
|
|
I elem = AllocInternal(multilist);
|
|
|
|
|
if (elem == InvalidIndex())
|
|
|
|
|
return elem;
|
|
|
|
|
|
|
|
|
|
Construct(&Element(elem));
|
|
|
|
|
|
|
|
|
|
return elem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
void CUtlLinkedList<T, S, ML, I, M>::Free(I elem)
|
|
|
|
|
{
|
|
|
|
|
Assert(IsValidIndex(elem) && IndexInRange(elem));
|
|
|
|
|
Unlink(elem);
|
|
|
|
|
|
|
|
|
|
ListElem_t& internalElem = InternalElement(elem);
|
|
|
|
|
Destruct(&internalElem.m_Element);
|
|
|
|
|
internalElem.m_Next = m_FirstFree;
|
|
|
|
|
m_FirstFree = elem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Insertion methods; allocates and links (uses default constructor)
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
I CUtlLinkedList<T, S, ML, I, M>::InsertBefore(I before)
|
|
|
|
|
{
|
|
|
|
|
// Make a new node
|
|
|
|
|
I newNode = AllocInternal();
|
|
|
|
|
if (newNode == InvalidIndex())
|
|
|
|
|
return newNode;
|
|
|
|
|
|
|
|
|
|
// Link it in
|
|
|
|
|
LinkBefore(before, newNode);
|
|
|
|
|
|
|
|
|
|
// Construct the data
|
|
|
|
|
Construct(&Element(newNode));
|
|
|
|
|
|
|
|
|
|
return newNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
I CUtlLinkedList<T, S, ML, I, M>::InsertAfter(I after)
|
|
|
|
|
{
|
|
|
|
|
// Make a new node
|
|
|
|
|
I newNode = AllocInternal();
|
|
|
|
|
if (newNode == InvalidIndex())
|
|
|
|
|
return newNode;
|
|
|
|
|
|
|
|
|
|
// Link it in
|
|
|
|
|
LinkAfter(after, newNode);
|
|
|
|
|
|
|
|
|
|
// Construct the data
|
|
|
|
|
Construct(&Element(newNode));
|
|
|
|
|
|
|
|
|
|
return newNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline I CUtlLinkedList<T, S, ML, I, M>::AddToHead()
|
|
|
|
|
{
|
|
|
|
|
return InsertAfter(InvalidIndex());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline I CUtlLinkedList<T, S, ML, I, M>::AddToTail()
|
|
|
|
|
{
|
|
|
|
|
return InsertBefore(InvalidIndex());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Insertion methods; allocates and links (uses copy constructor)
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
I CUtlLinkedList<T, S, ML, I, M>::InsertBefore(I before, T const& src)
|
|
|
|
|
{
|
|
|
|
|
// Make a new node
|
|
|
|
|
I newNode = AllocInternal();
|
|
|
|
|
if (newNode == InvalidIndex())
|
|
|
|
|
return newNode;
|
|
|
|
|
|
|
|
|
|
// Link it in
|
|
|
|
|
LinkBefore(before, newNode);
|
|
|
|
|
|
|
|
|
|
// Construct the data
|
|
|
|
|
CopyConstruct(&Element(newNode), src);
|
|
|
|
|
|
|
|
|
|
return newNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
I CUtlLinkedList<T, S, ML, I, M>::InsertAfter(I after, T const& src)
|
|
|
|
|
{
|
|
|
|
|
// Make a new node
|
|
|
|
|
I newNode = AllocInternal();
|
|
|
|
|
if (newNode == InvalidIndex())
|
|
|
|
|
return newNode;
|
|
|
|
|
|
|
|
|
|
// Link it in
|
|
|
|
|
LinkAfter(after, newNode);
|
|
|
|
|
|
|
|
|
|
// Construct the data
|
|
|
|
|
CopyConstruct(&Element(newNode), src);
|
|
|
|
|
|
|
|
|
|
return newNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline I CUtlLinkedList<T, S, ML, I, M>::AddToHead(T const& src)
|
|
|
|
|
{
|
|
|
|
|
return InsertAfter(InvalidIndex(), src);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline I CUtlLinkedList<T, S, ML, I, M>::AddToTail(T const& src)
|
|
|
|
|
{
|
|
|
|
|
return InsertBefore(InvalidIndex(), src);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// Removal methods
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
template<class T, class S, bool ML, class I, class M>
|
|
|
|
|
I CUtlLinkedList<T, S, ML, I, M>::Find(const T& src) const
|
|
|
|
|
{
|
|
|
|
|
for (I i = Head(); i != InvalidIndex(); i = Next(i))
|
|
|
|
|
{
|
|
|
|
|
if (Element(i) == src)
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
return InvalidIndex();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class T, class S, bool ML, class I, class M>
|
|
|
|
|
bool CUtlLinkedList<T, S, ML, I, M>::FindAndRemove(const T& src)
|
|
|
|
|
{
|
|
|
|
|
I i = Find(src);
|
|
|
|
|
if (i == InvalidIndex())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Remove(i);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
void CUtlLinkedList<T, S, ML, I, M>::Remove(I elem)
|
|
|
|
|
{
|
|
|
|
|
Free(elem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
void CUtlLinkedList<T, S, ML, I, M>::RemoveAll()
|
|
|
|
|
{
|
|
|
|
|
// Have to do some convoluted stuff to invoke the destructor on all
|
|
|
|
|
// valid elements for the multilist case (since we don't have all elements
|
|
|
|
|
// connected to each other in a list).
|
|
|
|
|
|
|
|
|
|
if (m_LastAlloc == m_Memory.InvalidIterator())
|
|
|
|
|
{
|
|
|
|
|
Assert(m_Head == InvalidIndex());
|
|
|
|
|
Assert(m_Tail == InvalidIndex());
|
|
|
|
|
Assert(m_FirstFree == InvalidIndex());
|
|
|
|
|
Assert(m_ElementCount == 0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ML)
|
|
|
|
|
{
|
|
|
|
|
for (typename M::Iterator_t it = m_Memory.First(); it != m_Memory.InvalidIterator(); it = m_Memory.Next(it))
|
|
|
|
|
{
|
|
|
|
|
I i = m_Memory.GetIndex(it);
|
|
|
|
|
if (IsValidIndex(i)) // skip elements already in the free list
|
|
|
|
|
{
|
|
|
|
|
ListElem_t& internalElem = InternalElement(i);
|
|
|
|
|
Destruct(&internalElem.m_Element);
|
|
|
|
|
internalElem.m_Previous = i;
|
|
|
|
|
internalElem.m_Next = m_FirstFree;
|
|
|
|
|
m_FirstFree = i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (it == m_LastAlloc)
|
|
|
|
|
break; // don't destruct elements that haven't ever been constructed
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
I i = Head();
|
|
|
|
|
I next;
|
|
|
|
|
while (i != InvalidIndex())
|
|
|
|
|
{
|
|
|
|
|
next = Next(i);
|
|
|
|
|
ListElem_t& internalElem = InternalElement(i);
|
|
|
|
|
Destruct(&internalElem.m_Element);
|
|
|
|
|
internalElem.m_Previous = i;
|
|
|
|
|
internalElem.m_Next = next == InvalidIndex() ? m_FirstFree : next;
|
|
|
|
|
i = next;
|
|
|
|
|
}
|
|
|
|
|
if (Head() != InvalidIndex())
|
|
|
|
|
{
|
|
|
|
|
m_FirstFree = Head();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear everything else out
|
|
|
|
|
m_Head = InvalidIndex();
|
|
|
|
|
m_Tail = InvalidIndex();
|
|
|
|
|
m_ElementCount = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
// list modification
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
void CUtlLinkedList<T, S, ML, I, M>::LinkBefore(I before, I elem)
|
|
|
|
|
{
|
|
|
|
|
Assert(IsValidIndex(elem));
|
|
|
|
|
|
|
|
|
|
// Unlink it if it's in the list at the moment
|
|
|
|
|
Unlink(elem);
|
|
|
|
|
|
|
|
|
|
ListElem_t* pNewElem = &InternalElement(elem);
|
|
|
|
|
|
|
|
|
|
// The element *after* our newly linked one is the one we linked before.
|
|
|
|
|
pNewElem->m_Next = before;
|
|
|
|
|
|
2022-09-09 19:47:31 +02:00
|
|
|
|
S newElem_mPrevious; // we need to hang on to this for the comparison against InvalidIndex()
|
2022-08-13 19:41:45 +02:00
|
|
|
|
// below; otherwise we get a a load-hit-store on pNewElem->m_Previous, even
|
|
|
|
|
// with
|
|
|
|
|
if (before == InvalidIndex())
|
|
|
|
|
{
|
|
|
|
|
// In this case, we're linking to the end of the list, so reset the tail
|
|
|
|
|
newElem_mPrevious = m_Tail;
|
|
|
|
|
pNewElem->m_Previous = m_Tail;
|
|
|
|
|
m_Tail = elem;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Here, we're not linking to the end. Set the prev pointer to point to
|
|
|
|
|
// the element we're linking.
|
|
|
|
|
Assert(IsInList(before));
|
|
|
|
|
ListElem_t* beforeElem = &InternalElement(before);
|
|
|
|
|
pNewElem->m_Previous = newElem_mPrevious = beforeElem->m_Previous;
|
|
|
|
|
beforeElem->m_Previous = elem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reset the head if we linked to the head of the list
|
|
|
|
|
if (newElem_mPrevious == InvalidIndex())
|
|
|
|
|
m_Head = elem;
|
|
|
|
|
else
|
|
|
|
|
InternalElement(newElem_mPrevious).m_Next = elem;
|
|
|
|
|
|
|
|
|
|
// one more element baby
|
|
|
|
|
++m_ElementCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
void CUtlLinkedList<T, S, ML, I, M>::LinkAfter(I after, I elem)
|
|
|
|
|
{
|
|
|
|
|
Assert(IsValidIndex(elem));
|
|
|
|
|
|
|
|
|
|
// Unlink it if it's in the list at the moment
|
|
|
|
|
if (IsInList(elem))
|
|
|
|
|
Unlink(elem);
|
|
|
|
|
|
|
|
|
|
ListElem_t& newElem = InternalElement(elem);
|
|
|
|
|
|
|
|
|
|
// The element *before* our newly linked one is the one we linked after
|
|
|
|
|
newElem.m_Previous = after;
|
|
|
|
|
if (after == InvalidIndex())
|
|
|
|
|
{
|
|
|
|
|
// In this case, we're linking to the head of the list, reset the head
|
|
|
|
|
newElem.m_Next = m_Head;
|
|
|
|
|
m_Head = elem;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Here, we're not linking to the end. Set the next pointer to point to
|
|
|
|
|
// the element we're linking.
|
|
|
|
|
Assert(IsInList(after));
|
|
|
|
|
ListElem_t& afterElem = InternalElement(after);
|
|
|
|
|
newElem.m_Next = afterElem.m_Next;
|
|
|
|
|
afterElem.m_Next = elem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reset the tail if we linked to the tail of the list
|
|
|
|
|
if (newElem.m_Next == InvalidIndex())
|
|
|
|
|
m_Tail = elem;
|
|
|
|
|
else
|
|
|
|
|
InternalElement(newElem.m_Next).m_Previous = elem;
|
|
|
|
|
|
|
|
|
|
// one more element baby
|
|
|
|
|
++m_ElementCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
void CUtlLinkedList<T, S, ML, I, M>::Unlink(I elem)
|
|
|
|
|
{
|
|
|
|
|
Assert(IsValidIndex(elem));
|
|
|
|
|
if (IsInList(elem))
|
|
|
|
|
{
|
|
|
|
|
ListElem_t* pOldElem = &m_Memory[elem];
|
|
|
|
|
|
|
|
|
|
// If we're the first guy, reset the head
|
|
|
|
|
// otherwise, make our previous node's next pointer = our next
|
|
|
|
|
if (pOldElem->m_Previous != InvalidIndex())
|
|
|
|
|
{
|
|
|
|
|
m_Memory[pOldElem->m_Previous].m_Next = pOldElem->m_Next;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_Head = pOldElem->m_Next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we're the last guy, reset the tail
|
|
|
|
|
// otherwise, make our next node's prev pointer = our prev
|
|
|
|
|
if (pOldElem->m_Next != InvalidIndex())
|
|
|
|
|
{
|
|
|
|
|
m_Memory[pOldElem->m_Next].m_Previous = pOldElem->m_Previous;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
m_Tail = pOldElem->m_Previous;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This marks this node as not in the list,
|
|
|
|
|
// but not in the free list either
|
|
|
|
|
pOldElem->m_Previous = pOldElem->m_Next = elem;
|
|
|
|
|
|
|
|
|
|
// One less puppy
|
|
|
|
|
--m_ElementCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline void CUtlLinkedList<T, S, ML, I, M>::LinkToHead(I elem)
|
|
|
|
|
{
|
|
|
|
|
LinkAfter(InvalidIndex(), elem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T, class S, bool ML, class I, class M>
|
|
|
|
|
inline void CUtlLinkedList<T, S, ML, I, M>::LinkToTail(I elem)
|
|
|
|
|
{
|
|
|
|
|
LinkBefore(InvalidIndex(), elem);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
2022-09-09 19:47:31 +02:00
|
|
|
|
// Class to drop in to replace a CUtlLinkedList that needs to be more memory aggressive
|
2022-08-13 19:41:45 +02:00
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
DECLARE_POINTER_HANDLE(UtlPtrLinkedListIndex_t); // to enforce correct usage
|
|
|
|
|
|
|
|
|
|
template < typename T >
|
|
|
|
|
class CUtlPtrLinkedList
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
CUtlPtrLinkedList()
|
|
|
|
|
: m_pFirst(NULL),
|
|
|
|
|
m_nElems(0)
|
|
|
|
|
{
|
|
|
|
|
COMPILE_TIME_ASSERT(sizeof(IndexType_t) == sizeof(Node_t*));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~CUtlPtrLinkedList()
|
|
|
|
|
{
|
|
|
|
|
RemoveAll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef UtlPtrLinkedListIndex_t IndexType_t;
|
|
|
|
|
|
|
|
|
|
T& operator[](IndexType_t i)
|
|
|
|
|
{
|
|
|
|
|
return ((Node_t*)i)->elem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const T& operator[](IndexType_t i) const
|
|
|
|
|
{
|
|
|
|
|
return ((Node_t*)i)->elem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IndexType_t AddToTail()
|
|
|
|
|
{
|
|
|
|
|
return DoInsertBefore((IndexType_t)m_pFirst, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IndexType_t AddToTail(T const& src)
|
|
|
|
|
{
|
|
|
|
|
return DoInsertBefore((IndexType_t)m_pFirst, &src);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IndexType_t AddToHead()
|
|
|
|
|
{
|
|
|
|
|
IndexType_t result = DoInsertBefore((IndexType_t)m_pFirst, NULL);
|
|
|
|
|
m_pFirst = ((Node_t*)result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IndexType_t AddToHead(T const& src)
|
|
|
|
|
{
|
|
|
|
|
IndexType_t result = DoInsertBefore((IndexType_t)m_pFirst, &src);
|
|
|
|
|
m_pFirst = ((Node_t*)result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IndexType_t InsertBefore(IndexType_t before)
|
|
|
|
|
{
|
|
|
|
|
return DoInsertBefore(before, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IndexType_t InsertAfter(IndexType_t after)
|
|
|
|
|
{
|
|
|
|
|
Node_t* pBefore = ((Node_t*)after)->next;
|
|
|
|
|
return DoInsertBefore(pBefore, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IndexType_t InsertBefore(IndexType_t before, T const& src)
|
|
|
|
|
{
|
|
|
|
|
return DoInsertBefore(before, &src);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IndexType_t InsertAfter(IndexType_t after, T const& src)
|
|
|
|
|
{
|
|
|
|
|
Node_t* pBefore = ((Node_t*)after)->next;
|
|
|
|
|
return DoInsertBefore(pBefore, &src);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Remove(IndexType_t elem)
|
|
|
|
|
{
|
|
|
|
|
Node_t* p = (Node_t*)elem;
|
|
|
|
|
|
|
|
|
|
if (p->pNext == p)
|
|
|
|
|
{
|
|
|
|
|
m_pFirst = NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (m_pFirst == p)
|
|
|
|
|
{
|
|
|
|
|
m_pFirst = p->pNext;
|
|
|
|
|
}
|
|
|
|
|
p->pNext->pPrev = p->pPrev;
|
|
|
|
|
p->pPrev->pNext = p->pNext;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete p;
|
|
|
|
|
m_nElems--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RemoveAll()
|
|
|
|
|
{
|
|
|
|
|
Node_t* p = m_pFirst;
|
|
|
|
|
if (p)
|
|
|
|
|
{
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
Node_t* pNext = p->pNext;
|
|
|
|
|
delete p;
|
|
|
|
|
p = pNext;
|
|
|
|
|
} while (p != m_pFirst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pFirst = NULL;
|
|
|
|
|
m_nElems = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Count() const
|
|
|
|
|
{
|
|
|
|
|
return m_nElems;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IndexType_t Head() const
|
|
|
|
|
{
|
|
|
|
|
return (IndexType_t)m_pFirst;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IndexType_t Next(IndexType_t i) const
|
|
|
|
|
{
|
|
|
|
|
Node_t* p = ((Node_t*)i)->pNext;
|
|
|
|
|
if (p != m_pFirst)
|
|
|
|
|
{
|
|
|
|
|
return (IndexType_t)p;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsValidIndex(IndexType_t i) const
|
|
|
|
|
{
|
|
|
|
|
Node_t* p = ((Node_t*)i);
|
|
|
|
|
return (p && p->pNext && p->pPrev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline static IndexType_t InvalidIndex()
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
struct Node_t
|
|
|
|
|
{
|
|
|
|
|
Node_t() {}
|
|
|
|
|
Node_t(const T& _elem) : elem(_elem) {}
|
|
|
|
|
|
|
|
|
|
// Have to do it like this instead of 'new' because we have to use the internal memalloc singleton!
|
|
|
|
|
static Node_t* Alloc(const T* _elem)
|
|
|
|
|
{
|
|
|
|
|
Node_t* pNode = MemAllocSingleton()->Alloc<Node_t>(sizeof(Node_t));
|
|
|
|
|
pNode->elem(_elem);
|
|
|
|
|
|
|
|
|
|
return pNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
T elem;
|
|
|
|
|
Node_t* pPrev, * pNext;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Node_t* AllocNode(const T* pCopyFrom)
|
|
|
|
|
{
|
|
|
|
|
MEM_ALLOC_CREDIT_CLASS();
|
|
|
|
|
Node_t* p;
|
|
|
|
|
|
|
|
|
|
if (!pCopyFrom)
|
|
|
|
|
{
|
|
|
|
|
p = MemAllocSingleton()->Alloc<Node_t>(sizeof(Node_t));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
p = Node_t::Alloc(*pCopyFrom);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IndexType_t DoInsertBefore(IndexType_t before, const T* pCopyFrom)
|
|
|
|
|
{
|
|
|
|
|
Node_t* p = AllocNode(pCopyFrom);
|
|
|
|
|
Node_t* pBefore = (Node_t*)before;
|
|
|
|
|
if (pBefore)
|
|
|
|
|
{
|
|
|
|
|
p->pNext = pBefore;
|
|
|
|
|
p->pPrev = pBefore->pPrev;
|
|
|
|
|
pBefore->pPrev = p;
|
|
|
|
|
p->pPrev->pNext = p;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Assert(!m_pFirst);
|
|
|
|
|
m_pFirst = p->pNext = p->pPrev = p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_nElems++;
|
|
|
|
|
return (IndexType_t)p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Node_t* m_pFirst;
|
|
|
|
|
unsigned m_nElems;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
#endif // UTLLINKEDLIST_H
|