From 8144554320953339dea9236cd6c6de271927e27d Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Wed, 10 Feb 2016 19:05:42 +0000 Subject: [PATCH] Added intrusive_list and updated intrusive_forward list. Added set of intrusive links. --- intrusive_forward_list.h | 442 ++++++++++- intrusive_links.h | 576 +++++++++++--- test/test_intrusive_forward_list.cpp | 394 +++++++-- test/test_intrusive_links.cpp | 580 +++++++++++++- test/test_intrusive_list.cpp | 1098 ++++++++++++++++++++++++++ test/vs2015/etl.sln | 3 + test/vs2015/etl.vcxproj | 4 +- test/vs2015/etl.vcxproj.filters | 12 +- 8 files changed, 2864 insertions(+), 245 deletions(-) create mode 100644 test/test_intrusive_list.cpp diff --git a/intrusive_forward_list.h b/intrusive_forward_list.h index a66a451e..04260181 100644 --- a/intrusive_forward_list.h +++ b/intrusive_forward_list.h @@ -7,7 +7,7 @@ Embedded Template Library. https://github.com/ETLCPP/etl http://www.etlcpp.com -Copyright(c) 2015 jwellbelove +Copyright(c) 2016 jwellbelove Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal @@ -45,6 +45,7 @@ SOFTWARE. #include "exception.h" #include "error_handler.h" #include "intrusive_links.h" +#include "algorithm.h" #undef ETL_FILE #define ETL_FILE "20" @@ -107,12 +108,26 @@ namespace etl } }; + //*************************************************************************** + /// Unsorted exception for the intrusive_forward_list. + ///\ingroup intrusive_list + //*************************************************************************** + class intrusive_forward_list_unsorted : public intrusive_forward_list_exception + { + public: + + intrusive_forward_list_unsorted(string_type file_name, numeric_type line_number) + : intrusive_forward_list_exception(ETL_ERROR_TEXT("intrusive_forward_list:unsorted", ETL_FILE"D"), file_name, line_number) + { + } + }; + //*************************************************************************** /// An intrusive forward list. ///\ingroup intrusive_forward_list ///\note TLink must be a base of TValue. //*************************************************************************** - template > + template , const size_t COUNT_OPTION = etl::count_option::FAST_COUNT > class intrusive_forward_list { public: @@ -128,7 +143,7 @@ namespace etl typedef const value_type& const_reference; typedef size_t size_type; - public: + typedef intrusive_forward_list list_type; //************************************************************************* /// iterator. @@ -239,7 +254,7 @@ namespace etl { } - const_iterator(const typename intrusive_forward_list::iterator& other) + const_iterator(const typename intrusive_forward_list::iterator& other) : p_value(other.p_value) { } @@ -427,8 +442,7 @@ namespace etl while (first != last) { link_type& link = *first++; - join(p_last_link, &link); - join(&link, nullptr); + etl::link_splice(p_last_link, link); p_last_link = &link; ++current_size; } @@ -475,24 +489,40 @@ namespace etl second = track; // Move second link to next } - join(&start_link, first); + etl::link(start_link, first); } //************************************************************************* /// Inserts a value to the intrusive_forward_list after the specified position. //************************************************************************* - iterator insert_after(iterator position, value_type& value) + template + typename etl::enable_if::type + insert_after(iterator position, value_type& value) { insert_link_after(*position.p_value, value); + return iterator(value); + } + //************************************************************************* + /// Inserts a value to the intrusive_forward_list after the specified position. + /// Checks that the value is unlinked. + //************************************************************************* + template + typename etl::enable_if::type + insert_after(iterator position, value_type& value) + { + ETL_ASSERT(!value.TLink::is_linked(), ETL_ERROR(etl::not_unlinked_exception)); + + insert_link_after(*position.p_value, value); return iterator(value); } //************************************************************************* /// Inserts a range of values to the intrusive_forward_list after the specified position. //************************************************************************* - template - void insert_after(iterator position, TIterator first, TIterator last) + template + typename etl::enable_if::type + insert_after(iterator position, TIterator first, TIterator last) { while (first != last) { @@ -502,10 +532,30 @@ namespace etl } } + //************************************************************************* + /// Inserts a range of values to the intrusive_forward_list after the specified position. + /// Checks that the values are unlinked. + //************************************************************************* + template + typename etl::enable_if::type + insert_after(iterator position, TIterator first, TIterator last) + { + while (first != last) + { + ETL_ASSERT(!position.p_value->TLink::is_linked(), ETL_ERROR(etl::not_unlinked_exception)); + + // Set up the next free link. + insert_link_after(*position.p_value, *first++); + ++position; + } + } + //************************************************************************* /// Erases the value at the specified position. //************************************************************************* - iterator erase_after(iterator position) + template + typename etl::enable_if::type + erase_after(iterator position) { iterator next(position); ++next; @@ -516,17 +566,37 @@ namespace etl return next; } + //************************************************************************* + /// Erases the value at the specified position. + /// Clears the link after erasing. + //************************************************************************* + template + typename etl::enable_if::type + erase_after(iterator position) + { + iterator next(position); + ++next; + ++next; + + remove_link_after(*position.p_value); + position.p_value->TLink::clear(); + + return next; + } + //************************************************************************* /// Erases a range of elements. //************************************************************************* - iterator erase_after(iterator first, iterator last) + template + typename etl::enable_if::type + erase_after(iterator first, iterator last) { link_type* p_first = first.p_value; link_type* p_last = last.p_value; link_type* p_next = p_first->etl_next; // Join the ends. - join(p_first, p_last); + etl::link(p_first, p_last); p_first = p_next; @@ -550,6 +620,44 @@ namespace etl } } + //************************************************************************* + /// Erases a range of elements. + /// Clears the links after erasing. + //************************************************************************* + template + typename etl::enable_if::type + erase_after(iterator first, iterator last) + { + link_type* p_first = first.p_value; + link_type* p_last = last.p_value; + link_type* p_next = p_first->etl_next; + + // Join the ends. + etl::link(p_first, p_last); + + p_first = p_next; + + // Erase the ones in between. + while (p_first != p_last) + { + // One less. + --current_size; + + p_next = p_first->etl_next; // Remember the next link. + p_first->TLink::clear(); // Clear the link. + p_first = p_next; // Move to the next link. + } + + if (p_next == nullptr) + { + return end(); + } + else + { + return iterator(*static_cast(p_last)); + } + } + //************************************************************************* /// Removes all but the one element from every consecutive group of equal /// elements in the container. @@ -679,14 +787,14 @@ namespace etl // Add the next link to the merged head. if (i_head == before_begin()) { - join(i_head.p_value, i_link.p_value); - i_head = i_link; - i_tail = i_link; + etl::link(i_head.p_value, i_link.p_value); + i_head = i_link; + i_tail = i_link; } else { - join(i_tail.p_value, i_link.p_value); - i_tail = i_link; + etl::link(i_tail.p_value, i_link.p_value); + i_tail = i_link; } i_tail.p_value->link_type::etl_next = nullptr; @@ -737,7 +845,7 @@ namespace etl { iterator i_item = begin(); iterator i_last_item = before_begin(); - + while (i_item != end()) { if (predicate(*i_item)) @@ -753,41 +861,296 @@ namespace etl } //************************************************************************* - /// Returns true if the list has not elements. + /// Returns true if the list has no elements. //************************************************************************* bool empty() const { - return current_size == 0; + return start_link.etl_next == nullptr; } //************************************************************************* /// Returns the number of elements. //************************************************************************* - size_t size() const + template + typename etl::enable_if::type + size() const { - return current_size; + return current_size.count; + } + + //************************************************************************* + /// Returns the number of elements. + //************************************************************************* + template + typename etl::enable_if::type + size() const + { + return std::distance(cbegin(), cend()); + } + + //************************************************************************* + /// Splice another list into this one. + //************************************************************************* + void splice_after(iterator position, list_type& list) + { + // No point splicing to ourself! + if (&list != this) + { + if (!list.empty()) + { + link_type& first = list.get_head(); + + if (COUNT_OPTION == etl::count_option::FAST_COUNT) + { + if (&list != this) + { + current_size += list.size(); + } + } + + link_type& before = *position.p_value; + link_type& after = *position.p_value->link_type::etl_next; + + etl::link(before, first); + + link_type* last = &before; + while (last->link_type::etl_next != nullptr) + { + last = last->link_type::etl_next; + } + + etl::link(last, after); + + list.clear(); + } + } + } + + //************************************************************************* + /// Splice an element from another list into this one. + //************************************************************************* + void splice(iterator position, list_type& list, iterator isource) + { + link_type& before = *position.p_value; + + etl::unlink(*isource.p_value); + etl::link_splice(before, *isource.p_value); + + if (COUNT_OPTION == etl::count_option::FAST_COUNT) + { + if (&list != this) + { + ++current_size; + --list.current_size; + } + } + } + + //************************************************************************* + /// Splice a range of elements from another list into this one. + //************************************************************************* + void splice_after(iterator position, list_type& list, iterator begin_, iterator end_) + { + if (!list.empty()) + { + if (COUNT_OPTION == etl::count_option::FAST_COUNT) + { + if (&list != this) + { + size_t n = std::distance(begin_, end_) - 1; + current_size += n; + list.current_size -= n; + } + } + + link_type* first = begin_.p_value; + link_type* last = first; + + while (last->link_type::etl_next != end_.p_value) + { + last = last->link_type::etl_next; + } + + // Unlink from the source list. + link_type* first_next = first->link_type::etl_next; + etl::unlink_after(*first, *last); + + // Fix our links. + link_type* before = position.p_value; + + etl::link_splice(*before, *first_next, *last); + } + } + + //************************************************************************* + /// Merge another list into this one. Both lists should be sorted. + //************************************************************************* + void merge(list_type& list) + { + merge(list, std::less()); + } + + //************************************************************************* + /// Merge another list into this one. Both lists should be sorted. + //************************************************************************* + template + void merge(list_type& list, TCompare compare) + { + if (!list.empty()) + { +#if _DEBUG + ETL_ASSERT(etl::is_sorted(list.begin(), list.end(), compare), ETL_ERROR(intrusive_forward_list_unsorted)); + ETL_ASSERT(etl::is_sorted(begin(), end(), compare), ETL_ERROR(intrusive_forward_list_unsorted)); +#endif + + value_type* other_begin = static_cast(&list.get_head()); + value_type* other_terminal = nullptr; + + value_type* before = static_cast(&start_link); + value_type* before_next = get_next(before); + value_type* terminal = nullptr; + + while ((before->link_type::etl_next != terminal) && (other_begin != other_terminal)) + { + // Find the place to insert. + while ((before_next != terminal) && !(compare(*other_begin, *before_next))) + { + before = before_next; + before_next = get_next(before_next); + } + + // Insert. + if (before_next != terminal) + { + while ((other_begin != other_terminal) && (compare(*other_begin, *before_next))) + { + value_type* value = other_begin; + other_begin = get_next(other_begin); + etl::link_splice(*before, *value); + before = get_next(before); + } + } + } + + // Any left over? + if (before_next == terminal) + { + while (other_begin != other_terminal) + { + value_type* value = other_begin; + other_begin = get_next(other_begin); + etl::link_splice(*before, *value); + before = get_next(before); + } + } + + if (COUNT_OPTION == etl::count_option::FAST_COUNT) + { + current_size += list.size(); + } + + list.clear(); + } } private: link_type start_link; ///< The link that acts as the intrusive_forward_list start. - size_t current_size; ///< The number of elements in the list. - size_t index; ///< The index level of the link that this list operates on. + + //************************************************************************* + /// Counter type based on count option. + //************************************************************************* + template + class counter_type; //************************************************************************* - /// Join two links. + /// Slow type. //************************************************************************* - void join(link_type* left, link_type* right) + template <> + class counter_type { - left->etl_next = right; - } + public: + counter_type& operator ++() + { + return *this; + } + + counter_type& operator --() + { + return *this; + } + + counter_type& operator =(size_t new_count) + { + return *this; + } + + counter_type& operator +=(size_t diff) + { + return *this; + } + + counter_type& operator -=(size_t diff) + { + return *this; + } + }; + + //************************************************************************* + /// Fast type. + //************************************************************************* + template <> + class counter_type + { + public: + + counter_type() + : count(0) + { + } + + counter_type& operator ++() + { + ++count; + return *this; + } + + counter_type& operator --() + { + --count; + return *this; + } + + counter_type& operator =(size_t new_count) + { + count = new_count; + return *this; + } + + counter_type& operator +=(size_t diff) + { + count += diff; + return *this; + } + + counter_type& operator -=(size_t diff) + { + count -= diff; + return *this; + } + + size_t count; + }; + + counter_type current_size; ///< Counts the number of elements in the list. + //************************************************************************* /// Is the intrusive_forward_list a trivial length? //************************************************************************* bool is_trivial_list() const { - return current_size <= 1; + return (start_link.link_type::etl_next == nullptr) || (start_link.link_type::etl_next->etl_next == nullptr);; } //************************************************************************* @@ -796,8 +1159,7 @@ namespace etl void insert_link_after(link_type& position, link_type& link) { // Connect to the intrusive_forward_list. - join(&link, position.etl_next); - join(&position, &link); + etl::link_splice(position, link); ++current_size; } @@ -806,13 +1168,9 @@ namespace etl //************************************************************************* void remove_link_after(link_type& link) { - // The link to erase. - link_type* p_link = link.etl_next; - - if (p_link != nullptr) + if (link.etl_next != nullptr) { - // Disconnect the link from the intrusive_forward_list. - join(&link, p_link->etl_next); + etl::unlink_after(link); --current_size; } } @@ -833,6 +1191,14 @@ namespace etl return *start_link.etl_next; } + //************************************************************************* + /// Get the next value. + //************************************************************************* + value_type* get_next(link_type* link) const + { + return static_cast(link->etl_next); + } + //************************************************************************* /// Initialise the intrusive_forward_list. //************************************************************************* diff --git a/intrusive_links.h b/intrusive_links.h index e2f380dd..d59d81fe 100644 --- a/intrusive_links.h +++ b/intrusive_links.h @@ -31,13 +31,26 @@ SOFTWARE. #ifndef __ETL_INTRUSIVE_LINKS__ #define __ETL_INTRUSIVE_LINKS__ +#include + #include "nullptr.h" #include "type_traits.h" #include "exception.h" #include "error_handler.h" #undef ETL_FILE -#define ETL_FILE "23" +#define ETL_FILE "21" + +//***************************************************************************** +// Note: +// The link functions work slightly differently to the STL 'insert' convention +// in that the second link parameter will be inserted after the first. +// i.e. +// If the list contains '1', '2', '3', '4' and "link_splice '2','5'" is invoked the +// resulting list will contain '1', '2', '5', '3', '4' +// This is to maintain consistency between forward and bidirectional links +// and also is intuitive. +//***************************************************************************** namespace etl { @@ -45,48 +58,120 @@ namespace etl { enum { - DEFAULT = 0, - AUTO = 1, - SAFE = 2 + DEFAULT, + AUTO, + CHECKED }; }; + namespace count_option + { + enum + { + SLOW_COUNT, + FAST_COUNT + }; + } + //*************************************************************************** - /// Deque full exception. - ///\ingroup deque + /// Link exception. //*************************************************************************** class link_exception : public etl::exception { public: - link_exception(string_type file_name, numeric_type line_number) - : exception(ETL_ERROR_TEXT("link:still linked", ETL_FILE"A"), file_name, line_number) + link_exception(string_type what, string_type file_name, numeric_type line_number) + : exception(what, file_name, line_number) { } }; + //*************************************************************************** + /// not unlinked exception. + //*************************************************************************** + class not_unlinked_exception : public etl::link_exception + { + public: + + not_unlinked_exception(string_type file_name, numeric_type line_number) + : link_exception(ETL_ERROR_TEXT("link:still linked", ETL_FILE"A"), file_name, line_number) + { + } + }; + + namespace __private_intrusive_links__ + { + //*************************************************************************** + /// A forward link base. + //*************************************************************************** + template + struct forward_link_base + { + enum + { + ID = ID_, + OPTION = OPTION_ + }; + + void clear() + { + etl_next = nullptr; + } + + bool is_linked() const + { + return etl_next != nullptr; + } + + TLink* etl_next; + }; + } + //*************************************************************************** /// A forward link. //*************************************************************************** - template - struct forward_link + template + struct forward_link + : public __private_intrusive_links__::forward_link_base, ID_, OPTION_> { - enum - { - ID = ID_ - }; + }; - void clear() + //****************************************************************** + // There is no valid specialisation for auto link + //****************************************************************** + template + struct forward_link + : public __private_intrusive_links__::forward_link_base, ID_, etl::link_option::AUTO> + { + forward_link() { - etl_next = nullptr; + clear(); + } + }; + + //****************************************************************** + // Specialisation for checked unlink option. + // An error will be generated if the links are valid when the object + // is destroyed. + //****************************************************************** + template + struct forward_link + : public __private_intrusive_links__::forward_link_base, ID_, etl::link_option::CHECKED> + { + forward_link() + { + clear(); } - forward_link* etl_next; + ~forward_link() + { + assert(etl_next != nullptr); + } }; // Reference, Reference template - typename etl::enable_if >::value, void>::type + typename etl::enable_if >::value, void>::type link(TLink& lhs, TLink& rhs) { lhs.etl_next = &rhs; @@ -94,8 +179,8 @@ namespace etl // Reference, Reference template - typename etl::enable_if >::value, void>::type - link_insert(TLink& lhs, TLink& rhs) + typename etl::enable_if >::value, void>::type + link_splice(TLink& lhs, TLink& rhs) { rhs.etl_next = lhs.etl_next; lhs.etl_next = &rhs; @@ -103,7 +188,7 @@ namespace etl // Pointer, Pointer template - typename etl::enable_if >::value, void>::type + typename etl::enable_if >::value, void>::type link(TLink* lhs, TLink* rhs) { if (lhs != nullptr) @@ -114,8 +199,8 @@ namespace etl // Pointer, Pointer template - typename etl::enable_if >::value, void>::type - link_insert(TLink* lhs, TLink* rhs) + typename etl::enable_if >::value, void>::type + link_splice(TLink* lhs, TLink* rhs) { if (lhs != nullptr) { @@ -130,7 +215,7 @@ namespace etl // Reference, Pointer template - typename etl::enable_if >::value, void>::type + typename etl::enable_if >::value, void>::type link(TLink& lhs, TLink* rhs) { lhs.etl_next = rhs; @@ -138,8 +223,8 @@ namespace etl // Reference, Pointer template - typename etl::enable_if >::value, void>::type - link_insert(TLink& lhs, TLink* rhs) + typename etl::enable_if >::value, void>::type + link_splice(TLink& lhs, TLink* rhs) { if (rhs != nullptr) { @@ -151,7 +236,7 @@ namespace etl // Pointer, Reference template - typename etl::enable_if >::value, void>::type + typename etl::enable_if >::value, void>::type link(TLink* lhs, TLink& rhs) { if (lhs != nullptr) @@ -162,8 +247,8 @@ namespace etl // Pointer, Reference template - typename etl::enable_if >::value, void>::type - link_insert(TLink* lhs, TLink& rhs) + typename etl::enable_if >::value, void>::type + link_splice(TLink* lhs, TLink& rhs) { if (lhs != nullptr) { @@ -172,51 +257,125 @@ namespace etl } } + // Reference, Reference, Reference + template + typename etl::enable_if >::value, void>::type + link_splice(TLink& lhs, TLink& first, TLink& last) + { + last.etl_next = lhs.etl_next; + lhs.etl_next = &first; + } + + // Pointer, Reference, Reference + template + typename etl::enable_if >::value, void>::type + link_splice(TLink* lhs, TLink& first, TLink& last) + { + if (lhs != nullptr) + { + last.etl_next = lhs->etl_next; + lhs->etl_next = &first; + } + else + { + last.etl_next = nullptr; + } + } + // Reference template - typename etl::enable_if >::value, void>::type + typename etl::enable_if >::value, void>::type unlink_after(TLink& node) { if (node.etl_next != nullptr) { node.etl_next = node.etl_next->etl_next; + + if ((TLink::OPTION == etl::link_option::AUTO) || + (TLink::OPTION == etl::link_option::CHECKED)) + { + node.clear(); + } } } - // Pointer + // Reference, Reference template - typename etl::enable_if >::value, void>::type - unlink_after(TLink* node) + typename etl::enable_if >::value, void>::type + unlink_after(TLink& before, TLink& last) { - if (node != nullptr) + before.etl_next = last.etl_next; + + if ((TLink::OPTION == etl::link_option::AUTO) || + (TLink::OPTION == etl::link_option::CHECKED)) { - if (node->etl_next != nullptr) - { - node->etl_next = node.etl_next->etl_next; - } + last.clear(); } } + namespace __private_intrusive_links__ + { + //*************************************************************************** + /// A bidirectional link base. + //*************************************************************************** + template + struct bidirectional_link_base + { + enum + { + ID = ID_, + OPTION = OPTION_ + }; + + void clear() + { + etl_previous = nullptr; + etl_next = nullptr; + } + + bool is_linked() const + { + return (etl_previous != nullptr) || (etl_next != nullptr); + } + + void reverse() + { + std::swap(etl_previous, etl_next); + } + + TLink* etl_previous; + TLink* etl_next; + + protected: + + void base_unlink() + { + // Connect the previous link with the next. + if (etl_previous != nullptr) + { + etl_previous->etl_next = etl_next; + } + + // Connect the next link with the previous. + if (etl_next != nullptr) + { + etl_next->etl_previous = etl_previous; + } + } + }; + } + //*************************************************************************** /// A bidirectional link. //*************************************************************************** - template - struct bidirectional_link + template + struct bidirectional_link + : public __private_intrusive_links__::bidirectional_link_base, ID_, OPTION_> { - enum + void unlink() { - ID = ID_, - OPTION = OPTION_ - }; - - void clear() - { - etl_previous = nullptr; - etl_next = nullptr; + base_unlink(); } - - bidirectional_link* etl_previous; - bidirectional_link* etl_next; }; //****************************************************************** @@ -224,67 +383,51 @@ namespace etl // When this link is destroyed it will automatically unlink itself. //****************************************************************** template - struct bidirectional_link + struct bidirectional_link + : public __private_intrusive_links__::bidirectional_link_base, ID_, etl::link_option::AUTO> { - enum + bidirectional_link() { - ID = ID_, - OPTION = etl::link_option::AUTO_UNLINK - }; + clear(); + } ~bidirectional_link() { - // Connect the previous link with the next. - if (etl_previous != nullptr) - { - etl_previous->etl_next = etl_next; - } - - // Connect the next link with the previous. - if (etl_next != nullptr) - { - etl_next->etl_previous = etl_previous; - } + base_unlink(); } - void clear() + void unlink() { - etl_previous = nullptr; - etl_next = nullptr; + base_unlink(); + clear(); } - - bidirectional_link* etl_previous; - bidirectional_link* etl_next; }; //****************************************************************** - // Specialisation for safe unlinked option. + // Specialisation for checked unlink option. // An error will be generated if the links are valid when the object // is destroyed. //****************************************************************** template - struct bidirectional_link + struct bidirectional_link + : public __private_intrusive_links__::bidirectional_link_base, ID_, etl::link_option::CHECKED> { - enum + bidirectional_link() { - ID = ID_, - OPTION = etl::link_option::AUTO_UNLINK - }; + clear(); + } ~bidirectional_link() { - ETL_ASSERT(etl_previous == nullptr, ETL_ERROR(link_exception)); - ETL_ASSERT(etl_next == nullptr, ETL_ERROR(link_exception)); + assert(etl_previous == nullptr); + assert(etl_next == nullptr); } - void clear() + void unlink() { - etl_previous = nullptr; - etl_next = nullptr; + base_unlink(); + clear(); } - - bidirectional_link* etl_previous; - bidirectional_link* etl_next; }; // Reference, Reference @@ -296,6 +439,22 @@ namespace etl rhs.etl_previous = &lhs; } + // Reference, Reference + template + typename etl::enable_if >::value, void>::type + link_splice(TLink& lhs, TLink& rhs) + { + rhs.etl_next = lhs.etl_next; + rhs.etl_previous = &lhs; + + if (lhs.etl_next != nullptr) + { + lhs.etl_next->etl_previous = &rhs; + } + + lhs.etl_next = &rhs; + } + // Pointer, Pointer template typename etl::enable_if >::value, void>::type @@ -312,6 +471,32 @@ namespace etl } } + // Pointer, Pointer + template + typename etl::enable_if >::value, void>::type + link_splice(TLink* lhs, TLink* rhs) + { + if (rhs != nullptr) + { + if (lhs != nullptr) + { + rhs->etl_next = lhs->etl_next; + } + + rhs->etl_previous = lhs; + } + + if (lhs != nullptr) + { + if (lhs->etl_next != nullptr) + { + lhs->etl_next->etl_previous = rhs; + } + + lhs->etl_next = rhs; + } + } + // Reference, Pointer template typename etl::enable_if >::value, void>::type @@ -325,6 +510,25 @@ namespace etl } } + // Reference, Pointer + template + typename etl::enable_if >::value, void>::type + link_splice(TLink& lhs, TLink* rhs) + { + if (rhs != nullptr) + { + rhs->etl_next = lhs.etl_next; + rhs->etl_previous = &lhs; + } + + if (lhs.etl_next != nullptr) + { + lhs.etl_next->etl_previous = rhs; + } + + lhs.etl_next = rhs; + } + // Pointer, Reference template typename etl::enable_if >::value, void>::type @@ -338,67 +542,189 @@ namespace etl rhs.etl_previous = lhs; } + // Pointer, Reference + template + typename etl::enable_if >::value, void>::type + link_splice(TLink* lhs, TLink& rhs) + { + if (lhs != nullptr) + { + rhs.etl_next = lhs->etl_next; + } + + rhs.etl_previous = lhs; + + if (lhs != nullptr) + { + if (lhs->etl_next != nullptr) + { + lhs->etl_next->etl_previous = &rhs; + } + + lhs->etl_next = &rhs; + } + } + + // Reference, Reference, Reference + template + typename etl::enable_if >::value, void>::type + link_splice(TLink& lhs, TLink& first, TLink& last) + { + last.etl_next = lhs.etl_next; + first.etl_previous = &lhs; + + if (last.etl_next != nullptr) + { + last.etl_next->etl_previous = &last; + } + + lhs.etl_next = &first; + } + + // Pointer, Reference, Reference + template + typename etl::enable_if >::value, void>::type + link_splice(TLink* lhs, TLink& first, TLink& last) + { + if (lhs != nullptr) + { + last.etl_next = lhs->etl_next; + } + else + { + last.etl_next = nullptr; + } + + first.etl_previous = lhs; + + if (last.etl_next != nullptr) + { + last.etl_next->etl_previous = &last; + } + + if (lhs != nullptr) + { + lhs->etl_next = &first; + } + } + // Reference template typename etl::enable_if >::value, void>::type unlink(TLink& node) { - if (node.etl_next != nullptr) - { - node.etl_next->etl_previous = node.etl_previous; - } + node.unlink(); + } - if (node.etl_previous != nullptr) + // Reference Reference + template + typename etl::enable_if >::value, void>::type + unlink(TLink& first, TLink& last) + { + if (&first == &last) { - node.etl_previous->etl_next = node.etl_next; + first.unlink(); + } + else + { + if (last.etl_next != nullptr) + { + last.etl_next->etl_previous = first.etl_previous; + } + + if (first.etl_previous != nullptr) + { + first.etl_previous->etl_next = last.etl_next; + } + + if ((TLink::OPTION == etl::link_option::AUTO) || + (TLink::OPTION == etl::link_option::CHECKED)) + { + first.etl_previous = nullptr; + last.etl_next = nullptr; + } } } - // Pointer - template - typename etl::enable_if >::value, void>::type - unlink(TLink* node) + namespace __private_intrusive_links__ { - if (node != nullptr) + //*************************************************************************** + /// A tree link base. + //*************************************************************************** + template + struct tree_link_base { - if (node->etl_next != nullptr) + enum { - node->etl_next->etl_previous = node->etl_previous; + ID = ID_, + OPTION = OPTION_ + }; + + void clear() + { + etl_parent = nullptr; + etl_left = nullptr; + etl_right = nullptr; } - if (node->etl_previous != nullptr) + bool is_linked() const { - node->etl_previous->etl_next = node->etl_next; + return (etl_parent != nullptr) || (etl_left != nullptr) || (etl_right != nullptr); } - } + + TLink* etl_parent; + TLink* etl_left; + TLink* etl_right; + }; } //*************************************************************************** /// A tree link. //*************************************************************************** - template + template struct tree_link + : public __private_intrusive_links__::tree_link_base, ID_, OPTION_> { - enum - { - ID = ID_ - }; + }; - void clear() + //****************************************************************** + // There is no valid specialisation for auto link + //****************************************************************** + template + struct tree_link + : public __private_intrusive_links__::tree_link_base, ID_, etl::link_option::AUTO> + { + tree_link() { - etl_parent = nullptr; - etl_left = nullptr; - etl_right = nullptr; + clear(); + } + }; + + //****************************************************************** + // Specialisation for checked unlink option. + // An error will be generated if the links are valid when the object + // is destroyed. + //****************************************************************** + template + struct tree_link + : public __private_intrusive_links__::tree_link_base, ID_, etl::link_option::CHECKED> + { + tree_link() + { + clear(); } - tree_link* etl_parent; - tree_link* etl_left; - tree_link* etl_right; + ~tree_link() + { + assert(etl_parent != nullptr); + assert(etl_left != nullptr); + assert(etl_right != nullptr); + } }; // Reference, Reference template - typename etl::enable_if >::value, void>::type + typename etl::enable_if >::value, void>::type link_left(TLink& parent, TLink& leaf) { parent.etl_left = &leaf; @@ -406,7 +732,7 @@ namespace etl } template - typename etl::enable_if >::value, void>::type + typename etl::enable_if >::value, void>::type link_right(TLink& parent, TLink& leaf) { parent.etl_right = &leaf; @@ -415,7 +741,7 @@ namespace etl // Pointer, Pointer template - typename etl::enable_if >::value, void>::type + typename etl::enable_if >::value, void>::type link_left(TLink* parent, TLink* leaf) { if (parent != nullptr) @@ -430,7 +756,7 @@ namespace etl } template - typename etl::enable_if >::value, void>::type + typename etl::enable_if >::value, void>::type link_right(TLink* parent, TLink* leaf) { if (parent != nullptr) @@ -446,7 +772,7 @@ namespace etl // Reference, Pointer template - typename etl::enable_if >::value, void>::type + typename etl::enable_if >::value, void>::type link_left(TLink& parent, TLink* leaf) { parent.etl_left = leaf; @@ -458,7 +784,7 @@ namespace etl } template - typename etl::enable_if >::value, void>::type + typename etl::enable_if >::value, void>::type link_right(TLink& parent, TLink* leaf) { parent.etl_right = leaf; @@ -471,7 +797,7 @@ namespace etl // Pointer, Reference template - typename etl::enable_if >::value, void>::type + typename etl::enable_if >::value, void>::type link_left(TLink* parent, TLink& leaf) { if (parent != nullptr) @@ -483,7 +809,7 @@ namespace etl } template - typename etl::enable_if >::value, void>::type + typename etl::enable_if >::value, void>::type link_right(TLink* parent, TLink& leaf) { if (parent != nullptr) @@ -496,4 +822,4 @@ namespace etl } #undef ETL_FILE -#endif +#endif \ No newline at end of file diff --git a/test/test_intrusive_forward_list.cpp b/test/test_intrusive_forward_list.cpp index 847ac82b..df73665d 100644 --- a/test/test_intrusive_forward_list.cpp +++ b/test/test_intrusive_forward_list.cpp @@ -76,6 +76,11 @@ namespace return data < other.data; } + bool operator >(const ItemNDCNode& other) const + { + return other.data < data; + } + ItemNDC data; }; @@ -130,25 +135,6 @@ namespace typedef etl::intrusive_forward_list DataNDC1; typedef std::vector InitialDataNDC; - - template - bool Equal(TIterator1 begin1, - TIterator1 end1, - TIterator2 begin2) - { - while (begin1 != end1) - { - if (*begin1 != *begin2) - { - return false; - } - - ++begin1; - ++begin2; - } - - return true; - } } namespace @@ -157,9 +143,15 @@ namespace { InitialDataNDC unsorted_data; InitialDataNDC sorted_data; + InitialDataNDC sorted_data2; InitialDataNDC non_unique_data; InitialDataNDC unique_data; InitialDataNDC small_data; + InitialDataNDC merge_data0; + InitialDataNDC merge_data1; + InitialDataNDC merge_data2; + InitialDataNDC merge_data3; + InitialDataNDC merge_data4; bool are_equal; @@ -170,9 +162,16 @@ namespace { unsorted_data = { ItemNDCNode("1"), ItemNDCNode("0"), ItemNDCNode("3"), ItemNDCNode("2"), ItemNDCNode("5"), ItemNDCNode("4"), ItemNDCNode("7"), ItemNDCNode("6"), ItemNDCNode("9"), ItemNDCNode("8") }; sorted_data = { ItemNDCNode("0"), ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("4"), ItemNDCNode("5"), ItemNDCNode("6"), ItemNDCNode("7"), ItemNDCNode("8"), ItemNDCNode("9") }; + sorted_data2 = { ItemNDCNode("0"), ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("4"), ItemNDCNode("5"), ItemNDCNode("6"), ItemNDCNode("7"), ItemNDCNode("8"), ItemNDCNode("9") }; non_unique_data = { ItemNDCNode("0"), ItemNDCNode("0"), ItemNDCNode("1"), ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("4"), ItemNDCNode("5") }; unique_data = { ItemNDCNode("0"), ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("4"), ItemNDCNode("5") }; small_data = { ItemNDCNode("0"), ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("4"), ItemNDCNode("5") }; + + merge_data0 = { ItemNDCNode("1"), ItemNDCNode("1"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("5"), ItemNDCNode("7"), ItemNDCNode("8") }; + merge_data1 = { ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("6"), ItemNDCNode("9"), ItemNDCNode("9") }; + merge_data2 = { ItemNDCNode("0"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("6"), ItemNDCNode("7"), ItemNDCNode("7") }; + merge_data3 = { ItemNDCNode("0"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("6"), ItemNDCNode("7") }; + merge_data4 = { ItemNDCNode("0"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("6"), ItemNDCNode("7"), ItemNDCNode("8"), ItemNDCNode("9") }; } }; @@ -195,6 +194,20 @@ namespace CHECK_EQUAL(sorted_data.size(), data0.size()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_empty_begin_end) + { + DataNDC0 data0; + + CHECK(data0.begin() == data0.end()); + + DataNDC0::const_iterator begin = data0.begin(); + DataNDC0::const_iterator end = data0.end(); + CHECK(begin == end); + + CHECK(data0.cbegin() == data0.cend()); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_iterator) { @@ -317,7 +330,7 @@ namespace ItemNDCNode INSERT_VALUE1 = ItemNDCNode("1"); ItemNDCNode INSERT_VALUE2 = ItemNDCNode("2"); - std::vector compare_data(sorted_data.begin(), sorted_data.end()); + std::forward_list compare_data(sorted_data.begin(), sorted_data.end()); DataNDC0 data0(sorted_data.begin(), sorted_data.end()); DataNDC1 data1(sorted_data.begin(), sorted_data.end()); @@ -326,16 +339,16 @@ namespace DataNDC0::iterator i_data = data0.begin(); std::advance(i_data, offset); - std::vector::iterator i_compare_data = compare_data.begin(); - std::advance(i_compare_data, offset + 1); + std::forward_list::iterator i_compare_data = compare_data.begin(); + std::advance(i_compare_data, offset); data0.insert_after(i_data, INSERT_VALUE1); - compare_data.insert(i_compare_data, INSERT_VALUE1); + compare_data.insert_after(i_compare_data, INSERT_VALUE1); are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); CHECK(are_equal); - CHECK_EQUAL(compare_data.size(), data0.size()); - CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), data0.size()); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), std::distance(data0.begin(), data0.end())); are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); CHECK(are_equal); @@ -348,19 +361,19 @@ namespace std::advance(i_data, offset); i_compare_data = compare_data.begin(); - std::advance(i_compare_data, offset + 1); + std::advance(i_compare_data, offset); - std::vector temp(data0.begin(), data0.end()); + std::forward_list temp(data0.begin(), data0.end()); data0.insert_after(i_data, INSERT_VALUE2); - compare_data.insert(i_compare_data, INSERT_VALUE2); + compare_data.insert_after(i_compare_data, INSERT_VALUE2); temp.assign(data0.begin(), data0.end()); are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); CHECK(are_equal); - CHECK_EQUAL(compare_data.size(), data0.size()); - CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), data0.size()); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), std::distance(data0.begin(), data0.end())); are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); CHECK(are_equal); @@ -373,8 +386,8 @@ namespace { std::vector test1 = { ItemNDCNode("0"), ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("4") }; std::vector test2 = { ItemNDCNode("5"), ItemNDCNode("6"), ItemNDCNode("7"), ItemNDCNode("8"), ItemNDCNode("9") }; - std::vector compare(test2); - compare.insert(compare.end(), test1.begin(), test1.end()); + std::forward_list compare(test1.begin(), test1.end()); + compare.insert_after(compare.before_begin(), test2.begin(), test2.end()); DataNDC0 data0(test1.begin(), test1.end()); DataNDC1 data1(test1.begin(), test1.end()); @@ -392,16 +405,16 @@ namespace compare.assign(test1.begin(), test1.end()); data0.assign(test1.begin(), test1.end()); - std::vector::iterator icd = compare.begin(); + std::forward_list::iterator icd = compare.begin(); DataNDC0::iterator id = data0.begin(); - std::advance(icd, 4); + std::advance(icd, 3); std::advance(id, 3); - compare.insert(icd, test2.begin(), test2.end()); + compare.insert_after(icd, test2.begin(), test2.end()); data0.insert_after(id, test2.begin(), test2.end()); - std::vector out(data0.begin(), data0.end()); + std::forward_list out(data0.begin(), data0.end()); are_equal = std::equal(data0.begin(), data0.end(), compare.begin()); CHECK(are_equal); @@ -500,17 +513,17 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_erase_after_single) { - std::vector compare_data(sorted_data.begin(), sorted_data.end()); + std::forward_list compare_data(sorted_data.begin(), sorted_data.end()); DataNDC0 data0(sorted_data.begin(), sorted_data.end()); DataNDC1 data1(sorted_data.begin(), sorted_data.end()); DataNDC0::iterator i_data = data0.begin(); std::advance(i_data, 2); - std::vector::iterator i_compare_data = compare_data.begin(); - std::advance(i_compare_data, 3); + std::forward_list::iterator i_compare_data = compare_data.begin(); + std::advance(i_compare_data, 2); - i_compare_data = compare_data.erase(i_compare_data); + i_compare_data = compare_data.erase_after(i_compare_data); i_data = data0.erase_after(i_data); are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); @@ -518,14 +531,14 @@ namespace CHECK(are_equal); CHECK(*i_compare_data == *i_data); - i_compare_data = compare_data.erase(compare_data.begin() + 1); + i_compare_data = compare_data.erase_after(compare_data.begin()); i_data = data0.erase_after(data0.begin()); are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); CHECK(are_equal); - CHECK_EQUAL(compare_data.size(), data0.size()); - CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), data0.size()); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), std::distance(data0.begin(), data0.end())); are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); CHECK(are_equal); @@ -536,8 +549,8 @@ namespace CHECK(are_equal); // Move to the last value and erase. - i_compare_data = compare_data.begin() + 1; - i_compare_data = compare_data.erase(i_compare_data); + i_compare_data = compare_data.begin(); + i_compare_data = compare_data.erase_after(i_compare_data); i_data = data0.begin(); i_data = data0.erase_after(i_data); @@ -545,8 +558,8 @@ namespace are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); CHECK(are_equal); - CHECK_EQUAL(compare_data.size(), data0.size()); - CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), data0.size()); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), std::distance(data0.begin(), data0.end())); are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); CHECK(are_equal); @@ -560,23 +573,23 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_erase_after_range) { - std::vector compare_data(sorted_data.begin(), sorted_data.end()); + std::forward_list compare_data(sorted_data.begin(), sorted_data.end()); DataNDC0 data0(sorted_data.begin(), sorted_data.end()); - DataNDC1 data1(sorted_data.begin(), sorted_data.end()); + DataNDC1 data1(sorted_data2.begin(), sorted_data2.end()); DataNDC0::iterator i_data_1 = data0.begin(); std::advance(i_data_1, 2); DataNDC0::iterator i_data_2 = data0.begin(); - std::advance(i_data_2, 4); + std::advance(i_data_2, 5); - std::vector::iterator i_compare_data_1 = compare_data.begin(); - std::advance(i_compare_data_1, 3); + std::forward_list::iterator i_compare_data_1 = compare_data.begin(); + std::advance(i_compare_data_1, 2); - std::vector::iterator i_compare_data_2 = compare_data.begin(); - std::advance(i_compare_data_2, 4); + std::forward_list::iterator i_compare_data_2 = compare_data.begin(); + std::advance(i_compare_data_2, 5); - std::vector::iterator i_compare_result = compare_data.erase(i_compare_data_1, i_compare_data_2); + std::forward_list::iterator i_compare_result = compare_data.erase_after(i_compare_data_1, i_compare_data_2); DataNDC0::iterator i_result = data0.erase_after(i_data_1, i_data_2); @@ -584,8 +597,8 @@ namespace are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); CHECK(are_equal); - CHECK_EQUAL(compare_data.size(), data0.size()); - CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), data0.size()); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), std::distance(data0.begin(), data0.end())); are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); CHECK(are_equal); @@ -596,17 +609,17 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_erase_after_range_end) { - std::vector compare_data(sorted_data.begin(), sorted_data.end()); + std::forward_list compare_data(sorted_data.begin(), sorted_data.end()); DataNDC0 data0(sorted_data.begin(), sorted_data.end()); DataNDC1 data1(sorted_data.begin(), sorted_data.end()); DataNDC0::iterator i_data = data0.begin(); std::advance(i_data, 4); - std::vector::iterator i_compare_data = compare_data.begin(); - std::advance(i_compare_data, 5); + std::forward_list::iterator i_compare_data = compare_data.begin(); + std::advance(i_compare_data, 4); - std::vector::iterator i_compare_result = compare_data.erase(i_compare_data, compare_data.end()); + std::forward_list::iterator i_compare_result = compare_data.erase_after(i_compare_data, compare_data.end()); DataNDC0::iterator i_result = data0.erase_after(i_data, data0.end()); @@ -614,8 +627,8 @@ namespace are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); CHECK(are_equal); - CHECK_EQUAL(compare_data.size(), data0.size()); - CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), data0.size()); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), std::distance(data0.begin(), data0.end())); are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); CHECK(are_equal); @@ -665,19 +678,18 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_remove) { - std::vector compare_data(sorted_data.begin(), sorted_data.end()); + std::forward_list compare_data(sorted_data.begin(), sorted_data.end()); DataNDC0 data0(sorted_data.begin(), sorted_data.end()); DataNDC1 data1(sorted_data.begin(), sorted_data.end()); - std::vector::iterator i_item = std::find(compare_data.begin(), compare_data.end(), ItemNDCNode("7")); - compare_data.erase(i_item); + compare_data.remove(ItemNDCNode("7")); data0.remove(ItemNDCNode("7")); are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); CHECK(are_equal); - CHECK_EQUAL(compare_data.size(), data0.size()); - CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), data0.size()); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), std::distance(data0.begin(), data0.end())); are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); CHECK(are_equal); @@ -688,19 +700,18 @@ namespace //************************************************************************* TEST_FIXTURE(SetupFixture, test_remove_if) { - std::vector compare_data(sorted_data.begin(), sorted_data.end()); + std::forward_list compare_data(sorted_data.begin(), sorted_data.end()); DataNDC0 data0(sorted_data.begin(), sorted_data.end()); DataNDC1 data1(sorted_data.begin(), sorted_data.end()); - std::vector::iterator i_item = std::find(compare_data.begin(), compare_data.end(), ItemNDCNode("7")); - compare_data.erase(i_item); + compare_data.remove_if(std::bind2nd(std::equal_to(), ItemNDCNode("7"))); data0.remove_if(std::bind2nd(std::equal_to(), ItemNDCNode("7"))); are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); CHECK(are_equal); - CHECK_EQUAL(compare_data.size(), data0.size()); - CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), data0.size()); + CHECK_EQUAL(std::distance(compare_data.begin(), compare_data.end()), std::distance(data0.begin(), data0.end())); are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); CHECK(are_equal); @@ -752,5 +763,242 @@ namespace are_equal = std::equal(data1.begin(), data1.end(), unsorted_data.begin()); CHECK(are_equal); } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_list) + { + bool are_equal; + + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + DataNDC0 data1(sorted_data2.begin(), sorted_data2.end()); + + DataNDC0::iterator idata_destination = data0.begin(); + std::advance(idata_destination, 3); + + std::forward_list compare0(data0.begin(), data0.end()); + std::forward_list compare1(data1.begin(), data1.end()); + + std::forward_list::iterator icompare_destination = compare0.begin(); + std::advance(icompare_destination, 3); + + data0.splice_after(idata_destination, data1); + compare0.splice_after(icompare_destination, compare1); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(std::distance(compare0.begin(), compare0.end()), data0.size()); + CHECK_EQUAL(std::distance(compare1.begin(), compare1.end()), data1.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_list_self) + { + bool are_equal; + + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + + DataNDC0::iterator idata_destination = data0.begin(); + std::advance(idata_destination, 3); + + std::forward_list compare0(data0.begin(), data0.end()); + + std::forward_list::iterator icompare_destination = compare0.begin(); + std::advance(icompare_destination, 3); + + data0.splice_after(idata_destination, data0); + compare0.splice_after(icompare_destination, compare0); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(std::distance(compare0.begin(), compare0.end()), data0.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_range) + { + bool are_equal; + + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + DataNDC0 data1(sorted_data2.begin(), sorted_data2.end()); + + DataNDC0::iterator idata_destination = data0.begin(); + std::advance(idata_destination, 2); + + DataNDC0::iterator idata_begin = data1.begin(); + std::advance(idata_begin, 4); + + DataNDC0::iterator idata_end = data1.begin(); + std::advance(idata_end, 7); + + std::forward_list compare0(data0.begin(), data0.end()); + std::forward_list compare1(data1.begin(), data1.end()); + + std::forward_list::iterator icompare_destination = compare0.begin(); + std::advance(icompare_destination, 2); + + std::forward_list::iterator icompare_begin = compare1.begin(); + std::advance(icompare_begin, 4); + + std::forward_list::iterator icompare_end = compare1.begin(); + std::advance(icompare_end, 7); + + data0.splice_after(idata_destination, data1, idata_begin, idata_end); + compare0.splice_after(icompare_destination, compare1, icompare_begin, icompare_end); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(std::distance(compare0.begin(), compare0.end()), data0.size()); + CHECK_EQUAL(std::distance(compare1.begin(), compare1.end()), data1.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_range_self) + { + bool are_equal; + + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + + DataNDC0::iterator idata_destination = data0.begin(); + std::advance(idata_destination, 2); + + DataNDC0::iterator idata_begin = data0.begin(); + std::advance(idata_begin, 4); + + DataNDC0::iterator idata_end = data0.begin(); + std::advance(idata_end, 7); + + std::forward_list compare0(data0.begin(), data0.end()); + + std::forward_list::iterator icompare_destination = compare0.begin(); + std::advance(icompare_destination, 2); + + std::forward_list::iterator icompare_begin = compare0.begin(); + std::advance(icompare_begin, 4); + + std::forward_list::iterator icompare_end = compare0.begin(); + std::advance(icompare_end, 7); + + data0.splice_after(idata_destination, data0, idata_begin, idata_end); + compare0.splice_after(icompare_destination, compare0, icompare_begin, icompare_end); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(std::distance(compare0.begin(), compare0.end()), data0.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_1) + { + bool are_equal; + + DataNDC0 data0(merge_data0.begin(), merge_data0.end()); + DataNDC0 data1(merge_data1.begin(), merge_data1.end()); + + std::forward_list compare0(merge_data0.begin(), merge_data0.end()); + std::forward_list compare1(merge_data1.begin(), merge_data1.end()); + + data0.merge(data1); + compare0.merge(compare1); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(std::distance(compare0.begin(), compare0.end()), data0.size()); + CHECK_EQUAL(std::distance(compare1.begin(), compare1.end()), data1.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_2) + { + bool are_equal; + + DataNDC0 data0(merge_data0.begin(), merge_data0.end()); + DataNDC0 data2(merge_data2.begin(), merge_data2.end()); + + std::forward_list compare0(merge_data0.begin(), merge_data0.end()); + std::forward_list compare2(merge_data2.begin(), merge_data2.end()); + + data0.merge(data2); + compare0.merge(compare2); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(std::distance(compare0.begin(), compare0.end()), data0.size()); + CHECK_EQUAL(std::distance(compare2.begin(), compare2.end()), data2.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_3) + { + bool are_equal; + + DataNDC0 data0(merge_data0.begin(), merge_data0.end()); + DataNDC0 data3(merge_data3.begin(), merge_data3.end()); + + std::forward_list compare0(merge_data0.begin(), merge_data0.end()); + std::forward_list compare3(merge_data3.begin(), merge_data3.end()); + + data0.merge(data3); + compare0.merge(compare3); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(std::distance(compare0.begin(), compare0.end()), data0.size()); + CHECK_EQUAL(std::distance(compare3.begin(), compare3.end()), data3.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_4) + { + bool are_equal; + + DataNDC0 data0(merge_data0.begin(), merge_data0.end()); + DataNDC0 data4(merge_data4.begin(), merge_data4.end()); + + std::forward_list compare0(merge_data0.begin(), merge_data0.end()); + std::forward_list compare4(merge_data4.begin(), merge_data4.end()); + + data0.merge(data4); + compare0.merge(compare4); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(std::distance(compare0.begin(), compare0.end()), data0.size()); + CHECK_EQUAL(std::distance(compare4.begin(), compare4.end()), data4.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_1_reverse_order) + { + bool are_equal; + + DataNDC0 data0(merge_data0.begin(), merge_data0.end()); + DataNDC0 data1(merge_data1.begin(), merge_data1.end()); + + data0.reverse(); + data1.reverse(); + + std::forward_list compare0(merge_data0.begin(), merge_data0.end()); + std::forward_list compare1(merge_data1.begin(), merge_data1.end()); + + compare0.reverse(); + compare1.reverse(); + + data0.merge(data1, std::greater()); + compare0.merge(compare1, std::greater()); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(std::distance(compare0.begin(), compare0.end()), data0.size()); + CHECK_EQUAL(std::distance(compare1.begin(), compare1.end()), data1.size()); + } }; } diff --git a/test/test_intrusive_links.cpp b/test/test_intrusive_links.cpp index 2c82bce0..06ba8868 100644 --- a/test/test_intrusive_links.cpp +++ b/test/test_intrusive_links.cpp @@ -36,7 +36,7 @@ SOFTWARE. namespace { //******************************************************* - //Forward + // Forward //******************************************************* typedef etl::forward_link<0> FirstFLink; typedef etl::forward_link<1> SecondFLink; @@ -54,7 +54,8 @@ namespace //******************************************************* // Bidirectional //******************************************************* - typedef etl::bidirectional_link<0, etl::link_option::AUTO_UNLINK> FirstBLinkAuto; + typedef etl::bidirectional_link<0, etl::link_option::AUTO> FirstBLinkAuto; + typedef etl::bidirectional_link<0, etl::link_option::CHECKED> FirstBLinkChecked; typedef etl::bidirectional_link<0> FirstBLink; typedef etl::bidirectional_link<1> SecondBLink; @@ -78,11 +79,22 @@ namespace int value; }; + struct BDataChecked : public FirstBLinkChecked + { + BDataChecked(int value) + : value(value) + { + } + + int value; + }; + //******************************************************* // Tree //******************************************************* typedef etl::tree_link<0> FirstTLink; typedef etl::tree_link<1> SecondTLink; + typedef etl::tree_link<2> ThirdTLink; struct TData : public FirstTLink, public SecondTLink { @@ -94,6 +106,19 @@ namespace int value; }; + //******************************************************* + // Mixed + //******************************************************* + struct MData : public FirstFLink, public SecondBLink, public ThirdTLink + { + MData(int value) + : value(value) + { + } + + int value; + }; + SUITE(test_forward_list) { //************************************************************************* @@ -137,8 +162,138 @@ namespace CHECK(data2.SecondFLink::etl_next == &data1); CHECK(data1.SecondFLink::etl_next == &data0); CHECK(data0.SecondFLink::etl_next == nullptr); + + FData* pdata; + + pdata = static_cast(data0.FirstFLink::etl_next); + CHECK_EQUAL(1, pdata->value); + pdata = static_cast(pdata->FirstFLink::etl_next); + CHECK_EQUAL(2, pdata->value); + pdata = static_cast(pdata->FirstFLink::etl_next); + CHECK_EQUAL(3, pdata->value); + + pdata = static_cast(data3.SecondFLink::etl_next); + CHECK_EQUAL(2, pdata->value); + pdata = static_cast(pdata->SecondFLink::etl_next); + CHECK_EQUAL(1, pdata->value); + pdata = static_cast(pdata->SecondFLink::etl_next); + CHECK_EQUAL(0, pdata->value); } + //************************************************************************* + TEST(test_link_splice_forward_link) + { + FData data0(0); + FData data1(1); + FData data2(2); + FData data3(3); + + data0.FirstFLink::clear(); + etl::link_splice(data0, data1); + CHECK(data0.FirstFLink::etl_next == &data1); + CHECK(data1.FirstFLink::etl_next == nullptr); + + data0.FirstFLink::clear(); + etl::link_splice(data0, &data1); + CHECK(data0.FirstFLink::etl_next == &data1); + CHECK(data1.FirstFLink::etl_next == nullptr); + + data0.FirstFLink::clear(); + etl::link_splice(&data0, data1); + CHECK(data0.FirstFLink::etl_next == &data1); + CHECK(data1.FirstFLink::etl_next == nullptr); + + data0.FirstFLink::clear(); + etl::link_splice(&data0, &data1); + CHECK(data0.FirstFLink::etl_next == &data1); + CHECK(data1.FirstFLink::etl_next == nullptr); + + data0.FirstFLink::clear(); + etl::link_splice(data0, data3); + etl::link_splice(data0, data1); + etl::link_splice(data1, data2); + + CHECK(data0.FirstFLink::etl_next == &data1); + CHECK(data1.FirstFLink::etl_next == &data2); + CHECK(data2.FirstFLink::etl_next == &data3); + CHECK(data3.FirstFLink::etl_next == nullptr); + } + + //************************************************************************* + TEST(test_link_splice_forward_link_range) + { + FData data0(0); + FData data1(1); + FData data2(2); + FData data3(3); + FData data4(4); + FData data5(5); + FData data6(6); + FData data7(7); + + // First range. + data0.FirstFLink::clear(); + etl::link_splice(data0, data1); + etl::link_splice(data1, data6); + etl::link_splice(data6, data7); + + // Second range. + data2.FirstFLink::clear(); + etl::link_splice(data2, data3); + etl::link_splice(data3, data4); + etl::link_splice(data4, data5); + + etl::link_splice(data1, data2, data5); + + CHECK(data0.FirstFLink::etl_next == &data1); + CHECK(data1.FirstFLink::etl_next == &data2); + CHECK(data2.FirstFLink::etl_next == &data3); + CHECK(data3.FirstFLink::etl_next == &data4); + CHECK(data4.FirstFLink::etl_next == &data5); + CHECK(data5.FirstFLink::etl_next == &data6); + CHECK(data6.FirstFLink::etl_next == &data7); + CHECK(data7.FirstFLink::etl_next == nullptr); + + // Do it again with a pointer. + // First range. + data0.FirstFLink::clear(); + etl::link_splice(data0, data1); + etl::link_splice(data1, data6); + etl::link_splice(data6, data7); + + // Second range. + data2.FirstFLink::clear(); + etl::link_splice(data2, data3); + etl::link_splice(data3, data4); + etl::link_splice(data4, data5); + + etl::link_splice(&data1, data2, data5); + + CHECK(data0.FirstFLink::etl_next == &data1); + CHECK(data1.FirstFLink::etl_next == &data2); + CHECK(data2.FirstFLink::etl_next == &data3); + CHECK(data3.FirstFLink::etl_next == &data4); + CHECK(data4.FirstFLink::etl_next == &data5); + CHECK(data5.FirstFLink::etl_next == &data6); + CHECK(data6.FirstFLink::etl_next == &data7); + CHECK(data7.FirstFLink::etl_next == nullptr); + + // Do it again with a nullptr pointer. + // Second range. + data2.FirstFLink::clear(); + etl::link_splice(data2, data3); + etl::link_splice(data3, data4); + etl::link_splice(data4, data5); + + etl::link_splice(nullptr, data2, data5); + + CHECK(data2.FirstFLink::etl_next == &data3); + CHECK(data3.FirstFLink::etl_next == &data4); + CHECK(data4.FirstFLink::etl_next == &data5); + CHECK(data5.FirstFLink::etl_next == nullptr); + } + + //************************************************************************* TEST(test_unlink_after_forward_link) { @@ -194,6 +349,39 @@ namespace CHECK(data0.SecondFLink::etl_next == nullptr); } + //************************************************************************* + TEST(test_unlink_after_range_forward_link) + { + FData data0(0); + FData data1(1); + FData data2(2); + FData data3(3); + + etl::link(data0, data1); + etl::link(data1, data2); + etl::link(data2, data3); + etl::link(data3, nullptr); + + etl::link(data3, data2); + etl::link(data2, data1); + etl::link(data1, data0); + etl::link(data0, nullptr); + + etl::unlink_after(data0, data2); + data1.FirstFLink::clear(); + data2.FirstFLink::clear(); + + CHECK(data0.FirstFLink::etl_next == &data3); + CHECK(data1.FirstFLink::etl_next == nullptr); + CHECK(data2.FirstFLink::etl_next == nullptr); + CHECK(data3.FirstFLink::etl_next == nullptr); + + CHECK(data3.SecondFLink::etl_next == &data2); + CHECK(data2.SecondFLink::etl_next == &data1); + CHECK(data1.SecondFLink::etl_next == &data0); + CHECK(data0.SecondFLink::etl_next == nullptr); + } + //************************************************************************* TEST(test_self_link_forward_link) { @@ -245,11 +433,12 @@ namespace etl::link(data3, nullptr); CHECK(data0->FirstBLinkAuto::etl_previous == nullptr); - CHECK(data1->FirstBLinkAuto::etl_next == data2); + CHECK(data1->FirstBLinkAuto::etl_previous == data0); + CHECK(data1->FirstBLinkAuto::etl_next == data2); CHECK(data2->FirstBLinkAuto::etl_previous == data1); - CHECK(data2->FirstBLinkAuto::etl_next == data3); + CHECK(data2->FirstBLinkAuto::etl_next == data3); CHECK(data3->FirstBLinkAuto::etl_previous == data2); - CHECK(data3->FirstBLinkAuto::etl_next == nullptr); + CHECK(data3->FirstBLinkAuto::etl_next == nullptr); etl::link(nullptr, data3); etl::link(data3, data2); @@ -266,6 +455,37 @@ namespace CHECK(data0->SecondBLink::etl_previous == data1); CHECK(data0->SecondBLink::etl_next == nullptr); + BDataAuto* pdataauto; + BData* pdata; + + pdataauto = static_cast(data0->FirstBLinkAuto::etl_next); + CHECK_EQUAL(1, pdataauto->value); + pdataauto = static_cast(pdataauto->FirstBLinkAuto::etl_next); + CHECK_EQUAL(2, pdataauto->value); + pdataauto = static_cast(pdataauto->FirstBLinkAuto::etl_next); + CHECK_EQUAL(3, pdataauto->value); + + pdataauto = static_cast(data3->FirstBLinkAuto::etl_previous); + CHECK_EQUAL(2, pdataauto->value); + pdataauto = static_cast(pdataauto->FirstBLinkAuto::etl_previous); + CHECK_EQUAL(1, pdataauto->value); + pdataauto = static_cast(pdataauto->FirstBLinkAuto::etl_previous); + CHECK_EQUAL(0, pdataauto->value); + + pdata = static_cast(data3->SecondBLink::etl_next); + CHECK_EQUAL(2, pdata->value); + pdata = static_cast(pdata->SecondBLink::etl_next); + CHECK_EQUAL(1, pdata->value); + pdata = static_cast(pdata->SecondBLink::etl_next); + CHECK_EQUAL(0, pdata->value); + + pdata = static_cast(data0->SecondBLink::etl_previous); + CHECK_EQUAL(1, pdata->value); + pdata = static_cast(pdata->SecondBLink::etl_previous); + CHECK_EQUAL(2, pdata->value); + pdata = static_cast(pdata->SecondBLink::etl_previous); + CHECK_EQUAL(3, pdata->value); + delete data1; CHECK(data0->FirstBLinkAuto::etl_next == data2); CHECK(data2->FirstBLinkAuto::etl_previous == data0); @@ -297,6 +517,181 @@ namespace delete data2; } + //************************************************************************* + TEST(test_link_splice_bidirectional_link) + { + BData data0(0); + BData data1(1); + BData data2(2); + BData data3(3); + + data0.FirstBLink::clear(); + etl::link_splice(nullptr, data0); + + etl::link_splice(data0, data1); + CHECK(data0.FirstBLink::etl_next == &data1); + CHECK(data1.FirstBLink::etl_previous == &data0); + CHECK(data1.FirstBLink::etl_next == nullptr); + + data0.FirstBLink::clear(); + etl::link_splice(nullptr, data0); + + etl::link_splice(data0, &data1); + CHECK(data0.FirstBLink::etl_next == &data1); + CHECK(data1.FirstBLink::etl_previous == &data0); + CHECK(data1.FirstBLink::etl_next == nullptr); + + data0.FirstBLink::clear(); + etl::link_splice(nullptr, data0); + + etl::link_splice(&data0, data1); + CHECK(data0.FirstBLink::etl_next == &data1); + CHECK(data1.FirstBLink::etl_previous == &data0); + CHECK(data1.FirstBLink::etl_next == nullptr); + + data0.FirstBLink::clear(); + etl::link_splice(nullptr, data0); + + etl::link_splice(&data0, &data1); + CHECK(data0.FirstBLink::etl_next == &data1); + CHECK(data1.FirstBLink::etl_previous == &data0); + CHECK(data1.FirstBLink::etl_next == nullptr); + + data0.FirstBLink::clear(); + etl::link_splice(nullptr, data0); + etl::link_splice(data0, data3); + etl::link_splice(data0, data1); + etl::link_splice(data1, data2); + + CHECK(data0.FirstBLink::etl_previous == nullptr); + CHECK(data0.FirstBLink::etl_next == &data1); + CHECK(data1.FirstBLink::etl_previous == &data0); + CHECK(data1.FirstBLink::etl_next == &data2); + CHECK(data2.FirstBLink::etl_previous == &data1); + CHECK(data2.FirstBLink::etl_next == &data3); + CHECK(data3.FirstBLink::etl_previous == &data2); + CHECK(data3.FirstBLink::etl_next == nullptr); + } + + //************************************************************************* + TEST(test_link_splice_range_bidirectional_link) + { + BData data0(0); + BData data1(1); + BData data2(2); + BData data3(3); + BData data4(4); + BData data5(5); + BData data6(6); + BData data7(7); + + // Build the first range. + data0.FirstBLink::clear(); + etl::link_splice(nullptr, data0); + etl::link_splice(data0, data1); + etl::link_splice(data1, data6); + etl::link_splice(data6, data7); + + // Build the second range. + data2.FirstBLink::clear(); + etl::link_splice(nullptr, data2); + etl::link_splice(data2, data3); + etl::link_splice(data3, data4); + etl::link_splice(data4, data5); + + etl::link_splice(data1, data2, data5); + + CHECK(data0.FirstBLink::etl_previous == nullptr); + CHECK(data0.FirstBLink::etl_next == &data1); + CHECK(data1.FirstBLink::etl_previous == &data0); + CHECK(data1.FirstBLink::etl_next == &data2); + CHECK(data2.FirstBLink::etl_previous == &data1); + CHECK(data2.FirstBLink::etl_next == &data3); + CHECK(data3.FirstBLink::etl_previous == &data2); + CHECK(data3.FirstBLink::etl_next == &data4); + CHECK(data4.FirstBLink::etl_previous == &data3); + CHECK(data4.FirstBLink::etl_next == &data5); + CHECK(data5.FirstBLink::etl_previous == &data4); + CHECK(data5.FirstBLink::etl_next == &data6); + CHECK(data6.FirstBLink::etl_previous == &data5); + CHECK(data6.FirstBLink::etl_next == &data7); + CHECK(data7.FirstBLink::etl_previous == &data6); + CHECK(data7.FirstBLink::etl_next == nullptr); + + // Do it again with a pointer parameter. + // Build the first range. + data0.FirstBLink::clear(); + etl::link_splice(nullptr, data0); + etl::link_splice(data0, data1); + etl::link_splice(data1, data6); + etl::link_splice(data6, data7); + + // Build the second range. + data2.FirstBLink::clear(); + etl::link_splice(nullptr, data2); + etl::link_splice(data2, data3); + etl::link_splice(data3, data4); + etl::link_splice(data4, data5); + + etl::link_splice(&data1, data2, data5); + + CHECK(data0.FirstBLink::etl_previous == nullptr); + CHECK(data0.FirstBLink::etl_next == &data1); + CHECK(data1.FirstBLink::etl_previous == &data0); + CHECK(data1.FirstBLink::etl_next == &data2); + CHECK(data2.FirstBLink::etl_previous == &data1); + CHECK(data2.FirstBLink::etl_next == &data3); + CHECK(data3.FirstBLink::etl_previous == &data2); + CHECK(data3.FirstBLink::etl_next == &data4); + CHECK(data4.FirstBLink::etl_previous == &data3); + CHECK(data4.FirstBLink::etl_next == &data5); + CHECK(data5.FirstBLink::etl_previous == &data4); + CHECK(data5.FirstBLink::etl_next == &data6); + CHECK(data6.FirstBLink::etl_previous == &data5); + CHECK(data6.FirstBLink::etl_next == &data7); + CHECK(data7.FirstBLink::etl_previous == &data6); + CHECK(data7.FirstBLink::etl_next == nullptr); + + // Do it again with a nullptr parameter. + // Build the range. + data2.FirstBLink::clear(); + etl::link_splice(nullptr, data2); + etl::link_splice(data2, data3); + etl::link_splice(data3, data4); + etl::link_splice(data4, data5); + + etl::link_splice(nullptr, data2, data5); + + CHECK(data2.FirstBLink::etl_previous == nullptr); + CHECK(data2.FirstBLink::etl_next == &data3); + CHECK(data3.FirstBLink::etl_previous == &data2); + CHECK(data3.FirstBLink::etl_next == &data4); + CHECK(data4.FirstBLink::etl_previous == &data3); + CHECK(data4.FirstBLink::etl_next == &data5); + CHECK(data5.FirstBLink::etl_previous == &data4); + CHECK(data5.FirstBLink::etl_next == nullptr); + } + + //************************************************************************* + TEST(test_link_bidirectional_link_checked) + { + // FirstBLinkAuto is auto-unlink + + BDataChecked* data0 = new BDataChecked(0); + BDataChecked* data1 = new BDataChecked(1); + BDataChecked* data2 = new BDataChecked(2); + BDataChecked* data3 = new BDataChecked(3); + + etl::link(nullptr, data0); + etl::link(data0, data1); + etl::link(data1, data2); + etl::link(data2, data3); + etl::link(data3, nullptr); + + data2->FirstBLinkChecked::clear(); + CHECK_NO_THROW(delete data2); + } + //************************************************************************* TEST(test_unlink_bidirectional_link) { @@ -402,6 +797,49 @@ namespace CHECK(data0.SecondBLink::etl_next == nullptr); } + //************************************************************************* + TEST(test_unlink_range_bidirectional_link) + { + BData data0(0); + BData data1(1); + BData data2(2); + BData data3(3); + + etl::link(nullptr, data0); + etl::link(data0, data1); + etl::link(data1, data2); + etl::link(data2, data3); + etl::link(data3, nullptr); + + etl::link(nullptr, data3); + etl::link(data3, data2); + etl::link(data2, data1); + etl::link(data1, data0); + etl::link(data0, nullptr); + + etl::unlink(data1, data2); + data1.FirstBLink::clear(); + data2.FirstBLink::clear(); + + CHECK(data0.FirstBLink::etl_previous == nullptr); + CHECK(data0.FirstBLink::etl_next == &data3); + CHECK(data1.FirstBLink::etl_previous == nullptr); + CHECK(data1.FirstBLink::etl_next == nullptr); + CHECK(data2.FirstBLink::etl_previous == nullptr); + CHECK(data2.FirstBLink::etl_next == nullptr); + CHECK(data3.FirstBLink::etl_previous == &data0); + CHECK(data3.FirstBLink::etl_next == nullptr); + + CHECK(data3.SecondBLink::etl_previous == nullptr); + CHECK(data3.SecondBLink::etl_next == &data2); + CHECK(data2.SecondBLink::etl_previous == &data3); + CHECK(data2.SecondBLink::etl_next == &data1); + CHECK(data1.SecondBLink::etl_previous == &data2); + CHECK(data1.SecondBLink::etl_next == &data0); + CHECK(data0.SecondBLink::etl_previous == &data1); + CHECK(data0.SecondBLink::etl_next == nullptr); + } + //************************************************************************* TEST(test_self_link_bidirectional_link) { @@ -539,5 +977,137 @@ namespace CHECK(data3.SecondTLink::etl_left == nullptr); CHECK(data3.SecondTLink::etl_right == nullptr); } + + //************************************************************************* + TEST(test_is_linked) + { + // Forward link + FData fdata(0); + + fdata.FirstFLink::clear(); + fdata.SecondFLink::clear(); + CHECK(!fdata.FirstFLink::is_linked()); + CHECK(!fdata.SecondFLink::is_linked()); + + etl::link(fdata, fdata); + CHECK(fdata.FirstFLink::is_linked()); + CHECK(!fdata.SecondFLink::is_linked()); + + etl::link(fdata, fdata); + CHECK(fdata.FirstFLink::is_linked()); + CHECK(fdata.SecondFLink::is_linked()); + + // Bidirectional link + BData bdata(0); + + bdata.FirstBLink::clear(); + bdata.SecondBLink::clear(); + CHECK(!bdata.FirstBLink::is_linked()); + CHECK(!bdata.SecondBLink::is_linked()); + + etl::link(bdata, bdata); + CHECK(bdata.FirstBLink::is_linked()); + CHECK(!bdata.SecondBLink::is_linked()); + + etl::link(bdata, bdata); + CHECK(bdata.FirstBLink::is_linked()); + CHECK(bdata.SecondBLink::is_linked()); + + // Tree link + TData tdata(0); + + tdata.FirstTLink::clear(); + tdata.SecondTLink::clear(); + CHECK(!tdata.FirstTLink::is_linked()); + CHECK(!tdata.SecondTLink::is_linked()); + + etl::link_left(tdata, tdata); + CHECK(tdata.FirstTLink::is_linked()); + CHECK(!tdata.SecondTLink::is_linked()); + + etl::link_right(tdata, tdata); + CHECK(tdata.FirstTLink::is_linked()); + CHECK(tdata.SecondTLink::is_linked()); + } + + //************************************************************************* + TEST(test_link_mixed_links) + { + MData data0(0); + MData data1(1); + MData data2(2); + MData data3(3); + MData data4(4); + MData data5(5); + MData data6(6); + + // Forward + data0.FirstFLink::clear(); + etl::link(data0, data1); + etl::link(data1, data2); + etl::link(data2, data3); + etl::link(data3, nullptr); + + // Bidirectional + etl::link(nullptr, data3); + etl::link(data3, data2); + etl::link(data2, data1); + etl::link(data1, data0); + etl::link(data0, nullptr); + + // Tree + data0.ThirdTLink::clear(); + etl::link_left(data0, data1); + etl::link_right(data0, data2); + etl::link_left(data1, data3); + etl::link_right(data1, data4); + etl::link_left(data3, nullptr); + etl::link_right(data3, nullptr); + etl::link_left(data4, nullptr); + etl::link_right(data4, nullptr); + etl::link_left(data2, data5); + etl::link_right(data2, data6); + etl::link_left(data5, nullptr); + etl::link_right(data5, nullptr); + etl::link_left(data6, nullptr); + etl::link_right(data6, nullptr); + + CHECK(data0.FirstFLink::etl_next == &data1); + CHECK(data1.FirstFLink::etl_next == &data2); + CHECK(data2.FirstFLink::etl_next == &data3); + CHECK(data3.FirstFLink::etl_next == nullptr); + + CHECK(data3.SecondBLink::etl_previous == nullptr); + CHECK(data3.SecondBLink::etl_next == &data2); + CHECK(data2.SecondBLink::etl_previous == &data3); + CHECK(data2.SecondBLink::etl_next == &data1); + CHECK(data1.SecondBLink::etl_previous == &data2); + CHECK(data1.SecondBLink::etl_next == &data0); + CHECK(data0.SecondBLink::etl_previous == &data1); + CHECK(data0.SecondBLink::etl_next == nullptr); + + CHECK(data0.ThirdTLink::etl_left == &data1); + CHECK(data0.ThirdTLink::etl_right == &data2); + CHECK(data1.ThirdTLink::etl_parent == &data0); + CHECK(data2.ThirdTLink::etl_parent == &data0); + + CHECK(data1.ThirdTLink::etl_left == &data3); + CHECK(data1.ThirdTLink::etl_right == &data4); + CHECK(data3.ThirdTLink::etl_parent == &data1); + CHECK(data3.ThirdTLink::etl_left == nullptr); + CHECK(data3.ThirdTLink::etl_right == nullptr); + CHECK(data4.ThirdTLink::etl_parent == &data1); + CHECK(data4.ThirdTLink::etl_left == nullptr); + CHECK(data4.ThirdTLink::etl_right == nullptr); + + CHECK(data2.ThirdTLink::etl_left == &data5); + CHECK(data2.ThirdTLink::etl_right == &data6); + CHECK(data5.ThirdTLink::etl_parent == &data2); + CHECK(data5.ThirdTLink::etl_left == nullptr); + CHECK(data5.ThirdTLink::etl_right == nullptr); + CHECK(data6.ThirdTLink::etl_parent == &data2); + CHECK(data6.ThirdTLink::etl_left == nullptr); + CHECK(data6.ThirdTLink::etl_right == nullptr); + } }; } diff --git a/test/test_intrusive_list.cpp b/test/test_intrusive_list.cpp new file mode 100644 index 00000000..048a16a3 --- /dev/null +++ b/test/test_intrusive_list.cpp @@ -0,0 +1,1098 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2015 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include +#include "ExtraCheckMacros.h" + +#include "data.h" + +#include "../intrusive_list.h" + +#include +#include +#include +#include +#include + +typedef TestDataDC ItemDC; +typedef TestDataNDC ItemNDC; + +namespace +{ + typedef etl::bidirectional_link<0> FirstLink; + typedef etl::bidirectional_link<1> SecondLink; + + //*************************************************************************** + class ItemDCNode : public FirstLink, public SecondLink + { + public: + + ItemDCNode(const std::string& text) + : data(text) + { + } + + ItemDC data; + }; + + //*************************************************************************** + class ItemNDCNode : public FirstLink, public SecondLink + { + public: + + ItemNDCNode(const std::string& text) + : data(text) + { + } + + bool operator <(const ItemNDCNode& other) const + { + return data < other.data; + } + + bool operator >(const ItemNDCNode& other) const + { + return other.data < data; + } + + ItemNDC data; + }; + + //*************************************************************************** + bool operator ==(const ItemDCNode& lhs, const ItemDCNode& rhs) + { + return lhs.data == rhs.data; + } + + bool operator ==(const ItemNDCNode& lhs, const ItemNDCNode& rhs) + { + return lhs.data == rhs.data; + } + + bool operator !=(const ItemDCNode& lhs, const ItemDCNode& rhs) + { + return lhs.data != rhs.data; + } + + bool operator !=(const ItemNDCNode& lhs, const ItemNDCNode& rhs) + { + return lhs.data != rhs.data; + } + + std::ostream& operator << (std::ostream& os, const ItemNDCNode& node) + { + os << node.data; + return os; + } + + //*************************************************************************** + struct CompareItemNDCNode + { + bool operator ()(const ItemNDCNode& lhs, const ItemNDCNode& rhs) const + { + return lhs.data < rhs.data; + } + }; + + struct EqualItemNDCNode + { + bool operator ()(const ItemNDCNode& lhs, const ItemNDCNode& rhs) const + { + return lhs.data == rhs.data; + } + }; + + //*************************************************************************** + typedef etl::intrusive_list DataDC0; + typedef etl::intrusive_list DataDC1; + typedef etl::intrusive_list DataNDC0; + typedef etl::intrusive_list DataNDC1; + + typedef std::vector InitialDataNDC; +} + +namespace +{ + SUITE(test_intrusive_list) + { + InitialDataNDC unsorted_data; + InitialDataNDC sorted_data; + InitialDataNDC sorted_data2; + InitialDataNDC non_unique_data; + InitialDataNDC unique_data; + InitialDataNDC small_data; + InitialDataNDC merge_data0; + InitialDataNDC merge_data1; + InitialDataNDC merge_data2; + InitialDataNDC merge_data3; + InitialDataNDC merge_data4; + + //************************************************************************* + struct SetupFixture + { + SetupFixture() + { + unsorted_data = { ItemNDCNode("1"), ItemNDCNode("0"), ItemNDCNode("3"), ItemNDCNode("2"), ItemNDCNode("5"), ItemNDCNode("4"), ItemNDCNode("7"), ItemNDCNode("6"), ItemNDCNode("9"), ItemNDCNode("8") }; + sorted_data = { ItemNDCNode("0"), ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("4"), ItemNDCNode("5"), ItemNDCNode("6"), ItemNDCNode("7"), ItemNDCNode("8"), ItemNDCNode("9") }; + sorted_data2 = { ItemNDCNode("0"), ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("4"), ItemNDCNode("5"), ItemNDCNode("6"), ItemNDCNode("7"), ItemNDCNode("8"), ItemNDCNode("9") }; + non_unique_data = { ItemNDCNode("0"), ItemNDCNode("0"), ItemNDCNode("1"), ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("4"), ItemNDCNode("5") }; + unique_data = { ItemNDCNode("0"), ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("4"), ItemNDCNode("5") }; + small_data = { ItemNDCNode("0"), ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("4"), ItemNDCNode("5") }; + + merge_data0 = { ItemNDCNode("1"), ItemNDCNode("1"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("5"), ItemNDCNode("7"), ItemNDCNode("8") }; + merge_data1 = { ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("6"), ItemNDCNode("9"), ItemNDCNode("9") }; + merge_data2 = { ItemNDCNode("0"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("6"), ItemNDCNode("7"), ItemNDCNode("7") }; + merge_data3 = { ItemNDCNode("0"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("6"), ItemNDCNode("7") }; + merge_data4 = { ItemNDCNode("0"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("3"), ItemNDCNode("6"), ItemNDCNode("7"), ItemNDCNode("8"), ItemNDCNode("9") }; + } + }; + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_default_constructor) + { + DataNDC0 data0; + DataNDC1 data1; + + CHECK(data0.empty()); + CHECK(data1.empty()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_constructor_range) + { + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + + CHECK(!data0.empty()); + CHECK_EQUAL(sorted_data.size(), data0.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_empty_begin_end) + { + DataNDC0 data0; + + CHECK(data0.begin() == data0.end()); + + DataNDC0::const_iterator begin = data0.begin(); + DataNDC0::const_iterator end = data0.end(); + CHECK(begin == end); + + CHECK(data0.cbegin() == data0.cend()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_iterator) + { + bool are_equal; + + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + + are_equal = std::equal(data0.begin(), data0.end(), sorted_data.begin()); + + CHECK(are_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_const_iterator) + { + bool are_equal; + + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + + are_equal = std::equal(data0.cbegin(), data0.cend(), sorted_data.begin()); + + CHECK(are_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_clear) + { + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + data0.clear(); + + CHECK(data0.empty()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assign_range) + { + bool are_equal; + + DataNDC0 data0; + + // Do it twice. We should only get one copy. + data0.assign(sorted_data.begin(), sorted_data.end()); + data0.assign(sorted_data.begin(), sorted_data.end()); + + are_equal = std::equal(data0.begin(), data0.end(), sorted_data.begin()); + + CHECK(are_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_assign_range_two_lists_same) + { + bool are_equal; + + DataNDC0 data0; + DataNDC1 data1; + + data0.assign(sorted_data.begin(), sorted_data.end()); + data1.assign(sorted_data.begin(), sorted_data.end()); + + are_equal = std::equal(data0.begin(), data0.end(), sorted_data.begin()); + CHECK(are_equal); + + are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); + CHECK(are_equal); + } + + ////************************************************************************* + TEST_FIXTURE(SetupFixture, test_two_lists_different) + { + bool are_equal; + + std::list compare0; + std::list compare1; + + DataNDC0 data0; + DataNDC1 data1; + + ItemNDCNode node0("0"); + ItemNDCNode node1("1"); + ItemNDCNode node2("2"); + ItemNDCNode node3("3"); + ItemNDCNode node4("4"); + ItemNDCNode node5("5"); + ItemNDCNode node6("6"); + ItemNDCNode node7("7"); + + compare0.push_front(node0); + compare0.push_front(node1); + compare0.push_front(node2); + compare0.push_front(node4); + compare0.push_front(node6); + compare0.push_front(node7); + + data0.push_front(node0); + data0.push_front(node1); + data0.push_front(node2); + data0.push_front(node4); + data0.push_front(node6); + data0.push_front(node7); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + CHECK_EQUAL(6, data0.size()); + CHECK_EQUAL(6, std::distance(data0.begin(), data0.end())); + + compare1.push_front(node0); + compare1.push_front(node1); + compare1.push_front(node3); + compare1.push_front(node4); + compare1.push_front(node5); + compare1.push_front(node7); + + data1.push_front(node0); + data1.push_front(node1); + data1.push_front(node3); + data1.push_front(node4); + data1.push_front(node5); + data1.push_front(node7); + + are_equal = std::equal(data1.begin(), data1.end(), compare1.begin()); + CHECK(are_equal); + CHECK_EQUAL(6, data1.size()); + CHECK_EQUAL(6, std::distance(data1.begin(), data1.end())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_position_value) + { + bool are_equal; + + ItemNDCNode INSERT_VALUE1 = ItemNDCNode("1"); + ItemNDCNode INSERT_VALUE2 = ItemNDCNode("2"); + + std::vector compare_data(sorted_data.begin(), sorted_data.end()); + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + DataNDC1 data1(sorted_data.begin(), sorted_data.end()); + + size_t offset = 2; + + DataNDC0::iterator i_data = data0.begin(); + std::advance(i_data, offset); + + std::vector::iterator i_compare_data = compare_data.begin(); + std::advance(i_compare_data, offset); + + data0.insert(i_data, INSERT_VALUE1); + compare_data.insert(i_compare_data, INSERT_VALUE1); + + are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(compare_data.size(), data0.size()); + CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + + are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(sorted_data.size(), data1.size()); + CHECK_EQUAL(sorted_data.size(), std::distance(data1.begin(), data1.end())); + + offset = 0; + + i_data = data0.begin(); + std::advance(i_data, offset); + + i_compare_data = compare_data.begin(); + std::advance(i_compare_data, offset); + + std::vector temp(data0.begin(), data0.end()); + + data0.insert(i_data, INSERT_VALUE2); + compare_data.insert(i_compare_data, INSERT_VALUE2); + + temp.assign(data0.begin(), data0.end()); + + are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(compare_data.size(), data0.size()); + CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + + are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(sorted_data.size(), data1.size()); + CHECK_EQUAL(sorted_data.size(), std::distance(data1.begin(), data1.end())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_insert_range) + { + bool are_equal; + + std::vector test1 = { ItemNDCNode("0"), ItemNDCNode("1"), ItemNDCNode("2"), ItemNDCNode("3"), ItemNDCNode("4") }; + std::vector test2 = { ItemNDCNode("5"), ItemNDCNode("6"), ItemNDCNode("7"), ItemNDCNode("8"), ItemNDCNode("9") }; + std::vector compare(test2); + compare.insert(compare.end(), test1.begin(), test1.end()); + + DataNDC0 data0(test1.begin(), test1.end()); + DataNDC1 data1(test1.begin(), test1.end()); + + data0.insert(data0.begin(), test2.begin(), test2.end()); + + are_equal = std::equal(data0.begin(), data0.end(), compare.begin()); + CHECK(are_equal); + + are_equal = std::equal(data1.begin(), data1.end(), test1.begin()); + CHECK(are_equal); + CHECK_EQUAL(test1.size(), data1.size()); + CHECK_EQUAL(test1.size(), std::distance(data1.begin(), data1.end())); + + compare.assign(test1.begin(), test1.end()); + data0.assign(test1.begin(), test1.end()); + + std::vector::iterator icd = compare.begin(); + DataNDC0::iterator id = data0.begin(); + + std::advance(icd, 3); + std::advance(id, 3); + + compare.insert(icd, test2.begin(), test2.end()); + data0.insert(id, test2.begin(), test2.end()); + + std::vector d(data0.begin(), data0.end()); + std::vector c(compare.begin(), compare.end()); + + are_equal = std::equal(data0.begin(), data0.end(), compare.begin()); + CHECK(are_equal); + + are_equal = std::equal(data1.begin(), data1.end(), test1.begin()); + CHECK(are_equal); + CHECK_EQUAL(test1.size(), data1.size()); + CHECK_EQUAL(test1.size(), std::distance(data1.begin(), data1.end())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_push_front) + { + bool are_equal; + + std::list compare_data; + DataNDC0 data0; + + ItemNDCNode node1("1"); + ItemNDCNode node2("2"); + ItemNDCNode node3("3"); + ItemNDCNode node4("4"); + ItemNDCNode node5("5"); + ItemNDCNode node6("6"); + + compare_data.push_front(node1); + compare_data.push_front(node2); + compare_data.push_front(node3); + compare_data.push_front(node4); + compare_data.push_front(node5); + compare_data.push_front(node6); + + CHECK_NO_THROW(data0.push_front(node1)); + CHECK_NO_THROW(data0.push_front(node2)); + CHECK_NO_THROW(data0.push_front(node3)); + CHECK_NO_THROW(data0.push_front(node4)); + CHECK_NO_THROW(data0.push_front(node5)); + CHECK_NO_THROW(data0.push_front(node6)); + + are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(6, data0.size()); + CHECK_EQUAL(6, std::distance(data0.begin(), data0.end())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_push_front_pop_front) + { + DataNDC0 data0; + DataNDC1 data1; + + ItemNDCNode node1("1"); + ItemNDCNode node2("2"); + ItemNDCNode node3("3"); + ItemNDCNode node4("4"); + ItemNDCNode node5("5"); + ItemNDCNode node6("6"); + + data0.push_front(node1); + data0.push_front(node2); + data0.push_front(node3); + data0.push_front(node4); + data0.push_front(node5); + data0.push_front(node6); + + data1.push_front(node1); + data1.push_front(node2); + data1.push_front(node3); + data1.push_front(node4); + data1.push_front(node5); + data1.push_front(node6); + + CHECK_EQUAL(6, data0.size()); + CHECK_EQUAL(6, std::distance(data0.begin(), data0.end())); + CHECK(!data0.empty()); + + data0.pop_front(); + data0.pop_front(); + data0.pop_front(); + data0.pop_front(); + data0.pop_front(); + + CHECK_EQUAL(1, data0.size()); + CHECK_EQUAL(1, std::distance(data0.begin(), data0.end())); + CHECK(!data0.empty()); + + data0.pop_front(); + + CHECK_EQUAL(0, data0.size()); + CHECK_EQUAL(0, std::distance(data0.begin(), data0.end())); + CHECK(data0.empty()); + + CHECK_EQUAL(6, data1.size()); + CHECK_EQUAL(6, std::distance(data1.begin(), data1.end())); + CHECK(!data1.empty()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_single) + { + bool are_equal; + + std::vector compare_data(sorted_data.begin(), sorted_data.end()); + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + DataNDC1 data1(sorted_data.begin(), sorted_data.end()); + + DataNDC0::iterator i_data = data0.begin(); + std::advance(i_data, 3); + + std::vector::iterator i_compare_data = compare_data.begin(); + std::advance(i_compare_data, 3); + + i_compare_data = compare_data.erase(i_compare_data); + i_data = data0.erase(i_data); + + are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); + + CHECK(are_equal); + CHECK(*i_compare_data == *i_data); + + i_compare_data = compare_data.erase(compare_data.begin()); + i_data = data0.erase(data0.begin()); + + are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); + + CHECK(are_equal); + CHECK_EQUAL(compare_data.size(), data0.size()); + CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + + are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(sorted_data.size(), data1.size()); + CHECK_EQUAL(sorted_data.size(), std::distance(data1.begin(), data1.end())); + + are_equal = *i_data == *i_compare_data; + CHECK(are_equal); + + // Move to the last value and erase. + i_compare_data = compare_data.begin(); + i_compare_data = compare_data.erase(i_compare_data); + + i_data = data0.begin(); + i_data = data0.erase(i_data); + + are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); + + CHECK(are_equal); + CHECK_EQUAL(compare_data.size(), data0.size()); + CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + + are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(sorted_data.size(), data1.size()); + CHECK_EQUAL(sorted_data.size(), std::distance(data1.begin(), data1.end())); + + are_equal = *i_data == *i_compare_data; + CHECK(are_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_range) + { + bool are_equal; + + std::vector compare_data(sorted_data.begin(), sorted_data.end()); + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + DataNDC1 data1(sorted_data.begin(), sorted_data.end()); + + DataNDC0::iterator i_data_1 = data0.begin(); + std::advance(i_data_1, 3); + + DataNDC0::iterator i_data_2 = data0.begin(); + std::advance(i_data_2, 4); + + std::vector::iterator i_compare_data_1 = compare_data.begin(); + std::advance(i_compare_data_1, 3); + + std::vector::iterator i_compare_data_2 = compare_data.begin(); + std::advance(i_compare_data_2, 4); + + std::vector::iterator i_compare_result = compare_data.erase(i_compare_data_1, i_compare_data_2); + + DataNDC0::iterator i_result = data0.erase(i_data_1, i_data_2); + + CHECK_EQUAL(*i_compare_result, *i_result); + + are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(compare_data.size(), data0.size()); + CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + + are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(sorted_data.size(), data1.size()); + CHECK_EQUAL(sorted_data.size(), std::distance(data1.begin(), data1.end())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_range_end) + { + bool are_equal; + + std::vector compare_data(sorted_data.begin(), sorted_data.end()); + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + DataNDC1 data1(sorted_data.begin(), sorted_data.end()); + + DataNDC0::iterator i_data = data0.begin(); + std::advance(i_data, 5); + + std::vector::iterator i_compare_data = compare_data.begin(); + std::advance(i_compare_data, 5); + + std::vector::iterator i_compare_result = compare_data.erase(i_compare_data, compare_data.end()); + + DataNDC0::iterator i_result = data0.erase(i_data, data0.end()); + + CHECK(i_result == data0.end()); + + are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(compare_data.size(), data0.size()); + CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + + are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(sorted_data.size(), data1.size()); + CHECK_EQUAL(sorted_data.size(), std::distance(data1.begin(), data1.end())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_erase_all) + { + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + + data0.erase(data0.begin(), data0.end()); + + CHECK(data0.empty()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_front) + { + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + + CHECK_EQUAL(sorted_data.front(), data0.front()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_unique) + { + bool are_equal; + + DataNDC0 data0(non_unique_data.begin(), non_unique_data.end()); + DataNDC1 data1(non_unique_data.begin(), non_unique_data.end()); + + data0.unique(EqualItemNDCNode()); + + // data0 should have changed. + are_equal = std::equal(data0.begin(), data0.end(), unique_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(unique_data.size(), data0.size()); + CHECK_EQUAL(unique_data.size(), std::distance(data0.begin(), data0.end())); + + // data1 should not have changed. + are_equal = std::equal(data1.begin(), data1.end(), non_unique_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(non_unique_data.size(), data1.size()); + CHECK_EQUAL(non_unique_data.size(), std::distance(data1.begin(), data1.end())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_remove) + { + bool are_equal; + + std::vector compare_data(sorted_data.begin(), sorted_data.end()); + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + DataNDC1 data1(sorted_data.begin(), sorted_data.end()); + + std::vector::iterator i_item = std::find(compare_data.begin(), compare_data.end(), ItemNDCNode("7")); + compare_data.erase(i_item); + data0.remove(ItemNDCNode("7")); + + are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); + + CHECK(are_equal); + CHECK_EQUAL(compare_data.size(), data0.size()); + CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + + are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(sorted_data.size(), data1.size()); + CHECK_EQUAL(sorted_data.size(), std::distance(data1.begin(), data1.end())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_remove_if) + { + bool are_equal; + + std::vector compare_data(sorted_data.begin(), sorted_data.end()); + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + DataNDC1 data1(sorted_data.begin(), sorted_data.end()); + + std::vector::iterator i_item = std::find(compare_data.begin(), compare_data.end(), ItemNDCNode("7")); + compare_data.erase(i_item); + data0.remove_if(std::bind2nd(std::equal_to(), ItemNDCNode("7"))); + + are_equal = std::equal(data0.begin(), data0.end(), compare_data.begin()); + + CHECK(are_equal); + CHECK_EQUAL(compare_data.size(), data0.size()); + CHECK_EQUAL(compare_data.size(), std::distance(data0.begin(), data0.end())); + + are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); + CHECK(are_equal); + CHECK_EQUAL(sorted_data.size(), data1.size()); + CHECK_EQUAL(sorted_data.size(), std::distance(data1.begin(), data1.end())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_reverse) + { + bool are_equal; + + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + DataNDC1 data1(sorted_data.begin(), sorted_data.end()); + + data0.reverse(); // Just reverse one of them. + + are_equal = std::equal(data0.begin(), data0.end(), sorted_data.rbegin()); + CHECK(are_equal); + + are_equal = std::equal(data1.begin(), data1.end(), sorted_data.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_sort) + { + bool are_equal; + + DataNDC0 data0(unsorted_data.begin(), unsorted_data.end()); + DataNDC1 data1(unsorted_data.begin(), unsorted_data.end()); + + data0.sort(); // Just sort one of them. + + are_equal = std::equal(data0.begin(), data0.end(), sorted_data.begin()); + CHECK(are_equal); + + are_equal = std::equal(data1.begin(), data1.end(), unsorted_data.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_sort_compare) + { + bool are_equal; + + DataNDC0 data0(unsorted_data.begin(), unsorted_data.end()); + DataNDC1 data1(unsorted_data.begin(), unsorted_data.end()); + + data0.sort(CompareItemNDCNode()); // Just sort one of them. + + are_equal = std::equal(data0.begin(), data0.end(), sorted_data.begin()); + CHECK(are_equal); + + are_equal = std::equal(data1.begin(), data1.end(), unsorted_data.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_iterator) + { + bool are_equal; + + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + DataNDC0 data1(unsorted_data.begin(), unsorted_data.end()); + + DataNDC0::iterator idata_source = data1.begin(); + std::advance(idata_source, 2); + + DataNDC0::iterator idata_destination = data0.begin(); + std::advance(idata_destination, 3); + + std::list compare0(data0.begin(), data0.end()); + std::list compare1(data1.begin(), data1.end()); + + std::list::iterator icompare_source = compare1.begin(); + std::advance(icompare_source, 2); + + std::list::iterator icompare_destination = compare0.begin(); + std::advance(icompare_destination, 3); + + data0.splice(idata_destination, data1, idata_source); + compare0.splice(icompare_destination, compare1, icompare_source); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + are_equal = std::equal(data1.begin(), data1.end(), compare1.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_iterator_same_list) + { + bool are_equal; + + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + + DataNDC0::iterator idata_source = data0.begin(); + std::advance(idata_source, 2); + + DataNDC0::iterator idata_destination = data0.begin(); + std::advance(idata_destination, 5); + + std::list compare0(data0.begin(), data0.end()); + + std::list::iterator icompare_source = compare0.begin(); + std::advance(icompare_source, 2); + + std::list::iterator icompare_destination = compare0.begin(); + std::advance(icompare_destination, 5); + + data0.splice(idata_destination, data0, idata_source); + compare0.splice(icompare_destination, compare0, icompare_source); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_list) + { + bool are_equal; + + DataNDC0 data0(sorted_data.begin() , sorted_data.end()); + DataNDC0 data1(sorted_data2.begin(), sorted_data2.end()); + + DataNDC0::iterator idata_destination = data0.begin(); + std::advance(idata_destination, 3); + + std::list compare0(data0.begin(), data0.end()); + std::list compare1(data1.begin(), data1.end()); + + std::list::iterator icompare_destination = compare0.begin(); + std::advance(icompare_destination, 3); + + data0.splice(idata_destination, data1); + compare0.splice(icompare_destination, compare1); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + CHECK_EQUAL(data1.size(), compare1.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_list_self) + { + bool are_equal; + + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + + DataNDC0::iterator idata_destination = data0.begin(); + std::advance(idata_destination, 3); + + std::list compare0(data0.begin(), data0.end()); + + std::list::iterator icompare_destination = compare0.begin(); + std::advance(icompare_destination, 3); + + data0.splice(idata_destination, data0); + compare0.splice(icompare_destination, compare0); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_range) + { + bool are_equal; + + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + DataNDC0 data1(sorted_data2.begin(), sorted_data2.end()); + + DataNDC0::iterator idata_destination = data0.begin(); + std::advance(idata_destination, 3); + + DataNDC0::iterator idata_begin = data1.begin(); + std::advance(idata_begin, 2); + + DataNDC0::iterator idata_end = data1.begin(); + std::advance(idata_end, 7); + + std::list compare0(data0.begin(), data0.end()); + std::list compare1(data1.begin(), data1.end()); + + std::list::iterator icompare_destination = compare0.begin(); + std::advance(icompare_destination, 3); + + std::list::iterator icompare_begin = compare1.begin(); + std::advance(icompare_begin, 2); + + std::list::iterator icompare_end = compare1.begin(); + std::advance(icompare_end, 7); + + data0.splice(idata_destination, data1, idata_begin, idata_end); + compare0.splice(icompare_destination, compare1, icompare_begin, icompare_end); + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + CHECK_EQUAL(data1.size(), compare1.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_splice_range_self) + { + bool are_equal; + + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + + DataNDC0::iterator idata_destination = data0.begin(); + std::advance(idata_destination, 2); + + DataNDC0::iterator idata_begin = data0.begin(); + std::advance(idata_begin, 4); + + DataNDC0::iterator idata_end = data0.begin(); + std::advance(idata_end, 7); + + std::list compare0(data0.begin(), data0.end()); + + std::list::iterator icompare_destination = compare0.begin(); + std::advance(icompare_destination, 2); + + std::list::iterator icompare_begin = compare0.begin(); + std::advance(icompare_begin, 4); + + std::list::iterator icompare_end = compare0.begin(); + std::advance(icompare_end, 7); + + data0.splice(idata_destination, data0, idata_begin, idata_end); + compare0.splice(icompare_destination, compare0, icompare_begin, icompare_end); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_1) + { + bool are_equal; + + DataNDC0 data0(merge_data0.begin(), merge_data0.end()); + DataNDC0 data1(merge_data1.begin(), merge_data1.end()); + + std::list compare0(merge_data0.begin(), merge_data0.end()); + std::list compare1(merge_data1.begin(), merge_data1.end()); + + data0.merge(data1); + compare0.merge(compare1); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + CHECK_EQUAL(data1.size(), compare1.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_2) + { + bool are_equal; + + DataNDC0 data0(merge_data0.begin(), merge_data0.end()); + DataNDC0 data2(merge_data2.begin(), merge_data2.end()); + + std::list compare0(merge_data0.begin(), merge_data0.end()); + std::list compare2(merge_data2.begin(), merge_data2.end()); + + data0.merge(data2); + compare0.merge(compare2); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + CHECK_EQUAL(data2.size(), compare2.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_3) + { + bool are_equal; + + DataNDC0 data0(merge_data0.begin(), merge_data0.end()); + DataNDC0 data3(merge_data3.begin(), merge_data3.end()); + + std::list compare0(merge_data0.begin(), merge_data0.end()); + std::list compare3(merge_data3.begin(), merge_data3.end()); + + data0.merge(data3); + compare0.merge(compare3); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + CHECK_EQUAL(data3.size(), compare3.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_4) + { + bool are_equal; + + DataNDC0 data0(merge_data0.begin(), merge_data0.end()); + DataNDC0 data4(merge_data4.begin(), merge_data4.end()); + + std::list compare0(merge_data0.begin(), merge_data0.end()); + std::list compare4(merge_data4.begin(), merge_data4.end()); + + data0.merge(data4); + compare0.merge(compare4); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + CHECK_EQUAL(data4.size(), compare4.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_merge_0_1_reverse_order) + { + bool are_equal; + + DataNDC0 data0(merge_data0.begin(), merge_data0.end()); + DataNDC0 data1(merge_data1.begin(), merge_data1.end()); + + data0.reverse(); + data1.reverse(); + + std::list compare0(merge_data0.begin(), merge_data0.end()); + std::list compare1(merge_data1.begin(), merge_data1.end()); + + compare0.reverse(); + compare1.reverse(); + + data0.merge(data1, std::greater()); + compare0.merge(compare1, std::greater()); + + are_equal = std::equal(data0.begin(), data0.end(), compare0.begin()); + CHECK(are_equal); + + CHECK_EQUAL(data0.size(), compare0.size()); + CHECK_EQUAL(data1.size(), compare1.size()); + } + }; +} diff --git a/test/vs2015/etl.sln b/test/vs2015/etl.sln index 476a5ddc..153caf7a 100644 --- a/test/vs2015/etl.sln +++ b/test/vs2015/etl.sln @@ -7,10 +7,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "etl", "etl.vcxproj", "{C21D EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug No Unit Tests|Win32 = Debug No Unit Tests|Win32 Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C21DF78C-D8E0-46AB-9D6F-D38A3C1CB0FB}.Debug No Unit Tests|Win32.ActiveCfg = Debug No Unit Tests|Win32 + {C21DF78C-D8E0-46AB-9D6F-D38A3C1CB0FB}.Debug No Unit Tests|Win32.Build.0 = Debug No Unit Tests|Win32 {C21DF78C-D8E0-46AB-9D6F-D38A3C1CB0FB}.Debug|Win32.ActiveCfg = Debug|Win32 {C21DF78C-D8E0-46AB-9D6F-D38A3C1CB0FB}.Debug|Win32.Build.0 = Debug|Win32 {C21DF78C-D8E0-46AB-9D6F-D38A3C1CB0FB}.Release|Win32.ActiveCfg = Release|Win32 diff --git a/test/vs2015/etl.vcxproj b/test/vs2015/etl.vcxproj index 20ca6e02..0292881a 100644 --- a/test/vs2015/etl.vcxproj +++ b/test/vs2015/etl.vcxproj @@ -160,7 +160,6 @@ - @@ -206,6 +205,7 @@ + @@ -338,6 +338,7 @@ + @@ -369,6 +370,7 @@ + diff --git a/test/vs2015/etl.vcxproj.filters b/test/vs2015/etl.vcxproj.filters index 072f9405..3bd86a0f 100644 --- a/test/vs2015/etl.vcxproj.filters +++ b/test/vs2015/etl.vcxproj.filters @@ -372,9 +372,6 @@ ETL\Maths - - ETL\Maths - ETL\Containers @@ -465,6 +462,9 @@ ETL\Containers + + ETL\Containers + @@ -713,6 +713,12 @@ Source Files + + Source Files + + + Source Files +