oj 3.13.23 → 3.14.0

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: 3d2f8a01b7a59da7cd25ed3551065dddd9e31e5184989804d172c2e39cc769d6
4
- data.tar.gz: 7211a96856ec44173fa9dcc7245e3cb2f6b7c2e22a746ef35f168126873f4c88
3
+ metadata.gz: 5864f273679d1441d9731976eb4f4c3cdcccaa86c877fae2aeb719cd03f45167
4
+ data.tar.gz: 815abc1a8572ce907b280ca632fd010c766ad55e581c3a59d76c0f94ede24234
5
5
  SHA512:
6
- metadata.gz: ee3ad5cac6cac48bcf7fdf682e69770dfee08b502735cc75b84ae1635196fdcdaf2fde021e32e6d897bdc6935e111882ddc48fa5a274fe0d98ad45c2fdf68917
7
- data.tar.gz: 3e05212fa84a2bf05954ec870ac27da7c845335b411507aa0bf5e45497403cccf4c40990f0ee8cee6d21f19163f33116bb240836795482737eaa744bc5ff6d94
6
+ metadata.gz: 3318017139004f712665ff2fda5dcfc761b33079bb96dd1d8f5baf59d146a45956e3c58f1e700044a714ca2643d5a54196e3b306566130c9bda7242d8c688b91
7
+ data.tar.gz: f8f6f637c883ca2b4fa8c76f349940f6be3c1af524d43f3cc0ed4df27a21896f480da520b2897e191d38e50ef939139bdd242c3e3f33aa0c0e3aa8bd3a008a6b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.14.0 - 2022-01-30
4
+
5
+ - Tracing is now a compile time option giving a 15 to 20% performance boost.
6
+
7
+ - Some cleanup in teh fast parser.
8
+
3
9
  ## 3.13.23 - 2022-11-06
4
10
 
5
11
  - Fixed issue with Oj::Parser extension regarding GC timeing.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # [![{}j](http://www.ohler.com/dev/images/oj_comet_64.svg)](http://www.ohler.com/oj) gem
2
2
 
3
- [![Build Status](https://img.shields.io/github/workflow/status/ohler55/oj/CI?logo=github)](https://github.com/ohler55/oj/actions/workflows/CI.yml)
3
+ [![CI](https://github.com/ohler55/oj/actions/workflows/CI.yml/badge.svg)](https://github.com/ohler55/oj/actions/workflows/CI.yml)
4
4
  ![Gem](https://img.shields.io/gem/v/oj.svg)
5
5
  ![Gem](https://img.shields.io/gem/dt/oj.svg)
6
6
  [![SemVer compatibility](https://api.dependabot.com/badges/compatibility_score?dependency-name=oj&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=oj&package-manager=bundler&version-scheme=semver)
@@ -70,6 +70,7 @@ links.
70
70
  - [{file:Compatibility.md}](pages/Compatibility.md) lists current compatibility with Rubys and Rails.
71
71
  - [{file:Advanced.md}](pages/Advanced.md) for fast parser and marshalling features.
72
72
  - [{file:Security.md}](pages/Security.md) for security considerations.
73
+ - [{file:InstallOptions.md}](pages/InstallOptions.md) for install option.
73
74
 
74
75
  ## Releases
75
76
 
data/ext/oj/compat.c CHANGED
@@ -54,9 +54,7 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
54
54
  } else {
55
55
  rb_hash_aset(parent->val, rkey, rstr);
56
56
  }
57
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
58
- oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rstr);
59
- }
57
+ TRACE_PARSE_CALL(pi->options.trace, "set_string", pi, rstr);
60
58
  }
61
59
  }
62
60
 
@@ -68,9 +66,7 @@ static VALUE start_hash(ParseInfo pi) {
68
66
  } else {
69
67
  h = rb_hash_new();
70
68
  }
71
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
72
- oj_trace_parse_in("start_hash", pi, __FILE__, __LINE__);
73
- }
69
+ TRACE_PARSE_IN(pi->options.trace, "start_hash", pi);
74
70
  return h;
75
71
  }
76
72
 
@@ -93,9 +89,7 @@ static void end_hash(struct _parseInfo *pi) {
93
89
  parent->classname = 0;
94
90
  }
95
91
  }
96
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
97
- oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
98
- }
92
+ TRACE_PARSE_HASH_END(pi->options.trace, pi);
99
93
  }
100
94
 
101
95
  static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
@@ -110,16 +104,12 @@ static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig
110
104
  }
111
105
  }
112
106
  pi->stack.head->val = rstr;
113
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
114
- oj_trace_parse_call("add_string", pi, __FILE__, __LINE__, rstr);
115
- }
107
+ TRACE_PARSE_CALL(pi->options.trace, "add_string", pi, rstr);
116
108
  }
117
109
 
118
110
  static void add_num(ParseInfo pi, NumInfo ni) {
119
111
  pi->stack.head->val = oj_num_as_value(ni);
120
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
121
- oj_trace_parse_call("add_number", pi, __FILE__, __LINE__, pi->stack.head->val);
122
- }
112
+ TRACE_PARSE_CALL(pi->options.trace, "add_number", pi, pi->stack.head->val);
123
113
  }
124
114
 
125
115
  static void hash_set_num(struct _parseInfo *pi, Val parent, NumInfo ni) {
@@ -138,9 +128,7 @@ static void hash_set_num(struct _parseInfo *pi, Val parent, NumInfo ni) {
138
128
  } else {
139
129
  rb_hash_aset(stack_peek(&pi->stack)->val, oj_calc_hash_key(pi, parent), rval);
140
130
  }
141
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
142
- oj_trace_parse_call("set_number", pi, __FILE__, __LINE__, rval);
143
- }
131
+ TRACE_PARSE_CALL(pi->options.trace, "set_number", pi, rval);
144
132
  }
145
133
 
146
134
  static void hash_set_value(ParseInfo pi, Val parent, VALUE value) {
@@ -157,18 +145,14 @@ static void hash_set_value(ParseInfo pi, Val parent, VALUE value) {
157
145
  } else {
158
146
  rb_hash_aset(stack_peek(&pi->stack)->val, oj_calc_hash_key(pi, parent), value);
159
147
  }
160
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
161
- oj_trace_parse_call("set_value", pi, __FILE__, __LINE__, value);
162
- }
148
+ TRACE_PARSE_CALL(pi->options.trace, "set_value", pi, value);
163
149
  }
164
150
 
