oj 2.0.14 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of oj might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +47 -332
- data/ext/oj/buf.h +103 -0
- data/ext/oj/circarray.c +93 -0
- data/ext/oj/circarray.h +48 -0
- data/ext/oj/compat.c +112 -0
- data/ext/oj/dump.c +2 -1
- data/ext/oj/err.c +82 -0
- data/ext/oj/err.h +64 -0
- data/ext/oj/extconf.rb +1 -0
- data/ext/oj/hash.c +149 -0
- data/ext/oj/{cache.h → hash.h} +9 -10
- data/ext/oj/hash_test.c +501 -0
- data/ext/oj/object.c +514 -0
- data/ext/oj/odd.c +159 -0
- data/ext/oj/odd.h +61 -0
- data/ext/oj/oj.c +235 -305
- data/ext/oj/oj.h +18 -23
- data/ext/oj/parse.c +798 -0
- data/ext/oj/parse.h +88 -0
- data/ext/oj/resolve.c +117 -0
- data/ext/oj/resolve.h +38 -0
- data/ext/oj/saj.c +58 -86
- data/ext/oj/scp.c +308 -0
- data/ext/oj/strict.c +166 -0
- data/ext/oj/val_stack.c +48 -0
- data/ext/oj/val_stack.h +167 -0
- data/lib/oj.rb +1 -0
- data/lib/oj/saj.rb +11 -8
- data/lib/oj/schandler.rb +70 -0
- data/lib/oj/version.rb +1 -1
- data/test/bug.rb +14 -22
- data/test/perf_compat.rb +128 -0
- data/test/{perf_obj.rb → perf_object.rb} +18 -6
- data/test/perf_scp.rb +151 -0
- data/test/perf_strict.rb +23 -122
- data/test/sample.rb +2 -2
- data/test/test_compat.rb +342 -0
- data/test/test_object.rb +390 -0
- data/test/test_saj.rb +1 -1
- data/test/test_scp.rb +224 -0
- data/test/test_strict.rb +250 -0
- data/test/tests.rb +8 -18
- metadata +31 -10
- data/ext/oj/cache.c +0 -148
- data/ext/oj/load.c +0 -1089
- data/test/perf1.rb +0 -64
- data/test/perf2.rb +0 -76
- data/test/perf_obj_old.rb +0 -213
data/ext/oj/oj.h
CHANGED
@@ -49,7 +49,6 @@ extern "C" {
|
|
49
49
|
#if SAFE_CACHE
|
50
50
|
#include <pthread.h>
|
51
51
|
#endif
|
52
|
-
#include "cache.h"
|
53
52
|
#include "cache8.h"
|
54
53
|
|
55
54
|
#ifdef RUBINIUS_RUBY
|
@@ -65,9 +64,7 @@ enum st_retval {ST_CONTINUE = 0, ST_STOP = 1, ST_DELETE = 2, ST_CHECK};
|
|
65
64
|
#endif
|
66
65
|
#endif
|
67
66
|
|
68
|
-
#
|
69
|
-
|
70
|
-
#define MAX_ODD_ARGS 10
|
67
|
+
#include "err.h"
|
71
68
|
|
72
69
|
typedef enum {
|
73
70
|
Yes = 'y',
|
@@ -113,7 +110,7 @@ typedef struct _Options {
|
|
113
110
|
char bigdec_as_num; // YesNo
|
114
111
|
char bigdec_load; // YesNo
|
115
112
|
const char *create_id; // 0 or string
|
116
|
-
size_t
|
113
|
+
size_t create_id_len; // length of create_id
|
117
114
|
int sec_prec; // second precision when dumping time
|
118
115
|
DumpOpts dump_opts;
|
119
116
|
} *Options;
|
@@ -131,14 +128,6 @@ typedef struct _Out {
|
|
131
128
|
int allocated;
|
132
129
|
} *Out;
|
133
130
|
|
134
|
-
typedef struct _Odd {
|
135
|
-
VALUE clas; // Ruby class
|
136
|
-
VALUE create_obj;
|
137
|
-
ID create_op;
|
138
|
-
int attr_cnt;
|
139
|
-
ID attrs[MAX_ODD_ARGS]; // 0 terminated attr IDs
|
140
|
-
} *Odd;
|
141
|
-
|
142
131
|
enum {
|
143
132
|
STR_VAL = 0x00,
|
144
133
|
COL_VAL = 0x01,
|
@@ -161,20 +150,26 @@ typedef struct _Leaf {
|
|
161
150
|
uint8_t value_type;
|
162
151
|
} *Leaf;
|
163
152
|
|
164
|
-
extern VALUE
|
165
|
-
extern
|
153
|
+
extern VALUE oj_saj_parse(int argc, VALUE *argv, VALUE self);
|
154
|
+
extern VALUE oj_sc_parse(int argc, VALUE *argv, VALUE self);
|
155
|
+
|
156
|
+
extern VALUE oj_strict_parse(int argc, VALUE *argv, VALUE self);
|
157
|
+
extern VALUE oj_compat_parse(int argc, VALUE *argv, VALUE self);
|
158
|
+
extern VALUE oj_object_parse(int argc, VALUE *argv, VALUE self);
|
159
|
+
|
160
|
+
extern VALUE oj_strict_parse_cstr(int argc, VALUE *argv, char *json);
|
161
|
+
extern VALUE oj_compat_parse_cstr(int argc, VALUE *argv, char *json);
|
162
|
+
extern VALUE oj_object_parse_cstr(int argc, VALUE *argv, char *json);
|
163
|
+
|
164
|
+
extern void oj_parse_options(VALUE ropts, Options copts);
|
166
165
|
|
167
166
|
extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
|
168
167
|
extern void oj_write_obj_to_file(VALUE obj, const char *path, Options copts);
|
169
168
|
extern void oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out);
|
170
169
|
extern void oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts);
|
171
170
|
|
172
|
-
extern void _oj_raise_error(const char *msg, const char *xml, const char *current, const char* file, int line);
|
173
|
-
|
174
171
|
extern void oj_init_doc(void);
|
175
172
|
|
176
|
-
extern Odd oj_get_odd(VALUE clas);
|
177
|
-
|
178
173
|
extern VALUE Oj;
|
179
174
|
extern struct _Options oj_default_options;
|
180
175
|
#if HAS_ENCODING_SUPPORT
|
@@ -186,7 +181,6 @@ extern VALUE oj_bigdecimal_class;
|
|
186
181
|
extern VALUE oj_date_class;
|
187
182
|
extern VALUE oj_datetime_class;
|
188
183
|
extern VALUE oj_doc_class;
|
189
|
-
extern VALUE oj_parse_error_class;
|
190
184
|
extern VALUE oj_stringio_class;
|
191
185
|
extern VALUE oj_struct_class;
|
192
186
|
extern VALUE oj_time_class;
|
@@ -194,15 +188,19 @@ extern VALUE oj_time_class;
|
|
194
188
|
extern VALUE oj_slash_string;
|
195
189
|
|
196
190
|
extern ID oj_add_value_id;
|
191
|
+
extern ID oj_array_append_id;
|
197
192
|
extern ID oj_array_end_id;
|
198
193
|
extern ID oj_array_start_id;
|
199
194
|
extern ID oj_as_json_id;
|
200
195
|
extern ID oj_error_id;
|
196
|
+
extern ID oj_fileno_id;
|
201
197
|
extern ID oj_hash_end_id;
|
198
|
+
extern ID oj_hash_set_id;
|
202
199
|
extern ID oj_hash_start_id;
|
203
200
|
extern ID oj_instance_variables_id;
|
204
201
|
extern ID oj_json_create_id;
|
205
202
|
extern ID oj_new_id;
|
203
|
+
extern ID oj_read_id;
|
206
204
|
extern ID oj_string_id;
|
207
205
|
extern ID oj_to_hash_id;
|
208
206
|
extern ID oj_to_json_id;
|
@@ -214,9 +212,6 @@ extern ID oj_tv_sec_id;
|
|
214
212
|
extern ID oj_tv_usec_id;
|
215
213
|
extern ID oj_utc_offset_id;
|
216
214
|
|
217
|
-
extern Cache oj_class_cache;
|
218
|
-
extern Cache oj_attr_cache;
|
219
|
-
|
220
215
|
#if SAFE_CACHE
|
221
216
|
extern pthread_mutex_t oj_cache_mutex;
|
222
217
|
#endif
|
data/ext/oj/parse.c
ADDED
@@ -0,0 +1,798 @@
|
|
1
|
+
/* parse.c
|
2
|
+
* Copyright (c) 2013, Peter Ohler
|
3
|
+
* All rights reserved.
|
4
|
+
*
|
5
|
+
* Redistribution and use in source and binary forms, with or without
|
6
|
+
* modification, are permitted provided that the following conditions are met:
|
7
|
+
*
|
8
|
+
* - Redistributions of source code must retain the above copyright notice, this
|
9
|
+
* list of conditions and the following disclaimer.
|
10
|
+
*
|
11
|
+
* - Redistributions in binary form must reproduce the above copyright notice,
|
12
|
+
* this list of conditions and the following disclaimer in the documentation
|
13
|
+
* and/or other materials provided with the distribution.
|
14
|
+
*
|
15
|
+
* - Neither the name of Peter Ohler nor the names of its contributors may be
|
16
|
+
* used to endorse or promote products derived from this software without
|
17
|
+
* specific prior written permission.
|
18
|
+
*
|
19
|
+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
22
|
+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
23
|
+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
24
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
25
|
+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
26
|
+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
27
|
+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
*/
|
30
|
+
|
31
|
+
#include <stdlib.h>
|
32
|
+
#include <stdio.h>
|
33
|
+
#include <string.h>
|
34
|
+
#include <unistd.h>
|
35
|
+
#include <math.h>
|
36
|
+
|
37
|
+
#include "oj.h"
|
38
|
+
#include "parse.h"
|
39
|
+
#include "buf.h"
|
40
|
+
#include "val_stack.h"
|
41
|
+
|
42
|
+
// Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
|
43
|
+
#define OJ_INFINITY (1.0/0.0)
|
44
|
+
|
45
|
+
#ifdef RUBINIUS_RUBY
|
46
|
+
#define NUM_MAX 0x07FFFFFF
|
47
|
+
#else
|
48
|
+
#define NUM_MAX (FIXNUM_MAX >> 8)
|
49
|
+
#endif
|
50
|
+
#define EXP_MAX 1023
|
51
|
+
#define I64_MAX 0x7FFFFFFFFFFFFFFFLL
|
52
|
+
#define DEC_MAX 14
|
53
|
+
|
54
|
+
static void
|
55
|
+
next_non_white(ParseInfo pi) {
|
56
|
+
for (; 1; pi->cur++) {
|
57
|
+
switch(*pi->cur) {
|
58
|
+
case ' ':
|
59
|
+
case '\t':
|
60
|
+
case '\f':
|
61
|
+
case '\n':
|
62
|
+
case '\r':
|
63
|
+
break;
|
64
|
+
default:
|
65
|
+
return;
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
static void
|
71
|
+
skip_comment(ParseInfo pi) {
|
72
|
+
if ('*' == *pi->cur) {
|
73
|
+
pi->cur++;
|
74
|
+
for (; '\0' != *pi->cur; pi->cur++) {
|
75
|
+
if ('*' == *pi->cur && '/' == *(pi->cur + 1)) {
|
76
|
+
pi->cur += 2;
|
77
|
+
return;
|
78
|
+
} else if ('\0' == *pi->cur) {
|
79
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "comment not terminated");
|
80
|
+
return;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
} else if ('/' == *pi->cur) {
|
84
|
+
for (; 1; pi->cur++) {
|
85
|
+
switch (*pi->cur) {
|
86
|
+
case '\n':
|
87
|
+
case '\r':
|
88
|
+
case '\f':
|
89
|
+
case '\0':
|
90
|
+
return;
|
91
|
+
default:
|
92
|
+
break;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
} else {
|
96
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid comment format");
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
static void
|
101
|
+
add_value(ParseInfo pi, VALUE rval) {
|
102
|
+
Val parent = stack_peek(&pi->stack);
|
103
|
+
|
104
|
+
if (0 == parent) { // simple add
|
105
|
+
pi->add_value(pi, rval);
|
106
|
+
} else {
|
107
|
+
switch (parent->next) {
|
108
|
+
case NEXT_ARRAY_NEW:
|
109
|
+
case NEXT_ARRAY_ELEMENT:
|
110
|
+
pi->array_append_value(pi, rval);
|
111
|
+
parent->next = NEXT_ARRAY_COMMA;
|
112
|
+
break;
|
113
|
+
case NEXT_HASH_VALUE:
|
114
|
+
pi->hash_set_value(pi, parent->key, parent->klen, rval);
|
115
|
+
if (0 != parent->key && (parent->key < pi->json || pi->cur < parent->key)) {
|
116
|
+
xfree((char*)parent->key);
|
117
|
+
parent->key = 0;
|
118
|
+
}
|
119
|
+
parent->next = NEXT_HASH_COMMA;
|
120
|
+
break;
|
121
|
+
case NEXT_HASH_NEW:
|
122
|
+
case NEXT_HASH_KEY:
|
123
|
+
case NEXT_HASH_COMMA:
|
124
|
+
case NEXT_NONE:
|
125
|
+
case NEXT_ARRAY_COMMA:
|
126
|
+
case NEXT_HASH_COLON:
|
127
|
+
default:
|
128
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next));
|
129
|
+
break;
|
130
|
+
}
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
static void
|
135
|
+
read_null(ParseInfo pi) {
|
136
|
+
if ('u' == *pi->cur++ && 'l' == *pi->cur++ && 'l' == *pi->cur++) {
|
137
|
+
add_value(pi, Qnil);
|
138
|
+
} else {
|
139
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected null");
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
static void
|
144
|
+
read_true(ParseInfo pi) {
|
145
|
+
if ('r' == *pi->cur++ && 'u' == *pi->cur++ && 'e' == *pi->cur++) {
|
146
|
+
add_value(pi, Qtrue);
|
147
|
+
} else {
|
148
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected true");
|
149
|
+
}
|
150
|
+
}
|
151
|
+
|
152
|
+
static void
|
153
|
+
read_false(ParseInfo pi) {
|
154
|
+
if ('a' == *pi->cur++ && 'l' == *pi->cur++ && 's' == *pi->cur++ && 'e' == *pi->cur++) {
|
155
|
+
add_value(pi, Qfalse);
|
156
|
+
} else {
|
157
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected false");
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
static uint32_t
|
162
|
+
read_hex(ParseInfo pi, const char *h) {
|
163
|
+
uint32_t b = 0;
|
164
|
+
int i;
|
165
|
+
|
166
|
+
for (i = 0; i < 4; i++, h++) {
|
167
|
+
b = b << 4;
|
168
|
+
if ('0' <= *h && *h <= '9') {
|
169
|
+
b += *h - '0';
|
170
|
+
} else if ('A' <= *h && *h <= 'F') {
|
171
|
+
b += *h - 'A' + 10;
|
172
|
+
} else if ('a' <= *h && *h <= 'f') {
|
173
|
+
b += *h - 'a' + 10;
|
174
|
+
} else {
|
175
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hex character");
|
176
|
+
return 0;
|
177
|
+
}
|
178
|
+
}
|
179
|
+
return b;
|
180
|
+
}
|
181
|
+
|
182
|
+
static void
|
183
|
+
unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
|
184
|
+
if (0x0000007F >= code) {
|
185
|
+
buf_append(buf, (char)code);
|
186
|
+
} else if (0x000007FF >= code) {
|
187
|
+
buf_append(buf, 0xC0 | (code >> 6));
|
188
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
189
|
+
} else if (0x0000FFFF >= code) {
|
190
|
+
buf_append(buf, 0xE0 | (code >> 12));
|
191
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
192
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
193
|
+
} else if (0x001FFFFF >= code) {
|
194
|
+
buf_append(buf, 0xF0 | (code >> 18));
|
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 if (0x03FFFFFF >= code) {
|
199
|
+
buf_append(buf, 0xF8 | (code >> 24));
|
200
|
+
buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
|
201
|
+
buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
|
202
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
203
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
204
|
+
} else if (0x7FFFFFFF >= code) {
|
205
|
+
buf_append(buf, 0xFC | (code >> 30));
|
206
|
+
buf_append(buf, 0x80 | ((code >> 24) & 0x3F));
|
207
|
+
buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
|
208
|
+
buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
|
209
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
210
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
211
|
+
} else {
|
212
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid Unicode character");
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
// entered at /
|
217
|
+
static void
|
218
|
+
read_escaped_str(ParseInfo pi, const char *start) {
|
219
|
+
struct _Buf buf;
|
220
|
+
const char *s;
|
221
|
+
int cnt = (int)(pi->cur - start);
|
222
|
+
uint32_t code;
|
223
|
+
Val parent = stack_peek(&pi->stack);
|
224
|
+
|
225
|
+
buf_init(&buf);
|
226
|
+
if (0 < cnt) {
|
227
|
+
buf_append_string(&buf, start, cnt);
|
228
|
+
}
|
229
|
+
for (s = pi->cur; '"' != *s; s++) {
|
230
|
+
if ('\0' == *s) {
|
231
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
|
232
|
+
buf_cleanup(&buf);
|
233
|
+
return;
|
234
|
+
} else if ('\\' == *s) {
|
235
|
+
s++;
|
236
|
+
switch (*s) {
|
237
|
+
case 'n': buf_append(&buf, '\n'); break;
|
238
|
+
case 'r': buf_append(&buf, '\r'); break;
|
239
|
+
case 't': buf_append(&buf, '\t'); break;
|
240
|
+
case 'f': buf_append(&buf, '\f'); break;
|
241
|
+
case 'b': buf_append(&buf, '\b'); break;
|
242
|
+
case '"': buf_append(&buf, '"'); break;
|
243
|
+
case '/': buf_append(&buf, '/'); break;
|
244
|
+
case '\\': buf_append(&buf, '\\'); break;
|
245
|
+
case 'u':
|
246
|
+
s++;
|
247
|
+
if (0 == (code = read_hex(pi, s)) && err_has(&pi->err)) {
|
248
|
+
buf_cleanup(&buf);
|
249
|
+
return;
|
250
|
+
}
|
251
|
+
s += 3;
|
252
|
+
if (0x0000D800 <= code && code <= 0x0000DFFF) {
|
253
|
+
uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
|
254
|
+
uint32_t c2;
|
255
|
+
|
256
|
+
s++;
|
257
|
+
if ('\\' != *s || 'u' != *(s + 1)) {
|
258
|
+
pi->cur = s;
|
259
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
|
260
|
+
buf_cleanup(&buf);
|
261
|
+
return;
|
262
|
+
}
|
263
|
+
s += 2;
|
264
|
+
if (0 == (c2 = read_hex(pi, s)) && err_has(&pi->err)) {
|
265
|
+
buf_cleanup(&buf);
|
266
|
+
return;
|
267
|
+
}
|
268
|
+
s += 3;
|
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
|
+
pi->cur = s;
|
280
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
|
281
|
+
buf_cleanup(&buf);
|
282
|
+
return;
|
283
|
+
}
|
284
|
+
} else {
|
285
|
+
buf_append(&buf, *s);
|
286
|
+
}
|
287
|
+
}
|
288
|
+
if (0 == parent) {
|
289
|
+
pi->add_cstr(pi, buf.head, buf_len(&buf), start);
|
290
|
+
} else {
|
291
|
+
switch (parent->next) {
|
292
|
+
case NEXT_ARRAY_NEW:
|
293
|
+
case NEXT_ARRAY_ELEMENT:
|
294
|
+
pi->array_append_cstr(pi, buf.head, buf_len(&buf), start);
|
295
|
+
parent->next = NEXT_ARRAY_COMMA;
|
296
|
+
break;
|
297
|
+
case NEXT_HASH_NEW:
|
298
|
+
case NEXT_HASH_KEY:
|
299
|
+
// key will not be between pi->json and pi->cur.
|
300
|
+
parent->key = strdup(buf.head);
|
301
|
+
parent->klen = buf_len(&buf);
|
302
|
+
parent->k1 = *start;
|
303
|
+
parent->next = NEXT_HASH_COLON;
|
304
|
+
break;
|
305
|
+
case NEXT_HASH_VALUE:
|
306
|
+
pi->hash_set_cstr(pi, parent->key, parent->klen, buf.head, buf_len(&buf), start);
|
307
|
+
if (0 != parent->key && (parent->key < pi->json || pi->cur < parent->key)) {
|
308
|
+
xfree((char*)parent->key);
|
309
|
+
parent->key = 0;
|
310
|
+
}
|
311
|
+
parent->next = NEXT_HASH_COMMA;
|
312
|
+
break;
|
313
|
+
case NEXT_HASH_COMMA:
|
314
|
+
case NEXT_NONE:
|
315
|
+
case NEXT_ARRAY_COMMA:
|
316
|
+
case NEXT_HASH_COLON:
|
317
|
+
default:
|
318
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next));
|
319
|
+
break;
|
320
|
+
}
|
321
|
+
}
|
322
|
+
pi->cur = s + 1;
|
323
|
+
buf_cleanup(&buf);
|
324
|
+
}
|
325
|
+
|
326
|
+
static void
|
327
|
+
read_str(ParseInfo pi) {
|
328
|
+
const char *str = pi->cur;
|
329
|
+
Val parent = stack_peek(&pi->stack);
|
330
|
+
|
331
|
+
for (; '"' != *pi->cur; pi->cur++) {
|
332
|
+
if ('\0' == *pi->cur) {
|
333
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
|
334
|
+
return;
|
335
|
+
} else if ('\\' == *pi->cur) {
|
336
|
+
read_escaped_str(pi, str);
|
337
|
+
return;
|
338
|
+
}
|
339
|
+
}
|
340
|
+
if (0 == parent) { // simple add
|
341
|
+
pi->add_cstr(pi, str, pi->cur - str, str);
|
342
|
+
} else {
|
343
|
+
switch (parent->next) {
|
344
|
+
case NEXT_ARRAY_NEW:
|
345
|
+
case NEXT_ARRAY_ELEMENT:
|
346
|
+
pi->array_append_cstr(pi, str, pi->cur - str, str);
|
347
|
+
parent->next = NEXT_ARRAY_COMMA;
|
348
|
+
break;
|
349
|
+
case NEXT_HASH_NEW:
|
350
|
+
case NEXT_HASH_KEY:
|
351
|
+
parent->key = str;
|
352
|
+
parent->klen = pi->cur - str;
|
353
|
+
parent->k1 = *str;
|
354
|
+
parent->next = NEXT_HASH_COLON;
|
355
|
+
break;
|
356
|
+
case NEXT_HASH_VALUE:
|
357
|
+
pi->hash_set_cstr(pi, parent->key, parent->klen, str, pi->cur - str, str);
|
358
|
+
if (0 != parent->key && (parent->key < pi->json || pi->cur < parent->key)) {
|
359
|
+
xfree((char*)parent->key);
|
360
|
+
parent->key = 0;
|
361
|
+
}
|
362
|
+
parent->next = NEXT_HASH_COMMA;
|
363
|
+
break;
|
364
|
+
case NEXT_HASH_COMMA:
|
365
|
+
case NEXT_NONE:
|
366
|
+
case NEXT_ARRAY_COMMA:
|
367
|
+
case NEXT_HASH_COLON:
|
368
|
+
default:
|
369
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next));
|
370
|
+
break;
|
371
|
+
}
|
372
|
+
}
|
373
|
+
pi->cur++; // move past "
|
374
|
+
}
|
375
|
+
|
376
|
+
static void
|
377
|
+
read_num(ParseInfo pi) {
|
378
|
+
struct _NumInfo ni;
|
379
|
+
Val parent = stack_peek(&pi->stack);
|
380
|
+
int zero_cnt = 0;
|
381
|
+
|
382
|
+
ni.str = pi->cur;
|
383
|
+
ni.i = 0;
|
384
|
+
ni.num = 0;
|
385
|
+
ni.div = 1;
|
386
|
+
ni.len = 0;
|
387
|
+
ni.exp = 0;
|
388
|
+
ni.dec_cnt = 0;
|
389
|
+
ni.big = 0;
|
390
|
+
ni.infinity = 0;
|
391
|
+
ni.neg = 0;
|
392
|
+
|
393
|
+
if ('-' == *pi->cur) {
|
394
|
+
pi->cur++;
|
395
|
+
ni.neg = 1;
|
396
|
+
} else if ('+' == *pi->cur) {
|
397
|
+
pi->cur++;
|
398
|
+
}
|
399
|
+
if ('I' == *pi->cur) {
|
400
|
+
if (0 != strncmp("Infinity", pi->cur, 8)) {
|
401
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
402
|
+
return;
|
403
|
+
}
|
404
|
+
pi->cur += 8;
|
405
|
+
ni.infinity = 1;
|
406
|
+
return;
|
407
|
+
}
|
408
|
+
for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
|
409
|
+
ni.dec_cnt++;
|
410
|
+
if (ni.big) {
|
411
|
+
ni.big++;
|
412
|
+
} else {
|
413
|
+
int d = (*pi->cur - '0');
|
414
|
+
|
415
|
+
if (0 == d) {
|
416
|
+
zero_cnt++;
|
417
|
+
} else {
|
418
|
+
zero_cnt = 0;
|
419
|
+
}
|
420
|
+
ni.i = ni.i * 10 + d;
|
421
|
+
if (I64_MAX <= ni.i || DEC_MAX < ni.dec_cnt - zero_cnt) {
|
422
|
+
ni.big = 1;
|
423
|
+
}
|
424
|
+
}
|
425
|
+
}
|
426
|
+
if ('.' == *pi->cur) {
|
427
|
+
pi->cur++;
|
428
|
+
for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
|
429
|
+
int d = (*pi->cur - '0');
|
430
|
+
|
431
|
+
if (0 == d) {
|
432
|
+
zero_cnt++;
|
433
|
+
} else {
|
434
|
+
zero_cnt = 0;
|
435
|
+
}
|
436
|
+
ni.dec_cnt++;
|
437
|
+
ni.num = ni.num * 10 + d;
|
438
|
+
ni.div *= 10;
|
439
|
+
if (I64_MAX <= ni.div || DEC_MAX < ni.dec_cnt - zero_cnt) {
|
440
|
+
ni.big = 1;
|
441
|
+
}
|
442
|
+
}
|
443
|
+
}
|
444
|
+
if ('e' == *pi->cur || 'E' == *pi->cur) {
|
445
|
+
int eneg = 0;
|
446
|
+
|
447
|
+
pi->cur++;
|
448
|
+
if ('-' == *pi->cur) {
|
449
|
+
pi->cur++;
|
450
|
+
eneg = 1;
|
451
|
+
} else if ('+' == *pi->cur) {
|
452
|
+
pi->cur++;
|
453
|
+
}
|
454
|
+
for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
|
455
|
+
ni.exp = ni.exp * 10 + (*pi->cur - '0');
|
456
|
+
if (EXP_MAX <= ni.exp) {
|
457
|
+
ni.big = 1;
|
458
|
+
}
|
459
|
+
}
|
460
|
+
if (eneg) {
|
461
|
+
ni.exp = -ni.exp;
|
462
|
+
}
|
463
|
+
}
|
464
|
+
ni.dec_cnt -= zero_cnt;
|
465
|
+
ni.len = pi->cur - ni.str;
|
466
|
+
if (Yes == pi->options.bigdec_load) {
|
467
|
+
ni.big = 1;
|
468
|
+
}
|
469
|
+
if (0 == parent) {
|
470
|
+
pi->add_num(pi, &ni);
|
471
|
+
} else {
|
472
|
+
switch (parent->next) {
|
473
|
+
case NEXT_ARRAY_NEW:
|
474
|
+
case NEXT_ARRAY_ELEMENT:
|
475
|
+
pi->array_append_num(pi, &ni);
|
476
|
+
parent->next = NEXT_ARRAY_COMMA;
|
477
|
+
break;
|
478
|
+
case NEXT_HASH_VALUE:
|
479
|
+
pi->hash_set_num(pi, parent->key, parent->klen, &ni);
|
480
|
+
if (0 != parent->key && (parent->key < pi->json || pi->cur < parent->key)) {
|
481
|
+
xfree((char*)parent->key);
|
482
|
+
parent->key = 0;
|
483
|
+
}
|
484
|
+
parent->next = NEXT_HASH_COMMA;
|
485
|
+
break;
|
486
|
+
default:
|
487
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next));
|
488
|
+
break;
|
489
|
+
}
|
490
|
+
}
|
491
|
+
}
|
492
|
+
|
493
|
+
static void
|
494
|
+
array_start(ParseInfo pi) {
|
495
|
+
VALUE v = Qnil;
|
496
|
+
|
497
|
+
v = pi->start_array(pi);
|
498
|
+
stack_push(&pi->stack, v, NEXT_ARRAY_NEW);
|
499
|
+
}
|
500
|
+
|
501
|
+
static void
|
502
|
+
array_end(ParseInfo pi) {
|
503
|
+
Val array = stack_pop(&pi->stack);
|
504
|
+
|
505
|
+
if (0 == array) {
|
506
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected array close");
|
507
|
+
} else if (NEXT_ARRAY_COMMA != array->next && NEXT_ARRAY_NEW != array->next) {
|
508
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not an array close", oj_stack_next_string(array->next));
|
509
|
+
} else {
|
510
|
+
pi->end_array(pi);
|
511
|
+
add_value(pi, array->val);
|
512
|
+
}
|
513
|
+
}
|
514
|
+
|
515
|
+
static void
|
516
|
+
hash_start(ParseInfo pi) {
|
517
|
+
VALUE v = Qnil;
|
518
|
+
|
519
|
+
v = pi->start_hash(pi);
|
520
|
+
stack_push(&pi->stack, v, NEXT_HASH_NEW);
|
521
|
+
}
|
522
|
+
|
523
|
+
static void
|
524
|
+
hash_end(ParseInfo pi) {
|
525
|
+
Val hash = stack_peek(&pi->stack);
|
526
|
+
|
527
|
+
// leave hash on stack until just before
|
528
|
+
if (0 == hash) {
|
529
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected hash close");
|
530
|
+
} else if (NEXT_HASH_COMMA != hash->next && NEXT_HASH_NEW != hash->next) {
|
531
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a hash close", oj_stack_next_string(hash->next));
|
532
|
+
} else {
|
533
|
+
pi->end_hash(pi);
|
534
|
+
stack_pop(&pi->stack);
|
535
|
+
add_value(pi, hash->val);
|
536
|
+
}
|
537
|
+
}
|
538
|
+
|
539
|
+
static void
|
540
|
+
comma(ParseInfo pi) {
|
541
|
+
Val parent = stack_peek(&pi->stack);
|
542
|
+
|
543
|
+
if (0 == parent) {
|
544
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
|
545
|
+
} else if (NEXT_ARRAY_COMMA == parent->next) {
|
546
|
+
parent->next = NEXT_ARRAY_ELEMENT;
|
547
|
+
} else if (NEXT_HASH_COMMA == parent->next) {
|
548
|
+
parent->next = NEXT_HASH_KEY;
|
549
|
+
} else {
|
550
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
|
551
|
+
}
|
552
|
+
}
|
553
|
+
|
554
|
+
static void
|
555
|
+
colon(ParseInfo pi) {
|
556
|
+
Val parent = stack_peek(&pi->stack);
|
557
|
+
|
558
|
+
if (0 != parent && NEXT_HASH_COLON == parent->next) {
|
559
|
+
parent->next = NEXT_HASH_VALUE;
|
560
|
+
} else {
|
561
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected colon");
|
562
|
+
}
|
563
|
+
}
|
564
|
+
|
565
|
+
void
|
566
|
+
oj_parse2(ParseInfo pi) {
|
567
|
+
pi->cur = pi->json;
|
568
|
+
err_init(&pi->err);
|
569
|
+
stack_init(&pi->stack);
|
570
|
+
while (1) {
|
571
|
+
next_non_white(pi);
|
572
|
+
switch (*pi->cur++) {
|
573
|
+
case '{':
|
574
|
+
hash_start(pi);
|
575
|
+
break;
|
576
|
+
case '}':
|
577
|
+
hash_end(pi);
|
578
|
+
break;
|
579
|
+
case ':':
|
580
|
+
colon(pi);
|
581
|
+
break;
|
582
|
+
case '[':
|
583
|
+
array_start(pi);
|
584
|
+
break;
|
585
|
+
case ']':
|
586
|
+
array_end(pi);
|
587
|
+
break;
|
588
|
+
case ',':
|
589
|
+
comma(pi);
|
590
|
+
break;
|
591
|
+
case '"':
|
592
|
+
read_str(pi);
|
593
|
+
break;
|
594
|
+
case '+':
|
595
|
+
case '-':
|
596
|
+
case '0':
|
597
|
+
case '1':
|
598
|
+
case '2':
|
599
|
+
case '3':
|
600
|
+
case '4':
|
601
|
+
case '5':
|
602
|
+
case '6':
|
603
|
+
case '7':
|
604
|
+
case '8':
|
605
|
+
case '9':
|
606
|
+
case 'I':
|
607
|
+
pi->cur--;
|
608
|
+
read_num(pi);
|
609
|
+
break;
|
610
|
+
case 't':
|
611
|
+
read_true(pi);
|
612
|
+
break;
|
613
|
+
case 'f':
|
614
|
+
read_false(pi);
|
615
|
+
break;
|
616
|
+
case 'n':
|
617
|
+
read_null(pi);
|
618
|
+
break;
|
619
|
+
case '/':
|
620
|
+
skip_comment(pi);
|
621
|
+
break;
|
622
|
+
case '\0':
|
623
|
+
pi->cur--;
|
624
|
+
return;
|
625
|
+
default:
|
626
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
627
|
+
return;
|
628
|
+
}
|
629
|
+
if (err_has(&pi->err)) {
|
630
|
+
return;
|
631
|
+
}
|
632
|
+
}
|
633
|
+
}
|
634
|
+
|
635
|
+
VALUE
|
636
|
+
oj_num_as_value(NumInfo ni) {
|
637
|
+
VALUE rnum = Qnil;
|
638
|
+
|
639
|
+
if (ni->infinity) {
|
640
|
+
if (ni->neg) {
|
641
|
+
rnum = rb_float_new(-OJ_INFINITY);
|
642
|
+
} else {
|
643
|
+
rnum = rb_float_new(OJ_INFINITY);
|
644
|
+
}
|
645
|
+
} else if (1 == ni->div && 0 == ni->exp) { // fixnum
|
646
|
+
if (ni->big) {
|
647
|
+
if (256 > ni->len) {
|
648
|
+
char buf[256];
|
649
|
+
|
650
|
+
memcpy(buf, ni->str, ni->len);
|
651
|
+
buf[ni->len] = '\0';
|
652
|
+
rnum = rb_cstr_to_inum(buf, 10, 0);
|
653
|
+
} else {
|
654
|
+
char *buf = ALLOC_N(char, ni->len + 1);
|
655
|
+
|
656
|
+
memcpy(buf, ni->str, ni->len);
|
657
|
+
buf[ni->len] = '\0';
|
658
|
+
rnum = rb_cstr_to_inum(buf, 10, 0);
|
659
|
+
xfree(buf);
|
660
|
+
}
|
661
|
+
} else {
|
662
|
+
if (ni->neg) {
|
663
|
+
rnum = LONG2NUM(-ni->i);
|
664
|
+
} else {
|
665
|
+
rnum = LONG2NUM(ni->i);
|
666
|
+
}
|
667
|
+
}
|
668
|
+
} else { // decimal
|
669
|
+
if (ni->big) {
|
670
|
+
rnum = rb_funcall(oj_bigdecimal_class, oj_new_id, 1, rb_str_new(ni->str, ni->len));
|
671
|
+
} else {
|
672
|
+
double d = (double)ni->i + (double)ni->num / (double)ni->div;
|
673
|
+
|
674
|
+
if (ni->neg) {
|
675
|
+
d = -d;
|
676
|
+
}
|
677
|
+
if (0 != ni->exp) {
|
678
|
+
d *= pow(10.0, ni->exp);
|
679
|
+
}
|
680
|
+
rnum = rb_float_new(d);
|
681
|
+
}
|
682
|
+
}
|
683
|
+
return rnum;
|
684
|
+
}
|
685
|
+
|
686
|
+
void
|
687
|
+
oj_set_error_at(ParseInfo pi, VALUE err_clas, const char* file, int line, const char *format, ...) {
|
688
|
+
va_list ap;
|
689
|
+
char msg[128];
|
690
|
+
|
691
|
+
va_start(ap, format);
|
692
|
+
vsnprintf(msg, sizeof(msg) - 1, format, ap);
|
693
|
+
va_end(ap);
|
694
|
+
pi->err.clas = err_clas;
|
695
|
+
_oj_err_set_with_location(&pi->err, err_clas, msg, pi->json, pi->cur - 1, file, line);
|
696
|
+
}
|
697
|
+
|
698
|
+
static VALUE
|
699
|
+
protect_parse(VALUE pip) {
|
700
|
+
oj_parse2((ParseInfo)pip);
|
701
|
+
|
702
|
+
return Qnil;
|
703
|
+
}
|
704
|
+
|
705
|
+
VALUE
|
706
|
+
oj_pi_parse(int argc, VALUE *argv, ParseInfo pi, char *json) {
|
707
|
+
char *buf = 0;
|
708
|
+
VALUE input;
|
709
|
+
VALUE result = Qnil;
|
710
|
+
int line = 0;
|
711
|
+
int free_json = 0;
|
712
|
+
|
713
|
+
if (argc < 1) {
|
714
|
+
rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
|
715
|
+
}
|
716
|
+
input = argv[0];
|
717
|
+
if (2 == argc) {
|
718
|
+
oj_parse_options(argv[1], &pi->options);
|
719
|
+
}
|
720
|
+
pi->cbc = (void*)0;
|
721
|
+
if (0 != json) {
|
722
|
+
pi->json = json;
|
723
|
+
free_json = 1;
|
724
|
+
} else if (rb_type(input) == T_STRING) {
|
725
|
+
pi->json = StringValuePtr(input);
|
726
|
+
} else {
|
727
|
+
VALUE clas = rb_obj_class(input);
|
728
|
+
VALUE s;
|
729
|
+
|
730
|
+
if (oj_stringio_class == clas) {
|
731
|
+
s = rb_funcall2(input, oj_string_id, 0, 0);
|
732
|
+
pi->json = StringValuePtr(s);
|
733
|
+
#ifndef JRUBY_RUBY
|
734
|
+
#if !IS_WINDOWS
|
735
|
+
// JRuby gets confused with what is the real fileno.
|
736
|
+
} else if (rb_respond_to(input, oj_fileno_id) && Qnil != (s = rb_funcall(input, oj_fileno_id, 0))) {
|
737
|
+
int fd = FIX2INT(s);
|
738
|
+
ssize_t cnt;
|
739
|
+
size_t len = lseek(fd, 0, SEEK_END);
|
740
|
+
|
741
|
+
lseek(fd, 0, SEEK_SET);
|
742
|
+
buf = ALLOC_N(char, len + 1);
|
743
|
+
pi->json = buf;
|
744
|
+
if (0 >= (cnt = read(fd, (char*)pi->json, len)) || cnt != (ssize_t)len) {
|
745
|
+
if (0 != buf) {
|
746
|
+
xfree(buf);
|
747
|
+
}
|
748
|
+
rb_raise(rb_eIOError, "failed to read from IO Object.");
|
749
|
+
}
|
750
|
+
((char*)pi->json)[len] = '\0';
|
751
|
+
/* skip UTF-8 BOM if present */
|
752
|
+
if (0xEF == (uint8_t)*pi->json && 0xBB == (uint8_t)pi->json[1] && 0xBF == (uint8_t)pi->json[2]) {
|
753
|
+
pi->json += 3;
|
754
|
+
}
|
755
|
+
#endif
|
756
|
+
#endif
|
757
|
+
} else if (rb_respond_to(input, oj_read_id)) {
|
758
|
+
s = rb_funcall2(input, oj_read_id, 0, 0);
|
759
|
+
pi->json = StringValuePtr(s);
|
760
|
+
} else {
|
761
|
+
rb_raise(rb_eArgError, "strict_parse() expected a String or IO Object.");
|
762
|
+
}
|
763
|
+
}
|
764
|
+
if (Yes == pi->options.circular) {
|
765
|
+
pi->circ_array = oj_circ_array_new();
|
766
|
+
} else {
|
767
|
+
pi->circ_array = 0;
|
768
|
+
}
|
769
|
+
// GC can run at any time. When it runs any Object created by C will be
|
770
|
+
// freed. This usually only happens with large files but it does happen and
|
771
|
+
// it happens more frequently on Ruby 1.8.7.
|
772
|
+
#if HAS_GC_GUARD
|
773
|
+
rb_gc_disable();
|
774
|
+
#endif
|
775
|
+
rb_protect(protect_parse, (VALUE)pi, &line);
|
776
|
+
result = stack_head_val(&pi->stack);
|
777
|
+
#if HAS_GC_GUARD
|
778
|
+
RB_GC_GUARD(result);
|
779
|
+
rb_gc_enable();
|
780
|
+
#endif
|
781
|
+
// proceed with cleanup
|
782
|
+
if (0 != pi->circ_array) {
|
783
|
+
oj_circ_array_free(pi->circ_array);
|
784
|
+
}
|
785
|
+
if (0 != buf) {
|
786
|
+
xfree(buf);
|
787
|
+
} else if (free_json) {
|
788
|
+
xfree(json);
|
789
|
+
}
|
790
|
+
stack_cleanup(&pi->stack);
|
791
|
+
if (0 != line) {
|
792
|
+
rb_jump_tag(line);
|
793
|
+
}
|
794
|
+
if (err_has(&pi->err)) {
|
795
|
+
oj_err_raise(&pi->err);
|
796
|
+
}
|
797
|
+
return result;
|
798
|
+
}
|