qr_reader-native 0.0.1

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 (95) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-mruby.txt +22 -0
  3. data/LICENSE-spinel.txt +19 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +61 -0
  6. data/ext/qr_reader_native/SPINEL_VERSION +1 -0
  7. data/ext/qr_reader_native/extconf.rb +8 -0
  8. data/ext/qr_reader_native/mruby/error.h +13 -0
  9. data/ext/qr_reader_native/mruby/internal.h +145 -0
  10. data/ext/qr_reader_native/mruby/string.h +22 -0
  11. data/ext/qr_reader_native/mruby.h +93 -0
  12. data/ext/qr_reader_native/mruby_shim.h +118 -0
  13. data/ext/qr_reader_native/qr_reader_native.c +13513 -0
  14. data/ext/qr_reader_native/qr_reader_native.h +29 -0
  15. data/ext/qr_reader_native/qr_reader_native_ext.c +598 -0
  16. data/ext/qr_reader_native/re_cased.h +71 -0
  17. data/ext/qr_reader_native/re_compile.c +5784 -0
  18. data/ext/qr_reader_native/re_ctype.h +3540 -0
  19. data/ext/qr_reader_native/re_exec.c +2190 -0
  20. data/ext/qr_reader_native/re_internal.h +671 -0
  21. data/ext/qr_reader_native/re_prop.h +4629 -0
  22. data/ext/qr_reader_native/re_spinel.c +363 -0
  23. data/ext/qr_reader_native/re_spinel.h +22 -0
  24. data/ext/qr_reader_native/re_spinel_utf8.inc +97 -0
  25. data/ext/qr_reader_native/re_utf8.c +156 -0
  26. data/ext/qr_reader_native/sp_alloc.c +1447 -0
  27. data/ext/qr_reader_native/sp_alloc.h +1196 -0
  28. data/ext/qr_reader_native/sp_argf.h +33 -0
  29. data/ext/qr_reader_native/sp_array.c +1052 -0
  30. data/ext/qr_reader_native/sp_array.h +456 -0
  31. data/ext/qr_reader_native/sp_bigint.c +5826 -0
  32. data/ext/qr_reader_native/sp_bigint.h +117 -0
  33. data/ext/qr_reader_native/sp_cold.c +5259 -0
  34. data/ext/qr_reader_native/sp_compat.h +402 -0
  35. data/ext/qr_reader_native/sp_core.c +818 -0
  36. data/ext/qr_reader_native/sp_core.h +56 -0
  37. data/ext/qr_reader_native/sp_crypto.c +877 -0
  38. data/ext/qr_reader_native/sp_crypto.h +169 -0
  39. data/ext/qr_reader_native/sp_dtoa.c +1533 -0
  40. data/ext/qr_reader_native/sp_dtoa.h +29 -0
  41. data/ext/qr_reader_native/sp_encoding_names.h +172 -0
  42. data/ext/qr_reader_native/sp_enum.h +86 -0
  43. data/ext/qr_reader_native/sp_exc.c +1185 -0
  44. data/ext/qr_reader_native/sp_exc.h +186 -0
  45. data/ext/qr_reader_native/sp_exc_ctx.h +56 -0
  46. data/ext/qr_reader_native/sp_fiber.c +889 -0
  47. data/ext/qr_reader_native/sp_fiber.h +162 -0
  48. data/ext/qr_reader_native/sp_fiber_ctx.h +84 -0
  49. data/ext/qr_reader_native/sp_format.c +695 -0
  50. data/ext/qr_reader_native/sp_format.h +83 -0
  51. data/ext/qr_reader_native/sp_gc.c +1793 -0
  52. data/ext/qr_reader_native/sp_gc.h +762 -0
  53. data/ext/qr_reader_native/sp_hash.c +131 -0
  54. data/ext/qr_reader_native/sp_hash.h +131 -0
  55. data/ext/qr_reader_native/sp_inspect.c +215 -0
  56. data/ext/qr_reader_native/sp_inspect.h +128 -0
  57. data/ext/qr_reader_native/sp_io.c +1676 -0
  58. data/ext/qr_reader_native/sp_io.h +272 -0
  59. data/ext/qr_reader_native/sp_iobuffer.c +1014 -0
  60. data/ext/qr_reader_native/sp_iobuffer.h +167 -0
  61. data/ext/qr_reader_native/sp_marshal.c +487 -0
  62. data/ext/qr_reader_native/sp_marshal.h +60 -0
  63. data/ext/qr_reader_native/sp_net.c +1029 -0
  64. data/ext/qr_reader_native/sp_net.h +205 -0
  65. data/ext/qr_reader_native/sp_pack.c +1254 -0
  66. data/ext/qr_reader_native/sp_poly_cold.c +1220 -0
  67. data/ext/qr_reader_native/sp_proc.c +410 -0
  68. data/ext/qr_reader_native/sp_proc.h +327 -0
  69. data/ext/qr_reader_native/sp_process.c +547 -0
  70. data/ext/qr_reader_native/sp_process_status.c +211 -0
  71. data/ext/qr_reader_native/sp_process_status.h +66 -0
  72. data/ext/qr_reader_native/sp_random.c +287 -0
  73. data/ext/qr_reader_native/sp_random.h +52 -0
  74. data/ext/qr_reader_native/sp_range.h +144 -0
  75. data/ext/qr_reader_native/sp_re.c +1379 -0
  76. data/ext/qr_reader_native/sp_re.h +186 -0
  77. data/ext/qr_reader_native/sp_re_names.h +59 -0
  78. data/ext/qr_reader_native/sp_sched.c +4044 -0
  79. data/ext/qr_reader_native/sp_sched.h +295 -0
  80. data/ext/qr_reader_native/sp_slab.c +1507 -0
  81. data/ext/qr_reader_native/sp_str.c +1462 -0
  82. data/ext/qr_reader_native/sp_str.h +306 -0
  83. data/ext/qr_reader_native/sp_str_crypt.c +23 -0
  84. data/ext/qr_reader_native/sp_string.c +92 -0
  85. data/ext/qr_reader_native/sp_string.h +253 -0
  86. data/ext/qr_reader_native/sp_system.c +78 -0
  87. data/ext/qr_reader_native/sp_system.h +18 -0
  88. data/ext/qr_reader_native/sp_time.c +860 -0
  89. data/ext/qr_reader_native/sp_time.h +100 -0
  90. data/ext/qr_reader_native/sp_types.h +292 -0
  91. data/ext/qr_reader_native/spinel_rt.h +19017 -0
  92. data/ext/qr_reader_native/unicase.c +399 -0
  93. data/ext/qr_reader_native/unicase.h +901 -0
  94. data/lib/qr_reader/native.rb +6 -0
  95. metadata +153 -0