165
151
  static VALUE start_array(ParseInfo pi) {
166
152
  if (Qnil != pi->options.array_class) {
167
153
  return rb_class_new_instance(0, NULL, pi->options.array_class);
168
154
  }
169
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
170
- oj_trace_parse_in("start_array", pi, __FILE__, __LINE__);
171
- }
155
+ TRACE_PARSE_IN(pi->options.trace, "start_array", pi);
172
156
  return rb_ary_new();
173
157
  }
174
158
 
@@ -184,9 +168,7 @@ static void array_append_num(ParseInfo pi, NumInfo ni) {
184
168
  } else {
185
169
  rb_ary_push(parent->val, rval);
186
170
  }
187
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
188
- oj_trace_parse_call("append_number", pi, __FILE__, __LINE__, rval);
189
- }
171
+ TRACE_PARSE_CALL(pi->options.trace, "append_number", pi, rval);
190
172
  }
191
173
 
192
174
  static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
@@ -201,9 +183,7 @@ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const c
201
183
  }
202
184
  }
203
185
  rb_ary_push(stack_peek(&pi->stack)->val, rstr);
204
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
205
- oj_trace_parse_call("append_string", pi, __FILE__, __LINE__, rstr);
206
- }
186
+ TRACE_PARSE_CALL(pi->options.trace, "append_string", pi, rstr);
207
187
  }
208
188
 
209
189
  void oj_set_compat_callbacks(ParseInfo pi) {
data/ext/oj/custom.c CHANGED
@@ -24,20 +24,20 @@ static void dump_obj_str(VALUE obj, int depth, Out out) {
24
24
  {"s", 1, Qnil},
25
25
  {NULL, 0, Qnil},
26
26
  };
27
- attrs->value = rb_funcall(obj, oj_to_s_id, 0);
27
+ attrs->value = oj_safe_string_convert(obj);
28
28
 
29
29
  oj_code_attrs(obj, attrs, depth, out, Yes == out->opts->create_ok);
30
30
  }
31
31
 
32
32
  static void dump_obj_as_str(VALUE obj, int depth, Out out) {
33
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
33
+ volatile VALUE rstr = oj_safe_string_convert(obj);
34
34
  const char *str = RSTRING_PTR(rstr);
35
35
 
36
36
  oj_dump_cstr(str, RSTRING_LEN(rstr), 0, 0, out);
37
37
  }
38
38
 
39
39
  static void bigdecimal_dump(VALUE obj, int depth, Out out) {
40
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
40
+ volatile VALUE rstr = oj_safe_string_convert(obj);
41
41
  const char *str = RSTRING_PTR(rstr);
42
42
  int len = (int)RSTRING_LEN(rstr);
43
43
 
@@ -305,7 +305,7 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
305
305
  switch (rb_type(key)) {
306
306
  case T_STRING: oj_dump_str(key, 0, out, false); break;
307
307
  case T_SYMBOL: oj_dump_sym(key, 0, out, false); break;
308
- default: oj_dump_str(rb_funcall(key, oj_to_s_id, 0), 0, out, false); break;
308
+ default: oj_dump_str(oj_safe_string_convert(key), 0, out, false); break;
309
309
  }
310
310
  if (!out->opts->dump_opts.use) {
311
311
  *out->cur++ = ':';
@@ -479,17 +479,13 @@ static VALUE dump_common(VALUE obj, int depth, Out out) {
479
479
  const char *s;
480
480
  int len;
481
481
 
482
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
483
- oj_trace("to_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
484
- }
482
+ TRACE(out->opts->trace, "to_json", obj, depth + 1, TraceRubyIn);
485
483
  if (0 == rb_obj_method_arity(obj, oj_to_json_id)) {
486
484
  rs = rb_funcall(obj, oj_to_json_id, 0);
487
485
  } else {
488
486
  rs = rb_funcall2(obj, oj_to_json_id, out->argc, out->argv);
489
487
  }
490
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
491
- oj_trace("to_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
492
- }
488
+ TRACE(out->opts->trace, "to_json", obj, depth + 1, TraceRubyOut);
493
489
  s = RSTRING_PTR(rs);
494
490
  len = (int)RSTRING_LEN(rs);
495
491
 
@@ -499,9 +495,7 @@ static VALUE dump_common(VALUE obj, int depth, Out out) {
499
495
  } else if (Yes == out->opts->as_json && rb_respond_to(obj, oj_as_json_id)) {
500
496
  volatile VALUE aj;
501
497
 
502
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
503
- oj_trace("as_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
504
- }
498
+ TRACE(out->opts->trace, "as_json", obj, depth + 1, TraceRubyIn);
505
499
  // Some classes elect to not take an options argument so check the arity
506
500
  // of as_json.
507
501
  if (0 == rb_obj_method_arity(obj, oj_as_json_id)) {
@@ -509,12 +503,10 @@ static VALUE dump_common(VALUE obj, int depth, Out out) {
509
503
  } else {
510
504
  aj = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
511
505
  }
512
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
513
- oj_trace("as_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
514
- }
506
+ TRACE(out->opts->trace, "as_json", obj, depth + 1, TraceRubyOut);
515
507
  // Catch the obvious brain damaged recursive dumping.
516
508
  if (aj == obj) {
517
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
509
+ volatile VALUE rstr = oj_safe_string_convert(obj);
518
510
 
519
511
  oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), false, false, out);
520
512
  } else {
@@ -876,9 +868,7 @@ static DumpFunc custom_funcs[] = {
876
868
  void oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok) {
877
869
  int type = rb_type(obj);
878
870
 
879
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
880
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
881
- }
871
+ TRACE(out->opts->trace, "dump", obj, depth, TraceIn);
882
872
  if (MAX_DEPTH < depth) {
883
873
  rb_raise(rb_eNoMemError, "Too deeply nested.\n");
884
874
  }
@@ -887,16 +877,12 @@ void oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok) {
887
877
 
888
878
  if (NULL != f) {
889
879
  f(obj, depth, out, true);
890
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
891
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
892
- }
880
+ TRACE(out->opts->trace, "dump", obj, depth, TraceOut);
893
881
  return;
894
882
  }
895
883
  }
896
884
  oj_dump_nil(Qnil, depth, out, false);
897
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
898
- oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
899
- }
885
+ TRACE(out->opts->trace, "dump", Qnil, depth, TraceOut);
900
886
  }
901
887
 
902
888
  ///// load functions /////
@@ -969,9 +955,7 @@ static void end_hash(struct _parseInfo *pi) {
969
955
  }
970
956
  parent->clas = Qundef;
971
957
  }
972
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
973
- oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
974
- }
958
+ TRACE_PARSE_HASH_END(pi->options.trace, pi);
975
959
  }
976
960
 
