oj 3.16.17 → 3.17.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8cea6645d77bacff66ea8a69d5c5211aa2e9366decc1e8dec909cc83f08933af
4
- data.tar.gz: d286d46148ef014003a9a23a87cf33da532e09a6ce0228f4e5b47eff963d985f
3
+ metadata.gz: e7458dcdf494ef6b1b283ca86d51fba0b3102ecf5cec13f43682878c01708c80
4
+ data.tar.gz: ab8099b8b275aa5acab45a012bbc96ef0c860e041be2c4ae7d6f7cd331da1755
5
5
  SHA512:
6
- metadata.gz: a66845146062e764671ed0556076590ecc6507f04e21613ed5514df9e19e35c5e48b2fe9095cd7376fd82376a9b055aa8fc9e17e74c2bfaaa80ba1cebef0ddfe
7
- data.tar.gz: 8b4af7c9cc6fadf664ff643c081783921e05f793d03c512bcf178e47173fc0e620f851382cb423c99769f01fd6080ad376c663a781faa14249cfc66009a182d2
6
+ metadata.gz: 60d445fd27bbea120359c21d0c1b0ac1d4fbbd678c8fd28efe620706d382a2f0cc434645967c9bac0b6d08d1065fe99dc4b15e2623d98ca0e15a61eb591a993b
7
+ data.tar.gz: b7736fd3a7b27f98ef3e4df05464000849403ff33a3f82ecd6d8c51b40d0c2753d23ed14c078eb26506d504de991e35ebe68e5703225c80aab6ebbd28b7a80dc
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.17.3 - 2026-06-04
4
+
5
+ - Fixed issue in intern.c and fast.c.
6
+
7
+ ## 3.17.2 - 2026-05-27
8
+
9
+ - Fixed multiple issues related to extreme sizes.
10
+
11
+ ## 3.17.1 - 2026-05-15
12
+
13
+ - Fixed "quoted string not terminated" error.
14
+
15
+ ## 3.17.0 - 2026-04-19
16
+
17
+ - A "safe" parser has been added as a variation of the Oj:Parser thanks to @meinac.
18
+
3
19
  ## 3.16.17 - 2026-04-12
4
20
 
5
21
  - Rails optimize for Hash and Array now overrides `as_json` for those
data/README.md CHANGED
@@ -46,11 +46,6 @@ gem 'oj'
46
46
 
47
47
  See the Quickstart sections of the [Rails](pages/Rails.md) and [json](pages/JsonGem.md) docs.
48
48
 
