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