oj 3.11.5 → 3.16.5
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/CHANGELOG.md +1421 -0
- data/README.md +19 -5
- data/RELEASE_NOTES.md +61 -0
- data/ext/oj/buf.h +20 -6
- data/ext/oj/cache.c +329 -0
- data/ext/oj/cache.h +22 -0
- data/ext/oj/cache8.c +10 -9
- data/ext/oj/circarray.c +8 -6
- data/ext/oj/circarray.h +2 -2
- data/ext/oj/code.c +19 -33
- data/ext/oj/code.h +2 -2
- data/ext/oj/compat.c +27 -77
- data/ext/oj/custom.c +86 -179
- data/ext/oj/debug.c +126 -0
- data/ext/oj/dump.c +256 -249
- data/ext/oj/dump.h +26 -12
- data/ext/oj/dump_compat.c +565 -642
- data/ext/oj/dump_leaf.c +17 -63
- data/ext/oj/dump_object.c +65 -187
- data/ext/oj/dump_strict.c +27 -51
- data/ext/oj/encoder.c +43 -0
- data/ext/oj/err.c +2 -13
- data/ext/oj/err.h +24 -8
- data/ext/oj/extconf.rb +21 -6
- data/ext/oj/fast.c +149 -149
- data/ext/oj/intern.c +313 -0
- data/ext/oj/intern.h +22 -0
- data/ext/oj/mem.c +318 -0
- data/ext/oj/mem.h +53 -0
- data/ext/oj/mimic_json.c +121 -106
- data/ext/oj/object.c +85 -162
- data/ext/oj/odd.c +89 -67
- data/ext/oj/odd.h +15 -15
- data/ext/oj/oj.c +542 -411
- data/ext/oj/oj.h +99 -73
- data/ext/oj/parse.c +175 -187
- data/ext/oj/parse.h +26 -24
- data/ext/oj/parser.c +1600 -0
- data/ext/oj/parser.h +101 -0
- data/ext/oj/rails.c +112 -159
- data/ext/oj/rails.h +1 -1
- data/ext/oj/reader.c +11 -14
- data/ext/oj/reader.h +4 -2
- data/ext/oj/resolve.c +5 -24
- data/ext/oj/rxclass.c +7 -6
- data/ext/oj/rxclass.h +1 -1
- data/ext/oj/saj.c +22 -33
- data/ext/oj/saj2.c +584 -0
- data/ext/oj/saj2.h +23 -0
- data/ext/oj/scp.c +5 -28
- data/ext/oj/sparse.c +28 -72
- data/ext/oj/stream_writer.c +50 -40
- data/ext/oj/strict.c +56 -61
- data/ext/oj/string_writer.c +72 -39
- data/ext/oj/trace.h +31 -4
- data/ext/oj/usual.c +1218 -0
- data/ext/oj/usual.h +69 -0
- data/ext/oj/util.h +1 -1
- data/ext/oj/val_stack.c +14 -3
- data/ext/oj/val_stack.h +8 -7
- data/ext/oj/validate.c +46 -0
- data/ext/oj/wab.c +63 -88
- data/lib/oj/active_support_helper.rb +1 -3
- data/lib/oj/bag.rb +7 -1
- data/lib/oj/easy_hash.rb +4 -5
- data/lib/oj/error.rb +1 -2
- data/lib/oj/json.rb +162 -150
- data/lib/oj/mimic.rb +9 -7
- data/lib/oj/saj.rb +20 -6
- data/lib/oj/schandler.rb +5 -4
- data/lib/oj/state.rb +12 -8
- data/lib/oj/version.rb +1 -2
- data/lib/oj.rb +2 -0
- data/pages/Compatibility.md +1 -1
- data/pages/InstallOptions.md +20 -0
- data/pages/JsonGem.md +15 -0
- data/pages/Modes.md +8 -3
- data/pages/Options.md +43 -5
- data/pages/Parser.md +309 -0
- data/pages/Rails.md +14 -2
- data/test/_test_active.rb +8 -9
- data/test/_test_active_mimic.rb +7 -8
- data/test/_test_mimic_rails.rb +17 -20
- data/test/activerecord/result_test.rb +5 -6
- data/test/activesupport6/encoding_test.rb +63 -28
- data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
- data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
- data/test/{activesupport5 → activesupport7}/encoding_test.rb +86 -50
- data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
- data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
- data/test/files.rb +15 -15
- data/test/foo.rb +16 -45
- data/test/helper.rb +11 -8
- data/test/isolated/shared.rb +3 -2
- data/test/json_gem/json_addition_test.rb +2 -2
- data/test/json_gem/json_common_interface_test.rb +8 -6
- data/test/json_gem/json_encoding_test.rb +0 -0
- data/test/json_gem/json_ext_parser_test.rb +1 -0
- data/test/json_gem/json_fixtures_test.rb +3 -2
- data/test/json_gem/json_generator_test.rb +56 -38
- data/test/json_gem/json_generic_object_test.rb +11 -11
- data/test/json_gem/json_parser_test.rb +54 -47
- data/test/json_gem/json_string_matching_test.rb +9 -9
- data/test/json_gem/test_helper.rb +7 -3
- data/test/mem.rb +34 -0
- data/test/perf.rb +22 -27
- data/test/perf_compat.rb +31 -33
- data/test/perf_dump.rb +50 -0
- data/test/perf_fast.rb +80 -82
- data/test/perf_file.rb +27 -29
- data/test/perf_object.rb +65 -69
- data/test/perf_once.rb +59 -0
- data/test/perf_parser.rb +183 -0
- data/test/perf_saj.rb +46 -54
- data/test/perf_scp.rb +58 -69
- data/test/perf_simple.rb +41 -39
- data/test/perf_strict.rb +74 -82
- data/test/perf_wab.rb +67 -69
- data/test/prec.rb +5 -5
- data/test/sample/change.rb +0 -1
- data/test/sample/dir.rb +0 -1
- data/test/sample/doc.rb +0 -1
- data/test/sample/file.rb +0 -1
- data/test/sample/group.rb +0 -1
- data/test/sample/hasprops.rb +0 -1
- data/test/sample/layer.rb +0 -1
- data/test/sample/rect.rb +0 -1
- data/test/sample/shape.rb +0 -1
- data/test/sample/text.rb +0 -1
- data/test/sample.rb +16 -16
- data/test/sample_json.rb +8 -8
- data/test/test_compat.rb +95 -43
- data/test/test_custom.rb +73 -51
- data/test/test_debian.rb +7 -10
- data/test/test_fast.rb +135 -79
- data/test/test_file.rb +41 -30
- data/test/test_gc.rb +16 -5
- data/test/test_generate.rb +5 -5
- data/test/test_hash.rb +5 -5
- data/test/test_integer_range.rb +9 -9
- data/test/test_null.rb +20 -20
- data/test/test_object.rb +99 -96
- data/test/test_parser.rb +11 -0
- data/test/test_parser_debug.rb +27 -0
- data/test/test_parser_saj.rb +337 -0
- data/test/test_parser_usual.rb +251 -0
- data/test/test_rails.rb +2 -2
- data/test/test_saj.rb +10 -8
- data/test/test_scp.rb +37 -39
- data/test/test_strict.rb +40 -32
- data/test/test_various.rb +165 -84
- data/test/test_wab.rb +48 -44
- data/test/test_writer.rb +47 -47
- data/test/tests.rb +13 -5
- data/test/tests_mimic.rb +12 -3
- data/test/tests_mimic_addition.rb +12 -3
- metadata +74 -128
- data/ext/oj/hash.c +0 -131
- data/ext/oj/hash.h +0 -19
- data/ext/oj/hash_test.c +0 -491
- data/test/activesupport4/decoding_test.rb +0 -108
- data/test/activesupport4/encoding_test.rb +0 -531
- data/test/activesupport4/test_helper.rb +0 -41
- data/test/activesupport5/test_helper.rb +0 -72
- data/test/bar.rb +0 -35
- data/test/baz.rb +0 -16
- data/test/zoo.rb +0 -13
data/ext/oj/parse.c
CHANGED
@@ -12,14 +12,19 @@
|
|
12
12
|
|
13
13
|
#include "buf.h"
|
14
14
|
#include "encode.h"
|
15
|
+
#include "mem.h"
|
15
16
|
#include "oj.h"
|
16
17
|
#include "rxclass.h"
|
17
18
|
#include "val_stack.h"
|
18
19
|
|
20
|
+
#ifdef OJ_USE_SSE4_2
|
21
|
+
#include <nmmintrin.h>
|
22
|
+
#endif
|
23
|
+
|
19
24
|
// Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
|
20
25
|
#define OJ_INFINITY (1.0 / 0.0)
|
21
26
|
|
22
|
-
|
27
|
+
// #define EXP_MAX 1023
|
23
28
|
#define EXP_MAX 100000
|
24
29
|
#define DEC_MAX 15
|
25
30
|
|
@@ -44,11 +49,7 @@ static void skip_comment(ParseInfo pi) {
|
|
44
49
|
pi->cur += 2;
|
45
50
|
return;
|
46
51
|
} else if (pi->end <= pi->cur) {
|
47
|
-
oj_set_error_at(pi,
|
48
|
-
oj_parse_error_class,
|
49
|
-
__FILE__,
|
50
|
-
__LINE__,
|
51
|
-
"comment not terminated");
|
52
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "comment not terminated");
|
52
53
|
return;
|
53
54
|
}
|
54
55
|
}
|
@@ -81,9 +82,8 @@ static void add_value(ParseInfo pi, VALUE rval) {
|
|
81
82
|
break;
|
82
83
|
case NEXT_HASH_VALUE:
|
83
84
|
pi->hash_set_value(pi, parent, rval);
|
84
|
-
if (0 != parent->key && 0 < parent->klen &&
|
85
|
-
(
|
86
|
-
xfree((char *)parent->key);
|
85
|
+
if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
|
86
|
+
OJ_R_FREE((char *)parent->key);
|
87
87
|
parent->key = 0;
|
88
88
|
}
|
89
89
|
parent->next = NEXT_HASH_COMMA;
|
@@ -183,6 +183,46 @@ static void unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
|
|
183
183
|
}
|
184
184
|
}
|
185
185
|
|
186
|
+
static inline const char *scan_string_noSIMD(const char *str, const char *end) {
|
187
|
+
for (; '"' != *str; str++) {
|
188
|
+
if (end <= str || '\0' == *str || '\\' == *str) {
|
189
|
+
break;
|
190
|
+
}
|
191
|
+
}
|
192
|
+
return str;
|
193
|
+
}
|
194
|
+
|
195
|
+
#ifdef OJ_USE_SSE4_2
|
196
|
+
static inline const char *scan_string_SIMD(const char *str, const char *end) {
|
197
|
+
static const char chars[16] = "\x00\\\"";
|
198
|
+
const __m128i terminate = _mm_loadu_si128((const __m128i *)&chars[0]);
|
199
|
+
const char *_end = (const char *)(end - 16);
|
200
|
+
|
201
|
+
for (; str <= _end; str += 16) {
|
202
|
+
const __m128i string = _mm_loadu_si128((const __m128i *)str);
|
203
|
+
const int r = _mm_cmpestri(terminate,
|
204
|
+
3,
|
205
|
+
string,
|
206
|
+
16,
|
207
|
+
_SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT);
|
208
|
+
if (r != 16) {
|
209
|
+
str = (char *)(str + r);
|
210
|
+
return str;
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
return scan_string_noSIMD(str, end);
|
215
|
+
}
|
216
|
+
#endif
|
217
|
+
|
218
|
+
static const char *(*scan_func)(const char *str, const char *end) = scan_string_noSIMD;
|
219
|
+
|
220
|
+
void oj_scanner_init(void) {
|
221
|
+
#ifdef OJ_USE_SSE4_2
|
222
|
+
scan_func = scan_string_SIMD;
|
223
|
+
#endif
|
224
|
+
}
|
225
|
+
|
186
226
|
// entered at /
|
187
227
|
static void read_escaped_str(ParseInfo pi, const char *start) {
|
188
228
|
struct _buf buf;
|
@@ -192,19 +232,20 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
192
232
|
Val parent = stack_peek(&pi->stack);
|
193
233
|
|
194
234
|
buf_init(&buf);
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
if (
|
200
|
-
|
201
|
-
|
202
|
-
__FILE__,
|
203
|
-
__LINE__,
|
204
|
-
"quoted string not terminated");
|
235
|
+
buf_append_string(&buf, start, cnt);
|
236
|
+
|
237
|
+
for (s = pi->cur; '"' != *s;) {
|
238
|
+
const char *scanned = scan_func(s, pi->end);
|
239
|
+
if (scanned >= pi->end || '\0' == *scanned) {
|
240
|
+
// if (scanned >= pi->end) {
|
241
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
|
205
242
|
buf_cleanup(&buf);
|
206
243
|
return;
|
207
|
-
}
|
244
|
+
}
|
245
|
+
buf_append_string(&buf, s, (size_t)(scanned - s));
|
246
|
+
s = scanned;
|
247
|
+
|
248
|
+
if ('\\' == *s) {
|
208
249
|
s++;
|
209
250
|
switch (*s) {
|
210
251
|
case 'n': buf_append(&buf, '\n'); break;
|
@@ -234,11 +275,7 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
234
275
|
break;
|
235
276
|
}
|
236
277
|
pi->cur = s;
|
237
|
-
oj_set_error_at(pi,
|
238
|
-
oj_parse_error_class,
|
239
|
-
__FILE__,
|
240
|
-
__LINE__,
|
241
|
-
"invalid escaped character");
|
278
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
|
242
279
|
buf_cleanup(&buf);
|
243
280
|
return;
|
244
281
|
}
|
@@ -265,16 +302,11 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
265
302
|
break;
|
266
303
|
}
|
267
304
|
pi->cur = s;
|
268
|
-
oj_set_error_at(pi,
|
269
|
-
oj_parse_error_class,
|
270
|
-
__FILE__,
|
271
|
-
__LINE__,
|
272
|
-
"invalid escaped character");
|
305
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
|
273
306
|
buf_cleanup(&buf);
|
274
307
|
return;
|
275
308
|
}
|
276
|
-
|
277
|
-
buf_append(&buf, *s);
|
309
|
+
s++;
|
278
310
|
}
|
279
311
|
}
|
280
312
|
if (0 == parent) {
|
@@ -290,7 +322,7 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
290
322
|
case NEXT_HASH_KEY:
|
291
323
|
if (Qundef == (parent->key_val = pi->hash_key(pi, buf.head, buf_len(&buf)))) {
|
292
324
|
parent->klen = buf_len(&buf);
|
293
|
-
parent->key =
|
325
|
+
parent->key = OJ_MALLOC(parent->klen + 1);
|
294
326
|
memcpy((char *)parent->key, buf.head, parent->klen);
|
295
327
|
*(char *)(parent->key + parent->klen) = '\0';
|
296
328
|
} else {
|
@@ -302,9 +334,8 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
302
334
|
break;
|
303
335
|
case NEXT_HASH_VALUE:
|
304
336
|
pi->hash_set_cstr(pi, parent, buf.head, buf_len(&buf), start);
|
305
|
-
if (0 != parent->key && 0 < parent->klen &&
|
306
|
-
(
|
307
|
-
xfree((char *)parent->key);
|
337
|
+
if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
|
338
|
+
OJ_R_FREE((char *)parent->key);
|
308
339
|
parent->key = 0;
|
309
340
|
}
|
310
341
|
parent->next = NEXT_HASH_COMMA;
|
@@ -331,22 +362,20 @@ static void read_str(ParseInfo pi) {
|
|
331
362
|
const char *str = pi->cur;
|
332
363
|
Val parent = stack_peek(&pi->stack);
|
333
364
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
read_escaped_str(pi, str);
|
347
|
-
return;
|
348
|
-
}
|
365
|
+
pi->cur = scan_func(pi->cur, pi->end);
|
366
|
+
if (RB_UNLIKELY(pi->end <= pi->cur)) {
|
367
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
|
368
|
+
return;
|
369
|
+
}
|
370
|
+
if (RB_UNLIKELY('\0' == *pi->cur)) {
|
371
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "NULL byte in string");
|
372
|
+
return;
|
373
|
+
}
|
374
|
+
if ('\\' == *pi->cur) {
|
375
|
+
read_escaped_str(pi, str);
|
376
|
+
return;
|
349
377
|
}
|
378
|
+
|
350
379
|
if (0 == parent) { // simple add
|
351
380
|
pi->add_cstr(pi, str, pi->cur - str, str);
|
352
381
|
} else {
|
@@ -370,9 +399,8 @@ static void read_str(ParseInfo pi) {
|
|
370
399
|
break;
|
371
400
|
case NEXT_HASH_VALUE:
|
372
401
|
pi->hash_set_cstr(pi, parent, str, pi->cur - str, str);
|
373
|
-
if (0 != parent->key && 0 < parent->klen &&
|
374
|
-
(
|
375
|
-
xfree((char *)parent->key);
|
402
|
+
if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
|
403
|
+
OJ_R_FREE((char *)parent->key);
|
376
404
|
parent->key = 0;
|
377
405
|
}
|
378
406
|
parent->next = NEXT_HASH_COMMA;
|
@@ -398,6 +426,7 @@ static void read_num(ParseInfo pi) {
|
|
398
426
|
struct _numInfo ni;
|
399
427
|
Val parent = stack_peek(&pi->stack);
|
400
428
|
|
429
|
+
ni.pi = pi;
|
401
430
|
ni.str = pi->cur;
|
402
431
|
ni.i = 0;
|
403
432
|
ni.num = 0;
|
@@ -414,7 +443,7 @@ static void read_num(ParseInfo pi) {
|
|
414
443
|
ni.no_big = !pi->options.compat_bigdec;
|
415
444
|
ni.bigdec_load = pi->options.compat_bigdec;
|
416
445
|
} else {
|
417
|
-
ni.no_big
|
446
|
+
ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load ||
|
418
447
|
RubyDec == pi->options.bigdec_load);
|
419
448
|
ni.bigdec_load = pi->options.bigdec_load;
|
420
449
|
}
|
@@ -424,33 +453,21 @@ static void read_num(ParseInfo pi) {
|
|
424
453
|
ni.neg = 1;
|
425
454
|
} else if ('+' == *pi->cur) {
|
426
455
|
if (StrictMode == pi->options.mode) {
|
427
|
-
oj_set_error_at(pi,
|
428
|
-
oj_parse_error_class,
|
429
|
-
__FILE__,
|
430
|
-
__LINE__,
|
431
|
-
"not a number or other value");
|
456
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
432
457
|
return;
|
433
458
|
}
|
434
459
|
pi->cur++;
|
435
460
|
}
|
436
461
|
if ('I' == *pi->cur) {
|
437
462
|
if (No == pi->options.allow_nan || 0 != strncmp("Infinity", pi->cur, 8)) {
|
438
|
-
oj_set_error_at(pi,
|
439
|
-
oj_parse_error_class,
|
440
|
-
__FILE__,
|
441
|
-
__LINE__,
|
442
|
-
"not a number or other value");
|
463
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
443
464
|
return;
|
444
465
|
}
|
445
466
|
pi->cur += 8;
|
446
467
|
ni.infinity = 1;
|
447
468
|
} else if ('N' == *pi->cur || 'n' == *pi->cur) {
|
448
469
|
if ('a' != pi->cur[1] || ('N' != pi->cur[2] && 'n' != pi->cur[2])) {
|
449
|
-
oj_set_error_at(pi,
|
450
|
-
oj_parse_error_class,
|
451
|
-
__FILE__,
|
452
|
-
__LINE__,
|
453
|
-
"not a number or other value");
|
470
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
454
471
|
return;
|
455
472
|
}
|
456
473
|
pi->cur += 3;
|
@@ -459,37 +476,31 @@ static void read_num(ParseInfo pi) {
|
|
459
476
|
int dec_cnt = 0;
|
460
477
|
bool zero1 = false;
|
461
478
|
|
479
|
+
// Skip leading zeros.
|
480
|
+
for (; '0' == *pi->cur; pi->cur++) {
|
481
|
+
zero1 = true;
|
482
|
+
}
|
483
|
+
|
462
484
|
for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
|
463
|
-
|
464
|
-
zero1 = true;
|
465
|
-
}
|
466
|
-
if (0 < ni.i) {
|
467
|
-
dec_cnt++;
|
468
|
-
}
|
469
|
-
if (!ni.big) {
|
470
|
-
int d = (*pi->cur - '0');
|
485
|
+
int d = (*pi->cur - '0');
|
471
486
|
|
472
|
-
|
473
|
-
|
474
|
-
oj_set_error_at(pi,
|
475
|
-
oj_parse_error_class,
|
476
|
-
__FILE__,
|
477
|
-
__LINE__,
|
478
|
-
"not a number");
|
479
|
-
return;
|
480
|
-
}
|
481
|
-
zero1 = false;
|
482
|
-
}
|
483
|
-
ni.i = ni.i * 10 + d;
|
484
|
-
if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
|
485
|
-
ni.big = 1;
|
486
|
-
}
|
487
|
+
if (RB_LIKELY(0 != ni.i)) {
|
488
|
+
dec_cnt++;
|
487
489
|
}
|
490
|
+
ni.i = ni.i * 10 + d;
|
491
|
+
}
|
492
|
+
if (RB_UNLIKELY(0 != ni.i && zero1 && CompatMode == pi->options.mode)) {
|
493
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
|
494
|
+
return;
|
488
495
|
}
|
496
|
+
if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
|
497
|
+
ni.big = true;
|
498
|
+
}
|
499
|
+
|
489
500
|
if ('.' == *pi->cur) {
|
490
501
|
pi->cur++;
|
491
502
|
// A trailing . is not a valid decimal but if encountered allow it
|
492
|
-
// except when
|
503
|
+
// except when mimicking the JSON gem or in strict mode.
|
493
504
|
if (StrictMode == pi->options.mode || CompatMode == pi->options.mode) {
|
494
505
|
int pos = (int)(pi->cur - ni.str);
|
495
506
|
|
@@ -505,25 +516,20 @@ static void read_num(ParseInfo pi) {
|
|
505
516
|
for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
|
506
517
|
int d = (*pi->cur - '0');
|
507
518
|
|
508
|
-
if (0
|
519
|
+
if (RB_LIKELY(0 != ni.num || 0 != ni.i)) {
|
509
520
|
dec_cnt++;
|
510
521
|
}
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
}
|
515
|
-
} else {
|
516
|
-
ni.num = ni.num * 10 + d;
|
517
|
-
ni.div *= 10;
|
518
|
-
ni.di++;
|
519
|
-
if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
|
520
|
-
if (!ni.no_big) {
|
521
|
-
ni.big = true;
|
522
|
-
}
|
523
|
-
}
|
524
|
-
}
|
522
|
+
ni.num = ni.num * 10 + d;
|
523
|
+
ni.div *= 10;
|
524
|
+
ni.di++;
|
525
525
|
}
|
526
526
|
}
|
527
|
+
if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
|
528
|
+
if (!ni.no_big) {
|
529
|
+
ni.big = true;
|
530
|
+
}
|
531
|
+
}
|
532
|
+
|
527
533
|
if ('e' == *pi->cur || 'E' == *pi->cur) {
|
528
534
|
int eneg = 0;
|
529
535
|
|
@@ -576,9 +582,8 @@ static void read_num(ParseInfo pi) {
|
|
576
582
|
break;
|
577
583
|
case NEXT_HASH_VALUE:
|
578
584
|
pi->hash_set_num(pi, parent, &ni);
|
579
|
-
if (0 != parent->key && 0 < parent->klen &&
|
580
|
-
(
|
581
|
-
xfree((char *)parent->key);
|
585
|
+
if (0 != parent->key && 0 < parent->klen && (parent->key < pi->json || pi->cur < parent->key)) {
|
586
|
+
OJ_R_FREE((char *)parent->key);
|
582
587
|
parent->key = 0;
|
583
588
|
}
|
584
589
|
parent->next = NEXT_HASH_COMMA;
|
@@ -596,7 +601,7 @@ static void read_num(ParseInfo pi) {
|
|
596
601
|
}
|
597
602
|
|
598
603
|
static void array_start(ParseInfo pi) {
|
599
|
-
|
604
|
+
VALUE v = pi->start_array(pi);
|
600
605
|
|
601
606
|
stack_push(&pi->stack, v, NEXT_ARRAY_NEW);
|
602
607
|
}
|
@@ -620,13 +625,13 @@ static void array_end(ParseInfo pi) {
|
|
620
625
|
}
|
621
626
|
|
622
627
|
static void hash_start(ParseInfo pi) {
|
623
|
-
|
628
|
+
VALUE v = pi->start_hash(pi);
|
624
629
|
|
625
630
|
stack_push(&pi->stack, v, NEXT_HASH_NEW);
|
626
631
|
}
|
627
632
|
|
628
633
|
static void hash_end(ParseInfo pi) {
|
629
|
-
|
634
|
+
Val hash = stack_peek(&pi->stack);
|
630
635
|
|
631
636
|
// leave hash on stack until just before
|
632
637
|
if (0 == hash) {
|
@@ -705,17 +710,10 @@ void oj_parse2(ParseInfo pi) {
|
|
705
710
|
case '[': array_start(pi); break;
|
706
711
|
case ']': array_end(pi); break;
|
707
712
|
case ',': comma(pi); break;
|
708
|
-
case '"':
|
709
|
-
read_str(pi);
|
710
|
-
break;
|
711
|
-
// case '+':
|
713
|
+
case '"': read_str(pi); break;
|
712
714
|
case '+':
|
713
715
|
if (CompatMode == pi->options.mode) {
|
714
|
-
oj_set_error_at(pi,
|
715
|
-
oj_parse_error_class,
|
716
|
-
__FILE__,
|
717
|
-
__LINE__,
|
718
|
-
"unexpected character");
|
716
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
719
717
|
return;
|
720
718
|
}
|
721
719
|
pi->cur--;
|
@@ -741,11 +739,7 @@ void oj_parse2(ParseInfo pi) {
|
|
741
739
|
pi->cur--;
|
742
740
|
read_num(pi);
|
743
741
|
} else {
|
744
|
-
oj_set_error_at(pi,
|
745
|
-
oj_parse_error_class,
|
746
|
-
__FILE__,
|
747
|
-
__LINE__,
|
748
|
-
"unexpected character");
|
742
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
749
743
|
}
|
750
744
|
break;
|
751
745
|
case 't': read_true(pi); break;
|
@@ -765,9 +759,7 @@ void oj_parse2(ParseInfo pi) {
|
|
765
759
|
}
|
766
760
|
break;
|
767
761
|
case '\0': pi->cur--; return;
|
768
|
-
default:
|
769
|
-
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
770
|
-
return;
|
762
|
+
default: oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character"); return;
|
771
763
|
}
|
772
764
|
if (err_has(&pi->err)) {
|
773
765
|
return;
|
@@ -804,16 +796,15 @@ static VALUE parse_big_decimal(VALUE str) {
|
|
804
796
|
}
|
805
797
|
|
806
798
|
static long double exp_plus[] = {
|
807
|
-
1.0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9,
|
808
|
-
1.
|
809
|
-
1.
|
810
|
-
1.
|
811
|
-
1.0e40, 1.0e41, 1.0e42, 1.0e43, 1.0e44, 1.0e45, 1.0e46, 1.0e47, 1.0e48, 1.0e49,
|
799
|
+
1.0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10, 1.0e11, 1.0e12,
|
800
|
+
1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20, 1.0e21, 1.0e22, 1.0e23, 1.0e24, 1.0e25,
|
801
|
+
1.0e26, 1.0e27, 1.0e28, 1.0e29, 1.0e30, 1.0e31, 1.0e32, 1.0e33, 1.0e34, 1.0e35, 1.0e36, 1.0e37, 1.0e38,
|
802
|
+
1.0e39, 1.0e40, 1.0e41, 1.0e42, 1.0e43, 1.0e44, 1.0e45, 1.0e46, 1.0e47, 1.0e48, 1.0e49,
|
812
803
|
};
|
813
804
|
|
814
805
|
VALUE
|
815
806
|
oj_num_as_value(NumInfo ni) {
|
816
|
-
|
807
|
+
VALUE rnum = Qnil;
|
817
808
|
|
818
809
|
if (ni->infinity) {
|
819
810
|
if (ni->neg) {
|
@@ -832,12 +823,12 @@ oj_num_as_value(NumInfo ni) {
|
|
832
823
|
buf[ni->len] = '\0';
|
833
824
|
rnum = rb_cstr_to_inum(buf, 10, 0);
|
834
825
|
} else {
|
835
|
-
char *buf =
|
826
|
+
char *buf = OJ_R_ALLOC_N(char, ni->len + 1);
|
836
827
|
|
837
828
|
memcpy(buf, ni->str, ni->len);
|
838
829
|
buf[ni->len] = '\0';
|
839
830
|
rnum = rb_cstr_to_inum(buf, 10, 0);
|
840
|
-
|
831
|
+
OJ_R_FREE(buf);
|
841
832
|
}
|
842
833
|
} else {
|
843
834
|
if (ni->neg) {
|
@@ -848,7 +839,7 @@ oj_num_as_value(NumInfo ni) {
|
|
848
839
|
}
|
849
840
|
} else { // decimal
|
850
841
|
if (ni->big) {
|
851
|
-
|
842
|
+
VALUE bd = rb_str_new(ni->str, ni->len);
|
852
843
|
|
853
844
|
rnum = rb_rescue2(parse_big_decimal, bd, rescue_big_decimal, bd, rb_eException, 0);
|
854
845
|
if (ni->no_big) {
|
@@ -876,15 +867,19 @@ oj_num_as_value(NumInfo ni) {
|
|
876
867
|
}
|
877
868
|
rnum = rb_float_new((double)ld);
|
878
869
|
} else if (RubyDec == ni->bigdec_load) {
|
879
|
-
|
870
|
+
VALUE sv = rb_str_new(ni->str, ni->len);
|
880
871
|
|
881
872
|
rnum = rb_funcall(sv, rb_intern("to_f"), 0);
|
882
873
|
} else {
|
883
|
-
char
|
874
|
+
char *end;
|
884
875
|
double d = strtod(ni->str, &end);
|
885
876
|
|
886
877
|
if ((long)ni->len != (long)(end - ni->str)) {
|
887
|
-
|
878
|
+
if (Qnil == ni->pi->err_class) {
|
879
|
+
rb_raise(oj_parse_error_class, "Invalid float");
|
880
|
+
} else {
|
881
|
+
rb_raise(ni->pi->err_class, "Invalid float");
|
882
|
+
}
|
888
883
|
}
|
889
884
|
rnum = rb_float_new(d);
|
890
885
|
}
|
@@ -892,21 +887,24 @@ oj_num_as_value(NumInfo ni) {
|
|
892
887
|
return rnum;
|
893
888
|
}
|
894
889
|
|
895
|
-
void oj_set_error_at(ParseInfo
|
896
|
-
VALUE err_clas,
|
897
|
-
const char *file,
|
898
|
-
int line,
|
899
|
-
const char *format,
|
900
|
-
...) {
|
890
|
+
void oj_set_error_at(ParseInfo pi, VALUE err_clas, const char *file, int line, const char *format, ...) {
|
901
891
|
va_list ap;
|
902
892
|
char msg[256];
|
903
|
-
char
|
904
|
-
char
|
905
|
-
char
|
893
|
+
char *p = msg;
|
894
|
+
char *end = p + sizeof(msg) - 2;
|
895
|
+
char *start;
|
906
896
|
Val vp;
|
897
|
+
int mlen;
|
907
898
|
|
908
899
|
va_start(ap, format);
|
909
|
-
|
900
|
+
mlen = vsnprintf(msg, sizeof(msg) - 1, format, ap);
|
901
|
+
if (0 < mlen) {
|
902
|
+
if (sizeof(msg) - 2 < (size_t)mlen) {
|
903
|
+
p = end - 2;
|
904
|
+
} else {
|
905
|
+
p += mlen;
|
906
|
+
}
|
907
|
+
}
|
910
908
|
va_end(ap);
|
911
909
|
pi->err.clas = err_clas;
|
912
910
|
if (p + 3 < end) {
|
@@ -942,14 +940,7 @@ void oj_set_error_at(ParseInfo pi,
|
|
942
940
|
}
|
943
941
|
*p = '\0';
|
944
942
|
if (0 == pi->json) {
|
945
|
-
oj_err_set(&pi->err,
|
946
|
-
err_clas,
|
947
|
-
"%s at line %d, column %d [%s:%d]",
|
948
|
-
msg,
|
949
|
-
pi->rd.line,
|
950
|
-
pi->rd.col,
|
951
|
-
file,
|
952
|
-
line);
|
943
|
+
oj_err_set(&pi->err, err_clas, "%s at line %d, column %d [%s:%d]", msg, pi->rd.line, pi->rd.col, file, line);
|
953
944
|
} else {
|
954
945
|
_oj_err_set_with_location(&pi->err, err_clas, msg, pi->json, pi->cur - 1, file, line);
|
955
946
|
}
|
@@ -963,24 +954,25 @@ static VALUE protect_parse(VALUE pip) {
|
|
963
954
|
|
964
955
|
extern int oj_utf8_index;
|
965
956
|
|
966
|
-
static void oj_pi_set_input_str(ParseInfo pi,
|
967
|
-
|
957
|
+
static void oj_pi_set_input_str(ParseInfo pi, VALUE *inputp) {
|
958
|
+
int idx = RB_ENCODING_GET(*inputp);
|
968
959
|
|
969
|
-
if (
|
970
|
-
*
|
960
|
+
if (oj_utf8_encoding_index != idx) {
|
961
|
+
rb_encoding *enc = rb_enc_from_index(idx);
|
962
|
+
*inputp = rb_str_conv_enc(*inputp, enc, oj_utf8_encoding);
|
971
963
|
}
|
972
|
-
pi->json =
|
964
|
+
pi->json = RSTRING_PTR(*inputp);
|
973
965
|
pi->end = pi->json + RSTRING_LEN(*inputp);
|
974
966
|
}
|
975
967
|
|
976
968
|
VALUE
|
977
969
|
oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yieldOk) {
|
978
|
-
char *
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
int
|
983
|
-
int
|
970
|
+
char *buf = 0;
|
971
|
+
VALUE input;
|
972
|
+
VALUE wrapped_stack;
|
973
|
+
VALUE result = Qnil;
|
974
|
+
int line = 0;
|
975
|
+
int free_json = 0;
|
984
976
|
|
985
977
|
if (argc < 1) {
|
986
978
|
rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
|
@@ -1016,8 +1008,8 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
|
|
1016
1008
|
rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
|
1017
1009
|
}
|
1018
1010
|
} else {
|
1019
|
-
VALUE
|
1020
|
-
|
1011
|
+
VALUE clas = rb_obj_class(input);
|
1012
|
+
VALUE s;
|
1021
1013
|
|
1022
1014
|
if (oj_stringio_class == clas) {
|
1023
1015
|
s = rb_funcall2(input, oj_string_id, 0, 0);
|
@@ -1029,19 +1021,18 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
|
|
1029
1021
|
size_t len = lseek(fd, 0, SEEK_END);
|
1030
1022
|
|
1031
1023
|
lseek(fd, 0, SEEK_SET);
|
1032
|
-
buf =
|
1024
|
+
buf = OJ_R_ALLOC_N(char, len + 1);
|
1033
1025
|
pi->json = buf;
|
1034
1026
|
pi->end = buf + len;
|
1035
1027
|
if (0 >= (cnt = read(fd, (char *)pi->json, len)) || cnt != (ssize_t)len) {
|
1036
1028
|
if (0 != buf) {
|
1037
|
-
|
1029
|
+
OJ_R_FREE(buf);
|
1038
1030
|
}
|
1039
1031
|
rb_raise(rb_eIOError, "failed to read from IO Object.");
|
1040
1032
|
}
|
1041
1033
|
((char *)pi->json)[len] = '\0';
|
1042
1034
|
/* skip UTF-8 BOM if present */
|
1043
|
-
if (0xEF == (uint8_t)*pi->json && 0xBB == (uint8_t)pi->json[1] &&
|
1044
|
-
0xBF == (uint8_t)pi->json[2]) {
|
1035
|
+
if (0xEF == (uint8_t)*pi->json && 0xBB == (uint8_t)pi->json[1] && 0xBF == (uint8_t)pi->json[2]) {
|
1045
1036
|
pi->cur += 3;
|
1046
1037
|
}
|
1047
1038
|
#endif
|
@@ -1067,8 +1058,7 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
|
|
1067
1058
|
wrapped_stack = oj_stack_init(&pi->stack);
|
1068
1059
|
rb_protect(protect_parse, (VALUE)pi, &line);
|
1069
1060
|
if (Qundef == pi->stack.head->val && !empty_ok(&pi->options)) {
|
1070
|
-
if (No == pi->options.nilnil ||
|
1071
|
-
(CompatMode == pi->options.mode && 0 < pi->cur - pi->json)) {
|
1061
|
+
if (No == pi->options.nilnil || (CompatMode == pi->options.mode && 0 < pi->cur - pi->json)) {
|
1072
1062
|
oj_set_error_at(pi, oj_json_parser_error_class, __FILE__, __LINE__, "Empty input");
|
1073
1063
|
}
|
1074
1064
|
}
|
@@ -1096,9 +1086,7 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
|
|
1096
1086
|
switch (v->next) {
|
1097
1087
|
case NEXT_ARRAY_NEW:
|
1098
1088
|
case NEXT_ARRAY_ELEMENT:
|
1099
|
-
case NEXT_ARRAY_COMMA:
|
1100
|
-
oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Array not terminated");
|
1101
|
-
break;
|
1089
|
+
case NEXT_ARRAY_COMMA: oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Array not terminated"); break;
|
1102
1090
|
case NEXT_HASH_NEW:
|
1103
1091
|
case NEXT_HASH_KEY:
|
1104
1092
|
case NEXT_HASH_COLON:
|
@@ -1116,9 +1104,9 @@ CLEANUP:
|
|
1116
1104
|
oj_circ_array_free(pi->circ_array);
|
1117
1105
|
}
|
1118
1106
|
if (0 != buf) {
|
1119
|
-
|
1107
|
+
OJ_R_FREE(buf);
|
1120
1108
|
} else if (free_json) {
|
1121
|
-
|
1109
|
+
OJ_R_FREE(json);
|
1122
1110
|
}
|
1123
1111
|
stack_cleanup(&pi->stack);
|
1124
1112
|
if (pi->str_rx.head != oj_default_options.str_rx.head) {
|
@@ -1129,7 +1117,7 @@ CLEANUP:
|
|
1129
1117
|
if (Qnil != pi->err_class) {
|
1130
1118
|
pi->err.clas = pi->err_class;
|
1131
1119
|
}
|
1132
|
-
if (CompatMode == pi->options.mode && Yes != pi->options.safe) {
|
1120
|
+
if ((CompatMode == pi->options.mode || RailsMode == pi->options.mode) && Yes != pi->options.safe) {
|
1133
1121
|
// The json gem requires the error message be UTF-8 encoded. In
|
1134
1122
|
// additional the complete JSON source must be returned. There
|
1135
1123
|
// does not seem to be a size limit.
|