oj 3.10.1 → 3.11.1

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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/ext/oj/buf.h +2 -30
  4. data/ext/oj/cache8.h +1 -29
  5. data/ext/oj/circarray.c +4 -8
  6. data/ext/oj/circarray.h +1 -4
  7. data/ext/oj/code.c +3 -6
  8. data/ext/oj/code.h +1 -4
  9. data/ext/oj/compat.c +1 -4
  10. data/ext/oj/custom.c +2 -5
  11. data/ext/oj/dump.c +24 -14
  12. data/ext/oj/dump.h +1 -4
  13. data/ext/oj/dump_compat.c +1 -4
  14. data/ext/oj/dump_leaf.c +2 -5
  15. data/ext/oj/dump_object.c +2 -5
  16. data/ext/oj/dump_strict.c +1 -4
  17. data/ext/oj/encode.h +1 -29
  18. data/ext/oj/err.c +1 -4
  19. data/ext/oj/err.h +1 -29
  20. data/ext/oj/fast.c +14 -42
  21. data/ext/oj/hash.c +4 -32
  22. data/ext/oj/hash.h +1 -29
  23. data/ext/oj/hash_test.c +1 -29
  24. data/ext/oj/mimic_json.c +9 -8
  25. data/ext/oj/object.c +4 -6
  26. data/ext/oj/odd.c +1 -4
  27. data/ext/oj/odd.h +1 -4
  28. data/ext/oj/oj.c +43 -10
  29. data/ext/oj/oj.h +6 -5
  30. data/ext/oj/parse.c +124 -49
  31. data/ext/oj/parse.h +3 -5
  32. data/ext/oj/rails.c +1 -5
  33. data/ext/oj/rails.h +1 -4
  34. data/ext/oj/reader.c +5 -8
  35. data/ext/oj/reader.h +2 -5
  36. data/ext/oj/resolve.c +1 -4
  37. data/ext/oj/resolve.h +1 -4
  38. data/ext/oj/rxclass.c +3 -6
  39. data/ext/oj/rxclass.h +1 -4
  40. data/ext/oj/saj.c +6 -9
  41. data/ext/oj/scp.c +1 -4
  42. data/ext/oj/sparse.c +56 -27
  43. data/ext/oj/stream_writer.c +4 -9
  44. data/ext/oj/strict.c +3 -6
  45. data/ext/oj/string_writer.c +1 -4
  46. data/ext/oj/trace.c +5 -8
  47. data/ext/oj/trace.h +1 -4
  48. data/ext/oj/util.c +1 -1
  49. data/ext/oj/util.h +1 -1
  50. data/ext/oj/val_stack.c +1 -29
  51. data/ext/oj/val_stack.h +1 -29
  52. data/ext/oj/wab.c +1 -4
  53. data/lib/oj/mimic.rb +45 -1
  54. data/lib/oj/version.rb +1 -1
  55. data/lib/oj.rb +0 -8
  56. data/pages/Modes.md +1 -0
  57. data/pages/Options.md +23 -11
  58. data/pages/Rails.md +39 -0
  59. data/test/bar.rb +21 -8
  60. data/test/helper.rb +10 -0
  61. data/test/json_gem/json_common_interface_test.rb +8 -3
  62. data/test/json_gem/json_generator_test.rb +15 -3
  63. data/test/json_gem/test_helper.rb +8 -0
  64. data/test/prec.rb +23 -0
  65. data/test/sample_json.rb +1 -1
  66. data/test/test_compat.rb +16 -3
  67. data/test/test_custom.rb +11 -0
  68. data/test/test_object.rb +8 -0
  69. data/test/test_rails.rb +9 -0
  70. data/test/test_various.rb +2 -2
  71. metadata +87 -85
data/ext/oj/hash.h CHANGED
@@ -1,32 +1,4 @@
1
- /* hash.h
2
- * Copyright (c) 2011, Peter Ohler
3
- * All rights reserved.
4
- *
5
- * Redistribution and use in source and binary forms, with or without
6
- * modification, are permitted provided that the following conditions are met:
7
- *
8
- * - Redistributions of source code must retain the above copyright notice, this
9
- * list of conditions and the following disclaimer.
10
- *
11
- * - Redistributions in binary form must reproduce the above copyright notice,
12
- * this list of conditions and the following disclaimer in the documentation
13
- * and/or other materials provided with the distribution.
14
- *
15
- * - Neither the name of Peter Ohler nor the names of its contributors may be
16
- * used to endorse or promote products derived from this software without
17
- * specific prior written permission.
18
- *
19
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
- */
1
+ // Copyright (c) 2011 Peter Ohler. All rights reserved.
30
2
 
