From fff229a95dc8e964c9fe82ad80bb051ef00f75aa Mon Sep 17 00:00:00 2001 From: simonlo-sony Date: Tue, 30 Mar 2021 17:02:42 +0100 Subject: [PATCH 001/385] Bump default "registry_version" setting - only applies to working without DNS-SD when the "registry_address" is hard-coded (cherry picked from commit 7e6185d8d602dc061a5c0f151413c3fe57170424) --- Development/nmos/settings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos/settings.h b/Development/nmos/settings.h index 75d741d80..acbddea7d 100644 --- a/Development/nmos/settings.h +++ b/Development/nmos/settings.h @@ -103,7 +103,7 @@ namespace nmos const web::json::field_as_string registry_address{ U("registry_address") }; // registry_version [node]: used to construct request URLs for registry APIs (if not discovered via DNS-SD) - const web::json::field_as_string_or registry_version{ U("registry_version"), U("v1.2") }; + const web::json::field_as_string_or registry_version{ U("registry_version"), U("v1.3") }; // port numbers [registry, node]: ports to which clients should connect for each API From 9577343d53fd31d5e05e5e183f818fa5904f8492 Mon Sep 17 00:00:00 2001 From: garethsb Date: Thu, 1 Apr 2021 14:38:20 +0100 Subject: [PATCH 002/385] Allow websocket_listener to be reopened Lots more unit tests required at some point, but that was true already! (cherry picked from commit 392bb8a78a63c3ee6e9062a7a4d7dd3cf1bb8241) --- Development/cmake/NmosCppTest.cmake | 1 + Development/cpprest/test/ws_listener_test.cpp | 29 +++++++++++++++++++ Development/cpprest/ws_listener_impl.cpp | 14 ++++++++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 Development/cpprest/test/ws_listener_test.cpp diff --git a/Development/cmake/NmosCppTest.cmake b/Development/cmake/NmosCppTest.cmake index 51ba68055..f0844c27b 100644 --- a/Development/cmake/NmosCppTest.cmake +++ b/Development/cmake/NmosCppTest.cmake @@ -25,6 +25,7 @@ set(NMOS_CPP_TEST_CPPREST_TEST_SOURCES ${NMOS_CPP_DIR}/cpprest/test/json_utils_test.cpp ${NMOS_CPP_DIR}/cpprest/test/json_visit_test.cpp ${NMOS_CPP_DIR}/cpprest/test/regex_utils_test.cpp + ${NMOS_CPP_DIR}/cpprest/test/ws_listener_test.cpp ) set(NMOS_CPP_TEST_CPPREST_TEST_HEADERS ) diff --git a/Development/cpprest/test/ws_listener_test.cpp b/Development/cpprest/test/ws_listener_test.cpp new file mode 100644 index 000000000..6d91f8047 --- /dev/null +++ b/Development/cpprest/test/ws_listener_test.cpp @@ -0,0 +1,29 @@ +// The first "test" is of course whether the header compiles standalone +#include "cpprest/ws_listener.h" + +#include "bst/test/test.h" + +BST_TEST_CASE(testWebSocketListenerCloseOpen) +{ + for (auto port = 49152; port <= 65535; ++port) + { + web::websockets::experimental::listener::websocket_listener ws(web::uri_builder(U("ws://localhost")).set_port(port).to_uri()); + try + { + ws.open().wait(); + } + catch (const web::websockets::websocket_exception&) + { + // could well be that port is already in use, so just try the next one + continue; + } + ws.close().wait(); + // it now ought to be possible to reopen a closed websocket_listener + // i.e. this open task ought not to result in an exception! + ws.open().wait(); + ws.close().wait(); + return; + } + // hmm, ran out of dynamic ports?! + BST_REQUIRE(false); +} diff --git a/Development/cpprest/ws_listener_impl.cpp b/Development/cpprest/ws_listener_impl.cpp index 32f20565c..264deeac9 100644 --- a/Development/cpprest/ws_listener_impl.cpp +++ b/Development/cpprest/ws_listener_impl.cpp @@ -308,7 +308,19 @@ namespace web try { - server.init_asio(); + // either initialise or restart the io_service + if (0 == &server.get_io_service()) + { + server.init_asio(); + } + else + { +#if BOOST_VERSION >= 106600 + server.get_io_service().restart(); +#else + server.get_io_service().reset(); +#endif + } server.start_perpetual(); // hmm, is one thread enough? thread = std::thread(&server_t::run, &server); From 34936cc98c4418de47a32d30da6d5eee8c275994 Mon Sep 17 00:00:00 2001 From: garethsb Date: Tue, 6 Apr 2021 13:54:00 +0100 Subject: [PATCH 003/385] Preparatory work for supporting ST 2110-22 and JPEG XS --- Development/nmos/media_type.h | 6 +++ Development/sdp/json.h | 85 +++++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/Development/nmos/media_type.h b/Development/nmos/media_type.h index bcaa5442d..68f14de67 100644 --- a/Development/nmos/media_type.h +++ b/Development/nmos/media_type.h @@ -16,8 +16,14 @@ namespace nmos { // Video media types + // Uncompressed Video + // See https://tools.ietf.org/html/rfc4175#section-6 const media_type video_raw{ U("video/raw") }; + // JPEG XS + // See https://tools.ietf.org/html/draft-ietf-payload-rtp-jpegxs-09#section-6 + const media_type video_jxsv{ U("video/jxsv") }; + // Audio media types inline media_type audio_L(unsigned int bit_depth) diff --git a/Development/sdp/json.h b/Development/sdp/json.h index be20ff9cc..9cf2aeece 100644 --- a/Development/sdp/json.h +++ b/Development/sdp/json.h @@ -400,19 +400,37 @@ namespace sdp // See https://tools.ietf.org/html/rfc4175 // and SMPTE ST 2110-20:2017 Section 7 Session Description Protocol (SDP) Considerations // and VSF TR-05:2018 + + // ST 2110-20:2017 Section 7.2 Required Media Type Parameters + const web::json::field_as_string sampling{ U("sampling") }; const web::json::field width{ U("width") }; const web::json::field height{ U("height") }; - const web::json::field_as_string exactframerate{ U("exactframerate") }; - const web::json::field_as_bool_or interlace{ U("interlace"), false }; - const web::json::field_as_bool_or segmented{ U("segmented"), false }; - const web::json::field_as_bool_or top_field_first{ U("top-field-first"), false }; - const web::json::field_as_string sampling{ U("sampling") }; const web::json::field depth{ U("depth") }; - const web::json::field_as_string transfer_characteristic_system{ U("TCS") }; // "if unspecified, receivers shall assume the value SDR" + const web::json::field_as_string exactframerate{ U("exactframerate") }; const web::json::field_as_string colorimetry{ U("colorimetry") }; const web::json::field_as_string packing_mode{ U("PM") }; const web::json::field_as_string smpte_standard_number{ U("SSN") }; + // ST 2110-20:2017 Section 7.3 Media Type Parameters with default values + const web::json::field_as_bool_or interlace{ U("interlace"), false }; + const web::json::field_as_bool_or segmented{ U("segmented"), false }; + // RFC 4175 defines top-field-first, but it's not included in ST 2110-20 + const web::json::field_as_bool_or top_field_first{ U("top-field-first"), false }; + // hm, for the following optional parameters, it seems important to distinguish + // an omitted parameter from an explicitly specified default value... + // "If the TCS value is not specified, receivers shall assume the value SDR" per ST 2110-20:2017 Section 7.6 + const web::json::field_as_string transfer_characteristic_system{ U("TCS") }; + // "In the absence of [the RANGE] parameter, NARROW shall be the assumed value" per ST 2110-20:2017 Section 7.3 + // Hmm, the JPEG XS payload mapping says that "when paired with the UNSPECIFIED colo[ri]metry, FULL SHALL be the default assumed value" + // See https://tools.ietf.org/html/draft-ietf-payload-rtp-jpegxs-09#section-6 + const web::json::field_as_string range{ U("RANGE") }; + // "If [MAXUDP is] absent, it indicates that the Standard UDP Size Limit [i.e. 1460 octets] is in use." + const web::json::field max_udp_packet_size{ U("MAXUDP") }; + // "When it is signaled, PAR shall be signaled as a ratio of two integer decimal numbers separated by a colon character (e.g. 12:11). + // The first integer in the PAR is the width of a luminance sample, and the second integer is the height. The smallest integer values + // possible for width and height shall be used. If PAR is not signaled, the receiver shall assume that PAR = 1:1." + const web::json::field_as_string pixel_aspect_ratio{ U("PAR") }; + // See SMPTE ST 2110-21:2017 Section 8 Session Description Considerations const web::json::field_as_string type_parameter{ U("TP") }; @@ -424,6 +442,13 @@ namespace sdp // and https://tools.ietf.org/html/rfc8331 const web::json::field_as_string DID_SDID{ U("DID_SDID") }; // e.g. "{0x41,0x01}" const web::json::field VPID_Code{ U("VPID_Code") }; // 1..255 + + // See the JPEG XS payload mapping + const web::json::field transmission_mode{ U("transmode") }; // the T bit (0 for out-of-order-allowed or 1 for sequential) + const web::json::field packet_mode{ U("packetmode") }; // the K bit (0 for codestream or 1 for slice) + const web::json::field_as_string profile{ U("profile") }; // e.g. 'Main-444.12' or 'High-444.12' + const web::json::field_as_string level{ U("level") }; // e.g. '2k-1' or '4k-1' + const web::json::field_as_string sublevel{ U("sublevel") }; // e.g. 'Sublev3bpp' or 'Sublev6pp' } } @@ -431,7 +456,7 @@ namespace sdp { // Colour (sub-)sampling mode of the video stream, e.g. "YCbCr-4:2:2" // See https://tools.ietf.org/html/rfc4175 - // and SMPTE ST 2110-20:2107 Section 7.4.1 Samping + // and SMPTE ST 2110-20:2017 Section 7.4.1 Sampling // and VSF TR-05:2018, etc. DEFINE_STRING_ENUM(sampling) namespace samplings @@ -443,12 +468,21 @@ namespace sdp const sampling YCbCr_4_2_2{ U("YCbCr-4:2:2") }; const sampling YCbCr_4_2_0{ U("YCbCr-4:2:0") }; const sampling YCbCr_4_1_1{ U("YCbCr-4:1:1") }; + // Constant luminance YCbCr + const sampling CLYCbCr_4_4_4{ U("CLYCbCr-4:4:4") }; + const sampling CLYCbCr_4_2_2{ U("CLYCbCr-4:2:2") }; + const sampling CLYCbCr_4_2_0{ U("CLYCbCr-4:2:0") }; // Constant intensity ICtCp const sampling ICtCp_4_4_4{ U("ICtCp-4:4:4") }; const sampling ICtCp_4_2_2{ U("ICtCp-4:2:2") }; const sampling ICtCp_4_2_0{ U("ICtCp-4:2:0") }; // XYZ const sampling XYZ{ U("XYZ") }; + // Key signal represented as a single component + const sampling KEY{ U("KEY") }; + // Hmm, only the JPEG XS payload mapping includes this value, for "sampling signaled by the payload" + // See https://tools.ietf.org/html/draft-ietf-payload-rtp-jpegxs-09#section-6 + const sampling UNSPECIFIED{ U("UNSPECIFIED") }; } // Colorimetry @@ -468,6 +502,15 @@ namespace sdp const colorimetry BT2020{ U("BT2020") }; // ITU-R BT.2100 Table 2 const colorimetry BT2100{ U("BT2100") }; + // SMPTE ST 2065-1 Academy Color Encoding Specification (ACES) + const colorimetry ST2065_1{ U("ST2065-1") }; + // SMPTE ST 2065-3 Academy Density Exchange Encoding (ADX) + const colorimetry ST2065_3{ U("ST2065-3") }; + // Colorimetry that is not specified and must be manually coordinated between sender and receiver + // Hmm, the JPEG XS payload mapping adds that it may be "signaled in the payload by the Color Specification Box of ISO/IEC 21122-3" + const colorimetry UNSPECIFIED{ U("UNSPECIFIED") }; + // ISO 11664-1 CIE 1931 standard colorimetric system + const colorimetry XYZ{ U("XYZ") }; } // Packing Mode @@ -503,7 +546,7 @@ namespace sdp } // TCS (Transfer Characteristic System) - // See SMPTE ST 2110-21:2017 Section 7.6 Permitted values of TCS + // See SMPTE ST 2110-20:2017 Section 7.6 Permitted values of TCS // and AMWA IS-04 v1.2 "transfer_characteristic" DEFINE_STRING_ENUM(transfer_characteristic_system) namespace transfer_characteristic_systems @@ -514,6 +557,32 @@ namespace sdp const transfer_characteristic_system PQ{ U("PQ") }; // Hybrid Log Gamma const transfer_characteristic_system HLG{ U("HLG") }; + // Video streams of linear encoded floating-point samples (depth=16f), such that all values fall within the range [0..1.0]. + const transfer_characteristic_system LINEAR{ U("LINEAR") }; + // Video Stream of linear encoded floating-point samples (depth=16f) normalized from PQ as specified in ITU-R BT.2100-0 + const transfer_characteristic_system BT2100LINPQ{ U("BT2100LINPQ") }; + // Video Stream of linear encoded floating-point samples (depth=16f) normalized from HLG as specified in ITU-R BT.2100-0 + const transfer_characteristic_system BT2100LINHLG{ U("BT2100LINHLG") }; + // Video stream of linear encoded floating-point samples (depth=16f) as specified in SMPTE ST 2065-1 + const transfer_characteristic_system ST2065_1{ U("ST2065-1") }; + // Video stream utilizing the transfer characteristic specified in SMPTE ST 428-1 Section 4.3. + const transfer_characteristic_system ST428_1{ U("ST428-1") }; + // Video streams of density encoded samples, such as those defined in SMPTE ST 2065-3. + const transfer_characteristic_system DENSITY{ U("DENSITY") }; + // Video streams whose transfer characteristics are not specified. The transfer characteristics must be manually coordinated between sender and receiver. + // Hmm, the JPEG XS payload mapping includes "video streams whose transfer characteristics are signaled by the payload as specified in ISO/IEC 21122-3". + // See https://tools.ietf.org/html/draft-ietf-payload-rtp-jpegxs-09#section-6 + const transfer_characteristic_system UNSPECIFIED{ U("UNSPECIFIED") }; + } + + // RANGE + // See SMPTE ST 2110-20:2017 7.3 Media Type Parameters with default values + DEFINE_STRING_ENUM(range) + namespace ranges + { + const range NARROW{ U("NARROW") }; + const range FULLPROTECT{ U("FULLPROTECT") }; + const range FULL{ U("FULL") }; } } From 0f69ff7410cc0272b5ca7502d44962aec19f80b5 Mon Sep 17 00:00:00 2001 From: garethsb Date: Thu, 8 Apr 2021 14:54:41 +0100 Subject: [PATCH 004/385] Fix some links to AMWA specs/repos (now main rather than master branch, and the rendered specs are now at specs.amwa.tv not amwa-tv.github.io) (cherry picked from commit 2e2663cb79fea4e7875a18479033cdb18f4034a4) --- Development/nmos/activation_utils.cpp | 4 ++-- Development/nmos/connection_api.cpp | 2 +- Development/nmos/group_hint.h | 2 +- Development/nmos/node_resources.cpp | 2 +- Development/nmos/ssl_context_options.h | 2 +- README.md | 18 +++++++++--------- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Development/nmos/activation_utils.cpp b/Development/nmos/activation_utils.cpp index 37fa3fe6e..cc59a634e 100644 --- a/Development/nmos/activation_utils.cpp +++ b/Development/nmos/activation_utils.cpp @@ -93,8 +93,8 @@ namespace nmos activation[nmos::fields::mode] = value::null(); // Each of these fields "returns to null [...] when the resource is unlocked by setting the activation mode to null." - // See https://github.com/amwa-tv/nmos-device-connection-management/blob/v1.0/APIs/schemas/v1.0-activation-response-schema.json - // and https://github.com/amwa-tv/nmos-device-connection-management/blob/v1.1/APIs/schemas/activation-response-schema.json + // See https://github.com/AMWA-TV/nmos-device-connection-management/blob/v1.0/APIs/schemas/v1.0-activation-response-schema.json + // and https://github.com/AMWA-TV/nmos-device-connection-management/blob/v1.1/APIs/schemas/activation-response-schema.json activation[nmos::fields::requested_time] = value::null(); activation[nmos::fields::activation_time] = value::null(); diff --git a/Development/nmos/connection_api.cpp b/Development/nmos/connection_api.cpp index 164586002..48ac3f5db 100644 --- a/Development/nmos/connection_api.cpp +++ b/Development/nmos/connection_api.cpp @@ -775,7 +775,7 @@ namespace nmos // "The 'receiver_id' key MUST be set to `null` in all cases except where a unicast push-based Sender is configured to transmit to an NMOS Receiver, and the 'active' key is set to 'true'." // "The 'sender_id' key MUST be set to `null` in all cases except where the Receiver is currently configured to receive from an NMOS Sender, and the 'active' key is set to 'true'. - // See https://github.com/amwa-tv/nmos-discovery-registration/blob/v1.2.2/docs/4.3.%20Behaviour%20-%20Nodes.md#api-resources + // See https://github.com/AMWA-TV/nmos-discovery-registration/blob/v1.2.2/docs/4.3.%20Behaviour%20-%20Nodes.md#api-resources const auto ci = active && !connected_id.empty() ? value::string(connected_id) : value::null(); // "When the 'active' parameters of a Sender or Receiver are modified, or when a re-activation of the same parameters diff --git a/Development/nmos/group_hint.h b/Development/nmos/group_hint.h index 67b4d724a..33da9326e 100644 --- a/Development/nmos/group_hint.h +++ b/Development/nmos/group_hint.h @@ -5,7 +5,7 @@ #include "nmos/string_enum.h" // Group Hint -// See https://github.com/AMWA-TV/nmos-parameter-registers/blob/master/tags/grouphint.md +// See https://github.com/AMWA-TV/nmos-parameter-registers/blob/main/tags/grouphint.md namespace nmos { namespace fields diff --git a/Development/nmos/node_resources.cpp b/Development/nmos/node_resources.cpp index 7765f6425..2a68763e9 100644 --- a/Development/nmos/node_resources.cpp +++ b/Development/nmos/node_resources.cpp @@ -107,7 +107,7 @@ namespace nmos if (0 <= nmos::experimental::fields::manifest_port(settings)) { - // See https://github.com/AMWA-TV/nmos-parameter-registers/blob/master/device-control-types/manifest-base.md + // See https://github.com/AMWA-TV/nmos-parameter-registers/blob/main/device-control-types/manifest-base.md // and nmos::experimental::make_manifest_api_manifest auto manifest_uri = web::uri_builder() .set_scheme(nmos::http_scheme(settings)) diff --git a/Development/nmos/ssl_context_options.h b/Development/nmos/ssl_context_options.h index fcd80f71a..7e2642610 100644 --- a/Development/nmos/ssl_context_options.h +++ b/Development/nmos/ssl_context_options.h @@ -12,7 +12,7 @@ namespace nmos // "Implementations SHALL NOT use TLS 1.0 or 1.1. These are deprecated." // "Implementations SHALL NOT use SSL. Although the SSL protocol has previously, // been used to secure HTTP traffic no version of SSL is now considered secure." - // See https://github.com/AMWA-TV/nmos-api-security/blob/master/best-practice-secure-comms.md#tls + // See https://github.com/AMWA-TV/nmos-secure-communication/blob/v1.0.x/docs/1.0.%20Secure%20Communication.md#tls const auto ssl_context_options = ( boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3 diff --git a/README.md b/README.md index b43c64fd9..f4a7aaee2 100644 --- a/README.md +++ b/README.md @@ -3,17 +3,17 @@ ## Introduction -This repository contains an implementation of the [AMWA Networked Media Open Specifications](https://amwa-tv.github.io/nmos) in C++, [licensed](LICENSE) under the terms of the Apache License 2.0. +This repository contains an implementation of the [AMWA Networked Media Open Specifications](https://specs.amwa.tv/nmos/) in C++, [licensed](LICENSE) under the terms of the Apache License 2.0. -- [AMWA IS-04 NMOS Discovery and Registration Specification](https://amwa-tv.github.io/nmos-discovery-registration) -- [AMWA IS-05 NMOS Device Connection Management Specification](https://amwa-tv.github.io/nmos-device-connection-management) -- [AMWA IS-07 NMOS Event & Tally Specification](https://amwa-tv.github.io/nmos-event-tally) -- [AMWA IS-08 NMOS Audio Channel Mapping Specification](https://amwa-tv.github.io/nmos-audio-channel-mapping) -- [AMWA IS-09 NMOS System Parameters Specification](https://amwa-tv.github.io/nmos-system) (originally defined in JT-NM TR-1001-1:2018 Annex A) -- [AMWA BCP-002-01 NMOS Grouping Recommendations - Natural Grouping](https://amwa-tv.github.io/nmos-grouping) -- [AMWA BCP-003-01 Secure Communication in NMOS Systems](https://amwa-tv.github.io/nmos-secure-communication) +- [AMWA IS-04 NMOS Discovery and Registration Specification](https://specs.amwa.tv/is-04/) +- [AMWA IS-05 NMOS Device Connection Management Specification](https://specs.amwa.tv/is-05/) +- [AMWA IS-07 NMOS Event & Tally Specification](https://specs.amwa.tv/is-07/) +- [AMWA IS-08 NMOS Audio Channel Mapping Specification](https://specs.amwa.tv/is-08/) +- [AMWA IS-09 NMOS System Parameters Specification](https://specs.amwa.tv/is-09/) (originally defined in JT-NM TR-1001-1:2018 Annex A) +- [AMWA BCP-002-01 NMOS Grouping Recommendations - Natural Grouping](https://specs.amwa.tv/bcp-002-01/) +- [AMWA BCP-003-01 Secure Communication in NMOS Systems](https://specs.amwa.tv/bcp-003-01/) -For more information about AMWA, NMOS and the Networked Media Incubator, please refer to http://amwa.tv/. +For more information about AMWA, NMOS and the Networked Media Incubator, please refer to . - The [nmos module](Development/nmos) includes implementations of the NMOS Node, Registration and Query APIs, the NMOS Connection API, and so on. - The [nmos-cpp-registry application](Development/nmos-cpp-registry) provides a simple but functional instance of an NMOS Registration & Discovery System (RDS), utilising the nmos module. From cdc16d48e4bf5e6e6f808e90cbf75384d6efe6b5 Mon Sep 17 00:00:00 2001 From: garethsb Date: Thu, 8 Apr 2021 15:28:45 +0100 Subject: [PATCH 005/385] Fix some links to AMWA specs/repos (capabilities branch is now merged to main in nmos-parameter-registers) (cherry picked from commit a4bc88848d32717bb17f2495e9f7e58837d383ea) --- Development/nmos/capabilities.h | 2 +- Development/nmos/sdp_utils.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Development/nmos/capabilities.h b/Development/nmos/capabilities.h index e57cabafa..9b089f627 100644 --- a/Development/nmos/capabilities.h +++ b/Development/nmos/capabilities.h @@ -41,7 +41,7 @@ namespace nmos bool match_rational_constraint(const nmos::rational& value, const web::json::value& constraint); // NMOS Parameter Registers - Capabilities register - // See https://github.com/AMWA-TV/nmos-parameter-registers/blob/capabilities/capabilities/README.md + // See https://github.com/AMWA-TV/nmos-parameter-registers/blob/main/capabilities/README.md namespace caps { namespace meta diff --git a/Development/nmos/sdp_utils.cpp b/Development/nmos/sdp_utils.cpp index 7625e1a68..d70faa958 100644 --- a/Development/nmos/sdp_utils.cpp +++ b/Development/nmos/sdp_utils.cpp @@ -1187,7 +1187,7 @@ namespace nmos if (!nmos::caps::meta::enabled(constraint_set)) return false; // NMOS Parameter Registers - Capabilities register - // See https://github.com/AMWA-TV/nmos-parameter-registers/blob/capabilities/capabilities/README.md + // See https://github.com/AMWA-TV/nmos-parameter-registers/blob/main/capabilities/README.md static const std::map> match_constraints { // General Constraints From 0b88d7e33295322385b00583e83fafe3bf41e9ba Mon Sep 17 00:00:00 2001 From: garethsb Date: Sun, 11 Apr 2021 06:44:21 +0100 Subject: [PATCH 006/385] BCP-004-01 has been elevated --- Development/nmos/capabilities.h | 12 ++++++------ README.md | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Development/nmos/capabilities.h b/Development/nmos/capabilities.h index 9b089f627..a32da5000 100644 --- a/Development/nmos/capabilities.h +++ b/Development/nmos/capabilities.h @@ -7,7 +7,7 @@ namespace nmos { // BCP-004-01 Receiver Capabilities - // See https://github.com/AMWA-TV/nmos-receiver-capabilities/blob/v1.0-dev/docs/1.0.%20Receiver%20Capabilities.md + // See https://github.com/AMWA-TV/nmos-receiver-capabilities/blob/v1.0.0/docs/1.0.%20Receiver%20Capabilities.md namespace fields { const web::json::field_as_value_or constraint_sets{ U("constraint_sets"), {} }; @@ -19,19 +19,19 @@ namespace nmos template <> nmos::rational inline no_minimum() { return (std::numeric_limits::max)(); } template <> nmos::rational inline no_maximum() { return 0; } - // See https://github.com/AMWA-TV/nmos-receiver-capabilities/blob/v1.0-dev/docs/1.0.%20Receiver%20Capabilities.md#string-constraint-keywords + // See https://github.com/AMWA-TV/nmos-receiver-capabilities/blob/v1.0.0/docs/1.0.%20Receiver%20Capabilities.md#string-constraint-keywords web::json::value make_caps_string_constraint(const std::vector& enum_values = {}); - // See https://github.com/AMWA-TV/nmos-receiver-capabilities/blob/v1.0-dev/docs/1.0.%20Receiver%20Capabilities.md#integer-and-number-constraint-keywords + // See https://github.com/AMWA-TV/nmos-receiver-capabilities/blob/v1.0.0/docs/1.0.%20Receiver%20Capabilities.md#integer-and-number-constraint-keywords web::json::value make_caps_integer_constraint(const std::vector& enum_values = {}, int64_t minimum = no_minimum(), int64_t maximum = no_maximum()); - // See https://github.com/AMWA-TV/nmos-receiver-capabilities/blob/v1.0-dev/docs/1.0.%20Receiver%20Capabilities.md#integer-and-number-constraint-keywords + // See https://github.com/AMWA-TV/nmos-receiver-capabilities/blob/v1.0.0/docs/1.0.%20Receiver%20Capabilities.md#integer-and-number-constraint-keywords web::json::value make_caps_number_constraint(const std::vector& enum_values = {}, double minimum = no_minimum(), double maximum = no_maximum()); - // See https://github.com/AMWA-TV/nmos-receiver-capabilities/blob/v1.0-dev/docs/1.0.%20Receiver%20Capabilities.md#boolean-constraint-keywords + // See https://github.com/AMWA-TV/nmos-receiver-capabilities/blob/v1.0.0/docs/1.0.%20Receiver%20Capabilities.md#boolean-constraint-keywords web::json::value make_caps_boolean_constraint(const std::vector& enum_values = {}); - // See https://github.com/AMWA-TV/nmos-receiver-capabilities/blob/v1.0-dev/docs/1.0.%20Receiver%20Capabilities.md#rational-constraint-keywords + // See https://github.com/AMWA-TV/nmos-receiver-capabilities/blob/v1.0.0/docs/1.0.%20Receiver%20Capabilities.md#rational-constraint-keywords web::json::value make_caps_rational_constraint(const std::vector& enum_values = {}, const nmos::rational& minimum = no_minimum(), const nmos::rational& maximum = no_maximum()); bool match_string_constraint(const utility::string_t& value, const web::json::value& constraint); diff --git a/README.md b/README.md index f4a7aaee2..43444ee37 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ This repository contains an implementation of the [AMWA Networked Media Open Spe - [AMWA IS-09 NMOS System Parameters Specification](https://specs.amwa.tv/is-09/) (originally defined in JT-NM TR-1001-1:2018 Annex A) - [AMWA BCP-002-01 NMOS Grouping Recommendations - Natural Grouping](https://specs.amwa.tv/bcp-002-01/) - [AMWA BCP-003-01 Secure Communication in NMOS Systems](https://specs.amwa.tv/bcp-003-01/) +- [AMWA BCP-004-01 NMOS Receiver Capabilities](https://specs.amwa.tv/bcp-004-01/) For more information about AMWA, NMOS and the Networked Media Incubator, please refer to . @@ -105,6 +106,7 @@ The implementation is designed to be extended. Development is ongoing, following Recent activity on the project (newest first): +- 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 - Added support for the IS-08 Channel Mapping API - JT-NM Tested 03/20 badge From 333de7a657d9b3f637a9a2880a48f887097009e0 Mon Sep 17 00:00:00 2001 From: simonlo-sony Date: Thu, 22 Apr 2021 23:03:11 +0100 Subject: [PATCH 007/385] Fix macOS "Reference cannot be bound to dereferenced null pointer" while executing &server.get_io_service(), to prevent SEGFAULT while running testWebSocketListenerCloseOpen --- Development/cpprest/ws_listener_impl.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Development/cpprest/ws_listener_impl.cpp b/Development/cpprest/ws_listener_impl.cpp index 264deeac9..712611f3b 100644 --- a/Development/cpprest/ws_listener_impl.cpp +++ b/Development/cpprest/ws_listener_impl.cpp @@ -289,6 +289,7 @@ namespace web public: explicit websocket_listener_wspp(web::uri address, websocket_listener_config config) : websocket_listener_impl(std::move(address), std::move(config)) + , init(false) { // since we cannot set the callback function before the server constructor we can't get log message from that server.get_alog().set_log_handler(configuration().get_log_callback()); @@ -309,9 +310,10 @@ namespace web try { // either initialise or restart the io_service - if (0 == &server.get_io_service()) + if (!init) { server.init_asio(); + init = true; } else { @@ -635,6 +637,7 @@ namespace web server_t server; connections_t connections; std::mutex mutex; + bool init; // flag to identify whether to initialise or restart the io_service }; std::unique_ptr make_websocket_listener_impl(web::uri&& address, websocket_listener_config&& config) From 071157b9ceb5a340fd9a86a426776e5262057d99 Mon Sep 17 00:00:00 2001 From: garethsb Date: Fri, 7 May 2021 15:20:47 +0100 Subject: [PATCH 008/385] Merge pull request #183 from lo-simon/tls-refactor Enhance certificate management to load from memory instead of loading from file Co-authored-by: Gareth Sylvester-Bradley <31761158+garethsb@users.noreply.github.com> Co-authored-by: Simon Lo --- Development/CMakeLists.txt | 2 + Development/boost/asio/ssl/use_tmp_ecdh.hpp | 31 ++++- Development/cmake/NmosCppLibraries.cmake | 3 + Development/nmos-cpp-node/config.json | 11 +- .../nmos-cpp-node/node_implementation.cpp | 8 +- Development/nmos-cpp-registry/config.json | 11 +- Development/nmos-cpp-registry/main.cpp | 7 +- .../registry_implementation.cpp | 25 ++++ .../registry_implementation.h | 23 ++++ Development/nmos/certificate_handlers.cpp | 128 ++++++++++++++++++ Development/nmos/certificate_handlers.h | 78 +++++++++++ Development/nmos/certificate_settings.h | 56 ++++++++ Development/nmos/client_utils.cpp | 24 ++-- Development/nmos/client_utils.h | 5 +- .../nmos/connection_events_activation.cpp | 4 +- .../nmos/connection_events_activation.h | 8 +- Development/nmos/node_api_target_handler.cpp | 8 +- Development/nmos/node_api_target_handler.h | 21 ++- Development/nmos/node_behaviour.cpp | 46 +++---- Development/nmos/node_behaviour.h | 7 +- Development/nmos/node_server.cpp | 16 ++- Development/nmos/node_server.h | 16 ++- Development/nmos/node_system_behaviour.cpp | 24 ++-- Development/nmos/node_system_behaviour.h | 5 +- Development/nmos/registry_server.cpp | 12 +- Development/nmos/registry_server.h | 37 ++++- Development/nmos/server_utils.cpp | 73 ++++++---- Development/nmos/server_utils.h | 7 +- Development/nmos/settings.h | 21 +-- Sandbox/run_nmos_testing.sh | 23 ++-- 30 files changed, 587 insertions(+), 153 deletions(-) create mode 100644 Development/nmos-cpp-registry/registry_implementation.cpp create mode 100644 Development/nmos-cpp-registry/registry_implementation.h create mode 100644 Development/nmos/certificate_handlers.cpp create mode 100644 Development/nmos/certificate_handlers.h create mode 100644 Development/nmos/certificate_settings.h diff --git a/Development/CMakeLists.txt b/Development/CMakeLists.txt index f5b3032b2..a975cefdd 100644 --- a/Development/CMakeLists.txt +++ b/Development/CMakeLists.txt @@ -60,8 +60,10 @@ endif() set(NMOS_CPP_REGISTRY_SOURCES ${NMOS_CPP_DIR}/nmos-cpp-registry/main.cpp + ${NMOS_CPP_DIR}/nmos-cpp-registry/registry_implementation.cpp ) set(NMOS_CPP_REGISTRY_HEADERS + ${NMOS_CPP_DIR}/nmos-cpp-registry/registry_implementation.h ) add_executable( diff --git a/Development/boost/asio/ssl/use_tmp_ecdh.hpp b/Development/boost/asio/ssl/use_tmp_ecdh.hpp index 922a955c5..a9e2e8a09 100644 --- a/Development/boost/asio/ssl/use_tmp_ecdh.hpp +++ b/Development/boost/asio/ssl/use_tmp_ecdh.hpp @@ -47,7 +47,7 @@ struct ec_key_cleanup }; inline -BOOST_ASIO_SYNC_OP_VOID do_use_tmp_ecdh_file(boost::asio::ssl::context& ctx, +BOOST_ASIO_SYNC_OP_VOID do_use_tmp_ecdh(boost::asio::ssl::context& ctx, BIO* bio, boost::system::error_code& ec) { ::ERR_clear_error(); @@ -94,7 +94,7 @@ BOOST_ASIO_SYNC_OP_VOID use_tmp_ecdh_file(boost::asio::ssl::context& ctx, bio_cleanup bio = { ::BIO_new_file(certificate.c_str(), "r") }; if (bio.p) { - return do_use_tmp_ecdh_file(ctx, bio.p, ec); + return do_use_tmp_ecdh(ctx, bio.p, ec); } ec = boost::system::error_code( @@ -111,9 +111,36 @@ void use_tmp_ecdh_file(boost::asio::ssl::context& ctx, const std::string& certif boost::asio::detail::throw_error(ec, "use_tmp_ecdh_file"); } +inline +BOOST_ASIO_SYNC_OP_VOID use_tmp_ecdh(boost::asio::ssl::context& ctx, + const boost::asio::const_buffer& certificate, boost::system::error_code& ec) +{ + ::ERR_clear_error(); + + bio_cleanup bio = { ::BIO_new_mem_buf(const_cast(boost::asio::buffer_cast(certificate)), static_cast(boost::asio::buffer_size(certificate))) }; + if (bio.p) + { + return do_use_tmp_ecdh(ctx, bio.p, ec); + } + + ec = boost::system::error_code( + static_cast(::ERR_get_error()), + boost::asio::error::get_ssl_category()); + BOOST_ASIO_SYNC_OP_VOID_RETURN(ec); +} + +inline +void use_tmp_ecdh(boost::asio::ssl::context& ctx, const boost::asio::const_buffer& certificate) +{ + boost::system::error_code ec; + use_tmp_ecdh(ctx, certificate, ec); + boost::asio::detail::throw_error(ec, "use_tmp_ecdh"); +} + } // namespace use_tmp_ecdh_details using use_tmp_ecdh_details::use_tmp_ecdh_file; +using use_tmp_ecdh_details::use_tmp_ecdh; } // namespace ssl } // namespace asio diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index 6ee1c8175..571af9b11 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -703,6 +703,7 @@ set(NMOS_CPP_NMOS_SOURCES ${NMOS_CPP_DIR}/nmos/api_downgrade.cpp ${NMOS_CPP_DIR}/nmos/api_utils.cpp ${NMOS_CPP_DIR}/nmos/capabilities.cpp + ${NMOS_CPP_DIR}/nmos/certificate_handlers.cpp ${NMOS_CPP_DIR}/nmos/channelmapping_activation.cpp ${NMOS_CPP_DIR}/nmos/channelmapping_api.cpp ${NMOS_CPP_DIR}/nmos/channelmapping_resources.cpp @@ -764,6 +765,8 @@ set(NMOS_CPP_NMOS_HEADERS ${NMOS_CPP_DIR}/nmos/api_utils.h ${NMOS_CPP_DIR}/nmos/api_version.h ${NMOS_CPP_DIR}/nmos/capabilities.h + ${NMOS_CPP_DIR}/nmos/certificate_handlers.h + ${NMOS_CPP_DIR}/nmos/certificate_settings.h ${NMOS_CPP_DIR}/nmos/channelmapping_activation.h ${NMOS_CPP_DIR}/nmos/channelmapping_api.h ${NMOS_CPP_DIR}/nmos/channelmapping_resources.h diff --git a/Development/nmos-cpp-node/config.json b/Development/nmos-cpp-node/config.json index e504ab645..5e03868c3 100644 --- a/Development/nmos-cpp-node/config.json +++ b/Development/nmos-cpp-node/config.json @@ -187,14 +187,15 @@ // when true, server certificates etc. must also be configured //"server_secure": false, - // private_key_files [registry, node]: full paths of private key files in PEM format - //"private_key_files": ["server-ecdsa-key.pem", "server-rsa-key.pem"], - - // certificate_chain_files [registry, node]: full paths of server certificate chain files which must be in PEM format and must be sorted + // server_certificates [registry, node]: an array of server certificate objects, each has the name of the key algorithm, the full paths of private key file and certificate chain file + // each value must be an object like { "key_algorithm": "ECDSA", "private_key_file": "server-ecdsa-key.pem", "certificate_chain_file": "server-ecdsa-chain.pem" } + // key_algorithm (attribute of server_certificates objects): name of the key algorithm for the certificate, see nmos::key_algorithm + // private_key_file (attribute of server_certificates objects): full path of private key file in PEM format + // certificate_chain_file (attribute of server_certificates object): full path of certificate chain file in PEM format, which must be sorted // starting with the server's certificate, followed by any intermediate CA certificates, and ending with the highest level (root) CA // on Windows, if C++ REST SDK is built with CPPREST_HTTP_LISTENER_IMPL=httpsys (reported as "listener=httpsys" by nmos::get_build_settings_info) // one of the certificates must also be bound to each port e.g. using 'netsh add sslcert' - //"certificate_chain_files": ["server-ecdsa-chain.pem", "server-rsa-chain.pem"], + //"server_certificates": [{"key_algorithm": "ECDSA", "private_key_file": "server-ecdsa-key.pem", "certificate_chain_file": "server-ecdsa-chain.pem"}, {"key_algorithm": "RSA", "private_key_file": "server-rsa-key.pem", "certificate_chain_file": "server-rsa-chain.pem"}], // validate_certificates [registry, node]: boolean value, false (ignore all server certificate validation errors), or true (do not ignore, the default behaviour) //"validate_certificates": true, diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 9ea87cf09..ce4cff0d3 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -243,7 +243,7 @@ void node_implementation_thread(nmos::node_model& model, slog::base_gate& gate_) { return impl::channels_repeat[index % (int)impl::channels_repeat.size()]; })); - + source = nmos::make_audio_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, channels, model.settings); } else if (impl::ports::data == port) @@ -959,12 +959,13 @@ nmos::events_ws_message_handler make_node_implementation_events_ws_message_handl // Example Connection API activation callback to perform application-specific operations to complete activation nmos::connection_activation_handler make_node_implementation_connection_activation_handler(nmos::node_model& model, slog::base_gate& gate) { + auto handle_load_ca_certificates = nmos::make_load_ca_certificates_handler(model.settings, gate); // this example uses this callback to (un)subscribe a IS-07 Events WebSocket receiver when it is activated // and, in addition to the message handler, specifies the optional close handler in order that any subsequent // connection errors are reflected into the /active endpoint by setting master_enable to false auto handle_events_ws_message = make_node_implementation_events_ws_message_handler(model, gate); auto handle_close = nmos::experimental::make_events_ws_close_handler(model, gate); - auto connection_events_activation_handler = nmos::make_connection_events_websocket_activation_handler(handle_events_ws_message, handle_close, model.settings, gate); + auto connection_events_activation_handler = nmos::make_connection_events_websocket_activation_handler(handle_load_ca_certificates, handle_events_ws_message, handle_close, model.settings, gate); return [connection_events_activation_handler, &gate](const nmos::resource& resource, const nmos::resource& connection_resource) { @@ -1043,6 +1044,9 @@ namespace impl nmos::experimental::node_implementation make_node_implementation(nmos::node_model& model, slog::base_gate& gate) { return nmos::experimental::node_implementation() + .on_load_server_certificates(nmos::make_load_server_certificates_handler(model.settings, gate)) + .on_load_dh_param(nmos::make_load_dh_param_handler(model.settings, gate)) + .on_load_ca_certificates(nmos::make_load_ca_certificates_handler(model.settings, gate)) .on_system_changed(make_node_implementation_system_global_handler(model, gate)) // may be omitted if not required .on_registration_changed(make_node_implementation_registration_handler(gate)) // may be omitted if not required .on_parse_transport_file(make_node_implementation_transport_file_parser()) // may be omitted if the default is sufficient diff --git a/Development/nmos-cpp-registry/config.json b/Development/nmos-cpp-registry/config.json index 154f7cbb5..cccb758c6 100644 --- a/Development/nmos-cpp-registry/config.json +++ b/Development/nmos-cpp-registry/config.json @@ -145,14 +145,15 @@ // when true, server certificates etc. must also be configured //"server_secure": false, - // private_key_files [registry, node]: full paths of private key files in PEM format - //"private_key_files": ["server-ecdsa-key.pem", "server-rsa-key.pem"], - - // certificate_chain_files [registry, node]: full paths of server certificate chain files which must be in PEM format and must be sorted + // server_certificates [registry, node]: an array of server certificate objects, each has the name of the key algorithm, the full paths of private key file and certificate chain file + // each value must be an object like { "key_algorithm": "ECDSA", "private_key_file": "server-ecdsa-key.pem", "certificate_chain_file": "server-ecdsa-chain.pem" } + // key_algorithm (attribute of server_certificates objects): name of the key algorithm for the certificate, see nmos::key_algorithm + // private_key_file (attribute of server_certificates objects): full path of private key file in PEM format + // certificate_chain_file (attribute of server_certificates object): full path of certificate chain file in PEM format, which must be sorted // starting with the server's certificate, followed by any intermediate CA certificates, and ending with the highest level (root) CA // on Windows, if C++ REST SDK is built with CPPREST_HTTP_LISTENER_IMPL=httpsys (reported as "listener=httpsys" by nmos::get_build_settings_info) // one of the certificates must also be bound to each port e.g. using 'netsh add sslcert' - //"certificate_chain_files": ["server-ecdsa-chain.pem", "server-rsa-chain.pem"], + //"server_certificates": [{"key_algorithm": "ECDSA", "private_key_file": "server-ecdsa-key.pem", "certificate_chain_file": "server-ecdsa-chain.pem"}, {"key_algorithm": "RSA", "private_key_file": "server-rsa-key.pem", "certificate_chain_file": "server-rsa-chain.pem"}], // validate_certificates [registry, node]: boolean value, false (ignore all server certificate validation errors), or true (do not ignore, the default behaviour) //"validate_certificates": true, diff --git a/Development/nmos-cpp-registry/main.cpp b/Development/nmos-cpp-registry/main.cpp index 052ed235c..dcd93daa5 100644 --- a/Development/nmos-cpp-registry/main.cpp +++ b/Development/nmos-cpp-registry/main.cpp @@ -5,6 +5,7 @@ #include "nmos/process_utils.h" #include "nmos/registry_server.h" #include "nmos/server.h" +#include "registry_implementation.h" int main(int argc, char* argv[]) { @@ -89,9 +90,13 @@ int main(int argc, char* argv[]) slog::log(gate, SLOG_FLF) << "Build settings: " << nmos::get_build_settings_info(); slog::log(gate, SLOG_FLF) << "Initial settings: " << registry_model.settings.serialize(); + // Set up the callbacks between the registry server and the underlying implementation + + auto registry_implementation = make_registry_implementation(registry_model, gate); + // Set up the registry server - auto registry_server = nmos::experimental::make_registry_server(registry_model, log_model, gate); + auto registry_server = nmos::experimental::make_registry_server(registry_model, registry_implementation, log_model, gate); if (!nmos::experimental::fields::http_trace(registry_model.settings)) { diff --git a/Development/nmos-cpp-registry/registry_implementation.cpp b/Development/nmos-cpp-registry/registry_implementation.cpp new file mode 100644 index 000000000..91e0f89de --- /dev/null +++ b/Development/nmos-cpp-registry/registry_implementation.cpp @@ -0,0 +1,25 @@ +#include "registry_implementation.h" + +#include "nmos/model.h" +#include "nmos/registry_server.h" +#include "nmos/slog.h" + +// example registry implementation details +namespace impl +{ + // custom logging category for the example registry implementation thread + namespace categories + { + const nmos::category registry_implementation{ "registry_implementation" }; + } +} + +// This constructs all the callbacks used to integrate the example device-specific underlying implementation +// into the server instance for the NMOS Registry. +nmos::experimental::registry_implementation make_registry_implementation(nmos::registry_model& model, slog::base_gate& gate) +{ + return nmos::experimental::registry_implementation() + .on_load_server_certificates(nmos::make_load_server_certificates_handler(model.settings, gate)) + .on_load_dh_param(nmos::make_load_dh_param_handler(model.settings, gate)) + .on_load_ca_certificates(nmos::make_load_ca_certificates_handler(model.settings, gate)); +} diff --git a/Development/nmos-cpp-registry/registry_implementation.h b/Development/nmos-cpp-registry/registry_implementation.h new file mode 100644 index 000000000..d4aaf1c0b --- /dev/null +++ b/Development/nmos-cpp-registry/registry_implementation.h @@ -0,0 +1,23 @@ +#ifndef NMOS_CPP_REGISTRY_REGISTRY_IMPLEMENTATION_H +#define NMOS_CPP_REGISTRY_REGISTRY_IMPLEMENTATION_H + +namespace slog +{ + class base_gate; +} + +namespace nmos +{ + struct registry_model; + + namespace experimental + { + struct registry_implementation; + } +} + +// This constructs all the callbacks used to integrate the example device-specific underlying implementation +// into the server instance for the NMOS Registry. +nmos::experimental::registry_implementation make_registry_implementation(nmos::registry_model& model, slog::base_gate& gate); + +#endif diff --git a/Development/nmos/certificate_handlers.cpp b/Development/nmos/certificate_handlers.cpp new file mode 100644 index 000000000..6a0a04245 --- /dev/null +++ b/Development/nmos/certificate_handlers.cpp @@ -0,0 +1,128 @@ +#include "nmos/certificate_handlers.h" + +#include "cpprest/basic_utils.h" +#include "nmos/certificate_settings.h" +#include "nmos/slog.h" + +namespace nmos +{ + // construct callback to load certification authorities from file based on settings, see nmos/certificate_settings.h + load_ca_certificates_handler make_load_ca_certificates_handler(const nmos::settings& settings, slog::base_gate& gate) + { + const auto ca_certificate_file = nmos::experimental::fields::ca_certificate_file(settings); + + return [&, ca_certificate_file]() + { + utility::string_t data; + + slog::log(gate, SLOG_FLF) << "Load certification authorities"; + + if (ca_certificate_file.empty()) + { + slog::log(gate, SLOG_FLF) << "Missing certification authorities file"; + } + else + { + std::ifstream ca_file(ca_certificate_file); + std::stringstream cacerts; + cacerts << ca_file.rdbuf(); + data = utility::s2us(cacerts.str()); + } + return data; + }; + } + + // construct callback to load server certificates from files based on settings, see nmos/certificate_settings.h + load_server_certificates_handler make_load_server_certificates_handler(const nmos::settings& settings, slog::base_gate& gate) + { + // load the server private keys and certificate chains from files + auto server_certificates = nmos::experimental::fields::server_certificates(settings); + if (0 == server_certificates.size()) + { + // (deprecated, replaced by server_certificates) + const auto private_key_files = nmos::experimental::fields::private_key_files(settings); + const auto certificate_chain_files = nmos::experimental::fields::certificate_chain_files(settings); + + const auto size = std::min(private_key_files.size(), certificate_chain_files.size()); + for (size_t i = 0; i < size; ++i) + { + web::json::push_back(server_certificates, + web::json::value_of({ + { nmos::experimental::fields::private_key_file, private_key_files.at(i) }, + { nmos::experimental::fields::certificate_chain_file, certificate_chain_files.at(i) } + }) + ); + } + } + + return [&, server_certificates]() + { + slog::log(gate, SLOG_FLF) << "Load server private keys and certificate chains"; + + auto data = std::vector(); + + if (0 == server_certificates.size()) + { + slog::log(gate, SLOG_FLF) << "Missing server certificates"; + } + + for (const auto& server_certificate : server_certificates.as_array()) + { + const auto key_algorithm = nmos::experimental::fields::key_algorithm(server_certificate); + const auto private_key_file = nmos::experimental::fields::private_key_file(server_certificate); + const auto certificate_chain_file = nmos::experimental::fields::certificate_chain_file(server_certificate); + + std::stringstream pkey; + if (private_key_file.empty()) + { + slog::log(gate, SLOG_FLF) << "Missing private key file"; + } + else + { + std::ifstream pkey_file(private_key_file); + pkey << pkey_file.rdbuf(); + } + + std::stringstream cert_chain; + if (certificate_chain_file.empty()) + { + slog::log(gate, SLOG_FLF) << "Missing certificate chain file"; + } + else + { + std::ifstream cert_chain_file(certificate_chain_file); + cert_chain << cert_chain_file.rdbuf(); + } + + data.push_back(nmos::server_certificate(nmos::key_algorithm{ key_algorithm }, utility::s2us(pkey.str()), utility::s2us(cert_chain.str()))); + } + return data; + }; + } + + // construct callback to load Diffie-Hellman parameters for ephemeral key exchange support from file based on settings, see nmos/certificate_settings.h + load_dh_param_handler make_load_dh_param_handler(const nmos::settings& settings, slog::base_gate& gate) + { + const auto dh_param_file = nmos::experimental::fields::dh_param_file(settings); + + return[&, dh_param_file]() + { + slog::log(gate, SLOG_FLF) << "Load DH parameters"; + + utility::string_t data; + + if (dh_param_file.empty()) + { + slog::log(gate, SLOG_FLF) << "Missing DH parameters file"; + } + else + { + std::ifstream dh_file(dh_param_file); + std::stringstream dh_param; + dh_param << dh_file.rdbuf(); + data = utility::s2us(dh_param.str()); + } + return data; + }; + } +} diff --git a/Development/nmos/certificate_handlers.h b/Development/nmos/certificate_handlers.h new file mode 100644 index 000000000..3b79a8525 --- /dev/null +++ b/Development/nmos/certificate_handlers.h @@ -0,0 +1,78 @@ +#ifndef NMOS_CERTIFICATE_HANDLERS_H +#define NMOS_CERTIFICATE_HANDLERS_H + +#include +#include +#include "cpprest/details/basic_types.h" +#include "nmos/settings.h" +#include "nmos/string_enum.h" + +namespace slog +{ + class base_gate; +} + +namespace nmos +{ + // callback to supply trusted root CA certificate(s) in PEM format + // this callback is executed when opening the HTTP or WebSocket client + // this callback should not throw exceptions + // on Windows, if C++ REST SDK is built with CPPREST_HTTP_CLIENT_IMPL=winhttp (reported as "client=winhttp" by nmos::get_build_settings_info) + // the trusted root CA certificates must also be imported into the certificate store + typedef std::function load_ca_certificates_handler; + + // common key algorithms + DEFINE_STRING_ENUM(key_algorithm) + namespace key_algorithms + { + const key_algorithm ECDSA{ U("ECDSA") }; + const key_algorithm RSA{ U("RSA") }; + } + + // server certificate details including the private key and the certificate chain in PEM format + // the key algorithm may also be specified + struct server_certificate + { + server_certificate() {} + + server_certificate(utility::string_t private_key, utility::string_t certificate_chain) + : private_key(std::move(private_key)) + , certificate_chain(std::move(certificate_chain)) + {} + + server_certificate(nmos::key_algorithm key_algorithm, utility::string_t private_key, utility::string_t certificate_chain) + : key_algorithm(std::move(key_algorithm)) + , private_key(std::move(private_key)) + , certificate_chain(std::move(certificate_chain)) + {} + + nmos::key_algorithm key_algorithm; + utility::string_t private_key; + // the chain should be sorted starting with the server's certificate, followed by any intermediate CA certificates, and ending with the highest level (root) CA + utility::string_t certificate_chain; + }; + + // callback to supply a list of server certificates + // this callback is executed when opening the HTTP or WebSocket listener + // this callback should not throw exceptions + // on Windows, if C++ REST SDK is built with CPPREST_HTTP_LISTENER_IMPL=httpsys (reported as "listener=httpsys" by nmos::get_build_settings_info) + // one of the certificates must also be bound to each port e.g. using 'netsh add sslcert' + typedef std::function()> load_server_certificates_handler; + + // callback to supply Diffie-Hellman parameters for ephemeral key exchange support, in PEM format or empty string for no support + // see e.g. https://wiki.openssl.org/index.php/Diffie-Hellman_parameters + // this callback is executed when opening the HTTP or WebSocket listener + // this callback should not throw exceptions + typedef std::function load_dh_param_handler; + + // construct callback to load certification authorities from file based on settings, see nmos/certificate_settings.h + load_ca_certificates_handler make_load_ca_certificates_handler(const nmos::settings& settings, slog::base_gate& gate); + + // construct callback to load server certificates from files based on settings, see nmos/certificate_settings.h + load_server_certificates_handler make_load_server_certificates_handler(const nmos::settings& settings, slog::base_gate& gate); + + // construct callback to load Diffie-Hellman parameters for ephemeral key exchange support from file based on settings, see nmos/certificate_settings.h + load_dh_param_handler make_load_dh_param_handler(const nmos::settings& settings, slog::base_gate& gate); +} + +#endif diff --git a/Development/nmos/certificate_settings.h b/Development/nmos/certificate_settings.h new file mode 100644 index 000000000..a01fc90f0 --- /dev/null +++ b/Development/nmos/certificate_settings.h @@ -0,0 +1,56 @@ +#ifndef NMOS_CERTIFICATE_SETTINGS_H +#define NMOS_CERTIFICATE_SETTINGS_H + +#include "cpprest/json_utils.h" + +namespace slog +{ + class base_gate; +} + +namespace nmos +{ + namespace experimental + { + namespace fields + { + // ca_certificate_file: full path of certification authorities file in PEM format + // on Windows, if C++ REST SDK is built with CPPREST_HTTP_CLIENT_IMPL=winhttp (reported as "client=winhttp" by nmos::get_build_settings_info) + // the trusted root CA certificates must also be imported into the certificate store + const web::json::field_as_string_or ca_certificate_file{ U("ca_certificate_file"), U("") }; + + // server_certificates [registry, node]: an array of server certificate objects, each has the name of the key algorithm, the full paths of private key file and certificate chain file + // each value must be an object like { "key_algorithm": "ECDSA", "private_key_file": "server-key.pem, "certificate_chain_file": "server-chain.pem" } + // see key_algorithm, private_key_file and certificate_chain_file below + const web::json::field_as_value_or server_certificates{ U("server_certificates"), web::json::value::array() }; + + // key_algorithm (attribute of server_certificates objects): name of the key algorithm for the certificate, see nmos::key_algorithm + const web::json::field_as_string_or key_algorithm{ U("key_algorithm"), U("") }; + + // private_key_file (attribute of server_certificates objects): full path of private key file in PEM format + const web::json::field_as_string_or private_key_file{ U("private_key_file"), U("") }; + + // certificate_chain_file (attribute of server_certificates objects): full path of certificate chain file in PEM format, which must be sorted + // starting with the server's certificate, followed by any intermediate CA certificates, and ending with the highest level (root) CA + // on Windows, if C++ REST SDK is built with CPPREST_HTTP_LISTENER_IMPL=httpsys (reported as "listener=httpsys" by nmos::get_build_settings_info) + // one of the certificates must also be bound to each port e.g. using 'netsh add sslcert' + const web::json::field_as_string_or certificate_chain_file{ U("certificate_chain_file"), U("") }; + + // dh_param_file [registry, node]: Diffie-Hellman parameters file in PEM format for ephemeral key exchange support, or empty string for no support + const web::json::field_as_string_or dh_param_file{ U("dh_param_file"), U("") }; + + // (deprecated, replaced by server_certificates) + // private_key_files [registry, node]: full paths of private key files in PEM format + const web::json::field_as_value_or private_key_files{ U("private_key_files"), web::json::value::array() }; + + // (deprecated, replaced by server_certificates) + // certificate_chain_files [registry, node]: full paths of server certificate chain files which must be in PEM format and must be sorted + // starting with the server's certificate, followed by any intermediate CA certificates, and ending with the highest level (root) CA + // on Windows, if C++ REST SDK is built with CPPREST_HTTP_LISTENER_IMPL=httpsys (reported as "listener=httpsys" by nmos::get_build_settings_info) + // one of the certificates must also be bound to each port e.g. using 'netsh add sslcert' + const web::json::field_as_value_or certificate_chain_files{ U("certificate_chain_files"), web::json::value::array() }; + } + } +} + +#endif diff --git a/Development/nmos/client_utils.cpp b/Development/nmos/client_utils.cpp index 5d0b9adf8..efc2adabd 100644 --- a/Development/nmos/client_utils.cpp +++ b/Development/nmos/client_utils.cpp @@ -7,6 +7,7 @@ #include "cpprest/details/system_error.h" #include "cpprest/http_utils.h" #include "cpprest/ws_client.h" +#include "nmos/certificate_settings.h" #include "nmos/slog.h" #include "nmos/ssl_context_options.h" @@ -28,15 +29,22 @@ namespace nmos { #if !defined(_WIN32) || !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) template - inline std::function make_client_ssl_context_callback(const nmos::settings& settings) + inline std::function make_client_ssl_context_callback(const nmos::settings& settings, load_ca_certificates_handler load_ca_certificates, slog::base_gate& gate) { - const auto ca_certificate_file = utility::us2s(nmos::experimental::fields::ca_certificate_file(settings)); - return [ca_certificate_file](boost::asio::ssl::context& ctx) + if (!load_ca_certificates) + { + load_ca_certificates = make_load_ca_certificates_handler(settings, gate); + } + + return [load_ca_certificates](boost::asio::ssl::context& ctx) { try { ctx.set_options(nmos::details::ssl_context_options); - ctx.load_verify_file(ca_certificate_file); + + const auto cacerts = utility::us2s(load_ca_certificates()); + ctx.add_certificate_authority(boost::asio::buffer(cacerts.data(), cacerts.size())); + set_cipher_list(ctx, nmos::details::ssl_cipher_list); } catch (const boost::system::system_error& e) @@ -50,14 +58,14 @@ namespace nmos // construct client config based on settings, e.g. using the specified proxy // with the remaining options defaulted, e.g. request timeout - web::http::client::http_client_config make_http_client_config(const nmos::settings& settings) + web::http::client::http_client_config make_http_client_config(const nmos::settings& settings, load_ca_certificates_handler load_ca_certificates, slog::base_gate& gate) { web::http::client::http_client_config config; const auto proxy = proxy_uri(settings); if (!proxy.is_empty()) config.set_proxy(proxy); config.set_validate_certificates(nmos::experimental::fields::validate_certificates(settings)); #if !defined(_WIN32) && !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) - config.set_ssl_context_callback(details::make_client_ssl_context_callback(settings)); + config.set_ssl_context_callback(details::make_client_ssl_context_callback(settings, load_ca_certificates, gate)); #endif return config; @@ -65,14 +73,14 @@ namespace nmos // construct client config based on settings, e.g. using the specified proxy // with the remaining options defaulted - web::websockets::client::websocket_client_config make_websocket_client_config(const nmos::settings& settings) + web::websockets::client::websocket_client_config make_websocket_client_config(const nmos::settings& settings, load_ca_certificates_handler load_ca_certificates, slog::base_gate& gate) { web::websockets::client::websocket_client_config config; const auto proxy = proxy_uri(settings); if (!proxy.is_empty()) config.set_proxy(proxy); config.set_validate_certificates(nmos::experimental::fields::validate_certificates(settings)); #if !defined(_WIN32) || !defined(__cplusplus_winrt) - config.set_ssl_context_callback(details::make_client_ssl_context_callback(settings)); + config.set_ssl_context_callback(details::make_client_ssl_context_callback(settings, load_ca_certificates, gate)); #endif return config; diff --git a/Development/nmos/client_utils.h b/Development/nmos/client_utils.h index e9007e62e..1e76e8bc8 100644 --- a/Development/nmos/client_utils.h +++ b/Development/nmos/client_utils.h @@ -2,6 +2,7 @@ #define NMOS_CLIENT_UTILS_H #include "cpprest/http_client.h" // for http_client, http_client_config, http_response, etc. +#include "nmos/certificate_handlers.h" #include "nmos/settings.h" namespace web { namespace websockets { namespace client { class websocket_client_config; } } } @@ -12,11 +13,11 @@ namespace nmos { // construct client config based on settings, e.g. using the specified proxy // with the remaining options defaulted, e.g. request timeout - web::http::client::http_client_config make_http_client_config(const nmos::settings& settings); + web::http::client::http_client_config make_http_client_config(const nmos::settings& settings, load_ca_certificates_handler load_ca_certificates, slog::base_gate& gate); // construct client config based on settings, e.g. using the specified proxy // with the remaining options defaulted - web::websockets::client::websocket_client_config make_websocket_client_config(const nmos::settings& settings); + web::websockets::client::websocket_client_config make_websocket_client_config(const nmos::settings& settings, load_ca_certificates_handler load_ca_certificates, slog::base_gate& gate); // make an API request with logging pplx::task api_request(web::http::client::http_client client, web::http::http_request request, slog::base_gate& gate, const pplx::cancellation_token& token = pplx::cancellation_token::none()); diff --git a/Development/nmos/connection_events_activation.cpp b/Development/nmos/connection_events_activation.cpp index e452c58cb..d132e6f75 100644 --- a/Development/nmos/connection_events_activation.cpp +++ b/Development/nmos/connection_events_activation.cpp @@ -11,9 +11,9 @@ namespace nmos { // this handler can be used to (un)subscribe IS-07 Events WebSocket receivers with the specified handlers, when they are activated - nmos::connection_activation_handler make_connection_events_websocket_activation_handler(nmos::events_ws_message_handler message_handler, nmos::events_ws_close_handler close_handler, const nmos::settings& settings, slog::base_gate& gate) + nmos::connection_activation_handler make_connection_events_websocket_activation_handler(load_ca_certificates_handler load_ca_certificates, events_ws_message_handler message_handler, events_ws_close_handler close_handler, const nmos::settings& settings, slog::base_gate& gate) { - std::shared_ptr events_ws_client(new nmos::events_ws_client(nmos::make_websocket_client_config(settings), nmos::fields::events_heartbeat_interval(settings), gate)); + std::shared_ptr events_ws_client(new nmos::events_ws_client(nmos::make_websocket_client_config(settings, load_ca_certificates, gate), nmos::fields::events_heartbeat_interval(settings), gate)); events_ws_client->set_message_handler(message_handler); events_ws_client->set_close_handler(close_handler); diff --git a/Development/nmos/connection_events_activation.h b/Development/nmos/connection_events_activation.h index 41b1136be..1014f2400 100644 --- a/Development/nmos/connection_events_activation.h +++ b/Development/nmos/connection_events_activation.h @@ -1,6 +1,7 @@ #ifndef NMOS_CONNECTION_EVENTS_ACTIVATION_H #define NMOS_CONNECTION_EVENTS_ACTIVATION_H +#include "nmos/certificate_handlers.h" #include "nmos/connection_activation.h" #include "nmos/events_ws_client.h" // for nmos::events_ws_message_handler, etc. #include "nmos/settings.h" // just a forward declaration of nmos::settings @@ -10,7 +11,12 @@ namespace nmos struct node_model; // this handler can be used to (un)subscribe IS-07 Events WebSocket receivers with the specified handlers, when they are activated - nmos::connection_activation_handler make_connection_events_websocket_activation_handler(nmos::events_ws_message_handler message_handler, nmos::events_ws_close_handler close_handler, const nmos::settings& settings, slog::base_gate& gate); + nmos::connection_activation_handler make_connection_events_websocket_activation_handler(nmos::load_ca_certificates_handler load_ca_certificates, nmos::events_ws_message_handler message_handler, nmos::events_ws_close_handler close_handler, const nmos::settings& settings, slog::base_gate& gate); + + inline nmos::connection_activation_handler make_connection_events_websocket_activation_handler(nmos::events_ws_message_handler message_handler, nmos::events_ws_close_handler close_handler, const nmos::settings& settings, slog::base_gate& gate) + { + return make_connection_events_websocket_activation_handler({}, std::move(message_handler), std::move(close_handler), settings, gate); + } inline nmos::connection_activation_handler make_connection_events_websocket_activation_handler(nmos::events_ws_message_handler message_handler, const nmos::settings& settings, slog::base_gate& gate) { diff --git a/Development/nmos/node_api_target_handler.cpp b/Development/nmos/node_api_target_handler.cpp index 2e0203137..03dcc25a1 100644 --- a/Development/nmos/node_api_target_handler.cpp +++ b/Development/nmos/node_api_target_handler.cpp @@ -13,9 +13,9 @@ namespace nmos { // implement the Node API /receivers/{receiverId}/target endpoint using the Connection API implementation with the specified transport file parser and the specified validator // (the /target endpoint is only required to support RTP transport, other transport types use the Connection API) - node_api_target_handler make_node_api_target_handler(nmos::node_model& model, transport_file_parser parse_transport_file, details::connection_resource_patch_validator validate_merged) + node_api_target_handler make_node_api_target_handler(nmos::node_model& model, load_ca_certificates_handler load_ca_certificates, transport_file_parser parse_transport_file, details::connection_resource_patch_validator validate_merged) { - return [&model, parse_transport_file, validate_merged](const nmos::id& receiver_id, const web::json::value& sender_data, slog::base_gate& gate) + return [&model, load_ca_certificates, parse_transport_file, validate_merged](const nmos::id& receiver_id, const web::json::value& sender_data, slog::base_gate& gate) { using web::json::value; using web::json::value_of; @@ -28,7 +28,7 @@ namespace nmos // if manifest_href is null, this will throw json_exception which will be reported appropriately as 400 Bad Request const auto manifest_href = nmos::fields::manifest_href(sender_data).as_string(); - web::http::client::http_client client(manifest_href, nmos::with_read_lock(model.mutex, [&model] { return nmos::make_http_client_config(model.settings); })); + web::http::client::http_client client(manifest_href, nmos::with_read_lock(model.mutex, [&, load_ca_certificates] { return nmos::make_http_client_config(model.settings, load_ca_certificates, gate); })); return api_request(client, web::http::methods::GET, gate).then([manifest_href, &gate](web::http::http_response res) { if (res.status_code() != web::http::status_codes::OK) @@ -97,8 +97,6 @@ namespace nmos }; } - // implement the Node API /receivers/{receiverId}/target endpoint using the Connection API implementation with the default transport file parser - // (the /target endpoint is only required to support RTP transport, other transport types use the Connection API) node_api_target_handler make_node_api_target_handler(nmos::node_model& model) { return make_node_api_target_handler(model, &nmos::parse_rtp_transport_file, {}); diff --git a/Development/nmos/node_api_target_handler.h b/Development/nmos/node_api_target_handler.h index aa1dfc451..076cb7574 100644 --- a/Development/nmos/node_api_target_handler.h +++ b/Development/nmos/node_api_target_handler.h @@ -1,6 +1,7 @@ #ifndef NMOS_NODE_API_TARGET_HANDLER_H #define NMOS_NODE_API_TARGET_HANDLER_H +#include "nmos/certificate_handlers.h" #include "nmos/connection_api.h" namespace web @@ -11,11 +12,6 @@ namespace web } } -namespace slog -{ - class base_gate; -} - namespace nmos { struct node_model; @@ -23,19 +19,20 @@ namespace nmos // handler for the Node API /receivers/{receiverId}/target endpoint typedef std::function(const nmos::id& receiver_id, const web::json::value& sender_data, slog::base_gate& gate)> node_api_target_handler; - // implement the Node API /receivers/{receiverId}/target endpoint using the Connection API implementation with the specified transport file parser and the specified validator + // implement the Node API /receivers/{receiverId}/target endpoint using the Connection API implementation with the specified handlers // (the /target endpoint is only required to support RTP transport, other transport types use the Connection API) - node_api_target_handler make_node_api_target_handler(nmos::node_model& model, transport_file_parser parse_transport_file, details::connection_resource_patch_validator validate_merged); + node_api_target_handler make_node_api_target_handler(nmos::node_model& model, load_ca_certificates_handler load_ca_certificates, transport_file_parser parse_transport_file, details::connection_resource_patch_validator validate_merged); + + inline node_api_target_handler make_node_api_target_handler(nmos::node_model& model, transport_file_parser parse_transport_file, details::connection_resource_patch_validator validate_merged) + { + return make_node_api_target_handler(model, {}, std::move(parse_transport_file), std::move(validate_merged)); + } - // implement the Node API /receivers/{receiverId}/target endpoint using the Connection API implementation with the specified transport file parser - // (the /target endpoint is only required to support RTP transport, other transport types use the Connection API) inline node_api_target_handler make_node_api_target_handler(nmos::node_model& model, transport_file_parser parse_transport_file) { - return make_node_api_target_handler(model, parse_transport_file, {}); + return make_node_api_target_handler(model, std::move(parse_transport_file), {}); } - // implement the Node API /receivers/{receiverId}/target endpoint using the Connection API implementation with the default transport file parser - // (the /target endpoint is only required to support RTP transport, other transport types use the Connection API) node_api_target_handler make_node_api_target_handler(nmos::node_model& model); } diff --git a/Development/nmos/node_behaviour.cpp b/Development/nmos/node_behaviour.cpp index 1d51f4d5f..fa5a8dcdf 100644 --- a/Development/nmos/node_behaviour.cpp +++ b/Development/nmos/node_behaviour.cpp @@ -21,11 +21,11 @@ namespace nmos { namespace details { - void node_behaviour_thread(nmos::model& model, registration_handler registration_changed, mdns::service_advertiser& advertiser, mdns::service_discovery& discovery, slog::base_gate& gate); + void node_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, registration_handler registration_changed, mdns::service_advertiser& advertiser, mdns::service_discovery& discovery, slog::base_gate& gate); // registered operation - void initial_registration(nmos::id& self_id, nmos::model& model, const nmos::id& grain_id, slog::base_gate& gate); - void registered_operation(const nmos::id& self_id, nmos::model& model, const nmos::id& grain_id, registration_handler registration_changed, slog::base_gate& gate); + void initial_registration(nmos::id& self_id, nmos::model& model, const nmos::id& grain_id, load_ca_certificates_handler load_ca_certificates, slog::base_gate& gate); + void registered_operation(const nmos::id& self_id, nmos::model& model, const nmos::id& grain_id, load_ca_certificates_handler load_ca_certificates, registration_handler registration_changed, slog::base_gate& gate); // peer to peer operation void peer_to_peer_operation(nmos::model& model, const nmos::id& grain_id, mdns::service_discovery& discovery, mdns::service_advertiser& advertiser, slog::base_gate& gate); @@ -42,7 +42,7 @@ namespace nmos // uses the default DNS-SD implementation // callbacks from this function are called with the model locked, and may read or write directly to the model - void node_behaviour_thread(nmos::model& model, registration_handler registration_changed, slog::base_gate& gate_) + void node_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, registration_handler registration_changed, slog::base_gate& gate_) { nmos::details::omanip_gate gate(gate_, nmos::stash_category(nmos::categories::node_behaviour)); @@ -51,31 +51,31 @@ namespace nmos mdns::service_discovery discovery(gate); - details::node_behaviour_thread(model, std::move(registration_changed), advertiser, discovery, gate); + details::node_behaviour_thread(model, std::move(load_ca_certificates), std::move(registration_changed), advertiser, discovery, gate); } // uses the specified DNS-SD implementation // callbacks from this function are called with the model locked, and may read or write directly to the model - void node_behaviour_thread(nmos::model& model, registration_handler registration_changed, mdns::service_advertiser& advertiser, mdns::service_discovery& discovery, slog::base_gate& gate_) + void node_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, registration_handler registration_changed, mdns::service_advertiser& advertiser, mdns::service_discovery& discovery, slog::base_gate& gate_) { nmos::details::omanip_gate gate(gate_, nmos::stash_category(nmos::categories::node_behaviour)); - details::node_behaviour_thread(model, std::move(registration_changed), advertiser, discovery, gate); + details::node_behaviour_thread(model, std::move(load_ca_certificates), std::move(registration_changed), advertiser, discovery, gate); } // uses the default DNS-SD implementation - void node_behaviour_thread(nmos::model& model, slog::base_gate& gate) + void node_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, slog::base_gate& gate) { - node_behaviour_thread(model, {}, gate); + node_behaviour_thread(model, load_ca_certificates, {}, gate); } // uses the specified DNS-SD implementation - void node_behaviour_thread(nmos::model& model, mdns::service_advertiser& advertiser, mdns::service_discovery& discovery, slog::base_gate& gate) + void node_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, mdns::service_advertiser& advertiser, mdns::service_discovery& discovery, slog::base_gate& gate) { - node_behaviour_thread(model, {}, advertiser, discovery, gate); + node_behaviour_thread(model, load_ca_certificates, {}, advertiser, discovery, gate); } - void details::node_behaviour_thread(nmos::model& model, registration_handler registration_changed, mdns::service_advertiser& advertiser, mdns::service_discovery& discovery, slog::base_gate& gate) + void details::node_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, registration_handler registration_changed, mdns::service_advertiser& advertiser, mdns::service_discovery& discovery, slog::base_gate& gate) { // The possible states of node behaviour represent the two primary modes (registered operation and peer-to-peer operation) // and a few hopefully ephemeral states as the node works through the "Standard Registration Sequences". @@ -160,7 +160,7 @@ namespace nmos case initial_registration: // "5. The Node registers itself with the Registration API by taking the object it holds under the Node API's /self resource and POSTing this to the Registration API." - details::initial_registration(self_id, model, grain_id, gate); + details::initial_registration(self_id, model, grain_id, load_ca_certificates, gate); if (details::has_discovered_registration_services(model)) { @@ -177,7 +177,7 @@ namespace nmos case registered_operation: // "6. The Node persists itself in the registry by issuing heartbeats." // "7. The Node registers its other resources (from /devices, /sources etc) with the Registration API." - details::registered_operation(self_id, model, grain_id, registration_changed, gate); + details::registered_operation(self_id, model, grain_id, load_ca_certificates, registration_changed, gate); if (details::has_discovered_registration_services(model)) { @@ -482,16 +482,16 @@ namespace nmos handle_registration_error_conditions(response, false, gate, operation); } - web::http::client::http_client_config make_registration_client_config(const nmos::settings& settings) + web::http::client::http_client_config make_registration_client_config(const nmos::settings& settings, load_ca_certificates_handler load_ca_certificates, slog::base_gate& gate) { - auto config = nmos::make_http_client_config(settings); + auto config = nmos::make_http_client_config(settings, std::move(load_ca_certificates), gate); config.set_timeout(std::chrono::seconds(nmos::fields::registration_request_max(settings))); return config; } - web::http::client::http_client_config make_heartbeat_client_config(const nmos::settings& settings) + web::http::client::http_client_config make_heartbeat_client_config(const nmos::settings& settings, load_ca_certificates_handler load_ca_certificates, slog::base_gate& gate) { - auto config = nmos::make_http_client_config(settings); + auto config = nmos::make_http_client_config(settings, std::move(load_ca_certificates), gate); config.set_timeout(std::chrono::seconds(nmos::fields::registration_heartbeat_max(settings))); return config; } @@ -669,7 +669,7 @@ namespace nmos } // there is significant similarity between initial_registration and registered_operation but I'm too tired to refactor again right now... - void initial_registration(nmos::id& self_id, nmos::model& model, const nmos::id& grain_id, slog::base_gate& gate) + void initial_registration(nmos::id& self_id, nmos::model& model, const nmos::id& grain_id, load_ca_certificates_handler load_ca_certificates, slog::base_gate& gate) { slog::log(gate, SLOG_FLF) << "Attempting initial registration"; @@ -756,7 +756,7 @@ namespace nmos grain.updated = strictly_increasing_update(resources); }); - registration_client.reset(new web::http::client::http_client(base_uri, make_registration_client_config(model.settings))); + registration_client.reset(new web::http::client::http_client(base_uri, make_registration_client_config(model.settings, load_ca_certificates, gate))); } events = web::json::value::array(); @@ -827,7 +827,7 @@ namespace nmos request.wait(); } - void registered_operation(const nmos::id& self_id, nmos::model& model, const nmos::id& grain_id, registration_handler registration_changed, slog::base_gate& gate) + void registered_operation(const nmos::id& self_id, nmos::model& model, const nmos::id& grain_id, load_ca_certificates_handler load_ca_certificates, registration_handler registration_changed, slog::base_gate& gate) { slog::log(gate, SLOG_FLF) << "Adopting registered operation"; @@ -893,8 +893,8 @@ namespace nmos const auto registry_version = parse_api_version(web::uri::split_path(base_uri.path()).back()); if (registry_version != grain->version) break; - registration_client.reset(new web::http::client::http_client(base_uri, make_registration_client_config(model.settings))); - heartbeat_client.reset(new web::http::client::http_client(base_uri, make_heartbeat_client_config(model.settings))); + registration_client.reset(new web::http::client::http_client(base_uri, make_registration_client_config(model.settings, load_ca_certificates, gate))); + heartbeat_client.reset(new web::http::client::http_client(base_uri, make_heartbeat_client_config(model.settings, load_ca_certificates, gate))); // "The first interaction with a new Registration API [after a server side or connectivity issue] // should be a heartbeat to confirm whether whether the Node is still present in the registry" diff --git a/Development/nmos/node_behaviour.h b/Development/nmos/node_behaviour.h index 855c4ccde..2ac5c7bdb 100644 --- a/Development/nmos/node_behaviour.h +++ b/Development/nmos/node_behaviour.h @@ -2,6 +2,7 @@ #define NMOS_NODE_BEHAVIOUR_H #include +#include "nmos/certificate_handlers.h" namespace web { @@ -34,17 +35,17 @@ namespace nmos // uses the default DNS-SD implementation // callbacks from this function are called with the model locked, and may read or write directly to the model - void node_behaviour_thread(nmos::model& model, registration_handler registration_changed, slog::base_gate& gate); + void node_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, registration_handler registration_changed, slog::base_gate& gate); // uses the specified DNS-SD implementation // callbacks from this function are called with the model locked, and may read or write directly to the model void node_behaviour_thread(nmos::model& model, registration_handler registration_changed, mdns::service_advertiser& advertiser, mdns::service_discovery& discovery, slog::base_gate& gate); // uses the default DNS-SD implementation - void node_behaviour_thread(nmos::model& model, slog::base_gate& gate); + void node_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, slog::base_gate& gate); // uses the specified DNS-SD implementation - void node_behaviour_thread(nmos::model& model, mdns::service_advertiser& advertiser, mdns::service_discovery& discovery, slog::base_gate& gate); + void node_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, mdns::service_advertiser& advertiser, mdns::service_discovery& discovery, slog::base_gate& gate); } #endif diff --git a/Development/nmos/node_server.cpp b/Development/nmos/node_server.cpp index d69e94308..bcda87156 100644 --- a/Development/nmos/node_server.cpp +++ b/Development/nmos/node_server.cpp @@ -45,7 +45,7 @@ namespace nmos // Configure the Node API - nmos::node_api_target_handler target_handler = nmos::make_node_api_target_handler(node_model, node_implementation.parse_transport_file, node_implementation.validate_staged); + nmos::node_api_target_handler target_handler = nmos::make_node_api_target_handler(node_model, node_implementation.load_ca_certificates, node_implementation.parse_transport_file, node_implementation.validate_staged); node_server.api_routers[{ {}, nmos::fields::node_port(node_model.settings) }].mount({}, nmos::make_node_api(node_model, target_handler, gate)); node_server.api_routers[{ {}, nmos::experimental::fields::manifest_port(node_model.settings) }].mount({}, nmos::experimental::make_manifest_api(node_model, gate)); @@ -64,7 +64,7 @@ namespace nmos // Set up the listeners for each HTTP API port - auto http_config = nmos::make_http_listener_config(node_model.settings); + auto http_config = nmos::make_http_listener_config(node_model.settings, node_implementation.load_server_certificates, node_implementation.load_dh_param, gate); for (auto& api_router : node_server.api_routers) { @@ -77,7 +77,7 @@ namespace nmos // Set up the handlers for each WebSocket API port - auto websocket_config = nmos::make_websocket_listener_config(node_model.settings); + auto websocket_config = nmos::make_websocket_listener_config(node_model.settings, node_implementation.load_server_certificates, node_implementation.load_dh_param, gate); websocket_config.set_log_callback(nmos::make_slog_logging_callback(gate)); for (auto& ws_handler : node_server.ws_handlers) @@ -93,13 +93,14 @@ namespace nmos // Set up node operation (including the DNS-SD advertisements) + auto load_ca_certificates = node_implementation.load_ca_certificates; auto registration_changed = node_implementation.registration_changed; auto resolve_auto = node_implementation.resolve_auto; auto set_transportfile = node_implementation.set_transportfile; auto connection_activated = node_implementation.connection_activated; auto channelmapping_activated = node_implementation.channelmapping_activated; node_server.thread_functions.assign({ - [&, registration_changed] { nmos::node_behaviour_thread(node_model, registration_changed, gate); }, + [&, load_ca_certificates, registration_changed] { nmos::node_behaviour_thread(node_model, load_ca_certificates, registration_changed, gate); }, [&] { nmos::send_events_ws_messages_thread(events_ws_listener, node_model, events_ws_api.second, gate); }, [&] { nmos::erase_expired_events_resources_thread(node_model, gate); }, [&, resolve_auto, set_transportfile, connection_activated] { nmos::connection_activation_thread(node_model, resolve_auto, set_transportfile, connection_activated, gate); }, @@ -109,22 +110,23 @@ namespace nmos auto system_changed = node_implementation.system_changed; if (system_changed) { - node_server.thread_functions.push_back([&, system_changed] { nmos::node_system_behaviour_thread(node_model, system_changed, gate); }); + node_server.thread_functions.push_back([&, load_ca_certificates, system_changed] { nmos::node_system_behaviour_thread(node_model, load_ca_certificates, system_changed, gate); }); } return node_server; } + // deprecated nmos::server make_node_server(nmos::node_model& node_model, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_merged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::experimental::log_model& log_model, slog::base_gate& gate) { return make_node_server(node_model, node_implementation().on_parse_transport_file(std::move(parse_transport_file)).on_validate_merged(std::move(validate_merged)).on_resolve_auto(std::move(resolve_auto)).on_set_transportfile(std::move(set_transportfile)).on_connection_activated(std::move(connection_activated)), log_model, gate); } - + // deprecated nmos::server make_node_server(nmos::node_model& node_model, nmos::transport_file_parser parse_transport_file, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::experimental::log_model& log_model, slog::base_gate& gate) { return make_node_server(node_model, node_implementation().on_parse_transport_file(std::move(parse_transport_file)).on_resolve_auto(std::move(resolve_auto)).on_set_transportfile(std::move(set_transportfile)).on_connection_activated(std::move(connection_activated)), log_model, gate); } - + // deprecated nmos::server make_node_server(nmos::node_model& node_model, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::experimental::log_model& log_model, slog::base_gate& gate) { return make_node_server(node_model, node_implementation().on_resolve_auto(std::move(resolve_auto)).on_set_transportfile(std::move(set_transportfile)).on_connection_activated(std::move(connection_activated)), log_model, gate); diff --git a/Development/nmos/node_server.h b/Development/nmos/node_server.h index c0c3a991d..9be34f7ff 100644 --- a/Development/nmos/node_server.h +++ b/Development/nmos/node_server.h @@ -1,6 +1,7 @@ #ifndef NMOS_NODE_SERVER_H #define NMOS_NODE_SERVER_H +#include "nmos/certificate_handlers.h" #include "nmos/channelmapping_api.h" #include "nmos/channelmapping_activation.h" #include "nmos/connection_api.h" @@ -22,8 +23,11 @@ namespace nmos // underlying implementation into the server instance for the NMOS Node struct node_implementation { - node_implementation(nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_staged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated) - : system_changed(std::move(system_changed)) + node_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates, nmos::system_global_handler system_changed, nmos::registration_handler registration_changed, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_merged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated) + : load_server_certificates(std::move(load_server_certificates)) + , load_dh_param(std::move(load_dh_param)) + , load_ca_certificates(std::move(load_ca_certificates)) + , system_changed(std::move(system_changed)) , registration_changed(std::move(registration_changed)) , parse_transport_file(std::move(parse_transport_file)) , validate_staged(std::move(validate_staged)) @@ -38,6 +42,9 @@ namespace nmos : parse_transport_file(&nmos::parse_rtp_transport_file) {} + node_implementation& on_load_server_certificates(nmos::load_server_certificates_handler load_server_certificates) { this->load_server_certificates = std::move(load_server_certificates); return *this; } + node_implementation& on_load_dh_param(nmos::load_dh_param_handler load_dh_param) { this->load_dh_param = std::move(load_dh_param); return *this; } + node_implementation& on_load_ca_certificates(nmos::load_ca_certificates_handler load_ca_certificates) { this->load_ca_certificates = std::move(load_ca_certificates); return *this; } node_implementation& on_system_changed(nmos::system_global_handler system_changed) { this->system_changed = std::move(system_changed); return *this; } node_implementation& on_registration_changed(nmos::registration_handler registration_changed) { this->registration_changed = std::move(registration_changed); return *this; } node_implementation& on_parse_transport_file(nmos::transport_file_parser parse_transport_file) { this->parse_transport_file = std::move(parse_transport_file); return *this; } @@ -57,6 +64,10 @@ namespace nmos return parse_transport_file && resolve_auto && set_transportfile && connection_activated; } + nmos::load_server_certificates_handler load_server_certificates; + nmos::load_dh_param_handler load_dh_param; + nmos::load_ca_certificates_handler load_ca_certificates; + nmos::system_global_handler system_changed; nmos::registration_handler registration_changed; @@ -76,6 +87,7 @@ namespace nmos // and the experimental Logging API and Settings API, according to the specified data models and callbacks nmos::server make_node_server(nmos::node_model& node_model, nmos::experimental::node_implementation node_implementation, nmos::experimental::log_model& log_model, slog::base_gate& gate); + // deprecated nmos::server make_node_server(nmos::node_model& node_model, nmos::transport_file_parser parse_transport_file, nmos::details::connection_resource_patch_validator validate_merged, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::experimental::log_model& log_model, slog::base_gate& gate); nmos::server make_node_server(nmos::node_model& node_model, nmos::transport_file_parser parse_transport_file, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::experimental::log_model& log_model, slog::base_gate& gate); nmos::server make_node_server(nmos::node_model& node_model, nmos::connection_resource_auto_resolver resolve_auto, nmos::connection_sender_transportfile_setter set_transportfile, nmos::connection_activation_handler connection_activated, nmos::experimental::log_model& log_model, slog::base_gate& gate); diff --git a/Development/nmos/node_system_behaviour.cpp b/Development/nmos/node_system_behaviour.cpp index 0dbaecb7b..71f1308dd 100644 --- a/Development/nmos/node_system_behaviour.cpp +++ b/Development/nmos/node_system_behaviour.cpp @@ -18,9 +18,9 @@ namespace nmos { namespace details { - void node_system_behaviour_thread(nmos::model& model, system_global_handler system_changed, mdns::service_discovery& discovery, slog::base_gate& gate); + void node_system_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, system_global_handler system_changed, mdns::service_discovery& discovery, slog::base_gate& gate); - void node_system_behaviour(nmos::model& model, system_global_handler system_changed, slog::base_gate& gate); + void node_system_behaviour(nmos::model& model, load_ca_certificates_handler load_ca_certificates, system_global_handler system_changed, slog::base_gate& gate); // background service discovery void system_services_background_discovery(nmos::model& model, mdns::service_discovery& discovery, slog::base_gate& gate); @@ -32,25 +32,25 @@ namespace nmos // uses the default DNS-SD implementation // callbacks from this function are called with the model locked, and may read or write directly to the model - void node_system_behaviour_thread(nmos::model& model, system_global_handler system_changed, slog::base_gate& gate_) + void node_system_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, system_global_handler system_changed, slog::base_gate& gate_) { nmos::details::omanip_gate gate(gate_, nmos::stash_category(nmos::categories::node_system_behaviour)); mdns::service_discovery discovery(gate); - details::node_system_behaviour_thread(model, std::move(system_changed), discovery, gate); + details::node_system_behaviour_thread(model, std::move(load_ca_certificates), std::move(system_changed), discovery, gate); } // uses the specified DNS-SD implementation // callbacks from this function are called with the model locked, and may read or write directly to the model - void node_system_behaviour_thread(nmos::model& model, system_global_handler system_changed, mdns::service_discovery& discovery, slog::base_gate& gate_) + void node_system_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, system_global_handler system_changed, mdns::service_discovery& discovery, slog::base_gate& gate_) { nmos::details::omanip_gate gate(gate_, nmos::stash_category(nmos::categories::node_system_behaviour)); - details::node_system_behaviour_thread(model, std::move(system_changed), discovery, gate); + details::node_system_behaviour_thread(model, std::move(load_ca_certificates), std::move(system_changed), discovery, gate); } - void details::node_system_behaviour_thread(nmos::model& model, system_global_handler system_changed, mdns::service_discovery& discovery, slog::base_gate& gate) + void details::node_system_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, system_global_handler system_changed, mdns::service_discovery& discovery, slog::base_gate& gate) { enum { @@ -103,7 +103,7 @@ namespace nmos break; case node_system_behaviour: - details::node_system_behaviour(model, system_changed, gate); + details::node_system_behaviour(model, load_ca_certificates, system_changed, gate); // Should no further System APIs be available or TTLs on advertised services expired, a re-query may be performed. mode = rediscovery; @@ -229,9 +229,9 @@ namespace nmos namespace details { - web::http::client::http_client_config make_system_client_config(const nmos::settings& settings) + web::http::client::http_client_config make_system_client_config(const nmos::settings& settings, load_ca_certificates_handler load_ca_certificates, slog::base_gate& gate) { - auto config = nmos::make_http_client_config(settings); + auto config = nmos::make_http_client_config(settings, std::move(load_ca_certificates), gate); config.set_timeout(std::chrono::seconds(nmos::fields::system_request_max(settings))); return config; } @@ -368,7 +368,7 @@ namespace nmos }); } - void node_system_behaviour(nmos::model& model, system_global_handler system_changed, slog::base_gate& gate) + void node_system_behaviour(nmos::model& model, load_ca_certificates_handler load_ca_certificates, system_global_handler system_changed, slog::base_gate& gate) { slog::log(gate, SLOG_FLF) << "Attempting System API node behaviour"; @@ -407,7 +407,7 @@ namespace nmos if (!state.client) { const auto base_uri = top_system_service(model.settings); - state.client.reset(new web::http::client::http_client(base_uri, make_system_client_config(model.settings))); + state.client.reset(new web::http::client::http_client(base_uri, make_system_client_config(model.settings, load_ca_certificates, gate))); } auto token = cancellation_source.get_token(); diff --git a/Development/nmos/node_system_behaviour.h b/Development/nmos/node_system_behaviour.h index 32f5d1b40..46d9db29b 100644 --- a/Development/nmos/node_system_behaviour.h +++ b/Development/nmos/node_system_behaviour.h @@ -2,6 +2,7 @@ #define NMOS_NODE_SYSTEM_BEHAVIOUR_H #include +#include "nmos/certificate_handlers.h" namespace web { @@ -38,11 +39,11 @@ namespace nmos // uses the default DNS-SD implementation // callbacks from this function are called with the model locked, and may read or write directly to the model - void node_system_behaviour_thread(nmos::model& model, system_global_handler system_changed, slog::base_gate& gate); + void node_system_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, system_global_handler system_changed, slog::base_gate& gate); // uses the specified DNS-SD implementation // callbacks from this function are called with the model locked, and may read or write directly to the model - void node_system_behaviour_thread(nmos::model& model, system_global_handler system_changed, mdns::service_discovery& discovery, slog::base_gate& gate); + void node_system_behaviour_thread(nmos::model& model, load_ca_certificates_handler load_ca_certificates, system_global_handler system_changed, mdns::service_discovery& discovery, slog::base_gate& gate); } #endif diff --git a/Development/nmos/registry_server.cpp b/Development/nmos/registry_server.cpp index 43bcad39d..9df52277b 100644 --- a/Development/nmos/registry_server.cpp +++ b/Development/nmos/registry_server.cpp @@ -29,7 +29,7 @@ namespace nmos { // Construct a server instance for an NMOS Registry instance, implementing the IS-04 Registration and Query APIs, the Node API, the IS-09 System API // and the experimental DNS-SD Browsing API, Logging API and Settings API, according to the specified data models - nmos::server make_registry_server(nmos::registry_model& registry_model, nmos::experimental::log_model& log_model, slog::base_gate& gate) + nmos::server make_registry_server(nmos::registry_model& registry_model, nmos::experimental::registry_implementation registry_implementation, nmos::experimental::log_model& log_model, slog::base_gate& gate) { // Log the API addresses we'll be using @@ -105,7 +105,7 @@ namespace nmos // Set up the listeners for each HTTP API port - auto http_config = nmos::make_http_listener_config(registry_model.settings); + auto http_config = nmos::make_http_listener_config(registry_model.settings, registry_implementation.load_server_certificates, registry_implementation.load_dh_param, gate); for (auto& api_router : registry_server.api_routers) { @@ -118,7 +118,7 @@ namespace nmos // Set up the handlers for each WebSocket API port - auto websocket_config = nmos::make_websocket_listener_config(registry_model.settings); + auto websocket_config = nmos::make_websocket_listener_config(registry_model.settings, registry_implementation.load_server_certificates, registry_implementation.load_dh_param, gate); websocket_config.set_log_callback(nmos::make_slog_logging_callback(gate)); for (auto& ws_handler : registry_server.ws_handlers) @@ -142,6 +142,12 @@ namespace nmos return registry_server; } + + // deprecated + nmos::server make_registry_server(nmos::registry_model& registry_model, nmos::experimental::log_model& log_model, slog::base_gate& gate) + { + return make_registry_server(registry_model, registry_implementation(), log_model, gate); + } } void advertise_registry_thread(nmos::registry_model& model, slog::base_gate& gate) diff --git a/Development/nmos/registry_server.h b/Development/nmos/registry_server.h index 9a5290789..9ba1d3709 100644 --- a/Development/nmos/registry_server.h +++ b/Development/nmos/registry_server.h @@ -1,6 +1,8 @@ #ifndef NMOS_REGISTRY_SERVER_H #define NMOS_REGISTRY_SERVER_H +#include "nmos/certificate_handlers.h" + namespace slog { class base_gate; @@ -16,8 +18,41 @@ namespace nmos { struct log_model; - // Construct a server instance for an NMOS Registry instance, implementing the IS-04 Registration and Query APIs, the Node API, the IS-09 System API + // a type to simplify passing around the application callbacks necessary to integrate the device-specific + // underlying implementation into the server instance for the NMOS Registry + struct registry_implementation + { + registry_implementation(nmos::load_server_certificates_handler load_server_certificates, nmos::load_dh_param_handler load_dh_param, nmos::load_ca_certificates_handler load_ca_certificates) + : load_server_certificates(std::move(load_server_certificates)) + , load_dh_param(std::move(load_dh_param)) + , load_ca_certificates(std::move(load_ca_certificates)) + {} + + // use the default constructor and chaining member functions for fluent initialization + // (by itself, the default constructor does not construct a valid instance) + registry_implementation() + {} + + registry_implementation& on_load_server_certificates(nmos::load_server_certificates_handler load_server_certificates) { this->load_server_certificates = std::move(load_server_certificates); return *this; } + registry_implementation& on_load_dh_param(nmos::load_dh_param_handler load_dh_param) { this->load_dh_param = std::move(load_dh_param); return *this; } + registry_implementation& on_load_ca_certificates(nmos::load_ca_certificates_handler load_ca_certificates) { this->load_ca_certificates = std::move(load_ca_certificates); return *this; } + + // determine if the required callbacks have been specified + bool valid() const + { + return true; + } + + nmos::load_server_certificates_handler load_server_certificates; + nmos::load_dh_param_handler load_dh_param; + nmos::load_ca_certificates_handler load_ca_certificates; + }; + + // Construct a server instance for an NMOS Registry instance, implementing the IS-04 Registration and Query APIs, the Node API, the IS-09 System API, the IS-10 Authorization API // and the experimental DNS-SD Browsing API, Logging API and Settings API, according to the specified data models + nmos::server make_registry_server(nmos::registry_model& registry_model, nmos::experimental::registry_implementation registry_implementation, nmos::experimental::log_model& log_model, slog::base_gate& gate); + + // deprecated nmos::server make_registry_server(nmos::registry_model& registry_model, nmos::experimental::log_model& log_model, slog::base_gate& gate); } } diff --git a/Development/nmos/server_utils.cpp b/Development/nmos/server_utils.cpp index 487f1b731..e000bc651 100644 --- a/Development/nmos/server_utils.cpp +++ b/Development/nmos/server_utils.cpp @@ -6,9 +6,11 @@ #include "boost/asio/ssl/use_tmp_ecdh.hpp" #endif #include "cpprest/basic_utils.h" +#include "nmos/certificate_handlers.h" #include "cpprest/details/system_error.h" #include "cpprest/http_listener.h" #include "cpprest/ws_listener.h" +#include "nmos/slog.h" #include "nmos/ssl_context_options.h" // Utility types, constants and functions for implementing NMOS REST API servers @@ -18,41 +20,62 @@ namespace nmos { #if !defined(_WIN32) || !defined(__cplusplus_winrt) || defined(CPPREST_FORCE_HTTP_CLIENT_ASIO) template - inline std::function make_listener_ssl_context_callback(const nmos::settings& settings) + inline std::function make_listener_ssl_context_callback(const nmos::settings& settings, load_server_certificates_handler load_server_certificates, load_dh_param_handler load_dh_param, slog::base_gate& gate) { - const auto& private_key_files = nmos::experimental::fields::private_key_files(settings); - const auto& certificate_chain_files = nmos::experimental::fields::certificate_chain_files(settings); - const auto& dh_param_file = utility::us2s(nmos::experimental::fields::dh_param_file(settings)); - return [private_key_files, certificate_chain_files, dh_param_file](boost::asio::ssl::context& ctx) + if (!load_server_certificates) + { + load_server_certificates = make_load_server_certificates_handler(settings, gate); + } + + if (!load_dh_param) + { + load_dh_param = make_load_dh_param_handler(settings, gate); + } + + return [load_server_certificates, load_dh_param](boost::asio::ssl::context& ctx) { try { ctx.set_options(nmos::details::ssl_context_options); - if (private_key_files.size() == 0) - { - throw ExceptionType({}, "Missing private key file"); - } - for (const auto& private_key_file : private_key_files.as_array()) - { - ctx.use_private_key_file(utility::us2s(private_key_file.as_string()), boost::asio::ssl::context::pem); - } + const auto server_certificates = load_server_certificates(); - if (certificate_chain_files.size() == 0) + if (server_certificates.empty()) { - throw ExceptionType({}, "Missing certificate chain file"); + throw ExceptionType({}, "Missing server certificates"); } - for (const auto& certificate_chain_file : certificate_chain_files.as_array()) + + for (const auto& server_certificate : server_certificates) { - ctx.use_certificate_chain_file(utility::us2s(certificate_chain_file.as_string())); - // any one of the certificates may have ECDH parameters, so ignore errors... - boost::system::error_code ec; - use_tmp_ecdh_file(ctx, utility::us2s(certificate_chain_file.as_string()), ec); + const auto key = utility::us2s(server_certificate.private_key); + if (0 == key.size()) + { + throw ExceptionType({}, "Missing private key"); + } + const auto cert_chain = utility::us2s(server_certificate.certificate_chain); + if (0 == cert_chain.size()) + { + throw ExceptionType({}, "Missing certificate chain"); + } + ctx.use_private_key(boost::asio::buffer(key.data(), key.size()), boost::asio::ssl::context_base::pem); + ctx.use_certificate_chain(boost::asio::buffer(cert_chain.data(), cert_chain.size())); + + const auto key_algorithm = server_certificate.key_algorithm; + if (key_algorithm.name.empty() || key_algorithm == key_algorithms::ECDSA) + { + // certificates may not have ECDH parameters, so ignore errors... + boost::system::error_code ec; + use_tmp_ecdh(ctx, boost::asio::buffer(cert_chain.data(), cert_chain.size()), ec); + } } set_cipher_list(ctx, nmos::details::ssl_cipher_list); - if (!dh_param_file.empty()) ctx.use_tmp_dh_file(dh_param_file); + const auto dh_param = utility::us2s(load_dh_param()); + if (dh_param.size()) + { + ctx.use_tmp_dh(boost::asio::buffer(dh_param.data(), dh_param.size())); + } } catch (const boost::system::system_error& e) { @@ -64,7 +87,7 @@ namespace nmos } // construct listener config based on settings - web::http::experimental::listener::http_listener_config make_http_listener_config(const nmos::settings& settings) + web::http::experimental::listener::http_listener_config make_http_listener_config(const nmos::settings& settings, load_server_certificates_handler load_server_certificates, load_dh_param_handler load_dh_param, slog::base_gate& gate) { web::http::experimental::listener::http_listener_config config; config.set_backlog(nmos::fields::listen_backlog(settings)); @@ -72,19 +95,19 @@ namespace nmos // hmm, hostport_listener::on_accept(...) in http_server_asio.cpp // only expects boost::system::system_error to be thrown, so for now // don't use web::http::http_exception - config.set_ssl_context_callback(details::make_listener_ssl_context_callback(settings)); + config.set_ssl_context_callback(details::make_listener_ssl_context_callback(settings, load_server_certificates, load_dh_param, gate)); #endif return config; } // construct listener config based on settings - web::websockets::experimental::listener::websocket_listener_config make_websocket_listener_config(const nmos::settings& settings) + web::websockets::experimental::listener::websocket_listener_config make_websocket_listener_config(const nmos::settings& settings, load_server_certificates_handler load_server_certificates, load_dh_param_handler load_dh_param, slog::base_gate& gate) { web::websockets::experimental::listener::websocket_listener_config config; config.set_backlog(nmos::fields::listen_backlog(settings)); #if !defined(_WIN32) || !defined(__cplusplus_winrt) - config.set_ssl_context_callback(details::make_listener_ssl_context_callback(settings)); + config.set_ssl_context_callback(details::make_listener_ssl_context_callback(settings, load_server_certificates, load_dh_param, gate)); #endif return config; diff --git a/Development/nmos/server_utils.h b/Development/nmos/server_utils.h index bd6238264..48b87909e 100644 --- a/Development/nmos/server_utils.h +++ b/Development/nmos/server_utils.h @@ -3,16 +3,19 @@ #include "cpprest/http_listener.h" // forward declaration of web::http::experimental::listener::http_listener_config #include "cpprest/ws_listener.h" // forward declaration of web::websockets::experimental::listener::websocket_listener_config +#include "nmos/certificate_handlers.h" #include "nmos/settings.h" +namespace slog { class base_gate; } + // Utility types, constants and functions for implementing NMOS REST API servers namespace nmos { // construct listener config based on settings - web::http::experimental::listener::http_listener_config make_http_listener_config(const nmos::settings& settings); + web::http::experimental::listener::http_listener_config make_http_listener_config(const nmos::settings& settings, load_server_certificates_handler load_server_certificates, load_dh_param_handler load_dh_param, slog::base_gate& gate); // construct listener config based on settings - web::websockets::experimental::listener::websocket_listener_config make_websocket_listener_config(const nmos::settings& settings); + web::websockets::experimental::listener::websocket_listener_config make_websocket_listener_config(const nmos::settings& settings, load_server_certificates_handler load_server_certificates, load_dh_param_handler load_dh_param, slog::base_gate& gate); namespace experimental { diff --git a/Development/nmos/settings.h b/Development/nmos/settings.h index acbddea7d..cf82324c2 100644 --- a/Development/nmos/settings.h +++ b/Development/nmos/settings.h @@ -256,34 +256,17 @@ namespace nmos const web::json::field_as_integer_or href_mode{ U("href_mode"), 0 }; // when omitted, a default heuristic is used // client_secure [registry, node]: whether clients should use a secure connection for communication (https and wss) - // when true, CA root certificates must also be configured + // when true, CA root certificates must also be configured, see nmos/certificate_settings.h const web::json::field_as_bool_or client_secure{ U("client_secure"), false }; - // ca_certificate_file [registry, node]: full path of certification authorities file in PEM format - // on Windows, if C++ REST SDK is built with CPPREST_HTTP_CLIENT_IMPL=winhttp (reported as "client=winhttp" by nmos::get_build_settings_info) - // the trusted root CA certificates must also be imported into the certificate store - const web::json::field_as_string_or ca_certificate_file{ U("ca_certificate_file"), U("") }; - // server_secure [registry, node]: whether server should listen for secure connection for communication (https and wss) // e.g. typically false when using a reverse proxy, or the same as client_secure otherwise - // when true, server certificates etc. must also be configured + // when true, server certificates etc. must also be configured, see nmos/certificate_settings.h const web::json::field_as_bool_or server_secure{ U("server_secure"), false }; - // private_key_files [registry, node]: full paths of private key files in PEM format - const web::json::field_as_value_or private_key_files{ U("private_key_files"), web::json::value::array() }; - - // certificate_chain_files [registry, node]: full paths of server certificate chain files which must be in PEM format and must be sorted - // starting with the server's certificate, followed by any intermediate CA certificates, and ending with the highest level (root) CA - // on Windows, if C++ REST SDK is built with CPPREST_HTTP_LISTENER_IMPL=httpsys (reported as "listener=httpsys" by nmos::get_build_settings_info) - // one of the certificates must also be bound to each port e.g. using 'netsh add sslcert' - const web::json::field_as_value_or certificate_chain_files{ U("certificate_chain_files"), web::json::value::array() }; - // validate_certificates [registry, node]: boolean value, false (ignore all server certificate validation errors), or true (do not ignore, the default behaviour) const web::json::field_as_bool_or validate_certificates{ U("validate_certificates"), true }; - // dh_param_file [registry, node]: Diffie-Hellman parameters file in PEM format for ephemeral key exchange support, or empty string for no support - const web::json::field_as_string_or dh_param_file{ U("dh_param_file"), U("") }; - // system_interval_min/system_interval_max [node]: used to poll for System API changes; default is about one hour const web::json::field_as_integer_or system_interval_min{ U("system_interval_min"), 3600 }; const web::json::field_as_integer_or system_interval_max{ U("system_interval_max"), 3660 }; diff --git a/Sandbox/run_nmos_testing.sh b/Sandbox/run_nmos_testing.sh index c5d7e1091..447a7ee6e 100755 --- a/Sandbox/run_nmos_testing.sh +++ b/Sandbox/run_nmos_testing.sh @@ -44,15 +44,20 @@ if [[ "${config_secure}" == "True" ]]; then common_params=",\"client_secure\":true,\ \"server_secure\":true,\ \"ca_certificate_file\":\"test_data/BCP00301/ca/certs/ca.cert.pem\",\ - \"private_key_files\":[\ - \"test_data/BCP00301/ca/intermediate/private/ecdsa.api.testsuite.nmos.tv.key.pem\", - \"test_data/BCP00301/ca/intermediate/private/rsa.api.testsuite.nmos.tv.key.pem\"],\ - \"certificate_chain_files\":[\ - \"test_data/BCP00301/ca/intermediate/certs/ecdsa.api.testsuite.nmos.tv.cert.chain.pem\",\ - \"test_data/BCP00301/ca/intermediate/certs/rsa.api.testsuite.nmos.tv.cert.chain.pem\"],\ - \"dh_param_file\":\"test_data/BCP00301/ca/intermediate/private/dhparam.pem\", - \"host_name\":\"${host}\", - \"host_address\":\"${host_ip}\" + \"server_certificates\":\ + [{\ + \"key_algorithm\":\"ECDSA\",\ + \"private_key_file\":\"test_data/BCP00301/ca/intermediate/private/ecdsa.api.testsuite.nmos.tv.key.pem\",\ + \"certificate_chain_file\":\"test_data/BCP00301/ca/intermediate/certs/ecdsa.api.testsuite.nmos.tv.cert.chain.pem\"\ + },\ + {\ + \"key_algorithm\":\"RSA\",\ + \"private_key_file\":\"test_data/BCP00301/ca/intermediate/private/rsa.api.testsuite.nmos.tv.key.pem\",\ + \"certificate_chain_file\":\"test_data/BCP00301/ca/intermediate/certs/rsa.api.testsuite.nmos.tv.cert.chain.pem\"\ + }],\ + \"dh_param_file\":\"test_data/BCP00301/ca/intermediate/private/dhparam.pem\",\ + \"host_name\":\"${host}\",\ + \"host_address\":\"${host_ip}\"\ " registry_url=https://${host}:8088 else From 58aa4d20ccada0636fac0d8580b1abe8635e102f Mon Sep 17 00:00:00 2001 From: Simon Lo Date: Tue, 18 May 2021 10:38:06 +0100 Subject: [PATCH 009/385] Fix bcp-003-01 for windows (#185) Fix running of test-suite on WSL 18.04 for Windows ASIO build. Update name of `server_certificate' structure to `certificate` for future use. Optimize reading of certificate files. --- .github/workflows/build-test.yml | 10 +++---- .github/workflows/src/amwa-test.yml | 10 +++---- Development/nmos/certificate_handlers.cpp | 36 ++++++++++------------- Development/nmos/certificate_handlers.h | 14 ++++----- 4 files changed, 33 insertions(+), 37 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 8942b80b4..234b363ac 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -273,11 +273,11 @@ jobs: - name: install wsl if: runner.os == 'Windows' run: | - & curl -L https://aka.ms/wslubuntu2004 -o ubuntu204.appx - Rename-Item .\ubuntu204.appx .\ubuntu204.zip - Expand-Archive .\ubuntu204.zip .\ubuntu204 - cd ubuntu204 - .\ubuntu2004.exe install --root + & curl -L https://aka.ms/wsl-ubuntu-1804 -o ubuntu-1804.appx + Rename-Item .\ubuntu-1804.appx .\ubuntu-1804.zip + Expand-Archive .\ubuntu-1804.zip .\ubuntu-1804 + cd ubuntu-1804 + .\ubuntu1804.exe install --root - name: AMWA test suite shell: bash diff --git a/.github/workflows/src/amwa-test.yml b/.github/workflows/src/amwa-test.yml index 7607ef828..aa2202570 100644 --- a/.github/workflows/src/amwa-test.yml +++ b/.github/workflows/src/amwa-test.yml @@ -1,11 +1,11 @@ - name: install wsl if: runner.os == 'Windows' run: | - & curl -L https://aka.ms/wslubuntu2004 -o ubuntu204.appx - Rename-Item .\ubuntu204.appx .\ubuntu204.zip - Expand-Archive .\ubuntu204.zip .\ubuntu204 - cd ubuntu204 - .\ubuntu2004.exe install --root + & curl -L https://aka.ms/wsl-ubuntu-1804 -o ubuntu-1804.appx + Rename-Item .\ubuntu-1804.appx .\ubuntu-1804.zip + Expand-Archive .\ubuntu-1804.zip .\ubuntu-1804 + cd ubuntu-1804 + .\ubuntu1804.exe install --root - name: AMWA test suite shell: bash diff --git a/Development/nmos/certificate_handlers.cpp b/Development/nmos/certificate_handlers.cpp index 6a0a04245..d04dede53 100644 --- a/Development/nmos/certificate_handlers.cpp +++ b/Development/nmos/certificate_handlers.cpp @@ -13,8 +13,6 @@ namespace nmos return [&, ca_certificate_file]() { - utility::string_t data; - slog::log(gate, SLOG_FLF) << "Load certification authorities"; if (ca_certificate_file.empty()) @@ -23,12 +21,12 @@ namespace nmos } else { - std::ifstream ca_file(ca_certificate_file); - std::stringstream cacerts; + utility::ifstream_t ca_file(ca_certificate_file); + utility::stringstream_t cacerts; cacerts << ca_file.rdbuf(); - data = utility::s2us(cacerts.str()); + return cacerts.str(); } - return data; + return utility::string_t{}; }; } @@ -59,7 +57,7 @@ namespace nmos { slog::log(gate, SLOG_FLF) << "Load server private keys and certificate chains"; - auto data = std::vector(); + auto data = std::vector(); if (0 == server_certificates.size()) { @@ -72,29 +70,29 @@ namespace nmos const auto private_key_file = nmos::experimental::fields::private_key_file(server_certificate); const auto certificate_chain_file = nmos::experimental::fields::certificate_chain_file(server_certificate); - std::stringstream pkey; + utility::stringstream_t pkey; if (private_key_file.empty()) { - slog::log(gate, SLOG_FLF) << "Missing private key file"; + slog::log(gate, SLOG_FLF) << "Missing server private key file"; } else { - std::ifstream pkey_file(private_key_file); + utility::ifstream_t pkey_file(private_key_file); pkey << pkey_file.rdbuf(); } - std::stringstream cert_chain; + utility::stringstream_t cert_chain; if (certificate_chain_file.empty()) { - slog::log(gate, SLOG_FLF) << "Missing certificate chain file"; + slog::log(gate, SLOG_FLF) << "Missing server certificate chain file"; } else { - std::ifstream cert_chain_file(certificate_chain_file); + utility::ifstream_t cert_chain_file(certificate_chain_file); cert_chain << cert_chain_file.rdbuf(); } - data.push_back(nmos::server_certificate(nmos::key_algorithm{ key_algorithm }, utility::s2us(pkey.str()), utility::s2us(cert_chain.str()))); + data.push_back(nmos::certificate(nmos::key_algorithm{ key_algorithm }, pkey.str(), cert_chain.str())); } return data; }; @@ -109,20 +107,18 @@ namespace nmos { slog::log(gate, SLOG_FLF) << "Load DH parameters"; - utility::string_t data; - if (dh_param_file.empty()) { slog::log(gate, SLOG_FLF) << "Missing DH parameters file"; } else { - std::ifstream dh_file(dh_param_file); - std::stringstream dh_param; + utility::ifstream_t dh_file(dh_param_file); + utility::stringstream_t dh_param; dh_param << dh_file.rdbuf(); - data = utility::s2us(dh_param.str()); + return dh_param.str(); } - return data; + return utility::string_t{}; }; } } diff --git a/Development/nmos/certificate_handlers.h b/Development/nmos/certificate_handlers.h index 3b79a8525..9633ae54f 100644 --- a/Development/nmos/certificate_handlers.h +++ b/Development/nmos/certificate_handlers.h @@ -29,18 +29,18 @@ namespace nmos const key_algorithm RSA{ U("RSA") }; } - // server certificate details including the private key and the certificate chain in PEM format + // certificate details including the private key and the certificate chain in PEM format // the key algorithm may also be specified - struct server_certificate + struct certificate { - server_certificate() {} + certificate() {} - server_certificate(utility::string_t private_key, utility::string_t certificate_chain) + certificate(utility::string_t private_key, utility::string_t certificate_chain) : private_key(std::move(private_key)) , certificate_chain(std::move(certificate_chain)) {} - server_certificate(nmos::key_algorithm key_algorithm, utility::string_t private_key, utility::string_t certificate_chain) + certificate(nmos::key_algorithm key_algorithm, utility::string_t private_key, utility::string_t certificate_chain) : key_algorithm(std::move(key_algorithm)) , private_key(std::move(private_key)) , certificate_chain(std::move(certificate_chain)) @@ -48,7 +48,7 @@ namespace nmos nmos::key_algorithm key_algorithm; utility::string_t private_key; - // the chain should be sorted starting with the server's certificate, followed by any intermediate CA certificates, and ending with the highest level (root) CA + // the chain should be sorted starting with the end entity's certificate, followed by any intermediate CA certificates, and ending with the highest level (root) CA utility::string_t certificate_chain; }; @@ -57,7 +57,7 @@ namespace nmos // this callback should not throw exceptions // on Windows, if C++ REST SDK is built with CPPREST_HTTP_LISTENER_IMPL=httpsys (reported as "listener=httpsys" by nmos::get_build_settings_info) // one of the certificates must also be bound to each port e.g. using 'netsh add sslcert' - typedef std::function()> load_server_certificates_handler; + typedef std::function()> load_server_certificates_handler; // callback to supply Diffie-Hellman parameters for ephemeral key exchange support, in PEM format or empty string for no support // see e.g. https://wiki.openssl.org/index.php/Diffie-Hellman_parameters From cba288e143acf6a0c3d9bd9f6044c1c2eef3e9de Mon Sep 17 00:00:00 2001 From: "Jonathan Thorpe (Sony)" Date: Thu, 27 May 2021 11:53:05 +0100 Subject: [PATCH 010/385] Update json validator to version 2.1.0 and nlohmann json to version 3.9.1 to fix issue #182. --- Development/cmake/NmosCppLibraries.cmake | 2 + Development/cmake/NmosCppTest.cmake | 1 + Development/nmos/test/json_validator_test.cpp | 87 + Development/third_party/nlohmann/README.md | 2 +- .../third_party/nlohmann/json-patch.cpp | 115 + .../third_party/nlohmann/json-patch.hpp | 38 + .../third_party/nlohmann/json-schema.hpp | 57 +- Development/third_party/nlohmann/json-uri.cpp | 42 +- .../third_party/nlohmann/json-validator.cpp | 277 +- Development/third_party/nlohmann/json.hpp | 17490 ++++++++++------ 10 files changed, 11559 insertions(+), 6552 deletions(-) create mode 100644 Development/nmos/test/json_validator_test.cpp create mode 100644 Development/third_party/nlohmann/json-patch.cpp create mode 100644 Development/third_party/nlohmann/json-patch.hpp diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index 571af9b11..ccf973367 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -619,12 +619,14 @@ install(TARGETS nmos_is09_schemas_static DESTINATION lib) # json schema validator library set(JSON_SCHEMA_VALIDATOR_SOURCES + ${NMOS_CPP_DIR}/third_party/nlohmann/json-patch.cpp ${NMOS_CPP_DIR}/third_party/nlohmann/json-schema-draft7.json.cpp ${NMOS_CPP_DIR}/third_party/nlohmann/json-validator.cpp ${NMOS_CPP_DIR}/third_party/nlohmann/json-uri.cpp ) set(JSON_SCHEMA_VALIDATOR_HEADERS + ${NMOS_CPP_DIR}/third_party/nlohmann/json-patch.hpp ${NMOS_CPP_DIR}/third_party/nlohmann/json-schema.hpp ${NMOS_CPP_DIR}/third_party/nlohmann/json.hpp ) diff --git a/Development/cmake/NmosCppTest.cmake b/Development/cmake/NmosCppTest.cmake index f0844c27b..024f6aef6 100644 --- a/Development/cmake/NmosCppTest.cmake +++ b/Development/cmake/NmosCppTest.cmake @@ -50,6 +50,7 @@ set(NMOS_CPP_TEST_NMOS_TEST_SOURCES ${NMOS_CPP_DIR}/nmos/test/channels_test.cpp ${NMOS_CPP_DIR}/nmos/test/did_sdid_test.cpp ${NMOS_CPP_DIR}/nmos/test/event_type_test.cpp + ${NMOS_CPP_DIR}/nmos/test/json_validator_test.cpp ${NMOS_CPP_DIR}/nmos/test/paging_utils_test.cpp ) set(NMOS_CPP_TEST_NMOS_TEST_HEADERS diff --git a/Development/nmos/test/json_validator_test.cpp b/Development/nmos/test/json_validator_test.cpp new file mode 100644 index 000000000..92bb29542 --- /dev/null +++ b/Development/nmos/test/json_validator_test.cpp @@ -0,0 +1,87 @@ +// The first "test" is of course whether the header compiles standalone +#include "cpprest/json_validator.h" + +#include "bst/test/test.h" +#include "cpprest/basic_utils.h" // for utility::us2s, utility::s2us +#include "cpprest/json_utils.h" + +namespace +{ + const auto id = web::uri{ U("/test") }; + + web::json::experimental::json_validator make_validator(const web::json::value& schema, const web::uri& id) + { + return web::json::experimental::json_validator + { + [&](const web::uri&) { return schema; }, + { id } + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testInvalidTypeSchema) +{ + using web::json::value_of; + + const bool keep_order = true; + + const auto schema = value_of({ + { U("$schema"), U("http://json-schema.org/draft-04/schema#")}, + { U("type"), U("object")}, + { U("properties"), value_of({ + { U("foo"), value_of({ + { U("anyOf"), value_of({ + value_of({ + { U("type"), U("string") }, + { U("pattern"), U("^auto$") } + }, keep_order), + U("bad") }) + }}) + }}) + }}); + + // invalid JSON-type for schema + BST_REQUIRE_THROW(make_validator(schema, id), std::invalid_argument); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +BST_TEST_CASE(testEnumSchema) +{ + using web::json::value_of; + + const bool keep_order = true; + + const auto schema = value_of({ + { U("$schema"), U("http://json-schema.org/draft-04/schema#")}, + { U("type"), U("object")}, + { U("properties"), value_of({ + { U("foo"), value_of({ + { U("anyOf"), value_of({ + value_of({ + { U("type"), U("string") }, + { U("pattern"), U("^auto$") } + }, keep_order), + value_of({ + { U("enum"), value_of({ + { U("good") } + }) + }}) + }) + }}) + }}) + }}); + + auto validator = make_validator(schema, id); + + // not in anyOf + BST_REQUIRE_THROW(validator.validate(value_of({ { U("foo"), U("bad") } }), id), web::json::json_exception); + + // in anyOf, pattern + validator.validate(value_of({ { U("foo"), U("auto") } }), id); + BST_REQUIRE(true); + + // in anyOf, enum + validator.validate(value_of({ { U("foo"), U("good") } }), id); + BST_REQUIRE(true); +} diff --git a/Development/third_party/nlohmann/README.md b/Development/third_party/nlohmann/README.md index 0a9210222..e980c8a55 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-2018 Niels Lohmann . +- Copyright (c) 2013-2020 Niels Lohmann . ## Modern C++ JSON schema validator diff --git a/Development/third_party/nlohmann/json-patch.cpp b/Development/third_party/nlohmann/json-patch.cpp new file mode 100644 index 000000000..2276c92be --- /dev/null +++ b/Development/third_party/nlohmann/json-patch.cpp @@ -0,0 +1,115 @@ +#include "json-patch.hpp" + +#include + +namespace +{ + +// originally from http://jsonpatch.com/, http://json.schemastore.org/json-patch +// with fixes +const nlohmann::json patch_schema = R"patch({ + "title": "JSON schema for JSONPatch files", + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + + "items": { + "oneOf": [ + { + "additionalProperties": false, + "required": [ "value", "op", "path"], + "properties": { + "path" : { "$ref": "#/definitions/path" }, + "op": { + "description": "The operation to perform.", + "type": "string", + "enum": [ "add", "replace", "test" ] + }, + "value": { + "description": "The value to add, replace or test." + } + } + }, + { + "additionalProperties": false, + "required": [ "op", "path"], + "properties": { + "path" : { "$ref": "#/definitions/path" }, + "op": { + "description": "The operation to perform.", + "type": "string", + "enum": [ "remove" ] + } + } + }, + { + "additionalProperties": false, + "required": [ "from", "op", "path" ], + "properties": { + "path" : { "$ref": "#/definitions/path" }, + "op": { + "description": "The operation to perform.", + "type": "string", + "enum": [ "move", "copy" ] + }, + "from": { + "$ref": "#/definitions/path", + "description": "A JSON Pointer path pointing to the location to move/copy from." + } + } + } + ] + }, + "definitions": { + "path": { + "description": "A JSON Pointer path.", + "type": "string" + } + } +})patch"_json; +}; // namespace + +namespace nlohmann +{ + +json_patch::json_patch(json &&patch) + : j_(std::move(patch)) +{ + validateJsonPatch(j_); +} + +json_patch::json_patch(const json &patch) + : j_(std::move(patch)) +{ + validateJsonPatch(j_); +} + +json_patch &json_patch::add(const json::json_pointer &ptr, json value) +{ + j_.push_back(json{{"op", "add"}, {"path", ptr}, {"value", std::move(value)}}); + return *this; +} + +json_patch &json_patch::replace(const json::json_pointer &ptr, json value) +{ + j_.push_back(json{{"op", "replace"}, {"path", ptr}, {"value", std::move(value)}}); + return *this; +} + +json_patch &json_patch::remove(const json::json_pointer &ptr) +{ + j_.push_back(json{{"op", "remove"}, {"path", ptr}}); + return *this; +} + +void json_patch::validateJsonPatch(json const &patch) +{ + // static put here to have it created at the first usage of validateJsonPatch + static nlohmann::json_schema::json_validator patch_validator(patch_schema); + + patch_validator.validate(patch); + + for (auto const &op : patch) + json::json_pointer(op["path"].get()); +} + +} // namespace nlohmann diff --git a/Development/third_party/nlohmann/json-patch.hpp b/Development/third_party/nlohmann/json-patch.hpp new file mode 100644 index 000000000..80fe1dfc2 --- /dev/null +++ b/Development/third_party/nlohmann/json-patch.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +namespace nlohmann +{ +class JsonPatchFormatException : public std::exception +{ +public: + explicit JsonPatchFormatException(std::string msg) + : ex_{std::move(msg)} {} + + inline const char *what() const noexcept override final { return ex_.c_str(); } + +private: + std::string ex_; +}; + +class json_patch +{ +public: + json_patch() = default; + json_patch(json &&patch); + json_patch(const json &patch); + + json_patch &add(const json::json_pointer &, json value); + json_patch &replace(const json::json_pointer &, json value); + json_patch &remove(const json::json_pointer &); + + operator json() const { return j_; } + +private: + json j_; + + static void validateJsonPatch(json const &patch); +}; +} // namespace nlohmann diff --git a/Development/third_party/nlohmann/json-schema.hpp b/Development/third_party/nlohmann/json-schema.hpp index 165a9c90f..baa971971 100644 --- a/Development/third_party/nlohmann/json-schema.hpp +++ b/Development/third_party/nlohmann/json-schema.hpp @@ -48,10 +48,12 @@ class JSON_SCHEMA_VALIDATOR_API json_uri { std::string urn_; - std::string proto_; - std::string hostname_; + std::string scheme_; + std::string authority_; std::string path_; - json::json_pointer pointer_; + + json::json_pointer pointer_; // fragment part if JSON-Pointer + std::string identifier_; // fragment part if Locatation Independent ID protected: // decodes a JSON uri and replaces all or part of the currently stored values @@ -59,7 +61,8 @@ class JSON_SCHEMA_VALIDATOR_API json_uri std::tuple tie() const { - return std::tie(urn_, proto_, hostname_, path_, pointer_); + return std::tie(urn_, scheme_, authority_, path_, + identifier_ != "" ? identifier_ : pointer_); } public: @@ -68,14 +71,23 @@ class JSON_SCHEMA_VALIDATOR_API json_uri update(uri); } - const std::string protocol() const { return proto_; } - const std::string hostname() const { return hostname_; } - const std::string path() const { return path_; } + const std::string &scheme() const { return scheme_; } + const std::string &authority() const { return authority_; } + const std::string &path() const { return path_; } + + const json::json_pointer &pointer() const { return pointer_; } + const std::string &identifier() const { return identifier_; } - const json::json_pointer pointer() const { return pointer_; } + std::string fragment() const + { + if (identifier_ == "") + return pointer_; + else + return identifier_; + } - const std::string url() const { return location(); } - const std::string location() const; + std::string url() const { return location(); } + std::string location() const; static std::string escape(const std::string &); @@ -91,6 +103,9 @@ class JSON_SCHEMA_VALIDATOR_API json_uri // append a pointer-field to the pointer-part of this uri json_uri append(const std::string &field) const { + if (identifier_ != "") + return *this; + json_uri u = *this; u.pointer_ /= field; return u; @@ -141,6 +156,11 @@ class JSON_SCHEMA_VALIDATOR_API basic_error_handler : public error_handler operator bool() const { return error_; } }; +/** + * Checks validity of JSON schema built-in string format specifiers like 'date-time', 'ipv4', ... + */ +void default_string_format_check(const std::string &format, const std::string &value); + class root_schema; class JSON_SCHEMA_VALIDATOR_API json_validator @@ -149,18 +169,27 @@ class JSON_SCHEMA_VALIDATOR_API json_validator public: json_validator(schema_loader = nullptr, format_checker = nullptr); + + json_validator(const json &, schema_loader = nullptr, format_checker = nullptr); + json_validator(json &&, schema_loader = nullptr, format_checker = nullptr); + json_validator(json_validator &&); - ~json_validator(); json_validator &operator=(json_validator &&); - // insert and set thea root-schema + json_validator(json_validator const &) = delete; + json_validator &operator=(json_validator const &) = delete; + + ~json_validator(); + + // insert and set the root-schema void set_root_schema(const json &); + void set_root_schema(json &&); // validate a json-document based on the root-schema - void validate(const json &) const; + json validate(const json &) const; // validate a json-document based on the root-schema with a custom error-handler - void validate(const json &, error_handler &) const; + json validate(const json &, error_handler &) const; }; } // namespace json_schema diff --git a/Development/third_party/nlohmann/json-uri.cpp b/Development/third_party/nlohmann/json-uri.cpp index c0e37cb76..6ff1c169a 100644 --- a/Development/third_party/nlohmann/json-uri.cpp +++ b/Development/third_party/nlohmann/json-uri.cpp @@ -6,7 +6,7 @@ * SPDX-License-Identifier: MIT * */ -#include "json-schema.hpp" +#include #include @@ -44,16 +44,15 @@ void json_uri::update(const std::string &uri) auto location = uri.substr(0, pointer_separator); - if (location.size()) { // a location part has been found - pointer_ = ""_json_pointer; // if a location is given, the pointer is emptied + if (location.size()) { // a location part has been found // if it is an URN take it as it is if (location.find("urn:") == 0) { urn_ = location; // and clear URL members - proto_ = ""; - hostname_ = ""; + scheme_ = ""; + authority_ = ""; path_ = ""; } else { // it is an URL @@ -65,13 +64,13 @@ void json_uri::update(const std::string &uri) urn_ = ""; // clear URN-member if URL is parsed - proto_ = location.substr(pos, proto - pos); + scheme_ = location.substr(pos, proto - pos); pos = 3 + proto; // 3 == "://" - auto hostname = location.find("/", pos); - if (hostname != std::string::npos) { // and the hostname (no proto without hostname) - hostname_ = location.substr(pos, hostname - pos); - pos = hostname; + auto authority = location.find("/", pos); + if (authority != std::string::npos) { // and the hostname (no proto without hostname) + authority_ = location.substr(pos, authority - pos); + pos = authority; } } @@ -91,20 +90,26 @@ void json_uri::update(const std::string &uri) } } - pointer_ = json::json_pointer(pointer); + pointer_ = ""_json_pointer; + identifier_ = ""; + + if (pointer[0] == '/') + pointer_ = json::json_pointer(pointer); + else + identifier_ = pointer; } -const std::string json_uri::location() const +std::string json_uri::location() const { if (urn_.size()) return urn_; std::stringstream s; - if (proto_.size() > 0) - s << proto_ << "://"; + if (scheme_.size() > 0) + s << scheme_ << "://"; - s << hostname_ + s << authority_ << path_; return s.str(); @@ -114,7 +119,12 @@ std::string json_uri::to_string() const { std::stringstream s; - s << location() << " # " << pointer_.to_string(); + s << location() << " # "; + + if (identifier_ == "") + s << pointer_.to_string(); + else + s << identifier_; return s.str(); } diff --git a/Development/third_party/nlohmann/json-validator.cpp b/Development/third_party/nlohmann/json-validator.cpp index 2a7e063e6..f16f3ef6c 100644 --- a/Development/third_party/nlohmann/json-validator.cpp +++ b/Development/third_party/nlohmann/json-validator.cpp @@ -6,13 +6,16 @@ * SPDX-License-Identifier: MIT * */ -#include +#include + +#include "json-patch.hpp" #include #include #include using nlohmann::json; +using nlohmann::json_patch; using nlohmann::json_uri; using nlohmann::json_schema::root_schema; using namespace nlohmann::json_schema; @@ -30,16 +33,25 @@ using namespace nlohmann::json_schema; namespace { +static const json EmptyDefault{}; + class schema { protected: root_schema *root_; public: + virtual ~schema() = default; + schema(root_schema *root) : root_(root) {} - virtual void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const = 0; + virtual void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const = 0; + + virtual const json &defaultValue(const json::json_pointer &, const json &, error_handler &) const + { + return EmptyDefault; + } static std::shared_ptr make(json &schema, root_schema *root, @@ -50,14 +62,28 @@ class schema class schema_ref : public schema { const std::string id_; - std::shared_ptr target_; + std::weak_ptr target_; + + void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final + { + auto target = target_.lock(); + + if (target) + target->validate(ptr, instance, patch, e); + else + e.error(ptr, instance, "unresolved or freed schema-reference " + id_); + } - void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const final + const json &defaultValue(const json::json_pointer &ptr, const json &instance, error_handler &e) const override { - if (target_) - target_->validate(ptr, instance, e); + auto target = target_.lock(); + + if (target) + return target->defaultValue(ptr, instance, e); else - e.error(ptr, instance, "unresolved schema-reference " + id_); + e.error(ptr, instance, "unresolved or freed schema-reference " + id_); + + return EmptyDefault; } public: @@ -65,7 +91,7 @@ class schema_ref : public schema : schema(root), id_(id) {} const std::string &id() const { return id_; } - void set_target(std::shared_ptr target) { target_ = target; } + void set_target(const std::shared_ptr &target) { target_ = target; } }; } // namespace @@ -83,8 +109,8 @@ class root_schema : public schema std::shared_ptr root_; struct schema_file { - std::map> schemas; - std::map> unresolved; // contains all unresolved references from any other file seen during parsing + std::map> schemas; + std::map> unresolved; // contains all unresolved references from any other file seen during parsing json unknown_keywords; }; @@ -101,25 +127,25 @@ class root_schema : public schema } public: - root_schema(schema_loader loader, - format_checker format) - : schema(this), loader_(loader), format_check_(format) {} + root_schema(schema_loader &&loader, + format_checker &&format) + : schema(this), loader_(std::move(loader)), format_check_(std::move(format)) {} format_checker &format_check() { return format_check_; } void insert(const json_uri &uri, const std::shared_ptr &s) { auto &file = get_or_create_file(uri.location()); - auto schema = file.schemas.lower_bound(uri.pointer()); - if (schema != file.schemas.end() && !(file.schemas.key_comp()(uri.pointer(), schema->first))) { + auto schema = file.schemas.lower_bound(uri.fragment()); + if (schema != file.schemas.end() && !(file.schemas.key_comp()(uri.fragment(), schema->first))) { throw std::invalid_argument("schema with " + uri.to_string() + " already inserted"); return; } - file.schemas.insert({uri.pointer(), s}); + file.schemas.insert({uri.fragment(), s}); // was someone referencing this newly inserted schema? - auto unresolved = file.unresolved.find(uri.pointer()); + auto unresolved = file.unresolved.find(uri.fragment()); if (unresolved != file.unresolved.end()) { unresolved->second->set_target(s); file.unresolved.erase(unresolved); @@ -130,14 +156,19 @@ class root_schema : public schema { auto &file = get_or_create_file(uri.location()); auto new_uri = uri.append(key); - auto pointer = new_uri.pointer(); + auto fragment = new_uri.pointer(); // is there a reference looking for this unknown-keyword, which is thus no longer a unknown keyword but a schema - auto unresolved = file.unresolved.find(pointer); + auto unresolved = file.unresolved.find(fragment); if (unresolved != file.unresolved.end()) schema::make(value, this, {}, {{new_uri}}); - else // no, nothing ref'd it - file.unknown_keywords[pointer] = value; + else // no, nothing ref'd it, keep for later + file.unknown_keywords[fragment] = value; + + // recursively add possible subschemas of unknown keywords + if (value.type() == json::value_t::object) + for (auto &subsch : value.items()) + insert_unknown_keyword(new_uri, subsch.key(), subsch.value()); } std::shared_ptr get_or_create_ref(const json_uri &uri) @@ -145,32 +176,40 @@ class root_schema : public schema auto &file = get_or_create_file(uri.location()); // existing schema - auto schema = file.schemas.find(uri.pointer()); + auto schema = file.schemas.find(uri.fragment()); if (schema != file.schemas.end()) return schema->second; // referencing an unknown keyword, turn it into schema - try { - auto &subschema = file.unknown_keywords.at(uri.pointer()); - auto s = schema::make(subschema, this, {}, {{uri}}); - file.unknown_keywords.erase(uri.pointer()); - return s; - } catch (...) { + // + // an unknown keyword can only be referenced by a json-pointer, + // not by a plain name fragment + if (uri.pointer() != "") { + try { + auto &subschema = file.unknown_keywords.at(uri.pointer()); // null is returned if not existing + auto s = schema::make(subschema, this, {}, {{uri}}); // A JSON Schema MUST be an object or a boolean. + if (s) { // nullptr if invalid schema, e.g. null + file.unknown_keywords.erase(uri.fragment()); + return s; + } + } catch (nlohmann::detail::out_of_range &) { // at() did not find it + } } // get or create a schema_ref - auto r = file.unresolved.lower_bound(uri.pointer()); - if (r != file.unresolved.end() && !(file.unresolved.key_comp()(uri.pointer(), r->first))) { - return r->second; + auto r = file.unresolved.lower_bound(uri.fragment()); + if (r != file.unresolved.end() && !(file.unresolved.key_comp()(uri.fragment(), r->first))) { + return r->second; // unresolved, already seen previously - use existing reference } else { return file.unresolved.insert(r, - {uri.pointer(), std::make_shared(uri.to_string(), this)}) - ->second; + {uri.fragment(), std::make_shared(uri.to_string(), this)}) + ->second; // unresolved, create reference } } void set_root_schema(json schema) { + files_.clear(); root_ = schema::make(schema, this, {}, {{"#"}}); // load all files which have not yet been loaded @@ -200,14 +239,30 @@ class root_schema : public schema if (!new_schema_loaded) // if no new schema loaded, no need to try again break; } while (1); + + for (const auto &file : files_) + if (file.second.unresolved.size() != 0) + throw std::invalid_argument("after all files have been parsed, '" + + (file.first == "" ? "" : file.first) + + "' has still undefined references."); + } + + void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final + { + if (root_) + root_->validate(ptr, instance, patch, e); + else + e.error(ptr, "", "no root schema has yet been set for validating an instance"); } - void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const final + const json &defaultValue(const json::json_pointer &ptr, const json &instance, error_handler &e) const override { if (root_) - root_->validate(ptr, instance, e); + return root_->defaultValue(ptr, instance, e); else e.error(ptr, "", "no root schema has yet been set for validating an instance"); + + return EmptyDefault; } }; @@ -225,7 +280,7 @@ class first_error_handler : public error_handler json instance_; std::string message_; - void error(const json::json_pointer & ptr, const json & instance, const std::string & message) override + void error(const json::json_pointer &ptr, const json &instance, const std::string &message) override { if (*this) return; @@ -242,15 +297,20 @@ class logical_not : public schema { std::shared_ptr subschema_; - void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const final + void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final { first_error_handler esub; - subschema_->validate(ptr, instance, esub); + subschema_->validate(ptr, instance, patch, esub); if (!esub) e.error(ptr, instance, "the subschema has succeeded, but it is required to not validate"); } + const json &defaultValue(const json::json_pointer &ptr, const json &instance, error_handler &e) const override + { + return subschema_->defaultValue(ptr, instance, e); + } + public: logical_not(json &sch, root_schema *root, @@ -272,13 +332,13 @@ class logical_combination : public schema { std::vector> subschemata_; - void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const final + void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const final { size_t count = 0; for (auto &s : subschemata_) { first_error_handler esub; - s->validate(ptr, instance, esub); + s->validate(ptr, instance, patch, esub); if (!esub) count++; @@ -319,7 +379,7 @@ template <> const std::string logical_combination::key = "oneOf"; template <> -bool logical_combination::is_validate_complete(const json &instance, const json::json_pointer &ptr, error_handler &e, const first_error_handler &esub, size_t) +bool logical_combination::is_validate_complete(const json &, const json::json_pointer &, error_handler &e, const first_error_handler &esub, size_t) { if (esub) e.error(esub.ptr_, esub.instance_, "at least one subschema has failed, but all of them are required to validate - " + esub.message_); @@ -342,6 +402,7 @@ bool logical_combination::is_validate_complete(const json &instance, cons class type_schema : public schema { + json defaultValue_{}; std::vector> type_; std::pair enum_, const_; std::vector> logic_; @@ -354,13 +415,18 @@ class type_schema : public schema std::shared_ptr if_, then_, else_; - void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override final + const json &defaultValue(const json::json_pointer &, const json &, error_handler &) const override + { + return defaultValue_; + } + + void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override final { // depending on the type of instance run the type specific validator - if present auto type = type_[(uint8_t) instance.type()]; if (type) - type->validate(ptr, instance, e); + type->validate(ptr, instance, patch, e); else e.error(ptr, instance, "unexpected instance type"); @@ -381,18 +447,18 @@ class type_schema : public schema e.error(ptr, instance, "instance not const"); for (auto l : logic_) - l->validate(ptr, instance, e); + l->validate(ptr, instance, patch, e); if (if_) { first_error_handler err; - if_->validate(ptr, instance, err); + if_->validate(ptr, instance, patch, err); if (!err) { if (then_) - then_->validate(ptr, instance, e); + then_->validate(ptr, instance, patch, e); } else { if (else_) - else_->validate(ptr, instance, e); + else_->validate(ptr, instance, patch, e); } } } @@ -411,7 +477,6 @@ class type_schema : public schema {"string", json::value_t::string}, {"boolean", json::value_t::boolean}, {"integer", json::value_t::number_integer}, - {"integer", json::value_t::number_unsigned}, {"number", json::value_t::number_float}, }; @@ -445,15 +510,22 @@ class type_schema : public schema sch.erase(attr); } + const auto defaultAttr = sch.find("default"); + if (defaultAttr != sch.end()) { + defaultValue_ = defaultAttr.value(); + } + for (auto &key : known_keywords) sch.erase(key); - // with nlohmann::json floats can be seen as unsigned or integer - reuse the number-validator for - // integer values as well, if they have not been specified + // with nlohmann::json float instance (but number in schema-definition) can be seen as unsigned or integer - + // reuse the number-validator for integer values as well, if they have not been specified explicitly if (type_[(uint8_t) json::value_t::number_float] && !type_[(uint8_t) json::value_t::number_integer]) - type_[(uint8_t) json::value_t::number_integer] = - type_[(uint8_t) json::value_t::number_unsigned] = - type_[(uint8_t) json::value_t::number_float]; + type_[(uint8_t) json::value_t::number_integer] = type_[(uint8_t) json::value_t::number_float]; + + // #54: JSON-schema does not differentiate between unsigned and signed integer - nlohmann::json does + // we stick with JSON-schema: use the integer-validator if instance-value is unsigned + type_[(uint8_t) json::value_t::number_unsigned] = type_[(uint8_t) json::value_t::number_integer]; attr = sch.find("enum"); if (attr != sch.end()) { @@ -535,7 +607,7 @@ class string : public schema return len; } - void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override + void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override { if (minLength_.first) { if (utf8_length(instance) < minLength_.second) { @@ -625,7 +697,7 @@ class numeric : public schema return std::fabs(res) > std::fabs(eps); } - void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override + void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override { T value = instance; // conversion of json to value_type @@ -684,7 +756,7 @@ class numeric : public schema class null : public schema { - void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override + void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override { if (!instance.is_null()) e.error(ptr, instance, "expected to be null"); @@ -697,7 +769,7 @@ class null : public schema class boolean_type : public schema { - void validate(const json::json_pointer &, const json &, error_handler &) const override {} + void validate(const json::json_pointer &, const json &, json_patch &, error_handler &) const override {} public: boolean_type(json &, root_schema *root) @@ -707,7 +779,7 @@ class boolean_type : public schema class boolean : public schema { bool true_; - void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override + void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override { if (!true_) { // false schema // empty array @@ -731,7 +803,7 @@ class required : public schema { const std::vector required_; - void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override final + void validate(const json::json_pointer &ptr, const json &instance, json_patch &, error_handler &e) const override final { for (auto &r : required_) if (instance.find(r) == instance.end()) @@ -759,7 +831,7 @@ class object : public schema std::shared_ptr propertyNames_; - void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override + void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override { if (maxProperties_.first && instance.size() > maxProperties_.second) e.error(ptr, instance, "too many properties"); @@ -774,31 +846,49 @@ class object : public schema // for each property in instance for (auto &p : instance.items()) { if (propertyNames_) - propertyNames_->validate(ptr, p.key(), e); + propertyNames_->validate(ptr, p.key(), patch, e); bool a_prop_or_pattern_matched = false; auto schema_p = properties_.find(p.key()); // check if it is in "properties" if (schema_p != properties_.end()) { a_prop_or_pattern_matched = true; - schema_p->second->validate(ptr / p.key(), p.value(), e); + schema_p->second->validate(ptr / p.key(), p.value(), patch, e); } +#ifndef NO_STD_REGEX // check all matching patternProperties for (auto &schema_pp : patternProperties_) if (REGEX_NAMESPACE::regex_search(p.key(), schema_pp.first)) { a_prop_or_pattern_matched = true; - schema_pp.second->validate(ptr / p.key(), p.value(), e); + schema_pp.second->validate(ptr / p.key(), p.value(), patch, e); } +#endif + // check additionalProperties as a last resort - if (!a_prop_or_pattern_matched && additionalProperties_) - additionalProperties_->validate(ptr / p.key(), p.value(), e); + if (!a_prop_or_pattern_matched && additionalProperties_) { + first_error_handler additional_prop_err; + additionalProperties_->validate(ptr / p.key(), p.value(), patch, additional_prop_err); + if (additional_prop_err) + e.error(ptr, instance, "validation failed for additional property '" + p.key() + "': " + additional_prop_err.message_); + } + } + + // reverse search + for (auto const &prop : properties_) { + const auto finding = instance.find(prop.first); + if (instance.end() == finding) { // if the prop is not in the instance + const auto &defaultValue = prop.second->defaultValue(ptr, instance, e); + if (!defaultValue.empty()) { // if default value is available + patch.add((ptr / prop.first), defaultValue); + } + } } for (auto &dep : dependencies_) { auto prop = instance.find(dep.first); - if (prop != instance.end()) // if dependency-property is present in instance - dep.second->validate(ptr / dep.first, instance, e); // validate + if (prop != instance.end()) // if dependency-property is present in instance + dep.second->validate(ptr / dep.first, instance, patch, e); // validate } } @@ -893,7 +983,7 @@ class array : public schema std::shared_ptr contains_; - void validate(const json::json_pointer &ptr, const json &instance, error_handler &e) const override + void validate(const json::json_pointer &ptr, const json &instance, json_patch &patch, error_handler &e) const override { if (maxItems_.first && instance.size() > maxItems_.second) e.error(ptr, instance, "array has too many items"); @@ -912,7 +1002,7 @@ class array : public schema size_t index = 0; if (items_schema_) for (auto &i : instance) { - items_schema_->validate(ptr / index, i, e); + items_schema_->validate(ptr / index, i, patch, e); index++; } else { @@ -929,7 +1019,7 @@ class array : public schema if (!item_validator) break; - item_validator->validate(ptr / index, i, e); + item_validator->validate(ptr / index, i, patch, e); } } @@ -937,7 +1027,7 @@ class array : public schema bool contained = false; for (auto &item : instance) { first_error_handler local_e; - contains_->validate(ptr, item, local_e); + contains_->validate(ptr, item, patch, local_e); if (!local_e) { contained = true; break; @@ -1008,8 +1098,8 @@ std::shared_ptr type_schema::make(json &schema, switch (type) { case json::value_t::null: return std::make_shared(schema, root); + case json::value_t::number_unsigned: - return std::make_shared>(schema, root, kw); case json::value_t::number_integer: return std::make_shared>(schema, root, kw); case json::value_t::number_float: @@ -1029,6 +1119,7 @@ std::shared_ptr type_schema::make(json &schema, return nullptr; } } // namespace + namespace { @@ -1037,6 +1128,13 @@ std::shared_ptr schema::make(json &schema, const std::vector &keys, std::vector uris) { + // remove URIs which contain plain name identifiers, as sub-schemas cannot be referenced + for (auto uri = uris.begin(); uri != uris.end();) + if (uri->identifier() != "") + uri = uris.erase(uri); + else + uri++; + // append to all URIs the keys for this sub-schema for (auto &key : keys) for (auto &uri : uris) @@ -1082,15 +1180,15 @@ std::shared_ptr schema::make(json &schema, schema.erase("title"); schema.erase("description"); } else { - return nullptr; // TODO error/throw? when schema is invalid + throw std::invalid_argument("invalid JSON-type for a schema for " + uris[0].to_string() + ", expected: boolean or object"); } - for (auto &uri : uris) { // for all URI references this schema + for (auto &uri : uris) { // for all URIs this schema is referenced by root->insert(uri, sch); if (schema.type() == json::value_t::object) for (auto &u : schema.items()) - root->insert_unknown_keyword(uri, u.key(), u.value()); + root->insert_unknown_keyword(uri, u.key(), u.value()); // insert unknown keywords for later reference } return sch; } @@ -1112,8 +1210,20 @@ namespace json_schema json_validator::json_validator(schema_loader loader, format_checker format) - : root_(std::unique_ptr(new root_schema(loader, format))) + : root_(std::unique_ptr(new root_schema(std::move(loader), std::move(format)))) +{ +} + +json_validator::json_validator(const json &schema, schema_loader loader, format_checker format) + : json_validator(std::move(loader), std::move(format)) { + set_root_schema(schema); +} + +json_validator::json_validator(json &&schema, schema_loader loader, format_checker format) + : json_validator(std::move(loader), std::move(format)) +{ + set_root_schema(std::move(schema)); } // move constructor, destructor and move assignment operator can be defaulted here @@ -1127,16 +1237,23 @@ void json_validator::set_root_schema(const json &schema) root_->set_root_schema(schema); } -void json_validator::validate(const json &instance) const +void json_validator::set_root_schema(json &&schema) +{ + root_->set_root_schema(std::move(schema)); +} + +json json_validator::validate(const json &instance) const { throwing_error_handler err; - validate(instance, err); + return validate(instance, err); } -void json_validator::validate(const json &instance, error_handler &err) const +json json_validator::validate(const json &instance, error_handler &err) const { json::json_pointer ptr; - root_->validate(ptr, instance, err); + json_patch patch; + root_->validate(ptr, instance, patch, err); + return patch; } } // namespace json_schema diff --git a/Development/third_party/nlohmann/json.hpp b/Development/third_party/nlohmann/json.hpp index a2feec0ff..a70aaf8cb 100644 --- a/Development/third_party/nlohmann/json.hpp +++ b/Development/third_party/nlohmann/json.hpp @@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.6.1 +| | |__ | | | | | | version 3.9.1 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License . @@ -31,12 +31,10 @@ SOFTWARE. #define INCLUDE_NLOHMANN_JSON_HPP_ #define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 6 +#define NLOHMANN_JSON_VERSION_MINOR 9 #define NLOHMANN_JSON_VERSION_PATCH 1 #include // all_of, find, for_each -#include // assert -#include // and, not, or #include // nullptr_t, ptrdiff_t, size_t #include // hash, less #include // initializer_list @@ -58,7 +56,6 @@ SOFTWARE. #include // transform #include // array -#include // and, not #include // forward_list #include // inserter, front_inserter, end #include // map @@ -105,6909 +102,10008 @@ struct position_t } // namespace detail } // namespace nlohmann +// #include -namespace nlohmann -{ -namespace detail -{ -//////////////// -// exceptions // -//////////////// -/*! -@brief general exception of the @ref basic_json class +#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(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 13) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 13 -This class is an extension of `std::exception` objects with a member @a id for -exception ids. It is used as the base class for all exceptions thrown by the -@ref basic_json class. This class can hence be used as "wildcard" to catch -exceptions. +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x -Subclasses: -- @ref parse_error for exceptions indicating a parse error -- @ref invalid_iterator for exceptions indicating errors with iterators -- @ref type_error for exceptions indicating executing a member function with - a wrong type -- @ref out_of_range for exceptions indicating access out of the defined range -- @ref other_error for exceptions indicating other library errors +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) -@internal -@note To have nothrow-copy-constructible exceptions, we internally use - `std::runtime_error` which can cope with arbitrary-length error messages. - Intermediate strings are built with static functions and then passed to - the actual constructor. -@endinternal +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b -@liveexample{The following code shows how arbitrary library exceptions can be -caught.,exception} +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) -@since version 3.0.0 -*/ -class exception : public std::exception -{ - public: - /// returns the explanatory string - const char* what() const noexcept override - { - return m.what(); - } +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c - /// the id of the exception - const int id; +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) - protected: - exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) - static std::string name(const std::string& ename, int id_) - { - return "[json.exception." + ename + "." + std::to_string(id_) + "] "; - } +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) - private: - /// an exception object as storage for error messages - std::runtime_error m; -}; +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) -/*! -@brief exception indicating a parse error +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) -This exception is thrown by the library when a parse error occurs. Parse errors -can occur during the deserialization of JSON text, CBOR, MessagePack, as well -as when using JSON Patch. +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif -Member @a byte holds the byte index of the last read character in the input -file. +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif -Exceptions have ids 1xx. +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif -name / id | example message | description ------------------------------- | --------------- | ------------------------- -json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. -json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. -json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. -json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. -json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. -json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. -json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. -json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. -json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. -json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. -json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. -json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. -json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(_MSC_VER) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif -@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). +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif -@liveexample{The following code shows how a `parse_error` exception can be -caught.,parse_error} +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif -@sa - @ref exception for the base class of the library exceptions -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa - @ref out_of_range for exceptions indicating access out of the defined range -@sa - @ref other_error for exceptions indicating other library errors +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif -@since version 3.0.0 -*/ -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 - */ - static parse_error create(int id_, const position_t& pos, const std::string& what_arg) - { - std::string w = exception::name("parse_error", id_) + "parse error" + - position_string(pos) + ": " + what_arg; - return parse_error(id_, pos.chars_read_total, w.c_str()); - } +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif - static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) - { - std::string w = exception::name("parse_error", id_) + "parse error" + - (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + - ": " + what_arg; - return parse_error(id_, byte_, w.c_str()); - } +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION 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 JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION 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 JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif - /*! - @brief byte index of the parse error +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif - The byte index of the last read character in the input file. +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif - @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; +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif - private: - parse_error(int id_, std::size_t byte_, const char* what_arg) - : exception(id_, what_arg), byte(byte_) {} +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif - 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); - } -}; +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif -/*! -@brief exception indicating errors with iterators +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif -This exception is thrown if iterators passed to a library function do not match -the expected semantics. +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif -Exceptions have ids 2xx. +#if defined(JSON_HEDLEY_TI_VERSION) + #undef 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 JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif -name / id | example message | description ------------------------------------ | --------------- | ------------------------- -json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. -json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. -json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. -json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. -json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. -json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. -json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. -json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. -json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. -json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. -json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. -json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. -json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. -json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif -@liveexample{The following code shows how an `invalid_iterator` exception can be -caught.,invalid_iterator} +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa - @ref out_of_range for exceptions indicating access out of the defined range -@sa - @ref other_error for exceptions indicating other library errors +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif -@since version 3.0.0 -*/ -class invalid_iterator : public exception -{ - public: - static invalid_iterator create(int id_, const std::string& what_arg) - { - std::string w = exception::name("invalid_iterator", id_) + what_arg; - return invalid_iterator(id_, w.c_str()); - } +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif - private: - invalid_iterator(int id_, const char* what_arg) - : exception(id_, what_arg) {} -}; +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif -/*! -@brief exception indicating executing a member function with a wrong type +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif -This exception is thrown in case of a type error; that is, a library function is -executed on a JSON value whose type does not match the expected semantics. +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif -Exceptions have ids 3xx. +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif -name / id | example message | description ------------------------------ | --------------- | ------------------------- -json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. -json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. -json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. -json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. -json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. -json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. -json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. -json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. -json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. -json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. -json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. -json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. -json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. -json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. -json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. -json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | -json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif -@liveexample{The following code shows how a `type_error` exception can be -caught.,type_error} +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref out_of_range for exceptions indicating access out of the defined range -@sa - @ref other_error for exceptions indicating other library errors +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif -@since version 3.0.0 -*/ -class type_error : public exception -{ - public: - static type_error create(int id_, const std::string& what_arg) - { - std::string w = exception::name("type_error", id_) + what_arg; - return type_error(id_, w.c_str()); - } +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif - private: - type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif -/*! -@brief exception indicating access out of the defined range +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif -This exception is thrown in case a library function is called on an input -parameter that exceeds the expected range, for instance in case of array -indices or nonexisting object keys. +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif -Exceptions have ids 4xx. +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0) + #endif +#endif -name / id | example message | description -------------------------------- | --------------- | ------------------------- -json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. -json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. -json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. -json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. -json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. -json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. -json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. | -json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | -json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif -@liveexample{The following code shows how an `out_of_range` exception can be -caught.,out_of_range} +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa - @ref other_error for exceptions indicating other library errors +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif -@since version 3.0.0 -*/ -class out_of_range : public exception -{ - public: - static out_of_range create(int id_, const std::string& what_arg) - { - std::string w = exception::name("out_of_range", id_) + what_arg; - return out_of_range(id_, w.c_str()); - } +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif - private: - out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif -/*! -@brief exception indicating other library errors +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif -This exception is thrown in case of errors that cannot be classified with the -other exception types. +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif -Exceptions have ids 5xx. +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif -name / id | example message | description ------------------------------- | --------------- | ------------------------- -json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif -@sa - @ref exception for the base class of the library exceptions -@sa - @ref parse_error for exceptions indicating a parse error -@sa - @ref invalid_iterator for exceptions indicating errors with iterators -@sa - @ref type_error for exceptions indicating executing a member function with - a wrong type -@sa - @ref out_of_range for exceptions indicating access out of the defined range +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif -@liveexample{The following code shows how an `other_error` exception can be -caught.,other_error} +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif -@since version 3.0.0 -*/ -class other_error : public exception -{ - public: - static other_error create(int id_, const std::string& what_arg) - { - std::string w = exception::name("other_error", id_) + what_arg; - return other_error(id_, w.c_str()); - } +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif - private: - other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; -} // namespace detail -} // namespace nlohmann +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif -// #include +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif -#include // pair +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif -// This file contains all internal macro definitions -// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif -// 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 +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #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" +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) #endif -// disable documentation warnings on clang -#if defined(__clang__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wdocumentation" +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #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) +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) #else - #define JSON_DEPRECATED + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #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 +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) #else - #define JSON_NODISCARD + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) #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) +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) #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) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif -// override exception macros -#if defined(JSON_THROW_USER) - #undef JSON_THROW - #define JSON_THROW JSON_THROW_USER +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE #endif -#if defined(JSON_TRY_USER) - #undef JSON_TRY - #define JSON_TRY JSON_TRY_USER +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #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 + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION #endif -#if defined(JSON_INTERNAL_CATCH_USER) - #undef JSON_INTERNAL_CATCH - #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) #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) +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) #else - #define JSON_LIKELY(x) x - #define JSON_UNLIKELY(x) x + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #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 +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #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. +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif -#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ - template class ObjectType, \ - template class ArrayType, \ - class StringType, class BooleanType, class NumberIntegerType, \ - class NumberUnsignedType, class NumberFloatType, \ - template class AllocatorType, \ - template class JSONSerializer> +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif -#define NLOHMANN_BASIC_JSON_TPL \ - basic_json +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif -// #include +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif -#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 +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif -namespace nlohmann -{ -namespace detail -{ -// alias templates to reduce boilerplate -template -using enable_if_t = typename std::enable_if::type; +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif -template -using uncvref_t = typename std::remove_cv::type>::type; +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif -// implementation of C++14 index_sequence and affiliates -// source: https://stackoverflow.com/a/32223343 -template -struct index_sequence -{ - using type = index_sequence; - using value_type = std::size_t; - static constexpr std::size_t size() noexcept - { - return sizeof...(Ints); - } -}; +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif -template -struct merge_and_renumber; +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif -template -struct merge_and_renumber, index_sequence> - : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP \ +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif -template -struct make_index_sequence - : merge_and_renumber < typename make_index_sequence < N / 2 >::type, - typename make_index_sequence < N - N / 2 >::type > {}; +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif -template<> struct make_index_sequence<0> : index_sequence<> {}; -template<> struct make_index_sequence<1> : index_sequence<0> {}; +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif -template -using index_sequence_for = make_index_sequence; +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif -// dispatch utility (taken from ranges-v3) -template struct priority_tag : priority_tag < N - 1 > {}; -template<> struct priority_tag<0> {}; +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif -// taken from ranges-v3 -template -struct static_const -{ - static constexpr T value{}; -}; +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif -template -constexpr T static_const::value; -} // namespace detail -} // namespace nlohmann +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif -// #include +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif -#include // not -#include // numeric_limits -#include // false_type, is_constructible, is_integral, is_same, true_type -#include // declval +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif -// #include +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif -#include // random_access_iterator_tag +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif -// #include +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP -namespace nlohmann -{ -namespace detail -{ -template struct make_void -{ - using type = void; -}; -template using void_t = typename make_void::type; -} // namespace detail -} // namespace nlohmann +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif -// #include +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define 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 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 JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif -namespace nlohmann -{ -namespace detail -{ -template -struct iterator_types {}; +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif -template -struct iterator_types < - It, - void_t> -{ - using difference_type = typename It::difference_type; - using value_type = typename It::value_type; - using pointer = typename It::pointer; - using reference = typename It::reference; - using iterator_category = typename It::iterator_category; -}; +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif -// This is required as some compilers implement std::iterator_traits in a way that -// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. -template -struct iterator_traits -{ -}; +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif -template -struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> - : iterator_types -{ -}; +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif -template -struct iterator_traits::value>> -{ - using iterator_category = std::random_access_iterator_tag; - using value_type = T; - using difference_type = ptrdiff_t; - using pointer = T*; - using reference = T&; -}; -} // namespace detail -} // namespace nlohmann +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif -// #include +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif -// #include +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif -// #include +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif -#include +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif -// #include +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif -// http://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; -}; +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define 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(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif -template class Op, - class... Args> -struct detector -{ - using value_t = std::false_type; - using type = Default; -}; +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif -template class Op, class... Args> -struct detector>, Op, Args...> -{ - using value_t = std::true_type; - using type = Op; -}; +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + JSON_HEDLEY_HAS_FEATURE(c_static_assert) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif -template