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/intern.c
CHANGED
|
@@ -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/object.c
CHANGED
|
@@ -31,7 +31,7 @@ inline static long read_long(const char *str, size_t len) {
|
|
|
31
31
|
static VALUE calc_hash_key(ParseInfo pi, Val kval, char k1) {
|
|
32
32
|
volatile VALUE rkey;
|
|
33
33
|
|
|
34
|
-
if (':' == k1) {
|
|
34
|
+
if (':' == k1 && 0 < kval->klen) {
|
|
35
35
|
return ID2SYM(rb_intern3(kval->key + 1, kval->klen - 1, oj_utf8_encoding));
|
|
36
36
|
}
|
|
37
37
|
if (Yes == pi->options.sym_key) {
|
|
@@ -54,7 +54,7 @@ static VALUE str_to_value(ParseInfo pi, const char *str, size_t len, const char
|
|
|
54
54
|
} else if (pi->circ_array && 3 <= len && '^' == *orig && 'r' == orig[1]) {
|
|
55
55
|
long i = read_long(str + 2, len - 2);
|
|
56
56
|
|
|
57
|
-
if (0
|
|
57
|
+
if (0 >= i) {
|
|
58
58
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a valid ID number");
|
|
59
59
|
return Qnil;
|
|
60
60
|
}
|
|
@@ -207,7 +207,7 @@ oj_parse_xml_time(const char *str, int len) {
|
|
|
207
207
|
|
|
208
208
|
static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t len) {
|
|
209
209
|
const char *key = kval->key;
|
|
210
|
-
|
|
210
|
+
size_t klen = kval->klen;
|
|
211
211
|
|
|
212
212
|
if (2 == klen) {
|
|
213
213
|
switch (key[1]) {
|
|
@@ -230,7 +230,12 @@ static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t
|
|
|
230
230
|
parent->odd_args = oj_odd_alloc_args(odd);
|
|
231
231
|
break;
|
|
232
232
|
}
|
|
233
|
-
case 'm':
|
|
233
|
+
case 'm':
|
|
234
|
+
if (0 == len) {
|
|
235
|
+
return 0;
|
|
236
|
+
}
|
|
237
|
+
parent->val = ID2SYM(rb_intern3(str + 1, len - 1, oj_utf8_encoding));
|
|
238
|
+
break;
|
|
234
239
|
case 's': parent->val = rb_utf8_str_new(str, len); break;
|
|
235
240
|
case 'c': // class
|
|
236
241
|
{
|
|
@@ -319,20 +324,26 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
|
|
|
319
324
|
e1 = *RARRAY_CONST_PTR(value);
|
|
320
325
|
// check for anonymous Struct
|
|
321
326
|
if (T_ARRAY == rb_type(e1)) {
|
|
322
|
-
|
|
323
|
-
|
|
327
|
+
// Build the member-name list in a GC-managed Ruby array. Using a
|
|
328
|
+
// fixed C buffer (VALUE args[1024]) here overflowed the stack when
|
|
329
|
+
// the JSON supplied more than 1024 member names.
|
|
330
|
+
volatile VALUE names = rb_ary_new2(RARRAY_LEN(e1));
|
|
324
331
|
size_t i;
|
|
325
332
|
size_t cnt = RARRAY_LEN(e1);
|
|
326
333
|
|
|
327
334
|
for (i = 0; i < cnt; i++) {
|
|
328
|
-
|
|
329
|
-
args[i] = rb_funcall(rstr, oj_to_sym_id, 0);
|
|
335
|
+
rb_ary_push(names, rb_funcall(RARRAY_AREF(e1, i), oj_to_sym_id, 0));
|
|
330
336
|
}
|
|
331
|
-
sc =
|
|
337
|
+
sc = rb_apply(rb_cStruct, oj_new_id, names);
|
|
332
338
|
} else {
|
|
333
339
|
// If struct is not defined then we let this fail and raise an exception.
|
|
334
340
|
sc = oj_name2struct(pi, *RARRAY_CONST_PTR(value), rb_eArgError);
|
|
335
341
|
}
|
|
342
|
+
if (Qundef == sc || Qnil == sc) {
|
|
343
|
+
// oj_name2struct already recorded the error; do not pass an
|
|
344
|
+
// unresolved (Qundef) class on to rb_obj_alloc/rb_class_new_instance.
|
|
345
|
+
return 1;
|
|
346
|
+
}
|
|
336
347
|
if (sc == rb_cRange) {
|
|
337
348
|
parent->val = rb_class_new_instance((int)(len - 1), RARRAY_CONST_PTR(value) + 1, rb_cRange);
|
|
338
349
|
} else {
|
|
@@ -389,7 +400,7 @@ void oj_set_obj_ivar(Val parent, Val kval, VALUE value) {
|
|
|
389
400
|
|
|
390
401
|
static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
|
|
391
402
|
const char *key = kval->key;
|
|
392
|
-
|
|
403
|
+
size_t klen = kval->klen;
|
|
393
404
|
Val parent = stack_peek(&pi->stack);
|
|
394
405
|
volatile VALUE rval = Qnil;
|
|
395
406
|
|
|
@@ -431,7 +442,7 @@ WHICH_TYPE:
|
|
|
431
442
|
if (0 != oj_odd_set_arg(parent->odd_args, kval->key, kval->klen, rval)) {
|
|
432
443
|
char buf[256];
|
|
433
444
|
|
|
434
|
-
if (
|
|
445
|
+
if (sizeof(buf) - 1 <= klen) {
|
|
435
446
|
klen = sizeof(buf) - 2;
|
|
436
447
|
}
|
|
437
448
|
memcpy(buf, key, klen);
|
|
@@ -460,7 +471,7 @@ WHICH_TYPE:
|
|
|
460
471
|
|
|
461
472
|
static void hash_set_num(ParseInfo pi, Val kval, NumInfo ni) {
|
|
462
473
|
const char *key = kval->key;
|
|
463
|
-
|
|
474
|
+
size_t klen = kval->klen;
|
|
464
475
|
Val parent = stack_peek(&pi->stack);
|
|
465
476
|
volatile VALUE rval = Qnil;
|
|
466
477
|
|
|
@@ -500,7 +511,7 @@ WHICH_TYPE:
|
|
|
500
511
|
if (0 != oj_odd_set_arg(parent->odd_args, key, klen, rval)) {
|
|
501
512
|
char buf[256];
|
|
502
513
|
|
|
503
|
-
if (
|
|
514
|
+
if (sizeof(buf) - 1 <= klen) {
|
|
504
515
|
klen = sizeof(buf) - 2;
|
|
505
516
|
}
|
|
506
517
|
memcpy(buf, key, klen);
|
|
@@ -529,7 +540,7 @@ WHICH_TYPE:
|
|
|
529
540
|
|
|
530
541
|
static void hash_set_value(ParseInfo pi, Val kval, VALUE value) {
|
|
531
542
|
const char *key = kval->key;
|
|
532
|
-
|
|
543
|
+
size_t klen = kval->klen;
|
|
533
544
|
Val parent = stack_peek(&pi->stack);
|
|
534
545
|
|
|
535
546
|
WHICH_TYPE:
|
|
@@ -585,7 +596,7 @@ WHICH_TYPE:
|
|
|
585
596
|
} else if (0 != oj_odd_set_arg(parent->odd_args, key, klen, value)) {
|
|
586
597
|
char buf[256];
|
|
587
598
|
|
|
588
|
-
if (
|
|
599
|
+
if (sizeof(buf) - 1 <= klen) {
|
|
589
600
|
klen = sizeof(buf) - 2;
|
|
590
601
|
}
|
|
591
602
|
memcpy(buf, key, klen);
|
data/ext/oj/odd.c
CHANGED
|
@@ -166,7 +166,11 @@ Odd oj_get_oddc(const char *classname, size_t len) {
|
|
|
166
166
|
if (len == odd->clen && 0 == strncmp(classname, odd->classname, len)) {
|
|
167
167
|
return odd;
|
|
168
168
|
}
|
|
169
|
-
|
|
169
|
+
// The module test needs a "::" after the module name, so the name has
|
|
170
|
+
// to be shorter than what it is compared against. Without that the
|
|
171
|
+
// comparison and the character after it both run past len.
|
|
172
|
+
if (odd->is_module && odd->clen < len && 0 == strncmp(odd->classname, classname, odd->clen) &&
|
|
173
|
+
':' == classname[odd->clen]) {
|
|
170
174
|
return odd;
|
|
171
175
|
}
|
|
172
176
|
}
|
|
@@ -195,7 +199,7 @@ int oj_odd_set_arg(OddArgs args, const char *key, size_t klen, VALUE value) {
|
|
|
195
199
|
int i;
|
|
196
200
|
|
|
197
201
|
for (i = args->odd->attr_cnt, np = args->odd->attr_names, vp = args->args; 0 < i; i--, np++, vp++) {
|
|
198
|
-
if (
|
|
202
|
+
if (klen == strlen(*np) && 0 == memcmp(key, *np, klen)) {
|
|
199
203
|
*vp = value;
|
|
200
204
|
return 0;
|
|
201
205
|
}
|
data/ext/oj/oj.c
CHANGED
|
@@ -299,7 +299,8 @@ static VALUE only_array_from_string(const char *str) {
|
|
|
299
299
|
* floats, 0 indicates use Ruby
|
|
300
300
|
* - *:float_format* [_String_] the C printf format string for printing floats.
|
|
301
301
|
* Default follows the float_precision and will be changed if float_precision is
|
|
302
|
-
* changed. The string can be no more than 6 bytes
|
|
302
|
+
* changed. The string can be no more than 6 bytes and can hold at most one
|
|
303
|
+
* directive, which must be one of aAeEfgG and take no argument of its own.
|
|
303
304
|
* - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false
|
|
304
305
|
* - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false
|
|
305
306
|
* - *:use_raw_json* [_Boolean_|_nil_] call raw_json() methods on dump, default is false
|
|
@@ -574,7 +575,8 @@ static VALUE get_def_opts(VALUE self) {
|
|
|
574
575
|
* when dumping the seconds portion of time.
|
|
575
576
|
* - *:float_format* [_String_] the C printf format string for printing floats.
|
|
576
577
|
* Default follows the float_precision and will be changed if float_precision
|
|
577
|
-
* is changed. The string can be no more than 6 bytes
|
|
578
|
+
* is changed. The string can be no more than 6 bytes and can hold at most one
|
|
579
|
+
* directive, which must be one of aAeEfgG and take no argument of its own.
|
|
578
580
|
* - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0 indicates use Ruby.
|
|
579
581
|
* - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false.
|
|
580
582
|
* - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false.
|
|
@@ -606,9 +608,52 @@ static VALUE get_def_opts(VALUE self) {
|
|
|
606
608
|
* - *:trace* [_Boolean_] turn trace on or off.
|
|
607
609
|
* - *:safe* [_Boolean_] turn safe mimic on or off.
|
|
608
610
|
*/
|
|
611
|
+
static ID oj_default_options_keeper_id = 0;
|
|
612
|
+
|
|
613
|
+
// The default options are a plain C global so the Ruby objects in them, the
|
|
614
|
+
// classes of the hash_class, array_class, and ignore options and the regexps
|
|
615
|
+
// and the classes of the match_string option, can not be reached by the GC and
|
|
616
|
+
// are collected while the defaults still refer to them. Holding them in an
|
|
617
|
+
// array the GC does know about keeps them alive. The array is rebuilt whenever
|
|
618
|
+
// the defaults change and replaces the previous one so that only the objects
|
|
619
|
+
// the defaults are currently using are held.
|
|
620
|
+
static void keep_default_options(void) {
|
|
621
|
+
VALUE keep = rb_ary_new();
|
|
622
|
+
|
|
623
|
+
if (0 == oj_default_options_keeper_id) {
|
|
624
|
+
oj_default_options_keeper_id = rb_intern("@default_options_keeper");
|
|
625
|
+
}
|
|
626
|
+
if (Qnil != oj_default_options.hash_class) {
|
|
627
|
+
rb_ary_push(keep, oj_default_options.hash_class);
|
|
628
|
+
}
|
|
629
|
+
if (Qnil != oj_default_options.array_class) {
|
|
630
|
+
rb_ary_push(keep, oj_default_options.array_class);
|
|
631
|
+
}
|
|
632
|
+
if (NULL != oj_default_options.ignore) {
|
|
633
|
+
VALUE *vp;
|
|
634
|
+
|
|
635
|
+
for (vp = oj_default_options.ignore; Qnil != *vp; vp++) {
|
|
636
|
+
rb_ary_push(keep, *vp);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
oj_rxclass_keep(&oj_default_options.str_rx, keep);
|
|
640
|
+
// An instance variable of the Oj module is a reference the GC can follow on
|
|
641
|
+
// every engine, unlike a C global.
|
|
642
|
+
rb_ivar_set(Oj, oj_default_options_keeper_id, keep);
|
|
643
|
+
}
|
|
644
|
+
|
|
609
645
|
static VALUE set_def_opts(VALUE self, VALUE opts) {
|
|
610
646
|
Check_Type(opts, T_HASH);
|
|
647
|
+
// A :match_string option replaces the regexps instead of adding to them so
|
|
648
|
+
// the ones the defaults are holding have to be freed here, the only place
|
|
649
|
+
// that owns them. A per call options struct only aliases them.
|
|
650
|
+
if (Qnil != rb_hash_lookup(opts, match_string_sym)) {
|
|
651
|
+
oj_rxclass_cleanup(&oj_default_options.str_rx);
|
|
652
|
+
oj_default_options.str_rx.head = NULL;
|
|
653
|
+
oj_default_options.str_rx.tail = NULL;
|
|
654
|
+
}
|
|
611
655
|
oj_parse_options(opts, &oj_default_options);
|
|
656
|
+
keep_default_options();
|
|
612
657
|
|
|
613
658
|
return Qnil;
|
|
614
659
|
}
|
|
@@ -662,6 +707,46 @@ bool set_yesno_options(VALUE key, VALUE value, Options copts) {
|
|
|
662
707
|
return false;
|
|
663
708
|
}
|
|
664
709
|
|
|
710
|
+
inline static bool in_set(const char *set, char c) {
|
|
711
|
+
return '\0' != c && NULL != strchr(set, c);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// The format is used with one double and nothing else, so a directive that
|
|
715
|
+
// asks for anything more takes it from whatever is left in the argument
|
|
716
|
+
// registers. %s reads it as a pointer and %n writes through it.
|
|
717
|
+
static void validate_float_format(const char *str, size_t len) {
|
|
718
|
+
const char *s = str;
|
|
719
|
+
const char *end = str + len;
|
|
720
|
+
int cnt = 0;
|
|
721
|
+
|
|
722
|
+
for (; s < end; s++) {
|
|
723
|
+
if ('%' != *s) {
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
s++;
|
|
727
|
+
if (s < end && '%' == *s) {
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
if (1 < ++cnt) {
|
|
731
|
+
rb_raise(rb_eArgError, ":float_format must not have more than one directive.");
|
|
732
|
+
}
|
|
733
|
+
for (; s < end && in_set("-+ #0", *s); s++) {
|
|
734
|
+
}
|
|
735
|
+
for (; s < end && '0' <= *s && *s <= '9'; s++) {
|
|
736
|
+
}
|
|
737
|
+
if (s < end && '.' == *s) {
|
|
738
|
+
for (s++; s < end && '0' <= *s && *s <= '9'; s++) {
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
if (s < end && 'l' == *s) {
|
|
742
|
+
s++;
|
|
743
|
+
}
|
|
744
|
+
if (end <= s || !in_set("aAeEfgG", *s)) {
|
|
745
|
+
rb_raise(rb_eArgError, ":float_format directive must be one of aAeEfgG and take no argument of its own.");
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
665
750
|
static const char *make_only_value(VALUE v) {
|
|
666
751
|
switch (rb_type(v)) {
|
|
667
752
|
case RUBY_T_NIL:
|
|
@@ -910,9 +995,13 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
|
910
995
|
rb_raise(rb_eArgError, ":decimal_class must be BigDecimal or Float.");
|
|
911
996
|
}
|
|
912
997
|
} else if (create_id_sym == k) {
|
|
998
|
+
// A per call copy of the options aliases the buffer the default
|
|
999
|
+
// options own, so only the defaults may free it.
|
|
1000
|
+
bool owned = (&oj_default_options == copts && NULL != copts->create_id && oj_json_class != copts->create_id);
|
|
1001
|
+
|
|
913
1002
|
if (Qnil == v) {
|
|
914
|
-
if (
|
|
915
|
-
OJ_R_FREE((char *)
|
|
1003
|
+
if (owned) {
|
|
1004
|
+
OJ_R_FREE((char *)copts->create_id);
|
|
916
1005
|
}
|
|
917
1006
|
copts->create_id = NULL;
|
|
918
1007
|
copts->create_id_len = 0;
|
|
@@ -920,7 +1009,10 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
|
920
1009
|
const char *str = StringValuePtr(v);
|
|
921
1010
|
|
|
922
1011
|
len = RSTRING_LEN(v);
|
|
923
|
-
if (len != copts->create_id_len || 0 != strcmp(copts->create_id, str)) {
|
|
1012
|
+
if (NULL == copts->create_id || len != copts->create_id_len || 0 != strcmp(copts->create_id, str)) {
|
|
1013
|
+
if (owned) {
|
|
1014
|
+
OJ_R_FREE((char *)copts->create_id);
|
|
1015
|
+
}
|
|
924
1016
|
copts->create_id = OJ_R_ALLOC_N(char, len + 1);
|
|
925
1017
|
strcpy((char *)copts->create_id, str);
|
|
926
1018
|
copts->create_id_len = len;
|
|
@@ -1052,7 +1144,11 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
|
1052
1144
|
copts->array_class = v;
|
|
1053
1145
|
}
|
|
1054
1146
|
} else if (ignore_sym == k) {
|
|
1055
|
-
|
|
1147
|
+
// Only free when replacing the defaults; a per-call copy's ignore still
|
|
1148
|
+
// aliases the default-owned buffer, which oj_free_call_options() frees.
|
|
1149
|
+
if (&oj_default_options == copts) {
|
|
1150
|
+
OJ_R_FREE(copts->ignore);
|
|
1151
|
+
}
|
|
1056
1152
|
copts->ignore = NULL;
|
|
1057
1153
|
if (Qnil != v) {
|
|
1058
1154
|
size_t cnt;
|
|
@@ -1122,18 +1218,19 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
|
1122
1218
|
if (6 < RSTRING_LEN(v)) {
|
|
1123
1219
|
rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
|
|
1124
1220
|
}
|
|
1221
|
+
validate_float_format(RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
|
|
1125
1222
|
strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
|
|
1126
1223
|
copts->float_fmt[RSTRING_LEN(v)] = '\0';
|
|
1127
1224
|
} else if (only_sym == k) {
|
|
1128
|
-
|
|
1225
|
+
// Only free when replacing the defaults; a per-call copy's pointer still
|
|
1226
|
+
// aliases the default-owned buffer, which oj_free_call_options() frees.
|
|
1227
|
+
if (&oj_default_options == copts && NULL != copts->dump_opts.only) {
|
|
1129
1228
|
OJ_R_FREE((void *)copts->dump_opts.only);
|
|
1130
|
-
copts->dump_opts.only = NULL;
|
|
1131
1229
|
}
|
|
1132
1230
|
copts->dump_opts.only = make_only_value(v);
|
|
1133
1231
|
} else if (except_sym == k) {
|
|
1134
|
-
if (NULL != copts->dump_opts.except) {
|
|
1232
|
+
if (&oj_default_options == copts && NULL != copts->dump_opts.except) {
|
|
1135
1233
|
OJ_R_FREE((void *)copts->dump_opts.except);
|
|
1136
|
-
copts->dump_opts.except = NULL;
|
|
1137
1234
|
}
|
|
1138
1235
|
copts->dump_opts.except = make_only_value(v);
|
|
1139
1236
|
}
|
|
@@ -1153,6 +1250,120 @@ void oj_parse_options(VALUE ropts, Options copts) {
|
|
|
1153
1250
|
return;
|
|
1154
1251
|
}
|
|
1155
1252
|
|
|
1253
|
+
// Free the option buffers that oj_parse_options() allocated for a single call.
|
|
1254
|
+
// A per-call options struct is a copy of oj_default_options, so its create_id,
|
|
1255
|
+
// ignore, only, and except members initially alias the ones owned by the
|
|
1256
|
+
// defaults. Only free a member when it differs from the default, i.e. when it
|
|
1257
|
+
// was allocated for this call; the default-owned buffers must be left alone.
|
|
1258
|
+
void oj_free_call_options(Options copts) {
|
|
1259
|
+
if (NULL != copts->create_id && oj_default_options.create_id != copts->create_id) {
|
|
1260
|
+
OJ_R_FREE((char *)copts->create_id);
|
|
1261
|
+
copts->create_id = NULL;
|
|
1262
|
+
copts->create_id_len = 0;
|
|
1263
|
+
}
|
|
1264
|
+
if (NULL != copts->ignore && oj_default_options.ignore != copts->ignore) {
|
|
1265
|
+
OJ_R_FREE(copts->ignore);
|
|
1266
|
+
copts->ignore = NULL;
|
|
1267
|
+
}
|
|
1268
|
+
if (NULL != copts->dump_opts.only && oj_default_options.dump_opts.only != copts->dump_opts.only) {
|
|
1269
|
+
OJ_R_FREE((void *)copts->dump_opts.only);
|
|
1270
|
+
copts->dump_opts.only = NULL;
|
|
1271
|
+
}
|
|
1272
|
+
if (NULL != copts->dump_opts.except && oj_default_options.dump_opts.except != copts->dump_opts.except) {
|
|
1273
|
+
OJ_R_FREE((void *)copts->dump_opts.except);
|
|
1274
|
+
copts->dump_opts.except = NULL;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
static char *dup_option_str(const char *str, size_t len) {
|
|
1279
|
+
char *buf = OJ_R_ALLOC_N(char, len + 1);
|
|
1280
|
+
|
|
1281
|
+
memcpy(buf, str, len);
|
|
1282
|
+
buf[len] = '\0';
|
|
1283
|
+
|
|
1284
|
+
return buf;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
// Give an options struct private copies of the option buffers it still shares
|
|
1288
|
+
// with the defaults so that it owns all of them.
|
|
1289
|
+
//
|
|
1290
|
+
// An options struct that outlives the call it was created in can not rely on
|
|
1291
|
+
// the default-owned buffers it aliases: Oj.default_options= is free to replace
|
|
1292
|
+
// them at any time, which would leave the alias dangling. Call this once, in
|
|
1293
|
+
// the call that creates such an object and after oj_parse_options(), then free
|
|
1294
|
+
// the buffers with oj_options_release() when the object is collected.
|
|
1295
|
+
void oj_options_take_ownership(Options copts) {
|
|
1296
|
+
if (NULL != copts->create_id && oj_default_options.create_id == copts->create_id) {
|
|
1297
|
+
copts->create_id = dup_option_str(copts->create_id, copts->create_id_len);
|
|
1298
|
+
}
|
|
1299
|
+
if (NULL != copts->ignore && oj_default_options.ignore == copts->ignore) {
|
|
1300
|
+
VALUE *vp;
|
|
1301
|
+
VALUE *buf;
|
|
1302
|
+
size_t cnt = 0;
|
|
1303
|
+
|
|
1304
|
+
for (vp = copts->ignore; Qnil != *vp; vp++) {
|
|
1305
|
+
cnt++;
|
|
1306
|
+
}
|
|
1307
|
+
buf = OJ_R_ALLOC_N(VALUE, cnt + 1);
|
|
1308
|
+
memcpy(buf, copts->ignore, sizeof(VALUE) * (cnt + 1));
|
|
1309
|
+
copts->ignore = buf;
|
|
1310
|
+
}
|
|
1311
|
+
if (NULL != copts->dump_opts.only && oj_default_options.dump_opts.only == copts->dump_opts.only) {
|
|
1312
|
+
copts->dump_opts.only = dup_option_str(copts->dump_opts.only, strlen(copts->dump_opts.only));
|
|
1313
|
+
}
|
|
1314
|
+
if (NULL != copts->dump_opts.except && oj_default_options.dump_opts.except == copts->dump_opts.except) {
|
|
1315
|
+
copts->dump_opts.except = dup_option_str(copts->dump_opts.except, strlen(copts->dump_opts.except));
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
// Free the option buffers of an options struct that owns all of them, i.e. one
|
|
1320
|
+
// that oj_options_take_ownership() was called on. Unlike oj_free_call_options()
|
|
1321
|
+
// this never looks at the defaults, so it is safe to call from a GC free
|
|
1322
|
+
// function, where the defaults may have been replaced since the object was
|
|
1323
|
+
// created.
|
|
1324
|
+
void oj_options_release(Options copts) {
|
|
1325
|
+
if (NULL != copts->create_id) {
|
|
1326
|
+
OJ_R_FREE((char *)copts->create_id);
|
|
1327
|
+
copts->create_id = NULL;
|
|
1328
|
+
copts->create_id_len = 0;
|
|
1329
|
+
}
|
|
1330
|
+
if (NULL != copts->ignore) {
|
|
1331
|
+
OJ_R_FREE(copts->ignore);
|
|
1332
|
+
copts->ignore = NULL;
|
|
1333
|
+
}
|
|
1334
|
+
if (NULL != copts->dump_opts.only) {
|
|
1335
|
+
OJ_R_FREE((void *)copts->dump_opts.only);
|
|
1336
|
+
copts->dump_opts.only = NULL;
|
|
1337
|
+
}
|
|
1338
|
+
if (NULL != copts->dump_opts.except) {
|
|
1339
|
+
OJ_R_FREE((void *)copts->dump_opts.except);
|
|
1340
|
+
copts->dump_opts.except = NULL;
|
|
1341
|
+
}
|
|
1342
|
+
// The str_rx chain is detached from the defaults when the owner is created
|
|
1343
|
+
// so the chain, if there is one, was built for this options struct alone.
|
|
1344
|
+
oj_rxclass_cleanup(&copts->str_rx);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// Mark the Ruby objects held by an owned options struct. A long lived object
|
|
1348
|
+
// such as a writer or an encoder can be the only reference to the classes in
|
|
1349
|
+
// the options so without this they could be collected while the owner is still
|
|
1350
|
+
// alive leaving the options with dangling VALUEs.
|
|
1351
|
+
void oj_options_mark(Options copts) {
|
|
1352
|
+
if (Qnil != copts->hash_class) {
|
|
1353
|
+
rb_gc_mark(copts->hash_class);
|
|
1354
|
+
}
|
|
1355
|
+
if (Qnil != copts->array_class) {
|
|
1356
|
+
rb_gc_mark(copts->array_class);
|
|
1357
|
+
}
|
|
1358
|
+
if (NULL != copts->ignore) {
|
|
1359
|
+
VALUE *vp;
|
|
1360
|
+
|
|
1361
|
+
for (vp = copts->ignore; Qnil != *vp; vp++) {
|
|
1362
|
+
rb_gc_mark(*vp);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1156
1367
|
static int match_string_cb(VALUE key, VALUE value, VALUE rx) {
|
|
1157
1368
|
RxClass rc = (RxClass)rx;
|
|
1158
1369
|
|
|
@@ -1444,6 +1655,7 @@ static VALUE dump_ensure(VALUE a) {
|
|
|
1444
1655
|
volatile struct dump_arg *arg = (void *)a;
|
|
1445
1656
|
|
|
1446
1657
|
oj_out_free(arg->out);
|
|
1658
|
+
oj_free_call_options(arg->copts);
|
|
1447
1659
|
|
|
1448
1660
|
return Qnil;
|
|
1449
1661
|
}
|
|
@@ -1559,6 +1771,7 @@ static VALUE to_file(int argc, VALUE *argv, VALUE self) {
|
|
|
1559
1771
|
oj_parse_options(argv[2], &copts);
|
|
1560
1772
|
}
|
|
1561
1773
|
oj_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
|
|
1774
|
+
oj_free_call_options(&copts);
|
|
1562
1775
|
|
|
1563
1776
|
return Qnil;
|
|
1564
1777
|
}
|
|
@@ -1580,6 +1793,7 @@ static VALUE to_stream(int argc, VALUE *argv, VALUE self) {
|
|
|
1580
1793
|
oj_parse_options(argv[2], &copts);
|
|
1581
1794
|
}
|
|
1582
1795
|
oj_write_obj_to_stream(argv[1], *argv, &copts);
|
|
1796
|
+
oj_free_call_options(&copts);
|
|
1583
1797
|
|
|
1584
1798
|
return Qnil;
|
|
1585
1799
|
}
|
data/ext/oj/oj.h
CHANGED
|
@@ -197,6 +197,7 @@ typedef struct _out {
|
|
|
197
197
|
bool allocated;
|
|
198
198
|
bool omit_nil;
|
|
199
199
|
bool omit_null_byte;
|
|
200
|
+
bool key_filter_off; // rails: suppress the :only/:except key filter for an as_json result
|
|
200
201
|
int argc;
|
|
201
202
|
VALUE *argv;
|
|
202
203
|
ROptTable ropts;
|
|
@@ -256,6 +257,10 @@ extern VALUE oj_custom_parse_cstr(int argc, VALUE *argv, char *json, size_t len)
|
|
|
256
257
|
|
|
257
258
|
extern bool oj_hash_has_key(VALUE hash, VALUE key);
|
|
258
259
|
extern void oj_parse_options(VALUE ropts, Options copts);
|
|
260
|
+
extern void oj_free_call_options(Options copts);
|
|
261
|
+
extern void oj_options_take_ownership(Options copts);
|
|
262
|
+
extern void oj_options_release(Options copts);
|
|
263
|
+
extern void oj_options_mark(Options copts);
|
|
259
264
|
|
|
260
265
|
extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
|
|
261
266
|
extern void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
|
|
@@ -321,7 +326,6 @@ extern VALUE oj_max_nesting_sym;
|
|
|
321
326
|
extern VALUE oj_object_class_sym;
|
|
322
327
|
extern VALUE oj_object_nl_sym;
|
|
323
328
|
extern VALUE oj_quirks_mode_sym;
|
|
324
|
-
extern VALUE oj_skip_null_byte_sym;
|
|
325
329
|
extern VALUE oj_space_before_sym;
|
|
326
330
|
extern VALUE oj_space_sym;
|
|
327
331
|
extern VALUE oj_symbolize_names_sym;
|
data/ext/oj/parse.c
CHANGED
|
@@ -414,6 +414,11 @@ static void read_escaped_str(ParseInfo pi, const char *start) {
|
|
|
414
414
|
|
|
415
415
|
if ('\\' == *s) {
|
|
416
416
|
s++;
|
|
417
|
+
if (pi->end <= s) {
|
|
418
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "quoted string not terminated");
|
|
419
|
+
buf_cleanup(&buf);
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
417
422
|
switch (*s) {
|
|
418
423
|
case 'n': buf_append(&buf, '\n'); break;
|
|
419
424
|
case 'r': buf_append(&buf, '\r'); break;
|
|
@@ -1096,7 +1101,9 @@ void oj_set_error_at(ParseInfo pi, VALUE err_clas, const char *file, int line, c
|
|
|
1096
1101
|
}
|
|
1097
1102
|
va_end(ap);
|
|
1098
1103
|
pi->err.clas = err_clas;
|
|
1099
|
-
|
|
1104
|
+
// The eight bytes below, plus the ')' and the terminator that the two bytes
|
|
1105
|
+
// held back from end are for.
|
|
1106
|
+
if (p + 8 <= end) {
|
|
1100
1107
|
*p++ = ' ';
|
|
1101
1108
|
*p++ = '(';
|
|
1102
1109
|
*p++ = 'a';
|
|
@@ -1306,9 +1313,16 @@ CLEANUP:
|
|
|
1306
1313
|
OJ_R_FREE(json);
|
|
1307
1314
|
}
|
|
1308
1315
|
stack_cleanup(&pi->stack);
|
|
1309
|
-
|
|
1310
|
-
|
|
1316
|
+
// The parsers match against pi->options.str_rx. A :match_string option
|
|
1317
|
+
// builds a chain of its own for this call, which has to be freed. Without
|
|
1318
|
+
// the option the member still aliases the chain owned by the defaults and
|
|
1319
|
+
// must be left alone.
|
|
1320
|
+
if (pi->options.str_rx.head != oj_default_options.str_rx.head) {
|
|
1321
|
+
oj_rxclass_cleanup(&pi->options.str_rx);
|
|
1322
|
+
pi->options.str_rx.head = NULL;
|
|
1323
|
+
pi->options.str_rx.tail = NULL;
|
|
1311
1324
|
}
|
|
1325
|
+
oj_free_call_options(&pi->options);
|
|
1312
1326
|
if (err_has(&pi->err)) {
|
|
1313
1327
|
rb_set_errinfo(Qnil);
|
|
1314
1328
|
if (Qnil != pi->err_class) {
|