schemurai 3.1.0-aarch64-linux-gnu

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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +395 -0
  4. data/lib/schemurai/backend.rb +34 -0
  5. data/lib/schemurai/container_checks/array_diagnostics.rb +119 -0
  6. data/lib/schemurai/container_checks/object_diagnostics.rb +152 -0
  7. data/lib/schemurai/container_checks.rb +534 -0
  8. data/lib/schemurai/decimal.rb +11 -0
  9. data/lib/schemurai/dialect.rb +106 -0
  10. data/lib/schemurai/dialect_keywords.rb +91 -0
  11. data/lib/schemurai/dialects/draft2019_09.rb +27 -0
  12. data/lib/schemurai/dialects/draft2020_12.rb +27 -0
  13. data/lib/schemurai/dialects/draft7.rb +27 -0
  14. data/lib/schemurai/ecma_regexp.rb +46 -0
  15. data/lib/schemurai/error_message.rb +102 -0
  16. data/lib/schemurai/evaluation.rb +63 -0
  17. data/lib/schemurai/evaluation_limits.rb +65 -0
  18. data/lib/schemurai/evaluation_pool.rb +66 -0
  19. data/lib/schemurai/evaluator.rb +861 -0
  20. data/lib/schemurai/formats.rb +403 -0
  21. data/lib/schemurai/json_pointer.rb +11 -0
  22. data/lib/schemurai/limit_budget.rb +78 -0
  23. data/lib/schemurai/limit_exceeded.rb +18 -0
  24. data/lib/schemurai/limited_evaluator.rb +43 -0
  25. data/lib/schemurai/meta_schemas/draft2019_09.rb +149 -0
  26. data/lib/schemurai/meta_schemas/draft2020_12.rb +149 -0
  27. data/lib/schemurai/meta_schemas/draft7.rb +121 -0
  28. data/lib/schemurai/meta_schemas.rb +28 -0
  29. data/lib/schemurai/native/4.0/schemurai_native.so +0 -0
  30. data/lib/schemurai/native/diagnostics.rb +53 -0
  31. data/lib/schemurai/native/evaluator.rb +87 -0
  32. data/lib/schemurai/native/null_numeric.rb +23 -0
  33. data/lib/schemurai/number_checks.rb +128 -0
  34. data/lib/schemurai/schema_domain.rb +95 -0
  35. data/lib/schemurai/schema_graph.rb +538 -0
  36. data/lib/schemurai/schema_node.rb +55 -0
  37. data/lib/schemurai/string_checks.rb +166 -0
  38. data/lib/schemurai/type_checks.rb +59 -0
  39. data/lib/schemurai/unique_items.rb +68 -0
  40. data/lib/schemurai/value_equality.rb +72 -0
  41. data/lib/schemurai/version.rb +6 -0
  42. data/lib/schemurai/vm/compiler.rb +410 -0
  43. data/lib/schemurai/vm/evaluator.rb +663 -0
  44. data/lib/schemurai/vm/limited_evaluator.rb +27 -0
  45. data/lib/schemurai.rb +307 -0
  46. data/lib/schemurai_native.rb +4 -0
  47. data/sig/schemurai.rbs +108 -0
  48. metadata +121 -0
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "decimal"
4
+ require_relative "type_checks"
5
+
6
+ module Schemurai
7
+ module Internal
8
+ module NumberChecks
9
+ MAXIMUM = 1 << 0
10
+ MINIMUM = 1 << 1
11
+ EXCLUSIVE_MAXIMUM = 1 << 2
12
+ EXCLUSIVE_MINIMUM = 1 << 3
13
+ MULTIPLE_OF = 1 << 4
14
+
15
+ class State
16
+ CACHE_LIMIT = 16
17
+ private_constant :CACHE_LIMIT
18
+
19
+ attr_writer :budget
20
+
21
+ def decimal(value)
22
+ @budget&.check_deadline!
23
+ result = cached_decimal(value)
24
+ @budget&.check_deadline!
25
+ result
26
+ end
27
+
28
+ private def cached_decimal(value)
29
+ return value if value.is_a?(Integer) || value.is_a?(Rational)
30
+
31
+ decimals = (@decimals ||= {})
32
+ return decimals[value] if decimals.key?(value)
33
+
34
+ decimals.clear if decimals.length >= CACHE_LIMIT
35
+ decimals[value] = Decimal.parse(value)
36
+ end
37
+
38
+ def multiple?(rules, value)
39
+ results = (@multiple_results ||= {}.compare_by_identity)
40
+ values = (results[rules] ||= {})
41
+ return values[value] if values.key?(value)
42
+
43
+ values.clear if values.length >= CACHE_LIMIT
44
+ divisor = rules.multiple_of
45
+ values[value] = divisor.positive? && decimal(value).remainder(divisor).zero?
46
+ end
47
+ end
48
+
49
+ module_function def actual(rules, value, state)
50
+ (rules.mask & MULTIPLE_OF).zero? ? value : state.decimal(value)
51
+ end
52
+
53
+ module_function def bound?(actual, bit, limit)
54
+ violated = case bit
55
+ when MAXIMUM then actual > limit
56
+ when MINIMUM then actual < limit
57
+ when EXCLUSIVE_MAXIMUM then actual >= limit
58
+ when EXCLUSIVE_MINIMUM then actual <= limit
59
+ else raise "unknown numeric bound"
60
+ end
61
+ violated ? false : true
62
+ end
63
+
64
+ # Both the VM and native validity frames use the same ordered rules.
65
+ module Evaluation
66
+ private def number_state
67
+ @number_state ||= State.new
68
+ end
69
+
70
+ private def valid_number?(rules, value)
71
+ mask = rules.mask
72
+ actual = (mask & MULTIPLE_OF).zero? ? value : number_state.decimal(value)
73
+ return false if (mask & MAXIMUM) != 0 && actual > rules.maximum
74
+ return false if (mask & MINIMUM) != 0 && actual < rules.minimum
75
+ return false if (mask & EXCLUSIVE_MAXIMUM) != 0 && actual >= rules.exclusive_maximum
76
+ return false if (mask & EXCLUSIVE_MINIMUM) != 0 && actual <= rules.exclusive_minimum
77
+ return true if (mask & MULTIPLE_OF).zero?
78
+
79
+ valid_multiple?(rules, value)
80
+ end
81
+
82
+ private def valid_multiple?(rules, value)
83
+ number_state.multiple?(rules, value)
84
+ end
85
+ end
86
+
87
+ # One native numeric instruction owns this frame while Rust yields between
88
+ # reached assertions. No instance method runs during construction.
89
+ class Frame
90
+ include Evaluation
91
+
92
+ def initialize(rules, value, state)
93
+ @rules = rules
94
+ @value = value
95
+ @number_state = state
96
+ end
97
+
98
+ def numeric? = TypeChecks.number?(@value)
99
+ def integral? = TypeChecks.integral?(@value)
100
+
101
+ def valid?(required)
102
+ return required.zero? unless numeric?
103
+ if required == TypeChecks::INTEGER
104
+ return false unless integral?
105
+ end
106
+
107
+ valid_number?(@rules, @value)
108
+ end
109
+
110
+ def prepare
111
+ @actual = NumberChecks.actual(@rules, @value, @number_state)
112
+ true
113
+ end
114
+
115
+ def check(bit)
116
+ case bit
117
+ when MAXIMUM then NumberChecks.bound?(@actual, bit, @rules.maximum)
118
+ when MINIMUM then NumberChecks.bound?(@actual, bit, @rules.minimum)
119
+ when EXCLUSIVE_MAXIMUM then NumberChecks.bound?(@actual, bit, @rules.exclusive_maximum)
120
+ when EXCLUSIVE_MINIMUM then NumberChecks.bound?(@actual, bit, @rules.exclusive_minimum)
121
+ when MULTIPLE_OF then @number_state.multiple?(@rules, @value)
122
+ else raise "unknown numeric assertion"
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Schemurai
4
+ module Internal
5
+ module SchemaDomain
6
+ module_function def validate!(schema, label: "schema")
7
+ unless schema.equal?(true) || schema.equal?(false) || schema.instance_of?(Hash)
8
+ invalid!(label, "must be a boolean or an object")
9
+ end
10
+
11
+ validate_value!(schema, label, [], {}, {})
12
+ schema
13
+ end
14
+
15
+ module_function def validate_registry!(schemas)
16
+ invalid!("schemas", "must be an object") unless schemas.instance_of?(Hash)
17
+
18
+ schemas.each do |uri, schema|
19
+ invalid!("schemas", "contains a non-string URI key") unless uri.instance_of?(String)
20
+
21
+ validate!(schema, label: "schemas[#{uri.inspect}]")
22
+ end
23
+ schemas
24
+ end
25
+
26
+ module_function def validate_value!(value, label, path, ancestors, completed)
27
+ case value
28
+ when nil, true, false
29
+ nil
30
+ when String, Integer
31
+ invalid_at!(label, path, "uses a subclass of #{value.class.superclass}") unless supported_exact_scalar?(value)
32
+ when Float
33
+ invalid_at!(label, path, "must contain only finite numbers") unless value.finite?
34
+ invalid_at!(label, path, "uses a Float subclass") unless value.instance_of?(Float)
35
+ when Array
36
+ invalid_at!(label, path, "uses an Array subclass") unless value.instance_of?(Array)
37
+ descend!(value, label, path, ancestors, completed) do
38
+ value.each_with_index do |item, index|
39
+ path << index
40
+ validate_value!(item, label, path, ancestors, completed)
41
+ path.pop
42
+ end
43
+ end
44
+ when Hash
45
+ invalid_at!(label, path, "uses a Hash subclass") unless value.instance_of?(Hash)
46
+ descend!(value, label, path, ancestors, completed) do
47
+ value.each do |key, item|
48
+ invalid_at!(label, path, "contains a non-string object key") unless key.instance_of?(String)
49
+
50
+ path << key
51
+ validate_value!(item, label, path, ancestors, completed)
52
+ path.pop
53
+ end
54
+ end
55
+ else
56
+ invalid_at!(label, path, "contains unsupported #{value.class}")
57
+ end
58
+ end
59
+ private_class_method :validate_value!
60
+
61
+ module_function def supported_exact_scalar?(value)
62
+ value.instance_of?(String) || value.instance_of?(Integer)
63
+ end
64
+ private_class_method :supported_exact_scalar?
65
+
66
+ module_function def descend!(value, label, path, ancestors, completed)
67
+ # A completed subtree cannot contain a cycle back to this path. Only
68
+ # publish after the entire subtree passes, preserving first-error paths.
69
+ return if completed.key?(value.object_id)
70
+
71
+ invalid_at!(label, path, "contains a recursive container") if ancestors.key?(value.object_id)
72
+
73
+ ancestors[value.object_id] = true
74
+ yield
75
+ completed[value.object_id] = true
76
+ ensure
77
+ ancestors.delete(value.object_id)
78
+ end
79
+ private_class_method :descend!
80
+
81
+ module_function def invalid_at!(label, path, reason)
82
+ location = path.reduce(label.dup) do |result, segment|
83
+ result << "/" << segment.to_s.gsub("~", "~0").gsub("/", "~1")
84
+ end
85
+ invalid!(location, reason)
86
+ end
87
+ private_class_method :invalid_at!
88
+
89
+ module_function def invalid!(path, reason)
90
+ raise Error, "invalid JSON-shaped #{path}: #{reason}"
91
+ end
92
+ private_class_method :invalid!
93
+ end
94
+ end
95
+ end