oj 3.15.1 → 3.16.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 257582096951b3c4db4b4979c49afd542ae855ba454bd6d51dcb1a69f68c2405
4
- data.tar.gz: d835a958d019f7034e93d3f97ca1829fad74a4e26743f6a8b2b581c03ac9f14a
3
+ metadata.gz: 380409882636ecb1f8e3e54369f85dc11d7e167db741f5aec15bfadad2180601
4
+ data.tar.gz: 0c60d6f8a5b7d76904b84d378e41632f67c0e3fa2e61aeacfb680cadf15ef9dc
5
5
  SHA512:
6
- metadata.gz: 374e55547de2db4b2199c05bebe5fa74953021883f7f609e328466c076016817b0cb8a7be03ead9c68b3fe4895eb5de0bcd60e1384ff7f1a5f7cd480824faba0
7
- data.tar.gz: ec9cf41116602a51c49ff73ba6fc39ecbb501d51cbab0a6065bb38d2ac15a7f42332dc66f0d97df40bea6da5fafbc15209d06039180829c4c4b299b0e138676e
6
+ metadata.gz: 1cbb6d75ab0c495c8d40983970e8fca54aa846eedb4e031368a98874f1db4ca68f160076ac59c230186316dc686923bad86196fa09bf585f501cf11fe814f9bc
7
+ data.tar.gz: 9d521e53c4e5311eda8abb0f89b12ae2dd3f8007abb02f0b5ff7728311f6d0b64221a493f8df66a7f223dd12c2c2ba6d47b309867f99e2fe206d440d028368a6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.16.1 - 2023-09-01
4
+
5
+ - Fixed exception type on number parsing. (thank you @jasonpenny)
6
+
7
+ ## 3.16.0 - 2023-08-16
8
+
9
+ - Added the `float_format` option.
10
+
11
+ - Expanded the `max_nesting` option to allow integer values as well as
12
+ the previous boolean (true or nil).
13
+
14
+ - Skip nesting tests with Truffle Ruby in the json gem tests.
15
+
3
16
  ## 3.15.1 - 2023-07-30
4
17
 
5
18
  - Add protection against some using `require 'oj/json`, an internal file.
data/ext/oj/dump_compat.c CHANGED
@@ -851,13 +851,18 @@ static DumpFunc compat_funcs[] = {
851
851
  };
852
852
 
