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