oj 3.16.6 → 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.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +81 -0
  3. data/README.md +0 -16
  4. data/ext/oj/compat.c +3 -3
  5. data/ext/oj/custom.c +17 -11
  6. data/ext/oj/dump.c +592 -81
  7. data/ext/oj/dump.h +9 -2
  8. data/ext/oj/dump_compat.c +15 -9
  9. data/ext/oj/dump_leaf.c +1 -1
  10. data/ext/oj/dump_object.c +32 -17
  11. data/ext/oj/dump_strict.c +20 -14
  12. data/ext/oj/extconf.rb +10 -7
  13. data/ext/oj/fast.c +40 -23
  14. data/ext/oj/intern.c +1 -1
  15. data/ext/oj/mimic_json.c +11 -7
  16. data/ext/oj/object.c +8 -8
  17. data/ext/oj/oj.c +256 -10
  18. data/ext/oj/oj.h +55 -52
  19. data/ext/oj/parse.c +243 -46
  20. data/ext/oj/parser.c +113 -37
  21. data/ext/oj/parser.h +2 -0
  22. data/ext/oj/rails.c +41 -36
  23. data/ext/oj/rxclass.c +1 -1
  24. data/ext/oj/rxclass.h +1 -1
  25. data/ext/oj/safe.c +230 -0
  26. data/ext/oj/safe.h +79 -0
  27. data/ext/oj/saj.c +13 -8
  28. data/ext/oj/scp.c +3 -6
  29. data/ext/oj/simd.h +219 -0
  30. data/ext/oj/sparse.c +6 -3
  31. data/ext/oj/stream_writer.c +3 -13
  32. data/ext/oj/strict.c +2 -4
  33. data/ext/oj/string_writer.c +5 -7
  34. data/ext/oj/usual.c +20 -7
  35. data/ext/oj/wab.c +5 -4
  36. data/lib/oj/version.rb +1 -1
  37. data/pages/Encoding.md +1 -1
  38. metadata +22 -114
  39. data/test/_test_active.rb +0 -75
  40. data/test/_test_active_mimic.rb +0 -95
  41. data/test/_test_mimic_rails.rb +0 -123
  42. data/test/activerecord/result_test.rb +0 -31
  43. data/test/activesupport6/abstract_unit.rb +0 -44
  44. data/test/activesupport6/decoding_test.rb +0 -133
  45. data/test/activesupport6/encoding_test.rb +0 -542
  46. data/test/activesupport6/encoding_test_cases.rb +0 -98
  47. data/test/activesupport6/test_common.rb +0 -17
  48. data/test/activesupport6/test_helper.rb +0 -163
  49. data/test/activesupport6/time_zone_test_helpers.rb +0 -39
  50. data/test/activesupport7/abstract_unit.rb +0 -52
  51. data/test/activesupport7/decoding_test.rb +0 -125
  52. data/test/activesupport7/encoding_test.rb +0 -536
  53. data/test/activesupport7/encoding_test_cases.rb +0 -104
  54. data/test/activesupport7/time_zone_test_helpers.rb +0 -47
  55. data/test/files.rb +0 -29
  56. data/test/foo.rb +0 -26
  57. data/test/helper.rb +0 -39
  58. data/test/isolated/shared.rb +0 -309
  59. data/test/isolated/test_mimic_after.rb +0 -13
  60. data/test/isolated/test_mimic_alone.rb +0 -12
  61. data/test/isolated/test_mimic_as_json.rb +0 -45
  62. data/test/isolated/test_mimic_before.rb +0 -13
  63. data/test/isolated/test_mimic_define.rb +0 -28
  64. data/test/isolated/test_mimic_rails_after.rb +0 -22
  65. data/test/isolated/test_mimic_rails_before.rb +0 -21
  66. data/test/isolated/test_mimic_redefine.rb +0 -15
  67. data/test/json_gem/json_addition_test.rb +0 -216
  68. data/test/json_gem/json_common_interface_test.rb +0 -155
  69. data/test/json_gem/json_encoding_test.rb +0 -107
  70. data/test/json_gem/json_ext_parser_test.rb +0 -21
  71. data/test/json_gem/json_fixtures_test.rb +0 -36
  72. data/test/json_gem/json_generator_test.rb +0 -413
  73. data/test/json_gem/json_generic_object_test.rb +0 -90
  74. data/test/json_gem/json_parser_test.rb +0 -477
  75. data/test/json_gem/json_string_matching_test.rb +0 -42
  76. data/test/json_gem/test_helper.rb +0 -30
  77. data/test/mem.rb +0 -34
  78. data/test/perf.rb +0 -102
  79. data/test/perf_compat.rb +0 -128
  80. data/test/perf_dump.rb +0 -50
  81. data/test/perf_fast.rb +0 -162
  82. data/test/perf_file.rb +0 -62
  83. data/test/perf_object.rb +0 -134
  84. data/test/perf_once.rb +0 -59
  85. data/test/perf_parser.rb +0 -183
  86. data/test/perf_saj.rb +0 -101
  87. data/test/perf_scp.rb +0 -140
  88. data/test/perf_simple.rb +0 -289
  89. data/test/perf_strict.rb +0 -137
  90. data/test/perf_wab.rb +0 -129
  91. data/test/prec.rb +0 -23
  92. data/test/sample/change.rb +0 -13
  93. data/test/sample/dir.rb +0 -18
  94. data/test/sample/doc.rb +0 -35
  95. data/test/sample/file.rb +0 -47
  96. data/test/sample/group.rb +0 -15
  97. data/test/sample/hasprops.rb +0 -15
  98. data/test/sample/layer.rb +0 -11
  99. data/test/sample/line.rb +0 -20
  100. data/test/sample/oval.rb +0 -10
  101. data/test/sample/rect.rb +0 -9
  102. data/test/sample/shape.rb +0 -34
  103. data/test/sample/text.rb +0 -19
  104. data/test/sample.rb +0 -54
  105. data/test/sample_json.rb +0 -37
  106. data/test/test_compat.rb +0 -567
  107. data/test/test_custom.rb +0 -555
  108. data/test/test_debian.rb +0 -50
  109. data/test/test_fast.rb +0 -526
  110. data/test/test_file.rb +0 -250
  111. data/test/test_gc.rb +0 -60
  112. data/test/test_generate.rb +0 -21
  113. data/test/test_hash.rb +0 -39
  114. data/test/test_integer_range.rb +0 -72
  115. data/test/test_null.rb +0 -376
  116. data/test/test_object.rb +0 -1030
  117. data/test/test_parser.rb +0 -11
  118. data/test/test_parser_debug.rb +0 -27
  119. data/test/test_parser_saj.rb +0 -337
  120. data/test/test_parser_usual.rb +0 -251
  121. data/test/test_rails.rb +0 -35
  122. data/test/test_saj.rb +0 -188
  123. data/test/test_scp.rb +0 -431
  124. data/test/test_strict.rb +0 -441
  125. data/test/test_various.rb +0 -801
  126. data/test/test_wab.rb +0 -311
  127. data/test/test_writer.rb +0 -380
  128. data/test/tests.rb +0 -33
  129. data/test/tests_mimic.rb +0 -23
  130. data/test/tests_mimic_addition.rb +0 -16
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\
@@ -152,8 +152,146 @@ inline static size_t newline_friendly_size(const uint8_t *str, size_t len) {
152
152
  return calculate_string_size(str, len, newline_friendly_chars);
153
153
  }
