oj 3.9.1 → 3.16.11
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 +1452 -0
- data/README.md +21 -6
- data/RELEASE_NOTES.md +61 -0
- data/ext/oj/buf.h +50 -68
- 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 +9 -36
- data/ext/oj/circarray.c +38 -42
- data/ext/oj/circarray.h +12 -13
- data/ext/oj/code.c +158 -179
- data/ext/oj/code.h +20 -22
- data/ext/oj/compat.c +145 -205
- data/ext/oj/custom.c +740 -880
- data/ext/oj/debug.c +126 -0
- data/ext/oj/dump.c +1145 -844
- data/ext/oj/dump.h +71 -57
- data/ext/oj/dump_compat.c +575 -655
- data/ext/oj/dump_leaf.c +96 -186
- data/ext/oj/dump_object.c +533 -660
- data/ext/oj/dump_strict.c +306 -340
- data/ext/oj/encode.h +4 -33
- data/ext/oj/encoder.c +43 -0
- data/ext/oj/err.c +28 -28
- data/ext/oj/err.h +39 -42
- data/ext/oj/extconf.rb +28 -7
- data/ext/oj/fast.c +1052 -1113
- 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 +471 -430
- data/ext/oj/object.c +532 -580
- data/ext/oj/odd.c +156 -142
- data/ext/oj/odd.h +25 -26
- data/ext/oj/oj.c +1346 -961
- data/ext/oj/oj.h +307 -290
- data/ext/oj/parse.c +954 -858
- data/ext/oj/parse.h +74 -72
- data/ext/oj/parser.c +1600 -0
- data/ext/oj/parser.h +103 -0
- data/ext/oj/rails.c +819 -836
- data/ext/oj/rails.h +8 -11
- data/ext/oj/reader.c +136 -147
- data/ext/oj/reader.h +69 -83
- data/ext/oj/resolve.c +41 -63
- data/ext/oj/resolve.h +4 -6
- data/ext/oj/rxclass.c +69 -72
- data/ext/oj/rxclass.h +12 -13
- data/ext/oj/saj.c +440 -485
- data/ext/oj/saj2.c +584 -0
- data/ext/oj/saj2.h +23 -0
- data/ext/oj/scp.c +79 -118
- data/ext/oj/simd.h +10 -0
- data/ext/oj/sparse.c +739 -709
- data/ext/oj/stream_writer.c +141 -175
- data/ext/oj/strict.c +103 -128
- data/ext/oj/string_writer.c +244 -261
- data/ext/oj/trace.c +34 -41
- data/ext/oj/trace.h +42 -15
- data/ext/oj/usual.c +1218 -0
- data/ext/oj/usual.h +69 -0
- data/ext/oj/util.c +107 -107
- data/ext/oj/util.h +4 -3
- data/ext/oj/val_stack.c +61 -78
- data/ext/oj/val_stack.h +80 -114
- data/ext/oj/validate.c +46 -0
- data/ext/oj/wab.c +316 -361
- 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 +54 -20
- 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 -8
- data/pages/Compatibility.md +1 -1
- data/pages/Encoding.md +1 -1
- data/pages/InstallOptions.md +20 -0
- data/pages/JsonGem.md +15 -0
- data/pages/Modes.md +9 -3
- data/pages/Options.md +62 -12
- data/pages/Parser.md +309 -0
- data/pages/Rails.md +73 -22
- metadata +68 -192
- data/ext/oj/hash.c +0 -163
- data/ext/oj/hash.h +0 -46
- data/ext/oj/hash_test.c +0 -512
- data/test/_test_active.rb +0 -76
- data/test/_test_active_mimic.rb +0 -96
- data/test/_test_mimic_rails.rb +0 -126
- data/test/activerecord/result_test.rb +0 -27
- 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/decoding_test.rb +0 -125
- data/test/activesupport5/encoding_test.rb +0 -485
- data/test/activesupport5/encoding_test_cases.rb +0 -90
- data/test/activesupport5/test_helper.rb +0 -50
- data/test/activesupport5/time_zone_test_helpers.rb +0 -24
- data/test/bar.rb +0 -25
- data/test/files.rb +0 -29
- data/test/foo.rb +0 -21
- data/test/helper.rb +0 -26
- data/test/isolated/shared.rb +0 -308
- data/test/isolated/test_mimic_after.rb +0 -13
- data/test/isolated/test_mimic_alone.rb +0 -12
- data/test/isolated/test_mimic_as_json.rb +0 -45
- data/test/isolated/test_mimic_before.rb +0 -13
- data/test/isolated/test_mimic_define.rb +0 -28
- data/test/isolated/test_mimic_rails_after.rb +0 -22
- data/test/isolated/test_mimic_rails_before.rb +0 -21
- data/test/isolated/test_mimic_redefine.rb +0 -15
- data/test/json_gem/json_addition_test.rb +0 -216
- data/test/json_gem/json_common_interface_test.rb +0 -148
- data/test/json_gem/json_encoding_test.rb +0 -107
- data/test/json_gem/json_ext_parser_test.rb +0 -20
- data/test/json_gem/json_fixtures_test.rb +0 -35
- data/test/json_gem/json_generator_test.rb +0 -383
- data/test/json_gem/json_generic_object_test.rb +0 -90
- data/test/json_gem/json_parser_test.rb +0 -470
- data/test/json_gem/json_string_matching_test.rb +0 -42
- data/test/json_gem/test_helper.rb +0 -18
- data/test/perf.rb +0 -107
- data/test/perf_compat.rb +0 -130
- data/test/perf_fast.rb +0 -164
- data/test/perf_file.rb +0 -64
- data/test/perf_object.rb +0 -138
- data/test/perf_saj.rb +0 -109
- data/test/perf_scp.rb +0 -151
- data/test/perf_simple.rb +0 -287
- data/test/perf_strict.rb +0 -145
- data/test/perf_wab.rb +0 -131
- data/test/sample/change.rb +0 -14
- data/test/sample/dir.rb +0 -19
- data/test/sample/doc.rb +0 -36
- data/test/sample/file.rb +0 -48
- data/test/sample/group.rb +0 -16
- data/test/sample/hasprops.rb +0 -16
- data/test/sample/layer.rb +0 -12
- data/test/sample/line.rb +0 -20
- data/test/sample/oval.rb +0 -10
- data/test/sample/rect.rb +0 -10
- data/test/sample/shape.rb +0 -35
- data/test/sample/text.rb +0 -20
- data/test/sample.rb +0 -54
- data/test/sample_json.rb +0 -37
- data/test/test_compat.rb +0 -509
- data/test/test_custom.rb +0 -503
- data/test/test_debian.rb +0 -53
- data/test/test_fast.rb +0 -470
- data/test/test_file.rb +0 -239
- data/test/test_gc.rb +0 -49
- data/test/test_hash.rb +0 -29
- data/test/test_integer_range.rb +0 -73
- data/test/test_null.rb +0 -376
- data/test/test_object.rb +0 -1018
- data/test/test_saj.rb +0 -186
- data/test/test_scp.rb +0 -433
- data/test/test_strict.rb +0 -410
- data/test/test_various.rb +0 -741
- data/test/test_wab.rb +0 -307
- data/test/test_writer.rb +0 -380
- data/test/tests.rb +0 -24
- data/test/tests_mimic.rb +0 -14
- data/test/tests_mimic_addition.rb +0 -7
- data/test/zoo.rb +0 -13
data/ext/oj/sparse.c
CHANGED
@@ -1,784 +1,808 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
* All rights reserved.
|
4
|
-
*/
|
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.
|
5
3
|
|
6
|
-
#include <
|
4
|
+
#include <math.h>
|
7
5
|
#include <stdio.h>
|
6
|
+
#include <stdlib.h>
|
8
7
|
#include <string.h>
|
9
8
|
#include <unistd.h>
|
10
|
-
#include <math.h>
|
11
9
|
|
12
|
-
#include "
|
10
|
+
#include "buf.h"
|
13
11
|
#include "encode.h"
|
12
|
+
#include "intern.h" // for oj_strndup()
|
13
|
+
#include "mem.h"
|
14
|
+
#include "oj.h"
|
14
15
|
#include "parse.h"
|
15
|
-
#include "buf.h"
|
16
|
-
#include "hash.h" // for oj_strndup()
|
17
16
|
#include "val_stack.h"
|
18
17
|
|
19
18
|
// Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
|
20
|
-
#define OJ_INFINITY
|
19
|
+
#define OJ_INFINITY (1.0 / 0.0)
|
21
20
|
|
22
21
|
#ifdef RUBINIUS_RUBY
|
23
|
-
#define NUM_MAX
|
22
|
+
#define NUM_MAX 0x07FFFFFF
|
24
23
|
#else
|
25
|
-
#define NUM_MAX
|
24
|
+
#define NUM_MAX (FIXNUM_MAX >> 8)
|
26
25
|
#endif
|
27
|
-
#define EXP_MAX
|
28
|
-
#define DEC_MAX
|
26
|
+
#define EXP_MAX 100000
|
27
|
+
#define DEC_MAX 15
|
29
28
|
|
30
|
-
static void
|
31
|
-
|
32
|
-
char c = reader_get(&pi->rd);
|
29
|
+
static void skip_comment(ParseInfo pi) {
|
30
|
+
char c = reader_get(&pi->rd);
|
33
31
|
|
34
32
|
if ('*' == c) {
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
+
}
|
43
41
|
} else if ('/' == c) {
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
}
|
54
|
-
}
|
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
|
+
}
|
55
51
|
} else {
|
56
|
-
|
52
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid comment format");
|
57
53
|
}
|
58
54
|
if ('\0' == c) {
|
59
|
-
|
60
|
-
|
55
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "comment not terminated");
|
56
|
+
return;
|
61
57
|
}
|
62
58
|
}
|
63
59
|
|
64
|
-
static void
|
65
|
-
|
66
|
-
Val parent = stack_peek(&pi->stack);
|
60
|
+
static void add_value(ParseInfo pi, VALUE rval) {
|
61
|
+
Val parent = stack_peek(&pi->stack);
|
67
62
|
|
68
|
-
if (0 == parent) {
|
69
|
-
|
63
|
+
if (0 == parent) { // simple add
|
64
|
+
pi->add_value(pi, rval);
|
70
65
|
} else {
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
+
}
|
96
96
|
}
|
97
97
|
}
|
98
98
|
|
99
|
-
static void
|
100
|
-
|
101
|
-
Val parent = stack_peek(&pi->stack);
|
99
|
+
static void add_num_value(ParseInfo pi, NumInfo ni) {
|
100
|
+
Val parent = stack_peek(&pi->stack);
|
102
101
|
|
103
102
|
if (0 == parent) {
|
104
|
-
|
103
|
+
pi->add_num(pi, ni);
|
105
104
|
} else {
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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
|
+
}
|
125
129
|
}
|
126
130
|
}
|
127
131
|
|
128
|
-
static void
|
129
|
-
read_true(ParseInfo pi) {
|
132
|
+
static void read_true(ParseInfo pi) {
|
130
133
|
if (0 == reader_expect(&pi->rd, "rue")) {
|
131
|
-
|
134
|
+
add_value(pi, Qtrue);
|
132
135
|
} else {
|
133
|
-
|
136
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected true");
|
134
137
|
}
|
135
138
|
}
|
136
139
|
|
137
|
-
static void
|
138
|
-
read_false(ParseInfo pi) {
|
140
|
+
static void read_false(ParseInfo pi) {
|
139
141
|
if (0 == reader_expect(&pi->rd, "alse")) {
|
140
|
-
|
142
|
+
add_value(pi, Qfalse);
|
141
143
|
} else {
|
142
|
-
|
144
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected false");
|
143
145
|
}
|
144
146
|
}
|
145
147
|
|
146
|
-
static uint32_t
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
char c;
|
148
|
+
static uint32_t read_hex(ParseInfo pi) {
|
149
|
+
uint32_t b = 0;
|
150
|
+
int i;
|
151
|
+
char c;
|
151
152
|
|
152
153
|
for (i = 0; i < 4; i++) {
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
+
}
|
165
166
|
}
|
166
167
|
return b;
|
167
168
|
}
|
168
169
|
|
169
|
-
static void
|
170
|
-
unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
|
170
|
+
static void unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
|
171
171
|
if (0x0000007F >= code) {
|
172
|
-
|
172
|
+
buf_append(buf, (char)code);
|
173
173
|
} else if (0x000007FF >= code) {
|
174
|
-
|
175
|
-
|
174
|
+
buf_append(buf, 0xC0 | (code >> 6));
|
175
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
176
176
|
} else if (0x0000FFFF >= code) {
|
177
|
-
|
178
|
-
|
179
|
-
|
177
|
+
buf_append(buf, 0xE0 | (code >> 12));
|
178
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
179
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
180
180
|
} else if (0x001FFFFF >= code) {
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
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));
|
185
185
|
} else if (0x03FFFFFF >= code) {
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
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));
|
191
191
|
} else if (0x7FFFFFFF >= code) {
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
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));
|
198
198
|
} else {
|
199
|
-
|
199
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid Unicode character");
|
200
200
|
}
|
201
201
|
}
|
202
202
|
|
203
203
|
// entered at backslash
|
204
|
-
static void
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
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);
|
210
209
|
|
211
210
|
buf_init(&buf);
|
212
211
|
if (pi->rd.str < pi->rd.tail) {
|
213
|
-
|
212
|
+
buf_append_string(&buf, pi->rd.str, pi->rd.tail - pi->rd.str);
|
214
213
|
}
|
215
214
|
while ('\"' != (c = reader_get(&pi->rd))) {
|
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
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
return;
|
282
|
-
}
|
283
|
-
} else {
|
284
|
-
buf_append(&buf, c);
|
285
|
-
}
|
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
|
+
}
|
286
280
|
}
|
287
281
|
if (0 == parent) {
|
288
|
-
|
282
|
+
pi->add_cstr(pi, buf.head, buf_len(&buf), pi->rd.str);
|
289
283
|
} else {
|
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
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
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
|
+
}
|
327
326
|
}
|
328
327
|
buf_cleanup(&buf);
|
329
328
|
}
|
330
329
|
|
331
|
-
static void
|
332
|
-
|
333
|
-
|
334
|
-
char c;
|
330
|
+
static void read_str(ParseInfo pi) {
|
331
|
+
Val parent = stack_peek(&pi->stack);
|
332
|
+
char c;
|
335
333
|
|
336
334
|
reader_protect(&pi->rd);
|
337
335
|
while ('\"' != (c = reader_get(&pi->rd))) {
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
}
|
348
|
-
if (0 == parent) {
|
349
|
-
|
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);
|
350
348
|
} else {
|
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
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
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
|
+
}
|
390
393
|
}
|
391
394
|
reader_release(&pi->rd);
|
392
395
|
}
|
393
396
|
|
394
|
-
static void
|
395
|
-
|
396
|
-
|
397
|
-
char c;
|
397
|
+
static void read_num(ParseInfo pi) {
|
398
|
+
struct _numInfo ni;
|
399
|
+
char c;
|
398
400
|
|
399
401
|
reader_protect(&pi->rd);
|
400
|
-
ni.i
|
401
|
-
ni.num
|
402
|
-
ni.div
|
403
|
-
ni.di
|
404
|
-
ni.len
|
405
|
-
ni.exp
|
406
|
-
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;
|
407
409
|
ni.infinity = 0;
|
408
|
-
ni.nan
|
409
|
-
ni.neg
|
410
|
-
ni.
|
411
|
-
|
410
|
+
ni.nan = 0;
|
411
|
+
ni.neg = 0;
|
412
|
+
ni.has_exp = 0;
|
413
|
+
if (CompatMode == pi->options.mode) {
|
414
|
+
ni.no_big = !pi->options.compat_bigdec;
|
415
|
+
ni.bigdec_load = pi->options.compat_bigdec;
|
416
|
+
} else {
|
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;
|
420
|
+
}
|
421
|
+
|
412
422
|
c = reader_get(&pi->rd);
|
413
423
|
if ('-' == c) {
|
414
|
-
|
415
|
-
|
424
|
+
c = reader_get(&pi->rd);
|
425
|
+
ni.neg = 1;
|
416
426
|
} else if ('+' == c) {
|
417
|
-
|
427
|
+
c = reader_get(&pi->rd);
|
418
428
|
}
|
419
429
|
if ('I' == c) {
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
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;
|
428
438
|
} else {
|
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
|
-
|
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
|
+
}
|
505
523
|
}
|
506
524
|
ni.str = pi->rd.str;
|
507
525
|
ni.len = pi->rd.tail - pi->rd.str;
|
508
526
|
// Check for special reserved values for Infinity and NaN.
|
509
527
|
if (ni.big) {
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
}
|
519
|
-
if (
|
520
|
-
|
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
|
+
}
|
536
|
+
}
|
537
|
+
if (CompatMode == pi->options.mode) {
|
538
|
+
if (pi->options.compat_bigdec) {
|
539
|
+
ni.big = 1;
|
540
|
+
}
|
541
|
+
} else if (BigDec == pi->options.bigdec_load) {
|
542
|
+
ni.big = 1;
|
521
543
|
}
|
522
544
|
add_num_value(pi, &ni);
|
523
545
|
reader_release(&pi->rd);
|
524
546
|
}
|
525
547
|
|
526
|
-
static void
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
ni.
|
532
|
-
ni.
|
533
|
-
ni.
|
534
|
-
ni.
|
535
|
-
ni.
|
536
|
-
ni.
|
537
|
-
ni.
|
538
|
-
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;
|
539
560
|
ni.infinity = 0;
|
540
|
-
ni.nan
|
541
|
-
ni.neg
|
542
|
-
|
561
|
+
ni.nan = 1;
|
562
|
+
ni.neg = 0;
|
563
|
+
if (CompatMode == pi->options.mode) {
|
564
|
+
ni.no_big = !pi->options.compat_bigdec;
|
565
|
+
ni.bigdec_load = pi->options.compat_bigdec;
|
566
|
+
} else {
|
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;
|
570
|
+
}
|
543
571
|
|
544
|
-
if ('a' != reader_get(&pi->rd) ||
|
545
|
-
|
546
|
-
|
547
|
-
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;
|
548
575
|
}
|
549
|
-
if (
|
550
|
-
|
576
|
+
if (CompatMode == pi->options.mode) {
|
577
|
+
if (pi->options.compat_bigdec) {
|
578
|
+
ni.big = 1;
|
579
|
+
}
|
580
|
+
} else if (BigDec == pi->options.bigdec_load) {
|
581
|
+
ni.big = 1;
|
551
582
|
}
|
552
583
|
add_num_value(pi, &ni);
|
553
584
|
}
|
554
585
|
|
555
|
-
static void
|
556
|
-
|
557
|
-
VALUE v = pi->start_array(pi);
|
586
|
+
static void array_start(ParseInfo pi) {
|
587
|
+
VALUE v = pi->start_array(pi);
|
558
588
|
|
559
589
|
stack_push(&pi->stack, v, NEXT_ARRAY_NEW);
|
560
590
|
}
|
561
591
|
|
562
|
-
static void
|
563
|
-
|
564
|
-
Val array = stack_pop(&pi->stack);
|
592
|
+
static void array_end(ParseInfo pi) {
|
593
|
+
Val array = stack_pop(&pi->stack);
|
565
594
|
|
566
595
|
if (0 == array) {
|
567
|
-
|
596
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected array close");
|
568
597
|
} else if (NEXT_ARRAY_COMMA != array->next && NEXT_ARRAY_NEW != array->next) {
|
569
|
-
|
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));
|
570
604
|
} else {
|
571
|
-
|
572
|
-
|
605
|
+
pi->end_array(pi);
|
606
|
+
add_value(pi, array->val);
|
573
607
|
}
|
574
608
|
}
|
575
609
|
|
576
|
-
static void
|
577
|
-
|
578
|
-
volatile VALUE v = pi->start_hash(pi);
|
610
|
+
static void hash_start(ParseInfo pi) {
|
611
|
+
volatile VALUE v = pi->start_hash(pi);
|
579
612
|
|
580
613
|
stack_push(&pi->stack, v, NEXT_HASH_NEW);
|
581
614
|
}
|
582
615
|
|
583
|
-
static void
|
584
|
-
|
585
|
-
volatile Val hash = stack_peek(&pi->stack);
|
616
|
+
static void hash_end(ParseInfo pi) {
|
617
|
+
volatile Val hash = stack_peek(&pi->stack);
|
586
618
|
|
587
619
|
// leave hash on stack until just before
|
588
620
|
if (0 == hash) {
|
589
|
-
|
621
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected hash close");
|
590
622
|
} else if (NEXT_HASH_COMMA != hash->next && NEXT_HASH_NEW != hash->next) {
|
591
|
-
|
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));
|
592
629
|
} else {
|
593
|
-
|
594
|
-
|
595
|
-
|
630
|
+
pi->end_hash(pi);
|
631
|
+
stack_pop(&pi->stack);
|
632
|
+
add_value(pi, hash->val);
|
596
633
|
}
|
597
634
|
}
|
598
635
|
|
599
|
-
static void
|
600
|
-
|
601
|
-
Val parent = stack_peek(&pi->stack);
|
636
|
+
static void comma(ParseInfo pi) {
|
637
|
+
Val parent = stack_peek(&pi->stack);
|
602
638
|
|
603
639
|
if (0 == parent) {
|
604
|
-
|
640
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
|
605
641
|
} else if (NEXT_ARRAY_COMMA == parent->next) {
|
606
|
-
|
642
|
+
parent->next = NEXT_ARRAY_ELEMENT;
|
607
643
|
} else if (NEXT_HASH_COMMA == parent->next) {
|
608
|
-
|
644
|
+
parent->next = NEXT_HASH_KEY;
|
609
645
|
} else {
|
610
|
-
|
646
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
|
611
647
|
}
|
612
648
|
}
|
613
649
|
|
614
|
-
static void
|
615
|
-
|
616
|
-
Val parent = stack_peek(&pi->stack);
|
650
|
+
static void colon(ParseInfo pi) {
|
651
|
+
Val parent = stack_peek(&pi->stack);
|
617
652
|
|
618
653
|
if (0 != parent && NEXT_HASH_COLON == parent->next) {
|
619
|
-
|
654
|
+
parent->next = NEXT_HASH_VALUE;
|
620
655
|
} else {
|
621
|
-
|
656
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected colon");
|
622
657
|
}
|
623
658
|
}
|
624
659
|
|
625
|
-
void
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
long start = 0;
|
660
|
+
void oj_sparse2(ParseInfo pi) {
|
661
|
+
int first = 1;
|
662
|
+
char c;
|
663
|
+
long start = 0;
|
630
664
|
|
631
665
|
err_init(&pi->err);
|
632
666
|
while (1) {
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
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
|
-
rb_yield_values2(3, args);
|
769
|
-
} else {
|
770
|
-
rb_proc_call_with_block(pi->proc, 3, args, Qnil);
|
771
|
-
}
|
772
|
-
} else if (!pi->has_callbacks) {
|
773
|
-
first = 0;
|
774
|
-
}
|
775
|
-
start = pi->rd.pos;
|
776
|
-
}
|
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
|
+
}
|
777
802
|
}
|
778
803
|
}
|
779
804
|
|
780
|
-
static VALUE
|
781
|
-
protect_parse(VALUE pip) {
|
805
|
+
static VALUE protect_parse(VALUE pip) {
|
782
806
|
oj_sparse2((ParseInfo)pip);
|
783
807
|
|
784
808
|
return Qnil;
|
@@ -786,46 +810,47 @@ protect_parse(VALUE pip) {
|
|
786
810
|
|
787
811
|
VALUE
|
788
812
|
oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd) {
|
789
|
-
volatile VALUE
|
790
|
-
volatile VALUE
|
791
|
-
VALUE
|
792
|
-
int
|
813
|
+
volatile VALUE input;
|
814
|
+
volatile VALUE wrapped_stack;
|
815
|
+
VALUE result = Qnil;
|
816
|
+
int line = 0;
|
793
817
|
|
794
818
|
if (argc < 1) {
|
795
|
-
|
819
|
+
rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
|
796
820
|
}
|
797
821
|
input = argv[0];
|
798
822
|
if (2 <= argc) {
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
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
|
+
}
|
804
828
|
}
|
805
829
|
if (Qnil == input) {
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
} else if (CompatMode == pi->options.mode && T_STRING == rb_type(input) && No == pi->options.nilnil &&
|
812
|
-
|
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.");
|
813
838
|
}
|
814
839
|
if (rb_block_given_p()) {
|
815
|
-
|
840
|
+
pi->proc = Qnil;
|
816
841
|
} else {
|
817
|
-
|
842
|
+
pi->proc = Qundef;
|
818
843
|
}
|
819
844
|
oj_reader_init(&pi->rd, input, fd, CompatMode == pi->options.mode);
|
820
|
-
pi->json = 0;
|
845
|
+
pi->json = 0; // indicates reader is in use
|
821
846
|
|
822
847
|
if (Yes == pi->options.circular) {
|
823
|
-
|
848
|
+
pi->circ_array = oj_circ_array_new();
|
824
849
|
} else {
|
825
|
-
|
850
|
+
pi->circ_array = 0;
|
826
851
|
}
|
827
852
|
if (No == pi->options.allow_gc) {
|
828
|
-
|
853
|
+
rb_gc_disable();
|
829
854
|
}
|
830
855
|
// GC can run at any time. When it runs any Object created by C will be
|
831
856
|
// freed. We protect against this by wrapping the value stack in a ruby
|
@@ -834,77 +859,82 @@ oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd) {
|
|
834
859
|
wrapped_stack = oj_stack_init(&pi->stack);
|
835
860
|
rb_protect(protect_parse, (VALUE)pi, &line);
|
836
861
|
if (Qundef == pi->stack.head->val && !empty_ok(&pi->options)) {
|
837
|
-
|
862
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Empty input");
|
838
863
|
}
|
839
|
-
result
|
864
|
+
result = stack_head_val(&pi->stack);
|
840
865
|
DATA_PTR(wrapped_stack) = 0;
|
841
866
|
if (No == pi->options.allow_gc) {
|
842
|
-
|
867
|
+
rb_gc_enable();
|
843
868
|
}
|
844
869
|
if (!err_has(&pi->err)) {
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
oj_set_error_at(pi, err_class, __FILE__, __LINE__, "not terminated");
|
876
|
-
}
|
877
|
-
}
|
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
|
+
}
|
878
900
|
}
|
879
901
|
CLEANUP:
|
880
902
|
// proceed with cleanup
|
881
903
|
if (0 != pi->circ_array) {
|
882
|
-
|
904
|
+
oj_circ_array_free(pi->circ_array);
|
883
905
|
}
|
884
906
|
stack_cleanup(&pi->stack);
|
885
907
|
if (0 != fd) {
|
886
|
-
|
908
|
+
#ifdef _WIN32
|
909
|
+
rb_w32_close(fd);
|
910
|
+
#else
|
911
|
+
close(fd);
|
912
|
+
#endif
|
887
913
|
}
|
888
914
|
if (err_has(&pi->err)) {
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
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);
|
906
936
|
} else if (0 != line) {
|
907
|
-
|
937
|
+
rb_jump_tag(line);
|
908
938
|
}
|
909
939
|
return result;
|
910
940
|
}
|