Skip to content

Commit 649f11d

Browse files
authored
Merge pull request ceph#63844 from aainscow/internal_map_non_const_iters
interval_map: Add non-const iterators Reviewed-by: Radoslaw Zarzynski <[email protected]>
2 parents 94b8a15 + 9859e8d commit 649f11d

File tree

3 files changed

+106
-9
lines changed

3 files changed

+106
-9
lines changed

src/common/interval_map.h

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
* commutativity, which doesn't work if we want more recent insertions
3131
* to overwrite previous ones.
3232
*/
33-
template <typename K, typename V, typename S, template<typename, typename, typename ...> class C = std::map>
33+
template <typename K, typename V, typename S, template<typename, typename, typename ...> class C = std::map, bool nonconst_iterator = false>
3434
class interval_map {
3535
S s;
3636
using Map = C<K, std::pair<K, V> >;
@@ -215,6 +215,7 @@ class interval_map {
215215
set.insert(i.get_off(), i.get_len());
216216
}
217217
}
218+
218219
class const_iterator {
219220
Cmapiter it;
220221
const_iterator(Cmapiter &&it) : it(std::move(it)) {}
@@ -269,6 +270,77 @@ class interval_map {
269270
const_iterator end() const {
270271
return const_iterator(m.end());
271272
}
273+
274+
const_iterator cbegin() const {
275+
return const_iterator(m.begin());
276+
}
277+
const_iterator cend() const {
278+
return const_iterator(m.end());
279+
}
280+
281+
class iterator {
282+
Mapiter it;
283+
bool end;
284+
iterator(Mapiter &&it, bool end) : it(std::move(it)), end(end) {}
285+
286+
friend class interval_map;
287+
public:
288+
iterator(const iterator &) = default;
289+
iterator &operator=(const iterator &) = default;
290+
291+
iterator &operator++() {
292+
/* While the buffer can be modified with a non-const iterator, it
293+
* not change size. Allow changes in size would allow for the interval
294+
* to be merged into the next one, which would allow for unexpected
295+
* behaviour.
296+
*/
297+
if (!end && get_val().length() != get_len()) {
298+
throw std::out_of_range("buffer length has changed");
299+
}
300+
++it;
301+
return *this;
302+
}
303+
iterator operator++(int) {
304+
return const_iterator(it++);
305+
}
306+
iterator &operator--() {
307+
--it;
308+
return *this;
309+
}
310+
iterator operator--(int) {
311+
return const_iterator(it--);
312+
}
313+
bool operator==(const iterator &rhs) const {
314+
return it == rhs.it;
315+
}
316+
bool operator!=(const iterator &rhs) const {
317+
return it != rhs.it;
318+
}
319+
K get_off() const {
320+
return it->first;
321+
}
322+
K get_len() const {
323+
return it->second.first;
324+
}
325+
V &get_val() {
326+
return it->second.second;
327+
}
328+
iterator &operator*() {
329+
return *this;
330+
}
331+
constexpr bool contains(K _off, K _len) const {
332+
K off = get_off();
333+
K len = get_len();
334+
return off <= _off && _off + _len <= off + len;
335+
}
336+
};
337+
static constexpr bool nonconst_iterator_cond() { return nonconst_iterator; }
338+
iterator begin() requires (nonconst_iterator) {
339+
return iterator(m.begin(), false);
340+
}
341+
iterator end() requires (nonconst_iterator) {
342+
return iterator(m.end(), true);
343+
}
272344
std::pair<const_iterator, const_iterator> get_containing_range(
273345
K off,
274346
K len) const {

src/osd/ECUtil.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ struct bl_split_merge {
5858

5959
using extent_set = interval_set<uint64_t, boost::container::flat_map, false>;
6060
using extent_map = interval_map<uint64_t, ceph::buffer::list, bl_split_merge,
61-
boost::container::flat_map>;
61+
boost::container::flat_map, true>;
6262

6363
/* Slice iterator. This looks for contiguous buffers which are common
6464
* across all shards in the out_set.
@@ -74,8 +74,8 @@ class slice_iterator {
7474
uint64_t length = std::numeric_limits<uint64_t>::max();
7575
uint64_t start = std::numeric_limits<uint64_t>::max();
7676
uint64_t end = std::numeric_limits<uint64_t>::max();
77-
shard_id_map<std::pair<extent_map::const_iterator,
78-
bufferlist::const_iterator>> iters;
77+
shard_id_map<std::pair<extent_map::iterator,
78+
bufferlist::iterator>> iters;
7979
shard_id_map<bufferptr> in;
8080
shard_id_map<bufferptr> out;
8181
const shard_id_set &out_set;
@@ -992,10 +992,10 @@ class shard_extent_map_t {
992992
size_t shard_count() { return extent_maps.size(); }
993993

994994

995-
void assert_buffer_contents_equal(shard_extent_map_t other) const {
995+
void assert_buffer_contents_equal(shard_extent_map_t other) {
996996
for (auto &&[shard, emap] : extent_maps) {
997997
for (auto &&i : emap) {
998-
bufferlist bl = i.get_val();
998+
bufferlist &bl = i.get_val();
999999
bufferlist otherbl;
10001000
other.get_buffer(shard, i.get_off(), i.get_len(), otherbl);
10011001
ceph_assert(bl.contents_equal(otherbl));

src/test/common/test_interval_map.cc

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class IntervalMapTest : public ::testing::Test {
2727
using TestType = T;
2828
};
2929

30-
template <typename _key, typename _can_merge, template<typename, typename, typename ...> class _map = std::map>
30+
template <typename _key, typename _can_merge, template<typename, typename, typename ...> class _map = std::map, bool _nonconst_iterator = false>
3131
struct bufferlist_test_type {
3232
using key = _key;
3333
using value = bufferlist;
@@ -62,7 +62,7 @@ struct bufferlist_test_type {
6262
};
6363

6464
using splitter = boost::mpl::apply<make_splitter, _can_merge>;
65-
using imap = interval_map<key, value, splitter, _map>;
65+
using imap = interval_map<key, value, splitter, _map, _nonconst_iterator>;
6666

6767
struct generate_random {
6868
bufferlist operator()(key len) {
@@ -81,7 +81,8 @@ using IntervalMapTypes = ::testing::Types<
8181
bufferlist_test_type<uint64_t, std::false_type>,
8282
bufferlist_test_type<uint64_t, std::true_type>,
8383
bufferlist_test_type<uint64_t, std::false_type, boost::container::flat_map>,
84-
bufferlist_test_type<uint64_t, std::true_type, boost::container::flat_map>
84+
bufferlist_test_type<uint64_t, std::true_type, boost::container::flat_map>,
85+
bufferlist_test_type<uint64_t, std::true_type, boost::container::flat_map, true>
8586
>;
8687

8788
TYPED_TEST_SUITE(IntervalMapTest, IntervalMapTypes);
@@ -405,3 +406,27 @@ TYPED_TEST(IntervalMapTest, print) {
405406
EXPECT_EQ("{0~5(5),10~5(5)}", out.str() );
406407
}
407408
}
409+
410+
/* This test does nothing unless nonconst_iterator is set on the interval map
411+
* If it is set, then the simple fact that append_zero() compiles means that
412+
* the non-const iterator has been enabled and used. The test then checks that
413+
* changing the size of a buffer is illegal.
414+
*/
415+
TYPED_TEST(IntervalMapTest, append_buffer_in_loop) {
416+
USING_WITH_MERGE;
417+
imap m;
418+
if constexpr (m.nonconst_iterator_cond()) {
419+
m.insert(0, 5, gen(5));
420+
try {
421+
for (auto && i : m) {
422+
i.get_val().append_zero(10);
423+
}
424+
FAIL(); // Should panic
425+
} catch (const std::out_of_range& exception) {
426+
ASSERT_EQ("buffer length has changed"sv, exception.what());
427+
} catch (const std::exception&) {
428+
// Wrong exception.
429+
FAIL();
430+
}
431+
}
432+
}

0 commit comments

Comments
 (0)