]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/perf_counters_key.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / common / perf_counters_key.cc
1 #include "common/perf_counters_key.h"
2
3 #include <algorithm>
4 #include <iterator>
5 #include <numeric>
6
7 namespace ceph::perf_counters {
8 namespace detail {
9
10 // use a null character to delimit strings
11 constexpr char DELIMITER = '\0';
12
13
14 // write a delimited string to the output
15 auto 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
23 inline 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
30 template <std::contiguous_iterator Iterator>
31 class 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
67 bool label_key_less(const label_pair& lhs, const label_pair& rhs)
68 {
69 return lhs.first < rhs.first;
70 }
71 bool label_key_equal(const label_pair& lhs, const label_pair& rhs)
72 {
73 return lhs.first == rhs.first;
74 }
75
76 std::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
100 std::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
143 std::string_view name(const char* begin, const char* end)
144 {
145 auto pos = std::find(begin, end, DELIMITER);
146 return {begin, pos};
147 }
148
149 std::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
161 std::string key_create(std::string_view counter_name)
162 {
163 label_pair* end = nullptr;
164 return detail::create(counter_name, end, end);
165 }
166
167 std::string_view key_name(std::string_view key)
168 {
169 return detail::name(key.begin(), key.end());
170 }
171
172 label_range key_labels(std::string_view key)
173 {
174 return detail::labels(key.begin(), key.end());
175 }
176
177
178 label_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
184 void 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
203 auto 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
211 label_iterator& label_iterator::operator++()
212 {
213 advance(state);
214 return *this;
215 }
216
217 label_iterator label_iterator::operator++(int)
218 {
219 label_iterator tmp = *this;
220 advance(state);
221 return tmp;
222 }
223
224 } // namespace ceph::perf_counters