oj 3.16.11 → 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 +133 -0
- data/README.md +0 -16
- 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 +12 -5
- data/ext/oj/dump.c +433 -206
- data/ext/oj/dump.h +12 -0
- data/ext/oj/dump_compat.c +17 -5
- data/ext/oj/dump_object.c +25 -10
- data/ext/oj/dump_strict.c +13 -8
- data/ext/oj/extconf.rb +11 -7
- data/ext/oj/fast.c +123 -60
- data/ext/oj/intern.c +7 -2
- data/ext/oj/mimic_json.c +3 -0
- data/ext/oj/object.c +26 -15
- data/ext/oj/odd.c +6 -2
- data/ext/oj/oj.c +511 -22
- data/ext/oj/oj.h +61 -53
- data/ext/oj/parse.c +229 -30
- data/ext/oj/parse.h +0 -1
- data/ext/oj/parser.c +226 -65
- data/ext/oj/rails.c +197 -57
- 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 +244 -0
- data/ext/oj/safe.h +91 -0
- data/ext/oj/saj.c +56 -12
- data/ext/oj/simd.h +210 -1
- data/ext/oj/sparse.c +22 -9
- 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 +15 -27
- 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 +21 -19
data/ext/oj/dump.h
CHANGED
|
@@ -62,15 +62,27 @@ extern size_t oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d,
|
|
|
62
62
|
|
|
63
63
|
extern time_t oj_sec_from_time_hard_way(VALUE obj);
|
|
64
64
|
|
|
65
|
+
extern bool oj_key_skip(VALUE key, const char *only, const char *except);
|
|
66
|
+
|
|
65
67
|
inline static void assure_size(Out out, size_t len) {
|
|
66
68
|
if (out->end - out->cur <= (long)len) {
|
|
67
69
|
oj_grow_out(out, len);
|
|
68
70
|
}
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
// Bytes that fill_indent(), or the dump_opts equivalent used when :indent is a
|
|
74
|
+
// String, writes at this depth. nl_size is array_size or hash_size.
|
|
75
|
+
inline static size_t indent_len(Out out, int depth, size_t nl_size) {
|
|
76
|
+
if (out->opts->dump_opts.use) {
|
|
77
|
+
return depth * out->opts->dump_opts.indent_size + nl_size;
|
|
78
|
+
}
|
|
79
|
+
return depth * out->indent + 1;
|
|
80
|
+
}
|
|
81
|
+
|
|
71
82
|
inline static void fill_indent(Out out, int cnt) {
|
|
72
83
|
if (0 < out->indent) {
|
|
73
84
|
cnt *= out->indent;
|
|
85
|
+
assure_size(out, cnt + 1);
|
|
74
86
|
*out->cur++ = '\n';
|
|
75
87
|
memset(out->cur, ' ', cnt);
|
|
76
88
|
out->cur += cnt;
|
data/ext/oj/dump_compat.c
CHANGED
|
@@ -148,10 +148,10 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
|
|
|
148
148
|
} else {
|
|
149
149
|
size = d2 * out->indent + 2;
|
|
150
150
|
}
|
|
151
|
-
assure_size(out, size * cnt);
|
|
152
151
|
cnt--;
|
|
153
152
|
for (i = 0; i <= cnt; i++) {
|
|
154
153
|
if (out->opts->dump_opts.use) {
|
|
154
|
+
assure_size(out, size);
|
|
155
155
|
if (0 < out->opts->dump_opts.array_size) {
|
|
156
156
|
APPEND_CHARS(out->cur, out->opts->dump_opts.array_nl, out->opts->dump_opts.array_size);
|
|
157
157
|
}
|
|
@@ -162,6 +162,7 @@ static void dump_array(VALUE a, int depth, Out out, bool as_ok) {
|
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
} else {
|
|
165
|
+
assure_size(out, size);
|
|
165
166
|
fill_indent(out, d2);
|
|
166
167
|
}
|
|
167
168
|
oj_dump_compat_val(RARRAY_AREF(a, i), d2, out, true);
|
|
@@ -298,9 +299,10 @@ static ID message_id = 0;
|
|
|
298
299
|
static ID backtrace_id = 0;
|
|
299
300
|
|
|
300
301
|
static void exception_alt(VALUE obj, int depth, Out out) {
|
|
301
|
-
int
|
|
302
|
-
size_t
|
|
303
|
-
size_t
|
|
302
|
+
int d3 = depth + 2;
|
|
303
|
+
size_t size = d3 * out->indent + 2;
|
|
304
|
+
size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
|
|
305
|
+
volatile VALUE bt;
|
|
304
306
|
|
|
305
307
|
if (0 == message_id) {
|
|
306
308
|
message_id = rb_intern("message");
|
|
@@ -331,7 +333,12 @@ static void exception_alt(VALUE obj, int depth, Out out) {
|
|
|
331
333
|
if (0 < out->opts->dump_opts.after_size) {
|
|
332
334
|
APPEND_CHARS(out->cur, out->opts->dump_opts.after_sep, out->opts->dump_opts.after_size);
|
|
333
335
|
}
|
|
334
|
-
|
|
336
|
+
bt = rb_funcall(obj, backtrace_id, 0);
|
|
337
|
+
if (T_ARRAY == rb_type(bt)) {
|
|
338
|
+
dump_array(bt, depth, out, false);
|
|
339
|
+
} else {
|
|
340
|
+
oj_dump_nil(Qnil, depth, out, false);
|
|
341
|
+
}
|
|
335
342
|
fill_indent(out, depth);
|
|
336
343
|
*out->cur++ = '}';
|
|
337
344
|
*out->cur = '\0';
|
|
@@ -605,6 +612,11 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
|
|
|
605
612
|
if (out->omit_nil && Qnil == value) {
|
|
606
613
|
return ST_CONTINUE;
|
|
607
614
|
}
|
|
615
|
+
if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
|
|
616
|
+
if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
|
|
617
|
+
return ST_CONTINUE;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
608
620
|
if (!out->opts->dump_opts.use) {
|
|
609
621
|
assure_size(out, depth * out->indent + 1);
|
|
610
622
|
fill_indent(out, depth);
|
data/ext/oj/dump_object.c
CHANGED
|
@@ -10,6 +10,24 @@ static const char hex_chars[17] = "0123456789abcdef";
|
|
|
10
10
|
|
|
11
11
|
static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out);
|
|
12
12
|
|
|
13
|
+
// An odd class is dumped as {"^O":class,...} and is rebuilt with a create
|
|
14
|
+
// function when loaded so there is no place to put a "^i" circular id on it.
|
|
15
|
+
// Registering it in the circular cache would consume an id that is never
|
|
16
|
+
// written and any later occurrence would then be dumped as a "^r" that refers
|
|
17
|
+
// to nothing. Instead dump odd classes in full each time they are encountered
|
|
18
|
+
// just like Time, Rational, and Complex are.
|
|
19
|
+
static void dump_circular_obj(VALUE obj, VALUE clas, int depth, Out out) {
|
|
20
|
+
if (NULL != oj_get_odd(clas)) {
|
|
21
|
+
dump_obj_attrs(obj, clas, 0, depth, out);
|
|
22
|
+
} else {
|
|
23
|
+
long id = oj_check_circular(obj, out);
|
|
24
|
+
|
|
25
|
+
if (0 <= id) {
|
|
26
|
+
dump_obj_attrs(obj, clas, id, depth, out);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
13
31
|
static void dump_time(VALUE obj, Out out) {
|
|
14
32
|
switch (out->opts->time_format) {
|
|
15
33
|
case RubyTime:
|
|
@@ -47,11 +65,7 @@ static void dump_data(VALUE obj, int depth, Out out, bool as_ok) {
|
|
|
47
65
|
oj_dump_cstr(str, len, 0, 0, out);
|
|
48
66
|
}
|
|
49
67
|
} else {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (0 <= id) {
|
|
53
|
-
dump_obj_attrs(obj, clas, id, depth, out);
|
|
54
|
-
}
|
|
68
|
+
dump_circular_obj(obj, clas, depth, out);
|
|
55
69
|
}
|
|
56
70
|
}
|
|
57
71
|
}
|
|
@@ -74,11 +88,7 @@ static void dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
|
|
|
74
88
|
oj_dump_raw(str, len, out);
|
|
75
89
|
}
|
|
76
90
|
} else {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (0 <= id) {
|
|
80
|
-
dump_obj_attrs(obj, clas, id, depth, out);
|
|
81
|
-
}
|
|
91
|
+
dump_circular_obj(obj, clas, depth, out);
|
|
82
92
|
}
|
|
83
93
|
}
|
|
84
94
|
|
|
@@ -210,6 +220,11 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
|
|
|
210
220
|
if (out->omit_nil && Qnil == value) {
|
|
211
221
|
return ST_CONTINUE;
|
|
212
222
|
}
|
|
223
|
+
if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
|
|
224
|
+
if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
|
|
225
|
+
return ST_CONTINUE;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
213
228
|
assure_size(out, size);
|
|
214
229
|
fill_indent(out, depth);
|
|
215
230
|
switch (rb_type(key)) {
|
data/ext/oj/dump_strict.c
CHANGED
|
@@ -193,14 +193,19 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
|
|
|
193
193
|
if (out->omit_nil && Qnil == value) {
|
|
194
194
|
return ST_CONTINUE;
|
|
195
195
|
}
|
|
196
|
+
if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
|
|
197
|
+
if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
|
|
198
|
+
return ST_CONTINUE;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
196
201
|
if (!out->opts->dump_opts.use) {
|
|
197
202
|
size = depth * out->indent + 1;
|
|
198
203
|
assure_size(out, size);
|
|
199
204
|
fill_indent(out, depth);
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
205
|
+
switch (rtype) {
|
|
206
|
+
case T_STRING: oj_dump_str(key, 0, out, false); break;
|
|
207
|
+
case T_SYMBOL: oj_dump_sym(key, 0, out, false); break;
|
|
208
|
+
default: oj_dump_str(oj_safe_string_convert(key), 0, out, false); break;
|
|
204
209
|
}
|
|
205
210
|
*out->cur++ = ':';
|
|
206
211
|
} else {
|
|
@@ -215,10 +220,10 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
|
|
|
215
220
|
APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
|
|
216
221
|
}
|
|
217
222
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
223
|
+
switch (rtype) {
|
|
224
|
+
case T_STRING: oj_dump_str(key, 0, out, false); break;
|
|
225
|
+
case T_SYMBOL: oj_dump_sym(key, 0, out, false); break;
|
|
226
|
+
default: oj_dump_str(oj_safe_string_convert(key), 0, out, false); break;
|
|
222
227
|
}
|
|
223
228
|
size = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
|
|
224
229
|
assure_size(out, size);
|
data/ext/oj/extconf.rb
CHANGED
|
@@ -32,16 +32,20 @@ have_func('pthread_mutex_init')
|
|
|
32
32
|
have_func('getrlimit', 'sys/resource.h')
|
|
33
33
|
have_func('rb_enc_interned_str')
|
|
34
34
|
have_func('rb_ext_ractor_safe', 'ruby.h')
|
|
35
|
+
have_func('rb_hash_start', 'ruby.h')
|
|
35
36
|
|
|
36
37
|
dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
# SIMD optimizations use runtime CPU detection and function-level target attributes
|
|
40
|
+
# We do NOT add global -msse4.2/-msse2 flags here because:
|
|
41
|
+
# 1. It would cause illegal instruction errors on CPUs without SSE4.2
|
|
42
|
+
# 2. The code uses __attribute__((target("sse4.2"))) for SSE4.2 functions
|
|
43
|
+
# 3. Runtime detection in oj_get_simd_implementation() selects the right path
|
|
44
|
+
#
|
|
45
|
+
# We only add -msse2 if available, since SSE2 is baseline for all x86_64 CPUs
|
|
46
|
+
# and needed for compiling the SSE2 fallback code on 32-bit x86
|
|
47
|
+
if try_cflags('-msse2')
|
|
48
|
+
$CPPFLAGS += ' -msse2'
|
|
45
49
|
end
|
|
46
50
|
|
|
47
51
|
if enable_config('trace-log', false)
|
data/ext/oj/fast.c
CHANGED
|
@@ -80,21 +80,10 @@ static void each_leaf(Doc doc, VALUE self);
|
|
|
80
80
|
static int move_step(Doc doc, const char *path, int loc);
|
|
81
81
|
static Leaf get_doc_leaf(Doc doc, const char *path);
|
|
82
82
|
static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
|
|
83
|
-
static void each_value(Doc doc, Leaf leaf);
|
|
83
|
+
static void each_value(Doc doc, Leaf leaf, VALUE self);
|
|
84
84
|
|
|
85
85
|
VALUE oj_doc_class = Qundef;
|
|
86
86
|
|
|
87
|
-
// This is only for CentOS 5.4 with Ruby 1.9.3-p0.
|
|
88
|
-
#ifndef HAVE_STPCPY
|
|
89
|
-
char *stpcpy(char *dest, const char *src) {
|
|
90
|
-
size_t cnt = strlen(src);
|
|
91
|
-
|
|
92
|
-
strcpy(dest, src);
|
|
93
|
-
|
|
94
|
-
return dest + cnt;
|
|
95
|
-
}
|
|
96
|
-
#endif
|
|
97
|
-
|
|
98
87
|
inline static void next_non_white(ParseInfo pi) {
|
|
99
88
|
for (; 1; pi->s++) {
|
|
100
89
|
switch (*pi->s) {
|
|
@@ -103,12 +92,35 @@ inline static void next_non_white(ParseInfo pi) {
|
|
|
103
92
|
case '\f':
|
|
104
93
|
case '\n':
|
|
105
94
|
case '\r': break;
|
|
106
|
-
case '/':
|
|
95
|
+
case '/':
|
|
96
|
+
skip_comment(pi);
|
|
97
|
+
// A comment that is not terminated by a newline ends on the null
|
|
98
|
+
// terminator. Stop here so the loop does not step past it.
|
|
99
|
+
if ('\0' == *pi->s) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
107
103
|
default: return;
|
|
108
104
|
}
|
|
109
105
|
}
|
|
110
106
|
}
|
|
111
107
|
|
|
108
|
+
inline static bool is_value_end(char c) {
|
|
109
|
+
switch (c) {
|
|
110
|
+
case '\0':
|
|
111
|
+
case ' ':
|
|
112
|
+
case '\t':
|
|
113
|
+
case '\f':
|
|
114
|
+
case '\n':
|
|
115
|
+
case '\r':
|
|
116
|
+
case '/':
|
|
117
|
+
case ',':
|
|
118
|
+
case ']':
|
|
119
|
+
case '}': return true;
|
|
120
|
+
default: return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
112
124
|
inline static char *ulong_fill(char *s, size_t num) {
|
|
113
125
|
char buf[32];
|
|
114
126
|
char *b = buf + sizeof(buf) - 1;
|
|
@@ -221,10 +233,11 @@ static void skip_comment(ParseInfo pi) {
|
|
|
221
233
|
if ('*' == *pi->s && '/' == *(pi->s + 1)) {
|
|
222
234
|
pi->s++;
|
|
223
235
|
return;
|
|
224
|
-
} else if ('\0' == *pi->s) {
|
|
225
|
-
raise_error("comment not terminated", pi->str, pi->s);
|
|
226
236
|
}
|
|
227
237
|
}
|
|
238
|
+
// The loop only ends on the null terminator so the comment was never
|
|
239
|
+
// closed.
|
|
240
|
+
raise_error("comment not terminated", pi->str, pi->s);
|
|
228
241
|
} else if ('/' == *pi->s) {
|
|
229
242
|
for (; 1; pi->s++) {
|
|
230
243
|
switch (*pi->s) {
|
|
@@ -246,6 +259,19 @@ static void skip_comment(ParseInfo pi) {
|
|
|
246
259
|
#define NUM_MAX (FIXNUM_MAX >> 8)
|
|
247
260
|
#endif
|
|
248
261
|
|
|
262
|
+
static void validate_integer_size(size_t limit, char *head, char *tail) {
|
|
263
|
+
size_t total = (size_t)(tail - head);
|
|
264
|
+
bool has_sign = (head[0] == '-' || head[0] == '+');
|
|
265
|
+
size_t digit_count = total - (has_sign ? 1 : 0);
|
|
266
|
+
|
|
267
|
+
if (digit_count > limit) {
|
|
268
|
+
rb_raise(oj_parse_error_class,
|
|
269
|
+
"integer exceeds :max_integer_digits (%lu > %lu)",
|
|
270
|
+
(unsigned long)digit_count,
|
|
271
|
+
(unsigned long)limit);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
249
275
|
static void leaf_fixnum_value(Leaf leaf) {
|
|
250
276
|
char *s = leaf->str;
|
|
251
277
|
int64_t n = 0;
|
|
@@ -265,7 +291,12 @@ static void leaf_fixnum_value(Leaf leaf) {
|
|
|
265
291
|
}
|
|
266
292
|
}
|
|
267
293
|
if (big) {
|
|
268
|
-
|
|
294
|
+
size_t limit = oj_default_options.max_integer_digits;
|
|
295
|
+
char c = *s;
|
|
296
|
+
|
|
297
|
+
if (0 < limit) {
|
|
298
|
+
validate_integer_size(limit, leaf->str, s);
|
|
299
|
+
}
|
|
269
300
|
|
|
270
301
|
*s = '\0';
|
|
271
302
|
leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
|
|
@@ -448,27 +479,42 @@ static Leaf read_num(ParseInfo pi) {
|
|
|
448
479
|
char *start = pi->s;
|
|
449
480
|
int type = T_FIXNUM;
|
|
450
481
|
Leaf leaf;
|
|
482
|
+
int dcnt = 0;
|
|
483
|
+
int ecnt = 1;
|
|
451
484
|
|
|
452
|
-
|
|
485
|
+
// read_next() sends a leading '+' here as well, and Oj.load() takes one.
|
|
486
|
+
if ('-' == *pi->s || '+' == *pi->s) {
|
|
453
487
|
pi->s++;
|
|
454
488
|
}
|
|
455
489
|
// digits
|
|
456
490
|
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
|
491
|
+
dcnt++;
|
|
457
492
|
}
|
|
458
493
|
if ('.' == *pi->s) {
|
|
459
494
|
type = T_FLOAT;
|
|
460
495
|
pi->s++;
|
|
461
496
|
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
|
497
|
+
dcnt++;
|
|
462
498
|
}
|
|
463
499
|
}
|
|
464
500
|
if ('e' == *pi->s || 'E' == *pi->s) {
|
|
501
|
+
// An exponent makes it a float whether or not a '.' was seen. Without
|
|
502
|
+
// this leaf_fixnum_value() stops reading at the 'e' and the exponent is
|
|
503
|
+
// dropped.
|
|
504
|
+
type = T_FLOAT;
|
|
465
505
|
pi->s++;
|
|
466
506
|
if ('-' == *pi->s || '+' == *pi->s) {
|
|
467
507
|
pi->s++;
|
|
468
508
|
}
|
|
469
|
-
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
|
509
|
+
for (ecnt = 0; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
|
510
|
+
ecnt++;
|
|
470
511
|
}
|
|
471
512
|
}
|
|
513
|
+
// A float is converted with rb_cstr_to_dbl(), which has to be given
|
|
514
|
+
// something it can convert.
|
|
515
|
+
if (T_FLOAT == type && (0 == dcnt || 0 == ecnt || !is_value_end(*pi->s))) {
|
|
516
|
+
raise_error("invalid number", pi->str, pi->s);
|
|
517
|
+
}
|
|
472
518
|
leaf = leaf_new(pi->doc, type);
|
|
473
519
|
leaf->str = start;
|
|
474
520
|
|
|
@@ -655,7 +701,11 @@ static void doc_free(Doc doc) {
|
|
|
655
701
|
static VALUE protect_open_proc(VALUE x) {
|
|
656
702
|
ParseInfo pi = (ParseInfo)x;
|
|
657
703
|
|
|
658
|
-
pi->doc->data
|
|
704
|
+
pi->doc->data = read_next(pi); // parse
|
|
705
|
+
// read_obj() and read_array() terminate each value where it ends. The top
|
|
706
|
+
// level value has no closing delimiter to do that on, so a number leaf runs
|
|
707
|
+
// to the end of the document unless it is terminated here.
|
|
708
|
+
*pi->s = '\0';
|
|
659
709
|
*pi->doc->where = pi->doc->data;
|
|
660
710
|
pi->doc->where = pi->doc->where_path;
|
|
661
711
|
if (rb_block_given_p()) {
|
|
@@ -703,21 +753,23 @@ static void mark_doc(void *ptr) {
|
|
|
703
753
|
}
|
|
704
754
|
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
|
705
755
|
static void compact_leaf(Leaf leaf) {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
756
|
+
if (NULL != leaf) {
|
|
757
|
+
switch (leaf->value_type) {
|
|
758
|
+
case COL_VAL:
|
|
759
|
+
if (NULL != leaf->elements) {
|
|
760
|
+
Leaf first = leaf->elements->next;
|
|
761
|
+
Leaf e = first;
|
|
711
762
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
763
|
+
do {
|
|
764
|
+
compact_leaf(e);
|
|
765
|
+
e = e->next;
|
|
766
|
+
} while (e != first);
|
|
767
|
+
}
|
|
768
|
+
break;
|
|
769
|
+
case RUBY_VAL: leaf->value = rb_gc_location(leaf->value); break;
|
|
719
770
|
|
|
720
|
-
|
|
771
|
+
default: break;
|
|
772
|
+
}
|
|
721
773
|
}
|
|
722
774
|
}
|
|
723
775
|
|
|
@@ -783,14 +835,11 @@ static VALUE parse_json(VALUE clas, char *json, bool given) {
|
|
|
783
835
|
doc->self = self;
|
|
784
836
|
result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
|
|
785
837
|
if (given || 0 != ex) {
|
|
838
|
+
// The doc is detached from its wrapper so the GC will not free it.
|
|
839
|
+
// doc_free() releases both the doc and its json buffer (doc->json), so
|
|
840
|
+
// the caller must not free json separately.
|
|
786
841
|
DATA_PTR(doc->self) = NULL;
|
|
787
|
-
// TBD is this needed?
|
|
788
|
-
/*
|
|
789
842
|
doc_free(pi.doc);
|
|
790
|
-
if (0 != ex) { // will jump so caller will not free
|
|
791
|
-
OJ_R_FREE(json);
|
|
792
|
-
}
|
|
793
|
-
*/
|
|
794
843
|
} else {
|
|
795
844
|
result = doc->self;
|
|
796
845
|
}
|
|
@@ -858,7 +907,7 @@ static bool key_match(const char *pat, const char *key, int plen) {
|
|
|
858
907
|
static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
|
859
908
|
Leaf leaf = *lp;
|
|
860
909
|
|
|
861
|
-
if (MAX_STACK <= lp - stack) {
|
|
910
|
+
if (MAX_STACK <= (lp + 1) - stack) {
|
|
862
911
|
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
|
863
912
|
}
|
|
864
913
|
if ('\0' != *path) {
|
|
@@ -939,6 +988,7 @@ static void each_leaf(Doc doc, VALUE self) {
|
|
|
939
988
|
|
|
940
989
|
doc->where++;
|
|
941
990
|
if (MAX_STACK <= doc->where - doc->where_path) {
|
|
991
|
+
doc->where--;
|
|
942
992
|
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
|
943
993
|
}
|
|
944
994
|
do {
|
|
@@ -950,11 +1000,14 @@ static void each_leaf(Doc doc, VALUE self) {
|
|
|
950
1000
|
}
|
|
951
1001
|
} else {
|
|
952
1002
|
rb_yield(self);
|
|
1003
|
+
if (NULL == DATA_PTR(self)) {
|
|
1004
|
+
rb_raise(rb_eIOError, "Document closed.");
|
|
1005
|
+
}
|
|
953
1006
|
}
|
|
954
1007
|
}
|
|
955
1008
|
|
|
956
1009
|
static int move_step(Doc doc, const char *path, int loc) {
|
|
957
|
-
if (MAX_STACK <= doc->where - doc->where_path) {
|
|
1010
|
+
if (MAX_STACK <= (doc->where + 1) - doc->where_path) {
|
|
958
1011
|
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
|
959
1012
|
}
|
|
960
1013
|
if ('\0' == *path) {
|
|
@@ -962,8 +1015,9 @@ static int move_step(Doc doc, const char *path, int loc) {
|
|
|
962
1015
|
} else {
|
|
963
1016
|
Leaf leaf;
|
|
964
1017
|
|
|
1018
|
+
// A document with no root (empty or comment only JSON) has no leaf to
|
|
1019
|
+
// step from so the path can not be located.
|
|
965
1020
|
if (0 == doc->where || 0 == (leaf = *doc->where)) {
|
|
966
|
-
printf("*** Internal error at %s\n", path);
|
|
967
1021
|
return loc;
|
|
968
1022
|
}
|
|
969
1023
|
if ('.' == *path && '.' == *(path + 1)) {
|
|
@@ -1043,19 +1097,22 @@ static int move_step(Doc doc, const char *path, int loc) {
|
|
|
1043
1097
|
return loc;
|
|
1044
1098
|
}
|
|
1045
1099
|
|
|
1046
|
-
static void each_value(Doc doc, Leaf leaf) {
|
|
1100
|
+
static void each_value(Doc doc, Leaf leaf, VALUE self) {
|
|
1047
1101
|
if (COL_VAL == leaf->value_type) {
|
|
1048
1102
|
if (0 != leaf->elements) {
|
|
1049
1103
|
Leaf first = leaf->elements->next;
|
|
1050
1104
|
Leaf e = first;
|
|
1051
1105
|
|
|
1052
1106
|
do {
|
|
1053
|
-
each_value(doc, e);
|
|
1107
|
+
each_value(doc, e, self);
|
|
1054
1108
|
e = e->next;
|
|
1055
1109
|
} while (e != first);
|
|
1056
1110
|
}
|
|
1057
1111
|
} else {
|
|
1058
1112
|
rb_yield(leaf_value(doc, leaf));
|
|
1113
|
+
if (NULL == DATA_PTR(self)) {
|
|
1114
|
+
rb_raise(rb_eIOError, "Document closed.");
|
|
1115
|
+
}
|
|
1059
1116
|
}
|
|
1060
1117
|
}
|
|
1061
1118
|
|
|
@@ -1091,12 +1148,7 @@ static VALUE doc_open(VALUE clas, VALUE str) {
|
|
|
1091
1148
|
|
|
1092
1149
|
memcpy(json, StringValuePtr(str), len);
|
|
1093
1150
|
obj = parse_json(clas, json, given);
|
|
1094
|
-
//
|
|
1095
|
-
/*
|
|
1096
|
-
if (given) {
|
|
1097
|
-
OJ_R_FREE(json);
|
|
1098
|
-
}
|
|
1099
|
-
*/
|
|
1151
|
+
// json is owned by the doc and freed by doc_free(); do not free it here.
|
|
1100
1152
|
return obj;
|
|
1101
1153
|
}
|
|
1102
1154
|
|
|
@@ -1128,7 +1180,9 @@ static VALUE doc_open_file(VALUE clas, VALUE filename) {
|
|
|
1128
1180
|
int given = rb_block_given_p();
|
|
1129
1181
|
|
|
1130
1182
|
path = StringValuePtr(filename);
|
|
1131
|
-
|
|
1183
|
+
// Open in binary mode. On Windows a text mode read translates CRLF into LF
|
|
1184
|
+
// so fewer bytes are read than the file size reported by ftell().
|
|
1185
|
+
if (0 == (f = fopen(path, "rb"))) {
|
|
1132
1186
|
rb_raise(rb_eIOError, "%s", strerror(errno));
|
|
1133
1187
|
}
|
|
1134
1188
|
fseek(f, 0, SEEK_END);
|
|
@@ -1146,12 +1200,7 @@ static VALUE doc_open_file(VALUE clas, VALUE filename) {
|
|
|
1146
1200
|
fclose(f);
|
|
1147
1201
|
json[len] = '\0';
|
|
1148
1202
|
obj = parse_json(clas, json, given);
|
|
1149
|
-
//
|
|
1150
|
-
/*
|
|
1151
|
-
if (given) {
|
|
1152
|
-
OJ_R_FREE(json);
|
|
1153
|
-
}
|
|
1154
|
-
*/
|
|
1203
|
+
// json is owned by the doc and freed by doc_free(); do not free it here.
|
|
1155
1204
|
return obj;
|
|
1156
1205
|
}
|
|
1157
1206
|
|
|
@@ -1250,10 +1299,14 @@ static VALUE doc_path(VALUE self) {
|
|
|
1250
1299
|
* #=> nil
|
|
1251
1300
|
*/
|
|
1252
1301
|
static VALUE doc_local_key(VALUE self) {
|
|
1253
|
-
Doc doc
|
|
1254
|
-
Leaf leaf
|
|
1255
|
-
volatile VALUE key
|
|
1302
|
+
Doc doc = self_doc(self);
|
|
1303
|
+
Leaf leaf;
|
|
1304
|
+
volatile VALUE key = Qnil;
|
|
1256
1305
|
|
|
1306
|
+
if (NULL == doc->where || NULL == *doc->where) {
|
|
1307
|
+
return Qnil;
|
|
1308
|
+
}
|
|
1309
|
+
leaf = *doc->where;
|
|
1257
1310
|
if (T_HASH == leaf->parent_type) {
|
|
1258
1311
|
key = rb_utf8_str_new_cstr(leaf->key);
|
|
1259
1312
|
} else if (T_ARRAY == leaf->parent_type) {
|
|
@@ -1408,6 +1461,9 @@ static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
|
|
1408
1461
|
return Qnil;
|
|
1409
1462
|
}
|
|
1410
1463
|
}
|
|
1464
|
+
if (NULL == doc->where || NULL == *doc->where) {
|
|
1465
|
+
return Qnil;
|
|
1466
|
+
}
|
|
1411
1467
|
each_leaf(doc, self);
|
|
1412
1468
|
if (0 < wlen) {
|
|
1413
1469
|
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
|
@@ -1491,12 +1547,19 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
|
1491
1547
|
Leaf first = (*doc->where)->elements->next;
|
|
1492
1548
|
Leaf e = first;
|
|
1493
1549
|
|
|
1550
|
+
if (MAX_STACK <= (doc->where + 1) - doc->where_path) {
|
|
1551
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
|
1552
|
+
}
|
|
1494
1553
|
doc->where++;
|
|
1495
1554
|
do {
|
|
1496
1555
|
*doc->where = e;
|
|
1497
1556
|
rb_yield(self);
|
|
1557
|
+
if (NULL == DATA_PTR(self)) {
|
|
1558
|
+
rb_raise(rb_eIOError, "Document closed.");
|
|
1559
|
+
}
|
|
1498
1560
|
e = e->next;
|
|
1499
1561
|
} while (e != first);
|
|
1562
|
+
doc->where--;
|
|
1500
1563
|
}
|
|
1501
1564
|
if (0 < wlen) {
|
|
1502
1565
|
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
|
@@ -1540,7 +1603,7 @@ static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
|
|
|
1540
1603
|
path = StringValuePtr(*argv);
|
|
1541
1604
|
}
|
|
1542
1605
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
|
1543
|
-
each_value(doc, leaf);
|
|
1606
|
+
each_value(doc, leaf, self);
|
|
1544
1607
|
}
|
|
1545
1608
|
}
|
|
1546
1609
|
return Qnil;
|
data/ext/oj/intern.c
CHANGED
|
@@ -69,7 +69,7 @@ static VALUE form_attr(const char *str, size_t len) {
|
|
|
69
69
|
memcpy(b + 1, str, len);
|
|
70
70
|
b[len + 1] = '\0';
|
|
71
71
|
}
|
|
72
|
-
id = rb_intern3(
|
|
72
|
+
id = rb_intern3(b, len + 1, oj_utf8_encoding);
|
|
73
73
|
OJ_R_FREE(b);
|
|
74
74
|
return id;
|
|
75
75
|
}
|
|
@@ -97,6 +97,8 @@ static const rb_data_type_t oj_cache_type = {
|
|
|
97
97
|
};
|
|
98
98
|
|
|
99
99
|
void oj_hash_init(void) {
|
|
100
|
+
oj_hash_seed_init();
|
|
101
|
+
|
|
100
102
|
VALUE cache_class = rb_define_class_under(Oj, "Cache", rb_cObject);
|
|
101
103
|
rb_undef_alloc_func(cache_class);
|
|
102
104
|
|
|
@@ -151,7 +153,7 @@ ID oj_attr_intern(const char *key, size_t len) {
|
|
|
151
153
|
static uint64_t hash_calc(const uint8_t *key, size_t len) {
|
|
152
154
|
const uint8_t *end = key + len;
|
|
153
155
|
const uint8_t *endless = key + (len & 0xFFFFFFFC);
|
|
154
|
-
uint64_t h = (uint64_t)len;
|
|
156
|
+
uint64_t h = (uint64_t)len ^ oj_hash_seed;
|
|
155
157
|
uint64_t k;
|
|
156
158
|
|
|
157
159
|
while (key < endless) {
|
|
@@ -188,6 +190,9 @@ static VALUE resolve_classname(VALUE mod, const char *classname, int auto_define
|
|
|
188
190
|
|
|
189
191
|
if (rb_const_defined_at(mod, ci)) {
|
|
190
192
|
clas = rb_const_get_at(mod, ci);
|
|
193
|
+
if (!RB_TYPE_P(clas, T_CLASS) && !RB_TYPE_P(clas, T_MODULE)) {
|
|
194
|
+
clas = Qundef;
|
|
195
|
+
}
|
|
191
196
|
} else if (auto_define) {
|
|
192
197
|
clas = rb_define_class_under(mod, classname, oj_bag_class);
|
|
193
198
|
} else {
|
data/ext/oj/mimic_json.c
CHANGED
|
@@ -711,6 +711,7 @@ static struct _options mimic_object_to_json_options = {0, // indent
|
|
|
711
711
|
0, // cache_str
|
|
712
712
|
0, // int_range_min
|
|
713
713
|
0, // int_range_max
|
|
714
|
+
0, // max_integer_digits
|
|
714
715
|
oj_json_class, // create_id
|
|
715
716
|
10, // create_id_len
|
|
716
717
|
3, // sec_prec
|
|
@@ -735,6 +736,8 @@ static struct _options mimic_object_to_json_options = {0, // indent
|
|
|
735
736
|
false, // omit_nil
|
|
736
737
|
false, // omit_null_byte
|
|
737
738
|
100, // max_depth
|
|
739
|
+
NULL, // only
|
|
740
|
+
NULL, // except
|
|
738
741
|
},
|
|
739
742
|
{
|
|
740
743
|
// str_rx
|