From 225c8f150ad6b098de6a650fc0cce613d5d66802 Mon Sep 17 00:00:00 2001 From: Guillaume Racicot Date: Sat, 6 Jun 2020 11:36:39 -0400 Subject: [PATCH 01/31] Disable std::swap specialization in C++20 and added a friend swap function --- include/nlohmann/detail/macro_scope.hpp | 6 +++- include/nlohmann/json.hpp | 33 +++++++++++++++++++++ single_include/nlohmann/json.hpp | 39 ++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 25e6b76eb9..a48d18e893 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -20,7 +20,11 @@ #endif // C++ language standard detection -#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 +#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 592d19667b..abc5305664 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -5865,6 +5865,34 @@ class basic_json /*! @brief exchanges the values + Exchanges the contents of the JSON value from @a left with those of @a right. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. implemented as a friend function callable via ADL. + + @param[in,out] left JSON value to exchange the contents with + @param[in,out] right JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + left.swap(right); + } + + /*! + @brief exchanges the values + Exchanges the contents of a JSON array with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is @@ -8635,6 +8663,9 @@ struct less<::nlohmann::detail::value_t> } }; +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + /*! @brief exchanges the values of two JSON objects @@ -8649,6 +8680,8 @@ inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcep j1.swap(j2); } +#endif + } // namespace std /*! diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index a24a08b5ae..53a9380cdc 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2048,7 +2048,11 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif // C++ language standard detection -#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 +#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 #define JSON_HAS_CPP_17 #define JSON_HAS_CPP_14 #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) @@ -21628,6 +21632,34 @@ class basic_json /*! @brief exchanges the values + Exchanges the contents of the JSON value from @a left with those of @a right. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. implemented as a friend function callable via ADL. + + @param[in,out] left JSON value to exchange the contents with + @param[in,out] right JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + left.swap(right); + } + + /*! + @brief exchanges the values + Exchanges the contents of a JSON array with those of @a other. Does not invoke any move, copy, or swap operations on individual elements. All iterators and references remain valid. The past-the-end iterator is @@ -24398,6 +24430,9 @@ struct less<::nlohmann::detail::value_t> } }; +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + /*! @brief exchanges the values of two JSON objects @@ -24412,6 +24447,8 @@ inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcep j1.swap(j2); } +#endif + } // namespace std /*! From 82fbbeeac5c4a75fa4af95f10c97c56ef95ddbed Mon Sep 17 00:00:00 2001 From: Guillaume Racicot Date: Sat, 6 Jun 2020 12:28:52 -0400 Subject: [PATCH 02/31] Adapted unit tests to use ADL calls for swap like the new swappable concept --- test/src/unit-concepts.cpp | 4 ++-- test/src/unit-modifiers.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp index 899d1eaf9f..1f3afc0c9f 100644 --- a/test/src/unit-concepts.cpp +++ b/test/src/unit-concepts.cpp @@ -154,7 +154,7 @@ TEST_CASE("concepts") json j {1, 2, 3}; json::iterator it1 = j.begin(); json::iterator it2 = j.end(); - std::swap(it1, it2); + swap(it1, it2); CHECK(it1 == j.end()); CHECK(it2 == j.begin()); } @@ -162,7 +162,7 @@ TEST_CASE("concepts") json j {1, 2, 3}; json::const_iterator it1 = j.cbegin(); json::const_iterator it2 = j.cend(); - std::swap(it1, it2); + swap(it1, it2); CHECK(it1 == j.end()); CHECK(it2 == j.begin()); } diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index 9214c60822..4b8d076218 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -878,7 +878,8 @@ TEST_CASE("modifiers") json j("hello world"); json k(42.23); - std::swap(j, k); + using std::swap; + swap(j, k); CHECK(j == json(42.23)); CHECK(k == json("hello world")); From 71830be06d80b08a0a1eda8a34c063246c923792 Mon Sep 17 00:00:00 2001 From: chenguoping Date: Wed, 10 Jun 2020 19:27:28 +0800 Subject: [PATCH 03/31] fix issue#1275 --- include/nlohmann/json.hpp | 25 +++++++++++++++++++++++++ single_include/nlohmann/json.hpp | 25 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index d1dc411924..0deb058338 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3788,6 +3788,31 @@ class basic_json JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } + /*! + @brief overload for a default value of type rvalue + @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const + */ + template>::value + and not std::is_same::value, int>::type = 0> + detail::uncvref_t value(const typename object_t::key_type& key, ValueType && default_value) && + { + // only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return std::move(it->template get_ref()); + } + + return std::forward(default_value); + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + } + /*! @brief overload for a default value of type const char* @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index dd9b21c4f4..9fd621c799 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -19566,6 +19566,31 @@ class basic_json JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } + /*! + @brief overload for a default value of type rvalue + @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const + */ + template>::value + and not std::is_same::value, int>::type = 0> + detail::uncvref_t value(const typename object_t::key_type& key, ValueType && default_value) && + { + // only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return std::move(it->template get_ref()); + } + + return std::forward(default_value); + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + } + /*! @brief overload for a default value of type const char* @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const From a3df26b771cbe866e000615300302e2be22eb8a4 Mon Sep 17 00:00:00 2001 From: chenguoping Date: Wed, 10 Jun 2020 19:27:57 +0800 Subject: [PATCH 04/31] add some test cases --- test/src/unit-element_access2.cpp | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index e881a9479c..6df0ca59ac 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -148,6 +148,45 @@ TEST_CASE("element access 2") SECTION("access specified element with default value") { + SECTION("move semantics") + { + SECTION("json is rvalue") + { + json j = {{"x", "123"}}; + std::string defval = "default"; + auto val = std::move(j).value("x", defval); + + CHECK(j["x"] == ""); + CHECK(defval == "default"); + CHECK(val == "123"); + } + + SECTION("default is rvalue") + { + json j = {{"x", "123"}}; + std::string defval = "default"; + auto val = std::move(j).value("y", std::move(defval)); + + CHECK(j["x"] == "123"); + CHECK(defval == ""); + CHECK(val == "default"); + } + + SECTION("access on non-object value") + { + json j_nonobject(json::value_t::array); + const json j_nonobject_const(j_nonobject); + std::string defval = "default"; + + CHECK_THROWS_AS(std::move(j_nonobject).value("foo", defval), json::type_error&); + CHECK_THROWS_AS(std::move(j_nonobject_const).value("foo", defval), json::type_error&); + CHECK_THROWS_WITH(std::move(j_nonobject).value("foo", defval), + "[json.exception.type_error.306] cannot use value() with array"); + CHECK_THROWS_WITH(std::move(j_nonobject_const).value("foo", defval), + "[json.exception.type_error.306] cannot use value() with array"); + } + } + SECTION("given a key") { SECTION("access existing value") From 691fb0c57a142889f788c72e8b7d64548cb5b06c Mon Sep 17 00:00:00 2001 From: chenguoping Date: Tue, 16 Jun 2020 15:35:26 +0800 Subject: [PATCH 05/31] fix issue#2059 --- include/nlohmann/detail/iterators/iteration_proxy.hpp | 3 ++- single_include/nlohmann/json.hpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/iterators/iteration_proxy.hpp b/include/nlohmann/detail/iterators/iteration_proxy.hpp index c61d96296c..48927b0183 100644 --- a/include/nlohmann/detail/iterators/iteration_proxy.hpp +++ b/include/nlohmann/detail/iterators/iteration_proxy.hpp @@ -12,7 +12,8 @@ namespace nlohmann { namespace detail { -template +template::value, int>::type = 0> void int_to_string( string_type& target, std::size_t value ) { target = std::to_string(value); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cc822a543b..69a4ec0921 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3650,7 +3650,8 @@ namespace nlohmann { namespace detail { -template +template::value, int>::type = 0> void int_to_string( string_type& target, std::size_t value ) { target = std::to_string(value); From b53c6e2f8193cb6469ff4319f6c516ca94de71d3 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 16 Jun 2020 12:28:59 +0200 Subject: [PATCH 06/31] :sparkles: ignore comments --- include/nlohmann/detail/input/lexer.hpp | 88 ++++++++++++++++++++++++- single_include/nlohmann/json.hpp | 88 ++++++++++++++++++++++++- test/src/unit-class_lexer.cpp | 20 ++++++ 3 files changed, 190 insertions(+), 6 deletions(-) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 0ff0c7362d..00af0ce215 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -112,8 +112,11 @@ class lexer : public lexer_base public: using token_type = typename lexer_base::token_type; - explicit lexer(InputAdapterType&& adapter) - : ia(std::move(adapter)), decimal_point_char(static_cast(get_decimal_point())) {} + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast(get_decimal_point())) + {} // delete because of pointer members lexer(const lexer&) = delete; @@ -131,7 +134,7 @@ class lexer : public lexer_base JSON_HEDLEY_PURE static char get_decimal_point() noexcept { - const auto loc = localeconv(); + const auto* loc = localeconv(); assert(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } @@ -826,6 +829,71 @@ class lexer : public lexer_base } } + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + // remember character after '/' to distinguish comment types + const auto comment_char = get(); + + // expect // or /* to start a comment + if (comment_char != '/' and comment_char != '*') + { + return false; + } + + while (true) + { + switch (get()) + { + // EOF inside a /* comment is an error, in // it is OK + case std::char_traits::eof(): + case '\0': + { + return comment_char == '/'; + } + + // a newline ends the // comment + case '\n': + case '\r': + { + if (comment_char == '/') + { + return true; + } + break; + } + + // */ ends the /* comment + case '*': + { + if (comment_char == '*') + { + switch (get()) + { + case '/': + { + return true; + } + + default: + { + unget(); + break; + } + } + } + break; + } + + default: + break; + } + } + } + JSON_HEDLEY_NON_NULL(2) static void strtof(float& f, const char* str, char** endptr) noexcept { @@ -1431,6 +1499,17 @@ class lexer : public lexer_base } while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + // ignore comments + if (ignore_comments and current == '/') + { + if (not scan_comment()) + { + error_message = "invalid comment"; + return token_type::parse_error; + } + get(); + } + switch (current) { // structural characters @@ -1499,6 +1578,9 @@ class lexer : public lexer_base /// input adapter InputAdapterType ia; + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = false; + /// the current character char_int_type current = std::char_traits::eof(); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cc822a543b..82435fa7ad 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -8179,8 +8179,11 @@ class lexer : public lexer_base public: using token_type = typename lexer_base::token_type; - explicit lexer(InputAdapterType&& adapter) - : ia(std::move(adapter)), decimal_point_char(static_cast(get_decimal_point())) {} + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast(get_decimal_point())) + {} // delete because of pointer members lexer(const lexer&) = delete; @@ -8198,7 +8201,7 @@ class lexer : public lexer_base JSON_HEDLEY_PURE static char get_decimal_point() noexcept { - const auto loc = localeconv(); + const auto* loc = localeconv(); assert(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } @@ -8893,6 +8896,71 @@ class lexer : public lexer_base } } + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + // remember character after '/' to distinguish comment types + const auto comment_char = get(); + + // expect // or /* to start a comment + if (comment_char != '/' and comment_char != '*') + { + return false; + } + + while (true) + { + switch (get()) + { + // EOF inside a /* comment is an error, in // it is OK + case std::char_traits::eof(): + case '\0': + { + return comment_char == '/'; + } + + // a newline ends the // comment + case '\n': + case '\r': + { + if (comment_char == '/') + { + return true; + } + break; + } + + // */ ends the /* comment + case '*': + { + if (comment_char == '*') + { + switch (get()) + { + case '/': + { + return true; + } + + default: + { + unget(); + break; + } + } + } + break; + } + + default: + break; + } + } + } + JSON_HEDLEY_NON_NULL(2) static void strtof(float& f, const char* str, char** endptr) noexcept { @@ -9498,6 +9566,17 @@ class lexer : public lexer_base } while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + // ignore comments + if (ignore_comments and current == '/') + { + if (not scan_comment()) + { + error_message = "invalid comment"; + return token_type::parse_error; + } + get(); + } + switch (current) { // structural characters @@ -9566,6 +9645,9 @@ class lexer : public lexer_base /// input adapter InputAdapterType ia; + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = false; + /// the current character char_int_type current = std::char_traits::eof(); diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 0e1b53785a..c4423e6023 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -127,6 +127,8 @@ TEST_CASE("lexer class") // store scan() result const auto res = scan_string(s.c_str()); + CAPTURE(s); + switch (c) { // single characters that are valid tokens @@ -161,6 +163,9 @@ TEST_CASE("lexer class") break; } + // case ('/'): + // break; + // anything else is not expected default: { @@ -179,4 +184,19 @@ TEST_CASE("lexer class") s += "\""; CHECK((scan_string(s.c_str()) == json::lexer::token_type::value_string)); } + + // SECTION("ignore comments") + // { + // CHECK((scan_string("/") == json::lexer::token_type::parse_error)); + // + // CHECK((scan_string("/!") == json::lexer::token_type::parse_error)); + // CHECK((scan_string("/*") == json::lexer::token_type::parse_error)); + // CHECK((scan_string("/**") == json::lexer::token_type::parse_error)); + // + // CHECK((scan_string("//") == json::lexer::token_type::end_of_input)); + // CHECK((scan_string("/**/") == json::lexer::token_type::end_of_input)); + // CHECK((scan_string("/** /") == json::lexer::token_type::parse_error)); + // + // CHECK((scan_string("/***/") == json::lexer::token_type::end_of_input)); + // } } From f4c4bab600c80746e2fd7e0c03a715d3a9ce0288 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 16 Jun 2020 12:55:36 +0200 Subject: [PATCH 07/31] :sparkles: add option JSON_TestDataDirectory to set path with test data #2189 --- cmake/download_test_data.cmake | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/cmake/download_test_data.cmake b/cmake/download_test_data.cmake index fe95235f89..7d73c6d35d 100644 --- a/cmake/download_test_data.cmake +++ b/cmake/download_test_data.cmake @@ -3,15 +3,21 @@ find_package(Git) set(JSON_TEST_DATA_URL https://github.com/nlohmann/json_test_data) set(JSON_TEST_DATA_VERSION 2.0.0) -# target to download test data -add_custom_target(download_test_data - COMMAND test -d json_test_data || ${GIT_EXECUTABLE} clone -c advice.detachedHead=false --branch v${JSON_TEST_DATA_VERSION} ${JSON_TEST_DATA_URL}.git --quiet --depth 1 - COMMENT "Downloading test data from ${JSON_TEST_DATA_URL} (v${JSON_TEST_DATA_VERSION})" - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} -) - -# create a header with the path to the downloaded test data -file(WRITE ${CMAKE_BINARY_DIR}/include/test_data.hpp "#define TEST_DATA_DIRECTORY \"${CMAKE_BINARY_DIR}/json_test_data\"\n") +# if variable is set, use test data from given directory rather than downloading them +if(JSON_TestDataDirectory) + message(STATUS "Using test data in ${JSON_TestDataDirectory}.") + add_custom_target(download_test_data) + file(WRITE ${CMAKE_BINARY_DIR}/include/test_data.hpp "#define TEST_DATA_DIRECTORY \"${JSON_TestDataDirectory}\"\n") +else() + # target to download test data + add_custom_target(download_test_data + COMMAND test -d json_test_data || ${GIT_EXECUTABLE} clone -c advice.detachedHead=false --branch v${JSON_TEST_DATA_VERSION} ${JSON_TEST_DATA_URL}.git --quiet --depth 1 + COMMENT "Downloading test data from ${JSON_TEST_DATA_URL} (v${JSON_TEST_DATA_VERSION})" + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + # create a header with the path to the downloaded test data + file(WRITE ${CMAKE_BINARY_DIR}/include/test_data.hpp "#define TEST_DATA_DIRECTORY \"${CMAKE_BINARY_DIR}/json_test_data\"\n") +endif() # determine the operating system (for debug and support purposes) find_program(UNAME_COMMAND uname) From 4d96f4cf6aadd3e5d7b0c0f8bb1472644617e1cb Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 16 Jun 2020 20:23:01 +0200 Subject: [PATCH 08/31] :wrench: overwork CMake files --- cmake/download_test_data.cmake | 3 +-- test/cmake_fetch_content/project/CMakeLists.txt | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/cmake/download_test_data.cmake b/cmake/download_test_data.cmake index 7d73c6d35d..7d7ff1784d 100644 --- a/cmake/download_test_data.cmake +++ b/cmake/download_test_data.cmake @@ -1,5 +1,3 @@ -find_package(Git) - set(JSON_TEST_DATA_URL https://github.com/nlohmann/json_test_data) set(JSON_TEST_DATA_VERSION 2.0.0) @@ -9,6 +7,7 @@ if(JSON_TestDataDirectory) add_custom_target(download_test_data) file(WRITE ${CMAKE_BINARY_DIR}/include/test_data.hpp "#define TEST_DATA_DIRECTORY \"${JSON_TestDataDirectory}\"\n") else() + find_package(Git) # target to download test data add_custom_target(download_test_data COMMAND test -d json_test_data || ${GIT_EXECUTABLE} clone -c advice.detachedHead=false --branch v${JSON_TEST_DATA_VERSION} ${JSON_TEST_DATA_URL}.git --quiet --depth 1 diff --git a/test/cmake_fetch_content/project/CMakeLists.txt b/test/cmake_fetch_content/project/CMakeLists.txt index 742a112b1a..fd8fbdd5f3 100644 --- a/test/cmake_fetch_content/project/CMakeLists.txt +++ b/test/cmake_fetch_content/project/CMakeLists.txt @@ -4,9 +4,8 @@ project(DummyImport CXX) include(FetchContent) -FetchContent_Declare(json - GIT_REPOSITORY ${CMAKE_CURRENT_SOURCE_DIR}/../../.. - GIT_TAG HEAD) +get_filename_component(GIT_REPOSITORY_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../.. ABSOLUTE) +FetchContent_Declare(json GIT_REPOSITORY ${GIT_REPOSITORY_DIRECTORY} GIT_TAG HEAD) FetchContent_GetProperties(json) if(NOT json_POPULATED) From 8aaa4013a39786b57964741cd759539112c4fbf2 Mon Sep 17 00:00:00 2001 From: chenguoping Date: Wed, 17 Jun 2020 15:05:28 +0800 Subject: [PATCH 09/31] remove overload function, change default_value to rvalue --- include/nlohmann/json.hpp | 36 ++++++-------------------------- single_include/nlohmann/json.hpp | 36 ++++++-------------------------- 2 files changed, 12 insertions(+), 60 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 0deb058338..8f0347eaf0 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3770,7 +3770,7 @@ class basic_json template::value and not std::is_same::value, int>::type = 0> - ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + ValueType value(const typename object_t::key_type& key, ValueType && default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -3782,36 +3782,12 @@ class basic_json return *it; } - return default_value; + return std::move(default_value); } JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } - /*! - @brief overload for a default value of type rvalue - @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const - */ - template>::value - and not std::is_same::value, int>::type = 0> - detail::uncvref_t value(const typename object_t::key_type& key, ValueType && default_value) && - { - // only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) - { - // if key is found, return value and given default value otherwise - const auto it = find(key); - if (it != end()) - { - return std::move(it->template get_ref()); - } - - return std::forward(default_value); - } - - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); - } /*! @brief overload for a default value of type const char* @@ -3819,7 +3795,7 @@ class basic_json */ string_t value(const typename object_t::key_type& key, const char* default_value) const { - return value(key, string_t(default_value)); + return value(key, std::move(string_t(default_value))); } /*! @@ -3867,7 +3843,7 @@ class basic_json */ template::value, int>::type = 0> - ValueType value(const json_pointer& ptr, const ValueType& default_value) const + ValueType value(const json_pointer& ptr, ValueType && default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -3879,7 +3855,7 @@ class basic_json } JSON_INTERNAL_CATCH (out_of_range&) { - return default_value; + return std::move(default_value); } } @@ -3893,7 +3869,7 @@ class basic_json JSON_HEDLEY_NON_NULL(3) string_t value(const json_pointer& ptr, const char* default_value) const { - return value(ptr, string_t(default_value)); + return value(ptr, std::move(string_t(default_value))); } /*! diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 9fd621c799..0c2cdf3db2 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -19548,7 +19548,7 @@ class basic_json template::value and not std::is_same::value, int>::type = 0> - ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + ValueType value(const typename object_t::key_type& key, ValueType && default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -19560,36 +19560,12 @@ class basic_json return *it; } - return default_value; + return std::move(default_value); } JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } - /*! - @brief overload for a default value of type rvalue - @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const - */ - template>::value - and not std::is_same::value, int>::type = 0> - detail::uncvref_t value(const typename object_t::key_type& key, ValueType && default_value) && - { - // only works for objects - if (JSON_HEDLEY_LIKELY(is_object())) - { - // if key is found, return value and given default value otherwise - const auto it = find(key); - if (it != end()) - { - return std::move(it->template get_ref()); - } - - return std::forward(default_value); - } - - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); - } /*! @brief overload for a default value of type const char* @@ -19597,7 +19573,7 @@ class basic_json */ string_t value(const typename object_t::key_type& key, const char* default_value) const { - return value(key, string_t(default_value)); + return value(key, std::move(string_t(default_value))); } /*! @@ -19645,7 +19621,7 @@ class basic_json */ template::value, int>::type = 0> - ValueType value(const json_pointer& ptr, const ValueType& default_value) const + ValueType value(const json_pointer& ptr, ValueType && default_value) const { // at only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -19657,7 +19633,7 @@ class basic_json } JSON_INTERNAL_CATCH (out_of_range&) { - return default_value; + return std::move(default_value); } } @@ -19671,7 +19647,7 @@ class basic_json JSON_HEDLEY_NON_NULL(3) string_t value(const json_pointer& ptr, const char* default_value) const { - return value(ptr, string_t(default_value)); + return value(ptr, std::move(string_t(default_value))); } /*! From f466919ec29fee46b1dbbb30d241d80b2334cdfe Mon Sep 17 00:00:00 2001 From: chenguoping Date: Wed, 17 Jun 2020 15:07:25 +0800 Subject: [PATCH 10/31] change test cases --- test/src/unit-element_access2.cpp | 41 ++----------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index 6df0ca59ac..d2a1910876 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -148,45 +148,6 @@ TEST_CASE("element access 2") SECTION("access specified element with default value") { - SECTION("move semantics") - { - SECTION("json is rvalue") - { - json j = {{"x", "123"}}; - std::string defval = "default"; - auto val = std::move(j).value("x", defval); - - CHECK(j["x"] == ""); - CHECK(defval == "default"); - CHECK(val == "123"); - } - - SECTION("default is rvalue") - { - json j = {{"x", "123"}}; - std::string defval = "default"; - auto val = std::move(j).value("y", std::move(defval)); - - CHECK(j["x"] == "123"); - CHECK(defval == ""); - CHECK(val == "default"); - } - - SECTION("access on non-object value") - { - json j_nonobject(json::value_t::array); - const json j_nonobject_const(j_nonobject); - std::string defval = "default"; - - CHECK_THROWS_AS(std::move(j_nonobject).value("foo", defval), json::type_error&); - CHECK_THROWS_AS(std::move(j_nonobject_const).value("foo", defval), json::type_error&); - CHECK_THROWS_WITH(std::move(j_nonobject).value("foo", defval), - "[json.exception.type_error.306] cannot use value() with array"); - CHECK_THROWS_WITH(std::move(j_nonobject_const).value("foo", defval), - "[json.exception.type_error.306] cannot use value() with array"); - } - } - SECTION("given a key") { SECTION("access existing value") @@ -226,6 +187,7 @@ TEST_CASE("element access 2") CHECK(j.value("_", 12.34) == Approx(12.34)); CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); CHECK(j.value("_", json({10, 100})) == json({10, 100})); + CHECK(j.value("_", std::move("default_value")) == "default_value"); CHECK(j_const.value("_", 2) == 2); CHECK(j_const.value("_", 2u) == 2u); @@ -234,6 +196,7 @@ TEST_CASE("element access 2") CHECK(j_const.value("_", 12.34) == Approx(12.34)); CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); CHECK(j_const.value("_", json({10, 100})) == json({10, 100})); + CHECK(j_const.value("_", std::move("default_value")) == "default_value"); } SECTION("access on non-object type") From e86b3fae98854312fc6e59c62e35919a8d8012b1 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 17 Jun 2020 12:35:59 +0200 Subject: [PATCH 11/31] :wrench: add label to tests that require a git checkout --- test/cmake_fetch_content/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/cmake_fetch_content/CMakeLists.txt b/test/cmake_fetch_content/CMakeLists.txt index 6d92d14977..0b0d9f65eb 100644 --- a/test/cmake_fetch_content/CMakeLists.txt +++ b/test/cmake_fetch_content/CMakeLists.txt @@ -11,8 +11,10 @@ if (${CMAKE_VERSION} VERSION_GREATER "3.11.0") ) set_tests_properties(cmake_fetch_content_configure PROPERTIES FIXTURES_SETUP cmake_fetch_content + LABELS git_required ) set_tests_properties(cmake_fetch_content_build PROPERTIES FIXTURES_REQUIRED cmake_fetch_content + LABELS git_required ) endif() From da8fa3535a71515aeddc68421e63017c1f503d3a Mon Sep 17 00:00:00 2001 From: chenguoping Date: Wed, 17 Jun 2020 20:41:31 +0800 Subject: [PATCH 12/31] drop testcase --- test/src/unit-element_access2.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/src/unit-element_access2.cpp b/test/src/unit-element_access2.cpp index d2a1910876..e881a9479c 100644 --- a/test/src/unit-element_access2.cpp +++ b/test/src/unit-element_access2.cpp @@ -187,7 +187,6 @@ TEST_CASE("element access 2") CHECK(j.value("_", 12.34) == Approx(12.34)); CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); CHECK(j.value("_", json({10, 100})) == json({10, 100})); - CHECK(j.value("_", std::move("default_value")) == "default_value"); CHECK(j_const.value("_", 2) == 2); CHECK(j_const.value("_", 2u) == 2u); @@ -196,7 +195,6 @@ TEST_CASE("element access 2") CHECK(j_const.value("_", 12.34) == Approx(12.34)); CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}})); CHECK(j_const.value("_", json({10, 100})) == json({10, 100})); - CHECK(j_const.value("_", std::move("default_value")) == "default_value"); } SECTION("access on non-object type") From 4a6c68c7eba1c6c84e90c238b3e1f27dcb096d38 Mon Sep 17 00:00:00 2001 From: chenguoping Date: Wed, 17 Jun 2020 20:44:31 +0800 Subject: [PATCH 13/31] drop new blank line --- include/nlohmann/json.hpp | 1 - single_include/nlohmann/json.hpp | 1 - 2 files changed, 2 deletions(-) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 8f0347eaf0..990fe3cfbb 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -3788,7 +3788,6 @@ class basic_json JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } - /*! @brief overload for a default value of type const char* @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 0c2cdf3db2..8a79c47987 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -19566,7 +19566,6 @@ class basic_json JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); } - /*! @brief overload for a default value of type const char* @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const From e9bfcf72550c7ba99b5cdd5c0b1e5038c1bcc4ae Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 17 Jun 2020 14:59:47 +0200 Subject: [PATCH 14/31] :zap: improve comment parsing --- include/nlohmann/detail/input/lexer.hpp | 70 +++++++++++-------------- single_include/nlohmann/json.hpp | 70 +++++++++++-------------- 2 files changed, 62 insertions(+), 78 deletions(-) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 00af0ce215..eab64f406d 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -835,62 +835,54 @@ class lexer : public lexer_base */ bool scan_comment() { - // remember character after '/' to distinguish comment types - const auto comment_char = get(); - - // expect // or /* to start a comment - if (comment_char != '/' and comment_char != '*') - { - return false; - } - - while (true) + switch (get()) { - switch (get()) + case '/': { - // EOF inside a /* comment is an error, in // it is OK - case std::char_traits::eof(): - case '\0': + while (true) { - return comment_char == '/'; - } - - // a newline ends the // comment - case '\n': - case '\r': - { - if (comment_char == '/') + switch (get()) { - return true; + case '\n': + case '\r': + return true; + + default: + break; } - break; } + } - // */ ends the /* comment - case '*': + case '*': + { + while (true) { - if (comment_char == '*') + switch (get()) { - switch (get()) + case std::char_traits::eof(): + case '\0': + return false; + + case '*': { - case '/': + switch (get()) { - return true; - } + case '/': + return true; - default: - { - unget(); - break; + default: + { + unget(); + break; + } } } } - break; } - - default: - break; } + + default: + return false; } } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 82435fa7ad..bdd97a145a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -8902,62 +8902,54 @@ class lexer : public lexer_base */ bool scan_comment() { - // remember character after '/' to distinguish comment types - const auto comment_char = get(); - - // expect // or /* to start a comment - if (comment_char != '/' and comment_char != '*') - { - return false; - } - - while (true) + switch (get()) { - switch (get()) + case '/': { - // EOF inside a /* comment is an error, in // it is OK - case std::char_traits::eof(): - case '\0': + while (true) { - return comment_char == '/'; - } - - // a newline ends the // comment - case '\n': - case '\r': - { - if (comment_char == '/') + switch (get()) { - return true; + case '\n': + case '\r': + return true; + + default: + break; } - break; } + } - // */ ends the /* comment - case '*': + case '*': + { + while (true) { - if (comment_char == '*') + switch (get()) { - switch (get()) + case std::char_traits::eof(): + case '\0': + return false; + + case '*': { - case '/': + switch (get()) { - return true; - } + case '/': + return true; - default: - { - unget(); - break; + default: + { + unget(); + break; + } } } } - break; } - - default: - break; } + + default: + return false; } } From 88a37010d6cfa808ddfa9559a15285b8291ad187 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 17 Jun 2020 21:14:23 +0200 Subject: [PATCH 15/31] :bug: serialize 32-bit floating-point numbers as float 32 in MessagePack (0xCA) #2196 --- .../features/binary_formats/messagepack.md | 7 ++-- .../nlohmann/detail/output/binary_writer.hpp | 14 ++++++-- include/nlohmann/json.hpp | 6 ++-- single_include/nlohmann/json.hpp | 20 +++++++---- test/src/unit-msgpack.cpp | 34 +++++++++++++++++++ 5 files changed, 64 insertions(+), 17 deletions(-) diff --git a/doc/mkdocs/docs/features/binary_formats/messagepack.md b/doc/mkdocs/docs/features/binary_formats/messagepack.md index ed06105693..3e041bb708 100644 --- a/doc/mkdocs/docs/features/binary_formats/messagepack.md +++ b/doc/mkdocs/docs/features/binary_formats/messagepack.md @@ -31,7 +31,8 @@ number_unsigned | 128..255 | uint 8 | 0xCC number_unsigned | 256..65535 | uint 16 | 0xCD number_unsigned | 65536..4294967295 | uint 32 | 0xCE number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF -number_float | *any value* | float 64 | 0xCB +number_float | *any value representable by a float* | float 32 | 0xCA +number_float | *any value NOT representable by a float* | float 64 | 0xCB string | *length*: 0..31 | fixstr | 0xA0..0xBF string | *length*: 32..255 | str 8 | 0xD9 string | *length*: 256..65535 | str 16 | 0xDA @@ -61,10 +62,6 @@ binary | *size*: 65536..4294967295 | bin 32 | 0xC6 - arrays with more than 4294967295 elements - objects with more than 4294967295 elements -!!! info "Unused MessagePack types" - - The following MessagePack types are not used in the conversion: float 32 (0xCA) - !!! info "NaN/infinity handling" If NaN or Infinity are stored inside a JSON number, they are serialized properly. function which serializes NaN or Infinity to `null`. diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 269df0d554..f322119119 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -504,8 +504,18 @@ class binary_writer case value_t::number_float: { - oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); + if (static_cast(j.m_value.number_float) >= static_cast(std::numeric_limits::lowest()) and + static_cast(j.m_value.number_float) <= static_cast((std::numeric_limits::max)()) and + static_cast(static_cast(j.m_value.number_float)) == static_cast(j.m_value.number_float)) + { + oa->write_character(get_msgpack_float_prefix(static_cast(j.m_value.number_float))); + write_number(static_cast(j.m_value.number_float)); + } + else + { + oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); + write_number(j.m_value.number_float); + } break; } diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 409a6e7993..790ecd4b25 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -7040,7 +7040,8 @@ class basic_json number_unsigned | 256..65535 | uint 16 | 0xCD number_unsigned | 65536..4294967295 | uint 32 | 0xCE number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF - number_float | *any value* | float 64 | 0xCB + number_float | *any value representable by a float* | float 32 | 0xCA + number_float | *any value NOT representable by a float* | float 64 | 0xCB string | *length*: 0..31 | fixstr | 0xA0..0xBF string | *length*: 32..255 | str 8 | 0xD9 string | *length*: 256..65535 | str 16 | 0xDA @@ -7064,9 +7065,6 @@ class basic_json - arrays with more than 4294967295 elements - objects with more than 4294967295 elements - @note The following MessagePack types are not used in the conversion: - - float 32 (0xCA) - @note Any MessagePack output created @ref to_msgpack can be successfully parsed by @ref from_msgpack. diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cc822a543b..85fffc7c7e 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -12714,8 +12714,18 @@ class binary_writer case value_t::number_float: { - oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); + if (static_cast(j.m_value.number_float) >= static_cast(std::numeric_limits::lowest()) and + static_cast(j.m_value.number_float) <= static_cast((std::numeric_limits::max)()) and + static_cast(static_cast(j.m_value.number_float)) == static_cast(j.m_value.number_float)) + { + oa->write_character(get_msgpack_float_prefix(static_cast(j.m_value.number_float))); + write_number(static_cast(j.m_value.number_float)); + } + else + { + oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); + write_number(j.m_value.number_float); + } break; } @@ -22821,7 +22831,8 @@ class basic_json number_unsigned | 256..65535 | uint 16 | 0xCD number_unsigned | 65536..4294967295 | uint 32 | 0xCE number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF - number_float | *any value* | float 64 | 0xCB + number_float | *any value representable by a float* | float 32 | 0xCA + number_float | *any value NOT representable by a float* | float 64 | 0xCB string | *length*: 0..31 | fixstr | 0xA0..0xBF string | *length*: 32..255 | str 8 | 0xD9 string | *length*: 256..65535 | str 16 | 0xDA @@ -22845,9 +22856,6 @@ class basic_json - arrays with more than 4294967295 elements - objects with more than 4294967295 elements - @note The following MessagePack types are not used in the conversion: - - float 32 (0xCA) - @note Any MessagePack output created @ref to_msgpack can be successfully parsed by @ref from_msgpack. diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index 2744a67bf7..5baecf0274 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -783,6 +783,40 @@ TEST_CASE("MessagePack") CHECK(json::from_msgpack(result) == v); CHECK(json::from_msgpack(result, true, false) == j); } + + SECTION("1.0") + { + double v = 1.0; + json j = v; + std::vector expected = + { + 0xca, 0x3f, 0x80, 0x00, 0x00 + }; + const auto result = json::to_msgpack(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_msgpack(result) == j); + CHECK(json::from_msgpack(result) == v); + CHECK(json::from_msgpack(result, true, false) == j); + } + + SECTION("128.128") + { + double v = 128.1280059814453125; + json j = v; + std::vector expected = + { + 0xca, 0x43, 0x00, 0x20, 0xc5 + }; + const auto result = json::to_msgpack(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_msgpack(result) == j); + CHECK(json::from_msgpack(result) == v); + CHECK(json::from_msgpack(result, true, false) == j); + } } } From 74520d8bb0aa62374e5c5465b5b0f3b43d75d956 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Wed, 17 Jun 2020 22:03:14 +0200 Subject: [PATCH 16/31] :construction: extend API --- include/nlohmann/detail/input/lexer.hpp | 8 +++ include/nlohmann/detail/input/parser.hpp | 7 ++- include/nlohmann/json.hpp | 63 ++++++++++++------- single_include/nlohmann/json.hpp | 78 +++++++++++++++++------- test/src/unit-class_lexer.cpp | 58 ++++++++++++------ 5 files changed, 148 insertions(+), 66 deletions(-) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index eab64f406d..d5e243e632 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -837,6 +837,7 @@ class lexer : public lexer_base { switch (get()) { + // single-line comments skip input until a newline or EOF is read case '/': { while (true) @@ -845,6 +846,8 @@ class lexer : public lexer_base { case '\n': case '\r': + case std::char_traits::eof(): + case '\0': return true; default: @@ -853,6 +856,7 @@ class lexer : public lexer_base } } + // multi-line comments skip input until */ is read case '*': { while (true) @@ -877,10 +881,14 @@ class lexer : public lexer_base } } } + + default: + break; } } } + // unexpected character after reading '/' default: return false; } diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index 0546b88cb8..c79b492aab 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -63,8 +63,11 @@ class parser /// a parser reading from an input adapter explicit parser(InputAdapterType&& adapter, const parser_callback_t cb = nullptr, - const bool allow_exceptions_ = true) - : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_) + const bool allow_exceptions_ = true, + const bool skip_comments = false) + : callback(cb) + , m_lexer(std::move(adapter), skip_comments) + , allow_exceptions(allow_exceptions_) { // read first token get_token(); diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 409a6e7993..8698a9bb8a 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -196,10 +196,12 @@ class basic_json static ::nlohmann::detail::parser parser( InputAdapterType adapter, detail::parser_callback_tcb = nullptr, - bool allow_exceptions = true + const bool allow_exceptions = true, + const bool ignore_comments = false ) { - return ::nlohmann::detail::parser(std::move(adapter), std::move(cb), allow_exceptions); + return ::nlohmann::detail::parser(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); } using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; @@ -6563,6 +6565,8 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -6591,16 +6595,18 @@ class basic_json @liveexample{The example below demonstrates the `parse()` function reading from a contiguous container.,parse__contiguouscontainer__parser_callback_t} - @since version 2.0.3 (contiguous containers) + @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to + ignore comments. */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json parse(InputType&& i, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions).parse(true, result); + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -6617,6 +6623,8 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -6632,10 +6640,11 @@ class basic_json static basic_json parse(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions).parse(true, result); + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -6643,10 +6652,11 @@ class basic_json JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) static basic_json parse(detail::span_input_adapter&& i, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(i.get(), cb, allow_exceptions).parse(true, result); + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -6666,6 +6676,8 @@ class basic_json iterators. @param[in] i input to read from + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default) @return Whether the input read from @a i is valid JSON. @@ -6678,22 +6690,25 @@ class basic_json from a string.,accept__string} */ template - static bool accept(InputType&& i) + static bool accept(InputType&& i, + const bool ignore_comments = false) { - return parser(detail::input_adapter(std::forward(i))).accept(true); + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments).accept(true); } template - static bool accept(IteratorType first, IteratorType last) + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) { - return parser(detail::input_adapter(std::move(first), std::move(last))).accept(true); + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); } JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) - static bool accept(detail::span_input_adapter&& i) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) { - return parser(i.get()).accept(true); + return parser(i.get(), nullptr, false, ignore_comments).accept(true); } /*! @@ -6713,6 +6728,9 @@ class basic_json @param[in,out] sax SAX event listener @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) @param[in] strict whether the input has to be consumed completely + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default); only applieds to + the JSON file format. @return return value of the last processed SAX event @@ -6737,11 +6755,12 @@ class basic_json JSON_HEDLEY_NON_NULL(2) static bool sax_parse(InputType&& i, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = detail::input_adapter(std::forward(i)); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } @@ -6749,11 +6768,12 @@ class basic_json JSON_HEDLEY_NON_NULL(3) static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = detail::input_adapter(std::move(first), std::move(last)); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } @@ -6762,11 +6782,12 @@ class basic_json JSON_HEDLEY_NON_NULL(2) static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = i.get(); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index bdd97a145a..099fdd8eaf 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -8904,6 +8904,7 @@ class lexer : public lexer_base { switch (get()) { + // single-line comments skip input until a newline or EOF is read case '/': { while (true) @@ -8912,6 +8913,8 @@ class lexer : public lexer_base { case '\n': case '\r': + case std::char_traits::eof(): + case '\0': return true; default: @@ -8920,6 +8923,7 @@ class lexer : public lexer_base } } + // multi-line comments skip input until */ is read case '*': { while (true) @@ -8944,10 +8948,14 @@ class lexer : public lexer_base } } } + + default: + break; } } } + // unexpected character after reading '/' default: return false; } @@ -9742,8 +9750,11 @@ class parser /// a parser reading from an input adapter explicit parser(InputAdapterType&& adapter, const parser_callback_t cb = nullptr, - const bool allow_exceptions_ = true) - : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_) + const bool allow_exceptions_ = true, + const bool skip_comments = false) + : callback(cb) + , m_lexer(std::move(adapter), skip_comments) + , allow_exceptions(allow_exceptions_) { // read first token get_token(); @@ -16051,10 +16062,12 @@ class basic_json static ::nlohmann::detail::parser parser( InputAdapterType adapter, detail::parser_callback_tcb = nullptr, - bool allow_exceptions = true + const bool allow_exceptions = true, + const bool ignore_comments = false ) { - return ::nlohmann::detail::parser(std::move(adapter), std::move(cb), allow_exceptions); + return ::nlohmann::detail::parser(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); } using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; @@ -22418,6 +22431,8 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -22446,16 +22461,18 @@ class basic_json @liveexample{The example below demonstrates the `parse()` function reading from a contiguous container.,parse__contiguouscontainer__parser_callback_t} - @since version 2.0.3 (contiguous containers) + @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to + ignore comments. */ template JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json parse(InputType&& i, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions).parse(true, result); + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -22472,6 +22489,8 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -22487,10 +22506,11 @@ class basic_json static basic_json parse(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions).parse(true, result); + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -22498,10 +22518,11 @@ class basic_json JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) static basic_json parse(detail::span_input_adapter&& i, const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) + const bool allow_exceptions = true, + const bool ignore_comments = false) { basic_json result; - parser(i.get(), cb, allow_exceptions).parse(true, result); + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); return result; } @@ -22521,6 +22542,8 @@ class basic_json iterators. @param[in] i input to read from + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default) @return Whether the input read from @a i is valid JSON. @@ -22533,22 +22556,25 @@ class basic_json from a string.,accept__string} */ template - static bool accept(InputType&& i) + static bool accept(InputType&& i, + const bool ignore_comments = false) { - return parser(detail::input_adapter(std::forward(i))).accept(true); + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments).accept(true); } template - static bool accept(IteratorType first, IteratorType last) + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) { - return parser(detail::input_adapter(std::move(first), std::move(last))).accept(true); + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); } JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) - static bool accept(detail::span_input_adapter&& i) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) { - return parser(i.get()).accept(true); + return parser(i.get(), nullptr, false, ignore_comments).accept(true); } /*! @@ -22568,6 +22594,9 @@ class basic_json @param[in,out] sax SAX event listener @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) @param[in] strict whether the input has to be consumed completely + @param[in] ignore_comments whether comments should be ignored (true) or + yield a parse error (true); (optional, false by default); only applieds to + the JSON file format. @return return value of the last processed SAX event @@ -22592,11 +22621,12 @@ class basic_json JSON_HEDLEY_NON_NULL(2) static bool sax_parse(InputType&& i, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = detail::input_adapter(std::forward(i)); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } @@ -22604,11 +22634,12 @@ class basic_json JSON_HEDLEY_NON_NULL(3) static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = detail::input_adapter(std::move(first), std::move(last)); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } @@ -22617,11 +22648,12 @@ class basic_json JSON_HEDLEY_NON_NULL(2) static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, input_format_t format = input_format_t::json, - const bool strict = true) + const bool strict = true, + const bool ignore_comments = false) { auto ia = i.get(); return format == input_format_t::json - ? parser(std::move(ia)).sax_parse(sax, strict) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index c4423e6023..aee4703fcd 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -37,11 +37,11 @@ using nlohmann::json; namespace { // shortcut to scan a string literal -json::lexer::token_type scan_string(const char* s); -json::lexer::token_type scan_string(const char* s) +json::lexer::token_type scan_string(const char* s, const bool ignore_comments = false); +json::lexer::token_type scan_string(const char* s, const bool ignore_comments) { auto ia = nlohmann::detail::input_adapter(s); - return nlohmann::detail::lexer(std::move(ia)).scan(); + return nlohmann::detail::lexer(std::move(ia), ignore_comments).scan(); } } @@ -163,9 +163,6 @@ TEST_CASE("lexer class") break; } - // case ('/'): - // break; - // anything else is not expected default: { @@ -185,18 +182,39 @@ TEST_CASE("lexer class") CHECK((scan_string(s.c_str()) == json::lexer::token_type::value_string)); } - // SECTION("ignore comments") - // { - // CHECK((scan_string("/") == json::lexer::token_type::parse_error)); - // - // CHECK((scan_string("/!") == json::lexer::token_type::parse_error)); - // CHECK((scan_string("/*") == json::lexer::token_type::parse_error)); - // CHECK((scan_string("/**") == json::lexer::token_type::parse_error)); - // - // CHECK((scan_string("//") == json::lexer::token_type::end_of_input)); - // CHECK((scan_string("/**/") == json::lexer::token_type::end_of_input)); - // CHECK((scan_string("/** /") == json::lexer::token_type::parse_error)); - // - // CHECK((scan_string("/***/") == json::lexer::token_type::end_of_input)); - // } + SECTION("fail on comments") + { + CHECK((scan_string("/", false) == json::lexer::token_type::parse_error)); + + CHECK((scan_string("/!", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/*", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/**", false) == json::lexer::token_type::parse_error)); + + CHECK((scan_string("//", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/**/", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/** /", false) == json::lexer::token_type::parse_error)); + + CHECK((scan_string("/***/", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/* true */", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/*/**/", false) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/*/* */", false) == json::lexer::token_type::parse_error)); + } + + SECTION("ignore comments") + { + CHECK((scan_string("/", true) == json::lexer::token_type::parse_error)); + + CHECK((scan_string("/!", true) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/*", true) == json::lexer::token_type::parse_error)); + CHECK((scan_string("/**", true) == json::lexer::token_type::parse_error)); + + CHECK((scan_string("//", true) == json::lexer::token_type::end_of_input)); + CHECK((scan_string("/**/", true) == json::lexer::token_type::end_of_input)); + CHECK((scan_string("/** /", true) == json::lexer::token_type::parse_error)); + + CHECK((scan_string("/***/", true) == json::lexer::token_type::end_of_input)); + CHECK((scan_string("/* true */", true) == json::lexer::token_type::end_of_input)); + CHECK((scan_string("/*/**/", true) == json::lexer::token_type::end_of_input)); + CHECK((scan_string("/*/* */", true) == json::lexer::token_type::end_of_input)); + } } From cd115cbc33d074e231d3a703c8120b72223cb1e4 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 18 Jun 2020 12:50:32 +0200 Subject: [PATCH 17/31] :white_check_mark: update test suite --- cmake/download_test_data.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/download_test_data.cmake b/cmake/download_test_data.cmake index fe95235f89..b023d8b458 100644 --- a/cmake/download_test_data.cmake +++ b/cmake/download_test_data.cmake @@ -1,7 +1,7 @@ find_package(Git) set(JSON_TEST_DATA_URL https://github.com/nlohmann/json_test_data) -set(JSON_TEST_DATA_VERSION 2.0.0) +set(JSON_TEST_DATA_VERSION 3.0.0) # target to download test data add_custom_target(download_test_data From 0585ecc56b210e045e639c41474cc72c475c8b76 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 19 Jun 2020 13:10:35 +0200 Subject: [PATCH 18/31] :white_check_mark: add tests for comment skipping --- include/nlohmann/detail/input/lexer.hpp | 8 ++++- single_include/nlohmann/json.hpp | 8 ++++- test/src/unit-class_parser.cpp | 45 ++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index d5e243e632..9dba2972c3 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -1507,7 +1507,13 @@ class lexer : public lexer_base error_message = "invalid comment"; return token_type::parse_error; } - get(); + + // skip following whitespace + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); } switch (current) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 099fdd8eaf..c341d40d27 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -9574,7 +9574,13 @@ class lexer : public lexer_base error_message = "invalid comment"; return token_type::parse_error; } - get(); + + // skip following whitespace + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); } switch (current) diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 1912094aad..de5c963858 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -224,6 +224,7 @@ class SaxCountdown : public nlohmann::json::json_sax_t json parser_helper(const std::string& s); bool accept_helper(const std::string& s); +void comments_helper(const std::string& s); json parser_helper(const std::string& s) { @@ -241,6 +242,8 @@ json parser_helper(const std::string& s) json::sax_parse(s, &sdp); CHECK(j_sax == j); + comments_helper(s); + return j; } @@ -275,11 +278,51 @@ bool accept_helper(const std::string& s) // 6. check if this approach came to the same result CHECK(ok_noexcept == ok_noexcept_cb); - // 7. return result + // 7. check if comments are properly ignored + if (ok_accept) + { + comments_helper(s); + } + + // 8. return result return ok_accept; } + +void comments_helper(const std::string& s) +{ + json _; + + // parse/accept with default parser + CHECK_NOTHROW(_ = json::parse(s)); + CHECK(json::accept(s)); + + // parse/accept while skipping comments + CHECK_NOTHROW(_ = json::parse(s, nullptr, false, true)); + CHECK(json::accept(s, true)); + + std::vector json_with_comments; + + // start with a comment + json_with_comments.push_back(std::string("// this is a comment\n") + s); + json_with_comments.push_back(std::string("/* this is a comment */") + s); + // end with a comment + json_with_comments.push_back(s + "// this is a comment"); + json_with_comments.push_back(s + "/* this is a comment */"); + + // check all strings + for (const auto& json_with_comment : json_with_comments) + { + CAPTURE(json_with_comment) + CHECK_THROWS_AS(_ = json::parse(json_with_comment), json::parse_error); + CHECK(not json::accept(json_with_comment)); + + CHECK_NOTHROW(_ = json::parse(json_with_comment, nullptr, true, true)); + CHECK(json::accept(json_with_comment, true)); + } } +} // namespace + TEST_CASE("parser class") { SECTION("parse") From b64002bbca497e79f56466ced8f99f90ca9cf347 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 19 Jun 2020 13:24:08 +0200 Subject: [PATCH 19/31] :recycle: extract common code to function --- .../nlohmann/detail/output/binary_writer.hpp | 47 +++++++++---------- single_include/nlohmann/json.hpp | 47 +++++++++---------- 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index f322119119..7289da3dce 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -28,6 +28,7 @@ class binary_writer { using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; public: /*! @@ -194,18 +195,7 @@ class binary_writer } else { - if (static_cast(j.m_value.number_float) >= static_cast(std::numeric_limits::lowest()) and - static_cast(j.m_value.number_float) <= static_cast((std::numeric_limits::max)()) and - static_cast(static_cast(j.m_value.number_float)) == static_cast(j.m_value.number_float)) - { - oa->write_character(get_cbor_float_prefix(static_cast(j.m_value.number_float))); - write_number(static_cast(j.m_value.number_float)); - } - else - { - oa->write_character(get_cbor_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); - } + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); } break; } @@ -504,18 +494,7 @@ class binary_writer case value_t::number_float: { - if (static_cast(j.m_value.number_float) >= static_cast(std::numeric_limits::lowest()) and - static_cast(j.m_value.number_float) <= static_cast((std::numeric_limits::max)()) and - static_cast(static_cast(j.m_value.number_float)) == static_cast(j.m_value.number_float)) - { - oa->write_character(get_msgpack_float_prefix(static_cast(j.m_value.number_float))); - write_number(static_cast(j.m_value.number_float)); - } - else - { - oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); - } + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); break; } @@ -1528,6 +1507,26 @@ class binary_writer oa->write_characters(vec.data(), sizeof(NumberType)); } + void write_compact_float(const number_float_t n, detail::input_format_t format) + { + if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) and + static_cast(n) <= static_cast((std::numeric_limits::max)()) and + static_cast(static_cast(n)) == static_cast(n)) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast(n)) + : get_msgpack_float_prefix(static_cast(n))); + write_number(static_cast(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } + } + public: // The following to_char_type functions are implement the conversion // between uint8_t and CharType. In case CharType is not unsigned, diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 85fffc7c7e..33d0c35776 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -12238,6 +12238,7 @@ class binary_writer { using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; public: /*! @@ -12404,18 +12405,7 @@ class binary_writer } else { - if (static_cast(j.m_value.number_float) >= static_cast(std::numeric_limits::lowest()) and - static_cast(j.m_value.number_float) <= static_cast((std::numeric_limits::max)()) and - static_cast(static_cast(j.m_value.number_float)) == static_cast(j.m_value.number_float)) - { - oa->write_character(get_cbor_float_prefix(static_cast(j.m_value.number_float))); - write_number(static_cast(j.m_value.number_float)); - } - else - { - oa->write_character(get_cbor_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); - } + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); } break; } @@ -12714,18 +12704,7 @@ class binary_writer case value_t::number_float: { - if (static_cast(j.m_value.number_float) >= static_cast(std::numeric_limits::lowest()) and - static_cast(j.m_value.number_float) <= static_cast((std::numeric_limits::max)()) and - static_cast(static_cast(j.m_value.number_float)) == static_cast(j.m_value.number_float)) - { - oa->write_character(get_msgpack_float_prefix(static_cast(j.m_value.number_float))); - write_number(static_cast(j.m_value.number_float)); - } - else - { - oa->write_character(get_msgpack_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); - } + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); break; } @@ -13738,6 +13717,26 @@ class binary_writer oa->write_characters(vec.data(), sizeof(NumberType)); } + void write_compact_float(const number_float_t n, detail::input_format_t format) + { + if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) and + static_cast(n) <= static_cast((std::numeric_limits::max)()) and + static_cast(static_cast(n)) == static_cast(n)) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast(n)) + : get_msgpack_float_prefix(static_cast(n))); + write_number(static_cast(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } + } + public: // The following to_char_type functions are implement the conversion // between uint8_t and CharType. In case CharType is not unsigned, From 24992003d99fefdd9fb5a605f0eb58a05a91985b Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 20 Jun 2020 09:55:11 +0200 Subject: [PATCH 20/31] :memo: add notes from #2189 --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d7492da34f..42d057a35b 100644 --- a/README.md +++ b/README.md @@ -1557,4 +1557,6 @@ $ cmake --build . $ ctest --output-on-failure ``` -For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml). +Note that during the `ctest` stage, several JSON test files are downloaded from an [external repository](https://github.com/nlohmann/json_test_data). If policies forbid downloading artifacts during testing, you can download the files yourself and pass the directory with the test files via `-DJSON_TestDataDirectory=path` to CMake. Then, no Internet connectivity is required. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information. + +In case you have downloaded the library rather than checked out the code via Git, test `cmake_fetch_content_configure`. Please execute `ctest -LE git_required` to skip these tests. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information. From f0e73163f242cd631848d007a3765fe6b94c5bcd Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Sat, 20 Jun 2020 15:25:54 +0200 Subject: [PATCH 21/31] json_pointer.hpp: Mention more exception in documentation Forgotten in dcd3a6c6 (move the catch of std::invalid_argument into array_index(), 2020-03-23). --- include/nlohmann/detail/json_pointer.hpp | 2 ++ single_include/nlohmann/json.hpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 421c5ec8fe..18b66e2345 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -325,6 +325,8 @@ class json_pointer @return integer representation of @a s + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit @throw out_of_range.404 if string @a s could not be converted to an integer */ static int array_index(const std::string& s) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 33d0c35776..22fc0e3300 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -11357,6 +11357,8 @@ class json_pointer @return integer representation of @a s + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit @throw out_of_range.404 if string @a s could not be converted to an integer */ static int array_index(const std::string& s) From e22ce4506564f3630e4cd63dc0795115df90be71 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sun, 21 Jun 2020 13:28:00 +0200 Subject: [PATCH 22/31] :children_crossing: improve diagnostics --- include/nlohmann/detail/input/lexer.hpp | 7 ++++++- single_include/nlohmann/json.hpp | 7 ++++++- test/src/unit-class_lexer.cpp | 25 +++++++++++++++++++++++++ test/src/unit-class_parser.cpp | 6 ++++++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 9dba2972c3..580b1c2250 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -865,7 +865,10 @@ class lexer : public lexer_base { case std::char_traits::eof(): case '\0': + { + error_message = "invalid comment; missing closing '*/'"; return false; + } case '*': { @@ -890,7 +893,10 @@ class lexer : public lexer_base // unexpected character after reading '/' default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; return false; + } } } @@ -1504,7 +1510,6 @@ class lexer : public lexer_base { if (not scan_comment()) { - error_message = "invalid comment"; return token_type::parse_error; } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f4fe864290..cdc3de0950 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -8932,7 +8932,10 @@ class lexer : public lexer_base { case std::char_traits::eof(): case '\0': + { + error_message = "invalid comment; missing closing '*/'"; return false; + } case '*': { @@ -8957,7 +8960,10 @@ class lexer : public lexer_base // unexpected character after reading '/' default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; return false; + } } } @@ -9571,7 +9577,6 @@ class lexer : public lexer_base { if (not scan_comment()) { - error_message = "invalid comment"; return token_type::parse_error; } diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index aee4703fcd..d8304ccf17 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -45,6 +45,15 @@ json::lexer::token_type scan_string(const char* s, const bool ignore_comments) } } +std::string get_error_message(const char* s, const bool ignore_comments = false); +std::string get_error_message(const char* s, const bool ignore_comments) +{ + auto ia = nlohmann::detail::input_adapter(s); + auto lexer = nlohmann::detail::lexer(std::move(ia), ignore_comments); + lexer.scan(); + return lexer.get_error_message(); +} + TEST_CASE("lexer class") { SECTION("scan") @@ -185,32 +194,48 @@ TEST_CASE("lexer class") SECTION("fail on comments") { CHECK((scan_string("/", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/", false) == "invalid literal"); CHECK((scan_string("/!", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/!", false) == "invalid literal"); CHECK((scan_string("/*", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/*", false) == "invalid literal"); CHECK((scan_string("/**", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/**", false) == "invalid literal"); CHECK((scan_string("//", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("//", false) == "invalid literal"); CHECK((scan_string("/**/", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/**/", false) == "invalid literal"); CHECK((scan_string("/** /", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/** /", false) == "invalid literal"); CHECK((scan_string("/***/", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/***/", false) == "invalid literal"); CHECK((scan_string("/* true */", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/* true */", false) == "invalid literal"); CHECK((scan_string("/*/**/", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/*/**/", false) == "invalid literal"); CHECK((scan_string("/*/* */", false) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/*/* */", false) == "invalid literal"); } SECTION("ignore comments") { CHECK((scan_string("/", true) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/", true) == "invalid comment; expecting '/' or '*' after '/'"); CHECK((scan_string("/!", true) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/!", true) == "invalid comment; expecting '/' or '*' after '/'"); CHECK((scan_string("/*", true) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/*", true) == "invalid comment; missing closing '*/'"); CHECK((scan_string("/**", true) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/**", true) == "invalid comment; missing closing '*/'"); CHECK((scan_string("//", true) == json::lexer::token_type::end_of_input)); CHECK((scan_string("/**/", true) == json::lexer::token_type::end_of_input)); CHECK((scan_string("/** /", true) == json::lexer::token_type::parse_error)); + CHECK(get_error_message("/** /", true) == "invalid comment; missing closing '*/'"); CHECK((scan_string("/***/", true) == json::lexer::token_type::end_of_input)); CHECK((scan_string("/* true */", true) == json::lexer::token_type::end_of_input)); diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index de5c963858..da16ffcab7 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -1877,4 +1877,10 @@ TEST_CASE("parser class") } } } + + SECTION("error messages for comments") + { + CHECK_THROWS_WITH_AS(json::parse("/a", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid comment; expecting '/' or '*' after '/'; last read: '/a'", json::parse_error); + CHECK_THROWS_WITH_AS(json::parse("/*", nullptr, true, true), "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid comment; missing closing '*/'; last read: '/*'", json::parse_error); + } } From 65e8ee985ae31e73223e6f722c26d6a1fb0563c8 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 22 Jun 2020 08:59:03 +0200 Subject: [PATCH 23/31] :hammer: clean up --- README.md | 8 ++--- include/nlohmann/detail/input/lexer.hpp | 21 +++++++------ include/nlohmann/json.hpp | 21 +++++++------ single_include/nlohmann/json.hpp | 42 ++++++++++++++----------- 4 files changed, 48 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 42d057a35b..0699bdc307 100644 --- a/README.md +++ b/README.md @@ -1508,7 +1508,7 @@ The library supports **Unicode input** as follows: ### Comments in JSON -This library does not support comments. It does so for three reasons: +This library does not support comments by default. It does so for three reasons: 1. Comments are not part of the [JSON specification](https://tools.ietf.org/html/rfc8259). You may argue that `//` or `/* */` are allowed in JavaScript, but JSON is not JavaScript. 2. This was not an oversight: Douglas Crockford [wrote on this](https://plus.google.com/118095276221607585885/posts/RK8qyGVaGSr) in May 2012: @@ -1519,11 +1519,7 @@ This library does not support comments. It does so for three reasons: 3. It is dangerous for interoperability if some libraries would add comment support while others don't. Please check [The Harmful Consequences of the Robustness Principle](https://tools.ietf.org/html/draft-iab-protocol-maintenance-01) on this. -This library will not support comments in the future. If you wish to use comments, I see three options: - -1. Strip comments before using this library. -2. Use a different JSON library with comment support. -3. Use a format that natively supports comments (e.g., YAML or JSON5). +However, you can pass set parameter `ignore_comments` to true in the `parse` function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace. ### Order of object keys diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 580b1c2250..8042f3c4ef 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -1489,6 +1489,15 @@ class lexer : public lexer_base return true; } + void skip_whitespace() + { + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + } + token_type scan() { // initially, skip the BOM @@ -1499,11 +1508,7 @@ class lexer : public lexer_base } // read next character and ignore whitespace - do - { - get(); - } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + skip_whitespace(); // ignore comments if (ignore_comments and current == '/') @@ -1514,11 +1519,7 @@ class lexer : public lexer_base } // skip following whitespace - do - { - get(); - } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + skip_whitespace(); } switch (current) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index f4fa590ca2..cc7e3fbc2a 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -6565,8 +6565,9 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -6623,8 +6624,9 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -6676,8 +6678,9 @@ class basic_json iterators. @param[in] i input to read from - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return Whether the input read from @a i is valid JSON. @@ -6728,9 +6731,9 @@ class basic_json @param[in,out] sax SAX event listener @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) @param[in] strict whether the input has to be consumed completely - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default); only applieds to - the JSON file format. + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default); only applies to the JSON file format. @return return value of the last processed SAX event diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index cdc3de0950..683e2d5608 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -9556,6 +9556,15 @@ class lexer : public lexer_base return true; } + void skip_whitespace() + { + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + } + token_type scan() { // initially, skip the BOM @@ -9566,11 +9575,7 @@ class lexer : public lexer_base } // read next character and ignore whitespace - do - { - get(); - } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + skip_whitespace(); // ignore comments if (ignore_comments and current == '/') @@ -9581,11 +9586,7 @@ class lexer : public lexer_base } // skip following whitespace - do - { - get(); - } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + skip_whitespace(); } switch (current) @@ -22451,8 +22452,9 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -22509,8 +22511,9 @@ class basic_json (optional) @param[in] allow_exceptions whether to throw exceptions in case of a parse error (optional, true by default) - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return deserialized JSON value; in case of a parse error and @a allow_exceptions set to `false`, the return value will be @@ -22562,8 +22565,9 @@ class basic_json iterators. @param[in] i input to read from - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) @return Whether the input read from @a i is valid JSON. @@ -22614,9 +22618,9 @@ class basic_json @param[in,out] sax SAX event listener @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) @param[in] strict whether the input has to be consumed completely - @param[in] ignore_comments whether comments should be ignored (true) or - yield a parse error (true); (optional, false by default); only applieds to - the JSON file format. + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default); only applies to the JSON file format. @return return value of the last processed SAX event From ecbb2756fd7629a8dba64be581e19488d0be3061 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Sat, 20 Jun 2020 15:36:28 +0200 Subject: [PATCH 24/31] json_pointer::array_index: Use unsigned values for the array index when parsing The current code uses std::stoi to convert the input string to an int array_index. This limits the maximum addressable array size to ~2GB on most platforms. But all callers immediately convert the result of array_index to BasicJsonType::size_type. So let's parse it as unsigned long long, which allows us to have as big arrays as available memory. And also makes the call sites nicer to read. One complication arises on platforms where size_type is smaller than unsigned long long. We need to bail out on these if the parsed array index does not fit into size_type. --- include/nlohmann/detail/json_pointer.hpp | 33 +++++++++++++-------- include/nlohmann/json.hpp | 4 +-- single_include/nlohmann/json.hpp | 37 +++++++++++++++--------- test/src/unit-json_pointer.cpp | 29 +++++++++++++++---- 4 files changed, 69 insertions(+), 34 deletions(-) diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 18b66e2345..5628029200 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -3,6 +3,7 @@ #include // all_of #include // assert #include // isdigit +#include // max #include // accumulate #include // string #include // move @@ -328,9 +329,12 @@ class json_pointer @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index begins not with a digit @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type */ - static int array_index(const std::string& s) + static typename BasicJsonType::size_type array_index(const std::string& s) { + using size_type = typename BasicJsonType::size_type; + // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0')) { @@ -346,10 +350,10 @@ class json_pointer } std::size_t processed_chars = 0; - int res = 0; + unsigned long long res = 0; JSON_TRY { - res = std::stoi(s, &processed_chars); + res = std::stoull(s, &processed_chars); } JSON_CATCH(std::out_of_range&) { @@ -362,7 +366,14 @@ class json_pointer JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); } - return res; + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast((std::numeric_limits::max)())) + { + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE + } + + return static_cast(res); } json_pointer top() const @@ -421,7 +432,7 @@ class json_pointer case detail::value_t::array: { // create an entry in the array - result = &result->operator[](static_cast(array_index(reference_token))); + result = &result->operator[](array_index(reference_token)); break; } @@ -499,8 +510,7 @@ class json_pointer else { // convert array index to number; unchecked access - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); + ptr = &ptr->operator[](array_index(reference_token)); } break; } @@ -544,7 +554,7 @@ class json_pointer } // note: at performs range check - ptr = &ptr->at(static_cast(array_index(reference_token))); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -594,8 +604,7 @@ class json_pointer } // use unchecked array access - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); + ptr = &ptr->operator[](array_index(reference_token)); break; } @@ -638,7 +647,7 @@ class json_pointer } // note: at performs range check - ptr = &ptr->at(static_cast(array_index(reference_token))); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -702,7 +711,7 @@ class json_pointer } } - const auto idx = static_cast(array_index(reference_token)); + const auto idx = array_index(reference_token); if (idx >= ptr->size()) { // index out of range diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 790ecd4b25..048307b3a9 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -8179,7 +8179,7 @@ class basic_json else { const auto idx = json_pointer::array_index(last_path); - if (JSON_HEDLEY_UNLIKELY(static_cast(idx) > parent.size())) + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); @@ -8222,7 +8222,7 @@ class basic_json else if (parent.is_array()) { // note erase performs range check - parent.erase(static_cast(json_pointer::array_index(last_path))); + parent.erase(json_pointer::array_index(last_path)); } }; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 22fc0e3300..b919d2318e 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -11032,6 +11032,7 @@ class json_reverse_iterator : public std::reverse_iterator #include // all_of #include // assert #include // isdigit +#include // max #include // accumulate #include // string #include // move @@ -11360,9 +11361,12 @@ class json_pointer @throw parse_error.106 if an array index begins with '0' @throw parse_error.109 if an array index begins not with a digit @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type */ - static int array_index(const std::string& s) + static typename BasicJsonType::size_type array_index(const std::string& s) { + using size_type = typename BasicJsonType::size_type; + // error condition (cf. RFC 6901, Sect. 4) if (JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0')) { @@ -11378,10 +11382,10 @@ class json_pointer } std::size_t processed_chars = 0; - int res = 0; + unsigned long long res = 0; JSON_TRY { - res = std::stoi(s, &processed_chars); + res = std::stoull(s, &processed_chars); } JSON_CATCH(std::out_of_range&) { @@ -11394,7 +11398,14 @@ class json_pointer JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); } - return res; + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast((std::numeric_limits::max)())) + { + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE + } + + return static_cast(res); } json_pointer top() const @@ -11453,7 +11464,7 @@ class json_pointer case detail::value_t::array: { // create an entry in the array - result = &result->operator[](static_cast(array_index(reference_token))); + result = &result->operator[](array_index(reference_token)); break; } @@ -11531,8 +11542,7 @@ class json_pointer else { // convert array index to number; unchecked access - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); + ptr = &ptr->operator[](array_index(reference_token)); } break; } @@ -11576,7 +11586,7 @@ class json_pointer } // note: at performs range check - ptr = &ptr->at(static_cast(array_index(reference_token))); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -11626,8 +11636,7 @@ class json_pointer } // use unchecked array access - ptr = &ptr->operator[]( - static_cast(array_index(reference_token))); + ptr = &ptr->operator[](array_index(reference_token)); break; } @@ -11670,7 +11679,7 @@ class json_pointer } // note: at performs range check - ptr = &ptr->at(static_cast(array_index(reference_token))); + ptr = &ptr->at(array_index(reference_token)); break; } @@ -11734,7 +11743,7 @@ class json_pointer } } - const auto idx = static_cast(array_index(reference_token)); + const auto idx = array_index(reference_token); if (idx >= ptr->size()) { // index out of range @@ -23971,7 +23980,7 @@ class basic_json else { const auto idx = json_pointer::array_index(last_path); - if (JSON_HEDLEY_UNLIKELY(static_cast(idx) > parent.size())) + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) { // avoid undefined behavior JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); @@ -24014,7 +24023,7 @@ class basic_json else if (parent.is_array()) { // note erase performs range check - parent.erase(static_cast(json_pointer::array_index(last_path))); + parent.erase(json_pointer::array_index(last_path)); } }; diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp index ca11efa428..89a41d76b4 100644 --- a/test/src/unit-json_pointer.cpp +++ b/test/src/unit-json_pointer.cpp @@ -348,12 +348,29 @@ TEST_CASE("JSON pointers") CHECK_THROWS_WITH(j_const["/1+1"_json_pointer] == 1, "[json.exception.out_of_range.404] unresolved reference token '1+1'"); - CHECK_THROWS_AS(j["/111111111111111111111111"_json_pointer] = 1, json::out_of_range&); - CHECK_THROWS_WITH(j["/111111111111111111111111"_json_pointer] = 1, - "[json.exception.out_of_range.404] unresolved reference token '111111111111111111111111'"); - CHECK_THROWS_AS(j_const["/111111111111111111111111"_json_pointer] == 1, json::out_of_range&); - CHECK_THROWS_WITH(j_const["/111111111111111111111111"_json_pointer] == 1, - "[json.exception.out_of_range.404] unresolved reference token '111111111111111111111111'"); + { + auto too_large_index = std::to_string((std::numeric_limits::max)()) + "1"; + json::json_pointer jp(std::string("/") + too_large_index); + std::string throw_msg = std::string("[json.exception.out_of_range.404] unresolved reference token '") + too_large_index + "'"; + + CHECK_THROWS_AS(j[jp] = 1, json::out_of_range&); + CHECK_THROWS_WITH(j[jp] = 1, throw_msg.c_str()); + CHECK_THROWS_AS(j_const[jp] == 1, json::out_of_range&); + CHECK_THROWS_WITH(j_const[jp] == 1, throw_msg.c_str()); + } + + if (sizeof(typename json::size_type) < sizeof(unsigned long long)) + { + auto size_type_max_uul = static_cast((std::numeric_limits::max)()); + auto too_large_index = std::to_string(size_type_max_uul); + json::json_pointer jp(std::string("/") + too_large_index); + std::string throw_msg = std::string("[json.exception.out_of_range.410] array index ") + too_large_index + " exceeds size_type"; + + CHECK_THROWS_AS(j[jp] = 1, json::out_of_range&); + CHECK_THROWS_WITH(j[jp] = 1, throw_msg.c_str()); + CHECK_THROWS_AS(j_const[jp] == 1, json::out_of_range&); + CHECK_THROWS_WITH(j_const[jp] == 1, throw_msg.c_str()); + } CHECK_THROWS_AS(j.at("/one"_json_pointer) = 1, json::parse_error&); CHECK_THROWS_WITH(j.at("/one"_json_pointer) = 1, From aeef50709e6a49f5807cdf787473ca8adecbe23c Mon Sep 17 00:00:00 2001 From: chenguoping Date: Mon, 22 Jun 2020 20:17:56 +0800 Subject: [PATCH 25/31] to allow for ADL in int_to_string() function --- include/nlohmann/detail/iterators/iteration_proxy.hpp | 4 +++- single_include/nlohmann/json.hpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/iterators/iteration_proxy.hpp b/include/nlohmann/detail/iterators/iteration_proxy.hpp index 48927b0183..d19be4ddd6 100644 --- a/include/nlohmann/detail/iterators/iteration_proxy.hpp +++ b/include/nlohmann/detail/iterators/iteration_proxy.hpp @@ -16,7 +16,9 @@ template::value, int>::type = 0> void int_to_string( string_type& target, std::size_t value ) { - target = std::to_string(value); + // For ADL + using std::to_string; + target = to_string(value); } template class iteration_proxy_value { diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 69a4ec0921..58b12a7df1 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3654,7 +3654,9 @@ template::value, int>::type = 0> void int_to_string( string_type& target, std::size_t value ) { - target = std::to_string(value); + // For ADL + using std::to_string; + target = to_string(value); } template class iteration_proxy_value { From 8b3d2399a487a3d5c52a09d258d9df83e7a6066e Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 22 Jun 2020 22:32:21 +0200 Subject: [PATCH 26/31] :rotating_light: remove warnings --- .clang-tidy | 27 +- .../nlohmann/detail/input/binary_reader.hpp | 166 ++++++------ .../nlohmann/detail/input/input_adapters.hpp | 4 +- include/nlohmann/detail/input/json_sax.hpp | 20 +- include/nlohmann/detail/input/lexer.hpp | 2 +- .../detail/iterators/internal_iterator.hpp | 2 - include/nlohmann/detail/json_ref.hpp | 19 +- .../nlohmann/detail/output/binary_writer.hpp | 26 +- include/nlohmann/detail/output/serializer.hpp | 8 +- single_include/nlohmann/json.hpp | 247 +++++++++--------- 10 files changed, 250 insertions(+), 271 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index feee819451..046d84f870 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,23 +1,20 @@ -Checks: '-*, - bugprone-*, - cert-*, - clang-analyzer-*, - google-*, - -google-runtime-references, +Checks: '*, + -cppcoreguidelines-avoid-goto, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-macro-usage, + -fuchsia-default-arguments-calls, + -fuchsia-default-arguments-declarations, + -fuchsia-overloaded-operator, -google-explicit-constructor, - hicpp-*, + -google-runtime-references, + -hicpp-avoid-goto, + -hicpp-explicit-conversions, -hicpp-no-array-decay, -hicpp-uppercase-literal-suffix, - -hicpp-explicit-conversions, - misc-*, - -misc-non-private-member-variables-in-classes, - llvm-*, -llvm-header-guard, - modernize-*, + -llvm-include-order, + -misc-non-private-member-variables-in-classes, -modernize-use-trailing-return-type, - performance-*, - portability-*, - readability-*, -readability-magic-numbers, -readability-uppercase-literal-suffix' diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index 93667e8f66..a27a153875 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -145,7 +145,7 @@ class binary_reader */ bool parse_bson_internal() { - std::int32_t document_size; + std::int32_t document_size{}; get_number(input_format_t::bson, document_size); if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) @@ -184,8 +184,6 @@ class binary_reader } *out++ = static_cast(current); } - - return true; } /*! @@ -230,7 +228,7 @@ class binary_reader } // All BSON binary values have a subtype - std::uint8_t subtype; + std::uint8_t subtype{}; get_number(input_format_t::bson, subtype); result.set_subtype(subtype); @@ -254,13 +252,13 @@ class binary_reader { case 0x01: // double { - double number; + double number{}; return get_number(input_format_t::bson, number) and sax->number_float(static_cast(number), ""); } case 0x02: // string { - std::int32_t len; + std::int32_t len{}; string_t value; return get_number(input_format_t::bson, len) and get_bson_string(len, value) and sax->string(value); } @@ -277,7 +275,7 @@ class binary_reader case 0x05: // binary { - std::int32_t len; + std::int32_t len{}; binary_t value; return get_number(input_format_t::bson, len) and get_bson_binary(len, value) and sax->binary(value); } @@ -294,13 +292,13 @@ class binary_reader case 0x10: // int32 { - std::int32_t value; + std::int32_t value{}; return get_number(input_format_t::bson, value) and sax->number_integer(value); } case 0x12: // int64 { - std::int64_t value; + std::int64_t value{}; return get_number(input_format_t::bson, value) and sax->number_integer(value); } @@ -365,7 +363,7 @@ class binary_reader */ bool parse_bson_array() { - std::int32_t document_size; + std::int32_t document_size{}; get_number(input_format_t::bson, document_size); if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) @@ -429,25 +427,25 @@ class binary_reader case 0x18: // Unsigned integer (one-byte uint8_t follows) { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } case 0x19: // Unsigned integer (two-byte uint16_t follows) { - std::uint16_t number; + std::uint16_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } case 0x1A: // Unsigned integer (four-byte uint32_t follows) { - std::uint32_t number; + std::uint32_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } case 0x1B: // Unsigned integer (eight-byte uint64_t follows) { - std::uint64_t number; + std::uint64_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } @@ -480,25 +478,25 @@ class binary_reader case 0x38: // Negative integer (one-byte uint8_t follows) { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); } case 0x39: // Negative integer -1-n (two-byte uint16_t follows) { - std::uint16_t number; + std::uint16_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); } case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) { - std::uint32_t number; + std::uint32_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); } case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) { - std::uint64_t number; + std::uint64_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - static_cast(number)); } @@ -602,25 +600,25 @@ class binary_reader case 0x98: // array (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } case 0x99: // array (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } case 0x9A: // array (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } case 0x9B: // array (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } @@ -656,25 +654,25 @@ class binary_reader case 0xB8: // map (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } case 0xB9: // map (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } case 0xBA: // map (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } case 0xBB: // map (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } @@ -740,13 +738,13 @@ class binary_reader case 0xFA: // Single-Precision Float (four-byte IEEE 754) { - float number; + float number{}; return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); } case 0xFB: // Double-Precision Float (eight-byte IEEE 754) { - double number; + double number{}; return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); } @@ -809,25 +807,25 @@ class binary_reader case 0x78: // UTF-8 string (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } case 0x79: // UTF-8 string (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } @@ -904,28 +902,28 @@ class binary_reader case 0x58: // Binary data (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; 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; + std::uint16_t len{}; 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; + std::uint32_t len{}; 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; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); } @@ -1290,85 +1288,85 @@ class binary_reader case 0xCA: // float 32 { - float number; + float number{}; return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); } case 0xCB: // float 64 { - double number; + double number{}; return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); } case 0xCC: // uint 8 { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xCD: // uint 16 { - std::uint16_t number; + std::uint16_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xCE: // uint 32 { - std::uint32_t number; + std::uint32_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xCF: // uint 64 { - std::uint64_t number; + std::uint64_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xD0: // int 8 { - std::int8_t number; + std::int8_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xD1: // int 16 { - std::int16_t number; + std::int16_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xD2: // int 32 { - std::int32_t number; + std::int32_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xD3: // int 64 { - std::int64_t number; + std::int64_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xDC: // array 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); } case 0xDD: // array 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); } case 0xDE: // map 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); } case 0xDF: // map 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); } @@ -1473,19 +1471,19 @@ class binary_reader case 0xD9: // str 8 { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); } case 0xDA: // str 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); } case 0xDB: // str 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); } @@ -1520,29 +1518,29 @@ class binary_reader { case 0xC4: // bin 8 { - std::uint8_t len; + std::uint8_t len{}; 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; + std::uint16_t len{}; 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; + std::uint32_t len{}; 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; - std::int8_t subtype; + std::uint8_t len{}; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) and get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, len, result) and @@ -1551,8 +1549,8 @@ class binary_reader case 0xC8: // ext 16 { - std::uint16_t len; - std::int8_t subtype; + std::uint16_t len{}; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) and get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, len, result) and @@ -1561,8 +1559,8 @@ class binary_reader case 0xC9: // ext 32 { - std::uint32_t len; - std::int8_t subtype; + std::uint32_t len{}; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) and get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, len, result) and @@ -1571,7 +1569,7 @@ class binary_reader case 0xD4: // fixext 1 { - std::int8_t subtype; + 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); @@ -1579,7 +1577,7 @@ class binary_reader case 0xD5: // fixext 2 { - std::int8_t subtype; + 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); @@ -1587,7 +1585,7 @@ class binary_reader case 0xD6: // fixext 4 { - std::int8_t subtype; + 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); @@ -1595,7 +1593,7 @@ class binary_reader case 0xD7: // fixext 8 { - std::int8_t subtype; + 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); @@ -1603,7 +1601,7 @@ class binary_reader case 0xD8: // fixext 16 { - std::int8_t subtype; + 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); @@ -1712,31 +1710,31 @@ class binary_reader { case 'U': { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'i': { - std::int8_t len; + std::int8_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'I': { - std::int16_t len; + std::int16_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'l': { - std::int32_t len; + std::int32_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'L': { - std::int64_t len; + std::int64_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } @@ -1756,7 +1754,7 @@ class binary_reader { case 'U': { - std::uint8_t number; + std::uint8_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -1767,7 +1765,7 @@ class binary_reader case 'i': { - std::int8_t number; + std::int8_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -1778,7 +1776,7 @@ class binary_reader case 'I': { - std::int16_t number; + std::int16_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -1789,7 +1787,7 @@ class binary_reader case 'l': { - std::int32_t number; + std::int32_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -1800,7 +1798,7 @@ class binary_reader case 'L': { - std::int64_t number; + std::int64_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -1885,43 +1883,43 @@ class binary_reader case 'U': { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_unsigned(number); } case 'i': { - std::int8_t number; + std::int8_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'I': { - std::int16_t number; + std::int16_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'l': { - std::int32_t number; + std::int32_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'L': { - std::int64_t number; + std::int64_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'd': { - float number; + float number{}; return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); } case 'D': { - double number; + double number{}; return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); } diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp index ed9db0f6f0..47ea4a5b16 100644 --- a/include/nlohmann/detail/input/input_adapters.hpp +++ b/include/nlohmann/detail/input/input_adapters.hpp @@ -76,7 +76,7 @@ class input_stream_adapter { // clear stream flags; we use underlying streambuf I/O, do not // maintain ifstream flags, except eof - if (is) + if (is != nullptr) { is->clear(is->rdstate() & std::ios::eofbit); } @@ -411,7 +411,7 @@ template < typename CharT, contiguous_bytes_input_adapter input_adapter(CharT b) { auto length = std::strlen(reinterpret_cast(b)); - auto ptr = reinterpret_cast(b); + const auto* ptr = reinterpret_cast(b); return input_adapter(ptr, ptr + length); } diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 66b08bff76..c7c1db81e1 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -269,16 +269,16 @@ class json_sax_dom_parser switch ((ex.id / 100) % 100) { case 1: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 4: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); // LCOV_EXCL_START case 2: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 3: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 5: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); default: assert(false); // LCOV_EXCL_STOP @@ -523,16 +523,16 @@ class json_sax_dom_callback_parser switch ((ex.id / 100) % 100) { case 1: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 4: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); // LCOV_EXCL_START case 2: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 3: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 5: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); default: assert(false); // LCOV_EXCL_STOP diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 0ff0c7362d..6d188e4709 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -131,7 +131,7 @@ class lexer : public lexer_base JSON_HEDLEY_PURE static char get_decimal_point() noexcept { - const auto loc = localeconv(); + const auto* loc = localeconv(); assert(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } diff --git a/include/nlohmann/detail/iterators/internal_iterator.hpp b/include/nlohmann/detail/iterators/internal_iterator.hpp index 742df483a1..2c81f723fd 100644 --- a/include/nlohmann/detail/iterators/internal_iterator.hpp +++ b/include/nlohmann/detail/iterators/internal_iterator.hpp @@ -18,8 +18,6 @@ template struct internal_iterator typename BasicJsonType::object_t::iterator object_iterator {}; /// iterator for JSON arrays typename BasicJsonType::array_t::iterator array_iterator {}; - /// iterator for JSON binary arrays - 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/json_ref.hpp b/include/nlohmann/detail/json_ref.hpp index c8dec7330f..c9bf6cb228 100644 --- a/include/nlohmann/detail/json_ref.hpp +++ b/include/nlohmann/detail/json_ref.hpp @@ -16,23 +16,30 @@ class json_ref using value_type = BasicJsonType; json_ref(value_type&& value) - : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true) + : owned_value(std::move(value)) + , value_ref(&owned_value) + , is_rvalue(true) {} json_ref(const value_type& value) - : value_ref(const_cast(&value)), is_rvalue(false) + : value_ref(const_cast(&value)) + , is_rvalue(false) {} json_ref(std::initializer_list init) - : owned_value(init), value_ref(&owned_value), is_rvalue(true) + : owned_value(init) + , value_ref(&owned_value) + , is_rvalue(true) {} template < class... Args, enable_if_t::value, int> = 0 > json_ref(Args && ... args) - : owned_value(std::forward(args)...), value_ref(&owned_value), - is_rvalue(true) {} + : owned_value(std::forward(args)...) + , value_ref(&owned_value) + , is_rvalue(true) + {} // class should be movable only json_ref(json_ref&&) = default; @@ -63,7 +70,7 @@ class json_ref private: mutable value_type owned_value = nullptr; value_type* value_ref = nullptr; - const bool is_rvalue; + const bool is_rvalue = true; }; } // namespace detail } // namespace nlohmann diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 7289da3dce..3bac02270c 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -573,7 +573,7 @@ class binary_writer const auto N = j.m_value.binary->size(); if (N <= (std::numeric_limits::max)()) { - std::uint8_t output_type; + std::uint8_t output_type{}; bool fixed = true; if (use_ext) { @@ -615,30 +615,18 @@ class binary_writer } else if (N <= (std::numeric_limits::max)()) { - std::uint8_t output_type; - if (use_ext) - { - output_type = 0xC8; // ext 16 - } - else - { - output_type = 0xC5; // bin 16 - } + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 oa->write_character(to_char_type(output_type)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { - std::uint8_t output_type; - if (use_ext) - { - output_type = 0xC9; // ext 32 - } - else - { - output_type = 0xC6; // bin 32 - } + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 oa->write_character(to_char_type(output_type)); write_number(static_cast(N)); diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index e8f31fd8a5..d86113b37f 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -9,7 +9,7 @@ #include // uint8_t #include // snprintf #include // numeric_limits -#include // string +#include // string, char_traits #include // is_same #include // move @@ -59,8 +59,8 @@ class serializer error_handler_t error_handler_ = error_handler_t::strict) : o(std::move(s)) , loc(std::localeconv()) - , thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)) - , decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) , indent_char(ichar) , indent_string(512, indent_char) , error_handler(error_handler_) @@ -701,7 +701,7 @@ class serializer } // use a pointer to fill the buffer - auto buffer_ptr = number_buffer.begin(); + auto* buffer_ptr = number_buffer.begin(); const bool is_negative = std::is_same::value and not(x >= 0); // see issue #755 number_unsigned_t abs_value; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index e2aea10370..d0e01c861a 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4502,7 +4502,7 @@ class input_stream_adapter { // clear stream flags; we use underlying streambuf I/O, do not // maintain ifstream flags, except eof - if (is) + if (is != nullptr) { is->clear(is->rdstate() & std::ios::eofbit); } @@ -4837,7 +4837,7 @@ template < typename CharT, contiguous_bytes_input_adapter input_adapter(CharT b) { auto length = std::strlen(reinterpret_cast(b)); - auto ptr = reinterpret_cast(b); + const auto* ptr = reinterpret_cast(b); return input_adapter(ptr, ptr + length); } @@ -5154,16 +5154,16 @@ class json_sax_dom_parser switch ((ex.id / 100) % 100) { case 1: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 4: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); // LCOV_EXCL_START case 2: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 3: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 5: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); default: assert(false); // LCOV_EXCL_STOP @@ -5408,16 +5408,16 @@ class json_sax_dom_callback_parser switch ((ex.id / 100) % 100) { case 1: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 4: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); // LCOV_EXCL_START case 2: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 3: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); case 5: - JSON_THROW(*static_cast(&ex)); + JSON_THROW(*dynamic_cast(&ex)); default: assert(false); // LCOV_EXCL_STOP @@ -5886,7 +5886,7 @@ class binary_reader */ bool parse_bson_internal() { - std::int32_t document_size; + std::int32_t document_size{}; get_number(input_format_t::bson, document_size); if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) @@ -5925,8 +5925,6 @@ class binary_reader } *out++ = static_cast(current); } - - return true; } /*! @@ -5971,7 +5969,7 @@ class binary_reader } // All BSON binary values have a subtype - std::uint8_t subtype; + std::uint8_t subtype{}; get_number(input_format_t::bson, subtype); result.set_subtype(subtype); @@ -5995,13 +5993,13 @@ class binary_reader { case 0x01: // double { - double number; + double number{}; return get_number(input_format_t::bson, number) and sax->number_float(static_cast(number), ""); } case 0x02: // string { - std::int32_t len; + std::int32_t len{}; string_t value; return get_number(input_format_t::bson, len) and get_bson_string(len, value) and sax->string(value); } @@ -6018,7 +6016,7 @@ class binary_reader case 0x05: // binary { - std::int32_t len; + std::int32_t len{}; binary_t value; return get_number(input_format_t::bson, len) and get_bson_binary(len, value) and sax->binary(value); } @@ -6035,13 +6033,13 @@ class binary_reader case 0x10: // int32 { - std::int32_t value; + std::int32_t value{}; return get_number(input_format_t::bson, value) and sax->number_integer(value); } case 0x12: // int64 { - std::int64_t value; + std::int64_t value{}; return get_number(input_format_t::bson, value) and sax->number_integer(value); } @@ -6106,7 +6104,7 @@ class binary_reader */ bool parse_bson_array() { - std::int32_t document_size; + std::int32_t document_size{}; get_number(input_format_t::bson, document_size); if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) @@ -6170,25 +6168,25 @@ class binary_reader case 0x18: // Unsigned integer (one-byte uint8_t follows) { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } case 0x19: // Unsigned integer (two-byte uint16_t follows) { - std::uint16_t number; + std::uint16_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } case 0x1A: // Unsigned integer (four-byte uint32_t follows) { - std::uint32_t number; + std::uint32_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } case 0x1B: // Unsigned integer (eight-byte uint64_t follows) { - std::uint64_t number; + std::uint64_t number{}; return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); } @@ -6221,25 +6219,25 @@ class binary_reader case 0x38: // Negative integer (one-byte uint8_t follows) { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); } case 0x39: // Negative integer -1-n (two-byte uint16_t follows) { - std::uint16_t number; + std::uint16_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); } case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) { - std::uint32_t number; + std::uint32_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); } case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) { - std::uint64_t number; + std::uint64_t number{}; return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - static_cast(number)); } @@ -6343,25 +6341,25 @@ class binary_reader case 0x98: // array (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } case 0x99: // array (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } case 0x9A: // array (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } case 0x9B: // array (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); } @@ -6397,25 +6395,25 @@ class binary_reader case 0xB8: // map (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } case 0xB9: // map (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } case 0xBA: // map (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } case 0xBB: // map (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); } @@ -6481,13 +6479,13 @@ class binary_reader case 0xFA: // Single-Precision Float (four-byte IEEE 754) { - float number; + float number{}; return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); } case 0xFB: // Double-Precision Float (eight-byte IEEE 754) { - double number; + double number{}; return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); } @@ -6550,25 +6548,25 @@ class binary_reader case 0x78: // UTF-8 string (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } case 0x79: // UTF-8 string (two-byte uint16_t for n follow) { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) { - std::uint64_t len; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); } @@ -6645,28 +6643,28 @@ class binary_reader case 0x58: // Binary data (one-byte uint8_t for n follows) { - std::uint8_t len; + std::uint8_t len{}; 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; + std::uint16_t len{}; 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; + std::uint32_t len{}; 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; + std::uint64_t len{}; return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); } @@ -7031,85 +7029,85 @@ class binary_reader case 0xCA: // float 32 { - float number; + float number{}; return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); } case 0xCB: // float 64 { - double number; + double number{}; return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); } case 0xCC: // uint 8 { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xCD: // uint 16 { - std::uint16_t number; + std::uint16_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xCE: // uint 32 { - std::uint32_t number; + std::uint32_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xCF: // uint 64 { - std::uint64_t number; + std::uint64_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); } case 0xD0: // int 8 { - std::int8_t number; + std::int8_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xD1: // int 16 { - std::int16_t number; + std::int16_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xD2: // int 32 { - std::int32_t number; + std::int32_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xD3: // int 64 { - std::int64_t number; + std::int64_t number{}; return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } case 0xDC: // array 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); } case 0xDD: // array 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); } case 0xDE: // map 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); } case 0xDF: // map 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); } @@ -7214,19 +7212,19 @@ class binary_reader case 0xD9: // str 8 { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); } case 0xDA: // str 16 { - std::uint16_t len; + std::uint16_t len{}; return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); } case 0xDB: // str 32 { - std::uint32_t len; + std::uint32_t len{}; return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); } @@ -7261,29 +7259,29 @@ class binary_reader { case 0xC4: // bin 8 { - std::uint8_t len; + std::uint8_t len{}; 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; + std::uint16_t len{}; 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; + std::uint32_t len{}; 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; - std::int8_t subtype; + std::uint8_t len{}; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) and get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, len, result) and @@ -7292,8 +7290,8 @@ class binary_reader case 0xC8: // ext 16 { - std::uint16_t len; - std::int8_t subtype; + std::uint16_t len{}; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) and get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, len, result) and @@ -7302,8 +7300,8 @@ class binary_reader case 0xC9: // ext 32 { - std::uint32_t len; - std::int8_t subtype; + std::uint32_t len{}; + std::int8_t subtype{}; return get_number(input_format_t::msgpack, len) and get_number(input_format_t::msgpack, subtype) and get_binary(input_format_t::msgpack, len, result) and @@ -7312,7 +7310,7 @@ class binary_reader case 0xD4: // fixext 1 { - std::int8_t subtype; + 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); @@ -7320,7 +7318,7 @@ class binary_reader case 0xD5: // fixext 2 { - std::int8_t subtype; + 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); @@ -7328,7 +7326,7 @@ class binary_reader case 0xD6: // fixext 4 { - std::int8_t subtype; + 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); @@ -7336,7 +7334,7 @@ class binary_reader case 0xD7: // fixext 8 { - std::int8_t subtype; + 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); @@ -7344,7 +7342,7 @@ class binary_reader case 0xD8: // fixext 16 { - std::int8_t subtype; + 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); @@ -7453,31 +7451,31 @@ class binary_reader { case 'U': { - std::uint8_t len; + std::uint8_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'i': { - std::int8_t len; + std::int8_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'I': { - std::int16_t len; + std::int16_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'l': { - std::int32_t len; + std::int32_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } case 'L': { - std::int64_t len; + std::int64_t len{}; return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); } @@ -7497,7 +7495,7 @@ class binary_reader { case 'U': { - std::uint8_t number; + std::uint8_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -7508,7 +7506,7 @@ class binary_reader case 'i': { - std::int8_t number; + std::int8_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -7519,7 +7517,7 @@ class binary_reader case 'I': { - std::int16_t number; + std::int16_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -7530,7 +7528,7 @@ class binary_reader case 'l': { - std::int32_t number; + std::int32_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -7541,7 +7539,7 @@ class binary_reader case 'L': { - std::int64_t number; + std::int64_t number{}; if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; @@ -7626,43 +7624,43 @@ class binary_reader case 'U': { - std::uint8_t number; + std::uint8_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_unsigned(number); } case 'i': { - std::int8_t number; + std::int8_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'I': { - std::int16_t number; + std::int16_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'l': { - std::int32_t number; + std::int32_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'L': { - std::int64_t number; + std::int64_t number{}; return get_number(input_format_t::ubjson, number) and sax->number_integer(number); } case 'd': { - float number; + float number{}; return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); } case 'D': { - double number; + double number{}; return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); } @@ -8202,7 +8200,7 @@ class lexer : public lexer_base JSON_HEDLEY_PURE static char get_decimal_point() noexcept { - const auto loc = localeconv(); + const auto* loc = localeconv(); assert(loc != nullptr); return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); } @@ -10249,8 +10247,6 @@ template struct internal_iterator typename BasicJsonType::object_t::iterator object_iterator {}; /// iterator for JSON arrays typename BasicJsonType::array_t::iterator array_iterator {}; - /// iterator for JSON binary arrays - typename BasicJsonType::binary_t::container_type::iterator binary_iterator {}; /// generic iterator for all other types primitive_iterator_t primitive_iterator {}; }; @@ -12026,23 +12022,30 @@ class json_ref using value_type = BasicJsonType; json_ref(value_type&& value) - : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true) + : owned_value(std::move(value)) + , value_ref(&owned_value) + , is_rvalue(true) {} json_ref(const value_type& value) - : value_ref(const_cast(&value)), is_rvalue(false) + : value_ref(const_cast(&value)) + , is_rvalue(false) {} json_ref(std::initializer_list init) - : owned_value(init), value_ref(&owned_value), is_rvalue(true) + : owned_value(init) + , value_ref(&owned_value) + , is_rvalue(true) {} template < class... Args, enable_if_t::value, int> = 0 > json_ref(Args && ... args) - : owned_value(std::forward(args)...), value_ref(&owned_value), - is_rvalue(true) {} + : owned_value(std::forward(args)...) + , value_ref(&owned_value) + , is_rvalue(true) + {} // class should be movable only json_ref(json_ref&&) = default; @@ -12073,7 +12076,7 @@ class json_ref private: mutable value_type owned_value = nullptr; value_type* value_ref = nullptr; - const bool is_rvalue; + const bool is_rvalue = true; }; } // namespace detail } // namespace nlohmann @@ -12787,7 +12790,7 @@ class binary_writer const auto N = j.m_value.binary->size(); if (N <= (std::numeric_limits::max)()) { - std::uint8_t output_type; + std::uint8_t output_type{}; bool fixed = true; if (use_ext) { @@ -12829,30 +12832,18 @@ class binary_writer } else if (N <= (std::numeric_limits::max)()) { - std::uint8_t output_type; - if (use_ext) - { - output_type = 0xC8; // ext 16 - } - else - { - output_type = 0xC5; // bin 16 - } + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 oa->write_character(to_char_type(output_type)); write_number(static_cast(N)); } else if (N <= (std::numeric_limits::max)()) { - std::uint8_t output_type; - if (use_ext) - { - output_type = 0xC9; // ext 32 - } - else - { - output_type = 0xC6; // bin 32 - } + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 oa->write_character(to_char_type(output_type)); write_number(static_cast(N)); @@ -13806,7 +13797,7 @@ class binary_writer #include // uint8_t #include // snprintf #include // numeric_limits -#include // string +#include // string, char_traits #include // is_same #include // move @@ -14973,8 +14964,8 @@ class serializer error_handler_t error_handler_ = error_handler_t::strict) : o(std::move(s)) , loc(std::localeconv()) - , thousands_sep(loc->thousands_sep == nullptr ? '\0' : * (loc->thousands_sep)) - , decimal_point(loc->decimal_point == nullptr ? '\0' : * (loc->decimal_point)) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) , indent_char(ichar) , indent_string(512, indent_char) , error_handler(error_handler_) @@ -15615,7 +15606,7 @@ class serializer } // use a pointer to fill the buffer - auto buffer_ptr = number_buffer.begin(); + auto* buffer_ptr = number_buffer.begin(); const bool is_negative = std::is_same::value and not(x >= 0); // see issue #755 number_unsigned_t abs_value; From a9809f3381d4eb03d54fa2dddb23753903678f93 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 22 Jun 2020 23:02:28 +0200 Subject: [PATCH 27/31] :checkered_flag: revert change that breaks with MSVC --- include/nlohmann/detail/output/serializer.hpp | 2 +- single_include/nlohmann/json.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index d86113b37f..c089b94fc8 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -701,7 +701,7 @@ class serializer } // use a pointer to fill the buffer - auto* buffer_ptr = number_buffer.begin(); + auto buffer_ptr = number_buffer.begin(); const bool is_negative = std::is_same::value and not(x >= 0); // see issue #755 number_unsigned_t abs_value; diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index d0e01c861a..affa48c999 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -15606,7 +15606,7 @@ class serializer } // use a pointer to fill the buffer - auto* buffer_ptr = number_buffer.begin(); + auto buffer_ptr = number_buffer.begin(); const bool is_negative = std::is_same::value and not(x >= 0); // see issue #755 number_unsigned_t abs_value; From 0ecf297457272a887a454508bb66610e9156a920 Mon Sep 17 00:00:00 2001 From: chenguoping Date: Tue, 23 Jun 2020 09:14:55 +0800 Subject: [PATCH 28/31] drop std::enable_if part --- include/nlohmann/detail/iterators/iteration_proxy.hpp | 3 +-- single_include/nlohmann/json.hpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/nlohmann/detail/iterators/iteration_proxy.hpp b/include/nlohmann/detail/iterators/iteration_proxy.hpp index d19be4ddd6..7d0f1e5910 100644 --- a/include/nlohmann/detail/iterators/iteration_proxy.hpp +++ b/include/nlohmann/detail/iterators/iteration_proxy.hpp @@ -12,8 +12,7 @@ namespace nlohmann { namespace detail { -template::value, int>::type = 0> +template void int_to_string( string_type& target, std::size_t value ) { // For ADL diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 58b12a7df1..a6f9af99bf 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3650,8 +3650,7 @@ namespace nlohmann { namespace detail { -template::value, int>::type = 0> +template void int_to_string( string_type& target, std::size_t value ) { // For ADL From ec43371e07a903e6ffe74c87309a0811905a3a3c Mon Sep 17 00:00:00 2001 From: Alex Reinking Date: Fri, 26 Jun 2020 11:47:36 -0700 Subject: [PATCH 29/31] Enable CMake policy CMP0077 Projects that import json via [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) or `add_subdirectory` pointed at a git submodule may want to set `JSON_BuildTests` to "NO". However, this doesn't work without creating an identical `option()` in the importing project. Enabling CMP0077 in supported versions of CMake changes the behavior of `option()` to allow importing projects to set default values for the variables without touching the cache. See the documentation for CMP0077 here: https://cmake.org/cmake/help/latest/policy/CMP0077.html --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f163041f5..f7ba4fa38f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,12 @@ include(ExternalProject) ## ## OPTIONS ## + +if (POLICY CMP0077) + # Allow CMake 3.13+ to override options when using FetchContent / add_subdirectory. + cmake_policy(SET CMP0077 NEW) +endif () + option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ON) option(JSON_Install "Install CMake targets during install step." ON) option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF) From aefa0b3e865a19224d030a07984b7a8a52169d93 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 27 Jun 2020 12:54:42 +0200 Subject: [PATCH 30/31] :wrench: use Github discussions for questions --- .github/ISSUE_TEMPLATE/config.yml | 5 ++++ .github/ISSUE_TEMPLATE/question.md | 40 ------------------------------ 2 files changed, 5 insertions(+), 40 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/question.md diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..0e96633842 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/nlohmann/json/discussions + about: Ask questions and discuss with other community members diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index 3f522cde77..0000000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: Question -about: Ask a question regarding the library. -title: '' -labels: 'kind: question' -assignees: '' - ---- - - - -#### What do you want to achieve? - - - -#### What have you tried? - - - - - -#### Can you provide a small code example? - - - -#### Which compiler and operating system are you using? - - - - -- Compiler: ___ -- Operating system: ___ - -#### Which version of the library did you use? - - - -- [ ] latest release version 3.7.3 -- [ ] other release - please state the version: ___ -- [ ] the `develop` branch From 5ba0f65c34832aa18fb2b582d58a0c1f92c93bfb Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 27 Jun 2020 12:55:41 +0200 Subject: [PATCH 31/31] :wrench: remove feature request template --- .github/ISSUE_TEMPLATE/Feature_request.md | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/Feature_request.md diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md deleted file mode 100644 index f7effbaf68..0000000000 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: 'kind: enhancement/improvement' -assignees: '' - ---- - -#### Which feature do you want to see in the library? - - - -#### How would the feature be usable for other users? - -