#pragma once
|
|
#include <stddef.h>
|
|
#include "gp/algorithm/tmp_manip.hpp"
|
|
#include "gp/algorithm/modifiers.hpp"
|
|
#include "gp_config.hpp"
|
|
|
|
namespace gp {
|
|
template<typename T, typename allocator, bool copy_allocator = false, bool may_contain_self = false>
|
|
class ring_list{
|
|
public:
|
|
class explorer;
|
|
class node {
|
|
T* value;
|
|
node* prev;
|
|
node* next;
|
|
public:
|
|
node(T* p)
|
|
: value{p}
|
|
, prev{this}
|
|
, next{this}
|
|
{}
|
|
|
|
friend class gp::ring_list<T, allocator, copy_allocator, may_contain_self>::explorer;
|
|
friend class gp::ring_list<T, allocator, copy_allocator, may_contain_self>;
|
|
};
|
|
|
|
class explorer {
|
|
node* pos;
|
|
public:
|
|
explorer(node* v)
|
|
: pos{v}
|
|
{}
|
|
|
|
bool operator==(const explorer& oth) const {
|
|
return pos == oth.pos;
|
|
}
|
|
|
|
bool operator!=(const explorer& oth) const {
|
|
return pos != oth.pos;
|
|
}
|
|
|
|
explorer operator++() {
|
|
pos = pos->next;
|
|
return pos;
|
|
}
|
|
|
|
explorer operator++(int) {
|
|
auto tmp = pos;
|
|
pos = pos->next;
|
|
return tmp;
|
|
}
|
|
|
|
explorer operator--() {
|
|
pos = pos->prev;
|
|
return pos;
|
|
}
|
|
|
|
explorer operator--(int) {
|
|
auto tmp = pos;
|
|
pos = pos->prev;
|
|
return tmp;
|
|
}
|
|
|
|
T& operator*() {
|
|
return *(pos->value);
|
|
}
|
|
|
|
friend class ring_list;
|
|
};
|
|
|
|
private:
|
|
node* any_node;
|
|
size_t sz;
|
|
typename gp::either<
|
|
copy_allocator,
|
|
allocator,
|
|
gp::reference_wrapper<allocator>
|
|
>::type alloc;
|
|
|
|
void stitch_around(node* n) {
|
|
n->prev->next = n->next;
|
|
n->next->prev = n->prev;
|
|
}
|
|
|
|
public:
|
|
ring_list()
|
|
: any_node{nullptr}
|
|
, sz{0}
|
|
, alloc{}
|
|
{}
|
|
|
|
ring_list(node* initial, allocator& _alloc)
|
|
: any_node{initial}
|
|
, sz{1}
|
|
, alloc{_alloc}
|
|
{}
|
|
|
|
ring_list(allocator& _alloc)
|
|
: any_node{nullptr}
|
|
, sz{0}
|
|
, alloc{_alloc}
|
|
{}
|
|
|
|
template<typename V = T, typename ...Args>
|
|
bool insert(Args&&... elem) {
|
|
allocator& used_allocator = alloc;
|
|
void* mem;
|
|
[[unlikely]] if(
|
|
nullptr == (mem = used_allocator.allocate(sizeof(V)))
|
|
) return false;
|
|
T* p = new(mem) V(elem...);
|
|
node* to_insert = nullptr;
|
|
[[unlikely]] if(
|
|
nullptr == (to_insert = reinterpret_cast<node*>(used_allocator.allocate(sizeof(node))))
|
|
) return false;
|
|
to_insert = new(to_insert) node(p);
|
|
[[unlikely]] if (any_node == nullptr)
|
|
{
|
|
any_node = to_insert;
|
|
} else {
|
|
to_insert->prev = any_node->prev;
|
|
to_insert->next = any_node;
|
|
to_insert->prev->next = to_insert;
|
|
any_node->prev = to_insert;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
explorer explore() {
|
|
return any_node;
|
|
}
|
|
|
|
void remove(explorer value) {
|
|
auto v = value.pos;
|
|
if(v == any_node) {
|
|
if(v->next == v) {
|
|
any_node = nullptr;
|
|
} else {
|
|
any_node = any_node->next;
|
|
stitch_around(v);
|
|
}
|
|
} else {
|
|
stitch_around(v);
|
|
}
|
|
allocator& used_allocator = alloc;
|
|
v->value->~T();
|
|
gp_config::assertion(used_allocator.deallocate(v->value), "Bad free of value");
|
|
v->~node();
|
|
gp_config::assertion(used_allocator.deallocate(v), "Bad free of node");
|
|
}
|
|
|
|
~ring_list()
|
|
{
|
|
// TODO: Find a less hackish way to resove this issue
|
|
if constexpr (!may_contain_self) {
|
|
while(any_node) {
|
|
remove(explore());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|