Skip to content

Commit

Permalink
Prefer vector over sets when configuring compiler steps
Browse files Browse the repository at this point in the history
Signed-off-by: Juan Cruz Viotti <[email protected]>
  • Loading branch information
jviotti committed Sep 3, 2024
1 parent 265cd2e commit bc82bd6
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 73 deletions.
15 changes: 10 additions & 5 deletions src/jsonschema/compile_describe.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,17 @@ auto describe_type_check(const bool valid, const JSON::Type current,
}

auto describe_types_check(const bool valid, const JSON::Type current,
const std::set<JSON::Type> &expected,
const std::vector<JSON::Type> &expected,
std::ostringstream &message) -> void {
assert(expected.size() > 1);
auto copy = expected;
if (copy.contains(JSON::Type::Real) && copy.contains(JSON::Type::Integer)) {
copy.erase(JSON::Type::Integer);

const auto match_real{
std::find(copy.cbegin(), copy.cend(), JSON::Type::Real)};
const auto match_integer{
std::find(copy.cbegin(), copy.cend(), JSON::Type::Integer)};
if (match_real != copy.cend() && match_integer != copy.cend()) {
copy.erase(match_integer);
}

if (copy.size() == 1) {
Expand All @@ -85,7 +90,7 @@ auto describe_types_check(const bool valid, const JSON::Type current,
}

if (valid && current == JSON::Type::Integer &&
copy.contains(JSON::Type::Real)) {
std::find(copy.cbegin(), copy.cend(), JSON::Type::Real) != copy.cend()) {
message << "number";
} else {
message << to_string(current);
Expand Down Expand Up @@ -775,7 +780,7 @@ struct DescribeVisitor {
message << " but did not define properties ";
for (auto iterator = missing.cbegin(); iterator != missing.cend();
++iterator) {
if (std::next(iterator) == value.cend()) {
if (std::next(iterator) == missing.cend()) {
message << "and " << escape_string(*iterator);
} else {
message << escape_string(*iterator) << ", ";
Expand Down
41 changes: 30 additions & 11 deletions src/jsonschema/compile_evaluate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class EvaluationContext {
auto defines_any_adjacent_annotation(
const Pointer &expected_instance_location,
const Pointer &base_evaluate_path,
const std::set<std::string> &keywords) const -> bool {
const std::vector<std::string> &keywords) const -> bool {
for (const auto &keyword : keywords) {
if (this->defines_any_adjacent_annotation(expected_instance_location,
base_evaluate_path, keyword)) {
Expand All @@ -102,7 +102,7 @@ class EvaluationContext {

auto defines_adjacent_annotation(const Pointer &expected_instance_location,
const Pointer &base_evaluate_path,
const std::set<std::string> &keywords,
const std::vector<std::string> &keywords,
const JSON &value) const -> bool {
// TODO: We should be taking masks into account
for (const auto &keyword : keywords) {
Expand All @@ -119,7 +119,7 @@ class EvaluationContext {

auto defines_annotation(const Pointer &expected_instance_location,
const Pointer &base_evaluate_path,
const std::set<std::string> &keywords,
const std::vector<std::string> &keywords,
const JSON &value) const -> bool {
if (keywords.empty()) {
return false;
Expand All @@ -131,7 +131,10 @@ class EvaluationContext {
instance_annotations) {
assert(!schema_location.empty());
const auto &keyword{schema_location.back()};
if (keyword.is_property() && keywords.contains(keyword.to_property()) &&

if (keyword.is_property() &&
std::find(keywords.cbegin(), keywords.cend(),
keyword.to_property()) != keywords.cend() &&
schema_annotations.contains(value) &&
schema_location.initial().starts_with(base_evaluate_path)) {
bool blacklisted = false;
Expand All @@ -153,7 +156,7 @@ class EvaluationContext {
}

auto largest_annotation_index(const Pointer &expected_instance_location,
const std::set<std::string> &keywords,
const std::vector<std::string> &keywords,
const std::uint64_t default_value) const
-> std::uint64_t {
// TODO: We should be taking masks into account
Expand All @@ -163,7 +166,12 @@ class EvaluationContext {
this->annotations(expected_instance_location)) {
assert(!schema_location.empty());
const auto &keyword{schema_location.back()};
if (!keyword.is_property() || !keywords.contains(keyword.to_property())) {
if (!keyword.is_property()) {
continue;
}

if (std::find(keywords.cbegin(), keywords.cend(),
keyword.to_property()) == keywords.cend()) {
continue;
}

Expand Down Expand Up @@ -492,9 +500,16 @@ auto evaluate_step(
const auto &target{context.resolve_target(instance)};
// In non-strict mode, we consider a real number that represents an
// integer to be an integer
result = assertion.value.contains(target.type()) ||
(assertion.value.contains(JSON::Type::Integer) &&
target.is_integer_real());
for (const auto type : assertion.value) {
if (type == JSON::Type::Integer && target.is_integer_real()) {
result = true;
break;
} else if (type == target.type()) {
result = true;
break;
}
}

EVALUATE_END(assertion, SchemaCompilerAssertionTypeAny);
} else if (IS_STEP(SchemaCompilerAssertionTypeStrict)) {
EVALUATE_BEGIN_NO_PRECONDITION(assertion,
Expand All @@ -506,7 +521,9 @@ auto evaluate_step(
SchemaCompilerAssertionTypeStrictAny);
// Otherwise we are we even emitting this instruction?
assert(assertion.value.size() > 1);
result = assertion.value.contains(context.resolve_target(instance).type());
result = (std::find(assertion.value.cbegin(), assertion.value.cend(),
context.resolve_target(instance).type()) !=
assertion.value.cend());
EVALUATE_END(assertion, SchemaCompilerAssertionTypeStrictAny);
} else if (IS_STEP(SchemaCompilerAssertionRegex)) {
EVALUATE_BEGIN(assertion, SchemaCompilerAssertionRegex, target.is_string());
Expand Down Expand Up @@ -548,7 +565,9 @@ auto evaluate_step(
EVALUATE_END(assertion, SchemaCompilerAssertionEqual);
} else if (IS_STEP(SchemaCompilerAssertionEqualsAny)) {
EVALUATE_BEGIN_NO_PRECONDITION(assertion, SchemaCompilerAssertionEqualsAny);
result = assertion.value.contains(context.resolve_target(instance));
result =
(std::find(assertion.value.cbegin(), assertion.value.cend(),
context.resolve_target(instance)) != assertion.value.cend());
EVALUATE_END(assertion, SchemaCompilerAssertionEqualsAny);
} else if (IS_STEP(SchemaCompilerAssertionGreaterEqual)) {
EVALUATE_BEGIN(assertion, SchemaCompilerAssertionGreaterEqual,
Expand Down
16 changes: 8 additions & 8 deletions src/jsonschema/default_compiler_2019_09.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ auto compiler_2019_09_validation_dependentrequired(
continue;
}

std::set<JSON::String> properties;
std::vector<JSON::String> properties;
for (const auto &property : entry.second.as_array()) {
assert(property.is_string());
properties.emplace(property.to_string());
properties.push_back(property.to_string());
}

if (!properties.empty()) {
Expand Down Expand Up @@ -257,16 +257,16 @@ auto compiler_2019_09_applicator_unevaluatedproperties(

if (schema_context.vocabularies.contains(
"https://json-schema.org/draft/2019-09/vocab/applicator")) {
dependencies.emplace("properties");
dependencies.emplace("patternProperties");
dependencies.emplace("additionalProperties");
dependencies.push_back("properties");
dependencies.push_back("patternProperties");
dependencies.push_back("additionalProperties");
}

if (schema_context.vocabularies.contains(
"https://json-schema.org/draft/2020-12/vocab/applicator")) {
dependencies.emplace("properties");
dependencies.emplace("patternProperties");
dependencies.emplace("additionalProperties");
dependencies.push_back("properties");
dependencies.push_back("patternProperties");
dependencies.push_back("additionalProperties");
}

SchemaCompilerTemplate children{compile(context, schema_context,
Expand Down
38 changes: 19 additions & 19 deletions src/jsonschema/default_compiler_draft4.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ auto compiler_draft4_validation_type(
} else if (type == "number") {
return {make<SchemaCompilerAssertionTypeStrictAny>(
true, context, schema_context, dynamic_context,
std::set<JSON::Type>{JSON::Type::Real, JSON::Type::Integer})};
std::vector<JSON::Type>{JSON::Type::Real, JSON::Type::Integer})};
} else if (type == "integer") {
return {make<SchemaCompilerAssertionTypeStrict>(
true, context, schema_context, dynamic_context, JSON::Type::Integer)};
Expand Down Expand Up @@ -118,7 +118,7 @@ auto compiler_draft4_validation_type(
} else if (type == "number") {
return {make<SchemaCompilerAssertionTypeStrictAny>(
true, context, schema_context, dynamic_context,
std::set<JSON::Type>{JSON::Type::Real, JSON::Type::Integer})};
std::vector<JSON::Type>{JSON::Type::Real, JSON::Type::Integer})};
} else if (type == "integer") {
return {make<SchemaCompilerAssertionTypeStrict>(
true, context, schema_context, dynamic_context, JSON::Type::Integer)};
Expand All @@ -129,26 +129,26 @@ auto compiler_draft4_validation_type(
return {};
}
} else if (schema_context.schema.at(dynamic_context.keyword).is_array()) {
std::set<JSON::Type> types;
std::vector<JSON::Type> types;
for (const auto &type :
schema_context.schema.at(dynamic_context.keyword).as_array()) {
assert(type.is_string());
const auto &type_string{type.to_string()};
if (type_string == "null") {
types.emplace(JSON::Type::Null);
types.push_back(JSON::Type::Null);
} else if (type_string == "boolean") {
types.emplace(JSON::Type::Boolean);
types.push_back(JSON::Type::Boolean);
} else if (type_string == "object") {
types.emplace(JSON::Type::Object);
types.push_back(JSON::Type::Object);
} else if (type_string == "array") {
types.emplace(JSON::Type::Array);
types.push_back(JSON::Type::Array);
} else if (type_string == "number") {
types.emplace(JSON::Type::Integer);
types.emplace(JSON::Type::Real);
types.push_back(JSON::Type::Integer);
types.push_back(JSON::Type::Real);
} else if (type_string == "integer") {
types.emplace(JSON::Type::Integer);
types.push_back(JSON::Type::Integer);
} else if (type_string == "string") {
types.emplace(JSON::Type::String);
types.push_back(JSON::Type::String);
}
}

Expand Down Expand Up @@ -177,11 +177,11 @@ auto compiler_draft4_validation_required(
if (schema_context.schema.at(dynamic_context.keyword).empty()) {
return {};
} else if (schema_context.schema.at(dynamic_context.keyword).size() > 1) {
std::set<JSON::String> properties;
std::vector<JSON::String> properties;
for (const auto &property :
schema_context.schema.at(dynamic_context.keyword).as_array()) {
assert(property.is_string());
properties.emplace(property.to_string());
properties.push_back(property.to_string());
}

if (properties.size() == 1) {
Expand Down Expand Up @@ -492,11 +492,11 @@ auto compiler_draft4_applicator_additionalproperties_conditional_annotation(

SchemaCompilerValueStrings dependencies;
if (schema_context.schema.defines("properties")) {
dependencies.insert("properties");
dependencies.push_back("properties");
}

if (schema_context.schema.defines("patternProperties")) {
dependencies.insert("patternProperties");
dependencies.push_back("patternProperties");
}

if (dependencies.empty()) {
Expand Down Expand Up @@ -815,10 +815,10 @@ auto compiler_draft4_applicator_dependencies(
{entry.first}, empty_pointer)));
}
} else if (entry.second.is_array()) {
std::set<JSON::String> properties;
std::vector<JSON::String> properties;
for (const auto &property : entry.second.as_array()) {
assert(property.is_string());
properties.emplace(property.to_string());
properties.push_back(property.to_string());
}

if (!properties.empty()) {
Expand Down Expand Up @@ -851,10 +851,10 @@ auto compiler_draft4_validation_enum(
JSON{schema_context.schema.at(dynamic_context.keyword).front()})};
}

std::set<JSON> options;
std::vector<JSON> options;
for (const auto &option :
schema_context.schema.at(dynamic_context.keyword).as_array()) {
options.insert(option);
options.push_back(option);
}

return {make<SchemaCompilerAssertionEqualsAny>(
Expand Down
22 changes: 11 additions & 11 deletions src/jsonschema/default_compiler_draft6.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ auto compiler_draft6_validation_type(
} else if (type == "number") {
return {make<SchemaCompilerAssertionTypeStrictAny>(
true, context, schema_context, dynamic_context,
std::set<JSON::Type>{JSON::Type::Real, JSON::Type::Integer})};
std::vector<JSON::Type>{JSON::Type::Real, JSON::Type::Integer})};
} else if (type == "integer") {
return {make<SchemaCompilerAssertionType>(
true, context, schema_context, dynamic_context, JSON::Type::Integer)};
Expand Down Expand Up @@ -64,7 +64,7 @@ auto compiler_draft6_validation_type(
} else if (type == "number") {
return {make<SchemaCompilerAssertionTypeStrictAny>(
true, context, schema_context, dynamic_context,
std::set<JSON::Type>{JSON::Type::Real, JSON::Type::Integer})};
std::vector<JSON::Type>{JSON::Type::Real, JSON::Type::Integer})};
} else if (type == "integer") {
return {make<SchemaCompilerAssertionType>(
true, context, schema_context, dynamic_context, JSON::Type::Integer)};
Expand All @@ -75,26 +75,26 @@ auto compiler_draft6_validation_type(
return {};
}
} else if (schema_context.schema.at(dynamic_context.keyword).is_array()) {
std::set<JSON::Type> types;
std::vector<JSON::Type> types;
for (const auto &type :
schema_context.schema.at(dynamic_context.keyword).as_array()) {
assert(type.is_string());
const auto &type_string{type.to_string()};
if (type_string == "null") {
types.emplace(JSON::Type::Null);
types.push_back(JSON::Type::Null);
} else if (type_string == "boolean") {
types.emplace(JSON::Type::Boolean);
types.push_back(JSON::Type::Boolean);
} else if (type_string == "object") {
types.emplace(JSON::Type::Object);
types.push_back(JSON::Type::Object);
} else if (type_string == "array") {
types.emplace(JSON::Type::Array);
types.push_back(JSON::Type::Array);
} else if (type_string == "number") {
types.emplace(JSON::Type::Integer);
types.emplace(JSON::Type::Real);
types.push_back(JSON::Type::Integer);
types.push_back(JSON::Type::Real);
} else if (type_string == "integer") {
types.emplace(JSON::Type::Integer);
types.push_back(JSON::Type::Integer);
} else if (type_string == "string") {
types.emplace(JSON::Type::String);
types.push_back(JSON::Type::String);
}
}

Expand Down
22 changes: 13 additions & 9 deletions src/jsonschema/include/sourcemeta/jsontoolkit/jsonschema_compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,29 @@ struct SchemaCompilerValueNone {};
/// Represents a compiler step JSON value
using SchemaCompilerValueJSON = JSON;

// Note that for these steps, we prefer vectors over sets as the former performs
// better for small collections, where we can even guarantee uniqueness when
// generating the instructions

/// @ingroup jsonschema_compiler
/// Represents a set of JSON values
using SchemaCompilerValueArray = std::set<JSON>;
using SchemaCompilerValueArray = std::vector<JSON>;

/// @ingroup jsonschema_compiler
/// Represents a compiler step string value
using SchemaCompilerValueString = JSON::String;
/// Represents a compiler step string values
using SchemaCompilerValueStrings = std::vector<JSON::String>;

/// @ingroup jsonschema_compiler
/// Represents a compiler step string values
using SchemaCompilerValueStrings = std::set<JSON::String>;
/// Represents a compiler step JSON types value
using SchemaCompilerValueTypes = std::vector<JSON::Type>;

/// @ingroup jsonschema_compiler
/// Represents a compiler step JSON type value
using SchemaCompilerValueType = JSON::Type;
/// Represents a compiler step string value
using SchemaCompilerValueString = JSON::String;

/// @ingroup jsonschema_compiler
/// Represents a compiler step JSON types value
using SchemaCompilerValueTypes = std::set<JSON::Type>;
/// Represents a compiler step JSON type value
using SchemaCompilerValueType = JSON::Type;

/// @ingroup jsonschema_compiler
/// Represents a compiler step ECMA regular expression value. We store both the
Expand Down
Loading

0 comments on commit bc82bd6

Please sign in to comment.