49
- ## multi_json
50
-
51
- Code which uses [multi_json](https://github.com/intridea/multi_json)
52
- will automatically prefer Oj if it is installed.
53
-
54
49
  ## Support
55
50
 
56
51
  [Get supported Oj with a Tidelift Subscription.](https://tidelift.com/subscription/pkg/rubygems-oj?utm_source=rubygems-oj&utm_medium=referral&utm_campaign=readme) Security updates are [supported](https://tidelift.com/security).
@@ -83,17 +78,6 @@ See [{file:CHANGELOG.md}](CHANGELOG.md) and [{file:RELEASE_NOTES.md}](RELEASE_NO
83
78
 
84
79
  - *RubyGems* *repo*: https://rubygems.org/gems/oj
85
80
 
86
- Follow [@peterohler on Twitter](http://twitter.com/peterohler) for announcements and news about the Oj gem.
87
-
88
- #### Performance Comparisons
89
-
90
- - [Oj Strict Mode Performance](http://www.ohler.com/dev/oj_misc/performance_strict.html) compares Oj strict mode parser performance to other JSON parsers.
91
-
92
- - [Oj Compat Mode Performance](http://www.ohler.com/dev/oj_misc/performance_compat.html) compares Oj compat mode parser performance to other JSON parsers.
93
-
94
- - [Oj Object Mode Performance](http://www.ohler.com/dev/oj_misc/performance_object.html) compares Oj object mode parser performance to other marshallers.
95
-
96
- - [Oj Callback Performance](http://www.ohler.com/dev/oj_misc/performance_callback.html) compares Oj callback parser performance to other JSON parsers.
97
81
 
98
82
  #### Links of Interest
99
83
 
data/ext/oj/compat.c CHANGED
@@ -27,7 +27,7 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
27
27
  volatile VALUE rkey = oj_calc_hash_key(pi, kval);
28
28
 
29
29
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
30
- VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
30
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
31
31
 
32
32
  if (Qnil != clas) {
33
33
  rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
@@ -84,7 +84,7 @@ static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig
84
84
  volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
85
85
 
86
86
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
87
- VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
87
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
88
88
 
89
89
  if (Qnil != clas) {
90
90
  pi->stack.head->val = rb_funcall(clas, oj_json_create_id, 1, rstr);
@@ -155,7 +155,7 @@ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const c
155
155
  volatile VALUE rstr = oj_cstr_to_value(str, len, (size_t)pi->options.cache_str);
156
156
 
157
157
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
158
- VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
158
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
159
159
 
160
160
  if (Qnil != clas) {
161
161
  rb_ary_push(stack_peek(&pi->stack)->val, rb_funcall(clas, oj_json_create_id, 1, rstr));
data/ext/oj/custom.c CHANGED
@@ -915,7 +915,7 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
915
915
  volatile VALUE rkey = oj_calc_hash_key(pi, kval);
916
916
 
917
917
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
918
- VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
918
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
919
919
 
920
920
  if (Qnil != clas) {
921
921
  rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
@@ -1020,7 +1020,7 @@ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const c
1020
1020
  volatile VALUE rstr = rb_utf8_str_new(str, len);
1021
1021
 
1022
1022
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
1023
- VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
1023
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
1024
1024
 
1025
1025
  if (Qnil != clas) {
1026
1026
  rb_ary_push(stack_peek(&pi->stack)->val, rb_funcall(clas, oj_json_create_id, 1, rstr));
data/ext/oj/dump.c CHANGED
@@ -40,7 +40,7 @@ static size_t ascii_friendly_size(const uint8_t *str, size_t len);
40
40
  static const char hex_chars[17] = "0123456789abcdef";
41
41
 
42
42
  // JSON standard except newlines are no escaped
43
- static char newline_friendly_chars[256] = "\
43
+ static char newline_friendly_chars[257] = "\
44
44
  66666666221622666666666666666666\
45
45
  11211111111111111111111111111111\
46
46
  11111111111111111111111111112111\
@@ -51,7 +51,7 @@ static char newline_friendly_chars[256] = "\
51
51
  11111111111111111111111111111111";
52
52
 
53
53
  // JSON standard
54
- static char hibit_friendly_chars[256] = "\
54
+ static char hibit_friendly_chars[257] = "\
55
55
  66666666222622666666666666666666\
56
56
  11211111111111111111111111111111\
57
57
  11111111111111111111111111112111\
@@ -62,7 +62,7 @@ static char hibit_friendly_chars[256] = "\
62
62
  11111111111111111111111111111111";
63
63
 
64
64
  // JSON standard but escape forward slashes `/`
65
- static char slash_friendly_chars[256] = "\
65
+ static char slash_friendly_chars[257] = "\
66
66
  66666666222622666666666666666666\
67
67
  11211111111111121111111111111111\
68
68
  11111111111111111111111111112111\
@@ -74,7 +74,7 @@ static char slash_friendly_chars[256] = "\
74
74
 
75
75
  // High bit set characters are always encoded as unicode. Worse case is 3
76
76
  // bytes per character in the output. That makes this conservative.
77
- static char ascii_friendly_chars[256] = "\
77
+ static char ascii_friendly_chars[257] = "\
78
78
  66666666222622666666666666666666\
79
79
  11211111111111111111111111111111\
80
80
  11111111111111111111111111112111\
@@ -85,7 +85,7 @@ static char ascii_friendly_chars[256] = "\
85
85
  33333333333333333333333333333333";
86
86
 
87
87
  // XSS safe mode
88
- static char xss_friendly_chars[256] = "\
88
+ static char xss_friendly_chars[257] = "\
89
89
  66666666222622666666666666666666\
90
90
  11211161111111121111111111116161\
91
91
  11111111111111111111111111112111\
@@ -96,7 +96,7 @@ static char xss_friendly_chars[256] = "\
96
96
  33333333333333333333333333333333";
97
97
 
98
98
  // JSON XSS combo
99
- static char hixss_friendly_chars[256] = "\
99
+ static char hixss_friendly_chars[257] = "\
100
100
  66666666222622666666666666666666\
101
101
  11211111111111111111111111111111\
102
102
  11111111111111111111111111112111\
@@ -107,7 +107,7 @@ static char hixss_friendly_chars[256] = "\
107
107
  11611111111111111111111111111111";
108
108
 
109
109
  // Rails XSS combo
110
- static char rails_xss_friendly_chars[256] = "\
110
+ static char rails_xss_friendly_chars[257] = "\
111
111
  66666666222622666666666666666666\
112
112
  11211161111111111111111111116161\
113
113
  11111111111111111111111111112111\
@@ -118,7 +118,7 @@ static char rails_xss_friendly_chars[256] = "\
118
118
  11611111111111111111111111111111";
119
119
 
120
120
  // Rails HTML non-escape
121
- static char rails_friendly_chars[256] = "\
121
+ static char rails_friendly_chars[257] = "\
122
122
  66666666222622666666666666666666\
123
123
  11211111111111111111111111111111\
124
124
  11111111111111111111111111112111\
data/ext/oj/fast.c CHANGED
@@ -80,21 +80,10 @@ static void each_leaf(Doc doc, VALUE self);
80
80
  static int move_step(Doc doc, const char *path, int loc);
81
81
  static Leaf get_doc_leaf(Doc doc, const char *path);
82
82
  static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
83
- static void each_value(Doc doc, Leaf leaf);
83
+ static void each_value(Doc doc, Leaf leaf, VALUE self);
84
84
 
85
85
  VALUE oj_doc_class = Qundef;
86
86
 
87
- // This is only for CentOS 5.4 with Ruby 1.9.3-p0.
88
- #ifndef HAVE_STPCPY
89
- char *stpcpy(char *dest, const char *src) {
90
- size_t cnt = strlen(src);
91
-
92
- strcpy(dest, src);
93
-
94
- return dest + cnt;
95
- }
96
- #endif
97
-
98
87
  inline static void next_non_white(ParseInfo pi) {
99
88
  for (; 1; pi->s++) {
100
89
  switch (*pi->s) {
@@ -246,6 +235,19 @@ static void skip_comment(ParseInfo pi) {
246
235
  #define NUM_MAX (FIXNUM_MAX >> 8)
247
236
  #endif
248
237
 
238
+ static void validate_integer_size(size_t limit, char *head, char *tail) {
239
+ size_t total = (size_t)(tail - head);
240
+ bool has_sign = (head[0] == '-' || head[0] == '+');
241
+ size_t digit_count = total - (has_sign ? 1 : 0);
242
+
243
+ if (digit_count > limit) {
244
+ rb_raise(oj_parse_error_class,
245
+ "integer exceeds :max_integer_digits (%lu > %lu)",
246
+ (unsigned long)digit_count,
247
+ (unsigned long)limit);
248
+ }
249
+ }
250
+
249
251
  static void leaf_fixnum_value(Leaf leaf) {
250
252
  char *s = leaf->str;
251
253
  int64_t n = 0;
@@ -265,7 +267,12 @@ static void leaf_fixnum_value(Leaf leaf) {
265
267
  }
266
268
  }
267
269
  if (big) {
268
- char c = *s;
270
+ size_t limit = oj_default_options.max_integer_digits;
271
+ char c = *s;
272
+
273
+ if (0 < limit) {
274
+ validate_integer_size(limit, leaf->str, s);
275
+ }
269
276
 
270
277
  *s = '\0';
271
278
  leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
@@ -950,6 +957,9 @@ static void each_leaf(Doc doc, VALUE self) {
950
957
  }
951
958
  } else {
952
959
  rb_yield(self);
960
+ if (NULL == DATA_PTR(self)) {
961
+ rb_raise(rb_eIOError, "Document closed.");
962
+ }
953
963
  }
954
964
  }
955
965
 
@@ -1043,19 +1053,22 @@ static int move_step(Doc doc, const char *path, int loc) {
1043
1053
  return loc;
1044
1054
  }
1045
1055
 
1046
- static void each_value(Doc doc, Leaf leaf) {
1056
+ static void each_value(Doc doc, Leaf leaf, VALUE self) {
1047
1057
  if (COL_VAL == leaf->value_type) {
1048
1058
  if (0 != leaf->elements) {
1049
1059
  Leaf first = leaf->elements->next;
1050
1060
  Leaf e = first;
1051
1061
 
1052
1062
  do {
1053
- each_value(doc, e);
1063
+ each_value(doc, e, self);
1054
1064
  e = e->next;
1055
1065
  } while (e != first);
1056
1066
  }
1057
1067
  } else {
1058
1068
  rb_yield(leaf_value(doc, leaf));
1069
+ if (NULL == DATA_PTR(self)) {
1070
+ rb_raise(rb_eIOError, "Document closed.");
1071
+ }
1059
1072
  }
1060
1073
  }
1061
1074
 
@@ -1491,12 +1504,19 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1491
1504
  Leaf first = (*doc->where)->elements->next;
1492
1505
  Leaf e = first;
1493
1506
 
1507
+ if (MAX_STACK <= (doc->where + 1) - doc->where_path) {
1508
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
1509
+ }
1494
1510
  doc->where++;
1495
1511
  do {
1496
1512
  *doc->where = e;
1497
1513
  rb_yield(self);
1514
+ if (NULL == DATA_PTR(self)) {
1515
+ rb_raise(rb_eIOError, "Document closed.");
1516
+ }
1498
1517
  e = e->next;
1499
1518
  } while (e != first);
1519
+ doc->where--;
1500
1520
  }
1501
1521
  if (0 < wlen) {
1502
1522
  memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
@@ -1540,7 +1560,7 @@ static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
1540
1560
  path = StringValuePtr(*argv);
1541
1561
  }
1542
1562
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1543
- each_value(doc, leaf);
1563
+ each_value(doc, leaf, self);
1544
1564
  }
1545
1565
  }
1546
1566
  return Qnil;
data/ext/oj/intern.c CHANGED
@@ -69,7 +69,7 @@ static VALUE form_attr(const char *str, size_t len) {
69
69
  memcpy(b + 1, str, len);
70
70
  b[len + 1] = '\0';
71
71
  }
72
- id = rb_intern3(buf, len + 1, oj_utf8_encoding);
72
+ id = rb_intern3(b, len + 1, oj_utf8_encoding);
73
73
  OJ_R_FREE(b);
74
74
  return id;
75
75
  }
data/ext/oj/mimic_json.c CHANGED
@@ -711,6 +711,7 @@ static struct _options mimic_object_to_json_options = {0, // indent
711
711
  0, // cache_str
712
712
  0, // int_range_min
713
713
  0, // int_range_max
714
+ 0, // max_integer_digits
714
715
  oj_json_class, // create_id
715
716
  10, // create_id_len
716
717
  3, // sec_prec
data/ext/oj/oj.c CHANGED
@@ -20,6 +20,8 @@
20
20
  #include "rails.h"
21
21
  #include "simd.h"
22
22
 
23
+ #define MAX_INDENT 16
24
+
23
25
  typedef struct _yesNoOpt {
24
26
  VALUE sym;
25
27
  char *attr;
@@ -122,6 +124,7 @@ static VALUE empty_string_sym;
122
124
  static VALUE escape_mode_sym;
123
125
  static VALUE except_sym;
124
126
  static VALUE integer_range_sym;
127
+ static VALUE max_integer_digits_sym;
125
128
  static VALUE fast_sym;
126
129
  static VALUE float_prec_sym;
127
130
  static VALUE float_format_sym;
@@ -206,6 +209,7 @@ struct _options oj_default_options = {
206
209
  0, // cache_str
207
210
  0, // int_range_min
208
211
  0, // int_range_max
212
+ 0, // max_integer_digits
209
213
  oj_json_class, // create_id
210
214
  10, // create_id_len
211
215
  9, // sec_prec
@@ -334,6 +338,11 @@ static VALUE only_array_from_string(const char *str) {
334
338
  * - *:cache_str* [_Fixnum_] maximum string value length to cache (strings less
335
339
  * than this are cached)
336
340
  * - *:integer_range* [_Range_] Dump integers outside range as strings.
341
+ * - *:max_integer_digits* [_Fixnum_] Maximum number of decimal digits allowed in a
342
+ * parsed integer. When the limit is exceeded a parse error is raised. 0 (the
343
+ * default) disables the limit. Setting a reasonable limit is recommended when
344
+ * parsing untrusted input to mitigate CPU-DoS attacks. Only applies to the
345
+ * legacy parsers (Oj.load, Oj::Doc, JSON.parse mimic); Oj::Parser is unaffected.
337
346
  * - *:trace* [_true,_|_false_] Trace all load and dump calls, default is false
338
347
  * (trace is off)
339
348
  * - *:safe* [_true,_|_false_] Safe mimic breaks JSON mimic to be safer, default
@@ -448,6 +457,7 @@ static VALUE get_def_opts(VALUE self) {
448
457
  } else {
449
458
  rb_hash_aset(opts, integer_range_sym, Qnil);
450
459
  }
460
+ rb_hash_aset(opts, max_integer_digits_sym, LONG2NUM((long)oj_default_options.max_integer_digits));
451
461
  switch (oj_default_options.escape_mode) {
452
462
  case NLEsc: rb_hash_aset(opts, escape_mode_sym, newline_sym); break;
453
463
  case JSONEsc: rb_hash_aset(opts, escape_mode_sym, json_sym); break;
@@ -591,6 +601,8 @@ static VALUE get_def_opts(VALUE self) {
591
601
  * - *:cache_keys* [_Boolean_] if true then hash keys are cached
592
602
  * - *:cache_str* [_Fixnum_] maximum string value length to cache (strings less than this are cached)
593
603
  * - *:integer_range* [_Range_] Dump integers outside range as strings.
604
+ * - *:max_integer_digits* [_Fixnum_] Maximum decimal digits in a parsed integer
605
+ * (0 = unlimited). Use to mitigate CPU-DoS via huge integer values in JSON.
594
606
  * - *:trace* [_Boolean_] turn trace on or off.
595
607
  * - *:safe* [_Boolean_] turn safe mimic on or off.
596
608
  */
@@ -756,7 +768,10 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
756
768
  case T_FIXNUM:
757
769
  copts->dump_opts.indent_size = 0;
758
770
  *copts->dump_opts.indent_str = '\0';
759
- copts->indent = FIX2INT(v);
771
+ if (MAX_INDENT < FIX2INT(v)) {
772
+ rb_raise(rb_eArgError, "indent is limited to %d characters.", MAX_INDENT);
773
+ }
774
+ copts->indent = FIX2INT(v);
760
775
  break;
761
776
  case T_STRING:
762
777
  if (sizeof(copts->dump_opts.indent_str) <= (len = RSTRING_LEN(v))) {
@@ -1071,6 +1086,20 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
1071
1086
  } else if (Qfalse != v) {
1072
1087
  rb_raise(rb_eArgError, ":integer_range must be a range of Fixnum.");
1073
1088
  }
1089
+ } else if (max_integer_digits_sym == k) {
1090
+ if (Qnil == v || Qfalse == v) {
1091
+ copts->max_integer_digits = 0;
1092
+ } else if (T_FIXNUM == rb_type(v)) {
1093
+ long n = FIX2LONG(v);
1094
+
1095
+ if (n < 0) {
1096
+ rb_raise(rb_eArgError, ":max_integer_digits must be >= 0.");
1097
+ }
1098
+
1099
+ copts->max_integer_digits = (size_t)n;
1100
+ } else {
1101
+ rb_raise(rb_eArgError, ":max_integer_digits must be a non-negative Integer.");
1102
+ }
1074
1103
  } else if (symbol_keys_sym == k || oj_symbolize_names_sym == k) {
1075
1104
  if (Qnil == v) {
1076
1105
  return ST_CONTINUE;
@@ -2153,6 +2182,8 @@ void Init_oj(void) {
2153
2182
  rb_gc_register_address(&escape_mode_sym);
2154
2183
  integer_range_sym = ID2SYM(rb_intern("integer_range"));
2155
2184
  rb_gc_register_address(&integer_range_sym);
2185
+ max_integer_digits_sym = ID2SYM(rb_intern("max_integer_digits"));
2186
+ rb_gc_register_address(&max_integer_digits_sym);
2156
2187
  fast_sym = ID2SYM(rb_intern("fast"));
2157
2188
  rb_gc_register_address(&fast_sym);
2158
2189
  float_format_sym = ID2SYM(rb_intern("float_format"));
data/ext/oj/oj.h CHANGED
@@ -124,43 +124,44 @@ typedef struct _dumpOpts {
124
124
  } *DumpOpts;
125
125
 
126
126
  typedef struct _options {
127
- int indent; // indention for dump, default 2
128
- char circular; // YesNo
129
- char auto_define; // YesNo
130
- char sym_key; // YesNo
131
- char escape_mode; // Escape_Mode
132
- char mode; // Mode
133
- char class_cache; // YesNo
134
- char time_format; // TimeFormat
135
- char bigdec_as_num; // YesNo
136
- char bigdec_load; // BigLoad
137
- char compat_bigdec; // boolean (0 or 1)
138
- char to_hash; // YesNo
139
- char to_json; // YesNo
140
- char as_json; // YesNo
141
- char raw_json; // YesNo
142
- char nilnil; // YesNo
143
- char empty_string; // YesNo
144
- char allow_gc; // allow GC during parse
145
- char quirks_mode; // allow single JSON values instead of documents
146
- char allow_invalid; // YesNo - allow invalid unicode
147
- char create_ok; // YesNo allow create_id
148
- char allow_nan; // YesNo for parsing only
149
- char trace; // YesNo
150
- char safe; // YesNo
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
- char cache_keys; // YesNo
154
- char cache_str; // string short than or equal to this are cache
155
- int64_t int_range_min; // dump numbers below as string
156
- int64_t int_range_max; // dump numbers above as string
157
- const char *create_id; // 0 or string
158
- size_t create_id_len; // length of create_id
159
- int sec_prec; // second precision when dumping time
160
- char float_prec; // float precision, linked to float_fmt
161
- char float_fmt[7]; // float format for dumping, if empty use Ruby
162
- VALUE hash_class; // class to use in place of Hash on load
163
- VALUE array_class; // class to use in place of Array on load
127
+ int indent; // indention for dump, default 2
128
+ char circular; // YesNo
129
+ char auto_define; // YesNo
130
+ char sym_key; // YesNo
131
+ char escape_mode; // Escape_Mode
132
+ char mode; // Mode
133
+ char class_cache; // YesNo
134
+ char time_format; // TimeFormat
135
+ char bigdec_as_num; // YesNo
136
+ char bigdec_load; // BigLoad
137
+ char compat_bigdec; // boolean (0 or 1)
138
+ char to_hash; // YesNo
139
+ char to_json; // YesNo
140
+ char as_json; // YesNo
141
+ char raw_json; // YesNo
142
+ char nilnil; // YesNo
143
+ char empty_string; // YesNo
144
+ char allow_gc; // allow GC during parse
145
+ char quirks_mode; // allow single JSON values instead of documents
146
+ char allow_invalid; // YesNo - allow invalid unicode
147
+ char create_ok; // YesNo allow create_id
148
+ char allow_nan; // YesNo for parsing only
149
+ char trace; // YesNo
150
+ char safe; // YesNo
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
+ char cache_keys; // YesNo
154
+ char cache_str; // string short than or equal to this are cache
155
+ int64_t int_range_min; // dump numbers below as string
156
+ int64_t int_range_max; // dump numbers above as string
157
+ size_t max_integer_digits; // 0 = unlimited; max decimal digits for parsed integers
158
+ const char *create_id; // 0 or string
159
+ size_t create_id_len; // length of create_id
160
+ int sec_prec; // second precision when dumping time
161
+ char float_prec; // float precision, linked to float_fmt
162
+ char float_fmt[7]; // float format for dumping, if empty use Ruby
163
+ VALUE hash_class; // class to use in place of Hash on load
164
+ VALUE array_class; // class to use in place of Array on load
164
165
  struct _dumpOpts dump_opts;
165
166
  struct _rxClass str_rx;
166
167
  VALUE *ignore; // Qnil terminated array of classes or NULL
data/ext/oj/parse.c CHANGED
@@ -394,7 +394,7 @@ void oj_scanner_init(void) {
394
394
  static void read_escaped_str(ParseInfo pi, const char *start) {
395
395
  struct _buf buf;
396
396
  const char *s;
397
- int cnt = (int)(pi->cur - start);
397
+ size_t cnt = pi->cur - start;
398
398
  uint32_t code;
399
399
  Val parent = stack_peek(&pi->stack);
400
400
 
@@ -669,7 +669,7 @@ static void read_num(ParseInfo pi) {
669
669
  // A trailing . is not a valid decimal but if encountered allow it
670
670
  // except when mimicking the JSON gem or in strict mode.
671
671
  if (StrictMode == pi->options.mode || CompatMode == pi->options.mode) {
672
- int pos = (int)(pi->cur - ni.str);
672
+ size_t pos = pi->cur - ni.str;
673
673
 
674
674
  if (1 == pos || (2 == pos && ni.neg)) {
675
675
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number");
@@ -971,6 +971,20 @@ static long double exp_plus[] = {
971
971
  1.0e39, 1.0e40, 1.0e41, 1.0e42, 1.0e43, 1.0e44, 1.0e45, 1.0e46, 1.0e47, 1.0e48, 1.0e49,
972
972
  };
973
973
 
974
+ static void validate_integer_size(size_t limit, NumInfo ni) {
975
+ size_t digit_count = ni->len - (ni->neg ? 1 : 0);
976
+
977
+ if (digit_count > limit) {
978
+ oj_set_error_at(ni->pi,
979
+ (Qnil != ni->pi->err_class) ? ni->pi->err_class : oj_parse_error_class,
980
+ __FILE__,
981
+ __LINE__,
982
+ "integer exceeds :max_integer_digits (%lu > %lu)",
983
+ (unsigned long)digit_count,
984
+ (unsigned long)limit);
985
+ }
986
+ }
987
+
974
988
  VALUE
975
989
  oj_num_as_value(NumInfo ni) {
976
990
  VALUE rnum = Qnil;
@@ -984,6 +998,12 @@ oj_num_as_value(NumInfo ni) {
984
998
  } else if (ni->nan) {
985
999
  rnum = rb_float_new(0.0 / 0.0);
986
1000
  } else if (1 == ni->div && 0 == ni->exp && !ni->has_exp) { // fixnum
1001
+ size_t limit = (NULL != ni->pi) ? ni->pi->options.max_integer_digits : 0;
1002
+
1003
+ if (0 < limit) {
1004
+ validate_integer_size(limit, ni);
1005
+ }
1006
+
987
1007
  if (ni->big) {
988
1008
  if (256 > ni->len) {
989
1009
  char buf[256];