schemurai 3.0.2 → 3.1.0
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/README.md +22 -3
- data/ext/schemurai_native/Cargo.lock +14 -3
- data/ext/schemurai_native/Cargo.toml +4 -3
- data/ext/schemurai_native/extconf.rb +4 -0
- data/ext/schemurai_native/src/diagnostics.rs +71 -112
- data/ext/schemurai_native/src/number/arbitrary.rs +57 -1
- data/ext/schemurai_native/src/number.rs +1 -1
- data/lib/schemurai/evaluation_limits.rb +65 -0
- data/lib/schemurai/limited_evaluator.rb +2 -59
- data/lib/schemurai/version.rb +1 -1
- data/lib/schemurai/vm/limited_evaluator.rb +2 -57
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 663b805a46f6c7bf28678bcc50b2ffae37d3337b3025710a75c0782a5d7c8182
|
|
4
|
+
data.tar.gz: 41a280754bfa72b41626e44176ef7cf0c983cc27a504aedf049759df005498b1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 88be3ae2e41ce4d46cbb5c79dc66e9bfe7d9041d63619b17dc27dc7977384679435df13f0e234701033eca5f829f9d4e7321b9b08ad8e9b6fdfdf0fa5d64c03c
|
|
7
|
+
data.tar.gz: a0ea620a03a983a0ad22319c4a22727c316ced09711bd8565a716d8ebcf4c523bc827e957b347c0b19886d487f7529d305b425f63e1e0ffa4b3b7d8f1822e406
|
data/README.md
CHANGED
|
@@ -288,9 +288,28 @@ scans use per-item evaluation so successful scans can poll.
|
|
|
288
288
|
`Schemurai.backend`, `SchemaRegistry#backend`, and `Validator#backend` expose
|
|
289
289
|
the selected backend. Pass `backend: :ruby` to force the Ruby oracle or
|
|
290
290
|
`backend: :vm` to ahead-of-time compile schemas for the validator virtual machine.
|
|
291
|
-
Pass `backend: :native` to use the experimental Rust VM extension.
|
|
292
|
-
|
|
293
|
-
|
|
291
|
+
Pass `backend: :native` to use the experimental Rust VM extension. Releases
|
|
292
|
+
include precompiled gems on RubyGems.org for CRuby 4.0 on these platforms:
|
|
293
|
+
|
|
294
|
+
| OS | CPU | libc | Gem platform |
|
|
295
|
+
| --- | --- | --- | --- |
|
|
296
|
+
| Linux | x64 | glibc 2.36+ | `x86_64-linux-gnu` |
|
|
297
|
+
| Linux | arm64 | glibc 2.36+ | `aarch64-linux-gnu` |
|
|
298
|
+
| Linux | x64 | musl (Alpine 3.23+) | `x86_64-linux-musl` |
|
|
299
|
+
| Linux | arm64 | musl (Alpine 3.23+) | `aarch64-linux-musl` |
|
|
300
|
+
| macOS 14+ | x64 | — | `x86_64-darwin` |
|
|
301
|
+
| macOS 14+ | arm64 | — | `arm64-darwin` |
|
|
302
|
+
|
|
303
|
+
RubyGems/Bundler selects the matching platform gem during normal installation;
|
|
304
|
+
these gems need no C compiler, Rust, or Ruby development headers. Each platform
|
|
305
|
+
gem bundles its supported Ruby minor ABIs and restricts `required_ruby_version`
|
|
306
|
+
to that range. Ruby patch releases share the same binary. To build from source,
|
|
307
|
+
use `gem install schemurai --platform ruby` or Bundler's `force_ruby_platform`.
|
|
308
|
+
See [native gem releases](docs/native-gem-releases.md) for the build matrix.
|
|
309
|
+
|
|
310
|
+
Building the native extension from source requires a C toolchain, Ruby
|
|
311
|
+
development headers, and Rust 1.93 or newer. If cargo is absent, installation of
|
|
312
|
+
the source gem skips the optional native
|
|
294
313
|
extension and Ruby/VM remain available; explicitly selecting `:native` then
|
|
295
314
|
raises `LoadError`. Native execution borrows Ruby values directly; its current coverage and
|
|
296
315
|
remaining VM delegation are documented below.
|
|
@@ -67,6 +67,16 @@ dependencies = [
|
|
|
67
67
|
"num-traits",
|
|
68
68
|
]
|
|
69
69
|
|
|
70
|
+
[[package]]
|
|
71
|
+
name = "num-bigint"
|
|
72
|
+
version = "0.5.1"
|
|
73
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
74
|
+
checksum = "93e7820bc0a80a0238e650327316f929ba18d5be054b647490a3a6a339f3e7c0"
|
|
75
|
+
dependencies = [
|
|
76
|
+
"num-integer",
|
|
77
|
+
"num-traits",
|
|
78
|
+
]
|
|
79
|
+
|
|
70
80
|
[[package]]
|
|
71
81
|
name = "num-integer"
|
|
72
82
|
version = "0.1.47"
|
|
@@ -82,7 +92,7 @@ version = "0.4.2"
|
|
|
82
92
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
83
93
|
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
|
84
94
|
dependencies = [
|
|
85
|
-
"num-bigint",
|
|
95
|
+
"num-bigint 0.4.8",
|
|
86
96
|
"num-integer",
|
|
87
97
|
"num-traits",
|
|
88
98
|
]
|
|
@@ -127,13 +137,14 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
|
|
127
137
|
|
|
128
138
|
[[package]]
|
|
129
139
|
name = "schemurai-native"
|
|
130
|
-
version = "3.0
|
|
140
|
+
version = "3.1.0"
|
|
131
141
|
dependencies = [
|
|
132
142
|
"base64-simd",
|
|
133
143
|
"base64-turbo",
|
|
134
144
|
"memchr",
|
|
135
|
-
"num-bigint",
|
|
145
|
+
"num-bigint 0.5.1",
|
|
136
146
|
"num-rational",
|
|
147
|
+
"num-traits",
|
|
137
148
|
"regex-automata",
|
|
138
149
|
"regex-syntax",
|
|
139
150
|
"ryu",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "schemurai-native"
|
|
3
|
-
version = "3.0
|
|
3
|
+
version = "3.1.0"
|
|
4
4
|
edition = "2024"
|
|
5
5
|
rust-version = "1.93"
|
|
6
6
|
publish = false
|
|
@@ -9,8 +9,9 @@ publish = false
|
|
|
9
9
|
base64-simd = "0.8.0"
|
|
10
10
|
base64-turbo = "0.5.0"
|
|
11
11
|
memchr = "2.8.3"
|
|
12
|
-
num-bigint = "0.
|
|
13
|
-
num-rational = "0.4.2"
|
|
12
|
+
num-bigint = "0.5.1"
|
|
13
|
+
num-rational = { version = "0.4.2", default-features = false, features = ["std"] }
|
|
14
|
+
num-traits = "0.2.19"
|
|
14
15
|
regex-automata = "0.4.18"
|
|
15
16
|
regex-syntax = "0.8.11"
|
|
16
17
|
ryu = "1.0.23"
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
require "mkmf"
|
|
4
4
|
|
|
5
|
+
# Platform gems resolve Ruby symbols from the running interpreter so the same
|
|
6
|
+
# binary also works with Rubies built without a shared libruby.
|
|
7
|
+
$LIBRUBYARG = "" unless enable_config("link-ruby", true) # rubocop:disable Style/GlobalVars -- mkmf build configuration.
|
|
8
|
+
|
|
5
9
|
unless find_executable("cargo")
|
|
6
10
|
warn "cargo was not found; skipping the optional native backend (Ruby and VM remain available)"
|
|
7
11
|
File.write("Makefile", dummy_makefile(".").join)
|
|
@@ -713,6 +713,75 @@ impl Execution<'_> {
|
|
|
713
713
|
if !self.budget.checkpoint() {
|
|
714
714
|
return Err(Unsupported);
|
|
715
715
|
}
|
|
716
|
+
// Container entry policy is shared; child traversal stays specific
|
|
717
|
+
// to each container's paths, assertions and annotation coverage.
|
|
718
|
+
let container = match op {
|
|
719
|
+
Op::Array(required, rules) => {
|
|
720
|
+
Some((*required, &rules.header, rules.header_only(), TYPE_ARRAY))
|
|
721
|
+
}
|
|
722
|
+
Op::Object(required, rules) => {
|
|
723
|
+
Some((*required, &rules.header, rules.header_only(), TYPE_OBJECT))
|
|
724
|
+
}
|
|
725
|
+
_ => None,
|
|
726
|
+
};
|
|
727
|
+
if let Some((required, header, header_only, expected_kind)) = container {
|
|
728
|
+
if self.caches.report_continuations_allowed()
|
|
729
|
+
&& !header_only
|
|
730
|
+
&& (kind == TYPE_UNSUPPORTED
|
|
731
|
+
|| self.caches.dynamic_calls()
|
|
732
|
+
|| prior.has_runtime_properties())
|
|
733
|
+
{
|
|
734
|
+
let result = self.caches.container_report(
|
|
735
|
+
node,
|
|
736
|
+
value,
|
|
737
|
+
(source.1, required, expected_kind == TYPE_OBJECT),
|
|
738
|
+
report,
|
|
739
|
+
&prior,
|
|
740
|
+
tracked,
|
|
741
|
+
)?;
|
|
742
|
+
if tracked {
|
|
743
|
+
prior.merge_diagnostics(self, result)?;
|
|
744
|
+
}
|
|
745
|
+
if report.silent && report.failed {
|
|
746
|
+
break;
|
|
747
|
+
}
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
if header_only && header.runtime_required(self.caches, kind) {
|
|
751
|
+
if report.collect_header(self.caches, header, required, value, source)? {
|
|
752
|
+
break;
|
|
753
|
+
}
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
if kind == expected_kind && !self.budget.checkpoint() {
|
|
757
|
+
return Err(Unsupported);
|
|
758
|
+
}
|
|
759
|
+
if required != 0 && kind != expected_kind {
|
|
760
|
+
report.add(self.caches, "type", source, value, 0)?;
|
|
761
|
+
}
|
|
762
|
+
if kind != expected_kind {
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
if report.silent {
|
|
766
|
+
let status = match op {
|
|
767
|
+
Op::Array(_, rules) => {
|
|
768
|
+
self.array_tracked(rules, value, depth + 1, active, &mut prior)
|
|
769
|
+
}
|
|
770
|
+
Op::Object(_, rules) => {
|
|
771
|
+
self.object_tracked(rules, value, depth + 1, active, &mut prior)
|
|
772
|
+
}
|
|
773
|
+
_ => unreachable!("container entry"),
|
|
774
|
+
};
|
|
775
|
+
if status == Unsupported {
|
|
776
|
+
return Err(Unsupported);
|
|
777
|
+
}
|
|
778
|
+
report.failed |= status == Invalid;
|
|
779
|
+
if report.failed {
|
|
780
|
+
break;
|
|
781
|
+
}
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
716
785
|
match op {
|
|
717
786
|
Op::Unsupported => return Err(Unsupported),
|
|
718
787
|
Op::UnresolvedRef(message) => {
|
|
@@ -842,62 +911,7 @@ impl Execution<'_> {
|
|
|
842
911
|
}
|
|
843
912
|
}
|
|
844
913
|
}
|
|
845
|
-
Op::Array(
|
|
846
|
-
if self.caches.report_continuations_allowed()
|
|
847
|
-
&& !rules.header_only()
|
|
848
|
-
&& (kind == TYPE_UNSUPPORTED
|
|
849
|
-
|| self.caches.dynamic_calls()
|
|
850
|
-
|| prior.has_runtime_properties())
|
|
851
|
-
{
|
|
852
|
-
let result = self.caches.container_report(
|
|
853
|
-
node,
|
|
854
|
-
value,
|
|
855
|
-
(source.1, *required, false),
|
|
856
|
-
report,
|
|
857
|
-
&prior,
|
|
858
|
-
tracked,
|
|
859
|
-
)?;
|
|
860
|
-
if tracked {
|
|
861
|
-
prior.merge_diagnostics(self, result)?;
|
|
862
|
-
}
|
|
863
|
-
if report.silent && report.failed {
|
|
864
|
-
break;
|
|
865
|
-
}
|
|
866
|
-
continue;
|
|
867
|
-
}
|
|
868
|
-
if rules.header_only() && rules.header.runtime_required(self.caches, kind) {
|
|
869
|
-
if report.collect_header(
|
|
870
|
-
self.caches,
|
|
871
|
-
&rules.header,
|
|
872
|
-
*required,
|
|
873
|
-
value,
|
|
874
|
-
source,
|
|
875
|
-
)? {
|
|
876
|
-
break;
|
|
877
|
-
}
|
|
878
|
-
continue;
|
|
879
|
-
}
|
|
880
|
-
if kind == TYPE_ARRAY && !self.budget.checkpoint() {
|
|
881
|
-
return Err(Unsupported);
|
|
882
|
-
}
|
|
883
|
-
if *required != 0 && kind != TYPE_ARRAY {
|
|
884
|
-
report.add(self.caches, "type", source, value, 0)?;
|
|
885
|
-
}
|
|
886
|
-
if kind != TYPE_ARRAY {
|
|
887
|
-
continue;
|
|
888
|
-
}
|
|
889
|
-
if report.silent {
|
|
890
|
-
let status =
|
|
891
|
-
self.array_tracked(rules, value, depth + 1, active, &mut prior);
|
|
892
|
-
if status == Unsupported {
|
|
893
|
-
return Err(Unsupported);
|
|
894
|
-
}
|
|
895
|
-
report.failed |= status == Invalid;
|
|
896
|
-
if report.failed {
|
|
897
|
-
break;
|
|
898
|
-
}
|
|
899
|
-
continue;
|
|
900
|
-
}
|
|
914
|
+
Op::Array(_, rules) => {
|
|
901
915
|
let mut coverage = Tracked::new(Valid);
|
|
902
916
|
let len = unsafe { sn_len(value) };
|
|
903
917
|
if rules.max.is_some_and(|max| len > max) {
|
|
@@ -1168,62 +1182,7 @@ impl Execution<'_> {
|
|
|
1168
1182
|
prior.merge_diagnostics(self, coverage)?;
|
|
1169
1183
|
}
|
|
1170
1184
|
}
|
|
1171
|
-
Op::Object(
|
|
1172
|
-
if self.caches.report_continuations_allowed()
|
|
1173
|
-
&& !rules.header_only()
|
|
1174
|
-
&& (kind == TYPE_UNSUPPORTED
|
|
1175
|
-
|| self.caches.dynamic_calls()
|
|
1176
|
-
|| prior.has_runtime_properties())
|
|
1177
|
-
{
|
|
1178
|
-
let result = self.caches.container_report(
|
|
1179
|
-
node,
|
|
1180
|
-
value,
|
|
1181
|
-
(source.1, *required, true),
|
|
1182
|
-
report,
|
|
1183
|
-
&prior,
|
|
1184
|
-
tracked,
|
|
1185
|
-
)?;
|
|
1186
|
-
if tracked {
|
|
1187
|
-
prior.merge_diagnostics(self, result)?;
|
|
1188
|
-
}
|
|
1189
|
-
if report.silent && report.failed {
|
|
1190
|
-
break;
|
|
1191
|
-
}
|
|
1192
|
-
continue;
|
|
1193
|
-
}
|
|
1194
|
-
if rules.header_only() && rules.header.runtime_required(self.caches, kind) {
|
|
1195
|
-
if report.collect_header(
|
|
1196
|
-
self.caches,
|
|
1197
|
-
&rules.header,
|
|
1198
|
-
*required,
|
|
1199
|
-
value,
|
|
1200
|
-
source,
|
|
1201
|
-
)? {
|
|
1202
|
-
break;
|
|
1203
|
-
}
|
|
1204
|
-
continue;
|
|
1205
|
-
}
|
|
1206
|
-
if kind == TYPE_OBJECT && !self.budget.checkpoint() {
|
|
1207
|
-
return Err(Unsupported);
|
|
1208
|
-
}
|
|
1209
|
-
if *required != 0 && kind != TYPE_OBJECT {
|
|
1210
|
-
report.add(self.caches, "type", source, value, 0)?;
|
|
1211
|
-
}
|
|
1212
|
-
if kind != TYPE_OBJECT {
|
|
1213
|
-
continue;
|
|
1214
|
-
}
|
|
1215
|
-
if report.silent {
|
|
1216
|
-
let status =
|
|
1217
|
-
self.object_tracked(rules, value, depth + 1, active, &mut prior);
|
|
1218
|
-
if status == Unsupported {
|
|
1219
|
-
return Err(Unsupported);
|
|
1220
|
-
}
|
|
1221
|
-
report.failed |= status == Invalid;
|
|
1222
|
-
if report.failed {
|
|
1223
|
-
break;
|
|
1224
|
-
}
|
|
1225
|
-
continue;
|
|
1226
|
-
}
|
|
1185
|
+
Op::Object(_, rules) => {
|
|
1227
1186
|
let mut coverage = Tracked::new(Valid);
|
|
1228
1187
|
let len = unsafe { sn_len(value) };
|
|
1229
1188
|
if rules.max.is_some_and(|max| len > max) {
|
|
@@ -3,8 +3,28 @@
|
|
|
3
3
|
//! method dispatch can invalidate the evaluator's borrowed instance storage.
|
|
4
4
|
use super::*;
|
|
5
5
|
use num_bigint::{BigInt, Sign};
|
|
6
|
+
use num_traits::float::FloatCore;
|
|
6
7
|
use std::borrow::Cow;
|
|
7
8
|
|
|
9
|
+
// Use the application's BigInt version rather than num-rational's optional one.
|
|
10
|
+
pub(super) type BigRational = num_rational::Ratio<BigInt>;
|
|
11
|
+
|
|
12
|
+
fn binary_float(value: f64) -> Option<BigRational> {
|
|
13
|
+
if !value.is_finite() {
|
|
14
|
+
return None;
|
|
15
|
+
}
|
|
16
|
+
let (mantissa, exponent, sign) = value.integer_decode();
|
|
17
|
+
let numerator = BigInt::from(mantissa) * BigInt::from(sign);
|
|
18
|
+
Some(if exponent < 0 {
|
|
19
|
+
BigRational::new(
|
|
20
|
+
numerator,
|
|
21
|
+
BigInt::from(1) << exponent.unsigned_abs() as usize,
|
|
22
|
+
)
|
|
23
|
+
} else {
|
|
24
|
+
BigRational::from_integer(numerator << exponent as usize)
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
8
28
|
fn integer(value: Value) -> BigInt {
|
|
9
29
|
if unsafe { sn_fixnum(value) } != 0 {
|
|
10
30
|
return BigInt::from(unsafe { sn_integer(value) });
|
|
@@ -98,7 +118,7 @@ impl Rules {
|
|
|
98
118
|
// in binary, but rounds Rational bounds to Float. With multipleOf,
|
|
99
119
|
// Ruby first converts the instance's decimal spelling to a Rational.
|
|
100
120
|
let binary = if actual.is_none() && self.limits.iter().any(|(_, limit)| limit.integer) {
|
|
101
|
-
|
|
121
|
+
binary_float(n)
|
|
102
122
|
} else {
|
|
103
123
|
None
|
|
104
124
|
};
|
|
@@ -152,3 +172,39 @@ impl Rules {
|
|
|
152
172
|
Ok(bits)
|
|
153
173
|
}
|
|
154
174
|
}
|
|
175
|
+
|
|
176
|
+
#[cfg(test)]
|
|
177
|
+
mod tests {
|
|
178
|
+
use super::*;
|
|
179
|
+
|
|
180
|
+
#[test]
|
|
181
|
+
fn binary_float_preserves_exact_values() {
|
|
182
|
+
let cases = [
|
|
183
|
+
(0.0, BigRational::from_integer(BigInt::from(0))),
|
|
184
|
+
(-0.0, BigRational::from_integer(BigInt::from(0))),
|
|
185
|
+
(
|
|
186
|
+
0.1,
|
|
187
|
+
BigRational::new(BigInt::from(3602879701896397_u64), BigInt::from(1) << 55),
|
|
188
|
+
),
|
|
189
|
+
(
|
|
190
|
+
f64::from_bits(1),
|
|
191
|
+
BigRational::new(BigInt::from(1), BigInt::from(1) << 1074),
|
|
192
|
+
),
|
|
193
|
+
(
|
|
194
|
+
f64::MIN_POSITIVE,
|
|
195
|
+
BigRational::new(BigInt::from(1), BigInt::from(1) << 1022),
|
|
196
|
+
),
|
|
197
|
+
(
|
|
198
|
+
f64::MAX,
|
|
199
|
+
BigRational::from_integer(BigInt::from((1_u64 << 53) - 1) << 971),
|
|
200
|
+
),
|
|
201
|
+
];
|
|
202
|
+
for (value, expected) in cases {
|
|
203
|
+
assert_eq!(binary_float(value), Some(expected.clone()), "{value}");
|
|
204
|
+
assert_eq!(binary_float(-value), Some(-expected), "{}", -value);
|
|
205
|
+
}
|
|
206
|
+
for value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
|
|
207
|
+
assert_eq!(binary_float(value), None);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//! multipleOf semantics. All evaluation helpers operate under the GVL and
|
|
3
3
|
//! allocate no Ruby objects; unsupported magnitudes retain VM compatibility.
|
|
4
4
|
use super::*;
|
|
5
|
-
use
|
|
5
|
+
use arbitrary::BigRational;
|
|
6
6
|
use std::cmp::Ordering;
|
|
7
7
|
mod arbitrary;
|
|
8
8
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "limit_budget"
|
|
4
|
+
|
|
5
|
+
module Schemurai
|
|
6
|
+
module Internal
|
|
7
|
+
# Limit policy shared by evaluators; error storage and scalar state stay local.
|
|
8
|
+
module EvaluationLimits
|
|
9
|
+
def validate(instance, max_errors: nil, timeout: nil, budget: nil)
|
|
10
|
+
with_budget(max_errors, timeout, budget) { super(instance) }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def valid?(instance, max_errors: nil, timeout: nil, budget: nil)
|
|
14
|
+
with_budget(max_errors, timeout, budget) { super(instance) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private def with_budget(max_errors, timeout, budget)
|
|
18
|
+
@budget = budget || LimitBudget.new(max_errors: max_errors, timeout: timeout)
|
|
19
|
+
result = yield
|
|
20
|
+
@budget.check_deadline!
|
|
21
|
+
result
|
|
22
|
+
ensure
|
|
23
|
+
@budget = nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private def evaluate(node, instance)
|
|
27
|
+
@budget.checkpoint
|
|
28
|
+
super
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private def evaluate_valid(node, instance)
|
|
32
|
+
@budget.checkpoint
|
|
33
|
+
super
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private def valid_string?(node, value)
|
|
37
|
+
@budget.check_deadline!
|
|
38
|
+
result = super
|
|
39
|
+
@budget.check_deadline!
|
|
40
|
+
result
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private def check_string(node, value)
|
|
44
|
+
@budget.check_deadline!
|
|
45
|
+
result = super
|
|
46
|
+
@budget.check_deadline!
|
|
47
|
+
result
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private def valid_content?(schema, value)
|
|
51
|
+
@budget.check_deadline!
|
|
52
|
+
result = super
|
|
53
|
+
@budget.check_deadline!
|
|
54
|
+
result
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private def check_content(schema, value)
|
|
58
|
+
@budget.check_deadline!
|
|
59
|
+
result = super
|
|
60
|
+
@budget.check_deadline!
|
|
61
|
+
result
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -1,63 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "limit_exceeded"
|
|
4
|
-
require_relative "
|
|
4
|
+
require_relative "evaluation_limits"
|
|
5
5
|
require_relative "evaluator"
|
|
6
6
|
|
|
7
7
|
module Schemurai
|
|
8
8
|
module Internal
|
|
9
9
|
# Ruby evaluation with invocation-local limits.
|
|
10
10
|
class LimitedEvaluator < Evaluator
|
|
11
|
-
|
|
12
|
-
@budget = budget || LimitBudget.new(max_errors: max_errors, timeout: timeout)
|
|
13
|
-
errors = []
|
|
14
|
-
each_error(instance) do |error|
|
|
15
|
-
errors << error
|
|
16
|
-
end
|
|
17
|
-
@budget.check_deadline!
|
|
18
|
-
Result.new(errors)
|
|
19
|
-
ensure
|
|
20
|
-
@budget = nil
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def valid?(instance, max_errors: nil, timeout: nil, budget: nil)
|
|
24
|
-
@budget = budget || LimitBudget.new(max_errors: max_errors, timeout: timeout)
|
|
25
|
-
result = super(instance)
|
|
26
|
-
@budget.check_deadline!
|
|
27
|
-
result
|
|
28
|
-
ensure
|
|
29
|
-
@budget = nil
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
private def evaluate(node, instance)
|
|
33
|
-
@budget.checkpoint
|
|
34
|
-
super
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
private def evaluate_valid(node, instance)
|
|
38
|
-
@budget.checkpoint
|
|
39
|
-
super
|
|
40
|
-
end
|
|
11
|
+
include EvaluationLimits
|
|
41
12
|
|
|
42
13
|
private def add_error(keyword, message = nil, append_keyword: true, &block)
|
|
43
14
|
@budget.reserve_error if @error_callback
|
|
44
15
|
super
|
|
45
16
|
end
|
|
46
17
|
|
|
47
|
-
private def valid_string?(node, value)
|
|
48
|
-
@budget.check_deadline!
|
|
49
|
-
result = super
|
|
50
|
-
@budget.check_deadline!
|
|
51
|
-
result
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
private def check_string(node, value)
|
|
55
|
-
@budget.check_deadline!
|
|
56
|
-
result = super
|
|
57
|
-
@budget.check_deadline!
|
|
58
|
-
result
|
|
59
|
-
end
|
|
60
|
-
|
|
61
18
|
private def decimal(value)
|
|
62
19
|
@budget.check_deadline!
|
|
63
20
|
result = super
|
|
@@ -72,20 +29,6 @@ module Schemurai
|
|
|
72
29
|
result
|
|
73
30
|
end
|
|
74
31
|
|
|
75
|
-
private def valid_content?(schema, value)
|
|
76
|
-
@budget.check_deadline!
|
|
77
|
-
result = super
|
|
78
|
-
@budget.check_deadline!
|
|
79
|
-
result
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
private def check_content(schema, value)
|
|
83
|
-
@budget.check_deadline!
|
|
84
|
-
result = super
|
|
85
|
-
@budget.check_deadline!
|
|
86
|
-
result
|
|
87
|
-
end
|
|
88
|
-
|
|
89
32
|
private def json_equal?(left, right)
|
|
90
33
|
@budget.checkpoint
|
|
91
34
|
super
|
data/lib/schemurai/version.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../limit_exceeded"
|
|
4
|
-
require_relative "../
|
|
4
|
+
require_relative "../evaluation_limits"
|
|
5
5
|
require_relative "evaluator"
|
|
6
6
|
|
|
7
7
|
module Schemurai
|
|
@@ -10,73 +10,18 @@ module Schemurai
|
|
|
10
10
|
class LimitedEvaluator < Evaluator
|
|
11
11
|
include Internal::ValueEquality::Checkpoints
|
|
12
12
|
include Internal::StringChecks::TimedRegexps
|
|
13
|
-
|
|
14
|
-
def validate(instance, max_errors: nil, timeout: nil, budget: nil)
|
|
15
|
-
@budget = budget || Internal::LimitBudget.new(max_errors: max_errors, timeout: timeout)
|
|
16
|
-
result = super(instance)
|
|
17
|
-
@budget.check_deadline!
|
|
18
|
-
result
|
|
19
|
-
ensure
|
|
20
|
-
@budget = nil
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def valid?(instance, max_errors: nil, timeout: nil, budget: nil)
|
|
24
|
-
@budget = budget || Internal::LimitBudget.new(max_errors: max_errors, timeout: timeout)
|
|
25
|
-
result = super(instance)
|
|
26
|
-
@budget.check_deadline!
|
|
27
|
-
result
|
|
28
|
-
ensure
|
|
29
|
-
@budget = nil
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
private def evaluate(node, instance)
|
|
33
|
-
@budget.checkpoint
|
|
34
|
-
super
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
private def evaluate_valid(node, instance)
|
|
38
|
-
@budget.checkpoint
|
|
39
|
-
super
|
|
40
|
-
end
|
|
13
|
+
include Internal::EvaluationLimits
|
|
41
14
|
|
|
42
15
|
private def add_error(keyword, message = nil, append_keyword: true, &block)
|
|
43
16
|
@budget.reserve_error if @errors
|
|
44
17
|
super
|
|
45
18
|
end
|
|
46
19
|
|
|
47
|
-
private def valid_string?(node, value)
|
|
48
|
-
@budget.check_deadline!
|
|
49
|
-
result = super
|
|
50
|
-
@budget.check_deadline!
|
|
51
|
-
result
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
private def check_string(node, value)
|
|
55
|
-
@budget.check_deadline!
|
|
56
|
-
result = super
|
|
57
|
-
@budget.check_deadline!
|
|
58
|
-
result
|
|
59
|
-
end
|
|
60
|
-
|
|
61
20
|
private def number_state
|
|
62
21
|
state = super
|
|
63
22
|
state.budget = @budget
|
|
64
23
|
state
|
|
65
24
|
end
|
|
66
|
-
|
|
67
|
-
private def valid_content?(schema, value)
|
|
68
|
-
@budget.check_deadline!
|
|
69
|
-
result = super
|
|
70
|
-
@budget.check_deadline!
|
|
71
|
-
result
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
private def check_content(schema, value)
|
|
75
|
-
@budget.check_deadline!
|
|
76
|
-
result = super
|
|
77
|
-
@budget.check_deadline!
|
|
78
|
-
result
|
|
79
|
-
end
|
|
80
25
|
end
|
|
81
26
|
end
|
|
82
27
|
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: 3.0
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kuya KOHARA (oakcask)
|
|
@@ -84,6 +84,7 @@ files:
|
|
|
84
84
|
- lib/schemurai/ecma_regexp.rb
|
|
85
85
|
- lib/schemurai/error_message.rb
|
|
86
86
|
- lib/schemurai/evaluation.rb
|
|
87
|
+
- lib/schemurai/evaluation_limits.rb
|
|
87
88
|
- lib/schemurai/evaluation_pool.rb
|
|
88
89
|
- lib/schemurai/evaluator.rb
|
|
89
90
|
- lib/schemurai/formats.rb
|
|
@@ -117,7 +118,7 @@ licenses:
|
|
|
117
118
|
metadata:
|
|
118
119
|
homepage_uri: https://github.com/oakcask/schemurai
|
|
119
120
|
source_code_uri: https://github.com/oakcask/schemurai/tree/main
|
|
120
|
-
documentation_uri: https://rubydoc.info/gems/schemurai/3.0
|
|
121
|
+
documentation_uri: https://rubydoc.info/gems/schemurai/3.1.0
|
|
121
122
|
rubygems_mfa_required: 'true'
|
|
122
123
|
rdoc_options:
|
|
123
124
|
- "--main"
|