schemurai 2.2.0 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 01b0c89354b90232337525b7fe75c063053fecd2965036bb7ff600d2b4e823cb
4
- data.tar.gz: f45549ac8cf41eae0c2b28220ca93fe87042cec2aa7c7926382a00f8f65c105d
3
+ metadata.gz: 163dbc9593611cce4f28183766bd5e17a08387a54883c17a502a198eb010ad36
4
+ data.tar.gz: aa3d5f90db227194179023b31043cf36b4597e4af5f1e2209b2846e9c36f9044
5
5
  SHA512:
6
- metadata.gz: 618d494971d7a1745075dae9b9e6155562f9c0f0fb058e923cd457f26bb062be2858db9f2ffaf711d035ee83490179e005869106f3ae29ef0c8011512bed2af9
7
- data.tar.gz: 6ecd49a7320b6b936067808a2a0bd07d6e497418427d0e1612d624dd703a71c6eae99f9ae27ab0a94c2946c3a6495fdc2c11bf665b7d59a9fad628f285af0f5e
6
+ metadata.gz: 5f5ba215dde91e1bf364ff6ebbbebb7ea40b284e3908cfcb578d48aad2174c34e5cf6d9fd121947bb54f8b2cf0fad626e629ed94d6b4a52ab41ba84ad7dfbc55
7
+ data.tar.gz: 4efa97250aad095e414eb230310ae580bbd029b834aa11322cab8da0b19df7a1fb6703240ee0439fd4abe788fcfa47c918ac92806bef174d38d0110bb35cc934
@@ -4,10 +4,13 @@ require "json"
4
4
  require "base64"
5
5
  require_relative "evaluation"
6
6
  require_relative "error_message"
7
+ require_relative "unique_items"
7
8
 
8
9
  module Schemurai
9
10
  module Internal
10
11
  class Evaluator
12
+ include UniqueItems
13
+
11
14
  MISSING_SEGMENT = Object.new.freeze
12
15
 
13
16
  def backend = :ruby
@@ -226,11 +229,7 @@ module Schemurai
226
229
  length = value.length
227
230
  return false if schema.key?("maxItems") && length > schema["maxItems"]
228
231
  return false if schema.key?("minItems") && length < schema["minItems"]
229
- if schema["uniqueItems"]
230
- value.each_with_index do |item, index|
231
- return false if value[0...index].any? { |previous| json_equal?(previous, item) }
232
- end
233
- end
232
+ return false if schema["uniqueItems"] && !unique_items?(value)
234
233
 
235
234
  prefix_items = schema["prefixItems"]
236
235
  if prefix_items.is_a?(Array)
@@ -555,12 +554,7 @@ module Schemurai
555
554
  limit(schema, "maxItems", value.length) { |actual, expected| actual <= expected }
556
555
  limit(schema, "minItems", value.length) { |actual, expected| actual >= expected }
557
556
 
558
- if schema["uniqueItems"]
559
- duplicate = value.each_with_index.any? do |item, index|
560
- value[0...index].any? { |previous| json_equal?(previous, item) }
561
- end
562
- add_error("uniqueItems") { ErrorMessage.unique_items } if duplicate
563
- end
557
+ add_error("uniqueItems") { ErrorMessage.unique_items } if schema["uniqueItems"] && !unique_items?(value)
564
558
 
565
559
  prefix_items = schema["prefixItems"]
566
560
  if prefix_items.is_a?(Array)
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schemurai
4
+ module Internal
5
+ module UniqueItems
6
+ private def unique_items?(values)
7
+ # Avoid building an index for small arrays and early duplicates.
8
+ if values.length <= 16
9
+ values.each_with_index do |item, index|
10
+ index.times { |previous| return false if json_equal?(values[previous], item) }
11
+ end
12
+ return true
13
+ end
14
+
15
+ buckets = {}
16
+ # Store a single index until a collision needs a bucket array.
17
+ index = 0
18
+ while index < values.length
19
+ item = values[index]
20
+ fingerprint = json_fingerprint(item)
21
+ if (bucket = buckets[fingerprint])
22
+ if bucket.is_a?(Integer)
23
+ return false if json_equal?(values[bucket], item)
24
+
25
+ buckets[fingerprint] = [bucket, index]
26
+ else
27
+ return false if bucket.any? { |previous| json_equal?(values[previous], item) }
28
+
29
+ bucket << index
30
+ end
31
+ else
32
+ buckets[fingerprint] = index
33
+ end
34
+ index += 1
35
+ end
36
+ true
37
+ end
38
+
39
+ private def json_fingerprint(value)
40
+ case value
41
+ when Numeric
42
+ # JSON numbers compare across Integer/Float. Converting integral
43
+ # floats to integers also preserves precision for large integers.
44
+ return value.hash if value.is_a?(Integer)
45
+
46
+ number = value.to_f
47
+ (number.finite? && number == number.to_i) ? number.to_i.hash : number.hash
48
+ when Array
49
+ # Mask before multiplication to keep intermediate values immediate
50
+ # integers; hash each step to retain order without temporary arrays.
51
+ fingerprint = value.class.hash
52
+ value.each { |item| fingerprint = (((fingerprint & 0x1fffffff) * 31) ^ json_fingerprint(item)).hash }
53
+ fingerprint
54
+ when Hash
55
+ # Object key order is immaterial. Hash collisions are resolved using
56
+ # json_equal?, so this fingerprint never decides equality itself.
57
+ entries = 0
58
+ value.each_pair { |key, item| entries ^= (((key.hash & 0x1fffffff) * 31) ^ json_fingerprint(item)).hash }
59
+ (value.class.hash ^ value.length ^ entries).hash
60
+ else
61
+ value.hash
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Schemurai
4
4
  # The installed Schemurai version.
