oj 3.17.3 → 3.17.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +75 -0
- data/ext/oj/cache.c +17 -1
- data/ext/oj/cache.h +5 -0
- data/ext/oj/circarray.c +6 -2
- data/ext/oj/compat.c +2 -2
- data/ext/oj/custom.c +5 -3
- data/ext/oj/dump.c +27 -12
- data/ext/oj/dump.h +10 -0
- data/ext/oj/dump_compat.c +11 -4
- data/ext/oj/dump_object.c +20 -10
- data/ext/oj/extconf.rb +1 -0
- data/ext/oj/fast.c +87 -44
- data/ext/oj/intern.c +6 -1
- data/ext/oj/object.c +26 -15
- data/ext/oj/odd.c +6 -2
- data/ext/oj/oj.c +224 -10
- data/ext/oj/oj.h +5 -1
- data/ext/oj/parse.c +17 -3
- data/ext/oj/parse.h +0 -1
- data/ext/oj/parser.c +120 -35
- data/ext/oj/rails.c +166 -40
- data/ext/oj/reader.c +18 -6
- data/ext/oj/resolve.c +11 -2
- data/ext/oj/rxclass.c +16 -0
- data/ext/oj/rxclass.h +1 -0
- data/ext/oj/safe.c +62 -48
- data/ext/oj/safe.h +14 -2
- data/ext/oj/saj.c +46 -10
- data/ext/oj/sparse.c +16 -6
- data/ext/oj/stream_writer.c +16 -1
- data/ext/oj/string_writer.c +23 -7
- data/ext/oj/usual.c +5 -1
- data/ext/oj/util.c +27 -0
- data/ext/oj/util.h +2 -1
- data/ext/oj/val_stack.h +24 -5
- data/ext/oj/wab.c +14 -26
- data/lib/oj/version.rb +1 -1
- data/pages/Compatibility.md +1 -1
- data/pages/Custom.md +2 -3
- data/pages/InstallOptions.md +8 -6
- data/pages/Modes.md +1 -1
- data/pages/Options.md +5 -6
- data/pages/Parser.md +2 -2
- data/pages/Rails.md +2 -0
- data/pages/Security.md +21 -7
- metadata +2 -2
data/ext/oj/reader.c
CHANGED
|
@@ -88,8 +88,8 @@ void oj_reader_init(Reader reader, VALUE io, int fd, bool to_s) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
int oj_reader_read(Reader reader) {
|
|
91
|
-
int
|
|
92
|
-
|
|
91
|
+
int err;
|
|
92
|
+
ptrdiff_t shift = 0;
|
|
93
93
|
|
|
94
94
|
if (0 == reader->read_func) {
|
|
95
95
|
return -1;
|
|
@@ -164,8 +164,14 @@ static VALUE partial_io_cb(VALUE rbuf) {
|
|
|
164
164
|
}
|
|
165
165
|
str = StringValuePtr(rstr);
|
|
166
166
|
cnt = RSTRING_LEN(rstr);
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
if (cnt > (size_t)(reader->end - reader->tail)) {
|
|
168
|
+
// A misbehaving IO returned more than the requested number of bytes.
|
|
169
|
+
// Copying it in with strcpy() would overflow the reader buffer.
|
|
170
|
+
rb_raise(rb_eIOError, "read returned more than the requested number of bytes");
|
|
171
|
+
}
|
|
172
|
+
memcpy(reader->tail, str, cnt);
|
|
173
|
+
reader->tail[cnt] = '\0';
|
|
174
|
+
reader->read_end = reader->tail + cnt;
|
|
169
175
|
|
|
170
176
|
return Qtrue;
|
|
171
177
|
}
|
|
@@ -184,8 +190,14 @@ static VALUE io_cb(VALUE rbuf) {
|
|
|
184
190
|
}
|
|
185
191
|
str = StringValuePtr(rstr);
|
|
186
192
|
cnt = RSTRING_LEN(rstr);
|
|
187
|
-
|
|
188
|
-
|
|
193
|
+
if (cnt > (size_t)(reader->end - reader->tail)) {
|
|
194
|
+
// A misbehaving IO returned more than the requested number of bytes.
|
|
195
|
+
// Copying it in with strcpy() would overflow the reader buffer.
|
|
196
|
+
rb_raise(rb_eIOError, "read returned more than the requested number of bytes");
|
|
197
|
+
}
|
|
198
|
+
memcpy(reader->tail, str, cnt);
|
|
199
|
+
reader->tail[cnt] = '\0';
|
|
200
|
+
reader->read_end = reader->tail + cnt;
|
|
189
201
|
|
|
190
202
|
return Qtrue;
|
|
191
203
|
}
|
data/ext/oj/resolve.c
CHANGED
|
@@ -19,6 +19,9 @@ inline static VALUE resolve_classname(VALUE mod, const char *classname, int auto
|
|
|
19
19
|
|
|
20
20
|
if (rb_const_defined_at(mod, ci)) {
|
|
21
21
|
clas = rb_const_get_at(mod, ci);
|
|
22
|
+
if (!RB_TYPE_P(clas, T_CLASS) && !RB_TYPE_P(clas, T_MODULE)) {
|
|
23
|
+
clas = Qundef;
|
|
24
|
+
}
|
|
22
25
|
} else if (auto_define) {
|
|
23
26
|
clas = rb_define_class_under(mod, classname, oj_bag_class);
|
|
24
27
|
} else {
|
|
@@ -32,7 +35,8 @@ static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int a
|
|
|
32
35
|
VALUE clas;
|
|
33
36
|
char *end = class_name + sizeof(class_name) - 1;
|
|
34
37
|
char *s;
|
|
35
|
-
const char *n
|
|
38
|
+
const char *n = name;
|
|
39
|
+
size_t nlen = len;
|
|
36
40
|
|
|
37
41
|
clas = rb_cObject;
|
|
38
42
|
for (s = class_name; 0 < len; n++, len--) {
|
|
@@ -55,7 +59,12 @@ static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int a
|
|
|
55
59
|
}
|
|
56
60
|
*s = '\0';
|
|
57
61
|
if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
|
|
58
|
-
|
|
62
|
+
if (sizeof(class_name) <= nlen) {
|
|
63
|
+
nlen = sizeof(class_name) - 1;
|
|
64
|
+
}
|
|
65
|
+
memcpy(class_name, name, nlen);
|
|
66
|
+
class_name[nlen] = '\0';
|
|
67
|
+
oj_set_error_at(pi, error_class, __FILE__, __LINE__, "class '%s' is not defined", class_name);
|
|
59
68
|
if (Qnil != error_class) {
|
|
60
69
|
pi->err_class = error_class;
|
|
61
70
|
}
|
data/ext/oj/rxclass.c
CHANGED
|
@@ -142,3 +142,19 @@ void oj_rxclass_copy(RxClass src, RxClass dest) {
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
|
+
|
|
146
|
+
// Add the Ruby objects in the chain to an array. The regexps and the classes
|
|
147
|
+
// can be referenced from nowhere else, as with a default match_string option,
|
|
148
|
+
// and are collected unless something holds onto them.
|
|
149
|
+
void oj_rxclass_keep(RxClass rc, VALUE keep) {
|
|
150
|
+
RxC rxc;
|
|
151
|
+
|
|
152
|
+
for (rxc = rc->head; NULL != rxc; rxc = rxc->next) {
|
|
153
|
+
if (Qnil != rxc->rrx) {
|
|
154
|
+
rb_ary_push(keep, rxc->rrx);
|
|
155
|
+
}
|
|
156
|
+
if (Qnil != rxc->clas) {
|
|
157
|
+
rb_ary_push(keep, rxc->clas);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
data/ext/oj/rxclass.h
CHANGED
|
@@ -22,5 +22,6 @@ extern int oj_rxclass_append(RxClass rc, const char *expr, VALUE clas);
|
|
|
22
22
|
extern VALUE oj_rxclass_match(RxClass rc, const char *str, size_t len);
|
|
23
23
|
extern void oj_rxclass_copy(RxClass src, RxClass dest);
|
|
24
24
|
extern void oj_rxclass_rappend(RxClass rc, VALUE rx, VALUE clas);
|
|
25
|
+
extern void oj_rxclass_keep(RxClass rc, VALUE keep);
|
|
25
26
|
|
|
26
27
|
#endif /* OJ_RXCLASS_H */
|
data/ext/oj/safe.c
CHANGED
|
@@ -5,7 +5,7 @@ static VALUE max_hash_size_sym, max_array_size_sym, max_depth_sym, max_total_ele
|
|
|
5
5
|
max_array_size_error_class, max_depth_error_class, max_total_elements_error_class;
|
|
6
6
|
|
|
7
7
|
static void check_object_size(safe_T safe) {
|
|
8
|
-
if (
|
|
8
|
+
if (!safe->max_hash_size_isset) {
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -23,7 +23,7 @@ static void check_object_size(safe_T safe) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
static void check_array_size(safe_T safe) {
|
|
26
|
-
if (
|
|
26
|
+
if (!safe->max_array_size_isset) {
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -41,7 +41,7 @@ static void check_array_size(safe_T safe) {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
static void check_max_depth(safe_T safe, ojParser p) {
|
|
44
|
-
if (
|
|
44
|
+
if (!safe->max_depth_isset || safe->max_depth >= (p->depth + 1)) {
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -55,7 +55,7 @@ static void check_max_total_elements(safe_T safe) {
|
|
|
55
55
|
* null, true) are not counted. As a result, `current_elements_count`
|
|
56
56
|
* always holds one less than the actual total.
|
|
57
57
|
*/
|
|
58
|
-
if (
|
|
58
|
+
if (!safe->max_total_elements_isset || safe->max_total_elements > safe->current_elements_count) {
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -140,58 +140,72 @@ DEFINE_DELEGATED_OBJECT_FUNCTION(add_float);
|
|
|
140
140
|
DEFINE_DELEGATED_OBJECT_FUNCTION(add_big);
|
|
141
141
|
DEFINE_DELEGATED_OBJECT_FUNCTION(add_str);
|
|
142
142
|
|
|
143
|
+
// The counting wrappers live in the parser function table, and the option
|
|
144
|
+
// setters of the usual parser write to the same slots, so an option set after
|
|
145
|
+
// the parser was built puts the unwrapped handler back and the limits stop
|
|
146
|
+
// being applied for that kind of value.
|
|
147
|
+
#define WRAP(funcs, slot, wrapper, saved) \
|
|
148
|
+
do { \
|
|
149
|
+
if (wrapper != (funcs)->slot) { \
|
|
150
|
+
safe->saved = (funcs)->slot; \
|
|
151
|
+
} \
|
|
152
|
+
(funcs)->slot = wrapper; \
|
|
153
|
+
} while (0)
|
|
154
|
+
|
|
155
|
+
static void wrap_funcs(ojParser p, safe_T safe) {
|
|
156
|
+
Funcs f = &p->funcs[ARRAY_FUN];
|
|
157
|
+
|
|
158
|
+
WRAP(f, open_object, safe_open_object, delegated_open_object_func);
|
|
159
|
+
WRAP(f, open_array, safe_open_array, delegated_open_array_func);
|
|
160
|
+
// The following overrides are done for counting objects
|
|
161
|
+
WRAP(f, add_null, safe_add_null, delegated_add_null_func);
|
|
162
|
+
WRAP(f, add_true, safe_add_true, delegated_add_true_func);
|
|
163
|
+
WRAP(f, add_false, safe_add_false, delegated_add_false_func);
|
|
164
|
+
WRAP(f, add_int, safe_add_int, delegated_add_int_func);
|
|
165
|
+
WRAP(f, add_float, safe_add_float, delegated_add_float_func);
|
|
166
|
+
WRAP(f, add_big, safe_add_big, delegated_add_big_func);
|
|
167
|
+
WRAP(f, add_str, safe_add_str, delegated_add_str_func);
|
|
168
|
+
|
|
169
|
+
f = &p->funcs[OBJECT_FUN];
|
|
170
|
+
WRAP(f, open_object, safe_open_object_key, delegated_open_object_key_func);
|
|
171
|
+
WRAP(f, open_array, safe_open_array_key, delegated_open_array_key_func);
|
|
172
|
+
WRAP(f, add_null, safe_add_null_key, delegated_add_null_key_func);
|
|
173
|
+
WRAP(f, add_true, safe_add_true_key, delegated_add_true_key_func);
|
|
174
|
+
WRAP(f, add_false, safe_add_false_key, delegated_add_false_key_func);
|
|
175
|
+
WRAP(f, add_int, safe_add_int_key, delegated_add_int_key_func);
|
|
176
|
+
WRAP(f, add_float, safe_add_float_key, delegated_add_float_key_func);
|
|
177
|
+
WRAP(f, add_big, safe_add_big_key, delegated_add_big_key_func);
|
|
178
|
+
WRAP(f, add_str, safe_add_str_key, delegated_add_str_key_func);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
static VALUE safe_option(ojParser p, const char *key, VALUE value) {
|
|
182
|
+
safe_T safe = (safe_T)p->ctx;
|
|
183
|
+
VALUE rv;
|
|
184
|
+
|
|
185
|
+
if (0 == strcmp("omit_null", key)) {
|
|
186
|
+
return safe->omit_null ? Qtrue : Qfalse;
|
|
187
|
+
}
|
|
188
|
+
rv = safe->delegated_option_func(p, key, value);
|
|
189
|
+
if (0 == strcmp("omit_null=", key)) {
|
|
190
|
+
safe->omit_null = (Qtrue == rv);
|
|
191
|
+
}
|
|
192
|
+
wrap_funcs(p, safe);
|
|
193
|
+
|
|
194
|
+
return rv;
|
|
195
|
+
}
|
|
196
|
+
|
|
143
197
|
void oj_init_safe_parser(ojParser p, safe_T safe, VALUE options) {
|
|
144
198
|
// Safe parser inherits all members of usual parser
|
|
145
199
|
oj_init_usual(p, &safe->usual);
|
|
146
200
|
|
|
147
201
|
safe->delegated_start_func = p->start;
|
|
148
202
|
p->start = safe_start;
|
|
203
|
+
safe->omit_null = false;
|
|
149
204
|
|
|
150
|
-
|
|
205
|
+
wrap_funcs(p, safe);
|
|
151
206
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
safe->delegated_open_object_func = f->open_object;
|
|
155
|
-
f->open_object = safe_open_object;
|
|
156
|
-
safe->delegated_open_array_func = f->open_array;
|
|
157
|
-
f->open_array = safe_open_array;
|
|
158
|
-
// The following overrides are done for counting objects
|
|
159
|
-
safe->delegated_add_null_func = f->add_null;
|
|
160
|
-
f->add_null = safe_add_null;
|
|
161
|
-
safe->delegated_add_true_func = f->add_true;
|
|
162
|
-
f->add_true = safe_add_true;
|
|
163
|
-
safe->delegated_add_false_func = f->add_false;
|
|
164
|
-
f->add_false = safe_add_false;
|
|
165
|
-
safe->delegated_add_int_func = f->add_int;
|
|
166
|
-
f->add_int = safe_add_int;
|
|
167
|
-
safe->delegated_add_float_func = f->add_float;
|
|
168
|
-
f->add_float = safe_add_float;
|
|
169
|
-
safe->delegated_add_big_func = f->add_big;
|
|
170
|
-
f->add_big = safe_add_big;
|
|
171
|
-
safe->delegated_add_str_func = f->add_str;
|
|
172
|
-
f->add_str = safe_add_str;
|
|
173
|
-
|
|
174
|
-
// Object parser functions
|
|
175
|
-
f = &p->funcs[OBJECT_FUN];
|
|
176
|
-
safe->delegated_open_object_key_func = f->open_object;
|
|
177
|
-
f->open_object = safe_open_object_key;
|
|
178
|
-
safe->delegated_open_array_key_func = f->open_array;
|
|
179
|
-
f->open_array = safe_open_array_key;
|
|
180
|
-
// The following overrides are done for counting objects
|
|
181
|
-
safe->delegated_add_null_key_func = f->add_null;
|
|
182
|
-
f->add_null = safe_add_null_key;
|
|
183
|
-
safe->delegated_add_true_key_func = f->add_true;
|
|
184
|
-
f->add_true = safe_add_true_key;
|
|
185
|
-
safe->delegated_add_false_key_func = f->add_false;
|
|
186
|
-
f->add_false = safe_add_false_key;
|
|
187
|
-
safe->delegated_add_int_key_func = f->add_int;
|
|
188
|
-
f->add_int = safe_add_int_key;
|
|
189
|
-
safe->delegated_add_float_key_func = f->add_float;
|
|
190
|
-
f->add_float = safe_add_float_key;
|
|
191
|
-
safe->delegated_add_big_key_func = f->add_big;
|
|
192
|
-
f->add_big = safe_add_big_key;
|
|
193
|
-
safe->delegated_add_str_key_func = f->add_str;
|
|
194
|
-
f->add_str = safe_add_str_key;
|
|
207
|
+
safe->delegated_option_func = p->option;
|
|
208
|
+
p->option = safe_option;
|
|
195
209
|
|
|
196
210
|
SET_CONFIG(max_hash_size);
|
|
197
211
|
SET_CONFIG(max_array_size);
|
data/ext/oj/safe.h
CHANGED
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
VALUE rb_##config_name = rb_hash_aref(options, config_name##_sym); \
|
|
9
9
|
\
|
|
10
10
|
if (RB_INTEGER_TYPE_P(rb_##config_name)) { \
|
|
11
|
-
safe->config_name
|
|
11
|
+
safe->config_name = NUM2LONG(rb_##config_name); \
|
|
12
|
+
safe->config_name##_isset = true; \
|
|
12
13
|
} else if (!NIL_P(rb_##config_name)) { \
|
|
13
14
|
rb_raise(rb_eArgError, "Incorrect value provided for `" #config_name "`"); \
|
|
14
15
|
} else { \
|
|
15
|
-
safe->config_name
|
|
16
|
+
safe->config_name = 0; \
|
|
17
|
+
safe->config_name##_isset = false; \
|
|
16
18
|
} \
|
|
17
19
|
} while (0);
|
|
18
20
|
|
|
@@ -49,10 +51,20 @@ typedef struct _safe_S {
|
|
|
49
51
|
long int max_total_elements;
|
|
50
52
|
long int max_json_size_bytes;
|
|
51
53
|
|
|
54
|
+
bool max_hash_size_isset;
|
|
55
|
+
bool max_array_size_isset;
|
|
56
|
+
bool max_depth_isset;
|
|
57
|
+
bool max_total_elements_isset;
|
|
58
|
+
|
|
52
59
|
long int current_hash_size;
|
|
53
60
|
long int current_array_size;
|
|
54
61
|
long int current_elements_count;
|
|
55
62
|
|
|
63
|
+
// omit_null is kept here because the usual parser reports it by looking at
|
|
64
|
+
// the add_null slot, which is one of the slots wrapped below.
|
|
65
|
+
bool omit_null;
|
|
66
|
+
|
|
67
|
+
VALUE (*delegated_option_func)(struct _ojParser *p, const char *key, VALUE value);
|
|
56
68
|
void (*delegated_start_func)(struct _ojParser *p);
|
|
57
69
|
|
|
58
70
|
// Array functions
|
data/ext/oj/saj.c
CHANGED
|
@@ -82,7 +82,14 @@ inline static void next_non_white(ParseInfo pi) {
|
|
|
82
82
|
case '\f':
|
|
83
83
|
case '\n':
|
|
84
84
|
case '\r': break;
|
|
85
|
-
case '/':
|
|
85
|
+
case '/':
|
|
86
|
+
skip_comment(pi);
|
|
87
|
+
/* A comment that is not terminated by a newline ends on the null
|
|
88
|
+
* terminator. Stop here so the loop does not step past it. */
|
|
89
|
+
if ('\0' == *pi->s) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
86
93
|
default: return;
|
|
87
94
|
}
|
|
88
95
|
}
|
|
@@ -118,14 +125,15 @@ static void skip_comment(ParseInfo pi) {
|
|
|
118
125
|
if ('*' == *pi->s && '/' == *(pi->s + 1)) {
|
|
119
126
|
pi->s++;
|
|
120
127
|
return;
|
|
121
|
-
} else if ('\0' == *pi->s) {
|
|
122
|
-
if (pi->has_error) {
|
|
123
|
-
call_error("comment not terminated", pi, __FILE__, __LINE__);
|
|
124
|
-
} else {
|
|
125
|
-
raise_error("comment not terminated", pi->str, pi->s);
|
|
126
|
-
}
|
|
127
128
|
}
|
|
128
129
|
}
|
|
130
|
+
/* The loop only ends on the null terminator so the comment was never
|
|
131
|
+
* closed. */
|
|
132
|
+
if (pi->has_error) {
|
|
133
|
+
call_error("comment not terminated", pi, __FILE__, __LINE__);
|
|
134
|
+
} else {
|
|
135
|
+
raise_error("comment not terminated", pi->str, pi->s);
|
|
136
|
+
}
|
|
129
137
|
} else if ('/' == *pi->s) {
|
|
130
138
|
for (; 1; pi->s++) {
|
|
131
139
|
switch (*pi->s) {
|
|
@@ -190,6 +198,12 @@ static void read_hash(ParseInfo pi, const char *key) {
|
|
|
190
198
|
} else {
|
|
191
199
|
while (1) {
|
|
192
200
|
next_non_white(pi);
|
|
201
|
+
if ('"' != *pi->s) {
|
|
202
|
+
if (pi->has_error) {
|
|
203
|
+
call_error("invalid format, expected a key", pi, __FILE__, __LINE__);
|
|
204
|
+
}
|
|
205
|
+
raise_error("invalid format, expected a key", pi->str, pi->s);
|
|
206
|
+
}
|
|
193
207
|
ks = read_quoted_value(pi);
|
|
194
208
|
next_non_white(pi);
|
|
195
209
|
if (':' == *pi->s) {
|
|
@@ -606,6 +620,19 @@ static void saj_parse(VALUE handler, char *json) {
|
|
|
606
620
|
}
|
|
607
621
|
}
|
|
608
622
|
|
|
623
|
+
struct _sajArgs {
|
|
624
|
+
VALUE handler;
|
|
625
|
+
char *json;
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
static VALUE protect_saj_parse(VALUE x) {
|
|
629
|
+
struct _sajArgs *args = (struct _sajArgs *)x;
|
|
630
|
+
|
|
631
|
+
saj_parse(args->handler, args->json);
|
|
632
|
+
|
|
633
|
+
return Qnil;
|
|
634
|
+
}
|
|
635
|
+
|
|
609
636
|
/* call-seq: saj_parse(handler, io)
|
|
610
637
|
*
|
|
611
638
|
* Parses an IO stream or file containing an JSON document. Raises an exception
|
|
@@ -670,8 +697,17 @@ oj_saj_parse(int argc, VALUE *argv, VALUE self) {
|
|
|
670
697
|
rb_raise(rb_eArgError, "saj_parse() expected a String or IO Object.");
|
|
671
698
|
}
|
|
672
699
|
}
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
700
|
+
{
|
|
701
|
+
// saj_parse() raises on a malformed document so the json buffer has to
|
|
702
|
+
// be freed even when the parse does not return normally.
|
|
703
|
+
struct _sajArgs args = {*argv, json};
|
|
704
|
+
int ex = 0;
|
|
705
|
+
|
|
706
|
+
rb_protect(protect_saj_parse, (VALUE)&args, &ex);
|
|
707
|
+
OJ_R_FREE(json);
|
|
708
|
+
if (0 != ex) {
|
|
709
|
+
rb_jump_tag(ex);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
676
712
|
return Qnil;
|
|
677
713
|
}
|
data/ext/oj/sparse.c
CHANGED
|
@@ -290,13 +290,13 @@ static void read_escaped_str(ParseInfo pi) {
|
|
|
290
290
|
case NEXT_HASH_NEW:
|
|
291
291
|
case NEXT_HASH_KEY:
|
|
292
292
|
if (Qundef == (parent->key_val = pi->hash_key(pi, buf.head, buf_len(&buf)))) {
|
|
293
|
-
parent->klen
|
|
294
|
-
parent->key
|
|
295
|
-
|
|
296
|
-
*(char *)(parent->key + parent->klen) = '\0';
|
|
293
|
+
parent->klen = buf_len(&buf);
|
|
294
|
+
parent->key = oj_strndup(buf.head, parent->klen);
|
|
295
|
+
parent->kalloc = 1;
|
|
297
296
|
} else {
|
|
298
|
-
parent->key
|
|
299
|
-
parent->klen
|
|
297
|
+
parent->key = "";
|
|
298
|
+
parent->klen = 0;
|
|
299
|
+
parent->kalloc = 0;
|
|
300
300
|
}
|
|
301
301
|
parent->k1 = *pi->rd.str;
|
|
302
302
|
parent->next = NEXT_HASH_COLON;
|
|
@@ -906,7 +906,17 @@ CLEANUP:
|
|
|
906
906
|
if (0 != pi->circ_array) {
|
|
907
907
|
oj_circ_array_free(pi->circ_array);
|
|
908
908
|
}
|
|
909
|
+
reader_cleanup(&pi->rd);
|
|
909
910
|
stack_cleanup(&pi->stack);
|
|
911
|
+
// A :match_string option builds a chain of regexps for this call that has to
|
|
912
|
+
// be freed. Without the option the member aliases the chain owned by the
|
|
913
|
+
// defaults and must be left alone.
|
|
914
|
+
if (pi->options.str_rx.head != oj_default_options.str_rx.head) {
|
|
915
|
+
oj_rxclass_cleanup(&pi->options.str_rx);
|
|
916
|
+
pi->options.str_rx.head = NULL;
|
|
917
|
+
pi->options.str_rx.tail = NULL;
|
|
918
|
+
}
|
|
919
|
+
oj_free_call_options(&pi->options);
|
|
910
920
|
if (0 != fd) {
|
|
911
921
|
#ifdef _WIN32
|
|
912
922
|
rb_w32_close(fd);
|
data/ext/oj/stream_writer.c
CHANGED
|
@@ -16,15 +16,29 @@ static void stream_writer_free(void *ptr) {
|
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
sw = (StreamWriter)ptr;
|
|
19
|
+
oj_options_release(&sw->sw.opts);
|
|
19
20
|
OJ_R_FREE(sw->sw.out.buf);
|
|
20
21
|
OJ_R_FREE(sw->sw.types);
|
|
21
22
|
OJ_R_FREE(ptr);
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
static void stream_writer_mark(void *ptr) {
|
|
26
|
+
if (NULL != ptr) {
|
|
27
|
+
StreamWriter sw = (StreamWriter)ptr;
|
|
28
|
+
|
|
29
|
+
oj_options_mark(&sw->sw.opts);
|
|
30
|
+
// The writer can be the only reference to the stream as in
|
|
31
|
+
// Oj::StreamWriter.new(StringIO.new) so it must be marked.
|
|
32
|
+
if (Qnil != sw->stream) {
|
|
33
|
+
rb_gc_mark(sw->stream);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
24
38
|
static const rb_data_type_t oj_stream_writer_type = {
|
|
25
39
|
"Oj/stream_writer",
|
|
26
40
|
{
|
|
27
|
-
|
|
41
|
+
stream_writer_mark,
|
|
28
42
|
stream_writer_free,
|
|
29
43
|
NULL,
|
|
30
44
|
},
|
|
@@ -116,6 +130,7 @@ static VALUE stream_writer_new(int argc, VALUE *argv, VALUE self) {
|
|
|
116
130
|
oj_str_writer_init(&sw->sw, 4096);
|
|
117
131
|
sw->flush_limit = 0;
|
|
118
132
|
}
|
|
133
|
+
oj_options_take_ownership(&sw->sw.opts);
|
|
119
134
|
sw->sw.out.indent = sw->sw.opts.indent;
|
|
120
135
|
sw->stream = stream;
|
|
121
136
|
sw->type = type;
|
data/ext/oj/string_writer.c
CHANGED
|
@@ -42,12 +42,18 @@ static void maybe_comma(StrWriter sw) {
|
|
|
42
42
|
|
|
43
43
|
// Used by stream writer also.
|
|
44
44
|
void oj_str_writer_init(StrWriter sw, int buf_size) {
|
|
45
|
-
sw->opts
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
sw->
|
|
45
|
+
sw->opts = oj_default_options;
|
|
46
|
+
// Detach from the match_string regexps owned by the defaults before the
|
|
47
|
+
// options are parsed. A :match_string option then builds a chain of its own
|
|
48
|
+
// that is freed with the writer instead of being appended to the chain the
|
|
49
|
+
// defaults are using.
|
|
50
|
+
sw->opts.str_rx.head = NULL;
|
|
51
|
+
sw->opts.str_rx.tail = NULL;
|
|
52
|
+
sw->depth = 0;
|
|
53
|
+
sw->types = OJ_R_ALLOC_N(char, 256);
|
|
54
|
+
sw->types_end = sw->types + 256;
|
|
55
|
+
*sw->types = '\0';
|
|
56
|
+
sw->keyWritten = 0;
|
|
51
57
|
|
|
52
58
|
if (0 == buf_size) {
|
|
53
59
|
buf_size = 4096;
|
|
@@ -72,6 +78,8 @@ void oj_str_writer_init(StrWriter sw, int buf_size) {
|
|
|
72
78
|
sw->out.argv = NULL;
|
|
73
79
|
sw->out.ropts = NULL;
|
|
74
80
|
sw->out.omit_nil = oj_default_options.dump_opts.omit_nil;
|
|
81
|
+
// oj_str_writer_init does not call oj_out_init, so clear this here too.
|
|
82
|
+
sw->out.key_filter_off = false;
|
|
75
83
|
}
|
|
76
84
|
|
|
77
85
|
void oj_str_writer_push_key(StrWriter sw, const char *key) {
|
|
@@ -234,15 +242,22 @@ static void string_writer_free(void *ptr) {
|
|
|
234
242
|
sw = (StrWriter)ptr;
|
|
235
243
|
|
|
236
244
|
oj_out_free(&sw->out);
|
|
245
|
+
oj_options_release(&sw->opts);
|
|
237
246
|
|
|
238
247
|
OJ_R_FREE(sw->types);
|
|
239
248
|
OJ_R_FREE(ptr);
|
|
240
249
|
}
|
|
241
250
|
|
|
251
|
+
static void string_writer_mark(void *ptr) {
|
|
252
|
+
if (NULL != ptr) {
|
|
253
|
+
oj_options_mark(&((StrWriter)ptr)->opts);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
242
257
|
static const rb_data_type_t oj_string_writer_type = {
|
|
243
258
|
"Oj/string_writer",
|
|
244
259
|
{
|
|
245
|
-
|
|
260
|
+
string_writer_mark,
|
|
246
261
|
string_writer_free,
|
|
247
262
|
NULL,
|
|
248
263
|
},
|
|
@@ -279,6 +294,7 @@ static VALUE str_writer_new(int argc, VALUE *argv, VALUE self) {
|
|
|
279
294
|
if (1 == argc) {
|
|
280
295
|
oj_parse_options(argv[0], &sw->opts);
|
|
281
296
|
}
|
|
297
|
+
oj_options_take_ownership(&sw->opts);
|
|
282
298
|
sw->out.argc = argc - 1;
|
|
283
299
|
sw->out.argv = argv + 1;
|
|
284
300
|
sw->out.indent = sw->opts.indent;
|
data/ext/oj/usual.c
CHANGED
|
@@ -831,6 +831,7 @@ static VALUE opt_create_id_set(ojParser p, VALUE value) {
|
|
|
831
831
|
Usual d = (Usual)p->ctx;
|
|
832
832
|
|
|
833
833
|
if (Qnil == value) {
|
|
834
|
+
OJ_R_FREE(d->create_id);
|
|
834
835
|
d->create_id = NULL;
|
|
835
836
|
d->create_id_len = 0;
|
|
836
837
|
p->funcs[OBJECT_FUN].add_str = add_str_key;
|
|
@@ -850,12 +851,15 @@ static VALUE opt_create_id_set(ojParser p, VALUE value) {
|
|
|
850
851
|
if (1 << (8 * sizeof(d->create_id_len)) <= len) {
|
|
851
852
|
rb_raise(rb_eArgError, "The create_id values is limited to %d bytes.", 1 << (8 * sizeof(d->create_id_len)));
|
|
852
853
|
}
|
|
853
|
-
d->
|
|
854
|
+
char *prev = d->create_id;
|
|
855
|
+
|
|
854
856
|
d->create_id = str_dup(RSTRING_PTR(value), len);
|
|
857
|
+
d->create_id_len = (uint8_t)len;
|
|
855
858
|
p->funcs[OBJECT_FUN].add_str = add_str_key_create;
|
|
856
859
|
p->funcs[TOP_FUN].close_object = close_object_create;
|
|
857
860
|
p->funcs[ARRAY_FUN].close_object = close_object_create;
|
|
858
861
|
p->funcs[OBJECT_FUN].close_object = close_object_create;
|
|
862
|
+
OJ_R_FREE(prev);
|
|
859
863
|
}
|
|
860
864
|
return opt_create_id(p, value);
|
|
861
865
|
}
|
data/ext/oj/util.c
CHANGED
|
@@ -134,3 +134,30 @@ void sec_as_time(int64_t secs, TimeInfo ti) {
|
|
|
134
134
|
secs = secs - (int64_t)ti->min * 60LL;
|
|
135
135
|
ti->sec = (int)secs;
|
|
136
136
|
}
|
|
137
|
+
|
|
138
|
+
// The inverse of sec_as_time(). The days are counted with the civil calendar
|
|
139
|
+
// algorithm from Howard Hinnant's date library so that no libc call is needed.
|
|
140
|
+
// timegm() is not available on Windows and the mktime() based replacement that
|
|
141
|
+
// was used there returned the wrong value in any timezone east of UTC.
|
|
142
|
+
int64_t time_as_sec(TimeInfo ti) {
|
|
143
|
+
int64_t year = (int64_t)ti->year;
|
|
144
|
+
int64_t mon = (int64_t)ti->mon;
|
|
145
|
+
int64_t era;
|
|
146
|
+
int64_t yoe; // year of the era, [0, 399]
|
|
147
|
+
int64_t doy; // day of the year, [0, 365]
|
|
148
|
+
int64_t doe; // day of the era, [0, 146096]
|
|
149
|
+
int64_t days; // days since 1970-01-01
|
|
150
|
+
|
|
151
|
+
// Start the year on March 1st so that a leap day falls on the last day of
|
|
152
|
+
// the year and the day of the year is a simple expression.
|
|
153
|
+
if (mon <= 2) {
|
|
154
|
+
year--;
|
|
155
|
+
}
|
|
156
|
+
era = (0 <= year ? year : year - 399) / 400;
|
|
157
|
+
yoe = year - era * 400;
|
|
158
|
+
doy = (153 * (mon + (2 < mon ? -3 : 9)) + 2) / 5 + (int64_t)ti->day - 1;
|
|
159
|
+
doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
|
|
160
|
+
days = era * 146097 + doe - 719468;
|
|
161
|
+
|
|
162
|
+
return days * SECS_PER_DAY + (int64_t)ti->hour * 3600LL + (int64_t)ti->min * 60LL + (int64_t)ti->sec;
|
|
163
|
+
}
|
data/ext/oj/util.h
CHANGED
data/ext/oj/val_stack.h
CHANGED
|
@@ -35,8 +35,8 @@ typedef struct _val {
|
|
|
35
35
|
const char *classname;
|
|
36
36
|
VALUE clas;
|
|
37
37
|
OddArgs odd_args;
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
size_t klen;
|
|
39
|
+
size_t clen;
|
|
40
40
|
char next; // ValNext
|
|
41
41
|
char k1; // first original character in the key
|
|
42
42
|
char kalloc;
|
|
@@ -62,6 +62,16 @@ inline static int stack_empty(ValStack stack) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
inline static void stack_cleanup(ValStack stack) {
|
|
65
|
+
Val v;
|
|
66
|
+
|
|
67
|
+
// end_hash() frees the args of a completed odd class. Anything still here
|
|
68
|
+
// belongs to a parse that was abandoned part way through.
|
|
69
|
+
for (v = stack->head; v < stack->tail; v++) {
|
|
70
|
+
if (NULL != v->odd_args) {
|
|
71
|
+
oj_odd_free(v->odd_args);
|
|
72
|
+
v->odd_args = NULL;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
65
75
|
if (stack->base != stack->head) {
|
|
66
76
|
OJ_R_FREE(stack->head);
|
|
67
77
|
stack->head = NULL;
|
|
@@ -70,9 +80,11 @@ inline static void stack_cleanup(ValStack stack) {
|
|
|
70
80
|
|
|
71
81
|
inline static void stack_push(ValStack stack, VALUE val, ValNext next) {
|
|
72
82
|
if (stack->end <= stack->tail) {
|
|
73
|
-
size_t
|
|
74
|
-
size_t
|
|
75
|
-
Val
|
|
83
|
+
size_t len = stack->end - stack->head;
|
|
84
|
+
size_t toff = stack->tail - stack->head;
|
|
85
|
+
Val head = stack->head;
|
|
86
|
+
const char *old = (const char *)stack->head;
|
|
87
|
+
size_t i;
|
|
76
88
|
|
|
77
89
|
// A realloc can trigger a GC so make sure it happens outside the lock
|
|
78
90
|
// but lock before changing pointers.
|
|
@@ -82,6 +94,13 @@ inline static void stack_push(ValStack stack, VALUE val, ValNext next) {
|
|
|
82
94
|
} else {
|
|
83
95
|
OJ_R_REALLOC_N(head, struct _val, len + STACK_INC);
|
|
84
96
|
}
|
|
97
|
+
// A key short enough to be kept in the Val itself points into the
|
|
98
|
+
// block that just moved.
|
|
99
|
+
for (i = 0; i < toff; i++) {
|
|
100
|
+
if (old <= head[i].key && head[i].key < old + sizeof(struct _val) * len) {
|
|
101
|
+
head[i].key = head[i].karray;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
85
104
|
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
|
86
105
|
pthread_mutex_lock(&stack->mutex);
|
|
87
106
|
#else
|