31
3
  #ifndef OJ_HASH_H
32
4
  #define OJ_HASH_H
data/ext/oj/hash_test.c CHANGED
@@ -1,32 +1,4 @@
1
- /* hash_test.c
2
- * Copyright (c) 2011, Peter Ohler
3
- * All rights reserved.
4
- *
5
- * Redistribution and use in source and binary forms, with or without
6
- * modification, are permitted provided that the following conditions are met:
7
- *
8
- * - Redistributions of source code must retain the above copyright notice, this
9
- * list of conditions and the following disclaimer.
10
- *
11
- * - Redistributions in binary form must reproduce the above copyright notice,
12
- * this list of conditions and the following disclaimer in the documentation
13
- * and/or other materials provided with the distribution.
14
- *
15
- * - Neither the name of Peter Ohler nor the names of its contributors may be
16
- * used to endorse or promote products derived from this software without
17
- * specific prior written permission.
18
- *
19
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
- */
1
+ // Copyright (c) 2011 Peter Ohler. All rights reserved.
30
2
 
31
3
  // if windows, comment out the whole file. It's only a performance test.
32
4
  #ifndef _WIN32
data/ext/oj/mimic_json.c CHANGED
@@ -1,7 +1,4 @@
1
- /* mimic_json.c
2
- * Copyright (c) 2012, 2017, Peter Ohler
3
- * All rights reserved.
4
- */
1
+ // Copyright (c) 2012, 2017 Peter Ohler. All rights reserved.
5
2
 
6
3
  #include "oj.h"
7
4
  #include "encode.h"
@@ -367,7 +364,6 @@ mimic_generate_core(int argc, VALUE *argv, Options copts) {
367
364
  struct _out out;
368
365
  VALUE rstr;
369
366
 
370
- // TBD
371
367
  memset(buf, 0, sizeof(buf));
372
368
 
373
369
  out.buf = buf;
@@ -513,7 +509,7 @@ mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
513
509
  pi.options.create_ok = No;
514
510
  pi.options.allow_nan = (bang ? Yes : No);
515
511
  pi.options.nilnil = No;
516
- pi.options.bigdec_load = FloatDec;
512
+ pi.options.bigdec_load = RubyDec;
517
513
  pi.options.mode = CompatMode;
518
514
  pi.max_depth = 100;
519
515
 
@@ -563,6 +559,9 @@ mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
563
559
  pi.options.array_class = v;
564
560
  }
565
561
  }
562
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_decimal_class_sym)) {
563
+ pi.options.compat_bigdec = (oj_bigdecimal_class == rb_hash_lookup(ropts, oj_decimal_class_sym));
564
+ }
566
565
  v = rb_hash_lookup(ropts, oj_max_nesting_sym);
