Merge pull request #37 from enduro2d/feature/intrusive_list

Feature/intrusive_list
This commit is contained in:
BlackMat MATov
2019-01-17 17:47:36 +07:00
committed by GitHub
6 changed files with 815 additions and 2 deletions

View File

@@ -13,11 +13,12 @@
#include "color32.hpp"
#include "filesystem.hpp"
#include "filesystem.inl"
#include "ilist.hpp"
#include "image.hpp"
#include "iptr.hpp"
#include "mesh.hpp"
#include "module.hpp"
#include "path.hpp"
#include "refcount.hpp"
#include "streams.hpp"
#include "strfmts.hpp"
#include "strings.hpp"

View File

@@ -28,6 +28,9 @@ namespace e2d
template < typename T >
class intrusive_ptr;
template < typename T, typename Tag >
class intrusive_list;
template < typename Char >
class basic_string_hash;
}

View File

@@ -0,0 +1,463 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#pragma once
#include "_utils.hpp"
namespace e2d
{
template < typename Tag >
class intrusive_list_hook;
template < typename Tag, typename T, typename TP, typename TR >
class intrusive_list_iterator;
}
namespace e2d
{
template < typename Tag >
class intrusive_list_hook {
public:
using node_ptr = intrusive_list_hook*;
using const_node_ptr = const intrusive_list_hook*;
template < typename U, typename UTag >
friend class intrusive_list;
template < typename UTag, typename U, typename UP, typename UR >
friend class intrusive_list_iterator;
public:
intrusive_list_hook() = default;
~intrusive_list_hook() noexcept {
if ( is_linked() ) {
unlink();
}
}
intrusive_list_hook(node_ptr prev, node_ptr next) noexcept
: prev_(prev)
, next_(next) {}
intrusive_list_hook(const intrusive_list_hook& other) noexcept {
E2D_UNUSED(other);
}
intrusive_list_hook& operator=(const intrusive_list_hook& other) noexcept {
E2D_UNUSED(other);
return *this;
}
private:
bool unique() const noexcept {
return !next_ || next_ == this;
}
bool is_linked() const noexcept {
return !unique();
}
void link_before(node_ptr next) noexcept {
node_ptr prev = next->prev_;
prev_ = prev;
next_ = next;
next->prev_ = this;
prev->next_ = this;
}
void link_after(node_ptr prev) noexcept {
node_ptr next = prev->next_;
next_ = next;
prev_ = prev;
prev->next_ = this;
next->prev_ = this;
}
void unlink() noexcept {
node_ptr next = next_;
node_ptr prev = prev_;
prev->next_ = next;
next->prev_ = prev;
prev_ = nullptr;
next_ = nullptr;
}
static void swap_prev(node_ptr l, node_ptr r){
node_ptr tmp = l->prev_;
l->prev_ = r->prev_;
r->prev_ = tmp;
}
static void swap_next(node_ptr l, node_ptr r){
node_ptr tmp = l->next_;
l->next_ = r->next_;
r->next_ = tmp;
}
static void swap_nodes(node_ptr l, node_ptr r) noexcept {
if ( l != r ) {
node_ptr prev_l = l->prev_;
node_ptr next_l = l->next_;
node_ptr prev_r = r->prev_;
node_ptr next_r = r->next_;
swap_prev(next_l, next_r);
swap_next(prev_l, prev_r);
swap_prev(l, r);
swap_next(l, r);
}
}
private:
node_ptr prev_{nullptr};
node_ptr next_{nullptr};
};
}
namespace e2d
{
template < typename Tag, typename T, typename TP, typename TR >
class intrusive_list_iterator final
: public std::iterator<
std::bidirectional_iterator_tag,
T,
ptrdiff_t,
TP,
TR>
{
public:
using self_type = intrusive_list_iterator;
using node_t = intrusive_list_hook<Tag>;
using node_ptr = typename node_t::node_ptr;
using const_node_ptr = typename node_t::const_node_ptr;
template < typename UTag, typename U, typename UP, typename UR >
friend class intrusive_list_iterator;
public:
intrusive_list_iterator() = default;
intrusive_list_iterator(const_node_ptr node) noexcept
: node_(const_cast<node_ptr>(node)) {}
template < typename UTP, typename UTR >
intrusive_list_iterator(const intrusive_list_iterator<Tag,T,UTP,UTR>& other) noexcept
: node_(const_cast<node_ptr>(other.node_)) {}
node_ptr node() noexcept {
return node_;
}
const_node_ptr node() const noexcept {
return node_;
}
self_type& operator++() noexcept {
node_ = node_->next_;
return *this;
}
const self_type operator++(int) noexcept {
self_type tmp(*this);
node_ = node_->next_;
return tmp;
}
self_type& operator--() noexcept {
node_ = node_->prev_;
return *this;
}
const self_type operator--(int) noexcept {
self_type tmp(*this);
node_ = node_->prev_;
return tmp;
}
TR operator*() const noexcept {
E2D_ASSERT(node_);
return static_cast<TR>(*node_);
}
TP operator->() const noexcept {
E2D_ASSERT(node_);
return static_cast<TP>(node_);
}
private:
node_ptr node_{nullptr};
};
template < typename Tag, typename T, typename LTP, typename LTR, typename RTP, typename RTR >
bool operator==(
const intrusive_list_iterator<Tag,T,LTP,LTR>& l,
const intrusive_list_iterator<Tag,T,RTP,RTR>& r) noexcept
{
return l.node() == r.node();
}
template < typename Tag, typename T, typename LTP, typename LTR, typename RTP, typename RTR >
bool operator!=(
const intrusive_list_iterator<Tag,T,LTP,LTR>& l,
const intrusive_list_iterator<Tag,T,RTP,RTR>& r) noexcept
{
return l.node() != r.node();
}
}
namespace e2d
{
template < typename T, typename Tag >
class intrusive_list final {
public:
using self_type = intrusive_list;
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using iterator = intrusive_list_iterator<Tag, T, pointer, reference>;
using const_iterator = intrusive_list_iterator<Tag, T, const_pointer, const_reference>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
public:
intrusive_list() = default;
~intrusive_list() noexcept;
iterator begin() noexcept;
const_iterator begin() const noexcept;
const_iterator cbegin() const noexcept;
iterator end() noexcept;
const_iterator end() const noexcept;
const_iterator cend() const noexcept;
reverse_iterator rbegin() noexcept;
const_reverse_iterator rbegin() const noexcept;
const_reverse_iterator crbegin() const noexcept;
reverse_iterator rend() noexcept;
const_reverse_iterator rend() const noexcept;
const_reverse_iterator crend() const noexcept;
T& back() noexcept;
const T& back() const noexcept;
T& front() noexcept;
const T& front() const noexcept;
std::size_t size() const noexcept;
bool empty() const noexcept;
void clear() noexcept;
void swap(intrusive_list& other) noexcept;
void pop_back() noexcept;
void pop_front() noexcept;
void push_back(T& v) noexcept;
void push_front(T& v) noexcept;
iterator erase(const_iterator pos) noexcept;
iterator insert(const_iterator pos, T& v) noexcept;
static iterator iterator_to(T& v) noexcept;
static const_iterator iterator_to(const T& v) noexcept;
private:
using node_t = intrusive_list_hook<Tag>;
using node_ptr = typename node_t::node_ptr;
node_t root_{&root_, &root_};
};
}
namespace e2d
{
template < typename T, typename Tag >
intrusive_list<T,Tag>::~intrusive_list() noexcept {
clear();
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::iterator
intrusive_list<T,Tag>::begin() noexcept {
return iterator(root_.next_);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::const_iterator
intrusive_list<T,Tag>::begin() const noexcept {
return const_iterator(root_.next_);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::const_iterator
intrusive_list<T,Tag>::cbegin() const noexcept {
return const_iterator(root_.next_);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::iterator
intrusive_list<T,Tag>::end() noexcept {
return iterator(&root_);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::const_iterator
intrusive_list<T,Tag>::end() const noexcept {
return const_iterator(&root_);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::const_iterator
intrusive_list<T,Tag>::cend() const noexcept {
return const_iterator(&root_);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::reverse_iterator
intrusive_list<T,Tag>::rbegin() noexcept {
return reverse_iterator(&root_);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::const_reverse_iterator
intrusive_list<T,Tag>::rbegin() const noexcept {
return const_reverse_iterator(&root_);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::const_reverse_iterator
intrusive_list<T,Tag>::crbegin() const noexcept {
return const_reverse_iterator(&root_);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::reverse_iterator
intrusive_list<T,Tag>::rend() noexcept {
return reverse_iterator(root_.next_);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::const_reverse_iterator
intrusive_list<T,Tag>::rend() const noexcept {
return const_reverse_iterator(root_.next_);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::const_reverse_iterator
intrusive_list<T,Tag>::crend() const noexcept {
return const_reverse_iterator(root_.next_);
}
template < typename T, typename Tag >
T& intrusive_list<T,Tag>::back() noexcept {
E2D_ASSERT(!empty());
return static_cast<T&>(*root_.prev_);
}
template < typename T, typename Tag >
const T& intrusive_list<T,Tag>::back() const noexcept {
E2D_ASSERT(!empty());
return static_cast<const T&>(*root_.prev_);
}
template < typename T, typename Tag >
T& intrusive_list<T,Tag>::front() noexcept {
E2D_ASSERT(!empty());
return static_cast<T&>(*root_.next_);
}
template < typename T, typename Tag >
const T& intrusive_list<T,Tag>::front() const noexcept {
E2D_ASSERT(!empty());
return static_cast<const T&>(*root_.next_);
}
template < typename T, typename Tag >
std::size_t intrusive_list<T,Tag>::size() const noexcept {
std::size_t result{0u};
const node_t* node = root_.next_;
while ( node != &root_ ) {
++result;
node = node->next_;
}
return result;
}
template < typename T, typename Tag >
bool intrusive_list<T,Tag>::empty() const noexcept {
return root_.unique();
}
template < typename T, typename Tag >
void intrusive_list<T,Tag>::clear() noexcept {
while ( !empty() ) {
pop_back();
}
}
template < typename T, typename Tag >
void intrusive_list<T,Tag>::swap(intrusive_list<T,Tag>& other) noexcept {
intrusive_list_hook<Tag>::swap_nodes(&root_, &other.root_);
}
template < typename T, typename Tag >
void intrusive_list<T,Tag>::pop_back() noexcept {
E2D_ASSERT(!empty());
node_ptr node = root_.prev_;
node->unlink();
}
template < typename T, typename Tag >
void intrusive_list<T,Tag>::pop_front() noexcept {
E2D_ASSERT(!empty());
node_ptr node = root_.next_;
node->unlink();
}
template < typename T, typename Tag >
void intrusive_list<T,Tag>::push_back(T& v) noexcept {
node_t& node = static_cast<node_t&>(v);
E2D_ASSERT(!node.is_linked());
node.link_before(&root_);
}
template < typename T, typename Tag >
void intrusive_list<T,Tag>::push_front(T& v) noexcept {
node_t& node = static_cast<node_t&>(v);
E2D_ASSERT(!node.is_linked());
node.link_after(&root_);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::iterator intrusive_list<T,Tag>::erase(const_iterator pos) noexcept {
node_ptr node = pos.node();
E2D_ASSERT(node != &root_ && node->is_linked());
++pos;
node->unlink();
return iterator(pos.node());
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::iterator intrusive_list<T,Tag>::insert(const_iterator pos, T& v) noexcept {
node_t& node = static_cast<node_t&>(v);
E2D_ASSERT(!node.is_linked());
node.link_before(pos.node());
return iterator(&node);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::iterator intrusive_list<T,Tag>::iterator_to(T& v) noexcept {
node_t& node = static_cast<node_t&>(v);
E2D_ASSERT(node.is_linked());
return iterator(&node);
}
template < typename T, typename Tag >
typename intrusive_list<T,Tag>::const_iterator intrusive_list<T,Tag>::iterator_to(const T& v) noexcept {
const node_t& node = static_cast<const node_t&>(v);
E2D_ASSERT(node.is_linked());
return const_iterator(&node);
}
}

View File

@@ -0,0 +1,346 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "_utils.hpp"
using namespace e2d;
namespace
{
struct ilist_tag1 {};
struct ilist_tag2 {};
class obj_t
: public intrusive_list_hook<ilist_tag1>
, public intrusive_list_hook<ilist_tag2>
{
public:
int i{42};
obj_t() = default;
obj_t(int ni) : i(ni) {}
};
}
TEST_CASE("ilist") {
{
intrusive_list<obj_t, ilist_tag1> l;
REQUIRE(l.empty());
REQUIRE_FALSE(l.size());
}
SECTION("push_back/push_front") {
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
auto o3 = std::make_unique<obj_t>(3);
auto o4 = std::make_unique<obj_t>(4);
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
REQUIRE(l.back().i == 1);
REQUIRE(l.front().i == 1);
l.push_back(*o2);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 1);
l.push_front(*o3);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 3);
l.push_front(*o4);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 4);
}
}
SECTION("pop_back/pop_front") {
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
auto o3 = std::make_unique<obj_t>(3);
auto o4 = std::make_unique<obj_t>(4);
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
l.push_back(*o3);
l.push_back(*o4);
l.pop_front();
REQUIRE(l.front().i == 2);
REQUIRE(l.back().i == 4);
l.pop_back();
REQUIRE(l.front().i == 2);
REQUIRE(l.back().i == 3);
l.pop_front();
REQUIRE(l.front().i == 3);
REQUIRE(l.back().i == 3);
l.pop_back();
REQUIRE(l.empty());
}
}
SECTION("empty/size/clear") {
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
auto o3 = std::make_unique<obj_t>(2);
{
intrusive_list<obj_t, ilist_tag1> l;
REQUIRE(l.empty());
REQUIRE_FALSE(l.size());
l.push_back(*o1);
REQUIRE_FALSE(l.empty());
REQUIRE(l.size() == 1u);
l.push_front(*o2);
REQUIRE_FALSE(l.empty());
REQUIRE(l.size() == 2u);
l.push_back(*o3);
REQUIRE_FALSE(l.empty());
REQUIRE(l.size() == 3u);
l.pop_front();
REQUIRE_FALSE(l.empty());
REQUIRE(l.size() == 2u);
l.pop_back();
REQUIRE_FALSE(l.empty());
REQUIRE(l.size() == 1u);
l.pop_front();
REQUIRE(l.empty());
REQUIRE_FALSE(l.size());
}
{
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
l.push_back(*o3);
REQUIRE_FALSE(l.empty());
REQUIRE(l.size() == 3u);
}
}
SECTION("swap") {
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
auto o3 = std::make_unique<obj_t>(3);
auto o4 = std::make_unique<obj_t>(4);
intrusive_list<obj_t, ilist_tag1> l1;
intrusive_list<obj_t, ilist_tag1> l2;
l1.push_back(*o1);
l1.push_back(*o2);
l2.push_back(*o3);
l2.push_back(*o4);
l1.swap(l2);
REQUIRE(l1.back().i == 4);
REQUIRE(l1.front().i == 3);
REQUIRE(l2.back().i == 2);
REQUIRE(l2.front().i == 1);
}
}
SECTION("insert/erase") {
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
auto o3 = std::make_unique<obj_t>(3);
auto o4 = std::make_unique<obj_t>(4);
intrusive_list<obj_t, ilist_tag1> l;
REQUIRE(l.insert(l.end(), *o1)->i == 1); // 1
{
auto i = l.insert(++l.begin(), *o2); // 1 2
REQUIRE(std::distance(l.begin(), i) == 1);
}
{
auto i = l.insert(l.begin(), *o3); // 3 1 2
REQUIRE(std::distance(l.begin(), i) == 0);
}
{
auto iter = l.begin();
REQUIRE(iter->i == 3);
REQUIRE((++iter)->i == 1);
REQUIRE((++iter)->i == 2);
REQUIRE(++iter == l.end());
}
// 3 1 2 4
l.push_back(*o4);
// 3 1 2
REQUIRE(l.erase(--l.end()) == l.end());
REQUIRE(l.size() == 3);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 3);
// 3 2
REQUIRE(l.erase(++l.begin())->i == 2);
REQUIRE(l.size() == 2);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 3);
// 2
REQUIRE(l.erase(l.begin())->i == 2);
REQUIRE(l.size() == 1);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 2);
}
}
SECTION("iterator_to") {
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
auto o3 = std::make_unique<obj_t>(3);
auto o4 = std::make_unique<obj_t>(4);
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
l.push_back(*o3);
l.push_back(*o4);
REQUIRE(l.iterator_to(*o1) == l.begin());
REQUIRE(l.iterator_to(*o2) == ++l.begin());
{
const auto& co3 = o3;
const auto& co4 = o4;
REQUIRE(l.iterator_to(*co3) == --(--l.end()));
REQUIRE(l.iterator_to(*co4) == --l.end());
}
}
SECTION("auto_unlink") {
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
o1.reset();
REQUIRE(l.size() == 1u);
REQUIRE(l.back().i == 2);
REQUIRE(l.front().i == 2);
}
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
o2.reset();
REQUIRE(l.size() == 1u);
REQUIRE(l.back().i == 1);
REQUIRE(l.front().i == 1);
}
}
SECTION("multitag") {
{
auto o1 = std::make_unique<obj_t>();
intrusive_list<obj_t, ilist_tag1> l1;
intrusive_list<obj_t, ilist_tag2> l2;
l1.push_back(*o1);
l2.push_back(*o1);
REQUIRE(l1.size() == 1);
REQUIRE(l2.size() == 1);
REQUIRE(l1.begin()->i == 42);
REQUIRE(l2.begin()->i == 42);
l2.pop_back();
REQUIRE(l1.size() == 1);
REQUIRE(l1.begin()->i == 42);
REQUIRE(l2.empty());
}
}
SECTION("iterator") {
{
auto o1 = std::make_unique<obj_t>(1);
auto o2 = std::make_unique<obj_t>(2);
intrusive_list<obj_t, ilist_tag1> l;
l.push_back(*o1);
l.push_back(*o2);
std::transform(l.begin(), l.end(), l.begin(), [](obj_t& o){
return o.i * 5;
});
{
auto iter = l.begin();
REQUIRE((*iter).i == 5);
REQUIRE((++iter)->i == 10);
}
{
auto iter = l.rbegin();
REQUIRE((*iter).i == 10);
REQUIRE((++iter)->i == 5);
}
REQUIRE(15 == std::accumulate(l.begin(), l.end(), 0, [](int acc, obj_t& o){
return acc + o.i;
}));
REQUIRE(15 == std::accumulate(l.rbegin(), l.rend(), 0, [](int acc, obj_t& o){
return acc + o.i;
}));
{
const auto& cl = l;
REQUIRE(15 == std::accumulate(cl.begin(), cl.end(), 0, [](int acc, const obj_t& o){
return acc + o.i;
}));
REQUIRE(15 == std::accumulate(cl.rbegin(), cl.rend(), 0, [](int acc, const obj_t& o){
return acc + o.i;
}));
}
{
REQUIRE(l.begin() == l.begin());
REQUIRE(l.begin() == l.cbegin());
REQUIRE(l.begin() != l.end());
REQUIRE(l.begin() != l.cend());
REQUIRE(l.cbegin() == l.begin());
REQUIRE(l.cbegin() == l.cbegin());
REQUIRE(l.cbegin() != l.end());
REQUIRE(l.cbegin() != l.cend());
REQUIRE(l.end() == l.end());
REQUIRE(l.end() == l.cend());
REQUIRE(l.end() != l.begin());
REQUIRE(l.end() != l.cbegin());
REQUIRE(l.cend() == l.end());
REQUIRE(l.cend() == l.cend());
REQUIRE(l.cend() != l.begin());
REQUIRE(l.cend() != l.cbegin());
}
{
REQUIRE(l.rbegin() == l.rbegin());
REQUIRE(l.rbegin() == l.crbegin());
REQUIRE(l.rbegin() != l.rend());
REQUIRE(l.rbegin() != l.crend());
REQUIRE(l.crbegin() == l.rbegin());
REQUIRE(l.crbegin() == l.crbegin());
REQUIRE(l.crbegin() != l.rend());
REQUIRE(l.crbegin() != l.crend());
REQUIRE(l.rend() == l.rend());
REQUIRE(l.rend() == l.crend());
REQUIRE(l.rend() != l.rbegin());
REQUIRE(l.rend() != l.crbegin());
REQUIRE(l.crend() == l.rend());
REQUIRE(l.crend() == l.crend());
REQUIRE(l.crend() != l.rbegin());
REQUIRE(l.crend() != l.crbegin());
}
}
}
}

View File

@@ -74,7 +74,7 @@ namespace
int obj2_t::dtor_counter = 0;
}
TEST_CASE("refcount") {
TEST_CASE("iptr") {
{
intrusive_ptr<obj_t> p;
REQUIRE_FALSE(p);