/* * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * In addition, as a special exception, the author gives permission to * link the code of this program with the Half-Life Game Engine ("HL * Engine") and Modified Game Libraries ("MODs") developed by Valve, * L.L.C ("Valve"). You must obey the GNU General Public License in all * respects for all of the code used other than the HL Engine and MODs * from Valve. If you modify this file, you may extend this exception * to your version of the file, but you are not obligated to do so. If * you do not wish to do so, delete this exception statement from your * version. * */ #pragma once #include "osconfig.h" #include "basetypes.h" #include "utlmemory.h" // 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)) #define INVALID_LLIST_IDX ((I)~0) // 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. template class CUtlLinkedList { public: typedef T ElemType_t; typedef I IndexType_t; // constructor, destructor CUtlLinkedList(int growSize = 0, int initSize = 0); CUtlLinkedList(void *pMemory, int memsize); ~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); // 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); // 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 inline static I InvalidIndex() { return INVALID_LLIST_IDX; } inline static size_t ElementSize() { return sizeof(ListElem_t); } // list statistics int Count() const; I MaxElementIndex() const; // 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 struct ListElem_t { T m_Element; I m_Previous; I m_Next; private: // No copy constructor for these... ListElem_t(const 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]; } void ResetDbgInfo() { m_pElements = m_Memory.Base(); } // copy constructors not allowed CUtlLinkedList(CUtlLinkedList const& list) { Assert(0); } CUtlMemory m_Memory; I m_Head; I m_Tail; I m_FirstFree; I m_ElementCount; // The number actually in the list I m_TotalElements; // The number allocated // For debugging purposes; // it's in release builds so this can be used in libraries correctly ListElem_t *m_pElements; }; // Constructor, Destructor template CUtlLinkedList::CUtlLinkedList(int growSize, int initSize) : m_Memory(growSize, initSize) { ConstructList(); ResetDbgInfo(); } template CUtlLinkedList::CUtlLinkedList(void *pMemory, int memsize) : m_Memory((ListElem_t *)pMemory, memsize / sizeof(ListElem_t)) { ConstructList(); ResetDbgInfo(); } template CUtlLinkedList::~CUtlLinkedList() { RemoveAll(); } template void CUtlLinkedList::ConstructList() { m_Head = InvalidIndex(); m_Tail = InvalidIndex(); m_FirstFree = InvalidIndex(); m_ElementCount = m_TotalElements = 0; } // Gets particular elements 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; } // List statistics template inline int CUtlLinkedList::Count() const { return m_ElementCount; } template inline I CUtlLinkedList::MaxElementIndex() const { return m_Memory.NumAllocated(); } // Traversing the list 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; } // Are nodes in the list or valid? template inline bool CUtlLinkedList::IsValidIndex(I i) const { return (i < m_TotalElements) && (i >= 0) && ((m_Memory[i].m_Previous != i) || (m_Memory[i].m_Next == i)); } template inline bool CUtlLinkedList::IsInList(I i) const { return (i < m_TotalElements) && (i >= 0) && (Previous(i) != i); } // Makes sure we have enough memory allocated to store a requested # of elements template< class T, class I > void CUtlLinkedList::EnsureCapacity(int num) { m_Memory.EnsureCapacity(num); ResetDbgInfo(); } // Deallocate memory template void CUtlLinkedList::Purge() { RemoveAll(); m_Memory.Purge(); m_FirstFree = InvalidIndex(); m_TotalElements = 0; ResetDbgInfo(); } template void CUtlLinkedList::PurgeAndDeleteElements() { int iNext; for (int i = Head(); i != InvalidIndex(); i = iNext) { iNext = Next(i); delete Element(i); } Purge(); } // Node allocation/deallocation template I CUtlLinkedList::AllocInternal(bool multilist) { I elem; if (m_FirstFree == InvalidIndex()) { // Nothing in the free list; add. // Since nothing is in the free list, m_TotalElements == total # of elements // the list knows about. if (m_TotalElements == m_Memory.NumAllocated()) m_Memory.Grow(); Assert(m_TotalElements != InvalidIndex()); elem = (I)m_TotalElements; m_TotalElements++; if (elem == InvalidIndex()) { Error("CUtlLinkedList overflow!\n"); } } else { elem = m_FirstFree; m_FirstFree = InternalElement(m_FirstFree).m_Next; } if (!multilist) InternalElement(elem).m_Next = InternalElement(elem).m_Previous = elem; else InternalElement(elem).m_Next = InternalElement(elem).m_Previous = InvalidIndex(); ResetDbgInfo(); return elem; } template I CUtlLinkedList::Alloc(bool multilist) { I elem = AllocInternal(multilist); Construct(&Element(elem)); return elem; } template void CUtlLinkedList::Free(I elem) { Assert(IsValidIndex(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 I CUtlLinkedList::InsertBefore(I before) { // Make a new node I newNode = AllocInternal(); // Link it in LinkBefore(before, newNode); // Construct the data Construct(&Element(newNode)); return newNode; } template I CUtlLinkedList::InsertAfter(I after) { // Make a new node I newNode = AllocInternal(); // Link it in LinkAfter(after, newNode); // Construct the data Construct(&Element(newNode)); return newNode; } template inline I CUtlLinkedList::AddToHead() { return InsertAfter(InvalidIndex()); } template inline I CUtlLinkedList::AddToTail() { return InsertBefore(InvalidIndex()); } // Insertion methods; allocates and links (uses copy constructor) template I CUtlLinkedList::InsertBefore(I before, T const& src) { // Make a new node I newNode = AllocInternal(); // Link it in LinkBefore(before, newNode); // Construct the data CopyConstruct(&Element(newNode), src); return newNode; } template I CUtlLinkedList::InsertAfter(I after, T const& src) { // Make a new node I newNode = AllocInternal(); // Link it in LinkAfter(after, newNode); // Construct the data 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); } // Removal methods template I CUtlLinkedList::Find(const T &src) const { for (I i = Head(); i != InvalidIndex(); i = Next(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_TotalElements == 0) return; // Put everything into the free list I prev = InvalidIndex(); for (int i = (int)m_TotalElements; --i >= 0;) { // Invoke the destructor if (IsValidIndex((I)i)) Destruct(&Element((I)i)); // next points to the next free list item InternalElement((I)i).m_Next = prev; // Indicates it's in the free list InternalElement((I)i).m_Previous = (I)i; prev = (I)i; } // First free points to the first element m_FirstFree = 0; // Clear everything else out m_Head = InvalidIndex(); m_Tail = InvalidIndex(); m_ElementCount = 0; } // list modification template void CUtlLinkedList::LinkBefore(I before, I elem) { Assert(IsValidIndex(elem)); // Unlink it if it's in the list at the moment Unlink(elem); ListElem_t& newElem = InternalElement(elem); // The element *after* our newly linked one is the one we linked before. newElem.m_Next = before; if (before == InvalidIndex()) { // In this case, we're linking to the end of the list, so reset the tail newElem.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); newElem.m_Previous = beforeElem.m_Previous; beforeElem.m_Previous = elem; } // Reset the head if we linked to the head of the list if (newElem.m_Previous == InvalidIndex()) m_Head = elem; else InternalElement(newElem.m_Previous).m_Next = elem; // one more element baby m_ElementCount++; } template void CUtlLinkedList::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 void CUtlLinkedList::Unlink(I elem) { Assert(IsValidIndex(elem)); if (IsInList(elem)) { ListElem_t *pBase = m_Memory.Base(); ListElem_t *pOldElem = &pBase[elem]; // If we're the first guy, reset the head // otherwise, make our previous node's next pointer = our next if (pOldElem->m_Previous != INVALID_LLIST_IDX) { pBase[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 != INVALID_LLIST_IDX) { pBase[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 inline void CUtlLinkedList::LinkToHead(I elem) { LinkAfter(InvalidIndex(), elem); } template inline void CUtlLinkedList::LinkToTail(I elem) { LinkBefore(InvalidIndex(), elem); }