From dfc1980cb08adf0bda16eb470d4a63694e4dddb1 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Mon, 6 Apr 2026 09:31:31 -0400 Subject: [PATCH] Upgrade Core to `b6640a840bf149edca223c9129a71d64474e12fc` Signed-off-by: Juan Cruz Viotti --- DEPENDENCIES | 2 +- vendor/core/src/core/json/json_value.cc | 17 +- vendor/core/src/core/jsonschema/frame.cc | 18 +- vendor/core/src/core/jsonschema/jsonschema.cc | 56 +++-- .../src/core/jsonschema/known_resolver.in.cc | 208 +++++++++++++----- .../sourcemeta/core/uritemplate_router.h | 31 +-- .../core/uritemplate/uritemplate_router.cc | 49 ++++- .../uritemplate/uritemplate_router_view.cc | 81 +++++-- .../src/extension/alterschema/CMakeLists.txt | 2 + .../src/extension/alterschema/alterschema.cc | 4 + .../draft_official_dialect_with_https.h | 76 +++++++ .../modern_official_dialect_with_http.h | 49 +++++ vendor/core/src/lang/numeric/decimal.cc | 11 + .../include/sourcemeta/core/numeric_decimal.h | 4 + 14 files changed, 483 insertions(+), 125 deletions(-) create mode 100644 vendor/core/src/extension/alterschema/common/draft_official_dialect_with_https.h create mode 100644 vendor/core/src/extension/alterschema/common/modern_official_dialect_with_http.h diff --git a/DEPENDENCIES b/DEPENDENCIES index f053cc8..b554af3 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,2 +1,2 @@ vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02 -core https://github.com/sourcemeta/core 57e8c91ed68e3ee903526fd2f45cb16ca46759d8 +core https://github.com/sourcemeta/core b6640a840bf149edca223c9129a71d64474e12fc diff --git a/vendor/core/src/core/json/json_value.cc b/vendor/core/src/core/json/json_value.cc index 0feab58..916884a 100644 --- a/vendor/core/src/core/json/json_value.cc +++ b/vendor/core/src/core/json/json_value.cc @@ -543,8 +543,13 @@ auto JSON::operator-=(const JSON &substractive) -> JSON & { const auto division{dividend_value / divisor_value}; Real integral = 0; - return !std::isinf(division) && !std::isnan(division) && - std::modf(division, &integral) == 0.0; + if (!std::isinf(division) && !std::isnan(division) && + std::modf(division, &integral) == 0.0) { + return true; + } + + return Decimal::strict_from(dividend_value) + .divisible_by(Decimal::strict_from(divisor_value)); } if (this->is_decimal() && divisor.is_decimal()) { @@ -557,8 +562,8 @@ auto JSON::operator-=(const JSON &substractive) -> JSON & { return this->to_decimal().divisible_by(divisor_decimal); } - const Decimal divisor_decimal{divisor.to_real()}; - return this->to_decimal().divisible_by(divisor_decimal); + return this->to_decimal().divisible_by( + Decimal::strict_from(divisor.to_real())); } if (this->is_integer()) { @@ -566,8 +571,8 @@ auto JSON::operator-=(const JSON &substractive) -> JSON & { return dividend_decimal.divisible_by(divisor.to_decimal()); } - const Decimal dividend_decimal{this->to_real()}; - return dividend_decimal.divisible_by(divisor.to_decimal()); + return Decimal::strict_from(this->to_real()) + .divisible_by(divisor.to_decimal()); } [[nodiscard]] auto diff --git a/vendor/core/src/core/jsonschema/frame.cc b/vendor/core/src/core/jsonschema/frame.cc index f6f18b8..ed10ce0 100644 --- a/vendor/core/src/core/jsonschema/frame.cc +++ b/vendor/core/src/core/jsonschema/frame.cc @@ -352,15 +352,17 @@ auto SchemaFrame::to_json( root.at("locations").assign_assume_new("dynamic", JSON::make_object()); for (const auto &location : this->locations_) { auto entry{JSON::make_object()}; - entry.assign_assume_new("parent", - sourcemeta::core::to_json(location.second.parent)); + entry.assign_assume_new("parent", location.second.parent.has_value() + ? JSON{sourcemeta::core::to_string( + location.second.parent.value())} + : JSON{nullptr}); entry.assign_assume_new("type", sourcemeta::core::to_json(location.second.type)); entry.assign_assume_new("root", this->root_.empty() ? JSON{nullptr} : JSON{this->root_}); entry.assign_assume_new("base", JSON{JSON::String{location.second.base}}); - entry.assign_assume_new("pointer", - sourcemeta::core::to_json(location.second.pointer)); + entry.assign_assume_new( + "pointer", JSON{sourcemeta::core::to_string(location.second.pointer)}); if (tracker.has_value()) { entry.assign_assume_new("position", sourcemeta::core::to_json(tracker.value().get( @@ -371,8 +373,8 @@ auto SchemaFrame::to_json( entry.assign_assume_new( "relativePointer", - sourcemeta::core::to_json( - this->relative_instance_location(location.second))); + JSON{sourcemeta::core::to_string( + this->relative_instance_location(location.second))}); entry.assign_assume_new("dialect", JSON{JSON::String{location.second.dialect}}); entry.assign_assume_new( @@ -403,8 +405,8 @@ auto SchemaFrame::to_json( auto entry{JSON::make_object()}; entry.assign_assume_new("type", sourcemeta::core::to_json(reference.first.first)); - entry.assign_assume_new("origin", - sourcemeta::core::to_json(reference.first.second)); + entry.assign_assume_new( + "origin", JSON{sourcemeta::core::to_string(reference.first.second)}); if (tracker.has_value()) { entry.assign_assume_new("position", diff --git a/vendor/core/src/core/jsonschema/jsonschema.cc b/vendor/core/src/core/jsonschema/jsonschema.cc index e2b3006..2b084f3 100644 --- a/vendor/core/src/core/jsonschema/jsonschema.cc +++ b/vendor/core/src/core/jsonschema/jsonschema.cc @@ -66,37 +66,54 @@ auto sourcemeta::core::to_string(const SchemaBaseDialect base_dialect) auto sourcemeta::core::to_base_dialect(const std::string_view base_dialect) -> std::optional { - if (base_dialect == "https://json-schema.org/draft/2020-12/schema") { + if (base_dialect == "https://json-schema.org/draft/2020-12/schema" || + base_dialect == "http://json-schema.org/draft/2020-12/schema") { return SchemaBaseDialect::JSON_Schema_2020_12; } else if (base_dialect == - "https://json-schema.org/draft/2020-12/hyper-schema") { + "https://json-schema.org/draft/2020-12/hyper-schema" || + base_dialect == + "http://json-schema.org/draft/2020-12/hyper-schema") { return SchemaBaseDialect::JSON_Schema_2020_12_Hyper; - } else if (base_dialect == "https://json-schema.org/draft/2019-09/schema") { + } else if (base_dialect == "https://json-schema.org/draft/2019-09/schema" || + base_dialect == "http://json-schema.org/draft/2019-09/schema") { return SchemaBaseDialect::JSON_Schema_2019_09; } else if (base_dialect == - "https://json-schema.org/draft/2019-09/hyper-schema") { + "https://json-schema.org/draft/2019-09/hyper-schema" || + base_dialect == + "http://json-schema.org/draft/2019-09/hyper-schema") { return SchemaBaseDialect::JSON_Schema_2019_09_Hyper; - } else if (base_dialect == "http://json-schema.org/draft-07/schema#") { + } else if (base_dialect == "http://json-schema.org/draft-07/schema#" || + base_dialect == "https://json-schema.org/draft-07/schema#") { return SchemaBaseDialect::JSON_Schema_Draft_7; - } else if (base_dialect == "http://json-schema.org/draft-07/hyper-schema#") { + } else if (base_dialect == "http://json-schema.org/draft-07/hyper-schema#" || + base_dialect == "https://json-schema.org/draft-07/hyper-schema#") { return SchemaBaseDialect::JSON_Schema_Draft_7_Hyper; - } else if (base_dialect == "http://json-schema.org/draft-06/schema#") { + } else if (base_dialect == "http://json-schema.org/draft-06/schema#" || + base_dialect == "https://json-schema.org/draft-06/schema#") { return SchemaBaseDialect::JSON_Schema_Draft_6; - } else if (base_dialect == "http://json-schema.org/draft-06/hyper-schema#") { + } else if (base_dialect == "http://json-schema.org/draft-06/hyper-schema#" || + base_dialect == "https://json-schema.org/draft-06/hyper-schema#") { return SchemaBaseDialect::JSON_Schema_Draft_6_Hyper; - } else if (base_dialect == "http://json-schema.org/draft-04/schema#") { + } else if (base_dialect == "http://json-schema.org/draft-04/schema#" || + base_dialect == "https://json-schema.org/draft-04/schema#") { return SchemaBaseDialect::JSON_Schema_Draft_4; - } else if (base_dialect == "http://json-schema.org/draft-04/hyper-schema#") { + } else if (base_dialect == "http://json-schema.org/draft-04/hyper-schema#" || + base_dialect == "https://json-schema.org/draft-04/hyper-schema#") { return SchemaBaseDialect::JSON_Schema_Draft_4_Hyper; - } else if (base_dialect == "http://json-schema.org/draft-03/schema#") { + } else if (base_dialect == "http://json-schema.org/draft-03/schema#" || + base_dialect == "https://json-schema.org/draft-03/schema#") { return SchemaBaseDialect::JSON_Schema_Draft_3; - } else if (base_dialect == "http://json-schema.org/draft-03/hyper-schema#") { + } else if (base_dialect == "http://json-schema.org/draft-03/hyper-schema#" || + base_dialect == "https://json-schema.org/draft-03/hyper-schema#") { return SchemaBaseDialect::JSON_Schema_Draft_3_Hyper; - } else if (base_dialect == "http://json-schema.org/draft-02/hyper-schema#") { + } else if (base_dialect == "http://json-schema.org/draft-02/hyper-schema#" || + base_dialect == "https://json-schema.org/draft-02/hyper-schema#") { return SchemaBaseDialect::JSON_Schema_Draft_2_Hyper; - } else if (base_dialect == "http://json-schema.org/draft-01/hyper-schema#") { + } else if (base_dialect == "http://json-schema.org/draft-01/hyper-schema#" || + base_dialect == "https://json-schema.org/draft-01/hyper-schema#") { return SchemaBaseDialect::JSON_Schema_Draft_1_Hyper; - } else if (base_dialect == "http://json-schema.org/draft-00/hyper-schema#") { + } else if (base_dialect == "http://json-schema.org/draft-00/hyper-schema#" || + base_dialect == "https://json-schema.org/draft-00/hyper-schema#") { return SchemaBaseDialect::JSON_Schema_Draft_0_Hyper; } @@ -517,9 +534,9 @@ auto sourcemeta::core::vocabularies(const SchemaResolver &resolver, std::string_view dialect) -> sourcemeta::core::Vocabularies { const auto base_dialect_string{to_string(base_dialect)}; - // As a performance optimization shortcut - if (base_dialect_string == dialect) { + if (base_dialect_string == dialect || + to_base_dialect(dialect) == base_dialect) { if (base_dialect == SchemaBaseDialect::JSON_Schema_2020_12) { return Vocabularies{ {Vocabularies::Known::JSON_Schema_2020_12_Core, true}, @@ -584,10 +601,7 @@ auto sourcemeta::core::vocabularies(const SchemaResolver &resolver, // At this point we are sure that the dialect is vocabulary aware and the // identifier keyword is indeed `$id`, so we can avoid the added // complexity of the generic `id` function. - assert(schema_dialect.defines("$id") && - schema_dialect.at("$id").is_string() && - URI::canonicalize(schema_dialect.at("$id").to_string()) == - URI::canonicalize(dialect)); + assert(schema_dialect.defines("$id") && schema_dialect.at("$id").is_string()); /* * (4) Retrieve the vocabularies explicitly or implicitly declared by the diff --git a/vendor/core/src/core/jsonschema/known_resolver.in.cc b/vendor/core/src/core/jsonschema/known_resolver.in.cc index a8a17c9..a2c1d48 100644 --- a/vendor/core/src/core/jsonschema/known_resolver.in.cc +++ b/vendor/core/src/core/jsonschema/known_resolver.in.cc @@ -75,173 +75,275 @@ enum class KnownSchema : std::uint8_t { static auto parse_identifier(const std::string_view identifier) -> KnownSchema { // JSON Schema 2020-12 if (identifier == "https://json-schema.org/draft/2020-12/schema" || - identifier == "https://json-schema.org/draft/2020-12/schema#") { + identifier == "https://json-schema.org/draft/2020-12/schema#" || + identifier == "http://json-schema.org/draft/2020-12/schema" || + identifier == "http://json-schema.org/draft/2020-12/schema#") { return KnownSchema::JSONSCHEMA_2020_12; } else if (identifier == "https://json-schema.org/draft/2020-12/hyper-schema" || identifier == - "https://json-schema.org/draft/2020-12/hyper-schema#") { + "https://json-schema.org/draft/2020-12/hyper-schema#" || + identifier == + "http://json-schema.org/draft/2020-12/hyper-schema" || + identifier == + "http://json-schema.org/draft/2020-12/hyper-schema#") { return KnownSchema::HYPERSCHEMA_2020_12; } else if (identifier == - "https://json-schema.org/draft/2020-12/meta/applicator") { + "https://json-schema.org/draft/2020-12/meta/applicator" || + identifier == + "http://json-schema.org/draft/2020-12/meta/applicator") { return KnownSchema::JSONSCHEMA_2020_12_APPLICATOR; } else if (identifier == - "https://json-schema.org/draft/2020-12/meta/content") { + "https://json-schema.org/draft/2020-12/meta/content" || + identifier == + "http://json-schema.org/draft/2020-12/meta/content") { return KnownSchema::JSONSCHEMA_2020_12_CONTENT; - } else if (identifier == "https://json-schema.org/draft/2020-12/meta/core") { + } else if (identifier == "https://json-schema.org/draft/2020-12/meta/core" || + identifier == "http://json-schema.org/draft/2020-12/meta/core") { return KnownSchema::JSONSCHEMA_2020_12_CORE; - } else if (identifier == - "https://json-schema.org/draft/2020-12/meta/format-annotation") { + } else if ( + identifier == + "https://json-schema.org/draft/2020-12/meta/format-annotation" || + identifier == + "http://json-schema.org/draft/2020-12/meta/format-annotation") { return KnownSchema::JSONSCHEMA_2020_12_FORMAT_ANNOTATION; - } else if (identifier == - "https://json-schema.org/draft/2020-12/meta/format-assertion") { + } else if (identifier == "https://json-schema.org/draft/2020-12/meta/" + "format-assertion" || + identifier == + "http://json-schema.org/draft/2020-12/meta/format-assertion") { return KnownSchema::JSONSCHEMA_2020_12_FORMAT_ASSERTION; } else if (identifier == - "https://json-schema.org/draft/2020-12/meta/hyper-schema") { + "https://json-schema.org/draft/2020-12/meta/hyper-schema" || + identifier == + "http://json-schema.org/draft/2020-12/meta/hyper-schema") { return KnownSchema::JSONSCHEMA_2020_12_HYPER_SCHEMA; } else if (identifier == - "https://json-schema.org/draft/2020-12/meta/meta-data") { + "https://json-schema.org/draft/2020-12/meta/meta-data" || + identifier == + "http://json-schema.org/draft/2020-12/meta/meta-data") { return KnownSchema::JSONSCHEMA_2020_12_META_DATA; } else if (identifier == - "https://json-schema.org/draft/2020-12/meta/unevaluated") { + "https://json-schema.org/draft/2020-12/meta/unevaluated" || + identifier == + "http://json-schema.org/draft/2020-12/meta/unevaluated") { return KnownSchema::JSONSCHEMA_2020_12_UNEVALUATED; } else if (identifier == - "https://json-schema.org/draft/2020-12/meta/validation") { + "https://json-schema.org/draft/2020-12/meta/validation" || + identifier == + "http://json-schema.org/draft/2020-12/meta/validation") { return KnownSchema::JSONSCHEMA_2020_12_VALIDATION; - } else if (identifier == "https://json-schema.org/draft/2020-12/links") { + } else if (identifier == "https://json-schema.org/draft/2020-12/links" || + identifier == "http://json-schema.org/draft/2020-12/links") { return KnownSchema::LINKS_2020_12; } else if (identifier == - "https://json-schema.org/draft/2020-12/output/schema") { + "https://json-schema.org/draft/2020-12/output/schema" || + identifier == + "http://json-schema.org/draft/2020-12/output/schema") { return KnownSchema::JSONSCHEMA_2020_12_OUTPUT; // JSON Schema 2019-09 } else if (identifier == "https://json-schema.org/draft/2019-09/schema" || - identifier == "https://json-schema.org/draft/2019-09/schema#") { + identifier == "https://json-schema.org/draft/2019-09/schema#" || + identifier == "http://json-schema.org/draft/2019-09/schema" || + identifier == "http://json-schema.org/draft/2019-09/schema#") { return KnownSchema::JSONSCHEMA_2019_09; } else if (identifier == "https://json-schema.org/draft/2019-09/hyper-schema" || identifier == - "https://json-schema.org/draft/2019-09/hyper-schema#") { + "https://json-schema.org/draft/2019-09/hyper-schema#" || + identifier == + "http://json-schema.org/draft/2019-09/hyper-schema" || + identifier == + "http://json-schema.org/draft/2019-09/hyper-schema#") { return KnownSchema::HYPERSCHEMA_2019_09; } else if (identifier == - "https://json-schema.org/draft/2019-09/meta/applicator") { + "https://json-schema.org/draft/2019-09/meta/applicator" || + identifier == + "http://json-schema.org/draft/2019-09/meta/applicator") { return KnownSchema::JSONSCHEMA_2019_09_APPLICATOR; } else if (identifier == - "https://json-schema.org/draft/2019-09/meta/content") { + "https://json-schema.org/draft/2019-09/meta/content" || + identifier == + "http://json-schema.org/draft/2019-09/meta/content") { return KnownSchema::JSONSCHEMA_2019_09_CONTENT; - } else if (identifier == "https://json-schema.org/draft/2019-09/meta/core") { + } else if (identifier == "https://json-schema.org/draft/2019-09/meta/core" || + identifier == "http://json-schema.org/draft/2019-09/meta/core") { return KnownSchema::JSONSCHEMA_2019_09_CORE; } else if (identifier == - "https://json-schema.org/draft/2019-09/meta/format") { + "https://json-schema.org/draft/2019-09/meta/format" || + identifier == "http://json-schema.org/draft/2019-09/meta/format") { return KnownSchema::JSONSCHEMA_2019_09_FORMAT; } else if (identifier == - "https://json-schema.org/draft/2019-09/meta/hyper-schema") { + "https://json-schema.org/draft/2019-09/meta/hyper-schema" || + identifier == + "http://json-schema.org/draft/2019-09/meta/hyper-schema") { return KnownSchema::JSONSCHEMA_2019_09_HYPER_SCHEMA; } else if (identifier == - "https://json-schema.org/draft/2019-09/meta/meta-data") { + "https://json-schema.org/draft/2019-09/meta/meta-data" || + identifier == + "http://json-schema.org/draft/2019-09/meta/meta-data") { return KnownSchema::JSONSCHEMA_2019_09_META_DATA; } else if (identifier == - "https://json-schema.org/draft/2019-09/meta/validation") { + "https://json-schema.org/draft/2019-09/meta/validation" || + identifier == + "http://json-schema.org/draft/2019-09/meta/validation") { return KnownSchema::JSONSCHEMA_2019_09_VALIDATION; - } else if (identifier == "https://json-schema.org/draft/2019-09/links") { + } else if (identifier == "https://json-schema.org/draft/2019-09/links" || + identifier == "http://json-schema.org/draft/2019-09/links") { return KnownSchema::LINKS_2019_09; } else if (identifier == - "https://json-schema.org/draft/2019-09/output/schema") { + "https://json-schema.org/draft/2019-09/output/schema" || + identifier == + "http://json-schema.org/draft/2019-09/output/schema") { return KnownSchema::JSONSCHEMA_2019_09_OUTPUT; } else if (identifier == - "https://json-schema.org/draft/2019-09/output/hyper-schema") { + "https://json-schema.org/draft/2019-09/output/hyper-schema" || + identifier == + "http://json-schema.org/draft/2019-09/output/hyper-schema") { return KnownSchema::HYPERSCHEMA_2019_09_OUTPUT; // JSON Schema Draft7 } else if (identifier == "http://json-schema.org/draft-07/schema#" || - identifier == "http://json-schema.org/draft-07/schema") { + identifier == "http://json-schema.org/draft-07/schema" || + identifier == "https://json-schema.org/draft-07/schema#" || + identifier == "https://json-schema.org/draft-07/schema") { return KnownSchema::JSONSCHEMA_DRAFT7; } else if (identifier == "http://json-schema.org/draft-07/hyper-schema#" || - identifier == "http://json-schema.org/draft-07/hyper-schema") { + identifier == "http://json-schema.org/draft-07/hyper-schema" || + identifier == "https://json-schema.org/draft-07/hyper-schema#" || + identifier == "https://json-schema.org/draft-07/hyper-schema") { return KnownSchema::HYPERSCHEMA_DRAFT7; } else if (identifier == "http://json-schema.org/draft-07/links#" || - identifier == "http://json-schema.org/draft-07/links") { + identifier == "http://json-schema.org/draft-07/links" || + identifier == "https://json-schema.org/draft-07/links#" || + identifier == "https://json-schema.org/draft-07/links") { return KnownSchema::LINKS_DRAFT7; } else if (identifier == - "http://json-schema.org/draft-07/hyper-schema-output") { + "http://json-schema.org/draft-07/hyper-schema-output" || + identifier == + "https://json-schema.org/draft-07/hyper-schema-output") { return KnownSchema::HYPERSCHEMA_DRAFT7_OUTPUT; // JSON Schema Draft6 } else if (identifier == "http://json-schema.org/draft-06/schema#" || - identifier == "http://json-schema.org/draft-06/schema") { + identifier == "http://json-schema.org/draft-06/schema" || + identifier == "https://json-schema.org/draft-06/schema#" || + identifier == "https://json-schema.org/draft-06/schema") { return KnownSchema::JSONSCHEMA_DRAFT6; } else if (identifier == "http://json-schema.org/draft-06/hyper-schema#" || - identifier == "http://json-schema.org/draft-06/hyper-schema") { + identifier == "http://json-schema.org/draft-06/hyper-schema" || + identifier == "https://json-schema.org/draft-06/hyper-schema#" || + identifier == "https://json-schema.org/draft-06/hyper-schema") { return KnownSchema::HYPERSCHEMA_DRAFT6; } else if (identifier == "http://json-schema.org/draft-06/links#" || - identifier == "http://json-schema.org/draft-06/links") { + identifier == "http://json-schema.org/draft-06/links" || + identifier == "https://json-schema.org/draft-06/links#" || + identifier == "https://json-schema.org/draft-06/links") { return KnownSchema::LINKS_DRAFT6; // JSON Schema Draft4 } else if (identifier == "http://json-schema.org/draft-04/schema#" || - identifier == "http://json-schema.org/draft-04/schema") { + identifier == "http://json-schema.org/draft-04/schema" || + identifier == "https://json-schema.org/draft-04/schema#" || + identifier == "https://json-schema.org/draft-04/schema") { return KnownSchema::JSONSCHEMA_DRAFT4; } else if (identifier == "http://json-schema.org/draft-04/hyper-schema#" || - identifier == "http://json-schema.org/draft-04/hyper-schema") { + identifier == "http://json-schema.org/draft-04/hyper-schema" || + identifier == "https://json-schema.org/draft-04/hyper-schema#" || + identifier == "https://json-schema.org/draft-04/hyper-schema") { return KnownSchema::HYPERSCHEMA_DRAFT4; } else if (identifier == "http://json-schema.org/draft-04/links#" || - identifier == "http://json-schema.org/draft-04/links") { + identifier == "http://json-schema.org/draft-04/links" || + identifier == "https://json-schema.org/draft-04/links#" || + identifier == "https://json-schema.org/draft-04/links") { return KnownSchema::LINKS_DRAFT4; // JSON Schema Draft3 } else if (identifier == "http://json-schema.org/draft-03/schema#" || - identifier == "http://json-schema.org/draft-03/schema") { + identifier == "http://json-schema.org/draft-03/schema" || + identifier == "https://json-schema.org/draft-03/schema#" || + identifier == "https://json-schema.org/draft-03/schema") { return KnownSchema::JSONSCHEMA_DRAFT3; } else if (identifier == "http://json-schema.org/draft-03/hyper-schema#" || - identifier == "http://json-schema.org/draft-03/hyper-schema") { + identifier == "http://json-schema.org/draft-03/hyper-schema" || + identifier == "https://json-schema.org/draft-03/hyper-schema#" || + identifier == "https://json-schema.org/draft-03/hyper-schema") { return KnownSchema::HYPERSCHEMA_DRAFT3; } else if (identifier == "http://json-schema.org/draft-03/links#" || - identifier == "http://json-schema.org/draft-03/links") { + identifier == "http://json-schema.org/draft-03/links" || + identifier == "https://json-schema.org/draft-03/links#" || + identifier == "https://json-schema.org/draft-03/links") { return KnownSchema::LINKS_DRAFT3; } else if (identifier == "http://json-schema.org/draft-03/json-ref#" || - identifier == "http://json-schema.org/draft-03/json-ref") { + identifier == "http://json-schema.org/draft-03/json-ref" || + identifier == "https://json-schema.org/draft-03/json-ref#" || + identifier == "https://json-schema.org/draft-03/json-ref") { return KnownSchema::JSON_REF_DRAFT3; // JSON Schema Draft2 } else if (identifier == "http://json-schema.org/draft-02/schema#" || - identifier == "http://json-schema.org/draft-02/schema") { + identifier == "http://json-schema.org/draft-02/schema" || + identifier == "https://json-schema.org/draft-02/schema#" || + identifier == "https://json-schema.org/draft-02/schema") { return KnownSchema::JSONSCHEMA_DRAFT2; } else if (identifier == "http://json-schema.org/draft-02/hyper-schema#" || - identifier == "http://json-schema.org/draft-02/hyper-schema") { + identifier == "http://json-schema.org/draft-02/hyper-schema" || + identifier == "https://json-schema.org/draft-02/hyper-schema#" || + identifier == "https://json-schema.org/draft-02/hyper-schema") { return KnownSchema::HYPERSCHEMA_DRAFT2; } else if (identifier == "http://json-schema.org/draft-02/links#" || - identifier == "http://json-schema.org/draft-02/links") { + identifier == "http://json-schema.org/draft-02/links" || + identifier == "https://json-schema.org/draft-02/links#" || + identifier == "https://json-schema.org/draft-02/links") { return KnownSchema::LINKS_DRAFT2; } else if (identifier == "http://json-schema.org/draft-02/json-ref#" || - identifier == "http://json-schema.org/draft-02/json-ref") { + identifier == "http://json-schema.org/draft-02/json-ref" || + identifier == "https://json-schema.org/draft-02/json-ref#" || + identifier == "https://json-schema.org/draft-02/json-ref") { return KnownSchema::JSON_REF_DRAFT2; // JSON Schema Draft1 } else if (identifier == "http://json-schema.org/draft-01/schema#" || - identifier == "http://json-schema.org/draft-01/schema") { + identifier == "http://json-schema.org/draft-01/schema" || + identifier == "https://json-schema.org/draft-01/schema#" || + identifier == "https://json-schema.org/draft-01/schema") { return KnownSchema::JSONSCHEMA_DRAFT1; } else if (identifier == "http://json-schema.org/draft-01/hyper-schema#" || - identifier == "http://json-schema.org/draft-01/hyper-schema") { + identifier == "http://json-schema.org/draft-01/hyper-schema" || + identifier == "https://json-schema.org/draft-01/hyper-schema#" || + identifier == "https://json-schema.org/draft-01/hyper-schema") { return KnownSchema::HYPERSCHEMA_DRAFT1; } else if (identifier == "http://json-schema.org/draft-01/links#" || - identifier == "http://json-schema.org/draft-01/links") { + identifier == "http://json-schema.org/draft-01/links" || + identifier == "https://json-schema.org/draft-01/links#" || + identifier == "https://json-schema.org/draft-01/links") { return KnownSchema::LINKS_DRAFT1; } else if (identifier == "http://json-schema.org/draft-01/json-ref#" || - identifier == "http://json-schema.org/draft-01/json-ref") { + identifier == "http://json-schema.org/draft-01/json-ref" || + identifier == "https://json-schema.org/draft-01/json-ref#" || + identifier == "https://json-schema.org/draft-01/json-ref") { return KnownSchema::JSON_REF_DRAFT1; // JSON Schema Draft0 } else if (identifier == "http://json-schema.org/draft-00/schema#" || - identifier == "http://json-schema.org/draft-00/schema") { + identifier == "http://json-schema.org/draft-00/schema" || + identifier == "https://json-schema.org/draft-00/schema#" || + identifier == "https://json-schema.org/draft-00/schema") { return KnownSchema::JSONSCHEMA_DRAFT0; } else if (identifier == "http://json-schema.org/draft-00/hyper-schema#" || - identifier == "http://json-schema.org/draft-00/hyper-schema") { + identifier == "http://json-schema.org/draft-00/hyper-schema" || + identifier == "https://json-schema.org/draft-00/hyper-schema#" || + identifier == "https://json-schema.org/draft-00/hyper-schema") { return KnownSchema::HYPERSCHEMA_DRAFT0; } else if (identifier == "http://json-schema.org/draft-00/links#" || - identifier == "http://json-schema.org/draft-00/links") { + identifier == "http://json-schema.org/draft-00/links" || + identifier == "https://json-schema.org/draft-00/links#" || + identifier == "https://json-schema.org/draft-00/links") { return KnownSchema::LINKS_DRAFT0; } else if (identifier == "http://json-schema.org/draft-00/json-ref#" || - identifier == "http://json-schema.org/draft-00/json-ref") { + identifier == "http://json-schema.org/draft-00/json-ref" || + identifier == "https://json-schema.org/draft-00/json-ref#" || + identifier == "https://json-schema.org/draft-00/json-ref") { return KnownSchema::JSON_REF_DRAFT0; // OpenAPI v3.2 diff --git a/vendor/core/src/core/uritemplate/include/sourcemeta/core/uritemplate_router.h b/vendor/core/src/core/uritemplate/include/sourcemeta/core/uritemplate_router.h index 649af74..f140a26 100644 --- a/vendor/core/src/core/uritemplate/include/sourcemeta/core/uritemplate_router.h +++ b/vendor/core/src/core/uritemplate/include/sourcemeta/core/uritemplate_router.h @@ -11,6 +11,7 @@ #include // std::function #include // std::unique_ptr #include // std::span +#include // std::string #include // std::string_view #include // std::pair #include // std::variant @@ -68,6 +69,10 @@ class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplateRouter { /// Construct an empty router URITemplateRouter() = default; + /// Construct a router with a base path prefix. During matching, the base + /// path is stripped from incoming request paths before matching + explicit URITemplateRouter(std::string_view base_path); + // To avoid mistakes URITemplateRouter(const URITemplateRouter &) = delete; URITemplateRouter(URITemplateRouter &&) = delete; @@ -95,8 +100,12 @@ class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplateRouter { [[nodiscard]] auto arguments() const noexcept -> const std::vector>> &; + /// Access the base path prefix + [[nodiscard]] auto base_path() const noexcept -> std::string_view; + private: Node root_; + std::string base_path_; std::vector>> arguments_; }; @@ -104,25 +113,6 @@ class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplateRouter { /// A read-only view of a serialized URI Template router class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplateRouterView { public: - /// A serialized node in the binary format -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4324) -#endif - struct alignas(8) Node { - std::uint32_t string_offset; - std::uint32_t string_length; - std::uint32_t first_literal_child; - std::uint32_t literal_child_count; - std::uint32_t variable_child; - URITemplateRouter::NodeType type; - std::uint8_t padding; - URITemplateRouter::Identifier identifier; - }; -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - /// Save a router to a binary file static auto save(const URITemplateRouter &router, const std::filesystem::path &path) -> void; @@ -148,6 +138,9 @@ class SOURCEMETA_CORE_URITEMPLATE_EXPORT URITemplateRouterView { const URITemplateRouter::ArgumentCallback &callback) const -> void; + /// Access the base path prefix + [[nodiscard]] auto base_path() const noexcept -> std::string_view; + private: std::vector data_; }; diff --git a/vendor/core/src/core/uritemplate/uritemplate_router.cc b/vendor/core/src/core/uritemplate/uritemplate_router.cc index aa464af..4f6bfc9 100644 --- a/vendor/core/src/core/uritemplate/uritemplate_router.cc +++ b/vendor/core/src/core/uritemplate/uritemplate_router.cc @@ -94,13 +94,53 @@ inline auto extract_segment(const char *start, const char *end) } // namespace +URITemplateRouter::URITemplateRouter(const std::string_view base_path) + : base_path_{base_path} { + assert(this->base_path_.empty() || this->base_path_.front() == '/'); + const auto last = this->base_path_.find_last_not_of('/'); + if (last == std::string::npos) { + this->base_path_.clear(); + } else { + this->base_path_.erase(last + 1); + } +} + +auto URITemplateRouter::base_path() const noexcept -> std::string_view { + return this->base_path_; +} + auto URITemplateRouter::add(const std::string_view uri_template, const Identifier identifier, const std::span arguments) -> void { assert(identifier > 0); + // Walk base path segments to establish the trie prefix + Node *current = nullptr; + if (!this->base_path_.empty()) { + const char *base_position = this->base_path_.data(); + const char *const base_end = base_position + this->base_path_.size(); + while (base_position < base_end) { + while (base_position < base_end && *base_position == '/') { + ++base_position; + } + if (base_position >= base_end) { + break; + } + const char *segment_start = base_position; + while (base_position < base_end && *base_position != '/') { + ++base_position; + } + const std::string_view segment{ + segment_start, + static_cast(base_position - segment_start)}; + auto &literals = current ? current->literals : this->root_.literals; + current = &find_or_create_literal_child(literals, segment); + } + } + if (uri_template.empty()) { - this->root_.identifier = identifier; + auto &target = current ? *current : this->root_; + target.identifier = identifier; if (!arguments.empty()) { assert(std::ranges::none_of(this->arguments_, [&identifier](const auto &entry) { @@ -114,7 +154,7 @@ auto URITemplateRouter::add(const std::string_view uri_template, return; } - Node *current = nullptr; + Node *base_path_end = current; bool absorbed = false; const char *position = uri_template.data(); const char *const end = position + uri_template.size(); @@ -261,9 +301,10 @@ auto URITemplateRouter::add(const std::string_view uri_template, } } - if (current == nullptr && uri_template.size() == 1 && + if (current == base_path_end && uri_template.size() == 1 && uri_template[0] == '/') { - current = &find_or_create_literal_child(this->root_.literals, ""); + auto &literals = current ? current->literals : this->root_.literals; + current = &find_or_create_literal_child(literals, ""); } if (!absorbed && current != nullptr) { diff --git a/vendor/core/src/core/uritemplate/uritemplate_router_view.cc b/vendor/core/src/core/uritemplate/uritemplate_router_view.cc index 2a2b876..c3481f2 100644 --- a/vendor/core/src/core/uritemplate/uritemplate_router_view.cc +++ b/vendor/core/src/core/uritemplate/uritemplate_router_view.cc @@ -14,7 +14,7 @@ namespace sourcemeta::core { namespace { constexpr std::uint32_t ROUTER_MAGIC = 0x52544552; // "RTER" -constexpr std::uint32_t ROUTER_VERSION = 2; +constexpr std::uint32_t ROUTER_VERSION = 3; constexpr std::uint32_t NO_CHILD = std::numeric_limits::max(); // Type tags for argument value serialization @@ -28,6 +28,8 @@ struct RouterHeader { std::uint32_t node_count; std::uint32_t string_table_offset; std::uint32_t arguments_offset; + std::uint32_t base_path_offset; + std::uint32_t base_path_length; }; struct ArgumentEntryHeader { @@ -36,9 +38,20 @@ struct ArgumentEntryHeader { std::uint32_t blob_length; }; +struct alignas(8) SerializedNode { + std::uint32_t string_offset; + std::uint32_t string_length; + std::uint32_t first_literal_child; + std::uint32_t literal_child_count; + std::uint32_t variable_child; + URITemplateRouter::NodeType type; + std::uint8_t padding; + URITemplateRouter::Identifier identifier; +}; + // Binary search for a literal child matching the given segment inline auto binary_search_literal_children( - const URITemplateRouterView::Node *nodes, const char *string_table, + const SerializedNode *nodes, const char *string_table, const std::size_t string_table_size, const std::uint32_t first_child, const std::uint32_t child_count, const char *segment, const std::uint32_t segment_length) noexcept -> std::uint32_t { @@ -82,7 +95,7 @@ inline auto binary_search_literal_children( auto URITemplateRouterView::save(const URITemplateRouter &router, const std::filesystem::path &path) -> void { - std::vector nodes; + std::vector nodes; std::string string_table; std::queue queue; std::unordered_map @@ -90,7 +103,7 @@ auto URITemplateRouterView::save(const URITemplateRouter &router, const auto &root = router.root(); - Node root_serialized{}; + SerializedNode root_serialized{}; root_serialized.string_offset = 0; root_serialized.string_length = 0; root_serialized.type = URITemplateRouter::NodeType::Root; @@ -125,7 +138,7 @@ auto URITemplateRouterView::save(const URITemplateRouter &router, const auto *node = queue.front(); queue.pop(); - Node serialized{}; + SerializedNode serialized{}; serialized.string_offset = static_cast(string_table.size()); serialized.type = node->type; serialized.string_length = static_cast(node->value.size()); @@ -224,14 +237,22 @@ auto URITemplateRouterView::save(const URITemplateRouter &router, argument_entries.push_back(entry); } + // Append the base path to the string table + const auto base_path_string_offset = + static_cast(string_table.size()); + const auto base_path_value = router.base_path(); + string_table.append(base_path_value.data(), base_path_value.size()); + RouterHeader header{}; header.magic = ROUTER_MAGIC; header.version = ROUTER_VERSION; header.node_count = static_cast(nodes.size()); header.string_table_offset = static_cast( - sizeof(RouterHeader) + nodes.size() * sizeof(Node)); + sizeof(RouterHeader) + nodes.size() * sizeof(SerializedNode)); header.arguments_offset = static_cast( header.string_table_offset + string_table.size()); + header.base_path_offset = base_path_string_offset; + header.base_path_length = static_cast(base_path_value.size()); std::ofstream file(path, std::ios::binary); if (!file) { @@ -239,8 +260,9 @@ auto URITemplateRouterView::save(const URITemplateRouter &router, } file.write(reinterpret_cast(&header), sizeof(header)); - file.write(reinterpret_cast(nodes.data()), - static_cast(nodes.size() * sizeof(Node))); + file.write( + reinterpret_cast(nodes.data()), + static_cast(nodes.size() * sizeof(SerializedNode))); file.write(string_table.data(), static_cast(string_table.size())); @@ -306,15 +328,15 @@ auto URITemplateRouterView::match(const std::string_view path, } if (header->node_count == 0 || - header->node_count > - (this->data_.size() - sizeof(RouterHeader)) / sizeof(Node)) { + header->node_count > (this->data_.size() - sizeof(RouterHeader)) / + sizeof(SerializedNode)) { return 0; } - const auto *nodes = - reinterpret_cast(this->data_.data() + sizeof(RouterHeader)); + const auto *nodes = reinterpret_cast( + this->data_.data() + sizeof(RouterHeader)); const auto nodes_size = - static_cast(header->node_count) * sizeof(Node); + static_cast(header->node_count) * sizeof(SerializedNode); const auto expected_string_table_offset = sizeof(RouterHeader) + nodes_size; if (header->string_table_offset < expected_string_table_offset || header->string_table_offset > this->data_.size()) { @@ -599,4 +621,37 @@ auto URITemplateRouterView::arguments( } } +auto URITemplateRouterView::base_path() const noexcept -> std::string_view { + if (this->data_.size() < sizeof(RouterHeader)) { + return {}; + } + + const auto *header = + reinterpret_cast(this->data_.data()); + if (header->magic != ROUTER_MAGIC || header->version != ROUTER_VERSION) { + return {}; + } + + if (header->base_path_length == 0) { + return {}; + } + + if (header->string_table_offset > this->data_.size() || + header->arguments_offset < header->string_table_offset || + header->arguments_offset > this->data_.size()) { + return {}; + } + + const auto *string_table = reinterpret_cast( + this->data_.data() + header->string_table_offset); + const auto string_table_size = + header->arguments_offset - header->string_table_offset; + if (header->base_path_offset > string_table_size || + header->base_path_length > string_table_size - header->base_path_offset) { + return {}; + } + + return {string_table + header->base_path_offset, header->base_path_length}; +} + } // namespace sourcemeta::core diff --git a/vendor/core/src/extension/alterschema/CMakeLists.txt b/vendor/core/src/extension/alterschema/CMakeLists.txt index 43353c9..5fe9f4e 100644 --- a/vendor/core/src/extension/alterschema/CMakeLists.txt +++ b/vendor/core/src/extension/alterschema/CMakeLists.txt @@ -31,6 +31,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME alterschema common/content_schema_without_media_type.h common/dependencies_property_tautology.h common/dependent_required_tautology.h + common/draft_official_dialect_with_https.h common/draft_official_dialect_without_empty_fragment.h common/draft_ref_siblings.h common/drop_allof_empty_schemas.h @@ -52,6 +53,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME alterschema common/min_contains_without_contains.h common/minimum_real_for_integer.h common/modern_official_dialect_with_empty_fragment.h + common/modern_official_dialect_with_http.h common/non_applicable_additional_items.h common/non_applicable_enum_validation_keywords.h common/non_applicable_type_specific_keywords.h diff --git a/vendor/core/src/extension/alterschema/alterschema.cc b/vendor/core/src/extension/alterschema/alterschema.cc index 31be5c3..a6e9744 100644 --- a/vendor/core/src/extension/alterschema/alterschema.cc +++ b/vendor/core/src/extension/alterschema/alterschema.cc @@ -59,6 +59,7 @@ inline auto APPLIES_TO_POINTERS(std::vector &&keywords) #include "common/content_schema_without_media_type.h" #include "common/dependencies_property_tautology.h" #include "common/dependent_required_tautology.h" +#include "common/draft_official_dialect_with_https.h" #include "common/draft_official_dialect_without_empty_fragment.h" #include "common/draft_ref_siblings.h" #include "common/drop_allof_empty_schemas.h" @@ -80,6 +81,7 @@ inline auto APPLIES_TO_POINTERS(std::vector &&keywords) #include "common/min_contains_without_contains.h" #include "common/minimum_real_for_integer.h" #include "common/modern_official_dialect_with_empty_fragment.h" +#include "common/modern_official_dialect_with_http.h" #include "common/non_applicable_additional_items.h" #include "common/non_applicable_enum_validation_keywords.h" #include "common/non_applicable_type_specific_keywords.h" @@ -149,6 +151,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) -> void { bundle.add(); bundle.add(); + bundle.add(); bundle.add(); bundle.add(); bundle.add(); @@ -184,6 +187,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) -> void { bundle.add(); bundle.add(); bundle.add(); + bundle.add(); bundle.add(); bundle.add(); bundle.add(); diff --git a/vendor/core/src/extension/alterschema/common/draft_official_dialect_with_https.h b/vendor/core/src/extension/alterschema/common/draft_official_dialect_with_https.h new file mode 100644 index 0000000..d9e83d9 --- /dev/null +++ b/vendor/core/src/extension/alterschema/common/draft_official_dialect_with_https.h @@ -0,0 +1,76 @@ +class DraftOfficialDialectWithHttps final : public SchemaTransformRule { +public: + using mutates = std::true_type; + using reframe_after_transform = std::true_type; + DraftOfficialDialectWithHttps() + : SchemaTransformRule{ + "draft_official_dialect_with_https", + "The official dialect URI of Draft 7 and older must use " + "\"http://\" instead of \"https://\""} {}; + + [[nodiscard]] auto + condition(const sourcemeta::core::JSON &schema, + const sourcemeta::core::JSON &, + const sourcemeta::core::Vocabularies &, + const sourcemeta::core::SchemaFrame &, + const sourcemeta::core::SchemaFrame::Location &location, + const sourcemeta::core::SchemaWalker &, + const sourcemeta::core::SchemaResolver &) const + -> sourcemeta::core::SchemaTransformRule::Result override { + using sourcemeta::core::SchemaBaseDialect; + ONLY_CONTINUE_IF( + location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_7 || + location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_7_Hyper || + location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_6 || + location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_6_Hyper || + location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_4 || + location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_4_Hyper || + location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_3 || + location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_3_Hyper || + location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_2_Hyper || + location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_1_Hyper || + location.base_dialect == SchemaBaseDialect::JSON_Schema_Draft_0_Hyper); + ONLY_CONTINUE_IF(schema.is_object() && schema.defines("$schema") && + schema.at("$schema").is_string()); + const auto &dialect{schema.at("$schema").to_string()}; + ONLY_CONTINUE_IF(dialect.starts_with("https://json-schema.org/")); + ONLY_CONTINUE_IF( + dialect == "https://json-schema.org/draft-07/schema" || + dialect == "https://json-schema.org/draft-07/schema#" || + dialect == "https://json-schema.org/draft-07/hyper-schema" || + dialect == "https://json-schema.org/draft-07/hyper-schema#" || + dialect == "https://json-schema.org/draft-06/schema" || + dialect == "https://json-schema.org/draft-06/schema#" || + dialect == "https://json-schema.org/draft-06/hyper-schema" || + dialect == "https://json-schema.org/draft-06/hyper-schema#" || + dialect == "https://json-schema.org/draft-04/schema" || + dialect == "https://json-schema.org/draft-04/schema#" || + dialect == "https://json-schema.org/draft-04/hyper-schema" || + dialect == "https://json-schema.org/draft-04/hyper-schema#" || + dialect == "https://json-schema.org/draft-03/schema" || + dialect == "https://json-schema.org/draft-03/schema#" || + dialect == "https://json-schema.org/draft-03/hyper-schema" || + dialect == "https://json-schema.org/draft-03/hyper-schema#" || + dialect == "https://json-schema.org/draft-02/schema" || + dialect == "https://json-schema.org/draft-02/schema#" || + dialect == "https://json-schema.org/draft-02/hyper-schema" || + dialect == "https://json-schema.org/draft-02/hyper-schema#" || + dialect == "https://json-schema.org/draft-01/schema" || + dialect == "https://json-schema.org/draft-01/schema#" || + dialect == "https://json-schema.org/draft-01/hyper-schema" || + dialect == "https://json-schema.org/draft-01/hyper-schema#" || + dialect == "https://json-schema.org/draft-00/schema" || + dialect == "https://json-schema.org/draft-00/schema#" || + dialect == "https://json-schema.org/draft-00/hyper-schema" || + dialect == "https://json-schema.org/draft-00/hyper-schema#"); + return APPLIES_TO_KEYWORDS("$schema"); + } + + auto transform(sourcemeta::core::JSON &schema, const Result &) const + -> void override { + const auto &old_dialect{schema.at("$schema").to_string()}; + std::string new_dialect{"http://"}; + new_dialect += old_dialect.substr(8); + schema.at("$schema").into(sourcemeta::core::JSON{new_dialect}); + } +}; diff --git a/vendor/core/src/extension/alterschema/common/modern_official_dialect_with_http.h b/vendor/core/src/extension/alterschema/common/modern_official_dialect_with_http.h new file mode 100644 index 0000000..cca26b7 --- /dev/null +++ b/vendor/core/src/extension/alterschema/common/modern_official_dialect_with_http.h @@ -0,0 +1,49 @@ +class ModernOfficialDialectWithHttp final : public SchemaTransformRule { +public: + using mutates = std::true_type; + using reframe_after_transform = std::true_type; + ModernOfficialDialectWithHttp() + : SchemaTransformRule{ + "modern_official_dialect_with_http", + "The official dialect URI of 2019-09 and later must use " + "\"https://\" instead of \"http://\""} {}; + + [[nodiscard]] auto + condition(const sourcemeta::core::JSON &schema, + const sourcemeta::core::JSON &, + const sourcemeta::core::Vocabularies &, + const sourcemeta::core::SchemaFrame &, + const sourcemeta::core::SchemaFrame::Location &location, + const sourcemeta::core::SchemaWalker &, + const sourcemeta::core::SchemaResolver &) const + -> sourcemeta::core::SchemaTransformRule::Result override { + using sourcemeta::core::SchemaBaseDialect; + ONLY_CONTINUE_IF( + location.base_dialect == SchemaBaseDialect::JSON_Schema_2020_12 || + location.base_dialect == SchemaBaseDialect::JSON_Schema_2020_12_Hyper || + location.base_dialect == SchemaBaseDialect::JSON_Schema_2019_09 || + location.base_dialect == SchemaBaseDialect::JSON_Schema_2019_09_Hyper); + ONLY_CONTINUE_IF(schema.is_object() && schema.defines("$schema") && + schema.at("$schema").is_string()); + const auto &dialect{schema.at("$schema").to_string()}; + ONLY_CONTINUE_IF(dialect.starts_with("http://json-schema.org/")); + ONLY_CONTINUE_IF( + dialect == "http://json-schema.org/draft/2020-12/schema" || + dialect == "http://json-schema.org/draft/2020-12/schema#" || + dialect == "http://json-schema.org/draft/2020-12/hyper-schema" || + dialect == "http://json-schema.org/draft/2020-12/hyper-schema#" || + dialect == "http://json-schema.org/draft/2019-09/schema" || + dialect == "http://json-schema.org/draft/2019-09/schema#" || + dialect == "http://json-schema.org/draft/2019-09/hyper-schema" || + dialect == "http://json-schema.org/draft/2019-09/hyper-schema#"); + return APPLIES_TO_KEYWORDS("$schema"); + } + + auto transform(sourcemeta::core::JSON &schema, const Result &) const + -> void override { + const auto &old_dialect{schema.at("$schema").to_string()}; + std::string new_dialect{"https://"}; + new_dialect += old_dialect.substr(7); + schema.at("$schema").into(sourcemeta::core::JSON{new_dialect}); + } +}; diff --git a/vendor/core/src/lang/numeric/decimal.cc b/vendor/core/src/lang/numeric/decimal.cc index 2eeb8cf..6db7c0a 100644 --- a/vendor/core/src/lang/numeric/decimal.cc +++ b/vendor/core/src/lang/numeric/decimal.cc @@ -5,7 +5,9 @@ #include // std::array #include // assert +#include // std::to_chars #include // std::isfinite +#include // std::size_t #include // std::strlen #include // std::setprecision #include // std::numeric_limits @@ -506,6 +508,15 @@ auto Decimal::negative_infinity() -> Decimal { return result; } +auto Decimal::strict_from(const double value) -> Decimal { + std::array buffer{}; + const auto result{ + std::to_chars(buffer.data(), buffer.data() + buffer.size(), value)}; + assert(result.ec == std::errc{}); + return Decimal{std::string_view{ + buffer.data(), static_cast(result.ptr - buffer.data())}}; +} + auto Decimal::to_scientific_string() const -> std::string { std::string result; diff --git a/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric_decimal.h b/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric_decimal.h index b07f83c..aefc78d 100644 --- a/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric_decimal.h +++ b/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric_decimal.h @@ -77,6 +77,10 @@ class SOURCEMETA_CORE_NUMERIC_EXPORT Decimal { /// Create a signaling NaN value with an optional payload [[nodiscard]] static auto snan(std::uint64_t payload = 0) -> Decimal; + /// Create a decimal from a double by converting through its shortest + /// round-trip string representation, avoiding IEEE 754 precision artifacts + [[nodiscard]] static auto strict_from(double value) -> Decimal; + /// Create a positive infinity value [[nodiscard]] static auto infinity() -> Decimal;