oj 3.17.3 → 3.17.5

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.
data/ext/oj/intern.c CHANGED
@@ -97,6 +97,8 @@ static const rb_data_type_t oj_cache_type = {
97
97
  };
98
98
 
99
99
  void oj_hash_init(void) {
100
+ oj_hash_seed_init();
101
+
100
102
  VALUE cache_class = rb_define_class_under(Oj, "Cache", rb_cObject);
101
103
  rb_undef_alloc_func(cache_class);
102
104
 
@@ -151,7 +153,7 @@ ID oj_attr_intern(const char *key, size_t len) {
151
153
  static uint64_t hash_calc(const uint8_t *key, size_t len) {
152
154
  const uint8_t *end = key + len;
153
155
  const uint8_t *endless = key + (len & 0xFFFFFFFC);
154
- uint64_t h = (uint64_t)len;
156
+ uint64_t h = (uint64_t)len ^ oj_hash_seed;
155
157
  uint64_t k;
156
158
 
157
159
  while (key < endless) {
@@ -188,6 +190,9 @@ static VALUE resolve_classname(VALUE mod, const char *classname, int auto_define
188
190
 
189
191
  if (rb_const_defined_at(mod, ci)) {
190
192
  clas = rb_const_get_at(mod, ci);
193
+ if (!RB_TYPE_P(clas, T_CLASS) && !RB_TYPE_P(clas, T_MODULE)) {
194
+ clas = Qundef;
195
+ }
191
196
  } else if (auto_define) {
192
197
  clas = rb_define_class_under(mod, classname, oj_bag_class);
193
198
  } else {
data/ext/oj/object.c CHANGED
@@ -31,7 +31,7 @@ inline static long read_long(const char *str, size_t len) {
31
31
  static VALUE calc_hash_key(ParseInfo pi, Val kval, char k1) {
32
32
  volatile VALUE rkey;
33
33
 
34
- if (':' == k1) {
34
+ if (':' == k1 && 0 < kval->klen) {
35
35
  return ID2SYM(rb_intern3(kval->key + 1, kval->klen - 1, oj_utf8_encoding));
36
36
  }
37
37
  if (Yes == pi->options.sym_key) {
@@ -54,7 +54,7 @@ static VALUE str_to_value(ParseInfo pi, const char *str, size_t len, const char
54
54
  } else if (pi->circ_array && 3 <= len && '^' == *orig && 'r' == orig[1]) {
55
55
  long i = read_long(str + 2, len - 2);
56
56
 
57
- if (0 > i) {
57
+ if (0 >= i) {
58
58
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a valid ID number");
59
59
  return Qnil;
60
60
  }
@@ -207,7 +207,7 @@ oj_parse_xml_time(const char *str, int len) {
207
207
 
208
208
  static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t len) {
209
209
  const char *key = kval->key;
210
- int klen = kval->klen;
210
+ size_t klen = kval->klen;
211
211
 
212
212
  if (2 == klen) {
213
213
  switch (key[1]) {
@@ -230,7 +230,12 @@ static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t
230
230
  parent->odd_args = oj_odd_alloc_args(odd);
231
231
  break;
232
232
  }
233
- case 'm': parent->val = ID2SYM(rb_intern3(str + 1, len - 1, oj_utf8_encoding)); break;
233
+ case 'm':
234
+ if (0 == len) {
235
+ return 0;
236
+ }
237
+ parent->val = ID2SYM(rb_intern3(str + 1, len - 1, oj_utf8_encoding));
238
+ break;
234
239
  case 's': parent->val = rb_utf8_str_new(str, len); break;
235
240
  case 'c': // class
236
241
  {
@@ -319,20 +324,26 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
319
324
  e1 = *RARRAY_CONST_PTR(value);
320
325
  // check for anonymous Struct
321
326
  if (T_ARRAY == rb_type(e1)) {
322
- VALUE args[1024];
323
- volatile VALUE rstr;
327
+ // Build the member-name list in a GC-managed Ruby array. Using a
328
+ // fixed C buffer (VALUE args[1024]) here overflowed the stack when
329
+ // the JSON supplied more than 1024 member names.
330
+ volatile VALUE names = rb_ary_new2(RARRAY_LEN(e1));
324
331
  size_t i;
325
332
  size_t cnt = RARRAY_LEN(e1);
326
333
 
327
334
  for (i = 0; i < cnt; i++) {
328
- rstr = RARRAY_AREF(e1, i);
329
- args[i] = rb_funcall(rstr, oj_to_sym_id, 0);
335
+ rb_ary_push(names, rb_funcall(RARRAY_AREF(e1, i), oj_to_sym_id, 0));
330
336
  }
331
- sc = rb_funcall2(rb_cStruct, oj_new_id, (int)cnt, args);
337
+ sc = rb_apply(rb_cStruct, oj_new_id, names);
332
338
  } else {
333
339
  // If struct is not defined then we let this fail and raise an exception.
334
340
  sc = oj_name2struct(pi, *RARRAY_CONST_PTR(value), rb_eArgError);
335
341
  }
342
+ if (Qundef == sc || Qnil == sc) {
343
+ // oj_name2struct already recorded the error; do not pass an
344
+ // unresolved (Qundef) class on to rb_obj_alloc/rb_class_new_instance.
345
+ return 1;
346
+ }
336
347
  if (sc == rb_cRange) {
337
348
  parent->val = rb_class_new_instance((int)(len - 1), RARRAY_CONST_PTR(value) + 1, rb_cRange);
338
349
  } else {
@@ -389,7 +400,7 @@ void oj_set_obj_ivar(Val parent, Val kval, VALUE value) {
389
400
 
390
401
  static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
391
402
  const char *key = kval->key;
392
- int klen = kval->klen;
403
+ size_t klen = kval->klen;
393
404
  Val parent = stack_peek(&pi->stack);
394
405
  volatile VALUE rval = Qnil;
395
406
 
@@ -431,7 +442,7 @@ WHICH_TYPE:
431
442
  if (0 != oj_odd_set_arg(parent->odd_args, kval->key, kval->klen, rval)) {
432
443
  char buf[256];
433
444
 
434
- if ((int)sizeof(buf) - 1 <= klen) {
445
+ if (sizeof(buf) - 1 <= klen) {
435
446
  klen = sizeof(buf) - 2;
436
447
  }
437
448
  memcpy(buf, key, klen);
@@ -460,7 +471,7 @@ WHICH_TYPE:
460
471
 
461
472
  static void hash_set_num(ParseInfo pi, Val kval, NumInfo ni) {
462
473
  const char *key = kval->key;
463
- int klen = kval->klen;
474
+ size_t klen = kval->klen;
464
475
  Val parent = stack_peek(&pi->stack);
465
476
  volatile VALUE rval = Qnil;
466
477
 
@@ -500,7 +511,7 @@ WHICH_TYPE:
500
511
  if (0 != oj_odd_set_arg(parent->odd_args, key, klen, rval)) {
501
512
  char buf[256];
502
513
 
503
- if ((int)sizeof(buf) - 1 <= klen) {
514
+ if (sizeof(buf) - 1 <= klen) {
504
515
  klen = sizeof(buf) - 2;
505
516
  }
506
517
  memcpy(buf, key, klen);
@@ -529,7 +540,7 @@ WHICH_TYPE:
529
540
 
530
541
  static void hash_set_value(ParseInfo pi, Val kval, VALUE value) {
531
542
  const char *key = kval->key;
532
- int klen = kval->klen;
543
+ size_t klen = kval->klen;
533
544
  Val parent = stack_peek(&pi->stack);
534
545
 
535
546
  WHICH_TYPE:
@@ -585,7 +596,7 @@ WHICH_TYPE:
585
596
  } else if (0 != oj_odd_set_arg(parent->odd_args, key, klen, value)) {
586
597
  char buf[256];
587
598
 
588
- if ((int)sizeof(buf) - 1 <= klen) {
599
+ if (sizeof(buf) - 1 <= klen) {
589
600
  klen = sizeof(buf) - 2;
590
601
  }
591
602
  memcpy(buf, key, klen);
data/ext/oj/odd.c CHANGED
@@ -166,7 +166,11 @@ Odd oj_get_oddc(const char *classname, size_t len) {
166
166
  if (len == odd->clen && 0 == strncmp(classname, odd->classname, len)) {
167
167
  return odd;
168
168
  }
169
- if (odd->is_module && 0 == strncmp(odd->classname, classname, odd->clen) && ':' == classname[odd->clen]) {
169
+ // The module test needs a "::" after the module name, so the name has
170
+ // to be shorter than what it is compared against. Without that the
171
+ // comparison and the character after it both run past len.
172
+ if (odd->is_module && odd->clen < len && 0 == strncmp(odd->classname, classname, odd->clen) &&
173
+ ':' == classname[odd->clen]) {
170
174
  return odd;
171
175
  }
172
176
  }
@@ -195,7 +199,7 @@ int oj_odd_set_arg(OddArgs args, const char *key, size_t klen, VALUE value) {
195
199
  int i;
196
200
 
197
201
  for (i = args->odd->attr_cnt, np = args->odd->attr_names, vp = args->args; 0 < i; i--, np++, vp++) {
198
- if (0 == strncmp(key, *np, klen) && '\0' == *((*np) + klen)) {
202
+ if (klen == strlen(*np) && 0 == memcmp(key, *np, klen)) {
199
203
  *vp = value;
200
204
  return 0;
201
205
  }
data/ext/oj/oj.c CHANGED
@@ -299,7 +299,8 @@ static VALUE only_array_from_string(const char *str) {
299
299
  * floats, 0 indicates use Ruby
300
300
  * - *:float_format* [_String_] the C printf format string for printing floats.
301
301
  * Default follows the float_precision and will be changed if float_precision is
302
- * changed. The string can be no more than 6 bytes.
302
+ * changed. The string can be no more than 6 bytes and can hold at most one
303
+ * directive, which must be one of aAeEfgG and take no argument of its own.
303
304
  * - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false
304
305
  * - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false
305
306
  * - *:use_raw_json* [_Boolean_|_nil_] call raw_json() methods on dump, default is false
@@ -574,7 +575,8 @@ static VALUE get_def_opts(VALUE self) {
574
575
  * when dumping the seconds portion of time.
575
576
  * - *:float_format* [_String_] the C printf format string for printing floats.
576
577
  * Default follows the float_precision and will be changed if float_precision
577
- * is changed. The string can be no more than 6 bytes.
578
+ * is changed. The string can be no more than 6 bytes and can hold at most one
579
+ * directive, which must be one of aAeEfgG and take no argument of its own.
578
580
  * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0 indicates use Ruby.
579
581
  * - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false.
580
582
  * - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false.
@@ -606,9 +608,52 @@ static VALUE get_def_opts(VALUE self) {
606
608
  * - *:trace* [_Boolean_] turn trace on or off.
607
609
  * - *:safe* [_Boolean_] turn safe mimic on or off.
608
610
  */
611
+ static ID oj_default_options_keeper_id = 0;
612
+
613
+ // The default options are a plain C global so the Ruby objects in them, the
614
+ // classes of the hash_class, array_class, and ignore options and the regexps
615
+ // and the classes of the match_string option, can not be reached by the GC and
616
+ // are collected while the defaults still refer to them. Holding them in an
617
+ // array the GC does know about keeps them alive. The array is rebuilt whenever
618
+ // the defaults change and replaces the previous one so that only the objects
619
+ // the defaults are currently using are held.
620
+ static void keep_default_options(void) {
621
+ VALUE keep = rb_ary_new();
622
+
623
+ if (0 == oj_default_options_keeper_id) {
624
+ oj_default_options_keeper_id = rb_intern("@default_options_keeper");
625
+ }
626
+ if (Qnil != oj_default_options.hash_class) {
627
+ rb_ary_push(keep, oj_default_options.hash_class);
628
+ }
629
+ if (Qnil != oj_default_options.array_class) {
630
+ rb_ary_push(keep, oj_default_options.array_class);
631
+ }
632
+ if (NULL != oj_default_options.ignore) {
633
+ VALUE *vp;
634
+
635
+ for (vp = oj_default_options.ignore; Qnil != *vp; vp++) {
636
+ rb_ary_push(keep, *vp);
637
+ }
638
+ }
639
+ oj_rxclass_keep(&oj_default_options.str_rx, keep);
640
+ // An instance variable of the Oj module is a reference the GC can follow on
641
+ // every engine, unlike a C global.
642
+ rb_ivar_set(Oj, oj_default_options_keeper_id, keep);
643
+ }
644
+
609
645
  static VALUE set_def_opts(VALUE self, VALUE opts) {
610
646
  Check_Type(opts, T_HASH);
647
+ // A :match_string option replaces the regexps instead of adding to them so
648
+ // the ones the defaults are holding have to be freed here, the only place
649
+ // that owns them. A per call options struct only aliases them.
650
+ if (Qnil != rb_hash_lookup(opts, match_string_sym)) {
651
+ oj_rxclass_cleanup(&oj_default_options.str_rx);
652
+ oj_default_options.str_rx.head = NULL;
653
+ oj_default_options.str_rx.tail = NULL;
654
+ }
611
655
  oj_parse_options(opts, &oj_default_options);
656
+ keep_default_options();
612
657
 
613
658
  return Qnil;
614
659
  }
@@ -662,6 +707,46 @@ bool set_yesno_options(VALUE key, VALUE value, Options copts) {
662
707
  return false;
663
708
  }
664
709
 
710
+ inline static bool in_set(const char *set, char c) {
711
+ return '\0' != c && NULL != strchr(set, c);
712
+ }
713
+
714
+ // The format is used with one double and nothing else, so a directive that
715
+ // asks for anything more takes it from whatever is left in the argument
716
+ // registers. %s reads it as a pointer and %n writes through it.
717
+ static void validate_float_format(const char *str, size_t len) {
718
+ const char *s = str;
719
+ const char *end = str + len;
720
+ int cnt = 0;
721
+
722
+ for (; s < end; s++) {
723
+ if ('%' != *s) {
724
+ continue;
725
+ }
726
+ s++;
727
+ if (s < end && '%' == *s) {
728
+ continue;
729
+ }
730
+ if (1 < ++cnt) {
731
+ rb_raise(rb_eArgError, ":float_format must not have more than one directive.");
732
+ }
733
+ for (; s < end && in_set("-+ #0", *s); s++) {
734
+ }
735
+ for (; s < end && '0' <= *s && *s <= '9'; s++) {
736
+ }
737
+ if (s < end && '.' == *s) {
738
+ for (s++; s < end && '0' <= *s && *s <= '9'; s++) {
739
+ }
740
+ }
741
+ if (s < end && 'l' == *s) {
742
+ s++;
743
+ }
744
+ if (end <= s || !in_set("aAeEfgG", *s)) {
745
+ rb_raise(rb_eArgError, ":float_format directive must be one of aAeEfgG and take no argument of its own.");
746
+ }
747
+ }
748
+ }
749
+
665
750
  static const char *make_only_value(VALUE v) {
666
751
  switch (rb_type(v)) {
667
752
  case RUBY_T_NIL:
@@ -910,9 +995,13 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
910
995
  rb_raise(rb_eArgError, ":decimal_class must be BigDecimal or Float.");
911
996
  }
912
997
  } else if (create_id_sym == k) {
998
+ // A per call copy of the options aliases the buffer the default
999
+ // options own, so only the defaults may free it.
1000
+ bool owned = (&oj_default_options == copts && NULL != copts->create_id && oj_json_class != copts->create_id);
1001
+
913
1002
  if (Qnil == v) {
914
- if (oj_json_class != oj_default_options.create_id && NULL != copts->create_id) {
915
- OJ_R_FREE((char *)oj_default_options.create_id);
1003
+ if (owned) {
1004
+ OJ_R_FREE((char *)copts->create_id);
916
1005
  }
917
1006
  copts->create_id = NULL;
918
1007
  copts->create_id_len = 0;
@@ -920,7 +1009,10 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
920
1009
  const char *str = StringValuePtr(v);
921
1010
 
922
1011
  len = RSTRING_LEN(v);
923
- if (len != copts->create_id_len || 0 != strcmp(copts->create_id, str)) {
1012
+ if (NULL == copts->create_id || len != copts->create_id_len || 0 != strcmp(copts->create_id, str)) {
1013
+ if (owned) {
1014
+ OJ_R_FREE((char *)copts->create_id);
1015
+ }
924
1016
  copts->create_id = OJ_R_ALLOC_N(char, len + 1);
925
1017
  strcpy((char *)copts->create_id, str);
926
1018
  copts->create_id_len = len;
@@ -1052,7 +1144,11 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1052
1144
  copts->array_class = v;
1053
1145
  }
1054
1146
  } else if (ignore_sym == k) {
1055
- OJ_R_FREE(copts->ignore);
1147
+ // Only free when replacing the defaults; a per-call copy's ignore still
1148
+ // aliases the default-owned buffer, which oj_free_call_options() frees.
1149
+ if (&oj_default_options == copts) {
1150
+ OJ_R_FREE(copts->ignore);
1151
+ }
1056
1152
  copts->ignore = NULL;
1057
1153
  if (Qnil != v) {
1058
1154
  size_t cnt;
@@ -1122,18 +1218,19 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1122
1218
  if (6 < RSTRING_LEN(v)) {
1123
1219
  rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
1124
1220
  }
1221
+ validate_float_format(RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
1125
1222
  strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
1126
1223
  copts->float_fmt[RSTRING_LEN(v)] = '\0';
1127
1224
  } else if (only_sym == k) {
1128
- if (NULL != copts->dump_opts.only) {
1225
+ // Only free when replacing the defaults; a per-call copy's pointer still
1226
+ // aliases the default-owned buffer, which oj_free_call_options() frees.
1227
+ if (&oj_default_options == copts && NULL != copts->dump_opts.only) {
1129
1228
  OJ_R_FREE((void *)copts->dump_opts.only);
1130
- copts->dump_opts.only = NULL;
1131
1229
  }
1132
1230
  copts->dump_opts.only = make_only_value(v);
1133
1231
  } else if (except_sym == k) {
1134
- if (NULL != copts->dump_opts.except) {
1232
+ if (&oj_default_options == copts && NULL != copts->dump_opts.except) {
1135
1233
  OJ_R_FREE((void *)copts->dump_opts.except);
1136
- copts->dump_opts.except = NULL;
1137
1234
  }
1138
1235
  copts->dump_opts.except = make_only_value(v);
1139
1236
  }
@@ -1153,6 +1250,120 @@ void oj_parse_options(VALUE ropts, Options copts) {
1153
1250
  return;
1154
1251
  }
1155
1252
 
1253
+ // Free the option buffers that oj_parse_options() allocated for a single call.
1254
+ // A per-call options struct is a copy of oj_default_options, so its create_id,
1255
+ // ignore, only, and except members initially alias the ones owned by the
1256
+ // defaults. Only free a member when it differs from the default, i.e. when it
1257
+ // was allocated for this call; the default-owned buffers must be left alone.
1258
+ void oj_free_call_options(Options copts) {
1259
+ if (NULL != copts->create_id && oj_default_options.create_id != copts->create_id) {
1260
+ OJ_R_FREE((char *)copts->create_id);
1261
+ copts->create_id = NULL;
1262
+ copts->create_id_len = 0;
1263
+ }
1264
+ if (NULL != copts->ignore && oj_default_options.ignore != copts->ignore) {
1265
+ OJ_R_FREE(copts->ignore);
1266
+ copts->ignore = NULL;
1267
+ }
1268
+ if (NULL != copts->dump_opts.only && oj_default_options.dump_opts.only != copts->dump_opts.only) {
1269
+ OJ_R_FREE((void *)copts->dump_opts.only);
1270
+ copts->dump_opts.only = NULL;
1271
+ }
1272
+ if (NULL != copts->dump_opts.except && oj_default_options.dump_opts.except != copts->dump_opts.except) {
1273
+ OJ_R_FREE((void *)copts->dump_opts.except);
1274
+ copts->dump_opts.except = NULL;
1275
+ }
1276
+ }
1277
+
1278
+ static char *dup_option_str(const char *str, size_t len) {
1279
+ char *buf = OJ_R_ALLOC_N(char, len + 1);
1280
+
1281
+ memcpy(buf, str, len);
1282
+ buf[len] = '\0';
1283
+
1284
+ return buf;
1285
+ }
1286
+
1287
+ // Give an options struct private copies of the option buffers it still shares
1288
+ // with the defaults so that it owns all of them.
1289
+ //
1290
+ // An options struct that outlives the call it was created in can not rely on
1291
+ // the default-owned buffers it aliases: Oj.default_options= is free to replace
1292
+ // them at any time, which would leave the alias dangling. Call this once, in
1293
+ // the call that creates such an object and after oj_parse_options(), then free
1294
+ // the buffers with oj_options_release() when the object is collected.
1295
+ void oj_options_take_ownership(Options copts) {
1296
+ if (NULL != copts->create_id && oj_default_options.create_id == copts->create_id) {
1297
+ copts->create_id = dup_option_str(copts->create_id, copts->create_id_len);
1298
+ }
1299
+ if (NULL != copts->ignore && oj_default_options.ignore == copts->ignore) {
1300
+ VALUE *vp;
1301
+ VALUE *buf;
1302
+ size_t cnt = 0;
1303
+
1304
+ for (vp = copts->ignore; Qnil != *vp; vp++) {
1305
+ cnt++;
1306
+ }
1307
+ buf = OJ_R_ALLOC_N(VALUE, cnt + 1);
1308
+ memcpy(buf, copts->ignore, sizeof(VALUE) * (cnt + 1));
1309
+ copts->ignore = buf;
1310
+ }
1311
+ if (NULL != copts->dump_opts.only && oj_default_options.dump_opts.only == copts->dump_opts.only) {
1312
+ copts->dump_opts.only = dup_option_str(copts->dump_opts.only, strlen(copts->dump_opts.only));
1313
+ }
1314
+ if (NULL != copts->dump_opts.except && oj_default_options.dump_opts.except == copts->dump_opts.except) {
1315
+ copts->dump_opts.except = dup_option_str(copts->dump_opts.except, strlen(copts->dump_opts.except));
1316
+ }
1317
+ }
1318
+
1319
+ // Free the option buffers of an options struct that owns all of them, i.e. one
1320
+ // that oj_options_take_ownership() was called on. Unlike oj_free_call_options()
1321
+ // this never looks at the defaults, so it is safe to call from a GC free
1322
+ // function, where the defaults may have been replaced since the object was
1323
+ // created.
1324
+ void oj_options_release(Options copts) {
1325
+ if (NULL != copts->create_id) {
1326
+ OJ_R_FREE((char *)copts->create_id);
1327
+ copts->create_id = NULL;
1328
+ copts->create_id_len = 0;
1329
+ }
1330
+ if (NULL != copts->ignore) {
1331
+ OJ_R_FREE(copts->ignore);
1332
+ copts->ignore = NULL;
1333
+ }
1334
+ if (NULL != copts->dump_opts.only) {
1335
+ OJ_R_FREE((void *)copts->dump_opts.only);
1336
+ copts->dump_opts.only = NULL;
1337
+ }
1338
+ if (NULL != copts->dump_opts.except) {
1339
+ OJ_R_FREE((void *)copts->dump_opts.except);
1340
+ copts->dump_opts.except = NULL;
1341
+ }
1342
+ // The str_rx chain is detached from the defaults when the owner is created
1343
+ // so the chain, if there is one, was built for this options struct alone.
1344
+ oj_rxclass_cleanup(&copts->str_rx);
1345
+ }
1346
+
1347
+ // Mark the Ruby objects held by an owned options struct. A long lived object
1348
+ // such as a writer or an encoder can be the only reference to the classes in
1349
+ // the options so without this they could be collected while the owner is still
1350
+ // alive leaving the options with dangling VALUEs.
1351
+ void oj_options_mark(Options copts) {
1352
+ if (Qnil != copts->hash_class) {
1353
+ rb_gc_mark(copts->hash_class);
1354
+ }
1355
+ if (Qnil != copts->array_class) {
1356
+ rb_gc_mark(copts->array_class);
1357
+ }
1358
+ if (NULL != copts->ignore) {
1359
+ VALUE *vp;
1360
+
1361
+ for (vp = copts->ignore; Qnil != *vp; vp++) {
1362
+ rb_gc_mark(*vp);
1363
+ }
1364
+ }
1365
+ }
1366
+
1156
1367
  static int match_string_cb(VALUE key, VALUE value, VALUE rx) {
1157
1368
  RxClass rc = (RxClass)rx;
1158
1369
 
@@ -1444,6 +1655,7 @@ static VALUE dump_ensure(VALUE a) {
1444
1655
  volatile struct dump_arg *arg = (void *)a;
1445
1656
 
1446
1657
  oj_out_free(arg->out);
1658
+ oj_free_call_options(arg->copts);
1447
1659
 
1448
1660
  return Qnil;
1449
1661
  }
@@ -1559,6 +1771,7 @@ static VALUE to_file(int argc, VALUE *argv, VALUE self) {
1559
1771
  oj_parse_options(argv[2], &copts);
1560
1772
  }
1561
1773
  oj_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
1774
+ oj_free_call_options(&copts);
1562
1775
 
1563
1776
  return Qnil;
1564
1777
  }
@@ -1580,6 +1793,7 @@ static VALUE to_stream(int argc, VALUE *argv, VALUE self) {
1580
1793
  oj_parse_options(argv[2], &copts);
1581
1794
  }
1582
1795
  oj_write_obj_to_stream(argv[1], *argv, &copts);
1796
+ oj_free_call_options(&copts);
1583
1797
 
1584
1798
  return Qnil;
1585
1799
  }
data/ext/oj/oj.h CHANGED
@@ -197,6 +197,7 @@ typedef struct _out {
197
197
  bool allocated;
198
198
  bool omit_nil;
199
199
  bool omit_null_byte;
200
+ bool key_filter_off; // rails: suppress the :only/:except key filter for an as_json result
200
201
  int argc;
201
202
  VALUE *argv;
202
203
  ROptTable ropts;
@@ -256,6 +257,10 @@ extern VALUE oj_custom_parse_cstr(int argc, VALUE *argv, char *json, size_t len)
256
257
 
257
258
  extern bool oj_hash_has_key(VALUE hash, VALUE key);
258
259
  extern void oj_parse_options(VALUE ropts, Options copts);
260
+ extern void oj_free_call_options(Options copts);
261
+ extern void oj_options_take_ownership(Options copts);
262
+ extern void oj_options_release(Options copts);
263
+ extern void oj_options_mark(Options copts);
259
264
 
260
265
  extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
261
266
  extern void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
@@ -321,7 +326,6 @@ extern VALUE oj_max_nesting_sym;
321
326
  extern VALUE oj_object_class_sym;
322
327
  extern VALUE oj_object_nl_sym;
323
328
  extern VALUE oj_quirks_mode_sym;
324
- extern VALUE oj_skip_null_byte_sym;
325
329
  extern VALUE oj_space_before_sym;
326
330
  extern VALUE oj_space_sym;
327
331
  extern VALUE oj_symbolize_names_sym;
data/ext/oj/parse.c CHANGED
@@ -414,6 +414,11 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
414
414
 
415
415
  if ('\\' == *s) {
416
416
  s++;
417
+ if (pi->end <= s) {
418
+ oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
419
+ buf_cleanup(&buf);
420
+ return;
421
+ }
417
422
  switch (*s) {
418
423
  case 'n': buf_append(&buf, '\n'); break;
419
424
  case 'r': buf_append(&buf, '\r'); break;
@@ -1096,7 +1101,9 @@ void oj_set_error_at(ParseInfo pi, VALUE err_clas, const char *file, int line, c
1096
1101
  }
1097
1102
  va_end(ap);
1098
1103
  pi->err.clas = err_clas;
1099
- if (p + 3 < end) {
1104
+ // The eight bytes below, plus the ')' and the terminator that the two bytes
1105
+ // held back from end are for.
1106
+ if (p + 8 <= end) {
1100
1107
  *p++ = ' ';
1101
1108
  *p++ = '(';
1102
1109
  *p++ = 'a';
@@ -1306,9 +1313,16 @@ CLEANUP:
1306
1313
  OJ_R_FREE(json);
1307
1314
  }
1308
1315
  stack_cleanup(&pi->stack);
1309
- if (pi->str_rx.head != oj_default_options.str_rx.head) {
1310
- oj_rxclass_cleanup(&pi->str_rx);
1316
+ // The parsers match against pi->options.str_rx. A :match_string option
1317
+ // builds a chain of its own for this call, which has to be freed. Without
1318
+ // the option the member still aliases the chain owned by the defaults and
1319
+ // must be left alone.
1320
+ if (pi->options.str_rx.head != oj_default_options.str_rx.head) {
1321
+ oj_rxclass_cleanup(&pi->options.str_rx);
1322
+ pi->options.str_rx.head = NULL;
1323
+ pi->options.str_rx.tail = NULL;
1311
1324
  }
1325
+ oj_free_call_options(&pi->options);
1312
1326
  if (err_has(&pi->err)) {
1313
1327
  rb_set_errinfo(Qnil);
1314
1328
  if (Qnil != pi->err_class) {
data/ext/oj/parse.h CHANGED
@@ -49,7 +49,6 @@ typedef struct _parseInfo {
49
49
  VALUE handler;
50
50
  struct _valStack stack;
51
51
  CircArray circ_array;
52
- struct _rxClass str_rx;
53
52
  int expect_value;
54
53
  int max_depth; // just for the json gem
55
54
  VALUE proc;