154
154
 
155
+ #ifdef HAVE_SIMD_NEON
156
+ inline static uint8x16x4_t load_uint8x16_4(const unsigned char *table) {
157
+ uint8x16x4_t tab;
158
+ tab.val[0] = vld1q_u8(table);
159
+ tab.val[1] = vld1q_u8(table + 16);
160
+ tab.val[2] = vld1q_u8(table + 32);
161
+ tab.val[3] = vld1q_u8(table + 48);
162
+ return tab;
163
+ }
164
+
165
+ static uint8x16x4_t hibit_friendly_chars_neon[2];
166
+ static uint8x16x4_t rails_friendly_chars_neon[2];
167
+ static uint8x16x4_t rails_xss_friendly_chars_neon[4];
168
+
169
+ void initialize_neon(void) {
170
+ // We only need the first 128 bytes of the hibit friendly chars table. Everything above 127 is
171
+ // set to 1. If that ever changes, the code will need to be updated.
172
+ hibit_friendly_chars_neon[0] = load_uint8x16_4((const unsigned char *)hibit_friendly_chars);
173
+ hibit_friendly_chars_neon[1] = load_uint8x16_4((const unsigned char *)hibit_friendly_chars + 64);
174
+
175
+ // rails_friendly_chars is the same as hibit_friendly_chars. Only the first 128 bytes have values
176
+ // that are not '1'. If that ever changes, the code will need to be updated.
177
+ rails_friendly_chars_neon[0] = load_uint8x16_4((const unsigned char *)rails_friendly_chars);
178
+ rails_friendly_chars_neon[1] = load_uint8x16_4((const unsigned char *)rails_friendly_chars + 64);
179
+
180
+ rails_xss_friendly_chars_neon[0] = load_uint8x16_4((const unsigned char *)rails_xss_friendly_chars);
181
+ rails_xss_friendly_chars_neon[1] = load_uint8x16_4((const unsigned char *)rails_xss_friendly_chars + 64);
182
+ rails_xss_friendly_chars_neon[2] = load_uint8x16_4((const unsigned char *)rails_xss_friendly_chars + 128);
183
+ rails_xss_friendly_chars_neon[3] = load_uint8x16_4((const unsigned char *)rails_xss_friendly_chars + 192);
184
+
185
+ // All bytes should be 0 except for those that need more than 1 byte of output. This will allow the
186
+ // code to limit the lookups to the first 128 bytes (values 0 - 127). Bytes above 127 will result
187
+ // in 0 with the vqtbl4q_u8 instruction.
188
+ uint8x16_t one = vdupq_n_u8('1');
189
+ for (int i = 0; i < 2; i++) {
190
+ for (int j = 0; j < 4; j++) {
191
+ hibit_friendly_chars_neon[i].val[j] = vsubq_u8(hibit_friendly_chars_neon[i].val[j], one);
192
+ rails_friendly_chars_neon[i].val[j] = vsubq_u8(rails_friendly_chars_neon[i].val[j], one);
193
+ }
194
+ }
195
+
196
+ for (int i = 0; i < 4; i++) {
197
+ for (int j = 0; j < 4; j++) {
198
+ rails_xss_friendly_chars_neon[i].val[j] = vsubq_u8(rails_xss_friendly_chars_neon[i].val[j], one);
199
+ }
200
+ }
201
+ }
202
+ #endif
203
+
204
+ #ifdef HAVE_SIMD_SSE4_2
205
+
206
+ static __m128i hibit_friendly_chars_sse42[8];
207
+
208
+ // From: https://stackoverflow.com/questions/36998538/fastest-way-to-horizontally-sum-sse-unsigned-byte-vector
209
+ inline static OJ_TARGET_SSE42 uint32_t _mm_sum_epu8(const __m128i v) {
210
+ __m128i vsum = _mm_sad_epu8(v, _mm_setzero_si128());
211
+ return _mm_cvtsi128_si32(vsum) + _mm_extract_epi16(vsum, 4);
212
+ }
213
+
214
+ inline static OJ_TARGET_SSE42 size_t hibit_friendly_size_sse42(const uint8_t *str, size_t len) {
215
+ size_t size = 0;
216
+ size_t i = 0;
217
+
218
+ for (; i + sizeof(__m128i) <= len; i += sizeof(__m128i), str += sizeof(__m128i)) {
219
+ size += sizeof(__m128i);
220
+
221
+ __m128i chunk = _mm_loadu_si128((__m128i *)str);
222
+ __m128i tmp = vector_lookup_sse42(chunk, hibit_friendly_chars_sse42, 8);
223
+ size += _mm_sum_epu8(tmp);
224
+ }
225
+ size_t total = size + calculate_string_size(str, len - i, hibit_friendly_chars);
226
+ return total;
227
+ }
228
+
229
+ void OJ_TARGET_SSE42 initialize_sse42(void) {
230
+ for (int i = 0; i < 8; i++) {
231
+ hibit_friendly_chars_sse42[i] = _mm_sub_epi8(
232
+ _mm_loadu_si128((__m128i *)(hibit_friendly_chars + i * sizeof(__m128i))),
233
+ _mm_set1_epi8('1'));
234
+ }
235
+ }
236
+
237
+ #else
238
+
239
+ #define SIMD_TARGET
240
+
241
+ #endif /* HAVE_SIMD_SSE4_2 */
242
+
155
243
  inline static size_t hibit_friendly_size(const uint8_t *str, size_t len) {
244
+ #ifdef HAVE_SIMD_NEON
245
+ size_t size = 0;
246
+ size_t i = 0;
247
+
248
+ for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
249
+ size += sizeof(uint8x16_t);
250
+
251
+ // See https://lemire.me/blog/2019/07/23/arbitrary-byte-to-byte-maps-using-arm-neon/
252
+ uint8x16_t chunk = vld1q_u8(str);
253
+ uint8x16_t tmp1 = vqtbl4q_u8(hibit_friendly_chars_neon[0], chunk);
254
+ uint8x16_t tmp2 = vqtbl4q_u8(hibit_friendly_chars_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
255
+ uint8x16_t result = vorrq_u8(tmp1, tmp2);
256
+ uint8_t tmp = vaddvq_u8(result);
257
+ size += tmp;
258
+ }
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
281
+ size_t total = size + calculate_string_size(str, len - i, hibit_friendly_chars);
282
+ #endif
283
+
284
+ return total;
285
+ #elif defined(HAVE_SIMD_SSE4_2)
286
+ if (SIMD_Impl == SIMD_SSE42) {
287
+ if (len >= sizeof(__m128i)) {
288
+ return hibit_friendly_size_sse42(str, len);
289
+ }
290
+ }
156
291
  return calculate_string_size(str, len, hibit_friendly_chars);
292
+ #else
293
+ return calculate_string_size(str, len, hibit_friendly_chars);
294
+ #endif
157
295
  }
