Skip to content

Commit 4800ee2

Browse files
Abseil Teamcopybara-github
authored andcommitted
Implement btree::iterator::+= and -=.
PiperOrigin-RevId: 735878054 Change-Id: I37e0c89b66f5e31376e007dda8d4420a6dfe5269
1 parent 1ecdfbc commit 4800ee2

File tree

5 files changed

+243
-6
lines changed

5 files changed

+243
-6
lines changed

absl/container/btree_benchmark.cc

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ double ContainerInfo(const btree_map<int, BigTypePtr<Size>>& b) {
735735

736736
BIG_TYPE_PTR_BENCHMARKS(32);
737737

738-
void BM_BtreeSet_IteratorSubtraction(benchmark::State& state) {
738+
void BM_BtreeSet_IteratorDifference(benchmark::State& state) {
739739
absl::InsecureBitGen bitgen;
740740
std::vector<int> vec;
741741
// Randomize the set's insertion order so the nodes aren't all full.
@@ -756,6 +756,52 @@ void BM_BtreeSet_IteratorSubtraction(benchmark::State& state) {
756756
}
757757
}
758758

759+
BENCHMARK(BM_BtreeSet_IteratorDifference)->Range(1 << 10, 1 << 20);
760+
761+
void BM_BtreeSet_IteratorAddition(benchmark::State& state) {
762+
absl::InsecureBitGen bitgen;
763+
std::vector<int> vec;
764+
// Randomize the set's insertion order so the nodes aren't all full.
765+
vec.reserve(static_cast<size_t>(state.range(0)));
766+
for (int i = 0; i < state.range(0); ++i) vec.push_back(i);
767+
absl::c_shuffle(vec, bitgen);
768+
769+
absl::btree_set<int> set;
770+
for (int i : vec) set.insert(i);
771+
772+
size_t distance = absl::Uniform(bitgen, 0u, set.size());
773+
while (state.KeepRunningBatch(distance)) {
774+
// Let the increment go all the way to the `end` iterator.
775+
const size_t begin =
776+
absl::Uniform(absl::IntervalClosed, bitgen, 0u, set.size() - distance);
777+
auto it = set.find(static_cast<int>(begin));
778+
benchmark::DoNotOptimize(it += static_cast<int>(distance));
779+
distance = absl::Uniform(bitgen, 0u, set.size());
780+
}
781+
}
782+
783+
BENCHMARK(BM_BtreeSet_IteratorAddition)->Range(1 << 10, 1 << 20);
784+
785+
void BM_BtreeSet_IteratorSubtraction(benchmark::State& state) {
786+
absl::InsecureBitGen bitgen;
787+
std::vector<int> vec;
788+
// Randomize the set's insertion order so the nodes aren't all full.
789+
vec.reserve(static_cast<size_t>(state.range(0)));
790+
for (int i = 0; i < state.range(0); ++i) vec.push_back(i);
791+
absl::c_shuffle(vec, bitgen);
792+
793+
absl::btree_set<int> set;
794+
for (int i : vec) set.insert(i);
795+
796+
size_t distance = absl::Uniform(bitgen, 0u, set.size());
797+
while (state.KeepRunningBatch(distance)) {
798+
size_t end = absl::Uniform(bitgen, distance, set.size());
799+
auto it = set.find(static_cast<int>(end));
800+
benchmark::DoNotOptimize(it -= static_cast<int>(distance));
801+
distance = absl::Uniform(bitgen, 0u, set.size());
802+
}
803+
}
804+
759805
BENCHMARK(BM_BtreeSet_IteratorSubtraction)->Range(1 << 10, 1 << 20);
760806

761807
} // namespace

absl/container/btree_map.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@
4747
// iterator at the current position. Another important difference is that
4848
// key-types must be copy-constructible.
4949
//
50-
// Another API difference is that btree iterators can be subtracted, and this
51-
// is faster than using std::distance.
50+
// There are other API differences: first, btree iterators can be subtracted,
51+
// and this is faster than using `std::distance`. Additionally, btree
52+
// iterators can be advanced via `operator+=` and `operator-=`, which is faster
53+
// than using `std::advance`.
5254
//
5355
// B-tree maps are not exception-safe.
5456

absl/container/btree_set.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@
4646
// reason, `insert()`, `erase()`, and `extract_and_get_next()` return a valid
4747
// iterator at the current position.
4848
//
49-
// Another API difference is that btree iterators can be subtracted, and this
50-
// is faster than using std::distance.
49+
// There are other API differences: first, btree iterators can be subtracted,
50+
// and this is faster than using `std::distance`. Additionally, btree
51+
// iterators can be advanced via `operator+=` and `operator-=`, which is faster
52+
// than using `std::advance`.
5153
//
5254
// B-tree sets are not exception-safe.
5355

absl/container/btree_test.cc

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3351,7 +3351,7 @@ TEST(Btree, ReusePoisonMemory) {
33513351
set.insert(0);
33523352
}
33533353

3354-
TEST(Btree, IteratorSubtraction) {
3354+
TEST(Btree, IteratorDifference) {
33553355
absl::BitGen bitgen;
33563356
std::vector<int> vec;
33573357
// Randomize the set's insertion order so the nodes aren't all full.
@@ -3369,6 +3369,94 @@ TEST(Btree, IteratorSubtraction) {
33693369
}
33703370
}
33713371

3372+
TEST(Btree, IteratorAddition) {
3373+
absl::BitGen bitgen;
3374+
std::vector<int> vec;
3375+
3376+
// Randomize the set's insertion order so the nodes aren't all full.
3377+
constexpr int kSetSize = 1000000;
3378+
for (int i = 0; i < kSetSize; ++i) vec.push_back(i);
3379+
absl::c_shuffle(vec, bitgen);
3380+
3381+
absl::btree_set<int> set;
3382+
for (int i : vec) set.insert(i);
3383+
3384+
for (int i = 0; i < 1000; ++i) {
3385+
int begin = absl::Uniform(bitgen, 0, kSetSize);
3386+
int end = absl::Uniform(bitgen, begin, kSetSize);
3387+
ASSERT_LE(begin, end);
3388+
3389+
auto it = set.find(begin);
3390+
it += end - begin;
3391+
ASSERT_EQ(it, set.find(end)) << end;
3392+
3393+
it += begin - end;
3394+
ASSERT_EQ(it, set.find(begin)) << begin;
3395+
}
3396+
}
3397+
3398+
TEST(Btree, IteratorAdditionOutOfBounds) {
3399+
const absl::btree_set<int> set({5});
3400+
3401+
auto it = set.find(5);
3402+
3403+
auto forward = it;
3404+
forward += 1;
3405+
EXPECT_EQ(forward, set.end());
3406+
3407+
auto backward = it;
3408+
EXPECT_EQ(backward, set.begin());
3409+
3410+
if (IsAssertEnabled()) {
3411+
EXPECT_DEATH(forward += 1, "n == 0");
3412+
EXPECT_DEATH(backward += -1, "position >= node->start");
3413+
}
3414+
}
3415+
3416+
TEST(Btree, IteratorSubtraction) {
3417+
absl::BitGen bitgen;
3418+
std::vector<int> vec;
3419+
3420+
// Randomize the set's insertion order so the nodes aren't all full.
3421+
constexpr int kSetSize = 1000000;
3422+
for (int i = 0; i < kSetSize; ++i) vec.push_back(i);
3423+
absl::c_shuffle(vec, bitgen);
3424+
3425+
absl::btree_set<int> set;
3426+
for (int i : vec) set.insert(i);
3427+
3428+
for (int i = 0; i < 1000; ++i) {
3429+
int begin = absl::Uniform(bitgen, 0, kSetSize);
3430+
int end = absl::Uniform(bitgen, begin, kSetSize);
3431+
ASSERT_LE(begin, end);
3432+
3433+
auto it = set.find(end);
3434+
it -= end - begin;
3435+
ASSERT_EQ(it, set.find(begin)) << begin;
3436+
3437+
it -= begin - end;
3438+
ASSERT_EQ(it, set.find(end)) << end;
3439+
}
3440+
}
3441+
3442+
TEST(Btree, IteratorSubtractionOutOfBounds) {
3443+
const absl::btree_set<int> set({5});
3444+
3445+
auto it = set.find(5);
3446+
3447+
auto backward = it;
3448+
EXPECT_EQ(backward, set.begin());
3449+
3450+
auto forward = it;
3451+
forward -= -1;
3452+
EXPECT_EQ(forward, set.end());
3453+
3454+
if (IsAssertEnabled()) {
3455+
EXPECT_DEATH(backward -= 1, "position >= node->start");
3456+
EXPECT_DEATH(forward -= -1, "n == 0");
3457+
}
3458+
}
3459+
33723460
TEST(Btree, DereferencingEndIterator) {
33733461
if (!IsAssertEnabled()) GTEST_SKIP() << "Assertions not enabled.";
33743462

absl/container/internal/btree.h

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include "absl/base/config.h"
6161
#include "absl/base/internal/raw_logging.h"
6262
#include "absl/base/macros.h"
63+
#include "absl/base/optimization.h"
6364
#include "absl/container/internal/common.h"
6465
#include "absl/container/internal/common_policy_traits.h"
6566
#include "absl/container/internal/compressed_tuple.h"
@@ -708,6 +709,8 @@ class btree_node {
708709
}
709710

710711
// Getter for the parent of this node.
712+
// TODO(ezb): assert that the child of the returned node at position
713+
// `node_->position()` maps to the current node.
711714
btree_node *parent() const { return *GetField<0>(); }
712715
// Getter for whether the node is the root of the tree. The parent of the
713716
// root of the tree is the leftmost node in the tree which is guaranteed to
@@ -1175,6 +1178,26 @@ class btree_iterator : private btree_iterator_generation_info {
11751178
return distance_slow(other);
11761179
}
11771180

1181+
// Advances the iterator by `n`. Values of `n` must not result in going past
1182+
// the `end` iterator (for a positive `n`) or before the `begin` iterator (for
1183+
// a negative `n`).
1184+
btree_iterator &operator+=(difference_type n) {
1185+
assert_valid_generation(node_);
1186+
if (n == 0) return *this;
1187+
if (n < 0) return decrement_n_slow(-n);
1188+
return increment_n_slow(n);
1189+
}
1190+
1191+
// Moves the iterator by `n` positions backwards. Values of `n` must not
1192+
// result in going before the `begin` iterator (for a positive `n`) or past
1193+
// the `end` iterator (for a negative `n`).
1194+
btree_iterator &operator-=(difference_type n) {
1195+
assert_valid_generation(node_);
1196+
if (n == 0) return *this;
1197+
if (n < 0) return increment_n_slow(-n);
1198+
return decrement_n_slow(n);
1199+
}
1200+
11781201
// Accessors for the key/value the iterator is pointing at.
11791202
reference operator*() const {
11801203
ABSL_HARDENING_ASSERT(node_ != nullptr);
@@ -1277,6 +1300,7 @@ class btree_iterator : private btree_iterator_generation_info {
12771300
increment_slow();
12781301
}
12791302
void increment_slow();
1303+
btree_iterator &increment_n_slow(difference_type n);
12801304

12811305
void decrement() {
12821306
assert_valid_generation(node_);
@@ -1286,6 +1310,7 @@ class btree_iterator : private btree_iterator_generation_info {
12861310
decrement_slow();
12871311
}
12881312
void decrement_slow();
1313+
btree_iterator &decrement_n_slow(difference_type n);
12891314

12901315
const key_type &key() const {
12911316
return node_->key(static_cast<size_type>(position_));
@@ -2172,6 +2197,80 @@ void btree_iterator<N, R, P>::decrement_slow() {
21722197
}
21732198
}
21742199

2200+
template <typename N, typename R, typename P>
2201+
btree_iterator<N, R, P> &btree_iterator<N, R, P>::increment_n_slow(
2202+
difference_type n) {
2203+
N *node = node_;
2204+
int position = position_;
2205+
ABSL_ASSUME(n > 0);
2206+
while (n > 0) {
2207+
if (node->is_leaf()) {
2208+
if (position + n < node->finish()) {
2209+
position += n;
2210+
break;
2211+
} else {
2212+
n -= node->finish() - position;
2213+
position = node->finish();
2214+
btree_iterator save = {node, position};
2215+
while (position == node->finish() && !node->is_root()) {
2216+
position = node->position();
2217+
node = node->parent();
2218+
}
2219+
if (position == node->finish()) {
2220+
ABSL_HARDENING_ASSERT(n == 0);
2221+
return *this = save;
2222+
}
2223+
}
2224+
} else {
2225+
--n;
2226+
assert(position < node->finish());
2227+
node = node->child(static_cast<field_type>(position + 1));
2228+
while (node->is_internal()) {
2229+
node = node->start_child();
2230+
}
2231+
position = node->start();
2232+
}
2233+
}
2234+
node_ = node;
2235+
position_ = position;
2236+
return *this;
2237+
}
2238+
2239+
template <typename N, typename R, typename P>
2240+
btree_iterator<N, R, P> &btree_iterator<N, R, P>::decrement_n_slow(
2241+
difference_type n) {
2242+
N *node = node_;
2243+
int position = position_;
2244+
ABSL_ASSUME(n > 0);
2245+
while (n > 0) {
2246+
if (node->is_leaf()) {
2247+
if (position - n >= node->start()) {
2248+
position -= n;
2249+
break;
2250+
} else {
2251+
n -= 1 + position - node->start();
2252+
position = node->start() - 1;
2253+
while (position < node->start() && !node->is_root()) {
2254+
position = node->position() - 1;
2255+
node = node->parent();
2256+
}
2257+
ABSL_HARDENING_ASSERT(position >= node->start());
2258+
}
2259+
} else {
2260+
--n;
2261+
assert(position >= node->start());
2262+
node = node->child(static_cast<field_type>(position));
2263+
while (node->is_internal()) {
2264+
node = node->child(node->finish());
2265+
}
2266+
position = node->finish() - 1;
2267+
}
2268+
}
2269+
node_ = node;
2270+
position_ = position;
2271+
return *this;
2272+
}
2273+
21752274
////
21762275
// btree methods
21772276
template <typename P>

0 commit comments

Comments
 (0)