977
961
  static void hash_set_num(struct _parseInfo *pi, Val kval, NumInfo ni) {
data/ext/oj/dump.c CHANGED
@@ -463,7 +463,7 @@ void oj_dump_time(VALUE obj, Out out, int withZone) {
463
463
  }
464
464
 
465
465
  void oj_dump_ruby_time(VALUE obj, Out out) {
466
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
466
+ volatile VALUE rstr = oj_safe_string_convert(obj);
467
467
 
468
468
  oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
469
469
  }
@@ -736,13 +736,9 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
736
736
  } else {
737
737
  volatile VALUE jv;
738
738
 
739
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
740
- oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
741
- }
739
+ TRACE(out->opts->trace, "raw_json", obj, depth + 1, TraceRubyIn);
742
740
  jv = rb_funcall(obj, oj_raw_json_id, 2, RB_INT2NUM(depth), RB_INT2NUM(out->indent));
743
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
744
- oj_trace("raw_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
745
- }
741
+ TRACE(out->opts->trace, "raw_json", obj, depth + 1, TraceRubyOut);
746
742
  oj_dump_raw(RSTRING_PTR(jv), (size_t)RSTRING_LEN(jv), out);
747
743
  }
748
744
  }
@@ -932,7 +928,7 @@ void oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
932
928
  }
933
929
 
934
930
  void oj_dump_obj_to_s(VALUE obj, Out out) {
935
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
931
+ volatile VALUE rstr = oj_safe_string_convert(obj);
936
932
 
937
933
  oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
938
934
  }
@@ -1173,7 +1169,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1173
1169
  } else if (d == (double)(long long int)d) {
1174
1170
  cnt = snprintf(buf, sizeof(buf), "%.1f", d);
1175
1171
  } else if (0 == out->opts->float_prec) {
1176
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1172
+ volatile VALUE rstr = oj_safe_string_convert(obj);
1177
1173
 
1178
1174
  cnt = (int)RSTRING_LEN(rstr);
1179
1175
  if ((int)sizeof(buf) <= cnt) {
@@ -1195,7 +1191,7 @@ int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char
1195
1191
  // Round off issues at 16 significant digits so check for obvious ones of
1196
1192
  // 0001 and 9999.
1197
1193
  if (17 <= cnt && (0 == strcmp("0001", buf + cnt - 4) || 0 == strcmp("9999", buf + cnt - 4))) {
1198
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1194
+ volatile VALUE rstr = oj_safe_string_convert(obj);
1199
1195
 
1200
1196
  strcpy(buf, RSTRING_PTR(rstr));
1201
1197
  cnt = (int)RSTRING_LEN(rstr);
data/ext/oj/dump_compat.c CHANGED
@@ -109,17 +109,13 @@ dump_to_json(VALUE obj, Out out) {
109
109
  const char *s;
110
110
  int len;
111
111
 
112
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
113
- oj_trace("to_json", obj, __FILE__, __LINE__, 0, TraceRubyIn);
114
- }
112
+ TRACE(out->opts->trace, "to_json", obj, 0, TraceRubyIn);
115
113
  if (0 == rb_obj_method_arity(obj, oj_to_json_id)) {
116
114
  rs = rb_funcall(obj, oj_to_json_id, 0);
117
115
  } else {
118
116
  rs = rb_funcall2(obj, oj_to_json_id, out->argc, out->argv);
119
117
  }
120
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
121
- oj_trace("to_json", obj, __FILE__, __LINE__, 0, TraceRubyOut);
122
- }
118
+ TRACE(out->opts->trace, "to_json", obj, 0, TraceRubyOut);
123
119
 
124
120
  s = RSTRING_PTR(rs);
125
121
  len = (int)RSTRING_LEN(rs);
@@ -299,7 +295,7 @@ datetime_alt(VALUE obj, int depth, Out out) {
299
295
  attrs[3].value = rb_funcall(obj, hour_id, 0);
300
296
  attrs[4].value = rb_funcall(obj, min_id, 0);
301
297
  attrs[5].value = rb_funcall(obj, sec_id, 0);
302
- attrs[6].value = rb_funcall(rb_funcall(obj, offset_id, 0), oj_to_s_id, 0);
298
+ attrs[6].value = oj_safe_string_convert(rb_funcall(obj, offset_id, 0));
303
299
  attrs[7].value = rb_funcall(obj, start_id, 0);
304
300
 
305
301
  oj_code_attrs(obj, attrs, depth, out, true);
@@ -606,7 +602,7 @@ dump_float(VALUE obj, int depth, Out out, bool as_ok) {
606
602
  } else if (oj_rails_float_opt) {
607
603
  cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, "%0.16g");
608
604
  } else {
609
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
605
+ volatile VALUE rstr = oj_safe_string_convert(obj);
610
606
 
611
607
  strcpy(buf, RSTRING_PTR(rstr));
612
608
  cnt = (int)RSTRING_LEN(rstr);
@@ -648,7 +644,7 @@ hash_cb(VALUE key, VALUE value, VALUE ov) {
648
644
  break;
649
645
  default:
650
646
  /*rb_raise(rb_eTypeError, "In :compat mode all Hash keys must be Strings or Symbols, not %s.\n", rb_class2name(rb_obj_class(key)));*/
651
- oj_dump_str(rb_funcall(key, oj_to_s_id, 0), 0, out, false);
647
+ oj_dump_str(oj_safe_string_convert(key), 0, out, false);
652
648
  break;
653
649
  }
654
650
  if (!out->opts->dump_opts.use) {
@@ -833,7 +829,7 @@ dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
833
829
  if (use_bignum_alt) {
834
830
  rs = rb_big2str(obj, 10);
835
831
  } else {
836
- rs = rb_funcall(obj, oj_to_s_id, 0);
832
+ rs = oj_safe_string_convert(obj);
837
833
  }
838
834
  rb_check_type(rs, T_STRING);
839
835
  cnt = (int)RSTRING_LEN(rs);
@@ -893,9 +889,7 @@ void
893
889
  oj_dump_compat_val(VALUE obj, int depth, Out out, bool as_ok) {
894
890
  int type = rb_type(obj);
895
891
 
896
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
897
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
898
- }
892
+ TRACE(out->opts->trace, "dump", obj, depth, TraceIn);
899
893
  if (out->opts->dump_opts.max_depth <= depth) {
900
894
  // When JSON.dump is called then an ArgumentError is expected and the
901
895
  // limit is the depth inclusive. If JSON.generate is called then a
@@ -918,14 +912,10 @@ oj_dump_compat_val(VALUE obj, int depth, Out out, bool as_ok) {
918
912
 
919
913
  if (NULL != f) {
920
914
  f(obj, depth, out, as_ok);
921
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
922
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
923
- }
915
+ TRACE(out->opts->trace, "dump", obj, depth, TraceOut);
924
916
  return;
925
917
  }
926
918
  }
927
919
  oj_dump_nil(Qnil, depth, out, false);
928
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
929
- oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
930
- }
920
+ TRACE(out->opts->trace, "dump", Qnil, depth, TraceOut);
931
921
  }
data/ext/oj/dump_object.c CHANGED
@@ -30,7 +30,7 @@ static void dump_data(VALUE obj, int depth, Out out, bool as_ok) {
30
30
  *out->cur = '\0';
31
31
  } else {
32
32
  if (oj_bigdecimal_class == clas) {
33
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
33
+ volatile VALUE rstr = oj_safe_string_convert(obj);
34
34
  const char * str = RSTRING_PTR(rstr);
35
35
  int len = (int)RSTRING_LEN(rstr);
36
36
 
@@ -59,7 +59,7 @@ static void dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
59
59
  VALUE clas = rb_obj_class(obj);
60
60
 
61
61
  if (oj_bigdecimal_class == clas) {
62
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
62
+ volatile VALUE rstr = oj_safe_string_convert(obj);
63
63
  const char * str = RSTRING_PTR(rstr);
64
64
  int len = (int)RSTRING_LEN(rstr);
65
65
 
@@ -682,9 +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 (RB_UNLIKELY(Yes == out->opts->trace)) {
686
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
687
- }
685
+ TRACE(out->opts->trace, "dump", obj, depth, TraceIn);
688
686
  if (MAX_DEPTH < depth) {
689
687
  rb_raise(rb_eNoMemError, "Too deeply nested.\n");
690
688
  }
@@ -693,14 +691,10 @@ void oj_dump_obj_val(VALUE obj, int depth, Out out) {
693
691
 
694
692
  if (NULL != f) {
695
693
  f(obj, depth, out, false);
696
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
697
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
698
- }
694
+ TRACE(out->opts->trace, "dump", obj, depth, TraceOut);
699
695
  return;
700
696
  }
701
697
  }
702
698
  oj_dump_nil(Qnil, depth, out, false);
703
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
704
- oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
705
- }
699
+ TRACE(out->opts->trace, "dump", Qnil, depth, TraceOut);
706
700
  }
data/ext/oj/dump_strict.c CHANGED
@@ -92,7 +92,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
92
92
  } else if (d == (double)(long long int)d) {
93
93
  cnt = snprintf(buf, sizeof(buf), "%.1f", d);
94
94
  } else if (0 == out->opts->float_prec) {
95
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
95
+ volatile VALUE rstr = oj_safe_string_convert(obj);
96
96
 
97
97
  cnt = (int)RSTRING_LEN(rstr);
98
98
  if ((int)sizeof(buf) <= cnt) {
@@ -290,7 +290,7 @@ static void dump_data_strict(VALUE obj, int depth, Out out, bool as_ok) {
290
290
  VALUE clas = rb_obj_class(obj);
291
291
 
292
292
  if (oj_bigdecimal_class == clas) {
293
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
293
+ volatile VALUE rstr = oj_safe_string_convert(obj);
294
294
 
295
295
  oj_dump_raw(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), out);
296
296
  } else {
@@ -302,7 +302,7 @@ static void dump_data_null(VALUE obj, int depth, Out out, bool as_ok) {
302
302
  VALUE clas = rb_obj_class(obj);
303
303
 
304
304
  if (oj_bigdecimal_class == clas) {
305
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
305
+ volatile VALUE rstr = oj_safe_string_convert(obj);
306
306
 
307
307
  oj_dump_raw(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), out);
308
308
  } else {
@@ -338,9 +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 (RB_UNLIKELY(Yes == out->opts->trace)) {
342
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
343
- }
341
+ TRACE(out->opts->trace, "dump", obj, depth, TraceIn);
344
342
  if (MAX_DEPTH < depth) {
345
343
  rb_raise(rb_eNoMemError, "Too deeply nested.\n");
346
344
  }
@@ -349,9 +347,7 @@ void oj_dump_strict_val(VALUE obj, int depth, Out out) {
349
347
 
350
348
  if (NULL != f) {
351
349
  f(obj, depth, out, false);
352
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
353
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
354
- }
350
+ TRACE(out->opts->trace, "dump", obj, depth, TraceOut);
355
351
  return;
356
352
  }
357
353
  }
@@ -386,9 +382,7 @@ static DumpFunc null_funcs[] = {
386
382
  void oj_dump_null_val(VALUE obj, int depth, Out out) {
387
383
  int type = rb_type(obj);
388
384
 
389
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
390
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
391
- }
385
+ TRACE(out->opts->trace, "dump", obj, depth, TraceOut);
392
386
  if (MAX_DEPTH < depth) {
393
387
  rb_raise(rb_eNoMemError, "Too deeply nested.\n");
394
388
  }
@@ -397,14 +391,10 @@ void oj_dump_null_val(VALUE obj, int depth, Out out) {
397
391
 
398
392
  if (NULL != f) {
399
393
  f(obj, depth, out, false);
400
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
401
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
402
- }
394
+ TRACE(out->opts->trace, "dump", obj, depth, TraceOut);
403
395
  return;
404
396
  }
405
397
  }
406
398
  oj_dump_nil(Qnil, depth, out, false);
407
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
408
- oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
409
- }
399
+ TRACE(out->opts->trace, "dump", Qnil, depth, TraceOut);
410
400
  }
data/ext/oj/extconf.rb CHANGED
@@ -35,8 +35,16 @@ have_func('rb_hash_bulk_insert', 'ruby.h') unless '2' == version[0] && '6' == ve
35
35
  dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
36
36
 
37
37
  if with_config('--with-sse42')
38
- $CPPFLAGS += ' -msse4.2'
39
- dflags['OJ_USE_SSE4_2'] = 1
38
+ if try_cflags('-msse4.2')
39
+ $CPPFLAGS += ' -msse4.2'
40
+ dflags['OJ_USE_SSE4_2'] = 1
41
+ else
42
+ warn 'SSE 4.2 is not supported on this platform.'
43
+ end
44
+ end
45
+
46
+ if enable_config('trace-log', false)
47
+ dflags['OJ_ENABLE_TRACE_LOG'] = 1
40
48
  end
41
49
 
42
50
  dflags.each do |k,v|
data/ext/oj/fast.c CHANGED
@@ -74,7 +74,7 @@ static char *read_quoted_value(ParseInfo pi);
74
74
  static void skip_comment(ParseInfo pi);
75
75
 
76
76
  static VALUE protect_open_proc(VALUE x);
77
- static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated);
77
+ static VALUE parse_json(VALUE clas, char *json, bool given);
78
78
  static void each_leaf(Doc doc, VALUE self);
79
79
  static int move_step(Doc doc, const char *path, int loc);
80
80
  static Leaf get_doc_leaf(Doc doc, const char *path);
@@ -651,7 +651,8 @@ static void doc_free(Doc doc) {
651
651
  xfree(b);
652
652
  }
653
653
  }
654
- // xfree(f);
654
+ xfree(doc->json);
655
+ xfree(doc);
655
656
  }
656
657
  }
657
658
 
@@ -671,7 +672,6 @@ static void free_doc_cb(void *x) {
671
672
  Doc doc = (Doc)x;
672
673
 
673
674
  if (0 != doc) {
674
- xfree(doc->json);
675
675
  doc_free(doc);
676
676
  }
677
677
  }
@@ -749,20 +749,15 @@ static const rb_data_type_t oj_doc_type = {
749
749
  0,
750
750
  };
751
751
 
752
- static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated) {
752
+ static VALUE parse_json(VALUE clas, char *json, bool given) {
753
753
  struct _parseInfo pi;
754
754
  volatile VALUE result = Qnil;
755
755
  Doc doc;
756
756
  int ex = 0;
757
757
  volatile VALUE self;
758
758
 
759
- // TBD are both needed? is stack allocation ever needed?
759
+ doc = RB_ALLOC_N(struct _doc, 1);
760
760
 
761
- if (given) {
762
- doc = ALLOCA_N(struct _doc, 1);
763
- } else {
764
- doc = ALLOC(struct _doc);
765
- }
766
761
  // skip UTF-8 BOM if present
767
762
  if (0xEF == (uint8_t)*json && 0xBB == (uint8_t)json[1] && 0xBF == (uint8_t)json[2]) {
768
763
  pi.str = json + 3;
@@ -787,18 +782,20 @@ static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated) {
787
782
  }
788
783
  }
789
784
  #endif
785
+ doc->json = json;
790
786
  self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
791
787
  doc->self = self;
792
- doc->json = json;
793
788
  DATA_PTR(doc->self) = doc;
794
789
  result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
795
790
  if (given || 0 != ex) {
796
791
  DATA_PTR(doc->self) = NULL;
792
+ // TBD is this needed?
793
+ /*
797
794
  doc_free(pi.doc);
798
- if (allocated && 0 != ex) { // will jump so caller will not free
795
+ if (0 != ex) { // will jump so caller will not free
799
796
  xfree(json);
800
797
  }
801
- rb_gc_enable();
798
+ */
802
799
  } else {
803
800
  result = doc->self;
804
801
  }
@@ -1092,27 +1089,19 @@ static VALUE doc_open(VALUE clas, VALUE str) {
1092
1089
  size_t len;
1093
1090
  volatile VALUE obj;
1094
1091
  int given = rb_block_given_p();
1095
- int allocate;
1096
1092
 
1097
1093
  Check_Type(str, T_STRING);
1098
1094
  len = (int)RSTRING_LEN(str) + 1;
1099
- allocate = (SMALL_JSON < len || !given);
1100
- if (allocate) {
1101
- json = ALLOC_N(char, len);
1102
- } else {
1103
- json = ALLOCA_N(char, len);
1104
- }
1105
- // It should not be necessaary to stop GC but if it is not stopped and a
1106
- // large string is parsed that string is corrupted or freed during
1107
- // parsing. I'm not sure what is going on exactly but disabling GC avoids
1108
- // the issue.
1109
- rb_gc_disable();
1095
+ json = RB_ALLOC_N(char, len);
1096
+
1110
1097
  memcpy(json, StringValuePtr(str), len);
1111
- obj = parse_json(clas, json, given, allocate);
1112
- rb_gc_enable();
1113
- if (given && allocate) {
1098
+ obj = parse_json(clas, json, given);
1099
+ // TBD is this needed
1100
+ /*
1101
+ if (given) {
1114
1102
  xfree(json);
1115
1103
  }
1104
+ */
1116
1105
  return obj;
1117
1106
  }
1118
1107
 
@@ -1142,7 +1131,6 @@ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1142
1131
  size_t len;
1143
1132
  volatile VALUE obj;
1144
1133
  int given = rb_block_given_p();
1145
- int allocate;
1146
1134
 
1147
1135
  Check_Type(filename, T_STRING);
1148
1136
  path = StringValuePtr(filename);
@@ -1151,12 +1139,8 @@ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1151
1139
  }
1152
1140
  fseek(f, 0, SEEK_END);
1153
1141
  len = ftell(f);
1154
- allocate = (SMALL_JSON < len || !given);
1155
- if (allocate) {
1156
- json = ALLOC_N(char, len + 1);
1157
- } else {
1158
- json = ALLOCA_N(char, len + 1);
1159
- }
1142
+ json = RB_ALLOC_N(char, len + 1);
1143
+
1160
1144
  fseek(f, 0, SEEK_SET);
1161
1145
  if (len != fread(json, 1, len, f)) {
1162
1146
  fclose(f);
@@ -1167,12 +1151,13 @@ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1167
1151
  }
1168
1152
  fclose(f);
1169
1153
  json[len] = '\0';
1170
- rb_gc_disable();
1171
- obj = parse_json(clas, json, given, allocate);
1172
- rb_gc_enable();
1173
- if (given && allocate) {
1154
+ obj = parse_json(clas, json, given);
1155
+ // TBD is this needed
1156
+ /*
1157
+ if (given) {
1174
1158
  xfree(json);
1175
1159
  }
1160
+ */
1176
1161
  return obj;
1177
1162
  }
1178
1163
 
@@ -1656,11 +1641,9 @@ static VALUE doc_close(VALUE self) {
1656
1641
  Doc doc = self_doc(self);
1657
1642
 
1658
1643
  rb_gc_unregister_address(&doc->self);
1659
- DATA_PTR(doc->self) = 0;
1644
+ DATA_PTR(doc->self) = NULL;
1660
1645
  if (0 != doc) {
1661
- xfree(doc->json);
1662
1646
  doc_free(doc);
1663
- xfree(doc);
1664
1647
  }
1665
1648
  return Qnil;
1666
1649
  }
data/ext/oj/object.c CHANGED
@@ -609,9 +609,7 @@ WHICH_TYPE:
609
609
  }
610
610
 
