oj 3.13.16 → 3.13.19

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d07b4081b5dda88146594adba591542d11106f1c1eadcf7c56e38358d0ee0af3
4
- data.tar.gz: 6bdc82c3812f45a8ae9d72ec0b2f43ee223d95ac250399fc8dae8daf34cd3b49
3
+ metadata.gz: bfbe0dd2157d1aba97ad11d82eee1870fbb64f4f197ae75b17a2e781c64144f7
4
+ data.tar.gz: 907ff02298adc4555834ca1c1c6078a3586abfde85f790b4dcba284c5be60248
5
5
  SHA512:
6
- metadata.gz: 4358aa15b7956a5b577163e43fbdc0512212a218124878ceedc2932483f27acabed2d2b92eb888c8e54601f4f4aaf3dce3b959a36effbea989e7e7ea8956d1c1
7
- data.tar.gz: 1e1710bce62aadfbcc1196357d2e692892b75cc18c982fb6f53c2e35782d9bd2256cb3b6ab98951691430f23e8c2d8db26987a95dcba29f87b472729613b8d2d
6
+ metadata.gz: c56828991af620bb1436db19b5660ec588fce4858b455bd2b6c7f11f2bccaf3a7afca8fe98b62ad868a1a18ab584d38220e1b5c4c1243d3a8c1201c3b0c51a54
7
+ data.tar.gz: f687f4bd9a0a64ecd75cee755722cc2f4f80fc356fe884fd34bf5cf68066d2e648050de7a49c93c121740e790afbcc168b1a8efc5f8280f64ca9bf3d23bc736b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.13.19 - 2022-07-29
4
+
5
+ - TruffleRuby issues resolved.
6
+
7
+ ## 3.13.18 - 2022-07-25
8
+
9
+ - Fixed SSE detection at run time.
10
+
11
+ ## 3.13.17 - 2022-07-15
12
+
13
+ - Fixed Oj::Parser to detect unterminated arrays and objects.
14
+
3
15
  ## 3.13.16 - 2022-07-06
4
16
 
5
17
  - Added line and column as optional arguments to the Oj::Parser.saj parser.
data/ext/oj/buf.h CHANGED
@@ -39,6 +39,10 @@ inline static const char *buf_str(Buf buf) {
39
39
  }
40
40
 
