oj 3.13.11 → 3.13.23

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -0
  3. data/README.md +2 -0
  4. data/ext/oj/buf.h +4 -0
  5. data/ext/oj/circarray.c +1 -1
  6. data/ext/oj/code.c +15 -22
  7. data/ext/oj/compat.c +10 -10
  8. data/ext/oj/custom.c +62 -108
  9. data/ext/oj/dump.c +85 -97
  10. data/ext/oj/dump.h +12 -8
  11. data/ext/oj/dump_compat.c +46 -88
  12. data/ext/oj/dump_leaf.c +14 -58
  13. data/ext/oj/dump_object.c +33 -156
  14. data/ext/oj/dump_strict.c +17 -29
  15. data/ext/oj/extconf.rb +5 -4
  16. data/ext/oj/fast.c +24 -22
  17. data/ext/oj/intern.c +15 -11
  18. data/ext/oj/intern.h +1 -1
  19. data/ext/oj/mimic_json.c +44 -32
  20. data/ext/oj/object.c +42 -41
  21. data/ext/oj/odd.c +83 -63
  22. data/ext/oj/odd.h +13 -13
  23. data/ext/oj/oj.c +57 -22
  24. data/ext/oj/oj.h +24 -3
  25. data/ext/oj/parse.c +114 -78
  26. data/ext/oj/parse.h +2 -0
  27. data/ext/oj/parser.c +77 -21
  28. data/ext/oj/parser.h +12 -0
  29. data/ext/oj/rails.c +41 -65
  30. data/ext/oj/rails.h +1 -1
  31. data/ext/oj/reader.c +2 -0
  32. data/ext/oj/saj.c +11 -23
  33. data/ext/oj/saj2.c +333 -85
  34. data/ext/oj/saj2.h +23 -0
  35. data/ext/oj/sparse.c +4 -0
  36. data/ext/oj/stream_writer.c +3 -1
  37. data/ext/oj/strict.c +13 -13
  38. data/ext/oj/string_writer.c +12 -5
  39. data/ext/oj/usual.c +82 -129
  40. data/ext/oj/usual.h +68 -0
  41. data/ext/oj/val_stack.c +1 -1
  42. data/ext/oj/validate.c +21 -26
  43. data/ext/oj/wab.c +21 -26
  44. data/lib/oj/saj.rb +20 -6
  45. data/lib/oj/state.rb +1 -1
  46. data/lib/oj/version.rb +1 -1
  47. data/pages/Compatibility.md +1 -1
  48. data/pages/Options.md +6 -0
  49. data/test/activesupport7/abstract_unit.rb +49 -0
  50. data/test/activesupport7/decoding_test.rb +125 -0
  51. data/test/activesupport7/encoding_test.rb +486 -0
  52. data/test/activesupport7/encoding_test_cases.rb +104 -0
  53. data/test/activesupport7/time_zone_test_helpers.rb +47 -0
  54. data/test/bar.rb +3 -8
  55. data/test/foo.rb +3 -3
  56. data/test/helper.rb +8 -2
  57. data/test/json_gem/json_generator_test.rb +5 -4
  58. data/test/json_gem/json_parser_test.rb +8 -1
  59. data/test/json_gem/test_helper.rb +7 -3
  60. data/test/perf_dump.rb +50 -0
  61. data/test/test_compat.rb +25 -0
  62. data/test/test_custom.rb +13 -2
  63. data/test/test_file.rb +23 -7
  64. data/test/test_gc.rb +11 -0
  65. data/test/test_object.rb +8 -10
  66. data/test/test_parser.rb +3 -19
  67. data/test/test_parser_debug.rb +27 -0
  68. data/test/test_parser_saj.rb +92 -2
  69. data/test/test_scp.rb +2 -4
  70. data/test/test_strict.rb +2 -0
  71. data/test/test_various.rb +8 -3
  72. data/test/test_wab.rb +2 -0
  73. data/test/tests.rb +9 -0
  74. data/test/tests_mimic.rb +9 -0
  75. data/test/tests_mimic_addition.rb +9 -0
  76. 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
- if (0 < cnt) {
196
- buf_append_string(&buf, start, cnt);
197
- }
198
- for (s = pi->cur; '"' != *s; s++) {
199
- if (s >= pi->end) {
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
- } else if ('\\' == *s) {
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
- } else {
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
- for (; '"' != *pi->cur; pi->cur++) {
335
- if (pi->end <= pi->cur) {
336
- oj_set_error_at(pi,
337
- oj_parse_error_class,
338
- __FILE__,
339
- __LINE__,
340
- "quoted string not terminated");
341
- return;
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
- if (0 == ni.i && '0' == *pi->cur) {
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
- if (0 < d) {
473
- if (zero1 && CompatMode == pi->options.mode) {
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 < ni.num || 0 < ni.i) {
548
+ if (RB_LIKELY(0 != ni.num || 0 != ni.i)) {
509
549
  dec_cnt++;
510
550
  }
511
- if (INT64_MAX <= ni.div) {
512
- if (!ni.no_big) {
513
- ni.big = true;
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
- }
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
- volatile VALUE v = pi->start_array(pi);
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
- volatile VALUE v = pi->start_hash(pi);
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
- volatile Val hash = stack_peek(&pi->stack);
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
- volatile VALUE rnum = Qnil;
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
- volatile VALUE bd = rb_str_new(ni->str, ni->len);
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
- volatile VALUE sv = rb_str_new(ni->str, ni->len);
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, volatile VALUE *inputp) {
975
- rb_encoding *enc = rb_enc_get(*inputp);
1009
+ static void oj_pi_set_input_str(ParseInfo pi, VALUE *inputp) {
1010
+ int idx = RB_ENCODING_GET(*inputp);
976
1011
 
977
- if (oj_utf8_encoding != enc) {
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 * buf = 0;
987
- volatile VALUE input;
988
- volatile VALUE wrapped_stack;
989
- volatile VALUE result = Qnil;
990
- int line = 0;
991
- int free_json = 0;
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 clas = rb_obj_class(input);
1028
- volatile VALUE s;
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
@@ -97,6 +97,8 @@ static inline void parse_info_init(ParseInfo pi) {
97
97
  memset(pi, 0, sizeof(struct _parseInfo));
98
98
  }
99
99
 
100
+ extern void oj_scanner_init(void);
101
+
100
102
  static inline bool empty_ok(Options options) {
101
103
  switch (options->mode) {
102
104
  case ObjectMode:
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 400
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[401] = {
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, 1.0e309L, 1.0e310L, 1.0e311L, 1.0e312L, 1.0e313L, 1.0e314L, 1.0e315L, 1.0e316L, 1.0e317L, 1.0e318L,
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: calc_num(p); break;
872
- case NUM_NEWLINE: calc_num(p); b++;
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': calc_num(p); break;
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(p);
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(p);
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
- oj_set_parser_validator(p);
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 * key = NULL;
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 saj parser. Note the default SAJ parser can not be used
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 */