oj 3.9.2 → 3.10.18
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/README.md +2 -2
- data/ext/oj/buf.h +2 -30
- data/ext/oj/cache8.h +1 -29
- data/ext/oj/circarray.c +4 -8
- data/ext/oj/circarray.h +1 -4
- data/ext/oj/code.c +3 -6
- data/ext/oj/code.h +1 -4
- data/ext/oj/compat.c +6 -9
- data/ext/oj/custom.c +8 -7
- data/ext/oj/dump.c +33 -26
- data/ext/oj/dump.h +1 -4
- data/ext/oj/dump_compat.c +9 -14
- data/ext/oj/dump_leaf.c +2 -5
- data/ext/oj/dump_object.c +19 -15
- data/ext/oj/dump_strict.c +7 -9
- data/ext/oj/encode.h +1 -29
- data/ext/oj/err.c +1 -4
- data/ext/oj/err.h +1 -29
- data/ext/oj/extconf.rb +5 -0
- data/ext/oj/fast.c +14 -42
- data/ext/oj/hash.c +4 -32
- data/ext/oj/hash.h +1 -29
- data/ext/oj/hash_test.c +1 -29
- data/ext/oj/mimic_json.c +28 -10
- data/ext/oj/object.c +4 -6
- data/ext/oj/odd.c +1 -4
- data/ext/oj/odd.h +1 -4
- data/ext/oj/oj.c +74 -38
- data/ext/oj/oj.h +9 -7
- data/ext/oj/parse.c +127 -52
- data/ext/oj/parse.h +4 -5
- data/ext/oj/rails.c +38 -8
- data/ext/oj/rails.h +1 -4
- data/ext/oj/reader.c +5 -8
- data/ext/oj/reader.h +2 -5
- data/ext/oj/resolve.c +1 -4
- data/ext/oj/resolve.h +1 -4
- data/ext/oj/rxclass.c +3 -6
- data/ext/oj/rxclass.h +1 -4
- data/ext/oj/saj.c +6 -9
- data/ext/oj/scp.c +1 -4
- data/ext/oj/sparse.c +31 -26
- data/ext/oj/stream_writer.c +4 -9
- data/ext/oj/strict.c +3 -6
- data/ext/oj/string_writer.c +1 -4
- data/ext/oj/trace.c +5 -8
- data/ext/oj/trace.h +1 -4
- data/ext/oj/util.c +1 -1
- data/ext/oj/util.h +1 -1
- data/ext/oj/val_stack.c +1 -29
- data/ext/oj/val_stack.h +1 -29
- data/ext/oj/wab.c +10 -13
- data/lib/oj/mimic.rb +45 -1
- data/lib/oj/version.rb +1 -1
- data/lib/oj.rb +0 -8
- data/pages/Modes.md +1 -1
- data/pages/Options.md +15 -11
- data/pages/Rails.md +60 -21
- data/test/activesupport5/abstract_unit.rb +45 -0
- data/test/activesupport5/decoding_test.rb +68 -60
- data/test/activesupport5/encoding_test.rb +111 -96
- data/test/activesupport5/encoding_test_cases.rb +33 -25
- data/test/activesupport5/test_helper.rb +43 -21
- data/test/activesupport5/time_zone_test_helpers.rb +18 -3
- data/test/activesupport6/abstract_unit.rb +44 -0
- data/test/activesupport6/decoding_test.rb +133 -0
- data/test/activesupport6/encoding_test.rb +507 -0
- data/test/activesupport6/encoding_test_cases.rb +98 -0
- data/test/activesupport6/test_common.rb +17 -0
- data/test/activesupport6/test_helper.rb +163 -0
- data/test/activesupport6/time_zone_test_helpers.rb +39 -0
- data/test/bar.rb +21 -11
- data/test/baz.rb +16 -0
- data/test/foo.rb +39 -8
- data/test/json_gem/json_common_interface_test.rb +8 -3
- data/test/prec.rb +23 -0
- data/test/sample_json.rb +1 -1
- data/test/test_compat.rb +14 -8
- data/test/test_custom.rb +36 -6
- data/test/test_integer_range.rb +1 -2
- data/test/test_object.rb +12 -3
- data/test/test_rails.rb +35 -0
- data/test/test_strict.rb +24 -1
- data/test/test_various.rb +42 -64
- data/test/tests.rb +1 -0
- metadata +29 -7
data/ext/oj/oj.c
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
|
2
|
-
* Copyright (c) 2012, Peter Ohler
|
3
|
-
* All rights reserved.
|
4
|
-
*/
|
1
|
+
// Copyright (c) 2012 Peter Ohler. All rights reserved.
|
5
2
|
|
6
3
|
#include <stdlib.h>
|
7
4
|
#include <errno.h>
|
@@ -91,6 +88,7 @@ VALUE oj_slash_string;
|
|
91
88
|
VALUE oj_allow_nan_sym;
|
92
89
|
VALUE oj_array_class_sym;
|
93
90
|
VALUE oj_create_additions_sym;
|
91
|
+
VALUE oj_decimal_class_sym;
|
94
92
|
VALUE oj_hash_class_sym;
|
95
93
|
VALUE oj_indent_sym;
|
96
94
|
VALUE oj_object_class_sym;
|
@@ -115,10 +113,12 @@ static VALUE custom_sym;
|
|
115
113
|
static VALUE empty_string_sym;
|
116
114
|
static VALUE escape_mode_sym;
|
117
115
|
static VALUE integer_range_sym;
|
116
|
+
static VALUE fast_sym;
|
118
117
|
static VALUE float_prec_sym;
|
119
118
|
static VALUE float_sym;
|
120
119
|
static VALUE huge_sym;
|
121
120
|
static VALUE ignore_sym;
|
121
|
+
static VALUE ignore_under_sym;
|
122
122
|
static VALUE json_sym;
|
123
123
|
static VALUE match_string_sym;
|
124
124
|
static VALUE mode_sym;
|
@@ -181,8 +181,10 @@ struct _options oj_default_options = {
|
|
181
181
|
Yes, // allow_nan
|
182
182
|
No, // trace
|
183
183
|
No, // safe
|
184
|
-
|
185
|
-
|
184
|
+
false, // sec_prec_set
|
185
|
+
No, // ignore_under
|
186
|
+
0, // int_range_min
|
187
|
+
0, // int_range_max
|
186
188
|
oj_json_class, // create_id
|
187
189
|
10, // create_id_len
|
188
190
|
9, // sec_prec
|
@@ -227,7 +229,7 @@ struct _options oj_default_options = {
|
|
227
229
|
* - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load and dump modes to use for JSON
|
228
230
|
* - *:time_format* [_:unix_|_:unix_zone_|_:xmlschema_|_:ruby_] time format when dumping
|
229
231
|
* - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or as a String
|
230
|
-
* - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_] load decimals as BigDecimal instead of as a Float. :auto pick the most precise for the number of digits.
|
232
|
+
* - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_:fast_] load decimals as BigDecimal instead of as a Float. :auto pick the most precise for the number of digits. :float should be the same as ruby. :fast may require rounding but is must faster.
|
231
233
|
* - *:create_id* [_String_|_nil_] create id for json compatible object encoding, default is 'json_class'
|
232
234
|
* - *:create_additions* [_Boolean_|_nil_] if true allow creation of instances using create_id on load.
|
233
235
|
* - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the seconds portion of time
|
@@ -251,6 +253,7 @@ struct _options oj_default_options = {
|
|
251
253
|
* - *:array_class* [_Class_|_nil_] Class to use instead of Array on load
|
252
254
|
* - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil values are omitted
|
253
255
|
* - *:ignore* [_nil_|Array] either nil or an Array of classes to ignore when dumping
|
256
|
+
* - *:ignore_under* [Boolean] if true then attributes that start with _ are ignored when dumping in object or custom mode.
|
254
257
|
* - *:integer_range* [_Range_] Dump integers outside range as strings.
|
255
258
|
* - *:trace* [_true,_|_false_] Trace all load and dump calls, default is false (trace is off)
|
256
259
|
* - *:safe* [_true,_|_false_] Safe mimic breaks JSON mimic to be safer, default is false (safe is off)
|
@@ -286,6 +289,7 @@ get_def_opts(VALUE self) {
|
|
286
289
|
rb_hash_aset(opts, oj_trace_sym, (Yes == oj_default_options.trace) ? Qtrue : ((No == oj_default_options.trace) ? Qfalse : Qnil));
|
287
290
|
rb_hash_aset(opts, oj_safe_sym, (Yes == oj_default_options.safe) ? Qtrue : ((No == oj_default_options.safe) ? Qfalse : Qnil));
|
288
291
|
rb_hash_aset(opts, float_prec_sym, INT2FIX(oj_default_options.float_prec));
|
292
|
+
rb_hash_aset(opts, ignore_under_sym, (Yes == oj_default_options.ignore_under) ? Qtrue : ((No == oj_default_options.ignore_under) ? Qfalse : Qnil));
|
289
293
|
switch (oj_default_options.mode) {
|
290
294
|
case StrictMode: rb_hash_aset(opts, mode_sym, strict_sym); break;
|
291
295
|
case CompatMode: rb_hash_aset(opts, mode_sym, compat_sym); break;
|
@@ -297,16 +301,16 @@ get_def_opts(VALUE self) {
|
|
297
301
|
default: rb_hash_aset(opts, mode_sym, object_sym); break;
|
298
302
|
}
|
299
303
|
|
300
|
-
if (oj_default_options.
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
else {
|
309
|
-
|
304
|
+
if (oj_default_options.int_range_max != 0 || oj_default_options.int_range_min != 0) {
|
305
|
+
VALUE range = rb_obj_alloc(rb_cRange);
|
306
|
+
VALUE min = LONG2FIX(oj_default_options.int_range_min);
|
307
|
+
VALUE max = LONG2FIX(oj_default_options.int_range_max);
|
308
|
+
|
309
|
+
rb_ivar_set(range, oj_begin_id, min);
|
310
|
+
rb_ivar_set(range, oj_end_id, max);
|
311
|
+
rb_hash_aset(opts, integer_range_sym, range);
|
312
|
+
} else {
|
313
|
+
rb_hash_aset(opts, integer_range_sym, Qnil);
|
310
314
|
}
|
311
315
|
switch (oj_default_options.escape_mode) {
|
312
316
|
case NLEsc: rb_hash_aset(opts, escape_mode_sym, newline_sym); break;
|
@@ -326,6 +330,7 @@ get_def_opts(VALUE self) {
|
|
326
330
|
switch (oj_default_options.bigdec_load) {
|
327
331
|
case BigDec: rb_hash_aset(opts, bigdecimal_load_sym, bigdecimal_sym);break;
|
328
332
|
case FloatDec: rb_hash_aset(opts, bigdecimal_load_sym, float_sym); break;
|
333
|
+
case FastDec: rb_hash_aset(opts, bigdecimal_load_sym, fast_sym); break;
|
329
334
|
case AutoDec:
|
330
335
|
default: rb_hash_aset(opts, bigdecimal_load_sym, auto_sym); break;
|
331
336
|
}
|
@@ -398,6 +403,7 @@ get_def_opts(VALUE self) {
|
|
398
403
|
* - *:array_class* [_Class_|_nil_] Class to use instead of Array on load.
|
399
404
|
* - *:omit_nil* [_true_|_false_] if true Hash and Object attributes with nil values are omitted.
|
400
405
|
* - *:ignore* [_nil_|Array] either nil or an Array of classes to ignore when dumping
|
406
|
+
* - *:ignore_under* [_Boolean_] if true then attributes that start with _ are ignored when dumping in object or custom mode.
|
401
407
|
* - *:integer_range* [_Range_] Dump integers outside range as strings.
|
402
408
|
* - *:trace* [_Boolean_] turn trace on or off.
|
403
409
|
* - *:safe* [_Boolean_] turn safe mimic on or off.
|
@@ -431,6 +437,7 @@ oj_parse_options(VALUE ropts, Options copts) {
|
|
431
437
|
{ oj_allow_nan_sym, &copts->allow_nan },
|
432
438
|
{ oj_trace_sym, &copts->trace },
|
433
439
|
{ oj_safe_sym, &copts->safe },
|
440
|
+
{ ignore_under_sym, &copts->ignore_under },
|
434
441
|
{ oj_create_additions_sym, &copts->create_ok },
|
435
442
|
{ Qnil, 0 }
|
436
443
|
};
|
@@ -506,8 +513,12 @@ oj_parse_options(VALUE ropts, Options copts) {
|
|
506
513
|
n = NUM2INT(v);
|
507
514
|
if (0 > n) {
|
508
515
|
n = 0;
|
516
|
+
copts->sec_prec_set = false;
|
509
517
|
} else if (9 < n) {
|
510
518
|
n = 9;
|
519
|
+
copts->sec_prec_set = true;
|
520
|
+
} else {
|
521
|
+
copts->sec_prec_set = true;
|
511
522
|
}
|
512
523
|
copts->sec_prec = n;
|
513
524
|
}
|
@@ -563,12 +574,27 @@ oj_parse_options(VALUE ropts, Options copts) {
|
|
563
574
|
copts->bigdec_load = BigDec;
|
564
575
|
} else if (float_sym == v) {
|
565
576
|
copts->bigdec_load = FloatDec;
|
577
|
+
} else if (fast_sym == v) {
|
578
|
+
copts->bigdec_load = FastDec;
|
566
579
|
} else if (auto_sym == v || Qfalse == v) {
|
567
580
|
copts->bigdec_load = AutoDec;
|
568
581
|
} else {
|
569
582
|
rb_raise(rb_eArgError, ":bigdecimal_load must be :bigdecimal, :float, or :auto.");
|
570
583
|
}
|
571
584
|
}
|
585
|
+
if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_decimal_class_sym)) {
|
586
|
+
v = rb_hash_lookup(ropts, oj_decimal_class_sym);
|
587
|
+
if (rb_cFloat == v) {
|
588
|
+
copts->bigdec_load = FloatDec;
|
589
|
+
} else if (oj_bigdecimal_class == v) {
|
590
|
+
copts->bigdec_load = BigDec;
|
591
|
+
} else if (Qnil == v) {
|
592
|
+
copts->bigdec_load = AutoDec;
|
593
|
+
} else {
|
594
|
+
rb_raise(rb_eArgError, ":decimal_class must be BigDecimal, Float, or nil.");
|
595
|
+
}
|
596
|
+
}
|
597
|
+
|
572
598
|
if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, create_id_sym)) {
|
573
599
|
v = rb_hash_lookup(ropts, create_id_sym);
|
574
600
|
if (Qnil == v) {
|
@@ -738,24 +764,26 @@ oj_parse_options(VALUE ropts, Options copts) {
|
|
738
764
|
}
|
739
765
|
}
|
740
766
|
if (Qnil != (v = rb_hash_lookup(ropts, integer_range_sym))) {
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
767
|
+
if (TYPE(v) == T_STRUCT && rb_obj_class(v) == rb_cRange) {
|
768
|
+
VALUE min = rb_funcall(v, oj_begin_id, 0);
|
769
|
+
VALUE max = rb_funcall(v, oj_end_id, 0);
|
770
|
+
|
771
|
+
if (TYPE(min) != T_FIXNUM || TYPE(max) != T_FIXNUM) {
|
772
|
+
rb_raise(rb_eArgError, ":integer_range range bounds is not Fixnum.");
|
773
|
+
}
|
774
|
+
|
775
|
+
copts->int_range_min = FIX2LONG(min);
|
776
|
+
copts->int_range_max = FIX2LONG(max);
|
777
|
+
} else if (Qfalse != v) {
|
778
|
+
rb_raise(rb_eArgError, ":integer_range must be a range of Fixnum.");
|
779
|
+
}
|
754
780
|
}
|
755
781
|
}
|
756
782
|
|
757
783
|
static int
|
758
|
-
match_string_cb(VALUE key, VALUE value,
|
784
|
+
match_string_cb(VALUE key, VALUE value, VALUE rx) {
|
785
|
+
RxClass rc = (RxClass)rx;
|
786
|
+
|
759
787
|
if (T_CLASS != rb_type(value)) {
|
760
788
|
rb_raise(rb_eArgError, "for :match_string, the hash values must be a Class.");
|
761
789
|
}
|
@@ -790,7 +818,7 @@ oj_parse_opt_match_string(RxClass rc, VALUE ropts) {
|
|
790
818
|
}
|
791
819
|
|
792
820
|
/* Document-method: load
|
793
|
-
* call-seq: load(json, options) { _|_obj, start, len_|_ }
|
821
|
+
* call-seq: load(json, options={}) { _|_obj, start, len_|_ }
|
794
822
|
*
|
795
823
|
* Parses a JSON document String into a Object, Hash, Array, String, Fixnum,
|
796
824
|
* Float, true, false, or nil according to the default mode or the mode
|
@@ -874,7 +902,7 @@ load(int argc, VALUE *argv, VALUE self) {
|
|
874
902
|
}
|
875
903
|
|
876
904
|
/* Document-method: load_file
|
877
|
-
* call-seq: load_file(path, options) { _|_obj, start, len_|_ }
|
905
|
+
* call-seq: load_file(path, options={}) { _|_obj, start, len_|_ }
|
878
906
|
*
|
879
907
|
* Parses a JSON document String into a Object, Hash, Array, String, Fixnum,
|
880
908
|
* Float, true, false, or nil according to the default mode or the mode
|
@@ -954,11 +982,13 @@ load_file(int argc, VALUE *argv, VALUE self) {
|
|
954
982
|
}
|
955
983
|
switch (mode) {
|
956
984
|
case StrictMode:
|
985
|
+
case NullMode:
|
957
986
|
oj_set_strict_callbacks(&pi);
|
958
987
|
return oj_pi_sparse(argc, argv, &pi, fd);
|
959
|
-
case NullMode:
|
960
|
-
case CompatMode:
|
961
988
|
case CustomMode:
|
989
|
+
oj_set_custom_callbacks(&pi);
|
990
|
+
return oj_pi_sparse(argc, argv, &pi, fd);
|
991
|
+
case CompatMode:
|
962
992
|
case RailsMode:
|
963
993
|
oj_set_compat_callbacks(&pi);
|
964
994
|
return oj_pi_sparse(argc, argv, &pi, fd);
|
@@ -1030,7 +1060,7 @@ safe_load(VALUE self, VALUE doc) {
|
|
1030
1060
|
*/
|
1031
1061
|
|
1032
1062
|
/* Document-method: dump
|
1033
|
-
* call-seq: dump(obj, options)
|
1063
|
+
* call-seq: dump(obj, options={})
|
1034
1064
|
*
|
1035
1065
|
* Dumps an Object (obj) to a string.
|
1036
1066
|
* - *obj* [_Object_] Object to serialize as an JSON document String
|
@@ -1052,6 +1082,9 @@ dump(int argc, VALUE *argv, VALUE self) {
|
|
1052
1082
|
if (2 == argc) {
|
1053
1083
|
oj_parse_options(argv[1], &copts);
|
1054
1084
|
}
|
1085
|
+
if (CompatMode == copts.mode && copts.escape_mode != ASCIIEsc) {
|
1086
|
+
copts.escape_mode = JSONEsc;
|
1087
|
+
}
|
1055
1088
|
out.buf = buf;
|
1056
1089
|
out.end = buf + sizeof(buf) - 10;
|
1057
1090
|
out.allocated = false;
|
@@ -1125,7 +1158,7 @@ to_json(int argc, VALUE *argv, VALUE self) {
|
|
1125
1158
|
}
|
1126
1159
|
|
1127
1160
|
/* Document-method: to_file
|
1128
|
-
* call-seq: to_file(file_path, obj, options)
|
1161
|
+
* call-seq: to_file(file_path, obj, options={})
|
1129
1162
|
*
|
1130
1163
|
* Dumps an Object to the specified file.
|
1131
1164
|
* - *file* [_String_] _path file path to write the JSON document to
|
@@ -1148,7 +1181,7 @@ to_file(int argc, VALUE *argv, VALUE self) {
|
|
1148
1181
|
}
|
1149
1182
|
|
1150
1183
|
/* Document-method: to_stream
|
1151
|
-
* call-seq: to_stream(io, obj, options)
|
1184
|
+
* call-seq: to_stream(io, obj, options={})
|
1152
1185
|
*
|
1153
1186
|
* Dumps an Object to the specified IO stream.
|
1154
1187
|
* - *io* [_IO_] IO stream to write the JSON document to
|
@@ -1636,10 +1669,12 @@ Init_oj() {
|
|
1636
1669
|
empty_string_sym = ID2SYM(rb_intern("empty_string")); rb_gc_register_address(&empty_string_sym);
|
1637
1670
|
escape_mode_sym = ID2SYM(rb_intern("escape_mode")); rb_gc_register_address(&escape_mode_sym);
|
1638
1671
|
integer_range_sym = ID2SYM(rb_intern("integer_range")); rb_gc_register_address(&integer_range_sym);
|
1672
|
+
fast_sym = ID2SYM(rb_intern("fast")); rb_gc_register_address(&fast_sym);
|
1639
1673
|
float_prec_sym = ID2SYM(rb_intern("float_precision")); rb_gc_register_address(&float_prec_sym);
|
1640
1674
|
float_sym = ID2SYM(rb_intern("float")); rb_gc_register_address(&float_sym);
|
1641
1675
|
huge_sym = ID2SYM(rb_intern("huge")); rb_gc_register_address(&huge_sym);
|
1642
1676
|
ignore_sym = ID2SYM(rb_intern("ignore")); rb_gc_register_address(&ignore_sym);
|
1677
|
+
ignore_under_sym = ID2SYM(rb_intern("ignore_under")); rb_gc_register_address(&ignore_under_sym);
|
1643
1678
|
json_sym = ID2SYM(rb_intern("json")); rb_gc_register_address(&json_sym);
|
1644
1679
|
match_string_sym = ID2SYM(rb_intern("match_string")); rb_gc_register_address(&match_string_sym);
|
1645
1680
|
mode_sym = ID2SYM(rb_intern("mode")); rb_gc_register_address(&mode_sym);
|
@@ -1653,6 +1688,7 @@ Init_oj() {
|
|
1653
1688
|
oj_array_nl_sym = ID2SYM(rb_intern("array_nl")); rb_gc_register_address(&oj_array_nl_sym);
|
1654
1689
|
oj_ascii_only_sym = ID2SYM(rb_intern("ascii_only")); rb_gc_register_address(&oj_ascii_only_sym);
|
1655
1690
|
oj_create_additions_sym = ID2SYM(rb_intern("create_additions"));rb_gc_register_address(&oj_create_additions_sym);
|
1691
|
+
oj_decimal_class_sym = ID2SYM(rb_intern("decimal_class")); rb_gc_register_address(&oj_decimal_class_sym);
|
1656
1692
|
oj_hash_class_sym = ID2SYM(rb_intern("hash_class")); rb_gc_register_address(&oj_hash_class_sym);
|
1657
1693
|
oj_indent_sym = ID2SYM(rb_intern("indent")); rb_gc_register_address(&oj_indent_sym);
|
1658
1694
|
oj_max_nesting_sym = ID2SYM(rb_intern("max_nesting")); rb_gc_register_address(&oj_max_nesting_sym);
|
data/ext/oj/oj.h
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
|
2
|
-
* Copyright (c) 2011, Peter Ohler
|
3
|
-
* All rights reserved.
|
4
|
-
*/
|
1
|
+
// Copyright (c) 2011 Peter Ohler. All rights reserved.
|
5
2
|
|
6
3
|
#ifndef OJ_H
|
7
4
|
#define OJ_H
|
@@ -77,7 +74,9 @@ typedef enum {
|
|
77
74
|
typedef enum {
|
78
75
|
BigDec = 'b',
|
79
76
|
FloatDec = 'f',
|
80
|
-
AutoDec = 'a'
|
77
|
+
AutoDec = 'a',
|
78
|
+
FastDec = 'F',
|
79
|
+
RubyDec = 'r',
|
81
80
|
} BigLoad;
|
82
81
|
|
83
82
|
typedef enum {
|
@@ -149,8 +148,10 @@ typedef struct _options {
|
|
149
148
|
char allow_nan; // YEsyNo for parsing only
|
150
149
|
char trace; // YesNo
|
151
150
|
char safe; // YesNo
|
152
|
-
|
153
|
-
|
151
|
+
char sec_prec_set; // boolean (0 or 1)
|
152
|
+
char ignore_under; // YesNo - ignore attrs starting with _ if true in object and custom modes
|
153
|
+
int64_t int_range_min; // dump numbers below as string
|
154
|
+
int64_t int_range_max; // dump numbers above as string
|
154
155
|
const char *create_id; // 0 or string
|
155
156
|
size_t create_id_len; // length of create_id
|
156
157
|
int sec_prec; // second precision when dumping time
|
@@ -309,6 +310,7 @@ extern VALUE oj_array_class_sym;
|
|
309
310
|
extern VALUE oj_array_nl_sym;
|
310
311
|
extern VALUE oj_ascii_only_sym;
|
311
312
|
extern VALUE oj_create_additions_sym;
|
313
|
+
extern VALUE oj_decimal_class_sym;
|
312
314
|
extern VALUE oj_hash_class_sym;
|
313
315
|
extern VALUE oj_indent_sym;
|
314
316
|
extern VALUE oj_max_nesting_sym;
|
data/ext/oj/parse.c
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
|
2
|
-
* Copyright (c) 2013, Peter Ohler
|
3
|
-
* All rights reserved.
|
4
|
-
*/
|
1
|
+
// Copyright (c) 2013 Peter Ohler. All rights reserved.
|
5
2
|
|
6
3
|
#include <stdlib.h>
|
7
4
|
#include <stdio.h>
|
@@ -215,18 +212,6 @@ read_escaped_str(ParseInfo pi, const char *start) {
|
|
215
212
|
case '"': buf_append(&buf, '"'); break;
|
216
213
|
case '/': buf_append(&buf, '/'); break;
|
217
214
|
case '\\': buf_append(&buf, '\\'); break;
|
218
|
-
case '\'':
|
219
|
-
// The json gem claims this is not an error despite the
|
220
|
-
// ECMA-404 indicating it is not valid.
|
221
|
-
if (CompatMode == pi->options.mode) {
|
222
|
-
buf_append(&buf, '\'');
|
223
|
-
} else {
|
224
|
-
pi->cur = s;
|
225
|
-
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
|
226
|
-
buf_cleanup(&buf);
|
227
|
-
return;
|
228
|
-
}
|
229
|
-
break;
|
230
215
|
case 'u':
|
231
216
|
s++;
|
232
217
|
if (0 == (code = read_hex(pi, s)) && err_has(&pi->err)) {
|
@@ -266,6 +251,12 @@ read_escaped_str(ParseInfo pi, const char *start) {
|
|
266
251
|
}
|
267
252
|
break;
|
268
253
|
default:
|
254
|
+
// The json gem claims this is not an error despite the
|
255
|
+
// ECMA-404 indicating it is not valid.
|
256
|
+
if (CompatMode == pi->options.mode) {
|
257
|
+
buf_append(&buf, *s);
|
258
|
+
break;
|
259
|
+
}
|
269
260
|
pi->cur = s;
|
270
261
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid escaped character");
|
271
262
|
buf_cleanup(&buf);
|
@@ -393,13 +384,18 @@ read_num(ParseInfo pi) {
|
|
393
384
|
ni.infinity = 0;
|
394
385
|
ni.nan = 0;
|
395
386
|
ni.neg = 0;
|
396
|
-
ni.
|
397
|
-
ni.no_big = (FloatDec == pi->options.bigdec_load);
|
387
|
+
ni.has_exp = 0;
|
388
|
+
ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load || RubyDec == pi->options.bigdec_load);
|
389
|
+
ni.bigdec_load = pi->options.bigdec_load;
|
398
390
|
|
399
391
|
if ('-' == *pi->cur) {
|
400
392
|
pi->cur++;
|
401
393
|
ni.neg = 1;
|
402
394
|
} else if ('+' == *pi->cur) {
|
395
|
+
if (StrictMode == pi->options.mode) {
|
396
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
397
|
+
return;
|
398
|
+
}
|
403
399
|
pi->cur++;
|
404
400
|
}
|
405
401
|
if ('I' == *pi->cur) {
|
@@ -446,8 +442,14 @@ read_num(ParseInfo pi) {
|
|
446
442
|
if ('.' == *pi->cur) {
|
447
443
|
pi->cur++;
|
448
444
|
// A trailing . is not a valid decimal but if encountered allow it
|
449
|
-
// except when mimicing the JSON gem.
|
450
|
-
if (CompatMode == pi->options.mode) {
|
445
|
+
// except when mimicing the JSON gem or in strict mode.
|
446
|
+
if (StrictMode == pi->options.mode || CompatMode == pi->options.mode) {
|
447
|
+
int pos = (int)(pi->cur - ni.str);
|
448
|
+
|
449
|
+
if (1 == pos || (2 == pos && ni.neg)) {
|
450
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
|
451
|
+
return;
|
452
|
+
}
|
451
453
|
if (*pi->cur < '0' || '9' < *pi->cur) {
|
452
454
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
|
453
455
|
return;
|
@@ -459,18 +461,26 @@ read_num(ParseInfo pi) {
|
|
459
461
|
if (0 < ni.num || 0 < ni.i) {
|
460
462
|
dec_cnt++;
|
461
463
|
}
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
464
|
+
if (INT64_MAX <= ni.div) {
|
465
|
+
if (!ni.no_big) {
|
466
|
+
ni.big = true;
|
467
|
+
}
|
468
|
+
} else {
|
469
|
+
ni.num = ni.num * 10 + d;
|
470
|
+
ni.div *= 10;
|
471
|
+
ni.di++;
|
472
|
+
if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
|
473
|
+
if (!ni.no_big) {
|
474
|
+
ni.big = true;
|
475
|
+
}
|
476
|
+
}
|
467
477
|
}
|
468
478
|
}
|
469
479
|
}
|
470
480
|
if ('e' == *pi->cur || 'E' == *pi->cur) {
|
471
481
|
int eneg = 0;
|
472
482
|
|
473
|
-
ni.
|
483
|
+
ni.has_exp = 1;
|
474
484
|
pi->cur++;
|
475
485
|
if ('-' == *pi->cur) {
|
476
486
|
pi->cur++;
|
@@ -481,7 +491,7 @@ read_num(ParseInfo pi) {
|
|
481
491
|
for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
|
482
492
|
ni.exp = ni.exp * 10 + (*pi->cur - '0');
|
483
493
|
if (EXP_MAX <= ni.exp) {
|
484
|
-
ni.big =
|
494
|
+
ni.big = true;
|
485
495
|
}
|
486
496
|
}
|
487
497
|
if (eneg) {
|
@@ -731,7 +741,7 @@ oj_parse2(ParseInfo pi) {
|
|
731
741
|
}
|
732
742
|
|
733
743
|
static VALUE
|
734
|
-
rescue_big_decimal(VALUE str) {
|
744
|
+
rescue_big_decimal(VALUE str, VALUE ignore) {
|
735
745
|
rb_raise(oj_parse_error_class, "Invalid value for BigDecimal()");
|
736
746
|
return Qnil;
|
737
747
|
}
|
@@ -741,6 +751,59 @@ parse_big_decimal(VALUE str) {
|
|
741
751
|
return rb_funcall(rb_cObject, oj_bigdecimal_id, 1, str);
|
742
752
|
}
|
743
753
|
|
754
|
+
static long double exp_plus[] = {
|
755
|
+
1.0,
|
756
|
+
1.0e1,
|
757
|
+
1.0e2,
|
758
|
+
1.0e3,
|
759
|
+
1.0e4,
|
760
|
+
1.0e5,
|
761
|
+
1.0e6,
|
762
|
+
1.0e7,
|
763
|
+
1.0e8,
|
764
|
+
1.0e9,
|
765
|
+
1.0e10,
|
766
|
+
1.0e11,
|
767
|
+
1.0e12,
|
768
|
+
1.0e13,
|
769
|
+
1.0e14,
|
770
|
+
1.0e15,
|
771
|
+
1.0e16,
|
772
|
+
1.0e17,
|
773
|
+
1.0e18,
|
774
|
+
1.0e19,
|
775
|
+
1.0e20,
|
776
|
+
1.0e21,
|
777
|
+
1.0e22,
|
778
|
+
1.0e23,
|
779
|
+
1.0e24,
|
780
|
+
1.0e25,
|
781
|
+
1.0e26,
|
782
|
+
1.0e27,
|
783
|
+
1.0e28,
|
784
|
+
1.0e29,
|
785
|
+
1.0e30,
|
786
|
+
1.0e31,
|
787
|
+
1.0e32,
|
788
|
+
1.0e33,
|
789
|
+
1.0e34,
|
790
|
+
1.0e35,
|
791
|
+
1.0e36,
|
792
|
+
1.0e37,
|
793
|
+
1.0e38,
|
794
|
+
1.0e39,
|
795
|
+
1.0e40,
|
796
|
+
1.0e41,
|
797
|
+
1.0e42,
|
798
|
+
1.0e43,
|
799
|
+
1.0e44,
|
800
|
+
1.0e45,
|
801
|
+
1.0e46,
|
802
|
+
1.0e47,
|
803
|
+
1.0e48,
|
804
|
+
1.0e49,
|
805
|
+
};
|
806
|
+
|
744
807
|
VALUE
|
745
808
|
oj_num_as_value(NumInfo ni) {
|
746
809
|
volatile VALUE rnum = Qnil;
|
@@ -753,7 +816,7 @@ oj_num_as_value(NumInfo ni) {
|
|
753
816
|
}
|
754
817
|
} else if (ni->nan) {
|
755
818
|
rnum = rb_float_new(0.0/0.0);
|
756
|
-
} else if (1 == ni->div && 0 == ni->exp) { // fixnum
|
819
|
+
} else if (1 == ni->div && 0 == ni->exp && !ni->has_exp) { // fixnum
|
757
820
|
if (ni->big) {
|
758
821
|
if (256 > ni->len) {
|
759
822
|
char buf[256];
|
@@ -784,33 +847,39 @@ oj_num_as_value(NumInfo ni) {
|
|
784
847
|
if (ni->no_big) {
|
785
848
|
rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
|
786
849
|
}
|
787
|
-
} else {
|
788
|
-
|
789
|
-
long double d = (long double)ni->i * (long double)ni->div + (long double)ni->num;
|
850
|
+
} else if (FastDec == ni->bigdec_load) {
|
851
|
+
long double ld = (long double)ni->i * (long double)ni->div + (long double)ni->num;
|
790
852
|
int x = (int)((int64_t)ni->exp - ni->di);
|
791
853
|
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
rnum = rb_rescue2(parse_big_decimal, bd, rescue_big_decimal, bd, rb_eException, 0);
|
799
|
-
if (ni->no_big) {
|
800
|
-
rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
|
801
|
-
}
|
802
|
-
} else {
|
803
|
-
d = roundl(d);
|
804
|
-
if (0 < x) {
|
805
|
-
d *= powl(10.0L, x);
|
806
|
-
} else if (0 > x) {
|
807
|
-
d /= powl(10.0L, -x);
|
854
|
+
if (0 < x) {
|
855
|
+
if (x < (int)(sizeof(exp_plus) / sizeof(*exp_plus))) {
|
856
|
+
ld *= exp_plus[x];
|
857
|
+
} else {
|
858
|
+
ld *= powl(10.0, x);
|
808
859
|
}
|
809
|
-
|
810
|
-
|
860
|
+
} else if (x < 0) {
|
861
|
+
if (-x < (int)(sizeof(exp_plus) / sizeof(*exp_plus))) {
|
862
|
+
ld /= exp_plus[-x];
|
863
|
+
} else {
|
864
|
+
ld /= powl(10.0, -x);
|
811
865
|
}
|
812
|
-
rnum = rb_float_new((double)d);
|
813
866
|
}
|
867
|
+
if (ni->neg) {
|
868
|
+
ld = -ld;
|
869
|
+
}
|
870
|
+
rnum = rb_float_new((double)ld);
|
871
|
+
} else if (RubyDec == ni->bigdec_load) {
|
872
|
+
volatile VALUE sv = rb_str_new(ni->str, ni->len);
|
873
|
+
|
874
|
+
rnum = rb_funcall(sv, rb_intern("to_f"), 0);
|
875
|
+
} else {
|
876
|
+
char *end;
|
877
|
+
double d = strtod(ni->str, &end);
|
878
|
+
|
879
|
+
if ((long)ni->len != (long)(end - ni->str)) {
|
880
|
+
rb_raise(oj_parse_error_class, "Invalid float");
|
881
|
+
}
|
882
|
+
rnum = rb_float_new(d);
|
814
883
|
}
|
815
884
|
}
|
816
885
|
return rnum;
|
@@ -832,6 +901,12 @@ oj_set_error_at(ParseInfo pi, VALUE err_clas, const char* file, int line, const
|
|
832
901
|
if (p + 3 < end) {
|
833
902
|
*p++ = ' ';
|
834
903
|
*p++ = '(';
|
904
|
+
*p++ = 'a';
|
905
|
+
*p++ = 'f';
|
906
|
+
*p++ = 't';
|
907
|
+
*p++ = 'e';
|
908
|
+
*p++ = 'r';
|
909
|
+
*p++ = ' ';
|
835
910
|
start = p;
|
836
911
|
for (vp = pi->stack.head; vp < pi->stack.tail; vp++) {
|
837
912
|
if (end <= p + 1 + vp->klen) {
|
@@ -1050,7 +1125,7 @@ CLEANUP:
|
|
1050
1125
|
}
|
1051
1126
|
args[0] = msg;
|
1052
1127
|
if (pi->err.clas == oj_parse_error_class) {
|
1053
|
-
// The error was an Oj::ParseError so change to a JSON::
|
1128
|
+
// The error was an Oj::ParseError so change to a JSON::ParserError.
|
1054
1129
|
pi->err.clas = oj_json_parser_error_class;
|
1055
1130
|
}
|
1056
1131
|
rb_exc_raise(rb_class_new_instance(1, args, pi->err.clas));
|
data/ext/oj/parse.h
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
|
2
|
-
* Copyright (c) 2011, Peter Ohler
|
3
|
-
* All rights reserved.
|
4
|
-
*/
|
1
|
+
// Copyright (c) 2011 Peter Ohler. All rights reserved.
|
5
2
|
|
6
3
|
#ifndef OJ_PARSE_H
|
7
4
|
#define OJ_PARSE_H
|
@@ -31,8 +28,9 @@ typedef struct _numInfo {
|
|
31
28
|
int infinity;
|
32
29
|
int nan;
|
33
30
|
int neg;
|
34
|
-
int
|
31
|
+
int has_exp;
|
35
32
|
int no_big;
|
33
|
+
int bigdec_load;
|
36
34
|
} *NumInfo;
|
37
35
|
|
38
36
|
typedef struct _parseInfo {
|
@@ -80,6 +78,7 @@ extern VALUE oj_num_as_value(NumInfo ni);
|
|
80
78
|
extern void oj_set_strict_callbacks(ParseInfo pi);
|
81
79
|
extern void oj_set_object_callbacks(ParseInfo pi);
|
82
80
|
extern void oj_set_compat_callbacks(ParseInfo pi);
|
81
|
+
extern void oj_set_custom_callbacks(ParseInfo pi);
|
83
82
|
extern void oj_set_wab_callbacks(ParseInfo pi);
|
84
83
|
|
85
84
|
extern void oj_sparse2(ParseInfo pi);
|