You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
551 lines
13 KiB
551 lines
13 KiB
//========= 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<T, MAX_SIZE > > |
|
{ |
|
typedef CUtlQueue< T, CUtlMemoryFixed<T, MAX_SIZE > > 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<T, M>::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<T, M>::CUtlQueue( T *pMemory, int numElements ) : |
|
m_memory( pMemory, numElements ), m_head( QUEUE_ITERATOR_INVALID ), m_tail( QUEUE_ITERATOR_INVALID ) |
|
{ |
|
} |
|
|
|
template <class T, class M> |
|
inline T CUtlQueue<T, M>::RemoveAtHead() |
|
{ |
|
T temp; |
|
RemoveAtHead( temp ); |
|
return temp; |
|
} |
|
|
|
template <class T, class M> |
|
inline bool CUtlQueue<T, M>::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 <class T, class M> |
|
inline T CUtlQueue<T, M>::RemoveAtTail() |
|
{ |
|
T temp; |
|
RemoveAtTail( temp ); |
|
return temp; |
|
} |
|
|
|
template <class T, class M> |
|
inline bool CUtlQueue<T, M>::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 <class T, class M> |
|
inline T const& CUtlQueue<T, M>::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 <class T, class M> |
|
inline T const& CUtlQueue<T, M>::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 <class T, class M> |
|
void CUtlQueue<T, M>::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 <class T, class M> |
|
bool CUtlQueue<T, M>::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 <class T, class M> |
|
QueueIter_t CUtlQueue<T, M>::First() const |
|
{ |
|
return m_head; |
|
} |
|
|
|
template <class T, class M> |
|
QueueIter_t CUtlQueue<T, M>::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 <class T, class M> |
|
QueueIter_t CUtlQueue<T, M>::Last() const |
|
{ |
|
return m_tail; |
|
} |
|
|
|
template <class T, class M> |
|
QueueIter_t CUtlQueue<T, M>::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 <class T, class M> |
|
QueueIter_t CUtlQueue<T, M>::Next_Unchecked( QueueIter_t it ) const |
|
{ |
|
return it == m_memory.Count() - 1 ? QueueIter_t( 0 ) : QueueIter_t( it + 1 ); |
|
} |
|
|
|
template <class T, class M> |
|
QueueIter_t CUtlQueue<T, M>::Previous_Unchecked( QueueIter_t it ) const |
|
{ |
|
return it == 0 ? QueueIter_t( m_memory.Count() - 1 ) : QueueIter_t( it - 1 ); |
|
} |
|
|
|
template <class T, class M> |
|
bool CUtlQueue<T, M>::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 <class T, class M> |
|
T const& CUtlQueue<T, M>::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 <class T, class M> |
|
int CUtlQueue<T, M>::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 <class T, class M> |
|
bool CUtlQueue<T, M>::IsEmpty() const |
|
{ |
|
Assert( ( m_head == QUEUE_ITERATOR_INVALID ) == ( m_tail == QUEUE_ITERATOR_INVALID ) ); |
|
return ( m_head == QUEUE_ITERATOR_INVALID ); |
|
} |
|
|
|
template <class T, class M> |
|
void CUtlQueue<T, M>::RemoveAll() |
|
{ |
|
m_head = m_tail = QUEUE_ITERATOR_INVALID; |
|
} |
|
|
|
template <class T, class M> |
|
void CUtlQueue<T, M>::Purge() |
|
{ |
|
m_head = m_tail = QUEUE_ITERATOR_INVALID; |
|
m_memory.Purge(); |
|
} |
|
|
|
|
|
#ifdef TEST_UTLQUEUE |
|
|
|
#include <stdlib.h> |
|
|
|
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
|
|
|