From 71bfedc563787d421b0298fcad7a44c5ea2f6f55 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Fri, 20 Feb 2026 13:17:28 -0400 Subject: [PATCH] Upgrade Core to `ad6f5dfdda34daaf1ee5f2a4c587c73628885307` Signed-off-by: Juan Cruz Viotti --- DEPENDENCIES | 2 +- vendor/core/src/core/jsonschema/bundle.cc | 72 +++- vendor/core/src/lang/numeric/decimal.cc | 374 ++++++++++++++++++ .../include/sourcemeta/core/numeric_decimal.h | 18 + 4 files changed, 450 insertions(+), 16 deletions(-) diff --git a/DEPENDENCIES b/DEPENDENCIES index 032d7c6e7..b3cb3ef1e 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,3 +1,3 @@ vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02 -core https://github.com/sourcemeta/core 554cce43fba49ed1ded207257d8efeb27da0dff6 +core https://github.com/sourcemeta/core 9eb79e6d10f9a141b214b29867f602bd3ec6202a jsonschema-test-suite https://github.com/json-schema-org/JSON-Schema-Test-Suite a247442bca7a4798ab6187e035200feacd5423ed diff --git a/vendor/core/src/core/jsonschema/bundle.cc b/vendor/core/src/core/jsonschema/bundle.cc index ac9784aa9..46c697c50 100644 --- a/vendor/core/src/core/jsonschema/bundle.cc +++ b/vendor/core/src/core/jsonschema/bundle.cc @@ -4,6 +4,7 @@ #include // std::reference_wrapper #include // std::ostringstream #include // std::tuple +#include // std::unordered_map #include // std::unordered_set #include // std::move #include // std::vector @@ -132,13 +133,14 @@ auto embed_schema(sourcemeta::core::JSON &root, auto bundle_schema(sourcemeta::core::JSON &root, const sourcemeta::core::Pointer &container, - const sourcemeta::core::JSON &subschema, + sourcemeta::core::JSON &subschema, const sourcemeta::core::SchemaWalker &walker, const sourcemeta::core::SchemaResolver &resolver, std::string_view default_dialect, std::string_view default_id, const sourcemeta::core::SchemaFrame::Paths &paths, - std::unordered_set &bundled, + std::unordered_map &bundled, const std::size_t depth = 0) -> void { // Create a fresh frame for each schema we analyze to avoid key collisions // between different schemas that have references at the same pointer paths @@ -153,6 +155,12 @@ auto bundle_schema(sourcemeta::core::JSON &root, frame.analyse(subschema, walker, resolver, default_dialect, default_id); } + std::vector> + deferred; + std::vector< + std::pair> + ref_rewrites; + frame.for_each_unresolved_reference([&](const auto &pointer, const auto &reference) { // We don't want to bundle official schemas, as we can expect @@ -178,9 +186,18 @@ auto bundle_schema(sourcemeta::core::JSON &root, assert(!reference.base.empty()); const sourcemeta::core::JSON::String identifier{reference.base}; - // Skip if already bundled to avoid infinite loops on circular - // references if (bundled.contains(identifier)) { + const auto &mapped_id{bundled.at(identifier)}; + if (mapped_id != identifier) { + sourcemeta::core::URI rewrite_uri{mapped_id}; + if (reference.fragment.has_value()) { + rewrite_uri.fragment(reference.fragment.value()); + } + + ref_rewrites.emplace_back(sourcemeta::core::to_pointer(pointer), + rewrite_uri.recompose()); + } + return; } @@ -210,6 +227,9 @@ auto bundle_schema(sourcemeta::core::JSON &root, "The JSON document is not a valid JSON Schema"); } + auto remote_id = + sourcemeta::core::identify(remote.value(), resolver, default_dialect); + // If the reference has a fragment, verify it exists in the remote // schema if (reference.fragment.has_value()) { @@ -226,19 +246,40 @@ auto bundle_schema(sourcemeta::core::JSON &root, } } + sourcemeta::core::JSON::String effective_id{ + remote_id.empty() ? sourcemeta::core::JSON::String{identifier} + : sourcemeta::core::JSON::String{remote_id}}; + if (remote.value().is_object()) { - // Always insert an identifier, as a schema might refer to another - // schema using another URI (i.e. due to relying on HTTP - // re-directions, etc) - sourcemeta::core::reidentify(remote.value(), identifier, + sourcemeta::core::reidentify(remote.value(), effective_id, remote_base_dialect.value()); } - bundled.emplace(identifier); - bundle_schema(root, container, remote.value(), walker, resolver, - default_dialect, identifier, paths, bundled, depth + 1); - embed_schema(root, container, identifier, std::move(remote).value()); + if (effective_id != identifier) { + sourcemeta::core::URI rewrite_uri{effective_id}; + if (reference.fragment.has_value()) { + rewrite_uri.fragment(reference.fragment.value()); + } + + ref_rewrites.emplace_back(sourcemeta::core::to_pointer(pointer), + rewrite_uri.recompose()); + } + + bundled.emplace(identifier, effective_id); + bundled.emplace(effective_id, effective_id); + deferred.emplace_back(std::move(remote).value(), std::move(effective_id)); }); + + for (auto &[rewrite_pointer, rewrite_value] : ref_rewrites) { + sourcemeta::core::set(subschema, rewrite_pointer, + sourcemeta::core::JSON{rewrite_value}); + } + + for (auto &[remote, effective_id] : deferred) { + bundle_schema(root, container, remote, walker, resolver, default_dialect, + effective_id, paths, bundled, depth + 1); + embed_schema(root, container, effective_id, std::move(remote)); + } } } // namespace @@ -265,12 +306,13 @@ auto bundle(JSON &schema, const SchemaWalker &walker, // Pre-scan the schema to find any already-embedded schemas and mark them // as bundled to avoid re-embedding them. This includes the root schema itself // and any schemas already embedded within it - std::unordered_set bundled; + std::unordered_map bundled; SchemaFrame initial_frame{SchemaFrame::Mode::Locations}; initial_frame.analyse(schema, walker, resolver, default_dialect, default_id, paths); - initial_frame.for_each_resource_uri( - [&bundled](const auto uri) { bundled.emplace(uri); }); + initial_frame.for_each_resource_uri([&bundled](const auto uri) { + bundled.emplace(JSON::String{uri}, JSON::String{uri}); + }); if (default_container.has_value()) { // This is undefined behavior assert(!default_container.value().empty()); diff --git a/vendor/core/src/lang/numeric/decimal.cc b/vendor/core/src/lang/numeric/decimal.cc index 7c480f713..5c941751e 100644 --- a/vendor/core/src/lang/numeric/decimal.cc +++ b/vendor/core/src/lang/numeric/decimal.cc @@ -927,6 +927,380 @@ auto Decimal::divisible_by(const Decimal &divisor) const -> bool { return remainder.is_zero(); } +auto Decimal::same_quantum(const Decimal &other) const -> bool { + if (this->is_nan() && other.is_nan()) { + return true; + } + + if (this->is_infinite() && other.is_infinite()) { + return true; + } + + if (this->is_nan() || other.is_nan() || this->is_infinite() || + other.is_infinite()) { + return false; + } + + return this->exponent_ == other.exponent_; +} + +auto Decimal::reduce() const -> Decimal { + if (!this->is_finite()) { + return *this; + } + + if (this->is_zero()) { + Decimal result; + if (this->flags_ & FLAG_SIGN) { + result.flags_ = FLAG_SIGN; + } + + return result; + } + + if (this->flags_ & FLAG_BIG) { + auto big = coefficient_as_big(this->coefficient_, this->coefficient_high_, + this->flags_); + auto stripped_count = big.strip_trailing_zeros(); + auto new_exponent = + static_cast(this->exponent_) + stripped_count; + if (new_exponent > std::numeric_limits::max() || + new_exponent < std::numeric_limits::min()) { + throw NumericOverflowError{}; + } + + Decimal result; + bool result_negative = (this->flags_ & FLAG_SIGN) != 0; + store_big_result(result.coefficient_, result.coefficient_high_, + result.flags_, std::move(big), result_negative); + result.exponent_ = static_cast(new_exponent); + return result; + } + + auto coefficient = this->coefficient_; + auto exponent = this->exponent_; + strip_trailing_zeros(coefficient, exponent); + + Decimal result; + result.coefficient_ = coefficient; + result.exponent_ = exponent; + if (this->flags_ & FLAG_SIGN) { + result.flags_ = FLAG_SIGN; + } + + return result; +} + +auto Decimal::logb() const -> Decimal { + if (this->is_nan()) { + return *this; + } + + if (this->is_infinite()) { + return Decimal::infinity(); + } + + if (this->is_zero()) { + throw NumericDivisionByZeroError{}; + } + + std::int64_t digits; + if (this->flags_ & FLAG_BIG) { + auto big = coefficient_as_big(this->coefficient_, this->coefficient_high_, + this->flags_); + digits = static_cast(big.digit_count()); + } else { + digits = static_cast( + digit_count(static_cast(this->coefficient_))); + } + + auto adjusted = digits + this->exponent_ - 1; + return Decimal{adjusted}; +} + +auto Decimal::scale_by(const Decimal &scale) const -> Decimal { + if (this->is_snan()) { + throw NumericInvalidOperationError{}; + } + + if (scale.is_snan()) { + throw NumericInvalidOperationError{}; + } + + if (this->is_qnan()) { + return *this; + } + + if (scale.is_qnan()) { + return scale; + } + + if (!scale.is_finite() || scale.exponent_ != 0) { + throw NumericInvalidOperationError{}; + } + + if (this->is_infinite()) { + return *this; + } + + if (!scale.is_int64()) { + throw NumericOverflowError{}; + } + + auto scale_value = scale.to_int64(); + auto new_exponent = static_cast(this->exponent_) + scale_value; + if (new_exponent > std::numeric_limits::max() || + new_exponent < std::numeric_limits::min()) { + throw NumericOverflowError{}; + } + + Decimal result{*this}; + result.exponent_ = static_cast(new_exponent); + return result; +} + +auto Decimal::compare_total(const Decimal &other) const -> Decimal { + auto classify = [](const Decimal &value) -> int { + if (value.is_qnan()) { + return value.is_signed() ? -6 : 6; + } + + if (value.is_snan()) { + return value.is_signed() ? -5 : 5; + } + + if (value.is_infinite()) { + return value.is_signed() ? -4 : 4; + } + + if (value.is_zero()) { + return value.is_signed() ? -2 : 2; + } + + return value.is_signed() ? -3 : 3; + }; + + auto left_class = classify(*this); + auto right_class = classify(other); + + if (left_class != right_class) { + if (left_class < right_class) { + return Decimal{-1}; + } + + return Decimal{1}; + } + + bool is_negative = left_class < 0; + + if (this->is_nan()) { + auto left_payload = this->nan_payload(); + auto right_payload = other.nan_payload(); + if (left_payload != right_payload) { + bool left_less = left_payload < right_payload; + if (is_negative) { + left_less = !left_less; + } + + return left_less ? Decimal{-1} : Decimal{1}; + } + + return Decimal{0}; + } + + if (this->is_infinite()) { + return Decimal{0}; + } + + if (this->is_zero()) { + if (this->exponent_ != other.exponent_) { + bool left_less = this->exponent_ < other.exponent_; + if (is_negative) { + left_less = !left_less; + } + + return left_less ? Decimal{-1} : Decimal{1}; + } + + return Decimal{0}; + } + + int magnitude_compare; + if ((this->flags_ & FLAG_BIG) || (other.flags_ & FLAG_BIG)) { + auto left_big = coefficient_as_big(this->coefficient_, + this->coefficient_high_, this->flags_); + auto right_big = coefficient_as_big(other.coefficient_, + other.coefficient_high_, other.flags_); + auto left_digit_count = static_cast(left_big.digit_count()); + auto right_digit_count = static_cast(right_big.digit_count()); + auto left_adjusted = this->exponent_ + left_digit_count - 1; + auto right_adjusted = other.exponent_ + right_digit_count - 1; + + if (left_adjusted != right_adjusted) { + magnitude_compare = left_adjusted < right_adjusted ? -1 : 1; + } else { + BigCoefficient::align_exponents(left_big, right_big, this->exponent_, + other.exponent_); + magnitude_compare = left_big.compare(right_big); + } + } else { + if (this->exponent_ == other.exponent_) { + if (this->coefficient_ < other.coefficient_) { + magnitude_compare = -1; + } else if (this->coefficient_ > other.coefficient_) { + magnitude_compare = 1; + } else { + magnitude_compare = 0; + } + } else { + auto left_digits = + digit_count(static_cast(this->coefficient_)); + auto right_digits = + digit_count(static_cast(other.coefficient_)); + auto left_adjusted = + this->exponent_ + static_cast(left_digits) - 1; + auto right_adjusted = + other.exponent_ + static_cast(right_digits) - 1; + + if (left_adjusted != right_adjusted) { + magnitude_compare = left_adjusted < right_adjusted ? -1 : 1; + } else { + auto exponent_difference = this->exponent_ - other.exponent_; + if (exponent_difference > 0) { + if (exponent_difference <= 18) { + auto multiplier = + POWERS_OF_10[static_cast(exponent_difference)]; + auto product = + static_cast(this->coefficient_) * + multiplier; + auto right_128 = + static_cast(other.coefficient_); + if (product < right_128) { + magnitude_compare = -1; + } else if (product > right_128) { + magnitude_compare = 1; + } else { + magnitude_compare = 0; + } + } else { + magnitude_compare = 1; + } + } else { + auto difference = static_cast(-exponent_difference); + if (difference <= 18) { + auto multiplier = POWERS_OF_10[difference]; + auto product = + static_cast(other.coefficient_) * + multiplier; + auto left_128 = + static_cast(this->coefficient_); + if (left_128 < product) { + magnitude_compare = -1; + } else if (left_128 > product) { + magnitude_compare = 1; + } else { + magnitude_compare = 0; + } + } else { + magnitude_compare = -1; + } + } + } + } + } + + if (magnitude_compare != 0) { + if (is_negative) { + magnitude_compare = -magnitude_compare; + } + + return magnitude_compare < 0 ? Decimal{-1} : Decimal{1}; + } + + if (this->exponent_ != other.exponent_) { + bool left_less = this->exponent_ < other.exponent_; + if (is_negative) { + left_less = !left_less; + } + + return left_less ? Decimal{-1} : Decimal{1}; + } + + return Decimal{0}; +} + +auto Decimal::divide_integer(const Decimal &other) const -> Decimal { + if (this->is_snan() || other.is_snan()) { + throw NumericInvalidOperationError{}; + } + + if (this->is_nan()) { + return Decimal::nan(this->nan_payload()); + } + + if (other.is_nan()) { + return Decimal::nan(other.nan_payload()); + } + + if (this->is_infinite() && other.is_infinite()) { + throw NumericInvalidOperationError{}; + } + + bool result_negative = ((this->flags_ ^ other.flags_) & FLAG_SIGN) != 0; + + if (this->is_infinite()) { + Decimal result = + result_negative ? Decimal::negative_infinity() : Decimal::infinity(); + return result; + } + + if (other.is_zero()) { + if (this->is_zero()) { + throw NumericInvalidOperationError{}; + } + + throw NumericDivisionByZeroError{}; + } + + if (other.is_infinite()) { + Decimal result; + if (result_negative) { + result.flags_ = FLAG_SIGN; + } + + return result; + } + + if (this->is_zero()) { + Decimal result; + if (result_negative) { + result.flags_ = FLAG_SIGN; + } + + return result; + } + + auto dividend_big = coefficient_as_big(this->coefficient_, + this->coefficient_high_, this->flags_); + auto divisor_big = coefficient_as_big(other.coefficient_, + other.coefficient_high_, other.flags_); + + BigCoefficient::align_exponents(dividend_big, divisor_big, this->exponent_, + other.exponent_); + + auto [quotient, remainder] = dividend_big.divide_modulo(divisor_big); + + Decimal result; + store_big_result(result.coefficient_, result.coefficient_high_, result.flags_, + std::move(quotient), result_negative); + if (result.is_zero()) { + result.flags_ = result_negative ? FLAG_SIGN : 0; + } + + result.exponent_ = 0; + return result; +} + auto Decimal::operator==(const Decimal &other) const -> bool { if (this->is_nan() || other.is_nan()) { return false; 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 639265820..a55f02e0d 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 @@ -166,6 +166,24 @@ class SOURCEMETA_CORE_NUMERIC_EXPORT Decimal { /// Check if this decimal number is divisible by another [[nodiscard]] auto divisible_by(const Decimal &divisor) const -> bool; + /// Strip trailing zeros from the coefficient + [[nodiscard]] auto reduce() const -> Decimal; + + /// Return the adjusted exponent (floor of base-10 logarithm) + [[nodiscard]] auto logb() const -> Decimal; + + /// Scale the number by a power of 10 + [[nodiscard]] auto scale_by(const Decimal &scale) const -> Decimal; + + /// Check if two numbers have the same quantum (exponent) + [[nodiscard]] auto same_quantum(const Decimal &other) const -> bool; + + /// IEEE 754 total ordering comparison returning -1, 0, or 1 + [[nodiscard]] auto compare_total(const Decimal &other) const -> Decimal; + + /// Integer division (truncate toward zero) + [[nodiscard]] auto divide_integer(const Decimal &other) const -> Decimal; + /// Add another decimal number to this one auto operator+=(const Decimal &other) -> Decimal &;