158
296
 
159
297
  inline static size_t slash_friendly_size(const uint8_t *str, size_t len) {
@@ -183,10 +321,52 @@ inline static size_t hixss_friendly_size(const uint8_t *str, size_t len) {
183
321
  }
184
322
 
185
323
  inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
186
- long size = 0;
187
- size_t i = len;
188
- uint8_t hi = 0;
324
+ long size = 0;
325
+ uint32_t hi = 0;
326
+
327
+ #ifdef HAVE_SIMD_NEON
328
+ size_t i = 0;
329
+
330
+ if (len >= sizeof(uint8x16_t)) {
331
+ uint8x16_t has_some_hibit = vdupq_n_u8(0);
332
+ uint8x16_t hibit = vdupq_n_u8(0x80);
333
+
334
+ for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
335
+ size += sizeof(uint8x16_t);
189
336
 
337
+ uint8x16_t chunk = vld1q_u8(str);
338
+
339
+ // Check to see if any of these bytes have the high bit set.
340
+ has_some_hibit = vorrq_u8(has_some_hibit, vandq_u8(chunk, hibit));
341
+
342
+ uint8x16_t tmp1 = vqtbl4q_u8(rails_xss_friendly_chars_neon[0], chunk);
343
+ uint8x16_t tmp2 = vqtbl4q_u8(rails_xss_friendly_chars_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
344
+ uint8x16_t tmp3 = vqtbl4q_u8(rails_xss_friendly_chars_neon[2], veorq_u8(chunk, vdupq_n_u8(0x80)));
345
+ uint8x16_t tmp4 = vqtbl4q_u8(rails_xss_friendly_chars_neon[3], veorq_u8(chunk, vdupq_n_u8(0xc0)));
346
+ uint8x16_t result = vorrq_u8(tmp4, vorrq_u8(tmp3, vorrq_u8(tmp1, tmp2)));
347
+ uint8_t tmp = vaddvq_u8(result);
348
+ size += tmp;
349
+ }
350
+
351
+ // 'hi' should be set if any of the bytes we processed have the high bit set. It doesn't matter which ones.
352
+ hi = vmaxvq_u8(has_some_hibit) != 0;
353
+ }
354
+
355
+ size_t len_remaining = len - i;
356
+
357
+ for (; i < len; str++, i++) {
358
+ size += rails_xss_friendly_chars[*str];
359
+ hi |= *str & 0x80;
360
+ }
361
+
362
+ size -= (len_remaining * ((size_t)'0'));
363
+
364
+ if (0 == hi) {
365
+ return size;
366
+ }
367
+ return -(size);
368
+ #else
369
+ size_t i = len;
190
370
  for (; 0 < i; str++, i--) {
191
371
  size += rails_xss_friendly_chars[*str];
192
372
  hi |= *str & 0x80;
@@ -195,13 +375,53 @@ inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
195
375
  return size - len * (size_t)'0';
196
376
  }
197
377
  return -(size - len * (size_t)'0');
378
+ #endif /* HAVE_SIMD_NEON */
198
379
  }
