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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -4
  3. data/ext/schemurai_native/Cargo.lock +55 -1
  4. data/ext/schemurai_native/Cargo.toml +4 -1
  5. data/ext/schemurai_native/extconf.rb +1 -1
  6. data/ext/schemurai_native/glue.c +1679 -62
  7. data/ext/schemurai_native/protocol.def +45 -0
  8. data/ext/schemurai_native/src/budget.rs +49 -0
  9. data/ext/schemurai_native/src/container.rs +460 -0
  10. data/ext/schemurai_native/src/content.rs +74 -27
  11. data/ext/schemurai_native/src/diagnostics/array.rs +392 -0
  12. data/ext/schemurai_native/src/diagnostics/object.rs +218 -0
  13. data/ext/schemurai_native/src/diagnostics.rs +1087 -307
  14. data/ext/schemurai_native/src/enumeration.rs +6 -1
  15. data/ext/schemurai_native/src/evaluation.rs +49 -20
  16. data/ext/schemurai_native/src/format.rs +126 -14
  17. data/ext/schemurai_native/src/graph.rs +37 -0
  18. data/ext/schemurai_native/src/instance_graph.rs +90 -0
  19. data/ext/schemurai_native/src/integer_scan.rs +132 -0
  20. data/ext/schemurai_native/src/lib.rs +917 -238
  21. data/ext/schemurai_native/src/memo.rs +11 -38
  22. data/ext/schemurai_native/src/number/arbitrary.rs +154 -0
  23. data/ext/schemurai_native/src/number.rs +250 -59
  24. data/ext/schemurai_native/src/pattern.rs +147 -109
  25. data/ext/schemurai_native/src/pointer.rs +97 -0
  26. data/ext/schemurai_native/src/runtime/properties.rs +162 -0
  27. data/ext/schemurai_native/src/runtime/reports.rs +266 -0
  28. data/ext/schemurai_native/src/runtime/scopes.rs +430 -0
  29. data/ext/schemurai_native/src/runtime.rs +1020 -0
  30. data/ext/schemurai_native/src/scope.rs +60 -16
  31. data/ext/schemurai_native/src/string.rs +68 -0
  32. data/ext/schemurai_native/src/tracking.rs +244 -174
  33. data/lib/schemurai/container_checks/array_diagnostics.rb +119 -0
  34. data/lib/schemurai/container_checks/object_diagnostics.rb +152 -0
  35. data/lib/schemurai/container_checks.rb +534 -0
  36. data/lib/schemurai/decimal.rb +11 -0
  37. data/lib/schemurai/dialect.rb +0 -12
  38. data/lib/schemurai/ecma_regexp.rb +46 -0
  39. data/lib/schemurai/evaluation_pool.rb +66 -0
  40. data/lib/schemurai/evaluator.rb +9 -48
  41. data/lib/schemurai/json_pointer.rb +11 -0
  42. data/lib/schemurai/native/diagnostics.rb +4 -3
  43. data/lib/schemurai/native/evaluator.rb +38 -10
  44. data/lib/schemurai/native/null_numeric.rb +23 -0
  45. data/lib/schemurai/number_checks.rb +128 -0
  46. data/lib/schemurai/string_checks.rb +166 -0
  47. data/lib/schemurai/type_checks.rb +59 -0
  48. data/lib/schemurai/value_equality.rb +72 -0
  49. data/lib/schemurai/version.rb +1 -1
  50. data/lib/schemurai/vm/compiler.rb +18 -26
  51. data/lib/schemurai/vm/evaluator.rb +87 -545
  52. data/lib/schemurai/vm/limited_evaluator.rb +7 -22
  53. 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
- fn decimal_float(value: f64) -> Option<Fraction> {
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(Fraction {
290
- numerator: 0,
291
- denominator: 1,
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 negative {
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 exact = if integer {
490
- read_integer(value).map(|numerator| Fraction {
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
- read_integer(numerator).zip(read_integer(denominator)).map(
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 Unsupported;
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 wide_multiple_words(words: &[u64; 16], divisor: Fraction, sum_words: bool) -> Verdict {
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. At most 16 u64 words sum below
604
- // 2^68, so one u128 remainder replaces per-word division.
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 eval_guarded(&self, required: i32, value: Value) -> Verdict {
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 match bit {
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 match bit {
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(&self, value: Value) -> Verdict {
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
- pub(super) fn failures(&self, value: Value) -> Result<i32, Verdict> {
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
- Err(status)
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 match bit {
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
- Unsupported
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 can use a bounded magnitude below.
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 = match *bit {
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 = match *bit {
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) {