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