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/.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? - - 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 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) diff --git a/README.md b/README.md index d7492da34f..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 @@ -1557,4 +1553,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. diff --git a/cmake/download_test_data.cmake b/cmake/download_test_data.cmake index fe95235f89..a3f3f199f1 100644 --- a/cmake/download_test_data.cmake +++ b/cmake/download_test_data.cmake @@ -1,17 +1,22 @@ -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} -) +set(JSON_TEST_DATA_VERSION 3.0.0) -# 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() + 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 + 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) 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/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..8042f3c4ef 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,77 @@ class lexer : public lexer_base } } + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + switch (get()) + { + // single-line comments skip input until a newline or EOF is read + case '/': + { + while (true) + { + switch (get()) + { + case '\n': + case '\r': + case std::char_traits::eof(): + case '\0': + return true; + + default: + break; + } + } + } + + // multi-line comments skip input until */ is read + case '*': + { + while (true) + { + switch (get()) + { + case std::char_traits::eof(): + case '\0': + { + error_message = "invalid comment; missing closing '*/'"; + return false; + } + + case '*': + { + switch (get()) + { + case '/': + return true; + + default: + { + unget(); + break; + } + } + } + + default: + break; + } + } + } + + // unexpected character after reading '/' + default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; + return false; + } + } + } + JSON_HEDLEY_NON_NULL(2) static void strtof(float& f, const char* str, char** endptr) noexcept { @@ -1415,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 @@ -1425,11 +1508,19 @@ class lexer : public lexer_base } // read next character and ignore whitespace - do + skip_whitespace(); + + // ignore comments + if (ignore_comments and current == '/') { - get(); + if (not scan_comment()) + { + return token_type::parse_error; + } + + // skip following whitespace + skip_whitespace(); } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); switch (current) { @@ -1499,6 +1590,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/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/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/iterators/iteration_proxy.hpp b/include/nlohmann/detail/iterators/iteration_proxy.hpp index c61d96296c..7d0f1e5910 100644 --- a/include/nlohmann/detail/iterators/iteration_proxy.hpp +++ b/include/nlohmann/detail/iterators/iteration_proxy.hpp @@ -15,7 +15,9 @@ namespace detail template 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/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 421c5ec8fe..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 @@ -325,10 +326,15 @@ 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 + @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')) { @@ -344,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&) { @@ -360,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 @@ -419,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; } @@ -497,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; } @@ -542,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; } @@ -592,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; } @@ -636,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; } @@ -700,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/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/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/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index 269df0d554..3bac02270c 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,8 +494,7 @@ 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); + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); break; } @@ -584,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) { @@ -626,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)); @@ -1518,6 +1495,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/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index e8f31fd8a5..c089b94fc8 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_) diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 409a6e7993..ace649b661 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; @@ -3770,7 +3772,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,7 +3784,7 @@ 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()))); @@ -3794,7 +3796,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))); } /*! @@ -3842,7 +3844,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())) @@ -3854,7 +3856,7 @@ class basic_json } JSON_INTERNAL_CATCH (out_of_range&) { - return default_value; + return std::move(default_value); } } @@ -3868,7 +3870,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))); } /*! @@ -5865,6 +5867,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 @@ -6563,6 +6593,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 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 @@ -6591,16 +6624,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 +6652,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 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 @@ -6632,10 +6670,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 +6682,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 +6706,9 @@ class basic_json iterators. @param[in] i input to read from + @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. @@ -6678,22 +6721,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 +6759,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 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 @@ -6737,11 +6786,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 +6799,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 +6813,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); } @@ -7040,7 +7092,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 +7117,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. @@ -8181,7 +8231,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")); @@ -8224,7 +8274,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)); } }; @@ -8659,6 +8709,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 @@ -8673,6 +8726,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 cc822a543b..770418f46d 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) @@ -3653,7 +3657,9 @@ namespace detail template 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 { @@ -4498,7 +4504,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); } @@ -4833,7 +4839,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); } @@ -5150,16 +5156,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 @@ -5404,16 +5410,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 @@ -5882,7 +5888,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)))) @@ -5921,8 +5927,6 @@ class binary_reader } *out++ = static_cast(current); } - - return true; } /*! @@ -5967,7 +5971,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); @@ -5991,13 +5995,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); } @@ -6014,7 +6018,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); } @@ -6031,13 +6035,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); } @@ -6102,7 +6106,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)))) @@ -6166,25 +6170,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); } @@ -6217,25 +6221,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)); } @@ -6339,25 +6343,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)); } @@ -6393,25 +6397,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)); } @@ -6477,13 +6481,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), ""); } @@ -6546,25 +6550,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); } @@ -6641,28 +6645,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); } @@ -7027,85 +7031,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)); } @@ -7210,19 +7214,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); } @@ -7257,29 +7261,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 @@ -7288,8 +7292,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 @@ -7298,8 +7302,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 @@ -7308,7 +7312,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); @@ -7316,7 +7320,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); @@ -7324,7 +7328,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); @@ -7332,7 +7336,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); @@ -7340,7 +7344,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); @@ -7449,31 +7453,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); } @@ -7493,7 +7497,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; @@ -7504,7 +7508,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; @@ -7515,7 +7519,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; @@ -7526,7 +7530,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; @@ -7537,7 +7541,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; @@ -7622,43 +7626,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), ""); } @@ -8179,8 +8183,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 +8205,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 +8900,77 @@ class lexer : public lexer_base } } + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + switch (get()) + { + // single-line comments skip input until a newline or EOF is read + case '/': + { + while (true) + { + switch (get()) + { + case '\n': + case '\r': + case std::char_traits::eof(): + case '\0': + return true; + + default: + break; + } + } + } + + // multi-line comments skip input until */ is read + case '*': + { + while (true) + { + switch (get()) + { + case std::char_traits::eof(): + case '\0': + { + error_message = "invalid comment; missing closing '*/'"; + return false; + } + + case '*': + { + switch (get()) + { + case '/': + return true; + + default: + { + unget(); + break; + } + } + } + + default: + break; + } + } + } + + // unexpected character after reading '/' + default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; + return false; + } + } + } + JSON_HEDLEY_NON_NULL(2) static void strtof(float& f, const char* str, char** endptr) noexcept { @@ -9482,6 +9560,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 @@ -9492,11 +9579,19 @@ class lexer : public lexer_base } // read next character and ignore whitespace - do + skip_whitespace(); + + // ignore comments + if (ignore_comments and current == '/') { - get(); + if (not scan_comment()) + { + return token_type::parse_error; + } + + // skip following whitespace + skip_whitespace(); } - while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); switch (current) { @@ -9566,6 +9661,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(); @@ -9668,8 +9766,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(); @@ -10245,8 +10346,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 {}; }; @@ -11032,6 +11131,7 @@ class json_reverse_iterator : public std::reverse_iterator #include // all_of #include // assert #include // isdigit +#include // max #include // accumulate #include // string #include // move @@ -11357,10 +11457,15 @@ 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 + @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')) { @@ -11376,10 +11481,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&) { @@ -11392,7 +11497,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 @@ -11451,7 +11563,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; } @@ -11529,8 +11641,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; } @@ -11574,7 +11685,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; } @@ -11624,8 +11735,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; } @@ -11668,7 +11778,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; } @@ -11732,7 +11842,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 @@ -12022,23 +12132,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; @@ -12069,7 +12186,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 @@ -12238,6 +12355,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 +12522,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,8 +12821,7 @@ 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); + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); break; } @@ -12794,7 +12900,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) { @@ -12836,30 +12942,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)); @@ -13728,6 +13822,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, @@ -13793,7 +13907,7 @@ class binary_writer #include // uint8_t #include // snprintf #include // numeric_limits -#include // string +#include // string, char_traits #include // is_same #include // move @@ -14960,8 +15074,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_) @@ -15977,10 +16091,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; @@ -19551,7 +19667,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())) @@ -19563,7 +19679,7 @@ 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()))); @@ -19575,7 +19691,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))); } /*! @@ -19623,7 +19739,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())) @@ -19635,7 +19751,7 @@ class basic_json } JSON_INTERNAL_CATCH (out_of_range&) { - return default_value; + return std::move(default_value); } } @@ -19649,7 +19765,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))); } /*! @@ -21646,6 +21762,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 @@ -22344,6 +22488,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 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 @@ -22372,16 +22519,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; } @@ -22398,6 +22547,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 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 @@ -22413,10 +22565,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; } @@ -22424,10 +22577,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; } @@ -22447,6 +22601,9 @@ class basic_json iterators. @param[in] i input to read from + @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. @@ -22459,22 +22616,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); } /*! @@ -22494,6 +22654,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 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 @@ -22518,11 +22681,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); } @@ -22530,11 +22694,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); } @@ -22543,11 +22708,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); } @@ -22821,7 +22987,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 +23012,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. @@ -23962,7 +24126,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")); @@ -24005,7 +24169,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)); } }; @@ -24440,6 +24604,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 @@ -24454,6 +24621,8 @@ inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcep j1.swap(j2); } +#endif + } // namespace std /*! 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() 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) diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 0e1b53785a..d8304ccf17 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -37,14 +37,23 @@ 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(); } } +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") @@ -127,6 +136,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 @@ -179,4 +190,56 @@ TEST_CASE("lexer class") s += "\""; CHECK((scan_string(s.c_str()) == json::lexer::token_type::value_string)); } + + 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)); + CHECK((scan_string("/*/**/", true) == json::lexer::token_type::end_of_input)); + CHECK((scan_string("/*/* */", 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 1912094aad..da16ffcab7 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") @@ -1834,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); + } } diff --git a/test/src/unit-concepts.cpp b/test/src/unit-concepts.cpp index 02ca3614dd..fbb5183899 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-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, diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index 6e7031b777..c67e0528a1 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")); 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); + } } }