oj 3.17.1 → 3.17.6
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 +87 -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 +5 -5
- data/ext/oj/custom.c +7 -5
- 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 +104 -48
- data/ext/oj/intern.c +7 -2
- data/ext/oj/object.c +26 -15
- data/ext/oj/odd.c +6 -2
- data/ext/oj/oj.c +277 -26
- data/ext/oj/oj.h +6 -1
- data/ext/oj/parse.c +19 -5
- data/ext/oj/parse.h +0 -1
- data/ext/oj/parser.c +135 -37
- data/ext/oj/rails.c +180 -42
- data/ext/oj/reader.c +18 -6
- data/ext/oj/resolve.c +11 -2
- data/ext/oj/rxclass.c +17 -1
- data/ext/oj/rxclass.h +2 -1
- data/ext/oj/safe.c +63 -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 +23 -6
- 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 +6 -0
- data/pages/Security.md +21 -7
- metadata +2 -2
data/ext/oj/safe.c
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
|
|
1
2
|
#include "safe.h"
|
|
2
3
|
|
|
3
4
|
static VALUE max_hash_size_sym, max_array_size_sym, max_depth_sym, max_total_elements_sym, max_hash_size_error_class,
|
|
4
5
|
max_array_size_error_class, max_depth_error_class, max_total_elements_error_class;
|
|
5
6
|
|
|
6
7
|
static void check_object_size(safe_T safe) {
|
|
7
|
-
if (
|
|
8
|
+
if (!safe->max_hash_size_isset) {
|
|
8
9
|
return;
|
|
9
10
|
}
|
|
10
11
|
|
|
@@ -22,7 +23,7 @@ static void check_object_size(safe_T safe) {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
static void check_array_size(safe_T safe) {
|
|
25
|
-
if (
|
|
26
|
+
if (!safe->max_array_size_isset) {
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -40,7 +41,7 @@ static void check_array_size(safe_T safe) {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
static void check_max_depth(safe_T safe, ojParser p) {
|
|
43
|
-
if (
|
|
44
|
+
if (!safe->max_depth_isset || safe->max_depth >= (p->depth + 1)) {
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
47
|
|
|
@@ -54,7 +55,7 @@ static void check_max_total_elements(safe_T safe) {
|
|
|
54
55
|
* null, true) are not counted. As a result, `current_elements_count`
|
|
55
56
|
* always holds one less than the actual total.
|
|
56
57
|
*/
|
|
57
|
-
if (
|
|
58
|
+
if (!safe->max_total_elements_isset || safe->max_total_elements > safe->current_elements_count) {
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -139,58 +140,72 @@ DEFINE_DELEGATED_OBJECT_FUNCTION(add_float);
|
|
|
139
140
|
DEFINE_DELEGATED_OBJECT_FUNCTION(add_big);
|
|
140
141
|
DEFINE_DELEGATED_OBJECT_FUNCTION(add_str);
|
|
141
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
|
+
|
|
142
197
|
void oj_init_safe_parser(ojParser p, safe_T safe, VALUE options) {
|
|
143
198
|
// Safe parser inherits all members of usual parser
|
|
144
199
|
oj_init_usual(p, &safe->usual);
|
|
145
200
|
|
|
146
201
|
safe->delegated_start_func = p->start;
|
|
147
202
|
p->start = safe_start;
|
|
203
|
+
safe->omit_null = false;
|
|
148
204
|
|
|
149
|
-
|
|
205
|
+
wrap_funcs(p, safe);
|
|
150
206
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
safe->delegated_open_object_func = f->open_object;
|
|
154
|
-
f->open_object = safe_open_object;
|
|
155
|
-
safe->delegated_open_array_func = f->open_array;
|
|
156
|
-
f->open_array = safe_open_array;
|
|
157
|
-
// The following overrides are done for counting objects
|
|
158
|
-
safe->delegated_add_null_func = f->add_null;
|
|
159
|
-
f->add_null = safe_add_null;
|
|
160
|
-
safe->delegated_add_true_func = f->add_true;
|
|
161
|
-
f->add_true = safe_add_true;
|
|
162
|
-
safe->delegated_add_false_func = f->add_false;
|
|
163
|
-
f->add_false = safe_add_false;
|
|
164
|
-
safe->delegated_add_int_func = f->add_int;
|
|
165
|
-
f->add_int = safe_add_int;
|
|
166
|
-
safe->delegated_add_float_func = f->add_float;
|
|
167
|
-
f->add_float = safe_add_float;
|
|
168
|
-
safe->delegated_add_big_func = f->add_big;
|
|
169
|
-
f->add_big = safe_add_big;
|
|
170
|
-
safe->delegated_add_str_func = f->add_str;
|
|
171
|
-
f->add_str = safe_add_str;
|
|
172
|
-
|
|
173
|
-
// Object parser functions
|
|
174
|
-
f = &p->funcs[OBJECT_FUN];
|
|
175
|
-
safe->delegated_open_object_key_func = f->open_object;
|
|
176
|
-
f->open_object = safe_open_object_key;
|
|
177
|
-
safe->delegated_open_array_key_func = f->open_array;
|
|
178
|
-
f->open_array = safe_open_array_key;
|
|
179
|
-
// The following overrides are done for counting objects
|
|
180
|
-
safe->delegated_add_null_key_func = f->add_null;
|
|
181
|
-
f->add_null = safe_add_null_key;
|
|
182
|
-
safe->delegated_add_true_key_func = f->add_true;
|
|
183
|
-
f->add_true = safe_add_true_key;
|
|
184
|
-
safe->delegated_add_false_key_func = f->add_false;
|
|
185
|
-
f->add_false = safe_add_false_key;
|
|
186
|
-
safe->delegated_add_int_key_func = f->add_int;
|
|
187
|
-
f->add_int = safe_add_int_key;
|
|
188
|
-
safe->delegated_add_float_key_func = f->add_float;
|
|
189
|
-
f->add_float = safe_add_float_key;
|
|
190
|
-
safe->delegated_add_big_key_func = f->add_big;
|
|
191
|
-
f->add_big = safe_add_big_key;
|
|
192
|
-
safe->delegated_add_str_key_func = f->add_str;
|
|
193
|
-
f->add_str = safe_add_str_key;
|
|
207
|
+
safe->delegated_option_func = p->option;
|
|
208
|
+
p->option = safe_option;
|
|
194
209
|
|
|
195
210
|
SET_CONFIG(max_hash_size);
|
|
196
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
|
@@ -63,7 +63,7 @@ static VALUE form_attr(const char *str, size_t len) {
|
|
|
63
63
|
memcpy(b + 1, str, len);
|
|
64
64
|
b[len + 1] = '\0';
|
|
65
65
|
|
|
66
|
-
id = rb_intern3(
|
|
66
|
+
id = rb_intern3(b, len + 1, oj_utf8_encoding);
|
|
67
67
|
OJ_R_FREE(b);
|
|
68
68
|
return id;
|
|
69
69
|
}
|
|
@@ -200,7 +200,10 @@ static void push_key(ojParser p) {
|
|
|
200
200
|
d->ktail = d->khead + pos;
|
|
201
201
|
d->kend = d->khead + cap;
|
|
202
202
|
}
|
|
203
|
-
|
|
203
|
+
if (32000 < klen) {
|
|
204
|
+
rb_raise(oj_json_parser_error_class, "Key too long. Keys are limited to 32,000 bytes.");
|
|
205
|
+
}
|
|
206
|
+
d->ktail->len = (int16_t)klen;
|
|
204
207
|
if (klen < sizeof(d->ktail->buf)) {
|
|
205
208
|
memcpy(d->ktail->buf, key, klen);
|
|
206
209
|
d->ktail->buf[klen] = '\0';
|
|
@@ -608,12 +611,16 @@ static void dfree(ojParser p) {
|
|
|
608
611
|
Usual d = (Usual)p->ctx;
|
|
609
612
|
|
|
610
613
|
cache_free(d->str_cache);
|
|
614
|
+
d->str_cache = NULL;
|
|
611
615
|
cache_free(d->attr_cache);
|
|
616
|
+
d->attr_cache = NULL;
|
|
612
617
|
if (NULL != d->sym_cache) {
|
|
613
618
|
cache_free(d->sym_cache);
|
|
619
|
+
d->sym_cache = NULL;
|
|
614
620
|
}
|
|
615
621
|
if (NULL != d->class_cache) {
|
|
616
622
|
cache_free(d->class_cache);
|
|
623
|
+
d->class_cache = NULL;
|
|
617
624
|
}
|
|
618
625
|
OJ_R_FREE(d->vhead);
|
|
619
626
|
OJ_R_FREE(d->chead);
|
|
@@ -640,6 +647,12 @@ static void mark(ojParser p) {
|
|
|
640
647
|
if (NULL != d->class_cache) {
|
|
641
648
|
cache_mark(d->class_cache);
|
|
642
649
|
}
|
|
650
|
+
if (Qnil != d->hash_class) {
|
|
651
|
+
rb_gc_mark(d->hash_class);
|
|
652
|
+
}
|
|
653
|
+
if (Qnil != d->array_class) {
|
|
654
|
+
rb_gc_mark(d->array_class);
|
|
655
|
+
}
|
|
643
656
|
for (vp = d->vhead; vp < d->vtail; vp++) {
|
|
644
657
|
if (Qundef != *vp) {
|
|
645
658
|
rb_gc_mark(*vp);
|
|
@@ -818,6 +831,7 @@ static VALUE opt_create_id_set(ojParser p, VALUE value) {
|
|
|
818
831
|
Usual d = (Usual)p->ctx;
|
|
819
832
|
|
|
820
833
|
if (Qnil == value) {
|
|
834
|
+
OJ_R_FREE(d->create_id);
|
|
821
835
|
d->create_id = NULL;
|
|
822
836
|
d->create_id_len = 0;
|
|
823
837
|
p->funcs[OBJECT_FUN].add_str = add_str_key;
|
|
@@ -837,12 +851,15 @@ static VALUE opt_create_id_set(ojParser p, VALUE value) {
|
|
|
837
851
|
if (1 << (8 * sizeof(d->create_id_len)) <= len) {
|
|
838
852
|
rb_raise(rb_eArgError, "The create_id values is limited to %d bytes.", 1 << (8 * sizeof(d->create_id_len)));
|
|
839
853
|
}
|
|
840
|
-
d->
|
|
854
|
+
char *prev = d->create_id;
|
|
855
|
+
|
|
841
856
|
d->create_id = str_dup(RSTRING_PTR(value), len);
|
|
857
|
+
d->create_id_len = (uint8_t)len;
|
|
842
858
|
p->funcs[OBJECT_FUN].add_str = add_str_key_create;
|
|
843
859
|
p->funcs[TOP_FUN].close_object = close_object_create;
|
|
844
860
|
p->funcs[ARRAY_FUN].close_object = close_object_create;
|
|
845
861
|
p->funcs[OBJECT_FUN].close_object = close_object_create;
|
|
862
|
+
OJ_R_FREE(prev);
|
|
846
863
|
}
|
|
847
864
|
return opt_create_id(p, value);
|
|
848
865
|
}
|
|
@@ -1050,10 +1067,10 @@ static VALUE opt_symbol_keys_set(ojParser p, VALUE value) {
|
|
|
1050
1067
|
if (NULL != d->sym_cache) {
|
|
1051
1068
|
cache_free(d->sym_cache);
|
|
1052
1069
|
d->sym_cache = NULL;
|
|
1070
|
+
d->key_cache = NULL;
|
|
1053
1071
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
}
|
|
1072
|
+
d->cache_keys = false;
|
|
1073
|
+
d->get_key = str_key;
|
|
1057
1074
|
}
|
|
1058
1075
|
return (NULL != d->sym_cache) ? Qtrue : Qfalse;
|
|
1059
1076
|
}
|
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
|