oj 3.11.0 → 3.16.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 +1421 -0
- data/README.md +20 -5
- data/RELEASE_NOTES.md +61 -0
- data/ext/oj/buf.h +48 -38
- data/ext/oj/cache.c +329 -0
- data/ext/oj/cache.h +22 -0
- data/ext/oj/cache8.c +60 -62
- data/ext/oj/cache8.h +8 -7
- data/ext/oj/circarray.c +35 -35
- data/ext/oj/circarray.h +11 -9
- data/ext/oj/code.c +156 -174
- data/ext/oj/code.h +19 -18
- data/ext/oj/compat.c +140 -197
- data/ext/oj/custom.c +737 -879
- data/ext/oj/debug.c +126 -0
- data/ext/oj/dump.c +830 -835
- data/ext/oj/dump.h +65 -53
- data/ext/oj/dump_compat.c +566 -642
- data/ext/oj/dump_leaf.c +95 -182
- data/ext/oj/dump_object.c +518 -659
- data/ext/oj/dump_strict.c +301 -334
- data/ext/oj/encode.h +3 -4
- data/ext/oj/encoder.c +43 -0
- data/ext/oj/err.c +27 -24
- data/ext/oj/err.h +38 -13
- data/ext/oj/extconf.rb +23 -7
- data/ext/oj/fast.c +1043 -1073
- data/ext/oj/intern.c +313 -0
- data/ext/oj/intern.h +22 -0
- data/ext/oj/mem.c +318 -0
- data/ext/oj/mem.h +53 -0
- data/ext/oj/mimic_json.c +449 -423
- data/ext/oj/object.c +530 -576
- data/ext/oj/odd.c +155 -138
- data/ext/oj/odd.h +24 -22
- data/ext/oj/oj.c +1331 -993
- data/ext/oj/oj.h +306 -292
- data/ext/oj/parse.c +934 -938
- data/ext/oj/parse.h +73 -70
- data/ext/oj/parser.c +1600 -0
- data/ext/oj/parser.h +101 -0
- data/ext/oj/rails.c +795 -845
- data/ext/oj/rails.h +7 -7
- data/ext/oj/reader.c +132 -140
- data/ext/oj/reader.h +67 -78
- data/ext/oj/resolve.c +40 -59
- data/ext/oj/resolve.h +3 -2
- data/ext/oj/rxclass.c +67 -67
- data/ext/oj/rxclass.h +11 -9
- data/ext/oj/saj.c +441 -480
- data/ext/oj/saj2.c +584 -0
- data/ext/oj/saj2.h +23 -0
- data/ext/oj/scp.c +78 -111
- data/ext/oj/sparse.c +726 -730
- data/ext/oj/stream_writer.c +146 -165
- data/ext/oj/strict.c +103 -123
- data/ext/oj/string_writer.c +241 -253
- data/ext/oj/trace.c +29 -33
- data/ext/oj/trace.h +41 -11
- data/ext/oj/usual.c +1218 -0
- data/ext/oj/usual.h +69 -0
- data/ext/oj/util.c +103 -103
- data/ext/oj/util.h +3 -2
- data/ext/oj/val_stack.c +60 -49
- data/ext/oj/val_stack.h +79 -85
- data/ext/oj/validate.c +46 -0
- data/ext/oj/wab.c +307 -350
- data/lib/oj/active_support_helper.rb +1 -3
- data/lib/oj/bag.rb +8 -1
- data/lib/oj/easy_hash.rb +9 -9
- data/lib/oj/error.rb +1 -2
- data/lib/oj/json.rb +162 -150
- data/lib/oj/mimic.rb +9 -19
- data/lib/oj/saj.rb +20 -6
- data/lib/oj/schandler.rb +5 -4
- data/lib/oj/state.rb +12 -8
- data/lib/oj/version.rb +1 -2
- data/lib/oj.rb +2 -0
- data/pages/Compatibility.md +1 -1
- data/pages/InstallOptions.md +20 -0
- data/pages/JsonGem.md +15 -0
- data/pages/Modes.md +8 -3
- data/pages/Options.md +43 -5
- data/pages/Parser.md +309 -0
- data/pages/Rails.md +14 -2
- data/test/_test_active.rb +8 -9
- data/test/_test_active_mimic.rb +7 -8
- data/test/_test_mimic_rails.rb +17 -20
- data/test/activerecord/result_test.rb +12 -8
- data/test/activesupport6/encoding_test.rb +63 -28
- data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
- data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
- data/test/{activesupport5 → activesupport7}/encoding_test.rb +86 -50
- data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
- data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
- data/test/files.rb +15 -15
- data/test/foo.rb +17 -43
- data/test/helper.rb +16 -3
- data/test/isolated/shared.rb +3 -2
- data/test/json_gem/json_addition_test.rb +2 -2
- data/test/json_gem/json_common_interface_test.rb +8 -6
- data/test/json_gem/json_encoding_test.rb +0 -0
- data/test/json_gem/json_ext_parser_test.rb +1 -0
- data/test/json_gem/json_fixtures_test.rb +3 -2
- data/test/json_gem/json_generator_test.rb +71 -41
- data/test/json_gem/json_generic_object_test.rb +11 -11
- data/test/json_gem/json_parser_test.rb +54 -47
- data/test/json_gem/json_string_matching_test.rb +9 -9
- data/test/json_gem/test_helper.rb +12 -0
- data/test/mem.rb +34 -0
- data/test/perf.rb +22 -27
- data/test/perf_compat.rb +31 -33
- data/test/perf_dump.rb +50 -0
- data/test/perf_fast.rb +80 -82
- data/test/perf_file.rb +27 -29
- data/test/perf_object.rb +65 -69
- data/test/perf_once.rb +59 -0
- data/test/perf_parser.rb +183 -0
- data/test/perf_saj.rb +46 -54
- data/test/perf_scp.rb +58 -69
- data/test/perf_simple.rb +41 -39
- data/test/perf_strict.rb +74 -82
- data/test/perf_wab.rb +67 -69
- data/test/prec.rb +5 -5
- data/test/sample/change.rb +0 -1
- data/test/sample/dir.rb +0 -1
- data/test/sample/doc.rb +0 -1
- data/test/sample/file.rb +0 -1
- data/test/sample/group.rb +0 -1
- data/test/sample/hasprops.rb +0 -1
- data/test/sample/layer.rb +0 -1
- data/test/sample/rect.rb +0 -1
- data/test/sample/shape.rb +0 -1
- data/test/sample/text.rb +0 -1
- data/test/sample.rb +16 -16
- data/test/sample_json.rb +8 -8
- data/test/test_compat.rb +97 -45
- data/test/test_custom.rb +73 -51
- data/test/test_debian.rb +7 -10
- data/test/test_fast.rb +135 -79
- data/test/test_file.rb +41 -30
- data/test/test_gc.rb +16 -5
- data/test/test_generate.rb +21 -0
- data/test/test_hash.rb +15 -5
- data/test/test_integer_range.rb +9 -9
- data/test/test_null.rb +20 -20
- data/test/test_object.rb +99 -96
- data/test/test_parser.rb +11 -0
- data/test/test_parser_debug.rb +27 -0
- data/test/test_parser_saj.rb +337 -0
- data/test/test_parser_usual.rb +251 -0
- data/test/test_rails.rb +2 -2
- data/test/test_saj.rb +10 -8
- data/test/test_scp.rb +38 -40
- data/test/test_strict.rb +40 -32
- data/test/test_various.rb +165 -84
- data/test/test_wab.rb +48 -44
- data/test/test_writer.rb +47 -47
- data/test/tests.rb +13 -5
- data/test/tests_mimic.rb +12 -3
- data/test/tests_mimic_addition.rb +12 -3
- metadata +75 -127
- data/ext/oj/hash.c +0 -135
- data/ext/oj/hash.h +0 -18
- data/ext/oj/hash_test.c +0 -484
- data/test/activesupport4/decoding_test.rb +0 -108
- data/test/activesupport4/encoding_test.rb +0 -531
- data/test/activesupport4/test_helper.rb +0 -41
- data/test/activesupport5/test_helper.rb +0 -72
- data/test/bar.rb +0 -35
- data/test/baz.rb +0 -16
- data/test/zoo.rb +0 -13
data/ext/oj/sparse.c
CHANGED
@@ -1,814 +1,808 @@
|
|
1
1
|
// Copyright (c) 2013 Peter Ohler. All rights reserved.
|
2
|
+
// Licensed under the MIT License. See LICENSE file in the project root for license details.
|
2
3
|
|
3
|
-
#include <
|
4
|
+
#include <math.h>
|
4
5
|
#include <stdio.h>
|
6
|
+
#include <stdlib.h>
|
5
7
|
#include <string.h>
|
6
8
|
#include <unistd.h>
|
7
|
-
#include <math.h>
|
8
9
|
|
9
|
-
#include "
|
10
|
+
#include "buf.h"
|
10
11
|
#include "encode.h"
|
12
|
+
#include "intern.h" // for oj_strndup()
|
13
|
+
#include "mem.h"
|
14
|
+
#include "oj.h"
|
11
15
|
#include "parse.h"
|
12
|
-
#include "buf.h"
|
13
|
-
#include "hash.h" // for oj_strndup()
|
14
16
|
#include "val_stack.h"
|
15
17
|
|
16
18
|
// Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
|
17
|
-
#define OJ_INFINITY
|
19
|
+
#define OJ_INFINITY (1.0 / 0.0)
|
18
20
|
|
19
21
|
#ifdef RUBINIUS_RUBY
|
20
|
-
#define NUM_MAX
|
22
|
+
#define NUM_MAX 0x07FFFFFF
|
21
23
|
#else
|
22
|
-
#define NUM_MAX
|
24
|
+
#define NUM_MAX (FIXNUM_MAX >> 8)
|
23
25
|
#endif
|
24
|
-
#define EXP_MAX
|
25
|
-
#define DEC_MAX
|
26
|
+
#define EXP_MAX 100000
|
27
|
+
#define DEC_MAX 15
|
26
28
|
|
27
|
-
static void
|
28
|
-
|
29
|
-
char c = reader_get(&pi->rd);
|
29
|
+
static void skip_comment(ParseInfo pi) {
|
30
|
+
char c = reader_get(&pi->rd);
|
30
31
|
|
31
32
|
if ('*' == c) {
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
while ('\0' != (c = reader_get(&pi->rd))) {
|
34
|
+
if ('*' == c) {
|
35
|
+
c = reader_get(&pi->rd);
|
36
|
+
if ('/' == c) {
|
37
|
+
return;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
40
41
|
} else if ('/' == c) {
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
}
|
51
|
-
}
|
42
|
+
while ('\0' != (c = reader_get(&pi->rd))) {
|
43
|
+
switch (c) {
|
44
|
+
case '\n':
|
45
|
+
case '\r':
|
46
|
+
case '\f':
|
47
|
+
case '\0': return;
|
48
|
+
default: break;
|
49
|
+
}
|
50
|
+
}
|
52
51
|
} else {
|
53
|
-
|
52
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid comment format");
|
54
53
|
}
|
55
54
|
if ('\0' == c) {
|
56
|
-
|
57
|
-
|
55
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "comment not terminated");
|
56
|
+
return;
|
58
57
|
}
|
59
58
|
}
|
60
59
|
|
61
|
-
static void
|
62
|
-
|
63
|
-
Val parent = stack_peek(&pi->stack);
|
60
|
+
static void add_value(ParseInfo pi, VALUE rval) {
|
61
|
+
Val parent = stack_peek(&pi->stack);
|
64
62
|
|
65
|
-
if (0 == parent) {
|
66
|
-
|
63
|
+
if (0 == parent) { // simple add
|
64
|
+
pi->add_value(pi, rval);
|
67
65
|
} else {
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
66
|
+
switch (parent->next) {
|
67
|
+
case NEXT_ARRAY_NEW:
|
68
|
+
case NEXT_ARRAY_ELEMENT:
|
69
|
+
pi->array_append_value(pi, rval);
|
70
|
+
parent->next = NEXT_ARRAY_COMMA;
|
71
|
+
break;
|
72
|
+
case NEXT_HASH_VALUE:
|
73
|
+
pi->hash_set_value(pi, parent, rval);
|
74
|
+
if (parent->kalloc) {
|
75
|
+
OJ_R_FREE((char *)parent->key);
|
76
|
+
}
|
77
|
+
parent->key = 0;
|
78
|
+
parent->kalloc = 0;
|
79
|
+
parent->next = NEXT_HASH_COMMA;
|
80
|
+
break;
|
81
|
+
case NEXT_HASH_NEW:
|
82
|
+
case NEXT_HASH_KEY:
|
83
|
+
case NEXT_HASH_COMMA:
|
84
|
+
case NEXT_NONE:
|
85
|
+
case NEXT_ARRAY_COMMA:
|
86
|
+
case NEXT_HASH_COLON:
|
87
|
+
default:
|
88
|
+
oj_set_error_at(pi,
|
89
|
+
oj_parse_error_class,
|
90
|
+
__FILE__,
|
91
|
+
__LINE__,
|
92
|
+
"expected %s",
|
93
|
+
oj_stack_next_string(parent->next));
|
94
|
+
break;
|
95
|
+
}
|
93
96
|
}
|
94
97
|
}
|
95
98
|
|
96
|
-
static void
|
97
|
-
|
98
|
-
Val parent = stack_peek(&pi->stack);
|
99
|
+
static void add_num_value(ParseInfo pi, NumInfo ni) {
|
100
|
+
Val parent = stack_peek(&pi->stack);
|
99
101
|
|
100
102
|
if (0 == parent) {
|
101
|
-
|
103
|
+
pi->add_num(pi, ni);
|
102
104
|
} else {
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
105
|
+
switch (parent->next) {
|
106
|
+
case NEXT_ARRAY_NEW:
|
107
|
+
case NEXT_ARRAY_ELEMENT:
|
108
|
+
pi->array_append_num(pi, ni);
|
109
|
+
parent->next = NEXT_ARRAY_COMMA;
|
110
|
+
break;
|
111
|
+
case NEXT_HASH_VALUE:
|
112
|
+
pi->hash_set_num(pi, parent, ni);
|
113
|
+
if (parent->kalloc) {
|
114
|
+
OJ_R_FREE((char *)parent->key);
|
115
|
+
}
|
116
|
+
parent->key = 0;
|
117
|
+
parent->kalloc = 0;
|
118
|
+
parent->next = NEXT_HASH_COMMA;
|
119
|
+
break;
|
120
|
+
default:
|
121
|
+
oj_set_error_at(pi,
|
122
|
+
oj_parse_error_class,
|
123
|
+
__FILE__,
|
124
|
+
__LINE__,
|
125
|
+
"expected %s",
|
126
|
+
oj_stack_next_string(parent->next));
|
127
|
+
break;
|
128
|
+
}
|
122
129
|
}
|
123
130
|
}
|
124
131
|
|
125
|
-
static void
|
126
|
-
read_true(ParseInfo pi) {
|
132
|
+
static void read_true(ParseInfo pi) {
|
127
133
|
if (0 == reader_expect(&pi->rd, "rue")) {
|
128
|
-
|
134
|
+
add_value(pi, Qtrue);
|
129
135
|
} else {
|
130
|
-
|
136
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected true");
|
131
137
|
}
|
132
138
|
}
|
133
139
|
|
134
|
-
static void
|
135
|
-
read_false(ParseInfo pi) {
|
140
|
+
static void read_false(ParseInfo pi) {
|
136
141
|
if (0 == reader_expect(&pi->rd, "alse")) {
|
137
|
-
|
142
|
+
add_value(pi, Qfalse);
|
138
143
|
} else {
|
139
|
-
|
144
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected false");
|
140
145
|
}
|
141
146
|
}
|
142
147
|
|
143
|
-
static uint32_t
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
char c;
|
148
|
+
static uint32_t read_hex(ParseInfo pi) {
|
149
|
+
uint32_t b = 0;
|
150
|
+
int i;
|
151
|
+
char c;
|
148
152
|
|
149
153
|
for (i = 0; i < 4; i++) {
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
154
|
+
c = reader_get(&pi->rd);
|
155
|
+
b = b << 4;
|
156
|
+
if ('0' <= c && c <= '9') {
|
157
|
+
b += c - '0';
|
158
|
+
} else if ('A' <= c && c <= 'F') {
|
159
|
+
b += c - 'A' + 10;
|
160
|
+
} else if ('a' <= c && c <= 'f') {
|
161
|
+
b += c - 'a' + 10;
|
162
|
+
} else {
|
163
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hex character");
|
164
|
+
return 0;
|
165
|
+
}
|
162
166
|
}
|
163
167
|
return b;
|
164
168
|
}
|
165
169
|
|
166
|
-
static void
|
167
|
-
unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
|
170
|
+
static void unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
|
168
171
|
if (0x0000007F >= code) {
|
169
|
-
|
172
|
+
buf_append(buf, (char)code);
|
170
173
|
} else if (0x000007FF >= code) {
|
171
|
-
|
172
|
-
|
174
|
+
buf_append(buf, 0xC0 | (code >> 6));
|
175
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
173
176
|
} else if (0x0000FFFF >= code) {
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
+
buf_append(buf, 0xE0 | (code >> 12));
|
178
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
179
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
177
180
|
} else if (0x001FFFFF >= code) {
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
181
|
+
buf_append(buf, 0xF0 | (code >> 18));
|
182
|
+
buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
|
183
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
184
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
182
185
|
} else if (0x03FFFFFF >= code) {
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
186
|
+
buf_append(buf, 0xF8 | (code >> 24));
|
187
|
+
buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
|
188
|
+
buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
|
189
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
190
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
188
191
|
} else if (0x7FFFFFFF >= code) {
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
192
|
+
buf_append(buf, 0xFC | (code >> 30));
|
193
|
+
buf_append(buf, 0x80 | ((code >> 24) & 0x3F));
|
194
|
+
buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
|
195
|
+
buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
|
196
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
197
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
195
198
|
} else {
|
196
|
-
|
199
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid Unicode character");
|
197
200
|
}
|
198
201
|
}
|
199
202
|
|
200
203
|
// entered at backslash
|
201
|
-
static void
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
Val parent = stack_peek(&pi->stack);
|
204
|
+
static void read_escaped_str(ParseInfo pi) {
|
205
|
+
struct _buf buf;
|
206
|
+
char c;
|
207
|
+
uint32_t code;
|
208
|
+
Val parent = stack_peek(&pi->stack);
|
207
209
|
|
208
210
|
buf_init(&buf);
|
209
211
|
if (pi->rd.str < pi->rd.tail) {
|
210
|
-
|
212
|
+
buf_append_string(&buf, pi->rd.str, pi->rd.tail - pi->rd.str);
|
211
213
|
}
|
212
214
|
while ('\"' != (c = reader_get(&pi->rd))) {
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
215
|
+
if ('\0' == c) {
|
216
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
|
217
|
+
buf_cleanup(&buf);
|
218
|
+
return;
|
219
|
+
} else if ('\\' == c) {
|
220
|
+
c = reader_get(&pi->rd);
|
221
|
+
switch (c) {
|
222
|
+
case 'n': buf_append(&buf, '\n'); break;
|
223
|
+
case 'r': buf_append(&buf, '\r'); break;
|
224
|
+
case 't': buf_append(&buf, '\t'); break;
|
225
|
+
case 'f': buf_append(&buf, '\f'); break;
|
226
|
+
case 'b': buf_append(&buf, '\b'); break;
|
227
|
+
case '"': buf_append(&buf, '"'); break;
|
228
|
+
case '/': buf_append(&buf, '/'); break;
|
229
|
+
case '\\': buf_append(&buf, '\\'); break;
|
230
|
+
case 'u':
|
231
|
+
if (0 == (code = read_hex(pi)) && err_has(&pi->err)) {
|
232
|
+
buf_cleanup(&buf);
|
233
|
+
return;
|
234
|
+
}
|
235
|
+
if (0x0000D800 <= code && code <= 0x0000DFFF) {
|
236
|
+
uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
|
237
|
+
uint32_t c2;
|
238
|
+
char ch2;
|
239
|
+
|
240
|
+
c = reader_get(&pi->rd);
|
241
|
+
ch2 = reader_get(&pi->rd);
|
242
|
+
if ('\\' != c || 'u' != ch2) {
|
243
|
+
if (Yes == pi->options.allow_invalid) {
|
244
|
+
unicode_to_chars(pi, &buf, code);
|
245
|
+
reader_backup(&pi->rd);
|
246
|
+
reader_backup(&pi->rd);
|
247
|
+
break;
|
248
|
+
}
|
249
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
|
250
|
+
buf_cleanup(&buf);
|
251
|
+
return;
|
252
|
+
}
|
253
|
+
if (0 == (c2 = read_hex(pi)) && err_has(&pi->err)) {
|
254
|
+
buf_cleanup(&buf);
|
255
|
+
return;
|
256
|
+
}
|
257
|
+
c2 = (c2 - 0x0000DC00) & 0x000003FF;
|
258
|
+
code = ((c1 << 10) | c2) + 0x00010000;
|
259
|
+
}
|
260
|
+
unicode_to_chars(pi, &buf, code);
|
261
|
+
if (err_has(&pi->err)) {
|
262
|
+
buf_cleanup(&buf);
|
263
|
+
return;
|
264
|
+
}
|
265
|
+
break;
|
266
|
+
default:
|
267
|
+
// The json gem claims this is not an error despite the
|
268
|
+
// ECMA-404 indicating it is not valid.
|
269
|
+
if (CompatMode == pi->options.mode) {
|
270
|
+
buf_append(&buf, c);
|
271
|
+
break;
|
272
|
+
}
|
273
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
|
274
|
+
buf_cleanup(&buf);
|
275
|
+
return;
|
276
|
+
}
|
277
|
+
} else {
|
278
|
+
buf_append(&buf, c);
|
279
|
+
}
|
278
280
|
}
|
279
281
|
if (0 == parent) {
|
280
|
-
|
282
|
+
pi->add_cstr(pi, buf.head, buf_len(&buf), pi->rd.str);
|
281
283
|
} else {
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
284
|
+
switch (parent->next) {
|
285
|
+
case NEXT_ARRAY_NEW:
|
286
|
+
case NEXT_ARRAY_ELEMENT:
|
287
|
+
pi->array_append_cstr(pi, buf.head, buf_len(&buf), pi->rd.str);
|
288
|
+
parent->next = NEXT_ARRAY_COMMA;
|
289
|
+
break;
|
290
|
+
case NEXT_HASH_NEW:
|
291
|
+
case NEXT_HASH_KEY:
|
292
|
+
if (Qundef == (parent->key_val = pi->hash_key(pi, buf.head, buf_len(&buf)))) {
|
293
|
+
parent->klen = buf_len(&buf);
|
294
|
+
parent->key = malloc(parent->klen + 1);
|
295
|
+
memcpy((char *)parent->key, buf.head, parent->klen);
|
296
|
+
*(char *)(parent->key + parent->klen) = '\0';
|
297
|
+
} else {
|
298
|
+
parent->key = "";
|
299
|
+
parent->klen = 0;
|
300
|
+
}
|
301
|
+
parent->k1 = *pi->rd.str;
|
302
|
+
parent->next = NEXT_HASH_COLON;
|
303
|
+
break;
|
304
|
+
case NEXT_HASH_VALUE:
|
305
|
+
pi->hash_set_cstr(pi, parent, buf.head, buf_len(&buf), pi->rd.str);
|
306
|
+
if (parent->kalloc) {
|
307
|
+
OJ_R_FREE((char *)parent->key);
|
308
|
+
}
|
309
|
+
parent->key = 0;
|
310
|
+
parent->kalloc = 0;
|
311
|
+
parent->next = NEXT_HASH_COMMA;
|
312
|
+
break;
|
313
|
+
case NEXT_HASH_COMMA:
|
314
|
+
case NEXT_NONE:
|
315
|
+
case NEXT_ARRAY_COMMA:
|
316
|
+
case NEXT_HASH_COLON:
|
317
|
+
default:
|
318
|
+
oj_set_error_at(pi,
|
319
|
+
oj_parse_error_class,
|
320
|
+
__FILE__,
|
321
|
+
__LINE__,
|
322
|
+
"expected %s, not a string",
|
323
|
+
oj_stack_next_string(parent->next));
|
324
|
+
break;
|
325
|
+
}
|
319
326
|
}
|
320
327
|
buf_cleanup(&buf);
|
321
328
|
}
|
322
329
|
|
323
|
-
static void
|
324
|
-
|
325
|
-
|
326
|
-
char c;
|
330
|
+
static void read_str(ParseInfo pi) {
|
331
|
+
Val parent = stack_peek(&pi->stack);
|
332
|
+
char c;
|
327
333
|
|
328
334
|
reader_protect(&pi->rd);
|
329
335
|
while ('\"' != (c = reader_get(&pi->rd))) {
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
}
|
340
|
-
if (0 == parent) {
|
341
|
-
|
336
|
+
if ('\0' == c) {
|
337
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
|
338
|
+
return;
|
339
|
+
} else if ('\\' == c) {
|
340
|
+
reader_backup(&pi->rd);
|
341
|
+
read_escaped_str(pi);
|
342
|
+
reader_release(&pi->rd);
|
343
|
+
return;
|
344
|
+
}
|
345
|
+
}
|
346
|
+
if (0 == parent) { // simple add
|
347
|
+
pi->add_cstr(pi, pi->rd.str, pi->rd.tail - pi->rd.str - 1, pi->rd.str);
|
342
348
|
} else {
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
349
|
+
switch (parent->next) {
|
350
|
+
case NEXT_ARRAY_NEW:
|
351
|
+
case NEXT_ARRAY_ELEMENT:
|
352
|
+
pi->array_append_cstr(pi, pi->rd.str, pi->rd.tail - pi->rd.str - 1, pi->rd.str);
|
353
|
+
parent->next = NEXT_ARRAY_COMMA;
|
354
|
+
break;
|
355
|
+
case NEXT_HASH_NEW:
|
356
|
+
case NEXT_HASH_KEY:
|
357
|
+
parent->klen = pi->rd.tail - pi->rd.str - 1;
|
358
|
+
if (sizeof(parent->karray) <= parent->klen) {
|
359
|
+
parent->key = oj_strndup(pi->rd.str, parent->klen);
|
360
|
+
parent->kalloc = 1;
|
361
|
+
} else {
|
362
|
+
memcpy(parent->karray, pi->rd.str, parent->klen);
|
363
|
+
parent->karray[parent->klen] = '\0';
|
364
|
+
parent->key = parent->karray;
|
365
|
+
parent->kalloc = 0;
|
366
|
+
}
|
367
|
+
parent->key_val = pi->hash_key(pi, parent->key, parent->klen);
|
368
|
+
parent->k1 = *pi->rd.str;
|
369
|
+
parent->next = NEXT_HASH_COLON;
|
370
|
+
break;
|
371
|
+
case NEXT_HASH_VALUE:
|
372
|
+
pi->hash_set_cstr(pi, parent, pi->rd.str, pi->rd.tail - pi->rd.str - 1, pi->rd.str);
|
373
|
+
if (parent->kalloc) {
|
374
|
+
OJ_R_FREE((char *)parent->key);
|
375
|
+
}
|
376
|
+
parent->key = 0;
|
377
|
+
parent->kalloc = 0;
|
378
|
+
parent->next = NEXT_HASH_COMMA;
|
379
|
+
break;
|
380
|
+
case NEXT_HASH_COMMA:
|
381
|
+
case NEXT_NONE:
|
382
|
+
case NEXT_ARRAY_COMMA:
|
383
|
+
case NEXT_HASH_COLON:
|
384
|
+
default:
|
385
|
+
oj_set_error_at(pi,
|
386
|
+
oj_parse_error_class,
|
387
|
+
__FILE__,
|
388
|
+
__LINE__,
|
389
|
+
"expected %s, not a string",
|
390
|
+
oj_stack_next_string(parent->next));
|
391
|
+
break;
|
392
|
+
}
|
382
393
|
}
|
383
394
|
reader_release(&pi->rd);
|
384
395
|
}
|
385
396
|
|
386
|
-
static void
|
387
|
-
|
388
|
-
|
389
|
-
char c;
|
397
|
+
static void read_num(ParseInfo pi) {
|
398
|
+
struct _numInfo ni;
|
399
|
+
char c;
|
390
400
|
|
391
401
|
reader_protect(&pi->rd);
|
392
|
-
ni.i
|
393
|
-
ni.num
|
394
|
-
ni.div
|
395
|
-
ni.di
|
396
|
-
ni.len
|
397
|
-
ni.exp
|
398
|
-
ni.big
|
402
|
+
ni.i = 0;
|
403
|
+
ni.num = 0;
|
404
|
+
ni.div = 1;
|
405
|
+
ni.di = 0;
|
406
|
+
ni.len = 0;
|
407
|
+
ni.exp = 0;
|
408
|
+
ni.big = 0;
|
399
409
|
ni.infinity = 0;
|
400
|
-
ni.nan
|
401
|
-
ni.neg
|
402
|
-
ni.has_exp
|
410
|
+
ni.nan = 0;
|
411
|
+
ni.neg = 0;
|
412
|
+
ni.has_exp = 0;
|
403
413
|
if (CompatMode == pi->options.mode) {
|
404
|
-
|
405
|
-
|
414
|
+
ni.no_big = !pi->options.compat_bigdec;
|
415
|
+
ni.bigdec_load = pi->options.compat_bigdec;
|
406
416
|
} else {
|
407
|
-
|
408
|
-
|
417
|
+
ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load ||
|
418
|
+
RubyDec == pi->options.bigdec_load);
|
419
|
+
ni.bigdec_load = pi->options.bigdec_load;
|
409
420
|
}
|
410
421
|
|
411
422
|
c = reader_get(&pi->rd);
|
412
423
|
if ('-' == c) {
|
413
|
-
|
414
|
-
|
424
|
+
c = reader_get(&pi->rd);
|
425
|
+
ni.neg = 1;
|
415
426
|
} else if ('+' == c) {
|
416
|
-
|
427
|
+
c = reader_get(&pi->rd);
|
417
428
|
}
|
418
429
|
if ('I' == c) {
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
430
|
+
if (No == pi->options.allow_nan) {
|
431
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
432
|
+
return;
|
433
|
+
} else if (0 != reader_expect(&pi->rd, "nfinity")) {
|
434
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
435
|
+
return;
|
436
|
+
}
|
437
|
+
ni.infinity = 1;
|
427
438
|
} else {
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
439
|
+
int dec_cnt = 0;
|
440
|
+
bool zero1 = false;
|
441
|
+
|
442
|
+
for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) {
|
443
|
+
if (0 == ni.i && '0' == c) {
|
444
|
+
zero1 = true;
|
445
|
+
}
|
446
|
+
if (0 < ni.i) {
|
447
|
+
dec_cnt++;
|
448
|
+
}
|
449
|
+
if (ni.big) {
|
450
|
+
ni.big++;
|
451
|
+
} else {
|
452
|
+
int d = (c - '0');
|
453
|
+
|
454
|
+
if (0 < d) {
|
455
|
+
if (zero1 && CompatMode == pi->options.mode) {
|
456
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
|
457
|
+
return;
|
458
|
+
}
|
459
|
+
zero1 = false;
|
460
|
+
}
|
461
|
+
ni.i = ni.i * 10 + d;
|
462
|
+
if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
|
463
|
+
ni.big = 1;
|
464
|
+
}
|
465
|
+
}
|
466
|
+
}
|
467
|
+
if ('.' == c) {
|
468
|
+
c = reader_get(&pi->rd);
|
469
|
+
// A trailing . is not a valid decimal but if encountered allow it
|
470
|
+
// except when mimicking the JSON gem.
|
471
|
+
if (CompatMode == pi->options.mode) {
|
472
|
+
if (c < '0' || '9' < c) {
|
473
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
|
474
|
+
}
|
475
|
+
}
|
476
|
+
for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) {
|
477
|
+
int d = (c - '0');
|
478
|
+
|
479
|
+
if (0 < ni.num || 0 < ni.i) {
|
480
|
+
dec_cnt++;
|
481
|
+
}
|
482
|
+
if (INT64_MAX <= ni.div) {
|
483
|
+
if (!ni.no_big) {
|
484
|
+
ni.big = true;
|
485
|
+
}
|
486
|
+
} else {
|
487
|
+
ni.num = ni.num * 10 + d;
|
488
|
+
ni.div *= 10;
|
489
|
+
ni.di++;
|
490
|
+
if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
|
491
|
+
if (!ni.no_big) {
|
492
|
+
ni.big = true;
|
493
|
+
}
|
494
|
+
}
|
495
|
+
}
|
496
|
+
}
|
497
|
+
}
|
498
|
+
if ('e' == c || 'E' == c) {
|
499
|
+
int eneg = 0;
|
500
|
+
|
501
|
+
ni.has_exp = 1;
|
502
|
+
c = reader_get(&pi->rd);
|
503
|
+
if ('-' == c) {
|
504
|
+
c = reader_get(&pi->rd);
|
505
|
+
eneg = 1;
|
506
|
+
} else if ('+' == c) {
|
507
|
+
c = reader_get(&pi->rd);
|
508
|
+
}
|
509
|
+
for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) {
|
510
|
+
ni.exp = ni.exp * 10 + (c - '0');
|
511
|
+
if (EXP_MAX <= ni.exp) {
|
512
|
+
ni.big = 1;
|
513
|
+
}
|
514
|
+
}
|
515
|
+
if (eneg) {
|
516
|
+
ni.exp = -ni.exp;
|
517
|
+
}
|
518
|
+
}
|
519
|
+
ni.len = pi->rd.tail - pi->rd.str;
|
520
|
+
if (0 != c) {
|
521
|
+
reader_backup(&pi->rd);
|
522
|
+
}
|
512
523
|
}
|
513
524
|
ni.str = pi->rd.str;
|
514
525
|
ni.len = pi->rd.tail - pi->rd.str;
|
515
526
|
// Check for special reserved values for Infinity and NaN.
|
516
527
|
if (ni.big) {
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
528
|
+
if (0 == strcasecmp(INF_VAL, ni.str)) {
|
529
|
+
ni.infinity = 1;
|
530
|
+
} else if (0 == strcasecmp(NINF_VAL, ni.str)) {
|
531
|
+
ni.infinity = 1;
|
532
|
+
ni.neg = 1;
|
533
|
+
} else if (0 == strcasecmp(NAN_VAL, ni.str)) {
|
534
|
+
ni.nan = 1;
|
535
|
+
}
|
525
536
|
}
|
526
537
|
if (CompatMode == pi->options.mode) {
|
527
|
-
|
528
|
-
|
529
|
-
|
538
|
+
if (pi->options.compat_bigdec) {
|
539
|
+
ni.big = 1;
|
540
|
+
}
|
530
541
|
} else if (BigDec == pi->options.bigdec_load) {
|
531
|
-
|
542
|
+
ni.big = 1;
|
532
543
|
}
|
533
544
|
add_num_value(pi, &ni);
|
534
545
|
reader_release(&pi->rd);
|
535
546
|
}
|
536
547
|
|
537
|
-
static void
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
ni.
|
543
|
-
ni.
|
544
|
-
ni.
|
545
|
-
ni.
|
546
|
-
ni.
|
547
|
-
ni.
|
548
|
-
ni.
|
549
|
-
ni.big = 0;
|
548
|
+
static void read_nan(ParseInfo pi) {
|
549
|
+
struct _numInfo ni;
|
550
|
+
char c;
|
551
|
+
|
552
|
+
ni.str = pi->rd.str;
|
553
|
+
ni.i = 0;
|
554
|
+
ni.num = 0;
|
555
|
+
ni.div = 1;
|
556
|
+
ni.di = 0;
|
557
|
+
ni.len = 0;
|
558
|
+
ni.exp = 0;
|
559
|
+
ni.big = 0;
|
550
560
|
ni.infinity = 0;
|
551
|
-
ni.nan
|
552
|
-
ni.neg
|
561
|
+
ni.nan = 1;
|
562
|
+
ni.neg = 0;
|
553
563
|
if (CompatMode == pi->options.mode) {
|
554
|
-
|
555
|
-
|
564
|
+
ni.no_big = !pi->options.compat_bigdec;
|
565
|
+
ni.bigdec_load = pi->options.compat_bigdec;
|
556
566
|
} else {
|
557
|
-
|
558
|
-
|
567
|
+
ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load ||
|
568
|
+
RubyDec == pi->options.bigdec_load);
|
569
|
+
ni.bigdec_load = pi->options.bigdec_load;
|
559
570
|
}
|
560
571
|
|
561
|
-
if ('a' != reader_get(&pi->rd) ||
|
562
|
-
|
563
|
-
|
564
|
-
return;
|
572
|
+
if ('a' != reader_get(&pi->rd) || ('N' != (c = reader_get(&pi->rd)) && 'n' != c)) {
|
573
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
574
|
+
return;
|
565
575
|
}
|
566
576
|
if (CompatMode == pi->options.mode) {
|
567
|
-
|
568
|
-
|
569
|
-
|
577
|
+
if (pi->options.compat_bigdec) {
|
578
|
+
ni.big = 1;
|
579
|
+
}
|
570
580
|
} else if (BigDec == pi->options.bigdec_load) {
|
571
|
-
|
581
|
+
ni.big = 1;
|
572
582
|
}
|
573
583
|
add_num_value(pi, &ni);
|
574
584
|
}
|
575
585
|
|
576
|
-
static void
|
577
|
-
|
578
|
-
VALUE v = pi->start_array(pi);
|
586
|
+
static void array_start(ParseInfo pi) {
|
587
|
+
VALUE v = pi->start_array(pi);
|
579
588
|
|
580
589
|
stack_push(&pi->stack, v, NEXT_ARRAY_NEW);
|
581
590
|
}
|
582
591
|
|
583
|
-
static void
|
584
|
-
|
585
|
-
Val array = stack_pop(&pi->stack);
|
592
|
+
static void array_end(ParseInfo pi) {
|
593
|
+
Val array = stack_pop(&pi->stack);
|
586
594
|
|
587
595
|
if (0 == array) {
|
588
|
-
|
596
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected array close");
|
589
597
|
} else if (NEXT_ARRAY_COMMA != array->next && NEXT_ARRAY_NEW != array->next) {
|
590
|
-
|
598
|
+
oj_set_error_at(pi,
|
599
|
+
oj_parse_error_class,
|
600
|
+
__FILE__,
|
601
|
+
__LINE__,
|
602
|
+
"expected %s, not an array close",
|
603
|
+
oj_stack_next_string(array->next));
|
591
604
|
} else {
|
592
|
-
|
593
|
-
|
605
|
+
pi->end_array(pi);
|
606
|
+
add_value(pi, array->val);
|
594
607
|
}
|
595
608
|
}
|
596
609
|
|
597
|
-
static void
|
598
|
-
|
599
|
-
volatile VALUE v = pi->start_hash(pi);
|
610
|
+
static void hash_start(ParseInfo pi) {
|
611
|
+
volatile VALUE v = pi->start_hash(pi);
|
600
612
|
|
601
613
|
stack_push(&pi->stack, v, NEXT_HASH_NEW);
|
602
614
|
}
|
603
615
|
|
604
|
-
static void
|
605
|
-
|
606
|
-
volatile Val hash = stack_peek(&pi->stack);
|
616
|
+
static void hash_end(ParseInfo pi) {
|
617
|
+
volatile Val hash = stack_peek(&pi->stack);
|
607
618
|
|
608
619
|
// leave hash on stack until just before
|
609
620
|
if (0 == hash) {
|
610
|
-
|
621
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected hash close");
|
611
622
|
} else if (NEXT_HASH_COMMA != hash->next && NEXT_HASH_NEW != hash->next) {
|
612
|
-
|
623
|
+
oj_set_error_at(pi,
|
624
|
+
oj_parse_error_class,
|
625
|
+
__FILE__,
|
626
|
+
__LINE__,
|
627
|
+
"expected %s, not a hash close",
|
628
|
+
oj_stack_next_string(hash->next));
|
613
629
|
} else {
|
614
|
-
|
615
|
-
|
616
|
-
|
630
|
+
pi->end_hash(pi);
|
631
|
+
stack_pop(&pi->stack);
|
632
|
+
add_value(pi, hash->val);
|
617
633
|
}
|
618
634
|
}
|
619
635
|
|
620
|
-
static void
|
621
|
-
|
622
|
-
Val parent = stack_peek(&pi->stack);
|
636
|
+
static void comma(ParseInfo pi) {
|
637
|
+
Val parent = stack_peek(&pi->stack);
|
623
638
|
|
624
639
|
if (0 == parent) {
|
625
|
-
|
640
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
|
626
641
|
} else if (NEXT_ARRAY_COMMA == parent->next) {
|
627
|
-
|
642
|
+
parent->next = NEXT_ARRAY_ELEMENT;
|
628
643
|
} else if (NEXT_HASH_COMMA == parent->next) {
|
629
|
-
|
644
|
+
parent->next = NEXT_HASH_KEY;
|
630
645
|
} else {
|
631
|
-
|
646
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
|
632
647
|
}
|
633
648
|
}
|
634
649
|
|
635
|
-
static void
|
636
|
-
|
637
|
-
Val parent = stack_peek(&pi->stack);
|
650
|
+
static void colon(ParseInfo pi) {
|
651
|
+
Val parent = stack_peek(&pi->stack);
|
638
652
|
|
639
653
|
if (0 != parent && NEXT_HASH_COLON == parent->next) {
|
640
|
-
|
654
|
+
parent->next = NEXT_HASH_VALUE;
|
641
655
|
} else {
|
642
|
-
|
656
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected colon");
|
643
657
|
}
|
644
658
|
}
|
645
659
|
|
646
|
-
void
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
long start = 0;
|
660
|
+
void oj_sparse2(ParseInfo pi) {
|
661
|
+
int first = 1;
|
662
|
+
char c;
|
663
|
+
long start = 0;
|
651
664
|
|
652
665
|
err_init(&pi->err);
|
653
666
|
while (1) {
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
VALUE args[3];
|
790
|
-
long len = pi->rd.pos - start;
|
791
|
-
|
792
|
-
*args = stack_head_val(&pi->stack);
|
793
|
-
args[1] = LONG2NUM(start);
|
794
|
-
args[2] = LONG2NUM(len);
|
795
|
-
|
796
|
-
if (Qnil == pi->proc) {
|
797
|
-
rb_yield_values2(3, args);
|
798
|
-
} else {
|
799
|
-
rb_proc_call_with_block(pi->proc, 3, args, Qnil);
|
800
|
-
}
|
801
|
-
} else if (!pi->has_callbacks) {
|
802
|
-
first = 0;
|
803
|
-
}
|
804
|
-
start = pi->rd.pos;
|
805
|
-
// TBD break if option set to allow that
|
806
|
-
}
|
667
|
+
if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
|
668
|
+
VALUE err_clas = oj_get_json_err_class("NestingError");
|
669
|
+
|
670
|
+
oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
|
671
|
+
pi->err_class = err_clas;
|
672
|
+
return;
|
673
|
+
}
|
674
|
+
c = reader_next_non_white(&pi->rd);
|
675
|
+
if (!first && '\0' != c) {
|
676
|
+
oj_set_error_at(pi,
|
677
|
+
oj_parse_error_class,
|
678
|
+
__FILE__,
|
679
|
+
__LINE__,
|
680
|
+
"unexpected characters after the JSON document");
|
681
|
+
}
|
682
|
+
switch (c) {
|
683
|
+
case '{': hash_start(pi); break;
|
684
|
+
case '}': hash_end(pi); break;
|
685
|
+
case ':': colon(pi); break;
|
686
|
+
case '[': array_start(pi); break;
|
687
|
+
case ']': array_end(pi); break;
|
688
|
+
case ',': comma(pi); break;
|
689
|
+
case '"': read_str(pi); break;
|
690
|
+
case '+':
|
691
|
+
if (CompatMode == pi->options.mode) {
|
692
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
693
|
+
return;
|
694
|
+
}
|
695
|
+
pi->cur--;
|
696
|
+
read_num(pi);
|
697
|
+
break;
|
698
|
+
case '-':
|
699
|
+
case '0':
|
700
|
+
case '1':
|
701
|
+
case '2':
|
702
|
+
case '3':
|
703
|
+
case '4':
|
704
|
+
case '5':
|
705
|
+
case '6':
|
706
|
+
case '7':
|
707
|
+
case '8':
|
708
|
+
case '9':
|
709
|
+
reader_backup(&pi->rd);
|
710
|
+
read_num(pi);
|
711
|
+
break;
|
712
|
+
case 'I':
|
713
|
+
if (Yes == pi->options.allow_nan) {
|
714
|
+
reader_backup(&pi->rd);
|
715
|
+
read_num(pi);
|
716
|
+
} else {
|
717
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
718
|
+
return;
|
719
|
+
}
|
720
|
+
break;
|
721
|
+
case 'N':
|
722
|
+
if (Yes == pi->options.allow_nan) {
|
723
|
+
read_nan(pi);
|
724
|
+
} else {
|
725
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
726
|
+
return;
|
727
|
+
}
|
728
|
+
break;
|
729
|
+
case 't': read_true(pi); break;
|
730
|
+
case 'f': read_false(pi); break;
|
731
|
+
case 'n':
|
732
|
+
c = reader_get(&pi->rd);
|
733
|
+
if ('u' == c) {
|
734
|
+
if (0 == reader_expect(&pi->rd, "ll")) {
|
735
|
+
add_value(pi, Qnil);
|
736
|
+
} else {
|
737
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected null");
|
738
|
+
return;
|
739
|
+
}
|
740
|
+
} else if ('a' == c) {
|
741
|
+
struct _numInfo ni;
|
742
|
+
|
743
|
+
c = reader_get(&pi->rd);
|
744
|
+
if ('N' != c && 'n' != c) {
|
745
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected NaN");
|
746
|
+
return;
|
747
|
+
}
|
748
|
+
ni.str = pi->rd.str;
|
749
|
+
ni.i = 0;
|
750
|
+
ni.num = 0;
|
751
|
+
ni.div = 1;
|
752
|
+
ni.di = 0;
|
753
|
+
ni.len = 0;
|
754
|
+
ni.exp = 0;
|
755
|
+
ni.big = 0;
|
756
|
+
ni.infinity = 0;
|
757
|
+
ni.nan = 1;
|
758
|
+
ni.neg = 0;
|
759
|
+
if (CompatMode == pi->options.mode) {
|
760
|
+
ni.no_big = !pi->options.compat_bigdec;
|
761
|
+
ni.bigdec_load = pi->options.compat_bigdec;
|
762
|
+
} else {
|
763
|
+
ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load ||
|
764
|
+
RubyDec == pi->options.bigdec_load);
|
765
|
+
ni.bigdec_load = pi->options.bigdec_load;
|
766
|
+
}
|
767
|
+
add_num_value(pi, &ni);
|
768
|
+
} else {
|
769
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid token");
|
770
|
+
return;
|
771
|
+
}
|
772
|
+
break;
|
773
|
+
case '/': skip_comment(pi); break;
|
774
|
+
case '\0': return;
|
775
|
+
default:
|
776
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character '%c' [0x%02x]", c, c);
|
777
|
+
return;
|
778
|
+
}
|
779
|
+
if (err_has(&pi->err)) {
|
780
|
+
return;
|
781
|
+
}
|
782
|
+
if (stack_empty(&pi->stack)) {
|
783
|
+
if (Qundef != pi->proc) {
|
784
|
+
VALUE args[3];
|
785
|
+
long len = pi->rd.pos - start;
|
786
|
+
|
787
|
+
*args = stack_head_val(&pi->stack);
|
788
|
+
args[1] = LONG2NUM(start);
|
789
|
+
args[2] = LONG2NUM(len);
|
790
|
+
|
791
|
+
if (Qnil == pi->proc) {
|
792
|
+
rb_yield_values2(3, args);
|
793
|
+
} else {
|
794
|
+
rb_proc_call_with_block(pi->proc, 3, args, Qnil);
|
795
|
+
}
|
796
|
+
} else if (!pi->has_callbacks) {
|
797
|
+
first = 0;
|
798
|
+
}
|
799
|
+
start = pi->rd.pos;
|
800
|
+
// TBD break if option set to allow that
|
801
|
+
}
|
807
802
|
}
|
808
803
|
}
|
809
804
|
|
810
|
-
static VALUE
|
811
|
-
protect_parse(VALUE pip) {
|
805
|
+
static VALUE protect_parse(VALUE pip) {
|
812
806
|
oj_sparse2((ParseInfo)pip);
|
813
807
|
|
814
808
|
return Qnil;
|
@@ -816,46 +810,47 @@ protect_parse(VALUE pip) {
|
|
816
810
|
|
817
811
|
VALUE
|
818
812
|
oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd) {
|
819
|
-
volatile VALUE
|
820
|
-
volatile VALUE
|
821
|
-
VALUE
|
822
|
-
int
|
813
|
+
volatile VALUE input;
|
814
|
+
volatile VALUE wrapped_stack;
|
815
|
+
VALUE result = Qnil;
|
816
|
+
int line = 0;
|
823
817
|
|
824
818
|
if (argc < 1) {
|
825
|
-
|
819
|
+
rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
|
826
820
|
}
|
827
821
|
input = argv[0];
|
828
822
|
if (2 <= argc) {
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
823
|
+
if (T_HASH == rb_type(argv[1])) {
|
824
|
+
oj_parse_options(argv[1], &pi->options);
|
825
|
+
} else if (3 <= argc && T_HASH == rb_type(argv[2])) {
|
826
|
+
oj_parse_options(argv[2], &pi->options);
|
827
|
+
}
|
834
828
|
}
|
835
829
|
if (Qnil == input) {
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
} else if (CompatMode == pi->options.mode && T_STRING == rb_type(input) && No == pi->options.nilnil &&
|
842
|
-
|
830
|
+
if (Yes == pi->options.nilnil) {
|
831
|
+
return Qnil;
|
832
|
+
} else {
|
833
|
+
rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
|
834
|
+
}
|
835
|
+
} else if (CompatMode == pi->options.mode && T_STRING == rb_type(input) && No == pi->options.nilnil &&
|
836
|
+
0 == RSTRING_LEN(input)) {
|
837
|
+
rb_raise(oj_json_parser_error_class, "An empty string is not a valid JSON string.");
|
843
838
|
}
|
844
839
|
if (rb_block_given_p()) {
|
845
|
-
|
840
|
+
pi->proc = Qnil;
|
846
841
|
} else {
|
847
|
-
|
842
|
+
pi->proc = Qundef;
|
848
843
|
}
|
849
844
|
oj_reader_init(&pi->rd, input, fd, CompatMode == pi->options.mode);
|
850
|
-
pi->json = 0;
|
845
|
+
pi->json = 0; // indicates reader is in use
|
851
846
|
|
852
847
|
if (Yes == pi->options.circular) {
|
853
|
-
|
848
|
+
pi->circ_array = oj_circ_array_new();
|
854
849
|
} else {
|
855
|
-
|
850
|
+
pi->circ_array = 0;
|
856
851
|
}
|
857
852
|
if (No == pi->options.allow_gc) {
|
858
|
-
|
853
|
+
rb_gc_disable();
|
859
854
|
}
|
860
855
|
// GC can run at any time. When it runs any Object created by C will be
|
861
856
|
// freed. We protect against this by wrapping the value stack in a ruby
|
@@ -864,81 +859,82 @@ oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd) {
|
|
864
859
|
wrapped_stack = oj_stack_init(&pi->stack);
|
865
860
|
rb_protect(protect_parse, (VALUE)pi, &line);
|
866
861
|
if (Qundef == pi->stack.head->val && !empty_ok(&pi->options)) {
|
867
|
-
|
862
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Empty input");
|
868
863
|
}
|
869
|
-
result
|
864
|
+
result = stack_head_val(&pi->stack);
|
870
865
|
DATA_PTR(wrapped_stack) = 0;
|
871
866
|
if (No == pi->options.allow_gc) {
|
872
|
-
|
867
|
+
rb_gc_enable();
|
873
868
|
}
|
874
869
|
if (!err_has(&pi->err)) {
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
oj_set_error_at(pi, err_class, __FILE__, __LINE__, "not terminated");
|
906
|
-
}
|
907
|
-
}
|
870
|
+
// If the stack is not empty then the JSON terminated early.
|
871
|
+
Val v;
|
872
|
+
VALUE err_class = oj_parse_error_class;
|
873
|
+
|
874
|
+
if (0 != line) {
|
875
|
+
VALUE ec = rb_obj_class(rb_errinfo());
|
876
|
+
|
877
|
+
if (rb_eIOError != ec) {
|
878
|
+
goto CLEANUP;
|
879
|
+
}
|
880
|
+
// Sometimes the class of the error is 0 which seems broken.
|
881
|
+
if (rb_eArgError != ec && 0 != ec) {
|
882
|
+
err_class = ec;
|
883
|
+
}
|
884
|
+
}
|
885
|
+
if (0 != (v = stack_peek(&pi->stack))) {
|
886
|
+
switch (v->next) {
|
887
|
+
case NEXT_ARRAY_NEW:
|
888
|
+
case NEXT_ARRAY_ELEMENT:
|
889
|
+
case NEXT_ARRAY_COMMA: oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Array not terminated"); break;
|
890
|
+
case NEXT_HASH_NEW:
|
891
|
+
case NEXT_HASH_KEY:
|
892
|
+
case NEXT_HASH_COLON:
|
893
|
+
case NEXT_HASH_VALUE:
|
894
|
+
case NEXT_HASH_COMMA:
|
895
|
+
oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Hash/Object not terminated");
|
896
|
+
break;
|
897
|
+
default: oj_set_error_at(pi, err_class, __FILE__, __LINE__, "not terminated");
|
898
|
+
}
|
899
|
+
}
|
908
900
|
}
|
909
901
|
CLEANUP:
|
910
902
|
// proceed with cleanup
|
911
903
|
if (0 != pi->circ_array) {
|
912
|
-
|
904
|
+
oj_circ_array_free(pi->circ_array);
|
913
905
|
}
|
914
906
|
stack_cleanup(&pi->stack);
|
915
907
|
if (0 != fd) {
|
916
|
-
|
908
|
+
#ifdef _WIN32
|
909
|
+
rb_w32_close(fd);
|
910
|
+
#else
|
911
|
+
close(fd);
|
912
|
+
#endif
|
917
913
|
}
|
918
914
|
if (err_has(&pi->err)) {
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
915
|
+
rb_set_errinfo(Qnil);
|
916
|
+
if (Qnil != pi->err_class && 0 != pi->err_class) {
|
917
|
+
pi->err.clas = pi->err_class;
|
918
|
+
}
|
919
|
+
if (CompatMode == pi->options.mode && Yes != pi->options.safe) {
|
920
|
+
// The json gem requires the error message be UTF-8 encoded. In
|
921
|
+
// additional the complete JSON source should be returned but that
|
922
|
+
// is not possible without stored all the bytes read and reading
|
923
|
+
// the remaining bytes on the stream. Both seem like a very bad
|
924
|
+
// idea.
|
925
|
+
VALUE args[] = {oj_encode(rb_str_new2(pi->err.msg))};
|
926
|
+
|
927
|
+
if (pi->err.clas == oj_parse_error_class) {
|
928
|
+
// The error was an Oj::ParseError so change to a JSON::ParserError.
|
929
|
+
pi->err.clas = oj_json_parser_error_class;
|
930
|
+
}
|
931
|
+
rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
|
932
|
+
} else {
|
933
|
+
oj_err_raise(&pi->err);
|
934
|
+
}
|
935
|
+
oj_err_raise(&pi->err);
|
940
936
|
} else if (0 != line) {
|
941
|
-
|
937
|
+
rb_jump_tag(line);
|
942
938
|
}
|
943
939
|
return result;
|
944
940
|
}
|