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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +79 -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 +11 -4
- data/ext/oj/dump_object.c +20 -10
- data/ext/oj/extconf.rb +1 -0
- data/ext/oj/fast.c +87 -44
- data/ext/oj/intern.c +6 -1
- data/ext/oj/object.c +26 -15
- data/ext/oj/odd.c +6 -2
- data/ext/oj/oj.c +271 -25
- data/ext/oj/oj.h +6 -1
- data/ext/oj/parse.c +17 -3
- data/ext/oj/parse.h +0 -1
- data/ext/oj/parser.c +120 -35
- data/ext/oj/rails.c +180 -42
- data/ext/oj/reader.c +18 -6
- data/ext/oj/resolve.c +11 -2
- data/ext/oj/rxclass.c +16 -0
- data/ext/oj/rxclass.h +1 -0
- data/ext/oj/safe.c +62 -48
- data/ext/oj/safe.h +14 -2
- data/ext/oj/saj.c +46 -10
- data/ext/oj/sparse.c +16 -6
- data/ext/oj/stream_writer.c +16 -1
- data/ext/oj/string_writer.c +23 -7
- 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 +6 -0
- data/pages/Security.md +21 -7
- metadata +2 -2
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';
|
|
@@ -1306,9 +1313,16 @@ CLEANUP:
|
|
|
1306
1313
|
OJ_R_FREE(json);
|
|
1307
1314
|
}
|
|
1308
1315
|
stack_cleanup(&pi->stack);
|
|
1309
|
-
|
|
1310
|
-
|
|
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
data/ext/oj/parser.c
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#include <fcntl.h>
|
|
6
6
|
|
|
7
7
|
#include "oj.h"
|
|
8
|
+
#include "simd.h"
|
|
8
9
|
|
|
9
10
|
#define DEBUG 0
|
|
10
11
|
|
|
@@ -594,9 +595,64 @@ static void big_change(ojParser p) {
|
|
|
594
595
|
}
|
|
595
596
|
}
|
|
596
597
|
|
|
597
|
-
|
|
598
|
+
// Scan forward over string content, returning the first byte that is not
|
|
599
|
+
// STR_OK in string_map. This is a pure drop-in for the scalar loop
|
|
600
|
+
//
|
|
601
|
+
// for (; STR_OK == string_map[*b]; b++) {}
|
|
602
|
+
//
|
|
603
|
+
// and must return the exact same stop position. The stop set (bytes whose
|
|
604
|
+
// string_map class is not 'R'/STR_OK) is:
|
|
605
|
+
//
|
|
606
|
+
// * control bytes 0x00..0x1F (class '.', also stops at the NUL terminator)
|
|
607
|
+
// * the quote 0x22 '"' (class 'z')
|
|
608
|
+
// * the backslash 0x5C '\' (class 'A')
|
|
609
|
+
// * every high byte 0x80..0xFF (UTF-8 lead/continuation, classes M/P/Q/'.')
|
|
610
|
+
//
|
|
611
|
+
// Note this differs from parse.c's string_scan_neon, which stops only on
|
|
612
|
+
// \0 \\ " -- parser.c hands multi-byte UTF-8 to its state machine, so the
|
|
613
|
+
// scanner must stop on the high bytes too. The predictor below is derived from
|
|
614
|
+
// string_map, not copied from parse.c.
|
|
615
|
+
//
|
|
616
|
+
// The NEON path only loads a full 16-byte vector when [b, b+16) stays within
|
|
617
|
+
// [., end), so it never reads past the string's allocation; the sub-16-byte
|
|
618
|
+
// tail (and the whole scan on non-NEON builds) uses the scalar loop, which
|
|
619
|
+
// stops naturally at the guaranteed NUL terminator.
|
|
620
|
+
static inline const byte *oj_scan_str_simd(const byte *b, const byte *end) {
|
|
621
|
+
#ifdef HAVE_SIMD_NEON
|
|
622
|
+
if (SIMD_NEON == SIMD_Impl) {
|
|
623
|
+
const uint8x16_t quote = vdupq_n_u8('"');
|
|
624
|
+
const uint8x16_t bslash = vdupq_n_u8('\\');
|
|
625
|
+
const uint8x16_t space = vdupq_n_u8(0x20);
|
|
626
|
+
const uint8x16_t high = vdupq_n_u8(0x80);
|
|
627
|
+
|
|
628
|
+
while (b + sizeof(uint8x16_t) <= end) {
|
|
629
|
+
const uint8x16_t chunk = vld1q_u8((const uint8_t *)b);
|
|
630
|
+
// special lane == 0xFF for any byte that stops the scan.
|
|
631
|
+
const uint8x16_t special = vorrq_u8(vorrq_u8(vceqq_u8(chunk, quote), vceqq_u8(chunk, bslash)),
|
|
632
|
+
vorrq_u8(vcltq_u8(chunk, space), vcgeq_u8(chunk, high)));
|
|
633
|
+
// Reduce to a 64-bit mask with 4 bits per lane (same idiom as
|
|
634
|
+
// parse.c's string_scan_neon) and locate the first set lane.
|
|
635
|
+
const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(special), 4);
|
|
636
|
+
uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
|
|
637
|
+
if (0 != mask) {
|
|
638
|
+
mask &= 0x8888888888888888ull;
|
|
639
|
+
return b + (OJ_CTZ64(mask) >> 2);
|
|
640
|
+
}
|
|
641
|
+
b += sizeof(uint8x16_t);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
#else
|
|
645
|
+
(void)end;
|
|
646
|
+
#endif
|
|
647
|
+
for (; STR_OK == string_map[*b]; b++) {
|
|
648
|
+
}
|
|
649
|
+
return b;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
598
653
|
const byte *start;
|
|
599
|
-
const byte *b
|
|
654
|
+
const byte *b = json;
|
|
655
|
+
const byte *end = json + len;
|
|
600
656
|
int i;
|
|
601
657
|
|
|
602
658
|
p->line = 1;
|
|
@@ -629,8 +685,7 @@ static void parse(ojParser p, const byte *json, bool more) {
|
|
|
629
685
|
b++;
|
|
630
686
|
p->key.tail = p->key.head;
|
|
631
687
|
start = b;
|
|
632
|
-
|
|
633
|
-
}
|
|
688
|
+
b = oj_scan_str_simd(b, end);
|
|
634
689
|
buf_append_string(&p->key, (const char *)start, b - start);
|
|
635
690
|
if ('"' == *b) {
|
|
636
691
|
p->map = colon_map;
|
|
@@ -654,8 +709,7 @@ static void parse(ojParser p, const byte *json, bool more) {
|
|
|
654
709
|
b++;
|
|
655
710
|
start = b;
|
|
656
711
|
p->buf.tail = p->buf.head;
|
|
657
|
-
|
|
658
|
-
}
|
|
712
|
+
b = oj_scan_str_simd(b, end);
|
|
659
713
|
buf_append_string(&p->buf, (const char *)start, b - start);
|
|
660
714
|
if ('"' == *b) {
|
|
661
715
|
p->cur = b - json;
|
|
@@ -838,10 +892,10 @@ static void parse(ojParser p, const byte *json, bool more) {
|
|
|
838
892
|
case EXP_DIGIT:
|
|
839
893
|
p->map = exp_map;
|
|
840
894
|
for (; NUM_DIGIT == digit_map[*b]; b++) {
|
|
841
|
-
|
|
895
|
+
int x = p->num.exp * 10 + (*b - '0');
|
|
842
896
|
|
|
843
897
|
if (x <= MAX_EXP) {
|
|
844
|
-
p->num.exp = x;
|
|
898
|
+
p->num.exp = (int16_t)x;
|
|
845
899
|
} else {
|
|
846
900
|
big_change(p);
|
|
847
901
|
p->map = big_exp_map;
|
|
@@ -905,8 +959,7 @@ static void parse(ojParser p, const byte *json, bool more) {
|
|
|
905
959
|
break;
|
|
906
960
|
case STR_OK:
|
|
907
961
|
start = b;
|
|
908
|
-
|
|
909
|
-
}
|
|
962
|
+
b = oj_scan_str_simd(b, end);
|
|
910
963
|
if (':' == p->next_map[256]) {
|
|
911
964
|
buf_append_string(&p->key, (const char *)start, b - start);
|
|
912
965
|
} else {
|
|
@@ -1229,7 +1282,8 @@ static int opt_cb(VALUE rkey, VALUE value, VALUE ptr) {
|
|
|
1229
1282
|
* Oj::Parser.new(:usual, cache_keys: true).
|
|
1230
1283
|
*/
|
|
1231
1284
|
static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
|
|
1232
|
-
ojParser
|
|
1285
|
+
ojParser p = OJ_R_ALLOC(struct _ojParser);
|
|
1286
|
+
volatile VALUE pv;
|
|
1233
1287
|
|
|
1234
1288
|
#if HAVE_RB_EXT_RACTOR_SAFE
|
|
1235
1289
|
// This doesn't seem to do anything.
|
|
@@ -1239,6 +1293,10 @@ static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
|
|
|
1239
1293
|
buf_init(&p->key);
|
|
1240
1294
|
buf_init(&p->buf);
|
|
1241
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);
|
|
1242
1300
|
|
|
1243
1301
|
if (argc < 1) {
|
|
1244
1302
|
oj_set_parser_validator(p);
|
|
@@ -1279,7 +1337,7 @@ static VALUE parser_new(int argc, VALUE *argv, VALUE self) {
|
|
|
1279
1337
|
rb_hash_foreach(ropts, opt_cb, (VALUE)p);
|
|
1280
1338
|
}
|
|
1281
1339
|
}
|
|
1282
|
-
return
|
|
1340
|
+
return pv;
|
|
1283
1341
|
}
|
|
1284
1342
|
|
|
1285
1343
|
// Create a new parser without setting the delegate. The parser is
|
|
@@ -1419,7 +1477,7 @@ static VALUE parser_parse(VALUE self, VALUE json) {
|
|
|
1419
1477
|
|
|
1420
1478
|
parser_reset(p);
|
|
1421
1479
|
p->start(p);
|
|
1422
|
-
parse(p, ptr, false);
|
|
1480
|
+
parse(p, ptr, (size_t)RSTRING_LEN(json), false);
|
|
1423
1481
|
validate_document_end(p);
|
|
1424
1482
|
|
|
1425
1483
|
return p->result(p);
|
|
@@ -1440,7 +1498,7 @@ static VALUE load(VALUE self) {
|
|
|
1440
1498
|
while (true) {
|
|
1441
1499
|
rb_funcall(p->reader, oj_readpartial_id, 2, INT2NUM(16385), rbuf);
|
|
1442
1500
|
if (0 < RSTRING_LEN(rbuf)) {
|
|
1443
|
-
parse(p, (byte *)StringValuePtr(rbuf), true);
|
|
1501
|
+
parse(p, (byte *)StringValuePtr(rbuf), (size_t)RSTRING_LEN(rbuf), true);
|
|
1444
1502
|
}
|
|
1445
1503
|
if (Qtrue == rb_funcall(p->reader, oj_eofq_id, 0)) {
|
|
1446
1504
|
if (0 < p->depth) {
|
|
@@ -1471,6 +1529,39 @@ static VALUE parser_load(VALUE self, VALUE reader) {
|
|
|
1471
1529
|
return p->result(p);
|
|
1472
1530
|
}
|
|
1473
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
|
+
|
|
1474
1565
|
/* Document-method: file(filename)
|
|
1475
1566
|
* call-seq: file(filename)
|
|
1476
1567
|
*
|
|
@@ -1479,9 +1570,10 @@ static VALUE parser_load(VALUE self, VALUE reader) {
|
|
|
1479
1570
|
* Returns the result according to the delegate of the parser.
|
|
1480
1571
|
*/
|
|
1481
1572
|
static VALUE parser_file(VALUE self, VALUE filename) {
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1573
|
+
struct _fileParse fp;
|
|
1574
|
+
ojParser p;
|
|
1575
|
+
const char *path;
|
|
1576
|
+
int fd;
|
|
1485
1577
|
|
|
1486
1578
|
TypedData_Get_Struct(self, struct _ojParser, &oj_parser_type, p);
|
|
1487
1579
|
|
|
@@ -1500,26 +1592,15 @@ static VALUE parser_file(VALUE self, VALUE filename) {
|
|
|
1500
1592
|
// Use threaded version.
|
|
1501
1593
|
// TBD only if has pthreads
|
|
1502
1594
|
// TBD parse_large(p, fd);
|
|
1595
|
+
close(fd);
|
|
1503
1596
|
return p->result(p);
|
|
1504
1597
|
}
|
|
1505
1598
|
#endif
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1599
|
+
fp.p = p;
|
|
1600
|
+
fp.path = path;
|
|
1601
|
+
fp.fd = fd;
|
|
1509
1602
|
|
|
1510
|
-
|
|
1511
|
-
if (0 < (rsize = read(fd, buf, size))) {
|
|
1512
|
-
buf[rsize] = '\0';
|
|
1513
|
-
parse(p, buf, true);
|
|
1514
|
-
}
|
|
1515
|
-
if (rsize <= 0) {
|
|
1516
|
-
if (0 != rsize) {
|
|
1517
|
-
rb_raise(rb_eIOError, "error reading from %s", path);
|
|
1518
|
-
}
|
|
1519
|
-
break;
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
return p->result(p);
|
|
1603
|
+
return rb_ensure(file_parse, (VALUE)&fp, file_close, (VALUE)&fp);
|
|
1523
1604
|
}
|
|
1524
1605
|
|
|
1525
1606
|
/* Document-method: just_one
|
|
@@ -1632,15 +1713,19 @@ static VALUE parser_safe(int argc, VALUE *argv, VALUE self) {
|
|
|
1632
1713
|
options = rb_hash_new();
|
|
1633
1714
|
}
|
|
1634
1715
|
|
|
1635
|
-
ojParser
|
|
1716
|
+
ojParser p = OJ_R_ALLOC(struct _ojParser);
|
|
1717
|
+
volatile VALUE pv;
|
|
1636
1718
|
|
|
1637
1719
|
memset(p, 0, sizeof(struct _ojParser));
|
|
1638
1720
|
buf_init(&p->key);
|
|
1639
1721
|
buf_init(&p->buf);
|
|
1640
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);
|
|
1641
1726
|
oj_set_parser_safe(p, options);
|
|
1642
1727
|
|
|
1643
|
-
return
|
|
1728
|
+
return pv;
|
|
1644
1729
|
}
|
|
1645
1730
|
|
|
1646
1731
|
/* Document-class: Oj::Parser
|