Skip to content

Commit 60fda1d

Browse files
committed
Merge PR ceph#62200 into main
* refs/pull/62200/head: common/include: Add map type election and fmt::format to interval_map Reviewed-by: Patrick Donnelly <[email protected]>
2 parents 88d2ce7 + 96be3f1 commit 60fda1d

File tree

4 files changed

+152
-46
lines changed

4 files changed

+152
-46
lines changed

src/common/interval_map.h

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include "include/interval_set.h"
1919
#include <initializer_list>
2020

21-
template <typename K, typename V, typename S>
2221
/**
2322
* interval_map
2423
*
@@ -31,13 +30,14 @@ template <typename K, typename V, typename S>
3130
* commutativity, which doesn't work if we want more recent insertions
3231
* to overwrite previous ones.
3332
*/
33+
template <typename K, typename V, typename S, template<typename, typename, typename ...> class C = std::map>
3434
class interval_map {
3535
S s;
36-
using map = std::map<K, std::pair<K, V> >;
37-
using mapiter = typename std::map<K, std::pair<K, V> >::iterator;
38-
using cmapiter = typename std::map<K, std::pair<K, V> >::const_iterator;
39-
map m;
40-
std::pair<mapiter, mapiter> get_range(K off, K len) {
36+
using Map = C<K, std::pair<K, V> >;
37+
using Mapiter = typename Map::iterator;
38+
using Cmapiter = typename Map::const_iterator;
39+
Map m;
40+
std::pair<Mapiter, Mapiter> get_range(K off, K len) {
4141
// fst is first iterator with end after off (may be end)
4242
auto fst = m.upper_bound(off);
4343
if (fst != m.begin())
@@ -49,15 +49,15 @@ class interval_map {
4949
auto lst = m.lower_bound(off + len);
5050
return std::make_pair(fst, lst);
5151
}
52-
std::pair<cmapiter, cmapiter> get_range(K off, K len) const {
52+
std::pair<Cmapiter, Cmapiter> get_range(K off, K len) const {
5353
// fst is first iterator with end after off (may be end)
5454
auto fst = get_range_fst(off);
5555

5656
// lst is first iterator with start after off + len (may be end)
5757
auto lst = m.lower_bound(off + len);
5858
return std::make_pair(fst, lst);
5959
}
60-
cmapiter get_range_fst(K off) const {
60+
Cmapiter get_range_fst(K off) const {
6161
// fst is first iterator with end after off (may be end)
6262
auto fst = m.upper_bound(off);
6363
if (fst != m.begin())
@@ -67,7 +67,7 @@ class interval_map {
6767

6868
return fst;
6969
}
70-
void try_merge(mapiter niter) {
70+
void try_merge(Mapiter niter) {
7171
if (niter != m.begin()) {
7272
auto prev = niter;
7373
prev--;
@@ -109,7 +109,7 @@ class interval_map {
109109
}
110110
public:
111111
interval_map() = default;
112-
interval_map(std::initializer_list<typename map::value_type> l) {
112+
interval_map(std::initializer_list<typename Map::value_type> l) {
113113
for (auto& v : l) {
114114
insert(v.first, v.second.first, v.second.second);
115115
}
@@ -202,17 +202,23 @@ class interval_map {
202202
bool empty() const {
203203
return m.empty();
204204
}
205-
interval_set<K> get_interval_set() const {
206-
interval_set<K> ret;
205+
interval_set<K, C> get_interval_set() const {
206+
interval_set<K, C> ret;
207207
for (auto &&i: *this) {
208208
ret.insert(i.get_off(), i.get_len());
209209
}
210210
return ret;
211211
}
212+
template<bool strict = true>
213+
void to_interval_set(interval_set<K, C, strict> &set) const {
214+
for (auto &&i: *this) {
215+
set.insert(i.get_off(), i.get_len());
216+
}
217+
}
212218
class const_iterator {
213-
cmapiter it;
214-
const_iterator(cmapiter &&it) : it(std::move(it)) {}
215-
const_iterator(const cmapiter &it) : it(it) {}
219+
Cmapiter it;
220+
const_iterator(Cmapiter &&it) : it(std::move(it)) {}
221+
const_iterator(const Cmapiter &it) : it(it) {}
216222

217223
friend class interval_map;
218224
public:
@@ -302,25 +308,41 @@ class interval_map {
302308
return m == rhs.m;
303309
}
304310

305-
std::ostream &print(std::ostream &out) const {
311+
void print(std::ostream &os) const {
306312
bool first = true;
307-
out << "{";
313+
os << "{";
308314
for (auto &&i: *this) {
309315
if (first) {
310316
first = false;
311317
} else {
312-
out << ",";
318+
os << ",";
313319
}
314-
out << i.get_off() << "~" << i.get_len() << "("
320+
os << i.get_off() << "~" << i.get_len() << "("
315321
<< s.length(i.get_val()) << ")";
316322
}
317-
return out << "}";
323+
os << "}";
324+
}
325+
326+
std::string fmt_print() const
327+
requires has_formatter<K> {
328+
std::string str = "{";
329+
bool first = true;
330+
for (auto &&i: *this) {
331+
if (first) {
332+
first = false;
333+
} else {
334+
str += ",";
335+
}
336+
str += fmt::format("{}~{}({})", i.get_off(), i.get_len(),
337+
s.length(i.get_val()));
338+
}
339+
str += "}";
340+
return str;
318341
}
319342
};
320343

321-
template <typename K, typename V, typename S>
322-
std::ostream &operator<<(std::ostream &out, const interval_map<K, V, S> &m) {
323-
return m.print(out);
324-
}
344+
// make sure fmt::range would not try (and fail) to treat interval_map as a range
345+
template <typename K, typename V, typename S, template<typename, typename, typename ...> class C>
346+
struct fmt::is_range<interval_map<K, V, S, C>, char> : std::false_type {};
325347

326348
#endif

src/include/interval_set.h

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
#include <iterator>
2020
#include <map>
2121
#include <ostream>
22+
#include <fmt/ranges.h>
23+
#include "common/fmt_common.h"
24+
#include "common/dout.h"
2225

2326
#include "encoding.h"
2427

@@ -246,6 +249,35 @@ class interval_set {
246249
return const_iterator(m.end());
247250
}
248251

252+
void print(std::ostream& os) const {
253+
os << "[";
254+
bool first = true;
255+
for (const auto& [start, len] : *this) {
256+
if (!first) {
257+
os << ",";
258+
}
259+
os << start << "~" << len;
260+
first = false;
261+
}
262+
os << "]";
263+
}
264+
265+
std::string fmt_print() const
266+
requires has_formatter<T> {
267+
std::string s = "[";
268+
bool first = true;
269+
for (const auto& [start, len] : *this) {
270+
if (!first) {
271+
s += ",";
272+
} else {
273+
first = false;
274+
}
275+
s += fmt::format("{}~{}", start, len);
276+
}
277+
s += "]";
278+
return s;
279+
}
280+
249281
// helpers
250282
private:
251283
auto find_inc(T start) const {
@@ -963,19 +995,8 @@ struct denc_traits<interval_set<T, C>> {
963995
}
964996
};
965997

966-
967-
template<typename T, template<typename, typename, typename ...> class C>
968-
inline std::ostream& operator<<(std::ostream& out, const interval_set<T,C> &s) {
969-
out << "[";
970-
bool first = true;
971-
for (const auto& [start, len] : s) {
972-
if (!first) out << ",";
973-
out << start << "~" << len;
974-
first = false;
975-
}
976-
out << "]";
977-
return out;
978-
}
979-
998+
// make sure fmt::range would not try (and fail) to treat interval_set as a range
999+
template<typename T, template<typename, typename, typename ...> class C, bool strict>
1000+
struct fmt::is_range<interval_set<T, C, strict>, char> : std::false_type {};
9801001

9811002
#endif

src/test/common/test_interval_map.cc

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

30-
template <typename _key>
30+
template <typename _key, typename _can_merge, template<typename, typename, typename ...> class _map = std::map>
3131
struct bufferlist_test_type {
3232
using key = _key;
3333
using value = bufferlist;
3434

35+
static constexpr bool can_merge() {
36+
return _can_merge::value;
37+
}
38+
3539
struct make_splitter {
3640
template <typename merge_t>
3741
struct apply {
@@ -57,6 +61,9 @@ struct bufferlist_test_type {
5761
};
5862
};
5963

64+
using splitter = boost::mpl::apply<make_splitter, _can_merge>;
65+
using imap = interval_map<key, value, splitter, _map>;
66+
6067
struct generate_random {
6168
bufferlist operator()(key len) {
6269
bufferlist bl;
@@ -70,21 +77,27 @@ struct bufferlist_test_type {
7077
};
7178
};
7279

73-
using IntervalMapTypes = ::testing::Types< bufferlist_test_type<uint64_t> >;
80+
using IntervalMapTypes = ::testing::Types<
81+
bufferlist_test_type<uint64_t, std::false_type>,
82+
bufferlist_test_type<uint64_t, std::true_type>,
83+
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>
85+
>;
7486

7587
TYPED_TEST_SUITE(IntervalMapTest, IntervalMapTypes);
7688

89+
// Someone with more C++ foo can probably figure out a better way of doing this.
90+
// However, for now, we skip some tests if using the wrong merge.
7791
#define USING(_can_merge) \
7892
using TT = typename TestFixture::TestType; \
7993
using key = typename TT::key; (void)key(0); \
8094
using val = typename TT::value; (void)val(0); \
81-
using splitter = typename boost::mpl::apply< \
82-
typename TT::make_splitter, \
83-
_can_merge>; \
84-
using imap = interval_map<key, val, splitter>; (void)imap(); \
95+
using splitter = TT::splitter; \
96+
using imap = TT::imap; (void)imap(); \
8597
typename TT::generate_random gen; \
8698
val v(gen(5)); \
87-
splitter split; (void)split.split(0, 0, v);
99+
splitter split; (void)split.split(0, 0, v); \
100+
{ _can_merge cm; if(TT::can_merge() != cm()) return; }
88101

89102
#define USING_NO_MERGE USING(std::false_type)
90103
#define USING_WITH_MERGE USING(std::true_type)
@@ -371,4 +384,24 @@ TYPED_TEST(IntervalMapTest, get_start_end_off)
371384
m.insert(20,5, gen(5));
372385
ASSERT_EQ(5, m.get_start_off());
373386
ASSERT_EQ(25, m.get_end_off());
374-
}
387+
}
388+
389+
TYPED_TEST(IntervalMapTest, print) {
390+
USING_NO_MERGE;
391+
imap m;
392+
393+
{
394+
std::ostringstream out;
395+
m.insert(0, 5, gen(5));
396+
out << m;
397+
ASSERT_EQ("{0~5(5)}", fmt::format("{}", m));
398+
EXPECT_EQ("{0~5(5)}", out.str() );
399+
}
400+
{
401+
std::ostringstream out;
402+
m.insert(10, 5, gen(5));
403+
out << m;
404+
ASSERT_EQ("{0~5(5),10~5(5)}", fmt::format("{}", m));
405+
EXPECT_EQ("{0~5(5),10~5(5)}", out.str() );
406+
}
407+
}

src/test/common/test_interval_set.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1859,4 +1859,34 @@ TYPED_TEST(IntervalSetTest, subtract) {
18591859

18601860
ASSERT_STRICT_DEATH(iset1.subtract(iset2), ASSERT_EQ(iset1, iset3));
18611861
}
1862+
1863+
// Subtract identical
1864+
{
1865+
ISet iset1, iset2;
1866+
iset1.union_insert(0, 5);
1867+
iset2.union_insert(0, 5);
1868+
1869+
iset1.subtract(iset2);
1870+
ASSERT_TRUE(iset1.empty());
1871+
}
1872+
}
1873+
1874+
TYPED_TEST(IntervalSetTest, print) {
1875+
typedef typename TestFixture::ISet ISet;
1876+
1877+
ISet iset;
1878+
{
1879+
std::ostringstream out;
1880+
iset.insert(0, 5);
1881+
out << iset;
1882+
ASSERT_EQ("[0~5]", fmt::format("{}", iset));
1883+
EXPECT_EQ("[0~5]", out.str() );
1884+
}
1885+
{
1886+
std::ostringstream out;
1887+
iset.insert(10, 5);
1888+
out << iset;
1889+
ASSERT_EQ("[0~5,10~5]", fmt::format("{}", iset));
1890+
EXPECT_EQ("[0~5,10~5]", out.str() );
1891+
}
18621892
}

0 commit comments

Comments
 (0)