utils: intrusive_list sorting functions

This commit is contained in:
BlackMATov
2020-04-22 17:39:44 +07:00
parent 379218055d
commit 8e470e1b09
2 changed files with 135 additions and 8 deletions

View File

@@ -109,6 +109,20 @@ namespace e2d
swap_next(l, r); swap_next(l, r);
} }
} }
static void transfer_nodes(node_ptr p, node_ptr b, node_ptr e) noexcept {
if ( b != e ) {
node_ptr prev_p = p->prev_;
node_ptr prev_b = b->prev_;
node_ptr prev_e = e->prev_;
prev_e->next_ = p;
p->prev_ = prev_e;
prev_b->next_ = e;
e->prev_ = prev_b;
prev_p->next_ = b;
b->prev_ = prev_p;
}
}
private: private:
node_ptr prev_{nullptr}; node_ptr prev_{nullptr};
node_ptr next_{nullptr}; node_ptr next_{nullptr};
@@ -252,15 +266,15 @@ namespace e2d
void swap(intrusive_list& other) noexcept; void swap(intrusive_list& other) noexcept;
template < typename Disposer > template < typename Disposer >
void clear_and_dispose(Disposer&& disposer); void clear_and_dispose(Disposer disposer);
void clear() noexcept; void clear() noexcept;
template < typename Disposer > template < typename Disposer >
void pop_back_and_dispose(Disposer&& disposer); void pop_back_and_dispose(Disposer disposer);
void pop_back() noexcept; void pop_back() noexcept;
template < typename Disposer > template < typename Disposer >
void pop_front_and_dispose(Disposer&& disposer); void pop_front_and_dispose(Disposer disposer);
void pop_front() noexcept; void pop_front() noexcept;
void push_back(T& v) noexcept; void push_back(T& v) noexcept;
@@ -268,9 +282,20 @@ namespace e2d
iterator insert(const_iterator pos, T& v) noexcept; iterator insert(const_iterator pos, T& v) noexcept;
template < typename Disposer > template < typename Disposer >
iterator erase_and_dispose(const_iterator pos, Disposer&& disposer); iterator erase_and_dispose(const_iterator pos, Disposer disposer);
iterator erase(const_iterator pos) noexcept; iterator erase(const_iterator pos) noexcept;
void splice(const_iterator pos, intrusive_list& x) noexcept;
void splice(const_iterator pos, const_iterator f, const_iterator e) noexcept;
template < typename Predicate >
void merge(intrusive_list& x, Predicate predicate);
void merge(intrusive_list& x);
template < typename Predicate >
void sort(Predicate predicate);
void sort();
static iterator iterator_to(T& v) noexcept; static iterator iterator_to(T& v) noexcept;
static const_iterator iterator_to(const T& v) noexcept; static const_iterator iterator_to(const T& v) noexcept;
@@ -408,7 +433,7 @@ namespace e2d
template < typename T, typename Tag > template < typename T, typename Tag >
template < typename Disposer > template < typename Disposer >
void intrusive_list<T,Tag>::clear_and_dispose(Disposer&& disposer) { void intrusive_list<T,Tag>::clear_and_dispose(Disposer disposer) {
while ( !empty() ) { while ( !empty() ) {
pop_back_and_dispose(disposer); pop_back_and_dispose(disposer);
} }
@@ -421,7 +446,7 @@ namespace e2d
template < typename T, typename Tag > template < typename T, typename Tag >
template < typename Disposer > template < typename Disposer >
void intrusive_list<T,Tag>::pop_back_and_dispose(Disposer&& disposer) { void intrusive_list<T,Tag>::pop_back_and_dispose(Disposer disposer) {
E2D_ASSERT(!empty()); E2D_ASSERT(!empty());
node_ptr node = root_.prev_; node_ptr node = root_.prev_;
node->unlink(); node->unlink();
@@ -435,7 +460,7 @@ namespace e2d
template < typename T, typename Tag > template < typename T, typename Tag >
template < typename Disposer > template < typename Disposer >
void intrusive_list<T,Tag>::pop_front_and_dispose(Disposer&& disposer) { void intrusive_list<T,Tag>::pop_front_and_dispose(Disposer disposer) {
E2D_ASSERT(!empty()); E2D_ASSERT(!empty());
node_ptr node = root_.next_; node_ptr node = root_.next_;
node->unlink(); node->unlink();
@@ -471,7 +496,7 @@ namespace e2d
template < typename T, typename Tag > template < typename T, typename Tag >
template < typename Disposer > template < typename Disposer >
typename intrusive_list<T,Tag>::iterator intrusive_list<T,Tag>::erase_and_dispose(const_iterator pos, Disposer&& disposer) { typename intrusive_list<T,Tag>::iterator intrusive_list<T,Tag>::erase_and_dispose(const_iterator pos, Disposer disposer) {
node_ptr node = pos.node(); node_ptr node = pos.node();
E2D_ASSERT(node != &root_ && node->is_linked()); E2D_ASSERT(node != &root_ && node->is_linked());
++pos; ++pos;
@@ -485,6 +510,70 @@ namespace e2d
return erase_and_dispose(pos, null_disposer()); return erase_and_dispose(pos, null_disposer());
} }
template < typename T, typename Tag >
void intrusive_list<T,Tag>::splice(const_iterator pos, intrusive_list& x) noexcept {
intrusive_list_hook<Tag>::transfer_nodes(pos.node(), x.begin().node(), x.end().node());
}
template < typename T, typename Tag >
void intrusive_list<T,Tag>::splice(const_iterator pos, const_iterator f, const_iterator e) noexcept {
intrusive_list_hook<Tag>::transfer_nodes(pos.node(), f.node(), e.node());
}
template < typename T, typename Tag >
template < typename Predicate >
void intrusive_list<T,Tag>::merge(intrusive_list& x, Predicate predicate) {
const_iterator b(cbegin()), e(cend()), ex(x.cend());
while ( !x.empty() ) {
const_iterator ix(x.cbegin());
while ( b != e && !predicate(*ix, *b) ) {
++b;
}
if ( b == e ) {
splice(e, x);
break;
} else {
do {
++ix;
} while ( ix != ex && predicate(*ix, *b) );
splice(b, x.begin(), ix);
}
}
}
template < typename T, typename Tag >
void intrusive_list<T,Tag>::merge(intrusive_list& x) {
merge(x, std::less<value_type>());
}
template < typename T, typename Tag >
template < typename Predicate >
void intrusive_list<T,Tag>::sort(Predicate predicate) {
if ( root_.next_ != &root_ && root_.next_ != root_.prev_ ) {
intrusive_list left_list;
intrusive_list right_list;
const_iterator mid(cbegin()), tail(cend());
while ( (mid != tail) && (++mid != tail) ) {
--tail;
}
left_list.splice(left_list.cbegin(), cbegin(), mid);
right_list.splice(right_list.cbegin(), *this);
left_list.sort(predicate);
right_list.sort(predicate);
splice(cbegin(), left_list);
merge(right_list, predicate);
}
}
template < typename T, typename Tag >
void intrusive_list<T,Tag>::sort() {
sort(std::less<value_type>());
}
template < typename T, typename Tag > template < typename T, typename Tag >
typename intrusive_list<T,Tag>::iterator intrusive_list<T,Tag>::iterator_to(T& v) noexcept { typename intrusive_list<T,Tag>::iterator intrusive_list<T,Tag>::iterator_to(T& v) noexcept {
node_t& node = static_cast<node_t&>(v); node_t& node = static_cast<node_t&>(v);

View File

@@ -7,6 +7,8 @@
#include "_utils.hpp" #include "_utils.hpp"
using namespace e2d; using namespace e2d;
#include <random>
namespace namespace
{ {
struct ilist_tag1 {}; struct ilist_tag1 {};
@@ -21,6 +23,10 @@ namespace
obj_t() = default; obj_t() = default;
obj_t(int ni) : i(ni) {} obj_t(int ni) : i(ni) {}
}; };
bool operator<(const obj_t& l, const obj_t& r) noexcept {
return l.i < r.i;
}
} }
TEST_CASE("intrusive_list") { TEST_CASE("intrusive_list") {
@@ -433,4 +439,36 @@ TEST_CASE("intrusive_list") {
} }
} }
} }
SECTION("sort") {
{
std::random_device rd;
std::mt19937 rnd(rd());
std::uniform_int_distribution<u32> uni_size(0, 1024);
std::uniform_int_distribution<i32> uni_number(-65536, 65535);
const auto pred = [](const obj_t& l, const obj_t& r) noexcept {
return l.i < r.i;
};
const auto r_pred = [](const obj_t& l, const obj_t& r) noexcept {
return l.i > r.i;
};
for ( std::size_t i = 0; i < 1024; ++i ) {
vector<std::unique_ptr<obj_t>> objs;
intrusive_list<obj_t, ilist_tag1> l;
for ( u32 b = 0, e = uni_size(rnd); b < e; ++b ) {
objs.push_back(std::make_unique<obj_t>(uni_number(rnd)));
l.push_back(*objs.back());
}
l.sort();
REQUIRE(std::is_sorted(l.cbegin(), l.cend()));
l.sort(r_pred);
REQUIRE(std::is_sorted(l.cbegin(), l.cend(), r_pred));
l.sort(pred);
REQUIRE(std::is_sorted(l.cbegin(), l.cend(), pred));
}
}
}
} }