oj 3.11.5 → 3.16.5
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|