zeek/src/Queue.h

243 lines
6.7 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#ifndef queue_h
#define queue_h
// Queue.h --
// Interface for class Queue, current implementation is as an
// array of ent's. This implementation was chosen to optimize
// getting to the ent's rather than inserting and deleting.
// Also push's and pop's from the front or the end of the queue
// are very efficient. The only really expensive operation
// is resizing the list, which involves getting new space
// and moving the data. Resizing occurs automatically when inserting
// more elements than the list can currently hold. Automatic
// resizing is done one "chunk_size" of elements at a time and
// always increases the size of the list. Resizing to zero
// (or to less than the current value of num_entries)
// will decrease the size of the list to the current number of
// elements. Resize returns the new max_entries.
//
// Entries must be either a pointer to the data or nonzero data with
// sizeof(data) <= sizeof(void*).
template<typename T>
class QueueIterator
{
T* const entries;
int offset;
int num_entries;
public:
QueueIterator(T* entries, int offset, int num_entries) :
entries(entries), offset(offset), num_entries(num_entries) {}
bool operator==(const QueueIterator& rhs) { return entries == rhs.entries && offset == rhs.offset; }
bool operator!=(const QueueIterator& rhs) { return entries != rhs.entries || offset != rhs.offset; }
QueueIterator & operator++() { offset++; return *this; }
QueueIterator operator++(int) { auto t = *this; offset++; return t; }
QueueIterator & operator--() { offset--; return *this; }
QueueIterator operator--(int) { auto t = *this; offset--; return t; }
std::ptrdiff_t operator-(QueueIterator const& sibling) const { return offset - sibling.offset; }
QueueIterator & operator+=(int amount) { offset += amount; return *this; }
QueueIterator & operator-=(int amount) { offset -= amount; return *this; }
bool operator<(QueueIterator const&sibling) const { return offset < sibling.offset;}
bool operator<=(QueueIterator const&sibling) const { return offset <= sibling.offset; }
bool operator>(QueueIterator const&sibling) const { return offset > sibling.offset; }
bool operator>=(QueueIterator const&sibling) const { return offset >= sibling.offset; }
T& operator[](int index)
{
return entries[index];
}
T& operator*()
{
return entries[offset];
}
};
namespace std {
template<typename T>
class iterator_traits<QueueIterator<T> >
{
public:
using difference_type = std::ptrdiff_t;
using size_type = std::size_t;
using value_type = T;
using pointer = T*;
using reference = T&;
using iterator_category = std::random_access_iterator_tag;
};
}
template<typename T>
class Queue {
public:
explicit Queue(int size = 0)
{
const int DEFAULT_CHUNK_SIZE = 10;
chunk_size = DEFAULT_CHUNK_SIZE;
head = tail = num_entries = 0;
if ( size < 0 )
{
entries = new T[1];
max_entries = 0;
}
else
{
if ( (entries = new T[chunk_size+1]) )
max_entries = chunk_size;
else
{
entries = new T[1];
max_entries = 0;
}
}
}
~Queue() { delete[] entries; }
int length() const { return num_entries; }
int resize(int new_size = 0) // 0 => size to fit current number of entries
{
if ( new_size < num_entries )
new_size = num_entries; // do not lose any entries
if ( new_size != max_entries )
{
// Note, allocate extra space, so that we can always
// use the [max_entries] element.
// ### Yin, why not use realloc()?
T* new_entries = new T[new_size+1];
if ( new_entries )
{
if ( head <= tail )
memcpy( new_entries, entries + head,
sizeof(T) * num_entries );
else
{
int len = num_entries - tail;
memcpy( new_entries, entries + head,
sizeof(T) * len );
memcpy( new_entries + len, entries,
sizeof(T) * tail );
}
delete [] entries;
entries = new_entries;
max_entries = new_size;
head = 0;
tail = num_entries;
}
else
{ // out of memory
}
}
return max_entries;
}
// remove all entries without delete[] entry
void clear() { head = tail = num_entries = 0; }
// helper functions for iterating over queue
T& front() { return entries[head]; }
T& back() { return entries[tail]; }
const T& front() const { return entries[head]; }
const T& back() const { return entries[tail]; }
void push_front(const T& a) // add in front of queue
{
if ( num_entries == max_entries )
{
resize(max_entries+chunk_size); // make more room
chunk_size *= 2;
}
++num_entries;
if ( head )
entries[--head] = a;
else
{
head = max_entries;
entries[head] = a;
}
}
void push_back(const T& a) // add at end of queue
{
if ( num_entries == max_entries )
{
resize(max_entries+chunk_size); // make more room
chunk_size *= 2;
}
++num_entries;
if ( tail < max_entries )
entries[tail++] = a;
else
{
entries[tail] = a;
tail = 0;
}
}
void pop_front()
{
--num_entries;
if ( head < max_entries )
head++;
else
head = 0;
}
void pop_back()
{
--num_entries;
if ( tail )
--tail;
else
tail = max_entries;
}
// return nth *PHYSICAL* entry of queue (do not remove)
T& operator[](int i) const { return entries[i]; }
// Type traits needed for some of the std algorithms to work
using value_type = T;
// Iterator support
using iterator = QueueIterator<T>;
using const_iterator = QueueIterator<const T>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
iterator begin() { return { entries, 0, num_entries }; }
iterator end() { return { entries, num_entries, num_entries }; }
const_iterator begin() const { return { entries, 0, num_entries }; }
const_iterator end() const { return { entries, num_entries, num_entries }; }
const_iterator cbegin() const { return { entries, 0, num_entries }; }
const_iterator cend() const { return { entries, num_entries, num_entries }; }
reverse_iterator rbegin() { return reverse_iterator{end()}; }
reverse_iterator rend() { return reverse_iterator{begin()}; }
const_reverse_iterator rbegin() const { return const_reverse_iterator{end()}; }
const_reverse_iterator rend() const { return const_reverse_iterator{begin()}; }
const_reverse_iterator crbegin() const { return rbegin(); }
const_reverse_iterator crend() const { return rend(); }
protected:
T* entries;
int chunk_size; // increase size by this amount when necessary
int max_entries; // entry's index range: 0 .. max_entries
int num_entries;
int head; // beginning of the queue in the ring
int tail; // just beyond the end of the queue in the ring
};
template<typename T>
using PQueue = Queue<T*>;
#endif /* queue_h */