oj 3.16.0 → 3.17.3

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.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +107 -0
  3. data/README.md +0 -16
  4. data/ext/oj/cache.c +4 -2
  5. data/ext/oj/cache.h +3 -2
  6. data/ext/oj/compat.c +3 -3
  7. data/ext/oj/custom.c +17 -11
  8. data/ext/oj/dump.c +624 -90
  9. data/ext/oj/dump.h +9 -2
  10. data/ext/oj/dump_compat.c +15 -9
  11. data/ext/oj/dump_leaf.c +1 -1
  12. data/ext/oj/dump_object.c +32 -17
  13. data/ext/oj/dump_strict.c +20 -14
  14. data/ext/oj/extconf.rb +11 -9
  15. data/ext/oj/fast.c +50 -32
  16. data/ext/oj/intern.c +24 -7
  17. data/ext/oj/mimic_json.c +17 -11
  18. data/ext/oj/object.c +21 -13
  19. data/ext/oj/oj.c +387 -140
  20. data/ext/oj/oj.h +59 -55
  21. data/ext/oj/parse.c +248 -47
  22. data/ext/oj/parser.c +179 -89
  23. data/ext/oj/parser.h +4 -2
  24. data/ext/oj/rails.c +64 -42
  25. data/ext/oj/reader.c +1 -1
  26. data/ext/oj/rxclass.c +1 -1
  27. data/ext/oj/rxclass.h +1 -1
  28. data/ext/oj/safe.c +230 -0
  29. data/ext/oj/safe.h +79 -0
  30. data/ext/oj/saj.c +15 -10
  31. data/ext/oj/scp.c +3 -6
  32. data/ext/oj/simd.h +219 -0
  33. data/ext/oj/sparse.c +6 -3
  34. data/ext/oj/stream_writer.c +38 -28
  35. data/ext/oj/strict.c +2 -4
  36. data/ext/oj/string_writer.c +54 -22
  37. data/ext/oj/usual.c +57 -35
  38. data/ext/oj/usual.h +1 -0
  39. data/ext/oj/val_stack.c +13 -2
  40. data/ext/oj/wab.c +5 -4
  41. data/lib/oj/mimic.rb +1 -5
  42. data/lib/oj/schandler.rb +5 -4
  43. data/lib/oj/version.rb +1 -1
  44. data/pages/Encoding.md +1 -1
  45. metadata +36 -100
  46. data/test/_test_active.rb +0 -75
  47. data/test/_test_active_mimic.rb +0 -95
  48. data/test/_test_mimic_rails.rb +0 -123
  49. data/test/activerecord/result_test.rb +0 -31
  50. data/test/activesupport6/abstract_unit.rb +0 -44
  51. data/test/activesupport6/decoding_test.rb +0 -133
  52. data/test/activesupport6/encoding_test.rb +0 -507
  53. data/test/activesupport6/encoding_test_cases.rb +0 -98
  54. data/test/activesupport6/test_common.rb +0 -17
  55. data/test/activesupport6/test_helper.rb +0 -163
  56. data/test/activesupport6/time_zone_test_helpers.rb +0 -39
  57. data/test/activesupport7/abstract_unit.rb +0 -49
  58. data/test/activesupport7/decoding_test.rb +0 -125
  59. data/test/activesupport7/encoding_test.rb +0 -486
  60. data/test/activesupport7/encoding_test_cases.rb +0 -104
  61. data/test/activesupport7/time_zone_test_helpers.rb +0 -47
  62. data/test/files.rb +0 -29
  63. data/test/foo.rb +0 -14
  64. data/test/helper.rb +0 -39
  65. data/test/isolated/shared.rb +0 -309
  66. data/test/isolated/test_mimic_after.rb +0 -13
  67. data/test/isolated/test_mimic_alone.rb +0 -12
  68. data/test/isolated/test_mimic_as_json.rb +0 -45
  69. data/test/isolated/test_mimic_before.rb +0 -13
  70. data/test/isolated/test_mimic_define.rb +0 -28
  71. data/test/isolated/test_mimic_rails_after.rb +0 -22
  72. data/test/isolated/test_mimic_rails_before.rb +0 -21
  73. data/test/isolated/test_mimic_redefine.rb +0 -15
  74. data/test/json_gem/json_addition_test.rb +0 -216
  75. data/test/json_gem/json_common_interface_test.rb +0 -155
  76. data/test/json_gem/json_encoding_test.rb +0 -107
  77. data/test/json_gem/json_ext_parser_test.rb +0 -21
  78. data/test/json_gem/json_fixtures_test.rb +0 -36
  79. data/test/json_gem/json_generator_test.rb +0 -407
  80. data/test/json_gem/json_generic_object_test.rb +0 -90
  81. data/test/json_gem/json_parser_test.rb +0 -477
  82. data/test/json_gem/json_string_matching_test.rb +0 -42
  83. data/test/json_gem/test_helper.rb +0 -30
  84. data/test/mem.rb +0 -34
  85. data/test/perf.rb +0 -102
  86. data/test/perf_compat.rb +0 -128
  87. data/test/perf_dump.rb +0 -50
  88. data/test/perf_fast.rb +0 -162
  89. data/test/perf_file.rb +0 -62
  90. data/test/perf_object.rb +0 -134
  91. data/test/perf_once.rb +0 -59
  92. data/test/perf_parser.rb +0 -183
  93. data/test/perf_saj.rb +0 -101
  94. data/test/perf_scp.rb +0 -140
  95. data/test/perf_simple.rb +0 -289
  96. data/test/perf_strict.rb +0 -137
  97. data/test/perf_wab.rb +0 -129
  98. data/test/prec.rb +0 -23
  99. data/test/sample/change.rb +0 -13
  100. data/test/sample/dir.rb +0 -18
  101. data/test/sample/doc.rb +0 -35
  102. data/test/sample/file.rb +0 -47
  103. data/test/sample/group.rb +0 -15
  104. data/test/sample/hasprops.rb +0 -15
  105. data/test/sample/layer.rb +0 -11
  106. data/test/sample/line.rb +0 -20
  107. data/test/sample/oval.rb +0 -10
  108. data/test/sample/rect.rb +0 -9
  109. data/test/sample/shape.rb +0 -34
  110. data/test/sample/text.rb +0 -19
  111. data/test/sample.rb +0 -54
  112. data/test/sample_json.rb +0 -37
  113. data/test/test_compat.rb +0 -567
  114. data/test/test_custom.rb +0 -554
  115. data/test/test_debian.rb +0 -50
  116. data/test/test_fast.rb +0 -526
  117. data/test/test_file.rb +0 -250
  118. data/test/test_gc.rb +0 -60
  119. data/test/test_generate.rb +0 -21
  120. data/test/test_hash.rb +0 -39
  121. data/test/test_integer_range.rb +0 -72
  122. data/test/test_null.rb +0 -376
  123. data/test/test_object.rb +0 -1016
  124. data/test/test_parser.rb +0 -11
  125. data/test/test_parser_debug.rb +0 -27
  126. data/test/test_parser_saj.rb +0 -337
  127. data/test/test_parser_usual.rb +0 -217
  128. data/test/test_rails.rb +0 -35
  129. data/test/test_saj.rb +0 -188
  130. data/test/test_scp.rb +0 -431
  131. data/test/test_strict.rb +0 -431
  132. data/test/test_various.rb +0 -801
  133. data/test/test_wab.rb +0 -311
  134. data/test/test_writer.rb +0 -380
  135. data/test/tests.rb +0 -33
  136. data/test/tests_mimic.rb +0 -23
  137. data/test/tests_mimic_addition.rb +0 -16
