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
39 changes: 11 additions & 28 deletions src/datadog/datadog_agent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#include <cassert>
#include <chrono>
#include <exception>
#include <string>
#include <unordered_map>
#include <unordered_set>
Expand Down Expand Up @@ -31,37 +30,21 @@ HTTPClient::URL traces_endpoint(const HTTPClient::URL& agent_url) {

Expected<void> msgpack_encode(
std::string& destination,
const std::vector<std::unique_ptr<SpanData>>& spans) try {
msgpack::pack_array(destination, spans.size());

for (const auto& span_ptr : spans) {
assert(span_ptr);
auto result = msgpack_encode(destination, *span_ptr);
if (auto* error = result.if_error()) {
return std::move(*error);
}
}

return std::nullopt;
} catch (const std::exception& error) {
return Error{Error::MESSAGEPACK_ENCODE_FAILURE, error.what()};
const std::vector<std::unique_ptr<SpanData>>& spans) {
return msgpack::pack_array(destination, spans,
[](auto& destination, const auto& span_ptr) {
assert(span_ptr);
return msgpack_encode(destination, *span_ptr);
});
}

Expected<void> msgpack_encode(
std::string& destination,
const std::vector<DatadogAgent::TraceChunk>& trace_chunks) try {
msgpack::pack_array(destination, trace_chunks.size());

for (const auto& chunk : trace_chunks) {
auto result = msgpack_encode(destination, chunk.spans);
if (auto* error = result.if_error()) {
return std::move(*error);
}
}

return std::nullopt;
} catch (const std::exception& error) {
return Error{Error::MESSAGEPACK_ENCODE_FAILURE, error.what()};
const std::vector<DatadogAgent::TraceChunk>& trace_chunks) {
return msgpack::pack_array(destination, trace_chunks,
[](auto& destination, const auto& chunk) {
return msgpack_encode(destination, chunk.spans);
});
}

std::variant<CollectorResponse, std::string> parse_agent_traces_response(
Expand Down
132 changes: 132 additions & 0 deletions src/datadog/msgpack.cpp
Original file line number Diff line number Diff line change
@@ -1 +1,133 @@
#include "msgpack.h"

#include <cassert>
#include <limits>
#include <type_traits>

#include "error.h"

namespace datadog {
namespace tracing {
namespace msgpack {
namespace {
// MessagePack values are prefixed by a byte naming their type.
namespace types {
constexpr auto ARRAY32 = std::byte(0xDD);
constexpr auto DOUBLE = std::byte(0xCB);
constexpr auto INT64 = std::byte(0xD3);
constexpr auto MAP32 = std::byte(0xDF);
constexpr auto STR32 = std::byte(0xDB);
constexpr auto UINT64 = std::byte(0xCF);
} // namespace types

std::string make_overflow_message(std::string_view type, std::size_t actual,
std::size_t max) {
std::string message;
message += "Cannot msgpack encode ";
message += type;
message += " of size ";
message += std::to_string(actual);
message += ", which exceeds the protocol maximum of ";
message += std::to_string(max);
message += '.';
return message;
}

template <typename Integer>
void push_number_big_endian(std::string& buffer, Integer integer) {
// Assume two's complement.
const std::make_unsigned_t<Integer> value = integer;

// The loop below is more likely to unroll if we don't call any functions
// within it.
char buf[sizeof value];

// The most significant byte of `value` goes to the front of `buf`, and the
// least significant byte of `value` goes
// to the back of `buf`, and so on in between.
// On a big endian architecture, this is just a complicated way to copy
// `value`. On a little endian architecture, which is much more common, this
// effectively copies the bytes of `value` backwards.
const int size = sizeof value;
for (int i = 0; i < size; ++i) {
const char byte = (value >> (8 * ((size - 1) - i))) & 0xFF;
buf[i] = byte;
}

buffer.append(buf, sizeof buf);
}

} // namespace

void pack_integer(std::string& buffer, std::int64_t value) {
buffer.push_back(static_cast<char>(types::INT64));
push_number_big_endian(buffer, static_cast<std::int64_t>(value));
}

void pack_integer(std::string& buffer, std::uint64_t value) {
buffer.push_back(static_cast<char>(types::UINT64));
push_number_big_endian(buffer, static_cast<std::uint64_t>(value));
}

void pack_double(std::string& buffer, double value) {
buffer.push_back(static_cast<char>(types::DOUBLE));

// The following is lifted from the "msgpack-c" project.
// See "pack_double" in
// <https://github.com/msgpack/msgpack-c/blob/cpp_master/include/msgpack/v1/pack.hpp>

union {
double as_double;
uint64_t as_integer;
} memory;
memory.as_double = value;

#if defined(TARGET_OS_IPHONE)
// ok
#elif defined(__arm__) && !(__ARM_EABI__) // arm-oabi
// https://github.com/msgpack/msgpack-perl/pull/1
memory.as_integer =
(memory.as_integer & 0xFFFFFFFFUL) << 32UL | (memory.as_integer >> 32UL);
#endif

push_number_big_endian(buffer, memory.as_integer);
}

Expected<void> pack_string(std::string& buffer, std::string_view value) {
const auto size = value.size();
const auto max = std::numeric_limits<std::uint32_t>::max();
if (size > max) {
return Error{Error::MESSAGEPACK_ENCODE_FAILURE,
make_overflow_message("string", size, max)};
}
buffer.push_back(static_cast<char>(types::STR32));
push_number_big_endian(buffer, static_cast<std::uint32_t>(size));
buffer.append(value.begin(), value.end());
return {};
}

Expected<void> pack_array(std::string& buffer, size_t size) {
const auto max = std::numeric_limits<std::uint32_t>::max();
if (size > max) {
return Error{Error::MESSAGEPACK_ENCODE_FAILURE,
make_overflow_message("array", size, max)};
}
buffer.push_back(static_cast<char>(types::ARRAY32));
push_number_big_endian(buffer, static_cast<std::uint32_t>(size));
return {};
}

Expected<void> pack_map(std::string& buffer, size_t size) {
const auto max = std::numeric_limits<std::uint32_t>::max();
if (size > max) {
return Error{Error::MESSAGEPACK_ENCODE_FAILURE,
make_overflow_message("map", size, max)};
}
buffer.push_back(static_cast<char>(types::MAP32));
push_number_big_endian(buffer, static_cast<std::uint32_t>(size));
return {};
}

} // namespace msgpack
} // namespace tracing
} // namespace datadog
Loading