diff --git a/eng/pipelines/templates/jobs/archetype-sdk-client.yml b/eng/pipelines/templates/jobs/archetype-sdk-client.yml index 5682ab435..c99b3aa0f 100644 --- a/eng/pipelines/templates/jobs/archetype-sdk-client.yml +++ b/eng/pipelines/templates/jobs/archetype-sdk-client.yml @@ -32,7 +32,7 @@ jobs: Linux_x64_gcc8: Pool: $(LinuxPool) OSVmImage: - VcpkgInstall: 'curl[ssl] libxml2 openssl nlohmann-json' + VcpkgInstall: 'curl[ssl] libxml2 openssl' VCPKG_DEFAULT_TRIPLET: 'x64-linux' CC: '/usr/bin/gcc-8' CXX: '/usr/bin/g++-8' @@ -40,7 +40,7 @@ jobs: Linux_x64_gcc9: Pool: $(LinuxPool) OSVmImage: - VcpkgInstall: 'curl[ssl] libxml2 openssl nlohmann-json' + VcpkgInstall: 'curl[ssl] libxml2 openssl' VCPKG_DEFAULT_TRIPLET: 'x64-linux' CC: '/usr/bin/gcc-9' CXX: '/usr/bin/g++-9' @@ -48,13 +48,13 @@ jobs: Linux_x64: Pool: $(LinuxPool) OSVmImage: - VcpkgInstall: 'curl[ssl] libxml2 openssl nlohmann-json' + VcpkgInstall: 'curl[ssl] libxml2 openssl' VCPKG_DEFAULT_TRIPLET: 'x64-linux' BuildArgs: '-j 10' Win_x86: Pool: $(WindowsPool) OSVmImage: - VcpkgInstall: 'curl[winssl] libxml2 nlohmann-json' + VcpkgInstall: 'curl[winssl] libxml2' VCPKG_DEFAULT_TRIPLET: 'x86-windows-static' CMAKE_GENERATOR: 'Visual Studio 16 2019' CMAKE_GENERATOR_PLATFORM: Win32 @@ -63,7 +63,7 @@ jobs: Win_x64: Pool: $(WindowsPool) OSVmImage: - VcpkgInstall: 'curl[winssl] libxml2 nlohmann-json' + VcpkgInstall: 'curl[winssl] libxml2' VCPKG_DEFAULT_TRIPLET: 'x64-windows-static' CMAKE_GENERATOR: 'Visual Studio 16 2019' CMAKE_GENERATOR_PLATFORM: x64 @@ -71,7 +71,7 @@ jobs: MacOS_x64: Pool: OSVmImage: 'macOS-10.14' - VcpkgInstall: 'curl[ssl] libxml2 openssl nlohmann-json' + VcpkgInstall: 'curl[ssl] libxml2 openssl' VCPKG_DEFAULT_TRIPLET: 'x64-osx' CHECK_CLANG_FORMAT: 1 @@ -79,7 +79,7 @@ jobs: Linux_x64_with_unit_test: Pool: $(LinuxPool) OSVmImage: - VcpkgInstall: 'curl[ssl] libxml2 openssl nlohmann-json' + VcpkgInstall: 'curl[ssl] libxml2 openssl' VCPKG_DEFAULT_TRIPLET: 'x64-linux' CmakeArgs: ' -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -DBUILD_CODE_COVERAGE=ON' AptDependencies: 'gcovr lcov' @@ -90,7 +90,7 @@ jobs: Win_x86_with_unit_test: Pool: $(WindowsPool) OSVmImage: - VcpkgInstall: 'curl[winssl] libxml2 nlohmann-json' + VcpkgInstall: 'curl[winssl] libxml2' VCPKG_DEFAULT_TRIPLET: 'x86-windows-static' CMAKE_GENERATOR: 'Visual Studio 16 2019' CMAKE_GENERATOR_PLATFORM: Win32 @@ -99,7 +99,7 @@ jobs: Win_x64_with_unit_test: Pool: $(WindowsPool) OSVmImage: - VcpkgInstall: 'curl[winssl] libxml2 nlohmann-json' + VcpkgInstall: 'curl[winssl] libxml2' VCPKG_DEFAULT_TRIPLET: 'x64-windows-static' CMAKE_GENERATOR: 'Visual Studio 16 2019' CMAKE_GENERATOR_PLATFORM: x64 @@ -108,7 +108,7 @@ jobs: MacOS_x64_with_unit_test: Pool: OSVmImage: 'macOS-10.14' - VcpkgInstall: 'curl[ssl] libxml2 openssl nlohmann-json' + VcpkgInstall: 'curl[ssl] libxml2 openssl' VCPKG_DEFAULT_TRIPLET: 'x64-osx' CmakeArgs: ' -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DBUILD_TRANSPORT_CURL=ON' pool: @@ -204,7 +204,7 @@ jobs: pool: name: ${{ parameters.WindowsPool }} variables: - VcpkgDependencies: curl[winssl] libxml2 nlohmann-json + VcpkgDependencies: curl[winssl] libxml2 VCPKG_DEFAULT_TRIPLET: 'x64-windows-static' steps: - template: /eng/common/pipelines/templates/steps/verify-links.yml diff --git a/eng/pipelines/templates/jobs/archetype-sdk-tests.yml b/eng/pipelines/templates/jobs/archetype-sdk-tests.yml index 331a995e0..2eb926a96 100644 --- a/eng/pipelines/templates/jobs/archetype-sdk-tests.yml +++ b/eng/pipelines/templates/jobs/archetype-sdk-tests.yml @@ -25,7 +25,7 @@ jobs: matrix: Linux_x64_with_unit_test: OSVmImage: 'ubuntu-18.04' - VcpkgInstall: 'curl[ssl] libxml2 openssl nlohmann-json' + VcpkgInstall: 'curl[ssl] libxml2 openssl' VCPKG_DEFAULT_TRIPLET: 'x64-linux' CmakeArgs: ' -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -DBUILD_CODE_COVERAGE=ON' AptDependencies: 'gcovr lcov' @@ -35,7 +35,7 @@ jobs: # Not asking for any transport adapter will default to OS -> windows:winHttp or !windows:libcurl Win_x86_with_unit_test_winHttp: OSVmImage: 'windows-2019' - VcpkgInstall: 'libxml2 nlohmann-json' + VcpkgInstall: 'libxml2' VCPKG_DEFAULT_TRIPLET: 'x86-windows-static' CMAKE_GENERATOR: 'Visual Studio 16 2019' CMAKE_GENERATOR_PLATFORM: Win32 @@ -43,7 +43,7 @@ jobs: BuildArgs: '-v --parallel 8' Win_x64_with_unit_test_winHttp: OSVmImage: 'windows-2019' - VcpkgInstall: 'libxml2 nlohmann-json' + VcpkgInstall: 'libxml2' VCPKG_DEFAULT_TRIPLET: 'x64-windows-static' CMAKE_GENERATOR: 'Visual Studio 16 2019' CMAKE_GENERATOR_PLATFORM: x64 @@ -52,7 +52,7 @@ jobs: # specify libcurl to be used on Windows Win_x86_with_unit_test_libcurl: OSVmImage: 'windows-2019' - VcpkgInstall: 'curl[winssl] libxml2 nlohmann-json' + VcpkgInstall: 'curl[winssl] libxml2' VCPKG_DEFAULT_TRIPLET: 'x86-windows-static' CMAKE_GENERATOR: 'Visual Studio 16 2019' CMAKE_GENERATOR_PLATFORM: Win32 @@ -60,7 +60,7 @@ jobs: BuildArgs: '-v --parallel 8' Win_x64_with_unit_test_libcurl: OSVmImage: 'windows-2019' - VcpkgInstall: 'curl[winssl] libxml2 nlohmann-json' + VcpkgInstall: 'curl[winssl] libxml2' VCPKG_DEFAULT_TRIPLET: 'x64-windows-static' CMAKE_GENERATOR: 'Visual Studio 16 2019' CMAKE_GENERATOR_PLATFORM: x64 @@ -68,7 +68,7 @@ jobs: BuildArgs: '-v --parallel 8' MacOS_x64_with_unit_test: OSVmImage: 'macOS-10.14' - VcpkgInstall: 'curl[ssl] libxml2 openssl nlohmann-json' + VcpkgInstall: 'curl[ssl] libxml2 openssl' VCPKG_DEFAULT_TRIPLET: 'x64-osx' CmakeArgs: ' -DBUILD_TESTING=ON -DRUN_LONG_UNIT_TESTS=ON' pool: diff --git a/sdk/core/azure-core/CMakeLists.txt b/sdk/core/azure-core/CMakeLists.txt index 58106ba32..fb79ae4ff 100644 --- a/sdk/core/azure-core/CMakeLists.txt +++ b/sdk/core/azure-core/CMakeLists.txt @@ -32,17 +32,6 @@ if(BUILD_TRANSPORT_CURL) message("Libcurl version ${CURL_VERSION_STRING}") endif() -# Storage requires 3.6.0 version for using `contains` feature -set(NLOHMANN_JSON_MIN_REQUIRED_VERSION 3.6.0) -# Try to find the config cmake file. Typically for when the integration is made at CMake level -# (CMake project available with a configuration file to consume) -find_package(nlohmann_json ${NLOHMANN_JSON_MIN_REQUIRED_VERSION} CONFIG QUIET) -if(NOT nlohmann_json_FOUND) - # If CMake project/config is not found, last option is to find the library installed. The integration is done by the linker directly. - find_package(nlohmann_json ${NLOHMANN_JSON_MIN_REQUIRED_VERSION} REQUIRED) -endif() -message("nlohmann json version ${nlohmann_json_VERSION}") - if(BUILD_TRANSPORT_CURL) SET(CURL_TRANSPORT_ADAPTER_SRC src/http/curl/curl.cpp) SET(CURL_TRANSPORT_ADAPTER_INC inc/azure/core/http/curl/curl.hpp) @@ -62,6 +51,7 @@ set( inc/azure/core/http/policy.hpp inc/azure/core/http/transport.hpp inc/azure/core/internal/contract.hpp + inc/azure/core/internal/json.hpp inc/azure/core/internal/log.hpp inc/azure/core/internal/strings.hpp inc/azure/core/logging/logging.hpp @@ -122,8 +112,7 @@ create_code_coverage(core azure-core azure-core-test) # ${CURL_INCLUDE_DIRS} needs to be public as long as we #include in public headers. target_include_directories(azure-core PUBLIC ${CURL_INCLUDE_DIRS}) -target_include_directories(azure-core INTERFACE ${nlohmann_json_INCLUDE_DIRS}) -target_link_libraries(azure-core INTERFACE Threads::Threads nlohmann_json::nlohmann_json) +target_link_libraries(azure-core INTERFACE Threads::Threads) if(MSVC) target_link_libraries(azure-core PRIVATE crypt32) diff --git a/sdk/core/azure-core/inc/azure/core/internal/json.hpp b/sdk/core/azure-core/inc/azure/core/internal/json.hpp index a0a273606..9aa3d5875 100644 --- a/sdk/core/azure-core/inc/azure/core/internal/json.hpp +++ b/sdk/core/azure-core/inc/azure/core/internal/json.hpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.6.0 +| | |__ | | | | | | version 3.8.0 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . @@ -27,16 +27,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef INCLUDE_NLOHMANN_JSON_HPP_ -#define INCLUDE_NLOHMANN_JSON_HPP_ +#pragma once -#define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 6 -#define NLOHMANN_JSON_VERSION_PATCH 0 +// #define NLOHMANN_JSON_VERSION_MAJOR 3 +// #define NLOHMANN_JSON_VERSION_MINOR 8 +// #define NLOHMANN_JSON_VERSION_PATCH 0 #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 @@ -56,7 +54,6 @@ SOFTWARE. #include // transform #include // array -#include // and, not #include // forward_list #include // inserter, front_inserter, end #include // map @@ -67,6 +64,15 @@ 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 #include // exception @@ -77,7 +83,7 @@ SOFTWARE. #include // size_t -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { /// struct to capture the start position of the current token struct position_t { @@ -92,9 +98,1986 @@ namespace nlohmann { namespace detail { constexpr operator size_t() const { return chars_read_total; } }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail -namespace nlohmann { namespace detail { +// #include + +#include // pair +// #include +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + +#if !defined(_az_JSON_HEDLEY_VERSION) || (_az_JSON_HEDLEY_VERSION < 13) +#if defined(_az_JSON_HEDLEY_VERSION) +#undef _az_JSON_HEDLEY_VERSION +#endif +#define _az_JSON_HEDLEY_VERSION 13 + +#if defined(_az_JSON_HEDLEY_STRINGIFY_EX) +#undef _az_JSON_HEDLEY_STRINGIFY_EX +#endif +#define _az_JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(_az_JSON_HEDLEY_STRINGIFY) +#undef _az_JSON_HEDLEY_STRINGIFY +#endif +#define _az_JSON_HEDLEY_STRINGIFY(x) _az_JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(_az_JSON_HEDLEY_CONCAT_EX) +#undef _az_JSON_HEDLEY_CONCAT_EX +#endif +#define _az_JSON_HEDLEY_CONCAT_EX(a, b) a##b + +#if defined(_az_JSON_HEDLEY_CONCAT) +#undef _az_JSON_HEDLEY_CONCAT +#endif +#define _az_JSON_HEDLEY_CONCAT(a, b) _az_JSON_HEDLEY_CONCAT_EX(a, b) + +#if defined(_az_JSON_HEDLEY_CONCAT3_EX) +#undef _az_JSON_HEDLEY_CONCAT3_EX +#endif +#define _az_JSON_HEDLEY_CONCAT3_EX(a, b, c) a##b##c + +#if defined(_az_JSON_HEDLEY_CONCAT3) +#undef _az_JSON_HEDLEY_CONCAT3 +#endif +#define _az_JSON_HEDLEY_CONCAT3(a, b, c) _az_JSON_HEDLEY_CONCAT3_EX(a, b, c) + +#if defined(_az_JSON_HEDLEY_VERSION_ENCODE) +#undef _az_JSON_HEDLEY_VERSION_ENCODE +#endif +#define _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, revision) \ + (((major)*1000000) + ((minor)*1000) + (revision)) + +#if defined(_az_JSON_HEDLEY_VERSION_DECODE_MAJOR) +#undef _az_JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define _az_JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(_az_JSON_HEDLEY_VERSION_DECODE_MINOR) +#undef _az_JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define _az_JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(_az_JSON_HEDLEY_VERSION_DECODE_REVISION) +#undef _az_JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define _az_JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(_az_JSON_HEDLEY_GNUC_VERSION) +#undef _az_JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) +#define _az_JSON_HEDLEY_GNUC_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) +#define _az_JSON_HEDLEY_GNUC_VERSION _az_JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(_az_JSON_HEDLEY_GNUC_VERSION_CHECK) +#undef _az_JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_GNUC_VERSION) +#define _az_JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_GNUC_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_MSVC_VERSION) +#undef _az_JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) +#define _az_JSON_HEDLEY_MSVC_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + _MSC_FULL_VER / 10000000, \ + (_MSC_FULL_VER % 10000000) / 100000, \ + (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) +#define _az_JSON_HEDLEY_MSVC_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + _MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) +#define _az_JSON_HEDLEY_MSVC_VERSION _az_JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(_az_JSON_HEDLEY_MSVC_VERSION_CHECK) +#undef _az_JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(_MSC_VER) +#define _az_JSON_HEDLEY_MSVC_VERSION_CHECK(major, minor, patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) +#define _az_JSON_HEDLEY_MSVC_VERSION_CHECK(major, minor, patch) \ + (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) +#define _az_JSON_HEDLEY_MSVC_VERSION_CHECK(major, minor, patch) \ + (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else +#define _az_JSON_HEDLEY_MSVC_VERSION_CHECK(major, minor, patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(_az_JSON_HEDLEY_INTEL_VERSION) +#undef _az_JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) +#define _az_JSON_HEDLEY_INTEL_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + __INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) +#define _az_JSON_HEDLEY_INTEL_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(_az_JSON_HEDLEY_INTEL_VERSION_CHECK) +#undef _az_JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_INTEL_VERSION) +#define _az_JSON_HEDLEY_INTEL_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_INTEL_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_INTEL_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_PGI_VERSION) +#undef _az_JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) +#define _az_JSON_HEDLEY_PGI_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(_az_JSON_HEDLEY_PGI_VERSION_CHECK) +#undef _az_JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_PGI_VERSION) +#define _az_JSON_HEDLEY_PGI_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_PGI_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_PGI_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_SUNPRO_VERSION) +#undef _az_JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) +#define _az_JSON_HEDLEY_SUNPRO_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + (((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), \ + (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), \ + (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) +#define _az_JSON_HEDLEY_SUNPRO_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C)&0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) +#define _az_JSON_HEDLEY_SUNPRO_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + (((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), \ + (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), \ + (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) +#define _az_JSON_HEDLEY_SUNPRO_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC)&0xf) +#endif + +#if defined(_az_JSON_HEDLEY_SUNPRO_VERSION_CHECK) +#undef _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_SUNPRO_VERSION) +#define _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_SUNPRO_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_EMSCRIPTEN_VERSION) +#undef _az_JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) +#define _az_JSON_HEDLEY_EMSCRIPTEN_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(_az_JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) +#undef _az_JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_EMSCRIPTEN_VERSION) +#define _az_JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_EMSCRIPTEN_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_ARM_VERSION) +#undef _az_JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) +#define _az_JSON_HEDLEY_ARM_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + __ARMCOMPILER_VERSION / 1000000, \ + (__ARMCOMPILER_VERSION % 1000000) / 10000, \ + (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) +#define _az_JSON_HEDLEY_ARM_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + __ARMCC_VERSION / 1000000, \ + (__ARMCC_VERSION % 1000000) / 10000, \ + (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(_az_JSON_HEDLEY_ARM_VERSION_CHECK) +#undef _az_JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_ARM_VERSION) +#define _az_JSON_HEDLEY_ARM_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_ARM_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_ARM_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_IBM_VERSION) +#undef _az_JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) +#define _az_JSON_HEDLEY_IBM_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) +#define _az_JSON_HEDLEY_IBM_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) +#define _az_JSON_HEDLEY_IBM_VERSION _az_JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(_az_JSON_HEDLEY_IBM_VERSION_CHECK) +#undef _az_JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_IBM_VERSION) +#define _az_JSON_HEDLEY_IBM_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_IBM_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_IBM_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_TI_VERSION) +#undef _az_JSON_HEDLEY_TI_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) \ + && (defined(__TMS470__) || defined(__TI_ARM__) || defined(__MSP430__) \ + || defined(__TMS320C2000__)) +#if (__TI_COMPILER_VERSION__ >= 16000000) +#define _az_JSON_HEDLEY_TI_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + __TI_COMPILER_VERSION__ / 1000000, \ + (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(_az_JSON_HEDLEY_TI_VERSION_CHECK) +#undef _az_JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_TI_VERSION) +#define _az_JSON_HEDLEY_TI_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_TI_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_TI_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_TI_CL2000_VERSION) +#undef _az_JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) +#define _az_JSON_HEDLEY_TI_CL2000_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + __TI_COMPILER_VERSION__ / 1000000, \ + (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(_az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK) +#undef _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_TI_CL2000_VERSION) +#define _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_TI_CL2000_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_TI_CL430_VERSION) +#undef _az_JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) +#define _az_JSON_HEDLEY_TI_CL430_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + __TI_COMPILER_VERSION__ / 1000000, \ + (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(_az_JSON_HEDLEY_TI_CL430_VERSION_CHECK) +#undef _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_TI_CL430_VERSION) +#define _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_TI_CL430_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_TI_ARMCL_VERSION) +#undef _az_JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) +#define _az_JSON_HEDLEY_TI_ARMCL_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + __TI_COMPILER_VERSION__ / 1000000, \ + (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(_az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) +#undef _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_TI_ARMCL_VERSION) +#define _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_TI_ARMCL_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_TI_CL6X_VERSION) +#undef _az_JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) +#define _az_JSON_HEDLEY_TI_CL6X_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + __TI_COMPILER_VERSION__ / 1000000, \ + (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(_az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK) +#undef _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_TI_CL6X_VERSION) +#define _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_TI_CL6X_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_TI_CL7X_VERSION) +#undef _az_JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) +#define _az_JSON_HEDLEY_TI_CL7X_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + __TI_COMPILER_VERSION__ / 1000000, \ + (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(_az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK) +#undef _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_TI_CL7X_VERSION) +#define _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_TI_CL7X_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_TI_CLPRU_VERSION) +#undef _az_JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) +#define _az_JSON_HEDLEY_TI_CLPRU_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + __TI_COMPILER_VERSION__ / 1000000, \ + (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ + (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(_az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) +#undef _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_TI_CLPRU_VERSION) +#define _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_TI_CLPRU_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_CRAY_VERSION) +#undef _az_JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) +#if defined(_RELEASE_PATCHLEVEL) +#define _az_JSON_HEDLEY_CRAY_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) +#else +#define _az_JSON_HEDLEY_CRAY_VERSION _az_JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) +#endif +#endif + +#if defined(_az_JSON_HEDLEY_CRAY_VERSION_CHECK) +#undef _az_JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_CRAY_VERSION) +#define _az_JSON_HEDLEY_CRAY_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_CRAY_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_CRAY_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_IAR_VERSION) +#undef _az_JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) +#if __VER__ > 1000 +#define _az_JSON_HEDLEY_IAR_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) +#else +#define _az_JSON_HEDLEY_IAR_VERSION _az_JSON_HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0) +#endif +#endif + +#if defined(_az_JSON_HEDLEY_IAR_VERSION_CHECK) +#undef _az_JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_IAR_VERSION) +#define _az_JSON_HEDLEY_IAR_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_IAR_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_IAR_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_TINYC_VERSION) +#undef _az_JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) +#define _az_JSON_HEDLEY_TINYC_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(_az_JSON_HEDLEY_TINYC_VERSION_CHECK) +#undef _az_JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_TINYC_VERSION) +#define _az_JSON_HEDLEY_TINYC_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_TINYC_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_TINYC_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_DMC_VERSION) +#undef _az_JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) +#define _az_JSON_HEDLEY_DMC_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(_az_JSON_HEDLEY_DMC_VERSION_CHECK) +#undef _az_JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_DMC_VERSION) +#define _az_JSON_HEDLEY_DMC_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_DMC_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_DMC_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_COMPCERT_VERSION) +#undef _az_JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) +#define _az_JSON_HEDLEY_COMPCERT_VERSION \ + _az_JSON_HEDLEY_VERSION_ENCODE( \ + __COMPCERT_VERSION__ / 10000, \ + (__COMPCERT_VERSION__ / 100) % 100, \ + __COMPCERT_VERSION__ % 100) +#endif + +#if defined(_az_JSON_HEDLEY_COMPCERT_VERSION_CHECK) +#undef _az_JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_COMPCERT_VERSION) +#define _az_JSON_HEDLEY_COMPCERT_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_COMPCERT_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_COMPCERT_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_PELLES_VERSION) +#undef _az_JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) +#define _az_JSON_HEDLEY_PELLES_VERSION _az_JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(_az_JSON_HEDLEY_PELLES_VERSION_CHECK) +#undef _az_JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_PELLES_VERSION) +#define _az_JSON_HEDLEY_PELLES_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_PELLES_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_PELLES_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_GCC_VERSION) +#undef _az_JSON_HEDLEY_GCC_VERSION +#endif +#if defined(_az_JSON_HEDLEY_GNUC_VERSION) && !defined(__clang__) \ + && !defined(_az_JSON_HEDLEY_INTEL_VERSION) && !defined(_az_JSON_HEDLEY_PGI_VERSION) \ + && !defined(_az_JSON_HEDLEY_ARM_VERSION) && !defined(_az_JSON_HEDLEY_TI_VERSION) \ + && !defined(_az_JSON_HEDLEY_TI_ARMCL_VERSION) && !defined(_az_JSON_HEDLEY_TI_CL430_VERSION) \ + && !defined(_az_JSON_HEDLEY_TI_CL2000_VERSION) && !defined(_az_JSON_HEDLEY_TI_CL6X_VERSION) \ + && !defined(_az_JSON_HEDLEY_TI_CL7X_VERSION) && !defined(_az_JSON_HEDLEY_TI_CLPRU_VERSION) \ + && !defined(__COMPCERT__) +#define _az_JSON_HEDLEY_GCC_VERSION _az_JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(_az_JSON_HEDLEY_GCC_VERSION_CHECK) +#undef _az_JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(_az_JSON_HEDLEY_GCC_VERSION) +#define _az_JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) \ + (_az_JSON_HEDLEY_GCC_VERSION >= _az_JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else +#define _az_JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) (0) +#endif + +#if defined(_az_JSON_HEDLEY_HAS_ATTRIBUTE) +#undef _az_JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) +#define _az_JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +#define _az_JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(_az_JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) +#undef _az_JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) +#define _az_JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) __has_attribute(attribute) +#else +#define _az_JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) \ + _az_JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_GCC_HAS_ATTRIBUTE) +#undef _az_JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) +#define _az_JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute, major, minor, patch) __has_attribute(attribute) +#else +#define _az_JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute, major, minor, patch) \ + _az_JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE) +#undef _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) \ + && (!defined(_az_JSON_HEDLEY_SUNPRO_VERSION) || _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 15, 0)) +#define _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else +#define _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(_az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) +#undef _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) +#define _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns, attribute) (0) +#elif !defined(_az_JSON_HEDLEY_PGI_VERSION) && !defined(_az_JSON_HEDLEY_IAR_VERSION) \ + && (!defined(_az_JSON_HEDLEY_SUNPRO_VERSION) || _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 15, 0)) \ + && (!defined(_az_JSON_HEDLEY_MSVC_VERSION) || _az_JSON_HEDLEY_MSVC_VERSION_CHECK(19, 20, 0)) +#define _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns, attribute) _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else +#define _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns, attribute) (0) +#endif + +#if defined(_az_JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) +#undef _az_JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) +#define _az_JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute, major, minor, patch) \ + __has_cpp_attribute(attribute) +#else +#define _az_JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute, major, minor, patch) \ + _az_JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) +#undef _az_JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) +#define _az_JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute, major, minor, patch) \ + __has_cpp_attribute(attribute) +#else +#define _az_JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute, major, minor, patch) \ + _az_JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_HAS_BUILTIN) +#undef _az_JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) +#define _az_JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else +#define _az_JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(_az_JSON_HEDLEY_GNUC_HAS_BUILTIN) +#undef _az_JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) +#define _az_JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin, major, minor, patch) __has_builtin(builtin) +#else +#define _az_JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin, major, minor, patch) \ + _az_JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_GCC_HAS_BUILTIN) +#undef _az_JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) +#define _az_JSON_HEDLEY_GCC_HAS_BUILTIN(builtin, major, minor, patch) __has_builtin(builtin) +#else +#define _az_JSON_HEDLEY_GCC_HAS_BUILTIN(builtin, major, minor, patch) \ + _az_JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_HAS_FEATURE) +#undef _az_JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) +#define _az_JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else +#define _az_JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(_az_JSON_HEDLEY_GNUC_HAS_FEATURE) +#undef _az_JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) +#define _az_JSON_HEDLEY_GNUC_HAS_FEATURE(feature, major, minor, patch) __has_feature(feature) +#else +#define _az_JSON_HEDLEY_GNUC_HAS_FEATURE(feature, major, minor, patch) \ + _az_JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_GCC_HAS_FEATURE) +#undef _az_JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) +#define _az_JSON_HEDLEY_GCC_HAS_FEATURE(feature, major, minor, patch) __has_feature(feature) +#else +#define _az_JSON_HEDLEY_GCC_HAS_FEATURE(feature, major, minor, patch) \ + _az_JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_HAS_EXTENSION) +#undef _az_JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) +#define _az_JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else +#define _az_JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(_az_JSON_HEDLEY_GNUC_HAS_EXTENSION) +#undef _az_JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) +#define _az_JSON_HEDLEY_GNUC_HAS_EXTENSION(extension, major, minor, patch) __has_extension(extension) +#else +#define _az_JSON_HEDLEY_GNUC_HAS_EXTENSION(extension, major, minor, patch) \ + _az_JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_GCC_HAS_EXTENSION) +#undef _az_JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) +#define _az_JSON_HEDLEY_GCC_HAS_EXTENSION(extension, major, minor, patch) __has_extension(extension) +#else +#define _az_JSON_HEDLEY_GCC_HAS_EXTENSION(extension, major, minor, patch) \ + _az_JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) +#undef _az_JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) +#define _az_JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else +#define _az_JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(_az_JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) +#undef _az_JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) +#define _az_JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute, major, minor, patch) \ + __has_declspec_attribute(attribute) +#else +#define _az_JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute, major, minor, patch) \ + _az_JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) +#undef _az_JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) +#define _az_JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute, major, minor, patch) \ + __has_declspec_attribute(attribute) +#else +#define _az_JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute, major, minor, patch) \ + _az_JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_HAS_WARNING) +#undef _az_JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) +#define _az_JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else +#define _az_JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(_az_JSON_HEDLEY_GNUC_HAS_WARNING) +#undef _az_JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) +#define _az_JSON_HEDLEY_GNUC_HAS_WARNING(warning, major, minor, patch) __has_warning(warning) +#else +#define _az_JSON_HEDLEY_GNUC_HAS_WARNING(warning, major, minor, patch) \ + _az_JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_GCC_HAS_WARNING) +#undef _az_JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) +#define _az_JSON_HEDLEY_GCC_HAS_WARNING(warning, major, minor, patch) __has_warning(warning) +#else +#define _az_JSON_HEDLEY_GCC_HAS_WARNING(warning, major, minor, patch) \ + _az_JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +/* _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(_az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) +#undef _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +#if _az_JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +#if _az_JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + _az_JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") xpr _az_JSON_HEDLEY_DIAGNOSTIC_POP +#else +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + _az_JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") xpr _az_JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#endif +#endif +#if !defined(_az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(_az_JSON_HEDLEY_CONST_CAST) +#undef _az_JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +#define _az_JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif _az_JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 6, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define _az_JSON_HEDLEY_CONST_CAST(T, expr) \ + (__extension__({ \ + _az_JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL((T)(expr)); \ + _az_JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +#define _az_JSON_HEDLEY_CONST_CAST(T, expr) ((T)(expr)) +#endif + +#if defined(_az_JSON_HEDLEY_REINTERPRET_CAST) +#undef _az_JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) +#define _az_JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else +#define _az_JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T)(expr)) +#endif + +#if defined(_az_JSON_HEDLEY_STATIC_CAST) +#undef _az_JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) +#define _az_JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else +#define _az_JSON_HEDLEY_STATIC_CAST(T, expr) ((T)(expr)) +#endif + +#if defined(_az_JSON_HEDLEY_CPP_CAST) +#undef _az_JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +#if _az_JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +#define _az_JSON_HEDLEY_CPP_CAST(T, expr) \ + _az_JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"")((T)(expr)) _az_JSON_HEDLEY_DIAGNOSTIC_POP +#elif _az_JSON_HEDLEY_IAR_VERSION_CHECK(8, 3, 0) +#define _az_JSON_HEDLEY_CPP_CAST(T, expr) \ + _az_JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") _az_JSON_HEDLEY_DIAGNOSTIC_POP #else +#define _az_JSON_HEDLEY_CPP_CAST(T, expr) ((T)(expr)) +#endif +#else +#define _az_JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || defined(__clang__) \ + || _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 0, 0) || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) \ + || _az_JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) || _az_JSON_HEDLEY_PGI_VERSION_CHECK(18, 4, 0) \ + || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || _az_JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) \ + || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 7, 0) || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(2, 0, 1) \ + || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 1, 0) || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 0, 0) \ + || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) \ + || _az_JSON_HEDLEY_CRAY_VERSION_CHECK(5, 0, 0) || _az_JSON_HEDLEY_TINYC_VERSION_CHECK(0, 9, 17) \ + || _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(8, 0, 0) \ + || (_az_JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) && defined(__C99_PRAGMA_OPERATOR)) +#define _az_JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif _az_JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) +#define _az_JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else +#define _az_JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(_az_JSON_HEDLEY_DIAGNOSTIC_PUSH) +#undef _az_JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(_az_JSON_HEDLEY_DIAGNOSTIC_POP) +#undef _az_JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) +#define _az_JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") +#define _az_JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") +#define _az_JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 6, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") +#define _az_JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif _az_JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) +#define _az_JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif _az_JSON_HEDLEY_ARM_VERSION_CHECK(5, 6, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") +#define _az_JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif _az_JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 4, 0) || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 1, 0) \ + || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") +#define _az_JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif _az_JSON_HEDLEY_PELLES_VERSION_CHECK(2, 90, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") +#define _az_JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else +#define _az_JSON_HEDLEY_DIAGNOSTIC_PUSH +#define _az_JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +#if defined(_az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) +#undef _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if _az_JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif _az_JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 3, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif _az_JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable : 4996)) +#elif _az_JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) \ + || (_az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) \ + || (_az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) \ + || (_az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) \ + || (_az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) \ + || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 13, 0) && !defined(__cplusplus) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED \ + _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 13, 0) && defined(__cplusplus) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED \ + _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif _az_JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif _az_JSON_HEDLEY_PELLES_VERSION_CHECK(2, 90, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(_az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) +#undef _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if _az_JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif _az_JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 3, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif _az_JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable : 4068)) +#elif _az_JSON_HEDLEY_TI_VERSION_CHECK(16, 9, 0) || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 0, 0) \ + || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 3, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif _az_JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#else +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(_az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) +#undef _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if _az_JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES \ + _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 6, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif _az_JSON_HEDLEY_INTEL_VERSION_CHECK(17, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif _az_JSON_HEDLEY_MSVC_VERSION_CHECK(19, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable : 5030)) +#elif _az_JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 14, 0) && defined(__cplusplus) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES \ + _Pragma("error_messages(off,attrskipunsup)") +#elif _az_JSON_HEDLEY_TI_VERSION_CHECK(18, 1, 0) || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 3, 0) \ + || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif _az_JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#else +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(_az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) +#undef _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if _az_JSON_HEDLEY_HAS_WARNING("-Wcast-qual") +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 0, 0) +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else +#define _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(_az_JSON_HEDLEY_DEPRECATED) +#undef _az_JSON_HEDLEY_DEPRECATED +#endif +#if defined(_az_JSON_HEDLEY_DEPRECATED_FOR) +#undef _az_JSON_HEDLEY_DEPRECATED_FOR +#endif +#if _az_JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) +#define _az_JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " #since)) +#define _az_JSON_HEDLEY_DEPRECATED_FOR(since, replacement) \ + __declspec(deprecated("Since " #since "; use " #replacement)) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) +#define _az_JSON_HEDLEY_DEPRECATED(since) \ + _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) +#define _az_JSON_HEDLEY_DEPRECATED_FOR(since, replacement) \ + _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_( \ + [[deprecated("Since " #since "; use " #replacement)]]) +#elif _az_JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) \ + || _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 5, 0) || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) \ + || _az_JSON_HEDLEY_ARM_VERSION_CHECK(5, 6, 0) || _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 13, 0) \ + || _az_JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) || _az_JSON_HEDLEY_TI_VERSION_CHECK(18, 1, 0) \ + || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18, 1, 0) || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 3, 0) \ + || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 3, 0) +#define _az_JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) +#define _az_JSON_HEDLEY_DEPRECATED_FOR(since, replacement) \ + __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif _az_JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 1, 0) \ + || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || _az_JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) \ + || (_az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) \ + || (_az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) \ + || (_az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) \ + || (_az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) \ + || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define _az_JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) +#define _az_JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif _az_JSON_HEDLEY_MSVC_VERSION_CHECK(13, 10, 0) || _az_JSON_HEDLEY_PELLES_VERSION_CHECK(6, 50, 0) +#define _az_JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) +#define _az_JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif _az_JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define _az_JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") +#define _az_JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else +#define _az_JSON_HEDLEY_DEPRECATED(since) +#define _az_JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(_az_JSON_HEDLEY_UNAVAILABLE) +#undef _az_JSON_HEDLEY_UNAVAILABLE +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(warning) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 3, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define _az_JSON_HEDLEY_UNAVAILABLE(available_since) \ + __attribute__((__warning__("Not available until " #available_since))) +#else +#define _az_JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(_az_JSON_HEDLEY_WARN_UNUSED_RESULT) +#undef _az_JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(_az_JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) +#undef _az_JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if (_az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) +#define _az_JSON_HEDLEY_WARN_UNUSED_RESULT \ + _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#define _az_JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) \ + _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) +#define _az_JSON_HEDLEY_WARN_UNUSED_RESULT \ + _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#define _az_JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) \ + _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif _az_JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 4, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || _az_JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) \ + || (_az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) \ + || (_az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) \ + || (_az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) \ + || (_az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) \ + || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) \ + || (_az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 15, 0) && defined(__cplusplus)) \ + || _az_JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) +#define _az_JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) +#define _az_JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif defined(_Check_return_) /* SAL */ +#define _az_JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ +#define _az_JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else +#define _az_JSON_HEDLEY_WARN_UNUSED_RESULT +#define _az_JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(_az_JSON_HEDLEY_SENTINEL) +#undef _az_JSON_HEDLEY_SENTINEL +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 0, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || _az_JSON_HEDLEY_ARM_VERSION_CHECK(5, 4, 0) +#define _az_JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else +#define _az_JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(_az_JSON_HEDLEY_NO_RETURN) +#undef _az_JSON_HEDLEY_NO_RETURN +#endif +#if _az_JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define _az_JSON_HEDLEY_NO_RETURN __noreturn +#elif _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define _az_JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +#define _az_JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) +#define _az_JSON_HEDLEY_NO_RETURN _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif _az_JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 2, 0) \ + || _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) \ + || _az_JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || _az_JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) \ + || (_az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) \ + || (_az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) \ + || (_az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) \ + || (_az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) \ + || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define _az_JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 10, 0) +#define _az_JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif _az_JSON_HEDLEY_MSVC_VERSION_CHECK(13, 10, 0) +#define _az_JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6, 0, 0) && defined(__cplusplus) +#define _az_JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif _az_JSON_HEDLEY_COMPCERT_VERSION_CHECK(3, 2, 0) +#define _az_JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif _az_JSON_HEDLEY_PELLES_VERSION_CHECK(9, 0, 0) +#define _az_JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else +#define _az_JSON_HEDLEY_NO_RETURN +#endif + +#if defined(_az_JSON_HEDLEY_NO_ESCAPE) +#undef _az_JSON_HEDLEY_NO_ESCAPE +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(noescape) +#define _az_JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else +#define _az_JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(_az_JSON_HEDLEY_UNREACHABLE) +#undef _az_JSON_HEDLEY_UNREACHABLE +#endif +#if defined(_az_JSON_HEDLEY_UNREACHABLE_RETURN) +#undef _az_JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(_az_JSON_HEDLEY_ASSUME) +#undef _az_JSON_HEDLEY_ASSUME +#endif +#if _az_JSON_HEDLEY_MSVC_VERSION_CHECK(13, 10, 0) || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define _az_JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif _az_JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) +#define _az_JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 2, 0) || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4, 0, 0) +#if defined(__cplusplus) +#define _az_JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) +#else +#define _az_JSON_HEDLEY_ASSUME(expr) _nassert(expr) +#endif +#endif +#if (_az_JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(_az_JSON_HEDLEY_ARM_VERSION))) \ + || _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 5, 0) || _az_JSON_HEDLEY_PGI_VERSION_CHECK(18, 10, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || _az_JSON_HEDLEY_IBM_VERSION_CHECK(13, 1, 5) +#define _az_JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(_az_JSON_HEDLEY_ASSUME) +#define _az_JSON_HEDLEY_UNREACHABLE() _az_JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(_az_JSON_HEDLEY_ASSUME) +#if defined(_az_JSON_HEDLEY_UNREACHABLE) +#define _az_JSON_HEDLEY_ASSUME(expr) \ + _az_JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (_az_JSON_HEDLEY_UNREACHABLE(), 1))) +#else +#define _az_JSON_HEDLEY_ASSUME(expr) _az_JSON_HEDLEY_STATIC_CAST(void, expr) +#endif +#endif +#if defined(_az_JSON_HEDLEY_UNREACHABLE) +#if _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 2, 0) || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4, 0, 0) +#define _az_JSON_HEDLEY_UNREACHABLE_RETURN(value) \ + return (_az_JSON_HEDLEY_STATIC_CAST(void, _az_JSON_HEDLEY_ASSUME(0)), (value)) +#else +#define _az_JSON_HEDLEY_UNREACHABLE_RETURN(value) _az_JSON_HEDLEY_UNREACHABLE() +#endif +#else +#define _az_JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(_az_JSON_HEDLEY_UNREACHABLE) +#define _az_JSON_HEDLEY_UNREACHABLE() _az_JSON_HEDLEY_ASSUME(0) +#endif + +_az_JSON_HEDLEY_DIAGNOSTIC_PUSH +#if _az_JSON_HEDLEY_HAS_WARNING("-Wpedantic") +#pragma clang diagnostic ignored "-Wpedantic" +#endif +#if _az_JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if _az_JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros", 4, 0, 0) +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wvariadic-macros" +#elif defined(_az_JSON_HEDLEY_GCC_VERSION) +#pragma GCC diagnostic ignored "-Wvariadic-macros" +#endif +#endif +#if defined(_az_JSON_HEDLEY_NON_NULL) +#undef _az_JSON_HEDLEY_NON_NULL +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 3, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) +#define _az_JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else +#define _az_JSON_HEDLEY_NON_NULL(...) +#endif +_az_JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(_az_JSON_HEDLEY_PRINTF_FORMAT) +#undef _az_JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && _az_JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format, 4, 4, 0) \ + && !defined(__USE_MINGW_ANSI_STDIO) +#define _az_JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) \ + __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && _az_JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format, 4, 4, 0) \ + && defined(__USE_MINGW_ANSI_STDIO) +#define _az_JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) \ + __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif _az_JSON_HEDLEY_HAS_ATTRIBUTE(format) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 1, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || _az_JSON_HEDLEY_ARM_VERSION_CHECK(5, 6, 0) \ + || _az_JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || _az_JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) \ + || (_az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) \ + || (_az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) \ + || (_az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) \ + || (_az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) \ + || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define _az_JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) \ + __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif _az_JSON_HEDLEY_PELLES_VERSION_CHECK(6, 0, 0) +#define _az_JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) \ + __declspec(vaformat(printf, string_idx, first_to_check)) +#else +#define _az_JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) +#endif + +#if defined(_az_JSON_HEDLEY_CONSTEXPR) +#undef _az_JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) +#if __cplusplus >= 201103L +#define _az_JSON_HEDLEY_CONSTEXPR _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) +#endif +#endif +#if !defined(_az_JSON_HEDLEY_CONSTEXPR) +#define _az_JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(_az_JSON_HEDLEY_PREDICT) +#undef _az_JSON_HEDLEY_PREDICT +#endif +#if defined(_az_JSON_HEDLEY_LIKELY) +#undef _az_JSON_HEDLEY_LIKELY +#endif +#if defined(_az_JSON_HEDLEY_UNLIKELY) +#undef _az_JSON_HEDLEY_UNLIKELY +#endif +#if defined(_az_JSON_HEDLEY_UNPREDICTABLE) +#undef _az_JSON_HEDLEY_UNPREDICTABLE +#endif +#if _az_JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) +#define _az_JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if _az_JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) \ + || _az_JSON_HEDLEY_GCC_VERSION_CHECK(9, 0, 0) +#define _az_JSON_HEDLEY_PREDICT(expr, value, probability) \ + __builtin_expect_with_probability((expr), (value), (probability)) +#define _az_JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + __builtin_expect_with_probability(!!(expr), 1, (probability)) +#define _az_JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + __builtin_expect_with_probability(!!(expr), 0, (probability)) +#define _az_JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +#define _az_JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#elif _az_JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 0, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) \ + || (_az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 15, 0) && defined(__cplusplus)) \ + || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || _az_JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) \ + || _az_JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 7, 0) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(3, 1, 0) || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 1, 0) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6, 1, 0) || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) \ + || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) || _az_JSON_HEDLEY_TINYC_VERSION_CHECK(0, 9, 27) \ + || _az_JSON_HEDLEY_CRAY_VERSION_CHECK(8, 1, 0) +#define _az_JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) \ + : (_az_JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +#define _az_JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) \ + ? __builtin_expect(!!(expr), 1) \ + : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +#define _az_JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) \ + ? __builtin_expect(!!(expr), 0) \ + : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +#define _az_JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +#define _az_JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +#define _az_JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (_az_JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +#define _az_JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +#define _az_JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +#define _az_JSON_HEDLEY_LIKELY(expr) (!!(expr)) +#define _az_JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(_az_JSON_HEDLEY_UNPREDICTABLE) +#define _az_JSON_HEDLEY_UNPREDICTABLE(expr) _az_JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(_az_JSON_HEDLEY_MALLOC) +#undef _az_JSON_HEDLEY_MALLOC +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 1, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) \ + || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || _az_JSON_HEDLEY_IBM_VERSION_CHECK(12, 1, 0) \ + || _az_JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) \ + || (_az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) \ + || (_az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) \ + || (_az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) \ + || (_az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) \ + || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define _az_JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 10, 0) +#define _az_JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif _az_JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) +#define _az_JSON_HEDLEY_MALLOC __declspec(restrict) +#else +#define _az_JSON_HEDLEY_MALLOC +#endif + +#if defined(_az_JSON_HEDLEY_PURE) +#undef _az_JSON_HEDLEY_PURE +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(pure) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(2, 96, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) \ + || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || _az_JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) \ + || _az_JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) \ + || (_az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) \ + || (_az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) \ + || (_az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) \ + || (_az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) \ + || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) || _az_JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) +#define _az_JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 10, 0) +#define _az_JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) \ + && (_az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(2, 0, 1) || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4, 0, 0) \ + || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0)) +#define _az_JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +#define _az_JSON_HEDLEY_PURE +#endif + +#if defined(_az_JSON_HEDLEY_CONST) +#undef _az_JSON_HEDLEY_CONST +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(const) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(2, 5, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) \ + || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || _az_JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) \ + || _az_JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) \ + || (_az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) \ + || (_az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) \ + || (_az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) \ + || (_az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) \ + || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) || _az_JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) +#define _az_JSON_HEDLEY_CONST __attribute__((__const__)) +#elif _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 10, 0) +#define _az_JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else +#define _az_JSON_HEDLEY_CONST _az_JSON_HEDLEY_PURE +#endif + +#if defined(_az_JSON_HEDLEY_RESTRICT) +#undef _az_JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) +#define _az_JSON_HEDLEY_RESTRICT restrict +#elif _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 1, 0) || _az_JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) \ + || _az_JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || _az_JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 2, 4) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 1, 0) || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) \ + || (_az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 14, 0) && defined(__cplusplus)) \ + || _az_JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) || defined(__clang__) +#define _az_JSON_HEDLEY_RESTRICT __restrict +#elif _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 3, 0) && !defined(__cplusplus) +#define _az_JSON_HEDLEY_RESTRICT _Restrict +#else +#define _az_JSON_HEDLEY_RESTRICT +#endif + +#if defined(_az_JSON_HEDLEY_INLINE) +#undef _az_JSON_HEDLEY_INLINE +#endif +#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) \ + || (defined(__cplusplus) && (__cplusplus >= 199711L)) +#define _az_JSON_HEDLEY_INLINE inline +#elif defined(_az_JSON_HEDLEY_GCC_VERSION) || _az_JSON_HEDLEY_ARM_VERSION_CHECK(6, 2, 0) +#define _az_JSON_HEDLEY_INLINE __inline__ +#elif _az_JSON_HEDLEY_MSVC_VERSION_CHECK(12, 0, 0) || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) \ + || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 1, 0) || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(3, 1, 0) \ + || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 2, 0) || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8, 0, 0) \ + || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define _az_JSON_HEDLEY_INLINE __inline +#else +#define _az_JSON_HEDLEY_INLINE +#endif + +#if defined(_az_JSON_HEDLEY_ALWAYS_INLINE) +#undef _az_JSON_HEDLEY_ALWAYS_INLINE +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 0, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) \ + || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || _az_JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) \ + || _az_JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) \ + || (_az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) \ + || (_az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) \ + || (_az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) \ + || (_az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) \ + || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define _az_JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) _az_JSON_HEDLEY_INLINE +#elif _az_JSON_HEDLEY_MSVC_VERSION_CHECK(12, 0, 0) +#define _az_JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) \ + && (_az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) \ + || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6, 1, 0) \ + || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) \ + || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0)) +#define _az_JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif _az_JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define _az_JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +#define _az_JSON_HEDLEY_ALWAYS_INLINE _az_JSON_HEDLEY_INLINE +#endif + +#if defined(_az_JSON_HEDLEY_NEVER_INLINE) +#undef _az_JSON_HEDLEY_NEVER_INLINE +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 0, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) \ + || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || _az_JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) \ + || _az_JSON_HEDLEY_TI_VERSION_CHECK(15, 12, 0) \ + || (_az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4, 8, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5, 2, 0) \ + || (_az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6, 4, 0) \ + || (_az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 0, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(4, 3, 0) \ + || (_az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0) || _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1, 2, 0) \ + || _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2, 1, 0) +#define _az_JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif _az_JSON_HEDLEY_MSVC_VERSION_CHECK(13, 10, 0) +#define _az_JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif _az_JSON_HEDLEY_PGI_VERSION_CHECK(10, 2, 0) +#define _az_JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6, 0, 0) && defined(__cplusplus) +#define _az_JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif _az_JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define _az_JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif _az_JSON_HEDLEY_COMPCERT_VERSION_CHECK(3, 2, 0) +#define _az_JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif _az_JSON_HEDLEY_PELLES_VERSION_CHECK(9, 0, 0) +#define _az_JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else +#define _az_JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(_az_JSON_HEDLEY_PRIVATE) +#undef _az_JSON_HEDLEY_PRIVATE +#endif +#if defined(_az_JSON_HEDLEY_PUBLIC) +#undef _az_JSON_HEDLEY_PUBLIC +#endif +#if defined(_az_JSON_HEDLEY_IMPORT) +#undef _az_JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +#define _az_JSON_HEDLEY_PRIVATE +#define _az_JSON_HEDLEY_PUBLIC __declspec(dllexport) +#define _az_JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 3, 0) \ + || _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) \ + || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || _az_JSON_HEDLEY_IBM_VERSION_CHECK(13, 1, 0) \ + || (defined(__TI_EABI__) \ + && ((_az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 2, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7, 5, 0))) +#define _az_JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +#define _az_JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +#else +#define _az_JSON_HEDLEY_PRIVATE +#define _az_JSON_HEDLEY_PUBLIC +#endif +#define _az_JSON_HEDLEY_IMPORT extern +#endif + +#if defined(_az_JSON_HEDLEY_NO_THROW) +#undef _az_JSON_HEDLEY_NO_THROW +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 3, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define _az_JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif _az_JSON_HEDLEY_MSVC_VERSION_CHECK(13, 1, 0) || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) +#define _az_JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else +#define _az_JSON_HEDLEY_NO_THROW +#endif + +#if defined(_az_JSON_HEDLEY_FALL_THROUGH) +#undef _az_JSON_HEDLEY_FALL_THROUGH +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(7, 0, 0) +#define _az_JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang, fallthrough) +#define _az_JSON_HEDLEY_FALL_THROUGH \ + _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) +#define _az_JSON_HEDLEY_FALL_THROUGH _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ +#define _az_JSON_HEDLEY_FALL_THROUGH __fallthrough +#else +#define _az_JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(_az_JSON_HEDLEY_RETURNS_NON_NULL) +#undef _az_JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 9, 0) +#define _az_JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ +#define _az_JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else +#define _az_JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(_az_JSON_HEDLEY_ARRAY_PARAM) +#undef _az_JSON_HEDLEY_ARRAY_PARAM +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__STDC_NO_VLA__) \ + && !defined(__cplusplus) && !defined(_az_JSON_HEDLEY_PGI_VERSION) \ + && !defined(_az_JSON_HEDLEY_TINYC_VERSION) +#define _az_JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else +#define _az_JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(_az_JSON_HEDLEY_IS_CONSTANT) +#undef _az_JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(_az_JSON_HEDLEY_REQUIRE_CONSTEXPR) +#undef _az_JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* _az_JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(_az_JSON_HEDLEY_IS_CONSTEXPR_) +#undef _az_JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if _az_JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 4, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || _az_JSON_HEDLEY_TINYC_VERSION_CHECK(0, 9, 19) \ + || _az_JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || _az_JSON_HEDLEY_IBM_VERSION_CHECK(13, 1, 0) \ + || _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6, 1, 0) \ + || (_az_JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 10, 0) && !defined(__cplusplus)) \ + || _az_JSON_HEDLEY_CRAY_VERSION_CHECK(8, 1, 0) +#define _az_JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +#if _az_JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) \ + || _az_JSON_HEDLEY_GCC_VERSION_CHECK(3, 4, 0) || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) \ + || _az_JSON_HEDLEY_IBM_VERSION_CHECK(13, 1, 0) || _az_JSON_HEDLEY_CRAY_VERSION_CHECK(8, 1, 0) \ + || _az_JSON_HEDLEY_ARM_VERSION_CHECK(5, 4, 0) || _az_JSON_HEDLEY_TINYC_VERSION_CHECK(0, 9, 24) +#if defined(__INTPTR_TYPE__) +#define _az_JSON_HEDLEY_IS_CONSTEXPR_(expr) \ + __builtin_types_compatible_p( \ + __typeof__((1 ? (void*)((__INTPTR_TYPE__)((expr)*0)) : (int*)0)), int*) +#else +#include +#define _az_JSON_HEDLEY_IS_CONSTEXPR_(expr) \ + __builtin_types_compatible_p(__typeof__((1 ? (void*)((intptr_t)((expr)*0)) : (int*)0)), int*) +#endif +#elif ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) \ + && !defined(_az_JSON_HEDLEY_SUNPRO_VERSION) && !defined(_az_JSON_HEDLEY_PGI_VERSION) \ + && !defined(_az_JSON_HEDLEY_IAR_VERSION)) \ + || _az_JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 9, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(17, 0, 0) || _az_JSON_HEDLEY_IBM_VERSION_CHECK(12, 1, 0) \ + || _az_JSON_HEDLEY_ARM_VERSION_CHECK(5, 3, 0) +#if defined(__INTPTR_TYPE__) +#define _az_JSON_HEDLEY_IS_CONSTEXPR_(expr) \ + _Generic((1 ? (void*)((__INTPTR_TYPE__)((expr)*0)) : (int*)0), int* : 1, void* : 0) +#else +#include +#define _az_JSON_HEDLEY_IS_CONSTEXPR_(expr) \ + _Generic((1 ? (void*)((intptr_t)*0) : (int*)0), int* : 1, void* : 0) +#endif +#elif defined(_az_JSON_HEDLEY_GCC_VERSION) || defined(_az_JSON_HEDLEY_INTEL_VERSION) \ + || defined(_az_JSON_HEDLEY_TINYC_VERSION) || defined(_az_JSON_HEDLEY_TI_ARMCL_VERSION) \ + || _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK(18, 12, 0) || defined(_az_JSON_HEDLEY_TI_CL2000_VERSION) \ + || defined(_az_JSON_HEDLEY_TI_CL6X_VERSION) || defined(_az_JSON_HEDLEY_TI_CL7X_VERSION) \ + || defined(_az_JSON_HEDLEY_TI_CLPRU_VERSION) || defined(__clang__) +#define _az_JSON_HEDLEY_IS_CONSTEXPR_(expr) \ + (sizeof(void) != sizeof(*(1 ? ((void*)((expr)*0L)) : ((struct { char v[sizeof(void) * 2]; }*)1)))) +#endif +#endif +#if defined(_az_JSON_HEDLEY_IS_CONSTEXPR_) +#if !defined(_az_JSON_HEDLEY_IS_CONSTANT) +#define _az_JSON_HEDLEY_IS_CONSTANT(expr) _az_JSON_HEDLEY_IS_CONSTEXPR_(expr) +#endif +#define _az_JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (_az_JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else +#if !defined(_az_JSON_HEDLEY_IS_CONSTANT) +#define _az_JSON_HEDLEY_IS_CONSTANT(expr) (0) +#endif +#define _az_JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(_az_JSON_HEDLEY_BEGIN_C_DECLS) +#undef _az_JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(_az_JSON_HEDLEY_END_C_DECLS) +#undef _az_JSON_HEDLEY_END_C_DECLS +#endif +#if defined(_az_JSON_HEDLEY_C_DECL) +#undef _az_JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) +#define _az_JSON_HEDLEY_BEGIN_C_DECLS \ + extern "C" \ + { +#define _az_JSON_HEDLEY_END_C_DECLS } +#define _az_JSON_HEDLEY_C_DECL extern "C" +#else +#define _az_JSON_HEDLEY_BEGIN_C_DECLS +#define _az_JSON_HEDLEY_END_C_DECLS +#define _az_JSON_HEDLEY_C_DECL +#endif + +#if defined(_az_JSON_HEDLEY_STATIC_ASSERT) +#undef _az_JSON_HEDLEY_STATIC_ASSERT +#endif +#if !defined(__cplusplus) \ + && ((defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) \ + || _az_JSON_HEDLEY_HAS_FEATURE(c_static_assert) || _az_JSON_HEDLEY_GCC_VERSION_CHECK(6, 0, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || defined(_Static_assert)) +#define _az_JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif (defined(__cplusplus) && (__cplusplus >= 201103L)) || _az_JSON_HEDLEY_MSVC_VERSION_CHECK(16, 0, 0) +#define _az_JSON_HEDLEY_STATIC_ASSERT(expr, message) \ + _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +#define _az_JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(_az_JSON_HEDLEY_NULL) +#undef _az_JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) +#if __cplusplus >= 201103L +#define _az_JSON_HEDLEY_NULL _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) +#elif defined(NULL) +#define _az_JSON_HEDLEY_NULL NULL +#else +#define _az_JSON_HEDLEY_NULL _az_JSON_HEDLEY_STATIC_CAST(void*, 0) +#endif +#elif defined(NULL) +#define _az_JSON_HEDLEY_NULL NULL +#else +#define _az_JSON_HEDLEY_NULL ((void*)0) +#endif + +#if defined(_az_JSON_HEDLEY_MESSAGE) +#undef _az_JSON_HEDLEY_MESSAGE +#endif +#if _az_JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +#define _az_JSON_HEDLEY_MESSAGE(msg) \ + _az_JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + _az_JSON_HEDLEY_PRAGMA(message msg) \ + _az_JSON_HEDLEY_DIAGNOSTIC_POP +#elif _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 4, 0) || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define _az_JSON_HEDLEY_MESSAGE(msg) _az_JSON_HEDLEY_PRAGMA(message msg) +#elif _az_JSON_HEDLEY_CRAY_VERSION_CHECK(5, 0, 0) +#define _az_JSON_HEDLEY_MESSAGE(msg) _az_JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif _az_JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) +#define _az_JSON_HEDLEY_MESSAGE(msg) _az_JSON_HEDLEY_PRAGMA(message(msg)) +#elif _az_JSON_HEDLEY_PELLES_VERSION_CHECK(2, 0, 0) +#define _az_JSON_HEDLEY_MESSAGE(msg) _az_JSON_HEDLEY_PRAGMA(message(msg)) +#else +#define _az_JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(_az_JSON_HEDLEY_WARNING) +#undef _az_JSON_HEDLEY_WARNING +#endif +#if _az_JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +#define _az_JSON_HEDLEY_WARNING(msg) \ + _az_JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + _az_JSON_HEDLEY_PRAGMA(clang warning msg) \ + _az_JSON_HEDLEY_DIAGNOSTIC_POP +#elif _az_JSON_HEDLEY_GCC_VERSION_CHECK(4, 8, 0) || _az_JSON_HEDLEY_PGI_VERSION_CHECK(18, 4, 0) \ + || _az_JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) +#define _az_JSON_HEDLEY_WARNING(msg) _az_JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif _az_JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) +#define _az_JSON_HEDLEY_WARNING(msg) _az_JSON_HEDLEY_PRAGMA(message(msg)) +#else +#define _az_JSON_HEDLEY_WARNING(msg) _az_JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(_az_JSON_HEDLEY_REQUIRE) +#undef _az_JSON_HEDLEY_REQUIRE +#endif +#if defined(_az_JSON_HEDLEY_REQUIRE_MSG) +#undef _az_JSON_HEDLEY_REQUIRE_MSG +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +#if _az_JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +#define _az_JSON_HEDLEY_REQUIRE(expr) \ + _az_JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) _az_JSON_HEDLEY_DIAGNOSTIC_POP +#define _az_JSON_HEDLEY_REQUIRE_MSG(expr, msg) \ + _az_JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) _az_JSON_HEDLEY_DIAGNOSTIC_POP +#else +#define _az_JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +#define _az_JSON_HEDLEY_REQUIRE_MSG(expr, msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +#endif +#else +#define _az_JSON_HEDLEY_REQUIRE(expr) +#define _az_JSON_HEDLEY_REQUIRE_MSG(expr, msg) +#endif + +#if defined(_az_JSON_HEDLEY_FLAGS) +#undef _az_JSON_HEDLEY_FLAGS +#endif +#if _az_JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) +#define _az_JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#endif + +#if defined(_az_JSON_HEDLEY_FLAGS_CAST) +#undef _az_JSON_HEDLEY_FLAGS_CAST +#endif +#if _az_JSON_HEDLEY_INTEL_VERSION_CHECK(19, 0, 0) +#define _az_JSON_HEDLEY_FLAGS_CAST(T, expr) \ + (__extension__({ \ + _az_JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)")((T)(expr)); \ + _az_JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +#define _az_JSON_HEDLEY_FLAGS_CAST(T, expr) _az_JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(_az_JSON_HEDLEY_EMPTY_BASES) +#undef _az_JSON_HEDLEY_EMPTY_BASES +#endif +#if _az_JSON_HEDLEY_MSVC_VERSION_CHECK(19, 0, 23918) && !_az_JSON_HEDLEY_MSVC_VERSION_CHECK(20, 0, 0) +#define _az_JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else +#define _az_JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(_az_JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) +#undef _az_JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) +#define _az_JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major, minor, patch) (0) +#else +#define _az_JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major, minor, patch) \ + _az_JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) +#endif + +#if defined(_az_JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) +#undef _az_JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define _az_JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) _az_JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(_az_JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) +#undef _az_JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define _az_JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(_az_JSON_HEDLEY_CLANG_HAS_BUILTIN) +#undef _az_JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define _az_JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) _az_JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(_az_JSON_HEDLEY_CLANG_HAS_FEATURE) +#undef _az_JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define _az_JSON_HEDLEY_CLANG_HAS_FEATURE(feature) _az_JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(_az_JSON_HEDLEY_CLANG_HAS_EXTENSION) +#undef _az_JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define _az_JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) _az_JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(_az_JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) +#undef _az_JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define _az_JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) \ + _az_JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(_az_JSON_HEDLEY_CLANG_HAS_WARNING) +#undef _az_JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define _az_JSON_HEDLEY_CLANG_HAS_WARNING(warning) _az_JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(_az_JSON_HEDLEY_VERSION) || (_az_JSON_HEDLEY_VERSION < X) */ + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) +#if defined(__clang__) +#if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 +#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" +#endif +#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) +#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 +#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" +#endif +#endif +#endif + +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 201703L) \ + || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 +#define _az_JSON_HAS_CPP_17 +#define _az_JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) +#define _az_JSON_HAS_CPP_14 +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// disable documentation warnings on clang +#if defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) \ + && !defined(JSON_NOEXCEPTION) +#define _az_JSON_THROW(exception) throw exception +#define _az_JSON_TRY try +#define _az_JSON_CATCH(exception) catch (exception) +#define _az_JSON_INTERNAL_CATCH(exception) catch (exception) +#else +#include +#define _az_JSON_THROW(exception) std::abort() +#define _az_JSON_TRY if (true) +#define _az_JSON_CATCH(exception) if (false) +#define _az_JSON_INTERNAL_CATCH(exception) if (false) +#endif + +// override exception macros +#if defined(_az_JSON_THROW_USER) +#undef _az_JSON_THROW +#define _az_JSON_THROW _az_JSON_THROW_USER +#endif +#if defined(_az_JSON_TRY_USER) +#undef _az_JSON_TRY +#define _az_JSON_TRY _az_JSON_TRY_USER +#endif +#if defined(_az_JSON_CATCH_USER) +#undef _az_JSON_CATCH +#define _az_JSON_CATCH _az_JSON_CATCH_USER +#undef _az_JSON_INTERNAL_CATCH +#define _az_JSON_INTERNAL_CATCH _az_JSON_CATCH_USER +#endif +#if defined(_az_JSON_INTERNAL_CATCH_USER) +#undef _az_JSON_INTERNAL_CATCH +#define _az_JSON_INTERNAL_CATCH _az_JSON_INTERNAL_CATCH_USER +#endif + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define _az_NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template < \ + template \ + class ObjectType, \ + template \ + class ArrayType, \ + class StringType, \ + class BooleanType, \ + class NumberIntegerType, \ + class NumberUnsignedType, \ + class NumberFloatType, \ + template \ + class AllocatorType, \ + template \ + class JSONSerializer, \ + class BinaryType> + +#define _az_NLOHMANN_BASIC_JSON_TPL \ + basic_json< \ + ObjectType, \ + ArrayType, \ + StringType, \ + BooleanType, \ + NumberIntegerType, \ + NumberUnsignedType, \ + NumberFloatType, \ + AllocatorType, \ + JSONSerializer, \ + BinaryType> + +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { //////////////// // exceptions // //////////////// @@ -130,12 +2113,14 @@ namespace nlohmann { namespace detail { class exception : public std::exception { public: /// returns the explanatory string + _az_JSON_HEDLEY_RETURNS_NON_NULL const char* what() const noexcept override { return m.what(); } /// the id of the exception const int id; protected: + _az_JSON_HEDLEY_NON_NULL(3) exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} static std::string name(const std::string& ename, int id_) @@ -327,6 +2312,7 @@ namespace nlohmann { namespace detail { } private: + _az_JSON_HEDLEY_NON_NULL(3) invalid_iterator(int id_, const char* what_arg) : exception(id_, what_arg) {} }; @@ -397,6 +2383,7 @@ namespace nlohmann { namespace detail { } private: + _az_JSON_HEDLEY_NON_NULL(3) type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} }; @@ -449,6 +2436,7 @@ namespace nlohmann { namespace detail { } private: + _az_JSON_HEDLEY_NON_NULL(3) out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} }; @@ -486,186 +2474,21 @@ namespace nlohmann { namespace detail { } private: + _az_JSON_HEDLEY_NON_NULL(3) other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include -#include // pair - -// This file contains all internal macro definitions -// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them - -// exclude unsupported compilers -#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) -#if defined(__clang__) -#if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 -#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" -#endif -#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) -#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 -#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" -#endif -#endif -#endif - -// disable float-equal warnings on GCC/clang -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - -// disable documentation warnings on clang -#if defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdocumentation" -#endif - -// allow for portable deprecation warnings -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) -#define JSON_DEPRECATED __attribute__((deprecated)) -#elif defined(_MSC_VER) -#define JSON_DEPRECATED __declspec(deprecated) -#else -#define JSON_DEPRECATED -#endif - -// allow for portable nodiscard warnings -#if defined(__has_cpp_attribute) -#if __has_cpp_attribute(nodiscard) -#define JSON_NODISCARD [[nodiscard]] -#elif __has_cpp_attribute(gnu::warn_unused_result) -#define JSON_NODISCARD [[gnu::warn_unused_result]] -#else -#define JSON_NODISCARD -#endif -#else -#define JSON_NODISCARD -#endif - -// allow to disable exceptions -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) \ - && !defined(JSON_NOEXCEPTION) -#define JSON_THROW(exception) throw exception -#define JSON_TRY try -#define JSON_CATCH(exception) catch (exception) -#define JSON_INTERNAL_CATCH(exception) catch (exception) -#else -#include -#define JSON_THROW(exception) std::abort() -#define JSON_TRY if (true) -#define JSON_CATCH(exception) if (false) -#define JSON_INTERNAL_CATCH(exception) if (false) -#endif - -// override exception macros -#if defined(JSON_THROW_USER) -#undef JSON_THROW -#define JSON_THROW JSON_THROW_USER -#endif -#if defined(JSON_TRY_USER) -#undef JSON_TRY -#define JSON_TRY JSON_TRY_USER -#endif -#if defined(JSON_CATCH_USER) -#undef JSON_CATCH -#define JSON_CATCH JSON_CATCH_USER -#undef JSON_INTERNAL_CATCH -#define JSON_INTERNAL_CATCH JSON_CATCH_USER -#endif -#if defined(JSON_INTERNAL_CATCH_USER) -#undef JSON_INTERNAL_CATCH -#define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER -#endif - -// manual branch prediction -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) -#define JSON_LIKELY(x) __builtin_expect(x, 1) -#define JSON_UNLIKELY(x) __builtin_expect(x, 0) -#else -#define JSON_LIKELY(x) x -#define JSON_UNLIKELY(x) x -#endif - -// C++ language standard detection -#if (defined(__cplusplus) && __cplusplus >= 201703L) \ - || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 -#define JSON_HAS_CPP_17 -#define JSON_HAS_CPP_14 -#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) -#define JSON_HAS_CPP_14 -#endif - -/*! -@brief macro to briefly define a mapping between an enum and JSON -@def NLOHMANN_JSON_SERIALIZE_ENUM -@since version 3.4.0 -*/ -#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ - template inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if( \ - std::begin(m), \ - std::end(m), \ - [e](const std::pair& ej_pair) -> bool { \ - return ej_pair.first == e; \ - }); \ - j = ((it != std::end(m)) ? it : std::begin(m))->second; \ - } \ - template inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if( \ - std::begin(m), \ - std::end(m), \ - [j](const std::pair& ej_pair) -> bool { \ - return ej_pair.second == j; \ - }); \ - e = ((it != std::end(m)) ? it : std::begin(m))->first; \ - } - -// Ugly macros to avoid uglier copy-paste when specializing basic_json. They -// may be removed in the future once the class is split. - -#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ - template < \ - template \ - class ObjectType, \ - template \ - class ArrayType, \ - class StringType, \ - class BooleanType, \ - class NumberIntegerType, \ - class NumberUnsignedType, \ - class NumberFloatType, \ - template \ - class AllocatorType, \ - template \ - class JSONSerializer> - -#define NLOHMANN_BASIC_JSON_TPL \ - basic_json< \ - ObjectType, \ - ArrayType, \ - StringType, \ - BooleanType, \ - NumberIntegerType, \ - NumberUnsignedType, \ - NumberFloatType, \ - AllocatorType, \ - JSONSerializer> - // #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 -namespace nlohmann { namespace detail { +// #include + +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { // alias templates to reduce boilerplate template using enable_if_t = typename std::enable_if::type; @@ -720,32 +2543,33 @@ namespace nlohmann { namespace detail { }; template constexpr T static_const::value; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include -#include // not #include // numeric_limits #include // false_type, is_constructible, is_integral, is_same, true_type #include // declval +// #include + // #include #include // random_access_iterator_tag // #include -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { template struct make_void { using type = void; }; template using void_t = typename make_void::type; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { template struct iterator_types { }; @@ -786,7 +2610,7 @@ namespace nlohmann { namespace detail { using pointer = T*; using reference = T&; }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include @@ -798,8 +2622,8 @@ namespace nlohmann { namespace detail { // #include -// http://en.cppreference.com/w/cpp/experimental/is_detected -namespace nlohmann { namespace detail { +// https://en.cppreference.com/w/cpp/experimental/is_detected +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { struct nonesuch { nonesuch() = delete; @@ -841,11 +2665,8 @@ namespace nlohmann { namespace detail { template class Op, class... Args> using is_detected_convertible = std::is_convertible, To>; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail -// #include -#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ -#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ #include // int64_t, uint64_t #include // map @@ -858,435 +2679,475 @@ namespace nlohmann { namespace detail { @see https://github.com/nlohmann @since version 1.0.0 */ -namespace nlohmann { -/*! -@brief default JSONSerializer template argument +namespace Azure { namespace Core { namespace Internal { namespace Json { + /*! + @brief default JSONSerializer template argument + + This serializer ignores the template arguments and uses ADL + ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) + for serialization. + */ + template struct adl_serializer; -This serializer ignores the template arguments and uses ADL -([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) -for serialization. -*/ -template struct adl_serializer; + template < + template class ObjectType = std::map, + template class ArrayType = std::vector, + class StringType = std::string, + class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = adl_serializer, + class BinaryType = std::vector> + class basic_json; -template < - template class ObjectType = std::map, - template class ArrayType = std::vector, - class StringType = std::string, - class BooleanType = bool, - class NumberIntegerType = std::int64_t, - class NumberUnsignedType = std::uint64_t, - class NumberFloatType = double, - template class AllocatorType = std::allocator, - template class JSONSerializer = adl_serializer> -class basic_json; + /*! + @brief JSON Pointer -/*! -@brief JSON Pointer + A JSON pointer defines a string syntax for identifying a specific value + within a JSON document. It can be used with functions `at` and + `operator[]`. Furthermore, JSON pointers are the base for JSON patches. -A JSON pointer defines a string syntax for identifying a specific value -within a JSON document. It can be used with functions `at` and -`operator[]`. Furthermore, JSON pointers are the base for JSON patches. + @sa [RFC 6901](https://tools.ietf.org/html/rfc6901) -@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + @since version 2.0.0 + */ + template class json_pointer; -@since version 2.0.0 -*/ -template class json_pointer; + /*! + @brief default JSON class -/*! -@brief default JSON class + This type is the default specialization of the @ref basic_json class which + uses the standard template types. -This type is the default specialization of the @ref basic_json class which -uses the standard template types. + @since version 1.0.0 + */ + using json = basic_json<>; +}}}} // namespace Azure::Core::Internal::Json -@since version 1.0.0 -*/ -using json = basic_json<>; -} // namespace nlohmann - -#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ - -namespace nlohmann { -/*! -@brief detail namespace with internal helper functions - -This namespace collects functions that should not be exposed, -implementations of some @ref basic_json methods, and meta-programming helpers. - -@since version 2.1.0 -*/ -namespace detail { - ///////////// - // helpers // - ///////////// - - // Note to maintainers: - // - // Every trait in this file expects a non CV-qualified type. - // The only exceptions are in the 'aliases for detected' section - // (i.e. those of the form: decltype(T::member_function(std::declval()))) - // - // In this case, T has to be properly CV-qualified to constraint the function arguments - // (e.g. to_json(BasicJsonType&, const T&)) - - template struct is_basic_json : std::false_type - { - }; - - NLOHMANN_BASIC_JSON_TPL_DECLARATION - struct is_basic_json : std::true_type - { - }; - - ////////////////////////// - // aliases for detected // - ////////////////////////// - - template using mapped_type_t = typename T::mapped_type; - - template using key_type_t = typename T::key_type; - - template using value_type_t = typename T::value_type; - - template using difference_type_t = typename T::difference_type; - - template using pointer_t = typename T::pointer; - - template using reference_t = typename T::reference; - - template using iterator_category_t = typename T::iterator_category; - - template using iterator_t = typename T::iterator; - - template - using to_json_function = decltype(T::to_json(std::declval()...)); - - template - using from_json_function = decltype(T::from_json(std::declval()...)); - - template - using get_template_function = decltype(std::declval().template get()); - - // trait checking if JSONSerializer::from_json(json const&, udt&) exists - template - struct has_from_json : std::false_type - { - }; - - template - struct has_from_json::value>> - { - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value - = is_detected_exact::value; - }; - - // This trait checks if JSONSerializer::from_json(json const&) exists - // this overload is used for non-default-constructible user-defined-types - template - struct has_non_default_from_json : std::false_type - { - }; - - template - struct has_non_default_from_json::value>> - { - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value - = is_detected_exact::value; - }; - - // This trait checks if BasicJsonType::json_serializer::to_json exists - // Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite - // recursion. - template - struct has_to_json : std::false_type - { - }; - - template - struct has_to_json::value>> - { - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value - = is_detected_exact::value; - }; - - /////////////////// - // is_ functions // - /////////////////// - - template struct is_iterator_traits : std::false_type - { - }; - - template struct is_iterator_traits> - { - private: - using traits = iterator_traits; - - public: - static constexpr auto value = is_detected::value - && is_detected::value && is_detected::value - && is_detected::value - && is_detected::value; - }; - - // source: https://stackoverflow.com/a/37193089/4116453 - - template struct is_complete_type : std::false_type - { - }; - - template struct is_complete_type : std::true_type - { - }; - - template - struct is_compatible_object_type_impl : std::false_type - { - }; - - template - struct is_compatible_object_type_impl< - BasicJsonType, - CompatibleObjectType, - enable_if_t< - is_detected::value - and is_detected::value>> - { - - using object_t = typename BasicJsonType::object_t; - - // macOS's is_constructible does not play well with nonesuch... - static constexpr bool value = std::is_constructible< - typename object_t::key_type, - typename CompatibleObjectType::key_type>::value - and std::is_constructible::value; - }; - - template - struct is_compatible_object_type - : is_compatible_object_type_impl - { - }; - - template - struct is_constructible_object_type_impl : std::false_type - { - }; - - template - struct is_constructible_object_type_impl< - BasicJsonType, - ConstructibleObjectType, - enable_if_t< - is_detected::value - and is_detected::value>> - { - using object_t = typename BasicJsonType::object_t; - - static constexpr bool value = (std::is_constructible< - typename ConstructibleObjectType::key_type, - typename object_t::key_type>::value - and std::is_same< - typename object_t::mapped_type, - typename ConstructibleObjectType::mapped_type>::value) - or (has_from_json::value - or has_non_default_from_json< - BasicJsonType, - typename ConstructibleObjectType::mapped_type>::value); - }; - - template - struct is_constructible_object_type - : is_constructible_object_type_impl - { - }; - - template - struct is_compatible_string_type_impl : std::false_type - { - }; - - template - struct is_compatible_string_type_impl< - BasicJsonType, - CompatibleStringType, - enable_if_t::value>> - { - static constexpr auto value - = std::is_constructible::value; - }; - - template - struct is_compatible_string_type - : is_compatible_string_type_impl - { - }; - - template - struct is_constructible_string_type_impl : std::false_type - { - }; - - template - struct is_constructible_string_type_impl< - BasicJsonType, - ConstructibleStringType, - enable_if_t::value>> - { - static constexpr auto value - = std::is_constructible::value; - }; - - template - struct is_constructible_string_type - : is_constructible_string_type_impl - { - }; - - template - struct is_compatible_array_type_impl : std::false_type - { - }; - - template - struct is_compatible_array_type_impl< - BasicJsonType, - CompatibleArrayType, - enable_if_t< - is_detected::value - and is_detected::value and - // This is needed because json_reverse_iterator has a ::iterator type... - // Therefore it is detected as a CompatibleArrayType. - // The real fix would be to have an Iterable concept. - not is_iterator_traits>::value>> - { - static constexpr bool value - = std::is_constructible::value; - }; - - template - struct is_compatible_array_type - : is_compatible_array_type_impl - { - }; - - template - struct is_constructible_array_type_impl : std::false_type - { - }; - - template - struct is_constructible_array_type_impl< - BasicJsonType, - ConstructibleArrayType, - enable_if_t::value>> - : std::true_type - { - }; - - template - struct is_constructible_array_type_impl< - BasicJsonType, - ConstructibleArrayType, - enable_if_t< - not std::is_same::value - and is_detected::value - and is_detected::value - and is_complete_type>::value>> - { - static constexpr bool value = - // This is needed because json_reverse_iterator has a ::iterator type, - // furthermore, std::back_insert_iterator (and other iterators) have a base class - // `iterator`... Therefore it is detected as a ConstructibleArrayType. The real fix would be - // to have an Iterable concept. - not is_iterator_traits>::value and - - (std::is_same< - typename ConstructibleArrayType::value_type, - typename BasicJsonType::array_t::value_type>::value - or has_from_json::value - or has_non_default_from_json:: - value); - }; - - template - struct is_constructible_array_type - : is_constructible_array_type_impl - { - }; - - template - struct is_compatible_integer_type_impl : std::false_type - { - }; - - template - struct is_compatible_integer_type_impl< - RealIntegerType, - CompatibleNumberIntegerType, - enable_if_t< - std::is_integral::value - and std::is_integral::value - and not std::is_same::value>> - { - // is there an assert somewhere on overflows? - using RealLimits = std::numeric_limits; - using CompatibleLimits = std::numeric_limits; - - static constexpr auto value - = std::is_constructible::value - and CompatibleLimits::is_integer and RealLimits::is_signed == CompatibleLimits::is_signed; - }; - - template - struct is_compatible_integer_type - : is_compatible_integer_type_impl - { - }; - - template - struct is_compatible_type_impl : std::false_type - { - }; - - template - struct is_compatible_type_impl< - BasicJsonType, - CompatibleType, - enable_if_t::value>> - { - static constexpr bool value = has_to_json::value; - }; - - template - struct is_compatible_type : is_compatible_type_impl - { - }; -} // namespace detail -} // namespace nlohmann +namespace Azure { namespace Core { namespace Internal { namespace Json { + /*! + @brief detail namespace with internal helper functions + + This namespace collects functions that should not be exposed, + implementations of some @ref basic_json methods, and meta-programming helpers. + + @since version 2.1.0 + */ + namespace detail { + ///////////// + // helpers // + ///////////// + + // Note to maintainers: + // + // Every trait in this file expects a non CV-qualified type. + // The only exceptions are in the 'aliases for detected' section + // (i.e. those of the form: decltype(T::member_function(std::declval()))) + // + // In this case, T has to be properly CV-qualified to constraint the function arguments + // (e.g. to_json(BasicJsonType&, const T&)) + + template struct is_basic_json : std::false_type + { + }; + + _az_NLOHMANN_BASIC_JSON_TPL_DECLARATION + struct is_basic_json<_az_NLOHMANN_BASIC_JSON_TPL> : std::true_type + { + }; + + ////////////////////// + // json_ref helpers // + ////////////////////// + + template class json_ref; + + template struct is_json_ref : std::false_type + { + }; + + template struct is_json_ref> : std::true_type + { + }; + + ////////////////////////// + // aliases for detected // + ////////////////////////// + + template using mapped_type_t = typename T::mapped_type; + + template using key_type_t = typename T::key_type; + + template using value_type_t = typename T::value_type; + + template using difference_type_t = typename T::difference_type; + + template using pointer_t = typename T::pointer; + + template using reference_t = typename T::reference; + + template using iterator_category_t = typename T::iterator_category; + + template using iterator_t = typename T::iterator; + + template + using to_json_function = decltype(T::to_json(std::declval()...)); + + template + using from_json_function = decltype(T::from_json(std::declval()...)); + + template + using get_template_function = decltype(std::declval().template get()); + + // trait checking if JSONSerializer::from_json(json const&, udt&) exists + template + struct has_from_json : std::false_type + { + }; + + template + struct has_from_json::value>> + { + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value + = is_detected_exact:: + value; + }; + + // This trait checks if JSONSerializer::from_json(json const&) exists + // this overload is used for non-default-constructible user-defined-types + template + struct has_non_default_from_json : std::false_type + { + }; + + template + struct has_non_default_from_json::value>> + { + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value + = is_detected_exact::value; + }; + + // This trait checks if BasicJsonType::json_serializer::to_json exists + // Do not evaluate the trait when T is a basic_json type, to avoid template instantiation + // infinite recursion. + template + struct has_to_json : std::false_type + { + }; + + template + struct has_to_json::value>> + { + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value + = is_detected_exact::value; + }; + + /////////////////// + // is_ functions // + /////////////////// + + template struct is_iterator_traits : std::false_type + { + }; + + template struct is_iterator_traits> + { + private: + using traits = iterator_traits; + + public: + static constexpr auto value = is_detected::value + && is_detected::value && is_detected::value + && is_detected::value + && is_detected::value; + }; + + // source: https://stackoverflow.com/a/37193089/4116453 + + template struct is_complete_type : std::false_type + { + }; + + template struct is_complete_type : std::true_type + { + }; + + template + struct is_compatible_object_type_impl : std::false_type + { + }; + + template + struct is_compatible_object_type_impl< + BasicJsonType, + CompatibleObjectType, + enable_if_t< + is_detected::value + and is_detected::value>> + { + + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = std::is_constructible< + typename object_t::key_type, + typename CompatibleObjectType::key_type>::value + and std::is_constructible::value; + }; + + template + struct is_compatible_object_type + : is_compatible_object_type_impl + { + }; + + template + struct is_constructible_object_type_impl : std::false_type + { + }; + + template + struct is_constructible_object_type_impl< + BasicJsonType, + ConstructibleObjectType, + enable_if_t< + is_detected::value + and is_detected::value>> + { + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value + = (std::is_default_constructible::value + and (std::is_move_assignable::value or std::is_copy_assignable::value) + and (std::is_constructible::value and std::is_same::value)) + or (has_from_json::value + or has_non_default_from_json< + BasicJsonType, + typename ConstructibleObjectType::mapped_type>::value); + }; + + template + struct is_constructible_object_type + : is_constructible_object_type_impl + { + }; + + template + struct is_compatible_string_type_impl : std::false_type + { + }; + + template + struct is_compatible_string_type_impl< + BasicJsonType, + CompatibleStringType, + enable_if_t::value>> + { + static constexpr auto value + = std::is_constructible::value; + }; + + template + struct is_compatible_string_type + : is_compatible_string_type_impl + { + }; + + template + struct is_constructible_string_type_impl : std::false_type + { + }; + + template + struct is_constructible_string_type_impl< + BasicJsonType, + ConstructibleStringType, + enable_if_t::value>> + { + static constexpr auto value + = std::is_constructible::value; + }; + + template + struct is_constructible_string_type + : is_constructible_string_type_impl + { + }; + + template + struct is_compatible_array_type_impl : std::false_type + { + }; + + template + struct is_compatible_array_type_impl< + BasicJsonType, + CompatibleArrayType, + enable_if_t< + is_detected::value + and is_detected::value and + // This is needed because json_reverse_iterator has a ::iterator type... + // Therefore it is detected as a CompatibleArrayType. + // The real fix would be to have an Iterable concept. + not is_iterator_traits>::value>> + { + static constexpr bool value + = std::is_constructible::value; + }; + + template + struct is_compatible_array_type + : is_compatible_array_type_impl + { + }; + + template + struct is_constructible_array_type_impl : std::false_type + { + }; + + template + struct is_constructible_array_type_impl< + BasicJsonType, + ConstructibleArrayType, + enable_if_t< + std::is_same::value>> + : std::true_type + { + }; + + template + struct is_constructible_array_type_impl< + BasicJsonType, + ConstructibleArrayType, + enable_if_t< + not std::is_same::value + and std::is_default_constructible::value + and (std::is_move_assignable::value or std::is_copy_assignable::value) + and is_detected::value + and is_detected::value + and is_complete_type>::value>> + { + static constexpr bool value = + // This is needed because json_reverse_iterator has a ::iterator type, + // furthermore, std::back_insert_iterator (and other iterators) have a + // base class `iterator`... Therefore it is detected as a + // ConstructibleArrayType. The real fix would be to have an Iterable + // concept. + not is_iterator_traits>::value and + + (std::is_same< + typename ConstructibleArrayType::value_type, + typename BasicJsonType::array_t::value_type>::value + or has_from_json::value + or has_non_default_from_json< + BasicJsonType, + typename ConstructibleArrayType::value_type>::value); + }; + + template + struct is_constructible_array_type + : is_constructible_array_type_impl + { + }; + + template + struct is_compatible_integer_type_impl : std::false_type + { + }; + + template + struct is_compatible_integer_type_impl< + RealIntegerType, + CompatibleNumberIntegerType, + enable_if_t< + std::is_integral::value + and std::is_integral::value + and not std::is_same::value>> + { + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value + = std::is_constructible::value + and CompatibleLimits::is_integer and RealLimits::is_signed == CompatibleLimits::is_signed; + }; + + template + struct is_compatible_integer_type + : is_compatible_integer_type_impl + { + }; + + template + struct is_compatible_type_impl : std::false_type + { + }; + + template + struct is_compatible_type_impl< + BasicJsonType, + CompatibleType, + enable_if_t::value>> + { + static constexpr bool value = has_to_json::value; + }; + + template + struct is_compatible_type : is_compatible_type_impl + { + }; + + // https://en.cppreference.com/w/cpp/types/conjunction + template struct conjunction : std::true_type + { + }; + template struct conjunction : B1 + { + }; + template + struct conjunction : std::conditional, B1>::type + { + }; + + template struct is_constructible_tuple : std::false_type + { + }; + + template + struct is_constructible_tuple> + : conjunction...> + { + }; + } // namespace detail +}}}} // namespace Azure::Core::Internal::Json // #include #include // array -#include // and #include // size_t #include // uint8_t #include // string -namespace nlohmann { namespace detail { +// #include + +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { /////////////////////////// // JSON type enumeration // /////////////////////////// @@ -1325,22 +3186,26 @@ namespace nlohmann { namespace detail { number_integer, ///< number value (signed integer) number_unsigned, ///< number value (unsigned integer) number_float, ///< number value (floating-point) - discarded ///< discarded by the the parser callback function + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function }; /*! @brief comparison operator for JSON types Returns an ordering that is similar to Python: - - order: null < boolean < number < object < array < string + - order: null < boolean < number < object < array < string < binary - furthermore, each type is not smaller than itself - discarded values are not comparable + - binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. @since version 1.0.0 */ inline bool operator<(const value_t lhs, const value_t rhs) noexcept { - static constexpr std::array order = {{ + static constexpr std::array order = {{ 0 /* null */, 3 /* object */, 4 /* array */, @@ -1348,413 +3213,447 @@ namespace nlohmann { namespace detail { 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, - 2 /* float */ + 2 /* float */, + 6 /* binary */ }}; const auto l_index = static_cast(lhs); const auto r_index = static_cast(rhs); return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index]; } -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail -namespace nlohmann { -namespace detail { - template - void from_json(const BasicJsonType& j, typename std::nullptr_t& n) - { - if (JSON_UNLIKELY(not j.is_null())) +namespace Azure { namespace Core { namespace Internal { namespace Json { + namespace detail { + template + void from_json(const BasicJsonType& j, typename std::nullptr_t& n) { - JSON_THROW( - type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); - } - n = nullptr; - } - - // overloads for basic_json template parameters - template < - typename BasicJsonType, - typename ArithmeticType, - enable_if_t< - std::is_arithmetic::value - and not std::is_same::value, - int> = 0> - void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) - { - switch (static_cast(j)) - { - case value_t::number_unsigned: { - val = static_cast( - *j.template get_ptr()); - break; - } - case value_t::number_integer: { - val = static_cast( - *j.template get_ptr()); - break; - } - case value_t::number_float: { - val = static_cast( - *j.template get_ptr()); - break; - } - - default: - JSON_THROW( - type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); - } - } - - template - void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) - { - if (JSON_UNLIKELY(not j.is_boolean())) - { - JSON_THROW( - type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); - } - b = *j.template get_ptr(); - } - - template - void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) - { - if (JSON_UNLIKELY(not j.is_string())) - { - JSON_THROW( - type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); - } - s = *j.template get_ptr(); - } - - template < - typename BasicJsonType, - typename ConstructibleStringType, - enable_if_t< - is_constructible_string_type::value - and not std::is_same:: - value, - int> = 0> - void from_json(const BasicJsonType& j, ConstructibleStringType& s) - { - if (JSON_UNLIKELY(not j.is_string())) - { - JSON_THROW( - type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); - } - - s = *j.template get_ptr(); - } - - template - void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) - { - get_arithmetic_value(j, val); - } - - template - void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) - { - get_arithmetic_value(j, val); - } - - template - void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) - { - get_arithmetic_value(j, val); - } - - template < - typename BasicJsonType, - typename EnumType, - enable_if_t::value, int> = 0> - void from_json(const BasicJsonType& j, EnumType& e) - { - typename std::underlying_type::type val; - get_arithmetic_value(j, val); - e = static_cast(val); - } - - // forward_list doesn't have an insert method - template < - typename BasicJsonType, - typename T, - typename Allocator, - enable_if_t::value, int> = 0> - void from_json(const BasicJsonType& j, std::forward_list& l) - { - if (JSON_UNLIKELY(not j.is_array())) - { - JSON_THROW( - type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); - } - std::transform(j.rbegin(), j.rend(), std::front_inserter(l), [](const BasicJsonType& i) { - return i.template get(); - }); - } - - // valarray doesn't have an insert method - template < - typename BasicJsonType, - typename T, - enable_if_t::value, int> = 0> - void from_json(const BasicJsonType& j, std::valarray& l) - { - if (JSON_UNLIKELY(not j.is_array())) - { - JSON_THROW( - type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); - } - l.resize(j.size()); - std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l)); - } - - template - void from_json_array_impl( - const BasicJsonType& j, - typename BasicJsonType::array_t& arr, - priority_tag<3> /*unused*/) - { - arr = *j.template get_ptr(); - } - - template - auto from_json_array_impl( - const BasicJsonType& j, - std::array& arr, - priority_tag<2> /*unused*/) -> decltype(j.template get(), void()) - { - for (std::size_t i = 0; i < N; ++i) - { - arr[i] = j.at(i).template get(); - } - } - - template - auto from_json_array_impl( - const BasicJsonType& j, - ConstructibleArrayType& arr, - priority_tag<1> /*unused*/) - -> decltype( - arr.reserve(std::declval()), - j.template get(), - void()) - { - using std::end; - - arr.reserve(j.size()); - std::transform(j.begin(), j.end(), std::inserter(arr, end(arr)), [](const BasicJsonType& i) { - // get() returns *this, this won't call a from_json - // method when value_type is BasicJsonType - return i.template get(); - }); - } - - template - void from_json_array_impl( - const BasicJsonType& j, - ConstructibleArrayType& arr, - priority_tag<0> /*unused*/) - { - using std::end; - - std::transform(j.begin(), j.end(), std::inserter(arr, end(arr)), [](const BasicJsonType& i) { - // get() returns *this, this won't call a from_json - // method when value_type is BasicJsonType - return i.template get(); - }); - } - - template < - typename BasicJsonType, - typename ConstructibleArrayType, - enable_if_t< - is_constructible_array_type::value - and not is_constructible_object_type::value - and not is_constructible_string_type::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(), - void()) - { - if (JSON_UNLIKELY(not j.is_array())) - { - JSON_THROW( - type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); - } - - from_json_array_impl(j, arr, priority_tag<3>{}); - } - - template < - typename BasicJsonType, - typename ConstructibleObjectType, - enable_if_t< - is_constructible_object_type::value, - int> = 0> - void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) - { - if (JSON_UNLIKELY(not j.is_object())) - { - JSON_THROW( - type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); - } - - auto inner_object = j.template get_ptr(); - using value_type = typename ConstructibleObjectType::value_type; - std::transform( - inner_object->begin(), - inner_object->end(), - std::inserter(obj, obj.begin()), - [](typename BasicJsonType::object_t::value_type const& p) { - return value_type( - p.first, p.second.template get()); - }); - } - - // overload for arithmetic types, not chosen for basic_json template arguments - // (BooleanType, etc..); note: Is it really necessary to provide explicit - // overloads for boolean_t etc. in case of a custom BooleanType which is not - // an arithmetic type? - template < - typename BasicJsonType, - typename ArithmeticType, - enable_if_t< - std::is_arithmetic::value - and not std::is_same::value - and not std::is_same::value - and not std::is_same::value - and not std::is_same::value, - int> = 0> - void from_json(const BasicJsonType& j, ArithmeticType& val) - { - switch (static_cast(j)) - { - case value_t::number_unsigned: { - val = static_cast( - *j.template get_ptr()); - break; - } - case value_t::number_integer: { - val = static_cast( - *j.template get_ptr()); - break; - } - case value_t::number_float: { - val = static_cast( - *j.template get_ptr()); - break; - } - case value_t::boolean: { - val = static_cast( - *j.template get_ptr()); - break; - } - - default: - JSON_THROW( - type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); - } - } - - template - void from_json(const BasicJsonType& j, std::pair& p) - { - p = {j.at(0).template get(), j.at(1).template get()}; - } - - template - void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) - { - t = std::make_tuple(j.at(Idx).template get::type>()...); - } - - template - void from_json(const BasicJsonType& j, std::tuple& t) - { - from_json_tuple_impl(j, t, index_sequence_for{}); - } - - template < - typename BasicJsonType, - typename Key, - typename Value, - typename Compare, - typename Allocator, - typename - = enable_if_t::value>> - void from_json(const BasicJsonType& j, std::map& m) - { - if (JSON_UNLIKELY(not j.is_array())) - { - JSON_THROW( - type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); - } - for (const auto& p : j) - { - if (JSON_UNLIKELY(not p.is_array())) + if (_az_JSON_HEDLEY_UNLIKELY(not j.is_null())) { - JSON_THROW( - type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + _az_JSON_THROW( + type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); } - m.emplace(p.at(0).template get(), p.at(1).template get()); + n = nullptr; } - } - template < - typename BasicJsonType, - typename Key, - typename Value, - typename Hash, - typename KeyEqual, - typename Allocator, - typename - = enable_if_t::value>> - void from_json( - const BasicJsonType& j, - std::unordered_map& m) - { - if (JSON_UNLIKELY(not j.is_array())) + // overloads for basic_json template parameters + template < + typename BasicJsonType, + typename ArithmeticType, + enable_if_t< + std::is_arithmetic::value + and not std::is_same::value, + int> = 0> + void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) { - JSON_THROW( - type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); - } - for (const auto& p : j) - { - if (JSON_UNLIKELY(not p.is_array())) + switch (static_cast(j)) { - JSON_THROW( - type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + case value_t::number_unsigned: { + val = static_cast( + *j.template get_ptr()); + break; + } + case value_t::number_integer: { + val = static_cast( + *j.template get_ptr()); + break; + } + case value_t::number_float: { + val = static_cast( + *j.template get_ptr()); + break; + } + + default: + _az_JSON_THROW( + type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); } - m.emplace(p.at(0).template get(), p.at(1).template get()); } - } - struct from_json_fn - { - template - auto operator()(const BasicJsonType& j, T& val) const noexcept(noexcept(from_json(j, val))) - -> decltype(from_json(j, val), void()) + template + void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) { - return from_json(j, val); + if (_az_JSON_HEDLEY_UNLIKELY(not j.is_boolean())) + { + _az_JSON_THROW( + type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); + } + b = *j.template get_ptr(); } - }; -} // namespace detail -/// namespace to hold default `from_json` function -/// to see why this is required: -/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html -namespace { - constexpr const auto& from_json = detail::static_const::value; -} // namespace -} // namespace nlohmann + template + void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) + { + if (_az_JSON_HEDLEY_UNLIKELY(not j.is_string())) + { + _az_JSON_THROW( + type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } + s = *j.template get_ptr(); + } + + template < + typename BasicJsonType, + typename ConstructibleStringType, + enable_if_t< + is_constructible_string_type::value + and not std::is_same:: + value, + int> = 0> + void from_json(const BasicJsonType& j, ConstructibleStringType& s) + { + if (_az_JSON_HEDLEY_UNLIKELY(not j.is_string())) + { + _az_JSON_THROW( + type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } + + s = *j.template get_ptr(); + } + + template + void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) + { + get_arithmetic_value(j, val); + } + + template + void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) + { + get_arithmetic_value(j, val); + } + + template + void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) + { + get_arithmetic_value(j, val); + } + + template < + typename BasicJsonType, + typename EnumType, + enable_if_t::value, int> = 0> + void from_json(const BasicJsonType& j, EnumType& e) + { + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); + } + + // forward_list doesn't have an insert method + template < + typename BasicJsonType, + typename T, + typename Allocator, + enable_if_t::value, int> = 0> + void from_json(const BasicJsonType& j, std::forward_list& l) + { + if (_az_JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + _az_JSON_THROW( + type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.clear(); + std::transform(j.rbegin(), j.rend(), std::front_inserter(l), [](const BasicJsonType& i) { + return i.template get(); + }); + } + + // valarray doesn't have an insert method + template < + typename BasicJsonType, + typename T, + enable_if_t::value, int> = 0> + void from_json(const BasicJsonType& j, std::valarray& l) + { + if (_az_JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + _az_JSON_THROW( + type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.resize(j.size()); + std::copy(j.begin(), j.end(), std::begin(l)); + } + + template + auto from_json(const BasicJsonType& j, T (&arr)[N]) -> decltype(j.template get(), void()) + { + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } + } + + template + void from_json_array_impl( + const BasicJsonType& j, + typename BasicJsonType::array_t& arr, + priority_tag<3> /*unused*/) + { + arr = *j.template get_ptr(); + } + + template + auto from_json_array_impl( + const BasicJsonType& j, + std::array& arr, + priority_tag<2> /*unused*/) -> decltype(j.template get(), void()) + { + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } + } + + template + auto from_json_array_impl( + const BasicJsonType& j, + ConstructibleArrayType& arr, + priority_tag<1> /*unused*/) + -> decltype( + arr.reserve(std::declval()), + j.template get(), + void()) + { + using std::end; + + ConstructibleArrayType ret; + ret.reserve(j.size()); + std::transform(j.begin(), j.end(), std::inserter(ret, end(ret)), [](const BasicJsonType& i) { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); + } + + template + void from_json_array_impl( + const BasicJsonType& j, + ConstructibleArrayType& arr, + priority_tag<0> /*unused*/) + { + using std::end; + + ConstructibleArrayType ret; + std::transform(j.begin(), j.end(), std::inserter(ret, end(ret)), [](const BasicJsonType& i) { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); + } + + template < + typename BasicJsonType, + typename ConstructibleArrayType, + enable_if_t< + is_constructible_array_type::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(), + void()) + { + if (_az_JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + _az_JSON_THROW( + type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + + from_json_array_impl(j, arr, priority_tag<3>{}); + } + + template + void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) + { + if (_az_JSON_HEDLEY_UNLIKELY(not j.is_binary())) + { + _az_JSON_THROW( + type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); + } + + bin = *j.template get_ptr(); + } + + template < + typename BasicJsonType, + typename ConstructibleObjectType, + enable_if_t< + is_constructible_object_type::value, + int> = 0> + void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) + { + if (_az_JSON_HEDLEY_UNLIKELY(not j.is_object())) + { + _az_JSON_THROW( + type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); + } + + ConstructibleObjectType ret; + auto inner_object = j.template get_ptr(); + using value_type = typename ConstructibleObjectType::value_type; + std::transform( + inner_object->begin(), + inner_object->end(), + std::inserter(ret, ret.begin()), + [](typename BasicJsonType::object_t::value_type const& p) { + return value_type( + p.first, p.second.template get()); + }); + obj = std::move(ret); + } + + // overload for arithmetic types, not chosen for basic_json template arguments + // (BooleanType, etc..); note: Is it really necessary to provide explicit + // overloads for boolean_t etc. in case of a custom BooleanType which is not + // an arithmetic type? + template < + typename BasicJsonType, + typename ArithmeticType, + enable_if_t< + std::is_arithmetic::value + and not std::is_same:: + value + and not std::is_same:: + value + and not std::is_same::value + and not std::is_same::value, + int> = 0> + void from_json(const BasicJsonType& j, ArithmeticType& val) + { + switch (static_cast(j)) + { + case value_t::number_unsigned: { + val = static_cast( + *j.template get_ptr()); + break; + } + case value_t::number_integer: { + val = static_cast( + *j.template get_ptr()); + break; + } + case value_t::number_float: { + val = static_cast( + *j.template get_ptr()); + break; + } + case value_t::boolean: { + val = static_cast( + *j.template get_ptr()); + break; + } + + default: + _az_JSON_THROW( + type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } + } + + template + void from_json(const BasicJsonType& j, std::pair& p) + { + p = {j.at(0).template get(), j.at(1).template get()}; + } + + template + void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) + { + t = std::make_tuple( + j.at(Idx).template get::type>()...); + } + + template + void from_json(const BasicJsonType& j, std::tuple& t) + { + from_json_tuple_impl(j, t, index_sequence_for{}); + } + + template < + typename BasicJsonType, + typename Key, + typename Value, + typename Compare, + typename Allocator, + typename + = enable_if_t::value>> + void from_json(const BasicJsonType& j, std::map& m) + { + if (_az_JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + _az_JSON_THROW( + type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (_az_JSON_HEDLEY_UNLIKELY(not p.is_array())) + { + _az_JSON_THROW( + type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } + } + + template < + typename BasicJsonType, + typename Key, + typename Value, + typename Hash, + typename KeyEqual, + typename Allocator, + typename + = enable_if_t::value>> + void from_json( + const BasicJsonType& j, + std::unordered_map& m) + { + if (_az_JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + _az_JSON_THROW( + type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (_az_JSON_HEDLEY_UNLIKELY(not p.is_array())) + { + _az_JSON_THROW( + type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } + } + + struct from_json_fn + { + template + auto operator()(const BasicJsonType& j, T& val) const noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) + { + return from_json(j, val); + } + }; + } // namespace detail + + /// namespace to hold default `from_json` function + /// to see why this is required: + /// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html + namespace { + constexpr const auto& from_json = detail::static_const::value; + } // namespace +}}}} // namespace Azure::Core::Internal::Json // #include #include // copy -#include // or, and, not #include // begin, end #include // string #include // tuple, get @@ -1763,6 +3662,8 @@ namespace { #include // valarray #include // vector +// #include + // #include #include // size_t @@ -1774,7 +3675,11 @@ namespace { // #include -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { + template void int_to_string(string_type& target, std::size_t value) + { + target = std::to_string(value); + } template class iteration_proxy_value { public: using difference_type = std::ptrdiff_t; @@ -1782,6 +3687,8 @@ namespace nlohmann { namespace detail { using pointer = value_type*; using reference = value_type&; using iterator_category = std::input_iterator_tag; + using string_type = typename std::remove_cv< + typename std::remove_reference().key())>::type>::type; private: /// the iterator @@ -1791,9 +3698,9 @@ namespace nlohmann { namespace detail { /// last stringified array index mutable std::size_t array_index_last = 0; /// a string representation of the array index - mutable std::string array_index_str = "0"; + mutable string_type array_index_str = "0"; /// an empty string (to return a reference for primitive values) - const std::string empty_str = ""; + const string_type empty_str = ""; public: explicit iteration_proxy_value(IteratorType it) noexcept : anchor(it) {} @@ -1817,7 +3724,7 @@ namespace nlohmann { namespace detail { bool operator!=(const iteration_proxy_value& o) const { return anchor != o.anchor; } /// return key of the iterator - const std::string& key() const + const string_type& key() const { assert(anchor.m_object != nullptr); @@ -1827,7 +3734,7 @@ namespace nlohmann { namespace detail { case value_t::array: { if (array_index != array_index_last) { - array_index_str = std::to_string(array_index); + int_to_string(array_index_str, array_index); array_index_last = array_index; } return array_index_str; @@ -1873,7 +3780,8 @@ namespace nlohmann { namespace detail { // For further reference see https://blog.tartanllama.xyz/structured-bindings/ // And see https://github.com/nlohmann/json/pull/1391 template = 0> - auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) + auto get(const Azure::Core::Internal::Json::detail::iteration_proxy_value& i) + -> decltype(i.key()) { return i.key(); } @@ -1881,11 +3789,12 @@ namespace nlohmann { namespace detail { // For further reference see https://blog.tartanllama.xyz/structured-bindings/ // And see https://github.com/nlohmann/json/pull/1391 template = 0> - auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) + auto get(const Azure::Core::Internal::Json::detail::iteration_proxy_value& i) + -> decltype(i.value()) { return i.value(); } -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // The Addition to the STD Namespace is required to add // Structured Bindings Support to the iteration_proxy_value class @@ -1898,15 +3807,15 @@ namespace std { #pragma clang diagnostic ignored "-Wmismatched-tags" #endif template -class tuple_size<::nlohmann::detail::iteration_proxy_value> +class tuple_size<::Azure::Core::Internal::Json::detail::iteration_proxy_value> : public std::integral_constant { }; template -class tuple_element> { +class tuple_element> { public: - using type - = decltype(get(std::declval<::nlohmann::detail::iteration_proxy_value>())); + using type = decltype(get( + std::declval<::Azure::Core::Internal::Json::detail::iteration_proxy_value>())); }; #if defined(__clang__) #pragma clang diagnostic pop @@ -1919,415 +3828,618 @@ public: // #include -namespace nlohmann { -namespace detail { - ////////////////// - // constructors // - ////////////////// +namespace Azure { namespace Core { namespace Internal { namespace Json { + namespace detail { + ////////////////// + // constructors // + ////////////////// - template struct external_constructor; + template struct external_constructor; - template <> struct external_constructor - { - template - static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + template <> struct external_constructor { - j.m_type = value_t::boolean; - j.m_value = b; - j.assert_invariant(); - } - }; + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } + }; - template <> struct external_constructor - { - template - static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + template <> struct external_constructor { - j.m_type = value_t::string; - j.m_value = s; - j.assert_invariant(); - } + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } - template - static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + template + static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + { + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } + + template < + typename BasicJsonType, + typename CompatibleStringType, + enable_if_t< + not std::is_same::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleStringType& str) + { + j.m_type = value_t::string; + j.m_value.string = j.template create(str); + j.assert_invariant(); + } + }; + + template <> struct external_constructor { - j.m_type = value_t::string; - j.m_value = std::move(s); - j.assert_invariant(); + template + static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) + { + j.m_type = value_t::binary; + typename BasicJsonType::binary_t value{b}; + j.m_value = value; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) + { + j.m_type = value_t::binary; + typename BasicJsonType::binary_t value{std::move(b)}; + j.m_value = value; + j.assert_invariant(); + } + }; + + template <> struct external_constructor + { + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } + }; + + template <> struct external_constructor + { + template + static void construct( + BasicJsonType& j, + typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } + }; + + template <> struct external_constructor + { + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } + }; + + template <> struct external_constructor + { + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_type = value_t::array; + j.m_value = arr; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + { + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.assert_invariant(); + } + + template < + typename BasicJsonType, + typename CompatibleArrayType, + enable_if_t< + not std::is_same::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, const std::vector& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for (const bool x : arr) + { + j.m_value.array->push_back(x); + } + j.assert_invariant(); + } + + template < + typename BasicJsonType, + typename T, + enable_if_t::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + if (arr.size() > 0) + { + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + } + j.assert_invariant(); + } + }; + + template <> struct external_constructor + { + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_type = value_t::object; + j.m_value = obj; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.assert_invariant(); + } + + template < + typename BasicJsonType, + typename CompatibleObjectType, + enable_if_t< + not std::is_same::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; + + j.m_type = value_t::object; + j.m_value.object + = j.template create(begin(obj), end(obj)); + j.assert_invariant(); + } + }; + + ///////////// + // to_json // + ///////////// + + template < + typename BasicJsonType, + typename T, + enable_if_t::value, int> = 0> + void to_json(BasicJsonType& j, T b) noexcept + { + external_constructor::construct(j, b); } template < typename BasicJsonType, - typename CompatibleStringType, + typename CompatibleString, enable_if_t< - not std::is_same::value, + std::is_constructible::value, int> = 0> - static void construct(BasicJsonType& j, const CompatibleStringType& str) + void to_json(BasicJsonType& j, const CompatibleString& s) { - j.m_type = value_t::string; - j.m_value.string = j.template create(str); - j.assert_invariant(); - } - }; - - template <> struct external_constructor - { - template - static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept - { - j.m_type = value_t::number_float; - j.m_value = val; - j.assert_invariant(); - } - }; - - template <> struct external_constructor - { - template - static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept - { - j.m_type = value_t::number_unsigned; - j.m_value = val; - j.assert_invariant(); - } - }; - - template <> struct external_constructor - { - template - static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept - { - j.m_type = value_t::number_integer; - j.m_value = val; - j.assert_invariant(); - } - }; - - template <> struct external_constructor - { - template - static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) - { - j.m_type = value_t::array; - j.m_value = arr; - j.assert_invariant(); + external_constructor::construct(j, s); } template - static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) { - j.m_type = value_t::array; - j.m_value = std::move(arr); - j.assert_invariant(); + external_constructor::construct(j, std::move(s)); + } + + template < + typename BasicJsonType, + typename FloatType, + enable_if_t::value, int> = 0> + void to_json(BasicJsonType& j, FloatType val) noexcept + { + external_constructor::construct( + j, static_cast(val)); + } + + template < + typename BasicJsonType, + typename CompatibleNumberUnsignedType, + enable_if_t< + is_compatible_integer_type< + typename BasicJsonType::number_unsigned_t, + CompatibleNumberUnsignedType>::value, + int> = 0> + void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept + { + external_constructor::construct( + j, static_cast(val)); + } + + template < + typename BasicJsonType, + typename CompatibleNumberIntegerType, + enable_if_t< + is_compatible_integer_type< + typename BasicJsonType::number_integer_t, + CompatibleNumberIntegerType>::value, + int> = 0> + void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept + { + external_constructor::construct( + j, static_cast(val)); + } + + template < + typename BasicJsonType, + typename EnumType, + enable_if_t::value, int> = 0> + void to_json(BasicJsonType& j, EnumType e) noexcept + { + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); + } + + template void to_json(BasicJsonType& j, const std::vector& e) + { + external_constructor::construct(j, e); } template < typename BasicJsonType, typename CompatibleArrayType, enable_if_t< - not std::is_same::value, + is_compatible_array_type::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> - static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + void to_json(BasicJsonType& j, const CompatibleArrayType& arr) { - using std::begin; - using std::end; - j.m_type = value_t::array; - j.m_value.array = j.template create(begin(arr), end(arr)); - j.assert_invariant(); + external_constructor::construct(j, arr); } template - static void construct(BasicJsonType& j, const std::vector& arr) + void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) { - j.m_type = value_t::array; - j.m_value = value_t::array; - j.m_value.array->reserve(arr.size()); - for (const bool x : arr) - { - j.m_value.array->push_back(x); - } - j.assert_invariant(); + external_constructor::construct(j, bin); } template < typename BasicJsonType, typename T, enable_if_t::value, int> = 0> - static void construct(BasicJsonType& j, const std::valarray& arr) + void to_json(BasicJsonType& j, const std::valarray& arr) { - j.m_type = value_t::array; - j.m_value = value_t::array; - j.m_value.array->resize(arr.size()); - std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); - j.assert_invariant(); - } - }; - - template <> struct external_constructor - { - template - static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) - { - j.m_type = value_t::object; - j.m_value = obj; - j.assert_invariant(); + external_constructor::construct(j, std::move(arr)); } template - static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) { - j.m_type = value_t::object; - j.m_value = std::move(obj); - j.assert_invariant(); + external_constructor::construct(j, std::move(arr)); } template < typename BasicJsonType, typename CompatibleObjectType, enable_if_t< - not std::is_same::value, + is_compatible_object_type::value + and not is_basic_json::value, int> = 0> - static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + void to_json(BasicJsonType& j, const CompatibleObjectType& obj) { - using std::begin; - using std::end; + external_constructor::construct(j, obj); + } - j.m_type = value_t::object; - j.m_value.object = j.template create(begin(obj), end(obj)); - j.assert_invariant(); + template + void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + external_constructor::construct(j, std::move(obj)); + } + + template < + typename BasicJsonType, + typename T, + std::size_t N, + enable_if_t< + not std::is_constructible::value, + int> = 0> + void to_json(BasicJsonType& j, const T (&arr)[N]) + { + external_constructor::construct(j, arr); + } + + template < + typename BasicJsonType, + typename T1, + typename T2, + enable_if_t< + std::is_constructible::value + && std::is_constructible::value, + int> = 0> + void to_json(BasicJsonType& j, const std::pair& p) + { + j = {p.first, p.second}; + } + + // for https://github.com/nlohmann/json/pull/1134 + template < + typename BasicJsonType, + typename T, + enable_if_t< + std::is_same>::value, + int> = 0> + void to_json(BasicJsonType& j, const T& b) + { + j = {{b.key(), b.value()}}; + } + + template + void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) + { + j = {std::get(t)...}; + } + + template < + typename BasicJsonType, + typename T, + enable_if_t::value, int> = 0> + void to_json(BasicJsonType& j, const T& t) + { + to_json_tuple_impl(j, t, make_index_sequence::value>{}); + } + + struct to_json_fn + { + template + auto operator()(BasicJsonType& j, T&& val) const + noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); + } + }; + } // namespace detail + + /// namespace to hold default `to_json` function + namespace { + constexpr const auto& to_json = detail::static_const::value; + } // namespace +}}}} // namespace Azure::Core::Internal::Json + +namespace Azure { namespace Core { namespace Internal { namespace Json { + + template struct adl_serializer + { + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @param[in] j JSON value to read from + @param[in,out] val value to write to + */ + template + static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( + noexcept(::Azure::Core::Internal::Json::from_json(std::forward(j), val))) + -> decltype( + ::Azure::Core::Internal::Json::from_json(std::forward(j), val), + void()) + { + ::Azure::Core::Internal::Json::from_json(std::forward(j), val); + } + + /*! + @brief convert any value type to a JSON value + + This function is usually called by the constructors of the @ref basic_json + class. + + @param[in,out] j JSON value to write to + @param[in] val value to read from + */ + template + static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( + noexcept(::Azure::Core::Internal::Json::to_json(j, std::forward(val)))) + -> decltype(::Azure::Core::Internal::Json::to_json(j, std::forward(val)), void()) + { + ::Azure::Core::Internal::Json::to_json(j, std::forward(val)); } }; - ///////////// - // to_json // - ///////////// +}}}} // namespace Azure::Core::Internal::Json - template < - typename BasicJsonType, - typename T, - enable_if_t::value, int> = 0> - void to_json(BasicJsonType& j, T b) noexcept - { - external_constructor::construct(j, b); - } +// #include - template < - typename BasicJsonType, - typename CompatibleString, - enable_if_t< - std::is_constructible::value, - int> = 0> - void to_json(BasicJsonType& j, const CompatibleString& s) - { - external_constructor::construct(j, s); - } +#include // uint8_t +#include // tie +#include // move - template - void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) - { - external_constructor::construct(j, std::move(s)); - } +namespace Azure { namespace Core { namespace Internal { namespace Json { - template < - typename BasicJsonType, - typename FloatType, - enable_if_t::value, int> = 0> - void to_json(BasicJsonType& j, FloatType val) noexcept - { - external_constructor::construct( - j, static_cast(val)); - } + /*! + @brief an internal type for a backed binary type - template < - typename BasicJsonType, - typename CompatibleNumberUnsignedType, - enable_if_t< - is_compatible_integer_type< - typename BasicJsonType::number_unsigned_t, - CompatibleNumberUnsignedType>::value, - int> = 0> - void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept - { - external_constructor::construct( - j, static_cast(val)); - } + 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. - template < - typename BasicJsonType, - typename CompatibleNumberIntegerType, - enable_if_t< - is_compatible_integer_type< - typename BasicJsonType::number_integer_t, - CompatibleNumberIntegerType>::value, - int> = 0> - void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept - { - external_constructor::construct( - j, static_cast(val)); - } + @tparam BinaryType container to store bytes (`std::vector` by + default) - template < - typename BasicJsonType, - typename EnumType, - enable_if_t::value, int> = 0> - void to_json(BasicJsonType& j, EnumType e) noexcept - { - using underlying_type = typename std::underlying_type::type; - external_constructor::construct(j, static_cast(e)); - } + @since version 3.8.0 + */ + template class byte_container_with_subtype : public BinaryType { + public: + /// the type of the underlying container + using container_type = BinaryType; - template void to_json(BasicJsonType& j, const std::vector& e) - { - external_constructor::construct(j, e); - } + byte_container_with_subtype() noexcept(noexcept(container_type())) : container_type() {} - template < - typename BasicJsonType, - typename CompatibleArrayType, - enable_if_t< - is_compatible_array_type::value - and not is_compatible_object_type::value - and not is_compatible_string_type::value - and not is_basic_json::value, - int> = 0> - void to_json(BasicJsonType& j, const CompatibleArrayType& arr) - { - external_constructor::construct(j, arr); - } - - template < - typename BasicJsonType, - typename T, - enable_if_t::value, int> = 0> - void to_json(BasicJsonType& j, const std::valarray& arr) - { - external_constructor::construct(j, std::move(arr)); - } - - template - void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) - { - external_constructor::construct(j, std::move(arr)); - } - - template < - typename BasicJsonType, - typename CompatibleObjectType, - enable_if_t< - is_compatible_object_type::value - and not is_basic_json::value, - int> = 0> - void to_json(BasicJsonType& j, const CompatibleObjectType& obj) - { - external_constructor::construct(j, obj); - } - - template - void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) - { - external_constructor::construct(j, std::move(obj)); - } - - template < - typename BasicJsonType, - typename T, - std::size_t N, - enable_if_t< - not std::is_constructible::value, - int> = 0> - void to_json(BasicJsonType& j, const T (&arr)[N]) - { - external_constructor::construct(j, arr); - } - - template - void to_json(BasicJsonType& j, const std::pair& p) - { - j = {p.first, p.second}; - } - - // for https://github.com/nlohmann/json/pull/1134 - template < - typename BasicJsonType, - typename T, - enable_if_t< - std::is_same>::value, - int> = 0> - void to_json(BasicJsonType& j, const T& b) - { - j = {{b.key(), b.value()}}; - } - - template - void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) - { - j = {std::get(t)...}; - } - - template - void to_json(BasicJsonType& j, const std::tuple& t) - { - to_json_tuple_impl(j, t, index_sequence_for{}); - } - - struct to_json_fn - { - template - auto operator()(BasicJsonType& j, T&& val) const - noexcept(noexcept(to_json(j, std::forward(val)))) - -> decltype(to_json(j, std::forward(val)), void()) + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) { - return to_json(j, std::forward(val)); } + + 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 detail -/// namespace to hold default `to_json` function -namespace { - constexpr const auto& to_json = detail::static_const::value; -} // namespace -} // namespace nlohmann +}}}} // namespace Azure::Core::Internal::Json -namespace nlohmann { - -template struct adl_serializer -{ - /*! - @brief convert a JSON value to any value type - - This function is usually called by the `get()` function of the - @ref basic_json class (either explicit or via conversion operators). - - @param[in] j JSON value to read from - @param[in,out] val value to write to - */ - template - static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( - noexcept(::nlohmann::from_json(std::forward(j), val))) - -> decltype(::nlohmann::from_json(std::forward(j), val), void()) - { - ::nlohmann::from_json(std::forward(j), val); - } - - /*! - @brief convert any value type to a JSON value - - This function is usually called by the constructors of the @ref basic_json - class. - - @param[in,out] j JSON value to write to - @param[in] val value to read from - */ - template - static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( - noexcept(::nlohmann::to_json(j, std::forward(val)))) - -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) - { - ::nlohmann::to_json(j, std::forward(val)); - } -}; - -} // namespace nlohmann +// #include // #include @@ -2371,7 +4483,7 @@ template struct adl_serializer // #include -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { /// the supported input formats enum class input_format_t { @@ -2386,46 +4498,24 @@ namespace nlohmann { namespace detail { // 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: + using char_type = char; + + _az_JSON_HEDLEY_NON_NULL(2) explicit file_input_adapter(std::FILE* f) noexcept : m_file(f) {} // make class move-only 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 - { - return std::fgetc(m_file); - } + std::char_traits::int_type get_character() noexcept { return std::fgetc(m_file); } private: /// the file pointer to read from @@ -2441,85 +4531,101 @@ namespace nlohmann { namespace detail { 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 + using char_type = char; + + ~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()) {} + explicit input_stream_adapter(std::istream& i) : 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) + if (_az_JSON_HEDLEY_UNLIKELY(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 { + // General-purpose iterator-based adapter. It might not be as fast as + // theoretically possible for some containers, but it is extremely versatile. + template class iterator_input_adapter { public: - input_buffer_adapter(const char* b, const std::size_t l) noexcept : cursor(b), limit(b + l) {} + using char_type = typename std::iterator_traits::value_type; - // 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& operator=(input_buffer_adapter&&) = delete; - ~input_buffer_adapter() override = default; - - std::char_traits::int_type get_character() noexcept override + iterator_input_adapter(IteratorType first, IteratorType last) + : current(std::move(first)), end(std::move(last)) { - if (JSON_LIKELY(cursor < limit)) - { - return std::char_traits::to_int_type(*(cursor++)); - } + } - return std::char_traits::eof(); + typename std::char_traits::int_type get_character() + { + if (_az_JSON_HEDLEY_LIKELY(current != end)) + { + auto result = std::char_traits::to_int_type(*current); + std::advance(current, 1); + return result; + } + else + { + return std::char_traits::eof(); + } } private: - /// pointer to the current character - const char* cursor; - /// pointer past the last character - const char* const limit; + IteratorType current; + IteratorType end; + + template friend struct wide_string_input_helper; + + bool empty() const { return current == end; } }; - template struct wide_string_input_helper + template struct wide_string_input_helper; + + template struct wide_string_input_helper { // UTF-32 static void fill_buffer( - const WideStringType& str, - size_t& current_wchar, + BaseInputAdapter& input, std::array::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled) { utf8_bytes_index = 0; - if (current_wchar == str.size()) + if (_az_JSON_HEDLEY_UNLIKELY(input.empty())) { utf8_bytes[0] = std::char_traits::eof(); utf8_bytes_filled = 1; @@ -2527,7 +4633,7 @@ namespace nlohmann { namespace detail { else { // get the current character - const auto wc = static_cast(str[current_wchar++]); + const auto wc = input.get_character(); // UTF-32 to UTF-8 encoding if (wc < 0x80) @@ -2537,29 +4643,32 @@ namespace nlohmann { namespace detail { } else if (wc <= 0x7FF) { - utf8_bytes[0] - = static_cast::int_type>(0xC0u | ((wc >> 6u) & 0x1Fu)); - utf8_bytes[1] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes[0] = static_cast::int_type>( + 0xC0u | ((static_cast(wc) >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast::int_type>( + 0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 2; } else if (wc <= 0xFFFF) { - utf8_bytes[0] - = static_cast::int_type>(0xE0u | ((wc >> 12u) & 0x0Fu)); - utf8_bytes[1] - = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); - utf8_bytes[2] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes[0] = static_cast::int_type>( + 0xE0u | ((static_cast(wc) >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast::int_type>( + 0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>( + 0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 3; } else if (wc <= 0x10FFFF) { - utf8_bytes[0] - = static_cast::int_type>(0xF0u | ((wc >> 18u) & 0x07u)); - utf8_bytes[1] - = static_cast::int_type>(0x80u | ((wc >> 12u) & 0x3Fu)); - utf8_bytes[2] - = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); - utf8_bytes[3] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes[0] = static_cast::int_type>( + 0xF0u | ((static_cast(wc) >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast::int_type>( + 0x80u | ((static_cast(wc) >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>( + 0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>( + 0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 4; } else @@ -2572,19 +4681,18 @@ namespace nlohmann { namespace detail { } }; - template struct wide_string_input_helper + template struct wide_string_input_helper { // UTF-16 static void fill_buffer( - const WideStringType& str, - size_t& current_wchar, + BaseInputAdapter& input, std::array::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled) { utf8_bytes_index = 0; - if (current_wchar == str.size()) + if (_az_JSON_HEDLEY_UNLIKELY(input.empty())) { utf8_bytes[0] = std::char_traits::eof(); utf8_bytes_filled = 1; @@ -2592,7 +4700,7 @@ namespace nlohmann { namespace detail { else { // get the current character - const auto wc = static_cast(str[current_wchar++]); + const auto wc = input.get_character(); // UTF-16 to UTF-8 encoding if (wc < 0x80) @@ -2602,24 +4710,29 @@ namespace nlohmann { namespace detail { } else if (wc <= 0x7FF) { - utf8_bytes[0] = static_cast::int_type>(0xC0u | ((wc >> 6u))); - utf8_bytes[1] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes[0] = static_cast::int_type>( + 0xC0u | ((static_cast(wc) >> 6u))); + utf8_bytes[1] = static_cast::int_type>( + 0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 2; } else if (0xD800 > wc or wc >= 0xE000) { - utf8_bytes[0] = static_cast::int_type>(0xE0u | ((wc >> 12u))); - utf8_bytes[1] - = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); - utf8_bytes[2] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes[0] = static_cast::int_type>( + 0xE0u | ((static_cast(wc) >> 12u))); + utf8_bytes[1] = static_cast::int_type>( + 0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>( + 0x80u | (static_cast(wc) & 0x3Fu)); utf8_bytes_filled = 3; } else { - if (current_wchar < str.size()) + if (_az_JSON_HEDLEY_UNLIKELY(not input.empty())) { - const auto wc2 = static_cast(str[current_wchar++]); - const auto charcode = 0x10000u + (((wc & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + const auto wc2 = static_cast(input.get_character()); + const auto charcode + = 0x10000u + (((static_cast(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); utf8_bytes[1] = static_cast::int_type>( @@ -2632,8 +4745,6 @@ namespace nlohmann { namespace detail { } else { - // unknown character - ++current_wchar; utf8_bytes[0] = static_cast::int_type>(wc); utf8_bytes_filled = 1; } @@ -2642,17 +4753,19 @@ namespace nlohmann { namespace detail { } }; - template - class wide_string_input_adapter : public input_adapter_protocol { + // Wraps another input apdater to convert wide character types into individual bytes. + template class wide_string_input_adapter { public: - explicit wide_string_input_adapter(const WideStringType& w) noexcept : str(w) {} + using char_type = char; - std::char_traits::int_type get_character() noexcept override + wide_string_input_adapter(BaseInputAdapter base) : base_adapter(base) {} + + typename std::char_traits::int_type get_character() noexcept { // check if buffer needs to be filled if (utf8_bytes_index == utf8_bytes_filled) { - fill_buffer(); + fill_buffer(); assert(utf8_bytes_filled > 0); assert(utf8_bytes_index == 0); @@ -2665,18 +4778,14 @@ namespace nlohmann { namespace detail { } private: + BaseInputAdapter base_adapter; + template void fill_buffer() { - wide_string_input_helper::fill_buffer( - str, current_wchar, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + wide_string_input_helper::fill_buffer( + base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); } - /// the wstring to process - const WideStringType& str; - - /// index of the current wchar in str - std::size_t current_wchar = 0; - /// a buffer for UTF-8 bytes std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; @@ -2686,32 +4795,108 @@ namespace nlohmann { namespace detail { std::size_t utf8_bytes_filled = 0; }; - class input_adapter { + template struct iterator_input_adapter_factory + { + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using adapter_type = iterator_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(std::move(first), std::move(last)); + } + }; + + template struct is_iterator_of_multibyte + { + using value_type = typename std::iterator_traits::value_type; + enum + { + value = sizeof(value_type) > 1 + }; + }; + + template + struct iterator_input_adapter_factory< + IteratorType, + enable_if_t::value>> + { + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using base_adapter_type = iterator_input_adapter; + using adapter_type = wide_string_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(base_adapter_type(std::move(first), std::move(last))); + } + }; + + // General purpose iterator-based input + template + typename iterator_input_adapter_factory::adapter_type input_adapter( + IteratorType first, + IteratorType last) + { + using factory_type = iterator_input_adapter_factory; + return factory_type::create(first, last); + } + + // Convenience shorthand from container to iterator + template + auto input_adapter(const ContainerType& container) + -> decltype(input_adapter(begin(container), end(container))) + { + // Enable ADL + using std::begin; + using std::end; + + return input_adapter(begin(container), end(container)); + } + + // Special cases with fast paths + inline file_input_adapter input_adapter(std::FILE* file) { 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); + } + + using contiguous_bytes_input_adapter + = decltype(input_adapter(std::declval(), std::declval())); + + // Null-delimited strings, and the like. + template < + typename CharT, + typename std::enable_if< + std::is_pointer::value and not std::is_array::value + and std::is_integral::type>::value + and sizeof(typename std::remove_pointer::type) == 1, + int>::type + = 0> + contiguous_bytes_input_adapter input_adapter(CharT b) + { + auto length = std::strlen(reinterpret_cast(b)); + auto ptr = reinterpret_cast(b); + return input_adapter(ptr, ptr + length); + } + + template + auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) + { + return input_adapter(array, array + N); + } + + // 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: - // native support - 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)) {} - - /// input adapter for input stream - input_adapter(std::istream&& i) : ia(std::make_shared(i)) {} - - input_adapter(const std::wstring& ws) - : ia(std::make_shared>(ws)) - { - } - - input_adapter(const std::u16string& ws) - : ia(std::make_shared>(ws)) - { - } - - input_adapter(const std::u32string& ws) - : ia(std::make_shared>(ws)) - { - } - - /// input adapter for buffer template < typename CharT, typename std::enable_if< @@ -2720,30 +4905,11 @@ namespace nlohmann { namespace detail { 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), reinterpret_cast(b) + l) { } - // derived support - - /// input adapter for string literal - template < - typename CharT, - typename std::enable_if< - std::is_pointer::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))) - { - } - - /// input adapter for iterator range with contiguous storage template < class IteratorType, typename std::enable_if< @@ -2752,70 +4918,16 @@ namespace nlohmann { namespace detail { 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 http://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_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); - } - } - - /// input adapter for array - template - input_adapter(T (&array)[N]) : input_adapter(std::begin(array), std::end(array)) + span_input_adapter(IteratorType first, IteratorType last) : ia(input_adapter(first, last)) { } - /// input adapter for contiguous container - template < - class ContiguousContainer, - typename std::enable_if< - not std::is_pointer::value - and std::is_base_of< - std::random_access_iterator_tag, - typename iterator_traits()))>::iterator_category>::value, - int>::type - = 0> - input_adapter(const ContiguousContainer& c) : input_adapter(std::begin(c), std::end(c)) - { - } - - operator input_adapter_t() { return ia; } + contiguous_bytes_input_adapter&& get() { return std::move(ia); } private: - /// the actual adapter - input_adapter_t ia = nullptr; + contiguous_bytes_input_adapter ia; }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include @@ -2829,662 +4941,686 @@ namespace nlohmann { namespace detail { // #include -namespace nlohmann { - -/*! -@brief SAX interface - -This class describes the SAX interface used by @ref nlohmann::json::sax_parse. -Each function is called in different situations while the input is parsed. The -boolean return value informs the parser whether to continue processing the -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; +namespace Azure { namespace Core { namespace Internal { namespace Json { /*! - @brief a null value was read - @return whether parsing should proceed + @brief SAX interface + + This class describes the SAX interface used by @ref Azure::Core::Internal::Json::json::sax_parse. + Each function is called in different situations while the input is parsed. The + boolean return value informs the parser whether to continue processing the + input. */ - virtual bool null() = 0; - - /*! - @brief a boolean value was read - @param[in] val boolean value - @return whether parsing should proceed - */ - virtual bool boolean(bool val) = 0; - - /*! - @brief an integer number was read - @param[in] val integer value - @return whether parsing should proceed - */ - virtual bool number_integer(number_integer_t val) = 0; - - /*! - @brief an unsigned integer number was read - @param[in] val unsigned integer value - @return whether parsing should proceed - */ - virtual bool number_unsigned(number_unsigned_t val) = 0; - - /*! - @brief an floating-point number was read - @param[in] val floating-point value - @param[in] s raw token value - @return whether parsing should proceed - */ - virtual bool number_float(number_float_t val, const string_t& s) = 0; - - /*! - @brief a string was read - @param[in] val string value - @return whether parsing should proceed - @note It is safe to move the passed string. - */ - virtual bool string(string_t& val) = 0; - - /*! - @brief the beginning of an object was read - @param[in] elements number of object elements or -1 if unknown - @return whether parsing should proceed - @note binary formats may report the number of elements - */ - virtual bool start_object(std::size_t elements) = 0; - - /*! - @brief an object key was read - @param[in] val object key - @return whether parsing should proceed - @note It is safe to move the passed string. - */ - virtual bool key(string_t& val) = 0; - - /*! - @brief the end of an object was read - @return whether parsing should proceed - */ - virtual bool end_object() = 0; - - /*! - @brief the beginning of an array was read - @param[in] elements number of array elements or -1 if unknown - @return whether parsing should proceed - @note binary formats may report the number of elements - */ - virtual bool start_array(std::size_t elements) = 0; - - /*! - @brief the end of an array was read - @return whether parsing should proceed - */ - virtual bool end_array() = 0; - - /*! - @brief a parse error occurred - @param[in] position the position in the input where the error occurs - @param[in] last_token the last read token - @param[in] ex an exception object describing the error - @return whether parsing should proceed (must return false) - */ - virtual bool parse_error( - std::size_t position, - const std::string& last_token, - const detail::exception& ex) - = 0; - - virtual ~json_sax() = default; -}; - -namespace detail { - /*! - @brief SAX implementation to create a JSON value from SAX events - - This class implements the @ref json_sax interface and processes the SAX events - to create a JSON value which makes it basically a DOM parser. The structure or - hierarchy of the JSON value is managed by the stack `ref_stack` which contains - a pointer to the respective array or object for each recursion depth. - - After successful parsing, the value that is passed by reference to the - constructor contains the parsed value. - - @tparam BasicJsonType the JSON type - */ - template class json_sax_dom_parser { - public: + template struct json_sax + { 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 binary_t = typename BasicJsonType::binary_t; /*! - @param[in, out] r reference to a JSON value that is manipulated while - parsing - @param[in] allow_exceptions_ whether parse errors yield exceptions + @brief a null value was read + @return whether parsing should proceed */ - explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) - : root(r), allow_exceptions(allow_exceptions_) - { - } + virtual bool null() = 0; - // make class move-only - json_sax_dom_parser(const json_sax_dom_parser&) = delete; - json_sax_dom_parser(json_sax_dom_parser&&) = default; - json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; - json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; - ~json_sax_dom_parser() = default; + /*! + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; - bool null() - { - handle_value(nullptr); - return true; - } + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; - bool boolean(bool val) - { - handle_value(val); - return true; - } + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; - bool number_integer(number_integer_t val) - { - handle_value(val); - return true; - } + /*! + @brief an floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; - bool number_unsigned(number_unsigned_t val) - { - handle_value(val); - return true; - } + /*! + @brief a string was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool string(string_t& val) = 0; - bool number_float(number_float_t val, const string_t& /*unused*/) - { - handle_value(val); - return true; - } + /*! + @brief a binary string was read + @param[in] val binary value + @return whether parsing should proceed + @note It is safe to move the passed binary. + */ + virtual bool binary(binary_t& val) = 0; - bool string(string_t& val) - { - handle_value(val); - return true; - } + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; - bool start_object(std::size_t len) - { - ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + /*! + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; - if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); - } + /*! + @brief the end of an object was read + @return whether parsing should proceed + */ + virtual bool end_object() = 0; - return true; - } + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; - bool key(string_t& val) - { - // add null at given key and store the reference for later - object_element = &(ref_stack.back()->m_value.object->operator[](val)); - return true; - } + /*! + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; - bool end_object() - { - ref_stack.pop_back(); - return true; - } - - bool start_array(std::size_t len) - { - ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); - - if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); - } - - return true; - } - - bool end_array() - { - ref_stack.pop_back(); - return true; - } - - bool parse_error( - std::size_t /*unused*/, - const std::string& /*unused*/, + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error( + std::size_t position, + const std::string& last_token, const detail::exception& ex) - { - errored = true; - if (allow_exceptions) - { - // determine the proper exception type from the id - switch ((ex.id / 100) % 100) - { - case 1: - JSON_THROW(*static_cast(&ex)); - case 4: - JSON_THROW(*static_cast(&ex)); - // LCOV_EXCL_START - case 2: - JSON_THROW(*static_cast(&ex)); - case 3: - JSON_THROW(*static_cast(&ex)); - case 5: - JSON_THROW(*static_cast(&ex)); - default: - assert(false); - // LCOV_EXCL_STOP - } - } - return false; - } + = 0; - constexpr bool is_errored() const { return errored; } - - private: - /*! - @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 BasicJsonType* handle_value(Value&& v) - { - if (ref_stack.empty()) - { - root = BasicJsonType(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(std::forward(v)); - return &(ref_stack.back()->m_value.array->back()); - } - - assert(ref_stack.back()->is_object()); - assert(object_element); - *object_element = BasicJsonType(std::forward(v)); - return object_element; - } - - /// the parsed JSON value - BasicJsonType& root; - /// stack to model hierarchy of values - std::vector ref_stack{}; - /// helper to hold the reference for the next object element - BasicJsonType* object_element = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; + virtual ~json_sax() = default; }; - template class json_sax_dom_callback_parser { - public: - 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 parser_callback_t = typename BasicJsonType::parser_callback_t; - using parse_event_t = typename BasicJsonType::parse_event_t; + namespace detail { + /*! + @brief SAX implementation to create a JSON value from SAX events - json_sax_dom_callback_parser( - BasicJsonType& r, - const parser_callback_t cb, - const bool allow_exceptions_ = true) - : root(r), callback(cb), allow_exceptions(allow_exceptions_) - { - keep_stack.push_back(true); - } + This class implements the @ref json_sax interface and processes the SAX events + to create a JSON value which makes it basically a DOM parser. The structure or + hierarchy of the JSON value is managed by the stack `ref_stack` which contains + a pointer to the respective array or object for each recursion depth. - // make class move-only - json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; - json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; - json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; - json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; - ~json_sax_dom_callback_parser() = default; + After successful parsing, the value that is passed by reference to the + constructor contains the parsed value. - bool null() - { - handle_value(nullptr); - return true; - } + @tparam BasicJsonType the JSON type + */ + template class json_sax_dom_parser { + public: + 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 binary_t = typename BasicJsonType::binary_t; - bool boolean(bool val) - { - handle_value(val); - return true; - } - - bool number_integer(number_integer_t val) - { - handle_value(val); - return true; - } - - bool number_unsigned(number_unsigned_t val) - { - handle_value(val); - return true; - } - - bool number_float(number_float_t val, const string_t& /*unused*/) - { - handle_value(val); - return true; - } - - bool string(string_t& val) - { - handle_value(val); - return true; - } - - bool start_object(std::size_t len) - { - // check callback for object start - const bool keep - = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); - keep_stack.push_back(keep); - - auto val = handle_value(BasicJsonType::value_t::object, true); - ref_stack.push_back(val.second); - - // check object limit - if (ref_stack.back() - and JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + /*! + @param[in, out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions + */ + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) + : root(r), allow_exceptions(allow_exceptions_) { - JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); } - return true; - } + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; + ~json_sax_dom_parser() = default; - bool key(string_t& val) - { - BasicJsonType k = BasicJsonType(val); - - // check callback for key - const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); - key_keep_stack.push_back(keep); - - // add discarded value at given key and store the reference for later - if (keep and ref_stack.back()) + bool null() { - object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + handle_value(nullptr); + return true; } - return true; - } - - bool end_object() - { - if (ref_stack.back() - and not callback( - static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + bool boolean(bool val) { - // discard object - *ref_stack.back() = discarded; + handle_value(val); + return true; } - assert(not ref_stack.empty()); - assert(not keep_stack.empty()); - ref_stack.pop_back(); - keep_stack.pop_back(); - - if (not ref_stack.empty() and ref_stack.back() and ref_stack.back()->is_object()) + bool number_integer(number_integer_t val) { - // remove discarded value - for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + + if (_az_JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) { - if (it->is_discarded()) + _az_JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); + } + + return true; + } + + bool key(string_t& val) + { + // add null at given key and store the reference for later + object_element = &(ref_stack.back()->m_value.object->operator[](val)); + return true; + } + + bool end_object() + { + ref_stack.pop_back(); + return true; + } + + bool start_array(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); + + if (_az_JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + _az_JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); + } + + return true; + } + + bool end_array() + { + ref_stack.pop_back(); + return true; + } + + bool parse_error( + std::size_t /*unused*/, + const std::string& /*unused*/, + const detail::exception& ex) + { + errored = true; + if (allow_exceptions) + { + // determine the proper exception type from the id + switch ((ex.id / 100) % 100) { - ref_stack.back()->erase(it); - break; + case 1: + _az_JSON_THROW(*static_cast(&ex)); + case 4: + _az_JSON_THROW(*static_cast(&ex)); + // LCOV_EXCL_START + case 2: + _az_JSON_THROW(*static_cast(&ex)); + case 3: + _az_JSON_THROW(*static_cast(&ex)); + case 5: + _az_JSON_THROW(*static_cast(&ex)); + default: + assert(false); + // LCOV_EXCL_STOP } } + return false; } - return true; - } + constexpr bool is_errored() const { return errored; } - bool start_array(std::size_t len) - { - const bool keep - = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); - keep_stack.push_back(keep); - - auto val = handle_value(BasicJsonType::value_t::array, true); - ref_stack.push_back(val.second); - - // check array limit - if (ref_stack.back() - and JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + private: + /*! + @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 _az_JSON_HEDLEY_RETURNS_NON_NULL BasicJsonType* handle_value(Value&& v) { - JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); - } - - return true; - } - - bool end_array() - { - bool keep = true; - - if (ref_stack.back()) - { - keep = callback( - static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); - if (not keep) + if (ref_stack.empty()) { - // discard array + root = BasicJsonType(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(std::forward(v)); + return &(ref_stack.back()->m_value.array->back()); + } + + assert(ref_stack.back()->is_object()); + assert(object_element); + *object_element = BasicJsonType(std::forward(v)); + return object_element; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack{}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + }; + + template class json_sax_dom_callback_parser { + public: + 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 binary_t = typename BasicJsonType::binary_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + + json_sax_dom_callback_parser( + BasicJsonType& r, + const parser_callback_t cb, + const bool allow_exceptions_ = true) + : root(r), callback(cb), allow_exceptions(allow_exceptions_) + { + keep_stack.push_back(true); + } + + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; + ~json_sax_dom_callback_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep + = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); + + // check object limit + if (ref_stack.back() + and _az_JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + _az_JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); + } + + return true; + } + + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); + + // check callback for key + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); + + // add discarded value at given key and store the reference for later + if (keep and ref_stack.back()) + { + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + } + + return true; + } + + bool end_object() + { + if (ref_stack.back() + and not callback( + static_cast(ref_stack.size()) - 1, + parse_event_t::object_end, + *ref_stack.back())) + { + // discard object *ref_stack.back() = discarded; } - } - assert(not ref_stack.empty()); - assert(not keep_stack.empty()); - ref_stack.pop_back(); - keep_stack.pop_back(); + assert(not ref_stack.empty()); + assert(not keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); - // remove discarded value - if (not keep and not ref_stack.empty() and ref_stack.back()->is_array()) - { - ref_stack.back()->m_value.array->pop_back(); - } - - return true; - } - - bool parse_error( - std::size_t /*unused*/, - const std::string& /*unused*/, - const detail::exception& ex) - { - errored = true; - if (allow_exceptions) - { - // determine the proper exception type from the id - switch ((ex.id / 100) % 100) + if (not ref_stack.empty() and ref_stack.back() and ref_stack.back()->is_structured()) { - case 1: - JSON_THROW(*static_cast(&ex)); - case 4: - JSON_THROW(*static_cast(&ex)); - // LCOV_EXCL_START - case 2: - JSON_THROW(*static_cast(&ex)); - case 3: - JSON_THROW(*static_cast(&ex)); - case 5: - JSON_THROW(*static_cast(&ex)); - default: - assert(false); - // LCOV_EXCL_STOP + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + { + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } + } } + + return true; } - return false; - } - constexpr bool is_errored() const { return errored; } - - private: - /*! - @param[in] v value to add to the JSON value we build during parsing - @param[in] skip_callback whether we should skip calling the callback - function; this is required after start_array() and - start_object() SAX events, because otherwise we would call the - callback function with an empty array or object, respectively. - - @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 - - @return pair of boolean (whether value should be kept) and pointer (to the - passed value in the ref_stack hierarchy; nullptr if not kept) - */ - template - std::pair handle_value(Value&& v, const bool skip_callback = false) - { - assert(not keep_stack.empty()); - - // do not handle this value if we know it would be added to a discarded - // container - if (not keep_stack.back()) + bool start_array(std::size_t len) { - return {false, nullptr}; + const bool keep + = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); + + // check array limit + if (ref_stack.back() + and _az_JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + _az_JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); + } + + return true; } - // create value - auto value = BasicJsonType(std::forward(v)); - - // check callback - const bool keep = skip_callback - or callback(static_cast(ref_stack.size()), parse_event_t::value, value); - - // do not handle this value if we just learnt it shall be discarded - if (not keep) + bool end_array() { - return {false, nullptr}; + bool keep = true; + + if (ref_stack.back()) + { + keep = callback( + static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (not keep) + { + // discard array + *ref_stack.back() = discarded; + } + } + + assert(not ref_stack.empty()); + assert(not keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + // remove discarded value + if (not keep and not ref_stack.empty() and ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->pop_back(); + } + + return true; } - if (ref_stack.empty()) + bool parse_error( + std::size_t /*unused*/, + const std::string& /*unused*/, + const detail::exception& ex) { - root = std::move(value); - return {true, &root}; + errored = true; + if (allow_exceptions) + { + // determine the proper exception type from the id + switch ((ex.id / 100) % 100) + { + case 1: + _az_JSON_THROW(*static_cast(&ex)); + case 4: + _az_JSON_THROW(*static_cast(&ex)); + // LCOV_EXCL_START + case 2: + _az_JSON_THROW(*static_cast(&ex)); + case 3: + _az_JSON_THROW(*static_cast(&ex)); + case 5: + _az_JSON_THROW(*static_cast(&ex)); + default: + assert(false); + // LCOV_EXCL_STOP + } + } + return false; } - // skip this value if we already decided to skip the parent - // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) - if (not ref_stack.back()) + constexpr bool is_errored() const { return errored; } + + private: + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. + + @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 + + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) + */ + template + std::pair handle_value(Value&& v, const bool skip_callback = false) { - return {false, nullptr}; + assert(not keep_stack.empty()); + + // do not handle this value if we know it would be added to a discarded + // container + if (not keep_stack.back()) + { + return {false, nullptr}; + } + + // create value + auto value = BasicJsonType(std::forward(v)); + + // check callback + const bool keep = skip_callback + or callback(static_cast(ref_stack.size()), parse_event_t::value, value); + + // do not handle this value if we just learnt it shall be discarded + if (not keep) + { + return {false, nullptr}; + } + + if (ref_stack.empty()) + { + root = std::move(value); + return {true, &root}; + } + + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (not ref_stack.back()) + { + return {false, nullptr}; + } + + // we now only expect arrays and objects + assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->push_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; + } + + // object + assert(ref_stack.back()->is_object()); + // check if we should store an element for the current key + assert(not key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); + + if (not store_element) + { + return {false, nullptr}; + } + + assert(object_element); + *object_element = std::move(value); + return {true, object_element}; } - // we now only expect arrays and objects - assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack{}; + /// stack to manage which values to keep + std::vector keep_stack{}; + /// stack to manage which object keys to keep + std::vector key_keep_stack{}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; + }; - // array - if (ref_stack.back()->is_array()) + template class json_sax_acceptor { + public: + 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 binary_t = typename BasicJsonType::binary_t; + + bool null() { return true; } + + bool boolean(bool /*unused*/) { return true; } + + bool number_integer(number_integer_t /*unused*/) { return true; } + + bool number_unsigned(number_unsigned_t /*unused*/) { return true; } + + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) { return true; } + + bool string(string_t& /*unused*/) { return true; } + + bool binary(binary_t& /*unused*/) { return true; } + + bool start_object(std::size_t /*unused*/ = std::size_t(-1)) { return true; } + + bool key(string_t& /*unused*/) { return true; } + + bool end_object() { return true; } + + bool start_array(std::size_t /*unused*/ = std::size_t(-1)) { return true; } + + bool end_array() { return true; } + + bool parse_error( + std::size_t /*unused*/, + const std::string& /*unused*/, + const detail::exception& /*unused*/) { - ref_stack.back()->m_value.array->push_back(std::move(value)); - return {true, &(ref_stack.back()->m_value.array->back())}; + return false; } + }; + } // namespace detail - // object - assert(ref_stack.back()->is_object()); - // check if we should store an element for the current key - assert(not key_keep_stack.empty()); - const bool store_element = key_keep_stack.back(); - key_keep_stack.pop_back(); - - if (not store_element) - { - return {false, nullptr}; - } - - assert(object_element); - *object_element = std::move(value); - return {true, object_element}; - } - - /// the parsed JSON value - BasicJsonType& root; - /// stack to model hierarchy of values - std::vector ref_stack{}; - /// stack to manage which values to keep - std::vector keep_stack{}; - /// stack to manage which object keys to keep - std::vector key_keep_stack{}; - /// helper to hold the reference for the next object element - BasicJsonType* object_element = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// callback function - const parser_callback_t callback = nullptr; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; - /// a discarded value for the callback - BasicJsonType discarded = BasicJsonType::value_t::discarded; - }; - - template class json_sax_acceptor { - public: - 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; - - bool null() { return true; } - - bool boolean(bool /*unused*/) { return true; } - - bool number_integer(number_integer_t /*unused*/) { return true; } - - bool number_unsigned(number_unsigned_t /*unused*/) { return true; } - - bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) { return true; } - - bool string(string_t& /*unused*/) { return true; } - - bool start_object(std::size_t /*unused*/ = std::size_t(-1)) { return true; } - - bool key(string_t& /*unused*/) { return true; } - - bool end_object() { return true; } - - bool start_array(std::size_t /*unused*/ = std::size_t(-1)) { return true; } - - bool end_array() { return true; } - - bool parse_error( - std::size_t /*unused*/, - const std::string& /*unused*/, - const detail::exception& /*unused*/) - { - return false; - } - }; -} // namespace detail - -} // namespace nlohmann +}}}} // namespace Azure::Core::Internal::Json // #include @@ -3498,7 +5634,7 @@ namespace detail { // #include -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { template using null_function_t = decltype(std::declval().null()); template @@ -3623,11 +5759,24 @@ namespace nlohmann { namespace detail { "Missing/invalid function: bool parse_error(std::size_t, const " "std::string&, const exception&)"); }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { 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 // /////////////////// @@ -3635,13 +5784,19 @@ namespace nlohmann { namespace detail { /*! @brief deserialization of CBOR, MessagePack, and UBJSON values */ - template > + template < + typename BasicJsonType, + typename InputAdapterType, + typename SAX = json_sax_dom_parser> 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 binary_t = typename BasicJsonType::binary_t; using json_sax_t = SAX; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; public: /*! @@ -3649,10 +5804,9 @@ namespace nlohmann { namespace detail { @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 @@ -3669,6 +5823,7 @@ namespace nlohmann { namespace detail { @return */ + _az_JSON_HEDLEY_NON_NULL(3) bool sax_parse(const input_format_t format, json_sax_t* sax_, const bool strict = true) { sax = sax_; @@ -3708,7 +5863,7 @@ namespace nlohmann { namespace detail { get(); } - if (JSON_UNLIKELY(current != std::char_traits::eof())) + if (_az_JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) { return sax->parse_error( chars_read, @@ -3726,18 +5881,6 @@ namespace nlohmann { namespace detail { return result; } - /*! - @brief determine system byte order - - @return true if and only if system's byte order is little endian - - @note from http://stackoverflow.com/a/1001328/266378 - */ - static constexpr bool little_endianess(int num = 1) noexcept - { - return *reinterpret_cast(&num) == 1; - } - private: ////////// // BSON // @@ -3752,12 +5895,12 @@ namespace nlohmann { namespace detail { std::int32_t document_size; get_number(input_format_t::bson, document_size); - if (JSON_UNLIKELY(not sax->start_object(std::size_t(-1)))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) { return false; } - if (JSON_UNLIKELY(not parse_bson_element_list(/*is_array*/ false))) + if (_az_JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/ false))) { return false; } @@ -3778,7 +5921,7 @@ namespace nlohmann { namespace detail { while (true) { get(); - if (JSON_UNLIKELY(not unexpect_eof(input_format_t::bson, "cstring"))) + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "cstring"))) { return false; } @@ -3786,7 +5929,7 @@ namespace nlohmann { namespace detail { { return true; } - *out++ = static_cast(current); + *out++ = static_cast(current); } return true; @@ -3805,7 +5948,7 @@ namespace nlohmann { namespace detail { */ template bool get_bson_string(const NumberType len, string_t& result) { - if (JSON_UNLIKELY(len < 1)) + if (_az_JSON_HEDLEY_UNLIKELY(len < 1)) { auto last_token = get_token_string(); return sax->parse_error( @@ -3821,7 +5964,41 @@ namespace nlohmann { namespace detail { } return get_string(input_format_t::bson, len - static_cast(1), result) - and get() != std::char_traits::eof(); + and get() != std::char_traits::eof(); + } + + /*! + @brief Parses a byte array input of length @a len from the BSON input. + @param[in] len The length of the byte array to be read. + @param[in, out] result A reference to the binary variable where the read + array is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 0 + @return `true` if the byte array was successfully parsed + */ + template bool get_bson_binary(const NumberType len, binary_t& result) + { + if (_az_JSON_HEDLEY_UNLIKELY(len < 0)) + { + auto last_token = get_token_string(); + 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"))); + } + + // 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); } /*! @@ -3835,7 +6012,7 @@ namespace nlohmann { namespace detail { @return whether a valid BSON-object/array was passed to the SAX parser */ bool parse_bson_element_internal( - const int element_type, + const char_int_type element_type, const std::size_t element_type_parse_position) { switch (element_type) @@ -3865,6 +6042,14 @@ namespace nlohmann { namespace detail { return parse_bson_array(); } + case 0x05: // binary + { + std::int32_t len; + binary_t value; + return get_number(input_format_t::bson, len) + and get_bson_binary(len, value) and sax->binary(value); + } + case 0x08: // boolean { return sax->boolean(get() != 0); @@ -3891,7 +6076,7 @@ namespace nlohmann { namespace detail { default: // anything else not supported (yet) { - std::array cr{}; + std::array cr{{}}; (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type)); return sax->parse_error( element_type_parse_position, @@ -3919,15 +6104,16 @@ namespace nlohmann { namespace detail { bool parse_bson_element_list(const bool is_array) { string_t key; - while (int element_type = get()) + + while (auto element_type = get()) { - if (JSON_UNLIKELY(not unexpect_eof(input_format_t::bson, "element list"))) + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "element list"))) { return false; } const std::size_t element_type_parse_position = chars_read; - if (JSON_UNLIKELY(not get_bson_cstr(key))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_bson_cstr(key))) { return false; } @@ -3937,7 +6123,7 @@ namespace nlohmann { namespace detail { return false; } - if (JSON_UNLIKELY( + if (_az_JSON_HEDLEY_UNLIKELY( not parse_bson_element_internal(element_type, element_type_parse_position))) { return false; @@ -3959,12 +6145,12 @@ namespace nlohmann { namespace detail { std::int32_t document_size; get_number(input_format_t::bson, document_size); - if (JSON_UNLIKELY(not sax->start_array(std::size_t(-1)))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) { return false; } - if (JSON_UNLIKELY(not parse_bson_element_list(/*is_array*/ true))) + if (_az_JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/ true))) { return false; } @@ -3988,7 +6174,7 @@ namespace nlohmann { namespace detail { switch (get_char ? get() : current) { // EOF - case std::char_traits::eof(): + case std::char_traits::eof(): return unexpect_eof(input_format_t::cbor, "value"); // Integer 0x00..0x17 (0..23) @@ -4098,6 +6284,41 @@ namespace nlohmann { namespace detail { static_cast(-1) - static_cast(number)); } + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: // Binary data (one-byte uint8_t for n follows) + case 0x59: // Binary data (two-byte uint16_t for n follow) + case 0x5A: // Binary data (four-byte uint32_t for n follow) + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + case 0x5F: // Binary data (indefinite length) + { + binary_t b; + return get_cbor_binary(b) and sax->binary(b); + } + // UTF-8 string (0x00..0x17 bytes follow) case 0x60: case 0x61: @@ -4262,13 +6483,13 @@ namespace nlohmann { namespace detail { case 0xF9: // Half-Precision Float (two-byte IEEE 754) { - const int byte1_raw = get(); - if (JSON_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) + const auto byte1_raw = get(); + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) { return false; } - const int byte2_raw = get(); - if (JSON_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) + const auto byte2_raw = get(); + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) { return false; } @@ -4289,7 +6510,7 @@ namespace nlohmann { namespace detail { const int exp = (half >> 10u) & 0x1Fu; const unsigned int mant = half & 0x3FFu; assert(0 <= exp and exp <= 32); - assert(0 <= mant and mant <= 1024); + assert(mant <= 1024); switch (exp) { case 0: @@ -4349,7 +6570,7 @@ namespace nlohmann { namespace detail { */ bool get_cbor_string(string_t& result) { - if (JSON_UNLIKELY(not unexpect_eof(input_format_t::cbor, "string"))) + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "string"))) { return false; } @@ -4445,6 +6666,115 @@ namespace nlohmann { namespace detail { } } + /*! + @brief reads a CBOR byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into the byte array. + Additionally, CBOR's byte arrays with indefinite lengths are supported. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_cbor_binary(binary_t& result) + { + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "binary"))) + { + return false; + } + + switch (current) + { + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: { + return get_binary( + input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + 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); + } + + 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); + } + + 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); + } + + 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); + } + + case 0x5F: // Binary data (indefinite length) + { + while (get() != 0xFF) + { + binary_t chunk; + if (not get_cbor_binary(chunk)) + { + return false; + } + result.insert(result.end(), chunk.begin(), chunk.end()); + } + return true; + } + + 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::cbor, + "expected length specification (0x40-0x5B) or indefinite binary array type " + "(0x5F); last byte: 0x" + + last_token, + "binary"))); + } + } + } + /*! @param[in] len the length of the array or std::size_t(-1) for an array of indefinite size @@ -4452,7 +6782,7 @@ namespace nlohmann { namespace detail { */ bool get_cbor_array(const std::size_t len) { - if (JSON_UNLIKELY(not sax->start_array(len))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->start_array(len))) { return false; } @@ -4461,7 +6791,7 @@ namespace nlohmann { namespace detail { { for (std::size_t i = 0; i < len; ++i) { - if (JSON_UNLIKELY(not parse_cbor_internal())) + if (_az_JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) { return false; } @@ -4471,7 +6801,7 @@ namespace nlohmann { namespace detail { { while (get() != 0xFF) { - if (JSON_UNLIKELY(not parse_cbor_internal(false))) + if (_az_JSON_HEDLEY_UNLIKELY(not parse_cbor_internal(false))) { return false; } @@ -4488,7 +6818,7 @@ namespace nlohmann { namespace detail { */ bool get_cbor_object(const std::size_t len) { - if (JSON_UNLIKELY(not sax->start_object(len))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->start_object(len))) { return false; } @@ -4499,12 +6829,12 @@ namespace nlohmann { namespace detail { for (std::size_t i = 0; i < len; ++i) { get(); - if (JSON_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) { return false; } - if (JSON_UNLIKELY(not parse_cbor_internal())) + if (_az_JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) { return false; } @@ -4515,12 +6845,12 @@ namespace nlohmann { namespace detail { { while (get() != 0xFF) { - if (JSON_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) { return false; } - if (JSON_UNLIKELY(not parse_cbor_internal())) + if (_az_JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) { return false; } @@ -4543,7 +6873,7 @@ namespace nlohmann { namespace detail { switch (get()) { // EOF - case std::char_traits::eof(): + case std::char_traits::eof(): return unexpect_eof(input_format_t::msgpack, "value"); // positive fixint @@ -4749,7 +7079,11 @@ namespace nlohmann { namespace detail { case 0xBC: case 0xBD: case 0xBE: - case 0xBF: { + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { string_t s; return get_msgpack_string(s) and sax->string(s); } @@ -4763,6 +7097,22 @@ namespace nlohmann { namespace detail { case 0xC3: // true return sax->boolean(true); + case 0xC4: // bin 8 + case 0xC5: // bin 16 + case 0xC6: // bin 32 + case 0xC7: // ext 8 + case 0xC8: // ext 16 + case 0xC9: // ext 32 + case 0xD4: // fixext 1 + case 0xD5: // fixext 2 + case 0xD6: // fixext 4 + case 0xD7: // fixext 8 + case 0xD8: // fixext 16 + { + binary_t b; + return get_msgpack_binary(b) and sax->binary(b); + } + case 0xCA: // float 32 { float number; @@ -4825,14 +7175,6 @@ namespace nlohmann { namespace detail { return get_number(input_format_t::msgpack, number) and sax->number_integer(number); } - case 0xD9: // str 8 - case 0xDA: // str 16 - case 0xDB: // str 32 - { - string_t s; - return get_msgpack_string(s) and sax->string(s); - } - case 0xDC: // array 16 { std::uint16_t len; @@ -4923,7 +7265,7 @@ namespace nlohmann { namespace detail { */ bool get_msgpack_string(string_t& result) { - if (JSON_UNLIKELY(not unexpect_eof(input_format_t::msgpack, "string"))) + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::msgpack, "string"))) { return false; } @@ -5005,20 +7347,136 @@ namespace nlohmann { namespace detail { } } + /*! + @brief reads a MessagePack byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into a byte array. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_msgpack_binary(binary_t& result) + { + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) { + result.set_subtype(static_cast(subtype)); + return true; + }; + + switch (current) + { + case 0xC4: // bin 8 + { + std::uint8_t len; + return get_number(input_format_t::msgpack, len) + and get_binary(input_format_t::msgpack, len, result); + } + + 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); + } + + 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); + } + + case 0xC7: // ext 8 + { + std::uint8_t len; + std::int8_t subtype; + return get_number(input_format_t::msgpack, len) + and get_number(input_format_t::msgpack, subtype) + and get_binary(input_format_t::msgpack, len, result) + and assign_and_return_true(subtype); + } + + case 0xC8: // ext 16 + { + std::uint16_t len; + std::int8_t subtype; + return get_number(input_format_t::msgpack, len) + and get_number(input_format_t::msgpack, subtype) + and get_binary(input_format_t::msgpack, len, result) + and assign_and_return_true(subtype); + } + + case 0xC9: // ext 32 + { + std::uint32_t len; + std::int8_t subtype; + return get_number(input_format_t::msgpack, len) + and get_number(input_format_t::msgpack, subtype) + and get_binary(input_format_t::msgpack, len, result) + and assign_and_return_true(subtype); + } + + case 0xD4: // fixext 1 + { + 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 + { + 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 + { + 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 + { + 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 + { + std::int8_t subtype; + return get_number(input_format_t::msgpack, subtype) + and get_binary(input_format_t::msgpack, 16, result) + and assign_and_return_true(subtype); + } + + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + /*! @param[in] len the length of the array @return whether array creation completed */ bool get_msgpack_array(const std::size_t len) { - if (JSON_UNLIKELY(not sax->start_array(len))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->start_array(len))) { return false; } for (std::size_t i = 0; i < len; ++i) { - if (JSON_UNLIKELY(not parse_msgpack_internal())) + if (_az_JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal())) { return false; } @@ -5033,7 +7491,7 @@ namespace nlohmann { namespace detail { */ bool get_msgpack_object(const std::size_t len) { - if (JSON_UNLIKELY(not sax->start_object(len))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->start_object(len))) { return false; } @@ -5042,12 +7500,12 @@ namespace nlohmann { namespace detail { for (std::size_t i = 0; i < len; ++i) { get(); - if (JSON_UNLIKELY(not get_msgpack_string(key) or not sax->key(key))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_msgpack_string(key) or not sax->key(key))) { return false; } - if (JSON_UNLIKELY(not parse_msgpack_internal())) + if (_az_JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal())) { return false; } @@ -5094,7 +7552,7 @@ namespace nlohmann { namespace detail { get(); // TODO(niels): may we ignore N here? } - if (JSON_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value"))) + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value"))) { return false; } @@ -5157,7 +7615,7 @@ namespace nlohmann { namespace detail { { case 'U': { std::uint8_t number; - if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; } @@ -5167,7 +7625,7 @@ namespace nlohmann { namespace detail { case 'i': { std::int8_t number; - if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; } @@ -5177,7 +7635,7 @@ namespace nlohmann { namespace detail { case 'I': { std::int16_t number; - if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; } @@ -5187,7 +7645,7 @@ namespace nlohmann { namespace detail { case 'l': { std::int32_t number; - if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; } @@ -5197,7 +7655,7 @@ namespace nlohmann { namespace detail { case 'L': { std::int64_t number; - if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) { return false; } @@ -5232,7 +7690,7 @@ namespace nlohmann { namespace detail { @return whether pair creation completed */ - bool get_ubjson_size_type(std::pair& result) + bool get_ubjson_size_type(std::pair& result) { result.first = string_t::npos; // size result.second = 0; // type @@ -5242,15 +7700,15 @@ namespace nlohmann { namespace detail { if (current == '$') { result.second = get(); // must not ignore 'N', because 'N' maybe the type - if (JSON_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "type"))) + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "type"))) { return false; } get_ignore_noop(); - if (JSON_UNLIKELY(current != '#')) + if (_az_JSON_HEDLEY_UNLIKELY(current != '#')) { - if (JSON_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value"))) + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value"))) { return false; } @@ -5282,11 +7740,11 @@ namespace nlohmann { namespace detail { @param prefix the previously read or set type prefix @return whether value creation completed */ - bool get_ubjson_value(const int prefix) + bool get_ubjson_value(const char_int_type prefix) { switch (prefix) { - case std::char_traits::eof(): // EOF + case std::char_traits::eof(): // EOF return unexpect_eof(input_format_t::ubjson, "value"); case 'T': // true @@ -5337,11 +7795,11 @@ namespace nlohmann { namespace detail { case 'C': // char { get(); - if (JSON_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "char"))) + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "char"))) { return false; } - if (JSON_UNLIKELY(current > 127)) + if (_az_JSON_HEDLEY_UNLIKELY(current > 127)) { auto last_token = get_token_string(); return sax->parse_error( @@ -5355,7 +7813,7 @@ namespace nlohmann { namespace detail { "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"))); } - string_t s(1, static_cast(current)); + string_t s(1, static_cast(current)); return sax->string(s); } @@ -5391,15 +7849,15 @@ namespace nlohmann { namespace detail { */ bool get_ubjson_array() { - std::pair size_and_type; - if (JSON_UNLIKELY(not get_ubjson_size_type(size_and_type))) + std::pair size_and_type; + if (_az_JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) { return false; } if (size_and_type.first != string_t::npos) { - if (JSON_UNLIKELY(not sax->start_array(size_and_type.first))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->start_array(size_and_type.first))) { return false; } @@ -5410,7 +7868,7 @@ namespace nlohmann { namespace detail { { for (std::size_t i = 0; i < size_and_type.first; ++i) { - if (JSON_UNLIKELY(not get_ubjson_value(size_and_type.second))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second))) { return false; } @@ -5421,7 +7879,7 @@ namespace nlohmann { namespace detail { { for (std::size_t i = 0; i < size_and_type.first; ++i) { - if (JSON_UNLIKELY(not parse_ubjson_internal())) + if (_az_JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) { return false; } @@ -5430,14 +7888,14 @@ namespace nlohmann { namespace detail { } else { - if (JSON_UNLIKELY(not sax->start_array(std::size_t(-1)))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) { return false; } while (current != ']') { - if (JSON_UNLIKELY(not parse_ubjson_internal(false))) + if (_az_JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal(false))) { return false; } @@ -5453,8 +7911,8 @@ namespace nlohmann { namespace detail { */ bool get_ubjson_object() { - std::pair size_and_type; - if (JSON_UNLIKELY(not get_ubjson_size_type(size_and_type))) + std::pair size_and_type; + if (_az_JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) { return false; } @@ -5462,7 +7920,7 @@ namespace nlohmann { namespace detail { string_t key; if (size_and_type.first != string_t::npos) { - if (JSON_UNLIKELY(not sax->start_object(size_and_type.first))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->start_object(size_and_type.first))) { return false; } @@ -5471,11 +7929,11 @@ namespace nlohmann { namespace detail { { for (std::size_t i = 0; i < size_and_type.first; ++i) { - if (JSON_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) { return false; } - if (JSON_UNLIKELY(not get_ubjson_value(size_and_type.second))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second))) { return false; } @@ -5486,11 +7944,11 @@ namespace nlohmann { namespace detail { { for (std::size_t i = 0; i < size_and_type.first; ++i) { - if (JSON_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) { return false; } - if (JSON_UNLIKELY(not parse_ubjson_internal())) + if (_az_JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) { return false; } @@ -5500,18 +7958,18 @@ namespace nlohmann { namespace detail { } else { - if (JSON_UNLIKELY(not sax->start_object(std::size_t(-1)))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) { return false; } while (current != '}') { - if (JSON_UNLIKELY(not get_ubjson_string(key, false) or not sax->key(key))) + if (_az_JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key, false) or not sax->key(key))) { return false; } - if (JSON_UNLIKELY(not parse_ubjson_internal())) + if (_az_JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) { return false; } @@ -5523,6 +7981,9 @@ namespace nlohmann { namespace detail { return sax->end_object(); } + // Note, no reader for UBJSON binary types is implemented because they do + // not exist + /////////////////////// // Utility functions // /////////////////////// @@ -5532,20 +7993,20 @@ namespace nlohmann { namespace detail { This function provides the interface to the used input adapter. It does not throw in case the input reached EOF, but returns a -'ve valued - `std::char_traits::eof()` in that case. + `std::char_traits::eof()` in that case. @return character read from the input */ - int get() + char_int_type get() { ++chars_read; - return current = ia->get_character(); + return current = ia.get_character(); } /*! @return character read from the input after ignoring all 'N' entries */ - int get_ignore_noop() + char_int_type get_ignore_noop() { do { @@ -5576,7 +8037,7 @@ namespace nlohmann { namespace detail { for (std::size_t i = 0; i < sizeof(NumberType); ++i) { get(); - if (JSON_UNLIKELY(not unexpect_eof(format, "number"))) + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "number"))) { return false; } @@ -5617,11 +8078,40 @@ namespace nlohmann { namespace detail { bool success = true; std::generate_n(std::back_inserter(result), len, [this, &success, &format]() { get(); - if (JSON_UNLIKELY(not unexpect_eof(format, "string"))) + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "string"))) { success = false; } - return static_cast(current); + return std::char_traits::to_char_type(current); + }); + return success; + } + + /*! + @brief create a byte array by reading bytes from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of bytes to read + @param[out] result byte array created by reading @a len bytes + + @return whether byte array creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of memory. + */ + template + bool get_binary(const input_format_t format, const NumberType len, binary_t& result) + { + bool success = true; + std::generate_n(std::back_inserter(result), len, [this, &success, &format]() { + get(); + if (_az_JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "binary"))) + { + success = false; + } + return static_cast(current); }); return success; } @@ -5631,9 +8121,10 @@ namespace nlohmann { namespace detail { @param[in] context further context information (for diagnostics) @return whether the last read character is not EOF */ + _az_JSON_HEDLEY_NON_NULL(3) bool unexpect_eof(const input_format_t format, const char* context) const { - if (JSON_UNLIKELY(current == std::char_traits::eof())) + if (_az_JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) { return sax->parse_error( chars_read, @@ -5649,7 +8140,7 @@ namespace nlohmann { namespace detail { */ std::string get_token_string() const { - std::array cr{}; + std::array cr{{}}; (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current)); return std::string{cr.data()}; } @@ -5657,7 +8148,7 @@ namespace nlohmann { namespace detail { /*! @param[in] format the current format @param[in] detail a detailed error message - @param[in] context further contect information + @param[in] context further context information @return a message string to use in the parse_error exceptions */ std::string exception_message( @@ -5694,10 +8185,10 @@ namespace nlohmann { namespace detail { private: /// input adapter - input_adapter_t ia = nullptr; + InputAdapterType ia; /// the current character - int current = std::char_traits::eof(); + char_int_type current = std::char_traits::eof(); /// the number of characters read std::size_t chars_read = 0; @@ -5708,7 +8199,7 @@ namespace nlohmann { namespace detail { /// the SAX parser json_sax_t* sax = nullptr; }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include @@ -5730,22 +8221,12 @@ namespace nlohmann { namespace detail { // #include -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { /////////// // lexer // /////////// - /*! - @brief lexical analysis - - This class organizes the lexical analysis during JSON deserialization. - */ - template class lexer { - 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; - + template class lexer_base { public: /// token types for the parser enum class token_type @@ -5770,6 +8251,8 @@ namespace nlohmann { namespace detail { }; /// return name of values of type token_type (only used for errors) + _az_JSON_HEDLEY_RETURNS_NON_NULL + _az_JSON_HEDLEY_CONST static const char* token_type_name(const token_type t) noexcept { switch (t) @@ -5784,9 +8267,9 @@ namespace nlohmann { namespace detail { 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 "'['"; @@ -5812,17 +8295,35 @@ namespace nlohmann { namespace detail { // LCOV_EXCL_STOP } } + }; + /*! + @brief lexical analysis - explicit lexer(detail::input_adapter_t&& adapter) - : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) + 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; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + using token_type = typename lexer_base::token_type; + + explicit lexer(InputAdapterType&& adapter) + : ia(std::move(adapter)), + decimal_point_char(static_cast(get_decimal_point())) { } // 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: @@ -5831,6 +8332,7 @@ namespace nlohmann { namespace detail { ///////////////////// /// return the locale-dependent decimal point + _az_JSON_HEDLEY_PURE static char get_decimal_point() noexcept { const auto loc = localeconv(); @@ -5905,7 +8407,7 @@ namespace nlohmann { namespace detail { @return true if and only if no range violation was detected */ - bool next_byte_in_range(std::initializer_list ranges) + bool next_byte_in_range(std::initializer_list ranges) { assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); add(current); @@ -5913,7 +8415,7 @@ namespace nlohmann { namespace detail { for (auto range = ranges.begin(); range != ranges.end(); ++range) { get(); - if (JSON_LIKELY(*range <= current and current <= *(++range))) + if (_az_JSON_HEDLEY_LIKELY(*range <= current and current <= *(++range))) { add(current); } @@ -5956,7 +8458,7 @@ namespace nlohmann { namespace detail { switch (get()) { // end of file while parsing string - case std::char_traits::eof(): { + case std::char_traits::eof(): { error_message = "invalid string: missing closing quote"; return token_type::parse_error; } @@ -6008,7 +8510,7 @@ namespace nlohmann { namespace detail { const int codepoint1 = get_codepoint(); int codepoint = codepoint1; // start with codepoint1 - if (JSON_UNLIKELY(codepoint1 == -1)) + if (_az_JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) { error_message = "invalid string: '\\u' must be followed by 4 hex digits"; return token_type::parse_error; @@ -6018,18 +8520,18 @@ namespace nlohmann { namespace detail { if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF) { // expect next \uxxxx entry - if (JSON_LIKELY(get() == '\\' and get() == 'u')) + if (_az_JSON_HEDLEY_LIKELY(get() == '\\' and get() == 'u')) { const int codepoint2 = get_codepoint(); - if (JSON_UNLIKELY(codepoint2 == -1)) + if (_az_JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) { error_message = "invalid string: '\\u' must be followed by 4 hex digits"; return token_type::parse_error; } // check if codepoint2 is a low surrogate - if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) + if (_az_JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) { // overwrite codepoint codepoint = static_cast( @@ -6044,21 +8546,21 @@ namespace nlohmann { namespace detail { } else { - error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed " + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed " "by U+DC00..U+DFFF"; return token_type::parse_error; } } else { - error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by " + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by " "U+DC00..U+DFFF"; return token_type::parse_error; } } else { - if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) + if (_az_JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) { error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; @@ -6073,31 +8575,37 @@ namespace nlohmann { namespace detail { if (codepoint < 0x80) { // 1-byte characters: 0xxxxxxx (ASCII) - add(codepoint); + add(static_cast(codepoint)); } else if (codepoint <= 0x7FF) { // 2-byte characters: 110xxxxx 10xxxxxx - add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + add(static_cast( + 0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast( + 0x80u | (static_cast(codepoint) & 0x3Fu))); } else if (codepoint <= 0xFFFF) { // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); - add(static_cast( + add(static_cast( + 0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast( 0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + add(static_cast( + 0x80u | (static_cast(codepoint) & 0x3Fu))); } else { // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); - add(static_cast( + add(static_cast( + 0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast( 0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); - add(static_cast( + add(static_cast( 0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + add(static_cast( + 0x80u | (static_cast(codepoint) & 0x3Fu))); } break; @@ -6435,7 +8943,7 @@ namespace nlohmann { namespace detail { case 0xDD: case 0xDE: case 0xDF: { - if (JSON_UNLIKELY(not next_byte_in_range({0x80, 0xBF}))) + if (_az_JSON_HEDLEY_UNLIKELY(not next_byte_in_range({0x80, 0xBF}))) { return token_type::parse_error; } @@ -6444,7 +8952,7 @@ namespace nlohmann { namespace detail { // U+0800..U+0FFF: bytes E0 A0..BF 80..BF case 0xE0: { - if (JSON_UNLIKELY(not(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + if (_az_JSON_HEDLEY_UNLIKELY(not(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -6467,7 +8975,7 @@ namespace nlohmann { namespace detail { case 0xEC: case 0xEE: case 0xEF: { - if (JSON_UNLIKELY(not(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + if (_az_JSON_HEDLEY_UNLIKELY(not(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -6476,7 +8984,7 @@ namespace nlohmann { namespace detail { // U+D000..U+D7FF: bytes ED 80..9F 80..BF case 0xED: { - if (JSON_UNLIKELY(not(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + if (_az_JSON_HEDLEY_UNLIKELY(not(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -6485,7 +8993,7 @@ namespace nlohmann { namespace detail { // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF case 0xF0: { - if (JSON_UNLIKELY(not(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + if (_az_JSON_HEDLEY_UNLIKELY(not(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -6496,7 +9004,7 @@ namespace nlohmann { namespace detail { case 0xF1: case 0xF2: case 0xF3: { - if (JSON_UNLIKELY(not(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + if (_az_JSON_HEDLEY_UNLIKELY(not(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -6505,7 +9013,7 @@ namespace nlohmann { namespace detail { // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF case 0xF4: { - if (JSON_UNLIKELY(not(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + if (_az_JSON_HEDLEY_UNLIKELY(not(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) { return token_type::parse_error; } @@ -6521,16 +9029,19 @@ namespace nlohmann { namespace detail { } } + _az_JSON_HEDLEY_NON_NULL(2) static void strtof(float& f, const char* str, char** endptr) noexcept { f = std::strtof(str, endptr); } + _az_JSON_HEDLEY_NON_NULL(2) static void strtof(double& f, const char* str, char** endptr) noexcept { f = std::strtod(str, endptr); } + _az_JSON_HEDLEY_NON_NULL(2) static void strtof(long double& f, const char* str, char** endptr) noexcept { f = std::strtold(str, endptr); @@ -6553,7 +9064,7 @@ namespace nlohmann { namespace detail { minus | zero | any1 | [error] | [error] | [error] | [error] | [error] zero | done | done | exponent | done | done | decimal1 | done any1 | any1 | any1 | exponent | done | done | decimal1 | done - decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error] + decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] decimal2 | decimal2 | decimal2 | exponent | done | done | done | done exponent | any2 | any2 | [error] | sign | sign | [error] | [error] sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] @@ -6884,15 +9395,17 @@ namespace nlohmann { namespace detail { @param[in] length the length of the passed literal text @param[in] return_type the token type to return on success */ + _az_JSON_HEDLEY_NON_NULL(2) token_type scan_literal( - const char* literal_text, + const char_type* literal_text, const std::size_t length, token_type return_type) { - assert(current == literal_text[0]); + assert(std::char_traits::to_char_type(current) == literal_text[0]); for (std::size_t i = 1; i < length; ++i) { - if (JSON_UNLIKELY(get() != literal_text[i])) + if (_az_JSON_HEDLEY_UNLIKELY( + std::char_traits::to_char_type(get()) != literal_text[i])) { error_message = "invalid literal"; return token_type::parse_error; @@ -6910,7 +9423,7 @@ namespace nlohmann { namespace detail { { token_buffer.clear(); token_string.clear(); - token_string.push_back(std::char_traits::to_char_type(current)); + token_string.push_back(std::char_traits::to_char_type(current)); } /* @@ -6923,7 +9436,7 @@ namespace nlohmann { namespace detail { @return character read from the input */ - std::char_traits::int_type get() + char_int_type get() { ++position.chars_read_total; ++position.chars_read_current_line; @@ -6935,12 +9448,12 @@ namespace nlohmann { namespace detail { } else { - current = ia->get_character(); + current = ia.get_character(); } - if (JSON_LIKELY(current != std::char_traits::eof())) + if (_az_JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) { - token_string.push_back(std::char_traits::to_char_type(current)); + token_string.push_back(std::char_traits::to_char_type(current)); } if (current == '\n') @@ -6979,7 +9492,7 @@ namespace nlohmann { namespace detail { --position.chars_read_current_line; } - if (JSON_LIKELY(current != std::char_traits::eof())) + if (_az_JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) { assert(not token_string.empty()); token_string.pop_back(); @@ -6987,7 +9500,10 @@ namespace nlohmann { namespace detail { } /// add a character to token_buffer - void add(int c) { token_buffer.push_back(std::char_traits::to_char_type(c)); } + void add(char_int_type c) + { + token_buffer.push_back(static_cast(c)); + } public: ///////////////////// @@ -7022,17 +9538,17 @@ namespace nlohmann { namespace detail { std::string result; for (const auto c : token_string) { - if ('\x00' <= c and c <= '\x1F') + if (static_cast(c) <= '\x1F') { // escape control characters - std::array cs{}; + std::array cs{{}}; (std::snprintf)(cs.data(), cs.size(), "", static_cast(c)); result += cs.data(); } else { // add character as is - result.push_back(c); + result.push_back(static_cast(c)); } } @@ -7040,6 +9556,7 @@ namespace nlohmann { namespace detail { } /// return syntax error message + _az_JSON_HEDLEY_RETURNS_NON_NULL constexpr const char* get_error_message() const noexcept { return error_message; } ///////////////////// @@ -7096,12 +9613,19 @@ namespace nlohmann { namespace detail { return token_type::value_separator; // literals - case 't': - return scan_literal("true", 4, token_type::literal_true); - case 'f': - return scan_literal("false", 5, token_type::literal_false); - case 'n': - return scan_literal("null", 4, token_type::literal_null); + case 't': { + std::array true_literal = {{'t', 'r', 'u', 'e'}}; + return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); + } + case 'f': { + std::array false_literal = {{'f', 'a', 'l', 's', 'e'}}; + return scan_literal( + false_literal.data(), false_literal.size(), token_type::literal_false); + } + case 'n': { + std::array null_literal = {{'n', 'u', 'l', 'l'}}; + return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); + } // string case '\"': @@ -7124,7 +9648,7 @@ namespace nlohmann { namespace detail { // end of input (the null byte is needed when parsing from // string literals) case '\0': - case std::char_traits::eof(): + case std::char_traits::eof(): return token_type::end_of_input; // error @@ -7136,10 +9660,10 @@ namespace nlohmann { namespace detail { private: /// input adapter - detail::input_adapter_t ia = nullptr; + InputAdapterType ia; /// the current character - std::char_traits::int_type current = std::char_traits::eof(); + char_int_type current = std::char_traits::eof(); /// whether the next get() call should just return current bool next_unget = false; @@ -7148,7 +9672,7 @@ namespace nlohmann { namespace detail { position_t position{}; /// raw input token string (for error messages) - std::vector token_string{}; + std::vector token_string{}; /// buffer for variable-length tokens (numbers, strings) string_t token_buffer{}; @@ -7162,9 +9686,9 @@ namespace nlohmann { namespace detail { number_float_t value_float = 0; /// the decimal point - const char decimal_point_char = '.'; + const char_int_type decimal_point_char = '.'; }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include @@ -7190,48 +9714,49 @@ namespace nlohmann { namespace detail { // #include -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { 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 decent parser. + This class implements a recursive descent parser. */ - template class parser { + 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, + InputAdapterType&& adapter, + const parser_callback_t cb = nullptr, const bool allow_exceptions_ = true) : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_) { @@ -7322,7 +9847,9 @@ namespace nlohmann { namespace detail { return sax_parse(&sax_acceptor, strict); } - template bool sax_parse(SAX* sax, const bool strict = true) + template + _az_JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) { (void)detail::is_sax_static_asserts{}; const bool result = sax_parse_internal(sax); @@ -7341,7 +9868,7 @@ namespace nlohmann { namespace detail { } private: - template bool sax_parse_internal(SAX* sax) + template _az_JSON_HEDLEY_NON_NULL(2) bool sax_parse_internal(SAX* sax) { // stack to remember the hierarchy of structured values we are parsing // true = array; false = object @@ -7357,7 +9884,7 @@ namespace nlohmann { namespace detail { switch (last_token) { case token_type::begin_object: { - if (JSON_UNLIKELY(not sax->start_object(std::size_t(-1)))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) { return false; } @@ -7365,7 +9892,7 @@ namespace nlohmann { namespace detail { // closing } -> we are done if (get_token() == token_type::end_object) { - if (JSON_UNLIKELY(not sax->end_object())) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->end_object())) { return false; } @@ -7373,7 +9900,7 @@ namespace nlohmann { namespace detail { } // parse key - if (JSON_UNLIKELY(last_token != token_type::value_string)) + if (_az_JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) { return sax->parse_error( m_lexer.get_position(), @@ -7383,13 +9910,13 @@ namespace nlohmann { namespace detail { m_lexer.get_position(), exception_message(token_type::value_string, "object key"))); } - if (JSON_UNLIKELY(not sax->key(m_lexer.get_string()))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string()))) { return false; } // parse separator (:) - if (JSON_UNLIKELY(get_token() != token_type::name_separator)) + if (_az_JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) { return sax->parse_error( m_lexer.get_position(), @@ -7409,7 +9936,7 @@ namespace nlohmann { namespace detail { } case token_type::begin_array: { - if (JSON_UNLIKELY(not sax->start_array(std::size_t(-1)))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) { return false; } @@ -7417,7 +9944,7 @@ namespace nlohmann { namespace detail { // closing ] -> we are done if (get_token() == token_type::end_array) { - if (JSON_UNLIKELY(not sax->end_array())) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->end_array())) { return false; } @@ -7434,7 +9961,7 @@ namespace nlohmann { namespace detail { case token_type::value_float: { const auto res = m_lexer.get_number_float(); - if (JSON_UNLIKELY(not std::isfinite(res))) + if (_az_JSON_HEDLEY_UNLIKELY(not std::isfinite(res))) { return sax->parse_error( m_lexer.get_position(), @@ -7443,7 +9970,7 @@ namespace nlohmann { namespace detail { 406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); } - if (JSON_UNLIKELY(not sax->number_float(res, m_lexer.get_string()))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->number_float(res, m_lexer.get_string()))) { return false; } @@ -7452,7 +9979,7 @@ namespace nlohmann { namespace detail { } case token_type::literal_false: { - if (JSON_UNLIKELY(not sax->boolean(false))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->boolean(false))) { return false; } @@ -7460,7 +9987,7 @@ namespace nlohmann { namespace detail { } case token_type::literal_null: { - if (JSON_UNLIKELY(not sax->null())) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->null())) { return false; } @@ -7468,7 +9995,7 @@ namespace nlohmann { namespace detail { } case token_type::literal_true: { - if (JSON_UNLIKELY(not sax->boolean(true))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->boolean(true))) { return false; } @@ -7476,7 +10003,7 @@ namespace nlohmann { namespace detail { } case token_type::value_integer: { - if (JSON_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer()))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer()))) { return false; } @@ -7484,7 +10011,7 @@ namespace nlohmann { namespace detail { } case token_type::value_string: { - if (JSON_UNLIKELY(not sax->string(m_lexer.get_string()))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->string(m_lexer.get_string()))) { return false; } @@ -7492,7 +10019,7 @@ namespace nlohmann { namespace detail { } case token_type::value_unsigned: { - if (JSON_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned()))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned()))) { return false; } @@ -7545,9 +10072,9 @@ namespace nlohmann { namespace detail { } // closing ] - if (JSON_LIKELY(last_token == token_type::end_array)) + if (_az_JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) { - if (JSON_UNLIKELY(not sax->end_array())) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->end_array())) { return false; } @@ -7574,7 +10101,7 @@ namespace nlohmann { namespace detail { if (get_token() == token_type::value_separator) { // parse key - if (JSON_UNLIKELY(get_token() != token_type::value_string)) + if (_az_JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) { return sax->parse_error( m_lexer.get_position(), @@ -7585,13 +10112,13 @@ namespace nlohmann { namespace detail { exception_message(token_type::value_string, "object key"))); } - if (JSON_UNLIKELY(not sax->key(m_lexer.get_string()))) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string()))) { return false; } // parse separator (:) - if (JSON_UNLIKELY(get_token() != token_type::name_separator)) + if (_az_JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) { return sax->parse_error( m_lexer.get_position(), @@ -7608,9 +10135,9 @@ namespace nlohmann { namespace detail { } // closing } - if (JSON_LIKELY(last_token == token_type::end_object)) + if (_az_JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) { - if (JSON_UNLIKELY(not sax->end_object())) + if (_az_JSON_HEDLEY_UNLIKELY(not sax->end_object())) { return false; } @@ -7670,7 +10197,7 @@ namespace nlohmann { namespace detail { 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 @@ -7678,7 +10205,7 @@ namespace nlohmann { namespace detail { /// whether to throw exceptions in case of errors const bool allow_exceptions = true; }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include @@ -7687,7 +10214,7 @@ namespace nlohmann { namespace detail { #include // ptrdiff_t #include // numeric_limits -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { /* @brief an iterator for primitive JSON types @@ -7783,9 +10310,9 @@ namespace nlohmann { namespace detail { return *this; } }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { /*! @brief an iterator value @@ -7798,17 +10325,20 @@ namespace nlohmann { namespace detail { typename BasicJsonType::object_t::iterator object_iterator{}; /// iterator for JSON arrays typename BasicJsonType::array_t::iterator array_iterator{}; + /// iterator for JSON binary arrays + typename BasicJsonType::binary_t::container_type::iterator binary_iterator{}; /// generic iterator for all other types primitive_iterator_t primitive_iterator{}; }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include -#include // not #include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next #include // conditional, is_const, remove_const +// #include + // #include // #include @@ -7823,7 +10353,7 @@ namespace nlohmann { namespace detail { // #include -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { // forward declare, to be able to friend it later on template class iteration_proxy; template class iteration_proxy_value; @@ -7925,6 +10455,31 @@ namespace nlohmann { namespace detail { to iterator is not defined. */ + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + { + } + + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + /*! @brief converting constructor @param[in] other non-const iterator to copy from @@ -7937,7 +10492,7 @@ namespace nlohmann { namespace detail { /*! @brief converting assignment - @param[in,out] other non-const iterator to copy from + @param[in] other non-const iterator to copy from @return const/non-const iterator @note It is not checked whether @a other is initialized. */ @@ -8032,15 +10587,15 @@ namespace nlohmann { namespace detail { } case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + _az_JSON_THROW(invalid_iterator::create(214, "cannot get value")); default: { - if (JSON_LIKELY(m_it.primitive_iterator.is_begin())) + if (_az_JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) { return *m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + _az_JSON_THROW(invalid_iterator::create(214, "cannot get value")); } } } @@ -8066,12 +10621,12 @@ namespace nlohmann { namespace detail { } default: { - if (JSON_LIKELY(m_it.primitive_iterator.is_begin())) + if (_az_JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) { return m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + _az_JSON_THROW(invalid_iterator::create(214, "cannot get value")); } } } @@ -8163,9 +10718,9 @@ namespace nlohmann { namespace detail { bool operator==(const iter_impl& other) const { // if objects are not the same, the comparison is undefined - if (JSON_UNLIKELY(m_object != other.m_object)) + if (_az_JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW( + _az_JSON_THROW( invalid_iterator::create(212, "cannot compare iterators of different containers")); } @@ -8197,9 +10752,9 @@ namespace nlohmann { namespace detail { bool operator<(const iter_impl& other) const { // if objects are not the same, the comparison is undefined - if (JSON_UNLIKELY(m_object != other.m_object)) + if (_az_JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) { - JSON_THROW( + _az_JSON_THROW( invalid_iterator::create(212, "cannot compare iterators of different containers")); } @@ -8208,7 +10763,7 @@ namespace nlohmann { namespace detail { switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); + _az_JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); case value_t::array: return (m_it.array_iterator < other.m_it.array_iterator); @@ -8247,7 +10802,7 @@ namespace nlohmann { namespace detail { switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + _az_JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); case value_t::array: { std::advance(m_it.array_iterator, i); @@ -8313,7 +10868,7 @@ namespace nlohmann { namespace detail { switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + _az_JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); case value_t::array: return m_it.array_iterator - other.m_it.array_iterator; @@ -8334,21 +10889,21 @@ namespace nlohmann { namespace detail { switch (m_object->m_type) { case value_t::object: - JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); + _az_JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); case value_t::array: return *std::next(m_it.array_iterator, n); case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + _az_JSON_THROW(invalid_iterator::create(214, "cannot get value")); default: { - if (JSON_LIKELY(m_it.primitive_iterator.get_value() == -n)) + if (_az_JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) { return *m_object; } - JSON_THROW(invalid_iterator::create(214, "cannot get value")); + _az_JSON_THROW(invalid_iterator::create(214, "cannot get value")); } } } @@ -8361,12 +10916,12 @@ namespace nlohmann { namespace detail { { assert(m_object != nullptr); - if (JSON_LIKELY(m_object->is_object())) + if (_az_JSON_HEDLEY_LIKELY(m_object->is_object())) { return m_it.object_iterator->first; } - JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); + _az_JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); } /*! @@ -8381,7 +10936,7 @@ namespace nlohmann { namespace detail { /// the actual iterator of the associated instance internal_iterator::type> m_it{}; }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include @@ -8391,7 +10946,7 @@ namespace nlohmann { namespace detail { #include // reverse_iterator #include // declval -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { ////////////////////// // reverse_iterator // ////////////////////// @@ -8496,7 +11051,7 @@ namespace nlohmann { namespace detail { return it.operator*(); } }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include @@ -8504,6 +11059,7 @@ namespace nlohmann { namespace detail { #include // all_of #include // assert +#include // isdigit #include // accumulate #include // string #include // move @@ -8515,890 +11071,928 @@ namespace nlohmann { namespace detail { // #include -namespace nlohmann { -template class json_pointer { - // allow basic_json to access private members - NLOHMANN_BASIC_JSON_TPL_DECLARATION - friend class basic_json; +namespace Azure { namespace Core { namespace Internal { namespace Json { + template class json_pointer { + // allow basic_json to access private members + _az_NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; -public: - /*! - @brief create JSON pointer + public: + /*! + @brief create JSON pointer - Create a JSON pointer according to the syntax described in - [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). - @param[in] s string representing the JSON pointer; if omitted, the empty - string is assumed which references the whole JSON value + @param[in] s string representing the JSON pointer; if omitted, the empty + string is assumed which references the whole JSON value - @throw parse_error.107 if the given JSON pointer @a s is nonempty and does - not begin with a slash (`/`); see example below + @throw parse_error.107 if the given JSON pointer @a s is nonempty and does + not begin with a slash (`/`); see example below - @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is - not followed by `0` (representing `~`) or `1` (representing `/`); see - example below + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is + not followed by `0` (representing `~`) or `1` (representing `/`); see + example below - @liveexample{The example shows the construction several valid JSON pointers - as well as the exceptional behavior.,json_pointer} + @liveexample{The example shows the construction several valid JSON pointers + as well as the exceptional behavior.,json_pointer} - @since version 2.0.0 - */ - explicit json_pointer(const std::string& s = "") : reference_tokens(split(s)) {} + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") : reference_tokens(split(s)) {} - /*! - @brief return a string representation of the JSON pointer + /*! + @brief return a string representation of the JSON pointer - @invariant For each JSON pointer `ptr`, it holds: - @code {.cpp} - ptr == json_pointer(ptr.to_string()); - @endcode + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode - @return a string representation of the JSON pointer + @return a string representation of the JSON pointer - @liveexample{The example shows the result of `to_string`.,json_pointer__to_string} + @liveexample{The example shows the result of `to_string`.,json_pointer__to_string} - @since version 2.0.0 - */ - std::string to_string() const - { - return std::accumulate( - reference_tokens.begin(), - reference_tokens.end(), - std::string{}, - [](const std::string& a, const std::string& b) { return a + "/" + escape(b); }); - } - - /// @copydoc to_string() - operator std::string() const { return to_string(); } - - /*! - @brief append another JSON pointer at the end of this JSON pointer - - @param[in] ptr JSON pointer to append - @return JSON pointer with @a ptr appended - - @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} - - @complexity Linear in the length of @a ptr. - - @sa @ref operator/=(std::string) to append a reference token - @sa @ref operator/=(std::size_t) to append an array index - @sa @ref operator/(const json_pointer&, const json_pointer&) for a binary operator - - @since version 3.6.0 - */ - json_pointer& operator/=(const json_pointer& ptr) - { - reference_tokens.insert( - reference_tokens.end(), ptr.reference_tokens.begin(), ptr.reference_tokens.end()); - return *this; - } - - /*! - @brief append an unescaped reference token at the end of this JSON pointer - - @param[in] token reference token to append - @return JSON pointer with @a token appended without escaping @a token - - @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} - - @complexity Amortized constant. - - @sa @ref operator/=(const json_pointer&) to append a JSON pointer - @sa @ref operator/=(std::size_t) to append an array index - @sa @ref operator/(const json_pointer&, std::size_t) for a binary operator - - @since version 3.6.0 - */ - json_pointer& operator/=(std::string token) - { - push_back(std::move(token)); - return *this; - } - - /*! - @brief append an array index at the end of this JSON pointer - - @param[in] array_index array index ot append - @return JSON pointer with @a array_index appended - - @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} - - @complexity Amortized constant. - - @sa @ref operator/=(const json_pointer&) to append a JSON pointer - @sa @ref operator/=(std::string) to append a reference token - @sa @ref operator/(const json_pointer&, std::string) for a binary operator - - @since version 3.6.0 - */ - json_pointer& operator/=(std::size_t array_index) { return *this /= std::to_string(array_index); } - - /*! - @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON - pointer - - @param[in] lhs JSON pointer - @param[in] rhs JSON pointer - @return a new JSON pointer with @a rhs appended to @a lhs - - @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} - - @complexity Linear in the length of @a lhs and @a rhs. - - @sa @ref operator/=(const json_pointer&) to append a JSON pointer - - @since version 3.6.0 - */ - friend json_pointer operator/(const json_pointer& lhs, const json_pointer& rhs) - { - return json_pointer(lhs) /= rhs; - } - - /*! - @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer - - @param[in] ptr JSON pointer - @param[in] token reference token - @return a new JSON pointer with unescaped @a token appended to @a ptr - - @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} - - @complexity Linear in the length of @a ptr. - - @sa @ref operator/=(std::string) to append a reference token - - @since version 3.6.0 - */ - friend json_pointer operator/(const json_pointer& ptr, std::string token) - { - return json_pointer(ptr) /= std::move(token); - } - - /*! - @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer - - @param[in] ptr JSON pointer - @param[in] array_index array index - @return a new JSON pointer with @a array_index appended to @a ptr - - @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} - - @complexity Linear in the length of @a ptr. - - @sa @ref operator/=(std::size_t) to append an array index - - @since version 3.6.0 - */ - friend json_pointer operator/(const json_pointer& ptr, std::size_t array_index) - { - return json_pointer(ptr) /= array_index; - } - - /*! - @brief returns the parent of this JSON pointer - - @return parent of this JSON pointer; in case this JSON pointer is the root, - the root itself is returned - - @complexity Linear in the length of the JSON pointer. - - @liveexample{The example shows the result of `parent_pointer` for different - JSON Pointers.,json_pointer__parent_pointer} - - @since version 3.6.0 - */ - json_pointer parent_pointer() const - { - if (empty()) + @since version 2.0.0 + */ + std::string to_string() const { + return std::accumulate( + reference_tokens.begin(), + reference_tokens.end(), + std::string{}, + [](const std::string& a, const std::string& b) { return a + "/" + escape(b); }); + } + + /// @copydoc to_string() + operator std::string() const { return to_string(); } + + /*! + @brief append another JSON pointer at the end of this JSON pointer + + @param[in] ptr JSON pointer to append + @return JSON pointer with @a ptr appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, const json_pointer&) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert( + reference_tokens.end(), ptr.reference_tokens.begin(), ptr.reference_tokens.end()); return *this; } - json_pointer res = *this; - res.pop_back(); - return res; - } + /*! + @brief append an unescaped reference token at the end of this JSON pointer - /*! - @brief remove last reference token + @param[in] token reference token to append + @return JSON pointer with @a token appended without escaping @a token - @pre not `empty()` + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} - @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back} + @complexity Amortized constant. - @complexity Constant. + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, std::size_t) for a binary operator - @throw out_of_range.405 if JSON pointer has no parent - - @since version 3.6.0 - */ - void pop_back() - { - if (JSON_UNLIKELY(empty())) + @since version 3.6.0 + */ + json_pointer& operator/=(std::string token) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + push_back(std::move(token)); + return *this; } - reference_tokens.pop_back(); - } + /*! + @brief append an array index at the end of this JSON pointer - /*! - @brief return last reference token + @param[in] array_idx array index to append + @return JSON pointer with @a array_idx appended - @pre not `empty()` - @return last reference token + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} - @liveexample{The example shows the usage of `back`.,json_pointer__back} + @complexity Amortized constant. - @complexity Constant. + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/(const json_pointer&, std::string) for a binary operator - @throw out_of_range.405 if JSON pointer has no parent + @since version 3.6.0 + */ + json_pointer& operator/=(std::size_t array_idx) { return *this /= std::to_string(array_idx); } - @since version 3.6.0 - */ - const std::string& back() - { - if (JSON_UNLIKELY(empty())) + /*! + @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON + pointer + + @param[in] lhs JSON pointer + @param[in] rhs JSON pointer + @return a new JSON pointer with @a rhs appended to @a lhs + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a lhs and @a rhs. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& lhs, const json_pointer& rhs) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + return json_pointer(lhs) /= rhs; } - return reference_tokens.back(); - } + /*! + @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer - /*! - @brief append an unescaped token at the end of the reference pointer + @param[in] ptr JSON pointer + @param[in] token reference token + @return a new JSON pointer with unescaped @a token appended to @a ptr - @param[in] token token to add + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} - @complexity Amortized constant. + @complexity Linear in the length of @a ptr. - @liveexample{The example shows the result of `push_back` for different - JSON Pointers.,json_pointer__push_back} + @sa @ref operator/=(std::string) to append a reference token - @since version 3.6.0 - */ - void push_back(const std::string& token) { reference_tokens.push_back(token); } - - /// @copydoc push_back(const std::string&) - void push_back(std::string&& token) { reference_tokens.push_back(std::move(token)); } - - /*! - @brief return whether pointer points to the root document - - @return true iff the JSON pointer points to the root document - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @liveexample{The example shows the result of `empty` for different JSON - Pointers.,json_pointer__empty} - - @since version 3.6.0 - */ - bool empty() const noexcept { return reference_tokens.empty(); } - -private: - /*! - @param[in] s reference token to be converted into an array index - - @return integer representation of @a s - - @throw out_of_range.404 if string @a s could not be converted to an integer - */ - static int array_index(const std::string& s) - { - std::size_t processed_chars = 0; - const int res = std::stoi(s, &processed_chars); - - // check if the string was completely read - if (JSON_UNLIKELY(processed_chars != s.size())) + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::string token) { - JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + return json_pointer(ptr) /= std::move(token); } - return res; - } + /*! + @brief create a new JSON pointer by appending the array-index-token at the end of the JSON + pointer - json_pointer top() const - { - if (JSON_UNLIKELY(empty())) + @param[in] ptr JSON pointer + @param[in] array_idx array index + @return a new JSON pointer with @a array_idx appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::size_t) to append an array index + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx) { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + return json_pointer(ptr) /= array_idx; } - json_pointer result = *this; - result.reference_tokens = {reference_tokens[0]}; - return result; - } + /*! + @brief returns the parent of this JSON pointer - /*! - @brief create and return a reference to the pointed to value + @return parent of this JSON pointer; in case this JSON pointer is the root, + the root itself is returned - @complexity Linear in the number of reference tokens. + @complexity Linear in the length of the JSON pointer. - @throw parse_error.109 if array index is not a number - @throw type_error.313 if value cannot be unflattened - */ - BasicJsonType& get_and_create(BasicJsonType& j) const - { - using size_type = typename BasicJsonType::size_type; - auto result = &j; + @liveexample{The example shows the result of `parent_pointer` for different + JSON Pointers.,json_pointer__parent_pointer} - // in case no reference tokens exist, return a reference to the JSON value - // j which will be overwritten by a primitive value - for (const auto& reference_token : reference_tokens) + @since version 3.6.0 + */ + json_pointer parent_pointer() const { - switch (result->m_type) + if (empty()) { - case detail::value_t::null: { - if (reference_token == "0") - { - // start a new array if reference token is 0 - result = &result->operator[](0); - } - else - { - // start a new object otherwise - result = &result->operator[](reference_token); - } - break; - } - - case detail::value_t::object: { - // create an entry in the object - result = &result->operator[](reference_token); - break; - } - - case detail::value_t::array: { - // create an entry in the array - JSON_TRY - { - result = &result->operator[](static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create( - 109, 0, "array index '" + reference_token + "' is not a number")); - } - break; - } - - /* - The following code is only reached if there exists a reference - token _and_ the current value is primitive. In this case, we have - an error situation, because primitive values may only occur as - single value; that is, with an empty list of reference tokens. - */ - default: - JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); - } - } - - return *result; - } - - /*! - @brief return a reference to the pointed to value - - @note This version does not throw if a value is not present, but tries to - create nested values instead. For instance, calling this function - with pointer `"/this/that"` on a null value is equivalent to calling - `operator[]("this").operator[]("that")` on that value, effectively - changing the null value to an object. - - @param[in] ptr a JSON value - - @return reference to the JSON value pointed to by the JSON pointer - - @complexity Linear in the length of the JSON pointer. - - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - BasicJsonType& get_unchecked(BasicJsonType* ptr) const - { - using size_type = typename BasicJsonType::size_type; - for (const auto& reference_token : reference_tokens) - { - // convert null values to arrays or objects before continuing - if (ptr->m_type == detail::value_t::null) - { - // check if reference token is a number - const bool nums - = std::all_of(reference_token.begin(), reference_token.end(), [](const char x) { - return x >= '0' and x <= '9'; - }); - - // change value to array for numbers or "-" or to object otherwise - *ptr = (nums or reference_token == "-") ? detail::value_t::array : detail::value_t::object; + return *this; } - switch (ptr->m_type) - { - case detail::value_t::object: { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case detail::value_t::array: { - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create( - 106, 0, "array index '" + reference_token + "' must not begin with '0'")); - } - - if (reference_token == "-") - { - // explicitly treat "-" as index beyond the end - ptr = &ptr->operator[](ptr->m_value.array->size()); - } - else - { - // convert array index to number; unchecked access - JSON_TRY - { - ptr = &ptr->operator[](static_cast(array_index(reference_token))); - } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create( - 109, 0, "array index '" + reference_token + "' is not a number")); - } - } - break; - } - - default: - JSON_THROW(detail::out_of_range::create( - 404, "unresolved reference token '" + reference_token + "'")); - } + json_pointer res = *this; + res.pop_back(); + return res; } - return *ptr; - } + /*! + @brief remove last reference token - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - BasicJsonType& get_checked(BasicJsonType* ptr) const - { - using size_type = typename BasicJsonType::size_type; - for (const auto& reference_token : reference_tokens) + @pre not `empty()` + + @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + void pop_back() { - switch (ptr->m_type) + if (_az_JSON_HEDLEY_UNLIKELY(empty())) { - case detail::value_t::object: { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } - - case detail::value_t::array: { - if (JSON_UNLIKELY(reference_token == "-")) - { - // "-" always fails the range check - JSON_THROW(detail::out_of_range::create( - 402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) - + ") is out of range")); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create( - 106, 0, "array index '" + reference_token + "' must not begin with '0'")); - } - - // note: at performs range check - JSON_TRY { ptr = &ptr->at(static_cast(array_index(reference_token))); } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create( - 109, 0, "array index '" + reference_token + "' is not a number")); - } - break; - } - - default: - JSON_THROW(detail::out_of_range::create( - 404, "unresolved reference token '" + reference_token + "'")); + _az_JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); } + + reference_tokens.pop_back(); } - return *ptr; - } + /*! + @brief return last reference token - /*! - @brief return a const reference to the pointed to value + @pre not `empty()` + @return last reference token - @param[in] ptr a JSON value + @liveexample{The example shows the usage of `back`.,json_pointer__back} - @return const reference to the JSON value pointed to by the JSON - pointer + @complexity Constant. - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const - { - using size_type = typename BasicJsonType::size_type; - for (const auto& reference_token : reference_tokens) + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + const std::string& back() const { - switch (ptr->m_type) + if (_az_JSON_HEDLEY_UNLIKELY(empty())) { - case detail::value_t::object: { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case detail::value_t::array: { - if (JSON_UNLIKELY(reference_token == "-")) - { - // "-" cannot be used for const access - JSON_THROW(detail::out_of_range::create( - 402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) - + ") is out of range")); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create( - 106, 0, "array index '" + reference_token + "' must not begin with '0'")); - } - - // use unchecked array access - JSON_TRY { ptr = &ptr->operator[](static_cast(array_index(reference_token))); } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create( - 109, 0, "array index '" + reference_token + "' is not a number")); - } - break; - } - - default: - JSON_THROW(detail::out_of_range::create( - 404, "unresolved reference token '" + reference_token + "'")); + _az_JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); } + + return reference_tokens.back(); } - return *ptr; - } + /*! + @brief append an unescaped token at the end of the reference pointer - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - const BasicJsonType& get_checked(const BasicJsonType* ptr) const - { - using size_type = typename BasicJsonType::size_type; - for (const auto& reference_token : reference_tokens) + @param[in] token token to add + + @complexity Amortized constant. + + @liveexample{The example shows the result of `push_back` for different + JSON Pointers.,json_pointer__push_back} + + @since version 3.6.0 + */ + void push_back(const std::string& token) { reference_tokens.push_back(token); } + + /// @copydoc push_back(const std::string&) + void push_back(std::string&& token) { reference_tokens.push_back(std::move(token)); } + + /*! + @brief return whether pointer points to the root document + + @return true iff the JSON pointer points to the root document + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example shows the result of `empty` for different JSON + Pointers.,json_pointer__empty} + + @since version 3.6.0 + */ + bool empty() const noexcept { return reference_tokens.empty(); } + + private: + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw out_of_range.404 if string @a s could not be converted to an integer + */ + static int array_index(const std::string& s) { - switch (ptr->m_type) + // error condition (cf. RFC 6901, Sect. 4) + if (_az_JSON_HEDLEY_UNLIKELY(s.size() > 1 and s[0] == '0')) { - case detail::value_t::object: { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } - - case detail::value_t::array: { - if (JSON_UNLIKELY(reference_token == "-")) - { - // "-" always fails the range check - JSON_THROW(detail::out_of_range::create( - 402, - "array index '-' (" + std::to_string(ptr->m_value.array->size()) - + ") is out of range")); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) - { - JSON_THROW(detail::parse_error::create( - 106, 0, "array index '" + reference_token + "' must not begin with '0'")); - } - - // note: at performs range check - JSON_TRY { ptr = &ptr->at(static_cast(array_index(reference_token))); } - JSON_CATCH(std::invalid_argument&) - { - JSON_THROW(detail::parse_error::create( - 109, 0, "array index '" + reference_token + "' is not a number")); - } - break; - } - - default: - JSON_THROW(detail::out_of_range::create( - 404, "unresolved reference token '" + reference_token + "'")); + _az_JSON_THROW( + detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'")); } + + // error condition (cf. RFC 6901, Sect. 4) + if (_az_JSON_HEDLEY_UNLIKELY(s.size() > 1 and not(s[0] >= '1' and s[0] <= '9'))) + { + _az_JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number")); + } + + std::size_t processed_chars = 0; + int res = 0; + _az_JSON_TRY { res = std::stoi(s, &processed_chars); } + _az_JSON_CATCH(std::out_of_range&) + { + _az_JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + } + + // check if the string was completely read + if (_az_JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) + { + _az_JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + } + + return res; } - return *ptr; - } - - /*! - @brief split the string input to reference tokens - - @note This function is only called by the json_pointer constructor. - All exceptions below are documented there. - - @throw parse_error.107 if the pointer is not empty or begins with '/' - @throw parse_error.108 if character '~' is not followed by '0' or '1' - */ - static std::vector split(const std::string& reference_string) - { - std::vector result; - - // special case: empty reference string -> no reference tokens - if (reference_string.empty()) + json_pointer top() const { + if (_az_JSON_HEDLEY_UNLIKELY(empty())) + { + _az_JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; return result; } - // check if nonempty reference string begins with slash - if (JSON_UNLIKELY(reference_string[0] != '/')) - { - JSON_THROW(detail::parse_error::create( - 107, - 1, - "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'")); - } + /*! + @brief create and return a reference to the pointed to value - // extract the reference tokens: - // - slash: position of the last read slash (or end of string) - // - start: position after the previous slash - for ( - // search for the first slash after the first character - std::size_t slash = reference_string.find_first_of('/', 1), - // set the beginning of the first reference token - start = 1; - // we can stop if start == 0 (if slash == std::string::npos) - start != 0; - // set the beginning of the next reference token - // (will eventually be 0 if slash == std::string::npos) - start = (slash == std::string::npos) ? 0 : slash + 1, - // find next slash - slash = reference_string.find_first_of('/', start)) - { - // use the text between the beginning of the reference token - // (start) and the last slash (slash). - auto reference_token = reference_string.substr(start, slash - start); + @complexity Linear in the number of reference tokens. - // check reference tokens are properly escaped - for (std::size_t pos = reference_token.find_first_of('~'); pos != std::string::npos; - pos = reference_token.find_first_of('~', pos + 1)) + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + BasicJsonType& get_and_create(BasicJsonType& j) const + { + using size_type = typename BasicJsonType::size_type; + auto result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) { - assert(reference_token[pos] == '~'); - - // ~ must be followed by 0 or 1 - if (JSON_UNLIKELY( - pos == reference_token.size() - 1 - or (reference_token[pos + 1] != '0' and reference_token[pos + 1] != '1'))) + switch (result->type()) { - JSON_THROW(detail::parse_error::create( - 108, 0, "escape character '~' must be followed with '0' or '1'")); + case detail::value_t::null: { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: { + // create an entry in the array + result = &result->operator[](static_cast(array_index(reference_token))); + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + default: + _az_JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); } } - // finally, store the reference token - unescape(reference_token); - result.push_back(reference_token); + return *result; } - return result; - } + /*! + @brief return a reference to the pointed to value - /*! - @brief replace all occurrences of a substring by another string + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. - @param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t - @param[in] f the substring to replace with @a t - @param[in] t the string to replace @a f + @param[in] ptr a JSON value - @pre The search string @a f must not be empty. **This precondition is - enforced with an assertion.** + @return reference to the JSON value pointed to by the JSON pointer - @since version 2.0.0 - */ - static void replace_substring(std::string& s, const std::string& f, const std::string& t) - { - assert(not f.empty()); - for (auto pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find next occurrence of f + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const { - } - } - - /// escape "~" to "~0" and "/" to "~1" - static std::string escape(std::string s) - { - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); - return s; - } - - /// unescape "~1" to tilde and "~0" to slash (order is important!) - static void unescape(std::string& s) - { - replace_substring(s, "~1", "/"); - replace_substring(s, "~0", "~"); - } - - /*! - @param[in] reference_string the reference string to the current value - @param[in] value the value to consider - @param[in,out] result the result object to insert values to - - @note Empty objects or arrays are flattened to `null`. - */ - static void flatten( - const std::string& reference_string, - const BasicJsonType& value, - BasicJsonType& result) - { - switch (value.m_type) - { - case detail::value_t::array: { - if (value.m_value.array->empty()) + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) { - // flatten empty array as null - result[reference_string] = nullptr; + // check if reference token is a number + const bool nums = std::all_of( + reference_token.begin(), reference_token.end(), [](const unsigned char x) { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr + = (nums or reference_token == "-") ? detail::value_t::array : detail::value_t::object; } - else + + switch (ptr->type()) { - // iterate array and use index as reference string - for (std::size_t i = 0; i < value.m_value.array->size(); ++i) - { - flatten( - reference_string + "/" + std::to_string(i), - value.m_value.array->operator[](i), - result); + case detail::value_t::object: { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: { + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + ptr = &ptr->operator[](static_cast(array_index(reference_token))); + } + break; + } + + default: + _az_JSON_THROW(detail::out_of_range::create( + 404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: { + if (_az_JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + _az_JSON_THROW(detail::out_of_range::create( + 402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // note: at performs range check + ptr = &ptr->at(static_cast(array_index(reference_token))); + break; + } + + default: + _az_JSON_THROW(detail::out_of_range::create( + 404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: { + if (_az_JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + _az_JSON_THROW(detail::out_of_range::create( + 402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // use unchecked array access + ptr = &ptr->operator[](static_cast(array_index(reference_token))); + break; + } + + default: + _az_JSON_THROW(detail::out_of_range::create( + 404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: { + if (_az_JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + _az_JSON_THROW(detail::out_of_range::create( + 402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // note: at performs range check + ptr = &ptr->at(static_cast(array_index(reference_token))); + break; + } + + default: + _az_JSON_THROW(detail::out_of_range::create( + 404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + bool contains(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: { + if (not ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: { + if (_az_JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + if (_az_JSON_HEDLEY_UNLIKELY( + reference_token.size() == 1 + and not("0" <= reference_token and reference_token <= "9"))) + { + // invalid char + return false; + } + if (_az_JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) + { + if (_az_JSON_HEDLEY_UNLIKELY( + not('1' <= reference_token[0] and reference_token[0] <= '9'))) + { + // first char should be between '1' and '9' + return false; + } + for (std::size_t i = 1; i < reference_token.size(); i++) + { + if (_az_JSON_HEDLEY_UNLIKELY( + not('0' <= reference_token[i] and reference_token[i] <= '9'))) + { + // other char should be between '0' and '9' + return false; + } + } + } + + const auto idx = static_cast(array_index(reference_token)); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); + break; + } + + default: { + // we do not expect primitive values if there is still a + // reference token to process + return false; } } - break; } - case detail::value_t::object: { - if (value.m_value.object->empty()) + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (_az_JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + _az_JSON_THROW(detail::parse_error::create( + 107, + 1, + "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'")); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == std::string::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = (slash == std::string::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) { - // flatten empty object as null - result[reference_string] = nullptr; - } - else - { - // iterate object and use keys as reference string - for (const auto& element : *value.m_value.object) + assert(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (_az_JSON_HEDLEY_UNLIKELY( + pos == reference_token.size() - 1 + or (reference_token[pos + 1] != '0' and reference_token[pos + 1] != '1'))) { - flatten(reference_string + "/" + escape(element.first), element.second, result); + _az_JSON_THROW(detail::parse_error::create( + 108, 0, "escape character '~' must be followed with '0' or '1'")); } } - break; + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); } - default: { - // add primitive value with its reference string - result[reference_string] = value; - break; - } - } - } - - /*! - @param[in] value flattened JSON - - @return unflattened JSON - - @throw parse_error.109 if array index is not a number - @throw type_error.314 if value is not an object - @throw type_error.315 if object values are not primitive - @throw type_error.313 if value cannot be unflattened - */ - static BasicJsonType unflatten(const BasicJsonType& value) - { - if (JSON_UNLIKELY(not value.is_object())) - { - JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); + return result; } - BasicJsonType result; + /*! + @brief replace all occurrences of a substring by another string - // iterate the JSON object values - for (const auto& element : *value.m_value.object) + @param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t + @param[in] f the substring to replace with @a t + @param[in] t the string to replace @a f + + @pre The search string @a f must not be empty. **This precondition is + enforced with an assertion.** + + @since version 2.0.0 + */ + static void replace_substring(std::string& s, const std::string& f, const std::string& t) { - if (JSON_UNLIKELY(not element.second.is_primitive())) + assert(not f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f { - JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); } - - // assign value to reference pointed to by JSON pointer; Note that if - // the JSON pointer is "" (i.e., points to the whole value), function - // get_and_create returns a reference to result itself. An assignment - // will then create a primitive value. - json_pointer(element.first).get_and_create(result) = element.second; } - return result; - } + /// escape "~" to "~0" and "/" to "~1" + static std::string escape(std::string s) + { + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } - /*! - @brief compares two JSON pointers for equality + /// unescape "~1" to tilde and "~0" to slash (order is important!) + static void unescape(std::string& s) + { + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); + } - @param[in] lhs JSON pointer to compare - @param[in] rhs JSON pointer to compare - @return whether @a lhs is equal to @a rhs + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to - @complexity Linear in the length of the JSON pointer + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten( + const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten( + reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), + result); + } + } + break; + } - @exceptionsafety No-throw guarantee: this function never throws exceptions. - */ - friend bool operator==(json_pointer const& lhs, json_pointer const& rhs) noexcept - { - return lhs.reference_tokens == rhs.reference_tokens; - } + case detail::value_t::object: { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), element.second, result); + } + } + break; + } - /*! - @brief compares two JSON pointers for inequality + default: { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } - @param[in] lhs JSON pointer to compare - @param[in] rhs JSON pointer to compare - @return whether @a lhs is not equal @a rhs + /*! + @param[in] value flattened JSON - @complexity Linear in the length of the JSON pointer + @return unflattened JSON - @exceptionsafety No-throw guarantee: this function never throws exceptions. - */ - friend bool operator!=(json_pointer const& lhs, json_pointer const& rhs) noexcept - { - return not(lhs == rhs); - } + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType unflatten(const BasicJsonType& value) + { + if (_az_JSON_HEDLEY_UNLIKELY(not value.is_object())) + { + _az_JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); + } - /// the reference tokens - std::vector reference_tokens; -}; -} // namespace nlohmann + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (_az_JSON_HEDLEY_UNLIKELY(not element.second.is_primitive())) + { + _az_JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); + } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + /*! + @brief compares two JSON pointers for equality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is equal to @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator==(json_pointer const& lhs, json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } + + /*! + @brief compares two JSON pointers for inequality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is not equal @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator!=(json_pointer const& lhs, json_pointer const& rhs) noexcept + { + return not(lhs == rhs); + } + + /// the reference tokens + std::vector reference_tokens; + }; +}}}} // namespace Azure::Core::Internal::Json // #include @@ -9407,7 +12001,7 @@ private: // #include -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { template class json_ref { public: using value_type = BasicJsonType; @@ -9459,7 +12053,7 @@ namespace nlohmann { namespace detail { value_type* value_ref = nullptr; const bool is_rvalue; }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include @@ -9471,6 +12065,7 @@ namespace nlohmann { namespace detail { #include // reverse #include // array +#include // isnan, isinf #include // uint8_t, uint16_t, uint32_t, uint64_t #include // memcpy #include // numeric_limits @@ -9478,6 +12073,8 @@ namespace nlohmann { namespace detail { // #include +// #include + // #include #include // copy @@ -9488,8 +12085,9 @@ namespace nlohmann { namespace detail { #include // basic_ostream #include // basic_string #include // vector +// #include -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { /// abstract output adapter interface template struct output_adapter_protocol { @@ -9510,6 +12108,7 @@ namespace nlohmann { namespace detail { void write_character(CharType c) override { v.push_back(c); } + _az_JSON_HEDLEY_NON_NULL(2) void write_characters(const CharType* s, std::size_t length) override { std::copy(s, s + length, std::back_inserter(v)); @@ -9527,6 +12126,7 @@ namespace nlohmann { namespace detail { void write_character(CharType c) override { stream.put(c); } + _az_JSON_HEDLEY_NON_NULL(2) void write_characters(const CharType* s, std::size_t length) override { stream.write(s, static_cast(length)); @@ -9544,6 +12144,7 @@ namespace nlohmann { namespace detail { void write_character(CharType c) override { str.push_back(c); } + _az_JSON_HEDLEY_NON_NULL(2) void write_characters(const CharType* s, std::size_t length) override { str.append(s, length); } private: @@ -9573,9 +12174,9 @@ namespace nlohmann { namespace detail { private: output_adapter_t oa = nullptr; }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { /////////////////// // binary writer // /////////////////// @@ -9585,6 +12186,7 @@ namespace nlohmann { namespace detail { */ template class binary_writer { using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; public: /*! @@ -9608,7 +12210,7 @@ namespace nlohmann { namespace detail { } default: { - JSON_THROW(type_error::create( + _az_JSON_THROW(type_error::create( 317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()))); @@ -9726,8 +12328,40 @@ namespace nlohmann { namespace detail { } 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; } @@ -9806,6 +12440,42 @@ namespace nlohmann { namespace detail { break; } + case value_t::binary: { + // step 1: write control byte and the binary array size + const auto N = j.m_value.binary->size(); + if (N <= 0x17) + { + write_number(static_cast(0x40 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x58)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x59)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + oa->write_characters(reinterpret_cast(j.m_value.binary->data()), N); + + break; + } + case value_t::object: { // step 1: write control byte and the object size const auto N = j.m_value.object->size(); @@ -10050,6 +12720,97 @@ namespace nlohmann { namespace detail { break; } + case value_t::binary: { + // 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(); + + // step 1: write control byte and the byte string length + const auto N = j.m_value.binary->size(); + if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type; + bool fixed = true; + if (use_ext) + { + switch (N) + { + case 1: + output_type = 0xD4; // fixext 1 + break; + case 2: + output_type = 0xD5; // fixext 2 + break; + case 4: + output_type = 0xD6; // fixext 4 + break; + case 8: + output_type = 0xD7; // fixext 8 + break; + case 16: + output_type = 0xD8; // fixext 16 + break; + default: + output_type = 0xC7; // ext 8 + fixed = false; + break; + } + } + else + { + output_type = 0xC4; // bin 8 + fixed = false; + } + + oa->write_character(to_char_type(output_type)); + if (not fixed) + { + write_number(static_cast(N)); + } + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type; + if (use_ext) + { + output_type = 0xC8; // ext 16 + } + else + { + output_type = 0xC5; // bin 16 + } + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type; + if (use_ext) + { + output_type = 0xC9; // ext 32 + } + else + { + output_type = 0xC6; // bin 32 + } + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + + // step 1.5: if this is an ext type, write the subtype + if (use_ext) + { + write_number(static_cast(j.m_value.binary->subtype())); + } + + // step 2: write the byte string + oa->write_characters(reinterpret_cast(j.m_value.binary->data()), N); + + break; + } + case value_t::object: { // step 1: write control byte and the object size const auto N = j.m_value.object->size(); @@ -10185,6 +12946,48 @@ namespace nlohmann { namespace detail { break; } + case value_t::binary: { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + if (use_type and not j.m_value.binary->empty()) + { + assert(use_count); + oa->write_character(to_char_type('$')); + oa->write_character('U'); + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.binary->size(), true); + } + + if (use_type) + { + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + j.m_value.binary->size()); + } + else + { + for (size_t i = 0; i < j.m_value.binary->size(); ++i) + { + oa->write_character(to_char_type('U')); + oa->write_character(j.m_value.binary->data()[i]); + } + } + + if (not use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + case value_t::object: { if (add_prefix) { @@ -10248,9 +13051,9 @@ namespace nlohmann { namespace detail { static std::size_t calc_bson_entry_header_size(const string_t& name) { const auto it = name.find(static_cast(0)); - if (JSON_UNLIKELY(it != BasicJsonType::string_t::npos)) + if (_az_JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) { - JSON_THROW(out_of_range::create( + _az_JSON_THROW(out_of_range::create( 409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")")); } @@ -10364,7 +13167,7 @@ namespace nlohmann { namespace detail { } else { - JSON_THROW(out_of_range::create( + _az_JSON_THROW(out_of_range::create( 407, "integer number " + std::to_string(value) + " cannot be represented by BSON as it does not fit int64")); @@ -10387,17 +13190,28 @@ namespace nlohmann { namespace detail { */ static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value) { - std::size_t embedded_document_size = 0ul; std::size_t array_index = 0ul; - for (const auto& el : value) - { - embedded_document_size += calc_bson_element_size(std::to_string(array_index++), el); - } + const std::size_t embedded_document_size = std::accumulate( + std::begin(value), + std::end(value), + std::size_t(0), + [&array_index]( + std::size_t result, const typename BasicJsonType::array_t::value_type& el) { + return result + calc_bson_element_size(std::to_string(array_index++), el); + }); return sizeof(std::int32_t) + embedded_document_size + 1ul; } + /*! + @return The size of the BSON-encoded binary array @a value + */ + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + /*! @brief Writes a BSON element with key @a name and array @a value */ @@ -10416,6 +13230,19 @@ namespace nlohmann { namespace detail { oa->write_character(to_char_type(0x00)); } + /*! + @brief Writes a BSON element with key @a name and binary value @a value + */ + void write_bson_binary(const string_t& name, const binary_t& value) + { + write_bson_entry_header(name, 0x05); + + write_number(static_cast(value.size())); + write_number(value.has_subtype() ? value.subtype() : std::uint8_t(0x00)); + + oa->write_characters(reinterpret_cast(value.data()), value.size()); + } + /*! @brief Calculates the size necessary to serialize the JSON value @a j with its @a name @return The calculated size for the BSON document entry for @a j with the given @a name. @@ -10431,6 +13258,9 @@ namespace nlohmann { namespace detail { case value_t::array: return header_size + calc_bson_array_size(*j.m_value.array); + case value_t::binary: + return header_size + calc_bson_binary_size(*j.m_value.binary); + case value_t::boolean: return header_size + 1ul; @@ -10474,6 +13304,9 @@ namespace nlohmann { namespace detail { case value_t::array: return write_bson_array(name, *j.m_value.array); + case value_t::binary: + return write_bson_binary(name, *j.m_value.binary); + case value_t::boolean: return write_bson_boolean(name, j.m_value.boolean); @@ -10511,7 +13344,7 @@ namespace nlohmann { namespace detail { std::size_t document_size = std::accumulate( value.begin(), value.end(), - 0ul, + std::size_t(0), [](size_t result, const typename BasicJsonType::object_t::value_type& el) { return result += calc_bson_element_size(el.first, el.second); }); @@ -10628,7 +13461,7 @@ namespace nlohmann { namespace detail { } else { - JSON_THROW(out_of_range::create( + _az_JSON_THROW(out_of_range::create( 407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64")); @@ -10696,7 +13529,7 @@ namespace nlohmann { namespace detail { // LCOV_EXCL_START else { - JSON_THROW(out_of_range::create( + _az_JSON_THROW(out_of_range::create( 407, "integer number " + std::to_string(n) + " cannot be represented by UBJSON as it does not fit int64")); @@ -10749,19 +13582,23 @@ namespace nlohmann { namespace detail { } case value_t::number_unsigned: { - if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + if (j.m_value.number_unsigned + <= static_cast((std::numeric_limits::max)())) { return 'i'; } - if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + if (j.m_value.number_unsigned + <= static_cast((std::numeric_limits::max)())) { return 'U'; } - if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + if (j.m_value.number_unsigned + <= static_cast((std::numeric_limits::max)())) { return 'I'; } - if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + if (j.m_value.number_unsigned + <= static_cast((std::numeric_limits::max)())) { return 'l'; } @@ -10775,7 +13612,8 @@ namespace nlohmann { namespace detail { case value_t::string: return 'S'; - case value_t::array: + case value_t::array: // fallthrough + case value_t::binary: return '['; case value_t::object: @@ -10849,7 +13687,7 @@ namespace nlohmann { namespace detail { static_assert( sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); - static_assert(std::is_pod::value, "CharType must be POD"); + static_assert(std::is_trivial::value, "CharType must be trivial"); CharType result; std::memcpy(&result, &x, sizeof(x)); return result; @@ -10874,12 +13712,12 @@ namespace nlohmann { namespace detail { 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; }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include @@ -10888,7 +13726,6 @@ namespace nlohmann { namespace detail { #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 @@ -10899,18 +13736,23 @@ namespace nlohmann { namespace detail { #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 -namespace nlohmann { namespace detail { +// #include + +// #include + +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { /*! @brief implements the Grisu2 algorithm for binary to decimal floating-point @@ -11226,7 +14068,7 @@ namespace nlohmann { namespace detail { // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) // ==> 2^(alpha - e - 1) <= c // - // If c were an exakt power of ten, i.e. c = 10^k, one may determine k as + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as // // k = ceil( log_10( 2^(alpha - e - 1) ) ) // = ceil( (alpha - e - 1) * log_10(2) ) @@ -11621,7 +14463,7 @@ namespace nlohmann { namespace detail { // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * // 2^e // - assert(p2 <= std::numeric_limits::max() / 10); + assert(p2 <= (std::numeric_limits::max)() / 10); p2 *= 10; const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e @@ -11687,6 +14529,7 @@ namespace nlohmann { namespace detail { len is the length of the buffer (number of decimal digits) The buffer must be large enough, i.e. >= max_digits10. */ + _az_JSON_HEDLEY_NON_NULL(1) inline void grisu2(char* buf, int& len, int& decimal_exponent, diyfp m_minus, diyfp v, diyfp m_plus) { @@ -11746,6 +14589,7 @@ namespace nlohmann { namespace detail { The buffer must be large enough, i.e. >= max_digits10. */ template + _az_JSON_HEDLEY_NON_NULL(1) void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) { static_assert( @@ -11784,6 +14628,8 @@ namespace nlohmann { namespace detail { @return a pointer to the element following the exponent. @pre -1000 < e < 1000 */ + _az_JSON_HEDLEY_NON_NULL(1) + _az_JSON_HEDLEY_RETURNS_NON_NULL inline char* append_exponent(char* buf, int e) { assert(e > -1000); @@ -11834,6 +14680,8 @@ namespace nlohmann { namespace detail { @pre min_exp < 0 @pre max_exp > 0 */ + _az_JSON_HEDLEY_NON_NULL(1) + _az_JSON_HEDLEY_RETURNS_NON_NULL inline char* format_buffer(char* buf, int len, int decimal_exponent, int min_exp, int max_exp) { assert(min_exp < 0); @@ -11851,11 +14699,11 @@ namespace nlohmann { namespace detail { // digits[000] // len <= max_exp + 2 - std::memset(buf + k, '0', static_cast(n - k)); + std::memset(buf + k, '0', static_cast(n) - static_cast(k)); // Make it look like a floating-point number (#362, #378) buf[n + 0] = '.'; buf[n + 1] = '0'; - return buf + (n + 2); + return buf + (static_cast(n) + 2); } if (0 < n and n <= max_exp) @@ -11865,9 +14713,12 @@ namespace nlohmann { namespace detail { assert(k > n); - std::memmove(buf + (n + 1), buf + n, static_cast(k - n)); + std::memmove( + buf + (static_cast(n) + 1), + buf + n, + static_cast(k) - static_cast(n)); buf[n] = '.'; - return buf + (k + 1); + return buf + (static_cast(k) + 1U); } if (min_exp < n and n <= 0) @@ -11875,11 +14726,11 @@ namespace nlohmann { namespace detail { // 0.[000]digits // len <= 2 + (-min_exp - 1) + max_digits10 - std::memmove(buf + (2 + -n), buf, static_cast(k)); + std::memmove(buf + (2 + static_cast(-n)), buf, static_cast(k)); buf[0] = '0'; buf[1] = '.'; std::memset(buf + 2, '0', static_cast(-n)); - return buf + (2 + (-n) + k); + return buf + (2U + static_cast(-n) + static_cast(k)); } if (k == 1) @@ -11894,9 +14745,9 @@ namespace nlohmann { namespace detail { // d.igitsE+123 // len <= max_digits10 + 1 + 5 - std::memmove(buf + 2, buf + 1, static_cast(k - 1)); + std::memmove(buf + 2, buf + 1, static_cast(k) - 1); buf[1] = '.'; - buf += 1 + k; + buf += 1 + static_cast(k); } *buf++ = 'e'; @@ -11915,7 +14766,9 @@ namespace nlohmann { namespace detail { @note The buffer must be large enough. @note The result is NOT null-terminated. */ - template char* to_chars(char* first, const char* last, FloatType value) + template + _az_JSON_HEDLEY_NON_NULL(1, 2) + _az_JSON_HEDLEY_RETURNS_NON_NULL char* to_chars(char* first, const char* last, FloatType value) { static_cast(last); // maybe unused - fix warning assert(std::isfinite(value)); @@ -11960,7 +14813,7 @@ namespace nlohmann { namespace detail { return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); } -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include @@ -11974,7 +14827,7 @@ namespace nlohmann { namespace detail { // #include -namespace nlohmann { namespace detail { +namespace Azure { namespace Core { namespace Internal { namespace Json { namespace detail { /////////////////// // serialization // /////////////////// @@ -11992,6 +14845,7 @@ namespace nlohmann { namespace detail { 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_char_t = typename BasicJsonType::binary_t::value_type; static constexpr std::uint8_t UTF8_ACCEPT = 0; static constexpr std::uint8_t UTF8_REJECT = 1; @@ -12030,11 +14884,16 @@ namespace nlohmann { namespace detail { - 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 + - 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] indent_step the indent level - @param[in] current_indent the current indent level (only used internally) + @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) */ void dump( const BasicJsonType& val, @@ -12058,7 +14917,7 @@ namespace nlohmann { namespace detail { // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - if (JSON_UNLIKELY(indent_string.size() < new_indent)) + if (_az_JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } @@ -12130,7 +14989,7 @@ namespace nlohmann { namespace detail { // variable to hold indentation for recursive calls const auto new_indent = current_indent + indent_step; - if (JSON_UNLIKELY(indent_string.size() < new_indent)) + if (_az_JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) { indent_string.resize(indent_string.size() * 2, ' '); } @@ -12180,6 +15039,76 @@ namespace nlohmann { namespace detail { return; } + case value_t::binary: { + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (_az_JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (not val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_characters("{\"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 + { + o->write_characters("null}", 5); + } + } + return; + } + case value_t::boolean: { if (val.m_value.boolean) { @@ -12363,7 +15292,7 @@ namespace nlohmann { namespace detail { case error_handler_t::strict: { std::string sn(3, '\0'); (std::snprintf)(&sn[0], sn.size(), "%.2X", byte); - JSON_THROW(type_error::create( + _az_JSON_THROW(type_error::create( 316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn)); } @@ -12443,7 +15372,7 @@ namespace nlohmann { namespace detail { } // we finished processing the string - if (JSON_LIKELY(state == UTF8_ACCEPT)) + if (_az_JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT)) { // write buffer if (bytes > 0) @@ -12459,7 +15388,7 @@ namespace nlohmann { namespace detail { case error_handler_t::strict: { std::string sn(3, '\0'); (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast(s.back())); - JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn)); + _az_JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn)); } case error_handler_t::ignore: { @@ -12536,7 +15465,8 @@ namespace nlohmann { namespace detail { typename NumberType, detail::enable_if_t< std::is_same::value - or std::is_same::value, + or std::is_same::value + or std::is_same::value, int> = 0> void dump_integer(NumberType x) { @@ -12579,7 +15509,7 @@ namespace nlohmann { namespace detail { if (is_negative) { *buffer_ptr = '-'; - abs_value = static_cast(std::abs(static_cast(x))); + abs_value = remove_sign(static_cast(x)); // account one more byte for the minus sign n_chars = 1 + count_digits(abs_value); @@ -12657,7 +15587,8 @@ namespace nlohmann { namespace detail { void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) { char* begin = number_buffer.data(); - char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); + char* end + = ::Azure::Core::Internal::Json::detail::to_chars(begin, begin + number_buffer.size(), x); o->write_characters(begin, static_cast(end - begin)); } @@ -12768,10 +15699,38 @@ namespace nlohmann { namespace detail { codep = (state != UTF8_ACCEPT) ? (byte & 0x3fu) | (codep << 6u) : (0xFFu >> type) & (byte); - state = utf8d[256u + state * 16u + type]; + std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); + assert(index < 400); + state = utf8d[index]; return state; } + /* + * Overload to make the compiler happy while it is instantiating + * dump_integer for number_unsigned_t. + * Must never be called. + */ + number_unsigned_t remove_sign(number_unsigned_t x) + { + assert(false); // LCOV_EXCL_LINE + return x; // LCOV_EXCL_LINE + } + + /* + * Helper function for dump_integer + * + * This function takes a negative signed integer and returns its absolute + * value as unsigned integer. The plus/minus shuffling is necessary as we can + * not directly remove the sign of an arbitrary signed integer as the + * absolute values of INT_MIN and INT_MAX are usually not the same. See + * #1708 for details. + */ + inline number_unsigned_t remove_sign(number_integer_t x) noexcept + { + assert(x < 0 and x < (std::numeric_limits::max)()); + return static_cast(-(x + 1)) + 1; + } + private: /// the output of the serializer output_adapter_t o = nullptr; @@ -12797,7 +15756,7 @@ namespace nlohmann { namespace detail { /// error_handler how to react on decoding errors const error_handler_t error_handler; }; -}} // namespace nlohmann::detail +}}}}} // namespace Azure::Core::Internal::Json::detail // #include @@ -12808,7772 +15767,8459 @@ namespace nlohmann { namespace detail { @see https://github.com/nlohmann @since version 1.0.0 */ -namespace nlohmann { - -/*! -@brief a class to store JSON values - -@tparam ObjectType type for JSON objects (`std::map` by default; will be used -in @ref object_t) -@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used -in @ref array_t) -@tparam StringType type for JSON strings and object keys (`std::string` by -default; will be used in @ref string_t) -@tparam BooleanType type for JSON booleans (`bool` by default; will be used -in @ref boolean_t) -@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by -default; will be used in @ref number_integer_t) -@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c -`uint64_t` by default; will be used in @ref number_unsigned_t) -@tparam NumberFloatType type for JSON floating-point numbers (`double` by -default; will be used in @ref number_float_t) -@tparam AllocatorType type of the allocator to use (`std::allocator` by -default) -@tparam JSONSerializer the serializer to resolve internal calls to `to_json()` -and `from_json()` (@ref adl_serializer by default) - -@requirement The class satisfies the following concept requirements: -- Basic - - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible): - JSON values can be default constructed. The result will be a JSON null - value. - - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible): - A JSON value can be constructed from an rvalue argument. - - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible): - A JSON value can be copy-constructed from an lvalue expression. - - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable): - A JSON value van be assigned from an rvalue argument. - - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable): - A JSON value can be copy-assigned from an lvalue expression. - - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible): - JSON values can be destructed. -- Layout - - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType): - JSON values have - [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout): - All non-static data members are private and standard layout types, the - class has no virtual functions or (virtual) base classes. -- Library-wide - - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable): - JSON values can be compared with `==`, see @ref - operator==(const_reference,const_reference). - - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable): - JSON values can be compared with `<`, see @ref - operator<(const_reference,const_reference). - - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable): - Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of - other compatible types, using unqualified function call @ref swap(). - - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer): - JSON values can be compared against `std::nullptr_t` objects which are used - to model the `null` value. -- Container - - [Container](https://en.cppreference.com/w/cpp/named_req/Container): - JSON values can be used like STL containers and provide iterator access. - - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer); - JSON values can be used like STL containers and provide reverse iterator - access. - -@invariant The member variables @a m_value and @a m_type have the following -relationship: -- If `m_type == value_t::object`, then `m_value.object != nullptr`. -- If `m_type == value_t::array`, then `m_value.array != nullptr`. -- If `m_type == value_t::string`, then `m_value.string != nullptr`. -The invariants are checked by member function assert_invariant(). - -@internal -@note ObjectType trick from http://stackoverflow.com/a/9860911 -@endinternal - -@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange -Format](http://rfc7159.net/rfc7159) - -@since version 1.0.0 - -@nosubgrouping -*/ -NLOHMANN_BASIC_JSON_TPL_DECLARATION -class basic_json { -private: - template friend struct detail::external_constructor; - friend ::nlohmann::json_pointer; - friend ::nlohmann::detail::parser; - friend ::nlohmann::detail::serializer; - template friend class ::nlohmann::detail::iter_impl; - template - friend class ::nlohmann::detail::binary_writer; - template friend class ::nlohmann::detail::binary_reader; - template friend class ::nlohmann::detail::json_sax_dom_parser; - template friend class ::nlohmann::detail::json_sax_dom_callback_parser; - - /// workaround type for MSVC - 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 primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; - template - using internal_iterator = ::nlohmann::detail::internal_iterator; - template using iter_impl = ::nlohmann::detail::iter_impl; - template - using iteration_proxy = ::nlohmann::detail::iteration_proxy; - template - using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; - - template - using output_adapter_t = ::nlohmann::detail::output_adapter_t; - - using binary_reader = ::nlohmann::detail::binary_reader; - template - using binary_writer = ::nlohmann::detail::binary_writer; - - using serializer = ::nlohmann::detail::serializer; - -public: - using value_t = detail::value_t; - /// JSON Pointer, see @ref nlohmann::json_pointer - using json_pointer = ::nlohmann::json_pointer; - template using json_serializer = JSONSerializer; - /// how to treat decoding errors - using error_handler_t = detail::error_handler_t; - /// helper type for initializer lists of basic_json values - using initializer_list_t = std::initializer_list>; - - using input_format_t = detail::input_format_t; - /// SAX interface type, see @ref nlohmann::json_sax - using json_sax_t = json_sax; - - //////////////// - // exceptions // - //////////////// - - /// @name exceptions - /// Classes to implement user-defined exceptions. - /// @{ - - /// @copydoc detail::exception - using exception = detail::exception; - /// @copydoc detail::parse_error - using parse_error = detail::parse_error; - /// @copydoc detail::invalid_iterator - using invalid_iterator = detail::invalid_iterator; - /// @copydoc detail::type_error - using type_error = detail::type_error; - /// @copydoc detail::out_of_range - using out_of_range = detail::out_of_range; - /// @copydoc detail::other_error - using other_error = detail::other_error; - - /// @} - - ///////////////////// - // container types // - ///////////////////// - - /// @name container types - /// The canonic container types to use @ref basic_json like any other STL - /// container. - /// @{ - - /// the type of elements in a basic_json container - using value_type = basic_json; - - /// the type of an element reference - using reference = value_type&; - /// the type of an element const reference - using const_reference = const value_type&; - - /// a type to represent differences between iterators - using difference_type = std::ptrdiff_t; - /// a type to represent container sizes - using size_type = std::size_t; - - /// the allocator type - using allocator_type = AllocatorType; - - /// the type of an element pointer - using pointer = typename std::allocator_traits::pointer; - /// the type of an element const pointer - using const_pointer = typename std::allocator_traits::const_pointer; - - /// an iterator for a basic_json container - using iterator = iter_impl; - /// a const iterator for a basic_json container - using const_iterator = iter_impl; - /// a reverse iterator for a basic_json container - using reverse_iterator = json_reverse_iterator; - /// a const reverse iterator for a basic_json container - using const_reverse_iterator = json_reverse_iterator; - - /// @} +namespace Azure { namespace Core { namespace Internal { namespace Json { /*! - @brief returns the allocator associated with the container - */ - static allocator_type get_allocator() { return allocator_type(); } + @brief a class to store JSON values - /*! - @brief returns version information on the library + @tparam ObjectType type for JSON objects (`std::map` by default; will be used + in @ref object_t) + @tparam ArrayType type for JSON arrays (`std::vector` by default; will be used + in @ref array_t) + @tparam StringType type for JSON strings and object keys (`std::string` by + default; will be used in @ref string_t) + @tparam BooleanType type for JSON booleans (`bool` by default; will be used + in @ref boolean_t) + @tparam NumberIntegerType type for JSON integer numbers (`int64_t` by + default; will be used in @ref number_integer_t) + @tparam NumberUnsignedType type for JSON unsigned integer numbers (@c + `uint64_t` by default; will be used in @ref number_unsigned_t) + @tparam NumberFloatType type for JSON floating-point numbers (`double` by + default; will be used in @ref number_float_t) + @tparam BinaryType type for packed binary data for compatibility with binary + serialization formats (`std::vector` by default; will be used in + @ref binary_t) + @tparam AllocatorType type of the allocator to use (`std::allocator` by + default) + @tparam JSONSerializer the serializer to resolve internal calls to `to_json()` + and `from_json()` (@ref adl_serializer by default) - This function returns a JSON object with information about the library, - including the version number and information on the platform and compiler. + @requirement The class satisfies the following concept requirements: + - Basic + - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible): + JSON values can be default constructed. The result will be a JSON null + value. + - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible): + A JSON value can be constructed from an rvalue argument. + - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible): + A JSON value can be copy-constructed from an lvalue expression. + - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable): + A JSON value van be assigned from an rvalue argument. + - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable): + A JSON value can be copy-assigned from an lvalue expression. + - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible): + JSON values can be destructed. + - Layout + - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType): + JSON values have + [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout): + All non-static data members are private and standard layout types, the + class has no virtual functions or (virtual) base classes. + - Library-wide + - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable): + JSON values can be compared with `==`, see @ref + operator==(const_reference,const_reference). + - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable): + JSON values can be compared with `<`, see @ref + operator<(const_reference,const_reference). + - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable): + Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of + other compatible types, using unqualified function call @ref swap(). + - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer): + JSON values can be compared against `std::nullptr_t` objects which are used + to model the `null` value. + - Container + - [Container](https://en.cppreference.com/w/cpp/named_req/Container): + JSON values can be used like STL containers and provide iterator access. + - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer); + JSON values can be used like STL containers and provide reverse iterator + access. - @return JSON object holding version information - key | description - ----------- | --------------- - `compiler` | Information on the used compiler. It is an object with the following keys: `c++` - (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, - `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). - `copyright` | The copyright line for the library as string. - `name` | The name of the library as string. - `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, - and `unknown`. `url` | The URL of the project as string. `version` | The version of the - library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by - [Semantic Versioning](http://semver.org), and `string` (the version string). + @invariant The member variables @a m_value and @a m_type have the following + relationship: + - If `m_type == value_t::object`, then `m_value.object != nullptr`. + - If `m_type == value_t::array`, then `m_value.array != nullptr`. + - If `m_type == value_t::string`, then `m_value.string != nullptr`. + The invariants are checked by member function assert_invariant(). - @liveexample{The following code shows an example output of the `meta()` - function.,meta} + @internal + @note ObjectType trick from https://stackoverflow.com/a/9860911 + @endinternal - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @complexity Constant. - - @since 2.1.0 - */ - JSON_NODISCARD - static basic_json meta() - { - basic_json result; - - result["copyright"] = "(C) 2013-2017 Niels Lohmann"; - result["name"] = "JSON for Modern C++"; - result["url"] = "https://github.com/nlohmann/json"; - result["version"]["string"] = std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." - + std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." - + std::to_string(NLOHMANN_JSON_VERSION_PATCH); - result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; - result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; - result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; - -#ifdef _WIN32 - result["platform"] = "win32"; -#elif defined __linux__ - result["platform"] = "linux"; -#elif defined __APPLE__ - result["platform"] = "apple"; -#elif defined __unix__ - result["platform"] = "unix"; -#else - result["platform"] = "unknown"; -#endif - -#if defined(__ICC) || defined(__INTEL_COMPILER) - result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; -#elif defined(__clang__) - result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; -#elif defined(__GNUC__) || defined(__GNUG__) - result["compiler"] - = {{"family", "gcc"}, - {"version", - std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." - + std::to_string(__GNUC_PATCHLEVEL__)}}; -#elif defined(__HP_cc) || defined(__HP_aCC) - result["compiler"] = "hp" -#elif defined(__IBMCPP__) - result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; -#elif defined(_MSC_VER) - result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; -#elif defined(__PGI) - result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; -#elif defined(__SUNPRO_CC) - result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; -#else - result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; -#endif - -#ifdef __cplusplus - result["compiler"]["c++"] = std::to_string(__cplusplus); -#else - result["compiler"]["c++"] = "unknown"; -#endif - return result; - } - - /////////////////////////// - // JSON value data types // - /////////////////////////// - - /// @name JSON value data types - /// The data types to store a JSON value. These types are derived from - /// the template arguments passed to class @ref basic_json. - /// @{ - -#if defined(JSON_HAS_CPP_14) - // Use transparent comparator if possible, combined with perfect forwarding - // on find() and count() calls prevents unnecessary string construction. - using object_comparator_t = std::less<>; -#else - using object_comparator_t = std::less; -#endif - - /*! - @brief a type for an object - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: - > An object is an unordered collection of zero or more name/value pairs, - > where a name is a string and a value is a string, number, boolean, null, - > object, or array. - - To store objects in C++, a type is defined by the template parameters - described below. - - @tparam ObjectType the container to store objects (e.g., `std::map` or - `std::unordered_map`) - @tparam StringType the type of the keys or names (e.g., `std::string`). - The comparison function `std::less` is used to order elements - inside the container. - @tparam AllocatorType the allocator to use for objects (e.g., - `std::allocator`) - - #### Default type - - With the default values for @a ObjectType (`std::map`), @a StringType - (`std::string`), and @a AllocatorType (`std::allocator`), the default - value for @a object_t is: - - @code {.cpp} - std::map< - std::string, // key_type - basic_json, // value_type - std::less, // key_compare - std::allocator> // allocator_type - > - @endcode - - #### Behavior - - The choice of @a object_t influences the behavior of the JSON class. With - the default type, objects have the following behavior: - - - When all names are unique, objects will be interoperable in the sense - that all software implementations receiving that object will agree on - the name-value mappings. - - When the names within an object are not unique, it is unspecified which - one of the values for a given key will be chosen. For instance, - `{"key": 2, "key": 1}` could be equal to either `{"key": 1}` or - `{"key": 2}`. - - Internally, name/value pairs are stored in lexicographical order of the - names. Objects will also be serialized (see @ref dump) in this order. - For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored - and serialized as `{"a": 2, "b": 1}`. - - When comparing objects, the order of the name/value pairs is irrelevant. - This makes objects interoperable in the sense that they will not be - affected by these differences. For instance, `{"b": 1, "a": 2}` and - `{"a": 2, "b": 1}` will be treated as equal. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. - - In this class, the object's limit of nesting is not explicitly constrained. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the - @ref max_size function of a JSON object. - - #### Storage - - Objects are stored as pointers in a @ref basic_json type. That is, for any - access to object values, a pointer of type `object_t*` must be - dereferenced. - - @sa @ref array_t -- type for an array value + @see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange + Format](http://rfc7159.net/rfc7159) @since version 1.0.0 - @note The order name/value pairs are added to the object is *not* - preserved by the library. Therefore, iterating an object may return - name/value pairs in a different order than they were originally stored. In - fact, keys will be traversed in alphabetical order as `std::map` with - `std::less` is used by default. Please note this behavior conforms to [RFC - 7159](http://rfc7159.net/rfc7159), because any order implements the - specified "unordered" nature of JSON objects. + @nosubgrouping */ - using object_t = ObjectType< - StringType, - basic_json, - object_comparator_t, - AllocatorType>>; - - /*! - @brief a type for an array - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: - > An array is an ordered sequence of zero or more values. - - To store objects in C++, a type is defined by the template parameters - explained below. - - @tparam ArrayType container type to store arrays (e.g., `std::vector` or - `std::list`) - @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) - - #### Default type - - With the default values for @a ArrayType (`std::vector`) and @a - AllocatorType (`std::allocator`), the default value for @a array_t is: - - @code {.cpp} - std::vector< - basic_json, // value_type - std::allocator // allocator_type - > - @endcode - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. - - In this class, the array's limit of nesting is not explicitly constrained. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the - @ref max_size function of a JSON array. - - #### Storage - - Arrays are stored as pointers in a @ref basic_json type. That is, for any - access to array values, a pointer of type `array_t*` must be dereferenced. - - @sa @ref object_t -- type for an object value - - @since version 1.0.0 - */ - using array_t = ArrayType>; - - /*! - @brief a type for a string - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: - > A string is a sequence of zero or more Unicode characters. - - To store objects in C++, a type is defined by the template parameter - described below. Unicode values are split by the JSON class into - byte-sized characters during deserialization. - - @tparam StringType the container to store strings (e.g., `std::string`). - Note this container is used for keys/names in objects, see @ref object_t. - - #### Default type - - With the default values for @a StringType (`std::string`), the default - value for @a string_t is: - - @code {.cpp} - std::string - @endcode - - #### Encoding - - Strings are stored in UTF-8 encoding. Therefore, functions like - `std::string::size()` or `std::string::length()` return the number of - bytes in the string rather than the number of characters or glyphs. - - #### String comparison - - [RFC 7159](http://rfc7159.net/rfc7159) states: - > Software implementations are typically required to test names of object - > members for equality. Implementations that transform the textual - > representation into sequences of Unicode code units and then perform the - > comparison numerically, code unit by code unit, are interoperable in the - > sense that implementations will agree in all cases on equality or - > inequality of two strings. For example, implementations that compare - > strings with escaped characters unconverted may incorrectly find that - > `"a\\b"` and `"a\u005Cb"` are not equal. - - This implementation is interoperable as it does compare strings code unit - by code unit. - - #### Storage - - String values are stored as pointers in a @ref basic_json type. That is, - for any access to string values, a pointer of type `string_t*` must be - dereferenced. - - @since version 1.0.0 - */ - using string_t = StringType; - - /*! - @brief a type for a boolean - - [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a - type which differentiates the two literals `true` and `false`. - - To store objects in C++, a type is defined by the template parameter @a - BooleanType which chooses the type to use. - - #### Default type - - With the default values for @a BooleanType (`bool`), the default value for - @a boolean_t is: - - @code {.cpp} - bool - @endcode - - #### Storage - - Boolean values are stored directly inside a @ref basic_json type. - - @since version 1.0.0 - */ - using boolean_t = BooleanType; - - /*! - @brief a type for a number (integer) - - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. - - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. - - To store integer numbers in C++, a type is defined by the template - parameter @a NumberIntegerType which chooses the type to use. - - #### Default type - - With the default values for @a NumberIntegerType (`int64_t`), the default - value for @a number_integer_t is: - - @code {.cpp} - int64_t - @endcode - - #### Default behavior - - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in integer literals lead to an interpretation as octal - number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. - During deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the range and precision of numbers. - - When the default type is used, the maximal integer number that can be - stored is `9223372036854775807` (INT64_MAX) and the minimal integer number - that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers - that are out of range will yield over/underflow when used in a - constructor. During deserialization, too large or small integer numbers - will be automatically be stored as @ref number_unsigned_t or @ref - number_float_t. - - [RFC 7159](http://rfc7159.net/rfc7159) further states: - > Note that when such software is used, numbers that are integers and are - > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense - > that implementations will agree exactly on their numeric values. - - As this range is a subrange of the exactly supported range [INT64_MIN, - INT64_MAX], this class's integer type is interoperable. - - #### Storage - - Integer number values are stored directly inside a @ref basic_json type. - - @sa @ref number_float_t -- type for number values (floating-point) - - @sa @ref number_unsigned_t -- type for number values (unsigned integer) - - @since version 1.0.0 - */ - using number_integer_t = NumberIntegerType; - - /*! - @brief a type for a number (unsigned) - - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. - - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. - - To store unsigned integer numbers in C++, a type is defined by the - template parameter @a NumberUnsignedType which chooses the type to use. - - #### Default type - - With the default values for @a NumberUnsignedType (`uint64_t`), the - default value for @a number_unsigned_t is: - - @code {.cpp} - uint64_t - @endcode - - #### Default behavior - - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in integer literals lead to an interpretation as octal - number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. - During deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the range and precision of numbers. - - When the default type is used, the maximal integer number that can be - stored is `18446744073709551615` (UINT64_MAX) and the minimal integer - number that can be stored is `0`. Integer numbers that are out of range - will yield over/underflow when used in a constructor. During - deserialization, too large or small integer numbers will be automatically - be stored as @ref number_integer_t or @ref number_float_t. - - [RFC 7159](http://rfc7159.net/rfc7159) further states: - > Note that when such software is used, numbers that are integers and are - > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense - > that implementations will agree exactly on their numeric values. - - As this range is a subrange (when considered in conjunction with the - number_integer_t type) of the exactly supported range [0, UINT64_MAX], - this class's integer type is interoperable. - - #### Storage - - Integer number values are stored directly inside a @ref basic_json type. - - @sa @ref number_float_t -- type for number values (floating-point) - @sa @ref number_integer_t -- type for number values (integer) - - @since version 2.0.0 - */ - using number_unsigned_t = NumberUnsignedType; - - /*! - @brief a type for a number (floating-point) - - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. - - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. - - To store floating-point numbers in C++, a type is defined by the template - parameter @a NumberFloatType which chooses the type to use. - - #### Default type - - With the default values for @a NumberFloatType (`double`), the default - value for @a number_float_t is: - - @code {.cpp} - double - @endcode - - #### Default behavior - - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in floating-point literals will be ignored. Internally, - the value will be stored as decimal number. For instance, the C++ - floating-point literal `01.2` will be serialized to `1.2`. During - deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. - - #### Limits - - [RFC 7159](http://rfc7159.net/rfc7159) states: - > This specification allows implementations to set limits on the range and - > precision of numbers accepted. Since software that implements IEEE - > 754-2008 binary64 (double precision) numbers is generally available and - > widely used, good interoperability can be achieved by implementations - > that expect no more precision or range than these provide, in the sense - > that implementations will approximate JSON numbers within the expected - > precision. - - This implementation does exactly follow this approach, as it uses double - precision floating-point numbers. Note values smaller than - `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` - will be stored as NaN internally and be serialized to `null`. - - #### Storage - - Floating-point number values are stored directly inside a @ref basic_json - type. - - @sa @ref number_integer_t -- type for number values (integer) - - @sa @ref number_unsigned_t -- type for number values (unsigned integer) - - @since version 1.0.0 - */ - using number_float_t = NumberFloatType; - - /// @} - -private: - /// helper for exception-safe object creation - template static T* create(Args&&... args) - { - AllocatorType alloc; - using AllocatorTraits = std::allocator_traits>; - - auto deleter = [&](T* object) { AllocatorTraits::deallocate(alloc, object, 1); }; - std::unique_ptr object(AllocatorTraits::allocate(alloc, 1), deleter); - AllocatorTraits::construct(alloc, object.get(), std::forward(args)...); - assert(object != nullptr); - return object.release(); - } - - //////////////////////// - // JSON value storage // - //////////////////////// - - /*! - @brief a JSON value - - The actual storage for a JSON value of the @ref basic_json class. This - union combines the different storage types for the JSON value types - defined in @ref value_t. - - JSON type | value_t type | used type - --------- | --------------- | ------------------------ - object | object | pointer to @ref object_t - array | array | pointer to @ref array_t - string | string | pointer to @ref string_t - boolean | boolean | @ref boolean_t - number | number_integer | @ref number_integer_t - number | number_unsigned | @ref number_unsigned_t - number | number_float | @ref number_float_t - null | null | *no value is stored* - - @note Variable-length types (objects, arrays, and strings) are stored as - pointers. The size of the union should not exceed 64 bits if the default - value types are used. - - @since version 1.0.0 - */ - union json_value - { - /// object (stored with pointer to save storage) - object_t* object; - /// array (stored with pointer to save storage) - array_t* array; - /// string (stored with pointer to save storage) - string_t* string; - /// boolean - boolean_t boolean; - /// number (integer) - number_integer_t number_integer; - /// number (unsigned integer) - number_unsigned_t number_unsigned; - /// number (floating-point) - number_float_t number_float; - - /// default constructor (for null values) - json_value() = default; - /// constructor for booleans - json_value(boolean_t v) noexcept : boolean(v) {} - /// constructor for numbers (integer) - json_value(number_integer_t v) noexcept : number_integer(v) {} - /// constructor for numbers (unsigned) - json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} - /// constructor for numbers (floating-point) - json_value(number_float_t v) noexcept : number_float(v) {} - /// constructor for empty values of a given type - json_value(value_t t) + _az_NLOHMANN_BASIC_JSON_TPL_DECLARATION + class basic_json { + private: + template friend struct detail::external_constructor; + friend ::Azure::Core::Internal::Json::json_pointer; + + template + friend class ::Azure::Core::Internal::Json::detail::parser; + friend ::Azure::Core::Internal::Json::detail::serializer; + template friend class ::Azure::Core::Internal::Json::detail::iter_impl; + template + friend class ::Azure::Core::Internal::Json::detail::binary_writer; + template + friend class ::Azure::Core::Internal::Json::detail::binary_reader; + template + friend class ::Azure::Core::Internal::Json::detail::json_sax_dom_parser; + template + friend class ::Azure::Core::Internal::Json::detail::json_sax_dom_callback_parser; + + /// workaround type for MSVC + using basic_json_t = _az_NLOHMANN_BASIC_JSON_TPL; + + // convenience aliases for types residing in namespace detail; + using lexer = ::Azure::Core::Internal::Json::detail::lexer_base; + + template + static ::Azure::Core::Internal::Json::detail::parser parser( + InputAdapterType adapter, + detail::parser_callback_t cb = nullptr, + bool allow_exceptions = true) { - switch (t) + return ::Azure::Core::Internal::Json::detail::parser( + std::move(adapter), std::move(cb), allow_exceptions); + } + + using primitive_iterator_t = ::Azure::Core::Internal::Json::detail::primitive_iterator_t; + template + using internal_iterator + = ::Azure::Core::Internal::Json::detail::internal_iterator; + template + using iter_impl = ::Azure::Core::Internal::Json::detail::iter_impl; + template + using iteration_proxy = ::Azure::Core::Internal::Json::detail::iteration_proxy; + template + using json_reverse_iterator + = ::Azure::Core::Internal::Json::detail::json_reverse_iterator; + + template + using output_adapter_t = ::Azure::Core::Internal::Json::detail::output_adapter_t; + + template + using binary_reader + = ::Azure::Core::Internal::Json::detail::binary_reader; + template + using binary_writer + = ::Azure::Core::Internal::Json::detail::binary_writer; + + using serializer = ::Azure::Core::Internal::Json::detail::serializer; + + public: + using value_t = detail::value_t; + /// JSON Pointer, see @ref nlohmann::json_pointer + using json_pointer = ::Azure::Core::Internal::Json::json_pointer; + template using json_serializer = JSONSerializer; + /// how to treat decoding errors + using error_handler_t = detail::error_handler_t; + /// helper type for initializer lists of basic_json values + using initializer_list_t = std::initializer_list>; + + using input_format_t = detail::input_format_t; + /// SAX interface type, see @ref Azure::Core::Internal::Json::json_sax + using json_sax_t = json_sax; + + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + /// @copydoc detail::exception + using exception = detail::exception; + /// @copydoc detail::parse_error + using parse_error = detail::parse_error; + /// @copydoc detail::invalid_iterator + using invalid_iterator = detail::invalid_iterator; + /// @copydoc detail::type_error + using type_error = detail::type_error; + /// @copydoc detail::out_of_range + using out_of_range = detail::out_of_range; + /// @copydoc detail::other_error + using other_error = detail::other_error; + + /// @} + + ///////////////////// + // container types // + ///////////////////// + + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ + + /// the type of elements in a basic_json container + using value_type = basic_json; + + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; + + /// the allocator type + using allocator_type = AllocatorType; + + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; + + /// an iterator for a basic_json container + using iterator = iter_impl; + /// a const iterator for a basic_json container + using const_iterator = iter_impl; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; + + /// @} + + /*! + @brief returns the allocator associated with the container + */ + static allocator_type get_allocator() { return allocator_type(); } + + // /*! + // @brief returns version information on the library + + // This function returns a JSON object with information about the library, + // including the version number and information on the platform and compiler. + + // @return JSON object holding version information + // key | description + // ----------- | --------------- + // `compiler` | Information on the used compiler. It is an object with the following keys: + // `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, + // `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the + // compiler version). `copyright` | The copyright line for the library as string. `name` | The + // name of the library as string. `platform` | The used platform as string. Possible values + // are `win32`, `linux`, `apple`, `unix`, and `unknown`. `url` | The URL of the project + // as string. `version` | The version of the library. It is an object with the following + // keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), + // and `string` (the version string). + + // @liveexample{The following code shows an example output of the `meta()` + // function.,meta} + + // @exceptionsafety Strong guarantee: if an exception is thrown, there are no + // changes to any JSON value. + + // @complexity Constant. + + // @since 2.1.0 + // */ + // _az_JSON_HEDLEY_WARN_UNUSED_RESULT + // static basic_json meta() + // { + // basic_json result; + + // result["copyright"] = "(C) 2013-2017 Niels Lohmann"; + // result["name"] = "JSON for Modern C++"; + // result["url"] = "https://github.com/nlohmann/json"; + // result["version"]["string"] = std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." + // + std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." + // + std::to_string(NLOHMANN_JSON_VERSION_PATCH); + // result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; + // result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; + // result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; + + // #ifdef _WIN32 + // result["platform"] = "win32"; + // #elif defined __linux__ + // result["platform"] = "linux"; + // #elif defined __APPLE__ + // result["platform"] = "apple"; + // #elif defined __unix__ + // result["platform"] = "unix"; + // #else + // result["platform"] = "unknown"; + // #endif + + // #if defined(__ICC) || defined(__INTEL_COMPILER) + // result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; + // #elif defined(__clang__) + // result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; + // #elif defined(__GNUC__) || defined(__GNUG__) + // result["compiler"] + // = {{"family", "gcc"}, + // {"version", + // std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + // + std::to_string(__GNUC_PATCHLEVEL__)}}; + // #elif defined(__HP_cc) || defined(__HP_aCC) + // result["compiler"] = "hp" + // #elif defined(__IBMCPP__) + // result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; + // #elif defined(_MSC_VER) + // result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; + // #elif defined(__PGI) + // result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; + // #elif defined(__SUNPRO_CC) + // result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; + // #else + // result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; + // #endif + + // #ifdef __cplusplus + // result["compiler"]["c++"] = std::to_string(__cplusplus); + // #else + // result["compiler"]["c++"] = "unknown"; + // #endif + // return result; + // } + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ + +#if defined(_az_JSON_HAS_CPP_14) + // Use transparent comparator if possible, combined with perfect forwarding + // on find() and count() calls prevents unnecessary string construction. + using object_comparator_t = std::less<>; +#else + using object_comparator_t = std::less; +#endif + + /*! + @brief a type for an object + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: + > An object is an unordered collection of zero or more name/value pairs, + > where a name is a string and a value is a string, number, boolean, null, + > object, or array. + + To store objects in C++, a type is defined by the template parameters + described below. + + @tparam ObjectType the container to store objects (e.g., `std::map` or + `std::unordered_map`) + @tparam StringType the type of the keys or names (e.g., `std::string`). + The comparison function `std::less` is used to order elements + inside the container. + @tparam AllocatorType the allocator to use for objects (e.g., + `std::allocator`) + + #### Default type + + With the default values for @a ObjectType (`std::map`), @a StringType + (`std::string`), and @a AllocatorType (`std::allocator`), the default + value for @a object_t is: + + @code {.cpp} + std::map< + std::string, // key_type + basic_json, // value_type + std::less, // key_compare + std::allocator> // allocator_type + > + @endcode + + #### Behavior + + The choice of @a object_t influences the behavior of the JSON class. With + the default type, objects have the following behavior: + + - When all names are unique, objects will be interoperable in the sense + that all software implementations receiving that object will agree on + the name-value mappings. + - When the names within an object are not unique, it is unspecified which + one of the values for a given key will be chosen. For instance, + `{"key": 2, "key": 1}` could be equal to either `{"key": 1}` or + `{"key": 2}`. + - Internally, name/value pairs are stored in lexicographical order of the + names. Objects will also be serialized (see @ref dump) in this order. + For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored + and serialized as `{"a": 2, "b": 1}`. + - When comparing objects, the order of the name/value pairs is irrelevant. + This makes objects interoperable in the sense that they will not be + affected by these differences. For instance, `{"b": 1, "a": 2}` and + `{"a": 2, "b": 1}` will be treated as equal. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the object's limit of nesting is not explicitly constrained. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON object. + + #### Storage + + Objects are stored as pointers in a @ref basic_json type. That is, for any + access to object values, a pointer of type `object_t*` must be + dereferenced. + + @sa @ref array_t -- type for an array value + + @since version 1.0.0 + + @note The order name/value pairs are added to the object is *not* + preserved by the library. Therefore, iterating an object may return + name/value pairs in a different order than they were originally stored. In + fact, keys will be traversed in alphabetical order as `std::map` with + `std::less` is used by default. Please note this behavior conforms to [RFC + 7159](http://rfc7159.net/rfc7159), because any order implements the + specified "unordered" nature of JSON objects. + */ + using object_t = ObjectType< + StringType, + basic_json, + object_comparator_t, + AllocatorType>>; + + /*! + @brief a type for an array + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: + > An array is an ordered sequence of zero or more values. + + To store objects in C++, a type is defined by the template parameters + explained below. + + @tparam ArrayType container type to store arrays (e.g., `std::vector` or + `std::list`) + @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) + + #### Default type + + With the default values for @a ArrayType (`std::vector`) and @a + AllocatorType (`std::allocator`), the default value for @a array_t is: + + @code {.cpp} + std::vector< + basic_json, // value_type + std::allocator // allocator_type + > + @endcode + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the array's limit of nesting is not explicitly constrained. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON array. + + #### Storage + + Arrays are stored as pointers in a @ref basic_json type. That is, for any + access to array values, a pointer of type `array_t*` must be dereferenced. + + @sa @ref object_t -- type for an object value + + @since version 1.0.0 + */ + using array_t = ArrayType>; + + /*! + @brief a type for a string + + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: + > A string is a sequence of zero or more Unicode characters. + + To store objects in C++, a type is defined by the template parameter + described below. Unicode values are split by the JSON class into + byte-sized characters during deserialization. + + @tparam StringType the container to store strings (e.g., `std::string`). + Note this container is used for keys/names in objects, see @ref object_t. + + #### Default type + + With the default values for @a StringType (`std::string`), the default + value for @a string_t is: + + @code {.cpp} + std::string + @endcode + + #### Encoding + + Strings are stored in UTF-8 encoding. Therefore, functions like + `std::string::size()` or `std::string::length()` return the number of + bytes in the string rather than the number of characters or glyphs. + + #### String comparison + + [RFC 7159](http://rfc7159.net/rfc7159) states: + > Software implementations are typically required to test names of object + > members for equality. Implementations that transform the textual + > representation into sequences of Unicode code units and then perform the + > comparison numerically, code unit by code unit, are interoperable in the + > sense that implementations will agree in all cases on equality or + > inequality of two strings. For example, implementations that compare + > strings with escaped characters unconverted may incorrectly find that + > `"a\\b"` and `"a\u005Cb"` are not equal. + + This implementation is interoperable as it does compare strings code unit + by code unit. + + #### Storage + + String values are stored as pointers in a @ref basic_json type. That is, + for any access to string values, a pointer of type `string_t*` must be + dereferenced. + + @since version 1.0.0 + */ + using string_t = StringType; + + /*! + @brief a type for a boolean + + [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a + type which differentiates the two literals `true` and `false`. + + To store objects in C++, a type is defined by the template parameter @a + BooleanType which chooses the type to use. + + #### Default type + + With the default values for @a BooleanType (`bool`), the default value for + @a boolean_t is: + + @code {.cpp} + bool + @endcode + + #### Storage + + Boolean values are stored directly inside a @ref basic_json type. + + @since version 1.0.0 + */ + using boolean_t = BooleanType; + + /*! + @brief a type for a number (integer) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store integer numbers in C++, a type is defined by the template + parameter @a NumberIntegerType which chooses the type to use. + + #### Default type + + With the default values for @a NumberIntegerType (`int64_t`), the default + value for @a number_integer_t is: + + @code {.cpp} + int64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `9223372036854775807` (INT64_MAX) and the minimal integer number + that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers + that are out of range will yield over/underflow when used in a + constructor. During deserialization, too large or small integer numbers + will be automatically be stored as @ref number_unsigned_t or @ref + number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange of the exactly supported range [INT64_MIN, + INT64_MAX], this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_integer_t = NumberIntegerType; + + /*! + @brief a type for a number (unsigned) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store unsigned integer numbers in C++, a type is defined by the + template parameter @a NumberUnsignedType which chooses the type to use. + + #### Default type + + With the default values for @a NumberUnsignedType (`uint64_t`), the + default value for @a number_unsigned_t is: + + @code {.cpp} + uint64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. + + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange (when considered in conjunction with the + number_integer_t type) of the exactly supported range [0, UINT64_MAX], + this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa @ref number_float_t -- type for number values (floating-point) + @sa @ref number_integer_t -- type for number values (integer) + + @since version 2.0.0 + */ + using number_unsigned_t = NumberUnsignedType; + + /*! + @brief a type for a number (floating-point) + + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store floating-point numbers in C++, a type is defined by the template + parameter @a NumberFloatType which chooses the type to use. + + #### Default type + + With the default values for @a NumberFloatType (`double`), the default + value for @a number_float_t is: + + @code {.cpp} + double + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in floating-point literals will be ignored. Internally, + the value will be stored as decimal number. For instance, the C++ + floating-point literal `01.2` will be serialized to `1.2`. During + deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 7159](http://rfc7159.net/rfc7159) states: + > This specification allows implementations to set limits on the range and + > precision of numbers accepted. Since software that implements IEEE + > 754-2008 binary64 (double precision) numbers is generally available and + > widely used, good interoperability can be achieved by implementations + > that expect no more precision or range than these provide, in the sense + > that implementations will approximate JSON numbers within the expected + > precision. + + This implementation does exactly follow this approach, as it uses double + precision floating-point numbers. Note values smaller than + `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` + will be stored as NaN internally and be serialized to `null`. + + #### Storage + + Floating-point number values are stored directly inside a @ref basic_json + type. + + @sa @ref number_integer_t -- type for number values (integer) + + @sa @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_float_t = NumberFloatType; + + /*! + @brief a type for a packed binary type + + 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 + 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 `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). + + [MessagePack's documentation on the bin type + family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family) + describes this type as: + > Bin format family stores an byte array in 2, 3, or 5 bytes of extra bytes + > in addition to the size of the byte array. + + [BSON's specifications](http://bsonspec.org/spec.html) describe several + binary types; however, this type is intended to represent the generic binary + type which has the description: + > Generic binary subtype - This is the most commonly used binary subtype and + > should be the 'default' for drivers and tools. + + 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 + decomposable into bytes. + + The default representation of this binary format is a + `std::vector`, which is a very common way to represent a byte + array in modern C++. + + #### Default type + + The default values for @a BinaryType is `std::vector` + + #### Storage + + 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. + + #### Notes on subtypes + + - CBOR + - Binary values are represented as byte strings. No subtypes are + supported and will be ignored when CBOR is written. + - MessagePack + - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, + or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) + is used. For other sizes, the ext family (ext8, ext16, ext32) is used. + The subtype is then added as singed 8-bit integer. + - If no subtype is given, the bin family (bin8, bin16, bin32) is used. + - BSON + - If a subtype is given, it is used and added as unsigned 8-bit integer. + - If no subtype is given, the generic binary subtype 0x00 is used. + + @sa @ref binary -- create a binary array + + @since version 3.8.0 + */ + using binary_t = Azure::Core::Internal::Json::byte_container_with_subtype; + /// @} + + private: + /// helper for exception-safe object creation + template + _az_JSON_HEDLEY_RETURNS_NON_NULL static T* create(Args&&... args) + { + AllocatorType alloc; + using AllocatorTraits = std::allocator_traits>; + + auto deleter = [&](T* object) { AllocatorTraits::deallocate(alloc, object, 1); }; + std::unique_ptr object(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, object.get(), std::forward(args)...); + assert(object != nullptr); + return object.release(); + } + + //////////////////////// + // JSON value storage // + //////////////////////// + + /*! + @brief a JSON value + + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + 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 binary_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. + + @since version 1.0.0 + */ + union json_value + { + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// binary (stored with pointer to save storage) + binary_t* binary; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) { - case value_t::object: { - object = create(); - break; + switch (t) + { + case value_t::object: { + object = create(); + break; + } + + case value_t::array: { + array = create(); + break; + } + + case value_t::string: { + string = create(""); + break; + } + + case value_t::binary: { + binary = create(); + break; + } + + case value_t::boolean: { + boolean = boolean_t(false); + break; + } + + case value_t::number_integer: { + number_integer = number_integer_t(0); + break; + } + + case value_t::number_unsigned: { + number_unsigned = number_unsigned_t(0); + break; + } + + case value_t::number_float: { + number_float = number_float_t(0.0); + break; + } + + case value_t::null: { + object = nullptr; // silence warning, see #821 + break; + } + + default: { + object = nullptr; // silence warning, see #821 + if (_az_JSON_HEDLEY_UNLIKELY(t == value_t::null)) + { + _az_JSON_THROW(other_error::create( + 500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.8.0")); // LCOV_EXCL_LINE + } + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) { string = create(value); } + + /// constructor for rvalue strings + json_value(string_t&& value) { string = create(std::move(value)); } + + /// constructor for objects + json_value(const object_t& value) { object = create(value); } + + /// constructor for rvalue objects + json_value(object_t&& value) { object = create(std::move(value)); } + + /// constructor for arrays + json_value(const array_t& value) { array = create(value); } + + /// constructor for rvalue arrays + json_value(array_t&& value) { array = create(std::move(value)); } + + /// constructor for binary arrays + json_value(const typename binary_t::container_type& 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)); } + + void destroy(value_t t) noexcept + { + // flatten the current json_value to a heap-allocated stack + std::vector stack; + + // move the top-level items to stack + if (t == value_t::array) + { + stack.reserve(array->size()); + std::move(array->begin(), array->end(), std::back_inserter(stack)); + } + else if (t == value_t::object) + { + stack.reserve(object->size()); + for (auto&& it : *object) + { + stack.push_back(std::move(it.second)); + } } - case value_t::array: { - array = create(); - break; + while (not stack.empty()) + { + // move the last item to local variable to be processed + basic_json current_item(std::move(stack.back())); + stack.pop_back(); + + // if current_item is array/object, move + // its children to the stack to be processed later + if (current_item.is_array()) + { + std::move( + current_item.m_value.array->begin(), + current_item.m_value.array->end(), + std::back_inserter(stack)); + + current_item.m_value.array->clear(); + } + else if (current_item.is_object()) + { + for (auto&& it : *current_item.m_value.object) + { + stack.push_back(std::move(it.second)); + } + + current_item.m_value.object->clear(); + } + + // it's now safe that current_item get destructed + // since it doesn't have any children } + switch (t) + { + case value_t::object: { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + break; + } + + case value_t::array: { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + break; + } + + case value_t::string: { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, string); + std::allocator_traits::deallocate(alloc, string, 1); + break; + } + + case value_t::binary: { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, binary); + std::allocator_traits::deallocate(alloc, binary, 1); + break; + } + + default: { + break; + } + } + } + }; + + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + */ + void assert_invariant() const noexcept + { + 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: + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /*! + @brief parser event types + + The parser callback distinguishes the following events: + - `object_start`: the parser read `{` and started to process a JSON object + - `key`: the parser read a key of a value in an object + - `object_end`: the parser read `}` and finished processing a JSON object + - `array_start`: the parser read `[` and started to process a JSON array + - `array_end`: the parser read `]` and finished processing a JSON array + - `value`: the parser finished reading a JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + @sa @ref parser_callback_t for more information and examples + */ + using parse_event_t = detail::parse_event_t; + + /*! + @brief per-element parser callback type + + With a parser callback function, the result of parsing a JSON text can be + influenced. When passed to @ref parse, it is called on certain events + (passed as @ref parse_event_t via parameter @a event) with a set recursion + depth @a depth and context JSON value @a parsed. The return value of the + callback function is a boolean indicating whether the element that emitted + the callback shall be kept or not. + + We distinguish six scenarios (determined by the event type) in which the + callback function can be called. The following table describes the values + of the parameters @a depth, @a event, and @a parsed. + + parameter @a event | description | parameter @a depth | parameter @a parsed + ------------------ | ----------- | ------------------ | ------------------- + parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth + of the parent of the JSON object | a JSON value with type discarded parse_event_t::key | the + parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON + string containing the key parse_event_t::object_end | the parser read `}` and finished + processing a JSON object | depth of the parent of the JSON object | the parsed JSON object + parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of + the parent of the JSON array | a JSON value with type discarded parse_event_t::array_end | the + parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | + the parsed JSON array parse_event_t::value | the parser finished reading a JSON value | depth of + the value | the parsed JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + Discarding a value (i.e., returning `false`) has different effects + depending on the context in which function was called: + + - Discarded values in structured types are skipped. That is, the parser + will behave as if the discarded value was never read. + - In case a value outside a structured type is skipped, it is replaced + with `null`. This case happens if the top-level element is skipped. + + @param[in] depth the depth of the recursion during parsing + + @param[in] event an event of type parse_event_t indicating the context in + the callback function has been called + + @param[in,out] parsed the current intermediate parse result; note that + writing to this value has no effect for parse_event_t::key events + + @return Whether the JSON value which called the function during parsing + should be kept (`true`) or not (`false`). In the latter case, it is either + skipped completely or replaced by an empty discarded object. + + @sa @ref parse for examples + + @since version 1.0.0 + */ + using parser_callback_t = detail::parser_callback_t; + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ + + /*! + @brief create an empty value with a given type + + Create an empty JSON value with a given type. The value will be default + initialized with an empty value which depends on the type: + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` + binary | empty array + + @param[in] v the type of the value to create + + @complexity Constant. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows the constructor for different @ref + value_t values,basic_json__value_t} + + @sa @ref clear() -- restores the postcondition of this constructor + + @since version 1.0.0 + */ + basic_json(const value_t v) : m_type(v), m_value(v) { assert_invariant(); } + + /*! + @brief create a null object + + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). + The passed null pointer itself is not read -- it is only used to choose + the right constructor. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} + + @since version 1.0.0 + */ + basic_json(std::nullptr_t = nullptr) noexcept : basic_json(value_t::null) + { + assert_invariant(); + } + + /*! + @brief create a JSON value + + This is a "catch all" constructor for all compatible JSON types; that is, + types for which a `to_json()` method exists. The constructor forwards the + parameter @a val to that method (to `json_serializer::to_json` method + with `U = uncvref_t`, to be exact). + + Template type @a CompatibleType includes, but is not limited to, the + following types: + - **arrays**: @ref array_t and all kinds of compatible containers such as + `std::vector`, `std::deque`, `std::list`, `std::forward_list`, + `std::array`, `std::valarray`, `std::set`, `std::unordered_set`, + `std::multiset`, and `std::unordered_multiset` with a `value_type` from + which a @ref basic_json value can be constructed. + - **objects**: @ref object_t and all kinds of compatible associative + containers such as `std::map`, `std::unordered_map`, `std::multimap`, + and `std::unordered_multimap` with a `key_type` compatible to + @ref string_t and a `value_type` from which a @ref basic_json value can + be constructed. + - **strings**: @ref string_t, string literals, and all compatible string + containers can be used. + - **numbers**: @ref number_integer_t, @ref number_unsigned_t, + @ref number_float_t, and all convertible number types such as `int`, + `size_t`, `int64_t`, `float` or `double` can be used. + - **boolean**: @ref boolean_t / `bool` can be used. + - **binary**: @ref binary_t / `std::vector` may be used, + unfortunately because string literals cannot be distinguished from binary + character arrays by the C++ type system, all types compatible with `const + char*` will be directed to the string constructor instead. This is both + for backwards compatibility, and due to the fact that a binary type is not + a standard JSON type. + + See the examples below. + + @tparam CompatibleType a type such that: + - @a CompatibleType is not derived from `std::istream`, + - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move + constructors), + - @a CompatibleType is not a different @ref basic_json type (i.e. with different template + arguments) + - @a CompatibleType is not a @ref basic_json nested type (e.g., + @ref json_pointer, @ref iterator, etc ...) + - @ref @ref json_serializer has a + `to_json(basic_json_t&, CompatibleType&&)` method + + @tparam U = `uncvref_t` + + @param[in] val the value to be forwarded to the respective constructor + + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. + + @exceptionsafety Depends on the called constructor. For types directly + supported by the library (i.e., all types for which no `to_json()` function + was provided), strong guarantee holds: if an exception is thrown, there are + no changes to any JSON value. + + @liveexample{The following code shows the constructor with several + compatible types.,basic_json__CompatibleType} + + @since version 2.1.0 + */ + template < + typename CompatibleType, + typename U = detail::uncvref_t, + detail::enable_if_t< + not detail::is_basic_json::value + and detail::is_compatible_type::value, + int> = 0> + basic_json(CompatibleType&& val) noexcept(noexcept(JSONSerializer::to_json( + std::declval(), + std::forward(val)))) + { + JSONSerializer::to_json(*this, std::forward(val)); + assert_invariant(); + } + + /*! + @brief create a JSON value from an existing one + + This is a constructor for existing @ref basic_json types. + It does not hijack copy/move constructors, since the parameter has different + template arguments than the current ones. + + The constructor tries to convert the internal @ref m_value of the parameter. + + @tparam BasicJsonType a type such that: + - @a BasicJsonType is a @ref basic_json type. + - @a BasicJsonType has different template arguments than @ref basic_json_t. + + @param[in] val the @ref basic_json value to be converted. + + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. + + @exceptionsafety Depends on the called constructor. For types directly + supported by the library (i.e., all types for which no `to_json()` function + was provided), strong guarantee holds: if an exception is thrown, there are + no changes to any JSON value. + + @since version 3.2.0 + */ + template < + typename BasicJsonType, + detail::enable_if_t< + detail::is_basic_json::value + and not std::is_same::value, + int> = 0> + basic_json(const BasicJsonType& val) + { + using other_boolean_t = typename BasicJsonType::boolean_t; + using other_number_float_t = typename BasicJsonType::number_float_t; + using other_number_integer_t = typename BasicJsonType::number_integer_t; + using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; + 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::binary_t; + + switch (val.type()) + { + case value_t::boolean: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_float: + JSONSerializer::to_json( + *this, val.template get()); + break; + case value_t::number_integer: + JSONSerializer::to_json( + *this, val.template get()); + break; + case value_t::number_unsigned: + JSONSerializer::to_json( + *this, val.template get()); + break; + case value_t::string: + JSONSerializer::to_json( + *this, val.template get_ref()); + break; + case value_t::object: + JSONSerializer::to_json( + *this, val.template get_ref()); + break; + case value_t::array: + JSONSerializer::to_json( + *this, val.template get_ref()); + break; + case value_t::binary: + JSONSerializer::to_json( + *this, val.template get_ref()); + break; + case value_t::null: + *this = nullptr; + break; + case value_t::discarded: + m_type = value_t::discarded; + break; + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + assert_invariant(); + } + + /*! + @brief create a container (array or object) from an initializer list + + Creates a JSON value of type array or object from the passed initializer + list @a init. In case @a type_deduction is `true` (default), the type of + the JSON value to be created is deducted from the initializer list @a init + according to the following rules: + + 1. If the list is empty, an empty JSON object value `{}` is created. + 2. If the list consists of pairs whose first element is a string, a JSON + object value is created where the first elements of the pairs are + treated as keys and the second elements are as values. + 3. In all other cases, an array is created. + + The rules aim to create the best fit between a C++ initializer list and + JSON values. The rationale is as follows: + + 1. The empty initializer list is written as `{}` which is exactly an empty + JSON object. + 2. C++ has no way of describing mapped types other than to list a list of + pairs. As JSON requires that keys must be of type string, rule 2 is the + weakest constraint one can pose on initializer lists to interpret them + as an object. + 3. In all other cases, the initializer list could not be interpreted as + JSON object type, so interpreting it as JSON array type is safe. + + With the rules described above, the following JSON values cannot be + expressed by an initializer list: + + - the empty array (`[]`): use @ref array(initializer_list_t) + with an empty initializer list in this case + - arrays whose elements satisfy rule 2: use @ref + array(initializer_list_t) with the same initializer list + in this case + + @note When used without parentheses around an empty initializer list, @ref + basic_json() is called instead of this function, yielding the JSON null + value. + + @param[in] init initializer list with JSON values + + @param[in] type_deduction internal parameter; when set to `true`, the type + of the JSON value is deducted from the initializer list @a init; when set + to `false`, the type provided via @a manual_type is forced. This mode is + used by the functions @ref array(initializer_list_t) and + @ref object(initializer_list_t). + + @param[in] manual_type internal parameter; when @a type_deduction is set + to `false`, the created JSON value will use the provided type (only @ref + value_t::array and @ref value_t::object are valid); when @a type_deduction + is set to `true`, this parameter has no effect + + @throw type_error.301 if @a type_deduction is `false`, @a manual_type is + `value_t::object`, but @a init contains an element which is not a pair + whose first element is a string. In this case, the constructor could not + create an object. If @a type_deduction would have be `true`, an array + would have been created. See @ref object(initializer_list_t) + for an example. + + @complexity Linear in the size of the initializer list @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The example below shows how JSON values are created from + initializer lists.,basic_json__list_init_t} + + @sa @ref array(initializer_list_t) -- create a JSON array + value from an initializer list + @sa @ref object(initializer_list_t) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + basic_json( + initializer_list_t init, + bool type_deduction = true, + value_t manual_type = value_t::array) + { + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of( + init.begin(), init.end(), [](const detail::json_ref& element_ref) { + return element_ref->is_array() and element_ref->size() == 2 + and (*element_ref)[0].is_string(); + }); + + // adjust type if type deduction is not wanted + if (not type_deduction) + { + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if object is wanted but impossible, throw an exception + if (_az_JSON_HEDLEY_UNLIKELY(manual_type == value_t::object and not is_an_object)) + { + _az_JSON_THROW(type_error::create(301, "cannot create object from initializer list")); + } + } + + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + m_value = value_t::object; + + std::for_each( + init.begin(), init.end(), [this](const detail::json_ref& element_ref) { + auto element = element_ref.moved_or_copied(); + m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); + }); + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + m_value.array = create(init.begin(), init.end()); + } + + assert_invariant(); + } + + /*! + @brief explicitly create a binary array (without subtype) + + 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 + 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 container containing bytes to use as binary type + + @return JSON binary array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @since version 3.8.0 + */ + _az_JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = init; + return res; + } + + /*! + @brief explicitly create a binary array (with subtype) + + 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 + 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 container containing bytes to use as binary type + @param[in] subtype subtype to use in MessagePack and BSON + + @return JSON binary array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @since version 3.8.0 + */ + _az_JSON_HEDLEY_WARN_UNUSED_RESULT + 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&) + _az_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; + res.m_value = std::move(init); + return res; + } + + /// @copydoc binary(const typename binary_t::container_type&, std::uint8_t) + _az_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 + + Creates a JSON array value from a given initializer list. That is, given a + list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the + initializer list is empty, the empty array `[]` is created. + + @note This function is only needed to express two edge cases that cannot + be realized with the initializer list constructor (@ref + basic_json(initializer_list_t, bool, value_t)). These cases + are: + 1. creating an array whose elements are all pairs whose first element is a + string -- in this case, the initializer list constructor would create an + object, taking the first elements as keys + 2. creating an empty array -- passing the empty initializer list to the + initializer list constructor yields an empty object + + @param[in] init initializer list with JSON values to create an array from + (optional) + + @return JSON array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows an example for the `array` + function.,array} + + @sa @ref basic_json(initializer_list_t, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref object(initializer_list_t) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + _az_JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json array(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::array); + } + + /*! + @brief explicitly create an object from an initializer list + + Creates a JSON object value from a given initializer list. The initializer + lists elements must be pairs, and their first elements must be strings. If + the initializer list is empty, the empty object `{}` is created. + + @note This function is only added for symmetry reasons. In contrast to the + related function @ref array(initializer_list_t), there are + no cases which can only be expressed by this function. That is, any + initializer list @a init can also be passed to the initializer list + constructor @ref basic_json(initializer_list_t, bool, value_t). + + @param[in] init initializer list to create an object from (optional) + + @return JSON object value + + @throw type_error.301 if @a init is not a list of pairs whose first + elements are strings. In this case, no object can be created. When such a + value is passed to @ref basic_json(initializer_list_t, bool, value_t), + an array would have been created from the passed initializer list @a init. + See example below. + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows an example for the `object` + function.,object} + + @sa @ref basic_json(initializer_list_t, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref array(initializer_list_t) -- create a JSON array + value from an initializer list + + @since version 1.0.0 + */ + _az_JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json object(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::object); + } + + /*! + @brief construct an array with count copies of given value + + Constructs a JSON array value by creating @a cnt copies of a passed value. + In case @a cnt is `0`, an empty array is created. + + @param[in] cnt the number of JSON copies of @a val to create + @param[in] val the JSON value to copy + + @post `std::distance(begin(),end()) == cnt` holds. + + @complexity Linear in @a cnt. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows examples for the @ref + basic_json(size_type\, const basic_json&) + constructor.,basic_json__size_type_basic_json} + + @since version 1.0.0 + */ + basic_json(size_type cnt, const basic_json& val) : m_type(value_t::array) + { + m_value.array = create(cnt, val); + assert_invariant(); + } + + /*! + @brief construct a JSON container given an iterator range + + Constructs the JSON value with the contents of the range `[first, last)`. + The semantics depends on the different types a JSON value can have: + - In case of a null type, invalid_iterator.206 is thrown. + - In case of other primitive types (number, boolean, or string), @a first + must be `begin()` and @a last must be `end()`. In this case, the value is + copied. Otherwise, invalid_iterator.204 is thrown. + - In case of structured types (array, object), the constructor behaves as + similar versions for `std::vector` or `std::map`; that is, a JSON array + or object is constructed from the values in the range. + + @tparam InputIT an input iterator type (@ref iterator or @ref + const_iterator) + + @param[in] first begin of the range to copy from (included) + @param[in] last end of the range to copy from (excluded) + + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion (see warning).** If + assertions are switched off, a violation of this precondition yields + undefined behavior. + + @pre Range `[first, last)` is valid. Usually, this precondition cannot be + checked efficiently. Only certain edge cases are detected; see the + description of the exceptions below. A violation of this precondition + yields undefined behavior. + + @warning A precondition is enforced with a runtime assertion that will + result in calling `std::abort` if this precondition is not met. + Assertions can be disabled by defining `NDEBUG` at compile time. + See https://en.cppreference.com/w/cpp/error/assert for more + information. + + @throw invalid_iterator.201 if iterators @a first and @a last are not + compatible (i.e., do not belong to the same JSON value). In this case, + the range `[first, last)` is undefined. + @throw invalid_iterator.204 if iterators @a first and @a last belong to a + primitive type (number, boolean, or string), but @a first does not point + to the first element any more. In this case, the range `[first, last)` is + undefined. See example code below. + @throw invalid_iterator.206 if iterators @a first and @a last belong to a + null value. In this case, the range `[first, last)` is undefined. + + @complexity Linear in distance between @a first and @a last. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The example below shows several ways to create JSON values by + specifying a subrange with iterators.,basic_json__InputIt_InputIt} + + @since version 1.0.0 + */ + template < + class InputIT, + typename std::enable_if< + std::is_same::value + or std::is_same::value, + int>::type + = 0> + basic_json(InputIT first, InputIT last) + { + assert(first.m_object != nullptr); + assert(last.m_object != nullptr); + + // make sure iterator fits the current value + if (_az_JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + _az_JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); + } + + // copy type from first iterator + m_type = first.m_object->m_type; + + // check if iterator range is complete for primitive values + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: case value_t::string: { - string = create(""); + if (_az_JSON_HEDLEY_UNLIKELY( + not first.m_it.primitive_iterator.is_begin() + or not last.m_it.primitive_iterator.is_end())) + { + _az_JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } break; } - case value_t::boolean: { - boolean = boolean_t(false); + default: break; - } + } + switch (m_type) + { case value_t::number_integer: { - number_integer = number_integer_t(0); + m_value.number_integer = first.m_object->m_value.number_integer; break; } case value_t::number_unsigned: { - number_unsigned = number_unsigned_t(0); + m_value.number_unsigned = first.m_object->m_value.number_unsigned; break; } case value_t::number_float: { - number_float = number_float_t(0.0); + m_value.number_float = first.m_object->m_value.number_float; break; } - case value_t::null: { - object = nullptr; // silence warning, see #821 - break; - } - - default: { - object = nullptr; // silence warning, see #821 - if (JSON_UNLIKELY(t == value_t::null)) - { - JSON_THROW(other_error::create( - 500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.6.0")); // LCOV_EXCL_LINE - } - break; - } - } - } - - /// constructor for strings - json_value(const string_t& value) { string = create(value); } - - /// constructor for rvalue strings - json_value(string_t&& value) { string = create(std::move(value)); } - - /// constructor for objects - json_value(const object_t& value) { object = create(value); } - - /// constructor for rvalue objects - json_value(object_t&& value) { object = create(std::move(value)); } - - /// constructor for arrays - json_value(const array_t& value) { array = create(value); } - - /// constructor for rvalue arrays - json_value(array_t&& value) { array = create(std::move(value)); } - - void destroy(value_t t) noexcept - { - switch (t) - { - case value_t::object: { - AllocatorType alloc; - std::allocator_traits::destroy(alloc, object); - std::allocator_traits::deallocate(alloc, object, 1); - break; - } - - case value_t::array: { - AllocatorType alloc; - std::allocator_traits::destroy(alloc, array); - std::allocator_traits::deallocate(alloc, array, 1); + case value_t::boolean: { + m_value.boolean = first.m_object->m_value.boolean; break; } case value_t::string: { - AllocatorType alloc; - std::allocator_traits::destroy(alloc, string); - std::allocator_traits::deallocate(alloc, string, 1); + m_value = *first.m_object->m_value.string; break; } - default: { - break; - } - } - } - }; - - /*! - @brief checks the class invariants - - This function asserts the class invariants. It needs to be called at the - end of every constructor to make sure that created objects respect the - invariant. Furthermore, it has to be called each time the type of a JSON - value is changed, because the invariant expresses a relationship between - @a m_type and @a m_value. - */ - void assert_invariant() const noexcept - { - 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); - } - -public: - ////////////////////////// - // JSON parser callback // - ////////////////////////// - - /*! - @brief parser event types - - The parser callback distinguishes the following events: - - `object_start`: the parser read `{` and started to process a JSON object - - `key`: the parser read a key of a value in an object - - `object_end`: the parser read `}` and finished processing a JSON object - - `array_start`: the parser read `[` and started to process a JSON array - - `array_end`: the parser read `]` and finished processing a JSON array - - `value`: the parser finished reading a JSON value - - @image html callback_events.png "Example when certain parse events are triggered" - - @sa @ref parser_callback_t for more information and examples - */ - using parse_event_t = typename parser::parse_event_t; - - /*! - @brief per-element parser callback type - - With a parser callback function, the result of parsing a JSON text can be - influenced. When passed to @ref parse, it is called on certain events - (passed as @ref parse_event_t via parameter @a event) with a set recursion - depth @a depth and context JSON value @a parsed. The return value of the - callback function is a boolean indicating whether the element that emitted - the callback shall be kept or not. - - We distinguish six scenarios (determined by the event type) in which the - callback function can be called. The following table describes the values - of the parameters @a depth, @a event, and @a parsed. - - parameter @a event | description | parameter @a depth | parameter @a parsed - ------------------ | ----------- | ------------------ | ------------------- - parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of - the parent of the JSON object | a JSON value with type discarded parse_event_t::key | the parser - read a key of a value in an object | depth of the currently parsed JSON object | a JSON string - containing the key parse_event_t::object_end | the parser read `}` and finished processing a JSON - object | depth of the parent of the JSON object | the parsed JSON object - parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of - the parent of the JSON array | a JSON value with type discarded parse_event_t::array_end | the - parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the - parsed JSON array parse_event_t::value | the parser finished reading a JSON value | depth of the - value | the parsed JSON value - - @image html callback_events.png "Example when certain parse events are triggered" - - Discarding a value (i.e., returning `false`) has different effects - depending on the context in which function was called: - - - Discarded values in structured types are skipped. That is, the parser - will behave as if the discarded value was never read. - - In case a value outside a structured type is skipped, it is replaced - with `null`. This case happens if the top-level element is skipped. - - @param[in] depth the depth of the recursion during parsing - - @param[in] event an event of type parse_event_t indicating the context in - the callback function has been called - - @param[in,out] parsed the current intermediate parse result; note that - writing to this value has no effect for parse_event_t::key events - - @return Whether the JSON value which called the function during parsing - should be kept (`true`) or not (`false`). In the latter case, it is either - skipped completely or replaced by an empty discarded object. - - @sa @ref parse for examples - - @since version 1.0.0 - */ - using parser_callback_t = typename parser::parser_callback_t; - - ////////////////// - // constructors // - ////////////////// - - /// @name constructors and destructors - /// Constructors of class @ref basic_json, copy/move constructor, copy - /// assignment, static functions creating objects, and the destructor. - /// @{ - - /*! - @brief create an empty value with a given type - - Create an empty JSON value with a given type. The value will be default - initialized with an empty value which depends on the type: - - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` - - @param[in] v the type of the value to create - - @complexity Constant. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @liveexample{The following code shows the constructor for different @ref - value_t values,basic_json__value_t} - - @sa @ref clear() -- restores the postcondition of this constructor - - @since version 1.0.0 - */ - basic_json(const value_t v) : m_type(v), m_value(v) { assert_invariant(); } - - /*! - @brief create a null object - - Create a `null` JSON value. It either takes a null pointer as parameter - (explicitly creating `null`) or no parameter (implicitly creating `null`). - The passed null pointer itself is not read -- it is only used to choose - the right constructor. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. - - @liveexample{The following code shows the constructor with and without a - null pointer parameter.,basic_json__nullptr_t} - - @since version 1.0.0 - */ - basic_json(std::nullptr_t = nullptr) noexcept : basic_json(value_t::null) { assert_invariant(); } - - /*! - @brief create a JSON value - - This is a "catch all" constructor for all compatible JSON types; that is, - types for which a `to_json()` method exists. The constructor forwards the - parameter @a val to that method (to `json_serializer::to_json` method - with `U = uncvref_t`, to be exact). - - Template type @a CompatibleType includes, but is not limited to, the - following types: - - **arrays**: @ref array_t and all kinds of compatible containers such as - `std::vector`, `std::deque`, `std::list`, `std::forward_list`, - `std::array`, `std::valarray`, `std::set`, `std::unordered_set`, - `std::multiset`, and `std::unordered_multiset` with a `value_type` from - which a @ref basic_json value can be constructed. - - **objects**: @ref object_t and all kinds of compatible associative - containers such as `std::map`, `std::unordered_map`, `std::multimap`, - and `std::unordered_multimap` with a `key_type` compatible to - @ref string_t and a `value_type` from which a @ref basic_json value can - be constructed. - - **strings**: @ref string_t, string literals, and all compatible string - containers can be used. - - **numbers**: @ref number_integer_t, @ref number_unsigned_t, - @ref number_float_t, and all convertible number types such as `int`, - `size_t`, `int64_t`, `float` or `double` can be used. - - **boolean**: @ref boolean_t / `bool` can be used. - - See the examples below. - - @tparam CompatibleType a type such that: - - @a CompatibleType is not derived from `std::istream`, - - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move - constructors), - - @a CompatibleType is not a different @ref basic_json type (i.e. with different template - arguments) - - @a CompatibleType is not a @ref basic_json nested type (e.g., - @ref json_pointer, @ref iterator, etc ...) - - @ref @ref json_serializer has a - `to_json(basic_json_t&, CompatibleType&&)` method - - @tparam U = `uncvref_t` - - @param[in] val the value to be forwarded to the respective constructor - - @complexity Usually linear in the size of the passed @a val, also - depending on the implementation of the called `to_json()` - method. - - @exceptionsafety Depends on the called constructor. For types directly - supported by the library (i.e., all types for which no `to_json()` function - was provided), strong guarantee holds: if an exception is thrown, there are - no changes to any JSON value. - - @liveexample{The following code shows the constructor with several - compatible types.,basic_json__CompatibleType} - - @since version 2.1.0 - */ - template < - typename CompatibleType, - typename U = detail::uncvref_t, - detail::enable_if_t< - not detail::is_basic_json::value - and detail::is_compatible_type::value, - int> = 0> - basic_json(CompatibleType&& val) noexcept(noexcept( - JSONSerializer::to_json(std::declval(), std::forward(val)))) - { - JSONSerializer::to_json(*this, std::forward(val)); - assert_invariant(); - } - - /*! - @brief create a JSON value from an existing one - - This is a constructor for existing @ref basic_json types. - It does not hijack copy/move constructors, since the parameter has different - template arguments than the current ones. - - The constructor tries to convert the internal @ref m_value of the parameter. - - @tparam BasicJsonType a type such that: - - @a BasicJsonType is a @ref basic_json type. - - @a BasicJsonType has different template arguments than @ref basic_json_t. - - @param[in] val the @ref basic_json value to be converted. - - @complexity Usually linear in the size of the passed @a val, also - depending on the implementation of the called `to_json()` - method. - - @exceptionsafety Depends on the called constructor. For types directly - supported by the library (i.e., all types for which no `to_json()` function - was provided), strong guarantee holds: if an exception is thrown, there are - no changes to any JSON value. - - @since version 3.2.0 - */ - template < - typename BasicJsonType, - detail::enable_if_t< - detail::is_basic_json::value - and not std::is_same::value, - int> = 0> - basic_json(const BasicJsonType& val) - { - using other_boolean_t = typename BasicJsonType::boolean_t; - using other_number_float_t = typename BasicJsonType::number_float_t; - using other_number_integer_t = typename BasicJsonType::number_integer_t; - using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using other_string_t = typename BasicJsonType::string_t; - using other_object_t = typename BasicJsonType::object_t; - using other_array_t = typename BasicJsonType::array_t; - - switch (val.type()) - { - case value_t::boolean: - JSONSerializer::to_json(*this, val.template get()); - break; - case value_t::number_float: - JSONSerializer::to_json( - *this, val.template get()); - break; - case value_t::number_integer: - JSONSerializer::to_json( - *this, val.template get()); - break; - case value_t::number_unsigned: - JSONSerializer::to_json( - *this, val.template get()); - break; - case value_t::string: - JSONSerializer::to_json( - *this, val.template get_ref()); - break; - case value_t::object: - JSONSerializer::to_json( - *this, val.template get_ref()); - break; - case value_t::array: - JSONSerializer::to_json(*this, val.template get_ref()); - break; - case value_t::null: - *this = nullptr; - break; - case value_t::discarded: - m_type = value_t::discarded; - break; - default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE - } - assert_invariant(); - } - - /*! - @brief create a container (array or object) from an initializer list - - Creates a JSON value of type array or object from the passed initializer - list @a init. In case @a type_deduction is `true` (default), the type of - the JSON value to be created is deducted from the initializer list @a init - according to the following rules: - - 1. If the list is empty, an empty JSON object value `{}` is created. - 2. If the list consists of pairs whose first element is a string, a JSON - object value is created where the first elements of the pairs are - treated as keys and the second elements are as values. - 3. In all other cases, an array is created. - - The rules aim to create the best fit between a C++ initializer list and - JSON values. The rationale is as follows: - - 1. The empty initializer list is written as `{}` which is exactly an empty - JSON object. - 2. C++ has no way of describing mapped types other than to list a list of - pairs. As JSON requires that keys must be of type string, rule 2 is the - weakest constraint one can pose on initializer lists to interpret them - as an object. - 3. In all other cases, the initializer list could not be interpreted as - JSON object type, so interpreting it as JSON array type is safe. - - With the rules described above, the following JSON values cannot be - expressed by an initializer list: - - - the empty array (`[]`): use @ref array(initializer_list_t) - with an empty initializer list in this case - - arrays whose elements satisfy rule 2: use @ref - array(initializer_list_t) with the same initializer list - in this case - - @note When used without parentheses around an empty initializer list, @ref - basic_json() is called instead of this function, yielding the JSON null - value. - - @param[in] init initializer list with JSON values - - @param[in] type_deduction internal parameter; when set to `true`, the type - of the JSON value is deducted from the initializer list @a init; when set - to `false`, the type provided via @a manual_type is forced. This mode is - used by the functions @ref array(initializer_list_t) and - @ref object(initializer_list_t). - - @param[in] manual_type internal parameter; when @a type_deduction is set - to `false`, the created JSON value will use the provided type (only @ref - value_t::array and @ref value_t::object are valid); when @a type_deduction - is set to `true`, this parameter has no effect - - @throw type_error.301 if @a type_deduction is `false`, @a manual_type is - `value_t::object`, but @a init contains an element which is not a pair - whose first element is a string. In this case, the constructor could not - create an object. If @a type_deduction would have be `true`, an array - would have been created. See @ref object(initializer_list_t) - for an example. - - @complexity Linear in the size of the initializer list @a init. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @liveexample{The example below shows how JSON values are created from - initializer lists.,basic_json__list_init_t} - - @sa @ref array(initializer_list_t) -- create a JSON array - value from an initializer list - @sa @ref object(initializer_list_t) -- create a JSON object - value from an initializer list - - @since version 1.0.0 - */ - basic_json( - initializer_list_t init, - bool type_deduction = true, - value_t manual_type = value_t::array) - { - // check if each element is an array with two elements whose first - // element is a string - bool is_an_object = std::all_of( - init.begin(), init.end(), [](const detail::json_ref& element_ref) { - return element_ref->is_array() and element_ref->size() == 2 - and (*element_ref)[0].is_string(); - }); - - // adjust type if type deduction is not wanted - if (not type_deduction) - { - // if array is wanted, do not create an object though possible - if (manual_type == value_t::array) - { - is_an_object = false; - } - - // if object is wanted but impossible, throw an exception - if (JSON_UNLIKELY(manual_type == value_t::object and not is_an_object)) - { - JSON_THROW(type_error::create(301, "cannot create object from initializer list")); - } - } - - if (is_an_object) - { - // the initializer list is a list of pairs -> create object - m_type = value_t::object; - m_value = value_t::object; - - std::for_each( - init.begin(), init.end(), [this](const detail::json_ref& element_ref) { - auto element = element_ref.moved_or_copied(); - m_value.object->emplace( - std::move(*((*element.m_value.array)[0].m_value.string)), - std::move((*element.m_value.array)[1])); - }); - } - else - { - // the initializer list describes an array -> create array - m_type = value_t::array; - m_value.array = create(init.begin(), init.end()); - } - - assert_invariant(); - } - - /*! - @brief explicitly create an array from an initializer list - - Creates a JSON array value from a given initializer list. That is, given a - list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the - initializer list is empty, the empty array `[]` is created. - - @note This function is only needed to express two edge cases that cannot - be realized with the initializer list constructor (@ref - basic_json(initializer_list_t, bool, value_t)). These cases - are: - 1. creating an array whose elements are all pairs whose first element is a - string -- in this case, the initializer list constructor would create an - object, taking the first elements as keys - 2. creating an empty array -- passing the empty initializer list to the - initializer list constructor yields an empty object - - @param[in] init initializer list with JSON values to create an array from - (optional) - - @return JSON array value - - @complexity Linear in the size of @a init. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @liveexample{The following code shows an example for the `array` - function.,array} - - @sa @ref basic_json(initializer_list_t, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref object(initializer_list_t) -- create a JSON object - value from an initializer list - - @since version 1.0.0 - */ - JSON_NODISCARD - static basic_json array(initializer_list_t init = {}) - { - return basic_json(init, false, value_t::array); - } - - /*! - @brief explicitly create an object from an initializer list - - Creates a JSON object value from a given initializer list. The initializer - lists elements must be pairs, and their first elements must be strings. If - the initializer list is empty, the empty object `{}` is created. - - @note This function is only added for symmetry reasons. In contrast to the - related function @ref array(initializer_list_t), there are - no cases which can only be expressed by this function. That is, any - initializer list @a init can also be passed to the initializer list - constructor @ref basic_json(initializer_list_t, bool, value_t). - - @param[in] init initializer list to create an object from (optional) - - @return JSON object value - - @throw type_error.301 if @a init is not a list of pairs whose first - elements are strings. In this case, no object can be created. When such a - value is passed to @ref basic_json(initializer_list_t, bool, value_t), - an array would have been created from the passed initializer list @a init. - See example below. - - @complexity Linear in the size of @a init. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @liveexample{The following code shows an example for the `object` - function.,object} - - @sa @ref basic_json(initializer_list_t, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref array(initializer_list_t) -- create a JSON array - value from an initializer list - - @since version 1.0.0 - */ - JSON_NODISCARD - static basic_json object(initializer_list_t init = {}) - { - return basic_json(init, false, value_t::object); - } - - /*! - @brief construct an array with count copies of given value - - Constructs a JSON array value by creating @a cnt copies of a passed value. - In case @a cnt is `0`, an empty array is created. - - @param[in] cnt the number of JSON copies of @a val to create - @param[in] val the JSON value to copy - - @post `std::distance(begin(),end()) == cnt` holds. - - @complexity Linear in @a cnt. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @liveexample{The following code shows examples for the @ref - basic_json(size_type\, const basic_json&) - constructor.,basic_json__size_type_basic_json} - - @since version 1.0.0 - */ - basic_json(size_type cnt, const basic_json& val) : m_type(value_t::array) - { - m_value.array = create(cnt, val); - assert_invariant(); - } - - /*! - @brief construct a JSON container given an iterator range - - Constructs the JSON value with the contents of the range `[first, last)`. - The semantics depends on the different types a JSON value can have: - - In case of a null type, invalid_iterator.206 is thrown. - - In case of other primitive types (number, boolean, or string), @a first - must be `begin()` and @a last must be `end()`. In this case, the value is - copied. Otherwise, invalid_iterator.204 is thrown. - - In case of structured types (array, object), the constructor behaves as - similar versions for `std::vector` or `std::map`; that is, a JSON array - or object is constructed from the values in the range. - - @tparam InputIT an input iterator type (@ref iterator or @ref - const_iterator) - - @param[in] first begin of the range to copy from (included) - @param[in] last end of the range to copy from (excluded) - - @pre Iterators @a first and @a last must be initialized. **This - precondition is enforced with an assertion (see warning).** If - assertions are switched off, a violation of this precondition yields - undefined behavior. - - @pre Range `[first, last)` is valid. Usually, this precondition cannot be - checked efficiently. Only certain edge cases are detected; see the - description of the exceptions below. A violation of this precondition - yields undefined behavior. - - @warning A precondition is enforced with a runtime assertion that will - result in calling `std::abort` if this precondition is not met. - Assertions can be disabled by defining `NDEBUG` at compile time. - See https://en.cppreference.com/w/cpp/error/assert for more - information. - - @throw invalid_iterator.201 if iterators @a first and @a last are not - compatible (i.e., do not belong to the same JSON value). In this case, - the range `[first, last)` is undefined. - @throw invalid_iterator.204 if iterators @a first and @a last belong to a - primitive type (number, boolean, or string), but @a first does not point - to the first element any more. In this case, the range `[first, last)` is - undefined. See example code below. - @throw invalid_iterator.206 if iterators @a first and @a last belong to a - null value. In this case, the range `[first, last)` is undefined. - - @complexity Linear in distance between @a first and @a last. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @liveexample{The example below shows several ways to create JSON values by - specifying a subrange with iterators.,basic_json__InputIt_InputIt} - - @since version 1.0.0 - */ - template < - class InputIT, - typename std::enable_if< - std::is_same::value - or std::is_same::value, - int>::type - = 0> - basic_json(InputIT first, InputIT last) - { - assert(first.m_object != nullptr); - assert(last.m_object != nullptr); - - // make sure iterator fits the current value - if (JSON_UNLIKELY(first.m_object != last.m_object)) - { - JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); - } - - // copy type from first iterator - m_type = first.m_object->m_type; - - // check if iterator range is complete for primitive values - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: { - if (JSON_UNLIKELY( - not first.m_it.primitive_iterator.is_begin() - or not last.m_it.primitive_iterator.is_end())) - { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); - } - break; - } - - default: - break; - } - - switch (m_type) - { - case value_t::number_integer: { - m_value.number_integer = first.m_object->m_value.number_integer; - break; - } - - case value_t::number_unsigned: { - m_value.number_unsigned = first.m_object->m_value.number_unsigned; - break; - } - - case value_t::number_float: { - m_value.number_float = first.m_object->m_value.number_float; - break; - } - - case value_t::boolean: { - m_value.boolean = first.m_object->m_value.boolean; - break; - } - - case value_t::string: { - m_value = *first.m_object->m_value.string; - break; - } - - case value_t::object: { - m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); - break; - } - - case value_t::array: { - m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); - break; - } - - default: - JSON_THROW(invalid_iterator::create( - 206, - "cannot construct with iterators from " + std::string(first.m_object->type_name()))); - } - - assert_invariant(); - } - - /////////////////////////////////////// - // other constructors and destructor // - /////////////////////////////////////// - - /// @private - basic_json(const detail::json_ref& ref) : basic_json(ref.moved_or_copied()) {} - - /*! - @brief copy constructor - - Creates a copy of a given JSON value. - - @param[in] other the JSON value to copy - - @post `*this == other` - - @complexity Linear in the size of @a other. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes to any JSON value. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is linear. - - As postcondition, it holds: `other == basic_json(other)`. - - @liveexample{The following code shows an example for the copy - constructor.,basic_json__basic_json} - - @since version 1.0.0 - */ - basic_json(const basic_json& other) : m_type(other.m_type) - { - // check of passed value is valid - other.assert_invariant(); - - switch (m_type) - { - case value_t::object: { - m_value = *other.m_value.object; - break; - } - - case value_t::array: { - m_value = *other.m_value.array; - break; - } - - case value_t::string: { - m_value = *other.m_value.string; - break; - } - - case value_t::boolean: { - m_value = other.m_value.boolean; - break; - } - - case value_t::number_integer: { - m_value = other.m_value.number_integer; - break; - } - - case value_t::number_unsigned: { - m_value = other.m_value.number_unsigned; - break; - } - - case value_t::number_float: { - m_value = other.m_value.number_float; - break; - } - - default: - break; - } - - assert_invariant(); - } - - /*! - @brief move constructor - - Move constructor. Constructs a JSON value with the contents of the given - value @a other using move semantics. It "steals" the resources from @a - other and leaves it as JSON null value. - - @param[in,out] other value to move to this object - - @post `*this` has the same value as @a other before the call. - @post @a other is a JSON null value. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. - - @requirement This function helps `basic_json` satisfying the - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible) - requirements. - - @liveexample{The code below shows the move constructor explicitly called - via std::move.,basic_json__moveconstructor} - - @since version 1.0.0 - */ - basic_json(basic_json&& other) noexcept - : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) - { - // check that passed value is valid - other.assert_invariant(); - - // invalidate payload - other.m_type = value_t::null; - other.m_value = {}; - - assert_invariant(); - } - - /*! - @brief copy assignment - - Copy assignment operator. Copies a JSON value via the "copy and swap" - strategy: It is expressed in terms of the copy constructor, destructor, - and the `swap()` member function. - - @param[in] other value to copy from - - @complexity Linear. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is linear. - - @liveexample{The code below shows and example for the copy assignment. It - creates a copy of value `a` which is then swapped with `b`. Finally\, the - copy of `a` (which is the null value after the swap) is - destroyed.,basic_json__copyassignment} - - @since version 1.0.0 - */ - basic_json& operator=(basic_json other) noexcept( - std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable< - value_t>::value and std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value) - { - // check that passed value is valid - other.assert_invariant(); - - using std::swap; - swap(m_type, other.m_type); - swap(m_value, other.m_value); - - assert_invariant(); - return *this; - } - - /*! - @brief destructor - - Destroys the JSON value and frees all allocated memory. - - @complexity Linear. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is linear. - - All stored elements are destroyed and all memory is freed. - - @since version 1.0.0 - */ - ~basic_json() noexcept - { - assert_invariant(); - m_value.destroy(m_type); - } - - /// @} - -public: - /////////////////////// - // object inspection // - /////////////////////// - - /// @name object inspection - /// Functions to inspect the type of a JSON value. - /// @{ - - /*! - @brief serialization - - Serialization function for JSON values. The function tries to mimic - Python's `json.dumps()` function, and currently supports its @a indent - and @a ensure_ascii parameters. - - @param[in] indent If indent is nonnegative, then array elements and object - members will be pretty-printed with that indent level. An indent level of - `0` will only insert newlines. `-1` (the default) selects the most compact - representation. - @param[in] indent_char The character to use for indentation if @a indent is - greater than `0`. The default is ` ` (space). - @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] error_handler how to react on decoding errors; there are three - 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). - - @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 - - @complexity Linear. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @liveexample{The following example shows the effect of different @a indent\, - @a indent_char\, and @a ensure_ascii parameters to the result of the - serialization.,dump} - - @see https://docs.python.org/2/library/json.html#json.dump - - @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. - */ - 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 - { - 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)); - } - else - { - s.dump(*this, false, ensure_ascii, 0); - } - - return result; - } - - /*! - @brief return the type of the JSON value (explicit) - - Return the type of the JSON value as a value from the @ref value_t - enumeration. - - @return the type of the JSON value - Value type | return value - ------------------------- | ------------------------- - null | value_t::null - boolean | value_t::boolean - string | value_t::string - number (integer) | value_t::number_integer - number (unsigned integer) | value_t::number_unsigned - number (floating-point) | value_t::number_float - object | value_t::object - array | value_t::array - discarded | value_t::discarded - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `type()` for all JSON - types.,type} - - @sa @ref operator value_t() -- return the type of the JSON value (implicit) - @sa @ref type_name() -- return the type as string - - @since version 1.0.0 - */ - constexpr value_t type() const noexcept { return m_type; } - - /*! - @brief return whether type is primitive - - This function returns true if and only if the JSON type is primitive - (string, number, boolean, or null). - - @return `true` if type is primitive (string, number, boolean, or null), - `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_primitive()` for all JSON - types.,is_primitive} - - @sa @ref is_structured() -- returns whether JSON value is structured - @sa @ref is_null() -- returns whether JSON value is `null` - @sa @ref is_string() -- returns whether JSON value is a string - @sa @ref is_boolean() -- returns whether JSON value is a boolean - @sa @ref is_number() -- returns whether JSON value is a number - - @since version 1.0.0 - */ - constexpr bool is_primitive() const noexcept - { - return is_null() or is_string() or is_boolean() or is_number(); - } - - /*! - @brief return whether type is structured - - This function returns true if and only if the JSON type is structured - (array or object). - - @return `true` if type is structured (array or object), `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_structured()` for all JSON - types.,is_structured} - - @sa @ref is_primitive() -- returns whether value is primitive - @sa @ref is_array() -- returns whether value is an array - @sa @ref is_object() -- returns whether value is an object - - @since version 1.0.0 - */ - constexpr bool is_structured() const noexcept { return is_array() or is_object(); } - - /*! - @brief return whether value is null - - This function returns true if and only if the JSON value is null. - - @return `true` if type is null, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_null()` for all JSON - types.,is_null} - - @since version 1.0.0 - */ - constexpr bool is_null() const noexcept { return m_type == value_t::null; } - - /*! - @brief return whether value is a boolean - - This function returns true if and only if the JSON value is a boolean. - - @return `true` if type is boolean, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_boolean()` for all JSON - types.,is_boolean} - - @since version 1.0.0 - */ - constexpr bool is_boolean() const noexcept { return m_type == value_t::boolean; } - - /*! - @brief return whether value is a number - - This function returns true if and only if the JSON value is a number. This - includes both integer (signed and unsigned) and floating-point values. - - @return `true` if type is number (regardless whether integer, unsigned - integer or floating-type), `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_number()` for all JSON - types.,is_number} - - @sa @ref is_number_integer() -- check if value is an integer or unsigned - integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - @sa @ref is_number_float() -- check if value is a floating-point number - - @since version 1.0.0 - */ - constexpr bool is_number() const noexcept { return is_number_integer() or is_number_float(); } - - /*! - @brief return whether value is an integer number - - This function returns true if and only if the JSON value is a signed or - unsigned integer number. This excludes floating-point values. - - @return `true` if type is an integer or unsigned integer number, `false` - otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_number_integer()` for all - JSON types.,is_number_integer} - - @sa @ref is_number() -- check if value is a number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - @sa @ref is_number_float() -- check if value is a floating-point number - - @since version 1.0.0 - */ - constexpr bool is_number_integer() const noexcept - { - return m_type == value_t::number_integer or m_type == value_t::number_unsigned; - } - - /*! - @brief return whether value is an unsigned integer number - - This function returns true if and only if the JSON value is an unsigned - integer number. This excludes floating-point and signed integer values. - - @return `true` if type is an unsigned integer number, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_number_unsigned()` for all - JSON types.,is_number_unsigned} - - @sa @ref is_number() -- check if value is a number - @sa @ref is_number_integer() -- check if value is an integer or unsigned - integer number - @sa @ref is_number_float() -- check if value is a floating-point number - - @since version 2.0.0 - */ - constexpr bool is_number_unsigned() const noexcept { return m_type == value_t::number_unsigned; } - - /*! - @brief return whether value is a floating-point number - - This function returns true if and only if the JSON value is a - floating-point number. This excludes signed and unsigned integer values. - - @return `true` if type is a floating-point number, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_number_float()` for all - JSON types.,is_number_float} - - @sa @ref is_number() -- check if value is number - @sa @ref is_number_integer() -- check if value is an integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - - @since version 1.0.0 - */ - constexpr bool is_number_float() const noexcept { return m_type == value_t::number_float; } - - /*! - @brief return whether value is an object - - This function returns true if and only if the JSON value is an object. - - @return `true` if type is object, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_object()` for all JSON - types.,is_object} - - @since version 1.0.0 - */ - constexpr bool is_object() const noexcept { return m_type == value_t::object; } - - /*! - @brief return whether value is an array - - This function returns true if and only if the JSON value is an array. - - @return `true` if type is array, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_array()` for all JSON - types.,is_array} - - @since version 1.0.0 - */ - constexpr bool is_array() const noexcept { return m_type == value_t::array; } - - /*! - @brief return whether value is a string - - This function returns true if and only if the JSON value is a string. - - @return `true` if type is string, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_string()` for all JSON - types.,is_string} - - @since version 1.0.0 - */ - constexpr bool is_string() const noexcept { return m_type == value_t::string; } - - /*! - @brief return whether value is discarded - - This function returns true if and only if the JSON value was discarded - during parsing with a callback function (see @ref parser_callback_t). - - @note This function will always be `false` for JSON values after parsing. - That is, discarded values can only occur during parsing, but will be - removed when inside a structured value or replaced by null in other cases. - - @return `true` if type is discarded, `false` otherwise. - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies `is_discarded()` for all JSON - types.,is_discarded} - - @since version 1.0.0 - */ - constexpr bool is_discarded() const noexcept { return m_type == value_t::discarded; } - - /*! - @brief return the type of the JSON value (implicit) - - Implicitly return the type of the JSON value as a value from the @ref - value_t enumeration. - - @return the type of the JSON value - - @complexity Constant. - - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. - - @liveexample{The following code exemplifies the @ref value_t operator for - all JSON types.,operator__value_t} - - @sa @ref type() -- return the type of the JSON value (explicit) - @sa @ref type_name() -- return the type as string - - @since version 1.0.0 - */ - constexpr operator value_t() const noexcept { return m_type; } - - /// @} - -private: - ////////////////// - // value access // - ////////////////// - - /// get a boolean (explicit) - boolean_t get_impl(boolean_t* /*unused*/) const - { - if (JSON_LIKELY(is_boolean())) - { - return m_value.boolean; - } - - JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()))); - } - - /// get a pointer to the value (object) - object_t* get_impl_ptr(object_t* /*unused*/) noexcept - { - return is_object() ? m_value.object : nullptr; - } - - /// get a pointer to the value (object) - constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept - { - return is_object() ? m_value.object : nullptr; - } - - /// get a pointer to the value (array) - array_t* get_impl_ptr(array_t* /*unused*/) noexcept - { - return is_array() ? m_value.array : nullptr; - } - - /// get a pointer to the value (array) - constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept - { - return is_array() ? m_value.array : nullptr; - } - - /// get a pointer to the value (string) - string_t* get_impl_ptr(string_t* /*unused*/) noexcept - { - return is_string() ? m_value.string : nullptr; - } - - /// get a pointer to the value (string) - constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept - { - return is_string() ? m_value.string : nullptr; - } - - /// get a pointer to the value (boolean) - boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept - { - return is_boolean() ? &m_value.boolean : nullptr; - } - - /// get a pointer to the value (boolean) - constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept - { - return is_boolean() ? &m_value.boolean : nullptr; - } - - /// get a pointer to the value (integer number) - number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept - { - return is_number_integer() ? &m_value.number_integer : nullptr; - } - - /// get a pointer to the value (integer number) - constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept - { - return is_number_integer() ? &m_value.number_integer : nullptr; - } - - /// get a pointer to the value (unsigned number) - number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept - { - return is_number_unsigned() ? &m_value.number_unsigned : nullptr; - } - - /// get a pointer to the value (unsigned number) - constexpr const number_unsigned_t* get_impl_ptr( - const number_unsigned_t* /*unused*/) const noexcept - { - return is_number_unsigned() ? &m_value.number_unsigned : nullptr; - } - - /// get a pointer to the value (floating-point number) - number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept - { - return is_number_float() ? &m_value.number_float : nullptr; - } - - /// get a pointer to the value (floating-point number) - constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept - { - return is_number_float() ? &m_value.number_float : nullptr; - } - - /*! - @brief helper function to implement get_ref() - - This function helps to implement get_ref() without code duplication for - const and non-const overloads - - @tparam ThisType will be deduced as `basic_json` or `const basic_json` - - @throw type_error.303 if ReferenceType does not match underlying value - type of the current JSON - */ - template - static ReferenceType get_ref_impl(ThisType& obj) - { - // delegate the call to get_ptr<>() - auto ptr = obj.template get_ptr::type>(); - - if (JSON_LIKELY(ptr != nullptr)) - { - return *ptr; - } - - JSON_THROW(type_error::create( - 303, - "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()))); - } - -public: - /// @name value access - /// Direct access to the stored value of a JSON value. - /// @{ - - /*! - @brief get special-case overload - - This overloads avoids a lot of template boilerplate, it can be seen as the - identity method - - @tparam BasicJsonType == @ref basic_json - - @return a copy of *this - - @complexity Constant. - - @since version 2.1.0 - */ - template < - typename BasicJsonType, - detail::enable_if_t< - std::is_same::type, basic_json_t>::value, - int> = 0> - basic_json get() const - { - return *this; - } - - /*! - @brief get special-case overload - - This overloads converts the current @ref basic_json in a different - @ref basic_json type - - @tparam BasicJsonType == @ref basic_json - - @return a copy of *this, converted into @tparam BasicJsonType - - @complexity Depending on the implementation of the called `from_json()` - method. - - @since version 3.2.0 - */ - template < - typename BasicJsonType, - detail::enable_if_t< - not std::is_same::value - and detail::is_basic_json::value, - int> = 0> - BasicJsonType get() const - { - return *this; - } - - /*! - @brief get a value (explicit) - - Explicit type conversion between the JSON value and a compatible value - which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) - and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). - The value is converted by calling the @ref json_serializer - `from_json()` method. - - The function is equivalent to executing - @code {.cpp} - ValueType ret; - JSONSerializer::from_json(*this, ret); - return ret; - @endcode - - This overloads is chosen if: - - @a ValueType is not @ref basic_json, - - @ref json_serializer has a `from_json()` method of the form - `void from_json(const basic_json&, ValueType&)`, and - - @ref json_serializer does not have a `from_json()` method of - the form `ValueType from_json(const basic_json&)` - - @tparam ValueTypeCV the provided value type - @tparam ValueType the returned value type - - @return copy of the JSON value, converted to @a ValueType - - @throw what @ref json_serializer `from_json()` method throws - - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,get__ValueType_const} - - @since version 2.1.0 - */ - template < - typename ValueTypeCV, - typename ValueType = detail::uncvref_t, - detail::enable_if_t< - not detail::is_basic_json::value - and detail::has_from_json::value - and not detail::has_non_default_from_json::value, - int> = 0> - ValueType get() const noexcept(noexcept(JSONSerializer::from_json( - std::declval(), - std::declval()))) - { - // we cannot static_assert on ValueTypeCV being non-const, because - // there is support for get(), which is why we - // still need the uncvref - static_assert( - not std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); - static_assert( - std::is_default_constructible::value, - "types must be DefaultConstructible when used with get()"); - - ValueType ret; - JSONSerializer::from_json(*this, ret); - return ret; - } - - /*! - @brief get a value (explicit); special case - - Explicit type conversion between the JSON value and a compatible value - which is **not** - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) and **not** - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). The - value is converted by calling the @ref json_serializer `from_json()` method. - - The function is equivalent to executing - @code {.cpp} - return JSONSerializer::from_json(*this); - @endcode - - This overloads is chosen if: - - @a ValueType is not @ref basic_json and - - @ref json_serializer has a `from_json()` method of the form - `ValueType from_json(const basic_json&)` - - @note If @ref json_serializer has both overloads of - `from_json()`, this one is chosen. - - @tparam ValueTypeCV the provided value type - @tparam ValueType the returned value type - - @return copy of the JSON value, converted to @a ValueType - - @throw what @ref json_serializer `from_json()` method throws - - @since version 2.1.0 - */ - template < - typename ValueTypeCV, - typename ValueType = detail::uncvref_t, - detail::enable_if_t< - not std::is_same::value - and detail::has_non_default_from_json::value, - int> = 0> - ValueType get() const noexcept( - noexcept(JSONSerializer::from_json(std::declval()))) - { - static_assert( - not std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); - return JSONSerializer::from_json(*this); - } - - /*! - @brief get a value (explicit) - - Explicit type conversion between the JSON value and a compatible value. - The value is filled into the input parameter by calling the @ref json_serializer - `from_json()` method. - - The function is equivalent to executing - @code {.cpp} - ValueType v; - JSONSerializer::from_json(*this, v); - @endcode - - This overloads is chosen if: - - @a ValueType is not @ref basic_json, - - @ref json_serializer has a `from_json()` method of the form - `void from_json(const basic_json&, ValueType&)`, and - - @tparam ValueType the input parameter type. - - @return the input parameter, allowing chaining calls. - - @throw what @ref json_serializer `from_json()` method throws - - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,get_to} - - @since version 3.3.0 - */ - template < - typename ValueType, - detail::enable_if_t< - not detail::is_basic_json::value - and detail::has_from_json::value, - int> = 0> - ValueType& get_to(ValueType& v) const noexcept( - noexcept(JSONSerializer::from_json(std::declval(), v))) - { - JSONSerializer::from_json(*this, v); - return v; - } - - /*! - @brief get a pointer value (implicit) - - Implicit pointer access to the internally stored JSON value. No copies are - made. - - @warning Writing data to the pointee of the result yields an undefined - state. - - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. Enforced by a static - assertion. - - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise - - @complexity Constant. - - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get_ptr} - - @since version 1.0.0 - */ - template < - typename PointerType, - typename std::enable_if::value, int>::type = 0> - auto get_ptr() noexcept - -> decltype(std::declval().get_impl_ptr(std::declval())) - { - // delegate the call to get_impl_ptr<>() - return get_impl_ptr(static_cast(nullptr)); - } - - /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() - */ - template < - typename PointerType, - typename std::enable_if< - std::is_pointer::value - and std::is_const::type>::value, - int>::type - = 0> - constexpr auto get_ptr() const noexcept - -> decltype(std::declval().get_impl_ptr(std::declval())) - { - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); - } - - /*! - @brief get a pointer value (explicit) - - Explicit pointer access to the internally stored JSON value. No copies are - made. - - @warning The pointer becomes invalid if the underlying JSON object - changes. - - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. - - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise - - @complexity Constant. - - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get__PointerType} - - @sa @ref get_ptr() for explicit pointer-member access - - @since version 1.0.0 - */ - template < - typename PointerType, - typename std::enable_if::value, int>::type = 0> - auto get() noexcept -> decltype(std::declval().template get_ptr()) - { - // delegate the call to get_ptr - return get_ptr(); - } - - /*! - @brief get a pointer value (explicit) - @copydoc get() - */ - template < - typename PointerType, - typename std::enable_if::value, int>::type = 0> - constexpr auto get() const noexcept - -> decltype(std::declval().template get_ptr()) - { - // delegate the call to get_ptr - return get_ptr(); - } - - /*! - @brief get a reference value (implicit) - - Implicit reference access to the internally stored JSON value. No copies - are made. - - @warning Writing data to the referee of the result yields an undefined - state. - - @tparam ReferenceType reference type; must be a reference to @ref array_t, - @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or - @ref number_float_t. Enforced by static assertion. - - @return reference to the internally stored JSON value if the requested - reference type @a ReferenceType fits to the JSON value; throws - type_error.303 otherwise - - @throw type_error.303 in case passed type @a ReferenceType is incompatible - with the stored JSON value; see example below - - @complexity Constant. - - @liveexample{The example shows several calls to `get_ref()`.,get_ref} - - @since version 1.1.0 - */ - template < - typename ReferenceType, - typename std::enable_if::value, int>::type = 0> - ReferenceType get_ref() - { - // delegate call to get_ref_impl - return get_ref_impl(*this); - } - - /*! - @brief get a reference value (implicit) - @copydoc get_ref() - */ - template < - typename ReferenceType, - typename std::enable_if< - std::is_reference::value - and std::is_const::type>::value, - int>::type - = 0> - ReferenceType get_ref() const - { - // delegate call to get_ref_impl - return get_ref_impl(*this); - } - - /*! - @brief get a value (implicit) - - Implicit type conversion between the JSON value and a compatible value. - The call is realized by calling @ref get() const. - - @tparam ValueType non-pointer type compatible to the JSON value, for - instance `int` for JSON integer numbers, `bool` for JSON booleans, or - `std::vector` types for JSON arrays. The character type of @ref string_t - as well as an initializer list of this type is excluded to avoid - ambiguities as these types implicitly convert to `std::string`. - - @return copy of the JSON value, converted to type @a ValueType - - @throw type_error.302 in case passed type @a ValueType is incompatible - to the JSON value type (e.g., the JSON value is of type boolean, but a - string is requested); see example below - - @complexity Linear in the size of the JSON value. - - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,operator__ValueType} - - @since version 1.0.0 - */ - template < - typename ValueType, - typename std::enable_if< - not std::is_pointer::value - and not std::is_same>::value - and not std::is_same::value - and not detail::is_basic_json::value - -#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 - and not std::is_same< - ValueType, - std::initializer_list>::value -#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) and _MSC_VER <= 1914)) - and not std::is_same::value -#endif -#endif - and detail:: - is_detected::value, - int>::type - = 0> - operator ValueType() const - { - // delegate the call to get<>() const - return get(); - } - - /// @} - - //////////////////// - // element access // - //////////////////// - - /// @name element access - /// Access to the JSON value. - /// @{ - - /*! - @brief access specified array element with bounds checking - - Returns a reference to the element at specified location @a idx, with - bounds checking. - - @param[in] idx index of the element to access - - @return reference to the element at index @a idx - - @throw type_error.304 if the JSON value is not an array; in this case, - calling `at` with an index makes no sense. See example below. - @throw out_of_range.401 if the index @a idx is out of range of the array; - that is, `idx >= size()`. See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Constant. - - @since version 1.0.0 - - @liveexample{The example below shows how array elements can be read and - written using `at()`. It also demonstrates the different exceptions that - can be thrown.,at__size_type} - */ - reference at(size_type idx) - { - // at only works for arrays - if (JSON_LIKELY(is_array())) - { - JSON_TRY { return m_value.array->at(idx); } - JSON_CATCH(std::out_of_range&) - { - // create better exception explanation - JSON_THROW( - out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); - } - } - else - { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); - } - } - - /*! - @brief access specified array element with bounds checking - - Returns a const reference to the element at specified location @a idx, - with bounds checking. - - @param[in] idx index of the element to access - - @return const reference to the element at index @a idx - - @throw type_error.304 if the JSON value is not an array; in this case, - calling `at` with an index makes no sense. See example below. - @throw out_of_range.401 if the index @a idx is out of range of the array; - that is, `idx >= size()`. See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Constant. - - @since version 1.0.0 - - @liveexample{The example below shows how array elements can be read using - `at()`. It also demonstrates the different exceptions that can be thrown., - at__size_type_const} - */ - const_reference at(size_type idx) const - { - // at only works for arrays - if (JSON_LIKELY(is_array())) - { - JSON_TRY { return m_value.array->at(idx); } - JSON_CATCH(std::out_of_range&) - { - // create better exception explanation - JSON_THROW( - out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); - } - } - else - { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); - } - } - - /*! - @brief access specified object element with bounds checking - - Returns a reference to the element at with specified key @a key, with - bounds checking. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw type_error.304 if the JSON value is not an object; in this case, - calling `at` with a key makes no sense. See example below. - @throw out_of_range.403 if the key @a key is is not stored in the object; - that is, `find(key) == end()`. See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Logarithmic in the size of the container. - - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - - @liveexample{The example below shows how object elements can be read and - written using `at()`. It also demonstrates the different exceptions that - can be thrown.,at__object_t_key_type} - */ - reference at(const typename object_t::key_type& key) - { - // at only works for objects - if (JSON_LIKELY(is_object())) - { - JSON_TRY { return m_value.object->at(key); } - JSON_CATCH(std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); - } - } - else - { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); - } - } - - /*! - @brief access specified object element with bounds checking - - Returns a const reference to the element at with specified key @a key, - with bounds checking. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @throw type_error.304 if the JSON value is not an object; in this case, - calling `at` with a key makes no sense. See example below. - @throw out_of_range.403 if the key @a key is is not stored in the object; - that is, `find(key) == end()`. See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Logarithmic in the size of the container. - - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - - @liveexample{The example below shows how object elements can be read using - `at()`. It also demonstrates the different exceptions that can be thrown., - at__object_t_key_type_const} - */ - const_reference at(const typename object_t::key_type& key) const - { - // at only works for objects - if (JSON_LIKELY(is_object())) - { - JSON_TRY { return m_value.object->at(key); } - JSON_CATCH(std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); - } - } - else - { - JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); - } - } - - /*! - @brief access specified array element - - Returns a reference to the element at specified location @a idx. - - @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), - then the array is silently filled up with `null` values to make `idx` a - valid reference to the last stored element. - - @param[in] idx index of the element to access - - @return reference to the element at index @a idx - - @throw type_error.305 if the JSON value is not an array or null; in that - cases, using the [] operator with an index makes no sense. - - @complexity Constant if @a idx is in the range of the array. Otherwise - linear in `idx - size()`. - - @liveexample{The example below shows how array elements can be read and - written using `[]` operator. Note the addition of `null` - values.,operatorarray__size_type} - - @since version 1.0.0 - */ - reference operator[](size_type idx) - { - // implicitly convert null value to an empty array - if (is_null()) - { - m_type = value_t::array; - m_value.array = create(); - assert_invariant(); - } - - // operator[] only works for arrays - if (JSON_LIKELY(is_array())) - { - // fill up array with null values if given idx is outside range - if (idx >= m_value.array->size()) - { - m_value.array->insert(m_value.array->end(), idx - m_value.array->size() + 1, basic_json()); - } - - return m_value.array->operator[](idx); - } - - JSON_THROW(type_error::create( - 305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); - } - - /*! - @brief access specified array element - - Returns a const reference to the element at specified location @a idx. - - @param[in] idx index of the element to access - - @return const reference to the element at index @a idx - - @throw type_error.305 if the JSON value is not an array; in that case, - using the [] operator with an index makes no sense. - - @complexity Constant. - - @liveexample{The example below shows how array elements can be read using - the `[]` operator.,operatorarray__size_type_const} - - @since version 1.0.0 - */ - const_reference operator[](size_type idx) const - { - // const operator[] only works for arrays - if (JSON_LIKELY(is_array())) - { - return m_value.array->operator[](idx); - } - - JSON_THROW(type_error::create( - 305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); - } - - /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - reference operator[](const typename object_t::key_type& key) - { - // implicitly convert null value to an empty object - if (is_null()) - { - m_type = value_t::object; - m_value.object = create(); - assert_invariant(); - } - - // operator[] only works for objects - if (JSON_LIKELY(is_object())) - { - return m_value.object->operator[](key); - } - - JSON_THROW(type_error::create( - 305, "cannot use operator[] with a string argument with " + std::string(type_name()))); - } - - /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @pre The element with key @a key must exist. **This precondition is - enforced with an assertion.** - - @throw type_error.305 if the JSON value is not an object; in that case, - using the [] operator with a key makes no sense. - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.0.0 - */ - const_reference operator[](const typename object_t::key_type& key) const - { - // const operator[] only works for objects - if (JSON_LIKELY(is_object())) - { - assert(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; - } - - JSON_THROW(type_error::create( - 305, "cannot use operator[] with a string argument with " + std::string(type_name()))); - } - - /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. - - @param[in] key key of the element to access - - @return reference to the element at key @a key - - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.1.0 - */ - template reference operator[](T* key) - { - // implicitly convert null to object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); - } - - // at only works for objects - if (JSON_LIKELY(is_object())) - { - return m_value.object->operator[](key); - } - - JSON_THROW(type_error::create( - 305, "cannot use operator[] with a string argument with " + std::string(type_name()))); - } - - /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key - - @pre The element with key @a key must exist. **This precondition is - enforced with an assertion.** - - @throw type_error.305 if the JSON value is not an object; in that case, - using the [] operator with a key makes no sense. - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value - - @since version 1.1.0 - */ - template const_reference operator[](T* key) const - { - // at only works for objects - if (JSON_LIKELY(is_object())) - { - assert(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; - } - - JSON_THROW(type_error::create( - 305, "cannot use operator[] with a string argument with " + std::string(type_name()))); - } - - /*! - @brief access specified object element with default value - - Returns either a copy of an object's element at the specified key @a key - or a given default value if no element with key @a key exists. - - The function is basically equivalent to executing - @code {.cpp} - try { - return at(key); - } catch(out_of_range) { - return default_value; - } - @endcode - - @note Unlike @ref at(const typename object_t::key_type&), this function - does not throw if the given key @a key was not found. - - @note Unlike @ref operator[](const typename object_t::key_type& key), this - function does not implicitly add an element to the position defined by @a - key. This function is furthermore also applicable to const objects. - - @param[in] key key of the element to access - @param[in] default_value the value to return if @a key is not found - - @tparam ValueType type compatible to JSON values, for instance `int` for - JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for - JSON arrays. Note the type of the expected value at @a key and the default - value @a default_value must be compatible. - - @return copy of the element at key @a key or @a default_value if @a key - is not found - - @throw type_error.306 if the JSON value is not an object; in that case, - using `value()` with a key makes no sense. - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be queried - with a default value.,basic_json__value} - - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - - @since version 1.0.0 - */ - template < - class ValueType, - typename std::enable_if::value, int>::type = 0> - ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const - { - // at only works for objects - if (JSON_LIKELY(is_object())) - { - // if key is found, return value and given default value otherwise - const auto it = find(key); - if (it != end()) - { - return *it; - } - - return default_value; - } - - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); - } - - /*! - @brief overload for a default value of type const char* - @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const - */ - string_t value(const typename object_t::key_type& key, const char* default_value) const - { - return value(key, string_t(default_value)); - } - - /*! - @brief access specified object element via JSON Pointer with default value - - Returns either a copy of an object's element at the specified key @a key - or a given default value if no element with key @a key exists. - - The function is basically equivalent to executing - @code {.cpp} - try { - return at(ptr); - } catch(out_of_range) { - return default_value; - } - @endcode - - @note Unlike @ref at(const json_pointer&), this function does not throw - if the given key @a key was not found. - - @param[in] ptr a JSON pointer to the element to access - @param[in] default_value the value to return if @a ptr found no value - - @tparam ValueType type compatible to JSON values, for instance `int` for - JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for - JSON arrays. Note the type of the expected value at @a key and the default - value @a default_value must be compatible. - - @return copy of the element at key @a key or @a default_value if @a key - is not found - - @throw type_error.306 if the JSON value is not an object; in that case, - using `value()` with a key makes no sense. - - @complexity Logarithmic in the size of the container. - - @liveexample{The example below shows how object elements can be queried - with a default value.,basic_json__value_ptr} - - @sa @ref operator[](const json_pointer&) for unchecked access by reference - - @since version 2.0.2 - */ - template < - class ValueType, - typename std::enable_if::value, int>::type = 0> - ValueType value(const json_pointer& ptr, const ValueType& default_value) const - { - // at only works for objects - if (JSON_LIKELY(is_object())) - { - // if pointer resolves a value, return it or use default value - JSON_TRY { return ptr.get_checked(this); } - JSON_INTERNAL_CATCH(out_of_range&) { return default_value; } - } - - JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); - } - - /*! - @brief overload for a default value of type const char* - @copydoc basic_json::value(const json_pointer&, ValueType) const - */ - string_t value(const json_pointer& ptr, const char* default_value) const - { - return value(ptr, string_t(default_value)); - } - - /*! - @brief access the first element - - Returns a reference to the first element in the container. For a JSON - container `c`, the expression `c.front()` is equivalent to `*c.begin()`. - - @return In case of a structured type (array or object), a reference to the - first element is returned. In case of number, string, or boolean values, a - reference to the value is returned. - - @complexity Constant. - - @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, **guarded by - assertions**). - @post The JSON value remains unchanged. - - @throw invalid_iterator.214 when called on `null` value - - @liveexample{The following code shows an example for `front()`.,front} - - @sa @ref back() -- access the last element - - @since version 1.0.0 - */ - reference front() { return *begin(); } - - /*! - @copydoc basic_json::front() - */ - const_reference front() const { return *cbegin(); } - - /*! - @brief access the last element - - Returns a reference to the last element in the container. For a JSON - container `c`, the expression `c.back()` is equivalent to - @code {.cpp} - auto tmp = c.end(); - --tmp; - return *tmp; - @endcode - - @return In case of a structured type (array or object), a reference to the - last element is returned. In case of number, string, or boolean values, a - reference to the value is returned. - - @complexity Constant. - - @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, **guarded by - assertions**). - @post The JSON value remains unchanged. - - @throw invalid_iterator.214 when called on a `null` value. See example - below. - - @liveexample{The following code shows an example for `back()`.,back} - - @sa @ref front() -- access the first element - - @since version 1.0.0 - */ - reference back() - { - auto tmp = end(); - --tmp; - return *tmp; - } - - /*! - @copydoc basic_json::back() - */ - const_reference back() const - { - auto tmp = cend(); - --tmp; - return *tmp; - } - - /*! - @brief remove element given an iterator - - Removes the element specified by iterator @a pos. The iterator @a pos must - be valid and dereferenceable. Thus the `end()` iterator (which is valid, - but is not dereferenceable) cannot be used as a value for @a pos. - - If called on a primitive type other than `null`, the resulting JSON value - will be `null`. - - @param[in] pos iterator to the element to remove - @return Iterator following the last removed element. If the iterator @a - pos refers to the last element, the `end()` iterator is returned. - - @tparam IteratorType an @ref iterator or @ref const_iterator - - @post Invalidates iterators and references at or after the point of the - erase, including the `end()` iterator. - - @throw type_error.307 if called on a `null` value; example: `"cannot use - erase() with null"` - @throw invalid_iterator.202 if called on an iterator which does not belong - to the current JSON value; example: `"iterator does not fit current - value"` - @throw invalid_iterator.205 if called on a primitive type with invalid - iterator (i.e., any iterator which is not `begin()`); example: `"iterator - out of range"` - - @complexity The complexity depends on the type: - - objects: amortized constant - - arrays: linear in distance between @a pos and the end of the container - - strings: linear in the length of the string - - other types: constant - - @liveexample{The example shows the result of `erase()` for different JSON - types.,erase__IteratorType} - - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at - the given index - - @since version 1.0.0 - */ - template < - class IteratorType, - typename std::enable_if< - std::is_same::value - or std::is_same::value, - int>::type - = 0> - IteratorType erase(IteratorType pos) - { - // make sure iterator fits the current value - if (JSON_UNLIKELY(this != pos.m_object)) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); - } - - IteratorType result = end(); - - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: { - if (JSON_UNLIKELY(not pos.m_it.primitive_iterator.is_begin())) - { - JSON_THROW(invalid_iterator::create(205, "iterator out of range")); - } - - if (is_string()) - { - AllocatorType alloc; - std::allocator_traits::destroy(alloc, m_value.string); - std::allocator_traits::deallocate(alloc, m_value.string, 1); - m_value.string = nullptr; - } - - m_type = value_t::null; - assert_invariant(); - break; - } - - case value_t::object: { - result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); - break; - } - - case value_t::array: { - result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); - break; - } - - default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); - } - - return result; - } - - /*! - @brief remove elements given an iterator range - - Removes the element specified by the range `[first; last)`. The iterator - @a first does not need to be dereferenceable if `first == last`: erasing - an empty range is a no-op. - - If called on a primitive type other than `null`, the resulting JSON value - will be `null`. - - @param[in] first iterator to the beginning of the range to remove - @param[in] last iterator past the end of the range to remove - @return Iterator following the last removed element. If the iterator @a - second refers to the last element, the `end()` iterator is returned. - - @tparam IteratorType an @ref iterator or @ref const_iterator - - @post Invalidates iterators and references at or after the point of the - erase, including the `end()` iterator. - - @throw type_error.307 if called on a `null` value; example: `"cannot use - erase() with null"` - @throw invalid_iterator.203 if called on iterators which does not belong - to the current JSON value; example: `"iterators do not fit current value"` - @throw invalid_iterator.204 if called on a primitive type with invalid - iterators (i.e., if `first != begin()` and `last != end()`); example: - `"iterators out of range"` - - @complexity The complexity depends on the type: - - objects: `log(size()) + std::distance(first, last)` - - arrays: linear in the distance between @a first and @a last, plus linear - in the distance between @a last and end of the container - - strings: linear in the length of the string - - other types: constant - - @liveexample{The example shows the result of `erase()` for different JSON - types.,erase__IteratorType_IteratorType} - - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at - the given index - - @since version 1.0.0 - */ - template < - class IteratorType, - typename std::enable_if< - std::is_same::value - or std::is_same::value, - int>::type - = 0> - IteratorType erase(IteratorType first, IteratorType last) - { - // make sure iterator fits the current value - if (JSON_UNLIKELY(this != first.m_object or this != last.m_object)) - { - JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); - } - - IteratorType result = end(); - - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: { - if (JSON_LIKELY( - not first.m_it.primitive_iterator.is_begin() - or not last.m_it.primitive_iterator.is_end())) - { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); - } - - if (is_string()) - { - AllocatorType alloc; - std::allocator_traits::destroy(alloc, m_value.string); - std::allocator_traits::deallocate(alloc, m_value.string, 1); - m_value.string = nullptr; - } - - m_type = value_t::null; - assert_invariant(); - break; - } - - case value_t::object: { - result.m_it.object_iterator - = m_value.object->erase(first.m_it.object_iterator, last.m_it.object_iterator); - break; - } - - case value_t::array: { - result.m_it.array_iterator - = m_value.array->erase(first.m_it.array_iterator, last.m_it.array_iterator); - break; - } - - default: - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); - } - - return result; - } - - /*! - @brief remove element from a JSON object given a key - - Removes elements from a JSON object with the key value @a key. - - @param[in] key value of the elements to remove - - @return Number of elements removed. If @a ObjectType is the default - `std::map` type, the return value will always be `0` (@a key was not - found) or `1` (@a key was found). - - @post References and iterators to the erased elements are invalidated. - Other references and iterators are not affected. - - @throw type_error.307 when called on a type other than JSON object; - example: `"cannot use erase() with null"` - - @complexity `log(size()) + count(key)` - - @liveexample{The example shows the effect of `erase()`.,erase__key_type} - - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const size_type) -- removes the element from an array at - the given index - - @since version 1.0.0 - */ - size_type erase(const typename object_t::key_type& key) - { - // this erase only works for objects - if (JSON_LIKELY(is_object())) - { - return m_value.object->erase(key); - } - - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); - } - - /*! - @brief remove element from a JSON array given an index - - Removes element from a JSON array at the index @a idx. - - @param[in] idx index of the element to remove - - @throw type_error.307 when called on a type other than JSON object; - example: `"cannot use erase() with null"` - @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 - is out of range"` - - @complexity Linear in distance between @a idx and the end of the container. - - @liveexample{The example shows the effect of `erase()`.,erase__size_type} - - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - - @since version 1.0.0 - */ - void erase(const size_type idx) - { - // this erase only works for arrays - if (JSON_LIKELY(is_array())) - { - if (JSON_UNLIKELY(idx >= size())) - { - JSON_THROW( - out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); - } - - m_value.array->erase(m_value.array->begin() + static_cast(idx)); - } - else - { - JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); - } - } - - /// @} - - //////////// - // lookup // - //////////// - - /// @name lookup - /// @{ - - /*! - @brief find an element in a JSON object - - Finds an element in a JSON object with key equivalent to @a key. If the - element is not found or the JSON value is not an object, end() is - returned. - - @note This method always returns @ref end() when executed on a JSON type - that is not an object. - - @param[in] key key value of the element to search for. - - @return Iterator to an element with key equivalent to @a key. If no such - element is found or the JSON value is not an object, past-the-end (see - @ref end()) iterator is returned. - - @complexity Logarithmic in the size of the JSON object. - - @liveexample{The example shows how `find()` is used.,find__key_type} - - @sa @ref contains(KeyT&&) const -- checks whether a key exists - - @since version 1.0.0 - */ - template iterator find(KeyT&& key) - { - auto result = end(); - - if (is_object()) - { - result.m_it.object_iterator = m_value.object->find(std::forward(key)); - } - - return result; - } - - /*! - @brief find an element in a JSON object - @copydoc find(KeyT&&) - */ - template const_iterator find(KeyT&& key) const - { - auto result = cend(); - - if (is_object()) - { - result.m_it.object_iterator = m_value.object->find(std::forward(key)); - } - - return result; - } - - /*! - @brief returns the number of occurrences of a key in a JSON object - - Returns the number of elements with key @a key. If ObjectType is the - default `std::map` type, the return value will always be `0` (@a key was - not found) or `1` (@a key was found). - - @note This method always returns `0` when executed on a JSON type that is - not an object. - - @param[in] key key value of the element to count - - @return Number of elements with key @a key. If the JSON value is not an - object, the return value will be `0`. - - @complexity Logarithmic in the size of the JSON object. - - @liveexample{The example shows how `count()` is used.,count} - - @since version 1.0.0 - */ - template size_type count(KeyT&& key) const - { - // return 0 for all nonobject types - return is_object() ? m_value.object->count(std::forward(key)) : 0; - } - - /*! - @brief check the existence of an element in a JSON object - - Check whether an element exists in a JSON object with key equivalent to - @a key. If the element is not found or the JSON value is not an object, - false is returned. - - @note This method always returns false when executed on a JSON type - that is not an object. - - @param[in] key key value to check its existence. - - @return true if an element with specified @a key exists. If no such - element with such key is found or the JSON value is not an object, - false is returned. - - @complexity Logarithmic in the size of the JSON object. - - @liveexample{The following code shows an example for `contains()`.,contains} - - @sa @ref find(KeyT&&) -- returns an iterator to an object element - - @since version 3.6.0 - */ - template bool contains(KeyT&& key) const - { - return is_object() and m_value.object->find(std::forward(key)) != m_value.object->end(); - } - - /// @} - - /////////////// - // iterators // - /////////////// - - /// @name iterators - /// @{ - - /*! - @brief returns an iterator to the first element - - Returns an iterator to the first element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return iterator to the first element - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - @liveexample{The following code shows an example for `begin()`.,begin} - - @sa @ref cbegin() -- returns a const iterator to the beginning - @sa @ref end() -- returns an iterator to the end - @sa @ref cend() -- returns a const iterator to the end - - @since version 1.0.0 - */ - iterator begin() noexcept - { - iterator result(this); - result.set_begin(); - return result; - } - - /*! - @copydoc basic_json::cbegin() - */ - const_iterator begin() const noexcept { return cbegin(); } - - /*! - @brief returns a const iterator to the first element - - Returns a const iterator to the first element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return const iterator to the first element - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).begin()`. - - @liveexample{The following code shows an example for `cbegin()`.,cbegin} - - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref end() -- returns an iterator to the end - @sa @ref cend() -- returns a const iterator to the end - - @since version 1.0.0 - */ - const_iterator cbegin() const noexcept - { - const_iterator result(this); - result.set_begin(); - return result; - } - - /*! - @brief returns an iterator to one past the last element - - Returns an iterator to one past the last element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return iterator one past the last element - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - @liveexample{The following code shows an example for `end()`.,end} - - @sa @ref cend() -- returns a const iterator to the end - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref cbegin() -- returns a const iterator to the beginning - - @since version 1.0.0 - */ - iterator end() noexcept - { - iterator result(this); - result.set_end(); - return result; - } - - /*! - @copydoc basic_json::cend() - */ - const_iterator end() const noexcept { return cend(); } - - /*! - @brief returns a const iterator to one past the last element - - Returns a const iterator to one past the last element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return const iterator one past the last element - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).end()`. - - @liveexample{The following code shows an example for `cend()`.,cend} - - @sa @ref end() -- returns an iterator to the end - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref cbegin() -- returns a const iterator to the beginning - - @since version 1.0.0 - */ - const_iterator cend() const noexcept - { - const_iterator result(this); - result.set_end(); - return result; - } - - /*! - @brief returns an iterator to the reverse-beginning - - Returns an iterator to the reverse-beginning; that is, the last element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(end())`. - - @liveexample{The following code shows an example for `rbegin()`.,rbegin} - - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref crend() -- returns a const reverse iterator to the end - - @since version 1.0.0 - */ - reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - - /*! - @copydoc basic_json::crbegin() - */ - const_reverse_iterator rbegin() const noexcept { return crbegin(); } - - /*! - @brief returns an iterator to the reverse-end - - Returns an iterator to the reverse-end; that is, one before the first - element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(begin())`. - - @liveexample{The following code shows an example for `rend()`.,rend} - - @sa @ref crend() -- returns a const reverse iterator to the end - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - - @since version 1.0.0 - */ - reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - - /*! - @copydoc basic_json::crend() - */ - const_reverse_iterator rend() const noexcept { return crend(); } - - /*! - @brief returns a const reverse iterator to the last element - - Returns a const iterator to the reverse-beginning; that is, the last - element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).rbegin()`. - - @liveexample{The following code shows an example for `crbegin()`.,crbegin} - - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref crend() -- returns a const reverse iterator to the end - - @since version 1.0.0 - */ - const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } - - /*! - @brief returns a const reverse iterator to one before the first - - Returns a const reverse iterator to the reverse-end; that is, one before - the first element. - - @image html range-rbegin-rend.svg "Illustration from cppreference.com" - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).rend()`. - - @liveexample{The following code shows an example for `crend()`.,crend} - - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - - @since version 1.0.0 - */ - const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } - -public: - /*! - @brief wrapper to access iterator member functions in range-based for - - This function allows to access @ref iterator::key() and @ref - iterator::value() during range-based for loops. In these loops, a - reference to the JSON values is returned, so there is no access to the - underlying iterator. - - For loop without iterator_wrapper: - - @code{cpp} - for (auto it = j_object.begin(); it != j_object.end(); ++it) - { - std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; - } - @endcode - - Range-based for loop without iterator proxy: - - @code{cpp} - for (auto it : j_object) - { - // "it" is of type json::reference and has no key() member - std::cout << "value: " << it << '\n'; - } - @endcode - - Range-based for loop with iterator proxy: - - @code{cpp} - for (auto it : json::iterator_wrapper(j_object)) - { - std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; - } - @endcode - - @note When iterating over an array, `key()` will return the index of the - element as string (see example). - - @param[in] ref reference to a JSON value - @return iteration proxy object wrapping @a ref with an interface to use in - range-based for loops - - @liveexample{The following code shows how the wrapper is used,iterator_wrapper} - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Constant. - - @note The name of this function is not yet final and may change in the - future. - - @deprecated This stream operator is deprecated and will be removed in - future 4.0.0 of the library. Please use @ref items() instead; - that is, replace `json::iterator_wrapper(j)` with `j.items()`. - */ - JSON_DEPRECATED - static iteration_proxy iterator_wrapper(reference ref) noexcept { return ref.items(); } - - /*! - @copydoc iterator_wrapper(reference) - */ - JSON_DEPRECATED - static iteration_proxy iterator_wrapper(const_reference ref) noexcept - { - return ref.items(); - } - - /*! - @brief helper to access iterator member functions in range-based for - - This function allows to access @ref iterator::key() and @ref - iterator::value() during range-based for loops. In these loops, a - reference to the JSON values is returned, so there is no access to the - underlying iterator. - - For loop without `items()` function: - - @code{cpp} - for (auto it = j_object.begin(); it != j_object.end(); ++it) - { - std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; - } - @endcode - - Range-based for loop without `items()` function: - - @code{cpp} - for (auto it : j_object) - { - // "it" is of type json::reference and has no key() member - std::cout << "value: " << it << '\n'; - } - @endcode - - Range-based for loop with `items()` function: - - @code{cpp} - for (auto& el : j_object.items()) - { - std::cout << "key: " << el.key() << ", value:" << el.value() << '\n'; - } - @endcode - - The `items()` function also allows to use - [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding) - (C++17): - - @code{cpp} - for (auto& [key, val] : j_object.items()) - { - std::cout << "key: " << key << ", value:" << val << '\n'; - } - @endcode - - @note When iterating over an array, `key()` will return the index of the - element as string (see example). For primitive types (e.g., numbers), - `key()` returns an empty string. - - @return iteration proxy object wrapping @a ref with an interface to use in - range-based for loops - - @liveexample{The following code shows how the function is used.,items} - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Constant. - - @since version 3.1.0, structured bindings support since 3.5.0. - */ - iteration_proxy items() noexcept { return iteration_proxy(*this); } - - /*! - @copydoc items() - */ - iteration_proxy items() const noexcept - { - return iteration_proxy(*this); - } - - /// @} - - ////////////// - // capacity // - ////////////// - - /// @name capacity - /// @{ - - /*! - @brief checks whether the container is empty. - - Checks if a JSON value has no elements (i.e. whether its @ref size is `0`). - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `true` - boolean | `false` - string | `false` - number | `false` - object | result of function `object_t::empty()` - array | result of function `array_t::empty()` - - @liveexample{The following code uses `empty()` to check if a JSON - object contains any elements.,empty} - - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their `empty()` functions have constant - complexity. - - @iterators No changes. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @note This function does not return whether a string stored as JSON value - is empty - it returns whether the JSON container itself is empty which is - false in the case of a string. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - Has the semantics of `begin() == end()`. - - @sa @ref size() -- returns the number of elements - - @since version 1.0.0 - */ - bool empty() const noexcept - { - switch (m_type) - { - case value_t::null: { - // null values are empty - return true; - } - - case value_t::array: { - // delegate call to array_t::empty() - return m_value.array->empty(); - } - - case value_t::object: { - // delegate call to object_t::empty() - return m_value.object->empty(); - } - - default: { - // all other types are nonempty - return false; - } - } - } - - /*! - @brief returns the number of elements - - Returns the number of elements in a JSON value. - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `0` - boolean | `1` - string | `1` - number | `1` - object | result of function object_t::size() - array | result of function array_t::size() - - @liveexample{The following code calls `size()` on the different value - types.,size} - - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their size() functions have constant - complexity. - - @iterators No changes. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @note This function does not return the length of a string stored as JSON - value - it returns the number of elements in the JSON value which is 1 in - the case of a string. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - Has the semantics of `std::distance(begin(), end())`. - - @sa @ref empty() -- checks whether the container is empty - @sa @ref max_size() -- returns the maximal number of elements - - @since version 1.0.0 - */ - size_type size() const noexcept - { - switch (m_type) - { - case value_t::null: { - // null values are empty - return 0; - } - - case value_t::array: { - // delegate call to array_t::size() - return m_value.array->size(); - } - - case value_t::object: { - // delegate call to object_t::size() - return m_value.object->size(); - } - - default: { - // all other types have size 1 - return 1; - } - } - } - - /*! - @brief returns the maximum possible number of elements - - Returns the maximum number of elements a JSON value is able to hold due to - system or library implementation limitations, i.e. `std::distance(begin(), - end())` for the JSON value. - - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `0` (same as `size()`) - boolean | `1` (same as `size()`) - string | `1` (same as `size()`) - number | `1` (same as `size()`) - object | result of function `object_t::max_size()` - array | result of function `array_t::max_size()` - - @liveexample{The following code calls `max_size()` on the different value - types. Note the output is implementation specific.,max_size} - - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their `max_size()` functions have constant - complexity. - - @iterators No changes. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @requirement This function helps `basic_json` satisfying the - [Container](https://en.cppreference.com/w/cpp/named_req/Container) - requirements: - - The complexity is constant. - - Has the semantics of returning `b.size()` where `b` is the largest - possible JSON value. - - @sa @ref size() -- returns the number of elements - - @since version 1.0.0 - */ - size_type max_size() const noexcept - { - switch (m_type) - { - case value_t::array: { - // delegate call to array_t::max_size() - return m_value.array->max_size(); - } - - case value_t::object: { - // delegate call to object_t::max_size() - return m_value.object->max_size(); - } - - default: { - // all other types have max_size() == size() - return size(); - } - } - } - - /// @} - - /////////////// - // modifiers // - /////////////// - - /// @name modifiers - /// @{ - - /*! - @brief clears the contents - - Clears the content of a JSON value and resets it to the default value as - if @ref basic_json(value_t) would have been called with the current value - type from @ref type(): - - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` - - @post Has the same effect as calling - @code {.cpp} - *this = basic_json(type()); - @endcode - - @liveexample{The example below shows the effect of `clear()` to different - JSON types.,clear} - - @complexity Linear in the size of the JSON value. - - @iterators All iterators, pointers and references related to this container - are invalidated. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @sa @ref basic_json(value_t) -- constructor that creates an object with the - same value than calling `clear()` - - @since version 1.0.0 - */ - void clear() noexcept - { - switch (m_type) - { - case value_t::number_integer: { - m_value.number_integer = 0; - break; - } - - case value_t::number_unsigned: { - m_value.number_unsigned = 0; - break; - } - - case value_t::number_float: { - m_value.number_float = 0.0; - break; - } - - case value_t::boolean: { - m_value.boolean = false; - break; - } - - case value_t::string: { - m_value.string->clear(); - break; - } - - case value_t::array: { - m_value.array->clear(); - break; - } - - case value_t::object: { - m_value.object->clear(); - break; - } - - default: - break; - } - } - - /*! - @brief add an object to an array - - Appends the given element @a val to the end of the JSON value. If the - function is called on a JSON null value, an empty array is created before - appending @a val. - - @param[in] val the value to add to the JSON array - - @throw type_error.308 when called on a type other than JSON array or - null; example: `"cannot use push_back() with number"` - - @complexity Amortized constant. - - @liveexample{The example shows how `push_back()` and `+=` can be used to - add elements to a JSON array. Note how the `null` value was silently - converted to a JSON array.,push_back} - - @since version 1.0.0 - */ - void push_back(basic_json&& val) - { - // push_back only works for null objects or arrays - if (JSON_UNLIKELY(not(is_null() or is_array()))) - { - JSON_THROW( - type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); - } - - // transform null object into an array - if (is_null()) - { - m_type = value_t::array; - m_value = value_t::array; - assert_invariant(); - } - - // add element to array (move semantics) - m_value.array->push_back(std::move(val)); - // invalidate object: mark it null so we do not call the destructor - // cppcheck-suppress accessMoved - val.m_type = value_t::null; - } - - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ - reference operator+=(basic_json&& val) - { - push_back(std::move(val)); - return *this; - } - - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ - void push_back(const basic_json& val) - { - // push_back only works for null objects or arrays - if (JSON_UNLIKELY(not(is_null() or is_array()))) - { - JSON_THROW( - type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); - } - - // transform null object into an array - if (is_null()) - { - m_type = value_t::array; - m_value = value_t::array; - assert_invariant(); - } - - // add element to array - m_value.array->push_back(val); - } - - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ - reference operator+=(const basic_json& val) - { - push_back(val); - return *this; - } - - /*! - @brief add an object to an object - - Inserts the given element @a val to the JSON object. If the function is - called on a JSON null value, an empty object is created before inserting - @a val. - - @param[in] val the value to add to the JSON object - - @throw type_error.308 when called on a type other than JSON object or - null; example: `"cannot use push_back() with number"` - - @complexity Logarithmic in the size of the container, O(log(`size()`)). - - @liveexample{The example shows how `push_back()` and `+=` can be used to - add elements to a JSON object. Note how the `null` value was silently - converted to a JSON object.,push_back__object_t__value} - - @since version 1.0.0 - */ - void push_back(const typename object_t::value_type& val) - { - // push_back only works for null objects or objects - if (JSON_UNLIKELY(not(is_null() or is_object()))) - { - JSON_THROW( - type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); - } - - // transform null object into an object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); - } - - // add element to array - m_value.object->insert(val); - } - - /*! - @brief add an object to an object - @copydoc push_back(const typename object_t::value_type&) - */ - reference operator+=(const typename object_t::value_type& val) - { - push_back(val); - return *this; - } - - /*! - @brief add an object to an object - - This function allows to use `push_back` with an initializer list. In case - - 1. the current value is an object, - 2. the initializer list @a init contains only two elements, and - 3. the first element of @a init is a string, - - @a init is converted into an object element and added using - @ref push_back(const typename object_t::value_type&). Otherwise, @a init - is converted to a JSON value and added using @ref push_back(basic_json&&). - - @param[in] init an initializer list - - @complexity Linear in the size of the initializer list @a init. - - @note This function is required to resolve an ambiguous overload error, - because pairs like `{"key", "value"}` can be both interpreted as - `object_t::value_type` or `std::initializer_list`, see - https://github.com/nlohmann/json/issues/235 for more information. - - @liveexample{The example shows how initializer lists are treated as - objects when possible.,push_back__initializer_list} - */ - void push_back(initializer_list_t init) - { - if (is_object() and init.size() == 2 and (*init.begin())->is_string()) - { - basic_json&& key = init.begin()->moved_or_copied(); - push_back(typename object_t::value_type( - std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); - } - else - { - push_back(basic_json(init)); - } - } - - /*! - @brief add an object to an object - @copydoc push_back(initializer_list_t) - */ - reference operator+=(initializer_list_t init) - { - push_back(init); - return *this; - } - - /*! - @brief add an object to an array - - Creates a JSON value from the passed parameters @a args to the end of the - JSON value. If the function is called on a JSON null value, an empty array - is created before appending the value created from @a args. - - @param[in] args arguments to forward to a constructor of @ref basic_json - @tparam Args compatible types to create a @ref basic_json object - - @throw type_error.311 when called on a type other than JSON array or - null; example: `"cannot use emplace_back() with number"` - - @complexity Amortized constant. - - @liveexample{The example shows how `push_back()` can be used to add - elements to a JSON array. Note how the `null` value was silently converted - to a JSON array.,emplace_back} - - @since version 2.0.8 - */ - template void emplace_back(Args&&... args) - { - // emplace_back only works for null objects or arrays - if (JSON_UNLIKELY(not(is_null() or is_array()))) - { - JSON_THROW( - type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()))); - } - - // transform null object into an array - if (is_null()) - { - m_type = value_t::array; - m_value = value_t::array; - assert_invariant(); - } - - // add element to array (perfect forwarding) - m_value.array->emplace_back(std::forward(args)...); - } - - /*! - @brief add an object to an object if key does not exist - - Inserts a new element into a JSON object constructed in-place with the - given @a args if there is no element with the key in the container. If the - function is called on a JSON null value, an empty object is created before - appending the value created from @a args. - - @param[in] args arguments to forward to a constructor of @ref basic_json - @tparam Args compatible types to create a @ref basic_json object - - @return a pair consisting of an iterator to the inserted element, or the - already-existing element if no insertion happened, and a bool - denoting whether the insertion took place. - - @throw type_error.311 when called on a type other than JSON object or - null; example: `"cannot use emplace() with number"` - - @complexity Logarithmic in the size of the container, O(log(`size()`)). - - @liveexample{The example shows how `emplace()` can be used to add elements - to a JSON object. Note how the `null` value was silently converted to a - JSON object. Further note how no value is added if there was already one - value stored with the same key.,emplace} - - @since version 2.0.8 - */ - template std::pair emplace(Args&&... args) - { - // emplace only works for null objects or arrays - if (JSON_UNLIKELY(not(is_null() or is_object()))) - { - JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()))); - } - - // transform null object into an object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); - } - - // add element to array (perfect forwarding) - auto res = m_value.object->emplace(std::forward(args)...); - // create result iterator and set iterator to the result of emplace - auto it = begin(); - it.m_it.object_iterator = res.first; - - // return pair of iterator and boolean - return {it, res.second}; - } - - /// Helper for insertion of an iterator - /// @note: This uses std::distance to support GCC 4.8, - /// see https://github.com/nlohmann/json/pull/1257 - template iterator insert_iterator(const_iterator pos, Args&&... args) - { - iterator result(this); - assert(m_value.array != nullptr); - - auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); - m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); - result.m_it.array_iterator = m_value.array->begin() + insert_pos; - - // This could have been written as: - // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); - // but the return value of insert is missing in GCC 4.8, so it is written this way instead. - - return result; - } - - /*! - @brief inserts element - - Inserts element @a val before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] val element to insert - @return iterator pointing to the inserted @a val. - - @throw type_error.309 if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` - - @complexity Constant plus linear in the distance between @a pos and end of - the container. - - @liveexample{The example shows how `insert()` is used.,insert} - - @since version 1.0.0 - */ - iterator insert(const_iterator pos, const basic_json& val) - { - // insert only works for arrays - if (JSON_LIKELY(is_array())) - { - // check if iterator pos fits to this JSON value - if (JSON_UNLIKELY(pos.m_object != this)) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); - } - - // insert to array and return iterator - return insert_iterator(pos, val); - } - - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); - } - - /*! - @brief inserts element - @copydoc insert(const_iterator, const basic_json&) - */ - iterator insert(const_iterator pos, basic_json&& val) { return insert(pos, val); } - - /*! - @brief inserts elements - - Inserts @a cnt copies of @a val before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] cnt number of copies of @a val to insert - @param[in] val element to insert - @return iterator pointing to the first element inserted, or @a pos if - `cnt==0` - - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` - - @complexity Linear in @a cnt plus linear in the distance between @a pos - and end of the container. - - @liveexample{The example shows how `insert()` is used.,insert__count} - - @since version 1.0.0 - */ - iterator insert(const_iterator pos, size_type cnt, const basic_json& val) - { - // insert only works for arrays - if (JSON_LIKELY(is_array())) - { - // check if iterator pos fits to this JSON value - if (JSON_UNLIKELY(pos.m_object != this)) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); - } - - // insert to array and return iterator - return insert_iterator(pos, cnt, val); - } - - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); - } - - /*! - @brief inserts elements - - Inserts elements from range `[first, last)` before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert - - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` - @throw invalid_iterator.210 if @a first and @a last do not belong to the - same JSON value; example: `"iterators do not fit"` - @throw invalid_iterator.211 if @a first or @a last are iterators into - container for which insert is called; example: `"passed iterators may not - belong to container"` - - @return iterator pointing to the first element inserted, or @a pos if - `first==last` - - @complexity Linear in `std::distance(first, last)` plus linear in the - distance between @a pos and end of the container. - - @liveexample{The example shows how `insert()` is used.,insert__range} - - @since version 1.0.0 - */ - iterator insert(const_iterator pos, const_iterator first, const_iterator last) - { - // insert only works for arrays - if (JSON_UNLIKELY(not is_array())) - { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); - } - - // check if iterator pos fits to this JSON value - if (JSON_UNLIKELY(pos.m_object != this)) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); - } - - // check if range iterators belong to the same JSON object - if (JSON_UNLIKELY(first.m_object != last.m_object)) - { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); - } - - if (JSON_UNLIKELY(first.m_object == this)) - { - JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); - } - - // insert to array and return iterator - return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); - } - - /*! - @brief inserts elements - - Inserts elements from initializer list @a ilist before iterator @a pos. - - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] ilist initializer list to insert the values from - - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` - - @return iterator pointing to the first element inserted, or @a pos if - `ilist` is empty - - @complexity Linear in `ilist.size()` plus linear in the distance between - @a pos and end of the container. - - @liveexample{The example shows how `insert()` is used.,insert__ilist} - - @since version 1.0.0 - */ - iterator insert(const_iterator pos, initializer_list_t ilist) - { - // insert only works for arrays - if (JSON_UNLIKELY(not is_array())) - { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); - } - - // check if iterator pos fits to this JSON value - if (JSON_UNLIKELY(pos.m_object != this)) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); - } - - // insert to array and return iterator - return insert_iterator(pos, ilist.begin(), ilist.end()); - } - - /*! - @brief inserts elements - - Inserts elements from range `[first, last)`. - - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert - - @throw type_error.309 if called on JSON values other than objects; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if iterator @a first or @a last does does not - point to an object; example: `"iterators first and last must point to - objects"` - @throw invalid_iterator.210 if @a first and @a last do not belong to the - same JSON value; example: `"iterators do not fit"` - - @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number - of elements to insert. - - @liveexample{The example shows how `insert()` is used.,insert__range_object} - - @since version 3.0.0 - */ - void insert(const_iterator first, const_iterator last) - { - // insert only works for objects - if (JSON_UNLIKELY(not is_object())) - { - JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); - } - - // check if range iterators belong to the same JSON object - if (JSON_UNLIKELY(first.m_object != last.m_object)) - { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); - } - - // passed iterators must belong to objects - if (JSON_UNLIKELY(not first.m_object->is_object())) - { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); - } - - m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); - } - - /*! - @brief updates a JSON object from another object, overwriting existing keys - - Inserts all values from JSON object @a j and overwrites existing keys. - - @param[in] j JSON object to read values from - - @throw type_error.312 if called on JSON values other than objects; example: - `"cannot use update() with string"` - - @complexity O(N*log(size() + N)), where N is the number of elements to - insert. - - @liveexample{The example shows how `update()` is used.,update} - - @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update - - @since version 3.0.0 - */ - void update(const_reference j) - { - // implicitly convert null value to an empty object - if (is_null()) - { - m_type = value_t::object; - m_value.object = create(); - assert_invariant(); - } - - if (JSON_UNLIKELY(not is_object())) - { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); - } - if (JSON_UNLIKELY(not j.is_object())) - { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()))); - } - - for (auto it = j.cbegin(); it != j.cend(); ++it) - { - m_value.object->operator[](it.key()) = it.value(); - } - } - - /*! - @brief updates a JSON object from another object, overwriting existing keys - - Inserts all values from from range `[first, last)` and overwrites existing - keys. - - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert - - @throw type_error.312 if called on JSON values other than objects; example: - `"cannot use update() with string"` - @throw invalid_iterator.202 if iterator @a first or @a last does does not - point to an object; example: `"iterators first and last must point to - objects"` - @throw invalid_iterator.210 if @a first and @a last do not belong to the - same JSON value; example: `"iterators do not fit"` - - @complexity O(N*log(size() + N)), where N is the number of elements to - insert. - - @liveexample{The example shows how `update()` is used__range.,update} - - @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update - - @since version 3.0.0 - */ - void update(const_iterator first, const_iterator last) - { - // implicitly convert null value to an empty object - if (is_null()) - { - m_type = value_t::object; - m_value.object = create(); - assert_invariant(); - } - - if (JSON_UNLIKELY(not is_object())) - { - JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); - } - - // check if range iterators belong to the same JSON object - if (JSON_UNLIKELY(first.m_object != last.m_object)) - { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); - } - - // passed iterators must belong to objects - if (JSON_UNLIKELY(not first.m_object->is_object() or not last.m_object->is_object())) - { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); - } - - for (auto it = first; it != last; ++it) - { - m_value.object->operator[](it.key()) = it.value(); - } - } - - /*! - @brief exchanges the values - - Exchanges the contents of the JSON value 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 JSON value to exchange the contents with - - @complexity Constant. - - @liveexample{The example below shows how JSON values can be swapped with - `swap()`.,swap__reference} - - @since version 1.0.0 - */ - void swap(reference other) noexcept( - std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable< - value_t>::value and std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value) - { - std::swap(m_type, other.m_type); - std::swap(m_value, other.m_value); - assert_invariant(); - } - - /*! - @brief exchanges the values - - Exchanges the contents of a JSON array with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other array to exchange the contents with - - @throw type_error.310 when JSON value is not an array; example: `"cannot - use swap() with string"` - - @complexity Constant. - - @liveexample{The example below shows how arrays can be swapped with - `swap()`.,swap__array_t} - - @since version 1.0.0 - */ - void swap(array_t& other) - { - // swap only works for arrays - if (JSON_LIKELY(is_array())) - { - std::swap(*(m_value.array), other); - } - else - { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); - } - } - - /*! - @brief exchanges the values - - Exchanges the contents of a JSON object 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 object to exchange the contents with - - @throw type_error.310 when JSON value is not an object; example: - `"cannot use swap() with string"` - - @complexity Constant. - - @liveexample{The example below shows how objects can be swapped with - `swap()`.,swap__object_t} - - @since version 1.0.0 - */ - void swap(object_t& other) - { - // swap only works for objects - if (JSON_LIKELY(is_object())) - { - std::swap(*(m_value.object), other); - } - else - { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); - } - } - - /*! - @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 string 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__string_t} - - @since version 1.0.0 - */ - void swap(string_t& other) - { - // swap only works for strings - if (JSON_LIKELY(is_string())) - { - std::swap(*(m_value.string), other); - } - else - { - JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); - } - } - - /// @} - -public: - ////////////////////////////////////////// - // lexicographical comparison operators // - ////////////////////////////////////////// - - /// @name lexicographical comparison operators - /// @{ - - /*! - @brief comparison: equal - - Compares two JSON values for equality according to the following rules: - - Two JSON values are equal if (1) they are from the same type and (2) - their stored values are the same according to their respective - `operator==`. - - Integer and floating-point numbers are automatically converted before - comparison. Note than two NaN values are always treated as unequal. - - Two JSON null values are equal. - - @note Floating-point inside JSON values numbers are compared with - `json::number_float_t::operator==` which is `double::operator==` by - default. To compare floating-point while respecting an epsilon, an alternative - [comparison - function](https://github.com/mariokonrad/marnav/blob/master/src/marnav/math/floatingpoint.hpp#L34-#L39) - could be used, for instance - @code {.cpp} - template::value, - T>::type> inline bool is_same(T a, T b, T epsilon = std::numeric_limits::epsilon()) noexcept - { - return std::abs(a - b) <= epsilon; - } - @endcode - - @note NaN values never compare equal to themselves or to other NaN values. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are equal - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @complexity Linear. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__equal} - - @since version 1.0.0 - */ - friend bool operator==(const_reference lhs, const_reference rhs) noexcept - { - const auto lhs_type = lhs.type(); - const auto rhs_type = rhs.type(); - - if (lhs_type == rhs_type) - { - switch (lhs_type) - { - case value_t::array: - return *lhs.m_value.array == *rhs.m_value.array; - - case value_t::object: - return *lhs.m_value.object == *rhs.m_value.object; - - case value_t::null: - return true; - - case value_t::string: - return *lhs.m_value.string == *rhs.m_value.string; - - case value_t::boolean: - return lhs.m_value.boolean == rhs.m_value.boolean; - - case value_t::number_integer: - return lhs.m_value.number_integer == rhs.m_value.number_integer; - - case value_t::number_unsigned: - return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; - - case value_t::number_float: - return lhs.m_value.number_float == rhs.m_value.number_float; - - default: - return false; - } - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) - { - return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) - { - return static_cast(lhs.m_value.number_unsigned) - == rhs.m_value.number_integer; - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_integer - == static_cast(rhs.m_value.number_unsigned); - } - - return false; - } - - /*! - @brief comparison: equal - @copydoc operator==(const_reference, const_reference) - */ - template < - typename ScalarType, - typename std::enable_if::value, int>::type = 0> - friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept - { - return lhs == basic_json(rhs); - } - - /*! - @brief comparison: equal - @copydoc operator==(const_reference, const_reference) - */ - template < - typename ScalarType, - typename std::enable_if::value, int>::type = 0> - friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept - { - return basic_json(lhs) == rhs; - } - - /*! - @brief comparison: not equal - - Compares two JSON values for inequality by calculating `not (lhs == rhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are not equal - - @complexity Linear. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__notequal} - - @since version 1.0.0 - */ - friend bool operator!=(const_reference lhs, const_reference rhs) noexcept - { - return not(lhs == rhs); - } - - /*! - @brief comparison: not equal - @copydoc operator!=(const_reference, const_reference) - */ - template < - typename ScalarType, - typename std::enable_if::value, int>::type = 0> - friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept - { - return lhs != basic_json(rhs); - } - - /*! - @brief comparison: not equal - @copydoc operator!=(const_reference, const_reference) - */ - template < - typename ScalarType, - typename std::enable_if::value, int>::type = 0> - friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept - { - return basic_json(lhs) != rhs; - } - - /*! - @brief comparison: less than - - Compares whether one JSON value @a lhs is less than another JSON value @a - rhs according to the following rules: - - If @a lhs and @a rhs have the same type, the values are compared using - the default `<` operator. - - Integer and floating-point numbers are automatically converted before - comparison - - In case @a lhs and @a rhs have different types, the values are ignored - and the order of the types is considered, see - @ref operator<(const value_t, const value_t). - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than @a rhs - - @complexity Linear. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__less} - - @since version 1.0.0 - */ - friend bool operator<(const_reference lhs, const_reference rhs) noexcept - { - const auto lhs_type = lhs.type(); - const auto rhs_type = rhs.type(); - - if (lhs_type == rhs_type) - { - switch (lhs_type) - { - case value_t::array: - return *lhs.m_value.array < *rhs.m_value.array; - - case value_t::object: - return *lhs.m_value.object < *rhs.m_value.object; - - case value_t::null: - return false; - - case value_t::string: - return *lhs.m_value.string < *rhs.m_value.string; - - case value_t::boolean: - return lhs.m_value.boolean < rhs.m_value.boolean; - - case value_t::number_integer: - return lhs.m_value.number_integer < rhs.m_value.number_integer; - - case value_t::number_unsigned: - return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; - - case value_t::number_float: - return lhs.m_value.number_float < rhs.m_value.number_float; - - default: - return false; - } - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) - { - return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_integer - < static_cast(rhs.m_value.number_unsigned); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) - { - return static_cast(lhs.m_value.number_unsigned) - < rhs.m_value.number_integer; - } - - // We only reach this line if we cannot compare values. In that case, - // we compare types. Note we have to call the operator explicitly, - // because MSVC has problems otherwise. - return operator<(lhs_type, rhs_type); - } - - /*! - @brief comparison: less than - @copydoc operator<(const_reference, const_reference) - */ - template < - typename ScalarType, - typename std::enable_if::value, int>::type = 0> - friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept - { - return lhs < basic_json(rhs); - } - - /*! - @brief comparison: less than - @copydoc operator<(const_reference, const_reference) - */ - template < - typename ScalarType, - typename std::enable_if::value, int>::type = 0> - friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept - { - return basic_json(lhs) < rhs; - } - - /*! - @brief comparison: less than or equal - - Compares whether one JSON value @a lhs is less than or equal to another - JSON value by calculating `not (rhs < lhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than or equal to @a rhs - - @complexity Linear. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__greater} - - @since version 1.0.0 - */ - friend bool operator<=(const_reference lhs, const_reference rhs) noexcept - { - return not(rhs < lhs); - } - - /*! - @brief comparison: less than or equal - @copydoc operator<=(const_reference, const_reference) - */ - template < - typename ScalarType, - typename std::enable_if::value, int>::type = 0> - friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept - { - return lhs <= basic_json(rhs); - } - - /*! - @brief comparison: less than or equal - @copydoc operator<=(const_reference, const_reference) - */ - template < - typename ScalarType, - typename std::enable_if::value, int>::type = 0> - friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept - { - return basic_json(lhs) <= rhs; - } - - /*! - @brief comparison: greater than - - Compares whether one JSON value @a lhs is greater than another - JSON value by calculating `not (lhs <= rhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than to @a rhs - - @complexity Linear. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__lessequal} - - @since version 1.0.0 - */ - friend bool operator>(const_reference lhs, const_reference rhs) noexcept - { - return not(lhs <= rhs); - } - - /*! - @brief comparison: greater than - @copydoc operator>(const_reference, const_reference) - */ - template < - typename ScalarType, - typename std::enable_if::value, int>::type = 0> - friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept - { - return lhs > basic_json(rhs); - } - - /*! - @brief comparison: greater than - @copydoc operator>(const_reference, const_reference) - */ - template < - typename ScalarType, - typename std::enable_if::value, int>::type = 0> - friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept - { - return basic_json(lhs) > rhs; - } - - /*! - @brief comparison: greater than or equal - - Compares whether one JSON value @a lhs is greater than or equal to another - JSON value by calculating `not (lhs < rhs)`. - - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than or equal to @a rhs - - @complexity Linear. - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @liveexample{The example demonstrates comparing several JSON - types.,operator__greaterequal} - - @since version 1.0.0 - */ - friend bool operator>=(const_reference lhs, const_reference rhs) noexcept - { - return not(lhs < rhs); - } - - /*! - @brief comparison: greater than or equal - @copydoc operator>=(const_reference, const_reference) - */ - template < - typename ScalarType, - typename std::enable_if::value, int>::type = 0> - friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept - { - return lhs >= basic_json(rhs); - } - - /*! - @brief comparison: greater than or equal - @copydoc operator>=(const_reference, const_reference) - */ - template < - typename ScalarType, - typename std::enable_if::value, int>::type = 0> - friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept - { - return basic_json(lhs) >= rhs; - } - - /// @} - - /////////////////// - // serialization // - /////////////////// - - /// @name serialization - /// @{ - - /*! - @brief serialize to stream - - Serialize the given JSON value @a j to the output stream @a o. The JSON - value will be serialized using the @ref dump member function. - - - The indentation of the output can be controlled with the member variable - `width` of the output stream @a o. For instance, using the manipulator - `std::setw(4)` on @a o sets the indentation level to `4` and the - serialization result is the same as calling `dump(4)`. - - - The indentation character can be controlled with the member variable - `fill` of the output stream @a o. For instance, the manipulator - `std::setfill('\\t')` sets indentation to use a tab character rather than - the default space character. - - @param[in,out] o stream to serialize to - @param[in] j JSON value to serialize - - @return the stream @a o - - @throw type_error.316 if a string stored inside the JSON value is not - UTF-8 encoded - - @complexity Linear. - - @liveexample{The example below shows the serialization with different - parameters to `width` to adjust the indentation level.,operator_serialize} - - @since version 1.0.0; indentation character added in version 3.0.0 - */ - friend std::ostream& operator<<(std::ostream& o, const basic_json& j) - { - // read width member and use it as indentation parameter if nonzero - const bool pretty_print = o.width() > 0; - const auto indentation = pretty_print ? o.width() : 0; - - // reset width to 0 for subsequent calls to this stream - o.width(0); - - // do the actual serialization - serializer s(detail::output_adapter(o), o.fill()); - s.dump(j, pretty_print, false, static_cast(indentation)); - return o; - } - - /*! - @brief serialize to stream - @deprecated This stream operator is deprecated and will be removed in - future 4.0.0 of the library. Please use - @ref operator<<(std::ostream&, const basic_json&) - instead; that is, replace calls like `j >> o;` with `o << j;`. - @since version 1.0.0; deprecated since version 3.0.0 - */ - JSON_DEPRECATED - friend std::ostream& operator>>(const basic_json& j, std::ostream& o) { return o << j; } - - /// @} - - ///////////////////// - // deserialization // - ///////////////////// - - /// @name deserialization - /// @{ - - /*! - @brief deserialize from a compatible input - - This function reads from a compatible input. Examples are: - - an array of 1-byte values - - strings with character/literal type with size of 1 byte - - input streams - - container with contiguous storage of 1-byte values. Compatible container - types include `std::vector`, `std::string`, `std::array`, - `std::valarray`, and `std::initializer_list`. Furthermore, C-style - arrays can be used with `std::begin()`/`std::end()`. User-defined - containers can be used as long as they implement random-access iterators - and a contiguous storage. - - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - assertion.** - - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with a noncompliant container and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. - - @param[in] i input to read from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - @param[in] allow_exceptions whether to throw exceptions in case of a - parse error (optional, true by default) - - @return deserialized JSON value; in case of a parse error and - @a allow_exceptions set to `false`, the return value will be - value_t::discarded. - - @throw parse_error.101 if a parse error occurs; example: `""unexpected end - of input; expected string literal""` - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the `parse()` function reading - from an array.,parse__array__parser_callback_t} - - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__string__parser_callback_t} - - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__istream__parser_callback_t} - - @liveexample{The example below demonstrates the `parse()` function reading - from a contiguous container.,parse__contiguouscontainer__parser_callback_t} - - @since version 2.0.3 (contiguous containers) - */ - JSON_NODISCARD - static basic_json parse( - detail::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); - return result; - } - - static bool accept(detail::input_adapter&& i) { return parser(i).accept(true); } - - /*! - @brief generate SAX events - - The SAX event lister must follow the interface of @ref json_sax. - - This function reads from a compatible input. Examples are: - - an array of 1-byte values - - strings with character/literal type with size of 1 byte - - input streams - - container with contiguous storage of 1-byte values. Compatible container - types include `std::vector`, `std::string`, `std::array`, - `std::valarray`, and `std::initializer_list`. Furthermore, C-style - arrays can be used with `std::begin()`/`std::end()`. User-defined - containers can be used as long as they implement random-access iterators - and a contiguous storage. - - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - assertion.** - - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with a noncompliant container and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. - - @param[in] i input to read from - @param[in,out] sax SAX event listener - @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) - @param[in] strict whether the input has to be consumed completely - - @return return value of the last processed SAX event - - @throw parse_error.101 if a parse error occurs; example: `""unexpected end - of input; expected string literal""` - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the SAX consumer @a sax has - a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the `sax_parse()` function - reading from string and processing the events with a user-defined SAX - event consumer.,sax_parse} - - @since version 3.2.0 - */ - template - static bool sax_parse( - detail::input_adapter&& i, - SAX* sax, - input_format_t format = input_format_t::json, - const bool strict = true) - { - assert(sax); - 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); - } - - /*! - @brief deserialize from an iterator range with contiguous storage - - This function reads from an iterator range of a container with contiguous - storage of 1-byte values. Compatible container types include - `std::vector`, `std::string`, `std::array`, `std::valarray`, and - `std::initializer_list`. Furthermore, C-style arrays can be used with - `std::begin()`/`std::end()`. User-defined containers can be used as long - as they implement random-access iterators and a contiguous storage. - - @pre The iterator range is contiguous. Violating this precondition yields - undefined behavior. **This precondition is enforced with an assertion.** - @pre Each element in the range has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with noncompliant iterators and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. - - @tparam IteratorType iterator of container with contiguous storage - @param[in] first begin of the range to parse (included) - @param[in] last end of the range to parse (excluded) - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - @param[in] allow_exceptions whether to throw exceptions in case of a - parse error (optional, true by default) - - @return deserialized JSON value; in case of a parse error and - @a allow_exceptions set to `false`, the return value will be - value_t::discarded. - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below demonstrates the `parse()` function reading - from an iterator range.,parse__iteratortype__parser_callback_t} - - @since version 2.0.3 - */ - template < - class IteratorType, - typename std::enable_if< - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits::iterator_category>::value, - int>::type - = 0> - static basic_json parse( - IteratorType first, - IteratorType last, - const parser_callback_t cb = nullptr, - const bool allow_exceptions = true) - { - basic_json result; - parser(detail::input_adapter(first, last), cb, allow_exceptions).parse(true, result); - return result; - } - - template < - class IteratorType, - typename std::enable_if< - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits::iterator_category>::value, - int>::type - = 0> - static bool accept(IteratorType first, IteratorType last) - { - return parser(detail::input_adapter(first, last)).accept(true); - } - - template < - class IteratorType, - class SAX, - typename std::enable_if< - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits::iterator_category>::value, - int>::type - = 0> - static bool sax_parse(IteratorType first, IteratorType last, SAX* sax) - { - return parser(detail::input_adapter(first, last)).sax_parse(sax); - } - - /*! - @brief deserialize from stream - @deprecated This stream operator is deprecated and will be removed in - version 4.0.0 of the library. Please use - @ref operator>>(std::istream&, basic_json&) - instead; that is, replace calls like `j << i;` with `i >> j;`. - @since version 1.0.0; deprecated since version 3.0.0 - */ - JSON_DEPRECATED - friend std::istream& operator<<(basic_json& j, std::istream& i) { return operator>>(i, j); } - - /*! - @brief deserialize from stream - - Deserializes an input stream to a JSON value. - - @param[in,out] i input stream to read a serialized JSON value from - @param[in,out] j JSON value to write the deserialized input to - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. - - @note A UTF-8 byte order mark is silently ignored. - - @liveexample{The example below shows how a JSON value is constructed by - reading a serialization from a stream.,operator_deserialize} - - @sa parse(std::istream&, const parser_callback_t) for a variant with a - parser callback function to filter values while parsing - - @since version 1.0.0 - */ - friend std::istream& operator>>(std::istream& i, basic_json& j) - { - parser(detail::input_adapter(i)).parse(false, j); - return i; - } - - /// @} - - /////////////////////////// - // convenience functions // - /////////////////////////// - - /*! - @brief return the type as string - - Returns the type name as string to be used in error messages - usually to - indicate that a function was called on a wrong JSON type. - - @return a string representation of a the @a m_type member: - Value type | return value - ----------- | ------------- - null | `"null"` - boolean | `"boolean"` - string | `"string"` - number | `"number"` (for all number types) - object | `"object"` - array | `"array"` - discarded | `"discarded"` - - @exceptionsafety No-throw guarantee: this function never throws exceptions. - - @complexity Constant. - - @liveexample{The following code exemplifies `type_name()` for all JSON - types.,type_name} - - @sa @ref type() -- return the type of the JSON value - @sa @ref operator value_t() -- return the type of the JSON value (implicit) - - @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept` - since 3.0.0 - */ - const char* type_name() const noexcept - { - { - switch (m_type) - { - case value_t::null: - return "null"; - case value_t::object: - return "object"; - case value_t::array: - return "array"; - case value_t::string: - return "string"; - case value_t::boolean: - return "boolean"; - case value_t::discarded: - return "discarded"; - default: - return "number"; - } - } - } - -private: - ////////////////////// - // member variables // - ////////////////////// - - /// the type of the current element - value_t m_type = value_t::null; - - /// the value of the current element - json_value m_value = {}; - - ////////////////////////////////////////// - // binary serialization/deserialization // - ////////////////////////////////////////// - - /// @name binary serialization/deserialization support - /// @{ - -public: - /*! - @brief create a CBOR serialization of a given JSON value - - Serializes a given JSON value @a j to a byte vector using the CBOR (Concise - Binary Object Representation) serialization format. CBOR is a binary - serialization format which aims to be more compact than JSON itself, yet - more efficient to parse. - - The library uses the following mapping from JSON values types to - CBOR types according to the CBOR specification (RFC 7049): - - JSON value type | value/range | CBOR type | first byte - --------------- | ------------------------------------------ | ---------------------------------- - | --------------- null | `null` | Null | 0xF6 - boolean | `true` | True | 0xF5 boolean | - `false` | False | 0xF4 - number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) - | 0x3B number_integer | -2147483648..-32769 | Negative integer (4 bytes - follow) | 0x3A number_integer | -32768..-129 | Negative integer (2 - bytes follow) | 0x39 number_integer | -128..-25 | Negative - integer (1 byte follow) | 0x38 number_integer | -24..-1 | - Negative integer | 0x20..0x37 number_integer | 0..23 | Integer | 0x00..0x17 - number_integer | 24..255 | Unsigned integer (1 byte follow) | - 0x18 number_integer | 256..65535 | Unsigned integer (2 bytes - follow) | 0x19 number_integer | 65536..4294967295 | Unsigned integer (4 - bytes follow) | 0x1A number_integer | 4294967296..18446744073709551615 | Unsigned - integer (8 bytes follow) | 0x1B number_unsigned | 0..23 | - Integer | 0x00..0x17 number_unsigned | 24..255 | Unsigned integer (1 - byte follow) | 0x18 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 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 string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) - | 0x7A string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes - follow) | 0x7B array | *size*: 0..23 | array | - 0x80..0x97 array | *size*: 23..255 | array (1 byte follow) | - 0x98 array | *size*: 256..65535 | array (2 bytes follow) | 0x99 - array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9A array - | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9B object | - *size*: 0..23 | map | 0xA0..0xB7 - object | *size*: 23..255 | map (1 byte follow) | 0xB8 object - | *size*: 256..65535 | map (2 bytes follow) | 0xB9 object | - *size*: 65536..4294967295 | map (4 bytes follow) | 0xBA object | - *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xBB - - @note The mapping is **complete** in the sense that any JSON value type - can be converted to a CBOR value. - - @note If NaN or Infinity are stored inside a JSON number, they are - serialized properly. This behavior differs from the @ref dump() - function which serializes NaN or Infinity to `null`. - - @note The following CBOR types are not used in the conversion: - - byte strings (0x40..0x5F) - - UTF-8 strings terminated by "break" (0x7F) - - arrays terminated by "break" (0x9F) - - maps terminated by "break" (0xBF) - - date/time (0xC0..0xC1) - - bignum (0xC2..0xC3) - - decimal fraction (0xC4) - - bigfloat (0xC5) - - tagged items (0xC6..0xD4, 0xD8..0xDB) - - expected conversions (0xD5..0xD7) - - simple values (0xE0..0xF3, 0xF8) - - undefined (0xF7) - - half and single-precision floats (0xF9-0xFA) - - break (0xFF) - - @param[in] j JSON value to serialize - @return MessagePack serialization as byte vector - - @complexity Linear in the size of the JSON value @a j. - - @liveexample{The example shows the serialization of a JSON value to a byte - vector in CBOR format.,to_cbor} - - @sa http://cbor.io - @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the - analogous deserialization - @sa @ref to_msgpack(const basic_json&) for the related MessagePack format - @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the - related UBJSON format - - @since version 2.0.9 - */ - static std::vector to_cbor(const basic_json& j) - { - std::vector result; - to_cbor(j, result); - return result; - } - - static void to_cbor(const basic_json& j, detail::output_adapter o) - { - binary_writer(o).write_cbor(j); - } - - static void to_cbor(const basic_json& j, detail::output_adapter o) - { - binary_writer(o).write_cbor(j); - } - - /*! - @brief create a MessagePack serialization of a given JSON value - - Serializes a given JSON value @a j to a byte vector using the MessagePack - serialization format. MessagePack is a binary serialization format which - aims to be more compact than JSON itself, yet more efficient to parse. - - The library uses the following mapping from JSON values types to - MessagePack types according to the MessagePack specification: - - JSON value type | value/range | MessagePack type | first byte - --------------- | --------------------------------- | ---------------- | ---------- - null | `null` | nil | 0xC0 - boolean | `true` | true | 0xC3 - boolean | `false` | false | 0xC2 - number_integer | -9223372036854775808..-2147483649 | int64 | 0xD3 - number_integer | -2147483648..-32769 | int32 | 0xD2 - number_integer | -32768..-129 | int16 | 0xD1 - number_integer | -128..-33 | int8 | 0xD0 - number_integer | -32..-1 | negative fixint | 0xE0..0xFF - number_integer | 0..127 | positive fixint | 0x00..0x7F - number_integer | 128..255 | uint 8 | 0xCC - number_integer | 256..65535 | uint 16 | 0xCD - number_integer | 65536..4294967295 | uint 32 | 0xCE - number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF - number_unsigned | 0..127 | positive fixint | 0x00..0x7F - number_unsigned | 128..255 | uint 8 | 0xCC - number_unsigned | 256..65535 | uint 16 | 0xCD - number_unsigned | 65536..4294967295 | uint 32 | 0xCE - number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF - number_float | *any value* | float 64 | 0xCB - string | *length*: 0..31 | fixstr | 0xA0..0xBF - string | *length*: 32..255 | str 8 | 0xD9 - string | *length*: 256..65535 | str 16 | 0xDA - string | *length*: 65536..4294967295 | str 32 | 0xDB - array | *size*: 0..15 | fixarray | 0x90..0x9F - array | *size*: 16..65535 | array 16 | 0xDC - array | *size*: 65536..4294967295 | array 32 | 0xDD - object | *size*: 0..15 | fix map | 0x80..0x8F - object | *size*: 16..65535 | map 16 | 0xDE - object | *size*: 65536..4294967295 | map 32 | 0xDF - - @note The mapping is **complete** in the sense that any JSON value type - can be converted to a MessagePack value. - - @note The following values can **not** be converted to a MessagePack value: - - strings with more than 4294967295 bytes - - arrays with more than 4294967295 elements - - objects with more than 4294967295 elements - - @note The following MessagePack types are not used in the conversion: - - bin 8 - bin 32 (0xC4..0xC6) - - ext 8 - ext 32 (0xC7..0xC9) - - float 32 (0xCA) - - fixext 1 - fixext 16 (0xD4..0xD8) - - @note Any MessagePack output created @ref to_msgpack can be successfully - parsed by @ref from_msgpack. - - @note If NaN or Infinity are stored inside a JSON number, they are - serialized properly. This behavior differs from the @ref dump() - function which serializes NaN or Infinity to `null`. - - @param[in] j JSON value to serialize - @return MessagePack serialization as byte vector - - @complexity Linear in the size of the JSON value @a j. - - @liveexample{The example shows the serialization of a JSON value to a byte - vector in MessagePack format.,to_msgpack} - - @sa http://msgpack.org - @sa @ref from_msgpack for the analogous deserialization - @sa @ref to_cbor(const basic_json& for the related CBOR format - @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the - related UBJSON format - - @since version 2.0.9 - */ - static std::vector to_msgpack(const basic_json& j) - { - std::vector result; - to_msgpack(j, result); - return result; - } - - static void to_msgpack(const basic_json& j, detail::output_adapter o) - { - binary_writer(o).write_msgpack(j); - } - - static void to_msgpack(const basic_json& j, detail::output_adapter o) - { - binary_writer(o).write_msgpack(j); - } - - /*! - @brief create a UBJSON serialization of a given JSON value - - Serializes a given JSON value @a j to a byte vector using the UBJSON - (Universal Binary JSON) serialization format. UBJSON aims to be more compact - than JSON itself, yet more efficient to parse. - - The library uses the following mapping from JSON values types to - UBJSON types according to the UBJSON specification: - - JSON value type | value/range | UBJSON type | marker - --------------- | --------------------------------- | ----------- | ------ - null | `null` | null | `Z` - boolean | `true` | true | `T` - boolean | `false` | false | `F` - number_integer | -9223372036854775808..-2147483649 | int64 | `L` - number_integer | -2147483648..-32769 | int32 | `l` - number_integer | -32768..-129 | int16 | `I` - number_integer | -128..127 | int8 | `i` - number_integer | 128..255 | uint8 | `U` - number_integer | 256..32767 | int16 | `I` - number_integer | 32768..2147483647 | int32 | `l` - number_integer | 2147483648..9223372036854775807 | int64 | `L` - number_unsigned | 0..127 | int8 | `i` - number_unsigned | 128..255 | uint8 | `U` - number_unsigned | 256..32767 | int16 | `I` - number_unsigned | 32768..2147483647 | int32 | `l` - number_unsigned | 2147483648..9223372036854775807 | int64 | `L` - number_float | *any value* | float64 | `D` - string | *with shortest length indicator* | string | `S` - array | *see notes on optimized format* | array | `[` - object | *see notes on optimized format* | map | `{` - - @note The mapping is **complete** in the sense that any JSON value type - can be converted to a UBJSON value. - - @note The following values can **not** be converted to a UBJSON value: - - strings with more than 9223372036854775807 bytes (theoretical) - - unsigned integer numbers above 9223372036854775807 - - @note The following markers are not used in the conversion: - - `Z`: no-op values are not created. - - `C`: single-byte strings are serialized with `S` markers. - - @note Any UBJSON output created @ref to_ubjson can be successfully parsed - by @ref from_ubjson. - - @note If NaN or Infinity are stored inside a JSON number, they are - serialized properly. This behavior differs from the @ref dump() - function which serializes NaN or Infinity to `null`. - - @note The optimized formats for containers are supported: Parameter - @a use_size adds size information to the beginning of a container and - removes the closing marker. Parameter @a use_type further checks - whether all elements of a container have the same type and adds the - type marker to the beginning of the container. The @a use_type - parameter must only be used together with @a use_size = true. Note - that @a use_size = true alone may result in larger representations - - the benefit of this parameter is that the receiving side is - immediately informed on the number of elements of the container. - - @param[in] j JSON value to serialize - @param[in] use_size whether to add size annotations to container types - @param[in] use_type whether to add type annotations to container types - (must be combined with @a use_size = true) - @return UBJSON serialization as byte vector - - @complexity Linear in the size of the JSON value @a j. - - @liveexample{The example shows the serialization of a JSON value to a byte - vector in UBJSON format.,to_ubjson} - - @sa http://ubjson.org - @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the - analogous deserialization - @sa @ref to_cbor(const basic_json& for the related CBOR format - @sa @ref to_msgpack(const basic_json&) for the related MessagePack format - - @since version 3.1.0 - */ - static std::vector to_ubjson( - const basic_json& j, - const bool use_size = false, - const bool use_type = false) - { - std::vector result; - to_ubjson(j, result, use_size, use_type); - return result; - } - - static void to_ubjson( - const basic_json& j, - detail::output_adapter o, - const bool use_size = false, - const bool use_type = false) - { - binary_writer(o).write_ubjson(j, use_size, use_type); - } - - static void to_ubjson( - const basic_json& j, - detail::output_adapter o, - const bool use_size = false, - const bool use_type = false) - { - binary_writer(o).write_ubjson(j, use_size, use_type); - } - - /*! - @brief Serializes the given JSON object `j` to BSON and returns a vector - containing the corresponding BSON-representation. - - BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are - stored as a single entity (a so-called document). - - The library uses the following mapping from JSON values types to BSON types: - - JSON value type | value/range | BSON type | marker - --------------- | --------------------------------- | ----------- | ------ - null | `null` | null | 0x0A - boolean | `true`, `false` | boolean | 0x08 - number_integer | -9223372036854775808..-2147483649 | int64 | 0x12 - number_integer | -2147483648..2147483647 | int32 | 0x10 - number_integer | 2147483648..9223372036854775807 | int64 | 0x12 - number_unsigned | 0..2147483647 | int32 | 0x10 - number_unsigned | 2147483648..9223372036854775807 | int64 | 0x12 - number_unsigned | 9223372036854775808..18446744073709551615| -- | -- - number_float | *any value* | double | 0x01 - string | *any value* | string | 0x02 - array | *any value* | document | 0x04 - object | *any value* | document | 0x03 - - @warning The mapping is **incomplete**, since only JSON-objects (and things - contained therein) can be serialized to BSON. - Also, integers larger than 9223372036854775807 cannot be serialized to BSON, - and the keys may not contain U+0000, since they are serialized a - zero-terminated c-strings. - - @throw out_of_range.407 if `j.is_number_unsigned() && j.get() > - 9223372036854775807` - @throw out_of_range.409 if a key in `j` contains a NULL (U+0000) - @throw type_error.317 if `!j.is_object()` - - @pre The input `j` is required to be an object: `j.is_object() == true`. - - @note Any BSON output created via @ref to_bson can be successfully parsed - by @ref from_bson. - - @param[in] j JSON value to serialize - @return BSON serialization as byte vector - - @complexity Linear in the size of the JSON value @a j. - - @liveexample{The example shows the serialization of a JSON value to a byte - vector in BSON format.,to_bson} - - @sa http://bsonspec.org/spec.html - @sa @ref from_bson(detail::input_adapter&&, const bool strict) for the - analogous deserialization - @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the - related UBJSON format - @sa @ref to_cbor(const basic_json&) for the related CBOR format - @sa @ref to_msgpack(const basic_json&) for the related MessagePack format - */ - static std::vector to_bson(const basic_json& j) - { - std::vector result; - to_bson(j, result); - return result; - } - - /*! - @brief Serializes the given JSON object `j` to BSON and forwards the - corresponding BSON-representation to the given output_adapter `o`. - @param j The JSON object to convert to BSON. - @param o The output adapter that receives the binary BSON representation. - @pre The input `j` shall be an object: `j.is_object() == true` - @sa @ref to_bson(const basic_json&) - */ - static void to_bson(const basic_json& j, detail::output_adapter o) - { - binary_writer(o).write_bson(j); - } - - /*! - @copydoc to_bson(const basic_json&, detail::output_adapter) - */ - static void to_bson(const basic_json& j, detail::output_adapter o) - { - binary_writer(o).write_bson(j); - } - - /*! - @brief create a JSON value from an input in CBOR format - - Deserializes a given input @a i to a JSON value using the CBOR (Concise - Binary Object Representation) serialization format. - - The library maps CBOR types to JSON value types as follows: - - CBOR type | JSON value type | first byte - ---------------------- | --------------- | ---------- - Integer | number_unsigned | 0x00..0x17 - Unsigned integer | number_unsigned | 0x18 - Unsigned integer | number_unsigned | 0x19 - Unsigned integer | number_unsigned | 0x1A - Unsigned integer | number_unsigned | 0x1B - Negative integer | number_integer | 0x20..0x37 - Negative integer | number_integer | 0x38 - Negative integer | number_integer | 0x39 - Negative integer | number_integer | 0x3A - Negative integer | number_integer | 0x3B - Negative integer | number_integer | 0x40..0x57 - UTF-8 string | string | 0x60..0x77 - UTF-8 string | string | 0x78 - UTF-8 string | string | 0x79 - UTF-8 string | string | 0x7A - UTF-8 string | string | 0x7B - UTF-8 string | string | 0x7F - array | array | 0x80..0x97 - array | array | 0x98 - array | array | 0x99 - array | array | 0x9A - array | array | 0x9B - array | array | 0x9F - map | object | 0xA0..0xB7 - map | object | 0xB8 - map | object | 0xB9 - map | object | 0xBA - map | object | 0xBB - map | object | 0xBF - False | `false` | 0xF4 - True | `true` | 0xF5 - Null | `null` | 0xF6 - Half-Precision Float | number_float | 0xF9 - Single-Precision Float | number_float | 0xFA - Double-Precision Float | number_float | 0xFB - - @warning The mapping is **incomplete** in the sense that not all CBOR - types can be converted to a JSON value. The following CBOR types - are not supported and will yield parse errors (parse_error.112): - - byte strings (0x40..0x5F) - - date/time (0xC0..0xC1) - - bignum (0xC2..0xC3) - - decimal fraction (0xC4) - - bigfloat (0xC5) - - tagged items (0xC6..0xD4, 0xD8..0xDB) - - expected conversions (0xD5..0xD7) - - simple values (0xE0..0xF3, 0xF8) - - undefined (0xF7) - - @warning CBOR allows map keys of any type, whereas JSON only allows - strings as keys in object values. Therefore, CBOR maps with keys - other than UTF-8 strings are rejected (parse_error.113). - - @note Any CBOR output created @ref to_cbor can be successfully parsed by - @ref from_cbor. - - @param[in] i an input in CBOR format convertible to an input adapter - @param[in] strict whether to expect the input to be consumed until EOF - (true by default) - @param[in] allow_exceptions whether to throw exceptions in case of a - parse error (optional, true by default) - - @return deserialized JSON value; in case of a parse error and - @a allow_exceptions set to `false`, the return value will be - value_t::discarded. - - @throw parse_error.110 if the given input ends prematurely or the end of - file was not reached when @a strict was set to true - @throw parse_error.112 if unsupported features from CBOR were - used in the given input @a v or if the input is not valid CBOR - @throw parse_error.113 if a string was expected as map key, but not found - - @complexity Linear in the size of the input @a i. - - @liveexample{The example shows the deserialization of a byte vector in CBOR - format to a JSON value.,from_cbor} - - @sa http://cbor.io - @sa @ref to_cbor(const basic_json&) for the analogous serialization - @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for the - related MessagePack format - @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the - related UBJSON format - - @since version 2.0.9; parameter @a start_index since 2.1.1; changed to - consume input adapters, removed start_index parameter, and added - @a strict parameter since 3.0.0; added @a allow_exceptions parameter - since 3.2.0 - */ - JSON_NODISCARD - static basic_json from_cbor( - detail::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(detail::input_adapter(i)).sax_parse(input_format_t::cbor, &sdp, strict); - return res ? result : basic_json(value_t::discarded); - } - - /*! - @copydoc from_cbor(detail::input_adapter&&, const bool, const bool) - */ - template < - typename A1, - typename A2, - detail::enable_if_t::value, int> = 0> - JSON_NODISCARD static basic_json from_cbor( - A1&& a1, - A2&& a2, - 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(std::forward(a1), std::forward(a2))) - .sax_parse(input_format_t::cbor, &sdp, strict); - return res ? result : basic_json(value_t::discarded); - } - - /*! - @brief create a JSON value from an input in MessagePack format - - Deserializes a given input @a i to a JSON value using the MessagePack - serialization format. - - The library maps MessagePack types to JSON value types as follows: - - MessagePack type | JSON value type | first byte - ---------------- | --------------- | ---------- - positive fixint | number_unsigned | 0x00..0x7F - fixmap | object | 0x80..0x8F - fixarray | array | 0x90..0x9F - fixstr | string | 0xA0..0xBF - nil | `null` | 0xC0 - false | `false` | 0xC2 - true | `true` | 0xC3 - float 32 | number_float | 0xCA - float 64 | number_float | 0xCB - uint 8 | number_unsigned | 0xCC - uint 16 | number_unsigned | 0xCD - uint 32 | number_unsigned | 0xCE - uint 64 | number_unsigned | 0xCF - int 8 | number_integer | 0xD0 - int 16 | number_integer | 0xD1 - int 32 | number_integer | 0xD2 - int 64 | number_integer | 0xD3 - str 8 | string | 0xD9 - str 16 | string | 0xDA - str 32 | string | 0xDB - array 16 | array | 0xDC - array 32 | array | 0xDD - map 16 | object | 0xDE - map 32 | object | 0xDF - negative fixint | number_integer | 0xE0-0xFF - - @warning The mapping is **incomplete** in the sense that not all - MessagePack types can be converted to a JSON value. The following - MessagePack types are not supported and will yield parse errors: - - bin 8 - bin 32 (0xC4..0xC6) - - ext 8 - ext 32 (0xC7..0xC9) - - fixext 1 - fixext 16 (0xD4..0xD8) - - @note Any MessagePack output created @ref to_msgpack can be successfully - parsed by @ref from_msgpack. - - @param[in] i an input in MessagePack format convertible to an input - adapter - @param[in] strict whether to expect the input to be consumed until EOF - (true by default) - @param[in] allow_exceptions whether to throw exceptions in case of a - parse error (optional, true by default) - - @return deserialized JSON value; in case of a parse error and - @a allow_exceptions set to `false`, the return value will be - value_t::discarded. - - @throw parse_error.110 if the given input ends prematurely or the end of - file was not reached when @a strict was set to true - @throw parse_error.112 if unsupported features from MessagePack were - used in the given input @a i or if the input is not valid MessagePack - @throw parse_error.113 if a string was expected as map key, but not found - - @complexity Linear in the size of the input @a i. - - @liveexample{The example shows the deserialization of a byte vector in - MessagePack format to a JSON value.,from_msgpack} - - @sa http://msgpack.org - @sa @ref to_msgpack(const basic_json&) for the analogous serialization - @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the - related CBOR format - @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for - the related UBJSON format - @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for - the related BSON format - - @since version 2.0.9; parameter @a start_index since 2.1.1; changed to - consume input adapters, removed start_index parameter, and added - @a strict parameter since 3.0.0; added @a allow_exceptions parameter - since 3.2.0 - */ - JSON_NODISCARD - static basic_json from_msgpack( - detail::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(detail::input_adapter(i)).sax_parse(input_format_t::msgpack, &sdp, strict); - return res ? result : basic_json(value_t::discarded); - } - - /*! - @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool) - */ - template < - typename A1, - typename A2, - detail::enable_if_t::value, int> = 0> - JSON_NODISCARD static basic_json from_msgpack( - A1&& a1, - A2&& a2, - 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(std::forward(a1), std::forward(a2))) - .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 - - Deserializes a given input @a i to a JSON value using the UBJSON (Universal - Binary JSON) serialization format. - - The library maps UBJSON types to JSON value types as follows: - - UBJSON type | JSON value type | marker - ----------- | --------------------------------------- | ------ - no-op | *no value, next value is read* | `N` - null | `null` | `Z` - false | `false` | `F` - true | `true` | `T` - float32 | number_float | `d` - float64 | number_float | `D` - uint8 | number_unsigned | `U` - int8 | number_integer | `i` - int16 | number_integer | `I` - int32 | number_integer | `l` - int64 | number_integer | `L` - string | string | `S` - char | string | `C` - array | array (optimized values are supported) | `[` - object | object (optimized values are supported) | `{` - - @note The mapping is **complete** in the sense that any UBJSON value can - be converted to a JSON value. - - @param[in] i an input in UBJSON format convertible to an input adapter - @param[in] strict whether to expect the input to be consumed until EOF - (true by default) - @param[in] allow_exceptions whether to throw exceptions in case of a - parse error (optional, true by default) - - @return deserialized JSON value; in case of a parse error and - @a allow_exceptions set to `false`, the return value will be - value_t::discarded. - - @throw parse_error.110 if the given input ends prematurely or the end of - file was not reached when @a strict was set to true - @throw parse_error.112 if a parse error occurs - @throw parse_error.113 if a string could not be parsed successfully - - @complexity Linear in the size of the input @a i. - - @liveexample{The example shows the deserialization of a byte vector in - UBJSON format to a JSON value.,from_ubjson} - - @sa http://ubjson.org - @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the - analogous serialization - @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the - related CBOR format - @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for - the related MessagePack format - @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for - the related BSON format - - @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0 - */ - JSON_NODISCARD - static basic_json from_ubjson( - detail::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(detail::input_adapter(i)).sax_parse(input_format_t::ubjson, &sdp, strict); - return res ? result : basic_json(value_t::discarded); - } - - /*! - @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool) - */ - template < - typename A1, - typename A2, - detail::enable_if_t::value, int> = 0> - JSON_NODISCARD static basic_json from_ubjson( - A1&& a1, - A2&& a2, - 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(std::forward(a1), std::forward(a2))) - .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 - - Deserializes a given input @a i to a JSON value using the BSON (Binary JSON) - serialization format. - - The library maps BSON record types to JSON value types as follows: - - BSON type | BSON marker byte | JSON value type - --------------- | ---------------- | --------------------------- - double | 0x01 | number_float - string | 0x02 | string - document | 0x03 | object - array | 0x04 | array - binary | 0x05 | still unsupported - undefined | 0x06 | still unsupported - ObjectId | 0x07 | still unsupported - boolean | 0x08 | boolean - UTC Date-Time | 0x09 | still unsupported - null | 0x0A | null - Regular Expr. | 0x0B | still unsupported - DB Pointer | 0x0C | still unsupported - JavaScript Code | 0x0D | still unsupported - Symbol | 0x0E | still unsupported - JavaScript Code | 0x0F | still unsupported - int32 | 0x10 | number_integer - Timestamp | 0x11 | still unsupported - 128-bit decimal float | 0x13 | still unsupported - Max Key | 0x7F | still unsupported - Min Key | 0xFF | still unsupported - - @warning The mapping is **incomplete**. The unsupported mappings - are indicated in the table above. - - @param[in] i an input in BSON format convertible to an input adapter - @param[in] strict whether to expect the input to be consumed until EOF - (true by default) - @param[in] allow_exceptions whether to throw exceptions in case of a - parse error (optional, true by default) - - @return deserialized JSON value; in case of a parse error and - @a allow_exceptions set to `false`, the return value will be - value_t::discarded. - - @throw parse_error.114 if an unsupported BSON record type is encountered - - @complexity Linear in the size of the input @a i. - - @liveexample{The example shows the deserialization of a byte vector in - BSON format to a JSON value.,from_bson} - - @sa http://bsonspec.org/spec.html - @sa @ref to_bson(const basic_json&) for the analogous serialization - @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the - related CBOR format - @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for - the related MessagePack format - @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the - related UBJSON format - */ - JSON_NODISCARD - static basic_json from_bson( - detail::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(detail::input_adapter(i)).sax_parse(input_format_t::bson, &sdp, strict); - return res ? result : basic_json(value_t::discarded); - } - - /*! - @copydoc from_bson(detail::input_adapter&&, const bool, const bool) - */ - template < - typename A1, - typename A2, - detail::enable_if_t::value, int> = 0> - JSON_NODISCARD static basic_json from_bson( - A1&& a1, - A2&& a2, - 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(std::forward(a1), std::forward(a2))) - .sax_parse(input_format_t::bson, &sdp, strict); - return res ? result : basic_json(value_t::discarded); - } - - /// @} - - ////////////////////////// - // JSON Pointer support // - ////////////////////////// - - /// @name JSON Pointer functions - /// @{ - - /*! - @brief access specified element via JSON Pointer - - Uses a JSON pointer to retrieve a reference to the respective JSON value. - No bound checking is performed. Similar to @ref operator[](const typename - object_t::key_type&), `null` values are created in arrays and objects if - necessary. - - In particular: - - If the JSON pointer points to an object key that does not exist, it - is created an filled with a `null` value before a reference to it - is returned. - - If the JSON pointer points to an array index that does not exist, it - is created an filled with a `null` value before a reference to it - is returned. All indices between the current maximum and the given - index are also filled with `null`. - - The special value `-` is treated as a synonym for the index past the - end. - - @param[in] ptr a JSON pointer - - @return reference to the element pointed to by @a ptr - - @complexity Constant. - - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.404 if the JSON pointer can not be resolved - - @liveexample{The behavior is shown in the example.,operatorjson_pointer} - - @since version 2.0.0 - */ - reference operator[](const json_pointer& ptr) { return ptr.get_unchecked(this); } - - /*! - @brief access specified element via JSON Pointer - - Uses a JSON pointer to retrieve a reference to the respective JSON value. - No bound checking is performed. The function does not change the JSON - value; no `null` values are created. In particular, the the special value - `-` yields an exception. - - @param[in] ptr JSON pointer to the desired element - - @return const reference to the element pointed to by @a ptr - - @complexity Constant. - - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - - @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} - - @since version 2.0.0 - */ - const_reference operator[](const json_pointer& ptr) const { return ptr.get_unchecked(this); } - - /*! - @brief access specified element via JSON Pointer - - Returns a reference to the element at with specified JSON pointer @a ptr, - with bounds checking. - - @param[in] ptr JSON pointer to the desired element - - @return reference to the element pointed to by @a ptr - - @throw parse_error.106 if an array index in the passed JSON pointer @a ptr - begins with '0'. See example below. - - @throw parse_error.109 if an array index in the passed JSON pointer @a ptr - is not a number. See example below. - - @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr - is out of range. See example below. - - @throw out_of_range.402 if the array index '-' is used in the passed JSON - pointer @a ptr. As `at` provides checked access (and no elements are - implicitly inserted), the index '-' is always invalid. See example below. - - @throw out_of_range.403 if the JSON pointer describes a key of an object - which cannot be found. See example below. - - @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. - See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Constant. - - @since version 2.0.0 - - @liveexample{The behavior is shown in the example.,at_json_pointer} - */ - reference at(const json_pointer& ptr) { return ptr.get_checked(this); } - - /*! - @brief access specified element via JSON Pointer - - Returns a const reference to the element at with specified JSON pointer @a - ptr, with bounds checking. - - @param[in] ptr JSON pointer to the desired element - - @return reference to the element pointed to by @a ptr - - @throw parse_error.106 if an array index in the passed JSON pointer @a ptr - begins with '0'. See example below. - - @throw parse_error.109 if an array index in the passed JSON pointer @a ptr - is not a number. See example below. - - @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr - is out of range. See example below. - - @throw out_of_range.402 if the array index '-' is used in the passed JSON - pointer @a ptr. As `at` provides checked access (and no elements are - implicitly inserted), the index '-' is always invalid. See example below. - - @throw out_of_range.403 if the JSON pointer describes a key of an object - which cannot be found. See example below. - - @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. - See example below. - - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. - - @complexity Constant. - - @since version 2.0.0 - - @liveexample{The behavior is shown in the example.,at_json_pointer_const} - */ - const_reference at(const json_pointer& ptr) const { return ptr.get_checked(this); } - - /*! - @brief return flattened JSON value - - The function creates a JSON object whose keys are JSON pointers (see [RFC - 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all - primitive. The original JSON value can be restored using the @ref - unflatten() function. - - @return an object that maps JSON pointers to primitive values - - @note Empty objects and arrays are flattened to `null` and will not be - reconstructed correctly by the @ref unflatten() function. - - @complexity Linear in the size the JSON value. - - @liveexample{The following code shows how a JSON object is flattened to an - object whose keys consist of JSON pointers.,flatten} - - @sa @ref unflatten() for the reverse function - - @since version 2.0.0 - */ - basic_json flatten() const - { - basic_json result(value_t::object); - json_pointer::flatten("", *this, result); - return result; - } - - /*! - @brief unflatten a previously flattened JSON value - - The function restores the arbitrary nesting of a JSON value that has been - flattened before using the @ref flatten() function. The JSON value must - meet certain constraints: - 1. The value must be an object. - 2. The keys must be JSON pointers (see - [RFC 6901](https://tools.ietf.org/html/rfc6901)) - 3. The mapped values must be primitive JSON types. - - @return the original JSON from a flattened version - - @note Empty objects and arrays are flattened by @ref flatten() to `null` - values and can not unflattened to their original type. Apart from - this example, for a JSON value `j`, the following is always true: - `j == j.flatten().unflatten()`. - - @complexity Linear in the size the JSON value. - - @throw type_error.314 if value is not an object - @throw type_error.315 if object values are not primitive - - @liveexample{The following code shows how a flattened JSON object is - unflattened into the original nested JSON object.,unflatten} - - @sa @ref flatten() for the reverse function - - @since version 2.0.0 - */ - basic_json unflatten() const { return json_pointer::unflatten(*this); } - - /// @} - - ////////////////////////// - // JSON Patch functions // - ////////////////////////// - - /// @name JSON Patch functions - /// @{ - - /*! - @brief applies a JSON patch - - [JSON Patch](http://jsonpatch.com) defines a JSON document structure for - expressing a sequence of operations to apply to a JSON) document. With - this function, a JSON Patch is applied to the current JSON value by - executing all operations from the patch. - - @param[in] json_patch JSON patch document - @return patched document - - @note The application of a patch is atomic: Either all operations succeed - and the patched document is returned or an exception is thrown. In - any case, the original value is not changed: the patch is applied - to a copy of the value. - - @throw parse_error.104 if the JSON patch does not consist of an array of - objects - - @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory - attributes are missing); example: `"operation add must have member path"` - - @throw out_of_range.401 if an array index is out of range. - - @throw out_of_range.403 if a JSON pointer inside the patch could not be - resolved successfully in the current JSON value; example: `"key baz not - found"` - - @throw out_of_range.405 if JSON pointer has no parent ("add", "remove", - "move") - - @throw other_error.501 if "test" operation was unsuccessful - - @complexity Linear in the size of the JSON value and the length of the - JSON patch. As usually only a fraction of the JSON value is affected by - the patch, the complexity can usually be neglected. - - @liveexample{The following code shows how a JSON patch is applied to a - value.,patch} - - @sa @ref diff -- create a JSON patch by comparing two JSON values - - @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) - @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) - - @since version 2.0.0 - */ - basic_json patch(const basic_json& json_patch) const - { - // make a working copy to apply the patch to - basic_json result = *this; - - // the valid JSON Patch operations - enum class patch_operations - { - add, - remove, - replace, - move, - copy, - test, - invalid - }; - - const auto get_op = [](const std::string& op) { - if (op == "add") - { - return patch_operations::add; - } - if (op == "remove") - { - return patch_operations::remove; - } - if (op == "replace") - { - return patch_operations::replace; - } - if (op == "move") - { - return patch_operations::move; - } - if (op == "copy") - { - return patch_operations::copy; - } - if (op == "test") - { - return patch_operations::test; - } - - return patch_operations::invalid; - }; - - // wrapper for "add" operation; add value at ptr - const auto operation_add = [&result](json_pointer& ptr, basic_json val) { - // adding to the root of the target document means replacing it - if (ptr.empty()) - { - result = val; - return; - } - - // make sure the top element of the pointer exists - json_pointer top_pointer = ptr.top(); - if (top_pointer != ptr) - { - result.at(top_pointer); - } - - // get reference to parent of JSON pointer ptr - const auto last_path = ptr.back(); - ptr.pop_back(); - basic_json& parent = result[ptr]; - - switch (parent.m_type) - { - case value_t::null: case value_t::object: { - // use operator[] to add value - parent[last_path] = val; + m_value.object = create(first.m_it.object_iterator, last.m_it.object_iterator); break; } case value_t::array: { - if (last_path == "-") + m_value.array = create(first.m_it.array_iterator, last.m_it.array_iterator); + break; + } + + case value_t::binary: { + m_value = *first.m_object->m_value.binary; + break; + } + + default: + _az_JSON_THROW(invalid_iterator::create( + 206, + "cannot construct with iterators from " + std::string(first.m_object->type_name()))); + } + + assert_invariant(); + } + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + template < + typename JsonRef, + detail::enable_if_t< + detail::conjunction< + detail::is_json_ref, + std::is_same>::value, + int> = 0> + basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) + { + } + + /*! + @brief copy constructor + + Creates a copy of a given JSON value. + + @param[in] other the JSON value to copy + + @post `*this == other` + + @complexity Linear in the size of @a other. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + - As postcondition, it holds: `other == basic_json(other)`. + + @liveexample{The following code shows an example for the copy + constructor.,basic_json__basic_json} + + @since version 1.0.0 + */ + basic_json(const basic_json& other) : m_type(other.m_type) + { + // check of passed value is valid + other.assert_invariant(); + + switch (m_type) + { + case value_t::object: { + m_value = *other.m_value.object; + break; + } + + case value_t::array: { + m_value = *other.m_value.array; + break; + } + + case value_t::string: { + m_value = *other.m_value.string; + break; + } + + case value_t::boolean: { + m_value = other.m_value.boolean; + break; + } + + case value_t::number_integer: { + m_value = other.m_value.number_integer; + break; + } + + case value_t::number_unsigned: { + m_value = other.m_value.number_unsigned; + break; + } + + case value_t::number_float: { + m_value = other.m_value.number_float; + break; + } + + case value_t::binary: { + m_value = *other.m_value.binary; + break; + } + + default: + break; + } + + assert_invariant(); + } + + /*! + @brief move constructor + + Move constructor. Constructs a JSON value with the contents of the given + value @a other using move semantics. It "steals" the resources from @a + other and leaves it as JSON null value. + + @param[in,out] other value to move to this object + + @post `*this` has the same value as @a other before the call. + @post @a other is a JSON null value. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @requirement This function helps `basic_json` satisfying the + [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible) + requirements. + + @liveexample{The code below shows the move constructor explicitly called + via std::move.,basic_json__moveconstructor} + + @since version 1.0.0 + */ + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) + { + // check that passed value is valid + other.assert_invariant(); + + // invalidate payload + other.m_type = value_t::null; + other.m_value = {}; + + assert_invariant(); + } + + /*! + @brief copy assignment + + Copy assignment operator. Copies a JSON value via the "copy and swap" + strategy: It is expressed in terms of the copy constructor, destructor, + and the `swap()` member function. + + @param[in] other value to copy from + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + + @liveexample{The code below shows and example for the copy assignment. It + creates a copy of value `a` which is then swapped with `b`. Finally\, the + copy of `a` (which is the null value after the swap) is + destroyed.,basic_json__copyassignment} + + @since version 1.0.0 + */ + basic_json& operator=(basic_json other) noexcept( + std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable< + value_t>::value and std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value) + { + // check that passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + assert_invariant(); + return *this; + } + + /*! + @brief destructor + + Destroys the JSON value and frees all allocated memory. + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + - All stored elements are destroyed and all memory is freed. + + @since version 1.0.0 + */ + ~basic_json() noexcept + { + assert_invariant(); + m_value.destroy(m_type); + } + + /// @} + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ + + /*! + @brief serialization + + Serialization function for JSON values. The function tries to mimic + Python's `json.dumps()` function, and currently supports its @a indent + and @a ensure_ascii parameters. + + @param[in] indent If indent is nonnegative, then array elements and object + members will be pretty-printed with that indent level. An indent level of + `0` will only insert newlines. `-1` (the default) selects the most compact + representation. + @param[in] indent_char The character to use for indentation if @a indent is + greater than `0`. The default is ` ` (space). + @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] error_handler how to react on decoding errors; there are three + 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). + + @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 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. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @liveexample{The following example shows the effect of different @a indent\, + @a indent_char\, and @a ensure_ascii parameters to the result of the + serialization.,dump} + + @see https://docs.python.org/2/library/json.html#json.dump + + @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; 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 + { + 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)); + } + else + { + s.dump(*this, false, ensure_ascii, 0); + } + + return result; + } + + /*! + @brief return the type of the JSON value (explicit) + + Return the type of the JSON value as a value from the @ref value_t + enumeration. + + @return the type of the JSON value + Value type | return value + ------------------------- | ------------------------- + null | value_t::null + boolean | value_t::boolean + string | value_t::string + number (integer) | value_t::number_integer + number (unsigned integer) | value_t::number_unsigned + number (floating-point) | value_t::number_float + object | value_t::object + array | value_t::array + binary | value_t::binary + discarded | value_t::discarded + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `type()` for all JSON + types.,type} + + @sa @ref operator value_t() -- return the type of the JSON value (implicit) + @sa @ref type_name() -- return the type as string + + @since version 1.0.0 + */ + constexpr value_t type() const noexcept { return m_type; } + + /*! + @brief return whether type is primitive + + This function returns true if and only if the JSON type is primitive + (string, number, boolean, or null). + + @return `true` if type is primitive (string, number, boolean, or null), + `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_primitive()` for all JSON + types.,is_primitive} + + @sa @ref is_structured() -- returns whether JSON value is structured + @sa @ref is_null() -- returns whether JSON value is `null` + @sa @ref is_string() -- returns whether JSON value is a string + @sa @ref is_boolean() -- returns whether JSON value is a boolean + @sa @ref is_number() -- returns whether JSON value is a number + @sa @ref is_binary() -- returns whether JSON value is a binary array + + @since version 1.0.0 + */ + constexpr bool is_primitive() const noexcept + { + return is_null() or is_string() or is_boolean() or is_number() or is_binary(); + } + + /*! + @brief return whether type is structured + + This function returns true if and only if the JSON type is structured + (array or object). + + @return `true` if type is structured (array or object), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_structured()` for all JSON + types.,is_structured} + + @sa @ref is_primitive() -- returns whether value is primitive + @sa @ref is_array() -- returns whether value is an array + @sa @ref is_object() -- returns whether value is an object + + @since version 1.0.0 + */ + constexpr bool is_structured() const noexcept { return is_array() or is_object(); } + + /*! + @brief return whether value is null + + This function returns true if and only if the JSON value is null. + + @return `true` if type is null, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_null()` for all JSON + types.,is_null} + + @since version 1.0.0 + */ + constexpr bool is_null() const noexcept { return m_type == value_t::null; } + + /*! + @brief return whether value is a boolean + + This function returns true if and only if the JSON value is a boolean. + + @return `true` if type is boolean, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_boolean()` for all JSON + types.,is_boolean} + + @since version 1.0.0 + */ + constexpr bool is_boolean() const noexcept { return m_type == value_t::boolean; } + + /*! + @brief return whether value is a number + + This function returns true if and only if the JSON value is a number. This + includes both integer (signed and unsigned) and floating-point values. + + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number()` for all JSON + types.,is_number} + + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number() const noexcept { return is_number_integer() or is_number_float(); } + + /*! + @brief return whether value is an integer number + + This function returns true if and only if the JSON value is a signed or + unsigned integer number. This excludes floating-point values. + + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_integer()` for all + JSON types.,is_number_integer} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number_integer() const noexcept + { + return m_type == value_t::number_integer or m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is an unsigned integer number + + This function returns true if and only if the JSON value is an unsigned + integer number. This excludes floating-point and signed integer values. + + @return `true` if type is an unsigned integer number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_unsigned()` for all + JSON types.,is_number_unsigned} + + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_float() -- check if value is a floating-point number + + @since version 2.0.0 + */ + constexpr bool is_number_unsigned() const noexcept + { + return m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is a floating-point number + + This function returns true if and only if the JSON value is a + floating-point number. This excludes signed and unsigned integer values. + + @return `true` if type is a floating-point number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_float()` for all + JSON types.,is_number_float} + + @sa @ref is_number() -- check if value is number + @sa @ref is_number_integer() -- check if value is an integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + + @since version 1.0.0 + */ + constexpr bool is_number_float() const noexcept { return m_type == value_t::number_float; } + + /*! + @brief return whether value is an object + + This function returns true if and only if the JSON value is an object. + + @return `true` if type is object, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_object()` for all JSON + types.,is_object} + + @since version 1.0.0 + */ + constexpr bool is_object() const noexcept { return m_type == value_t::object; } + + /*! + @brief return whether value is an array + + This function returns true if and only if the JSON value is an array. + + @return `true` if type is array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_array()` for all JSON + types.,is_array} + + @since version 1.0.0 + */ + constexpr bool is_array() const noexcept { return m_type == value_t::array; } + + /*! + @brief return whether value is a string + + This function returns true if and only if the JSON value is a string. + + @return `true` if type is string, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_string()` for all JSON + types.,is_string} + + @since version 1.0.0 + */ + constexpr bool is_string() const noexcept { return m_type == value_t::string; } + + /*! + @brief return whether value is a binary array + + This function returns true if and only if the JSON value is a binary array. + + @return `true` if type is binary array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_binary()` for all JSON + types.,is_binary} + + @since version 3.8.0 + */ + constexpr bool is_binary() const noexcept { return m_type == value_t::binary; } + + /*! + @brief return whether value is discarded + + This function returns true if and only if the JSON value was discarded + during parsing with a callback function (see @ref parser_callback_t). + + @note This function will always be `false` for JSON values after parsing. + That is, discarded values can only occur during parsing, but will be + removed when inside a structured value or replaced by null in other cases. + + @return `true` if type is discarded, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_discarded()` for all JSON + types.,is_discarded} + + @since version 1.0.0 + */ + constexpr bool is_discarded() const noexcept { return m_type == value_t::discarded; } + + /*! + @brief return the type of the JSON value (implicit) + + Implicitly return the type of the JSON value as a value from the @ref + value_t enumeration. + + @return the type of the JSON value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies the @ref value_t operator for + all JSON types.,operator__value_t} + + @sa @ref type() -- return the type of the JSON value (explicit) + @sa @ref type_name() -- return the type as string + + @since version 1.0.0 + */ + constexpr operator value_t() const noexcept { return m_type; } + + /// @} + + private: + ////////////////// + // value access // + ////////////////// + + /// get a boolean (explicit) + boolean_t get_impl(boolean_t* /*unused*/) const + { + if (_az_JSON_HEDLEY_LIKELY(is_boolean())) + { + return m_value.boolean; + } + + _az_JSON_THROW( + type_error::create(302, "type must be boolean, but is " + std::string(type_name()))); + } + + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t* /*unused*/) noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t* /*unused*/) noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t* /*unused*/) noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr( + const number_integer_t* /*unused*/) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr( + const number_unsigned_t* /*unused*/) const noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (binary) + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /// get a pointer to the value (binary) + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /*! + @brief helper function to implement get_ref() + + This function helps to implement get_ref() without code duplication for + const and non-const overloads + + @tparam ThisType will be deduced as `basic_json` or `const basic_json` + + @throw type_error.303 if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // delegate the call to get_ptr<>() + auto ptr = obj.template get_ptr::type>(); + + if (_az_JSON_HEDLEY_LIKELY(ptr != nullptr)) + { + return *ptr; + } + + _az_JSON_THROW(type_error::create( + 303, + "incompatible ReferenceType for get_ref, actual type is " + + std::string(obj.type_name()))); + } + + public: + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ + + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template < + typename BasicJsonType, + detail::enable_if_t< + std::is_same::type, basic_json_t>::value, + int> = 0> + basic_json get() const + { + return *this; + } + + /*! + @brief get special-case overload + + This overloads converts the current @ref basic_json in a different + @ref basic_json type + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this, converted into @tparam BasicJsonType + + @complexity Depending on the implementation of the called `from_json()` + method. + + @since version 3.2.0 + */ + template < + typename BasicJsonType, + detail::enable_if_t< + not std::is_same::value + and detail::is_basic_json::value, + int> = 0> + BasicJsonType get() const + { + return *this; + } + + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const basic_json&)` + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} + + @since version 2.1.0 + */ + template < + typename ValueTypeCV, + typename ValueType = detail::uncvref_t, + detail::enable_if_t< + not detail::is_basic_json::value + and detail::has_from_json::value + and not detail::has_non_default_from_json::value, + int> = 0> + ValueType get() const noexcept(noexcept(JSONSerializer::from_json( + std::declval(), + std::declval()))) + { + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert( + not std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + static_assert( + std::is_default_constructible::value, + "types must be DefaultConstructible when used with get()"); + + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + } + + /*! + @brief get a value (explicit); special case + + Explicit type conversion between the JSON value and a compatible value + which is **not** + [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) and **not** + [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). The + value is converted by calling the @ref json_serializer `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const basic_json&)` + + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @since version 2.1.0 + */ + template < + typename ValueTypeCV, + typename ValueType = detail::uncvref_t, + detail::enable_if_t< + not std::is_same::value + and detail::has_non_default_from_json::value, + int> = 0> + ValueType get() const noexcept( + noexcept(JSONSerializer::from_json(std::declval()))) + { + static_assert( + not std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return JSONSerializer::from_json(*this); + } + + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value. + The value is filled into the input parameter by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType v; + JSONSerializer::from_json(*this, v); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + + @tparam ValueType the input parameter type. + + @return the input parameter, allowing chaining calls. + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get_to} + + @since version 3.3.0 + */ + template < + typename ValueType, + detail::enable_if_t< + not detail::is_basic_json::value + and detail::has_from_json::value, + int> = 0> + ValueType& get_to(ValueType& v) const noexcept( + noexcept(JSONSerializer::from_json(std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + template < + typename T, + std::size_t N, + typename Array = T (&)[N], + detail::enable_if_t::value, int> = 0> + Array get_to(T (&v)[N]) const + noexcept(noexcept(JSONSerializer::from_json(std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + /*! + @brief get a pointer value (implicit) + + Implicit pointer access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the pointee of the result yields an undefined + state. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} + + @since version 1.0.0 + */ + template < + typename PointerType, + typename std::enable_if::value, int>::type = 0> + auto get_ptr() noexcept + -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (implicit) + @copydoc get_ptr() + */ + template < + typename PointerType, + typename std::enable_if< + std::is_pointer::value + and std::is_const::type>::value, + int>::type + = 0> + constexpr auto get_ptr() const noexcept + -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template < + typename PointerType, + typename std::enable_if::value, int>::type = 0> + auto get() noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template < + typename PointerType, + typename std::enable_if::value, int>::type = 0> + constexpr auto get() const noexcept + -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a reference value (implicit) + + Implicit reference access to the internally stored JSON value. No copies + are made. + + @warning Writing data to the referee of the result yields an undefined + state. + + @tparam ReferenceType reference type; must be a reference to @ref array_t, + @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or + @ref number_float_t. Enforced by static assertion. + + @return reference to the internally stored JSON value if the requested + reference type @a ReferenceType fits to the JSON value; throws + type_error.303 otherwise + + @throw type_error.303 in case passed type @a ReferenceType is incompatible + with the stored JSON value; see example below + + @complexity Constant. + + @liveexample{The example shows several calls to `get_ref()`.,get_ref} + + @since version 1.1.0 + */ + template < + typename ReferenceType, + typename std::enable_if::value, int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a reference value (implicit) + @copydoc get_ref() + */ + template < + typename ReferenceType, + typename std::enable_if< + std::is_reference::value + and std::is_const::type>::value, + int>::type + = 0> + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a value (implicit) + + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. + + @return copy of the JSON value, converted to type @a ValueType + + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} + + @since version 1.0.0 + */ + template < + typename ValueType, + typename std::enable_if< + not std::is_pointer::value + and not std::is_same>::value + and not std::is_same::value + and not detail::is_basic_json::value + and not std::is_same< + ValueType, + std::initializer_list>::value +#if defined(_az_JSON_HAS_CPP_17) \ + && (defined(__GNUC__) || (defined(_MSC_VER) and _MSC_VER >= 1910 and _MSC_VER <= 1914)) + and not std::is_same::value +#endif + and detail:: + is_detected:: + value, + int>::type + = 0> + operator ValueType() const + { + // delegate the call to get<>() const + 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()) + { + _az_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()) + { + _az_JSON_THROW( + type_error::create(302, "type must be binary, but is " + std::string(type_name()))); + } + + return *get_ptr(); + } + + /// @} + + //////////////////// + // element access // + //////////////////// + + /// @name element access + /// Access to the JSON value. + /// @{ + + /*! + @brief access specified array element with bounds checking + + Returns a reference to the element at specified location @a idx, with + bounds checking. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__size_type} + */ + reference at(size_type idx) + { + // at only works for arrays + if (_az_JSON_HEDLEY_LIKELY(is_array())) + { + _az_JSON_TRY { return m_value.array->at(idx); } + _az_JSON_CATCH(std::out_of_range&) + { + // create better exception explanation + _az_JSON_THROW( + out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + } + else + { + _az_JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified array element with bounds checking + + Returns a const reference to the element at specified location @a idx, + with bounds checking. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__size_type_const} + */ + const_reference at(size_type idx) const + { + // at only works for arrays + if (_az_JSON_HEDLEY_LIKELY(is_array())) + { + _az_JSON_TRY { return m_value.array->at(idx); } + _az_JSON_CATCH(std::out_of_range&) + { + // create better exception explanation + _az_JSON_THROW( + out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + } + else + { + _az_JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a reference to the element at with specified key @a key, with + bounds checking. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Logarithmic in the size of the container. + + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__object_t_key_type} + */ + reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (_az_JSON_HEDLEY_LIKELY(is_object())) + { + _az_JSON_TRY { return m_value.object->at(key); } + _az_JSON_CATCH(std::out_of_range&) + { + // create better exception explanation + _az_JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + } + } + else + { + _az_JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a const reference to the element at with specified key @a key, + with bounds checking. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Logarithmic in the size of the container. + + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__object_t_key_type_const} + */ + const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (_az_JSON_HEDLEY_LIKELY(is_object())) + { + _az_JSON_TRY { return m_value.object->at(key); } + _az_JSON_CATCH(std::out_of_range&) + { + // create better exception explanation + _az_JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + } + } + else + { + _az_JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()))); + } + } + + /*! + @brief access specified array element + + Returns a reference to the element at specified location @a idx. + + @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), + then the array is silently filled up with `null` values to make `idx` a + valid reference to the last stored element. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw type_error.305 if the JSON value is not an array or null; in that + cases, using the [] operator with an index makes no sense. + + @complexity Constant if @a idx is in the range of the array. Otherwise + linear in `idx - size()`. + + @liveexample{The example below shows how array elements can be read and + written using `[]` operator. Note the addition of `null` + values.,operatorarray__size_type} + + @since version 1.0.0 + */ + reference operator[](size_type idx) + { + // implicitly convert null value to an empty array + if (is_null()) + { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (_az_JSON_HEDLEY_LIKELY(is_array())) + { + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) + { + m_value.array->insert( + m_value.array->end(), idx - m_value.array->size() + 1, basic_json()); + } + + return m_value.array->operator[](idx); + } + + _az_JSON_THROW(type_error::create( + 305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + } + + /*! + @brief access specified array element + + Returns a const reference to the element at specified location @a idx. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw type_error.305 if the JSON value is not an array; in that case, + using the [] operator with an index makes no sense. + + @complexity Constant. + + @liveexample{The example below shows how array elements can be read using + the `[]` operator.,operatorarray__size_type_const} + + @since version 1.0.0 + */ + const_reference operator[](size_type idx) const + { + // const operator[] only works for arrays + if (_az_JSON_HEDLEY_LIKELY(is_array())) + { + return m_value.array->operator[](idx); + } + + _az_JSON_THROW(type_error::create( + 305, "cannot use operator[] with a numeric argument with " + std::string(type_name()))); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + reference operator[](const typename object_t::key_type& key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (_az_JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->operator[](key); + } + + _az_JSON_THROW(type_error::create( + 305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw type_error.305 if the JSON value is not an object; in that case, + using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.0.0 + */ + const_reference operator[](const typename object_t::key_type& key) const + { + // const operator[] only works for objects + if (_az_JSON_HEDLEY_LIKELY(is_object())) + { + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + _az_JSON_THROW(type_error::create( + 305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template _az_JSON_HEDLEY_NON_NULL(2) reference operator[](T* key) + { + // implicitly convert null to object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // at only works for objects + if (_az_JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->operator[](key); + } + + _az_JSON_THROW(type_error::create( + 305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw type_error.305 if the JSON value is not an object; in that case, + using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template _az_JSON_HEDLEY_NON_NULL(2) const_reference operator[](T* key) const + { + // at only works for objects + if (_az_JSON_HEDLEY_LIKELY(is_object())) + { + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + _az_JSON_THROW(type_error::create( + 305, "cannot use operator[] with a string argument with " + std::string(type_name()))); + } + + /*! + @brief access specified object element with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(key); + } catch(out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const typename object_t::key_type&), this function + does not throw if the given key @a key was not found. + + @note Unlike @ref operator[](const typename object_t::key_type& key), this + function does not implicitly add an element to the position defined by @a + key. This function is furthermore also applicable to const objects. + + @param[in] key key of the element to access + @param[in] default_value the value to return if @a key is not found + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw type_error.302 if @a default_value does not match the type of the + value at @a key + @throw type_error.306 if the JSON value is not an object; in that case, + using `value()` with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + + @since version 1.0.0 + */ + template < + class ValueType, + typename std::enable_if< + std::is_convertible::value + and not std::is_same::value, + int>::type + = 0> + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + { + // at only works for objects + if (_az_JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return *it; + } + + return default_value; + } + + _az_JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const + */ + string_t value(const typename object_t::key_type& key, const char* default_value) const + { + return value(key, string_t(default_value)); + } + + /*! + @brief access specified object element via JSON Pointer with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(ptr); + } catch(out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const json_pointer&), this function does not throw + if the given key @a key was not found. + + @param[in] ptr a JSON pointer to the element to access + @param[in] default_value the value to return if @a ptr found no value + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw type_error.302 if @a default_value does not match the type of the + value at @a ptr + @throw type_error.306 if the JSON value is not an object; in that case, + using `value()` with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value_ptr} + + @sa @ref operator[](const json_pointer&) for unchecked access by reference + + @since version 2.0.2 + */ + template < + class ValueType, + typename std::enable_if::value, int>::type = 0> + ValueType value(const json_pointer& ptr, const ValueType& default_value) const + { + // at only works for objects + if (_az_JSON_HEDLEY_LIKELY(is_object())) + { + // if pointer resolves a value, return it or use default value + _az_JSON_TRY { return ptr.get_checked(this); } + _az_JSON_INTERNAL_CATCH(out_of_range&) { return default_value; } + } + + _az_JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()))); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const json_pointer&, ValueType) const + */ + _az_JSON_HEDLEY_NON_NULL(3) + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + + /*! + @brief access the first element + + Returns a reference to the first element in the container. For a JSON + container `c`, the expression `c.front()` is equivalent to `*c.begin()`. + + @return In case of a structured type (array or object), a reference to the + first element is returned. In case of number, string, boolean, or binary + values, a reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw invalid_iterator.214 when called on `null` value + + @liveexample{The following code shows an example for `front()`.,front} + + @sa @ref back() -- access the last element + + @since version 1.0.0 + */ + reference front() { return *begin(); } + + /*! + @copydoc basic_json::front() + */ + const_reference front() const { return *cbegin(); } + + /*! + @brief access the last element + + Returns a reference to the last element in the container. For a JSON + container `c`, the expression `c.back()` is equivalent to + @code {.cpp} + auto tmp = c.end(); + --tmp; + return *tmp; + @endcode + + @return In case of a structured type (array or object), a reference to the + last element is returned. In case of number, string, boolean, or binary + values, a reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw invalid_iterator.214 when called on a `null` value. See example + below. + + @liveexample{The following code shows an example for `back()`.,back} + + @sa @ref front() -- access the first element + + @since version 1.0.0 + */ + reference back() + { + auto tmp = end(); + --tmp; + return *tmp; + } + + /*! + @copydoc basic_json::back() + */ + const_reference back() const + { + auto tmp = cend(); + --tmp; + return *tmp; + } + + /*! + @brief remove element given an iterator + + Removes the element specified by iterator @a pos. The iterator @a pos must + be valid and dereferenceable. Thus the `end()` iterator (which is valid, + but is not dereferenceable) cannot be used as a value for @a pos. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] pos iterator to the element to remove + @return Iterator following the last removed element. If the iterator @a + pos refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.202 if called on an iterator which does not belong + to the current JSON value; example: `"iterator does not fit current + value"` + @throw invalid_iterator.205 if called on a primitive type with invalid + iterator (i.e., any iterator which is not `begin()`); example: `"iterator + out of range"` + + @complexity The complexity depends on the type: + - objects: amortized constant + - arrays: linear in distance between @a pos and the end of the container + - strings and binary: linear in the length of the member + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType} + + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template < + class IteratorType, + typename std::enable_if< + std::is_same::value + or std::is_same::value, + int>::type + = 0> + IteratorType erase(IteratorType pos) + { + // make sure iterator fits the current value + if (_az_JSON_HEDLEY_UNLIKELY(this != pos.m_object)) + { + _az_JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: { + if (_az_JSON_HEDLEY_UNLIKELY(not pos.m_it.primitive_iterator.is_begin())) { - // special case: append to back - parent.push_back(val); + _az_JSON_THROW(invalid_iterator::create(205, "iterator out of range")); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } + + default: + _az_JSON_THROW( + type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + + return result; + } + + /*! + @brief remove elements given an iterator range + + Removes the element specified by the range `[first; last)`. The iterator + @a first does not need to be dereferenceable if `first == last`: erasing + an empty range is a no-op. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] first iterator to the beginning of the range to remove + @param[in] last iterator past the end of the range to remove + @return Iterator following the last removed element. If the iterator @a + second refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.203 if called on iterators which does not belong + to the current JSON value; example: `"iterators do not fit current value"` + @throw invalid_iterator.204 if called on a primitive type with invalid + iterators (i.e., if `first != begin()` and `last != end()`); example: + `"iterators out of range"` + + @complexity The complexity depends on the type: + - objects: `log(size()) + std::distance(first, last)` + - arrays: linear in the distance between @a first and @a last, plus linear + in the distance between @a last and end of the container + - strings and binary: linear in the length of the member + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType_IteratorType} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template < + class IteratorType, + typename std::enable_if< + std::is_same::value + or std::is_same::value, + int>::type + = 0> + IteratorType erase(IteratorType first, IteratorType last) + { + // make sure iterator fits the current value + if (_az_JSON_HEDLEY_UNLIKELY(this != first.m_object or this != last.m_object)) + { + _az_JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: { + if (_az_JSON_HEDLEY_LIKELY( + not first.m_it.primitive_iterator.is_begin() + or not last.m_it.primitive_iterator.is_end())) + { + _az_JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: { + result.m_it.object_iterator + = m_value.object->erase(first.m_it.object_iterator, last.m_it.object_iterator); + break; + } + + case value_t::array: { + result.m_it.array_iterator + = m_value.array->erase(first.m_it.array_iterator, last.m_it.array_iterator); + break; + } + + default: + _az_JSON_THROW( + type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + + return result; + } + + /*! + @brief remove element from a JSON object given a key + + Removes elements from a JSON object with the key value @a key. + + @param[in] key value of the elements to remove + + @return Number of elements removed. If @a ObjectType is the default + `std::map` type, the return value will always be `0` (@a key was not + found) or `1` (@a key was found). + + @post References and iterators to the erased elements are invalidated. + Other references and iterators are not affected. + + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + + @complexity `log(size()) + count(key)` + + @liveexample{The example shows the effect of `erase()`.,erase__key_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + size_type erase(const typename object_t::key_type& key) + { + // this erase only works for objects + if (_az_JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->erase(key); + } + + _az_JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + + /*! + @brief remove element from a JSON array given an index + + Removes element from a JSON array at the index @a idx. + + @param[in] idx index of the element to remove + + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 + is out of range"` + + @complexity Linear in distance between @a idx and the end of the container. + + @liveexample{The example shows the effect of `erase()`.,erase__size_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + + @since version 1.0.0 + */ + void erase(const size_type idx) + { + // this erase only works for arrays + if (_az_JSON_HEDLEY_LIKELY(is_array())) + { + if (_az_JSON_HEDLEY_UNLIKELY(idx >= size())) + { + _az_JSON_THROW( + out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + + m_value.array->erase(m_value.array->begin() + static_cast(idx)); + } + else + { + _az_JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()))); + } + } + + /// @} + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + + /*! + @brief find an element in a JSON object + + Finds an element in a JSON object with key equivalent to @a key. If the + element is not found or the JSON value is not an object, end() is + returned. + + @note This method always returns @ref end() when executed on a JSON type + that is not an object. + + @param[in] key key value of the element to search for. + + @return Iterator to an element with key equivalent to @a key. If no such + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `find()` is used.,find__key_type} + + @sa @ref contains(KeyT&&) const -- checks whether a key exists + + @since version 1.0.0 + */ + template iterator find(KeyT&& key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /*! + @brief find an element in a JSON object + @copydoc find(KeyT&&) + */ + template const_iterator find(KeyT&& key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /*! + @brief returns the number of occurrences of a key in a JSON object + + Returns the number of elements with key @a key. If ObjectType is the + default `std::map` type, the return value will always be `0` (@a key was + not found) or `1` (@a key was found). + + @note This method always returns `0` when executed on a JSON type that is + not an object. + + @param[in] key key value of the element to count + + @return Number of elements with key @a key. If the JSON value is not an + object, the return value will be `0`. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `count()` is used.,count} + + @since version 1.0.0 + */ + template size_type count(KeyT&& key) const + { + // return 0 for all nonobject types + return is_object() ? m_value.object->count(std::forward(key)) : 0; + } + + /*! + @brief check the existence of an element in a JSON object + + Check whether an element exists in a JSON object with key equivalent to + @a key. If the element is not found or the JSON value is not an object, + false is returned. + + @note This method always returns false when executed on a JSON type + that is not an object. + + @param[in] key key value to check its existence. + + @return true if an element with specified @a key exists. If no such + element with such key is found or the JSON value is not an object, + false is returned. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The following code shows an example for `contains()`.,contains} + + @sa @ref find(KeyT&&) -- returns an iterator to an object element + @sa @ref contains(const json_pointer&) const -- checks the existence for a JSON pointer + + @since version 3.6.0 + */ + template < + typename KeyT, + typename std::enable_if< + not std::is_same::type, json_pointer>::value, + int>::type + = 0> + bool contains(KeyT&& key) const + { + return is_object() and m_value.object->find(std::forward(key)) != m_value.object->end(); + } + + /*! + @brief check the existence of an element in a JSON object given a JSON pointer + + Check whether the given JSON pointer @a ptr can be resolved in the current + JSON value. + + @note This method can be executed on any JSON value type. + + @param[in] ptr JSON pointer to check its existence. + + @return true if the JSON pointer can be resolved to a stored value, false + otherwise. + + @post If `j.contains(ptr)` returns true, it is safe to call `j[ptr]`. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The following code shows an example for `contains()`.,contains_json_pointer} + + @sa @ref contains(KeyT &&) const -- checks the existence of a key + + @since version 3.7.0 + */ + bool contains(const json_pointer& ptr) const { return ptr.contains(this); } + + /// @} + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + + /*! + @brief returns an iterator to the first element + + Returns an iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `begin()`.,begin} + + @sa @ref cbegin() -- returns a const iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /*! + @copydoc basic_json::cbegin() + */ + const_iterator begin() const noexcept { return cbegin(); } + + /*! + @brief returns a const iterator to the first element + + Returns a const iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).begin()`. + + @liveexample{The following code shows an example for `cbegin()`.,cbegin} + + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /*! + @brief returns an iterator to one past the last element + + Returns an iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `end()`.,end} + + @sa @ref cend() -- returns a const iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /*! + @copydoc basic_json::cend() + */ + const_iterator end() const noexcept { return cend(); } + + /*! + @brief returns a const iterator to one past the last element + + Returns a const iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).end()`. + + @liveexample{The following code shows an example for `cend()`.,cend} + + @sa @ref end() -- returns an iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /*! + @brief returns an iterator to the reverse-beginning + + Returns an iterator to the reverse-beginning; that is, the last element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(end())`. + + @liveexample{The following code shows an example for `rbegin()`.,rbegin} + + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + + /*! + @copydoc basic_json::crbegin() + */ + const_reverse_iterator rbegin() const noexcept { return crbegin(); } + + /*! + @brief returns an iterator to the reverse-end + + Returns an iterator to the reverse-end; that is, one before the first + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(begin())`. + + @liveexample{The following code shows an example for `rend()`.,rend} + + @sa @ref crend() -- returns a const reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + + /*! + @copydoc basic_json::crend() + */ + const_reverse_iterator rend() const noexcept { return crend(); } + + /*! + @brief returns a const reverse iterator to the last element + + Returns a const iterator to the reverse-beginning; that is, the last + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rbegin()`. + + @liveexample{The following code shows an example for `crbegin()`.,crbegin} + + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } + + /*! + @brief returns a const reverse iterator to one before the first + + Returns a const reverse iterator to the reverse-end; that is, one before + the first element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rend()`. + + @liveexample{The following code shows an example for `crend()`.,crend} + + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } + + public: + /*! + @brief wrapper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + For loop without iterator_wrapper: + + @code{cpp} + for (auto it = j_object.begin(); it != j_object.end(); ++it) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + Range-based for loop without iterator proxy: + + @code{cpp} + for (auto it : j_object) + { + // "it" is of type json::reference and has no key() member + std::cout << "value: " << it << '\n'; + } + @endcode + + Range-based for loop with iterator proxy: + + @code{cpp} + for (auto it : json::iterator_wrapper(j_object)) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + @note When iterating over an array, `key()` will return the index of the + element as string (see example). + + @param[in] ref reference to a JSON value + @return iteration proxy object wrapping @a ref with an interface to use in + range-based for loops + + @liveexample{The following code shows how the wrapper is used,iterator_wrapper} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @note The name of this function is not yet final and may change in the + future. + + @deprecated This stream operator is deprecated and will be removed in + future 4.0.0 of the library. Please use @ref items() instead; + that is, replace `json::iterator_wrapper(j)` with `j.items()`. + */ + _az_JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(reference ref) noexcept + { + return ref.items(); + } + + /*! + @copydoc iterator_wrapper(reference) + */ + _az_JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(const_reference ref) noexcept + { + return ref.items(); + } + + /*! + @brief helper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + For loop without `items()` function: + + @code{cpp} + for (auto it = j_object.begin(); it != j_object.end(); ++it) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + Range-based for loop without `items()` function: + + @code{cpp} + for (auto it : j_object) + { + // "it" is of type json::reference and has no key() member + std::cout << "value: " << it << '\n'; + } + @endcode + + Range-based for loop with `items()` function: + + @code{cpp} + for (auto& el : j_object.items()) + { + std::cout << "key: " << el.key() << ", value:" << el.value() << '\n'; + } + @endcode + + The `items()` function also allows to use + [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding) + (C++17): + + @code{cpp} + for (auto& [key, val] : j_object.items()) + { + std::cout << "key: " << key << ", value:" << val << '\n'; + } + @endcode + + @note When iterating over an array, `key()` will return the index of the + 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 + + @liveexample{The following code shows how the function is used.,items} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 3.1.0, structured bindings support since 3.5.0. + */ + iteration_proxy items() noexcept { return iteration_proxy(*this); } + + /*! + @copydoc items() + */ + iteration_proxy items() const noexcept + { + return iteration_proxy(*this); + } + + /// @} + + ////////////// + // capacity // + ////////////// + + /// @name capacity + /// @{ + + /*! + @brief checks whether the container is empty. + + Checks if a JSON value has no elements (i.e. whether its @ref size is `0`). + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `true` + boolean | `false` + string | `false` + number | `false` + binary | `false` + object | result of function `object_t::empty()` + array | result of function `array_t::empty()` + + @liveexample{The following code uses `empty()` to check if a JSON + object contains any elements.,empty} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `empty()` functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @note This function does not return whether a string stored as JSON value + is empty - it returns whether the JSON container itself is empty which is + false in the case of a string. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `begin() == end()`. + + @sa @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + bool empty() const noexcept + { + switch (m_type) + { + case value_t::null: { + // null values are empty + return true; + } + + case value_t::array: { + // delegate call to array_t::empty() + return m_value.array->empty(); + } + + case value_t::object: { + // delegate call to object_t::empty() + return m_value.object->empty(); + } + + default: { + // all other types are nonempty + return false; + } + } + } + + /*! + @brief returns the number of elements + + Returns the number of elements in a JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` + boolean | `1` + string | `1` + number | `1` + binary | `1` + object | result of function object_t::size() + array | result of function array_t::size() + + @liveexample{The following code calls `size()` on the different value + types.,size} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their size() functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @note This function does not return the length of a string stored as JSON + value - it returns the number of elements in the JSON value which is 1 in + the case of a string. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `std::distance(begin(), end())`. + + @sa @ref empty() -- checks whether the container is empty + @sa @ref max_size() -- returns the maximal number of elements + + @since version 1.0.0 + */ + size_type size() const noexcept + { + switch (m_type) + { + case value_t::null: { + // null values are empty + return 0; + } + + case value_t::array: { + // delegate call to array_t::size() + return m_value.array->size(); + } + + case value_t::object: { + // delegate call to object_t::size() + return m_value.object->size(); + } + + default: { + // all other types have size 1 + return 1; + } + } + } + + /*! + @brief returns the maximum possible number of elements + + Returns the maximum number of elements a JSON value is able to hold due to + system or library implementation limitations, i.e. `std::distance(begin(), + end())` for the JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` (same as `size()`) + boolean | `1` (same as `size()`) + string | `1` (same as `size()`) + number | `1` (same as `size()`) + binary | `1` (same as `size()`) + object | result of function `object_t::max_size()` + array | result of function `array_t::max_size()` + + @liveexample{The following code calls `max_size()` on the different value + types. Note the output is implementation specific.,max_size} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `max_size()` functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of returning `b.size()` where `b` is the largest + possible JSON value. + + @sa @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + size_type max_size() const noexcept + { + switch (m_type) + { + case value_t::array: { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } + + case value_t::object: { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } + + default: { + // all other types have max_size() == size() + return size(); + } + } + } + + /// @} + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + + /*! + @brief clears the contents + + Clears the content of a JSON value and resets it to the default value as + if @ref basic_json(value_t) would have been called with the current value + type from @ref type(): + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + binary | An empty byte vector + object | `{}` + array | `[]` + + @post Has the same effect as calling + @code {.cpp} + *this = basic_json(type()); + @endcode + + @liveexample{The example below shows the effect of `clear()` to different + JSON types.,clear} + + @complexity Linear in the size of the JSON value. + + @iterators All iterators, pointers and references related to this container + are invalidated. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @sa @ref basic_json(value_t) -- constructor that creates an object with the + same value than calling `clear()` + + @since version 1.0.0 + */ + void clear() noexcept + { + switch (m_type) + { + case value_t::number_integer: { + m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: { + m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: { + m_value.number_float = 0.0; + break; + } + + case value_t::boolean: { + m_value.boolean = false; + break; + } + + case value_t::string: { + m_value.string->clear(); + break; + } + + case value_t::binary: { + m_value.binary->clear(); + break; + } + + case value_t::array: { + m_value.array->clear(); + break; + } + + case value_t::object: { + m_value.object->clear(); + break; + } + + default: + break; + } + } + + /*! + @brief add an object to an array + + Appends the given element @a val to the end of the JSON value. If the + function is called on a JSON null value, an empty array is created before + appending @a val. + + @param[in] val the value to add to the JSON array + + @throw type_error.308 when called on a type other than JSON array or + null; example: `"cannot use push_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON array. Note how the `null` value was silently + converted to a JSON array.,push_back} + + @since version 1.0.0 + */ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (_az_JSON_HEDLEY_UNLIKELY(not(is_null() or is_array()))) + { + _az_JSON_THROW( + type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (move semantics) + m_value.array->push_back(std::move(val)); + // if val is moved from, basic_json move constructor marks it null so we do not call the + // destructor + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (_az_JSON_HEDLEY_UNLIKELY(not(is_null() or is_array()))) + { + _az_JSON_THROW( + type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array + m_value.array->push_back(val); + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + Inserts the given element @a val to the JSON object. If the function is + called on a JSON null value, an empty object is created before inserting + @a val. + + @param[in] val the value to add to the JSON object + + @throw type_error.308 when called on a type other than JSON object or + null; example: `"cannot use push_back() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON object. Note how the `null` value was silently + converted to a JSON object.,push_back__object_t__value} + + @since version 1.0.0 + */ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (_az_JSON_HEDLEY_UNLIKELY(not(is_null() or is_object()))) + { + _az_JSON_THROW( + type_error::create(308, "cannot use push_back() with " + std::string(type_name()))); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array + m_value.object->insert(val); + } + + /*! + @brief add an object to an object + @copydoc push_back(const typename object_t::value_type&) + */ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + This function allows to use `push_back` with an initializer list. In case + + 1. the current value is an object, + 2. the initializer list @a init contains only two elements, and + 3. the first element of @a init is a string, + + @a init is converted into an object element and added using + @ref push_back(const typename object_t::value_type&). Otherwise, @a init + is converted to a JSON value and added using @ref push_back(basic_json&&). + + @param[in] init an initializer list + + @complexity Linear in the size of the initializer list @a init. + + @note This function is required to resolve an ambiguous overload error, + because pairs like `{"key", "value"}` can be both interpreted as + `object_t::value_type` or `std::initializer_list`, see + https://github.com/nlohmann/json/issues/235 for more information. + + @liveexample{The example shows how initializer lists are treated as + objects when possible.,push_back__initializer_list} + */ + void push_back(initializer_list_t init) + { + if (is_object() and init.size() == 2 and (*init.begin())->is_string()) + { + basic_json&& key = init.begin()->moved_or_copied(); + push_back(typename object_t::value_type( + std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); + } + else + { + push_back(basic_json(init)); + } + } + + /*! + @brief add an object to an object + @copydoc push_back(initializer_list_t) + */ + reference operator+=(initializer_list_t init) + { + push_back(init); + return *this; + } + + /*! + @brief add an object to an array + + Creates a JSON value from the passed parameters @a args to the end of the + JSON value. If the function is called on a JSON null value, an empty array + is created before appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return reference to the inserted element + + @throw type_error.311 when called on a type other than JSON array or + null; example: `"cannot use emplace_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` can be used to add + elements to a JSON array. Note how the `null` value was silently converted + to a JSON array.,emplace_back} + + @since version 2.0.8, returns reference since 3.7.0 + */ + template reference emplace_back(Args&&... args) + { + // emplace_back only works for null objects or arrays + if (_az_JSON_HEDLEY_UNLIKELY(not(is_null() or is_array()))) + { + _az_JSON_THROW( + type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()))); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) +#ifdef _az_JSON_HAS_CPP_17 + return m_value.array->emplace_back(std::forward(args)...); +#else + m_value.array->emplace_back(std::forward(args)...); + return m_value.array->back(); +#endif + } + + /*! + @brief add an object to an object if key does not exist + + Inserts a new element into a JSON object constructed in-place with the + given @a args if there is no element with the key in the container. If the + function is called on a JSON null value, an empty object is created before + appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return a pair consisting of an iterator to the inserted element, or the + already-existing element if no insertion happened, and a bool + denoting whether the insertion took place. + + @throw type_error.311 when called on a type other than JSON object or + null; example: `"cannot use emplace() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `emplace()` can be used to add elements + to a JSON object. Note how the `null` value was silently converted to a + JSON object. Further note how no value is added if there was already one + value stored with the same key.,emplace} + + @since version 2.0.8 + */ + template std::pair emplace(Args&&... args) + { + // emplace only works for null objects or arrays + if (_az_JSON_HEDLEY_UNLIKELY(not(is_null() or is_object()))) + { + _az_JSON_THROW( + type_error::create(311, "cannot use emplace() with " + std::string(type_name()))); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward(args)...); + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; + } + + /// Helper for insertion of an iterator + /// @note: This uses std::distance to support GCC 4.8, + /// see https://github.com/nlohmann/json/pull/1257 + template iterator insert_iterator(const_iterator pos, Args&&... args) + { + iterator result(this); + assert(m_value.array != nullptr); + + auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); + m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); + result.m_it.array_iterator = m_value.array->begin() + insert_pos; + + // This could have been written as: + // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + // but the return value of insert is missing in GCC 4.8, so it is written this way instead. + + return result; + } + + /*! + @brief inserts element + + Inserts element @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] val element to insert + @return iterator pointing to the inserted @a val. + + @throw type_error.309 if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @complexity Constant plus linear in the distance between @a pos and end of + the container. + + @liveexample{The example shows how `insert()` is used.,insert} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (_az_JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (_az_JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + _az_JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + return insert_iterator(pos, val); + } + + _az_JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + /*! + @brief inserts element + @copydoc insert(const_iterator, const basic_json&) + */ + iterator insert(const_iterator pos, basic_json&& val) { return insert(pos, val); } + + /*! + @brief inserts elements + + Inserts @a cnt copies of @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] cnt number of copies of @a val to insert + @param[in] val element to insert + @return iterator pointing to the first element inserted, or @a pos if + `cnt==0` + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @complexity Linear in @a cnt plus linear in the distance between @a pos + and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__count} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + { + // insert only works for arrays + if (_az_JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (_az_JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + _az_JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + return insert_iterator(pos, cnt, val); + } + + _az_JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)` before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + @throw invalid_iterator.211 if @a first or @a last are iterators into + container for which insert is called; example: `"passed iterators may not + belong to container"` + + @return iterator pointing to the first element inserted, or @a pos if + `first==last` + + @complexity Linear in `std::distance(first, last)` plus linear in the + distance between @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__range} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (_az_JSON_HEDLEY_UNLIKELY(not is_array())) + { + _az_JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + // check if iterator pos fits to this JSON value + if (_az_JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + _az_JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // check if range iterators belong to the same JSON object + if (_az_JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + _az_JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } + + if (_az_JSON_HEDLEY_UNLIKELY(first.m_object == this)) + { + _az_JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); + } + + // insert to array and return iterator + return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + } + + /*! + @brief inserts elements + + Inserts elements from initializer list @a ilist before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] ilist initializer list to insert the values from + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @return iterator pointing to the first element inserted, or @a pos if + `ilist` is empty + + @complexity Linear in `ilist.size()` plus linear in the distance between + @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__ilist} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, initializer_list_t ilist) + { + // insert only works for arrays + if (_az_JSON_HEDLEY_UNLIKELY(not is_array())) + { + _az_JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + // check if iterator pos fits to this JSON value + if (_az_JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + _az_JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + return insert_iterator(pos, ilist.begin(), ilist.end()); + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)`. + + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.309 if called on JSON values other than objects; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + + @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number + of elements to insert. + + @liveexample{The example shows how `insert()` is used.,insert__range_object} + + @since version 3.0.0 + */ + void insert(const_iterator first, const_iterator last) + { + // insert only works for objects + if (_az_JSON_HEDLEY_UNLIKELY(not is_object())) + { + _az_JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()))); + } + + // check if range iterators belong to the same JSON object + if (_az_JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + _az_JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } + + // passed iterators must belong to objects + if (_az_JSON_HEDLEY_UNLIKELY(not first.m_object->is_object())) + { + _az_JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + } + + m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + } + + /*! + @brief updates a JSON object from another object, overwriting existing keys + + Inserts all values from JSON object @a j and overwrites existing keys. + + @param[in] j JSON object to read values from + + @throw type_error.312 if called on JSON values other than objects; example: + `"cannot use update() with string"` + + @complexity O(N*log(size() + N)), where N is the number of elements to + insert. + + @liveexample{The example shows how `update()` is used.,update} + + @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update + + @since version 3.0.0 + */ + void update(const_reference j) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (_az_JSON_HEDLEY_UNLIKELY(not is_object())) + { + _az_JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + } + if (_az_JSON_HEDLEY_UNLIKELY(not j.is_object())) + { + _az_JSON_THROW( + type_error::create(312, "cannot use update() with " + std::string(j.type_name()))); + } + + for (auto it = j.cbegin(); it != j.cend(); ++it) + { + m_value.object->operator[](it.key()) = it.value(); + } + } + + /*! + @brief updates a JSON object from another object, overwriting existing keys + + Inserts all values from from range `[first, last)` and overwrites existing + keys. + + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.312 if called on JSON values other than objects; example: + `"cannot use update() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + + @complexity O(N*log(size() + N)), where N is the number of elements to + insert. + + @liveexample{The example shows how `update()` is used__range.,update} + + @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update + + @since version 3.0.0 + */ + void update(const_iterator first, const_iterator last) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (_az_JSON_HEDLEY_UNLIKELY(not is_object())) + { + _az_JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()))); + } + + // check if range iterators belong to the same JSON object + if (_az_JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + _az_JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } + + // passed iterators must belong to objects + if (_az_JSON_HEDLEY_UNLIKELY(not first.m_object->is_object() or not last.m_object->is_object())) + { + _az_JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + } + + for (auto it = first; it != last; ++it) + { + m_value.object->operator[](it.key()) = it.value(); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of the JSON value 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 JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + void swap(reference other) noexcept( + std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable< + value_t>::value and std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value) + { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + assert_invariant(); + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON array with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other array to exchange the contents with + + @throw type_error.310 when JSON value is not an array; example: `"cannot + use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how arrays can be swapped with + `swap()`.,swap__array_t} + + @since version 1.0.0 + */ + void swap(array_t& other) + { + // swap only works for arrays + if (_az_JSON_HEDLEY_LIKELY(is_array())) + { + std::swap(*(m_value.array), other); + } + else + { + _az_JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON object 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 object to exchange the contents with + + @throw type_error.310 when JSON value is not an object; example: + `"cannot use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how objects can be swapped with + `swap()`.,swap__object_t} + + @since version 1.0.0 + */ + void swap(object_t& other) + { + // swap only works for objects + if (_az_JSON_HEDLEY_LIKELY(is_object())) + { + std::swap(*(m_value.object), other); + } + else + { + _az_JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /*! + @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 string 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__string_t} + + @since version 1.0.0 + */ + void swap(string_t& other) + { + // swap only works for strings + if (_az_JSON_HEDLEY_LIKELY(is_string())) + { + std::swap(*(m_value.string), other); + } + else + { + _az_JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /*! + @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 (_az_JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + _az_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 (_az_JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + _az_JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()))); + } + } + + /// @} + + public: + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// @name lexicographical comparison operators + /// @{ + + /*! + @brief comparison: equal + + Compares two JSON values for equality according to the following rules: + - Two JSON values are equal if (1) they are from the same type and (2) + their stored values are the same according to their respective + `operator==`. + - Integer and floating-point numbers are automatically converted before + comparison. Note that two NaN values are always treated as unequal. + - Two JSON null values are equal. + + @note Floating-point inside JSON values numbers are compared with + `json::number_float_t::operator==` which is `double::operator==` by + default. To compare floating-point while respecting an epsilon, an alternative + [comparison + function](https://github.com/mariokonrad/marnav/blob/master/include/marnav/math/floatingpoint.hpp#L34-#L39) + could be used, for instance + @code {.cpp} + template::value, + T>::type> inline bool is_same(T a, T b, T epsilon = std::numeric_limits::epsilon()) noexcept + { + return std::abs(a - b) <= epsilon; + } + @endcode + Or you can self-defined operator equal function like this: + @code {.cpp} + bool my_equal(const_reference lhs, const_reference rhs) { + const auto lhs_type lhs.type(); + const auto rhs_type rhs.type(); + if (lhs_type == rhs_type) { + switch(lhs_type) + // self_defined case + case value_t::number_float: + return std::abs(lhs - rhs) <= std::numeric_limits::epsilon(); + // other cases remain the same with the original + ... + } + ... + } + @endcode + + @note NaN values never compare equal to themselves or to other NaN values. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are equal + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__equal} + + @since version 1.0.0 + */ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + return *lhs.m_value.array == *rhs.m_value.array; + + case value_t::object: + return *lhs.m_value.object == *rhs.m_value.object; + + case value_t::null: + return true; + + case value_t::string: + return *lhs.m_value.string == *rhs.m_value.string; + + case value_t::boolean: + return lhs.m_value.boolean == rhs.m_value.boolean; + + case value_t::number_integer: + return lhs.m_value.number_integer == rhs.m_value.number_integer; + + case value_t::number_unsigned: + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + + case value_t::number_float: + return lhs.m_value.number_float == rhs.m_value.number_float; + + case value_t::binary: + return *lhs.m_value.binary == *rhs.m_value.binary; + + default: + return false; + } + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) + == rhs.m_value.number_integer; + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer + == static_cast(rhs.m_value.number_unsigned); + } + + return false; + } + + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template < + typename ScalarType, + typename std::enable_if::value, int>::type = 0> + friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs == basic_json(rhs); + } + + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template < + typename ScalarType, + typename std::enable_if::value, int>::type = 0> + friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) == rhs; + } + + /*! + @brief comparison: not equal + + Compares two JSON values for inequality by calculating `not (lhs == rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are not equal + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__notequal} + + @since version 1.0.0 + */ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + return not(lhs == rhs); + } + + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template < + typename ScalarType, + typename std::enable_if::value, int>::type = 0> + friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs != basic_json(rhs); + } + + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template < + typename ScalarType, + typename std::enable_if::value, int>::type = 0> + friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) != rhs; + } + + /*! + @brief comparison: less than + + Compares whether one JSON value @a lhs is less than another JSON value @a + rhs according to the following rules: + - If @a lhs and @a rhs have the same type, the values are compared using + the default `<` operator. + - Integer and floating-point numbers are automatically converted before + comparison + - In case @a lhs and @a rhs have different types, the values are ignored + and the order of the types is considered, see + @ref operator<(const value_t, const value_t). + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__less} + + @since version 1.0.0 + */ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + // note parentheses are necessary, see + // https://github.com/nlohmann/json/issues/1530 + return (*lhs.m_value.array) < (*rhs.m_value.array); + + case value_t::object: + return (*lhs.m_value.object) < (*rhs.m_value.object); + + case value_t::null: + return false; + + case value_t::string: + return (*lhs.m_value.string) < (*rhs.m_value.string); + + case value_t::boolean: + return (lhs.m_value.boolean) < (rhs.m_value.boolean); + + case value_t::number_integer: + return (lhs.m_value.number_integer) < (rhs.m_value.number_integer); + + case value_t::number_unsigned: + return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned); + + case value_t::number_float: + return (lhs.m_value.number_float) < (rhs.m_value.number_float); + + case value_t::binary: + return (*lhs.m_value.binary) < (*rhs.m_value.binary); + + default: + return false; + } + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer + < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) + < rhs.m_value.number_integer; + } + + // We only reach this line if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + return operator<(lhs_type, rhs_type); + } + + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template < + typename ScalarType, + typename std::enable_if::value, int>::type = 0> + friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs < basic_json(rhs); + } + + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template < + typename ScalarType, + typename std::enable_if::value, int>::type = 0> + friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) < rhs; + } + + /*! + @brief comparison: less than or equal + + Compares whether one JSON value @a lhs is less than or equal to another + JSON value by calculating `not (rhs < lhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than or equal to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greater} + + @since version 1.0.0 + */ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + return not(rhs < lhs); + } + + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template < + typename ScalarType, + typename std::enable_if::value, int>::type = 0> + friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs <= basic_json(rhs); + } + + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template < + typename ScalarType, + typename std::enable_if::value, int>::type = 0> + friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) <= rhs; + } + + /*! + @brief comparison: greater than + + Compares whether one JSON value @a lhs is greater than another + JSON value by calculating `not (lhs <= rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__lessequal} + + @since version 1.0.0 + */ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + return not(lhs <= rhs); + } + + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template < + typename ScalarType, + typename std::enable_if::value, int>::type = 0> + friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs > basic_json(rhs); + } + + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template < + typename ScalarType, + typename std::enable_if::value, int>::type = 0> + friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) > rhs; + } + + /*! + @brief comparison: greater than or equal + + Compares whether one JSON value @a lhs is greater than or equal to another + JSON value by calculating `not (lhs < rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than or equal to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greaterequal} + + @since version 1.0.0 + */ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + return not(lhs < rhs); + } + + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template < + typename ScalarType, + typename std::enable_if::value, int>::type = 0> + friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept + { + return lhs >= basic_json(rhs); + } + + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template < + typename ScalarType, + typename std::enable_if::value, int>::type = 0> + friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) >= rhs; + } + + /// @} + + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ + + /*! + @brief serialize to stream + + Serialize the given JSON value @a j to the output stream @a o. The JSON + value will be serialized using the @ref dump member function. + + - The indentation of the output can be controlled with the member variable + `width` of the output stream @a o. For instance, using the manipulator + `std::setw(4)` on @a o sets the indentation level to `4` and the + serialization result is the same as calling `dump(4)`. + + - The indentation character can be controlled with the member variable + `fill` of the output stream @a o. For instance, the manipulator + `std::setfill('\\t')` sets indentation to use a tab character rather than + the default space character. + + @param[in,out] o stream to serialize to + @param[in] j JSON value to serialize + + @return the stream @a o + + @throw type_error.316 if a string stored inside the JSON value is not + UTF-8 encoded + + @complexity Linear. + + @liveexample{The example below shows the serialization with different + parameters to `width` to adjust the indentation level.,operator_serialize} + + @since version 1.0.0; indentation character added in version 3.0.0 + */ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = o.width() > 0; + const auto indentation = pretty_print ? o.width() : 0; + + // reset width to 0 for subsequent calls to this stream + o.width(0); + + // do the actual serialization + serializer s(detail::output_adapter(o), o.fill()); + s.dump(j, pretty_print, false, static_cast(indentation)); + return o; + } + + /*! + @brief serialize to stream + @deprecated This stream operator is deprecated and will be removed in + future 4.0.0 of the library. Please use + @ref operator<<(std::ostream&, const basic_json&) + instead; that is, replace calls like `j >> o;` with `o << j;`. + @since version 1.0.0; deprecated since version 3.0.0 + */ + _az_JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&)) + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) { return o << j; } + + /// @} + + ///////////////////// + // deserialization // + ///////////////////// + + /// @name deserialization + /// @{ + + /*! + @brief deserialize from a compatible input + + @tparam InputType A compatible input, for instance + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. + + @param[in] i input to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb or reading from the input @a i has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__istream__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 (contiguous containers) + */ + template + _az_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; + } + + /*! + @brief deserialize from a pair of character iterators + + The value_type of the iterator must be a integral type with size of 1, 2 or + 4 bytes, which will be interpreted respectively as UTF-8, UTF-16 and UTF-32. + + @param[in] first iterator to start of character range + @param[in] last iterator to end of character range + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + template + _az_JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json parse( + IteratorType first, + IteratorType last, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) + { + basic_json result; + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions) + .parse(true, result); + return result; + } + + _az_JSON_HEDLEY_WARN_UNUSED_RESULT + _az_JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) + static basic_json parse( + detail::span_input_adapter&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true) + { + basic_json result; + parser(i.get(), cb, allow_exceptions).parse(true, result); + return result; + } + + /*! + @brief check if the input is valid JSON + + Unlike the @ref parse(InputType&&, const parser_callback_t,const bool) + function, this function neither throws an exception in case of invalid JSON + input (i.e., a parse error) nor creates diagnostic information. + + @tparam InputType A compatible input, for instance + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. + + @param[in] i input to read from + + @return Whether the input read from @a i is valid JSON. + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `accept()` function reading + from a string.,accept__string} + */ + template static bool accept(InputType&& i) + { + return parser(detail::input_adapter(std::forward(i))).accept(true); + } + + template static bool accept(IteratorType first, IteratorType last) + { + return parser(detail::input_adapter(std::move(first), std::move(last))).accept(true); + } + + _az_JSON_HEDLEY_WARN_UNUSED_RESULT + _az_JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) + static bool accept(detail::span_input_adapter&& i) { return parser(i.get()).accept(true); } + + /*! + @brief generate SAX events + + The SAX event lister must follow the interface of @ref json_sax. + + This function reads from a compatible input. Examples are: + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. + + @param[in] i input to read from + @param[in,out] sax SAX event listener + @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) + @param[in] strict whether the input has to be consumed completely + + @return return value of the last processed SAX event + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the SAX consumer @a sax has + a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `sax_parse()` function + reading from string and processing the events with a user-defined SAX + event consumer.,sax_parse} + + @since version 3.2.0 + */ + template + _az_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 + _az_JSON_HEDLEY_NON_NULL(3) + static bool sax_parse( + IteratorType first, + IteratorType last, + SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true) + { + auto ia = detail::input_adapter(std::move(first), std::move(last)); + return format == input_format_t::json + ? parser(std::move(ia)).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)) + .sax_parse(format, sax, strict); + } + + template + _az_JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) + _az_JSON_HEDLEY_NON_NULL(2) static bool sax_parse( + detail::span_input_adapter&& i, + SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true) + { + auto ia = i.get(); + 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); + } + + /*! + @brief deserialize from stream + @deprecated This stream operator is deprecated and will be removed in + version 4.0.0 of the library. Please use + @ref operator>>(std::istream&, basic_json&) + instead; that is, replace calls like `j << i;` with `i >> j;`. + @since version 1.0.0; deprecated since version 3.0.0 + */ + _az_JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&)) + friend std::istream& operator<<(basic_json& j, std::istream& i) { return operator>>(i, j); } + + /*! + @brief deserialize from stream + + Deserializes an input stream to a JSON value. + + @param[in,out] i input stream to read a serialized JSON value from + @param[in,out] j JSON value to write the deserialized input to + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below shows how a JSON value is constructed by + reading a serialization from a stream.,operator_deserialize} + + @sa parse(std::istream&, const parser_callback_t) for a variant with a + parser callback function to filter values while parsing + + @since version 1.0.0 + */ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + parser(detail::input_adapter(i)).parse(false, j); + return i; + } + + /// @} + + /////////////////////////// + // convenience functions // + /////////////////////////// + + /*! + @brief return the type as string + + Returns the type name as string to be used in error messages - usually to + indicate that a function was called on a wrong JSON type. + + @return a string representation of a the @a m_type member: + Value type | return value + ----------- | ------------- + null | `"null"` + boolean | `"boolean"` + string | `"string"` + number | `"number"` (for all number types) + object | `"object"` + array | `"array"` + binary | `"binary"` + discarded | `"discarded"` + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @complexity Constant. + + @liveexample{The following code exemplifies `type_name()` for all JSON + types.,type_name} + + @sa @ref type() -- return the type of the JSON value + @sa @ref operator value_t() -- return the type of the JSON value (implicit) + + @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept` + since 3.0.0 + */ + _az_JSON_HEDLEY_RETURNS_NON_NULL + const char* type_name() const noexcept + { + { + switch (m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::binary: + return "binary"; + case value_t::discarded: + return "discarded"; + default: + return "number"; + } + } + } + + private: + ////////////////////// + // member variables // + ////////////////////// + + /// the type of the current element + value_t m_type = value_t::null; + + /// the value of the current element + json_value m_value = {}; + + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ + + public: + /*! + @brief create a CBOR serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the CBOR (Concise + Binary Object Representation) serialization format. CBOR is a binary + serialization format which aims to be more compact than JSON itself, yet + more efficient to parse. + + The library uses the following mapping from JSON values types to + CBOR types according to the CBOR specification (RFC 7049): + + JSON value type | value/range | CBOR type | first byte + --------------- | ------------------------------------------ | + ---------------------------------- | --------------- null | `null` | Null | 0xF6 + boolean | `true` | True | 0xF5 boolean | + `false` | False | 0xF4 + number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) + | 0x3B number_integer | -2147483648..-32769 | Negative integer (4 bytes + follow) | 0x3A number_integer | -32768..-129 | Negative integer + (2 bytes follow) | 0x39 number_integer | -128..-25 | Negative + integer (1 byte follow) | 0x38 number_integer | -24..-1 | + Negative integer | 0x20..0x37 number_integer | 0..23 | Integer | 0x00..0x17 + number_integer | 24..255 | Unsigned integer (1 byte follow) + | 0x18 number_integer | 256..65535 | Unsigned integer (2 bytes + follow) | 0x19 number_integer | 65536..4294967295 | Unsigned integer + (4 bytes follow) | 0x1A number_integer | 4294967296..18446744073709551615 | Unsigned + integer (8 bytes follow) | 0x1B number_unsigned | 0..23 | + Integer | 0x00..0x17 number_unsigned | 24..255 | Unsigned integer (1 + byte follow) | 0x18 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 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 string | *length*: 65536..4294967295 | UTF-8 string + (4 bytes follow) | 0x7A string | *length*: 4294967296..18446744073709551615 | + UTF-8 string (8 bytes follow) | 0x7B array | *size*: 0..23 | array | 0x80..0x97 + array | *size*: 23..255 | array (1 byte follow) | 0x98 + array | *size*: 256..65535 | array (2 bytes follow) | 0x99 + array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9A + array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9B + object | *size*: 0..23 | map | + 0xA0..0xB7 object | *size*: 23..255 | map (1 byte follow) | + 0xB8 object | *size*: 256..65535 | map (2 bytes follow) | + 0xB9 object | *size*: 65536..4294967295 | map (4 bytes follow) | + 0xBA object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | + 0xBB binary | *size*: 0..23 | byte string | + 0x40..0x57 binary | *size*: 23..255 | byte string (1 byte + follow) | 0x58 binary | *size*: 256..65535 | byte string (2 + bytes follow) | 0x59 binary | *size*: 65536..4294967295 | byte string + (4 bytes follow) | 0x5A binary | *size*: 4294967296..18446744073709551615 | byte + string (8 bytes follow) | 0x5B + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a CBOR value. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @note The following CBOR types are not used in the conversion: + - UTF-8 strings terminated by "break" (0x7F) + - arrays terminated by "break" (0x9F) + - maps terminated by "break" (0xBF) + - byte strings terminated by "break" (0x5F) + - date/time (0xC0..0xC1) + - bignum (0xC2..0xC3) + - decimal fraction (0xC4) + - bigfloat (0xC5) + - tagged items (0xC6..0xD4, 0xD8..0xDB) + - expected conversions (0xD5..0xD7) + - simple values (0xE0..0xF3, 0xF8) + - undefined (0xF7) + - half-precision floats (0xF9) + - break (0xFF) + + @param[in] j JSON value to serialize + @return CBOR serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in CBOR format.,to_cbor} + + @sa http://cbor.io + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the + analogous deserialization + @sa @ref to_msgpack(const basic_json&) for the related MessagePack format + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + + @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) + { + std::vector result; + to_cbor(j, result); + return result; + } + + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the MessagePack + serialization format. MessagePack is a binary serialization format which + aims to be more compact than JSON itself, yet more efficient to parse. + + The library uses the following mapping from JSON values types to + MessagePack types according to the MessagePack specification: + + JSON value type | value/range | MessagePack type | first byte + --------------- | --------------------------------- | ---------------- | ---------- + null | `null` | nil | 0xC0 + boolean | `true` | true | 0xC3 + boolean | `false` | false | 0xC2 + number_integer | -9223372036854775808..-2147483649 | int64 | 0xD3 + number_integer | -2147483648..-32769 | int32 | 0xD2 + number_integer | -32768..-129 | int16 | 0xD1 + number_integer | -128..-33 | int8 | 0xD0 + number_integer | -32..-1 | negative fixint | 0xE0..0xFF + number_integer | 0..127 | positive fixint | 0x00..0x7F + number_integer | 128..255 | uint 8 | 0xCC + number_integer | 256..65535 | uint 16 | 0xCD + number_integer | 65536..4294967295 | uint 32 | 0xCE + number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF + number_unsigned | 0..127 | positive fixint | 0x00..0x7F + number_unsigned | 128..255 | uint 8 | 0xCC + number_unsigned | 256..65535 | uint 16 | 0xCD + number_unsigned | 65536..4294967295 | uint 32 | 0xCE + number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF + number_float | *any value* | float 64 | 0xCB + string | *length*: 0..31 | fixstr | 0xA0..0xBF + string | *length*: 32..255 | str 8 | 0xD9 + string | *length*: 256..65535 | str 16 | 0xDA + string | *length*: 65536..4294967295 | str 32 | 0xDB + array | *size*: 0..15 | fixarray | 0x90..0x9F + array | *size*: 16..65535 | array 16 | 0xDC + array | *size*: 65536..4294967295 | array 32 | 0xDD + object | *size*: 0..15 | fix map | 0x80..0x8F + object | *size*: 16..65535 | map 16 | 0xDE + object | *size*: 65536..4294967295 | map 32 | 0xDF + binary | *size*: 0..255 | bin 8 | 0xC4 + binary | *size*: 256..65535 | bin 16 | 0xC5 + binary | *size*: 65536..4294967295 | bin 32 | 0xC6 + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a MessagePack value. + + @note The following values can **not** be converted to a MessagePack value: + - strings with more than 4294967295 bytes + - byte strings with more than 4294967295 bytes + - arrays with more than 4294967295 elements + - objects with more than 4294967295 elements + + @note The following MessagePack types are not used in the conversion: + - float 32 (0xCA) + + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in MessagePack format.,to_msgpack} + + @sa http://msgpack.org + @sa @ref from_msgpack for the analogous deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9 + */ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + to_msgpack(j, result); + return result; + } + + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /*! + @brief create a UBJSON serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the UBJSON + (Universal Binary JSON) serialization format. UBJSON aims to be more compact + than JSON itself, yet more efficient to parse. + + The library uses the following mapping from JSON values types to + UBJSON types according to the UBJSON specification: + + JSON value type | value/range | UBJSON type | marker + --------------- | --------------------------------- | ----------- | ------ + null | `null` | null | `Z` + boolean | `true` | true | `T` + boolean | `false` | false | `F` + number_integer | -9223372036854775808..-2147483649 | int64 | `L` + number_integer | -2147483648..-32769 | int32 | `l` + number_integer | -32768..-129 | int16 | `I` + number_integer | -128..127 | int8 | `i` + number_integer | 128..255 | uint8 | `U` + number_integer | 256..32767 | int16 | `I` + number_integer | 32768..2147483647 | int32 | `l` + number_integer | 2147483648..9223372036854775807 | int64 | `L` + number_unsigned | 0..127 | int8 | `i` + number_unsigned | 128..255 | uint8 | `U` + number_unsigned | 256..32767 | int16 | `I` + number_unsigned | 32768..2147483647 | int32 | `l` + number_unsigned | 2147483648..9223372036854775807 | int64 | `L` + number_float | *any value* | float64 | `D` + string | *with shortest length indicator* | string | `S` + array | *see notes on optimized format* | array | `[` + object | *see notes on optimized format* | map | `{` + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a UBJSON value. + + @note The following values can **not** be converted to a UBJSON value: + - strings with more than 9223372036854775807 bytes (theoretical) + - unsigned integer numbers above 9223372036854775807 + + @note The following markers are not used in the conversion: + - `Z`: no-op values are not created. + - `C`: single-byte strings are serialized with `S` markers. + + @note Any UBJSON output created @ref to_ubjson can be successfully parsed + by @ref from_ubjson. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @note The optimized formats for containers are supported: Parameter + @a use_size adds size information to the beginning of a container and + removes the closing marker. Parameter @a use_type further checks + whether all elements of a container have the same type and adds the + type marker to the beginning of the container. The @a use_type + parameter must only be used together with @a use_size = true. Note + that @a use_size = true alone may result in larger representations - + the benefit of this parameter is that the receiving side is + immediately informed on the number of elements of the container. + + @note If the JSON data contains the binary type, the value stored is a list + of integers, as suggested by the UBJSON documentation. In particular, + this means that serialization and the deserialization of a JSON + containing binary values into UBJSON and back will result in a + different JSON object. + + @param[in] j JSON value to serialize + @param[in] use_size whether to add size annotations to container types + @param[in] use_type whether to add type annotations to container types + (must be combined with @a use_size = true) + @return UBJSON serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in UBJSON format.,to_ubjson} + + @sa http://ubjson.org + @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the + analogous deserialization + @sa @ref to_cbor(const basic_json& for the related CBOR format + @sa @ref to_msgpack(const basic_json&) for the related MessagePack format + + @since version 3.1.0 + */ + static std::vector to_ubjson( + const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_ubjson(j, result, use_size, use_type); + return result; + } + + static void to_ubjson( + const basic_json& j, + detail::output_adapter o, + const bool use_size = false, + const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + static void to_ubjson( + const basic_json& j, + detail::output_adapter o, + const bool use_size = false, + const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + /*! + @brief Serializes the given JSON object `j` to BSON and returns a vector + containing the corresponding BSON-representation. + + BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are + stored as a single entity (a so-called document). + + The library uses the following mapping from JSON values types to BSON types: + + JSON value type | value/range | BSON type | marker + --------------- | --------------------------------- | ----------- | ------ + null | `null` | null | 0x0A + boolean | `true`, `false` | boolean | 0x08 + number_integer | -9223372036854775808..-2147483649 | int64 | 0x12 + number_integer | -2147483648..2147483647 | int32 | 0x10 + number_integer | 2147483648..9223372036854775807 | int64 | 0x12 + number_unsigned | 0..2147483647 | int32 | 0x10 + number_unsigned | 2147483648..9223372036854775807 | int64 | 0x12 + number_unsigned | 9223372036854775808..18446744073709551615| -- | -- + number_float | *any value* | double | 0x01 + string | *any value* | string | 0x02 + array | *any value* | document | 0x04 + object | *any value* | document | 0x03 + binary | *any value* | binary | 0x05 + + @warning The mapping is **incomplete**, since only JSON-objects (and things + contained therein) can be serialized to BSON. + Also, integers larger than 9223372036854775807 cannot be serialized to BSON, + and the keys may not contain U+0000, since they are serialized a + zero-terminated c-strings. + + @throw out_of_range.407 if `j.is_number_unsigned() && j.get() > + 9223372036854775807` + @throw out_of_range.409 if a key in `j` contains a NULL (U+0000) + @throw type_error.317 if `!j.is_object()` + + @pre The input `j` is required to be an object: `j.is_object() == true`. + + @note Any BSON output created via @ref to_bson can be successfully parsed + by @ref from_bson. + + @param[in] j JSON value to serialize + @return BSON serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in BSON format.,to_bson} + + @sa http://bsonspec.org/spec.html + @sa @ref from_bson(detail::input_adapter&&, const bool strict) for the + analogous deserialization + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + @sa @ref to_cbor(const basic_json&) for the related CBOR format + @sa @ref to_msgpack(const basic_json&) for the related MessagePack format + */ + static std::vector to_bson(const basic_json& j) + { + std::vector result; + to_bson(j, result); + return result; + } + + /*! + @brief Serializes the given JSON object `j` to BSON and forwards the + corresponding BSON-representation to the given output_adapter `o`. + @param j The JSON object to convert to BSON. + @param o The output adapter that receives the binary BSON representation. + @pre The input `j` shall be an object: `j.is_object() == true` + @sa @ref to_bson(const basic_json&) + */ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /*! + @copydoc to_bson(const basic_json&, detail::output_adapter) + */ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /*! + @brief create a JSON value from an input in CBOR format + + Deserializes a given input @a i to a JSON value using the CBOR (Concise + Binary Object Representation) serialization format. + + The library maps CBOR types to JSON value types as follows: + + CBOR type | JSON value type | first byte + ---------------------- | --------------- | ---------- + Integer | number_unsigned | 0x00..0x17 + Unsigned integer | number_unsigned | 0x18 + Unsigned integer | number_unsigned | 0x19 + Unsigned integer | number_unsigned | 0x1A + Unsigned integer | number_unsigned | 0x1B + Negative integer | number_integer | 0x20..0x37 + Negative integer | number_integer | 0x38 + Negative integer | number_integer | 0x39 + Negative integer | number_integer | 0x3A + Negative integer | number_integer | 0x3B + Byte string | binary | 0x40..0x57 + Byte string | binary | 0x58 + Byte string | binary | 0x59 + Byte string | binary | 0x5A + Byte string | binary | 0x5B + UTF-8 string | string | 0x60..0x77 + UTF-8 string | string | 0x78 + UTF-8 string | string | 0x79 + UTF-8 string | string | 0x7A + UTF-8 string | string | 0x7B + UTF-8 string | string | 0x7F + array | array | 0x80..0x97 + array | array | 0x98 + array | array | 0x99 + array | array | 0x9A + array | array | 0x9B + array | array | 0x9F + map | object | 0xA0..0xB7 + map | object | 0xB8 + map | object | 0xB9 + map | object | 0xBA + map | object | 0xBB + map | object | 0xBF + False | `false` | 0xF4 + True | `true` | 0xF5 + Null | `null` | 0xF6 + Half-Precision Float | number_float | 0xF9 + Single-Precision Float | number_float | 0xFA + Double-Precision Float | number_float | 0xFB + + @warning The mapping is **incomplete** in the sense that not all CBOR + types can be converted to a JSON value. The following CBOR types + are not supported and will yield parse errors (parse_error.112): + - date/time (0xC0..0xC1) + - bignum (0xC2..0xC3) + - decimal fraction (0xC4) + - bigfloat (0xC5) + - tagged items (0xC6..0xD4, 0xD8..0xDB) + - expected conversions (0xD5..0xD7) + - simple values (0xE0..0xF3, 0xF8) + - undefined (0xF7) + + @warning CBOR allows map keys of any type, whereas JSON only allows + strings as keys in object values. Therefore, CBOR maps with keys + other than UTF-8 strings are rejected (parse_error.113). + + @note Any CBOR output created @ref to_cbor can be successfully parsed by + @ref from_cbor. + + @param[in] i an input in CBOR format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if unsupported features from CBOR were + used in the given input @a v or if the input is not valid CBOR + @throw parse_error.113 if a string was expected as map key, but not found + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in CBOR + format to a JSON value.,from_cbor} + + @sa http://cbor.io + @sa @ref to_cbor(const basic_json&) for the analogous serialization + @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for the + related MessagePack format + @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9; parameter @a start_index since 2.1.1; changed to + consume input adapters, removed start_index parameter, and added + @a strict parameter since 3.0.0; added @a allow_exceptions parameter + since 3.2.0 + */ + template + _az_JSON_HEDLEY_WARN_UNUSED_RESULT 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); + 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); + } + + /*! + @copydoc from_cbor(detail::input_adapter&&, const bool, const bool) + */ + template + _az_JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_cbor( + IteratorType first, + IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)) + .sax_parse(input_format_t::cbor, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + _az_JSON_HEDLEY_WARN_UNUSED_RESULT _az_JSON_HEDLEY_DEPRECATED_FOR( + 3.8.0, + from_cbor(ptr, ptr + len)) static basic_json + from_cbor( + const T* ptr, + std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions); + } + + _az_JSON_HEDLEY_WARN_UNUSED_RESULT + _az_JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + 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); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)) + .sax_parse(input_format_t::cbor, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @brief create a JSON value from an input in MessagePack format + + Deserializes a given input @a i to a JSON value using the MessagePack + serialization format. + + The library maps MessagePack types to JSON value types as follows: + + MessagePack type | JSON value type | first byte + ---------------- | --------------- | ---------- + positive fixint | number_unsigned | 0x00..0x7F + fixmap | object | 0x80..0x8F + fixarray | array | 0x90..0x9F + fixstr | string | 0xA0..0xBF + nil | `null` | 0xC0 + false | `false` | 0xC2 + true | `true` | 0xC3 + float 32 | number_float | 0xCA + float 64 | number_float | 0xCB + uint 8 | number_unsigned | 0xCC + uint 16 | number_unsigned | 0xCD + uint 32 | number_unsigned | 0xCE + uint 64 | number_unsigned | 0xCF + int 8 | number_integer | 0xD0 + int 16 | number_integer | 0xD1 + int 32 | number_integer | 0xD2 + int 64 | number_integer | 0xD3 + str 8 | string | 0xD9 + str 16 | string | 0xDA + str 32 | string | 0xDB + array 16 | array | 0xDC + array 32 | array | 0xDD + map 16 | object | 0xDE + map 32 | object | 0xDF + bin 8 | binary | 0xC4 + bin 16 | binary | 0xC5 + bin 32 | binary | 0xC6 + ext 8 | binary | 0xC7 + ext 16 | binary | 0xC8 + ext 32 | binary | 0xC9 + fixext 1 | binary | 0xD4 + fixext 2 | binary | 0xD5 + fixext 4 | binary | 0xD6 + fixext 8 | binary | 0xD7 + fixext 16 | binary | 0xD8 + negative fixint | number_integer | 0xE0-0xFF + + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + + @param[in] i an input in MessagePack format convertible to an input + adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if unsupported features from MessagePack were + used in the given input @a i or if the input is not valid MessagePack + @throw parse_error.113 if a string was expected as map key, but not found + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + MessagePack format to a JSON value.,from_msgpack} + + @sa http://msgpack.org + @sa @ref to_msgpack(const basic_json&) for the analogous serialization + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the + related CBOR format + @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for + the related UBJSON format + @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for + the related BSON format + + @since version 2.0.9; parameter @a start_index since 2.1.1; changed to + consume input adapters, removed start_index parameter, and added + @a strict parameter since 3.0.0; added @a allow_exceptions parameter + since 3.2.0 + */ + template + _az_JSON_HEDLEY_WARN_UNUSED_RESULT 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); + 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); + } + + /*! + @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool) + */ + template + _az_JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_msgpack( + IteratorType first, + IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)) + .sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + _az_JSON_HEDLEY_WARN_UNUSED_RESULT _az_JSON_HEDLEY_DEPRECATED_FOR( + 3.8.0, + from_msgpack(ptr, ptr + len)) static basic_json + from_msgpack( + const T* ptr, + std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } + + _az_JSON_HEDLEY_WARN_UNUSED_RESULT + _az_JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + 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); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)) + .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 + + Deserializes a given input @a i to a JSON value using the UBJSON (Universal + Binary JSON) serialization format. + + The library maps UBJSON types to JSON value types as follows: + + UBJSON type | JSON value type | marker + ----------- | --------------------------------------- | ------ + no-op | *no value, next value is read* | `N` + null | `null` | `Z` + false | `false` | `F` + true | `true` | `T` + float32 | number_float | `d` + float64 | number_float | `D` + uint8 | number_unsigned | `U` + int8 | number_integer | `i` + int16 | number_integer | `I` + int32 | number_integer | `l` + int64 | number_integer | `L` + string | string | `S` + char | string | `C` + array | array (optimized values are supported) | `[` + object | object (optimized values are supported) | `{` + + @note The mapping is **complete** in the sense that any UBJSON value can + be converted to a JSON value. + + @param[in] i an input in UBJSON format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if a parse error occurs + @throw parse_error.113 if a string could not be parsed successfully + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + UBJSON format to a JSON value.,from_ubjson} + + @sa http://ubjson.org + @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the + analogous serialization + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the + related CBOR format + @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for + the related MessagePack format + @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for + the related BSON format + + @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0 + */ + template + _az_JSON_HEDLEY_WARN_UNUSED_RESULT 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); + 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); + } + + /*! + @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool) + */ + template + _az_JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_ubjson( + IteratorType first, + IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)) + .sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + _az_JSON_HEDLEY_WARN_UNUSED_RESULT _az_JSON_HEDLEY_DEPRECATED_FOR( + 3.8.0, + from_ubjson(ptr, ptr + len)) static basic_json + from_ubjson( + const T* ptr, + std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } + + _az_JSON_HEDLEY_WARN_UNUSED_RESULT + _az_JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + 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); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)) + .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 + + Deserializes a given input @a i to a JSON value using the BSON (Binary JSON) + serialization format. + + The library maps BSON record types to JSON value types as follows: + + BSON type | BSON marker byte | JSON value type + --------------- | ---------------- | --------------------------- + double | 0x01 | number_float + string | 0x02 | string + document | 0x03 | object + array | 0x04 | array + binary | 0x05 | still unsupported + undefined | 0x06 | still unsupported + ObjectId | 0x07 | still unsupported + boolean | 0x08 | boolean + UTC Date-Time | 0x09 | still unsupported + null | 0x0A | null + Regular Expr. | 0x0B | still unsupported + DB Pointer | 0x0C | still unsupported + JavaScript Code | 0x0D | still unsupported + Symbol | 0x0E | still unsupported + JavaScript Code | 0x0F | still unsupported + int32 | 0x10 | number_integer + Timestamp | 0x11 | still unsupported + 128-bit decimal float | 0x13 | still unsupported + Max Key | 0x7F | still unsupported + Min Key | 0xFF | still unsupported + + @warning The mapping is **incomplete**. The unsupported mappings + are indicated in the table above. + + @param[in] i an input in BSON format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.114 if an unsupported BSON record type is encountered + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + BSON format to a JSON value.,from_bson} + + @sa http://bsonspec.org/spec.html + @sa @ref to_bson(const basic_json&) for the analogous serialization + @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the + related CBOR format + @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for + the related MessagePack format + @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the + related UBJSON format + */ + template + _az_JSON_HEDLEY_WARN_UNUSED_RESULT 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); + 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); + } + + /*! + @copydoc from_bson(detail::input_adapter&&, const bool, const bool) + */ + template + _az_JSON_HEDLEY_WARN_UNUSED_RESULT static basic_json from_bson( + IteratorType first, + IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)) + .sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + _az_JSON_HEDLEY_WARN_UNUSED_RESULT _az_JSON_HEDLEY_DEPRECATED_FOR( + 3.8.0, + from_bson(ptr, ptr + len)) static basic_json + from_bson( + const T* ptr, + std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } + + _az_JSON_HEDLEY_WARN_UNUSED_RESULT + _az_JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + 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); + auto ia = i.get(); + const bool res = binary_reader(std::move(ia)) + .sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + /// @} + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. Similar to @ref operator[](const typename + object_t::key_type&), `null` values are created in arrays and objects if + necessary. + + In particular: + - If the JSON pointer points to an object key that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. + - If the JSON pointer points to an array index that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. All indices between the current maximum and the given + index are also filled with `null`. + - The special value `-` is treated as a synonym for the index past the + end. + + @param[in] ptr a JSON pointer + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + + @liveexample{The behavior is shown in the example.,operatorjson_pointer} + + @since version 2.0.0 + */ + reference operator[](const json_pointer& ptr) { return ptr.get_unchecked(this); } + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. The function does not change the JSON + value; no `null` values are created. In particular, the special value + `-` yields an exception. + + @param[in] ptr JSON pointer to the desired element + + @return const reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + + @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} + + @since version 2.0.0 + */ + const_reference operator[](const json_pointer& ptr) const { return ptr.get_unchecked(this); } + + /*! + @brief access specified element via JSON Pointer + + Returns a reference to the element at with specified JSON pointer @a ptr, + with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.403 if the JSON pointer describes a key of an object + which cannot be found. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 2.0.0 + + @liveexample{The behavior is shown in the example.,at_json_pointer} + */ + reference at(const json_pointer& ptr) { return ptr.get_checked(this); } + + /*! + @brief access specified element via JSON Pointer + + Returns a const reference to the element at with specified JSON pointer @a + ptr, with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.403 if the JSON pointer describes a key of an object + which cannot be found. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 2.0.0 + + @liveexample{The behavior is shown in the example.,at_json_pointer_const} + */ + const_reference at(const json_pointer& ptr) const { return ptr.get_checked(this); } + + /*! + @brief return flattened JSON value + + The function creates a JSON object whose keys are JSON pointers (see [RFC + 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all + primitive. The original JSON value can be restored using the @ref + unflatten() function. + + @return an object that maps JSON pointers to primitive values + + @note Empty objects and arrays are flattened to `null` and will not be + reconstructed correctly by the @ref unflatten() function. + + @complexity Linear in the size the JSON value. + + @liveexample{The following code shows how a JSON object is flattened to an + object whose keys consist of JSON pointers.,flatten} + + @sa @ref unflatten() for the reverse function + + @since version 2.0.0 + */ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /*! + @brief unflatten a previously flattened JSON value + + The function restores the arbitrary nesting of a JSON value that has been + flattened before using the @ref flatten() function. The JSON value must + meet certain constraints: + 1. The value must be an object. + 2. The keys must be JSON pointers (see + [RFC 6901](https://tools.ietf.org/html/rfc6901)) + 3. The mapped values must be primitive JSON types. + + @return the original JSON from a flattened version + + @note Empty objects and arrays are flattened by @ref flatten() to `null` + values and can not unflattened to their original type. Apart from + this example, for a JSON value `j`, the following is always true: + `j == j.flatten().unflatten()`. + + @complexity Linear in the size the JSON value. + + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + + @liveexample{The following code shows how a flattened JSON object is + unflattened into the original nested JSON object.,unflatten} + + @sa @ref flatten() for the reverse function + + @since version 2.0.0 + */ + basic_json unflatten() const { return json_pointer::unflatten(*this); } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /*! + @brief applies a JSON patch + + [JSON Patch](http://jsonpatch.com) defines a JSON document structure for + expressing a sequence of operations to apply to a JSON) document. With + this function, a JSON Patch is applied to the current JSON value by + executing all operations from the patch. + + @param[in] json_patch JSON patch document + @return patched document + + @note The application of a patch is atomic: Either all operations succeed + and the patched document is returned or an exception is thrown. In + any case, the original value is not changed: the patch is applied + to a copy of the value. + + @throw parse_error.104 if the JSON patch does not consist of an array of + objects + + @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory + attributes are missing); example: `"operation add must have member path"` + + @throw out_of_range.401 if an array index is out of range. + + @throw out_of_range.403 if a JSON pointer inside the patch could not be + resolved successfully in the current JSON value; example: `"key baz not + found"` + + @throw out_of_range.405 if JSON pointer has no parent ("add", "remove", + "move") + + @throw other_error.501 if "test" operation was unsuccessful + + @complexity Linear in the size of the JSON value and the length of the + JSON patch. As usually only a fraction of the JSON value is affected by + the patch, the complexity can usually be neglected. + + @liveexample{The following code shows how a JSON patch is applied to a + value.,patch} + + @sa @ref diff -- create a JSON patch by comparing two JSON values + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) + + @since version 2.0.0 + */ + basic_json patch(const basic_json& json_patch) const + { + // make a working copy to apply the patch to + basic_json result = *this; + + // the valid JSON Patch operations + enum class patch_operations + { + add, + remove, + replace, + move, + copy, + test, + invalid + }; + + const auto get_op = [](const std::string& op) { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + const auto operation_add = [&result](json_pointer& ptr, basic_json val) { + // adding to the root of the target document means replacing it + if (ptr.empty()) + { + result = val; + return; + } + + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result[ptr]; + + switch (parent.m_type) + { + case value_t::null: + case value_t::object: { + // use operator[] to add value + parent[last_path] = val; + break; + } + + case value_t::array: { + if (last_path == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + const auto idx = json_pointer::array_index(last_path); + if (_az_JSON_HEDLEY_UNLIKELY(static_cast(idx) > parent.size())) + { + // avoid undefined behavior + _az_JSON_THROW(out_of_range::create( + 401, "array index " + std::to_string(idx) + " is out of range")); + } + + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); + } + break; + } + + // if there exists a parent it cannot be primitive + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + }; + + // wrapper for "remove" operation; remove value at ptr + const auto operation_remove = [&result](json_pointer& ptr) { + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (_az_JSON_HEDLEY_LIKELY(it != parent.end())) + { + parent.erase(it); } else { - const auto idx = json_pointer::array_index(last_path); - if (JSON_UNLIKELY(static_cast(idx) > parent.size())) - { - // avoid undefined behavior - JSON_THROW(out_of_range::create( - 401, "array index " + std::to_string(idx) + " is out of range")); - } - - // default case: insert add offset - parent.insert(parent.begin() + static_cast(idx), val); + _az_JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found")); } - break; } - - // if there exists a parent it cannot be primitive - default: // LCOV_EXCL_LINE - assert(false); // LCOV_EXCL_LINE - } - }; - - // wrapper for "remove" operation; remove value at ptr - const auto operation_remove = [&result](json_pointer& ptr) { - // get reference to parent of JSON pointer ptr - const auto last_path = ptr.back(); - ptr.pop_back(); - basic_json& parent = result.at(ptr); - - // remove child - if (parent.is_object()) - { - // perform range check - auto it = parent.find(last_path); - if (JSON_LIKELY(it != parent.end())) + else if (parent.is_array()) { - parent.erase(it); + // note erase performs range check + parent.erase(static_cast(json_pointer::array_index(last_path))); } - else - { - JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found")); - } - } - else if (parent.is_array()) - { - // note erase performs range check - parent.erase(static_cast(json_pointer::array_index(last_path))); - } - }; - - // type check: top level value must be an array - if (JSON_UNLIKELY(not json_patch.is_array())) - { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); - } - - // iterate and apply the operations - for (const auto& val : json_patch) - { - // wrapper to get a value for an operation - const auto get_value - = [&val]( - const std::string& op, const std::string& member, bool string_type) -> basic_json& { - // find value - auto it = val.m_value.object->find(member); - - // context-sensitive error message - const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; - - // check if desired value is present - if (JSON_UNLIKELY(it == val.m_value.object->end())) - { - JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'")); - } - - // check if result is of type string - if (JSON_UNLIKELY(string_type and not it->second.is_string())) - { - JSON_THROW( - parse_error::create(105, 0, error_msg + " must have string member '" + member + "'")); - } - - // no error: return value - return it->second; }; - // type check: every element of the array must be an object - if (JSON_UNLIKELY(not val.is_object())) + // type check: top level value must be an array + if (_az_JSON_HEDLEY_UNLIKELY(not json_patch.is_array())) { - JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + _az_JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); } - // collect mandatory members - const std::string op = get_value("op", "op", true); - const std::string path = get_value(op, "path", true); - json_pointer ptr(path); - - switch (get_op(op)) + // iterate and apply the operations + for (const auto& val : json_patch) { - case patch_operations::add: { - operation_add(ptr, get_value("add", "value", false)); - break; - } + // wrapper to get a value for an operation + const auto get_value = + [&val]( + const std::string& op, const std::string& member, bool string_type) -> basic_json& { + // find value + auto it = val.m_value.object->find(member); - case patch_operations::remove: { - operation_remove(ptr); - break; - } + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; - case patch_operations::replace: { - // the "path" location must exist - use at() - result.at(ptr) = get_value("replace", "value", false); - break; - } - - case patch_operations::move: { - const std::string from_path = get_value("move", "from", true); - json_pointer from_ptr(from_path); - - // the "from" location must exist - use at() - basic_json v = result.at(from_ptr); - - // The move operation is functionally identical to a - // "remove" operation on the "from" location, followed - // immediately by an "add" operation at the target - // location with the value that was just removed. - operation_remove(from_ptr); - operation_add(ptr, v); - break; - } - - case patch_operations::copy: { - const std::string from_path = get_value("copy", "from", true); - const json_pointer from_ptr(from_path); - - // the "from" location must exist - use at() - basic_json v = result.at(from_ptr); - - // The copy is functionally identical to an "add" - // operation at the target location using the value - // specified in the "from" member. - operation_add(ptr, v); - break; - } - - case patch_operations::test: { - bool success = false; - JSON_TRY + // check if desired value is present + if (_az_JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) { - // check if "value" matches the one at "path" + _az_JSON_THROW( + parse_error::create(105, 0, error_msg + " must have member '" + member + "'")); + } + + // check if result is of type string + if (_az_JSON_HEDLEY_UNLIKELY(string_type and not it->second.is_string())) + { + _az_JSON_THROW(parse_error::create( + 105, 0, error_msg + " must have string member '" + member + "'")); + } + + // no error: return value + return it->second; + }; + + // type check: every element of the array must be an object + if (_az_JSON_HEDLEY_UNLIKELY(not val.is_object())) + { + _az_JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects")); + } + + // collect mandatory members + const std::string op = get_value("op", "op", true); + const std::string path = get_value(op, "path", true); + json_pointer ptr(path); + + switch (get_op(op)) + { + case patch_operations::add: { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: { + operation_remove(ptr); + break; + } + + case patch_operations::replace: { // the "path" location must exist - use at() - success = (result.at(ptr) == get_value("test", "value", false)); - } - JSON_INTERNAL_CATCH(out_of_range&) - { - // ignore out of range errors: success remains false + result.at(ptr) = get_value("replace", "value", false); + break; } - // throw an exception if test fails - if (JSON_UNLIKELY(not success)) + case patch_operations::move: { + const std::string from_path = get_value("move", "from", true); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: { + const std::string from_path = get_value("copy", "from", true); + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The copy is functionally identical to an "add" + // operation at the target location using the value + // specified in the "from" member. + operation_add(ptr, v); + break; + } + + case patch_operations::test: { + bool success = false; + _az_JSON_TRY + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + _az_JSON_INTERNAL_CATCH(out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if test fails + if (_az_JSON_HEDLEY_UNLIKELY(not success)) + { + _az_JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); + } + + break; + } + + default: { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + _az_JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid")); + } + } + } + + return result; + } + + /*! + @brief creates a diff as a JSON patch + + Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can + be changed into the value @a target by calling @ref patch function. + + @invariant For two JSON values @a source and @a target, the following code + yields always `true`: + @code {.cpp} + source.patch(diff(source, target)) == target; + @endcode + + @note Currently, only `remove`, `add`, and `replace` operations are + generated. + + @param[in] source JSON value to compare from + @param[in] target JSON value to compare against + @param[in] path helper value to create JSON pointers + + @return a JSON patch to convert the @a source to @a target + + @complexity Linear in the lengths of @a source and @a target. + + @liveexample{The following code shows how a JSON patch is created as a + diff for two JSON values.,diff} + + @sa @ref patch -- apply a JSON patch + @sa @ref merge_patch -- apply a JSON Merge Patch + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + + @since version 2.0.0 + */ + _az_JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json diff( + const basic_json& source, + const basic_json& target, + const std::string& path = "") + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back({{"op", "replace"}, {"path", path}, {"value", target}}); + return result; + } + + switch (source.type()) + { + case value_t::array: { + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() and i < target.size()) { - JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump())); + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // i now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert( + result.begin() + end_index, + object({{"op", "remove"}, {"path", path + "/" + std::to_string(i)}})); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back({{"op", "add"}, {"path", path + "/-"}, {"value", target[i]}}); + ++i; + } + + break; + } + + case value_t::object: { + // first pass: traverse this object's elements + for (auto it = source.cbegin(); it != source.cend(); ++it) + { + // escape the key name to be used in a JSON patch + const auto key = json_pointer::escape(it.key()); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object({{"op", "remove"}, {"path", path + "/" + key}})); + } + } + + // second pass: traverse other object's elements + for (auto it = target.cbegin(); it != target.cend(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto key = json_pointer::escape(it.key()); + result.push_back({{"op", "add"}, {"path", path + "/" + key}, {"value", it.value()}}); + } } break; } default: { - // op must be "add", "remove", "replace", "move", "copy", or - // "test" - JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid")); + // both primitive type: replace value + result.push_back({{"op", "replace"}, {"path", path}, {"value", target}}); + break; } } - } - return result; - } - - /*! - @brief creates a diff as a JSON patch - - Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can - be changed into the value @a target by calling @ref patch function. - - @invariant For two JSON values @a source and @a target, the following code - yields always `true`: - @code {.cpp} - source.patch(diff(source, target)) == target; - @endcode - - @note Currently, only `remove`, `add`, and `replace` operations are - generated. - - @param[in] source JSON value to compare from - @param[in] target JSON value to compare against - @param[in] path helper value to create JSON pointers - - @return a JSON patch to convert the @a source to @a target - - @complexity Linear in the lengths of @a source and @a target. - - @liveexample{The following code shows how a JSON patch is created as a - diff for two JSON values.,diff} - - @sa @ref patch -- apply a JSON patch - @sa @ref merge_patch -- apply a JSON Merge Patch - - @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) - - @since version 2.0.0 - */ - JSON_NODISCARD - static basic_json diff( - const basic_json& source, - const basic_json& target, - const std::string& path = "") - { - // the patch - basic_json result(value_t::array); - - // if the values are the same, return empty patch - if (source == target) - { return result; } - if (source.type() != target.type()) + /// @} + + //////////////////////////////// + // JSON Merge Patch functions // + //////////////////////////////// + + /// @name JSON Merge Patch functions + /// @{ + + /*! + @brief applies a JSON Merge Patch + + The merge patch format is primarily intended for use with the HTTP PATCH + method as a means of describing a set of modifications to a target + resource's content. This function applies a merge patch to the current + JSON value. + + The function implements the following algorithm from Section 2 of + [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396): + + ``` + define MergePatch(Target, Patch): + if Patch is an Object: + if Target is not an Object: + Target = {} // Ignore the contents and set it to an empty Object + for each Name/Value pair in Patch: + if Value is null: + if Name exists in Target: + remove the Name/Value pair from Target + else: + Target[Name] = MergePatch(Target[Name], Value) + return Target + else: + return Patch + ``` + + Thereby, `Target` is the current object; that is, the patch is applied to + the current value. + + @param[in] apply_patch the patch to apply + + @complexity Linear in the lengths of @a patch. + + @liveexample{The following code shows how a JSON Merge Patch is applied to + a JSON document.,merge_patch} + + @sa @ref patch -- apply a JSON patch + @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396) + + @since version 3.0.0 + */ + void merge_patch(const basic_json& apply_patch) { - // different types: replace value - result.push_back({{"op", "replace"}, {"path", path}, {"value", target}}); - return result; - } - - switch (source.type()) - { - case value_t::array: { - // first pass: traverse common elements - std::size_t i = 0; - while (i < source.size() and i < target.size()) + if (apply_patch.is_object()) + { + if (not is_object()) { - // recursive call to compare array values at index i - auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); - result.insert(result.end(), temp_diff.begin(), temp_diff.end()); - ++i; + *this = object(); } - - // i now reached the end of at least one array - // in a second pass, traverse the remaining elements - - // remove my remaining elements - const auto end_index = static_cast(result.size()); - while (i < source.size()) + for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it) { - // add operations in reverse order to avoid invalid - // indices - result.insert( - result.begin() + end_index, - object({{"op", "remove"}, {"path", path + "/" + std::to_string(i)}})); - ++i; - } - - // add other remaining elements - while (i < target.size()) - { - result.push_back( - {{"op", "add"}, {"path", path + "/" + std::to_string(i)}, {"value", target[i]}}); - ++i; - } - - break; - } - - case value_t::object: { - // first pass: traverse this object's elements - for (auto it = source.cbegin(); it != source.cend(); ++it) - { - // escape the key name to be used in a JSON patch - const auto key = json_pointer::escape(it.key()); - - if (target.find(it.key()) != target.end()) + if (it.value().is_null()) { - // recursive call to compare object values at key it - auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key); - result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + erase(it.key()); } else { - // found a key that is not in o -> remove it - result.push_back(object({{"op", "remove"}, {"path", path + "/" + key}})); + operator[](it.key()).merge_patch(it.value()); } } - - // second pass: traverse other object's elements - for (auto it = target.cbegin(); it != target.cend(); ++it) - { - if (source.find(it.key()) == source.end()) - { - // found a key that is not in this -> add it - const auto key = json_pointer::escape(it.key()); - result.push_back({{"op", "add"}, {"path", path + "/" + key}, {"value", it.value()}}); - } - } - - break; } - - default: { - // both primitive type: replace value - result.push_back({{"op", "replace"}, {"path", path}, {"value", target}}); - break; + else + { + *this = apply_patch; } } - return result; - } - - /// @} - - //////////////////////////////// - // JSON Merge Patch functions // - //////////////////////////////// - - /// @name JSON Merge Patch functions - /// @{ + /// @} + }; /*! - @brief applies a JSON Merge Patch + @brief user-defined to_string function for JSON values - The merge patch format is primarily intended for use with the HTTP PATCH - method as a means of describing a set of modifications to a target - resource's content. This function applies a merge patch to the current - JSON value. + This function implements a user-defined to_string for JSON objects. - The function implements the following algorithm from Section 2 of - [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396): - - ``` - define MergePatch(Target, Patch): - if Patch is an Object: - if Target is not an Object: - Target = {} // Ignore the contents and set it to an empty Object - for each Name/Value pair in Patch: - if Value is null: - if Name exists in Target: - remove the Name/Value pair from Target - else: - Target[Name] = MergePatch(Target[Name], Value) - return Target - else: - return Patch - ``` - - Thereby, `Target` is the current object; that is, the patch is applied to - the current value. - - @param[in] apply_patch the patch to apply - - @complexity Linear in the lengths of @a patch. - - @liveexample{The following code shows how a JSON Merge Patch is applied to - a JSON document.,merge_patch} - - @sa @ref patch -- apply a JSON patch - @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396) - - @since version 3.0.0 + @param[in] j a JSON object + @return a std::string object */ - void merge_patch(const basic_json& apply_patch) - { - if (apply_patch.is_object()) - { - if (not is_object()) - { - *this = object(); - } - for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it) - { - if (it.value().is_null()) - { - erase(it.key()); - } - else - { - operator[](it.key()).merge_patch(it.value()); - } - } - } - else - { - *this = apply_patch; - } - } - /// @} -}; -} // namespace nlohmann + _az_NLOHMANN_BASIC_JSON_TPL_DECLARATION + std::string to_string(const _az_NLOHMANN_BASIC_JSON_TPL& j) { return j.dump(); } +}}}} // namespace Azure::Core::Internal::Json /////////////////////// // nonmember support // @@ -20583,17 +24229,17 @@ public: namespace std { /// hash value for JSON objects -template <> struct hash +template <> struct hash { /*! @brief return a hash value for a JSON object @since version 1.0.0 */ - std::size_t operator()(const nlohmann::json& j) const + std::size_t operator()(const Azure::Core::Internal::Json::json& j) const { // a naive hashing via the string representation - const auto& h = hash(); + const auto& h = hash(); return h(j.dump()); } }; @@ -20601,15 +24247,17 @@ template <> struct hash /// specialization for std::less /// @note: do not remove the space after '<', /// see https://github.com/nlohmann/json/pull/679 -template <> struct less<::nlohmann::detail::value_t> +template <> struct less<::Azure::Core::Internal::Json::detail::value_t> { /*! @brief compare two value_t enum values @since version 3.0.0 */ - bool operator()(nlohmann::detail::value_t lhs, nlohmann::detail::value_t rhs) const noexcept + bool operator()( + Azure::Core::Internal::Json::detail::value_t lhs, + Azure::Core::Internal::Json::detail::value_t rhs) const noexcept { - return nlohmann::detail::operator<(lhs, rhs); + return Azure::Core::Internal::Json::detail::operator<(lhs, rhs); } }; @@ -20619,9 +24267,11 @@ template <> struct less<::nlohmann::detail::value_t> @since version 1.0.0 */ template <> -inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( - is_nothrow_move_constructible::value and - is_nothrow_move_assignable::value) +inline void swap( + Azure::Core::Internal::Json::json& j1, + Azure::Core::Internal::Json::json& + j2) noexcept(is_nothrow_move_constructible::value and + is_nothrow_move_assignable::value) { j1.swap(j2); } @@ -20641,10 +24291,11 @@ if no parse error occurred. @since version 1.0.0 */ -inline nlohmann::json operator"" _json(const char* s, std::size_t n) -{ - return nlohmann::json::parse(s, s + n); -} +// _az_JSON_HEDLEY_NON_NULL(1) +// inline Azure::Core::Internal::Json::json operator"" _json(const char* s, std::size_t n) +// { +// return Azure::Core::Internal::Json::json::parse(s, s + n); +// } /*! @brief user-defined string literal for JSON pointer @@ -20659,10 +24310,13 @@ object if no parse error occurred. @since version 2.0.0 */ -inline nlohmann::json::json_pointer operator"" _json_pointer(const char* s, std::size_t n) -{ - return nlohmann::json::json_pointer(std::string(s, n)); -} +// _az_JSON_HEDLEY_NON_NULL(1) +// inline Azure::Core::Internal::Json::json::json_pointer operator"" _json_pointer( +// const char* s, +// std::size_t n) +// { +// return Azure::Core::Internal::Json::json::json_pointer(std::string(s, n)); +// } // #include @@ -20674,18 +24328,157 @@ inline nlohmann::json::json_pointer operator"" _json_pointer(const char* s, std: #pragma GCC diagnostic pop #endif -// clean up -#undef JSON_INTERNAL_CATCH -#undef JSON_CATCH -#undef JSON_THROW -#undef JSON_TRY -#undef JSON_LIKELY -#undef JSON_UNLIKELY -#undef JSON_DEPRECATED -#undef JSON_NODISCARD -#undef JSON_HAS_CPP_14 -#undef JSON_HAS_CPP_17 -#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION -#undef NLOHMANN_BASIC_JSON_TPL - -#endif // INCLUDE_NLOHMANN_JSON_HPP_ +#undef _az_JSON_CATCH +#undef _az_JSON_HAS_CPP_14 +#undef _az_JSON_HAS_CPP_17 +#undef _az_JSON_HEDLEY_ALWAYS_INLINE +#undef _az_JSON_HEDLEY_ARM_VERSION +#undef _az_JSON_HEDLEY_ARM_VERSION_CHECK +#undef _az_JSON_HEDLEY_ARRAY_PARAM +#undef _az_JSON_HEDLEY_ASSUME +#undef _az_JSON_HEDLEY_BEGIN_C_DECLS +#undef _az_JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#undef _az_JSON_HEDLEY_CLANG_HAS_BUILTIN +#undef _az_JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#undef _az_JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE +#undef _az_JSON_HEDLEY_CLANG_HAS_EXTENSION +#undef _az_JSON_HEDLEY_CLANG_HAS_FEATURE +#undef _az_JSON_HEDLEY_CLANG_HAS_WARNING +#undef _az_JSON_HEDLEY_COMPCERT_VERSION +#undef _az_JSON_HEDLEY_COMPCERT_VERSION_CHECK +#undef _az_JSON_HEDLEY_CONCAT +#undef _az_JSON_HEDLEY_CONCAT3 +#undef _az_JSON_HEDLEY_CONCAT3_EX +#undef _az_JSON_HEDLEY_CONCAT_EX +#undef _az_JSON_HEDLEY_CONST +#undef _az_JSON_HEDLEY_CONSTEXPR +#undef _az_JSON_HEDLEY_CONST_CAST +#undef _az_JSON_HEDLEY_CPP_CAST +#undef _az_JSON_HEDLEY_CRAY_VERSION +#undef _az_JSON_HEDLEY_CRAY_VERSION_CHECK +#undef _az_JSON_HEDLEY_C_DECL +#undef _az_JSON_HEDLEY_DEPRECATED +#undef _az_JSON_HEDLEY_DEPRECATED_FOR +#undef _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#undef _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#undef _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#undef _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#undef _az_JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#undef _az_JSON_HEDLEY_DIAGNOSTIC_POP +#undef _az_JSON_HEDLEY_DIAGNOSTIC_PUSH +#undef _az_JSON_HEDLEY_DMC_VERSION +#undef _az_JSON_HEDLEY_DMC_VERSION_CHECK +#undef _az_JSON_HEDLEY_EMPTY_BASES +#undef _az_JSON_HEDLEY_EMSCRIPTEN_VERSION +#undef _az_JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#undef _az_JSON_HEDLEY_END_C_DECLS +#undef _az_JSON_HEDLEY_FALL_THROUGH +#undef _az_JSON_HEDLEY_FLAGS +#undef _az_JSON_HEDLEY_FLAGS_CAST +#undef _az_JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#undef _az_JSON_HEDLEY_GCC_HAS_BUILTIN +#undef _az_JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#undef _az_JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#undef _az_JSON_HEDLEY_GCC_HAS_EXTENSION +#undef _az_JSON_HEDLEY_GCC_HAS_FEATURE +#undef _az_JSON_HEDLEY_GCC_HAS_WARNING +#undef _az_JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#undef _az_JSON_HEDLEY_GCC_VERSION +#undef _az_JSON_HEDLEY_GCC_VERSION_CHECK +#undef _az_JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#undef _az_JSON_HEDLEY_GNUC_HAS_BUILTIN +#undef _az_JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#undef _az_JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#undef _az_JSON_HEDLEY_GNUC_HAS_EXTENSION +#undef _az_JSON_HEDLEY_GNUC_HAS_FEATURE +#undef _az_JSON_HEDLEY_GNUC_HAS_WARNING +#undef _az_JSON_HEDLEY_GNUC_VERSION +#undef _az_JSON_HEDLEY_GNUC_VERSION_CHECK +#undef _az_JSON_HEDLEY_HAS_ATTRIBUTE +#undef _az_JSON_HEDLEY_HAS_BUILTIN +#undef _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#undef _az_JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#undef _az_JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#undef _az_JSON_HEDLEY_HAS_EXTENSION +#undef _az_JSON_HEDLEY_HAS_FEATURE +#undef _az_JSON_HEDLEY_HAS_WARNING +#undef _az_JSON_HEDLEY_IAR_VERSION +#undef _az_JSON_HEDLEY_IAR_VERSION_CHECK +#undef _az_JSON_HEDLEY_IBM_VERSION +#undef _az_JSON_HEDLEY_IBM_VERSION_CHECK +#undef _az_JSON_HEDLEY_IMPORT +#undef _az_JSON_HEDLEY_INLINE +#undef _az_JSON_HEDLEY_INTEL_VERSION +#undef _az_JSON_HEDLEY_INTEL_VERSION_CHECK +#undef _az_JSON_HEDLEY_IS_CONSTANT +#undef _az_JSON_HEDLEY_IS_CONSTEXPR_ +#undef _az_JSON_HEDLEY_LIKELY +#undef _az_JSON_HEDLEY_MALLOC +#undef _az_JSON_HEDLEY_MESSAGE +#undef _az_JSON_HEDLEY_MSVC_VERSION +#undef _az_JSON_HEDLEY_MSVC_VERSION_CHECK +#undef _az_JSON_HEDLEY_NEVER_INLINE +#undef _az_JSON_HEDLEY_NON_NULL +#undef _az_JSON_HEDLEY_NO_ESCAPE +#undef _az_JSON_HEDLEY_NO_RETURN +#undef _az_JSON_HEDLEY_NO_THROW +#undef _az_JSON_HEDLEY_NULL +#undef _az_JSON_HEDLEY_PELLES_VERSION +#undef _az_JSON_HEDLEY_PELLES_VERSION_CHECK +#undef _az_JSON_HEDLEY_PGI_VERSION +#undef _az_JSON_HEDLEY_PGI_VERSION_CHECK +#undef _az_JSON_HEDLEY_PRAGMA +#undef _az_JSON_HEDLEY_PREDICT +#undef _az_JSON_HEDLEY_PREDICT_FALSE +#undef _az_JSON_HEDLEY_PREDICT_TRUE +#undef _az_JSON_HEDLEY_PRINTF_FORMAT +#undef _az_JSON_HEDLEY_PRIVATE +#undef _az_JSON_HEDLEY_PUBLIC +#undef _az_JSON_HEDLEY_PURE +#undef _az_JSON_HEDLEY_REINTERPRET_CAST +#undef _az_JSON_HEDLEY_REQUIRE +#undef _az_JSON_HEDLEY_REQUIRE_CONSTEXPR +#undef _az_JSON_HEDLEY_REQUIRE_MSG +#undef _az_JSON_HEDLEY_RESTRICT +#undef _az_JSON_HEDLEY_RETURNS_NON_NULL +#undef _az_JSON_HEDLEY_SENTINEL +#undef _az_JSON_HEDLEY_STATIC_ASSERT +#undef _az_JSON_HEDLEY_STATIC_CAST +#undef _az_JSON_HEDLEY_STRINGIFY +#undef _az_JSON_HEDLEY_STRINGIFY_EX +#undef _az_JSON_HEDLEY_SUNPRO_VERSION +#undef _az_JSON_HEDLEY_SUNPRO_VERSION_CHECK +#undef _az_JSON_HEDLEY_TINYC_VERSION +#undef _az_JSON_HEDLEY_TINYC_VERSION_CHECK +#undef _az_JSON_HEDLEY_TI_ARMCL_VERSION +#undef _az_JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#undef _az_JSON_HEDLEY_TI_CL2000_VERSION +#undef _az_JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#undef _az_JSON_HEDLEY_TI_CL430_VERSION +#undef _az_JSON_HEDLEY_TI_CL430_VERSION_CHECK +#undef _az_JSON_HEDLEY_TI_CL6X_VERSION +#undef _az_JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#undef _az_JSON_HEDLEY_TI_CL7X_VERSION +#undef _az_JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#undef _az_JSON_HEDLEY_TI_CLPRU_VERSION +#undef _az_JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#undef _az_JSON_HEDLEY_TI_VERSION +#undef _az_JSON_HEDLEY_TI_VERSION_CHECK +#undef _az_JSON_HEDLEY_UNAVAILABLE +#undef _az_JSON_HEDLEY_UNLIKELY +#undef _az_JSON_HEDLEY_UNPREDICTABLE +#undef _az_JSON_HEDLEY_UNREACHABLE +#undef _az_JSON_HEDLEY_UNREACHABLE_RETURN +#undef _az_JSON_HEDLEY_VERSION +#undef _az_JSON_HEDLEY_VERSION_DECODE_MAJOR +#undef _az_JSON_HEDLEY_VERSION_DECODE_MINOR +#undef _az_JSON_HEDLEY_VERSION_DECODE_REVISION +#undef _az_JSON_HEDLEY_VERSION_ENCODE +#undef _az_JSON_HEDLEY_WARNING +#undef _az_JSON_HEDLEY_WARN_UNUSED_RESULT +#undef _az_JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#undef _az_JSON_INTERNAL_CATCH +#undef _az_JSON_THROW +#undef _az_JSON_TRY +#undef _az_NLOHMANN_BASIC_JSON_TPL +#undef _az_NLOHMANN_BASIC_JSON_TPL_DECLARATION diff --git a/sdk/core/azure-core/test/ut/json.cpp b/sdk/core/azure-core/test/ut/json.cpp index fe2216925..ed6887001 100644 --- a/sdk/core/azure-core/test/ut/json.cpp +++ b/sdk/core/azure-core/test/ut/json.cpp @@ -1,12 +1,12 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // SPDX-License-Identifier: MIT +#include #include -#include -using json = nlohmann::json; +using json = Azure::Core::Internal::Json::json; -// Just a simple test to ensure that Azure Core is bringing nlohmann header +// Just a simple test to ensure that Azure Core internal is wrapping nlohmann json TEST(Json, create) { json j; diff --git a/sdk/core/azure-core/vcpkg/CONTROL b/sdk/core/azure-core/vcpkg/CONTROL index 330c09126..5fa51fe8c 100644 --- a/sdk/core/azure-core/vcpkg/CONTROL +++ b/sdk/core/azure-core/vcpkg/CONTROL @@ -3,7 +3,6 @@ # Source: azure-core-cpp Version: @AZ_LIBRARY_VERSION@ -Build-Depends: nlohmann-json Description: Microsoft Azure Core SDK for C++ This library provides shared primitives, abstractions, and helpers for modern Azure SDK client libraries written in the C++. Homepage: https://github.com/Azure/azure-sdk-for-cpp/tree/master/sdk/core/azure-core diff --git a/sdk/core/azure-core/vcpkg/Config.cmake.in b/sdk/core/azure-core/vcpkg/Config.cmake.in index 241b5c3aa..0c2c595d9 100644 --- a/sdk/core/azure-core/vcpkg/Config.cmake.in +++ b/sdk/core/azure-core/vcpkg/Config.cmake.in @@ -5,7 +5,6 @@ include(CMakeFindDependencyMacro) find_dependency(Threads) -find_dependency(nlohmann_json @NLOHMANN_JSON_MIN_REQUIRED_VERSION@) if(@BUILD_TRANSPORT_CURL@) find_dependency(CURL @CURL_MIN_REQUIRED_VERSION@) endif() diff --git a/sdk/keyvault/azure-security-keyvault-common/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-common/CMakeLists.txt index 96b17694c..04da8cc75 100644 --- a/sdk/keyvault/azure-security-keyvault-common/CMakeLists.txt +++ b/sdk/keyvault/azure-security-keyvault-common/CMakeLists.txt @@ -55,6 +55,11 @@ target_include_directories( target_link_libraries(azure-security-keyvault-common PUBLIC Azure::azure-core) +if(MSVC) + # C28020 and C28204 are introduced by nlohmann/json + target_compile_options(azure-security-keyvault-common PUBLIC /wd28204 /wd28020) +endif() + # coverage. Has no effect if BUILD_CODE_COVERAGE is OFF create_code_coverage(keyvault azure-security-keyvault-common azure-security-keyvault-common-test) diff --git a/sdk/keyvault/azure-security-keyvault-common/src/keyvault_exception.cpp b/sdk/keyvault/azure-security-keyvault-common/src/keyvault_exception.cpp index adfe7d769..194696293 100644 --- a/sdk/keyvault/azure-security-keyvault-common/src/keyvault_exception.cpp +++ b/sdk/keyvault/azure-security-keyvault-common/src/keyvault_exception.cpp @@ -6,7 +6,7 @@ #include -#include +#include #include using namespace Azure::Security::KeyVault::Common; @@ -43,7 +43,7 @@ KeyVaultException KeyVaultException::CreateFromResponse( if (contentType.find("json") != std::string::npos) { - auto jsonParser = nlohmann::json::parse(bodyBuffer); + auto jsonParser = Azure::Core::Internal::Json::json::parse(bodyBuffer); auto& error = jsonParser["error"]; errorCode = error["code"].get(); message = error["message"].get(); diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/key_vault_key.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/key_vault_key.cpp index ea16a79b8..6bf714968 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/key_vault_key.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/key_vault_key.cpp @@ -3,7 +3,7 @@ #include "azure/keyvault/keys/key_vault_key.hpp" -#include +#include using namespace Azure::Security::KeyVault::Keys; @@ -24,7 +24,7 @@ KeyVaultKey Details::KeyVaultKeyDeserialize( Azure::Core::Http::RawResponse const& rawResponse) { auto body = rawResponse.GetBody(); - auto jsonParser = nlohmann::json::parse(body); + auto jsonParser = Azure::Core::Internal::Json::json::parse(body); KeyVaultKey key(name); auto const& jsonKey = jsonParser["key"]; diff --git a/sdk/storage/README.md b/sdk/storage/README.md index 19688a00c..9688743c6 100644 --- a/sdk/storage/README.md +++ b/sdk/storage/README.md @@ -47,7 +47,7 @@ For general suggestions about Azure, use our [Azure feedback forum](https://feed On Windows, dependencies are managed by [vcpkg](https://github.com/microsoft/vcpkg). You can reference the [Quick Start](https://github.com/microsoft/vcpkg#quick-start-windows) to quickly set yourself up. After Vcpkg is initialized and bootstrapped, you can install the dependencies: ```BatchFile -vcpkg.exe install libxml2:x64-windows-static curl:x64-windows-static nlohmann-json:x64-windows-static +vcpkg.exe install libxml2:x64-windows-static curl:x64-windows-static ``` #### Unix Platforms @@ -58,7 +58,6 @@ You can use the package manager on different Unix platforms to install the depen - libxml2. - OpenSSL. - libcurl. - - nlohmann-json. ### Build from Source @@ -109,7 +108,6 @@ TODO when ready. ## Dependencies - [Azure Core SDK](https://github.com/Azure/azure-sdk-for-cpp/blob/master/README.md) - - [nlohmann/json](https://github.com/nlohmann/json) - libxml2 ## Code Samples diff --git a/sdk/storage/azure-storage-common/src/storage_exception.cpp b/sdk/storage/azure-storage-common/src/storage_exception.cpp index 47252fd9c..fc3862415 100644 --- a/sdk/storage/azure-storage-common/src/storage_exception.cpp +++ b/sdk/storage/azure-storage-common/src/storage_exception.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include "azure/storage/common/constants.hpp" #include "azure/storage/common/xml_wrapper.hpp" @@ -111,7 +111,7 @@ namespace Azure { namespace Storage { response->GetHeaders().at(Details::HttpHeaderContentType).find("json") != std::string::npos) { - auto jsonParser = nlohmann::json::parse(bodyBuffer); + auto jsonParser = Azure::Core::Internal::Json::json::parse(bodyBuffer); errorCode = jsonParser["error"]["code"].get(); message = jsonParser["error"]["message"].get(); } diff --git a/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/protocol/datalake_rest_client.hpp b/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/protocol/datalake_rest_client.hpp index 4fdb9a8aa..dfe925265 100644 --- a/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/protocol/datalake_rest_client.hpp +++ b/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/protocol/datalake_rest_client.hpp @@ -12,8 +12,6 @@ #include #include -#include - #include #include #include @@ -24,6 +22,8 @@ #include #include +#include + namespace Azure { namespace Storage { namespace Files { namespace DataLake { namespace Details { @@ -548,7 +548,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { Models::ServiceListFileSystemsResult result = bodyBuffer.empty() ? Models::ServiceListFileSystemsResult() : ServiceListFileSystemsResultFromFileSystemList( - FileSystemListFromJson(nlohmann::json::parse(bodyBuffer))); + FileSystemListFromJson(Azure::Core::Internal::Json::json::parse(bodyBuffer))); if (response.GetHeaders().find(Details::HeaderContinuationToken) != response.GetHeaders().end()) { @@ -564,7 +564,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { } } - static Models::FileSystem FileSystemFromJson(const nlohmann::json& node) + static Models::FileSystem FileSystemFromJson(const Azure::Core::Internal::Json::json& node) { Models::FileSystem result; result.Name = node["name"].get(); @@ -574,7 +574,8 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { return result; } - static Models::FileSystemList FileSystemListFromJson(const nlohmann::json& node) + static Models::FileSystemList FileSystemListFromJson( + const Azure::Core::Internal::Json::json& node) { Models::FileSystemList result; for (const auto& element : node["filesystems"]) @@ -950,7 +951,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { Models::FileSystemListPathsResult result = bodyBuffer.empty() ? Models::FileSystemListPathsResult() : FileSystemListPathsResultFromPathList( - PathListFromJson(nlohmann::json::parse(bodyBuffer))); + PathListFromJson(Azure::Core::Internal::Json::json::parse(bodyBuffer))); if (response.GetHeaders().find(Details::HeaderContinuationToken) != response.GetHeaders().end()) { @@ -966,7 +967,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { } } - static Models::Path PathFromJson(const nlohmann::json& node) + static Models::Path PathFromJson(const Azure::Core::Internal::Json::json& node) { Models::Path result; result.Name = node["name"].get(); @@ -987,7 +988,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { return result; } - static Models::PathList PathListFromJson(const nlohmann::json& node) + static Models::PathList PathListFromJson(const Azure::Core::Internal::Json::json& node) { Models::PathList result; for (const auto& element : node["paths"]) @@ -2057,7 +2058,8 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { Models::PathSetAccessControlRecursiveResult result = bodyBuffer.empty() ? Models::PathSetAccessControlRecursiveResult() : PathSetAccessControlRecursiveResultFromSetAccessControlRecursiveResponse( - SetAccessControlRecursiveResponseFromJson(nlohmann::json::parse(bodyBuffer))); + SetAccessControlRecursiveResponseFromJson( + Azure::Core::Internal::Json::json::parse(bodyBuffer))); if (response.GetHeaders().find(Details::HeaderContinuationToken) != response.GetHeaders().end()) { @@ -2073,7 +2075,8 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { } } - static Models::AclFailedEntry AclFailedEntryFromJson(const nlohmann::json& node) + static Models::AclFailedEntry AclFailedEntryFromJson( + const Azure::Core::Internal::Json::json& node) { Models::AclFailedEntry result; result.Name = node["name"].get(); @@ -2083,7 +2086,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { } static Models::SetAccessControlRecursiveResponse SetAccessControlRecursiveResponseFromJson( - const nlohmann::json& node) + const Azure::Core::Internal::Json::json& node) { Models::SetAccessControlRecursiveResponse result; result.DirectoriesSuccessful = node["directoriesSuccessful"].get(); diff --git a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/protocol/share_rest_client.hpp b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/protocol/share_rest_client.hpp index b97c510b5..ea12d02e3 100644 --- a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/protocol/share_rest_client.hpp +++ b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/protocol/share_rest_client.hpp @@ -14,8 +14,6 @@ #include #include -#include - #include #include #include @@ -26,6 +24,8 @@ #include #include +#include + namespace Azure { namespace Storage { namespace Files { namespace Shares { namespace Details { @@ -2877,7 +2877,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { std::string json_body; { - nlohmann::json json; + Azure::Core::Internal::Json::json json; SharePermissionToJson(json, createPermissionOptions.Permission); json_body = json.dump(); } @@ -3461,7 +3461,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { } static void SharePermissionToJson( - nlohmann::json& node, + Azure::Core::Internal::Json::json& node, const Models::SharePermission& object) { node["permission"] = object.Permission; @@ -3479,7 +3479,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { Models::ShareGetPermissionResult result = bodyBuffer.empty() ? Models::ShareGetPermissionResult() : ShareGetPermissionResultFromSharePermission( - SharePermissionFromJson(nlohmann::json::parse(bodyBuffer))); + SharePermissionFromJson(Azure::Core::Internal::Json::json::parse(bodyBuffer))); return Azure::Core::Response( std::move(result), std::move(responsePtr)); } @@ -3490,7 +3490,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { } } - static Models::SharePermission SharePermissionFromJson(const nlohmann::json& node) + static Models::SharePermission SharePermissionFromJson( + const Azure::Core::Internal::Json::json& node) { Models::SharePermission result; result.Permission = node["permission"].get();