5
- VERSION = "2.2.0"
5
+ VERSION = "2.2.2"
6
6
  end
@@ -4,6 +4,7 @@ require "base64"
4
4
  require "json"
5
5
  require_relative "../evaluation"
6
6
  require_relative "../error_message"
7
+ require_relative "../unique_items"
7
8
  require_relative "compiler"
8
9
 
9
10
  module Schemurai
@@ -49,6 +50,8 @@ module Schemurai
49
50
  private_constant :EvaluationBuffer
50
51
 
51
52
  class Evaluator
53
+ include Internal::UniqueItems
54
+
52
55
  MISSING_SEGMENT = Object.new.freeze
53
56
  DECIMAL_CACHE_LIMIT = 16
54
57
 
@@ -552,17 +555,7 @@ module Schemurai
552
555
  length = value.length
553
556
  return false if rules.max_items && length > rules.max_items
554
557
  return false if rules.min_items && length < rules.min_items
555
- if rules.unique
556
- index = 1
557
- while index < length
558
- previous_index = 0
559
- while previous_index < index
560
- return false if json_equal?(value[previous_index], value[index])
561
- previous_index += 1
562
- end
563
- index += 1
564
- end
565
- end
558
+ return false if rules.unique && !unique_items?(value)
566
559
 
567
560
  if (prefix_items = rules.prefix_items)
568
561
  prefix_items.each_with_index do |child, index|
@@ -616,25 +609,10 @@ module Schemurai
616
609
 
617
610
  add_error("minItems") { Internal::ErrorMessage.size("minItems", rules.min_items, value.length) }
618
611
  end
619
- if rules.unique
620
- duplicate = false
621
- index = 1
622
- while index < value.length && !duplicate
623
- previous_index = 0
624
- while previous_index < index
625
- if json_equal?(value[previous_index], value[index])
626
- duplicate = true
627
- break
628
- end
629
- previous_index += 1
630
- end
631
- index += 1
632
- end
633
- if duplicate
634
- return Evaluation.invalid unless @errors
612
+ if rules.unique && !unique_items?(value)
613
+ return Evaluation.invalid unless @errors
635
614
 
636
- add_error("uniqueItems") { Internal::ErrorMessage.unique_items }
637
- end
615
+ add_error("uniqueItems") { Internal::ErrorMessage.unique_items }
638
616
  end
639
617
 
640
618
  if (prefix_items = rules.prefix_items)
data/sig/schemurai.rbs CHANGED
@@ -1,13 +1,6 @@
1
1
  module Schemurai
2
2
  # A JSON-shaped Ruby value accepted as a schema value or validation instance.
3
- type json_value =
4
- nil
5
- | bool
6
- | Integer
7
- | Float
8
- | String
9
- | Array[json_value]
10
- | Hash[String, json_value]
3
+ type json_value = nil | bool | Integer | Float | String | Array[json_value] | Hash[String, json_value]
11
4
 
12
5
  # A JSON Schema root.
13
6
  type schema = bool | Hash[String, json_value]
@@ -50,12 +43,7 @@ module Schemurai
50
43
  def self.[]: (String keyword, String instance_path, String schema_path, String message) -> ValidationError
51
44
  | (keyword: String, instance_path: String, schema_path: String, message: String) -> ValidationError
52
45
 
53
- def to_h: () -> {
54
- keyword: String,
55
- instance_path: String,
56
- schema_path: String,
57
- message: String
58
- }
46
+ def to_h: () -> { keyword: String, instance_path: String, schema_path: String, message: String }
59
47
  end
60
48
 
61
49
  class Result
@@ -68,7 +56,9 @@ module Schemurai
68
56
 
69
57
  interface _Evaluator
70
58
  def backend: () -> backend
59
+
71
60
  def validate: (json_value instance) -> Result
61
+
72
62
  def valid?: (json_value instance) -> bool
73
63
  end
74
64
 