@@ -0,0 +1,818 @@
1
+ /* sp_core.c -- runtime helpers split out of spinel_rt.h into
2
+ * libspinel_rt.a. See sp_core.h for the rationale. */
3
+ #include "sp_core.h"
4
+ #include <float.h>
5
+ #include <limits.h>
6
+ #include <string.h>
7
+ #include "sp_alloc.h" /* sp_str_byte_len: embedded-NUL detection in Integer()/Float() */
8
+ #include "sp_dtoa.h" /* sp_read_float: locale-independent String#to_f / Float() */
9
+ #include <stdlib.h>
10
+ #include <ctype.h>
11
+ #include <errno.h>
12
+ #include <locale.h>
13
+ /* macOS keeps the *_l / locale_t surface in <xlocale.h>; glibc exposes it from
14
+ <locale.h> under POSIX.1-2008. Guarded so a platform with neither is not a
15
+ build error either. (#3370) */
16
+ #if defined(__has_include)
17
+ # if __has_include(<xlocale.h>)
18
+ # include <xlocale.h>
19
+ # endif
20
+ #endif
21
+
22
+ /* Must match sp_types.h: sp_int is pointer-width (int64 on 64-bit,
23
+ int32 on 32-bit) so this TU's helper ABI agrees with the generated TU. */
24
+ typedef intptr_t sp_int;
25
+ typedef double sp_float;
26
+ /* 10^p fits sp_int only up to this exponent (p>= it collapses to 0):
27
+ 10^19 > INT64_MAX, 10^10 > INT32_MAX. */
28
+ #if INTPTR_MAX == INT32_MAX
29
+ #define SP_INT_POW10_LIMIT 10
30
+ #else
31
+ #define SP_INT_POW10_LIMIT 19
32
+ #endif
33
+
34
+ /* Defined in the generated translation unit (spinel_rt.h); referenced
35
+ here and resolved at link time. */
36
+ SP_NORETURN void sp_raise_cls(const char *cls, const char *msg);
37
+ const char *sp_sprintf(const char *fmt, ...);
38
+
39
+ /* CRuby's `String#to_i` accepts a leading sign, then digits with
40
+ `_` between consecutive digits, and stops at the first non-digit
41
+ (returning what it has so far rather than raising). `"1_2_3asdf"`
42
+ -> 123. spinel previously emitted `(sp_int)atoll(s)` which stops
43
+ at the first `_`, returning 1 instead. Issue #619. */
44
+ sp_int sp_str_to_i_cruby(const char *s) {SP_GC_ROOT_STR(s);
45
+ if (!s) return 0;
46
+ const char *p = s;
47
+ while (isspace((unsigned char)*p)) p++;
48
+ int neg = 0;
49
+ if (*p == '+') p++;
50
+ else if (*p == '-') { neg = 1; p++; }
51
+ sp_int v = 0;
52
+ int any = 0;
53
+ while (*p) {
54
+ if (*p >= '0' && *p <= '9') {
55
+ /* Accumulate the signed value: the magnitude of INTPTR_MIN does not
56
+ fit sp_int, even though the negative value does. Signed-overflow
57
+ on `v * 10 + digit` is undefined behavior;
58
+ detect via sp_ckd_*_iptr (sp_compat.h). CRuby promotes to Bignum
59
+ on overflow but spinel's int model is int64-only -- raise
60
+ RangeError instead of silently saturating, so a user-side
61
+ `rescue` can react. */
62
+ sp_int t, digit = (sp_int)(*p - '0');
63
+ if (neg) digit = -digit;
64
+ if (sp_ckd_mul_iptr(v, 10, &t) ||
65
+ sp_ckd_add_iptr(t, digit, &v)) {
66
+ sp_raise_cls("RangeError", sp_sprintf("integer overflow parsing \"%s\"", s));
67
+ }
68
+ any = 1;
69
+ p++;
70
+ }
71
+ else if (*p == '_' && any && p[1] >= '0' && p[1] <= '9') {
72
+ p++;
73
+ }
74
+ else {
75
+ break;
76
+ }
77
+ }
78
+ if (!any) return 0;
79
+ return v;
80
+ }
81
+
82
+ /* `String#to_f`: parse a leading float, tolerating `_` between digits as a
83
+ separator (CRuby's `"1_000.5".to_f == 1000.5`), returning 0.0 when no
84
+ number leads. Underscores are stripped into a small scratch buffer before
85
+ strtod so the C parser (which stops at `_`) sees clean digits. */
86
+ double sp_str_to_f_cruby(const char *s) {
87
+ if (!s) return 0.0;
88
+ const char *p = s;
89
+ while (isspace((unsigned char)*p)) p++;
90
+ /* copy the leading numeric run, dropping `_` that sit between two digits */
91
+ char buf[512];
92
+ size_t n = 0;
93
+ if (*p == '+' || *p == '-') { if (n < sizeof buf - 1) buf[n++] = *p; p++; }
94
+ for (; *p && n < sizeof buf - 1; p++) {
95
+ if ((*p >= '0' && *p <= '9') || *p == '.' || *p == 'e' || *p == 'E' ||
96
+ *p == '+' || *p == '-') {
97
+ buf[n++] = *p;
98
+ }
99
+ else if (*p == '_' && n > 0 &&
100
+ ((buf[n-1] >= '0' && buf[n-1] <= '9')) &&
101
+ (p[1] >= '0' && p[1] <= '9')) {
102
+ /* separator between digits: skip it */
103
+ }
104
+ else {
105
+ break;
106
+ }
107
+ }
108
+ buf[n] = 0;
109
+ double d = 0.0;
110
+ sp_read_float(buf, NULL, &d); /* locale-independent parse */
111
+ return d;
112
+ }
113
+
114
+ /* Digit value of `c` in bases up to 36, or -1. */
115
+ static int sp_digit36(int c) {
116
+ if (c >= '0' && c <= '9') return c - '0';
117
+ if (c >= 'a' && c <= 'z') return c - 'a' + 10;
118
+ if (c >= 'A' && c <= 'Z') return c - 'A' + 10;
119
+ return -1;
120
+ }
121
+
122
+ /* Skips leading whitespace and sign, resolves base 0 from the prefix
123
+ (0x -> 16, 0b -> 2, 0/0o -> 8, 0d -> 10, otherwise 10), and skips a
124
+ prefix matching the base. Per CRuby, only base 0 enables prefix-based
125
+ dispatch -- explicit bases just *accept* the matching prefix. A leading
126
+ 0 followed by anything but a prefix letter is octal whatever comes next,
127
+ so "08" stops at the 8 (Integer() refuses it) rather than reading 8. */
128
+ static const char *sp_int_head(const char *p, sp_int *base, int *neg) {
129
+ while (isspace((unsigned char)*p)) p++;
130
+ *neg = 0;
131
+ if (*p == '+') p++;
132
+ else if (*p == '-') { *neg = 1; p++; }
133
+ if (*base == 0) {
134
+ *base = 10;
135
+ if (*p == '0') {
136
+ char n = p[1];
137
+ if (n == 'x' || n == 'X') *base = 16;
138
+ else if (n == 'b' || n == 'B') *base = 2;
139
+ else if (n == 'o' || n == 'O') *base = 8;
140
+ else if (n != 0 && n != 'd' && n != 'D') *base = 8;
141
+ }
142
+ }
143
+ if (*p == '0' && p[1] != 0) {
144
+ char n = p[1];
145
+ if ((*base == 16) && (n == 'x' || n == 'X')) p += 2;
146
+ else if ((*base == 2) && (n == 'b' || n == 'B')) p += 2;
147
+ else if ((*base == 8) && (n == 'o' || n == 'O')) p += 2;
148
+ /* base 10 accepts the explicit decimal prefix too (#3719) */
149
+ else if ((*base == 10) && (n == 'd' || n == 'D')) p += 2;
150
+ }
151
+ return p;
152
+ }
153
+
154
+ /* Accumulates digits of `base` into *v, consuming `_` only between
155
+ digits; *any is set once a digit is read. Returns the stop position,
156
+ or NULL on sp_int overflow. */
157
+ static const char *sp_int_scan(const char *p, sp_int base, sp_int *v, int *any) {
158
+ *v = 0;
159
+ *any = 0;
160
+ for (;; p++) {
161
+ int d = sp_digit36((unsigned char)*p);
162
+ if (d < 0 || d >= (int)base) {
163
+ if (*p == '_' && *any) {
164
+ int n = sp_digit36((unsigned char)p[1]);
165
+ if (n >= 0 && n < (int)base) continue;
166
+ }
167
+ return p;
168
+ }
169
+ sp_int t;
170
+ if (sp_ckd_mul_iptr(*v, base, &t) ||
171
+ sp_ckd_add_iptr(t, (sp_int)d, v)) return NULL;
172
+ *any = 1;
173
+ }
174
+ }
175
+
176
+ /* The promote-mode parsers (sp_str_to_i_promote, spinel_rt.h) read the
177
+ same text as the sp_int ones below, and take a Bignum where the sp_int
178
+ scan overflows. Whether it does, for `s` in `base` (0 resolves from the
179
+ prefix): */
180
+ int sp_str_int_overflows(const char *s, intptr_t base) {
181
+ if (!s) return 0;
182
+ int neg, any;
183
+ sp_int v, b = base;
184
+ const char *p = sp_int_head(s, &b, &neg);
185
+ if (b < 2 || b > 36) return 0;
186
+ return sp_int_scan(p, b, &v, &any) == NULL;
187
+ }
188
+
189
+ /* ...and the digits as the Bignum parser wants them: a '-' for a negative
190
+ value, then the digits without their `_` separators. *base comes back
191
+ resolved, *rest just past the last digit. A malloc'd string. */
192
+ char *sp_int_digits_dup(const char *s, intptr_t *base, const char **rest) {
193
+ int neg, any = 0;
194
+ sp_int b = *base;
195
+ const char *p = sp_int_head(s, &b, &neg);
196
+ size_t k = 0;
197
+ char *out = (char *)malloc(strlen(p) + 2);
198
+ if (!out) return NULL;
199
+ if (neg) out[k++] = '-';
200
+ for (;; p++) {
201
+ int d = sp_digit36((unsigned char)*p);
202
+ if (d < 0 || d >= (int)b) {
203
+ if (*p == '_' && any) {
204
+ int n = sp_digit36((unsigned char)p[1]);
205
+ if (n >= 0 && n < (int)b) continue;
206
+ }
207
+ break;
208
+ }
209
+ out[k++] = *p;
210
+ any = 1;
211
+ }
212
+ out[k] = 0;
213
+ *base = b;
214
+ if (rest) *rest = p;
215
+ return out;
216
+ }
217
+
218
+ /* `String#to_i(base)` with a non-decimal base. Accepts bases 2..36
219
+ like MRI; `_` is allowed between digits the same way as base 10.
220
+ Stops at the first invalid digit and returns what's parsed so
221
+ far. Base 0 auto-detects from the prefix. Issue #883. */
222
+ sp_int sp_str_to_i_base(const char *s, sp_int base) {SP_GC_ROOT_STR(s);
223
+ if (!s) return 0;
224
+ /* CRuby rejects a radix outside 2..36 rather than falling back to 10 */
225
+ if (base != 0 && (base < 2 || base > 36))
226
+ sp_raise_cls("ArgumentError", sp_sprintf("invalid radix %lld", (long long)base));
227
+ int neg, any;
228
+ sp_int v;
229
+ const char *p = sp_int_head(s, &base, &neg);
230
+ if (!sp_int_scan(p, base, &v, &any))
231
+ sp_raise_cls("RangeError", sp_sprintf("integer overflow parsing \"%s\"", s));
232
+ if (!any) return 0;
233
+ return neg ? -v : v;
234
+ }
235
+
236
+ /* CRuby's `Integer(s)` raises ArgumentError for unparseable input
237
+ (empty string, leading/trailing junk, all-whitespace). The bare
238
+ `(sp_int)strtoll(s, NULL, 10)` spinel previously emitted silently
239
+ returned 0 instead, which made `Integer(s) rescue 0` always take
240
+ the main branch. This helper matches CRuby semantics: skips
241
+ leading/trailing whitespace, requires at least one valid digit,
242
+ rejects trailing junk. Accepts an optional leading `+` / `-`. */
243
+ sp_int sp_str_to_i_strict(const char *s) {SP_GC_ROOT_STR(s);
244
+ if (!s) sp_raise_cls("ArgumentError", "invalid value for Integer(): nil");
245
+ /* an embedded NUL makes the Ruby string longer than its C prefix: CRuby
246
+ rejects it, a C-string scan would silently parse the prefix. */
247
+ if (strlen(s) != sp_str_byte_len(s))
248
+ sp_raise_cls("ArgumentError", sp_sprintf("invalid value for Integer(): \"%s\"", s));
249
+ /* Delegate to the base-aware parser with auto-detection: unlike a bare
250
+ strtoll it handles digit-separating underscores ("1_000") and CRuby's
251
+ prefix bases ("0x1A", "0b101", and leading-0 octal "077" -> 63). */
252
+ return sp_str_to_i_strict_base(s, 0);
253
+ }
254
+
255
+ /* `Integer(s, base)` with explicit base. Bases 2..36, MRI-compatible
256
+ prefix recognition (0x / 0b / 0o when the base matches). Raises
257
+ ArgumentError on invalid input or unsupported base. Issue #887. */
258
+ /* The shared body. `lenient` is Kernel#Integer's `exception: false`: every
259
+ rejection answers nil (SP_INT_NIL) instead of raising (#3718). */
260
+ static sp_int sp_str_to_i_base_impl(const char *s, sp_int base, int lenient) {SP_GC_ROOT_STR(s);
261
+ #define SP_INT_REJECT(cls, msg) do { if (lenient) return SP_INT_NIL; sp_raise_cls(cls, msg); } while (0)
262
+ if (!s) SP_INT_REJECT("ArgumentError", "invalid value for Integer(): nil");
263
+ /* an embedded NUL makes the Ruby string longer than its C prefix: CRuby
264
+ rejects it, a C-string scan would silently parse the prefix. */
265
+ if (strlen(s) != sp_str_byte_len(s))
266
+ SP_INT_REJECT("ArgumentError", sp_sprintf("invalid value for Integer(): \"%s\"", s));
267
+ int neg, any;
268
+ sp_int v;
269
+ const char *p = sp_int_head(s, &base, &neg);
270
+ if (base < 2 || base > 36) SP_INT_REJECT("ArgumentError", sp_sprintf("invalid radix %lld", (long long)base));
271
+ if (*p == '\0') SP_INT_REJECT("ArgumentError", sp_sprintf("invalid value for Integer(): \"%s\"", s));
272
+ p = sp_int_scan(p, base, &v, &any);
273
+ if (!p) SP_INT_REJECT("RangeError", sp_sprintf("integer overflow parsing \"%s\"", s));
274
+ if (!any) SP_INT_REJECT("ArgumentError", sp_sprintf("invalid value for Integer(): \"%s\"", s));
275
+ while (isspace((unsigned char)*p)) p++;
276
+ if (*p != '\0') SP_INT_REJECT("ArgumentError", sp_sprintf("invalid value for Integer(): \"%s\"", s));
277
+ return neg ? -v : v;
278
+ #undef SP_INT_REJECT
279
+ }
280
+ sp_int sp_str_to_i_strict_base(const char *s, sp_int base) {
281
+ return sp_str_to_i_base_impl(s, base, 0);
282
+ }
283
+ /* Kernel#Integer(s[, base], exception: false) */
284
+ sp_int sp_str_to_i_lenient_base(const char *s, sp_int base) {
285
+ return sp_str_to_i_base_impl(s, base, 1);
286
+ }
287
+
288
+ /* The decimal text Float() accepts once its underscores are stripped: an
289
+ optional sign, digits, an optional '.' that may have no digit after it
290
+ ("5.", "1.e5"; at least one digit in all), and an optional exponent that
291
+ has a digit of its own. Nothing may follow, so a second point is refused. */
292
+ static int sp_float_text_shape_ok(const char *p) {
293
+ int nd = 0;
294
+ if (*p == '+' || *p == '-') p++;
295
+ while (isdigit((unsigned char)*p)) { p++; nd++; }
296
+ if (*p == '.') { p++; while (isdigit((unsigned char)*p)) { p++; nd++; } }
297
+ if (!nd) return 0;
298
+ if (*p == 'e' || *p == 'E') {
299
+ p++;
300
+ if (*p == '+' || *p == '-') p++;
301
+ if (!isdigit((unsigned char)*p)) return 0;
302
+ while (isdigit((unsigned char)*p)) p++;
303
+ }
304
+ return *p == '\0';
305
+ }
306
+
307
+ /* Kernel#Float() raises ArgumentError on unparseable input. strtod
308
+ on its own would silently return 0.0 for "abc" or empty input;
309
+ match MRI semantics by validating at-least-one-digit + no-trailing-
310
+ junk. Whitespace flanking is fine. Issue #888. */
311
+ static sp_float sp_str_to_f_impl(const char *s, int lenient) {SP_GC_ROOT_STR(s);
312
+ if (!s) { if (lenient) return sp_float_nil(); sp_raise_cls("ArgumentError", "invalid value for Float(): nil"); }
313
+ /* embedded NUL: the Ruby string extends past its C prefix -- reject rather
314
+ than silently parsing the prefix ("1\\0" is not a float in CRuby). */
315
+ size_t blen = sp_str_byte_len(s);
316
+ if (strlen(s) != blen) goto bad0;
317
+ {
318
+ /* Clean into a buffer, enforcing CRuby's shape rules that strtod is looser
319
+ about: an '_' only BETWEEN two digits of the active base (stripped);
320
+ a hex literal is integral (hex digits only after 0x); at least one real
321
+ digit must appear (rejects "inf"/"nan", which strtod would parse). A
322
+ decimal literal's own shape is checked below once it is copied. */
323
+ size_t n = strlen(s);
324
+ char sbuf[256];
325
+ char *buf = n < sizeof sbuf ? sbuf : (char *)malloc(n + 1);
326
+ if (!buf) { perror("malloc"); exit(1); }
327
+ size_t o = 0;
328
+ int hex = 0, sawdigit = 0, sawp = 0, sawdot = 0;
329
+ const char *q = s;
330
+ while (isspace((unsigned char)*q)) q++;
331
+ const char *start = q;
332
+ if (*q == '+' || *q == '-') buf[o++] = *q++;
333
+ if (q[0] == '0' && (q[1] == 'x' || q[1] == 'X')) { hex = 1; buf[o++] = *q++; buf[o++] = *q++; }
334
+ for (; *q && !isspace((unsigned char)*q); q++) {
335
+ char ch = *q;
336
+ if (ch == '_') {
337
+ int pd = q > start && (hex ? isxdigit((unsigned char)q[-1]) : isdigit((unsigned char)q[-1]));
338
+ int nd = hex ? isxdigit((unsigned char)q[1]) : isdigit((unsigned char)q[1]);
339
+ if (!(pd && nd)) goto bad;
340
+ continue; /* a valid digit separator: strip */
341
+ }
342
+ if (hex) {
343
+ /* hex FLOATS are valid Float() input: 0x1p4 / 0x1.8p-1 (hex digits,
344
+ an optional single point before the mandatory p-exponent, then
345
+ decimal exponent digits with an optional sign) */
346
+ /* sawp/sawdot flags, NOT strchr(buf, ...): buf is not yet
347
+ NUL-terminated inside this loop, so strchr read past the written
348
+ prefix into uninitialized stack (a stray 'p' there failed valid
349
+ inputs like "0xa" depending on the caller's stack residue) */
350
+ if (ch == 'p' || ch == 'P') {
351
+ if (!sawdigit || sawp) goto bad;
352
+ sawp = 1;
353
+ buf[o++] = ch;
354
+ if (q[1] == '+' || q[1] == '-') { buf[o++] = q[1]; q++; }
355
+ if (!isdigit((unsigned char)q[1])) goto bad;
356
+ continue;
357
+ }
358
+ if (sawp) {
359
+ if (!isdigit((unsigned char)ch)) goto bad;
360
+ buf[o++] = ch;
361
+ continue;
362
+ }
363
+ if (ch == '.') {
364
+ if (sawdot || !isxdigit((unsigned char)q[1])) goto bad;
365
+ sawdot = 1;
366
+ buf[o++] = ch;
367
+ continue;
368
+ }
369
+ if (!isxdigit((unsigned char)ch)) goto bad;
370
+ sawdigit = 1;
371
+ }
372
+ else {
373
+ if (isdigit((unsigned char)ch)) sawdigit = 1;
374
+ }
375
+ buf[o++] = ch;
376
+ }
377
+ while (isspace((unsigned char)*q)) q++;
378
+ if (*q || !sawdigit) goto bad; /* junk after spaces / no digits */
379
+ buf[o] = '\0';
380
+ /* sp_read_float below stops at the first character it cannot use and
381
+ succeeds, so text after a valid prefix ("1.0foo", "1e", "0b10") has to
382
+ be refused here: digits, an optional fraction, then an optional exponent
383
+ with at least one digit of its own. A hex literal was validated above. */
384
+ if (!hex && !sp_float_text_shape_ok(buf)) goto bad;
385
+ {
386
+ char *endptr;
387
+ double v = 0.0;
388
+ /* A hex literal (0x...) is validated integral above and has no decimal
389
+ point, so strtod (which parses hex floats) is safe and locale-neutral;
390
+ a decimal literal uses the locale-independent sp_read_float. The shape
391
+ was already validated, so a short read is malformed input. */
392
+ if (hex) {
393
+ v = strtod(buf, &endptr);
394
+ if (endptr == buf || *endptr != '\0') goto bad;
395
+ }
396
+ else {
397
+ /* The buffer is already a complete, validated float literal (a digit
398
+ appeared, no trailing junk), so sp_read_float's success is enough;
399
+ its endp is left at the start for all-zero input, so don't gate on
400
+ it consuming the buffer. */
401
+ if (!sp_read_float(buf, &endptr, &v)) goto bad;
402
+ }
403
+ if (buf != sbuf) free(buf);
404
+ return (sp_float)v;
405
+ }
406
+ bad:
407
+ if (buf != sbuf) free(buf);
408
+ }
409
+ bad0:
410
+ /* Kernel#Float(s, exception: false) answers nil for everything this rejects */
411
+ if (lenient) return sp_float_nil();
412
+ sp_raise_cls("ArgumentError", sp_sprintf("invalid value for Float(): \"%s\"", s));
413
+ return 0.0; /* unreachable */
414
+ }
415
+ sp_float sp_str_to_f_strict(const char *s) { return sp_str_to_f_impl(s, 0); }
416
+ sp_float sp_str_to_f_lenient(const char *s) { return sp_str_to_f_impl(s, 1); }
417
+
418
+ /* Kernel#sprintf's float directives (%f/%e/%g/%a with width/flags) are emitted
419
+ by faithfully delegating to libc snprintf, which is locale-sensitive for the
420
+ decimal point. Run that one call under a pinned "C" locale so the output
421
+ always uses '.', matching Ruby, regardless of the process locale. The number
422
+ primitives (Float#to_s / to_f) are locale-free via fp_uscale; this is only
423
+ for the printf-compatible field/flag machinery libc handles best. */
424
+ int sp_snprintf_c_float(char *buf, size_t size, const char *fmt, double v) {
425
+ #if defined(_WIN32)
426
+ /* the UCRT's %a keeps every trailing zero of the mantissa ("0x1.0000000000000p+0");
427
+ MinGW-w64's own C99 printf trims as glibc does */
428
+ if (strpbrk(fmt, "aA")) return __mingw_snprintf(buf, size, fmt, v);
429
+ #endif
430
+ static locale_t sp_c_loc = (locale_t)0;
431
+ if (!sp_c_loc) sp_c_loc = newlocale(LC_ALL_MASK, "C", (locale_t)0);
432
+ if (sp_c_loc) {
433
+ locale_t old = uselocale(sp_c_loc);
434
+ int n = snprintf(buf, size, fmt, v);
435
+ uselocale(old);
436
+ return n;
437
+ }
438
+ return snprintf(buf, size, fmt, v);
439
+ }
440
+
441
+ /* Ruby's float conversions go through its BSD__dtoa (missing/dtoa.c), whose
442
+ fast path generates the digits in double arithmetic and, unlike David
443
+ Gay's original, settles a remainder within its error bound of one half as
444
+ a tie, to even: `format("%.2f", 2.675)` answers 2.68 where C's printf
445
+ answers 2.67 (2.675 is stored as 2.67499999999999982), and
446
+ `format("%.2f", 0.69 / 6)` (0.11499999999999999112) answers 0.12 (#7270).
447
+ The fast path is run here as dtoa runs it (mode 3 for %f with the
448
+ precision as decimals, mode 2 for %e/%g as significant digits), and the
449
+ digits it settles on are rebuilt into the double libc then lays out. Where
450
+ dtoa leaves the fast path for its exact arithmetic (more than 14 digits, a
451
+ cut before the first digit, a denormal), the exact binary value is
452
+ rounded, which is what libc does. *keep_zeros is set when dtoa answers its
453
+ digits untrimmed: a tie settled to even whose exact value lies above the
454
+ half keeps its trailing zeros, which %g then prints (`format("%.5g",
455
+ 7.91905000000000001)` is 7.9190). */
456
+ static double sp_float_round_ruby(double v, int mode, int ndigits, int *keep_zeros) {
457
+ static const double tens[] = {
458
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14,
459
+ 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22 };
460
+ static const double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };
461
+ if (!isfinite(v) || v == 0.0) return v;
462
+ union { double f; uint64_t u; } x, d2, e;
463
+ double d = v < 0 ? -v : v;
464
+ x.f = d;
465
+ int bexp = (int)((x.u >> 52) & 0x7ff);
466
+ if (bexp == 0) return v;
467
+ /* k: dtoa's estimate of floor(log10(d)), exact when it can check it */
468
+ d2.u = (x.u & 0x000fffffffffffffULL) | 0x3ff0000000000000ULL;
469
+ int i = bexp - 1023;
470
+ double dsl = (d2.f - 1.5) * 0.289529654602168 + 0.1760912590558 + i * 0.301029995663981;
471
+ int k = (int)dsl;
472
+ if (dsl < 0. && dsl != k) k--;
473
+ int k_check = 1;
474
+ if (k >= 0 && k <= 22) {
475
+ if (d < tens[k]) k--;
476
+ k_check = 0;
477
+ }
478
+ int ilim, ilim1;
479
+ if (mode == 2) {
480
+ if (ndigits <= 0) ndigits = 1;
481
+ ilim = ilim1 = ndigits;
482
+ }
483
+ else {
484
+ ilim = ndigits + k + 1;
485
+ ilim1 = ilim - 1;
486
+ }
487
+ if (ilim <= 0 || ilim > 14) return v;
488
+ int ieps = 2;
489
+ if (k > 0) {
490
+ double ds = tens[k & 0xf];
491
+ int j = k >> 4;
492
+ if (j & 0x10) { j &= 0xf; d /= bigtens[4]; ieps++; }
493
+ for (int n = 0; j; j >>= 1, n++)
494
+ if (j & 1) { ieps++; ds *= bigtens[n]; }
495
+ d /= ds;
496
+ }
497
+ else if (k < 0) {
498
+ int j1 = -k;
499
+ d *= tens[j1 & 0xf];
500
+ for (int j = j1 >> 4, n = 0; j; j >>= 1, n++)
501
+ if (j & 1) { ieps++; d *= bigtens[n]; }
502
+ }
503
+ if (k_check && d < 1.) {
504
+ if (ilim1 <= 0) return v;
505
+ ilim = ilim1; k--; d *= 10.; ieps++;
506
+ }
507
+ e.f = ieps * d + 7.;
508
+ e.u -= (uint64_t)52 << 52;
509
+ double eps = e.f * tens[ilim - 1];
510
+ char s[24];
511
+ int n = 0;
512
+ for (int t = 1;; t++, d *= 10.) {
513
+ long L = (long)d;
514
+ if (!(d -= L)) ilim = t;
515
+ s[n++] = (char)('0' + L);
516
+ if (t == ilim) {
517
+ if (d > 0.5 + eps) goto bump_up;
518
+ if (d < 0.5 - eps) break;
519
+ /* within the error bound of a half: a tie, to even */
520
+ if ((s[n - 1] - '0') & 1) goto bump_up;
521
+ /* dtoa redoes these digits exactly and keeps them whatever the exact
522
+ remainder; above an exact half it does not trim their zeros */
523
+ /* an integer below 1e15 is redone by dtoa's small-integer loop, which
524
+ stops at the last kept digit without trimming */
525
+ if (keep_zeros && x.f == floor(x.f) && k <= 14) *keep_zeros = 1;
526
+ else if (keep_zeros && n < 20) {
527
+ char ex[900];
528
+ snprintf(ex, sizeof ex, "%.800e", v < 0 ? -v : v);
529
+ int dpos = n; /* digit after the kept ones, in ex's mantissa */
530
+ const char *q = ex; int seen = 0;
531
+ while (*q && *q != 'e') {
532
+ if (*q >= '0' && *q <= '9') {
533
+ if (seen == dpos) break;
534
+ seen++;
535
+ }
536
+ q++;
537
+ }
538
+ if (*q >= '0' && *q <= '9') {
539
+ int above = *q > '5';
540
+ if (*q == '5') for (const char *t = q + 1; *t && *t != 'e'; t++) if (*t != '0') { above = 1; break; }
541
+ *keep_zeros = above;
542
+ }
543
+ }
544
+ break;
545
+ }
546
+ }
547
+ goto rebuild;
548
+ bump_up:
549
+ while (n > 0 && s[n - 1] == '9') n--;
550
+ if (n == 0) { s[n++] = '1'; k++; }
551
+ else s[n - 1]++;
552
+ rebuild:
553
+ s[n] = 0;
554
+ char buf[64];
555
+ snprintf(buf, sizeof buf, "0.%se%d", s, k + 1);
556
+ char *end = NULL;
557
+ double r = 0;
558
+ if (!sp_read_float(buf, &end, &r) || !isfinite(r)) return v;
559
+ return v < 0 ? -r : r;
560
+ }
561
+ /* sprintf's float directives: the C-locale delegation above, with Ruby's
562
+ rounding. The conversion decides how many significant digits survive --
563
+ %f counts them from the decimal point, %e from the first digit, %g is a
564
+ significant-digit count of its own. A `*` precision takes an argument this
565
+ call does not receive, so such a format is left to libc. */
566
+ int sp_snprintf_ruby_float(char *buf, size_t size, const char *fmt, double v) {
567
+ size_t n = fmt ? strlen(fmt) : 0;
568
+ char conv = n ? fmt[n - 1] : 0;
569
+ if ((conv == 'f' || conv == 'e' || conv == 'E' || conv == 'g' || conv == 'G') &&
570
+ !strchr(fmt, '*') && isfinite(v) && v != 0.0) {
571
+ const char *dot = strchr(fmt, '.');
572
+ int prec = 6; /* C's default for all five */
573
+ if (dot) {
574
+ prec = 0;
575
+ for (const char *q = dot + 1; *q >= '0' && *q <= '9'; q++) prec = prec * 10 + (*q - '0');
576
+ }
577
+ int keep_zeros = 0;
578
+ if (conv == 'f') v = sp_float_round_ruby(v, 3, prec, NULL);
579
+ else if (conv == 'e' || conv == 'E') v = sp_float_round_ruby(v, 2, prec + 1, NULL);
580
+ else {
581
+ v = sp_float_round_ruby(v, 2, prec, &keep_zeros);
582
+ /* %g with dtoa's digits untrimmed: the alternate form keeps the zeros;
583
+ a point it adds with no digit after it is not Ruby's */
584
+ if (keep_zeros && !strchr(fmt, '#') && n + 2 < 48) {
585
+ /* the flags and the width are laid out here, around the digits */
586
+ const char *f = fmt + 1;
587
+ int minus = 0, zero = 0, plus = 0, space = 0, width = 0;
588
+ for (;; f++) {
589
+ if (*f == '-') minus = 1;
590
+ else if (*f == '0') zero = 1;
591
+ else if (*f == '+') plus = 1;
592
+ else if (*f == ' ') space = 1;
593
+ else break;
594
+ }
595
+ while (*f >= '0' && *f <= '9' && width < 10000) width = width * 10 + (*f++ - '0');
596
+ if (width > 1000) return sp_snprintf_c_float(buf, size, fmt, v);
597
+ char af[64], body[512];
598
+ snprintf(af, sizeof af, "%%%s%s#%s", plus ? "+" : "", space ? " " : "", f);
599
+ int w = sp_snprintf_c_float(body, sizeof body, af, v);
600
+ if (w < 0 || (size_t)w >= sizeof body) return sp_snprintf_c_float(buf, size, fmt, v);
601
+ char *pt = strchr(body, '.');
602
+ if (pt && (pt[1] < '0' || pt[1] > '9')) { memmove(pt, pt + 1, strlen(pt + 1) + 1); w--; }
603
+ int pad = width > w ? width - w : 0;
604
+ char out[1100]; int o = 0;
605
+ const char *b0 = body;
606
+ if (!minus && zero && pad) {
607
+ if (*b0 == '-' || *b0 == '+' || *b0 == ' ') out[o++] = *b0++;
608
+ while (pad-- > 0) out[o++] = '0';
609
+ }
610
+ else if (!minus) while (pad-- > 0) out[o++] = ' ';
611
+ while (*b0) out[o++] = *b0++;
612
+ if (minus) while (pad-- > 0) out[o++] = ' ';
613
+ out[o] = 0;
614
+ if (size) { size_t c = (size_t)o < size - 1 ? (size_t)o : size - 1; memcpy(buf, out, c); buf[c] = 0; }
615
+ return o;
616
+ }
617
+ }
618
+ }
619
+ return sp_snprintf_c_float(buf, size, fmt, v);
620
+ }
621
+
622
+ /* Cold integer-math and String#oct helpers, moved out of spinel_rt.h
623
+ * so they're compiled once into libspinel_rt.a rather than re-parsed
624
+ * in every generated translation unit. Leaf functions: arithmetic +
625
+ * libc + sp_raise_cls only. */
626
+ sp_int sp_gcd(sp_int a,sp_int b){if(a<0)a=-a;if(b<0)b=-b;while(b){sp_int t=b;b=a%b;a=t;}return a;}
627
+ sp_int sp_lcm(sp_int a,sp_int b){if(a==0||b==0)return 0;sp_int g=sp_gcd(a,b);if(a<0)a=-a;if(b<0)b=-b;return (a/g)*b;}
628
+ sp_int sp_powmod(sp_int base,sp_int exp,sp_int mod){if(exp<0)sp_raise_cls("RangeError","Integer#pow() 1st argument cannot be negative when 2nd argument specified");if(mod==0)sp_raise_cls("ZeroDivisionError","divided by 0");sp_int r=1;sp_int m=mod<0?-mod:mod;if(m==1){r=0;}
629
+ else{base=base%m;if(base<0)base+=m;while(exp>0){if(exp%2==1)r=r*base%m;exp=exp/2;base=base*base%m;}}if(mod<0&&r>0)r-=m;return r;}
630
+ sp_int sp_ceildiv(sp_int a,sp_int b){if(b==0)sp_raise_cls("ZeroDivisionError","divided by 0");if(b==-1)return -a;sp_int q=a/b;if(a%b!=0&&((a^b)>=0))q++;return q;}
631
+ sp_int sp_int_clamp(sp_int v,sp_int lo,sp_int hi){return v<lo?lo:v>hi?hi:v;}
632
+ sp_float sp_float_clamp(sp_float v,sp_float lo,sp_float hi){return v<lo?lo:v>hi?hi:v;}
633
+ /* Integer square root via Newton's method -- exact for the full
634
+ sp_int range. CRuby raises Math::DomainError on negative input
635
+ (flattened runtime name "Math::DomainError"). The seed is n/2, not
636
+ (n+1)/2: at n == INTPTR_MAX the latter overflows (signed UB), and
637
+ n/2 is a valid Newton seed for all n >= 2. */
638
+ sp_int sp_int_sqrt(sp_int n){if(n<0)sp_raise_cls("Math::DomainError","Numerical argument is out of domain - \"isqrt\"");if(n<2)return n;sp_int x=n,y=n/2;while(y<x){x=y;y=(x+n/x)/2;}return x;}
639
+ /* Integer#round/ceil/floor/truncate at 10^(-ndigits). Pure integer
640
+ arithmetic (no double precision loss above 2^53). 10^p fits sp_int
641
+ only for p<=18; p>=19 collapses to 0. Round-up multiply is overflow-
642
+ guarded and falls back to the truncated value. */
643
+ sp_int sp_ipow10(sp_int p){sp_int f=1;sp_int i=0;while(i<p){f*=10;i++;}return f;}
644
+ void sp_int_round_check_ndigits(sp_int nd) {
645
+ if (nd > INT_MAX || nd < INT_MIN)
646
+ sp_raise_cls("RangeError", sp_sprintf("integer %lld too %s to convert to 'int'",
647
+ (long long)nd, nd < 0 ? "small" : "big"));
648
+ }
649
+ sp_int sp_int_round(sp_int v,sp_int nd){sp_int_round_check_ndigits(nd);if(nd>=0)return v;sp_int p=-nd;if(p>=SP_INT_POW10_LIMIT)return 0;sp_int f=sp_ipow10(p);sp_int q=v/f,r=v%f,half=f/2;if(v>=0){if(r>=half&&q<INTPTR_MAX/f)return(q+1)*f;return q*f;}if(-r>=half&&q>INTPTR_MIN/f)return(q-1)*f;return q*f;}
650
+ sp_int sp_int_ceil(sp_int v,sp_int nd){if(nd>=0)return v;sp_int p=-nd;if(p>=SP_INT_POW10_LIMIT)return 0;sp_int f=sp_ipow10(p);sp_int q=v/f,r=v%f;if(r!=0&&v>0&&q<INTPTR_MAX/f)return(q+1)*f;return q*f;}
651
+ sp_int sp_int_floor(sp_int v,sp_int nd){if(nd>=0)return v;sp_int p=-nd;if(p>=SP_INT_POW10_LIMIT)return 0;sp_int f=sp_ipow10(p);sp_int q=v/f,r=v%f;if(r!=0&&v<0&&q>INTPTR_MIN/f)return(q-1)*f;return q*f;}
652
+ sp_int sp_int_truncate(sp_int v,sp_int nd){if(nd>=0)return v;sp_int p=-nd;if(p>=SP_INT_POW10_LIMIT)return 0;sp_int f=sp_ipow10(p);return(v/f)*f;}
653
+ /* String#oct: prefix auto-detection (0x=hex, 0b=bin, 0o/0=oct, else
654
+ base-8). Matches CRuby. */
655
+ /* String#oct: lenient Integer(str, 8)-style parse. Skips leading whitespace,
656
+ accepts an optional sign, an optional base prefix (0x/0b/0o/0d, or a leading
657
+ 0 = octal), and single `_` separators between digits. Stops at the first
658
+ character not valid in the selected base (a leading/doubled underscore ends
659
+ parsing too); an unparseable string is 0. A value past sp_int raises
660
+ RangeError, like the sibling Integer() parsers. */
661
+ sp_int sp_str_oct(const char*s){SP_GC_ROOT_STR(s);
662
+ if(!s)return 0;
663
+ const char*p=s;
664
+ while(isspace((unsigned char)*p))p++;
665
+ int sign=1;
666
+ if(*p=='+')p++;
667
+ else if(*p=='-'){sign=-1;p++;}
668
+ int base=8;
669
+ if(p[0]=='0'){
670
+ char c=p[1];
671
+ if(c=='x'||c=='X'){base=16;p+=2;}
672
+ else if(c=='b'||c=='B'){base=2;p+=2;}
673
+ else if(c=='o'||c=='O'){base=8;p+=2;}
674
+ else if(c=='d'||c=='D'){base=10;p+=2;}
675
+ /* a bare leading 0 is octal; keep p on the 0 (a valid octal digit) */
676
+ }
677
+ sp_int val=0; int any=0, prev_us=0;
678
+ for(;;){
679
+ char c=*p; int d;
680
+ if(c>='0'&&c<='9')d=c-'0';
681
+ else if(c>='a'&&c<='z')d=c-'a'+10;
682
+ else if(c>='A'&&c<='Z')d=c-'A'+10;
683
+ else if(c=='_'){ if(!any||prev_us)break; prev_us=1; p++; continue; }
684
+ else break;
685
+ if(d>=base)break;
686
+ sp_int t;
687
+ if(sp_ckd_mul_iptr(val,(sp_int)base,&t)||
688
+ sp_ckd_add_iptr(t,(sp_int)d,&val))
689
+ sp_raise_cls("RangeError",sp_sprintf("integer overflow parsing \"%s\"",s));
690
+ any=1; prev_us=0; p++;
691
+ }
692
+ return sign*val;
693
+ }
694
+
695
+ /* Float#round(n) and its siblings at a positive digit count. Scaling by 10**n
696
+ and rounding the product reads the product's OWN representation error as
697
+ part of the value: 64.781995 * 1e5 is 6478199.4999999991, so the digit that
698
+ should round up rounds down and the answer comes out a decimal short. CRuby
699
+ compensates by asking whether the next step up is still <= the input, which
700
+ is what makes 64.781995.round(5) answer 64.782 rather than 64.78199. The
701
+ floor/ceil forms compensate the same way, from the other side.
702
+ nd <= 0 answers an Integer and is the caller's own path; this is the Float
703
+ half. (#3983) */
704
+ /* Float#round / #floor / #ceil / #truncate at a POSITIVE digit count, laid
705
+ out as CRuby lays it out (numeric.c: flo_round, rb_float_floor,
706
+ rb_float_ceil). Scaling by a power of ten and rounding the product reads the
707
+ product's OWN representation error as part of the value -- 64.781995 * 1e5
708
+ is 6478199.4999999991, so the digit that should round up rounds down and the
709
+ answer comes out a decimal short (#3983). Each form compensates differently,
710
+ and the asymmetry is deliberate:
711
+ round compensates on both sides, which is what makes 2.675.round(2)
712
+ answer 2.68 rather than the exact value's 2.67;
713
+ floor compensates upward only;
714
+ ceil takes the scaled product as it stands.
715
+ The two guards decide whether the digit being asked for is inside the
716
+ double's reach at all: past it the value is unchanged, short of it the value
717
+ rounds away to zero. A digit count a power of ten cannot represent exactly
718
+ (>= DBL_DIG) goes through the decimal conversion, which is exact, in place
719
+ of CRuby's rational arithmetic. */
720
+ /* The round family past DBL_DIG digits, decided on the value's own decimal
721
+ expansion rather than on a scaled product. The cut digit and what follows it
722
+ settle the tie the same way CRuby's rational arithmetic does, which the
723
+ library conversion alone cannot: it breaks exact ties to even, and Ruby's
724
+ default breaks them away from zero. */
725
+ static double sp_prec_decimal_round(double x, intptr_t nd, int op) {
726
+ char buf[512];
727
+ int extra = 25;
728
+ snprintf(buf, sizeof buf, "%.*f", (int)nd + extra, x);
729
+ char *dot = strchr(buf, '.');
730
+ if (!dot) return x;
731
+ char *cut = dot + 1 + (int)nd; /* the first digit past the cut */
732
+ char cutd = *cut;
733
+ int tail_nonzero = 0;
734
+ for (char *q = cut + 1; *q; q++) if (*q != '0') { tail_nonzero = 1; break; }
735
+ *cut = '\0'; /* the truncation toward zero */
736
+ double r = strtod(buf, NULL);
737
+ int away;
738
+ if (cutd > '5') away = 1;
739
+ else if (cutd < '5') away = 0;
740
+ else if (tail_nonzero) away = 1;
741
+ else if (op == SP_PREC_HALF_DOWN) away = 0;
742
+ else if (op == SP_PREC_HALF_EVEN) {
743
+ char last = cut[-1];
744
+ away = (last >= '0' && last <= '9') ? ((last - '0') % 2 != 0) : 0;
745
+ }
746
+ else away = 1; /* the default: ties leave zero behind */
747
+ if (!away) return r;
748
+ double step = 1.0 / pow(10, (double)nd);
749
+ snprintf(buf, sizeof buf, "%.*f", (int)nd, x < 0.0 ? r - step : r + step);
750
+ return strtod(buf, NULL);
751
+ }
752
+ static int sp_prec_overflow(intptr_t nd, int binexp) {
753
+ return nd >= (DBL_DIG + 2) - (binexp > 0 ? binexp / 4 : binexp / 3 - 1);
754
+ }
755
+ static int sp_prec_underflow(intptr_t nd, int binexp) {
756
+ return nd < -(binexp > 0 ? binexp / 3 + 1 : binexp / 4);
757
+ }
758
+ double sp_float_prec_op(double x, intptr_t nd, int op) {
759
+ if (x == 0.0 || !isfinite(x) || nd <= 0) return x;
760
+ if (op == SP_PREC_TRUNC) return sp_float_prec_op(x, nd, signbit(x) ? SP_PREC_CEIL : SP_PREC_FLOOR);
761
+ int binexp = 0;
762
+ frexp(x, &binexp);
763
+ if (sp_prec_overflow(nd, binexp)) return x;
764
+ if (sp_prec_underflow(nd, binexp)) {
765
+ /* round leaves zero whatever the sign; floor keeps a negative value's own
766
+ first decimal step, and ceil keeps a positive one's */
767
+ if (op == SP_PREC_FLOOR && x < 0.0) { /* fall through to the arithmetic */ }
768
+ else if (op == SP_PREC_CEIL && x > 0.0) { /* likewise */ }
769
+ else return 0.0;
770
+ }
771
+ /* Past DBL_DIG digits a power of ten is no longer exact, so the scaled form
772
+ reads its own error as part of the value; CRuby rounds those by rational.
773
+ The decimal conversion is the same exact answer. floor and ceil keep the
774
+ scaled form -- that is what CRuby answers for them. */
775
+ if (nd >= DBL_DIG && op != SP_PREC_FLOOR && op != SP_PREC_CEIL) return sp_prec_decimal_round(x, nd, op);
776
+ double s = pow(10, (double)nd);
777
+ double xs = x * s;
778
+ if (!isfinite(s) || !isfinite(xs)) return x;
779
+ double f;
780
+ switch (op) {
781
+ case SP_PREC_FLOOR: {
782
+ f = floor(xs);
783
+ double res = (f + 1) / s;
784
+ return res > x ? f / s : res;
785
+ }
786
+ case SP_PREC_CEIL:
787
+ return ceil(xs) / s;
788
+ case SP_PREC_HALF_EVEN: {
789
+ /* the tie is decided on the fractional part alone, so the integral part
790
+ is split off first and only rejoined at the end */
791
+ double u, v, us, vs, ff, d, uf;
792
+ v = modf(x, &u);
793
+ us = u * s; vs = v * s;
794
+ if (x > 0.0) {
795
+ ff = floor(vs); uf = us + ff; d = vs - ff;
796
+ if (d > 0.5) d = 1.0;
797
+ else if (d == 0.5 || (uf + 0.5) / s <= x) d = fmod(uf, 2.0);
798
+ else d = 0.0;
799
+ return (us + ff + d) / s;
800
+ }
801
+ ff = ceil(vs); uf = us + ff; d = ff - vs;
802
+ if (d > 0.5) d = 1.0;
803
+ else if (d == 0.5 || (uf - 0.5) / s >= x) d = fmod(-uf, 2.0);
804
+ else d = 0.0;
805
+ return (us + ff - d) / s;
806
+ }
807
+ case SP_PREC_HALF_DOWN:
808
+ f = round(xs);
809
+ if (x > 0) { if ((f - 0.5) / s >= x) f -= 1; }
810
+ else { if ((f + 0.5) / s <= x) f += 1; }
811
+ return f / s;
812
+ default:
813
+ f = round(xs);
814
+ if (x > 0) { if ((f + 0.5) / s <= x) f += 1; }
815
+ else { if ((f - 0.5) / s >= x) f -= 1; }
816
+ return f / s;
817
+ }
818
+ }