LCOV - code coverage report
Current view: top level - datadog - msgpack.cpp (source / functions) Hit Total Coverage
Test: filtered.info Lines: 56 57 98.2 %
Date: 2024-01-03 20:30:12 Functions: 10 10 100.0 %

          Line data    Source code
       1             : #include "msgpack.h"
       2             : 
       3             : #include <cassert>
       4             : #include <climits>
       5             : #include <limits>
       6             : #include <type_traits>
       7             : 
       8             : #include "error.h"
       9             : 
      10             : namespace datadog {
      11             : namespace tracing {
      12             : namespace msgpack {
      13             : namespace {
      14             : // MessagePack values are prefixed by a byte naming their type.
      15             : namespace types {
      16             : constexpr auto ARRAY32 = std::byte(0xDD);
      17             : constexpr auto DOUBLE = std::byte(0xCB);
      18             : constexpr auto INT64 = std::byte(0xD3);
      19             : constexpr auto MAP32 = std::byte(0xDF);
      20             : constexpr auto STR32 = std::byte(0xDB);
      21             : constexpr auto UINT64 = std::byte(0xCF);
      22             : }  // namespace types
      23             : 
      24           5 : std::string make_overflow_message(StringView type, std::size_t actual,
      25             :                                   std::size_t max) {
      26           5 :   std::string message;
      27           5 :   message += "Cannot msgpack encode ";
      28           5 :   append(message, type);
      29           5 :   message += " of size ";
      30           5 :   message += std::to_string(actual);
      31           5 :   message += ", which exceeds the protocol maximum of ";
      32           5 :   message += std::to_string(max);
      33           5 :   message += '.';
      34           5 :   return message;
      35           0 : }
      36             : 
      37             : template <typename Integer>
      38       17153 : void push_number_big_endian(std::string& buffer, Integer integer) {
      39             :   // Assume two's complement.
      40       17153 :   const std::make_unsigned_t<Integer> value = integer;
      41             : 
      42             :   // The loop below is more likely to unroll if we don't call any functions
      43             :   // within it.
      44             :   char buf[sizeof value];
      45             : 
      46             :   // The most significant byte of `value` goes to the front of `buf`, and the
      47             :   // least significant byte of `value` goes
      48             :   // to the back of `buf`, and so on in between.
      49             :   // On a big endian architecture, this is just a complicated way to copy
      50             :   // `value`. On a little endian architecture, which is much more common, this
      51             :   // effectively copies the bytes of `value` backwards.
      52       17153 :   const int size = sizeof value;
      53      100817 :   for (int i = 0; i < size; ++i) {
      54       83664 :     const char byte = (value >> (CHAR_BIT * ((size - 1) - i))) & 0xFF;
      55       83664 :     buf[i] = byte;
      56             :   }
      57             : 
      58       17153 :   buffer.append(buf, sizeof buf);
      59       17153 : }
      60             : 
      61             : }  // namespace
      62             : 
      63         419 : void pack_integer(std::string& buffer, std::int64_t value) {
      64         419 :   buffer.push_back(static_cast<char>(types::INT64));
      65         419 :   push_number_big_endian(buffer, static_cast<std::int64_t>(value));
      66         419 : }
      67             : 
      68        2095 : void pack_integer(std::string& buffer, std::uint64_t value) {
      69        2095 :   buffer.push_back(static_cast<char>(types::UINT64));
      70        2095 :   push_number_big_endian(buffer, static_cast<std::uint64_t>(value));
      71        2095 : }
      72             : 
      73        1249 : void pack_double(std::string& buffer, double value) {
      74        1249 :   buffer.push_back(static_cast<char>(types::DOUBLE));
      75             : 
      76             :   // The following is lifted from the "msgpack-c" project.
      77             :   // See "pack_double" in
      78             :   // <https://github.com/msgpack/msgpack-c/blob/cpp_master/include/msgpack/v1/pack.hpp>
      79             : 
      80             :   union {
      81             :     double as_double;
      82             :     uint64_t as_integer;
      83             :   } memory;
      84        1249 :   memory.as_double = value;
      85             : 
      86             : #if defined(TARGET_OS_IPHONE)
      87             :   // ok
      88             : #elif defined(__arm__) && !(__ARM_EABI__)  // arm-oabi
      89             :   // https://github.com/msgpack/msgpack-perl/pull/1
      90             :   memory.as_integer =
      91             :       (memory.as_integer & 0xFFFFFFFFUL) << 32UL | (memory.as_integer >> 32UL);
      92             : #endif
      93             : 
      94        1249 :   push_number_big_endian(buffer, memory.as_integer);
      95        1249 : }
      96             : 
      97       11295 : Expected<void> pack_string(std::string& buffer, const char* begin,
      98             :                            std::size_t size) {
      99       11295 :   const auto max = std::numeric_limits<std::uint32_t>::max();
     100       11295 :   if (size > max) {
     101           2 :     return Error{Error::MESSAGEPACK_ENCODE_FAILURE,
     102           1 :                  make_overflow_message("string", size, max)};
     103             :   }
     104       11294 :   buffer.push_back(static_cast<char>(types::STR32));
     105       11294 :   push_number_big_endian(buffer, static_cast<std::uint32_t>(size));
     106       11294 :   buffer.append(begin, size);
     107       11294 :   return {};
     108             : }
     109             : 
     110         839 : Expected<void> pack_array(std::string& buffer, std::size_t size) {
     111         839 :   const auto max = std::numeric_limits<std::uint32_t>::max();
     112         839 :   if (size > max) {
     113           4 :     return Error{Error::MESSAGEPACK_ENCODE_FAILURE,
     114           2 :                  make_overflow_message("array", size, max)};
     115             :   }
     116         837 :   buffer.push_back(static_cast<char>(types::ARRAY32));
     117         837 :   push_number_big_endian(buffer, static_cast<std::uint32_t>(size));
     118         837 :   return {};
     119             : }
     120             : 
     121        1261 : Expected<void> pack_map(std::string& buffer, std::size_t size) {
     122        1261 :   const auto max = std::numeric_limits<std::uint32_t>::max();
     123        1261 :   if (size > max) {
     124           4 :     return Error{Error::MESSAGEPACK_ENCODE_FAILURE,
     125           2 :                  make_overflow_message("map", size, max)};
     126             :   }
     127        1259 :   buffer.push_back(static_cast<char>(types::MAP32));
     128        1259 :   push_number_big_endian(buffer, static_cast<std::uint32_t>(size));
     129        1259 :   return {};
     130             : }
     131             : 
     132             : }  // namespace msgpack
     133             : }  // namespace tracing
     134             : }  // namespace datadog

Generated by: LCOV version 1.16