567
566
  if (Qtrue == v) {
568
567
  pi.max_depth = 100;
@@ -686,7 +685,8 @@ static struct _options mimic_object_to_json_options = {
686
685
  No, // class_cache
687
686
  RubyTime, // time_format
688
687
  No, // bigdec_as_num
689
- FloatDec, // bigdec_load
688
+ RubyDec, // bigdec_load
689
+ false, // compat_bigdec
690
690
  No, // to_hash
691
691
  No, // to_json
692
692
  No, // as_json
@@ -707,7 +707,7 @@ static struct _options mimic_object_to_json_options = {
707
707
  oj_json_class,// create_id
708
708
  10, // create_id_len
709
709
  3, // sec_prec
710
- 16, // float_prec
710
+ 0, // float_prec
711
711
  "%0.16g", // float_fmt
712
712
  Qnil, // hash_class
713
713
  Qnil, // array_class
@@ -841,6 +841,7 @@ oj_mimic_json_methods(VALUE json) {
841
841
  }
842
842
  // Pull in the JSON::State mimic file.
843
843
  state_class = rb_const_get_at(generator, rb_intern("State"));
844
+ rb_gc_register_mark_object(state_class);
844
845
 
845
846
  symbolize_names_sym = ID2SYM(rb_intern("symbolize_names")); rb_gc_register_address(&symbolize_names_sym);
846
847
  }
data/ext/oj/object.c CHANGED
@@ -1,7 +1,4 @@
1
- /* object.c
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 <stdint.h>
7
4
  #include <stdio.h>
@@ -295,7 +292,7 @@ hat_num(ParseInfo pi, Val parent, Val kval, NumInfo ni) {
295
292
  // offset and then a conversion to UTC keeps makes the time
296
293
  // match the expected value.
297
294
  parent->val = rb_funcall2(parent->val, oj_utc_id, 0, 0);
298
- } else if (ni->hasExp) {
295
+ } else if (ni->has_exp) {
299
296
  int64_t t = (int64_t)(ni->i + ni->exp);
300
297
  struct _timeInfo ti;
301
298
  VALUE args[8];
@@ -669,7 +666,8 @@ static void
669
666
  array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
670
667
  volatile VALUE rval = Qnil;
671
668
 
672
- if (3 <= len && 0 != pi->circ_array) {
669
+ // orig lets us know whether the string was ^r1 or \u005er1
670
+ if (3 <= len && 0 != pi->circ_array && '^' == orig[0] && 0 == rb_array_len(stack_peek(&pi->stack)->val)) {
673
671
  if ('i' == str[1]) {
674
672
  long i = read_long(str + 2, len - 2);
675
673
 
data/ext/oj/odd.c CHANGED
@@ -1,7 +1,4 @@
1
- /* odd.c
2
- * Copyright (c) 2011, Peter Ohler
3
- * All rights reserved.
4
- */
1
+ // Copyright (c) 2011 Peter Ohler. All rights reserved.
5
2
 
6
3
  #include <string.h>
7
4
 
data/ext/oj/odd.h CHANGED
@@ -1,7 +1,4 @@
1
- /* odd.h
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_ODD_H
7
4
  #define OJ_ODD_H
data/ext/oj/oj.c CHANGED
@@ -1,7 +1,4 @@
1
- /* oj.c
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;
@@ -109,12 +107,14 @@ static VALUE bigdecimal_load_sym;
109
107
  static VALUE bigdecimal_sym;
110
108
  static VALUE circular_sym;
111
109
  static VALUE class_cache_sym;
110
+ static VALUE compat_bigdecimal_sym;
112
111
  static VALUE compat_sym;
113
112
  static VALUE create_id_sym;
114
113
  static VALUE custom_sym;
115
114
  static VALUE empty_string_sym;
116
115
  static VALUE escape_mode_sym;
117
116
  static VALUE integer_range_sym;
117
+ static VALUE fast_sym;
118
118
  static VALUE float_prec_sym;
119
119
  static VALUE float_sym;
120
120
  static VALUE huge_sym;
@@ -169,6 +169,7 @@ struct _options oj_default_options = {
169
169
  UnixTime, // time_format
170
170
  NotSet, // bigdec_as_num
171
171
  AutoDec, // bigdec_load
172
+ false, // compat_bigdec
172
173
  No, // to_hash
173
174
  No, // to_json
174
175
  No, // as_json
@@ -230,7 +231,8 @@ struct _options oj_default_options = {
230
231
  * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load and dump modes to use for JSON
231
232
  * - *:time_format* [_:unix_|_:unix_zone_|_:xmlschema_|_:ruby_] time format when dumping
232
233
  * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or as a String
233
- * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_] load decimals as BigDecimal instead of as a Float. :auto pick the most precise for the number of digits.
234
+ * - *: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.
235
+ * - *:compat_bigdecimal* [_true_|_false_] load decimals as BigDecimal instead of as a Float when in compat or rails mode.
234
236
  * - *:create_id* [_String_|_nil_] create id for json compatible object encoding, default is 'json_class'
235
237
  * - *:create_additions* [_Boolean_|_nil_] if true allow creation of instances using create_id on load.
236
238
  * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the seconds portion of time
@@ -331,9 +333,11 @@ get_def_opts(VALUE self) {
331
333
  switch (oj_default_options.bigdec_load) {
332
334
  case BigDec: rb_hash_aset(opts, bigdecimal_load_sym, bigdecimal_sym);break;
333
335
  case FloatDec: rb_hash_aset(opts, bigdecimal_load_sym, float_sym); break;
336
+ case FastDec: rb_hash_aset(opts, bigdecimal_load_sym, fast_sym); break;
334
337
  case AutoDec:
335
338
  default: rb_hash_aset(opts, bigdecimal_load_sym, auto_sym); break;
336
339
  }
340
+ rb_hash_aset(opts, compat_bigdecimal_sym, oj_default_options.compat_bigdec ? Qtrue : Qfalse);
337
341
  rb_hash_aset(opts, create_id_sym, (NULL == oj_default_options.create_id) ? Qnil : rb_str_new2(oj_default_options.create_id));
338
342
  rb_hash_aset(opts, oj_space_sym, (0 == oj_default_options.dump_opts.after_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.after_sep));
339
343
  rb_hash_aset(opts, oj_space_before_sym, (0 == oj_default_options.dump_opts.before_size) ? Qnil : rb_str_new2(oj_default_options.dump_opts.before_sep));
@@ -379,6 +383,7 @@ get_def_opts(VALUE self) {
379
383
  * - *:escape* [_:newline_|_:json_|_:xss_safe_|_:ascii_|_unicode_xss_|_nil_] mode encodes all high-bit characters as escaped sequences if :ascii, :json is standand UTF-8 JSON encoding, :newline is the same as :json but newlines are not escaped, :unicode_xss allows unicode but escapes &, <, and >, and any \u20xx characters along with some others, and :xss_safe escapes &, <, and >, and some others.
380
384
  * - *:bigdecimal_as_decimal* [_Boolean_|_nil_] dump BigDecimal as a decimal number or as a String.
381
385
  * - *:bigdecimal_load* [_:bigdecimal_|_:float_|_:auto_|_nil_] load decimals as BigDecimal instead of as a Float. :auto pick the most precise for the number of digits.
386
+ * - *:compat_bigdecimal* [_true_|_false_] load decimals as BigDecimal instead of as a Float in compat mode.
382
387
  * - *:mode* [_:object_|_:strict_|_:compat_|_:null_|_:custom_|_:rails_|_:wab_] load and dump mode to use for JSON :strict raises an exception when a non-supported Object is encountered. :compat attempts to extract variable values from an Object using to_json() or to_hash() then it walks the Object's variables if neither is found. The :object mode ignores to_hash() and to_json() methods and encodes variables using code internal to the Oj gem. The :null mode ignores non-supported Objects and replaces them with a null. The :custom mode honors all dump options. The :rails more mimics rails and Active behavior.
383
388
  * - *:time_format* [_:unix_|_:xmlschema_|_:ruby_] time format when dumping in :compat mode :unix decimal number denoting the number of seconds since 1/1/1970, :unix_zone decimal number denoting the number of seconds since 1/1/1970 plus the utc_offset in the exponent, :xmlschema date-time format taken from XML Schema as a String, :ruby Time.to_s formatted String.
384
389
  * - *:create_id* [_String_|_nil_] create id for json compatible object encoding
@@ -574,12 +579,29 @@ oj_parse_options(VALUE ropts, Options copts) {
574
579
  copts->bigdec_load = BigDec;
575
580
  } else if (float_sym == v) {
576
581
  copts->bigdec_load = FloatDec;
582
+ } else if (fast_sym == v) {
583
+ copts->bigdec_load = FastDec;
577
584
  } else if (auto_sym == v || Qfalse == v) {
578
585
  copts->bigdec_load = AutoDec;
579
586
  } else {
580
587
  rb_raise(rb_eArgError, ":bigdecimal_load must be :bigdecimal, :float, or :auto.");
581
588
  }
582
589
  }
590
+ if (Qnil != (v = rb_hash_lookup(ropts, compat_bigdecimal_sym))) {
591
+ copts->compat_bigdec = (Qtrue == v);
592
+ }
593
+ if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, oj_decimal_class_sym)) {
594
+ v = rb_hash_lookup(ropts, oj_decimal_class_sym);
595
+ if (rb_cFloat == v) {
596
+ copts->compat_bigdec = FloatDec;
597
+ } else if (oj_bigdecimal_class == v) {
598
+ copts->compat_bigdec = BigDec;
599
+ } else if (Qnil == v) {
600
+ copts->compat_bigdec = AutoDec;
601
+ } else {
602
+ rb_raise(rb_eArgError, ":decimal_class must be BigDecimal, Float, or nil.");
603
+ }
604
+ }
583
605
  if (Qtrue == rb_funcall(ropts, oj_has_key_id, 1, create_id_sym)) {
584
606
  v = rb_hash_lookup(ropts, create_id_sym);
585
607
  if (Qnil == v) {
@@ -803,7 +825,7 @@ oj_parse_opt_match_string(RxClass rc, VALUE ropts) {
803
825
  }
804
826
 
805
827
  /* Document-method: load
806
- * call-seq: load(json, options) { _|_obj, start, len_|_ }
828
+ * call-seq: load(json, options={}) { _|_obj, start, len_|_ }
807
829
  *
808
830
  * Parses a JSON document String into a Object, Hash, Array, String, Fixnum,
809
831
  * Float, true, false, or nil according to the default mode or the mode
@@ -887,7 +909,7 @@ load(int argc, VALUE *argv, VALUE self) {
887
909
  }
888
910
 
889
911
  /* Document-method: load_file
890
- * call-seq: load_file(path, options) { _|_obj, start, len_|_ }
912
+ * call-seq: load_file(path, options={}) { _|_obj, start, len_|_ }
891
913
  *
892
914
  * Parses a JSON document String into a Object, Hash, Array, String, Fixnum,
893
915
  * Float, true, false, or nil according to the default mode or the mode
@@ -1045,7 +1067,7 @@ safe_load(VALUE self, VALUE doc) {
1045
1067
  */
1046
1068
 
1047
1069
  /* Document-method: dump
1048
- * call-seq: dump(obj, options)
1070
+ * call-seq: dump(obj, options={})
1049
1071
  *
1050
1072
  * Dumps an Object (obj) to a string.
1051
1073
  * - *obj* [_Object_] Object to serialize as an JSON document String
@@ -1143,7 +1165,7 @@ to_json(int argc, VALUE *argv, VALUE self) {
1143
1165
  }
1144
1166
 
1145
1167
  /* Document-method: to_file
1146
- * call-seq: to_file(file_path, obj, options)
1168
+ * call-seq: to_file(file_path, obj, options={})
1147
1169
  *
1148
1170
  * Dumps an Object to the specified file.
1149
1171
  * - *file* [_String_] _path file path to write the JSON document to
@@ -1166,7 +1188,7 @@ to_file(int argc, VALUE *argv, VALUE self) {
1166
1188
  }
1167
1189
 
1168
1190
  /* Document-method: to_stream
1169
- * call-seq: to_stream(io, obj, options)
1191
+ * call-seq: to_stream(io, obj, options={})
1170
1192
  *
1171
1193
  * Dumps an Object to the specified IO stream.
1172
1194
  * - *io* [_IO_] IO stream to write the JSON document to
@@ -1627,13 +1649,21 @@ Init_oj() {
1627
1649
  rb_require("oj/schandler");
1628
1650
 
1629
1651
  oj_bag_class = rb_const_get_at(Oj, rb_intern("Bag"));
1652
+ rb_gc_register_mark_object(oj_bag_class);
1630
1653
  oj_bigdecimal_class = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
1654
+ rb_gc_register_mark_object(oj_bigdecimal_class);
1631
1655
  oj_date_class = rb_const_get(rb_cObject, rb_intern("Date"));
1656
+ rb_gc_register_mark_object(oj_date_class);
1632
1657
  oj_datetime_class = rb_const_get(rb_cObject, rb_intern("DateTime"));
1658
+ rb_gc_register_mark_object(oj_datetime_class);
1633
1659
  oj_enumerable_class = rb_const_get(rb_cObject, rb_intern("Enumerable"));
1660
+ rb_gc_register_mark_object(oj_enumerable_class);
1634
1661
  oj_parse_error_class = rb_const_get_at(Oj, rb_intern("ParseError"));
1662
+ rb_gc_register_mark_object(oj_parse_error_class);
1635
1663
  oj_stringio_class = rb_const_get(rb_cObject, rb_intern("StringIO"));
1664
+ rb_gc_register_mark_object(oj_stringio_class);
1636
1665
  oj_struct_class = rb_const_get(rb_cObject, rb_intern("Struct"));
1666
+ rb_gc_register_mark_object(oj_struct_class);
1637
1667
  oj_json_parser_error_class = rb_eEncodingError; // replaced if mimic is called
1638
1668
  oj_json_generator_error_class = rb_eEncodingError; // replaced if mimic is called
1639
1669
 
@@ -1648,12 +1678,14 @@ Init_oj() {
1648
1678
  bigdecimal_sym = ID2SYM(rb_intern("bigdecimal")); rb_gc_register_address(&bigdecimal_sym);
1649
1679
  circular_sym = ID2SYM(rb_intern("circular")); rb_gc_register_address(&circular_sym);
1650
1680
  class_cache_sym = ID2SYM(rb_intern("class_cache")); rb_gc_register_address(&class_cache_sym);
1681
+ compat_bigdecimal_sym = ID2SYM(rb_intern("compat_bigdecimal"));rb_gc_register_address(&compat_bigdecimal_sym);
1651
1682
  compat_sym = ID2SYM(rb_intern("compat")); rb_gc_register_address(&compat_sym);
1652
1683
  create_id_sym = ID2SYM(rb_intern("create_id")); rb_gc_register_address(&create_id_sym);
1653
1684
  custom_sym = ID2SYM(rb_intern("custom")); rb_gc_register_address(&custom_sym);
1654
1685
  empty_string_sym = ID2SYM(rb_intern("empty_string")); rb_gc_register_address(&empty_string_sym);
1655
1686
  escape_mode_sym = ID2SYM(rb_intern("escape_mode")); rb_gc_register_address(&escape_mode_sym);
1656
1687
  integer_range_sym = ID2SYM(rb_intern("integer_range")); rb_gc_register_address(&integer_range_sym);
1688
+ fast_sym = ID2SYM(rb_intern("fast")); rb_gc_register_address(&fast_sym);
1657
1689
  float_prec_sym = ID2SYM(rb_intern("float_precision")); rb_gc_register_address(&float_prec_sym);
1658
1690
  float_sym = ID2SYM(rb_intern("float")); rb_gc_register_address(&float_sym);
1659
1691
  huge_sym = ID2SYM(rb_intern("huge")); rb_gc_register_address(&huge_sym);
@@ -1672,6 +1704,7 @@ Init_oj() {
1672
1704
  oj_array_nl_sym = ID2SYM(rb_intern("array_nl")); rb_gc_register_address(&oj_array_nl_sym);
1673
1705
  oj_ascii_only_sym = ID2SYM(rb_intern("ascii_only")); rb_gc_register_address(&oj_ascii_only_sym);
1674
1706
  oj_create_additions_sym = ID2SYM(rb_intern("create_additions"));rb_gc_register_address(&oj_create_additions_sym);
1707
+ oj_decimal_class_sym = ID2SYM(rb_intern("decimal_class")); rb_gc_register_address(&oj_decimal_class_sym);
1675
1708
  oj_hash_class_sym = ID2SYM(rb_intern("hash_class")); rb_gc_register_address(&oj_hash_class_sym);
1676
1709
  oj_indent_sym = ID2SYM(rb_intern("indent")); rb_gc_register_address(&oj_indent_sym);
1677
1710
  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
- /* oj.h
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 {
@@ -136,6 +135,7 @@ typedef struct _options {
136
135
  char time_format; // TimeFormat
137
136
  char bigdec_as_num; // YesNo
138
137
  char bigdec_load; // BigLoad
138
+ char compat_bigdec; // boolean (0 or 1)
139
139
  char to_hash; // YesNo
140
140
  char to_json; // YesNo
141
141
  char as_json; // YesNo
@@ -311,6 +311,7 @@ extern VALUE oj_array_class_sym;
311
311
  extern VALUE oj_array_nl_sym;
312
312
  extern VALUE oj_ascii_only_sym;
313
313
  extern VALUE oj_create_additions_sym;
314
+ extern VALUE oj_decimal_class_sym;
314
315
  extern VALUE oj_hash_class_sym;
315
316
  extern VALUE oj_indent_sym;
316
317
  extern VALUE oj_max_nesting_sym;
data/ext/oj/parse.c CHANGED
@@ -1,7 +1,4 @@
1
- /* parse.c
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,8 +384,14 @@ read_num(ParseInfo pi) {
393
384
  ni.infinity = 0;
394
385
  ni.nan = 0;
395
386
  ni.neg = 0;
396
- ni.hasExp = 0;
397
- ni.no_big = (FloatDec == pi->options.bigdec_load);
387
+ ni.has_exp = 0;
388
+ if (CompatMode == pi->options.mode) {
389
+ ni.no_big = !pi->options.compat_bigdec;
390
+ ni.bigdec_load = pi->options.compat_bigdec;
391
+ } else {
392
+ ni.no_big = (FloatDec == pi->options.bigdec_load || FastDec == pi->options.bigdec_load || RubyDec == pi->options.bigdec_load);
393
+ ni.bigdec_load = pi->options.bigdec_load;
394
+ }
398
395
 
399
396
  if ('-' == *pi->cur) {
400
397
  pi->cur++;
@@ -453,6 +450,7 @@ read_num(ParseInfo pi) {
453
450
  // except when mimicing the JSON gem or in strict mode.
454
451
  if (StrictMode == pi->options.mode || CompatMode == pi->options.mode) {
455
452
  int pos = (int)(pi->cur - ni.str);
453
+
456
454
  if (1 == pos || (2 == pos && ni.neg)) {
457
455
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
458
456
  return;
@@ -468,18 +466,26 @@ read_num(ParseInfo pi) {
468
466
  if (0 < ni.num || 0 < ni.i) {
469
467
  dec_cnt++;
470
468
  }
471
- ni.num = ni.num * 10 + d;
472
- ni.div *= 10;
473
- ni.di++;
474
- if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
475
- ni.big = 1;
469
+ if (INT64_MAX <= ni.div) {
470
+ if (!ni.no_big) {
471
+ ni.big = true;
472
+ }
473
+ } else {
474
+ ni.num = ni.num * 10 + d;
475
+ ni.div *= 10;
476
+ ni.di++;
477
+ if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
478
+ if (!ni.no_big) {
479
+ ni.big = true;
480
+ }
481
+ }
476
482
  }
477
483
  }
478
484
  }
479
485
  if ('e' == *pi->cur || 'E' == *pi->cur) {
480
486
  int eneg = 0;
481
487
 
482
- ni.hasExp = 1;
488
+ ni.has_exp = 1;
483
489
  pi->cur++;
484
490
  if ('-' == *pi->cur) {
485
491
  pi->cur++;
@@ -490,7 +496,7 @@ read_num(ParseInfo pi) {
490
496
  for (; '0' <= *pi->cur && *pi->cur <= '9'; pi->cur++) {
491
497
  ni.exp = ni.exp * 10 + (*pi->cur - '0');
492
498
  if (EXP_MAX <= ni.exp) {
493
- ni.big = 1;
499
+ ni.big = true;
494
500
  }
495
501
  }
496
502
  if (eneg) {
@@ -510,7 +516,11 @@ read_num(ParseInfo pi) {
510
516
  ni.nan = 1;
511
517
  }
512
518
  }
513
- if (BigDec == pi->options.bigdec_load) {
519
+ if (CompatMode == pi->options.mode) {
520
+ if (pi->options.compat_bigdec) {
521
+ ni.big = 1;
522
+ }
523
+ } else if (BigDec == pi->options.bigdec_load) {
514
524
  ni.big = 1;
515
525
  }
516
526
  if (0 == parent) {
@@ -750,6 +760,59 @@ parse_big_decimal(VALUE str) {
750
760
  return rb_funcall(rb_cObject, oj_bigdecimal_id, 1, str);
751
761
  }
752
762
 
763
+ static long double exp_plus[] = {
764
+ 1.0,
765
+ 1.0e1,
766
+ 1.0e2,
767
+ 1.0e3,
768
+ 1.0e4,
769
+ 1.0e5,
770
+ 1.0e6,
771
+ 1.0e7,
772
+ 1.0e8,
773
+ 1.0e9,
774
+ 1.0e10,
775
+ 1.0e11,
776
+ 1.0e12,
777
+ 1.0e13,
778
+ 1.0e14,
779
+ 1.0e15,
780
+ 1.0e16,
781
+ 1.0e17,
782
+ 1.0e18,
783
+ 1.0e19,
784
+ 1.0e20,
785
+ 1.0e21,
786
+ 1.0e22,
787
+ 1.0e23,
788
+ 1.0e24,
789
+ 1.0e25,
790
+ 1.0e26,
791
+ 1.0e27,
792
+ 1.0e28,
793
+ 1.0e29,
794
+ 1.0e30,
795
+ 1.0e31,
796
+ 1.0e32,
797
+ 1.0e33,
798
+ 1.0e34,
799
+ 1.0e35,
800
+ 1.0e36,
801
+ 1.0e37,
802
+ 1.0e38,
803
+ 1.0e39,
804
+ 1.0e40,
805
+ 1.0e41,
806
+ 1.0e42,
807
+ 1.0e43,
808
+ 1.0e44,
809
+ 1.0e45,
810
+ 1.0e46,
811
+ 1.0e47,
812
+ 1.0e48,
813
+ 1.0e49,
814
+ };
815
+
753
816
  VALUE
754
817
  oj_num_as_value(NumInfo ni) {
755
818
  volatile VALUE rnum = Qnil;
@@ -762,7 +825,7 @@ oj_num_as_value(NumInfo ni) {
762
825
  }
763
826
  } else if (ni->nan) {
764
827
  rnum = rb_float_new(0.0/0.0);
765
- } else if (1 == ni->div && 0 == ni->exp) { // fixnum
828
+ } else if (1 == ni->div && 0 == ni->exp && !ni->has_exp) { // fixnum
766
829
  if (ni->big) {
767
830
  if (256 > ni->len) {
768
831
  char buf[256];
@@ -793,33 +856,39 @@ oj_num_as_value(NumInfo ni) {
793
856
  if (ni->no_big) {
794
857
  rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
795
858
  }
796
- } else {
797
- // All these machinations are to get rounding to work better.
798
- long double d = (long double)ni->i * (long double)ni->div + (long double)ni->num;
859
+ } else if (FastDec == ni->bigdec_load) {
860
+ long double ld = (long double)ni->i * (long double)ni->div + (long double)ni->num;
799
861
  int x = (int)((int64_t)ni->exp - ni->di);
800
862
 
801
- // Rounding sometimes cuts off the last digit even if there are only
802
- // 15 digits. This attempts to fix those few cases where this
803
- // occurs.
804
- if ((long double)INT64_MAX > d && (int64_t)d != (ni->i * ni->div + ni->num)) {
805
- volatile VALUE bd = rb_str_new(ni->str, ni->len);
806
-
807
- rnum = rb_rescue2(parse_big_decimal, bd, rescue_big_decimal, bd, rb_eException, 0);
808
- if (ni->no_big) {
809
- rnum = rb_funcall(rnum, rb_intern("to_f"), 0);
810
- }
811
- } else {
812
- d = roundl(d);
813
- if (0 < x) {
814
- d *= powl(10.0L, x);
815
- } else if (0 > x) {
816
- d /= powl(10.0L, -x);
863
+ if (0 < x) {
864
+ if (x < (int)(sizeof(exp_plus) / sizeof(*exp_plus))) {
865
+ ld *= exp_plus[x];
866
+ } else {
867
+ ld *= powl(10.0, x);
817
868
  }
818
- if (ni->neg) {
819
- d = -d;
869
+ } else if (x < 0) {
870
+ if (-x < (int)(sizeof(exp_plus) / sizeof(*exp_plus))) {
871
+ ld /= exp_plus[-x];
872
+ } else {
873
+ ld /= powl(10.0, -x);
820
874
  }
821
- rnum = rb_float_new((double)d);
822
875
  }
876
+ if (ni->neg) {
877
+ ld = -ld;
878
+ }
879
+ rnum = rb_float_new((double)ld);
880
+ } else if (RubyDec == ni->bigdec_load) {
881
+ volatile VALUE sv = rb_str_new(ni->str, ni->len);
882
+
883
+ rnum = rb_funcall(sv, rb_intern("to_f"), 0);
884
+ } else {
885
+ char *end;
886
+ double d = strtod(ni->str, &end);
887
+
888
+ if ((long)ni->len != (long)(end - ni->str)) {
889
+ rb_raise(oj_parse_error_class, "Invalid float");
890
+ }
891
+ rnum = rb_float_new(d);
823
892
  }
824
893
  }
825
894
  return rnum;
@@ -841,6 +910,12 @@ oj_set_error_at(ParseInfo pi, VALUE err_clas, const char* file, int line, const
841
910
  if (p + 3 < end) {
842
911
  *p++ = ' ';
843
912
  *p++ = '(';
913
+ *p++ = 'a';
914
+ *p++ = 'f';
915
+ *p++ = 't';
916
+ *p++ = 'e';
917
+ *p++ = 'r';
918
+ *p++ = ' ';
844
919
  start = p;
845
920
  for (vp = pi->stack.head; vp < pi->stack.tail; vp++) {
846
921
  if (end <= p + 1 + vp->klen) {
data/ext/oj/parse.h CHANGED
@@ -1,7 +1,4 @@
1
- /* parse.h
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 hasExp;
31
+ int has_exp;
35
32
  int no_big;
33
+ int bigdec_load;
36
34
  } *NumInfo;
37
35
 
38
36
  typedef struct _parseInfo {