Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 46 additions & 12 deletions libs/meter/meter_id/meter_id.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,39 @@
#include <meter_id.h>

#include <util.h>

#include <array>
#include <sstream>

namespace spectator {

// Define the static member
const std::regex INVALID_CHARS("[^-._A-Za-z0-9~^]");
namespace {

// spectatord accepts only -._A-Za-z0-9~^ in names, tag keys and tag values; every other byte becomes '_'.
// Built at compile time so sanitizing is one table lookup per byte (this replaces a std::regex_replace per string).
constexpr std::array<bool, 256> MakeValidCharTable()
{
std::array<bool, 256> table{};
for (char c = 'A'; c <= 'Z'; ++c) table[static_cast<unsigned char>(c)] = true;
for (char c = 'a'; c <= 'z'; ++c) table[static_cast<unsigned char>(c)] = true;
for (char c = '0'; c <= '9'; ++c) table[static_cast<unsigned char>(c)] = true;
for (const char c : {'-', '.', '_', '~', '^'}) table[static_cast<unsigned char>(c)] = true;
return table;
}

constexpr std::array<bool, 256> kValidChars = MakeValidCharTable();

// Appends src to dest with invalid characters replaced by '_'. Grows dest once instead of once per character.
void AppendSanitized(std::string& dest, const std::string& src)
{
const size_t start = dest.size();
dest.resize(start + src.size());
char* out = dest.data() + start;
for (const char c : src)
{
*out++ = kValidChars[static_cast<unsigned char>(c)] ? c : '_';
}
}

std::unordered_map<std::string, std::string> ValidateTags(const std::unordered_map<std::string, std::string>& tags)
{
Expand All @@ -22,22 +49,29 @@ std::unordered_map<std::string, std::string> ValidateTags(const std::unordered_m
return validTags;
}

std::string ReplaceInvalidChars(const std::string& s) { return std::regex_replace(s, INVALID_CHARS, "_"); }

std::string ToSpectatorId(const std::string& name, const std::unordered_map<std::string, std::string>& tags)
{
std::ostringstream ss;
ss << ReplaceInvalidChars(name);
if (!tags.empty())
size_t size = name.size();
for (const auto& [key, value] : tags)
{
for (const auto& [fst, snd] : tags)
{
ss << "," << ReplaceInvalidChars(fst) << "=" << ReplaceInvalidChars(snd);
}
size += key.size() + value.size() + 2; // ',' and '='
}
return ss.str();

std::string id;
id.reserve(size);
AppendSanitized(id, name);
for (const auto& [key, value] : tags)
{
id += ',';
AppendSanitized(id, key);
id += '=';
AppendSanitized(id, value);
}
return id;
}

} // namespace

MeterId::MeterId(const std::string& name, const std::unordered_map<std::string, std::string>& tags)
: m_name(name), m_tags(ValidateTags(tags))
{
Expand Down
1 change: 0 additions & 1 deletion libs/meter/meter_id/meter_id.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#include <string>
#include <map>
#include <regex>
#include <functional>
#include <unordered_map>

Expand Down
18 changes: 18 additions & 0 deletions libs/meter/meter_id/test_meter_id.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ TEST(MeterIdTest, IllegalCharsAreReplaced)
EXPECT_EQ("test______^____-_~______________.___foo", id.GetSpectatordId());
}

TEST(MeterIdTest, IllegalCharsInTagsAreReplaced)
{
const MeterId id("foo", {{"a b", "c/d"}});
EXPECT_EQ("foo,a_b=c_d", id.GetSpectatordId());
}

TEST(MeterIdTest, ValidCharsAreKept)
{
const MeterId id("azAZ09-._~^");
EXPECT_EQ("azAZ09-._~^", id.GetSpectatordId());
}

TEST(MeterIdTest, EachNonAsciiByteIsReplaced)
{
const MeterId id("caf\xc3\xa9");
EXPECT_EQ("caf__", id.GetSpectatordId());
}

TEST(MeterIdTest, LookupTags)
{
const MeterId id1("foo", {{"a", "1"}, {"b", "2"}, {"c", "3"}});
Expand Down
1 change: 1 addition & 0 deletions libs/utils/src/util.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <util.h>

#include <algorithm>
#include <sstream>
#include <vector>

Expand Down
1 change: 1 addition & 0 deletions libs/writer/writer_wrapper/test_writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <iostream>
#include <thread>
#include <chrono>
#include <regex>
#include <algorithm>

#include "../writer_types/test_utils/uds_server/uds_server.h"
Expand Down
2 changes: 2 additions & 0 deletions spectator/registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// header keeps spdlog and the spectator::Logger name out of consumers.
#include <logger.h>

#include <regex>

namespace spectator {

void SetLogLevel(spdlog::level::level_enum level)
Expand Down
Loading