smarter_json 0.9.2 → 0.9.9
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/.gitignore +1 -0
- data/CHANGELOG.md +77 -54
- data/README.md +215 -72
- data/docs/_introduction.md +6 -12
- data/docs/basic_read_api.md +29 -19
- data/docs/basic_write_api.md +2 -2
- data/docs/examples.md +32 -23
- data/docs/options.md +14 -14
- data/ext/smarter_json/smarter_json.c +223 -89
- data/ext/smarter_json/vendor/LICENSE-fast_float-MIT +27 -0
- data/ext/smarter_json/vendor/eisel_lemire.h +117 -0
- data/ext/smarter_json/vendor/eisel_lemire.md +29 -0
- data/ext/smarter_json/vendor/eisel_lemire_powers.h +663 -0
- data/lib/smarter_json/backports.rb +28 -0
- data/lib/smarter_json/options.rb +52 -0
- data/lib/smarter_json/parser.rb +400 -139
- data/lib/smarter_json/version.rb +1 -1
- data/lib/smarter_json.rb +3 -1
- metadata +9 -5
- data/ext/smarter_json/vendor/ryu.h +0 -819
- data/ext/smarter_json/vendor/ryu.md +0 -22
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
#ifdef __ARM_NEON
|
|
5
5
|
#include <arm_neon.h>
|
|
6
6
|
#endif
|
|
7
|
-
#include "vendor/
|
|
7
|
+
#include "vendor/eisel_lemire.h" /* Eisel-Lemire decimal->double, correctly rounded (fast_float) */
|
|
8
8
|
|
|
9
9
|
/* Branch hints / prefetch on the hot scan loops. No-ops on compilers without the
|
|
10
10
|
* builtins (the code is correct either way; these only steer code layout). */
|
|
@@ -12,10 +12,12 @@
|
|
|
12
12
|
# define FJ_LIKELY(x) __builtin_expect(!!(x), 1)
|
|
13
13
|
# define FJ_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
|
14
14
|
# define FJ_PREFETCH(p) __builtin_prefetch(p)
|
|
15
|
+
# define FJ_ALWAYS_INLINE inline __attribute__((always_inline))
|
|
15
16
|
#else
|
|
16
17
|
# define FJ_LIKELY(x) (x)
|
|
17
18
|
# define FJ_UNLIKELY(x) (x)
|
|
18
19
|
# define FJ_PREFETCH(p) ((void)0)
|
|
20
|
+
# define FJ_ALWAYS_INLINE inline
|
|
19
21
|
#endif
|
|
20
22
|
|
|
21
23
|
/*
|
|
@@ -48,8 +50,7 @@ static ID fj_name_id;
|
|
|
48
50
|
static VALUE fj_sym_encoding;
|
|
49
51
|
static VALUE fj_sym_symbolize_keys;
|
|
50
52
|
static VALUE fj_sym_first_wins;
|
|
51
|
-
static VALUE
|
|
52
|
-
static VALUE fj_sym_bigdecimal_load;
|
|
53
|
+
static VALUE fj_sym_decimal_precision;
|
|
53
54
|
static VALUE fj_sym_float;
|
|
54
55
|
static VALUE fj_sym_bigdecimal;
|
|
55
56
|
static VALUE fj_sym_on_warning;
|
|
@@ -70,8 +71,7 @@ typedef struct {
|
|
|
70
71
|
int depth;
|
|
71
72
|
int symbolize_keys;
|
|
72
73
|
int dup_first_wins;
|
|
73
|
-
int
|
|
74
|
-
int bigdecimal_load; /* 0 = float, 1 = auto, 2 = bigdecimal */
|
|
74
|
+
int decimal_precision; /* 0 = float, 1 = auto, 2 = bigdecimal */
|
|
75
75
|
fj_kc_slot *kcache; /* per-parse key cache (NULL when interning unavailable) */
|
|
76
76
|
VALUE on_warning; /* on_warning: callable invoked per non-fatal lenient fix, else Qnil */
|
|
77
77
|
} fj_state;
|
|
@@ -168,20 +168,39 @@ static long fj_mbws(const char *p, long n) {
|
|
|
168
168
|
return 0;
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
/* Skip a run of whitespace. This is hot on pretty-printed input, where most of
|
|
172
|
+
* the bytes are indentation. Indentation is homogeneous — all spaces OR all tabs,
|
|
173
|
+
* the two common styles — so a run of it is skipped 8 bytes at a time with a
|
|
174
|
+
* single 64-bit compare (the uniform-byte patterns read the same regardless of
|
|
175
|
+
* endianness). Everything else — newlines, CR, short/partial runs, and Unicode
|
|
176
|
+
* whitespace — falls to the tight byte loop, which also avoids the per-byte helper
|
|
177
|
+
* calls (fj_byte / fj_is_ws / fj_advance) the previous byte-at-a-time version paid.
|
|
178
|
+
* The set of bytes treated as whitespace is unchanged. */
|
|
171
179
|
static void fj_skip_pure_ws(fj_state *st) {
|
|
180
|
+
const char *p = st->buf + st->pos;
|
|
181
|
+
const char *end = st->buf + st->len;
|
|
172
182
|
for (;;) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
} else if (b >= 0x80) {
|
|
178
|
-
long m = fj_mbws(st->buf + st->pos, st->len - st->pos);
|
|
179
|
-
if (m == 0) break;
|
|
180
|
-
st->pos += m;
|
|
181
|
-
} else {
|
|
183
|
+
while (end - p >= 8) {
|
|
184
|
+
uint64_t w;
|
|
185
|
+
memcpy(&w, p, 8);
|
|
186
|
+
if (w == 0x2020202020202020ULL || w == 0x0909090909090909ULL) { p += 8; continue; }
|
|
182
187
|
break;
|
|
183
188
|
}
|
|
189
|
+
if (p >= end) break;
|
|
190
|
+
{
|
|
191
|
+
unsigned char b = (unsigned char)*p;
|
|
192
|
+
if (b == 0x20 || (b >= 0x09 && b <= 0x0D)) {
|
|
193
|
+
p++;
|
|
194
|
+
} else if (b >= 0x80) {
|
|
195
|
+
long m = fj_mbws(p, end - p);
|
|
196
|
+
if (m == 0) break;
|
|
197
|
+
p += m;
|
|
198
|
+
} else {
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
184
202
|
}
|
|
203
|
+
st->pos = p - st->buf;
|
|
185
204
|
}
|
|
186
205
|
|
|
187
206
|
/* A comment marker only starts a comment when preceded by whitespace or at the
|
|
@@ -228,6 +247,18 @@ static void fj_skip_ws_comments(fj_state *st) {
|
|
|
228
247
|
}
|
|
229
248
|
}
|
|
230
249
|
|
|
250
|
+
/* Cheap guard for the hot loop: could the current byte begin whitespace or a
|
|
251
|
+
* comment marker, so the (otherwise no-op) fj_skip_ws_comments call is actually
|
|
252
|
+
* needed? Compact data — the next byte is already a structural char or a value
|
|
253
|
+
* start — answers no, and we elide both the call and its memcpy/lookahead. ASCII
|
|
254
|
+
* whitespace, '#', '/', and possible multibyte-ws lead bytes (>=0x80) answer yes;
|
|
255
|
+
* EOF (-1) answers no (the caller's existing -1 checks handle it). Behaviour is
|
|
256
|
+
* identical to always calling fj_skip_ws_comments — this only skips a known no-op. */
|
|
257
|
+
static inline int fj_needs_ws_skip(int b) {
|
|
258
|
+
if (b < 0) return 0;
|
|
259
|
+
return b == 0x20 || (b >= 0x09 && b <= 0x0D) || b == '#' || b == '/' || b >= 0x80;
|
|
260
|
+
}
|
|
261
|
+
|
|
231
262
|
/* forward declarations (mutual recursion) */
|
|
232
263
|
static VALUE fj_parse_value(fj_state *st);
|
|
233
264
|
static VALUE fj_parse_member_value(fj_state *st);
|
|
@@ -471,7 +502,7 @@ static VALUE fj_to_bigdecimal_token(const char *p, long n) {
|
|
|
471
502
|
* (quoteless path) call these, so the Integer/Float a token produces is identical
|
|
472
503
|
* no matter which path scanned it. [p, n) is the raw token slice (with any sign),
|
|
473
504
|
* needed only by the bignum / strtod fallbacks. */
|
|
474
|
-
static VALUE fj_int_from_parts(uint64_t m, int digits, int neg, int overflow, const char *p, long n) {
|
|
505
|
+
static FJ_ALWAYS_INLINE VALUE fj_int_from_parts(uint64_t m, int digits, int neg, int overflow, const char *p, long n) {
|
|
475
506
|
if (!overflow && digits >= 1 && digits <= 18) {
|
|
476
507
|
int64_t v = (int64_t)m;
|
|
477
508
|
return LL2NUM(neg ? -v : v);
|
|
@@ -481,16 +512,87 @@ static VALUE fj_int_from_parts(uint64_t m, int digits, int neg, int overflow, co
|
|
|
481
512
|
return rb_str_to_inum(fj_strip_underscores(p, n), 10, 0);
|
|
482
513
|
}
|
|
483
514
|
|
|
515
|
+
/* Convert a >17-digit / subnormal float token to a double. A double resolves ~17
|
|
516
|
+
* significant decimals; the digits past that affect only the final round-to-nearest-
|
|
517
|
+
* even, which a single sticky marker ("was any dropped digit nonzero?") captures. So
|
|
518
|
+
* we keep FJ_FLOAT_ODD_DIGITS significant digits and, if more nonzero digits follow,
|
|
519
|
+
* force the last kept digit odd (round-to-odd). strtod's round-to-nearest of that
|
|
520
|
+
* shorter mantissa then equals round-to-nearest of the full value — but strtod grinds
|
|
521
|
+
* far fewer digits. The kept count is well above 2x double's ~16 significant decimals,
|
|
522
|
+
* which is what round-to-odd needs to be exact (verified bit-for-bit against
|
|
523
|
+
* JSON.parse on the high-precision corpus). The token is rebuilt into a NUL-terminated
|
|
524
|
+
* "<digits>e<exp>" buffer (passing the raw input slice would make rb_cstr_to_dbl treat
|
|
525
|
+
* the trailing delimiter as garbage and re-run strtod a second time). */
|
|
526
|
+
#define FJ_FLOAT_ODD_DIGITS 40
|
|
527
|
+
static VALUE fj_float_strtod(const char *p, long n) {
|
|
528
|
+
char digits[FJ_FLOAT_ODD_DIGITS];
|
|
529
|
+
char out[FJ_FLOAT_ODD_DIGITS + 40];
|
|
530
|
+
long i = 0, ow = 0, kept = 0, point_pos = 0, lead_frac_zeros = 0;
|
|
531
|
+
int neg = 0, after_point = 0, seen_sig = 0, sticky = 0, esign = 0;
|
|
532
|
+
int64_t expl_exp = 0, x;
|
|
533
|
+
|
|
534
|
+
if (i < n && (p[i] == '+' || p[i] == '-')) { neg = (p[i] == '-'); i++; }
|
|
535
|
+
|
|
536
|
+
for (; i < n; i++) {
|
|
537
|
+
char c = p[i];
|
|
538
|
+
if (c == '_') continue;
|
|
539
|
+
if (c == '.') { after_point = 1; continue; }
|
|
540
|
+
if (c == 'e' || c == 'E') { i++; break; }
|
|
541
|
+
if (!seen_sig && c == '0') { if (after_point) lead_frac_zeros++; continue; }
|
|
542
|
+
seen_sig = 1;
|
|
543
|
+
if (!after_point) point_pos++;
|
|
544
|
+
if (kept < FJ_FLOAT_ODD_DIGITS) digits[kept++] = c;
|
|
545
|
+
else if (c != '0') sticky = 1;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (i < n && (p[i] == '+' || p[i] == '-')) { esign = (p[i] == '-'); i++; }
|
|
549
|
+
for (; i < n; i++) {
|
|
550
|
+
char c = p[i];
|
|
551
|
+
if (c == '_') continue;
|
|
552
|
+
if (c < '0' || c > '9') break;
|
|
553
|
+
expl_exp = expl_exp * 10 + (c - '0');
|
|
554
|
+
}
|
|
555
|
+
if (esign) expl_exp = -expl_exp;
|
|
556
|
+
|
|
557
|
+
if (kept == 0) return rb_float_new(neg ? -0.0 : 0.0);
|
|
558
|
+
|
|
559
|
+
/* round-to-odd: a dropped nonzero tail forces the last kept digit odd. */
|
|
560
|
+
if (sticky && ((digits[kept - 1] - '0') % 2) == 0) digits[kept - 1]++;
|
|
561
|
+
|
|
562
|
+
x = expl_exp + point_pos - lead_frac_zeros - kept;
|
|
563
|
+
if (neg) out[ow++] = '-';
|
|
564
|
+
memcpy(out + ow, digits, (size_t)kept);
|
|
565
|
+
ow += kept;
|
|
566
|
+
/* Append "e<exp>" by hand. snprintf here showed up as BSD_vfprintf in profiling —
|
|
567
|
+
a full printf formatter per number is absurdly heavy for one integer. */
|
|
568
|
+
out[ow++] = 'e';
|
|
569
|
+
if (x < 0) { out[ow++] = '-'; x = -x; }
|
|
570
|
+
{
|
|
571
|
+
char ex[24];
|
|
572
|
+
int en = 0;
|
|
573
|
+
if (x == 0) ex[en++] = '0';
|
|
574
|
+
else while (x > 0) { ex[en++] = (char)('0' + (int)(x % 10)); x /= 10; }
|
|
575
|
+
while (en > 0) out[ow++] = ex[--en];
|
|
576
|
+
}
|
|
577
|
+
out[ow] = '\0';
|
|
578
|
+
return rb_float_new(rb_cstr_to_dbl(out, 0));
|
|
579
|
+
}
|
|
580
|
+
|
|
484
581
|
/* e10 is the final base-10 exponent (already adjusted by the fraction length). */
|
|
485
|
-
static VALUE fj_float_from_parts(uint64_t m10, int m10digits, int64_t e10, int neg, int overflow, const char *p, long n) {
|
|
486
|
-
/*
|
|
487
|
-
|
|
582
|
+
static FJ_ALWAYS_INLINE VALUE fj_float_from_parts(uint64_t m10, int m10digits, int64_t e10, int neg, int overflow, const char *p, long n) {
|
|
583
|
+
/* Fast path by mantissa width (our scanner accumulates m10 exactly up to 18
|
|
584
|
+
digits, flagging overflow beyond):
|
|
585
|
+
1..18 digits -> Eisel-Lemire, correctly-rounded for any exact uint64 mantissa
|
|
586
|
+
(Mushtak-Lemire). This pulls full-double-precision data (e.g.
|
|
587
|
+
citylots coordinates, 18 sig digits) off the slow strtod
|
|
588
|
+
fallback — the stdlib json gem still strtods it.
|
|
589
|
+
>18 digits / overflow / extreme exponent -> strtod (round-to-odd). */
|
|
590
|
+
if (!overflow && m10digits >= 1 && m10digits <= 18 && (long)m10digits + e10 >= -307) {
|
|
488
591
|
if (m10 == 0) return rb_float_new(neg ? -0.0 : 0.0);
|
|
489
|
-
return rb_float_new(
|
|
592
|
+
return rb_float_new(fj_eisel_lemire_s2d(e10, m10, neg));
|
|
490
593
|
}
|
|
491
|
-
/* Fallback for >
|
|
492
|
-
|
|
493
|
-
return rb_float_new(rb_str_to_dbl(fj_strip_underscores(p, n), 0));
|
|
594
|
+
/* Fallback for >18 digits / extreme or subnormal exponents. */
|
|
595
|
+
return fj_float_strtod(p, n);
|
|
494
596
|
}
|
|
495
597
|
|
|
496
598
|
/* Scan an already-bounded quoteless token [p, p+n) exactly once: validate it as a
|
|
@@ -571,8 +673,8 @@ static int fj_try_decimal(fj_state *st, const char *p, long n, VALUE *out) {
|
|
|
571
673
|
e10 -= frac;
|
|
572
674
|
/* :bigdecimal always; :auto only when significant digits > 16. m10digits is >=
|
|
573
675
|
* the significant-digit count, so m10digits <= 16 skips the fj_sig_digits scan. */
|
|
574
|
-
if (st->
|
|
575
|
-
(st->
|
|
676
|
+
if (st->decimal_precision == 2 ||
|
|
677
|
+
(st->decimal_precision == 1 && m10digits > 16 && fj_sig_digits(p, n) > 16)) {
|
|
576
678
|
*out = fj_to_bigdecimal_token(p, n);
|
|
577
679
|
} else {
|
|
578
680
|
*out = fj_float_from_parts(m10, m10digits, e10, neg, overflow, p, n);
|
|
@@ -596,7 +698,7 @@ static VALUE fj_parse_number(fj_state *st) {
|
|
|
596
698
|
long nlen;
|
|
597
699
|
int is_float = 0, neg = 0, overflow = 0;
|
|
598
700
|
uint64_t m10 = 0; /* mantissa: integer + fraction digits */
|
|
599
|
-
int m10digits = 0; /* mantissa digit chars (caps the
|
|
701
|
+
int m10digits = 0; /* mantissa digit chars (caps the Eisel-Lemire fast path at 18) */
|
|
600
702
|
int frac = 0; /* fraction digit chars: e10 -= frac */
|
|
601
703
|
int64_t e10 = 0;
|
|
602
704
|
|
|
@@ -683,8 +785,8 @@ static VALUE fj_parse_number(fj_state *st) {
|
|
|
683
785
|
* when significant digits > 16. Since m10digits >= significant digits, m10digits
|
|
684
786
|
* <= 16 guarantees not-BigDecimal and lets us skip the fj_sig_digits scan
|
|
685
787
|
* entirely (the common case — e.g. every coordinate in canada.json). */
|
|
686
|
-
if (st->
|
|
687
|
-
(st->
|
|
788
|
+
if (st->decimal_precision == 2 ||
|
|
789
|
+
(st->decimal_precision == 1 && m10digits > 16 && fj_sig_digits(np, nlen) > 16)) {
|
|
688
790
|
return fj_to_bigdecimal_token(np, nlen);
|
|
689
791
|
}
|
|
690
792
|
return fj_float_from_parts(m10, m10digits, e10, neg, overflow, np, nlen);
|
|
@@ -851,7 +953,8 @@ static VALUE fj_classify_quoteless(fj_state *st, const char *p0, long n0) {
|
|
|
851
953
|
* before the whitespace check. */
|
|
852
954
|
enum { FJ_QL_ORD = 0, FJ_QL_TERM, FJ_QL_WS, FJ_QL_CMT };
|
|
853
955
|
static const unsigned char fj_ql_class[256] = {
|
|
854
|
-
[','] = FJ_QL_TERM, ['
|
|
956
|
+
[','] = FJ_QL_TERM, ['{'] = FJ_QL_TERM, ['}'] = FJ_QL_TERM,
|
|
957
|
+
['['] = FJ_QL_TERM, [']'] = FJ_QL_TERM,
|
|
855
958
|
[0x0A] = FJ_QL_TERM, [0x0D] = FJ_QL_TERM,
|
|
856
959
|
[0x09] = FJ_QL_WS, [0x0B] = FJ_QL_WS, [0x0C] = FJ_QL_WS, [' '] = FJ_QL_WS,
|
|
857
960
|
['#'] = FJ_QL_CMT, ['/'] = FJ_QL_CMT,
|
|
@@ -1078,7 +1181,8 @@ static int fj_try_member_number(fj_state *st, VALUE *out) {
|
|
|
1078
1181
|
/* Commit only if the number abuts a value terminator; otherwise (whitespace,
|
|
1079
1182
|
* letters, a second '.', "0x…", …) leave it to the quoteless scanner. */
|
|
1080
1183
|
t = (unsigned char)*p;
|
|
1081
|
-
if (!(t == ',' || t == '
|
|
1184
|
+
if (!(t == ',' || t == '{' || t == '}' || t == '[' || t == ']' ||
|
|
1185
|
+
t == 0x0A || t == 0x0D || p == buf + st->len)) {
|
|
1082
1186
|
return 0;
|
|
1083
1187
|
}
|
|
1084
1188
|
|
|
@@ -1089,8 +1193,8 @@ static int fj_try_member_number(fj_state *st, VALUE *out) {
|
|
|
1089
1193
|
return 1;
|
|
1090
1194
|
}
|
|
1091
1195
|
e10 -= frac;
|
|
1092
|
-
if (st->
|
|
1093
|
-
(st->
|
|
1196
|
+
if (st->decimal_precision == 2 ||
|
|
1197
|
+
(st->decimal_precision == 1 && m10digits > 16 && fj_sig_digits(np, nlen) > 16)) {
|
|
1094
1198
|
*out = fj_to_bigdecimal_token(np, nlen);
|
|
1095
1199
|
} else {
|
|
1096
1200
|
*out = fj_float_from_parts(m10, m10digits, e10, neg, overflow, np, nlen);
|
|
@@ -1164,19 +1268,9 @@ static void fj_hash_bulk_insert(long count, const VALUE *pairs, VALUE hash) {
|
|
|
1164
1268
|
void rb_hash_bulk_insert(long, const VALUE *, VALUE);
|
|
1165
1269
|
#endif
|
|
1166
1270
|
|
|
1167
|
-
/* Hash entry count as a C long. RHASH_SIZE is not part of the public C API on
|
|
1168
|
-
* older Ruby (< ~2.7), but rb_hash_size (Hash#size's implementation) is available
|
|
1169
|
-
* everywhere. Only used on the rare :raise duplicate-key path, so the boxing cost
|
|
1170
|
-
* is irrelevant — and it keeps the extension buildable down to Ruby 2.5. */
|
|
1171
|
-
static inline long fj_hash_len(VALUE hash) {
|
|
1172
|
-
return NUM2LONG(rb_hash_size(hash));
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
1271
|
/* Build a Hash from `count` interleaved key,value slots. Fast path (String keys,
|
|
1176
|
-
* default :last_wins
|
|
1177
|
-
*
|
|
1178
|
-
* actually happened. symbolize_keys / :first_wins use a per-member loop into the
|
|
1179
|
-
* same pre-sized hash. */
|
|
1272
|
+
* default :last_wins): pre-size + bulk insert. symbolize_keys / :first_wins use a
|
|
1273
|
+
* per-member loop into the same pre-sized hash. */
|
|
1180
1274
|
static VALUE fj_build_object(fj_state *st, const VALUE *pairs, long count) {
|
|
1181
1275
|
long entries = count / 2, i;
|
|
1182
1276
|
VALUE hash = rb_hash_new_capa(entries);
|
|
@@ -1185,22 +1279,13 @@ static VALUE fj_build_object(fj_state *st, const VALUE *pairs, long count) {
|
|
|
1185
1279
|
* the per-member loop below to report each dropped duplicate key. */
|
|
1186
1280
|
if (!st->symbolize_keys && !st->dup_first_wins && st->on_warning == Qnil) {
|
|
1187
1281
|
rb_hash_bulk_insert(count, pairs, hash);
|
|
1188
|
-
if (st->dup_raise && fj_hash_len(hash) < entries) {
|
|
1189
|
-
VALUE seen = rb_hash_new_capa(entries);
|
|
1190
|
-
for (i = 0; i + 1 < count; i += 2) {
|
|
1191
|
-
long before = fj_hash_len(seen);
|
|
1192
|
-
rb_hash_aset(seen, pairs[i], Qtrue);
|
|
1193
|
-
if (fj_hash_len(seen) == before) fj_error(st, "duplicate key");
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
1282
|
return hash;
|
|
1197
1283
|
}
|
|
1198
1284
|
|
|
1199
1285
|
for (i = 0; i + 1 < count; i += 2) {
|
|
1200
1286
|
VALUE k = st->symbolize_keys ? rb_funcall(pairs[i], fj_to_sym_id, 0) : pairs[i];
|
|
1201
|
-
if (st->dup_first_wins || st->
|
|
1287
|
+
if (st->dup_first_wins || st->on_warning != Qnil) {
|
|
1202
1288
|
if (RTEST(rb_funcall(hash, fj_key_p_id, 1, k))) {
|
|
1203
|
-
if (st->dup_raise) fj_error(st, "duplicate key");
|
|
1204
1289
|
fj_warn(st, fj_sym_duplicate_key, "duplicate key");
|
|
1205
1290
|
if (st->dup_first_wins) continue;
|
|
1206
1291
|
}
|
|
@@ -1274,11 +1359,14 @@ static VALUE fj_parse_iter(fj_state *st, int implicit_root) {
|
|
|
1274
1359
|
int is_obj;
|
|
1275
1360
|
|
|
1276
1361
|
if (ps->fhead == 0) { /* top level: parse exactly one value */
|
|
1277
|
-
fj_skip_ws_comments(st);
|
|
1278
1362
|
b = fj_byte(st);
|
|
1363
|
+
if (FJ_UNLIKELY(fj_needs_ws_skip(b))) { fj_skip_ws_comments(st); b = fj_byte(st); }
|
|
1279
1364
|
if (b == '{') { fj_advance(st, 1); fj_fpush(ps, ps->vhead, 1); vss = 0; continue; }
|
|
1280
1365
|
if (b == '[') { fj_advance(st, 1); fj_fpush(ps, ps->vhead, 0); vss = 0; continue; }
|
|
1281
1366
|
if (b == -1) fj_error(st, "unexpected end of input");
|
|
1367
|
+
/* Top-level scalar: must be a recognized JSON value (number / literal / quoted
|
|
1368
|
+
* string). A bare word raises — no top-level quoteless strings (B-broad). The
|
|
1369
|
+
* scalar-vs-separator boundary is enforced in fj_parse_c. */
|
|
1282
1370
|
result = fj_parse_value(st);
|
|
1283
1371
|
break;
|
|
1284
1372
|
}
|
|
@@ -1288,8 +1376,8 @@ static VALUE fj_parse_iter(fj_state *st, int implicit_root) {
|
|
|
1288
1376
|
|
|
1289
1377
|
if (is_obj) {
|
|
1290
1378
|
VALUE key;
|
|
1291
|
-
fj_skip_ws_comments(st);
|
|
1292
1379
|
b = fj_byte(st);
|
|
1380
|
+
if (FJ_UNLIKELY(fj_needs_ws_skip(b))) { fj_skip_ws_comments(st); b = fj_byte(st); }
|
|
1293
1381
|
if (b == ',') { /* collapsing separator: skip empty member */
|
|
1294
1382
|
if (st->on_warning != Qnil && !vss) fj_warn(st, fj_sym_empty_slot, "extra comma, collapsed an empty slot");
|
|
1295
1383
|
vss = 0;
|
|
@@ -1316,11 +1404,12 @@ static VALUE fj_parse_iter(fj_state *st, int implicit_root) {
|
|
|
1316
1404
|
}
|
|
1317
1405
|
if (b == ']') fj_error(st, "unexpected ']' — expected a key or '}'");
|
|
1318
1406
|
key = fj_parse_object_key(st);
|
|
1319
|
-
|
|
1320
|
-
if (
|
|
1407
|
+
b = fj_byte(st);
|
|
1408
|
+
if (FJ_UNLIKELY(fj_needs_ws_skip(b))) { fj_skip_ws_comments(st); b = fj_byte(st); }
|
|
1409
|
+
if (b != ':') fj_error(st, "expected ':' after object key");
|
|
1321
1410
|
fj_advance(st, 1);
|
|
1322
|
-
fj_skip_ws_comments(st);
|
|
1323
1411
|
b = fj_byte(st);
|
|
1412
|
+
if (FJ_UNLIKELY(fj_needs_ws_skip(b))) { fj_skip_ws_comments(st); b = fj_byte(st); }
|
|
1324
1413
|
if (b == '{' || b == '[') {
|
|
1325
1414
|
fj_vpush(ps, key);
|
|
1326
1415
|
fj_advance(st, 1);
|
|
@@ -1340,8 +1429,8 @@ static VALUE fj_parse_iter(fj_state *st, int implicit_root) {
|
|
|
1340
1429
|
fj_vpush(ps, fj_parse_member_value(st));
|
|
1341
1430
|
vss = 1;
|
|
1342
1431
|
} else { /* array */
|
|
1343
|
-
fj_skip_ws_comments(st);
|
|
1344
1432
|
b = fj_byte(st);
|
|
1433
|
+
if (FJ_UNLIKELY(fj_needs_ws_skip(b))) { fj_skip_ws_comments(st); b = fj_byte(st); }
|
|
1345
1434
|
if (b == ',') { /* collapsing separator: skip empty slot */
|
|
1346
1435
|
if (st->on_warning != Qnil && !vss) fj_warn(st, fj_sym_empty_slot, "extra comma, collapsed an empty slot");
|
|
1347
1436
|
vss = 0;
|
|
@@ -1367,6 +1456,15 @@ static VALUE fj_parse_iter(fj_state *st, int implicit_root) {
|
|
|
1367
1456
|
vss = 0;
|
|
1368
1457
|
continue;
|
|
1369
1458
|
}
|
|
1459
|
+
/* Strict hot path: inline the two commonest element types — a number and a
|
|
1460
|
+
plain double-quoted string — so they skip fj_parse_member_value's byte
|
|
1461
|
+
re-read + switch. Everything else (quoteless, single/triple-quote,
|
|
1462
|
+
smart-quote, literals) falls through to the full dispatch below. */
|
|
1463
|
+
if (b == '-' || b == '+' || b == '.' || (b >= '0' && b <= '9')) {
|
|
1464
|
+
VALUE num;
|
|
1465
|
+
if (fj_try_member_number(st, &num)) { fj_vpush(ps, num); vss = 1; continue; }
|
|
1466
|
+
}
|
|
1467
|
+
if (b == '"') { fj_vpush(ps, fj_parse_string(st, '"')); vss = 1; continue; }
|
|
1370
1468
|
fj_vpush(ps, fj_parse_member_value(st));
|
|
1371
1469
|
vss = 1;
|
|
1372
1470
|
}
|
|
@@ -1391,9 +1489,46 @@ static int fj_implicit_root_ahead(fj_state *st) {
|
|
|
1391
1489
|
return result;
|
|
1392
1490
|
}
|
|
1393
1491
|
|
|
1492
|
+
/* Between top-level documents, whitespace, comments, AND commas all separate
|
|
1493
|
+
* (commas collapse like the in-container lenient-comma rule). A space alone never
|
|
1494
|
+
* separates — that is handled inside the document by the quoteless run. Mirrors
|
|
1495
|
+
* the Ruby Parser#skip_document_separators. */
|
|
1496
|
+
static void fj_skip_document_separators(fj_state *st) {
|
|
1497
|
+
for (;;) {
|
|
1498
|
+
fj_skip_ws_comments(st);
|
|
1499
|
+
if (fj_byte(st) != ',') break;
|
|
1500
|
+
fj_advance(st, 1);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
static int fj_is_hws(int b) { return b == ' ' || b == '\t' || b == 0x0B || b == 0x0C; }
|
|
1505
|
+
|
|
1506
|
+
/* After a top-level value: a self-delimiting value (object / array / string) may be
|
|
1507
|
+
* followed by anything, but a bare scalar (number / keyword) must be followed by a
|
|
1508
|
+
* real separator — a newline, ',', a comment, or EOF. A space is NOT a separator, so
|
|
1509
|
+
* `1 2 3` and `42 "x" true` raise. Mirrors the Ruby Parser#enforce_scalar_boundary. */
|
|
1510
|
+
static void fj_enforce_scalar_boundary(fj_state *st, VALUE value) {
|
|
1511
|
+
int b, nx;
|
|
1512
|
+
if (RB_TYPE_P(value, T_STRING) || RB_TYPE_P(value, T_HASH) || RB_TYPE_P(value, T_ARRAY)) return;
|
|
1513
|
+
for (;;) {
|
|
1514
|
+
b = fj_byte(st);
|
|
1515
|
+
if (b != -1 && fj_is_hws(b)) { fj_advance(st, 1); continue; }
|
|
1516
|
+
if (b != -1 && b >= 0x80) {
|
|
1517
|
+
long m = fj_mbws(st->buf + st->pos, st->len - st->pos);
|
|
1518
|
+
if (m > 0) { st->pos += m; continue; } /* multibyte horizontal whitespace (NBSP, …) */
|
|
1519
|
+
}
|
|
1520
|
+
break;
|
|
1521
|
+
}
|
|
1522
|
+
b = fj_byte(st);
|
|
1523
|
+
if (b == -1 || b == 0x0A || b == 0x0D || b == ',') return;
|
|
1524
|
+
if (b == '#') return;
|
|
1525
|
+
if (b == '/') { nx = fj_byte_at(st, 1); if (nx == '/' || nx == '*') return; }
|
|
1526
|
+
fj_error(st, "a top-level number or keyword must be followed by a newline, ',', or end of input");
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1394
1529
|
static VALUE fj_parse_c(VALUE self, VALUE input, VALUE opts) {
|
|
1395
1530
|
fj_state st;
|
|
1396
|
-
VALUE
|
|
1531
|
+
VALUE enc_opt, dk;
|
|
1397
1532
|
|
|
1398
1533
|
Check_Type(input, T_STRING);
|
|
1399
1534
|
|
|
@@ -1423,13 +1558,12 @@ static VALUE fj_parse_c(VALUE self, VALUE input, VALUE opts) {
|
|
|
1423
1558
|
st.symbolize_keys = RTEST(rb_hash_aref(opts, fj_sym_symbolize_keys));
|
|
1424
1559
|
dk = rb_hash_aref(opts, fj_sym_duplicate_key);
|
|
1425
1560
|
st.dup_first_wins = (dk == fj_sym_first_wins);
|
|
1426
|
-
st.dup_raise = (dk == fj_sym_raise);
|
|
1427
1561
|
|
|
1428
1562
|
{
|
|
1429
|
-
VALUE bd = rb_hash_aref(opts,
|
|
1430
|
-
if (bd == fj_sym_float) st.
|
|
1431
|
-
else if (bd == fj_sym_bigdecimal) st.
|
|
1432
|
-
else st.
|
|
1563
|
+
VALUE bd = rb_hash_aref(opts, fj_sym_decimal_precision);
|
|
1564
|
+
if (bd == fj_sym_float) st.decimal_precision = 0;
|
|
1565
|
+
else if (bd == fj_sym_bigdecimal) st.decimal_precision = 2;
|
|
1566
|
+
else st.decimal_precision = 1; /* :auto (default), including nil */
|
|
1433
1567
|
}
|
|
1434
1568
|
|
|
1435
1569
|
st.on_warning = rb_hash_aref(opts, fj_sym_on_warning); /* Qnil when absent */
|
|
@@ -1439,36 +1573,37 @@ static VALUE fj_parse_c(VALUE self, VALUE input, VALUE opts) {
|
|
|
1439
1573
|
st.pos = 3;
|
|
1440
1574
|
}
|
|
1441
1575
|
|
|
1442
|
-
/* With a block: yield each top-level
|
|
1443
|
-
* concatenated). Same loop as the Ruby each_value path
|
|
1576
|
+
/* With a block: yield each top-level document until EOF and return the document
|
|
1577
|
+
* count (NDJSON / JSONL / concatenated). Same loop as the Ruby each_value path. */
|
|
1444
1578
|
if (rb_block_given_p()) {
|
|
1579
|
+
long count = 0;
|
|
1445
1580
|
for (;;) {
|
|
1446
|
-
|
|
1581
|
+
VALUE v;
|
|
1582
|
+
fj_skip_document_separators(&st);
|
|
1447
1583
|
if (fj_eof(&st)) break;
|
|
1448
|
-
|
|
1584
|
+
v = fj_parse_iter(&st, fj_implicit_root_ahead(&st));
|
|
1585
|
+
fj_enforce_scalar_boundary(&st, v);
|
|
1586
|
+
rb_yield(v);
|
|
1587
|
+
count++;
|
|
1449
1588
|
}
|
|
1450
|
-
return
|
|
1589
|
+
return LONG2NUM(count);
|
|
1451
1590
|
}
|
|
1452
1591
|
|
|
1453
|
-
/* No block:
|
|
1454
|
-
*
|
|
1455
|
-
*
|
|
1456
|
-
*
|
|
1457
|
-
*
|
|
1458
|
-
* whitespace / newline / concatenation do), so a bracketless comma list still
|
|
1459
|
-
* raises in fj_parse_iter — the unsupported implicit-root array. */
|
|
1460
|
-
fj_skip_ws_comments(&st);
|
|
1461
|
-
if (fj_eof(&st)) return Qnil;
|
|
1462
|
-
value = fj_parse_iter(&st, fj_implicit_root_ahead(&st));
|
|
1463
|
-
fj_skip_ws_comments(&st);
|
|
1464
|
-
if (fj_eof(&st)) return value;
|
|
1592
|
+
/* No block: always return an Array of every top-level document (0 -> [], 1 ->
|
|
1593
|
+
* [doc], 2+ -> [d1, d2, …]) — the always-array contract. Documents are separated by
|
|
1594
|
+
* newline / comma / concatenation (self-delimiting values); a space alone never
|
|
1595
|
+
* separates, and a bare scalar must be followed by a real separator, so `1 2 3`
|
|
1596
|
+
* raises while `1\n2\n3` and `1, 2, 3` are three documents. */
|
|
1465
1597
|
{
|
|
1466
1598
|
VALUE arr = rb_ary_new();
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1599
|
+
for (;;) {
|
|
1600
|
+
VALUE v;
|
|
1601
|
+
fj_skip_document_separators(&st);
|
|
1602
|
+
if (fj_eof(&st)) break;
|
|
1603
|
+
v = fj_parse_iter(&st, fj_implicit_root_ahead(&st));
|
|
1604
|
+
fj_enforce_scalar_boundary(&st, v);
|
|
1605
|
+
rb_ary_push(arr, v);
|
|
1606
|
+
}
|
|
1472
1607
|
return arr;
|
|
1473
1608
|
}
|
|
1474
1609
|
}
|
|
@@ -1493,8 +1628,7 @@ void Init_smarter_json(void) {
|
|
|
1493
1628
|
fj_sym_encoding = ID2SYM(rb_intern("encoding"));
|
|
1494
1629
|
fj_sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
|
1495
1630
|
fj_sym_first_wins = ID2SYM(rb_intern("first_wins"));
|
|
1496
|
-
|
|
1497
|
-
fj_sym_bigdecimal_load = ID2SYM(rb_intern("bigdecimal_load"));
|
|
1631
|
+
fj_sym_decimal_precision = ID2SYM(rb_intern("decimal_precision"));
|
|
1498
1632
|
fj_sym_float = ID2SYM(rb_intern("float"));
|
|
1499
1633
|
fj_sym_bigdecimal = ID2SYM(rb_intern("bigdecimal"));
|
|
1500
1634
|
fj_sym_on_warning = ID2SYM(rb_intern("on_warning"));
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 The fast_float authors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any
|
|
6
|
+
person obtaining a copy of this software and associated
|
|
7
|
+
documentation files (the "Software"), to deal in the
|
|
8
|
+
Software without restriction, including without
|
|
9
|
+
limitation the rights to use, copy, modify, merge,
|
|
10
|
+
publish, distribute, sublicense, and/or sell copies of
|
|
11
|
+
the Software, and to permit persons to whom the Software
|
|
12
|
+
is furnished to do so, subject to the following
|
|
13
|
+
conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice
|
|
16
|
+
shall be included in all copies or substantial portions
|
|
17
|
+
of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
20
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
21
|
+
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
22
|
+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
23
|
+
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
24
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
25
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
26
|
+
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
27
|
+
DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/* Eisel-Lemire decimal->double, ported from fast_float:
|
|
2
|
+
* include/fast_float/decimal_to_binary.h, the compute_float<binary_format<double>>
|
|
3
|
+
* + compute_product_approximation routines.
|
|
4
|
+
*
|
|
5
|
+
* Algorithm authors: Michael Eisel (original approach) and Daniel Lemire
|
|
6
|
+
* (formalization, proof, and the fast_float implementation) — hence "Eisel-Lemire".
|
|
7
|
+
*
|
|
8
|
+
* Copyright (c) 2021 The fast_float authors. Tri-licensed Apache-2.0 / MIT / BSL-1.0;
|
|
9
|
+
* used here under MIT — see LICENSE-fast_float-MIT in this directory.
|
|
10
|
+
*
|
|
11
|
+
* This is the "without fallback" variant (Noble Mushtak & Daniel Lemire, "Fast
|
|
12
|
+
* Number Parsing Without Fallback"): for ANY nonzero mantissa w that fits exactly
|
|
13
|
+
* in a uint64 (i.e. <= 19 significant digits, not truncated) and decimal exponent
|
|
14
|
+
* q, it returns the correctly-rounded binary64 with no slow-path needed.
|
|
15
|
+
*
|
|
16
|
+
* smarter_json uses it as THE decimal->double path for mantissas up to 18 digits
|
|
17
|
+
* (everything wider / overflowed / with an extreme exponent goes to the strtod
|
|
18
|
+
* round-to-odd fallback). It is correctly-rounded across that whole range, with no
|
|
19
|
+
* round-to-even tie loss, and is fast on the common short-mantissa case.
|
|
20
|
+
* Verified bit-for-bit vs JSON.parse. See eisel_lemire.md for provenance. */
|
|
21
|
+
#ifndef FJ_EISEL_LEMIRE_H
|
|
22
|
+
#define FJ_EISEL_LEMIRE_H
|
|
23
|
+
|
|
24
|
+
#include <stdint.h>
|
|
25
|
+
#include <string.h>
|
|
26
|
+
#include "eisel_lemire_powers.h"
|
|
27
|
+
|
|
28
|
+
/* binary_format<double> constants from fast_float. */
|
|
29
|
+
#define FJ_EL_MANTISSA_BITS 52
|
|
30
|
+
#define FJ_EL_MIN_EXPONENT (-1023)
|
|
31
|
+
#define FJ_EL_INFINITE_POWER 0x7FF
|
|
32
|
+
#define FJ_EL_SMALLEST_POW10 (-342)
|
|
33
|
+
#define FJ_EL_LARGEST_POW10 308
|
|
34
|
+
#define FJ_EL_MIN_RTE (-4) /* min_exponent_round_to_even */
|
|
35
|
+
#define FJ_EL_MAX_RTE 23 /* max_exponent_round_to_even */
|
|
36
|
+
|
|
37
|
+
/* (((152170 + 65536) * q) >> 16) + 63 == floor(log2(10^q)) + q + 63, see paper. */
|
|
38
|
+
static inline int32_t fj_el_power(int32_t q) {
|
|
39
|
+
return (((152170 + 65536) * q) >> 16) + 63;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static inline void fj_el_mul128(uint64_t a, uint64_t b, uint64_t *hi, uint64_t *lo) {
|
|
43
|
+
#if defined(__SIZEOF_INT128__)
|
|
44
|
+
__uint128_t p = (__uint128_t)a * (__uint128_t)b;
|
|
45
|
+
*lo = (uint64_t)p;
|
|
46
|
+
*hi = (uint64_t)(p >> 64);
|
|
47
|
+
#else
|
|
48
|
+
uint64_t a0 = (uint32_t)a, a1 = a >> 32, b0 = (uint32_t)b, b1 = b >> 32;
|
|
49
|
+
uint64_t p00 = a0 * b0, p01 = a0 * b1, p10 = a1 * b0, p11 = a1 * b1;
|
|
50
|
+
uint64_t mid = p10 + (p00 >> 32) + (uint32_t)p01;
|
|
51
|
+
*hi = p11 + (mid >> 32) + (p01 >> 32);
|
|
52
|
+
*lo = (mid << 32) | (uint32_t)p00;
|
|
53
|
+
#endif
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static inline double fj_el_bits2double(uint64_t bits) {
|
|
57
|
+
double d;
|
|
58
|
+
memcpy(&d, &bits, sizeof(d));
|
|
59
|
+
return d;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* q = power of ten, w = mantissa (NONZERO, exact, fits in uint64). neg = sign. */
|
|
63
|
+
static inline double fj_eisel_lemire_s2d(int64_t q, uint64_t w, int neg) {
|
|
64
|
+
const uint64_t sign = (uint64_t)(neg != 0) << 63;
|
|
65
|
+
uint64_t mantissa, prod_hi, prod_lo, sp_hi, sp_lo;
|
|
66
|
+
int32_t power2;
|
|
67
|
+
int lz, upperbit, shift, index;
|
|
68
|
+
|
|
69
|
+
if (q < FJ_EL_SMALLEST_POW10) return fj_el_bits2double(sign); /* underflow -> 0 */
|
|
70
|
+
if (q > FJ_EL_LARGEST_POW10)
|
|
71
|
+
return fj_el_bits2double(sign | ((uint64_t)FJ_EL_INFINITE_POWER << FJ_EL_MANTISSA_BITS));
|
|
72
|
+
|
|
73
|
+
lz = __builtin_clzll(w);
|
|
74
|
+
w <<= lz;
|
|
75
|
+
|
|
76
|
+
/* compute_product_approximation<mantissa_bits + 3 = 55>: precision_mask = 0x1FF. */
|
|
77
|
+
index = 2 * (int)(q - FJ_EL_SMALLEST_POWER_OF_FIVE);
|
|
78
|
+
fj_el_mul128(w, fj_power_of_five_128[index], &prod_hi, &prod_lo);
|
|
79
|
+
if ((prod_hi & 0x1FF) == 0x1FF) {
|
|
80
|
+
fj_el_mul128(w, fj_power_of_five_128[index + 1], &sp_hi, &sp_lo);
|
|
81
|
+
prod_lo += sp_hi;
|
|
82
|
+
if (sp_hi > prod_lo) prod_hi++;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
upperbit = (int)(prod_hi >> 63);
|
|
86
|
+
shift = upperbit + 64 - FJ_EL_MANTISSA_BITS - 3; /* upperbit + 9 */
|
|
87
|
+
mantissa = prod_hi >> shift;
|
|
88
|
+
power2 = (int32_t)(fj_el_power((int32_t)q) + upperbit - lz - FJ_EL_MIN_EXPONENT);
|
|
89
|
+
|
|
90
|
+
if (power2 <= 0) { /* subnormal */
|
|
91
|
+
if (-power2 + 1 >= 64) return fj_el_bits2double(sign); /* far below min -> 0 */
|
|
92
|
+
mantissa >>= (-power2 + 1);
|
|
93
|
+
mantissa += (mantissa & 1);
|
|
94
|
+
mantissa >>= 1;
|
|
95
|
+
power2 = (mantissa < ((uint64_t)1 << FJ_EL_MANTISSA_BITS)) ? 0 : 1;
|
|
96
|
+
return fj_el_bits2double(sign | ((uint64_t)power2 << FJ_EL_MANTISSA_BITS) | mantissa);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* round-to-even: if we land exactly between two doubles, round down. */
|
|
100
|
+
if ((prod_lo <= 1) && (q >= FJ_EL_MIN_RTE) && (q <= FJ_EL_MAX_RTE) &&
|
|
101
|
+
((mantissa & 3) == 1) && ((mantissa << shift) == prod_hi)) {
|
|
102
|
+
mantissa &= ~(uint64_t)1;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
mantissa += (mantissa & 1);
|
|
106
|
+
mantissa >>= 1;
|
|
107
|
+
if (mantissa >= ((uint64_t)2 << FJ_EL_MANTISSA_BITS)) {
|
|
108
|
+
mantissa = (uint64_t)1 << FJ_EL_MANTISSA_BITS;
|
|
109
|
+
power2++;
|
|
110
|
+
}
|
|
111
|
+
mantissa &= ~((uint64_t)1 << FJ_EL_MANTISSA_BITS); /* drop implicit bit */
|
|
112
|
+
if (power2 >= FJ_EL_INFINITE_POWER)
|
|
113
|
+
return fj_el_bits2double(sign | ((uint64_t)FJ_EL_INFINITE_POWER << FJ_EL_MANTISSA_BITS));
|
|
114
|
+
return fj_el_bits2double(sign | ((uint64_t)power2 << FJ_EL_MANTISSA_BITS) | mantissa);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
#endif
|