oj 2.9.0 → 2.9.1
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 +7 -7
- data/README.md +9 -4
- data/ext/oj/buf.h +1 -1
- data/ext/oj/compat.c +8 -8
- data/ext/oj/dump.c +18 -3
- data/ext/oj/err.h +1 -1
- data/ext/oj/object.c +19 -10
- data/ext/oj/oj.c +37 -31
- data/ext/oj/oj.h +2 -0
- data/ext/oj/parse.c +10 -7
- data/ext/oj/parse.h +11 -1
- data/ext/oj/reader.c +244 -0
- data/ext/oj/reader.h +173 -0
- data/ext/oj/sparse.c +803 -0
- data/ext/oj/strict.c +6 -2
- data/ext/oj/val_stack.h +3 -0
- data/lib/oj/version.rb +1 -1
- data/test/a.rb +38 -0
- data/test/bug.rb +17 -0
- data/test/e.rb +12 -0
- data/test/foo.rb +24 -0
- data/test/lots.rb +68 -0
- data/test/mj.rb +48 -0
- data/test/perf_file.rb +64 -0
- data/test/perf_object.rb +2 -1
- data/test/perf_str.rb +38 -0
- data/test/perf_strictx.rb +97 -0
- data/test/struct.rb +29 -0
- data/test/test_file.rb +242 -0
- data/test/test_mimic.rb +4 -7
- data/test/test_strictx.rb +58 -0
- data/test/tests.rb +34 -17
- data/test/x.rb +59 -0
- metadata +40 -31
- data/test/perf1.rb +0 -64
- data/test/perf2.rb +0 -76
- data/test/perf_obj_old.rb +0 -213
data/ext/oj/reader.c
ADDED
@@ -0,0 +1,244 @@
|
|
1
|
+
/* reader.c
|
2
|
+
* Copyright (c) 2011, 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 <errno.h>
|
33
|
+
#include <stdio.h>
|
34
|
+
#include <strings.h>
|
35
|
+
#include <sys/types.h>
|
36
|
+
#if NEEDS_UIO
|
37
|
+
#include <sys/uio.h>
|
38
|
+
#endif
|
39
|
+
#include <unistd.h>
|
40
|
+
#include <time.h>
|
41
|
+
|
42
|
+
#include "ruby.h"
|
43
|
+
#include "oj.h"
|
44
|
+
#include "reader.h"
|
45
|
+
|
46
|
+
#define BUF_PAD 4
|
47
|
+
|
48
|
+
static VALUE rescue_cb(VALUE rdr, VALUE err);
|
49
|
+
static VALUE io_cb(VALUE rdr);
|
50
|
+
static VALUE partial_io_cb(VALUE rdr);
|
51
|
+
static int read_from_io(Reader reader);
|
52
|
+
static int read_from_fd(Reader reader);
|
53
|
+
static int read_from_io_partial(Reader reader);
|
54
|
+
static int read_from_str(Reader reader);
|
55
|
+
|
56
|
+
void
|
57
|
+
oj_reader_init(Reader reader, VALUE io, int fd) {
|
58
|
+
reader->head = reader->base;
|
59
|
+
*((char*)reader->head) = '\0';
|
60
|
+
reader->end = reader->head + sizeof(reader->base) - BUF_PAD;
|
61
|
+
reader->tail = reader->head;
|
62
|
+
reader->read_end = reader->head;
|
63
|
+
reader->pro = 0;
|
64
|
+
reader->str = 0;
|
65
|
+
reader->line = 1;
|
66
|
+
reader->col = 0;
|
67
|
+
reader->free_head = 0;
|
68
|
+
|
69
|
+
if (0 != fd) {
|
70
|
+
reader->read_func = read_from_fd;
|
71
|
+
reader->fd = fd;
|
72
|
+
} else if (rb_cString == rb_obj_class(io)) {
|
73
|
+
reader->read_func = read_from_str;
|
74
|
+
reader->in_str = StringValuePtr(io);
|
75
|
+
reader->head = (char*)reader->in_str;
|
76
|
+
reader->tail = reader->head;
|
77
|
+
reader->read_end = reader->head + RSTRING_LEN(io);
|
78
|
+
} else if (oj_stringio_class == rb_obj_class(io)) {
|
79
|
+
VALUE s = rb_funcall2(io, oj_string_id, 0, 0);
|
80
|
+
|
81
|
+
reader->read_func = read_from_str;
|
82
|
+
reader->in_str = StringValuePtr(s);
|
83
|
+
reader->head = (char*)reader->in_str;
|
84
|
+
reader->tail = reader->head;
|
85
|
+
reader->read_end = reader->head + RSTRING_LEN(s);
|
86
|
+
} else if (rb_respond_to(io, oj_readpartial_id)) {
|
87
|
+
VALUE rfd;
|
88
|
+
|
89
|
+
if (rb_respond_to(io, oj_fileno_id) && Qnil != (rfd = rb_funcall(io, oj_fileno_id, 0))) {
|
90
|
+
reader->read_func = read_from_fd;
|
91
|
+
reader->fd = FIX2INT(rfd);
|
92
|
+
} else {
|
93
|
+
reader->read_func = read_from_io_partial;
|
94
|
+
reader->io = io;
|
95
|
+
}
|
96
|
+
} else if (rb_respond_to(io, oj_read_id)) {
|
97
|
+
VALUE rfd;
|
98
|
+
|
99
|
+
if (rb_respond_to(io, oj_fileno_id) && Qnil != (rfd = rb_funcall(io, oj_fileno_id, 0))) {
|
100
|
+
reader->read_func = read_from_fd;
|
101
|
+
reader->fd = FIX2INT(rfd);
|
102
|
+
} else {
|
103
|
+
reader->read_func = read_from_io;
|
104
|
+
reader->io = io;
|
105
|
+
}
|
106
|
+
} else {
|
107
|
+
rb_raise(rb_eException, "parser io argument must respond to readpartial() or read().\n");
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
int
|
112
|
+
oj_reader_read(Reader reader) {
|
113
|
+
int err;
|
114
|
+
size_t shift = 0;
|
115
|
+
|
116
|
+
if (0 == reader->read_func) {
|
117
|
+
return -1;
|
118
|
+
}
|
119
|
+
// if there is not much room to read into, shift or realloc a larger buffer.
|
120
|
+
if (reader->head < reader->tail && 4096 > reader->end - reader->tail) {
|
121
|
+
if (0 == reader->pro) {
|
122
|
+
shift = reader->tail - reader->head;
|
123
|
+
} else {
|
124
|
+
shift = reader->pro - reader->head - 1; // leave one character so we can backup one
|
125
|
+
}
|
126
|
+
if (0 >= shift) { /* no space left so allocate more */
|
127
|
+
char *old = reader->head;
|
128
|
+
size_t size = reader->end - reader->head + BUF_PAD;
|
129
|
+
|
130
|
+
if (reader->head == reader->base) {
|
131
|
+
reader->head = ALLOC_N(char, size * 2);
|
132
|
+
memcpy(reader->head, old, size);
|
133
|
+
} else {
|
134
|
+
REALLOC_N(reader->head, char, size * 2);
|
135
|
+
}
|
136
|
+
reader->free_head = 1;
|
137
|
+
reader->end = reader->head + size * 2 - BUF_PAD;
|
138
|
+
reader->tail = reader->head + (reader->tail - old);
|
139
|
+
reader->read_end = reader->head + (reader->read_end - old);
|
140
|
+
if (0 != reader->pro) {
|
141
|
+
reader->pro = reader->head + (reader->pro - old);
|
142
|
+
}
|
143
|
+
if (0 != reader->str) {
|
144
|
+
reader->str = reader->head + (reader->str - old);
|
145
|
+
}
|
146
|
+
} else {
|
147
|
+
memmove(reader->head, reader->head + shift, reader->read_end - (reader->head + shift));
|
148
|
+
reader->tail -= shift;
|
149
|
+
reader->read_end -= shift;
|
150
|
+
if (0 != reader->pro) {
|
151
|
+
reader->pro -= shift;
|
152
|
+
}
|
153
|
+
if (0 != reader->str) {
|
154
|
+
reader->str -= shift;
|
155
|
+
}
|
156
|
+
}
|
157
|
+
}
|
158
|
+
err = reader->read_func(reader);
|
159
|
+
*reader->read_end = '\0';
|
160
|
+
|
161
|
+
return err;
|
162
|
+
}
|
163
|
+
|
164
|
+
static VALUE
|
165
|
+
rescue_cb(VALUE rbuf, VALUE err) {
|
166
|
+
#if (defined(RUBINIUS_RUBY) || (1 == RUBY_VERSION_MAJOR && 8 == RUBY_VERSION_MINOR))
|
167
|
+
if (rb_obj_class(err) != rb_eTypeError) {
|
168
|
+
#else
|
169
|
+
if (rb_obj_class(err) != rb_eEOFError) {
|
170
|
+
#endif
|
171
|
+
Reader reader = (Reader)rbuf;
|
172
|
+
|
173
|
+
rb_raise(err, "at line %d, column %d\n", reader->line, reader->col);
|
174
|
+
}
|
175
|
+
return Qfalse;
|
176
|
+
}
|
177
|
+
|
178
|
+
static VALUE
|
179
|
+
partial_io_cb(VALUE rbuf) {
|
180
|
+
Reader reader = (Reader)rbuf;
|
181
|
+
VALUE args[1];
|
182
|
+
VALUE rstr;
|
183
|
+
char *str;
|
184
|
+
size_t cnt;
|
185
|
+
|
186
|
+
args[0] = ULONG2NUM(reader->end - reader->tail);
|
187
|
+
rstr = rb_funcall2(reader->io, oj_readpartial_id, 1, args);
|
188
|
+
str = StringValuePtr(rstr);
|
189
|
+
cnt = strlen(str);
|
190
|
+
//printf("*** read %lu bytes, str: '%s'\n", cnt, str);
|
191
|
+
strcpy(reader->tail, str);
|
192
|
+
reader->read_end = reader->tail + cnt;
|
193
|
+
|
194
|
+
return Qtrue;
|
195
|
+
}
|
196
|
+
|
197
|
+
static VALUE
|
198
|
+
io_cb(VALUE rbuf) {
|
199
|
+
Reader reader = (Reader)rbuf;
|
200
|
+
VALUE args[1];
|
201
|
+
VALUE rstr;
|
202
|
+
char *str;
|
203
|
+
size_t cnt;
|
204
|
+
|
205
|
+
args[0] = ULONG2NUM(reader->end - reader->tail);
|
206
|
+
rstr = rb_funcall2(reader->io, oj_read_id, 1, args);
|
207
|
+
str = StringValuePtr(rstr);
|
208
|
+
cnt = strlen(str);
|
209
|
+
//printf("*** read %lu bytes, str: '%s'\n", cnt, str);
|
210
|
+
strcpy(reader->tail, str);
|
211
|
+
reader->read_end = reader->tail + cnt;
|
212
|
+
|
213
|
+
return Qtrue;
|
214
|
+
}
|
215
|
+
|
216
|
+
static int
|
217
|
+
read_from_io_partial(Reader reader) {
|
218
|
+
return (Qfalse == rb_rescue(partial_io_cb, (VALUE)reader, rescue_cb, (VALUE)reader));
|
219
|
+
}
|
220
|
+
|
221
|
+
static int
|
222
|
+
read_from_io(Reader reader) {
|
223
|
+
return (Qfalse == rb_rescue(io_cb, (VALUE)reader, rescue_cb, (VALUE)reader));
|
224
|
+
}
|
225
|
+
|
226
|
+
static int
|
227
|
+
read_from_fd(Reader reader) {
|
228
|
+
ssize_t cnt;
|
229
|
+
size_t max = reader->end - reader->tail;
|
230
|
+
|
231
|
+
cnt = read(reader->fd, reader->tail, max);
|
232
|
+
if (cnt < 0) {
|
233
|
+
return -1;
|
234
|
+
} else if (0 != cnt) {
|
235
|
+
reader->read_end = reader->tail + cnt;
|
236
|
+
}
|
237
|
+
return 0;
|
238
|
+
}
|
239
|
+
|
240
|
+
// This is only called when the end of the string is reached so just return -1.
|
241
|
+
static int
|
242
|
+
read_from_str(Reader reader) {
|
243
|
+
return -1;
|
244
|
+
}
|
data/ext/oj/reader.h
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
/* reader.h
|
2
|
+
* Copyright (c) 2011, 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
|
+
#ifndef __OJ_READER_H__
|
32
|
+
#define __OJ_READER_H__
|
33
|
+
|
34
|
+
typedef struct _Reader {
|
35
|
+
char base[0x00001000];
|
36
|
+
char *head;
|
37
|
+
char *end;
|
38
|
+
char *tail;
|
39
|
+
char *read_end; /* one past last character read */
|
40
|
+
char *pro; /* protection start, buffer can not slide past this point */
|
41
|
+
char *str; /* start of current string being read */
|
42
|
+
int line;
|
43
|
+
int col;
|
44
|
+
int free_head;
|
45
|
+
int (*read_func)(struct _Reader *reader);
|
46
|
+
union {
|
47
|
+
int fd;
|
48
|
+
VALUE io;
|
49
|
+
const char *in_str;
|
50
|
+
};
|
51
|
+
} *Reader;
|
52
|
+
|
53
|
+
extern void oj_reader_init(Reader reader, VALUE io, int fd);
|
54
|
+
extern int oj_reader_read(Reader reader);
|
55
|
+
|
56
|
+
static inline char
|
57
|
+
reader_get(Reader reader) {
|
58
|
+
//printf("*** drive get from '%s' from start: %ld buf: %p from read_end: %ld\n", reader->tail, reader->tail - reader->head, reader->head, reader->read_end - reader->tail);
|
59
|
+
if (reader->read_end <= reader->tail) {
|
60
|
+
if (0 != oj_reader_read(reader)) {
|
61
|
+
return '\0';
|
62
|
+
}
|
63
|
+
}
|
64
|
+
if ('\n' == *reader->tail) {
|
65
|
+
reader->line++;
|
66
|
+
reader->col = 0;
|
67
|
+
}
|
68
|
+
reader->col++;
|
69
|
+
|
70
|
+
return *reader->tail++;
|
71
|
+
}
|
72
|
+
|
73
|
+
static inline void
|
74
|
+
reader_backup(Reader reader) {
|
75
|
+
reader->tail--;
|
76
|
+
reader->col--;
|
77
|
+
if (0 >= reader->col) {
|
78
|
+
reader->line--;
|
79
|
+
// allow col to be negative since we never backup twice in a row
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
static inline void
|
84
|
+
reader_protect(Reader reader) {
|
85
|
+
reader->pro = reader->tail;
|
86
|
+
reader->str = reader->tail; // can't have str before pro
|
87
|
+
}
|
88
|
+
|
89
|
+
static inline void
|
90
|
+
reader_release(Reader reader) {
|
91
|
+
reader->pro = 0;
|
92
|
+
}
|
93
|
+
|
94
|
+
/* Starts by reading a character so it is safe to use with an empty or
|
95
|
+
* compacted buffer.
|
96
|
+
*/
|
97
|
+
static inline char
|
98
|
+
reader_next_non_white(Reader reader) {
|
99
|
+
char c;
|
100
|
+
|
101
|
+
while ('\0' != (c = reader_get(reader))) {
|
102
|
+
switch(c) {
|
103
|
+
case ' ':
|
104
|
+
case '\t':
|
105
|
+
case '\f':
|
106
|
+
case '\n':
|
107
|
+
case '\r':
|
108
|
+
break;
|
109
|
+
default:
|
110
|
+
return c;
|
111
|
+
}
|
112
|
+
}
|
113
|
+
return '\0';
|
114
|
+
}
|
115
|
+
|
116
|
+
/* Starts by reading a character so it is safe to use with an empty or
|
117
|
+
* compacted buffer.
|
118
|
+
*/
|
119
|
+
static inline char
|
120
|
+
reader_next_white(Reader reader) {
|
121
|
+
char c;
|
122
|
+
|
123
|
+
while ('\0' != (c = reader_get(reader))) {
|
124
|
+
switch(c) {
|
125
|
+
case ' ':
|
126
|
+
case '\t':
|
127
|
+
case '\f':
|
128
|
+
case '\n':
|
129
|
+
case '\r':
|
130
|
+
case '\0':
|
131
|
+
return c;
|
132
|
+
default:
|
133
|
+
break;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
return '\0';
|
137
|
+
}
|
138
|
+
|
139
|
+
static inline int
|
140
|
+
reader_expect(Reader reader, const char *s) {
|
141
|
+
for (; '\0' != *s; s++) {
|
142
|
+
if (reader_get(reader) != *s) {
|
143
|
+
return -1;
|
144
|
+
}
|
145
|
+
}
|
146
|
+
return 0;
|
147
|
+
}
|
148
|
+
|
149
|
+
static inline void
|
150
|
+
reader_cleanup(Reader reader) {
|
151
|
+
if (reader->free_head && 0 != reader->head) {
|
152
|
+
xfree(reader->head);
|
153
|
+
reader->head = 0;
|
154
|
+
reader->free_head = 0;
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
static inline int
|
159
|
+
is_white(char c) {
|
160
|
+
switch(c) {
|
161
|
+
case ' ':
|
162
|
+
case '\t':
|
163
|
+
case '\f':
|
164
|
+
case '\n':
|
165
|
+
case '\r':
|
166
|
+
return 1;
|
167
|
+
default:
|
168
|
+
break;
|
169
|
+
}
|
170
|
+
return 0;
|
171
|
+
}
|
172
|
+
|
173
|
+
#endif /* __OJ_READER_H__ */
|
data/ext/oj/sparse.c
ADDED
@@ -0,0 +1,803 @@
|
|
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 DEC_MAX 14
|
52
|
+
|
53
|
+
static void
|
54
|
+
skip_comment(ParseInfo pi) {
|
55
|
+
char c = reader_get(&pi->rd);
|
56
|
+
|
57
|
+
if ('*' == c) {
|
58
|
+
while ('\0' != (c = reader_get(&pi->rd))) {
|
59
|
+
if ('*' == c) {
|
60
|
+
c = reader_get(&pi->rd);
|
61
|
+
if ('/' == c) {
|
62
|
+
return;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
} else if ('/' == c) {
|
67
|
+
while ('\0' != (c = reader_get(&pi->rd))) {
|
68
|
+
switch (c) {
|
69
|
+
case '\n':
|
70
|
+
case '\r':
|
71
|
+
case '\f':
|
72
|
+
case '\0':
|
73
|
+
return;
|
74
|
+
default:
|
75
|
+
break;
|
76
|
+
}
|
77
|
+
}
|
78
|
+
} else {
|
79
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid comment format");
|
80
|
+
}
|
81
|
+
if ('\0' == c) {
|
82
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "comment not terminated");
|
83
|
+
return;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
static void
|
88
|
+
add_value(ParseInfo pi, VALUE rval) {
|
89
|
+
Val parent = stack_peek(&pi->stack);
|
90
|
+
|
91
|
+
if (0 == parent) { // simple add
|
92
|
+
pi->add_value(pi, rval);
|
93
|
+
} else {
|
94
|
+
switch (parent->next) {
|
95
|
+
case NEXT_ARRAY_NEW:
|
96
|
+
case NEXT_ARRAY_ELEMENT:
|
97
|
+
pi->array_append_value(pi, rval);
|
98
|
+
parent->next = NEXT_ARRAY_COMMA;
|
99
|
+
break;
|
100
|
+
case NEXT_HASH_VALUE:
|
101
|
+
pi->hash_set_value(pi, parent->key, parent->klen, rval);
|
102
|
+
if (parent->kalloc) {
|
103
|
+
xfree((char*)parent->key);
|
104
|
+
}
|
105
|
+
parent->key = 0;
|
106
|
+
parent->kalloc = 0;
|
107
|
+
parent->next = NEXT_HASH_COMMA;
|
108
|
+
break;
|
109
|
+
case NEXT_HASH_NEW:
|
110
|
+
case NEXT_HASH_KEY:
|
111
|
+
case NEXT_HASH_COMMA:
|
112
|
+
case NEXT_NONE:
|
113
|
+
case NEXT_ARRAY_COMMA:
|
114
|
+
case NEXT_HASH_COLON:
|
115
|
+
default:
|
116
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next));
|
117
|
+
break;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
static void
|
123
|
+
add_num_value(ParseInfo pi, NumInfo ni) {
|
124
|
+
Val parent = stack_peek(&pi->stack);
|
125
|
+
|
126
|
+
if (0 == parent) {
|
127
|
+
pi->add_num(pi, ni);
|
128
|
+
} else {
|
129
|
+
switch (parent->next) {
|
130
|
+
case NEXT_ARRAY_NEW:
|
131
|
+
case NEXT_ARRAY_ELEMENT:
|
132
|
+
pi->array_append_num(pi, ni);
|
133
|
+
parent->next = NEXT_ARRAY_COMMA;
|
134
|
+
break;
|
135
|
+
case NEXT_HASH_VALUE:
|
136
|
+
pi->hash_set_num(pi, parent->key, parent->klen, ni);
|
137
|
+
if (parent->kalloc) {
|
138
|
+
xfree((char*)parent->key);
|
139
|
+
}
|
140
|
+
parent->key = 0;
|
141
|
+
parent->kalloc = 0;
|
142
|
+
parent->next = NEXT_HASH_COMMA;
|
143
|
+
break;
|
144
|
+
default:
|
145
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s", oj_stack_next_string(parent->next));
|
146
|
+
break;
|
147
|
+
}
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
static void
|
152
|
+
read_true(ParseInfo pi) {
|
153
|
+
if (0 == reader_expect(&pi->rd, "rue")) {
|
154
|
+
add_value(pi, Qtrue);
|
155
|
+
} else {
|
156
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected true");
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
static void
|
161
|
+
read_false(ParseInfo pi) {
|
162
|
+
if (0 == reader_expect(&pi->rd, "alse")) {
|
163
|
+
add_value(pi, Qfalse);
|
164
|
+
} else {
|
165
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected false");
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
static uint32_t
|
170
|
+
read_hex(ParseInfo pi) {
|
171
|
+
uint32_t b = 0;
|
172
|
+
int i;
|
173
|
+
char c;
|
174
|
+
|
175
|
+
for (i = 0; i < 4; i++) {
|
176
|
+
c = reader_get(&pi->rd);
|
177
|
+
b = b << 4;
|
178
|
+
if ('0' <= c && c <= '9') {
|
179
|
+
b += c - '0';
|
180
|
+
} else if ('A' <= c && c <= 'F') {
|
181
|
+
b += c - 'A' + 10;
|
182
|
+
} else if ('a' <= c && c <= 'f') {
|
183
|
+
b += c - 'a' + 10;
|
184
|
+
} else {
|
185
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hex character");
|
186
|
+
return 0;
|
187
|
+
}
|
188
|
+
}
|
189
|
+
return b;
|
190
|
+
}
|
191
|
+
|
192
|
+
static void
|
193
|
+
unicode_to_chars(ParseInfo pi, Buf buf, uint32_t code) {
|
194
|
+
if (0x0000007F >= code) {
|
195
|
+
buf_append(buf, (char)code);
|
196
|
+
} else if (0x000007FF >= code) {
|
197
|
+
buf_append(buf, 0xC0 | (code >> 6));
|
198
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
199
|
+
} else if (0x0000FFFF >= code) {
|
200
|
+
buf_append(buf, 0xE0 | (code >> 12));
|
201
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
202
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
203
|
+
} else if (0x001FFFFF >= code) {
|
204
|
+
buf_append(buf, 0xF0 | (code >> 18));
|
205
|
+
buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
|
206
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
207
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
208
|
+
} else if (0x03FFFFFF >= code) {
|
209
|
+
buf_append(buf, 0xF8 | (code >> 24));
|
210
|
+
buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
|
211
|
+
buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
|
212
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
213
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
214
|
+
} else if (0x7FFFFFFF >= code) {
|
215
|
+
buf_append(buf, 0xFC | (code >> 30));
|
216
|
+
buf_append(buf, 0x80 | ((code >> 24) & 0x3F));
|
217
|
+
buf_append(buf, 0x80 | ((code >> 18) & 0x3F));
|
218
|
+
buf_append(buf, 0x80 | ((code >> 12) & 0x3F));
|
219
|
+
buf_append(buf, 0x80 | ((code >> 6) & 0x3F));
|
220
|
+
buf_append(buf, 0x80 | (0x3F & code));
|
221
|
+
} else {
|
222
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid Unicode character");
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
// entered at backslash
|
227
|
+
static void
|
228
|
+
read_escaped_str(ParseInfo pi) {
|
229
|
+
struct _Buf buf;
|
230
|
+
char c;
|
231
|
+
uint32_t code;
|
232
|
+
Val parent = stack_peek(&pi->stack);
|
233
|
+
|
234
|
+
buf_init(&buf);
|
235
|
+
if (pi->rd.str < pi->rd.tail) {
|
236
|
+
buf_append_string(&buf, pi->rd.str, pi->rd.tail - pi->rd.str);
|
237
|
+
}
|
238
|
+
while ('\"' != (c = reader_get(&pi->rd))) {
|
239
|
+
if ('\0' == c) {
|
240
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
|
241
|
+
buf_cleanup(&buf);
|
242
|
+
return;
|
243
|
+
} else if ('\\' == c) {
|
244
|
+
c = reader_get(&pi->rd);
|
245
|
+
switch (c) {
|
246
|
+
case 'n': buf_append(&buf, '\n'); break;
|
247
|
+
case 'r': buf_append(&buf, '\r'); break;
|
248
|
+
case 't': buf_append(&buf, '\t'); break;
|
249
|
+
case 'f': buf_append(&buf, '\f'); break;
|
250
|
+
case 'b': buf_append(&buf, '\b'); break;
|
251
|
+
case '"': buf_append(&buf, '"'); break;
|
252
|
+
case '/': buf_append(&buf, '/'); break;
|
253
|
+
case '\\': buf_append(&buf, '\\'); break;
|
254
|
+
case 'u':
|
255
|
+
if (0 == (code = read_hex(pi)) && err_has(&pi->err)) {
|
256
|
+
buf_cleanup(&buf);
|
257
|
+
return;
|
258
|
+
}
|
259
|
+
if (0x0000D800 <= code && code <= 0x0000DFFF) {
|
260
|
+
uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
|
261
|
+
uint32_t c2;
|
262
|
+
char ch2;
|
263
|
+
|
264
|
+
c = reader_get(&pi->rd);
|
265
|
+
ch2 = reader_get(&pi->rd);
|
266
|
+
if ('\\' != c || 'u' != ch2) {
|
267
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
|
268
|
+
buf_cleanup(&buf);
|
269
|
+
return;
|
270
|
+
}
|
271
|
+
if (0 == (c2 = read_hex(pi)) && err_has(&pi->err)) {
|
272
|
+
buf_cleanup(&buf);
|
273
|
+
return;
|
274
|
+
}
|
275
|
+
c2 = (c2 - 0x0000DC00) & 0x000003FF;
|
276
|
+
code = ((c1 << 10) | c2) + 0x00010000;
|
277
|
+
}
|
278
|
+
unicode_to_chars(pi, &buf, code);
|
279
|
+
if (err_has(&pi->err)) {
|
280
|
+
buf_cleanup(&buf);
|
281
|
+
return;
|
282
|
+
}
|
283
|
+
break;
|
284
|
+
default:
|
285
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
|
286
|
+
buf_cleanup(&buf);
|
287
|
+
return;
|
288
|
+
}
|
289
|
+
} else {
|
290
|
+
buf_append(&buf, c);
|
291
|
+
}
|
292
|
+
}
|
293
|
+
if (0 == parent) {
|
294
|
+
pi->add_cstr(pi, buf.head, buf_len(&buf), pi->rd.str);
|
295
|
+
} else {
|
296
|
+
switch (parent->next) {
|
297
|
+
case NEXT_ARRAY_NEW:
|
298
|
+
case NEXT_ARRAY_ELEMENT:
|
299
|
+
pi->array_append_cstr(pi, buf.head, buf_len(&buf), pi->rd.str);
|
300
|
+
parent->next = NEXT_ARRAY_COMMA;
|
301
|
+
break;
|
302
|
+
case NEXT_HASH_NEW:
|
303
|
+
case NEXT_HASH_KEY:
|
304
|
+
parent->key = strdup(buf.head);
|
305
|
+
parent->klen = buf_len(&buf);
|
306
|
+
parent->k1 = *pi->rd.str;
|
307
|
+
parent->next = NEXT_HASH_COLON;
|
308
|
+
break;
|
309
|
+
case NEXT_HASH_VALUE:
|
310
|
+
pi->hash_set_cstr(pi, parent->key, parent->klen, buf.head, buf_len(&buf), pi->rd.str);
|
311
|
+
if (parent->kalloc) {
|
312
|
+
xfree((char*)parent->key);
|
313
|
+
}
|
314
|
+
parent->key = 0;
|
315
|
+
parent->kalloc = 0;
|
316
|
+
parent->next = NEXT_HASH_COMMA;
|
317
|
+
break;
|
318
|
+
case NEXT_HASH_COMMA:
|
319
|
+
case NEXT_NONE:
|
320
|
+
case NEXT_ARRAY_COMMA:
|
321
|
+
case NEXT_HASH_COLON:
|
322
|
+
default:
|
323
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next));
|
324
|
+
break;
|
325
|
+
}
|
326
|
+
}
|
327
|
+
buf_cleanup(&buf);
|
328
|
+
}
|
329
|
+
|
330
|
+
static void
|
331
|
+
read_str(ParseInfo pi) {
|
332
|
+
Val parent = stack_peek(&pi->stack);
|
333
|
+
char c;
|
334
|
+
|
335
|
+
reader_protect(&pi->rd);
|
336
|
+
while ('\"' != (c = reader_get(&pi->rd))) {
|
337
|
+
if ('\0' == c) {
|
338
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
|
339
|
+
return;
|
340
|
+
} else if ('\\' == c) {
|
341
|
+
reader_backup(&pi->rd);
|
342
|
+
read_escaped_str(pi);
|
343
|
+
reader_release(&pi->rd);
|
344
|
+
return;
|
345
|
+
}
|
346
|
+
}
|
347
|
+
if (0 == parent) { // simple add
|
348
|
+
pi->add_cstr(pi, pi->rd.str, pi->rd.tail - pi->rd.str - 1, pi->rd.str);
|
349
|
+
} else {
|
350
|
+
switch (parent->next) {
|
351
|
+
case NEXT_ARRAY_NEW:
|
352
|
+
case NEXT_ARRAY_ELEMENT:
|
353
|
+
pi->array_append_cstr(pi, pi->rd.str, pi->rd.tail - pi->rd.str - 1, pi->rd.str);
|
354
|
+
parent->next = NEXT_ARRAY_COMMA;
|
355
|
+
break;
|
356
|
+
case NEXT_HASH_NEW:
|
357
|
+
case NEXT_HASH_KEY:
|
358
|
+
parent->klen = pi->rd.tail - pi->rd.str - 1;
|
359
|
+
if (sizeof(parent->karray) <= parent->klen) {
|
360
|
+
parent->key = strndup(pi->rd.str, parent->klen);
|
361
|
+
parent->kalloc = 1;
|
362
|
+
} else {
|
363
|
+
memcpy(parent->karray, pi->rd.str, parent->klen);
|
364
|
+
parent->karray[parent->klen] = '\0';
|
365
|
+
parent->key = parent->karray;
|
366
|
+
parent->kalloc = 0;
|
367
|
+
}
|
368
|
+
parent->k1 = *pi->rd.str;
|
369
|
+
parent->next = NEXT_HASH_COLON;
|
370
|
+
break;
|
371
|
+
case NEXT_HASH_VALUE:
|
372
|
+
pi->hash_set_cstr(pi, parent->key, parent->klen, pi->rd.str, pi->rd.tail - pi->rd.str - 1, pi->rd.str);
|
373
|
+
if (parent->kalloc) {
|
374
|
+
xfree((char*)parent->key);
|
375
|
+
}
|
376
|
+
parent->key = 0;
|
377
|
+
parent->kalloc = 0;
|
378
|
+
parent->next = NEXT_HASH_COMMA;
|
379
|
+
break;
|
380
|
+
case NEXT_HASH_COMMA:
|
381
|
+
case NEXT_NONE:
|
382
|
+
case NEXT_ARRAY_COMMA:
|
383
|
+
case NEXT_HASH_COLON:
|
384
|
+
default:
|
385
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a string", oj_stack_next_string(parent->next));
|
386
|
+
break;
|
387
|
+
}
|
388
|
+
}
|
389
|
+
reader_release(&pi->rd);
|
390
|
+
}
|
391
|
+
|
392
|
+
static void
|
393
|
+
read_num(ParseInfo pi) {
|
394
|
+
struct _NumInfo ni;
|
395
|
+
int zero_cnt = 0;
|
396
|
+
char c;
|
397
|
+
|
398
|
+
reader_protect(&pi->rd);
|
399
|
+
ni.i = 0;
|
400
|
+
ni.num = 0;
|
401
|
+
ni.div = 1;
|
402
|
+
ni.len = 0;
|
403
|
+
ni.exp = 0;
|
404
|
+
ni.dec_cnt = 0;
|
405
|
+
ni.big = 0;
|
406
|
+
ni.infinity = 0;
|
407
|
+
ni.nan = 0;
|
408
|
+
ni.neg = 0;
|
409
|
+
ni.no_big = (FloatDec == pi->options.bigdec_load);
|
410
|
+
c = reader_get(&pi->rd);
|
411
|
+
if ('-' == c) {
|
412
|
+
c = reader_get(&pi->rd);
|
413
|
+
ni.neg = 1;
|
414
|
+
} else if ('+' == c) {
|
415
|
+
c = reader_get(&pi->rd);
|
416
|
+
}
|
417
|
+
if ('I' == c) {
|
418
|
+
if (0 != reader_expect(&pi->rd, "nfinity")) {
|
419
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
420
|
+
return;
|
421
|
+
}
|
422
|
+
ni.infinity = 1;
|
423
|
+
} else {
|
424
|
+
for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) {
|
425
|
+
ni.dec_cnt++;
|
426
|
+
if (ni.big) {
|
427
|
+
ni.big++;
|
428
|
+
} else {
|
429
|
+
int d = (c - '0');
|
430
|
+
|
431
|
+
if (0 == d) {
|
432
|
+
zero_cnt++;
|
433
|
+
} else {
|
434
|
+
zero_cnt = 0;
|
435
|
+
}
|
436
|
+
ni.i = ni.i * 10 + d;
|
437
|
+
if (LONG_MAX <= ni.i || DEC_MAX < ni.dec_cnt - zero_cnt) {
|
438
|
+
ni.big = 1;
|
439
|
+
}
|
440
|
+
}
|
441
|
+
}
|
442
|
+
if ('.' == c) {
|
443
|
+
c = reader_get(&pi->rd);
|
444
|
+
for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) {
|
445
|
+
int d = (c - '0');
|
446
|
+
|
447
|
+
if (0 == d) {
|
448
|
+
zero_cnt++;
|
449
|
+
} else {
|
450
|
+
zero_cnt = 0;
|
451
|
+
}
|
452
|
+
ni.dec_cnt++;
|
453
|
+
ni.num = ni.num * 10 + d;
|
454
|
+
ni.div *= 10;
|
455
|
+
if (LONG_MAX <= ni.div || DEC_MAX < ni.dec_cnt - zero_cnt) {
|
456
|
+
ni.big = 1;
|
457
|
+
}
|
458
|
+
}
|
459
|
+
}
|
460
|
+
if ('e' == c || 'E' == c) {
|
461
|
+
int eneg = 0;
|
462
|
+
|
463
|
+
c = reader_get(&pi->rd);
|
464
|
+
if ('-' == c) {
|
465
|
+
c = reader_get(&pi->rd);
|
466
|
+
eneg = 1;
|
467
|
+
} else if ('+' == c) {
|
468
|
+
c = reader_get(&pi->rd);
|
469
|
+
}
|
470
|
+
for (; '0' <= c && c <= '9'; c = reader_get(&pi->rd)) {
|
471
|
+
ni.exp = ni.exp * 10 + (c - '0');
|
472
|
+
if (EXP_MAX <= ni.exp) {
|
473
|
+
ni.big = 1;
|
474
|
+
}
|
475
|
+
}
|
476
|
+
if (eneg) {
|
477
|
+
ni.exp = -ni.exp;
|
478
|
+
}
|
479
|
+
}
|
480
|
+
ni.dec_cnt -= zero_cnt;
|
481
|
+
ni.len = pi->rd.tail - pi->rd.str;
|
482
|
+
reader_backup(&pi->rd);
|
483
|
+
}
|
484
|
+
if (BigDec == pi->options.bigdec_load) {
|
485
|
+
ni.big = 1;
|
486
|
+
}
|
487
|
+
ni.str = pi->rd.str;
|
488
|
+
ni.len = pi->rd.tail - pi->rd.str;
|
489
|
+
add_num_value(pi, &ni);
|
490
|
+
reader_release(&pi->rd);
|
491
|
+
}
|
492
|
+
|
493
|
+
static void
|
494
|
+
read_nan(ParseInfo pi) {
|
495
|
+
struct _NumInfo ni;
|
496
|
+
char c;
|
497
|
+
|
498
|
+
ni.str = pi->rd.str;
|
499
|
+
ni.i = 0;
|
500
|
+
ni.num = 0;
|
501
|
+
ni.div = 1;
|
502
|
+
ni.len = 0;
|
503
|
+
ni.exp = 0;
|
504
|
+
ni.dec_cnt = 0;
|
505
|
+
ni.big = 0;
|
506
|
+
ni.infinity = 0;
|
507
|
+
ni.nan = 1;
|
508
|
+
ni.neg = 0;
|
509
|
+
ni.no_big = (FloatDec == pi->options.bigdec_load);
|
510
|
+
|
511
|
+
if ('a' != reader_get(&pi->rd) ||
|
512
|
+
('N' != (c = reader_get(&pi->rd)) && 'n' != c)) {
|
513
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
514
|
+
return;
|
515
|
+
}
|
516
|
+
if (BigDec == pi->options.bigdec_load) {
|
517
|
+
ni.big = 1;
|
518
|
+
}
|
519
|
+
add_num_value(pi, &ni);
|
520
|
+
}
|
521
|
+
|
522
|
+
static void
|
523
|
+
array_start(ParseInfo pi) {
|
524
|
+
VALUE v = pi->start_array(pi);
|
525
|
+
|
526
|
+
stack_push(&pi->stack, v, NEXT_ARRAY_NEW);
|
527
|
+
}
|
528
|
+
|
529
|
+
static void
|
530
|
+
array_end(ParseInfo pi) {
|
531
|
+
Val array = stack_pop(&pi->stack);
|
532
|
+
|
533
|
+
if (0 == array) {
|
534
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected array close");
|
535
|
+
} else if (NEXT_ARRAY_COMMA != array->next && NEXT_ARRAY_NEW != array->next) {
|
536
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not an array close", oj_stack_next_string(array->next));
|
537
|
+
} else {
|
538
|
+
pi->end_array(pi);
|
539
|
+
add_value(pi, array->val);
|
540
|
+
}
|
541
|
+
}
|
542
|
+
|
543
|
+
static void
|
544
|
+
hash_start(ParseInfo pi) {
|
545
|
+
volatile VALUE v = pi->start_hash(pi);
|
546
|
+
|
547
|
+
stack_push(&pi->stack, v, NEXT_HASH_NEW);
|
548
|
+
}
|
549
|
+
|
550
|
+
static void
|
551
|
+
hash_end(ParseInfo pi) {
|
552
|
+
volatile Val hash = stack_peek(&pi->stack);
|
553
|
+
|
554
|
+
// leave hash on stack until just before
|
555
|
+
if (0 == hash) {
|
556
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected hash close");
|
557
|
+
} else if (NEXT_HASH_COMMA != hash->next && NEXT_HASH_NEW != hash->next) {
|
558
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected %s, not a hash close", oj_stack_next_string(hash->next));
|
559
|
+
} else {
|
560
|
+
pi->end_hash(pi);
|
561
|
+
stack_pop(&pi->stack);
|
562
|
+
add_value(pi, hash->val);
|
563
|
+
}
|
564
|
+
}
|
565
|
+
|
566
|
+
static void
|
567
|
+
comma(ParseInfo pi) {
|
568
|
+
Val parent = stack_peek(&pi->stack);
|
569
|
+
|
570
|
+
if (0 == parent) {
|
571
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
|
572
|
+
} else if (NEXT_ARRAY_COMMA == parent->next) {
|
573
|
+
parent->next = NEXT_ARRAY_ELEMENT;
|
574
|
+
} else if (NEXT_HASH_COMMA == parent->next) {
|
575
|
+
parent->next = NEXT_HASH_KEY;
|
576
|
+
} else {
|
577
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected comma");
|
578
|
+
}
|
579
|
+
}
|
580
|
+
|
581
|
+
static void
|
582
|
+
colon(ParseInfo pi) {
|
583
|
+
Val parent = stack_peek(&pi->stack);
|
584
|
+
|
585
|
+
if (0 != parent && NEXT_HASH_COLON == parent->next) {
|
586
|
+
parent->next = NEXT_HASH_VALUE;
|
587
|
+
} else {
|
588
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected colon");
|
589
|
+
}
|
590
|
+
}
|
591
|
+
|
592
|
+
void
|
593
|
+
oj_sparse2(ParseInfo pi) {
|
594
|
+
char c;
|
595
|
+
|
596
|
+
err_init(&pi->err);
|
597
|
+
while (1) {
|
598
|
+
c = reader_next_non_white(&pi->rd);
|
599
|
+
switch (c) {
|
600
|
+
case '{':
|
601
|
+
hash_start(pi);
|
602
|
+
break;
|
603
|
+
case '}':
|
604
|
+
hash_end(pi);
|
605
|
+
break;
|
606
|
+
case ':':
|
607
|
+
colon(pi);
|
608
|
+
break;
|
609
|
+
case '[':
|
610
|
+
array_start(pi);
|
611
|
+
break;
|
612
|
+
case ']':
|
613
|
+
array_end(pi);
|
614
|
+
break;
|
615
|
+
case ',':
|
616
|
+
comma(pi);
|
617
|
+
break;
|
618
|
+
case '"':
|
619
|
+
read_str(pi);
|
620
|
+
break;
|
621
|
+
case '+':
|
622
|
+
case '-':
|
623
|
+
case '0':
|
624
|
+
case '1':
|
625
|
+
case '2':
|
626
|
+
case '3':
|
627
|
+
case '4':
|
628
|
+
case '5':
|
629
|
+
case '6':
|
630
|
+
case '7':
|
631
|
+
case '8':
|
632
|
+
case '9':
|
633
|
+
case 'I':
|
634
|
+
reader_backup(&pi->rd);
|
635
|
+
read_num(pi);
|
636
|
+
break;
|
637
|
+
case 'N':
|
638
|
+
read_nan(pi);
|
639
|
+
break;
|
640
|
+
case 't':
|
641
|
+
read_true(pi);
|
642
|
+
break;
|
643
|
+
case 'f':
|
644
|
+
read_false(pi);
|
645
|
+
break;
|
646
|
+
case 'n':
|
647
|
+
c = reader_get(&pi->rd);
|
648
|
+
if ('u' == c) {
|
649
|
+
if (0 == reader_expect(&pi->rd, "ll")) {
|
650
|
+
add_value(pi, Qnil);
|
651
|
+
} else {
|
652
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected null");
|
653
|
+
return;
|
654
|
+
}
|
655
|
+
} else if ('a' == c) {
|
656
|
+
struct _NumInfo ni;
|
657
|
+
|
658
|
+
c = reader_get(&pi->rd);
|
659
|
+
if ('N' != c && 'n' != c) {
|
660
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "expected NaN");
|
661
|
+
return;
|
662
|
+
}
|
663
|
+
ni.str = pi->rd.str;
|
664
|
+
ni.i = 0;
|
665
|
+
ni.num = 0;
|
666
|
+
ni.div = 1;
|
667
|
+
ni.len = 0;
|
668
|
+
ni.exp = 0;
|
669
|
+
ni.dec_cnt = 0;
|
670
|
+
ni.big = 0;
|
671
|
+
ni.infinity = 0;
|
672
|
+
ni.nan = 1;
|
673
|
+
ni.neg = 0;
|
674
|
+
ni.no_big = (FloatDec == pi->options.bigdec_load);
|
675
|
+
add_num_value(pi, &ni);
|
676
|
+
} else {
|
677
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid token");
|
678
|
+
return;
|
679
|
+
}
|
680
|
+
break;
|
681
|
+
case '/':
|
682
|
+
skip_comment(pi);
|
683
|
+
break;
|
684
|
+
case '\0':
|
685
|
+
return;
|
686
|
+
default:
|
687
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
|
688
|
+
return;
|
689
|
+
}
|
690
|
+
if (err_has(&pi->err)) {
|
691
|
+
return;
|
692
|
+
}
|
693
|
+
if (Qundef != pi->proc && stack_empty(&pi->stack)) {
|
694
|
+
if (Qnil == pi->proc) {
|
695
|
+
rb_yield(stack_head_val(&pi->stack));
|
696
|
+
} else {
|
697
|
+
#if HAS_PROC_WITH_BLOCK
|
698
|
+
VALUE args[1];
|
699
|
+
|
700
|
+
*args = stack_head_val(&pi->stack);
|
701
|
+
rb_proc_call_with_block(pi->proc, 1, args, Qnil);
|
702
|
+
#else
|
703
|
+
oj_set_error_at(pi, rb_eNotImpError, __FILE__, __LINE__,
|
704
|
+
"Calling a Proc with a block not supported in this version. Use func() {|x| } syntax instead.");
|
705
|
+
return;
|
706
|
+
#endif
|
707
|
+
}
|
708
|
+
}
|
709
|
+
}
|
710
|
+
}
|
711
|
+
|
712
|
+
static VALUE
|
713
|
+
protect_parse(VALUE pip) {
|
714
|
+
oj_sparse2((ParseInfo)pip);
|
715
|
+
|
716
|
+
return Qnil;
|
717
|
+
}
|
718
|
+
|
719
|
+
VALUE
|
720
|
+
oj_pi_sparse(int argc, VALUE *argv, ParseInfo pi, int fd) {
|
721
|
+
volatile VALUE input;
|
722
|
+
volatile VALUE wrapped_stack;
|
723
|
+
VALUE result = Qnil;
|
724
|
+
int line = 0;
|
725
|
+
|
726
|
+
if (argc < 1) {
|
727
|
+
rb_raise(rb_eArgError, "Wrong number of arguments to parse.");
|
728
|
+
}
|
729
|
+
input = argv[0];
|
730
|
+
if (2 == argc) {
|
731
|
+
oj_parse_options(argv[1], &pi->options);
|
732
|
+
}
|
733
|
+
if (Qnil == input && Yes == pi->options.nilnil) {
|
734
|
+
return Qnil;
|
735
|
+
}
|
736
|
+
if (rb_block_given_p()) {
|
737
|
+
pi->proc = Qnil;
|
738
|
+
} else {
|
739
|
+
pi->proc = Qundef;
|
740
|
+
}
|
741
|
+
pi->cbc = (void*)0;
|
742
|
+
|
743
|
+
oj_reader_init(&pi->rd, input, fd);
|
744
|
+
pi->json = 0; // indicates reader is in use
|
745
|
+
|
746
|
+
if (Yes == pi->options.circular) {
|
747
|
+
pi->circ_array = oj_circ_array_new();
|
748
|
+
} else {
|
749
|
+
pi->circ_array = 0;
|
750
|
+
}
|
751
|
+
if (No == pi->options.allow_gc) {
|
752
|
+
rb_gc_disable();
|
753
|
+
}
|
754
|
+
// GC can run at any time. When it runs any Object created by C will be
|
755
|
+
// freed. We protect against this by wrapping the value stack in a ruby
|
756
|
+
// data object and poviding a mark function for ruby objects on the
|
757
|
+
// value stack (while it is in scope).
|
758
|
+
wrapped_stack = oj_stack_init(&pi->stack);
|
759
|
+
rb_protect(protect_parse, (VALUE)pi, &line);
|
760
|
+
result = stack_head_val(&pi->stack);
|
761
|
+
DATA_PTR(wrapped_stack) = 0;
|
762
|
+
if (No == pi->options.allow_gc) {
|
763
|
+
rb_gc_enable();
|
764
|
+
}
|
765
|
+
if (!err_has(&pi->err)) {
|
766
|
+
// If the stack is not empty then the JSON terminated early.
|
767
|
+
Val v;
|
768
|
+
|
769
|
+
if (0 != (v = stack_peek(&pi->stack))) {
|
770
|
+
switch (v->next) {
|
771
|
+
case NEXT_ARRAY_NEW:
|
772
|
+
case NEXT_ARRAY_ELEMENT:
|
773
|
+
case NEXT_ARRAY_COMMA:
|
774
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Array not terminated");
|
775
|
+
break;
|
776
|
+
case NEXT_HASH_NEW:
|
777
|
+
case NEXT_HASH_KEY:
|
778
|
+
case NEXT_HASH_COLON:
|
779
|
+
case NEXT_HASH_VALUE:
|
780
|
+
case NEXT_HASH_COMMA:
|
781
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Hash/Object not terminated");
|
782
|
+
break;
|
783
|
+
default:
|
784
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not terminated");
|
785
|
+
}
|
786
|
+
}
|
787
|
+
}
|
788
|
+
// proceed with cleanup
|
789
|
+
if (0 != pi->circ_array) {
|
790
|
+
oj_circ_array_free(pi->circ_array);
|
791
|
+
}
|
792
|
+
stack_cleanup(&pi->stack);
|
793
|
+
if (0 != fd) {
|
794
|
+
close(fd);
|
795
|
+
}
|
796
|
+
if (0 != line) {
|
797
|
+
rb_jump_tag(line);
|
798
|
+
}
|
799
|
+
if (err_has(&pi->err)) {
|
800
|
+
oj_err_raise(&pi->err);
|
801
|
+
}
|
802
|
+
return result;
|
803
|
+
}
|