@@ -82,76 +72,29 @@ module Schemurai
82
72
  class SchemaRegistry
83
73
  attr_reader backend: backend
84
74
 
85
- def initialize: (
86
- ?schemas: schemas,
87
- ?validate_schema: bool,
88
- ?backend: backend_selection
89
- ) -> void
75
+ def initialize: (?schemas: schemas, ?validate_schema: bool, ?backend: backend_selection) -> void
90
76
 
91
77
  def validate_schema?: () -> bool
92
78
 
93
- def compile: (
94
- schema schema,
95
- ?base_uri: String?,
96
- ?content: bool,
97
- ?format: bool
98
- ) -> Validator
79
+ def compile: (schema schema, ?base_uri: String?, ?content: bool, ?format: bool) -> Validator
99
80
 
100
81
  def validate_schema: (schema schema) -> Result
101
82
  def valid_schema?: (schema schema) -> bool
102
83
  def make_shareable: () -> self
103
84
  def shareable?: () -> bool
104
85
 
105
- def validator_for: (
106
- String uri,
107
- ?content: bool,
108
- ?format: bool
109
- ) -> Validator
86
+ def validator_for: (String uri, ?content: bool, ?format: bool) -> Validator
110
87
  end
111
88
 
112
89
  def self.backend: () -> backend
113
90
 
114
- def self.compile: (
115
- schema schema,
116
- ?schemas: schemas,
117
- ?base_uri: String?,
118
- ?content: bool,
119
- ?format: bool,
120
- ?validate_schema: bool,
121
- ?backend: backend_selection
122
- ) -> Validator
123
-
124
- def self.validate_schema: (
125
- schema schema,
126
- ?schemas: schemas,
127
- ?backend: backend_selection
128
- ) -> Result
129
-
130
- def self.valid_schema?: (
131
- schema schema,
132
- ?schemas: schemas,
133
- ?backend: backend_selection
134
- ) -> bool
135
-
136
- def self.validate: (
137
- schema schema,
138
- json_value instance,
139
- ?schemas: schemas,
140
- ?base_uri: String?,
141
- ?content: bool,
142
- ?format: bool,
143
- ?validate_schema: bool,
144
- ?backend: backend_selection
145
- ) -> Result
146
-
147
- def self.valid?: (
148
- schema schema,
149
- json_value instance,
150
- ?schemas: schemas,
151
- ?base_uri: String?,
152
- ?content: bool,
153
- ?format: bool,
154
- ?validate_schema: bool,
155
- ?backend: backend_selection
156
- ) -> bool
91
+ def self.compile: (schema schema, ?schemas: schemas, ?base_uri: String?, ?content: bool, ?format: bool, ?validate_schema: bool, ?backend: backend_selection) -> Validator
92
+
93
+ def self.validate_schema: (schema schema, ?schemas: schemas, ?backend: backend_selection) -> Result
94
+
95
+ def self.valid_schema?: (schema schema, ?schemas: schemas, ?backend: backend_selection) -> bool
96
+
97
+ def self.validate: (schema schema, json_value instance, ?schemas: schemas, ?base_uri: String?, ?content: bool, ?format: bool, ?validate_schema: bool, ?backend: backend_selection) -> Result
98
+
99
+ def self.valid?: (schema schema, json_value instance, ?schemas: schemas, ?base_uri: String?, ?content: bool, ?format: bool, ?validate_schema: bool, ?backend: backend_selection) -> bool
157
100
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schemurai
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kuya KOHARA (oakcask)
@@ -53,6 +53,7 @@ files:
53
53
  - lib/schemurai/schema_domain.rb
54
54
  - lib/schemurai/schema_graph.rb
55
55
  - lib/schemurai/schema_node.rb
56
+ - lib/schemurai/unique_items.rb
56
57
  - lib/schemurai/version.rb
57
58
  - lib/schemurai/vm/compiler.rb
58
59
  - lib/schemurai/vm/evaluator.rb
@@ -63,7 +64,7 @@ licenses:
63
64
  metadata:
64
65
  homepage_uri: https://github.com/oakcask/schemurai
65
66
  source_code_uri: https://github.com/oakcask/schemurai/tree/main
66
- documentation_uri: https://rubydoc.info/gems/schemurai/2.2.0
67
+ documentation_uri: https://rubydoc.info/gems/schemurai/2.2.2
67
68
  rubygems_mfa_required: 'true'
68
69
  rdoc_options:
69
70
  - "--main"
@@ -87,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
87
88
  - !ruby/object:Gem::Version
88
89
  version: '0'
89
90
  requirements: []
90
- rubygems_version: 4.0.16
91
+ rubygems_version: 4.0.20
91
92
  specification_version: 4
92
93
  summary: A fast, Ractor-safe, pure Ruby JSON Schema Draft 7+ validator
93
94
  test_files: []