#pragma once
#include <util/digest/multi.h>
#include <util/digest/sequence.h>
#include <util/generic/algorithm.h>
#include <util/generic/maybe.h>
#include <util/generic/string.h>
#include <util/generic/vector.h>
#include <util/stream/output.h>
#include <util/string/builder.h>
#include <util/string/strip.h>
#include <optional>
#include <type_traits>
namespace NMonitoring {
struct ILabel {
virtual ~ILabel() = default;
virtual TStringBuf Name() const noexcept = 0;
virtual TStringBuf Value() const noexcept = 0;
};
///////////////////////////////////////////////////////////////////////////
// TLabel
///////////////////////////////////////////////////////////////////////////
template <typename TStringBackend>
class TLabelImpl: public ILabel {
public:
using TStringType = TStringBackend;
TLabelImpl() = default;
inline TLabelImpl(TStringBuf name, TStringBuf value)
: Name_{name}
, Value_{value}
{
}
inline bool operator==(const TLabelImpl& rhs) const noexcept {
return Name_ == rhs.Name_ && Value_ == rhs.Value_;
}
inline bool operator!=(const TLabelImpl& rhs) const noexcept {
return !(*this == rhs);
}
inline TStringBuf Name() const noexcept {
return Name_;
}
inline TStringBuf Value() const noexcept {
return Value_;
}
inline const TStringBackend& NameStr() const {
return Name_;
}
inline const TStringBackend& ValueStr() const {
return Value_;
}
inline size_t Hash() const noexcept {
return MultiHash(Name_, Value_);
}
TStringBackend ToString() const {
TStringBackend buf = Name_;
buf += '=';
buf += Value_;
return buf;
}
static TLabelImpl FromString(TStringBuf str) {
TStringBuf name, value;
Y_ENSURE(str.TrySplit('=', name, value),
"invalid label string format: '" << str << '\'');
TStringBuf nameStripped = StripString(name);
Y_ENSURE(!nameStripped.empty(), "label name cannot be empty");
TStringBuf valueStripped = StripString(value);
Y_ENSURE(!valueStripped.empty(), "label value cannot be empty");
return {nameStripped, valueStripped};
}
static bool TryFromString(TStringBuf str, TLabelImpl& label) {
TStringBuf name, value;
if (!str.TrySplit('=', name, value)) {
return false;
}
TStringBuf nameStripped = StripString(name);
if (nameStripped.empty()) {
return false;
}
TStringBuf valueStripped = StripString(value);
if (valueStripped.empty()) {
return false;
}
label = {nameStripped, valueStripped};
return true;
}
private:
TStringBackend Name_;
TStringBackend Value_;
};
using TLabel = TLabelImpl<TString>;
struct ILabels : public TThrRefBase {
struct TIterator {
TIterator() = default;
TIterator(const ILabels* labels, size_t idx = 0)
: Labels_{labels}
, Idx_{idx}
{
}
TIterator& operator++() noexcept {
Idx_++;
return *this;
}
void operator+=(size_t i) noexcept {
Idx_ += i;
}
bool operator==(const TIterator& other) const noexcept {
return Idx_ == other.Idx_;
}
bool operator!=(const TIterator& other) const noexcept {
return !(*this == other);
}
const ILabel* operator->() const noexcept {
Y_VERIFY_DEBUG(Labels_);
return Labels_->Get(Idx_);
}
const ILabel& operator*() const noexcept {
Y_VERIFY_DEBUG(Labels_);
return *Labels_->Get(Idx_);
}
private:
const ILabels* Labels_{nullptr};
size_t Idx_{0};
};
virtual ~ILabels() = default;
virtual bool Add(TStringBuf name, TStringBuf value) noexcept = 0;
virtual bool Add(const ILabel& label) noexcept {
return Add(label.Name(), label.Value());
}
virtual bool Has(TStringBuf name) const noexcept = 0;
virtual size_t Size() const noexcept = 0;
virtual bool Empty() const noexcept = 0;
virtual void Clear() noexcept = 0;
virtual size_t Hash() const noexcept = 0;
virtual std::optional<const ILabel*> Get(TStringBuf name) const = 0;
// NB: there's no guarantee that indices are preserved after any object modification
virtual const ILabel* Get(size_t idx) const = 0;
TIterator begin() const {
return TIterator{this};
}
TIterator end() const {
return TIterator{this, Size()};
}
};
bool TryLoadLabelsFromString(TStringBuf sb, ILabels& labels);
bool TryLoadLabelsFromString(IInputStream& is, ILabels& labels);
///////////////////////////////////////////////////////////////////////////
// TLabels
///////////////////////////////////////////////////////////////////////////
template <typename TStringBackend>
class TLabelsImpl: public ILabels {
public:
using value_type = TLabelImpl<TStringBackend>;
TLabelsImpl() = default;
explicit TLabelsImpl(::NDetail::TReserveTag rt)
: Labels_(std::move(rt))
{}
explicit TLabelsImpl(size_t count)
: Labels_(count)
{}
explicit TLabelsImpl(size_t count, const value_type& label)
: Labels_(count, label)
{}
TLabelsImpl(std::initializer_list<value_type> il)
: Labels_(std::move(il))
{}
TLabelsImpl(const TLabelsImpl&) = default;
TLabelsImpl& operator=(const TLabelsImpl&) = default;
TLabelsImpl(TLabelsImpl&&) noexcept = default;
TLabelsImpl& operator=(TLabelsImpl&&) noexcept = default;
inline bool operator==(const TLabelsImpl& rhs) const {
return Labels_ == rhs.Labels_;
}
inline bool operator!=(const TLabelsImpl& rhs) const {
return Labels_ != rhs.Labels_;
}
bool Add(TStringBuf name, TStringBuf value) noexcept override {
if (Has(name)) {
return false;
}
Labels_.emplace_back(name, value);
return true;
}
using ILabels::Add;
bool Has(TStringBuf name) const noexcept override {
auto it = FindIf(Labels_, [name](const TLabelImpl<TStringBackend>& label) {
return name == TStringBuf{label.Name()};
});
return it != Labels_.end();
}
bool Has(const TString& name) const noexcept {
auto it = FindIf(Labels_, [name](const TLabelImpl<TStringBackend>& label) {
return name == TStringBuf{label.Name()};
});
return it != Labels_.end();
}
// XXX for backward compatibility
TMaybe<TLabelImpl<TStringBackend>> Find(TStringBuf name) const {
auto it = FindIf(Labels_, [name](const TLabelImpl<TStringBackend>& label) {
return name == TStringBuf{label.Name()};
});
if (it == Labels_.end()) {
return Nothing();
}
return *it;
}
std::optional<const ILabel*> Get(TStringBuf name) const override {
auto it = FindIf(Labels_, [name] (auto&& l) {
return name == l.Name();
});
if (it == Labels_.end()) {
return {};
}
return &*it;
}
const ILabel* Get(size_t idx) const noexcept override {
return &(*this)[idx];
}
TMaybe<TLabelImpl<TStringBackend>> Extract(TStringBuf name) {
auto it = FindIf(Labels_, [name](const TLabelImpl<TStringBackend>& label) {
return name == TStringBuf{label.Name()};
});
if (it == Labels_.end()) {
return Nothing();
}
TLabel tmp = *it;
Labels_.erase(it);
return tmp;
}
void SortByName() {
std::sort(Labels_.begin(), Labels_.end(), [](const auto& lhs, const auto& rhs) {
return lhs.Name() < rhs.Name();
});
}
inline size_t Hash() const noexcept override {
return TSimpleRangeHash()(Labels_);
}
inline TLabel* Data() const noexcept {
return const_cast<TLabel*>(Labels_.data());
}
inline size_t Size() const noexcept override {
return Labels_.size();
}
inline bool Empty() const noexcept override {
return Labels_.empty();
}
inline void Clear() noexcept override {
Labels_.clear();
}
TLabelImpl<TStringBackend>& front() {
return Labels_.front();
}
const TLabelImpl<TStringBackend>& front() const {
return Labels_.front();
}
TLabelImpl<TStringBackend>& back() {
return Labels_.back();
}
const TLabelImpl<TStringBackend>& back() const {
return Labels_.back();
}
TLabelImpl<TStringBackend>& operator[](size_t index) {
return Labels_[index];
}
const TLabelImpl<TStringBackend>& operator[](size_t index) const {
return Labels_[index];
}
TLabelImpl<TStringBackend>& at(size_t index) {
return Labels_.at(index);
}
const TLabelImpl<TStringBackend>& at(size_t index) const {
return Labels_.at(index);
}
size_t capacity() const {
return Labels_.capacity();
}
TLabelImpl<TStringBackend>* data() {
return Labels_.data();
}
const TLabelImpl<TStringBackend>* data() const {
return Labels_.data();
}
size_t size() const {
return Labels_.size();
}
bool empty() const {
return Labels_.empty();
}
void clear() {
Labels_.clear();
}
using ILabels::begin;
using ILabels::end;
using iterator = ILabels::TIterator;
using const_iterator = iterator;
protected:
TVector<TLabelImpl<TStringBackend>>& AsVector() {
return Labels_;
}
const TVector<TLabelImpl<TStringBackend>>& AsVector() const {
return Labels_;
}
private:
TVector<TLabelImpl<TStringBackend>> Labels_;
};
using TLabels = TLabelsImpl<TString>;
using ILabelsPtr = TIntrusivePtr<ILabels>;
template <typename T>
ILabelsPtr MakeLabels() {
return MakeIntrusive<TLabelsImpl<T>>();
}
template <typename T>
ILabelsPtr MakeLabels(std::initializer_list<TLabelImpl<T>> labels) {
return MakeIntrusive<TLabelsImpl<T>>(labels);
}
inline ILabelsPtr MakeLabels(TLabels&& labels) {
return MakeIntrusive<TLabels>(std::move(labels));
}
}
template<>
struct THash<NMonitoring::ILabelsPtr> {
size_t operator()(const NMonitoring::ILabelsPtr& labels) const noexcept {
return labels->Hash();
}
size_t operator()(const NMonitoring::ILabels& labels) const noexcept {
return labels.Hash();
}
};
template<typename TStringBackend>
struct THash<NMonitoring::TLabelsImpl<TStringBackend>> {
size_t operator()(const NMonitoring::TLabelsImpl<TStringBackend>& labels) const noexcept {
return labels.Hash();
}
};
template <typename TStringBackend>
struct THash<NMonitoring::TLabelImpl<TStringBackend>> {
inline size_t operator()(const NMonitoring::TLabelImpl<TStringBackend>& label) const noexcept {
return label.Hash();
}
};
inline bool operator==(const NMonitoring::ILabels& lhs, const NMonitoring::ILabels& rhs) {
if (lhs.Size() != rhs.Size()) {
return false;
}
for (auto&& l : lhs) {
auto rl = rhs.Get(l.Name());
if (!rl || (*rl)->Value() != l.Value()) {
return false;
}
}
return true;
}
bool operator==(const NMonitoring::ILabelsPtr& lhs, const NMonitoring::ILabelsPtr& rhs) = delete;
bool operator==(const NMonitoring::ILabels& lhs, const NMonitoring::ILabelsPtr& rhs) = delete;
bool operator==(const NMonitoring::ILabelsPtr& lhs, const NMonitoring::ILabels& rhs) = delete;
template<>
struct TEqualTo<NMonitoring::ILabelsPtr> {
bool operator()(const NMonitoring::ILabelsPtr& lhs, const NMonitoring::ILabelsPtr& rhs) {
return *lhs == *rhs;
}
bool operator()(const NMonitoring::ILabelsPtr& lhs, const NMonitoring::ILabels& rhs) {
return *lhs == rhs;
}
bool operator()(const NMonitoring::ILabels& lhs, const NMonitoring::ILabelsPtr& rhs) {
return lhs == *rhs;
}
};
#define Y_MONLIB_DEFINE_LABELS_OUT(T) \
template <> \
void Out<T>(IOutputStream& out, const T& labels) { \
Out<NMonitoring::ILabels>(out, labels); \
}
#define Y_MONLIB_DEFINE_LABEL_OUT(T) \
template <> \
void Out<T>(IOutputStream& out, const T& label) { \
Out<NMonitoring::ILabel>(out, label); \
}