srsLTE/lib/include/srsran/adt/intrusive_list.h

256 lines
7.9 KiB
C++

/**
* Copyright 2013-2022 Software Radio Systems Limited
*
* This file is part of srsRAN.
*
* srsRAN is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsRAN is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSRAN_INTRUSIVE_LIST_H
#define SRSRAN_INTRUSIVE_LIST_H
#include <iterator>
#include <type_traits>
namespace srsran {
struct default_intrusive_tag;
/// Base class of T, where T is a node of intrusive_forward_list<T>
template <typename Tag = default_intrusive_tag>
struct intrusive_forward_list_element {
intrusive_forward_list_element<Tag>* next_node = nullptr;
};
/**
* Forward linked list of pointers of type "T" that doesn't rely on allocations.
* It leverages each node's internal pointer (thus intrusive) to store the next node of the list.
* It supports push_front/pop_front, iteration, clear, etc.
* @tparam T node type. It must be a subclass of intrusive_forward_list_element<Tag>
* @tparam Tag useful to differentiate multiple intrusive lists in the same node
*/
template <typename T, typename Tag = default_intrusive_tag>
class intrusive_forward_list
{
using node_t = intrusive_forward_list_element<Tag>;
template <typename U>
class iterator_impl
{
using elem_t = typename std::conditional<std::is_const<U>::value, const node_t, node_t>::type;
public:
using iterator_category = std::forward_iterator_tag;
using value_type = U;
using difference_type = std::ptrdiff_t;
using pointer = U*;
using reference = U&;
explicit iterator_impl(elem_t* node_ = nullptr) : node(node_) {}
iterator_impl<U>& operator++()
{
node = node->next_node;
return *this;
}
pointer operator->() { return static_cast<pointer>(node); }
reference operator*() { return static_cast<reference>(*node); }
bool operator==(const iterator_impl<U>& other) const { return node == other.node; }
bool operator!=(const iterator_impl<U>& other) const { return node != other.node; }
private:
elem_t* node;
};
public:
using iterator = iterator_impl<T>;
using const_iterator = iterator_impl<const T>;
intrusive_forward_list()
{
static_assert(std::is_base_of<node_t, T>::value,
"Provided template argument T must have intrusive_forward_list_element<Tag> as base class");
}
intrusive_forward_list(const intrusive_forward_list&) = default;
intrusive_forward_list(intrusive_forward_list&& other) noexcept : node(other.node) { other.node = nullptr; }
intrusive_forward_list& operator=(const intrusive_forward_list&) = default;
intrusive_forward_list& operator =(intrusive_forward_list&& other) noexcept
{
node = other.node;
other.node = nullptr;
return *this;
}
T& front() const { return *static_cast<T*>(node); }
void push_front(T* t)
{
node_t* new_head = static_cast<node_t*>(t);
new_head->next_node = node;
node = new_head;
}
T* pop_front()
{
node_t* ret = node;
node = node->next_node;
return static_cast<T*>(ret);
}
void clear()
{
while (node != nullptr) {
node_t* torem = node;
node = node->next_node;
torem->next_node = nullptr;
}
}
bool empty() const { return node == nullptr; }
iterator begin() { return iterator(node); }
iterator end() { return iterator(nullptr); }
const_iterator begin() const { return const_iterator(node); }
const_iterator end() const { return const_iterator(nullptr); }
private:
node_t* node = nullptr;
};
template <typename Tag = default_intrusive_tag>
struct intrusive_double_linked_list_element {
intrusive_double_linked_list_element<Tag>* next_node = nullptr;
intrusive_double_linked_list_element<Tag>* prev_node = nullptr;
};
/**
* Double Linked List of pointers of type "T" that doesn't rely on allocations.
* Instead, it leverages T's internal pointers to store the next and previous nodes
* @tparam T node type. Must be a subclass of intrusive_double_linked_list_element<Tag>
* @tparam Tag tag of nodes. Useful to differentiate separate intrusive lists inside the same T node
*/
template <typename T, typename Tag = default_intrusive_tag>
class intrusive_double_linked_list
{
using node_t = intrusive_double_linked_list_element<Tag>;
template <typename U>
class iterator_impl
{
using elem_t = typename std::conditional<std::is_const<U>::value, const node_t, node_t>::type;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = U;
using difference_type = std::ptrdiff_t;
using pointer = U*;
using reference = U&;
explicit iterator_impl(elem_t* node_ = nullptr) : node(node_) {}
iterator_impl<U>& operator++()
{
node = node->next_node;
return *this;
}
iterator_impl<U>& operator--()
{
node = node->prev_node;
return *this;
}
pointer operator->() { return static_cast<pointer>(node); }
reference operator*() { return static_cast<reference>(*node); }
bool operator==(const iterator_impl<U>& other) const { return node == other.node; }
bool operator!=(const iterator_impl<U>& other) const { return node != other.node; }
private:
elem_t* node;
};
public:
using iterator = iterator_impl<T>;
using const_iterator = iterator_impl<const T>;
intrusive_double_linked_list()
{
static_assert(std::is_base_of<node_t, T>::value,
"Provided template argument T must have intrusive_forward_list_element<Tag> as base class");
}
intrusive_double_linked_list(const intrusive_double_linked_list&) = default;
intrusive_double_linked_list(intrusive_double_linked_list&& other) noexcept : node(other.node)
{
other.node = nullptr;
}
intrusive_double_linked_list& operator=(const intrusive_double_linked_list&) = default;
intrusive_double_linked_list& operator=(intrusive_double_linked_list&& other) noexcept
{
node = other.node;
other.node = nullptr;
return *this;
}
~intrusive_double_linked_list() { clear(); }
T& front() const { return *static_cast<T*>(node); }
void push_front(T* t)
{
node_t* new_head = static_cast<node_t*>(t);
new_head->prev_node = nullptr;
new_head->next_node = node;
if (node != nullptr) {
node->prev_node = new_head;
}
node = new_head;
}
void pop(T* t)
{
node_t* to_rem = static_cast<node_t*>(t);
if (to_rem == node) {
node = to_rem->next_node;
}
if (to_rem->prev_node != nullptr) {
to_rem->prev_node->next_node = to_rem->next_node;
}
if (to_rem->next_node != nullptr) {
to_rem->next_node->prev_node = to_rem->prev_node;
}
to_rem->next_node = nullptr;
to_rem->prev_node = nullptr;
}
void pop_front() { pop(static_cast<T*>(node)); }
void clear()
{
while (node != nullptr) {
node_t* torem = node;
node = node->next_node;
torem->next_node = nullptr;
torem->prev_node = nullptr;
}
}
bool empty() const { return node == nullptr; }
iterator begin() { return iterator(node); }
iterator end() { return iterator(nullptr); }
const_iterator begin() const { return const_iterator(node); }
const_iterator end() const { return const_iterator(nullptr); }
private:
node_t* node = nullptr;
};
} // namespace srsran
#endif // SRSRAN_INTRUSIVE_LIST_H