oj 3.16.3 → 3.17.4

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 (134) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +99 -0
  3. data/README.md +0 -16
  4. data/ext/oj/compat.c +3 -3
  5. data/ext/oj/custom.c +17 -11
  6. data/ext/oj/dump.c +619 -88
  7. data/ext/oj/dump.h +9 -2
  8. data/ext/oj/dump_compat.c +16 -9
  9. data/ext/oj/dump_leaf.c +1 -1
  10. data/ext/oj/dump_object.c +52 -27
  11. data/ext/oj/dump_strict.c +20 -14
  12. data/ext/oj/extconf.rb +11 -9
  13. data/ext/oj/fast.c +90 -65
  14. data/ext/oj/intern.c +1 -1
  15. data/ext/oj/mimic_json.c +14 -8
  16. data/ext/oj/object.c +18 -12
  17. data/ext/oj/oj.c +426 -11
  18. data/ext/oj/oj.h +60 -52
  19. data/ext/oj/parse.c +252 -48
  20. data/ext/oj/parse.h +0 -1
  21. data/ext/oj/parser.c +179 -47
  22. data/ext/oj/parser.h +4 -2
  23. data/ext/oj/rails.c +57 -36
  24. data/ext/oj/reader.c +17 -5
  25. data/ext/oj/rxclass.c +17 -1
  26. data/ext/oj/rxclass.h +2 -1
  27. data/ext/oj/safe.c +230 -0
  28. data/ext/oj/safe.h +79 -0
  29. data/ext/oj/saj.c +55 -20
  30. data/ext/oj/scp.c +3 -6
  31. data/ext/oj/simd.h +219 -0
  32. data/ext/oj/sparse.c +15 -3
  33. data/ext/oj/stream_writer.c +19 -14
  34. data/ext/oj/strict.c +2 -4
  35. data/ext/oj/string_writer.c +26 -14
  36. data/ext/oj/usual.c +37 -35
  37. data/ext/oj/wab.c +5 -4
  38. data/lib/oj/mimic.rb +1 -5
  39. data/lib/oj/schandler.rb +5 -4
  40. data/lib/oj/version.rb +1 -1
  41. data/pages/Encoding.md +1 -1
  42. metadata +22 -100
  43. data/test/_test_active.rb +0 -75
  44. data/test/_test_active_mimic.rb +0 -95
  45. data/test/_test_mimic_rails.rb +0 -123
  46. data/test/activerecord/result_test.rb +0 -31
  47. data/test/activesupport6/abstract_unit.rb +0 -44
  48. data/test/activesupport6/decoding_test.rb +0 -133
  49. data/test/activesupport6/encoding_test.rb +0 -507
  50. data/test/activesupport6/encoding_test_cases.rb +0 -98
  51. data/test/activesupport6/test_common.rb +0 -17
  52. data/test/activesupport6/test_helper.rb +0 -163
  53. data/test/activesupport6/time_zone_test_helpers.rb +0 -39
  54. data/test/activesupport7/abstract_unit.rb +0 -49
  55. data/test/activesupport7/decoding_test.rb +0 -125
  56. data/test/activesupport7/encoding_test.rb +0 -486
  57. data/test/activesupport7/encoding_test_cases.rb +0 -104
  58. data/test/activesupport7/time_zone_test_helpers.rb +0 -47
  59. data/test/files.rb +0 -29
  60. data/test/foo.rb +0 -14
  61. data/test/helper.rb +0 -39
  62. data/test/isolated/shared.rb +0 -309
  63. data/test/isolated/test_mimic_after.rb +0 -13
  64. data/test/isolated/test_mimic_alone.rb +0 -12
  65. data/test/isolated/test_mimic_as_json.rb +0 -45
  66. data/test/isolated/test_mimic_before.rb +0 -13
  67. data/test/isolated/test_mimic_define.rb +0 -28
  68. data/test/isolated/test_mimic_rails_after.rb +0 -22
  69. data/test/isolated/test_mimic_rails_before.rb +0 -21
  70. data/test/isolated/test_mimic_redefine.rb +0 -15
  71. data/test/json_gem/json_addition_test.rb +0 -216
  72. data/test/json_gem/json_common_interface_test.rb +0 -155
  73. data/test/json_gem/json_encoding_test.rb +0 -107
  74. data/test/json_gem/json_ext_parser_test.rb +0 -21
  75. data/test/json_gem/json_fixtures_test.rb +0 -36
  76. data/test/json_gem/json_generator_test.rb +0 -413
  77. data/test/json_gem/json_generic_object_test.rb +0 -90
  78. data/test/json_gem/json_parser_test.rb +0 -477
  79. data/test/json_gem/json_string_matching_test.rb +0 -42
  80. data/test/json_gem/test_helper.rb +0 -30
  81. data/test/mem.rb +0 -34
  82. data/test/perf.rb +0 -102
  83. data/test/perf_compat.rb +0 -128
  84. data/test/perf_dump.rb +0 -50
  85. data/test/perf_fast.rb +0 -162
  86. data/test/perf_file.rb +0 -62
  87. data/test/perf_object.rb +0 -134
  88. data/test/perf_once.rb +0 -59
  89. data/test/perf_parser.rb +0 -183
  90. data/test/perf_saj.rb +0 -101
  91. data/test/perf_scp.rb +0 -140
  92. data/test/perf_simple.rb +0 -289
  93. data/test/perf_strict.rb +0 -137
  94. data/test/perf_wab.rb +0 -129
  95. data/test/prec.rb +0 -23
  96. data/test/sample/change.rb +0 -13
  97. data/test/sample/dir.rb +0 -18
  98. data/test/sample/doc.rb +0 -35
  99. data/test/sample/file.rb +0 -47
  100. data/test/sample/group.rb +0 -15
  101. data/test/sample/hasprops.rb +0 -15
  102. data/test/sample/layer.rb +0 -11
  103. data/test/sample/line.rb +0 -20
  104. data/test/sample/oval.rb +0 -10
  105. data/test/sample/rect.rb +0 -9
  106. data/test/sample/shape.rb +0 -34
  107. data/test/sample/text.rb +0 -19
  108. data/test/sample.rb +0 -54
  109. data/test/sample_json.rb +0 -37
  110. data/test/test_compat.rb +0 -567
  111. data/test/test_custom.rb +0 -555
  112. data/test/test_debian.rb +0 -50
  113. data/test/test_fast.rb +0 -526
  114. data/test/test_file.rb +0 -250
  115. data/test/test_gc.rb +0 -60
  116. data/test/test_generate.rb +0 -21
  117. data/test/test_hash.rb +0 -39
  118. data/test/test_integer_range.rb +0 -72
  119. data/test/test_null.rb +0 -376
  120. data/test/test_object.rb +0 -1030
  121. data/test/test_parser.rb +0 -11
  122. data/test/test_parser_debug.rb +0 -27
  123. data/test/test_parser_saj.rb +0 -337
  124. data/test/test_parser_usual.rb +0 -227
  125. data/test/test_rails.rb +0 -35
  126. data/test/test_saj.rb +0 -188
  127. data/test/test_scp.rb +0 -431
  128. data/test/test_strict.rb +0 -441
  129. data/test/test_various.rb +0 -801
  130. data/test/test_wab.rb +0 -311
  131. data/test/test_writer.rb +0 -380
  132. data/test/tests.rb +0 -33
  133. data/test/tests_mimic.rb +0 -23
  134. data/test/tests_mimic_addition.rb +0 -16
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) {
@@ -103,7 +92,14 @@ inline static void next_non_white(ParseInfo pi) {
103
92
  case '\f':
104
93
  case '\n':
105
94
  case '\r': break;
106
- case '/': skip_comment(pi); break;
95
+ case '/':
96
+ skip_comment(pi);
97
+ // A comment that is not terminated by a newline ends on the null
98
+ // terminator. Stop here so the loop does not step past it.
99
+ if ('\0' == *pi->s) {
100
+ return;
101
+ }
102
+ break;
107
103
  default: return;
108
104
  }
109
105
  }
@@ -193,8 +189,7 @@ static VALUE leaf_value(Doc doc, Leaf leaf) {
193
189
  case T_FIXNUM: leaf_fixnum_value(leaf); break;
194
190
  case T_FLOAT: leaf_float_value(leaf); break;
195
191
  case T_STRING:
196
- leaf->value = rb_str_new2(leaf->str);
197
- leaf->value = oj_encode(leaf->value);
192
+ leaf->value = rb_utf8_str_new_cstr(leaf->str);
198
193
  leaf->value_type = RUBY_VAL;
199
194
  break;
200
195
  case T_ARRAY: return leaf_array_value(doc, leaf); break;
@@ -222,10 +217,11 @@ static void skip_comment(ParseInfo pi) {
222
217
  if ('*' == *pi->s && '/' == *(pi->s + 1)) {
223
218
  pi->s++;
224
219
  return;
225
- } else if ('\0' == *pi->s) {
226
- raise_error("comment not terminated", pi->str, pi->s);
227
220
  }
228
221
  }
222
+ // The loop only ends on the null terminator so the comment was never
223
+ // closed.
224
+ raise_error("comment not terminated", pi->str, pi->s);
229
225
  } else if ('/' == *pi->s) {
230
226
  for (; 1; pi->s++) {
231
227
  switch (*pi->s) {
@@ -247,6 +243,19 @@ static void skip_comment(ParseInfo pi) {
247
243
  #define NUM_MAX (FIXNUM_MAX >> 8)
248
244
  #endif
249
245
 
246
+ static void validate_integer_size(size_t limit, char *head, char *tail) {
247
+ size_t total = (size_t)(tail - head);
248
+ bool has_sign = (head[0] == '-' || head[0] == '+');
249
+ size_t digit_count = total - (has_sign ? 1 : 0);
250
+
251
+ if (digit_count > limit) {
252
+ rb_raise(oj_parse_error_class,
253
+ "integer exceeds :max_integer_digits (%lu > %lu)",
254
+ (unsigned long)digit_count,
255
+ (unsigned long)limit);
256
+ }
257
+ }
258
+
250
259
  static void leaf_fixnum_value(Leaf leaf) {
251
260
  char *s = leaf->str;
252
261
  int64_t n = 0;
@@ -266,7 +275,12 @@ static void leaf_fixnum_value(Leaf leaf) {
266
275
  }
267
276
  }
268
277
  if (big) {
269
- char c = *s;
278
+ size_t limit = oj_default_options.max_integer_digits;
279
+ char c = *s;
280
+
281
+ if (0 < limit) {
282
+ validate_integer_size(limit, leaf->str, s);
283
+ }
270
284
 
271
285
  *s = '\0';
272
286
  leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
@@ -309,8 +323,7 @@ static VALUE leaf_hash_value(Doc doc, Leaf leaf) {
309
323
  volatile VALUE key;
310
324
 
311
325
  do {
312
- key = rb_str_new2(e->key);
313
- key = oj_encode(key);
326
+ key = rb_utf8_str_new_cstr(e->key);
314
327
  rb_hash_aset(h, key, leaf_value(doc, e));
315
328
  e = e->next;
316
329
  } while (e != first);
@@ -573,7 +586,7 @@ static char *read_quoted_value(ParseInfo pi) {
573
586
  char *h = pi->s; // head
574
587
  char *t = h; // tail
575
588
 
576
- h++; // skip quote character
589
+ h++; // skip quote character
577
590
  t++;
578
591
  value = h;
579
592
  for (; '"' != *h; h++, t++) {
@@ -705,21 +718,23 @@ static void mark_doc(void *ptr) {
705
718
  }
706
719
  #ifdef HAVE_RB_GC_MARK_MOVABLE
707
720
  static void compact_leaf(Leaf leaf) {
708
- switch (leaf->value_type) {
709
- case COL_VAL:
710
- if (NULL != leaf->elements) {
711
- Leaf first = leaf->elements->next;
712
- Leaf e = first;
721
+ if (NULL != leaf) {
722
+ switch (leaf->value_type) {
723
+ case COL_VAL:
724
+ if (NULL != leaf->elements) {
725
+ Leaf first = leaf->elements->next;
726
+ Leaf e = first;
713
727
 
714
- do {
715
- compact_leaf(e);
716
- e = e->next;
717
- } while (e != first);
718
- }
719
- break;
720
- case RUBY_VAL: leaf->value = rb_gc_location(leaf->value); break;
728
+ do {
729
+ compact_leaf(e);
730
+ e = e->next;
731
+ } while (e != first);
732
+ }
733
+ break;
734
+ case RUBY_VAL: leaf->value = rb_gc_location(leaf->value); break;
721
735
 
722
- default: break;
736
+ default: break;
737
+ }
723
738
  }
724
739
  }
725
740
 
@@ -765,7 +780,7 @@ static VALUE parse_json(VALUE clas, char *json, bool given) {
765
780
  pi.s = pi.str;
766
781
  doc_init(doc);
767
782
  pi.doc = doc;
768
- #if IS_WINDOWS
783
+ #if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
769
784
  // assume a 1M stack and give half to ruby
770
785
  pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
771
786
  #else
@@ -785,14 +800,11 @@ static VALUE parse_json(VALUE clas, char *json, bool given) {
785
800
  doc->self = self;
786
801
  result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
787
802
  if (given || 0 != ex) {
803
+ // The doc is detached from its wrapper so the GC will not free it.
804
+ // doc_free() releases both the doc and its json buffer (doc->json), so
805
+ // the caller must not free json separately.
788
806
  DATA_PTR(doc->self) = NULL;
789
- // TBD is this needed?
790
- /*
791
807
  doc_free(pi.doc);
792
- if (0 != ex) { // will jump so caller will not free
793
- OJ_R_FREE(json);
794
- }
795
- */
796
808
  } else {
797
809
  result = doc->self;
798
810
  }
@@ -941,6 +953,7 @@ static void each_leaf(Doc doc, VALUE self) {
941
953
 
942
954
  doc->where++;
943
955
  if (MAX_STACK <= doc->where - doc->where_path) {
956
+ doc->where--;
944
957
  rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
945
958
  }
946
959
  do {
@@ -952,6 +965,9 @@ static void each_leaf(Doc doc, VALUE self) {
952
965
  }
953
966
  } else {
954
967
  rb_yield(self);
968
+ if (NULL == DATA_PTR(self)) {
969
+ rb_raise(rb_eIOError, "Document closed.");
970
+ }
955
971
  }
956
972
  }
957
973
 
@@ -964,8 +980,9 @@ static int move_step(Doc doc, const char *path, int loc) {
964
980
  } else {
965
981
  Leaf leaf;
966
982
 
983
+ // A document with no root (empty or comment only JSON) has no leaf to
984
+ // step from so the path can not be located.
967
985
  if (0 == doc->where || 0 == (leaf = *doc->where)) {
968
- printf("*** Internal error at %s\n", path);
969
986
  return loc;
970
987
  }
971
988
  if ('.' == *path && '.' == *(path + 1)) {
@@ -1045,19 +1062,22 @@ static int move_step(Doc doc, const char *path, int loc) {
1045
1062
  return loc;
1046
1063
  }
1047
1064
 
1048
- static void each_value(Doc doc, Leaf leaf) {
1065
+ static void each_value(Doc doc, Leaf leaf, VALUE self) {
1049
1066
  if (COL_VAL == leaf->value_type) {
1050
1067
  if (0 != leaf->elements) {
1051
1068
  Leaf first = leaf->elements->next;
1052
1069
  Leaf e = first;
1053
1070
 
1054
1071
  do {
1055
- each_value(doc, e);
1072
+ each_value(doc, e, self);
1056
1073
  e = e->next;
1057
1074
  } while (e != first);
1058
1075
  }
1059
1076
  } else {
1060
1077
  rb_yield(leaf_value(doc, leaf));
1078
+ if (NULL == DATA_PTR(self)) {
1079
+ rb_raise(rb_eIOError, "Document closed.");
1080
+ }
1061
1081
  }
1062
1082
  }
1063
1083
 
@@ -1088,17 +1108,12 @@ static VALUE doc_open(VALUE clas, VALUE str) {
1088
1108
  int given = rb_block_given_p();
1089
1109
 
1090
1110
  Check_Type(str, T_STRING);
1091
- len = (int)RSTRING_LEN(str) + 1;
1111
+ len = RSTRING_LEN(str) + 1;
1092
1112
  json = OJ_R_ALLOC_N(char, len);
1093
1113
 
1094
1114
  memcpy(json, StringValuePtr(str), len);
1095
1115
  obj = parse_json(clas, json, given);
1096
- // TBD is this needed
1097
- /*
1098
- if (given) {
1099
- OJ_R_FREE(json);
1100
- }
1101
- */
1116
+ // json is owned by the doc and freed by doc_free(); do not free it here.
1102
1117
  return obj;
1103
1118
  }
1104
1119
 
@@ -1130,7 +1145,9 @@ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1130
1145
  int given = rb_block_given_p();
1131
1146
 
1132
1147
  path = StringValuePtr(filename);
1133
- if (0 == (f = fopen(path, "r"))) {
1148
+ // Open in binary mode. On Windows a text mode read translates CRLF into LF
1149
+ // so fewer bytes are read than the file size reported by ftell().
1150
+ if (0 == (f = fopen(path, "rb"))) {
1134
1151
  rb_raise(rb_eIOError, "%s", strerror(errno));
1135
1152
  }
1136
1153
  fseek(f, 0, SEEK_END);
@@ -1148,12 +1165,7 @@ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1148
1165
  fclose(f);
1149
1166
  json[len] = '\0';
1150
1167
  obj = parse_json(clas, json, given);
1151
- // TBD is this needed
1152
- /*
1153
- if (given) {
1154
- OJ_R_FREE(json);
1155
- }
1156
- */
1168
+ // json is owned by the doc and freed by doc_free(); do not free it here.
1157
1169
  return obj;
1158
1170
  }
1159
1171
 
@@ -1252,13 +1264,16 @@ static VALUE doc_path(VALUE self) {
1252
1264
  * #=> nil
1253
1265
  */
1254
1266
  static VALUE doc_local_key(VALUE self) {
1255
- Doc doc = self_doc(self);
1256
- Leaf leaf = *doc->where;
1257
- volatile VALUE key = Qnil;
1267
+ Doc doc = self_doc(self);
1268
+ Leaf leaf;
1269
+ volatile VALUE key = Qnil;
1258
1270
 
1271
+ if (NULL == doc->where || NULL == *doc->where) {
1272
+ return Qnil;
1273
+ }
1274
+ leaf = *doc->where;
1259
1275
  if (T_HASH == leaf->parent_type) {
1260
- key = rb_str_new2(leaf->key);
1261
- key = oj_encode(key);
1276
+ key = rb_utf8_str_new_cstr(leaf->key);
1262
1277
  } else if (T_ARRAY == leaf->parent_type) {
1263
1278
  key = LONG2NUM(leaf->index);
1264
1279
  }
@@ -1411,6 +1426,9 @@ static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1411
1426
  return Qnil;
1412
1427
  }
1413
1428
  }
1429
+ if (NULL == doc->where || NULL == *doc->where) {
1430
+ return Qnil;
1431
+ }
1414
1432
  each_leaf(doc, self);
1415
1433
  if (0 < wlen) {
1416
1434
  memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
@@ -1494,12 +1512,19 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1494
1512
  Leaf first = (*doc->where)->elements->next;
1495
1513
  Leaf e = first;
1496
1514
 
1515
+ if (MAX_STACK <= (doc->where + 1) - doc->where_path) {
1516
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
1517
+ }
1497
1518
  doc->where++;
1498
1519
  do {
1499
1520
  *doc->where = e;
1500
1521
  rb_yield(self);
1522
+ if (NULL == DATA_PTR(self)) {
1523
+ rb_raise(rb_eIOError, "Document closed.");
1524
+ }
1501
1525
  e = e->next;
1502
1526
  } while (e != first);
1527
+ doc->where--;
1503
1528
  }
1504
1529
  if (0 < wlen) {
1505
1530
  memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
@@ -1543,7 +1568,7 @@ static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
1543
1568
  path = StringValuePtr(*argv);
1544
1569
  }
1545
1570
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1546
- each_value(doc, leaf);
1571
+ each_value(doc, leaf, self);
1547
1572
  }
1548
1573
  }
1549
1574
  return Qnil;
data/ext/oj/intern.c CHANGED
@@ -69,7 +69,7 @@ static VALUE form_attr(const char *str, size_t len) {
69
69
  memcpy(b + 1, str, len);
70
70
  b[len + 1] = '\0';
71
71
  }
72
- id = rb_intern3(buf, len + 1, oj_utf8_encoding);
72
+ id = rb_intern3(b, len + 1, oj_utf8_encoding);
73
73
  OJ_R_FREE(b);
74
74
  return id;
75
75
  }
data/ext/oj/mimic_json.c CHANGED
@@ -246,8 +246,7 @@ static VALUE mimic_dump(int argc, VALUE *argv, VALUE self) {
246
246
  if (0 == out.buf) {
247
247
  rb_raise(rb_eNoMemError, "Not enough memory.");
248
248
  }
249
- rstr = rb_str_new2(out.buf);
250
- rstr = oj_encode(rstr);
249
+ rstr = rb_utf8_str_new_cstr(out.buf);
251
250
  if (2 <= argc && Qnil != argv[1] && rb_respond_to(argv[1], oj_write_id)) {
252
251
  VALUE io = argv[1];
253
252
  VALUE args[1];
@@ -396,8 +395,7 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
396
395
  if (0 == out.buf) {
397
396
  rb_raise(rb_eNoMemError, "Not enough memory.");
398
397
  }
399
- rstr = rb_str_new2(out.buf);
400
- rstr = oj_encode(rstr);
398
+ rstr = rb_utf8_str_new_cstr(out.buf);
401
399
 
402
400
  oj_out_free(&out);
403
401
 
@@ -713,6 +711,7 @@ static struct _options mimic_object_to_json_options = {0, // indent
713
711
  0, // cache_str
714
712
  0, // int_range_min
715
713
  0, // int_range_max
714
+ 0, // max_integer_digits
716
715
  oj_json_class, // create_id
717
716
  10, // create_id_len
718
717
  3, // sec_prec
@@ -737,6 +736,8 @@ static struct _options mimic_object_to_json_options = {0, // indent
737
736
  false, // omit_nil
738
737
  false, // omit_null_byte
739
738
  100, // max_depth
739
+ NULL, // only
740
+ NULL, // except
740
741
  },
741
742
  {
742
743
  // str_rx
@@ -768,8 +769,7 @@ static VALUE mimic_object_to_json(int argc, VALUE *argv, VALUE self) {
768
769
  if (NULL == out.buf) {
769
770
  rb_raise(rb_eNoMemError, "Not enough memory.");
770
771
  }
771
- rstr = rb_str_new2(out.buf);
772
- rstr = oj_encode(rstr);
772
+ rstr = rb_utf8_str_new_cstr(out.buf);
773
773
 
774
774
  oj_out_free(&out);
775
775
 
@@ -837,11 +837,15 @@ void oj_mimic_json_methods(VALUE json) {
837
837
  } else {
838
838
  json_error = rb_define_class_under(json, "JSONError", rb_eStandardError);
839
839
  }
840
+
841
+ rb_global_variable(&oj_json_parser_error_class);
840
842
  if (rb_const_defined_at(json, rb_intern("ParserError"))) {
841
843
  oj_json_parser_error_class = rb_const_get(json, rb_intern("ParserError"));
842
844
  } else {
843
845
  oj_json_parser_error_class = rb_define_class_under(json, "ParserError", json_error);
844
846
  }
847
+
848
+ rb_global_variable(&oj_json_generator_error_class);
845
849
  if (rb_const_defined_at(json, rb_intern("GeneratorError"))) {
846
850
  oj_json_generator_error_class = rb_const_get(json, rb_intern("GeneratorError"));
847
851
  } else {
@@ -867,8 +871,8 @@ void oj_mimic_json_methods(VALUE json) {
867
871
  rb_require("oj/state");
868
872
  }
869
873
  // Pull in the JSON::State mimic file.
874
+ rb_global_variable(&state_class);
870
875
  state_class = rb_const_get_at(generator, rb_intern("State"));
871
- rb_gc_register_mark_object(state_class);
872
876
  }
873
877
 
874
878
  /* Document-module: JSON
@@ -905,7 +909,9 @@ oj_define_mimic_json(int argc, VALUE *argv, VALUE self) {
905
909
  }
906
910
  oj_mimic_json_methods(json);
907
911
 
908
- rb_define_method(rb_cObject, "to_json", mimic_object_to_json, -1);
912
+ if (!rb_const_defined(rb_cObject, rb_intern("ActiveSupport"))) {
913
+ rb_define_method(rb_cObject, "to_json", mimic_object_to_json, -1);
914
+ }
909
915
 
910
916
  rb_gv_set("$VERBOSE", verbose);
911
917
 
data/ext/oj/object.c CHANGED
@@ -305,7 +305,7 @@ static int hat_num(ParseInfo pi, Val parent, Val kval, NumInfo ni) {
305
305
 
306
306
  static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, volatile VALUE value) {
307
307
  if (T_ARRAY == rb_type(value)) {
308
- int len = (int)RARRAY_LEN(value);
308
+ size_t len = RARRAY_LEN(value);
309
309
 
310
310
  if (2 == klen && 'u' == key[1]) {
311
311
  volatile VALUE sc;
@@ -319,21 +319,28 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
319
319
  e1 = *RARRAY_CONST_PTR(value);
320
320
  // check for anonymous Struct
321
321
  if (T_ARRAY == rb_type(e1)) {
322
- VALUE args[1024];
323
- volatile VALUE rstr;
324
- int i, cnt = (int)RARRAY_LEN(e1);
322
+ // Build the member-name list in a GC-managed Ruby array. Using a
323
+ // fixed C buffer (VALUE args[1024]) here overflowed the stack when
324
+ // the JSON supplied more than 1024 member names.
325
+ volatile VALUE names = rb_ary_new2(RARRAY_LEN(e1));
326
+ size_t i;
327
+ size_t cnt = RARRAY_LEN(e1);
325
328
 
326
329
  for (i = 0; i < cnt; i++) {
327
- rstr = RARRAY_AREF(e1, i);
328
- args[i] = rb_funcall(rstr, oj_to_sym_id, 0);
330
+ rb_ary_push(names, rb_funcall(RARRAY_AREF(e1, i), oj_to_sym_id, 0));
329
331
  }
330
- sc = rb_funcall2(rb_cStruct, oj_new_id, cnt, args);
332
+ sc = rb_apply(rb_cStruct, oj_new_id, names);
331
333
  } else {
332
334
  // If struct is not defined then we let this fail and raise an exception.
333
335
  sc = oj_name2struct(pi, *RARRAY_CONST_PTR(value), rb_eArgError);
334
336
  }
337
+ if (Qundef == sc || Qnil == sc) {
338
+ // oj_name2struct already recorded the error; do not pass an
339
+ // unresolved (Qundef) class on to rb_obj_alloc/rb_class_new_instance.
340
+ return 1;
341
+ }
335
342
  if (sc == rb_cRange) {
336
- parent->val = rb_class_new_instance(len - 1, RARRAY_CONST_PTR(value) + 1, rb_cRange);
343
+ parent->val = rb_class_new_instance((int)(len - 1), RARRAY_CONST_PTR(value) + 1, rb_cRange);
337
344
  } else {
338
345
  // Create a properly initialized struct instance without calling the initialize method.
339
346
  parent->val = rb_obj_alloc(sc);
@@ -348,10 +355,10 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
348
355
  slen = FIX2INT(rb_funcall2(parent->val, oj_length_id, 0, 0));
349
356
  #endif
350
357
  // MRI >= 1.9
351
- if (len - 1 > slen) {
358
+ if (len - 1 > (size_t)slen) {
352
359
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
353
360
  } else {
354
- int i;
361
+ size_t i;
355
362
 
356
363
  for (i = 0; i < len - 1; i++) {
357
364
  rb_struct_aset(parent->val, INT2FIX(i), RARRAY_CONST_PTR(value)[i + 1]);
@@ -698,9 +705,8 @@ oj_object_parse(int argc, VALUE *argv, VALUE self) {
698
705
 
699
706
  if (T_STRING == rb_type(*argv)) {
700
707
  return oj_pi_parse(argc, argv, &pi, 0, 0, 1);
701
- } else {
702
- return oj_pi_sparse(argc, argv, &pi, 0);
703
708
  }
709
+ return oj_pi_sparse(argc, argv, &pi, 0);
704
710
  }
705
711
 
706
712
  VALUE