780 lines
25 KiB
C++
780 lines
25 KiB
C++
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* 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 <algorithm>
|
|
#include <forward_list>
|
|
#include <vector>
|
|
|
|
#include "gtest/gtest.h"
|
|
#include "intrusive_forward_list.h"
|
|
|
|
namespace art {
|
|
|
|
struct IFLTestValue : public IntrusiveForwardListNode<IFLTestValue> {
|
|
// Deliberately not explicit.
|
|
IFLTestValue(int v) : value(v) { } // NOLINT(runtime/explicit)
|
|
|
|
int value;
|
|
};
|
|
using IFLTestValueList = IntrusiveForwardList<IFLTestValue>;
|
|
using ConstIFLTestValueList = IntrusiveForwardList<const IFLTestValue>;
|
|
|
|
bool operator==(const IFLTestValue& lhs, const IFLTestValue& rhs) {
|
|
return lhs.value == rhs.value;
|
|
}
|
|
|
|
bool operator<(const IFLTestValue& lhs, const IFLTestValue& rhs) {
|
|
return lhs.value < rhs.value;
|
|
}
|
|
|
|
struct IFLTestValue2 {
|
|
// Deliberately not explicit.
|
|
IFLTestValue2(int v) : hook(), value(v) { } // NOLINT(runtime/explicit)
|
|
|
|
IntrusiveForwardListHook hook;
|
|
int value;
|
|
};
|
|
using IFLTestValue2List =
|
|
IntrusiveForwardList<IFLTestValue2, IntrusiveForwardListMemberHookTraits<IFLTestValue2>>;
|
|
|
|
bool operator==(const IFLTestValue2& lhs, const IFLTestValue2& rhs) {
|
|
return lhs.value == rhs.value;
|
|
}
|
|
|
|
bool operator<(const IFLTestValue2& lhs, const IFLTestValue2& rhs) {
|
|
return lhs.value < rhs.value;
|
|
}
|
|
|
|
#define ASSERT_LISTS_EQUAL(expected, value) \
|
|
do { \
|
|
ASSERT_EQ((expected).empty(), (value).empty()); \
|
|
ASSERT_EQ(std::distance((expected).begin(), (expected).end()), \
|
|
std::distance((value).begin(), (value).end())); \
|
|
ASSERT_TRUE(std::equal((expected).begin(), (expected).end(), (value).begin())); \
|
|
} while (false)
|
|
|
|
class IntrusiveForwardListTest : public testing::Test {
|
|
public:
|
|
template <typename ListType>
|
|
void IteratorToConstIterator();
|
|
|
|
template <typename ListType>
|
|
void IteratorOperators();
|
|
|
|
template <typename ListType>
|
|
void ConstructRange();
|
|
|
|
template <typename ListType>
|
|
void Assign();
|
|
|
|
template <typename ListType>
|
|
void PushPop();
|
|
|
|
template <typename ListType>
|
|
void InsertAfter1();
|
|
|
|
template <typename ListType>
|
|
void InsertAfter2();
|
|
|
|
template <typename ListType>
|
|
void EraseAfter1();
|
|
|
|
template <typename ListType>
|
|
void EraseAfter2();
|
|
|
|
template <typename ListType>
|
|
void SwapClear();
|
|
|
|
template <typename ListType>
|
|
void SpliceAfter();
|
|
|
|
template <typename ListType>
|
|
void Remove();
|
|
|
|
template <typename ListType>
|
|
void Unique();
|
|
|
|
template <typename ListType>
|
|
void Merge();
|
|
|
|
template <typename ListType>
|
|
void Sort1();
|
|
|
|
template <typename ListType>
|
|
void Sort2();
|
|
|
|
template <typename ListType>
|
|
void Reverse();
|
|
|
|
template <typename ListType>
|
|
void ModifyValue();
|
|
};
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::IteratorToConstIterator() {
|
|
ListType ifl;
|
|
typename ListType::iterator begin = ifl.begin();
|
|
typename ListType::const_iterator cbegin = ifl.cbegin();
|
|
typename ListType::const_iterator converted_begin = begin;
|
|
ASSERT_TRUE(converted_begin == cbegin);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, IteratorToConstIterator) {
|
|
IteratorToConstIterator<IFLTestValueList>();
|
|
IteratorToConstIterator<ConstIFLTestValueList>();
|
|
IteratorToConstIterator<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::IteratorOperators() {
|
|
using ValueType = typename ListType::value_type;
|
|
ListType ifl;
|
|
ASSERT_TRUE(ifl.begin() == ifl.cbegin());
|
|
ASSERT_FALSE(ifl.begin() != ifl.cbegin());
|
|
ASSERT_TRUE(ifl.end() == ifl.cend());
|
|
ASSERT_FALSE(ifl.end() != ifl.cend());
|
|
|
|
ASSERT_TRUE(ifl.begin() == ifl.end()); // Empty.
|
|
ASSERT_FALSE(ifl.begin() != ifl.end()); // Empty.
|
|
|
|
ValueType value(1);
|
|
ifl.insert_after(ifl.cbefore_begin(), value);
|
|
|
|
ASSERT_FALSE(ifl.begin() == ifl.end()); // Not empty.
|
|
ASSERT_TRUE(ifl.begin() != ifl.end()); // Not empty.
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, IteratorOperators) {
|
|
IteratorOperators<IFLTestValueList>();
|
|
IteratorOperators<ConstIFLTestValueList>();
|
|
IteratorOperators<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::ConstructRange() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref({ 1, 2, 7 });
|
|
std::vector<ValueType> storage(ref.begin(), ref.end());
|
|
ListType ifl(storage.begin(), storage.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, ConstructRange) {
|
|
ConstructRange<IFLTestValueList>();
|
|
ConstructRange<ConstIFLTestValueList>();
|
|
ConstructRange<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::Assign() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref1({ 2, 8, 5 });
|
|
std::vector<ValueType> storage1(ref1.begin(), ref1.end());
|
|
ListType ifl;
|
|
ifl.assign(storage1.begin(), storage1.end());
|
|
ASSERT_LISTS_EQUAL(ref1, ifl);
|
|
std::forward_list<int> ref2({ 7, 1, 3 });
|
|
std::vector<ValueType> storage2(ref2.begin(), ref2.end());
|
|
ifl.assign(storage2.begin(), storage2.end());
|
|
ASSERT_LISTS_EQUAL(ref2, ifl);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, Assign) {
|
|
Assign<IFLTestValueList>();
|
|
Assign<ConstIFLTestValueList>();
|
|
Assign<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::PushPop() {
|
|
using ValueType = typename ListType::value_type;
|
|
ValueType value3(3);
|
|
ValueType value7(7);
|
|
std::forward_list<int> ref;
|
|
ListType ifl;
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ref.push_front(3);
|
|
ifl.push_front(value3);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ASSERT_EQ(3, ifl.front());
|
|
ref.push_front(7);
|
|
ifl.push_front(value7);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ASSERT_EQ(7, ifl.front());
|
|
ref.pop_front();
|
|
ifl.pop_front();
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ASSERT_EQ(3, ifl.front());
|
|
ref.pop_front();
|
|
ifl.pop_front();
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, PushPop) {
|
|
PushPop<IFLTestValueList>();
|
|
PushPop<ConstIFLTestValueList>();
|
|
PushPop<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::InsertAfter1() {
|
|
using ValueType = typename ListType::value_type;
|
|
ValueType value4(4);
|
|
ValueType value8(8);
|
|
ValueType value5(5);
|
|
ValueType value3(3);
|
|
std::forward_list<int> ref;
|
|
ListType ifl;
|
|
|
|
auto ref_it = ref.insert_after(ref.before_begin(), 4);
|
|
auto ifl_it = ifl.insert_after(ifl.before_begin(), value4);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ASSERT_EQ(*ref_it, *ifl_it);
|
|
CHECK(ref_it == ref.begin());
|
|
ASSERT_TRUE(ifl_it == ifl.begin());
|
|
|
|
ref_it = ref.insert_after(ref.begin(), 8);
|
|
ifl_it = ifl.insert_after(ifl.begin(), value8);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ASSERT_EQ(*ref_it, *ifl_it);
|
|
CHECK(ref_it != ref.end());
|
|
ASSERT_TRUE(ifl_it != ifl.end());
|
|
CHECK(++ref_it == ref.end());
|
|
ASSERT_TRUE(++ifl_it == ifl.end());
|
|
|
|
ref_it = ref.insert_after(ref.begin(), 5);
|
|
ifl_it = ifl.insert_after(ifl.begin(), value5);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ASSERT_EQ(*ref_it, *ifl_it);
|
|
|
|
ref_it = ref.insert_after(ref_it, 3);
|
|
ifl_it = ifl.insert_after(ifl_it, value3);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ASSERT_EQ(*ref_it, *ifl_it);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, InsertAfter1) {
|
|
InsertAfter1<IFLTestValueList>();
|
|
InsertAfter1<ConstIFLTestValueList>();
|
|
InsertAfter1<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::InsertAfter2() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref;
|
|
ListType ifl;
|
|
|
|
auto ref_it = ref.insert_after(ref.before_begin(), { 2, 8, 5 });
|
|
std::vector<ValueType> storage1({ { 2 }, { 8 }, { 5 } });
|
|
auto ifl_it = ifl.insert_after(ifl.before_begin(), storage1.begin(), storage1.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ASSERT_EQ(*ref_it, *ifl_it);
|
|
|
|
std::vector<ValueType> storage2({ { 7 }, { 2 } });
|
|
ref_it = ref.insert_after(ref.begin(), { 7, 2 });
|
|
ifl_it = ifl.insert_after(ifl.begin(), storage2.begin(), storage2.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ASSERT_EQ(*ref_it, *ifl_it);
|
|
|
|
std::vector<ValueType> storage3({ { 1 }, { 3 }, { 4 }, { 9 } });
|
|
ref_it = ref.begin();
|
|
ifl_it = ifl.begin();
|
|
std::advance(ref_it, std::distance(ref.begin(), ref.end()) - 1);
|
|
std::advance(ifl_it, std::distance(ifl.begin(), ifl.end()) - 1);
|
|
ref_it = ref.insert_after(ref_it, { 1, 3, 4, 9 });
|
|
ifl_it = ifl.insert_after(ifl_it, storage3.begin(), storage3.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, InsertAfter2) {
|
|
InsertAfter2<IFLTestValueList>();
|
|
InsertAfter2<ConstIFLTestValueList>();
|
|
InsertAfter2<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::EraseAfter1() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref({ 1, 2, 7, 4, 5 });
|
|
std::vector<ValueType> storage(ref.begin(), ref.end());
|
|
ListType ifl(storage.begin(), storage.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
CHECK_EQ(std::distance(ref.begin(), ref.end()), 5);
|
|
|
|
auto ref_it = ref.begin();
|
|
auto ifl_it = ifl.begin();
|
|
std::advance(ref_it, 2);
|
|
std::advance(ifl_it, 2);
|
|
ref_it = ref.erase_after(ref_it);
|
|
ifl_it = ifl.erase_after(ifl_it);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
CHECK_EQ(std::distance(ref.begin(), ref.end()), 4);
|
|
CHECK(ref_it != ref.end());
|
|
ASSERT_TRUE(ifl_it != ifl.end());
|
|
CHECK(++ref_it == ref.end());
|
|
ASSERT_TRUE(++ifl_it == ifl.end());
|
|
|
|
ref_it = ref.begin();
|
|
ifl_it = ifl.begin();
|
|
std::advance(ref_it, 2);
|
|
std::advance(ifl_it, 2);
|
|
ref_it = ref.erase_after(ref_it);
|
|
ifl_it = ifl.erase_after(ifl_it);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
CHECK_EQ(std::distance(ref.begin(), ref.end()), 3);
|
|
CHECK(ref_it == ref.end());
|
|
ASSERT_TRUE(ifl_it == ifl.end());
|
|
|
|
ref_it = ref.erase_after(ref.begin());
|
|
ifl_it = ifl.erase_after(ifl.begin());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
CHECK_EQ(std::distance(ref.begin(), ref.end()), 2);
|
|
CHECK(ref_it != ref.end());
|
|
ASSERT_TRUE(ifl_it != ifl.end());
|
|
CHECK(++ref_it == ref.end());
|
|
ASSERT_TRUE(++ifl_it == ifl.end());
|
|
|
|
ref_it = ref.erase_after(ref.before_begin());
|
|
ifl_it = ifl.erase_after(ifl.before_begin());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
CHECK_EQ(std::distance(ref.begin(), ref.end()), 1);
|
|
CHECK(ref_it == ref.begin());
|
|
ASSERT_TRUE(ifl_it == ifl.begin());
|
|
|
|
ref_it = ref.erase_after(ref.before_begin());
|
|
ifl_it = ifl.erase_after(ifl.before_begin());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
CHECK_EQ(std::distance(ref.begin(), ref.end()), 0);
|
|
CHECK(ref_it == ref.begin());
|
|
ASSERT_TRUE(ifl_it == ifl.begin());
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, EraseAfter1) {
|
|
EraseAfter1<IFLTestValueList>();
|
|
EraseAfter1<ConstIFLTestValueList>();
|
|
EraseAfter1<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::EraseAfter2() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref({ 1, 2, 7, 4, 5, 3, 2, 8, 9 });
|
|
std::vector<ValueType> storage(ref.begin(), ref.end());
|
|
ListType ifl(storage.begin(), storage.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
CHECK_EQ(std::distance(ref.begin(), ref.end()), 9);
|
|
|
|
auto ref_it = ref.begin();
|
|
auto ifl_it = ifl.begin();
|
|
std::advance(ref_it, 3);
|
|
std::advance(ifl_it, 3);
|
|
ref_it = ref.erase_after(ref.begin(), ref_it);
|
|
ifl_it = ifl.erase_after(ifl.begin(), ifl_it);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ASSERT_EQ(std::distance(ref.begin(), ref_it), std::distance(ifl.begin(), ifl_it));
|
|
CHECK_EQ(std::distance(ref.begin(), ref.end()), 7);
|
|
|
|
ref_it = ref.erase_after(ref_it, ref.end());
|
|
ifl_it = ifl.erase_after(ifl_it, ifl.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
CHECK(ref_it == ref.end());
|
|
ASSERT_TRUE(ifl_it == ifl.end());
|
|
CHECK_EQ(std::distance(ref.begin(), ref.end()), 2);
|
|
|
|
ref_it = ref.erase_after(ref.before_begin(), ref.end());
|
|
ifl_it = ifl.erase_after(ifl.before_begin(), ifl.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
CHECK(ref_it == ref.end());
|
|
ASSERT_TRUE(ifl_it == ifl.end());
|
|
CHECK_EQ(std::distance(ref.begin(), ref.end()), 0);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, EraseAfter2) {
|
|
EraseAfter2<IFLTestValueList>();
|
|
EraseAfter2<ConstIFLTestValueList>();
|
|
EraseAfter2<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::SwapClear() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref1({ 1, 2, 7 });
|
|
std::vector<ValueType> storage1(ref1.begin(), ref1.end());
|
|
ListType ifl1(storage1.begin(), storage1.end());
|
|
std::forward_list<int> ref2({ 3, 8, 6 });
|
|
std::vector<ValueType> storage2(ref2.begin(), ref2.end());
|
|
ListType ifl2(storage2.begin(), storage2.end());
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
ref1.swap(ref2);
|
|
ifl1.swap(ifl2);
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
ref1.clear();
|
|
ifl1.clear();
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
swap(ref1, ref2);
|
|
swap(ifl1, ifl2);
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
ref1.clear();
|
|
ifl1.clear();
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, SwapClear) {
|
|
SwapClear<IFLTestValueList>();
|
|
SwapClear<ConstIFLTestValueList>();
|
|
SwapClear<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::SpliceAfter() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref1({ 3, 1, 2, 7, 4, 5, 4, 8, 7 });
|
|
std::forward_list<int> ref2;
|
|
std::vector<ValueType> storage(ref1.begin(), ref1.end());
|
|
ListType ifl1(storage.begin(), storage.end());
|
|
ListType ifl2;
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
|
|
// Move everything to ref2/ifl2.
|
|
ref2.splice_after(ref2.before_begin(), ref1);
|
|
ifl2.splice_after(ifl2.before_begin(), ifl1);
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
|
|
// Move first element (3) to ref1/ifl1.
|
|
ref1.splice_after(ref1.before_begin(), ref2, ref2.before_begin());
|
|
ifl1.splice_after(ifl1.before_begin(), ifl2, ifl2.before_begin());
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
|
|
// Move second element (2) to ref1/ifl1 after the first element (3).
|
|
ref1.splice_after(ref1.begin(), ref2, ref2.begin());
|
|
ifl1.splice_after(ifl1.begin(), ifl2, ifl2.begin());
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
|
|
// Move everything from ref2/ifl2 between the 2 elements now in ref1/ifl1.
|
|
ref1.splice_after(ref1.begin(), ref2);
|
|
ifl1.splice_after(ifl1.begin(), ifl2);
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
|
|
std::forward_list<int> check({ 3, 1, 7, 4, 5, 4, 8, 7, 2 });
|
|
ASSERT_LISTS_EQUAL(check, ifl1);
|
|
ASSERT_TRUE(ifl2.empty());
|
|
|
|
// Empty splice_after().
|
|
ref2.splice_after(
|
|
ref2.before_begin(), ref1, ref1.before_begin(), ref1.begin());
|
|
ifl2.splice_after(ifl2.before_begin(), ifl1, ifl1.before_begin(), ifl1.begin());
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
|
|
// Move { 1, 7 } to ref2/ifl2.
|
|
auto ref_it = ref1.begin();
|
|
auto ifl_it = ifl1.begin();
|
|
std::advance(ref_it, 3);
|
|
std::advance(ifl_it, 3);
|
|
ref2.splice_after(ref2.before_begin(), ref1, ref1.begin(), ref_it);
|
|
ifl2.splice_after(ifl2.before_begin(), ifl1, ifl1.begin(), ifl_it);
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
|
|
// Move { 8, 7, 2 } to the beginning of ref1/ifl1.
|
|
ref_it = ref1.begin();
|
|
ifl_it = ifl1.begin();
|
|
std::advance(ref_it, 3);
|
|
std::advance(ifl_it, 3);
|
|
ref1.splice_after(ref1.before_begin(), ref1, ref_it, ref1.end());
|
|
ifl1.splice_after(ifl1.before_begin(), ifl1, ifl_it, ifl1.end());
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
|
|
check.assign({ 8, 7, 2, 3, 4, 5, 4 });
|
|
ASSERT_LISTS_EQUAL(check, ifl1);
|
|
check.assign({ 1, 7 });
|
|
ASSERT_LISTS_EQUAL(check, ifl2);
|
|
|
|
// Move all but the first element to ref2/ifl2.
|
|
ref_it = ref2.begin();
|
|
ifl_it = ifl2.begin();
|
|
std::advance(ref_it, 1);
|
|
std::advance(ifl_it, 1);
|
|
ref2.splice_after(ref_it, ref1, ref1.begin(), ref1.end());
|
|
ifl2.splice_after(ifl_it, ifl1, ifl1.begin(), ifl1.end());
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
|
|
check.assign({8});
|
|
ASSERT_LISTS_EQUAL(check, ifl1);
|
|
|
|
// Move the first element of ref1/ifl1 to the beginning of ref1/ifl1 (do nothing).
|
|
ref1.splice_after(ref1.before_begin(), ref1, ref1.before_begin());
|
|
ifl1.splice_after(ifl1.before_begin(), ifl1, ifl1.before_begin());
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(check, ifl1);
|
|
|
|
// Move the first element of ref2/ifl2 after itself (do nothing).
|
|
ref1.splice_after(ref1.begin(), ref1, ref1.before_begin());
|
|
ifl1.splice_after(ifl1.begin(), ifl1, ifl1.before_begin());
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(check, ifl1);
|
|
|
|
check.assign({ 1, 7, 7, 2, 3, 4, 5, 4 });
|
|
ASSERT_LISTS_EQUAL(check, ifl2);
|
|
|
|
// Move the first element of ref2/ifl2 to the beginning of ref2/ifl2 (do nothing).
|
|
ref2.splice_after(ref2.before_begin(), ref2, ref2.before_begin());
|
|
ifl2.splice_after(ifl2.before_begin(), ifl2, ifl2.before_begin());
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
ASSERT_LISTS_EQUAL(check, ifl2);
|
|
|
|
// Move the first element of ref2/ifl2 after itself (do nothing).
|
|
ref2.splice_after(ref2.begin(), ref2, ref2.before_begin());
|
|
ifl2.splice_after(ifl2.begin(), ifl2, ifl2.before_begin());
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
ASSERT_LISTS_EQUAL(check, ifl2);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, SpliceAfter) {
|
|
SpliceAfter<IFLTestValueList>();
|
|
SpliceAfter<ConstIFLTestValueList>();
|
|
SpliceAfter<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::Remove() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref({ 3, 1, 2, 7, 4, 5, 4, 8, 7 });
|
|
std::vector<ValueType> storage(ref.begin(), ref.end());
|
|
ListType ifl(storage.begin(), storage.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ref.remove(1);
|
|
ifl.remove(1);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ref.remove(4);
|
|
ifl.remove(4);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
auto odd = [](ValueType value) { return (value.value & 1) != 0; };
|
|
ref.remove_if(odd);
|
|
ifl.remove_if(odd);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
auto all = [](ValueType value ATTRIBUTE_UNUSED) { return true; };
|
|
ref.remove_if(all);
|
|
ifl.remove_if(all);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, Remove) {
|
|
Remove<IFLTestValueList>();
|
|
Remove<ConstIFLTestValueList>();
|
|
Remove<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::Unique() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref({ 3, 1, 1, 2, 3, 3, 7, 7, 4, 4, 5, 7 });
|
|
std::vector<ValueType> storage(ref.begin(), ref.end());
|
|
ListType ifl(storage.begin(), storage.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
ref.unique();
|
|
ifl.unique();
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
std::forward_list<int> check({ 3, 1, 2, 3, 7, 4, 5, 7 });
|
|
ASSERT_LISTS_EQUAL(check, ifl);
|
|
|
|
auto bin_pred = [](const ValueType& lhs, const ValueType& rhs) {
|
|
return (lhs.value & ~1) == (rhs.value & ~1);
|
|
};
|
|
ref.unique(bin_pred);
|
|
ifl.unique(bin_pred);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
check.assign({ 3, 1, 2, 7, 4, 7 });
|
|
ASSERT_LISTS_EQUAL(check, ifl);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, Unique) {
|
|
Unique<IFLTestValueList>();
|
|
Unique<ConstIFLTestValueList>();
|
|
Unique<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::Merge() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref1({ 1, 4, 8, 8, 12 });
|
|
std::vector<ValueType> storage1(ref1.begin(), ref1.end());
|
|
ListType ifl1(storage1.begin(), storage1.end());
|
|
std::forward_list<int> ref2({ 3, 5, 6, 7, 9 });
|
|
std::vector<ValueType> storage2(ref2.begin(), ref2.end());
|
|
ListType ifl2(storage2.begin(), storage2.end());
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
CHECK(std::is_sorted(ref1.begin(), ref1.end()));
|
|
CHECK(std::is_sorted(ref2.begin(), ref2.end()));
|
|
ref1.merge(ref2);
|
|
ifl1.merge(ifl2);
|
|
ASSERT_LISTS_EQUAL(ref1, ifl1);
|
|
ASSERT_LISTS_EQUAL(ref2, ifl2);
|
|
CHECK(ref2.empty());
|
|
std::forward_list<int> check({ 1, 3, 4, 5, 6, 7, 8, 8, 9, 12 });
|
|
ASSERT_LISTS_EQUAL(check, ifl1);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, Merge) {
|
|
Merge<IFLTestValueList>();
|
|
Merge<ConstIFLTestValueList>();
|
|
Merge<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::Sort1() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref({ 2, 9, 8, 3, 7, 4, 1, 5, 3, 0 });
|
|
std::vector<ValueType> storage(ref.begin(), ref.end());
|
|
ListType ifl(storage.begin(), storage.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
CHECK(!std::is_sorted(ref.begin(), ref.end()));
|
|
ref.sort();
|
|
ifl.sort();
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
std::forward_list<int> check({ 0, 1, 2, 3, 3, 4, 5, 7, 8, 9 });
|
|
ASSERT_LISTS_EQUAL(check, ifl);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, Sort1) {
|
|
Sort1<IFLTestValueList>();
|
|
Sort1<ConstIFLTestValueList>();
|
|
Sort1<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::Sort2() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref({ 2, 9, 8, 3, 7, 4, 1, 5, 3, 0 });
|
|
std::vector<ValueType> storage(ref.begin(), ref.end());
|
|
ListType ifl(storage.begin(), storage.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
auto cmp = [](const ValueType& lhs, const ValueType& rhs) {
|
|
return (lhs.value & ~1) < (rhs.value & ~1);
|
|
};
|
|
CHECK(!std::is_sorted(ref.begin(), ref.end(), cmp));
|
|
ref.sort(cmp);
|
|
ifl.sort(cmp);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
std::forward_list<int> check({ 1, 0, 2, 3, 3, 4, 5, 7, 9, 8 });
|
|
ASSERT_LISTS_EQUAL(check, ifl);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, Sort2) {
|
|
Sort2<IFLTestValueList>();
|
|
Sort2<ConstIFLTestValueList>();
|
|
Sort2<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::Reverse() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref({ 8, 3, 5, 4, 1, 3 });
|
|
std::vector<ValueType> storage(ref.begin(), ref.end());
|
|
ListType ifl(storage.begin(), storage.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
CHECK(!std::is_sorted(ref.begin(), ref.end()));
|
|
ref.reverse();
|
|
ifl.reverse();
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
std::forward_list<int> check({ 3, 1, 4, 5, 3, 8 });
|
|
ASSERT_LISTS_EQUAL(check, ifl);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, Reverse) {
|
|
Reverse<IFLTestValueList>();
|
|
Reverse<ConstIFLTestValueList>();
|
|
Reverse<IFLTestValue2List>();
|
|
}
|
|
|
|
template <typename ListType>
|
|
void IntrusiveForwardListTest::ModifyValue() {
|
|
using ValueType = typename ListType::value_type;
|
|
std::forward_list<int> ref({ 3, 7, 42 });
|
|
std::vector<ValueType> storage(ref.begin(), ref.end());
|
|
ListType ifl(storage.begin(), storage.end());
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
|
|
auto add1 = [](const ValueType& value) { return value.value + 1; };
|
|
std::transform(ref.begin(), ref.end(), ref.begin(), add1);
|
|
std::transform(ifl.begin(), ifl.end(), ifl.begin(), add1);
|
|
ASSERT_LISTS_EQUAL(ref, ifl);
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, ModifyValue) {
|
|
ModifyValue<IFLTestValueList>();
|
|
// Does not compile with ConstIFLTestValueList because LHS of the assignment is const.
|
|
// ModifyValue<ConstIFLTestValueList>();
|
|
static_assert(std::is_const_v<ConstIFLTestValueList::iterator::value_type>);
|
|
ModifyValue<IFLTestValue2List>();
|
|
}
|
|
|
|
struct Tag1;
|
|
struct Tag2;
|
|
struct TwoListsValue : public IntrusiveForwardListNode<TwoListsValue, Tag1>,
|
|
public IntrusiveForwardListNode<TwoListsValue, Tag2> {
|
|
// Deliberately not explicit.
|
|
TwoListsValue(int v) : value(v) { } // NOLINT(runtime/explicit)
|
|
|
|
int value;
|
|
};
|
|
using FirstList =
|
|
IntrusiveForwardList<TwoListsValue, IntrusiveForwardListBaseHookTraits<TwoListsValue, Tag1>>;
|
|
using SecondList =
|
|
IntrusiveForwardList<TwoListsValue, IntrusiveForwardListBaseHookTraits<TwoListsValue, Tag2>>;
|
|
|
|
bool operator==(const TwoListsValue& lhs, const TwoListsValue& rhs) {
|
|
return lhs.value == rhs.value;
|
|
}
|
|
|
|
TEST_F(IntrusiveForwardListTest, TwoLists) {
|
|
// Test that a value can be in two lists at the same time and the hooks do not interfere.
|
|
std::vector<TwoListsValue> storage({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); // storage[i] = i
|
|
|
|
std::vector<int> order1({ 3, 1, 7, 2, 8, 9, 4, 0, 6, 5 });
|
|
FirstList list1;
|
|
auto pos1 = list1.before_begin();
|
|
for (size_t idx : order1) {
|
|
pos1 = list1.insert_after(pos1, storage[idx]);
|
|
}
|
|
|
|
std::vector<int> order2({ 8, 5, 1, 6, 7, 2, 9, 3, 0, 4 });
|
|
SecondList list2;
|
|
auto pos2 = list2.before_begin();
|
|
for (size_t idx : order2) {
|
|
pos2 = list2.insert_after(pos2, storage[idx]);
|
|
}
|
|
|
|
// Using `storage[i] = i`, we can easily compare that nodes of each list are in the right order.
|
|
ASSERT_LISTS_EQUAL(order1, list1);
|
|
ASSERT_LISTS_EQUAL(order2, list2);
|
|
}
|
|
|
|
} // namespace art
|