oj 3.16.3 → 3.17.4
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 +99 -0
- data/README.md +0 -16
- data/ext/oj/compat.c +3 -3
- data/ext/oj/custom.c +17 -11
- data/ext/oj/dump.c +619 -88
- data/ext/oj/dump.h +9 -2
- data/ext/oj/dump_compat.c +16 -9
- data/ext/oj/dump_leaf.c +1 -1
- data/ext/oj/dump_object.c +52 -27
- data/ext/oj/dump_strict.c +20 -14
- data/ext/oj/extconf.rb +11 -9
- data/ext/oj/fast.c +90 -65
- data/ext/oj/intern.c +1 -1
- data/ext/oj/mimic_json.c +14 -8
- data/ext/oj/object.c +18 -12
- data/ext/oj/oj.c +426 -11
- data/ext/oj/oj.h +60 -52
- data/ext/oj/parse.c +252 -48
- data/ext/oj/parse.h +0 -1
- data/ext/oj/parser.c +179 -47
- data/ext/oj/parser.h +4 -2
- data/ext/oj/rails.c +57 -36
- data/ext/oj/reader.c +17 -5
- data/ext/oj/rxclass.c +17 -1
- data/ext/oj/rxclass.h +2 -1
- data/ext/oj/safe.c +230 -0
- data/ext/oj/safe.h +79 -0
- data/ext/oj/saj.c +55 -20
- data/ext/oj/scp.c +3 -6
- data/ext/oj/simd.h +219 -0
- data/ext/oj/sparse.c +15 -3
- data/ext/oj/stream_writer.c +19 -14
- data/ext/oj/strict.c +2 -4
- data/ext/oj/string_writer.c +26 -14
- data/ext/oj/usual.c +37 -35
- data/ext/oj/wab.c +5 -4
- data/lib/oj/mimic.rb +1 -5
- data/lib/oj/schandler.rb +5 -4
- data/lib/oj/version.rb +1 -1
- data/pages/Encoding.md +1 -1
- metadata +22 -100
- data/test/_test_active.rb +0 -75
- data/test/_test_active_mimic.rb +0 -95
- data/test/_test_mimic_rails.rb +0 -123
- data/test/activerecord/result_test.rb +0 -31
- data/test/activesupport6/abstract_unit.rb +0 -44
- data/test/activesupport6/decoding_test.rb +0 -133
- data/test/activesupport6/encoding_test.rb +0 -507
- data/test/activesupport6/encoding_test_cases.rb +0 -98
- data/test/activesupport6/test_common.rb +0 -17
- data/test/activesupport6/test_helper.rb +0 -163
- data/test/activesupport6/time_zone_test_helpers.rb +0 -39
- data/test/activesupport7/abstract_unit.rb +0 -49
- data/test/activesupport7/decoding_test.rb +0 -125
- data/test/activesupport7/encoding_test.rb +0 -486
- data/test/activesupport7/encoding_test_cases.rb +0 -104
- data/test/activesupport7/time_zone_test_helpers.rb +0 -47
- data/test/files.rb +0 -29
- data/test/foo.rb +0 -14
- data/test/helper.rb +0 -39
- data/test/isolated/shared.rb +0 -309
- data/test/isolated/test_mimic_after.rb +0 -13
- data/test/isolated/test_mimic_alone.rb +0 -12
- data/test/isolated/test_mimic_as_json.rb +0 -45
- data/test/isolated/test_mimic_before.rb +0 -13
- data/test/isolated/test_mimic_define.rb +0 -28
- data/test/isolated/test_mimic_rails_after.rb +0 -22
- data/test/isolated/test_mimic_rails_before.rb +0 -21
- data/test/isolated/test_mimic_redefine.rb +0 -15
- data/test/json_gem/json_addition_test.rb +0 -216
- data/test/json_gem/json_common_interface_test.rb +0 -155
- data/test/json_gem/json_encoding_test.rb +0 -107
- data/test/json_gem/json_ext_parser_test.rb +0 -21
- data/test/json_gem/json_fixtures_test.rb +0 -36
- data/test/json_gem/json_generator_test.rb +0 -413
- data/test/json_gem/json_generic_object_test.rb +0 -90
- data/test/json_gem/json_parser_test.rb +0 -477
- data/test/json_gem/json_string_matching_test.rb +0 -42
- data/test/json_gem/test_helper.rb +0 -30
- data/test/mem.rb +0 -34
- data/test/perf.rb +0 -102
- data/test/perf_compat.rb +0 -128
- data/test/perf_dump.rb +0 -50
- data/test/perf_fast.rb +0 -162
- data/test/perf_file.rb +0 -62
- data/test/perf_object.rb +0 -134
- data/test/perf_once.rb +0 -59
- data/test/perf_parser.rb +0 -183
- data/test/perf_saj.rb +0 -101
- data/test/perf_scp.rb +0 -140
- data/test/perf_simple.rb +0 -289
- data/test/perf_strict.rb +0 -137
- data/test/perf_wab.rb +0 -129
- data/test/prec.rb +0 -23
- data/test/sample/change.rb +0 -13
- data/test/sample/dir.rb +0 -18
- data/test/sample/doc.rb +0 -35
- data/test/sample/file.rb +0 -47
- data/test/sample/group.rb +0 -15
- data/test/sample/hasprops.rb +0 -15
- data/test/sample/layer.rb +0 -11
- data/test/sample/line.rb +0 -20
- data/test/sample/oval.rb +0 -10
- data/test/sample/rect.rb +0 -9
- data/test/sample/shape.rb +0 -34
- data/test/sample/text.rb +0 -19
- data/test/sample.rb +0 -54
- data/test/sample_json.rb +0 -37
- data/test/test_compat.rb +0 -567
- data/test/test_custom.rb +0 -555
- data/test/test_debian.rb +0 -50
- data/test/test_fast.rb +0 -526
- data/test/test_file.rb +0 -250
- data/test/test_gc.rb +0 -60
- data/test/test_generate.rb +0 -21
- data/test/test_hash.rb +0 -39
- data/test/test_integer_range.rb +0 -72
- data/test/test_null.rb +0 -376
- data/test/test_object.rb +0 -1030
- data/test/test_parser.rb +0 -11
- data/test/test_parser_debug.rb +0 -27
- data/test/test_parser_saj.rb +0 -337
- data/test/test_parser_usual.rb +0 -227
- data/test/test_rails.rb +0 -35
- data/test/test_saj.rb +0 -188
- data/test/test_scp.rb +0 -431
- data/test/test_strict.rb +0 -441
- data/test/test_various.rb +0 -801
- data/test/test_wab.rb +0 -311
- data/test/test_writer.rb +0 -380
- data/test/tests.rb +0 -33
- data/test/tests_mimic.rb +0 -23
- data/test/tests_mimic_addition.rb +0 -16
data/ext/oj/oj.c
CHANGED
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
#include "odd.h"
|
|
19
19
|
#include "parse.h"
|
|
20
20
|
#include "rails.h"
|
|
21
|
+
#include "simd.h"
|
|
22
|
+
|
|
23
|
+
#define MAX_INDENT 16
|
|
21
24
|
|
|
22
25
|
typedef struct _yesNoOpt {
|
|
23
26
|
VALUE sym;
|
|
@@ -36,6 +39,7 @@ ID oj_as_json_id;
|
|
|
36
39
|
ID oj_begin_id;
|
|
37
40
|
ID oj_bigdecimal_id;
|
|
38
41
|
ID oj_end_id;
|
|
42
|
+
ID oj_eofq_id;
|
|
39
43
|
ID oj_exclude_end_id;
|
|
40
44
|
ID oj_error_id;
|
|
41
45
|
ID oj_file_id;
|
|
@@ -118,7 +122,9 @@ static VALUE create_id_sym;
|
|
|
118
122
|
static VALUE custom_sym;
|
|
119
123
|
static VALUE empty_string_sym;
|
|
120
124
|
static VALUE escape_mode_sym;
|
|
125
|
+
static VALUE except_sym;
|
|
121
126
|
static VALUE integer_range_sym;
|
|
127
|
+
static VALUE max_integer_digits_sym;
|
|
122
128
|
static VALUE fast_sym;
|
|
123
129
|
static VALUE float_prec_sym;
|
|
124
130
|
static VALUE float_format_sym;
|
|
@@ -136,6 +142,7 @@ static VALUE null_sym;
|
|
|
136
142
|
static VALUE object_sym;
|
|
137
143
|
static VALUE omit_null_byte_sym;
|
|
138
144
|
static VALUE omit_nil_sym;
|
|
145
|
+
static VALUE only_sym;
|
|
139
146
|
static VALUE rails_sym;
|
|
140
147
|
static VALUE raise_sym;
|
|
141
148
|
static VALUE ruby_sym;
|
|
@@ -165,6 +172,8 @@ pthread_mutex_t oj_cache_mutex;
|
|
|
165
172
|
VALUE oj_cache_mutex = Qnil;
|
|
166
173
|
#endif
|
|
167
174
|
|
|
175
|
+
SIMD_Implementation SIMD_Impl = SIMD_NONE;
|
|
176
|
+
|
|
168
177
|
extern void oj_parser_init();
|
|
169
178
|
|
|
170
179
|
const char oj_json_class[] = "json_class";
|
|
@@ -200,6 +209,7 @@ struct _options oj_default_options = {
|
|
|
200
209
|
0, // cache_str
|
|
201
210
|
0, // int_range_min
|
|
202
211
|
0, // int_range_max
|
|
212
|
+
0, // max_integer_digits
|
|
203
213
|
oj_json_class, // create_id
|
|
204
214
|
10, // create_id_len
|
|
205
215
|
9, // sec_prec
|
|
@@ -224,6 +234,8 @@ struct _options oj_default_options = {
|
|
|
224
234
|
false, // omit_nil
|
|
225
235
|
false, // omit_null_byte
|
|
226
236
|
MAX_DEPTH, // max_depth
|
|
237
|
+
NULL, // only
|
|
238
|
+
NULL, // except
|
|
227
239
|
},
|
|
228
240
|
{
|
|
229
241
|
// str_rx
|
|
@@ -234,6 +246,22 @@ struct _options oj_default_options = {
|
|
|
234
246
|
NULL,
|
|
235
247
|
};
|
|
236
248
|
|
|
249
|
+
static VALUE only_array_from_string(const char *str) {
|
|
250
|
+
volatile VALUE a = Qnil;
|
|
251
|
+
|
|
252
|
+
if (NULL != str && 2 < strlen(str)) {
|
|
253
|
+
str++;
|
|
254
|
+
char *cp;
|
|
255
|
+
|
|
256
|
+
a = rb_ary_new();
|
|
257
|
+
while (NULL != (cp = strchr(str, ':'))) {
|
|
258
|
+
rb_ary_push(a, rb_id2sym(rb_intern2(str, cp - str)));
|
|
259
|
+
str = cp + 1;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return a;
|
|
263
|
+
}
|
|
264
|
+
|
|
237
265
|
/* Document-method: default_options()
|
|
238
266
|
* call-seq: default_options()
|
|
239
267
|
*
|
|
@@ -310,10 +338,17 @@ struct _options oj_default_options = {
|
|
|
310
338
|
* - *:cache_str* [_Fixnum_] maximum string value length to cache (strings less
|
|
311
339
|
* than this are cached)
|
|
312
340
|
* - *:integer_range* [_Range_] Dump integers outside range as strings.
|
|
341
|
+
* - *:max_integer_digits* [_Fixnum_] Maximum number of decimal digits allowed in a
|
|
342
|
+
* parsed integer. When the limit is exceeded a parse error is raised. 0 (the
|
|
343
|
+
* default) disables the limit. Setting a reasonable limit is recommended when
|
|
344
|
+
* parsing untrusted input to mitigate CPU-DoS attacks. Only applies to the
|
|
345
|
+
* legacy parsers (Oj.load, Oj::Doc, JSON.parse mimic); Oj::Parser is unaffected.
|
|
313
346
|
* - *:trace* [_true,_|_false_] Trace all load and dump calls, default is false
|
|
314
347
|
* (trace is off)
|
|
315
348
|
* - *:safe* [_true,_|_false_] Safe mimic breaks JSON mimic to be safer, default
|
|
316
349
|
* is false (safe is off)
|
|
350
|
+
* - *:only* [_nil,_|_Array_] A list of the fields to encode. All others are skipped.
|
|
351
|
+
* - *:except* [_nil,_|_Array_] A list of the fields to not encode. All others are encoded.
|
|
317
352
|
*
|
|
318
353
|
* Return [_Hash_] all current option settings.
|
|
319
354
|
*/
|
|
@@ -422,6 +457,7 @@ static VALUE get_def_opts(VALUE self) {
|
|
|
422
457
|
} else {
|
|
423
458
|
rb_hash_aset(opts, integer_range_sym, Qnil);
|
|
424
459
|
}
|
|
460
|
+
rb_hash_aset(opts, max_integer_digits_sym, LONG2NUM((long)oj_default_options.max_integer_digits));
|
|
425
461
|
switch (oj_default_options.escape_mode) {
|
|
426
462
|
case NLEsc: rb_hash_aset(opts, escape_mode_sym, newline_sym); break;
|
|
427
463
|
case JSONEsc: rb_hash_aset(opts, escape_mode_sym, json_sym); break;
|
|
@@ -479,6 +515,9 @@ static VALUE get_def_opts(VALUE self) {
|
|
|
479
515
|
rb_hash_aset(opts, oj_hash_class_sym, oj_default_options.hash_class);
|
|
480
516
|
rb_hash_aset(opts, oj_array_class_sym, oj_default_options.array_class);
|
|
481
517
|
|
|
518
|
+
rb_hash_aset(opts, only_sym, only_array_from_string(oj_default_options.dump_opts.only));
|
|
519
|
+
rb_hash_aset(opts, except_sym, only_array_from_string(oj_default_options.dump_opts.except));
|
|
520
|
+
|
|
482
521
|
if (NULL == oj_default_options.ignore) {
|
|
483
522
|
rb_hash_aset(opts, ignore_sym, Qnil);
|
|
484
523
|
} else {
|
|
@@ -562,12 +601,57 @@ static VALUE get_def_opts(VALUE self) {
|
|
|
562
601
|
* - *:cache_keys* [_Boolean_] if true then hash keys are cached
|
|
563
602
|
* - *:cache_str* [_Fixnum_] maximum string value length to cache (strings less than this are cached)
|
|
564
603
|
* - *:integer_range* [_Range_] Dump integers outside range as strings.
|
|
604
|
+
* - *:max_integer_digits* [_Fixnum_] Maximum decimal digits in a parsed integer
|
|
605
|
+
* (0 = unlimited). Use to mitigate CPU-DoS via huge integer values in JSON.
|
|
565
606
|
* - *:trace* [_Boolean_] turn trace on or off.
|
|
566
607
|
* - *:safe* [_Boolean_] turn safe mimic on or off.
|
|
567
608
|
*/
|
|
609
|
+
static ID oj_default_options_keeper_id = 0;
|
|
610
|
+
|
|
611
|
+
// The default options are a plain C global so the Ruby objects in them, the
|
|
612
|
+
// classes of the hash_class, array_class, and ignore options and the regexps
|
|
613
|
+
// and the classes of the match_string option, can not be reached by the GC and
|
|
614
|
+
// are collected while the defaults still refer to them. Holding them in an
|
|
615
|
+
// array the GC does know about keeps them alive. The array is rebuilt whenever
|
|
616
|
+
// the defaults change and replaces the previous one so that only the objects
|
|
617
|
+
// the defaults are currently using are held.
|
|
618
|
+
static void keep_default_options(void) {
|
|
619
|
+
VALUE keep = rb_ary_new();
|
|
620
|
+
|
|
621
|
+
if (0 == oj_default_options_keeper_id) {
|
|
622
|
+
oj_default_options_keeper_id = rb_intern("@default_options_keeper");
|
|
623
|
+
}
|
|
624
|
+
if (Qnil != oj_default_options.hash_class) {
|
|
625
|
+
rb_ary_push(keep, oj_default_options.hash_class);
|
|
626
|
+
}
|
|
627
|
+
if (Qnil != oj_default_options.array_class) {
|
|
628
|
+
rb_ary_push(keep, oj_default_options.array_class);
|
|
629
|
+
}
|
|
630
|
+
if (NULL != oj_default_options.ignore) {
|
|
631
|
+
VALUE *vp;
|
|
632
|
+
|
|
633
|
+
for (vp = oj_default_options.ignore; Qnil != *vp; vp++) {
|
|
634
|
+
rb_ary_push(keep, *vp);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
oj_rxclass_keep(&oj_default_options.str_rx, keep);
|
|
638
|
+
// An instance variable of the Oj module is a reference the GC can follow on
|
|
639
|
+
// every engine, unlike a C global.
|
|
640
|
+
rb_ivar_set(Oj, oj_default_options_keeper_id, keep);
|
|
641
|
+
}
|
|
642
|
+
|
|
568
643
|
static VALUE set_def_opts(VALUE self, VALUE opts) {
|
|
569
644
|
Check_Type(opts, T_HASH);
|
|
645
|
+
// A :match_string option replaces the regexps instead of adding to them so
|
|
646
|
+
// the ones the defaults are holding have to be freed here, the only place
|
|
647
|
+
// that owns them. A per call options struct only aliases them.
|
|
648
|
+
if (Qnil != rb_hash_lookup(opts, match_string_sym)) {
|
|
649
|
+
oj_rxclass_cleanup(&oj_default_options.str_rx);
|
|
650
|
+
oj_default_options.str_rx.head = NULL;
|
|
651
|
+
oj_default_options.str_rx.tail = NULL;
|
|
652
|
+
}
|
|
570
653
|
oj_parse_options(opts, &oj_default_options);
|
|
654
|
+
keep_default_options();
|
|
571
655
|
|
|
572
656
|
return Qnil;
|
|
573
657
|
}
|
|
@@ -621,6 +705,95 @@ bool set_yesno_options(VALUE key, VALUE value, Options copts) {
|
|
|
621
705
|
return false;
|
|
622
706
|
}
|
|
623
707
|
|
|
708
|
+
static const char *make_only_value(VALUE v) {
|
|
709
|
+
switch (rb_type(v)) {
|
|
710
|
+
case RUBY_T_NIL:
|
|
711
|
+
case RUBY_T_NONE: return NULL;
|
|
712
|
+
case RUBY_T_ARRAY: {
|
|
713
|
+
long len = rb_array_len(v);
|
|
714
|
+
long i;
|
|
715
|
+
long size = 0;
|
|
716
|
+
char *buf;
|
|
717
|
+
char *bp;
|
|
718
|
+
|
|
719
|
+
for (i = 0; i < len; i++) {
|
|
720
|
+
VALUE x = rb_ary_entry(v, i);
|
|
721
|
+
|
|
722
|
+
switch (rb_type(x)) {
|
|
723
|
+
case RUBY_T_SYMBOL:
|
|
724
|
+
size += strlen(rb_id2name(rb_sym2id(x)));
|
|
725
|
+
size++;
|
|
726
|
+
break;
|
|
727
|
+
case RUBY_T_STRING:
|
|
728
|
+
size += strlen(StringValueCStr(x));
|
|
729
|
+
size++;
|
|
730
|
+
break;
|
|
731
|
+
default: rb_raise(rb_eArgError, ":only and :except must be nil, symbol, string, or array."); break;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
if (0 == size) {
|
|
735
|
+
return NULL;
|
|
736
|
+
}
|
|
737
|
+
buf = OJ_R_ALLOC_N(char, size + 2);
|
|
738
|
+
bp = buf;
|
|
739
|
+
*bp++ = ':';
|
|
740
|
+
for (i = 0; i < len; i++) {
|
|
741
|
+
VALUE x = rb_ary_entry(v, i);
|
|
742
|
+
const char *str;
|
|
743
|
+
|
|
744
|
+
switch (rb_type(x)) {
|
|
745
|
+
case RUBY_T_SYMBOL:
|
|
746
|
+
str = rb_id2name(rb_sym2id(x));
|
|
747
|
+
size = strlen(str);
|
|
748
|
+
memcpy(bp, str, size);
|
|
749
|
+
bp += size;
|
|
750
|
+
*bp++ = ':';
|
|
751
|
+
break;
|
|
752
|
+
case RUBY_T_STRING:
|
|
753
|
+
str = StringValueCStr(x);
|
|
754
|
+
size = strlen(str);
|
|
755
|
+
memcpy(bp, str, size);
|
|
756
|
+
bp += size;
|
|
757
|
+
*bp++ = ':';
|
|
758
|
+
break;
|
|
759
|
+
default:
|
|
760
|
+
// ignore
|
|
761
|
+
break;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
*bp = '\0';
|
|
765
|
+
|
|
766
|
+
return buf;
|
|
767
|
+
}
|
|
768
|
+
case RUBY_T_STRING: {
|
|
769
|
+
const char *str = StringValueCStr(v);
|
|
770
|
+
size_t size = strlen(str);
|
|
771
|
+
char *buf = OJ_R_ALLOC_N(char, size + 3);
|
|
772
|
+
|
|
773
|
+
buf[0] = ':';
|
|
774
|
+
strcpy(buf + 1, str);
|
|
775
|
+
buf[size + 1] = ':';
|
|
776
|
+
buf[size + 2] = '\0';
|
|
777
|
+
|
|
778
|
+
return buf;
|
|
779
|
+
}
|
|
780
|
+
case RUBY_T_SYMBOL: {
|
|
781
|
+
const char *str = rb_id2name(rb_sym2id(v));
|
|
782
|
+
size_t size = strlen(str);
|
|
783
|
+
char *buf = OJ_R_ALLOC_N(char, size + 3);
|
|
784
|
+
|
|
785
|
+
buf[0] = ':';
|
|
786
|
+
strcpy(buf + 1, str);
|
|
787
|
+
buf[size + 1] = ':';
|
|
788
|
+
buf[size + 2] = '\0';
|
|
789
|
+
|
|
790
|
+
return buf;
|
|
791
|
+
}
|
|
792
|
+
default: rb_raise(rb_eArgError, ":only and zzz :except must be nil, symbol, string, or array."); break;
|
|
793
|
+
}
|
|
794
|
+
return NULL;
|
|
795
|
+
}
|
|
796
|
+
|
|
624
797
|
static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
625
798
|
Options copts = (Options)opts;
|
|
626
799
|
size_t len;
|
|
@@ -638,7 +811,10 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
|
638
811
|
case T_FIXNUM:
|
|
639
812
|
copts->dump_opts.indent_size = 0;
|
|
640
813
|
*copts->dump_opts.indent_str = '\0';
|
|
641
|
-
|
|
814
|
+
if (MAX_INDENT < FIX2INT(v)) {
|
|
815
|
+
rb_raise(rb_eArgError, "indent is limited to %d characters.", MAX_INDENT);
|
|
816
|
+
}
|
|
817
|
+
copts->indent = FIX2INT(v);
|
|
642
818
|
break;
|
|
643
819
|
case T_STRING:
|
|
644
820
|
if (sizeof(copts->dump_opts.indent_str) <= (len = RSTRING_LEN(v))) {
|
|
@@ -788,6 +964,9 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
|
788
964
|
|
|
789
965
|
len = RSTRING_LEN(v);
|
|
790
966
|
if (len != copts->create_id_len || 0 != strcmp(copts->create_id, str)) {
|
|
967
|
+
if (&oj_default_options == copts && oj_json_class != copts->create_id) {
|
|
968
|
+
OJ_R_FREE((char *)copts->create_id);
|
|
969
|
+
}
|
|
791
970
|
copts->create_id = OJ_R_ALLOC_N(char, len + 1);
|
|
792
971
|
strcpy((char *)copts->create_id, str);
|
|
793
972
|
copts->create_id_len = len;
|
|
@@ -919,15 +1098,19 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
|
919
1098
|
copts->array_class = v;
|
|
920
1099
|
}
|
|
921
1100
|
} else if (ignore_sym == k) {
|
|
922
|
-
|
|
1101
|
+
// Only free when replacing the defaults; a per-call copy's ignore still
|
|
1102
|
+
// aliases the default-owned buffer, which oj_free_call_options() frees.
|
|
1103
|
+
if (&oj_default_options == copts) {
|
|
1104
|
+
OJ_R_FREE(copts->ignore);
|
|
1105
|
+
}
|
|
923
1106
|
copts->ignore = NULL;
|
|
924
1107
|
if (Qnil != v) {
|
|
925
|
-
|
|
1108
|
+
size_t cnt;
|
|
926
1109
|
|
|
927
1110
|
rb_check_type(v, T_ARRAY);
|
|
928
|
-
cnt =
|
|
1111
|
+
cnt = RARRAY_LEN(v);
|
|
929
1112
|
if (0 < cnt) {
|
|
930
|
-
|
|
1113
|
+
size_t i;
|
|
931
1114
|
|
|
932
1115
|
copts->ignore = OJ_R_ALLOC_N(VALUE, cnt + 1);
|
|
933
1116
|
for (i = 0; i < cnt; i++) {
|
|
@@ -953,6 +1136,20 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
|
953
1136
|
} else if (Qfalse != v) {
|
|
954
1137
|
rb_raise(rb_eArgError, ":integer_range must be a range of Fixnum.");
|
|
955
1138
|
}
|
|
1139
|
+
} else if (max_integer_digits_sym == k) {
|
|
1140
|
+
if (Qnil == v || Qfalse == v) {
|
|
1141
|
+
copts->max_integer_digits = 0;
|
|
1142
|
+
} else if (T_FIXNUM == rb_type(v)) {
|
|
1143
|
+
long n = FIX2LONG(v);
|
|
1144
|
+
|
|
1145
|
+
if (n < 0) {
|
|
1146
|
+
rb_raise(rb_eArgError, ":max_integer_digits must be >= 0.");
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
copts->max_integer_digits = (size_t)n;
|
|
1150
|
+
} else {
|
|
1151
|
+
rb_raise(rb_eArgError, ":max_integer_digits must be a non-negative Integer.");
|
|
1152
|
+
}
|
|
956
1153
|
} else if (symbol_keys_sym == k || oj_symbolize_names_sym == k) {
|
|
957
1154
|
if (Qnil == v) {
|
|
958
1155
|
return ST_CONTINUE;
|
|
@@ -972,11 +1169,23 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
|
|
|
972
1169
|
}
|
|
973
1170
|
} else if (float_format_sym == k) {
|
|
974
1171
|
rb_check_type(v, T_STRING);
|
|
975
|
-
if (6 <
|
|
1172
|
+
if (6 < RSTRING_LEN(v)) {
|
|
976
1173
|
rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
|
|
977
1174
|
}
|
|
978
1175
|
strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
|
|
979
1176
|
copts->float_fmt[RSTRING_LEN(v)] = '\0';
|
|
1177
|
+
} else if (only_sym == k) {
|
|
1178
|
+
// Only free when replacing the defaults; a per-call copy's pointer still
|
|
1179
|
+
// aliases the default-owned buffer, which oj_free_call_options() frees.
|
|
1180
|
+
if (&oj_default_options == copts && NULL != copts->dump_opts.only) {
|
|
1181
|
+
OJ_R_FREE((void *)copts->dump_opts.only);
|
|
1182
|
+
}
|
|
1183
|
+
copts->dump_opts.only = make_only_value(v);
|
|
1184
|
+
} else if (except_sym == k) {
|
|
1185
|
+
if (&oj_default_options == copts && NULL != copts->dump_opts.except) {
|
|
1186
|
+
OJ_R_FREE((void *)copts->dump_opts.except);
|
|
1187
|
+
}
|
|
1188
|
+
copts->dump_opts.except = make_only_value(v);
|
|
980
1189
|
}
|
|
981
1190
|
return ST_CONTINUE;
|
|
982
1191
|
}
|
|
@@ -994,6 +1203,120 @@ void oj_parse_options(VALUE ropts, Options copts) {
|
|
|
994
1203
|
return;
|
|
995
1204
|
}
|
|
996
1205
|
|
|
1206
|
+
// Free the option buffers that oj_parse_options() allocated for a single call.
|
|
1207
|
+
// A per-call options struct is a copy of oj_default_options, so its create_id,
|
|
1208
|
+
// ignore, only, and except members initially alias the ones owned by the
|
|
1209
|
+
// defaults. Only free a member when it differs from the default, i.e. when it
|
|
1210
|
+
// was allocated for this call; the default-owned buffers must be left alone.
|
|
1211
|
+
void oj_free_call_options(Options copts) {
|
|
1212
|
+
if (NULL != copts->create_id && oj_default_options.create_id != copts->create_id) {
|
|
1213
|
+
OJ_R_FREE((char *)copts->create_id);
|
|
1214
|
+
copts->create_id = NULL;
|
|
1215
|
+
copts->create_id_len = 0;
|
|
1216
|
+
}
|
|
1217
|
+
if (NULL != copts->ignore && oj_default_options.ignore != copts->ignore) {
|
|
1218
|
+
OJ_R_FREE(copts->ignore);
|
|
1219
|
+
copts->ignore = NULL;
|
|
1220
|
+
}
|
|
1221
|
+
if (NULL != copts->dump_opts.only && oj_default_options.dump_opts.only != copts->dump_opts.only) {
|
|
1222
|
+
OJ_R_FREE((void *)copts->dump_opts.only);
|
|
1223
|
+
copts->dump_opts.only = NULL;
|
|
1224
|
+
}
|
|
1225
|
+
if (NULL != copts->dump_opts.except && oj_default_options.dump_opts.except != copts->dump_opts.except) {
|
|
1226
|
+
OJ_R_FREE((void *)copts->dump_opts.except);
|
|
1227
|
+
copts->dump_opts.except = NULL;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
static char *dup_option_str(const char *str, size_t len) {
|
|
1232
|
+
char *buf = OJ_R_ALLOC_N(char, len + 1);
|
|
1233
|
+
|
|
1234
|
+
memcpy(buf, str, len);
|
|
1235
|
+
buf[len] = '\0';
|
|
1236
|
+
|
|
1237
|
+
return buf;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// Give an options struct private copies of the option buffers it still shares
|
|
1241
|
+
// with the defaults so that it owns all of them.
|
|
1242
|
+
//
|
|
1243
|
+
// An options struct that outlives the call it was created in can not rely on
|
|
1244
|
+
// the default-owned buffers it aliases: Oj.default_options= is free to replace
|
|
1245
|
+
// them at any time, which would leave the alias dangling. Call this once, in
|
|
1246
|
+
// the call that creates such an object and after oj_parse_options(), then free
|
|
1247
|
+
// the buffers with oj_options_release() when the object is collected.
|
|
1248
|
+
void oj_options_take_ownership(Options copts) {
|
|
1249
|
+
if (NULL != copts->create_id && oj_default_options.create_id == copts->create_id) {
|
|
1250
|
+
copts->create_id = dup_option_str(copts->create_id, copts->create_id_len);
|
|
1251
|
+
}
|
|
1252
|
+
if (NULL != copts->ignore && oj_default_options.ignore == copts->ignore) {
|
|
1253
|
+
VALUE *vp;
|
|
1254
|
+
VALUE *buf;
|
|
1255
|
+
size_t cnt = 0;
|
|
1256
|
+
|
|
1257
|
+
for (vp = copts->ignore; Qnil != *vp; vp++) {
|
|
1258
|
+
cnt++;
|
|
1259
|
+
}
|
|
1260
|
+
buf = OJ_R_ALLOC_N(VALUE, cnt + 1);
|
|
1261
|
+
memcpy(buf, copts->ignore, sizeof(VALUE) * (cnt + 1));
|
|
1262
|
+
copts->ignore = buf;
|
|
1263
|
+
}
|
|
1264
|
+
if (NULL != copts->dump_opts.only && oj_default_options.dump_opts.only == copts->dump_opts.only) {
|
|
1265
|
+
copts->dump_opts.only = dup_option_str(copts->dump_opts.only, strlen(copts->dump_opts.only));
|
|
1266
|
+
}
|
|
1267
|
+
if (NULL != copts->dump_opts.except && oj_default_options.dump_opts.except == copts->dump_opts.except) {
|
|
1268
|
+
copts->dump_opts.except = dup_option_str(copts->dump_opts.except, strlen(copts->dump_opts.except));
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
// Free the option buffers of an options struct that owns all of them, i.e. one
|
|
1273
|
+
// that oj_options_take_ownership() was called on. Unlike oj_free_call_options()
|
|
1274
|
+
// this never looks at the defaults, so it is safe to call from a GC free
|
|
1275
|
+
// function, where the defaults may have been replaced since the object was
|
|
1276
|
+
// created.
|
|
1277
|
+
void oj_options_release(Options copts) {
|
|
1278
|
+
if (NULL != copts->create_id) {
|
|
1279
|
+
OJ_R_FREE((char *)copts->create_id);
|
|
1280
|
+
copts->create_id = NULL;
|
|
1281
|
+
copts->create_id_len = 0;
|
|
1282
|
+
}
|
|
1283
|
+
if (NULL != copts->ignore) {
|
|
1284
|
+
OJ_R_FREE(copts->ignore);
|
|
1285
|
+
copts->ignore = NULL;
|
|
1286
|
+
}
|
|
1287
|
+
if (NULL != copts->dump_opts.only) {
|
|
1288
|
+
OJ_R_FREE((void *)copts->dump_opts.only);
|
|
1289
|
+
copts->dump_opts.only = NULL;
|
|
1290
|
+
}
|
|
1291
|
+
if (NULL != copts->dump_opts.except) {
|
|
1292
|
+
OJ_R_FREE((void *)copts->dump_opts.except);
|
|
1293
|
+
copts->dump_opts.except = NULL;
|
|
1294
|
+
}
|
|
1295
|
+
// The str_rx chain is detached from the defaults when the owner is created
|
|
1296
|
+
// so the chain, if there is one, was built for this options struct alone.
|
|
1297
|
+
oj_rxclass_cleanup(&copts->str_rx);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// Mark the Ruby objects held by an owned options struct. A long lived object
|
|
1301
|
+
// such as a writer or an encoder can be the only reference to the classes in
|
|
1302
|
+
// the options so without this they could be collected while the owner is still
|
|
1303
|
+
// alive leaving the options with dangling VALUEs.
|
|
1304
|
+
void oj_options_mark(Options copts) {
|
|
1305
|
+
if (Qnil != copts->hash_class) {
|
|
1306
|
+
rb_gc_mark(copts->hash_class);
|
|
1307
|
+
}
|
|
1308
|
+
if (Qnil != copts->array_class) {
|
|
1309
|
+
rb_gc_mark(copts->array_class);
|
|
1310
|
+
}
|
|
1311
|
+
if (NULL != copts->ignore) {
|
|
1312
|
+
VALUE *vp;
|
|
1313
|
+
|
|
1314
|
+
for (vp = copts->ignore; Qnil != *vp; vp++) {
|
|
1315
|
+
rb_gc_mark(*vp);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
|
|
997
1320
|
static int match_string_cb(VALUE key, VALUE value, VALUE rx) {
|
|
998
1321
|
RxClass rc = (RxClass)rx;
|
|
999
1322
|
|
|
@@ -1187,7 +1510,7 @@ static VALUE load_file(int argc, VALUE *argv, VALUE self) {
|
|
|
1187
1510
|
OJ_FREE(wide_path);
|
|
1188
1511
|
}
|
|
1189
1512
|
#else
|
|
1190
|
-
fd
|
|
1513
|
+
fd = open(path, O_RDONLY);
|
|
1191
1514
|
#endif
|
|
1192
1515
|
if (0 == fd) {
|
|
1193
1516
|
rb_raise(rb_eIOError, "%s", strerror(errno));
|
|
@@ -1276,8 +1599,7 @@ static VALUE dump_body(VALUE a) {
|
|
|
1276
1599
|
if (0 == arg->out->buf) {
|
|
1277
1600
|
rb_raise(rb_eNoMemError, "Not enough memory.");
|
|
1278
1601
|
}
|
|
1279
|
-
rstr =
|
|
1280
|
-
rstr = oj_encode(rstr);
|
|
1602
|
+
rstr = rb_utf8_str_new_cstr(arg->out->buf);
|
|
1281
1603
|
|
|
1282
1604
|
return rstr;
|
|
1283
1605
|
}
|
|
@@ -1286,6 +1608,7 @@ static VALUE dump_ensure(VALUE a) {
|
|
|
1286
1608
|
volatile struct dump_arg *arg = (void *)a;
|
|
1287
1609
|
|
|
1288
1610
|
oj_out_free(arg->out);
|
|
1611
|
+
oj_free_call_options(arg->copts);
|
|
1289
1612
|
|
|
1290
1613
|
return Qnil;
|
|
1291
1614
|
}
|
|
@@ -1377,8 +1700,7 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
|
|
|
1377
1700
|
if (0 == out.buf) {
|
|
1378
1701
|
rb_raise(rb_eNoMemError, "Not enough memory.");
|
|
1379
1702
|
}
|
|
1380
|
-
rstr =
|
|
1381
|
-
rstr = oj_encode(rstr);
|
|
1703
|
+
rstr = rb_utf8_str_new_cstr(out.buf);
|
|
1382
1704
|
|
|
1383
1705
|
oj_out_free(&out);
|
|
1384
1706
|
|
|
@@ -1402,6 +1724,7 @@ static VALUE to_file(int argc, VALUE *argv, VALUE self) {
|
|
|
1402
1724
|
oj_parse_options(argv[2], &copts);
|
|
1403
1725
|
}
|
|
1404
1726
|
oj_write_obj_to_file(argv[1], StringValuePtr(*argv), &copts);
|
|
1727
|
+
oj_free_call_options(&copts);
|
|
1405
1728
|
|
|
1406
1729
|
return Qnil;
|
|
1407
1730
|
}
|
|
@@ -1423,6 +1746,7 @@ static VALUE to_stream(int argc, VALUE *argv, VALUE self) {
|
|
|
1423
1746
|
oj_parse_options(argv[2], &copts);
|
|
1424
1747
|
}
|
|
1425
1748
|
oj_write_obj_to_stream(argv[1], *argv, &copts);
|
|
1749
|
+
oj_free_call_options(&copts);
|
|
1426
1750
|
|
|
1427
1751
|
return Qnil;
|
|
1428
1752
|
}
|
|
@@ -1780,6 +2104,78 @@ static VALUE mem_report(VALUE self) {
|
|
|
1780
2104
|
*
|
|
1781
2105
|
* - *:wab* specifically for WAB data exchange.
|
|
1782
2106
|
*/
|
|
2107
|
+
|
|
2108
|
+
// =============================================================================
|
|
2109
|
+
// Runtime SIMD CPU detection
|
|
2110
|
+
// Cross-platform support for Windows (MSVC), Linux, and macOS (GCC/Clang)
|
|
2111
|
+
// =============================================================================
|
|
2112
|
+
SIMD_Implementation oj_get_simd_implementation(void) {
|
|
2113
|
+
#ifdef HAVE_SIMD_X86
|
|
2114
|
+
// x86/x86_64 runtime detection
|
|
2115
|
+
|
|
2116
|
+
#if defined(_MSC_VER)
|
|
2117
|
+
// MSVC: Use __cpuid intrinsic
|
|
2118
|
+
int cpu_info[4];
|
|
2119
|
+
__cpuid(cpu_info, 1);
|
|
2120
|
+
|
|
2121
|
+
// Check for SSE4.2 (bit 20 of ECX)
|
|
2122
|
+
if (cpu_info[2] & (1 << 20)) {
|
|
2123
|
+
return SIMD_SSE42;
|
|
2124
|
+
}
|
|
2125
|
+
// Check for SSE2 (bit 26 of EDX)
|
|
2126
|
+
if (cpu_info[3] & (1 << 26)) {
|
|
2127
|
+
return SIMD_SSE2;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
#elif defined(__GNUC__) || defined(__clang__)
|
|
2131
|
+
// GCC/Clang: Use __builtin_cpu_supports if available
|
|
2132
|
+
#if defined(__has_builtin)
|
|
2133
|
+
#if __has_builtin(__builtin_cpu_supports)
|
|
2134
|
+
#define OJ_HAS_BUILTIN_CPU_SUPPORTS 1
|
|
2135
|
+
#endif
|
|
2136
|
+
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
|
|
2137
|
+
// GCC 4.8+ has __builtin_cpu_supports
|
|
2138
|
+
#define OJ_HAS_BUILTIN_CPU_SUPPORTS 1
|
|
2139
|
+
#endif
|
|
2140
|
+
|
|
2141
|
+
#ifdef OJ_HAS_BUILTIN_CPU_SUPPORTS
|
|
2142
|
+
#ifdef HAVE_SIMD_SSE4_2
|
|
2143
|
+
if (__builtin_cpu_supports("sse4.2")) {
|
|
2144
|
+
return SIMD_SSE42;
|
|
2145
|
+
}
|
|
2146
|
+
#endif
|
|
2147
|
+
#ifdef HAVE_SIMD_SSE2
|
|
2148
|
+
if (__builtin_cpu_supports("sse2")) {
|
|
2149
|
+
return SIMD_SSE2;
|
|
2150
|
+
}
|
|
2151
|
+
#endif
|
|
2152
|
+
#else
|
|
2153
|
+
// Fallback: Use CPUID instruction directly
|
|
2154
|
+
unsigned int eax, ebx, ecx, edx;
|
|
2155
|
+
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
|
|
2156
|
+
// Check for SSE4.2 (bit 20 of ECX)
|
|
2157
|
+
if (ecx & (1 << 20)) {
|
|
2158
|
+
return SIMD_SSE42;
|
|
2159
|
+
}
|
|
2160
|
+
// Check for SSE2 (bit 26 of EDX)
|
|
2161
|
+
if (edx & (1 << 26)) {
|
|
2162
|
+
return SIMD_SSE2;
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
#endif // OJ_HAS_BUILTIN_CPU_SUPPORTS
|
|
2166
|
+
|
|
2167
|
+
#endif // _MSC_VER vs GCC/Clang
|
|
2168
|
+
|
|
2169
|
+
#endif // HAVE_SIMD_X86
|
|
2170
|
+
|
|
2171
|
+
#ifdef HAVE_SIMD_NEON
|
|
2172
|
+
// ARM NEON is always available on ARM64 and detected at compile time
|
|
2173
|
+
return SIMD_NEON;
|
|
2174
|
+
#endif
|
|
2175
|
+
|
|
2176
|
+
return SIMD_NONE;
|
|
2177
|
+
}
|
|
2178
|
+
|
|
1783
2179
|
void Init_oj(void) {
|
|
1784
2180
|
int err = 0;
|
|
1785
2181
|
|
|
@@ -1849,6 +2245,7 @@ void Init_oj(void) {
|
|
|
1849
2245
|
oj_begin_id = rb_intern("begin");
|
|
1850
2246
|
oj_bigdecimal_id = rb_intern("BigDecimal");
|
|
1851
2247
|
oj_end_id = rb_intern("end");
|
|
2248
|
+
oj_eofq_id = rb_intern("eof?");
|
|
1852
2249
|
oj_error_id = rb_intern("error");
|
|
1853
2250
|
oj_exclude_end_id = rb_intern("exclude_end?");
|
|
1854
2251
|
oj_file_id = rb_intern("file?");
|
|
@@ -1952,6 +2349,8 @@ void Init_oj(void) {
|
|
|
1952
2349
|
rb_gc_register_address(&escape_mode_sym);
|
|
1953
2350
|
integer_range_sym = ID2SYM(rb_intern("integer_range"));
|
|
1954
2351
|
rb_gc_register_address(&integer_range_sym);
|
|
2352
|
+
max_integer_digits_sym = ID2SYM(rb_intern("max_integer_digits"));
|
|
2353
|
+
rb_gc_register_address(&max_integer_digits_sym);
|
|
1955
2354
|
fast_sym = ID2SYM(rb_intern("fast"));
|
|
1956
2355
|
rb_gc_register_address(&fast_sym);
|
|
1957
2356
|
float_format_sym = ID2SYM(rb_intern("float_format"));
|
|
@@ -2058,6 +2457,10 @@ void Init_oj(void) {
|
|
|
2058
2457
|
rb_gc_register_address(&xmlschema_sym);
|
|
2059
2458
|
xss_safe_sym = ID2SYM(rb_intern("xss_safe"));
|
|
2060
2459
|
rb_gc_register_address(&xss_safe_sym);
|
|
2460
|
+
only_sym = ID2SYM(rb_intern("only"));
|
|
2461
|
+
rb_gc_register_address(&only_sym);
|
|
2462
|
+
except_sym = ID2SYM(rb_intern("except"));
|
|
2463
|
+
rb_gc_register_address(&except_sym);
|
|
2061
2464
|
|
|
2062
2465
|
oj_slash_string = rb_str_new2("/");
|
|
2063
2466
|
rb_gc_register_address(&oj_slash_string);
|
|
@@ -2079,6 +2482,18 @@ void Init_oj(void) {
|
|
|
2079
2482
|
#endif
|
|
2080
2483
|
oj_init_doc();
|
|
2081
2484
|
|
|
2485
|
+
SIMD_Impl = oj_get_simd_implementation();
|
|
2486
|
+
|
|
2082
2487
|
oj_parser_init();
|
|
2083
2488
|
oj_scanner_init();
|
|
2489
|
+
|
|
2490
|
+
#ifdef HAVE_SIMD_NEON
|
|
2491
|
+
initialize_neon();
|
|
2492
|
+
#endif /* HAVE_SIMD_NEON */
|
|
2493
|
+
|
|
2494
|
+
#ifdef HAVE_SIMD_SSE4_2
|
|
2495
|
+
if (SIMD_Impl == SIMD_SSE42) {
|
|
2496
|
+
initialize_sse42();
|
|
2497
|
+
}
|
|
2498
|
+
#endif /* HAVE_SIMD_SSE4_2 */
|
|
2084
2499
|
}
|