oj 3.17.4 → 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/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:
@@ -953,9 +995,13 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
953
995
  rb_raise(rb_eArgError, ":decimal_class must be BigDecimal or Float.");
954
996
  }
955
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
+
956
1002
  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);
1003
+ if (owned) {
1004
+ OJ_R_FREE((char *)copts->create_id);
959
1005
  }
960
1006
  copts->create_id = NULL;
961
1007
  copts->create_id_len = 0;
@@ -963,8 +1009,8 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
963
1009
  const char *str = StringValuePtr(v);
964
1010
 
965
1011
  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) {
1012
+ if (NULL == copts->create_id || len != copts->create_id_len || 0 != strcmp(copts->create_id, str)) {
1013
+ if (owned) {
968
1014
  OJ_R_FREE((char *)copts->create_id);
969
1015
  }
970
1016
  copts->create_id = OJ_R_ALLOC_N(char, len + 1);
@@ -1172,6 +1218,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1172
1218
  if (6 < RSTRING_LEN(v)) {
1173
1219
  rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
1174
1220
  }
1221
+ validate_float_format(RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
1175
1222
  strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
1176
1223
  copts->float_fmt[RSTRING_LEN(v)] = '\0';
1177
1224
  } else if (only_sym == k) {
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;
@@ -325,7 +326,6 @@ extern VALUE oj_max_nesting_sym;
325
326
  extern VALUE oj_object_class_sym;
326
327
  extern VALUE oj_object_nl_sym;
327
328
  extern VALUE oj_quirks_mode_sym;
328
- extern VALUE oj_skip_null_byte_sym;
329
329
  extern VALUE oj_space_before_sym;
330
330
  extern VALUE oj_space_sym;
331
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';
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