oj 3.16.11 → 3.17.6
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 +133 -0
- data/README.md +0 -16
- data/ext/oj/cache.c +17 -1
- data/ext/oj/cache.h +5 -0
- data/ext/oj/circarray.c +6 -2
- data/ext/oj/compat.c +5 -5
- data/ext/oj/custom.c +12 -5
- data/ext/oj/dump.c +433 -206
- data/ext/oj/dump.h +12 -0
- data/ext/oj/dump_compat.c +17 -5
- data/ext/oj/dump_object.c +25 -10
- data/ext/oj/dump_strict.c +13 -8
- data/ext/oj/extconf.rb +11 -7
- data/ext/oj/fast.c +123 -60
- data/ext/oj/intern.c +7 -2
- data/ext/oj/mimic_json.c +3 -0
- data/ext/oj/object.c +26 -15
- data/ext/oj/odd.c +6 -2
- data/ext/oj/oj.c +511 -22
- data/ext/oj/oj.h +61 -53
- data/ext/oj/parse.c +229 -30
- data/ext/oj/parse.h +0 -1
- data/ext/oj/parser.c +226 -65
- data/ext/oj/rails.c +197 -57
- data/ext/oj/reader.c +18 -6
- data/ext/oj/resolve.c +11 -2
- data/ext/oj/rxclass.c +17 -1
- data/ext/oj/rxclass.h +2 -1
- data/ext/oj/safe.c +244 -0
- data/ext/oj/safe.h +91 -0
- data/ext/oj/saj.c +56 -12
- data/ext/oj/simd.h +210 -1
- data/ext/oj/sparse.c +22 -9
- data/ext/oj/stream_writer.c +16 -1
- data/ext/oj/string_writer.c +23 -7
- data/ext/oj/usual.c +23 -6
- data/ext/oj/util.c +27 -0
- data/ext/oj/util.h +2 -1
- data/ext/oj/val_stack.h +24 -5
- data/ext/oj/wab.c +15 -27
- data/lib/oj/version.rb +1 -1
- data/pages/Compatibility.md +1 -1
- data/pages/Custom.md +2 -3
- data/pages/InstallOptions.md +8 -6
- data/pages/Modes.md +1 -1
- data/pages/Options.md +5 -6
- data/pages/Parser.md +2 -2
- data/pages/Rails.md +6 -0
- data/pages/Security.md +21 -7
- metadata +21 -19
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\
|
|
@@ -201,6 +201,45 @@ void initialize_neon(void) {
|
|
|
201
201
|
}
|
|
202
202
|
#endif
|
|
203
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
|
+
|
|
204
243
|
inline static size_t hibit_friendly_size(const uint8_t *str, size_t len) {
|
|
205
244
|
#ifdef HAVE_SIMD_NEON
|
|
206
245
|
size_t size = 0;
|
|
@@ -218,8 +257,38 @@ inline static size_t hibit_friendly_size(const uint8_t *str, size_t len) {
|
|
|
218
257
|
size += tmp;
|
|
219
258
|
}
|
|
220
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
|
|
221
281
|
size_t total = size + calculate_string_size(str, len - i, hibit_friendly_chars);
|
|
282
|
+
#endif
|
|
283
|
+
|
|
222
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);
|
|
223
292
|
#else
|
|
224
293
|
return calculate_string_size(str, len, hibit_friendly_chars);
|
|
225
294
|
#endif
|
|
@@ -252,38 +321,46 @@ inline static size_t hixss_friendly_size(const uint8_t *str, size_t len) {
|
|
|
252
321
|
}
|
|
253
322
|
|
|
254
323
|
inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
|
|
255
|
-
long
|
|
256
|
-
|
|
324
|
+
long size = 0;
|
|
325
|
+
uint32_t hi = 0;
|
|
257
326
|
|
|
258
327
|
#ifdef HAVE_SIMD_NEON
|
|
259
328
|
size_t i = 0;
|
|
260
329
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
size += sizeof(uint8x16_t);
|
|
330
|
+
if (len >= sizeof(uint8x16_t)) {
|
|
331
|
+
uint8x16_t has_some_hibit = vdupq_n_u8(0);
|
|
332
|
+
uint8x16_t hibit = vdupq_n_u8(0x80);
|
|
265
333
|
|
|
266
|
-
uint8x16_t
|
|
334
|
+
for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
|
|
335
|
+
size += sizeof(uint8x16_t);
|
|
267
336
|
|
|
268
|
-
|
|
269
|
-
has_some_hibit = vorrq_u8(has_some_hibit, vandq_u8(chunk, hibit));
|
|
337
|
+
uint8x16_t chunk = vld1q_u8(str);
|
|
270
338
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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;
|
|
278
353
|
}
|
|
279
354
|
|
|
280
|
-
|
|
281
|
-
hi = vmaxvq_u8(has_some_hibit) != 0;
|
|
355
|
+
size_t len_remaining = len - i;
|
|
282
356
|
|
|
283
357
|
for (; i < len; str++, i++) {
|
|
284
|
-
size += rails_xss_friendly_chars[*str]
|
|
358
|
+
size += rails_xss_friendly_chars[*str];
|
|
285
359
|
hi |= *str & 0x80;
|
|
286
360
|
}
|
|
361
|
+
|
|
362
|
+
size -= (len_remaining * ((size_t)'0'));
|
|
363
|
+
|
|
287
364
|
if (0 == hi) {
|
|
288
365
|
return size;
|
|
289
366
|
}
|
|
@@ -302,37 +379,43 @@ inline static long rails_xss_friendly_size(const uint8_t *str, size_t len) {
|
|
|
302
379
|
}
|
|
303
380
|
|
|
304
381
|
inline static size_t rails_friendly_size(const uint8_t *str, size_t len) {
|
|
305
|
-
long
|
|
306
|
-
|
|
382
|
+
long size = 0;
|
|
383
|
+
uint32_t hi = 0;
|
|
307
384
|
#ifdef HAVE_SIMD_NEON
|
|
308
|
-
size_t i
|
|
385
|
+
size_t i = 0;
|
|
386
|
+
long extra = 0;
|
|
309
387
|
|
|
310
|
-
|
|
311
|
-
|
|
388
|
+
if (len >= sizeof(uint8x16_t)) {
|
|
389
|
+
uint8x16_t has_some_hibit = vdupq_n_u8(0);
|
|
390
|
+
uint8x16_t hibit = vdupq_n_u8(0x80);
|
|
312
391
|
|
|
313
|
-
|
|
314
|
-
|
|
392
|
+
for (; i + sizeof(uint8x16_t) <= len; i += sizeof(uint8x16_t), str += sizeof(uint8x16_t)) {
|
|
393
|
+
size += sizeof(uint8x16_t);
|
|
315
394
|
|
|
316
|
-
|
|
317
|
-
|
|
395
|
+
// See https://lemire.me/blog/2019/07/23/arbitrary-byte-to-byte-maps-using-arm-neon/
|
|
396
|
+
uint8x16_t chunk = vld1q_u8(str);
|
|
318
397
|
|
|
319
|
-
|
|
320
|
-
|
|
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));
|
|
321
400
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
+
}
|
|
328
407
|
|
|
329
|
-
|
|
330
|
-
|
|
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
|
+
}
|
|
331
411
|
|
|
332
|
-
for (; i < len; str++, i++) {
|
|
333
|
-
size += rails_friendly_chars[*str]
|
|
412
|
+
for (; i < len; str++, i++, extra++) {
|
|
413
|
+
size += rails_friendly_chars[*str];
|
|
334
414
|
hi |= *str & 0x80;
|
|
335
415
|
}
|
|
416
|
+
|
|
417
|
+
size -= (extra * ((size_t)'0'));
|
|
418
|
+
|
|
336
419
|
if (0 == hi) {
|
|
337
420
|
return size;
|
|
338
421
|
}
|
|
@@ -896,22 +979,23 @@ void oj_dump_raw_json(VALUE obj, int depth, Out out) {
|
|
|
896
979
|
}
|
|
897
980
|
}
|
|
898
981
|
|
|
982
|
+
#if defined(__clang__) || defined(__GNUC__)
|
|
983
|
+
#define FORCE_INLINE __attribute__((always_inline))
|
|
984
|
+
#else
|
|
985
|
+
#define FORCE_INLINE
|
|
986
|
+
#endif
|
|
987
|
+
|
|
899
988
|
#ifdef HAVE_SIMD_NEON
|
|
900
989
|
typedef struct _neon_match_result {
|
|
901
990
|
uint8x16_t needs_escape;
|
|
902
991
|
bool has_some_hibit;
|
|
903
992
|
bool do_unicode_validation;
|
|
993
|
+
uint64_t escape_mask;
|
|
904
994
|
} neon_match_result;
|
|
905
995
|
|
|
906
|
-
#if defined(__clang__) || defined(__GNUC__)
|
|
907
|
-
#define FORCE_INLINE __attribute__((always_inline))
|
|
908
|
-
#else
|
|
909
|
-
#define FORCE_INLINE
|
|
910
|
-
#endif
|
|
911
|
-
|
|
912
996
|
static inline FORCE_INLINE neon_match_result
|
|
913
997
|
neon_update(const char *str, uint8x16x4_t *cmap_neon, int neon_table_size, bool do_unicode_validation, bool has_hi) {
|
|
914
|
-
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};
|
|
915
999
|
|
|
916
1000
|
uint8x16_t chunk = vld1q_u8((const unsigned char *)str);
|
|
917
1001
|
uint8x16_t tmp1 = vqtbl4q_u8(cmap_neon[0], chunk);
|
|
@@ -927,17 +1011,122 @@ neon_update(const char *str, uint8x16x4_t *cmap_neon, int neon_table_size, bool
|
|
|
927
1011
|
result.has_some_hibit = vmaxvq_u8(has_some_hibit) != 0;
|
|
928
1012
|
result.do_unicode_validation = has_hi && do_unicode_validation && result.has_some_hibit;
|
|
929
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
|
+
}
|
|
930
1045
|
return result;
|
|
931
1046
|
}
|
|
932
1047
|
|
|
933
1048
|
#endif /* HAVE_SIMD_NEON */
|
|
934
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
|
+
|
|
935
1121
|
void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out out) {
|
|
936
1122
|
size_t size;
|
|
937
1123
|
char *cmap;
|
|
938
1124
|
#ifdef HAVE_SIMD_NEON
|
|
939
|
-
uint8x16x4_t *cmap_neon
|
|
940
|
-
int neon_table_size;
|
|
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;
|
|
941
1130
|
#endif /* HAVE_SIMD_NEON */
|
|
942
1131
|
const char *orig = str;
|
|
943
1132
|
bool has_hi = false;
|
|
@@ -1006,6 +1195,9 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
|
|
|
1006
1195
|
#ifdef HAVE_SIMD_NEON
|
|
1007
1196
|
cmap_neon = hibit_friendly_chars_neon;
|
|
1008
1197
|
neon_table_size = 2;
|
|
1198
|
+
#elif defined(HAVE_SIMD_SSE4_2)
|
|
1199
|
+
cmap_sse42 = hibit_friendly_chars_sse42;
|
|
1200
|
+
sse42_tab_size = 8;
|
|
1009
1201
|
#endif /* HAVE_NEON_SIMD */
|
|
1010
1202
|
size = hibit_friendly_size((uint8_t *)str, cnt);
|
|
1011
1203
|
}
|
|
@@ -1024,7 +1216,16 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
|
|
|
1024
1216
|
if (is_sym) {
|
|
1025
1217
|
*out->cur++ = ':';
|
|
1026
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
|
|
1027
1227
|
APPEND_CHARS(out->cur, str, cnt);
|
|
1228
|
+
#endif
|
|
1028
1229
|
*out->cur++ = '"';
|
|
1029
1230
|
} else {
|
|
1030
1231
|
const char *end = str + cnt;
|
|
@@ -1033,175 +1234,164 @@ void oj_dump_cstr(const char *str, size_t cnt, bool is_sym, bool escape1, Out ou
|
|
|
1033
1234
|
if (is_sym) {
|
|
1034
1235
|
*out->cur++ = ':';
|
|
1035
1236
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
const char *cursor = str;
|
|
1040
|
-
int neon_state = (cmap_neon != NULL) ? 1 : 4;
|
|
1041
|
-
char matches[16];
|
|
1042
|
-
bool do_hi_validation = false;
|
|
1043
|
-
// uint64_t neon_match_mask = 0;
|
|
1237
|
+
|
|
1238
|
+
#if defined(HAVE_SIMD_NEON) || defined(HAVE_SIMD_SSE4_2)
|
|
1239
|
+
|
|
1044
1240
|
#define SEARCH_FLUSH \
|
|
1045
1241
|
if (str > cursor) { \
|
|
1046
1242
|
APPEND_CHARS(out->cur, cursor, str - cursor); \
|
|
1047
1243
|
cursor = str; \
|
|
1048
1244
|
}
|
|
1049
1245
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
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
|
+
|
|
1054
1261
|
#ifdef HAVE_SIMD_NEON
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
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;
|
|
1288
|
+
}
|
|
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
|
+
}
|
|
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++;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
cursor = str;
|
|
1337
|
+
continue;
|
|
1338
|
+
}
|
|
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) {
|
|
1072
1349
|
const char *chunk_ptr = NULL;
|
|
1073
|
-
if (str + sizeof(
|
|
1350
|
+
if (str + sizeof(__m128i) <= end) {
|
|
1074
1351
|
chunk_ptr = str;
|
|
1075
1352
|
chunk_start = str;
|
|
1076
|
-
chunk_end = str + sizeof(
|
|
1353
|
+
chunk_end = str + sizeof(__m128i);
|
|
1077
1354
|
} else if ((end - str) >= SIMD_MINIMUM_THRESHOLD) {
|
|
1078
|
-
memset(out->cur, 'A', sizeof(
|
|
1355
|
+
memset(out->cur, 'A', sizeof(__m128i));
|
|
1079
1356
|
memcpy(out->cur, str, (end - str));
|
|
1080
1357
|
chunk_ptr = out->cur;
|
|
1081
1358
|
chunk_start = str;
|
|
1082
1359
|
chunk_end = end;
|
|
1083
1360
|
} else {
|
|
1084
|
-
|
|
1085
|
-
NEON_SET_STATE(4);
|
|
1086
|
-
break; /* Unreachable */
|
|
1361
|
+
break;
|
|
1087
1362
|
}
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
if ((result.do_unicode_validation) ||
|
|
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) {
|
|
1094
1369
|
SEARCH_FLUSH;
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
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++;
|
|
1381
|
+
}
|
|
1382
|
+
cursor = str;
|
|
1383
|
+
continue;
|
|
1100
1384
|
}
|
|
1101
1385
|
str = chunk_end;
|
|
1102
1386
|
}
|
|
1103
|
-
// We must have run out of data to use SIMD. Go to state 4.
|
|
1104
1387
|
SEARCH_FLUSH;
|
|
1105
|
-
NEON_SET_STATE(4);
|
|
1106
|
-
} break;
|
|
1107
|
-
case 3:
|
|
1108
|
-
cursor = str;
|
|
1109
|
-
// This fall through is intentional. We return to state 3 after we process
|
|
1110
|
-
// a byte (or multiple). We return to this state to ensure the cursor is
|
|
1111
|
-
// pointing to the correct location. We then resume looking for matches
|
|
1112
|
-
// within the previously processed chunk.
|
|
1113
|
-
case 2:
|
|
1114
|
-
if (str >= chunk_end) {
|
|
1115
|
-
NEON_SET_STATE(1);
|
|
1116
|
-
}
|
|
1117
|
-
if (!do_hi_validation) {
|
|
1118
|
-
long i = str - chunk_start;
|
|
1119
|
-
for (; str < chunk_end; i++) {
|
|
1120
|
-
if ((action = matches[i]) != '1') {
|
|
1121
|
-
break;
|
|
1122
|
-
}
|
|
1123
|
-
*out->cur++ = *str++;
|
|
1124
|
-
}
|
|
1125
|
-
// The loop above may have advanced str and directly output them to out->cur.
|
|
1126
|
-
// Ensure cursor is set appropriately.
|
|
1127
|
-
cursor = str;
|
|
1128
|
-
if (str >= chunk_end) {
|
|
1129
|
-
// We must have advanced past the end... we are done.
|
|
1130
|
-
NEON_SET_STATE(1);
|
|
1131
|
-
}
|
|
1132
|
-
} else {
|
|
1133
|
-
long match_index = str - chunk_start;
|
|
1134
|
-
action = matches[match_index];
|
|
1135
|
-
}
|
|
1136
|
-
NEON_RETURN_TO_STATE(3);
|
|
1137
|
-
break;
|
|
1138
|
-
case 4: action = cmap[(uint8_t)*str];
|
|
1139
|
-
}
|
|
1140
|
-
#undef NEON_SET_STATE
|
|
1141
|
-
#undef NEON_RETURN_TO_STATE
|
|
1142
|
-
#else
|
|
1143
|
-
action = cmap[(uint8_t)*str];
|
|
1144
|
-
#endif /* HAVE_SIMD_NEON */
|
|
1145
|
-
switch (action) {
|
|
1146
|
-
case '1':
|
|
1147
|
-
if (do_unicode_validation && check_start <= str) {
|
|
1148
|
-
if (0 != (0x80 & (uint8_t)*str)) {
|
|
1149
|
-
if (0xC0 == (0xC0 & (uint8_t)*str)) {
|
|
1150
|
-
check_start = check_unicode(str, end, orig);
|
|
1151
|
-
} else {
|
|
1152
|
-
raise_invalid_unicode(orig, (int)(end - orig), (int)(str - orig));
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
*out->cur++ = *str;
|
|
1157
|
-
break;
|
|
1158
|
-
case '2':
|
|
1159
|
-
*out->cur++ = '\\';
|
|
1160
|
-
switch (*str) {
|
|
1161
|
-
case '\\': *out->cur++ = '\\'; break;
|
|
1162
|
-
case '\b': *out->cur++ = 'b'; break;
|
|
1163
|
-
case '\t': *out->cur++ = 't'; break;
|
|
1164
|
-
case '\n': *out->cur++ = 'n'; break;
|
|
1165
|
-
case '\f': *out->cur++ = 'f'; break;
|
|
1166
|
-
case '\r': *out->cur++ = 'r'; break;
|
|
1167
|
-
default: *out->cur++ = *str; break;
|
|
1168
|
-
}
|
|
1169
|
-
break;
|
|
1170
|
-
case '3': // Unicode
|
|
1171
|
-
if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
|
|
1172
|
-
if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
|
|
1173
|
-
str = dump_unicode(str, end, out, orig);
|
|
1174
|
-
} else {
|
|
1175
|
-
check_start = check_unicode(str, end, orig);
|
|
1176
|
-
*out->cur++ = *str;
|
|
1177
|
-
}
|
|
1178
|
-
break;
|
|
1179
|
-
}
|
|
1180
|
-
str = dump_unicode(str, end, out, orig);
|
|
1181
|
-
break;
|
|
1182
|
-
case '6': // control characters
|
|
1183
|
-
if (*(uint8_t *)str < 0x80) {
|
|
1184
|
-
if (0 == (uint8_t)*str && out->opts->dump_opts.omit_null_byte) {
|
|
1185
|
-
break;
|
|
1186
|
-
}
|
|
1187
|
-
APPEND_CHARS(out->cur, "\\u00", 4);
|
|
1188
|
-
dump_hex((uint8_t)*str, out);
|
|
1189
|
-
} else {
|
|
1190
|
-
if (0xe2 == (uint8_t)*str && do_unicode_validation && 2 <= end - str) {
|
|
1191
|
-
if (0x80 == (uint8_t)str[1] && (0xa8 == (uint8_t)str[2] || 0xa9 == (uint8_t)str[2])) {
|
|
1192
|
-
str = dump_unicode(str, end, out, orig);
|
|
1193
|
-
} else {
|
|
1194
|
-
check_start = check_unicode(str, end, orig);
|
|
1195
|
-
*out->cur++ = *str;
|
|
1196
|
-
}
|
|
1197
|
-
break;
|
|
1198
|
-
}
|
|
1199
|
-
str = dump_unicode(str, end, out, orig);
|
|
1200
|
-
}
|
|
1201
|
-
break;
|
|
1202
|
-
default: break; // ignore, should never happen if the table is correct
|
|
1203
1388
|
}
|
|
1204
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
|
+
}
|
|
1205
1395
|
*out->cur++ = '"';
|
|
1206
1396
|
}
|
|
1207
1397
|
if (do_unicode_validation && 0 < str - orig && 0 != (0x80 & *(str - 1))) {
|
|
@@ -1266,10 +1456,11 @@ void oj_dump_raw(const char *str, size_t cnt, Out out) {
|
|
|
1266
1456
|
}
|
|
1267
1457
|
|
|
1268
1458
|
void oj_out_init(Out out) {
|
|
1269
|
-
out->buf
|
|
1270
|
-
out->cur
|
|
1271
|
-
out->end
|
|
1272
|
-
out->allocated
|
|
1459
|
+
out->buf = out->stack_buffer;
|
|
1460
|
+
out->cur = out->buf;
|
|
1461
|
+
out->end = out->buf + sizeof(out->stack_buffer) - BUFFER_EXTRA;
|
|
1462
|
+
out->allocated = false;
|
|
1463
|
+
out->key_filter_off = false;
|
|
1273
1464
|
}
|
|
1274
1465
|
|
|
1275
1466
|
void oj_out_free(Out out) {
|
|
@@ -1541,6 +1732,11 @@ void oj_dump_float(VALUE obj, int depth, Out out, bool as_ok) {
|
|
|
1541
1732
|
size_t oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const char *format) {
|
|
1542
1733
|
size_t cnt = snprintf(buf, blen, format, d);
|
|
1543
1734
|
|
|
1735
|
+
// snprintf() returns the length the output would have needed, which is
|
|
1736
|
+
// more than it wrote when the format asks for more room than buf has.
|
|
1737
|
+
if (blen <= cnt) {
|
|
1738
|
+
cnt = blen - 1;
|
|
1739
|
+
}
|
|
1544
1740
|
// Round off issues at 16 significant digits so check for obvious ones of
|
|
1545
1741
|
// 0001 and 9999.
|
|
1546
1742
|
if (17 <= cnt && (0 == strcmp("0001", buf + cnt - 4) || 0 == strcmp("9999", buf + cnt - 4))) {
|
|
@@ -1551,3 +1747,34 @@ size_t oj_dump_float_printf(char *buf, size_t blen, VALUE obj, double d, const c
|
|
|
1551
1747
|
}
|
|
1552
1748
|
return cnt;
|
|
1553
1749
|
}
|
|
1750
|
+
|
|
1751
|
+
// The only and except lists are the keys joined with a ':' and wrapped in one
|
|
1752
|
+
// at each end, so a key is in a list when ":key:" is in it. Matching in place
|
|
1753
|
+
// finds what searching the list for a copy of the key with the colons on it
|
|
1754
|
+
// found, without a buffer sized by the key.
|
|
1755
|
+
static bool key_listed(const char *list, const char *key, size_t klen) {
|
|
1756
|
+
const char *s;
|
|
1757
|
+
|
|
1758
|
+
for (s = list; NULL != (s = strchr(s, ':')); s++) {
|
|
1759
|
+
if (0 == strncmp(s + 1, key, klen) && ':' == s[klen + 1]) {
|
|
1760
|
+
return true;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
return false;
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
bool oj_key_skip(VALUE key, const char *only, const char *except) {
|
|
1767
|
+
const char *skey;
|
|
1768
|
+
|
|
1769
|
+
switch (rb_type(key)) {
|
|
1770
|
+
case RUBY_T_STRING: skey = StringValueCStr(key); break;
|
|
1771
|
+
case RUBY_T_SYMBOL: skey = rb_id2name(rb_sym2id(key)); break;
|
|
1772
|
+
default: skey = NULL; break;
|
|
1773
|
+
}
|
|
1774
|
+
if (NULL != skey && '\0' != *skey) {
|
|
1775
|
+
size_t klen = strlen(skey);
|
|
1776
|
+
|
|
1777
|
+
return ((NULL != only && !key_listed(only, skey, klen)) || (NULL != except && key_listed(except, skey, klen)));
|
|
1778
|
+
}
|
|
1779
|
+
return NULL != only;
|
|
1780
|
+
}
|