oj 3.15.0 → 3.16.0

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: 929e5766d8a556ed9590574901fe8268979fd6c165d10e1a2a8601a594dd6897
4
- data.tar.gz: 966fedee6a16f73c004b56050474377ae48521f5858da962f0f0416b5636a4c3
3
+ metadata.gz: affe42de16d26dfe301f38df7c0bc8ceee39ab25e5a6bd14bd7af6671cc3b422
4
+ data.tar.gz: ee1d7227f864b197aca62cb767f9202798b6d3d13cf0b5f89eae3f3a36450dbb
5
5
  SHA512:
6
- metadata.gz: f458f616c33c48d84cb0e1d2c1c45e8b677609c19251345328721fe3b7b6c4c5aa1fd4906da2234b60889a6e3bb45ee0371e7de7565bb6d1aa7d924e231e36dd
7
- data.tar.gz: fbbf233fc1ce57739d2fe538c179a24282e4c043740c2b47cce29745e3847712afa7828d321b877a3c29ab7d2216e7185af9a85bb9c8eb96b6036f9e3e49c1d1
6
+ metadata.gz: 8dbf6a0b5fef2b179dba864b595113f73ac27a952307559422bf1feda19fcc008a5e43a142d1bb3eb0cc4125ff18f35730208c7f82b11089ff90690e7b1f17d0
7
+ data.tar.gz: be09c04fa551288e2708c9151d0c966a09c3768ba15442cc54b8b4dd5850e2228b45608c06c8251c4e2ca32333353713f9c1feff81e4d34a48d1c6bf725014f6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.16.0 - 2023-08-16
4
+
5
+ - Added the `float_format` option.
6
+
7
+ - Expanded the `max_nesting` option to allow integer values as well as
8
+ the previous boolean (true or nil).
9
+
10
+ - Skip nesting tests with Truffle Ruby in the json gem tests.
11
+
12
+ ## 3.15.1 - 2023-07-30
13
+
14
+ - Add protection against some using `require 'oj/json`, an internal file.
15
+
16
+ - Fixed non-json errors when in compat mode.
17
+
3
18
  ## 3.15.0 - 2023-06-02
4
19
 
5
20
  - Added `omit_null_byte` option when dumping.
