prom 0.1.0
Client-independent C++23 Prometheus/OpenMetrics metric abstraction
Loading...
Searching...
No Matches
labels.hpp
Go to the documentation of this file.
1#pragma once
2
6
7#include <algorithm>
8#include <cstddef>
9#include <cstdint>
10#include <initializer_list>
11#include <span>
12#include <string>
13#include <string_view>
14#include <utility>
15#include <vector>
16
17namespace prom {
18
20struct Label {
21 std::string name;
22 std::string value;
23
24 bool operator==(const Label&) const = default;
25};
26
28[[nodiscard]] constexpr bool is_valid_metric_name(std::string_view name) noexcept {
29 if (name.empty()) {
30 return false;
31 }
32 auto is_alpha = [](const char c) {
33 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
34 };
35 auto is_alnum = [&](const char c) { return is_alpha(c) || (c >= '0' && c <= '9'); };
36 if (!is_alpha(name.front())) {
37 return false;
38 }
39 return std::ranges::all_of(name, is_alnum);
40}
41
44[[nodiscard]] constexpr bool is_valid_label_name(const std::string_view name) noexcept {
46 return false;
47 }
48 return name.size() < 2 || name[0] != '_' || name[1] != '_';
49}
50
55class Labels {
56public:
57 Labels() = default;
58
59 Labels(const std::initializer_list<Label> init) : labels_(init) {
60 normalize();
61 }
62
63 explicit Labels(std::vector<Label> labels) : labels_(std::move(labels)) {
64 normalize();
65 }
66
68 void set(std::string name, std::string value) {
69 labels_.push_back(Label{std::move(name), std::move(value)});
70 normalize();
71 }
72
74 [[nodiscard]] std::span<const Label> view() const noexcept {
75 return labels_;
76 }
77
78 [[nodiscard]] bool empty() const noexcept {
79 return labels_.empty();
80 }
81
82 [[nodiscard]] std::size_t size() const noexcept {
83 return labels_.size();
84 }
85
88 [[nodiscard]] std::size_t hash() const noexcept {
89 if (!hash_valid_) {
90 hash_ = compute_hash();
91 hash_valid_ = true;
92 }
93 return hash_;
94 }
95
97 [[nodiscard]] Labels merged_with(const Labels& other) const {
98 std::vector<Label> combined;
99 combined.reserve(labels_.size() + other.labels_.size());
100 combined.insert(combined.end(), labels_.begin(), labels_.end());
101 combined.insert(combined.end(), other.labels_.begin(), other.labels_.end());
102 return Labels{std::move(combined)};
103 }
104
105 bool operator==(const Labels& rhs) const {
106 return labels_ == rhs.labels_;
107 }
108
109private:
110 void normalize() {
111 std::ranges::stable_sort(labels_, {}, &Label::name);
112 // Collapse equal-name runs keeping the last entry (last write wins).
113 std::vector<Label> out;
114 out.reserve(labels_.size());
115 for (std::size_t i = 0; i < labels_.size(); ++i) {
116 const bool shadowed_by_next =
117 i + 1 < labels_.size() && labels_[i].name == labels_[i + 1].name;
118 if (!shadowed_by_next) {
119 out.push_back(std::move(labels_[i]));
120 }
121 }
122 labels_ = std::move(out);
123 hash_valid_ = false;
124 }
125
126 [[nodiscard]] std::size_t compute_hash() const noexcept {
127 constexpr std::uint64_t fnv_offset = 1469598103934665603ULL;
128 constexpr std::uint64_t fnv_prime = 1099511628211ULL;
129 std::uint64_t h = fnv_offset;
130 auto mix = [&](const std::string_view s) {
131 for (const char c : s) {
132 h ^= static_cast<std::uint8_t>(c);
133 h *= fnv_prime;
134 }
135 h ^= 0U; // explicit field separator
136 h *= fnv_prime;
137 };
138 for (const auto& [name, value] : labels_) {
139 mix(name);
140 mix(value);
141 }
142 return static_cast<std::size_t>(h);
143 }
144
145 std::vector<Label> labels_;
146 mutable std::size_t hash_ = 0;
147 mutable bool hash_valid_ = false;
148};
149
150} // namespace prom
151
154template <>
155struct std::hash<prom::Labels> {
156 [[nodiscard]] std::size_t operator()(const prom::Labels& labels) const noexcept {
157 return labels.hash();
158 }
159}; // namespace std
std::string name
Metric name (state-set label key).
Definition adapter.cpp:68
An immutable-by-convention set of labels, kept sorted by name with duplicates collapsed last-wins.
Definition labels.hpp:55
void set(std::string name, std::string value)
Insert or overwrite a label (last write wins on a repeated name).
Definition labels.hpp:68
std::size_t size() const noexcept
Definition labels.hpp:82
std::size_t hash() const noexcept
64-bit FNV-1a hash over the name\0value\0 stream; computed lazily and cached.
Definition labels.hpp:88
Labels merged_with(const Labels &other) const
Return *this overlaid with other; on a name collision other wins.
Definition labels.hpp:97
std::span< const Label > view() const noexcept
Read-only view over the sorted/deduped pairs.
Definition labels.hpp:74
Labels()=default
bool empty() const noexcept
Definition labels.hpp:78
Labels(std::vector< Label > labels)
Definition labels.hpp:63
Labels(const std::initializer_list< Label > init)
Definition labels.hpp:59
bool operator==(const Labels &rhs) const
Definition labels.hpp:105
Definition adapter.hpp:24
constexpr bool is_valid_metric_name(std::string_view name) noexcept
A metric name is valid when it matches Prometheus's [a-zA-Z_][a-zA-Z0-9_]*.
Definition labels.hpp:28
constexpr bool is_valid_label_name(const std::string_view name) noexcept
A label name follows the metric-name charset but additionally rejects the __ prefix,...
Definition labels.hpp:44
A single name="value" label pair.
Definition labels.hpp:20
std::string value
Definition labels.hpp:22
std::string name
Definition labels.hpp:21
bool operator==(const Label &) const =default
std::size_t operator()(const prom::Labels &labels) const noexcept
Definition labels.hpp:156