oj 3.13.11 → 3.13.23
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 +50 -0
- data/README.md +2 -0
- data/ext/oj/buf.h +4 -0
- data/ext/oj/circarray.c +1 -1
- data/ext/oj/code.c +15 -22
- data/ext/oj/compat.c +10 -10
- data/ext/oj/custom.c +62 -108
- data/ext/oj/dump.c +85 -97
- data/ext/oj/dump.h +12 -8
- data/ext/oj/dump_compat.c +46 -88
- data/ext/oj/dump_leaf.c +14 -58
- data/ext/oj/dump_object.c +33 -156
- data/ext/oj/dump_strict.c +17 -29
- data/ext/oj/extconf.rb +5 -4
- data/ext/oj/fast.c +24 -22
- data/ext/oj/intern.c +15 -11
- data/ext/oj/intern.h +1 -1
- data/ext/oj/mimic_json.c +44 -32
- data/ext/oj/object.c +42 -41
- data/ext/oj/odd.c +83 -63
- data/ext/oj/odd.h +13 -13
- data/ext/oj/oj.c +57 -22
- data/ext/oj/oj.h +24 -3
- data/ext/oj/parse.c +114 -78
- data/ext/oj/parse.h +2 -0
- data/ext/oj/parser.c +77 -21
- data/ext/oj/parser.h +12 -0
- data/ext/oj/rails.c +41 -65
- data/ext/oj/rails.h +1 -1
- data/ext/oj/reader.c +2 -0
- data/ext/oj/saj.c +11 -23
- data/ext/oj/saj2.c +333 -85
- data/ext/oj/saj2.h +23 -0
- data/ext/oj/sparse.c +4 -0
- data/ext/oj/stream_writer.c +3 -1
- data/ext/oj/strict.c +13 -13
- data/ext/oj/string_writer.c +12 -5
- data/ext/oj/usual.c +82 -129
- data/ext/oj/usual.h +68 -0
- data/ext/oj/val_stack.c +1 -1
- data/ext/oj/validate.c +21 -26
- data/ext/oj/wab.c +21 -26
- data/lib/oj/saj.rb +20 -6
- data/lib/oj/state.rb +1 -1
- data/lib/oj/version.rb +1 -1
- data/pages/Compatibility.md +1 -1
- data/pages/Options.md +6 -0
- data/test/activesupport7/abstract_unit.rb +49 -0
- data/test/activesupport7/decoding_test.rb +125 -0
- data/test/activesupport7/encoding_test.rb +486 -0
- data/test/activesupport7/encoding_test_cases.rb +104 -0
- data/test/activesupport7/time_zone_test_helpers.rb +47 -0
- data/test/bar.rb +3 -8
- data/test/foo.rb +3 -3
- data/test/helper.rb +8 -2
- data/test/json_gem/json_generator_test.rb +5 -4
- data/test/json_gem/json_parser_test.rb +8 -1
- data/test/json_gem/test_helper.rb +7 -3
- data/test/perf_dump.rb +50 -0
- data/test/test_compat.rb +25 -0
- data/test/test_custom.rb +13 -2
- data/test/test_file.rb +23 -7
- data/test/test_gc.rb +11 -0
- data/test/test_object.rb +8 -10
- data/test/test_parser.rb +3 -19
- data/test/test_parser_debug.rb +27 -0
- data/test/test_parser_saj.rb +92 -2
- data/test/test_scp.rb +2 -4
- data/test/test_strict.rb +2 -0
- data/test/test_various.rb +8 -3
- data/test/test_wab.rb +2 -0
- data/test/tests.rb +9 -0
- data/test/tests_mimic.rb +9 -0
- data/test/tests_mimic_addition.rb +9 -0
- metadata +13 -116
data/ext/oj/parse.c
CHANGED
@@ -183,6 +183,42 @@ 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, 3, string, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT);
|
204
|
+
if (r != 16) {
|
205
|
+
str = (char*)(str + r);
|
206
|
+
return str;
|
207
|
+
}
|
208
|
+
}
|
209
|
+
|
210
|
+
return scan_string_noSIMD(str, end);
|
211
|
+
}
|
212
|
+
#endif
|
213
|
+
|
214
|
+
static const char *(*scan_func) (const char *str, const char *end) = scan_string_noSIMD;
|
215
|
+
|
216
|
+
void oj_scanner_init(void) {
|
217
|
+
#ifdef OJ_USE_SSE4_2
|
218
|
+
scan_func = scan_string_SIMD;
|
219
|
+
#endif
|
220
|
+
}
|
221
|
+
|
186
222
|
// entered at /
|
187
223
|
static void read_escaped_str(ParseInfo pi, const char *start) {
|
188
224
|
struct _buf buf;
|
@@ -192,11 +228,11 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
192
228
|
Val parent = stack_peek(&pi->stack);
|
193
229
|
|
194
230
|
buf_init(&buf);
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
if (
|
231
|
+
buf_append_string(&buf, start, cnt);
|
232
|
+
|
233
|
+
for (s = pi->cur; '"' != *s;) {
|
234
|
+
const char *scanned = scan_func(s, pi->end);
|
235
|
+
if (scanned >= pi->end) {
|
200
236
|
oj_set_error_at(pi,
|
201
237
|
oj_parse_error_class,
|
202
238
|
__FILE__,
|
@@ -204,7 +240,12 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
204
240
|
"quoted string not terminated");
|
205
241
|
buf_cleanup(&buf);
|
206
242
|
return;
|
207
|
-
}
|
243
|
+
}
|
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;
|
@@ -273,8 +314,7 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
273
314
|
buf_cleanup(&buf);
|
274
315
|
return;
|
275
316
|
}
|
276
|
-
|
277
|
-
buf_append(&buf, *s);
|
317
|
+
s++;
|
278
318
|
}
|
279
319
|
}
|
280
320
|
if (0 == parent) {
|
@@ -331,22 +371,24 @@ static void read_str(ParseInfo pi) {
|
|
331
371
|
const char *str = pi->cur;
|
332
372
|
Val parent = stack_peek(&pi->stack);
|
333
373
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
} else if ('\0' == *pi->cur) {
|
343
|
-
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "NULL byte in string");
|
344
|
-
return;
|
345
|
-
} else if ('\\' == *pi->cur) {
|
346
|
-
read_escaped_str(pi, str);
|
347
|
-
return;
|
348
|
-
}
|
374
|
+
pi->cur = scan_func(pi->cur, pi->end);
|
375
|
+
if (RB_UNLIKELY(pi->end <= pi->cur)) {
|
376
|
+
oj_set_error_at(pi,
|
377
|
+
oj_parse_error_class,
|
378
|
+
__FILE__,
|
379
|
+
__LINE__,
|
380
|
+
"quoted string not terminated");
|
381
|
+
return;
|
349
382
|
}
|
383
|
+
if (RB_UNLIKELY('\0' == *pi->cur)) {
|
384
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "NULL byte in string");
|
385
|
+
return;
|
386
|
+
}
|
387
|
+
if ('\\' == *pi->cur) {
|
388
|
+
read_escaped_str(pi, str);
|
389
|
+
return;
|
390
|
+
}
|
391
|
+
|
350
392
|
if (0 == parent) { // simple add
|
351
393
|
pi->add_cstr(pi, str, pi->cur - str, str);
|
352
394
|
} else {
|
@@ -459,33 +501,31 @@ static void read_num(ParseInfo pi) {
|
|
459
501
|
int dec_cnt = 0;
|
460
502
|
bool zero1 = false;
|
461
503
|
|
504
|
+
// Skip leading zeros.
|
505
|
+
for (; '0' == *pi->cur; pi->cur++) {
|
506
|
+
zero1 = true;
|
507
|
+
}
|
508
|
+
|
462
509
|
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');
|
510
|
+
int d = (*pi->cur - '0');
|
471
511
|
|
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
|
-
}
|
512
|
+
if (RB_LIKELY(0 != ni.i)) {
|
513
|
+
dec_cnt++;
|
487
514
|
}
|
515
|
+
ni.i = ni.i * 10 + d;
|
488
516
|
}
|
517
|
+
if (RB_UNLIKELY(0 != ni.i && zero1 && CompatMode == pi->options.mode)) {
|
518
|
+
oj_set_error_at(pi,
|
519
|
+
oj_parse_error_class,
|
520
|
+
__FILE__,
|
521
|
+
__LINE__,
|
522
|
+
"not a number");
|
523
|
+
return;
|
524
|
+
}
|
525
|
+
if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
|
526
|
+
ni.big = true;
|
527
|
+
}
|
528
|
+
|
489
529
|
if ('.' == *pi->cur) {
|
490
530
|
pi->cur++;
|
491
531
|
// A trailing . is not a valid decimal but if encountered allow it
|
@@ -505,25 +545,20 @@ static void read_num(ParseInfo pi) {
|
|
505
545
|
for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
|
506
546
|
int d = (*pi->cur - '0');
|
507
547
|
|
508
|
-
if (0
|
548
|
+
if (RB_LIKELY(0 != ni.num || 0 != ni.i)) {
|
509
549
|
dec_cnt++;
|
510
550
|
}
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
|
520
|
-
if (!ni.no_big) {
|
521
|
-
ni.big = true;
|
522
|
-
}
|
523
|
-
}
|
524
|
-
}
|
551
|
+
ni.num = ni.num * 10 + d;
|
552
|
+
ni.div *= 10;
|
553
|
+
ni.di++;
|
554
|
+
}
|
555
|
+
}
|
556
|
+
if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
|
557
|
+
if (!ni.no_big) {
|
558
|
+
ni.big = true;
|
525
559
|
}
|
526
560
|
}
|
561
|
+
|
527
562
|
if ('e' == *pi->cur || 'E' == *pi->cur) {
|
528
563
|
int eneg = 0;
|
529
564
|
|
@@ -596,7 +631,7 @@ static void read_num(ParseInfo pi) {
|
|
596
631
|
}
|
597
632
|
|
598
633
|
static void array_start(ParseInfo pi) {
|
599
|
-
|
634
|
+
VALUE v = pi->start_array(pi);
|
600
635
|
|
601
636
|
stack_push(&pi->stack, v, NEXT_ARRAY_NEW);
|
602
637
|
}
|
@@ -620,13 +655,13 @@ static void array_end(ParseInfo pi) {
|
|
620
655
|
}
|
621
656
|
|
622
657
|
static void hash_start(ParseInfo pi) {
|
623
|
-
|
658
|
+
VALUE v = pi->start_hash(pi);
|
624
659
|
|
625
660
|
stack_push(&pi->stack, v, NEXT_HASH_NEW);
|
626
661
|
}
|
627
662
|
|
628
663
|
static void hash_end(ParseInfo pi) {
|
629
|
-
|
664
|
+
Val hash = stack_peek(&pi->stack);
|
630
665
|
|
631
666
|
// leave hash on stack until just before
|
632
667
|
if (0 == hash) {
|
@@ -813,7 +848,7 @@ static long double exp_plus[] = {
|
|
813
848
|
|
814
849
|
VALUE
|
815
850
|
oj_num_as_value(NumInfo ni) {
|
816
|
-
|
851
|
+
VALUE rnum = Qnil;
|
817
852
|
|
818
853
|
if (ni->infinity) {
|
819
854
|
if (ni->neg) {
|
@@ -848,7 +883,7 @@ oj_num_as_value(NumInfo ni) {
|
|
848
883
|
}
|
849
884
|
} else { // decimal
|
850
885
|
if (ni->big) {
|
851
|
-
|
886
|
+
VALUE bd = rb_str_new(ni->str, ni->len);
|
852
887
|
|
853
888
|
rnum = rb_rescue2(parse_big_decimal, bd, rescue_big_decimal, bd, rb_eException, 0);
|
854
889
|
if (ni->no_big) {
|
@@ -876,7 +911,7 @@ oj_num_as_value(NumInfo ni) {
|
|
876
911
|
}
|
877
912
|
rnum = rb_float_new((double)ld);
|
878
913
|
} else if (RubyDec == ni->bigdec_load) {
|
879
|
-
|
914
|
+
VALUE sv = rb_str_new(ni->str, ni->len);
|
880
915
|
|
881
916
|
rnum = rb_funcall(sv, rb_intern("to_f"), 0);
|
882
917
|
} else {
|
@@ -971,10 +1006,11 @@ static VALUE protect_parse(VALUE pip) {
|
|
971
1006
|
|
972
1007
|
extern int oj_utf8_index;
|
973
1008
|
|
974
|
-
static void oj_pi_set_input_str(ParseInfo pi,
|
975
|
-
|
1009
|
+
static void oj_pi_set_input_str(ParseInfo pi, VALUE *inputp) {
|
1010
|
+
int idx = RB_ENCODING_GET(*inputp);
|
976
1011
|
|
977
|
-
if (
|
1012
|
+
if (oj_utf8_encoding_index != idx) {
|
1013
|
+
rb_encoding *enc = rb_enc_from_index(idx);
|
978
1014
|
*inputp = rb_str_conv_enc(*inputp, enc, oj_utf8_encoding);
|
979
1015
|
}
|
980
1016
|
pi->json = RSTRING_PTR(*inputp);
|
@@ -983,12 +1019,12 @@ static void oj_pi_set_input_str(ParseInfo pi, volatile VALUE *inputp) {
|
|
983
1019
|
|
984
1020
|
VALUE
|
985
1021
|
oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yieldOk) {
|
986
|
-
char *
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
int
|
991
|
-
int
|
1022
|
+
char * buf = 0;
|
1023
|
+
VALUE input;
|
1024
|
+
VALUE wrapped_stack;
|
1025
|
+
VALUE result = Qnil;
|
1026
|
+
int line = 0;
|
1027
|
+
int free_json = 0;
|
992
1028
|
|
993
1029
|
if (argc < 1) {
|
994
1030
|
rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
|
@@ -1024,8 +1060,8 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
|
|
1024
1060
|
rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
|
1025
1061
|
}
|
1026
1062
|
} else {
|
1027
|
-
VALUE
|
1028
|
-
|
1063
|
+
VALUE clas = rb_obj_class(input);
|
1064
|
+
VALUE s;
|
1029
1065
|
|
1030
1066
|
if (oj_stringio_class == clas) {
|
1031
1067
|
s = rb_funcall2(input, oj_string_id, 0, 0);
|
data/ext/oj/parse.h
CHANGED
data/ext/oj/parser.c
CHANGED
@@ -11,8 +11,8 @@
|
|
11
11
|
#define USE_THREAD_LIMIT 0
|
12
12
|
// #define USE_THREAD_LIMIT 100000
|
13
13
|
#define MAX_EXP 4932
|
14
|
-
// max in the pow_map
|
15
|
-
#define MAX_POW
|
14
|
+
// max in the pow_map which is the limit for double
|
15
|
+
#define MAX_POW 308
|
16
16
|
|
17
17
|
#define MIN_SLEEP (1000000000LL / (double)CLOCKS_PER_SEC)
|
18
18
|
// 9,223,372,036,854,775,807
|
@@ -385,7 +385,7 @@ static const byte hex_map[256] = "\
|
|
385
385
|
................................\
|
386
386
|
................................";
|
387
387
|
|
388
|
-
static long double pow_map[
|
388
|
+
static long double pow_map[309] = {
|
389
389
|
1.0L, 1.0e1L, 1.0e2L, 1.0e3L, 1.0e4L, 1.0e5L, 1.0e6L, 1.0e7L, 1.0e8L, 1.0e9L, 1.0e10L,
|
390
390
|
1.0e11L, 1.0e12L, 1.0e13L, 1.0e14L, 1.0e15L, 1.0e16L, 1.0e17L, 1.0e18L, 1.0e19L, 1.0e20L, 1.0e21L,
|
391
391
|
1.0e22L, 1.0e23L, 1.0e24L, 1.0e25L, 1.0e26L, 1.0e27L, 1.0e28L, 1.0e29L, 1.0e30L, 1.0e31L, 1.0e32L,
|
@@ -414,15 +414,7 @@ static long double pow_map[401] = {
|
|
414
414
|
1.0e275L, 1.0e276L, 1.0e277L, 1.0e278L, 1.0e279L, 1.0e280L, 1.0e281L, 1.0e282L, 1.0e283L, 1.0e284L, 1.0e285L,
|
415
415
|
1.0e286L, 1.0e287L, 1.0e288L, 1.0e289L, 1.0e290L, 1.0e291L, 1.0e292L, 1.0e293L, 1.0e294L, 1.0e295L, 1.0e296L,
|
416
416
|
1.0e297L, 1.0e298L, 1.0e299L, 1.0e300L, 1.0e301L, 1.0e302L, 1.0e303L, 1.0e304L, 1.0e305L, 1.0e306L, 1.0e307L,
|
417
|
-
1.0e308L
|
418
|
-
1.0e319L, 1.0e320L, 1.0e321L, 1.0e322L, 1.0e323L, 1.0e324L, 1.0e325L, 1.0e326L, 1.0e327L, 1.0e328L, 1.0e329L,
|
419
|
-
1.0e330L, 1.0e331L, 1.0e332L, 1.0e333L, 1.0e334L, 1.0e335L, 1.0e336L, 1.0e337L, 1.0e338L, 1.0e339L, 1.0e340L,
|
420
|
-
1.0e341L, 1.0e342L, 1.0e343L, 1.0e344L, 1.0e345L, 1.0e346L, 1.0e347L, 1.0e348L, 1.0e349L, 1.0e350L, 1.0e351L,
|
421
|
-
1.0e352L, 1.0e353L, 1.0e354L, 1.0e355L, 1.0e356L, 1.0e357L, 1.0e358L, 1.0e359L, 1.0e360L, 1.0e361L, 1.0e362L,
|
422
|
-
1.0e363L, 1.0e364L, 1.0e365L, 1.0e366L, 1.0e367L, 1.0e368L, 1.0e369L, 1.0e370L, 1.0e371L, 1.0e372L, 1.0e373L,
|
423
|
-
1.0e374L, 1.0e375L, 1.0e376L, 1.0e377L, 1.0e378L, 1.0e379L, 1.0e380L, 1.0e381L, 1.0e382L, 1.0e383L, 1.0e384L,
|
424
|
-
1.0e385L, 1.0e386L, 1.0e387L, 1.0e388L, 1.0e389L, 1.0e390L, 1.0e391L, 1.0e392L, 1.0e393L, 1.0e394L, 1.0e395L,
|
425
|
-
1.0e396L, 1.0e397L, 1.0e398L, 1.0e399L, 1.0e400L};
|
417
|
+
1.0e308L};
|
426
418
|
|
427
419
|
static VALUE parser_class;
|
428
420
|
|
@@ -541,6 +533,7 @@ static void calc_num(ojParser p) {
|
|
541
533
|
// nothing to do
|
542
534
|
break;
|
543
535
|
}
|
536
|
+
p->type = OJ_NONE;
|
544
537
|
}
|
545
538
|
|
546
539
|
static void big_change(ojParser p) {
|
@@ -606,6 +599,8 @@ static void parse(ojParser p, const byte *json) {
|
|
606
599
|
const byte *b = json;
|
607
600
|
int i;
|
608
601
|
|
602
|
+
p->line = 1;
|
603
|
+
p->col = -1;
|
609
604
|
#if DEBUG
|
610
605
|
printf("*** parse - mode: %c %s\n", p->map[256], (const char *)json);
|
611
606
|
#endif
|
@@ -660,6 +655,7 @@ static void parse(ojParser p, const byte *json) {
|
|
660
655
|
}
|
661
656
|
buf_append_string(&p->buf, (const char *)start, b - start);
|
662
657
|
if ('"' == *b) {
|
658
|
+
p->cur = b - json;
|
663
659
|
p->funcs[p->stack[p->depth]].add_str(p);
|
664
660
|
p->map = (0 == p->depth) ? value_map : after_map;
|
665
661
|
break;
|
@@ -669,12 +665,14 @@ static void parse(ojParser p, const byte *json) {
|
|
669
665
|
p->next_map = (0 == p->depth) ? value_map : after_map;
|
670
666
|
break;
|
671
667
|
case OPEN_OBJECT:
|
668
|
+
p->cur = b - json;
|
672
669
|
p->funcs[p->stack[p->depth]].open_object(p);
|
673
670
|
p->depth++;
|
674
671
|
p->stack[p->depth] = OBJECT_FUN;
|
675
672
|
p->map = key1_map;
|
676
673
|
break;
|
677
674
|
case NUM_CLOSE_OBJECT:
|
675
|
+
p->cur = b - json;
|
678
676
|
calc_num(p);
|
679
677
|
// flow through
|
680
678
|
case CLOSE_OBJECT:
|
@@ -685,15 +683,18 @@ static void parse(ojParser p, const byte *json) {
|
|
685
683
|
return;
|
686
684
|
}
|
687
685
|
p->depth--;
|
686
|
+
p->cur = b - json;
|
688
687
|
p->funcs[p->stack[p->depth]].close_object(p);
|
689
688
|
break;
|
690
689
|
case OPEN_ARRAY:
|
690
|
+
p->cur = b - json;
|
691
691
|
p->funcs[p->stack[p->depth]].open_array(p);
|
692
692
|
p->depth++;
|
693
693
|
p->stack[p->depth] = ARRAY_FUN;
|
694
694
|
p->map = value_map;
|
695
695
|
break;
|
696
696
|
case NUM_CLOSE_ARRAY:
|
697
|
+
p->cur = b - json;
|
697
698
|
calc_num(p);
|
698
699
|
// flow through
|
699
700
|
case CLOSE_ARRAY:
|
@@ -704,9 +705,11 @@ static void parse(ojParser p, const byte *json) {
|
|
704
705
|
return;
|
705
706
|
}
|
706
707
|
p->depth--;
|
708
|
+
p->cur = b - json;
|
707
709
|
p->funcs[p->stack[p->depth]].close_array(p);
|
708
710
|
break;
|
709
711
|
case NUM_COMMA:
|
712
|
+
p->cur = b - json;
|
710
713
|
calc_num(p);
|
711
714
|
if (0 < p->depth && OBJECT_FUN == p->stack[p->depth]) {
|
712
715
|
p->map = key_map;
|
@@ -868,8 +871,14 @@ static void parse(ojParser p, const byte *json) {
|
|
868
871
|
b--;
|
869
872
|
p->map = big_exp_map;
|
870
873
|
break;
|
871
|
-
case NUM_SPC:
|
872
|
-
|
874
|
+
case NUM_SPC:
|
875
|
+
p->cur = b - json;
|
876
|
+
calc_num(p);
|
877
|
+
break;
|
878
|
+
case NUM_NEWLINE:
|
879
|
+
p->cur = b - json;
|
880
|
+
calc_num(p);
|
881
|
+
b++;
|
873
882
|
#ifdef SPACE_JUMP
|
874
883
|
// for (uint32_t *sj = (uint32_t*)b; 0x20202020 == *sj; sj++) { b += 4; }
|
875
884
|
for (uint16_t *sj = (uint16_t *)b; 0x2020 == *sj; sj++) {
|
@@ -890,6 +899,7 @@ static void parse(ojParser p, const byte *json) {
|
|
890
899
|
buf_append_string(&p->buf, (const char *)start, b - start);
|
891
900
|
}
|
892
901
|
if ('"' == *b) {
|
902
|
+
p->cur = b - json;
|
893
903
|
p->funcs[p->stack[p->depth]].add_str(p);
|
894
904
|
p->map = p->next_map;
|
895
905
|
break;
|
@@ -898,6 +908,7 @@ static void parse(ojParser p, const byte *json) {
|
|
898
908
|
break;
|
899
909
|
case STR_SLASH: p->map = esc_map; break;
|
900
910
|
case STR_QUOTE:
|
911
|
+
p->cur = b - json;
|
901
912
|
p->funcs[p->stack[p->depth]].add_str(p);
|
902
913
|
p->map = p->next_map;
|
903
914
|
break;
|
@@ -975,6 +986,7 @@ static void parse(ojParser p, const byte *json) {
|
|
975
986
|
case VAL_NULL:
|
976
987
|
if ('u' == b[1] && 'l' == b[2] && 'l' == b[3]) {
|
977
988
|
b += 3;
|
989
|
+
p->cur = b - json;
|
978
990
|
p->funcs[p->stack[p->depth]].add_null(p);
|
979
991
|
p->map = (0 == p->depth) ? value_map : after_map;
|
980
992
|
break;
|
@@ -1000,6 +1012,7 @@ static void parse(ojParser p, const byte *json) {
|
|
1000
1012
|
case VAL_TRUE:
|
1001
1013
|
if ('r' == b[1] && 'u' == b[2] && 'e' == b[3]) {
|
1002
1014
|
b += 3;
|
1015
|
+
p->cur = b - json;
|
1003
1016
|
p->funcs[p->stack[p->depth]].add_true(p);
|
1004
1017
|
p->map = (0 == p->depth) ? value_map : after_map;
|
1005
1018
|
break;
|
@@ -1025,6 +1038,7 @@ static void parse(ojParser p, const byte *json) {
|
|
1025
1038
|
case VAL_FALSE:
|
1026
1039
|
if ('a' == b[1] && 'l' == b[2] && 's' == b[3] && 'e' == b[4]) {
|
1027
1040
|
b += 4;
|
1041
|
+
p->cur = b - json;
|
1028
1042
|
p->funcs[p->stack[p->depth]].add_false(p);
|
1029
1043
|
p->map = (0 == p->depth) ? value_map : after_map;
|
1030
1044
|
break;
|
@@ -1058,6 +1072,7 @@ static void parse(ojParser p, const byte *json) {
|
|
1058
1072
|
parse_error(p, "expected null");
|
1059
1073
|
return;
|
1060
1074
|
}
|
1075
|
+
p->cur = b - json;
|
1061
1076
|
p->funcs[p->stack[p->depth]].add_null(p);
|
1062
1077
|
p->map = (0 == p->depth) ? value_map : after_map;
|
1063
1078
|
}
|
@@ -1069,6 +1084,7 @@ static void parse(ojParser p, const byte *json) {
|
|
1069
1084
|
parse_error(p, "expected false");
|
1070
1085
|
return;
|
1071
1086
|
}
|
1087
|
+
p->cur = b - json;
|
1072
1088
|
p->funcs[p->stack[p->depth]].add_false(p);
|
1073
1089
|
p->map = (0 == p->depth) ? value_map : after_map;
|
1074
1090
|
}
|
@@ -1080,6 +1096,7 @@ static void parse(ojParser p, const byte *json) {
|
|
1080
1096
|
parse_error(p, "expected true");
|
1081
1097
|
return;
|
1082
1098
|
}
|
1099
|
+
p->cur = b - json;
|
1083
1100
|
p->funcs[p->stack[p->depth]].add_true(p);
|
1084
1101
|
p->map = (0 == p->depth) ? value_map : after_map;
|
1085
1102
|
}
|
@@ -1097,6 +1114,9 @@ static void parse(ojParser p, const byte *json) {
|
|
1097
1114
|
p->map = trail_map;
|
1098
1115
|
}
|
1099
1116
|
}
|
1117
|
+
if (0 < p->depth) {
|
1118
|
+
parse_error(p, "parse error, not closed");
|
1119
|
+
}
|
1100
1120
|
if (0 == p->depth) {
|
1101
1121
|
switch (p->map[256]) {
|
1102
1122
|
case '0':
|
@@ -1107,7 +1127,10 @@ static void parse(ojParser p, const byte *json) {
|
|
1107
1127
|
case 'D':
|
1108
1128
|
case 'g':
|
1109
1129
|
case 'B':
|
1110
|
-
case 'Y':
|
1130
|
+
case 'Y':
|
1131
|
+
p->cur = b - json;
|
1132
|
+
calc_num(p);
|
1133
|
+
break;
|
1111
1134
|
}
|
1112
1135
|
}
|
1113
1136
|
return;
|
@@ -1122,7 +1145,9 @@ static void parser_free(void *ptr) {
|
|
1122
1145
|
p = (ojParser)ptr;
|
1123
1146
|
buf_cleanup(&p->key);
|
1124
1147
|
buf_cleanup(&p->buf);
|
1125
|
-
p->free
|
1148
|
+
if (NULL != p->free) {
|
1149
|
+
p->free(p);
|
1150
|
+
}
|
1126
1151
|
xfree(ptr);
|
1127
1152
|
}
|
1128
1153
|
|
@@ -1133,7 +1158,9 @@ static void parser_mark(void *ptr) {
|
|
1133
1158
|
if (0 != p->reader) {
|
1134
1159
|
rb_gc_mark(p->reader);
|
1135
1160
|
}
|
1136
|
-
p->mark
|
1161
|
+
if (NULL != p->mark) {
|
1162
|
+
p->mark(p);
|
1163
|
+
}
|
1137
1164
|
}
|
1138
1165
|
}
|
1139
1166
|
|
@@ -1190,7 +1217,7 @@ static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
|
|
1190
1217
|
p->map = value_map;
|
1191
1218
|
|
1192
1219
|
if (argc < 1) {
|
1193
|
-
|
1220
|
+
oj_set_parser_validator(p);
|
1194
1221
|
} else {
|
1195
1222
|
VALUE mode = argv[0];
|
1196
1223
|
|
@@ -1231,6 +1258,32 @@ static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
|
|
1231
1258
|
return Data_Wrap_Struct(parser_class, parser_mark, parser_free, p);
|
1232
1259
|
}
|
1233
1260
|
|
1261
|
+
// Create a new parser without setting the delegate. The parser is
|
1262
|
+
// wrapped. The parser is (ojParser)DATA_PTR(value) where value is the return
|
1263
|
+
// from this function. A delegate must be added before the parser can be
|
1264
|
+
// used. Optionally oj_parser_set_options can be called if the options are not
|
1265
|
+
// set directly.
|
1266
|
+
VALUE oj_parser_new() {
|
1267
|
+
ojParser p = ALLOC(struct _ojParser);
|
1268
|
+
|
1269
|
+
#if HAVE_RB_EXT_RACTOR_SAFE
|
1270
|
+
// This doesn't seem to do anything.
|
1271
|
+
rb_ext_ractor_safe(true);
|
1272
|
+
#endif
|
1273
|
+
memset(p, 0, sizeof(struct _ojParser));
|
1274
|
+
buf_init(&p->key);
|
1275
|
+
buf_init(&p->buf);
|
1276
|
+
p->map = value_map;
|
1277
|
+
|
1278
|
+
return Data_Wrap_Struct(parser_class, parser_mark, parser_free, p);
|
1279
|
+
}
|
1280
|
+
|
1281
|
+
// Set set the options from a hash (ropts).
|
1282
|
+
void oj_parser_set_option(ojParser p, VALUE ropts) {
|
1283
|
+
Check_Type(ropts, T_HASH);
|
1284
|
+
rb_hash_foreach(ropts, opt_cb, (VALUE)p);
|
1285
|
+
}
|
1286
|
+
|
1234
1287
|
/* Document-method: method_missing(value)
|
1235
1288
|
* call-seq: method_missing(value)
|
1236
1289
|
*
|
@@ -1284,7 +1337,7 @@ static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
|
|
1284
1337
|
*/
|
1285
1338
|
static VALUE parser_missing(int argc, VALUE *argv, VALUE self) {
|
1286
1339
|
ojParser p = (ojParser)DATA_PTR(self);
|
1287
|
-
const char
|
1340
|
+
const char *key = NULL;
|
1288
1341
|
volatile VALUE rkey = *argv;
|
1289
1342
|
volatile VALUE rv = Qnil;
|
1290
1343
|
|
@@ -1464,7 +1517,7 @@ static VALUE saj_parser = Qundef;
|
|
1464
1517
|
/* Document-method: saj
|
1465
1518
|
* call-seq: saj
|
1466
1519
|
*
|
1467
|
-
* Returns the default
|
1520
|
+
* Returns the default SAJ parser. Note the default SAJ parser can not be used
|
1468
1521
|
* concurrently in more than one thread.
|
1469
1522
|
*/
|
1470
1523
|
static VALUE parser_saj(VALUE self) {
|
@@ -1515,8 +1568,11 @@ static VALUE parser_validate(VALUE self) {
|
|
1515
1568
|
* isolates options to just the parser so that other parts of the code are not
|
1516
1569
|
* forced to use the same options.
|
1517
1570
|
*/
|
1518
|
-
void oj_parser_init() {
|
1571
|
+
void oj_parser_init(void) {
|
1519
1572
|
parser_class = rb_define_class_under(Oj, "Parser", rb_cObject);
|
1573
|
+
rb_gc_register_address(&parser_class);
|
1574
|
+
rb_undef_alloc_func(parser_class);
|
1575
|
+
|
1520
1576
|
rb_define_module_function(parser_class, "new", parser_new, -1);
|
1521
1577
|
rb_define_method(parser_class, "parse", parser_parse, 1);
|
1522
1578
|
rb_define_method(parser_class, "load", parser_load, 1);
|
data/ext/oj/parser.h
CHANGED
@@ -80,6 +80,7 @@ typedef struct _ojParser {
|
|
80
80
|
|
81
81
|
char token[8];
|
82
82
|
long line;
|
83
|
+
long cur; // only set before call to a function
|
83
84
|
long col;
|
84
85
|
int ri;
|
85
86
|
uint32_t ucode;
|
@@ -87,4 +88,15 @@ typedef struct _ojParser {
|
|
87
88
|
bool just_one;
|
88
89
|
} * ojParser;
|
89
90
|
|
91
|
+
// Create a new parser without setting the delegate. The parser is
|
92
|
+
// wrapped. The parser is (ojParser)DATA_PTR(value) where value is the return
|
93
|
+
// from this function. A delegate must be added before the parser can be
|
94
|
+
// used. Optionally oj_parser_set_options can be called if the options are not
|
95
|
+
// set directly.
|
96
|
+
extern VALUE oj_parser_new();
|
97
|
+
|
98
|
+
// Set set the options from a hash (ropts).
|
99
|
+
extern void oj_parser_set_option(ojParser p, VALUE ropts);
|
100
|
+
|
101
|
+
|
90
102
|
#endif /* OJ_PARSER_H */
|