// __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) // | | |__ | | | | | | version 3.12.0 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // SPDX-FileCopyrightText: 2013-2026 Niels Lohmann // SPDX-License-Identifier: MIT // cmake/test.cmake builds a unit test with C++ standards beyond C++11 only if the // source file mentions the corresponding version macro. To avoid rebuilding this // large file for every standard, tests that depend on the standard version (e.g., // those using JSON_HAS_FILESYSTEM, JSON_HAS_RANGES, or JSON_HAS_THREE_WAY_COMPARISON) // go into a separate file unit-regression3-cpp.cpp. This file stays C++11-only. #include "doctest_compatibility.h" // for some reason including this after the json header leads to linker errors with VS 2017... #include // skip tests if JSON_DisableEnumSerialization=ON (#4384): std::byte is a // scoped enum, so get() (needed below to get>() // from a plain JSON array, not just from an already-binary value) relies on // enum serialization being enabled // capture whether JSON_DELETE_DEPRECATED_FUNCTIONS was enabled on the command // line *before* including json.hpp, since the library #undefs it once the header // has been fully processed (see include/nlohmann/detail/macro_unscope.hpp); the // tests of deprecated functions are skipped if these functions are deleted #if defined(JSON_DELETE_DEPRECATED_FUNCTIONS) && (JSON_DELETE_DEPRECATED_FUNCTIONS == 1) #define JSON_TEST_DEPRECATED_FUNCTIONS_DELETED #endif #if defined(JSON_DISABLE_ENUM_SERIALIZATION) && (JSON_DISABLE_ENUM_SERIALIZATION == 1) #define SKIP_TESTS_FOR_ENUM_SERIALIZATION #endif #define JSON_TESTS_PRIVATE #include using json = nlohmann::json; using ordered_json = nlohmann::ordered_json; #ifdef JSON_TEST_NO_GLOBAL_UDLS using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) #endif #include #include #include #include #include // the explicit instantiation for #4825 is in unit-explicit_instantiation.cpp // NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors") ///////////////////////////////////////////////////////////////////// // for #3077 ///////////////////////////////////////////////////////////////////// class FooAlloc {}; class Foo { public: explicit Foo(const FooAlloc& /* unused */ = FooAlloc()) {} bool value = false; }; class FooBar { public: Foo foo{}; // NOLINT(readability-redundant-member-init) }; inline void from_json(const nlohmann::json& j, FooBar& fb) // NOLINT(misc-use-internal-linkage) { j.at("value").get_to(fb.foo.value); } ///////////////////////////////////////////////////////////////////// // for #3171 ///////////////////////////////////////////////////////////////////// struct for_3171_base // NOLINT(cppcoreguidelines-special-member-functions) { for_3171_base(const std::string& /*unused*/ = {}) {} virtual ~for_3171_base(); for_3171_base(const for_3171_base& other) // NOLINT(hicpp-use-equals-default,modernize-use-equals-default) : str(other.str) {} for_3171_base& operator=(const for_3171_base& other) { if (this != &other) { str = other.str; } return *this; } for_3171_base(for_3171_base&& other) noexcept : str(std::move(other.str)) {} for_3171_base& operator=(for_3171_base&& other) noexcept { if (this != &other) { str = std::move(other.str); } return *this; } virtual void _from_json(const json& j) { j.at("str").get_to(str); } std::string str{}; // NOLINT(readability-redundant-member-init) }; for_3171_base::~for_3171_base() = default; struct for_3171_derived : public for_3171_base { for_3171_derived() = default; ~for_3171_derived() override; explicit for_3171_derived(const std::string& /*unused*/) { } for_3171_derived(const for_3171_derived& other) // NOLINT(hicpp-use-equals-default,modernize-use-equals-default) : for_3171_base(other) {} for_3171_derived& operator=(const for_3171_derived& other) { if (this != &other) { for_3171_base::operator=(other); // Call base class assignment operator } return *this; } for_3171_derived(for_3171_derived&& other) noexcept : for_3171_base(std::move(other)) {} for_3171_derived& operator=(for_3171_derived&& other) noexcept { if (this != &other) { for_3171_base::operator=(std::move(other)); // Call base class move assignment operator } return *this; } }; for_3171_derived::~for_3171_derived() = default; inline void from_json(const json& j, for_3171_base& tb) // NOLINT(misc-use-internal-linkage) { tb._from_json(j); } ///////////////////////////////////////////////////////////////////// // for #3204 ///////////////////////////////////////////////////////////////////// struct for_3204_foo { for_3204_foo() = default; explicit for_3204_foo(std::string /*unused*/) {} // NOLINT(performance-unnecessary-value-param) }; struct for_3204_bar { enum constructed_from_t // NOLINT(cppcoreguidelines-use-enum-class) { constructed_from_none = 0, constructed_from_foo = 1, constructed_from_json = 2 }; explicit for_3204_bar(std::function /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param) : constructed_from(constructed_from_foo) {} explicit for_3204_bar(std::function /*unused*/) noexcept // NOLINT(performance-unnecessary-value-param) : constructed_from(constructed_from_json) {} constructed_from_t constructed_from = constructed_from_none; }; ///////////////////////////////////////////////////////////////////// // for #3333 ///////////////////////////////////////////////////////////////////// struct for_3333 final { for_3333(int x_ = 0, int y_ = 0) : x(x_), y(y_) {} template for_3333(const T& /*unused*/) { CHECK(false); } int x = 0; int y = 0; }; template <> inline for_3333::for_3333(const json& j) : for_3333(j.value("x", 0), j.value("y", 0)) {} ///////////////////////////////////////////////////////////////////// // for #3810 ///////////////////////////////////////////////////////////////////// struct Example_3810 { int bla{}; Example_3810() = default; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Example_3810, bla) // NOLINT(misc-use-internal-linkage) TEST_CASE("regression tests 3") { SECTION("issue #3077 - explicit constructor with default does not compile") { json j; j[0]["value"] = true; std::vector foo; j.get_to(foo); } SECTION("issue #3108 - ordered_json doesn't support range based erase") { ordered_json j = {1, 2, 2, 4}; auto last = std::unique(j.begin(), j.end()); j.erase(last, j.end()); CHECK(j.dump() == "[1,2,4]"); j.erase(std::remove_if(j.begin(), j.end(), [](const ordered_json & val) { return val == 2; }), j.end()); CHECK(j.dump() == "[1,4]"); } SECTION("issue #3343 - json and ordered_json are not interchangeable") { json::object_t jobj({ { "product", "one" } }); ordered_json::object_t ojobj({{"product", "one"}}); auto jit = jobj.begin(); auto ojit = ojobj.begin(); CHECK(jit->first == ojit->first); CHECK(jit->second.get() == ojit->second.get()); } SECTION("issue #3171 - if class is_constructible from std::string wrong from_json overload is being selected, compilation failed") { const json j{{ "str", "value"}}; // failed with: error: no match for ‘operator=’ (operand types are ‘for_3171_derived’ and ‘const nlohmann::basic_json<>::string_t’ // {aka ‘const std::__cxx11::basic_string’}) // s = *j.template get_ptr(); auto td = j.get(); CHECK(td.str == "value"); } SECTION("issue #3204 - ambiguous regression") { const for_3204_bar bar_from_foo([](for_3204_foo) noexcept {}); // NOLINT(performance-unnecessary-value-param) const for_3204_bar bar_from_json([](json) noexcept {}); // NOLINT(performance-unnecessary-value-param) CHECK(bar_from_foo.constructed_from == for_3204_bar::constructed_from_foo); CHECK(bar_from_json.constructed_from == for_3204_bar::constructed_from_json); } SECTION("issue #3333 - Ambiguous conversion from nlohmann::basic_json<> to custom class") { const json j { {"x", 1}, {"y", 2} }; const for_3333 p = j; CHECK(p.x == 1); CHECK(p.y == 2); } SECTION("issue #3810 - ordered_json doesn't support construction from C array of custom type") { Example_3810 states[45]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) // fix "not used" warning states[0].bla = 1; const auto* const expected = R"([{"bla":1},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0},{"bla":0}])"; // This works: nlohmann::json j; j["test"] = states; CHECK(j["test"].dump() == expected); // This doesn't compile: nlohmann::ordered_json oj; oj["test"] = states; CHECK(oj["test"].dump() == expected); } #if !defined(_MSVC_LANG) // MSVC returns garbage on invalid enum values, so this test is excluded // there. SECTION("issue #4762 - json exception 302 with unhelpful explanation : type must be number, but is number") { // In #4762, the main issue was that a json object with an invalid type // returned "number" as type_name(), because this was the default case. // This test makes sure we now return "invalid" instead. json j; j.m_data.m_type = static_cast(100); // NOLINT(clang-analyzer-optin.core.EnumCastOutOfRange) CHECK(j.type_name() == "invalid"); } #endif // owning_view is not available in libstdc++ < 12 } TEST_CASE_TEMPLATE("issue #4798 - nlohmann::json::to_msgpack() encode float NaN as double", T, double, float) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) { // With issue #4798, we encode NaN, infinity, and -infinity as float instead // of double to allow for smaller encodings. const json jx = std::numeric_limits::quiet_NaN(); const json jy = std::numeric_limits::infinity(); const json jz = -std::numeric_limits::infinity(); ///////////////////////////////////////////////////////////////////////// // MessagePack ///////////////////////////////////////////////////////////////////////// // expected MessagePack values const std::vector msgpack_x = {{0xCA, 0x7F, 0xC0, 0x00, 0x00}}; const std::vector msgpack_y = {{0xCA, 0x7F, 0x80, 0x00, 0x00}}; const std::vector msgpack_z = {{0xCA, 0xFF, 0x80, 0x00, 0x00}}; CHECK(json::to_msgpack(jx) == msgpack_x); CHECK(json::to_msgpack(jy) == msgpack_y); CHECK(json::to_msgpack(jz) == msgpack_z); CHECK(std::isnan(json::from_msgpack(msgpack_x).get())); CHECK(json::from_msgpack(msgpack_y).get() == std::numeric_limits::infinity()); CHECK(json::from_msgpack(msgpack_z).get() == -std::numeric_limits::infinity()); // Make sure the other MessagePakc encodings for NaN, infinity, and // -infinity are still supported. const std::vector msgpack_x_2 = {{0xCB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; const std::vector msgpack_y_2 = {{0xCB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; const std::vector msgpack_z_2 = {{0xCB, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; CHECK(std::isnan(json::from_msgpack(msgpack_x_2).get())); CHECK(json::from_msgpack(msgpack_y_2).get() == std::numeric_limits::infinity()); CHECK(json::from_msgpack(msgpack_z_2).get() == -std::numeric_limits::infinity()); ///////////////////////////////////////////////////////////////////////// // CBOR ///////////////////////////////////////////////////////////////////////// // expected CBOR values const std::vector cbor_x = {{0xF9, 0x7E, 0x00}}; const std::vector cbor_y = {{0xF9, 0x7C, 0x00}}; const std::vector cbor_z = {{0xF9, 0xfC, 0x00}}; CHECK(json::to_cbor(jx) == cbor_x); CHECK(json::to_cbor(jy) == cbor_y); CHECK(json::to_cbor(jz) == cbor_z); CHECK(std::isnan(json::from_cbor(cbor_x).get())); CHECK(json::from_cbor(cbor_y).get() == std::numeric_limits::infinity()); CHECK(json::from_cbor(cbor_z).get() == -std::numeric_limits::infinity()); // Make sure the other CBOR encodings for NaN, infinity, and -infinity are // still supported. const std::vector cbor_x_2 = {{0xFA, 0x7F, 0xC0, 0x00, 0x00}}; const std::vector cbor_y_2 = {{0xFA, 0x7F, 0x80, 0x00, 0x00}}; const std::vector cbor_z_2 = {{0xFA, 0xFF, 0x80, 0x00, 0x00}}; const std::vector cbor_x_3 = {{0xFB, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; const std::vector cbor_y_3 = {{0xFB, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; const std::vector cbor_z_3 = {{0xFB, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; CHECK(std::isnan(json::from_cbor(cbor_x_2).get())); CHECK(json::from_cbor(cbor_y_2).get() == std::numeric_limits::infinity()); CHECK(json::from_cbor(cbor_z_2).get() == -std::numeric_limits::infinity()); CHECK(std::isnan(json::from_cbor(cbor_x_3).get())); CHECK(json::from_cbor(cbor_y_3).get() == std::numeric_limits::infinity()); CHECK(json::from_cbor(cbor_z_3).get() == -std::numeric_limits::infinity()); } TEST_CASE("regression test #5074 - portable workaround for single-element brace init") { json const j_obj = {{"key", "value"}}; json const j = json::array({j_obj}); CHECK(j.is_array()); CHECK(j.size() == 1); CHECK(j[0] == j_obj); } struct Example_5122 { float b = 2; nlohmann::ordered_map c{}; // NOLINT(readability-redundant-member-init): needed for GCC -Weffc++ int a = 1; NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Example_5122, b, c, a) }; TEST_CASE("regression test #5122 - from_json into types holding nlohmann::ordered_map") { Example_5122 src; src.c.emplace("first", "1"); src.c.emplace("second", "2"); ordered_json const j = src; Example_5122 const dst = j.get(); CHECK(dst.b == src.b); CHECK(dst.a == src.a); REQUIRE(dst.c.size() == src.c.size()); auto src_it = src.c.begin(); auto dst_it = dst.c.begin(); for (; src_it != src.c.end(); ++src_it, ++dst_it) { CHECK(dst_it->first == src_it->first); CHECK(dst_it->second == src_it->second); } } // -Wself-assign-overloaded was introduced in Clang 7. Gate the pragma on // __has_warning so older Clang versions do not error with "unknown warning // group". The __has_warning check has to stay inside the __clang__ branch // because GCC does not provide it and would tokenize-error on the argument. #if defined(__clang__) && defined(__has_warning) #if __has_warning("-Wself-assign-overloaded") DOCTEST_CLANG_SUPPRESS_WARNING_PUSH DOCTEST_CLANG_SUPPRESS_WARNING("-Wself-assign-overloaded") #endif #endif TEST_CASE("regression test #5122 - nlohmann::ordered_map copy-assignment is self-assignment safe") { nlohmann::ordered_map m; m.emplace("first", "1"); m.emplace("second", "2"); // Insertion order is preserved by ordered_map, so we can check it directly. m = m; REQUIRE(m.size() == 2); auto it = m.begin(); CHECK(it->first == "first"); CHECK(it->second == "1"); ++it; CHECK(it->first == "second"); CHECK(it->second == "2"); } #if defined(__clang__) && defined(__has_warning) #if __has_warning("-Wself-assign-overloaded") DOCTEST_CLANG_SUPPRESS_WARNING_POP #endif #endif TEST_CASE("regression test #5122 - nlohmann::ordered_map move-assignment transfers contents") { nlohmann::ordered_map src; src.emplace("first", "1"); src.emplace("second", "2"); nlohmann::ordered_map dst; dst.emplace("stale", "x"); dst = std::move(src); REQUIRE(dst.size() == 2); auto it = dst.begin(); CHECK(it->first == "first"); CHECK(it->second == "1"); ++it; CHECK(it->first == "second"); CHECK(it->second == "2"); // Re-assigning into the moved-from object must leave it in a usable state. src = nlohmann::ordered_map {}; src.emplace("after-move", "3"); REQUIRE(src.size() == 1); CHECK(src.begin()->first == "after-move"); } // Stand-in for a third-party library (e.g., Eigen as of 3.4, which added // STL-compatible begin()/end() to its vector types), living in its own // namespace with its own to_json overload for its vector type. namespace issue_4320_eigen { // "array-compatible" from the library's point of view (it has begin()/end()), // but for which this (fake) third-party namespace provides its own to_json. struct vector3 { double v[3]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-use-default-member-init,modernize-use-default-member-init) vector3(double x, double y, double z) : v{x, y, z} {} // NOLINT(hicpp-member-init,cppcoreguidelines-pro-type-member-init) double x() const { return v[0]; } double y() const { return v[1]; } double z() const { return v[2]; } double* begin() { return v; } double* end() { return v + 3; } const double* begin() const { return v; } const double* end() const { return v + 3; } }; inline void to_json(json& j, const vector3& v) // NOLINT(misc-use-internal-linkage) { j = {{"x", v.x()}, {"y", v.y()}, {"z", v.z()}}; } } // namespace issue_4320_eigen // The user's own namespace, using the (fake) Eigen type as an implementation // detail behind a payload type that has nothing to do with vectors/arrays. namespace issue_4320 { // Publicly derives from issue_4320_eigen::vector3 but does *not* define its // own to_json - it is only ever used as a temporary to reach the base // class's to_json via ADL. struct vector3_wrapper : issue_4320_eigen::vector3 { using issue_4320_eigen::vector3::vector3; }; struct payload { double x, y, z; }; inline vector3_wrapper to_eigen(const payload& p) // NOLINT(misc-use-internal-linkage) { return {p.x, p.y, p.z}; } inline void to_json(json& j, const payload& p) // NOLINT(misc-use-internal-linkage) { // Unqualified call, passing a *derived* vector3_wrapper: relies on ADL // finding issue_4320_eigen::to_json(json&, const vector3&) through the // vector3 base class, via a derived-to-base conversion. Must NOT resolve // to the library's own generic array-compatible to_json (an exact-match // template for vector3_wrapper, since it also has begin()/end()), which // would serialize this as [x, y, z] instead of {"x":x, "y":y, "z":z}. to_json(j, to_eigen(p)); } } // namespace issue_4320 TEST_CASE("issue #4320 - custom base class must not leak nlohmann::detail into ADL") { // Before the fix, basic_json unconditionally derived from a type living in // nlohmann::detail (json_default_base), which made nlohmann::detail an // associated namespace of every basic_json for ADL purposes. That leaked // the library's internal generic-array to_json overload into unqualified // to_json() calls made from user code, silently bypassing user-defined // to_json overloads reached via a derived-to-base conversion. const issue_4320::payload p{1.0, 2.0, 3.0}; json j; to_json(j, p); CHECK(j == json({{"x", 1.0}, {"y", 2.0}, {"z", 3.0}})); } TEST_CASE("issue #5338 - truncated CBOR tagged binary subtype is rejected") { const std::vector> truncated_tags = { {0xD8}, {0xD9, 0x00}, {0xDA, 0x00, 0x00, 0x00}, {0xDB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; for (const auto& data : truncated_tags) { CAPTURE(data) for (const auto tag_handler : { json::cbor_tag_handler_t::ignore, json::cbor_tag_handler_t::store }) { CAPTURE(tag_handler) const auto result = json::from_cbor(data, true, false, tag_handler); CHECK(result.is_discarded()); } } } TEST_CASE("issue #5676 - SAX parsing of CBOR tags") { const json expected = json::binary({1, 2, 3}, 42); const auto cbor = json::to_cbor(expected); nlohmann::detail::json_sax_acceptor acceptor; CHECK_FALSE(json::sax_parse(cbor, &acceptor, json::input_format_t::cbor)); CHECK_FALSE(json::sax_parse(cbor, &acceptor, json::input_format_t::cbor, true, false, false, json::cbor_tag_handler_t::error)); CHECK(json::sax_parse(cbor, &acceptor, json::input_format_t::cbor, true, false, false, json::cbor_tag_handler_t::ignore)); json parsed; nlohmann::detail::json_sax_dom_parser sax(parsed); CHECK(json::sax_parse(cbor, &sax, json::input_format_t::cbor, true, false, false, json::cbor_tag_handler_t::store)); CHECK(parsed == expected); json iterator_parsed; nlohmann::detail::json_sax_dom_parser iterator_sax(iterator_parsed); CHECK(json::sax_parse(cbor.begin(), cbor.end(), &iterator_sax, json::input_format_t::cbor, true, false, false, json::cbor_tag_handler_t::store)); CHECK(iterator_parsed == expected); #ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED json span_parsed; nlohmann::detail::json_sax_dom_parser span_sax(span_parsed); CHECK(json::sax_parse(nlohmann::detail::span_input_adapter(cbor.data(), cbor.size()), &span_sax, json::input_format_t::cbor, true, false, false, json::cbor_tag_handler_t::store)); CHECK(span_parsed == expected); #endif const std::string text = "null"; CHECK(json::sax_parse(text, &acceptor, json::input_format_t::json, true, false, false, json::cbor_tag_handler_t::store)); CHECK(json::sax_parse(text.begin(), text.end(), &acceptor, json::input_format_t::json, true, false, false, json::cbor_tag_handler_t::store)); #ifndef JSON_TEST_DEPRECATED_FUNCTIONS_DELETED CHECK(json::sax_parse(nlohmann::detail::span_input_adapter(text.data(), text.size()), &acceptor, json::input_format_t::json, true, false, false, json::cbor_tag_handler_t::store)); #endif } TEST_CASE("issue #5402 - update(merge_objects=true) overwrites a primitive with an object") { json t = {{"k", 1}}; t.update(json{{"k", {{"x", 2}}}}, true); CHECK(t == json({{"k", {{"x", 2}}}})); json mixed = {{"keep", {{"a", 1}}}, {"replace", 1}}; mixed.update(json{{"keep", {{"b", 2}}}, {"replace", {{"x", 2}}}}, true); CHECK(mixed == json({{"keep", {{"a", 1}, {"b", 2}}}, {"replace", {{"x", 2}}}})); } TEST_CASE("regression test #5476 - array type without reserve()") { // the capacity reserved for definite-length arrays must not require the // array type to have a reserve() member function using deque_json = nlohmann::json::with_array_t; SECTION("std::deque") { const auto j = deque_json::parse(R"({"a":[1,[2,3]],"b":[]})"); CHECK(j.dump() == R"({"a":[1,[2,3]],"b":[]})"); // the binary formats pass a definite length to start_array() CHECK(deque_json::from_cbor(deque_json::to_cbor(j)) == j); CHECK(deque_json::from_msgpack(deque_json::to_msgpack(j)) == j); CHECK(deque_json::from_bon8(deque_json::to_bon8(j)) == j); // parse() instantiates the callback parser as well, which reserves too const auto with_callback = deque_json::parse(R"([1,2,3])", [](int /*depth*/, deque_json::parse_event_t /*event*/, deque_json& /*parsed*/) noexcept { return true; }); CHECK(with_callback == deque_json({1, 2, 3})); } SECTION("std::vector still reserves") { json array = json::array(); for (int i = 0; i < 100; ++i) { array.push_back(i); } const auto j = json::from_cbor(json::to_cbor(array)); CHECK(j == array); CHECK(j.get_ref().capacity() >= 100); } SECTION("the reservation stays capped") { // CBOR array announcing 2^32-1 elements, but truncated right after the // header: the input must be rejected without reserving that capacity const std::vector truncated = {0x9A, 0xFF, 0xFF, 0xFF, 0xFF}; CHECK(json::from_cbor(truncated, true, false).is_discarded()); } } TEST_CASE("issue #5317 - nested indefinite-length CBOR string chunks are rejected") { json _; CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x7F, 0x7F, 0x61, 0x61, 0xFF, 0xFF})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: indefinite-length string is not allowed inside indefinite-length string; last byte: 0x7F", json::parse_error&); CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0x5F, 0x5F, 0x41, 0x61, 0xFF, 0xFF})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR binary: indefinite-length binary array is not allowed inside indefinite-length binary array; last byte: 0x5F", json::parse_error&); CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector({0xA1, 0x7F, 0x7F, 0xFF, 0xFF, 0x01})), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR string: indefinite-length string is not allowed inside indefinite-length string; last byte: 0x7F", json::parse_error&); } DOCTEST_CLANG_SUPPRESS_WARNING_POP