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/rails.c CHANGED
@@ -140,7 +140,7 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
140
140
  volatile VALUE v;
141
141
  int cnt;
142
142
  int i;
143
- int len;
143
+ size_t len;
144
144
  const char *name;
145
145
 
146
146
  #ifdef RSTRUCT_LEN
@@ -161,7 +161,7 @@ static void dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
161
161
  volatile VALUE s = rb_sym2str(RARRAY_AREF(ma, i));
162
162
 
163
163
  name = RSTRING_PTR(s);
164
- len = (int)RSTRING_LEN(s);
164
+ len = RSTRING_LEN(s);
165
165
  assure_size(out, size + sep_len + 6);
166
166
  if (0 < i) {
167
167
  *out->cur++ = ',';
@@ -205,11 +205,11 @@ static void dump_bigdecimal(VALUE obj, int depth, Out out, bool as_ok) {
205
205
  if ('I' == *str || 'N' == *str || ('-' == *str && 'I' == str[1])) {
206
206
  oj_dump_nil(Qnil, depth, out, false);
207
207
  } else if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) {
208
- oj_dump_cstr(str, (int)RSTRING_LEN(rstr), 0, 0, out);
208
+ oj_dump_cstr(str, RSTRING_LEN(rstr), 0, 0, out);
209
209
  } else if (Yes == out->opts->bigdec_as_num) {
210
- oj_dump_raw(str, (int)RSTRING_LEN(rstr), out);
210
+ oj_dump_raw(str, RSTRING_LEN(rstr), out);
211
211
  } else {
212
- oj_dump_cstr(str, (int)RSTRING_LEN(rstr), 0, 0, out);
212
+ oj_dump_cstr(str, RSTRING_LEN(rstr), 0, 0, out);
213
213
  }
214
214
  }
215
215
 
