schemurai 3.0.0 → 3.0.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/README.md +6 -4
- data/ext/schemurai_native/Cargo.lock +55 -1
- data/ext/schemurai_native/Cargo.toml +4 -1
- data/ext/schemurai_native/extconf.rb +1 -1
- data/ext/schemurai_native/glue.c +1679 -62
- data/ext/schemurai_native/protocol.def +45 -0
- data/ext/schemurai_native/src/budget.rs +49 -0
- data/ext/schemurai_native/src/container.rs +460 -0
- data/ext/schemurai_native/src/content.rs +74 -27
- data/ext/schemurai_native/src/diagnostics/array.rs +392 -0
- data/ext/schemurai_native/src/diagnostics/object.rs +218 -0
- data/ext/schemurai_native/src/diagnostics.rs +1087 -307
- data/ext/schemurai_native/src/enumeration.rs +6 -1
- data/ext/schemurai_native/src/evaluation.rs +49 -20
- data/ext/schemurai_native/src/format.rs +126 -14
- data/ext/schemurai_native/src/graph.rs +37 -0
- data/ext/schemurai_native/src/instance_graph.rs +90 -0
- data/ext/schemurai_native/src/integer_scan.rs +132 -0
- data/ext/schemurai_native/src/lib.rs +917 -238
- data/ext/schemurai_native/src/memo.rs +11 -38
- data/ext/schemurai_native/src/number/arbitrary.rs +154 -0
- data/ext/schemurai_native/src/number.rs +250 -59
- data/ext/schemurai_native/src/pattern.rs +147 -109
- data/ext/schemurai_native/src/pointer.rs +97 -0
- data/ext/schemurai_native/src/runtime/properties.rs +162 -0
- data/ext/schemurai_native/src/runtime/reports.rs +266 -0
- data/ext/schemurai_native/src/runtime/scopes.rs +430 -0
- data/ext/schemurai_native/src/runtime.rs +1020 -0
- data/ext/schemurai_native/src/scope.rs +60 -16
- data/ext/schemurai_native/src/string.rs +68 -0
- data/ext/schemurai_native/src/tracking.rs +244 -174
- data/lib/schemurai/container_checks/array_diagnostics.rb +119 -0
- data/lib/schemurai/container_checks/object_diagnostics.rb +152 -0
- data/lib/schemurai/container_checks.rb +534 -0
- data/lib/schemurai/decimal.rb +11 -0
- data/lib/schemurai/dialect.rb +0 -12
- data/lib/schemurai/ecma_regexp.rb +46 -0
- data/lib/schemurai/evaluation_pool.rb +66 -0
- data/lib/schemurai/evaluator.rb +9 -48
- data/lib/schemurai/json_pointer.rb +11 -0
- data/lib/schemurai/native/diagnostics.rb +4 -3
- data/lib/schemurai/native/evaluator.rb +38 -10
- data/lib/schemurai/native/null_numeric.rb +23 -0
- data/lib/schemurai/number_checks.rb +128 -0
- data/lib/schemurai/string_checks.rb +166 -0
- data/lib/schemurai/type_checks.rb +59 -0
- data/lib/schemurai/value_equality.rb +72 -0
- data/lib/schemurai/version.rb +1 -1
- data/lib/schemurai/vm/compiler.rb +18 -26
- data/lib/schemurai/vm/evaluator.rb +87 -545
- data/lib/schemurai/vm/limited_evaluator.rb +7 -22
- metadata +25 -2
|
@@ -2,7 +2,9 @@
|
|
|
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 num_rational::BigRational;
|
|
5
6
|
use std::cmp::Ordering;
|
|
7
|
+
mod arbitrary;
|
|
6
8
|
|
|
7
9
|
// Strictly ordered integers are unique without fingerprinting their digits.
|
|
8
10
|
// Each comparison checks both original handles; mixed or unordered inputs
|
|
@@ -241,7 +243,12 @@ impl Fraction {
|
|
|
241
243
|
}
|
|
242
244
|
}
|
|
243
245
|
|
|
244
|
-
|
|
246
|
+
struct Decimal {
|
|
247
|
+
coefficient: i128,
|
|
248
|
+
power: i32,
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
fn decimal_parts(value: f64) -> Option<Decimal> {
|
|
245
252
|
if !value.is_finite() {
|
|
246
253
|
return None;
|
|
247
254
|
}
|
|
@@ -286,12 +293,21 @@ fn decimal_float(value: f64) -> Option<Fraction> {
|
|
|
286
293
|
.checked_add(i128::from(byte.checked_sub(b'0')?))?;
|
|
287
294
|
}
|
|
288
295
|
if numerator == 0 {
|
|
289
|
-
return Some(
|
|
290
|
-
|
|
291
|
-
|
|
296
|
+
return Some(Decimal {
|
|
297
|
+
coefficient: 0,
|
|
298
|
+
power: 0,
|
|
292
299
|
});
|
|
293
300
|
}
|
|
294
301
|
let power = exponent + trailing - fractional as i32;
|
|
302
|
+
Some(Decimal {
|
|
303
|
+
coefficient: if negative { -numerator } else { numerator },
|
|
304
|
+
power,
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
fn decimal_float(value: f64) -> Option<Fraction> {
|
|
309
|
+
let Decimal { coefficient, power } = decimal_parts(value)?;
|
|
310
|
+
let mut numerator = coefficient.abs();
|
|
295
311
|
let mut denominator = 1;
|
|
296
312
|
if power >= 0 {
|
|
297
313
|
numerator = numerator.checked_mul(10_i128.checked_pow(power as u32)?)?;
|
|
@@ -303,7 +319,7 @@ fn decimal_float(value: f64) -> Option<Fraction> {
|
|
|
303
319
|
(a, b) = (b, a % b);
|
|
304
320
|
}
|
|
305
321
|
Some(Fraction {
|
|
306
|
-
numerator: if
|
|
322
|
+
numerator: if coefficient < 0 {
|
|
307
323
|
-numerator / a
|
|
308
324
|
} else {
|
|
309
325
|
numerator / a
|
|
@@ -311,9 +327,11 @@ fn decimal_float(value: f64) -> Option<Fraction> {
|
|
|
311
327
|
denominator: denominator / a,
|
|
312
328
|
})
|
|
313
329
|
}
|
|
314
|
-
#[derive(Debug)]
|
|
330
|
+
#[derive(Debug, Default)]
|
|
315
331
|
struct Limit {
|
|
332
|
+
null: bool,
|
|
316
333
|
exact: Option<Fraction>,
|
|
334
|
+
arbitrary: Option<Box<BigRational>>,
|
|
317
335
|
wide_integer: Option<Value>,
|
|
318
336
|
wide_rounded_order: Option<Ordering>,
|
|
319
337
|
rounded: f64,
|
|
@@ -322,6 +340,24 @@ struct Limit {
|
|
|
322
340
|
}
|
|
323
341
|
|
|
324
342
|
impl Limit {
|
|
343
|
+
fn violation(
|
|
344
|
+
&self,
|
|
345
|
+
caches: &runtime::Caches,
|
|
346
|
+
value: Value,
|
|
347
|
+
bit: i32,
|
|
348
|
+
decimal: bool,
|
|
349
|
+
native: impl FnOnce() -> bool,
|
|
350
|
+
) -> Result<bool, Verdict> {
|
|
351
|
+
if self.null {
|
|
352
|
+
match caches.null_number(value, bit, decimal) {
|
|
353
|
+
Valid => Ok(false),
|
|
354
|
+
Invalid => Ok(true),
|
|
355
|
+
Unsupported => Err(Unsupported),
|
|
356
|
+
}
|
|
357
|
+
} else {
|
|
358
|
+
Ok(native())
|
|
359
|
+
}
|
|
360
|
+
}
|
|
325
361
|
fn float_order(&self, n: f64) -> Result<Option<Ordering>, Verdict> {
|
|
326
362
|
if let Some(exact) = self.exact {
|
|
327
363
|
return Ok(compare_float_integer(n, exact.numerator));
|
|
@@ -358,6 +394,7 @@ impl Limit {
|
|
|
358
394
|
}
|
|
359
395
|
#[derive(Debug)]
|
|
360
396
|
pub(super) struct Rules {
|
|
397
|
+
source: Value,
|
|
361
398
|
limits: Vec<(i32, Limit)>,
|
|
362
399
|
multiple: Option<Limit>,
|
|
363
400
|
pub(super) fast_limits: Option<Vec<(i32, i64)>>,
|
|
@@ -365,6 +402,7 @@ pub(super) struct Rules {
|
|
|
365
402
|
fixnum_flag: Value,
|
|
366
403
|
bounds_view: bool,
|
|
367
404
|
bounded_signs: Option<[bool; 2]>,
|
|
405
|
+
runtime_operands: bool,
|
|
368
406
|
}
|
|
369
407
|
|
|
370
408
|
#[derive(Debug)]
|
|
@@ -470,7 +508,11 @@ impl Engine {
|
|
|
470
508
|
.any(|(bit, _)| matches!(*bit, MAXIMUM | EXCLUSIVE_MAXIMUM)),
|
|
471
509
|
]
|
|
472
510
|
});
|
|
511
|
+
let runtime_operands = limits.iter().any(|(_, limit)| limit.null)
|
|
512
|
+
|| multiple.as_ref().is_some_and(|limit| limit.null);
|
|
473
513
|
Ok(Some(Rules {
|
|
514
|
+
source: operand,
|
|
515
|
+
runtime_operands,
|
|
474
516
|
limits,
|
|
475
517
|
multiple,
|
|
476
518
|
fast_limits,
|
|
@@ -482,25 +524,32 @@ impl Engine {
|
|
|
482
524
|
}
|
|
483
525
|
|
|
484
526
|
fn numeric_limit(&mut self, value: Value) -> Result<Option<Limit>, Value> {
|
|
527
|
+
if unsafe { sn_same(value, sn_nil()) } != 0 {
|
|
528
|
+
return Ok(Some(Limit {
|
|
529
|
+
null: true,
|
|
530
|
+
..Limit::default()
|
|
531
|
+
}));
|
|
532
|
+
}
|
|
485
533
|
let integer = unsafe { sn_kind(value) } & TYPE_INTEGER != 0;
|
|
486
534
|
if !integer && unsafe { sn_is_rational(value) } == 0 {
|
|
487
535
|
return Ok(None);
|
|
488
536
|
}
|
|
489
|
-
let
|
|
490
|
-
|
|
491
|
-
numerator,
|
|
492
|
-
denominator: 1,
|
|
493
|
-
})
|
|
537
|
+
let (numerator, denominator) = if integer {
|
|
538
|
+
(value, None)
|
|
494
539
|
} else {
|
|
495
540
|
let numerator = self.field(value, b"numerator\0")?;
|
|
496
541
|
let denominator = self.field(value, b"denominator\0")?;
|
|
497
|
-
|
|
498
|
-
|(numerator, denominator)| Fraction {
|
|
499
|
-
numerator,
|
|
500
|
-
denominator,
|
|
501
|
-
},
|
|
502
|
-
)
|
|
542
|
+
(numerator, Some(denominator))
|
|
503
543
|
};
|
|
544
|
+
let exact = read_integer(numerator)
|
|
545
|
+
.zip(denominator.map_or(Some(1), read_integer))
|
|
546
|
+
.map(|(numerator, denominator)| Fraction {
|
|
547
|
+
numerator,
|
|
548
|
+
denominator,
|
|
549
|
+
});
|
|
550
|
+
let arbitrary = exact
|
|
551
|
+
.is_none()
|
|
552
|
+
.then(|| Box::new(arbitrary::fraction(numerator, denominator)));
|
|
504
553
|
let rounded = self.field(value, b"to_f\0")?;
|
|
505
554
|
let rounded = unsafe { sn_float(rounded) };
|
|
506
555
|
let wide_rounded_order = if integer && exact.is_none() {
|
|
@@ -512,7 +561,9 @@ impl Engine {
|
|
|
512
561
|
fraction.numerator > 1 && (1_u128 << 64) % fraction.numerator as u128 == 1
|
|
513
562
|
});
|
|
514
563
|
Ok(Some(Limit {
|
|
564
|
+
null: false,
|
|
515
565
|
exact,
|
|
566
|
+
arbitrary,
|
|
516
567
|
wide_integer: (integer && exact.is_none()).then(|| self.retain(value)),
|
|
517
568
|
wide_rounded_order,
|
|
518
569
|
rounded,
|
|
@@ -586,12 +637,24 @@ fn wide_integer_multiple(value: Value, divisor: Fraction, sum_words: bool) -> Ve
|
|
|
586
637
|
}
|
|
587
638
|
let mut words = [0_u64; 16];
|
|
588
639
|
if unsafe { sn_integer1024(value, words.as_mut_ptr()) }.abs() > 1 {
|
|
589
|
-
return
|
|
640
|
+
return unbounded_integer_multiple(value, divisor, sum_words);
|
|
590
641
|
}
|
|
591
642
|
wide_multiple_words(&words, divisor, sum_words)
|
|
592
643
|
}
|
|
593
644
|
|
|
594
|
-
fn
|
|
645
|
+
fn unbounded_integer_multiple(value: Value, divisor: Fraction, sum_words: bool) -> Verdict {
|
|
646
|
+
let (words, _) = unbounded_integer_words(value);
|
|
647
|
+
wide_multiple_words(&words, divisor, sum_words)
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
fn unbounded_integer_words(value: Value) -> (Vec<u64>, i32) {
|
|
651
|
+
let count = unsafe { sn_integer_word_count(value) };
|
|
652
|
+
let mut words = vec![0_u64; count];
|
|
653
|
+
let sign = unsafe { sn_integer_words(value, words.as_mut_ptr(), count) };
|
|
654
|
+
(words, sign)
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
fn wide_multiple_words(words: &[u64], divisor: Fraction, sum_words: bool) -> Verdict {
|
|
595
658
|
let len = words
|
|
596
659
|
.iter()
|
|
597
660
|
.rposition(|&word| word != 0)
|
|
@@ -600,8 +663,8 @@ fn wide_multiple_words(words: &[u64; 16], divisor: Fraction, sum_words: bool) ->
|
|
|
600
663
|
let mut remainder = 0_u128;
|
|
601
664
|
if sum_words {
|
|
602
665
|
// If base 2^64 is 1 modulo the divisor, a magnitude has the same
|
|
603
|
-
// remainder as the sum of its words.
|
|
604
|
-
//
|
|
666
|
+
// remainder as the sum of its words. An addressable slice of u64 words
|
|
667
|
+
// sums within u128, so one remainder replaces per-word division.
|
|
605
668
|
remainder = words[..len]
|
|
606
669
|
.iter()
|
|
607
670
|
.map(|&word| u128::from(word))
|
|
@@ -630,6 +693,15 @@ fn wide_multiple_words(words: &[u64; 16], divisor: Fraction, sum_words: bool) ->
|
|
|
630
693
|
verdict(remainder == 0)
|
|
631
694
|
}
|
|
632
695
|
|
|
696
|
+
fn violates(bit: i32, order: Option<Ordering>) -> bool {
|
|
697
|
+
match bit {
|
|
698
|
+
MAXIMUM => order == Some(Ordering::Greater),
|
|
699
|
+
MINIMUM => order == Some(Ordering::Less),
|
|
700
|
+
EXCLUSIVE_MAXIMUM => matches!(order, Some(Ordering::Greater | Ordering::Equal)),
|
|
701
|
+
_ => matches!(order, Some(Ordering::Less | Ordering::Equal)),
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
633
705
|
fn fixnum_bounds(n: i64, limits: &[(i32, i64)]) -> Verdict {
|
|
634
706
|
verdict(limits.iter().all(|&(bit, limit)| match bit {
|
|
635
707
|
MAXIMUM => n <= limit,
|
|
@@ -700,7 +772,62 @@ fn wide_float_tie(value: Value, rounded: f64) -> Option<Ordering> {
|
|
|
700
772
|
impl Rules {
|
|
701
773
|
// Shared by validity scans and diagnostic prechecks. Numeric traversal
|
|
702
774
|
// borrows only opaque VALUEs and uses the compiled primitive shortcuts.
|
|
703
|
-
pub(super) fn
|
|
775
|
+
pub(super) fn runtime_valid(
|
|
776
|
+
&self,
|
|
777
|
+
caches: &runtime::Caches,
|
|
778
|
+
required: i32,
|
|
779
|
+
value: Value,
|
|
780
|
+
) -> Verdict {
|
|
781
|
+
caches
|
|
782
|
+
.number(self.source, value, required)
|
|
783
|
+
.map_or(Unsupported, |frame| frame.step(0))
|
|
784
|
+
}
|
|
785
|
+
pub(super) fn runtime_failures(
|
|
786
|
+
&self,
|
|
787
|
+
caches: &runtime::Caches,
|
|
788
|
+
required: i32,
|
|
789
|
+
value: Value,
|
|
790
|
+
mut visit: impl FnMut(&'static str) -> Result<(), Verdict>,
|
|
791
|
+
) -> Result<(), Verdict> {
|
|
792
|
+
let frame = caches.number(self.source, value, required)?;
|
|
793
|
+
if !frame.predicate(1)? {
|
|
794
|
+
if required != 0 {
|
|
795
|
+
visit("type")?;
|
|
796
|
+
}
|
|
797
|
+
return Ok(());
|
|
798
|
+
}
|
|
799
|
+
if required == TYPE_INTEGER && !frame.predicate(2)? {
|
|
800
|
+
visit("type")?;
|
|
801
|
+
}
|
|
802
|
+
if frame.step(3) != Valid {
|
|
803
|
+
return Err(Unsupported);
|
|
804
|
+
}
|
|
805
|
+
for (bit, keyword, step) in [
|
|
806
|
+
(MAXIMUM, "maximum", 4),
|
|
807
|
+
(MINIMUM, "minimum", 5),
|
|
808
|
+
(EXCLUSIVE_MAXIMUM, "exclusiveMaximum", 6),
|
|
809
|
+
(EXCLUSIVE_MINIMUM, "exclusiveMinimum", 7),
|
|
810
|
+
(MULTIPLE_OF, "multipleOf", 8),
|
|
811
|
+
] {
|
|
812
|
+
if ((bit == MULTIPLE_OF && self.multiple.is_some())
|
|
813
|
+
|| self.limits.iter().any(|&(candidate, _)| candidate == bit))
|
|
814
|
+
&& !frame.predicate(step)?
|
|
815
|
+
{
|
|
816
|
+
visit(keyword)?;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
Ok(())
|
|
820
|
+
}
|
|
821
|
+
pub(super) fn runtime_operands(&self) -> bool {
|
|
822
|
+
self.runtime_operands
|
|
823
|
+
}
|
|
824
|
+
pub(super) fn eval_guarded(
|
|
825
|
+
&self,
|
|
826
|
+
caches: &runtime::Caches,
|
|
827
|
+
required: i32,
|
|
828
|
+
value: Value,
|
|
829
|
+
budget: &budget::Budget,
|
|
830
|
+
) -> Verdict {
|
|
704
831
|
let fixnum = value & self.fixnum_flag != 0;
|
|
705
832
|
if fixnum {
|
|
706
833
|
if required != 0 && required & TYPE_INTEGRAL_NUMBER == 0 {
|
|
@@ -728,7 +855,7 @@ impl Rules {
|
|
|
728
855
|
} else if kind & TYPE_NUMBER == 0 {
|
|
729
856
|
Valid
|
|
730
857
|
} else {
|
|
731
|
-
self.eval(value)
|
|
858
|
+
self.eval(caches, value, budget)
|
|
732
859
|
}
|
|
733
860
|
}
|
|
734
861
|
|
|
@@ -783,12 +910,7 @@ impl Rules {
|
|
|
783
910
|
Ok(order) => order,
|
|
784
911
|
Err(status) => return status,
|
|
785
912
|
};
|
|
786
|
-
if
|
|
787
|
-
MAXIMUM => order == Some(Ordering::Greater),
|
|
788
|
-
MINIMUM => order == Some(Ordering::Less),
|
|
789
|
-
EXCLUSIVE_MAXIMUM => matches!(order, Some(Ordering::Greater | Ordering::Equal)),
|
|
790
|
-
_ => matches!(order, Some(Ordering::Less | Ordering::Equal)),
|
|
791
|
-
} {
|
|
913
|
+
if violates(bit, order) {
|
|
792
914
|
return Invalid;
|
|
793
915
|
}
|
|
794
916
|
}
|
|
@@ -810,19 +932,24 @@ impl Rules {
|
|
|
810
932
|
let Some(order) = limit.integer_order(value, integer, sign) else {
|
|
811
933
|
return Unsupported;
|
|
812
934
|
};
|
|
813
|
-
if
|
|
814
|
-
MAXIMUM => order == Ordering::Greater,
|
|
815
|
-
MINIMUM => order == Ordering::Less,
|
|
816
|
-
EXCLUSIVE_MAXIMUM => order != Ordering::Less,
|
|
817
|
-
_ => order != Ordering::Greater,
|
|
818
|
-
} {
|
|
935
|
+
if violates(bit, Some(order)) {
|
|
819
936
|
return Invalid;
|
|
820
937
|
}
|
|
821
938
|
}
|
|
822
939
|
Valid
|
|
823
940
|
}
|
|
824
941
|
|
|
825
|
-
pub(super) fn eval(
|
|
942
|
+
pub(super) fn eval(
|
|
943
|
+
&self,
|
|
944
|
+
caches: &runtime::Caches,
|
|
945
|
+
value: Value,
|
|
946
|
+
budget: &budget::Budget,
|
|
947
|
+
) -> Verdict {
|
|
948
|
+
if self.runtime_operands {
|
|
949
|
+
return self
|
|
950
|
+
.arbitrary_check(caches, value, budget, |_| true)
|
|
951
|
+
.map_or(Unsupported, |bits| verdict(bits == 0));
|
|
952
|
+
}
|
|
826
953
|
if let Some(multiple) = &self.fast_multiple
|
|
827
954
|
&& value & self.fixnum_flag != 0
|
|
828
955
|
{
|
|
@@ -833,10 +960,21 @@ impl Rules {
|
|
|
833
960
|
{
|
|
834
961
|
return fixnum_bounds(immediate_integer(value), limits);
|
|
835
962
|
}
|
|
836
|
-
self.check(value, |_| true)
|
|
963
|
+
let result = self.check(value, |_| true);
|
|
964
|
+
if result == Unsupported {
|
|
965
|
+
self.arbitrary_check(caches, value, budget, |_| true)
|
|
966
|
+
.map_or(Unsupported, |bits| verdict(bits == 0))
|
|
967
|
+
} else {
|
|
968
|
+
result
|
|
969
|
+
}
|
|
837
970
|
}
|
|
838
971
|
|
|
839
|
-
|
|
972
|
+
fn failures(
|
|
973
|
+
&self,
|
|
974
|
+
caches: &runtime::Caches,
|
|
975
|
+
value: Value,
|
|
976
|
+
budget: &budget::Budget,
|
|
977
|
+
) -> Result<i32, Verdict> {
|
|
840
978
|
if let Some(multiple) = &self.fast_multiple
|
|
841
979
|
&& value & self.fixnum_flag != 0
|
|
842
980
|
{
|
|
@@ -865,12 +1003,80 @@ impl Rules {
|
|
|
865
1003
|
false
|
|
866
1004
|
});
|
|
867
1005
|
if status == Unsupported {
|
|
868
|
-
|
|
1006
|
+
self.arbitrary_check(caches, value, budget, |_| false)
|
|
869
1007
|
} else {
|
|
870
1008
|
Ok(bits)
|
|
871
1009
|
}
|
|
872
1010
|
}
|
|
873
1011
|
|
|
1012
|
+
pub(super) fn visit_failures(
|
|
1013
|
+
&self,
|
|
1014
|
+
caches: &runtime::Caches,
|
|
1015
|
+
value: Value,
|
|
1016
|
+
budget: &budget::Budget,
|
|
1017
|
+
mut visit: impl FnMut(&'static str) -> Result<(), Verdict>,
|
|
1018
|
+
) -> Result<(), Verdict> {
|
|
1019
|
+
const KEYWORDS: [(i32, &str); 5] = [
|
|
1020
|
+
(MAXIMUM, "maximum"),
|
|
1021
|
+
(MINIMUM, "minimum"),
|
|
1022
|
+
(EXCLUSIVE_MAXIMUM, "exclusiveMaximum"),
|
|
1023
|
+
(EXCLUSIVE_MINIMUM, "exclusiveMinimum"),
|
|
1024
|
+
(MULTIPLE_OF, "multipleOf"),
|
|
1025
|
+
];
|
|
1026
|
+
if self.runtime_operands {
|
|
1027
|
+
let mut failure = None;
|
|
1028
|
+
let result = self.arbitrary_check(caches, value, budget, |bit| {
|
|
1029
|
+
let keyword = KEYWORDS
|
|
1030
|
+
.iter()
|
|
1031
|
+
.find(|&&(candidate, _)| candidate == bit)
|
|
1032
|
+
.expect("compiled numeric assertion")
|
|
1033
|
+
.1;
|
|
1034
|
+
if let Err(status) = visit(keyword) {
|
|
1035
|
+
failure = Some(status);
|
|
1036
|
+
true
|
|
1037
|
+
} else {
|
|
1038
|
+
false
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
if let Some(status) = failure {
|
|
1042
|
+
Err(status)
|
|
1043
|
+
} else {
|
|
1044
|
+
result.map(|_| ())
|
|
1045
|
+
}
|
|
1046
|
+
} else {
|
|
1047
|
+
let bits = self.failures(caches, value, budget)?;
|
|
1048
|
+
for (bit, keyword) in KEYWORDS {
|
|
1049
|
+
if !budget.checkpoint() {
|
|
1050
|
+
return Err(Unsupported);
|
|
1051
|
+
}
|
|
1052
|
+
if bits & bit != 0 {
|
|
1053
|
+
visit(keyword)?;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
Ok(())
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
pub(super) fn memory_usage(&self) -> usize {
|
|
1061
|
+
std::mem::size_of::<Self>()
|
|
1062
|
+
+ self.limits.capacity() * std::mem::size_of::<(i32, Limit)>()
|
|
1063
|
+
+ self
|
|
1064
|
+
.limits
|
|
1065
|
+
.iter()
|
|
1066
|
+
.map(|(_, limit)| limit.arbitrary_memory_usage())
|
|
1067
|
+
.sum::<usize>()
|
|
1068
|
+
+ self
|
|
1069
|
+
.multiple
|
|
1070
|
+
.as_ref()
|
|
1071
|
+
.map_or(0, Limit::arbitrary_memory_usage)
|
|
1072
|
+
+ self.fast_limits.as_ref().map_or(0, |limits| {
|
|
1073
|
+
limits.capacity() * std::mem::size_of::<(i32, i64)>()
|
|
1074
|
+
})
|
|
1075
|
+
+ self.fast_multiple.as_ref().map_or(0, |multiple| {
|
|
1076
|
+
multiple.limits.capacity() * std::mem::size_of::<(i32, i64)>()
|
|
1077
|
+
})
|
|
1078
|
+
}
|
|
1079
|
+
|
|
874
1080
|
fn check_wide_multiple(
|
|
875
1081
|
&self,
|
|
876
1082
|
value: Value,
|
|
@@ -886,12 +1092,7 @@ impl Rules {
|
|
|
886
1092
|
let Some(order) = limit.integer_order(value, integer, sign) else {
|
|
887
1093
|
return Unsupported;
|
|
888
1094
|
};
|
|
889
|
-
if
|
|
890
|
-
MAXIMUM => order == Ordering::Greater,
|
|
891
|
-
MINIMUM => order == Ordering::Less,
|
|
892
|
-
EXCLUSIVE_MAXIMUM => order != Ordering::Less,
|
|
893
|
-
_ => order != Ordering::Greater,
|
|
894
|
-
} {
|
|
1095
|
+
if violates(bit, Some(order)) {
|
|
895
1096
|
invalid = true;
|
|
896
1097
|
if failure(bit) {
|
|
897
1098
|
return Invalid;
|
|
@@ -907,7 +1108,7 @@ impl Rules {
|
|
|
907
1108
|
.multiple_of(divisor),
|
|
908
1109
|
)
|
|
909
1110
|
} else if sign.abs() > 1 {
|
|
910
|
-
|
|
1111
|
+
unbounded_integer_multiple(value, divisor, sum_words)
|
|
911
1112
|
} else {
|
|
912
1113
|
wide_multiple_words(&words, divisor, sum_words)
|
|
913
1114
|
};
|
|
@@ -944,7 +1145,7 @@ impl Rules {
|
|
|
944
1145
|
if !float && integer.is_none() {
|
|
945
1146
|
// An ordinary Integer outside i128 is beyond every representable
|
|
946
1147
|
// exact limit (whose denominator is positive). Bounds alone need
|
|
947
|
-
// only its sign. Multiples
|
|
1148
|
+
// only its sign. Multiples read the full magnitude below.
|
|
948
1149
|
if large_sign == 0 {
|
|
949
1150
|
return Unsupported;
|
|
950
1151
|
}
|
|
@@ -952,12 +1153,7 @@ impl Rules {
|
|
|
952
1153
|
let Some(order) = limit.integer_order(value, None, large_sign) else {
|
|
953
1154
|
return Unsupported;
|
|
954
1155
|
};
|
|
955
|
-
let violates =
|
|
956
|
-
MAXIMUM => order == Ordering::Greater,
|
|
957
|
-
MINIMUM => order == Ordering::Less,
|
|
958
|
-
EXCLUSIVE_MAXIMUM => order != Ordering::Less,
|
|
959
|
-
_ => order != Ordering::Greater,
|
|
960
|
-
};
|
|
1156
|
+
let violates = violates(*bit, Some(order));
|
|
961
1157
|
if violates {
|
|
962
1158
|
invalid = true;
|
|
963
1159
|
if failure(*bit) {
|
|
@@ -1027,12 +1223,7 @@ impl Rules {
|
|
|
1027
1223
|
n.partial_cmp(&limit.rounded)
|
|
1028
1224
|
}
|
|
1029
1225
|
};
|
|
1030
|
-
let violates =
|
|
1031
|
-
MAXIMUM => order == Some(Ordering::Greater),
|
|
1032
|
-
MINIMUM => order == Some(Ordering::Less),
|
|
1033
|
-
EXCLUSIVE_MAXIMUM => matches!(order, Some(Ordering::Greater | Ordering::Equal)),
|
|
1034
|
-
_ => matches!(order, Some(Ordering::Less | Ordering::Equal)),
|
|
1035
|
-
};
|
|
1226
|
+
let violates = violates(*bit, order);
|
|
1036
1227
|
if violates {
|
|
1037
1228
|
invalid = true;
|
|
1038
1229
|
if failure(*bit) {
|