#ifndef UTLLINKEDLIST_H #define UTLLINKEDLIST_H #ifdef _WIN32 #pragma once #endif #include "basetypes.h" #include "utlmemory.h" #include "utlfixedmemory.h" #include "utlblockmemory.h" #include "dbg.h" #define FOR_EACH_LL( listName, iteratorName ) \ for( int iteratorName=(listName).Head(); (listName).IsUtlLinkedList && iteratorName != (listName).InvalidIndex(); iteratorName = (listName).Next( iteratorName ) ) template struct UtlLinkedListElem_t { T m_Element; I m_Previous; I m_Next; private: UtlLinkedListElem_t(const UtlLinkedListElem_t&); }; template , I > > class CUtlLinkedList { public: typedef T ElemType_t; typedef S IndexType_t; typedef I IndexLocalType_t; typedef M MemoryAllocator_t; static const bool IsUtlLinkedList = true; CUtlLinkedList(int growSize = 0, int initSize = 0); ~CUtlLinkedList(); T& Element(I i); T const& Element(I i) const; T& operator[](I i); T const& operator[](I i) const; void EnsureCapacity(int num); void SetGrowSize(int growSize); void Purge(); void PurgeAndDeleteElements(); 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); I Find(const T& src) const; bool FindAndRemove(const T& src); void Remove(I elem); void RemoveAll(); I Alloc(bool multilist = false); void Free(I elem); void LinkBefore(I before, I elem); void LinkAfter(I after, I elem); void Unlink(I elem); void LinkToHead(I elem); void LinkToTail(I elem); inline static S InvalidIndex() { return (S)M::InvalidIndex(); } static bool IndexInRange(I index); inline static size_t ElementSize() { return sizeof(ListElem_t); } int Count() const; I MaxElementIndex() const; I NumAllocated(void) const { return m_NumAlloced; } I Head() const; I Tail() const; I Previous(I i) const; I Next(I i) const; template < typename List_t > class _CUtlLinkedList_constiterator_t { public: typedef typename List_t::ElemType_t ElemType_t; typedef typename List_t::IndexType_t IndexType_t; _CUtlLinkedList_constiterator_t() : m_list(0) , m_index(List_t::InvalidIndex()) { } _CUtlLinkedList_constiterator_t(const List_t& list, IndexType_t index) : m_list(&list) , m_index(index) { } _CUtlLinkedList_constiterator_t& operator++() { m_index = m_list->Next(m_index); return *this; } _CUtlLinkedList_constiterator_t operator++(int) { _CUtlLinkedList_constiterator_t temp = *this; ++* this; return temp; } _CUtlLinkedList_constiterator_t& operator--() { Assert(m_index != m_list->Head()); if (m_index == m_list->InvalidIndex()) { m_index = m_list->Tail(); } else { m_index = m_list->Previous(m_index); } return *this; } _CUtlLinkedList_constiterator_t operator--(int) { _CUtlLinkedList_constiterator_t temp = *this; --* this; return temp; } bool operator==(const _CUtlLinkedList_constiterator_t& other) const { Assert(m_list == other.m_list); return m_index == other.m_index; } bool operator!=(const _CUtlLinkedList_constiterator_t& other) const { Assert(m_list == other.m_list); return m_index != other.m_index; } const ElemType_t& operator*() const { return m_list->Element(m_index); } const ElemType_t* operator->() const { return (&**this); } protected: const List_t* m_list; IndexType_t m_index; }; template < typename List_t > class _CUtlLinkedList_iterator_t : public _CUtlLinkedList_constiterator_t< List_t > { public: typedef typename List_t::ElemType_t ElemType_t; typedef typename List_t::IndexType_t IndexType_t; typedef _CUtlLinkedList_constiterator_t< List_t > Base; _CUtlLinkedList_iterator_t() { } _CUtlLinkedList_iterator_t(const List_t& list, IndexType_t index) : _CUtlLinkedList_constiterator_t< List_t >(list, index) { } _CUtlLinkedList_iterator_t& operator++() { Base::m_index = Base::m_list->Next(Base::m_index); return *this; } _CUtlLinkedList_iterator_t operator++(int) { _CUtlLinkedList_iterator_t temp = *this; ++* this; return temp; } _CUtlLinkedList_iterator_t& operator--() { Assert(Base::m_index != Base::m_list->Head()); if (Base::m_index == Base::m_list->InvalidIndex()) { Base::m_index = Base::m_list->Tail(); } else { Base::m_index = Base::m_list->Previous(Base::m_index); } return *this; } _CUtlLinkedList_iterator_t operator--(int) { _CUtlLinkedList_iterator_t temp = *this; --* this; return temp; } ElemType_t& operator*() const { List_t* pMutableList = const_cast(Base::m_list); return pMutableList->Element(Base::m_index); } ElemType_t* operator->() const { return (&**this); } }; typedef _CUtlLinkedList_constiterator_t > const_iterator; typedef _CUtlLinkedList_iterator_t > iterator; const_iterator begin() const { return const_iterator(*this, Head()); } iterator begin() { return iterator(*this, Head()); } const_iterator end() const { return const_iterator(*this, InvalidIndex()); } iterator end() { return iterator(*this, InvalidIndex()); } bool IsValidIndex(I i) const; bool IsInList(I i) const; protected: typedef UtlLinkedListElem_t ListElem_t; I AllocInternal(bool multilist = false); void ConstructList(); ListElem_t& InternalElement(I i) { return m_Memory[i]; } ListElem_t const& InternalElement(I i) const { return m_Memory[i]; } CUtlLinkedList(CUtlLinkedList const& list) { Assert(0); } M m_Memory; I m_Head; I m_Tail; I m_FirstFree; I m_ElementCount; I m_NumAlloced; typename M::Iterator_t m_LastAlloc; ListElem_t* m_pElements; FORCEINLINE M const& Memory(void) const { return m_Memory; } void ResetDbgInfo() { m_pElements = m_Memory.Base(); } private: I PrivateNext(I i) const; }; template < class T > class CUtlFixedLinkedList : public CUtlLinkedList< T, int, true, int, CUtlFixedMemory< UtlLinkedListElem_t< T, int > > > { public: CUtlFixedLinkedList(int growSize = 0, int initSize = 0) : CUtlLinkedList< T, int, true, int, CUtlFixedMemory< UtlLinkedListElem_t< T, int > > >(growSize, initSize) {} typedef CUtlLinkedList< T, int, true, int, CUtlFixedMemory< UtlLinkedListElem_t< T, int > > > BaseClass; bool IsValidIndex(int i) const { if (!BaseClass::Memory().IsIdxValid(i)) return false; #ifdef _DEBUG if (BaseClass::Memory().IsIdxAfter(i, this->m_LastAlloc)) { Assert(0); return false; } #endif return (BaseClass::Memory()[i].m_Previous != i) || (BaseClass::Memory()[i].m_Next == i); } private: int MaxElementIndex() const { Assert(0); return BaseClass::InvalidIndex(); } void ResetDbgInfo() {} }; 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() {} }; template CUtlLinkedList::CUtlLinkedList(int growSize, int initSize) : m_Memory(growSize, initSize), m_LastAlloc(m_Memory.InvalidIterator()) { COMPILE_TIME_ASSERT(sizeof(S) == 4 || (((S)-1) > 0)); ConstructList(); ResetDbgInfo(); } template CUtlLinkedList::~CUtlLinkedList() { RemoveAll(); } template void CUtlLinkedList::ConstructList() { m_Head = InvalidIndex(); m_Tail = InvalidIndex(); m_FirstFree = InvalidIndex(); m_ElementCount = 0; m_NumAlloced = 0; } template inline T& CUtlLinkedList::Element(I i) { return m_Memory[i].m_Element; } template inline T const& CUtlLinkedList::Element(I i) const { return m_Memory[i].m_Element; } template inline T& CUtlLinkedList::operator[](I i) { return m_Memory[i].m_Element; } template inline T const& CUtlLinkedList::operator[](I i) const { return m_Memory[i].m_Element; } template inline int CUtlLinkedList::Count() const { #ifdef MULTILIST_PEDANTIC_ASSERTS AssertMsg(!ML, "CUtlLinkedList::Count() is meaningless for linked lists."); #endif return m_ElementCount; } template inline I CUtlLinkedList::MaxElementIndex() const { return m_Memory.NumAllocated(); } template inline I CUtlLinkedList::Head() const { return m_Head; } template inline I CUtlLinkedList::Tail() const { return m_Tail; } template inline I CUtlLinkedList::Previous(I i) const { Assert(IsValidIndex(i)); return InternalElement(i).m_Previous; } template inline I CUtlLinkedList::Next(I i) const { Assert(IsValidIndex(i)); return InternalElement(i).m_Next; } template inline I CUtlLinkedList::PrivateNext(I i) const { return InternalElement(i).m_Next; } #pragma warning(push) #pragma warning( disable: 4310 ) template inline bool CUtlLinkedList::IndexInRange(I index) { COMPILE_TIME_ASSERT(sizeof(I) >= sizeof(S)); COMPILE_TIME_ASSERT((sizeof(S) > 2) || (((S)-1) > 0)); 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 inline bool CUtlLinkedList::IsValidIndex(I i) const { if (!m_Memory.IsIdxValid(i)) return false; if (m_Memory.IsIdxAfter(i, m_LastAlloc)) return false; return (m_Memory[i].m_Previous != i) || (m_Memory[i].m_Next == i); } template inline bool CUtlLinkedList::IsInList(I i) const { if (!m_Memory.IsIdxValid(i) || m_Memory.IsIdxAfter(i, m_LastAlloc)) return false; return Previous(i) != i; } template< class T, class S, bool ML, class I, class M > void CUtlLinkedList::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::SetGrowSize(int growSize) { RemoveAll(); m_Memory.Init(growSize); ResetDbgInfo(); } template void CUtlLinkedList::Purge() { RemoveAll(); m_Memory.Purge(); m_FirstFree = InvalidIndex(); m_NumAlloced = 0; const typename M::Iterator_t scInvalidIterator = m_Memory.InvalidIterator(); m_LastAlloc = scInvalidIterator; ResetDbgInfo(); } template void CUtlLinkedList::PurgeAndDeleteElements() { I iNext; for (I i = Head(); i != InvalidIndex(); i = iNext) { iNext = Next(i); delete Element(i); } Purge(); } template I CUtlLinkedList::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)) { Error("CUtlLinkedList overflow! (exhausted memory allocator)\n"); return InvalidIndex(); } } if (!IndexInRange(m_Memory.GetIndex(it))) { Error("CUtlLinkedList overflow! (exhausted index range)\n"); 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 I CUtlLinkedList::Alloc(bool multilist) { I elem = AllocInternal(multilist); if (elem == InvalidIndex()) return elem; Construct(&Element(elem)); return elem; } template void CUtlLinkedList::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; } template I CUtlLinkedList::InsertBefore(I before) { I newNode = AllocInternal(); if (newNode == InvalidIndex()) return newNode; LinkBefore(before, newNode); Construct(&Element(newNode)); return newNode; } template I CUtlLinkedList::InsertAfter(I after) { I newNode = AllocInternal(); if (newNode == InvalidIndex()) return newNode; LinkAfter(after, newNode); Construct(&Element(newNode)); return newNode; } template inline I CUtlLinkedList::AddToHead() { return InsertAfter(InvalidIndex()); } template inline I CUtlLinkedList::AddToTail() { return InsertBefore(InvalidIndex()); } template I CUtlLinkedList::InsertBefore(I before, T const& src) { I newNode = AllocInternal(); if (newNode == InvalidIndex()) return newNode; LinkBefore(before, newNode); CopyConstruct(&Element(newNode), src); return newNode; } template I CUtlLinkedList::InsertAfter(I after, T const& src) { I newNode = AllocInternal(); if (newNode == InvalidIndex()) return newNode; LinkAfter(after, newNode); CopyConstruct(&Element(newNode), src); return newNode; } template inline I CUtlLinkedList::AddToHead(T const& src) { return InsertAfter(InvalidIndex(), src); } template inline I CUtlLinkedList::AddToTail(T const& src) { return InsertBefore(InvalidIndex(), src); } template I CUtlLinkedList::Find(const T& src) const { I invalidIndex = InvalidIndex(); for (I i = Head(); i != invalidIndex; i = PrivateNext(i)) { if (Element(i) == src) return i; } return InvalidIndex(); } template bool CUtlLinkedList::FindAndRemove(const T& src) { I i = Find(src); if (i == InvalidIndex()) { return false; } else { Remove(i); return true; } } template void CUtlLinkedList::Remove(I elem) { Free(elem); } template void CUtlLinkedList::RemoveAll() { 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)) { 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; } } 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(); } } m_Head = InvalidIndex(); m_Tail = InvalidIndex(); m_ElementCount = 0; } template void CUtlLinkedList::LinkBefore(I before, I elem) { Assert(IsValidIndex(elem)); Unlink(elem); ListElem_t* RESTRICT pNewElem = &InternalElement(elem); pNewElem->m_Next = before; S newElem_mPrevious; if (before == InvalidIndex()) { newElem_mPrevious = m_Tail; pNewElem->m_Previous = m_Tail; m_Tail = elem; } else { Assert(IsInList(before)); ListElem_t* RESTRICT beforeElem = &InternalElement(before); pNewElem->m_Previous = newElem_mPrevious = beforeElem->m_Previous; beforeElem->m_Previous = elem; } if (newElem_mPrevious == InvalidIndex()) m_Head = elem; else InternalElement(newElem_mPrevious).m_Next = elem; ++m_ElementCount; } template void CUtlLinkedList::LinkAfter(I after, I elem) { Assert(IsValidIndex(elem)); if (IsInList(elem)) Unlink(elem); ListElem_t& newElem = InternalElement(elem); newElem.m_Previous = after; if (after == InvalidIndex()) { newElem.m_Next = m_Head; m_Head = elem; } else { Assert(IsInList(after)); ListElem_t& afterElem = InternalElement(after); newElem.m_Next = afterElem.m_Next; afterElem.m_Next = elem; } if (newElem.m_Next == InvalidIndex()) m_Tail = elem; else InternalElement(newElem.m_Next).m_Previous = elem; ++m_ElementCount; } template void CUtlLinkedList::Unlink(I elem) { Assert(IsValidIndex(elem)); if (IsInList(elem)) { ListElem_t* RESTRICT pOldElem = &m_Memory[elem]; if (pOldElem->m_Previous != InvalidIndex()) { m_Memory[pOldElem->m_Previous].m_Next = pOldElem->m_Next; } else { m_Head = pOldElem->m_Next; } if (pOldElem->m_Next != InvalidIndex()) { m_Memory[pOldElem->m_Next].m_Previous = pOldElem->m_Previous; } else { m_Tail = pOldElem->m_Previous; } pOldElem->m_Previous = pOldElem->m_Next = elem; --m_ElementCount; } } template inline void CUtlLinkedList::LinkToHead(I elem) { LinkAfter(InvalidIndex(), elem); } template inline void CUtlLinkedList::LinkToTail(I elem) { LinkBefore(InvalidIndex(), elem); } DECLARE_POINTER_HANDLE(UtlPtrLinkedListIndex_t); 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) {} T elem; Node_t* pPrev, * pNext; }; Node_t* AllocNode(const T* pCopyFrom) { MEM_ALLOC_CREDIT_CLASS(); Node_t* p; if (!pCopyFrom) { p = new Node_t; } else { p = new Node_t(*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