]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/perf_counters_key.cc
update ceph source to reef 18.2.0
[ceph.git] / ceph / src / common / perf_counters_key.cc
CommitLineData
1e59de90
TL
1#include "common/perf_counters_key.h"
2
3#include <algorithm>
4#include <iterator>
5#include <numeric>
6
7namespace ceph::perf_counters {
8namespace detail {
9
10// use a null character to delimit strings
11constexpr char DELIMITER = '\0';
12
13
14// write a delimited string to the output
15auto write(std::string_view str, std::output_iterator<char> auto out)
16{
17 out = std::copy(str.begin(), str.end(), out);
18 *(out++) = DELIMITER;
19 return out;
20}
21
22// return the encoded size of a label
23inline std::size_t label_size(const label_pair& l)
24{
25 return l.first.size() + sizeof(DELIMITER)
26 + l.second.size() + sizeof(DELIMITER);
27}
28
29// an output iterator that writes label_pairs to a flat buffer
30template <std::contiguous_iterator Iterator>
31class label_insert_iterator {
32 using base_iterator = Iterator;
33
34 struct label_writer {
35 base_iterator pos; // write position
36
37 label_writer& operator=(const label_pair& l) {
38 pos = write(l.first, pos);
39 pos = write(l.second, pos);
40 return *this;
41 }
42 };
43 label_writer label;
44
45 public:
46 using difference_type = std::ptrdiff_t;
47 using value_type = label_writer;
48 using reference = value_type&;
49
50 label_insert_iterator() = default;
51 label_insert_iterator(base_iterator begin) : label{begin} {
52 static_assert(std::output_iterator<label_insert_iterator, label_pair>);
53 }
54
55 // increments are noops
56 label_insert_iterator& operator++() { return *this; }
57 label_insert_iterator operator++(int) { return *this; }
58
59 // can only dereference to assign
60 reference operator*() { return label; }
61
62 // return the wrapped iterator position
63 base_iterator base() { return label.pos; }
64};
65
66// compare label_pairs by their key only
67bool label_key_less(const label_pair& lhs, const label_pair& rhs)
68{
69 return lhs.first < rhs.first;
70}
71bool label_key_equal(const label_pair& lhs, const label_pair& rhs)
72{
73 return lhs.first == rhs.first;
74}
75
76std::string create(std::string_view counter_name,
77 label_pair* begin, label_pair* end)
78{
79 // sort the input labels and remove duplicate keys
80 std::sort(begin, end, label_key_less);
81 end = std::unique(begin, end, label_key_equal);
82
83 // calculate the total size and preallocate the buffer
84 auto size = std::accumulate(begin, end,
85 counter_name.size() + sizeof(DELIMITER),
86 [] (std::size_t sum, const label_pair& l) {
87 return sum + label_size(l);
88 });
89 std::string result;
90 result.resize(size);
91
92 // copy out the counter name and labels
93 auto out = result.begin();
94 out = write(counter_name, out);
95 std::copy(begin, end, label_insert_iterator{out});
96
97 return result;
98}
99
100std::string insert(const char* begin1, const char* end1,
101 label_pair* begin2, label_pair* end2)
102{
103 // sort the input labels and remove duplicate keys
104 std::sort(begin2, end2, label_key_less);
105 end2 = std::unique(begin2, end2, label_key_equal);
106
107 // find the first delimiter that marks the end of the counter name
108 auto pos = std::find(begin1, end1, DELIMITER);
109
110 // calculate the total size and preallocate the buffer
111 auto size = std::distance(begin1, end1);
112 if (pos == end1) { // add a delimiter if the key doesn't have one
113 size += sizeof(DELIMITER);
114 }
115 size = std::accumulate(begin2, end2, size,
116 [] (std::size_t sum, const label_pair& l) {
117 return sum + label_size(l);
118 });
119 std::string result;
120 result.resize(size);
121
122 // copy the counter name without the delimiter
123 auto out = std::copy(begin1, pos, result.begin());
124 if (pos != end1) {
125 ++pos; // advance past the delimiter
126 }
127 *(out++) = DELIMITER;
128
129 // merge the two sorted input ranges, drop any duplicate keys, and write
130 // them to output. the begin2 range is first so that new input labels can
131 // replace existing duplicates
132 auto end = std::set_union(begin2, end2,
133 label_iterator{pos, end1},
134 label_iterator{end1, end1},
135 label_insert_iterator{out},
136 label_key_less);
137 // fix up the size in case set_union() removed any duplicates
138 result.resize(std::distance(result.begin(), end.base()));
139
140 return result;
141}
142
143std::string_view name(const char* begin, const char* end)
144{
145 auto pos = std::find(begin, end, DELIMITER);
146 return {begin, pos};
147}
148
149std::string_view labels(const char* begin, const char* end)
150{
151 auto pos = std::find(begin, end, DELIMITER);
152 if (pos == end) {
153 return {};
154 }
155 return {std::next(pos), end};
156}
157
158} // namespace detail
159
160
161std::string key_create(std::string_view counter_name)
162{
163 label_pair* end = nullptr;
164 return detail::create(counter_name, end, end);
165}
166
167std::string_view key_name(std::string_view key)
168{
169 return detail::name(key.begin(), key.end());
170}
171
172label_range key_labels(std::string_view key)
173{
174 return detail::labels(key.begin(), key.end());
175}
176
177
178label_iterator::label_iterator(base_iterator begin, base_iterator end)
179 : state(make_state(begin, end))
180{
181 static_assert(std::forward_iterator<label_iterator>);
182}
183
184void label_iterator::advance(std::optional<iterator_state>& s)
185{
186 auto d = std::find(s->pos, s->end, detail::DELIMITER);
187 if (d == s->end) { // no delimiter for label key
188 s = std::nullopt;
189 return;
190 }
191 s->label.first = std::string_view{s->pos, d};
192 s->pos = std::next(d);
193
194 d = std::find(s->pos, s->end, detail::DELIMITER);
195 if (d == s->end) { // no delimiter for label name
196 s = std::nullopt;
197 return;
198 }
199 s->label.second = std::string_view{s->pos, d};
200 s->pos = std::next(d);
201}
202
203auto label_iterator::make_state(base_iterator begin, base_iterator end)
204 -> std::optional<iterator_state>
205{
206 std::optional state = iterator_state{begin, end};
207 advance(state);
208 return state;
209}
210
211label_iterator& label_iterator::operator++()
212{
213 advance(state);
214 return *this;
215}
216
217label_iterator label_iterator::operator++(int)
218{
219 label_iterator tmp = *this;
220 advance(state);
221 return tmp;
222}
223
224} // namespace ceph::perf_counters