diff --git a/README.md b/README.md index 0417c9b..e7275c5 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,12 @@ # cpp-xor-list [![Build Status](https://travis-ci.org/NikitkoCent/cpp-xor-list.svg?branch=master)](https://travis-ci.org/NikitkoCent/cpp-xor-list) [![Code Coverage](https://codecov.io/gh/NikitkoCent/cpp-xor-list/branch/master/graph/badge.svg)](https://codecov.io/gh/NikitkoCent/cpp-xor-list) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/NikitkoCent/cpp-xor-list/blob/master/LICENSE) C++11-compatible implementation of [XOR linked list](https://en.wikipedia.org/wiki/XOR_linked_list) -with STL-like interface. Most of C++14 `std::list` methods are supported. +with STL-like interface. Most of C++14 `std::list` methods are also supported.
Notes ### Not supported methods: -* `std::list::emplace` (but `std::list::emplace_` are supported) - may be added later -* comparison operators (`operator==`, `operator<=` etc) - may be added later -* `std::list::max_size` - may be added later -* `std::list::get_allocator` - may be added later -* `std::list::remove_if` - may be added later -* `std::erase_if` - may be added later +* `std::erase_if` - will be added after C++20 release
## Example diff --git a/include/xor_list/xor_list.h b/include/xor_list/xor_list.h index 12dba6a..82a2109 100644 --- a/include/xor_list/xor_list.h +++ b/include/xor_list/xor_list.h @@ -9,12 +9,13 @@ #include // ::std::conditional, ::std::enable_if, ::std::is_base_of #include // ::std::uint*_t #include // ::std::ptrdiff_t -#include // ::std::swap +#include // ::std::swap, ::std::equal, ::std::lexicographical_compare #include // ::std::tie #include // ::std::array +#include // ::std::numeric_limits -template > +template> class xor_list { private: @@ -134,7 +135,7 @@ class xor_list } } - virtual ~xor_list() + ~xor_list() { clear(); } @@ -157,6 +158,11 @@ class xor_list return *this; } + allocator_type get_allocator() const + { + return allocator_type(allocator); + } + void swap(xor_list &other) { swapImpl(other); @@ -182,16 +188,16 @@ class xor_list emplace_front(::std::move(data)); } - template + template void emplace_back(Args&&... args) { - (void)insertNodeToThisBefore(cend(), createNode(::std::forward(args)...)); + (void)emplace(cend(), ::std::forward(args)...); } - template + template void emplace_front(Args&&... args) { - (void)insertNodeToThisBefore(cbegin(), createNode(::std::forward(args)...)); + (void)emplace(cbegin(), ::std::forward(args)...); } void pop_front() @@ -209,6 +215,11 @@ class xor_list return length; } + size_type max_size() const noexcept + { + return ::std::numeric_limits::max() / sizeof(NodeWithValue); + } + bool empty() const noexcept { return (size() == 0); @@ -219,22 +230,22 @@ class xor_list destroySequence(cbegin(), cend(), size()); } - T& back() noexcept + T& back() { return *(--end()); } - const T& back() const noexcept + const T& back() const { return *(--cend()); } - T& front() noexcept + T& front() { return *begin(); } - const T& front() const noexcept + const T& front() const { return *cbegin(); } @@ -270,13 +281,13 @@ class xor_list return { reinterpret_cast(afterTail.xorPtr), &afterTail }; } - void sort() noexcept + void sort() { sort(::std::less{}); } - template - void sort(Compare isLess) noexcept + template + void sort(Compare isLess) { using Range = ::std::pair; struct NullableRange @@ -347,13 +358,12 @@ class xor_list // strong exception-safe guarantee iterator insert(const_iterator position, const_reference val) { - //insertNodeToThisBefore noexcept! - return insertNodeToThisBefore(position, createNode(val)).first; + return emplace(position, val); } // WARNING! Iterators equal to position will become invalid // strong exception-safe guarantee - template + template iterator insert(const_iterator position, InputIterator first, InputIterator last) { if (first == last) @@ -381,7 +391,17 @@ class xor_list return result; } + // WARNING! Iterators equal to position will become invalid + // strong exception-safe guarantee + template + iterator emplace(const_iterator position, Args&&... args) + { + //insertNodeToThisBefore noexcept! + return insertNodeToThisBefore(position, createNode(::std::forward(args)...)).first; + } + // WARNING! All iterators will become invalid + // Complexity: O(1) void reverse() noexcept { if (empty()) @@ -418,6 +438,32 @@ class xor_list return { first.prev, last.current }; } + size_type remove(const T &value) + { + return remove_if([&value](const T &elem) { return (elem == value); }); + } + + template + size_type remove_if(UnaryPredicate p) + { + size_type result = 0; + + for (const_iterator iter = cbegin(); iter != cend();) + { + if (p(*iter)) + { + iter = erase(iter); + ++result; + } + else + { + ++iter; + } + } + + return result; + } + void resize(size_type count) { resizeImpl(count); @@ -428,7 +474,7 @@ class xor_list resizeImpl(count, val); } - template + template typename ::std::enable_if<::std::is_base_of<::std::input_iterator_tag, typename ::std::iterator_traits::iterator_category>::value>::type assign(InputIterator first, InputIterator last) @@ -478,7 +524,7 @@ class xor_list } - void splice(const_iterator position, xor_list &x) noexcept + void splice(const_iterator position, xor_list &x) { if ((this == ::std::addressof(x)) || (x.empty())) { @@ -491,7 +537,7 @@ class xor_list (void)insertSequenceToThisBefore(position, range.first, range.second, distance); } - void splice(const_iterator position, xor_list &x, const_iterator i) noexcept + void splice(const_iterator position, xor_list &x, const_iterator i) { if ((this == ::std::addressof(x)) && ((position == i) || (position.prev == i.current))) { @@ -502,7 +548,7 @@ class xor_list (void)insertNodeToThisBefore(position, static_cast(range.first.current)); } - void splice(const_iterator position, xor_list &x, const_iterator first, const_iterator last) noexcept + void splice(const_iterator position, xor_list &x, const_iterator first, const_iterator last) { if (first == last) { @@ -522,7 +568,7 @@ class xor_list } // All iterators will become invalid - template + template void unique(BinaryPredicate isEqual) { if (size() < 2) @@ -545,14 +591,14 @@ class xor_list } // All iterators from *this and x will become invalid - void merge(xor_list &x) noexcept + void merge(xor_list &x) { merge(x, ::std::less{}); } // All iterators from *this and x will become invalid - template - void merge(xor_list &x, Compare isLess) noexcept + template + void merge(xor_list &x, Compare isLess) { if (!x.empty()) { @@ -595,14 +641,14 @@ class xor_list Node(const Node&) noexcept = default; Node(Node &&) noexcept = default; - + virtual ~Node() = default; Node& operator=(const Node&) noexcept = default; Node& operator=(Node &&) noexcept = default; }; - struct NodeWithValue : Node + struct NodeWithValue final : Node { T value; @@ -723,7 +769,7 @@ class xor_list }; - struct CutResult + struct CutResult final { ::std::pair cutted; iterator end; @@ -788,10 +834,8 @@ class xor_list return insertNodeBefore(position, node); } - /* - * All iterators equal to will become invalid - * Returns valid range [inserted, position] - */ + // All iterators equal to will become invalid + // Returns valid range [inserted, position] static ::std::pair insertNodeBefore(const_iterator position, NodeWithValue *const node) noexcept { @@ -819,11 +863,10 @@ class xor_list return insertSequenceBefore(position, begin, end); } - /* - * All iterators equal to , will become invalid - * Iterators equal to end still remains valid (--end == result.second) - * Returns valid range [begin, position] - */ + + // All iterators equal to , will become invalid + // Iterators equal to end still remains valid (--end == result.second) + // Returns valid range [begin, position] static ::std::pair insertSequenceBefore(const_iterator position, const_iterator begin, const_iterator end) noexcept @@ -852,14 +895,12 @@ class xor_list return cutSequence(first, last); } - /* - * Returns iterators to the first cutted and following the last cutted elements - * Decrement result.first or increment result.second is UB - * Dereference result.second is UB - * Increment and dereference result.first are still valid - * Decrement result.second returns is an iterator to the last cutted element - * Iterators equal to begin, end will become invalid - */ + // Returns iterators to the first cutted and following the last cutted elements + // Decrement result.first or increment result.second is UB + // Dereference result.second is UB + // Increment and dereference result.first are still valid + // Decrement result.second returns is an iterator to the last cutted element + // Iterators equal to begin, end will become invalid static CutResult cutSequence(const_iterator begin, const_iterator end) noexcept { @@ -1026,10 +1067,8 @@ class xor_list return mergeSequences(beginTo, endTo, beginFrom, endFrom, ::std::forward(isLess)); } - /* - * All iterators will become invalid - * Return new range [first, second) - */ + // All iterators will become invalid + // Return new range [first, second) template static ::std::pair mergeSequences(const_iterator beginTo, const_iterator endTo, @@ -1080,4 +1119,43 @@ class xor_list } }; + +// Comparison operators + +template +bool operator==(const xor_list &lhs, const xor_list &rhs) +{ + return ((lhs.size() == rhs.size()) && (::std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin()))); +} + +template +bool operator!=(const xor_list &lhs, const xor_list &rhs) +{ + return (!(lhs == rhs)); +} + +template +bool operator<(const xor_list &lhs, const xor_list &rhs) +{ + return ::std::lexicographical_compare(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend()); +} + +template +bool operator>(const xor_list &lhs, const xor_list &rhs) +{ + return (rhs < lhs); +} + +template +bool operator<=(const xor_list &lhs, const xor_list &rhs) +{ + return (!(rhs < lhs)); +} + +template +bool operator>=(const xor_list &lhs, const xor_list &rhs) +{ + return (!(lhs < rhs)); +} + #endif //XORLIST_XOR_LIST_H diff --git a/tests/list.cpp b/tests/list.cpp index b713c0f..3fd5a1b 100644 --- a/tests/list.cpp +++ b/tests/list.cpp @@ -5,6 +5,7 @@ #include #include #include +#include template @@ -1089,6 +1090,22 @@ TEST(LIST, EMPLACE2) ASSERT_THAT(list, ::testing::ElementsAre(2, 1, 0, -1)); } +TEST(LIST, EMPLACE3) +{ + xor_list> list{2, 1, 0, -1}; + + auto emplacedIter1 = list.emplace(++++list.cbegin(), 10); + auto emplacedIter2 = list.emplace(std::next(emplacedIter1), 20); + + ASSERT_EQ(list.size(), 6U); + ASSERT_THAT(list, ::testing::ElementsAre(2, 1, 10, 20, 0, -1)); + + ASSERT_EQ(*emplacedIter1, 10); + ASSERT_EQ(*emplacedIter2, 20); + + ASSERT_EQ(++emplacedIter1, emplacedIter2); +} + TEST(LIST, EMPLACE_EXCEPTION1) { xor_list> list; @@ -2293,3 +2310,178 @@ TEST(LIST, INSERT_RANGE_EXCEPTION2) ASSERT_EQ(list.size(), 4U); ASSERT_THAT(list, ::testing::ElementsAre(100, 200, 300, 400)); } + +TEST(LIST, REMOVE_EMPTY) +{ + xor_list> list; + + ASSERT_EQ(list.remove(1), 0U); + + ASSERT_TRUE(list.empty()); +} + +TEST(LIST, REMOVE_SINGLE) +{ + xor_list> list{1}; + + ASSERT_EQ(list.remove(1), 1U); + + ASSERT_TRUE(list.empty()); +} + +TEST(LIST, REMOVE_ALL) +{ + xor_list> list{1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + + ASSERT_EQ(list.remove(1), 10U); + + ASSERT_TRUE(list.empty()); +} + +TEST(LIST, REMOVE_MIDDLE) +{ + xor_list> list{0, 0, 0, 1, 1, 1, 0, 1, 0, 0}; + + ASSERT_EQ(list.remove(1), 4U); + + ASSERT_EQ(list.size(), 6U); + ASSERT_THAT(list, ::testing::ElementsAre(0, 0, 0, 0, 0, 0)); +} + +TEST(LIST, REMOVE_IF_EMPTY) +{ + xor_list> list; + + ASSERT_EQ(list.remove_if([](const Value &val) { return val > 0; }), 0U); + + ASSERT_TRUE(list.empty()); +} + +TEST(LIST, REMOVE_IF_SINGLE) +{ + xor_list> list{1}; + + ASSERT_EQ(list.remove_if([](const Value &val) { return val > 0; }), 1U); + + ASSERT_TRUE(list.empty()); +} + +TEST(LIST, REMOVE_IF_ALL) +{ + xor_list> list{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + ASSERT_EQ(list.remove_if([](const Value &) { return true; }), 10U); + + ASSERT_TRUE(list.empty()); +} + +TEST(LIST, REMOVE_IF_MIDDLE) +{ + xor_list> list{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + ASSERT_EQ(list.remove_if([](const Value &val) { return ((val > 3) && (val < 8)); }), 4U); + + ASSERT_EQ(list.size(), 6U); + ASSERT_THAT(list, ::testing::ElementsAre(1, 2, 3, 8, 9, 10)); +} + +TEST(LIST, MAX_SIZE_INVOKE) +{ + xor_list> list; + (void)list.max_size(); +} + +TEST(LIST, COMPARISON_EMPTY_EMPTY) +{ + xor_list> list, list2; + + ASSERT_TRUE(list == list2); + ASSERT_FALSE(list != list2); + ASSERT_FALSE(list < list2); + ASSERT_TRUE(list <= list2); + ASSERT_FALSE(list > list2); + ASSERT_TRUE(list >= list2); + + ASSERT_TRUE(list2 == list); + ASSERT_FALSE(list2 != list); + ASSERT_FALSE(list2 < list); + ASSERT_TRUE(list2 <= list); + ASSERT_FALSE(list2 > list); + ASSERT_TRUE(list2 >= list); +} + +TEST(LIST, COMPARISON_EMPTY_SINGLE) +{ + xor_list> list, list2{1}; + + ASSERT_FALSE(list == list2); + ASSERT_TRUE(list != list2); + ASSERT_TRUE(list < list2); + ASSERT_TRUE(list <= list2); + ASSERT_FALSE(list > list2); + ASSERT_FALSE(list >= list2); + + ASSERT_FALSE(list2 == list); + ASSERT_TRUE(list2 != list); + ASSERT_FALSE(list2 < list); + ASSERT_FALSE(list2 <= list); + ASSERT_TRUE(list2 > list); + ASSERT_TRUE(list2 >= list); +} + +TEST(LIST, COMPARISON_SINGLE_SINGLE) +{ + xor_list> list{1}, list2{2}; + + ASSERT_FALSE(list == list2); + ASSERT_TRUE(list != list2); + ASSERT_TRUE(list < list2); + ASSERT_TRUE(list <= list2); + ASSERT_FALSE(list > list2); + ASSERT_FALSE(list >= list2); + + ASSERT_FALSE(list2 == list); + ASSERT_TRUE(list2 != list); + ASSERT_FALSE(list2 < list); + ASSERT_FALSE(list2 <= list); + ASSERT_TRUE(list2 > list); + ASSERT_TRUE(list2 >= list); +} + +TEST(LIST, COMPARISON_GENERIC1) +{ + xor_list> list{1, 2, 3, 4, 5}, list2{1, 2, 3}; + + ASSERT_FALSE(list == list2); + ASSERT_TRUE(list != list2); + ASSERT_FALSE(list < list2); + ASSERT_FALSE(list <= list2); + ASSERT_TRUE(list > list2); + ASSERT_TRUE(list >= list2); + + ASSERT_FALSE(list2 == list); + ASSERT_TRUE(list2 != list); + ASSERT_TRUE(list2 < list); + ASSERT_TRUE(list2 <= list); + ASSERT_FALSE(list2 > list); + ASSERT_FALSE(list2 >= list); +} + +TEST(LIST, COMPARISON_GENERIC2) +{ + xor_list> list{1, 2, 3, 4, 5}, list2{10, 9, 8, 7, 6}; + + ASSERT_FALSE(list == list2); + ASSERT_TRUE(list != list2); + ASSERT_TRUE(list < list2); + ASSERT_TRUE(list <= list2); + ASSERT_FALSE(list > list2); + ASSERT_FALSE(list >= list2); + + ASSERT_FALSE(list2 == list); + ASSERT_TRUE(list2 != list); + ASSERT_FALSE(list2 < list); + ASSERT_FALSE(list2 <= list); + ASSERT_TRUE(list2 > list); + ASSERT_TRUE(list2 >= list); +}