[util] Add util::deque container

Adding the util::deque container, implemented with the util::linked_list
of arrays of items.

Also, use the deque for a kobject's blocked thread list to maintain
order instead of a vector using remove_swap().
This commit is contained in:
Justin C. Miller
2022-01-30 20:42:49 -08:00
parent 2aef7176ab
commit a7245116b6
4 changed files with 182 additions and 19 deletions

View File

@@ -0,0 +1,114 @@
#pragma once
/// \file linked_list.h
/// A generic templatized linked list.
#include <assert.h>
#include <util/linked_list.h>
namespace util {
template <typename T, unsigned N = 16>
class deque
{
struct node { T items[N]; };
using list_type = linked_list<node>;
using node_type = typename list_type::item_type;
public:
class iterator
{
public:
iterator(node_type *n, size_t i) : node(n), index(i) {}
iterator(const iterator &o) : node(o.node), index(o.index) {}
inline T& operator*() { assert(node); return node->items[index]; }
inline iterator & operator++() { incr(); return *this; }
inline iterator & operator++(int) { iterator old {*this}; incr(); return old; }
inline bool operator!=(const iterator &o) const { return !(*this == o); }
inline bool operator==(const iterator &o) const {
if (!node && !o.node) return true;
return index == o.index && node == o.node;
}
private:
void incr() { if (++index >= N) { index = 0; node = node->next(); }}
node_type *node;
size_t index;
};
deque() : m_first(0), m_next(N) {}
~deque() {
while (!m_list.empty())
delete m_list.pop_front();
}
inline void push_front(const T& item) {
if (!m_first) { // need a new block at the start
m_list.push_front(new node_type);
m_first = N;
}
m_list.front()->items[--m_first] = item;
}
inline void push_back(const T& item) {
if (m_next == N) { // need a new block at the end
m_list.push_back(new node_type);
m_next = 0;
}
m_list.back()->items[m_next++] = item;
}
inline T pop_front() {
assert(!empty() && "Calling pop_front() on an empty deque");
T value = m_list.front()->items[m_first++];
if (m_first == N) {
delete m_list.pop_front();
m_first = 0;
if (m_list.empty())
m_next = N;
}
return value;
}
inline T pop_back() {
assert(!empty() && "Calling pop_back() on an empty deque");
T value = m_list.back()->items[--m_next];
if (m_next == 0) {
delete m_list.pop_back();
m_next = N;
if (m_list.empty())
m_first = 0;
}
return value;
}
inline bool empty() const { return m_list.empty(); }
inline T& first() {
assert(!empty() && "Calling first() on an empty deque");
return m_list.front()->items[m_first];
}
inline const T& first() const {
assert(!empty() && "Calling first() on an empty deque");
return m_list.front()->items[m_first];
}
inline T& last() {
assert(!empty() && "Calling last() on an empty deque");
return m_list.back()->items[m_next-1];
}
inline const T& last() const {
assert(!empty() && "Calling last() on an empty deque");
return m_list.back()->items[m_next-1];
}
iterator begin() { return iterator {m_list.front(), m_first}; }
iterator end() { return iterator {m_list.back(), m_next}; }
private:
unsigned m_first; // Index of the first item in the first node
unsigned m_next; // Index of the first empty item in the last node
list_type m_list;
};
} // namespace util