This repository has been archived on 2024-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
2020-08-04 13:13:01 -04:00

498 lines
14 KiB
C++

//========= 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