oj 3.7.4 → 3.13.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1352 -0
- data/README.md +29 -8
- data/RELEASE_NOTES.md +61 -0
- data/ext/oj/buf.h +53 -72
- data/ext/oj/cache.c +326 -0
- data/ext/oj/cache.h +21 -0
- data/ext/oj/cache8.c +61 -64
- data/ext/oj/cache8.h +12 -39
- data/ext/oj/circarray.c +37 -43
- data/ext/oj/circarray.h +16 -17
- data/ext/oj/code.c +165 -179
- data/ext/oj/code.h +27 -29
- data/ext/oj/compat.c +174 -194
- data/ext/oj/custom.c +809 -866
- data/ext/oj/debug.c +132 -0
- data/ext/oj/dump.c +848 -863
- data/ext/oj/dump.h +81 -67
- data/ext/oj/dump_compat.c +85 -123
- data/ext/oj/dump_leaf.c +100 -188
- data/ext/oj/dump_object.c +527 -656
- data/ext/oj/dump_strict.c +315 -338
- data/ext/oj/encode.h +7 -34
- data/ext/oj/encoder.c +43 -0
- data/ext/oj/err.c +40 -29
- data/ext/oj/err.h +48 -48
- data/ext/oj/extconf.rb +17 -4
- data/ext/oj/fast.c +1070 -1087
- data/ext/oj/intern.c +301 -0
- data/ext/oj/intern.h +26 -0
- data/ext/oj/mimic_json.c +469 -436
- data/ext/oj/object.c +525 -593
- data/ext/oj/odd.c +154 -138
- data/ext/oj/odd.h +37 -38
- data/ext/oj/oj.c +1325 -986
- data/ext/oj/oj.h +333 -316
- data/ext/oj/parse.c +1002 -846
- data/ext/oj/parse.h +92 -87
- data/ext/oj/parser.c +1557 -0
- data/ext/oj/parser.h +91 -0
- data/ext/oj/rails.c +888 -878
- data/ext/oj/rails.h +11 -14
- data/ext/oj/reader.c +141 -147
- data/ext/oj/reader.h +73 -89
- data/ext/oj/resolve.c +41 -62
- data/ext/oj/resolve.h +7 -9
- data/ext/oj/rxclass.c +71 -75
- data/ext/oj/rxclass.h +18 -19
- data/ext/oj/saj.c +443 -486
- data/ext/oj/saj2.c +602 -0
- data/ext/oj/scp.c +88 -113
- data/ext/oj/sparse.c +787 -709
- data/ext/oj/stream_writer.c +133 -159
- data/ext/oj/strict.c +127 -118
- data/ext/oj/string_writer.c +230 -249
- data/ext/oj/trace.c +34 -41
- data/ext/oj/trace.h +19 -19
- data/ext/oj/usual.c +1254 -0
- data/ext/oj/util.c +136 -0
- data/ext/oj/util.h +20 -0
- data/ext/oj/val_stack.c +59 -67
- data/ext/oj/val_stack.h +91 -129
- data/ext/oj/validate.c +46 -0
- data/ext/oj/wab.c +342 -353
- data/lib/oj/bag.rb +1 -0
- data/lib/oj/easy_hash.rb +5 -4
- data/lib/oj/error.rb +1 -1
- data/lib/oj/json.rb +1 -1
- data/lib/oj/mimic.rb +48 -14
- data/lib/oj/saj.rb +20 -6
- data/lib/oj/state.rb +8 -7
- data/lib/oj/version.rb +2 -2
- data/lib/oj.rb +0 -8
- data/pages/Compatibility.md +1 -1
- data/pages/JsonGem.md +15 -0
- data/pages/Modes.md +53 -46
- data/pages/Options.md +72 -11
- data/pages/Parser.md +309 -0
- data/pages/Rails.md +73 -22
- data/pages/Security.md +1 -1
- data/test/activerecord/result_test.rb +7 -2
- data/test/activesupport5/abstract_unit.rb +45 -0
- data/test/activesupport5/decoding_test.rb +68 -60
- data/test/activesupport5/encoding_test.rb +111 -96
- data/test/activesupport5/encoding_test_cases.rb +33 -25
- data/test/activesupport5/test_helper.rb +43 -21
- data/test/activesupport5/time_zone_test_helpers.rb +18 -3
- data/test/activesupport6/abstract_unit.rb +44 -0
- data/test/activesupport6/decoding_test.rb +133 -0
- data/test/activesupport6/encoding_test.rb +507 -0
- data/test/activesupport6/encoding_test_cases.rb +98 -0
- data/test/activesupport6/test_common.rb +17 -0
- data/test/activesupport6/test_helper.rb +163 -0
- data/test/activesupport6/time_zone_test_helpers.rb +39 -0
- data/test/activesupport7/abstract_unit.rb +49 -0
- data/test/activesupport7/decoding_test.rb +125 -0
- data/test/activesupport7/encoding_test.rb +486 -0
- data/test/activesupport7/encoding_test_cases.rb +104 -0
- data/test/activesupport7/time_zone_test_helpers.rb +47 -0
- data/test/bar.rb +6 -12
- data/test/baz.rb +16 -0
- data/test/bug.rb +16 -0
- data/test/foo.rb +69 -75
- data/test/helper.rb +16 -0
- data/test/json_gem/json_common_interface_test.rb +8 -3
- data/test/json_gem/json_generator_test.rb +18 -4
- data/test/json_gem/json_parser_test.rb +9 -0
- data/test/json_gem/test_helper.rb +12 -0
- data/test/mem.rb +33 -0
- data/test/perf.rb +1 -1
- data/test/perf_dump.rb +50 -0
- data/test/perf_once.rb +58 -0
- data/test/perf_parser.rb +189 -0
- data/test/perf_scp.rb +11 -10
- data/test/perf_strict.rb +17 -23
- data/test/prec.rb +23 -0
- data/test/sample_json.rb +1 -1
- data/test/test_compat.rb +46 -10
- data/test/test_custom.rb +147 -8
- data/test/test_fast.rb +62 -2
- data/test/test_file.rb +25 -2
- data/test/test_gc.rb +13 -0
- data/test/test_generate.rb +21 -0
- data/test/test_hash.rb +11 -1
- data/test/test_integer_range.rb +7 -2
- data/test/test_object.rb +85 -9
- data/test/test_parser.rb +27 -0
- data/test/test_parser_saj.rb +335 -0
- data/test/test_parser_usual.rb +217 -0
- data/test/test_rails.rb +35 -0
- data/test/test_saj.rb +1 -1
- data/test/test_scp.rb +5 -5
- data/test/test_strict.rb +26 -1
- data/test/test_various.rb +87 -65
- data/test/test_wab.rb +2 -0
- data/test/test_writer.rb +19 -2
- data/test/tests.rb +1 -1
- data/test/zoo.rb +13 -0
- metadata +60 -110
- data/ext/oj/hash.c +0 -163
- data/ext/oj/hash.h +0 -46
- data/ext/oj/hash_test.c +0 -512
data/ext/oj/parse.c
CHANGED
|
@@ -1,853 +1,1004 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* All rights reserved.
|
|
4
|
-
*/
|
|
1
|
+
// Copyright (c) 2013 Peter Ohler. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file in the project root for license details.
|
|
5
3
|
|
|
6
|
-
#include
|
|
4
|
+
#include "parse.h"
|
|
5
|
+
|
|
6
|
+
#include <math.h>
|
|
7
|
+
#include <ruby/util.h>
|
|
7
8
|
#include <stdio.h>
|
|
9
|
+
#include <stdlib.h>
|
|
8
10
|
#include <string.h>
|
|
9
11
|
#include <unistd.h>
|
|
10
|
-
#include <math.h>
|
|
11
12
|
|
|
12
|
-
#include "oj.h"
|
|
13
|
-
#include "encode.h"
|
|
14
|
-
#include "parse.h"
|
|
15
13
|
#include "buf.h"
|
|
16
|
-
#include "
|
|
14
|
+
#include "encode.h"
|
|
15
|
+
#include "oj.h"
|
|
17
16
|
#include "rxclass.h"
|
|
17
|
+
#include "val_stack.h"
|
|
18
18
|
|
|
19
19
|
// Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
|
|
20
|
-
#define OJ_INFINITY
|
|
20
|
+
#define OJ_INFINITY (1.0 / 0.0)
|
|
21
21
|
|
|
22
22
|
//#define EXP_MAX 1023
|
|
23
|
-
#define EXP_MAX
|
|
24
|
-
#define DEC_MAX
|
|
23
|
+
#define EXP_MAX 100000
|
|
24
|
+
#define DEC_MAX 15
|
|
25
25
|
|
|
26
|
-
static void
|
|
27
|
-
next_non_white(ParseInfo pi) {
|
|
26
|
+
static void next_non_white(ParseInfo pi) {
|
|
28
27
|
for (; 1; pi->cur++) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
28
|
+
switch (*pi->cur) {
|
|
29
|
+
case ' ':
|
|
30
|
+
case '\t':
|
|
31
|
+
case '\f':
|
|
32
|
+
case '\n':
|
|
33
|
+
case '\r': break;
|
|
34
|
+
default: return;
|
|
35
|
+
}
|
|
39
36
|
}
|
|
40
37
|
}
|
|
41
38
|
|
|
42
|
-
static void
|
|
43
|
-
skip_comment(ParseInfo pi) {
|
|
39
|
+
static void skip_comment(ParseInfo pi) {
|
|
44
40
|
if ('*' == *pi->cur) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
41
|
+
pi->cur++;
|
|
42
|
+
for (; pi->cur < pi->end; pi->cur++) {
|
|
43
|
+
if ('*' == *pi->cur && '/' == *(pi->cur + 1)) {
|
|
44
|
+
pi->cur += 2;
|
|
45
|
+
return;
|
|
46
|
+
} else if (pi->end <= pi->cur) {
|
|
47
|
+
oj_set_error_at(pi,
|
|
48
|
+
oj_parse_error_class,
|
|
49
|
+
__FILE__,
|
|
50
|
+
__LINE__,
|
|
51
|
+
"comment not terminated");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
55
|
} else if ('/' == *pi->cur) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
}
|
|
56
|
+
for (; 1; pi->cur++) {
|
|
57
|
+
switch (*pi->cur) {
|
|
58
|
+
case '\n':
|
|
59
|
+
case '\r':
|
|
60
|
+
case '\f':
|
|
61
|
+
case '\0': return;
|
|
62
|
+
default: break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
67
65
|
} else {
|
|
68
|
-
|
|
66
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid comment format");
|
|
69
67
|
}
|
|
70
68
|
}
|
|
71
69
|
|
|
72
|
-
static void
|
|
73
|
-
|
|
74
|
-
Val parent = stack_peek(&pi->stack);
|
|
70
|
+
static void add_value(ParseInfo pi, VALUE rval) {
|
|
71
|
+
Val parent = stack_peek(&pi->stack);
|
|
75
72
|
|
|
76
|
-
if (0 == parent) {
|
|
77
|
-
|
|
73
|
+
if (0 == parent) { // simple add
|
|
74
|
+
pi->add_value(pi, rval);
|
|
78
75
|
} else {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
76
|
+
switch (parent->next) {
|
|
77
|
+
case NEXT_ARRAY_NEW:
|
|
78
|
+
case NEXT_ARRAY_ELEMENT:
|
|
79
|
+
pi->array_append_value(pi, rval);
|
|
80
|
+
parent->next = NEXT_ARRAY_COMMA;
|
|
81
|
+
break;
|
|
82
|
+
case NEXT_HASH_VALUE:
|
|
83
|
+
pi->hash_set_value(pi, parent, rval);
|
|
84
|
+
if (0 != parent->key && 0 < parent->klen &&
|
|
85
|
+
(parent->key < pi->json || pi->cur < parent->key)) {
|
|
86
|
+
xfree((char *)parent->key);
|
|
87
|
+
parent->key = 0;
|
|
88
|
+
}
|
|
89
|
+
parent->next = NEXT_HASH_COMMA;
|
|
90
|
+
break;
|
|
91
|
+
case NEXT_HASH_NEW:
|
|
92
|
+
case NEXT_HASH_KEY:
|
|
93
|
+
case NEXT_HASH_COMMA:
|
|
94
|
+
case NEXT_NONE:
|
|
95
|
+
case NEXT_ARRAY_COMMA:
|
|
96
|
+
case NEXT_HASH_COLON:
|
|
97
|
+
default:
|
|
98
|
+
oj_set_error_at(pi,
|
|
99
|
+
oj_parse_error_class,
|
|
100
|
+
__FILE__,
|
|
101
|
+
__LINE__,
|
|
102
|
+
"expected %s",
|
|
103
|
+
oj_stack_next_string(parent->next));
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
103
106
|
}
|
|
104
107
|
}
|
|
105
108
|
|
|
106
|
-
static void
|
|
107
|
-
read_null(ParseInfo pi) {
|
|
109
|
+
static void read_null(ParseInfo pi) {
|
|
108
110
|
if ('u' == *pi->cur++ && 'l' == *pi->cur++ && 'l' == *pi->cur++) {
|
|
109
|
-
|
|
111
|
+
add_value(pi, Qnil);
|
|
110
112
|
} else {
|
|
111
|
-
|
|
113
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected null");
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
static void
|
|
116
|
-
read_true(ParseInfo pi) {
|
|
117
|
+
static void read_true(ParseInfo pi) {
|
|
117
118
|
if ('r' == *pi->cur++ && 'u' == *pi->cur++ && 'e' == *pi->cur++) {
|
|
118
|
-
|
|
119
|
+
add_value(pi, Qtrue);
|
|
119
120
|
} else {
|
|
120
|
-
|
|
121
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected true");
|
|
121
122
|
}
|
|
122
123
|
}
|
|
123
124
|
|
|
124
|
-
static void
|
|
125
|
-
read_false(ParseInfo pi) {
|
|
125
|
+
static void read_false(ParseInfo pi) {
|
|
126
126
|
if ('a' == *pi->cur++ && 'l' == *pi->cur++ && 's' == *pi->cur++ && 'e' == *pi->cur++) {
|
|
127
|
-
|
|
127
|
+
add_value(pi, Qfalse);
|
|
128
128
|
} else {
|
|
129
|
-
|
|
129
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected false");
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
static uint32_t
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
int i;
|
|
133
|
+
static uint32_t read_hex(ParseInfo pi, const char *h) {
|
|
134
|
+
uint32_t b = 0;
|
|
135
|
+
int i;
|
|
137
136
|
|
|
138
137
|
for (i = 0; i < 4; i++, h++) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
138
|
+
b = b << 4;
|
|
139
|
+
if ('0' <= *h && *h <= '9') {
|
|
140
|
+
b += *h - '0';
|
|
141
|
+
} else if ('A' <= *h && *h <= 'F') {
|
|
142
|
+
b += *h - 'A' + 10;
|
|
143
|
+
} else if ('a' <= *h && *h <= 'f') {
|
|
144
|
+
b += *h - 'a' + 10;
|
|
145
|
+
} else {
|
|
146
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hex character");
|
|
147
|
+
return 0;
|
|
148
|
+
}
|
|
150
149
|
}
|
|
151
150
|
return b;
|
|
152
151
|
}
|
|
153
152
|
|
|
154
|
-
static void
|
|
155
|
-
unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
|
|
153
|
+
static void unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
|
|
156
154
|
if (0x0000007F >= code) {
|
|
157
|
-
|
|
155
|
+
buf_append(buf, (char)code);
|
|
158
156
|
} else if (0x000007FF >= code) {
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
buf_append(buf, 0xC0 | (code >> 6));
|
|
158
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
|
161
159
|
} else if (0x0000FFFF >= code) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
160
|
+
buf_append(buf, 0xE0 | (code >> 12));
|
|
161
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
|
162
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
|
165
163
|
} else if (0x001FFFFF >= code) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
164
|
+
buf_append(buf, 0xF0 | (code >> 18));
|
|
165
|
+
buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
|
|
166
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
|
167
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
|
170
168
|
} else if (0x03FFFFFF >= code) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
169
|
+
buf_append(buf, 0xF8 | (code >> 24));
|
|
170
|
+
buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
|
|
171
|
+
buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
|
|
172
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
|
173
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
|
176
174
|
} else if (0x7FFFFFFF >= code) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
175
|
+
buf_append(buf, 0xFC | (code >> 30));
|
|
176
|
+
buf_append(buf, 0x80 | ((code >> 24) & 0x3F));
|
|
177
|
+
buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
|
|
178
|
+
buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
|
|
179
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
|
180
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
|
183
181
|
} else {
|
|
184
|
-
|
|
182
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid Unicode character");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
static inline const char *scan_string_noSIMD(const char *str, const char *end) {
|
|
187
|
+
for (; '"' != *str; str++) {
|
|
188
|
+
if (end <= str || '\0' == *str || '\\' == *str) {
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
185
191
|
}
|
|
192
|
+
return str;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
#ifdef OJ_USE_SSE4_2
|
|
196
|
+
static inline const char *scan_string_SIMD(const char *str, const char *end) {
|
|
197
|
+
static const char chars[16] = "\x00\\\"";
|
|
198
|
+
const __m128i terminate = _mm_loadu_si128((const __m128i *)&chars[0]);
|
|
199
|
+
const char *_end = (const char *)(end - 16);
|
|
200
|
+
|
|
201
|
+
for (; str <= _end; str += 16) {
|
|
202
|
+
const __m128i string = _mm_loadu_si128((const __m128i *)str);
|
|
203
|
+
const int r = _mm_cmpestri(terminate, 3, string, 16, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT);
|
|
204
|
+
if (r != 16) {
|
|
205
|
+
str = (char*)(str + r);
|
|
206
|
+
return str;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return scan_string_noSIMD(str, end);
|
|
211
|
+
}
|
|
212
|
+
#endif
|
|
213
|
+
|
|
214
|
+
static const char *(*scan_func) (const char *str, const char *end) = scan_string_noSIMD;
|
|
215
|
+
|
|
216
|
+
void oj_scanner_init(void) {
|
|
217
|
+
#ifdef OJ_USE_SSE4_2
|
|
218
|
+
scan_func = scan_string_SIMD;
|
|
219
|
+
#endif
|
|
186
220
|
}
|
|
187
221
|
|
|
188
222
|
// entered at /
|
|
189
|
-
static void
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
Val parent = stack_peek(&pi->stack);
|
|
223
|
+
static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
224
|
+
struct _buf buf;
|
|
225
|
+
const char *s;
|
|
226
|
+
int cnt = (int)(pi->cur - start);
|
|
227
|
+
uint32_t code;
|
|
228
|
+
Val parent = stack_peek(&pi->stack);
|
|
196
229
|
|
|
197
230
|
buf_init(&buf);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
231
|
+
buf_append_string(&buf, start, cnt);
|
|
232
|
+
|
|
233
|
+
for (s = pi->cur; '"' != *s;) {
|
|
234
|
+
const char *scanned = scan_func(s, pi->end);
|
|
235
|
+
if (scanned >= pi->end) {
|
|
236
|
+
oj_set_error_at(pi,
|
|
237
|
+
oj_parse_error_class,
|
|
238
|
+
__FILE__,
|
|
239
|
+
__LINE__,
|
|
240
|
+
"quoted string not terminated");
|
|
241
|
+
buf_cleanup(&buf);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
buf_append_string(&buf, s, (size_t)(scanned - s));
|
|
246
|
+
s = scanned;
|
|
247
|
+
|
|
248
|
+
if ('\\' == *s) {
|
|
249
|
+
s++;
|
|
250
|
+
switch (*s) {
|
|
251
|
+
case 'n': buf_append(&buf, '\n'); break;
|
|
252
|
+
case 'r': buf_append(&buf, '\r'); break;
|
|
253
|
+
case 't': buf_append(&buf, '\t'); break;
|
|
254
|
+
case 'f': buf_append(&buf, '\f'); break;
|
|
255
|
+
case 'b': buf_append(&buf, '\b'); break;
|
|
256
|
+
case '"': buf_append(&buf, '"'); break;
|
|
257
|
+
case '/': buf_append(&buf, '/'); break;
|
|
258
|
+
case '\\': buf_append(&buf, '\\'); break;
|
|
259
|
+
case 'u':
|
|
260
|
+
s++;
|
|
261
|
+
if (0 == (code = read_hex(pi, s)) && err_has(&pi->err)) {
|
|
262
|
+
buf_cleanup(&buf);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
s += 3;
|
|
266
|
+
if (0x0000D800 <= code && code <= 0x0000DFFF) {
|
|
267
|
+
uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
|
|
268
|
+
uint32_t c2;
|
|
269
|
+
|
|
270
|
+
s++;
|
|
271
|
+
if ('\\' != *s || 'u' != *(s + 1)) {
|
|
272
|
+
if (Yes == pi->options.allow_invalid) {
|
|
273
|
+
s--;
|
|
274
|
+
unicode_to_chars(pi, &buf, code);
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
pi->cur = s;
|
|
278
|
+
oj_set_error_at(pi,
|
|
279
|
+
oj_parse_error_class,
|
|
280
|
+
__FILE__,
|
|
281
|
+
__LINE__,
|
|
282
|
+
"invalid escaped character");
|
|
283
|
+
buf_cleanup(&buf);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
s += 2;
|
|
287
|
+
if (0 == (c2 = read_hex(pi, s)) && err_has(&pi->err)) {
|
|
288
|
+
buf_cleanup(&buf);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
s += 3;
|
|
292
|
+
c2 = (c2 - 0x0000DC00) & 0x000003FF;
|
|
293
|
+
code = ((c1 << 10) | c2) + 0x00010000;
|
|
294
|
+
}
|
|
295
|
+
unicode_to_chars(pi, &buf, code);
|
|
296
|
+
if (err_has(&pi->err)) {
|
|
297
|
+
buf_cleanup(&buf);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
default:
|
|
302
|
+
// The json gem claims this is not an error despite the
|
|
303
|
+
// ECMA-404 indicating it is not valid.
|
|
304
|
+
if (CompatMode == pi->options.mode) {
|
|
305
|
+
buf_append(&buf, *s);
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
pi->cur = s;
|
|
309
|
+
oj_set_error_at(pi,
|
|
310
|
+
oj_parse_error_class,
|
|
311
|
+
__FILE__,
|
|
312
|
+
__LINE__,
|
|
313
|
+
"invalid escaped character");
|
|
314
|
+
buf_cleanup(&buf);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
s++;
|
|
318
|
+
}
|
|
276
319
|
}
|
|
277
320
|
if (0 == parent) {
|
|
278
|
-
|
|
321
|
+
pi->add_cstr(pi, buf.head, buf_len(&buf), start);
|
|
279
322
|
} else {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
323
|
+
switch (parent->next) {
|
|
324
|
+
case NEXT_ARRAY_NEW:
|
|
325
|
+
case NEXT_ARRAY_ELEMENT:
|
|
326
|
+
pi->array_append_cstr(pi, buf.head, buf_len(&buf), start);
|
|
327
|
+
parent->next = NEXT_ARRAY_COMMA;
|
|
328
|
+
break;
|
|
329
|
+
case NEXT_HASH_NEW:
|
|
330
|
+
case NEXT_HASH_KEY:
|
|
331
|
+
if (Qundef == (parent->key_val = pi->hash_key(pi, buf.head, buf_len(&buf)))) {
|
|
332
|
+
parent->klen = buf_len(&buf);
|
|
333
|
+
parent->key = malloc(parent->klen + 1);
|
|
334
|
+
memcpy((char *)parent->key, buf.head, parent->klen);
|
|
335
|
+
*(char *)(parent->key + parent->klen) = '\0';
|
|
336
|
+
} else {
|
|
337
|
+
parent->key = "";
|
|
338
|
+
parent->klen = 0;
|
|
339
|
+
}
|
|
340
|
+
parent->k1 = *start;
|
|
341
|
+
parent->next = NEXT_HASH_COLON;
|
|
342
|
+
break;
|
|
343
|
+
case NEXT_HASH_VALUE:
|
|
344
|
+
pi->hash_set_cstr(pi, parent, buf.head, buf_len(&buf), start);
|
|
345
|
+
if (0 != parent->key && 0 < parent->klen &&
|
|
346
|
+
(parent->key < pi->json || pi->cur < parent->key)) {
|
|
347
|
+
xfree((char *)parent->key);
|
|
348
|
+
parent->key = 0;
|
|
349
|
+
}
|
|
350
|
+
parent->next = NEXT_HASH_COMMA;
|
|
351
|
+
break;
|
|
352
|
+
case NEXT_HASH_COMMA:
|
|
353
|
+
case NEXT_NONE:
|
|
354
|
+
case NEXT_ARRAY_COMMA:
|
|
355
|
+
case NEXT_HASH_COLON:
|
|
356
|
+
default:
|
|
357
|
+
oj_set_error_at(pi,
|
|
358
|
+
oj_parse_error_class,
|
|
359
|
+
__FILE__,
|
|
360
|
+
__LINE__,
|
|
361
|
+
"expected %s, not a string",
|
|
362
|
+
oj_stack_next_string(parent->next));
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
316
365
|
}
|
|
317
366
|
pi->cur = s + 1;
|
|
318
367
|
buf_cleanup(&buf);
|
|
319
368
|
}
|
|
320
369
|
|
|
321
|
-
static void
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
370
|
+
static void read_str(ParseInfo pi) {
|
|
371
|
+
const char *str = pi->cur;
|
|
372
|
+
Val parent = stack_peek(&pi->stack);
|
|
373
|
+
|
|
374
|
+
pi->cur = scan_func(pi->cur, pi->end);
|
|
375
|
+
if (RB_UNLIKELY(pi->end <= pi->cur)) {
|
|
376
|
+
oj_set_error_at(pi,
|
|
377
|
+
oj_parse_error_class,
|
|
378
|
+
__FILE__,
|
|
379
|
+
__LINE__,
|
|
380
|
+
"quoted string not terminated");
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
if (RB_UNLIKELY('\0' == *pi->cur)) {
|
|
384
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "NULL byte in string");
|
|
385
|
+
return;
|
|
337
386
|
}
|
|
338
|
-
if (
|
|
339
|
-
|
|
387
|
+
if ('\\' == *pi->cur) {
|
|
388
|
+
read_escaped_str(pi, str);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (0 == parent) { // simple add
|
|
393
|
+
pi->add_cstr(pi, str, pi->cur - str, str);
|
|
340
394
|
} else {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
395
|
+
switch (parent->next) {
|
|
396
|
+
case NEXT_ARRAY_NEW:
|
|
397
|
+
case NEXT_ARRAY_ELEMENT:
|
|
398
|
+
pi->array_append_cstr(pi, str, pi->cur - str, str);
|
|
399
|
+
parent->next = NEXT_ARRAY_COMMA;
|
|
400
|
+
break;
|
|
401
|
+
case NEXT_HASH_NEW:
|
|
402
|
+
case NEXT_HASH_KEY:
|
|
403
|
+
if (Qundef == (parent->key_val = pi->hash_key(pi, str, pi->cur - str))) {
|
|
404
|
+
parent->key = str;
|
|
405
|
+
parent->klen = pi->cur - str;
|
|
406
|
+
} else {
|
|
407
|
+
parent->key = "";
|
|
408
|
+
parent->klen = 0;
|
|
409
|
+
}
|
|
410
|
+
parent->k1 = *str;
|
|
411
|
+
parent->next = NEXT_HASH_COLON;
|
|
412
|
+
break;
|
|
413
|
+
case NEXT_HASH_VALUE:
|
|
414
|
+
pi->hash_set_cstr(pi, parent, str, pi->cur - str, str);
|
|
415
|
+
if (0 != parent->key && 0 < parent->klen &&
|
|
416
|
+
(parent->key < pi->json || pi->cur < parent->key)) {
|
|
417
|
+
xfree((char *)parent->key);
|
|
418
|
+
parent->key = 0;
|
|
419
|
+
}
|
|
420
|
+
parent->next = NEXT_HASH_COMMA;
|
|
421
|
+
break;
|
|
422
|
+
case NEXT_HASH_COMMA:
|
|
423
|
+
case NEXT_NONE:
|
|
424
|
+
case NEXT_ARRAY_COMMA:
|
|
425
|
+
case NEXT_HASH_COLON:
|
|
426
|
+
default:
|
|
427
|
+
oj_set_error_at(pi,
|
|
428
|
+
oj_parse_error_class,
|
|
429
|
+
__FILE__,
|
|
430
|
+
__LINE__,
|
|
431
|
+
"expected %s, not a string",
|
|
432
|
+
oj_stack_next_string(parent->next));
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
375
435
|
}
|
|
376
|
-
pi->cur++;
|
|
436
|
+
pi->cur++; // move past "
|
|
377
437
|
}
|
|
378
438
|
|
|
379
|
-
static void
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
ni.
|
|
385
|
-
ni.
|
|
386
|
-
ni.
|
|
387
|
-
ni.
|
|
388
|
-
ni.
|
|
389
|
-
ni.
|
|
390
|
-
ni.
|
|
391
|
-
ni.big = 0;
|
|
439
|
+
static void read_num(ParseInfo pi) {
|
|
440
|
+
struct _numInfo ni;
|
|
441
|
+
Val parent = stack_peek(&pi->stack);
|
|
442
|
+
|
|
443
|
+
ni.str = pi->cur;
|
|
444
|
+
ni.i = 0;
|
|
445
|
+
ni.num = 0;
|
|
446
|
+
ni.div = 1;
|
|
447
|
+
ni.di = 0;
|
|
448
|
+
ni.len = 0;
|
|
449
|
+
ni.exp = 0;
|
|
450
|
+
ni.big = 0;
|
|
392
451
|
ni.infinity = 0;
|
|
393
|
-
ni.nan
|
|
394
|
-
ni.neg
|
|
395
|
-
ni.
|
|
396
|
-
|
|
452
|
+
ni.nan = 0;
|
|
453
|
+
ni.neg = 0;
|
|
454
|
+
ni.has_exp = 0;
|
|
455
|
+
if (CompatMode == pi->options.mode) {
|
|
456
|
+
ni.no_big = !pi->options.compat_bigdec;
|
|
457
|
+
ni.bigdec_load = pi->options.compat_bigdec;
|
|
458
|
+
} else {
|
|
459
|
+
ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load ||
|
|
460
|
+
RubyDec == pi->options.bigdec_load);
|
|
461
|
+
ni.bigdec_load = pi->options.bigdec_load;
|
|
462
|
+
}
|
|
397
463
|
|
|
398
464
|
if ('-' == *pi->cur) {
|
|
399
|
-
|
|
400
|
-
|
|
465
|
+
pi->cur++;
|
|
466
|
+
ni.neg = 1;
|
|
401
467
|
} else if ('+' == *pi->cur) {
|
|
402
|
-
|
|
468
|
+
if (StrictMode == pi->options.mode) {
|
|
469
|
+
oj_set_error_at(pi,
|
|
470
|
+
oj_parse_error_class,
|
|
471
|
+
__FILE__,
|
|
472
|
+
__LINE__,
|
|
473
|
+
"not a number or other value");
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
pi->cur++;
|
|
403
477
|
}
|
|
404
478
|
if ('I' == *pi->cur) {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
479
|
+
if (No == pi->options.allow_nan || 0 != strncmp("Infinity", pi->cur, 8)) {
|
|
480
|
+
oj_set_error_at(pi,
|
|
481
|
+
oj_parse_error_class,
|
|
482
|
+
__FILE__,
|
|
483
|
+
__LINE__,
|
|
484
|
+
"not a number or other value");
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
pi->cur += 8;
|
|
488
|
+
ni.infinity = 1;
|
|
411
489
|
} else if ('N' == *pi->cur || 'n' == *pi->cur) {
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
490
|
+
if ('a' != pi->cur[1] || ('N' != pi->cur[2] && 'n' != pi->cur[2])) {
|
|
491
|
+
oj_set_error_at(pi,
|
|
492
|
+
oj_parse_error_class,
|
|
493
|
+
__FILE__,
|
|
494
|
+
__LINE__,
|
|
495
|
+
"not a number or other value");
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
pi->cur += 3;
|
|
499
|
+
ni.nan = 1;
|
|
418
500
|
} else {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
501
|
+
int dec_cnt = 0;
|
|
502
|
+
bool zero1 = false;
|
|
503
|
+
|
|
504
|
+
// Skip leading zeros.
|
|
505
|
+
for (; '0' == *pi->cur; pi->cur++) {
|
|
506
|
+
zero1 = true;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
|
|
510
|
+
int d = (*pi->cur - '0');
|
|
511
|
+
|
|
512
|
+
if (RB_LIKELY(0 != ni.i)) {
|
|
513
|
+
dec_cnt++;
|
|
514
|
+
}
|
|
515
|
+
ni.i = ni.i * 10 + d;
|
|
516
|
+
}
|
|
517
|
+
if (RB_UNLIKELY(0 != ni.i && zero1 && CompatMode == pi->options.mode)) {
|
|
518
|
+
oj_set_error_at(pi,
|
|
519
|
+
oj_parse_error_class,
|
|
520
|
+
__FILE__,
|
|
521
|
+
__LINE__,
|
|
522
|
+
"not a number");
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (INT64_MAX <= ni.i || DEC_MAX < dec_cnt) {
|
|
526
|
+
ni.big = true;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if ('.' == *pi->cur) {
|
|
530
|
+
pi->cur++;
|
|
531
|
+
// A trailing . is not a valid decimal but if encountered allow it
|
|
532
|
+
// except when mimicking the JSON gem or in strict mode.
|
|
533
|
+
if (StrictMode == pi->options.mode || CompatMode == pi->options.mode) {
|
|
534
|
+
int pos = (int)(pi->cur - ni.str);
|
|
535
|
+
|
|
536
|
+
if (1 == pos || (2 == pos && ni.neg)) {
|
|
537
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
if (*pi->cur < '0' || '9' < *pi->cur) {
|
|
541
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
|
|
546
|
+
int d = (*pi->cur - '0');
|
|
547
|
+
|
|
548
|
+
if (RB_LIKELY(0 != ni.num || 0 != ni.i)) {
|
|
549
|
+
dec_cnt++;
|
|
550
|
+
}
|
|
551
|
+
ni.num = ni.num * 10 + d;
|
|
552
|
+
ni.div *= 10;
|
|
553
|
+
ni.di++;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
|
|
557
|
+
if (!ni.no_big) {
|
|
558
|
+
ni.big = true;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if ('e' == *pi->cur || 'E' == *pi->cur) {
|
|
563
|
+
int eneg = 0;
|
|
564
|
+
|
|
565
|
+
ni.has_exp = 1;
|
|
566
|
+
pi->cur++;
|
|
567
|
+
if ('-' == *pi->cur) {
|
|
568
|
+
pi->cur++;
|
|
569
|
+
eneg = 1;
|
|
570
|
+
} else if ('+' == *pi->cur) {
|
|
571
|
+
pi->cur++;
|
|
572
|
+
}
|
|
573
|
+
for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
|
|
574
|
+
ni.exp = ni.exp * 10 + (*pi->cur - '0');
|
|
575
|
+
if (EXP_MAX <= ni.exp) {
|
|
576
|
+
ni.big = true;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (eneg) {
|
|
580
|
+
ni.exp = -ni.exp;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
ni.len = pi->cur - ni.str;
|
|
491
584
|
}
|
|
492
585
|
// Check for special reserved values for Infinity and NaN.
|
|
493
586
|
if (ni.big) {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
587
|
+
if (0 == strcasecmp(INF_VAL, ni.str)) {
|
|
588
|
+
ni.infinity = 1;
|
|
589
|
+
} else if (0 == strcasecmp(NINF_VAL, ni.str)) {
|
|
590
|
+
ni.infinity = 1;
|
|
591
|
+
ni.neg = 1;
|
|
592
|
+
} else if (0 == strcasecmp(NAN_VAL, ni.str)) {
|
|
593
|
+
ni.nan = 1;
|
|
594
|
+
}
|
|
502
595
|
}
|
|
503
|
-
if (
|
|
504
|
-
|
|
596
|
+
if (CompatMode == pi->options.mode) {
|
|
597
|
+
if (pi->options.compat_bigdec) {
|
|
598
|
+
ni.big = 1;
|
|
599
|
+
}
|
|
600
|
+
} else if (BigDec == pi->options.bigdec_load) {
|
|
601
|
+
ni.big = 1;
|
|
505
602
|
}
|
|
506
603
|
if (0 == parent) {
|
|
507
|
-
|
|
604
|
+
pi->add_num(pi, &ni);
|
|
508
605
|
} else {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
606
|
+
switch (parent->next) {
|
|
607
|
+
case NEXT_ARRAY_NEW:
|
|
608
|
+
case NEXT_ARRAY_ELEMENT:
|
|
609
|
+
pi->array_append_num(pi, &ni);
|
|
610
|
+
parent->next = NEXT_ARRAY_COMMA;
|
|
611
|
+
break;
|
|
612
|
+
case NEXT_HASH_VALUE:
|
|
613
|
+
pi->hash_set_num(pi, parent, &ni);
|
|
614
|
+
if (0 != parent->key && 0 < parent->klen &&
|
|
615
|
+
(parent->key < pi->json || pi->cur < parent->key)) {
|
|
616
|
+
xfree((char *)parent->key);
|
|
617
|
+
parent->key = 0;
|
|
618
|
+
}
|
|
619
|
+
parent->next = NEXT_HASH_COMMA;
|
|
620
|
+
break;
|
|
621
|
+
default:
|
|
622
|
+
oj_set_error_at(pi,
|
|
623
|
+
oj_parse_error_class,
|
|
624
|
+
__FILE__,
|
|
625
|
+
__LINE__,
|
|
626
|
+
"expected %s",
|
|
627
|
+
oj_stack_next_string(parent->next));
|
|
628
|
+
break;
|
|
629
|
+
}
|
|
527
630
|
}
|
|
528
631
|
}
|
|
529
632
|
|
|
530
|
-
static void
|
|
531
|
-
|
|
532
|
-
volatile VALUE v = pi->start_array(pi);
|
|
633
|
+
static void array_start(ParseInfo pi) {
|
|
634
|
+
VALUE v = pi->start_array(pi);
|
|
533
635
|
|
|
534
636
|
stack_push(&pi->stack, v, NEXT_ARRAY_NEW);
|
|
535
637
|
}
|
|
536
638
|
|
|
537
|
-
static void
|
|
538
|
-
|
|
539
|
-
Val array = stack_pop(&pi->stack);
|
|
639
|
+
static void array_end(ParseInfo pi) {
|
|
640
|
+
Val array = stack_pop(&pi->stack);
|
|
540
641
|
|
|
541
642
|
if (0 == array) {
|
|
542
|
-
|
|
643
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected array close");
|
|
543
644
|
} else if (NEXT_ARRAY_COMMA != array->next && NEXT_ARRAY_NEW != array->next) {
|
|
544
|
-
|
|
645
|
+
oj_set_error_at(pi,
|
|
646
|
+
oj_parse_error_class,
|
|
647
|
+
__FILE__,
|
|
648
|
+
__LINE__,
|
|
649
|
+
"expected %s, not an array close",
|
|
650
|
+
oj_stack_next_string(array->next));
|
|
545
651
|
} else {
|
|
546
|
-
|
|
547
|
-
|
|
652
|
+
pi->end_array(pi);
|
|
653
|
+
add_value(pi, array->val);
|
|
548
654
|
}
|
|
549
655
|
}
|
|
550
656
|
|
|
551
|
-
static void
|
|
552
|
-
|
|
553
|
-
volatile VALUE v = pi->start_hash(pi);
|
|
657
|
+
static void hash_start(ParseInfo pi) {
|
|
658
|
+
VALUE v = pi->start_hash(pi);
|
|
554
659
|
|
|
555
660
|
stack_push(&pi->stack, v, NEXT_HASH_NEW);
|
|
556
661
|
}
|
|
557
662
|
|
|
558
|
-
static void
|
|
559
|
-
|
|
560
|
-
volatile Val hash = stack_peek(&pi->stack);
|
|
663
|
+
static void hash_end(ParseInfo pi) {
|
|
664
|
+
Val hash = stack_peek(&pi->stack);
|
|
561
665
|
|
|
562
666
|
// leave hash on stack until just before
|
|
563
667
|
if (0 == hash) {
|
|
564
|
-
|
|
668
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected hash close");
|
|
565
669
|
} else if (NEXT_HASH_COMMA != hash->next && NEXT_HASH_NEW != hash->next) {
|
|
566
|
-
|
|
670
|
+
oj_set_error_at(pi,
|
|
671
|
+
oj_parse_error_class,
|
|
672
|
+
__FILE__,
|
|
673
|
+
__LINE__,
|
|
674
|
+
"expected %s, not a hash close",
|
|
675
|
+
oj_stack_next_string(hash->next));
|
|
567
676
|
} else {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
677
|
+
pi->end_hash(pi);
|
|
678
|
+
stack_pop(&pi->stack);
|
|
679
|
+
add_value(pi, hash->val);
|
|
571
680
|
}
|
|
572
681
|
}
|
|
573
682
|
|
|
574
|
-
static void
|
|
575
|
-
|
|
576
|
-
Val parent = stack_peek(&pi->stack);
|
|
683
|
+
static void comma(ParseInfo pi) {
|
|
684
|
+
Val parent = stack_peek(&pi->stack);
|
|
577
685
|
|
|
578
686
|
if (0 == parent) {
|
|
579
|
-
|
|
687
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
|
|
580
688
|
} else if (NEXT_ARRAY_COMMA == parent->next) {
|
|
581
|
-
|
|
689
|
+
parent->next = NEXT_ARRAY_ELEMENT;
|
|
582
690
|
} else if (NEXT_HASH_COMMA == parent->next) {
|
|
583
|
-
|
|
691
|
+
parent->next = NEXT_HASH_KEY;
|
|
584
692
|
} else {
|
|
585
|
-
|
|
693
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
|
|
586
694
|
}
|
|
587
695
|
}
|
|
588
696
|
|
|
589
|
-
static void
|
|
590
|
-
|
|
591
|
-
Val parent = stack_peek(&pi->stack);
|
|
697
|
+
static void colon(ParseInfo pi) {
|
|
698
|
+
Val parent = stack_peek(&pi->stack);
|
|
592
699
|
|
|
593
700
|
if (0 != parent && NEXT_HASH_COLON == parent->next) {
|
|
594
|
-
|
|
701
|
+
parent->next = NEXT_HASH_VALUE;
|
|
595
702
|
} else {
|
|
596
|
-
|
|
703
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected colon");
|
|
597
704
|
}
|
|
598
705
|
}
|
|
599
706
|
|
|
600
|
-
void
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
long start = 0;
|
|
707
|
+
void oj_parse2(ParseInfo pi) {
|
|
708
|
+
int first = 1;
|
|
709
|
+
long start = 0;
|
|
604
710
|
|
|
605
711
|
pi->cur = pi->json;
|
|
606
712
|
err_init(&pi->err);
|
|
607
713
|
while (1) {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
|
|
612
|
-
pi->err_class = err_clas;
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
next_non_white(pi);
|
|
616
|
-
if (!first && '\0' != *pi->cur) {
|
|
617
|
-
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected characters after the JSON document");
|
|
618
|
-
}
|
|
714
|
+
if (0 < pi->max_depth && pi->max_depth <= pi->stack.tail - pi->stack.head - 1) {
|
|
715
|
+
VALUE err_clas = oj_get_json_err_class("NestingError");
|
|
619
716
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
717
|
+
oj_set_error_at(pi, err_clas, __FILE__, __LINE__, "Too deeply nested.");
|
|
718
|
+
pi->err_class = err_clas;
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
next_non_white(pi);
|
|
722
|
+
if (!first && '\0' != *pi->cur) {
|
|
723
|
+
oj_set_error_at(pi,
|
|
724
|
+
oj_parse_error_class,
|
|
725
|
+
__FILE__,
|
|
726
|
+
__LINE__,
|
|
727
|
+
"unexpected characters after the JSON document");
|
|
728
|
+
}
|
|
625
729
|
|
|
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
|
-
first = 0;
|
|
726
|
-
}
|
|
727
|
-
start = pi->cur - pi->json;
|
|
728
|
-
}
|
|
730
|
+
// If no tokens are consumed (i.e. empty string), throw a parse error
|
|
731
|
+
// this is the behavior of JSON.parse in both Ruby and JS.
|
|
732
|
+
if (No == pi->options.empty_string && 1 == first && '\0' == *pi->cur) {
|
|
733
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
switch (*pi->cur++) {
|
|
737
|
+
case '{': hash_start(pi); break;
|
|
738
|
+
case '}': hash_end(pi); break;
|
|
739
|
+
case ':': colon(pi); break;
|
|
740
|
+
case '[': array_start(pi); break;
|
|
741
|
+
case ']': array_end(pi); break;
|
|
742
|
+
case ',': comma(pi); break;
|
|
743
|
+
case '"':
|
|
744
|
+
read_str(pi);
|
|
745
|
+
break;
|
|
746
|
+
// case '+':
|
|
747
|
+
case '+':
|
|
748
|
+
if (CompatMode == pi->options.mode) {
|
|
749
|
+
oj_set_error_at(pi,
|
|
750
|
+
oj_parse_error_class,
|
|
751
|
+
__FILE__,
|
|
752
|
+
__LINE__,
|
|
753
|
+
"unexpected character");
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
pi->cur--;
|
|
757
|
+
read_num(pi);
|
|
758
|
+
break;
|
|
759
|
+
case '-':
|
|
760
|
+
case '0':
|
|
761
|
+
case '1':
|
|
762
|
+
case '2':
|
|
763
|
+
case '3':
|
|
764
|
+
case '4':
|
|
765
|
+
case '5':
|
|
766
|
+
case '6':
|
|
767
|
+
case '7':
|
|
768
|
+
case '8':
|
|
769
|
+
case '9':
|
|
770
|
+
pi->cur--;
|
|
771
|
+
read_num(pi);
|
|
772
|
+
break;
|
|
773
|
+
case 'I':
|
|
774
|
+
case 'N':
|
|
775
|
+
if (Yes == pi->options.allow_nan) {
|
|
776
|
+
pi->cur--;
|
|
777
|
+
read_num(pi);
|
|
778
|
+
} else {
|
|
779
|
+
oj_set_error_at(pi,
|
|
780
|
+
oj_parse_error_class,
|
|
781
|
+
__FILE__,
|
|
782
|
+
__LINE__,
|
|
783
|
+
"unexpected character");
|
|
784
|
+
}
|
|
785
|
+
break;
|
|
786
|
+
case 't': read_true(pi); break;
|
|
787
|
+
case 'f': read_false(pi); break;
|
|
788
|
+
case 'n':
|
|
789
|
+
if ('u' == *pi->cur) {
|
|
790
|
+
read_null(pi);
|
|
791
|
+
} else {
|
|
792
|
+
pi->cur--;
|
|
793
|
+
read_num(pi);
|
|
794
|
+
}
|
|
795
|
+
break;
|
|
796
|
+
case '/':
|
|
797
|
+
skip_comment(pi);
|
|
798
|
+
if (first) {
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
break;
|
|
802
|
+
case '\0': pi->cur--; return;
|
|
803
|
+
default:
|
|
804
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
if (err_has(&pi->err)) {
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
if (stack_empty(&pi->stack)) {
|
|
811
|
+
if (Qundef != pi->proc) {
|
|
812
|
+
VALUE args[3];
|
|
813
|
+
long len = (pi->cur - pi->json) - start;
|
|
814
|
+
|
|
815
|
+
*args = stack_head_val(&pi->stack);
|
|
816
|
+
args[1] = LONG2NUM(start);
|
|
817
|
+
args[2] = LONG2NUM(len);
|
|
818
|
+
|
|
819
|
+
if (Qnil == pi->proc) {
|
|
820
|
+
rb_yield_values2(3, args);
|
|
821
|
+
} else {
|
|
822
|
+
rb_proc_call_with_block(pi->proc, 3, args, Qnil);
|
|
823
|
+
}
|
|
824
|
+
} else if (!pi->has_callbacks) {
|
|
825
|
+
first = 0;
|
|
826
|
+
}
|
|
827
|
+
start = pi->cur - pi->json;
|
|
828
|
+
}
|
|
729
829
|
}
|
|
730
830
|
}
|
|
731
831
|
|
|
832
|
+
static VALUE rescue_big_decimal(VALUE str, VALUE ignore) {
|
|
833
|
+
rb_raise(oj_parse_error_class, "Invalid value for BigDecimal()");
|
|
834
|
+
return Qnil;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
static VALUE parse_big_decimal(VALUE str) {
|
|
838
|
+
return rb_funcall(rb_cObject, oj_bigdecimal_id, 1, str);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
static long double exp_plus[] = {
|
|
842
|
+
1.0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9,
|
|
843
|
+
1.0e10, 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15, 1.0e16, 1.0e17, 1.0e18, 1.0e19,
|
|
844
|
+
1.0e20, 1.0e21, 1.0e22, 1.0e23, 1.0e24, 1.0e25, 1.0e26, 1.0e27, 1.0e28, 1.0e29,
|
|
845
|
+
1.0e30, 1.0e31, 1.0e32, 1.0e33, 1.0e34, 1.0e35, 1.0e36, 1.0e37, 1.0e38, 1.0e39,
|
|
846
|
+
1.0e40, 1.0e41, 1.0e42, 1.0e43, 1.0e44, 1.0e45, 1.0e46, 1.0e47, 1.0e48, 1.0e49,
|
|
847
|
+
};
|
|
848
|
+
|
|
732
849
|
VALUE
|
|
733
850
|
oj_num_as_value(NumInfo ni) {
|
|
734
|
-
|
|
851
|
+
VALUE rnum = Qnil;
|
|
735
852
|
|
|
736
853
|
if (ni->infinity) {
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
854
|
+
if (ni->neg) {
|
|
855
|
+
rnum = rb_float_new(-OJ_INFINITY);
|
|
856
|
+
} else {
|
|
857
|
+
rnum = rb_float_new(OJ_INFINITY);
|
|
858
|
+
}
|
|
742
859
|
} else if (ni->nan) {
|
|
743
|
-
|
|
744
|
-
} else if (1 == ni->div && 0 == ni->exp) {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
} else {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
860
|
+
rnum = rb_float_new(0.0 / 0.0);
|
|
861
|
+
} else if (1 == ni->div && 0 == ni->exp && !ni->has_exp) { // fixnum
|
|
862
|
+
if (ni->big) {
|
|
863
|
+
if (256 > ni->len) {
|
|
864
|
+
char buf[256];
|
|
865
|
+
|
|
866
|
+
memcpy(buf, ni->str, ni->len);
|
|
867
|
+
buf[ni->len] = '\0';
|
|
868
|
+
rnum = rb_cstr_to_inum(buf, 10, 0);
|
|
869
|
+
} else {
|
|
870
|
+
char *buf = ALLOC_N(char, ni->len + 1);
|
|
871
|
+
|
|
872
|
+
memcpy(buf, ni->str, ni->len);
|
|
873
|
+
buf[ni->len] = '\0';
|
|
874
|
+
rnum = rb_cstr_to_inum(buf, 10, 0);
|
|
875
|
+
xfree(buf);
|
|
876
|
+
}
|
|
877
|
+
} else {
|
|
878
|
+
if (ni->neg) {
|
|
879
|
+
rnum = rb_ll2inum(-ni->i);
|
|
880
|
+
} else {
|
|
881
|
+
rnum = rb_ll2inum(ni->i);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
} else { // decimal
|
|
885
|
+
if (ni->big) {
|
|
886
|
+
VALUE bd = rb_str_new(ni->str, ni->len);
|
|
887
|
+
|
|
888
|
+
rnum = rb_rescue2(parse_big_decimal, bd, rescue_big_decimal, bd, rb_eException, 0);
|
|
889
|
+
if (ni->no_big) {
|
|
890
|
+
rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
|
|
891
|
+
}
|
|
892
|
+
} else if (FastDec == ni->bigdec_load) {
|
|
893
|
+
long double ld = (long double)ni->i * (long double)ni->div + (long double)ni->num;
|
|
894
|
+
int x = (int)((int64_t)ni->exp - ni->di);
|
|
895
|
+
|
|
896
|
+
if (0 < x) {
|
|
897
|
+
if (x < (int)(sizeof(exp_plus) / sizeof(*exp_plus))) {
|
|
898
|
+
ld *= exp_plus[x];
|
|
899
|
+
} else {
|
|
900
|
+
ld *= powl(10.0, x);
|
|
901
|
+
}
|
|
902
|
+
} else if (x < 0) {
|
|
903
|
+
if (-x < (int)(sizeof(exp_plus) / sizeof(*exp_plus))) {
|
|
904
|
+
ld /= exp_plus[-x];
|
|
905
|
+
} else {
|
|
906
|
+
ld /= powl(10.0, -x);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
if (ni->neg) {
|
|
910
|
+
ld = -ld;
|
|
911
|
+
}
|
|
912
|
+
rnum = rb_float_new((double)ld);
|
|
913
|
+
} else if (RubyDec == ni->bigdec_load) {
|
|
914
|
+
VALUE sv = rb_str_new(ni->str, ni->len);
|
|
915
|
+
|
|
916
|
+
rnum = rb_funcall(sv, rb_intern("to_f"), 0);
|
|
917
|
+
} else {
|
|
918
|
+
char * end;
|
|
919
|
+
double d = strtod(ni->str, &end);
|
|
920
|
+
|
|
921
|
+
if ((long)ni->len != (long)(end - ni->str)) {
|
|
922
|
+
rb_raise(oj_parse_error_class, "Invalid float");
|
|
923
|
+
}
|
|
924
|
+
rnum = rb_float_new(d);
|
|
925
|
+
}
|
|
799
926
|
}
|
|
800
927
|
return rnum;
|
|
801
928
|
}
|
|
802
929
|
|
|
803
|
-
void
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
930
|
+
void oj_set_error_at(ParseInfo pi,
|
|
931
|
+
VALUE err_clas,
|
|
932
|
+
const char *file,
|
|
933
|
+
int line,
|
|
934
|
+
const char *format,
|
|
935
|
+
...) {
|
|
936
|
+
va_list ap;
|
|
937
|
+
char msg[256];
|
|
938
|
+
char * p = msg;
|
|
939
|
+
char * end = p + sizeof(msg) - 2;
|
|
940
|
+
char * start;
|
|
941
|
+
Val vp;
|
|
942
|
+
int mlen;
|
|
811
943
|
|
|
812
944
|
va_start(ap, format);
|
|
813
|
-
|
|
945
|
+
mlen = vsnprintf(msg, sizeof(msg) - 1, format, ap);
|
|
946
|
+
if (0 < mlen) {
|
|
947
|
+
if (sizeof(msg) - 2 < (size_t)mlen) {
|
|
948
|
+
p = end - 2;
|
|
949
|
+
} else {
|
|
950
|
+
p += mlen;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
814
953
|
va_end(ap);
|
|
815
954
|
pi->err.clas = err_clas;
|
|
816
955
|
if (p + 3 < end) {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
956
|
+
*p++ = ' ';
|
|
957
|
+
*p++ = '(';
|
|
958
|
+
*p++ = 'a';
|
|
959
|
+
*p++ = 'f';
|
|
960
|
+
*p++ = 't';
|
|
961
|
+
*p++ = 'e';
|
|
962
|
+
*p++ = 'r';
|
|
963
|
+
*p++ = ' ';
|
|
964
|
+
start = p;
|
|
965
|
+
for (vp = pi->stack.head; vp < pi->stack.tail; vp++) {
|
|
966
|
+
if (end <= p + 1 + vp->klen) {
|
|
967
|
+
break;
|
|
968
|
+
}
|
|
969
|
+
if (NULL != vp->key) {
|
|
970
|
+
if (start < p) {
|
|
971
|
+
*p++ = '.';
|
|
972
|
+
}
|
|
973
|
+
memcpy(p, vp->key, vp->klen);
|
|
974
|
+
p += vp->klen;
|
|
975
|
+
} else {
|
|
976
|
+
if (RUBY_T_ARRAY == rb_type(vp->val)) {
|
|
977
|
+
if (end <= p + 12) {
|
|
978
|
+
break;
|
|
979
|
+
}
|
|
980
|
+
p += snprintf(p, end - p, "[%ld]", RARRAY_LEN(vp->val));
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
*p++ = ')';
|
|
840
985
|
}
|
|
841
986
|
*p = '\0';
|
|
842
987
|
if (0 == pi->json) {
|
|
843
|
-
|
|
988
|
+
oj_err_set(&pi->err,
|
|
989
|
+
err_clas,
|
|
990
|
+
"%s at line %d, column %d [%s:%d]",
|
|
991
|
+
msg,
|
|
992
|
+
pi->rd.line,
|
|
993
|
+
pi->rd.col,
|
|
994
|
+
file,
|
|
995
|
+
line);
|
|
844
996
|
} else {
|
|
845
|
-
|
|
997
|
+
_oj_err_set_with_location(&pi->err, err_clas, msg, pi->json, pi->cur - 1, file, line);
|
|
846
998
|
}
|
|
847
999
|
}
|
|
848
1000
|
|
|
849
|
-
static VALUE
|
|
850
|
-
protect_parse(VALUE pip) {
|
|
1001
|
+
static VALUE protect_parse(VALUE pip) {
|
|
851
1002
|
oj_parse2((ParseInfo)pip);
|
|
852
1003
|
|
|
853
1004
|
return Qnil;
|
|
@@ -855,102 +1006,103 @@ protect_parse(VALUE pip) {
|
|
|
855
1006
|
|
|
856
1007
|
extern int oj_utf8_index;
|
|
857
1008
|
|
|
858
|
-
static void
|
|
859
|
-
|
|
860
|
-
rb_encoding *enc = rb_to_encoding(rb_obj_encoding(*inputp));
|
|
1009
|
+
static void oj_pi_set_input_str(ParseInfo pi, VALUE *inputp) {
|
|
1010
|
+
int idx = RB_ENCODING_GET(*inputp);
|
|
861
1011
|
|
|
862
|
-
if (
|
|
863
|
-
|
|
1012
|
+
if (oj_utf8_encoding_index != idx) {
|
|
1013
|
+
rb_encoding *enc = rb_enc_from_index(idx);
|
|
1014
|
+
*inputp = rb_str_conv_enc(*inputp, enc, oj_utf8_encoding);
|
|
864
1015
|
}
|
|
865
|
-
pi->json =
|
|
866
|
-
pi->end
|
|
1016
|
+
pi->json = RSTRING_PTR(*inputp);
|
|
1017
|
+
pi->end = pi->json + RSTRING_LEN(*inputp);
|
|
867
1018
|
}
|
|
868
1019
|
|
|
869
1020
|
VALUE
|
|
870
1021
|
oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yieldOk) {
|
|
871
|
-
char
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
int
|
|
876
|
-
int
|
|
1022
|
+
char * buf = 0;
|
|
1023
|
+
VALUE input;
|
|
1024
|
+
VALUE wrapped_stack;
|
|
1025
|
+
VALUE result = Qnil;
|
|
1026
|
+
int line = 0;
|
|
1027
|
+
int free_json = 0;
|
|
877
1028
|
|
|
878
1029
|
if (argc < 1) {
|
|
879
|
-
|
|
1030
|
+
rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
|
|
880
1031
|
}
|
|
881
1032
|
input = argv[0];
|
|
882
1033
|
if (2 <= argc) {
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
1034
|
+
if (T_HASH == rb_type(argv[1])) {
|
|
1035
|
+
oj_parse_options(argv[1], &pi->options);
|
|
1036
|
+
} else if (3 <= argc && T_HASH == rb_type(argv[2])) {
|
|
1037
|
+
oj_parse_options(argv[2], &pi->options);
|
|
1038
|
+
}
|
|
888
1039
|
}
|
|
889
1040
|
if (yieldOk && rb_block_given_p()) {
|
|
890
|
-
|
|
1041
|
+
pi->proc = Qnil;
|
|
891
1042
|
} else {
|
|
892
|
-
|
|
1043
|
+
pi->proc = Qundef;
|
|
893
1044
|
}
|
|
894
1045
|
if (0 != json) {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
1046
|
+
pi->json = json;
|
|
1047
|
+
pi->end = json + len;
|
|
1048
|
+
free_json = 1;
|
|
898
1049
|
} else if (T_STRING == rb_type(input)) {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1050
|
+
if (CompatMode == pi->options.mode) {
|
|
1051
|
+
if (No == pi->options.nilnil && 0 == RSTRING_LEN(input)) {
|
|
1052
|
+
rb_raise(oj_json_parser_error_class, "An empty string is not a valid JSON string.");
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
oj_pi_set_input_str(pi, &input);
|
|
905
1056
|
} else if (Qnil == input) {
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1057
|
+
if (Yes == pi->options.nilnil) {
|
|
1058
|
+
return Qnil;
|
|
1059
|
+
} else {
|
|
1060
|
+
rb_raise(rb_eTypeError, "Nil is not a valid JSON source.");
|
|
1061
|
+
}
|
|
911
1062
|
} else {
|
|
912
|
-
|
|
913
|
-
|
|
1063
|
+
VALUE clas = rb_obj_class(input);
|
|
1064
|
+
VALUE s;
|
|
914
1065
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1066
|
+
if (oj_stringio_class == clas) {
|
|
1067
|
+
s = rb_funcall2(input, oj_string_id, 0, 0);
|
|
1068
|
+
oj_pi_set_input_str(pi, &s);
|
|
918
1069
|
#if !IS_WINDOWS
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1070
|
+
} else if (rb_cFile == clas && 0 == FIX2INT(rb_funcall(input, oj_pos_id, 0))) {
|
|
1071
|
+
int fd = FIX2INT(rb_funcall(input, oj_fileno_id, 0));
|
|
1072
|
+
ssize_t cnt;
|
|
1073
|
+
size_t len = lseek(fd, 0, SEEK_END);
|
|
1074
|
+
|
|
1075
|
+
lseek(fd, 0, SEEK_SET);
|
|
1076
|
+
buf = ALLOC_N(char, len + 1);
|
|
1077
|
+
pi->json = buf;
|
|
1078
|
+
pi->end = buf + len;
|
|
1079
|
+
if (0 >= (cnt = read(fd, (char *)pi->json, len)) || cnt != (ssize_t)len) {
|
|
1080
|
+
if (0 != buf) {
|
|
1081
|
+
xfree(buf);
|
|
1082
|
+
}
|
|
1083
|
+
rb_raise(rb_eIOError, "failed to read from IO Object.");
|
|
1084
|
+
}
|
|
1085
|
+
((char *)pi->json)[len] = '\0';
|
|
1086
|
+
/* skip UTF-8 BOM if present */
|
|
1087
|
+
if (0xEF == (uint8_t)*pi->json && 0xBB == (uint8_t)pi->json[1] &&
|
|
1088
|
+
0xBF == (uint8_t)pi->json[2]) {
|
|
1089
|
+
pi->cur += 3;
|
|
1090
|
+
}
|
|
939
1091
|
#endif
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
}
|
|
1092
|
+
} else if (rb_respond_to(input, oj_read_id)) {
|
|
1093
|
+
// use stream parser instead
|
|
1094
|
+
return oj_pi_sparse(argc, argv, pi, 0);
|
|
1095
|
+
} else {
|
|
1096
|
+
rb_raise(rb_eArgError, "parse() expected a String or IO Object.");
|
|
1097
|
+
}
|
|
946
1098
|
}
|
|
947
1099
|
if (Yes == pi->options.circular) {
|
|
948
|
-
|
|
1100
|
+
pi->circ_array = oj_circ_array_new();
|
|
949
1101
|
} else {
|
|
950
|
-
|
|
1102
|
+
pi->circ_array = 0;
|
|
951
1103
|
}
|
|
952
1104
|
if (No == pi->options.allow_gc) {
|
|
953
|
-
|
|
1105
|
+
rb_gc_disable();
|
|
954
1106
|
}
|
|
955
1107
|
// GC can run at any time. When it runs any Object created by C will be
|
|
956
1108
|
// freed. We protect against this by wrapping the value stack in a ruby
|
|
@@ -959,112 +1111,116 @@ oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json, size_t len, int yie
|
|
|
959
1111
|
wrapped_stack = oj_stack_init(&pi->stack);
|
|
960
1112
|
rb_protect(protect_parse, (VALUE)pi, &line);
|
|
961
1113
|
if (Qundef == pi->stack.head->val && !empty_ok(&pi->options)) {
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1114
|
+
if (No == pi->options.nilnil ||
|
|
1115
|
+
(CompatMode == pi->options.mode && 0 < pi->cur - pi->json)) {
|
|
1116
|
+
oj_set_error_at(pi, oj_json_parser_error_class, __FILE__, __LINE__, "Empty input");
|
|
1117
|
+
}
|
|
965
1118
|
}
|
|
966
|
-
result
|
|
1119
|
+
result = stack_head_val(&pi->stack);
|
|
967
1120
|
DATA_PTR(wrapped_stack) = 0;
|
|
968
1121
|
if (No == pi->options.allow_gc) {
|
|
969
|
-
|
|
1122
|
+
rb_gc_enable();
|
|
970
1123
|
}
|
|
971
1124
|
if (!err_has(&pi->err)) {
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
}
|
|
1125
|
+
// If the stack is not empty then the JSON terminated early.
|
|
1126
|
+
Val v;
|
|
1127
|
+
VALUE err_class = oj_parse_error_class;
|
|
1128
|
+
|
|
1129
|
+
if (0 != line) {
|
|
1130
|
+
VALUE ec = rb_obj_class(rb_errinfo());
|
|
1131
|
+
|
|
1132
|
+
if (rb_eArgError != ec && 0 != ec) {
|
|
1133
|
+
err_class = ec;
|
|
1134
|
+
}
|
|
1135
|
+
if (rb_eIOError != ec) {
|
|
1136
|
+
goto CLEANUP;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
if (NULL != (v = stack_peek(&pi->stack))) {
|
|
1140
|
+
switch (v->next) {
|
|
1141
|
+
case NEXT_ARRAY_NEW:
|
|
1142
|
+
case NEXT_ARRAY_ELEMENT:
|
|
1143
|
+
case NEXT_ARRAY_COMMA:
|
|
1144
|
+
oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Array not terminated");
|
|
1145
|
+
break;
|
|
1146
|
+
case NEXT_HASH_NEW:
|
|
1147
|
+
case NEXT_HASH_KEY:
|
|
1148
|
+
case NEXT_HASH_COLON:
|
|
1149
|
+
case NEXT_HASH_VALUE:
|
|
1150
|
+
case NEXT_HASH_COMMA:
|
|
1151
|
+
oj_set_error_at(pi, err_class, __FILE__, __LINE__, "Hash/Object not terminated");
|
|
1152
|
+
break;
|
|
1153
|
+
default: oj_set_error_at(pi, err_class, __FILE__, __LINE__, "not terminated");
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1004
1156
|
}
|
|
1005
1157
|
CLEANUP:
|
|
1006
1158
|
// proceed with cleanup
|
|
1007
1159
|
if (0 != pi->circ_array) {
|
|
1008
|
-
|
|
1160
|
+
oj_circ_array_free(pi->circ_array);
|
|
1009
1161
|
}
|
|
1010
1162
|
if (0 != buf) {
|
|
1011
|
-
|
|
1163
|
+
xfree(buf);
|
|
1012
1164
|
} else if (free_json) {
|
|
1013
|
-
|
|
1165
|
+
xfree(json);
|
|
1014
1166
|
}
|
|
1015
1167
|
stack_cleanup(&pi->stack);
|
|
1016
1168
|
if (pi->str_rx.head != oj_default_options.str_rx.head) {
|
|
1017
|
-
|
|
1169
|
+
oj_rxclass_cleanup(&pi->str_rx);
|
|
1018
1170
|
}
|
|
1019
1171
|
if (err_has(&pi->err)) {
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1172
|
+
rb_set_errinfo(Qnil);
|
|
1173
|
+
if (Qnil != pi->err_class) {
|
|
1174
|
+
pi->err.clas = pi->err_class;
|
|
1175
|
+
}
|
|
1176
|
+
if ((CompatMode == pi->options.mode || RailsMode == pi->options.mode) && Yes != pi->options.safe) {
|
|
1177
|
+
// The json gem requires the error message be UTF-8 encoded. In
|
|
1178
|
+
// additional the complete JSON source must be returned. There
|
|
1179
|
+
// does not seem to be a size limit.
|
|
1180
|
+
VALUE msg = oj_encode(rb_str_new2(pi->err.msg));
|
|
1181
|
+
VALUE args[1];
|
|
1182
|
+
|
|
1183
|
+
if (NULL != pi->json) {
|
|
1184
|
+
msg = rb_str_append(msg, oj_encode(rb_str_new2(" in '")));
|
|
1185
|
+
msg = rb_str_append(msg, oj_encode(rb_str_new2(pi->json)));
|
|
1186
|
+
}
|
|
1187
|
+
args[0] = msg;
|
|
1188
|
+
if (pi->err.clas == oj_parse_error_class) {
|
|
1189
|
+
// The error was an Oj::ParseError so change to a JSON::ParserError.
|
|
1190
|
+
pi->err.clas = oj_json_parser_error_class;
|
|
1191
|
+
}
|
|
1192
|
+
rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
|
|
1193
|
+
} else {
|
|
1194
|
+
oj_err_raise(&pi->err);
|
|
1195
|
+
}
|
|
1040
1196
|
} else if (0 != line) {
|
|
1041
|
-
|
|
1197
|
+
rb_jump_tag(line);
|
|
1042
1198
|
}
|
|
1043
1199
|
if (pi->options.quirks_mode == No) {
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1200
|
+
switch (rb_type(result)) {
|
|
1201
|
+
case T_NIL:
|
|
1202
|
+
case T_TRUE:
|
|
1203
|
+
case T_FALSE:
|
|
1204
|
+
case T_FIXNUM:
|
|
1205
|
+
case T_FLOAT:
|
|
1206
|
+
case T_CLASS:
|
|
1207
|
+
case T_STRING:
|
|
1208
|
+
case T_SYMBOL: {
|
|
1209
|
+
struct _err err;
|
|
1210
|
+
|
|
1211
|
+
if (Qnil == pi->err_class) {
|
|
1212
|
+
err.clas = oj_parse_error_class;
|
|
1213
|
+
} else {
|
|
1214
|
+
err.clas = pi->err_class;
|
|
1215
|
+
}
|
|
1216
|
+
snprintf(err.msg, sizeof(err.msg), "unexpected non-document value");
|
|
1217
|
+
oj_err_raise(&err);
|
|
1218
|
+
break;
|
|
1219
|
+
}
|
|
1220
|
+
default:
|
|
1221
|
+
// okay
|
|
1222
|
+
break;
|
|
1223
|
+
}
|
|
1068
1224
|
}
|
|
1069
1225
|
return result;
|
|
1070
1226
|
}
|