199
380
 
200
381
  inline static size_t rails_friendly_size(const uint8_t *str, size_t len) {
201
- long size = 0;
202
- size_t i = len;
203
- uint8_t hi = 0;
382
+ long size = 0;
383
+ uint32_t hi = 0;
384
+ #ifdef HAVE_SIMD_NEON
385
+ size_t i = 0;
386
+ long extra = 0;
387
+
388
+ if (len >= sizeof(uint8x16_t)) {
389
+ uint8x16_t has_some_hibit = vdupq_n_u8(0);
390
+ uint8x16_t hibit = vdupq_n_u8(0x80);
391
+
392
+ for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
393
+ size += sizeof(uint8x16_t);
394
+
395
+ // See https://lemire.me/blog/2019/07/23/arbitrary-byte-to-byte-maps-using-arm-neon/
396
+ uint8x16_t chunk = vld1q_u8(str);
397
+
398
+ // Check to see if any of these bytes have the high bit set.
399
+ has_some_hibit = vorrq_u8(has_some_hibit, vandq_u8(chunk, hibit));
400
+
401
+ uint8x16_t tmp1 = vqtbl4q_u8(rails_friendly_chars_neon[0], chunk);
402
+ uint8x16_t tmp2 = vqtbl4q_u8(rails_friendly_chars_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
403
+ uint8x16_t result = vorrq_u8(tmp1, tmp2);
404
+ uint8_t tmp = vaddvq_u8(result);
405
+ size += tmp;
406
+ }
407
+
408
+ // 'hi' should be set if any of the bytes we processed have the high bit set. It doesn't matter which ones.
409
+ hi = vmaxvq_u8(has_some_hibit) != 0;
410
+ }
204
411
 
412
+ for (; i < len; str++, i++, extra++) {
413
+ size += rails_friendly_chars[*str];
414
+ hi |= *str & 0x80;
415
+ }
416
+
417
+ size -= (extra * ((size_t)'0'));
418
+
419
+ if (0 == hi) {
420
+ return size;
421
+ }
422
+ return -(size);
423
+ #else
424
+ size_t i = len;
205
425
  for (; 0 < i; str++, i--) {
206
426
  size += rails_friendly_chars[*str];
207
427
  hi |= *str & 0x80;
@@ -210,9 +430,10 @@ inline static size_t rails_friendly_size(const uint8_t *str, size_t len) {
210
430
  return size - len * (size_t)'0';
211
431
  }
212
432
  return -(size - len * (size_t)'0');
433
+ #endif /* HAVE_SIMD_NEON */
213
434
  }
214
435
 
215
- const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, int *lenp) {
436
+ const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, size_t *lenp) {
216
437
  const char *str = NULL;
217
438
 
218
439
  if (AutoNan == opt) {
@@ -477,7 +698,7 @@ void oj_dump_time(VALUE obj, Out out, int withZone) {
477
698
  void oj_dump_ruby_time(VALUE obj, Out out) {
478
699
  volatile VALUE rstr = oj_safe_string_convert(obj);
479
700
 
480
- oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
701
+ oj_dump_cstr(RSTRING_PTR(rstr), RSTRING_LEN(rstr), 0, 0, out);
481
702
  }
482
703
 
483
704
  void oj_dump_xml_time(VALUE obj, Out out) {
@@ -711,13 +932,13 @@ void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
711
932
  rb_encoding *enc = rb_enc_from_index(idx);
712
933
  obj = rb_str_conv_enc(obj, enc, oj_utf8_encoding);
713
934
  }
714
- oj_dump_cstr(RSTRING_PTR(obj), (int)RSTRING_LEN(obj), 0, 0, out);
935
+ oj_dump_cstr(RSTRING_PTR(obj), RSTRING_LEN(obj), 0, 0, out);
715
936
  }
716
937
 
717
938
  void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
718
939
  volatile VALUE s = rb_sym2str(obj);
719
940
 
720
- oj_dump_cstr(RSTRING_PTR(s), (int)RSTRING_LEN(s), 0, 0, out);
941
+ oj_dump_cstr(RSTRING_PTR(s), RSTRING_LEN(s), 0, 0, out);
721
942
  }
722
943
 
723
944
  static void debug_raise(const char *orig, size_t cnt, int line) {
@@ -758,9 +979,155 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
758
979
  }
759
980
  }
760
981
 
982
+ #if defined(__clang__) || defined(__GNUC__)
983
+ #define FORCE_INLINE __attribute__((always_inline))
984
+ #else
985
+ #define FORCE_INLINE
986
+ #endif
987
+
988
+ #ifdef HAVE_SIMD_NEON
989
+ typedef struct _neon_match_result {
990
+ uint8x16_t needs_escape;
991
+ bool has_some_hibit;
992
+ bool do_unicode_validation;
993
+ uint64_t escape_mask;
994
+ } neon_match_result;
995
+
996
+ static inline FORCE_INLINE neon_match_result
997
+ neon_update(const char *str, uint8x16x4_t *cmap_neon, int neon_table_size, bool do_unicode_validation, bool has_hi) {
998
+ neon_match_result result = {.has_some_hibit = false, .do_unicode_validation = false, .escape_mask = 0};
999
+
1000
+ uint8x16_t chunk = vld1q_u8((const unsigned char *)str);
1001
+ uint8x16_t tmp1 = vqtbl4q_u8(cmap_neon[0], chunk);
1002
+ uint8x16_t tmp2 = vqtbl4q_u8(cmap_neon[1], veorq_u8(chunk, vdupq_n_u8(0x40)));
1003
+ result.needs_escape = vorrq_u8(tmp1, tmp2);
1004
+ if (neon_table_size > 2) {
1005
+ uint8x16_t tmp3 = vqtbl4q_u8(cmap_neon[2], veorq_u8(chunk, vdupq_n_u8(0x80)));
1006
+ uint8x16_t tmp4 = vqtbl4q_u8(cmap_neon[3], veorq_u8(chunk, vdupq_n_u8(0xc0)));
1007
+ result.needs_escape = vorrq_u8(result.needs_escape, vorrq_u8(tmp4, tmp3));
1008
+ }
1009
+ if (has_hi && do_unicode_validation) {
1010
+ uint8x16_t has_some_hibit = vandq_u8(chunk, vdupq_n_u8(0x80));
1011
+ result.has_some_hibit = vmaxvq_u8(has_some_hibit) != 0;
1012
+ result.do_unicode_validation = has_hi && do_unicode_validation && result.has_some_hibit;
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;
1017
+ return result;
1018
+ }
1019
+
1020
+ #elif defined(HAVE_SIMD_SSE4_2)
1021
+ typedef struct _sse42_match_result {
1022
+ __m128i actions;
1023
+ bool needs_escape;
1024
+ int escape_mask;
1025
+ bool has_some_hibit;
1026
+ bool do_unicode_validation;
1027
+ } sse42_match_result;
1028
+
1029
+ static inline OJ_TARGET_SSE42 sse42_match_result
1030
+ sse42_update(const char *str, __m128i *cmap_sse42, int sse42_tab_size, bool do_unicode_validation, bool has_hi) {
1031
+ sse42_match_result result = {.has_some_hibit = false, .do_unicode_validation = false};
1032
+
1033
+ __m128i chunk = _mm_loadu_si128((__m128i *)str);
1034
+ __m128i actions = vector_lookup_sse42(chunk, cmap_sse42, sse42_tab_size);
1035
+ __m128i needs_escape = _mm_xor_si128(_mm_cmpeq_epi8(actions, _mm_setzero_si128()), _mm_set1_epi8(0xFF));
1036
+ result.actions = _mm_add_epi8(actions, _mm_set1_epi8('1'));
1037
+
1038
+ result.escape_mask = _mm_movemask_epi8(needs_escape);
1039
+ result.needs_escape = result.escape_mask != 0;
1040
+ if (has_hi && do_unicode_validation) {
1041
+ __m128i has_some_hibit = _mm_and_si128(chunk, _mm_set1_epi8(0x80));
1042
+ result.has_some_hibit = _mm_movemask_epi8(has_some_hibit) != 0;
1043
+ result.do_unicode_validation = has_hi && do_unicode_validation && result.has_some_hibit;
1044
+ }
1045
+ return result;
1046
+ }
1047
+
1048
+ #endif /* HAVE_SIMD_NEON */
1049
+
1050
+ static inline FORCE_INLINE const char *process_character(char action,
1051
+ const char *str,
1052
+ const char *end,
1053
+ Out out,
1054
+ const char *orig,
1055
+ bool do_unicode_validation,
1056
+ const char **check_start_) {
1057
+ const char *check_start = *check_start_;
1058
+ switch (action) {
1059
+ case '1':
1060
+ if (do_unicode_validation && check_start <= str) {
1061
+ if (0 != (0x80 & (uint8_t)*str)) {
1062
+ if (0xC0 == (0xC0 & (uint8_t)*str)) {
1063
+ *check_start_ = check_unicode(str, end, orig);
1064
+ } else {
1065
+ raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
1066
+ }
1067
+ }
1068
+ }
1069
+ *out->cur++ = *str;
1070
+ break;
1071
+ case '2':
1072
+ *out->cur++ = '\\';
1073
+ switch (*str) {
1074
+ case '\\': *out->cur++ = '\\'; break;
1075
+ case '\b': *out->cur++ = 'b'; break;
1076
+ case '\t': *out->cur++ = 't'; break;
1077
+ case '\n': *out->cur++ = 'n'; break;
1078
+ case '\f': *out->cur++ = 'f'; break;
1079
+ case '\r': *out->cur++ = 'r'; break;
1080
+ default: *out->cur++ = *str; break;
1081
+ }
1082
+ break;
1083
+ case '3': // Unicode
1084
+ if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
1085
+ if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
1086
+ str = dump_unicode(str, end, out, orig);
1087
+ } else {
1088
+ *check_start_ = check_unicode(str, end, orig);
1089
+ *out->cur++ = *str;
1090
+ }
1091
+ break;
1092
+ }
1093
+ str = dump_unicode(str, end, out, orig);
1094
+ break;
1095
+ case '6': // control characters
1096
+ if (*(uint8_t *)str < 0x80) {
1097
+ if (0 == (uint8_t)*str && out->opts->dump_opts.omit_null_byte) {
1098
+ break;
1099
+ }
1100
+ APPEND_CHARS(out->cur, "\\u00", 4);
1101
+ dump_hex((uint8_t)*str, out);
1102
+ } else {
1103
+ if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
1104
+ if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
1105
+ str = dump_unicode(str, end, out, orig);
1106
+ } else {
1107
+ *check_start_ = check_unicode(str, end, orig);
1108
+ *out->cur++ = *str;
1109
+ }
1110
+ break;
1111
+ }
1112
+ str = dump_unicode(str, end, out, orig);
1113
+ }
1114
+ break;
1115
+ default: break; // ignore, should never happen if the table is correct
1116
+ }
1117
+
1118
+ return str;
1119
+ }
1120
+
761
1121
  void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
