Source code
Revision control
Copy as Markdown
Other Tools
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include "opentelemetry/common/key_value_iterable_view.h"
#include "opentelemetry/common/string_util.h"
#include "opentelemetry/nostd/function_ref.h"
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/nostd/unique_ptr.h"
#include "opentelemetry/version.h"
#include <cstring>
#include <string>
#include <type_traits>
OPENTELEMETRY_BEGIN_NAMESPACE
namespace common
{
// Constructor parameter for KeyValueStringTokenizer
struct KeyValueStringTokenizerOptions
{
char member_separator = ',';
char key_value_separator = '=';
bool ignore_empty_members = true;
};
// Tokenizer for key-value headers
class KeyValueStringTokenizer
{
public:
KeyValueStringTokenizer(
nostd::string_view str,
const KeyValueStringTokenizerOptions &opts = KeyValueStringTokenizerOptions()) noexcept
: str_(str), opts_(opts), index_(0)
{}
static nostd::string_view GetDefaultKeyOrValue()
{
static std::string default_str = "";
return default_str;
}
// Returns next key value in the string header
// @param valid_kv : if the found kv pair is valid or not
// @param key : key in kv pair
// @param key : value in kv pair
// @returns true if next kv pair was found, false otherwise.
bool next(bool &valid_kv, nostd::string_view &key, nostd::string_view &value) noexcept
{
valid_kv = true;
while (index_ < str_.size())
{
bool is_empty_pair = false;
size_t end = str_.find(opts_.member_separator, index_);
if (end == std::string::npos)
{
end = str_.size() - 1;
}
else if (end == index_) // empty pair. do not update end
{
is_empty_pair = true;
}
else
{
end--;
}
auto list_member = StringUtil::Trim(str_, index_, end);
if (list_member.size() == 0 || is_empty_pair)
{
// empty list member
index_ = end + 2 - is_empty_pair;
if (opts_.ignore_empty_members)
{
continue;
}
valid_kv = true;
key = GetDefaultKeyOrValue();
value = GetDefaultKeyOrValue();
return true;
}
auto key_end_pos = list_member.find(opts_.key_value_separator);
if (key_end_pos == std::string::npos)
{
// invalid member
valid_kv = false;
}
else
{
key = list_member.substr(0, key_end_pos);
value = list_member.substr(key_end_pos + 1);
}
index_ = end + 2;
return true;
}
// no more entries remaining
return false;
}
// Returns total number of tokens in header string
size_t NumTokens() const noexcept
{
size_t cnt = 0, begin = 0;
while (begin < str_.size())
{
++cnt;
size_t end = str_.find(opts_.member_separator, begin);
if (end == std::string::npos)
{
break;
}
begin = end + 1;
}
return cnt;
}
// Resets the iterator
void reset() noexcept { index_ = 0; }
private:
nostd::string_view str_;
KeyValueStringTokenizerOptions opts_;
size_t index_;
};
// Class to store fixed size array of key-value pairs of string type
class KeyValueProperties
{
// Class to store key-value pairs of string types
public:
class Entry
{
public:
Entry() : key_(nullptr), value_(nullptr) {}
// Copy constructor
Entry(const Entry ©)
{
key_ = CopyStringToPointer(copy.key_.get());
value_ = CopyStringToPointer(copy.value_.get());
}
// Copy assignment operator
Entry &operator=(Entry &other)
{
key_ = CopyStringToPointer(other.key_.get());
value_ = CopyStringToPointer(other.value_.get());
return *this;
}
// Move contructor and assignment operator
Entry(Entry &&other) = default;
Entry &operator=(Entry &&other) = default;
// Creates an Entry for a given key-value pair.
Entry(nostd::string_view key, nostd::string_view value)
{
key_ = CopyStringToPointer(key);
value_ = CopyStringToPointer(value);
}
// Gets the key associated with this entry.
nostd::string_view GetKey() const noexcept { return key_.get(); }
// Gets the value associated with this entry.
nostd::string_view GetValue() const noexcept { return value_.get(); }
// Sets the value for this entry. This overrides the previous value.
void SetValue(nostd::string_view value) noexcept { value_ = CopyStringToPointer(value); }
private:
// Store key and value as raw char pointers to avoid using std::string.
nostd::unique_ptr<const char[]> key_;
nostd::unique_ptr<const char[]> value_;
// Copies string into a buffer and returns a unique_ptr to the buffer.
// This is a workaround for the fact that memcpy doesn't accept a const destination.
nostd::unique_ptr<const char[]> CopyStringToPointer(nostd::string_view str)
{
char *temp = new char[str.size() + 1];
memcpy(temp, str.data(), str.size());
temp[str.size()] = '\0';
return nostd::unique_ptr<const char[]>(temp);
}
};
// Maintain the number of entries in entries_.
size_t num_entries_;
// Max size of allocated array
size_t max_num_entries_;
// Store entries in a C-style array to avoid using std::array or std::vector.
nostd::unique_ptr<Entry[]> entries_;
public:
// Create Key-value list of given size
// @param size : Size of list.
KeyValueProperties(size_t size) noexcept
: num_entries_(0), max_num_entries_(size), entries_(new Entry[size])
{}
// Create Empty Key-Value list
KeyValueProperties() noexcept : num_entries_(0), max_num_entries_(0), entries_(nullptr) {}
template <class T, class = typename std::enable_if<detail::is_key_value_iterable<T>::value>::type>
KeyValueProperties(const T &keys_and_values) noexcept
: num_entries_(0),
max_num_entries_(keys_and_values.size()),
entries_(new Entry[max_num_entries_])
{
for (auto &e : keys_and_values)
{
Entry entry(e.first, e.second);
(entries_.get())[num_entries_++] = std::move(entry);
}
}
// Adds new kv pair into kv properties
void AddEntry(nostd::string_view key, nostd::string_view value) noexcept
{
if (num_entries_ < max_num_entries_)
{
Entry entry(key, value);
(entries_.get())[num_entries_++] = std::move(entry);
}
}
// Returns all kv pair entries
bool GetAllEntries(
nostd::function_ref<bool(nostd::string_view, nostd::string_view)> callback) const noexcept
{
for (size_t i = 0; i < num_entries_; i++)
{
auto &entry = (entries_.get())[i];
if (!callback(entry.GetKey(), entry.GetValue()))
{
return false;
}
}
return true;
}
// Return value for key if exists, return false otherwise
bool GetValue(nostd::string_view key, std::string &value) const noexcept
{
for (size_t i = 0; i < num_entries_; i++)
{
auto &entry = (entries_.get())[i];
if (entry.GetKey() == key)
{
const auto &entry_value = entry.GetValue();
value = std::string(entry_value.data(), entry_value.size());
return true;
}
}
return false;
}
size_t Size() const noexcept { return num_entries_; }
};
} // namespace common
OPENTELEMETRY_END_NAMESPACE