Skip to content

Commit 3d604bc

Browse files
ezbrcopybara-github
authored andcommitted
Add tests for btrees in which slot_type is overaligned and slot_type is equal to field_type.
Also do some minor refactoring in btree.h. PiperOrigin-RevId: 529460378 Change-Id: I278833ada93bbb7652e149fceed08ce3485e4312
1 parent 2585295 commit 3d604bc

File tree

2 files changed

+68
-34
lines changed

2 files changed

+68
-34
lines changed

absl/container/btree_test.cc

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,8 +1233,10 @@ class BtreeNodePeer {
12331233
}
12341234

12351235
template <typename Btree>
1236-
constexpr static bool UsesGenerations() {
1237-
return Btree::params_type::kEnableGenerations;
1236+
constexpr static bool FieldTypeEqualsSlotType() {
1237+
return std::is_same<
1238+
typename btree_node<typename Btree::params_type>::field_type,
1239+
typename btree_node<typename Btree::params_type>::slot_type>::value;
12381240
}
12391241
};
12401242

@@ -1463,7 +1465,7 @@ class SizedBtreeSet
14631465
using Base = typename SizedBtreeSet::btree_set_container;
14641466

14651467
public:
1466-
SizedBtreeSet() {}
1468+
SizedBtreeSet() = default;
14671469
using Base::Base;
14681470
};
14691471

@@ -1509,10 +1511,9 @@ TEST(Btree, MovesComparisonsCopiesSwapsTracking) {
15091511
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>(), 61);
15101512
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set100)>(), 100);
15111513
if (sizeof(void *) == 8) {
1512-
EXPECT_EQ(
1513-
BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(),
1514-
// When we have generations, there is one fewer slot.
1515-
BtreeNodePeer::UsesGenerations<absl::btree_set<int32_t>>() ? 60 : 61);
1514+
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(),
1515+
// When we have generations, there is one fewer slot.
1516+
BtreeGenerationsEnabled() ? 60 : 61);
15161517
}
15171518

15181519
// Test key insertion/deletion in random order.
@@ -1568,10 +1569,9 @@ TEST(Btree, MovesComparisonsCopiesSwapsTrackingThreeWayCompare) {
15681569
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set61)>(), 61);
15691570
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<decltype(set100)>(), 100);
15701571
if (sizeof(void *) == 8) {
1571-
EXPECT_EQ(
1572-
BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(),
1573-
// When we have generations, there is one fewer slot.
1574-
BtreeNodePeer::UsesGenerations<absl::btree_set<int32_t>>() ? 60 : 61);
1572+
EXPECT_EQ(BtreeNodePeer::GetNumSlotsPerNode<absl::btree_set<int32_t>>(),
1573+
// When we have generations, there is one fewer slot.
1574+
BtreeGenerationsEnabled() ? 60 : 61);
15751575
}
15761576

15771577
// Test key insertion/deletion in random order.
@@ -3226,7 +3226,7 @@ TEST(Btree, MutatedKeysCaught) {
32263226
#ifndef _MSC_VER
32273227
// This test crashes on MSVC.
32283228
TEST(Btree, InvalidIteratorUse) {
3229-
if (!BtreeNodePeer::UsesGenerations<absl::btree_set<int>>())
3229+
if (!BtreeGenerationsEnabled())
32303230
GTEST_SKIP() << "Generation validation for iterators is disabled.";
32313231

32323232
// Invalid memory use can trigger heap-use-after-free in ASan or invalidated
@@ -3569,6 +3569,41 @@ TEST(Btree, InvalidPointerUse) {
35693569
EXPECT_DEATH(std::cout << *ptr, "heap-use-after-free");
35703570
}
35713571

3572+
template<typename Set>
3573+
void TestBasicFunctionality(Set set) {
3574+
using value_type = typename Set::value_type;
3575+
for (int i = 0; i < 100; ++i) { set.insert(value_type(i)); }
3576+
for (int i = 50; i < 100; ++i) { set.erase(value_type(i)); }
3577+
auto it = set.begin();
3578+
for (int i = 0; i < 50; ++i, ++it) {
3579+
ASSERT_EQ(set.find(value_type(i)), it) << i;
3580+
}
3581+
}
3582+
3583+
template<size_t align>
3584+
struct alignas(align) OveralignedKey {
3585+
explicit OveralignedKey(int i) : key(i) {}
3586+
bool operator<(const OveralignedKey &other) const { return key < other.key; }
3587+
int key = 0;
3588+
};
3589+
3590+
TEST(Btree, OveralignedKey) {
3591+
// Test basic functionality with both even and odd numbers of slots per node.
3592+
// The goal here is to detect cases where alignment may be incorrect.
3593+
TestBasicFunctionality(
3594+
SizedBtreeSet<OveralignedKey<16>, /*TargetValuesPerNode=*/8>());
3595+
TestBasicFunctionality(
3596+
SizedBtreeSet<OveralignedKey<16>, /*TargetValuesPerNode=*/9>());
3597+
}
3598+
3599+
TEST(Btree, FieldTypeEqualsSlotType) {
3600+
// This breaks if we try to do layout_type::Pointer<slot_type> because
3601+
// slot_type is the same as field_type.
3602+
using set_type = absl::btree_set<uint8_t>;
3603+
static_assert(BtreeNodePeer::FieldTypeEqualsSlotType<set_type>(), "");
3604+
TestBasicFunctionality(set_type());
3605+
}
3606+
35723607
} // namespace
35733608
} // namespace container_internal
35743609
ABSL_NAMESPACE_END

