oj 3.17.3 → 3.17.6

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:
@@ -751,12 +836,14 @@ static const char *make_only_value(VALUE v) {
751
836
  return NULL;
752
837
  }
753
838
 
754
- static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
755
- Options copts = (Options)opts;
756
- size_t len;
839
+ // Apply one key and value to copts. Returns true if the key named an option Oj
840
+ // knows and false if it did not. An unrecognized key is still ignored rather
841
+ // than an error; the return only lets a caller tell the two cases apart.
842
+ static bool parse_one_option(VALUE k, VALUE v, Options copts) {
843
+ size_t len;
757
844
 
758
845
  if (set_yesno_options(k, v, copts)) {
759
- return ST_CONTINUE;
846
+ return true;
760
847
  }
761
848
  if (oj_indent_sym == k) {
762
849
  switch (rb_type(v)) {
@@ -882,7 +969,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
882
969
  }
883
970
  } else if (bigdecimal_load_sym == k) {
884
971
  if (Qnil == v) {
885
- return ST_CONTINUE;
972
+ return true;
886
973
  }
887
974
 
888
975
  if (bigdecimal_sym == v || Qtrue == v) {
@@ -898,7 +985,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
898
985
  }
899
986
  } else if (compat_bigdecimal_sym == k) {
900
987
  if (Qnil == v) {
901
- return ST_CONTINUE;
988
+ return true;
902
989
  }
903
990
  copts->compat_bigdec = (Qtrue == v);
904
991
  } else if (oj_decimal_class_sym == k) {
@@ -910,9 +997,13 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
910
997
  rb_raise(rb_eArgError, ":decimal_class must be BigDecimal or Float.");
911
998
  }
912
999
  } else if (create_id_sym == k) {
1000
+ // A per call copy of the options aliases the buffer the default
1001
+ // options own, so only the defaults may free it.
1002
+ bool owned = (&oj_default_options == copts && NULL != copts->create_id && oj_json_class != copts->create_id);
1003
+
913
1004
  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);
1005
+ if (owned) {
1006
+ OJ_R_FREE((char *)copts->create_id);
916
1007
  }
917
1008
  copts->create_id = NULL;
918
1009
  copts->create_id_len = 0;
@@ -920,7 +1011,10 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
920
1011
  const char *str = StringValuePtr(v);
921
1012
 
922
1013
  len = RSTRING_LEN(v);
