2022-04-19 16:27:52 +01:00
|
|
|
// This file is part of the mcl project.
|
|
|
|
// Copyright (c) 2022 merryhime
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
#include <iterator>
|
|
|
|
#include <memory>
|
|
|
|
#include <type_traits>
|
|
|
|
|
|
|
|
#include "mcl/assert.hpp"
|
|
|
|
|
|
|
|
namespace mcl {
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class intrusive_list;
|
|
|
|
template<typename T>
|
|
|
|
class intrusive_list_iterator;
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class intrusive_list_node {
|
|
|
|
public:
|
2022-07-10 10:10:04 +01:00
|
|
|
bool is_sentinel() const
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
return is_sentinel_;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
intrusive_list_node* next = nullptr;
|
|
|
|
intrusive_list_node* prev = nullptr;
|
|
|
|
bool is_sentinel_ = false;
|
|
|
|
|
|
|
|
friend class intrusive_list<T>;
|
|
|
|
friend class intrusive_list_iterator<T>;
|
|
|
|
friend class intrusive_list_iterator<const T>;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class intrusive_list_sentinel final : public intrusive_list_node<T> {
|
|
|
|
using intrusive_list_node<T>::next;
|
|
|
|
using intrusive_list_node<T>::prev;
|
|
|
|
using intrusive_list_node<T>::is_sentinel_;
|
|
|
|
|
|
|
|
public:
|
2022-07-10 10:10:04 +01:00
|
|
|
intrusive_list_sentinel()
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
next = this;
|
|
|
|
prev = this;
|
|
|
|
is_sentinel_ = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class intrusive_list_iterator {
|
|
|
|
public:
|
|
|
|
using iterator_category = std::bidirectional_iterator_tag;
|
|
|
|
using difference_type = std::ptrdiff_t;
|
|
|
|
using value_type = T;
|
|
|
|
using pointer = value_type*;
|
|
|
|
using const_pointer = const value_type*;
|
|
|
|
using reference = value_type&;
|
|
|
|
using const_reference = const value_type&;
|
|
|
|
|
|
|
|
// If value_type is const, we want "const intrusive_list_node<value_type>", not "intrusive_list_node<const value_type>"
|
|
|
|
using node_type = std::conditional_t<std::is_const<value_type>::value,
|
|
|
|
const intrusive_list_node<std::remove_const_t<value_type>>,
|
|
|
|
intrusive_list_node<value_type>>;
|
|
|
|
using node_pointer = node_type*;
|
|
|
|
using node_reference = node_type&;
|
|
|
|
|
|
|
|
intrusive_list_iterator() = default;
|
|
|
|
intrusive_list_iterator(const intrusive_list_iterator& other) = default;
|
|
|
|
intrusive_list_iterator& operator=(const intrusive_list_iterator& other) = default;
|
|
|
|
|
|
|
|
explicit intrusive_list_iterator(node_pointer list_node)
|
2022-07-10 10:10:04 +01:00
|
|
|
: node(list_node) {}
|
2022-04-19 16:27:52 +01:00
|
|
|
explicit intrusive_list_iterator(pointer data)
|
2022-07-10 10:10:04 +01:00
|
|
|
: node(data) {}
|
2022-04-19 16:27:52 +01:00
|
|
|
explicit intrusive_list_iterator(reference data)
|
2022-07-10 10:10:04 +01:00
|
|
|
: node(&data) {}
|
2022-04-19 16:27:52 +01:00
|
|
|
|
2022-07-10 10:10:04 +01:00
|
|
|
intrusive_list_iterator& operator++()
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
node = node->next;
|
|
|
|
return *this;
|
|
|
|
}
|
2022-07-10 10:10:04 +01:00
|
|
|
intrusive_list_iterator& operator--()
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
node = node->prev;
|
|
|
|
return *this;
|
|
|
|
}
|
2022-07-10 10:10:04 +01:00
|
|
|
intrusive_list_iterator operator++(int)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
intrusive_list_iterator it(*this);
|
|
|
|
++*this;
|
|
|
|
return it;
|
|
|
|
}
|
2022-07-10 10:10:04 +01:00
|
|
|
intrusive_list_iterator operator--(int)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
intrusive_list_iterator it(*this);
|
|
|
|
--*this;
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
2022-07-10 10:10:04 +01:00
|
|
|
bool operator==(const intrusive_list_iterator& other) const
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
return node == other.node;
|
|
|
|
}
|
2022-07-10 10:10:04 +01:00
|
|
|
bool operator!=(const intrusive_list_iterator& other) const
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
return !operator==(other);
|
|
|
|
}
|
|
|
|
|
2022-07-10 10:10:04 +01:00
|
|
|
reference operator*() const
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
DEBUG_ASSERT(!node->is_sentinel());
|
|
|
|
return static_cast<reference>(*node);
|
|
|
|
}
|
2022-07-10 10:10:04 +01:00
|
|
|
pointer operator->() const
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
return std::addressof(operator*());
|
|
|
|
}
|
|
|
|
|
2022-07-10 10:10:04 +01:00
|
|
|
node_pointer AsNodePointer() const
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
friend class intrusive_list<T>;
|
|
|
|
node_pointer node = nullptr;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class intrusive_list {
|
|
|
|
public:
|
|
|
|
using difference_type = std::ptrdiff_t;
|
|
|
|
using size_type = std::size_t;
|
|
|
|
using value_type = T;
|
|
|
|
using pointer = value_type*;
|
|
|
|
using const_pointer = const value_type*;
|
|
|
|
using reference = value_type&;
|
|
|
|
using const_reference = const value_type&;
|
|
|
|
using iterator = intrusive_list_iterator<value_type>;
|
|
|
|
using const_iterator = intrusive_list_iterator<const value_type>;
|
|
|
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
|
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Inserts a node at the given location indicated by an iterator.
|
|
|
|
*
|
|
|
|
* @param location The location to insert the node.
|
|
|
|
* @param new_node The node to add.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
iterator insert(iterator location, pointer new_node)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
return insert_before(location, new_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Inserts a node at the given location, moving the previous
|
|
|
|
* node occupant ahead of the one inserted.
|
|
|
|
*
|
|
|
|
* @param location The location to insert the new node.
|
|
|
|
* @param new_node The node to insert into the list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
iterator insert_before(iterator location, pointer new_node)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
auto existing_node = location.AsNodePointer();
|
|
|
|
|
|
|
|
new_node->next = existing_node;
|
|
|
|
new_node->prev = existing_node->prev;
|
|
|
|
existing_node->prev->next = new_node;
|
|
|
|
existing_node->prev = new_node;
|
|
|
|
|
|
|
|
return iterator(new_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Inserts a new node into the list ahead of the position indicated.
|
|
|
|
*
|
|
|
|
* @param position Location to insert the node in front of.
|
|
|
|
* @param new_node The node to be inserted into the list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
iterator insert_after(iterator position, pointer new_node)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
if (empty())
|
|
|
|
return insert(begin(), new_node);
|
|
|
|
|
|
|
|
return insert(++position, new_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add an entry to the start of the list.
|
|
|
|
* @param node Node to add to the list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
void push_front(pointer node)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
insert(begin(), node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add an entry to the end of the list
|
|
|
|
* @param node Node to add to the list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
void push_back(pointer node)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
insert(end(), node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Erases the node at the front of the list.
|
|
|
|
* @note Must not be called on an empty list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
void pop_front()
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
DEBUG_ASSERT(!empty());
|
|
|
|
erase(begin());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Erases the node at the back of the list.
|
|
|
|
* @note Must not be called on an empty list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
void pop_back()
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
DEBUG_ASSERT(!empty());
|
|
|
|
erase(--end());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes a node from this list
|
|
|
|
* @param it An iterator that points to the node to remove from list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
pointer remove(iterator& it)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
DEBUG_ASSERT(it != end());
|
|
|
|
|
|
|
|
pointer node = &*it++;
|
|
|
|
|
|
|
|
node->prev->next = node->next;
|
|
|
|
node->next->prev = node->prev;
|
|
|
|
#if !defined(NDEBUG)
|
|
|
|
node->next = nullptr;
|
|
|
|
node->prev = nullptr;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes a node from this list
|
|
|
|
* @param it A constant iterator that points to the node to remove from list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
pointer remove(const iterator& it)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
iterator copy = it;
|
|
|
|
return remove(copy);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes a node from this list.
|
|
|
|
* @param node A pointer to the node to remove.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
pointer remove(pointer node)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
return remove(iterator(node));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes a node from this list.
|
|
|
|
* @param node A reference to the node to remove.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
pointer remove(reference node)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
return remove(iterator(node));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is this list empty?
|
|
|
|
* @returns true if there are no nodes in this list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
bool empty() const
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
return root->next == root.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the total number of elements within this list.
|
|
|
|
* @return the number of elements in this list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
size_type size() const
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
return static_cast<size_type>(std::distance(begin(), end()));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a reference to the node at the front of the list.
|
|
|
|
* @note Must not be called on an empty list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
reference front()
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
DEBUG_ASSERT(!empty());
|
|
|
|
return *begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a constant reference to the node at the front of the list.
|
|
|
|
* @note Must not be called on an empty list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
const_reference front() const
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
DEBUG_ASSERT(!empty());
|
|
|
|
return *begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a reference to the node at the back of the list.
|
|
|
|
* @note Must not be called on an empty list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
reference back()
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
DEBUG_ASSERT(!empty());
|
|
|
|
return *--end();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves a constant reference to the node at the back of the list.
|
|
|
|
* @note Must not be called on an empty list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
const_reference back() const
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
DEBUG_ASSERT(!empty());
|
|
|
|
return *--end();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Iterator interface
|
|
|
|
iterator begin() { return iterator(root->next); }
|
|
|
|
const_iterator begin() const { return const_iterator(root->next); }
|
|
|
|
const_iterator cbegin() const { return begin(); }
|
|
|
|
|
|
|
|
iterator end() { return iterator(root.get()); }
|
|
|
|
const_iterator end() const { return const_iterator(root.get()); }
|
|
|
|
const_iterator cend() const { return end(); }
|
|
|
|
|
|
|
|
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
|
|
|
const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
|
|
|
|
const_reverse_iterator crbegin() const { return rbegin(); }
|
|
|
|
|
|
|
|
reverse_iterator rend() { return reverse_iterator(begin()); }
|
|
|
|
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
|
|
|
|
const_reverse_iterator crend() const { return rend(); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Erases a node from the list, indicated by an iterator.
|
|
|
|
* @param it The iterator that points to the node to erase.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
iterator erase(iterator it)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
remove(it);
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Erases a node from this list.
|
|
|
|
* @param node A pointer to the node to erase from this list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
iterator erase(pointer node)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
return erase(iterator(node));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Erases a node from this list.
|
|
|
|
* @param node A reference to the node to erase from this list.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
iterator erase(reference node)
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
return erase(iterator(node));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Exchanges contents of this list with another list instance.
|
|
|
|
* @param other The other list to swap with.
|
|
|
|
*/
|
2022-07-10 10:10:04 +01:00
|
|
|
void swap(intrusive_list& other) noexcept
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
root.swap(other.root);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::shared_ptr<intrusive_list_node<T>> root = std::make_shared<intrusive_list_sentinel<T>>();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Exchanges contents of an intrusive list with another intrusive list.
|
|
|
|
* @tparam T The type of data being kept track of by the lists.
|
|
|
|
* @param lhs The first list.
|
|
|
|
* @param rhs The second list.
|
|
|
|
*/
|
|
|
|
template<typename T>
|
2022-07-10 10:10:04 +01:00
|
|
|
void swap(intrusive_list<T>& lhs, intrusive_list<T>& rhs) noexcept
|
|
|
|
{
|
2022-04-19 16:27:52 +01:00
|
|
|
lhs.swap(rhs);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace mcl
|