Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

* Count Unicode code points for `minLength` and `maxLength` without allocating a charlist for inputs larger than 256 bytes; retain standard conversion for inputs up to 256 bytes. Malformed UTF-8 now returns a validation error with `error_detail: "invalid_utf8"` instead of raising `UnicodeConversionError`.
* Compile each `patternProperties` regex once when the same schema also uses `additionalProperties`, with a bounded per-schema cache that preserves vocabulary gates, error behavior, and validation semantics.
* Reuse runtime pattern-match classification between sibling `patternProperties` and `additionalProperties` rules within one validation invocation, reducing repeated regex matching for wide objects while preserving rule order, annotations, and diagnostics.
* Reuse runtime pattern-match classification between sibling `patternProperties` and `additionalProperties` rules within one validation invocation, while restricting cache-aware two-rule dispatch to those exact sibling pairs so ordinary two-rule schemas retain their direct fast path.
* Adaptively merge evaluated keys from parent-dominated `anyOf` branches with balanced `MapSet` unions, avoiding repeated conversion of shared incoming evaluated keys while preserving full branch traversal, evaluated-key inputs, diagnostics, and legacy list-key behavior.
* Avoid rebuilding already-flat validation error lists at the public validation boundary, and fuse nested error flattening with legacy error formatting while preserving diagnostic order, values, and internal error grouping.
* Remove redundant input-key presence checks when validating `required`, while preserving declaration order, duplicate entries, non-object behavior, and diagnostic shape.
Expand Down
69 changes: 49 additions & 20 deletions lib/jsonschex/validator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -130,21 +130,50 @@ defmodule JSONSchex.Validator do
end
end

# Restrict cache eligibility at function-clause dispatch so ordinary two-rule
# schemas do not pay for a runtime pattern-keyword classification call. Exact
# heads keep the ordinary fallback free from a compound guard evaluation.
def validate_entry(
%Schema{rules: [rule1, rule2] = rules, source_id: nil},
%Schema{
rules: [
%Rule{name: :patternProperties} = first_rule,
%Rule{name: :additionalProperties} = second_rule
] = rules,
source_id: nil
},
data,
path,
context,
evaluated
) do
ctx = {path, evaluated, context}
validate_two_pattern_rules(rules, first_rule, second_rule, data, path, context, evaluated)
end

case two_rule_pattern_match_cache(rule1, rule2, data) do
nil ->
run_two_rules(rule1, rule2, data, path, context, evaluated, ctx)
cache ->
run_rules_with_pattern_cache(rules, data, path, context, evaluated, ctx, [], cache)
end
def validate_entry(
%Schema{
rules: [
%Rule{name: :additionalProperties} = first_rule,
%Rule{name: :patternProperties} = second_rule
] = rules,
source_id: nil
},
data,
path,
context,
evaluated
) do
validate_two_pattern_rules(rules, first_rule, second_rule, data, path, context, evaluated)
end

def validate_entry(
%Schema{rules: [rule1, rule2], source_id: nil},
data,
path,
context,
evaluated
) do
ctx = {path, evaluated, context}
run_two_rules(rule1, rule2, data, path, context, evaluated, ctx)
end

def validate_entry(
Expand Down Expand Up @@ -175,6 +204,18 @@ defmodule JSONSchex.Validator do
defp to_error_entry(err_ctx, path, rule_name, data) when is_map(err_ctx),
do: [%Error{path: path, rule: rule_name, context: err_ctx, value: data}]

defp validate_two_pattern_rules(rules, first_rule, second_rule, data, path, context, evaluated) do
ctx = {path, evaluated, context}

case pattern_match_cache(rules, data) do
nil ->
run_two_rules(first_rule, second_rule, data, path, context, evaluated, ctx)

cache ->
run_rules_with_pattern_cache(rules, data, path, context, evaluated, ctx, [], cache)
end
end

# Keep the specialized two-rule execution path for ordinary schemas. Only a
# direct pair of sibling object keywords enters the cache-aware runner.
defp run_two_rules(rule1, rule2, data, path, context, evaluated, ctx) do
Expand Down Expand Up @@ -337,18 +378,6 @@ defmodule JSONSchex.Validator do

defp pattern_match_cache_complete?(_rule, _cache), do: false

defp two_rule_pattern_match_cache(
%Rule{name: first_name} = first_rule,
%Rule{name: second_name} = second_rule,
data
)
when first_name in [:patternProperties, :additionalProperties] and
second_name in [:patternProperties, :additionalProperties] and
first_name != second_name do
pattern_match_cache([first_rule, second_rule], data)
end

defp two_rule_pattern_match_cache(_first_rule, _second_rule, _data), do: nil

# A cache map cannot repay its setup work when the two sibling rules would
# repeat exactly one match check. All larger candidate workloads stay eligible.
Expand Down