853
853
  static void set_state_depth(VALUE state, int depth) {
854
- VALUE json_module = rb_const_get_at(rb_cObject, rb_intern("JSON"));
855
- VALUE ext = rb_const_get(json_module, rb_intern("Ext"));
856
- VALUE generator = rb_const_get(ext, rb_intern("Generator"));
857
- VALUE state_class = rb_const_get(generator, rb_intern("State"));
854
+ if (0 == rb_const_defined(rb_cObject, rb_intern("JSON"))) {
855
+ rb_require("oj/json");
856
+ }
857
+ {
858
+ VALUE json_module = rb_const_get_at(rb_cObject, rb_intern("JSON"));
859
+ VALUE ext = rb_const_get(json_module, rb_intern("Ext"));
860
+ VALUE generator = rb_const_get(ext, rb_intern("Generator"));
861
+ VALUE state_class = rb_const_get(generator, rb_intern("State"));
858
862
 
859
- if (state_class == rb_obj_class(state)) {
860
- rb_funcall(state, rb_intern("depth="), 1, INT2NUM(depth));
863
+ if (state_class == rb_obj_class(state)) {
864
+ rb_funcall(state, rb_intern("depth="), 1, INT2NUM(depth));
865
+ }
861
866
  }
862
867
  }
863
868
 
@@ -865,20 +870,15 @@ void oj_dump_compat_val(VALUE obj, int depth, Out out, bool as_ok) {
865
870
  int type = rb_type(obj);
866
871
 
867
872
  TRACE(out->opts->trace, "dump", obj, depth, TraceIn);
873
+ // The max_nesting logic is that an empty Array or Hash is assumed to have
874
+ // content so the max_nesting should fail but a non-collection value is
875
+ // okay. That means a check for a collectable value is needed before
876
+ // raising.
868
877
  if (out->opts->dump_opts.max_depth <= depth) {
869
- // When JSON.dump is called then an ArgumentError is expected and the
870
- // limit is the depth inclusive. If JSON.generate is called then a
871
- // NestingError is expected and the limit is inclusive. Worse than
872
- // that there are unit tests for both.
873
- if (CALLER_DUMP == out->caller) {
878
+ if (RUBY_T_ARRAY == type || RUBY_T_HASH == type) {
874
879
  if (0 < out->argc) {
875
880
  set_state_depth(*out->argv, depth);
876
881
  }
877
- rb_raise(rb_eArgError, "Too deeply nested.");
878
- } else if (out->opts->dump_opts.max_depth < depth) {
879
- if (0 < out->argc) {
880
- set_state_depth(*out->argv, depth - 1);
881
- }
882
882
  raise_json_err("Too deeply nested", "NestingError");
883
883
  }
884
884
  }
data/ext/oj/mimic_json.c CHANGED
@@ -209,7 +209,6 @@ static VALUE mimic_dump(int argc, VALUE *argv, VALUE self) {
209
209
 
210
210
  oj_out_init(&out);
211
211
 
212
- out.caller = CALLER_DUMP;
213
212
  copts.escape_mode = JXEsc;
214
213
  copts.mode = CompatMode;
215
214
 
@@ -368,7 +367,6 @@ static VALUE mimic_generate_core(int argc, VALUE *argv, Options copts) {
368
367
  oj_out_init(&out);
369
368
 
370
369
  out.omit_nil = copts->dump_opts.omit_nil;
371
- out.caller = CALLER_GENERATE;
372
370
  // For obj.to_json or generate nan is not allowed but if called from dump
373
371
  // it is.
374
372
  copts->dump_opts.nan_dump = RaiseNan;
data/ext/oj/oj.c CHANGED
@@ -123,6 +123,7 @@ static VALUE escape_mode_sym;
123
123
  static VALUE integer_range_sym;
124
124
  static VALUE fast_sym;
125
125
  static VALUE float_prec_sym;
126
+ static VALUE float_format_sym;
126
127
  static VALUE float_sym;
127
128
  static VALUE huge_sym;
128
129
  static VALUE ignore_sym;
@@ -232,7 +233,7 @@ struct _options oj_default_options = {
232
233
  NULL, // tail
233
234
  {'\0'}, // err
234
235
  },
235
- NULL, // ignore
236
+ NULL,
236
237
  };
237
238
 
238
239
  /* Document-method: default_options()
@@ -267,6 +268,8 @@ struct _options oj_default_options = {
267
268
  *seconds portion of time
268
269
  * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0
269
270
  *indicates use Ruby
271
+ * - *:float_format* [_String_] the C printf format string for printing floats. Default follows
272
+ * the float_precision and will be changed if float_precision is changed. The string can be no more than 6 bytes.
270
273
  * - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false
271
274
  * - *:use_as_json* [_Boolean_|_nil_] call as_json() methods on dump, default is false
272
275
  * - *:use_raw_json* [_Boolean_|_nil_] call raw_json() methods on dump, default is false
@@ -378,6 +381,7 @@ static VALUE get_def_opts(VALUE self) {
378
381
  oj_safe_sym,
379
382
  (Yes == oj_default_options.safe) ? Qtrue : ((No == oj_default_options.safe) ? Qfalse : Qnil));
380
383
  rb_hash_aset(opts, float_prec_sym, INT2FIX(oj_default_options.float_prec));
384
+ rb_hash_aset(opts, float_format_sym, rb_str_new_cstr(oj_default_options.float_fmt));
381
385
  rb_hash_aset(opts, cache_str_sym, INT2FIX(oj_default_options.cache_str));
382
386
  rb_hash_aset(
383
387
  opts,
@@ -519,6 +523,8 @@ static VALUE get_def_opts(VALUE self) {
519
523
  *load.
520
524
  * - *:second_precision* [_Fixnum_|_nil_] number of digits after the decimal when dumping the
521
525
  *seconds portion of time.
526
+ * - *:float_format* [_String_] the C printf format string for printing floats. Default follows
527
+ * the float_precision and will be changed if float_precision is changed. The string can be no more than 6 bytes.
522
528
  * - *:float_precision* [_Fixnum_|_nil_] number of digits of precision when dumping floats, 0
523
529
  *indicates use Ruby.
524
530
  * - *:use_to_json* [_Boolean_|_nil_] call to_json() methods on dump, default is false.
@@ -617,7 +623,6 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
617
623
  if (set_yesno_options(k, v, copts)) {
618
624
  return ST_CONTINUE;
619
625
  }
620
-
621
626
  if (oj_indent_sym == k) {
622
627
  switch (rb_type(v)) {
623
628
  case T_NIL:
@@ -757,7 +762,6 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
757
762
  if (Qnil == v) {
758
763
  return ST_CONTINUE;
759
764
  }
760
-
761
765
  copts->compat_bigdec = (Qtrue == v);
762
766
  } else if (oj_decimal_class_sym == k) {
763
767
  if (rb_cFloat == v) {
@@ -949,6 +953,25 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
949
953
  return ST_CONTINUE;
950
954
  }
951
955
  copts->sym_key = (Qtrue == v) ? Yes : No;
956
+
957
+ } else if (oj_max_nesting_sym == k) {
958
+ if (Qtrue == v) {
959
+ copts->dump_opts.max_depth = 100;
960
+ } else if (Qfalse == v || Qnil == v) {
961
+ copts->dump_opts.max_depth = MAX_DEPTH;
962
+ } else if (T_FIXNUM == rb_type(v)) {
963
+ copts->dump_opts.max_depth = NUM2INT(v);
964
+ if (0 >= copts->dump_opts.max_depth) {
965
+ copts->dump_opts.max_depth = MAX_DEPTH;
966
+ }
967
+ }
968
+ } else if (float_format_sym == k) {
969
+ rb_check_type(v, T_STRING);
970
+ if (6 < (int)RSTRING_LEN(v)) {
971
+ rb_raise(rb_eArgError, ":float_format must be 6 bytes or less.");
972
+ }
973
+ strncpy(copts->float_fmt, RSTRING_PTR(v), (size_t)RSTRING_LEN(v));
974
+ copts->float_fmt[RSTRING_LEN(v)] = '\0';
952
975
  }
953
976
  return ST_CONTINUE;
954
977
  }
@@ -957,7 +980,6 @@ void oj_parse_options(VALUE ropts, Options copts) {
957
980
  if (T_HASH != rb_type(ropts)) {
958
981
  return;
959
982
  }
960
-
961
983
  rb_hash_foreach(ropts, parse_options_cb, (VALUE)copts);
962
984
  oj_parse_opt_match_string(&copts->str_rx, ropts);
963
985
 
@@ -1296,7 +1318,6 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
1296
1318
 
1297
1319
  arg.out->omit_nil = copts.dump_opts.omit_nil;
1298
1320
  arg.out->omit_null_byte = copts.dump_opts.omit_null_byte;
1299
- arg.out->caller = CALLER_DUMP;
1300
1321
 
1301
1322
  return rb_ensure(dump_body, (VALUE)&arg, dump_ensure, (VALUE)&arg);
1302
1323
  }
@@ -1308,8 +1329,9 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
1308
1329
  * will be called. The mode is set to :compat.
1309
1330
  * - *obj* [_Object_] Object to serialize as an JSON document String
1310
1331
  * - *options* [_Hash_]
1311
- * - *:max_nesting* [_boolean_] It true nesting is limited to 100. The option to detect circular
1312
- * references is available but is not compatible with the json gem., default is false
1332
+ * - *:max_nesting* [_Fixnum_|_boolean_] It true nesting is limited to 100. If a Fixnum nesting
1333
+ * is set to the provided value. The option to detect circular references is available but is not
1334
+ * compatible with the json gem., default is false or unlimited.
1313
1335
  * - *:allow_nan* [_boolean_] If true non JSON compliant words such as Nan and Infinity will be
1314
1336
  * used as appropriate, default is true.
1315
1337
  * - *:quirks_mode* [_boolean_] Allow single JSON values instead of documents, default is true
@@ -1929,6 +1951,8 @@ void Init_oj(void) {
1929
1951
  rb_gc_register_address(&integer_range_sym);
1930
1952
  fast_sym = ID2SYM(rb_intern("fast"));
1931
1953
  rb_gc_register_address(&fast_sym);
1954
+ float_format_sym = ID2SYM(rb_intern("float_format"));
1955
+ rb_gc_register_address(&float_format_sym);
1932
1956
  float_prec_sym = ID2SYM(rb_intern("float_precision"));
1933
1957
  rb_gc_register_address(&float_prec_sym);
1934
1958
  float_sym = ID2SYM(rb_intern("float"));
data/ext/oj/oj.h CHANGED
@@ -103,13 +103,6 @@ typedef enum {
103
103
  FILE_IO = 'f',
104
104
  } StreamWriterType;
105
105
 
106
- typedef enum {
107
- CALLER_DUMP = 'd',
108
- CALLER_TO_JSON = 't',
109
- CALLER_GENERATE = 'g',
110
- // Add the fast versions if necessary. Maybe unparse as well if needed.
111
- } DumpCaller;
112
-
113
106
  typedef struct _dumpOpts {
114
107
  bool use;
115
108
  char indent_str[16];
@@ -188,23 +181,22 @@ typedef struct _rOptTable {
188
181
  } *ROptTable;
189
182
 
190
183
  typedef struct _out {
191
- char stack_buffer[4096];
192
- char *buf;
193
- char *end;
194
- char *cur;
195
- Cache8 circ_cache;
196
- slot_t circ_cnt;
197
- int indent;
198
- int depth; // used by dump_hash
199
- Options opts;
200
- uint32_t hash_cnt;
201
- bool allocated;
202
- bool omit_nil;
203
- bool omit_null_byte;
204
- int argc;
205
- VALUE *argv;
206
- DumpCaller caller; // used for the mimic json only
207
- ROptTable ropts;
184
+ char stack_buffer[4096];
185
+ char *buf;
186
+ char *end;
187
+ char *cur;
188
+ Cache8 circ_cache;
189
+ slot_t circ_cnt;
190
+ int indent;
191
+ int depth; // used by dump_hash
192
+ Options opts;
193
+ uint32_t hash_cnt;
194
+ bool allocated;
195
+ bool omit_nil;
196
+ bool omit_null_byte;
197
+ int argc;
198
+ VALUE *argv;
199
+ ROptTable ropts;
208
200
  } *Out;
209
201
 
210
202
  typedef struct _strWriter {
data/ext/oj/parse.c CHANGED
@@ -875,7 +875,11 @@ oj_num_as_value(NumInfo ni) {
875
875
  double d = strtod(ni->str, &end);
876
876
 
877
877
  if ((long)ni->len != (long)(end - ni->str)) {
878
- rb_raise(ni->pi->err_class, "Invalid float");
878
+ if (Qnil == ni->pi->err_class) {
879
+ rb_raise(oj_parse_error_class, "Invalid float");
880
+ } else {
881
+ rb_raise(ni->pi->err_class, "Invalid float");
882
+ }
879
883
  }
880
884
  rnum = rb_float_new(d);
881
885
  }
data/ext/oj/rails.c CHANGED
@@ -890,7 +890,6 @@ static VALUE encode(VALUE obj, ROptTable ropts, Options opts, int argc, VALUE *a
890
890
  oj_out_init(&out);
891
891
 
892
892
  out.omit_nil = copts.dump_opts.omit_nil;
893
- out.caller = 0;
894
893
  out.cur = out.buf;
895
894
  out.circ_cnt = 0;
896
895
  out.opts = &copts;
@@ -70,7 +70,6 @@ void oj_str_writer_init(StrWriter sw, int buf_size) {
70
70
  sw->out.depth = 0;
71
71
  sw->out.argc = 0;
72
72
  sw->out.argv = NULL;
73
- sw->out.caller = 0;
74
73
  sw->out.ropts = NULL;
75
74
  sw->out.omit_nil = oj_default_options.dump_opts.omit_nil;
76
75
  }
data/lib/oj/json.rb CHANGED
@@ -2,7 +2,10 @@ require 'ostruct'
2
2
  require 'oj/state'
3
3
 
4
4
  if defined?(JSON::PRETTY_STATE_PROTOTYPE)
5
- warn "WARNING: oj/json is a compatability shim used by Oj. Requiring the file explicitly is no recommended."
5
+ # There are enough people that try to use both the json gen and oj in mimic
6
+ # mode so don't display the warning.
7
+
8
+ # warn "WARNING: oj/json is a compatability shim used by Oj. Requiring the file explicitly is not recommended."
6
9
  end
7
10
 
8
11
  unless defined?(JSON::PRETTY_STATE_PROTOTYPE)
data/lib/oj/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Oj
2
2
  # Current version of the module.
3
- VERSION = '3.15.1'
3
+ VERSION = '3.16.1'
4
4
  end
@@ -128,8 +128,10 @@ class JSONCommonInterfaceTest < Test::Unit::TestCase
128
128
  too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
129
129
  assert_equal too_deep, JSON.dump(eval(too_deep))
130
130
  assert_kind_of String, Marshal.dump(eval(too_deep))
131
- assert_raise(ArgumentError) { JSON.dump(eval(too_deep), 100) }
132
- assert_raise(ArgumentError) { Marshal.dump(eval(too_deep), 100) }
131
+ if RUBY_ENGINE != 'truffleruby'
132
+ assert_raise(ArgumentError) { JSON.dump(eval(too_deep), 100) }
133
+ assert_raise(ArgumentError) { Marshal.dump(eval(too_deep), 100) }
134
+ end
133
135
  assert_equal too_deep, JSON.dump(eval(too_deep), 101)
134
136
  assert_kind_of String, Marshal.dump(eval(too_deep), 101)
135
137
  output = StringIO.new
@@ -352,7 +352,7 @@ class JSONGeneratorTest < Test::Unit::TestCase
352
352
  too_deep_ary = eval too_deep
353
353
  assert_raise(JSON::NestingError) { JSON.generate too_deep_ary }
354
354
  assert_raise(JSON::NestingError) { JSON.generate too_deep_ary, :max_nesting => 100 }
355
- ok = JSON.generate too_deep_ary, :max_nesting => 101
355
+ ok = JSON.generate too_deep_ary, :max_nesting => 102
356
356
  assert_equal too_deep, ok
357
357
  ok = JSON.generate too_deep_ary, :max_nesting => nil
358
358
  assert_equal too_deep, ok
data/test/test_compat.rb CHANGED
@@ -467,10 +467,28 @@ class CompatJuice < Minitest::Test
467
467
  end
468
468
 
469
469
  def test_arg_passing
470
- json = Oj.to_json(Argy.new(), :max_nesting=> 40)
470
+ json = Oj.to_json(Argy.new(), :max_nesting => 40)
471
471
  assert_equal(%|{"args":"[{:max_nesting=>40}]"}|, json)
472
472
  end
473
473
 
474
+ def test_max_nesting
475
+ assert_raises() { Oj.to_json([[[[[]]]]], :max_nesting => 3) }
476
+ assert_raises() { Oj.dump([[[[[]]]]], :max_nesting => 3, :mode=>:compat) }
477
+
478
+ assert_raises() { Oj.to_json([[]], :max_nesting => 1) }
479
+ assert_equal('[[]]', Oj.to_json([[]], :max_nesting => 2))
480
+
481
+ assert_raises() { Oj.dump([[]], :max_nesting => 1, :mode=>:compat) }
482
+ assert_equal('[[]]', Oj.dump([[]], :max_nesting => 2, :mode=>:compat))
483
+
484
+ assert_raises() { Oj.to_json([[3]], :max_nesting => 1) }
485
+ assert_equal('[[3]]', Oj.to_json([[3]], :max_nesting => 2))
486
+
487
+ assert_raises() { Oj.dump([[3]], :max_nesting => 1, :mode=>:compat) }
488
+ assert_equal('[[3]]', Oj.dump([[3]], :max_nesting => 2, :mode=>:compat))
489
+
490
+ end
491
+
474
492
  def test_bad_unicode
475
493
  assert_raises() { Oj.to_json("\xE4xy") }
476
494
  end
data/test/test_strict.rb CHANGED
@@ -69,6 +69,16 @@ class StrictJuice < Minitest::Test
69
69
  dump_and_load(-2.48e100 * 1.0e10, false)
70
70
  end
71
71
 
72
+ def test_invalid_float
73
+ begin
74
+ Oj.load("64ecb72d29191067f91ff95b")
75
+ rescue Oj::ParseError => e
76
+ assert(e.message == "Invalid float")
77
+ return
78
+ end
79
+ assert(false, "*** expected an exception")
80
+ end
81
+
72
82
  def test_nan_dump
73
83
  assert_equal('null', Oj.dump(0/0.0, :nan => :null))
74
84
  assert_equal('3.3e14159265358979323846', Oj.dump(0/0.0, :nan => :huge))
data/test/test_various.rb CHANGED
@@ -105,6 +105,7 @@ class Juice < Minitest::Test
105
105
  allow_gc: false,
106
106
  quirks_mode: false,
107
107
  allow_invalid_unicode: true,
108
+ float_format: '%0.13g',
108
109
  float_precision: 13,
109
110
  mode: :strict,
110
111
  escape_mode: :ascii,
@@ -416,6 +417,11 @@ class Juice < Minitest::Test
416
417
  }
417
418
  end
418
419
 
420
+ def test_dump_float
421
+ json = Oj.dump(1.23e-2, :mode => :null, :float_format => '%0.4f')
422
+ assert_equal('0.0123', json)
423
+ end
424
+
419
425
  # Class
420
426
  def test_class_null
421
427
  json = Oj.dump(Juice, :mode => :null)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oj
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.15.1
4
+ version: 3.16.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-31 00:00:00.000000000 Z
11
+ date: 2023-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest