193 lines
4.3 KiB
C++
193 lines
4.3 KiB
C++
// Copyright 2025 Steven Le Rouzic
|
|
//
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
#pragma once
|
|
|
|
#include "asl/base/meta.hpp"
|
|
#include "asl/base/assert.hpp"
|
|
#include "asl/base/utility.hpp"
|
|
|
|
namespace asl
|
|
{
|
|
|
|
template<typename T>
|
|
struct intrusive_list_node
|
|
{
|
|
T* m_prev{};
|
|
T* m_next{};
|
|
};
|
|
|
|
template<typename T>
|
|
concept is_intrusive_list_node = derived_from<T, intrusive_list_node<T>>;
|
|
|
|
template<is_intrusive_list_node T>
|
|
class IntrusiveList
|
|
{
|
|
T* m_head{};
|
|
|
|
static void insert_after(T* before, T* after)
|
|
{
|
|
after->m_prev = before;
|
|
after->m_next = before->m_next;
|
|
|
|
before->m_next = after;
|
|
after->m_next->m_prev = after;
|
|
}
|
|
|
|
public:
|
|
constexpr IntrusiveList() = default;
|
|
|
|
explicit IntrusiveList(T* head)
|
|
{
|
|
push_front(head);
|
|
}
|
|
|
|
ASL_DELETE_COPY(IntrusiveList);
|
|
ASL_DEFAULT_MOVE(IntrusiveList);
|
|
~IntrusiveList() = default;
|
|
|
|
[[nodiscard]] constexpr bool is_empty() const { return m_head == nullptr; }
|
|
|
|
void push_front(T* node)
|
|
{
|
|
ASL_ASSERT(node->m_next == nullptr && node->m_prev == nullptr);
|
|
if (is_empty())
|
|
{
|
|
m_head = node;
|
|
node->m_prev = node;
|
|
node->m_next = node;
|
|
}
|
|
else
|
|
{
|
|
insert_after(m_head->m_prev, node);
|
|
m_head = node;
|
|
}
|
|
}
|
|
|
|
void push_back(T* node)
|
|
{
|
|
ASL_ASSERT(node->m_next == nullptr && node->m_prev == nullptr);
|
|
if (is_empty())
|
|
{
|
|
m_head = node;
|
|
node->m_prev = node;
|
|
node->m_next = node;
|
|
}
|
|
else
|
|
{
|
|
insert_after(m_head->m_prev, node);
|
|
}
|
|
}
|
|
|
|
constexpr auto front(this auto&& self)
|
|
{
|
|
using return_type = copy_const_t<un_ref_t<decltype(self)>, T>*;
|
|
return return_type{ self.m_head };
|
|
}
|
|
|
|
constexpr auto back(this auto&& self)
|
|
{
|
|
using return_type = copy_const_t<un_ref_t<decltype(self)>, T>*;
|
|
return return_type{ self.m_head != nullptr ? self.m_head->m_prev : nullptr };
|
|
}
|
|
|
|
void detach(T* node)
|
|
{
|
|
ASL_ASSERT(node->m_next != nullptr && node->m_prev != nullptr);
|
|
|
|
if (m_head->m_next == m_head)
|
|
{
|
|
ASL_ASSERT(m_head->m_prev == m_head);
|
|
ASL_ASSERT(m_head == node);
|
|
m_head = nullptr;
|
|
}
|
|
else
|
|
{
|
|
if (m_head == node)
|
|
{
|
|
m_head = node->m_next;
|
|
}
|
|
|
|
node->m_prev->m_next = node->m_next;
|
|
node->m_next->m_prev = node->m_prev;
|
|
}
|
|
|
|
node->m_next = nullptr;
|
|
node->m_prev = nullptr;
|
|
}
|
|
|
|
T* pop_front()
|
|
{
|
|
if (!is_empty())
|
|
{
|
|
T* node = m_head;
|
|
detach(node);
|
|
return node;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
T* pop_back()
|
|
{
|
|
if (!is_empty())
|
|
{
|
|
T* node = m_head->m_prev;
|
|
detach(node);
|
|
return node;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
template<typename TT>
|
|
struct generic_iterator
|
|
{
|
|
TT* m_node;
|
|
bool m_advanced = false;
|
|
|
|
public:
|
|
constexpr explicit generic_iterator(TT* node, bool end = false)
|
|
: m_node{node}
|
|
, m_advanced{end}
|
|
{}
|
|
|
|
constexpr bool operator==(this generic_iterator, generic_iterator other) = default;
|
|
|
|
constexpr generic_iterator& operator++()
|
|
{
|
|
m_node = m_node->m_next;
|
|
m_advanced = true;
|
|
return *this;
|
|
}
|
|
|
|
constexpr generic_iterator operator++(int)
|
|
{
|
|
return iterator{
|
|
exchange(m_node, m_node->m_next), exchange(m_advanced, true)
|
|
};
|
|
}
|
|
|
|
constexpr TT& operator*(this generic_iterator self) { return *self.m_node; }
|
|
|
|
constexpr TT* operator->(this generic_iterator self) { return self.m_node; }
|
|
};
|
|
|
|
using iterator = generic_iterator<T>;
|
|
using const_iterator = generic_iterator<const T>;
|
|
|
|
auto begin(this auto&& self)
|
|
{
|
|
using iterator_type = select_t<is_const<un_ref_t<decltype(self)>>, const_iterator, iterator>;
|
|
return iterator_type{ self.front(), self.is_empty() };
|
|
}
|
|
|
|
auto end(this auto&& self)
|
|
{
|
|
using iterator_type = select_t<is_const<un_ref_t<decltype(self)>>, const_iterator, iterator>;
|
|
return iterator_type{ self.front(), true };
|
|
}
|
|
};
|
|
|
|
} // namespace asl
|
|
|