summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/intrusive-list.h625
-rw-r--r--src/test-intrusive-list.cc585
2 files changed, 1210 insertions, 0 deletions
diff --git a/src/intrusive-list.h b/src/intrusive-list.h
new file mode 100644
index 00000000..290a0483
--- /dev/null
+++ b/src/intrusive-list.h
@@ -0,0 +1,625 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WABT_INTRUSIVE_LIST_H_
+#define WABT_INTRUSIVE_LIST_H_
+
+#include <cassert>
+#include <iterator>
+
+// This uses a similar interface as std::list, but is missing the following
+// features:
+//
+// * Add "extract_" functions that remove an element from the list and return
+// it.
+// * Only supports move-only operations
+// * No allocator support
+// * No initializer lists
+// * Asserts instead of exceptions
+// * Some functions are not implemented (merge, remove, remove_if, reverse,
+// unique, sort, non-member comparison operators)
+
+namespace wabt {
+
+template <typename T>
+class intrusive_list;
+
+template <typename T>
+class intrusive_list_base {
+ private:
+ friend class intrusive_list<T>;
+
+ mutable T* next_ = nullptr;
+ mutable T* prev_ = nullptr;
+};
+
+template <typename T>
+class intrusive_list {
+ public:
+ // types:
+ typedef T value_type;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ class iterator;
+ class const_iterator;
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ // construct/copy/destroy:
+ intrusive_list();
+ explicit intrusive_list(T* node);
+ explicit intrusive_list(T&& node);
+ intrusive_list(const intrusive_list&) = delete;
+ intrusive_list(intrusive_list&&);
+ ~intrusive_list();
+ intrusive_list& operator=(const intrusive_list& other) = delete;
+ intrusive_list& operator=(intrusive_list&& other);
+
+ // iterators:
+ iterator begin() noexcept;
+ const_iterator begin() const noexcept;
+ iterator end() noexcept;
+ const_iterator end() const noexcept;
+
+ reverse_iterator rbegin() noexcept;
+ const_reverse_iterator rbegin() const noexcept;
+ reverse_iterator rend() noexcept;
+ const_reverse_iterator rend() const noexcept;
+
+ const_iterator cbegin() const noexcept;
+ const_iterator cend() const noexcept;
+ const_reverse_iterator crbegin() const noexcept;
+ const_reverse_iterator crend() const noexcept;
+
+ // capacity:
+ size_type size() const noexcept;
+ bool empty() const noexcept;
+
+ // element access:
+ reference front();
+ const_reference front() const;
+ reference back();
+ const_reference back() const;
+
+ // modifiers:
+ template <class... Args>
+ void emplace_front(Args&&... args);
+ template <class... Args>
+ void emplace_back(Args&&... args);
+ void push_front(T* node);
+ void push_front(T&& node);
+ void push_back(T* node);
+ void push_back(T&& node);
+ void pop_front();
+ void pop_back();
+ T* extract_front();
+ T* extract_back();
+
+ template <class... Args>
+ iterator emplace(iterator pos, Args&&... args);
+ iterator insert(iterator pos, T* node);
+ iterator insert(iterator pos, T&& node);
+ T* extract(iterator it);
+
+ iterator erase(iterator pos);
+ iterator erase(iterator first, iterator last);
+ void swap(intrusive_list&);
+ void clear() noexcept;
+
+ void splice(iterator pos, intrusive_list& node);
+ void splice(iterator pos, intrusive_list&& node);
+ void splice(iterator pos, intrusive_list& node, iterator it);
+ void splice(iterator pos,
+ intrusive_list& node,
+ iterator first,
+ iterator last);
+
+ private:
+ T* first_ = nullptr;
+ T* last_ = nullptr;
+ size_t size_ = 0;
+};
+
+/// iterator
+template <typename T>
+class intrusive_list<T>::iterator {
+ public:
+ typedef std::ptrdiff_t difference_type;
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef T value_type;
+ typedef T* pointer;
+ typedef T& reference;
+
+ iterator(const intrusive_list<T>& list, T* node)
+ : list_(&list), node_(node) {}
+
+ reference operator*() const {
+ assert(node_);
+ return *node_;
+ }
+
+ pointer operator->() const {
+ assert(node_);
+ return node_;
+ }
+
+ iterator& operator++() {
+ assert(node_);
+ node_ = node_->next_;
+ return *this;
+ }
+
+ iterator operator++(int) {
+ iterator tmp = *this;
+ operator++();
+ return tmp;
+ }
+
+ iterator& operator--() {
+ node_ = node_ ? node_->prev_ : list_->last_;
+ return *this;
+ }
+
+ iterator operator--(int) {
+ iterator tmp = *this;
+ operator--();
+ return tmp;
+ }
+
+ bool operator==(iterator rhs) const {
+ assert(list_ == rhs.list_);
+ return node_ == rhs.node_;
+ }
+
+ bool operator!=(iterator rhs) const {
+ assert(list_ == rhs.list_);
+ return node_ != rhs.node_;
+ }
+
+ private:
+ friend class const_iterator;
+
+ const intrusive_list<T>* list_;
+ T* node_;
+};
+
+/// const_iterator
+template <typename T>
+class intrusive_list<T>::const_iterator {
+ public:
+ typedef std::ptrdiff_t difference_type;
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef T value_type;
+ typedef const T* pointer;
+ typedef const T& reference;
+
+ const_iterator(const intrusive_list<T>& list, T* node)
+ : list_(&list), node_(node) {}
+
+ const_iterator(const iterator& other)
+ : list_(other.list_), node_(other.node_) {}
+
+ reference operator*() const {
+ assert(node_);
+ return *node_;
+ }
+
+ pointer operator->() const {
+ assert(node_);
+ return node_;
+ }
+
+ const_iterator& operator++() {
+ assert(node_);
+ node_ = node_->next_;
+ return *this;
+ }
+
+ const_iterator operator++(int) {
+ const_iterator tmp = *this;
+ operator++();
+ return tmp;
+ }
+
+ const_iterator& operator--() {
+ node_ = node_ ? node_->prev_ : list_->last_;
+ return *this;
+ }
+
+ const_iterator operator--(int) {
+ const_iterator tmp = *this;
+ operator--();
+ return tmp;
+ }
+
+ bool operator==(const_iterator rhs) const {
+ assert(list_ == rhs.list_);
+ return node_ == rhs.node_;
+ }
+
+ bool operator!=(const_iterator rhs) const {
+ assert(list_ == rhs.list_);
+ return node_ != rhs.node_;
+ }
+
+ private:
+ const intrusive_list<T>* list_;
+ T* node_;
+};
+
+template <typename T>
+inline intrusive_list<T>::intrusive_list() {}
+
+template <typename T>
+inline intrusive_list<T>::intrusive_list(T* node) {
+ push_back(node);
+}
+
+template <typename T>
+inline intrusive_list<T>::intrusive_list(T&& node) {
+ push_back(std::move(node));
+}
+
+template <typename T>
+inline intrusive_list<T>::intrusive_list(intrusive_list&& other)
+ : first_(other.first_), last_(other.last_), size_(other.size_) {
+ other.first_ = other.last_ = nullptr;
+ other.size_ = 0;
+}
+
+template <typename T>
+inline intrusive_list<T>::~intrusive_list() {
+ clear();
+}
+
+template <typename T>
+inline intrusive_list<T>& intrusive_list<T>::operator=(
+ intrusive_list<T>&& other) {
+ clear();
+ first_ = other.first_;
+ last_ = other.last_;
+ size_ = other.size_;
+ other.first_ = other.last_ = nullptr;
+ other.size_ = 0;
+ return *this;
+}
+
+template <typename T>
+inline typename intrusive_list<T>::iterator
+intrusive_list<T>::begin() noexcept {
+ return iterator(*this, first_);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_iterator intrusive_list<T>::begin()
+ const noexcept {
+ return const_iterator(*this, first_);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::iterator intrusive_list<T>::end() noexcept {
+ return iterator(*this, nullptr);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_iterator intrusive_list<T>::end() const
+ noexcept {
+ return const_iterator(*this, nullptr);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::reverse_iterator
+intrusive_list<T>::rbegin() noexcept {
+ return reverse_iterator(iterator(*this, nullptr));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_reverse_iterator
+intrusive_list<T>::rbegin() const noexcept {
+ return const_reverse_iterator(const_iterator(*this, nullptr));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::reverse_iterator
+intrusive_list<T>::rend() noexcept {
+ return reverse_iterator(iterator(*this, first_));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_reverse_iterator
+intrusive_list<T>::rend() const noexcept {
+ return const_reverse_iterator(const_iterator(*this, first_));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_iterator intrusive_list<T>::cbegin()
+ const noexcept {
+ return const_iterator(*this, first_);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_iterator intrusive_list<T>::cend()
+ const noexcept {
+ return const_iterator(*this, nullptr);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_reverse_iterator
+intrusive_list<T>::crbegin() const noexcept {
+ return const_reverse_iterator(const_iterator(*this, nullptr));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_reverse_iterator
+intrusive_list<T>::crend() const noexcept {
+ return const_reverse_iterator(const_iterator(*this, first_));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::size_type intrusive_list<T>::size() const
+ noexcept {
+ return size_;
+}
+
+template <typename T>
+inline bool intrusive_list<T>::empty() const noexcept {
+ return size_ == 0;
+}
+
+template <typename T>
+inline typename intrusive_list<T>::reference intrusive_list<T>::front() {
+ assert(!empty());
+ return *first_;
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_reference intrusive_list<T>::front()
+ const {
+ assert(!empty());
+ return *first_;
+}
+
+template <typename T>
+inline typename intrusive_list<T>::reference intrusive_list<T>::back() {
+ assert(!empty());
+ return *last_;
+}
+
+template <typename T>
+inline typename intrusive_list<T>::const_reference intrusive_list<T>::back()
+ const {
+ assert(!empty());
+ return *last_;
+}
+
+template <typename T>
+template <class... Args>
+inline void intrusive_list<T>::emplace_front(Args&&... args) {
+ push_front(new T(args...));
+}
+
+template <typename T>
+template <class... Args>
+inline void intrusive_list<T>::emplace_back(Args&&... args) {
+ push_back(new T(args...));
+}
+
+template <typename T>
+inline void intrusive_list<T>::push_front(T* node) {
+ assert(node->prev_ == nullptr &&
+ node->next_ == nullptr);
+
+ if (first_) {
+ node->next_ = first_;
+ first_->prev_ = node;
+ } else {
+ last_ = node;
+ }
+ first_ = node;
+ size_++;
+}
+
+template <typename T>
+inline void intrusive_list<T>::push_front(T&& node) {
+ push_front(new T(std::move(node)));
+}
+
+template <typename T>
+inline void intrusive_list<T>::push_back(T* node) {
+ assert(node->prev_ == nullptr &&
+ node->next_ == nullptr);
+
+ if (last_) {
+ node->prev_ = last_;
+ last_->next_ = node;
+ } else {
+ first_ = node;
+ }
+ last_ = node;
+ size_++;
+}
+
+template <typename T>
+inline void intrusive_list<T>::push_back(T&& node) {
+ push_back(new T(std::move(node)));
+}
+
+template <typename T>
+inline void intrusive_list<T>::pop_front() {
+ delete extract_front();
+}
+
+template <typename T>
+inline void intrusive_list<T>::pop_back() {
+ delete extract_back();
+}
+
+template <typename T>
+inline T* intrusive_list<T>::extract_front() {
+ assert(!empty());
+ T* node = first_;
+ if (first_ == last_) {
+ first_ = last_ = nullptr;
+ } else {
+ first_ = first_->next_;
+ first_->prev_ = nullptr;
+ }
+ node->next_ = node->prev_ = nullptr;
+ size_--;
+ return node;
+}
+
+template <typename T>
+inline T* intrusive_list<T>::extract_back() {
+ assert(!empty());
+ T* node = last_;
+ if (first_ == last_) {
+ first_ = last_ = nullptr;
+ } else {
+ last_ = last_->prev_;
+ last_->next_ = nullptr;
+ }
+ node->next_ = node->prev_ = nullptr;
+ size_--;
+ return node;
+}
+
+template <typename T>
+template <class... Args>
+inline typename intrusive_list<T>::iterator intrusive_list<T>::emplace(
+ iterator pos,
+ Args&&... args) {
+ return insert(pos, new T(args...));
+}
+
+template <typename T>
+inline typename intrusive_list<T>::iterator intrusive_list<T>::insert(
+ iterator pos,
+ T* node) {
+ assert(node->prev_ == nullptr &&
+ node->next_ == nullptr);
+
+ if (pos == end()) {
+ push_back(node);
+ } else {
+ node->prev_ = pos->prev_;
+ node->next_ = &*pos;
+ if (pos->prev_)
+ pos->prev_->next_ = node;
+ else
+ first_ = node;
+ pos->prev_ = node;
+ size_++;
+ }
+ return iterator(*this, node);
+}
+
+template <typename T>
+inline typename intrusive_list<T>::iterator intrusive_list<T>::insert(
+ iterator pos,
+ T&& node) {
+ return insert(pos, new T(std::move(node)));
+}
+
+template <typename T>
+inline T* intrusive_list<T>::extract(iterator pos) {
+ assert(!empty());
+ assert(pos != end());
+ T* node = &*pos;
+ if (first_ == last_) {
+ first_ = last_ = nullptr;
+ } else {
+ if (node->prev_)
+ node->prev_->next_ = node->next_;
+ else
+ first_ = node->next_;
+
+ if (node->next_)
+ node->next_->prev_ = node->prev_;
+ else
+ last_ = node->prev_;
+ }
+ node->next_ = node->prev_ = nullptr;
+ size_--;
+ return node;
+}
+
+template <typename T>
+inline typename intrusive_list<T>::iterator intrusive_list<T>::erase(
+ iterator pos) {
+ iterator next = std::next(pos);
+ delete extract(pos);
+ return next;
+}
+
+template <typename T>
+inline typename intrusive_list<T>::iterator intrusive_list<T>::erase(
+ iterator first,
+ iterator last) {
+ while (first != last)
+ first = erase(first);
+ return first;
+}
+
+template <typename T>
+inline void intrusive_list<T>::swap(intrusive_list& other) {
+ std::swap(first_, other.first_);
+ std::swap(last_, other.last_);
+ std::swap(size_, other.size_);
+}
+
+template <typename T>
+inline void intrusive_list<T>::clear() noexcept {
+ for (T* iter = first_; iter;) {
+ T* next = iter->next_;
+ delete iter;
+ iter = next;
+ }
+ first_ = last_ = nullptr;
+ size_ = 0;
+}
+
+template <typename T>
+inline void intrusive_list<T>::splice(iterator pos, intrusive_list& other) {
+ splice(pos, other, other.begin(), other.end());
+}
+
+template <typename T>
+inline void intrusive_list<T>::splice(iterator pos, intrusive_list&& other) {
+ splice(pos, other, other.begin(), other.end());
+}
+
+template <typename T>
+inline void intrusive_list<T>::splice(iterator pos,
+ intrusive_list& other,
+ iterator it) {
+ insert(pos, other.extract(it));
+}
+
+template <typename T>
+inline void intrusive_list<T>::splice(iterator pos,
+ intrusive_list& other,
+ iterator first,
+ iterator last) {
+ while (first != last)
+ insert(pos, other.extract(first++));
+}
+
+} // namespace wabt
+
+#endif // WABT_INTRUSIVE_LIST_H_
diff --git a/src/test-intrusive-list.cc b/src/test-intrusive-list.cc
new file mode 100644
index 00000000..734fb6e8
--- /dev/null
+++ b/src/test-intrusive-list.cc
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "gtest/gtest.h"
+
+#include <memory>
+
+#include "intrusive-list.h"
+
+using namespace wabt;
+
+namespace {
+
+struct TestObject : intrusive_list_base<TestObject> {
+ static int creation_count;
+
+ TestObject(int data = 0, int data2 = 0) : data(data), data2(data2) {
+ ++creation_count;
+ }
+
+ // Allow move.
+ TestObject(TestObject&& other) {
+ // Don't increment creation_count; we're moving from other.
+ *this = std::move(other);
+ }
+
+ TestObject& operator=(TestObject&& other) {
+ data = other.data;
+ data2 = other.data2;
+ other.moved = true;
+ return *this;
+ }
+
+ // Disallow copy.
+ TestObject(const TestObject&) = delete;
+ TestObject& operator=(const TestObject&) = delete;
+
+ ~TestObject() {
+ if (!moved)
+ creation_count--;
+ }
+
+ int data;
+ int data2;
+ bool moved = false;
+};
+
+// static
+int TestObject::creation_count = 0;
+
+typedef intrusive_list<TestObject> TestObjectList;
+
+class IntrusiveListTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Reset to 0 even if the previous test leaked objects to keep the tests
+ // independent.
+ TestObject::creation_count = 0;
+ }
+
+ virtual void TearDown() {
+ ASSERT_EQ(0, TestObject::creation_count);
+ }
+
+ TestObjectList NewList(const std::vector<int>& data_values) {
+ TestObjectList result;
+ for (auto data_value : data_values)
+ result.emplace_back(data_value);
+ return result;
+ }
+
+ void AssertListEq(const TestObjectList& list,
+ const std::vector<int>& expected) {
+ size_t count = 0;
+ for (const TestObject& node : list) {
+ ASSERT_EQ(expected[count++], node.data);
+ }
+ ASSERT_EQ(count, expected.size());
+ }
+
+ void AssertListEq(const TestObjectList& list,
+ const std::vector<int>& expected,
+ const std::vector<int>& expected2) {
+ assert(expected.size() == expected2.size());
+ size_t count = 0;
+ for (const TestObject& node : list) {
+ ASSERT_EQ(expected[count], node.data);
+ ASSERT_EQ(expected2[count], node.data2);
+ count++;
+ }
+ ASSERT_EQ(count, expected.size());
+ }
+};
+
+} // end anonymous namespace
+
+TEST_F(IntrusiveListTest, default_constructor) {
+ TestObjectList list;
+}
+
+TEST_F(IntrusiveListTest, node_constructor) {
+ TestObjectList list(new TestObject(1));
+ AssertListEq(list, {1});
+}
+
+TEST_F(IntrusiveListTest, node_move_constructor) {
+ TestObjectList list(TestObject(1));
+ AssertListEq(list, {1});
+}
+
+TEST_F(IntrusiveListTest, move_constructor) {
+ TestObjectList list1 = NewList({1, 2, 3});
+ TestObjectList list2(std::move(list1));
+ AssertListEq(list1, {});
+ AssertListEq(list2, {1, 2, 3});
+}
+
+TEST_F(IntrusiveListTest, move_assignment_operator) {
+ TestObjectList list1 = NewList({1, 2, 3});
+ TestObjectList list2;
+
+ list2 = std::move(list1);
+ AssertListEq(list1, {});
+ AssertListEq(list2, {1, 2, 3});
+}
+
+namespace {
+
+class IntrusiveListIteratorTest : public IntrusiveListTest {
+ protected:
+ virtual void SetUp() {
+ IntrusiveListTest::SetUp();
+
+ list_.emplace_back(1);
+ list_.emplace_back(2);
+ list_.emplace_back(3);
+ }
+
+ virtual void TearDown() {
+ list_.clear();
+
+ IntrusiveListTest::TearDown();
+ }
+
+ template <typename Iter>
+ void TestForward(Iter first, Iter last, const std::vector<int>& expected) {
+ size_t count = 0;
+ while (first != last) {
+ ASSERT_EQ(expected[count], first->data);
+ ++first;
+ ++count;
+ }
+ ASSERT_EQ(count, expected.size());
+ }
+
+ template <typename Iter>
+ void TestBackward(Iter first, Iter last, const std::vector<int>& expected) {
+ size_t count = 0;
+ while (first != last) {
+ --last;
+ ASSERT_EQ(expected[count], last->data);
+ ++count;
+ }
+ ASSERT_EQ(count, expected.size());
+ }
+
+
+ TestObjectList list_;
+ const TestObjectList& clist_ = list_;
+};
+
+} // end of anonymous namespace
+
+TEST_F(IntrusiveListIteratorTest, begin_end_forward) {
+ TestForward(list_.begin(), list_.end(), {1, 2, 3});
+ TestForward(clist_.begin(), clist_.end(), {1, 2, 3});
+}
+
+TEST_F(IntrusiveListIteratorTest, rbegin_rend_forward) {
+ TestForward(list_.rbegin(), list_.rend(), {3, 2, 1});
+ TestForward(clist_.rbegin(), clist_.rend(), {3, 2, 1});
+}
+
+TEST_F(IntrusiveListIteratorTest, cbegin_cend_forward) {
+ TestForward(list_.cbegin(), list_.cend(), {1, 2, 3});
+ TestForward(clist_.cbegin(), clist_.cend(), {1, 2, 3});
+}
+
+TEST_F(IntrusiveListIteratorTest, crbegin_crend_forward) {
+ TestForward(list_.crbegin(), list_.crend(), {3, 2, 1});
+ TestForward(clist_.crbegin(), clist_.crend(), {3, 2, 1});
+}
+
+TEST_F(IntrusiveListIteratorTest, begin_end_backward) {
+ TestBackward(list_.begin(), list_.end(), {3, 2, 1});
+ TestBackward(clist_.begin(), clist_.end(), {3, 2, 1});
+}
+
+TEST_F(IntrusiveListIteratorTest, rbegin_rend_backward) {
+ TestBackward(list_.rbegin(), list_.rend(), {1, 2, 3});
+ TestBackward(clist_.rbegin(), clist_.rend(), {1, 2, 3});
+}
+
+TEST_F(IntrusiveListIteratorTest, cbegin_cend_backward) {
+ TestBackward(list_.cbegin(), list_.cend(), {3, 2, 1});
+ TestBackward(clist_.cbegin(), clist_.cend(), {3, 2, 1});
+}
+
+TEST_F(IntrusiveListIteratorTest, crbegin_crend_backward) {
+ TestBackward(list_.crbegin(), list_.crend(), {1, 2, 3});
+ TestBackward(clist_.crbegin(), clist_.crend(), {1, 2, 3});
+}
+
+TEST_F(IntrusiveListTest, size_empty) {
+ TestObjectList list;
+ ASSERT_EQ(0U, list.size());
+ ASSERT_TRUE(list.empty());
+
+ list.emplace_back(1);
+ ASSERT_EQ(1U, list.size());
+ ASSERT_FALSE(list.empty());
+}
+
+TEST_F(IntrusiveListTest, front_back) {
+ TestObjectList list;
+
+ list.emplace_back(1);
+ ASSERT_EQ(1, list.front().data);
+ ASSERT_EQ(1, list.back().data);
+
+ list.emplace_back(2);
+ ASSERT_EQ(1, list.front().data);
+ ASSERT_EQ(2, list.back().data);
+
+ const TestObjectList& clist = list;
+ ASSERT_EQ(1, clist.front().data);
+ ASSERT_EQ(2, clist.back().data);
+}
+
+TEST_F(IntrusiveListTest, emplace_front) {
+ TestObjectList list;
+
+ // Pass an additional arg to show that forwarding works properly.
+ list.emplace_front(1, 100);
+ list.emplace_front(2, 200);
+ list.emplace_front(3, 300);
+ list.emplace_front(4, 400);
+
+ AssertListEq(list, {4, 3, 2, 1}, {400, 300, 200, 100});
+}
+
+TEST_F(IntrusiveListTest, emplace_back) {
+ TestObjectList list;
+
+ // Pass an additional arg to show that forwarding works properly.
+ list.emplace_back(1, 100);
+ list.emplace_back(2, 200);
+ list.emplace_back(3, 300);
+ list.emplace_back(4, 400);
+
+ AssertListEq(list, {1, 2, 3, 4}, {100, 200, 300, 400});
+}
+
+TEST_F(IntrusiveListTest, push_front_pointer) {
+ TestObjectList list;
+
+ list.push_front(new TestObject(1));
+ list.push_front(new TestObject(2));
+ list.push_front(new TestObject(3));
+
+ AssertListEq(list, {3, 2, 1});
+}
+
+TEST_F(IntrusiveListTest, push_back_pointer) {
+ TestObjectList list;
+
+ list.push_back(new TestObject(1));
+ list.push_back(new TestObject(2));
+ list.push_back(new TestObject(3));
+
+ AssertListEq(list, {1, 2, 3});
+}
+
+TEST_F(IntrusiveListTest, push_front_move) {
+ TestObjectList list;
+
+ list.push_front(TestObject(1));
+ list.push_front(TestObject(2));
+ list.push_front(TestObject(3));
+
+ AssertListEq(list, {3, 2, 1});
+}
+
+TEST_F(IntrusiveListTest, push_back_move) {
+ TestObjectList list;
+
+ list.push_back(TestObject(1));
+ list.push_back(TestObject(2));
+ list.push_back(TestObject(3));
+
+ AssertListEq(list, {1, 2, 3});
+}
+
+TEST_F(IntrusiveListTest, pop_front) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ list.pop_front();
+ AssertListEq(list, {2, 3, 4});
+ list.pop_front();
+ AssertListEq(list, {3, 4});
+ list.pop_front();
+ AssertListEq(list, {4});
+ list.pop_front();
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, pop_back) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ list.pop_back();
+ AssertListEq(list, {1, 2, 3});
+ list.pop_back();
+ AssertListEq(list, {1, 2});
+ list.pop_back();
+ AssertListEq(list, {1});
+ list.pop_back();
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, extract_front) {
+ TestObjectList list = NewList({1, 2, 3});
+
+ std::unique_ptr<TestObject> t1(list.extract_front());
+ ASSERT_EQ(1, t1->data);
+ AssertListEq(list, {2, 3});
+
+ std::unique_ptr<TestObject> t2(list.extract_front());
+ ASSERT_EQ(2, t2->data);
+ AssertListEq(list, {3});
+
+ std::unique_ptr<TestObject> t3(list.extract_front());
+ ASSERT_EQ(3, t3->data);
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, extract_back) {
+ TestObjectList list = NewList({1, 2, 3});
+
+ std::unique_ptr<TestObject> t1(list.extract_back());
+ ASSERT_EQ(3, t1->data);
+ AssertListEq(list, {1, 2});
+
+ std::unique_ptr<TestObject> t2(list.extract_back());
+ ASSERT_EQ(2, t2->data);
+ AssertListEq(list, {1});
+
+ std::unique_ptr<TestObject> t3(list.extract_back());
+ ASSERT_EQ(1, t3->data);
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, emplace) {
+ TestObjectList list;
+
+ // Pass an additional arg to show that forwarding works properly.
+ list.emplace(list.begin(), 2, 200);
+ list.emplace(list.end(), 4, 400);
+ list.emplace(std::next(list.begin()), 3, 300);
+ list.emplace(list.begin(), 1, 100);
+
+ AssertListEq(list, {1, 2, 3, 4}, {100, 200, 300, 400});
+}
+
+TEST_F(IntrusiveListTest, insert_pointer) {
+ TestObjectList list;
+
+ list.insert(list.begin(), new TestObject(2));
+ list.insert(list.end(), new TestObject(4));
+ list.insert(std::next(list.begin()), new TestObject(3));
+ list.insert(list.begin(), new TestObject(1));
+
+ AssertListEq(list, {1, 2, 3, 4});
+}
+
+TEST_F(IntrusiveListTest, insert_move) {
+ TestObjectList list;
+
+ list.insert(list.begin(), TestObject(2));
+ list.insert(list.end(), TestObject(4));
+ list.insert(std::next(list.begin()), TestObject(3));
+ list.insert(list.begin(), TestObject(1));
+
+ AssertListEq(list, {1, 2, 3, 4});
+}
+
+TEST_F(IntrusiveListTest, extract) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ TestObjectList::iterator t1_iter = std::next(list.begin(), 0);
+ TestObjectList::iterator t2_iter = std::next(list.begin(), 1);
+ TestObjectList::iterator t3_iter = std::next(list.begin(), 2);
+ TestObjectList::iterator t4_iter = std::next(list.begin(), 3);
+
+ std::unique_ptr<TestObject> t2(list.extract(t2_iter));
+ ASSERT_EQ(2, t2->data);
+ AssertListEq(list, {1, 3, 4});
+
+ std::unique_ptr<TestObject> t4(list.extract(t4_iter));
+ ASSERT_EQ(4, t4->data);
+ AssertListEq(list, {1, 3});
+
+ std::unique_ptr<TestObject> t1(list.extract(t1_iter));
+ ASSERT_EQ(1, t1->data);
+ AssertListEq(list, {3});
+
+ std::unique_ptr<TestObject> t3(list.extract(t3_iter));
+ ASSERT_EQ(3, t3->data);
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, erase) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ TestObjectList::iterator t1_iter = std::next(list.begin(), 0);
+ TestObjectList::iterator t2_iter = std::next(list.begin(), 1);
+ TestObjectList::iterator t3_iter = std::next(list.begin(), 2);
+ TestObjectList::iterator t4_iter = std::next(list.begin(), 3);
+
+ // erase returns an iterator to the following node.
+ ASSERT_EQ(3, list.erase(t2_iter)->data);
+ AssertListEq(list, {1, 3, 4});
+
+ ASSERT_EQ(list.end(), list.erase(t4_iter));
+ AssertListEq(list, {1, 3});
+
+ ASSERT_EQ(3, list.erase(t1_iter)->data);
+ AssertListEq(list, {3});
+
+ ASSERT_EQ(list.end(), list.erase(t3_iter));
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, erase_range) {
+ TestObjectList list = NewList({1, 2, 3, 4, 5, 6});
+
+ // OK to erase an empty range.
+ list.erase(list.begin(), list.begin());
+ list.erase(list.end(), list.end());
+
+ // Erase the first element (1).
+ list.erase(list.begin(), std::next(list.begin()));
+ AssertListEq(list, {2, 3, 4, 5, 6});
+
+ // Erase the last element (6).
+ list.erase(std::prev(list.end()), list.end());
+ AssertListEq(list, {2, 3, 4, 5});
+
+ // Erase [3, 4] => [2, 5]
+ list.erase(std::next(list.begin()), std::prev(list.end()));
+ AssertListEq(list, {2, 5});
+
+ // Erase the rest.
+ list.erase(list.begin(), list.end());
+ AssertListEq(list, {});
+}
+
+TEST_F(IntrusiveListTest, swap) {
+ TestObjectList list1 = NewList({1, 2, 3, 4});
+
+ TestObjectList list2 = NewList({100, 200, 300});
+
+ AssertListEq(list1, {1, 2, 3, 4});
+ AssertListEq(list2, {100, 200, 300});
+
+ list1.swap(list2);
+
+ AssertListEq(list1, {100, 200, 300});
+ AssertListEq(list2, {1, 2, 3, 4});
+}
+
+TEST_F(IntrusiveListTest, clear) {
+ TestObjectList list = NewList({1, 2, 3, 4});
+
+ ASSERT_FALSE(list.empty());
+
+ list.clear();
+
+ ASSERT_EQ(0U, list.size());
+ ASSERT_TRUE(list.empty());
+}
+
+TEST_F(IntrusiveListTest, splice_list) {
+ TestObjectList list1 = NewList({1, 2, 3});
+
+ // Splice at beginning.
+ TestObjectList list2 = NewList({100, 200});
+ list1.splice(list1.begin(), list2);
+ AssertListEq(list1, {100, 200, 1, 2, 3});
+ AssertListEq(list2, {});
+
+ // Splice at end.
+ TestObjectList list3 = NewList({500, 600, 700});
+ list1.splice(list1.end(), list3);
+ AssertListEq(list1, {100, 200, 1, 2, 3, 500, 600, 700});
+ AssertListEq(list3, {});
+
+ // Splice in the middle.
+ TestObjectList list4 = NewList({400});
+ list1.splice(std::next(list1.begin(), 4), list4);
+ AssertListEq(list1, {100, 200, 1, 2, 400, 3, 500, 600, 700});
+ AssertListEq(list4, {});
+}
+
+TEST_F(IntrusiveListTest, splice_list_move) {
+ TestObjectList list1 = NewList({1, 2, 3});
+
+ // Splice at beginning.
+ list1.splice(list1.begin(), NewList({100, 200}));
+ AssertListEq(list1, {100, 200, 1, 2, 3});
+
+ // Splice at end.
+ list1.splice(list1.end(), NewList({500, 600, 700}));
+ AssertListEq(list1, {100, 200, 1, 2, 3, 500, 600, 700});
+
+ // Splice in the middle.
+ list1.splice(std::next(list1.begin(), 4), NewList({400}));
+ AssertListEq(list1, {100, 200, 1, 2, 400, 3, 500, 600, 700});
+}
+
+TEST_F(IntrusiveListTest, splice_node) {
+ TestObjectList list1 = NewList({1, 2, 3});
+
+ // Splice at beginning.
+ TestObjectList list2 = NewList({100, 200});
+ list1.splice(list1.begin(), list2, list2.begin());
+ AssertListEq(list1, {100, 1, 2, 3});
+ AssertListEq(list2, {200});
+
+ // Splice at end.
+ TestObjectList list3 = NewList({500, 600, 700});
+ list1.splice(list1.end(), list3, std::next(list3.begin(), 2));
+ AssertListEq(list1, {100, 1, 2, 3, 700});
+ AssertListEq(list3, {500, 600});
+
+ // Splice in the middle.
+ TestObjectList list4 = NewList({400});
+ list1.splice(std::next(list1.begin(), 3), list4, list4.begin());
+ AssertListEq(list1, {100, 1, 2, 400, 3, 700});
+ AssertListEq(list4, {});
+}
+
+TEST_F(IntrusiveListTest, splice_range) {
+ TestObjectList list1 = NewList({1, 2, 3});
+
+ // Splice at beginning.
+ TestObjectList list2 = NewList({100, 200, 300});
+ list1.splice(list1.begin(), list2, list2.begin(), std::prev(list2.end()));
+ AssertListEq(list1, {100, 200, 1, 2, 3});
+ AssertListEq(list2, {300});
+
+ // Splice at end.
+ TestObjectList list3 = NewList({500, 600, 700});
+ list1.splice(list1.end(), list3, std::next(list3.begin()), list3.end());
+ AssertListEq(list1, {100, 200, 1, 2, 3, 600, 700});
+ AssertListEq(list3, {500});
+
+ // Splice in the middle.
+ TestObjectList list4 = NewList({400});
+ list1.splice(std::next(list1.begin(), 4), list4, list4.begin(), list4.end());
+ AssertListEq(list1, {100, 200, 1, 2, 400, 3, 600, 700});
+ AssertListEq(list4, {});
+}