Skip to content

Commit

Permalink
Avoid copying subschemas in SchemaIteratorEntry
Browse files Browse the repository at this point in the history
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
  • Loading branch information
jviotti committed Feb 5, 2025
1 parent e28f893 commit 125f005
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 122 deletions.
35 changes: 19 additions & 16 deletions src/core/jsonschema/frame.cc
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ auto internal_analyse(const sourcemeta::core::JSON &schema,

// Schema identifier
std::optional<std::string> id{sourcemeta::core::identify(
entry.value, entry.base_dialect.value(),
entry.subschema.get(), entry.base_dialect.value(),
entry.pointer.empty() ? default_id : std::nullopt)};

// Store information
Expand All @@ -278,7 +278,7 @@ auto internal_analyse(const sourcemeta::core::JSON &schema,
supports_id_anchors(entry.common.base_dialect.value()) &&
sourcemeta::core::URI{entry.id.value()}.is_fragment_only();

if ((!entry.common.value.defines("$ref") || !ref_overrides) &&
if ((!entry.common.subschema.get().defines("$ref") || !ref_overrides) &&
// If we are dealing with a pre-2019-09 location independent
// identifier, we ignore it as a traditional identifier and take care
// of it as an anchor
Expand Down Expand Up @@ -323,7 +323,8 @@ auto internal_analyse(const sourcemeta::core::JSON &schema,
}

// Handle metaschema references
const auto maybe_metaschema{sourcemeta::core::dialect(entry.common.value)};
const auto maybe_metaschema{
sourcemeta::core::dialect(entry.common.subschema.get())};
if (maybe_metaschema.has_value()) {
sourcemeta::core::URI metaschema{maybe_metaschema.value()};
const auto nearest_bases{
Expand All @@ -334,7 +335,7 @@ auto internal_analyse(const sourcemeta::core::JSON &schema,

metaschema.canonicalize();
const std::string destination{metaschema.recompose()};
assert(entry.common.value.defines("$schema"));
assert(entry.common.subschema.get().defines("$schema"));
references.insert_or_assign(
{SchemaReferenceType::Static,
entry.common.pointer.concat({"$schema"})},
Expand All @@ -344,8 +345,8 @@ auto internal_analyse(const sourcemeta::core::JSON &schema,
}

// Handle schema anchors
for (const auto &[name, type] :
find_anchors(entry.common.value, entry.common.vocabularies)) {
for (const auto &[name, type] : find_anchors(entry.common.subschema.get(),
entry.common.vocabularies)) {
const auto bases{
find_nearest_bases(base_uris, entry.common.pointer, entry.id)};

Expand Down Expand Up @@ -490,12 +491,13 @@ auto internal_analyse(const sourcemeta::core::JSON &schema,

// Resolve references after all framing was performed
for (const auto &entry : subschema_entries) {
if (entry.common.value.is_object()) {
if (entry.common.subschema.get().is_object()) {
const auto nearest_bases{
find_nearest_bases(base_uris, entry.common.pointer, entry.id)};
if (entry.common.value.defines("$ref")) {
assert(entry.common.value.at("$ref").is_string());
sourcemeta::core::URI ref{entry.common.value.at("$ref").to_string()};
if (entry.common.subschema.get().defines("$ref")) {
assert(entry.common.subschema.get().at("$ref").is_string());
sourcemeta::core::URI ref{
entry.common.subschema.get().at("$ref").to_string()};
if (!nearest_bases.first.empty()) {
ref.try_resolve_from(nearest_bases.first.front());
}
Expand All @@ -511,9 +513,10 @@ auto internal_analyse(const sourcemeta::core::JSON &schema,

if (entry.common.vocabularies.contains(
"https://json-schema.org/draft/2019-09/vocab/core") &&
entry.common.value.defines("$recursiveRef")) {
assert(entry.common.value.at("$recursiveRef").is_string());
const auto &ref{entry.common.value.at("$recursiveRef").to_string()};
entry.common.subschema.get().defines("$recursiveRef")) {
assert(entry.common.subschema.get().at("$recursiveRef").is_string());
const auto &ref{
entry.common.subschema.get().at("$recursiveRef").to_string()};

// The behavior of this keyword is defined only for the value "#".
// Implementations MAY choose to consider other values to be errors.
Expand Down Expand Up @@ -542,10 +545,10 @@ auto internal_analyse(const sourcemeta::core::JSON &schema,

if (entry.common.vocabularies.contains(
"https://json-schema.org/draft/2020-12/vocab/core") &&
entry.common.value.defines("$dynamicRef")) {
assert(entry.common.value.at("$dynamicRef").is_string());
entry.common.subschema.get().defines("$dynamicRef")) {
assert(entry.common.subschema.get().at("$dynamicRef").is_string());
sourcemeta::core::URI ref{
entry.common.value.at("$dynamicRef").to_string()};
entry.common.subschema.get().at("$dynamicRef").to_string()};
if (!nearest_bases.first.empty()) {
ref.resolve_from(nearest_bases.first.front());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#define SOURCEMETA_CORE_JSONSCHEMA_TYPES_H_

#include <cstdint> // std::uint8_t
#include <functional> // std::function
#include <functional> // std::function, std::reference_wrapper
#include <map> // std::map
#include <optional> // std::optional
#include <set> // std::set
Expand Down Expand Up @@ -193,9 +193,7 @@ struct SchemaIteratorEntry {
std::optional<std::string> dialect;
std::map<std::string, bool> vocabularies;
std::optional<std::string> base_dialect;
// TODO: Do we really need a full copy of the JSON value if the client
// can get it through the JSON Pointer if needed?
JSON value;
std::reference_wrapper<const JSON> subschema;
PointerTemplate relative_instance_location;
bool orphan;
};
Expand Down
28 changes: 12 additions & 16 deletions src/core/jsonschema/walker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ auto walk(const sourcemeta::core::Pointer &pointer,
resolver, base_dialect.value(), new_dialect)};

if (type == SchemaWalkerType_t::Deep || level > 0) {
subschemas.push_back({pointer, new_dialect, vocabularies, base_dialect,
subschema, instance_location, orphan});
sourcemeta::core::SchemaIteratorEntry entry{
pointer, new_dialect, vocabularies, base_dialect,
subschema, instance_location, orphan};
subschemas.push_back(std::move(entry));
}

// We can't recurse any further
Expand Down Expand Up @@ -265,13 +267,10 @@ sourcemeta::core::SchemaIterator::SchemaIterator(
// not pass a default, then there is nothing we can do. We know
// the current schema is a subschema, but cannot walk any further.
if (!dialect.has_value()) {
this->subschemas.push_back({pointer,
std::nullopt,
{},
std::nullopt,
schema,
instance_location,
false});
sourcemeta::core::SchemaIteratorEntry entry{
pointer, std::nullopt, {}, std::nullopt,
schema, instance_location, false};
this->subschemas.push_back(std::move(entry));
} else {
walk(pointer, instance_location, this->subschemas, schema, walker, resolver,
dialect.value(), SchemaWalkerType_t::Deep, 0, false);
Expand Down Expand Up @@ -315,13 +314,10 @@ sourcemeta::core::SchemaKeywordIterator::SchemaKeywordIterator(
}

for (const auto &entry : schema.as_object()) {
this->entries.push_back({{entry.first},
dialect,
vocabularies,
base_dialect,
entry.second,
{},
false});
sourcemeta::core::SchemaIteratorEntry subschema_entry{
{entry.first}, dialect, vocabularies, base_dialect,
entry.second, {}, false};
this->entries.push_back(std::move(subschema_entry));
}

// Sort keywords based on priority for correct evaluation
Expand Down
Loading

0 comments on commit 125f005

Please sign in to comment.