762
- size_t size;
763
- char *cmap;
1122
+ size_t size;
1123
+ char *cmap;
1124
+ #ifdef HAVE_SIMD_NEON
1125
+ uint8x16x4_t *cmap_neon = NULL;
1126
+ int neon_table_size = 0;
1127
+ #elif defined(HAVE_SIMD_SSE4_2)
1128
+ __m128i *cmap_sse42 = NULL;
1129
+ int sse42_tab_size;
1130
+ #endif /* HAVE_SIMD_NEON */
764
1131
  const char *orig = str;
765
1132
  bool has_hi = false;
766
1133
  bool do_unicode_validation = false;
@@ -792,7 +1159,11 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
792
1159
  long sz;
793
1160
 
794
1161
  cmap = rails_xss_friendly_chars;
795
- sz = rails_xss_friendly_size((uint8_t *)str, cnt);
1162
+ #ifdef HAVE_SIMD_NEON
1163
+ cmap_neon = rails_xss_friendly_chars_neon;
1164
+ neon_table_size = 4;
1165
+ #endif /* HAVE_NEON_SIMD */
1166
+ sz = rails_xss_friendly_size((uint8_t *)str, cnt);
796
1167
  if (sz < 0) {
797
1168
  has_hi = true;
798
1169
  size = (size_t)-sz;
@@ -805,7 +1176,11 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
805
1176
  case RailsEsc: {
806
1177
  long sz;
807
1178
  cmap = rails_friendly_chars;
808
- sz = rails_friendly_size((uint8_t *)str, cnt);
1179
+ #ifdef HAVE_SIMD_NEON
1180
+ cmap_neon = rails_friendly_chars_neon;
1181
+ neon_table_size = 2;
1182
+ #endif /* HAVE_NEON_SIMD */
1183
+ sz = rails_friendly_size((uint8_t *)str, cnt);
809
1184
  if (sz < 0) {
810
1185
  has_hi = true;
811
1186
  size = (size_t)-sz;
@@ -816,7 +1191,15 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
816
1191
  break;
817
1192
  }
818
1193
  case JSONEsc:
819
- default: cmap = hibit_friendly_chars; size = hibit_friendly_size((uint8_t *)str, cnt);
1194
+ default: cmap = hibit_friendly_chars;
1195
+ #ifdef HAVE_SIMD_NEON
1196
+ cmap_neon = hibit_friendly_chars_neon;
1197
+ neon_table_size = 2;
1198
+ #elif defined(HAVE_SIMD_SSE4_2)
1199
+ cmap_sse42 = hibit_friendly_chars_sse42;
1200
+ sse42_tab_size = 8;
1201
+ #endif /* HAVE_NEON_SIMD */
1202
+ size = hibit_friendly_size((uint8_t *)str, cnt);
820
1203
  }
821
1204
  assure_size(out, size + BUFFER_EXTRA);
822
1205
  *out->cur++ = '"';
@@ -833,7 +1216,16 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
833
1216
  if (is_sym) {
834
1217
  *out->cur++ = ':';
835
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
836
1227
  APPEND_CHARS(out->cur, str, cnt);
1228
+ #endif
837
1229
  *out->cur++ = '"';
838
1230
  } else {
839
1231
  const char *end = str + cnt;
@@ -842,71 +1234,168 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
842
1234
  if (is_sym) {
843
1235
  *out->cur++ = ':';
844
1236
  }
845
- for (; str < end; str++) {
846
- switch (cmap[(uint8_t)*str]) {
847
- case '1':
848
- if (do_unicode_validation && check_start <= str) {
849
- if (0 != (0x80 & (uint8_t)*str)) {
850
- if (0xC0 == (0xC0 & (uint8_t)*str)) {
851
- check_start = check_unicode(str, end, orig);
852
- } else {
853
- raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
854
- }
855
- }
856
- }
857
- *out->cur++ = *str;
858
- break;
859
- case '2':
860
- *out->cur++ = '\\';
861
- switch (*str) {
862
- case '\\': *out->cur++ = '\\'; break;
863
- case '\b': *out->cur++ = 'b'; break;
864
- case '\t': *out->cur++ = 't'; break;
865
- case '\n': *out->cur++ = 'n'; break;
866
- case '\f': *out->cur++ = 'f'; break;
867
- case '\r': *out->cur++ = 'r'; break;
868
- default: *out->cur++ = *str; break;
1237
+
1238
+ #if defined(HAVE_SIMD_NEON) || defined(HAVE_SIMD_SSE4_2)
1239
+
1240
+ #define SEARCH_FLUSH \
1241
+ if (str > cursor) { \
1242
+ APPEND_CHARS(out->cur, cursor, str - cursor); \
1243
+ cursor = str; \
1244
+ }
1245
+
1246
+ const char *chunk_start;
1247
+ const char *chunk_end;
1248
+ const char *cursor = str;
1249
+ char matches[16];
1250
+ #endif /* HAVE_SIMD_NEON || HAVE_SIMD_SSE4_2 */
1251
+
1252
+ #if defined(HAVE_SIMD_NEON)
1253
+ bool use_simd = (cmap_neon != NULL && cnt >= (sizeof(uint8x16_t))) ? true : false;
1254
+ #elif defined(HAVE_SIMD_SSE4_2)
1255
+ bool use_simd = false;
1256
+ if (SIMD_Impl == SIMD_SSE42) {
1257
+ use_simd = (cmap_sse42 != NULL && cnt >= (sizeof(__m128i))) ? true : false;
1258
+ }
1259
+ #endif
1260
+
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
+
1273
+ if (use_simd) {
1274
+ while (str < end) {
1275
+ const char *chunk_ptr = NULL;
1276
+ if (str + sizeof(uint8x16_t) <= end) {
1277
+ chunk_ptr = str;
1278
+ chunk_start = str;
1279
+ chunk_end = str + sizeof(uint8x16_t);
1280
+ } else if ((end - str) >= SIMD_MINIMUM_THRESHOLD) {
1281
+ memset(out->cur, ' ', sizeof(uint8x16_t));
1282
+ MEMCPY16(out->cur, str, (end - str));
1283
+ chunk_ptr = out->cur;
1284
+ chunk_start = str;
1285
+ chunk_end = end;
1286
+ } else {
1287
+ break;
869
1288
  }
870
- break;
871
- case '3': // Unicode
872
- if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
873
- if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
874
- str = dump_unicode(str, end, out, orig);
1289
+ neon_match_result result = neon_update(chunk_ptr,
1290
+ cmap_neon,
1291
+ neon_table_size,
1292
+ do_unicode_validation,
1293
+ has_hi);
1294
+ if ((result.do_unicode_validation) || result.escape_mask != 0) {
1295
+ SEARCH_FLUSH;
1296
+ bool process_each = result.do_unicode_validation;
1297
+ uint8x16_t actions = vaddq_u8(result.needs_escape, vdupq_n_u8('1'));
1298
+ vst1q_u8((unsigned char *)matches, actions);
1299
+ // If no byte in this chunk had the high bit set then we can skip
1300
+ // all of the '1' bytes by directly copying them to the output.
1301
+ if (!process_each) {
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;
1308
+ }
1309
+ str = process_character(matches[esc_pos],
1310
+ str,
1311
+ end,
1312
+ out,
1313
+ orig,
1314
+ do_unicode_validation,
1315
+ &check_start);
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
+ }
875
1323
  } else {
876
- check_start = check_unicode(str, end, orig);
877
- *out->cur++ = *str;
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++;
1334
+ }
878
1335
  }
879
- break;
1336
+ cursor = str;
1337
+ continue;
880
1338
  }
881
- str = dump_unicode(str, end, out, orig);
882
- break;
883
- case '6': // control characters
884
- if (*(uint8_t *)str < 0x80) {
885
- if (0 == (uint8_t)*str && out->opts->dump_opts.omit_null_byte) {
1339
+ str = chunk_end;
1340
+ }
1341
+ SEARCH_FLUSH;
1342
+ }
1343
+ #endif
1344
+
1345
+ #ifdef HAVE_SIMD_SSE4_2
1346
+ if (SIMD_Impl == SIMD_SSE42) {
1347
+ if (use_simd) {
1348
+ while (str < end) {
1349
+ const char *chunk_ptr = NULL;
1350
+ if (str + sizeof(__m128i) <= end) {
1351
+ chunk_ptr = str;
1352
+ chunk_start = str;
1353
+ chunk_end = str + sizeof(__m128i);
1354
+ } else if ((end - str) >= SIMD_MINIMUM_THRESHOLD) {
1355
+ memset(out->cur, 'A', sizeof(__m128i));
1356
+ memcpy(out->cur, str, (end - str));
1357
+ chunk_ptr = out->cur;
1358
+ chunk_start = str;
1359
+ chunk_end = end;
1360
+ } else {
886
1361
  break;
887
1362
  }
888
- APPEND_CHARS(out->cur, "\\u00", 4);
889
- dump_hex((uint8_t)*str, out);
890
- } else {
891
- if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
892
- if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
893
- str = dump_unicode(str, end, out, orig);
894
- } else {
895
- check_start = check_unicode(str, end, orig);
896
- *out->cur++ = *str;
1363
+ sse42_match_result result = sse42_update(chunk_ptr,
1364
+ cmap_sse42,
1365
+ sse42_tab_size,
1366
+ do_unicode_validation,
1367
+ has_hi);
1368
+ if ((result.do_unicode_validation) || result.needs_escape) {
1369
+ SEARCH_FLUSH;
1370
+ _mm_storeu_si128((__m128i *)matches, result.actions);
1371
+ while (str < chunk_end) {
1372
+ long match_index = str - chunk_start;
1373
+ str = process_character(matches[match_index],
1374
+ str,
1375
+ end,
1376
+ out,
1377
+ orig,
1378
+ do_unicode_validation,
1379
+ &check_start);
1380
+ str++;
897
1381
  }
898
- break;
1382
+ cursor = str;
1383
+ continue;
899
1384
  }
900
- str = dump_unicode(str, end, out, orig);
1385
+ str = chunk_end;
901
1386
  }
902
- break;
903
- default: break; // ignore, should never happen if the table is correct
1387
+ SEARCH_FLUSH;
904
1388
  }
905
1389
  }
1390
+ #endif /* HAVE_SIMD_SSE4_2 */
1391
+
1392
+ for (; str < end; str++) {
1393
+ str = process_character(cmap[(uint8_t)*str], str, end, out, orig, do_unicode_validation, &check_start);
1394
+ }
906
1395
  *out->cur++ = '"';
907
1396
  }
908
1397
  if (do_unicode_validation && 0 < str - orig && 0 != (0x80 & *(str - 1))) {
909
- uint8_t c = (uint8_t) * (str - 1);
1398
+ uint8_t c = (uint8_t)*(str - 1);
910
1399
  int i;
911
1400
  int scnt = (int)(str - orig);
912
1401
 
@@ -957,7 +1446,7 @@ void oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
957
1446
  void oj_dump_obj_to_s(VALUE obj, Out out) {
958
1447
  volatile VALUE rstr = oj_safe_string_convert(obj);
959
1448
 
960
- oj_dump_cstr(RSTRING_PTR(rstr), (int)RSTRING_LEN(rstr), 0, 0, out);
1449
+ oj_dump_cstr(RSTRING_PTR(rstr), RSTRING_LEN(rstr), 0, 0, out);
961
1450
  }
962
1451
 
963
1452
  void oj_dump_raw(const char *str, size_t cnt, Out out) {
@@ -1092,7 +1581,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
1092
1581
 
1093
1582
  void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
1094
1583
  volatile VALUE rs = rb_big2str(obj, 10);
1095
- int cnt = (int)RSTRING_LEN(rs);
1584
+ size_t cnt = RSTRING_LEN(rs);
1096
1585
  bool dump_as_string = false;
1097
1586
 
1098
1587
  if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
@@ -1114,7 +1603,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1114
1603
  char buf[64];
1115
1604
  char *b;
1116
1605
  double d = rb_num2dbl(obj);
1117
- int cnt = 0;
1606
+ size_t cnt = 0;
1118
1607
 
1119
1608
  if (0.0 == d) {
1120
1609
  b = buf;
@@ -1225,7 +1714,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1225
1714
  } else if (0 == out->opts->float_prec) {
1226
1715
  volatile VALUE rstr = oj_safe_string_convert(obj);
1227
1716
 
1228
- cnt = (int)RSTRING_LEN(rstr);
1717
+ cnt = RSTRING_LEN(rstr);
1229
1718
  if ((int)sizeof(buf) <= cnt) {
1230
1719
  cnt = sizeof(buf) - 1;
1231
1720
  }
@@ -1239,8 +1728,8 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
1239
1728
  *out->cur = '\0';
1240
1729
  }
1241
1730
 
1242
- int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format) {
1243
- int cnt = snprintf(buf, blen, format, d);
1731
+ size_t oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format) {
1732
+ size_t cnt = snprintf(buf, blen, format, d);
1244
1733
 
1245
1734
  // Round off issues at 16 significant digits so check for obvious ones of
1246
1735
  // 0001 and 9999.
@@ -1248,7 +1737,29 @@ int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char
1248
1737
  volatile VALUE rstr = oj_safe_string_convert(obj);
1249
1738
 
1250
1739
  strcpy(buf, RSTRING_PTR(rstr));
1251
- cnt = (int)RSTRING_LEN(rstr);
1740
+ cnt = RSTRING_LEN(rstr);
1252
1741
  }
1253
1742
  return cnt;
1254
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
+ }