diff --git a/README.md b/README.md index 1615eb2..ae7e0a8 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,13 @@ Check out [this blog](https://mikeloomisgg.github.io/2019-07-02-making-a-seriali - Easy error handling ### Single Header only template library -Want to use this library? Just #include the header and you're good to go. Its less than 1000 lines of code. - +Want to use this library? Just #include the header and you're good to go. You can also install it like a normal cmake package if you know how that works. ### Cereal style packaging Easily pack objects into byte arrays using a pack free function: +Be aware that you will need to use the nvp_pack (name value pair) methods to have interop with python or javascript msgpack libraries. + ```c++ struct Person { std::string name; @@ -28,7 +29,7 @@ struct Person { std::vector aliases; template - void msgpack(T &pack) { + void pack(T &pack) { pack(name, age, aliases); } }; @@ -47,7 +48,6 @@ int main() { ### Roadmap - Support for extension types - The msgpack spec allows for additional types to be enumerated as Extensions. If reasonable use cases come about for this feature then it may be added. -- Name/value pairs - - The msgpack spec uses the 'map' type differently than this library. This library implements maps in which key/value pairs must all have the same value types. +- Support for unpacking types memberwise with callbacks for async reading of packed objects - Endian conversion shortcuts - On platforms that already hold types in big endian, the serialization could be optimized using type traits. diff --git a/msgpack/include/msgpack/msgpack.hpp b/msgpack/include/msgpack/msgpack.hpp index a300d5d..cf5a077 100644 --- a/msgpack/include/msgpack/msgpack.hpp +++ b/msgpack/include/msgpack/msgpack.hpp @@ -13,10 +13,12 @@ #include #include #include +#include namespace msgpack { enum class UnpackerError { - OutOfRange = 1 + OutOfRange = 1, + BadInput = 2 }; struct UnpackerErrCategory : public std::error_category { @@ -29,6 +31,8 @@ struct UnpackerErrCategory : public std::error_category { switch (static_cast(ev)) { case msgpack::UnpackerError::OutOfRange: return "tried to dereference out of range during deserialization"; + case msgpack::UnpackerError::BadInput: + return "data decoded does not match type of object"; default: return "(unrecognized error)"; } @@ -145,17 +149,39 @@ struct is_map > { static const bool value = true; }; +struct skip {}; + +template class Packer { public: - template void operator()(const Types &... args) { - (pack_type(std::forward(args)), ...); + if constexpr(nvp_packing) { + std::map placeholder{}; + const std::size_t n = sizeof...(Types); + for (auto i = 0U; i < n; i++) { + placeholder[std::to_string(i)]; + } + pack_type(placeholder); + (pack_nvp(std::forward(args)), ...); + } else { + (pack_type(std::forward(args)), ...); + } } template void process(const Types &... args) { - (pack_type(std::forward(args)), ...); + if constexpr(nvp_packing) { + std::map placeholder{}; + const std::size_t n = sizeof...(Types); + for (auto i = 0U; i < n; i++) { + placeholder[std::to_string(i)]; + } + pack_type(placeholder); + (pack_nvp(std::forward(args)), ...); + } else { + (pack_type(std::forward(args)), ...); + } } const std::vector &vector() const { @@ -167,7 +193,15 @@ class Packer { } private: - std::vector serialized_object; + std::vector serialized_object{}; + std::size_t current_index{}; + + template + void pack_nvp(const T &value) { + pack_type(std::to_string(current_index)); + pack_type(value); + current_index++; + } template void pack_type(const T &value) { @@ -176,9 +210,10 @@ class Packer { } else if constexpr (is_container::value || is_stdarray::value) { pack_array(value); } else { - auto recursive_packer = Packer{}; + auto recursive_packer = Packer{}; const_cast(value).pack(recursive_packer); - pack_type(recursive_packer.vector()); + auto vec = recursive_packer.vector(); + serialized_object.insert(serialized_object.end(), vec.begin(), vec.end()); } } @@ -232,6 +267,23 @@ class Packer { } } + void pack_map(std::map map) { + if (map.size() < 16) { + auto size_mask = uint8_t(0b10000000); + serialized_object.emplace_back(uint8_t(map.size() | size_mask)); + } else if (map.size() < std::numeric_limits::max()) { + serialized_object.emplace_back(map16); + for (auto i = sizeof(uint16_t); i > 0; --i) { + serialized_object.emplace_back(uint8_t(map.size() >> (8U * (i - 1)) & 0xff)); + } + } else if (map.size() < std::numeric_limits::max()) { + serialized_object.emplace_back(map32); + for (auto i = sizeof(uint32_t); i > 0; --i) { + serialized_object.emplace_back(uint8_t(map.size() >> (8U * (i - 1)) & 0xff)); + } + } + } + std::bitset<64> twos_complement(int64_t value) { if (value < 0) { auto abs_v = llabs(value); @@ -267,241 +319,219 @@ class Packer { return {(uint8_t) value}; } } -}; -template<> -inline -void Packer::pack_type(const int8_t &value) { - if (value > 31 || value < -32) { - serialized_object.emplace_back(int8); + void pack_type(const skip &value) { } - serialized_object.emplace_back(uint8_t(twos_complement(value).to_ulong())); -} -template<> -inline -void Packer::pack_type(const int16_t &value) { - if (abs(value) < abs(std::numeric_limits::min())) { - pack_type(int8_t(value)); - } else { - serialized_object.emplace_back(int16); - auto serialize_value = uint16_t(twos_complement(value).to_ulong()); - for (auto i = sizeof(value); i > 0; --i) { - serialized_object.emplace_back(uint8_t(serialize_value >> (8U * (i - 1)) & 0xff)); + void pack_type(const int8_t &value) { + if (value > 31 || value < -32) { + serialized_object.emplace_back(int8); } + serialized_object.emplace_back(uint8_t(twos_complement(value).to_ulong())); } -} -template<> -inline -void Packer::pack_type(const int32_t &value) { - if (abs(value) < abs(std::numeric_limits::min())) { - pack_type(int16_t(value)); - } else { - serialized_object.emplace_back(int32); - auto serialize_value = uint32_t(twos_complement(value).to_ulong()); - for (auto i = sizeof(value); i > 0; --i) { - serialized_object.emplace_back(uint8_t(serialize_value >> (8U * (i - 1)) & 0xff)); + void pack_type(const int16_t &value) { + if (abs(value) < abs(std::numeric_limits::min())) { + pack_type(int8_t(value)); + } else { + serialized_object.emplace_back(int16); + auto serialize_value = uint16_t(twos_complement(value).to_ulong()); + for (auto i = sizeof(value); i > 0; --i) { + serialized_object.emplace_back(uint8_t(serialize_value >> (8U * (i - 1)) & 0xff)); + } } } -} -template<> -inline -void Packer::pack_type(const int64_t &value) { - if (llabs(value) < llabs(std::numeric_limits::min()) && value != std::numeric_limits::min()) { - pack_type(int32_t(value)); - } else { - serialized_object.emplace_back(int64); - auto serialize_value = uint64_t(twos_complement(value).to_ullong()); - for (auto i = sizeof(value); i > 0; --i) { - serialized_object.emplace_back(uint8_t(serialize_value >> (8U * (i - 1)) & 0xff)); + void pack_type(const int32_t &value) { + if (abs(value) < abs(std::numeric_limits::min())) { + pack_type(int16_t(value)); + } else { + serialized_object.emplace_back(int32); + auto serialize_value = uint32_t(twos_complement(value).to_ulong()); + for (auto i = sizeof(value); i > 0; --i) { + serialized_object.emplace_back(uint8_t(serialize_value >> (8U * (i - 1)) & 0xff)); + } } } -} -template<> -inline -void Packer::pack_type(const uint8_t &value) { - if (value <= 0x7f) { - serialized_object.emplace_back(value); - } else { - serialized_object.emplace_back(uint8); - serialized_object.emplace_back(value); + void pack_type(const int64_t &value) { + if (llabs(value) < llabs(std::numeric_limits::min()) && value != std::numeric_limits::min()) { + pack_type(int32_t(value)); + } else { + serialized_object.emplace_back(int64); + auto serialize_value = uint64_t(twos_complement(value).to_ullong()); + for (auto i = sizeof(value); i > 0; --i) { + serialized_object.emplace_back(uint8_t(serialize_value >> (8U * (i - 1)) & 0xff)); + } + } } -} -template<> -inline -void Packer::pack_type(const uint16_t &value) { - if (value > std::numeric_limits::max()) { - serialized_object.emplace_back(uint16); - for (auto i = sizeof(value); i > 0U; --i) { - serialized_object.emplace_back(uint8_t(value >> (8U * (i - 1)) & 0xff)); + void pack_type(const uint8_t &value) { + if (value <= 0x7f) { + serialized_object.emplace_back(value); + } else { + serialized_object.emplace_back(uint8); + serialized_object.emplace_back(value); } - } else { - pack_type(uint8_t(value)); } -} -template<> -inline -void Packer::pack_type(const uint32_t &value) { - if (value > std::numeric_limits::max()) { - serialized_object.emplace_back(uint32); - for (auto i = sizeof(value); i > 0U; --i) { - serialized_object.emplace_back(uint8_t(value >> (8U * (i - 1)) & 0xff)); + void pack_type(const uint16_t &value) { + if (value > std::numeric_limits::max()) { + serialized_object.emplace_back(uint16); + for (auto i = sizeof(value); i > 0U; --i) { + serialized_object.emplace_back(uint8_t(value >> (8U * (i - 1)) & 0xff)); + } + } else { + pack_type(uint8_t(value)); } - } else { - pack_type(uint16_t(value)); } -} -template<> -inline -void Packer::pack_type(const uint64_t &value) { - if (value > std::numeric_limits::max()) { - serialized_object.emplace_back(uint64); - for (auto i = sizeof(value); i > 0U; --i) { - serialized_object.emplace_back(uint8_t(value >> (8U * (i - 1)) & 0xff)); + void pack_type(const uint32_t &value) { + if (value > std::numeric_limits::max()) { + serialized_object.emplace_back(uint32); + for (auto i = sizeof(value); i > 0U; --i) { + serialized_object.emplace_back(uint8_t(value >> (8U * (i - 1)) & 0xff)); + } + } else { + pack_type(uint16_t(value)); } - } else { - pack_type(uint32_t(value)); } -} -template<> -inline -void Packer::pack_type(const std::nullptr_t &/*value*/) { - serialized_object.emplace_back(nil); -} + void pack_type(const uint64_t &value) { + if (value > std::numeric_limits::max()) { + serialized_object.emplace_back(uint64); + for (auto i = sizeof(value); i > 0U; --i) { + serialized_object.emplace_back(uint8_t(value >> (8U * (i - 1)) & 0xff)); + } + } else { + pack_type(uint32_t(value)); + } + } -template<> -inline -void Packer::pack_type(const bool &value) { - if (value) { - serialized_object.emplace_back(true_bool); - } else { - serialized_object.emplace_back(false_bool); + void pack_type(const std::nullptr_t &/*value*/) { + serialized_object.emplace_back(nil); } -} -template<> -inline -void Packer::pack_type(const float &value) { - double integral_part; - auto fractional_remainder = float(modf(value, &integral_part)); - - if (fractional_remainder == 0) { // Just pack as int - pack_type(int64_t(integral_part)); - } else { - static_assert(std::numeric_limits::radix == 2); // TODO: Handle decimal floats - auto exponent = ilogb(value); - float full_mantissa = value / float(scalbn(1.0, exponent)); - auto sign_mask = std::bitset<32>(uint32_t(std::signbit(full_mantissa)) << 31); - auto excess_127_exponent_mask = std::bitset<32>(uint32_t(exponent + 127) << 23); - auto normalized_mantissa_mask = std::bitset<32>(); - float implied_mantissa = fabs(full_mantissa) - 1.0f; - for (auto i = 23U; i > 0; --i) { - integral_part = 0; - implied_mantissa *= 2; - implied_mantissa = float(modf(implied_mantissa, &integral_part)); - if (uint8_t(integral_part) == 1) { - normalized_mantissa_mask |= std::bitset<32>(uint32_t(1 << (i - 1))); - } + void pack_type(const bool &value) { + if (value) { + serialized_object.emplace_back(true_bool); + } else { + serialized_object.emplace_back(false_bool); } + } + + void pack_type(const float &value) { + double integral_part; + auto fractional_remainder = float(modf(value, &integral_part)); - uint32_t ieee754_float32 = (sign_mask | excess_127_exponent_mask | normalized_mantissa_mask).to_ulong(); - serialized_object.emplace_back(float32); - for (auto i = sizeof(uint32_t); i > 0; --i) { - serialized_object.emplace_back(uint8_t(ieee754_float32 >> (8U * (i - 1)) & 0xff)); + if (fractional_remainder == 0) { // Just pack as int + pack_type(int64_t(integral_part)); + } else { + // TODO: Handle decimal floats, some systems will use base 10 floats. Until then don't compile + static_assert(std::numeric_limits::radix == 2); + auto exponent = ilogb(value); + float full_mantissa = value / float(scalbn(1.0, exponent)); + auto sign_mask = std::bitset<32>(uint32_t(std::signbit(full_mantissa)) << 31); + auto excess_127_exponent_mask = std::bitset<32>(uint32_t(exponent + 127) << 23); + auto normalized_mantissa_mask = std::bitset<32>(); + float implied_mantissa = fabs(full_mantissa) - 1.0f; + for (auto i = 23U; i > 0; --i) { + integral_part = 0; + implied_mantissa *= 2; + implied_mantissa = float(modf(implied_mantissa, &integral_part)); + if (uint8_t(integral_part) == 1) { + normalized_mantissa_mask |= std::bitset<32>(uint32_t(1 << (i - 1))); + } + } + + uint32_t ieee754_float32 = (sign_mask | excess_127_exponent_mask | normalized_mantissa_mask).to_ulong(); + serialized_object.emplace_back(float32); + for (auto i = sizeof(uint32_t); i > 0; --i) { + serialized_object.emplace_back(uint8_t(ieee754_float32 >> (8U * (i - 1)) & 0xff)); + } } } -} -template<> -inline -void Packer::pack_type(const double &value) { - double integral_part; - double fractional_remainder = modf(value, &integral_part); - - if (fractional_remainder == 0) { // Just pack as int - pack_type(int64_t(integral_part)); - } else { - static_assert(std::numeric_limits::radix == 2); // TODO: Handle decimal floats - auto exponent = ilogb(value); - double full_mantissa = value / scalbn(1.0, exponent); - auto sign_mask = std::bitset<64>(uint64_t(std::signbit(full_mantissa)) << 63); - auto excess_127_exponent_mask = std::bitset<64>(uint64_t(exponent + 1023) << 52); - auto normalized_mantissa_mask = std::bitset<64>(); - double implied_mantissa = fabs(full_mantissa) - 1.0f; - - for (auto i = 52U; i > 0; --i) { - integral_part = 0; - implied_mantissa *= 2; - implied_mantissa = modf(implied_mantissa, &integral_part); - if (uint8_t(integral_part) == 1) { - normalized_mantissa_mask |= std::bitset<64>(uint64_t(1) << (i - 1)); + // TODO: Tested serialized doubles from javascript weren't unpacking correctly + void pack_type(const double &value) { + double integral_part; + double fractional_remainder = modf(value, &integral_part); + + if (fractional_remainder == 0) { // Just pack as int + pack_type(int64_t(integral_part)); + } else { + static_assert(std::numeric_limits::radix == 2); + auto exponent = ilogb(value); + double full_mantissa = value / scalbn(1.0, exponent); + auto sign_mask = std::bitset<64>(uint64_t(std::signbit(full_mantissa)) << 63); + auto excess_127_exponent_mask = std::bitset<64>(uint64_t(exponent + 1023) << 52); + auto normalized_mantissa_mask = std::bitset<64>(); + double implied_mantissa = fabs(full_mantissa) - 1.0f; + + for (auto i = 52U; i > 0; --i) { + integral_part = 0; + implied_mantissa *= 2; + implied_mantissa = modf(implied_mantissa, &integral_part); + if (uint8_t(integral_part) == 1) { + normalized_mantissa_mask |= std::bitset<64>(uint64_t(1) << (i - 1)); + } + } + auto ieee754_float64 = (sign_mask | excess_127_exponent_mask | normalized_mantissa_mask).to_ullong(); + serialized_object.emplace_back(float64); + for (auto i = sizeof(ieee754_float64); i > 0; --i) { + serialized_object.emplace_back(uint8_t(ieee754_float64 >> (8U * (i - 1)) & 0xff)); } - } - auto ieee754_float64 = (sign_mask | excess_127_exponent_mask | normalized_mantissa_mask).to_ullong(); - serialized_object.emplace_back(float64); - for (auto i = sizeof(ieee754_float64); i > 0; --i) { - serialized_object.emplace_back(uint8_t(ieee754_float64 >> (8U * (i - 1)) & 0xff)); } } -} -template<> -inline -void Packer::pack_type(const std::string &value) { - if (value.size() < 32) { - serialized_object.emplace_back(uint8_t(value.size()) | 0b10100000); - } else if (value.size() < std::numeric_limits::max()) { - serialized_object.emplace_back(str8); - serialized_object.emplace_back(uint8_t(value.size())); - } else if (value.size() < std::numeric_limits::max()) { - serialized_object.emplace_back(str16); - for (auto i = sizeof(uint16_t); i > 0; --i) { - serialized_object.emplace_back(uint8_t(value.size() >> (8U * (i - 1)) & 0xff)); + void pack_type(const std::string &value) { + if (value.size() < 32) { + serialized_object.emplace_back(uint8_t(value.size()) | 0b10100000); + } else if (value.size() < std::numeric_limits::max()) { + serialized_object.emplace_back(str8); + serialized_object.emplace_back(uint8_t(value.size())); + } else if (value.size() < std::numeric_limits::max()) { + serialized_object.emplace_back(str16); + for (auto i = sizeof(uint16_t); i > 0; --i) { + serialized_object.emplace_back(uint8_t(value.size() >> (8U * (i - 1)) & 0xff)); + } + } else if (value.size() < std::numeric_limits::max()) { + serialized_object.emplace_back(str32); + for (auto i = sizeof(uint32_t); i > 0; --i) { + serialized_object.emplace_back(uint8_t(value.size() >> (8U * (i - 1)) & 0xff)); + } + } else { + return; // Give up if string is too long } - } else if (value.size() < std::numeric_limits::max()) { - serialized_object.emplace_back(str32); - for (auto i = sizeof(uint32_t); i > 0; --i) { - serialized_object.emplace_back(uint8_t(value.size() >> (8U * (i - 1)) & 0xff)); + for (char i : value) { + serialized_object.emplace_back(static_cast(i)); } - } else { - return; // Give up if string is too long - } - for (char i : value) { - serialized_object.emplace_back(static_cast(i)); } -} -template<> -inline -void Packer::pack_type(const std::vector &value) { - if (value.size() < std::numeric_limits::max()) { - serialized_object.emplace_back(bin8); - serialized_object.emplace_back(uint8_t(value.size())); - } else if (value.size() < std::numeric_limits::max()) { - serialized_object.emplace_back(bin16); - for (auto i = sizeof(uint16_t); i > 0; --i) { - serialized_object.emplace_back(uint8_t(value.size() >> (8U * (i - 1)) & 0xff)); + void pack_type(const std::vector &value) { + if (value.size() < std::numeric_limits::max()) { + serialized_object.emplace_back(bin8); + serialized_object.emplace_back(uint8_t(value.size())); + } else if (value.size() < std::numeric_limits::max()) { + serialized_object.emplace_back(bin16); + for (auto i = sizeof(uint16_t); i > 0; --i) { + serialized_object.emplace_back(uint8_t(value.size() >> (8U * (i - 1)) & 0xff)); + } + } else if (value.size() < std::numeric_limits::max()) { + serialized_object.emplace_back(bin32); + for (auto i = sizeof(uint32_t); i > 0; --i) { + serialized_object.emplace_back(uint8_t(value.size() >> (8U * (i - 1)) & 0xff)); + } + } else { + return; // Give up if vector is too large } - } else if (value.size() < std::numeric_limits::max()) { - serialized_object.emplace_back(bin32); - for (auto i = sizeof(uint32_t); i > 0; --i) { - serialized_object.emplace_back(uint8_t(value.size() >> (8U * (i - 1)) & 0xff)); + for (const auto &elem : value) { + serialized_object.emplace_back(elem); } - } else { - return; // Give up if vector is too large } - for (const auto &elem : value) { - serialized_object.emplace_back(elem); - } -} +}; +template class Unpacker { public: Unpacker() : data_pointer(nullptr), data_end(nullptr) {}; @@ -511,7 +541,17 @@ class Unpacker { template void operator()(Types &... args) { - (unpack_type(std::forward(args)), ...); + if constexpr(nvp_unpacking) { + std::map placeholder{}; + const std::size_t n = sizeof...(Types); + for (auto i = 0U; i < n; i++) { + placeholder[std::to_string(i)]; + } + unpack_type(placeholder); + (unpack_nvp(std::forward(args)), ...); + } else { + (unpack_type(std::forward(args)), ...); + } } template @@ -524,11 +564,16 @@ class Unpacker { data_end = data_pointer + size; } + std::size_t bytes() { + return bytes_consumed; + } + std::error_code ec{}; private: const uint8_t *data_pointer; const uint8_t *data_end; + std::size_t bytes_consumed{}; uint8_t safe_data() { if (data_pointer < data_end) @@ -540,6 +585,7 @@ class Unpacker { void safe_increment(int64_t bytes = 1) { if (data_end - data_pointer >= 0) { data_pointer += bytes; + bytes_consumed += bytes; } else { ec = UnpackerError::OutOfRange; } @@ -554,12 +600,10 @@ class Unpacker { } else if constexpr (is_stdarray::value) { unpack_stdarray(value); } else { - auto recursive_data = std::vector{}; - unpack_type(recursive_data); - - auto recursive_unpacker = Unpacker{recursive_data.data(), recursive_data.size()}; - value.pack(recursive_unpacker); - ec = recursive_unpacker.ec; + auto unpacker = Unpacker{data_pointer, static_cast(data_end - data_pointer)}; + value.pack(unpacker); + safe_increment(unpacker.bytes()); + ec = unpacker.ec; } } @@ -631,7 +675,6 @@ class Unpacker { map_size += uint32_t(safe_data()) << 8 * (i - 1); safe_increment(); } - std::vector x{}; for (auto i = 0U; i < map_size; ++i) { KeyType key{}; MappedType value{}; @@ -665,352 +708,515 @@ class Unpacker { } } } -}; -template<> -inline -void Unpacker::unpack_type(int8_t &value) { - if (safe_data() == int8) { - safe_increment(); - value = safe_data(); - safe_increment(); - } else { - value = safe_data(); - safe_increment(); - } -} - -template<> -inline -void Unpacker::unpack_type(int16_t &value) { - if (safe_data() == int16) { - safe_increment(); - std::bitset<16> bits; - for (auto i = sizeof(uint16_t); i > 0; --i) { - bits |= uint16_t(safe_data()) << 8 * (i - 1); + void unpack_map(std::map /*map*/) { + if (safe_data() == map32) { safe_increment(); - } - if (bits[15]) { - value = -1 * (uint16_t((~bits).to_ulong()) + 1); + std::size_t map_size = 0; + for (auto i = sizeof(uint32_t); i > 0; --i) { + map_size += uint32_t(safe_data()) << 8 * (i - 1); + safe_increment(); + } + } else if (safe_data() == map16) { + safe_increment(); + std::size_t map_size = 0; + for (auto i = sizeof(uint16_t); i > 0; --i) { + map_size += uint16_t(safe_data()) << 8 * (i - 1); + safe_increment(); + } } else { - value = uint16_t(bits.to_ulong()); + safe_increment(); } - } else if (safe_data() == int8) { - int8_t val; - unpack_type(val); - value = val; - } else { - value = safe_data(); - safe_increment(); } -} -template<> -inline -void Unpacker::unpack_type(int32_t &value) { - if (safe_data() == int32) { - safe_increment(); - std::bitset<32> bits; - for (auto i = sizeof(uint32_t); i > 0; --i) { - bits |= uint32_t(safe_data()) << 8 * (i - 1); - safe_increment(); - } - if (bits[31]) { - value = -1 * ((~bits).to_ulong() + 1); - } else { - value = bits.to_ulong(); - } - } else if (safe_data() == int16) { - int16_t val; - unpack_type(val); - value = val; - } else if (safe_data() == int8) { - int8_t val; - unpack_type(val); - value = val; - } else { - value = safe_data(); - safe_increment(); + template + void unpack_nvp(T &value) { + std::string throwaway{}; + unpack_type(throwaway); + unpack_type(value); } -} -template<> -inline -void Unpacker::unpack_type(int64_t &value) { - if (safe_data() == int64) { - safe_increment(); - std::bitset<64> bits; - for (auto i = sizeof(value); i > 0; --i) { - bits |= std::bitset<8>(safe_data()).to_ullong() << 8 * (i - 1); - safe_increment(); + void unpack_type(int8_t &value) { + auto flag = safe_data(); + switch (flag) { + case int8: { + safe_increment(); + value = safe_data(); + safe_increment(); + break; + } + case uint8: { + uint8_t val{}; + unpack_type(val); + value = val; + break; + } + default: + value = safe_data(); + safe_increment(); + break; } - if (bits[63]) { - value = -1 * ((~bits).to_ullong() + 1); - } else { - value = bits.to_ullong(); + } + + void unpack_type(int16_t &value) { + auto flag = safe_data(); + switch (flag) { + case int16: { + safe_increment(); + std::bitset<16> bits; + for (auto i = sizeof(uint16_t); i > 0; --i) { + bits |= uint16_t(safe_data()) << 8 * (i - 1); + safe_increment(); + } + if (bits[15]) { + value = -1 * (uint16_t((~bits).to_ulong()) + 1); + } else { + value = uint16_t(bits.to_ulong()); + } + break; + } + case int8: { + int8_t val{}; + unpack_type(val); + value = val; + break; + } + case uint8: { + uint8_t val{}; + unpack_type(val); + value = val; + break; + } + default: + value = safe_data(); + safe_increment(); + break; } - } else if (safe_data() == int32) { - int32_t val; - unpack_type(val); - value = val; - } else if (safe_data() == int16) { - int16_t val; - unpack_type(val); - value = val; - } else if (safe_data() == int8) { - int8_t val; - unpack_type(val); - value = val; - } else { - value = safe_data(); - safe_increment(); } -} -template<> -inline -void Unpacker::unpack_type(uint8_t &value) { - if (safe_data() == uint8) { - safe_increment(); - value = safe_data(); - safe_increment(); - } else { - value = safe_data(); - safe_increment(); + void unpack_type(int32_t &value) { + auto flag = safe_data(); + switch (flag) { + case int32: { + safe_increment(); + std::bitset<32> bits; + for (auto i = sizeof(uint32_t); i > 0; --i) { + bits |= uint32_t(safe_data()) << 8 * (i - 1); + safe_increment(); + } + if (bits[31]) { + value = -1 * ((~bits).to_ulong() + 1); + } else { + value = bits.to_ulong(); + } + break; + } + case int16: { + int16_t val{}; + unpack_type(val); + value = val; + break; + } + case int8: { + int8_t val{}; + unpack_type(val); + value = val; + break; + } + case uint16: { + uint16_t val{}; + unpack_type(val); + value = val; + break; + } + case uint8: { + uint8_t val{}; + unpack_type(val); + value = val; + break; + } + default: + value = safe_data(); + safe_increment(); + break; + } } -} -template<> -inline -void Unpacker::unpack_type(uint16_t &value) { - if (safe_data() == uint16) { - safe_increment(); - for (auto i = sizeof(uint16_t); i > 0; --i) { - value += safe_data() << 8 * (i - 1); - safe_increment(); + void unpack_type(int64_t &value) { + auto flag = safe_data(); + switch (flag) { + case int64: { + safe_increment(); + std::bitset<64> bits; + for (auto i = sizeof(value); i > 0; --i) { + bits |= std::bitset<8>(safe_data()).to_ullong() << 8 * (i - 1); + safe_increment(); + } + if (bits[63]) { + value = -1 * ((~bits).to_ullong() + 1); + } else { + value = bits.to_ullong(); + } + break; + } + case int32: { + int32_t val{}; + unpack_type(val); + value = val; + break; + } + case int16: { + int16_t val{}; + unpack_type(val); + value = val; + break; + } + case int8: { + int8_t val{}; + unpack_type(val); + value = val; + break; + } + case uint32: { + uint32_t val{}; + unpack_type(val); + value = val; + break; + } + case uint16: { + uint16_t val{}; + unpack_type(val); + value = val; + break; + } + case uint8: { + uint8_t val{}; + unpack_type(val); + value = val; + break; + } + default: + value = safe_data(); + safe_increment(); + break; } - } else if (safe_data() == uint8) { - safe_increment(); - value = safe_data(); - safe_increment(); - } else { - value = safe_data(); - safe_increment(); } -} -template<> -inline -void Unpacker::unpack_type(uint32_t &value) { - if (safe_data() == uint32) { - safe_increment(); - for (auto i = sizeof(uint32_t); i > 0; --i) { - value += safe_data() << 8 * (i - 1); - safe_increment(); + void unpack_type(uint8_t &value) { + auto flag = safe_data(); + switch (flag) { + case uint8: { + safe_increment(); + value = safe_data(); + safe_increment(); + break; + } + case int8: { + safe_increment(); + value = safe_data(); + safe_increment(); + break; + } + default: + value = safe_data(); + safe_increment(); + break; } - } else if (safe_data() == uint16) { - safe_increment(); - for (auto i = sizeof(uint16_t); i > 0; --i) { - value += safe_data() << 8 * (i - 1); - safe_increment(); + } + + void unpack_type(uint16_t &value) { + auto flag = safe_data(); + switch (flag) { + case uint16: { + safe_increment(); + for (auto i = sizeof(uint16_t); i > 0; --i) { + value += safe_data() << 8 * (i - 1); + safe_increment(); + } + break; + } + case uint8: { + uint8_t val{}; + unpack_type(val); + value = static_cast(val); + break; + } + case int16: { + int16_t val{}; + unpack_type(val); + value = static_cast(val); + break; + } + case int8: { + int8_t val{}; + unpack_type(val); + value = static_cast(val); + break; + } + default: + value = safe_data(); + safe_increment(); + break; } - } else if (safe_data() == uint8) { - safe_increment(); - value = safe_data(); - safe_increment(); - } else { - value = safe_data(); - safe_increment(); } -} -template<> -inline -void Unpacker::unpack_type(uint64_t &value) { - if (safe_data() == uint64) { - safe_increment(); - for (auto i = sizeof(uint64_t); i > 0; --i) { - value += uint64_t(safe_data()) << 8 * (i - 1); - safe_increment(); + void unpack_type(uint32_t &value) { + auto flag = safe_data(); + switch (flag) { + case uint32: { + safe_increment(); + for (auto i = sizeof(uint32_t); i > 0; --i) { + value += safe_data() << 8 * (i - 1); + safe_increment(); + } + break; + } + case uint16: { + uint16_t val{}; + unpack_type(val); + value = static_cast(val); + break; + } + case uint8: { + uint8_t val{}; + unpack_type(val); + value = static_cast(val); + break; + } + case int32: { + int32_t val{}; + unpack_type(val); + value = static_cast(val); + break; + } + case int16: { + int16_t val{}; + unpack_type(val); + value = static_cast(val); + break; + } + case int8: { + int8_t val{}; + unpack_type(val); + value = static_cast(val); + break; + } + default: + value = safe_data(); + safe_increment(); + break; } - } else if (safe_data() == uint32) { - safe_increment(); - for (auto i = sizeof(uint32_t); i > 0; --i) { - value += uint64_t(safe_data()) << 8 * (i - 1); - safe_increment(); + } + + void unpack_type(uint64_t &value) { + auto flag = safe_data(); + switch (flag) { + case uint64: + safe_increment(); + for (auto i = sizeof(uint64_t); i > 0; --i) { + value += uint64_t(safe_data()) << 8 * (i - 1); + safe_increment(); + } + break; + case uint32: { + uint32_t val{}; + unpack_type(val); + value = val; + break; + } + case uint16: { + uint16_t val{}; + unpack_type(val); + value = val; + break; + } + case uint8: { + uint8_t val{}; + unpack_type(val); + value = val; + break; + } + case int64: { + int64_t val{}; + unpack_type(val); + value = static_cast(val); + break; + } + case int32: { + int32_t val{}; + unpack_type(val); + value = static_cast(val); + break; + } + case int16: { + int16_t val{}; + unpack_type(val); + value = static_cast(val); + break; + } + case int8: { + int8_t val{}; + unpack_type(val); + value = static_cast(val); + break; + } + default: + value = safe_data(); + safe_increment(); + break; } - data_pointer++; - } else if (safe_data() == uint16) { + } + + void unpack_type(std::nullptr_t &/*value*/) { safe_increment(); - for (auto i = sizeof(uint16_t); i > 0; --i) { - value += uint64_t(safe_data()) << 8 * (i - 1); - safe_increment(); + } + + void unpack_type(bool &value) { + if (safe_data() == 0xc3) { + value = true; + } else if (safe_data() == 0xc2) { + value = false; + } else { + ec = UnpackerError::BadInput; } - } else if (safe_data() == uint8) { - safe_increment(); - value = safe_data(); - safe_increment(); - } else { - value = safe_data(); safe_increment(); } -} -template<> -inline -void Unpacker::unpack_type(std::nullptr_t &/*value*/) { - safe_increment(); -} - -template<> -inline -void Unpacker::unpack_type(bool &value) { - value = safe_data() != 0xc2; - safe_increment(); -} - -template<> -inline -void Unpacker::unpack_type(float &value) { - if (safe_data() == float32) { - safe_increment(); - uint32_t data = 0; - for (auto i = sizeof(uint32_t); i > 0; --i) { - data += safe_data() << 8 * (i - 1); + void unpack_type(float &value) { + if (safe_data() == float32) { safe_increment(); - } - auto bits = std::bitset<32>(data); - auto mantissa = 1.0f; - for (auto i = 23U; i > 0; --i) { - if (bits[i - 1]) { - mantissa += 1.0f / (1 << (24 - i)); + uint32_t data = 0; + for (auto i = sizeof(uint32_t); i > 0; --i) { + data += safe_data() << 8 * (i - 1); + safe_increment(); } - } - if (bits[31]) { - mantissa *= -1; - } - uint8_t exponent = 0; - for (auto i = 0U; i < 8; ++i) { - exponent += bits[i + 23] << i; - } - exponent -= 127; - value = ldexp(mantissa, exponent); - } else { - if (safe_data() == int8 || safe_data() == int16 || safe_data() == int32 || safe_data() == int64) { - int64_t val = 0; - unpack_type(val); - value = float(val); + auto bits = std::bitset<32>(data); + auto mantissa = 1.0f; + for (auto i = 23U; i > 0; --i) { + if (bits[i - 1]) { + mantissa += 1.0f / (1 << (24 - i)); + } + } + if (bits[31]) { + mantissa *= -1; + } + uint8_t exponent = 0; + for (auto i = 0U; i < 8; ++i) { + exponent += bits[i + 23] << i; + } + exponent -= 127; + value = ldexp(mantissa, exponent); } else { - uint64_t val = 0; - unpack_type(val); - value = float(val); + if (safe_data() == int8 || safe_data() == int16 || safe_data() == int32 || safe_data() == int64) { + int64_t val = 0; + unpack_type(val); + value = float(val); + } else { + uint64_t val = 0; + unpack_type(val); + value = float(val); + } } } -} -template<> -inline -void Unpacker::unpack_type(double &value) { - if (safe_data() == float64) { - safe_increment(); - uint64_t data = 0; - for (auto i = sizeof(uint64_t); i > 0; --i) { - data += uint64_t(safe_data()) << 8 * (i - 1); + void unpack_type(double &value) { + if (safe_data() == float64) { safe_increment(); - } - auto bits = std::bitset<64>(data); - auto mantissa = 1.0; - for (auto i = 52U; i > 0; --i) { - if (bits[i - 1]) { - mantissa += 1.0 / (uint64_t(1) << (53 - i)); + uint64_t data = 0; + for (auto i = sizeof(uint64_t); i > 0; --i) { + data += uint64_t(safe_data()) << 8 * (i - 1); + safe_increment(); } - } - if (bits[63]) { - mantissa *= -1; - } - uint16_t exponent = 0; - for (auto i = 0U; i < 11; ++i) { - exponent += bits[i + 52] << i; - } - exponent -= 1023; - value = ldexp(mantissa, exponent); - } else { - if (safe_data() == int8 || safe_data() == int16 || safe_data() == int32 || safe_data() == int64) { - int64_t val = 0; - unpack_type(val); - value = float(val); + auto bits = std::bitset<64>(data); + auto mantissa = 1.0; + for (auto i = 52U; i > 0; --i) { + if (bits[i - 1]) { + mantissa += 1.0 / (uint64_t(1) << (53 - i)); + } + } + if (bits[63]) { + mantissa *= -1; + } + uint16_t exponent = 0; + for (auto i = 0U; i < 11; ++i) { + exponent += bits[i + 52] << i; + } + exponent -= 1023; + value = ldexp(mantissa, exponent); } else { - uint64_t val = 0; - unpack_type(val); - value = float(val); + if (safe_data() == int8 || safe_data() == int16 || safe_data() == int32 || safe_data() == int64) { + int64_t val = 0; + unpack_type(val); + value = float(val); + } else { + uint64_t val = 0; + unpack_type(val); + value = float(val); + } } } -} -template<> -inline -void Unpacker::unpack_type(std::string &value) { - std::size_t str_size = 0; - if (safe_data() == str32) { - safe_increment(); - for (auto i = sizeof(uint32_t); i > 0; --i) { - str_size += uint32_t(safe_data()) << 8 * (i - 1); + void unpack_type(std::string &value) { + std::size_t str_size = 0; + if (safe_data() == str32) { safe_increment(); - } - } else if (safe_data() == str16) { - safe_increment(); - for (auto i = sizeof(uint16_t); i > 0; --i) { - str_size += uint16_t(safe_data()) << 8 * (i - 1); + for (auto i = sizeof(uint32_t); i > 0; --i) { + str_size += uint32_t(safe_data()) << 8 * (i - 1); + safe_increment(); + } + } else if (safe_data() == str16) { safe_increment(); - } - } else if (safe_data() == str8) { - safe_increment(); - for (auto i = sizeof(uint8_t); i > 0; --i) { - str_size += uint8_t(safe_data()) << 8 * (i - 1); + for (auto i = sizeof(uint16_t); i > 0; --i) { + str_size += uint16_t(safe_data()) << 8 * (i - 1); + safe_increment(); + } + } else if (safe_data() == str8) { + safe_increment(); + for (auto i = sizeof(uint8_t); i > 0; --i) { + str_size += uint8_t(safe_data()) << 8 * (i - 1); + safe_increment(); + } + } else { + str_size = safe_data() & 0b00001111; safe_increment(); } - } else { - str_size = safe_data() & 0b00011111; - safe_increment(); - } - if (data_pointer + str_size <= data_end) { - value = std::string{data_pointer, data_pointer + str_size}; - safe_increment(str_size); - } else { - ec = UnpackerError::OutOfRange; + if (data_pointer + str_size <= data_end) { + value = std::string{data_pointer, data_pointer + str_size}; + safe_increment(str_size); + } else { + ec = UnpackerError::OutOfRange; + } } -} -template<> -inline -void Unpacker::unpack_type(std::vector &value) { - std::size_t bin_size = 0; - if (safe_data() == bin32) { - safe_increment(); - for (auto i = sizeof(uint32_t); i > 0; --i) { - bin_size += uint32_t(safe_data()) << 8 * (i - 1); + void unpack_type(std::vector &value) { + std::size_t bin_size = 0; + if (safe_data() == bin32) { safe_increment(); - } - } else if (safe_data() == bin16) { - safe_increment(); - for (auto i = sizeof(uint16_t); i > 0; --i) { - bin_size += uint16_t(safe_data()) << 8 * (i - 1); + for (auto i = sizeof(uint32_t); i > 0; --i) { + bin_size += uint32_t(safe_data()) << 8 * (i - 1); + safe_increment(); + } + } else if (safe_data() == bin16) { safe_increment(); - } - } else { - safe_increment(); - for (auto i = sizeof(uint8_t); i > 0; --i) { - bin_size += uint8_t(safe_data()) << 8 * (i - 1); + for (auto i = sizeof(uint16_t); i > 0; --i) { + bin_size += uint16_t(safe_data()) << 8 * (i - 1); + safe_increment(); + } + } else { safe_increment(); + for (auto i = sizeof(uint8_t); i > 0; --i) { + bin_size += uint8_t(safe_data()) << 8 * (i - 1); + safe_increment(); + } + } + if (data_pointer + bin_size <= data_end) { + value = std::vector{data_pointer, data_pointer + bin_size}; + safe_increment(bin_size); + } else { + ec = UnpackerError::OutOfRange; } } - if (data_pointer + bin_size <= data_end) { - value = std::vector{data_pointer, data_pointer + bin_size}; - safe_increment(bin_size); - } else { - ec = UnpackerError::OutOfRange; - } -} +}; template std::vector pack(PackableObject &obj) { @@ -1026,6 +1232,20 @@ std::vector pack(PackableObject &&obj) { return packer.vector(); } +template +std::vector nvp_pack(PackableObject &obj) { + auto packer = Packer{}; + obj.pack(packer); + return packer.vector(); +} + +template +std::vector nvp_pack(PackableObject &&obj) { + auto packer = Packer{}; + obj.pack(packer); + return packer.vector(); +} + template UnpackableObject unpack(const uint8_t *data_start, const std::size_t size, std::error_code &ec) { auto obj = UnpackableObject{}; @@ -1051,6 +1271,32 @@ UnpackableObject unpack(const std::vector &data) { std::error_code ec; return unpack(data.data(), data.size(), ec); } + +template +UnpackableObject nvp_unpack(const uint8_t *data_start, const std::size_t size, std::error_code &ec) { + auto obj = UnpackableObject{}; + auto unpacker = Unpacker(data_start, size); + obj.pack(unpacker); + ec = unpacker.ec; + return obj; +} + +template +UnpackableObject nvp_unpack(const uint8_t *data_start, const std::size_t size) { + std::error_code ec{}; + return nvp_unpack(data_start, size, ec); +} + +template +UnpackableObject nvp_unpack(const std::vector &data, std::error_code &ec) { + return nvp_unpack(data.data(), data.size(), ec); +} + +template +UnpackableObject nvp_unpack(const std::vector &data) { + std::error_code ec; + return nvp_unpack(data.data(), data.size(), ec); +} } #endif //CPPACK_PACKER_HPP diff --git a/msgpack/tests/CMakeLists.txt b/msgpack/tests/CMakeLists.txt index a9725f2..15fad52 100644 --- a/msgpack/tests/CMakeLists.txt +++ b/msgpack/tests/CMakeLists.txt @@ -8,6 +8,7 @@ add_executable(Msgpack_tests examples.cpp error_handling.cpp object_packing_tests.cpp + nvp_packing_tests.cpp ) if (MSVC) @@ -22,3 +23,6 @@ target_link_libraries(Msgpack_tests set_target_properties(Msgpack_tests PROPERTIES CXX_STANDARD 17) target_compile_features(Msgpack_tests PUBLIC cxx_std_17) + +include(Catch) +catch_discover_tests(Msgpack_tests) \ No newline at end of file diff --git a/msgpack/tests/error_handling.cpp b/msgpack/tests/error_handling.cpp index 197b3cf..bf964ce 100644 --- a/msgpack/tests/error_handling.cpp +++ b/msgpack/tests/error_handling.cpp @@ -17,10 +17,14 @@ struct ExampleError { TEST_CASE("Unpacking a short dataset safely fails") { ExampleError example{{{"compact", true}, {"schema", false}}}; - auto data = std::vector{0x82, 0xa7, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xc3, 0xa6, 0x73, 0x63}; + auto data = std::vector{0x82, 0xa7}; std::error_code ec{}; REQUIRE(!ec); REQUIRE(example.map != msgpack::unpack(data, ec).map); REQUIRE(ec); REQUIRE(ec == msgpack::UnpackerError::OutOfRange); +} + +TEST_CASE("Unpacking a mismatched dataset safely fails") { + // TODO: Add bad input fails for all unpack types } \ No newline at end of file diff --git a/msgpack/tests/examples.cpp b/msgpack/tests/examples.cpp index 6519c67..5eab6d8 100644 --- a/msgpack/tests/examples.cpp +++ b/msgpack/tests/examples.cpp @@ -7,32 +7,45 @@ #include "msgpack/msgpack.hpp" struct Example { - std::map map; + bool compact{}; + bool schema{}; template void pack(T &pack) { - pack(map); + pack(compact, schema); } }; TEST_CASE("Website example") { - Example example{{{"compact", true}, {"schema", false}}}; + Example example{true, false}; auto data = msgpack::pack(example); - REQUIRE(data.size() == 18); - REQUIRE(data == std::vector{0x82, 0xa7, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xc3, 0xa6, 0x73, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0xc2}); + REQUIRE(data.size() == 2); + REQUIRE(data == std::vector{0xc3, 0xc2}); - REQUIRE(example.map == msgpack::unpack(data).map); + REQUIRE(example.compact == msgpack::unpack(data).compact); + REQUIRE(example.schema == msgpack::unpack(data).schema); +} + +TEST_CASE("Name/value pair style packing") { + Example example{true, false}; + auto data = msgpack::nvp_pack(example); + + REQUIRE(data.size() == 7); + REQUIRE(data == std::vector{0x82, 0xa1, 0x30, 0xc3, 0xa1, 0x31, 0xc2}); + + REQUIRE(example.compact == msgpack::nvp_unpack(data).compact); + REQUIRE(example.schema == msgpack::nvp_unpack(data).schema); } TEST_CASE("Unpack with error handling") { - Example example{{{"compact", true}, {"schema", false}}}; auto data = std::vector{0x82, 0xa7, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xc3, 0xa6, 0x73, 0x63}; std::error_code ec{}; auto unpacked_object = msgpack::unpack(data, ec); - if (ec && ec == msgpack::UnpackerError::OutOfRange) + if (ec && ec == msgpack::UnpackerError::BadInput) REQUIRE(true); else REQUIRE(false); + REQUIRE(!unpacked_object.compact); + REQUIRE(!unpacked_object.schema); } \ No newline at end of file diff --git a/msgpack/tests/nvp_packing_tests.cpp b/msgpack/tests/nvp_packing_tests.cpp new file mode 100644 index 0000000..154d569 --- /dev/null +++ b/msgpack/tests/nvp_packing_tests.cpp @@ -0,0 +1,65 @@ +// +// Created by Mike Loomis on 8/21/2019. +// + +#include + +#include "msgpack/msgpack.hpp" + +#include + +struct ManyTypes { + int a = 1; + bool c = true; + std::nullptr_t d; + std::string e{"Foo bar"}; + std::array f{{"Foo", "Bar"}}; + + template + void pack(T &pack) { + pack(a, c, d, e, f); + } +}; + +struct ObjectWithNestedType { + ManyTypes a{}; + + template + void pack(T &pack) { + pack(a); + } +}; + +TEST_CASE("Complex nvp test") { + ManyTypes example{}; + auto data = msgpack::nvp_pack(example); + + REQUIRE(data == std::vector{0x85, 0xA1, 0x30, 0x01, 0xA1, 0x31, 0xC3, 0xA1, 0x32, 0xC0, 0xA1, 0x33, 0xA7, + 0x46, 0x6F, 0x6F, 0x20, 0x62, 0x61, 0x72, 0xA1, 0x34, 0x92, 0xA3, 0x46, 0x6F, + 0x6F, 0xA3, 0x42, 0x61, 0x72}); +} + +TEST_CASE("Can unpack javascript objects") { + auto imported_js_msgpack = + std::vector{0x86, 0xA3, 0x69, 0x6E, 0x74, 0x01, 0xA7, 0x62, 0x6F, 0x6F, 0x6C, 0x65, 0x61, 0x6E, 0xC3, + 0xA4, 0x6E, 0x75, 0x6C, 0x6C, 0xC0, 0xA6, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0xA7, 0x66, + 0x6F, 0x6F, 0x20, 0x62, 0x61, 0x72, 0xA5, 0x61, 0x72, 0x72, 0x61, 0x79, 0x92, 0xA3, 0x66, + 0x6F, 0x6F, 0xA3, 0x62, 0x61, 0x72, 0xA6, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x82, 0xA3, + 0x66, 0x6F, 0x6F, 0x01, 0xA3, 0x62, 0x61, 0x7A, 0xCB, 0x3F, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00}; + + REQUIRE(msgpack::nvp_unpack(imported_js_msgpack).a == 1); + REQUIRE(msgpack::nvp_unpack(imported_js_msgpack).c); + REQUIRE(msgpack::nvp_unpack(imported_js_msgpack).d == std::nullptr_t{}); + REQUIRE(msgpack::nvp_unpack(imported_js_msgpack).e == "foo bar"); + REQUIRE(msgpack::nvp_unpack(imported_js_msgpack).f == std::array{{"foo", "bar"}}); +} + +TEST_CASE("Nested types can also be packed with NVP style") { + ObjectWithNestedType test{}; + auto data = msgpack::nvp_pack(test); + + REQUIRE(data == std::vector{0x81, 0xA1, 0x30, 0x85, 0xA1, 0x30, 0x01, 0xA1, 0x31, 0xC3, 0xA1, 0x32, 0xC0, + 0xA1, 0x33, 0xA7, 0x46, 0x6F, 0x6F, 0x20, 0x62, 0x61, 0x72, 0xA1, 0x34, 0x92, + 0xA3, 0x46, 0x6F, 0x6F, 0xA3, 0x42, 0x61, 0x72}); +} \ No newline at end of file