//======= Copyright � 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 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 , 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(); } // Is a given index valid to use? (representable by S and not the invalid index) 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 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 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 CUtlLinkedList::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 CUtlLinkedList::~CUtlLinkedList() { RemoveAll(); } template void CUtlLinkedList::ConstructList() { m_Head = InvalidIndex(); m_Tail = InvalidIndex(); m_FirstFree = InvalidIndex(); m_ElementCount = 0; m_NumAlloced = 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 { #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(); } //----------------------------------------------------------------------------- // 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? //----------------------------------------------------------------------------- #pragma warning(push) #pragma warning( disable: 4310 ) // Allows "(I)(S)M::INVALID_INDEX" below template inline bool CUtlLinkedList::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 inline bool CUtlLinkedList::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 inline bool CUtlLinkedList::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 inline bool CUtlFixedLinkedList::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::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< class T, class S, bool ML, class I, class M > void CUtlLinkedList::SetAllocOwner(const char* pszAllocOwner) { m_Memory.SetAllocOwner(pszAllocOwner); } //----------------------------------------------------------------------------- // Deallocate memory //----------------------------------------------------------------------------- template void CUtlLinkedList::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 void CUtlLinkedList::PurgeAndDeleteElements() { I iNext; for (I i = Head(); i != InvalidIndex(); i = iNext) { iNext = Next(i); MemAllocSingleton()->Free(Element(i)); } Purge(); } //----------------------------------------------------------------------------- // Node allocation/deallocation //----------------------------------------------------------------------------- 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)) { ExecuteNTimes(10, Warning(eDLL_T::COMMON, "CUtlLinkedList overflow! (exhausted memory allocator)\n")); return InvalidIndex(); } } // We can overflow before the utlmemory overflows, since S != I if (!IndexInRange(m_Memory.GetIndex(it))) { ExecuteNTimes(10, Warning(eDLL_T::COMMON, "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; } //----------------------------------------------------------------------------- // Insertion methods; allocates and links (uses default constructor) //----------------------------------------------------------------------------- template I CUtlLinkedList::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 I CUtlLinkedList::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 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(); if (newNode == InvalidIndex()) return newNode; // 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(); if (newNode == InvalidIndex()) return newNode; // 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() { // 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 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* pNewElem = &InternalElement(elem); // The element *after* our newly linked one is the one we linked before. pNewElem->m_Next = before; S newElem_mPrevious; // we need to hang on to this for the comparison against InvalidIndex() // 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 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* 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 inline void CUtlLinkedList::LinkToHead(I elem) { LinkAfter(InvalidIndex(), elem); } template inline void CUtlLinkedList::LinkToTail(I elem) { LinkBefore(InvalidIndex(), elem); } //----------------------------------------------------------------------------- // Class to drop in to replace a CUtlLinkedList that needs to be more memory aggressive //----------------------------------------------------------------------------- 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(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(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