oj 3.16.0 → 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 +4 -4
- data/CHANGELOG.md +107 -0
- data/README.md +0 -16
- data/ext/oj/cache.c +4 -2
- data/ext/oj/cache.h +3 -2
- data/ext/oj/compat.c +3 -3
- data/ext/oj/custom.c +17 -11
- data/ext/oj/dump.c +624 -90
- data/ext/oj/dump.h +9 -2
- data/ext/oj/dump_compat.c +15 -9
- data/ext/oj/dump_leaf.c +1 -1
- data/ext/oj/dump_object.c +32 -17
- data/ext/oj/dump_strict.c +20 -14
- data/ext/oj/extconf.rb +11 -9
- data/ext/oj/fast.c +50 -32
- data/ext/oj/intern.c +24 -7
- data/ext/oj/mimic_json.c +17 -11
- data/ext/oj/object.c +21 -13
- data/ext/oj/oj.c +387 -140
- data/ext/oj/oj.h +59 -55
- data/ext/oj/parse.c +248 -47
- data/ext/oj/parser.c +179 -89
- data/ext/oj/parser.h +4 -2
- data/ext/oj/rails.c +64 -42
- data/ext/oj/reader.c +1 -1
- data/ext/oj/rxclass.c +1 -1
- data/ext/oj/rxclass.h +1 -1
- data/ext/oj/safe.c +230 -0
- data/ext/oj/safe.h +79 -0
- data/ext/oj/saj.c +15 -10
- data/ext/oj/scp.c +3 -6
- data/ext/oj/simd.h +219 -0
- data/ext/oj/sparse.c +6 -3
- data/ext/oj/stream_writer.c +38 -28
- data/ext/oj/strict.c +2 -4
- data/ext/oj/string_writer.c +54 -22
- data/ext/oj/usual.c +57 -35
- data/ext/oj/usual.h +1 -0
- data/ext/oj/val_stack.c +13 -2
- data/ext/oj/wab.c +5 -4
- data/lib/oj/mimic.rb +1 -5
- data/lib/oj/schandler.rb +5 -4
- data/lib/oj/version.rb +1 -1
- data/pages/Encoding.md +1 -1
- metadata +36 -100
- data/test/_test_active.rb +0 -75
- data/test/_test_active_mimic.rb +0 -95
- data/test/_test_mimic_rails.rb +0 -123
- data/test/activerecord/result_test.rb +0 -31
- data/test/activesupport6/abstract_unit.rb +0 -44
- data/test/activesupport6/decoding_test.rb +0 -133
- data/test/activesupport6/encoding_test.rb +0 -507
- data/test/activesupport6/encoding_test_cases.rb +0 -98
- data/test/activesupport6/test_common.rb +0 -17
- data/test/activesupport6/test_helper.rb +0 -163
- data/test/activesupport6/time_zone_test_helpers.rb +0 -39
- data/test/activesupport7/abstract_unit.rb +0 -49
- data/test/activesupport7/decoding_test.rb +0 -125
- data/test/activesupport7/encoding_test.rb +0 -486
- data/test/activesupport7/encoding_test_cases.rb +0 -104
- data/test/activesupport7/time_zone_test_helpers.rb +0 -47
- data/test/files.rb +0 -29
- data/test/foo.rb +0 -14
- data/test/helper.rb +0 -39
- data/test/isolated/shared.rb +0 -309
- data/test/isolated/test_mimic_after.rb +0 -13
- data/test/isolated/test_mimic_alone.rb +0 -12
- data/test/isolated/test_mimic_as_json.rb +0 -45
- data/test/isolated/test_mimic_before.rb +0 -13
- data/test/isolated/test_mimic_define.rb +0 -28
- data/test/isolated/test_mimic_rails_after.rb +0 -22
- data/test/isolated/test_mimic_rails_before.rb +0 -21
- data/test/isolated/test_mimic_redefine.rb +0 -15
- data/test/json_gem/json_addition_test.rb +0 -216
- data/test/json_gem/json_common_interface_test.rb +0 -155
- data/test/json_gem/json_encoding_test.rb +0 -107
- data/test/json_gem/json_ext_parser_test.rb +0 -21
- data/test/json_gem/json_fixtures_test.rb +0 -36
- data/test/json_gem/json_generator_test.rb +0 -407
- data/test/json_gem/json_generic_object_test.rb +0 -90
- data/test/json_gem/json_parser_test.rb +0 -477
- data/test/json_gem/json_string_matching_test.rb +0 -42
- data/test/json_gem/test_helper.rb +0 -30
- data/test/mem.rb +0 -34
- data/test/perf.rb +0 -102
- data/test/perf_compat.rb +0 -128
- data/test/perf_dump.rb +0 -50
- data/test/perf_fast.rb +0 -162
- data/test/perf_file.rb +0 -62
- data/test/perf_object.rb +0 -134
- data/test/perf_once.rb +0 -59
- data/test/perf_parser.rb +0 -183
- data/test/perf_saj.rb +0 -101
- data/test/perf_scp.rb +0 -140
- data/test/perf_simple.rb +0 -289
- data/test/perf_strict.rb +0 -137
- data/test/perf_wab.rb +0 -129
- data/test/prec.rb +0 -23
- data/test/sample/change.rb +0 -13
- data/test/sample/dir.rb +0 -18
- data/test/sample/doc.rb +0 -35
- data/test/sample/file.rb +0 -47
- data/test/sample/group.rb +0 -15
- data/test/sample/hasprops.rb +0 -15
- data/test/sample/layer.rb +0 -11
- data/test/sample/line.rb +0 -20
- data/test/sample/oval.rb +0 -10
- data/test/sample/rect.rb +0 -9
- data/test/sample/shape.rb +0 -34
- data/test/sample/text.rb +0 -19
- data/test/sample.rb +0 -54
- data/test/sample_json.rb +0 -37
- data/test/test_compat.rb +0 -567
- data/test/test_custom.rb +0 -554
- data/test/test_debian.rb +0 -50
- data/test/test_fast.rb +0 -526
- data/test/test_file.rb +0 -250
- data/test/test_gc.rb +0 -60
- data/test/test_generate.rb +0 -21
- data/test/test_hash.rb +0 -39
- data/test/test_integer_range.rb +0 -72
- data/test/test_null.rb +0 -376
- data/test/test_object.rb +0 -1016
- data/test/test_parser.rb +0 -11
- data/test/test_parser_debug.rb +0 -27
- data/test/test_parser_saj.rb +0 -337
- data/test/test_parser_usual.rb +0 -217
- data/test/test_rails.rb +0 -35
- data/test/test_saj.rb +0 -188
- data/test/test_scp.rb +0 -431
- data/test/test_strict.rb +0 -431
- data/test/test_various.rb +0 -801
- data/test/test_wab.rb +0 -311
- data/test/test_writer.rb +0 -380
- data/test/tests.rb +0 -33
- data/test/tests_mimic.rb +0 -23
- 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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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[
|
|
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
|
+
}
|
|
291
|
+
return calculate_string_size(str, len, hibit_friendly_chars);
|
|
292
|
+
#else
|
|
156
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
|
|
187
|
-
|
|
188
|
-
|
|
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);
|
|
189
333
|
|
|
334
|
+
for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
|
|
335
|
+
size += sizeof(uint8x16_t);
|
|
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,65 @@ 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
|
-
|
|
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
|
+
}
|
|
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;
|
|
425
|
+
for (; 0 < i; str++, i--) {
|
|
426
|
+
size += rails_friendly_chars[*str];
|
|
427
|
+
hi |= *str & 0x80;
|
|
428
|
+
}
|
|
429
|
+
if (0 == hi) {
|
|
430
|
+
return size - len * (size_t)'0';
|
|
431
|
+
}
|
|
432
|
+
return -(size - len * (size_t)'0');
|
|
433
|
+
#endif /* HAVE_SIMD_NEON */
|
|
202
434
|
}
|
|
203
435
|
|
|
204
|
-
const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus,
|
|
436
|
+
const char *oj_nan_str(VALUE obj, int opt, int mode, bool plus, size_t *lenp) {
|
|
205
437
|
const char *str = NULL;
|
|
206
438
|
|
|
207
439
|
if (AutoNan == opt) {
|
|
@@ -466,7 +698,7 @@ void oj_dump_time(VALUE obj, Out out, int withZone) {
|
|
|
466
698
|
void oj_dump_ruby_time(VALUE obj, Out out) {
|
|
467
699
|
volatile VALUE rstr = oj_safe_string_convert(obj);
|
|
468
700
|
|
|
469
|
-
oj_dump_cstr(RSTRING_PTR(rstr),
|
|
701
|
+
oj_dump_cstr(RSTRING_PTR(rstr), RSTRING_LEN(rstr), 0, 0, out);
|
|
470
702
|
}
|
|
471
703
|
|
|
472
704
|
void oj_dump_xml_time(VALUE obj, Out out) {
|
|
@@ -700,13 +932,13 @@ void oj_dump_str(VALUE obj, int depth, Out out, bool as_ok) {
|
|
|
700
932
|
rb_encoding *enc = rb_enc_from_index(idx);
|
|
701
933
|
obj = rb_str_conv_enc(obj, enc, oj_utf8_encoding);
|
|
702
934
|
}
|
|
703
|
-
oj_dump_cstr(RSTRING_PTR(obj),
|
|
935
|
+
oj_dump_cstr(RSTRING_PTR(obj), RSTRING_LEN(obj), 0, 0, out);
|
|
704
936
|
}
|
|
705
937
|
|
|
706
938
|
void oj_dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
|
|
707
939
|
volatile VALUE s = rb_sym2str(obj);
|
|
708
940
|
|
|
709
|
-
oj_dump_cstr(RSTRING_PTR(s),
|
|
941
|
+
oj_dump_cstr(RSTRING_PTR(s), RSTRING_LEN(s), 0, 0, out);
|
|
710
942
|
}
|
|
711
943
|
|
|
712
944
|
static void debug_raise(const char *orig, size_t cnt, int line) {
|
|
@@ -727,8 +959,11 @@ static void debug_raise(const char *orig, size_t cnt, int line) {
|
|
|
727
959
|
|
|
728
960
|
void oj_dump_raw_json(VALUE obj, int depth, Out out) {
|
|
729
961
|
if (oj_string_writer_class == rb_obj_class(obj)) {
|
|
730
|
-
StrWriter sw
|
|
731
|
-
size_t len
|
|
962
|
+
StrWriter sw;
|
|
963
|
+
size_t len;
|
|
964
|
+
|
|
965
|
+
sw = oj_str_writer_unwrap(obj);
|
|
966
|
+
len = sw->out.cur - sw->out.buf;
|
|
732
967
|
|
|
733
968
|
if (0 < len) {
|
|
734
969
|
len--;
|
|
@@ -744,11 +979,158 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
|
|
|
744
979
|
}
|
|
745
980
|
}
|
|
746
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
|
+
|
|
747
1121
|
void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
|
|
748
|
-
size_t
|
|
749
|
-
char
|
|
750
|
-
|
|
751
|
-
|
|
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 */
|
|
1131
|
+
const char *orig = str;
|
|
1132
|
+
bool has_hi = false;
|
|
1133
|
+
bool do_unicode_validation = false;
|
|
752
1134
|
|
|
753
1135
|
switch (out->opts->escape_mode) {
|
|
754
1136
|
case NLEsc:
|
|
@@ -769,28 +1151,55 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
|
|
|
769
1151
|
size = xss_friendly_size((uint8_t *)str, cnt);
|
|
770
1152
|
break;
|
|
771
1153
|
case JXEsc:
|
|
772
|
-
cmap
|
|
773
|
-
size
|
|
1154
|
+
cmap = hixss_friendly_chars;
|
|
1155
|
+
size = hixss_friendly_size((uint8_t *)str, cnt);
|
|
1156
|
+
do_unicode_validation = true;
|
|
774
1157
|
break;
|
|
775
1158
|
case RailsXEsc: {
|
|
776
1159
|
long sz;
|
|
777
1160
|
|
|
778
1161
|
cmap = rails_xss_friendly_chars;
|
|
779
|
-
|
|
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);
|
|
780
1167
|
if (sz < 0) {
|
|
781
1168
|
has_hi = true;
|
|
782
1169
|
size = (size_t)-sz;
|
|
783
1170
|
} else {
|
|
784
1171
|
size = (size_t)sz;
|
|
785
1172
|
}
|
|
1173
|
+
do_unicode_validation = true;
|
|
786
1174
|
break;
|
|
787
1175
|
}
|
|
788
|
-
case RailsEsc:
|
|
1176
|
+
case RailsEsc: {
|
|
1177
|
+
long sz;
|
|
789
1178
|
cmap = rails_friendly_chars;
|
|
790
|
-
|
|
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);
|
|
1184
|
+
if (sz < 0) {
|
|
1185
|
+
has_hi = true;
|
|
1186
|
+
size = (size_t)-sz;
|
|
1187
|
+
} else {
|
|
1188
|
+
size = (size_t)sz;
|
|
1189
|
+
}
|
|
1190
|
+
do_unicode_validation = true;
|
|
791
1191
|
break;
|
|
1192
|
+
}
|
|
792
1193
|
case JSONEsc:
|
|
793
|
-
default: cmap = hibit_friendly_chars;
|
|
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);
|
|
794
1203
|
}
|
|
795
1204
|
assure_size(out, size + BUFFER_EXTRA);
|
|
796
1205
|
*out->cur++ = '"';
|
|
@@ -807,7 +1216,16 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
|
|
|
807
1216
|
if (is_sym) {
|
|
808
1217
|
*out->cur++ = ':';
|
|
809
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
|
|
810
1227
|
APPEND_CHARS(out->cur, str, cnt);
|
|
1228
|
+
#endif
|
|
811
1229
|
*out->cur++ = '"';
|
|
812
1230
|
} else {
|
|
813
1231
|
const char *end = str + cnt;
|
|
@@ -816,74 +1234,168 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
|
|
|
816
1234
|
if (is_sym) {
|
|
817
1235
|
*out->cur++ = ':';
|
|
818
1236
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
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;
|
|
843
1288
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
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
|
+
}
|
|
850
1323
|
} else {
|
|
851
|
-
|
|
852
|
-
|
|
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
|
+
}
|
|
853
1335
|
}
|
|
854
|
-
|
|
1336
|
+
cursor = str;
|
|
1337
|
+
continue;
|
|
855
1338
|
}
|
|
856
|
-
str =
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
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 {
|
|
861
1361
|
break;
|
|
862
1362
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
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++;
|
|
873
1381
|
}
|
|
874
|
-
|
|
1382
|
+
cursor = str;
|
|
1383
|
+
continue;
|
|
875
1384
|
}
|
|
876
|
-
str =
|
|
1385
|
+
str = chunk_end;
|
|
877
1386
|
}
|
|
878
|
-
|
|
879
|
-
default: break; // ignore, should never happen if the table is correct
|
|
1387
|
+
SEARCH_FLUSH;
|
|
880
1388
|
}
|
|
881
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
|
+
}
|
|
882
1395
|
*out->cur++ = '"';
|
|
883
1396
|
}
|
|
884
|
-
if (
|
|
885
|
-
|
|
886
|
-
uint8_t c = (uint8_t) * (str - 1);
|
|
1397
|
+
if (do_unicode_validation && 0 < str - orig && 0 != (0x80 & *(str - 1))) {
|
|
1398
|
+
uint8_t c = (uint8_t)*(str - 1);
|
|
887
1399
|
int i;
|
|
888
1400
|
int scnt = (int)(str - orig);
|
|
889
1401
|
|
|
@@ -934,7 +1446,7 @@ void oj_dump_class(VALUE obj, int depth, Out out, bool as_ok) {
|
|
|
934
1446
|
void oj_dump_obj_to_s(VALUE obj, Out out) {
|
|
935
1447
|
volatile VALUE rstr = oj_safe_string_convert(obj);
|
|
936
1448
|
|
|
937
|
-
oj_dump_cstr(RSTRING_PTR(rstr),
|
|
1449
|
+
oj_dump_cstr(RSTRING_PTR(rstr), RSTRING_LEN(rstr), 0, 0, out);
|
|
938
1450
|
}
|
|
939
1451
|
|
|
940
1452
|
void oj_dump_raw(const char *str, size_t cnt, Out out) {
|
|
@@ -1069,7 +1581,7 @@ void oj_dump_fixnum(VALUE obj, int depth, Out out, bool as_ok) {
|
|
|
1069
1581
|
|
|
1070
1582
|
void oj_dump_bignum(VALUE obj, int depth, Out out, bool as_ok) {
|
|
1071
1583
|
volatile VALUE rs = rb_big2str(obj, 10);
|
|
1072
|
-
|
|
1584
|
+
size_t cnt = RSTRING_LEN(rs);
|
|
1073
1585
|
bool dump_as_string = false;
|
|
1074
1586
|
|
|
1075
1587
|
if (out->opts->int_range_max != 0 || out->opts->int_range_min != 0) { // Bignum cannot be inside of Fixnum range
|
|
@@ -1091,7 +1603,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
|
|
|
1091
1603
|
char buf[64];
|
|
1092
1604
|
char *b;
|
|
1093
1605
|
double d = rb_num2dbl(obj);
|
|
1094
|
-
|
|
1606
|
+
size_t cnt = 0;
|
|
1095
1607
|
|
|
1096
1608
|
if (0.0 == d) {
|
|
1097
1609
|
b = buf;
|
|
@@ -1202,7 +1714,7 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
|
|
|
1202
1714
|
} else if (0 == out->opts->float_prec) {
|
|
1203
1715
|
volatile VALUE rstr = oj_safe_string_convert(obj);
|
|
1204
1716
|
|
|
1205
|
-
cnt =
|
|
1717
|
+
cnt = RSTRING_LEN(rstr);
|
|
1206
1718
|
if ((int)sizeof(buf) <= cnt) {
|
|
1207
1719
|
cnt = sizeof(buf) - 1;
|
|
1208
1720
|
}
|
|
@@ -1216,8 +1728,8 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
|
|
|
1216
1728
|
*out->cur = '\0';
|
|
1217
1729
|
}
|
|
1218
1730
|
|
|
1219
|
-
|
|
1220
|
-
|
|
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);
|
|
1221
1733
|
|
|
1222
1734
|
// Round off issues at 16 significant digits so check for obvious ones of
|
|
1223
1735
|
// 0001 and 9999.
|
|
@@ -1225,7 +1737,29 @@ int oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char
|
|
|
1225
1737
|
volatile VALUE rstr = oj_safe_string_convert(obj);
|
|
1226
1738
|
|
|
1227
1739
|
strcpy(buf, RSTRING_PTR(rstr));
|
|
1228
|
-
cnt =
|
|
1740
|
+
cnt = RSTRING_LEN(rstr);
|
|
1229
1741
|
}
|
|
1230
1742
|
return cnt;
|
|
1231
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
|
+
}
|