oj 3.17.4 → 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
@@ -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.
@@ -705,6 +707,46 @@ bool set_yesno_options(VALUE key, VALUE value, Options copts) {
705
707
  return false;
706
708
  }
707
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
+
708
750
  static const char *make_only_value(VALUE v) {
709
751
  switch (rb_type(v)) {
710
752
  case RUBY_T_NIL:
@@ -794,12 +836,14 @@ static const char *make_only_value(VALUE v) {
794
836
  return NULL;
795
837
  }
796
838
 
797
- static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
798
- Options copts = (Options)opts;
799
- 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;
800
844
 
801
845
  if (set_yesno_options(k, v, copts)) {
802
- return ST_CONTINUE;
846
+ return true;
803
847
  }
804
848
  if (oj_indent_sym == k) {
805
849
  switch (rb_type(v)) {
@@ -925,7 +969,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
925
969
  }
926
970
  } else if (bigdecimal_load_sym == k) {
927
971
  if (Qnil == v) {
928
- return ST_CONTINUE;
972
+ return true;
929
973
  }
930
974
 
931
975
  if (bigdecimal_sym == v || Qtrue == v) {
@@ -941,7 +985,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
941
985
  }
942
986
  } else if (compat_bigdecimal_sym == k) {
943
987
  if (Qnil == v) {
944
- return ST_CONTINUE;
988
+ return true;
945
989
  }
946
990
  copts->compat_bigdec = (Qtrue == v);
947
991
  } else if (oj_decimal_class_sym == k) {
@@ -953,9 +997,13 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
953
997
  rb_raise(rb_eArgError, ":decimal_class must be BigDecimal or Float.");
954
998
  }
955
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
+
956
1004
  if (Qnil == v) {
957
- if (oj_json_class != oj_default_options.create_id && NULL != copts->create_id) {
958
- OJ_R_FREE((char *)oj_default_options.create_id);
1005
+ if (owned) {
1006
+ OJ_R_FREE((char *)copts->create_id);
959
1007
  }
960
1008
  copts->create_id = NULL;
961
1009
  copts->create_id_len = 0;
@@ -963,8 +1011,8 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
963
1011
  const char *str = StringValuePtr(v);
964
1012
 
965
1013
  len = RSTRING_LEN(v);
966
- if (len != copts->create_id_len || 0 != strcmp(copts->create_id, str)) {
967
- if (&oj_default_options == copts && oj_json_class != copts->create_id) {
1014
+ if (NULL == copts->create_id || len != copts->create_id_len || 0 != strcmp(copts->create_id, str)) {
1015
+ if (owned) {
968
1016
  OJ_R_FREE((char *)copts->create_id);
969
1017
  }
970
1018
  copts->create_id = OJ_R_ALLOC_N(char, len + 1);
@@ -1032,7 +1080,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1032
1080
  }
1033
1081
  } else if (nan_sym == k) {
1034
1082
  if (Qnil == v) {
1035
- return ST_CONTINUE;
1083
+ return true;
1036
1084
  }
1037
1085
  if (null_sym == v) {
1038
1086
  copts->dump_opts.nan_dump = NullNan;
@@ -1049,7 +1097,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1049
1097
  }
1050
1098
  } else if (omit_nil_sym == k) {
1051
1099
  if (Qnil == v) {
1052
- return ST_CONTINUE;
1100
+ return true;
1053
1101
  }
1054
1102
  if (Qtrue == v) {
1055
1103
  copts->dump_opts.omit_nil = true;
@@ -1060,7 +1108,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1060
1108
  }
1061
1109
  } else if (omit_null_byte_sym == k) {
1062
1110
  if (Qnil == v) {
1063
- return ST_CONTINUE;
1111
+ return true;
1064
1112
  }
1065
1113
  if (Qtrue == v) {
1066
1114
  copts->dump_opts.omit_null_byte = true;
@@ -1121,7 +1169,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1121
1169
  }
1122
1170
  } else if (integer_range_sym == k) {
1123
1171
  if (Qnil == v) {
1124
- return ST_CONTINUE;
1172
+ return true;
1125
1173
  }
1126
1174
  if (rb_obj_class(v) == rb_cRange) {
1127
1175
  VALUE min = rb_funcall(v, oj_begin_id, 0);
@@ -1152,7 +1200,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1152
1200
  }
1153
1201
  } else if (symbol_keys_sym == k || oj_symbolize_names_sym == k) {
1154
1202
  if (Qnil == v) {
1155
- return ST_CONTINUE;
1203
+ return true;
1156
1204
  }
1157
1205
  copts->sym_key = (Qtrue == v) ? Yes : No;
1158
1206
 
@@ -1172,6 +1220,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1172
1220
  if (6 < RSTRING_LEN(v)) {
1173
1221
  rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
1174
1222
  }
1223
+ validate_float_format(RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
1175
1224
  strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
1176
1225
  copts->float_fmt[RSTRING_LEN(v)] = '\0';
1177
1226
  } else if (only_sym == k) {
@@ -1186,21 +1235,51 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1186
1235
  OJ_R_FREE((void *)copts->dump_opts.except);
1187
1236
  }
1188
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;
1189
1255
  }
1190
1256
  return ST_CONTINUE;
1191
1257
  }
1192
1258
 
1193
- 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
+
1194
1265
  if (T_HASH != rb_type(ropts)) {
1195
- 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;
1196
1271
  }
1197
- rb_hash_foreach(ropts, parse_options_cb, (VALUE)copts);
1198
1272
  oj_parse_opt_match_string(&copts->str_rx, ropts);
1199
1273
 
1200
1274
  copts->dump_opts.use = (0 < copts->dump_opts.indent_size || 0 < copts->dump_opts.after_size ||
1201
1275
  0 < copts->dump_opts.before_size || 0 < copts->dump_opts.hash_size ||
1202
1276
  0 < copts->dump_opts.array_size);
1203
- return;
1277
+
1278
+ return op.consumed;
1279
+ }
1280
+
1281
+ void oj_parse_options(VALUE ropts, Options copts) {
1282
+ oj_parse_options_consumed(ropts, copts);
1204
1283
  }
1205
1284
 
1206
1285
  // Free the option buffers that oj_parse_options() allocated for a single call.
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,7 @@ 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);
259
261
  extern void oj_free_call_options(Options copts);
260
262
  extern void oj_options_take_ownership(Options copts);
261
263
  extern void oj_options_release(Options copts);
@@ -325,7 +327,6 @@ extern VALUE oj_max_nesting_sym;
325
327
  extern VALUE oj_object_class_sym;
326
328
  extern VALUE oj_object_nl_sym;
327
329
  extern VALUE oj_quirks_mode_sym;
328
- extern VALUE oj_skip_null_byte_sym;
329
330
  extern VALUE oj_space_before_sym;
330
331
  extern VALUE oj_space_sym;
331
332
  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';
data/ext/oj/parser.c CHANGED
@@ -892,10 +892,10 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
892
892
  case EXP_DIGIT:
893
893
  p->map = exp_map;
894
894
  for (; NUM_DIGIT == digit_map[*b]; b++) {
895
- int16_t x = p->num.exp * 10 + (int16_t)(*b - '0');
895
+ int x = p->num.exp * 10 + (*b - '0');
896
896
 
897
897
  if (x <= MAX_EXP) {
898
- p->num.exp = x;
898
+ p->num.exp = (int16_t)x;
899
899
  } else {
900
900
  big_change(p);
901
901
  p->map = big_exp_map;
@@ -1282,7 +1282,8 @@ static int opt_cb(VALUE rkey, VALUE value, VALUE ptr) {
1282
1282
  * Oj::Parser.new(:usual, cache_keys: true).
1283
1283
  */
1284
1284
  static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
1285
- ojParser p = OJ_R_ALLOC(struct _ojParser);
1285
+ ojParser p = OJ_R_ALLOC(struct _ojParser);
1286
+ volatile VALUE pv;
1286
1287
 
1287
1288
  #if HAVE_RB_EXT_RACTOR_SAFE
1288
1289
  // This doesn't seem to do anything.
@@ -1292,6 +1293,10 @@ static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
1292
1293
  buf_init(&p->key);
1293
1294
  buf_init(&p->buf);
1294
1295
  p->map = value_map;
1296
+ // Until the parser is wrapped the GC can not free it, so the mode and the
1297
+ // options, both of which raise on a value they do not take, are handled
1298
+ // after the wrap.
1299
+ pv = TypedData_Wrap_Struct(parser_class, &oj_parser_type, p);
1295
1300
 
1296
1301
  if (argc < 1) {
1297
1302
  oj_set_parser_validator(p);
@@ -1332,7 +1337,7 @@ static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
1332
1337
  rb_hash_foreach(ropts, opt_cb, (VALUE)p);
1333
1338
  }
1334
1339
  }
1335
- return TypedData_Wrap_Struct(parser_class, &oj_parser_type, p);
1340
+ return pv;
1336
1341
  }
1337
1342
 
1338
1343
  // Create a new parser without setting the delegate. The parser is
@@ -1524,6 +1529,39 @@ static VALUE parser_load(VALUE self, VALUE reader) {
1524
1529
  return p->result(p);
1525
1530
  }
1526
1531
 
1532
+ struct _fileParse {
1533
+ ojParser p;
1534
+ const char *path;
1535
+ int fd;
1536
+ };
1537
+
1538
+ static VALUE file_parse(VALUE x) {
1539
+ struct _fileParse *fp = (struct _fileParse *)x;
1540
+ byte buf[16385];
1541
+ size_t size = sizeof(buf) - 1;
1542
+ ssize_t rsize;
1543
+
1544
+ while (true) {
1545
+ if (0 > (rsize = read(fp->fd, buf, size))) {
1546
+ rb_raise(rb_eIOError, "error reading from %s", fp->path);
1547
+ }
1548
+ if (0 == rsize) {
1549
+ break;
1550
+ }
1551
+ buf[rsize] = '\0';
1552
+ parse(fp->p, buf, rsize, true);
1553
+ }
1554
+ validate_document_end(fp->p);
1555
+
1556
+ return fp->p->result(fp->p);
1557
+ }
1558
+
1559
+ static VALUE file_close(VALUE x) {
1560
+ close(((struct _fileParse *)x)->fd);
1561
+
1562
+ return Qnil;
1563
+ }
1564
+
1527
1565
  /* Document-method: file(filename)
1528
1566
  * call-seq: file(filename)
1529
1567
  *
@@ -1532,9 +1570,10 @@ static VALUE parser_load(VALUE self, VALUE reader) {
1532
1570
  * Returns the result according to the delegate of the parser.
1533
1571
  */
1534
1572
  static VALUE parser_file(VALUE self, VALUE filename) {
1535
- ojParser p;
1536
- const char *path;
1537
- int fd;
1573
+ struct _fileParse fp;
1574
+ ojParser p;
1575
+ const char *path;
1576
+ int fd;
1538
1577
 
1539
1578
  TypedData_Get_Struct(self, struct _ojParser, &oj_parser_type, p);
1540
1579
 
@@ -1553,26 +1592,15 @@ static VALUE parser_file(VALUE self, VALUE filename) {
1553
1592
  // Use threaded version.
1554
1593
  // TBD only if has pthreads
1555
1594
  // TBD parse_large(p, fd);
1595
+ close(fd);
1556
1596
  return p->result(p);
1557
1597
  }
1558
1598
  #endif
1559
- byte buf[16385];
1560
- size_t size = sizeof(buf) - 1;
1561
- size_t rsize;
1599
+ fp.p = p;
1600
+ fp.path = path;
1601
+ fp.fd = fd;
1562
1602
 
1563
- while (true) {
1564
- if (0 < (rsize = read(fd, buf, size))) {
1565
- buf[rsize] = '\0';
1566
- parse(p, buf, rsize, true);
1567
- }
1568
- if (rsize <= 0) {
1569
- if (0 != rsize) {
1570
- rb_raise(rb_eIOError, "error reading from %s", path);
1571
- }
1572
- break;
1573
- }
1574
- }
1575
- return p->result(p);
1603
+ return rb_ensure(file_parse, (VALUE)&fp, file_close, (VALUE)&fp);
1576
1604
  }
1577
1605
 
1578
1606
  /* Document-method: just_one
@@ -1685,15 +1713,19 @@ static VALUE parser_safe(int argc, VALUE *argv, VALUE self) {
1685
1713
  options = rb_hash_new();
1686
1714
  }
1687
1715
 
1688
- ojParser p = OJ_R_ALLOC(struct _ojParser);
1716
+ ojParser p = OJ_R_ALLOC(struct _ojParser);
1717
+ volatile VALUE pv;
1689
1718
 
1690
1719
  memset(p, 0, sizeof(struct _ojParser));
1691
1720
  buf_init(&p->key);
1692
1721
  buf_init(&p->buf);
1693
1722
  p->map = value_map;
1723
+ // The limits raise on a value that is not an Integer, so the parser has to
1724
+ // be wrapped before they are read.
1725
+ pv = TypedData_Wrap_Struct(parser_class, &oj_parser_type, p);
1694
1726
  oj_set_parser_safe(p, options);
1695
1727
 
1696
- return TypedData_Wrap_Struct(parser_class, &oj_parser_type, p);
1728
+ return pv;
1697
1729
  }
1698
1730
 
1699
1731
  /* Document-class: Oj::Parser