oj 3.17.1 → 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/oj.c CHANGED
@@ -20,6 +20,8 @@
20
20
  #include "rails.h"
21
21
  #include "simd.h"
22
22
 
23
+ #define MAX_INDENT 16
24
+
23
25
  typedef struct _yesNoOpt {
24
26
  VALUE sym;
25
27
  char *attr;
@@ -297,7 +299,8 @@ static VALUE only_array_from_string(const char *str) {
297
299
  * floats, 0 indicates use Ruby
298
300
  * - *:float_format* [_String_] the C printf format string for printing floats.
299
301
  * Default follows the float_precision and will be changed if float_precision is
300
- * 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.
301
304
  * - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false
302
305
  * - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false
303
306
  * - *:use_raw_json* [_Boolean_|_nil_] call raw_json() methods on dump, default is false
@@ -572,7 +575,8 @@ static VALUE get_def_opts(VALUE self) {
572
575
  * when dumping the seconds portion of time.
573
576
  * - *:float_format* [_String_] the C printf format string for printing floats.
574
577
  * Default follows the float_precision and will be changed if float_precision
575
- * 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.
576
580
  * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0 indicates use Ruby.
577
581
  * - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false.
578
582
  * - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false.
@@ -604,9 +608,52 @@ static VALUE get_def_opts(VALUE self) {
604
608
  * - *:trace* [_Boolean_] turn trace on or off.
605
609
  * - *:safe* [_Boolean_] turn safe mimic on or off.
606
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
+
607
645
  static VALUE set_def_opts(VALUE self, VALUE opts) {
608
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
+ }
609
655
  oj_parse_options(opts, &oj_default_options);
656
+ keep_default_options();
610
657
 
611
658
  return Qnil;
612
659
  }
@@ -660,6 +707,46 @@ bool set_yesno_options(VALUE key, VALUE value, Options copts) {
660
707
  return false;
661
708
  }
662
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
+
663
750
  static const char *make_only_value(VALUE v) {
664
751
  switch (rb_type(v)) {
665
752
  case RUBY_T_NIL:
@@ -749,12 +836,14 @@ static const char *make_only_value(VALUE v) {
749
836
  return NULL;
750
837
  }
751
838
 
752
- static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
753
- Options copts = (Options)opts;
754
- 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;
755
844
 
756
845
  if (set_yesno_options(k, v, copts)) {
757
- return ST_CONTINUE;
846
+ return true;
758
847
  }
759
848
  if (oj_indent_sym == k) {
760
849
  switch (rb_type(v)) {
@@ -766,7 +855,10 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
766
855
  case T_FIXNUM:
767
856
  copts->dump_opts.indent_size = 0;
768
857
  *copts->dump_opts.indent_str = '\0';
769
- copts->indent = FIX2INT(v);
858
+ if (MAX_INDENT < FIX2INT(v)) {
859
+ rb_raise(rb_eArgError, "indent is limited to %d characters.", MAX_INDENT);
860
+ }
861
+ copts->indent = FIX2INT(v);
770
862
  break;
771
863
  case T_STRING:
772
864
  if (sizeof(copts->dump_opts.indent_str) <= (len = RSTRING_LEN(v))) {
@@ -877,7 +969,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
877
969
  }
878
970
  } else if (bigdecimal_load_sym == k) {
879
971
  if (Qnil == v) {
880
- return ST_CONTINUE;
972
+ return true;
881
973
  }
882
974
 
883
975
  if (bigdecimal_sym == v || Qtrue == v) {
@@ -893,7 +985,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
893
985
  }
894
986
  } else if (compat_bigdecimal_sym == k) {
895
987
  if (Qnil == v) {
896
- return ST_CONTINUE;
988
+ return true;
897
989
  }
898
990
  copts->compat_bigdec = (Qtrue == v);
899
991
  } else if (oj_decimal_class_sym == k) {
@@ -905,9 +997,13 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
905
997
  rb_raise(rb_eArgError, ":decimal_class must be BigDecimal or Float.");
906
998
  }
907
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
+
908
1004
  if (Qnil == v) {
909
- if (oj_json_class != oj_default_options.create_id && NULL != copts->create_id) {
910
- OJ_R_FREE((char *)oj_default_options.create_id);
1005
+ if (owned) {
1006
+ OJ_R_FREE((char *)copts->create_id);
911
1007
  }
912
1008
  copts->create_id = NULL;
913
1009
  copts->create_id_len = 0;
@@ -915,7 +1011,10 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
915
1011
  const char *str = StringValuePtr(v);
916
1012
 
917
1013
  len = RSTRING_LEN(v);
918
- 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
+ }
919
1018
  copts->create_id = OJ_R_ALLOC_N(char, len + 1);
920
1019
  strcpy((char *)copts->create_id, str);
921
1020
  copts->create_id_len = len;
@@ -981,7 +1080,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
981
1080
  }
982
1081
  } else if (nan_sym == k) {
983
1082
  if (Qnil == v) {
984
- return ST_CONTINUE;
1083
+ return true;
985
1084
  }
986
1085
  if (null_sym == v) {
987
1086
  copts->dump_opts.nan_dump = NullNan;
@@ -998,7 +1097,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
998
1097
  }
999
1098
  } else if (omit_nil_sym == k) {
1000
1099
  if (Qnil == v) {
1001
- return ST_CONTINUE;
1100
+ return true;
1002
1101
  }
1003
1102
  if (Qtrue == v) {
1004
1103
  copts->dump_opts.omit_nil = true;
@@ -1009,7 +1108,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1009
1108
  }
1010
1109
  } else if (omit_null_byte_sym == k) {
1011
1110
  if (Qnil == v) {
1012
- return ST_CONTINUE;
1111
+ return true;
1013
1112
  }
1014
1113
  if (Qtrue == v) {
1015
1114
  copts->dump_opts.omit_null_byte = true;
@@ -1047,7 +1146,11 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1047
1146
  copts->array_class = v;
1048
1147
  }
1049
1148
  } else if (ignore_sym == k) {
1050
- 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
+ }
1051
1154
  copts->ignore = NULL;
1052
1155
  if (Qnil != v) {
1053
1156
  size_t cnt;
@@ -1066,7 +1169,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1066
1169
  }
1067
1170
  } else if (integer_range_sym == k) {
1068
1171
  if (Qnil == v) {
1069
- return ST_CONTINUE;
1172
+ return true;
1070
1173
  }
1071
1174
  if (rb_obj_class(v) == rb_cRange) {
1072
1175
  VALUE min = rb_funcall(v, oj_begin_id, 0);
@@ -1097,7 +1200,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1097
1200
  }
1098
1201
  } else if (symbol_keys_sym == k || oj_symbolize_names_sym == k) {
1099
1202
  if (Qnil == v) {
1100
- return ST_CONTINUE;
1203
+ return true;
1101
1204
  }
1102
1205
  copts->sym_key = (Qtrue == v) ? Yes : No;
1103
1206
 
@@ -1117,35 +1220,180 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1117
1220
  if (6 < RSTRING_LEN(v)) {
1118
1221
  rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
1119
1222
  }
1223
+ validate_float_format(RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
1120
1224
  strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
1121
1225
  copts->float_fmt[RSTRING_LEN(v)] = '\0';
1122
1226
  } else if (only_sym == k) {
1123
- 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) {
1124
1230
  OJ_R_FREE((void *)copts->dump_opts.only);
1125
- copts->dump_opts.only = NULL;
1126
1231
  }
1127
1232
  copts->dump_opts.only = make_only_value(v);
1128
1233
  } else if (except_sym == k) {
1129
- if (NULL != copts->dump_opts.except) {
1234
+ if (&oj_default_options == copts && NULL != copts->dump_opts.except) {
1130
1235
  OJ_R_FREE((void *)copts->dump_opts.except);
1131
- copts->dump_opts.except = NULL;
1132
1236
  }
1133
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;
1134
1255
  }
1135
1256
  return ST_CONTINUE;
1136
1257
  }
1137
1258
 
1138
- 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
+
1139
1265
  if (T_HASH != rb_type(ropts)) {
1140
- 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;
1141
1271
  }
1142
- rb_hash_foreach(ropts, parse_options_cb, (VALUE)copts);
1143
1272
  oj_parse_opt_match_string(&copts->str_rx, ropts);
1144
1273
 
1145
1274
  copts->dump_opts.use = (0 < copts->dump_opts.indent_size || 0 < copts->dump_opts.after_size ||
1146
1275
  0 < copts->dump_opts.before_size || 0 < copts->dump_opts.hash_size ||
1147
1276
  0 < copts->dump_opts.array_size);
1148
- 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
+ }
1149
1397
  }
1150
1398
 
1151
1399
  static int match_string_cb(VALUE key, VALUE value, VALUE rx) {
@@ -1439,6 +1687,7 @@ static VALUE dump_ensure(VALUE a) {
1439
1687
  volatile struct dump_arg *arg = (void *)a;
1440
1688
 
1441
1689
  oj_out_free(arg->out);
1690
+ oj_free_call_options(arg->copts);
1442
1691
 
1443
1692
  return Qnil;
1444
1693
  }
@@ -1554,6 +1803,7 @@ static VALUE to_file(int argc, VALUE *argv, VALUE self) {
1554
1803
  oj_parse_options(argv[2], &copts);
1555
1804
  }
1556
1805
  oj_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
1806
+ oj_free_call_options(&copts);
1557
1807
 
1558
1808
  return Qnil;
1559
1809
  }
@@ -1575,6 +1825,7 @@ static VALUE to_stream(int argc, VALUE *argv, VALUE self) {
1575
1825
  oj_parse_options(argv[2], &copts);
1576
1826
  }
1577
1827
  oj_write_obj_to_stream(argv[1], *argv, &copts);
1828
+ oj_free_call_options(&copts);
1578
1829
 
1579
1830
  return Qnil;
1580
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;
data/ext/oj/parse.c CHANGED
@@ -394,7 +394,7 @@ void oj_scanner_init(void) {
394
394
  static void read_escaped_str(ParseInfo pi, const char *start) {
395
395
  struct _buf buf;
396
396
  const char *s;
397
- int cnt = (int)(pi->cur - start);
397
+ size_t cnt = pi->cur - start;
398
398
  uint32_t code;
399
399
  Val parent = stack_peek(&pi->stack);
400
400
 
@@ -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;
@@ -669,7 +674,7 @@ static void read_num(ParseInfo pi) {
669
674
  // A trailing . is not a valid decimal but if encountered allow it
670
675
  // except when mimicking the JSON gem or in strict mode.
671
676
  if (StrictMode == pi->options.mode || CompatMode == pi->options.mode) {
672
- int pos = (int)(pi->cur - ni.str);
677
+ size_t pos = pi->cur - ni.str;
673
678
 
674
679
  if (1 == pos || (2 == pos && ni.neg)) {
675
680
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
@@ -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;