@@ -330,14 +330,14 @@ static void dump_timewithzone(VALUE obj, int depth, Out out, bool as_ok) {
330
330
  static void dump_to_s(VALUE obj, int depth, Out out, bool as_ok) {
331
331
  volatile VALUE rstr = oj_safe_string_convert(obj);
332
332
 
333
- oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
333
+ oj_dump_cstr(RSTRING_PTR(rstr), RSTRING_LEN(rstr), 0, 0, out);
334
334
  }
335
335
 
336
336
  static ID parameters_id = 0;
337
337
 
338
338
  typedef struct _strLen {
339
339
  const char *str;
340
- int len;
340
+ size_t len;
341
341
  } *StrLen;
342
342
 
343
343
  static void dump_actioncontroller_parameters(VALUE obj, int depth, Out out, bool as_ok) {
@@ -352,10 +352,10 @@ static StrLen columns_array(VALUE rcols, int *ccnt) {
352
352
  volatile VALUE v;
353
353
  StrLen cp;
354
354
  StrLen cols;
355
- int i;
356
- int cnt = (int)RARRAY_LEN(rcols);
355
+ size_t i;
356
+ size_t cnt = RARRAY_LEN(rcols);
357
357
 
358
- *ccnt = cnt;
358
+ *ccnt = (int)cnt;
359
359
  cols = OJ_R_ALLOC_N(struct _strLen, cnt);
360
360
  for (i = 0, cp = cols; i < cnt; i++, cp++) {
361
361
  v = RARRAY_AREF(rcols, i);
@@ -363,7 +363,7 @@ static StrLen columns_array(VALUE rcols, int *ccnt) {
363
363
  v = oj_safe_string_convert(v);
364
364
  }
365
365
  cp->str = StringValuePtr(v);
366
- cp->len = (int)RSTRING_LEN(v);
366
+ cp->len = RSTRING_LEN(v);
367
367
  }
368
368
  return cols;
369
369
  }
@@ -424,7 +424,8 @@ static void dump_activerecord_result(VALUE obj, int depth, Out out, bool as_ok)
424
424
  volatile VALUE rows;
425
425
  StrLen cols;
426
426
  int ccnt = 0;
427
- int i, rcnt;
427
+ size_t i;
428
+ size_t rcnt;
428
429
  size_t size;
429
430
  int d2 = depth + 1;
430
431
 
@@ -435,7 +436,7 @@ static void dump_activerecord_result(VALUE obj, int depth, Out out, bool as_ok)
435
436
  out->argc = 0;
436
437
  cols = columns_array(rb_ivar_get(obj, columns_id), &ccnt);
437
438
  rows = rb_ivar_get(obj, rows_id);
438
- rcnt = (int)RARRAY_LEN(rows);
439
+ rcnt = RARRAY_LEN(rows);
439
440
  assure_size(out, 2);
440
441
  *out->cur++ = '[';
441
442
  if (out->opts->dump_opts.use) {
@@ -622,6 +623,7 @@ static void encoder_free(void *ptr) {
622
623
  if (NULL != ptr) {
623
624
  Encoder e = (Encoder)ptr;
624
625
 
626
+ oj_options_release(&e->opts);
625
627
  if (NULL != e->ropts.table) {
626
628
  OJ_R_FREE(e->ropts.table);
627
629
  }
@@ -632,10 +634,19 @@ static void encoder_free(void *ptr) {
632
634
  static void encoder_mark(void *ptr) {
633
635
  if (NULL != ptr) {
634
636
  Encoder e = (Encoder)ptr;
637
+ int i;
635
638
 
639
+ oj_options_mark(&e->opts);
636
640
  if (Qnil != e->arg) {
637
641
  rb_gc_mark(e->arg);
638
642
  }
643
+ // The optimized classes in the encoder table are not reachable from
644
+ // anywhere else once optimize() is called on the encoder itself.
645
+ for (i = 0; i < e->ropts.len; i++) {
646
+ if (Qnil != e->ropts.table[i].clas) {
647
+ rb_gc_mark(e->ropts.table[i].clas);
648
+ }
649
+ }
639
650
  }
640
651
  }
641
652
 
@@ -660,13 +671,21 @@ static VALUE encoder_new(int argc, VALUE *argv, VALUE self) {
660
671
  Encoder e = OJ_R_ALLOC(struct _encoder);
661
672
 
662
673
  e->opts = oj_default_options;
663
- e->arg = Qnil;
674
+ // Detach from the match_string regexps owned by the defaults before the
675
+ // options are parsed so that a :match_string option builds a chain of its
676
+ // own that is freed with the encoder.
677
+ e->opts.str_rx.head = NULL;
678
+ e->opts.str_rx.tail = NULL;
664
679
  copy_opts(&ropts, &e->ropts);
665
680
 
666
681
  if (1 <= argc && Qnil != *argv) {
667
- oj_parse_options(*argv, &e->opts);
668
682
  e->arg = *argv;
683
+ } else {
684
+ e->arg = rb_hash_new();
669
685
  }
686
+ oj_parse_options(e->arg, &e->opts);
687
+ oj_options_take_ownership(&e->opts);
688
+
670
689
  return TypedData_Wrap_Struct(encoder_class, &oj_encoder_type, e);
671
690
  }
672
691
 
@@ -917,7 +936,7 @@ static VALUE encode(VALUE obj, ROptTable ropts, Options opts, int argc, VALUE *a
917
936
  if (Yes == copts.circular) {
918
937
  oj_cache8_new(&out.circ_cache);
919
938
  }
920
- // dump_rails_val(*argv, 0, &out, true);
939
+
921
940
  rb_protect(protect_dump, (VALUE)&oo, &line);
922
941
 
923
942
  if (0 == line) {
@@ -933,8 +952,7 @@ static VALUE encode(VALUE obj, ROptTable ropts, Options opts, int argc, VALUE *a
933
952
  if (0 == out.buf) {
934
953
  rb_raise(rb_eNoMemError, "Not enough memory.");
935
954
  }
936
- rstr = rb_str_new2(out.buf);
937
- rstr = oj_encode(rstr);
955
+ rstr = rb_utf8_str_new_cstr(out.buf);
938
956
  }
939
957
  if (Yes == copts.circular) {
940
958
  oj_cache8_delete(out.circ_cache);
@@ -1101,6 +1119,8 @@ static VALUE rails_set_decoder(VALUE self) {
1101
1119
  } else {
1102
1120
  json_error = rb_define_class_under(json, "JSONError", rb_eStandardError);
1103
1121
  }
1122
+
1123
+ rb_global_variable(&oj_json_parser_error_class);
1104
1124
  if (rb_const_defined_at(json, rb_intern("ParserError"))) {
1105
1125
  oj_json_parser_error_class = rb_const_get(json, rb_intern("ParserError"));
1106
1126
  } else {
@@ -1172,7 +1192,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1172
1192
  char buf[64];
1173
1193
  char *b;
1174
1194
  double d = rb_num2dbl(obj);
1175
- int cnt = 0;
1195
+ size_t cnt = 0;
1176
1196
 
1177
1197
  if (0.0 == d) {
1178
1198
  b = buf;
@@ -1193,7 +1213,7 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1193
1213
  volatile VALUE rstr = oj_safe_string_convert(obj);
1194
1214
 
1195
1215
  strcpy(buf, RSTRING_PTR(rstr));
1196
- cnt = (int)RSTRING_LEN(rstr);
1216
+ cnt = RSTRING_LEN(rstr);
1197
1217
  }
1198
1218
  }
1199
1219
  assure_size(out, cnt);
@@ -1205,7 +1225,8 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1205
1225
 
1206
1226
  static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
1207
1227
  size_t size;
1208
- int i, cnt;
1228
+ size_t i;
1229
+ size_t cnt;
1209
1230
  int d2 = depth + 1;
1210
1231
 
1211
1232
  if (Yes == out->opts->circular) {
@@ -1214,12 +1235,11 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
1214
1235
  return;
1215
1236
  }
1216
1237
  }
1217
- // if (!oj_rails_array_opt && as_ok && 0 < out->argc && rb_respond_to(a, oj_as_json_id)) {
1218
- if (as_ok && 0 < out->argc && rb_respond_to(a, oj_as_json_id)) {
1238
+ if (!oj_rails_array_opt && as_ok && rb_respond_to(a, oj_as_json_id)) {
1219
1239
  dump_as_json(a, depth, out, false);
1220
1240
  return;
1221
1241
  }
1222
- cnt = (int)RARRAY_LEN(a);
1242
+ cnt = RARRAY_LEN(a);
1223
1243
  *out->cur++ = '[';
1224
1244
  size = 2;
1225
1245
  assure_size(out, size);
@@ -1282,18 +1302,19 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
1282
1302
  if (out->omit_nil && Qnil == value) {
1283
1303
  return ST_CONTINUE;
1284
1304
  }
1285
- if (rtype != T_STRING && rtype != T_SYMBOL) {
1286
- key = oj_safe_string_convert(key);
1287
- rtype = rb_type(key);
1305
+ if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
1306
+ if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
1307
+ return ST_CONTINUE;
1308
+ }
1288
1309
  }
1289
1310
  if (!out->opts->dump_opts.use) {
1290
1311
  size = depth * out->indent + 1;
1291
1312
  assure_size(out, size);
1292
1313
  fill_indent(out, depth);
1293
- if (rtype == T_STRING) {
1294
- oj_dump_str(key, 0, out, false);
1295
- } else {
1296
- oj_dump_sym(key, 0, out, false);
1314
+ switch (rtype) {
1315
+ case T_STRING: oj_dump_str(key, 0, out, false); break;
1316
+ case T_SYMBOL: oj_dump_sym(key, 0, out, false); break;
1317
+ default: oj_dump_str(oj_safe_string_convert(key), 0, out, false); break;
1297
1318
  }
1298
1319
  *out->cur++ = ':';
1299
1320
  } else {
@@ -1308,10 +1329,10 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
1308
1329
  APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
1309
1330
  }
1310
1331
  }
1311
- if (rtype == T_STRING) {
1312
- oj_dump_str(key, 0, out, false);
1313
- } else {
1314
- oj_dump_sym(key, 0, out, false);
1332
+ switch (rtype) {
1333
+ case T_STRING: oj_dump_str(key, 0, out, false); break;
1334
+ case T_SYMBOL: oj_dump_sym(key, 0, out, false); break;
1335
+ default: oj_dump_str(oj_safe_string_convert(key), 0, out, false); break;
1315
1336
  }
1316
1337
  size = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
1317
1338
  assure_size(out, size);
@@ -1340,7 +1361,7 @@ static void dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
1340
1361
  return;
1341
1362
  }
1342
1363
  }
1343
- if ((!oj_rails_hash_opt || 0 < out->argc) && as_ok && rb_respond_to(obj, oj_as_json_id)) {
1364
+ if (!oj_rails_hash_opt && as_ok && rb_respond_to(obj, oj_as_json_id)) {
1344
1365
  dump_as_json(obj, depth, out, false);
1345
1366
  return;
1346
1367
  }
data/ext/oj/reader.c CHANGED
@@ -101,7 +101,7 @@ int oj_reader_read(Reader reader) {
101
101
  } else {
102
102
  shift = reader->pro - reader->head - 1; // leave one character so we can backup one
103
103
  }
104
- if (0 >= shift) { /* no space left so allocate more */
104
+ if (0 >= shift) { /* no space left so allocate more */
105
105
  const char *old = reader->head;
106
106
  size_t size = reader->end - reader->head + BUF_PAD;
107
107
 
@@ -164,8 +164,14 @@ static VALUE partial_io_cb(VALUE rbuf) {
164
164
  }
165
165
  str = StringValuePtr(rstr);
166
166
  cnt = RSTRING_LEN(rstr);
167
- strcpy(reader->tail, str);
168
- reader->read_end = reader->tail + cnt;
167
+ if (cnt > (size_t)(reader->end - reader->tail)) {
168
+ // A misbehaving IO returned more than the requested number of bytes.
169
+ // Copying it in with strcpy() would overflow the reader buffer.
170
+ rb_raise(rb_eIOError, "read returned more than the requested number of bytes");
171
+ }
172
+ memcpy(reader->tail, str, cnt);
173
+ reader->tail[cnt] = '\0';
174
+ reader->read_end = reader->tail + cnt;
169
175
 
170
176
  return Qtrue;
171
177
  }
@@ -184,8 +190,14 @@ static VALUE io_cb(VALUE rbuf) {
184
190
  }
185
191
  str = StringValuePtr(rstr);
186
192
  cnt = RSTRING_LEN(rstr);
187
- strcpy(reader->tail, str);
188
- reader->read_end = reader->tail + cnt;
193
+ if (cnt > (size_t)(reader->end - reader->tail)) {
194
+ // A misbehaving IO returned more than the requested number of bytes.
195
+ // Copying it in with strcpy() would overflow the reader buffer.
196
+ rb_raise(rb_eIOError, "read returned more than the requested number of bytes");
197
+ }
198
+ memcpy(reader->tail, str, cnt);
199
+ reader->tail[cnt] = '\0';
200
+ reader->read_end = reader->tail + cnt;
189
201
 
190
202
  return Qtrue;
191
203
  }
data/ext/oj/rxclass.c CHANGED
@@ -96,7 +96,7 @@ int oj_rxclass_append(RxClass rc, const char *expr, VALUE clas) {
96
96
  }
97
97
 
98
98
  VALUE
99
- oj_rxclass_match(RxClass rc, const char *str, int len) {
99
+ oj_rxclass_match(RxClass rc, const char *str, size_t len) {
100
100
  RxC rxc;
101
101
  char buf[4096];
102
102
 
@@ -142,3 +142,19 @@ void oj_rxclass_copy(RxClass src, RxClass dest) {
142
142
  }
143
143
  }
144
144
  }
145
+
146
+ // Add the Ruby objects in the chain to an array. The regexps and the classes
147
+ // can be referenced from nowhere else, as with a default match_string option,
148
+ // and are collected unless something holds onto them.
149
+ void oj_rxclass_keep(RxClass rc, VALUE keep) {
150
+ RxC rxc;
151
+
152
+ for (rxc = rc->head; NULL != rxc; rxc = rxc->next) {
153
+ if (Qnil != rxc->rrx) {
154
+ rb_ary_push(keep, rxc->rrx);
155
+ }
156
+ if (Qnil != rxc->clas) {
157
+ rb_ary_push(keep, rxc->clas);
158
+ }
159
+ }
160
+ }
data/ext/oj/rxclass.h CHANGED
@@ -19,8 +19,9 @@ typedef struct _rxClass {
19
19
  extern void oj_rxclass_init(RxClass rc);
20
20
  extern void oj_rxclass_cleanup(RxClass rc);
21
21
  extern int oj_rxclass_append(RxClass rc, const char *expr, VALUE clas);
22
- extern VALUE oj_rxclass_match(RxClass rc, const char *str, int len);
22
+ extern VALUE oj_rxclass_match(RxClass rc, const char *str, size_t len);
23
23
  extern void oj_rxclass_copy(RxClass src, RxClass dest);
24
24
  extern void oj_rxclass_rappend(RxClass rc, VALUE rx, VALUE clas);
25
+ extern void oj_rxclass_keep(RxClass rc, VALUE keep);
25
26
 
26
27
  #endif /* OJ_RXCLASS_H */
data/ext/oj/safe.c ADDED
@@ -0,0 +1,230 @@
1
+
2
+ #include "safe.h"
3
+
4
+ static VALUE max_hash_size_sym, max_array_size_sym, max_depth_sym, max_total_elements_sym, max_hash_size_error_class,
5
+ max_array_size_error_class, max_depth_error_class, max_total_elements_error_class;
6
+
7
+ static void check_object_size(safe_T safe) {
8
+ if (NIL_P(safe->max_hash_size)) {
9
+ return;
10
+ }
11
+
12
+ struct _usual usual = safe->usual;
13
+ Col current_object_location = usual.ctail - 1;
14
+
15
+ long int number_of_items_in_stack = usual.vtail - usual.vhead;
16
+ long int number_of_items_in_hash = (number_of_items_in_stack - current_object_location->vi - 1) / 2;
17
+
18
+ if (safe->max_hash_size > number_of_items_in_hash) {
19
+ return;
20
+ }
21
+
22
+ rb_raise(max_hash_size_error_class, "Too many object items!");
23
+ }
24
+
25
+ static void check_array_size(safe_T safe) {
26
+ if (NIL_P(safe->max_array_size)) {
27
+ return;
28
+ }
29
+
30
+ struct _usual usual = safe->usual;
31
+ Col current_object_location = usual.ctail - 1;
32
+
33
+ long int number_of_items_in_stack = usual.vtail - usual.vhead;
34
+ long int number_of_items_in_array = number_of_items_in_stack - current_object_location->vi - 1;
35
+
36
+ if (safe->max_array_size > number_of_items_in_array) {
37
+ return;
38
+ }
39
+
40
+ rb_raise(max_array_size_error_class, "Too many array items!");
41
+ }
42
+
43
+ static void check_max_depth(safe_T safe, ojParser p) {
44
+ if (NIL_P(safe->max_depth) || safe->max_depth >= (p->depth + 1)) {
45
+ return;
46
+ }
47
+
48
+ rb_raise(max_depth_error_class, "JSON is too deep!");
49
+ }
50
+
51
+ static void check_max_total_elements(safe_T safe) {
52
+ /*
53
+ * We check if `max_total_elements` is greater than `current_elements_count`
54
+ * (instead of greater than or equal) because top-level elements (e.g., [],
55
+ * null, true) are not counted. As a result, `current_elements_count`
56
+ * always holds one less than the actual total.
57
+ */
58
+ if (NIL_P(safe->max_total_elements) || safe->max_total_elements > safe->current_elements_count) {
59
+ return;
60
+ }
61
+
62
+ rb_raise(max_total_elements_error_class, "Too many elements!");
63
+ }
64
+
65
+ static void safe_start(ojParser p) {
66
+ safe_T safe = (safe_T)p->ctx;
67
+
68
+ safe->current_hash_size = 0;
69
+ safe->current_array_size = 0;
70
+ safe->current_elements_count = 0;
71
+
72
+ safe->delegated_start_func(p);
73
+ }
74
+
75
+ static void safe_open_object(ojParser p) {
76
+ safe_T safe = (safe_T)p->ctx;
77
+
78
+ safe->current_hash_size++;
79
+ safe->current_elements_count++;
80
+
81
+ check_array_size(safe);
82
+ check_max_depth(safe, p);
83
+ check_max_total_elements(safe);
84
+
85
+ safe->delegated_open_object_func(p);
86
+ }
87
+
88
+ static void safe_open_array(ojParser p) {
89
+ safe_T safe = (safe_T)p->ctx;
90
+
91
+ safe->current_array_size++;
92
+ safe->current_elements_count++;
93
+
94
+ check_array_size(safe);
95
+ check_max_depth(safe, p);
96
+ check_max_total_elements(safe);
97
+
98
+ safe->delegated_open_array_func(p);
99
+ }
100
+
101
+ DEFINE_DELEGATED_FUNCTION(add_null);
102
+ DEFINE_DELEGATED_FUNCTION(add_true);
103
+ DEFINE_DELEGATED_FUNCTION(add_false);
104
+ DEFINE_DELEGATED_FUNCTION(add_int);
105
+ DEFINE_DELEGATED_FUNCTION(add_float);
106
+ DEFINE_DELEGATED_FUNCTION(add_big);
107
+ DEFINE_DELEGATED_FUNCTION(add_str);
108
+
109
+ static void safe_open_object_key(ojParser p) {
110
+ safe_T safe = (safe_T)p->ctx;
111
+
112
+ safe->current_hash_size++;
113
+ safe->current_elements_count += 2;
114
+
115
+ check_object_size(safe);
116
+ check_max_depth(safe, p);
117
+ check_max_total_elements(safe);
118
+
119
+ safe->delegated_open_object_key_func(p);
120
+ }
121
+
122
+ static void safe_open_array_key(ojParser p) {
123
+ safe_T safe = (safe_T)p->ctx;
124
+
125
+ safe->current_array_size++;
126
+ safe->current_elements_count += 2;
127
+
128
+ check_object_size(safe);
129
+ check_max_depth(safe, p);
130
+ check_max_total_elements(safe);
131
+
132
+ safe->delegated_open_array_key_func(p);
133
+ }
134
+
135
+ DEFINE_DELEGATED_OBJECT_FUNCTION(add_null);
136
+ DEFINE_DELEGATED_OBJECT_FUNCTION(add_true);
137
+ DEFINE_DELEGATED_OBJECT_FUNCTION(add_false);
138
+ DEFINE_DELEGATED_OBJECT_FUNCTION(add_int);
139
+ DEFINE_DELEGATED_OBJECT_FUNCTION(add_float);
140
+ DEFINE_DELEGATED_OBJECT_FUNCTION(add_big);
141
+ DEFINE_DELEGATED_OBJECT_FUNCTION(add_str);
142
+
143
+ void oj_init_safe_parser(ojParser p, safe_T safe, VALUE options) {
144
+ // Safe parser inherits all members of usual parser
145
+ oj_init_usual(p, &safe->usual);
146
+
147
+ safe->delegated_start_func = p->start;
148
+ p->start = safe_start;
149
+
150
+ Funcs f;
151
+
152
+ // Array parser functions
153
+ f = &p->funcs[ARRAY_FUN];
154
+ safe->delegated_open_object_func = f->open_object;
155
+ f->open_object = safe_open_object;
156
+ safe->delegated_open_array_func = f->open_array;
157
+ f->open_array = safe_open_array;
158
+ // The following overrides are done for counting objects
159
+ safe->delegated_add_null_func = f->add_null;
160
+ f->add_null = safe_add_null;
161
+ safe->delegated_add_true_func = f->add_true;
162
+ f->add_true = safe_add_true;
163
+ safe->delegated_add_false_func = f->add_false;
164
+ f->add_false = safe_add_false;
165
+ safe->delegated_add_int_func = f->add_int;
166
+ f->add_int = safe_add_int;
167
+ safe->delegated_add_float_func = f->add_float;
168
+ f->add_float = safe_add_float;
169
+ safe->delegated_add_big_func = f->add_big;
170
+ f->add_big = safe_add_big;
171
+ safe->delegated_add_str_func = f->add_str;
172
+ f->add_str = safe_add_str;
173
+
174
+ // Object parser functions
175
+ f = &p->funcs[OBJECT_FUN];
176
+ safe->delegated_open_object_key_func = f->open_object;
177
+ f->open_object = safe_open_object_key;
178
+ safe->delegated_open_array_key_func = f->open_array;
179
+ f->open_array = safe_open_array_key;
180
+ // The following overrides are done for counting objects
181
+ safe->delegated_add_null_key_func = f->add_null;
182
+ f->add_null = safe_add_null_key;
183
+ safe->delegated_add_true_key_func = f->add_true;
184
+ f->add_true = safe_add_true_key;
185
+ safe->delegated_add_false_key_func = f->add_false;
186
+ f->add_false = safe_add_false_key;
187
+ safe->delegated_add_int_key_func = f->add_int;
188
+ f->add_int = safe_add_int_key;
189
+ safe->delegated_add_float_key_func = f->add_float;
190
+ f->add_float = safe_add_float_key;
191
+ safe->delegated_add_big_key_func = f->add_big;
192
+ f->add_big = safe_add_big_key;
193
+ safe->delegated_add_str_key_func = f->add_str;
194
+ f->add_str = safe_add_str_key;
195
+
196
+ SET_CONFIG(max_hash_size);
197
+ SET_CONFIG(max_array_size);
198
+ SET_CONFIG(max_depth);
199
+ SET_CONFIG(max_total_elements);
200
+ }
201
+
202
+ void oj_set_parser_safe(ojParser p, VALUE options) {
203
+ safe_T s = OJ_R_ALLOC(struct _safe_S);
204
+
205
+ oj_init_safe_parser(p, s, options);
206
+ }
207
+
208
+ void oj_safe_init(VALUE parser_class) {
209
+ VALUE validation_error_class = rb_define_class_under(parser_class, "ValidationError", rb_eRuntimeError);
210
+
211
+ max_hash_size_error_class = rb_define_class_under(parser_class, "HashSizeError", validation_error_class);
212
+ max_array_size_error_class = rb_define_class_under(parser_class, "ArraySizeError", validation_error_class);
213
+ max_depth_error_class = rb_define_class_under(parser_class, "DepthError", validation_error_class);
214
+ max_total_elements_error_class = rb_define_class_under(parser_class, "TotalElementsError", validation_error_class);
215
+
216
+ rb_gc_register_address(&max_hash_size_error_class);
217
+ rb_gc_register_address(&max_array_size_error_class);
218
+ rb_gc_register_address(&max_depth_error_class);
219
+ rb_gc_register_address(&max_total_elements_error_class);
220
+
221
+ max_hash_size_sym = ID2SYM(rb_intern("max_hash_size"));
222
+ max_array_size_sym = ID2SYM(rb_intern("max_array_size"));
223
+ max_depth_sym = ID2SYM(rb_intern("max_depth"));
224
+ max_total_elements_sym = ID2SYM(rb_intern("max_total_elements"));
225
+
226
+ rb_gc_register_address(&max_hash_size_sym);
227
+ rb_gc_register_address(&max_array_size_sym);
228
+ rb_gc_register_address(&max_depth_sym);
229
+ rb_gc_register_address(&max_total_elements_sym);
230
+ }
data/ext/oj/safe.h ADDED
@@ -0,0 +1,79 @@
1
+ #include <ruby.h>
2
+
3
+ #include "parser.h"
4
+ #include "usual.h"
5
+
6
+ #define SET_CONFIG(config_name) \
7
+ do { \
8
+ VALUE rb_##config_name = rb_hash_aref(options, config_name##_sym); \
9
+ \
10
+ if (RB_INTEGER_TYPE_P(rb_##config_name)) { \
11
+ safe->config_name = NUM2LONG(rb_##config_name); \
12
+ } else if (!NIL_P(rb_##config_name)) { \
13
+ rb_raise(rb_eArgError, "Incorrect value provided for `" #config_name "`"); \
14
+ } else { \
15
+ safe->config_name = Qnil; \
16
+ } \
17
+ } while (0);
18
+
19
+ #define DEFINE_DELEGATED_FUNCTION(function_name) \
20
+ static void safe_##function_name(ojParser p) { \
21
+ safe_T safe = (safe_T)p->ctx; \
22
+ \
23
+ safe->current_elements_count++; \
24
+ \
25
+ check_array_size(safe); \
26
+ check_max_total_elements(safe); \
27
+ \
28
+ safe->delegated_##function_name##_func(p); \
29
+ }
30
+
31
+ #define DEFINE_DELEGATED_OBJECT_FUNCTION(function_name) \
32
+ static void safe_##function_name##_key(ojParser p) { \
33
+ safe_T safe = (safe_T)p->ctx; \
34
+ \
35
+ safe->current_elements_count += 2; \
36
+ \
37
+ check_object_size(safe); \
38
+ check_max_total_elements(safe); \
39
+ \
40
+ safe->delegated_##function_name##_key_func(p); \
41
+ }
42
+
43
+ typedef struct _safe_S {
44
+ struct _usual usual;
45
+
46
+ long int max_hash_size;
47
+ long int max_array_size;
48
+ long int max_depth;
49
+ long int max_total_elements;
50
+ long int max_json_size_bytes;
51
+
52
+ long int current_hash_size;
53
+ long int current_array_size;
54
+ long int current_elements_count;
55
+
56
+ void (*delegated_start_func)(struct _ojParser *p);
57
+
58
+ // Array functions
59
+ void (*delegated_open_object_func)(struct _ojParser *p);
60
+ void (*delegated_open_array_func)(struct _ojParser *p);
61
+ void (*delegated_add_null_func)(struct _ojParser *p);
62
+ void (*delegated_add_true_func)(struct _ojParser *p);
63
+ void (*delegated_add_false_func)(struct _ojParser *p);
64
+ void (*delegated_add_int_func)(struct _ojParser *p);
65
+ void (*delegated_add_float_func)(struct _ojParser *p);
66
+ void (*delegated_add_big_func)(struct _ojParser *p);
67
+ void (*delegated_add_str_func)(struct _ojParser *p);
68
+
69
+ // Object functions
70
+ void (*delegated_open_object_key_func)(struct _ojParser *p);
71
+ void (*delegated_open_array_key_func)(struct _ojParser *p);
72
+ void (*delegated_add_null_key_func)(struct _ojParser *p);
73
+ void (*delegated_add_true_key_func)(struct _ojParser *p);
74
+ void (*delegated_add_false_key_func)(struct _ojParser *p);
75
+ void (*delegated_add_int_key_func)(struct _ojParser *p);
76
+ void (*delegated_add_float_key_func)(struct _ojParser *p);
77
+ void (*delegated_add_big_key_func)(struct _ojParser *p);
78
+ void (*delegated_add_str_key_func)(struct _ojParser *p);
79
+ } *safe_T;