//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #ifndef UTLQUEUE_H #define UTLQUEUE_H #ifdef _WIN32 #pragma once #endif #include "utlmemory.h" //#define TEST_UTLQUEUE enum QueueIter_t { QUEUE_ITERATOR_INVALID = 0xffffffff }; // T is the type stored in the queue template< class T, class M = CUtlMemory< T > > class CUtlQueue { public: CUtlQueue( int growSize = 0, int initSize = 0 ); CUtlQueue( T *pMemory, int numElements ); // return the item from the front of the queue and delete it T RemoveAtHead(); bool RemoveAtHead( T &removedElement ); // return the item from the end of the queue and delete it T RemoveAtTail(); bool RemoveAtTail( T &removedElement ); // return item at the front of the queue T const& Head() const; // return item at the end of the queue T const& Tail() const; // Add a new item to the end of the queue void Insert( T const &element ); // checks if an element of this value already exists on the stack, returns true if it does bool Check( T const element ) const; // iterators may be invalidated by Insert() QueueIter_t First() const; QueueIter_t Next( QueueIter_t it ) const; QueueIter_t Last() const; QueueIter_t Previous( QueueIter_t it ) const; bool IsValid( QueueIter_t it ) const; T const& Element( QueueIter_t it ) const; // Returns the count of elements in the queue int Count() const; // Return whether the queue is empty or not, faster than Count(). bool IsEmpty() const; // doesn't deallocate memory void RemoveAll(); // Memory deallocation void Purge(); protected: QueueIter_t Next_Unchecked( QueueIter_t it ) const; QueueIter_t Previous_Unchecked( QueueIter_t it ) const; M m_memory; // if m_head == m_tail == QUEUE_ITERATOR_INVALID, then the queue is empty QueueIter_t m_head; QueueIter_t m_tail; #ifdef TEST_UTLQUEUE friend void CUtlQueue_Test(); #endif }; //----------------------------------------------------------------------------- // The CUtlQueueFixed class: // A queue class with a fixed allocation scheme //----------------------------------------------------------------------------- template< class T, size_t MAX_SIZE > class CUtlQueueFixed : public CUtlQueue< T, CUtlMemoryFixed > { typedef CUtlQueue< T, CUtlMemoryFixed > BaseClass; public: // constructor, destructor CUtlQueueFixed( int growSize = 0, int initSize = 0 ) : BaseClass( growSize, initSize ) {} CUtlQueueFixed( T* pMemory, int numElements ) : BaseClass( pMemory, numElements ) {} }; template< class T, class M > inline CUtlQueue::CUtlQueue( int growSize, int initSize ) : m_memory( growSize, initSize ), m_head( QUEUE_ITERATOR_INVALID ), m_tail( QUEUE_ITERATOR_INVALID ) { } template< class T, class M > inline CUtlQueue::CUtlQueue( T *pMemory, int numElements ) : m_memory( pMemory, numElements ), m_head( QUEUE_ITERATOR_INVALID ), m_tail( QUEUE_ITERATOR_INVALID ) { } template inline T CUtlQueue::RemoveAtHead() { T temp; RemoveAtHead( temp ); return temp; } template inline bool CUtlQueue::RemoveAtHead( T &removedElement ) { Assert( m_head != QUEUE_ITERATOR_INVALID ); if ( m_head == QUEUE_ITERATOR_INVALID ) { Construct( &removedElement ); return false; } QueueIter_t it = m_head; removedElement = m_memory[ it ]; Destruct( &m_memory[ it ] ); if ( m_head == m_tail ) { m_head = m_tail = QUEUE_ITERATOR_INVALID; } else { m_head = Next_Unchecked( m_head ); } return true; } template inline T CUtlQueue::RemoveAtTail() { T temp; RemoveAtTail( temp ); return temp; } template inline bool CUtlQueue::RemoveAtTail( T &removedElement ) { Assert( m_tail != QUEUE_ITERATOR_INVALID ); if ( m_tail == QUEUE_ITERATOR_INVALID ) { Construct( &removedElement ); return false; } removedElement = m_memory[ m_tail ]; Destruct( &m_memory[ m_tail ] ); if ( m_head == m_tail ) { m_head = m_tail = QUEUE_ITERATOR_INVALID; } else { m_tail = Previous_Unchecked( m_tail ); } return true; } template inline T const& CUtlQueue::Head() const { Assert( m_head != QUEUE_ITERATOR_INVALID ); if ( m_head == QUEUE_ITERATOR_INVALID ) { static T dummy; return dummy; } return m_memory[ m_head ]; } template inline T const& CUtlQueue::Tail() const { Assert( m_tail != QUEUE_ITERATOR_INVALID ); if ( m_tail == QUEUE_ITERATOR_INVALID ) { static T dummy; return dummy; } return m_memory[ m_tail ]; } template void CUtlQueue::Insert( T const &element ) { if ( m_tail == QUEUE_ITERATOR_INVALID ) { // empty m_memory.EnsureCapacity( 1 ); m_head = m_tail = QueueIter_t( 0 ); } else { // non-empty QueueIter_t nextTail = Next_Unchecked( m_tail ); if ( nextTail == m_head ) // if non-empty, and growing by 1 appears to make the queue of length 1, then we were already full before the Insert { int nOldAllocCount = m_memory.NumAllocated(); m_memory.Grow(); int nNewAllocCount = m_memory.NumAllocated(); int nGrowAmount = nNewAllocCount - nOldAllocCount; nextTail = Next_Unchecked( m_tail ); // if nextTail was 0, then it now should be nOldAllocCount if ( m_head != QueueIter_t( 0 ) ) { // if the queue wraps around the end of m_memory, move the part at the end of memory to the new end of memory Q_memmove( &m_memory[ m_head + nGrowAmount ], &m_memory[ m_head ], ( nOldAllocCount - m_head ) * sizeof( T ) ); #ifdef _DEBUG Q_memset( &m_memory[ m_head ], 0xdd, nGrowAmount * sizeof( T ) ); #endif m_head = QueueIter_t( m_head + nGrowAmount ); } } m_tail = nextTail; } CopyConstruct( &m_memory[ m_tail ], element ); } template bool CUtlQueue::Check( T const element ) const { for ( QueueIter_t it = First(); it != QUEUE_ITERATOR_INVALID; it = Next( it ) ) { if ( m_memory[ it ] == element ) return true; } return false; } template QueueIter_t CUtlQueue::First() const { return m_head; } template QueueIter_t CUtlQueue::Next( QueueIter_t it ) const { if ( it == QUEUE_ITERATOR_INVALID ) return QUEUE_ITERATOR_INVALID; if ( it == m_tail ) return QUEUE_ITERATOR_INVALID; Assert( IsValid( it ) ); if ( !IsValid( it ) ) return QUEUE_ITERATOR_INVALID; return Next_Unchecked( it ); } template QueueIter_t CUtlQueue::Last() const { return m_tail; } template QueueIter_t CUtlQueue::Previous( QueueIter_t it ) const { if ( it == QUEUE_ITERATOR_INVALID ) return QUEUE_ITERATOR_INVALID; if ( it == m_head ) return QUEUE_ITERATOR_INVALID; Assert( IsValid( it ) ); if ( !IsValid( it ) ) return QUEUE_ITERATOR_INVALID; return Previous_Unchecked( it ); } template QueueIter_t CUtlQueue::Next_Unchecked( QueueIter_t it ) const { return it == m_memory.Count() - 1 ? QueueIter_t( 0 ) : QueueIter_t( it + 1 ); } template QueueIter_t CUtlQueue::Previous_Unchecked( QueueIter_t it ) const { return it == 0 ? QueueIter_t( m_memory.Count() - 1 ) : QueueIter_t( it - 1 ); } template bool CUtlQueue::IsValid( QueueIter_t it ) const { if ( it == QUEUE_ITERATOR_INVALID ) return false; if ( m_head == QUEUE_ITERATOR_INVALID ) return false; if ( m_head <= m_tail ) return it >= m_head && it <= m_tail; return ( it >= m_head && it < m_memory.Count() ) || ( it >= 0 && it <= m_tail ); } template T const& CUtlQueue::Element( QueueIter_t it ) const { Assert( it != QUEUE_ITERATOR_INVALID ); if ( it == QUEUE_ITERATOR_INVALID ) { static T dummy; return dummy; } Assert( IsValid( it ) ); return m_memory[ it ]; } template int CUtlQueue::Count() const { if ( m_head == QUEUE_ITERATOR_INVALID ) { Assert( m_tail == QUEUE_ITERATOR_INVALID ); return 0; } Assert( m_tail != QUEUE_ITERATOR_INVALID ); if ( m_head <= m_tail ) return m_tail + 1 - m_head; return m_tail + 1 - m_head + m_memory.Count(); } template bool CUtlQueue::IsEmpty() const { Assert( ( m_head == QUEUE_ITERATOR_INVALID ) == ( m_tail == QUEUE_ITERATOR_INVALID ) ); return ( m_head == QUEUE_ITERATOR_INVALID ); } template void CUtlQueue::RemoveAll() { m_head = m_tail = QUEUE_ITERATOR_INVALID; } template void CUtlQueue::Purge() { m_head = m_tail = QUEUE_ITERATOR_INVALID; m_memory.Purge(); } #ifdef TEST_UTLQUEUE #include struct Data_t { Data_t( int i = 0xffffffff ) : m_id( i ) {} Data_t( const Data_t &that ) : m_id( that.m_id ) {} ~Data_t() { m_id = 0xdddddddd; } Data_t &operator=( const Data_t &that ) { m_id = that.m_id; return *this; } int m_id; }; inline void CUtlQueue_Test() { CUtlQueue< Data_t > queue; for ( int n = 1; n < 100; ++n ) { Assert( queue.Count() == 0 ); Assert( queue.m_head == QUEUE_ITERATOR_INVALID ); Assert( queue.m_tail == QUEUE_ITERATOR_INVALID ); int w = rand() % n; for ( int i = 0; i < w; ++i ) { queue.Insert( Data_t( i ) ); } if ( w > 0 ) { Assert( queue.Head().m_id == queue.First() ); Assert( queue.Tail().m_id == queue.Last() ); Assert( queue.Head().m_id == 0 ); Assert( queue.Tail().m_id == w - 1 ); } Assert( queue.Count() == w ); for ( int j = 0; j < n; ++j ) { queue.Insert( Data_t( w + j ) ); if ( j == 0 ) { Assert( queue.Count() == w + j + 1 ); for ( int i = 0; i < w; ++i ) { queue.RemoveAtHead(); } } Assert( queue.Count() == j + 1 ); Assert( queue.m_head != QUEUE_ITERATOR_INVALID ); Assert( queue.m_tail != QUEUE_ITERATOR_INVALID ); int id = queue.Head().m_id % queue.m_memory.Count(); for ( QueueIter_t it = queue.First(); it != QUEUE_ITERATOR_INVALID; it = queue.Next( it ) ) { Assert( queue.Element( it ).m_id % queue.m_memory.Count() == id ); id = ( id + 1 ) % queue.m_memory.Count(); } id = queue.Tail().m_id % queue.m_memory.Count(); for ( QueueIter_t it = queue.Last(); it != QUEUE_ITERATOR_INVALID; it = queue.Previous( it ) ) { Assert( queue.Element( it ).m_id % queue.m_memory.Count() == id ); id = ( id + queue.m_memory.Count() - 1 ) % queue.m_memory.Count(); } for ( int i = 0; i < j; ++i ) { int id = queue.m_memory[ i ].m_id; if ( queue.IsValid( QueueIter_t( i ) ) ) { Assert( ( id & 0xff000000 ) == 0 ); } else { Assert( id == 0xdddddddd ); } } } Assert( queue.Count() == n ); #if 0 for ( int j = 0; j < n; ++j ) { Assert( queue.m_head != QUEUE_ITERATOR_INVALID ); Assert( queue.m_tail != QUEUE_ITERATOR_INVALID ); Assert( queue.Count() == n - j ); Data_t data = queue.RemoveAtHead(); Assert( queue.Count() == n - j - 1 ); if ( queue.Count() > 0 ) { int id = queue.Head().m_id % queue.m_memory.Count(); for ( QueueIter_t it = queue.First(); it != QUEUE_ITERATOR_INVALID; it = queue.Next( it ) ) { Assert( queue.Element( it ).m_id % queue.m_memory.Count() == id ); id = ( id + 1 ) % queue.m_memory.Count(); } id = queue.Tail().m_id % queue.m_memory.Count(); for ( QueueIter_t it = queue.Last(); it != QUEUE_ITERATOR_INVALID; it = queue.Previous( it ) ) { Assert( queue.Element( it ).m_id % queue.m_memory.Count() == id ); id = ( id + queue.m_memory.Count() - 1 ) % queue.m_memory.Count(); } } for ( int i = 0; i < j; ++i ) { int id = queue.m_memory[ i ].m_id; if ( queue.IsValid( QueueIter_t( i ) ) ) { Assert( ( id & 0xff000000 ) == 0 ); } else { Assert( id == 0xdddddddd ); } } } #else for ( int j = n - 1; j >= 0; --j ) { Assert( queue.m_head != QUEUE_ITERATOR_INVALID ); Assert( queue.m_tail != QUEUE_ITERATOR_INVALID ); Assert( queue.Count() == j + 1 ); Data_t data = queue.RemoveAtTail(); Assert( queue.Count() == j ); if ( queue.Count() > 0 ) { int id = queue.Head().m_id % queue.m_memory.Count(); for ( QueueIter_t it = queue.First(); it != QUEUE_ITERATOR_INVALID; it = queue.Next( it ) ) { Assert( queue.Element( it ).m_id % queue.m_memory.Count() == id ); id = ( id + 1 ) % queue.m_memory.Count(); } id = queue.Tail().m_id % queue.m_memory.Count(); for ( QueueIter_t it = queue.Last(); it != QUEUE_ITERATOR_INVALID; it = queue.Previous( it ) ) { Assert( queue.Element( it ).m_id % queue.m_memory.Count() == id ); id = ( id + queue.m_memory.Count() - 1 ) % queue.m_memory.Count(); } } for ( int i = 0; i < j; ++i ) { int id = queue.m_memory[ i ].m_id; if ( queue.IsValid( QueueIter_t( i ) ) ) { Assert( ( id & 0xff000000 ) == 0 ); } else { Assert( id == 0xdddddddd ); } } } #endif Assert( queue.Count() == 0 ); Assert( queue.m_head == QUEUE_ITERATOR_INVALID ); Assert( queue.m_tail == QUEUE_ITERATOR_INVALID ); } } #endif // TEST_UTLQUEUE #endif // UTLQUEUE_H