|
|
//========= 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( auto iteratorName=(listName).Head(); (listName).IsUtlLinkedList && iteratorName != (listName).InvalidIndex(); iteratorName = (listName).Next( iteratorName ) )
#define FOR_EACH_LL_BACK( listName, iteratorName ) \
for( auto iteratorName=(listName).Tail(); (listName).IsUtlLinkedList && iteratorName != (listName).InvalidIndex(); iteratorName = (listName).Previous( 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; enum { IsUtlLinkedList = true }; // Used to match this at compiletime
// constructor, destructor
CUtlLinkedList( int growSize = 0, int initSize = 0 ); ~CUtlLinkedList();
CUtlLinkedList( const CUtlLinkedList& ) = delete; CUtlLinkedList& operator=( const CUtlLinkedList& ) = delete;
// 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? (representible 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; inline bool IsEmpty( void ) const { return ( Head() == InvalidIndex() ); }
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;
// STL compatible const_iterator class
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;
// Default constructor -- gives a currently unusable iterator.
_CUtlLinkedList_constiterator_t() : m_list( 0 ) , m_index( List_t::InvalidIndex() ) { } // Normal constructor.
_CUtlLinkedList_constiterator_t( const List_t& list, IndexType_t index ) : m_list( &list ) , m_index( index ) { }
// Pre-increment operator++. This is the most efficient increment
// operator so it should always be used.
_CUtlLinkedList_constiterator_t& operator++() { m_index = m_list->Next( m_index ); return *this; } // Post-increment operator++. This is less efficient than pre-increment.
_CUtlLinkedList_constiterator_t operator++(int) { // Copy ourselves.
_CUtlLinkedList_constiterator_t temp = *this; // Increment ourselves.
++*this; // Return the copy.
return temp; }
// Pre-decrement operator--. This is the most efficient decrement
// operator so it should always be used.
_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; } // Post-decrement operator--. This is less efficient than post-decrement.
_CUtlLinkedList_constiterator_t operator--(int) { // Copy ourselves.
_CUtlLinkedList_constiterator_t temp = *this; // Decrement ourselves.
--*this; // Return the copy.
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: // Use a pointer rather than a reference so that we can support
// assignment of iterators.
const List_t* m_list; IndexType_t m_index; };
// STL compatible iterator class, using derivation so that a non-const
// list can return a const_iterator.
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;
// Default constructor -- gives a currently unusable iterator.
_CUtlLinkedList_iterator_t() { } // Normal constructor.
_CUtlLinkedList_iterator_t( const List_t& list, IndexType_t index ) : _CUtlLinkedList_constiterator_t< List_t >( list, index ) { }
// Pre-increment operator++. This is the most efficient increment
// operator so it should always be used.
_CUtlLinkedList_iterator_t& operator++() { Base::m_index = Base::m_list->Next( Base::m_index ); return *this; } // Post-increment operator++. This is less efficient than pre-increment.
_CUtlLinkedList_iterator_t operator++(int) { // Copy ourselves.
_CUtlLinkedList_iterator_t temp = *this; // Increment ourselves.
++*this; // Return the copy.
return temp; }
// Pre-decrement operator--. This is the most efficient decrement
// operator so it should always be used.
_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; } // Post-decrement operator--. This is less efficient than post-decrement.
_CUtlLinkedList_iterator_t operator--(int) { // Copy ourselves.
_CUtlLinkedList_iterator_t temp = *this; // Decrement ourselves.
--*this; // Return the copy.
return temp; }
ElemType_t& operator*() const { // Const_cast to allow sharing the implementation with the
// base class.
List_t* pMutableList = const_cast<List_t*>( Base::m_list ); return pMutableList->Element( Base::m_index ); }
ElemType_t* operator->() const { return (&**this); } };
typedef _CUtlLinkedList_constiterator_t<CUtlLinkedList<T, S, ML, I, M> > const_iterator; typedef _CUtlLinkedList_iterator_t<CUtlLinkedList<T, S, ML, I, M> > 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() ); }
// 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 ) RESTRICT; 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]; }
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, intp, true, intp, CUtlFixedMemory< UtlLinkedListElem_t< T, intp > > > { public: CUtlFixedLinkedList( int growSize = 0, int initSize = 0 ) : CUtlLinkedList< T, intp, true, intp, CUtlFixedMemory< UtlLinkedListElem_t< T, intp > > >( growSize, initSize ) {}
bool IsValidIndex( intp 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( int growSize, int initSize ) : m_Memory( growSize, initSize ), m_LastAlloc( m_Memory.InvalidIterator() ) { #if !defined( PLATFORM_WINDOWS_PC64 ) && !defined( PLATFORM_64BITS )
// Prevent signed non-int datatypes
COMPILE_TIME_ASSERT( sizeof(S) == 4 || ( ( (S)-1 ) > 0 ) ); #endif
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'
// These COMPILE_TIME_ASSERT checks need to be in individual scopes to avoid build breaks
// on MacOS and Linux due to a gcc bug.
{ 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); delete 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 ) RESTRICT { 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 ) ) { // We rarely if ever handle alloc failure. Continuing leads to corruption.
Error( "CUtlLinkedList overflow! (exhausted memory allocator)\n" ); return InvalidIndex(); } }
// We can overflow before the utlmemory overflows, since S != I
if ( !IndexInRange( m_Memory.GetIndex( it ) ) ) { // We rarely if ever handle alloc failure. Continuing leads to corruption.
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 <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 * RESTRICT 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 compairson against InvalidIndex()
// below; otherwise we get a a load-hit-store on pNewElem->m_Previous, even
// with RESTRICT
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 * RESTRICT 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 * RESTRICT 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 ); }
//-----------------------------------------------------------------------------
// Class to drop in to replace a CUtlLinkedList that needs to be more memory agressive
//-----------------------------------------------------------------------------
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 ) {}
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 // UTLLINKEDLIST_H
|