oj 3.9.2 → 3.10.18

Sign up to get free protection for your applications and to get access to all the features.
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);