611
611
  static VALUE start_hash(ParseInfo pi) {
612
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
613
- oj_trace_parse_in("start_hash", pi, __FILE__, __LINE__);
614
- }
612
+ TRACE_PARSE_IN(pi->options.trace, "start_hash", pi);
615
613
  return Qnil;
616
614
  }
617
615
 
@@ -627,9 +625,7 @@ static void end_hash(ParseInfo pi) {
627
625
  oj_odd_free(oa);
628
626
  parent->odd_args = NULL;
629
627
  }
630
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
631
- oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
632
- }
628
+ TRACE_PARSE_HASH_END(pi->options.trace, pi);
633
629
  }
634
630
 
635
631
  static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
data/ext/oj/oj.h CHANGED
@@ -379,6 +379,12 @@ extern bool oj_use_hash_alt;
379
379
  extern bool oj_use_array_alt;
380
380
  extern bool string_writer_optimized;
381
381
 
382
+ static inline VALUE oj_safe_string_convert(VALUE obj) {
383
+ VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
384
+ StringValue(rstr);
385
+ return rstr;
386
+ }
387
+
382
388
  #define APPEND_CHARS(buffer, chars, size) \
383
389
  { \
384
390
  memcpy(buffer, chars, size); \
data/ext/oj/parse.c CHANGED
@@ -16,6 +16,10 @@
16
16
  #include "rxclass.h"
17
17
  #include "val_stack.h"
18
18
 
19
+ #ifdef OJ_USE_SSE4_2
20
+ #include <nmmintrin.h>
21
+ #endif
22
+
19
23
  // Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
20
24
  #define OJ_INFINITY (1.0 / 0.0)
21
25
 
data/ext/oj/parser.c CHANGED
@@ -1263,7 +1263,7 @@ static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
1263
1263
  // from this function. A delegate must be added before the parser can be
1264
1264
  // used. Optionally oj_parser_set_options can be called if the options are not
1265
1265
  // set directly.
1266
- VALUE oj_parser_new() {
1266
+ VALUE oj_parser_new(void) {
1267
1267
  ojParser p = ALLOC(struct _ojParser);
1268
1268
 
1269
1269
  #if HAVE_RB_EXT_RACTOR_SAFE
data/ext/oj/rails.c CHANGED
@@ -198,7 +198,7 @@ static void dump_enumerable(VALUE obj, int depth, Out out, bool as_ok) {
198
198
  }
199
199
 
200
200
  static void dump_bigdecimal(VALUE obj, int depth, Out out, bool as_ok) {
201
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
201
+ volatile VALUE rstr = oj_safe_string_convert(obj);
202
202
  const char * str = RSTRING_PTR(rstr);
203
203
 
204
204
  if ('I' == *str || 'N' == *str || ('-' == *str && 'I' == str[1])) {
@@ -345,7 +345,7 @@ static void dump_timewithzone(VALUE obj, int depth, Out out, bool as_ok) {
345
345
  }
346
346
 
347
347
  static void dump_to_s(VALUE obj, int depth, Out out, bool as_ok) {
348
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
348
+ volatile VALUE rstr = oj_safe_string_convert(obj);
349
349
 
350
350
  oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
351
351
  }
@@ -377,7 +377,7 @@ static StrLen columns_array(VALUE rcols, int *ccnt) {
377
377
  for (i = 0, cp = cols; i < cnt; i++, cp++) {
378
378
  v = RARRAY_AREF(rcols, i);
379
379
  if (T_STRING != rb_type(v)) {
380
- v = rb_funcall(v, oj_to_s_id, 0);
380
+ v = oj_safe_string_convert(v);
381
381
  }
382
382
  cp->str = StringValuePtr(v);
383
383
  cp->len = (int)RSTRING_LEN(v);
@@ -517,9 +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 (RB_UNLIKELY(Yes == out->opts->trace)) {
521
- oj_trace("as_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyIn);
522
- }
520
+ TRACE(out->opts->trace, "as_json", obj, depth + 1, TraceRubyIn);
523
521
  // Some classes elect to not take an options argument so check the arity
524
522
  // of as_json.
525
523
  if (0 == rb_obj_method_arity(obj, oj_as_json_id)) {
@@ -527,9 +525,7 @@ static void dump_as_json(VALUE obj, int depth, Out out, bool as_ok) {
527
525
  } else {
528
526
  ja = rb_funcall2(obj, oj_as_json_id, out->argc, out->argv);
529
527
  }
530
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
531
- oj_trace("as_json", obj, __FILE__, __LINE__, depth + 1, TraceRubyOut);
532
- }
528
+ TRACE(out->opts->trace, "as_json", obj, depth + 1, TraceRubyOut);
533
529
 
534
530
  out->argc = 0;
535
531
  if (ja == obj || !as_ok) {
@@ -1208,7 +1204,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1208
1204
  } else if (oj_rails_float_opt) {
1209
1205
  cnt = oj_dump_float_printf(buf, sizeof(buf), obj, d, "%0.16g");
1210
1206
  } else {
1211
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
1207
+ volatile VALUE rstr = oj_safe_string_convert(obj);
1212
1208
 
1213
1209
  strcpy(buf, RSTRING_PTR(rstr));
1214
1210
  cnt = (int)RSTRING_LEN(rstr);
@@ -1301,7 +1297,7 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
1301
1297
  return ST_CONTINUE;
1302
1298
  }
1303
1299
  if (rtype != T_STRING && rtype != T_SYMBOL) {
1304
- key = rb_funcall(key, oj_to_s_id, 0);
1300
+ key = oj_safe_string_convert(key);
1305
1301
  rtype = rb_type(key);
1306
1302
  }
1307
1303
  if (!out->opts->dump_opts.use) {
@@ -1464,9 +1460,7 @@ static DumpFunc rails_funcs[] = {
1464
1460
  static void dump_rails_val(VALUE obj, int depth, Out out, bool as_ok) {
1465
1461
  int type = rb_type(obj);
1466
1462
 
1467
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
1468
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
1469
- }
1463
+ TRACE(out->opts->trace, "dump", obj, depth, TraceIn);
1470
1464
  if (MAX_DEPTH < depth) {
1471
1465
  rb_raise(rb_eNoMemError, "Too deeply nested.\n");
1472
1466
  }
@@ -1475,16 +1469,12 @@ static void dump_rails_val(VALUE obj, int depth, Out out, bool as_ok) {
1475
1469
 
1476
1470
  if (NULL != f) {
1477
1471
  f(obj, depth, out, as_ok);
1478
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
1479
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
1480
- }
1472
+ TRACE(out->opts->trace, "dump", obj, depth, TraceOut);
1481
1473
  return;
1482
1474
  }
1483
1475
  }
1484
1476
  oj_dump_nil(Qnil, depth, out, false);
1485
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
1486
- oj_trace("dump", Qnil, __FILE__, __LINE__, depth, TraceOut);
1487
- }
1477
+ TRACE(out->opts->trace, "dump", Qnil, depth, TraceOut);
1488
1478
  }
1489
1479
 
1490
1480
  void oj_dump_rails_val(VALUE obj, int depth, Out out) {
data/ext/oj/reader.c CHANGED
@@ -75,7 +75,7 @@ void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s) {
75
75
  reader->read_func = read_from_io;
76
76
  reader->io = io;
77
77
  } else if (to_s) {
78
- volatile VALUE rstr = rb_funcall(io, oj_to_s_id, 0);
78
+ volatile VALUE rstr = oj_safe_string_convert(io);
79
79
 
80
80
  reader->read_func = 0;
81
81
  reader->in_str = StringValuePtr(rstr);
data/ext/oj/strict.c CHANGED
@@ -50,15 +50,11 @@ VALUE oj_calc_hash_key(ParseInfo pi, Val parent) {
50
50
  }
51
51
 
52
52
  static void hash_end(ParseInfo pi) {
53
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
54
- oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
55
- }
53
+ TRACE_PARSE_HASH_END(pi->options.trace, pi);
56
54
  }
57
55
 
58
56
  static void array_end(ParseInfo pi) {
59
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
60
- oj_trace_parse_array_end(pi, __FILE__, __LINE__);
61
- }
57
+ TRACE_PARSE_ARRAY_END(pi->options.trace, pi);
62
58
  }
63
59
 
64
60
  static VALUE noop_hash_key(ParseInfo pi, const char *key, size_t klen) {
@@ -95,9 +91,7 @@ static VALUE start_hash(ParseInfo pi) {
95
91
  if (Qnil != pi->options.hash_class) {
96
92
  return rb_class_new_instance(0, NULL, pi->options.hash_class);
97
93
  }
98
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
99
- oj_trace_parse_in("start_hash", pi, __FILE__, __LINE__);
100
- }
94
+ TRACE_PARSE_IN(pi->options.trace, "start_hash", pi);
101
95
  return rb_hash_new();
102
96
  }
103
97
 
@@ -137,9 +131,7 @@ static void hash_set_value(ParseInfo pi, Val parent, VALUE value) {
137
131
  }
138
132
 
139
133
  static VALUE start_array(ParseInfo pi) {
140
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
141
- oj_trace_parse_in("start_array", pi, __FILE__, __LINE__);
142
- }
134
+ TRACE_PARSE_IN(pi->options.trace, "start_array", pi);
143
135
  return rb_ary_new();
144
136
  }
145
137
 
data/ext/oj/trace.h CHANGED
@@ -25,4 +25,20 @@ extern void
25
25
  extern void oj_trace_parse_hash_end(struct _parseInfo *pi, const char *file, int line);
26
26
  extern void oj_trace_parse_array_end(struct _parseInfo *pi, const char *file, int line);
27
27
 
28
+
29
+ #ifdef OJ_ENABLE_TRACE_LOG
30
+ #define TRACE(option, func, obj, depth, where) if (RB_UNLIKELY(Yes == option)) { oj_trace(func, obj, __FILE__, __LINE__, depth, where); }
31
+ #define TRACE_PARSE_IN(option, func, pi) if (RB_UNLIKELY(Yes == option)) { oj_trace_parse_in(func, pi, __FILE__, __LINE__); }
32
+ #define TRACE_PARSE_CALL(option, func, pi, obj) if (RB_UNLIKELY(Yes == option)) { oj_trace_parse_call(func, pi, __FILE__, __LINE__, obj); }
33
+ #define TRACE_PARSE_HASH_END(option, pi) if (RB_UNLIKELY(Yes == option)) { oj_trace_parse_hash_end(pi, __FILE__, __LINE__); }
34
+ #define TRACE_PARSE_ARRAY_END(option, pi) if (RB_UNLIKELY(Yes == option)) { oj_trace_parse_array_end(pi, __FILE__, __LINE__); }
35
+ #else
36
+ #define TRACE(option, func, obj, depth, where)
37
+ #define TRACE_PARSE_IN(option, func, pi)
38
+ #define TRACE_PARSE_CALL(option, func, pi, obj)
39
+ #define TRACE_PARSE_HASH_END(option, pi)
40
+ #define TRACE_PARSE_ARRAY_END(option, pi)
41
+ #endif
42
+
43
+
28
44
  #endif /* OJ_TRACE_H */
data/ext/oj/wab.c CHANGED
@@ -226,13 +226,13 @@ static void dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
226
226
  if (rb_cTime == clas) {
227
227
  dump_time(obj, out);
228
228
  } else if (oj_bigdecimal_class == clas) {
229
- volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
229
+ volatile VALUE rstr = oj_safe_string_convert(obj);
230
230
 
231
231
  oj_dump_raw(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), out);
232
232
  } else if (resolve_wab_uuid_class() == clas) {
233
- oj_dump_str(rb_funcall(obj, oj_to_s_id, 0), depth, out, false);
233
+ oj_dump_str(oj_safe_string_convert(obj), depth, out, false);
234
234
  } else if (resolve_uri_http_class() == clas) {
235
- oj_dump_str(rb_funcall(obj, oj_to_s_id, 0), depth, out, false);
235
+ oj_dump_str(oj_safe_string_convert(obj), depth, out, false);
236
236
  } else {
237
237
  raise_wab(obj);
238
238
  }
@@ -266,9 +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 (RB_UNLIKELY(Yes == out->opts->trace)) {
270
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceIn);
271
- }
269
+ TRACE(out->opts->trace, "dump", obj, depth, TraceIn);
272
270
  if (MAX_DEPTH < depth) {
273
271
  rb_raise(rb_eNoMemError, "Too deeply nested.\n");
274
272
  }
@@ -277,9 +275,7 @@ void oj_dump_wab_val(VALUE obj, int depth, Out out) {
277
275
 
278
276
  if (NULL != f) {
279
277
  f(obj, depth, out, false);
280
- if (RB_UNLIKELY(Yes == out->opts->trace)) {
281
- oj_trace("dump", obj, __FILE__, __LINE__, depth, TraceOut);
282
- }
278
+ TRACE(out->opts->trace, "dump", obj, depth, TraceOut);
283
279
  return;
284
280
  }
285
281
  }
@@ -312,15 +308,11 @@ static VALUE calc_hash_key(ParseInfo pi, Val parent) {
312
308
  }
313
309
 
314
310
  static void hash_end(ParseInfo pi) {
315
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
316
- oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
317
- }
311
+ TRACE_PARSE_HASH_END(pi->options.trace, pi);
318
312
  }
319
313
 
320
314
  static void array_end(ParseInfo pi) {
321
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
322
- oj_trace_parse_array_end(pi, __FILE__, __LINE__);
323
- }
315
+ TRACE_PARSE_ARRAY_END(pi->options.trace, pi);
324
316
  }
325
317
 
326
318
  static VALUE noop_hash_key(ParseInfo pi, const char *key, size_t klen) {
@@ -494,9 +486,7 @@ static void add_num(ParseInfo pi, NumInfo ni) {
494
486
  }
495
487
 
496
488
  static VALUE start_hash(ParseInfo pi) {
497
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
498
- oj_trace_parse_in("start_hash", pi, __FILE__, __LINE__);
499
- }
489
+ TRACE_PARSE_IN(pi->options.trace, "start_hash", pi);
500
490
  if (Qnil != pi->options.hash_class) {
501
491
  return rb_class_new_instance(0, NULL, pi->options.hash_class);
502
492
  }
@@ -533,9 +523,7 @@ static void hash_set_value(ParseInfo pi, Val parent, VALUE value) {
533
523
  }
534
524
 
535
525
  static VALUE start_array(ParseInfo pi) {
536
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
537
- oj_trace_parse_in("start_array", pi, __FILE__, __LINE__);
538
- }
526
+ TRACE_PARSE_IN(pi->options.trace, "start_array", pi);
539
527
  return rb_ary_new();
540
528
  }
541
529
 
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.23'
4
+ VERSION = '3.14.0'
5
5
  end
@@ -0,0 +1,20 @@
1
+ # Oj Install Options
2
+
3
+ ### Enable trace log
4
+
5
+ ```
6
+ $ gem install oj -- --enable-trace-log
7
+ ```
8
+
9
+ To enable Oj trace feature, it uses `--enable-trace-log` option when installing the gem.
10
+ Then, the trace logs will be displayed when `:trace` option is set to `true`.
11
+
12
+
13
+ ### Enable SIMD instructions
14
+
15
+ ```
16
+ $ gem install oj -- --with-sse42
17
+ ```
18
+
19
+ To enable the use of SIMD instructions in Oj, it uses the `--with-sse42` option when installing the gem.
20
+ This will enable the use of the SSE4.2 instructions in the internal.
data/test/foo.rb CHANGED
@@ -5,73 +5,25 @@ $: << File.join(File.dirname(__FILE__), "../lib")
5
5
  $: << File.join(File.dirname(__FILE__), "../ext")
6
6
 
7
7
  require "oj"
8
- require "socket"
9
- require 'io/nonblock'
10
8
 
11
- #pid = spawn("nc -d 0.1 -l 5000", out: "/dev/null")
12
- pid = spawn("nc -i 1 -l 7777", out: "/dev/null")
13
- at_exit { Process.kill 9, pid }
14
- sleep 0.2
15
- s = Socket.tcp("localhost", 7777)
16
- s.nonblock = false
17
- 1_000_000.times do |x|
18
- Oj.to_stream(s, { x: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]})
19
- end
20
9
 
21
- =begin
22
- IO.pipe do |r, w|
23
- if fork
24
- r.close
25
- #w.nonblock = false
26
- 1_000_000.times do |i|
27
- begin
28
- Oj.to_stream(w, { x: i})
29
- rescue IOError => e
30
- puts "*** #{i} raised #{e.class}: #{e}"
31
- IO.select(nil, [w])
32
- retry
33
- end
34
- w.puts
35
- end
36
- else
37
- w.close
38
- sleep(0.1)
39
- r.each_line { |b|
40
- #print b
41
- }
42
- r.close
43
- Process.exit(0)
44
- end
45
- end
46
- =end
10
+ p = Oj::Parser.new(:saj)
11
+
12
+ json = %|{
13
+ "array": [
14
+ {
15
+ "num" : 3,
16
+ "string": "message",
17
+ "hash" : {
18
+ "h2" : {
19
+ "a" : [ 1, 2, 3 ]
20
+ }
21
+ }
22
+ }
23
+ ],
24
+ "boolean" : true
25
+ }|
47
26
 
48
- =begin
49
- IO.pipe do |r, w|
50
- if fork
51
- r.close
52
- #w.nonblock = false
53
- a = []
54
- 10_000.times do |i|
55
- a << i
56
- end
57
- begin
58
- Oj.to_stream(w, a, indent: 2)
59
- rescue IOError => e
60
- puts "*** raised #{e.class}: #{e}"
61
- puts "*** fileno: #{w.fileno}"
62
- puts "*** is an IO?: #{w.kind_of?(IO)}"
63
- IO.select(nil, [w])
64
- retry
65
- end
66
- w.puts
67
- else
68
- w.close
69
- sleep(0.5)
70
- r.each_line { |b|
71
- #print b
72
- }
73
- r.close
74
- Process.exit(0)
75
- end
27
+ 1_000_000.times do
28
+ e = Oj.load(json)
76
29
  end