data/ext/oj/code.c CHANGED
@@ -185,24 +185,17 @@ void oj_code_attrs(VALUE obj, Attr attrs, int depth, Out out, bool with_class) {
185
185
  } else {
186
186
  char buf[32];
187
187
  char *b = buf + sizeof(buf) - 1;
188
- int neg = 0;
188
+ bool neg = false;
189
189
  long num = attrs->num;
190
190
  size_t cnt = 0;
191
191
 
192
192
  if (0 > num) {
193
- neg = 1;
193
+ neg = true;
194
194
  num = -num;
195
195
  }
196
196
  *b-- = '\0';
197
197
  if (0 < num) {
198
- for (; 0 < num; num /= 10, b--) {
199
- *b = (num % 10) + '0';
200
- }
201
- if (neg) {
202
- *b = '-';
203
- } else {
204
- b++;
205
- }
198
+ b = oj_longlong_to_string(num, neg, b);
206
199
  } else {
207
200
  *b = '0';
208
201
  }
data/ext/oj/compat.c CHANGED
@@ -13,32 +13,19 @@
13
13
  #include "trace.h"
14
14
 
15
15
  static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
16
- const char *key = kval->key;
17
- int klen = kval->klen;
18
- Val parent = stack_peek(&pi->stack);
19
- volatile VALUE rkey = kval->key_val;
16
+ const char *key = kval->key;
17
+ int klen = kval->klen;
18
+ Val parent = stack_peek(&pi->stack);
20
19
 
21
- if (Qundef == rkey && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
20
+ if (Qundef == kval->key_val && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
22
21
  *pi->options.create_id == *key && (int)pi->options.create_id_len == klen &&
23
22
  0 == strncmp(pi->options.create_id, key, klen)) {
24
23
  parent->classname = oj_strndup(str, len);
25
24
  parent->clen = len;
26
25
  } else {
27
26
  volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
27
+ volatile VALUE rkey = oj_calc_hash_key(pi, kval);
28
28
 
29
- if (Qundef == rkey) {
30
- if (Yes != pi->options.cache_keys) {
31
- if (Yes == pi->options.sym_key) {
32
- rkey = ID2SYM(rb_intern3(key, klen, oj_utf8_encoding));
33
- } else {
34
- rkey = rb_utf8_str_new(key, klen);
35
- }
36
- } else if (Yes == pi->options.sym_key) {
37
- rkey = oj_sym_intern(key, klen);
38
- } else {
39
- rkey = oj_str_intern(key, klen);
40
- }
41
- }
42
29
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
43
30
  VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
44
31
 
data/ext/oj/custom.c CHANGED
@@ -889,12 +889,11 @@ void oj_dump_custom_val(VALUE obj, int depth, Out out, bool as_ok) {
889
889
  ///// load functions /////
890
890
 
891
891
  static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
892
- const char *key = kval->key;
893
- int klen = kval->klen;
894
- Val parent = stack_peek(&pi->stack);
895
- volatile VALUE rkey = kval->key_val;
892
+ const char *key = kval->key;
893
+ int klen = kval->klen;
894
+ Val parent = stack_peek(&pi->stack);
896
895
 
897
- if (Qundef == rkey && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
896
+ if (Qundef == kval->key_val && Yes == pi->options.create_ok && NULL != pi->options.create_id &&
898
897
  *pi->options.create_id == *key && (int)pi->options.create_id_len == klen &&
899
898
  0 == strncmp(pi->options.create_id, key, klen)) {
900
899
  parent->clas = oj_name2class(pi, str, len, false, rb_eArgError);
@@ -907,15 +906,8 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
907
906
  }
908
907
  } else {
909
908
  volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
910
- // volatile VALUE rstr = rb_utf8_str_new(str, len);
909
+ volatile VALUE rkey = oj_calc_hash_key(pi, kval);
911
910
 
912
- if (Qundef == rkey) {
913
- if (Yes == pi->options.sym_key) {
914
- rkey = ID2SYM(rb_intern3(key, klen, oj_utf8_encoding));
915
- } else {
916
- rkey = rb_utf8_str_new(key, klen);
917
- }
918
- }
919
911
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
920
912
  VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
921
913
 
data/ext/oj/dump.c CHANGED
@@ -1010,11 +1010,33 @@ static const char digits_table[] = "\
1010
1010
  80818283848586878889\
1011
1011
  90919293949596979899";
1012
1012
 
1013
+ char *oj_longlong_to_string(long long num, bool negative, char *buf) {
1014
+ while (100 <= num) {
1015
+ unsigned idx = num % 100 * 2;
1016
+ *buf-- = digits_table[idx + 1];
1017
+ *buf-- = digits_table[idx];
1018
+ num /= 100;
1019
+ }
1020
+ if (num < 10) {
1021
+ *buf-- = num + '0';
1022
+ } else {
1023
+ *buf-- = digits_table[num * 2 + 1];
1024
+ *buf-- = digits_table[num * 2];
1025
+ }
1026
+
1027
+ if (negative) {
1028
+ *buf = '-';
1029
+ } else {
1030
+ buf++;
1031
+ }
1032
+ return buf;
1033
+ }
1034
+
1013
1035
  void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1014
1036
  char buf[32];
1015
1037
  char *b = buf + sizeof(buf) - 1;
1016
1038
  long long num = NUM2LL(obj);
1017
- int neg = 0;
1039
+ bool neg = false;
1018
1040
  size_t cnt = 0;
1019
1041
  bool dump_as_string = false;
1020
1042
 
@@ -1023,7 +1045,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1023
1045
  dump_as_string = true;
1024
1046
  }
1025
1047
  if (0 > num) {
1026
- neg = 1;
1048
+ neg = true;
1027
1049
  num = -num;
1028
1050
  }
1029
1051
  *b-- = '\0';
@@ -1032,24 +1054,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1032
1054
  *b-- = '"';
1033
1055
  }
1034
1056
  if (0 < num) {
1035
- while (100 <= num) {
1036
- unsigned idx = num % 100 * 2;
1037
- *b-- = digits_table[idx + 1];
1038
- *b-- = digits_table[idx];
1039
- num /= 100;
1040
- }
1041
- if (num < 10) {
1042
- *b-- = num + '0';
1043
- } else {
1044
- *b-- = digits_table[num * 2 + 1];
1045
- *b-- = digits_table[num * 2];
1046
- }
1047
-
1048
- if (neg) {
1049
- *b = '-';
1050
- } else {
1051
- b++;
1052
- }
1057
+ b = oj_longlong_to_string(num, neg, b);
1053
1058
  } else {
1054
1059
  *b = '0';
1055
1060
  }
data/ext/oj/dump.h CHANGED
@@ -93,10 +93,7 @@ inline static void dump_ulong(unsigned long num, Out out) {
93
93
 
94
94
  *b-- = '\0';
95
95
  if (0 < num) {
96
- for (; 0 < num; num /= 10, b--) {
97
- *b = (num % 10) + '0';
98
- }
99
- b++;
96
+ b = oj_longlong_to_string((long long)num, false, b);
100
97
  } else {
101
98
  *b = '0';
102
99
  }
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/extconf.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'mkmf'
2
4
  require 'rbconfig'
3
5
 
@@ -6,7 +8,7 @@ dir_config(extension_name)
6
8
 
7
9
  parts = RUBY_DESCRIPTION.split(' ')
8
10
  type = parts[0]
9
- type = type[4..-1] if type.start_with?('tcs-')
11
+ type = type[4..] if type.start_with?('tcs-')
10
12
  is_windows = RbConfig::CONFIG['host_os'] =~ /(mingw|mswin)/
11
13
  platform = RUBY_PLATFORM
12
14
  version = RUBY_VERSION.split('.')
@@ -56,7 +58,7 @@ dflags.each do |k, v|
56
58
  end
57
59
 
58
60
  $CPPFLAGS += ' -Wall'
59
- #puts "*** $CPPFLAGS: #{$CPPFLAGS}"
61
+ # puts "*** $CPPFLAGS: #{$CPPFLAGS}"
60
62
  # Adding the __attribute__ flag only works with gcc compilers and even then it
61
63
  # does not work to check args with varargs so just remove the check.
62
64
  CONFIG['warnflags'].slice!(/ -Wsuggest-attribute=format/)
data/ext/oj/fast.c CHANGED
@@ -40,7 +40,7 @@ typedef struct _doc {
40
40
  Leaf *where; // points to current location
41
41
  Leaf where_path[MAX_STACK]; // points to head of path
42
42
  char *json;
43
- unsigned long size; // number of leaves/branches in the doc
43
+ unsigned long size; // number of leaves/branches in the doc
44
44
  VALUE self;
45
45
  Batch batches;
46
46
  struct _batch batch0;
@@ -114,10 +114,7 @@ inline static char *ulong_fill(char *s, size_t num) {
114
114
  char *b = buf + sizeof(buf) - 1;
115
115
 
116
116
  *b-- = '\0';
117
- for (; 0 < num; num /= 10, b--) {
118
- *b = (num % 10) + '0';
119
- }
120
- b++;
117
+ b = oj_longlong_to_string((long long)num, false, b);
121
118
  if ('\0' == *b) {
122
119
  b--;
123
120
  *b = '0';
@@ -576,7 +573,7 @@ static char *read_quoted_value(ParseInfo pi) {
576
573
  char *h = pi->s; // head
577
574
  char *t = h; // tail
578
575
 
579
- h++; // skip quote character
576
+ h++; // skip quote character
580
577
  t++;
581
578
  value = h;
582
579
  for (; '"' != *h; h++, t++) {
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;
@@ -574,7 +572,6 @@ static VALUE mimic_parse_core(int argc, VALUE *argv, VALUE self, bool bang) {
574
572
  if (T_HASH != rb_type(ropts)) {
575
573
  rb_raise(rb_eArgError, "options must be a hash.");
576
574
  }
577
-
578
575
  rb_hash_foreach(ropts, parse_options_cb, (VALUE)&pi);
579
576
  v = rb_hash_lookup(ropts, oj_max_nesting_sym);
580
577
  if (Qtrue == v) {
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
 
@@ -1294,9 +1316,8 @@ static VALUE dump(int argc, VALUE *argv, VALUE self) {
1294
1316
 
1295
1317
  oj_out_init(arg.out);
1296
1318
 
1297
- arg.out->omit_nil = copts.dump_opts.omit_nil;
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
@@ -1343,7 +1365,7 @@ static VALUE to_json(int argc, VALUE *argv, VALUE self) {
1343
1365
 
1344
1366
  oj_out_init(&out);
1345
1367
 
1346
- out.omit_nil = copts.dump_opts.omit_nil;
1368
+ out.omit_nil = copts.dump_opts.omit_nil;
1347
1369
  out.omit_null_byte = copts.dump_opts.omit_null_byte;
1348
1370
  // For obj.to_json or generate nan is not allowed but if called from dump
1349
1371
  // it is.
@@ -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 {
@@ -262,12 +254,13 @@ extern VALUE oj_custom_parse_cstr(int argc, VALUE *argv, char *json, size_t len)
262
254
  extern bool oj_hash_has_key(VALUE hash, VALUE key);
263
255
  extern void oj_parse_options(VALUE ropts, Options copts);
264
256
 
265
- extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
266
- extern void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
267
- extern void oj_write_obj_to_file(VALUE obj, const char *path, Options copts);
268
- extern void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts);
269
- extern void oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out);
270
- extern void oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts);
257
+ extern void oj_dump_obj_to_json(VALUE obj, Options copts, Out out);
258
+ extern void oj_dump_obj_to_json_using_params(VALUE obj, Options copts, Out out, int argc, VALUE *argv);
259
+ extern void oj_write_obj_to_file(VALUE obj, const char *path, Options copts);
260
+ extern void oj_write_obj_to_stream(VALUE obj, VALUE stream, Options copts);
261
+ extern void oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out);
262
+ extern void oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts);
263
+ extern char *oj_longlong_to_string(long long num, bool negative, char *buf);
271
264
 
272
265
  extern void oj_str_writer_push_key(StrWriter sw, const char *key);
273
266
  extern void oj_str_writer_push_object(StrWriter sw, const char *key);
data/ext/oj/parse.c CHANGED
@@ -426,6 +426,7 @@ static void read_num(ParseInfo pi) {
426
426
  struct _numInfo ni;
427
427
  Val parent = stack_peek(&pi->stack);
428
428
 
429
+ ni.pi = pi;
429
430
  ni.str = pi->cur;
430
431
  ni.i = 0;
431
432
  ni.num = 0;
@@ -709,10 +710,7 @@ void oj_parse2(ParseInfo pi) {
709
710
  case '[': array_start(pi); break;
710
711
  case ']': array_end(pi); break;
711
712
  case ',': comma(pi); break;
712
- case '"':
713
- read_str(pi);
714
- break;
715
- // case '+':
713
+ case '"': read_str(pi); break;
716
714
  case '+':
717
715
  if (CompatMode == pi->options.mode) {
718
716
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "unexpected character");
@@ -877,7 +875,7 @@ oj_num_as_value(NumInfo ni) {
877
875
  double d = strtod(ni->str, &end);
878
876
 
879
877
  if ((long)ni->len != (long)(end - ni->str)) {
880
- rb_raise(oj_parse_error_class, "Invalid float");
878
+ rb_raise(ni->pi->err_class, "Invalid float");
881
879
  }
882
880
  rnum = rb_float_new(d);
883
881
  }
data/ext/oj/parse.h CHANGED
@@ -16,22 +16,24 @@
16
16
  #include "val_stack.h"
17
17
 
18
18
  struct _rxClass;
19
+ struct _parseInfo;
19
20
 
20
21
  typedef struct _numInfo {
21
- int64_t i;
22
- int64_t num;
23
- int64_t div;
24
- int64_t di;
25
- const char *str;
26
- size_t len;
27
- long exp;
28
- int big;
29
- int infinity;
30
- int nan;
31
- int neg;
32
- int has_exp;
33
- int no_big;
34
- int bigdec_load;
22
+ int64_t i;
23
+ int64_t num;
24
+ int64_t div;
25
+ int64_t di;
26
+ const char *str;
27
+ size_t len;
28
+ long exp;
29
+ struct _parseInfo *pi;
30
+ int big;
31
+ int infinity;
32
+ int nan;
33
+ int neg;
34
+ int has_exp;
35
+ int no_big;
36
+ int bigdec_load;
35
37
  } *NumInfo;
36
38
 
37
39
  typedef struct _parseInfo {
data/ext/oj/parser.h CHANGED
@@ -32,9 +32,9 @@ typedef struct _num {
32
32
  long double dub;
33
33
  int64_t fixnum; // holds all digits
34
34
  uint32_t len;
35
- int16_t div; // 10^div
35
+ int16_t div; // 10^div
36
36
  int16_t exp;
37
- uint8_t shift; // shift of fixnum to get decimal
37
+ uint8_t shift; // shift of fixnum to get decimal
38
38
  bool neg;
39
39
  bool exp_neg;
40
40
  // for numbers as strings, reuse buf
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;
data/ext/oj/reader.c CHANGED
@@ -101,7 +101,7 @@ int oj_reader_read(Reader reader) {
101
101
  } else {
102
102
  shift = reader->pro - reader->head - 1; // leave one character so we can backup one
103
103
  }
104
- if (0 >= shift) { /* no space left so allocate more */
104
+ if (0 >= shift) { /* no space left so allocate more */
105
105
  const char *old = reader->head;
106
106
  size_t size = reader->end - reader->head + BUF_PAD;
107
107
 
@@ -164,7 +164,6 @@ static VALUE partial_io_cb(VALUE rbuf) {
164
164
  }
165
165
  str = StringValuePtr(rstr);
166
166
  cnt = RSTRING_LEN(rstr);
167
- // printf("*** partial read %lu bytes, str: '%s'\n", cnt, str);
168
167
  strcpy(reader->tail, str);
169
168
  reader->read_end = reader->tail + cnt;
170
169
 
@@ -185,7 +184,6 @@ static VALUE io_cb(VALUE rbuf) {
185
184
  }
186
185
  str = StringValuePtr(rstr);
187
186
  cnt = RSTRING_LEN(rstr);
188
- // printf("*** read %lu bytes, str: '%s'\n", cnt, str);
189
187
  strcpy(reader->tail, str);
190
188
  reader->read_end = reader->tail + cnt;
191
189
 
data/ext/oj/saj.c CHANGED
@@ -587,7 +587,7 @@ static void saj_parse(VALUE handler, char *json) {
587
587
  if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
588
588
  pi.stack_min = (void *)((char *)&obj - (lim.rlim_cur / 4 * 3)); /* let 3/4ths of the stack be used only */
589
589
  } else {
590
- pi.stack_min = 0; /* indicates not to check stack limit */
590
+ pi.stack_min = 0; /* indicates not to check stack limit */
591
591
  }
592
592
  }
593
593
  #endif
@@ -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
  }
@@ -1,15 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/time'
2
4
 
3
5
  module Oj
4
-
5
6
  # Exists only to handle the ActiveSupport::TimeWithZone.
6
7
  class ActiveSupportHelper
7
-
8
8
  def self.createTimeWithZone(utc, zone)
9
9
  ActiveSupport::TimeWithZone.new(utc - utc.gmt_offset, ActiveSupport::TimeZone[zone])
10
10
  end
11
11
  end
12
-
13
12
  end
14
13
 
15
14
  Oj.register_odd(ActiveSupport::TimeWithZone, Oj::ActiveSupportHelper, :createTimeWithZone, :utc, 'time_zone.name')
data/lib/oj/json.rb CHANGED
@@ -1,178 +1,188 @@
1
1
  require 'ostruct'
2
2
  require 'oj/state'
3
3
 
4
- module JSON
5
- NaN = 0.0/0.0 unless defined?(::JSON::NaN)
6
- Infinity = 1.0/0.0 unless defined?(::JSON::Infinity)
7
- MinusInfinity = -1.0/0.0 unless defined?(::JSON::MinusInfinity)
8
- # Taken from the unit test. Note that items like check_circular? are not
9
- # present.
10
- PRETTY_STATE_PROTOTYPE = Ext::Generator::State.from_state({
11
- :allow_nan => false,
12
- :array_nl => "\n",
13
- :ascii_only => false,
14
- :buffer_initial_length => 1024,
15
- :depth => 0,
16
- :indent => " ",
17
- :max_nesting => 100,
18
- :object_nl => "\n",
19
- :space => " ",
20
- :space_before => "",
21
- }) unless defined?(::JSON::PRETTY_STATE_PROTOTYPE)
22
- SAFE_STATE_PROTOTYPE = Ext::Generator::State.from_state({
23
- :allow_nan => false,
24
- :array_nl => "",
25
- :ascii_only => false,
26
- :buffer_initial_length => 1024,
27
- :depth => 0,
28
- :indent => "",
29
- :max_nesting => 100,
30
- :object_nl => "",
31
- :space => "",
32
- :space_before => "",
33
- }) unless defined?(::JSON::SAFE_STATE_PROTOTYPE)
34
- FAST_STATE_PROTOTYPE = Ext::Generator::State.from_state({
35
- :allow_nan => false,
36
- :array_nl => "",
37
- :ascii_only => false,
38
- :buffer_initial_length => 1024,
39
- :depth => 0,
40
- :indent => "",
41
- :max_nesting => 0,
42
- :object_nl => "",
43
- :space => "",
44
- :space_before => "",
45
- }) unless defined?(::JSON::FAST_STATE_PROTOTYPE)
46
-
47
- def self.dump_default_options
48
- Oj::MimicDumpOption.new
49
- end
50
-
51
- def self.dump_default_options=(h)
52
- m = Oj::MimicDumpOption.new
53
- h.each do |k, v|
54
- m[k] = v
4
+ if defined?(JSON::PRETTY_STATE_PROTOTYPE)
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."
9
+ end
10
+
11
+ unless defined?(JSON::PRETTY_STATE_PROTOTYPE)
12
+ module JSON
13
+ NaN = 0.0/0.0 unless defined?(::JSON::NaN)
14
+ Infinity = 1.0/0.0 unless defined?(::JSON::Infinity)
15
+ MinusInfinity = -1.0/0.0 unless defined?(::JSON::MinusInfinity)
16
+ # Taken from the unit test. Note that items like check_circular? are not
17
+ # present.
18
+ PRETTY_STATE_PROTOTYPE = Ext::Generator::State.from_state({
19
+ :allow_nan => false,
20
+ :array_nl => "\n",
21
+ :ascii_only => false,
22
+ :buffer_initial_length => 1024,
23
+ :depth => 0,
24
+ :indent => " ",
25
+ :max_nesting => 100,
26
+ :object_nl => "\n",
27
+ :space => " ",
28
+ :space_before => "",
29
+ }) unless defined?(::JSON::PRETTY_STATE_PROTOTYPE)
30
+ SAFE_STATE_PROTOTYPE = Ext::Generator::State.from_state({
31
+ :allow_nan => false,
32
+ :array_nl => "",
33
+ :ascii_only => false,
34
+ :buffer_initial_length => 1024,
35
+ :depth => 0,
36
+ :indent => "",
37
+ :max_nesting => 100,
38
+ :object_nl => "",
39
+ :space => "",
40
+ :space_before => "",
41
+ }) unless defined?(::JSON::SAFE_STATE_PROTOTYPE)
42
+ FAST_STATE_PROTOTYPE = Ext::Generator::State.from_state({
43
+ :allow_nan => false,
44
+ :array_nl => "",
45
+ :ascii_only => false,
46
+ :buffer_initial_length => 1024,
47
+ :depth => 0,
48
+ :indent => "",
49
+ :max_nesting => 0,
50
+ :object_nl => "",
51
+ :space => "",
52
+ :space_before => "",
53
+ }) unless defined?(::JSON::FAST_STATE_PROTOTYPE)
54
+
55
+ def self.dump_default_options
56
+ Oj::MimicDumpOption.new
55
57
  end
56
- end
57
58
 
58
- def self.parser=(p)
59
- @@parser = p
60
- end
59
+ def self.dump_default_options=(h)
60
+ m = Oj::MimicDumpOption.new
61
+ h.each do |k, v|
62
+ m[k] = v
63
+ end
64
+ end
61
65
 
62
- def self.parser()
63
- @@parser
64
- end
66
+ def self.parser=(p)
67
+ @@parser = p
68
+ end
65
69
 
66
- def self.generator=(g)
67
- @@generator = g
68
- end
70
+ def self.parser()
71
+ @@parser
72
+ end
69
73
 
70
- def self.generator()
71
- @@generator
72
- end
74
+ def self.generator=(g)
75
+ @@generator = g
76
+ end
73
77
 
74
- module Ext
75
- class Parser
76
- def initialize(src)
77
- raise TypeError.new("already initialized") unless @source.nil?
78
+ def self.generator()
79
+ @@generator
80
+ end
78
81
 
79
- @source = src
80
- end
82
+ module Ext
83
+ class Parser
84
+ def initialize(src)
85
+ raise TypeError.new("already initialized") unless @source.nil?
81
86
 
82
- def source()
83
- raise TypeError.new("already initialized") if @source.nil?
87
+ @source = src
88
+ end
84
89
 
85
- @source
86
- end
87
-
88
- def parse()
89
- raise TypeError.new("already initialized") if @source.nil?
90
+ def source()
91
+ raise TypeError.new("already initialized") if @source.nil?
90
92
 
91
- JSON.parse(@source)
92
- end
93
-
94
- end # Parser
95
- end # Ext
96
-
97
- State = ::JSON::Ext::Generator::State unless defined?(::JSON::State)
98
-
99
- begin
100
- send(:remove_const, :Parser)
101
- rescue
102
- end
103
- Parser = ::JSON::Ext::Parser unless defined?(::JSON::Parser)
104
- self.parser = ::JSON::Ext::Parser
105
- self.generator = ::JSON::Ext::Generator
106
-
107
- # Taken directly from the json gem. Shamelessly copied. It is similar in
108
- # some ways to the Oj::Bag class or the Oj::EasyHash class.
109
- class GenericObject < OpenStruct
110
- class << self
111
- alias [] new
112
-
113
- def json_creatable?
114
- @json_creatable
115
- end
93
+ @source
94
+ end
116
95
 
117
- attr_writer :json_creatable
96
+ def parse()
97
+ raise TypeError.new("already initialized") if @source.nil?
118
98
 
119
- def json_create(data)
120
- data = data.dup
121
- data.delete JSON.create_id
122
- self[data]
123
- end
99
+ JSON.parse(@source)
100
+ end
124
101
 
125
- def from_hash(object)
126
- case
127
- when object.respond_to?(:to_hash)
128
- result = new
129
- object.to_hash.each do |key, value|
130
- result[key] = from_hash(value)
102
+ end # Parser
103
+ end # Ext
104
+
105
+ State = ::JSON::Ext::Generator::State unless defined?(::JSON::State)
106
+
107
+ begin
108
+ send(:remove_const, :Parser)
109
+ rescue
110
+ # ignore and move on
111
+ end
112
+ Parser = ::JSON::Ext::Parser unless defined?(::JSON::Parser)
113
+ self.parser = ::JSON::Ext::Parser
114
+ self.generator = ::JSON::Ext::Generator
115
+
116
+ # Taken directly from the json gem. Shamelessly copied. It is similar in
117
+ # some ways to the Oj::Bag class or the Oj::EasyHash class.
118
+ class GenericObject < OpenStruct
119
+ class << self
120
+ alias [] new
121
+
122
+ def json_creatable?
123
+ @json_creatable
124
+ end
125
+
126
+ attr_writer :json_creatable
127
+
128
+ def json_create(data)
129
+ data = data.dup
130
+ data.delete JSON.create_id
131
+ self[data]
132
+ end
133
+
134
+ def from_hash(object)
135
+ case
136
+ when object.respond_to?(:to_hash)
137
+ result = new
138
+ object.to_hash.each do |key, value|
139
+ result[key] = from_hash(value)
140
+ end
141
+ result
142
+ when object.respond_to?(:to_ary)
143
+ object.to_ary.map { |a| from_hash(a) }
144
+ else
145
+ object
131
146
  end
132
- result
133
- when object.respond_to?(:to_ary)
134
- object.to_ary.map { |a| from_hash(a) }
135
- else
136
- object
137
147
  end
138
- end
139
148
 
140
- def load(source, proc = nil, opts = {})
141
- result = ::JSON.load(source, proc, opts.merge(:object_class => self))
142
- result.nil? ? new : result
143
- end
149
+ def load(source, proc = nil, opts = {})
150
+ result = ::JSON.load(source, proc, opts.merge(:object_class => self))
151
+ result.nil? ? new : result
152
+ end
144
153
 
145
- def dump(obj, *args)
146
- ::JSON.dump(obj, *args)
147
- end
154
+ def dump(obj, *args)
155
+ ::JSON.dump(obj, *args)
156
+ end
148
157
 
149
- end # self
158
+ end # self
150
159
 
151
- self.json_creatable = false
160
+ self.json_creatable = false
152
161
 
153
- def to_hash
154
- table
155
- end
162
+ def to_hash
163
+ table
164
+ end
156
165
 
157
- def [](name)
158
- __send__(name)
159
- end unless method_defined?(:[])
166
+ def [](name)
167
+ __send__(name)
168
+ end unless method_defined?(:[])
160
169
 
161
- def []=(name, value)
162
- __send__("#{name}=", value)
163
- end unless method_defined?(:[]=)
170
+ def []=(name, value)
171
+ __send__("#{name}=", value)
172
+ end unless method_defined?(:[]=)
164
173
 
165
- def |(other)
166
- self.class[other.to_hash.merge(to_hash)]
167
- end
174
+ def |(other)
175
+ self.class[other.to_hash.merge(to_hash)]
176
+ end
168
177
 
169
- def as_json(*)
170
- { JSON.create_id => self.class.name }.merge to_hash
171
- end
178
+ def as_json(*)
179
+ { JSON.create_id => self.class.name }.merge to_hash
180
+ end
172
181
 
173
- def to_json(*a)
174
- as_json.to_json(*a)
182
+ def to_json(*a)
183
+ as_json.to_json(*a)
184
+ end
175
185
  end
176
- end
177
-
178
- end # JSON
186
+
187
+ end # JSON
188
+ end
data/lib/oj/mimic.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: false
2
+
1
3
  require 'bigdecimal'
2
4
  begin
3
5
  require 'ostruct'
@@ -266,7 +268,7 @@ module Oj
266
268
  end
267
269
  end
268
270
  def self.json_create(h)
269
- if usec = h.delete('u')
271
+ if (usec = h.delete('u'))
270
272
  h['n'] = usec * 1000
271
273
  end
272
274
  if instance_methods.include?(:tv_nsec)
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.0'
3
+ VERSION = '3.16.0'
4
4
  end
data/test/foo.rb CHANGED
@@ -5,11 +5,10 @@ $LOAD_PATH << '.'
5
5
  $LOAD_PATH << File.join(__dir__, '../lib')
6
6
  $LOAD_PATH << File.join(__dir__, '../ext')
7
7
 
8
+ require 'json'
8
9
  require 'oj'
9
-
10
- GC.stress = true
10
+ require 'oj/json'
11
11
 
12
12
  Oj.mimic_JSON
13
13
 
14
- Oj.add_to_json(Hash)
15
- pp JSON('{ "a": 1, "b": 2 }', :object_class => JSON::GenericObject)
14
+ JSON.parse("[]")
@@ -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/perf_dump.rb CHANGED
@@ -24,11 +24,11 @@ opts.parse(ARGV)
24
24
  @obj = {
25
25
  'a' => 'Alpha', # string
26
26
  'b' => true, # boolean
27
- 'c' => 12345, # number
28
- 'd' => [ true, [false, [-123456789, nil], 3.9676, ['Something else.', false], nil]], # mix it up array
27
+ 'c' => 12_345, # number
28
+ 'd' => [ true, [false, [-123_456_789, nil], 3.9676, ['Something else.', false], nil]], # mix it up array
29
29
  'e' => { 'zero' => nil, 'one' => 1, 'two' => 2, 'three' => [3], 'four' => [0, 1, 2, 3, 4] }, # hash
30
30
  'f' => nil, # nil
31
- 'h' => { 'a' => { 'b' => { 'c' => { 'd' => {'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
31
+ 'h' => { 'a' => { 'b' => { 'c' => { 'd' => { 'e' => { 'f' => { 'g' => nil }}}}}}}, # deep hash, not that deep
32
32
  'i' => [[[[[[[nil]]]]]]] # deep array, again, not that deep
33
33
  }
34
34
 
data/test/prec.rb CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  require 'oj'
4
4
 
5
- extras = {'locationLng' => -97.14690769100295}
5
+ extras = { 'locationLng' => -97.14690769100295 }
6
6
 
7
- Oj.default_options = {float_precision: 17}
7
+ Oj.default_options = { float_precision: 17 }
8
8
 
9
9
  encoded = Oj.dump(extras)
10
10
  puts encoded
@@ -15,8 +15,8 @@ require 'active_record'
15
15
  Oj::Rails.set_encoder()
16
16
  Oj::Rails.set_decoder()
17
17
 
18
- Oj.default_options = {float_precision: 17}
19
- # Using Oj rails encoder, gets the correct value: {'locationLng':-97.14690769100295}
18
+ Oj.default_options = { float_precision: 17 }
19
+ # Using Oj rails encoder, gets the correct value: { 'locationLng':-97.14690769100295 }
20
20
  encoded = ActiveSupport::JSON.encode(extras)
21
21
  puts encoded
22
22
  puts ActiveSupport::JSON.decode(encoded)
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
@@ -3,7 +3,7 @@
3
3
 
4
4
  $LOAD_PATH << __dir__
5
5
  @oj_dir = File.dirname(File.expand_path(__dir__))
6
- %w(lib ext).each do |dir|
6
+ %w[lib ext].each do |dir|
7
7
  $LOAD_PATH << File.join(@oj_dir, dir)
8
8
  end
9
9
 
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.0
4
+ version: 3.16.0
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-06-03 00:00:00.000000000 Z
11
+ date: 2023-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest