From daf2d296ddf3b6574abd682fe61a0af19a7a7c63 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 15 May 2020 14:12:32 +0200 Subject: [PATCH 01/12] :recycle: move wrapped binary type to separate file --- .../nlohmann/detail/conversions/from_json.hpp | 13 +- .../nlohmann/detail/conversions/to_json.hpp | 14 +- include/nlohmann/detail/input/json_sax.hpp | 4 - include/nlohmann/detail/wrapped_binary_t.hpp | 52 +++++++ include/nlohmann/json.hpp | 62 +------- single_include/nlohmann/json.hpp | 146 ++++++++++-------- test/src/unit-constructor1.cpp | 17 ++ test/src/unit-pointer_access.cpp | 38 ++--- 8 files changed, 192 insertions(+), 154 deletions(-) create mode 100644 include/nlohmann/detail/wrapped_binary_t.hpp diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index c389dca7..53eb40e3 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -228,9 +228,9 @@ template ::value and not is_constructible_object_type::value and not is_constructible_string_type::value and + not std::is_same::value and not is_basic_json::value, int > = 0 > - auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) -> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), j.template get(), @@ -245,6 +245,17 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } +template +void from_json(const BasicJsonType& j, typename BasicJsonType::internal_binary_t& bin) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_binary())) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); + } + + bin = *j.template get_ptr(); +} + template::value, int> = 0> void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp index 10b21d16..0a087c22 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -71,7 +71,7 @@ template<> struct external_constructor { template - static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) + static void construct(BasicJsonType& j, const typename BasicJsonType::internal_binary_t& b) { j.m_type = value_t::binary; typename BasicJsonType::internal_binary_t value{b}; @@ -80,7 +80,7 @@ struct external_constructor } template - static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) + static void construct(BasicJsonType& j, typename BasicJsonType::internal_binary_t&& b) { j.m_type = value_t::binary; typename BasicJsonType::internal_binary_t value{std::move(b)}; @@ -278,9 +278,9 @@ void to_json(BasicJsonType& j, const std::vector& e) template ::value and - not is_compatible_object_type< - BasicJsonType, CompatibleArrayType>::value and + not is_compatible_object_type::value and not is_compatible_string_type::value and + not std::is_same::value and not is_basic_json::value, int> = 0> void to_json(BasicJsonType& j, const CompatibleArrayType& arr) @@ -288,6 +288,12 @@ void to_json(BasicJsonType& j, const CompatibleArrayType& arr) external_constructor::construct(j, arr); } +template +void to_json(BasicJsonType& j, const typename BasicJsonType::internal_binary_t& bin) +{ + external_constructor::construct(j, bin); +} + template::value, int> = 0> void to_json(BasicJsonType& j, const std::valarray& arr) diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index c54b80df..ac41e6bb 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -23,13 +23,9 @@ input. template struct json_sax { - /// type for (signed) integers using number_integer_t = typename BasicJsonType::number_integer_t; - /// type for unsigned integers using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - /// type for floating-point numbers using number_float_t = typename BasicJsonType::number_float_t; - /// type for strings using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; diff --git a/include/nlohmann/detail/wrapped_binary_t.hpp b/include/nlohmann/detail/wrapped_binary_t.hpp new file mode 100644 index 00000000..f0ccba27 --- /dev/null +++ b/include/nlohmann/detail/wrapped_binary_t.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include // uint8_t +#include // move + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief an internal type for a backed binary type + +This type is designed to be `binary_t` but with the subtype implementation +detail. This type exists so that the user does not have to specify a struct +his- or herself with a specific naming scheme in order to override the +binary type. I.e. it's for ease of use. +*/ +template +struct wrapped_binary_t : public BinaryType +{ + using binary_t = BinaryType; + + wrapped_binary_t() noexcept(noexcept(binary_t())) = default; + + wrapped_binary_t(const binary_t& bint) noexcept(noexcept(binary_t(bint))) + : binary_t(bint) + {} + + wrapped_binary_t(binary_t&& bint) noexcept(noexcept(binary_t(std::move(bint)))) + : binary_t(std::move(bint)) + {} + + wrapped_binary_t(const binary_t& bint, + std::uint8_t st) noexcept(noexcept(binary_t(bint))) + : binary_t(bint) + , subtype(st) + , has_subtype(true) + {} + + wrapped_binary_t(binary_t&& bint, std::uint8_t st) noexcept(noexcept(binary_t(std::move(bint)))) + : binary_t(std::move(bint)) + , subtype(st) + , has_subtype(true) + {} + + std::uint8_t subtype = 0; + bool has_subtype = false; +}; + +} // namespace detail +} // namespace nlohmann diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index b72ea58d..e9650165 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -70,6 +70,7 @@ SOFTWARE. #include #include #include +#include #include /*! @@ -839,7 +840,7 @@ class basic_json This type is a type designed to carry binary data that appears in various serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and BSON's generic binary subtype. This type is NOT a part of standard JSON and - exists solely for compatibility with these binary types. As such, it is + exists solely for compatibility with these binary types. As such, it is simply defined as an ordered sequence of zero or more byte values. Additionally, as an implementation detail, the subtype of the binary data is @@ -850,9 +851,8 @@ class basic_json [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type as: - > Major type 2: a byte string. The string's length in bytes is - > represented following the rules for positive integers (major type - > 0). + > Major type 2: a byte string. The string's length in bytes is represented + > following the rules for positive integers (major type 0). [MessagePack's documentation on the bin type family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family) @@ -868,7 +868,7 @@ class basic_json None of these impose any limitations on the internal representation other than the basic unit of storage be some type of array whose parts are - decomposible into bytes. + decomposable into bytes. The default representation of this binary format is a `std::vector`, which is a very common way to represent a byte @@ -880,7 +880,7 @@ class basic_json #### Storage - Binary Arrays are stored as pointers in a @ref basic_json type. That is, + Binary Arrays are stored as pointers in a @ref basic_json type. That is, for any access to array values, a pointer of the type `binary_t*` must be dereferenced. @@ -890,43 +890,7 @@ class basic_json */ using binary_t = BinaryType; - /*! - @brief an internal type for a backed binary type - - This type is designed to be `binary_t` but with the subtype implementation - detail. This type exists so that the user does not have to specify a struct - his- or herself with a specific naming scheme in order to override the - binary type. I.e. it's for ease of use. - */ - struct internal_binary_t : public BinaryType - { - using BinaryType::BinaryType; - internal_binary_t() noexcept(noexcept(BinaryType())) - : BinaryType() - {} - internal_binary_t(const BinaryType& bint) noexcept(noexcept(BinaryType(bint))) - : BinaryType(bint) - {} - internal_binary_t(BinaryType&& bint) noexcept(noexcept(BinaryType(std::move(bint)))) - : BinaryType(std::move(bint)) - {} - internal_binary_t(const BinaryType& bint, std::uint8_t st) noexcept(noexcept(BinaryType(bint))) - : BinaryType(bint) - , subtype(st) - , has_subtype(true) - {} - internal_binary_t(BinaryType&& bint, std::uint8_t st) noexcept(noexcept(BinaryType(std::move(bint)))) - : BinaryType(std::move(bint)) - , subtype(st) - , has_subtype(true) - {} - - // TOOD: If minimum C++ version is ever bumped to C++17, this field - // deserves to be a std::optional - std::uint8_t subtype = 0; - bool has_subtype = false; - }; - + using internal_binary_t = nlohmann::detail::wrapped_binary_t; /// @} private: @@ -2802,18 +2766,6 @@ class basic_json return is_number_float() ? &m_value.number_float : nullptr; } - /// get a pointer to the value (binary) - binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept - { - return is_binary() ? reinterpret_cast(m_value.binary) : nullptr; - } - - /// get a pointer to the value (binary) - constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept - { - return is_binary() ? m_value.binary : nullptr; - } - /// get a pointer to the value (binary) internal_binary_t* get_impl_ptr(internal_binary_t* /*unused*/) noexcept { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index c21a4308..0727b005 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3435,9 +3435,9 @@ template ::value and not is_constructible_object_type::value and not is_constructible_string_type::value and + not std::is_same::value and not is_basic_json::value, int > = 0 > - auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) -> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), j.template get(), @@ -3452,6 +3452,17 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } +template +void from_json(const BasicJsonType& j, typename BasicJsonType::internal_binary_t& bin) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_binary())) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); + } + + bin = *j.template get_ptr(); +} + template::value, int> = 0> void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) @@ -3851,7 +3862,7 @@ template<> struct external_constructor { template - static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) + static void construct(BasicJsonType& j, const typename BasicJsonType::internal_binary_t& b) { j.m_type = value_t::binary; typename BasicJsonType::internal_binary_t value{b}; @@ -3860,7 +3871,7 @@ struct external_constructor } template - static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) + static void construct(BasicJsonType& j, typename BasicJsonType::internal_binary_t&& b) { j.m_type = value_t::binary; typename BasicJsonType::internal_binary_t value{std::move(b)}; @@ -4058,9 +4069,9 @@ void to_json(BasicJsonType& j, const std::vector& e) template ::value and - not is_compatible_object_type< - BasicJsonType, CompatibleArrayType>::value and + not is_compatible_object_type::value and not is_compatible_string_type::value and + not std::is_same::value and not is_basic_json::value, int> = 0> void to_json(BasicJsonType& j, const CompatibleArrayType& arr) @@ -4068,6 +4079,12 @@ void to_json(BasicJsonType& j, const CompatibleArrayType& arr) external_constructor::construct(j, arr); } +template +void to_json(BasicJsonType& j, const typename BasicJsonType::internal_binary_t& bin) +{ + external_constructor::construct(j, bin); +} + template::value, int> = 0> void to_json(BasicJsonType& j, const std::valarray& arr) @@ -4727,13 +4744,9 @@ input. template struct json_sax { - /// type for (signed) integers using number_integer_t = typename BasicJsonType::number_integer_t; - /// type for unsigned integers using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - /// type for floating-point numbers using number_float_t = typename BasicJsonType::number_float_t; - /// type for strings using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; @@ -15560,6 +15573,60 @@ class serializer // #include +// #include + + +#include // uint8_t +#include // move + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief an internal type for a backed binary type + +This type is designed to be `binary_t` but with the subtype implementation +detail. This type exists so that the user does not have to specify a struct +his- or herself with a specific naming scheme in order to override the +binary type. I.e. it's for ease of use. +*/ +template +struct wrapped_binary_t : public BinaryType +{ + using binary_t = BinaryType; + + wrapped_binary_t() noexcept(noexcept(binary_t())) = default; + + wrapped_binary_t(const binary_t& bint) noexcept(noexcept(binary_t(bint))) + : binary_t(bint) + {} + + wrapped_binary_t(binary_t&& bint) noexcept(noexcept(binary_t(std::move(bint)))) + : binary_t(std::move(bint)) + {} + + wrapped_binary_t(const binary_t& bint, + std::uint8_t st) noexcept(noexcept(binary_t(bint))) + : binary_t(bint) + , subtype(st) + , has_subtype(true) + {} + + wrapped_binary_t(binary_t&& bint, std::uint8_t st) noexcept(noexcept(binary_t(std::move(bint)))) + : binary_t(std::move(bint)) + , subtype(st) + , has_subtype(true) + {} + + std::uint8_t subtype = 0; + bool has_subtype = false; +}; + +} // namespace detail +} // namespace nlohmann + // #include @@ -16330,7 +16397,7 @@ class basic_json This type is a type designed to carry binary data that appears in various serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and BSON's generic binary subtype. This type is NOT a part of standard JSON and - exists solely for compatibility with these binary types. As such, it is + exists solely for compatibility with these binary types. As such, it is simply defined as an ordered sequence of zero or more byte values. Additionally, as an implementation detail, the subtype of the binary data is @@ -16341,9 +16408,8 @@ class basic_json [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type as: - > Major type 2: a byte string. The string's length in bytes is - > represented following the rules for positive integers (major type - > 0). + > Major type 2: a byte string. The string's length in bytes is represented + > following the rules for positive integers (major type 0). [MessagePack's documentation on the bin type family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family) @@ -16359,7 +16425,7 @@ class basic_json None of these impose any limitations on the internal representation other than the basic unit of storage be some type of array whose parts are - decomposible into bytes. + decomposable into bytes. The default representation of this binary format is a `std::vector`, which is a very common way to represent a byte @@ -16371,7 +16437,7 @@ class basic_json #### Storage - Binary Arrays are stored as pointers in a @ref basic_json type. That is, + Binary Arrays are stored as pointers in a @ref basic_json type. That is, for any access to array values, a pointer of the type `binary_t*` must be dereferenced. @@ -16381,43 +16447,7 @@ class basic_json */ using binary_t = BinaryType; - /*! - @brief an internal type for a backed binary type - - This type is designed to be `binary_t` but with the subtype implementation - detail. This type exists so that the user does not have to specify a struct - his- or herself with a specific naming scheme in order to override the - binary type. I.e. it's for ease of use. - */ - struct internal_binary_t : public BinaryType - { - using BinaryType::BinaryType; - internal_binary_t() noexcept(noexcept(BinaryType())) - : BinaryType() - {} - internal_binary_t(const BinaryType& bint) noexcept(noexcept(BinaryType(bint))) - : BinaryType(bint) - {} - internal_binary_t(BinaryType&& bint) noexcept(noexcept(BinaryType(std::move(bint)))) - : BinaryType(std::move(bint)) - {} - internal_binary_t(const BinaryType& bint, std::uint8_t st) noexcept(noexcept(BinaryType(bint))) - : BinaryType(bint) - , subtype(st) - , has_subtype(true) - {} - internal_binary_t(BinaryType&& bint, std::uint8_t st) noexcept(noexcept(BinaryType(std::move(bint)))) - : BinaryType(std::move(bint)) - , subtype(st) - , has_subtype(true) - {} - - // TOOD: If minimum C++ version is ever bumped to C++17, this field - // deserves to be a std::optional - std::uint8_t subtype = 0; - bool has_subtype = false; - }; - + using internal_binary_t = nlohmann::detail::wrapped_binary_t; /// @} private: @@ -18293,18 +18323,6 @@ class basic_json return is_number_float() ? &m_value.number_float : nullptr; } - /// get a pointer to the value (binary) - binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept - { - return is_binary() ? reinterpret_cast(m_value.binary) : nullptr; - } - - /// get a pointer to the value (binary) - constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept - { - return is_binary() ? m_value.binary : nullptr; - } - /// get a pointer to the value (binary) internal_binary_t* get_impl_ptr(internal_binary_t* /*unused*/) noexcept { diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 96a0be0f..742bdcd9 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -481,6 +481,23 @@ TEST_CASE("constructors") } } + SECTION("create a binary (explicit)") + { + SECTION("empty binary") + { + json::internal_binary_t b{}; + json j(b); + CHECK(j.type() == json::value_t::binary); + } + + SECTION("filled binary") + { + json::internal_binary_t b({1, 2, 3}); + json j(b); + CHECK(j.type() == json::value_t::binary); + } + } + SECTION("create an integer number (explicit)") { SECTION("uninitialized value") diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp index ed245dc6..5f3f9e31 100644 --- a/test/src/unit-pointer_access.cpp +++ b/test/src/unit-pointer_access.cpp @@ -60,7 +60,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -90,7 +89,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -120,7 +118,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -150,7 +147,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -180,7 +176,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -210,7 +205,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -240,7 +234,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -270,8 +263,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -301,7 +292,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -331,7 +321,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -361,7 +350,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -391,7 +379,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -421,7 +408,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); } @@ -451,26 +437,26 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } - SECTION("pointer access to const binary_t") + SECTION("pointer access to const internal_binary_t") { - using test_type = const json::binary_t; - const json value = json::binary_array({}); + using test_type = const json::internal_binary_t; + const json value = json::binary_array({1, 2, 3}); // check if pointers are returned correctly test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); - //CHECK(*p1 == value.get()); + CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); CHECK(p2 == value.get_ptr()); - //CHECK(*p2 == value.get()); + CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); CHECK(p3 == value.get_ptr()); - //CHECK(*p3 == value.get()); + CHECK(*p3 == value.get()); // check if null pointers are returned correctly CHECK(value.get_ptr() == nullptr); @@ -480,10 +466,10 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() != nullptr); } - SECTION("pointer access to const binary_t") + SECTION("pointer access to const internal_binary_t") { using test_type = const json::internal_binary_t; const json value = json::binary_array({}); @@ -491,15 +477,15 @@ TEST_CASE("pointer access") // check if pointers are returned correctly test_type* p1 = value.get_ptr(); CHECK(p1 == value.get_ptr()); - //CHECK(*p1 == value.get()); + CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); CHECK(p2 == value.get_ptr()); - //CHECK(*p2 == value.get()); + CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); CHECK(p3 == value.get_ptr()); - //CHECK(*p3 == value.get()); + CHECK(*p3 == value.get()); // check if null pointers are returned correctly CHECK(value.get_ptr() == nullptr); From a452e8a0a18c4269bc15148c7371469ea504b356 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 15 May 2020 22:38:18 +0200 Subject: [PATCH 02/12] :green_heart: fix GCC 4.9 compilation --- include/nlohmann/detail/wrapped_binary_t.hpp | 4 +++- single_include/nlohmann/json.hpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/wrapped_binary_t.hpp b/include/nlohmann/detail/wrapped_binary_t.hpp index f0ccba27..1ec6a98a 100644 --- a/include/nlohmann/detail/wrapped_binary_t.hpp +++ b/include/nlohmann/detail/wrapped_binary_t.hpp @@ -21,7 +21,9 @@ struct wrapped_binary_t : public BinaryType { using binary_t = BinaryType; - wrapped_binary_t() noexcept(noexcept(binary_t())) = default; + wrapped_binary_t() noexcept(noexcept(binary_t())) + : binary_t() + {} wrapped_binary_t(const binary_t& bint) noexcept(noexcept(binary_t(bint))) : binary_t(bint) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 0727b005..2d12e895 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -15597,7 +15597,9 @@ struct wrapped_binary_t : public BinaryType { using binary_t = BinaryType; - wrapped_binary_t() noexcept(noexcept(binary_t())) = default; + wrapped_binary_t() noexcept(noexcept(binary_t())) + : binary_t() + {} wrapped_binary_t(const binary_t& bint) noexcept(noexcept(binary_t(bint))) : binary_t(bint) From bc1886fb6022097dcf771d63e169276afb16be3e Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 15 May 2020 23:21:49 +0200 Subject: [PATCH 03/12] :recycle: refine interface of wrapped_binary_t --- .../nlohmann/detail/input/binary_reader.hpp | 36 ++--- .../nlohmann/detail/output/binary_writer.hpp | 13 +- include/nlohmann/detail/wrapped_binary_t.hpp | 57 +++++++- include/nlohmann/json.hpp | 30 ++-- single_include/nlohmann/json.hpp | 136 ++++++++++++------ 5 files changed, 186 insertions(+), 86 deletions(-) diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index 5f497089..8955ee7a 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -227,8 +227,8 @@ class binary_reader return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); } - result.has_subtype = true; // All BSON binary values have a subtype - get_number(input_format_t::bson, result.subtype); + result.m_has_subtype = true; // All BSON binary values have a subtype + get_number(input_format_t::bson, result.m_subtype); return get_binary(input_format_t::bson, len, result); } @@ -1524,58 +1524,58 @@ class binary_reader case 0xC7: // ext 8 { std::uint8_t len; - result.has_subtype = true; + result.m_has_subtype = true; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.subtype) and + get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, len, result); } case 0xC8: // ext 16 { std::uint16_t len; - result.has_subtype = true; + result.m_has_subtype = true; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.subtype) and + get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, len, result); } case 0xC9: // ext 32 { std::uint32_t len; - result.has_subtype = true; + result.m_has_subtype = true; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.subtype) and + get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, len, result); } case 0xD4: // fixext 1 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 1, result); + result.m_has_subtype = true; + return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 1, result); } case 0xD5: // fixext 2 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 2, result); + result.m_has_subtype = true; + return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 2, result); } case 0xD6: // fixext 4 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 4, result); + result.m_has_subtype = true; + return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 4, result); } case 0xD7: // fixext 8 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 8, result); + result.m_has_subtype = true; + return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 8, result); } case 0xD8: // fixext 16 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 16, result); + result.m_has_subtype = true; + return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 16, result); } default: // LCOV_EXCL_LINE diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 7d4a4031..39935d10 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -550,7 +550,7 @@ class binary_writer { // step 0: determine if the binary type has a set subtype to // determine whether or not to use the ext or fixext types - const bool use_ext = j.m_value.binary->has_subtype; + const bool use_ext = j.m_value.binary->has_subtype(); // step 1: write control byte and the byte string length const auto N = j.m_value.binary->size(); @@ -630,7 +630,9 @@ class binary_writer // step 1.5: if this is an ext type, write the subtype if (use_ext) { - write_number(j.m_value.binary->subtype); + std::uint8_t subtype; + write_number(subtype); + j.m_value.binary->set_subtype(subtype); } // step 2: write the byte string @@ -1085,12 +1087,7 @@ class binary_writer write_bson_entry_header(name, 0x05); write_number(static_cast(value.size())); - std::uint8_t subtype = 0x00; // Generic Binary Subtype - if (value.has_subtype) - { - subtype = value.subtype; - } - write_number(subtype); + write_number(value.has_subtype() ? value.subtype() : 0x00); oa->write_characters(reinterpret_cast(value.data()), value.size()); } diff --git a/include/nlohmann/detail/wrapped_binary_t.hpp b/include/nlohmann/detail/wrapped_binary_t.hpp index 1ec6a98a..8514f049 100644 --- a/include/nlohmann/detail/wrapped_binary_t.hpp +++ b/include/nlohmann/detail/wrapped_binary_t.hpp @@ -17,8 +17,13 @@ his- or herself with a specific naming scheme in order to override the binary type. I.e. it's for ease of use. */ template -struct wrapped_binary_t : public BinaryType +class wrapped_binary_t : public BinaryType { + public: + // to simplify code in binary_reader + template + friend class binary_reader; + using binary_t = BinaryType; wrapped_binary_t() noexcept(noexcept(binary_t())) @@ -36,18 +41,56 @@ struct wrapped_binary_t : public BinaryType wrapped_binary_t(const binary_t& bint, std::uint8_t st) noexcept(noexcept(binary_t(bint))) : binary_t(bint) - , subtype(st) - , has_subtype(true) + , m_subtype(st) + , m_has_subtype(true) {} wrapped_binary_t(binary_t&& bint, std::uint8_t st) noexcept(noexcept(binary_t(std::move(bint)))) : binary_t(std::move(bint)) - , subtype(st) - , has_subtype(true) + , m_subtype(st) + , m_has_subtype(true) {} - std::uint8_t subtype = 0; - bool has_subtype = false; + /*! + @brief set the subtype + @param subtype subtype to set (implementation specific) + */ + void set_subtype(std::uint8_t subtype) noexcept + { + m_subtype = subtype; + m_has_subtype = true; + } + + /*! + @brief get the subtype + @return subtype (implementation specific) + */ + constexpr std::uint8_t subtype() const noexcept + { + return m_subtype; + } + + /*! + @brief get whether a subtype was set + @return whether a subtype was set + */ + constexpr bool has_subtype() const noexcept + { + return m_has_subtype; + } + + /*! + @brief clear the subtype + */ + void clear_subtype() noexcept + { + m_subtype = 0; + m_has_subtype = false; + } + + private: + std::uint8_t m_subtype = 0; + bool m_has_subtype = false; }; } // namespace detail diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index e9650165..21becba4 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -839,15 +839,15 @@ class basic_json This type is a type designed to carry binary data that appears in various serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and - BSON's generic binary subtype. This type is NOT a part of standard JSON and + BSON's generic binary subtype. This type is NOT a part of standard JSON and exists solely for compatibility with these binary types. As such, it is simply defined as an ordered sequence of zero or more byte values. Additionally, as an implementation detail, the subtype of the binary data is - carried around as a `unint8_t`, which is compatible with both of the binary - data formats that use binary subtyping, (though the specific numbering is - incompatible with each other, and it is up to the user to translate between - them). + carried around as a `std::uint8_t`, which is compatible with both of the + binary data formats that use binary subtyping, (though the specific + numbering is incompatible with each other, and it is up to the user to + translate between them). [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type as: @@ -890,6 +890,18 @@ class basic_json */ using binary_t = BinaryType; + /*! + @brief binary array with a binary type + + This type is used to store binary types internally. It wrapps the template + type `BinaryType` (@ref binary_t) and adds a subtype to allow to distinguish + different binary types from different formats. + + While @ref binary_t is used to define how binary values are stored, this + type is used to access binary values once they are parsed. + + @sa @ref binary_array -- create a binary array + */ using internal_binary_t = nlohmann::detail::wrapped_binary_t; /// @} @@ -3882,8 +3894,7 @@ class basic_json { if (is_binary()) { - m_value.binary->has_subtype = true; - m_value.binary->subtype = subtype; + m_value.binary->set_subtype(subtype); } } @@ -3911,8 +3922,7 @@ class basic_json { if (is_binary()) { - m_value.binary->has_subtype = false; - m_value.binary->subtype = 0; + m_value.binary->clear_subtype(); } } @@ -3936,7 +3946,7 @@ class basic_json */ bool has_subtype() const noexcept { - return is_binary() and m_value.binary->has_subtype; + return is_binary() and m_value.binary->has_subtype(); } /*! diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 2d12e895..8b01d241 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -5804,8 +5804,8 @@ class binary_reader return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); } - result.has_subtype = true; // All BSON binary values have a subtype - get_number(input_format_t::bson, result.subtype); + result.m_has_subtype = true; // All BSON binary values have a subtype + get_number(input_format_t::bson, result.m_subtype); return get_binary(input_format_t::bson, len, result); } @@ -7101,58 +7101,58 @@ class binary_reader case 0xC7: // ext 8 { std::uint8_t len; - result.has_subtype = true; + result.m_has_subtype = true; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.subtype) and + get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, len, result); } case 0xC8: // ext 16 { std::uint16_t len; - result.has_subtype = true; + result.m_has_subtype = true; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.subtype) and + get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, len, result); } case 0xC9: // ext 32 { std::uint32_t len; - result.has_subtype = true; + result.m_has_subtype = true; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.subtype) and + get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, len, result); } case 0xD4: // fixext 1 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 1, result); + result.m_has_subtype = true; + return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 1, result); } case 0xD5: // fixext 2 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 2, result); + result.m_has_subtype = true; + return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 2, result); } case 0xD6: // fixext 4 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 4, result); + result.m_has_subtype = true; + return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 4, result); } case 0xD7: // fixext 8 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 8, result); + result.m_has_subtype = true; + return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 8, result); } case 0xD8: // fixext 16 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 16, result); + result.m_has_subtype = true; + return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 16, result); } default: // LCOV_EXCL_LINE @@ -12557,7 +12557,7 @@ class binary_writer { // step 0: determine if the binary type has a set subtype to // determine whether or not to use the ext or fixext types - const bool use_ext = j.m_value.binary->has_subtype; + const bool use_ext = j.m_value.binary->has_subtype(); // step 1: write control byte and the byte string length const auto N = j.m_value.binary->size(); @@ -12637,7 +12637,9 @@ class binary_writer // step 1.5: if this is an ext type, write the subtype if (use_ext) { - write_number(j.m_value.binary->subtype); + std::uint8_t subtype; + write_number(subtype); + j.m_value.binary->set_subtype(subtype); } // step 2: write the byte string @@ -13092,12 +13094,7 @@ class binary_writer write_bson_entry_header(name, 0x05); write_number(static_cast(value.size())); - std::uint8_t subtype = 0x00; // Generic Binary Subtype - if (value.has_subtype) - { - subtype = value.subtype; - } - write_number(subtype); + write_number(value.has_subtype() ? value.subtype() : 0x00); oa->write_characters(reinterpret_cast(value.data()), value.size()); } @@ -15593,8 +15590,13 @@ his- or herself with a specific naming scheme in order to override the binary type. I.e. it's for ease of use. */ template -struct wrapped_binary_t : public BinaryType +class wrapped_binary_t : public BinaryType { + public: + // to simplify code in binary_reader + template + friend class binary_reader; + using binary_t = BinaryType; wrapped_binary_t() noexcept(noexcept(binary_t())) @@ -15612,18 +15614,56 @@ struct wrapped_binary_t : public BinaryType wrapped_binary_t(const binary_t& bint, std::uint8_t st) noexcept(noexcept(binary_t(bint))) : binary_t(bint) - , subtype(st) - , has_subtype(true) + , m_subtype(st) + , m_has_subtype(true) {} wrapped_binary_t(binary_t&& bint, std::uint8_t st) noexcept(noexcept(binary_t(std::move(bint)))) : binary_t(std::move(bint)) - , subtype(st) - , has_subtype(true) + , m_subtype(st) + , m_has_subtype(true) {} - std::uint8_t subtype = 0; - bool has_subtype = false; + /*! + @brief set the subtype + @param subtype subtype to set (implementation specific) + */ + void set_subtype(std::uint8_t subtype) noexcept + { + m_subtype = subtype; + m_has_subtype = true; + } + + /*! + @brief get the subtype + @return subtype (implementation specific) + */ + constexpr std::uint8_t subtype() const noexcept + { + return m_subtype; + } + + /*! + @brief get whether a subtype was set + @return whether a subtype was set + */ + constexpr bool has_subtype() const noexcept + { + return m_has_subtype; + } + + /*! + @brief clear the subtype + */ + void clear_subtype() noexcept + { + m_subtype = 0; + m_has_subtype = false; + } + + private: + std::uint8_t m_subtype = 0; + bool m_has_subtype = false; }; } // namespace detail @@ -16398,15 +16438,15 @@ class basic_json This type is a type designed to carry binary data that appears in various serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and - BSON's generic binary subtype. This type is NOT a part of standard JSON and + BSON's generic binary subtype. This type is NOT a part of standard JSON and exists solely for compatibility with these binary types. As such, it is simply defined as an ordered sequence of zero or more byte values. Additionally, as an implementation detail, the subtype of the binary data is - carried around as a `unint8_t`, which is compatible with both of the binary - data formats that use binary subtyping, (though the specific numbering is - incompatible with each other, and it is up to the user to translate between - them). + carried around as a `std::uint8_t`, which is compatible with both of the + binary data formats that use binary subtyping, (though the specific + numbering is incompatible with each other, and it is up to the user to + translate between them). [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type as: @@ -16449,6 +16489,18 @@ class basic_json */ using binary_t = BinaryType; + /*! + @brief binary array with a binary type + + This type is used to store binary types internally. It wrapps the template + type `BinaryType` (@ref binary_t) and adds a subtype to allow to distinguish + different binary types from different formats. + + While @ref binary_t is used to define how binary values are stored, this + type is used to access binary values once they are parsed. + + @sa @ref binary_array -- create a binary array + */ using internal_binary_t = nlohmann::detail::wrapped_binary_t; /// @} @@ -19441,8 +19493,7 @@ class basic_json { if (is_binary()) { - m_value.binary->has_subtype = true; - m_value.binary->subtype = subtype; + m_value.binary->set_subtype(subtype); } } @@ -19470,8 +19521,7 @@ class basic_json { if (is_binary()) { - m_value.binary->has_subtype = false; - m_value.binary->subtype = 0; + m_value.binary->clear_subtype(); } } @@ -19495,7 +19545,7 @@ class basic_json */ bool has_subtype() const noexcept { - return is_binary() and m_value.binary->has_subtype; + return is_binary() and m_value.binary->has_subtype(); } /*! From 3ed059f6ffe44ae78a9b80c7323584ecb9b2ed5d Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 16 May 2020 12:56:18 +0200 Subject: [PATCH 04/12] :rewind: revert faulty changes --- include/nlohmann/detail/output/binary_writer.hpp | 6 ++---- single_include/nlohmann/json.hpp | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 39935d10..48364547 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -630,9 +630,7 @@ class binary_writer // step 1.5: if this is an ext type, write the subtype if (use_ext) { - std::uint8_t subtype; - write_number(subtype); - j.m_value.binary->set_subtype(subtype); + write_number(j.m_value.binary->subtype()); } // step 2: write the byte string @@ -1087,7 +1085,7 @@ class binary_writer write_bson_entry_header(name, 0x05); write_number(static_cast(value.size())); - write_number(value.has_subtype() ? value.subtype() : 0x00); + write_number(value.has_subtype() ? value.subtype() : std::uint8_t(0x00)); oa->write_characters(reinterpret_cast(value.data()), value.size()); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 8b01d241..9a650b9d 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -12637,9 +12637,7 @@ class binary_writer // step 1.5: if this is an ext type, write the subtype if (use_ext) { - std::uint8_t subtype; - write_number(subtype); - j.m_value.binary->set_subtype(subtype); + write_number(j.m_value.binary->subtype()); } // step 2: write the byte string @@ -13094,7 +13092,7 @@ class binary_writer write_bson_entry_header(name, 0x05); write_number(static_cast(value.size())); - write_number(value.has_subtype() ? value.subtype() : 0x00); + write_number(value.has_subtype() ? value.subtype() : std::uint8_t(0x00)); oa->write_characters(reinterpret_cast(value.data()), value.size()); } From dead99eb0e2c7b1ca1d097e28dff865215f17744 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 17 May 2020 13:51:59 +0200 Subject: [PATCH 05/12] :hammer: overwork binary subtypes --- .../nlohmann/detail/input/binary_reader.hpp | 85 ++++++---- include/nlohmann/detail/input/json_sax.hpp | 20 +-- .../nlohmann/detail/output/binary_writer.hpp | 2 +- include/nlohmann/detail/wrapped_binary_t.hpp | 31 ++-- include/nlohmann/json.hpp | 15 ++ single_include/nlohmann/json.hpp | 153 +++++++++++------- test/src/unit-class_parser.cpp | 4 +- test/src/unit-deserialization.cpp | 2 +- 8 files changed, 195 insertions(+), 117 deletions(-) diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index e03f95fb..35d7dbd6 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -227,8 +227,10 @@ class binary_reader return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); } - result.m_has_subtype = true; // All BSON binary values have a subtype - get_number(input_format_t::bson, result.m_subtype); + // All BSON binary values have a subtype + std::uint8_t subtype; + get_number(input_format_t::bson, subtype); + result.set_subtype(subtype); return get_binary(input_format_t::bson, len, result); } @@ -901,25 +903,29 @@ class binary_reader case 0x58: // Binary data (one-byte uint8_t for n follows) { std::uint8_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x59: // Binary data (two-byte uint16_t for n follow) { std::uint16_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x5A: // Binary data (four-byte uint32_t for n follow) { std::uint32_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x5B: // Binary data (eight-byte uint64_t for n follow) { std::uint64_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x5F: // Binary data (indefinite length) @@ -1501,81 +1507,104 @@ class binary_reader */ bool get_msgpack_binary(internal_binary_t& result) { + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) + { + result.set_subtype(static_cast(subtype)); + return true; + }; + switch (current) { case 0xC4: // bin 8 { std::uint8_t len; - return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); } case 0xC5: // bin 16 { std::uint16_t len; - return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); } case 0xC6: // bin 32 { std::uint32_t len; - return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); } case 0xC7: // ext 8 { std::uint8_t len; - result.m_has_subtype = true; + std::int8_t subtype; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.m_subtype) and - get_binary(input_format_t::msgpack, len, result); + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); } case 0xC8: // ext 16 { std::uint16_t len; - result.m_has_subtype = true; + std::int8_t subtype; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.m_subtype) and - get_binary(input_format_t::msgpack, len, result); + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); } case 0xC9: // ext 32 { std::uint32_t len; - result.m_has_subtype = true; + std::int8_t subtype; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.m_subtype) and - get_binary(input_format_t::msgpack, len, result); + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); } case 0xD4: // fixext 1 { - result.m_has_subtype = true; - return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 1, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 1, result) and + assign_and_return_true(subtype); } case 0xD5: // fixext 2 { - result.m_has_subtype = true; - return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 2, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 2, result) and + assign_and_return_true(subtype); } case 0xD6: // fixext 4 { - result.m_has_subtype = true; - return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 4, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 4, result) and + assign_and_return_true(subtype); } case 0xD7: // fixext 8 { - result.m_has_subtype = true; - return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 8, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 8, result) and + assign_and_return_true(subtype); } case 0xD8: // fixext 16 { - result.m_has_subtype = true; - return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 16, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 16, result) and + assign_and_return_true(subtype); } default: // LCOV_EXCL_LINE diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index ac41e6bb..54b53fd3 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -27,7 +27,7 @@ struct json_sax using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; + using internal_binary_t = typename BasicJsonType::internal_binary_t; /*! @brief a null value was read @@ -78,7 +78,7 @@ struct json_sax @return whether parsing should proceed @note It is safe to move the passed binary. */ - virtual bool binary(binary_t& val) = 0; + virtual bool binary(internal_binary_t& val) = 0; /*! @brief the beginning of an object was read @@ -154,7 +154,7 @@ class json_sax_dom_parser using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; + using internal_binary_t = typename BasicJsonType::internal_binary_t; /*! @param[in, out] r reference to a JSON value that is manipulated while @@ -208,9 +208,9 @@ class json_sax_dom_parser return true; } - bool binary(binary_t& val) + bool binary(internal_binary_t& val) { - handle_value(BasicJsonType::binary_array(std::move(val))); + handle_value(std::move(val)); return true; } @@ -343,7 +343,7 @@ class json_sax_dom_callback_parser using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; + using internal_binary_t = typename BasicJsonType::internal_binary_t; using parser_callback_t = typename BasicJsonType::parser_callback_t; using parse_event_t = typename BasicJsonType::parse_event_t; @@ -398,9 +398,9 @@ class json_sax_dom_callback_parser return true; } - bool binary(binary_t& val) + bool binary(internal_binary_t& val) { - handle_value(BasicJsonType::binary_array(val)); + handle_value(std::move(val)); return true; } @@ -654,7 +654,7 @@ class json_sax_acceptor using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; + using internal_binary_t = typename BasicJsonType::internal_binary_t; bool null() { @@ -686,7 +686,7 @@ class json_sax_acceptor return true; } - bool binary(binary_t& /*unused*/) + bool binary(internal_binary_t& /*unused*/) { return true; } diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index cd826f09..1feb95dc 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -658,7 +658,7 @@ class binary_writer // step 1.5: if this is an ext type, write the subtype if (use_ext) { - write_number(j.m_value.binary->subtype()); + write_number(static_cast(j.m_value.binary->subtype())); } // step 2: write the byte string diff --git a/include/nlohmann/detail/wrapped_binary_t.hpp b/include/nlohmann/detail/wrapped_binary_t.hpp index 8514f049..230641bd 100644 --- a/include/nlohmann/detail/wrapped_binary_t.hpp +++ b/include/nlohmann/detail/wrapped_binary_t.hpp @@ -12,42 +12,37 @@ namespace detail @brief an internal type for a backed binary type This type is designed to be `binary_t` but with the subtype implementation -detail. This type exists so that the user does not have to specify a struct -his- or herself with a specific naming scheme in order to override the -binary type. I.e. it's for ease of use. +detail. This type exists so that the user does not have to specify a type +themselves with a specific naming scheme in order to override the binary type. */ template class wrapped_binary_t : public BinaryType { public: - // to simplify code in binary_reader - template - friend class binary_reader; - using binary_t = BinaryType; wrapped_binary_t() noexcept(noexcept(binary_t())) : binary_t() {} - wrapped_binary_t(const binary_t& bint) noexcept(noexcept(binary_t(bint))) - : binary_t(bint) + wrapped_binary_t(const binary_t& b) noexcept(noexcept(binary_t(b))) + : binary_t(b) {} - wrapped_binary_t(binary_t&& bint) noexcept(noexcept(binary_t(std::move(bint)))) - : binary_t(std::move(bint)) + wrapped_binary_t(binary_t&& b) noexcept(noexcept(binary_t(std::move(b)))) + : binary_t(std::move(b)) {} - wrapped_binary_t(const binary_t& bint, - std::uint8_t st) noexcept(noexcept(binary_t(bint))) - : binary_t(bint) - , m_subtype(st) + wrapped_binary_t(const binary_t& b, + std::uint8_t subtype) noexcept(noexcept(binary_t(b))) + : binary_t(b) + , m_subtype(subtype) , m_has_subtype(true) {} - wrapped_binary_t(binary_t&& bint, std::uint8_t st) noexcept(noexcept(binary_t(std::move(bint)))) - : binary_t(std::move(bint)) - , m_subtype(st) + wrapped_binary_t(binary_t&& b, std::uint8_t subtype) noexcept(noexcept(binary_t(std::move(b)))) + : binary_t(std::move(b)) + , m_subtype(subtype) , m_has_subtype(true) {} diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 270c2787..2b976d91 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -900,6 +900,21 @@ class basic_json While @ref binary_t is used to define how binary values are stored, this type is used to access binary values once they are parsed. + Notes on subtypes: + + - CBOR + - Binary values are represented as byte strings. No subtypes are + supported and will be ignored when CBOR is written. + - MessagePack + - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, + or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) + is used. For other sizes, the ext family (ext8, ext16, ext32) is used. + The subtype is then added as singed 8-bit integer. + - If no subtype is given, the bin family (bin8, bin16, bin32) is used. + - BSON + - If a subtype is given, it is used and added as unsigned 8-bit integer. + - If no subtype is given, the generic binary subtype 0x00 is used. + @sa @ref binary_array -- create a binary array */ using internal_binary_t = nlohmann::detail::wrapped_binary_t; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 395be7c8..1a8aa14c 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4764,7 +4764,7 @@ struct json_sax using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; + using internal_binary_t = typename BasicJsonType::internal_binary_t; /*! @brief a null value was read @@ -4815,7 +4815,7 @@ struct json_sax @return whether parsing should proceed @note It is safe to move the passed binary. */ - virtual bool binary(binary_t& val) = 0; + virtual bool binary(internal_binary_t& val) = 0; /*! @brief the beginning of an object was read @@ -4891,7 +4891,7 @@ class json_sax_dom_parser using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; + using internal_binary_t = typename BasicJsonType::internal_binary_t; /*! @param[in, out] r reference to a JSON value that is manipulated while @@ -4945,9 +4945,9 @@ class json_sax_dom_parser return true; } - bool binary(binary_t& val) + bool binary(internal_binary_t& val) { - handle_value(BasicJsonType::binary_array(std::move(val))); + handle_value(std::move(val)); return true; } @@ -5080,7 +5080,7 @@ class json_sax_dom_callback_parser using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; + using internal_binary_t = typename BasicJsonType::internal_binary_t; using parser_callback_t = typename BasicJsonType::parser_callback_t; using parse_event_t = typename BasicJsonType::parse_event_t; @@ -5135,9 +5135,9 @@ class json_sax_dom_callback_parser return true; } - bool binary(binary_t& val) + bool binary(internal_binary_t& val) { - handle_value(BasicJsonType::binary_array(val)); + handle_value(std::move(val)); return true; } @@ -5391,7 +5391,7 @@ class json_sax_acceptor using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; + using internal_binary_t = typename BasicJsonType::internal_binary_t; bool null() { @@ -5423,7 +5423,7 @@ class json_sax_acceptor return true; } - bool binary(binary_t& /*unused*/) + bool binary(internal_binary_t& /*unused*/) { return true; } @@ -5820,8 +5820,10 @@ class binary_reader return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); } - result.m_has_subtype = true; // All BSON binary values have a subtype - get_number(input_format_t::bson, result.m_subtype); + // All BSON binary values have a subtype + std::uint8_t subtype; + get_number(input_format_t::bson, subtype); + result.set_subtype(subtype); return get_binary(input_format_t::bson, len, result); } @@ -6494,25 +6496,29 @@ class binary_reader case 0x58: // Binary data (one-byte uint8_t for n follows) { std::uint8_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x59: // Binary data (two-byte uint16_t for n follow) { std::uint16_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x5A: // Binary data (four-byte uint32_t for n follow) { std::uint32_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x5B: // Binary data (eight-byte uint64_t for n follow) { std::uint64_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x5F: // Binary data (indefinite length) @@ -7094,81 +7100,104 @@ class binary_reader */ bool get_msgpack_binary(internal_binary_t& result) { + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) + { + result.set_subtype(static_cast(subtype)); + return true; + }; + switch (current) { case 0xC4: // bin 8 { std::uint8_t len; - return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); } case 0xC5: // bin 16 { std::uint16_t len; - return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); } case 0xC6: // bin 32 { std::uint32_t len; - return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); } case 0xC7: // ext 8 { std::uint8_t len; - result.m_has_subtype = true; + std::int8_t subtype; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.m_subtype) and - get_binary(input_format_t::msgpack, len, result); + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); } case 0xC8: // ext 16 { std::uint16_t len; - result.m_has_subtype = true; + std::int8_t subtype; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.m_subtype) and - get_binary(input_format_t::msgpack, len, result); + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); } case 0xC9: // ext 32 { std::uint32_t len; - result.m_has_subtype = true; + std::int8_t subtype; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.m_subtype) and - get_binary(input_format_t::msgpack, len, result); + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); } case 0xD4: // fixext 1 { - result.m_has_subtype = true; - return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 1, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 1, result) and + assign_and_return_true(subtype); } case 0xD5: // fixext 2 { - result.m_has_subtype = true; - return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 2, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 2, result) and + assign_and_return_true(subtype); } case 0xD6: // fixext 4 { - result.m_has_subtype = true; - return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 4, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 4, result) and + assign_and_return_true(subtype); } case 0xD7: // fixext 8 { - result.m_has_subtype = true; - return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 8, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 8, result) and + assign_and_return_true(subtype); } case 0xD8: // fixext 16 { - result.m_has_subtype = true; - return get_number(input_format_t::msgpack, result.m_subtype) and get_binary(input_format_t::msgpack, 16, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 16, result) and + assign_and_return_true(subtype); } default: // LCOV_EXCL_LINE @@ -12682,7 +12711,7 @@ class binary_writer // step 1.5: if this is an ext type, write the subtype if (use_ext) { - write_number(j.m_value.binary->subtype()); + write_number(static_cast(j.m_value.binary->subtype())); } // step 2: write the byte string @@ -15631,42 +15660,37 @@ namespace detail @brief an internal type for a backed binary type This type is designed to be `binary_t` but with the subtype implementation -detail. This type exists so that the user does not have to specify a struct -his- or herself with a specific naming scheme in order to override the -binary type. I.e. it's for ease of use. +detail. This type exists so that the user does not have to specify a type +themselves with a specific naming scheme in order to override the binary type. */ template class wrapped_binary_t : public BinaryType { public: - // to simplify code in binary_reader - template - friend class binary_reader; - using binary_t = BinaryType; wrapped_binary_t() noexcept(noexcept(binary_t())) : binary_t() {} - wrapped_binary_t(const binary_t& bint) noexcept(noexcept(binary_t(bint))) - : binary_t(bint) + wrapped_binary_t(const binary_t& b) noexcept(noexcept(binary_t(b))) + : binary_t(b) {} - wrapped_binary_t(binary_t&& bint) noexcept(noexcept(binary_t(std::move(bint)))) - : binary_t(std::move(bint)) + wrapped_binary_t(binary_t&& b) noexcept(noexcept(binary_t(std::move(b)))) + : binary_t(std::move(b)) {} - wrapped_binary_t(const binary_t& bint, - std::uint8_t st) noexcept(noexcept(binary_t(bint))) - : binary_t(bint) - , m_subtype(st) + wrapped_binary_t(const binary_t& b, + std::uint8_t subtype) noexcept(noexcept(binary_t(b))) + : binary_t(b) + , m_subtype(subtype) , m_has_subtype(true) {} - wrapped_binary_t(binary_t&& bint, std::uint8_t st) noexcept(noexcept(binary_t(std::move(bint)))) - : binary_t(std::move(bint)) - , m_subtype(st) + wrapped_binary_t(binary_t&& b, std::uint8_t subtype) noexcept(noexcept(binary_t(std::move(b)))) + : binary_t(std::move(b)) + , m_subtype(subtype) , m_has_subtype(true) {} @@ -16545,6 +16569,21 @@ class basic_json While @ref binary_t is used to define how binary values are stored, this type is used to access binary values once they are parsed. + Notes on subtypes: + + - CBOR + - Binary values are represented as byte strings. No subtypes are + supported and will be ignored when CBOR is written. + - MessagePack + - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, + or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) + is used. For other sizes, the ext family (ext8, ext16, ext32) is used. + The subtype is then added as singed 8-bit integer. + - If no subtype is given, the bin family (bin8, bin16, bin32) is used. + - BSON + - If a subtype is given, it is used and added as unsigned 8-bit integer. + - If no subtype is given, the generic binary subtype 0x00 is used. + @sa @ref binary_array -- create a binary array */ using internal_binary_t = nlohmann::detail::wrapped_binary_t; diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 6f6b121e..f8eb6112 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -77,7 +77,7 @@ class SaxEventLogger return true; } - bool binary(std::vector& val) + bool binary(json::internal_binary_t& val) { std::string binary_contents = "binary("; std::string comma_space = ""; @@ -183,7 +183,7 @@ class SaxCountdown : public nlohmann::json::json_sax_t return events_left-- > 0; } - bool binary(std::vector&) override + bool binary(json::internal_binary_t&) override { return events_left-- > 0; } diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index a8688715..52ea9259 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -76,7 +76,7 @@ struct SaxEventLogger : public nlohmann::json_sax return true; } - bool binary(std::vector& val) override + bool binary(json::internal_binary_t& val) override { std::string binary_contents = "binary("; std::string comma_space = ""; From 904642f261f5698c6c2c611567252abec0f17247 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 17 May 2020 22:50:27 +0200 Subject: [PATCH 06/12] :recycle: rename internal_binary_t with binary_t --- README.md | 30 ++ doc/examples/is_array.cpp | 2 + doc/examples/is_array.link | 2 +- doc/examples/is_array.output | 1 + doc/examples/is_binary.cpp | 30 ++ doc/examples/is_binary.link | 1 + doc/examples/is_binary.output | 9 + doc/examples/is_boolean.cpp | 2 + doc/examples/is_boolean.link | 2 +- doc/examples/is_boolean.output | 1 + doc/examples/is_discarded.cpp | 2 + doc/examples/is_discarded.link | 2 +- doc/examples/is_discarded.output | 1 + doc/examples/is_null.cpp | 2 + doc/examples/is_null.link | 2 +- doc/examples/is_null.output | 1 + doc/examples/is_number.cpp | 2 + doc/examples/is_number.link | 2 +- doc/examples/is_number.output | 1 + doc/examples/is_number_float.cpp | 2 + doc/examples/is_number_float.link | 2 +- doc/examples/is_number_float.output | 1 + doc/examples/is_number_integer.cpp | 2 + doc/examples/is_number_integer.link | 2 +- doc/examples/is_number_integer.output | 1 + doc/examples/is_number_unsigned.cpp | 2 + doc/examples/is_number_unsigned.link | 2 +- doc/examples/is_number_unsigned.output | 1 + doc/examples/is_object.cpp | 2 + doc/examples/is_object.link | 2 +- doc/examples/is_object.output | 1 + doc/examples/is_primitive.cpp | 2 + doc/examples/is_primitive.link | 2 +- doc/examples/is_primitive.output | 1 + doc/examples/is_string.cpp | 2 + doc/examples/is_string.link | 2 +- doc/examples/is_string.output | 1 + doc/examples/is_structured.cpp | 2 + doc/examples/is_structured.link | 2 +- doc/examples/is_structured.output | 1 + doc/examples/sax_parse.cpp | 2 +- doc/examples/sax_parse.link | 2 +- doc/index.md | 9 +- .../nlohmann/detail/conversions/from_json.hpp | 6 +- .../nlohmann/detail/conversions/to_json.hpp | 12 +- .../nlohmann/detail/input/binary_reader.hpp | 18 +- include/nlohmann/detail/input/json_sax.hpp | 16 +- .../detail/iterators/internal_iterator.hpp | 2 +- .../nlohmann/detail/output/binary_writer.hpp | 6 +- include/nlohmann/detail/output/serializer.hpp | 4 +- include/nlohmann/detail/wrapped_binary_t.hpp | 98 ++++- include/nlohmann/json.hpp | 224 ++++------ single_include/nlohmann/json.hpp | 386 +++++++++--------- test/src/unit-class_parser.cpp | 4 +- test/src/unit-constructor1.cpp | 4 +- test/src/unit-deserialization.cpp | 2 +- test/src/unit-modifiers.cpp | 22 +- test/src/unit-msgpack.cpp | 6 +- test/src/unit-pointer_access.cpp | 40 +- 59 files changed, 536 insertions(+), 457 deletions(-) create mode 100644 doc/examples/is_binary.cpp create mode 100644 doc/examples/is_binary.link create mode 100644 doc/examples/is_binary.output diff --git a/README.md b/README.md index 612b8a66..d2c3f978 100644 --- a/README.md +++ b/README.md @@ -1047,6 +1047,36 @@ std::vector v_ubjson = json::to_ubjson(j); json j_from_ubjson = json::from_ubjson(v_ubjson); ``` +The library also supports binary types from BSON, CBOR (byte strings), and MessagePack (bin, ext, fixext). They are stored by default as `std::vector` to be processed outside of the library. + +```cpp +// CBOR byte string with payload 0xCAFE +std::vector v = {0x42, 0xCA, 0xFE}; + +// read value +json j = json::from_cbor(v); + +// the JSON value has type binary +j.is_binary(); // true + +// get reference to stored binary value +auto& binary = j.get_binary(); + +// the binary value has no subtype (CBOR has no binary subtypes) +binary.has_subtype(); // false + +// access std::vector member functions +binary.size(); // 2 +binary[0]; // 0xCA +binary[1]; // 0xFE + +// set subtype to 0x10 +binary.set_subtype(0x10); + +// serialize to MessagePack +auto cbor = json::to_msgpack(j); // 0xD5 (fixext2), 0x10, 0xCA, 0xFE +``` + ## Supported compilers diff --git a/doc/examples/is_array.cpp b/doc/examples/is_array.cpp index 235dab76..8286697b 100644 --- a/doc/examples/is_array.cpp +++ b/doc/examples/is_array.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary_array({1, 2, 3}); // call is_array() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_array() << '\n'; std::cout << j_array.is_array() << '\n'; std::cout << j_string.is_array() << '\n'; + std::cout << j_binary.is_array() << '\n'; } diff --git a/doc/examples/is_array.link b/doc/examples/is_array.link index 4232a922..fc292eb6 100644 --- a/doc/examples/is_array.link +++ b/doc/examples/is_array.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_array.output b/doc/examples/is_array.output index 5b8cc40c..7b7ef3f1 100644 --- a/doc/examples/is_array.output +++ b/doc/examples/is_array.output @@ -6,3 +6,4 @@ false false true false +false diff --git a/doc/examples/is_binary.cpp b/doc/examples/is_binary.cpp new file mode 100644 index 00000000..74173e91 --- /dev/null +++ b/doc/examples/is_binary.cpp @@ -0,0 +1,30 @@ +#include +#include + +using json = nlohmann::json; + +int main() +{ + // create JSON values + json j_null; + json j_boolean = true; + json j_number_integer = 17; + json j_number_unsigned_integer = 12345678987654321u; + json j_number_float = 23.42; + json j_object = {{"one", 1}, {"two", 2}}; + json j_array = {1, 2, 4, 8, 16}; + json j_string = "Hello, world"; + json j_binary = json::binary_array({1, 2, 3}); + + // call is_binary() + std::cout << std::boolalpha; + std::cout << j_null.is_binary() << '\n'; + std::cout << j_boolean.is_binary() << '\n'; + std::cout << j_number_integer.is_binary() << '\n'; + std::cout << j_number_unsigned_integer.is_binary() << '\n'; + std::cout << j_number_float.is_binary() << '\n'; + std::cout << j_object.is_binary() << '\n'; + std::cout << j_array.is_binary() << '\n'; + std::cout << j_string.is_binary() << '\n'; + std::cout << j_binary.is_binary() << '\n'; +} diff --git a/doc/examples/is_binary.link b/doc/examples/is_binary.link new file mode 100644 index 00000000..cc8e715e --- /dev/null +++ b/doc/examples/is_binary.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/is_binary.output b/doc/examples/is_binary.output new file mode 100644 index 00000000..505e76e4 --- /dev/null +++ b/doc/examples/is_binary.output @@ -0,0 +1,9 @@ +false +false +false +false +false +false +false +false +true diff --git a/doc/examples/is_boolean.cpp b/doc/examples/is_boolean.cpp index ccc202d0..d51d594e 100644 --- a/doc/examples/is_boolean.cpp +++ b/doc/examples/is_boolean.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary_array({1, 2, 3}); // call is_boolean() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_boolean() << '\n'; std::cout << j_array.is_boolean() << '\n'; std::cout << j_string.is_boolean() << '\n'; + std::cout << j_binary.is_boolean() << '\n'; } diff --git a/doc/examples/is_boolean.link b/doc/examples/is_boolean.link index 19f0b008..779a36e9 100644 --- a/doc/examples/is_boolean.link +++ b/doc/examples/is_boolean.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_boolean.output b/doc/examples/is_boolean.output index 721b3a5e..eace89d2 100644 --- a/doc/examples/is_boolean.output +++ b/doc/examples/is_boolean.output @@ -6,3 +6,4 @@ false false false false +false diff --git a/doc/examples/is_discarded.cpp b/doc/examples/is_discarded.cpp index 782e6499..914890c4 100644 --- a/doc/examples/is_discarded.cpp +++ b/doc/examples/is_discarded.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary_array({1, 2, 3}); // call is_discarded() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_discarded() << '\n'; std::cout << j_array.is_discarded() << '\n'; std::cout << j_string.is_discarded() << '\n'; + std::cout << j_binary.is_discarded() << '\n'; } diff --git a/doc/examples/is_discarded.link b/doc/examples/is_discarded.link index 9ec778f7..c51128f6 100644 --- a/doc/examples/is_discarded.link +++ b/doc/examples/is_discarded.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_discarded.output b/doc/examples/is_discarded.output index 485b1696..14718f64 100644 --- a/doc/examples/is_discarded.output +++ b/doc/examples/is_discarded.output @@ -6,3 +6,4 @@ false false false false +false diff --git a/doc/examples/is_null.cpp b/doc/examples/is_null.cpp index 4a635092..2e2730e1 100644 --- a/doc/examples/is_null.cpp +++ b/doc/examples/is_null.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary_array({1, 2, 3}); // call is_null() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_null() << '\n'; std::cout << j_array.is_null() << '\n'; std::cout << j_string.is_null() << '\n'; + std::cout << j_binary.is_null() << '\n'; } diff --git a/doc/examples/is_null.link b/doc/examples/is_null.link index dd1a5aab..fe02d31f 100644 --- a/doc/examples/is_null.link +++ b/doc/examples/is_null.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_null.output b/doc/examples/is_null.output index 4cc64628..42bbee2a 100644 --- a/doc/examples/is_null.output +++ b/doc/examples/is_null.output @@ -6,3 +6,4 @@ false false false false +false diff --git a/doc/examples/is_number.cpp b/doc/examples/is_number.cpp index eb23bd24..8ef27b0a 100644 --- a/doc/examples/is_number.cpp +++ b/doc/examples/is_number.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary_array({1, 2, 3}); // call is_number() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_number() << '\n'; std::cout << j_array.is_number() << '\n'; std::cout << j_string.is_number() << '\n'; + std::cout << j_binary.is_number() << '\n'; } diff --git a/doc/examples/is_number.link b/doc/examples/is_number.link index 43c52b47..c2623b29 100644 --- a/doc/examples/is_number.link +++ b/doc/examples/is_number.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_number.output b/doc/examples/is_number.output index 06dbc282..53ef340b 100644 --- a/doc/examples/is_number.output +++ b/doc/examples/is_number.output @@ -6,3 +6,4 @@ true false false false +false diff --git a/doc/examples/is_number_float.cpp b/doc/examples/is_number_float.cpp index 3435a7e0..8b8fa3bf 100644 --- a/doc/examples/is_number_float.cpp +++ b/doc/examples/is_number_float.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary_array({1, 2, 3}); // call is_number_float() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_number_float() << '\n'; std::cout << j_array.is_number_float() << '\n'; std::cout << j_string.is_number_float() << '\n'; + std::cout << j_binary.is_number_float() << '\n'; } diff --git a/doc/examples/is_number_float.link b/doc/examples/is_number_float.link index 4d8f0ea8..ec3c822e 100644 --- a/doc/examples/is_number_float.link +++ b/doc/examples/is_number_float.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_number_float.output b/doc/examples/is_number_float.output index 09afae4c..0e64601a 100644 --- a/doc/examples/is_number_float.output +++ b/doc/examples/is_number_float.output @@ -6,3 +6,4 @@ true false false false +false diff --git a/doc/examples/is_number_integer.cpp b/doc/examples/is_number_integer.cpp index 6f6e4204..efd76a7d 100644 --- a/doc/examples/is_number_integer.cpp +++ b/doc/examples/is_number_integer.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary_array({1, 2, 3}); // call is_number_integer() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_number_integer() << '\n'; std::cout << j_array.is_number_integer() << '\n'; std::cout << j_string.is_number_integer() << '\n'; + std::cout << j_binary.is_number_integer() << '\n'; } diff --git a/doc/examples/is_number_integer.link b/doc/examples/is_number_integer.link index 0233e0f7..49b57e66 100644 --- a/doc/examples/is_number_integer.link +++ b/doc/examples/is_number_integer.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_number_integer.output b/doc/examples/is_number_integer.output index be0f7393..c1df310f 100644 --- a/doc/examples/is_number_integer.output +++ b/doc/examples/is_number_integer.output @@ -6,3 +6,4 @@ false false false false +false diff --git a/doc/examples/is_number_unsigned.cpp b/doc/examples/is_number_unsigned.cpp index 2ea2673e..1a85b832 100644 --- a/doc/examples/is_number_unsigned.cpp +++ b/doc/examples/is_number_unsigned.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary_array({1, 2, 3}); // call is_number_unsigned() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_number_unsigned() << '\n'; std::cout << j_array.is_number_unsigned() << '\n'; std::cout << j_string.is_number_unsigned() << '\n'; + std::cout << j_binary.is_number_unsigned() << '\n'; } diff --git a/doc/examples/is_number_unsigned.link b/doc/examples/is_number_unsigned.link index 89817630..1af8cef1 100644 --- a/doc/examples/is_number_unsigned.link +++ b/doc/examples/is_number_unsigned.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_number_unsigned.output b/doc/examples/is_number_unsigned.output index fdf264e0..e6059d48 100644 --- a/doc/examples/is_number_unsigned.output +++ b/doc/examples/is_number_unsigned.output @@ -6,3 +6,4 @@ false false false false +false diff --git a/doc/examples/is_object.cpp b/doc/examples/is_object.cpp index 42cedf8f..63728eab 100644 --- a/doc/examples/is_object.cpp +++ b/doc/examples/is_object.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary_array({1, 2, 3}); // call is_object() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_object() << '\n'; std::cout << j_array.is_object() << '\n'; std::cout << j_string.is_object() << '\n'; + std::cout << j_binary.is_object() << '\n'; } diff --git a/doc/examples/is_object.link b/doc/examples/is_object.link index 5dd305bb..ce82ff85 100644 --- a/doc/examples/is_object.link +++ b/doc/examples/is_object.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_object.output b/doc/examples/is_object.output index e041e392..d9a429f8 100644 --- a/doc/examples/is_object.output +++ b/doc/examples/is_object.output @@ -6,3 +6,4 @@ false true false false +false diff --git a/doc/examples/is_primitive.cpp b/doc/examples/is_primitive.cpp index d70c83e3..88b0a6a9 100644 --- a/doc/examples/is_primitive.cpp +++ b/doc/examples/is_primitive.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary_array({1, 2, 3}); // call is_primitive() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_primitive() << '\n'; std::cout << j_array.is_primitive() << '\n'; std::cout << j_string.is_primitive() << '\n'; + std::cout << j_binary.is_primitive() << '\n'; } diff --git a/doc/examples/is_primitive.link b/doc/examples/is_primitive.link index ae58b2d7..4571494d 100644 --- a/doc/examples/is_primitive.link +++ b/doc/examples/is_primitive.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_primitive.output b/doc/examples/is_primitive.output index 635db6e3..77af24c0 100644 --- a/doc/examples/is_primitive.output +++ b/doc/examples/is_primitive.output @@ -6,3 +6,4 @@ true false false true +true diff --git a/doc/examples/is_string.cpp b/doc/examples/is_string.cpp index 2679cd1c..8bab2794 100644 --- a/doc/examples/is_string.cpp +++ b/doc/examples/is_string.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary_array({1, 2, 3}); // call is_string() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_string() << '\n'; std::cout << j_array.is_string() << '\n'; std::cout << j_string.is_string() << '\n'; + std::cout << j_binary.is_string() << '\n'; } diff --git a/doc/examples/is_string.link b/doc/examples/is_string.link index 92b05e75..9d126a95 100644 --- a/doc/examples/is_string.link +++ b/doc/examples/is_string.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_string.output b/doc/examples/is_string.output index 672eb438..6446f187 100644 --- a/doc/examples/is_string.output +++ b/doc/examples/is_string.output @@ -6,3 +6,4 @@ false false false true +false diff --git a/doc/examples/is_structured.cpp b/doc/examples/is_structured.cpp index 33e2bbd0..b022285b 100644 --- a/doc/examples/is_structured.cpp +++ b/doc/examples/is_structured.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary_array({1, 2, 3}); // call is_structured() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_structured() << '\n'; std::cout << j_array.is_structured() << '\n'; std::cout << j_string.is_structured() << '\n'; + std::cout << j_binary.is_structured() << '\n'; } diff --git a/doc/examples/is_structured.link b/doc/examples/is_structured.link index 44e06e9f..f3eb6272 100644 --- a/doc/examples/is_structured.link +++ b/doc/examples/is_structured.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_structured.output b/doc/examples/is_structured.output index e1186dd8..625c124b 100644 --- a/doc/examples/is_structured.output +++ b/doc/examples/is_structured.output @@ -6,3 +6,4 @@ false true true false +false diff --git a/doc/examples/sax_parse.cpp b/doc/examples/sax_parse.cpp index 82a99f7d..45273eb6 100644 --- a/doc/examples/sax_parse.cpp +++ b/doc/examples/sax_parse.cpp @@ -79,7 +79,7 @@ class sax_event_consumer : public json::json_sax_t return true; } - bool binary(binary_t& val) override + bool binary(json::binary_t& val) override { events.push_back("binary"); return true; diff --git a/doc/examples/sax_parse.link b/doc/examples/sax_parse.link index c51f517c..034b48c9 100644 --- a/doc/examples/sax_parse.link +++ b/doc/examples/sax_parse.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/index.md b/doc/index.md index f2de2e3a..3691461d 100644 --- a/doc/index.md +++ b/doc/index.md @@ -22,12 +22,14 @@ These pages contain the API documentation of JSON for Modern C++, a C++11 header @link nlohmann::basic_json::is_object is_object @endlink, @link nlohmann::basic_json::is_array is_array @endlink, @link nlohmann::basic_json::is_string is_string @endlink, - @link nlohmann::basic_json::is_discarded is_discarded @endlink -- check for value type + @link nlohmann::basic_json::is_discarded is_discarded @endlink, + @link nlohmann::basic_json::is_binary is_binary @endlink -- check for value type - @link nlohmann::basic_json::operator value_t() const operator value_t @endlink -- type of the value (implicit conversion) - value access - @link nlohmann::basic_json::get get @endlink -- get a value - @link nlohmann::basic_json::get_ptr get_ptr @endlink -- get a value pointer - @link nlohmann::basic_json::get_ref get_ref @endlink -- get a value reference + - @link nlohmann::basic_json::get_binary get_binary @endlink -- get a binary value - @link nlohmann::basic_json::operator ValueType() const operator ValueType @endlink -- get a value (implicit conversion) - @link nlohmann::basic_json::value value @endlink -- get a value from an object and return default value if key is not present - exceptions @@ -67,8 +69,9 @@ These pages contain the API documentation of JSON for Modern C++, a C++11 header - @link nlohmann::basic_json::number_integer_t signed integers @endlink - @link nlohmann::basic_json::number_unsigned_t unsigned integers @endlink - @link nlohmann::basic_json::number_float_t floating-point @endlink + - @link nlohmann::basic_json::binary_t binary values @endlink - further JSON standards - - @link nlohmann::json_pointer JSON Pointer @endlink (REF 6901) + - @link nlohmann::json_pointer JSON Pointer @endlink (RFC 6901) - @link nlohmann::basic_json::patch JSON Patch @endlink (RFC 6902) - @link nlohmann::basic_json::merge_patch JSON Merge Patch @endlink (RFC 7396) @@ -324,7 +327,7 @@ Note that this table only lists those exceptions thrown due to the type. For ins -@copyright Copyright © 2013-2019 Niels Lohmann. The code is licensed under the [MIT License](http://opensource.org/licenses/MIT). +@copyright Copyright © 2013-2020 Niels Lohmann. The code is licensed under the [MIT License](http://opensource.org/licenses/MIT). @author [Niels Lohmann](http://nlohmann.me) @see https://github.com/nlohmann/json to download the source code diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index b6105b05..e8731f22 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -228,7 +228,7 @@ template ::value and not is_constructible_object_type::value and not is_constructible_string_type::value and - not std::is_same::value and + not std::is_same::value and not is_basic_json::value, int > = 0 > auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) @@ -246,14 +246,14 @@ void()) } template -void from_json(const BasicJsonType& j, typename BasicJsonType::internal_binary_t& bin) +void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { if (JSON_HEDLEY_UNLIKELY(not j.is_binary())) { JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); } - bin = *j.template get_ptr(); + bin = *j.template get_ptr(); } template struct external_constructor { template - static void construct(BasicJsonType& j, const typename BasicJsonType::internal_binary_t& b) + static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) { j.m_type = value_t::binary; - typename BasicJsonType::internal_binary_t value{b}; + typename BasicJsonType::binary_t value{b}; j.m_value = value; j.assert_invariant(); } template - static void construct(BasicJsonType& j, typename BasicJsonType::internal_binary_t&& b) + static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) { j.m_type = value_t::binary; - typename BasicJsonType::internal_binary_t value{std::move(b)}; + typename BasicJsonType::binary_t value{std::move(b)}; j.m_value = value; j.assert_invariant(); } @@ -280,7 +280,7 @@ template ::value and not is_compatible_object_type::value and not is_compatible_string_type::value and - not std::is_same::value and + not std::is_same::value and not is_basic_json::value, int> = 0> void to_json(BasicJsonType& j, const CompatibleArrayType& arr) @@ -289,7 +289,7 @@ void to_json(BasicJsonType& j, const CompatibleArrayType& arr) } template -void to_json(BasicJsonType& j, const typename BasicJsonType::internal_binary_t& bin) +void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) { external_constructor::construct(j, bin); } diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index 35d7dbd6..e859c7d1 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -52,7 +52,7 @@ class binary_reader using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; using json_sax_t = SAX; public: @@ -219,7 +219,7 @@ class binary_reader @return `true` if the byte array was successfully parsed */ template - bool get_bson_binary(const NumberType len, internal_binary_t& result) + bool get_bson_binary(const NumberType len, binary_t& result) { if (JSON_HEDLEY_UNLIKELY(len < 0)) { @@ -276,7 +276,7 @@ class binary_reader case 0x05: // binary { std::int32_t len; - internal_binary_t value; + binary_t value; return get_number(input_format_t::bson, len) and get_bson_binary(len, value) and sax->binary(value); } @@ -532,7 +532,7 @@ class binary_reader case 0x5B: // Binary data (eight-byte uint64_t for n follow) case 0x5F: // Binary data (indefinite length) { - internal_binary_t b; + binary_t b; return get_cbor_binary(b) and sax->binary(b); } @@ -862,7 +862,7 @@ class binary_reader @return whether byte array creation completed */ - bool get_cbor_binary(internal_binary_t& result) + bool get_cbor_binary(binary_t& result) { if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "binary"))) { @@ -932,7 +932,7 @@ class binary_reader { while (get() != 0xFF) { - internal_binary_t chunk; + binary_t chunk; if (not get_cbor_binary(chunk)) { return false; @@ -1282,7 +1282,7 @@ class binary_reader case 0xD7: // fixext 8 case 0xD8: // fixext 16 { - internal_binary_t b; + binary_t b; return get_msgpack_binary(b) and sax->binary(b); } @@ -1505,7 +1505,7 @@ class binary_reader @return whether byte array creation completed */ - bool get_msgpack_binary(internal_binary_t& result) + bool get_msgpack_binary(binary_t& result) { // helper function to set the subtype auto assign_and_return_true = [&result](std::int8_t subtype) @@ -2223,7 +2223,7 @@ class binary_reader template bool get_binary(const input_format_t format, const NumberType len, - internal_binary_t& result) + binary_t& result) { bool success = true; std::generate_n(std::back_inserter(result), len, [this, &success, &format]() diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 54b53fd3..25be7e4b 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -27,7 +27,7 @@ struct json_sax using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; /*! @brief a null value was read @@ -78,7 +78,7 @@ struct json_sax @return whether parsing should proceed @note It is safe to move the passed binary. */ - virtual bool binary(internal_binary_t& val) = 0; + virtual bool binary(binary_t& val) = 0; /*! @brief the beginning of an object was read @@ -154,7 +154,7 @@ class json_sax_dom_parser using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; /*! @param[in, out] r reference to a JSON value that is manipulated while @@ -208,7 +208,7 @@ class json_sax_dom_parser return true; } - bool binary(internal_binary_t& val) + bool binary(binary_t& val) { handle_value(std::move(val)); return true; @@ -343,7 +343,7 @@ class json_sax_dom_callback_parser using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; using parser_callback_t = typename BasicJsonType::parser_callback_t; using parse_event_t = typename BasicJsonType::parse_event_t; @@ -398,7 +398,7 @@ class json_sax_dom_callback_parser return true; } - bool binary(internal_binary_t& val) + bool binary(binary_t& val) { handle_value(std::move(val)); return true; @@ -654,7 +654,7 @@ class json_sax_acceptor using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; bool null() { @@ -686,7 +686,7 @@ class json_sax_acceptor return true; } - bool binary(internal_binary_t& /*unused*/) + bool binary(binary_t& /*unused*/) { return true; } diff --git a/include/nlohmann/detail/iterators/internal_iterator.hpp b/include/nlohmann/detail/iterators/internal_iterator.hpp index 71539a6a..742df483 100644 --- a/include/nlohmann/detail/iterators/internal_iterator.hpp +++ b/include/nlohmann/detail/iterators/internal_iterator.hpp @@ -19,7 +19,7 @@ template struct internal_iterator /// iterator for JSON arrays typename BasicJsonType::array_t::iterator array_iterator {}; /// iterator for JSON binary arrays - typename BasicJsonType::binary_t::iterator binary_iterator {}; + typename BasicJsonType::binary_t::container_type::iterator binary_iterator {}; /// generic iterator for all other types primitive_iterator_t primitive_iterator {}; }; diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 1feb95dc..4478ab34 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -27,7 +27,7 @@ template class binary_writer { using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; public: /*! @@ -1080,7 +1080,7 @@ class binary_writer /*! @return The size of the BSON-encoded binary array @a value */ - static std::size_t calc_bson_binary_size(const typename BasicJsonType::internal_binary_t& value) + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) { return sizeof(std::int32_t) + value.size() + 1ul; } @@ -1108,7 +1108,7 @@ class binary_writer @brief Writes a BSON element with key @a name and binary value @a value */ void write_bson_binary(const string_t& name, - const internal_binary_t& value) + const binary_t& value) { write_bson_entry_header(name, 0x05); diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index 75ce6783..dc963f53 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -45,7 +45,7 @@ class serializer using number_float_t = typename BasicJsonType::number_float_t; using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using binary_t = typename BasicJsonType::binary_t; + using binary_char_t = typename BasicJsonType::binary_t::value_type; static constexpr std::uint8_t UTF8_ACCEPT = 0; static constexpr std::uint8_t UTF8_REJECT = 1; @@ -624,7 +624,7 @@ class serializer template::value or std::is_same::value or - std::is_same::value, + std::is_same::value, int> = 0> void dump_integer(NumberType x) { diff --git a/include/nlohmann/detail/wrapped_binary_t.hpp b/include/nlohmann/detail/wrapped_binary_t.hpp index 230641bd..6edd7b52 100644 --- a/include/nlohmann/detail/wrapped_binary_t.hpp +++ b/include/nlohmann/detail/wrapped_binary_t.hpp @@ -19,36 +19,51 @@ template class wrapped_binary_t : public BinaryType { public: - using binary_t = BinaryType; + /// the type of the underlying container + using container_type = BinaryType; - wrapped_binary_t() noexcept(noexcept(binary_t())) - : binary_t() + wrapped_binary_t() noexcept(noexcept(container_type())) + : container_type() {} - wrapped_binary_t(const binary_t& b) noexcept(noexcept(binary_t(b))) - : binary_t(b) + wrapped_binary_t(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) {} - wrapped_binary_t(binary_t&& b) noexcept(noexcept(binary_t(std::move(b)))) - : binary_t(std::move(b)) + wrapped_binary_t(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) {} - wrapped_binary_t(const binary_t& b, - std::uint8_t subtype) noexcept(noexcept(binary_t(b))) - : binary_t(b) + wrapped_binary_t(const container_type& b, + std::uint8_t subtype) noexcept(noexcept(container_type(b))) + : container_type(b) , m_subtype(subtype) , m_has_subtype(true) {} - wrapped_binary_t(binary_t&& b, std::uint8_t subtype) noexcept(noexcept(binary_t(std::move(b)))) - : binary_t(std::move(b)) + wrapped_binary_t(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) , m_subtype(subtype) , m_has_subtype(true) {} /*! - @brief set the subtype - @param subtype subtype to set (implementation specific) + @brief sets the binary subtype + + Sets the binary subtype of the value, also flags a binary JSON value as + having a subtype, which has implications for serialization. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 */ void set_subtype(std::uint8_t subtype) noexcept { @@ -57,8 +72,25 @@ class wrapped_binary_t : public BinaryType } /*! - @brief get the subtype - @return subtype (implementation specific) + @brief return the binary subtype + + Returns the numerical subtype of the value if it has a subtype. If it does + not have a subtype, this function will return size_t(-1) as a sentinel + value. + + @return the numerical subtype of the binary value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 */ constexpr std::uint8_t subtype() const noexcept { @@ -66,8 +98,20 @@ class wrapped_binary_t : public BinaryType } /*! - @brief get whether a subtype was set - @return whether a subtype was set + @brief return whether the value has a subtype + + @return whether the value has a subtype + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + + @since version 3.8.0 */ constexpr bool has_subtype() const noexcept { @@ -75,7 +119,23 @@ class wrapped_binary_t : public BinaryType } /*! - @brief clear the subtype + @brief clears the binary subtype + + Clears the binary subtype and flags the value as not having a subtype, which + has implications for serialization; for instance MessagePack will prefer the + bin family over the ext family. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 */ void clear_subtype() noexcept { diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 2b976d91..fc849a38 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -884,23 +884,7 @@ class basic_json for any access to array values, a pointer of the type `binary_t*` must be dereferenced. - @sa @ref array_t -- type for an array value - - @since version 3.8.0 - */ - using binary_t = BinaryType; - - /*! - @brief binary array with a binary type - - This type is used to store binary types internally. It wrapps the template - type `BinaryType` (@ref binary_t) and adds a subtype to allow to distinguish - different binary types from different formats. - - While @ref binary_t is used to define how binary values are stored, this - type is used to access binary values once they are parsed. - - Notes on subtypes: + #### Notes on subtypes - CBOR - Binary values are represented as byte strings. No subtypes are @@ -911,13 +895,15 @@ class basic_json is used. For other sizes, the ext family (ext8, ext16, ext32) is used. The subtype is then added as singed 8-bit integer. - If no subtype is given, the bin family (bin8, bin16, bin32) is used. - - BSON + - BSON - If a subtype is given, it is used and added as unsigned 8-bit integer. - If no subtype is given, the generic binary subtype 0x00 is used. @sa @ref binary_array -- create a binary array + + @since version 3.8.0 */ - using internal_binary_t = nlohmann::detail::wrapped_binary_t; + using binary_t = nlohmann::detail::wrapped_binary_t; /// @} private: @@ -978,7 +964,7 @@ class basic_json /// string (stored with pointer to save storage) string_t* string; /// binary (stored with pointer to save storage) - internal_binary_t* binary; + binary_t* binary; /// boolean boolean_t boolean; /// number (integer) @@ -1023,7 +1009,7 @@ class basic_json case value_t::binary: { - binary = create(); + binary = create(); break; } @@ -1106,27 +1092,27 @@ class basic_json } /// constructor for binary arrays - json_value(const binary_t& value) + json_value(const typename binary_t::container_type& value) { - binary = create(value); + binary = create(value); } /// constructor for rvalue binary arrays - json_value(binary_t&& value) + json_value(typename binary_t::container_type&& value) { - binary = create(std::move(value)); + binary = create(std::move(value)); } /// constructor for binary arrays (internal type) - json_value(const internal_binary_t& value) + json_value(const binary_t& value) { - binary = create(value); + binary = create(value); } /// constructor for rvalue binary arrays (internal type) - json_value(internal_binary_t&& value) + json_value(binary_t&& value) { - binary = create(std::move(value)); + binary = create(std::move(value)); } void destroy(value_t t) noexcept @@ -1206,7 +1192,7 @@ class basic_json case value_t::binary: { - AllocatorType alloc; + AllocatorType alloc; std::allocator_traits::destroy(alloc, binary); std::allocator_traits::deallocate(alloc, binary, 1); break; @@ -1492,7 +1478,7 @@ class basic_json using other_string_t = typename BasicJsonType::string_t; using other_object_t = typename BasicJsonType::object_t; using other_array_t = typename BasicJsonType::array_t; - using other_binary_t = typename BasicJsonType::internal_binary_t; + using other_binary_t = typename BasicJsonType::binary_t; switch (val.type()) { @@ -1686,7 +1672,7 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(const binary_t& init) + static basic_json binary_array(const typename binary_t::container_type& init) { auto res = basic_json(); res.m_type = value_t::binary; @@ -1695,11 +1681,11 @@ class basic_json } JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(const binary_t& init, std::uint8_t subtype) + static basic_json binary_array(const typename binary_t::container_type& init, std::uint8_t subtype) { auto res = basic_json(); res.m_type = value_t::binary; - res.m_value = internal_binary_t(init, subtype); + res.m_value = binary_t(init, subtype); return res; } @@ -1731,7 +1717,7 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(binary_t&& init) + static basic_json binary_array(typename binary_t::container_type&& init) { auto res = basic_json(); res.m_type = value_t::binary; @@ -1740,11 +1726,11 @@ class basic_json } JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(binary_t&& init, std::uint8_t subtype) + static basic_json binary_array(typename binary_t::container_type&& init, std::uint8_t subtype) { auto res = basic_json(); res.m_type = value_t::binary; - res.m_value = internal_binary_t(std::move(init), subtype); + res.m_value = binary_t(std::move(init), subtype); return res; } @@ -2794,13 +2780,13 @@ class basic_json } /// get a pointer to the value (binary) - internal_binary_t* get_impl_ptr(internal_binary_t* /*unused*/) noexcept + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept { return is_binary() ? m_value.binary : nullptr; } /// get a pointer to the value (binary) - constexpr const internal_binary_t* get_impl_ptr(const internal_binary_t* /*unused*/) const noexcept + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept { return is_binary() ? m_value.binary : nullptr; } @@ -3230,6 +3216,36 @@ class basic_json return get(); } + /*! + @return reference to the binary value + + @throw type_error.302 if the value is not binary + + @sa @ref is_binary() to check if the value is binary + + @since version 3.8.0 + */ + binary_t& get_binary() + { + if (not is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + + /// @copydoc get_binary() + const binary_t& get_binary() const + { + if (not is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + /// @} @@ -3852,118 +3868,6 @@ class basic_json return value(ptr, string_t(default_value)); } - /*! - @brief return the binary subtype - - Returns the numerical subtype of the JSON value, if the JSON value is of - type "binary", and it has a subtype. If it does not have a subtype (or the - object is not of type binary) this function will return size_t(-1) as a - sentinel value. - - @return the numerical subtype of the binary JSON value - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - std::size_t get_subtype() const noexcept - { - if (is_binary() and m_value.binary->has_subtype) - { - return m_value.binary->subtype; - } - - return std::size_t(-1); - } - - /*! - @brief sets the binary subtype - - Sets the binary subtype of the JSON value, also flags a binary JSON value as - having a subtype, which has implications for serialization to msgpack (will - prefer ext file formats over bin). If the JSON value is not a binary value, - this function does nothing. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref get_subtype() -- return the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - - void set_subtype(std::uint8_t subtype) noexcept - { - if (is_binary()) - { - m_value.binary->set_subtype(subtype); - } - } - - /*! - @brief clears the binary subtype - - Clears the binary subtype of the JSON value, also flags a binary JSON value - as not having a subtype, which has implications for serialization to msgpack - (will prefer bin file formats over ext). If the JSON value is not a binary - value, this function does nothing. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref get_subtype() -- return the binary subtype - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - void clear_subtype() noexcept - { - if (is_binary()) - { - m_value.binary->clear_subtype(); - } - } - - /*! - @brief return whether or not the binary subtype has a value - - Returns whether or not the binary subtype has a value. - - @return whether or not the binary subtype has a value. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref get_subtype() -- return the binary subtype - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - - @since version 3.8.0 - */ - bool has_subtype() const noexcept - { - return is_binary() and m_value.binary->has_subtype(); - } - /*! @brief access the first element @@ -4133,7 +4037,7 @@ class basic_json } else if (is_binary()) { - AllocatorType alloc; + AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.binary); std::allocator_traits::deallocate(alloc, m_value.binary, 1); m_value.binary = nullptr; @@ -4247,7 +4151,7 @@ class basic_json } else if (is_binary()) { - AllocatorType alloc; + AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.binary); std::allocator_traits::deallocate(alloc, m_value.binary, 1); m_value.binary = nullptr; @@ -6087,6 +5991,20 @@ class basic_json } } + /// @copydoc swap(binary_t) + void swap(typename binary_t::container_type& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + /// @} public: diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 1a8aa14c..0033be23 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3448,7 +3448,7 @@ template ::value and not is_constructible_object_type::value and not is_constructible_string_type::value and - not std::is_same::value and + not std::is_same::value and not is_basic_json::value, int > = 0 > auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) @@ -3466,14 +3466,14 @@ void()) } template -void from_json(const BasicJsonType& j, typename BasicJsonType::internal_binary_t& bin) +void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) { if (JSON_HEDLEY_UNLIKELY(not j.is_binary())) { JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); } - bin = *j.template get_ptr(); + bin = *j.template get_ptr(); } template struct external_constructor { template - static void construct(BasicJsonType& j, const typename BasicJsonType::internal_binary_t& b) + static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) { j.m_type = value_t::binary; - typename BasicJsonType::internal_binary_t value{b}; + typename BasicJsonType::binary_t value{b}; j.m_value = value; j.assert_invariant(); } template - static void construct(BasicJsonType& j, typename BasicJsonType::internal_binary_t&& b) + static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) { j.m_type = value_t::binary; - typename BasicJsonType::internal_binary_t value{std::move(b)}; + typename BasicJsonType::binary_t value{std::move(b)}; j.m_value = value; j.assert_invariant(); } @@ -4085,7 +4085,7 @@ template ::value and not is_compatible_object_type::value and not is_compatible_string_type::value and - not std::is_same::value and + not std::is_same::value and not is_basic_json::value, int> = 0> void to_json(BasicJsonType& j, const CompatibleArrayType& arr) @@ -4094,7 +4094,7 @@ void to_json(BasicJsonType& j, const CompatibleArrayType& arr) } template -void to_json(BasicJsonType& j, const typename BasicJsonType::internal_binary_t& bin) +void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) { external_constructor::construct(j, bin); } @@ -4764,7 +4764,7 @@ struct json_sax using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; /*! @brief a null value was read @@ -4815,7 +4815,7 @@ struct json_sax @return whether parsing should proceed @note It is safe to move the passed binary. */ - virtual bool binary(internal_binary_t& val) = 0; + virtual bool binary(binary_t& val) = 0; /*! @brief the beginning of an object was read @@ -4891,7 +4891,7 @@ class json_sax_dom_parser using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; /*! @param[in, out] r reference to a JSON value that is manipulated while @@ -4945,7 +4945,7 @@ class json_sax_dom_parser return true; } - bool binary(internal_binary_t& val) + bool binary(binary_t& val) { handle_value(std::move(val)); return true; @@ -5080,7 +5080,7 @@ class json_sax_dom_callback_parser using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; using parser_callback_t = typename BasicJsonType::parser_callback_t; using parse_event_t = typename BasicJsonType::parse_event_t; @@ -5135,7 +5135,7 @@ class json_sax_dom_callback_parser return true; } - bool binary(internal_binary_t& val) + bool binary(binary_t& val) { handle_value(std::move(val)); return true; @@ -5391,7 +5391,7 @@ class json_sax_acceptor using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; bool null() { @@ -5423,7 +5423,7 @@ class json_sax_acceptor return true; } - bool binary(internal_binary_t& /*unused*/) + bool binary(binary_t& /*unused*/) { return true; } @@ -5645,7 +5645,7 @@ class binary_reader using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; using json_sax_t = SAX; public: @@ -5812,7 +5812,7 @@ class binary_reader @return `true` if the byte array was successfully parsed */ template - bool get_bson_binary(const NumberType len, internal_binary_t& result) + bool get_bson_binary(const NumberType len, binary_t& result) { if (JSON_HEDLEY_UNLIKELY(len < 0)) { @@ -5869,7 +5869,7 @@ class binary_reader case 0x05: // binary { std::int32_t len; - internal_binary_t value; + binary_t value; return get_number(input_format_t::bson, len) and get_bson_binary(len, value) and sax->binary(value); } @@ -6125,7 +6125,7 @@ class binary_reader case 0x5B: // Binary data (eight-byte uint64_t for n follow) case 0x5F: // Binary data (indefinite length) { - internal_binary_t b; + binary_t b; return get_cbor_binary(b) and sax->binary(b); } @@ -6455,7 +6455,7 @@ class binary_reader @return whether byte array creation completed */ - bool get_cbor_binary(internal_binary_t& result) + bool get_cbor_binary(binary_t& result) { if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "binary"))) { @@ -6525,7 +6525,7 @@ class binary_reader { while (get() != 0xFF) { - internal_binary_t chunk; + binary_t chunk; if (not get_cbor_binary(chunk)) { return false; @@ -6875,7 +6875,7 @@ class binary_reader case 0xD7: // fixext 8 case 0xD8: // fixext 16 { - internal_binary_t b; + binary_t b; return get_msgpack_binary(b) and sax->binary(b); } @@ -7098,7 +7098,7 @@ class binary_reader @return whether byte array creation completed */ - bool get_msgpack_binary(internal_binary_t& result) + bool get_msgpack_binary(binary_t& result) { // helper function to set the subtype auto assign_and_return_true = [&result](std::int8_t subtype) @@ -7816,7 +7816,7 @@ class binary_reader template bool get_binary(const input_format_t format, const NumberType len, - internal_binary_t& result) + binary_t& result) { bool success = true; std::generate_n(std::back_inserter(result), len, [this, &success, &format]() @@ -10089,7 +10089,7 @@ template struct internal_iterator /// iterator for JSON arrays typename BasicJsonType::array_t::iterator array_iterator {}; /// iterator for JSON binary arrays - typename BasicJsonType::binary_t::iterator binary_iterator {}; + typename BasicJsonType::binary_t::container_type::iterator binary_iterator {}; /// generic iterator for all other types primitive_iterator_t primitive_iterator {}; }; @@ -12080,7 +12080,7 @@ template class binary_writer { using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; public: /*! @@ -13133,7 +13133,7 @@ class binary_writer /*! @return The size of the BSON-encoded binary array @a value */ - static std::size_t calc_bson_binary_size(const typename BasicJsonType::internal_binary_t& value) + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) { return sizeof(std::int32_t) + value.size() + 1ul; } @@ -13161,7 +13161,7 @@ class binary_writer @brief Writes a BSON element with key @a name and binary value @a value */ void write_bson_binary(const string_t& name, - const internal_binary_t& value) + const binary_t& value) { write_bson_entry_header(name, 0x05); @@ -14789,7 +14789,7 @@ class serializer using number_float_t = typename BasicJsonType::number_float_t; using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using binary_t = typename BasicJsonType::binary_t; + using binary_char_t = typename BasicJsonType::binary_t::value_type; static constexpr std::uint8_t UTF8_ACCEPT = 0; static constexpr std::uint8_t UTF8_REJECT = 1; @@ -15368,7 +15368,7 @@ class serializer template::value or std::is_same::value or - std::is_same::value, + std::is_same::value, int> = 0> void dump_integer(NumberType x) { @@ -15667,36 +15667,51 @@ template class wrapped_binary_t : public BinaryType { public: - using binary_t = BinaryType; + /// the type of the underlying container + using container_type = BinaryType; - wrapped_binary_t() noexcept(noexcept(binary_t())) - : binary_t() + wrapped_binary_t() noexcept(noexcept(container_type())) + : container_type() {} - wrapped_binary_t(const binary_t& b) noexcept(noexcept(binary_t(b))) - : binary_t(b) + wrapped_binary_t(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) {} - wrapped_binary_t(binary_t&& b) noexcept(noexcept(binary_t(std::move(b)))) - : binary_t(std::move(b)) + wrapped_binary_t(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) {} - wrapped_binary_t(const binary_t& b, - std::uint8_t subtype) noexcept(noexcept(binary_t(b))) - : binary_t(b) + wrapped_binary_t(const container_type& b, + std::uint8_t subtype) noexcept(noexcept(container_type(b))) + : container_type(b) , m_subtype(subtype) , m_has_subtype(true) {} - wrapped_binary_t(binary_t&& b, std::uint8_t subtype) noexcept(noexcept(binary_t(std::move(b)))) - : binary_t(std::move(b)) + wrapped_binary_t(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) , m_subtype(subtype) , m_has_subtype(true) {} /*! - @brief set the subtype - @param subtype subtype to set (implementation specific) + @brief sets the binary subtype + + Sets the binary subtype of the value, also flags a binary JSON value as + having a subtype, which has implications for serialization. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 */ void set_subtype(std::uint8_t subtype) noexcept { @@ -15705,8 +15720,25 @@ class wrapped_binary_t : public BinaryType } /*! - @brief get the subtype - @return subtype (implementation specific) + @brief return the binary subtype + + Returns the numerical subtype of the value if it has a subtype. If it does + not have a subtype, this function will return size_t(-1) as a sentinel + value. + + @return the numerical subtype of the binary value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 */ constexpr std::uint8_t subtype() const noexcept { @@ -15714,8 +15746,20 @@ class wrapped_binary_t : public BinaryType } /*! - @brief get whether a subtype was set - @return whether a subtype was set + @brief return whether the value has a subtype + + @return whether the value has a subtype + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + + @since version 3.8.0 */ constexpr bool has_subtype() const noexcept { @@ -15723,7 +15767,23 @@ class wrapped_binary_t : public BinaryType } /*! - @brief clear the subtype + @brief clears the binary subtype + + Clears the binary subtype and flags the value as not having a subtype, which + has implications for serialization; for instance MessagePack will prefer the + bin family over the ext family. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 */ void clear_subtype() noexcept { @@ -16553,23 +16613,7 @@ class basic_json for any access to array values, a pointer of the type `binary_t*` must be dereferenced. - @sa @ref array_t -- type for an array value - - @since version 3.8.0 - */ - using binary_t = BinaryType; - - /*! - @brief binary array with a binary type - - This type is used to store binary types internally. It wrapps the template - type `BinaryType` (@ref binary_t) and adds a subtype to allow to distinguish - different binary types from different formats. - - While @ref binary_t is used to define how binary values are stored, this - type is used to access binary values once they are parsed. - - Notes on subtypes: + #### Notes on subtypes - CBOR - Binary values are represented as byte strings. No subtypes are @@ -16580,13 +16624,15 @@ class basic_json is used. For other sizes, the ext family (ext8, ext16, ext32) is used. The subtype is then added as singed 8-bit integer. - If no subtype is given, the bin family (bin8, bin16, bin32) is used. - - BSON + - BSON - If a subtype is given, it is used and added as unsigned 8-bit integer. - If no subtype is given, the generic binary subtype 0x00 is used. @sa @ref binary_array -- create a binary array + + @since version 3.8.0 */ - using internal_binary_t = nlohmann::detail::wrapped_binary_t; + using binary_t = nlohmann::detail::wrapped_binary_t; /// @} private: @@ -16647,7 +16693,7 @@ class basic_json /// string (stored with pointer to save storage) string_t* string; /// binary (stored with pointer to save storage) - internal_binary_t* binary; + binary_t* binary; /// boolean boolean_t boolean; /// number (integer) @@ -16692,7 +16738,7 @@ class basic_json case value_t::binary: { - binary = create(); + binary = create(); break; } @@ -16775,27 +16821,27 @@ class basic_json } /// constructor for binary arrays - json_value(const binary_t& value) + json_value(const typename binary_t::container_type& value) { - binary = create(value); + binary = create(value); } /// constructor for rvalue binary arrays - json_value(binary_t&& value) + json_value(typename binary_t::container_type&& value) { - binary = create(std::move(value)); + binary = create(std::move(value)); } /// constructor for binary arrays (internal type) - json_value(const internal_binary_t& value) + json_value(const binary_t& value) { - binary = create(value); + binary = create(value); } /// constructor for rvalue binary arrays (internal type) - json_value(internal_binary_t&& value) + json_value(binary_t&& value) { - binary = create(std::move(value)); + binary = create(std::move(value)); } void destroy(value_t t) noexcept @@ -16875,7 +16921,7 @@ class basic_json case value_t::binary: { - AllocatorType alloc; + AllocatorType alloc; std::allocator_traits::destroy(alloc, binary); std::allocator_traits::deallocate(alloc, binary, 1); break; @@ -17161,7 +17207,7 @@ class basic_json using other_string_t = typename BasicJsonType::string_t; using other_object_t = typename BasicJsonType::object_t; using other_array_t = typename BasicJsonType::array_t; - using other_binary_t = typename BasicJsonType::internal_binary_t; + using other_binary_t = typename BasicJsonType::binary_t; switch (val.type()) { @@ -17355,7 +17401,7 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(const binary_t& init) + static basic_json binary_array(const typename binary_t::container_type& init) { auto res = basic_json(); res.m_type = value_t::binary; @@ -17364,11 +17410,11 @@ class basic_json } JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(const binary_t& init, std::uint8_t subtype) + static basic_json binary_array(const typename binary_t::container_type& init, std::uint8_t subtype) { auto res = basic_json(); res.m_type = value_t::binary; - res.m_value = internal_binary_t(init, subtype); + res.m_value = binary_t(init, subtype); return res; } @@ -17400,7 +17446,7 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(binary_t&& init) + static basic_json binary_array(typename binary_t::container_type&& init) { auto res = basic_json(); res.m_type = value_t::binary; @@ -17409,11 +17455,11 @@ class basic_json } JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(binary_t&& init, std::uint8_t subtype) + static basic_json binary_array(typename binary_t::container_type&& init, std::uint8_t subtype) { auto res = basic_json(); res.m_type = value_t::binary; - res.m_value = internal_binary_t(std::move(init), subtype); + res.m_value = binary_t(std::move(init), subtype); return res; } @@ -18463,13 +18509,13 @@ class basic_json } /// get a pointer to the value (binary) - internal_binary_t* get_impl_ptr(internal_binary_t* /*unused*/) noexcept + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept { return is_binary() ? m_value.binary : nullptr; } /// get a pointer to the value (binary) - constexpr const internal_binary_t* get_impl_ptr(const internal_binary_t* /*unused*/) const noexcept + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept { return is_binary() ? m_value.binary : nullptr; } @@ -18899,6 +18945,36 @@ class basic_json return get(); } + /*! + @return reference to the binary value + + @throw type_error.302 if the value is not binary + + @sa @ref is_binary() to check if the value is binary + + @since version 3.8.0 + */ + binary_t& get_binary() + { + if (not is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + + /// @copydoc get_binary() + const binary_t& get_binary() const + { + if (not is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + /// @} @@ -19521,118 +19597,6 @@ class basic_json return value(ptr, string_t(default_value)); } - /*! - @brief return the binary subtype - - Returns the numerical subtype of the JSON value, if the JSON value is of - type "binary", and it has a subtype. If it does not have a subtype (or the - object is not of type binary) this function will return size_t(-1) as a - sentinel value. - - @return the numerical subtype of the binary JSON value - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - std::size_t get_subtype() const noexcept - { - if (is_binary() and m_value.binary->has_subtype) - { - return m_value.binary->subtype; - } - - return std::size_t(-1); - } - - /*! - @brief sets the binary subtype - - Sets the binary subtype of the JSON value, also flags a binary JSON value as - having a subtype, which has implications for serialization to msgpack (will - prefer ext file formats over bin). If the JSON value is not a binary value, - this function does nothing. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref get_subtype() -- return the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - - void set_subtype(std::uint8_t subtype) noexcept - { - if (is_binary()) - { - m_value.binary->set_subtype(subtype); - } - } - - /*! - @brief clears the binary subtype - - Clears the binary subtype of the JSON value, also flags a binary JSON value - as not having a subtype, which has implications for serialization to msgpack - (will prefer bin file formats over ext). If the JSON value is not a binary - value, this function does nothing. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref get_subtype() -- return the binary subtype - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - void clear_subtype() noexcept - { - if (is_binary()) - { - m_value.binary->clear_subtype(); - } - } - - /*! - @brief return whether or not the binary subtype has a value - - Returns whether or not the binary subtype has a value. - - @return whether or not the binary subtype has a value. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref get_subtype() -- return the binary subtype - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - - @since version 3.8.0 - */ - bool has_subtype() const noexcept - { - return is_binary() and m_value.binary->has_subtype(); - } - /*! @brief access the first element @@ -19802,7 +19766,7 @@ class basic_json } else if (is_binary()) { - AllocatorType alloc; + AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.binary); std::allocator_traits::deallocate(alloc, m_value.binary, 1); m_value.binary = nullptr; @@ -19916,7 +19880,7 @@ class basic_json } else if (is_binary()) { - AllocatorType alloc; + AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.binary); std::allocator_traits::deallocate(alloc, m_value.binary, 1); m_value.binary = nullptr; @@ -21756,6 +21720,20 @@ class basic_json } } + /// @copydoc swap(binary_t) + void swap(typename binary_t::container_type& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + /// @} public: diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index f8eb6112..d43099f2 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -77,7 +77,7 @@ class SaxEventLogger return true; } - bool binary(json::internal_binary_t& val) + bool binary(json::binary_t& val) { std::string binary_contents = "binary("; std::string comma_space = ""; @@ -183,7 +183,7 @@ class SaxCountdown : public nlohmann::json::json_sax_t return events_left-- > 0; } - bool binary(json::internal_binary_t&) override + bool binary(json::binary_t&) override { return events_left-- > 0; } diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 742bdcd9..81fca64a 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -485,14 +485,14 @@ TEST_CASE("constructors") { SECTION("empty binary") { - json::internal_binary_t b{}; + json::binary_t b{}; json j(b); CHECK(j.type() == json::value_t::binary); } SECTION("filled binary") { - json::internal_binary_t b({1, 2, 3}); + json::binary_t b({1, 2, 3}); json j(b); CHECK(j.type() == json::value_t::binary); } diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index 52ea9259..15744b9e 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -76,7 +76,7 @@ struct SaxEventLogger : public nlohmann::json_sax return true; } - bool binary(json::internal_binary_t& val) override + bool binary(json::binary_t& val) override { std::string binary_contents = "binary("; std::string comma_space = ""; diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index 2349dc14..fc290162 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -968,21 +968,35 @@ TEST_CASE("modifiers") SECTION("binary_t type") { json j = json::binary_array({1, 2, 3, 4}); - json::binary_t s = {1, 2, 3, 4}; + json::binary_t s = {{5, 6, 7, 8}}; j.swap(s); - CHECK(j == json::binary_array({1, 2, 3, 4})); + CHECK(j == json::binary_array({5, 6, 7, 8})); j.swap(s); CHECK(j == json::binary_array({1, 2, 3, 4})); } - SECTION("non-string_t type") + SECTION("binary_t::container_type type") + { + json j = json::binary_array({1, 2, 3, 4}); + std::vector s = {{5, 6, 7, 8}}; + + j.swap(s); + + CHECK(j == json::binary_array({5, 6, 7, 8})); + + j.swap(s); + + CHECK(j == json::binary_array({1, 2, 3, 4})); + } + + SECTION("non-binary_t type") { json j = 17; - json::binary_t s = {1, 2, 3, 4}; + json::binary_t s = {{1, 2, 3, 4}}; CHECK_THROWS_AS(j.swap(s), json::type_error&); CHECK_THROWS_WITH(j.swap(s), "[json.exception.type_error.310] cannot use swap() with number"); diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index 40b63735..acdafaa8 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -1134,7 +1134,7 @@ TEST_CASE("MessagePack") const auto s = std::vector(N, 'x'); json j = json::binary_array(s); std::uint8_t subtype = 42; - j.set_subtype(subtype); + j.get_binary().set_subtype(subtype); // create expected byte vector std::vector expected; @@ -1209,7 +1209,7 @@ TEST_CASE("MessagePack") const auto s = std::vector(N, 'x'); json j = json::binary_array(s); std::uint8_t subtype = 42; - j.set_subtype(subtype); + j.get_binary().set_subtype(subtype); // create expected byte vector (hack: create string first) std::vector expected(N, 'x'); @@ -1245,7 +1245,7 @@ TEST_CASE("MessagePack") const auto s = std::vector(N, 'x'); json j = json::binary_array(s); std::uint8_t subtype = 42; - j.set_subtype(subtype); + j.get_binary().set_subtype(subtype); // create expected byte vector (hack: create string first) std::vector expected(N, 'x'); diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp index 5f3f9e31..72ee1ab7 100644 --- a/test/src/unit-pointer_access.cpp +++ b/test/src/unit-pointer_access.cpp @@ -60,7 +60,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const object_t") @@ -89,7 +89,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to array_t") @@ -118,7 +118,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const array_t") @@ -147,7 +147,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to string_t") @@ -176,7 +176,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const string_t") @@ -205,7 +205,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to boolean_t") @@ -234,7 +234,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const boolean_t") @@ -263,7 +263,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to number_integer_t") @@ -292,7 +292,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const number_integer_t") @@ -321,7 +321,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to number_unsigned_t") @@ -350,7 +350,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const number_unsigned_t") @@ -379,7 +379,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to number_float_t") @@ -408,7 +408,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const number_float_t") @@ -437,12 +437,12 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); - CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } - SECTION("pointer access to const internal_binary_t") + SECTION("pointer access to const binary_t") { - using test_type = const json::internal_binary_t; + using test_type = const json::binary_t; const json value = json::binary_array({1, 2, 3}); // check if pointers are returned correctly @@ -466,12 +466,12 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() != nullptr); } - SECTION("pointer access to const internal_binary_t") + SECTION("pointer access to const binary_t") { - using test_type = const json::internal_binary_t; + using test_type = const json::binary_t; const json value = json::binary_array({}); // check if pointers are returned correctly @@ -495,6 +495,6 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); - CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() != nullptr); } } From 9eb19bcc279623c56547ee1e51b39b13d013c4fc Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 18 May 2020 12:33:26 +0200 Subject: [PATCH 07/12] :white_check_mark: add more tests for binary type --- test/src/unit-conversions.cpp | 119 +++++++++++++++++++++++++++++++++- test/src/unit-modifiers.cpp | 7 +- test/src/unit-udt.cpp | 7 ++ 3 files changed, 129 insertions(+), 4 deletions(-) diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 8477cf63..d37e8e9b 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -189,7 +189,6 @@ TEST_CASE("value conversion") } } - SECTION("get an object (implicit)") { json::object_t o_reference = {{"object", json::object()}, @@ -1259,6 +1258,124 @@ TEST_CASE("value conversion") } } + SECTION("get a binary value (explicit)") + { + json::binary_t n_reference{{1, 2, 3}}; + json j(n_reference); + + SECTION("binary_t") + { + json::binary_t b = j.get(); + CHECK(*json(b).m_value.binary == *j.m_value.binary); + } + + SECTION("get_binary()") + { + SECTION("non-const") + { + auto& b = j.get_binary(); + CHECK(*json(b).m_value.binary == *j.m_value.binary); + } + + SECTION("non-const") + { + const json j_const = j; + const auto& b = j_const.get_binary(); + CHECK(*json(b).m_value.binary == *j.m_value.binary); + } + } + + SECTION("exception in case of a non-string type") + { + json j_null(json::value_t::null); + json j_object(json::value_t::object); + json j_array(json::value_t::array); + json j_string(json::value_t::string); + json j_boolean(json::value_t::boolean); + const json j_null_const(json::value_t::null); + const json j_object_const(json::value_t::object); + const json j_array_const(json::value_t::array); + const json j_string_const(json::value_t::string); + const json j_boolean_const(json::value_t::boolean); + + CHECK_THROWS_WITH_AS(j_null.get(), + "[json.exception.type_error.302] type must be binary, but is null", + json::type_error&); + CHECK_THROWS_WITH_AS(j_object.get(), + "[json.exception.type_error.302] type must be binary, but is object", + json::type_error&); + CHECK_THROWS_WITH_AS(j_array.get(), + "[json.exception.type_error.302] type must be binary, but is array", + json::type_error&); + CHECK_THROWS_WITH_AS(j_string.get(), + "[json.exception.type_error.302] type must be binary, but is string", + json::type_error&); + CHECK_THROWS_WITH_AS(j_boolean.get(), + "[json.exception.type_error.302] type must be binary, but is boolean", + json::type_error&); + + CHECK_THROWS_WITH_AS(j_null_const.get(), + "[json.exception.type_error.302] type must be binary, but is null", + json::type_error&); + CHECK_THROWS_WITH_AS(j_object_const.get(), + "[json.exception.type_error.302] type must be binary, but is object", + json::type_error&); + CHECK_THROWS_WITH_AS(j_array_const.get(), + "[json.exception.type_error.302] type must be binary, but is array", + json::type_error&); + CHECK_THROWS_WITH_AS(j_string_const.get(), + "[json.exception.type_error.302] type must be binary, but is string", + json::type_error&); + CHECK_THROWS_WITH_AS(j_boolean_const.get(), + "[json.exception.type_error.302] type must be binary, but is boolean", + json::type_error&); + + CHECK_THROWS_WITH_AS(j_null.get_binary(), + "[json.exception.type_error.302] type must be binary, but is null", + json::type_error&); + CHECK_THROWS_WITH_AS(j_object.get_binary(), + "[json.exception.type_error.302] type must be binary, but is object", + json::type_error&); + CHECK_THROWS_WITH_AS(j_array.get_binary(), + "[json.exception.type_error.302] type must be binary, but is array", + json::type_error&); + CHECK_THROWS_WITH_AS(j_string.get_binary(), + "[json.exception.type_error.302] type must be binary, but is string", + json::type_error&); + CHECK_THROWS_WITH_AS(j_boolean.get_binary(), + "[json.exception.type_error.302] type must be binary, but is boolean", + json::type_error&); + + CHECK_THROWS_WITH_AS(j_null_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is null", + json::type_error&); + CHECK_THROWS_WITH_AS(j_object_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is object", + json::type_error&); + CHECK_THROWS_WITH_AS(j_array_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is array", + json::type_error&); + CHECK_THROWS_WITH_AS(j_string_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is string", + json::type_error&); + CHECK_THROWS_WITH_AS(j_boolean_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is boolean", + json::type_error&); + } + } + + SECTION("get a binary value (implicit)") + { + json::binary_t n_reference{{1, 2, 3}}; + json j(n_reference); + + SECTION("binary_t") + { + json::binary_t b = j; + CHECK(*json(b).m_value.binary == *j.m_value.binary); + } + } + SECTION("get an enum") { enum c_enum { value_1, value_2 }; diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index fc290162..955051a6 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -996,10 +996,11 @@ TEST_CASE("modifiers") SECTION("non-binary_t type") { json j = 17; - json::binary_t s = {{1, 2, 3, 4}}; + json::binary_t s1 = {{1, 2, 3, 4}}; + std::vector s2 = {{5, 6, 7, 8}}; - CHECK_THROWS_AS(j.swap(s), json::type_error&); - CHECK_THROWS_WITH(j.swap(s), "[json.exception.type_error.310] cannot use swap() with number"); + CHECK_THROWS_WITH_AS(j.swap(s1), "[json.exception.type_error.310] cannot use swap() with number", json::type_error); + CHECK_THROWS_WITH_AS(j.swap(s2), "[json.exception.type_error.310] cannot use swap() with number", json::type_error); } } } diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index cd9ce69f..42b1375e 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -763,6 +763,13 @@ TEST_CASE("different basic_json types conversions") CHECK(cj == "forty-two"); } + SECTION("binary") + { + json j = json::binary_array({1, 2, 3}); + custom_json cj = j; + CHECK(cj.get_binary() == j.get_binary()); + } + SECTION("object") { json j = {{"forty", "two"}}; From b7ff40029a68ba33a18258cc18aa9c40fbff259a Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 18 May 2020 13:53:20 +0200 Subject: [PATCH 08/12] :boom: change serialization of binary values --- include/nlohmann/detail/output/serializer.hpp | 101 +++++++++---- include/nlohmann/json.hpp | 21 ++- single_include/nlohmann/json.hpp | 122 ++++++++++----- test/src/unit-cbor.cpp | 2 +- test/src/unit-regression.cpp | 3 +- test/src/unit-serialization.cpp | 140 +++++++++++++----- 6 files changed, 273 insertions(+), 116 deletions(-) diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index dc963f53..e8f31fd8 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -84,19 +84,22 @@ class serializer - strings and object keys are escaped using `escape_string()` - integer numbers are converted implicitly via `operator<<` - floating-point numbers are converted to a string using `"%g"` format - - if specified to, binary values are output using the syntax `b[]`, otherwise an exception is thrown + - binary values are serialized as objects containing the subtype and the + byte array @param[in] val value to serialize @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. @param[in] indent_step the indent level @param[in] current_indent the current indent level (only used internally) - @param[in] serialize_binary whether the output shall include non-standard binary output */ - void dump(const BasicJsonType& val, const bool pretty_print, + void dump(const BasicJsonType& val, + const bool pretty_print, const bool ensure_ascii, const unsigned int indent_step, - const unsigned int current_indent = 0, - const bool serialize_binary = false) + const unsigned int current_indent = 0) { switch (val.m_type) { @@ -127,7 +130,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\": ", 3); - dump(i->second, true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(i->second, true, ensure_ascii, indent_step, new_indent); o->write_characters(",\n", 2); } @@ -138,7 +141,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\": ", 3); - dump(i->second, true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(i->second, true, ensure_ascii, indent_step, new_indent); o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); @@ -155,7 +158,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); - dump(i->second, false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(i->second, false, ensure_ascii, indent_step, current_indent); o->write_character(','); } @@ -165,7 +168,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); - dump(i->second, false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(i->second, false, ensure_ascii, indent_step, current_indent); o->write_character('}'); } @@ -197,14 +200,14 @@ class serializer i != val.m_value.array->cend() - 1; ++i) { o->write_characters(indent_string.c_str(), new_indent); - dump(*i, true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(*i, true, ensure_ascii, indent_step, new_indent); o->write_characters(",\n", 2); } // last element assert(not val.m_value.array->empty()); o->write_characters(indent_string.c_str(), new_indent); - dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); @@ -218,13 +221,13 @@ class serializer for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) { - dump(*i, false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(*i, false, ensure_ascii, indent_step, current_indent); o->write_character(','); } // last element assert(not val.m_value.array->empty()); - dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); o->write_character(']'); } @@ -242,27 +245,73 @@ class serializer case value_t::binary: { - if (not serialize_binary) + if (pretty_print) { - JSON_THROW(type_error::create(317, "cannot serialize binary data to text JSON")); - } + o->write_characters("{\n", 2); - if (val.m_value.binary->empty()) - { - o->write_characters("b[]", 3); + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (not val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); } else { - o->write_characters("b[", 2); - for (auto i = val.m_value.binary->cbegin(); - i != val.m_value.binary->cend() - 1; ++i) + o->write_characters("{\"bytes\":[", 10); + + if (not val.m_value.binary->empty()) { - dump_integer(*i); - o->write_character(','); + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_value.binary->back()); } - dump_integer(val.m_value.binary->back()); - o->write_character(']'); + o->write_characters("],\"subtype\":", 12); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + o->write_character('}'); + } + else + { + o->write_characters("null}", 5); + } } return; } diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index fc849a38..fc4d8da1 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2234,16 +2234,15 @@ class basic_json possible values: `strict` (throws and exception in case a decoding error occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD), and `ignore` (ignore invalid UTF-8 sequences during serialization). - @param[in] serialize_binary Whether or not to allow serialization of binary - types to JSON. Because binary types are non-standard, this will produce - non-conformant JSON, and is disabled by default. This flag is primarily - useful for debugging. Will output the binary value as a list of 8-bit - numbers prefixed by "b" (e.g. "bindata" = b[3, 0, 42, 255]). @return string containing the serialization of the JSON value @throw type_error.316 if a string stored inside the JSON value is not - UTF-8 encoded + UTF-8 encoded and @a error_handler is set to strict + + @note Binary values are serialized as object containing two keys: + - "bytes": an array of bytes as integers + - "subtype": the subtype as integer or "null" if the binary has no subtype @complexity Linear. @@ -2258,24 +2257,24 @@ class basic_json @since version 1.0.0; indentation character @a indent_char, option @a ensure_ascii and exceptions added in version 3.0.0; error - handlers added in version 3.4.0. + handlers added in version 3.4.0; serialization of binary values added + in version 3.8.0. */ string_t dump(const int indent = -1, const char indent_char = ' ', const bool ensure_ascii = false, - const error_handler_t error_handler = error_handler_t::strict, - const bool serialize_binary = false) const + const error_handler_t error_handler = error_handler_t::strict) const { string_t result; serializer s(detail::output_adapter(result), indent_char, error_handler); if (indent >= 0) { - s.dump(*this, true, ensure_ascii, static_cast(indent), 0, serialize_binary); + s.dump(*this, true, ensure_ascii, static_cast(indent)); } else { - s.dump(*this, false, ensure_ascii, 0, 0, serialize_binary); + s.dump(*this, false, ensure_ascii, 0); } return result; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 0033be23..c94336e2 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -14828,19 +14828,22 @@ class serializer - strings and object keys are escaped using `escape_string()` - integer numbers are converted implicitly via `operator<<` - floating-point numbers are converted to a string using `"%g"` format - - if specified to, binary values are output using the syntax `b[]`, otherwise an exception is thrown + - binary values are serialized as objects containing the subtype and the + byte array @param[in] val value to serialize @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. @param[in] indent_step the indent level @param[in] current_indent the current indent level (only used internally) - @param[in] serialize_binary whether the output shall include non-standard binary output */ - void dump(const BasicJsonType& val, const bool pretty_print, + void dump(const BasicJsonType& val, + const bool pretty_print, const bool ensure_ascii, const unsigned int indent_step, - const unsigned int current_indent = 0, - const bool serialize_binary = false) + const unsigned int current_indent = 0) { switch (val.m_type) { @@ -14871,7 +14874,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\": ", 3); - dump(i->second, true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(i->second, true, ensure_ascii, indent_step, new_indent); o->write_characters(",\n", 2); } @@ -14882,7 +14885,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\": ", 3); - dump(i->second, true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(i->second, true, ensure_ascii, indent_step, new_indent); o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); @@ -14899,7 +14902,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); - dump(i->second, false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(i->second, false, ensure_ascii, indent_step, current_indent); o->write_character(','); } @@ -14909,7 +14912,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); - dump(i->second, false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(i->second, false, ensure_ascii, indent_step, current_indent); o->write_character('}'); } @@ -14941,14 +14944,14 @@ class serializer i != val.m_value.array->cend() - 1; ++i) { o->write_characters(indent_string.c_str(), new_indent); - dump(*i, true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(*i, true, ensure_ascii, indent_step, new_indent); o->write_characters(",\n", 2); } // last element assert(not val.m_value.array->empty()); o->write_characters(indent_string.c_str(), new_indent); - dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); @@ -14962,13 +14965,13 @@ class serializer for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) { - dump(*i, false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(*i, false, ensure_ascii, indent_step, current_indent); o->write_character(','); } // last element assert(not val.m_value.array->empty()); - dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); o->write_character(']'); } @@ -14986,27 +14989,73 @@ class serializer case value_t::binary: { - if (not serialize_binary) + if (pretty_print) { - JSON_THROW(type_error::create(317, "cannot serialize binary data to text JSON")); - } + o->write_characters("{\n", 2); - if (val.m_value.binary->empty()) - { - o->write_characters("b[]", 3); + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (not val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); } else { - o->write_characters("b[", 2); - for (auto i = val.m_value.binary->cbegin(); - i != val.m_value.binary->cend() - 1; ++i) + o->write_characters("{\"bytes\":[", 10); + + if (not val.m_value.binary->empty()) { - dump_integer(*i); - o->write_character(','); + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_value.binary->back()); } - dump_integer(val.m_value.binary->back()); - o->write_character(']'); + o->write_characters("],\"subtype\":", 12); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + o->write_character('}'); + } + else + { + o->write_characters("null}", 5); + } } return; } @@ -17963,16 +18012,15 @@ class basic_json possible values: `strict` (throws and exception in case a decoding error occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD), and `ignore` (ignore invalid UTF-8 sequences during serialization). - @param[in] serialize_binary Whether or not to allow serialization of binary - types to JSON. Because binary types are non-standard, this will produce - non-conformant JSON, and is disabled by default. This flag is primarily - useful for debugging. Will output the binary value as a list of 8-bit - numbers prefixed by "b" (e.g. "bindata" = b[3, 0, 42, 255]). @return string containing the serialization of the JSON value @throw type_error.316 if a string stored inside the JSON value is not - UTF-8 encoded + UTF-8 encoded and @a error_handler is set to strict + + @note Binary values are serialized as object containing two keys: + - "bytes": an array of bytes as integers + - "subtype": the subtype as integer or "null" if the binary has no subtype @complexity Linear. @@ -17987,24 +18035,24 @@ class basic_json @since version 1.0.0; indentation character @a indent_char, option @a ensure_ascii and exceptions added in version 3.0.0; error - handlers added in version 3.4.0. + handlers added in version 3.4.0; serialization of binary values added + in version 3.8.0. */ string_t dump(const int indent = -1, const char indent_char = ' ', const bool ensure_ascii = false, - const error_handler_t error_handler = error_handler_t::strict, - const bool serialize_binary = false) const + const error_handler_t error_handler = error_handler_t::strict) const { string_t result; serializer s(detail::output_adapter(result), indent_char, error_handler); if (indent >= 0) { - s.dump(*this, true, ensure_ascii, static_cast(indent), 0, serialize_binary); + s.dump(*this, true, ensure_ascii, static_cast(indent)); } else { - s.dump(*this, false, ensure_ascii, 0, 0, serialize_binary); + s.dump(*this, false, ensure_ascii, 0); } return result; diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 2a53bb52..d34ae789 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -1582,7 +1582,7 @@ TEST_CASE("CBOR") auto j = json::from_cbor(input); CHECK(j.is_binary()); auto k = json::binary_array({0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x99}); - CAPTURE(j.dump(0, ' ', false, json::error_handler_t::strict, true)) + CAPTURE(j.dump(0, ' ', false, json::error_handler_t::strict)) CHECK(j == k); } diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index c191628a..373344d8 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -1919,8 +1919,7 @@ TEST_CASE("regression tests") j.dump(4, // Indent ' ', // Indent char false, // Ensure ascii - json::error_handler_t::strict, // Error - true // Allow binary data + json::error_handler_t::strict // Error ) ); } diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp index 2681ab53..d0ddc58f 100644 --- a/test/src/unit-serialization.cpp +++ b/test/src/unit-serialization.cpp @@ -209,52 +209,114 @@ TEST_CASE_TEMPLATE("serialization for extreme integer values", T, int32_t, uint3 TEST_CASE("dump with binary values") { - SECTION("serialize_binary = false") - { - auto binary = json::binary_array({1, 2, 3, 4}); - auto binary_empty = json::binary_array({}); - json object = {{"key", binary}}; - json array = {"value", 1, binary}; + auto binary = json::binary_array({1, 2, 3, 4}); + auto binary_empty = json::binary_array({}); + auto binary_with_subtype = json::binary_array({1, 2, 3, 4}, 128); + auto binary_empty_with_subtype = json::binary_array({}, 128); - CHECK_THROWS_AS(binary.dump(), json::type_error); - CHECK_THROWS_AS(binary_empty.dump(), json::type_error); - CHECK_THROWS_AS(object.dump(), json::type_error); - CHECK_THROWS_AS(array.dump(), json::type_error); - CHECK_THROWS_WITH(binary.dump(), "[json.exception.type_error.317] cannot serialize binary data to text JSON"); - CHECK_THROWS_WITH(binary_empty.dump(), "[json.exception.type_error.317] cannot serialize binary data to text JSON"); - CHECK_THROWS_WITH(object.dump(), "[json.exception.type_error.317] cannot serialize binary data to text JSON"); - CHECK_THROWS_WITH(array.dump(), "[json.exception.type_error.317] cannot serialize binary data to text JSON"); + json object = {{"key", binary}}; + json object_empty = {{"key", binary_empty}}; + json object_with_subtype = {{"key", binary_with_subtype}}; + json object_empty_with_subtype = {{"key", binary_empty_with_subtype}}; + + json array = {"value", 1, binary}; + json array_empty = {"value", 1, binary_empty}; + json array_with_subtype = {"value", 1, binary_with_subtype}; + json array_empty_with_subtype = {"value", 1, binary_empty_with_subtype}; + + SECTION("normal") + { + CHECK(binary.dump() == "{\"bytes\":[1,2,3,4],\"subtype\":null}"); + CHECK(binary_empty.dump() == "{\"bytes\":[],\"subtype\":null}"); + CHECK(binary_with_subtype.dump() == "{\"bytes\":[1,2,3,4],\"subtype\":128}"); + CHECK(binary_empty_with_subtype.dump() == "{\"bytes\":[],\"subtype\":128}"); + + CHECK(object.dump() == "{\"key\":{\"bytes\":[1,2,3,4],\"subtype\":null}}"); + CHECK(object_empty.dump() == "{\"key\":{\"bytes\":[],\"subtype\":null}}"); + CHECK(object_with_subtype.dump() == "{\"key\":{\"bytes\":[1,2,3,4],\"subtype\":128}}"); + CHECK(object_empty_with_subtype.dump() == "{\"key\":{\"bytes\":[],\"subtype\":128}}"); + + CHECK(array.dump() == "[\"value\",1,{\"bytes\":[1,2,3,4],\"subtype\":null}]"); + CHECK(array_empty.dump() == "[\"value\",1,{\"bytes\":[],\"subtype\":null}]"); + CHECK(array_with_subtype.dump() == "[\"value\",1,{\"bytes\":[1,2,3,4],\"subtype\":128}]"); + CHECK(array_empty_with_subtype.dump() == "[\"value\",1,{\"bytes\":[],\"subtype\":128}]"); } - SECTION("serialize_binary = true") + SECTION("pretty-printed") { - auto binary = json::binary_array({1, 2, 3, 4}); - auto binary_empty = json::binary_array({}); - json object = {{"key", binary}}; - json array = {"value", 1, binary}; - - CHECK(binary.dump(-1, ' ', false, json::error_handler_t::strict, true) == "b[1,2,3,4]"); - CHECK(binary_empty.dump(-1, ' ', false, json::error_handler_t::strict, true) == "b[]"); - CHECK(object.dump(-1, ' ', false, json::error_handler_t::strict, true) == "{\"key\":b[1,2,3,4]}"); - CHECK(array.dump(-1, ' ', false, json::error_handler_t::strict, true) == "[\"value\",1,b[1,2,3,4]]"); - } - - SECTION("serialize_binary = true, pretty-printed") - { - auto binary = json::binary_array({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}); - auto binary_empty = json::binary_array({}); - json object = {{"key", binary}}; - json array = {"value", 1, binary}; - - CHECK(binary.dump(4, ' ', false, json::error_handler_t::strict, true) == "b[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]"); - CHECK(binary_empty.dump(4, ' ', false, json::error_handler_t::strict, true) == "b[]"); - CHECK(object.dump(4, ' ', false, json::error_handler_t::strict, true) == "{\n" - " \"key\": b[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]\n" + CHECK(binary.dump(4) == "{\n" + " \"bytes\": [1, 2, 3, 4],\n" + " \"subtype\": null\n" "}"); - CHECK(array.dump(4, ' ', false, json::error_handler_t::strict, true) == "[\n" + CHECK(binary_empty.dump(4) == "{\n" + " \"bytes\": [],\n" + " \"subtype\": null\n" + "}"); + CHECK(binary_with_subtype.dump(4) == "{\n" + " \"bytes\": [1, 2, 3, 4],\n" + " \"subtype\": 128\n" + "}"); + CHECK(binary_empty_with_subtype.dump(4) == "{\n" + " \"bytes\": [],\n" + " \"subtype\": 128\n" + "}"); + + CHECK(object.dump(4) == "{\n" + " \"key\": {\n" + " \"bytes\": [1, 2, 3, 4],\n" + " \"subtype\": null\n" + " }\n" + "}"); + CHECK(object_empty.dump(4) == "{\n" + " \"key\": {\n" + " \"bytes\": [],\n" + " \"subtype\": null\n" + " }\n" + "}"); + CHECK(object_with_subtype.dump(4) == "{\n" + " \"key\": {\n" + " \"bytes\": [1, 2, 3, 4],\n" + " \"subtype\": 128\n" + " }\n" + "}"); + CHECK(object_empty_with_subtype.dump(4) == "{\n" + " \"key\": {\n" + " \"bytes\": [],\n" + " \"subtype\": 128\n" + " }\n" + "}"); + + CHECK(array.dump(4) == "[\n" " \"value\",\n" " 1,\n" - " b[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]\n" + " {\n" + " \"bytes\": [1, 2, 3, 4],\n" + " \"subtype\": null\n" + " }\n" + "]"); + CHECK(array_empty.dump(4) == "[\n" + " \"value\",\n" + " 1,\n" + " {\n" + " \"bytes\": [],\n" + " \"subtype\": null\n" + " }\n" + "]"); + CHECK(array_with_subtype.dump(4) == "[\n" + " \"value\",\n" + " 1,\n" + " {\n" + " \"bytes\": [1, 2, 3, 4],\n" + " \"subtype\": 128\n" + " }\n" + "]"); + CHECK(array_empty_with_subtype.dump(4) == "[\n" + " \"value\",\n" + " 1,\n" + " {\n" + " \"bytes\": [],\n" + " \"subtype\": 128\n" + " }\n" "]"); } } From 79347b484b9df853ba13b34e7f8b438bee9c2f46 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 19 May 2020 12:40:32 +0200 Subject: [PATCH 09/12] :white_check_mark: improve test coverage --- test/src/unit-inspection.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp index c464b698..d14b89af 100644 --- a/test/src/unit-inspection.cpp +++ b/test/src/unit-inspection.cpp @@ -261,6 +261,9 @@ TEST_CASE("object inspection") // important test, because it yields a resize of the indent_string // inside the dump() function CHECK(j.dump(1024).size() == 15472); + + const auto binary = json::binary_array({1, 2, 3}, 128); + CHECK(binary.dump(1024).size() == 2086); } SECTION("dump and floating-point numbers") From 952a87a4f473d3c5dc676e40a04cde2697f4615c Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 19 May 2020 13:08:18 +0200 Subject: [PATCH 10/12] :truck: move byte container outside detail namespace --- doc/examples/swap__binary_t.cpp | 20 + doc/examples/swap__binary_t.output | 2 + ..._t.hpp => byte_container_with_subtype.hpp} | 29 +- include/nlohmann/json.hpp | 65 +-- single_include/nlohmann/json.hpp | 374 +++++++++--------- 5 files changed, 264 insertions(+), 226 deletions(-) create mode 100644 doc/examples/swap__binary_t.cpp create mode 100644 doc/examples/swap__binary_t.output rename include/nlohmann/{detail/wrapped_binary_t.hpp => byte_container_with_subtype.hpp} (76%) diff --git a/doc/examples/swap__binary_t.cpp b/doc/examples/swap__binary_t.cpp new file mode 100644 index 00000000..48fe9566 --- /dev/null +++ b/doc/examples/swap__binary_t.cpp @@ -0,0 +1,20 @@ +#include +#include + +using json = nlohmann::json; + +int main() +{ + // create a binary value + json value = json::binary_array({1, 2, 3}); + + // create a binary_t + json::binary_t binary = {{4, 5, 6}}; + + // swap the object stored in the JSON value + value.swap(binary); + + // output the values + std::cout << "value = " << value << '\n'; + std::cout << "binary = " << json(binary) << '\n'; +} diff --git a/doc/examples/swap__binary_t.output b/doc/examples/swap__binary_t.output new file mode 100644 index 00000000..68cd7689 --- /dev/null +++ b/doc/examples/swap__binary_t.output @@ -0,0 +1,2 @@ +value = {"bytes":[4,5,6],"subtype":null} +binary = {"bytes":[1,2,3],"subtype":null} diff --git a/include/nlohmann/detail/wrapped_binary_t.hpp b/include/nlohmann/byte_container_with_subtype.hpp similarity index 76% rename from include/nlohmann/detail/wrapped_binary_t.hpp rename to include/nlohmann/byte_container_with_subtype.hpp index 6edd7b52..243ece37 100644 --- a/include/nlohmann/detail/wrapped_binary_t.hpp +++ b/include/nlohmann/byte_container_with_subtype.hpp @@ -5,43 +5,47 @@ namespace nlohmann { -namespace detail -{ /*! @brief an internal type for a backed binary type -This type is designed to be `binary_t` but with the subtype implementation -detail. This type exists so that the user does not have to specify a type -themselves with a specific naming scheme in order to override the binary type. +This type extends the template parameter @a BinaryType provided to `basic_json` +with a subtype used by BSON and MessagePack. This type exists so that the user +does not have to specify a type themselves with a specific naming scheme in +order to override the binary type. + +@tparam BinaryType container to store bytes (`std::vector` by + default) + +@since version 3.8.0 */ template -class wrapped_binary_t : public BinaryType +class byte_container_with_subtype : public BinaryType { public: /// the type of the underlying container using container_type = BinaryType; - wrapped_binary_t() noexcept(noexcept(container_type())) + byte_container_with_subtype() noexcept(noexcept(container_type())) : container_type() {} - wrapped_binary_t(const container_type& b) noexcept(noexcept(container_type(b))) + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) : container_type(b) {} - wrapped_binary_t(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) : container_type(std::move(b)) {} - wrapped_binary_t(const container_type& b, - std::uint8_t subtype) noexcept(noexcept(container_type(b))) + byte_container_with_subtype(const container_type& b, + std::uint8_t subtype) noexcept(noexcept(container_type(b))) : container_type(b) , m_subtype(subtype) , m_has_subtype(true) {} - wrapped_binary_t(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b)))) + byte_container_with_subtype(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b)))) : container_type(std::move(b)) , m_subtype(subtype) , m_has_subtype(true) @@ -148,5 +152,4 @@ class wrapped_binary_t : public BinaryType bool m_has_subtype = false; }; -} // namespace detail } // namespace nlohmann diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index fc4d8da1..38f8f203 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -48,6 +48,7 @@ SOFTWARE. #include // vector #include +#include #include #include #include @@ -70,7 +71,6 @@ SOFTWARE. #include #include #include -#include #include /*! @@ -903,7 +903,7 @@ class basic_json @since version 3.8.0 */ - using binary_t = nlohmann::detail::wrapped_binary_t; + using binary_t = nlohmann::byte_container_with_subtype; /// @} private: @@ -946,7 +946,7 @@ class basic_json number | number_integer | @ref number_integer_t number | number_unsigned | @ref number_unsigned_t number | number_float | @ref number_float_t - binary | binary | pointer to @ref internal_binary_t + binary | binary | pointer to @ref binary_t null | null | *no value is stored* @note Variable-length types (objects, arrays, and strings) are stored as @@ -1645,22 +1645,22 @@ class basic_json } /*! - @brief explicitly create a binary array from an already constructed copy of - its base type + @brief explicitly create a binary array (without subtype) - Creates a JSON binary array value from a given `binary_t`. Binary values are - part of various binary formats, such as CBOR, MsgPack, and BSON. And this - constructor is used to create a value for serialization to those formats. + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. @note Note, this function exists because of the difficulty in correctly specifying the correct template overload in the standard value ctor, as both JSON arrays and JSON binary arrays are backed with some form of a - `std::vector`. Because JSON binary arrays are a non-standard extension it + `std::vector`. Because JSON binary arrays are a non-standard extension it was decided that it would be best to prevent automatic initialization of a binary array type, for backwards compatibility and so it does not happen on accident. - @param[in] init `binary_t` with JSON values to create a binary array from + @param[in] init container containing bytes to use as binary type @return JSON binary array value @@ -1680,32 +1680,24 @@ class basic_json return res; } - JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(const typename binary_t::container_type& init, std::uint8_t subtype) - { - auto res = basic_json(); - res.m_type = value_t::binary; - res.m_value = binary_t(init, subtype); - return res; - } - /*! - @brief explicitly create a binary array from an already constructed rvalue - copy of its base type + @brief explicitly create a binary array (with subtype) - Creates a JSON binary array value from a given `binary_t`. Binary values are - part of various binary formats, such as CBOR, MsgPack, and BSON. And this - constructor is used to create a value for serialization to those formats. + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. @note Note, this function exists because of the difficulty in correctly specifying the correct template overload in the standard value ctor, as both JSON arrays and JSON binary arrays are backed with some form of a - `std::vector`. Because JSON binary arrays are a non-standard extension it + `std::vector`. Because JSON binary arrays are a non-standard extension it was decided that it would be best to prevent automatic initialization of a - binary array type, for backwards compatibility and so it doesn't happen on + binary array type, for backwards compatibility and so it does not happen on accident. - @param[in] init `binary_t` with JSON values to create a binary array from + @param[in] init container containing bytes to use as binary type + @param[in] subtype subtype to use in MessagePack and BSON @return JSON binary array value @@ -1717,6 +1709,16 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary_array(const typename binary_t::container_type& init, std::uint8_t subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); + return res; + } + + /// @copydoc binary_array(const typename binary_t::container_type&) + JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json binary_array(typename binary_t::container_type&& init) { auto res = basic_json(); @@ -1725,6 +1727,7 @@ class basic_json return res; } + /// @copydoc binary_array(const typename binary_t::container_type&, std::uint8_t) JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json binary_array(typename binary_t::container_type&& init, std::uint8_t subtype) { @@ -6959,7 +6962,8 @@ class basic_json number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B - number_float | *any value* | Double-Precision Float | 0xFB + number_float | *any value representable by a float* | Single-Precision Float | 0xFA + number_float | *any value NOT representable by a float* | Double-Precision Float | 0xFB string | *length*: 0..23 | UTF-8 string | 0x60..0x77 string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 @@ -7001,7 +7005,7 @@ class basic_json - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) - - half and single-precision floats (0xF9-0xFA) + - half-precision floats (0xF9) - break (0xFF) @param[in] j JSON value to serialize @@ -7019,7 +7023,8 @@ class basic_json @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the related UBJSON format - @since version 2.0.9 + @since version 2.0.9; compact representation of floating-point numbers + since version 3.8.0 */ static std::vector to_cbor(const basic_json& j) { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index c94336e2..f7743772 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4223,6 +4223,163 @@ struct adl_serializer } // namespace nlohmann +// #include + + +#include // uint8_t +#include // move + +namespace nlohmann +{ + +/*! +@brief an internal type for a backed binary type + +This type extends the template parameter @a BinaryType provided to `basic_json` +with a subtype used by BSON and MessagePack. This type exists so that the user +does not have to specify a type themselves with a specific naming scheme in +order to override the binary type. + +@tparam BinaryType container to store bytes (`std::vector` by + default) + +@since version 3.8.0 +*/ +template +class byte_container_with_subtype : public BinaryType +{ + public: + /// the type of the underlying container + using container_type = BinaryType; + + byte_container_with_subtype() noexcept(noexcept(container_type())) + : container_type() + {} + + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) + {} + + byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + {} + + byte_container_with_subtype(const container_type& b, + std::uint8_t subtype) noexcept(noexcept(container_type(b))) + : container_type(b) + , m_subtype(subtype) + , m_has_subtype(true) + {} + + byte_container_with_subtype(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + , m_subtype(subtype) + , m_has_subtype(true) + {} + + /*! + @brief sets the binary subtype + + Sets the binary subtype of the value, also flags a binary JSON value as + having a subtype, which has implications for serialization. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void set_subtype(std::uint8_t subtype) noexcept + { + m_subtype = subtype; + m_has_subtype = true; + } + + /*! + @brief return the binary subtype + + Returns the numerical subtype of the value if it has a subtype. If it does + not have a subtype, this function will return size_t(-1) as a sentinel + value. + + @return the numerical subtype of the binary value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + constexpr std::uint8_t subtype() const noexcept + { + return m_subtype; + } + + /*! + @brief return whether the value has a subtype + + @return whether the value has a subtype + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + + @since version 3.8.0 + */ + constexpr bool has_subtype() const noexcept + { + return m_has_subtype; + } + + /*! + @brief clears the binary subtype + + Clears the binary subtype and flags the value as not having a subtype, which + has implications for serialization; for instance MessagePack will prefer the + bin family over the ext family. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void clear_subtype() noexcept + { + m_subtype = 0; + m_has_subtype = false; + } + + private: + std::uint8_t m_subtype = 0; + bool m_has_subtype = false; +}; + +} // namespace nlohmann + // #include // #include @@ -15694,160 +15851,6 @@ class serializer // #include -// #include - - -#include // uint8_t -#include // move - -namespace nlohmann -{ -namespace detail -{ - -/*! -@brief an internal type for a backed binary type - -This type is designed to be `binary_t` but with the subtype implementation -detail. This type exists so that the user does not have to specify a type -themselves with a specific naming scheme in order to override the binary type. -*/ -template -class wrapped_binary_t : public BinaryType -{ - public: - /// the type of the underlying container - using container_type = BinaryType; - - wrapped_binary_t() noexcept(noexcept(container_type())) - : container_type() - {} - - wrapped_binary_t(const container_type& b) noexcept(noexcept(container_type(b))) - : container_type(b) - {} - - wrapped_binary_t(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) - : container_type(std::move(b)) - {} - - wrapped_binary_t(const container_type& b, - std::uint8_t subtype) noexcept(noexcept(container_type(b))) - : container_type(b) - , m_subtype(subtype) - , m_has_subtype(true) - {} - - wrapped_binary_t(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b)))) - : container_type(std::move(b)) - , m_subtype(subtype) - , m_has_subtype(true) - {} - - /*! - @brief sets the binary subtype - - Sets the binary subtype of the value, also flags a binary JSON value as - having a subtype, which has implications for serialization. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref subtype() -- return the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - void set_subtype(std::uint8_t subtype) noexcept - { - m_subtype = subtype; - m_has_subtype = true; - } - - /*! - @brief return the binary subtype - - Returns the numerical subtype of the value if it has a subtype. If it does - not have a subtype, this function will return size_t(-1) as a sentinel - value. - - @return the numerical subtype of the binary value - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - constexpr std::uint8_t subtype() const noexcept - { - return m_subtype; - } - - /*! - @brief return whether the value has a subtype - - @return whether the value has a subtype - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref subtype() -- return the binary subtype - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - - @since version 3.8.0 - */ - constexpr bool has_subtype() const noexcept - { - return m_has_subtype; - } - - /*! - @brief clears the binary subtype - - Clears the binary subtype and flags the value as not having a subtype, which - has implications for serialization; for instance MessagePack will prefer the - bin family over the ext family. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref subtype() -- return the binary subtype - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - void clear_subtype() noexcept - { - m_subtype = 0; - m_has_subtype = false; - } - - private: - std::uint8_t m_subtype = 0; - bool m_has_subtype = false; -}; - -} // namespace detail -} // namespace nlohmann - // #include @@ -16681,7 +16684,7 @@ class basic_json @since version 3.8.0 */ - using binary_t = nlohmann::detail::wrapped_binary_t; + using binary_t = nlohmann::byte_container_with_subtype; /// @} private: @@ -16724,7 +16727,7 @@ class basic_json number | number_integer | @ref number_integer_t number | number_unsigned | @ref number_unsigned_t number | number_float | @ref number_float_t - binary | binary | pointer to @ref internal_binary_t + binary | binary | pointer to @ref binary_t null | null | *no value is stored* @note Variable-length types (objects, arrays, and strings) are stored as @@ -17423,22 +17426,22 @@ class basic_json } /*! - @brief explicitly create a binary array from an already constructed copy of - its base type + @brief explicitly create a binary array (without subtype) - Creates a JSON binary array value from a given `binary_t`. Binary values are - part of various binary formats, such as CBOR, MsgPack, and BSON. And this - constructor is used to create a value for serialization to those formats. + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. @note Note, this function exists because of the difficulty in correctly specifying the correct template overload in the standard value ctor, as both JSON arrays and JSON binary arrays are backed with some form of a - `std::vector`. Because JSON binary arrays are a non-standard extension it + `std::vector`. Because JSON binary arrays are a non-standard extension it was decided that it would be best to prevent automatic initialization of a binary array type, for backwards compatibility and so it does not happen on accident. - @param[in] init `binary_t` with JSON values to create a binary array from + @param[in] init container containing bytes to use as binary type @return JSON binary array value @@ -17458,32 +17461,24 @@ class basic_json return res; } - JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(const typename binary_t::container_type& init, std::uint8_t subtype) - { - auto res = basic_json(); - res.m_type = value_t::binary; - res.m_value = binary_t(init, subtype); - return res; - } - /*! - @brief explicitly create a binary array from an already constructed rvalue - copy of its base type + @brief explicitly create a binary array (with subtype) - Creates a JSON binary array value from a given `binary_t`. Binary values are - part of various binary formats, such as CBOR, MsgPack, and BSON. And this - constructor is used to create a value for serialization to those formats. + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. @note Note, this function exists because of the difficulty in correctly specifying the correct template overload in the standard value ctor, as both JSON arrays and JSON binary arrays are backed with some form of a - `std::vector`. Because JSON binary arrays are a non-standard extension it + `std::vector`. Because JSON binary arrays are a non-standard extension it was decided that it would be best to prevent automatic initialization of a - binary array type, for backwards compatibility and so it doesn't happen on + binary array type, for backwards compatibility and so it does not happen on accident. - @param[in] init `binary_t` with JSON values to create a binary array from + @param[in] init container containing bytes to use as binary type + @param[in] subtype subtype to use in MessagePack and BSON @return JSON binary array value @@ -17495,6 +17490,16 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary_array(const typename binary_t::container_type& init, std::uint8_t subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); + return res; + } + + /// @copydoc binary_array(const typename binary_t::container_type&) + JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json binary_array(typename binary_t::container_type&& init) { auto res = basic_json(); @@ -17503,6 +17508,7 @@ class basic_json return res; } + /// @copydoc binary_array(const typename binary_t::container_type&, std::uint8_t) JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json binary_array(typename binary_t::container_type&& init, std::uint8_t subtype) { @@ -22737,7 +22743,8 @@ class basic_json number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B - number_float | *any value* | Double-Precision Float | 0xFB + number_float | *any value representable by a float* | Single-Precision Float | 0xFA + number_float | *any value NOT representable by a float* | Double-Precision Float | 0xFB string | *length*: 0..23 | UTF-8 string | 0x60..0x77 string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 @@ -22779,7 +22786,7 @@ class basic_json - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) - - half and single-precision floats (0xF9-0xFA) + - half-precision floats (0xF9) - break (0xFF) @param[in] j JSON value to serialize @@ -22797,7 +22804,8 @@ class basic_json @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the related UBJSON format - @since version 2.0.9 + @since version 2.0.9; compact representation of floating-point numbers + since version 3.8.0 */ static std::vector to_cbor(const basic_json& j) { From 21b1680ea18165d5ceb0854a2d27aa4e2b5a3e4e Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 19 May 2020 13:30:22 +0200 Subject: [PATCH 11/12] :truck: rename binary_array() to binary() --- .../nlohmann/byte_container_with_subtype.hpp | 12 +++++++++ include/nlohmann/json.hpp | 14 +++++----- single_include/nlohmann/json.hpp | 26 ++++++++++++++----- test/src/unit-bson.cpp | 4 +-- test/src/unit-cbor.cpp | 14 +++++----- test/src/unit-comparison.cpp | 2 +- test/src/unit-constructor1.cpp | 6 ++--- test/src/unit-constructor2.cpp | 4 +-- test/src/unit-element_access1.cpp | 8 +++--- test/src/unit-inspection.cpp | 4 +-- test/src/unit-modifiers.cpp | 16 ++++++------ test/src/unit-msgpack.cpp | 12 ++++----- test/src/unit-pointer_access.cpp | 4 +-- test/src/unit-serialization.cpp | 8 +++--- test/src/unit-ubjson.cpp | 10 +++---- test/src/unit-udt.cpp | 2 +- 16 files changed, 85 insertions(+), 61 deletions(-) diff --git a/include/nlohmann/byte_container_with_subtype.hpp b/include/nlohmann/byte_container_with_subtype.hpp index 243ece37..b2eef1b7 100644 --- a/include/nlohmann/byte_container_with_subtype.hpp +++ b/include/nlohmann/byte_container_with_subtype.hpp @@ -1,6 +1,7 @@ #pragma once #include // uint8_t +#include // tie #include // move namespace nlohmann @@ -51,6 +52,17 @@ class byte_container_with_subtype : public BinaryType , m_has_subtype(true) {} + bool operator==(const byte_container_with_subtype& rhs) const + { + return std::tie(static_cast(*this), m_subtype, m_has_subtype) == + std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); + } + + bool operator!=(const byte_container_with_subtype& rhs) const + { + return !(rhs == *this); + } + /*! @brief sets the binary subtype diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 38f8f203..df006694 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -899,7 +899,7 @@ class basic_json - If a subtype is given, it is used and added as unsigned 8-bit integer. - If no subtype is given, the generic binary subtype 0x00 is used. - @sa @ref binary_array -- create a binary array + @sa @ref binary -- create a binary array @since version 3.8.0 */ @@ -1672,7 +1672,7 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(const typename binary_t::container_type& init) + static basic_json binary(const typename binary_t::container_type& init) { auto res = basic_json(); res.m_type = value_t::binary; @@ -1709,7 +1709,7 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(const typename binary_t::container_type& init, std::uint8_t subtype) + static basic_json binary(const typename binary_t::container_type& init, std::uint8_t subtype) { auto res = basic_json(); res.m_type = value_t::binary; @@ -1717,9 +1717,9 @@ class basic_json return res; } - /// @copydoc binary_array(const typename binary_t::container_type&) + /// @copydoc binary(const typename binary_t::container_type&) JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(typename binary_t::container_type&& init) + static basic_json binary(typename binary_t::container_type&& init) { auto res = basic_json(); res.m_type = value_t::binary; @@ -1727,9 +1727,9 @@ class basic_json return res; } - /// @copydoc binary_array(const typename binary_t::container_type&, std::uint8_t) + /// @copydoc binary(const typename binary_t::container_type&, std::uint8_t) JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(typename binary_t::container_type&& init, std::uint8_t subtype) + static basic_json binary(typename binary_t::container_type&& init, std::uint8_t subtype) { auto res = basic_json(); res.m_type = value_t::binary; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f7743772..e8c7ecb9 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4227,6 +4227,7 @@ struct adl_serializer #include // uint8_t +#include // tie #include // move namespace nlohmann @@ -4277,6 +4278,17 @@ class byte_container_with_subtype : public BinaryType , m_has_subtype(true) {} + bool operator==(const byte_container_with_subtype& rhs) const + { + return std::tie(static_cast(*this), m_subtype, m_has_subtype) == + std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); + } + + bool operator!=(const byte_container_with_subtype& rhs) const + { + return !(rhs == *this); + } + /*! @brief sets the binary subtype @@ -16680,7 +16692,7 @@ class basic_json - If a subtype is given, it is used and added as unsigned 8-bit integer. - If no subtype is given, the generic binary subtype 0x00 is used. - @sa @ref binary_array -- create a binary array + @sa @ref binary -- create a binary array @since version 3.8.0 */ @@ -17453,7 +17465,7 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(const typename binary_t::container_type& init) + static basic_json binary(const typename binary_t::container_type& init) { auto res = basic_json(); res.m_type = value_t::binary; @@ -17490,7 +17502,7 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(const typename binary_t::container_type& init, std::uint8_t subtype) + static basic_json binary(const typename binary_t::container_type& init, std::uint8_t subtype) { auto res = basic_json(); res.m_type = value_t::binary; @@ -17498,9 +17510,9 @@ class basic_json return res; } - /// @copydoc binary_array(const typename binary_t::container_type&) + /// @copydoc binary(const typename binary_t::container_type&) JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(typename binary_t::container_type&& init) + static basic_json binary(typename binary_t::container_type&& init) { auto res = basic_json(); res.m_type = value_t::binary; @@ -17508,9 +17520,9 @@ class basic_json return res; } - /// @copydoc binary_array(const typename binary_t::container_type&, std::uint8_t) + /// @copydoc binary(const typename binary_t::container_type&, std::uint8_t) JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(typename binary_t::container_type&& init, std::uint8_t subtype) + static basic_json binary(typename binary_t::container_type&& init, std::uint8_t subtype) { auto res = basic_json(); res.m_type = value_t::binary; diff --git a/test/src/unit-bson.cpp b/test/src/unit-bson.cpp index 318286d1..2a17f180 100644 --- a/test/src/unit-bson.cpp +++ b/test/src/unit-bson.cpp @@ -499,7 +499,7 @@ TEST_CASE("BSON") const auto s = std::vector(N, 'x'); json j = { - { "entry", json::binary_array(s) } + { "entry", json::binary(s) } }; std::vector expected = @@ -529,7 +529,7 @@ TEST_CASE("BSON") const std::vector md5hash = {0xd7, 0x7e, 0x27, 0x54, 0xbe, 0x12, 0x37, 0xfe, 0xd6, 0x0c, 0x33, 0x98, 0x30, 0x3b, 0x8d, 0xc4}; json j = { - { "entry", json::binary_array(md5hash, 5) } + { "entry", json::binary(md5hash, 5) } }; std::vector expected = diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index d34ae789..d7b87255 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -1450,7 +1450,7 @@ TEST_CASE("CBOR") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected; @@ -1484,7 +1484,7 @@ TEST_CASE("CBOR") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected; @@ -1519,7 +1519,7 @@ TEST_CASE("CBOR") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector (hack: create string first) std::vector expected(N, 'x'); @@ -1552,7 +1552,7 @@ TEST_CASE("CBOR") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector (hack: create string first) std::vector expected(N, 'x'); @@ -1581,7 +1581,7 @@ TEST_CASE("CBOR") std::vector input = {0x5F, 0x44, 0xaa, 0xbb, 0xcc, 0xdd, 0x43, 0xee, 0xff, 0x99, 0xFF}; auto j = json::from_cbor(input); CHECK(j.is_binary()); - auto k = json::binary_array({0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x99}); + auto k = json::binary({0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x99}); CAPTURE(j.dump(0, ' ', false, json::error_handler_t::strict)) CHECK(j == k); } @@ -1633,7 +1633,7 @@ TEST_CASE("CBOR") 0x00, 0x00, 0x00, 0x01, 0x61 }; json j = json::from_cbor(given); - CHECK(j == json::binary_array(std::vector {'a'})); + CHECK(j == json::binary(std::vector {'a'})); } SECTION("0x7b (string)") @@ -2502,7 +2502,7 @@ TEST_CASE("examples from RFC 7049 Appendix A") std::ifstream f_bin(TEST_DATA_DIRECTORY "/binary_data/cbor_binary.out", std::ios::binary); std::vector expected((std::istreambuf_iterator(f_bin)), std::istreambuf_iterator()); - CHECK(j == json::binary_array(expected)); + CHECK(j == json::binary(expected)); } SECTION("arrays") diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp index 91fbe1b4..db46507c 100644 --- a/test/src/unit-comparison.cpp +++ b/test/src/unit-comparison.cpp @@ -101,7 +101,7 @@ TEST_CASE("lexicographical comparison operators") true, false, {1, 2, 3}, {"one", "two", "three"}, {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}, - json::binary_array({1, 2, 3}), json::binary_array({1, 2, 4}) + json::binary({1, 2, 3}), json::binary({1, 2, 4}) }; SECTION("comparison: equal") diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 81fca64a..0240feef 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -121,7 +121,7 @@ TEST_CASE("constructors") auto t = json::value_t::binary; json j(t); CHECK(j.type() == t); - CHECK(j == json::binary_array({})); + CHECK(j == json::binary({})); } } @@ -1482,12 +1482,12 @@ TEST_CASE("constructors") SECTION("binary") { { - json j = json::binary_array({1, 2, 3}); + json j = json::binary({1, 2, 3}); json j_new(j.begin(), j.end()); CHECK((j == j_new)); } { - json j = json::binary_array({1, 2, 3}); + json j = json::binary({1, 2, 3}); json j_new(j.cbegin(), j.cend()); CHECK((j == j_new)); } diff --git a/test/src/unit-constructor2.cpp b/test/src/unit-constructor2.cpp index 8bbb5197..df20695c 100644 --- a/test/src/unit-constructor2.cpp +++ b/test/src/unit-constructor2.cpp @@ -94,7 +94,7 @@ TEST_CASE("other constructors and destructor") SECTION("binary") { - json j = json::binary_array({1, 2, 3}); + json j = json::binary({1, 2, 3}); json k(j); CHECK(j == k); } @@ -177,7 +177,7 @@ TEST_CASE("other constructors and destructor") SECTION("binary") { - json j = json::binary_array({1, 2, 3}); + json j = json::binary({1, 2, 3}); json k; k = j; CHECK(j == k); diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp index 46638e27..a0a18fec 100644 --- a/test/src/unit-element_access1.cpp +++ b/test/src/unit-element_access1.cpp @@ -698,13 +698,13 @@ TEST_CASE("element access 1") SECTION("binary") { { - json j = json::binary_array({1, 2, 3}); + json j = json::binary({1, 2, 3}); json::iterator it = j.erase(j.begin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { - json j = json::binary_array({1, 2, 3}); + json j = json::binary({1, 2, 3}); json::const_iterator it = j.erase(j.cbegin()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); @@ -896,13 +896,13 @@ TEST_CASE("element access 1") SECTION("binary") { { - json j = json::binary_array({1, 2, 3}); + json j = json::binary({1, 2, 3}); json::iterator it = j.erase(j.begin(), j.end()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); } { - json j = json::binary_array({1, 2, 3}); + json j = json::binary({1, 2, 3}); json::const_iterator it = j.erase(j.cbegin(), j.cend()); CHECK(j.type() == json::value_t::null); CHECK(it == j.end()); diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp index d14b89af..a5f4519b 100644 --- a/test/src/unit-inspection.cpp +++ b/test/src/unit-inspection.cpp @@ -262,7 +262,7 @@ TEST_CASE("object inspection") // inside the dump() function CHECK(j.dump(1024).size() == 15472); - const auto binary = json::binary_array({1, 2, 3}, 128); + const auto binary = json::binary({1, 2, 3}, 128); CHECK(binary.dump(1024).size() == 2086); } @@ -472,7 +472,7 @@ TEST_CASE("object inspection") SECTION("binary") { - json j = json::binary_array({}); + json j = json::binary({}); json::value_t t = j; CHECK(t == j.type()); } diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index 955051a6..9214c608 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -110,7 +110,7 @@ TEST_CASE("modifiers") { SECTION("empty binary") { - json j = json::binary_array({}); + json j = json::binary({}); json k = j; j.clear(); @@ -121,7 +121,7 @@ TEST_CASE("modifiers") SECTION("filled binary") { - json j = json::binary_array({1, 2, 3, 4, 5}); + json j = json::binary({1, 2, 3, 4, 5}); json k = j; j.clear(); @@ -967,30 +967,30 @@ TEST_CASE("modifiers") { SECTION("binary_t type") { - json j = json::binary_array({1, 2, 3, 4}); + json j = json::binary({1, 2, 3, 4}); json::binary_t s = {{5, 6, 7, 8}}; j.swap(s); - CHECK(j == json::binary_array({5, 6, 7, 8})); + CHECK(j == json::binary({5, 6, 7, 8})); j.swap(s); - CHECK(j == json::binary_array({1, 2, 3, 4})); + CHECK(j == json::binary({1, 2, 3, 4})); } SECTION("binary_t::container_type type") { - json j = json::binary_array({1, 2, 3, 4}); + json j = json::binary({1, 2, 3, 4}); std::vector s = {{5, 6, 7, 8}}; j.swap(s); - CHECK(j == json::binary_array({5, 6, 7, 8})); + CHECK(j == json::binary({5, 6, 7, 8})); j.swap(s); - CHECK(j == json::binary_array({1, 2, 3, 4})); + CHECK(j == json::binary({1, 2, 3, 4})); } SECTION("non-binary_t type") diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index acdafaa8..2fb89533 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -1132,7 +1132,7 @@ TEST_CASE("MessagePack") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); std::uint8_t subtype = 42; j.get_binary().set_subtype(subtype); @@ -1207,7 +1207,7 @@ TEST_CASE("MessagePack") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); std::uint8_t subtype = 42; j.get_binary().set_subtype(subtype); @@ -1243,7 +1243,7 @@ TEST_CASE("MessagePack") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); std::uint8_t subtype = 42; j.get_binary().set_subtype(subtype); @@ -1281,7 +1281,7 @@ TEST_CASE("MessagePack") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected; @@ -1319,7 +1319,7 @@ TEST_CASE("MessagePack") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector (hack: create string first) std::vector expected(N, 'x'); @@ -1352,7 +1352,7 @@ TEST_CASE("MessagePack") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector (hack: create string first) std::vector expected(N, 'x'); diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp index 72ee1ab7..36a05249 100644 --- a/test/src/unit-pointer_access.cpp +++ b/test/src/unit-pointer_access.cpp @@ -443,7 +443,7 @@ TEST_CASE("pointer access") SECTION("pointer access to const binary_t") { using test_type = const json::binary_t; - const json value = json::binary_array({1, 2, 3}); + const json value = json::binary({1, 2, 3}); // check if pointers are returned correctly test_type* p1 = value.get_ptr(); @@ -472,7 +472,7 @@ TEST_CASE("pointer access") SECTION("pointer access to const binary_t") { using test_type = const json::binary_t; - const json value = json::binary_array({}); + const json value = json::binary({}); // check if pointers are returned correctly test_type* p1 = value.get_ptr(); diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp index d0ddc58f..067d44c6 100644 --- a/test/src/unit-serialization.cpp +++ b/test/src/unit-serialization.cpp @@ -209,10 +209,10 @@ TEST_CASE_TEMPLATE("serialization for extreme integer values", T, int32_t, uint3 TEST_CASE("dump with binary values") { - auto binary = json::binary_array({1, 2, 3, 4}); - auto binary_empty = json::binary_array({}); - auto binary_with_subtype = json::binary_array({1, 2, 3, 4}, 128); - auto binary_empty_with_subtype = json::binary_array({}, 128); + auto binary = json::binary({1, 2, 3, 4}); + auto binary_empty = json::binary({}); + auto binary_with_subtype = json::binary({1, 2, 3, 4}, 128); + auto binary_empty_with_subtype = json::binary({}, 128); json object = {{"key", binary}}; json object_empty = {{"key", binary_empty}}; diff --git a/test/src/unit-ubjson.cpp b/test/src/unit-ubjson.cpp index e3395bf2..d6dde7ad 100644 --- a/test/src/unit-ubjson.cpp +++ b/test/src/unit-ubjson.cpp @@ -921,7 +921,7 @@ TEST_CASE("UBJSON") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected; @@ -972,7 +972,7 @@ TEST_CASE("UBJSON") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected; @@ -1012,7 +1012,7 @@ TEST_CASE("UBJSON") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected(N + 7, 'x'); @@ -1049,7 +1049,7 @@ TEST_CASE("UBJSON") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected(N + 9, 'x'); @@ -1081,7 +1081,7 @@ TEST_CASE("UBJSON") { const std::size_t N = 10; const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); SECTION("No Count No Type") { diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index 42b1375e..bb1c1f1b 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -765,7 +765,7 @@ TEST_CASE("different basic_json types conversions") SECTION("binary") { - json j = json::binary_array({1, 2, 3}); + json j = json::binary({1, 2, 3}); custom_json cj = j; CHECK(cj.get_binary() == j.get_binary()); } From 4d39644bd048d16e0674a7285445af06d2b79fa4 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 19 May 2020 13:45:52 +0200 Subject: [PATCH 12/12] :green_heart: fix build --- .github/workflows/macos.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- .github/workflows/windows.yml | 2 +- test/src/unit-bson.cpp | 2 +- test/src/unit-udt.cpp | 7 +++++-- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 96435a63..4444f72b 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -14,4 +14,4 @@ jobs: - name: build run: cmake --build build --parallel 10 - name: test - run: cd build ; ctest -j 10 + run: cd build ; ctest -j 10 --output-on-failure diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 5aa0dacb..6ee33cfa 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -14,4 +14,4 @@ jobs: - name: build run: cmake --build build --parallel 10 - name: test - run: cd build ; ctest -j 10 + run: cd build ; ctest -j 10 --output-on-failure diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 2758c185..f4b07c89 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -14,4 +14,4 @@ jobs: - name: build run: cmake --build build --parallel 10 - name: test - run: cd build ; ctest -j 10 -C Debug --exclude-regex "test-unicode" + run: cd build ; ctest -j 10 -C Debug --exclude-regex "test-unicode" --output-on-failure diff --git a/test/src/unit-bson.cpp b/test/src/unit-bson.cpp index 2a17f180..3f3dbd10 100644 --- a/test/src/unit-bson.cpp +++ b/test/src/unit-bson.cpp @@ -499,7 +499,7 @@ TEST_CASE("BSON") const auto s = std::vector(N, 'x'); json j = { - { "entry", json::binary(s) } + { "entry", json::binary(s, 0) } }; std::vector expected = diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index bb1c1f1b..2040899e 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -765,9 +765,12 @@ TEST_CASE("different basic_json types conversions") SECTION("binary") { - json j = json::binary({1, 2, 3}); + json j = json::binary({1, 2, 3}, 42); custom_json cj = j; - CHECK(cj.get_binary() == j.get_binary()); + CHECK(cj.get_binary().subtype() == 42); + std::vector cv = cj.get_binary(); + std::vector v = j.get_binary(); + CHECK(cv == v); } SECTION("object")