8298725: Add BitMap support for reverse iteration

Reviewed-by: stefank, aboldtch, tschatzl
This commit is contained in:
Kim Barrett 2023-03-24 18:17:45 +00:00
parent 97649489d0
commit 501b606816
3 changed files with 127 additions and 24 deletions

View File

@ -264,7 +264,7 @@ class BitMap {
void verify_range(idx_t beg, idx_t end) const NOT_DEBUG_RETURN;
// Applies an operation to the index of each set bit in [beg, end), in
// increasing order.
// increasing (decreasing for reverse iteration) order.
//
// If i is an index of the bitmap, the operation is either
// - function(i)
@ -276,8 +276,8 @@ class BitMap {
// an operation returning false.
//
// If an operation modifies the bitmap, modifications to bits at indices
// greater than the current index will affect which further indices the
// operation will be applied to.
// greater than (less than for reverse iteration) the current index will
// affect which further indices the operation will be applied to.
//
// precondition: beg and end form a valid range for the bitmap.
template<typename Function>
@ -296,6 +296,22 @@ class BitMap {
return iterate(cl, 0, size());
}
template<typename Function>
bool reverse_iterate(Function function, idx_t beg, idx_t end) const;
template<typename BitMapClosureType>
bool reverse_iterate(BitMapClosureType* cl, idx_t beg, idx_t end) const;
template<typename Function>
bool reverse_iterate(Function function) const {
return reverse_iterate(function, 0, size());
}
template<typename BitMapClosureType>
bool reverse_iterate(BitMapClosureType* cl) const {
return reverse_iterate(cl, 0, size());
}
// Return the index of the first set (or clear) bit in the range [beg, end),
// or end if none found.
// precondition: beg and end form a valid range for the bitmap.

View File

@ -351,6 +351,25 @@ inline bool BitMap::iterate(BitMapClosureType* cl, idx_t beg, idx_t end) const {
return iterate(function, beg, end);
}
template <typename Function>
inline bool BitMap::reverse_iterate(Function function, idx_t beg, idx_t end) const {
auto invoke = IterateInvoker<decltype(function(beg))>();
for (idx_t index; true; end = index) {
index = find_last_set_bit(beg, end);
if (index >= end) {
return true;
} else if (!invoke(function, index)) {
return false;
}
}
}
template <typename BitMapClosureType>
inline bool BitMap::reverse_iterate(BitMapClosureType* cl, idx_t beg, idx_t end) const {
auto function = [&](idx_t index) { return cl->do_bit(index); };
return reverse_iterate(function, beg, end);
}
// Returns a bit mask for a range of bits [beg, end) within a single word. Each
// bit in the mask is 0 if the bit is in the range, 1 if not in the range. The
// returned mask can be used directly to clear the range, or inverted to set the

View File

@ -59,31 +59,81 @@ static void test_iterate_lambda(const BitMap& map,
ASSERT_EQ(positions_index, positions_size);
}
static void test_reverse_iterate_lambda(const BitMap& map,
const idx_t* positions,
size_t positions_size) {
SCOPED_TRACE("reverse iterate with lambda");
size_t positions_index = positions_size;
auto f = [&](idx_t i) {
test_iterate_step(map, i, positions, --positions_index, positions_size);
};
ASSERT_TRUE(map.reverse_iterate(f));
ASSERT_EQ(positions_index, 0u);
}
struct TestBitMapIterationData {
const BitMap& _map;
const idx_t* _positions;
size_t _positions_index;
size_t _positions_size;
TestBitMapIterationData(const BitMap& map,
const idx_t* positions,
size_t positions_index,
size_t positions_size)
: _map(map),
_positions(positions),
_positions_index(positions_index),
_positions_size(positions_size)
{}
void test(idx_t index) const {
test_iterate_step(_map, index, _positions, _positions_index, _positions_size);
}
};
// Test closure returning bool. Also tests lambda returning bool.
static void test_iterate_closure(const BitMap& map,
const idx_t* positions,
size_t positions_size) {
SCOPED_TRACE("iterate with BitMapClosure");
struct Closure : public BitMapClosure {
const BitMap& _map;
const idx_t* _positions;
size_t _positions_index;
size_t _positions_size;
TestBitMapIterationData _data;
Closure(const BitMap& map, const idx_t* positions, size_t positions_size)
: _map(map),
_positions(positions),
_positions_index(0),
_positions_size(positions_size)
: _data(map, positions, 0, positions_size)
{}
bool do_bit(idx_t i) override {
test_iterate_step(_map, i, _positions, _positions_index++, _positions_size);
_data.test(i);
_data._positions_index += 1;
return true;
}
} closure{map, positions, positions_size};
ASSERT_TRUE(map.iterate(&closure));
ASSERT_EQ(closure._positions_index, positions_size);
ASSERT_EQ(closure._data._positions_index, positions_size);
}
static void test_reverse_iterate_closure(const BitMap& map,
const idx_t* positions,
size_t positions_size) {
SCOPED_TRACE("reverse iterate with BitMapClosure");
struct Closure : public BitMapClosure {
TestBitMapIterationData _data;
Closure(const BitMap& map, const idx_t* positions, size_t positions_size)
: _data(map, positions, positions_size, positions_size)
{}
bool do_bit(idx_t i) override {
_data._positions_index -= 1;
_data.test(i);
return true;
}
} closure{map, positions, positions_size};
ASSERT_TRUE(map.reverse_iterate(&closure));
ASSERT_EQ(closure._data._positions_index, 0u);
}
// Test closure returning void. Also tests lambda returning bool.
@ -92,24 +142,37 @@ static void test_iterate_non_closure(const BitMap& map,
size_t positions_size) {
SCOPED_TRACE("iterate with non-BitMapClosure");
struct Closure {
const BitMap& _map;
const idx_t* _positions;
size_t _positions_index;
size_t _positions_size;
TestBitMapIterationData _data;
Closure(const BitMap& map, const idx_t* positions, size_t positions_size)
: _map(map),
_positions(positions),
_positions_index(0),
_positions_size(positions_size)
: _data(map, positions, 0, positions_size)
{}
void do_bit(idx_t i) {
test_iterate_step(_map, i, _positions, _positions_index++, _positions_size);
_data.test(i);
_data._positions_index += 1;
}
} closure{map, positions, positions_size};
ASSERT_TRUE(map.iterate(&closure));
ASSERT_EQ(closure._positions_index, positions_size);
ASSERT_EQ(closure._data._positions_index, positions_size);
}
static void test_reverse_iterate_non_closure(const BitMap& map,
const idx_t* positions,
size_t positions_size) {
SCOPED_TRACE("reverse iterate with non-BitMapClosure");
struct Closure {
TestBitMapIterationData _data;
Closure(const BitMap& map, const idx_t* positions, size_t positions_size)
: _data(map, positions, positions_size, positions_size)
{}
void do_bit(idx_t i) {
_data._positions_index -= 1;
_data.test(i);
}
} closure{map, positions, positions_size};
ASSERT_TRUE(map.reverse_iterate(&closure));
ASSERT_EQ(closure._data._positions_index, 0u);
}
static void fill_iterate_map(BitMap& map,
@ -125,9 +188,14 @@ static void test_iterate(BitMap& map,
const idx_t* positions,
size_t positions_size) {
fill_iterate_map(map, positions, positions_size);
test_iterate_lambda(map, positions, positions_size);
test_iterate_closure(map, positions, positions_size);
test_iterate_non_closure(map, positions, positions_size);
test_reverse_iterate_lambda(map, positions, positions_size);
test_reverse_iterate_closure(map, positions, positions_size);
test_reverse_iterate_non_closure(map, positions, positions_size);
}
TEST(BitMap, iterate_empty) {