schemurai 2.2.3 → 2.3.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 +14 -3
- data/ext/schemurai_native/Cargo.lock +99 -0
- data/ext/schemurai_native/Cargo.toml +21 -0
- data/ext/schemurai_native/build.rs +35 -0
- data/ext/schemurai_native/extconf.rb +63 -0
- data/ext/schemurai_native/glue.c +639 -0
- data/ext/schemurai_native/protocol.def +63 -0
- data/ext/schemurai_native/protocol.h +10 -0
- data/ext/schemurai_native/src/content.rs +574 -0
- data/ext/schemurai_native/src/diagnostics.rs +965 -0
- data/ext/schemurai_native/src/format.rs +357 -0
- data/ext/schemurai_native/src/lib.rs +2376 -0
- data/ext/schemurai_native/src/number.rs +1055 -0
- data/ext/schemurai_native/src/pattern.rs +288 -0
- data/ext/schemurai_native/src/scope.rs +210 -0
- data/ext/schemurai_native/src/tracking.rs +532 -0
- data/lib/schemurai/backend.rb +2 -1
- data/lib/schemurai/native/diagnostics.rb +52 -0
- data/lib/schemurai/native/evaluator.rb +32 -0
- data/lib/schemurai/version.rb +1 -1
- data/lib/schemurai.rb +8 -7
- data/sig/schemurai.rbs +1 -1
- metadata +22 -4
|
@@ -0,0 +1,1055 @@
|
|
|
1
|
+
//! Numeric instructions distinguish Ruby's ordinary comparison from decimal
|
|
2
|
+
//! multipleOf semantics. All evaluation helpers operate under the GVL and
|
|
3
|
+
//! allocate no Ruby objects; unsupported magnitudes retain VM compatibility.
|
|
4
|
+
use super::*;
|
|
5
|
+
use std::cmp::Ordering;
|
|
6
|
+
|
|
7
|
+
// Strictly ordered integers are unique without fingerprinting their digits.
|
|
8
|
+
// Each comparison checks both original handles; mixed or unordered inputs
|
|
9
|
+
// retain the indexed JSON equality path. No Ruby allocation occurs here.
|
|
10
|
+
pub(super) fn ordered_big_unique(values: &[Value]) -> Option<Verdict> {
|
|
11
|
+
let direction = unsafe { sn_big_order(values[0], values[1]) };
|
|
12
|
+
match direction {
|
|
13
|
+
0 => return Some(Invalid),
|
|
14
|
+
-1 | 1 => {}
|
|
15
|
+
_ => return None,
|
|
16
|
+
}
|
|
17
|
+
for (offset, pair) in values[1..].windows(2).enumerate() {
|
|
18
|
+
let order = unsafe { sn_big_order(pair[0], pair[1]) };
|
|
19
|
+
if order == 0 {
|
|
20
|
+
return Some(Invalid);
|
|
21
|
+
}
|
|
22
|
+
if order != direction {
|
|
23
|
+
// A return to the first value is a duplicate even when it breaks
|
|
24
|
+
// the order. Otherwise indexing must examine the entire input.
|
|
25
|
+
if order == ADAPTER_UNSUPPORTED {
|
|
26
|
+
return None;
|
|
27
|
+
}
|
|
28
|
+
if unsafe { sn_big_order(values[0], pair[1]) } == 0 {
|
|
29
|
+
return Some(Invalid);
|
|
30
|
+
}
|
|
31
|
+
let tail = offset + 2;
|
|
32
|
+
return (values.len() - tail <= 16)
|
|
33
|
+
.then(|| ordered_tail_unique(values, tail, direction))
|
|
34
|
+
.flatten();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
Some(Valid)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// A short unordered tail can search the proven ordered prefix directly.
|
|
41
|
+
// Bound the tail to keep its internal pair comparisons constant-sized and
|
|
42
|
+
// avoid rebuilding a full fingerprint index after an almost complete scan.
|
|
43
|
+
fn ordered_tail_unique(values: &[Value], prefix: usize, direction: i32) -> Option<Verdict> {
|
|
44
|
+
for at in prefix..values.len() {
|
|
45
|
+
let (mut low, mut high) = (0, prefix);
|
|
46
|
+
while low < high {
|
|
47
|
+
let middle = low + (high - low) / 2;
|
|
48
|
+
match unsafe { sn_big_order(values[middle], values[at]) } {
|
|
49
|
+
0 => return Some(Invalid),
|
|
50
|
+
ADAPTER_UNSUPPORTED => return None,
|
|
51
|
+
order if order == direction => low = middle + 1,
|
|
52
|
+
_ => high = middle,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for previous in prefix..at {
|
|
56
|
+
if unsafe { sn_big_order(values[previous], values[at]) } == 0 {
|
|
57
|
+
return Some(Invalid);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
Some(Valid)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// A finite binary64 integer has at most 1,024 magnitude bits. Arithmetic
|
|
65
|
+
// adapters fill fixed stack words; there is no instance-tree conversion or
|
|
66
|
+
// Ruby allocation. Larger integers cannot equal any finite Float.
|
|
67
|
+
pub(super) fn integer_float_equal(integer: Value, float: f64) -> Verdict {
|
|
68
|
+
if !float.is_finite() || float.trunc() != float {
|
|
69
|
+
return Invalid;
|
|
70
|
+
}
|
|
71
|
+
let mut actual = [0_u64; 16];
|
|
72
|
+
let sign = unsafe { sn_integer1024(integer, actual.as_mut_ptr()) };
|
|
73
|
+
if sign.abs() > 1 || (sign < 0) != (float < 0.0) {
|
|
74
|
+
return Invalid;
|
|
75
|
+
}
|
|
76
|
+
let mut expected = [0_u64; 16];
|
|
77
|
+
if float != 0.0 {
|
|
78
|
+
let bits = float.to_bits();
|
|
79
|
+
let mantissa = (bits & ((1_u64 << 52) - 1)) | (1_u64 << 52);
|
|
80
|
+
let shift = ((bits >> 52) & 0x7ff) as i32 - 1023 - 52;
|
|
81
|
+
if shift < 0 {
|
|
82
|
+
expected[0] = mantissa >> shift.unsigned_abs();
|
|
83
|
+
} else {
|
|
84
|
+
let word = shift as usize / 64;
|
|
85
|
+
let bit = shift as usize % 64;
|
|
86
|
+
expected[word] = mantissa << bit;
|
|
87
|
+
if bit != 0 && word + 1 < expected.len() {
|
|
88
|
+
expected[word + 1] = mantissa >> (64 - bit);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
verdict(actual == expected)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
pub(super) fn integer_fingerprint(integer: Value) -> u64 {
|
|
96
|
+
let mut words = [0_u64; 16];
|
|
97
|
+
let sign = unsafe { sn_integer1024(integer, words.as_mut_ptr()) };
|
|
98
|
+
if sign.abs() > 1 {
|
|
99
|
+
// No finite Float can equal this magnitude. Mix bounded low words,
|
|
100
|
+
// sign, and magnitude size; full equality resolves all collisions,
|
|
101
|
+
// including integers that differ only in unsampled high digits.
|
|
102
|
+
let len = words
|
|
103
|
+
.iter()
|
|
104
|
+
.rposition(|&word| word != 0)
|
|
105
|
+
.map_or(0, |index| index + 1);
|
|
106
|
+
return integer_bucket(&words[..len], sign, unsafe { sn_integer_bytes(integer) });
|
|
107
|
+
}
|
|
108
|
+
exact_integer_fingerprint(&words, sign)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
pub(super) fn big_fingerprint(integer: Value) -> Option<u64> {
|
|
112
|
+
let mut words = [0_u64; 4];
|
|
113
|
+
let sign = unsafe { sn_big256(integer, words.as_mut_ptr()) };
|
|
114
|
+
if sign == ADAPTER_UNSUPPORTED {
|
|
115
|
+
return None;
|
|
116
|
+
}
|
|
117
|
+
Some(if sign.abs() > 1 {
|
|
118
|
+
integer_fingerprint(integer)
|
|
119
|
+
} else {
|
|
120
|
+
exact_integer_fingerprint(&words, sign)
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Only used by an index that restarts with canonical fingerprints if any item
|
|
125
|
+
// is not a Bignum. No Float can enter this index, so it needs no binary64
|
|
126
|
+
// normalization. Full integer equality resolves every fingerprint collision.
|
|
127
|
+
pub(super) fn big_only_fingerprint(integer: Value) -> Option<u64> {
|
|
128
|
+
let mut hash = 0;
|
|
129
|
+
(unsafe { sn_big_hash(integer, &mut hash) } != 0).then_some(hash)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// This convention is restricted to Big/Big comparisons. The caller restarts
|
|
133
|
+
// with the full hash when collisions accumulate, or canonical fingerprints
|
|
134
|
+
// when a non-Bignum appears. No sampled metadata decides equality.
|
|
135
|
+
pub(super) fn big_low_fingerprint(integer: Value) -> Option<u64> {
|
|
136
|
+
let sample = unsafe { sn_big_low_word(integer) };
|
|
137
|
+
(sample != 1 << 33).then(|| {
|
|
138
|
+
// Fold the short magnitude sample without scrambling adjacent low
|
|
139
|
+
// words. Exact equality and wider bounded retries resolve collisions.
|
|
140
|
+
let bits = u64::from(sample as u32) ^ if sample & (1 << 32) != 0 { u64::MAX } else { 0 };
|
|
141
|
+
let folded = bits ^ (bits >> 33);
|
|
142
|
+
folded ^ (folded >> 11) ^ (folded >> 22)
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
pub(super) fn big_low64_fingerprint(integer: Value) -> Option<u64> {
|
|
147
|
+
let mut word = 0;
|
|
148
|
+
let sign = unsafe { sn_big_low64_word(integer, &mut word) };
|
|
149
|
+
(sign != 3).then(|| {
|
|
150
|
+
let bits = word ^ if sign < 0 { u64::MAX } else { 0 };
|
|
151
|
+
let folded = bits ^ (bits >> 33);
|
|
152
|
+
let folded = folded ^ (folded >> 11) ^ (folded >> 22);
|
|
153
|
+
folded.wrapping_mul(0x9e3779b97f4a7c15)
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fn exact_integer_fingerprint(words: &[u64], sign: i32) -> u64 {
|
|
158
|
+
let top = words.iter().rposition(|&word| word != 0).unwrap_or(0);
|
|
159
|
+
if top == 0 && (words[0] < (1_u64 << 63) || (sign < 0 && words[0] == 1_u64 << 63)) {
|
|
160
|
+
let bits = if sign < 0 {
|
|
161
|
+
words[0].wrapping_neg()
|
|
162
|
+
} else {
|
|
163
|
+
words[0]
|
|
164
|
+
};
|
|
165
|
+
return mix(bits ^ TYPE_NUMBER as u64);
|
|
166
|
+
}
|
|
167
|
+
let exponent = top * 64 + 63 - words[top].leading_zeros() as usize;
|
|
168
|
+
let shift = exponent - 52;
|
|
169
|
+
let word = shift / 64;
|
|
170
|
+
let bit = shift % 64;
|
|
171
|
+
if words[..word].iter().any(|&word| word != 0)
|
|
172
|
+
|| (bit != 0 && words[word] & ((1_u64 << bit) - 1) != 0)
|
|
173
|
+
{
|
|
174
|
+
// Nonrepresentable integers cannot equal any Float. Keep their low
|
|
175
|
+
// digits instead of putting nearby integers into one rounded bucket.
|
|
176
|
+
return integer_bucket(&words[..=top], sign, exponent / 8 + 1);
|
|
177
|
+
}
|
|
178
|
+
let mut mantissa = words[word] >> bit;
|
|
179
|
+
if bit != 0 && word + 1 < words.len() {
|
|
180
|
+
mantissa |= words[word + 1] << (64 - bit);
|
|
181
|
+
}
|
|
182
|
+
// Exactly representable integers share bits with their equal Float.
|
|
183
|
+
// Every collision still goes through full JSON equality.
|
|
184
|
+
let bits = ((sign < 0) as u64) << 63
|
|
185
|
+
| ((exponent + 1023) as u64) << 52
|
|
186
|
+
| (mantissa & ((1_u64 << 52) - 1));
|
|
187
|
+
mix(bits ^ TYPE_NUMBER as u64)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Callers supply the prefix ending at the last nonzero word. It may be empty
|
|
191
|
+
// when all sampled low words of an oversized integer are zero.
|
|
192
|
+
fn integer_bucket(words: &[u64], sign: i32, bytes: usize) -> u64 {
|
|
193
|
+
let mut hash = bytes as u64 ^ TYPE_NUMBER as u64 ^ ((sign < 0) as u64);
|
|
194
|
+
// Independent lanes shorten the multiplication dependency chain while
|
|
195
|
+
// retaining every sampled word. Full VALUE equality resolves collisions.
|
|
196
|
+
let mut other = hash ^ 0x9e37_79b9_7f4a_7c15;
|
|
197
|
+
let (pairs, remainder) = words.as_chunks::<2>();
|
|
198
|
+
for pair in pairs {
|
|
199
|
+
hash = (hash ^ pair[0]).wrapping_mul(0x0000_0100_0000_01b3);
|
|
200
|
+
other = (other ^ pair[1]).wrapping_mul(0x0000_0100_0000_01b3);
|
|
201
|
+
}
|
|
202
|
+
if let [word] = remainder {
|
|
203
|
+
hash = (hash ^ word).wrapping_mul(0x0000_0100_0000_01b3);
|
|
204
|
+
}
|
|
205
|
+
mix(hash ^ other.rotate_left(32))
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
#[derive(Debug, Clone, Copy)]
|
|
209
|
+
struct Fraction {
|
|
210
|
+
numerator: i128,
|
|
211
|
+
denominator: i128,
|
|
212
|
+
}
|
|
213
|
+
impl Fraction {
|
|
214
|
+
fn compare(self, other: Self) -> Ordering {
|
|
215
|
+
let signs = self.numerator.signum().cmp(&other.numerator.signum());
|
|
216
|
+
if signs != Ordering::Equal {
|
|
217
|
+
return signs;
|
|
218
|
+
}
|
|
219
|
+
let (mut a, mut b) = (self.numerator.unsigned_abs(), self.denominator as u128);
|
|
220
|
+
let (mut c, mut d) = (other.numerator.unsigned_abs(), other.denominator as u128);
|
|
221
|
+
let mut reverse = self.numerator < 0;
|
|
222
|
+
loop {
|
|
223
|
+
let order = (a / b).cmp(&(c / d));
|
|
224
|
+
if order != Ordering::Equal {
|
|
225
|
+
return if reverse { order.reverse() } else { order };
|
|
226
|
+
}
|
|
227
|
+
let (r, s) = (a % b, c % d);
|
|
228
|
+
if r == 0 || s == 0 {
|
|
229
|
+
let order = r.cmp(&s);
|
|
230
|
+
return if reverse { order.reverse() } else { order };
|
|
231
|
+
}
|
|
232
|
+
(a, b, c, d) = (b, r, d, s);
|
|
233
|
+
reverse = !reverse;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
fn multiple_of(self, divisor: Self) -> bool {
|
|
238
|
+
// Both fractions are reduced: p must divide n and d must divide q.
|
|
239
|
+
divisor.numerator > 0
|
|
240
|
+
&& self.numerator % divisor.numerator == 0
|
|
241
|
+
&& divisor.denominator % self.denominator == 0
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
fn decimal_float(value: f64) -> Option<Fraction> {
|
|
246
|
+
if !value.is_finite() {
|
|
247
|
+
return None;
|
|
248
|
+
}
|
|
249
|
+
let mut buffer = ryu::Buffer::new();
|
|
250
|
+
let mut bytes = buffer.format_finite(value).as_bytes();
|
|
251
|
+
let negative = bytes.first() == Some(&b'-');
|
|
252
|
+
if negative {
|
|
253
|
+
bytes = &bytes[1..];
|
|
254
|
+
}
|
|
255
|
+
let exponent = if let Some(at) = bytes.iter().position(|&byte| byte == b'e') {
|
|
256
|
+
let exponent = std::str::from_utf8(&bytes[at + 1..])
|
|
257
|
+
.ok()?
|
|
258
|
+
.parse::<i32>()
|
|
259
|
+
.ok()?;
|
|
260
|
+
bytes = &bytes[..at];
|
|
261
|
+
exponent
|
|
262
|
+
} else {
|
|
263
|
+
0
|
|
264
|
+
};
|
|
265
|
+
let fractional = bytes
|
|
266
|
+
.iter()
|
|
267
|
+
.position(|&b| b == b'.')
|
|
268
|
+
.map_or(0, |at| bytes.len() - at - 1);
|
|
269
|
+
let mut end = bytes.len();
|
|
270
|
+
let mut trailing = 0;
|
|
271
|
+
while end > 0 && (bytes[end - 1] == b'0' || bytes[end - 1] == b'.') {
|
|
272
|
+
if bytes[end - 1] == b'0' {
|
|
273
|
+
trailing += 1;
|
|
274
|
+
}
|
|
275
|
+
end -= 1;
|
|
276
|
+
}
|
|
277
|
+
let mut numerator = 0_i128;
|
|
278
|
+
for &byte in &bytes[..end] {
|
|
279
|
+
if byte == b'.' {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if !byte.is_ascii_digit() {
|
|
283
|
+
return None;
|
|
284
|
+
}
|
|
285
|
+
numerator = numerator
|
|
286
|
+
.checked_mul(10)?
|
|
287
|
+
.checked_add(i128::from(byte.checked_sub(b'0')?))?;
|
|
288
|
+
}
|
|
289
|
+
if numerator == 0 {
|
|
290
|
+
return Some(Fraction {
|
|
291
|
+
numerator: 0,
|
|
292
|
+
denominator: 1,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
let power = exponent + trailing - fractional as i32;
|
|
296
|
+
let mut denominator = 1;
|
|
297
|
+
if power >= 0 {
|
|
298
|
+
numerator = numerator.checked_mul(10_i128.checked_pow(power as u32)?)?;
|
|
299
|
+
} else {
|
|
300
|
+
denominator = 10_i128.checked_pow(power.unsigned_abs())?;
|
|
301
|
+
}
|
|
302
|
+
let (mut a, mut b) = (numerator, denominator);
|
|
303
|
+
while b != 0 {
|
|
304
|
+
(a, b) = (b, a % b);
|
|
305
|
+
}
|
|
306
|
+
Some(Fraction {
|
|
307
|
+
numerator: if negative {
|
|
308
|
+
-numerator / a
|
|
309
|
+
} else {
|
|
310
|
+
numerator / a
|
|
311
|
+
},
|
|
312
|
+
denominator: denominator / a,
|
|
313
|
+
})
|
|
314
|
+
}
|
|
315
|
+
#[derive(Debug)]
|
|
316
|
+
struct Limit {
|
|
317
|
+
exact: Option<Fraction>,
|
|
318
|
+
wide_integer: Option<Value>,
|
|
319
|
+
wide_rounded_order: Option<Ordering>,
|
|
320
|
+
rounded: f64,
|
|
321
|
+
integer: bool,
|
|
322
|
+
sum_words: bool,
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
impl Limit {
|
|
326
|
+
fn float_order(&self, n: f64) -> Result<Option<Ordering>, Verdict> {
|
|
327
|
+
if let Some(exact) = self.exact {
|
|
328
|
+
return Ok(compare_float_integer(n, exact.numerator));
|
|
329
|
+
}
|
|
330
|
+
let Some(tie) = self.wide_rounded_order else {
|
|
331
|
+
return Err(Unsupported);
|
|
332
|
+
};
|
|
333
|
+
// Adjacent Floats cannot lie inside the rounding interval of an
|
|
334
|
+
// Integer. Only equality with its rounded Float needs an exact tie.
|
|
335
|
+
Ok(n.partial_cmp(&self.rounded).map(
|
|
336
|
+
|order| {
|
|
337
|
+
if order == Ordering::Equal { tie } else { order }
|
|
338
|
+
},
|
|
339
|
+
))
|
|
340
|
+
}
|
|
341
|
+
// Runtime callers have classified the instance as an ordinary Integer.
|
|
342
|
+
// Wide schema bounds remain rooted Ruby handles, with no digit export.
|
|
343
|
+
fn integer_order(&self, value: Value, integer: Option<i128>, sign: i32) -> Option<Ordering> {
|
|
344
|
+
if let Some(exact) = self.exact {
|
|
345
|
+
return Some(integer.map_or_else(
|
|
346
|
+
|| {
|
|
347
|
+
if sign > 0 {
|
|
348
|
+
Ordering::Greater
|
|
349
|
+
} else {
|
|
350
|
+
Ordering::Less
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
|n| compare_integer(n, exact),
|
|
354
|
+
));
|
|
355
|
+
}
|
|
356
|
+
self.wide_integer
|
|
357
|
+
.map(|bound| unsafe { sn_wide_integer_compare(value, bound) }.cmp(&0))
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
#[derive(Debug)]
|
|
361
|
+
pub(super) struct Rules {
|
|
362
|
+
limits: Vec<(i32, Limit)>,
|
|
363
|
+
multiple: Option<Limit>,
|
|
364
|
+
pub(super) fast_limits: Option<Vec<(i32, i64)>>,
|
|
365
|
+
pub(super) fast_multiple: Option<FastMultiple>,
|
|
366
|
+
fixnum_flag: Value,
|
|
367
|
+
bounds_view: bool,
|
|
368
|
+
bounded_signs: Option<[bool; 2]>,
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
#[derive(Debug)]
|
|
372
|
+
pub(super) struct FastMultiple {
|
|
373
|
+
numerator: i64,
|
|
374
|
+
limits: Vec<(i32, i64)>,
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
impl FastMultiple {
|
|
378
|
+
fn failures(&self, n: i64) -> i32 {
|
|
379
|
+
// An Integer has denominator 1. For a reduced divisor p/q, only
|
|
380
|
+
// divisibility by p remains; q is always divisible by 1.
|
|
381
|
+
let mut bits = if self.numerator > 0 && n % self.numerator == 0 {
|
|
382
|
+
0
|
|
383
|
+
} else {
|
|
384
|
+
MULTIPLE_OF
|
|
385
|
+
};
|
|
386
|
+
for &(bit, limit) in &self.limits {
|
|
387
|
+
if match bit {
|
|
388
|
+
MAXIMUM => n > limit,
|
|
389
|
+
MINIMUM => n < limit,
|
|
390
|
+
EXCLUSIVE_MAXIMUM => n >= limit,
|
|
391
|
+
_ => n <= limit,
|
|
392
|
+
} {
|
|
393
|
+
bits |= bit;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
bits
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
pub(super) fn eval(&self, n: i64) -> Verdict {
|
|
400
|
+
verdict(self.failures(n) == 0)
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
impl Engine {
|
|
405
|
+
pub(super) fn compile_number(&mut self, operand: Value) -> Result<Option<Rules>, Value> {
|
|
406
|
+
let mask = self.field(operand, b"mask\0")?;
|
|
407
|
+
let mask = unsafe { sn_integer(mask) } as i32;
|
|
408
|
+
let mut limits = vec![];
|
|
409
|
+
for (bit, name) in [
|
|
410
|
+
(MAXIMUM, b"maximum\0".as_slice()),
|
|
411
|
+
(MINIMUM, b"minimum\0"),
|
|
412
|
+
(EXCLUSIVE_MAXIMUM, b"exclusive_maximum\0"),
|
|
413
|
+
(EXCLUSIVE_MINIMUM, b"exclusive_minimum\0"),
|
|
414
|
+
] {
|
|
415
|
+
if mask & bit != 0 {
|
|
416
|
+
let value = self.field(operand, name)?;
|
|
417
|
+
let Some(limit) = self.numeric_limit(value)? else {
|
|
418
|
+
return Ok(None);
|
|
419
|
+
};
|
|
420
|
+
limits.push((bit, limit));
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
let multiple = if mask & MULTIPLE_OF == 0 {
|
|
424
|
+
None
|
|
425
|
+
} else {
|
|
426
|
+
let value = self.field(operand, b"multiple_of\0")?;
|
|
427
|
+
let Some(limit) = self.numeric_limit(value)? else {
|
|
428
|
+
return Ok(None);
|
|
429
|
+
};
|
|
430
|
+
Some(limit)
|
|
431
|
+
};
|
|
432
|
+
let fast_limits = if multiple.is_some() {
|
|
433
|
+
None
|
|
434
|
+
} else {
|
|
435
|
+
limits
|
|
436
|
+
.iter()
|
|
437
|
+
.map(|(bit, limit)| {
|
|
438
|
+
if !limit.integer {
|
|
439
|
+
return None;
|
|
440
|
+
}
|
|
441
|
+
Some((*bit, i64::try_from(limit.exact?.numerator).ok()?))
|
|
442
|
+
})
|
|
443
|
+
.collect()
|
|
444
|
+
};
|
|
445
|
+
let fast_multiple = multiple
|
|
446
|
+
.as_ref()
|
|
447
|
+
.and_then(|limit| limit.exact)
|
|
448
|
+
.and_then(|fraction| i64::try_from(fraction.numerator).ok())
|
|
449
|
+
.and_then(|numerator| {
|
|
450
|
+
let limits = limits
|
|
451
|
+
.iter()
|
|
452
|
+
.map(|&(bit, ref limit)| {
|
|
453
|
+
let fraction = limit.exact?;
|
|
454
|
+
(fraction.denominator == 1)
|
|
455
|
+
.then(|| i64::try_from(fraction.numerator).ok())
|
|
456
|
+
.flatten()
|
|
457
|
+
.map(|n| (bit, n))
|
|
458
|
+
})
|
|
459
|
+
.collect::<Option<Vec<_>>>()?;
|
|
460
|
+
Some(FastMultiple { numerator, limits })
|
|
461
|
+
});
|
|
462
|
+
let bounds_view = multiple.is_none() && limits.iter().all(|(_, limit)| limit.integer);
|
|
463
|
+
let bounded_signs = (bounds_view && limits.iter().all(|(_, limit)| limit.exact.is_some()))
|
|
464
|
+
.then(|| {
|
|
465
|
+
[
|
|
466
|
+
!limits
|
|
467
|
+
.iter()
|
|
468
|
+
.any(|(bit, _)| matches!(*bit, MINIMUM | EXCLUSIVE_MINIMUM)),
|
|
469
|
+
!limits
|
|
470
|
+
.iter()
|
|
471
|
+
.any(|(bit, _)| matches!(*bit, MAXIMUM | EXCLUSIVE_MAXIMUM)),
|
|
472
|
+
]
|
|
473
|
+
});
|
|
474
|
+
Ok(Some(Rules {
|
|
475
|
+
limits,
|
|
476
|
+
multiple,
|
|
477
|
+
fast_limits,
|
|
478
|
+
fast_multiple,
|
|
479
|
+
fixnum_flag: self.fixnum_flag,
|
|
480
|
+
bounds_view,
|
|
481
|
+
bounded_signs,
|
|
482
|
+
}))
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
fn numeric_limit(&mut self, value: Value) -> Result<Option<Limit>, Value> {
|
|
486
|
+
let integer = unsafe { sn_kind(value) } & TYPE_INTEGER != 0;
|
|
487
|
+
if !integer && unsafe { sn_is_rational(value) } == 0 {
|
|
488
|
+
return Ok(None);
|
|
489
|
+
}
|
|
490
|
+
let exact = if integer {
|
|
491
|
+
read_integer(value).map(|numerator| Fraction {
|
|
492
|
+
numerator,
|
|
493
|
+
denominator: 1,
|
|
494
|
+
})
|
|
495
|
+
} else {
|
|
496
|
+
let numerator = self.field(value, b"numerator\0")?;
|
|
497
|
+
let denominator = self.field(value, b"denominator\0")?;
|
|
498
|
+
read_integer(numerator).zip(read_integer(denominator)).map(
|
|
499
|
+
|(numerator, denominator)| Fraction {
|
|
500
|
+
numerator,
|
|
501
|
+
denominator,
|
|
502
|
+
},
|
|
503
|
+
)
|
|
504
|
+
};
|
|
505
|
+
let rounded = self.field(value, b"to_f\0")?;
|
|
506
|
+
let rounded = unsafe { sn_float(rounded) };
|
|
507
|
+
let wide_rounded_order = if integer && exact.is_none() {
|
|
508
|
+
wide_float_tie(value, rounded)
|
|
509
|
+
} else {
|
|
510
|
+
None
|
|
511
|
+
};
|
|
512
|
+
let sum_words = exact.is_some_and(|fraction| {
|
|
513
|
+
fraction.numerator > 1 && (1_u128 << 64) % fraction.numerator as u128 == 1
|
|
514
|
+
});
|
|
515
|
+
Ok(Some(Limit {
|
|
516
|
+
exact,
|
|
517
|
+
wide_integer: (integer && exact.is_none()).then(|| self.retain(value)),
|
|
518
|
+
wide_rounded_order,
|
|
519
|
+
rounded,
|
|
520
|
+
integer,
|
|
521
|
+
sum_words,
|
|
522
|
+
}))
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
fn read_integer(value: Value) -> Option<i128> {
|
|
527
|
+
read_integer_status(value).ok()
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Err(sign) identifies a magnitude outside i128; Err(0) is not an Integer.
|
|
531
|
+
fn read_integer_status(value: Value) -> Result<i128, i32> {
|
|
532
|
+
if unsafe { sn_fixnum(value) } != 0 {
|
|
533
|
+
return Ok(unsafe { sn_integer(value) } as i128);
|
|
534
|
+
}
|
|
535
|
+
if unsafe { sn_kind(value) } & TYPE_INTEGER == 0 || unsafe { sn_is_float(value) } != 0 {
|
|
536
|
+
return Err(0);
|
|
537
|
+
}
|
|
538
|
+
unpack_integer_status(value)
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Runtime numeric instructions have already rejected non-numeric instances.
|
|
542
|
+
// Their non-Float branch contains only ordinary Integers; compilation uses
|
|
543
|
+
// read_integer_status instead because schema metadata still needs checking.
|
|
544
|
+
fn read_known_integer_status(value: Value, fixnum_flag: Value) -> Result<i128, i32> {
|
|
545
|
+
if value & fixnum_flag != 0 {
|
|
546
|
+
return Ok(immediate_integer(value) as i128);
|
|
547
|
+
}
|
|
548
|
+
let mut words = [0_u64; 2];
|
|
549
|
+
let sign = unsafe { sn_integer_bounds128(value, words.as_mut_ptr()) };
|
|
550
|
+
integer_words_status(&words, sign)
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
fn unpack_integer_status(value: Value) -> Result<i128, i32> {
|
|
554
|
+
let mut words = [0_u64; 2];
|
|
555
|
+
let sign = unsafe { sn_integer128(value, words.as_mut_ptr()) };
|
|
556
|
+
integer_words_status(&words, sign)
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
fn integer_words_status(words: &[u64], sign: i32) -> Result<i128, i32> {
|
|
560
|
+
if sign.abs() > 1 {
|
|
561
|
+
return Err(sign);
|
|
562
|
+
}
|
|
563
|
+
if words[2..].iter().any(|&word| word != 0) {
|
|
564
|
+
return Err(sign);
|
|
565
|
+
}
|
|
566
|
+
let magnitude = u128::from(words[0]) | (u128::from(words[1]) << 64);
|
|
567
|
+
if sign < 0 {
|
|
568
|
+
if magnitude == 1_u128 << 127 {
|
|
569
|
+
Ok(i128::MIN)
|
|
570
|
+
} else {
|
|
571
|
+
i128::try_from(magnitude)
|
|
572
|
+
.map_err(|_| sign)?
|
|
573
|
+
.checked_neg()
|
|
574
|
+
.ok_or(sign)
|
|
575
|
+
}
|
|
576
|
+
} else {
|
|
577
|
+
i128::try_from(magnitude).map_err(|_| sign)
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
fn wide_integer_multiple(value: Value, divisor: Fraction, sum_words: bool) -> Verdict {
|
|
582
|
+
if divisor.numerator <= 0 {
|
|
583
|
+
return Invalid;
|
|
584
|
+
}
|
|
585
|
+
if divisor.numerator == 1 {
|
|
586
|
+
return Valid;
|
|
587
|
+
}
|
|
588
|
+
let mut words = [0_u64; 16];
|
|
589
|
+
if unsafe { sn_integer1024(value, words.as_mut_ptr()) }.abs() > 1 {
|
|
590
|
+
return Unsupported;
|
|
591
|
+
}
|
|
592
|
+
wide_multiple_words(&words, divisor, sum_words)
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
fn wide_multiple_words(words: &[u64; 16], divisor: Fraction, sum_words: bool) -> Verdict {
|
|
596
|
+
let len = words
|
|
597
|
+
.iter()
|
|
598
|
+
.rposition(|&word| word != 0)
|
|
599
|
+
.map_or(0, |i| i + 1);
|
|
600
|
+
let divisor = divisor.numerator as u128;
|
|
601
|
+
let mut remainder = 0_u128;
|
|
602
|
+
if sum_words {
|
|
603
|
+
// If base 2^64 is 1 modulo the divisor, a magnitude has the same
|
|
604
|
+
// remainder as the sum of its words. At most 16 u64 words sum below
|
|
605
|
+
// 2^68, so one u128 remainder replaces per-word division.
|
|
606
|
+
remainder = words[..len]
|
|
607
|
+
.iter()
|
|
608
|
+
.map(|&word| u128::from(word))
|
|
609
|
+
.sum::<u128>()
|
|
610
|
+
% divisor;
|
|
611
|
+
} else if divisor <= u64::MAX as u128 {
|
|
612
|
+
// remainder < divisor <= u64::MAX, so shifting it by 64 fits u128.
|
|
613
|
+
for &word in words[..len].iter().rev() {
|
|
614
|
+
remainder = ((remainder << 64) | u128::from(word)) % divisor;
|
|
615
|
+
}
|
|
616
|
+
} else {
|
|
617
|
+
// The positive divisor is at most i128::MAX. Doubling a remainder
|
|
618
|
+
// below it cannot overflow u128; consume magnitude bits without
|
|
619
|
+
// allocating or multiplying an arbitrary-precision integer.
|
|
620
|
+
for &word in words[..len].iter().rev() {
|
|
621
|
+
for bit in (0..64).rev() {
|
|
622
|
+
remainder = (remainder << 1) | u128::from((word >> bit) & 1);
|
|
623
|
+
if remainder >= divisor {
|
|
624
|
+
remainder -= divisor;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
// The instance denominator is one, which divides the reduced divisor's
|
|
630
|
+
// denominator. Sign does not affect divisibility by its positive numerator.
|
|
631
|
+
verdict(remainder == 0)
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
fn fixnum_bounds(n: i64, limits: &[(i32, i64)]) -> Verdict {
|
|
635
|
+
verdict(limits.iter().all(|&(bit, limit)| match bit {
|
|
636
|
+
MAXIMUM => n <= limit,
|
|
637
|
+
MINIMUM => n >= limit,
|
|
638
|
+
EXCLUSIVE_MAXIMUM => n < limit,
|
|
639
|
+
_ => n > limit,
|
|
640
|
+
}))
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
fn compare_integer(n: i128, limit: Fraction) -> Ordering {
|
|
644
|
+
let whole = limit.numerator / limit.denominator;
|
|
645
|
+
n.cmp(&whole)
|
|
646
|
+
.then_with(|| 0.cmp(&(limit.numerator % limit.denominator)))
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
fn compare_float_integer(n: f64, limit: i128) -> Option<Ordering> {
|
|
650
|
+
if n.is_nan() {
|
|
651
|
+
return None;
|
|
652
|
+
}
|
|
653
|
+
if n < i128::MIN as f64 {
|
|
654
|
+
return Some(Ordering::Less);
|
|
655
|
+
}
|
|
656
|
+
if n >= i128::MAX as f64 {
|
|
657
|
+
return Some(Ordering::Greater);
|
|
658
|
+
}
|
|
659
|
+
Some(
|
|
660
|
+
(n as i128)
|
|
661
|
+
.cmp(&limit)
|
|
662
|
+
.then_with(|| n.fract().partial_cmp(&0.0).unwrap()),
|
|
663
|
+
)
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Schema metadata only: record the comparison at the rounded Float once.
|
|
667
|
+
// Every finite binary64 magnitude fits 1,024 bits. No instance digit export
|
|
668
|
+
// or Ruby allocation is needed at runtime, and no word buffer is retained.
|
|
669
|
+
fn wide_float_tie(value: Value, rounded: f64) -> Option<Ordering> {
|
|
670
|
+
if rounded.is_infinite() {
|
|
671
|
+
return Some(if rounded.is_sign_negative() {
|
|
672
|
+
Ordering::Less
|
|
673
|
+
} else {
|
|
674
|
+
Ordering::Greater
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
let bits = rounded.to_bits();
|
|
678
|
+
let exponent = ((bits >> 52) & 0x7ff) as usize;
|
|
679
|
+
if !(1023 + 127..2047).contains(&exponent) {
|
|
680
|
+
return None;
|
|
681
|
+
}
|
|
682
|
+
let mut words = [0_u64; 16];
|
|
683
|
+
if unsafe { sn_integer1024(value, words.as_mut_ptr()) }.abs() > 1 {
|
|
684
|
+
return None;
|
|
685
|
+
}
|
|
686
|
+
let shift = exponent - 1023 - 52;
|
|
687
|
+
let magnitude = u128::from((bits & ((1_u64 << 52) - 1)) | (1_u64 << 52)) << (shift % 64);
|
|
688
|
+
let mut float_words = [0_u64; 16];
|
|
689
|
+
float_words[shift / 64] = magnitude as u64;
|
|
690
|
+
if shift / 64 + 1 < float_words.len() {
|
|
691
|
+
float_words[shift / 64 + 1] = (magnitude >> 64) as u64;
|
|
692
|
+
}
|
|
693
|
+
let order = float_words.iter().rev().cmp(words.iter().rev());
|
|
694
|
+
Some(if rounded.is_sign_negative() {
|
|
695
|
+
order.reverse()
|
|
696
|
+
} else {
|
|
697
|
+
order
|
|
698
|
+
})
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
impl Rules {
|
|
702
|
+
// Shared by validity scans and diagnostic prechecks. Numeric traversal
|
|
703
|
+
// borrows only opaque VALUEs and uses the compiled primitive shortcuts.
|
|
704
|
+
pub(super) fn eval_guarded(&self, required: i32, value: Value) -> Verdict {
|
|
705
|
+
let fixnum = value & self.fixnum_flag != 0;
|
|
706
|
+
if fixnum {
|
|
707
|
+
if required != 0 && required & TYPE_INTEGRAL_NUMBER == 0 {
|
|
708
|
+
return Invalid;
|
|
709
|
+
}
|
|
710
|
+
if let Some(multiple) = &self.fast_multiple {
|
|
711
|
+
return multiple.eval(immediate_integer(value));
|
|
712
|
+
}
|
|
713
|
+
if let Some(limits) = &self.fast_limits {
|
|
714
|
+
return fixnum_bounds(immediate_integer(value), limits);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
if self.bounds_view {
|
|
718
|
+
return self.eval_bounds_guarded(required, value);
|
|
719
|
+
}
|
|
720
|
+
let kind = if fixnum {
|
|
721
|
+
TYPE_INTEGRAL_NUMBER
|
|
722
|
+
} else {
|
|
723
|
+
unsafe { sn_kind(value) }
|
|
724
|
+
};
|
|
725
|
+
if kind == TYPE_UNSUPPORTED {
|
|
726
|
+
Unsupported
|
|
727
|
+
} else if required != 0 && kind & required == 0 {
|
|
728
|
+
Invalid
|
|
729
|
+
} else if kind & TYPE_NUMBER == 0 {
|
|
730
|
+
Valid
|
|
731
|
+
} else {
|
|
732
|
+
self.eval(value)
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
pub(super) fn eval_bounds_guarded(&self, required: i32, value: Value) -> Verdict {
|
|
737
|
+
if let [(bit, limit)] = self.limits.as_slice()
|
|
738
|
+
&& let Some(bound) = limit.wide_integer
|
|
739
|
+
{
|
|
740
|
+
// Both original handles are checked by the nonallocating Big/Big
|
|
741
|
+
// comparator. No magnitude export is needed for a single bound.
|
|
742
|
+
// Other kinds retain the scalar/type checks and Float tie rules.
|
|
743
|
+
let order = unsafe { sn_big_order(value, bound) };
|
|
744
|
+
if order != 3 {
|
|
745
|
+
return verdict(
|
|
746
|
+
(required == 0 || required & TYPE_INTEGRAL_NUMBER != 0)
|
|
747
|
+
&& match *bit {
|
|
748
|
+
MAXIMUM => order <= 0,
|
|
749
|
+
MINIMUM => order >= 0,
|
|
750
|
+
EXCLUSIVE_MAXIMUM => order < 0,
|
|
751
|
+
_ => order > 0,
|
|
752
|
+
},
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
let mut words = std::mem::MaybeUninit::<[u64; 2]>::uninit();
|
|
757
|
+
let mut kind = std::mem::MaybeUninit::<i32>::uninit();
|
|
758
|
+
let mut floating = std::mem::MaybeUninit::<f64>::uninit();
|
|
759
|
+
let sign = unsafe {
|
|
760
|
+
sn_number_bounds_view(
|
|
761
|
+
value,
|
|
762
|
+
words.as_mut_ptr().cast(),
|
|
763
|
+
kind.as_mut_ptr(),
|
|
764
|
+
floating.as_mut_ptr(),
|
|
765
|
+
required,
|
|
766
|
+
)
|
|
767
|
+
};
|
|
768
|
+
// The adapter always initializes kind, but only initializes the
|
|
769
|
+
// operand selected by its kind and sign. Never read unused outputs.
|
|
770
|
+
let kind = unsafe { kind.assume_init() };
|
|
771
|
+
if kind == TYPE_UNSUPPORTED {
|
|
772
|
+
return Unsupported;
|
|
773
|
+
}
|
|
774
|
+
if required != 0 && kind & required == 0 {
|
|
775
|
+
return Invalid;
|
|
776
|
+
}
|
|
777
|
+
if kind & TYPE_NUMBER == 0 {
|
|
778
|
+
return Valid;
|
|
779
|
+
}
|
|
780
|
+
if sign == ADAPTER_UNSUPPORTED {
|
|
781
|
+
let floating = unsafe { floating.assume_init() };
|
|
782
|
+
for &(bit, ref limit) in &self.limits {
|
|
783
|
+
let order = match limit.float_order(floating) {
|
|
784
|
+
Ok(order) => order,
|
|
785
|
+
Err(status) => return status,
|
|
786
|
+
};
|
|
787
|
+
if match bit {
|
|
788
|
+
MAXIMUM => order == Some(Ordering::Greater),
|
|
789
|
+
MINIMUM => order == Some(Ordering::Less),
|
|
790
|
+
EXCLUSIVE_MAXIMUM => matches!(order, Some(Ordering::Greater | Ordering::Equal)),
|
|
791
|
+
_ => matches!(order, Some(Ordering::Less | Ordering::Equal)),
|
|
792
|
+
} {
|
|
793
|
+
return Invalid;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
return Valid;
|
|
797
|
+
}
|
|
798
|
+
let integer = if sign.abs() > 1 {
|
|
799
|
+
None
|
|
800
|
+
} else {
|
|
801
|
+
integer_words_status(&unsafe { words.assume_init() }, sign).ok()
|
|
802
|
+
};
|
|
803
|
+
if integer.is_none()
|
|
804
|
+
&& let Some(signs) = self.bounded_signs
|
|
805
|
+
{
|
|
806
|
+
// These Integer limits all fit i128. Every larger magnitude is
|
|
807
|
+
// strictly outside them; compilation already combined their bits.
|
|
808
|
+
return verdict(signs[(sign > 0) as usize]);
|
|
809
|
+
}
|
|
810
|
+
for &(bit, ref limit) in &self.limits {
|
|
811
|
+
let Some(order) = limit.integer_order(value, integer, sign) else {
|
|
812
|
+
return Unsupported;
|
|
813
|
+
};
|
|
814
|
+
if match bit {
|
|
815
|
+
MAXIMUM => order == Ordering::Greater,
|
|
816
|
+
MINIMUM => order == Ordering::Less,
|
|
817
|
+
EXCLUSIVE_MAXIMUM => order != Ordering::Less,
|
|
818
|
+
_ => order != Ordering::Greater,
|
|
819
|
+
} {
|
|
820
|
+
return Invalid;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
Valid
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
pub(super) fn eval(&self, value: Value) -> Verdict {
|
|
827
|
+
if let Some(multiple) = &self.fast_multiple
|
|
828
|
+
&& value & self.fixnum_flag != 0
|
|
829
|
+
{
|
|
830
|
+
return multiple.eval(immediate_integer(value));
|
|
831
|
+
}
|
|
832
|
+
if let Some(limits) = &self.fast_limits
|
|
833
|
+
&& value & self.fixnum_flag != 0
|
|
834
|
+
{
|
|
835
|
+
return fixnum_bounds(immediate_integer(value), limits);
|
|
836
|
+
}
|
|
837
|
+
self.check(value, |_| true)
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
pub(super) fn failures(&self, value: Value) -> Result<i32, Verdict> {
|
|
841
|
+
if let Some(multiple) = &self.fast_multiple
|
|
842
|
+
&& value & self.fixnum_flag != 0
|
|
843
|
+
{
|
|
844
|
+
return Ok(multiple.failures(immediate_integer(value)));
|
|
845
|
+
}
|
|
846
|
+
if let Some(limits) = &self.fast_limits
|
|
847
|
+
&& value & self.fixnum_flag != 0
|
|
848
|
+
{
|
|
849
|
+
let n = immediate_integer(value);
|
|
850
|
+
return Ok(limits.iter().fold(0, |bits, &(bit, limit)| {
|
|
851
|
+
bits | if match bit {
|
|
852
|
+
MAXIMUM => n > limit,
|
|
853
|
+
MINIMUM => n < limit,
|
|
854
|
+
EXCLUSIVE_MAXIMUM => n >= limit,
|
|
855
|
+
_ => n <= limit,
|
|
856
|
+
} {
|
|
857
|
+
bit
|
|
858
|
+
} else {
|
|
859
|
+
0
|
|
860
|
+
}
|
|
861
|
+
}));
|
|
862
|
+
}
|
|
863
|
+
let mut bits = 0;
|
|
864
|
+
let status = self.check(value, |bit| {
|
|
865
|
+
bits |= bit;
|
|
866
|
+
false
|
|
867
|
+
});
|
|
868
|
+
if status == Unsupported {
|
|
869
|
+
Err(status)
|
|
870
|
+
} else {
|
|
871
|
+
Ok(bits)
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
fn check_wide_multiple(
|
|
876
|
+
&self,
|
|
877
|
+
value: Value,
|
|
878
|
+
divisor: Fraction,
|
|
879
|
+
sum_words: bool,
|
|
880
|
+
mut failure: impl FnMut(i32) -> bool,
|
|
881
|
+
) -> Verdict {
|
|
882
|
+
let mut words = [0_u64; 16];
|
|
883
|
+
let sign = unsafe { sn_integer1024(value, words.as_mut_ptr()) };
|
|
884
|
+
let integer = integer_words_status(&words, sign).ok();
|
|
885
|
+
let mut invalid = false;
|
|
886
|
+
for &(bit, ref limit) in &self.limits {
|
|
887
|
+
let Some(order) = limit.integer_order(value, integer, sign) else {
|
|
888
|
+
return Unsupported;
|
|
889
|
+
};
|
|
890
|
+
if match bit {
|
|
891
|
+
MAXIMUM => order == Ordering::Greater,
|
|
892
|
+
MINIMUM => order == Ordering::Less,
|
|
893
|
+
EXCLUSIVE_MAXIMUM => order != Ordering::Less,
|
|
894
|
+
_ => order != Ordering::Greater,
|
|
895
|
+
} {
|
|
896
|
+
invalid = true;
|
|
897
|
+
if failure(bit) {
|
|
898
|
+
return Invalid;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
let result = if let Some(n) = integer {
|
|
903
|
+
verdict(
|
|
904
|
+
Fraction {
|
|
905
|
+
numerator: n,
|
|
906
|
+
denominator: 1,
|
|
907
|
+
}
|
|
908
|
+
.multiple_of(divisor),
|
|
909
|
+
)
|
|
910
|
+
} else if sign.abs() > 1 {
|
|
911
|
+
Unsupported
|
|
912
|
+
} else {
|
|
913
|
+
wide_multiple_words(&words, divisor, sum_words)
|
|
914
|
+
};
|
|
915
|
+
match result {
|
|
916
|
+
Unsupported => return Unsupported,
|
|
917
|
+
Invalid => {
|
|
918
|
+
invalid = true;
|
|
919
|
+
failure(MULTIPLE_OF);
|
|
920
|
+
}
|
|
921
|
+
Valid => {}
|
|
922
|
+
}
|
|
923
|
+
verdict(!invalid)
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
fn check(&self, value: Value, mut failure: impl FnMut(i32) -> bool) -> Verdict {
|
|
927
|
+
let mut invalid = false;
|
|
928
|
+
let float = unsafe { sn_is_float(value) } != 0;
|
|
929
|
+
if !float
|
|
930
|
+
&& value & self.fixnum_flag == 0
|
|
931
|
+
&& let Some(limit) = &self.multiple
|
|
932
|
+
&& let Some(exact) = limit.exact
|
|
933
|
+
&& exact.numerator > 1
|
|
934
|
+
{
|
|
935
|
+
return self.check_wide_multiple(value, exact, limit.sum_words, failure);
|
|
936
|
+
}
|
|
937
|
+
let (integer, large_sign) = if float {
|
|
938
|
+
(None, 0)
|
|
939
|
+
} else {
|
|
940
|
+
match read_known_integer_status(value, self.fixnum_flag) {
|
|
941
|
+
Ok(integer) => (Some(integer), 0),
|
|
942
|
+
Err(sign) => (None, sign),
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
if !float && integer.is_none() {
|
|
946
|
+
// An ordinary Integer outside i128 is beyond every representable
|
|
947
|
+
// exact limit (whose denominator is positive). Bounds alone need
|
|
948
|
+
// only its sign. Multiples can use a bounded magnitude below.
|
|
949
|
+
if large_sign == 0 {
|
|
950
|
+
return Unsupported;
|
|
951
|
+
}
|
|
952
|
+
for (bit, limit) in &self.limits {
|
|
953
|
+
let Some(order) = limit.integer_order(value, None, large_sign) else {
|
|
954
|
+
return Unsupported;
|
|
955
|
+
};
|
|
956
|
+
let violates = match *bit {
|
|
957
|
+
MAXIMUM => order == Ordering::Greater,
|
|
958
|
+
MINIMUM => order == Ordering::Less,
|
|
959
|
+
EXCLUSIVE_MAXIMUM => order != Ordering::Less,
|
|
960
|
+
_ => order != Ordering::Greater,
|
|
961
|
+
};
|
|
962
|
+
if violates {
|
|
963
|
+
invalid = true;
|
|
964
|
+
if failure(*bit) {
|
|
965
|
+
return Invalid;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
if let Some(limit) = &self.multiple {
|
|
970
|
+
let Some(exact) = limit.exact else {
|
|
971
|
+
return Unsupported;
|
|
972
|
+
};
|
|
973
|
+
match wide_integer_multiple(value, exact, limit.sum_words) {
|
|
974
|
+
Unsupported => return Unsupported,
|
|
975
|
+
Invalid => {
|
|
976
|
+
invalid = true;
|
|
977
|
+
failure(MULTIPLE_OF);
|
|
978
|
+
}
|
|
979
|
+
Valid => {}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
return verdict(!invalid);
|
|
983
|
+
}
|
|
984
|
+
let decimal = if self.multiple.is_some() {
|
|
985
|
+
if let Some(integer) = integer {
|
|
986
|
+
Some(Fraction {
|
|
987
|
+
numerator: integer,
|
|
988
|
+
denominator: 1,
|
|
989
|
+
})
|
|
990
|
+
} else {
|
|
991
|
+
let Some(decimal) = decimal_float(unsafe { sn_float(value) }) else {
|
|
992
|
+
return Unsupported;
|
|
993
|
+
};
|
|
994
|
+
Some(decimal)
|
|
995
|
+
}
|
|
996
|
+
} else {
|
|
997
|
+
None
|
|
998
|
+
};
|
|
999
|
+
for (bit, limit) in &self.limits {
|
|
1000
|
+
let order = if integer.is_some() {
|
|
1001
|
+
let Some(order) = limit.integer_order(value, integer, 0) else {
|
|
1002
|
+
return Unsupported;
|
|
1003
|
+
};
|
|
1004
|
+
Some(order)
|
|
1005
|
+
} else if let Some(decimal) = decimal {
|
|
1006
|
+
if let Some(exact) = limit.exact {
|
|
1007
|
+
Some(decimal.compare(exact))
|
|
1008
|
+
} else if limit.wide_integer.is_some() {
|
|
1009
|
+
// The reduced decimal numerator fits i128 and its
|
|
1010
|
+
// denominator is positive. A bound outside i128 is
|
|
1011
|
+
// strictly beyond it; no Float/Big comparison is needed.
|
|
1012
|
+
Some(if limit.rounded.is_sign_negative() {
|
|
1013
|
+
Ordering::Greater
|
|
1014
|
+
} else {
|
|
1015
|
+
Ordering::Less
|
|
1016
|
+
})
|
|
1017
|
+
} else {
|
|
1018
|
+
return Unsupported;
|
|
1019
|
+
}
|
|
1020
|
+
} else {
|
|
1021
|
+
let n = unsafe { sn_float(value) };
|
|
1022
|
+
if limit.integer {
|
|
1023
|
+
match limit.float_order(n) {
|
|
1024
|
+
Ok(order) => order,
|
|
1025
|
+
Err(status) => return status,
|
|
1026
|
+
}
|
|
1027
|
+
} else {
|
|
1028
|
+
n.partial_cmp(&limit.rounded)
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
let violates = match *bit {
|
|
1032
|
+
MAXIMUM => order == Some(Ordering::Greater),
|
|
1033
|
+
MINIMUM => order == Some(Ordering::Less),
|
|
1034
|
+
EXCLUSIVE_MAXIMUM => matches!(order, Some(Ordering::Greater | Ordering::Equal)),
|
|
1035
|
+
_ => matches!(order, Some(Ordering::Less | Ordering::Equal)),
|
|
1036
|
+
};
|
|
1037
|
+
if violates {
|
|
1038
|
+
invalid = true;
|
|
1039
|
+
if failure(*bit) {
|
|
1040
|
+
return Invalid;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
if let Some(limit) = &self.multiple {
|
|
1045
|
+
let Some(exact) = limit.exact else {
|
|
1046
|
+
return Unsupported;
|
|
1047
|
+
};
|
|
1048
|
+
if !decimal.unwrap().multiple_of(exact) {
|
|
1049
|
+
invalid = true;
|
|
1050
|
+
failure(MULTIPLE_OF);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
verdict(!invalid)
|
|
1054
|
+
}
|
|
1055
|
+
}
|