923
- if (len != copts->create_id_len || 0 != strcmp(copts->create_id, str)) {
1014
+ if (NULL == copts->create_id || len != copts->create_id_len || 0 != strcmp(copts->create_id, str)) {
1015
+ if (owned) {
1016
+ OJ_R_FREE((char *)copts->create_id);
1017
+ }
924
1018
  copts->create_id = OJ_R_ALLOC_N(char, len + 1);
925
1019
  strcpy((char *)copts->create_id, str);
926
1020
  copts->create_id_len = len;
@@ -986,7 +1080,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
986
1080
  }
987
1081
  } else if (nan_sym == k) {
988
1082
  if (Qnil == v) {
989
- return ST_CONTINUE;
1083
+ return true;
990
1084
  }
991
1085
  if (null_sym == v) {
992
1086
  copts->dump_opts.nan_dump = NullNan;
@@ -1003,7 +1097,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1003
1097
  }
1004
1098
  } else if (omit_nil_sym == k) {
1005
1099
  if (Qnil == v) {
1006
- return ST_CONTINUE;
1100
+ return true;
1007
1101
  }
1008
1102
  if (Qtrue == v) {
1009
1103
  copts->dump_opts.omit_nil = true;
@@ -1014,7 +1108,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1014
1108
  }
1015
1109
  } else if (omit_null_byte_sym == k) {
1016
1110
  if (Qnil == v) {
1017
- return ST_CONTINUE;
1111
+ return true;
1018
1112
  }
1019
1113
  if (Qtrue == v) {
1020
1114
  copts->dump_opts.omit_null_byte = true;
@@ -1052,7 +1146,11 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1052
1146
  copts->array_class = v;
1053
1147
  }
1054
1148
  } else if (ignore_sym == k) {
1055
- OJ_R_FREE(copts->ignore);
1149
+ // Only free when replacing the defaults; a per-call copy's ignore still
1150
+ // aliases the default-owned buffer, which oj_free_call_options() frees.
1151
+ if (&oj_default_options == copts) {
1152
+ OJ_R_FREE(copts->ignore);
1153
+ }
1056
1154
  copts->ignore = NULL;
1057
1155
  if (Qnil != v) {
1058
1156
  size_t cnt;
@@ -1071,7 +1169,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1071
1169
  }
1072
1170
  } else if (integer_range_sym == k) {
1073
1171
  if (Qnil == v) {
1074
- return ST_CONTINUE;
1172
+ return true;
1075
1173
  }
1076
1174
  if (rb_obj_class(v) == rb_cRange) {
1077
1175
  VALUE min = rb_funcall(v, oj_begin_id, 0);
@@ -1102,7 +1200,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1102
1200
  }
1103
1201
  } else if (symbol_keys_sym == k || oj_symbolize_names_sym == k) {
1104
1202
  if (Qnil == v) {
1105
- return ST_CONTINUE;
1203
+ return true;
1106
1204
  }
1107
1205
  copts->sym_key = (Qtrue == v) ? Yes : No;
1108
1206
 
@@ -1122,35 +1220,180 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1122
1220
  if (6 < RSTRING_LEN(v)) {
1123
1221
  rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
1124
1222
  }
1223
+ validate_float_format(RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
1125
1224
  strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
1126
1225
  copts->float_fmt[RSTRING_LEN(v)] = '\0';
1127
1226
  } else if (only_sym == k) {
1128
- if (NULL != copts->dump_opts.only) {
1227
+ // Only free when replacing the defaults; a per-call copy's pointer still
1228
+ // aliases the default-owned buffer, which oj_free_call_options() frees.
1229
+ if (&oj_default_options == copts && NULL != copts->dump_opts.only) {
1129
1230
  OJ_R_FREE((void *)copts->dump_opts.only);
1130
- copts->dump_opts.only = NULL;
1131
1231
  }
1132
1232
  copts->dump_opts.only = make_only_value(v);
1133
1233
  } else if (except_sym == k) {
1134
- if (NULL != copts->dump_opts.except) {
1234
+ if (&oj_default_options == copts && NULL != copts->dump_opts.except) {
1135
1235
  OJ_R_FREE((void *)copts->dump_opts.except);
1136
- copts->dump_opts.except = NULL;
1137
1236
  }
1138
1237
  copts->dump_opts.except = make_only_value(v);
1238
+ } else {
1239
+ return false;
1240
+ }
1241
+ return true;
1242
+ }
1243
+
1244
+ // Collects the return of parse_one_option() over every key in a hash.
1245
+ struct _optsParse {
1246
+ Options copts;
1247
+ bool consumed;
1248
+ };
1249
+
1250
+ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1251
+ struct _optsParse *op = (struct _optsParse *)opts;
1252
+
1253
+ if (parse_one_option(k, v, op->copts)) {
1254
+ op->consumed = true;
1139
1255
  }
1140
1256
  return ST_CONTINUE;
1141
1257
  }
1142
1258
 
1143
- void oj_parse_options(VALUE ropts, Options copts) {
1259
+ // Apply the options in ropts to copts and report whether any key was
1260
+ // recognized. A hash of nothing but unknown keys leaves copts untouched and
1261
+ // returns false.
1262
+ bool oj_parse_options_consumed(VALUE ropts, Options copts) {
1263
+ struct _optsParse op = {copts, false};
1264
+
1144
1265
  if (T_HASH != rb_type(ropts)) {
1145
- return;
1266
+ return false;
1267
+ }
1268
+ rb_hash_foreach(ropts, parse_options_cb, (VALUE)&op);
1269
+ if (Qnil != rb_hash_lookup(ropts, match_string_sym)) {
1270
+ op.consumed = true;
1146
1271
  }
1147
- rb_hash_foreach(ropts, parse_options_cb, (VALUE)copts);
1148
1272
  oj_parse_opt_match_string(&copts->str_rx, ropts);
1149
1273
 
1150
1274
  copts->dump_opts.use = (0 < copts->dump_opts.indent_size || 0 < copts->dump_opts.after_size ||
1151
1275
  0 < copts->dump_opts.before_size || 0 < copts->dump_opts.hash_size ||
1152
1276
  0 < copts->dump_opts.array_size);
1153
- return;
1277
+
1278
+ return op.consumed;
1279
+ }
1280
+
1281
+ void oj_parse_options(VALUE ropts, Options copts) {
1282
+ oj_parse_options_consumed(ropts, copts);
1283
+ }
1284
+
1285
+ // Free the option buffers that oj_parse_options() allocated for a single call.
1286
+ // A per-call options struct is a copy of oj_default_options, so its create_id,
1287
+ // ignore, only, and except members initially alias the ones owned by the
1288
+ // defaults. Only free a member when it differs from the default, i.e. when it
1289
+ // was allocated for this call; the default-owned buffers must be left alone.
1290
+ void oj_free_call_options(Options copts) {
1291
+ if (NULL != copts->create_id && oj_default_options.create_id != copts->create_id) {
1292
+ OJ_R_FREE((char *)copts->create_id);
1293
+ copts->create_id = NULL;
1294
+ copts->create_id_len = 0;
1295
+ }
1296
+ if (NULL != copts->ignore && oj_default_options.ignore != copts->ignore) {
1297
+ OJ_R_FREE(copts->ignore);
1298
+ copts->ignore = NULL;
1299
+ }
1300
+ if (NULL != copts->dump_opts.only && oj_default_options.dump_opts.only != copts->dump_opts.only) {
1301
+ OJ_R_FREE((void *)copts->dump_opts.only);
1302
+ copts->dump_opts.only = NULL;
1303
+ }
1304
+ if (NULL != copts->dump_opts.except && oj_default_options.dump_opts.except != copts->dump_opts.except) {
1305
+ OJ_R_FREE((void *)copts->dump_opts.except);
1306
+ copts->dump_opts.except = NULL;
1307
+ }
1308
+ }
1309
+
1310
+ static char *dup_option_str(const char *str, size_t len) {
1311
+ char *buf = OJ_R_ALLOC_N(char, len + 1);
1312
+
1313
+ memcpy(buf, str, len);
1314
+ buf[len] = '\0';
1315
+
1316
+ return buf;
1317
+ }
1318
+
1319
+ // Give an options struct private copies of the option buffers it still shares
1320
+ // with the defaults so that it owns all of them.
1321
+ //
1322
+ // An options struct that outlives the call it was created in can not rely on
1323
+ // the default-owned buffers it aliases: Oj.default_options= is free to replace
1324
+ // them at any time, which would leave the alias dangling. Call this once, in
1325
+ // the call that creates such an object and after oj_parse_options(), then free
1326
+ // the buffers with oj_options_release() when the object is collected.
1327
+ void oj_options_take_ownership(Options copts) {
1328
+ if (NULL != copts->create_id && oj_default_options.create_id == copts->create_id) {
1329
+ copts->create_id = dup_option_str(copts->create_id, copts->create_id_len);
1330
+ }
1331
+ if (NULL != copts->ignore && oj_default_options.ignore == copts->ignore) {
1332
+ VALUE *vp;
1333
+ VALUE *buf;
1334
+ size_t cnt = 0;
1335
+
1336
+ for (vp = copts->ignore; Qnil != *vp; vp++) {
1337
+ cnt++;
1338
+ }
1339
+ buf = OJ_R_ALLOC_N(VALUE, cnt + 1);
1340
+ memcpy(buf, copts->ignore, sizeof(VALUE) * (cnt + 1));
1341
+ copts->ignore = buf;
1342
+ }
1343
+ if (NULL != copts->dump_opts.only && oj_default_options.dump_opts.only == copts->dump_opts.only) {
1344
+ copts->dump_opts.only = dup_option_str(copts->dump_opts.only, strlen(copts->dump_opts.only));
1345
+ }
1346
+ if (NULL != copts->dump_opts.except && oj_default_options.dump_opts.except == copts->dump_opts.except) {
1347
+ copts->dump_opts.except = dup_option_str(copts->dump_opts.except, strlen(copts->dump_opts.except));
1348
+ }
1349
+ }
1350
+
1351
+ // Free the option buffers of an options struct that owns all of them, i.e. one
1352
+ // that oj_options_take_ownership() was called on. Unlike oj_free_call_options()
1353
+ // this never looks at the defaults, so it is safe to call from a GC free
1354
+ // function, where the defaults may have been replaced since the object was
1355
+ // created.
1356
+ void oj_options_release(Options copts) {
1357
+ if (NULL != copts->create_id) {
1358
+ OJ_R_FREE((char *)copts->create_id);
1359
+ copts->create_id = NULL;
1360
+ copts->create_id_len = 0;
1361
+ }
1362
+ if (NULL != copts->ignore) {
1363
+ OJ_R_FREE(copts->ignore);
1364
+ copts->ignore = NULL;
1365
+ }
1366
+ if (NULL != copts->dump_opts.only) {
1367
+ OJ_R_FREE((void *)copts->dump_opts.only);
1368
+ copts->dump_opts.only = NULL;
1369
+ }
1370
+ if (NULL != copts->dump_opts.except) {
1371
+ OJ_R_FREE((void *)copts->dump_opts.except);
1372
+ copts->dump_opts.except = NULL;
1373
+ }
1374
+ // The str_rx chain is detached from the defaults when the owner is created
1375
+ // so the chain, if there is one, was built for this options struct alone.
1376
+ oj_rxclass_cleanup(&copts->str_rx);
1377
+ }
1378
+
1379
+ // Mark the Ruby objects held by an owned options struct. A long lived object
1380
+ // such as a writer or an encoder can be the only reference to the classes in
1381
+ // the options so without this they could be collected while the owner is still
1382
+ // alive leaving the options with dangling VALUEs.
1383
+ void oj_options_mark(Options copts) {
1384
+ if (Qnil != copts->hash_class) {
1385
+ rb_gc_mark(copts->hash_class);
1386
+ }
1387
+ if (Qnil != copts->array_class) {
1388
+ rb_gc_mark(copts->array_class);
1389
+ }
1390
+ if (NULL != copts->ignore) {
1391
+ VALUE *vp;
1392
+
1393
+ for (vp = copts->ignore; Qnil != *vp; vp++) {
1394
+ rb_gc_mark(*vp);
1395
+ }
1396
+ }
1154
1397
  }
1155
1398
 
1156
1399
  static int match_string_cb(VALUE key, VALUE value, VALUE rx) {
@@ -1444,6 +1687,7 @@ static VALUE dump_ensure(VALUE a) {
1444
1687
  volatile struct dump_arg *arg = (void *)a;
1445
1688
 
1446
1689
  oj_out_free(arg->out);
1690
+ oj_free_call_options(arg->copts);
1447
1691
 
1448
1692
  return Qnil;
1449
1693
  }
@@ -1559,6 +1803,7 @@ static VALUE to_file(int argc, VALUE *argv, VALUE self) {
1559
1803
  oj_parse_options(argv[2], &copts);
1560
1804
  }
1561
1805
  oj_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
1806
+ oj_free_call_options(&copts);
1562
1807
 
1563
1808
  return Qnil;
1564
1809
  }
@@ -1580,6 +1825,7 @@ static VALUE to_stream(int argc, VALUE *argv, VALUE self) {
1580
1825
  oj_parse_options(argv[2], &copts);
1581
1826
  }
1582
1827
  oj_write_obj_to_stream(argv[1], *argv, &copts);
1828
+ oj_free_call_options(&copts);
1583
1829
 
1584
1830
  return Qnil;
1585
1831
  }
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,11 @@ 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 bool oj_parse_options_consumed(VALUE ropts, Options copts);
261
+ extern void oj_free_call_options(Options copts);
262
+ extern void oj_options_take_ownership(Options copts);
263
+ extern void oj_options_release(Options copts);
264
+ extern void oj_options_mark(Options copts);
259
265
 
260
266
  extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
261
267
  extern void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
@@ -321,7 +327,6 @@ extern VALUE oj_max_nesting_sym;
321
327
  extern VALUE oj_object_class_sym;
322
328
  extern VALUE oj_object_nl_sym;
323
329
  extern VALUE oj_quirks_mode_sym;
324
- extern VALUE oj_skip_null_byte_sym;
325
330
  extern VALUE oj_space_before_sym;
326
331
  extern VALUE oj_space_sym;
327
332
  extern VALUE oj_symbolize_names_sym;