diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 748357a95e..3485a6895b 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -7,16 +7,51 @@ assignees: '' --- -- What is the issue you have? + -- Please describe the steps to reproduce the issue. Can you provide a small but working code example? +#### What is the issue you have? -- What is the expected behavior? + + -- And what is the actual behavior instead? +#### Please describe the steps to reproduce the issue. -- Which compiler and operating system are you using? Is it a [supported compiler](https://github.com/nlohmann/json#supported-compilers)? + + -- Did you use a released version of the library or the version from the `develop` branch? +1. +2. +3. -- If you experience a compilation error: can you [compile and run the unit tests](https://github.com/nlohmann/json#execute-unit-tests)? +#### Can you provide a small but working code example? + + + +#### What is the expected behavior? + + + +#### And what is the actual behavior instead? + + + +#### 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 + +#### If you experience a compilation error: can you [compile and run the unit tests](https://github.com/nlohmann/json#execute-unit-tests)? + +- [ ] yes +- [ ] no - please copy/paste the error message below diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md index 05a2c946e7..3652a1ee4a 100644 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -7,6 +7,11 @@ assignees: '' --- -- Describe the feature in as much detail as possible. +#### Which feature do you want to see in the library? + + + +#### How would the feature be usable for other users? + + -- Include sample usage where appropriate. diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 0870cd17b1..ec3ad69e02 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -7,10 +7,28 @@ assignees: '' --- -- Describe what you want to achieve. +#### What do you want to achieve? -- Describe what you tried. + -- Describe which system (OS, compiler) you are using. +#### What have you tried? -- Describe which version of the library you are using (release version, develop branch). + + + + +#### 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/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml deleted file mode 100644 index c6f8afb3c2..0000000000 --- a/.github/workflows/ccpp.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: C/C++ CI - -on: [push] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - name: prepare - run: mkdir build - - name: cmake - run: cd build ; cmake .. - - name: build - run: make -C build - - name: test - run: cd build ; ctest -j 10 diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000000..4444f72bab --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,17 @@ +name: macOS + +on: [push, pull_request] + +jobs: + build: + + runs-on: macos-latest + + steps: + - uses: actions/checkout@v1 + - name: cmake + run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug + - name: build + run: cmake --build build --parallel 10 + - name: test + run: cd build ; ctest -j 10 --output-on-failure diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 0000000000..6ee33cfae1 --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,17 @@ +name: Ubuntu + +on: [push, pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: cmake + run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug + - name: build + run: cmake --build build --parallel 10 + - name: test + run: cd build ; ctest -j 10 --output-on-failure diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000000..f4b07c89b0 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,17 @@ +name: Windows + +on: [push, pull_request] + +jobs: + build: + + runs-on: windows-latest + + steps: + - uses: actions/checkout@v1 + - name: cmake + run: cmake -S . -B build -G "Visual Studio 16 2019" -D CMAKE_BUILD_TYPE=Debug + - name: build + run: cmake --build build --parallel 10 + - name: test + run: cd build ; ctest -j 10 -C Debug --exclude-regex "test-unicode" --output-on-failure diff --git a/.travis.yml b/.travis.yml index acd542babe..3aea08eacd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -95,16 +95,17 @@ matrix: - os: linux compiler: gcc + dist: bionic addons: apt: sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-4.9', 'ninja-build'] + packages: ['g++-7', 'ninja-build'] before_script: - pip install --user cpp-coveralls after_success: - - coveralls --build-root test --include include/nlohmann --gcov 'gcov-4.9' --gcov-options '\-lp' + - coveralls --build-root test --include include/nlohmann --gcov 'gcov-7' --gcov-options '\-lp' env: - - COMPILER=g++-4.9 + - COMPILER=g++-7 - CMAKE_OPTIONS=-DJSON_Coverage=ON - MULTIPLE_HEADERS=ON diff --git a/CMakeLists.txt b/CMakeLists.txt index e2d0261bae..980c2c453e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,7 +117,7 @@ if(JSON_Install) FILES ${NLOHMANN_NATVIS_FILE} DESTINATION . ) - endif() +endif() export( TARGETS ${NLOHMANN_JSON_TARGET_NAME} NAMESPACE ${PROJECT_NAME}:: diff --git a/Makefile b/Makefile index c604c98803..08c40d2f34 100644 --- a/Makefile +++ b/Makefile @@ -102,7 +102,8 @@ doctest: # -Wno-switch-enum -Wno-covered-switch-default: pedantic/contradicting warnings about switches # -Wno-weak-vtables: exception class is defined inline, but has virtual method pedantic_clang: - $(MAKE) json_unit CXX=c++ CXXFLAGS=" \ + rm -fr build_pedantic + CXXFLAGS=" \ -std=c++11 -Wno-c++98-compat -Wno-c++98-compat-pedantic \ -Werror \ -Weverything \ @@ -115,11 +116,13 @@ pedantic_clang: -Wno-padded \ -Wno-range-loop-analysis \ -Wno-switch-enum -Wno-covered-switch-default \ - -Wno-weak-vtables" + -Wno-weak-vtables" cmake -S . -B build_pedantic -GNinja -DCMAKE_BUILD_TYPE=Debug -DJSON_MultipleHeaders=ON + cmake --build build_pedantic # calling GCC with most warnings pedantic_gcc: - $(MAKE) json_unit CXX=/usr/local/bin/g++-9 CXXFLAGS=" \ + rm -fr build_pedantic + CXXFLAGS=" \ -std=c++11 \ -Waddress \ -Waddress-of-packed-member \ @@ -233,7 +236,7 @@ pedantic_gcc: -Wno-system-headers \ -Wno-templates \ -Wno-undef \ - -Wnoexcept \ + -Wno-noexcept \ -Wnoexcept-type \ -Wnon-template-friend \ -Wnon-virtual-dtor \ @@ -340,7 +343,8 @@ pedantic_gcc: -Wvolatile-register-var \ -Wwrite-strings \ -Wzero-as-null-pointer-constant \ - " + " cmake -S . -B build_pedantic -GNinja -DCMAKE_BUILD_TYPE=Debug -DJSON_MultipleHeaders=ON + cmake --build build_pedantic ########################################################################## # benchmarks @@ -564,7 +568,7 @@ check_cmake_flags: NEXT_VERSION ?= "unreleased" ChangeLog.md: - github_changelog_generator -o ChangeLog.md --simple-list --release-url https://github.com/nlohmann/json/releases/tag/%s --future-release $(NEXT_VERSION) + github_changelog_generator -o ChangeLog.md --user nlohmann --project json --simple-list --release-url https://github.com/nlohmann/json/releases/tag/%s --future-release $(NEXT_VERSION) $(SED) -i 's|https://github.com/nlohmann/json/releases/tag/HEAD|https://github.com/nlohmann/json/tree/HEAD|' ChangeLog.md $(SED) -i '2i All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).' ChangeLog.md diff --git a/README.md b/README.md index bb5cb1aac6..899d56603f 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,11 @@ [![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json) [![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json) +[![Ubuntu](https://github.com/nlohmann/json/workflows/Ubuntu/badge.svg)](https://github.com/nlohmann/json/actions?query=workflow%3AUbuntu) +[![macOS](https://github.com/nlohmann/json/workflows/macOS/badge.svg)](https://github.com/nlohmann/json/actions?query=workflow%3AmacOS) +[![Windows](https://github.com/nlohmann/json/workflows/Windows/badge.svg)](https://github.com/nlohmann/json/actions?query=workflow%3AWindows) [![Build Status](https://circleci.com/gh/nlohmann/json.svg?style=svg)](https://circleci.com/gh/nlohmann/json) -[![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json) +[![Coverage Status](https://coveralls.io/repos/github/nlohmann/json/badge.svg?branch=develop)](https://coveralls.io/github/nlohmann/json?branch=develop) [![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f3732b3327e34358a0e9d1fe9f661f08)](https://www.codacy.com/app/nlohmann/json?utm_source=github.com&utm_medium=referral&utm_content=nlohmann/json&utm_campaign=Badge_Grade) [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/nlohmann/json.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/nlohmann/json/context:cpp) @@ -11,6 +14,7 @@ [![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/3lCHrFUZANONKv7a) [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fnlohmann%2Fjson.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fnlohmann%2Fjson?ref=badge_shield) [![GitHub Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases) [![GitHub Downloads](https://img.shields.io/github/downloads/nlohmann/json/total)](https://github.com/nlohmann/json/releases) [![GitHub Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](http://github.com/nlohmann/json/issues) @@ -1046,13 +1050,43 @@ std::vector v_ubjson = json::to_ubjson(j); json j_from_ubjson = json::from_ubjson(v_ubjson); ``` +The library also supports binary types from BSON, CBOR (byte strings), and MessagePack (bin, ext, fixext). They are stored by default as `std::vector` to be processed outside of the library. + +```cpp +// CBOR byte string with payload 0xCAFE +std::vector v = {0x42, 0xCA, 0xFE}; + +// read value +json j = json::from_cbor(v); + +// the JSON value has type binary +j.is_binary(); // true + +// get reference to stored binary value +auto& binary = j.get_binary(); + +// the binary value has no subtype (CBOR has no binary subtypes) +binary.has_subtype(); // false + +// access std::vector member functions +binary.size(); // 2 +binary[0]; // 0xCA +binary[1]; // 0xFE + +// set subtype to 0x10 +binary.set_subtype(0x10); + +// serialize to MessagePack +auto cbor = json::to_msgpack(j); // 0xD5 (fixext2), 0x10, 0xCA, 0xFE +``` + ## Supported compilers -Though it's 2019 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work: +Though it's 2020 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work: -- GCC 4.8 - 9.2 (and possibly later) -- Clang 3.4 - 9.0 (and possibly later) +- GCC 4.8 - 10.0 (and possibly later) +- Clang 3.4 - 10.0 (and possibly later) - Intel C++ Compiler 17.0.2 (and possibly later) - Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later) - Microsoft Visual C++ 2017 / Build Tools 15.5.180.51428 (and possibly later) @@ -1077,36 +1111,41 @@ Please note: - Unsupported versions of GCC and Clang are rejected by `#error` directives. This can be switched off by defining `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`. Note that you can expect no support in this case. -The following compilers are currently used in continuous integration at [Travis](https://travis-ci.org/nlohmann/json), [AppVeyor](https://ci.appveyor.com/project/nlohmann/json), and [CircleCI](https://circleci.com/gh/nlohmann/json): - -| Compiler | Operating System | Version String | -|-----------------------|------------------------------|----------------| -| GCC 4.8.5 | Ubuntu 14.04.5 LTS | g++-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.2) 4.8.5 | -| GCC 4.9.4 | Ubuntu 14.04.1 LTS | g++-4.9 (Ubuntu 4.9.4-2ubuntu1~14.04.1) 4.9.4 | -| GCC 5.5.0 | Ubuntu 14.04.1 LTS | g++-5 (Ubuntu 5.5.0-12ubuntu1~14.04) 5.5.0 20171010 | -| GCC 6.3.0 | Debian 9 (stretch) | g++ (Debian 6.3.0-18+deb9u1) 6.3.0 20170516 | -| GCC 6.4.0 | Ubuntu 14.04.1 LTS | g++-6 (Ubuntu 6.4.0-17ubuntu1~14.04) 6.4.0 20180424 | -| GCC 7.3.0 | Ubuntu 14.04.1 LTS | g++-7 (Ubuntu 7.3.0-21ubuntu1~14.04) 7.3.0 | -| GCC 7.3.0 | Windows Server 2012 R2 (x64) | g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.3.0 | -| GCC 8.1.0 | Ubuntu 14.04.1 LTS | g++-8 (Ubuntu 8.1.0-5ubuntu1~14.04) 8.1.0 | -| GCC 9.2.1 | Ubuntu 14.05.1 LTS | g++-9 (Ubuntu 9.2.1-16ubuntu1~14.04.1) 9.2.1 20191030 | -| Clang 3.5.0 | Ubuntu 14.04.1 LTS | clang version 3.5.0-4ubuntu2~trusty2 (tags/RELEASE_350/final) (based on LLVM 3.5.0) | -| Clang 3.6.2 | Ubuntu 14.04.1 LTS | clang version 3.6.2-svn240577-1~exp1 (branches/release_36) (based on LLVM 3.6.2) | -| Clang 3.7.1 | Ubuntu 14.04.1 LTS | clang version 3.7.1-svn253571-1~exp1 (branches/release_37) (based on LLVM 3.7.1) | -| Clang 3.8.0 | Ubuntu 14.04.1 LTS | clang version 3.8.0-2ubuntu3~trusty5 (tags/RELEASE_380/final) | -| Clang 3.9.1 | Ubuntu 14.04.1 LTS | clang version 3.9.1-4ubuntu3~14.04.3 (tags/RELEASE_391/rc2) | -| Clang 4.0.1 | Ubuntu 14.04.1 LTS | clang version 4.0.1-svn305264-1~exp1 (branches/release_40) | -| Clang 5.0.2 | Ubuntu 14.04.1 LTS | clang version 5.0.2-svn328729-1~exp1~20180509123505.100 (branches/release_50) | -| Clang 6.0.1 | Ubuntu 14.04.1 LTS | clang version 6.0.1-svn334776-1~exp1~20180726133705.85 (branches/release_60) | -| Clang 7.0.1 | Ubuntu 14.04.1 LTS | clang version 7.0.1-svn348686-1~exp1~20181213084532.54 (branches/release_70) | -| Clang Xcode 9.3 | OSX 10.13.3 | Apple LLVM version 9.1.0 (clang-902.0.39.2) | -| Clang Xcode 10.0 | OSX 10.13.3 | Apple LLVM version 10.0.0 (clang-1000.11.45.2) | -| Clang Xcode 10.1 | OSX 10.13.3 | Apple LLVM version 10.0.0 (clang-1000.11.45.5) | -| Clang Xcode 10.2 | OSX 10.14.4 | Apple LLVM version 10.0.1 (clang-1001.0.46.4) | -| Clang Xcode 11.2.1 | OSX 10.14.4 | Apple LLVM version 11.0.0 (clang-1100.0.33.12) | -| Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25420.1, MSVC 19.0.24215.1 | -| Visual Studio 15 2017 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 15.9.21+g9802d43bc3, MSVC 19.16.27032.1 | -| Visual Studio 16 2019 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 16.3.1+1def00d3d, MSVC 19.23.28106.4 | +The following compilers are currently used in continuous integration at [Travis](https://travis-ci.org/nlohmann/json), [AppVeyor](https://ci.appveyor.com/project/nlohmann/json), [GitHub Actions](https://github.com/nlohmann/json/actions), and [CircleCI](https://circleci.com/gh/nlohmann/json): + +| Compiler | Operating System | CI Provider | +|-----------------------------------------------------------------|--------------------|----------------| +| Apple Clang 9.1.0 (clang-902.0.39.1); Xcode 9.3 | macOS 10.13.3 | Travis | +| Apple Clang 9.1.0 (clang-902.0.39.2); Xcode 9.4.1 | macOS 10.13.6 | Travis | +| Apple Clang 10.0.0 (clang-1000.11.45.2); Xcode 10.0 | macOS 10.13.6 | Travis | +| Apple Clang 10.0.0 (clang-1000.11.45.5); Xcode 10.1 | macOS 10.13.6 | Travis | +| Apple Clang 10.0.1 (clang-1001.0.46.4); Xcode 10.2.1 | macOS 10.14.4 | Travis | +| Apple Clang 11.0.0 (clang-1100.0.33.12); Xcode 11.2.1 | macOS 10.14.6 | Travis | +| Apple Clang 11.0.3 (clang-1103.0.32.59); Xcode 11.4.1 | macOS 10.15.4 | GitHub Actions | +| Clang 3.5.0 (3.5.0-4ubuntu2~trusty2) | Ubuntu 14.04.5 LTS | Travis | +| Clang 3.6.2 (3.6.2-svn240577-1~exp1) | Ubuntu 14.04.5 LTS | Travis | +| Clang 3.7.1 (3.7.1-svn253571-1~exp1) | Ubuntu 14.04.5 LTS | Travis | +| Clang 3.8.0 (3.8.0-2ubuntu3~trusty5) | Ubuntu 14.04.5 LTS | Travis | +| Clang 3.9.1 (3.9.1-4ubuntu3~14.04.3) | Ubuntu 14.04.5 LTS | Travis | +| Clang 4.0.1 (4.0.1-svn305264-1~exp1) | Ubuntu 14.04.5 LTS | Travis | +| Clang 5.0.2 (version 5.0.2-svn328729-1~exp1~20180509123505.100) | Ubuntu 14.04.5 LTS | Travis | +| Clang 6.0.1 (6.0.1-svn334776-1~exp1~20190309042707.121) | Ubuntu 14.04.5 LTS | Travis | +| Clang 7.1.0 (7.1.0-svn353565-1~exp1~20190419134007.64) | Ubuntu 14.04.5 LTS | Travis | +| Clang 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04) | Ubuntu 18.04.4 LTS | Travis | +| GCC 4.8.5 (Ubuntu 4.8.5-4ubuntu8~14.04.2) | Ubuntu 14.04.5 LTS | Travis | +| GCC 4.9.4 (Ubuntu 4.9.4-2ubuntu1~14.04.1) | Ubuntu 14.04.5 LTS | Travis | +| GCC 5.5.0 (Ubuntu 5.5.0-12ubuntu1~14.04) | Ubuntu 14.04.5 LTS | Travis | +| GCC 6.3.0 (Debian 6.3.0-18+deb9u1) | Debian 9 | Circle CI | +| GCC 6.5.0 (Ubuntu 6.5.0-2ubuntu1~14.04.1) | Ubuntu 14.04.5 LTS | Travis | +| GCC 7.3.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project) | Windows-6.3.9600 | AppVeyor | +| GCC 7.5.0 (Ubuntu 7.5.0-3ubuntu1~14.04.1) | Ubuntu 14.04.5 LTS | Travis | +| GCC 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04) | Ubuntu 18.04.4 LTS | GitHub Actions | +| GCC 8.4.0 (Ubuntu 8.4.0-1ubuntu1~14.04) | Ubuntu 14.04.5 LTS | Travis | +| GCC 9.3.0 (Ubuntu 9.3.0-11ubuntu0~14.04) | Ubuntu 14.04.5 LTS | Travis | +| MSVC 19.0.24241.7 (Build Engine version 14.0.25420.1) | Windows-6.3.9600 | AppVeyor | +| MSVC 19.16.27035.0 (15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | AppVeyor | +| MSVC 19.25.28614.0 (Build Engine version 16.5.0+d4cbfca49 for .NET Framework) | Windows-10.0.17763 | AppVeyor | +| MSVC 19.25.28614.0 (Build Engine version 16.5.0+d4cbfca49 for .NET Framework) | Windows-10.0.17763 | GitHub Actions | ## License diff --git a/cmake/download_test_data.cmake b/cmake/download_test_data.cmake index c6dc135b78..fe95235f89 100644 --- a/cmake/download_test_data.cmake +++ b/cmake/download_test_data.cmake @@ -1,7 +1,7 @@ find_package(Git) set(JSON_TEST_DATA_URL https://github.com/nlohmann/json_test_data) -set(JSON_TEST_DATA_VERSION 1.0.0) +set(JSON_TEST_DATA_VERSION 2.0.0) # target to download test data add_custom_target(download_test_data @@ -12,3 +12,40 @@ add_custom_target(download_test_data # 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") + +# determine the operating system (for debug and support purposes) +find_program(UNAME_COMMAND uname) +find_program(VER_COMMAND ver) +find_program(LSB_RELEASE_COMMAND lsb_release) +find_program(SW_VERS_COMMAND sw_vers) +set(OS_VERSION_STRINGS "${CMAKE_SYSTEM}") +if (VER_COMMAND) + execute_process(COMMAND ${VER_COMMAND} OUTPUT_VARIABLE VER_COMMAND_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE) + set(OS_VERSION_STRINGS "${OS_VERSION_STRINGS}; ${VER_COMMAND_RESULT}") +endif() +if (SW_VERS_COMMAND) + execute_process(COMMAND ${SW_VERS_COMMAND} OUTPUT_VARIABLE SW_VERS_COMMAND_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) + string(REGEX REPLACE "[ ]*\n" "; " SW_VERS_COMMAND_RESULT "${SW_VERS_COMMAND_RESULT}") + set(OS_VERSION_STRINGS "${OS_VERSION_STRINGS}; ${SW_VERS_COMMAND_RESULT}") +endif() +if (LSB_RELEASE_COMMAND) + execute_process(COMMAND ${LSB_RELEASE_COMMAND} -a OUTPUT_VARIABLE LSB_RELEASE_COMMAND_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) + string(REGEX REPLACE "[ ]*\n" "; " LSB_RELEASE_COMMAND_RESULT "${LSB_RELEASE_COMMAND_RESULT}") + set(OS_VERSION_STRINGS "${OS_VERSION_STRINGS}; ${LSB_RELEASE_COMMAND_RESULT}") +endif() +if (UNAME_COMMAND) + execute_process(COMMAND ${UNAME_COMMAND} -a OUTPUT_VARIABLE UNAME_COMMAND_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) + set(OS_VERSION_STRINGS "${OS_VERSION_STRINGS}; ${UNAME_COMMAND_RESULT}") +endif() + +message(STATUS "Operating system: ${OS_VERSION_STRINGS}") + +# determine the compiler (for debug and support purposes) +if (MSVC) + execute_process(COMMAND ${CMAKE_CXX_COMPILER} OUTPUT_VARIABLE CXX_VERSION_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_VARIABLE CXX_VERSION_RESULT ERROR_STRIP_TRAILING_WHITESPACE) + set(CMAKE_CXX_COMPILER "${CXX_VERSION_RESULT}; MSVC_VERSION=${MSVC_VERSION}; MSVC_TOOLSET_VERSION=${MSVC_TOOLSET_VERSION}") +else() + execute_process(COMMAND ${CMAKE_CXX_COMPILER} --version OUTPUT_VARIABLE CXX_VERSION_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() +string(REGEX REPLACE "[ ]*\n" "; " CXX_VERSION_RESULT "${CXX_VERSION_RESULT}") +message(STATUS "Compiler: ${CXX_VERSION_RESULT}") diff --git a/doc/Doxyfile b/doc/Doxyfile index c106140ad2..2c44d0069e 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.16 +# Doxyfile 1.8.19 #--------------------------------------------------------------------------- # Project related configuration options @@ -23,6 +23,7 @@ STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO +JAVADOC_BANNER = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES @@ -33,7 +34,6 @@ ALIASES = "complexity=@par Complexity^^" \ "requirement=@par Requirements^^" \ "exceptionsafety=@par Exception safety^^" \ "iterators=@par Iterator validity^^" -TCL_SUBST = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO @@ -59,6 +59,7 @@ LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = NO +EXTRACT_PRIV_VIRTUAL = NO EXTRACT_PACKAGE = YES EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES @@ -140,9 +141,6 @@ REFERENCES_LINK_SOURCE = NO SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = NO -CLANG_ASSISTED_PARSING = YES -CLANG_OPTIONS = -std=c++11 -CLANG_DATABASE_PATH = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -194,8 +192,10 @@ GENERATE_TREEVIEW = NO ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +HTML_FORMULA_FORMAT = png FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +FORMULA_MACROFILE = USE_MATHJAX = NO MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest @@ -293,12 +293,10 @@ GENERATE_TAGFILE = html/nlohmann_json.tag ALLEXTERNALS = NO EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES -PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = NO -MSCGEN_PATH = DIA_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES diff --git a/doc/examples/README.link b/doc/examples/README.link index 24bb7422d8..d4ef2eb064 100644 --- a/doc/examples/README.link +++ b/doc/examples/README.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/array.link b/doc/examples/array.link index 5f02f8c5cb..4c69b84ecf 100644 --- a/doc/examples/array.link +++ b/doc/examples/array.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/at__object_t_key_type.link b/doc/examples/at__object_t_key_type.link index bac67ae9ab..0518fe656f 100644 --- a/doc/examples/at__object_t_key_type.link +++ b/doc/examples/at__object_t_key_type.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/at__object_t_key_type_const.link b/doc/examples/at__object_t_key_type_const.link index 3000cc5795..a7cd73be7b 100644 --- a/doc/examples/at__object_t_key_type_const.link +++ b/doc/examples/at__object_t_key_type_const.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/at__size_type.link b/doc/examples/at__size_type.link index b06ef70bf9..a98adcb036 100644 --- a/doc/examples/at__size_type.link +++ b/doc/examples/at__size_type.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/at__size_type_const.link b/doc/examples/at__size_type_const.link index ba465d31e6..73a13bb1cb 100644 --- a/doc/examples/at__size_type_const.link +++ b/doc/examples/at__size_type_const.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/at_json_pointer.link b/doc/examples/at_json_pointer.link index 4bae0ca359..a4e2c24fc2 100644 --- a/doc/examples/at_json_pointer.link +++ b/doc/examples/at_json_pointer.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/at_json_pointer_const.link b/doc/examples/at_json_pointer_const.link index 21e000c59b..f04bf5a670 100644 --- a/doc/examples/at_json_pointer_const.link +++ b/doc/examples/at_json_pointer_const.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/back.link b/doc/examples/back.link index 09a85fda5d..7fe2400a86 100644 --- a/doc/examples/back.link +++ b/doc/examples/back.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__CompatibleType.link b/doc/examples/basic_json__CompatibleType.link index 6f401441c7..a6336474ca 100644 --- a/doc/examples/basic_json__CompatibleType.link +++ b/doc/examples/basic_json__CompatibleType.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__InputIt_InputIt.link b/doc/examples/basic_json__InputIt_InputIt.link index bd8170d938..844e914f53 100644 --- a/doc/examples/basic_json__InputIt_InputIt.link +++ b/doc/examples/basic_json__InputIt_InputIt.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__basic_json.link b/doc/examples/basic_json__basic_json.link index 8e44fa7c52..757e2c7615 100644 --- a/doc/examples/basic_json__basic_json.link +++ b/doc/examples/basic_json__basic_json.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__copyassignment.link b/doc/examples/basic_json__copyassignment.link index b558aa04a7..86beb677ad 100644 --- a/doc/examples/basic_json__copyassignment.link +++ b/doc/examples/basic_json__copyassignment.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__list_init_t.link b/doc/examples/basic_json__list_init_t.link index b92a9fa258..126f692807 100644 --- a/doc/examples/basic_json__list_init_t.link +++ b/doc/examples/basic_json__list_init_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__moveconstructor.link b/doc/examples/basic_json__moveconstructor.link index c45e09bb6a..9318284e85 100644 --- a/doc/examples/basic_json__moveconstructor.link +++ b/doc/examples/basic_json__moveconstructor.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__nullptr_t.link b/doc/examples/basic_json__nullptr_t.link index 30568efb41..bcc4e96010 100644 --- a/doc/examples/basic_json__nullptr_t.link +++ b/doc/examples/basic_json__nullptr_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__size_type_basic_json.link b/doc/examples/basic_json__size_type_basic_json.link index 2b286dc1d7..6a6742b913 100644 --- a/doc/examples/basic_json__size_type_basic_json.link +++ b/doc/examples/basic_json__size_type_basic_json.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__value.link b/doc/examples/basic_json__value.link index df2bbdc58f..f47a80d6bc 100644 --- a/doc/examples/basic_json__value.link +++ b/doc/examples/basic_json__value.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__value_ptr.link b/doc/examples/basic_json__value_ptr.link index 3f393692db..14d3851b4a 100644 --- a/doc/examples/basic_json__value_ptr.link +++ b/doc/examples/basic_json__value_ptr.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/basic_json__value_t.link b/doc/examples/basic_json__value_t.link index 8dc41b22ee..d80f2482a6 100644 --- a/doc/examples/basic_json__value_t.link +++ b/doc/examples/basic_json__value_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/begin.link b/doc/examples/begin.link index 3875421d9e..1f59a9bd77 100644 --- a/doc/examples/begin.link +++ b/doc/examples/begin.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/cbegin.link b/doc/examples/cbegin.link index 547ae48589..26ff773428 100644 --- a/doc/examples/cbegin.link +++ b/doc/examples/cbegin.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/cend.link b/doc/examples/cend.link index 7b8bcba41b..a1adb3a82d 100644 --- a/doc/examples/cend.link +++ b/doc/examples/cend.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/clear.link b/doc/examples/clear.link index cad700c32a..0146c18783 100644 --- a/doc/examples/clear.link +++ b/doc/examples/clear.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/contains.link b/doc/examples/contains.link index c66676a6b7..f57e702681 100644 --- a/doc/examples/contains.link +++ b/doc/examples/contains.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/contains_json_pointer.cpp b/doc/examples/contains_json_pointer.cpp index 5375a43526..54bcaa9e4a 100644 --- a/doc/examples/contains_json_pointer.cpp +++ b/doc/examples/contains_json_pointer.cpp @@ -20,7 +20,6 @@ int main() << j.contains("/array/4"_json_pointer) << '\n' << j.contains("/baz"_json_pointer) << std::endl; - // out_of_range.106 try { // try to use an array index with leading '0' @@ -31,7 +30,6 @@ int main() std::cout << e.what() << '\n'; } - // out_of_range.109 try { // try to use an array index that is not a number diff --git a/doc/examples/contains_json_pointer.link b/doc/examples/contains_json_pointer.link index 778e151f16..1648e373d1 100644 --- a/doc/examples/contains_json_pointer.link +++ b/doc/examples/contains_json_pointer.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/contains_json_pointer.output b/doc/examples/contains_json_pointer.output index 82ec36758c..dd1eb38c19 100644 --- a/doc/examples/contains_json_pointer.output +++ b/doc/examples/contains_json_pointer.output @@ -2,9 +2,6 @@ true true true true -true false false false -[json.exception.parse_error.106] parse error: array index '01' must not begin with '0' -[json.exception.parse_error.109] parse error: array index 'one' is not a number diff --git a/doc/examples/count.link b/doc/examples/count.link index 9355bd2cfd..2c4c081311 100644 --- a/doc/examples/count.link +++ b/doc/examples/count.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/crbegin.link b/doc/examples/crbegin.link index 6079e7a308..f7da8d6fd2 100644 --- a/doc/examples/crbegin.link +++ b/doc/examples/crbegin.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/crend.link b/doc/examples/crend.link index 1263fc1f26..fc3d145fd2 100644 --- a/doc/examples/crend.link +++ b/doc/examples/crend.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/diff.link b/doc/examples/diff.link index 8873343faa..7adb19bba8 100644 --- a/doc/examples/diff.link +++ b/doc/examples/diff.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/dump.link b/doc/examples/dump.link index bfdb31bbb3..84d944151b 100644 --- a/doc/examples/dump.link +++ b/doc/examples/dump.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/emplace.link b/doc/examples/emplace.link index 36f2ed2f83..02b41d8bb9 100644 --- a/doc/examples/emplace.link +++ b/doc/examples/emplace.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/emplace_back.link b/doc/examples/emplace_back.link index 2ea20f36e0..7290ca6953 100644 --- a/doc/examples/emplace_back.link +++ b/doc/examples/emplace_back.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/empty.link b/doc/examples/empty.link index a182c12848..cfe5867c2c 100644 --- a/doc/examples/empty.link +++ b/doc/examples/empty.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/end.link b/doc/examples/end.link index 9ef322c4b6..abe8fedd7d 100644 --- a/doc/examples/end.link +++ b/doc/examples/end.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/erase__IteratorType.link b/doc/examples/erase__IteratorType.link index 67ea55abd2..f1d7ba51dd 100644 --- a/doc/examples/erase__IteratorType.link +++ b/doc/examples/erase__IteratorType.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/erase__IteratorType_IteratorType.link b/doc/examples/erase__IteratorType_IteratorType.link index 0ea298913c..2d6d24c200 100644 --- a/doc/examples/erase__IteratorType_IteratorType.link +++ b/doc/examples/erase__IteratorType_IteratorType.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/erase__key_type.link b/doc/examples/erase__key_type.link index 1d02dd58bb..df8239156b 100644 --- a/doc/examples/erase__key_type.link +++ b/doc/examples/erase__key_type.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/erase__size_type.link b/doc/examples/erase__size_type.link index c3d94bb83c..a46d5276c8 100644 --- a/doc/examples/erase__size_type.link +++ b/doc/examples/erase__size_type.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/exception.link b/doc/examples/exception.link index ed4cb445a7..5f40ff4c76 100644 --- a/doc/examples/exception.link +++ b/doc/examples/exception.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/find__key_type.link b/doc/examples/find__key_type.link index f23abc827f..0bfcc8a70f 100644 --- a/doc/examples/find__key_type.link +++ b/doc/examples/find__key_type.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/flatten.link b/doc/examples/flatten.link index d28cafb510..ac836a5854 100644 --- a/doc/examples/flatten.link +++ b/doc/examples/flatten.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/from_bson.link b/doc/examples/from_bson.link index 59fee15835..08af52bccc 100644 --- a/doc/examples/from_bson.link +++ b/doc/examples/from_bson.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/from_cbor.link b/doc/examples/from_cbor.link index a1798c9f38..bbb578c4a7 100644 --- a/doc/examples/from_cbor.link +++ b/doc/examples/from_cbor.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/from_msgpack.link b/doc/examples/from_msgpack.link index 4512c13293..77a55594c0 100644 --- a/doc/examples/from_msgpack.link +++ b/doc/examples/from_msgpack.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/from_ubjson.link b/doc/examples/from_ubjson.link index 390612bd9c..c5f82cb48c 100644 --- a/doc/examples/from_ubjson.link +++ b/doc/examples/from_ubjson.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/front.link b/doc/examples/front.link index 172924f9a1..ab3cc8d571 100644 --- a/doc/examples/front.link +++ b/doc/examples/front.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/get__PointerType.link b/doc/examples/get__PointerType.link index 1c67f73983..f4a213e15e 100644 --- a/doc/examples/get__PointerType.link +++ b/doc/examples/get__PointerType.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/get__ValueType_const.link b/doc/examples/get__ValueType_const.link index a3de6d6e4b..cf444a129f 100644 --- a/doc/examples/get__ValueType_const.link +++ b/doc/examples/get__ValueType_const.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/get_ptr.link b/doc/examples/get_ptr.link index ab05e4a492..139fd04352 100644 --- a/doc/examples/get_ptr.link +++ b/doc/examples/get_ptr.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/get_ref.link b/doc/examples/get_ref.link index d458912b36..360d002292 100644 --- a/doc/examples/get_ref.link +++ b/doc/examples/get_ref.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/get_to.link b/doc/examples/get_to.link index 02a167b0d8..5adf0ec780 100644 --- a/doc/examples/get_to.link +++ b/doc/examples/get_to.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/insert.link b/doc/examples/insert.link index 564226f5dd..4d742039fb 100644 --- a/doc/examples/insert.link +++ b/doc/examples/insert.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/insert__count.link b/doc/examples/insert__count.link index 100cd360de..49b892bb79 100644 --- a/doc/examples/insert__count.link +++ b/doc/examples/insert__count.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/insert__ilist.link b/doc/examples/insert__ilist.link index 36b181dcb9..ea6760f9e2 100644 --- a/doc/examples/insert__ilist.link +++ b/doc/examples/insert__ilist.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/insert__range.link b/doc/examples/insert__range.link index 00e83ac221..a2c24a0870 100644 --- a/doc/examples/insert__range.link +++ b/doc/examples/insert__range.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/insert__range_object.link b/doc/examples/insert__range_object.link index 70d43ee907..9c45c47390 100644 --- a/doc/examples/insert__range_object.link +++ b/doc/examples/insert__range_object.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/invalid_iterator.link b/doc/examples/invalid_iterator.link index 729243d35c..0ef322e817 100644 --- a/doc/examples/invalid_iterator.link +++ b/doc/examples/invalid_iterator.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_array.cpp b/doc/examples/is_array.cpp index 235dab7690..8ecc45035d 100644 --- a/doc/examples/is_array.cpp +++ b/doc/examples/is_array.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary({1, 2, 3}); // call is_array() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_array() << '\n'; std::cout << j_array.is_array() << '\n'; std::cout << j_string.is_array() << '\n'; + std::cout << j_binary.is_array() << '\n'; } diff --git a/doc/examples/is_array.link b/doc/examples/is_array.link index 4232a9223f..dd17ccbecb 100644 --- a/doc/examples/is_array.link +++ b/doc/examples/is_array.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_array.output b/doc/examples/is_array.output index 5b8cc40c96..7b7ef3f191 100644 --- a/doc/examples/is_array.output +++ b/doc/examples/is_array.output @@ -6,3 +6,4 @@ false false true false +false diff --git a/doc/examples/is_binary.cpp b/doc/examples/is_binary.cpp new file mode 100644 index 0000000000..d7f049ec46 --- /dev/null +++ b/doc/examples/is_binary.cpp @@ -0,0 +1,30 @@ +#include +#include + +using json = nlohmann::json; + +int main() +{ + // create JSON values + json j_null; + json j_boolean = true; + json j_number_integer = 17; + json j_number_unsigned_integer = 12345678987654321u; + json j_number_float = 23.42; + json j_object = {{"one", 1}, {"two", 2}}; + json j_array = {1, 2, 4, 8, 16}; + json j_string = "Hello, world"; + json j_binary = json::binary({1, 2, 3}); + + // call is_binary() + std::cout << std::boolalpha; + std::cout << j_null.is_binary() << '\n'; + std::cout << j_boolean.is_binary() << '\n'; + std::cout << j_number_integer.is_binary() << '\n'; + std::cout << j_number_unsigned_integer.is_binary() << '\n'; + std::cout << j_number_float.is_binary() << '\n'; + std::cout << j_object.is_binary() << '\n'; + std::cout << j_array.is_binary() << '\n'; + std::cout << j_string.is_binary() << '\n'; + std::cout << j_binary.is_binary() << '\n'; +} diff --git a/doc/examples/is_binary.link b/doc/examples/is_binary.link new file mode 100644 index 0000000000..0443f91050 --- /dev/null +++ b/doc/examples/is_binary.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/is_binary.output b/doc/examples/is_binary.output new file mode 100644 index 0000000000..505e76e4ff --- /dev/null +++ b/doc/examples/is_binary.output @@ -0,0 +1,9 @@ +false +false +false +false +false +false +false +false +true diff --git a/doc/examples/is_boolean.cpp b/doc/examples/is_boolean.cpp index ccc202d03b..0b79819556 100644 --- a/doc/examples/is_boolean.cpp +++ b/doc/examples/is_boolean.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary({1, 2, 3}); // call is_boolean() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_boolean() << '\n'; std::cout << j_array.is_boolean() << '\n'; std::cout << j_string.is_boolean() << '\n'; + std::cout << j_binary.is_boolean() << '\n'; } diff --git a/doc/examples/is_boolean.link b/doc/examples/is_boolean.link index 19f0b0081d..3e740d6c6e 100644 --- a/doc/examples/is_boolean.link +++ b/doc/examples/is_boolean.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_boolean.output b/doc/examples/is_boolean.output index 721b3a5e2e..eace89d22e 100644 --- a/doc/examples/is_boolean.output +++ b/doc/examples/is_boolean.output @@ -6,3 +6,4 @@ false false false false +false diff --git a/doc/examples/is_discarded.cpp b/doc/examples/is_discarded.cpp index 782e649982..09016655e0 100644 --- a/doc/examples/is_discarded.cpp +++ b/doc/examples/is_discarded.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary({1, 2, 3}); // call is_discarded() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_discarded() << '\n'; std::cout << j_array.is_discarded() << '\n'; std::cout << j_string.is_discarded() << '\n'; + std::cout << j_binary.is_discarded() << '\n'; } diff --git a/doc/examples/is_discarded.link b/doc/examples/is_discarded.link index 9ec778f791..de93aa9441 100644 --- a/doc/examples/is_discarded.link +++ b/doc/examples/is_discarded.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_discarded.output b/doc/examples/is_discarded.output index 485b169670..14718f64ea 100644 --- a/doc/examples/is_discarded.output +++ b/doc/examples/is_discarded.output @@ -6,3 +6,4 @@ false false false false +false diff --git a/doc/examples/is_null.cpp b/doc/examples/is_null.cpp index 4a635092c4..8a8433260d 100644 --- a/doc/examples/is_null.cpp +++ b/doc/examples/is_null.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary({1, 2, 3}); // call is_null() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_null() << '\n'; std::cout << j_array.is_null() << '\n'; std::cout << j_string.is_null() << '\n'; + std::cout << j_binary.is_null() << '\n'; } diff --git a/doc/examples/is_null.link b/doc/examples/is_null.link index dd1a5aab3d..fbeab6e57a 100644 --- a/doc/examples/is_null.link +++ b/doc/examples/is_null.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_null.output b/doc/examples/is_null.output index 4cc64628d6..42bbee2a79 100644 --- a/doc/examples/is_null.output +++ b/doc/examples/is_null.output @@ -6,3 +6,4 @@ false false false false +false diff --git a/doc/examples/is_number.cpp b/doc/examples/is_number.cpp index eb23bd244a..f107a048a3 100644 --- a/doc/examples/is_number.cpp +++ b/doc/examples/is_number.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary({1, 2, 3}); // call is_number() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_number() << '\n'; std::cout << j_array.is_number() << '\n'; std::cout << j_string.is_number() << '\n'; + std::cout << j_binary.is_number() << '\n'; } diff --git a/doc/examples/is_number.link b/doc/examples/is_number.link index 43c52b47a3..b10372a0aa 100644 --- a/doc/examples/is_number.link +++ b/doc/examples/is_number.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_number.output b/doc/examples/is_number.output index 06dbc28239..53ef340bb0 100644 --- a/doc/examples/is_number.output +++ b/doc/examples/is_number.output @@ -6,3 +6,4 @@ true false false false +false diff --git a/doc/examples/is_number_float.cpp b/doc/examples/is_number_float.cpp index 3435a7e09e..bba2b446fb 100644 --- a/doc/examples/is_number_float.cpp +++ b/doc/examples/is_number_float.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary({1, 2, 3}); // call is_number_float() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_number_float() << '\n'; std::cout << j_array.is_number_float() << '\n'; std::cout << j_string.is_number_float() << '\n'; + std::cout << j_binary.is_number_float() << '\n'; } diff --git a/doc/examples/is_number_float.link b/doc/examples/is_number_float.link index 4d8f0ea86c..f58a47f6d0 100644 --- a/doc/examples/is_number_float.link +++ b/doc/examples/is_number_float.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_number_float.output b/doc/examples/is_number_float.output index 09afae4c97..0e64601abd 100644 --- a/doc/examples/is_number_float.output +++ b/doc/examples/is_number_float.output @@ -6,3 +6,4 @@ true false false false +false diff --git a/doc/examples/is_number_integer.cpp b/doc/examples/is_number_integer.cpp index 6f6e42043a..8d6a5ae16d 100644 --- a/doc/examples/is_number_integer.cpp +++ b/doc/examples/is_number_integer.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary({1, 2, 3}); // call is_number_integer() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_number_integer() << '\n'; std::cout << j_array.is_number_integer() << '\n'; std::cout << j_string.is_number_integer() << '\n'; + std::cout << j_binary.is_number_integer() << '\n'; } diff --git a/doc/examples/is_number_integer.link b/doc/examples/is_number_integer.link index 0233e0f727..94a351decd 100644 --- a/doc/examples/is_number_integer.link +++ b/doc/examples/is_number_integer.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_number_integer.output b/doc/examples/is_number_integer.output index be0f739368..c1df310f4c 100644 --- a/doc/examples/is_number_integer.output +++ b/doc/examples/is_number_integer.output @@ -6,3 +6,4 @@ false false false false +false diff --git a/doc/examples/is_number_unsigned.cpp b/doc/examples/is_number_unsigned.cpp index 2ea2673e55..b52ac6b31c 100644 --- a/doc/examples/is_number_unsigned.cpp +++ b/doc/examples/is_number_unsigned.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary({1, 2, 3}); // call is_number_unsigned() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_number_unsigned() << '\n'; std::cout << j_array.is_number_unsigned() << '\n'; std::cout << j_string.is_number_unsigned() << '\n'; + std::cout << j_binary.is_number_unsigned() << '\n'; } diff --git a/doc/examples/is_number_unsigned.link b/doc/examples/is_number_unsigned.link index 8981763044..c8bed21235 100644 --- a/doc/examples/is_number_unsigned.link +++ b/doc/examples/is_number_unsigned.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_number_unsigned.output b/doc/examples/is_number_unsigned.output index fdf264e0c4..e6059d48fd 100644 --- a/doc/examples/is_number_unsigned.output +++ b/doc/examples/is_number_unsigned.output @@ -6,3 +6,4 @@ false false false false +false diff --git a/doc/examples/is_object.cpp b/doc/examples/is_object.cpp index 42cedf8f0f..a0216fd210 100644 --- a/doc/examples/is_object.cpp +++ b/doc/examples/is_object.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary({1, 2, 3}); // call is_object() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_object() << '\n'; std::cout << j_array.is_object() << '\n'; std::cout << j_string.is_object() << '\n'; + std::cout << j_binary.is_object() << '\n'; } diff --git a/doc/examples/is_object.link b/doc/examples/is_object.link index 5dd305bbb0..f8240d977c 100644 --- a/doc/examples/is_object.link +++ b/doc/examples/is_object.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_object.output b/doc/examples/is_object.output index e041e39228..d9a429f8fd 100644 --- a/doc/examples/is_object.output +++ b/doc/examples/is_object.output @@ -6,3 +6,4 @@ false true false false +false diff --git a/doc/examples/is_primitive.cpp b/doc/examples/is_primitive.cpp index d70c83e372..af3968e83b 100644 --- a/doc/examples/is_primitive.cpp +++ b/doc/examples/is_primitive.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary({1, 2, 3}); // call is_primitive() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_primitive() << '\n'; std::cout << j_array.is_primitive() << '\n'; std::cout << j_string.is_primitive() << '\n'; + std::cout << j_binary.is_primitive() << '\n'; } diff --git a/doc/examples/is_primitive.link b/doc/examples/is_primitive.link index ae58b2d7c9..7f1664989c 100644 --- a/doc/examples/is_primitive.link +++ b/doc/examples/is_primitive.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_primitive.output b/doc/examples/is_primitive.output index 635db6e348..77af24c009 100644 --- a/doc/examples/is_primitive.output +++ b/doc/examples/is_primitive.output @@ -6,3 +6,4 @@ true false false true +true diff --git a/doc/examples/is_string.cpp b/doc/examples/is_string.cpp index 2679cd1c5f..c89f550b06 100644 --- a/doc/examples/is_string.cpp +++ b/doc/examples/is_string.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary({1, 2, 3}); // call is_string() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_string() << '\n'; std::cout << j_array.is_string() << '\n'; std::cout << j_string.is_string() << '\n'; + std::cout << j_binary.is_string() << '\n'; } diff --git a/doc/examples/is_string.link b/doc/examples/is_string.link index 92b05e7580..999ccd9483 100644 --- a/doc/examples/is_string.link +++ b/doc/examples/is_string.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_string.output b/doc/examples/is_string.output index 672eb43858..6446f18784 100644 --- a/doc/examples/is_string.output +++ b/doc/examples/is_string.output @@ -6,3 +6,4 @@ false false false true +false diff --git a/doc/examples/is_structured.cpp b/doc/examples/is_structured.cpp index 33e2bbd059..41947b1be4 100644 --- a/doc/examples/is_structured.cpp +++ b/doc/examples/is_structured.cpp @@ -14,6 +14,7 @@ int main() json j_object = {{"one", 1}, {"two", 2}}; json j_array = {1, 2, 4, 8, 16}; json j_string = "Hello, world"; + json j_binary = json::binary({1, 2, 3}); // call is_structured() std::cout << std::boolalpha; @@ -25,4 +26,5 @@ int main() std::cout << j_object.is_structured() << '\n'; std::cout << j_array.is_structured() << '\n'; std::cout << j_string.is_structured() << '\n'; + std::cout << j_binary.is_structured() << '\n'; } diff --git a/doc/examples/is_structured.link b/doc/examples/is_structured.link index 44e06e9ffc..e54b5c1eef 100644 --- a/doc/examples/is_structured.link +++ b/doc/examples/is_structured.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/is_structured.output b/doc/examples/is_structured.output index e1186dd8f8..625c124b52 100644 --- a/doc/examples/is_structured.output +++ b/doc/examples/is_structured.output @@ -6,3 +6,4 @@ false true true false +false diff --git a/doc/examples/items.link b/doc/examples/items.link index 001c704a9a..61f135604f 100644 --- a/doc/examples/items.link +++ b/doc/examples/items.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/iterator_wrapper.link b/doc/examples/iterator_wrapper.link index 434031bb97..43c08ab076 100644 --- a/doc/examples/iterator_wrapper.link +++ b/doc/examples/iterator_wrapper.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/json_pointer.link b/doc/examples/json_pointer.link index 952bd74909..1d3074ffe1 100644 --- a/doc/examples/json_pointer.link +++ b/doc/examples/json_pointer.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/json_pointer__back.link b/doc/examples/json_pointer__back.link index adca23d751..0ab9687b64 100644 --- a/doc/examples/json_pointer__back.link +++ b/doc/examples/json_pointer__back.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/json_pointer__empty.link b/doc/examples/json_pointer__empty.link index e3759120b2..bac9163fb7 100644 --- a/doc/examples/json_pointer__empty.link +++ b/doc/examples/json_pointer__empty.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/json_pointer__operator_add.link b/doc/examples/json_pointer__operator_add.link index adc1ad519e..08cfe8c244 100644 --- a/doc/examples/json_pointer__operator_add.link +++ b/doc/examples/json_pointer__operator_add.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/json_pointer__operator_add_binary.link b/doc/examples/json_pointer__operator_add_binary.link index 4f7ed168b2..b973a409f6 100644 --- a/doc/examples/json_pointer__operator_add_binary.link +++ b/doc/examples/json_pointer__operator_add_binary.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/json_pointer__parent_pointer.link b/doc/examples/json_pointer__parent_pointer.link index 4aa6327ed5..63edd5eef2 100644 --- a/doc/examples/json_pointer__parent_pointer.link +++ b/doc/examples/json_pointer__parent_pointer.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/json_pointer__pop_back.link b/doc/examples/json_pointer__pop_back.link index a9f9c88b8c..93872948d8 100644 --- a/doc/examples/json_pointer__pop_back.link +++ b/doc/examples/json_pointer__pop_back.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/json_pointer__push_back.link b/doc/examples/json_pointer__push_back.link index d0cadd7caf..df40229e22 100644 --- a/doc/examples/json_pointer__push_back.link +++ b/doc/examples/json_pointer__push_back.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/json_pointer__to_string.link b/doc/examples/json_pointer__to_string.link index 244c481541..51eb09342c 100644 --- a/doc/examples/json_pointer__to_string.link +++ b/doc/examples/json_pointer__to_string.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/max_size.link b/doc/examples/max_size.link index 1212833df1..52bd366c73 100644 --- a/doc/examples/max_size.link +++ b/doc/examples/max_size.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/merge_patch.link b/doc/examples/merge_patch.link index 41aa4effaf..2410989403 100644 --- a/doc/examples/merge_patch.link +++ b/doc/examples/merge_patch.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/meta.link b/doc/examples/meta.link index f57174593d..6955de3d70 100644 --- a/doc/examples/meta.link +++ b/doc/examples/meta.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/meta.output b/doc/examples/meta.output index b5da2dfc8c..4e90365521 100644 --- a/doc/examples/meta.output +++ b/doc/examples/meta.output @@ -2,7 +2,7 @@ "compiler": { "c++": "201103", "family": "clang", - "version": "11.0.0 (clang-1100.0.33.8)" + "version": "11.0.3 (clang-1103.0.32.62)" }, "copyright": "(C) 2013-2017 Niels Lohmann", "name": "JSON for Modern C++", diff --git a/doc/examples/object.link b/doc/examples/object.link index fbed2f67d7..322128e7e8 100644 --- a/doc/examples/object.link +++ b/doc/examples/object.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operator__ValueType.link b/doc/examples/operator__ValueType.link index 798b8fdffd..e4db023a2d 100644 --- a/doc/examples/operator__ValueType.link +++ b/doc/examples/operator__ValueType.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operator__equal.link b/doc/examples/operator__equal.link index 6e1cc1cce6..966c4f732e 100644 --- a/doc/examples/operator__equal.link +++ b/doc/examples/operator__equal.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operator__equal__nullptr_t.link b/doc/examples/operator__equal__nullptr_t.link index 097da7769a..5321622b14 100644 --- a/doc/examples/operator__equal__nullptr_t.link +++ b/doc/examples/operator__equal__nullptr_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operator__greater.link b/doc/examples/operator__greater.link index c59a48a108..b52b4b8321 100644 --- a/doc/examples/operator__greater.link +++ b/doc/examples/operator__greater.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operator__greaterequal.link b/doc/examples/operator__greaterequal.link index 62ce78a6a9..7b9b42cfbd 100644 --- a/doc/examples/operator__greaterequal.link +++ b/doc/examples/operator__greaterequal.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operator__less.link b/doc/examples/operator__less.link index 52da7af1b4..96d69ee3ad 100644 --- a/doc/examples/operator__less.link +++ b/doc/examples/operator__less.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operator__lessequal.link b/doc/examples/operator__lessequal.link index 0c9ede3aba..750c6fcfc5 100644 --- a/doc/examples/operator__lessequal.link +++ b/doc/examples/operator__lessequal.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operator__notequal.link b/doc/examples/operator__notequal.link index 656ffa8a0e..bb094c4f73 100644 --- a/doc/examples/operator__notequal.link +++ b/doc/examples/operator__notequal.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operator__notequal__nullptr_t.link b/doc/examples/operator__notequal__nullptr_t.link index f9a17003f3..3a013c2e9f 100644 --- a/doc/examples/operator__notequal__nullptr_t.link +++ b/doc/examples/operator__notequal__nullptr_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operator__value_t.link b/doc/examples/operator__value_t.link index d2c38a37ce..71cb2143ee 100644 --- a/doc/examples/operator__value_t.link +++ b/doc/examples/operator__value_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operator_deserialize.link b/doc/examples/operator_deserialize.link index 1d34af298b..2e4f7283ab 100644 --- a/doc/examples/operator_deserialize.link +++ b/doc/examples/operator_deserialize.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operator_serialize.link b/doc/examples/operator_serialize.link index 2367d1c36e..f6d157ecfe 100644 --- a/doc/examples/operator_serialize.link +++ b/doc/examples/operator_serialize.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operatorarray__key_type.link b/doc/examples/operatorarray__key_type.link index 50fd455e1d..e03051d587 100644 --- a/doc/examples/operatorarray__key_type.link +++ b/doc/examples/operatorarray__key_type.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operatorarray__key_type_const.link b/doc/examples/operatorarray__key_type_const.link index 9467ca2c14..a9023380c3 100644 --- a/doc/examples/operatorarray__key_type_const.link +++ b/doc/examples/operatorarray__key_type_const.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operatorarray__size_type.link b/doc/examples/operatorarray__size_type.link index 018f0bde94..73b8b16923 100644 --- a/doc/examples/operatorarray__size_type.link +++ b/doc/examples/operatorarray__size_type.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operatorarray__size_type_const.link b/doc/examples/operatorarray__size_type_const.link index 8a9cd0b207..3f23018976 100644 --- a/doc/examples/operatorarray__size_type_const.link +++ b/doc/examples/operatorarray__size_type_const.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operatorjson_pointer.link b/doc/examples/operatorjson_pointer.link index 92468d781d..fa7aecb531 100644 --- a/doc/examples/operatorjson_pointer.link +++ b/doc/examples/operatorjson_pointer.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/operatorjson_pointer_const.link b/doc/examples/operatorjson_pointer_const.link index 836e755db6..eb01e1f030 100644 --- a/doc/examples/operatorjson_pointer_const.link +++ b/doc/examples/operatorjson_pointer_const.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/other_error.link b/doc/examples/other_error.link index bdb11752bc..f542efd122 100644 --- a/doc/examples/other_error.link +++ b/doc/examples/other_error.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/out_of_range.link b/doc/examples/out_of_range.link index 790eec6249..25833f31cd 100644 --- a/doc/examples/out_of_range.link +++ b/doc/examples/out_of_range.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/parse__array__parser_callback_t.link b/doc/examples/parse__array__parser_callback_t.link index 02a92b80bd..cdecb14a23 100644 --- a/doc/examples/parse__array__parser_callback_t.link +++ b/doc/examples/parse__array__parser_callback_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/parse__contiguouscontainer__parser_callback_t.link b/doc/examples/parse__contiguouscontainer__parser_callback_t.link index 8153d5d2e5..c681b8c3f9 100644 --- a/doc/examples/parse__contiguouscontainer__parser_callback_t.link +++ b/doc/examples/parse__contiguouscontainer__parser_callback_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/parse__istream__parser_callback_t.link b/doc/examples/parse__istream__parser_callback_t.link index 3b51c08b31..21118db292 100644 --- a/doc/examples/parse__istream__parser_callback_t.link +++ b/doc/examples/parse__istream__parser_callback_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/parse__iteratortype__parser_callback_t.link b/doc/examples/parse__iteratortype__parser_callback_t.link index 4e0174a0c8..d12ba20b7e 100644 --- a/doc/examples/parse__iteratortype__parser_callback_t.link +++ b/doc/examples/parse__iteratortype__parser_callback_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/parse__string__parser_callback_t.link b/doc/examples/parse__string__parser_callback_t.link index fb6bec03e3..2d24e2ec7c 100644 --- a/doc/examples/parse__string__parser_callback_t.link +++ b/doc/examples/parse__string__parser_callback_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/parse_error.link b/doc/examples/parse_error.link index 7e3e8644a8..f3c0da7890 100644 --- a/doc/examples/parse_error.link +++ b/doc/examples/parse_error.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/patch.link b/doc/examples/patch.link index 1a9d189f6b..d9b370a8dc 100644 --- a/doc/examples/patch.link +++ b/doc/examples/patch.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/push_back.link b/doc/examples/push_back.link index 4ee15bf1c8..b89d5c6359 100644 --- a/doc/examples/push_back.link +++ b/doc/examples/push_back.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/push_back__initializer_list.link b/doc/examples/push_back__initializer_list.link index f18e96f441..4e57e93a37 100644 --- a/doc/examples/push_back__initializer_list.link +++ b/doc/examples/push_back__initializer_list.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/push_back__object_t__value.link b/doc/examples/push_back__object_t__value.link index 396fdf25a1..6e0cf7b13b 100644 --- a/doc/examples/push_back__object_t__value.link +++ b/doc/examples/push_back__object_t__value.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/rbegin.link b/doc/examples/rbegin.link index 8c8c924168..06beb46da4 100644 --- a/doc/examples/rbegin.link +++ b/doc/examples/rbegin.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/rend.link b/doc/examples/rend.link index 596faf602e..bf8e33d4b1 100644 --- a/doc/examples/rend.link +++ b/doc/examples/rend.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/sax_parse.cpp b/doc/examples/sax_parse.cpp index 69e8962c36..45273eb6c2 100644 --- a/doc/examples/sax_parse.cpp +++ b/doc/examples/sax_parse.cpp @@ -79,6 +79,12 @@ class sax_event_consumer : public json::json_sax_t return true; } + bool binary(json::binary_t& val) override + { + events.push_back("binary"); + return true; + } + bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex) override { events.push_back("error: " + std::string(ex.what())); diff --git a/doc/examples/sax_parse.link b/doc/examples/sax_parse.link index 14aa5e3819..8bab127427 100644 --- a/doc/examples/sax_parse.link +++ b/doc/examples/sax_parse.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/size.link b/doc/examples/size.link index 7b2f25014a..31f00cad4a 100644 --- a/doc/examples/size.link +++ b/doc/examples/size.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/swap__array_t.link b/doc/examples/swap__array_t.link index 53670b8e6a..75937905d0 100644 --- a/doc/examples/swap__array_t.link +++ b/doc/examples/swap__array_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/swap__binary_t.cpp b/doc/examples/swap__binary_t.cpp new file mode 100644 index 0000000000..4b8fc3db7b --- /dev/null +++ b/doc/examples/swap__binary_t.cpp @@ -0,0 +1,20 @@ +#include +#include + +using json = nlohmann::json; + +int main() +{ + // create a binary value + json value = json::binary({1, 2, 3}); + + // create a binary_t + json::binary_t binary = {{4, 5, 6}}; + + // swap the object stored in the JSON value + value.swap(binary); + + // output the values + std::cout << "value = " << value << '\n'; + std::cout << "binary = " << json(binary) << '\n'; +} diff --git a/doc/examples/swap__binary_t.output b/doc/examples/swap__binary_t.output new file mode 100644 index 0000000000..68cd768921 --- /dev/null +++ b/doc/examples/swap__binary_t.output @@ -0,0 +1,2 @@ +value = {"bytes":[4,5,6],"subtype":null} +binary = {"bytes":[1,2,3],"subtype":null} diff --git a/doc/examples/swap__object_t.link b/doc/examples/swap__object_t.link index 256be861ea..45b04995a5 100644 --- a/doc/examples/swap__object_t.link +++ b/doc/examples/swap__object_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/swap__reference.link b/doc/examples/swap__reference.link index 49207f61a4..f056ec405b 100644 --- a/doc/examples/swap__reference.link +++ b/doc/examples/swap__reference.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/swap__string_t.link b/doc/examples/swap__string_t.link index b9fd75bd81..9b00374eb8 100644 --- a/doc/examples/swap__string_t.link +++ b/doc/examples/swap__string_t.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/to_bson.link b/doc/examples/to_bson.link index c069a220f2..3bad4a1c4c 100644 --- a/doc/examples/to_bson.link +++ b/doc/examples/to_bson.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/to_cbor.link b/doc/examples/to_cbor.link index bc4543afae..efac9b06c2 100644 --- a/doc/examples/to_cbor.link +++ b/doc/examples/to_cbor.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/to_msgpack.link b/doc/examples/to_msgpack.link index 9a3a0985fe..4c7a078cab 100644 --- a/doc/examples/to_msgpack.link +++ b/doc/examples/to_msgpack.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/to_ubjson.link b/doc/examples/to_ubjson.link index f565adba99..996e19df10 100644 --- a/doc/examples/to_ubjson.link +++ b/doc/examples/to_ubjson.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/type.link b/doc/examples/type.link index e027bd2c8f..746aaa6445 100644 --- a/doc/examples/type.link +++ b/doc/examples/type.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/type_error.link b/doc/examples/type_error.link index 6907fd7ab3..d054b5c242 100644 --- a/doc/examples/type_error.link +++ b/doc/examples/type_error.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/type_name.link b/doc/examples/type_name.link index 454da51938..c0c55597e1 100644 --- a/doc/examples/type_name.link +++ b/doc/examples/type_name.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/unflatten.link b/doc/examples/unflatten.link index 1af685376a..0d1be78d2b 100644 --- a/doc/examples/unflatten.link +++ b/doc/examples/unflatten.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/update.link b/doc/examples/update.link index 1aa7e60b78..2728616a20 100644 --- a/doc/examples/update.link +++ b/doc/examples/update.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/examples/update__range.link b/doc/examples/update__range.link index 222c6e6f62..562ebf0ce2 100644 --- a/doc/examples/update__range.link +++ b/doc/examples/update__range.link @@ -1 +1 @@ -online \ No newline at end of file +online \ No newline at end of file diff --git a/doc/index.md b/doc/index.md index f2de2e3a71..3691461d8a 100644 --- a/doc/index.md +++ b/doc/index.md @@ -22,12 +22,14 @@ These pages contain the API documentation of JSON for Modern C++, a C++11 header @link nlohmann::basic_json::is_object is_object @endlink, @link nlohmann::basic_json::is_array is_array @endlink, @link nlohmann::basic_json::is_string is_string @endlink, - @link nlohmann::basic_json::is_discarded is_discarded @endlink -- check for value type + @link nlohmann::basic_json::is_discarded is_discarded @endlink, + @link nlohmann::basic_json::is_binary is_binary @endlink -- check for value type - @link nlohmann::basic_json::operator value_t() const operator value_t @endlink -- type of the value (implicit conversion) - value access - @link nlohmann::basic_json::get get @endlink -- get a value - @link nlohmann::basic_json::get_ptr get_ptr @endlink -- get a value pointer - @link nlohmann::basic_json::get_ref get_ref @endlink -- get a value reference + - @link nlohmann::basic_json::get_binary get_binary @endlink -- get a binary value - @link nlohmann::basic_json::operator ValueType() const operator ValueType @endlink -- get a value (implicit conversion) - @link nlohmann::basic_json::value value @endlink -- get a value from an object and return default value if key is not present - exceptions @@ -67,8 +69,9 @@ These pages contain the API documentation of JSON for Modern C++, a C++11 header - @link nlohmann::basic_json::number_integer_t signed integers @endlink - @link nlohmann::basic_json::number_unsigned_t unsigned integers @endlink - @link nlohmann::basic_json::number_float_t floating-point @endlink + - @link nlohmann::basic_json::binary_t binary values @endlink - further JSON standards - - @link nlohmann::json_pointer JSON Pointer @endlink (REF 6901) + - @link nlohmann::json_pointer JSON Pointer @endlink (RFC 6901) - @link nlohmann::basic_json::patch JSON Patch @endlink (RFC 6902) - @link nlohmann::basic_json::merge_patch JSON Merge Patch @endlink (RFC 7396) @@ -324,7 +327,7 @@ Note that this table only lists those exceptions thrown due to the type. For ins -@copyright Copyright © 2013-2019 Niels Lohmann. The code is licensed under the [MIT License](http://opensource.org/licenses/MIT). +@copyright Copyright © 2013-2020 Niels Lohmann. The code is licensed under the [MIT License](http://opensource.org/licenses/MIT). @author [Niels Lohmann](http://nlohmann.me) @see https://github.com/nlohmann/json to download the source code diff --git a/doc/scripts/send_to_wandbox.py b/doc/scripts/send_to_wandbox.py index eeef6a810a..1126566940 100755 --- a/doc/scripts/send_to_wandbox.py +++ b/doc/scripts/send_to_wandbox.py @@ -42,7 +42,7 @@ def replacer(match): # Post the given JSON data to Wandbox's API, and return the result # as a JSON object. def upload(options): - request = urllib2.Request('http://melpon.org/wandbox/api/compile.json') + request = urllib2.Request('https://melpon.org/wandbox/api/compile.json') request.add_header('Content-Type', 'application/json') response = urllib2.urlopen(request, json.dumps(options)) return json.loads(response.read()) diff --git a/include/nlohmann/byte_container_with_subtype.hpp b/include/nlohmann/byte_container_with_subtype.hpp new file mode 100644 index 0000000000..69f9feb212 --- /dev/null +++ b/include/nlohmann/byte_container_with_subtype.hpp @@ -0,0 +1,166 @@ +#pragma once + +#include // uint8_t +#include // tie +#include // move + +namespace nlohmann +{ + +/*! +@brief an internal type for a backed binary type + +This type extends the template parameter @a BinaryType provided to `basic_json` +with a subtype used by BSON and MessagePack. This type exists so that the user +does not have to specify a type themselves with a specific naming scheme in +order to override the binary type. + +@tparam BinaryType container to store bytes (`std::vector` by + default) + +@since version 3.8.0 +*/ +template +class byte_container_with_subtype : public BinaryType +{ + public: + /// the type of the underlying container + using container_type = BinaryType; + + byte_container_with_subtype() noexcept(noexcept(container_type())) + : container_type() + {} + + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) + {} + + byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + {} + + byte_container_with_subtype(const container_type& b, std::uint8_t subtype) noexcept(noexcept(container_type(b))) + : container_type(b) + , m_subtype(subtype) + , m_has_subtype(true) + {} + + byte_container_with_subtype(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + , m_subtype(subtype) + , m_has_subtype(true) + {} + + bool operator==(const byte_container_with_subtype& rhs) const + { + return std::tie(static_cast(*this), m_subtype, m_has_subtype) == + std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); + } + + bool operator!=(const byte_container_with_subtype& rhs) const + { + return !(rhs == *this); + } + + /*! + @brief sets the binary subtype + + Sets the binary subtype of the value, also flags a binary JSON value as + having a subtype, which has implications for serialization. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void set_subtype(std::uint8_t subtype) noexcept + { + m_subtype = subtype; + m_has_subtype = true; + } + + /*! + @brief return the binary subtype + + Returns the numerical subtype of the value if it has a subtype. If it does + not have a subtype, this function will return size_t(-1) as a sentinel + value. + + @return the numerical subtype of the binary value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + constexpr std::uint8_t subtype() const noexcept + { + return m_subtype; + } + + /*! + @brief return whether the value has a subtype + + @return whether the value has a subtype + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + + @since version 3.8.0 + */ + constexpr bool has_subtype() const noexcept + { + return m_has_subtype; + } + + /*! + @brief clears the binary subtype + + Clears the binary subtype and flags the value as not having a subtype, which + has implications for serialization; for instance MessagePack will prefer the + bin family over the ext family. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void clear_subtype() noexcept + { + m_subtype = 0; + m_has_subtype = false; + } + + private: + std::uint8_t m_subtype = 0; + bool m_has_subtype = false; +}; + +} // namespace nlohmann diff --git a/include/nlohmann/detail/boolean_operators.hpp b/include/nlohmann/detail/boolean_operators.hpp new file mode 100644 index 0000000000..06335866b1 --- /dev/null +++ b/include/nlohmann/detail/boolean_operators.hpp @@ -0,0 +1,8 @@ +#pragma once + +// Header is removed in C++20. +// See for more information. + +#if __cplusplus <= 201703L + #include // and, not, or +#endif diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index c389dca7ad..e8731f2235 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -2,7 +2,6 @@ #include // transform #include // array -#include // and, not #include // forward_list #include // inserter, front_inserter, end #include // map @@ -13,6 +12,7 @@ #include // pair, declval #include // valarray +#include #include #include #include @@ -228,9 +228,9 @@ template ::value and not is_constructible_object_type::value and not is_constructible_string_type::value and + not std::is_same::value and not is_basic_json::value, int > = 0 > - auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) -> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), j.template get(), @@ -245,6 +245,17 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } +template +void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_binary())) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); + } + + bin = *j.template get_ptr(); +} + template::value, int> = 0> void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) diff --git a/include/nlohmann/detail/conversions/to_chars.hpp b/include/nlohmann/detail/conversions/to_chars.hpp index aed587cc2d..5851fd0ae8 100644 --- a/include/nlohmann/detail/conversions/to_chars.hpp +++ b/include/nlohmann/detail/conversions/to_chars.hpp @@ -2,12 +2,13 @@ #include // array #include // assert -#include // or, and, not #include // signbit, isfinite #include // intN_t, uintN_t #include // memcpy, memmove #include // numeric_limits #include // conditional + +#include #include namespace nlohmann diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp index 10b21d16b2..cae779c53d 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -1,7 +1,6 @@ #pragma once #include // copy -#include // or, and, not #include // begin, end #include // string #include // tuple, get @@ -10,6 +9,7 @@ #include // valarray #include // vector +#include #include #include #include @@ -74,7 +74,7 @@ struct external_constructor static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) { j.m_type = value_t::binary; - typename BasicJsonType::internal_binary_t value{b}; + typename BasicJsonType::binary_t value{b}; j.m_value = value; j.assert_invariant(); } @@ -83,7 +83,7 @@ struct external_constructor static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) { j.m_type = value_t::binary; - typename BasicJsonType::internal_binary_t value{std::move(b)}; + typename BasicJsonType::binary_t value{std::move(b)}; j.m_value = value; j.assert_invariant(); } @@ -278,9 +278,9 @@ void to_json(BasicJsonType& j, const std::vector& e) template ::value and - not is_compatible_object_type< - BasicJsonType, CompatibleArrayType>::value and + not is_compatible_object_type::value and not is_compatible_string_type::value and + not std::is_same::value and not is_basic_json::value, int> = 0> void to_json(BasicJsonType& j, const CompatibleArrayType& arr) @@ -288,6 +288,12 @@ void to_json(BasicJsonType& j, const CompatibleArrayType& arr) external_constructor::construct(j, arr); } +template +void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) +{ + external_constructor::construct(j, bin); +} + template::value, int> = 0> void to_json(BasicJsonType& j, const std::valarray& arr) diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index b8d9bec0a8..e859c7d12d 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -24,6 +24,20 @@ namespace nlohmann { namespace detail { + +/*! +@brief determine system byte order + +@return true if and only if system's byte order is little endian + +@note from https://stackoverflow.com/a/1001328/266378 +*/ +static inline bool little_endianess(int num = 1) noexcept +{ + return *reinterpret_cast(&num) == 1; +} + + /////////////////// // binary reader // /////////////////// @@ -31,14 +45,14 @@ namespace detail /*! @brief deserialization of CBOR, MessagePack, and UBJSON values */ -template> +template> class binary_reader { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; using json_sax_t = SAX; public: @@ -47,10 +61,9 @@ class binary_reader @param[in] adapter input adapter to read from */ - explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter)) + explicit binary_reader(InputAdapterType&& adapter) : ia(std::move(adapter)) { (void)detail::is_sax_static_asserts {}; - assert(ia); } // make class move-only @@ -119,18 +132,6 @@ class binary_reader return result; } - /*! - @brief determine system byte order - - @return true if and only if system's byte order is little endian - - @note from https://stackoverflow.com/a/1001328/266378 - */ - static constexpr bool little_endianess(int num = 1) noexcept - { - return *reinterpret_cast(&num) == 1; - } - private: ////////// // BSON // @@ -218,7 +219,7 @@ class binary_reader @return `true` if the byte array was successfully parsed */ template - bool get_bson_binary(const NumberType len, internal_binary_t& result) + bool get_bson_binary(const NumberType len, binary_t& result) { if (JSON_HEDLEY_UNLIKELY(len < 0)) { @@ -226,8 +227,10 @@ class binary_reader return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); } - result.has_subtype = true; // All BSON binary values have a subtype - get_number(input_format_t::bson, result.subtype); + // All BSON binary values have a subtype + std::uint8_t subtype; + get_number(input_format_t::bson, subtype); + result.set_subtype(subtype); return get_binary(input_format_t::bson, len, result); } @@ -273,7 +276,7 @@ class binary_reader case 0x05: // binary { std::int32_t len; - internal_binary_t value; + binary_t value; return get_number(input_format_t::bson, len) and get_bson_binary(len, value) and sax->binary(value); } @@ -529,7 +532,7 @@ class binary_reader case 0x5B: // Binary data (eight-byte uint64_t for n follow) case 0x5F: // Binary data (indefinite length) { - internal_binary_t b; + binary_t b; return get_cbor_binary(b) and sax->binary(b); } @@ -859,7 +862,7 @@ class binary_reader @return whether byte array creation completed */ - bool get_cbor_binary(internal_binary_t& result) + bool get_cbor_binary(binary_t& result) { if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "binary"))) { @@ -900,32 +903,36 @@ class binary_reader case 0x58: // Binary data (one-byte uint8_t for n follows) { std::uint8_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x59: // Binary data (two-byte uint16_t for n follow) { std::uint16_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x5A: // Binary data (four-byte uint32_t for n follow) { std::uint32_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x5B: // Binary data (eight-byte uint64_t for n follow) { std::uint64_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x5F: // Binary data (indefinite length) { while (get() != 0xFF) { - internal_binary_t chunk; + binary_t chunk; if (not get_cbor_binary(chunk)) { return false; @@ -1275,7 +1282,7 @@ class binary_reader case 0xD7: // fixext 8 case 0xD8: // fixext 16 { - internal_binary_t b; + binary_t b; return get_msgpack_binary(b) and sax->binary(b); } @@ -1498,95 +1505,110 @@ class binary_reader @return whether byte array creation completed */ - bool get_msgpack_binary(internal_binary_t& result) + bool get_msgpack_binary(binary_t& result) { - if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::msgpack, "binary"))) + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) { - return false; - } + result.set_subtype(static_cast(subtype)); + return true; + }; switch (current) { case 0xC4: // bin 8 { std::uint8_t len; - return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); } case 0xC5: // bin 16 { std::uint16_t len; - return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); } case 0xC6: // bin 32 { std::uint32_t len; - return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); } case 0xC7: // ext 8 { std::uint8_t len; - result.has_subtype = true; + std::int8_t subtype; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.subtype) and - get_binary(input_format_t::msgpack, len, result); + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); } case 0xC8: // ext 16 { std::uint16_t len; - result.has_subtype = true; + std::int8_t subtype; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.subtype) and - get_binary(input_format_t::msgpack, len, result); + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); } case 0xC9: // ext 32 { std::uint32_t len; - result.has_subtype = true; + std::int8_t subtype; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.subtype) and - get_binary(input_format_t::msgpack, len, result); + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); } case 0xD4: // fixext 1 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 1, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 1, result) and + assign_and_return_true(subtype); } case 0xD5: // fixext 2 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 2, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 2, result) and + assign_and_return_true(subtype); } case 0xD6: // fixext 4 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 4, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 4, result) and + assign_and_return_true(subtype); } case 0xD7: // fixext 8 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 8, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 8, result) and + assign_and_return_true(subtype); } case 0xD8: // fixext 16 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 16, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 16, result) and + assign_and_return_true(subtype); } - default: - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected binary type specification (0xC4-0xC9, 0xD4-0xD8); last byte: 0x" + last_token, "binary"))); - } + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE } } @@ -2093,7 +2115,7 @@ class binary_reader int get() { ++chars_read; - return current = ia->get_character(); + return current = ia.get_character(); } /*! @@ -2201,7 +2223,7 @@ class binary_reader template bool get_binary(const input_format_t format, const NumberType len, - internal_binary_t& result) + binary_t& result) { bool success = true; std::generate_n(std::back_inserter(result), len, [this, &success, &format]() @@ -2211,7 +2233,7 @@ class binary_reader { success = false; } - return static_cast(current); + return static_cast(current); }); return success; } @@ -2281,7 +2303,7 @@ class binary_reader private: /// input adapter - input_adapter_t ia = nullptr; + InputAdapterType ia; /// the current character int current = std::char_traits::eof(); diff --git a/include/nlohmann/detail/input/input_adapters.hpp b/include/nlohmann/detail/input/input_adapters.hpp index 92f0608e91..7ad26d00c9 100644 --- a/include/nlohmann/detail/input/input_adapters.hpp +++ b/include/nlohmann/detail/input/input_adapters.hpp @@ -27,32 +27,11 @@ enum class input_format_t { json, cbor, msgpack, ubjson, bson }; // input adapters // //////////////////// -/*! -@brief abstract input adapter interface - -Produces a stream of std::char_traits::int_type characters from a -std::istream, a buffer, or some other input type. Accepts the return of -exactly one non-EOF character for future input. The int_type characters -returned consist of all valid char values as positive values (typically -unsigned char), plus an EOF value outside that range, specified by the value -of the function std::char_traits::eof(). This value is typically -1, but -could be any arbitrary value which is not a valid char value. -*/ -struct input_adapter_protocol -{ - /// get a character [0,255] or std::char_traits::eof(). - virtual std::char_traits::int_type get_character() = 0; - virtual ~input_adapter_protocol() = default; -}; - -/// a type to simplify interfaces -using input_adapter_t = std::shared_ptr; - /*! Input adapter for stdio file access. This adapter read only 1 byte and do not use any buffer. This adapter is a very low level adapter. */ -class file_input_adapter : public input_adapter_protocol +class file_input_adapter { public: JSON_HEDLEY_NON_NULL(2) @@ -64,10 +43,9 @@ class file_input_adapter : public input_adapter_protocol file_input_adapter(const file_input_adapter&) = delete; file_input_adapter(file_input_adapter&&) = default; file_input_adapter& operator=(const file_input_adapter&) = delete; - file_input_adapter& operator=(file_input_adapter&&) = default; - ~file_input_adapter() override = default; + file_input_adapter& operator=(file_input_adapter&&) = delete; - std::char_traits::int_type get_character() noexcept override + std::char_traits::int_type get_character() noexcept { return std::fgetc(m_file); } @@ -87,48 +65,56 @@ characters following those used in parsing the JSON input. Clears the std::istream flags; any input errors (e.g., EOF) will be detected by the first subsequent call for input from the std::istream. */ -class input_stream_adapter : public input_adapter_protocol +class input_stream_adapter { public: - ~input_stream_adapter() override + ~input_stream_adapter() { // clear stream flags; we use underlying streambuf I/O, do not // maintain ifstream flags, except eof - is.clear(is.rdstate() & std::ios::eofbit); + if (is) + { + is->clear(is->rdstate() & std::ios::eofbit); + } } explicit input_stream_adapter(std::istream& i) - : is(i), sb(*i.rdbuf()) + : is(&i), sb(i.rdbuf()) {} // delete because of pointer members input_stream_adapter(const input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&) = delete; - input_stream_adapter(input_stream_adapter&&) = delete; - input_stream_adapter& operator=(input_stream_adapter&&) = delete; + input_stream_adapter& operator=(input_stream_adapter&& rhs) = delete; + + input_stream_adapter(input_stream_adapter&& rhs) : is(rhs.is), sb(rhs.sb) + { + rhs.is = nullptr; + rhs.sb = nullptr; + } // std::istream/std::streambuf use std::char_traits::to_int_type, to // ensure that std::char_traits::eof() and the character 0xFF do not // end up as the same value, eg. 0xFFFFFFFF. - std::char_traits::int_type get_character() override + std::char_traits::int_type get_character() { - auto res = sb.sbumpc(); + auto res = sb->sbumpc(); // set eof manually, as we don't use the istream interface. if (res == EOF) { - is.clear(is.rdstate() | std::ios::eofbit); + is->clear(is->rdstate() | std::ios::eofbit); } return res; } private: /// the associated input stream - std::istream& is; - std::streambuf& sb; + std::istream* is = nullptr; + std::streambuf* sb = nullptr; }; /// input adapter for buffer input -class input_buffer_adapter : public input_adapter_protocol +class input_buffer_adapter { public: input_buffer_adapter(const char* b, const std::size_t l) noexcept @@ -138,11 +124,10 @@ class input_buffer_adapter : public input_adapter_protocol // delete because of pointer members input_buffer_adapter(const input_buffer_adapter&) = delete; input_buffer_adapter& operator=(input_buffer_adapter&) = delete; - input_buffer_adapter(input_buffer_adapter&&) = delete; + input_buffer_adapter(input_buffer_adapter&&) = default; input_buffer_adapter& operator=(input_buffer_adapter&&) = delete; - ~input_buffer_adapter() override = default; - std::char_traits::int_type get_character() noexcept override + std::char_traits::int_type get_character() noexcept { if (JSON_HEDLEY_LIKELY(cursor < limit)) { @@ -285,14 +270,14 @@ struct wide_string_input_helper }; template -class wide_string_input_adapter : public input_adapter_protocol +class wide_string_input_adapter { public: explicit wide_string_input_adapter(const WideStringType& w) noexcept : str(w) {} - std::char_traits::int_type get_character() noexcept override + std::char_traits::int_type get_character() noexcept { // check if buffer needs to be filled if (utf8_bytes_index == utf8_bytes_filled) @@ -331,112 +316,164 @@ class wide_string_input_adapter : public input_adapter_protocol std::size_t utf8_bytes_filled = 0; }; -class input_adapter +inline file_input_adapter input_adapter(std::FILE* file) { - public: - // native support - JSON_HEDLEY_NON_NULL(2) - input_adapter(std::FILE* file) - : ia(std::make_shared(file)) {} - /// input adapter for input stream - input_adapter(std::istream& i) - : ia(std::make_shared(i)) {} + return file_input_adapter(file); +} + +inline input_stream_adapter input_adapter(std::istream& stream) +{ + return input_stream_adapter(stream); +} + +inline input_stream_adapter input_adapter(std::istream&& stream) +{ + return input_stream_adapter(stream); +} + +template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> +input_buffer_adapter input_adapter(CharT b, std::size_t l) +{ + return input_buffer_adapter(reinterpret_cast(b), l); +} + +template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> +input_buffer_adapter input_adapter(CharT b) +{ + return input_adapter(reinterpret_cast(b), + std::strlen(reinterpret_cast(b))); +} + +template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> +input_buffer_adapter input_adapter(IteratorType first, IteratorType last) +{ +#ifndef NDEBUG + // assertion to check that the iterator range is indeed contiguous, + // see https://stackoverflow.com/a/35008842/266378 for more discussion + const auto is_contiguous = std::accumulate( + first, last, std::pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first; + assert(is_contiguous); +#endif + + // assertion to check that each element is 1 byte long + static_assert( + sizeof(typename iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + const auto len = static_cast(std::distance(first, last)); + if (JSON_HEDLEY_LIKELY(len > 0)) + { + // there is at least one element: use the address of first + return input_buffer_adapter(reinterpret_cast(&(*first)), len); + } + else + { + // the address of first cannot be used: use nullptr + return input_buffer_adapter(nullptr, len); + } +} - /// input adapter for input stream - input_adapter(std::istream&& i) - : ia(std::make_shared(i)) {} +inline wide_string_input_adapter input_adapter(const std::wstring& ws) +{ + return wide_string_input_adapter(ws); +} - input_adapter(const std::wstring& ws) - : ia(std::make_shared>(ws)) {} - input_adapter(const std::u16string& ws) - : ia(std::make_shared>(ws)) {} +inline wide_string_input_adapter input_adapter(const std::u16string& ws) +{ + return wide_string_input_adapter(ws); +} - input_adapter(const std::u32string& ws) - : ia(std::make_shared>(ws)) {} +inline wide_string_input_adapter input_adapter(const std::u32string& ws) +{ + return wide_string_input_adapter(ws); +} + +template::value and + std::is_base_of()))>::iterator_category>::value, + int>::type = 0> +input_buffer_adapter input_adapter(const ContiguousContainer& c) +{ + return input_adapter(std::begin(c), std::end(c)); +} + + +template +input_buffer_adapter input_adapter(T (&array)[N]) +{ + return input_adapter(std::begin(array), std::end(array)); +} - /// input adapter for buffer +// This class only handles inputs of input_buffer_adapter type. +// It's required so that expressions like {ptr, len} can be implicitely casted +// to the correct adapter. +class span_input_adapter +{ + public: template::value and std::is_integral::type>::value and sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - input_adapter(CharT b, std::size_t l) - : ia(std::make_shared(reinterpret_cast(b), l)) {} + span_input_adapter(CharT b, std::size_t l) + : ia(reinterpret_cast(b), l) {} - // derived support - - /// input adapter for string literal template::value and std::is_integral::type>::value and sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - input_adapter(CharT b) - : input_adapter(reinterpret_cast(b), - std::strlen(reinterpret_cast(b))) {} + span_input_adapter(CharT b) + : span_input_adapter(reinterpret_cast(b), + std::strlen(reinterpret_cast(b))) {} - /// input adapter for iterator range with contiguous storage template::iterator_category, std::random_access_iterator_tag>::value, int>::type = 0> - input_adapter(IteratorType first, IteratorType last) - { -#ifndef NDEBUG - // assertion to check that the iterator range is indeed contiguous, - // see https://stackoverflow.com/a/35008842/266378 for more discussion - const auto is_contiguous = std::accumulate( - first, last, std::pair(true, 0), - [&first](std::pair res, decltype(*first) val) - { - res.first &= (val == *(std::next(std::addressof(*first), res.second++))); - return res; - }).first; - assert(is_contiguous); -#endif - - // assertion to check that each element is 1 byte long - static_assert( - sizeof(typename iterator_traits::value_type) == 1, - "each element in the iterator range must have the size of 1 byte"); - - const auto len = static_cast(std::distance(first, last)); - if (JSON_HEDLEY_LIKELY(len > 0)) - { - // there is at least one element: use the address of first - ia = std::make_shared(reinterpret_cast(&(*first)), len); - } - else - { - // the address of first cannot be used: use nullptr - ia = std::make_shared(nullptr, len); - } - } + span_input_adapter(IteratorType first, IteratorType last) + : ia(input_adapter(first, last)) {} - /// input adapter for array template - input_adapter(T (&array)[N]) - : input_adapter(std::begin(array), std::end(array)) {} + span_input_adapter(T (&array)[N]) + : span_input_adapter(std::begin(array), std::end(array)) {} /// input adapter for contiguous container template::value and std::is_base_of()))>::iterator_category>::value, int>::type = 0> - input_adapter(const ContiguousContainer& c) - : input_adapter(std::begin(c), std::end(c)) {} + span_input_adapter(const ContiguousContainer& c) + : span_input_adapter(std::begin(c), std::end(c)) {} - operator input_adapter_t() + input_buffer_adapter&& get() { - return ia; + return std::move(ia); } private: - /// the actual adapter - input_adapter_t ia = nullptr; + input_buffer_adapter ia; }; } // namespace detail } // namespace nlohmann diff --git a/include/nlohmann/detail/input/json_sax.hpp b/include/nlohmann/detail/input/json_sax.hpp index 47c859df01..25be7e4b85 100644 --- a/include/nlohmann/detail/input/json_sax.hpp +++ b/include/nlohmann/detail/input/json_sax.hpp @@ -23,13 +23,9 @@ input. template struct json_sax { - /// type for (signed) integers using number_integer_t = typename BasicJsonType::number_integer_t; - /// type for unsigned integers using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - /// type for floating-point numbers using number_float_t = typename BasicJsonType::number_float_t; - /// type for strings using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; @@ -214,7 +210,7 @@ class json_sax_dom_parser bool binary(binary_t& val) { - handle_binary(val); + handle_value(std::move(val)); return true; } @@ -327,36 +323,6 @@ class json_sax_dom_parser return object_element; } - /*! - @invariant If the ref stack is empty, then the passed value will be the new - root. - @invariant If the ref stack contains a value, then it is an array or an - object to which we can add elements - */ - template - JSON_HEDLEY_RETURNS_NON_NULL - BasicJsonType* handle_binary(BinaryValue&& v) - { - if (ref_stack.empty()) - { - root = BasicJsonType::binary_array(std::forward(v)); - return &root; - } - - assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); - - if (ref_stack.back()->is_array()) - { - ref_stack.back()->m_value.array->emplace_back(BasicJsonType::binary_array(std::forward(v))); - return &(ref_stack.back()->m_value.array->back()); - } - - assert(ref_stack.back()->is_object()); - assert(object_element); - *object_element = BasicJsonType::binary_array(std::forward(v)); - return object_element; - } - /// the parsed JSON value BasicJsonType& root; /// stack to model hierarchy of values @@ -434,7 +400,7 @@ class json_sax_dom_callback_parser bool binary(binary_t& val) { - handle_value(val); + handle_value(std::move(val)); return true; } diff --git a/include/nlohmann/detail/input/lexer.hpp b/include/nlohmann/detail/input/lexer.hpp index 2379cabb3c..4a36ab064d 100644 --- a/include/nlohmann/detail/input/lexer.hpp +++ b/include/nlohmann/detail/input/lexer.hpp @@ -22,19 +22,9 @@ namespace detail // lexer // /////////// -/*! -@brief lexical analysis - -This class organizes the lexical analysis during JSON deserialization. -*/ template -class lexer +class lexer_base { - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - public: /// token types for the parser enum class token_type @@ -75,9 +65,9 @@ class lexer return "null literal"; case token_type::value_string: return "string literal"; - case lexer::token_type::value_unsigned: - case lexer::token_type::value_integer: - case lexer::token_type::value_float: + case token_type::value_unsigned: + case token_type::value_integer: + case token_type::value_float: return "number literal"; case token_type::begin_array: return "'['"; @@ -103,15 +93,31 @@ class lexer // LCOV_EXCL_STOP } } +}; +/*! +@brief lexical analysis + +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer : public lexer_base +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + public: + using token_type = typename lexer_base::token_type; - explicit lexer(detail::input_adapter_t&& adapter) + explicit lexer(InputAdapterType&& adapter) : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {} // delete because of pointer members lexer(const lexer&) = delete; - lexer(lexer&&) = delete; + lexer(lexer&&) = default; lexer& operator=(lexer&) = delete; - lexer& operator=(lexer&&) = delete; + lexer& operator=(lexer&&) = default; ~lexer() = default; private: @@ -1256,7 +1262,7 @@ class lexer } else { - current = ia->get_character(); + current = ia.get_character(); } if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) @@ -1480,7 +1486,7 @@ class lexer private: /// input adapter - detail::input_adapter_t ia = nullptr; + InputAdapterType ia; /// the current character std::char_traits::int_type current = std::char_traits::eof(); diff --git a/include/nlohmann/detail/input/parser.hpp b/include/nlohmann/detail/input/parser.hpp index 8ff7ca4be3..0546b88cb8 100644 --- a/include/nlohmann/detail/input/parser.hpp +++ b/include/nlohmann/detail/input/parser.hpp @@ -24,44 +24,45 @@ namespace detail // parser // //////////// +enum class parse_event_t : uint8_t +{ + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value +}; + +template +using parser_callback_t = + std::function; + /*! @brief syntax analysis This class implements a recursive descent parser. */ -template +template class parser { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using lexer_t = lexer; + using lexer_t = lexer; using token_type = typename lexer_t::token_type; public: - enum class parse_event_t : uint8_t - { - /// the parser read `{` and started to process a JSON object - object_start, - /// the parser read `}` and finished processing a JSON object - object_end, - /// the parser read `[` and started to process a JSON array - array_start, - /// the parser read `]` and finished processing a JSON array - array_end, - /// the parser read a key of a value in an object - key, - /// the parser finished reading a JSON value - value - }; - - using parser_callback_t = - std::function; - /// a parser reading from an input adapter - explicit parser(detail::input_adapter_t&& adapter, - const parser_callback_t cb = nullptr, + 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_) { @@ -486,7 +487,7 @@ class parser private: /// callback function - const parser_callback_t callback = nullptr; + const parser_callback_t callback = nullptr; /// the type of the last read token token_type last_token = token_type::uninitialized; /// the lexer diff --git a/include/nlohmann/detail/iterators/internal_iterator.hpp b/include/nlohmann/detail/iterators/internal_iterator.hpp index 71539a6acf..742df483a1 100644 --- a/include/nlohmann/detail/iterators/internal_iterator.hpp +++ b/include/nlohmann/detail/iterators/internal_iterator.hpp @@ -19,7 +19,7 @@ template struct internal_iterator /// iterator for JSON arrays typename BasicJsonType::array_t::iterator array_iterator {}; /// iterator for JSON binary arrays - typename BasicJsonType::binary_t::iterator binary_iterator {}; + typename BasicJsonType::binary_t::container_type::iterator binary_iterator {}; /// generic iterator for all other types primitive_iterator_t primitive_iterator {}; }; diff --git a/include/nlohmann/detail/iterators/iter_impl.hpp b/include/nlohmann/detail/iterators/iter_impl.hpp index 3a36297190..0b79202a57 100644 --- a/include/nlohmann/detail/iterators/iter_impl.hpp +++ b/include/nlohmann/detail/iterators/iter_impl.hpp @@ -1,9 +1,9 @@ #pragma once -#include // not #include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next #include // conditional, is_const, remove_const +#include #include #include #include diff --git a/include/nlohmann/detail/meta/cpp_future.hpp b/include/nlohmann/detail/meta/cpp_future.hpp index 948cd4fb0c..11f88acc9d 100644 --- a/include/nlohmann/detail/meta/cpp_future.hpp +++ b/include/nlohmann/detail/meta/cpp_future.hpp @@ -1,9 +1,10 @@ #pragma once -#include // not #include // size_t #include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include + namespace nlohmann { namespace detail diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 13e92cb48c..3b93fa8b95 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -1,10 +1,10 @@ #pragma once -#include // not #include // numeric_limits #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval +#include #include #include #include diff --git a/include/nlohmann/detail/output/binary_writer.hpp b/include/nlohmann/detail/output/binary_writer.hpp index f1f901ee12..269df0d554 100644 --- a/include/nlohmann/detail/output/binary_writer.hpp +++ b/include/nlohmann/detail/output/binary_writer.hpp @@ -6,6 +6,7 @@ #include // memcpy #include // numeric_limits #include // string +#include // isnan, isinf #include #include @@ -26,7 +27,7 @@ template class binary_writer { using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; public: /*! @@ -177,8 +178,35 @@ class binary_writer case value_t::number_float: { - oa->write_character(get_cbor_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); + if (std::isnan(j.m_value.number_float)) + { + // NaN is 0xf97e00 in CBOR + oa->write_character(to_char_type(0xF9)); + oa->write_character(to_char_type(0x7E)); + oa->write_character(to_char_type(0x00)); + } + else if (std::isinf(j.m_value.number_float)) + { + // Infinity is 0xf97c00, -Infinity is 0xf9fc00 + oa->write_character(to_char_type(0xf9)); + oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); + oa->write_character(to_char_type(0x00)); + } + 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); + } + } break; } @@ -550,7 +578,7 @@ class binary_writer { // step 0: determine if the binary type has a set subtype to // determine whether or not to use the ext or fixext types - const bool use_ext = j.m_value.binary->has_subtype; + const bool use_ext = j.m_value.binary->has_subtype(); // step 1: write control byte and the byte string length const auto N = j.m_value.binary->size(); @@ -630,7 +658,7 @@ class binary_writer // step 1.5: if this is an ext type, write the subtype if (use_ext) { - write_number(j.m_value.binary->subtype); + write_number(static_cast(j.m_value.binary->subtype())); } // step 2: write the byte string @@ -1052,7 +1080,7 @@ class binary_writer /*! @return The size of the BSON-encoded binary array @a value */ - static std::size_t calc_bson_binary_size(const typename BasicJsonType::internal_binary_t& value) + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) { return sizeof(std::int32_t) + value.size() + 1ul; } @@ -1080,21 +1108,14 @@ class binary_writer @brief Writes a BSON element with key @a name and binary value @a value */ void write_bson_binary(const string_t& name, - const internal_binary_t& value) + const binary_t& value) { write_bson_entry_header(name, 0x05); write_number(static_cast(value.size())); - std::uint8_t subtype = 0x00; // Generic Binary Subtype - if (value.has_subtype) - { - subtype = value.subtype; - } - write_number(subtype); + write_number(value.has_subtype() ? value.subtype() : std::uint8_t(0x00)); - oa->write_characters( - reinterpret_cast(value.data()), - value.size()); + oa->write_characters(reinterpret_cast(value.data()), value.size()); } /*! @@ -1540,7 +1561,7 @@ class binary_writer private: /// whether we can assume little endianess - const bool is_little_endian = binary_reader::little_endianess(); + const bool is_little_endian = little_endianess(); /// the output output_adapter_t oa = nullptr; diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index f83f4a3778..e8f31fd8a5 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -3,7 +3,6 @@ #include // reverse, remove, fill, find, none_of #include // array #include // assert -#include // and, or #include // localeconv, lconv #include // labs, isfinite, isnan, signbit #include // size_t, ptrdiff_t @@ -14,6 +13,7 @@ #include // is_same #include // move +#include #include #include #include @@ -45,7 +45,7 @@ class serializer using number_float_t = typename BasicJsonType::number_float_t; using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using binary_t = typename BasicJsonType::binary_t; + using binary_char_t = typename BasicJsonType::binary_t::value_type; static constexpr std::uint8_t UTF8_ACCEPT = 0; static constexpr std::uint8_t UTF8_REJECT = 1; @@ -84,19 +84,22 @@ class serializer - strings and object keys are escaped using `escape_string()` - integer numbers are converted implicitly via `operator<<` - floating-point numbers are converted to a string using `"%g"` format - - if specified to, binary values are output using the syntax `b[]`, otherwise an exception is thrown + - binary values are serialized as objects containing the subtype and the + byte array @param[in] val value to serialize @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. @param[in] indent_step the indent level @param[in] current_indent the current indent level (only used internally) - @param[in] serialize_binary whether the output shall include non-standard binary output */ - void dump(const BasicJsonType& val, const bool pretty_print, + void dump(const BasicJsonType& val, + const bool pretty_print, const bool ensure_ascii, const unsigned int indent_step, - const unsigned int current_indent = 0, - const bool serialize_binary = false) + const unsigned int current_indent = 0) { switch (val.m_type) { @@ -127,7 +130,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\": ", 3); - dump(i->second, true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(i->second, true, ensure_ascii, indent_step, new_indent); o->write_characters(",\n", 2); } @@ -138,7 +141,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\": ", 3); - dump(i->second, true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(i->second, true, ensure_ascii, indent_step, new_indent); o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); @@ -155,7 +158,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); - dump(i->second, false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(i->second, false, ensure_ascii, indent_step, current_indent); o->write_character(','); } @@ -165,7 +168,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); - dump(i->second, false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(i->second, false, ensure_ascii, indent_step, current_indent); o->write_character('}'); } @@ -197,14 +200,14 @@ class serializer i != val.m_value.array->cend() - 1; ++i) { o->write_characters(indent_string.c_str(), new_indent); - dump(*i, true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(*i, true, ensure_ascii, indent_step, new_indent); o->write_characters(",\n", 2); } // last element assert(not val.m_value.array->empty()); o->write_characters(indent_string.c_str(), new_indent); - dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); @@ -218,13 +221,13 @@ class serializer for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) { - dump(*i, false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(*i, false, ensure_ascii, indent_step, current_indent); o->write_character(','); } // last element assert(not val.m_value.array->empty()); - dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); o->write_character(']'); } @@ -242,48 +245,73 @@ class serializer case value_t::binary: { - if (not serialize_binary) + if (pretty_print) { - JSON_THROW(type_error::create(317, "cannot serialize binary data to text JSON")); - } + o->write_characters("{\n", 2); - if (val.m_value.binary->empty()) - { - o->write_characters("b[]", 3); - } - else if (pretty_print) - { - o->write_characters("b[", 2); - for (auto i = val.m_value.binary->cbegin(); - i != val.m_value.binary->cend() - 1; ++i) + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { - dump_integer(*i); - o->write_character(','); - if (std::distance(val.m_value.binary->cbegin(), i) % 16 == 0) - { - o->write_character('\n'); - } - else + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (not val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) { - o->write_character(' '); + dump_integer(*i); + o->write_characters(", ", 2); } + dump_integer(val.m_value.binary->back()); } - dump_integer(val.m_value.binary->back()); - o->write_character(']'); + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); } else { - o->write_characters("b[", 2); - for (auto i = val.m_value.binary->cbegin(); - i != val.m_value.binary->cend() - 1; ++i) + o->write_characters("{\"bytes\":[", 10); + + if (not val.m_value.binary->empty()) { - dump_integer(*i); - o->write_character(','); + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_value.binary->back()); } - dump_integer(val.m_value.binary->back()); - o->write_character(']'); + o->write_characters("],\"subtype\":", 12); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + o->write_character('}'); + } + else + { + o->write_characters("null}", 5); + } } return; } @@ -645,7 +673,7 @@ class serializer template::value or std::is_same::value or - std::is_same::value, + std::is_same::value, int> = 0> void dump_integer(NumberType x) { diff --git a/include/nlohmann/detail/value_t.hpp b/include/nlohmann/detail/value_t.hpp index 86b4630b96..7580d856af 100644 --- a/include/nlohmann/detail/value_t.hpp +++ b/include/nlohmann/detail/value_t.hpp @@ -1,11 +1,12 @@ #pragma once #include // array -#include // and #include // size_t #include // uint8_t #include // string +#include + namespace nlohmann { namespace detail diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 237a48b175..2195dcb438 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -36,7 +36,6 @@ SOFTWARE. #include // all_of, find, for_each #include // assert -#include // and, not, or #include // nullptr_t, ptrdiff_t, size_t #include // hash, less #include // initializer_list @@ -49,6 +48,8 @@ SOFTWARE. #include // vector #include +#include +#include #include #include #include @@ -170,13 +171,15 @@ class basic_json private: template friend struct detail::external_constructor; friend ::nlohmann::json_pointer; - friend ::nlohmann::detail::parser; + + template + friend class ::nlohmann::detail::parser; friend ::nlohmann::detail::serializer; template friend class ::nlohmann::detail::iter_impl; template friend class ::nlohmann::detail::binary_writer; - template + template friend class ::nlohmann::detail::binary_reader; template friend class ::nlohmann::detail::json_sax_dom_parser; @@ -187,8 +190,17 @@ class basic_json using basic_json_t = NLOHMANN_BASIC_JSON_TPL; // convenience aliases for types residing in namespace detail; - using lexer = ::nlohmann::detail::lexer; - using parser = ::nlohmann::detail::parser; + using lexer = ::nlohmann::detail::lexer_base; + + template + static ::nlohmann::detail::parser parser( + InputAdapterType adapter, + detail::parser_callback_tcb = nullptr, + bool allow_exceptions = true + ) + { + return ::nlohmann::detail::parser(std::move(adapter), std::move(cb), allow_exceptions); + } using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; template @@ -202,7 +214,8 @@ class basic_json template using output_adapter_t = ::nlohmann::detail::output_adapter_t; - using binary_reader = ::nlohmann::detail::binary_reader; + template + using binary_reader = ::nlohmann::detail::binary_reader; template using binary_writer = ::nlohmann::detail::binary_writer; using serializer = ::nlohmann::detail::serializer; @@ -826,21 +839,20 @@ class basic_json This type is a type designed to carry binary data that appears in various serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and - BSON's generic binary subtype. This type is NOT a part of standard JSON and - exists solely for compatibility with these binary types. As such, it is + BSON's generic binary subtype. This type is NOT a part of standard JSON and + exists solely for compatibility with these binary types. As such, it is simply defined as an ordered sequence of zero or more byte values. Additionally, as an implementation detail, the subtype of the binary data is - carried around as a `unint8_t`, which is compatible with both of the binary - data formats that use binary subtyping, (though the specific numbering is - incompatible with each other, and it is up to the user to translate between - them). + carried around as a `std::uint8_t`, which is compatible with both of the + binary data formats that use binary subtyping, (though the specific + numbering is incompatible with each other, and it is up to the user to + translate between them). [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type as: - > Major type 2: a byte string. The string's length in bytes is - > represented following the rules for positive integers (major type - > 0). + > Major type 2: a byte string. The string's length in bytes is represented + > following the rules for positive integers (major type 0). [MessagePack's documentation on the bin type family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family) @@ -856,7 +868,7 @@ class basic_json None of these impose any limitations on the internal representation other than the basic unit of storage be some type of array whose parts are - decomposible into bytes. + decomposable into bytes. The default representation of this binary format is a `std::vector`, which is a very common way to represent a byte @@ -868,38 +880,30 @@ class basic_json #### Storage - Binary Arrays are stored as pointers in a @ref basic_json type. That is, + Binary Arrays are stored as pointers in a @ref basic_json type. That is, for any access to array values, a pointer of the type `binary_t*` must be dereferenced. - @sa @ref array_t -- type for an array value + #### Notes on subtypes - @since version 3.8.0 - */ + - CBOR + - Binary values are represented as byte strings. No subtypes are + supported and will be ignored when CBOR is written. + - MessagePack + - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, + or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) + is used. For other sizes, the ext family (ext8, ext16, ext32) is used. + The subtype is then added as singed 8-bit integer. + - If no subtype is given, the bin family (bin8, bin16, bin32) is used. + - BSON + - If a subtype is given, it is used and added as unsigned 8-bit integer. + - If no subtype is given, the generic binary subtype 0x00 is used. - using binary_t = BinaryType; + @sa @ref binary -- create a binary array - /*! - @brief an internal type for a backed binary type - - This type is designed to be `binary_t` but with the subtype implementation - detail. This type exists so that the user does not have to specify a struct - his- or herself with a specific naming scheme in order to override the - binary type. I.e. it's for ease of use. + @since version 3.8.0 */ - struct internal_binary_t : public BinaryType - { - using BinaryType::BinaryType; - internal_binary_t() noexcept(noexcept(BinaryType())) : BinaryType() {} - internal_binary_t(BinaryType const& bint) noexcept(noexcept(BinaryType(bint))) : BinaryType(bint) {} - internal_binary_t(BinaryType&& bint) noexcept(noexcept(BinaryType(std::move(bint)))) : BinaryType(std::move(bint)) {} - - // TOOD: If minimum C++ version is ever bumped to C++17, this field - // deserves to be a std::optional - std::uint8_t subtype = 0; - bool has_subtype = false; - }; - + using binary_t = nlohmann::byte_container_with_subtype; /// @} private: @@ -942,7 +946,7 @@ class basic_json number | number_integer | @ref number_integer_t number | number_unsigned | @ref number_unsigned_t number | number_float | @ref number_float_t - binary | binary | pointer to @ref internal_binary_t + binary | binary | pointer to @ref binary_t null | null | *no value is stored* @note Variable-length types (objects, arrays, and strings) are stored as @@ -960,7 +964,7 @@ class basic_json /// string (stored with pointer to save storage) string_t* string; /// binary (stored with pointer to save storage) - internal_binary_t* binary; + binary_t* binary; /// boolean boolean_t boolean; /// number (integer) @@ -1005,7 +1009,7 @@ class basic_json case value_t::binary: { - binary = create(); + binary = create(); break; } @@ -1088,15 +1092,27 @@ class basic_json } /// constructor for binary arrays - json_value(const binary_t& value) + json_value(const typename binary_t::container_type& value) { - binary = create(value); + binary = create(value); } /// constructor for rvalue binary arrays + json_value(typename binary_t::container_type&& value) + { + binary = create(std::move(value)); + } + + /// constructor for binary arrays (internal type) + json_value(const binary_t& value) + { + binary = create(value); + } + + /// constructor for rvalue binary arrays (internal type) json_value(binary_t&& value) { - binary = create(std::move(value)); + binary = create(std::move(value)); } void destroy(value_t t) noexcept @@ -1176,7 +1192,7 @@ class basic_json case value_t::binary: { - AllocatorType alloc; + AllocatorType alloc; std::allocator_traits::destroy(alloc, binary); std::allocator_traits::deallocate(alloc, binary, 1); break; @@ -1204,6 +1220,7 @@ class basic_json assert(m_type != value_t::object or m_value.object != nullptr); assert(m_type != value_t::array or m_value.array != nullptr); assert(m_type != value_t::string or m_value.string != nullptr); + assert(m_type != value_t::binary or m_value.binary != nullptr); } public: @@ -1226,7 +1243,7 @@ class basic_json @sa @ref parser_callback_t for more information and examples */ - using parse_event_t = typename parser::parse_event_t; + using parse_event_t = detail::parse_event_t; /*! @brief per-element parser callback type @@ -1277,7 +1294,7 @@ class basic_json @since version 1.0.0 */ - using parser_callback_t = typename parser::parser_callback_t; + using parser_callback_t = detail::parser_callback_t; ////////////////// // constructors // @@ -1461,7 +1478,7 @@ class basic_json using other_string_t = typename BasicJsonType::string_t; using other_object_t = typename BasicJsonType::object_t; using other_array_t = typename BasicJsonType::array_t; - using other_binary_t = typename BasicJsonType::internal_binary_t; + using other_binary_t = typename BasicJsonType::binary_t; switch (val.type()) { @@ -1628,22 +1645,22 @@ class basic_json } /*! - @brief explicitly create a binary array from an already constructed copy of - its base type + @brief explicitly create a binary array (without subtype) - Creates a JSON binary array value from a given `binary_t`. Binary values are - part of various binary formats, such as CBOR, MsgPack, and BSON. And this - constructor is used to create a value for serialization to those formats. + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. @note Note, this function exists because of the difficulty in correctly specifying the correct template overload in the standard value ctor, as both JSON arrays and JSON binary arrays are backed with some form of a - `std::vector`. Because JSON binary arrays are a non-standard extension it + `std::vector`. Because JSON binary arrays are a non-standard extension it was decided that it would be best to prevent automatic initialization of a binary array type, for backwards compatibility and so it does not happen on accident. - @param[in] init `binary_t` with JSON values to create a binary array from + @param[in] init container containing bytes to use as binary type @return JSON binary array value @@ -1655,7 +1672,7 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(binary_t const& init) + static basic_json binary(const typename binary_t::container_type& init) { auto res = basic_json(); res.m_type = value_t::binary; @@ -1664,22 +1681,23 @@ class basic_json } /*! - @brief explicitly create a binary array from an already constructed rvalue - copy of its base type + @brief explicitly create a binary array (with subtype) - Creates a JSON binary array value from a given `binary_t`. Binary values are - part of various binary formats, such as CBOR, MsgPack, and BSON. And this - constructor is used to create a value for serialization to those formats. + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. @note Note, this function exists because of the difficulty in correctly specifying the correct template overload in the standard value ctor, as both JSON arrays and JSON binary arrays are backed with some form of a - `std::vector`. Because JSON binary arrays are a non-standard extension it + `std::vector`. Because JSON binary arrays are a non-standard extension it was decided that it would be best to prevent automatic initialization of a - binary array type, for backwards compatibility and so it doesn't happen on + binary array type, for backwards compatibility and so it does not happen on accident. - @param[in] init `binary_t` with JSON values to create a binary array from + @param[in] init container containing bytes to use as binary type + @param[in] subtype subtype to use in MessagePack and BSON @return JSON binary array value @@ -1691,7 +1709,17 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(binary_t&& init) + static basic_json binary(const typename binary_t::container_type& init, std::uint8_t subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); + return res; + } + + /// @copydoc binary(const typename binary_t::container_type&) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init) { auto res = basic_json(); res.m_type = value_t::binary; @@ -1699,6 +1727,16 @@ class basic_json return res; } + /// @copydoc binary(const typename binary_t::container_type&, std::uint8_t) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init, std::uint8_t subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(std::move(init), subtype); + return res; + } + /*! @brief explicitly create an array from an initializer list @@ -1956,8 +1994,7 @@ class basic_json case value_t::binary: { - m_value.binary = create(first.m_it.binary_iterator, - last.m_it.binary_iterator); + m_value = *first.m_object->m_value.binary; break; } @@ -2200,16 +2237,15 @@ class basic_json possible values: `strict` (throws and exception in case a decoding error occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD), and `ignore` (ignore invalid UTF-8 sequences during serialization). - @param[in] serialize_binary Whether or not to allow serialization of binary - types to JSON. Because binary types are non-standard, this will produce - non-conformant JSON, and is disabled by default. This flag is primarily - useful for debugging. Will output the binary value as a list of 8-bit - numbers prefixed by "b" (e.g. "bindata" = b[3, 0, 42, 255]). @return string containing the serialization of the JSON value @throw type_error.316 if a string stored inside the JSON value is not - UTF-8 encoded + UTF-8 encoded and @a error_handler is set to strict + + @note Binary values are serialized as object containing two keys: + - "bytes": an array of bytes as integers + - "subtype": the subtype as integer or "null" if the binary has no subtype @complexity Linear. @@ -2224,24 +2260,24 @@ class basic_json @since version 1.0.0; indentation character @a indent_char, option @a ensure_ascii and exceptions added in version 3.0.0; error - handlers added in version 3.4.0. + handlers added in version 3.4.0; serialization of binary values added + in version 3.8.0. */ string_t dump(const int indent = -1, const char indent_char = ' ', const bool ensure_ascii = false, - const error_handler_t error_handler = error_handler_t::strict, - const bool serialize_binary = false) const + const error_handler_t error_handler = error_handler_t::strict) const { string_t result; serializer s(detail::output_adapter(result), indent_char, error_handler); if (indent >= 0) { - s.dump(*this, true, ensure_ascii, static_cast(indent), 0, serialize_binary); + s.dump(*this, true, ensure_ascii, static_cast(indent)); } else { - s.dump(*this, false, ensure_ascii, 0, 0, serialize_binary); + s.dump(*this, false, ensure_ascii, 0); } return result; @@ -2757,18 +2793,6 @@ class basic_json return is_binary() ? m_value.binary : nullptr; } - /// get a pointer to the value (binary) - internal_binary_t* get_impl_ptr(internal_binary_t* /*unused*/) noexcept - { - return is_binary() ? m_value.binary : nullptr; - } - - /// get a pointer to the value (binary) - constexpr const internal_binary_t* get_impl_ptr(const internal_binary_t* /*unused*/) const noexcept - { - return is_binary() ? m_value.binary : nullptr; - } - /*! @brief helper function to implement get_ref() @@ -3194,6 +3218,36 @@ class basic_json return get(); } + /*! + @return reference to the binary value + + @throw type_error.302 if the value is not binary + + @sa @ref is_binary() to check if the value is binary + + @since version 3.8.0 + */ + binary_t& get_binary() + { + if (not is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + + /// @copydoc get_binary() + const binary_t& get_binary() const + { + if (not is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + /// @} @@ -3816,120 +3870,6 @@ class basic_json return value(ptr, string_t(default_value)); } - /*! - @brief return the binary subtype - - Returns the numerical subtype of the JSON value, if the JSON value is of - type "binary", and it has a subtype. If it does not have a subtype (or the - object is not of type binary) this function will return size_t(-1) as a - sentinel value. - - @return the numerical subtype of the binary JSON value - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - std::size_t get_subtype() const noexcept - { - if (is_binary() and m_value.binary->has_subtype) - { - return m_value.binary->subtype; - } - - return std::size_t(-1); - } - - /*! - @brief sets the binary subtype - - Sets the binary subtype of the JSON value, also flags a binary JSON value as - having a subtype, which has implications for serialization to msgpack (will - prefer ext file formats over bin). If the JSON value is not a binary value, - this function does nothing. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref get_subtype() -- return the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - - void set_subtype(std::uint8_t subtype) noexcept - { - if (is_binary()) - { - m_value.binary->has_subtype = true; - m_value.binary->subtype = subtype; - } - } - - /*! - @brief clears the binary subtype - - Clears the binary subtype of the JSON value, also flags a binary JSON value - as not having a subtype, which has implications for serialization to msgpack - (will prefer bin file formats over ext). If the JSON value is not a binary - value, this function does nothing. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref get_subtype() -- return the binary subtype - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - void clear_subtype() noexcept - { - if (is_binary()) - { - m_value.binary->has_subtype = false; - m_value.binary->subtype = 0; - } - } - - /*! - @brief return whether or not the binary subtype has a value - - Returns whether or not the binary subtype has a value. - - @return whether or not the binary subtype has a value. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref get_subtype() -- return the binary subtype - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - - @since version 3.8.0 - */ - bool has_subtype() const noexcept - { - return is_binary() and m_value.binary->has_subtype; - } - /*! @brief access the first element @@ -4099,7 +4039,7 @@ class basic_json } else if (is_binary()) { - AllocatorType alloc; + AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.binary); std::allocator_traits::deallocate(alloc, m_value.binary, 1); m_value.binary = nullptr; @@ -4213,7 +4153,7 @@ class basic_json } else if (is_binary()) { - AllocatorType alloc; + AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.binary); std::allocator_traits::deallocate(alloc, m_value.binary, 1); m_value.binary = nullptr; @@ -4889,6 +4829,11 @@ class basic_json element as string (see example). For primitive types (e.g., numbers), `key()` returns an empty string. + @warning Using `items()` on temporary objects is dangerous. Make sure the + object's lifetime exeeds the iteration. See + for more + information. + @return iteration proxy object wrapping @a ref with an interface to use in range-based for loops @@ -6015,6 +5960,53 @@ class basic_json } } + /*! + @brief exchanges the values + + Exchanges the contents of a JSON string 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 + invalidated. + + @param[in,out] other binary to exchange the contents with + + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` + + @complexity Constant. + + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__binary_t} + + @since version 3.8.0 + */ + void swap(binary_t& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /// @copydoc swap(binary_t) + void swap(typename binary_t::container_type& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + /// @} public: @@ -6277,7 +6269,7 @@ class basic_json return (lhs.m_value.number_float) < (rhs.m_value.number_float); case value_t::binary: - return (lhs.m_value.binary) < (rhs.m_value.binary); + return (*lhs.m_value.binary) < (*rhs.m_value.binary); default: return false; @@ -6616,21 +6608,39 @@ class basic_json @since version 2.0.3 (contiguous containers) */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(InputType&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) + { + basic_json result; + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions).parse(true, result); + return result; + } + + + JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json parse(detail::input_adapter&& i, + static basic_json parse(detail::span_input_adapter&& i, const parser_callback_t cb = nullptr, const bool allow_exceptions = true) { basic_json result; - parser(i, cb, allow_exceptions).parse(true, result); + parser(i.get(), cb, allow_exceptions).parse(true, result); return result; } - static bool accept(detail::input_adapter&& i) + template + static bool accept(InputType&& i) { - return parser(i).accept(true); + return parser(detail::input_adapter(std::forward(i))).accept(true); } + static bool accept(detail::span_input_adapter&& i) + { + return parser(i.get()).accept(true); + } /*! @brief generate SAX events @@ -6684,17 +6694,31 @@ class basic_json @since version 3.2.0 */ + template + 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) + { + auto ia = detail::input_adapter(std::forward(i)); + return format == input_format_t::json + ? parser(std::move(ia)).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + template JSON_HEDLEY_NON_NULL(2) - static bool sax_parse(detail::input_adapter&& i, SAX* sax, + static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, input_format_t format = input_format_t::json, const bool strict = true) { + auto ia = i.get(); return format == input_format_t::json - ? parser(std::move(i)).sax_parse(sax, strict) - : detail::binary_reader(std::move(i)).sax_parse(format, sax, strict); + ? parser(std::move(ia)).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } + /*! @brief deserialize from an iterator range with contiguous storage @@ -6935,7 +6959,8 @@ class basic_json number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B - number_float | *any value* | Double-Precision Float | 0xFB + number_float | *any value representable by a float* | Single-Precision Float | 0xFA + number_float | *any value NOT representable by a float* | Double-Precision Float | 0xFB string | *length*: 0..23 | UTF-8 string | 0x60..0x77 string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 @@ -6977,11 +7002,11 @@ class basic_json - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) - - half and single-precision floats (0xF9-0xFA) + - half-precision floats (0xF9) - break (0xFF) @param[in] j JSON value to serialize - @return MessagePack serialization as byte vector + @return CBOR serialization as byte vector @complexity Linear in the size of the JSON value @a j. @@ -6995,7 +7020,8 @@ class basic_json @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the related UBJSON format - @since version 2.0.9 + @since version 2.0.9; compact representation of floating-point numbers + since version 3.8.0 */ static std::vector to_cbor(const basic_json& j) { @@ -7406,14 +7432,16 @@ class basic_json @a strict parameter since 3.0.0; added @a allow_exceptions parameter since 3.2.0 */ + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_cbor(detail::input_adapter&& i, + static basic_json from_cbor(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::cbor, &sdp, strict); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -7421,7 +7449,7 @@ class basic_json @copydoc from_cbor(detail::input_adapter&&, const bool, const bool) */ template::value, int> = 0> + detail::enable_if_t::value, int> = 0> JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_cbor(A1 && a1, A2 && a2, const bool strict = true, @@ -7429,7 +7457,18 @@ class basic_json { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).sax_parse(input_format_t::cbor, &sdp, strict); + const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(a2)).get()).sax_parse(input_format_t::cbor, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(i.get()).sax_parse(input_format_t::cbor, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -7519,14 +7558,16 @@ class basic_json @a strict parameter since 3.0.0; added @a allow_exceptions parameter since 3.2.0 */ + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_msgpack(detail::input_adapter&& i, + static basic_json from_msgpack(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::msgpack, &sdp, strict); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -7534,7 +7575,7 @@ class basic_json @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool) */ template::value, int> = 0> + detail::enable_if_t::value, int> = 0> JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_msgpack(A1 && a1, A2 && a2, const bool strict = true, @@ -7542,10 +7583,23 @@ class basic_json { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).sax_parse(input_format_t::msgpack, &sdp, strict); + const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(a2)).get()).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(i.get()).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! @brief create a JSON value from an input in UBJSON format @@ -7607,14 +7661,16 @@ class basic_json @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0 */ + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_ubjson(detail::input_adapter&& i, + static basic_json from_ubjson(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::ubjson, &sdp, strict); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -7622,7 +7678,7 @@ class basic_json @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool) */ template::value, int> = 0> + detail::enable_if_t::value, int> = 0> JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_ubjson(A1 && a1, A2 && a2, const bool strict = true, @@ -7630,10 +7686,22 @@ class basic_json { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).sax_parse(input_format_t::ubjson, &sdp, strict); + const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(a2)).get()).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + + static basic_json from_ubjson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(i.get()).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! @brief Create a JSON value from an input in BSON format @@ -7694,14 +7762,16 @@ class basic_json @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the related UBJSON format */ + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_bson(detail::input_adapter&& i, + static basic_json from_bson(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::bson, &sdp, strict); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -7709,7 +7779,7 @@ class basic_json @copydoc from_bson(detail::input_adapter&&, const bool, const bool) */ template::value, int> = 0> + detail::enable_if_t::value, int> = 0> JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_bson(A1 && a1, A2 && a2, const bool strict = true, @@ -7717,12 +7787,20 @@ class basic_json { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).sax_parse(input_format_t::bson, &sdp, strict); + const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(a2)).get()).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } - - + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(i.get()).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } /// @} ////////////////////////// diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f04091d02a..bf10cae2f7 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -36,7 +36,6 @@ SOFTWARE. #include // all_of, find, for_each #include // assert -#include // and, not, or #include // nullptr_t, ptrdiff_t, size_t #include // hash, less #include // initializer_list @@ -58,7 +57,6 @@ SOFTWARE. #include // transform #include // array -#include // and, not #include // forward_list #include // inserter, front_inserter, end #include // map @@ -69,6 +67,16 @@ SOFTWARE. #include // pair, declval #include // valarray +// #include + + +// Header is removed in C++20. +// See for more information. + +#if __cplusplus <= 201703L + #include // and, not, or +#endif + // #include @@ -2495,10 +2503,12 @@ class other_error : public exception // #include -#include // not #include // size_t #include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +// #include + + namespace nlohmann { namespace detail @@ -2560,11 +2570,12 @@ constexpr T static_const::value; // #include -#include // not #include // numeric_limits #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval +// #include + // #include @@ -3145,11 +3156,13 @@ struct is_constructible_tuple> : conjunction // array -#include // and #include // size_t #include // uint8_t #include // string +// #include + + namespace nlohmann { namespace detail @@ -3435,9 +3448,9 @@ template ::value and not is_constructible_object_type::value and not is_constructible_string_type::value and + not std::is_same::value and not is_basic_json::value, int > = 0 > - auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) -> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), j.template get(), @@ -3452,6 +3465,17 @@ void()) from_json_array_impl(j, arr, priority_tag<3> {}); } +template +void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_binary())) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); + } + + bin = *j.template get_ptr(); +} + template::value, int> = 0> void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) @@ -3599,7 +3623,6 @@ constexpr const auto& from_json = detail::static_const::va #include // copy -#include // or, and, not #include // begin, end #include // string #include // tuple, get @@ -3608,6 +3631,8 @@ constexpr const auto& from_json = detail::static_const::va #include // valarray #include // vector +// #include + // #include @@ -3854,7 +3879,7 @@ struct external_constructor static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) { j.m_type = value_t::binary; - typename BasicJsonType::internal_binary_t value{b}; + typename BasicJsonType::binary_t value{b}; j.m_value = value; j.assert_invariant(); } @@ -3863,7 +3888,7 @@ struct external_constructor static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) { j.m_type = value_t::binary; - typename BasicJsonType::internal_binary_t value{std::move(b)}; + typename BasicJsonType::binary_t value{std::move(b)}; j.m_value = value; j.assert_invariant(); } @@ -4058,9 +4083,9 @@ void to_json(BasicJsonType& j, const std::vector& e) template ::value and - not is_compatible_object_type< - BasicJsonType, CompatibleArrayType>::value and + not is_compatible_object_type::value and not is_compatible_string_type::value and + not std::is_same::value and not is_basic_json::value, int> = 0> void to_json(BasicJsonType& j, const CompatibleArrayType& arr) @@ -4068,6 +4093,12 @@ void to_json(BasicJsonType& j, const CompatibleArrayType& arr) external_constructor::construct(j, arr); } +template +void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) +{ + external_constructor::construct(j, bin); +} + template::value, int> = 0> void to_json(BasicJsonType& j, const std::valarray& arr) @@ -4192,6 +4223,176 @@ struct adl_serializer } // namespace nlohmann +// #include + + +#include // uint8_t +#include // tie +#include // move + +namespace nlohmann +{ + +/*! +@brief an internal type for a backed binary type + +This type extends the template parameter @a BinaryType provided to `basic_json` +with a subtype used by BSON and MessagePack. This type exists so that the user +does not have to specify a type themselves with a specific naming scheme in +order to override the binary type. + +@tparam BinaryType container to store bytes (`std::vector` by + default) + +@since version 3.8.0 +*/ +template +class byte_container_with_subtype : public BinaryType +{ + public: + /// the type of the underlying container + using container_type = BinaryType; + + byte_container_with_subtype() noexcept(noexcept(container_type())) + : container_type() + {} + + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) + {} + + byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + {} + + byte_container_with_subtype(const container_type& b, std::uint8_t subtype) noexcept(noexcept(container_type(b))) + : container_type(b) + , m_subtype(subtype) + , m_has_subtype(true) + {} + + byte_container_with_subtype(container_type&& b, std::uint8_t subtype) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + , m_subtype(subtype) + , m_has_subtype(true) + {} + + bool operator==(const byte_container_with_subtype& rhs) const + { + return std::tie(static_cast(*this), m_subtype, m_has_subtype) == + std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); + } + + bool operator!=(const byte_container_with_subtype& rhs) const + { + return !(rhs == *this); + } + + /*! + @brief sets the binary subtype + + Sets the binary subtype of the value, also flags a binary JSON value as + having a subtype, which has implications for serialization. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void set_subtype(std::uint8_t subtype) noexcept + { + m_subtype = subtype; + m_has_subtype = true; + } + + /*! + @brief return the binary subtype + + Returns the numerical subtype of the value if it has a subtype. If it does + not have a subtype, this function will return size_t(-1) as a sentinel + value. + + @return the numerical subtype of the binary value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + constexpr std::uint8_t subtype() const noexcept + { + return m_subtype; + } + + /*! + @brief return whether the value has a subtype + + @return whether the value has a subtype + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref clear_subtype() -- clears the binary subtype + + @since version 3.8.0 + */ + constexpr bool has_subtype() const noexcept + { + return m_has_subtype; + } + + /*! + @brief clears the binary subtype + + Clears the binary subtype and flags the value as not having a subtype, which + has implications for serialization; for instance MessagePack will prefer the + bin family over the ext family. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa @ref subtype() -- return the binary subtype + @sa @ref set_subtype() -- sets the binary subtype + @sa @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void clear_subtype() noexcept + { + m_subtype = 0; + m_has_subtype = false; + } + + private: + std::uint8_t m_subtype = 0; + bool m_has_subtype = false; +}; + +} // namespace nlohmann + +// #include + // #include // #include @@ -4248,32 +4449,11 @@ enum class input_format_t { json, cbor, msgpack, ubjson, bson }; // input adapters // //////////////////// -/*! -@brief abstract input adapter interface - -Produces a stream of std::char_traits::int_type characters from a -std::istream, a buffer, or some other input type. Accepts the return of -exactly one non-EOF character for future input. The int_type characters -returned consist of all valid char values as positive values (typically -unsigned char), plus an EOF value outside that range, specified by the value -of the function std::char_traits::eof(). This value is typically -1, but -could be any arbitrary value which is not a valid char value. -*/ -struct input_adapter_protocol -{ - /// get a character [0,255] or std::char_traits::eof(). - virtual std::char_traits::int_type get_character() = 0; - virtual ~input_adapter_protocol() = default; -}; - -/// a type to simplify interfaces -using input_adapter_t = std::shared_ptr; - /*! Input adapter for stdio file access. This adapter read only 1 byte and do not use any buffer. This adapter is a very low level adapter. */ -class file_input_adapter : public input_adapter_protocol +class file_input_adapter { public: JSON_HEDLEY_NON_NULL(2) @@ -4285,10 +4465,9 @@ class file_input_adapter : public input_adapter_protocol file_input_adapter(const file_input_adapter&) = delete; file_input_adapter(file_input_adapter&&) = default; file_input_adapter& operator=(const file_input_adapter&) = delete; - file_input_adapter& operator=(file_input_adapter&&) = default; - ~file_input_adapter() override = default; + file_input_adapter& operator=(file_input_adapter&&) = delete; - std::char_traits::int_type get_character() noexcept override + std::char_traits::int_type get_character() noexcept { return std::fgetc(m_file); } @@ -4308,48 +4487,56 @@ characters following those used in parsing the JSON input. Clears the std::istream flags; any input errors (e.g., EOF) will be detected by the first subsequent call for input from the std::istream. */ -class input_stream_adapter : public input_adapter_protocol +class input_stream_adapter { public: - ~input_stream_adapter() override + ~input_stream_adapter() { // clear stream flags; we use underlying streambuf I/O, do not // maintain ifstream flags, except eof - is.clear(is.rdstate() & std::ios::eofbit); + if (is) + { + is->clear(is->rdstate() & std::ios::eofbit); + } } explicit input_stream_adapter(std::istream& i) - : is(i), sb(*i.rdbuf()) + : is(&i), sb(i.rdbuf()) {} // delete because of pointer members input_stream_adapter(const input_stream_adapter&) = delete; input_stream_adapter& operator=(input_stream_adapter&) = delete; - input_stream_adapter(input_stream_adapter&&) = delete; - input_stream_adapter& operator=(input_stream_adapter&&) = delete; + input_stream_adapter& operator=(input_stream_adapter&& rhs) = delete; + + input_stream_adapter(input_stream_adapter&& rhs) : is(rhs.is), sb(rhs.sb) + { + rhs.is = nullptr; + rhs.sb = nullptr; + } // std::istream/std::streambuf use std::char_traits::to_int_type, to // ensure that std::char_traits::eof() and the character 0xFF do not // end up as the same value, eg. 0xFFFFFFFF. - std::char_traits::int_type get_character() override + std::char_traits::int_type get_character() { - auto res = sb.sbumpc(); + auto res = sb->sbumpc(); // set eof manually, as we don't use the istream interface. if (res == EOF) { - is.clear(is.rdstate() | std::ios::eofbit); + is->clear(is->rdstate() | std::ios::eofbit); } return res; } private: /// the associated input stream - std::istream& is; - std::streambuf& sb; + std::istream* is = nullptr; + std::streambuf* sb = nullptr; }; /// input adapter for buffer input -class input_buffer_adapter : public input_adapter_protocol +class input_buffer_adapter { public: input_buffer_adapter(const char* b, const std::size_t l) noexcept @@ -4359,11 +4546,10 @@ class input_buffer_adapter : public input_adapter_protocol // delete because of pointer members input_buffer_adapter(const input_buffer_adapter&) = delete; input_buffer_adapter& operator=(input_buffer_adapter&) = delete; - input_buffer_adapter(input_buffer_adapter&&) = delete; + input_buffer_adapter(input_buffer_adapter&&) = default; input_buffer_adapter& operator=(input_buffer_adapter&&) = delete; - ~input_buffer_adapter() override = default; - std::char_traits::int_type get_character() noexcept override + std::char_traits::int_type get_character() noexcept { if (JSON_HEDLEY_LIKELY(cursor < limit)) { @@ -4506,14 +4692,14 @@ struct wide_string_input_helper }; template -class wide_string_input_adapter : public input_adapter_protocol +class wide_string_input_adapter { public: explicit wide_string_input_adapter(const WideStringType& w) noexcept : str(w) {} - std::char_traits::int_type get_character() noexcept override + std::char_traits::int_type get_character() noexcept { // check if buffer needs to be filled if (utf8_bytes_index == utf8_bytes_filled) @@ -4552,112 +4738,164 @@ class wide_string_input_adapter : public input_adapter_protocol std::size_t utf8_bytes_filled = 0; }; -class input_adapter +inline file_input_adapter input_adapter(std::FILE* file) { - public: - // native support - JSON_HEDLEY_NON_NULL(2) - input_adapter(std::FILE* file) - : ia(std::make_shared(file)) {} - /// input adapter for input stream - input_adapter(std::istream& i) - : ia(std::make_shared(i)) {} + return file_input_adapter(file); +} - /// input adapter for input stream - input_adapter(std::istream&& i) - : ia(std::make_shared(i)) {} +inline input_stream_adapter input_adapter(std::istream& stream) +{ + return input_stream_adapter(stream); +} - input_adapter(const std::wstring& ws) - : ia(std::make_shared>(ws)) {} +inline input_stream_adapter input_adapter(std::istream&& stream) +{ + return input_stream_adapter(stream); +} - input_adapter(const std::u16string& ws) - : ia(std::make_shared>(ws)) {} +template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> +input_buffer_adapter input_adapter(CharT b, std::size_t l) +{ + return input_buffer_adapter(reinterpret_cast(b), l); +} - input_adapter(const std::u32string& ws) - : ia(std::make_shared>(ws)) {} +template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> +input_buffer_adapter input_adapter(CharT b) +{ + return input_adapter(reinterpret_cast(b), + std::strlen(reinterpret_cast(b))); +} - /// input adapter for buffer +template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> +input_buffer_adapter input_adapter(IteratorType first, IteratorType last) +{ +#ifndef NDEBUG + // assertion to check that the iterator range is indeed contiguous, + // see https://stackoverflow.com/a/35008842/266378 for more discussion + const auto is_contiguous = std::accumulate( + first, last, std::pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first; + assert(is_contiguous); +#endif + + // assertion to check that each element is 1 byte long + static_assert( + sizeof(typename iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + const auto len = static_cast(std::distance(first, last)); + if (JSON_HEDLEY_LIKELY(len > 0)) + { + // there is at least one element: use the address of first + return input_buffer_adapter(reinterpret_cast(&(*first)), len); + } + else + { + // the address of first cannot be used: use nullptr + return input_buffer_adapter(nullptr, len); + } +} + +inline wide_string_input_adapter input_adapter(const std::wstring& ws) +{ + return wide_string_input_adapter(ws); +} + + +inline wide_string_input_adapter input_adapter(const std::u16string& ws) +{ + return wide_string_input_adapter(ws); +} + +inline wide_string_input_adapter input_adapter(const std::u32string& ws) +{ + return wide_string_input_adapter(ws); +} + +template::value and + std::is_base_of()))>::iterator_category>::value, + int>::type = 0> +input_buffer_adapter input_adapter(const ContiguousContainer& c) +{ + return input_adapter(std::begin(c), std::end(c)); +} + + +template +input_buffer_adapter input_adapter(T (&array)[N]) +{ + return input_adapter(std::begin(array), std::end(array)); +} + +// This class only handles inputs of input_buffer_adapter type. +// It's required so that expressions like {ptr, len} can be implicitely casted +// to the correct adapter. +class span_input_adapter +{ + public: template::value and std::is_integral::type>::value and sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - input_adapter(CharT b, std::size_t l) - : ia(std::make_shared(reinterpret_cast(b), l)) {} - - // derived support + span_input_adapter(CharT b, std::size_t l) + : ia(reinterpret_cast(b), l) {} - /// input adapter for string literal template::value and std::is_integral::type>::value and sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - input_adapter(CharT b) - : input_adapter(reinterpret_cast(b), - std::strlen(reinterpret_cast(b))) {} + span_input_adapter(CharT b) + : span_input_adapter(reinterpret_cast(b), + std::strlen(reinterpret_cast(b))) {} - /// input adapter for iterator range with contiguous storage template::iterator_category, std::random_access_iterator_tag>::value, int>::type = 0> - input_adapter(IteratorType first, IteratorType last) - { -#ifndef NDEBUG - // assertion to check that the iterator range is indeed contiguous, - // see https://stackoverflow.com/a/35008842/266378 for more discussion - const auto is_contiguous = std::accumulate( - first, last, std::pair(true, 0), - [&first](std::pair res, decltype(*first) val) - { - res.first &= (val == *(std::next(std::addressof(*first), res.second++))); - return res; - }).first; - assert(is_contiguous); -#endif - - // assertion to check that each element is 1 byte long - static_assert( - sizeof(typename iterator_traits::value_type) == 1, - "each element in the iterator range must have the size of 1 byte"); - - const auto len = static_cast(std::distance(first, last)); - if (JSON_HEDLEY_LIKELY(len > 0)) - { - // there is at least one element: use the address of first - ia = std::make_shared(reinterpret_cast(&(*first)), len); - } - else - { - // the address of first cannot be used: use nullptr - ia = std::make_shared(nullptr, len); - } - } + span_input_adapter(IteratorType first, IteratorType last) + : ia(input_adapter(first, last)) {} - /// input adapter for array template - input_adapter(T (&array)[N]) - : input_adapter(std::begin(array), std::end(array)) {} + span_input_adapter(T (&array)[N]) + : span_input_adapter(std::begin(array), std::end(array)) {} /// input adapter for contiguous container template::value and std::is_base_of()))>::iterator_category>::value, int>::type = 0> - input_adapter(const ContiguousContainer& c) - : input_adapter(std::begin(c), std::end(c)) {} + span_input_adapter(const ContiguousContainer& c) + : span_input_adapter(std::begin(c), std::end(c)) {} - operator input_adapter_t() + input_buffer_adapter&& get() { - return ia; + return std::move(ia); } private: - /// the actual adapter - input_adapter_t ia = nullptr; + input_buffer_adapter ia; }; } // namespace detail } // namespace nlohmann @@ -4690,13 +4928,9 @@ input. template struct json_sax { - /// type for (signed) integers using number_integer_t = typename BasicJsonType::number_integer_t; - /// type for unsigned integers using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - /// type for floating-point numbers using number_float_t = typename BasicJsonType::number_float_t; - /// type for strings using string_t = typename BasicJsonType::string_t; using binary_t = typename BasicJsonType::binary_t; @@ -4881,7 +5115,7 @@ class json_sax_dom_parser bool binary(binary_t& val) { - handle_binary(val); + handle_value(std::move(val)); return true; } @@ -4994,36 +5228,6 @@ class json_sax_dom_parser return object_element; } - /*! - @invariant If the ref stack is empty, then the passed value will be the new - root. - @invariant If the ref stack contains a value, then it is an array or an - object to which we can add elements - */ - template - JSON_HEDLEY_RETURNS_NON_NULL - BasicJsonType* handle_binary(BinaryValue&& v) - { - if (ref_stack.empty()) - { - root = BasicJsonType::binary_array(std::forward(v)); - return &root; - } - - assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); - - if (ref_stack.back()->is_array()) - { - ref_stack.back()->m_value.array->emplace_back(BasicJsonType::binary_array(std::forward(v))); - return &(ref_stack.back()->m_value.array->back()); - } - - assert(ref_stack.back()->is_object()); - assert(object_element); - *object_element = BasicJsonType::binary_array(std::forward(v)); - return object_element; - } - /// the parsed JSON value BasicJsonType& root; /// stack to model hierarchy of values @@ -5101,7 +5305,7 @@ class json_sax_dom_callback_parser bool binary(binary_t& val) { - handle_value(val); + handle_value(std::move(val)); return true; } @@ -5581,6 +5785,20 @@ namespace nlohmann { namespace detail { + +/*! +@brief determine system byte order + +@return true if and only if system's byte order is little endian + +@note from https://stackoverflow.com/a/1001328/266378 +*/ +static inline bool little_endianess(int num = 1) noexcept +{ + return *reinterpret_cast(&num) == 1; +} + + /////////////////// // binary reader // /////////////////// @@ -5588,14 +5806,14 @@ namespace detail /*! @brief deserialization of CBOR, MessagePack, and UBJSON values */ -template> +template> class binary_reader { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; using json_sax_t = SAX; public: @@ -5604,10 +5822,9 @@ class binary_reader @param[in] adapter input adapter to read from */ - explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter)) + explicit binary_reader(InputAdapterType&& adapter) : ia(std::move(adapter)) { (void)detail::is_sax_static_asserts {}; - assert(ia); } // make class move-only @@ -5676,18 +5893,6 @@ class binary_reader return result; } - /*! - @brief determine system byte order - - @return true if and only if system's byte order is little endian - - @note from https://stackoverflow.com/a/1001328/266378 - */ - static constexpr bool little_endianess(int num = 1) noexcept - { - return *reinterpret_cast(&num) == 1; - } - private: ////////// // BSON // @@ -5775,7 +5980,7 @@ class binary_reader @return `true` if the byte array was successfully parsed */ template - bool get_bson_binary(const NumberType len, internal_binary_t& result) + bool get_bson_binary(const NumberType len, binary_t& result) { if (JSON_HEDLEY_UNLIKELY(len < 0)) { @@ -5783,8 +5988,10 @@ class binary_reader return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"))); } - result.has_subtype = true; // All BSON binary values have a subtype - get_number(input_format_t::bson, result.subtype); + // All BSON binary values have a subtype + std::uint8_t subtype; + get_number(input_format_t::bson, subtype); + result.set_subtype(subtype); return get_binary(input_format_t::bson, len, result); } @@ -5830,7 +6037,7 @@ class binary_reader case 0x05: // binary { std::int32_t len; - internal_binary_t value; + binary_t value; return get_number(input_format_t::bson, len) and get_bson_binary(len, value) and sax->binary(value); } @@ -6086,7 +6293,7 @@ class binary_reader case 0x5B: // Binary data (eight-byte uint64_t for n follow) case 0x5F: // Binary data (indefinite length) { - internal_binary_t b; + binary_t b; return get_cbor_binary(b) and sax->binary(b); } @@ -6416,7 +6623,7 @@ class binary_reader @return whether byte array creation completed */ - bool get_cbor_binary(internal_binary_t& result) + bool get_cbor_binary(binary_t& result) { if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "binary"))) { @@ -6457,32 +6664,36 @@ class binary_reader case 0x58: // Binary data (one-byte uint8_t for n follows) { std::uint8_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x59: // Binary data (two-byte uint16_t for n follow) { std::uint16_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x5A: // Binary data (four-byte uint32_t for n follow) { std::uint32_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x5B: // Binary data (eight-byte uint64_t for n follow) { std::uint64_t len; - return get_number(input_format_t::cbor, len) and get_binary(input_format_t::cbor, len, result); + return get_number(input_format_t::cbor, len) and + get_binary(input_format_t::cbor, len, result); } case 0x5F: // Binary data (indefinite length) { while (get() != 0xFF) { - internal_binary_t chunk; + binary_t chunk; if (not get_cbor_binary(chunk)) { return false; @@ -6832,7 +7043,7 @@ class binary_reader case 0xD7: // fixext 8 case 0xD8: // fixext 16 { - internal_binary_t b; + binary_t b; return get_msgpack_binary(b) and sax->binary(b); } @@ -7055,95 +7266,110 @@ class binary_reader @return whether byte array creation completed */ - bool get_msgpack_binary(internal_binary_t& result) + bool get_msgpack_binary(binary_t& result) { - if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::msgpack, "binary"))) + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) { - return false; - } + result.set_subtype(static_cast(subtype)); + return true; + }; switch (current) { case 0xC4: // bin 8 { std::uint8_t len; - return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); } case 0xC5: // bin 16 { std::uint16_t len; - return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); } case 0xC6: // bin 32 { std::uint32_t len; - return get_number(input_format_t::msgpack, len) and get_binary(input_format_t::msgpack, len, result); + return get_number(input_format_t::msgpack, len) and + get_binary(input_format_t::msgpack, len, result); } case 0xC7: // ext 8 { std::uint8_t len; - result.has_subtype = true; + std::int8_t subtype; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.subtype) and - get_binary(input_format_t::msgpack, len, result); + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); } case 0xC8: // ext 16 { std::uint16_t len; - result.has_subtype = true; + std::int8_t subtype; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.subtype) and - get_binary(input_format_t::msgpack, len, result); + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); } case 0xC9: // ext 32 { std::uint32_t len; - result.has_subtype = true; + std::int8_t subtype; return get_number(input_format_t::msgpack, len) and - get_number(input_format_t::msgpack, result.subtype) and - get_binary(input_format_t::msgpack, len, result); + get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, len, result) and + assign_and_return_true(subtype); } case 0xD4: // fixext 1 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 1, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 1, result) and + assign_and_return_true(subtype); } case 0xD5: // fixext 2 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 2, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 2, result) and + assign_and_return_true(subtype); } case 0xD6: // fixext 4 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 4, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 4, result) and + assign_and_return_true(subtype); } case 0xD7: // fixext 8 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 8, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 8, result) and + assign_and_return_true(subtype); } case 0xD8: // fixext 16 { - result.has_subtype = true; - return get_number(input_format_t::msgpack, result.subtype) and get_binary(input_format_t::msgpack, 16, result); + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) and + get_binary(input_format_t::msgpack, 16, result) and + assign_and_return_true(subtype); } - default: - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected binary type specification (0xC4-0xC9, 0xD4-0xD8); last byte: 0x" + last_token, "binary"))); - } + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE } } @@ -7650,7 +7876,7 @@ class binary_reader int get() { ++chars_read; - return current = ia->get_character(); + return current = ia.get_character(); } /*! @@ -7758,7 +7984,7 @@ class binary_reader template bool get_binary(const input_format_t format, const NumberType len, - internal_binary_t& result) + binary_t& result) { bool success = true; std::generate_n(std::back_inserter(result), len, [this, &success, &format]() @@ -7768,7 +7994,7 @@ class binary_reader { success = false; } - return static_cast(current); + return static_cast(current); }); return success; } @@ -7838,7 +8064,7 @@ class binary_reader private: /// input adapter - input_adapter_t ia = nullptr; + InputAdapterType ia; /// the current character int current = std::char_traits::eof(); @@ -7885,19 +8111,9 @@ namespace detail // lexer // /////////// -/*! -@brief lexical analysis - -This class organizes the lexical analysis during JSON deserialization. -*/ template -class lexer +class lexer_base { - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - public: /// token types for the parser enum class token_type @@ -7938,9 +8154,9 @@ class lexer return "null literal"; case token_type::value_string: return "string literal"; - case lexer::token_type::value_unsigned: - case lexer::token_type::value_integer: - case lexer::token_type::value_float: + case token_type::value_unsigned: + case token_type::value_integer: + case token_type::value_float: return "number literal"; case token_type::begin_array: return "'['"; @@ -7966,15 +8182,31 @@ class lexer // LCOV_EXCL_STOP } } +}; +/*! +@brief lexical analysis - explicit lexer(detail::input_adapter_t&& adapter) +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer : public lexer_base +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + public: + using token_type = typename lexer_base::token_type; + + explicit lexer(InputAdapterType&& adapter) : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {} // delete because of pointer members lexer(const lexer&) = delete; - lexer(lexer&&) = delete; + lexer(lexer&&) = default; lexer& operator=(lexer&) = delete; - lexer& operator=(lexer&&) = delete; + lexer& operator=(lexer&&) = default; ~lexer() = default; private: @@ -9119,7 +9351,7 @@ class lexer } else { - current = ia->get_character(); + current = ia.get_character(); } if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) @@ -9343,7 +9575,7 @@ class lexer private: /// input adapter - detail::input_adapter_t ia = nullptr; + InputAdapterType ia; /// the current character std::char_traits::int_type current = std::char_traits::eof(); @@ -9408,44 +9640,45 @@ namespace detail // parser // //////////// +enum class parse_event_t : uint8_t +{ + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value +}; + +template +using parser_callback_t = + std::function; + /*! @brief syntax analysis This class implements a recursive descent parser. */ -template +template class parser { using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; using number_float_t = typename BasicJsonType::number_float_t; using string_t = typename BasicJsonType::string_t; - using lexer_t = lexer; + using lexer_t = lexer; using token_type = typename lexer_t::token_type; public: - enum class parse_event_t : uint8_t - { - /// the parser read `{` and started to process a JSON object - object_start, - /// the parser read `}` and finished processing a JSON object - object_end, - /// the parser read `[` and started to process a JSON array - array_start, - /// the parser read `]` and finished processing a JSON array - array_end, - /// the parser read a key of a value in an object - key, - /// the parser finished reading a JSON value - value - }; - - using parser_callback_t = - std::function; - /// a parser reading from an input adapter - explicit parser(detail::input_adapter_t&& adapter, - const parser_callback_t cb = nullptr, + 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_) { @@ -9870,7 +10103,7 @@ class parser private: /// callback function - const parser_callback_t callback = nullptr; + const parser_callback_t callback = nullptr; /// the type of the last read token token_type last_token = token_type::uninitialized; /// the lexer @@ -10024,7 +10257,7 @@ template struct internal_iterator /// iterator for JSON arrays typename BasicJsonType::array_t::iterator array_iterator {}; /// iterator for JSON binary arrays - typename BasicJsonType::binary_t::iterator binary_iterator {}; + typename BasicJsonType::binary_t::container_type::iterator binary_iterator {}; /// generic iterator for all other types primitive_iterator_t primitive_iterator {}; }; @@ -10034,10 +10267,11 @@ template struct internal_iterator // #include -#include // not #include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next #include // conditional, is_const, remove_const +// #include + // #include // #include @@ -11866,6 +12100,7 @@ class json_ref #include // memcpy #include // numeric_limits #include // string +#include // isnan, isinf // #include @@ -12013,7 +12248,7 @@ template class binary_writer { using string_t = typename BasicJsonType::string_t; - using internal_binary_t = typename BasicJsonType::internal_binary_t; + using binary_t = typename BasicJsonType::binary_t; public: /*! @@ -12164,8 +12399,35 @@ class binary_writer case value_t::number_float: { - oa->write_character(get_cbor_float_prefix(j.m_value.number_float)); - write_number(j.m_value.number_float); + if (std::isnan(j.m_value.number_float)) + { + // NaN is 0xf97e00 in CBOR + oa->write_character(to_char_type(0xF9)); + oa->write_character(to_char_type(0x7E)); + oa->write_character(to_char_type(0x00)); + } + else if (std::isinf(j.m_value.number_float)) + { + // Infinity is 0xf97c00, -Infinity is 0xf9fc00 + oa->write_character(to_char_type(0xf9)); + oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); + oa->write_character(to_char_type(0x00)); + } + 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); + } + } break; } @@ -12537,7 +12799,7 @@ class binary_writer { // step 0: determine if the binary type has a set subtype to // determine whether or not to use the ext or fixext types - const bool use_ext = j.m_value.binary->has_subtype; + const bool use_ext = j.m_value.binary->has_subtype(); // step 1: write control byte and the byte string length const auto N = j.m_value.binary->size(); @@ -12617,7 +12879,7 @@ class binary_writer // step 1.5: if this is an ext type, write the subtype if (use_ext) { - write_number(j.m_value.binary->subtype); + write_number(static_cast(j.m_value.binary->subtype())); } // step 2: write the byte string @@ -13039,7 +13301,7 @@ class binary_writer /*! @return The size of the BSON-encoded binary array @a value */ - static std::size_t calc_bson_binary_size(const typename BasicJsonType::internal_binary_t& value) + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) { return sizeof(std::int32_t) + value.size() + 1ul; } @@ -13067,21 +13329,14 @@ class binary_writer @brief Writes a BSON element with key @a name and binary value @a value */ void write_bson_binary(const string_t& name, - const internal_binary_t& value) + const binary_t& value) { write_bson_entry_header(name, 0x05); write_number(static_cast(value.size())); - std::uint8_t subtype = 0x00; // Generic Binary Subtype - if (value.has_subtype) - { - subtype = value.subtype; - } - write_number(subtype); + write_number(value.has_subtype() ? value.subtype() : std::uint8_t(0x00)); - oa->write_characters( - reinterpret_cast(value.data()), - value.size()); + oa->write_characters(reinterpret_cast(value.data()), value.size()); } /*! @@ -13527,7 +13782,7 @@ class binary_writer private: /// whether we can assume little endianess - const bool is_little_endian = binary_reader::little_endianess(); + const bool is_little_endian = little_endianess(); /// the output output_adapter_t oa = nullptr; @@ -13543,7 +13798,6 @@ class binary_writer #include // reverse, remove, fill, find, none_of #include // array #include // assert -#include // and, or #include // localeconv, lconv #include // labs, isfinite, isnan, signbit #include // size_t, ptrdiff_t @@ -13554,17 +13808,21 @@ class binary_writer #include // is_same #include // move +// #include + // #include #include // array #include // assert -#include // or, and, not #include // signbit, isfinite #include // intN_t, uintN_t #include // memcpy, memmove #include // numeric_limits #include // conditional + +// #include + // #include @@ -14699,7 +14957,7 @@ class serializer using number_float_t = typename BasicJsonType::number_float_t; using number_integer_t = typename BasicJsonType::number_integer_t; using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using binary_t = typename BasicJsonType::binary_t; + using binary_char_t = typename BasicJsonType::binary_t::value_type; static constexpr std::uint8_t UTF8_ACCEPT = 0; static constexpr std::uint8_t UTF8_REJECT = 1; @@ -14738,19 +14996,22 @@ class serializer - strings and object keys are escaped using `escape_string()` - integer numbers are converted implicitly via `operator<<` - floating-point numbers are converted to a string using `"%g"` format - - if specified to, binary values are output using the syntax `b[]`, otherwise an exception is thrown + - binary values are serialized as objects containing the subtype and the + byte array @param[in] val value to serialize @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. @param[in] indent_step the indent level @param[in] current_indent the current indent level (only used internally) - @param[in] serialize_binary whether the output shall include non-standard binary output */ - void dump(const BasicJsonType& val, const bool pretty_print, + void dump(const BasicJsonType& val, + const bool pretty_print, const bool ensure_ascii, const unsigned int indent_step, - const unsigned int current_indent = 0, - const bool serialize_binary = false) + const unsigned int current_indent = 0) { switch (val.m_type) { @@ -14781,7 +15042,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\": ", 3); - dump(i->second, true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(i->second, true, ensure_ascii, indent_step, new_indent); o->write_characters(",\n", 2); } @@ -14792,7 +15053,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\": ", 3); - dump(i->second, true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(i->second, true, ensure_ascii, indent_step, new_indent); o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); @@ -14809,7 +15070,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); - dump(i->second, false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(i->second, false, ensure_ascii, indent_step, current_indent); o->write_character(','); } @@ -14819,7 +15080,7 @@ class serializer o->write_character('\"'); dump_escaped(i->first, ensure_ascii); o->write_characters("\":", 2); - dump(i->second, false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(i->second, false, ensure_ascii, indent_step, current_indent); o->write_character('}'); } @@ -14851,14 +15112,14 @@ class serializer i != val.m_value.array->cend() - 1; ++i) { o->write_characters(indent_string.c_str(), new_indent); - dump(*i, true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(*i, true, ensure_ascii, indent_step, new_indent); o->write_characters(",\n", 2); } // last element assert(not val.m_value.array->empty()); o->write_characters(indent_string.c_str(), new_indent); - dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent, serialize_binary); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); o->write_character('\n'); o->write_characters(indent_string.c_str(), current_indent); @@ -14872,13 +15133,13 @@ class serializer for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) { - dump(*i, false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(*i, false, ensure_ascii, indent_step, current_indent); o->write_character(','); } // last element assert(not val.m_value.array->empty()); - dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent, serialize_binary); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); o->write_character(']'); } @@ -14896,48 +15157,73 @@ class serializer case value_t::binary: { - if (not serialize_binary) + if (pretty_print) { - JSON_THROW(type_error::create(317, "cannot serialize binary data to text JSON")); - } + o->write_characters("{\n", 2); - if (val.m_value.binary->empty()) - { - o->write_characters("b[]", 3); - } - else if (pretty_print) - { - o->write_characters("b[", 2); - for (auto i = val.m_value.binary->cbegin(); - i != val.m_value.binary->cend() - 1; ++i) + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { - dump_integer(*i); - o->write_character(','); - if (std::distance(val.m_value.binary->cbegin(), i) % 16 == 0) - { - o->write_character('\n'); - } - else + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (not val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) { - o->write_character(' '); + dump_integer(*i); + o->write_characters(", ", 2); } + dump_integer(val.m_value.binary->back()); } - dump_integer(val.m_value.binary->back()); - o->write_character(']'); + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); } else { - o->write_characters("b[", 2); - for (auto i = val.m_value.binary->cbegin(); - i != val.m_value.binary->cend() - 1; ++i) + o->write_characters("{\"bytes\":[", 10); + + if (not val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\"subtype\":", 12); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + o->write_character('}'); + } + else { - dump_integer(*i); - o->write_character(','); + o->write_characters("null}", 5); } - - dump_integer(val.m_value.binary->back()); - o->write_character(']'); } return; } @@ -15299,7 +15585,7 @@ class serializer template::value or std::is_same::value or - std::is_same::value, + std::is_same::value, int> = 0> void dump_integer(NumberType x) { @@ -15677,13 +15963,15 @@ class basic_json private: template friend struct detail::external_constructor; friend ::nlohmann::json_pointer; - friend ::nlohmann::detail::parser; + + template + friend class ::nlohmann::detail::parser; friend ::nlohmann::detail::serializer; template friend class ::nlohmann::detail::iter_impl; template friend class ::nlohmann::detail::binary_writer; - template + template friend class ::nlohmann::detail::binary_reader; template friend class ::nlohmann::detail::json_sax_dom_parser; @@ -15694,8 +15982,17 @@ class basic_json using basic_json_t = NLOHMANN_BASIC_JSON_TPL; // convenience aliases for types residing in namespace detail; - using lexer = ::nlohmann::detail::lexer; - using parser = ::nlohmann::detail::parser; + using lexer = ::nlohmann::detail::lexer_base; + + template + static ::nlohmann::detail::parser parser( + InputAdapterType adapter, + detail::parser_callback_tcb = nullptr, + bool allow_exceptions = true + ) + { + return ::nlohmann::detail::parser(std::move(adapter), std::move(cb), allow_exceptions); + } using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; template @@ -15709,7 +16006,8 @@ class basic_json template using output_adapter_t = ::nlohmann::detail::output_adapter_t; - using binary_reader = ::nlohmann::detail::binary_reader; + template + using binary_reader = ::nlohmann::detail::binary_reader; template using binary_writer = ::nlohmann::detail::binary_writer; using serializer = ::nlohmann::detail::serializer; @@ -16333,21 +16631,20 @@ class basic_json This type is a type designed to carry binary data that appears in various serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and - BSON's generic binary subtype. This type is NOT a part of standard JSON and - exists solely for compatibility with these binary types. As such, it is + BSON's generic binary subtype. This type is NOT a part of standard JSON and + exists solely for compatibility with these binary types. As such, it is simply defined as an ordered sequence of zero or more byte values. Additionally, as an implementation detail, the subtype of the binary data is - carried around as a `unint8_t`, which is compatible with both of the binary - data formats that use binary subtyping, (though the specific numbering is - incompatible with each other, and it is up to the user to translate between - them). + carried around as a `std::uint8_t`, which is compatible with both of the + binary data formats that use binary subtyping, (though the specific + numbering is incompatible with each other, and it is up to the user to + translate between them). [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type as: - > Major type 2: a byte string. The string's length in bytes is - > represented following the rules for positive integers (major type - > 0). + > Major type 2: a byte string. The string's length in bytes is represented + > following the rules for positive integers (major type 0). [MessagePack's documentation on the bin type family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family) @@ -16363,7 +16660,7 @@ class basic_json None of these impose any limitations on the internal representation other than the basic unit of storage be some type of array whose parts are - decomposible into bytes. + decomposable into bytes. The default representation of this binary format is a `std::vector`, which is a very common way to represent a byte @@ -16375,38 +16672,30 @@ class basic_json #### Storage - Binary Arrays are stored as pointers in a @ref basic_json type. That is, + Binary Arrays are stored as pointers in a @ref basic_json type. That is, for any access to array values, a pointer of the type `binary_t*` must be dereferenced. - @sa @ref array_t -- type for an array value - - @since version 3.8.0 - */ + #### Notes on subtypes - using binary_t = BinaryType; + - CBOR + - Binary values are represented as byte strings. No subtypes are + supported and will be ignored when CBOR is written. + - MessagePack + - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, + or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) + is used. For other sizes, the ext family (ext8, ext16, ext32) is used. + The subtype is then added as singed 8-bit integer. + - If no subtype is given, the bin family (bin8, bin16, bin32) is used. + - BSON + - If a subtype is given, it is used and added as unsigned 8-bit integer. + - If no subtype is given, the generic binary subtype 0x00 is used. - /*! - @brief an internal type for a backed binary type + @sa @ref binary -- create a binary array - This type is designed to be `binary_t` but with the subtype implementation - detail. This type exists so that the user does not have to specify a struct - his- or herself with a specific naming scheme in order to override the - binary type. I.e. it's for ease of use. + @since version 3.8.0 */ - struct internal_binary_t : public BinaryType - { - using BinaryType::BinaryType; - internal_binary_t() noexcept(noexcept(BinaryType())) : BinaryType() {} - internal_binary_t(BinaryType const& bint) noexcept(noexcept(BinaryType(bint))) : BinaryType(bint) {} - internal_binary_t(BinaryType&& bint) noexcept(noexcept(BinaryType(std::move(bint)))) : BinaryType(std::move(bint)) {} - - // TOOD: If minimum C++ version is ever bumped to C++17, this field - // deserves to be a std::optional - std::uint8_t subtype = 0; - bool has_subtype = false; - }; - + using binary_t = nlohmann::byte_container_with_subtype; /// @} private: @@ -16449,7 +16738,7 @@ class basic_json number | number_integer | @ref number_integer_t number | number_unsigned | @ref number_unsigned_t number | number_float | @ref number_float_t - binary | binary | pointer to @ref internal_binary_t + binary | binary | pointer to @ref binary_t null | null | *no value is stored* @note Variable-length types (objects, arrays, and strings) are stored as @@ -16467,7 +16756,7 @@ class basic_json /// string (stored with pointer to save storage) string_t* string; /// binary (stored with pointer to save storage) - internal_binary_t* binary; + binary_t* binary; /// boolean boolean_t boolean; /// number (integer) @@ -16512,7 +16801,7 @@ class basic_json case value_t::binary: { - binary = create(); + binary = create(); break; } @@ -16595,15 +16884,27 @@ class basic_json } /// constructor for binary arrays - json_value(const binary_t& value) + json_value(const typename binary_t::container_type& value) { - binary = create(value); + binary = create(value); } /// constructor for rvalue binary arrays + json_value(typename binary_t::container_type&& value) + { + binary = create(std::move(value)); + } + + /// constructor for binary arrays (internal type) + json_value(const binary_t& value) + { + binary = create(value); + } + + /// constructor for rvalue binary arrays (internal type) json_value(binary_t&& value) { - binary = create(std::move(value)); + binary = create(std::move(value)); } void destroy(value_t t) noexcept @@ -16683,7 +16984,7 @@ class basic_json case value_t::binary: { - AllocatorType alloc; + AllocatorType alloc; std::allocator_traits::destroy(alloc, binary); std::allocator_traits::deallocate(alloc, binary, 1); break; @@ -16711,6 +17012,7 @@ class basic_json assert(m_type != value_t::object or m_value.object != nullptr); assert(m_type != value_t::array or m_value.array != nullptr); assert(m_type != value_t::string or m_value.string != nullptr); + assert(m_type != value_t::binary or m_value.binary != nullptr); } public: @@ -16733,7 +17035,7 @@ class basic_json @sa @ref parser_callback_t for more information and examples */ - using parse_event_t = typename parser::parse_event_t; + using parse_event_t = detail::parse_event_t; /*! @brief per-element parser callback type @@ -16784,7 +17086,7 @@ class basic_json @since version 1.0.0 */ - using parser_callback_t = typename parser::parser_callback_t; + using parser_callback_t = detail::parser_callback_t; ////////////////// // constructors // @@ -16968,7 +17270,7 @@ class basic_json using other_string_t = typename BasicJsonType::string_t; using other_object_t = typename BasicJsonType::object_t; using other_array_t = typename BasicJsonType::array_t; - using other_binary_t = typename BasicJsonType::internal_binary_t; + using other_binary_t = typename BasicJsonType::binary_t; switch (val.type()) { @@ -17135,22 +17437,22 @@ class basic_json } /*! - @brief explicitly create a binary array from an already constructed copy of - its base type + @brief explicitly create a binary array (without subtype) - Creates a JSON binary array value from a given `binary_t`. Binary values are - part of various binary formats, such as CBOR, MsgPack, and BSON. And this - constructor is used to create a value for serialization to those formats. + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. @note Note, this function exists because of the difficulty in correctly specifying the correct template overload in the standard value ctor, as both JSON arrays and JSON binary arrays are backed with some form of a - `std::vector`. Because JSON binary arrays are a non-standard extension it + `std::vector`. Because JSON binary arrays are a non-standard extension it was decided that it would be best to prevent automatic initialization of a binary array type, for backwards compatibility and so it does not happen on accident. - @param[in] init `binary_t` with JSON values to create a binary array from + @param[in] init container containing bytes to use as binary type @return JSON binary array value @@ -17162,7 +17464,7 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(binary_t const& init) + static basic_json binary(const typename binary_t::container_type& init) { auto res = basic_json(); res.m_type = value_t::binary; @@ -17171,22 +17473,23 @@ class basic_json } /*! - @brief explicitly create a binary array from an already constructed rvalue - copy of its base type + @brief explicitly create a binary array (with subtype) - Creates a JSON binary array value from a given `binary_t`. Binary values are - part of various binary formats, such as CBOR, MsgPack, and BSON. And this - constructor is used to create a value for serialization to those formats. + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. @note Note, this function exists because of the difficulty in correctly specifying the correct template overload in the standard value ctor, as both JSON arrays and JSON binary arrays are backed with some form of a - `std::vector`. Because JSON binary arrays are a non-standard extension it + `std::vector`. Because JSON binary arrays are a non-standard extension it was decided that it would be best to prevent automatic initialization of a - binary array type, for backwards compatibility and so it doesn't happen on + binary array type, for backwards compatibility and so it does not happen on accident. - @param[in] init `binary_t` with JSON values to create a binary array from + @param[in] init container containing bytes to use as binary type + @param[in] subtype subtype to use in MessagePack and BSON @return JSON binary array value @@ -17198,7 +17501,17 @@ class basic_json @since version 3.8.0 */ JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json binary_array(binary_t&& init) + static basic_json binary(const typename binary_t::container_type& init, std::uint8_t subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); + return res; + } + + /// @copydoc binary(const typename binary_t::container_type&) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init) { auto res = basic_json(); res.m_type = value_t::binary; @@ -17206,6 +17519,16 @@ class basic_json return res; } + /// @copydoc binary(const typename binary_t::container_type&, std::uint8_t) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init, std::uint8_t subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(std::move(init), subtype); + return res; + } + /*! @brief explicitly create an array from an initializer list @@ -17463,8 +17786,7 @@ class basic_json case value_t::binary: { - m_value.binary = create(first.m_it.binary_iterator, - last.m_it.binary_iterator); + m_value = *first.m_object->m_value.binary; break; } @@ -17707,16 +18029,15 @@ class basic_json possible values: `strict` (throws and exception in case a decoding error occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD), and `ignore` (ignore invalid UTF-8 sequences during serialization). - @param[in] serialize_binary Whether or not to allow serialization of binary - types to JSON. Because binary types are non-standard, this will produce - non-conformant JSON, and is disabled by default. This flag is primarily - useful for debugging. Will output the binary value as a list of 8-bit - numbers prefixed by "b" (e.g. "bindata" = b[3, 0, 42, 255]). @return string containing the serialization of the JSON value @throw type_error.316 if a string stored inside the JSON value is not - UTF-8 encoded + UTF-8 encoded and @a error_handler is set to strict + + @note Binary values are serialized as object containing two keys: + - "bytes": an array of bytes as integers + - "subtype": the subtype as integer or "null" if the binary has no subtype @complexity Linear. @@ -17731,24 +18052,24 @@ class basic_json @since version 1.0.0; indentation character @a indent_char, option @a ensure_ascii and exceptions added in version 3.0.0; error - handlers added in version 3.4.0. + handlers added in version 3.4.0; serialization of binary values added + in version 3.8.0. */ string_t dump(const int indent = -1, const char indent_char = ' ', const bool ensure_ascii = false, - const error_handler_t error_handler = error_handler_t::strict, - const bool serialize_binary = false) const + const error_handler_t error_handler = error_handler_t::strict) const { string_t result; serializer s(detail::output_adapter(result), indent_char, error_handler); if (indent >= 0) { - s.dump(*this, true, ensure_ascii, static_cast(indent), 0, serialize_binary); + s.dump(*this, true, ensure_ascii, static_cast(indent)); } else { - s.dump(*this, false, ensure_ascii, 0, 0, serialize_binary); + s.dump(*this, false, ensure_ascii, 0); } return result; @@ -18264,18 +18585,6 @@ class basic_json return is_binary() ? m_value.binary : nullptr; } - /// get a pointer to the value (binary) - internal_binary_t* get_impl_ptr(internal_binary_t* /*unused*/) noexcept - { - return is_binary() ? m_value.binary : nullptr; - } - - /// get a pointer to the value (binary) - constexpr const internal_binary_t* get_impl_ptr(const internal_binary_t* /*unused*/) const noexcept - { - return is_binary() ? m_value.binary : nullptr; - } - /*! @brief helper function to implement get_ref() @@ -18701,6 +19010,36 @@ class basic_json return get(); } + /*! + @return reference to the binary value + + @throw type_error.302 if the value is not binary + + @sa @ref is_binary() to check if the value is binary + + @since version 3.8.0 + */ + binary_t& get_binary() + { + if (not is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + + /// @copydoc get_binary() + const binary_t& get_binary() const + { + if (not is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + /// @} @@ -19323,120 +19662,6 @@ class basic_json return value(ptr, string_t(default_value)); } - /*! - @brief return the binary subtype - - Returns the numerical subtype of the JSON value, if the JSON value is of - type "binary", and it has a subtype. If it does not have a subtype (or the - object is not of type binary) this function will return size_t(-1) as a - sentinel value. - - @return the numerical subtype of the binary JSON value - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - std::size_t get_subtype() const noexcept - { - if (is_binary() and m_value.binary->has_subtype) - { - return m_value.binary->subtype; - } - - return std::size_t(-1); - } - - /*! - @brief sets the binary subtype - - Sets the binary subtype of the JSON value, also flags a binary JSON value as - having a subtype, which has implications for serialization to msgpack (will - prefer ext file formats over bin). If the JSON value is not a binary value, - this function does nothing. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref get_subtype() -- return the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - - void set_subtype(std::uint8_t subtype) noexcept - { - if (is_binary()) - { - m_value.binary->has_subtype = true; - m_value.binary->subtype = subtype; - } - } - - /*! - @brief clears the binary subtype - - Clears the binary subtype of the JSON value, also flags a binary JSON value - as not having a subtype, which has implications for serialization to msgpack - (will prefer bin file formats over ext). If the JSON value is not a binary - value, this function does nothing. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref get_subtype() -- return the binary subtype - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref has_subtype() -- returns whether or not the binary value has a - subtype - - @since version 3.8.0 - */ - void clear_subtype() noexcept - { - if (is_binary()) - { - m_value.binary->has_subtype = false; - m_value.binary->subtype = 0; - } - } - - /*! - @brief return whether or not the binary subtype has a value - - Returns whether or not the binary subtype has a value. - - @return whether or not the binary subtype has a value. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @sa @ref get_subtype() -- return the binary subtype - @sa @ref set_subtype() -- sets the binary subtype - @sa @ref clear_subtype() -- clears the binary subtype - - @since version 3.8.0 - */ - bool has_subtype() const noexcept - { - return is_binary() and m_value.binary->has_subtype; - } - /*! @brief access the first element @@ -19606,7 +19831,7 @@ class basic_json } else if (is_binary()) { - AllocatorType alloc; + AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.binary); std::allocator_traits::deallocate(alloc, m_value.binary, 1); m_value.binary = nullptr; @@ -19720,7 +19945,7 @@ class basic_json } else if (is_binary()) { - AllocatorType alloc; + AllocatorType alloc; std::allocator_traits::destroy(alloc, m_value.binary); std::allocator_traits::deallocate(alloc, m_value.binary, 1); m_value.binary = nullptr; @@ -20396,6 +20621,11 @@ class basic_json element as string (see example). For primitive types (e.g., numbers), `key()` returns an empty string. + @warning Using `items()` on temporary objects is dangerous. Make sure the + object's lifetime exeeds the iteration. See + for more + information. + @return iteration proxy object wrapping @a ref with an interface to use in range-based for loops @@ -21522,6 +21752,53 @@ class basic_json } } + /*! + @brief exchanges the values + + Exchanges the contents of a JSON string 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 + invalidated. + + @param[in,out] other binary to exchange the contents with + + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` + + @complexity Constant. + + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__binary_t} + + @since version 3.8.0 + */ + void swap(binary_t& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /// @copydoc swap(binary_t) + void swap(typename binary_t::container_type& other) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + /// @} public: @@ -21784,7 +22061,7 @@ class basic_json return (lhs.m_value.number_float) < (rhs.m_value.number_float); case value_t::binary: - return (lhs.m_value.binary) < (rhs.m_value.binary); + return (*lhs.m_value.binary) < (*rhs.m_value.binary); default: return false; @@ -22123,21 +22400,39 @@ class basic_json @since version 2.0.3 (contiguous containers) */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(InputType&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) + { + basic_json result; + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions).parse(true, result); + return result; + } + + + JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json parse(detail::input_adapter&& i, + static basic_json parse(detail::span_input_adapter&& i, const parser_callback_t cb = nullptr, const bool allow_exceptions = true) { basic_json result; - parser(i, cb, allow_exceptions).parse(true, result); + parser(i.get(), cb, allow_exceptions).parse(true, result); return result; } - static bool accept(detail::input_adapter&& i) + template + static bool accept(InputType&& i) { - return parser(i).accept(true); + return parser(detail::input_adapter(std::forward(i))).accept(true); } + static bool accept(detail::span_input_adapter&& i) + { + return parser(i.get()).accept(true); + } /*! @brief generate SAX events @@ -22191,17 +22486,31 @@ class basic_json @since version 3.2.0 */ + template + 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) + { + auto ia = detail::input_adapter(std::forward(i)); + return format == input_format_t::json + ? parser(std::move(ia)).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + template JSON_HEDLEY_NON_NULL(2) - static bool sax_parse(detail::input_adapter&& i, SAX* sax, + static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, input_format_t format = input_format_t::json, const bool strict = true) { + auto ia = i.get(); return format == input_format_t::json - ? parser(std::move(i)).sax_parse(sax, strict) - : detail::binary_reader(std::move(i)).sax_parse(format, sax, strict); + ? parser(std::move(ia)).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); } + /*! @brief deserialize from an iterator range with contiguous storage @@ -22442,7 +22751,8 @@ class basic_json number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B - number_float | *any value* | Double-Precision Float | 0xFB + number_float | *any value representable by a float* | Single-Precision Float | 0xFA + number_float | *any value NOT representable by a float* | Double-Precision Float | 0xFB string | *length*: 0..23 | UTF-8 string | 0x60..0x77 string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 @@ -22484,11 +22794,11 @@ class basic_json - expected conversions (0xD5..0xD7) - simple values (0xE0..0xF3, 0xF8) - undefined (0xF7) - - half and single-precision floats (0xF9-0xFA) + - half-precision floats (0xF9) - break (0xFF) @param[in] j JSON value to serialize - @return MessagePack serialization as byte vector + @return CBOR serialization as byte vector @complexity Linear in the size of the JSON value @a j. @@ -22502,7 +22812,8 @@ class basic_json @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the related UBJSON format - @since version 2.0.9 + @since version 2.0.9; compact representation of floating-point numbers + since version 3.8.0 */ static std::vector to_cbor(const basic_json& j) { @@ -22913,14 +23224,16 @@ class basic_json @a strict parameter since 3.0.0; added @a allow_exceptions parameter since 3.2.0 */ + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_cbor(detail::input_adapter&& i, + static basic_json from_cbor(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::cbor, &sdp, strict); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -22928,7 +23241,7 @@ class basic_json @copydoc from_cbor(detail::input_adapter&&, const bool, const bool) */ template::value, int> = 0> + detail::enable_if_t::value, int> = 0> JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_cbor(A1 && a1, A2 && a2, const bool strict = true, @@ -22936,7 +23249,18 @@ class basic_json { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).sax_parse(input_format_t::cbor, &sdp, strict); + const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(a2)).get()).sax_parse(input_format_t::cbor, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(i.get()).sax_parse(input_format_t::cbor, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -23026,14 +23350,16 @@ class basic_json @a strict parameter since 3.0.0; added @a allow_exceptions parameter since 3.2.0 */ + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_msgpack(detail::input_adapter&& i, + static basic_json from_msgpack(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::msgpack, &sdp, strict); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -23041,7 +23367,7 @@ class basic_json @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool) */ template::value, int> = 0> + detail::enable_if_t::value, int> = 0> JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_msgpack(A1 && a1, A2 && a2, const bool strict = true, @@ -23049,10 +23375,23 @@ class basic_json { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).sax_parse(input_format_t::msgpack, &sdp, strict); + const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(a2)).get()).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(i.get()).sax_parse(input_format_t::msgpack, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + /*! @brief create a JSON value from an input in UBJSON format @@ -23114,14 +23453,16 @@ class basic_json @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0 */ + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_ubjson(detail::input_adapter&& i, + static basic_json from_ubjson(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::ubjson, &sdp, strict); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -23129,7 +23470,7 @@ class basic_json @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool) */ template::value, int> = 0> + detail::enable_if_t::value, int> = 0> JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_ubjson(A1 && a1, A2 && a2, const bool strict = true, @@ -23137,10 +23478,22 @@ class basic_json { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).sax_parse(input_format_t::ubjson, &sdp, strict); + const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(a2)).get()).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + + static basic_json from_ubjson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(i.get()).sax_parse(input_format_t::ubjson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } + /*! @brief Create a JSON value from an input in BSON format @@ -23201,14 +23554,16 @@ class basic_json @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the related UBJSON format */ + template JSON_HEDLEY_WARN_UNUSED_RESULT - static basic_json from_bson(detail::input_adapter&& i, + static basic_json from_bson(InputType&& i, const bool strict = true, const bool allow_exceptions = true) { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::bson, &sdp, strict); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } @@ -23216,7 +23571,7 @@ class basic_json @copydoc from_bson(detail::input_adapter&&, const bool, const bool) */ template::value, int> = 0> + detail::enable_if_t::value, int> = 0> JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_bson(A1 && a1, A2 && a2, const bool strict = true, @@ -23224,12 +23579,20 @@ class basic_json { basic_json result; detail::json_sax_dom_parser sdp(result, allow_exceptions); - const bool res = binary_reader(detail::input_adapter(std::forward(a1), std::forward(a2))).sax_parse(input_format_t::bson, &sdp, strict); + const bool res = binary_reader(detail::span_input_adapter(std::forward(a1), std::forward(a2)).get()).sax_parse(input_format_t::bson, &sdp, strict); return res ? result : basic_json(value_t::discarded); } - - + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + const bool res = binary_reader(i.get()).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } /// @} ////////////////////////// diff --git a/test/Makefile b/test/Makefile index 499ff3c23b..7bf0fef98b 100644 --- a/test/Makefile +++ b/test/Makefile @@ -57,7 +57,10 @@ TESTCASES = $(patsubst src/unit-%.cpp,test-%,$(wildcard src/unit-*.cpp)) all: $(TESTCASES) clean: - rm -fr json_unit $(OBJECTS) $(SOURCES:.cpp=.gcno) $(SOURCES:.cpp=.gcda) $(TESTCASES) $(FUZZERS) + rm -fr json_unit $(OBJECTS) $(SOURCES:.cpp=.gcno) $(SOURCES:.cpp=.gcda) $(TESTCASES) $(FUZZERS) test_data.hpp + +test_data.hpp: + @echo "#define TEST_DATA_DIRECTORY" > $@ ############################################################################## # single test file @@ -67,7 +70,7 @@ json_unit: $(OBJECTS) ../single_include/nlohmann/json.hpp thirdparty/doctest/doc @echo "[CXXLD] $@" @$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) -o $@ -%.o: %.cpp ../single_include/nlohmann/json.hpp thirdparty/doctest/doctest.h +%.o: %.cpp ../single_include/nlohmann/json.hpp thirdparty/doctest/doctest.h test_data.hpp @echo "[CXX] $@" @$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ @@ -76,7 +79,7 @@ json_unit: $(OBJECTS) ../single_include/nlohmann/json.hpp thirdparty/doctest/doc # individual test cases ############################################################################## -test-%: src/unit-%.o src/unit.o ../single_include/nlohmann/json.hpp thirdparty/doctest/doctest.h +test-%: src/unit-%.o src/unit.o ../single_include/nlohmann/json.hpp thirdparty/doctest/doctest.h test_data.hpp @echo "[CXXLD] $@" @$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $< src/unit.o -o $@ diff --git a/test/src/unit-bson.cpp b/test/src/unit-bson.cpp index 9e8e996fc2..3f3dbd10e1 100644 --- a/test/src/unit-bson.cpp +++ b/test/src/unit-bson.cpp @@ -106,7 +106,7 @@ TEST_CASE("BSON") SECTION("string length must be at least 1") { // from https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=11175 - std::vector v = + std::vector v = { 0x20, 0x20, 0x20, 0x20, 0x02, @@ -123,7 +123,7 @@ TEST_CASE("BSON") SECTION("empty object") { json j = json::object(); - std::vector expected = + std::vector expected = { 0x05, 0x00, 0x00, 0x00, // size (little endian) // no entries @@ -145,7 +145,7 @@ TEST_CASE("BSON") { "entry", true } }; - std::vector expected = + std::vector expected = { 0x0D, 0x00, 0x00, 0x00, // size (little endian) 0x08, // entry: boolean @@ -169,7 +169,7 @@ TEST_CASE("BSON") { "entry", false } }; - std::vector expected = + std::vector expected = { 0x0D, 0x00, 0x00, 0x00, // size (little endian) 0x08, // entry: boolean @@ -193,7 +193,7 @@ TEST_CASE("BSON") { "entry", 4.2 } }; - std::vector expected = + std::vector expected = { 0x14, 0x00, 0x00, 0x00, // size (little endian) 0x01, /// entry: double @@ -217,7 +217,7 @@ TEST_CASE("BSON") { "entry", "bsonstr" } }; - std::vector expected = + std::vector expected = { 0x18, 0x00, 0x00, 0x00, // size (little endian) 0x02, /// entry: string (UTF-8) @@ -241,7 +241,7 @@ TEST_CASE("BSON") { "entry", nullptr } }; - std::vector expected = + std::vector expected = { 0x0C, 0x00, 0x00, 0x00, // size (little endian) 0x0A, /// entry: null @@ -264,7 +264,7 @@ TEST_CASE("BSON") { "entry", std::int32_t{0x12345678} } }; - std::vector expected = + std::vector expected = { 0x10, 0x00, 0x00, 0x00, // size (little endian) 0x10, /// entry: int32 @@ -288,7 +288,7 @@ TEST_CASE("BSON") { "entry", std::int64_t{0x1234567804030201} } }; - std::vector expected = + std::vector expected = { 0x14, 0x00, 0x00, 0x00, // size (little endian) 0x12, /// entry: int64 @@ -312,7 +312,7 @@ TEST_CASE("BSON") { "entry", std::int32_t{-1} } }; - std::vector expected = + std::vector expected = { 0x10, 0x00, 0x00, 0x00, // size (little endian) 0x10, /// entry: int32 @@ -336,7 +336,7 @@ TEST_CASE("BSON") { "entry", std::int64_t{-1} } }; - std::vector expected = + std::vector expected = { 0x10, 0x00, 0x00, 0x00, // size (little endian) 0x10, /// entry: int32 @@ -361,7 +361,7 @@ TEST_CASE("BSON") { "entry", std::uint64_t{0x1234567804030201} } }; - std::vector expected = + std::vector expected = { 0x14, 0x00, 0x00, 0x00, // size (little endian) 0x12, /// entry: int64 @@ -385,7 +385,7 @@ TEST_CASE("BSON") { "entry", std::uint64_t{0x42} } }; - std::vector expected = + std::vector expected = { 0x10, 0x00, 0x00, 0x00, // size (little endian) 0x10, /// entry: int32 @@ -409,7 +409,7 @@ TEST_CASE("BSON") { "entry", json::object() } }; - std::vector expected = + std::vector expected = { 0x11, 0x00, 0x00, 0x00, // size (little endian) 0x03, /// entry: embedded document @@ -437,7 +437,7 @@ TEST_CASE("BSON") { "entry", json::array() } }; - std::vector expected = + std::vector expected = { 0x11, 0x00, 0x00, 0x00, // size (little endian) 0x04, /// entry: embedded document @@ -465,7 +465,7 @@ TEST_CASE("BSON") { "entry", json::array({1, 2, 3, 4, 5, 6, 7, 8}) } }; - std::vector expected = + std::vector expected = { 0x49, 0x00, 0x00, 0x00, // size (little endian) 0x04, /// entry: embedded document @@ -496,13 +496,13 @@ TEST_CASE("BSON") SECTION("non-empty object with binary member") { const size_t N = 10; - const auto s = std::vector(N, 'x'); + const auto s = std::vector(N, 'x'); json j = { - { "entry", json::binary_array(s) } + { "entry", json::binary(s, 0) } }; - std::vector expected = + std::vector expected = { 0x1B, 0x00, 0x00, 0x00, // size (little endian) 0x05, // entry: binary @@ -523,6 +523,36 @@ TEST_CASE("BSON") CHECK(json::from_bson(result, true, false) == j); } + SECTION("non-empty object with binary member with subtype") + { + // an MD5 hash + const std::vector md5hash = {0xd7, 0x7e, 0x27, 0x54, 0xbe, 0x12, 0x37, 0xfe, 0xd6, 0x0c, 0x33, 0x98, 0x30, 0x3b, 0x8d, 0xc4}; + json j = + { + { "entry", json::binary(md5hash, 5) } + }; + + std::vector expected = + { + 0x21, 0x00, 0x00, 0x00, // size (little endian) + 0x05, // entry: binary + 'e', 'n', 't', 'r', 'y', '\x00', + + 0x10, 0x00, 0x00, 0x00, // size of binary (little endian) + 0x05, // MD5 binary subtype + 0xd7, 0x7e, 0x27, 0x54, 0xbe, 0x12, 0x37, 0xfe, 0xd6, 0x0c, 0x33, 0x98, 0x30, 0x3b, 0x8d, 0xc4, + + 0x00 // end marker + }; + + const auto result = json::to_bson(j); + CHECK(result == expected); + + // roundtrip + CHECK(json::from_bson(result) == j); + CHECK(json::from_bson(result, true, false) == j); + } + SECTION("Some more complex document") { // directly encoding uint64 is not supported in bson (only for timestamp values) @@ -534,7 +564,7 @@ TEST_CASE("BSON") {"object", {{ "string", "value" }}} }; - std::vector expected = + std::vector expected = { /*size */ 0x4f, 0x00, 0x00, 0x00, /*entry*/ 0x01, 'd', 'o', 'u', 'b', 'l', 'e', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x45, 0x40, @@ -592,7 +622,7 @@ TEST_CASE("BSON input/output_adapters") {"object", {{ "string", "value" }}} }; - std::vector bson_representation = + std::vector bson_representation = { /*size */ 0x4f, 0x00, 0x00, 0x00, /*entry*/ 0x01, 'd', 'o', 'u', 'b', 'l', 'e', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x45, 0x40, @@ -677,7 +707,7 @@ class SaxCountdown return events_left-- > 0; } - bool binary(std::vector&) + bool binary(std::vector&) { return events_left-- > 0; } @@ -721,7 +751,7 @@ TEST_CASE("Incomplete BSON Input") { SECTION("Incomplete BSON Input 1") { - std::vector incomplete_bson = + std::vector incomplete_bson = { 0x0D, 0x00, 0x00, 0x00, // size (little endian) 0x08, // entry: boolean @@ -741,7 +771,7 @@ TEST_CASE("Incomplete BSON Input") SECTION("Incomplete BSON Input 2") { - std::vector incomplete_bson = + std::vector incomplete_bson = { 0x0D, 0x00, 0x00, 0x00, // size (little endian) 0x08, // entry: boolean, unexpected EOF @@ -759,7 +789,7 @@ TEST_CASE("Incomplete BSON Input") SECTION("Incomplete BSON Input 3") { - std::vector incomplete_bson = + std::vector incomplete_bson = { 0x41, 0x00, 0x00, 0x00, // size (little endian) 0x04, /// entry: embedded document @@ -783,7 +813,7 @@ TEST_CASE("Incomplete BSON Input") SECTION("Incomplete BSON Input 4") { - std::vector incomplete_bson = + std::vector incomplete_bson = { 0x0D, 0x00, // size (incomplete), unexpected EOF }; @@ -821,9 +851,28 @@ TEST_CASE("Incomplete BSON Input") } } +TEST_CASE("Negative size of binary value") +{ + // invalid BSON: the size of the binary value is -1 + std::vector input = + { + 0x21, 0x00, 0x00, 0x00, // size (little endian) + 0x05, // entry: binary + 'e', 'n', 't', 'r', 'y', '\x00', + + 0xFF, 0xFF, 0xFF, 0xFF, // size of binary (little endian) + 0x05, // MD5 binary subtype + 0xd7, 0x7e, 0x27, 0x54, 0xbe, 0x12, 0x37, 0xfe, 0xd6, 0x0c, 0x33, 0x98, 0x30, 0x3b, 0x8d, 0xc4, + + 0x00 // end marker + }; + CHECK_THROWS_AS(json::from_bson(input), json::parse_error); + CHECK_THROWS_WITH(json::from_bson(input), "[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1"); +} + TEST_CASE("Unsupported BSON input") { - std::vector bson = + std::vector bson = { 0x0C, 0x00, 0x00, 0x00, // size (little endian) 0xFF, // entry type: Min key (not supported yet) @@ -876,19 +925,19 @@ TEST_CASE("BSON numerical data") CHECK(j.at("entry").is_number_integer()); std::uint64_t iu = *reinterpret_cast(&i); - std::vector expected_bson = + std::vector expected_bson = { 0x14u, 0x00u, 0x00u, 0x00u, // size (little endian) 0x12u, /// entry: int64 'e', 'n', 't', 'r', 'y', '\x00', - static_cast((iu >> (8u * 0u)) & 0xffu), - static_cast((iu >> (8u * 1u)) & 0xffu), - static_cast((iu >> (8u * 2u)) & 0xffu), - static_cast((iu >> (8u * 3u)) & 0xffu), - static_cast((iu >> (8u * 4u)) & 0xffu), - static_cast((iu >> (8u * 5u)) & 0xffu), - static_cast((iu >> (8u * 6u)) & 0xffu), - static_cast((iu >> (8u * 7u)) & 0xffu), + static_cast((iu >> (8u * 0u)) & 0xffu), + static_cast((iu >> (8u * 1u)) & 0xffu), + static_cast((iu >> (8u * 2u)) & 0xffu), + static_cast((iu >> (8u * 3u)) & 0xffu), + static_cast((iu >> (8u * 4u)) & 0xffu), + static_cast((iu >> (8u * 5u)) & 0xffu), + static_cast((iu >> (8u * 6u)) & 0xffu), + static_cast((iu >> (8u * 7u)) & 0xffu), 0x00u // end marker }; @@ -948,15 +997,15 @@ TEST_CASE("BSON numerical data") CHECK(j.at("entry").is_number_integer()); std::uint32_t iu = *reinterpret_cast(&i); - std::vector expected_bson = + std::vector expected_bson = { 0x10u, 0x00u, 0x00u, 0x00u, // size (little endian) 0x10u, /// entry: int32 'e', 'n', 't', 'r', 'y', '\x00', - static_cast((iu >> (8u * 0u)) & 0xffu), - static_cast((iu >> (8u * 1u)) & 0xffu), - static_cast((iu >> (8u * 2u)) & 0xffu), - static_cast((iu >> (8u * 3u)) & 0xffu), + static_cast((iu >> (8u * 0u)) & 0xffu), + static_cast((iu >> (8u * 1u)) & 0xffu), + static_cast((iu >> (8u * 2u)) & 0xffu), + static_cast((iu >> (8u * 3u)) & 0xffu), 0x00u // end marker }; @@ -1001,19 +1050,19 @@ TEST_CASE("BSON numerical data") CHECK(j.at("entry").is_number_integer()); std::uint64_t iu = *reinterpret_cast(&i); - std::vector expected_bson = + std::vector expected_bson = { 0x14u, 0x00u, 0x00u, 0x00u, // size (little endian) 0x12u, /// entry: int64 'e', 'n', 't', 'r', 'y', '\x00', - static_cast((iu >> (8u * 0u)) & 0xffu), - static_cast((iu >> (8u * 1u)) & 0xffu), - static_cast((iu >> (8u * 2u)) & 0xffu), - static_cast((iu >> (8u * 3u)) & 0xffu), - static_cast((iu >> (8u * 4u)) & 0xffu), - static_cast((iu >> (8u * 5u)) & 0xffu), - static_cast((iu >> (8u * 6u)) & 0xffu), - static_cast((iu >> (8u * 7u)) & 0xffu), + static_cast((iu >> (8u * 0u)) & 0xffu), + static_cast((iu >> (8u * 1u)) & 0xffu), + static_cast((iu >> (8u * 2u)) & 0xffu), + static_cast((iu >> (8u * 3u)) & 0xffu), + static_cast((iu >> (8u * 4u)) & 0xffu), + static_cast((iu >> (8u * 5u)) & 0xffu), + static_cast((iu >> (8u * 6u)) & 0xffu), + static_cast((iu >> (8u * 7u)) & 0xffu), 0x00u // end marker }; @@ -1062,15 +1111,15 @@ TEST_CASE("BSON numerical data") }; auto iu = i; - std::vector expected_bson = + std::vector expected_bson = { 0x10u, 0x00u, 0x00u, 0x00u, // size (little endian) 0x10u, /// entry: int32 'e', 'n', 't', 'r', 'y', '\x00', - static_cast((iu >> (8u * 0u)) & 0xffu), - static_cast((iu >> (8u * 1u)) & 0xffu), - static_cast((iu >> (8u * 2u)) & 0xffu), - static_cast((iu >> (8u * 3u)) & 0xffu), + static_cast((iu >> (8u * 0u)) & 0xffu), + static_cast((iu >> (8u * 1u)) & 0xffu), + static_cast((iu >> (8u * 2u)) & 0xffu), + static_cast((iu >> (8u * 3u)) & 0xffu), 0x00u // end marker }; @@ -1117,19 +1166,19 @@ TEST_CASE("BSON numerical data") }; auto iu = i; - std::vector expected_bson = + std::vector expected_bson = { 0x14u, 0x00u, 0x00u, 0x00u, // size (little endian) 0x12u, /// entry: int64 'e', 'n', 't', 'r', 'y', '\x00', - static_cast((iu >> (8u * 0u)) & 0xffu), - static_cast((iu >> (8u * 1u)) & 0xffu), - static_cast((iu >> (8u * 2u)) & 0xffu), - static_cast((iu >> (8u * 3u)) & 0xffu), - static_cast((iu >> (8u * 4u)) & 0xffu), - static_cast((iu >> (8u * 5u)) & 0xffu), - static_cast((iu >> (8u * 6u)) & 0xffu), - static_cast((iu >> (8u * 7u)) & 0xffu), + static_cast((iu >> (8u * 0u)) & 0xffu), + static_cast((iu >> (8u * 1u)) & 0xffu), + static_cast((iu >> (8u * 2u)) & 0xffu), + static_cast((iu >> (8u * 3u)) & 0xffu), + static_cast((iu >> (8u * 4u)) & 0xffu), + static_cast((iu >> (8u * 5u)) & 0xffu), + static_cast((iu >> (8u * 6u)) & 0xffu), + static_cast((iu >> (8u * 7u)) & 0xffu), 0x00u // end marker }; @@ -1167,19 +1216,19 @@ TEST_CASE("BSON numerical data") }; auto iu = i; - std::vector expected_bson = + std::vector expected_bson = { 0x14u, 0x00u, 0x00u, 0x00u, // size (little endian) 0x12u, /// entry: int64 'e', 'n', 't', 'r', 'y', '\x00', - static_cast((iu >> (8u * 0u)) & 0xffu), - static_cast((iu >> (8u * 1u)) & 0xffu), - static_cast((iu >> (8u * 2u)) & 0xffu), - static_cast((iu >> (8u * 3u)) & 0xffu), - static_cast((iu >> (8u * 4u)) & 0xffu), - static_cast((iu >> (8u * 5u)) & 0xffu), - static_cast((iu >> (8u * 6u)) & 0xffu), - static_cast((iu >> (8u * 7u)) & 0xffu), + static_cast((iu >> (8u * 0u)) & 0xffu), + static_cast((iu >> (8u * 1u)) & 0xffu), + static_cast((iu >> (8u * 2u)) & 0xffu), + static_cast((iu >> (8u * 3u)) & 0xffu), + static_cast((iu >> (8u * 4u)) & 0xffu), + static_cast((iu >> (8u * 5u)) & 0xffu), + static_cast((iu >> (8u * 6u)) & 0xffu), + static_cast((iu >> (8u * 7u)) & 0xffu), 0x00u // end marker }; @@ -1208,14 +1257,14 @@ TEST_CASE("BSON roundtrips" * doctest::skip()) CAPTURE(filename) { - INFO_WITH_TEMP(filename + ": std::vector"); + INFO_WITH_TEMP(filename + ": std::vector"); // parse JSON file std::ifstream f_json(filename); json j1 = json::parse(f_json); // parse BSON file std::ifstream f_bson(filename + ".bson", std::ios::binary); - std::vector packed( + std::vector packed( (std::istreambuf_iterator(f_bson)), std::istreambuf_iterator()); json j2; @@ -1248,7 +1297,7 @@ TEST_CASE("BSON roundtrips" * doctest::skip()) // parse BSON file std::ifstream f_bson(filename + ".bson", std::ios::binary); - std::vector packed( + std::vector packed( (std::istreambuf_iterator(f_bson)), std::istreambuf_iterator()); json j2; @@ -1266,13 +1315,13 @@ TEST_CASE("BSON roundtrips" * doctest::skip()) // parse BSON file std::ifstream f_bson(filename + ".bson", std::ios::binary); - std::vector packed( + std::vector packed( (std::istreambuf_iterator(f_bson)), std::istreambuf_iterator()); { - INFO_WITH_TEMP(filename + ": output adapters: std::vector"); - std::vector vec; + INFO_WITH_TEMP(filename + ": output adapters: std::vector"); + std::vector vec; json::to_bson(j1, vec); if (vec != packed) diff --git a/test/src/unit-cbor.cpp b/test/src/unit-cbor.cpp index 0137904169..a29e8dcd63 100644 --- a/test/src/unit-cbor.cpp +++ b/test/src/unit-cbor.cpp @@ -130,6 +130,24 @@ TEST_CASE("CBOR") CHECK(result.empty()); } + SECTION("NaN") + { + // NaN value + json j = std::numeric_limits::quiet_NaN(); + std::vector expected = {0xf9, 0x7e, 0x00}; + const auto result = json::to_cbor(j); + CHECK(result == expected); + } + + SECTION("Infinity") + { + // Infinity value + json j = std::numeric_limits::infinity(); + std::vector expected = {0xf9, 0x7c, 0x00}; + const auto result = json::to_cbor(j); + CHECK(result == expected); + } + SECTION("null") { json j = nullptr; @@ -816,7 +834,7 @@ TEST_CASE("CBOR") } } - SECTION("float") + SECTION("double-precision float") { SECTION("3.1415925") { @@ -837,6 +855,135 @@ TEST_CASE("CBOR") } } + SECTION("single-precision float") + { + SECTION("0.5") + { + double v = 0.5; + json j = v; + // its double-precision float binary value is + // {0xfb, 0x3f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + // but to save memory, we can store it as single-precision float. + std::vector expected = {0xfa, 0x3f, 0x00, 0x00, 0x00}; + const auto result = json::to_cbor(j); + CHECK(result == expected); + // roundtrip + CHECK(json::from_cbor(result) == j); + CHECK(json::from_cbor(result) == v); + } + SECTION("0.0") + { + double v = 0.0; + json j = v; + // its double-precision binary value is: + // {0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + std::vector expected = {0xfa, 0x00, 0x00, 0x00, 0x00}; + const auto result = json::to_cbor(j); + CHECK(result == expected); + // roundtrip + CHECK(json::from_cbor(result) == j); + CHECK(json::from_cbor(result) == v); + } + SECTION("-0.0") + { + double v = -0.0; + json j = v; + // its double-precision binary value is: + // {0xfb, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + std::vector expected = {0xfa, 0x80, 0x00, 0x00, 0x00}; + const auto result = json::to_cbor(j); + CHECK(result == expected); + // roundtrip + CHECK(json::from_cbor(result) == j); + CHECK(json::from_cbor(result) == v); + } + SECTION("100.0") + { + double v = 100.0; + json j = v; + // its double-precision binary value is: + // {0xfb, 0x40, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + std::vector expected = {0xfa, 0x42, 0xc8, 0x00, 0x00}; + const auto result = json::to_cbor(j); + CHECK(result == expected); + // roundtrip + CHECK(json::from_cbor(result) == j); + CHECK(json::from_cbor(result) == v); + } + SECTION("200.0") + { + double v = 200.0; + json j = v; + // its double-precision binary value is: + // {0xfb, 0x40, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + std::vector expected = {0xfa, 0x43, 0x48, 0x00, 0x00}; + const auto result = json::to_cbor(j); + CHECK(result == expected); + // roundtrip + CHECK(json::from_cbor(result) == j); + CHECK(json::from_cbor(result) == v); + } + SECTION("3.40282e+38(max float)") + { + float v = (std::numeric_limits::max)(); + json j = v; + std::vector expected = + { + 0xfa, 0x7f, 0x7f, 0xff, 0xff + }; + const auto result = json::to_cbor(j); + CHECK(result == expected); + // roundtrip + CHECK(json::from_cbor(result) == j); + CHECK(json::from_cbor(result) == v); + } + SECTION("-3.40282e+38(lowest float)") + { + double v = static_cast(std::numeric_limits::lowest()); + json j = v; + std::vector expected = + { + 0xfa, 0xff, 0x7f, 0xff, 0xff + }; + const auto result = json::to_cbor(j); + CHECK(result == expected); + // roundtrip + CHECK(json::from_cbor(result) == j); + CHECK(json::from_cbor(result) == v); + } + SECTION("1 + 3.40282e+38(more than max float)") + { + double v = static_cast((std::numeric_limits::max)()) + 0.1e+34; + json j = v; + std::vector expected = + { + 0xfb, 0x47, 0xf0, 0x00, 0x03, 0x04, 0xdc, 0x64, 0x49 + }; + // double + const auto result = json::to_cbor(j); + CHECK(result == expected); + // roundtrip + CHECK(json::from_cbor(result) == j); + CHECK(json::from_cbor(result) == v); + } + SECTION("-1 - 3.40282e+38(less than lowest float)") + { + double v = static_cast(std::numeric_limits::lowest()) - 1.0; + json j = v; + std::vector expected = + { + 0xfa, 0xff, 0x7f, 0xff, 0xff + }; + // the same with lowest float + const auto result = json::to_cbor(j); + CHECK(result == expected); + // roundtrip + CHECK(json::from_cbor(result) == j); + CHECK(json::from_cbor(result) == v); + } + + } + SECTION("half-precision float (edge cases)") { SECTION("errors") @@ -936,7 +1083,7 @@ TEST_CASE("CBOR") SECTION("NaN") { - json j = json::from_cbor(std::vector({0xf9, 0x7c, 0x01})); + json j = json::from_cbor(std::vector({0xf9, 0x7e, 0x00})); json::number_float_t d = j; CHECK(std::isnan(d)); CHECK(j.dump() == "null"); @@ -1303,7 +1450,7 @@ TEST_CASE("CBOR") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected; @@ -1337,7 +1484,7 @@ TEST_CASE("CBOR") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected; @@ -1372,7 +1519,7 @@ TEST_CASE("CBOR") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector (hack: create string first) std::vector expected(N, 'x'); @@ -1405,7 +1552,7 @@ TEST_CASE("CBOR") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector (hack: create string first) std::vector expected(N, 'x'); @@ -1428,6 +1575,53 @@ TEST_CASE("CBOR") CHECK(json::from_cbor(result, true, false) == j); } } + + SECTION("indefinite size") + { + std::vector input = {0x5F, 0x44, 0xaa, 0xbb, 0xcc, 0xdd, 0x43, 0xee, 0xff, 0x99, 0xFF}; + auto j = json::from_cbor(input); + CHECK(j.is_binary()); + auto k = json::binary({0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x99}); + CAPTURE(j.dump(0, ' ', false, json::error_handler_t::strict)) + CHECK(j == k); + } + + SECTION("binary in array") + { + // array with three empty byte strings + std::vector input = {0x83, 0x40, 0x40, 0x40}; + CHECK_NOTHROW(json::from_cbor(input)); + } + + SECTION("binary in object") + { + // object mapping "foo" to empty byte string + std::vector input = {0xA1, 0x63, 0x66, 0x6F, 0x6F, 0x40}; + CHECK_NOTHROW(json::from_cbor(input)); + } + + SECTION("SAX callback with binary") + { + // object mapping "foo" to byte string + std::vector input = {0xA1, 0x63, 0x66, 0x6F, 0x6F, 0x41, 0x00}; + + // callback to set binary_seen to true if a binary value was seen + bool binary_seen = false; + auto callback = [&binary_seen](int /*depth*/, json::parse_event_t /*event*/, json & parsed) + { + if (parsed.is_binary()) + { + binary_seen = true; + } + return true; + }; + + json j; + auto cbp = nlohmann::detail::json_sax_dom_callback_parser(j, callback, true); + CHECK(json::sax_parse(input, &cbp, json::input_format_t::cbor)); + CHECK(j.at("foo").is_binary()); + CHECK(binary_seen); + } } } @@ -1439,7 +1633,7 @@ TEST_CASE("CBOR") 0x00, 0x00, 0x00, 0x01, 0x61 }; json j = json::from_cbor(given); - CHECK(j == json::binary_array(std::vector {'a'})); + CHECK(j == json::binary(std::vector {'a'})); } SECTION("0x7b (string)") @@ -1508,6 +1702,9 @@ TEST_CASE("CBOR") CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0xBF, 0x61, 0x61, 0xF5})), json::parse_error&); CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0xA1, 0x61, 0X61})), json::parse_error&); CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0xBF, 0x61, 0X61})), json::parse_error&); + CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x5F})), json::parse_error&); + CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x5F, 0x00})), json::parse_error&); + CHECK_THROWS_AS(_ = json::from_cbor(std::vector({0x41})), json::parse_error&); CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x18})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input"); @@ -1557,6 +1754,12 @@ TEST_CASE("CBOR") "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR value: unexpected end of input"); CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0xBF, 0x61, 0x61})), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR value: unexpected end of input"); + CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x5F})), + "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR binary: unexpected end of input"); + CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x5F, 0x00})), + "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR binary: expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x00"); + CHECK_THROWS_WITH(_ = json::from_cbor(std::vector({0x41})), + "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR binary: unexpected end of input"); CHECK(json::from_cbor(std::vector({0x18}), true, false).is_discarded()); CHECK(json::from_cbor(std::vector({0x19}), true, false).is_discarded()); @@ -1582,6 +1785,9 @@ TEST_CASE("CBOR") CHECK(json::from_cbor(std::vector({0xBF, 0x61, 0x61, 0xF5}), true, false).is_discarded()); CHECK(json::from_cbor(std::vector({0xA1, 0x61, 0x61}), true, false).is_discarded()); CHECK(json::from_cbor(std::vector({0xBF, 0x61, 0x61}), true, false).is_discarded()); + CHECK(json::from_cbor(std::vector({0x5F}), true, false).is_discarded()); + CHECK(json::from_cbor(std::vector({0x5F, 0x00}), true, false).is_discarded()); + CHECK(json::from_cbor(std::vector({0x41}), true, false).is_discarded()); } SECTION("unsupported bytes") @@ -2296,7 +2502,7 @@ TEST_CASE("examples from RFC 7049 Appendix A") std::ifstream f_bin(TEST_DATA_DIRECTORY "/binary_data/cbor_binary.out", std::ios::binary); std::vector expected((std::istreambuf_iterator(f_bin)), std::istreambuf_iterator()); - CHECK(j == json::binary_array(expected)); + CHECK(j == json::binary(expected)); } SECTION("arrays") diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp index 4aad6d7847..1ac5068bdb 100644 --- a/test/src/unit-class_lexer.cpp +++ b/test/src/unit-class_lexer.cpp @@ -40,7 +40,8 @@ namespace json::lexer::token_type scan_string(const char* s); json::lexer::token_type scan_string(const char* s) { - return json::lexer(nlohmann::detail::input_adapter(s)).scan(); + auto ia = nlohmann::detail::input_adapter(s); + return nlohmann::detail::lexer(std::move(ia)).scan(); } } diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp index 6f6b121ee0..d43099f264 100644 --- a/test/src/unit-class_parser.cpp +++ b/test/src/unit-class_parser.cpp @@ -77,7 +77,7 @@ class SaxEventLogger return true; } - bool binary(std::vector& val) + bool binary(json::binary_t& val) { std::string binary_contents = "binary("; std::string comma_space = ""; @@ -183,7 +183,7 @@ class SaxCountdown : public nlohmann::json::json_sax_t return events_left-- > 0; } - bool binary(std::vector&) override + bool binary(json::binary_t&) override { return events_left-- > 0; } diff --git a/test/src/unit-comparison.cpp b/test/src/unit-comparison.cpp index 6b27240b87..db46507cab 100644 --- a/test/src/unit-comparison.cpp +++ b/test/src/unit-comparison.cpp @@ -56,21 +56,23 @@ TEST_CASE("lexicographical comparison operators") json::value_t::number_float, json::value_t::object, json::value_t::array, - json::value_t::string + json::value_t::string, + json::value_t::binary }; SECTION("comparison: less") { std::vector> expected = { - {false, true, true, true, true, true, true, true}, - {false, false, true, true, true, true, true, true}, - {false, false, false, false, false, true, true, true}, - {false, false, false, false, false, true, true, true}, - {false, false, false, false, false, true, true, true}, - {false, false, false, false, false, false, true, true}, - {false, false, false, false, false, false, false, true}, - {false, false, false, false, false, false, false, false} + {false, true, true, true, true, true, true, true, true}, + {false, false, true, true, true, true, true, true, true}, + {false, false, false, false, false, true, true, true, true}, + {false, false, false, false, false, true, true, true, true}, + {false, false, false, false, false, true, true, true, true}, + {false, false, false, false, false, false, true, true, true}, + {false, false, false, false, false, false, false, true, true}, + {false, false, false, false, false, false, false, false, true}, + {false, false, false, false, false, false, false, false, false} }; for (size_t i = 0; i < j_types.size(); ++i) @@ -98,29 +100,32 @@ TEST_CASE("lexicographical comparison operators") "foo", "bar", true, false, {1, 2, 3}, {"one", "two", "three"}, - {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}} + {{"first", 1}, {"second", 2}}, {{"a", "A"}, {"b", {"B"}}}, + json::binary({1, 2, 3}), json::binary({1, 2, 4}) }; SECTION("comparison: equal") { std::vector> expected = { - {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true} + {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true} }; for (size_t i = 0; i < j_values.size(); ++i) @@ -176,22 +181,24 @@ TEST_CASE("lexicographical comparison operators") { std::vector> expected = { - {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, - {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, - {false, false, false, true, true, true, true, true, true, true, false, false, true, true, true, true}, - {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true}, - {false, false, false, true, false, true, false, true, true, true, false, false, true, true, true, true}, - {false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true}, - {false, false, false, true, true, true, false, true, true, true, false, false, true, true, true, true}, - {false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true}, - {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false}, - {false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true}, - {false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true}, - {false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false}, - {false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false}, - {false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false}, - {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false} + {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, + {false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, + {false, false, false, true, true, true, true, true, true, true, false, false, true, true, true, true, true, true}, + {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, true, true, true}, + {false, false, false, true, false, true, false, true, true, true, false, false, true, true, true, true, true, true}, + {false, false, false, true, false, false, false, true, true, true, false, false, true, true, true, true, true, true}, + {false, false, false, true, true, true, false, true, true, true, false, false, true, true, true, true, true, true}, + {false, false, false, true, false, false, false, false, true, true, false, false, true, true, true, true, true, true}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true}, + {false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, true, true}, + {false, false, true, true, true, true, true, true, true, true, false, false, true, true, true, true, true, true}, + {false, false, true, true, true, true, true, true, true, true, true, false, true, true, true, true, true, true}, + {false, false, false, false, false, false, false, false, true, true, false, false, false, true, false, false, true, true}, + {false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, true, true}, + {false, false, false, false, false, false, false, false, true, true, false, false, true, true, false, false, true, true}, + {false, false, false, false, false, false, false, false, true, true, false, false, true, true, true, false, true, true}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true}, + {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false} }; for (size_t i = 0; i < j_values.size(); ++i) diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 2caf265357..0240feef4d 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -115,6 +115,14 @@ TEST_CASE("constructors") CHECK(j.type() == t); CHECK(j == 0.0); } + + SECTION("binary") + { + auto t = json::value_t::binary; + json j(t); + CHECK(j.type() == t); + CHECK(j == json::binary({})); + } } SECTION("create a null object (implicitly)") @@ -473,6 +481,23 @@ TEST_CASE("constructors") } } + SECTION("create a binary (explicit)") + { + SECTION("empty binary") + { + json::binary_t b{}; + json j(b); + CHECK(j.type() == json::value_t::binary); + } + + SECTION("filled binary") + { + json::binary_t b({1, 2, 3}); + json j(b); + CHECK(j.type() == json::value_t::binary); + } + } + SECTION("create an integer number (explicit)") { SECTION("uninitialized value") @@ -1453,6 +1478,20 @@ TEST_CASE("constructors") CHECK(j == j_new); } } + + SECTION("binary") + { + { + json j = json::binary({1, 2, 3}); + json j_new(j.begin(), j.end()); + CHECK((j == j_new)); + } + { + json j = json::binary({1, 2, 3}); + json j_new(j.cbegin(), j.cend()); + CHECK((j == j_new)); + } + } } SECTION("construct with two invalid iterators") diff --git a/test/src/unit-constructor2.cpp b/test/src/unit-constructor2.cpp index 69429b0b3c..df20695cf3 100644 --- a/test/src/unit-constructor2.cpp +++ b/test/src/unit-constructor2.cpp @@ -91,6 +91,13 @@ TEST_CASE("other constructors and destructor") json k(j); CHECK(j == k); } + + SECTION("binary") + { + json j = json::binary({1, 2, 3}); + json k(j); + CHECK(j == k); + } } SECTION("move constructor") @@ -167,6 +174,14 @@ TEST_CASE("other constructors and destructor") k = j; CHECK(j == k); } + + SECTION("binary") + { + json j = json::binary({1, 2, 3}); + json k; + k = j; + CHECK(j == k); + } } SECTION("destructor") diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index 1c61fae149..b36d9dfea9 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -58,6 +58,7 @@ TEST_CASE("convenience functions") CHECK(std::string(json(json::value_t::number_integer).type_name()) == "number"); CHECK(std::string(json(json::value_t::number_unsigned).type_name()) == "number"); CHECK(std::string(json(json::value_t::number_float).type_name()) == "number"); + CHECK(std::string(json(json::value_t::binary).type_name()) == "binary"); CHECK(std::string(json(json::value_t::boolean).type_name()) == "boolean"); CHECK(std::string(json(json::value_t::string).type_name()) == "string"); CHECK(std::string(json(json::value_t::discarded).type_name()) == "discarded"); diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 8477cf6392..d37e8e9b23 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -189,7 +189,6 @@ TEST_CASE("value conversion") } } - SECTION("get an object (implicit)") { json::object_t o_reference = {{"object", json::object()}, @@ -1259,6 +1258,124 @@ TEST_CASE("value conversion") } } + SECTION("get a binary value (explicit)") + { + json::binary_t n_reference{{1, 2, 3}}; + json j(n_reference); + + SECTION("binary_t") + { + json::binary_t b = j.get(); + CHECK(*json(b).m_value.binary == *j.m_value.binary); + } + + SECTION("get_binary()") + { + SECTION("non-const") + { + auto& b = j.get_binary(); + CHECK(*json(b).m_value.binary == *j.m_value.binary); + } + + SECTION("non-const") + { + const json j_const = j; + const auto& b = j_const.get_binary(); + CHECK(*json(b).m_value.binary == *j.m_value.binary); + } + } + + SECTION("exception in case of a non-string type") + { + json j_null(json::value_t::null); + json j_object(json::value_t::object); + json j_array(json::value_t::array); + json j_string(json::value_t::string); + json j_boolean(json::value_t::boolean); + const json j_null_const(json::value_t::null); + const json j_object_const(json::value_t::object); + const json j_array_const(json::value_t::array); + const json j_string_const(json::value_t::string); + const json j_boolean_const(json::value_t::boolean); + + CHECK_THROWS_WITH_AS(j_null.get(), + "[json.exception.type_error.302] type must be binary, but is null", + json::type_error&); + CHECK_THROWS_WITH_AS(j_object.get(), + "[json.exception.type_error.302] type must be binary, but is object", + json::type_error&); + CHECK_THROWS_WITH_AS(j_array.get(), + "[json.exception.type_error.302] type must be binary, but is array", + json::type_error&); + CHECK_THROWS_WITH_AS(j_string.get(), + "[json.exception.type_error.302] type must be binary, but is string", + json::type_error&); + CHECK_THROWS_WITH_AS(j_boolean.get(), + "[json.exception.type_error.302] type must be binary, but is boolean", + json::type_error&); + + CHECK_THROWS_WITH_AS(j_null_const.get(), + "[json.exception.type_error.302] type must be binary, but is null", + json::type_error&); + CHECK_THROWS_WITH_AS(j_object_const.get(), + "[json.exception.type_error.302] type must be binary, but is object", + json::type_error&); + CHECK_THROWS_WITH_AS(j_array_const.get(), + "[json.exception.type_error.302] type must be binary, but is array", + json::type_error&); + CHECK_THROWS_WITH_AS(j_string_const.get(), + "[json.exception.type_error.302] type must be binary, but is string", + json::type_error&); + CHECK_THROWS_WITH_AS(j_boolean_const.get(), + "[json.exception.type_error.302] type must be binary, but is boolean", + json::type_error&); + + CHECK_THROWS_WITH_AS(j_null.get_binary(), + "[json.exception.type_error.302] type must be binary, but is null", + json::type_error&); + CHECK_THROWS_WITH_AS(j_object.get_binary(), + "[json.exception.type_error.302] type must be binary, but is object", + json::type_error&); + CHECK_THROWS_WITH_AS(j_array.get_binary(), + "[json.exception.type_error.302] type must be binary, but is array", + json::type_error&); + CHECK_THROWS_WITH_AS(j_string.get_binary(), + "[json.exception.type_error.302] type must be binary, but is string", + json::type_error&); + CHECK_THROWS_WITH_AS(j_boolean.get_binary(), + "[json.exception.type_error.302] type must be binary, but is boolean", + json::type_error&); + + CHECK_THROWS_WITH_AS(j_null_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is null", + json::type_error&); + CHECK_THROWS_WITH_AS(j_object_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is object", + json::type_error&); + CHECK_THROWS_WITH_AS(j_array_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is array", + json::type_error&); + CHECK_THROWS_WITH_AS(j_string_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is string", + json::type_error&); + CHECK_THROWS_WITH_AS(j_boolean_const.get_binary(), + "[json.exception.type_error.302] type must be binary, but is boolean", + json::type_error&); + } + } + + SECTION("get a binary value (implicit)") + { + json::binary_t n_reference{{1, 2, 3}}; + json j(n_reference); + + SECTION("binary_t") + { + json::binary_t b = j; + CHECK(*json(b).m_value.binary == *j.m_value.binary); + } + } + SECTION("get an enum") { enum c_enum { value_1, value_2 }; diff --git a/test/src/unit-deserialization.cpp b/test/src/unit-deserialization.cpp index a868871540..15744b9eb7 100644 --- a/test/src/unit-deserialization.cpp +++ b/test/src/unit-deserialization.cpp @@ -76,7 +76,7 @@ struct SaxEventLogger : public nlohmann::json_sax return true; } - bool binary(std::vector& val) override + bool binary(json::binary_t& val) override { std::string binary_contents = "binary("; std::string comma_space = ""; diff --git a/test/src/unit-element_access1.cpp b/test/src/unit-element_access1.cpp index ebec93284c..a0a18fecb8 100644 --- a/test/src/unit-element_access1.cpp +++ b/test/src/unit-element_access1.cpp @@ -694,6 +694,22 @@ TEST_CASE("element access 1") CHECK(it == j.end()); } } + + SECTION("binary") + { + { + json j = json::binary({1, 2, 3}); + json::iterator it = j.erase(j.begin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = json::binary({1, 2, 3}); + json::const_iterator it = j.erase(j.cbegin()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } } SECTION("erase with one invalid iterator") @@ -876,6 +892,22 @@ TEST_CASE("element access 1") CHECK(it == j.end()); } } + + SECTION("binary") + { + { + json j = json::binary({1, 2, 3}); + json::iterator it = j.erase(j.begin(), j.end()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + { + json j = json::binary({1, 2, 3}); + json::const_iterator it = j.erase(j.cbegin(), j.cend()); + CHECK(j.type() == json::value_t::null); + CHECK(it == j.end()); + } + } } SECTION("erase with two invalid iterators") diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp index 04a59e53f8..a5f4519b61 100644 --- a/test/src/unit-inspection.cpp +++ b/test/src/unit-inspection.cpp @@ -49,6 +49,7 @@ TEST_CASE("object inspection") CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); + CHECK(not j.is_binary()); CHECK(j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); @@ -66,6 +67,7 @@ TEST_CASE("object inspection") CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); + CHECK(not j.is_binary()); CHECK(not j.is_object()); CHECK(j.is_array()); CHECK(not j.is_string()); @@ -83,6 +85,7 @@ TEST_CASE("object inspection") CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); + CHECK(not j.is_binary()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); @@ -100,6 +103,7 @@ TEST_CASE("object inspection") CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); + CHECK(not j.is_binary()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); @@ -117,6 +121,7 @@ TEST_CASE("object inspection") CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); + CHECK(not j.is_binary()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(j.is_string()); @@ -134,6 +139,7 @@ TEST_CASE("object inspection") CHECK(j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); + CHECK(not j.is_binary()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); @@ -151,6 +157,7 @@ TEST_CASE("object inspection") CHECK(j.is_number_integer()); CHECK(j.is_number_unsigned()); CHECK(not j.is_number_float()); + CHECK(not j.is_binary()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); @@ -168,6 +175,25 @@ TEST_CASE("object inspection") CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(j.is_number_float()); + CHECK(not j.is_binary()); + CHECK(not j.is_object()); + CHECK(not j.is_array()); + CHECK(not j.is_string()); + CHECK(not j.is_discarded()); + CHECK(j.is_primitive()); + CHECK(not j.is_structured()); + } + + SECTION("binary") + { + json j(json::value_t::binary); + CHECK(not j.is_null()); + CHECK(not j.is_boolean()); + CHECK(not j.is_number()); + CHECK(not j.is_number_integer()); + CHECK(not j.is_number_unsigned()); + CHECK(not j.is_number_float()); + CHECK(j.is_binary()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); @@ -185,6 +211,7 @@ TEST_CASE("object inspection") CHECK(not j.is_number_integer()); CHECK(not j.is_number_unsigned()); CHECK(not j.is_number_float()); + CHECK(not j.is_binary()); CHECK(not j.is_object()); CHECK(not j.is_array()); CHECK(not j.is_string()); @@ -234,6 +261,9 @@ TEST_CASE("object inspection") // important test, because it yields a resize of the indent_string // inside the dump() function CHECK(j.dump(1024).size() == 15472); + + const auto binary = json::binary({1, 2, 3}, 128); + CHECK(binary.dump(1024).size() == 2086); } SECTION("dump and floating-point numbers") @@ -439,5 +469,12 @@ TEST_CASE("object inspection") json::value_t t = j; CHECK(t == j.type()); } + + SECTION("binary") + { + json j = json::binary({}); + json::value_t t = j; + CHECK(t == j.type()); + } } } diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp index 2cc9dac6a5..1e68bc83e4 100644 --- a/test/src/unit-json_pointer.cpp +++ b/test/src/unit-json_pointer.cpp @@ -101,6 +101,9 @@ TEST_CASE("JSON pointers") CHECK(j["/foo/1"_json_pointer] == j["foo"][1]); CHECK(j.contains(json::json_pointer("/foo/0"))); CHECK(j.contains(json::json_pointer("/foo/1"))); + CHECK(not j.contains(json::json_pointer("/foo/3"))); + CHECK(not j.contains(json::json_pointer("/foo/+"))); + CHECK(not j.contains(json::json_pointer("/foo/1+2"))); CHECK(not j.contains(json::json_pointer("/foo/-"))); // checked array access diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index c4073801ca..9214c60822 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -106,6 +106,31 @@ TEST_CASE("modifiers") } } + SECTION("binary") + { + SECTION("empty binary") + { + json j = json::binary({}); + json k = j; + + j.clear(); + CHECK(not j.empty()); + CHECK(j == json(json::value_t::binary)); + CHECK(j == json(k.type())); + } + + SECTION("filled binary") + { + json j = json::binary({1, 2, 3, 4, 5}); + json k = j; + + j.clear(); + CHECK(not j.empty()); + CHECK(j == json(json::value_t::binary)); + CHECK(j == json(k.type())); + } + } + SECTION("number (integer)") { json j = 23; @@ -937,5 +962,46 @@ TEST_CASE("modifiers") CHECK_THROWS_WITH(j.swap(s), "[json.exception.type_error.310] cannot use swap() with number"); } } + + SECTION("binary_t") + { + SECTION("binary_t type") + { + json j = json::binary({1, 2, 3, 4}); + json::binary_t s = {{5, 6, 7, 8}}; + + j.swap(s); + + CHECK(j == json::binary({5, 6, 7, 8})); + + j.swap(s); + + CHECK(j == json::binary({1, 2, 3, 4})); + } + + SECTION("binary_t::container_type type") + { + json j = json::binary({1, 2, 3, 4}); + std::vector s = {{5, 6, 7, 8}}; + + j.swap(s); + + CHECK(j == json::binary({5, 6, 7, 8})); + + j.swap(s); + + CHECK(j == json::binary({1, 2, 3, 4})); + } + + SECTION("non-binary_t type") + { + json j = 17; + json::binary_t s1 = {{1, 2, 3, 4}}; + std::vector s2 = {{5, 6, 7, 8}}; + + CHECK_THROWS_WITH_AS(j.swap(s1), "[json.exception.type_error.310] cannot use swap() with number", json::type_error); + CHECK_THROWS_WITH_AS(j.swap(s2), "[json.exception.type_error.310] cannot use swap() with number", json::type_error); + } + } } } diff --git a/test/src/unit-msgpack.cpp b/test/src/unit-msgpack.cpp index cf5b19e795..2fb89533b2 100644 --- a/test/src/unit-msgpack.cpp +++ b/test/src/unit-msgpack.cpp @@ -1132,9 +1132,9 @@ TEST_CASE("MessagePack") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); std::uint8_t subtype = 42; - j.set_subtype(subtype); + j.get_binary().set_subtype(subtype); // create expected byte vector std::vector expected; @@ -1207,9 +1207,9 @@ TEST_CASE("MessagePack") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); std::uint8_t subtype = 42; - j.set_subtype(subtype); + j.get_binary().set_subtype(subtype); // create expected byte vector (hack: create string first) std::vector expected(N, 'x'); @@ -1243,9 +1243,9 @@ TEST_CASE("MessagePack") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); std::uint8_t subtype = 42; - j.set_subtype(subtype); + j.get_binary().set_subtype(subtype); // create expected byte vector (hack: create string first) std::vector expected(N, 'x'); @@ -1281,7 +1281,7 @@ TEST_CASE("MessagePack") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected; @@ -1319,7 +1319,7 @@ TEST_CASE("MessagePack") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector (hack: create string first) std::vector expected(N, 'x'); @@ -1352,7 +1352,7 @@ TEST_CASE("MessagePack") // create JSON value with string containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector (hack: create string first) std::vector expected(N, 'x'); @@ -1418,6 +1418,7 @@ TEST_CASE("MessagePack") CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xa5, 0x68, 0x65})), json::parse_error&); CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0x92, 0x01})), json::parse_error&); CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0x81, 0xa1, 0x61})), json::parse_error&); + CHECK_THROWS_AS(_ = json::from_msgpack(std::vector({0xc4, 0x02})), json::parse_error&); CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0x87})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack string: unexpected end of input"); @@ -1457,6 +1458,8 @@ TEST_CASE("MessagePack") "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing MessagePack value: unexpected end of input"); CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0x81, 0xa1, 0x61})), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack value: unexpected end of input"); + CHECK_THROWS_WITH(_ = json::from_msgpack(std::vector({0xc4, 0x02})), + "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing MessagePack binary: unexpected end of input"); CHECK(json::from_msgpack(std::vector({0x87}), true, false).is_discarded()); CHECK(json::from_msgpack(std::vector({0xcc}), true, false).is_discarded()); @@ -1477,6 +1480,8 @@ TEST_CASE("MessagePack") CHECK(json::from_msgpack(std::vector({0xa5, 0x68, 0x65}), true, false).is_discarded()); CHECK(json::from_msgpack(std::vector({0x92, 0x01}), true, false).is_discarded()); CHECK(json::from_msgpack(std::vector({0x81, 0xA1, 0x61}), true, false).is_discarded()); + CHECK(json::from_msgpack(std::vector({0xc4, 0x02}), true, false).is_discarded()); + CHECK(json::from_msgpack(std::vector({0xc4}), true, false).is_discarded()); } SECTION("unsupported bytes") diff --git a/test/src/unit-noexcept.cpp b/test/src/unit-noexcept.cpp index 8fbe9f9165..6952c0ee81 100644 --- a/test/src/unit-noexcept.cpp +++ b/test/src/unit-noexcept.cpp @@ -46,7 +46,12 @@ void to_json(json&, pod) noexcept; void to_json(json&, pod_bis); void from_json(const json&, pod) noexcept; void from_json(const json&, pod_bis); -static json* j; +void to_json(json&, pod) noexcept {} +void to_json(json&, pod_bis) {} +void from_json(const json&, pod) noexcept {} +void from_json(const json&, pod_bis) {} + +static json* j = nullptr; static_assert(noexcept(json{}), ""); static_assert(noexcept(nlohmann::to_json(*j, 2)), ""); @@ -79,4 +84,14 @@ TEST_CASE("runtime checks") CHECK(std::is_nothrow_copy_constructible::value == std::is_nothrow_copy_constructible::value); CHECK(std::is_nothrow_copy_constructible::value == std::is_nothrow_copy_constructible::value); } + + SECTION("silence -Wunneeded-internal-declaration errors") + { + j = nullptr; + json j2; + to_json(j2, pod()); + to_json(j2, pod_bis()); + from_json(j2, pod()); + from_json(j2, pod_bis()); + } } diff --git a/test/src/unit-pointer_access.cpp b/test/src/unit-pointer_access.cpp index 778bef7ba2..36a052498f 100644 --- a/test/src/unit-pointer_access.cpp +++ b/test/src/unit-pointer_access.cpp @@ -34,22 +34,6 @@ using nlohmann::json; TEST_CASE("pointer access") { - // create a JSON value with different types - json json_types = - { - {"boolean", true}, - { - "number", { - {"integer", 42}, - {"unsigned", 42u}, - {"floating-point", 17.23} - } - }, - {"string", "Hello, world!"}, - {"array", {1, 2, 3, 4, 5}}, - {"null", nullptr} - }; - SECTION("pointer access to object_t") { using test_type = json::object_t; @@ -61,11 +45,11 @@ TEST_CASE("pointer access") CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly @@ -76,6 +60,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const object_t") @@ -89,11 +74,11 @@ TEST_CASE("pointer access") CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly @@ -104,6 +89,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to array_t") @@ -117,11 +103,11 @@ TEST_CASE("pointer access") CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly @@ -132,6 +118,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const array_t") @@ -145,11 +132,11 @@ TEST_CASE("pointer access") CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly @@ -160,6 +147,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to string_t") @@ -173,11 +161,11 @@ TEST_CASE("pointer access") CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly @@ -188,6 +176,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const string_t") @@ -201,11 +190,11 @@ TEST_CASE("pointer access") CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly @@ -216,6 +205,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to boolean_t") @@ -229,11 +219,11 @@ TEST_CASE("pointer access") CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly @@ -244,6 +234,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const boolean_t") @@ -257,11 +248,11 @@ TEST_CASE("pointer access") //CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly @@ -272,6 +263,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to number_integer_t") @@ -285,11 +277,11 @@ TEST_CASE("pointer access") CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly @@ -300,6 +292,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const number_integer_t") @@ -313,11 +306,11 @@ TEST_CASE("pointer access") CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly @@ -328,6 +321,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to number_unsigned_t") @@ -341,11 +335,11 @@ TEST_CASE("pointer access") CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly @@ -356,6 +350,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const number_unsigned_t") @@ -369,11 +364,11 @@ TEST_CASE("pointer access") CHECK(*p1 == value.get()); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == value.get()); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == value.get()); // check if null pointers are returned correctly @@ -384,6 +379,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() != nullptr); CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to number_float_t") @@ -397,11 +393,11 @@ TEST_CASE("pointer access") CHECK(*p1 == Approx(value.get())); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == Approx(value.get())); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == Approx(value.get())); // check if null pointers are returned correctly @@ -412,6 +408,7 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); } SECTION("pointer access to const number_float_t") @@ -425,11 +422,11 @@ TEST_CASE("pointer access") CHECK(*p1 == Approx(value.get())); const test_type* p2 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p2 == value.get_ptr()); CHECK(*p2 == Approx(value.get())); const test_type* const p3 = value.get_ptr(); - CHECK(p1 == value.get_ptr()); + CHECK(p3 == value.get_ptr()); CHECK(*p3 == Approx(value.get())); // check if null pointers are returned correctly @@ -440,5 +437,64 @@ TEST_CASE("pointer access") CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() == nullptr); CHECK(value.get_ptr() != nullptr); + CHECK(value.get_ptr() == nullptr); + } + + SECTION("pointer access to const binary_t") + { + using test_type = const json::binary_t; + const json value = json::binary({1, 2, 3}); + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p2 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p3 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); + } + + SECTION("pointer access to const binary_t") + { + using test_type = const json::binary_t; + const json value = json::binary({}); + + // check if pointers are returned correctly + test_type* p1 = value.get_ptr(); + CHECK(p1 == value.get_ptr()); + CHECK(*p1 == value.get()); + + const test_type* p2 = value.get_ptr(); + CHECK(p2 == value.get_ptr()); + CHECK(*p2 == value.get()); + + const test_type* const p3 = value.get_ptr(); + CHECK(p3 == value.get_ptr()); + CHECK(*p3 == value.get()); + + // check if null pointers are returned correctly + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() == nullptr); + CHECK(value.get_ptr() != nullptr); } } diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 1fb372f556..373344d8d2 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -464,6 +464,11 @@ TEST_CASE("regression tests") s2 = o["name"]; CHECK(s2 == "value"); + + // improve coverage + o["int"] = 1; + CHECK_THROWS_AS(s2 = o["int"], json::type_error); + CHECK_THROWS_WITH(s2 = o["int"], "[json.exception.type_error.302] type must be string, but is number"); } SECTION("issue #146 - character following a surrogate pair is skipped") @@ -1914,8 +1919,7 @@ TEST_CASE("regression tests") j.dump(4, // Indent ' ', // Indent char false, // Ensure ascii - json::error_handler_t::strict, // Error - true // Allow binary data + json::error_handler_t::strict // Error ) ); } diff --git a/test/src/unit-serialization.cpp b/test/src/unit-serialization.cpp index dc8175ef6b..067d44c67c 100644 --- a/test/src/unit-serialization.cpp +++ b/test/src/unit-serialization.cpp @@ -206,3 +206,117 @@ TEST_CASE_TEMPLATE("serialization for extreme integer values", T, int32_t, uint3 CHECK(j.dump() == std::to_string(maximum)); } } + +TEST_CASE("dump with binary values") +{ + auto binary = json::binary({1, 2, 3, 4}); + auto binary_empty = json::binary({}); + auto binary_with_subtype = json::binary({1, 2, 3, 4}, 128); + auto binary_empty_with_subtype = json::binary({}, 128); + + json object = {{"key", binary}}; + json object_empty = {{"key", binary_empty}}; + json object_with_subtype = {{"key", binary_with_subtype}}; + json object_empty_with_subtype = {{"key", binary_empty_with_subtype}}; + + json array = {"value", 1, binary}; + json array_empty = {"value", 1, binary_empty}; + json array_with_subtype = {"value", 1, binary_with_subtype}; + json array_empty_with_subtype = {"value", 1, binary_empty_with_subtype}; + + SECTION("normal") + { + CHECK(binary.dump() == "{\"bytes\":[1,2,3,4],\"subtype\":null}"); + CHECK(binary_empty.dump() == "{\"bytes\":[],\"subtype\":null}"); + CHECK(binary_with_subtype.dump() == "{\"bytes\":[1,2,3,4],\"subtype\":128}"); + CHECK(binary_empty_with_subtype.dump() == "{\"bytes\":[],\"subtype\":128}"); + + CHECK(object.dump() == "{\"key\":{\"bytes\":[1,2,3,4],\"subtype\":null}}"); + CHECK(object_empty.dump() == "{\"key\":{\"bytes\":[],\"subtype\":null}}"); + CHECK(object_with_subtype.dump() == "{\"key\":{\"bytes\":[1,2,3,4],\"subtype\":128}}"); + CHECK(object_empty_with_subtype.dump() == "{\"key\":{\"bytes\":[],\"subtype\":128}}"); + + CHECK(array.dump() == "[\"value\",1,{\"bytes\":[1,2,3,4],\"subtype\":null}]"); + CHECK(array_empty.dump() == "[\"value\",1,{\"bytes\":[],\"subtype\":null}]"); + CHECK(array_with_subtype.dump() == "[\"value\",1,{\"bytes\":[1,2,3,4],\"subtype\":128}]"); + CHECK(array_empty_with_subtype.dump() == "[\"value\",1,{\"bytes\":[],\"subtype\":128}]"); + } + + SECTION("pretty-printed") + { + CHECK(binary.dump(4) == "{\n" + " \"bytes\": [1, 2, 3, 4],\n" + " \"subtype\": null\n" + "}"); + CHECK(binary_empty.dump(4) == "{\n" + " \"bytes\": [],\n" + " \"subtype\": null\n" + "}"); + CHECK(binary_with_subtype.dump(4) == "{\n" + " \"bytes\": [1, 2, 3, 4],\n" + " \"subtype\": 128\n" + "}"); + CHECK(binary_empty_with_subtype.dump(4) == "{\n" + " \"bytes\": [],\n" + " \"subtype\": 128\n" + "}"); + + CHECK(object.dump(4) == "{\n" + " \"key\": {\n" + " \"bytes\": [1, 2, 3, 4],\n" + " \"subtype\": null\n" + " }\n" + "}"); + CHECK(object_empty.dump(4) == "{\n" + " \"key\": {\n" + " \"bytes\": [],\n" + " \"subtype\": null\n" + " }\n" + "}"); + CHECK(object_with_subtype.dump(4) == "{\n" + " \"key\": {\n" + " \"bytes\": [1, 2, 3, 4],\n" + " \"subtype\": 128\n" + " }\n" + "}"); + CHECK(object_empty_with_subtype.dump(4) == "{\n" + " \"key\": {\n" + " \"bytes\": [],\n" + " \"subtype\": 128\n" + " }\n" + "}"); + + CHECK(array.dump(4) == "[\n" + " \"value\",\n" + " 1,\n" + " {\n" + " \"bytes\": [1, 2, 3, 4],\n" + " \"subtype\": null\n" + " }\n" + "]"); + CHECK(array_empty.dump(4) == "[\n" + " \"value\",\n" + " 1,\n" + " {\n" + " \"bytes\": [],\n" + " \"subtype\": null\n" + " }\n" + "]"); + CHECK(array_with_subtype.dump(4) == "[\n" + " \"value\",\n" + " 1,\n" + " {\n" + " \"bytes\": [1, 2, 3, 4],\n" + " \"subtype\": 128\n" + " }\n" + "]"); + CHECK(array_empty_with_subtype.dump(4) == "[\n" + " \"value\",\n" + " 1,\n" + " {\n" + " \"bytes\": [],\n" + " \"subtype\": 128\n" + " }\n" + "]"); + } +} diff --git a/test/src/unit-ubjson.cpp b/test/src/unit-ubjson.cpp index e3395bf226..d6dde7ad7b 100644 --- a/test/src/unit-ubjson.cpp +++ b/test/src/unit-ubjson.cpp @@ -921,7 +921,7 @@ TEST_CASE("UBJSON") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected; @@ -972,7 +972,7 @@ TEST_CASE("UBJSON") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected; @@ -1012,7 +1012,7 @@ TEST_CASE("UBJSON") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected(N + 7, 'x'); @@ -1049,7 +1049,7 @@ TEST_CASE("UBJSON") // create JSON value with byte array containing of N * 'x' const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); // create expected byte vector std::vector expected(N + 9, 'x'); @@ -1081,7 +1081,7 @@ TEST_CASE("UBJSON") { const std::size_t N = 10; const auto s = std::vector(N, 'x'); - json j = json::binary_array(s); + json j = json::binary(s); SECTION("No Count No Type") { diff --git a/test/src/unit-udt.cpp b/test/src/unit-udt.cpp index b1da8c3743..2040899edb 100644 --- a/test/src/unit-udt.cpp +++ b/test/src/unit-udt.cpp @@ -120,6 +120,8 @@ static void to_json(BasicJsonType& j, country c) case country::russia: j = u8"Российская Федерация"; return; + default: + break; } } @@ -761,6 +763,16 @@ TEST_CASE("different basic_json types conversions") CHECK(cj == "forty-two"); } + SECTION("binary") + { + json j = json::binary({1, 2, 3}, 42); + custom_json cj = j; + CHECK(cj.get_binary().subtype() == 42); + std::vector cv = cj.get_binary(); + std::vector v = j.get_binary(); + CHECK(cv == v); + } + SECTION("object") { json j = {{"forty", "two"}}; @@ -803,7 +815,9 @@ class Evil public: Evil() = default; template - Evil(T) {} + Evil(T t) : m_i(sizeof(t)) {} + + int m_i = 0; }; void from_json(const json&, Evil&) {} @@ -816,6 +830,10 @@ TEST_CASE("Issue #924") CHECK_NOTHROW(j.get()); CHECK_NOTHROW(j.get>()); + + // silence Wunused-template warnings + Evil e(1); + CHECK(e.m_i >= 0); } TEST_CASE("Issue #1237") diff --git a/test/thirdparty/doctest/doctest.h b/test/thirdparty/doctest/doctest.h index 4f3a0e330c..2f0ff2131f 100755 --- a/test/thirdparty/doctest/doctest.h +++ b/test/thirdparty/doctest/doctest.h @@ -2913,7 +2913,7 @@ typedef timer_large_integer::type ticks_t; //unsigned int getElapsedMilliseconds() const { // return static_cast(getElapsedMicroseconds() / 1000); //} - double getElapsedSeconds() const { return (getCurrentTicks() - m_ticks) / 1000000.0; } + double getElapsedSeconds() const { return static_cast((getCurrentTicks() - m_ticks)) / 1000000.0; } private: ticks_t m_ticks = 0;