From a0361680db6ba3d1153622cc3626bccba3b6dc39 Mon Sep 17 00:00:00 2001 From: Gareth Sylvester-Bradley Date: Fri, 23 Sep 2022 13:42:51 +0100 Subject: [PATCH 01/35] rql::details::rql_exception was previously thrown during evaluation when an unimplemented operator was encountered; if Query API subscriptions were created that used an unrecognized call-operator, this could result in an exception during subsequent nmos::insert_resource_events and therefore registrations to be rejected with a 501 Not Implemented, and uncaught exceptions when new WebSocket connections were opened for that subscription. This solves this by validating the call-operators immediately after the rql::parse_query so that such subscriptions are rejected (appropriately, with 501 Not Implemented), and (belt and braces) returning false, i.e. no match, if an exception is encountered during evaluation. --- Development/nmos/logging_api.cpp | 24 +++++++++++++------ Development/nmos/query_utils.cpp | 24 +++++++++++++++---- Development/rql/rql.cpp | 31 +++++++++++++++++++++++++ Development/rql/rql.h | 3 +++ Development/rql/test/rql_test.cpp | 38 +++++++++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 11 deletions(-) diff --git a/Development/nmos/logging_api.cpp b/Development/nmos/logging_api.cpp index bb93b521d..432d6fa95 100644 --- a/Development/nmos/logging_api.cpp +++ b/Development/nmos/logging_api.cpp @@ -36,16 +36,24 @@ namespace nmos return logging_api; } + static inline rql::extractor make_rql_extractor(const web::json::value& value) + { + return [&value](web::json::value& results, const web::json::value& key) + { + return web::json::extract(value.as_object(), results, key.as_string()); + }; + } + bool match_logging_rql(const web::json::value& value, const web::json::value& query) { - return query.is_null() || rql::evaluator + try { - [&value](web::json::value& results, const web::json::value& key) - { - return web::json::extract(value.as_object(), results, key.as_string()); - }, - rql::default_any_operators() - }(query) == rql::value_true; + return query.is_null() || rql::evaluator{ make_rql_extractor(value), rql::default_any_operators() }(query) == rql::value_true; + } + catch (const std::runtime_error&) // i.e. rql::details::rql_exception + { + return false; + } } // Predicate to match events against a query @@ -80,6 +88,8 @@ namespace nmos if (field.first == U("rql")) { rql_query = rql::parse_query(field.second.as_string()); + // validate against call-operators used in nmos::experimental::match_logging_rql + rql::validate_query(rql_query, rql::default_any_operators()); } // an error is reported for unimplemented parameters else diff --git a/Development/nmos/query_utils.cpp b/Development/nmos/query_utils.cpp index e49e484f8..14e381200 100644 --- a/Development/nmos/query_utils.cpp +++ b/Development/nmos/query_utils.cpp @@ -30,6 +30,8 @@ namespace nmos } } + rql::operators make_rql_operators(const nmos::resources& resources); + resource_query::resource_query(const nmos::api_version& version, const utility::string_t& resource_path, const web::json::value& flat_query_params) : version(version) , resource_path(resource_path) @@ -55,6 +57,8 @@ namespace nmos else if (field.first == U("rql")) { rql_query = rql::parse_query(field.second.as_string()); + // validate against call-operators used in nmos::match_rql + rql::validate_query(rql_query, make_rql_operators({})); } // extract the experimental flag, used to override the default behaviour that resources // "must have all [higher-versioned] keys stripped by the Query API before they are returned" @@ -239,7 +243,7 @@ namespace nmos : input; } - static inline rql::extractor make_extractor(const web::json::value& value) + static inline rql::extractor make_rql_extractor(const web::json::value& value) { return [&value](web::json::value& results, const web::json::value& key_path_) { @@ -277,7 +281,7 @@ namespace nmos const auto rel = [&resolve, &relation_name, &operators, &query](const web::json::value& relation_value) { // evaluate the call-operator against the specified data - return rql::evaluator{ make_extractor(resolve(relation_name, relation_value)), operators }(query); + return rql::evaluator{ make_rql_extractor(resolve(relation_name, relation_value)), operators }(query); }; // cf. rql::details::logical_or @@ -344,14 +348,26 @@ namespace nmos } } - bool match_rql(const web::json::value& value, const web::json::value& query, const nmos::resources& resources) + rql::operators make_rql_operators(const nmos::resources& resources) { auto operators = rql::default_any_operators(equal_to, less); operators[U("rel")] = std::bind(experimental::rel, std::cref(resources), std::placeholders::_1, std::placeholders::_2); operators[U("sub")] = experimental::sub; - return query.is_null() || rql::evaluator{ make_extractor(value), operators }(query) == rql::value_true; + return operators; + } + + bool match_rql(const web::json::value& value, const web::json::value& query, const nmos::resources& resources) + { + try + { + return query.is_null() || rql::evaluator{ make_rql_extractor(value), make_rql_operators(resources) }(query) == rql::value_true; + } + catch (const std::runtime_error&) // i.e. rql::details::rql_exception + { + return false; + } } resource_query::result_type resource_query::operator()(const nmos::api_version& resource_version, const nmos::api_version& resource_downgrade_version, const nmos::type& resource_type, const web::json::value& resource_data, const nmos::resources& resources) const diff --git a/Development/rql/rql.cpp b/Development/rql/rql.cpp index 6e196d977..57bd56290 100644 --- a/Development/rql/rql.cpp +++ b/Development/rql/rql.cpp @@ -308,6 +308,37 @@ namespace rql // Helpers for evaluating RQL + void validate_query(const web::json::value& query) + { + validate_query(query, default_operators()); + } + + void validate_query(const web::json::value& arg, const operators& operators) + { + if (is_call_operator(arg)) + { + const auto& name = arg.at(U("name")).as_string(); + const auto& args = arg.at(U("args")); + + const auto found = operators.find(name); + if (found == operators.end()) + { + throw details::unimplemented_operator(name); + } + validate_query(args, operators); + } + else if (arg.is_array()) + { + const auto& array_args = arg.as_array(); + + // depth-first recursion to report first unimplemented operator + for (const auto& array_arg : array_args) + { + validate_query(array_arg, operators); + } + } + } + evaluator::evaluator(extractor extract) : extract(extract) , operators(default_operators()) diff --git a/Development/rql/rql.h b/Development/rql/rql.h index bfbf06ebc..983c345d7 100644 --- a/Development/rql/rql.h +++ b/Development/rql/rql.h @@ -32,6 +32,9 @@ namespace rql typedef std::function extractor; typedef std::unordered_map> operators; + void validate_query(const web::json::value& query); // with default call-operators + void validate_query(const web::json::value& query, const operators& operators); + struct evaluator { explicit evaluator(extractor extract); // with default call-operators diff --git a/Development/rql/test/rql_test.cpp b/Development/rql/test/rql_test.cpp index a7eef8344..1382c0728 100644 --- a/Development/rql/test/rql_test.cpp +++ b/Development/rql/test/rql_test.cpp @@ -34,3 +34,41 @@ BST_TEST_CASE(testRqlParseQuery) BST_REQUIRE_STRING_EQUAL(U("baz%2Equx"), key_path.rbegin()->as_string()); } } + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testRqlValidateQuery) +{ + const rql::operators operators{ + { U("foo"), {} }, + { U("bar"), {} }, + { U("baz"), {} } + }; + + // no call-operator + { + const utility::string_t query_rql = U("meow"); + const auto rql_query = rql::parse_query(query_rql); + BST_REQUIRE_NO_THROW(rql::validate_query(rql_query, operators)); + } + + // only valid call-operators + { + const utility::string_t query_rql = U("foo(meow,bar(purr,baz(),hiss,(qux,yowl)))"); + const auto rql_query = rql::parse_query(query_rql); + BST_REQUIRE_NO_THROW(rql::validate_query(rql_query, operators)); + } + + // invalid call-operator + { + const utility::string_t query_rql = U("meow()"); + const auto rql_query = rql::parse_query(query_rql); + BST_REQUIRE_THROW(rql::validate_query(rql_query, operators), std::runtime_error); + } + + // invalid call-operator within an array arg nested in valid call-operators + { + const utility::string_t query_rql = U("foo(meow,bar(purr,baz(),hiss,(qux(),yowl)))"); + const auto rql_query = rql::parse_query(query_rql); + BST_REQUIRE_THROW(rql::validate_query(rql_query, operators), std::runtime_error); + } +} From 1f2d6a9964af4a5f2c0223ee034bdb40f0ed18ab Mon Sep 17 00:00:00 2001 From: Gareth Sylvester-Bradley Date: Wed, 2 Nov 2022 22:07:45 +0000 Subject: [PATCH 02/35] Remove Sandbox/conan-recipe, since now being maintained at https://github.com/conan-io/conan-center-index/tree/master/recipes/nmos-cpp --- README.md | 2 +- Sandbox/conan-recipe/CMakeLists.txt | 10 - Sandbox/conan-recipe/conandata.yml | 6 - Sandbox/conan-recipe/conanfile.py | 222 ------------------ .../conan-recipe/test_package/CMakeLists.txt | 11 - .../conan-recipe/test_package/conanfile.py | 43 ---- Sandbox/conan-recipe/test_package/main.cpp | 40 ---- 7 files changed, 1 insertion(+), 333 deletions(-) delete mode 100644 Sandbox/conan-recipe/CMakeLists.txt delete mode 100644 Sandbox/conan-recipe/conandata.yml delete mode 100644 Sandbox/conan-recipe/conanfile.py delete mode 100644 Sandbox/conan-recipe/test_package/CMakeLists.txt delete mode 100644 Sandbox/conan-recipe/test_package/conanfile.py delete mode 100644 Sandbox/conan-recipe/test_package/main.cpp diff --git a/README.md b/README.md index e0bd54da4..0862f1662 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ Recent activity on the project (newest first): - Update to RQL implementation to support advanced queries for string values containing '.' - Improvements to the SDP parser/generator - Improvements to Conan/CMake build, including updates to preferred version of dependencies such as Boost and OpenSSL -- Prepared a basic Conan recipe for building nmos-cpp, in [Sandbox/conan-recipe](Sandbox/conan-recipe) +- Prepared a basic Conan recipe for building nmos-cpp, in ~[Sandbox/conan-recipe](Sandbox/conan-recipe)~ - Refactored the CMake build to make it easier to use nmos-cpp from another project, demonstrated by [Sandbox/my-nmos-node](Sandbox/my-nmos-node) - Added support for BCP-004-01 Receiver Capabilities - Switched CI testing to run the nmos-cpp applications and the AMWA NMOS Testing Tool with secure communication (TLS) enabled, as per BCP-003-01 diff --git a/Sandbox/conan-recipe/CMakeLists.txt b/Sandbox/conan-recipe/CMakeLists.txt deleted file mode 100644 index 446552b52..000000000 --- a/Sandbox/conan-recipe/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# see https://github.com/conan-io/cmake-conan/issues/249#issuecomment-737011732 -cmake_minimum_required(VERSION 3.17) -project(cmake_wrapper) - -include(conanbuildinfo.cmake) -conan_basic_setup() - -# conanfile.py source() method extracts to source_subfolder -# nmos-cpp top-level CMakeLists.txt is in Development -add_subdirectory("source_subfolder/Development") diff --git a/Sandbox/conan-recipe/conandata.yml b/Sandbox/conan-recipe/conandata.yml deleted file mode 100644 index f5b501422..000000000 --- a/Sandbox/conan-recipe/conandata.yml +++ /dev/null @@ -1,6 +0,0 @@ -sources: - # see https://github.com/conan-io/conan-center-index/blob/master/docs/faqs.md#what-version-should-packages-use-for-libraries-without-official-releases - "cci.YYYYMMDD": - url: "https://github.com/sony/nmos-cpp/archive/.tar.gz" - "sony.master": - url: "https://github.com/sony/nmos-cpp/archive/master.tar.gz" diff --git a/Sandbox/conan-recipe/conanfile.py b/Sandbox/conan-recipe/conanfile.py deleted file mode 100644 index c24e5c142..000000000 --- a/Sandbox/conan-recipe/conanfile.py +++ /dev/null @@ -1,222 +0,0 @@ -import json -import os -import re -from conans import ConanFile, CMake, tools - -required_conan_version = ">=1.33.0" - -class NmosCppConan(ConanFile): - name = "nmos-cpp" - description = "An NMOS C++ Implementation" - license = "Apache-2.0" - url = "https://github.com/conan-io/conan-center-index" - homepage = "https://github.com/sony/nmos-cpp" - topics = ("amwa", "nmos", "is-04", "is-05", "is-07", "is-08", "is-09", "broadcasting", "network", "media") - - settings = "os", "compiler", "build_type", "arch" - # for now, no "shared" option support - options = { - "fPIC": [True, False], - } - # "fPIC" is handled automatically by Conan, injecting CMAKE_POSITION_INDEPENDENT_CODE - default_options = { - "fPIC": True, - } - - # wrapper CMakeLists.txt to call conan_basic_setup() - exports_sources = ["CMakeLists.txt"] - # use cmake_find_package_multi and prefer config-file packages - generators = "cmake", "cmake_find_package_multi" - - _cmake = None - - # for out-of-source build, cf. wrapper CMakeLists.txt - @property - def _source_subfolder(self): - return "source_subfolder" - - @property - def _build_subfolder(self): - return "build_subfolder" - - def config_options(self): - if self.settings.os == "Windows": - del self.options.fPIC - - def requirements(self): - # for now, consistent with project's conanfile.txt - self.requires("boost/1.79.0") - self.requires("cpprestsdk/2.10.18") - self.requires("websocketpp/0.8.2") - self.requires("openssl/1.1.1o") - self.requires("json-schema-validator/2.1.0") - - def build_requirements(self): - self.build_requires("cmake/[>3.17]") - - def validate(self): - if self.settings.compiler.get_safe("cppstd"): - tools.check_min_cppstd(self, 11) - - def source(self): - tools.get(**self.conan_data["sources"][self.version], - destination=self._source_subfolder, strip_root=True) - - def _configure_cmake(self): - if self._cmake: - return self._cmake - self._cmake = CMake(self) - # prefer config-file packages created by cmake_find_package_multi - # over any system-installed find-module packages - self._cmake.definitions["CMAKE_FIND_PACKAGE_PREFER_CONFIG"] = True - # no need to build unit tests - self._cmake.definitions["NMOS_CPP_BUILD_TESTS"] = False - # the examples (nmos-cpp-registry and nmos-cpp-node) are useful utilities for users - self._cmake.definitions["NMOS_CPP_BUILD_EXAMPLES"] = True - # out-of-source build - self._cmake.configure(build_folder=self._build_subfolder) - return self._cmake - - def build(self): - cmake = self._configure_cmake() - cmake.build() - - def package(self): - self.copy("LICENSE", dst="licenses", src=self._source_subfolder) - cmake = self._configure_cmake() - cmake.install() - cmake_folder = os.path.join(self.package_folder, "lib", "cmake") - self._create_components_file_from_cmake_target_file(os.path.join(cmake_folder, "nmos-cpp", "nmos-cpp-targets.cmake")) - # remove the project's own generated config-file package - tools.rmdir(cmake_folder) - - # based on abseil recipe - # see https://github.com/conan-io/conan-center-index/blob/master/recipes/abseil/all/conanfile.py - def _create_components_file_from_cmake_target_file(self, target_file_path): - components = {} - - target_content = tools.load(target_file_path) - - cmake_functions = re.findall(r"(?Padd_library|set_target_properties)[\n|\s]*\([\n|\s]*(?P[^)]*)\)", target_content) - for (cmake_function_name, cmake_function_args) in cmake_functions: - cmake_function_args = re.split(r"[\s|\n]+", cmake_function_args, maxsplit=2) - - cmake_imported_target_name = cmake_function_args[0] - cmake_target_nonamespace = cmake_imported_target_name.replace("nmos-cpp::", "") - component_name = cmake_target_nonamespace.lower() - # Conan component name cannot be the same as the package name - if component_name == "nmos-cpp": - component_name = "nmos-cpp-lib" - - components.setdefault(component_name, {"cmake_target": cmake_target_nonamespace}) - - if cmake_function_name == "add_library": - cmake_imported_target_type = cmake_function_args[1] - if cmake_imported_target_type in ["STATIC", "SHARED"]: - # library filenames are based on the target name by default - lib_name = cmake_target_nonamespace - # the filename may be changed by a straightforward command: - # set_property(TARGET Bonjour PROPERTY OUTPUT_NAME dnssd) - # but we'd have to read the nmos-cpp-targets-.cmake files - # and parse the IMPORTED_LOCATION_ values - if lib_name == "Bonjour": - lib_name = "dnssd" - components[component_name]["libs"] = [lib_name] - elif cmake_function_name == "set_target_properties": - target_properties = re.findall(r"(?PINTERFACE_[A-Z_]+)[\n|\s]+\"(?P.+)\"", cmake_function_args[2]) - for target_property in target_properties: - property_type = target_property[0] - # '\', '$' and '"' are escaped; '$' especially is important here - # see https://github.com/conan-io/conan/blob/release/1.39/conans/client/generators/cmake_common.py#L43-L48 - property_values = re.sub(r"\\(.)", r"\1", target_property[1]).split(";") - if property_type == "INTERFACE_LINK_LIBRARIES": - for dependency in property_values: - match_private = re.fullmatch(r"\$", dependency) - if match_private: - dependency = match_private.group(1) - if "::" in dependency: - dependency = dependency.replace("nmos-cpp::", "") - # Conan component name cannot be the same as the package name - if dependency == "nmos-cpp": - dependency = "nmos-cpp-lib" - # Conan packages for Boost, cpprestsdk, websocketpp and OpenSSL have component names that (except for being lowercase) match the CMake targets - # json-schema-validator overrides cmake_find_package[_multi] names - elif dependency == "nlohmann_json_schema_validator::nlohmann_json_schema_validator": - dependency = "json-schema-validator::json-schema-validator" - components[component_name].setdefault("requires" if not match_private else "requires_private", []).append(dependency.lower()) - elif "${_IMPORT_PREFIX}/lib/" in dependency: - self.output.warn("{} recipe does not handle {} {} (yet)".format(self.name, property_type, dependency)) - else: - components[component_name].setdefault("system_libs", []).append(dependency) - elif property_type == "INTERFACE_COMPILE_DEFINITIONS": - for property_value in property_values: - components[component_name].setdefault("defines", []).append(property_value) - elif property_type == "INTERFACE_COMPILE_FEATURES": - for property_value in property_values: - if property_value not in ["cxx_std_11"]: - self.output.warn("{} recipe does not handle {} {} (yet)".format(self.name, property_type, property_value)) - elif property_type == "INTERFACE_COMPILE_OPTIONS": - for property_value in property_values: - # handle forced include (Visual Studio /FI, gcc -include) by relying on includedirs containing "include" - property_value = property_value.replace("${_IMPORT_PREFIX}/include/", "") - components[component_name].setdefault("cxxflags", []).append(property_value) - elif property_type == "INTERFACE_INCLUDE_DIRECTORIES": - for property_value in property_values: - if property_value not in ["${_IMPORT_PREFIX}/include"]: - self.output.warn("{} recipe does not handle {} {} (yet)".format(self.name, property_type, property_value)) - elif property_type == "INTERFACE_LINK_OPTIONS": - for property_value in property_values: - # workaround required because otherwise "/ignore:4099" gets converted to "\ignore:4099.obj" - # thankfully the MSVC linker accepts both '/' and '-' for the option specifier and Visual Studio - # handles link options appearing in Link/AdditionalDependencies rather than Link/AdditionalOptions - # because the CMake generators put them in INTERFACE_LINK_LIBRARIES rather than INTERFACE_LINK_OPTIONS - # see https://github.com/conan-io/conan/pull/8812 - # and https://docs.microsoft.com/en-us/cpp/build/reference/linking?view=msvc-160#command-line - property_value = re.sub(r"^/", r"-", property_value) - components[component_name].setdefault("linkflags", []).append(property_value) - else: - self.output.warn("{} recipe does not handle {} (yet)".format(self.name, property_type)) - - # Save components informations in json file - with open(self._components_helper_filepath, "w") as json_file: - json.dump(components, json_file, indent=4) - - @property - def _components_helper_filepath(self): - return os.path.join(self.package_folder, "lib", "components.json") - - def package_info(self): - bindir = "bin" - libdir = "lib" - # on Windows, cmake_install() puts the binaries in a config-specific sub-folder - if self.settings.os == "Windows": - config_install_dir = "Debug" if self.settings.build_type == "Debug" else "Release" - bindir = os.path.join(bindir, config_install_dir) - libdir = os.path.join(libdir, config_install_dir) - - def _register_components(): - components_json_file = tools.load(self._components_helper_filepath) - components = json.loads(components_json_file) - for component_name, values in components.items(): - cmake_target = values["cmake_target"] - self.cpp_info.components[component_name].names["cmake_find_package"] = cmake_target - self.cpp_info.components[component_name].names["cmake_find_package_multi"] = cmake_target - self.cpp_info.components[component_name].libs = values.get("libs", []) - self.cpp_info.components[component_name].libdirs = [libdir] - self.cpp_info.components[component_name].defines = values.get("defines", []) - self.cpp_info.components[component_name].cxxflags = values.get("cxxflags", []) - linkflags = values.get("linkflags", []) - self.cpp_info.components[component_name].sharedlinkflags = linkflags - self.cpp_info.components[component_name].exelinkflags = linkflags - self.cpp_info.components[component_name].system_libs = values.get("system_libs", []) - self.cpp_info.components[component_name].frameworks = values.get("frameworks", []) - self.cpp_info.components[component_name].requires = values.get("requires", []) - # hmm, how should private requirements be indicated? this results in a string format error... - #self.cpp_info.components[component_name].requires.extend([(r, "private") for r in values.get("requires_private", [])]) - self.cpp_info.components[component_name].requires.extend(values.get("requires_private", [])) - _register_components() - - # add nmos-cpp-registry and nmos-cpp-node to the path - bin_path = os.path.join(self.package_folder, bindir) - self.output.info("Appending PATH environment variable: {}".format(bin_path)) - self.env_info.PATH.append(bin_path) diff --git a/Sandbox/conan-recipe/test_package/CMakeLists.txt b/Sandbox/conan-recipe/test_package/CMakeLists.txt deleted file mode 100644 index 9fd1df2d4..000000000 --- a/Sandbox/conan-recipe/test_package/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 3.1) -project(NmosCppTestPackage CXX) - -include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) -conan_basic_setup(TARGETS) - -find_package(nmos-cpp REQUIRED) - -add_executable(test_package main.cpp) -target_link_libraries(test_package nmos-cpp::compile-settings nmos-cpp::nmos-cpp) -set_target_properties(test_package PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED ON) diff --git a/Sandbox/conan-recipe/test_package/conanfile.py b/Sandbox/conan-recipe/test_package/conanfile.py deleted file mode 100644 index bc0089a6a..000000000 --- a/Sandbox/conan-recipe/test_package/conanfile.py +++ /dev/null @@ -1,43 +0,0 @@ -import os -import subprocess -from six import StringIO -from conans import ConanFile, CMake, tools - -class NmosCppTestPackageConan(ConanFile): - settings = "os", "compiler", "build_type", "arch" - # use cmake_find_package_multi because the project installs a config-file package - generators = "cmake", "cmake_find_package_multi" - - def build(self): - cmake = CMake(self) - cmake.configure() - cmake.build() - - def test(self): - if not tools.cross_building(self): - with open("registry-config.json", "w") as config: - config.write('{"http_port": 10000, "domain": "local.", "pri": 51967}') - with open("node-config.json", "w") as config: - config.write('{"http_port": 20000, "domain": "local.", "highest_pri": 51967, "lowest_pri": 51967}') - - # start up the installed nmos-cpp-registry to check it works - registry = subprocess.Popen(["nmos-cpp-registry", "registry-config.json"], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - - # run the test_package node which should have time to register and then exit - node_out = StringIO() - try: - bin_path = os.path.join("bin", "test_package") - self.run(bin_path + " node-config.json", run_environment=True, output=node_out) - finally: - registry.terminate() - if "Adopting registered operation" not in node_out.getvalue(): - self.output.warn("test_package node failed to register with nmos-cpp-registry\n" - "\n" - "nmos-cpp-registry log:\n" - "{}\n" - "test_package log:\n" - "{}" - .format(registry.communicate()[0], node_out.getvalue())) diff --git a/Sandbox/conan-recipe/test_package/main.cpp b/Sandbox/conan-recipe/test_package/main.cpp deleted file mode 100644 index 56040987c..000000000 --- a/Sandbox/conan-recipe/test_package/main.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include "cpprest/json_utils.h" -#include "nmos/id.h" -#include "nmos/log_gate.h" -#include "nmos/model.h" -#include "nmos/node_resource.h" -#include "nmos/node_server.h" -#include "nmos/server.h" - -const web::json::field_with_default how_long{ U("how_long"), 2000 }; - -int main(int argc, char* argv[]) -{ - nmos::node_model node_model; - nmos::experimental::log_model log_model; - nmos::experimental::log_gate gate(std::cerr, std::cout, log_model); - nmos::experimental::node_implementation node_implementation; - - if (argc > 1) - { - std::ifstream file(argv[1]); - node_model.settings = web::json::value::parse(file); - } - nmos::insert_node_default_settings(node_model.settings); - - auto node_server = nmos::experimental::make_node_server(node_model, node_implementation, log_model, gate); - nmos::insert_resource(node_model.node_resources, nmos::make_node(nmos::make_id(), node_model.settings)); - - try - { - nmos::server_guard node_server_guard(node_server); - std::this_thread::sleep_for(std::chrono::milliseconds(how_long(node_model.settings))); - } - catch (const std::exception& e) - { - slog::log(gate, SLOG_FLF) << "Exception: " << e.what(); - } - return 0; -} From f4d1f3bd3e750a27d0678c63533e160360b96167 Mon Sep 17 00:00:00 2001 From: Gareth Sylvester-Bradley Date: Wed, 2 Nov 2022 22:38:45 +0000 Subject: [PATCH 03/35] Bump current tested Conan version to 1.53.0 --- Development/cmake/NmosCppConan.cmake | 2 +- Documents/Dependencies.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Development/cmake/NmosCppConan.cmake b/Development/cmake/NmosCppConan.cmake index 2721382df..977c035ca 100644 --- a/Development/cmake/NmosCppConan.cmake +++ b/Development/cmake/NmosCppConan.cmake @@ -17,7 +17,7 @@ include(${CMAKE_CURRENT_BINARY_DIR}/conan.cmake) # it would be nice to output a message if its a more recent version than tested, like: # "Found Conan version 99.99 that is higher than the current tested version: " ${CONAN_VERSION_CUR}) set(CONAN_VERSION_MIN "1.47.0") -set(CONAN_VERSION_CUR "1.48.1") +set(CONAN_VERSION_CUR "1.53.0") conan_check(VERSION ${CONAN_VERSION_MIN} REQUIRED) set(NMOS_CPP_CONAN_BUILD_LIBS "missing" CACHE STRING "Semicolon separated list of libraries to build rather than download") diff --git a/Documents/Dependencies.md b/Documents/Dependencies.md index d43fe43da..a130a1b42 100644 --- a/Documents/Dependencies.md +++ b/Documents/Dependencies.md @@ -57,7 +57,7 @@ By default nmos-cpp uses [Conan](https://conan.io) to download most of its depen 2. Install Conan using `pip install conan` Notes: - On some platforms with Python 2 and Python 3 both installed this may need to be `pip3 install conan` - - Currently, Conan 1.47 or higher is required; version 1.48.1 (latest release at the time) has been tested + - Currently, Conan 1.47 or higher is required; version 1.53.0 (latest release at the time) has been tested - Conan evolves fairly quickly, so it's worth running `pip install --upgrade conan` regularly - By default [Conan assumes semver compatibility](https://docs.conan.io/en/1.42/creating_packages/define_abi_compatibility.html#versioning-schema). Boost and other C++ libraries do not meet this expectation and break ABI compatibility between e.g. minor versions. From cd6d15f03d47d06ba8f2e9e7cf4b71baa86a3e5d Mon Sep 17 00:00:00 2001 From: Gareth Sylvester-Bradley Date: Wed, 2 Nov 2022 22:15:35 +0000 Subject: [PATCH 04/35] Bump OpenSSL to 1.1.1s --- Development/conanfile.txt | 2 +- Documents/Dependencies.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Development/conanfile.txt b/Development/conanfile.txt index 8a352e9b7..39b6c4f8c 100644 --- a/Development/conanfile.txt +++ b/Development/conanfile.txt @@ -2,7 +2,7 @@ boost/1.79.0 cpprestsdk/2.10.18 websocketpp/0.8.2 -openssl/1.1.1o +openssl/1.1.1s json-schema-validator/2.1.0 zlib/1.2.13 diff --git a/Documents/Dependencies.md b/Documents/Dependencies.md index a130a1b42..764214922 100644 --- a/Documents/Dependencies.md +++ b/Documents/Dependencies.md @@ -240,7 +240,7 @@ It is also possible to use OpenSSL 1.0, but the OpenSSL team announced that [use 1. Download and install a recent release Notes: - On Windows, an installer can be downloaded from [Shining Light Productions - Win32 OpenSSL](https://slproweb.com/products/Win32OpenSSL.html) - The Win64 OpenSSL v1.1.1o installer (latest release at the time) has been tested + The Win64 OpenSSL v1.1.1s installer (latest release at the time) has been tested - On Linux distributions, an OpenSSL package may already be available The Ubuntu team announced an [OpenSSL 1.1.1 stable release update (SRU) for Ubuntu 18.04 LTS](https://lists.ubuntu.com/archives/ubuntu-devel/2018-December/040567.html) From 096868a30b7d64c20ac1a4a28714cc9e4d0cdda8 Mon Sep 17 00:00:00 2001 From: Gareth Sylvester-Bradley Date: Wed, 2 Nov 2022 22:18:30 +0000 Subject: [PATCH 05/35] Bump Boost to 1.80.0 --- Development/cmake/NmosCppDependencies.cmake | 2 +- Development/conanfile.txt | 2 +- Documents/Dependencies.md | 12 ++++++------ Documents/Getting-Started.md | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Development/cmake/NmosCppDependencies.cmake b/Development/cmake/NmosCppDependencies.cmake index 7b0b79d1f..9e976aae5 100644 --- a/Development/cmake/NmosCppDependencies.cmake +++ b/Development/cmake/NmosCppDependencies.cmake @@ -1,7 +1,7 @@ # Boost set(BOOST_VERSION_MIN "1.54.0") -set(BOOST_VERSION_CUR "1.79.0") +set(BOOST_VERSION_CUR "1.80.0") # note: 1.57.0 doesn't work due to https://svn.boost.org/trac10/ticket/10754 # note: some components are only required for one platform or other # so find_package(Boost) is called after adding those components diff --git a/Development/conanfile.txt b/Development/conanfile.txt index 39b6c4f8c..27591fd83 100644 --- a/Development/conanfile.txt +++ b/Development/conanfile.txt @@ -1,5 +1,5 @@ [requires] -boost/1.79.0 +boost/1.80.0 cpprestsdk/2.10.18 websocketpp/0.8.2 openssl/1.1.1s diff --git a/Documents/Dependencies.md b/Documents/Dependencies.md index 764214922..74047a2ea 100644 --- a/Documents/Dependencies.md +++ b/Documents/Dependencies.md @@ -80,9 +80,9 @@ If using Conan, this section can be skipped. 1. Download a [recent release](http://www.boost.org/users/download/) Notes: - - Several Boost releases have been tested, including Version 1.79.0 (latest release at the time) and Version 1.54.0 + - Several Boost releases have been tested, including Version 1.80.0 (latest release at the time) and Version 1.54.0 - On Linux distributions, a Boost libraries package may already be installed, e.g. Ubuntu 14.04 LTS has Version 1.54.0 -2. Expand the archive so that, for example, the boost\_1\_79\_0 directory is at the same level as the nmos-cpp directory +2. Expand the archive so that, for example, the boost\_1\_80\_0 directory is at the same level as the nmos-cpp directory 3. Build and stage (or install) the following Boost libraries for your platform/toolset: - atomic - chrono @@ -152,8 +152,8 @@ If using Conan, this section can be skipped. - Set ``Boost_USE_STATIC_LIBS`` (BOOL) to ``1`` (true) - If CMake cannot find it automatically, set hints for [finding Boost](https://cmake.org/cmake/help/latest/module/FindBoost.html), for example: - *Either* set ``Boost_DIR`` (PATH) to the location of the installed BoostConfig.cmake (since Boost 1.70.0) - - *Or* set ``BOOST_INCLUDEDIR`` (PATH) and ``BOOST_LIBRARYDIR`` (PATH) to the appropriate full paths, e.g. *````*``/boost_1_79_0`` - and *````*``/boost_1_79_0/x64/lib`` respectively to match the suggested ``b2`` command + - *Or* set ``BOOST_INCLUDEDIR`` (PATH) and ``BOOST_LIBRARYDIR`` (PATH) to the appropriate full paths, e.g. *````*``/boost_1_80_0`` + and *````*``/boost_1_80_0/x64/lib`` respectively to match the suggested ``b2`` command - Due to interactions with other dependencies, it may also be necessary to explicitly set ``WERROR`` (BOOL) to ``0`` so that compiler warnings are not treated as errors - To speed up the build by omitting the C++ REST SDK sample apps and test suite, set ``BUILD_SAMPLES`` and ``BUILD_TESTS`` (BOOL) to ``0`` (false) 3. Use CMake to generate build/project files, and then build *and* install @@ -172,8 +172,8 @@ cmake .. ^ -DCPPREST_EXCLUDE_COMPRESSION:BOOL="1" ^ -DCMAKE_CONFIGURATION_TYPES:STRING="Debug;Release" ^ -DBoost_USE_STATIC_LIBS:BOOL="1" ^ - -DBOOST_INCLUDEDIR:PATH="/boost_1_79_0" ^ - -DBOOST_LIBRARYDIR:PATH="/boost_1_79_0/x64/lib" ^ + -DBOOST_INCLUDEDIR:PATH="/boost_1_80_0" ^ + -DBOOST_LIBRARYDIR:PATH="/boost_1_80_0/x64/lib" ^ -DWERROR:BOOL="0" ^ -DBUILD_SAMPLES:BOOL="0" ^ -DBUILD_TESTS:BOOL="0" diff --git a/Documents/Getting-Started.md b/Documents/Getting-Started.md index 344010872..f5bd80f2a 100644 --- a/Documents/Getting-Started.md +++ b/Documents/Getting-Started.md @@ -32,8 +32,8 @@ Notes: - If CMake cannot find it automatically, set hints for [finding Boost](https://cmake.org/cmake/help/latest/module/FindBoost.html), for example: - *Either* set ``Boost_DIR`` (PATH) to the location of the installed *BoostConfig.cmake* (since Boost 1.70.0) - - *Or* set ``BOOST_INCLUDEDIR`` (PATH) and ``BOOST_LIBRARYDIR`` (PATH) to the appropriate full paths, e.g. *````*``/boost_1_79_0`` - and *````*``/boost_1_79_0/x64/lib`` respectively to match the suggested ``b2`` command + - *Or* set ``BOOST_INCLUDEDIR`` (PATH) and ``BOOST_LIBRARYDIR`` (PATH) to the appropriate full paths, e.g. *````*``/boost_1_80_0`` + and *````*``/boost_1_80_0/x64/lib`` respectively to match the suggested ``b2`` command - If CMake cannot find them automatically, set hints for finding the C++ REST SDK and WebSocket++, for example: - Set ``cpprestsdk_DIR`` (PATH) to the location of the installed *cpprestsdk-config.cmake* - *Either* set ``websocketpp_DIR`` (PATH) to the location of the installed *websocketpp-config.cmake* @@ -75,8 +75,8 @@ cmake .. ^ -G "Visual Studio 16 2019" ^ -DCMAKE_CONFIGURATION_TYPES:STRING="Debug;Release" ^ -DBoost_USE_STATIC_LIBS:BOOL="1" ^ - -DBOOST_INCLUDEDIR:PATH="/boost_1_79_0" ^ - -DBOOST_LIBRARYDIR:PATH="/boost_1_79_0/x64/lib" ^ + -DBOOST_INCLUDEDIR:PATH="/boost_1_80_0" ^ + -DBOOST_LIBRARYDIR:PATH="/boost_1_80_0/x64/lib" ^ -DWEBSOCKETPP_INCLUDE_DIR:PATH="/cpprestsdk/Release/libs/websocketpp" ``` From bffdd0df71e450ec03bb3101ee07ecb187e6bb85 Mon Sep 17 00:00:00 2001 From: Gareth Sylvester-Bradley Date: Wed, 2 Nov 2022 22:26:16 +0000 Subject: [PATCH 06/35] Bump nlohmann/json to 3.11.2 --- Development/third_party/nlohmann/README.md | 2 +- Development/third_party/nlohmann/json.hpp | 5337 ++++++++++++++------ 2 files changed, 3922 insertions(+), 1417 deletions(-) diff --git a/Development/third_party/nlohmann/README.md b/Development/third_party/nlohmann/README.md index 0d6bbf495..712836bd9 100644 --- a/Development/third_party/nlohmann/README.md +++ b/Development/third_party/nlohmann/README.md @@ -7,7 +7,7 @@ This directory contains the single header version of the [JSON for Modern C++](h Original source code: - Licensed under the MIT License . -- Copyright (c) 2013-2022 Niels Lohmann . +- Copyright (c) 2013-2022 Niels Lohmann . ## Modern C++ JSON schema validator diff --git a/Development/third_party/nlohmann/json.hpp b/Development/third_party/nlohmann/json.hpp index cb27e0581..5dd555f0c 100644 --- a/Development/third_party/nlohmann/json.hpp +++ b/Development/third_party/nlohmann/json.hpp @@ -1,31 +1,10 @@ -/* - __ _____ _____ _____ - __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.10.5 -|_____|_____|_____|_|___| https://github.com/nlohmann/json - -Licensed under the MIT License . -SPDX-License-Identifier: MIT -Copyright (c) 2013-2022 Niels Lohmann . - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT /****************************************************************************\ * Note on documentation: The source files contain links to the online * @@ -33,16 +12,12 @@ SOFTWARE. * contains the most recent documentation and should also be applicable to * * previous versions; documentation for deprecated functions is not * * removed, but marked deprecated. See "Generate documentation" section in * - * file doc/README.md. * + * file docs/README.md. * \****************************************************************************/ #ifndef INCLUDE_NLOHMANN_JSON_HPP_ #define INCLUDE_NLOHMANN_JSON_HPP_ -#define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 10 -#define NLOHMANN_JSON_VERSION_PATCH 5 - #include // all_of, find, for_each #include // nullptr_t, ptrdiff_t, size_t #include // hash, less @@ -58,12 +33,129 @@ SOFTWARE. #include // vector // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + -#include #include +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// This file contains all macro definitions affecting or depending on the ABI + +#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK + #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) + #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2 + #warning "Already included a different version of the library!" + #endif + #endif +#endif + +#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) +#define NLOHMANN_JSON_VERSION_PATCH 2 // NOLINT(modernize-macro-to-enum) + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + +#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 +#endif + +#if JSON_DIAGNOSTICS + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag +#else + #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS +#endif + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp +#else + #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION + #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 +#endif + +// Construct the namespace ABI tags component +#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b +#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ + NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) + +#define NLOHMANN_JSON_ABI_TAGS \ + NLOHMANN_JSON_ABI_TAGS_CONCAT( \ + NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ + NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) + +// Construct the namespace version component +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ + _v ## major ## _ ## minor ## _ ## patch +#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) + +#if NLOHMANN_JSON_NAMESPACE_NO_VERSION +#define NLOHMANN_JSON_NAMESPACE_VERSION +#else +#define NLOHMANN_JSON_NAMESPACE_VERSION \ + NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ + NLOHMANN_JSON_VERSION_MINOR, \ + NLOHMANN_JSON_VERSION_PATCH) +#endif + +// Combine namespace components +#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b +#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ + NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) + +#ifndef NLOHMANN_JSON_NAMESPACE +#define NLOHMANN_JSON_NAMESPACE \ + nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN +#define NLOHMANN_JSON_NAMESPACE_BEGIN \ + namespace nlohmann \ + { \ + inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ + NLOHMANN_JSON_ABI_TAGS, \ + NLOHMANN_JSON_NAMESPACE_VERSION) \ + { +#endif + +#ifndef NLOHMANN_JSON_NAMESPACE_END +#define NLOHMANN_JSON_NAMESPACE_END \ + } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ + } // namespace nlohmann +#endif + // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + #include // transform @@ -79,14 +171,31 @@ SOFTWARE. #include // valarray // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + +#include // nullptr_t #include // exception #include // runtime_error #include // to_string #include // vector // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + #include // array @@ -94,102 +203,130 @@ SOFTWARE. #include // uint8_t #include // string -namespace nlohmann -{ +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include // declval, pair +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +#include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { -/////////////////////////// -// JSON type enumeration // -/////////////////////////// -/*! -@brief the JSON type enumeration +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; -This enumeration collects the different JSON types. It is internally used to -distinguish the stored values, and the functions @ref basic_json::is_null(), -@ref basic_json::is_object(), @ref basic_json::is_array(), -@ref basic_json::is_string(), @ref basic_json::is_boolean(), -@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), -@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), -@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and -@ref basic_json::is_structured() rely on it. +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END -@note There are three enumeration entries (number_integer, number_unsigned, and -number_float), because the library distinguishes these three types for numbers: -@ref basic_json::number_unsigned_t is used for unsigned integers, -@ref basic_json::number_integer_t is used for signed integers, and -@ref basic_json::number_float_t is used for floating-point numbers or to -approximate integers which do not fit in the limits of their respective type. -@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON -value with the default value for a given type +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ -@since version 1.0.0 -*/ -enum class value_t : std::uint8_t +// https://en.cppreference.com/w/cpp/experimental/is_detected +struct nonesuch { - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (signed integer) - number_unsigned, ///< number value (unsigned integer) - number_float, ///< number value (floating-point) - binary, ///< binary array (ordered collection of bytes) - discarded ///< discarded by the parser callback function + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; }; -/*! -@brief comparison operator for JSON types - -Returns an ordering that is similar to Python: -- 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. +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; -@since version 1.0.0 -*/ -inline bool operator<(const value_t lhs, const value_t rhs) noexcept +template class Op, class... Args> +struct detector>, Op, Args...> { - static constexpr std::array order = {{ - 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, - 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, - 6 /* binary */ - } - }; + using value_t = std::true_type; + using type = Op; +}; - const auto l_index = static_cast(lhs); - const auto r_index = static_cast(rhs); - return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; -} -} // namespace detail -} // namespace nlohmann +template class Op, class... Args> +using is_detected = typename detector::value_t; -// #include +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; +template class Op, class... Args> +using detected_t = typename detector::type; -#include -// #include +template class Op, class... Args> +using detected_or = detector; +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END -#include // declval, pair // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson +// SPDX-License-Identifier: MIT + /* 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(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) @@ -2223,130 +2360,62 @@ JSON_HEDLEY_DIAGNOSTIC_POP #endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ -// #include +// This file contains all internal macro definitions (except those affecting ABI) +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them -#include +// #include -// #include +// 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 -namespace nlohmann -{ -namespace detail -{ -template struct make_void -{ - using type = void; -}; -template using void_t = typename make_void::type; -} // namespace detail -} // namespace nlohmann +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif +#ifdef __has_include + #if __has_include() + #include + #endif +#endif -// https://en.cppreference.com/w/cpp/experimental/is_detected -namespace nlohmann -{ -namespace detail -{ -struct nonesuch -{ - nonesuch() = delete; - ~nonesuch() = delete; - nonesuch(nonesuch const&) = delete; - nonesuch(nonesuch const&&) = delete; - void operator=(nonesuch const&) = delete; - void operator=(nonesuch&&) = delete; -}; - -template class Op, - class... Args> -struct detector -{ - using value_t = std::false_type; - using type = Default; -}; - -template class Op, class... Args> -struct detector>, Op, Args...> -{ - using value_t = std::true_type; - using type = Op; -}; - -template class Op, class... Args> -using is_detected = typename detector::value_t; - -template class Op, class... Args> -struct is_detected_lazy : is_detected { }; - -template class Op, class... Args> -using detected_t = typename detector::type; - -template class Op, class... Args> -using detected_or = detector; - -template class Op, class... Args> -using detected_or_t = typename detected_or::type; - -template class Op, class... Args> -using is_detected_exact = std::is_same>; - -template class Op, class... Args> -using is_detected_convertible = - std::is_convertible, To>; -} // namespace detail -} // namespace nlohmann - - -// 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 the user manually specified the used c++ version this is skipped -#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) - #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) - #define JSON_HAS_CPP_20 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) - #define JSON_HAS_CPP_14 - #endif - // the cpp 11 flag is always specified because it is the minimal required version - #define JSON_HAS_CPP_11 -#endif - -#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) - #ifdef JSON_HAS_CPP_17 - #if defined(__cpp_lib_filesystem) - #define JSON_HAS_FILESYSTEM 1 - #elif defined(__cpp_lib_experimental_filesystem) - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #elif !defined(__has_include) - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #elif __has_include() - #define JSON_HAS_FILESYSTEM 1 - #elif __has_include() - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #endif +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 @@ -2367,7 +2436,7 @@ using is_detected_convertible = #endif // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support - #if defined(_MSC_VER) && _MSC_VER < 1940 + #if defined(_MSC_VER) && _MSC_VER < 1914 #undef JSON_HAS_FILESYSTEM #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM #endif @@ -2394,6 +2463,38 @@ using is_detected_convertible = #define JSON_HAS_FILESYSTEM 0 #endif +#ifndef JSON_HAS_THREE_WAY_COMPARISON + #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ + && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L + #define JSON_HAS_THREE_WAY_COMPARISON 1 + #else + #define JSON_HAS_THREE_WAY_COMPARISON 0 + #endif +#endif + +#ifndef JSON_HAS_RANGES + // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error + #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 + #define JSON_HAS_RANGES 0 + #elif defined(__cpp_lib_ranges) + #define JSON_HAS_RANGES 1 + #else + #define JSON_HAS_RANGES 0 + #endif +#endif + +#ifdef JSON_HAS_CPP_17 + #define JSON_INLINE_VARIABLE inline +#else + #define JSON_INLINE_VARIABLE +#endif + +#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) + #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else + #define JSON_NO_UNIQUE_ADDRESS +#endif + // disable documentation warnings on clang #if defined(__clang__) #pragma clang diagnostic push @@ -2631,6 +2732,7 @@ using is_detected_convertible = #define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; #define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); /*! @brief macro @@ -2641,6 +2743,10 @@ using is_detected_convertible = friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + /*! @brief macro @def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE @@ -2650,6 +2756,10 @@ using is_detected_convertible = inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + // inspired from https://stackoverflow.com/a/26745591 // allows to call any std function as if (e.g. with begin): @@ -2699,13 +2809,132 @@ using is_detected_convertible = #define JSON_EXPLICIT explicit #endif -#ifndef JSON_DIAGNOSTICS - #define JSON_DIAGNOSTICS 0 +#ifndef JSON_DISABLE_ENUM_SERIALIZATION + #define JSON_DISABLE_ENUM_SERIALIZATION 0 +#endif + +#ifndef JSON_USE_GLOBAL_UDLS + #define JSON_USE_GLOBAL_UDLS 1 +#endif + +#if JSON_HAS_THREE_WAY_COMPARISON + #include // partial_ordering +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ + +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + 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 < 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 +*/ +#if JSON_HAS_THREE_WAY_COMPARISON + inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* +#else + inline bool operator<(const value_t lhs, const value_t rhs) noexcept #endif +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); +#if JSON_HAS_THREE_WAY_COMPARISON + if (l_index < order.size() && r_index < order.size()) + { + return order[l_index] <=> order[r_index]; // *NOPAD* + } + return std::partial_ordering::unordered; +#else + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +#endif +} -namespace nlohmann +// GCC selects the built-in operator< over an operator rewritten from +// a user-defined spaceship operator +// Clang, MSVC, and ICC select the rewritten candidate +// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) +#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) +inline bool operator<(const value_t lhs, const value_t rhs) noexcept { + return std::is_lt(lhs <=> rhs); // *NOPAD* +} +#endif + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + + + +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -2722,12 +2951,13 @@ enforced with an assertion.** @since version 2.0.0 */ -inline void replace_substring(std::string& s, const std::string& f, - const std::string& t) +template +inline void replace_substring(StringType& s, const StringType& f, + const StringType& t) { JSON_ASSERT(!f.empty()); for (auto pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found + pos != StringType::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 {} @@ -2740,10 +2970,11 @@ inline void replace_substring(std::string& s, const std::string& f, * * Note the order of escaping "~" to "~0" and "/" to "~1" is important. */ -inline std::string escape(std::string s) +template +inline StringType escape(StringType s) { - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); + replace_substring(s, StringType{"~"}, StringType{"~0"}); + replace_substring(s, StringType{"/"}, StringType{"~1"}); return s; } @@ -2754,24 +2985,36 @@ inline std::string escape(std::string s) * * Note the order of escaping "~1" to "/" and "~0" to "~" is important. */ -static void unescape(std::string& s) +template +static void unescape(StringType& s) { - replace_substring(s, "~1", "/"); - replace_substring(s, "~0", "~"); + replace_substring(s, StringType{"~1"}, StringType{"/"}); + replace_substring(s, StringType{"~0"}, StringType{"~"}); } -} // namespace detail -} // namespace nlohmann +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + #include // size_t -namespace nlohmann -{ +// #include + + +NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { + /// struct to capture the start position of the current token struct position_t { @@ -2789,266 +3032,47 @@ struct position_t } }; -} // namespace detail -} // namespace nlohmann +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include + +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-FileCopyrightText: 2018 The Abseil Authors +// SPDX-License-Identifier: MIT + + + +#include // array +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for // #include -namespace nlohmann -{ +NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { -//////////////// -// exceptions // -//////////////// -/// @brief general exception of the @ref basic_json class -/// @sa https://json.nlohmann.me/api/basic_json/exception/ -class exception : public std::exception -{ - public: - /// returns the explanatory string - const char* what() const noexcept override - { - return m.what(); - } +template +using uncvref_t = typename std::remove_cv::type>::type; - /// the id of the exception - const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) +#ifdef JSON_HAS_CPP_14 - protected: - JSON_HEDLEY_NON_NULL(3) - exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing) +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; - static std::string name(const std::string& ename, int id_) - { - return "[json.exception." + ename + "." + std::to_string(id_) + "] "; - } - - template - static std::string diagnostics(const BasicJsonType& leaf_element) - { -#if JSON_DIAGNOSTICS - std::vector tokens; - for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) - { - switch (current->m_parent->type()) - { - case value_t::array: - { - for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) - { - if (¤t->m_parent->m_value.array->operator[](i) == current) - { - tokens.emplace_back(std::to_string(i)); - break; - } - } - break; - } - - case value_t::object: - { - for (const auto& element : *current->m_parent->m_value.object) - { - if (&element.second == current) - { - tokens.emplace_back(element.first.c_str()); - break; - } - } - break; - } - - case value_t::null: // LCOV_EXCL_LINE - case value_t::string: // LCOV_EXCL_LINE - case value_t::boolean: // LCOV_EXCL_LINE - case value_t::number_integer: // LCOV_EXCL_LINE - case value_t::number_unsigned: // LCOV_EXCL_LINE - case value_t::number_float: // LCOV_EXCL_LINE - case value_t::binary: // LCOV_EXCL_LINE - case value_t::discarded: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - break; // LCOV_EXCL_LINE - } - } - - if (tokens.empty()) - { - return ""; - } - - return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, - [](const std::string & a, const std::string & b) - { - return a + "/" + detail::escape(b); - }) + ") "; -#else - static_cast(leaf_element); - return ""; -#endif - } - - private: - /// an exception object as storage for error messages - std::runtime_error m; -}; - -/// @brief exception indicating a parse error -/// @sa https://json.nlohmann.me/api/basic_json/parse_error/ -class parse_error : public exception -{ - public: - /*! - @brief create a parse error exception - @param[in] id_ the id of the exception - @param[in] pos the position where the error occurred (or with - chars_read_total=0 if the position cannot be - determined) - @param[in] what_arg the explanatory string - @return parse_error object - */ - template - static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("parse_error", id_) + "parse error" + - position_string(pos) + ": " + exception::diagnostics(context) + what_arg; - return {id_, pos.chars_read_total, w.c_str()}; - } - - template - static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("parse_error", id_) + "parse error" + - (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + - ": " + exception::diagnostics(context) + what_arg; - return {id_, byte_, w.c_str()}; - } - - /*! - @brief byte index of the parse error - - The byte index of the last read character in the input file. - - @note For an input with n bytes, 1 is the index of the first character and - n+1 is the index of the terminating null byte or the end of file. - This also holds true when reading a byte vector (CBOR or MessagePack). - */ - const std::size_t byte; - - private: - parse_error(int id_, std::size_t byte_, const char* what_arg) - : exception(id_, what_arg), byte(byte_) {} - - static std::string position_string(const position_t& pos) - { - return " at line " + std::to_string(pos.lines_read + 1) + - ", column " + std::to_string(pos.chars_read_current_line); - } -}; - -/// @brief exception indicating errors with iterators -/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/ -class invalid_iterator : public exception -{ - public: - template - static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; - return {id_, w.c_str()}; - } - - private: - JSON_HEDLEY_NON_NULL(3) - invalid_iterator(int id_, const char* what_arg) - : exception(id_, what_arg) {} -}; - -/// @brief exception indicating executing a member function with a wrong type -/// @sa https://json.nlohmann.me/api/basic_json/type_error/ -class type_error : public exception -{ - public: - template - static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; - return {id_, w.c_str()}; - } - - private: - JSON_HEDLEY_NON_NULL(3) - type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; - -/// @brief exception indicating access out of the defined range -/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/ -class out_of_range : public exception -{ - public: - template - static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; - return {id_, w.c_str()}; - } - - private: - JSON_HEDLEY_NON_NULL(3) - out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; - -/// @brief exception indicating other library errors -/// @sa https://json.nlohmann.me/api/basic_json/other_error/ -class other_error : public exception -{ - public: - template - static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) - { - std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; - return {id_, w.c_str()}; - } - - private: - JSON_HEDLEY_NON_NULL(3) - other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; - -} // namespace detail -} // namespace nlohmann - -// #include - -// #include - - -#include // size_t -#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type -#include // index_sequence, make_index_sequence, index_sequence_for - -// #include - - -namespace nlohmann -{ -namespace detail -{ - -template -using uncvref_t = typename std::remove_cv::type>::type; - -#ifdef JSON_HAS_CPP_14 - -// the following utilities are natively available in C++14 -using std::enable_if_t; -using std::index_sequence; -using std::make_index_sequence; -using std::index_sequence_for; - -#else +#else // alias templates to reduce boilerplate template @@ -3170,28 +3194,32 @@ template<> struct priority_tag<0> {}; template struct static_const { - static constexpr T value{}; + static JSON_INLINE_VARIABLE constexpr T value{}; }; -template -constexpr T static_const::value; // NOLINT(readability-redundant-declaration) - -} // namespace detail -} // namespace nlohmann - -// #include - +#ifndef JSON_HAS_CPP_17 + template + constexpr T static_const::value; +#endif -namespace nlohmann -{ -namespace detail +template +inline constexpr std::array make_array(Args&& ... args) { -// dispatching helper struct -template struct identity_tag {}; + return std::array {{static_cast(std::forward(args))...}}; +} + } // namespace detail -} // namespace nlohmann +NLOHMANN_JSON_NAMESPACE_END // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + #include // numeric_limits @@ -3199,23 +3227,30 @@ template struct identity_tag {}; #include // declval #include // tuple -// #include - - // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + #include // random_access_iterator_tag +// #include + // #include // #include -namespace nlohmann -{ +NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { + template struct iterator_types {}; @@ -3254,104 +3289,135 @@ struct iterator_traits::value>> using pointer = T*; using reference = T&; }; -} // namespace detail -} // namespace nlohmann + +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END + +// #include // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + // #include -namespace nlohmann -{ +NLOHMANN_JSON_NAMESPACE_BEGIN + NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); -} // namespace nlohmann + +NLOHMANN_JSON_NAMESPACE_END // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + // #include -namespace nlohmann -{ +NLOHMANN_JSON_NAMESPACE_BEGIN + NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); -} // namespace nlohmann + +NLOHMANN_JSON_NAMESPACE_END // #include // #include // #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.2 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ -#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ -#include // int64_t, uint64_t -#include // map -#include // allocator -#include // string -#include // vector + #include // int64_t, uint64_t + #include // map + #include // allocator + #include // string + #include // vector -/*! -@brief namespace for Niels Lohmann -@see https://github.com/nlohmann -@since version 1.0.0 -*/ -namespace nlohmann -{ -/*! -@brief default JSONSerializer template argument + // #include -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; - -/// a class to store JSON values -/// @sa https://json.nlohmann.me/api/basic_json/ -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; -/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document -/// @sa https://json.nlohmann.me/api/json_pointer/ -template -class json_pointer; + /*! + @brief namespace for Niels Lohmann + @see https://github.com/nlohmann + @since version 1.0.0 + */ + NLOHMANN_JSON_NAMESPACE_BEGIN -/*! -@brief default specialization -@sa https://json.nlohmann.me/api/json/ -*/ -using json = basic_json<>; + /*! + @brief default JSONSerializer template argument -/// @brief a minimal map-like container that preserves insertion order -/// @sa https://json.nlohmann.me/api/ordered_map/ -template -struct ordered_map; + 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; + + /// a class to store JSON values + /// @sa https://json.nlohmann.me/api/basic_json/ + 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; + + /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document + /// @sa https://json.nlohmann.me/api/json_pointer/ + template + class json_pointer; -/// @brief specialization that maintains the insertion order of object keys -/// @sa https://json.nlohmann.me/api/ordered_json/ -using ordered_json = basic_json; + /*! + @brief default specialization + @sa https://json.nlohmann.me/api/json/ + */ + using json = basic_json<>; + + /// @brief a minimal map-like container that preserves insertion order + /// @sa https://json.nlohmann.me/api/ordered_map/ + template + struct ordered_map; -} // namespace nlohmann + /// @brief specialization that maintains the insertion order of object keys + /// @sa https://json.nlohmann.me/api/ordered_json/ + using ordered_json = basic_json; + + NLOHMANN_JSON_NAMESPACE_END #endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ -namespace nlohmann -{ +NLOHMANN_JSON_NAMESPACE_BEGIN /*! @brief detail namespace with internal helper functions @@ -3362,6 +3428,7 @@ implementations of some @ref basic_json methods, and meta-programming helpers. */ namespace detail { + ///////////// // helpers // ///////////// @@ -3380,6 +3447,16 @@ template struct is_basic_json : std::false_type {}; NLOHMANN_BASIC_JSON_TPL_DECLARATION struct is_basic_json : std::true_type {}; +// used by exceptions create() member functions +// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t +// false_type otherwise +template +struct is_basic_json_context : + std::integral_constant < bool, + is_basic_json::type>::type>::value + || std::is_same::value > +{}; + ////////////////////// // json_ref helpers // ////////////////////// @@ -3481,6 +3558,24 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> T>::value; }; +template +using detect_key_compare = typename T::key_compare; + +template +struct has_key_compare : std::integral_constant::value> {}; + +// obtains the actual object key comparator +template +struct actual_object_comparator +{ + using object_t = typename BasicJsonType::object_t; + using object_comparator_t = typename BasicJsonType::default_object_comparator_t; + using type = typename std::conditional < has_key_compare::value, + typename object_t::key_compare, object_comparator_t>::type; +}; + +template +using actual_object_comparator_t = typename actual_object_comparator::type; /////////////////// // is_ functions // @@ -3488,10 +3583,10 @@ struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> // 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 conjunction : B { }; +template +struct conjunction +: std::conditional(B::value), conjunction, B>::type {}; // https://en.cppreference.com/w/cpp/types/negation template struct negation : std::integral_constant < bool, !B::value > { }; @@ -3655,9 +3750,18 @@ struct is_compatible_string_type template struct is_constructible_string_type { + // launder type through decltype() to fix compilation failure on ICPC +#ifdef __INTEL_COMPILER + using laundered_type = decltype(std::declval()); +#else + using laundered_type = ConstructibleStringType; +#endif + static constexpr auto value = - is_constructible::value; + conjunction < + is_constructible, + is_detected_exact>::value; }; template @@ -3775,6 +3879,81 @@ struct is_constructible_tuple : std::false_type {}; template struct is_constructible_tuple> : conjunction...> {}; +template +struct is_json_iterator_of : std::false_type {}; + +template +struct is_json_iterator_of : std::true_type {}; + +template +struct is_json_iterator_of : std::true_type +{}; + +// checks if a given type T is a template specialization of Primary +template