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