diff --git a/kotlin-native/runtime/src/main/cpp/IntrusiveList.hpp b/kotlin-native/runtime/src/main/cpp/IntrusiveList.hpp new file mode 100644 index 00000000000..a0c9f439916 --- /dev/null +++ b/kotlin-native/runtime/src/main/cpp/IntrusiveList.hpp @@ -0,0 +1,284 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +#pragma once + +#include +#include +#include + +#include "KAssert.h" +#include "Utils.hpp" + +namespace kotlin { + +template +struct DefaultIntrusiveForwardListTraits { + static T* next(const T& value) noexcept { return value.next_; } + + static void setNext(T& value, T* next) noexcept { value.next_ = next; } +}; + +// Intrusive variant of `std::forward_list`. Notable differences: +// * The container does not own nodes. Care must be taken not to allow a node +// to be in two containers at once, or twice into the same container. +// * The container is move-only, and moving invalidates `before_begin` iterator. +// * insert_after, erase_after take `iterator` instead of `const_iterator`, because +// they do in fact require mutability via `Traits::setNext`. +// * When the node leaves the container, nothing clears `next` pointer inside it. +// +// `Traits` must have 2 methods: +// static T* next(const T& value); +// static void setNext(T& value, T* next); +// NOTE: `setNext` and `next` must be callable even on uninitialized `T` (i.e. they +// should only access storage inside `T`). +template > +class intrusive_forward_list : private MoveOnly { +public: + using value_type = T; + using size_type = size_t; + using difference_type = ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + + class iterator { + public: + using difference_type = intrusive_forward_list::difference_type; + using value_type = intrusive_forward_list::value_type; + using pointer = intrusive_forward_list::pointer; + using reference = intrusive_forward_list::reference; + using iterator_category = std::forward_iterator_tag; + + iterator() noexcept = default; + iterator(const iterator&) noexcept = default; + iterator& operator=(const iterator&) noexcept = default; + + reference operator*() noexcept { return *node_; } + + pointer operator->() noexcept { return node_; } + + iterator& operator++() noexcept { + node_ = next(node_); + return *this; + } + + iterator operator++(int) noexcept { + auto result = *this; + ++(*this); + return result; + } + + bool operator==(const iterator& rhs) const noexcept { return node_ == rhs.node_; } + bool operator!=(const iterator& rhs) const noexcept { return !(*this == rhs); } + + private: + friend class intrusive_forward_list; + + explicit iterator(pointer node) noexcept : node_(node) {} + + intrusive_forward_list::pointer node_ = nullptr; + }; + + class const_iterator { + public: + using difference_type = intrusive_forward_list::difference_type; + using value_type = const intrusive_forward_list::value_type; + using pointer = intrusive_forward_list::const_pointer; + using reference = intrusive_forward_list::const_reference; + using iterator_category = std::forward_iterator_tag; + + const_iterator() noexcept = default; + const_iterator(const const_iterator&) noexcept = default; + const_iterator& operator=(const const_iterator&) noexcept = default; + + const_iterator(iterator it) noexcept : node_(it.node_) {} + + reference operator*() noexcept { return *node_; } + + pointer operator->() noexcept { return node_; } + + const_iterator& operator++() noexcept { + node_ = next(node_); + return *this; + } + + const_iterator operator++(int) noexcept { + auto result = *this; + ++(*this); + return result; + } + + bool operator==(const const_iterator& rhs) const noexcept { return node_ == rhs.node_; } + bool operator!=(const const_iterator& rhs) const noexcept { return !(*this == rhs); } + + private: + friend class intrusive_forward_list; + + explicit const_iterator(pointer node) noexcept : node_(node) {} + + pointer node_ = nullptr; + }; + + intrusive_forward_list() noexcept { + setNext(head(), nullptr); + } + + intrusive_forward_list(intrusive_forward_list&& rhs) noexcept : size_(rhs.size_) { + setNext(head(), next(rhs.head())); + setNext(rhs.head(), nullptr); + rhs.size_ = 0; + } + + template + intrusive_forward_list(InputIt first, InputIt last) noexcept { + setNext(head(), nullptr); + assign(std::move(first), std::move(last)); + } + + ~intrusive_forward_list() = default; + + intrusive_forward_list& operator=(intrusive_forward_list&& rhs) noexcept { + intrusive_forward_list tmp(std::move(rhs)); + swap(tmp); + return *this; + } + + void swap(intrusive_forward_list& rhs) noexcept { + using std::swap; + auto thisNext = next(head()); + auto rhsNext = next(rhs.head()); + swap(thisNext, rhsNext); + setNext(head(), thisNext); + setNext(rhs.head(), rhsNext); + swap(size_, rhs.size_); + } + + template + void assign(InputIt first, InputIt last) noexcept { + clear(); + insert_after(before_begin(), std::move(first), std::move(last)); + } + + reference front() noexcept { return *next(head()); } + const_reference front() const noexcept { return *next(head()); } + + iterator before_begin() noexcept { return iterator(head()); } + const_iterator before_begin() const noexcept { return const_iterator(head()); } + const_iterator cbefore_begin() const noexcept { return const_iterator(head()); } + + iterator begin() noexcept { return iterator(next(head())); } + const_iterator begin() const noexcept { return const_iterator(next(head())); } + const_iterator cbegin() const noexcept { return const_iterator(next(head())); } + + iterator end() noexcept { return iterator(); } + const_iterator end() const noexcept { return const_iterator(); } + const_iterator cend() const noexcept { return const_iterator(); } + + bool empty() const noexcept { return size_ == 0; } + + size_type max_size() const noexcept { return std::numeric_limits::max(); } + + void clear() noexcept { setNext(head(), nullptr); size_ = 0; } + + iterator insert_after(iterator pos, reference value) noexcept { + pointer nextNode = next(pos.node_); + setNext(pos.node_, &value); + setNext(&value, nextNode); + ++size_; + return iterator(&value); + } + + template + iterator insert_after(iterator pos, InputIt first, InputIt last) noexcept { + pointer nextNode = next(pos.node_); + pointer prevNode = pos.node_; + size_t newSize = size_; + for (auto it = first; it != last; ++it) { + setNext(prevNode, &*it); + prevNode = &*it; + ++newSize; + } + setNext(prevNode, nextNode); + size_ = newSize; + return iterator(prevNode); + } + + iterator erase_after(iterator pos) noexcept { + pointer prevNode = pos.node_; + pointer nodeToErase = next(pos.node_); + if (!nodeToErase) { + return end(); + } + pointer nextNode = next(nodeToErase); + setNext(prevNode, nextNode); + setNext(nodeToErase, nullptr); + --size_; + return iterator(nextNode); + } + + iterator erase_after(iterator first, iterator last) noexcept { + size_ -= std::distance(first, last) - 1; + setNext(first.node_, last.node_); + return last; + } + + void push_front(reference value) noexcept { insert_after(before_begin(), value); } + + void pop_front() noexcept { erase_after(before_begin()); } + + void remove(reference value) noexcept { + // TODO: no need to move on after finding the first match. + return remove_if([&value](const_reference x) { return &x == &value; }); + } + + template + void remove_if(P p) noexcept { + size_t newSize = size_; + pointer prev = head(); + pointer node = next(prev); + while (node) { + if (p(*node)) { + // The node is being removed. + node = next(node); + setNext(prev, node); + --newSize; + } else { + // The node is staying. + prev = node; + node = next(node); + } + } + size_ = newSize; + } + + // TODO: Implement splice_after. + + size_type size() const noexcept { + return size_; + } + +private: + static pointer next(const_pointer node) noexcept { return Traits::next(*node); } + + static void setNext(pointer node, pointer next) noexcept { return Traits::setNext(*node, next); } + + pointer head() noexcept { + return reinterpret_cast(headStorage_); + } + + const_pointer head() const noexcept { + return reinterpret_cast(headStorage_); + } + + alignas(value_type) char headStorage_[sizeof(value_type)] = { 0 }; + size_t size_ = 0; +}; + +template +intrusive_forward_list(InputIt, InputIt) -> intrusive_forward_list::value_type>; + +} // namespace kotlin diff --git a/kotlin-native/runtime/src/main/cpp/IntrusiveListTest.cpp b/kotlin-native/runtime/src/main/cpp/IntrusiveListTest.cpp new file mode 100644 index 00000000000..9025e9af6ee --- /dev/null +++ b/kotlin-native/runtime/src/main/cpp/IntrusiveListTest.cpp @@ -0,0 +1,624 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +#include "IntrusiveList.hpp" + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "Types.h" +#include "Utils.hpp" + +using namespace kotlin; + +namespace { + +class Element { +public: + explicit Element(int value) : value_(value) {} + + Element(const Element&) = default; + Element(Element&&) = default; + Element& operator=(const Element&) = default; + Element& operator=(Element&&) = default; + + int& operator*() { return value_; } + const int& operator*() const { return value_; } + + bool operator==(const Element& rhs) const { return value_ == rhs.value_; } + + bool operator!=(const Element& rhs) const { return !(*this == rhs); } + +private: + int value_; +}; + +class Node : private Pinned { +public: + explicit Node(int value) : value_(value) {} + + int& operator*() { return value_; } + const int& operator*() const { return value_; } + +private: + friend struct DefaultIntrusiveForwardListTraits; + + int value_; + // Use non-null marker to make sure inserting into the list properly updates this value. + Node* next_ = reinterpret_cast(0x1); +}; + +template +KStdList create(std::initializer_list list) { + KStdList result; + for (auto x : list) { + result.emplace_back(x); + } + return result; +} + +MATCHER_P(isEmpty, expected, (expected == !negation) ? "is empty" : "is not empty") { + bool actual = arg.empty(); + *result_listener << (actual ? "is empty" : "is not empty"); + return expected == actual; +} + +size_t getSize(const std::forward_list& list) { + return std::distance(list.begin(), list.end()); +} + +size_t getSize(const intrusive_forward_list& list) { + return list.size(); +} + +MATCHER_P(hasSize, expected, "") { + size_t actual = getSize(arg); + *result_listener << "of size " << actual; + return expected == actual; +} + +MATCHER_P(derefsTo, expected, "") { + const auto& actual = *arg; + *result_listener << "derefs to " << testing::PrintToString(actual); + return expected == actual; +} + +template +auto elementsAre(Args... args) { + return testing::AllOf( + isEmpty(sizeof...(args) == 0), + hasSize(sizeof...(args)), + testing::ElementsAre(derefsTo(args)...) + ); +} + +#define EXPECT_ELEMENTS_ARE(list, ...) \ + EXPECT_THAT(list, ::elementsAre(__VA_ARGS__)); + +} // namespace + +TEST(IntrusiveForwardListTest, CTAD) { + auto values = create>({1, 2, 3, 4}); + intrusive_forward_list list(values.begin(), values.end()); + static_assert(std::is_same_v); +} + +// Testing that operations on `intrusive_forward_list` give the same results as those on `std::forward_list`. +template +class ForwardListTest : public testing::Test {}; + +using ForwardListTestTypes = testing::Types, intrusive_forward_list>; +struct ForwardListTestNames { + template + static std::string GetName(int) { + if constexpr (std::is_same_v>) { + return "forward_list"; + } else if constexpr (std::is_same_v>) { + return "intrusive_forward_list"; + } else { + return "unknown"; + } + } +}; +TYPED_TEST_SUITE(ForwardListTest, ForwardListTestTypes, ForwardListTestNames); + +TYPED_TEST(ForwardListTest, DefaultCtor) { + using List = TypeParam; + List list; + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, RangeCtor) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, RangeCtorEmpty) { + using List = TypeParam; + auto values = create({}); + List list(values.begin(), values.end()); + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, MoveCtor) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + List otherList(std::move(list)); + EXPECT_ELEMENTS_ARE(list); + EXPECT_ELEMENTS_ARE(otherList, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, MoveAssignment) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto otherValues = create({5, 6, 7, 8}); + List otherList(otherValues.begin(), otherValues.end()); + otherList = std::move(list); + EXPECT_ELEMENTS_ARE(list); + EXPECT_ELEMENTS_ARE(otherList, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, MutableIterator) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + for (auto& x : list) { + *x += 5; + } + EXPECT_ELEMENTS_ARE(list, 6, 7, 8, 9); +} + +TYPED_TEST(ForwardListTest, MutableIteratorEmpty) { + using List = TypeParam; + List list; + for (auto& x : list) { + *x += 5; + } + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, BeforeBeginIterator) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto it = list.before_begin(); + ++it; + EXPECT_THAT(it, list.begin()); +} + +TYPED_TEST(ForwardListTest, BeforeBeginIteratorEmpty) { + using List = TypeParam; + List list; + auto it = list.before_begin(); + ++it; + EXPECT_THAT(it, list.end()); +} + +TYPED_TEST(ForwardListTest, MoveAssignmentIntoEmpty) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + List otherList; + otherList = std::move(list); + EXPECT_ELEMENTS_ARE(list); + EXPECT_ELEMENTS_ARE(otherList, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, MoveAssignmentFromEmpty) { + using List = TypeParam; + List list; + auto otherValues = create({5, 6, 7, 8}); + List otherList(otherValues.begin(), otherValues.end()); + otherList = std::move(list); + EXPECT_ELEMENTS_ARE(list); + EXPECT_ELEMENTS_ARE(otherList); +} + +TYPED_TEST(ForwardListTest, Swap) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto otherValues = create({5, 6, 7, 8}); + List otherList(otherValues.begin(), otherValues.end()); + using std::swap; + swap(list, otherList); + EXPECT_ELEMENTS_ARE(list, 5, 6, 7, 8); + EXPECT_ELEMENTS_ARE(otherList, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, SwapFirstEmpty) { + using List = TypeParam; + List list; + auto otherValues = create({5, 6, 7, 8}); + List otherList(otherValues.begin(), otherValues.end()); + using std::swap; + swap(list, otherList); + EXPECT_ELEMENTS_ARE(list, 5, 6, 7, 8); + EXPECT_ELEMENTS_ARE(otherList); +} + +TYPED_TEST(ForwardListTest, SwapSecondEmpty) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + List otherList; + using std::swap; + swap(list, otherList); + EXPECT_ELEMENTS_ARE(list); + EXPECT_ELEMENTS_ARE(otherList, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, SwapBothEmpty) { + using List = TypeParam; + List list; + List otherList; + using std::swap; + swap(list, otherList); + EXPECT_ELEMENTS_ARE(list); + EXPECT_ELEMENTS_ARE(otherList); +} + +TYPED_TEST(ForwardListTest, Assign) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto newValues = create({5, 6, 7, 8}); + list.assign(newValues.begin(), newValues.end()); + EXPECT_ELEMENTS_ARE(list, 5, 6, 7, 8); +} + +TYPED_TEST(ForwardListTest, AssignFromEmpty) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto newValues = create({}); + list.assign(newValues.begin(), newValues.end()); + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, AssignIntoEmpty) { + using List = TypeParam; + List list; + auto newValues = create({5, 6, 7, 8}); + list.assign(newValues.begin(), newValues.end()); + EXPECT_ELEMENTS_ARE(list, 5, 6, 7, 8); +} + +TYPED_TEST(ForwardListTest, Front) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + EXPECT_THAT(*list.front(), 1); + const List& constList = list; + EXPECT_THAT(*constList.front(), 1); +} + +TYPED_TEST(ForwardListTest, Clear) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + list.clear(); + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, ClearEmpty) { + using List = TypeParam; + List list; + list.clear(); + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, PushFront) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + typename List::value_type value(5); + list.push_front(value); + EXPECT_ELEMENTS_ARE(list, 5, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, PushFrontEmpty) { + using List = TypeParam; + List list; + typename List::value_type value(5); + list.push_front(value); + EXPECT_ELEMENTS_ARE(list, 5); +} + +TYPED_TEST(ForwardListTest, PopFront) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + list.pop_front(); + EXPECT_ELEMENTS_ARE(list, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, PopFrontIntoEmpty) { + using List = TypeParam; + auto values = create({1}); + List list(values.begin(), values.end()); + list.pop_front(); + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, Remove) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto& value = *std::next(list.begin(), 2); + ASSERT_THAT(*value, 3); + list.remove(value); + EXPECT_ELEMENTS_ARE(list, 1, 2, 4); +} + +TYPED_TEST(ForwardListTest, RemoveMissing) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + typename List::value_type value(5); + list.remove(value); + EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, RemoveAll) { + using List = TypeParam; + auto values = create({1}); + List list(values.begin(), values.end()); + auto& value = list.front(); + list.remove(value); + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, RemoveEmpty) { + using List = TypeParam; + List list; + typename List::value_type value(5); + list.remove(value); + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, RemoveIf) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + list.remove_if([](const auto& x) { return *x == 3; }); + EXPECT_ELEMENTS_ARE(list, 1, 2, 4); +} + +TYPED_TEST(ForwardListTest, RemoveIfMissing) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + list.remove_if([](const auto& x) { return *x == 5; }); + EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, RemoveIfAll) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + list.remove_if([](const auto& x) { return true; }); + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, RemoveIfEmpty) { + using List = TypeParam; + List list; + list.remove_if([](const auto& x) { return *x == 3; }); + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, RemoveIfAllEmpty) { + using List = TypeParam; + List list; + list.remove_if([](const auto& x) { return true; }); + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, InsertAfter) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto it = std::next(list.begin(), 2); + ASSERT_THAT(**it, 3); + typename List::value_type value(5); + auto result = list.insert_after(it, value); + EXPECT_THAT(result, std::next(list.begin(), 3)); + EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 5, 4); +} + +TYPED_TEST(ForwardListTest, InsertAfterFront) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto it = list.before_begin(); + typename List::value_type value(5); + auto result = list.insert_after(it, value); + EXPECT_THAT(result, list.begin()); + EXPECT_ELEMENTS_ARE(list, 5, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, InsertAfterFrontEmpty) { + using List = TypeParam; + List list; + auto it = list.before_begin(); + typename List::value_type value(5); + auto result = list.insert_after(it, value); + EXPECT_THAT(result, list.begin()); + EXPECT_ELEMENTS_ARE(list, 5); +} + +TYPED_TEST(ForwardListTest, InsertAfterRange) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto it = std::next(list.begin(), 2); + ASSERT_THAT(**it, 3); + auto otherValues = create({5, 6, 7, 8}); + auto result = list.insert_after(it, otherValues.begin(), otherValues.end()); + EXPECT_THAT(result, std::next(list.begin(), 6)); + EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 5, 6, 7, 8, 4); +} + +TYPED_TEST(ForwardListTest, InsertAfterEmptyRange) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto it = std::next(list.begin(), 2); + ASSERT_THAT(**it, 3); + auto otherValues = create({}); + auto result = list.insert_after(it, otherValues.begin(), otherValues.end()); + EXPECT_THAT(result, std::next(list.begin(), 2)); + EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, InsertAfterRangeFront) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto it = list.before_begin(); + auto otherValues = create({5, 6, 7, 8}); + auto result = list.insert_after(it, otherValues.begin(), otherValues.end()); + EXPECT_THAT(result, std::next(list.begin(), 3)); + EXPECT_ELEMENTS_ARE(list, 5, 6, 7, 8, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, InsertAfterEmptyRangeFront) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto it = list.before_begin(); + auto otherValues = create({}); + auto result = list.insert_after(it, otherValues.begin(), otherValues.end()); + EXPECT_THAT(result, list.before_begin()); + EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, InsertAfterRangeFrontEmpty) { + using List = TypeParam; + List list; + auto it = list.before_begin(); + auto otherValues = create({5, 6, 7, 8}); + auto result = list.insert_after(it, otherValues.begin(), otherValues.end()); + EXPECT_THAT(result, std::next(list.begin(), 3)); + EXPECT_ELEMENTS_ARE(list, 5, 6, 7, 8); +} + +TYPED_TEST(ForwardListTest, InsertAfterEmptyRangeFrontEmpty) { + using List = TypeParam; + List list; + auto it = list.before_begin(); + auto otherValues = create({}); + auto result = list.insert_after(it, otherValues.begin(), otherValues.end()); + EXPECT_THAT(result, list.before_begin()); + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, EraseAfter) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto it = std::next(list.begin(), 1); + ASSERT_THAT(**it, 2); + auto result = list.erase_after(it); + EXPECT_THAT(result, std::next(list.begin(), 2)); + EXPECT_ELEMENTS_ARE(list, 1, 2, 4); +} + +TYPED_TEST(ForwardListTest, EraseAfterNearEnd) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto it = std::next(list.begin(), 2); + ASSERT_THAT(**it, 3); + auto result = list.erase_after(it); + EXPECT_THAT(result, list.end()); + EXPECT_ELEMENTS_ARE(list, 1, 2, 3); +} + +TYPED_TEST(ForwardListTest, EraseAfterFront) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto it = list.before_begin(); + auto result = list.erase_after(it); + EXPECT_THAT(result, list.begin()); + EXPECT_ELEMENTS_ARE(list, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, EraseAfterToEmpty) { + using List = TypeParam; + auto values = create({1}); + List list(values.begin(), values.end()); + auto it = list.before_begin(); + auto result = list.erase_after(it); + EXPECT_THAT(result, list.end()); + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, EraseAfterRange) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto it = std::next(list.begin(), 2); + ASSERT_THAT(**it, 3); + auto result = list.erase_after(list.begin(), it); + EXPECT_THAT(result, std::next(list.begin(), 1)); + EXPECT_ELEMENTS_ARE(list, 1, 3, 4); +} + +TYPED_TEST(ForwardListTest, EraseAfterRangeToEnd) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto result = list.erase_after(list.begin(), list.end()); + EXPECT_THAT(result, list.end()); + EXPECT_ELEMENTS_ARE(list, 1); +} + +TYPED_TEST(ForwardListTest, EraseAfterEmptyRange) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto result = list.erase_after(std::next(list.begin(), 1), std::next(list.begin(), 2)); + EXPECT_THAT(result, std::next(list.begin(), 2)); + EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4); +} + +TYPED_TEST(ForwardListTest, EraseAfterRangeFront) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto it = std::next(list.begin(), 2); + ASSERT_THAT(**it, 3); + auto result = list.erase_after(list.before_begin(), it); + EXPECT_THAT(result, list.begin()); + EXPECT_ELEMENTS_ARE(list, 3, 4); +} + +TYPED_TEST(ForwardListTest, EraseAfterRangeFrontToEnd) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto result = list.erase_after(list.before_begin(), list.end()); + EXPECT_THAT(result, list.end()); + EXPECT_ELEMENTS_ARE(list); +} + +TYPED_TEST(ForwardListTest, EraseAfterEmptyRangeFront) { + using List = TypeParam; + auto values = create({1, 2, 3, 4}); + List list(values.begin(), values.end()); + auto result = list.erase_after(list.before_begin(), list.begin()); + EXPECT_THAT(result, list.begin()); + EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4); +}