absl/container/internal/btree.h

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ namespace container_internal {
8686
#define ABSL_BTREE_ENABLE_GENERATIONS
8787
#endif
8888

89+
#ifdef ABSL_BTREE_ENABLE_GENERATIONS
90+
constexpr bool BtreeGenerationsEnabled() { return true; }
91+
#else
92+
constexpr bool BtreeGenerationsEnabled() { return false; }
93+
#endif
94+
8995
template <typename Compare, typename T, typename U>
9096
using compare_result_t = absl::result_of_t<const Compare(const T &, const U &)>;
9197

@@ -378,12 +384,6 @@ struct common_params : common_policy_traits<SlotPolicy> {
378384
std::is_same<key_compare, StringBtreeDefaultGreater>::value;
379385
static constexpr bool kIsKeyCompareTransparent =
380386
IsTransparent<original_key_compare>::value || kIsKeyCompareStringAdapted;
381-
static constexpr bool kEnableGenerations =
382-
#ifdef ABSL_BTREE_ENABLE_GENERATIONS
383-
true;
384-
#else
385-
false;
386-
#endif
387387

388388
// A type which indicates if we have a key-compare-to functor or a plain old
389389
// key-compare functor.
@@ -589,7 +589,7 @@ class btree_node {
589589
constexpr static size_type SizeWithNSlots(size_type n) {
590590
return layout_type(
591591
/*parent*/ 1,
592-
/*generation*/ params_type::kEnableGenerations ? 1 : 0,
592+
/*generation*/ BtreeGenerationsEnabled() ? 1 : 0,
593593
/*position, start, finish, max_count*/ 4,
594594
/*slots*/ n,
595595
/*children*/ 0)
@@ -629,23 +629,22 @@ class btree_node {
629629
// has this value.
630630
constexpr static field_type kInternalNodeMaxCount = 0;
631631

632-
// Leaves can have less than kNodeSlots values.
633-
constexpr static layout_type LeafLayout(
634-
const size_type slot_count = kNodeSlots) {
632+
constexpr static layout_type Layout(const size_type slot_count,
633+
const size_type child_count) {
635634
return layout_type(
636635
/*parent*/ 1,
637-
/*generation*/ params_type::kEnableGenerations ? 1 : 0,
636+
/*generation*/ BtreeGenerationsEnabled() ? 1 : 0,
638637
/*position, start, finish, max_count*/ 4,
639638
/*slots*/ slot_count,
640-
/*children*/ 0);
639+
/*children*/ child_count);
640+
}
641+
// Leaves can have less than kNodeSlots values.
642+
constexpr static layout_type LeafLayout(
643+
const size_type slot_count = kNodeSlots) {
644+
return Layout(slot_count, 0);
641645
}
642646
constexpr static layout_type InternalLayout() {
643-
return layout_type(
644-
/*parent*/ 1,
645-
/*generation*/ params_type::kEnableGenerations ? 1 : 0,
646-
/*position, start, finish, max_count*/ 4,
647-
/*slots*/ kNodeSlots,
648-
/*children*/ kNodeSlots + 1);
647+
return Layout(kNodeSlots, kNodeSlots + 1);
649648
}
650649
constexpr static size_type LeafSize(const size_type slot_count = kNodeSlots) {
651650
return LeafLayout(slot_count).AllocSize();
@@ -729,24 +728,24 @@ class btree_node {
729728

730729
// Gets the root node's generation integer, which is the one used by the tree.
731730
uint32_t *get_root_generation() const {
732-
assert(params_type::kEnableGenerations);
731+
assert(BtreeGenerationsEnabled());
733732
const btree_node *curr = this;
734733
for (; !curr->is_root(); curr = curr->parent()) continue;
735734
return const_cast<uint32_t *>(&curr->GetField<1>()[0]);
736735
}
737736

738737
// Returns the generation for iterator validation.
739738
uint32_t generation() const {
740-
return params_type::kEnableGenerations ? *get_root_generation() : 0;
739+
return BtreeGenerationsEnabled() ? *get_root_generation() : 0;
741740
}
742741
// Updates generation. Should only be called on a root node or during node
743742
// initialization.
744743
void set_generation(uint32_t generation) {
745-
if (params_type::kEnableGenerations) GetField<1>()[0] = generation;
744+
if (BtreeGenerationsEnabled()) GetField<1>()[0] = generation;
746745
}
747746
// Updates the generation. We do this whenever the node is mutated.
748747
void next_generation() {
749-
if (params_type::kEnableGenerations) ++*get_root_generation();
748+
if (BtreeGenerationsEnabled()) ++*get_root_generation();
750749
}
751750

752751
// Getters for the key/value at position i in the node.

0 commit comments

Comments
 (0)