Source code

Revision control

Copy as Markdown

Other Tools

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <cstring>
#include <utility>
#include "opentelemetry/context/context_value.h"
#include "opentelemetry/nostd/shared_ptr.h"
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/nostd/variant.h"
#include "opentelemetry/version.h"
OPENTELEMETRY_BEGIN_NAMESPACE
namespace context
{
// The context class provides a context identifier. Is built as a linked list
// of DataList nodes and each context holds a shared_ptr to a place within
// the list that determines which keys and values it has access to. All that
// come before and none that come after.
class Context
{
public:
Context() = default;
// Creates a context object from a map of keys and identifiers, this will
// hold a shared_ptr to the head of the DataList linked list
template <class T>
Context(const T &keys_and_values) noexcept
{
head_ = nostd::shared_ptr<DataList>{new DataList(keys_and_values)};
}
// Creates a context object from a key and value, this will
// hold a shared_ptr to the head of the DataList linked list
Context(nostd::string_view key, ContextValue value) noexcept
{
head_ = nostd::shared_ptr<DataList>{new DataList(key, value)};
}
// Accepts a new iterable and then returns a new context that
// contains the new key and value data. It attaches the
// exisiting list to the end of the new list.
template <class T>
Context SetValues(T &values) noexcept
{
Context context = Context(values);
nostd::shared_ptr<DataList> last = context.head_;
while (last->next_ != nullptr)
{
last = last->next_;
}
last->next_ = head_;
return context;
}
// Accepts a new iterable and then returns a new context that
// contains the new key and value data. It attaches the
// exisiting list to the end of the new list.
Context SetValue(nostd::string_view key, ContextValue value) noexcept
{
Context context = Context(key, value);
context.head_->next_ = head_;
return context;
}
// Returns the value associated with the passed in key.
context::ContextValue GetValue(const nostd::string_view key) const noexcept
{
for (DataList *data = head_.get(); data != nullptr; data = data->next_.get())
{
if (key.size() == data->key_length_)
{
if (std::memcmp(key.data(), data->key_, data->key_length_) == 0)
{
return data->value_;
}
}
}
return ContextValue{};
}
// Checks for key and returns true if found
bool HasKey(const nostd::string_view key) const noexcept
{
return !nostd::holds_alternative<nostd::monostate>(GetValue(key));
}
bool operator==(const Context &other) const noexcept { return (head_ == other.head_); }
private:
// A linked list to contain the keys and values of this context node
class DataList
{
public:
char *key_;
nostd::shared_ptr<DataList> next_;
size_t key_length_;
ContextValue value_;
DataList() { next_ = nullptr; }
// Builds a data list off of a key and value iterable and returns the head
template <class T>
DataList(const T &keys_and_vals) : key_{nullptr}, next_(nostd::shared_ptr<DataList>{nullptr})
{
bool first = true;
auto *node = this;
for (auto &iter : keys_and_vals)
{
if (first)
{
*node = DataList(iter.first, iter.second);
first = false;
}
else
{
node->next_ = nostd::shared_ptr<DataList>(new DataList(iter.first, iter.second));
node = node->next_.get();
}
}
}
// Builds a data list with just a key and value, so it will just be the head
// and returns that head.
DataList(nostd::string_view key, const ContextValue &value)
{
key_ = new char[key.size()];
key_length_ = key.size();
memcpy(key_, key.data(), key.size() * sizeof(char));
value_ = value;
next_ = nostd::shared_ptr<DataList>{nullptr};
}
DataList &operator=(DataList &&other) noexcept
{
key_length_ = other.key_length_;
value_ = std::move(other.value_);
next_ = std::move(other.next_);
key_ = other.key_;
other.key_ = nullptr;
return *this;
}
~DataList()
{
if (key_ != nullptr)
{
delete[] key_;
}
}
};
// Head of the list which holds the keys and values of this context
nostd::shared_ptr<DataList> head_;
};
} // namespace context
OPENTELEMETRY_END_NAMESPACE