oj 3.17.6 → 3.17.8
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 +67 -0
- data/ext/oj/cache.c +64 -47
- data/ext/oj/cache.h +1 -1
- data/ext/oj/code.c +38 -28
- data/ext/oj/code.h +2 -0
- data/ext/oj/compat.c +61 -29
- data/ext/oj/custom.c +38 -5
- data/ext/oj/dump.c +93 -29
- data/ext/oj/dump_compat.c +8 -11
- data/ext/oj/dump_object.c +18 -2
- data/ext/oj/dump_strict.c +2 -1
- data/ext/oj/extconf.rb +7 -1
- data/ext/oj/fast.c +46 -29
- data/ext/oj/intern.c +75 -77
- data/ext/oj/mimic_json.c +5 -29
- data/ext/oj/object.c +21 -2
- data/ext/oj/oj.c +136 -33
- data/ext/oj/oj.h +9 -7
- data/ext/oj/parse.c +58 -4
- data/ext/oj/parse.h +23 -1
- data/ext/oj/parser.c +99 -36
- data/ext/oj/parser.h +1 -1
- data/ext/oj/rails.c +8 -3
- data/ext/oj/reader.c +4 -0
- data/ext/oj/reader.h +1 -0
- data/ext/oj/resolve.c +5 -4
- data/ext/oj/resolve.h +9 -0
- data/ext/oj/saj.c +14 -7
- data/ext/oj/saj2.c +44 -28
- data/ext/oj/simd.h +5 -3
- data/ext/oj/sparse.c +7 -0
- data/ext/oj/stream_writer.c +3 -0
- data/ext/oj/strict.c +42 -4
- data/ext/oj/string_writer.c +17 -3
- data/ext/oj/usual.c +12 -4
- data/ext/oj/val_stack.c +11 -22
- data/ext/oj/val_stack.h +45 -24
- data/ext/oj/wab.c +3 -0
- data/lib/oj/generic_object.rb +82 -0
- data/lib/oj/json.rb +1 -71
- data/lib/oj/mimic.rb +0 -1
- data/lib/oj/version.rb +1 -1
- metadata +16 -15
data/ext/oj/parse.c
CHANGED
|
@@ -390,6 +390,32 @@ void oj_scanner_init(void) {
|
|
|
390
390
|
}
|
|
391
391
|
}
|
|
392
392
|
|
|
393
|
+
// JSON does not allow an unescaped control character (below 0x20) in a
|
|
394
|
+
// string. Like a trailing decimal point, that is only rejected in strict mode
|
|
395
|
+
// and when mimicking the JSON gem.
|
|
396
|
+
static inline bool reject_control_chars(ParseInfo pi) {
|
|
397
|
+
return StrictMode == pi->options.mode || CompatMode == pi->options.mode;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
static const char *find_control_char(const char *s, const char *end) {
|
|
401
|
+
// Eight bytes at a time first: a byte below 0x20 sets its high bit in
|
|
402
|
+
// (w - 0x20...) & ~w.
|
|
403
|
+
for (; s + sizeof(uint64_t) <= end; s += sizeof(uint64_t)) {
|
|
404
|
+
uint64_t w;
|
|
405
|
+
|
|
406
|
+
memcpy(&w, s, sizeof(w));
|
|
407
|
+
if (0 != ((w - 0x2020202020202020ULL) & ~w & 0x8080808080808080ULL)) {
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
for (; s < end; s++) {
|
|
412
|
+
if ((unsigned char)*s < 0x20) {
|
|
413
|
+
return s;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return NULL;
|
|
417
|
+
}
|
|
418
|
+
|
|
393
419
|
// entered at /
|
|
394
420
|
static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
395
421
|
struct _buf buf;
|
|
@@ -409,6 +435,16 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
|
409
435
|
buf_cleanup(&buf);
|
|
410
436
|
return;
|
|
411
437
|
}
|
|
438
|
+
if (reject_control_chars(pi)) {
|
|
439
|
+
const char *ctrl = find_control_char(s, scanned);
|
|
440
|
+
|
|
441
|
+
if (NULL != ctrl) {
|
|
442
|
+
pi->cur = ctrl;
|
|
443
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid control character in string");
|
|
444
|
+
buf_cleanup(&buf);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
412
448
|
buf_append_string(&buf, s, (size_t)(scanned - s));
|
|
413
449
|
s = scanned;
|
|
414
450
|
|
|
@@ -543,6 +579,15 @@ static void read_str(ParseInfo pi) {
|
|
|
543
579
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "NULL byte in string");
|
|
544
580
|
return;
|
|
545
581
|
}
|
|
582
|
+
if (reject_control_chars(pi)) {
|
|
583
|
+
const char *ctrl = find_control_char(str, pi->cur);
|
|
584
|
+
|
|
585
|
+
if (NULL != ctrl) {
|
|
586
|
+
pi->cur = ctrl;
|
|
587
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid control character in string");
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
546
591
|
if ('\\' == *pi->cur) {
|
|
547
592
|
read_escaped_str(pi, str);
|
|
548
593
|
return;
|
|
@@ -1205,17 +1250,20 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
|
|
|
1205
1250
|
}
|
|
1206
1251
|
} else {
|
|
1207
1252
|
VALUE clas = rb_obj_class(input);
|
|
1208
|
-
VALUE s;
|
|
1209
1253
|
|
|
1210
1254
|
if (oj_stringio_class == clas) {
|
|
1211
|
-
|
|
1212
|
-
|
|
1255
|
+
input = rb_funcall2(input, oj_string_id, 0, 0);
|
|
1256
|
+
StringValue(input);
|
|
1257
|
+
oj_pi_set_input_str(pi, &input);
|
|
1213
1258
|
#if !IS_WINDOWS
|
|
1214
1259
|
} else if (rb_cFile == clas && 0 == FIX2INT(rb_funcall(input, oj_pos_id, 0))) {
|
|
1215
1260
|
int fd = FIX2INT(rb_funcall(input, oj_fileno_id, 0));
|
|
1216
1261
|
ssize_t cnt;
|
|
1217
1262
|
size_t len = lseek(fd, 0, SEEK_END);
|
|
1218
1263
|
|
|
1264
|
+
if ((size_t)-1 == len) {
|
|
1265
|
+
rb_raise(rb_eIOError, "failed to read from IO Object.");
|
|
1266
|
+
}
|
|
1219
1267
|
lseek(fd, 0, SEEK_SET);
|
|
1220
1268
|
buf = OJ_R_ALLOC_N(char, len + 1);
|
|
1221
1269
|
pi->json = buf;
|
|
@@ -1261,7 +1309,11 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
|
|
|
1261
1309
|
// value stack (while it is in scope).
|
|
1262
1310
|
wrapped_stack = oj_stack_init(&pi->stack);
|
|
1263
1311
|
rb_protect(protect_parse, (VALUE)pi, &line);
|
|
1264
|
-
|
|
1312
|
+
// Only when nothing is on the stack was the input actually empty. A
|
|
1313
|
+
// deferred hash (built at its closing brace from buffered pairs) leaves
|
|
1314
|
+
// head->val Qundef while its entry is still on the stack, e.g. when a
|
|
1315
|
+
// parse error cut the document short; the real error is already set.
|
|
1316
|
+
if (Qundef == pi->stack.head->val && stack_empty(&pi->stack) && !empty_ok(&pi->options)) {
|
|
1265
1317
|
if (No == pi->options.nilnil || (CompatMode == pi->options.mode && 0 < pi->cur - pi->json)) {
|
|
1266
1318
|
oj_set_error_at(pi, oj_json_parser_error_class, __FILE__, __LINE__, "Empty input");
|
|
1267
1319
|
}
|
|
@@ -1338,6 +1390,7 @@ CLEANUP:
|
|
|
1338
1390
|
if (NULL != pi->json) {
|
|
1339
1391
|
msg = rb_str_append(msg, rb_utf8_str_new_cstr(" in '"));
|
|
1340
1392
|
msg = rb_str_append(msg, rb_utf8_str_new_cstr(pi->json));
|
|
1393
|
+
RB_GC_GUARD(input);
|
|
1341
1394
|
}
|
|
1342
1395
|
args[0] = msg;
|
|
1343
1396
|
if (pi->err.clas == oj_parse_error_class) {
|
|
@@ -1377,5 +1430,6 @@ CLEANUP:
|
|
|
1377
1430
|
break;
|
|
1378
1431
|
}
|
|
1379
1432
|
}
|
|
1433
|
+
RB_GC_GUARD(input);
|
|
1380
1434
|
return result;
|
|
1381
1435
|
}
|
data/ext/oj/parse.h
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#define OJ_PARSE_H
|
|
6
6
|
|
|
7
7
|
#include <stdarg.h>
|
|
8
|
+
#include <stddef.h>
|
|
8
9
|
#include <stdio.h>
|
|
9
10
|
#include <string.h>
|
|
10
11
|
|
|
@@ -80,6 +81,7 @@ extern VALUE oj_num_as_value(NumInfo ni);
|
|
|
80
81
|
extern void oj_set_strict_callbacks(ParseInfo pi);
|
|
81
82
|
extern void oj_set_object_callbacks(ParseInfo pi);
|
|
82
83
|
extern void oj_set_compat_callbacks(ParseInfo pi);
|
|
84
|
+
extern void oj_compat_array_append_other(VALUE a, VALUE value);
|
|
83
85
|
extern void oj_set_custom_callbacks(ParseInfo pi);
|
|
84
86
|
extern void oj_set_wab_callbacks(ParseInfo pi);
|
|
85
87
|
|
|
@@ -90,11 +92,31 @@ extern VALUE oj_cstr_to_value(const char *str, size_t len, size_t cache_str);
|
|
|
90
92
|
extern VALUE oj_calc_hash_key(ParseInfo pi, Val parent);
|
|
91
93
|
|
|
92
94
|
static inline void parse_info_init(ParseInfo pi) {
|
|
93
|
-
|
|
95
|
+
// Zeroing the whole struct costs more than parsing a small document: the
|
|
96
|
+
// embedded reader buffer (rd.base, 4KB) and value stack entries
|
|
97
|
+
// (stack.base, 6.5KB) dominate its 11KB. Both are fully initialized
|
|
98
|
+
// before use — oj_reader_init() for stream parses, oj_stack_init() and
|
|
99
|
+
// stack_push() for the value stack — so zero only the scalars around
|
|
100
|
+
// them.
|
|
101
|
+
memset(pi, 0, offsetof(struct _parseInfo, rd));
|
|
102
|
+
memset((char *)pi + offsetof(struct _parseInfo, rd) + offsetof(struct _reader, head),
|
|
103
|
+
0,
|
|
104
|
+
offsetof(struct _parseInfo, stack) - offsetof(struct _parseInfo, rd) - offsetof(struct _reader, head));
|
|
105
|
+
memset((char *)pi + offsetof(struct _parseInfo, stack) + offsetof(struct _valStack, head),
|
|
106
|
+
0,
|
|
107
|
+
sizeof(struct _parseInfo) - offsetof(struct _parseInfo, stack) - offsetof(struct _valStack, head));
|
|
94
108
|
}
|
|
95
109
|
|
|
96
110
|
extern void oj_scanner_init(void);
|
|
97
111
|
|
|
112
|
+
static inline void oj_compat_array_append(VALUE a, VALUE value) {
|
|
113
|
+
if (rb_cArray == RBASIC_CLASS(a)) {
|
|
114
|
+
rb_ary_push(a, value);
|
|
115
|
+
} else {
|
|
116
|
+
oj_compat_array_append_other(a, value);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
98
120
|
static inline bool empty_ok(Options options) {
|
|
99
121
|
switch (options->mode) {
|
|
100
122
|
case ObjectMode:
|
data/ext/oj/parser.c
CHANGED
|
@@ -179,8 +179,8 @@ static const char neg_map[258] = "\
|
|
|
179
179
|
static const char zero_map[258] = "\
|
|
180
180
|
.........rs..r..................\
|
|
181
181
|
r...........u.t.................\
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
.....w.......................H..\
|
|
183
|
+
.....w.......................G..\
|
|
184
184
|
................................\
|
|
185
185
|
................................\
|
|
186
186
|
................................\
|
|
@@ -509,20 +509,29 @@ static void calc_num(ojParser p) {
|
|
|
509
509
|
d = -d;
|
|
510
510
|
}
|
|
511
511
|
if (0 < p->num.shift) {
|
|
512
|
-
|
|
513
|
-
}
|
|
514
|
-
if (0 < p->num.exp) {
|
|
515
|
-
long double x;
|
|
512
|
+
uint32_t shift = p->num.shift;
|
|
516
513
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
x = pow_map[p->num.exp];
|
|
514
|
+
while (MAX_POW < shift) {
|
|
515
|
+
d /= pow_map[MAX_POW];
|
|
516
|
+
shift -= MAX_POW;
|
|
521
517
|
}
|
|
518
|
+
d /= pow_map[shift];
|
|
519
|
+
}
|
|
520
|
+
if (0 < p->num.exp) {
|
|
522
521
|
if (p->num.exp_neg) {
|
|
523
|
-
|
|
522
|
+
int exp = p->num.exp;
|
|
523
|
+
|
|
524
|
+
while (MAX_POW < exp) {
|
|
525
|
+
d /= pow_map[MAX_POW];
|
|
526
|
+
exp -= MAX_POW;
|
|
527
|
+
}
|
|
528
|
+
d /= pow_map[exp];
|
|
524
529
|
} else {
|
|
525
|
-
|
|
530
|
+
if (MAX_POW < p->num.exp) {
|
|
531
|
+
d *= powl(10.0L, (long double)p->num.exp);
|
|
532
|
+
} else {
|
|
533
|
+
d *= pow_map[p->num.exp];
|
|
534
|
+
}
|
|
526
535
|
}
|
|
527
536
|
}
|
|
528
537
|
p->num.dub = d;
|
|
@@ -559,19 +568,31 @@ static void big_change(ojParser p) {
|
|
|
559
568
|
break;
|
|
560
569
|
case OJ_DECIMAL: {
|
|
561
570
|
int shift = p->num.shift;
|
|
571
|
+
int end = sizeof(buf) - 1;
|
|
562
572
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
buf[len] = '0' + (i % 10);
|
|
569
|
-
}
|
|
573
|
+
len = end;
|
|
574
|
+
do {
|
|
575
|
+
buf[--len] = '0' + (i % 10);
|
|
576
|
+
i /= 10;
|
|
577
|
+
} while (0 < i);
|
|
570
578
|
if (p->num.neg) {
|
|
571
|
-
buf
|
|
572
|
-
|
|
579
|
+
buf_append(&p->buf, '-');
|
|
580
|
+
}
|
|
581
|
+
if (end - len <= shift) {
|
|
582
|
+
buf_append_string(&p->buf, "0.", 2);
|
|
583
|
+
for (int cnt = shift - (end - len); 0 < cnt; cnt--) {
|
|
584
|
+
buf_append(&p->buf, '0');
|
|
585
|
+
}
|
|
586
|
+
buf_append_string(&p->buf, buf + len, end - len);
|
|
587
|
+
} else {
|
|
588
|
+
int decimal = end - shift;
|
|
589
|
+
|
|
590
|
+
buf_append_string(&p->buf, buf + len, decimal - len);
|
|
591
|
+
if (0 < shift) {
|
|
592
|
+
buf_append(&p->buf, '.');
|
|
593
|
+
buf_append_string(&p->buf, buf + decimal, shift);
|
|
594
|
+
}
|
|
573
595
|
}
|
|
574
|
-
buf_append_string(&p->buf, buf + len + 1, sizeof(buf) - len - 1);
|
|
575
596
|
if (0 < p->num.exp) {
|
|
576
597
|
int x = p->num.exp;
|
|
577
598
|
int d, div;
|
|
@@ -585,6 +606,7 @@ static void big_change(ojParser p) {
|
|
|
585
606
|
d = x / div % 10;
|
|
586
607
|
if (started || 0 < d) {
|
|
587
608
|
buf_append(&p->buf, '0' + d);
|
|
609
|
+
started = true;
|
|
588
610
|
}
|
|
589
611
|
}
|
|
590
612
|
}
|
|
@@ -740,6 +762,12 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
740
762
|
calc_num(p);
|
|
741
763
|
// flow through
|
|
742
764
|
case CLOSE_OBJECT:
|
|
765
|
+
// Inside an object value_map is only in use right after a colon.
|
|
766
|
+
if (value_map == p->map && 0 < p->depth && OBJECT_FUN == p->stack[p->depth]) {
|
|
767
|
+
p->col = b - json - p->col + 1;
|
|
768
|
+
parse_error(p, "expected a value, not an object close");
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
743
771
|
p->map = (1 == p->depth) ? value_map : after_map;
|
|
744
772
|
if (p->depth <= 0 || OBJECT_FUN != p->stack[p->depth]) {
|
|
745
773
|
p->col = b - json - p->col + 1;
|
|
@@ -859,6 +887,11 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
859
887
|
p->num.shift++;
|
|
860
888
|
} else {
|
|
861
889
|
big_change(p);
|
|
890
|
+
if (0 == p->num.shift) {
|
|
891
|
+
// No fraction digit was read yet so big_change()
|
|
892
|
+
// did not add the '.' before the digits that follow.
|
|
893
|
+
buf_append(&p->buf, '.');
|
|
894
|
+
}
|
|
862
895
|
p->map = big_frac_map;
|
|
863
896
|
break;
|
|
864
897
|
}
|
|
@@ -871,6 +904,7 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
871
904
|
break;
|
|
872
905
|
case NUM_ZERO: p->map = zero_map; break;
|
|
873
906
|
case NEG_DIGIT:
|
|
907
|
+
p->map = digit_map;
|
|
874
908
|
for (; NUM_DIGIT == digit_map[*b]; b++) {
|
|
875
909
|
uint64_t x = p->num.fixnum * 10 + (uint64_t)(*b - '0');
|
|
876
910
|
|
|
@@ -883,7 +917,6 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
883
917
|
}
|
|
884
918
|
}
|
|
885
919
|
b--;
|
|
886
|
-
p->map = digit_map;
|
|
887
920
|
break;
|
|
888
921
|
case EXP_SIGN:
|
|
889
922
|
p->num.exp_neg = ('-' == *b);
|
|
@@ -942,10 +975,12 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
942
975
|
case NUM_SPC:
|
|
943
976
|
p->cur = b - json;
|
|
944
977
|
calc_num(p);
|
|
978
|
+
p->map = (0 == p->depth) ? value_map : after_map;
|
|
945
979
|
break;
|
|
946
980
|
case NUM_NEWLINE:
|
|
947
981
|
p->cur = b - json;
|
|
948
982
|
calc_num(p);
|
|
983
|
+
p->map = (0 == p->depth) ? value_map : after_map;
|
|
949
984
|
b++;
|
|
950
985
|
#ifdef SPACE_JUMP
|
|
951
986
|
// for (uint32_t *sj = (uint32_t*)b; 0x20202020 == *sj; sj++) { b += 4; }
|
|
@@ -988,15 +1023,26 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
988
1023
|
p->ri++;
|
|
989
1024
|
p->ucode = p->ucode << 4 | (uint32_t)hex_map[*b];
|
|
990
1025
|
if (4 <= p->ri) {
|
|
991
|
-
byte
|
|
992
|
-
|
|
1026
|
+
byte utf8[8];
|
|
1027
|
+
Buf buf = (':' == p->next_map[256]) ? &p->key : &p->buf;
|
|
1028
|
+
uint32_t code = p->ucode;
|
|
993
1029
|
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1030
|
+
// A low surrogate right after a high one, which the previous
|
|
1031
|
+
// escape appended on its own, completes a pair.
|
|
1032
|
+
if (0xDC00 <= code && code <= 0xDFFF && 3 <= buf_len(buf)) {
|
|
1033
|
+
const byte *hi = (const byte *)buf->tail - 3;
|
|
1034
|
+
|
|
1035
|
+
if (0xED == hi[0] && 0xA0 <= hi[1] && hi[1] <= 0xAF) {
|
|
1036
|
+
uint32_t c1 = ((uint32_t)(hi[1] & 0x0F) << 6) | (hi[2] & 0x3F);
|
|
1037
|
+
|
|
1038
|
+
code = ((c1 << 10) | (code - 0xDC00)) + 0x00010000;
|
|
1039
|
+
buf->tail -= 3;
|
|
999
1040
|
}
|
|
1041
|
+
}
|
|
1042
|
+
size_t ulen = unicodeToUtf8(code, utf8);
|
|
1043
|
+
|
|
1044
|
+
if (0 < ulen) {
|
|
1045
|
+
buf_append_string(buf, (const char *)utf8, ulen);
|
|
1000
1046
|
} else {
|
|
1001
1047
|
parse_error(p, "invalid unicode");
|
|
1002
1048
|
return;
|
|
@@ -1186,11 +1232,9 @@ static void parse(ojParser p, const byte *json, size_t len, bool more) {
|
|
|
1186
1232
|
case '0':
|
|
1187
1233
|
case 'd':
|
|
1188
1234
|
case 'f':
|
|
1189
|
-
case 'z':
|
|
1190
1235
|
case 'X':
|
|
1191
1236
|
case 'D':
|
|
1192
1237
|
case 'g':
|
|
1193
|
-
case 'B':
|
|
1194
1238
|
case 'Y':
|
|
1195
1239
|
p->cur = b - json;
|
|
1196
1240
|
calc_num(p);
|
|
@@ -1440,6 +1484,23 @@ static void validate_primitives_are_complete(ojParser p) {
|
|
|
1440
1484
|
}
|
|
1441
1485
|
}
|
|
1442
1486
|
|
|
1487
|
+
// A top level number that stops after a '-', a '.', an 'e' or an exponent
|
|
1488
|
+
// sign is still waiting for a digit.
|
|
1489
|
+
static void validate_number_is_complete(ojParser p) {
|
|
1490
|
+
if (0 < p->depth) {
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
switch (p->map[256]) {
|
|
1494
|
+
case '-':
|
|
1495
|
+
case '.':
|
|
1496
|
+
case 'x':
|
|
1497
|
+
case 'z':
|
|
1498
|
+
case 'o':
|
|
1499
|
+
case 'B':
|
|
1500
|
+
case 'Z': parse_error(p, "incomplete number"); break;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1443
1504
|
static void validate_non_primitives_are_complete(ojParser p) {
|
|
1444
1505
|
if (0 >= p->depth) {
|
|
1445
1506
|
return;
|
|
@@ -1453,6 +1514,7 @@ static void validate_non_primitives_are_complete(ojParser p) {
|
|
|
1453
1514
|
|
|
1454
1515
|
static void validate_document_end(ojParser p) {
|
|
1455
1516
|
validate_primitives_are_complete(p);
|
|
1517
|
+
validate_number_is_complete(p);
|
|
1456
1518
|
validate_non_primitives_are_complete(p);
|
|
1457
1519
|
}
|
|
1458
1520
|
|
|
@@ -1465,19 +1527,20 @@ static void validate_document_end(ojParser p) {
|
|
|
1465
1527
|
*/
|
|
1466
1528
|
static VALUE parser_parse(VALUE self, VALUE json) {
|
|
1467
1529
|
ojParser p;
|
|
1468
|
-
int frozen = OBJ_FROZEN(json);
|
|
1469
1530
|
const byte *ptr;
|
|
1470
1531
|
|
|
1471
|
-
|
|
1532
|
+
StringValue(json);
|
|
1533
|
+
if (!OBJ_FROZEN(json)) {
|
|
1472
1534
|
rb_str_freeze(json);
|
|
1473
1535
|
}
|
|
1474
|
-
ptr = (const byte *)
|
|
1536
|
+
ptr = (const byte *)RSTRING_PTR(json);
|
|
1475
1537
|
|
|
1476
1538
|
TypedData_Get_Struct(self, struct _ojParser, &oj_parser_type, p);
|
|
1477
1539
|
|
|
1478
1540
|
parser_reset(p);
|
|
1479
1541
|
p->start(p);
|
|
1480
1542
|
parse(p, ptr, (size_t)RSTRING_LEN(json), false);
|
|
1543
|
+
RB_GC_GUARD(json);
|
|
1481
1544
|
validate_document_end(p);
|
|
1482
1545
|
|
|
1483
1546
|
return p->result(p);
|
|
@@ -1577,7 +1640,7 @@ static VALUE parser_file(VALUE self, VALUE filename) {
|
|
|
1577
1640
|
|
|
1578
1641
|
TypedData_Get_Struct(self, struct _ojParser, &oj_parser_type, p);
|
|
1579
1642
|
|
|
1580
|
-
path =
|
|
1643
|
+
path = StringValueCStr(filename);
|
|
1581
1644
|
|
|
1582
1645
|
parser_reset(p);
|
|
1583
1646
|
p->start(p);
|
data/ext/oj/parser.h
CHANGED
data/ext/oj/rails.c
CHANGED
|
@@ -1062,6 +1062,7 @@ static VALUE encode(VALUE obj, ROptTable ropts, Options opts, int argc, VALUE *a
|
|
|
1062
1062
|
}
|
|
1063
1063
|
if (Yes == copts.circular) {
|
|
1064
1064
|
oj_cache8_delete(out.circ_cache);
|
|
1065
|
+
out.circ_cache = NULL;
|
|
1065
1066
|
}
|
|
1066
1067
|
|
|
1067
1068
|
oj_out_free(&out);
|
|
@@ -1316,12 +1317,15 @@ static void dump_float(VALUE obj, int depth, Out out, bool as_ok) {
|
|
|
1316
1317
|
size_t cnt = 0;
|
|
1317
1318
|
|
|
1318
1319
|
if (0.0 == d) {
|
|
1319
|
-
b
|
|
1320
|
+
b = buf;
|
|
1321
|
+
if (signbit(d)) {
|
|
1322
|
+
*b++ = '-';
|
|
1323
|
+
}
|
|
1320
1324
|
*b++ = '0';
|
|
1321
1325
|
*b++ = '.';
|
|
1322
1326
|
*b++ = '0';
|
|
1323
|
-
*b
|
|
1324
|
-
cnt =
|
|
1327
|
+
*b = '\0';
|
|
1328
|
+
cnt = b - buf;
|
|
1325
1329
|
} else {
|
|
1326
1330
|
if (isnan(d) || OJ_INFINITY == d || -OJ_INFINITY == d) {
|
|
1327
1331
|
strcpy(buf, "null");
|
|
@@ -1372,6 +1376,7 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
|
|
|
1372
1376
|
cnt--;
|
|
1373
1377
|
for (i = 0; i <= cnt; i++) {
|
|
1374
1378
|
if (out->opts->dump_opts.use) {
|
|
1379
|
+
assure_size(out, size);
|
|
1375
1380
|
if (0 < out->opts->dump_opts.array_size) {
|
|
1376
1381
|
APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
|
|
1377
1382
|
}
|
data/ext/oj/reader.c
CHANGED
|
@@ -45,6 +45,7 @@ void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s) {
|
|
|
45
45
|
reader->line = 1;
|
|
46
46
|
reader->col = 0;
|
|
47
47
|
reader->free_head = 0;
|
|
48
|
+
reader->in_val = Qnil;
|
|
48
49
|
|
|
49
50
|
if (0 != fd) {
|
|
50
51
|
reader->read_func = read_from_fd;
|
|
@@ -52,6 +53,7 @@ void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s) {
|
|
|
52
53
|
} else if (rb_cString == io_class) {
|
|
53
54
|
reader->read_func = 0;
|
|
54
55
|
reader->in_str = StringValuePtr(io);
|
|
56
|
+
reader->in_val = io;
|
|
55
57
|
reader->head = (char *)reader->in_str;
|
|
56
58
|
reader->tail = reader->head;
|
|
57
59
|
reader->read_end = reader->head + RSTRING_LEN(io);
|
|
@@ -60,6 +62,7 @@ void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s) {
|
|
|
60
62
|
|
|
61
63
|
reader->read_func = 0;
|
|
62
64
|
reader->in_str = StringValuePtr(s);
|
|
65
|
+
reader->in_val = s;
|
|
63
66
|
reader->head = (char *)reader->in_str;
|
|
64
67
|
reader->tail = reader->head;
|
|
65
68
|
reader->read_end = reader->head + RSTRING_LEN(s);
|
|
@@ -79,6 +82,7 @@ void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s) {
|
|
|
79
82
|
|
|
80
83
|
reader->read_func = 0;
|
|
81
84
|
reader->in_str = StringValuePtr(rstr);
|
|
85
|
+
reader->in_val = rstr;
|
|
82
86
|
reader->head = (char *)reader->in_str;
|
|
83
87
|
reader->tail = reader->head;
|
|
84
88
|
reader->read_end = reader->head + RSTRING_LEN(rstr);
|
data/ext/oj/reader.h
CHANGED
data/ext/oj/resolve.c
CHANGED
|
@@ -82,8 +82,9 @@ oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE
|
|
|
82
82
|
|
|
83
83
|
VALUE
|
|
84
84
|
oj_name2struct(ParseInfo pi, VALUE nameVal, VALUE error_class) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
if (T_STRING != rb_type(nameVal)) {
|
|
86
|
+
oj_set_error_at(pi, error_class, __FILE__, __LINE__, "Invalid struct data");
|
|
87
|
+
return Qundef;
|
|
88
|
+
}
|
|
89
|
+
return resolve_classpath(pi, RSTRING_PTR(nameVal), RSTRING_LEN(nameVal), 0, error_class);
|
|
89
90
|
}
|
data/ext/oj/resolve.h
CHANGED
|
@@ -4,9 +4,18 @@
|
|
|
4
4
|
#ifndef OJ_RESOLVE_H
|
|
5
5
|
#define OJ_RESOLVE_H
|
|
6
6
|
|
|
7
|
+
#include <stdbool.h>
|
|
8
|
+
#include <string.h>
|
|
9
|
+
|
|
7
10
|
#include "ruby.h"
|
|
8
11
|
|
|
9
12
|
extern VALUE oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE error_class);
|
|
10
13
|
extern VALUE oj_name2struct(ParseInfo pi, VALUE nameVal, VALUE error_class);
|
|
11
14
|
|
|
15
|
+
inline static bool oj_is_backtrace_name(const char *name, size_t len) {
|
|
16
|
+
static const char backtrace[] = "Thread::Backtrace";
|
|
17
|
+
|
|
18
|
+
return sizeof(backtrace) - 1 == len && 0 == memcmp(backtrace, name, len);
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
#endif /* OJ_RESOLVE_H */
|
data/ext/oj/saj.c
CHANGED
|
@@ -646,13 +646,14 @@ static VALUE protect_saj_parse(VALUE x) {
|
|
|
646
646
|
*/
|
|
647
647
|
VALUE
|
|
648
648
|
oj_saj_parse(int argc, VALUE *argv, VALUE self) {
|
|
649
|
-
char *json
|
|
650
|
-
size_t len
|
|
651
|
-
VALUE input
|
|
649
|
+
char *json = 0;
|
|
650
|
+
size_t len = 0;
|
|
651
|
+
VALUE input;
|
|
652
652
|
|
|
653
653
|
if (argc < 2) {
|
|
654
654
|
rb_raise(rb_eArgError, "Wrong number of arguments to saj_parse.\n");
|
|
655
655
|
}
|
|
656
|
+
input = argv[1];
|
|
656
657
|
if (rb_type(input) == T_STRING) {
|
|
657
658
|
// the json string gets modified so make a copy of it
|
|
658
659
|
len = RSTRING_LEN(input) + 1;
|
|
@@ -663,16 +664,20 @@ oj_saj_parse(int argc, VALUE *argv, VALUE self) {
|
|
|
663
664
|
volatile VALUE s;
|
|
664
665
|
|
|
665
666
|
if (oj_stringio_class == clas) {
|
|
666
|
-
s
|
|
667
|
+
s = rb_funcall2(input, oj_string_id, 0, 0);
|
|
668
|
+
StringValueCStr(s);
|
|
667
669
|
len = RSTRING_LEN(s) + 1;
|
|
668
670
|
json = OJ_R_ALLOC_N(char, len);
|
|
669
|
-
strcpy(json,
|
|
671
|
+
strcpy(json, RSTRING_PTR(s));
|
|
670
672
|
#if !IS_WINDOWS
|
|
671
673
|
} else if (rb_cFile == clas && 0 == FIX2INT(rb_funcall(input, oj_pos_id, 0))) {
|
|
672
674
|
int fd = FIX2INT(rb_funcall(input, oj_fileno_id, 0));
|
|
673
675
|
ssize_t cnt;
|
|
674
676
|
|
|
675
677
|
len = lseek(fd, 0, SEEK_END);
|
|
678
|
+
if ((size_t)-1 == len) {
|
|
679
|
+
rb_raise(rb_eIOError, "failed to read from IO Object.");
|
|
680
|
+
}
|
|
676
681
|
lseek(fd, 0, SEEK_SET);
|
|
677
682
|
json = OJ_R_ALLOC_N(char, len + 1);
|
|
678
683
|
{
|
|
@@ -681,6 +686,7 @@ oj_saj_parse(int argc, VALUE *argv, VALUE self) {
|
|
|
681
686
|
while (total < len) {
|
|
682
687
|
cnt = read(fd, json + total, len - total);
|
|
683
688
|
if (cnt <= 0) {
|
|
689
|
+
OJ_R_FREE(json);
|
|
684
690
|
rb_raise(rb_eIOError, "failed to read from IO Object.");
|
|
685
691
|
}
|
|
686
692
|
total += cnt;
|
|
@@ -689,10 +695,11 @@ oj_saj_parse(int argc, VALUE *argv, VALUE self) {
|
|
|
689
695
|
json[len] = '\0';
|
|
690
696
|
#endif
|
|
691
697
|
} else if (rb_respond_to(input, oj_read_id)) {
|
|
692
|
-
s
|
|
698
|
+
s = rb_funcall2(input, oj_read_id, 0, 0);
|
|
699
|
+
StringValueCStr(s);
|
|
693
700
|
len = RSTRING_LEN(s) + 1;
|
|
694
701
|
json = OJ_R_ALLOC_N(char, len);
|
|
695
|
-
strcpy(json,
|
|
702
|
+
strcpy(json, RSTRING_PTR(s));
|
|
696
703
|
} else {
|
|
697
704
|
rb_raise(rb_eArgError, "saj_parse() expected a String or IO Object.");
|
|
698
705
|
}
|