summaryrefslogtreecommitdiff
path: root/absl/container/btree_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'absl/container/btree_test.cc')
-rw-r--r--absl/container/btree_test.cc383
1 files changed, 331 insertions, 52 deletions
diff --git a/absl/container/btree_test.cc b/absl/container/btree_test.cc
index 1bfa0c20..74337df2 100644
--- a/absl/container/btree_test.cc
+++ b/absl/container/btree_test.cc
@@ -55,6 +55,7 @@ using ::testing::ElementsAreArray;
using ::testing::IsEmpty;
using ::testing::IsNull;
using ::testing::Pair;
+using ::testing::SizeIs;
template <typename T, typename U>
void CheckPairEquals(const T &x, const U &y) {
@@ -1182,6 +1183,103 @@ TEST(Btree, RangeCtorSanity) {
EXPECT_EQ(1, tmap.size());
}
+} // namespace
+
+class BtreeNodePeer {
+ public:
+ // Yields the size of a leaf node with a specific number of values.
+ template <typename ValueType>
+ constexpr static size_t GetTargetNodeSize(size_t target_values_per_node) {
+ return btree_node<
+ set_params<ValueType, std::less<ValueType>, std::allocator<ValueType>,
+ /*TargetNodeSize=*/256, // This parameter isn't used here.
+ /*Multi=*/false>>::SizeWithNSlots(target_values_per_node);
+ }
+
+ // Yields the number of slots in a (non-root) leaf node for this btree.
+ template <typename Btree>
+ constexpr static size_t GetNumSlotsPerNode() {
+ return btree_node<typename Btree::params_type>::kNodeSlots;
+ }
+
+ template <typename Btree>
+ constexpr static size_t GetMaxFieldType() {
+ return std::numeric_limits<
+ typename btree_node<typename Btree::params_type>::field_type>::max();
+ }
+
+ template <typename Btree>
+ constexpr static bool UsesLinearNodeSearch() {
+ return btree_node<typename Btree::params_type>::use_linear_search::value;
+ }
+};
+
+namespace {
+
+class BtreeMapTest : public ::testing::Test {
+ public:
+ struct Key {};
+ struct Cmp {
+ template <typename T>
+ bool operator()(T, T) const {
+ return false;
+ }
+ };
+
+ struct KeyLin {
+ using absl_btree_prefer_linear_node_search = std::true_type;
+ };
+ struct CmpLin : Cmp {
+ using absl_btree_prefer_linear_node_search = std::true_type;
+ };
+
+ struct KeyBin {
+ using absl_btree_prefer_linear_node_search = std::false_type;
+ };
+ struct CmpBin : Cmp {
+ using absl_btree_prefer_linear_node_search = std::false_type;
+ };
+
+ template <typename K, typename C>
+ static bool IsLinear() {
+ return BtreeNodePeer::UsesLinearNodeSearch<absl::btree_map<K, int, C>>();
+ }
+};
+
+TEST_F(BtreeMapTest, TestLinearSearchPreferredForKeyLinearViaAlias) {
+ // Test requesting linear search by directly exporting an alias.
+ EXPECT_FALSE((IsLinear<Key, Cmp>()));
+ EXPECT_TRUE((IsLinear<KeyLin, Cmp>()));
+ EXPECT_TRUE((IsLinear<Key, CmpLin>()));
+ EXPECT_TRUE((IsLinear<KeyLin, CmpLin>()));
+}
+
+TEST_F(BtreeMapTest, LinearChoiceTree) {
+ // Cmp has precedence, and is forcing binary
+ EXPECT_FALSE((IsLinear<Key, CmpBin>()));
+ EXPECT_FALSE((IsLinear<KeyLin, CmpBin>()));
+ EXPECT_FALSE((IsLinear<KeyBin, CmpBin>()));
+ EXPECT_FALSE((IsLinear<int, CmpBin>()));
+ EXPECT_FALSE((IsLinear<std::string, CmpBin>()));
+ // Cmp has precedence, and is forcing linear
+ EXPECT_TRUE((IsLinear<Key, CmpLin>()));
+ EXPECT_TRUE((IsLinear<KeyLin, CmpLin>()));
+ EXPECT_TRUE((IsLinear<KeyBin, CmpLin>()));
+ EXPECT_TRUE((IsLinear<int, CmpLin>()));
+ EXPECT_TRUE((IsLinear<std::string, CmpLin>()));
+ // Cmp has no preference, Key determines linear vs binary.
+ EXPECT_FALSE((IsLinear<Key, Cmp>()));
+ EXPECT_TRUE((IsLinear<KeyLin, Cmp>()));
+ EXPECT_FALSE((IsLinear<KeyBin, Cmp>()));
+ // arithmetic key w/ std::less or std::greater: linear
+ EXPECT_TRUE((IsLinear<int, std::less<int>>()));
+ EXPECT_TRUE((IsLinear<double, std::greater<double>>()));
+ // arithmetic key w/ custom compare: binary
+ EXPECT_FALSE((IsLinear<int, Cmp>()));
+ // non-arithmetic key: binary
+ EXPECT_FALSE((IsLinear<std::string, std::less<std::string>>()));
+}
+
TEST(Btree, BtreeMapCanHoldMoveOnlyTypes) {
absl::btree_map<std::string, std::unique_ptr<std::string>> m;
@@ -1327,34 +1425,6 @@ TEST(Btree, RValueInsert) {
EXPECT_EQ(tracker.swaps(), 0);
}
-} // namespace
-
-class BtreeNodePeer {
- public:
- // Yields the size of a leaf node with a specific number of values.
- template <typename ValueType>
- constexpr static size_t GetTargetNodeSize(size_t target_values_per_node) {
- return btree_node<
- set_params<ValueType, std::less<ValueType>, std::allocator<ValueType>,
- /*TargetNodeSize=*/256, // This parameter isn't used here.
- /*Multi=*/false>>::SizeWithNValues(target_values_per_node);
- }
-
- // Yields the number of values in a (non-root) leaf node for this set.
- template <typename Set>
- constexpr static size_t GetNumValuesPerNode() {
- return btree_node<typename Set::params_type>::kNodeValues;
- }
-
- template <typename Set>
- constexpr static size_t GetMaxFieldType() {
- return std::numeric_limits<
- typename btree_node<typename Set::params_type>::field_type>::max();
- }
-};
-
-namespace {
-
// A btree set with a specific number of values per node.
template <typename Key, int TargetValuesPerNode, typename Cmp = std::less<Key>>
class SizedBtreeSet
@@ -1388,7 +1458,7 @@ void ExpectOperationCounts(const int expected_moves,
TEST(Btree, MovesComparisonsCopiesSwapsTracking) {
InstanceTracker tracker;
// Note: this is minimum number of values per node.
- SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/3> set3;
+ SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/4> set4;
// Note: this is the default number of values per node for a set of int32s
// (with 64-bit pointers).
SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/61> set61;
@@ -1399,28 +1469,28 @@ TEST(Btree, MovesComparisonsCopiesSwapsTracking) {
std::vector<int> values =
GenerateValuesWithSeed<int>(10000, 1 << 22, /*seed=*/23);
- EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set3)>(), 3);
- EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>(), 61);
- EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set100)>(), 100);
+ EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set4)>(), 4);
+ EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>(), 61);
+ EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set100)>(), 100);
if (sizeof(void *) == 8) {
- EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<absl::btree_set<int32_t>>(),
- BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>());
+ EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(),
+ BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>());
}
// Test key insertion/deletion in random order.
- ExpectOperationCounts(45281, 132551, values, &tracker, &set3);
+ ExpectOperationCounts(56540, 134212, values, &tracker, &set4);
ExpectOperationCounts(386718, 129807, values, &tracker, &set61);
ExpectOperationCounts(586761, 130310, values, &tracker, &set100);
// Test key insertion/deletion in sorted order.
std::sort(values.begin(), values.end());
- ExpectOperationCounts(26638, 92134, values, &tracker, &set3);
+ ExpectOperationCounts(24972, 85563, values, &tracker, &set4);
ExpectOperationCounts(20208, 87757, values, &tracker, &set61);
ExpectOperationCounts(20124, 96583, values, &tracker, &set100);
// Test key insertion/deletion in reverse sorted order.
std::reverse(values.begin(), values.end());
- ExpectOperationCounts(49951, 119325, values, &tracker, &set3);
+ ExpectOperationCounts(54949, 127531, values, &tracker, &set4);
ExpectOperationCounts(338813, 118266, values, &tracker, &set61);
ExpectOperationCounts(534529, 125279, values, &tracker, &set100);
}
@@ -1437,9 +1507,9 @@ struct MovableOnlyInstanceThreeWayCompare {
TEST(Btree, MovesComparisonsCopiesSwapsTrackingThreeWayCompare) {
InstanceTracker tracker;
// Note: this is minimum number of values per node.
- SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/3,
+ SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/4,
MovableOnlyInstanceThreeWayCompare>
- set3;
+ set4;
// Note: this is the default number of values per node for a set of int32s
// (with 64-bit pointers).
SizedBtreeSet<MovableOnlyInstance, /*TargetValuesPerNode=*/61,
@@ -1454,28 +1524,28 @@ TEST(Btree, MovesComparisonsCopiesSwapsTrackingThreeWayCompare) {
std::vector<int> values =
GenerateValuesWithSeed<int>(10000, 1 << 22, /*seed=*/23);
- EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set3)>(), 3);
- EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>(), 61);
- EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<decltype(set100)>(), 100);
+ EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set4)>(), 4);
+ EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>(), 61);
+ EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set100)>(), 100);
if (sizeof(void *) == 8) {
- EXPECT_EQ(BtreeNodePeer::GetNumValuesPerNode<absl::btree_set<int32_t>>(),
- BtreeNodePeer::GetNumValuesPerNode<decltype(set61)>());
+ EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(),
+ BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>());
}
// Test key insertion/deletion in random order.
- ExpectOperationCounts(45281, 122560, values, &tracker, &set3);
+ ExpectOperationCounts(56540, 124221, values, &tracker, &set4);
ExpectOperationCounts(386718, 119816, values, &tracker, &set61);
ExpectOperationCounts(586761, 120319, values, &tracker, &set100);
// Test key insertion/deletion in sorted order.
std::sort(values.begin(), values.end());
- ExpectOperationCounts(26638, 92134, values, &tracker, &set3);
+ ExpectOperationCounts(24972, 85563, values, &tracker, &set4);
ExpectOperationCounts(20208, 87757, values, &tracker, &set61);
ExpectOperationCounts(20124, 96583, values, &tracker, &set100);
// Test key insertion/deletion in reverse sorted order.
std::reverse(values.begin(), values.end());
- ExpectOperationCounts(49951, 109326, values, &tracker, &set3);
+ ExpectOperationCounts(54949, 117532, values, &tracker, &set4);
ExpectOperationCounts(338813, 108267, values, &tracker, &set61);
ExpectOperationCounts(534529, 115280, values, &tracker, &set100);
}
@@ -1968,6 +2038,30 @@ TEST(Btree, ExtractAndInsertNodeHandleMultiMap) {
EXPECT_EQ(res, ++other.begin());
}
+TEST(Btree, ExtractMultiMapEquivalentKeys) {
+ // Note: using string keys means a three-way comparator.
+ absl::btree_multimap<std::string, int> map;
+ for (int i = 0; i < 100; ++i) {
+ for (int j = 0; j < 100; ++j) {
+ map.insert({absl::StrCat(i), j});
+ }
+ }
+
+ for (int i = 0; i < 100; ++i) {
+ const std::string key = absl::StrCat(i);
+ auto node_handle = map.extract(key);
+ EXPECT_EQ(node_handle.key(), key);
+ EXPECT_EQ(node_handle.mapped(), 0) << i;
+ }
+
+ for (int i = 0; i < 100; ++i) {
+ const std::string key = absl::StrCat(i);
+ auto node_handle = map.extract(key);
+ EXPECT_EQ(node_handle.key(), key);
+ EXPECT_EQ(node_handle.mapped(), 1) << i;
+ }
+}
+
// For multisets, insert with hint also affects correctness because we need to
// insert immediately before the hint if possible.
struct InsertMultiHintData {
@@ -2109,6 +2203,31 @@ TEST(Btree, MergeIntoMultiMapsWithDifferentComparators) {
Pair(4, 1), Pair(4, 4), Pair(5, 5)));
}
+TEST(Btree, MergeIntoSetMovableOnly) {
+ absl::btree_set<MovableOnlyInstance> src;
+ src.insert(MovableOnlyInstance(1));
+ absl::btree_multiset<MovableOnlyInstance> dst1;
+ dst1.insert(MovableOnlyInstance(2));
+ absl::btree_set<MovableOnlyInstance> dst2;
+
+ // Test merge into multiset.
+ dst1.merge(src);
+
+ EXPECT_TRUE(src.empty());
+ // ElementsAre/ElementsAreArray don't work with move-only types.
+ ASSERT_THAT(dst1, SizeIs(2));
+ EXPECT_EQ(*dst1.begin(), MovableOnlyInstance(1));
+ EXPECT_EQ(*std::next(dst1.begin()), MovableOnlyInstance(2));
+
+ // Test merge into set.
+ dst2.merge(dst1);
+
+ EXPECT_TRUE(dst1.empty());
+ ASSERT_THAT(dst2, SizeIs(2));
+ EXPECT_EQ(*dst2.begin(), MovableOnlyInstance(1));
+ EXPECT_EQ(*std::next(dst2.begin()), MovableOnlyInstance(2));
+}
+
struct KeyCompareToWeakOrdering {
template <typename T>
absl::weak_ordering operator()(const T &a, const T &b) const {
@@ -2585,6 +2704,12 @@ struct MultiKey {
int i2;
};
+bool operator==(const MultiKey a, const MultiKey b) {
+ return a.i1 == b.i1 && a.i2 == b.i2;
+}
+
+// A heterogeneous comparator that has different equivalence classes for
+// different lookup types.
struct MultiKeyComp {
using is_transparent = void;
bool operator()(const MultiKey a, const MultiKey b) const {
@@ -2595,11 +2720,36 @@ struct MultiKeyComp {
bool operator()(const MultiKey a, const int b) const { return a.i1 < b; }
};
-// Test that when there's a heterogeneous comparator that behaves differently
-// for some heterogeneous operators, we get equal_range() right.
-TEST(Btree, MultiKeyEqualRange) {
- absl::btree_set<MultiKey, MultiKeyComp> set;
+// A heterogeneous, three-way comparator that has different equivalence classes
+// for different lookup types.
+struct MultiKeyThreeWayComp {
+ using is_transparent = void;
+ absl::weak_ordering operator()(const MultiKey a, const MultiKey b) const {
+ if (a.i1 < b.i1) return absl::weak_ordering::less;
+ if (a.i1 > b.i1) return absl::weak_ordering::greater;
+ if (a.i2 < b.i2) return absl::weak_ordering::less;
+ if (a.i2 > b.i2) return absl::weak_ordering::greater;
+ return absl::weak_ordering::equivalent;
+ }
+ absl::weak_ordering operator()(const int a, const MultiKey b) const {
+ if (a < b.i1) return absl::weak_ordering::less;
+ if (a > b.i1) return absl::weak_ordering::greater;
+ return absl::weak_ordering::equivalent;
+ }
+ absl::weak_ordering operator()(const MultiKey a, const int b) const {
+ if (a.i1 < b) return absl::weak_ordering::less;
+ if (a.i1 > b) return absl::weak_ordering::greater;
+ return absl::weak_ordering::equivalent;
+ }
+};
+template <typename Compare>
+class BtreeMultiKeyTest : public ::testing::Test {};
+using MultiKeyComps = ::testing::Types<MultiKeyComp, MultiKeyThreeWayComp>;
+TYPED_TEST_SUITE(BtreeMultiKeyTest, MultiKeyComps);
+
+TYPED_TEST(BtreeMultiKeyTest, EqualRange) {
+ absl::btree_set<MultiKey, TypeParam> set;
for (int i = 0; i < 100; ++i) {
for (int j = 0; j < 100; ++j) {
set.insert({i, j});
@@ -2609,11 +2759,140 @@ TEST(Btree, MultiKeyEqualRange) {
for (int i = 0; i < 100; ++i) {
auto equal_range = set.equal_range(i);
EXPECT_EQ(equal_range.first->i1, i);
- EXPECT_EQ(equal_range.first->i2, 0);
+ EXPECT_EQ(equal_range.first->i2, 0) << i;
EXPECT_EQ(std::distance(equal_range.first, equal_range.second), 100) << i;
}
}
+TYPED_TEST(BtreeMultiKeyTest, Extract) {
+ absl::btree_set<MultiKey, TypeParam> set;
+ for (int i = 0; i < 100; ++i) {
+ for (int j = 0; j < 100; ++j) {
+ set.insert({i, j});
+ }
+ }
+
+ for (int i = 0; i < 100; ++i) {
+ auto node_handle = set.extract(i);
+ EXPECT_EQ(node_handle.value().i1, i);
+ EXPECT_EQ(node_handle.value().i2, 0) << i;
+ }
+
+ for (int i = 0; i < 100; ++i) {
+ auto node_handle = set.extract(i);
+ EXPECT_EQ(node_handle.value().i1, i);
+ EXPECT_EQ(node_handle.value().i2, 1) << i;
+ }
+}
+
+TYPED_TEST(BtreeMultiKeyTest, Erase) {
+ absl::btree_set<MultiKey, TypeParam> set = {
+ {1, 1}, {2, 1}, {2, 2}, {3, 1}};
+ EXPECT_EQ(set.erase(2), 2);
+ EXPECT_THAT(set, ElementsAre(MultiKey{1, 1}, MultiKey{3, 1}));
+}
+
+TYPED_TEST(BtreeMultiKeyTest, Count) {
+ const absl::btree_set<MultiKey, TypeParam> set = {
+ {1, 1}, {2, 1}, {2, 2}, {3, 1}};
+ EXPECT_EQ(set.count(2), 2);
+}
+
+TEST(Btree, AllocConstructor) {
+ using Alloc = CountingAllocator<int>;
+ using Set = absl::btree_set<int, std::less<int>, Alloc>;
+ int64_t bytes_used = 0;
+ Alloc alloc(&bytes_used);
+ Set set(alloc);
+
+ set.insert({1, 2, 3});
+
+ EXPECT_THAT(set, ElementsAre(1, 2, 3));
+ EXPECT_GT(bytes_used, set.size() * sizeof(int));
+}
+
+TEST(Btree, AllocInitializerListConstructor) {
+ using Alloc = CountingAllocator<int>;
+ using Set = absl::btree_set<int, std::less<int>, Alloc>;
+ int64_t bytes_used = 0;
+ Alloc alloc(&bytes_used);
+ Set set({1, 2, 3}, alloc);
+
+ EXPECT_THAT(set, ElementsAre(1, 2, 3));
+ EXPECT_GT(bytes_used, set.size() * sizeof(int));
+}
+
+TEST(Btree, AllocRangeConstructor) {
+ using Alloc = CountingAllocator<int>;
+ using Set = absl::btree_set<int, std::less<int>, Alloc>;
+ int64_t bytes_used = 0;
+ Alloc alloc(&bytes_used);
+ std::vector<int> v = {1, 2, 3};
+ Set set(v.begin(), v.end(), alloc);
+
+ EXPECT_THAT(set, ElementsAre(1, 2, 3));
+ EXPECT_GT(bytes_used, set.size() * sizeof(int));
+}
+
+TEST(Btree, AllocCopyConstructor) {
+ using Alloc = CountingAllocator<int>;
+ using Set = absl::btree_set<int, std::less<int>, Alloc>;
+ int64_t bytes_used1 = 0;
+ Alloc alloc1(&bytes_used1);
+ Set set1(alloc1);
+
+ set1.insert({1, 2, 3});
+
+ int64_t bytes_used2 = 0;
+ Alloc alloc2(&bytes_used2);
+ Set set2(set1, alloc2);
+
+ EXPECT_THAT(set1, ElementsAre(1, 2, 3));
+ EXPECT_THAT(set2, ElementsAre(1, 2, 3));
+ EXPECT_GT(bytes_used1, set1.size() * sizeof(int));
+ EXPECT_EQ(bytes_used1, bytes_used2);
+}
+
+TEST(Btree, AllocMoveConstructor_SameAlloc) {
+ using Alloc = CountingAllocator<int>;
+ using Set = absl::btree_set<int, std::less<int>, Alloc>;
+ int64_t bytes_used = 0;
+ Alloc alloc(&bytes_used);
+ Set set1(alloc);
+
+ set1.insert({1, 2, 3});
+
+ const int64_t original_bytes_used = bytes_used;
+ EXPECT_GT(original_bytes_used, set1.size() * sizeof(int));
+
+ Set set2(std::move(set1), alloc);
+
+ EXPECT_THAT(set2, ElementsAre(1, 2, 3));
+ EXPECT_EQ(bytes_used, original_bytes_used);
+}
+
+TEST(Btree, AllocMoveConstructor_DifferentAlloc) {
+ using Alloc = CountingAllocator<int>;
+ using Set = absl::btree_set<int, std::less<int>, Alloc>;
+ int64_t bytes_used1 = 0;
+ Alloc alloc1(&bytes_used1);
+ Set set1(alloc1);
+
+ set1.insert({1, 2, 3});
+
+ const int64_t original_bytes_used = bytes_used1;
+ EXPECT_GT(original_bytes_used, set1.size() * sizeof(int));
+
+ int64_t bytes_used2 = 0;
+ Alloc alloc2(&bytes_used2);
+ Set set2(std::move(set1), alloc2);
+
+ EXPECT_THAT(set2, ElementsAre(1, 2, 3));
+ // We didn't free these bytes allocated by `set1` yet.
+ EXPECT_EQ(bytes_used1, original_bytes_used);
+ EXPECT_EQ(bytes_used2, original_bytes_used);
+}
+
} // namespace
} // namespace container_internal
ABSL_NAMESPACE_END