]>
Commit | Line | Data |
---|---|---|
1e59de90 TL |
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 |