1 #include "common/perf_counters_key.h"
7 namespace ceph::perf_counters
{
10 // use a null character to delimit strings
11 constexpr char DELIMITER
= '\0';
14 // write a delimited string to the output
15 auto write(std::string_view str
, std::output_iterator
<char> auto out
)
17 out
= std::copy(str
.begin(), str
.end(), out
);
22 // return the encoded size of a label
23 inline std::size_t label_size(const label_pair
& l
)
25 return l
.first
.size() + sizeof(DELIMITER
)
26 + l
.second
.size() + sizeof(DELIMITER
);
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
;
35 base_iterator pos
; // write position
37 label_writer
& operator=(const label_pair
& l
) {
38 pos
= write(l
.first
, pos
);
39 pos
= write(l
.second
, pos
);
46 using difference_type
= std::ptrdiff_t;
47 using value_type
= label_writer
;
48 using reference
= value_type
&;
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
>);
55 // increments are noops
56 label_insert_iterator
& operator++() { return *this; }
57 label_insert_iterator
operator++(int) { return *this; }
59 // can only dereference to assign
60 reference
operator*() { return label
; }
62 // return the wrapped iterator position
63 base_iterator
base() { return label
.pos
; }
66 // compare label_pairs by their key only
67 bool label_key_less(const label_pair
& lhs
, const label_pair
& rhs
)
69 return lhs
.first
< rhs
.first
;
71 bool label_key_equal(const label_pair
& lhs
, const label_pair
& rhs
)
73 return lhs
.first
== rhs
.first
;
76 std::string
create(std::string_view counter_name
,
77 label_pair
* begin
, label_pair
* end
)
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
);
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
);
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
});
100 std::string
insert(const char* begin1
, const char* end1
,
101 label_pair
* begin2
, label_pair
* end2
)
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
);
107 // find the first delimiter that marks the end of the counter name
108 auto pos
= std::find(begin1
, end1
, DELIMITER
);
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
);
115 size
= std::accumulate(begin2
, end2
, size
,
116 [] (std::size_t sum
, const label_pair
& l
) {
117 return sum
+ label_size(l
);
122 // copy the counter name without the delimiter
123 auto out
= std::copy(begin1
, pos
, result
.begin());
125 ++pos
; // advance past the delimiter
127 *(out
++) = DELIMITER
;
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
},
137 // fix up the size in case set_union() removed any duplicates
138 result
.resize(std::distance(result
.begin(), end
.base()));
143 std::string_view
name(const char* begin
, const char* end
)
145 auto pos
= std::find(begin
, end
, DELIMITER
);
149 std::string_view
labels(const char* begin
, const char* end
)
151 auto pos
= std::find(begin
, end
, DELIMITER
);
155 return {std::next(pos
), end
};
158 } // namespace detail
161 std::string
key_create(std::string_view counter_name
)
163 label_pair
* end
= nullptr;
164 return detail::create(counter_name
, end
, end
);
167 std::string_view
key_name(std::string_view key
)
169 return detail::name(key
.begin(), key
.end());
172 label_range
key_labels(std::string_view key
)
174 return detail::labels(key
.begin(), key
.end());
178 label_iterator::label_iterator(base_iterator begin
, base_iterator end
)
179 : state(make_state(begin
, end
))
181 static_assert(std::forward_iterator
<label_iterator
>);
184 void label_iterator::advance(std::optional
<iterator_state
>& s
)
186 auto d
= std::find(s
->pos
, s
->end
, detail::DELIMITER
);
187 if (d
== s
->end
) { // no delimiter for label key
191 s
->label
.first
= std::string_view
{s
->pos
, d
};
192 s
->pos
= std::next(d
);
194 d
= std::find(s
->pos
, s
->end
, detail::DELIMITER
);
195 if (d
== s
->end
) { // no delimiter for label name
199 s
->label
.second
= std::string_view
{s
->pos
, d
};
200 s
->pos
= std::next(d
);
203 auto label_iterator::make_state(base_iterator begin
, base_iterator end
)
204 -> std::optional
<iterator_state
>
206 std::optional state
= iterator_state
{begin
, end
};
211 label_iterator
& label_iterator::operator++()
217 label_iterator
label_iterator::operator++(int)
219 label_iterator tmp
= *this;
224 } // namespace ceph::perf_counters