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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +69 -0
- data/ext/oj/cache.c +17 -1
- data/ext/oj/cache.h +5 -0
- data/ext/oj/circarray.c +6 -2
- data/ext/oj/compat.c +2 -2
- data/ext/oj/custom.c +5 -3
- data/ext/oj/dump.c +27 -12
- data/ext/oj/dump.h +10 -0
- data/ext/oj/dump_compat.c +10 -4
- data/ext/oj/extconf.rb +1 -0
- data/ext/oj/fast.c +40 -5
- data/ext/oj/intern.c +6 -1
- data/ext/oj/object.c +15 -10
- data/ext/oj/odd.c +6 -2
- data/ext/oj/oj.c +53 -6
- data/ext/oj/oj.h +1 -1
- data/ext/oj/parse.c +8 -1
- data/ext/oj/parser.c +57 -25
- data/ext/oj/rails.c +162 -52
- data/ext/oj/reader.c +2 -2
- data/ext/oj/resolve.c +11 -2
- data/ext/oj/safe.c +62 -48
- data/ext/oj/safe.h +14 -2
- data/ext/oj/saj.c +6 -0
- data/ext/oj/sparse.c +7 -6
- data/ext/oj/string_writer.c +2 -0
- data/ext/oj/usual.c +5 -1
- data/ext/oj/util.c +27 -0
- data/ext/oj/util.h +2 -1
- data/ext/oj/val_stack.h +24 -5
- data/ext/oj/wab.c +14 -26
- data/lib/oj/version.rb +1 -1
- data/pages/Compatibility.md +1 -1
- data/pages/Custom.md +2 -3
- data/pages/InstallOptions.md +8 -6
- data/pages/Modes.md +1 -1
- data/pages/Options.md +5 -6
- data/pages/Parser.md +2 -2
- data/pages/Rails.md +2 -0
- data/pages/Security.md +21 -7
- metadata +2 -2
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 (
|
|
958
|
-
OJ_R_FREE((char *)
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
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
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1599
|
+
fp.p = p;
|
|
1600
|
+
fp.path = path;
|
|
1601
|
+
fp.fd = fd;
|
|
1562
1602
|
|
|
1563
|
-
|
|
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
|
|
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
|
|
1728
|
+
return pv;
|
|
1697
1729
|
}
|
|
1698
1730
|
|
|
1699
1731
|
/* Document-class: Oj::Parser
|