schemurai 2.2.1 → 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 +4 -4
- data/lib/schemurai/evaluator.rb +5 -11
- data/lib/schemurai/unique_items.rb +66 -0
- data/lib/schemurai/version.rb +1 -1
- data/lib/schemurai/vm/evaluator.rb +7 -29
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 163dbc9593611cce4f28183766bd5e17a08387a54883c17a502a198eb010ad36
|
|
4
|
+
data.tar.gz: aa3d5f90db227194179023b31043cf36b4597e4af5f1e2209b2846e9c36f9044
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f5ba215dde91e1bf364ff6ebbbebb7ea40b284e3908cfcb578d48aad2174c34e5cf6d9fd121947bb54f8b2cf0fad626e629ed94d6b4a52ab41ba84ad7dfbc55
|
|
7
|
+
data.tar.gz: 4efa97250aad095e414eb230310ae580bbd029b834aa11322cab8da0b19df7a1fb6703240ee0439fd4abe788fcfa47c918ac92806bef174d38d0110bb35cc934
|
data/lib/schemurai/evaluator.rb
CHANGED
|
@@ -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
|
data/lib/schemurai/version.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
637
|
-
end
|
|
615
|
+
add_error("uniqueItems") { Internal::ErrorMessage.unique_items }
|
|
638
616
|
end
|
|
639
617
|
|
|
640
618
|
if (prefix_items = rules.prefix_items)
|
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.
|
|
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.
|
|
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.
|
|
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: []
|