data/ext/oj/dump.h CHANGED
@@ -7,12 +7,17 @@
7
7
  #include <ruby.h>
8
8
 
9
9
  #include "oj.h"
10
+ #include "simd.h"
10
11
 
11
12
  #define MAX_DEPTH 1000
12
13
 
13
14
  // Extra padding at end of buffer.
14
15
  #define BUFFER_EXTRA 64
15
16
 
17
+ #ifdef HAVE_SIMD_NEON
18
+ extern void initialize_neon(void);
19
+ #endif /* HAVE_SIMD_NEON */
20
+
16
21
  extern void oj_dump_nil(VALUE obj, int depth, Out out, bool as_ok);
17
22
  extern void oj_dump_true(VALUE obj, int depth, Out out, bool as_ok);
18
23
  extern void oj_dump_false(VALUE obj, int depth, Out out, bool as_ok);
@@ -30,7 +35,7 @@ extern void oj_dump_xml_time(VALUE obj, Out out);
30
35
  extern void oj_dump_time(VALUE obj, Out out, int withZone);
31
36
  extern void oj_dump_obj_to_s(VALUE obj, Out out);
32
37
 
33
- extern const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp);
38
+ extern const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, size_t *lenp);
34
39
 
35
40
  // initialize an out buffer with the provided stack allocated memory
36
41
  extern void oj_out_init(Out out);
@@ -53,10 +58,12 @@ extern void oj_dump_raw_json(VALUE obj, int depth, Out out);
53
58
  extern VALUE oj_add_to_json(int argc, VALUE *argv, VALUE self);
54
59
  extern VALUE oj_remove_to_json(int argc, VALUE *argv, VALUE self);
55
60
 
56
- extern int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format);
61
+ extern size_t oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format);
57
62
 
58
63
  extern time_t oj_sec_from_time_hard_way(VALUE obj);
59
64
 
65
+ extern bool oj_key_skip(VALUE key, const char *only, const char *except);
66
+
60
67
  inline static void assure_size(Out out, size_t len) {
61
68
  if (out->end - out->cur <= (long)len) {
62
69
  oj_grow_out(out, len);
data/ext/oj/dump_compat.c CHANGED
@@ -103,7 +103,7 @@ static void dump_values_array(VALUE *values, int depth, Out out) {
103
103
  static void dump_to_json(VALUE obj, Out out) {
104
104
  volatile VALUE rs;
105
105
  const char *s;
106
- int len;
106
+ size_t len;
107
107
 
108
108
  TRACE(out->opts->trace, "to_json", obj, 0, TraceRubyIn);
109
109
  if (0 == rb_obj_method_arity(obj, oj_to_json_id)) {
@@ -115,7 +115,7 @@ static void dump_to_json(VALUE obj, Out out) {
115
115
 
116
116
  StringValue(rs);
117
117
  s = RSTRING_PTR(rs);
118
- len = (int)RSTRING_LEN(rs);
118
+ len = RSTRING_LEN(rs);
119
119
 
120
120
  assure_size(out, len + 1);
121
121
  APPEND_CHARS(out->cur, s, len);
@@ -124,7 +124,8 @@ static void dump_to_json(VALUE obj, Out out) {
124
124
 
125
125
  static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
126
126
  size_t size;
127
- int i, cnt;
127
+ size_t i;
128
+ size_t cnt;
128
129
  int d2 = depth + 1;
129
130
  long id = oj_check_circular(a, out);
130
131
 
@@ -136,7 +137,7 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
136
137
  dump_to_json(a, out);
137
138
  return;
138
139
  }
139
- cnt = (int)RARRAY_LEN(a);
140
+ cnt = RARRAY_LEN(a);
140
141
  *out->cur++ = '[';
141
142
  assure_size(out, 2);
142
143
  if (0 == cnt) {
@@ -147,10 +148,10 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
147
148
  } else {
148
149
  size = d2 * out->indent + 2;
149
150
  }
150
- assure_size(out, size * cnt);
151
151
  cnt--;
152
152
  for (i = 0; i <= cnt; i++) {
153
153
  if (out->opts->dump_opts.use) {
154
+ assure_size(out, size);
154
155
  if (0 < out->opts->dump_opts.array_size) {
155
156
  APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
156
157
  }
@@ -552,7 +553,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
552
553
  char buf[64];
553
554
  char *b;
554
555
  double d = rb_num2dbl(obj);
555
- int cnt = 0;
556
+ size_t cnt = 0;
556
557
 
557
558
  if (0.0 == d) {
558
559
  b = buf;
@@ -590,7 +591,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
590
591
  volatile VALUE rstr = oj_safe_string_convert(obj);
591
592
 
592
593
  strcpy(buf, RSTRING_PTR(rstr));
593
- cnt = (int)RSTRING_LEN(rstr);
594
+ cnt = RSTRING_LEN(rstr);
594
595
  }
595
596
  assure_size(out, cnt);
596
597
  APPEND_CHARS(out->cur, buf, cnt);
@@ -604,6 +605,11 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
604
605
  if (out->omit_nil && Qnil == value) {
605
606
  return ST_CONTINUE;
606
607
  }
608
+ if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
609
+ if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
610
+ return ST_CONTINUE;
611
+ }
612
+ }
607
613
  if (!out->opts->dump_opts.use) {
608
614
  assure_size(out, depth * out->indent + 1);
609
615
  fill_indent(out, depth);
@@ -800,7 +806,7 @@ static void dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
800
806
  // rb_big2str can not so unless overridden by using add_to_json(Integer)
801
807
  // this must use to_s to pass the json gem unit tests.
802
808
  volatile VALUE rs;
803
- int cnt;
809
+ size_t cnt;
804
810
  bool dump_as_string = false;
805
811
 
806
812
  if (use_bignum_alt) {
@@ -809,7 +815,7 @@ static void dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
809
815
  rs = oj_safe_string_convert(obj);
810
816
  }
811
817
  rb_check_type(rs, T_STRING);
812
- cnt = (int)RSTRING_LEN(rs);
818
+ cnt = RSTRING_LEN(rs);
813
819
 
814
820
  if (out->opts->int_range_min != 0 || out->opts->int_range_max != 0) {
815
821
  dump_as_string = true; // Bignum cannot be inside of Fixnum range
data/ext/oj/dump_leaf.c CHANGED
@@ -17,7 +17,7 @@ inline static void dump_chars(const char *s, size_t size, Out out) {
17
17
  static void dump_leaf_str(Leaf leaf, Out out) {
18
18
  switch (leaf->value_type) {
19
19
  case STR_VAL: oj_dump_cstr(leaf->str, strlen(leaf->str), 0, 0, out); break;
20
- case RUBY_VAL: oj_dump_cstr(StringValueCStr(leaf->value), (int)RSTRING_LEN(leaf->value), 0, 0, out); break;
20
+ case RUBY_VAL: oj_dump_cstr(StringValueCStr(leaf->value), RSTRING_LEN(leaf->value), 0, 0, out); break;
21
21
  case COL_VAL:
22
22
  default: rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type); break;
23
23
  }
data/ext/oj/dump_object.c CHANGED
@@ -33,7 +33,7 @@ static void dump_data(VALUE obj, int depth, Out out, bool as_ok) {
33
33
  if (oj_bigdecimal_class == clas) {
34
34
  volatile VALUE rstr = oj_safe_string_convert(obj);
35
35
  const char *str = RSTRING_PTR(rstr);
36
- int len = (int)RSTRING_LEN(rstr);
36
+ size_t len = RSTRING_LEN(rstr);
37
37
 
38
38
  if (No != out->opts->bigdec_as_num) {
39
39
  oj_dump_raw(str, len, out);
@@ -62,7 +62,7 @@ static void dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
62
62
  if (oj_bigdecimal_class == clas) {
63
63
  volatile VALUE rstr = oj_safe_string_convert(obj);
64
64
  const char *str = RSTRING_PTR(rstr);
65
- int len = (int)RSTRING_LEN(rstr);
65
+ size_t len = RSTRING_LEN(rstr);
66
66
 
67
67
  if (0 == strcasecmp("Infinity", str)) {
68
68
  str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
@@ -95,7 +95,8 @@ static void dump_class(VALUE obj, int depth, Out out, bool as_ok) {
95
95
 
96
96
  static void dump_array_class(VALUE a, VALUE clas, int depth, Out out) {
97
97
  size_t size;
98
- int i, cnt;
98
+ size_t i;
99
+ size_t cnt;
99
100
  int d2 = depth + 1;
100
101
  long id = oj_check_circular(a, out);
101
102
 
@@ -106,7 +107,7 @@ static void dump_array_class(VALUE a, VALUE clas, int depth, Out out) {
106
107
  dump_obj_attrs(a, clas, 0, depth, out);
107
108
  return;
108
109
  }
109
- cnt = (int)RARRAY_LEN(a);
110
+ cnt = RARRAY_LEN(a);
110
111
  *out->cur++ = '[';
111
112
  if (0 < id) {
112
113
  assure_size(out, d2 * out->indent + 16);
@@ -181,7 +182,7 @@ static void dump_str_class(VALUE obj, VALUE clas, int depth, Out out) {
181
182
  dump_obj_attrs(obj, clas, 0, depth, out);
182
183
  } else {
183
184
  const char *s = RSTRING_PTR(obj);
184
- size_t len = (int)RSTRING_LEN(obj);
185
+ size_t len = RSTRING_LEN(obj);
185
186
  char s1 = s[1];
186
187
 
187
188
  oj_dump_cstr(s, len, 0, (':' == *s || ('^' == *s && ('r' == s1 || 'i' == s1))), out);
@@ -195,7 +196,7 @@ static void dump_str(VALUE obj, int depth, Out out, bool as_ok) {
195
196
  static void dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
196
197
  volatile VALUE s = rb_sym2str(obj);
197
198
 
198
- oj_dump_cstr(RSTRING_PTR(s), (int)RSTRING_LEN(s), 1, 0, out);
199
+ oj_dump_cstr(RSTRING_PTR(s), RSTRING_LEN(s), 1, 0, out);
199
200
  }
200
201
 
201
202
  static int hash_cb(VALUE key, VALUE value, VALUE ov) {
@@ -209,6 +210,11 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
209
210
  if (out->omit_nil && Qnil == value) {
210
211
  return ST_CONTINUE;
211
212
  }
213
+ if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
214
+ if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
215
+ return ST_CONTINUE;
216
+ }
217
+ }
212
218
  assure_size(out, size);
213
219
  fill_indent(out, depth);
214
220
  switch (rb_type(key)) {
@@ -389,7 +395,7 @@ static void dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
389
395
  rb_raise(rb_eEncodingError, "Invalid type for raw JSON.");
390
396
  } else {
391
397
  const char *s = RSTRING_PTR(v);
392
- int len = (int)RSTRING_LEN(v);
398
+ size_t len = RSTRING_LEN(v);
393
399
  const char *name = rb_id2name(*odd->attrs);
394
400
  size_t nlen = strlen(name);
395
401
 
@@ -489,7 +495,7 @@ static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out)
489
495
  *out->cur++ = ',';
490
496
  fill_indent(out, d2);
491
497
  APPEND_CHARS(out->cur, "\"self\":", 7);
492
- oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
498
+ oj_dump_cstr(RSTRING_PTR(obj), RSTRING_LEN(obj), 0, 0, out);
493
499
  break;
494
500
  case T_ARRAY:
495
501
  assure_size(out, d2 * out->indent + 14);
@@ -564,11 +570,12 @@ static void dump_regexp(VALUE obj, int depth, Out out, bool as_ok) {
564
570
  static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
565
571
  VALUE clas = rb_obj_class(obj);
566
572
  const char *class_name = rb_class2name(clas);
567
- int i;
568
- int d2 = depth + 1;
569
- int d3 = d2 + 1;
570
- size_t len = strlen(class_name);
571
- size_t size = d2 * out->indent + d3 * out->indent + 10 + len;
573
+ size_t i;
574
+ int d2 = depth + 1;
575
+ int d3 = d2 + 1;
576
+ size_t len = strlen(class_name);
577
+ size_t size = d2 * out->indent + d3 * out->indent + 10 + len;
578
+ char circular = out->opts->circular;
572
579
 
573
580
  assure_size(out, size);
574
581
  *out->cur++ = '{';
@@ -577,14 +584,14 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
577
584
  if ('#' == *class_name) {
578
585
  VALUE ma = rb_struct_s_members(clas);
579
586
  const char *name;
580
- int cnt = (int)RARRAY_LEN(ma);
587
+ size_t cnt = RARRAY_LEN(ma);
581
588
 
582
589
  *out->cur++ = '[';
583
590
  for (i = 0; i < cnt; i++) {
584
591
  volatile VALUE s = rb_sym2str(RARRAY_AREF(ma, i));
585
592
 
586
593
  name = RSTRING_PTR(s);
587
- len = (int)RSTRING_LEN(s);
594
+ len = RSTRING_LEN(s);
588
595
  size = len + 3;
589
596
  assure_size(out, size);
590
597
  if (0 < i) {
@@ -607,14 +614,18 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
607
614
  {
608
615
  VALUE v;
609
616
  int cnt;
617
+
610
618
  #if RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
611
619
  cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
612
620
  #else // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
613
621
  cnt = (int)RSTRUCT_LEN(obj);
614
622
  #endif // RSTRUCT_LEN_RETURNS_INTEGER_OBJECT
615
623
 
616
- for (i = 0; i < cnt; i++) {
617
- v = RSTRUCT_GET(obj, i);
624
+ if (0 == strcmp(class_name, "Range")) {
625
+ out->opts->circular = 'n';
626
+ }
627
+ for (i = 0; i < (size_t)cnt; i++) {
628
+ v = RSTRUCT_GET(obj, (int)i);
618
629
  if (dump_ignore(out->opts, v)) {
619
630
  v = Qnil;
620
631
  }
@@ -630,6 +641,9 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
630
641
  // class in interpreted Ruby so length() may not be defined.
631
642
  int slen = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
632
643
 
644
+ if (0 == strcmp(class_name, "Range")) {
645
+ out->opts->circular = 'n';
646
+ }
633
647
  for (i = 0; i < slen; i++) {
634
648
  assure_size(out, size);
635
649
  fill_indent(out, d3);
@@ -641,6 +655,7 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
641
655
  }
642
656
  }
643
657
  #endif
658
+ out->opts->circular = circular;
644
659
  out->cur--;
645
660
  APPEND_CHARS(out->cur, "]}", 2);
646
661
  *out->cur = '\0';
data/ext/oj/dump_strict.c CHANGED
@@ -30,7 +30,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
30
30
  char buf[64];
31
31
  char* b;
32
32
  double d = rb_num2dbl(obj);
33
- int cnt = 0;
33
+ size_t cnt = 0;
34
34
 
35
35
  if (0.0 == d) {
36
36
  b = buf;
@@ -92,7 +92,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
92
92
  } else if (0 == out->opts->float_prec) {
93
93
  volatile VALUE rstr = oj_safe_string_convert(obj);
94
94
 
95
- cnt = (int)RSTRING_LEN(rstr);
95
+ cnt = RSTRING_LEN(rstr);
96
96
  if ((int)sizeof(buf) <= cnt) {
97
97
  cnt = sizeof(buf) - 1;
98
98
  }
@@ -109,7 +109,8 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
109
109
 
110
110
  static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
111
111
  size_t size;
112
- int i, cnt;
112
+ size_t i;
113
+ size_t cnt;
113
114
  int d2 = depth + 1;
114
115
 
115
116
  if (Yes == out->opts->circular) {
@@ -118,7 +119,7 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
118
119
  return;
119
120
  }
120
121
  }
121
- cnt = (int)RARRAY_LEN(a);
122
+ cnt = RARRAY_LEN(a);
122
123
  *out->cur++ = '[';
123
124
  size = 2;
124
125
  assure_size(out, size);
@@ -192,14 +193,19 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
192
193
  if (out->omit_nil && Qnil == value) {
193
194
  return ST_CONTINUE;
194
195
  }
196
+ if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
197
+ if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
198
+ return ST_CONTINUE;
199
+ }
200
+ }
195
201
  if (!out->opts->dump_opts.use) {
196
202
  size = depth * out->indent + 1;
197
203
  assure_size(out, size);
198
204
  fill_indent(out, depth);
199
- if (rtype == T_STRING) {
200
- oj_dump_str(key, 0, out, false);
201
- } else {
202
- oj_dump_sym(key, 0, out, false);
205
+ switch (rtype) {
206
+ case T_STRING: oj_dump_str(key, 0, out, false); break;
207
+ case T_SYMBOL: oj_dump_sym(key, 0, out, false); break;
208
+ default: oj_dump_str(oj_safe_string_convert(key), 0, out, false); break;
203
209
  }
204
210
  *out->cur++ = ':';
205
211
  } else {
@@ -214,10 +220,10 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
214
220
  APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
215
221
  }
216
222
  }
217
- if (rtype == T_STRING) {
218
- oj_dump_str(key, 0, out, false);
219
- } else {
220
- oj_dump_sym(key, 0, out, false);
223
+ switch (rtype) {
224
+ case T_STRING: oj_dump_str(key, 0, out, false); break;
225
+ case T_SYMBOL: oj_dump_sym(key, 0, out, false); break;
226
+ default: oj_dump_str(oj_safe_string_convert(key), 0, out, false); break;
221
227
  }
222
228
  size = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
223
229
  assure_size(out, size);
@@ -290,7 +296,7 @@ static void dump_data_strict(VALUE obj, int depth, Out out, bool as_ok) {
290
296
  if (oj_bigdecimal_class == clas) {
291
297
  volatile VALUE rstr = oj_safe_string_convert(obj);
292
298
 
293
- oj_dump_raw(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), out);
299
+ oj_dump_raw(RSTRING_PTR(rstr), RSTRING_LEN(rstr), out);
294
300
  } else {
295
301
  raise_strict(obj);
296
302
  }
@@ -302,7 +308,7 @@ static void dump_data_null(VALUE obj, int depth, Out out, bool as_ok) {
302
308
  if (oj_bigdecimal_class == clas) {
303
309
  volatile VALUE rstr = oj_safe_string_convert(obj);
304
310
 
305
- oj_dump_raw(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), out);
311
+ oj_dump_raw(RSTRING_PTR(rstr), RSTRING_LEN(rstr), out);
306
312
  } else {
307
313
  oj_dump_nil(Qnil, depth, out, false);
308
314
  }
data/ext/oj/extconf.rb CHANGED
@@ -29,20 +29,22 @@ dflags = {
29
29
  have_func('rb_gc_mark_movable')
30
30
  have_func('stpcpy')
31
31
  have_func('pthread_mutex_init')
32
+ have_func('getrlimit', 'sys/resource.h')
32
33
  have_func('rb_enc_interned_str')
33
34
  have_func('rb_ext_ractor_safe', 'ruby.h')
34
- # rb_hash_bulk_insert is deep down in a header not included in normal build and that seems to fool have_func.
35
- have_func('rb_hash_bulk_insert', 'ruby.h') unless '2' == version[0] && '6' == version[1]
36
35
 
37
36
  dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
38
37
 
39
- if with_config('--with-sse42')
40
- if try_cflags('-msse4.2')
41
- $CPPFLAGS += ' -msse4.2'
42
- dflags['OJ_USE_SSE4_2'] = 1
43
- else
44
- warn 'SSE 4.2 is not supported on this platform.'
45
- end
38
+ # SIMD optimizations use runtime CPU detection and function-level target attributes
39
+ # We do NOT add global -msse4.2/-msse2 flags here because:
40
+ # 1. It would cause illegal instruction errors on CPUs without SSE4.2
41
+ # 2. The code uses __attribute__((target("sse4.2"))) for SSE4.2 functions
42
+ # 3. Runtime detection in oj_get_simd_implementation() selects the right path
43
+ #
44
+ # We only add -msse2 if available, since SSE2 is baseline for all x86_64 CPUs
45
+ # and needed for compiling the SSE2 fallback code on 32-bit x86
46
+ if try_cflags('-msse2')
47
+ $CPPFLAGS += ' -msse2'
46
48
  end
47
49
 
48
50
  if enable_config('trace-log', false)
data/ext/oj/fast.c CHANGED
@@ -40,7 +40,7 @@ typedef struct _doc {
40
40
  Leaf *where; // points to current location
41
41
  Leaf where_path[MAX_STACK]; // points to head of path
42
42
  char *json;
43
- unsigned long size; // number of leaves/branches in the doc
43
+ unsigned long size; // number of leaves/branches in the doc
44
44
  VALUE self;
45
45
  Batch batches;
46
46
  struct _batch batch0;
@@ -80,21 +80,10 @@ static void each_leaf(Doc doc, VALUE self);
80
80
  static int move_step(Doc doc, const char *path, int loc);
81
81
  static Leaf get_doc_leaf(Doc doc, const char *path);
82
82
  static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
83
- static void each_value(Doc doc, Leaf leaf);
83
+ static void each_value(Doc doc, Leaf leaf, VALUE self);
84
84
 
85
85
  VALUE oj_doc_class = Qundef;
86
86
 
87
- // This is only for CentOS 5.4 with Ruby 1.9.3-p0.
88
- #ifndef HAVE_STPCPY
89
- char *stpcpy(char *dest, const char *src) {
90
- size_t cnt = strlen(src);
91
-
92
- strcpy(dest, src);
93
-
94
- return dest + cnt;
95
- }
96
- #endif
97
-
98
87
  inline static void next_non_white(ParseInfo pi) {
99
88
  for (; 1; pi->s++) {
100
89
  switch (*pi->s) {
@@ -193,8 +182,7 @@ static VALUE leaf_value(Doc doc, Leaf leaf) {
193
182
  case T_FIXNUM: leaf_fixnum_value(leaf); break;
194
183
  case T_FLOAT: leaf_float_value(leaf); break;
195
184
  case T_STRING:
196
- leaf->value = rb_str_new2(leaf->str);
197
- leaf->value = oj_encode(leaf->value);
185
+ leaf->value = rb_utf8_str_new_cstr(leaf->str);
198
186
  leaf->value_type = RUBY_VAL;
199
187
  break;
200
188
  case T_ARRAY: return leaf_array_value(doc, leaf); break;
@@ -247,6 +235,19 @@ static void skip_comment(ParseInfo pi) {
247
235
  #define NUM_MAX (FIXNUM_MAX >> 8)
248
236
  #endif
249
237
 
238
+ static void validate_integer_size(size_t limit, char *head, char *tail) {
239
+ size_t total = (size_t)(tail - head);
240
+ bool has_sign = (head[0] == '-' || head[0] == '+');
241
+ size_t digit_count = total - (has_sign ? 1 : 0);
242
+
243
+ if (digit_count > limit) {
244
+ rb_raise(oj_parse_error_class,
245
+ "integer exceeds :max_integer_digits (%lu > %lu)",
246
+ (unsigned long)digit_count,
247
+ (unsigned long)limit);
248
+ }
249
+ }
250
+
250
251
  static void leaf_fixnum_value(Leaf leaf) {
251
252
  char *s = leaf->str;
252
253
  int64_t n = 0;
@@ -266,7 +267,12 @@ static void leaf_fixnum_value(Leaf leaf) {
266
267
  }
267
268
  }
268
269
  if (big) {
269
- char c = *s;
270
+ size_t limit = oj_default_options.max_integer_digits;
271
+ char c = *s;
272
+
273
+ if (0 < limit) {
274
+ validate_integer_size(limit, leaf->str, s);
275
+ }
270
276
 
271
277
  *s = '\0';
272
278
  leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
@@ -309,8 +315,7 @@ static VALUE leaf_hash_value(Doc doc, Leaf leaf) {
309
315
  volatile VALUE key;
310
316
 
311
317
  do {
312
- key = rb_str_new2(e->key);
313
- key = oj_encode(key);
318
+ key = rb_utf8_str_new_cstr(e->key);
314
319
  rb_hash_aset(h, key, leaf_value(doc, e));
315
320
  e = e->next;
316
321
  } while (e != first);
@@ -573,7 +578,7 @@ static char *read_quoted_value(ParseInfo pi) {
573
578
  char *h = pi->s; // head
574
579
  char *t = h; // tail
575
580
 
576
- h++; // skip quote character
581
+ h++; // skip quote character
577
582
  t++;
578
583
  value = h;
579
584
  for (; '"' != *h; h++, t++) {
@@ -765,7 +770,7 @@ static VALUE parse_json(VALUE clas, char *json, bool given) {
765
770
  pi.s = pi.str;
766
771
  doc_init(doc);
767
772
  pi.doc = doc;
768
- #if IS_WINDOWS
773
+ #if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
769
774
  // assume a 1M stack and give half to ruby
770
775
  pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
771
776
  #else
@@ -780,11 +785,10 @@ static VALUE parse_json(VALUE clas, char *json, bool given) {
780
785
  }
781
786
  }
782
787
  #endif
783
- doc->json = json;
784
- self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
785
- doc->self = self;
786
- DATA_PTR(doc->self) = doc;
787
- result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
788
+ doc->json = json;
789
+ self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
790
+ doc->self = self;
791
+ result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
788
792
  if (given || 0 != ex) {
789
793
  DATA_PTR(doc->self) = NULL;
790
794
  // TBD is this needed?
@@ -953,6 +957,9 @@ static void each_leaf(Doc doc, VALUE self) {
953
957
  }
954
958
  } else {
955
959
  rb_yield(self);
960
+ if (NULL == DATA_PTR(self)) {
961
+ rb_raise(rb_eIOError, "Document closed.");
962
+ }
956
963
  }
957
964
  }
958
965
 
@@ -1046,19 +1053,22 @@ static int move_step(Doc doc, const char *path, int loc) {
1046
1053
  return loc;
1047
1054
  }
1048
1055
 
1049
- static void each_value(Doc doc, Leaf leaf) {
1056
+ static void each_value(Doc doc, Leaf leaf, VALUE self) {
1050
1057
  if (COL_VAL == leaf->value_type) {
1051
1058
  if (0 != leaf->elements) {
1052
1059
  Leaf first = leaf->elements->next;
1053
1060
  Leaf e = first;
1054
1061
 
1055
1062
  do {
1056
- each_value(doc, e);
1063
+ each_value(doc, e, self);
1057
1064
  e = e->next;
1058
1065
  } while (e != first);
1059
1066
  }
1060
1067
  } else {
1061
1068
  rb_yield(leaf_value(doc, leaf));
1069
+ if (NULL == DATA_PTR(self)) {
1070
+ rb_raise(rb_eIOError, "Document closed.");
1071
+ }
1062
1072
  }
1063
1073
  }
1064
1074
 
@@ -1089,7 +1099,7 @@ static VALUE doc_open(VALUE clas, VALUE str) {
1089
1099
  int given = rb_block_given_p();
1090
1100
 
1091
1101
  Check_Type(str, T_STRING);
1092
- len = (int)RSTRING_LEN(str) + 1;
1102
+ len = RSTRING_LEN(str) + 1;
1093
1103
  json = OJ_R_ALLOC_N(char, len);
1094
1104
 
1095
1105
  memcpy(json, StringValuePtr(str), len);
@@ -1258,8 +1268,7 @@ static VALUE doc_local_key(VALUE self) {
1258
1268
  volatile VALUE key = Qnil;
1259
1269
 
1260
1270
  if (T_HASH == leaf->parent_type) {
1261
- key = rb_str_new2(leaf->key);
1262
- key = oj_encode(key);
1271
+ key = rb_utf8_str_new_cstr(leaf->key);
1263
1272
  } else if (T_ARRAY == leaf->parent_type) {
1264
1273
  key = LONG2NUM(leaf->index);
1265
1274
  }
@@ -1495,12 +1504,19 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1495
1504
  Leaf first = (*doc->where)->elements->next;
1496
1505
  Leaf e = first;
1497
1506
 
1507
+ if (MAX_STACK <= (doc->where + 1) - doc->where_path) {
1508
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
1509
+ }
1498
1510
  doc->where++;
1499
1511
  do {
1500
1512
  *doc->where = e;
1501
1513
  rb_yield(self);
1514
+ if (NULL == DATA_PTR(self)) {
1515
+ rb_raise(rb_eIOError, "Document closed.");
1516
+ }
1502
1517
  e = e->next;
1503
1518
  } while (e != first);
1519
+ doc->where--;
1504
1520
  }
1505
1521
  if (0 < wlen) {
1506
1522
  memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
@@ -1544,7 +1560,7 @@ static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
1544
1560
  path = StringValuePtr(*argv);
1545
1561
  }
1546
1562
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1547
- each_value(doc, leaf);
1563
+ each_value(doc, leaf, self);
1548
1564
  }
1549
1565
  }
1550
1566
  return Qnil;
@@ -1609,7 +1625,9 @@ static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
1609
1625
  * Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
1610
1626
  */
1611
1627
  static VALUE doc_size(VALUE self) {
1612
- return ULONG2NUM(((Doc)DATA_PTR(self))->size);
1628
+ Doc d;
1629
+ TypedData_Get_Struct(self, struct _doc, &oj_doc_type, d);
1630
+ return ULONG2NUM(d->size);
1613
1631
  }
1614
1632
 
1615
1633
  /* @overload close() => nil