oj 3.13.16 → 3.13.19

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