oj 2.18.5 → 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 +5 -5
- data/CHANGELOG.md +1452 -0
- data/README.md +53 -221
- data/RELEASE_NOTES.md +61 -0
- data/ext/oj/buf.h +54 -72
- data/ext/oj/cache.c +329 -0
- data/ext/oj/cache.h +22 -0
- data/ext/oj/cache8.c +61 -63
- data/ext/oj/cache8.h +12 -39
- data/ext/oj/circarray.c +38 -67
- data/ext/oj/circarray.h +16 -42
- data/ext/oj/code.c +214 -0
- data/ext/oj/code.h +40 -0
- data/ext/oj/compat.c +194 -110
- data/ext/oj/custom.c +1074 -0
- data/ext/oj/debug.c +126 -0
- data/ext/oj/dump.c +1276 -2494
- data/ext/oj/dump.h +110 -0
- data/ext/oj/dump_compat.c +897 -0
- data/ext/oj/dump_leaf.c +162 -0
- data/ext/oj/dump_object.c +710 -0
- data/ext/oj/dump_strict.c +399 -0
- data/ext/oj/encode.h +7 -42
- data/ext/oj/encoder.c +43 -0
- data/ext/oj/err.c +28 -53
- data/ext/oj/err.h +49 -46
- data/ext/oj/extconf.rb +33 -32
- data/ext/oj/fast.c +1082 -1098
- 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 +919 -0
- data/ext/oj/object.c +545 -625
- data/ext/oj/odd.c +158 -168
- data/ext/oj/odd.h +32 -58
- data/ext/oj/oj.c +1727 -2080
- data/ext/oj/oj.h +334 -259
- data/ext/oj/parse.c +974 -753
- data/ext/oj/parse.h +97 -90
- data/ext/oj/parser.c +1600 -0
- data/ext/oj/parser.h +103 -0
- data/ext/oj/rails.c +1478 -0
- data/ext/oj/rails.h +18 -0
- data/ext/oj/reader.c +136 -163
- data/ext/oj/reader.h +76 -112
- data/ext/oj/resolve.c +45 -94
- data/ext/oj/resolve.h +7 -34
- data/ext/oj/rxclass.c +144 -0
- data/ext/oj/rxclass.h +26 -0
- data/ext/oj/saj.c +445 -511
- data/ext/oj/saj2.c +584 -0
- data/ext/oj/saj2.h +23 -0
- data/ext/oj/scp.c +82 -143
- data/ext/oj/simd.h +10 -0
- data/ext/oj/sparse.c +749 -644
- data/ext/oj/stream_writer.c +329 -0
- data/ext/oj/strict.c +114 -112
- data/ext/oj/string_writer.c +517 -0
- data/ext/oj/trace.c +72 -0
- data/ext/oj/trace.h +55 -0
- data/ext/oj/usual.c +1218 -0
- data/ext/oj/usual.h +69 -0
- data/ext/oj/util.c +136 -0
- data/ext/oj/util.h +20 -0
- data/ext/oj/val_stack.c +75 -72
- data/ext/oj/val_stack.h +94 -127
- data/ext/oj/validate.c +46 -0
- data/ext/oj/wab.c +586 -0
- data/lib/oj/active_support_helper.rb +1 -3
- data/lib/oj/bag.rb +8 -1
- data/lib/oj/easy_hash.rb +21 -13
- data/lib/oj/error.rb +10 -12
- data/lib/oj/json.rb +188 -0
- data/lib/oj/mimic.rb +165 -26
- data/lib/oj/saj.rb +20 -6
- data/lib/oj/schandler.rb +5 -4
- data/lib/oj/state.rb +135 -0
- data/lib/oj/version.rb +2 -3
- data/lib/oj.rb +3 -31
- 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/InstallOptions.md +20 -0
- data/pages/JsonGem.md +94 -0
- data/pages/Modes.md +161 -0
- data/pages/Options.md +337 -0
- data/pages/Parser.md +309 -0
- data/pages/Rails.md +167 -0
- data/pages/Security.md +20 -0
- data/pages/WAB.md +13 -0
- metadata +126 -163
- 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/activesupport_datetime_test.rb +0 -23
- data/test/bug.rb +0 -51
- data/test/bug2.rb +0 -10
- data/test/bug3.rb +0 -46
- data/test/bug_fast.rb +0 -32
- data/test/bug_load.rb +0 -24
- data/test/crash.rb +0 -111
- data/test/curl/curl_oj.rb +0 -46
- data/test/curl/get_oj.rb +0 -24
- data/test/curl/just_curl.rb +0 -31
- data/test/curl/just_oj.rb +0 -51
- data/test/example.rb +0 -11
- data/test/files.rb +0 -29
- data/test/foo.rb +0 -24
- data/test/helper.rb +0 -27
- data/test/io.rb +0 -48
- data/test/isolated/shared.rb +0 -310
- 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_rails_datetime.rb +0 -27
- data/test/isolated/test_mimic_redefine.rb +0 -15
- data/test/mod.rb +0 -16
- data/test/perf.rb +0 -107
- data/test/perf_compat.rb +0 -128
- 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 -128
- data/test/rails.rb +0 -50
- data/test/russian.rb +0 -18
- 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 -55
- data/test/sample_json.rb +0 -37
- data/test/struct.rb +0 -29
- data/test/test_compat.rb +0 -398
- data/test/test_debian.rb +0 -53
- data/test/test_fast.rb +0 -458
- data/test/test_file.rb +0 -245
- data/test/test_gc.rb +0 -49
- data/test/test_hash.rb +0 -29
- data/test/test_object.rb +0 -745
- data/test/test_saj.rb +0 -186
- data/test/test_scp.rb +0 -396
- data/test/test_serializer.rb +0 -59
- data/test/test_strict.rb +0 -254
- data/test/test_various.rb +0 -1383
- data/test/test_writer.rb +0 -308
- data/test/write_timebars.rb +0 -31
data/ext/oj/sparse.c
CHANGED
@@ -1,747 +1,808 @@
|
|
1
|
-
|
2
|
-
|
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
|
-
*/
|
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.
|
30
3
|
|
31
|
-
#include <
|
4
|
+
#include <math.h>
|
32
5
|
#include <stdio.h>
|
6
|
+
#include <stdlib.h>
|
33
7
|
#include <string.h>
|
34
8
|
#include <unistd.h>
|
35
|
-
#include <math.h>
|
36
9
|
|
10
|
+
#include "buf.h"
|
11
|
+
#include "encode.h"
|
12
|
+
#include "intern.h" // for oj_strndup()
|
13
|
+
#include "mem.h"
|
37
14
|
#include "oj.h"
|
38
15
|
#include "parse.h"
|
39
|
-
#include "buf.h"
|
40
|
-
#include "hash.h" // for oj_strndup()
|
41
16
|
#include "val_stack.h"
|
42
17
|
|
43
18
|
// Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
|
44
|
-
#define OJ_INFINITY
|
19
|
+
#define OJ_INFINITY (1.0 / 0.0)
|
45
20
|
|
46
21
|
#ifdef RUBINIUS_RUBY
|
47
|
-
#define NUM_MAX
|
22
|
+
#define NUM_MAX 0x07FFFFFF
|
48
23
|
#else
|
49
|
-
#define NUM_MAX
|
24
|
+
#define NUM_MAX (FIXNUM_MAX >> 8)
|
50
25
|
#endif
|
51
|
-
#define EXP_MAX
|
52
|
-
#define DEC_MAX
|
26
|
+
#define EXP_MAX 100000
|
27
|
+
#define DEC_MAX 15
|
53
28
|
|
54
|
-
static void
|
55
|
-
|
56
|
-
char c = reader_get(&pi->rd);
|
29
|
+
static void skip_comment(ParseInfo pi) {
|
30
|
+
char c = reader_get(&pi->rd);
|
57
31
|
|
58
32
|
if ('*' == c) {
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
+
}
|
67
41
|
} else if ('/' == c) {
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
}
|
78
|
-
}
|
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
|
+
}
|
79
51
|
} else {
|
80
|
-
|
52
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid comment format");
|
81
53
|
}
|
82
54
|
if ('\0' == c) {
|
83
|
-
|
84
|
-
|
55
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "comment not terminated");
|
56
|
+
return;
|
85
57
|
}
|
86
58
|
}
|
87
59
|
|
88
|
-
static void
|
89
|
-
|
90
|
-
Val parent = stack_peek(&pi->stack);
|
60
|
+
static void add_value(ParseInfo pi, VALUE rval) {
|
61
|
+
Val parent = stack_peek(&pi->stack);
|
91
62
|
|
92
|
-
if (0 == parent) {
|
93
|
-
|
63
|
+
if (0 == parent) { // simple add
|
64
|
+
pi->add_value(pi, rval);
|
94
65
|
} else {
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
+
}
|
120
96
|
}
|
121
97
|
}
|
122
98
|
|
123
|
-
static void
|
124
|
-
|
125
|
-
Val parent = stack_peek(&pi->stack);
|
99
|
+
static void add_num_value(ParseInfo pi, NumInfo ni) {
|
100
|
+
Val parent = stack_peek(&pi->stack);
|
126
101
|
|
127
102
|
if (0 == parent) {
|
128
|
-
|
103
|
+
pi->add_num(pi, ni);
|
129
104
|
} else {
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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
|
+
}
|
149
129
|
}
|
150
130
|
}
|
151
131
|
|
152
|
-
static void
|
153
|
-
read_true(ParseInfo pi) {
|
132
|
+
static void read_true(ParseInfo pi) {
|
154
133
|
if (0 == reader_expect(&pi->rd, "rue")) {
|
155
|
-
|
134
|
+
add_value(pi, Qtrue);
|
156
135
|
} else {
|
157
|
-
|
136
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected true");
|
158
137
|
}
|
159
138
|
}
|
160
139
|
|
161
|
-
static void
|
162
|
-
read_false(ParseInfo pi) {
|
140
|
+
static void read_false(ParseInfo pi) {
|
163
141
|
if (0 == reader_expect(&pi->rd, "alse")) {
|
164
|
-
|
142
|
+
add_value(pi, Qfalse);
|
165
143
|
} else {
|
166
|
-
|
144
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected false");
|
167
145
|
}
|
168
146
|
}
|
169
147
|
|
170
|
-
static uint32_t
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
char c;
|
148
|
+
static uint32_t read_hex(ParseInfo pi) {
|
149
|
+
uint32_t b = 0;
|
150
|
+
int i;
|
151
|
+
char c;
|
175
152
|
|
176
153
|
for (i = 0; i < 4; i++) {
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
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
|
+
}
|
189
166
|
}
|
190
167
|
return b;
|
191
168
|
}
|
192
169
|
|
193
|
-
static void
|
194
|
-
unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
|
170
|
+
static void unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
|
195
171
|
if (0x0000007F >= code) {
|
196
|
-
|
172
|
+
buf_append(buf, (char)code);
|
197
173
|
} else if (0x000007FF >= code) {
|
198
|
-
|
199
|
-
|
174
|
+
buf_append(buf, 0xC0 | (code >> 6));
|
175
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
200
176
|
} else if (0x0000FFFF >= code) {
|
201
|
-
|
202
|
-
|
203
|
-
|
177
|
+
buf_append(buf, 0xE0 | (code >> 12));
|
178
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
179
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
204
180
|
} else if (0x001FFFFF >= code) {
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
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));
|
209
185
|
} else if (0x03FFFFFF >= code) {
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
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));
|
215
191
|
} else if (0x7FFFFFFF >= code) {
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
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));
|
222
198
|
} else {
|
223
|
-
|
199
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid Unicode character");
|
224
200
|
}
|
225
201
|
}
|
226
202
|
|
227
203
|
// entered at backslash
|
228
|
-
static void
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
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);
|
234
209
|
|
235
210
|
buf_init(&buf);
|
236
211
|
if (pi->rd.str < pi->rd.tail) {
|
237
|
-
|
212
|
+
buf_append_string(&buf, pi->rd.str, pi->rd.tail - pi->rd.str);
|
238
213
|
}
|
239
214
|
while ('\"' != (c = reader_get(&pi->rd))) {
|
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
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
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
|
+
}
|
299
280
|
}
|
300
281
|
if (0 == parent) {
|
301
|
-
|
282
|
+
pi->add_cstr(pi, buf.head, buf_len(&buf), pi->rd.str);
|
302
283
|
} else {
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
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
|
+
}
|
338
326
|
}
|
339
327
|
buf_cleanup(&buf);
|
340
328
|
}
|
341
329
|
|
342
|
-
static void
|
343
|
-
|
344
|
-
|
345
|
-
char c;
|
330
|
+
static void read_str(ParseInfo pi) {
|
331
|
+
Val parent = stack_peek(&pi->stack);
|
332
|
+
char c;
|
346
333
|
|
347
334
|
reader_protect(&pi->rd);
|
348
335
|
while ('\"' != (c = reader_get(&pi->rd))) {
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
}
|
359
|
-
if (0 == parent) {
|
360
|
-
|
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);
|
361
348
|
} else {
|
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
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
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
|
+
}
|
401
393
|
}
|
402
394
|
reader_release(&pi->rd);
|
403
395
|
}
|
404
396
|
|
405
|
-
static void
|
406
|
-
|
407
|
-
|
408
|
-
char c;
|
397
|
+
static void read_num(ParseInfo pi) {
|
398
|
+
struct _numInfo ni;
|
399
|
+
char c;
|
409
400
|
|
410
401
|
reader_protect(&pi->rd);
|
411
|
-
ni.i
|
412
|
-
ni.num
|
413
|
-
ni.div
|
414
|
-
ni.di
|
415
|
-
ni.len
|
416
|
-
ni.exp
|
417
|
-
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;
|
418
409
|
ni.infinity = 0;
|
419
|
-
ni.nan
|
420
|
-
ni.neg
|
421
|
-
ni.
|
422
|
-
|
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
|
+
|
423
422
|
c = reader_get(&pi->rd);
|
424
423
|
if ('-' == c) {
|
425
|
-
|
426
|
-
|
424
|
+
c = reader_get(&pi->rd);
|
425
|
+
ni.neg = 1;
|
427
426
|
} else if ('+' == c) {
|
428
|
-
|
427
|
+
c = reader_get(&pi->rd);
|
429
428
|
}
|
430
429
|
if ('I' == c) {
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
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;
|
436
438
|
} else {
|
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
|
-
|
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
|
+
}
|
495
523
|
}
|
496
524
|
ni.str = pi->rd.str;
|
497
525
|
ni.len = pi->rd.tail - pi->rd.str;
|
498
526
|
// Check for special reserved values for Infinity and NaN.
|
499
527
|
if (ni.big) {
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
}
|
509
|
-
if (
|
510
|
-
|
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;
|
511
543
|
}
|
512
544
|
add_num_value(pi, &ni);
|
513
545
|
reader_release(&pi->rd);
|
514
546
|
}
|
515
547
|
|
516
|
-
static void
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
ni.
|
522
|
-
ni.
|
523
|
-
ni.
|
524
|
-
ni.
|
525
|
-
ni.
|
526
|
-
ni.
|
527
|
-
ni.
|
528
|
-
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;
|
529
560
|
ni.infinity = 0;
|
530
|
-
ni.nan
|
531
|
-
ni.neg
|
532
|
-
|
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
|
+
}
|
533
571
|
|
534
|
-
if ('a' != reader_get(&pi->rd) ||
|
535
|
-
|
536
|
-
|
537
|
-
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;
|
538
575
|
}
|
539
|
-
if (
|
540
|
-
|
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;
|
541
582
|
}
|
542
583
|
add_num_value(pi, &ni);
|
543
584
|
}
|
544
585
|
|
545
|
-
static void
|
546
|
-
|
547
|
-
VALUE v = pi->start_array(pi);
|
586
|
+
static void array_start(ParseInfo pi) {
|
587
|
+
VALUE v = pi->start_array(pi);
|
548
588
|
|
549
589
|
stack_push(&pi->stack, v, NEXT_ARRAY_NEW);
|
550
590
|
}
|
551
591
|
|
552
|
-
static void
|
553
|
-
|
554
|
-
Val array = stack_pop(&pi->stack);
|
592
|
+
static void array_end(ParseInfo pi) {
|
593
|
+
Val array = stack_pop(&pi->stack);
|
555
594
|
|
556
595
|
if (0 == array) {
|
557
|
-
|
596
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected array close");
|
558
597
|
} else if (NEXT_ARRAY_COMMA != array->next && NEXT_ARRAY_NEW != array->next) {
|
559
|
-
|
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));
|
560
604
|
} else {
|
561
|
-
|
562
|
-
|
605
|
+
pi->end_array(pi);
|
606
|
+
add_value(pi, array->val);
|
563
607
|
}
|
564
608
|
}
|
565
609
|
|
566
|
-
static void
|
567
|
-
|
568
|
-
volatile VALUE v = pi->start_hash(pi);
|
610
|
+
static void hash_start(ParseInfo pi) {
|
611
|
+
volatile VALUE v = pi->start_hash(pi);
|
569
612
|
|
570
613
|
stack_push(&pi->stack, v, NEXT_HASH_NEW);
|
571
614
|
}
|
572
615
|
|
573
|
-
static void
|
574
|
-
|
575
|
-
volatile Val hash = stack_peek(&pi->stack);
|
616
|
+
static void hash_end(ParseInfo pi) {
|
617
|
+
volatile Val hash = stack_peek(&pi->stack);
|
576
618
|
|
577
619
|
// leave hash on stack until just before
|
578
620
|
if (0 == hash) {
|
579
|
-
|
621
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected hash close");
|
580
622
|
} else if (NEXT_HASH_COMMA != hash->next && NEXT_HASH_NEW != hash->next) {
|
581
|
-
|
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));
|
582
629
|
} else {
|
583
|
-
|
584
|
-
|
585
|
-
|
630
|
+
pi->end_hash(pi);
|
631
|
+
stack_pop(&pi->stack);
|
632
|
+
add_value(pi, hash->val);
|
586
633
|
}
|
587
634
|
}
|
588
635
|
|
589
|
-
static void
|
590
|
-
|
591
|
-
Val parent = stack_peek(&pi->stack);
|
636
|
+
static void comma(ParseInfo pi) {
|
637
|
+
Val parent = stack_peek(&pi->stack);
|
592
638
|
|
593
639
|
if (0 == parent) {
|
594
|
-
|
640
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
|
595
641
|
} else if (NEXT_ARRAY_COMMA == parent->next) {
|
596
|
-
|
642
|
+
parent->next = NEXT_ARRAY_ELEMENT;
|
597
643
|
} else if (NEXT_HASH_COMMA == parent->next) {
|
598
|
-
|
644
|
+
parent->next = NEXT_HASH_KEY;
|
599
645
|
} else {
|
600
|
-
|
646
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
|
601
647
|
}
|
602
648
|
}
|
603
649
|
|
604
|
-
static void
|
605
|
-
|
606
|
-
Val parent = stack_peek(&pi->stack);
|
650
|
+
static void colon(ParseInfo pi) {
|
651
|
+
Val parent = stack_peek(&pi->stack);
|
607
652
|
|
608
653
|
if (0 != parent && NEXT_HASH_COLON == parent->next) {
|
609
|
-
|
654
|
+
parent->next = NEXT_HASH_VALUE;
|
610
655
|
} else {
|
611
|
-
|
656
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected colon");
|
612
657
|
}
|
613
658
|
}
|
614
659
|
|
615
|
-
void
|
616
|
-
|
617
|
-
|
618
|
-
|
660
|
+
void oj_sparse2(ParseInfo pi) {
|
661
|
+
int first = 1;
|
662
|
+
char c;
|
663
|
+
long start = 0;
|
619
664
|
|
620
665
|
err_init(&pi->err);
|
621
666
|
while (1) {
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
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
|
-
|
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
|
+
}
|
740
802
|
}
|
741
803
|
}
|
742
804
|
|
743
|
-
static VALUE
|
744
|
-
protect_parse(VALUE pip) {
|
805
|
+
static VALUE protect_parse(VALUE pip) {
|
745
806
|
oj_sparse2((ParseInfo)pip);
|
746
807
|
|
747
808
|
return Qnil;
|
@@ -749,87 +810,131 @@ protect_parse(VALUE pip) {
|
|
749
810
|
|
750
811
|
VALUE
|
751
812
|
oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd) {
|
752
|
-
volatile VALUE
|
753
|
-
volatile VALUE
|
754
|
-
VALUE
|
755
|
-
int
|
813
|
+
volatile VALUE input;
|
814
|
+
volatile VALUE wrapped_stack;
|
815
|
+
VALUE result = Qnil;
|
816
|
+
int line = 0;
|
756
817
|
|
757
818
|
if (argc < 1) {
|
758
|
-
|
819
|
+
rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
|
759
820
|
}
|
760
821
|
input = argv[0];
|
761
|
-
if (2
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
822
|
+
if (2 <= argc) {
|
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
|
+
}
|
828
|
+
}
|
829
|
+
if (Qnil == input) {
|
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.");
|
766
838
|
}
|
767
839
|
if (rb_block_given_p()) {
|
768
|
-
|
840
|
+
pi->proc = Qnil;
|
769
841
|
} else {
|
770
|
-
|
842
|
+
pi->proc = Qundef;
|
771
843
|
}
|
772
|
-
oj_reader_init(&pi->rd, input, fd);
|
773
|
-
pi->json = 0;
|
844
|
+
oj_reader_init(&pi->rd, input, fd, CompatMode == pi->options.mode);
|
845
|
+
pi->json = 0; // indicates reader is in use
|
774
846
|
|
775
847
|
if (Yes == pi->options.circular) {
|
776
|
-
|
848
|
+
pi->circ_array = oj_circ_array_new();
|
777
849
|
} else {
|
778
|
-
|
850
|
+
pi->circ_array = 0;
|
779
851
|
}
|
780
852
|
if (No == pi->options.allow_gc) {
|
781
|
-
|
853
|
+
rb_gc_disable();
|
782
854
|
}
|
783
855
|
// GC can run at any time. When it runs any Object created by C will be
|
784
856
|
// freed. We protect against this by wrapping the value stack in a ruby
|
785
|
-
// data object and
|
857
|
+
// data object and providing a mark function for ruby objects on the
|
786
858
|
// value stack (while it is in scope).
|
787
859
|
wrapped_stack = oj_stack_init(&pi->stack);
|
788
860
|
rb_protect(protect_parse, (VALUE)pi, &line);
|
789
|
-
|
861
|
+
if (Qundef == pi->stack.head->val && !empty_ok(&pi->options)) {
|
862
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Empty input");
|
863
|
+
}
|
864
|
+
result = stack_head_val(&pi->stack);
|
790
865
|
DATA_PTR(wrapped_stack) = 0;
|
791
866
|
if (No == pi->options.allow_gc) {
|
792
|
-
|
867
|
+
rb_gc_enable();
|
793
868
|
}
|
794
869
|
if (!err_has(&pi->err)) {
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
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
|
+
}
|
900
|
+
}
|
901
|
+
CLEANUP:
|
817
902
|
// proceed with cleanup
|
818
903
|
if (0 != pi->circ_array) {
|
819
|
-
|
904
|
+
oj_circ_array_free(pi->circ_array);
|
820
905
|
}
|
821
906
|
stack_cleanup(&pi->stack);
|
822
907
|
if (0 != fd) {
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
908
|
+
#ifdef _WIN32
|
909
|
+
rb_w32_close(fd);
|
910
|
+
#else
|
911
|
+
close(fd);
|
912
|
+
#endif
|
827
913
|
}
|
828
914
|
if (err_has(&pi->err)) {
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
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);
|
936
|
+
} else if (0 != line) {
|
937
|
+
rb_jump_tag(line);
|
833
938
|
}
|
834
939
|
return result;
|
835
940
|
}
|