diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index d834a0d56..6bb46fbdd 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -13,7 +13,9 @@ #include // tuple #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval - +#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L + #include // byte +#endif #include #include #include @@ -239,6 +241,30 @@ struct char_traits : std::char_traits } }; +#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L +template<> +struct char_traits : std::char_traits +{ + using char_type = std::byte; + using int_type = uint64_t; + + static int_type to_int_type(char_type c) noexcept + { + return static_cast(std::to_integer(c)); + } + + static char_type to_char_type(int_type i) noexcept + { + return std::byte(static_cast(i)); + } + + static constexpr int_type eof() noexcept + { + return static_cast(std::char_traits::eof()); + } +}; +#endif + /////////////////// // is_ functions // /////////////////// diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 374b54ecb..dabad062d 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3367,7 +3367,9 @@ NLOHMANN_JSON_NAMESPACE_END #include // tuple #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval - +#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L + #include // byte +#endif // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ @@ -3776,6 +3778,30 @@ struct char_traits : std::char_traits } }; +#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L +template<> +struct char_traits : std::char_traits +{ + using char_type = std::byte; + using int_type = uint64_t; + + static int_type to_int_type(char_type c) noexcept + { + return static_cast(std::to_integer(c)); + } + + static char_type to_char_type(int_type i) noexcept + { + return std::byte(static_cast(i)); + } + + static constexpr int_type eof() noexcept + { + return static_cast(std::char_traits::eof()); + } +}; +#endif + /////////////////// // is_ functions // /////////////////// diff --git a/tests/src/unit-msgpack.cpp b/tests/src/unit-msgpack.cpp index 0d49ae6db..bfea2dce1 100644 --- a/tests/src/unit-msgpack.cpp +++ b/tests/src/unit-msgpack.cpp @@ -1880,3 +1880,80 @@ TEST_CASE("MessagePack roundtrips" * doctest::skip()) } } } + +#ifdef JSON_HAS_CPP_17 +// Test suite for verifying MessagePack handling with std::byte input +TEST_CASE("MessagePack with std::byte") +{ + + SECTION("std::byte compatibility") + { + SECTION("vector roundtrip") + { + json original = + { + {"name", "test"}, + {"value", 42}, + {"array", {1, 2, 3}} + }; + + std::vector temp = json::to_msgpack(original); + // Convert the uint8_t vector to std::byte vector + std::vector msgpack_data(temp.size()); + for (size_t i = 0; i < temp.size(); ++i) + { + msgpack_data[i] = std::byte(temp[i]); + } + // Deserialize from std::byte vector back to JSON + json from_bytes; + CHECK_NOTHROW(from_bytes = json::from_msgpack(msgpack_data)); + + CHECK(from_bytes == original); + } + + SECTION("empty vector") + { + const std::vector empty_data; + CHECK_THROWS_WITH_AS([&]() { + [[maybe_unused]] auto result = json::from_msgpack(empty_data); + return true; + }(), + "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing MessagePack value: unexpected end of input", + json::parse_error&); + } + + SECTION("comparison with workaround") + { + json original = + { + {"string", "hello"}, + {"integer", 42}, + {"float", 3.14}, + {"boolean", true}, + {"null", nullptr}, + {"array", {1, 2, 3}}, + {"object", {{"key", "value"}}} + }; + + std::vector temp = json::to_msgpack(original); + + std::vector msgpack_data(temp.size()); + for (size_t i = 0; i < temp.size(); ++i) + { + msgpack_data[i] = std::byte(temp[i]); + } + // Attempt direct deserialization using std::byte input + json direct_result = json::from_msgpack(msgpack_data); + + // Test the workaround approach: reinterpret as unsigned char* and use iterator range + const auto *const char_start = reinterpret_cast(msgpack_data.data()); + const auto *const char_end = char_start + msgpack_data.size(); + json workaround_result = json::from_msgpack(char_start, char_end); + + // Verify that the final deserialized JSON matches the original JSON + CHECK(direct_result == workaround_result); + CHECK(direct_result == original); + } + } +} +#endif