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.

@@ -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
+ }
@@ -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__ */
@@ -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
+ }