41
41
  inline static void buf_append_string(Buf buf, const char *s, size_t slen) {
42
+ if (0 == slen) {
43
+ return;
44
+ }
45
+
42
46
  if (buf->end <= buf->tail + slen) {
43
47
  size_t len = buf->end - buf->head;
44
48
  size_t toff = buf->tail - buf->head;
data/ext/oj/custom.c CHANGED
@@ -484,7 +484,7 @@ static VALUE dump_common(VALUE obj, int depth, Out out) {
484
484
  const char * s;
485
485
  int len;
486
486
 
487
- if (Yes == out->opts->trace) {
487
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
488
488
  oj_trace("to_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
489
489
  }
490
490
  if (0 == rb_obj_method_arity(obj, oj_to_json_id)) {
@@ -492,7 +492,7 @@ static VALUE dump_common(VALUE obj, int depth, Out out) {
492
492
  } else {
493
493
  rs = rb_funcall2(obj, oj_to_json_id, out->argc, out->argv);
494
494
  }
495
- if (Yes == out->opts->trace) {
495
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
496
496
  oj_trace("to_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
497
497
  }
498
498
  s = RSTRING_PTR(rs);
@@ -504,7 +504,7 @@ static VALUE dump_common(VALUE obj, int depth, Out out) {
504
504
  } else if (Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
505
505
  volatile VALUE aj;
506
506
 
507
- if (Yes == out->opts->trace) {
507
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
508
508
  oj_trace("as_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
509
509
  }
510
510
  // Some classes elect to not take an options argument so check the arity
@@ -514,7 +514,7 @@ static VALUE dump_common(VALUE obj, int depth, Out out) {
514
514
  } else {
515
515
  aj = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
516
516
  }
517
- if (Yes == out->opts->trace) {
517
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
518
518
  oj_trace("as_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
519
519
  }
520
520
  // Catch the obvious brain damaged recursive dumping.
@@ -885,7 +885,7 @@ static DumpFunc custom_funcs[] = {
885
885
  void oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok) {
886
886
  int type = rb_type(obj);
887
887
 
888
- if (Yes == out->opts->trace) {
888
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
889
889
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
890
890
  }
891
891
  if (MAX_DEPTH < depth) {
@@ -896,14 +896,14 @@ void oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok) {
896
896
 
897
897
  if (NULL != f) {
898
898
  f(obj, depth, out, true);
899
- if (Yes == out->opts->trace) {
899
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
900
900
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
901
901
  }
902
902
  return;
903
903
  }
904
904
  }
905
905
  oj_dump_nil(Qnil, depth, out, false);
906
- if (Yes == out->opts->trace) {
906
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
907
907
  oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
908
908
  }
909
909
  }
data/ext/oj/dump.c CHANGED
@@ -736,11 +736,11 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
736
736
  } else {
737
737
  volatile VALUE jv;
738
738
 
739
- if (Yes == out->opts->trace) {
739
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
740
740
  oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
741
741
  }
742
742
  jv = rb_funcall(obj, oj_raw_json_id, 2, RB_INT2NUM(depth), RB_INT2NUM(out->indent));
743
- if (Yes == out->opts->trace) {
743
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
744
744
  oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
745
745
  }
746
746
  oj_dump_raw(RSTRING_PTR(jv), (size_t)RSTRING_LEN(jv), out);
data/ext/oj/dump_compat.c CHANGED
@@ -109,7 +109,7 @@ dump_to_json(VALUE obj, Out out) {
109
109
  const char *s;
110
110
  int len;
111
111
 
112
- if (Yes == out->opts->trace) {
112
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
113
113
  oj_trace("to_json", obj, __FILE__, __LINE__, 0, TraceRubyIn);
114
114
  }
115
115
  if (0 == rb_obj_method_arity(obj, oj_to_json_id)) {
@@ -117,7 +117,7 @@ dump_to_json(VALUE obj, Out out) {
117
117
  } else {
118
118
  rs = rb_funcall2(obj, oj_to_json_id, out->argc, out->argv);
119
119
  }
120
- if (Yes == out->opts->trace) {
120
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
121
121
  oj_trace("to_json", obj, __FILE__, __LINE__, 0, TraceRubyOut);
122
122
  }
123
123
 
@@ -893,7 +893,7 @@ void
893
893
  oj_dump_compat_val(VALUE obj, int depth, Out out, bool as_ok) {
894
894
  int type = rb_type(obj);
895
895
 
896
- if (Yes == out->opts->trace) {
896
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
897
897
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
898
898
  }
899
899
  if (out->opts->dump_opts.max_depth <= depth) {
@@ -918,14 +918,14 @@ oj_dump_compat_val(VALUE obj, int depth, Out out, bool as_ok) {
918
918
 
919
919
  if (NULL != f) {
920
920
  f(obj, depth, out, as_ok);
921
- if (Yes == out->opts->trace) {
921
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
922
922
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
923
923
  }
924
924
  return;
925
925
  }
926
926
  }
927
927
  oj_dump_nil(Qnil, depth, out, false);
928
- if (Yes == out->opts->trace) {
928
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
929
929
  oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
930
930
  }
931
931
  }
data/ext/oj/dump_object.c CHANGED
@@ -682,7 +682,7 @@ static DumpFunc obj_funcs[] = {
682
682
  void oj_dump_obj_val(VALUE obj, int depth, Out out) {
683
683
  int type = rb_type(obj);
684
684
 
685
- if (Yes == out->opts->trace) {
685
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
686
686
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
687
687
  }
688
688
  if (MAX_DEPTH < depth) {
@@ -693,14 +693,14 @@ void oj_dump_obj_val(VALUE obj, int depth, Out out) {
693
693
 
694
694
  if (NULL != f) {
695
695
  f(obj, depth, out, false);
696
- if (Yes == out->opts->trace) {
696
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
697
697
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
698
698
  }
699
699
  return;
700
700
  }
701
701
  }
702
702
  oj_dump_nil(Qnil, depth, out, false);
703
- if (Yes == out->opts->trace) {
703
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
704
704
  oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
705
705
  }
706
706
  }
data/ext/oj/dump_strict.c CHANGED
@@ -338,7 +338,7 @@ static DumpFunc strict_funcs[] = {
338
338
  void oj_dump_strict_val(VALUE obj, int depth, Out out) {
339
339
  int type = rb_type(obj);
340
340
 
341
- if (Yes == out->opts->trace) {
341
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
342
342
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
343
343
  }
344
344
  if (MAX_DEPTH < depth) {
@@ -349,7 +349,7 @@ void oj_dump_strict_val(VALUE obj, int depth, Out out) {
349
349
 
350
350
  if (NULL != f) {
351
351
  f(obj, depth, out, false);
352
- if (Yes == out->opts->trace) {
352
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
353
353
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
354
354
  }
355
355
  return;
@@ -386,7 +386,7 @@ static DumpFunc null_funcs[] = {
386
386
  void oj_dump_null_val(VALUE obj, int depth, Out out) {
387
387
  int type = rb_type(obj);
388
388
 
389
- if (Yes == out->opts->trace) {
389
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
390
390
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
391
391
  }
392
392
  if (MAX_DEPTH < depth) {
@@ -397,14 +397,14 @@ void oj_dump_null_val(VALUE obj, int depth, Out out) {
397
397
 
398
398
  if (NULL != f) {
399
399
  f(obj, depth, out, false);
400
- if (Yes == out->opts->trace) {
400
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
401
401
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
402
402
  }
403
403
  return;
404
404
  }
405
405
  }
406
406
  oj_dump_nil(Qnil, depth, out, false);
407
- if (Yes == out->opts->trace) {
407
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
408
408
  oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
409
409
  }
410
410
  }
data/ext/oj/extconf.rb CHANGED
@@ -34,9 +34,21 @@ have_func('rb_hash_bulk_insert', 'ruby.h') unless '2' == version[0] && '6' == ve
34
34
 
35
35
  dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
36
36
 
37
- if try_cflags('-msse4.2')
37
+ src =<<~SRC
38
+ #include <string.h>
39
+ #include <nmmintrin.h>
40
+ int main(int argc, char **argv) {
41
+ const char *str = "hello ";
42
+ const char chars[16] = "\x00\\\"";
43
+ const __m128i terminate = _mm_loadu_si128((const __m128i *)&chars[0]);
44
+ const __m128i string = _mm_loadu_si128((const __m128i *)str);
45
+ int r = _mm_cmpestri(terminate, 3, string, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT);
46
+ return r == 16 ? 0 : 1;
47
+ }
48
+ SRC
49
+
50
+ if try_run(src, '-msse4.2')
38
51
  $CPPFLAGS += ' -msse4.2'
39
- dflags['OJ_USE_SSE4_2'] = 1
40
52
  end
41
53
 
42
54
  dflags.each do |k,v|
data/ext/oj/oj.c CHANGED
@@ -2043,4 +2043,5 @@ void Init_oj(void) {
2043
2043
  oj_init_doc();
2044
2044
 
2045
2045
  oj_parser_init();
2046
+ oj_scanner_init();
2046
2047
  }
data/ext/oj/parse.c CHANGED
@@ -183,6 +183,78 @@ 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
+ // Taken from Tensorflow:
196
+ // https://github.com/tensorflow/tensorflow/blob/5dcfc51118817f27fad5246812d83e5dccdc5f72/tensorflow/core/lib/hash/crc32c_accelerate.cc#L21-L38
197
+ #ifdef __SSE4_2__
198
+ #if defined(__x86_64__) && defined(__GNUC__) && \
199
+ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
200
+ #define USE_SSE_DETECT 1
201
+ #elif defined(__x86_64__) && defined(__clang__)
202
+ #if __has_builtin(__builtin_cpu_supports)
203
+ #define USE_SSE_DETECT 1
204
+ #endif
205
+ #endif
206
+ #endif /* __SSE4_2__ */
207
+
208
+ // This version of Apple clang has a bug:
209
+ // https://llvm.org/bugs/show_bug.cgi?id=25510
210
+ #if defined(__APPLE__) && (__clang_major__ <= 8)
211
+ #undef USE_SSE_DETECT
212
+ #endif
213
+
214
+ #if defined(TRUFFLERUBY)
215
+ #undef USE_SSE_DETECT
216
+ #endif
217
+
218
+ #ifdef USE_SSE_DETECT
219
+ #include <nmmintrin.h>
220
+
221
+ static inline const char *scan_string_SIMD(const char *str, const char *end) {
222
+ static const char chars[16] = "\x00\\\"";
223
+ const __m128i terminate = _mm_loadu_si128((const __m128i *)&chars[0]);
224
+ const char *_end = (const char *)(end - 16);
225
+
226
+ for (; str <= _end; str += 16) {
227
+ const __m128i string = _mm_loadu_si128((const __m128i *)str);
228
+ const int r = _mm_cmpestri(terminate, 3, string, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT);
229
+ if (r != 16) {
230
+ str = (char*)(str + r);
231
+ return str;
232
+ }
233
+ }
234
+
235
+ return scan_string_noSIMD(str, end);
236
+ }
237
+ #endif
238
+
239
+ static bool cpu_supports_sse42(void) {
240
+ #if USE_SSE_DETECT
241
+ __builtin_cpu_init();
242
+ return (__builtin_cpu_supports("sse4.2"));
243
+ #else
244
+ return false;
245
+ #endif
246
+ }
247
+
248
+ static const char *(*scan_func) (const char *str, const char *end) = scan_string_noSIMD;
249
+
250
+ void oj_scanner_init(void) {
251
+ if (cpu_supports_sse42()) {
252
+ #if USE_SSE_DETECT
253
+ scan_func = scan_string_SIMD;
254
+ #endif
255
+ }
256
+ }
257
+
186
258
  // entered at /
187
259
  static void read_escaped_str(ParseInfo pi, const char *start) {
188
260
  struct _buf buf;
@@ -192,11 +264,11 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
192
264
  Val parent = stack_peek(&pi->stack);
193
265
 
194
266
  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) {
267
+ buf_append_string(&buf, start, cnt);
268
+
269
+ for (s = pi->cur; '"' != *s;) {
270
+ const char *scanned = scan_func(s, pi->end);
271
+ if (scanned >= pi->end) {
200
272
  oj_set_error_at(pi,
201
273
  oj_parse_error_class,
202
274
  __FILE__,
@@ -204,7 +276,12 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
204
276
  "quoted string not terminated");
205
277
  buf_cleanup(&buf);
206
278
  return;
207
- } else if ('\\' == *s) {
279
+ }
280
+
281
+ buf_append_string(&buf, s, (size_t)(scanned - s));
282
+ s = scanned;
283
+
284
+ if ('\\' == *s) {
208
285
  s++;
209
286
  switch (*s) {
210
287
  case 'n': buf_append(&buf, '\n'); break;
@@ -273,8 +350,7 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
273
350
  buf_cleanup(&buf);
274
351
  return;
275
352
  }
276
- } else {
277
- buf_append(&buf, *s);
353
+ s++;
278
354
  }
279
355
  }
280
356
  if (0 == parent) {
@@ -327,44 +403,11 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
327
403
  buf_cleanup(&buf);
328
404
  }
329
405
 
330
- static inline void scan_string_noSIMD(ParseInfo pi) {
331
- for (; '"' != *pi->cur; pi->cur++) {
332
- if (pi->end <= pi->cur || '\0' == *pi->cur || '\\' == *pi->cur) {
333
- return;
334
- }
335
- }
336
- }
337
-
338
- #if defined(OJ_USE_SSE4_2)
339
- #include <nmmintrin.h>
340
-
341
- static inline void scan_string_SIMD(ParseInfo pi) {
342
- static const char chars[16] = "\x00\\\"";
343
- const __m128i terminate = _mm_loadu_si128((const __m128i *)&chars[0]);
344
- const char *end = (const char *)(pi->end - 16);
345
-
346
- for (; pi->cur <= end; pi->cur += 16) {
347
- const __m128i string = _mm_loadu_si128((const __m128i *)pi->cur);
348
- const int r = _mm_cmpestri(terminate, 3, string, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT);
349
- if (r != 16) {
350
- pi->cur = (const char*)(pi->cur + r);
351
- return;
352
- }
353
- }
354
-
355
- scan_string_noSIMD(pi);
356
- }
357
- #endif
358
-
359
406
  static void read_str(ParseInfo pi) {
360
407
  const char *str = pi->cur;
361
408
  Val parent = stack_peek(&pi->stack);
362
409
 
363
- #if defined(OJ_USE_SSE4_2)
364
- scan_string_SIMD(pi);
365
- #else
366
- scan_string_noSIMD(pi);
367
- #endif
410
+ pi->cur = scan_func(pi->cur, pi->end);
368
411
  if (RB_UNLIKELY(pi->end <= pi->cur)) {
369
412
  oj_set_error_at(pi,
370
413
  oj_parse_error_class,
@@ -494,33 +537,31 @@ static void read_num(ParseInfo pi) {
494
537
  int dec_cnt = 0;
495
538
  bool zero1 = false;
496
539
 
540
+ // Skip leading zeros.
541
+ for (; '0' == *pi->cur; pi->cur++) {
542
+ zero1 = true;
543
+ }
544
+
497
545
  for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
498
- if (0 == ni.i && '0' == *pi->cur) {
499
- zero1 = true;
500
- }
501
- if (0 < ni.i) {
502
- dec_cnt++;
503
- }
504
- if (!ni.big) {
505
- int d = (*pi->cur - '0');
546
+ int d = (*pi->cur - '0');
506
547
 
507
- if (0 < d) {
508
- if (zero1 && CompatMode == pi->options.mode) {
509
- oj_set_error_at(pi,
510
- oj_parse_error_class,
511
- __FILE__,
512
- __LINE__,
513
- "not a number");
514
- return;
515
- }
516
- zero1 = false;
517
- }
518
- ni.i = ni.i * 10 + d;
519
- if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
520
- ni.big = 1;
521
- }
548
+ if (RB_LIKELY(0 != ni.i)) {
549
+ dec_cnt++;
522
550
  }
551
+ ni.i = ni.i * 10 + d;
523
552
  }
553
+ if (RB_UNLIKELY(0 != ni.i && zero1 && CompatMode == pi->options.mode)) {
554
+ oj_set_error_at(pi,
555
+ oj_parse_error_class,
556
+ __FILE__,
557
+ __LINE__,
558
+ "not a number");
559
+ return;
560
+ }
561
+ if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
562
+ ni.big = true;
563
+ }
564
+
524
565
  if ('.' == *pi->cur) {
525
566
  pi->cur++;
526
567
  // A trailing . is not a valid decimal but if encountered allow it
@@ -540,25 +581,20 @@ static void read_num(ParseInfo pi) {
540
581
  for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
541
582
  int d = (*pi->cur - '0');
542
583
 
543
- if (0 < ni.num || 0 < ni.i) {
584
+ if (RB_LIKELY(0 != ni.num || 0 != ni.i)) {
544
585
  dec_cnt++;
545
586
  }
546
- if (INT64_MAX <= ni.div) {
547
- if (!ni.no_big) {
548
- ni.big = true;
549
- }
550
- } else {
551
- ni.num = ni.num * 10 + d;
552
- ni.div *= 10;
553
- ni.di++;
554
- if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
555
- if (!ni.no_big) {
556
- ni.big = true;
557
- }
558
- }
559
- }
587
+ ni.num = ni.num * 10 + d;
588
+ ni.div *= 10;
589
+ ni.di++;
560
590
  }
561
591
  }
592
+ if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
593
+ if (!ni.no_big) {
594
+ ni.big = true;
595
+ }
596
+ }
597
+
562
598
  if ('e' == *pi->cur || 'E' == *pi->cur) {
563
599
  int eneg = 0;
564
600
 
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
@@ -600,7 +600,7 @@ static void parse(ojParser p, const byte *json) {
600
600
  int i;
601
601
 
602
602
  p->line = 1;
603
- p->col = -1;
603
+ p->col = -1;
604
604
  #if DEBUG
605
605
  printf("*** parse - mode: %c %s\n", p->map[256], (const char *)json);
606
606
  #endif
@@ -655,7 +655,7 @@ static void parse(ojParser p, const byte *json) {
655
655
  }
656
656
  buf_append_string(&p->buf, (const char *)start, b - start);
657
657
  if ('"' == *b) {
658
- p->cur = b - json;
658
+ p->cur = b - json;
659
659
  p->funcs[p->stack[p->depth]].add_str(p);
660
660
  p->map = (0 == p->depth) ? value_map : after_map;
661
661
  break;
@@ -665,14 +665,14 @@ static void parse(ojParser p, const byte *json) {
665
665
  p->next_map = (0 == p->depth) ? value_map : after_map;
666
666
  break;
667
667
  case OPEN_OBJECT:
668
- p->cur = b - json;
668
+ p->cur = b - json;
669
669
  p->funcs[p->stack[p->depth]].open_object(p);
670
670
  p->depth++;
671
671
  p->stack[p->depth] = OBJECT_FUN;
672
672
  p->map = key1_map;
673
673
  break;
674
674
  case NUM_CLOSE_OBJECT:
675
- p->cur = b - json;
675
+ p->cur = b - json;
676
676
  calc_num(p);
677
677
  // flow through
678
678
  case CLOSE_OBJECT:
@@ -683,18 +683,18 @@ static void parse(ojParser p, const byte *json) {
683
683
  return;
684
684
  }
685
685
  p->depth--;
686
- p->cur = b - json;
686
+ p->cur = b - json;
687
687
  p->funcs[p->stack[p->depth]].close_object(p);
688
688
  break;
689
689
  case OPEN_ARRAY:
690
- p->cur = b - json;
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
+ p->cur = b - json;
698
698
  calc_num(p);
699
699
  // flow through
700
700
  case CLOSE_ARRAY:
@@ -705,11 +705,11 @@ static void parse(ojParser p, const byte *json) {
705
705
  return;
706
706
  }
707
707
  p->depth--;
708
- p->cur = b - json;
708
+ p->cur = b - json;
709
709
  p->funcs[p->stack[p->depth]].close_array(p);
710
710
  break;
711
711
  case NUM_COMMA:
712
- p->cur = b - json;
712
+ p->cur = b - json;
713
713
  calc_num(p);
714
714
  if (0 < p->depth && OBJECT_FUN == p->stack[p->depth]) {
715
715
  p->map = key_map;
@@ -872,13 +872,13 @@ static void parse(ojParser p, const byte *json) {
872
872
  p->map = big_exp_map;
873
873
  break;
874
874
  case NUM_SPC:
875
- p->cur = b - json;
876
- calc_num(p);
877
- break;
875
+ p->cur = b - json;
876
+ calc_num(p);
877
+ break;
878
878
  case NUM_NEWLINE:
879
- p->cur = b - json;
880
- calc_num(p);
881
- b++;
879
+ p->cur = b - json;
880
+ calc_num(p);
881
+ b++;
882
882
  #ifdef SPACE_JUMP
883
883
  // for (uint32_t *sj = (uint32_t*)b; 0x20202020 == *sj; sj++) { b += 4; }
884
884
  for (uint16_t *sj = (uint16_t *)b; 0x2020 == *sj; sj++) {
@@ -899,7 +899,7 @@ static void parse(ojParser p, const byte *json) {
899
899
  buf_append_string(&p->buf, (const char *)start, b - start);
900
900
  }
901
901
  if ('"' == *b) {
902
- p->cur = b - json;
902
+ p->cur = b - json;
903
903
  p->funcs[p->stack[p->depth]].add_str(p);
904
904
  p->map = p->next_map;
905
905
  break;
@@ -908,7 +908,7 @@ static void parse(ojParser p, const byte *json) {
908
908
  break;
909
909
  case STR_SLASH: p->map = esc_map; break;
910
910
  case STR_QUOTE:
911
- p->cur = b - json;
911
+ p->cur = b - json;
912
912
  p->funcs[p->stack[p->depth]].add_str(p);
913
913
  p->map = p->next_map;
914
914
  break;
@@ -986,7 +986,7 @@ static void parse(ojParser p, const byte *json) {
986
986
  case VAL_NULL:
987
987
  if ('u' == b[1] && 'l' == b[2] && 'l' == b[3]) {
988
988
  b += 3;
989
- p->cur = b - json;
989
+ p->cur = b - json;
990
990
  p->funcs[p->stack[p->depth]].add_null(p);
991
991
  p->map = (0 == p->depth) ? value_map : after_map;
992
992
  break;
@@ -1012,7 +1012,7 @@ static void parse(ojParser p, const byte *json) {
1012
1012
  case VAL_TRUE:
1013
1013
  if ('r' == b[1] && 'u' == b[2] && 'e' == b[3]) {
1014
1014
  b += 3;
1015
- p->cur = b - json;
1015
+ p->cur = b - json;
1016
1016
  p->funcs[p->stack[p->depth]].add_true(p);
1017
1017
  p->map = (0 == p->depth) ? value_map : after_map;
1018
1018
  break;
@@ -1038,7 +1038,7 @@ static void parse(ojParser p, const byte *json) {
1038
1038
  case VAL_FALSE:
1039
1039
  if ('a' == b[1] && 'l' == b[2] && 's' == b[3] && 'e' == b[4]) {
1040
1040
  b += 4;
1041
- p->cur = b - json;
1041
+ p->cur = b - json;
1042
1042
  p->funcs[p->stack[p->depth]].add_false(p);
1043
1043
  p->map = (0 == p->depth) ? value_map : after_map;
1044
1044
  break;
@@ -1072,7 +1072,7 @@ static void parse(ojParser p, const byte *json) {
1072
1072
  parse_error(p, "expected null");
1073
1073
  return;
1074
1074
  }
1075
- p->cur = b - json;
1075
+ p->cur = b - json;
1076
1076
  p->funcs[p->stack[p->depth]].add_null(p);
1077
1077
  p->map = (0 == p->depth) ? value_map : after_map;
1078
1078
  }
@@ -1084,7 +1084,7 @@ static void parse(ojParser p, const byte *json) {
1084
1084
  parse_error(p, "expected false");
1085
1085
  return;
1086
1086
  }
1087
- p->cur = b - json;
1087
+ p->cur = b - json;
1088
1088
  p->funcs[p->stack[p->depth]].add_false(p);
1089
1089
  p->map = (0 == p->depth) ? value_map : after_map;
1090
1090
  }
@@ -1096,7 +1096,7 @@ static void parse(ojParser p, const byte *json) {
1096
1096
  parse_error(p, "expected true");
1097
1097
  return;
1098
1098
  }
1099
- p->cur = b - json;
1099
+ p->cur = b - json;
1100
1100
  p->funcs[p->stack[p->depth]].add_true(p);
1101
1101
  p->map = (0 == p->depth) ? value_map : after_map;
1102
1102
  }
@@ -1114,6 +1114,9 @@ static void parse(ojParser p, const byte *json) {
1114
1114
  p->map = trail_map;
1115
1115
  }
1116
1116
  }
1117
+ if (0 < p->depth) {
1118
+ parse_error(p, "parse error, not closed");
1119
+ }
1117
1120
  if (0 == p->depth) {
1118
1121
  switch (p->map[256]) {
1119
1122
  case '0':
@@ -1125,9 +1128,9 @@ static void parse(ojParser p, const byte *json) {
1125
1128
  case 'g':
1126
1129
  case 'B':
1127
1130
  case 'Y':
1128
- p->cur = b - json;
1129
- calc_num(p);
1130
- break;
1131
+ p->cur = b - json;
1132
+ calc_num(p);
1133
+ break;
1131
1134
  }
1132
1135
  }
1133
1136
  return;
data/ext/oj/rails.c CHANGED
@@ -517,7 +517,7 @@ static void dump_as_string(VALUE obj, int depth, Out out, bool as_ok) {
517
517
  static void dump_as_json(VALUE obj, int depth, Out out, bool as_ok) {
518
518
  volatile VALUE ja;
519
519
 
520
- if (Yes == out->opts->trace) {
520
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
521
521
  oj_trace("as_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
522
522
  }
523
523
  // Some classes elect to not take an options argument so check the arity
@@ -527,7 +527,7 @@ static void dump_as_json(VALUE obj, int depth, Out out, bool as_ok) {
527
527
  } else {
528
528
  ja = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
529
529
  }
530
- if (Yes == out->opts->trace) {
530
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
531
531
  oj_trace("as_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
532
532
  }
533
533
 
@@ -1464,7 +1464,7 @@ static DumpFunc rails_funcs[] = {
1464
1464
  static void dump_rails_val(VALUE obj, int depth, Out out, bool as_ok) {
1465
1465
  int type = rb_type(obj);
1466
1466
 
1467
- if (Yes == out->opts->trace) {
1467
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
1468
1468
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
1469
1469
  }
1470
1470
  if (MAX_DEPTH < depth) {
@@ -1475,14 +1475,14 @@ static void dump_rails_val(VALUE obj, int depth, Out out, bool as_ok) {
1475
1475
 
1476
1476
  if (NULL != f) {
1477
1477
  f(obj, depth, out, as_ok);
1478
- if (Yes == out->opts->trace) {
1478
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
1479
1479
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
1480
1480
  }
1481
1481
  return;
1482
1482
  }
1483
1483
  }
1484
1484
  oj_dump_nil(Qnil, depth, out, false);
1485
- if (Yes == out->opts->trace) {
1485
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
1486
1486
  oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
1487
1487
  }
1488
1488
  }
data/ext/oj/saj2.c CHANGED
@@ -289,7 +289,7 @@ static void add_float_key_loc(ojParser p) {
289
289
  }
290
290
 
291
291
  static void add_big(ojParser p) {
292
- rb_funcall((VALUE)p->ctx,
292
+ rb_funcall(((Delegate)p->ctx)->handler,
293
293
  oj_add_value_id,
294
294
  2,
295
295
  rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(buf_str(&p->buf), buf_len(&p->buf))),
@@ -297,7 +297,7 @@ static void add_big(ojParser p) {
297
297
  }
298
298
 
299
299
  static void add_big_loc(ojParser p) {
300
- rb_funcall((VALUE)p->ctx,
300
+ rb_funcall(((Delegate)p->ctx)->handler,
301
301
  oj_add_value_id,
302
302
  4,
303
303
  rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(buf_str(&p->buf), buf_len(&p->buf))),
@@ -307,7 +307,7 @@ static void add_big_loc(ojParser p) {
307
307
  }
308
308
 
309
309
  static void add_big_key(ojParser p) {
310
- rb_funcall((VALUE)p->ctx,
310
+ rb_funcall(((Delegate)p->ctx)->handler,
311
311
  oj_add_value_id,
312
312
  2,
313
313
  rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new(buf_str(&p->buf), buf_len(&p->buf))),
data/ext/oj/validate.c CHANGED
@@ -2,50 +2,45 @@
2
2
 
3
3
  #include "parser.h"
4
4
 
5
- static void
6
- noop(ojParser p) {
5
+ static void noop(ojParser p) {
7
6
  }
8
7
 
9
- static VALUE
10
- option(ojParser p, const char *key, VALUE value) {
8
+ static VALUE option(ojParser p, const char *key, VALUE value) {
11
9
  rb_raise(rb_eArgError, "%s is not an option for the validate delegate", key);
12
10
  return Qnil;
13
11
  }
14
12
 
15
- static VALUE
16
- result(ojParser p) {
13
+ static VALUE result(ojParser p) {
17
14
  return Qnil;
18
15
  }
19
16
 
20
- static void
21
- dfree(ojParser p) {
17
+ static void dfree(ojParser p) {
22
18
  }
23
19
 
24
- static void
25
- mark(ojParser p) {
20
+ static void mark(ojParser p) {
26
21
  }
27
22
 
28
23
  void oj_set_parser_validator(ojParser p) {
29
- p->ctx = NULL;
30
- Funcs end = p->funcs + 3;
24
+ Funcs end = p->funcs + 3;
31
25
  Funcs f;
26
+ p->ctx = NULL;
32
27
 
33
28
  for (f = p->funcs; f < end; f++) {
34
- f->add_null = noop;
35
- f->add_true = noop;
36
- f->add_false = noop;
37
- f->add_int = noop;
38
- f->add_float = noop;
39
- f->add_big = noop;
40
- f->add_str = noop;
41
- f->open_array = noop;
42
- f->close_array = noop;
43
- f->open_object = noop;
44
- f->close_object = noop;
29
+ f->add_null = noop;
30
+ f->add_true = noop;
31
+ f->add_false = noop;
32
+ f->add_int = noop;
33
+ f->add_float = noop;
34
+ f->add_big = noop;
35
+ f->add_str = noop;
36
+ f->open_array = noop;
37
+ f->close_array = noop;
38
+ f->open_object = noop;
39
+ f->close_object = noop;
45
40
  }
46
41
  p->option = option;
47
42
  p->result = result;
48
- p->free = dfree;
49
- p->mark = mark;
50
- p->start = noop;
43
+ p->free = dfree;
44
+ p->mark = mark;
45
+ p->start = noop;
51
46
  }
data/ext/oj/wab.c CHANGED
@@ -266,7 +266,7 @@ static DumpFunc wab_funcs[] = {
266
266
  void oj_dump_wab_val(VALUE obj, int depth, Out out) {
267
267
  int type = rb_type(obj);
268
268
 
269
- if (Yes == out->opts->trace) {
269
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
270
270
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
271
271
  }
272
272
  if (MAX_DEPTH < depth) {
@@ -277,7 +277,7 @@ void oj_dump_wab_val(VALUE obj, int depth, Out out) {
277
277
 
278
278
  if (NULL != f) {
279
279
  f(obj, depth, out, false);
280
- if (Yes == out->opts->trace) {
280
+ if (RB_UNLIKELY(Yes == out->opts->trace)) {
281
281
  oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
282
282
  }
283
283
  return;
data/lib/oj/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Oj
3
3
  # Current version of the module.
4
- VERSION = '3.13.16'
4
+ VERSION = '3.13.19'
5
5
  end
data/test/bar.rb CHANGED
@@ -6,11 +6,6 @@ $: << File.join(File.dirname(__FILE__), "../ext")
6
6
 
7
7
  require 'oj'
8
8
 
9
- puts Oj.dump({
10
- "float_test" => 0.25,
11
- "nan_test" => Float::NAN,
12
- "inf_test" => Float::INFINITY,
13
- "minus_inf_test" => -Float::INFINITY,
14
- "min_test" => Float::MIN,
15
- "max_test" => Float::MAX,
16
- }, mode: :object) # => {"float_test":0.25,"nan_test":3.3e14159265358979323846
9
+ p = Oj::Parser.validate
10
+ # p = Oj::Parser.new(:debug)
11
+ p.parse(%|{|)
data/test/helper.rb CHANGED
@@ -19,10 +19,16 @@ require 'pp'
19
19
  require 'oj'
20
20
 
21
21
 
22
- if defined?(GC.verify_compaction_references) == 'method'
22
+ def verify_gc_compaction
23
23
  # This method was added in Ruby 3.0.0. Calling it this way asks the GC to
24
24
  # move objects around, helping to find object movement bugs.
25
- GC.verify_compaction_references(double_heap: true, toward: :empty)
25
+ if defined?(GC.verify_compaction_references) == 'method' && !(RbConfig::CONFIG['host_os'] =~ /(mingw|mswin)/)
26
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.2.0")
27
+ GC.verify_compaction_references(expand_heap: true, toward: :empty)
28
+ else
29
+ GC.verify_compaction_references(double_heap: true, toward: :empty)
30
+ end
31
+ end
26
32
  end
27
33
 
28
34
 
@@ -15,10 +15,14 @@ else
15
15
  require 'oj'
16
16
  Oj.mimic_JSON
17
17
 
18
+ # This method was added in Ruby 3.0.0. Calling it this way asks the GC to
19
+ # move objects around, helping to find object movement bugs.
18
20
  if defined?(GC.verify_compaction_references) == 'method'
19
- # This method was added in Ruby 3.0.0. Calling it this way asks the GC to
20
- # move objects around, helping to find object movement bugs.
21
- GC.verify_compaction_references(double_heap: true, toward: :empty)
21
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.2.0")
22
+ GC.verify_compaction_references(expand_heap: true, toward: :empty)
23
+ else
24
+ GC.verify_compaction_references(double_heap: true, toward: :empty)
25
+ end
22
26
  end
23
27
  end
24
28
 
data/test/test_compat.rb CHANGED
@@ -504,6 +504,15 @@ class CompatJuice < Minitest::Test
504
504
  assert_equal("ぴーたー", Oj.load(json)['a'])
505
505
  end
506
506
 
507
+ def test_parse_large_escaped_string
508
+ invalid_json = %|{"a":\"aaaa\\nbbbb\\rcccc\\tddd\\feee\\bf\/\\\\\\u3074\\u30fc\\u305f\\u30fc }|
509
+ error = assert_raises() { Oj.load(invalid_json) }
510
+ assert(error.message.include?('quoted string not terminated'))
511
+
512
+ json = "\"aaaa\\nbbbb\\rcccc\\tddd\\feee\\bf\/\\\\\\u3074\\u30fc\\u305f\\u30fc \""
513
+ assert_equal("aaaa\nbbbb\rcccc\tddd\feee\bf/\\ぴーたー ", Oj.load(json))
514
+ end
515
+
507
516
  def dump_and_load(obj, trace=false)
508
517
  json = Oj.dump(obj)
509
518
  puts json if trace
data/test/test_object.rb CHANGED
@@ -224,7 +224,7 @@ class ObjectJuice < Minitest::Test
224
224
  #=begin
225
225
  if '3.1.0' <= RUBY_VERSION && !(RbConfig::CONFIG['host_os'] =~ /(mingw|mswin)/)
226
226
  #Oj::debug_odd("teardown before GC.verify_compaction_references")
227
- GC.verify_compaction_references(double_heap: true, toward: :empty)
227
+ verify_gc_compaction
228
228
  #Oj::debug_odd("teardown after GC.verify_compaction_references")
229
229
  end
230
230
  #=end
@@ -149,6 +149,17 @@ class SajTest < Minitest::Test
149
149
  assert_equal((12345.6789e7 * 10000).to_i, (handler.calls[0][1] * 10000).to_i)
150
150
  end
151
151
 
152
+ def test_bignum
153
+ handler = AllSaj.new()
154
+ json = %{-11.899999999999999}
155
+ p = Oj::Parser.new(:saj)
156
+ p.handler = handler
157
+ p.parse(json)
158
+ assert_equal(1, handler.calls.size)
159
+ assert_equal(:add_value, handler.calls[0][0])
160
+ assert_equal(-118999, (handler.calls[0][1] * 10000).to_i)
161
+ end
162
+
152
163
  def test_array_empty
153
164
  handler = AllSaj.new()
154
165
  json = %{[]}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oj
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.13.16
4
+ version: 3.13.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-06 00:00:00.000000000 Z
11
+ date: 2022-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler