From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

#pragma once
#include <iosfwd>
#include <vector>
#include <cassert>
#include <stdint.h>
#include <iterator>
#include <algorithm>
#include <type_traits>
#include <initializer_list>
namespace panda {
template <typename T> struct IntrusiveChainNode {
template <class S> friend S intrusive_chain_next (const S&);
template <class S> friend S intrusive_chain_prev (const S&);
template <class S> friend void intrusive_chain_next (const S&, const S&);
template <class S> friend void intrusive_chain_prev (const S&, const S&);
IntrusiveChainNode () : next(), prev() {}
protected:
T next;
T prev;
};
template <class T> T intrusive_chain_next (const T& node) { return node->next; }
template <class T> T intrusive_chain_prev (const T& node) { return node->prev; }
template <class T> void intrusive_chain_next (const T& node, const T& val) { node->next = val; }
template <class T> void intrusive_chain_prev (const T& node, const T& val) { node->prev = val; }
/// Iterator is not cyclic, so to work with the standard bidirectional algorithms it uses head and tail pointers alongside with the current one.
/// Typical cyclic implementation uses an extra sentinel Node object, which we will definitely do not want to allocate.
template <typename T> struct IntrusiveChainIterator {
template <typename S> friend struct IntrusiveChain;
template <typename S> friend struct IntrusiveChainIterator;
using difference_type = ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
using iterator_category = std::bidirectional_iterator_tag;
IntrusiveChainIterator (const T& tail, const T& current = T()) : tail(tail), current(current) {}
IntrusiveChainIterator (const IntrusiveChainIterator& o) : tail(o.tail), current(o.current) {}
template <typename S>
IntrusiveChainIterator (const IntrusiveChainIterator<S>& o) : tail(o.tail), current(o.current) {}
T& operator* () { return current; }
T* operator-> () { return &current; }
bool operator== (const IntrusiveChainIterator& other) const { return current == other.current; }
bool operator!= (const IntrusiveChainIterator& other) const { return !(*this == other); }
IntrusiveChainIterator& operator++ () {
current = intrusive_chain_next(current);
return *this;
}
IntrusiveChainIterator operator++ (int) {
IntrusiveChainIterator pos(*this);
++*this;
return pos;
}
IntrusiveChainIterator& operator-- () {
if (current) current = intrusive_chain_prev(current);
else current = tail;
return *this;
}
IntrusiveChainIterator operator-- (int) {
IntrusiveChainIterator pos(*this);
--*this;
return pos;
}
IntrusiveChainIterator& operator= (const IntrusiveChainIterator& oth) {
this->tail = oth.tail;
this->current = oth.current;
return *this;
}
private:
T tail;
T current; // current=nullptr indicates end()
};
template <typename T> struct IntrusiveChain {
using value_type = T;
using size_type = size_t;
using difference_type = std::ptrdiff_t;
using reference = T&;
using const_reference = const T&;
using pointer = T*;
using const_pointer = const T*;
using iterator = IntrusiveChainIterator<T>;
using const_iterator = IntrusiveChainIterator<T>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
IntrusiveChain () : head(), tail(), _size() {}
IntrusiveChain (std::initializer_list<T> il) : IntrusiveChain() {
for (const T& node : il) push_back(node);
}
~IntrusiveChain () { clear(); }
IntrusiveChain (const IntrusiveChain&) = delete;
IntrusiveChain& operator= (const IntrusiveChain&) = delete;
IntrusiveChain (IntrusiveChain&& other) noexcept {
std::swap(head, other.head);
std::swap(tail, other.tail);
}
IntrusiveChain& operator= (IntrusiveChain&& other) noexcept {
if (this != &other) {
std::swap(head, other.head);
std::swap(tail, other.tail);
std::swap(_size, other._size);
}
return *this;
}
void push_back (const T& node) {
if (!_size++) {
head = tail = node;
} else {
intrusive_chain_next(tail, node);
intrusive_chain_prev(node, tail);
intrusive_chain_next(node, T());
tail = node;
}
}
void push_front (const T& node) {
if (!_size++) {
head = tail = node;
} else {
intrusive_chain_prev(head, node);
intrusive_chain_prev(node, T());
intrusive_chain_next(node, head);
head = node;
}
}
/// @return false if empty or the last one
bool pop_back () {
if (head == tail) {
head = tail = T();
_size = 0;
return false;
}
auto removed = tail;
tail = intrusive_chain_prev(tail);
intrusive_chain_next(tail, T());
intrusive_chain_prev(removed, T());
--_size;
return true;
}
/// @return false if empty or the last one
bool pop_front () {
if (head == tail) {
head = tail = T();
_size = 0;
return false;
}
auto removed = head;
head = intrusive_chain_next(head);
intrusive_chain_prev(head, T());
intrusive_chain_next(removed, T());
--_size;
return true;
}
iterator insert (const T& pos, const T& node) {
if (pos) {
auto prev = intrusive_chain_prev(pos);
if (prev) {
++_size;
intrusive_chain_prev(node, prev);
intrusive_chain_next(node, pos);
intrusive_chain_next(prev, node);
intrusive_chain_prev(pos, node);
}
else push_front(node);
}
else push_back(node);
return iterator(tail, node);
}
iterator insert (const const_iterator& pos, const T& node) { return insert(pos.current, node); }
iterator erase (const T& pos) {
if (!pos) return end();
auto prev = intrusive_chain_prev(pos);
auto next = intrusive_chain_next(pos);
if (!prev) {
if (!next && pos != head) return end(); // element is not in container
pop_front();
return begin();
}
if (!next) {
pop_back();
return end();
}
intrusive_chain_next(prev, next);
intrusive_chain_prev(next, prev);
intrusive_chain_next(pos, T());
intrusive_chain_prev(pos, T());
--_size;
return iterator(tail, next);
}
iterator erase (const const_iterator& pos) { return erase(pos.current); }
void clear () {
for (auto node = head; node;) {
auto next = intrusive_chain_next(node);
intrusive_chain_next(node, T());
intrusive_chain_prev(node, T());
node = next;
}
head = tail = T();
_size = 0;
}
reference front () { return head; }
reference back () { return tail; }
const_reference front () const { return head; }
const_reference back () const { return tail; }
iterator begin () { return iterator(tail, head); }
iterator end () { return iterator(tail); }
const_iterator begin () const { return const_iterator(tail, head); }
const_iterator end () const { return const_iterator(tail); }
const_iterator cbegin () const { return const_iterator(tail, head); }
const_iterator cend () const { return const_iterator(tail); }
bool empty () const { return !head; }
size_t size () const { return _size; }
private:
T head;
T tail;
size_t _size;
};
template <typename C, typename CT, typename T>
std::basic_ostream<C,CT>& operator<< (std::basic_ostream<C,CT>& out, const IntrusiveChain<T>& chain) {
for (auto node : chain) {
out << "node: ";
if (node) {
out << *node;
} else {
out << "null";
}
out << '\n';
}
return out;
}
}