77
- =end
data/test/test_compat.rb CHANGED
@@ -513,6 +513,15 @@ class CompatJuice < Minitest::Test
513
513
  assert_equal("aaaa\nbbbb\rcccc\tddd\feee\bf/\\ぴーたー ", Oj.load(json))
514
514
  end
515
515
 
516
+ def test_invalid_to_s
517
+ obj = Object.new
518
+ def obj.to_s
519
+ nil
520
+ end
521
+
522
+ assert_raises(TypeError) { Oj.dump(obj, mode: :compat) }
523
+ end
524
+
516
525
  def dump_and_load(obj, trace=false)
517
526
  json = Oj.dump(obj)
518
527
  puts json if trace
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.23
4
+ version: 3.14.0
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-11-06 00:00:00.000000000 Z
11
+ date: 2023-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler
@@ -72,6 +72,7 @@ extra_rdoc_files:
72
72
  - pages/Compatibility.md
73
73
  - pages/Custom.md
74
74
  - pages/Encoding.md
75
+ - pages/InstallOptions.md
75
76
  - pages/JsonGem.md
76
77
  - pages/Modes.md
77
78
  - pages/Options.md
@@ -161,6 +162,7 @@ files:
161
162
  - pages/Compatibility.md
162
163
  - pages/Custom.md
163
164
  - pages/Encoding.md
165
+ - pages/InstallOptions.md
164
166
  - pages/JsonGem.md
165
167
  - pages/Modes.md
166
168
  - pages/Options.md