oj 3.16.16 → 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: 07c7a6ebc6baf4f02b93760ed3d7440cdc9b2af85d0976e525f34978ab823c45
4
- data.tar.gz: ed5a5a2e16f8df836a098de0da60657688049cfcf820d3142b4122302ae4e946
3
+ metadata.gz: e7458dcdf494ef6b1b283ca86d51fba0b3102ecf5cec13f43682878c01708c80
4
+ data.tar.gz: ab8099b8b275aa5acab45a012bbc96ef0c860e041be2c4ae7d6f7cd331da1755
5
5
  SHA512:
6
- metadata.gz: d8f5c73fbc30a2985c2352e466f8ef9762f831d15b9c0a8b9394d66a5613910fd5aca4c4bdc49c40230128d791244ad67f6f9852c4778e89828a4358bb997c11
7
- data.tar.gz: dbc5fe50d1ad89cb23918c950ac849052a95defb7e5509d7653236eb3569c811685e2c4c6546a8ab38c5a346aad0ccdd9770c855bc53f96c0c5312c7d61fab3e
6
+ metadata.gz: 60d445fd27bbea120359c21d0c1b0ac1d4fbbd678c8fd28efe620706d382a2f0cc434645967c9bac0b6d08d1065fe99dc4b15e2623d98ca0e15a61eb591a993b
7
+ data.tar.gz: b7736fd3a7b27f98ef3e4df05464000849403ff33a3f82ecd6d8c51b40d0c2753d23ed14c078eb26506d504de991e35ebe68e5703225c80aab6ebbd28b7a80dc
data/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
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
+
19
+ ## 3.16.17 - 2026-04-12
20
+
21
+ - Rails optimize for Hash and Array now overrides `as_json` for those
22
+ classes. Note that when either is optimized with `Oj.optimize_rails`
23
+ the `Array.as_json` and `Hash.as_json` will not be called.
24
+
25
+ - Add support for the rails encoder `:only` and `:except` options.
26
+
27
+ - Handle unterminated strings in usual parser (#1002)
28
+
29
+ - Fix read() not handling partial reads for large files (#1004)
30
+
31
+ - Raise error for incomplete primitive literals (#1005)
32
+
3
33
  ## 3.16.16 - 2026-03-13
4
34
 
5
35
  - Not closed arrays and objects are reported corrected in the usual parser.
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
@@ -287,6 +287,11 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
287
287
  if (out->omit_nil && Qnil == value) {
288
288
  return ST_CONTINUE;
289
289
  }
290
+ if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
291
+ if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
292
+ return ST_CONTINUE;
293
+ }
294
+ }
290
295
  if (!out->opts->dump_opts.use) {
291
296
  assure_size(out, depth * out->indent + 1);
292
297
  fill_indent(out, depth);
@@ -910,7 +915,7 @@ static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, c
910
915
  volatile VALUE rkey = oj_calc_hash_key(pi, kval);
911
916
 
912
917
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
913
- VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
918
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
914
919
 
915
920
  if (Qnil != clas) {
916
921
  rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
@@ -1015,7 +1020,7 @@ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const c
1015
1020
  volatile VALUE rstr = rb_utf8_str_new(str, len);
1016
1021
 
1017
1022
  if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
1018
- VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, (int)len);
1023
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
1019
1024
 
1020
1025
  if (Qnil != clas) {
1021
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\
@@ -257,7 +257,30 @@ inline static size_t hibit_friendly_size(const uint8_t *str, size_t len) {
257
257
  size += tmp;
258
258
  }
259
259
 
260
+ #ifdef HAVE_FAST_MEMCPY
261
+ size_t total = 0;
262
+ if (len - i > 4) {
263
+ size += (len - i);
264
+ unsigned char buf[sizeof(uint8x16_t)];
265
+ memset(buf, ' ', sizeof(buf));
266
+ fast_memcpy16(buf, str, len - i);
267
+
268
+ uint8x16_t chunk = vld1q_u8((const unsigned char *)buf);
269
+ uint8x16_t tmp1 = vqtbl4q_u8(hibit_friendly_chars_neon[0], chunk);
270
+ uint8x16_t tmp2 = vqtbl4q_u8(hibit_friendly_chars_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
271
+ uint8x16_t result = vorrq_u8(tmp1, tmp2);
272
+ uint8_t tmp = vaddvq_u8(result);
273
+
274
+ size += tmp;
275
+
276
+ total = size;
277
+ } else {
278
+ total = size + calculate_string_size(str, len - i, hibit_friendly_chars);
279
+ }
280
+ #else
260
281
  size_t total = size + calculate_string_size(str, len - i, hibit_friendly_chars);
282
+ #endif
283
+
261
284
  return total;
262
285
  #elif defined(HAVE_SIMD_SSE4_2)
263
286
  if (SIMD_Impl == SIMD_SSE42) {
@@ -967,11 +990,12 @@ typedef struct _neon_match_result {
967
990
  uint8x16_t needs_escape;
968
991
  bool has_some_hibit;
969
992
  bool do_unicode_validation;
993
+ uint64_t escape_mask;
970
994
  } neon_match_result;
971
995
 
972
996
  static inline FORCE_INLINE neon_match_result
973
997
  neon_update(const char *str, uint8x16x4_t *cmap_neon, int neon_table_size, bool do_unicode_validation, bool has_hi) {
974
- neon_match_result result = {.has_some_hibit = false, .do_unicode_validation = false};
998
+ neon_match_result result = {.has_some_hibit = false, .do_unicode_validation = false, .escape_mask = 0};
975
999
 
976
1000
  uint8x16_t chunk = vld1q_u8((const unsigned char *)str);
977
1001
  uint8x16_t tmp1 = vqtbl4q_u8(cmap_neon[0], chunk);
@@ -987,6 +1011,9 @@ neon_update(const char *str, uint8x16x4_t *cmap_neon, int neon_table_size, bool
987
1011
  result.has_some_hibit = vmaxvq_u8(has_some_hibit) != 0;
988
1012
  result.do_unicode_validation = has_hi && do_unicode_validation && result.has_some_hibit;
989
1013
  }
1014
+ const uint8x8_t res = vshrn_n_u16(vreinterpretq_u16_u8(vmvnq_u8(vceqq_u8(result.needs_escape, vdupq_n_u8(0)))), 4);
1015
+ const uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(res), 0);
1016
+ result.escape_mask = mask & 0x8888888888888888ull;
990
1017
  return result;
991
1018
  }
992
1019
 
@@ -1189,7 +1216,16 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
1189
1216
  if (is_sym) {
1190
1217
  *out->cur++ = ':';
1191
1218
  }
1219
+ #ifdef HAVE_FAST_MEMCPY
1220
+ if (cnt <= 16) {
1221
+ fast_memcpy16(out->cur, str, cnt);
1222
+ out->cur += size;
1223
+ } else {
1224
+ APPEND_CHARS(out->cur, str, cnt);
1225
+ }
1226
+ #else
1192
1227
  APPEND_CHARS(out->cur, str, cnt);
1228
+ #endif
1193
1229
  *out->cur++ = '"';
1194
1230
  } else {
1195
1231
  const char *end = str + cnt;
@@ -1223,6 +1259,17 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
1223
1259
  #endif
1224
1260
 
1225
1261
  #ifdef HAVE_SIMD_NEON
1262
+
1263
+ #ifdef HAVE_FAST_MEMCPY
1264
+ #define APPEND_CHARS_SMALL(dst, src, length) \
1265
+ fast_memcpy16((dst), (src), (length)); \
1266
+ (dst) += (length);
1267
+ #define MEMCPY16 fast_memcpy16
1268
+ #else
1269
+ #define APPEND_CHARS_SMALL(dst, src, length) APPEND_CHARS(dst, str, length);
1270
+ #define MEMCPY16 memcpy
1271
+ #endif
1272
+
1226
1273
  if (use_simd) {
1227
1274
  while (str < end) {
1228
1275
  const char *chunk_ptr = NULL;
@@ -1231,8 +1278,8 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
1231
1278
  chunk_start = str;
1232
1279
  chunk_end = str + sizeof(uint8x16_t);
1233
1280
  } else if ((end - str) >= SIMD_MINIMUM_THRESHOLD) {
1234
- memset(out->cur, 'A', sizeof(uint8x16_t));
1235
- memcpy(out->cur, str, (end - str));
1281
+ memset(out->cur, ' ', sizeof(uint8x16_t));
1282
+ MEMCPY16(out->cur, str, (end - str));
1236
1283
  chunk_ptr = out->cur;
1237
1284
  chunk_start = str;
1238
1285
  chunk_end = end;
@@ -1244,32 +1291,22 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
1244
1291
  neon_table_size,
1245
1292
  do_unicode_validation,
1246
1293
  has_hi);
1247
- if ((result.do_unicode_validation) || vmaxvq_u8(result.needs_escape) != 0) {
1294
+ if ((result.do_unicode_validation) || result.escape_mask != 0) {
1248
1295
  SEARCH_FLUSH;
1249
- uint8x16_t actions = vaddq_u8(result.needs_escape, vdupq_n_u8('1'));
1250
- uint8_t num_matches = vaddvq_u8(vandq_u8(result.needs_escape, vdupq_n_u8(0x1)));
1296
+ bool process_each = result.do_unicode_validation;
1297
+ uint8x16_t actions = vaddq_u8(result.needs_escape, vdupq_n_u8('1'));
1251
1298
  vst1q_u8((unsigned char *)matches, actions);
1252
- bool process_each = result.do_unicode_validation || (num_matches > sizeof(uint8x16_t) / 2);
1253
1299
  // If no byte in this chunk had the high bit set then we can skip
1254
1300
  // all of the '1' bytes by directly copying them to the output.
1255
1301
  if (!process_each) {
1256
- while (str < chunk_end) {
1257
- long i = str - chunk_start;
1258
- char action;
1259
- while (str < chunk_end && (action = matches[i++]) == '1') {
1260
- *out->cur++ = *str++;
1302
+ while (result.escape_mask) {
1303
+ int esc_pos = OJ_CTZ64(result.escape_mask) >> 2;
1304
+ long run_len = esc_pos - (str - chunk_start);
1305
+ if (run_len > 0) {
1306
+ APPEND_CHARS_SMALL(out->cur, str, run_len);
1307
+ str += run_len;
1261
1308
  }
1262
- cursor = str;
1263
- if (str >= chunk_end) {
1264
- break;
1265
- }
1266
- str = process_character(action, str, end, out, orig, do_unicode_validation, &check_start);
1267
- str++;
1268
- }
1269
- } else {
1270
- while (str < chunk_end) {
1271
- long match_index = str - chunk_start;
1272
- str = process_character(matches[match_index],
1309
+ str = process_character(matches[esc_pos],
1273
1310
  str,
1274
1311
  end,
1275
1312
  out,
@@ -1277,6 +1314,23 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
1277
1314
  do_unicode_validation,
1278
1315
  &check_start);
1279
1316
  str++;
1317
+ result.escape_mask &= result.escape_mask - 1;
1318
+ }
1319
+ if (str < chunk_end) {
1320
+ APPEND_CHARS_SMALL(out->cur, str, chunk_end - str);
1321
+ str = chunk_end;
1322
+ }
1323
+ } else {
1324
+ while (str < chunk_end) {
1325
+ long match_index = str - chunk_start;
1326
+ str = process_character(matches[match_index],
1327
+ str,
1328
+ end,
1329
+ out,
1330
+ orig,
1331
+ do_unicode_validation,
1332
+ &check_start);
1333
+ str++;
1280
1334
  }
1281
1335
  }
1282
1336
  cursor = str;
@@ -1317,12 +1371,12 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
1317
1371
  while (str < chunk_end) {
1318
1372
  long match_index = str - chunk_start;
1319
1373
  str = process_character(matches[match_index],
1320
- str,
1321
- end,
1322
- out,
1323
- orig,
1324
- do_unicode_validation,
1325
- &check_start);
1374
+ str,
1375
+ end,
1376
+ out,
1377
+ orig,
1378
+ do_unicode_validation,
1379
+ &check_start);
1326
1380
  str++;
1327
1381
  }
1328
1382
  cursor = str;
@@ -1687,3 +1741,25 @@ size_t oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const c
1687
1741
  }
1688
1742
  return cnt;
1689
1743
  }
1744
+
1745
+ bool oj_key_skip(VALUE key, const char *only, const char *except) {
1746
+ const char *skey;
1747
+
1748
+ switch (rb_type(key)) {
1749
+ case RUBY_T_STRING: skey = StringValueCStr(key); break;
1750
+ case RUBY_T_SYMBOL: skey = rb_id2name(rb_sym2id(key)); break;
1751
+ default: skey = NULL; break;
1752
+ }
1753
+ if (NULL != skey && '\0' != *skey) {
1754
+ size_t size = strlen(skey);
1755
+ char *buf = alloca(size + 3);
1756
+
1757
+ buf[0] = ':';
1758
+ strcpy(buf + 1, skey);
1759
+ buf[size + 1] = ':';
1760
+ buf[size + 2] = '\0';
1761
+
1762
+ return ((NULL != only && NULL == strstr(only, buf)) || (NULL != except && NULL != strstr(except, buf)));
1763
+ }
1764
+ return NULL != only;
1765
+ }
data/ext/oj/dump.h CHANGED
@@ -62,6 +62,8 @@ extern size_t oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d,
62
62
 
63
63
  extern time_t oj_sec_from_time_hard_way(VALUE obj);
64
64
 
65
+ extern bool oj_key_skip(VALUE key, const char *only, const char *except);
66
+
65
67
  inline static void assure_size(Out out, size_t len) {
66
68
  if (out->end - out->cur <= (long)len) {
67
69
  oj_grow_out(out, len);
data/ext/oj/dump_compat.c CHANGED
@@ -605,6 +605,11 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
605
605
  if (out->omit_nil && Qnil == value) {
606
606
  return ST_CONTINUE;
607
607
  }
608
+ if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
609
+ if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
610
+ return ST_CONTINUE;
611
+ }
612
+ }
608
613
  if (!out->opts->dump_opts.use) {
609
614
  assure_size(out, depth * out->indent + 1);
610
615
  fill_indent(out, depth);
data/ext/oj/dump_object.c CHANGED
@@ -210,6 +210,11 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
210
210
  if (out->omit_nil && Qnil == value) {
211
211
  return ST_CONTINUE;
212
212
  }
213
+ if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
214
+ if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
215
+ return ST_CONTINUE;
216
+ }
217
+ }
213
218
  assure_size(out, size);
214
219
  fill_indent(out, depth);
215
220
  switch (rb_type(key)) {
data/ext/oj/dump_strict.c CHANGED
@@ -193,14 +193,19 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
193
193
  if (out->omit_nil && Qnil == value) {
194
194
  return ST_CONTINUE;
195
195
  }
196
+ if (NULL != out->opts->dump_opts.only || NULL != out->opts->dump_opts.except) {
197
+ if (oj_key_skip(key, out->opts->dump_opts.only, out->opts->dump_opts.except)) {
198
+ return ST_CONTINUE;
199
+ }
200
+ }
196
201
  if (!out->opts->dump_opts.use) {
197
202
  size = depth * out->indent + 1;
198
203
  assure_size(out, size);
199
204
  fill_indent(out, depth);
200
- if (rtype == T_STRING) {
201
- oj_dump_str(key, 0, out, false);
202
- } else {
203
- oj_dump_sym(key, 0, out, false);
205
+ switch (rtype) {
206
+ case T_STRING: oj_dump_str(key, 0, out, false); break;
207
+ case T_SYMBOL: oj_dump_sym(key, 0, out, false); break;
208
+ default: oj_dump_str(oj_safe_string_convert(key), 0, out, false); break;
204
209
  }
205
210
  *out->cur++ = ':';
206
211
  } else {
@@ -215,10 +220,10 @@ static int hash_cb(VALUE key, VALUE value, VALUE ov) {
215
220
  APPEND_CHARS(out->cur, out->opts->dump_opts.indent_str, out->opts->dump_opts.indent_size);
216
221
  }
217
222
  }
218
- if (rtype == T_STRING) {
219
- oj_dump_str(key, 0, out, false);
220
- } else {
221
- oj_dump_sym(key, 0, out, false);
223
+ switch (rtype) {
224
+ case T_STRING: oj_dump_str(key, 0, out, false); break;
225
+ case T_SYMBOL: oj_dump_sym(key, 0, out, false); break;
226
+ default: oj_dump_str(oj_safe_string_convert(key), 0, out, false); break;
222
227
  }
223
228
  size = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
224
229
  assure_size(out, size);
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
@@ -735,6 +736,8 @@ static struct _options mimic_object_to_json_options = {0, // indent
735
736
  false, // omit_nil
736
737
  false, // omit_null_byte
737
738
  100, // max_depth
739
+ NULL, // only
740
+ NULL, // except
738
741
  },
739
742
  {
740
743
  // str_rx