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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  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 +6 -9
  10. data/ext/oj/custom.c +8 -7
  11. data/ext/oj/dump.c +33 -26
  12. data/ext/oj/dump.h +1 -4
  13. data/ext/oj/dump_compat.c +9 -14
  14. data/ext/oj/dump_leaf.c +2 -5
  15. data/ext/oj/dump_object.c +19 -15
  16. data/ext/oj/dump_strict.c +7 -9
  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/extconf.rb +5 -0
  21. data/ext/oj/fast.c +14 -42
  22. data/ext/oj/hash.c +4 -32
  23. data/ext/oj/hash.h +1 -29
  24. data/ext/oj/hash_test.c +1 -29
  25. data/ext/oj/mimic_json.c +28 -10
  26. data/ext/oj/object.c +4 -6
  27. data/ext/oj/odd.c +1 -4
  28. data/ext/oj/odd.h +1 -4
  29. data/ext/oj/oj.c +74 -38
  30. data/ext/oj/oj.h +9 -7
  31. data/ext/oj/parse.c +127 -52
  32. data/ext/oj/parse.h +4 -5
  33. data/ext/oj/rails.c +38 -8
  34. data/ext/oj/rails.h +1 -4
  35. data/ext/oj/reader.c +5 -8
  36. data/ext/oj/reader.h +2 -5
  37. data/ext/oj/resolve.c +1 -4
  38. data/ext/oj/resolve.h +1 -4
  39. data/ext/oj/rxclass.c +3 -6
  40. data/ext/oj/rxclass.h +1 -4
  41. data/ext/oj/saj.c +6 -9
  42. data/ext/oj/scp.c +1 -4
  43. data/ext/oj/sparse.c +31 -26
  44. data/ext/oj/stream_writer.c +4 -9
  45. data/ext/oj/strict.c +3 -6
  46. data/ext/oj/string_writer.c +1 -4
  47. data/ext/oj/trace.c +5 -8
  48. data/ext/oj/trace.h +1 -4
  49. data/ext/oj/util.c +1 -1
  50. data/ext/oj/util.h +1 -1
  51. data/ext/oj/val_stack.c +1 -29
  52. data/ext/oj/val_stack.h +1 -29
  53. data/ext/oj/wab.c +10 -13
  54. data/lib/oj/mimic.rb +45 -1
  55. data/lib/oj/version.rb +1 -1
  56. data/lib/oj.rb +0 -8
  57. data/pages/Modes.md +1 -1
  58. data/pages/Options.md +15 -11
  59. data/pages/Rails.md +60 -21
  60. data/test/activesupport5/abstract_unit.rb +45 -0
  61. data/test/activesupport5/decoding_test.rb +68 -60
  62. data/test/activesupport5/encoding_test.rb +111 -96
  63. data/test/activesupport5/encoding_test_cases.rb +33 -25
  64. data/test/activesupport5/test_helper.rb +43 -21
  65. data/test/activesupport5/time_zone_test_helpers.rb +18 -3
  66. data/test/activesupport6/abstract_unit.rb +44 -0
  67. data/test/activesupport6/decoding_test.rb +133 -0
  68. data/test/activesupport6/encoding_test.rb +507 -0
  69. data/test/activesupport6/encoding_test_cases.rb +98 -0
  70. data/test/activesupport6/test_common.rb +17 -0
  71. data/test/activesupport6/test_helper.rb +163 -0
  72. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  73. data/test/bar.rb +21 -11
  74. data/test/baz.rb +16 -0
  75. data/test/foo.rb +39 -8
  76. data/test/json_gem/json_common_interface_test.rb +8 -3
  77. data/test/prec.rb +23 -0
  78. data/test/sample_json.rb +1 -1
  79. data/test/test_compat.rb +14 -8
  80. data/test/test_custom.rb +36 -6
  81. data/test/test_integer_range.rb +1 -2
  82. data/test/test_object.rb +12 -3
  83. data/test/test_rails.rb +35 -0
  84. data/test/test_strict.rb +24 -1
  85. data/test/test_various.rb +42 -64
  86. data/test/tests.rb +1 -0
  87. metadata +29 -7
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;
@@ -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
- 0, // integer_range_min
185
- 0, // integer_range_max
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.integer_range_max != 0 || oj_default_options.integer_range_min != 0) {
301
- VALUE range = rb_obj_alloc(rb_cRange);
302
- VALUE min = LONG2FIX(oj_default_options.integer_range_min);
303
- VALUE max = LONG2FIX(oj_default_options.integer_range_max);
304
- rb_ivar_set(range, oj_begin_id, min);
305
- rb_ivar_set(range, oj_end_id, max);
306
- rb_hash_aset(opts, integer_range_sym, range);
307
- }
308
- else {
309
- rb_hash_aset(opts, integer_range_sym, Qnil);
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
- if (TYPE(v) == T_STRUCT && rb_obj_class(v) == rb_cRange) {
742
- VALUE min = rb_funcall(v, oj_begin_id, 0);
743
- VALUE max = rb_funcall(v, oj_end_id, 0);
744
-
745
- if (TYPE(min) != T_FIXNUM || TYPE(max) != T_FIXNUM) {
746
- rb_raise(rb_eArgError, ":integer_range range bounds is not Fixnum.");
747
- }
748
-
749
- copts->integer_range_min = FIX2LONG(min);
750
- copts->integer_range_max = FIX2LONG(max);
751
- } else if (Qfalse != v) {
752
- rb_raise(rb_eArgError, ":integer_range must be a range of Fixnum.");
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, RxClass rc) {
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
- /* 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 {
@@ -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
- int64_t integer_range_min; // dump numbers outside range as string
153
- int64_t integer_range_max;
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
- /* 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,13 +384,18 @@ 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
+ 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
- ni.num = ni.num * 10 + d;
463
- ni.div *= 10;
464
- ni.di++;
465
- if (INT64_MAX <= ni.div || DEC_MAX < dec_cnt) {
466
- ni.big = 1;
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.hasExp = 1;
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 = 1;
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
- // All these machinations are to get rounding to work better.
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
- // Rounding sometimes cuts off the last digit even if there are only
793
- // 15 digits. This attempts to fix those few cases where this
794
- // occurs.
795
- if ((long double)INT64_MAX > d && (int64_t)d != (ni->i * ni->div + ni->num)) {
796
- volatile VALUE bd = rb_str_new(ni->str, ni->len);
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
- if (ni->neg) {
810
- d = -d;
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::ParseError.
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
- /* 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 {
@@ -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);