Skip to content

Commit dfd6de0

Browse files
committed
split off
1 parent 45e91f5 commit dfd6de0

File tree

1 file changed

+80
-68
lines changed

1 file changed

+80
-68
lines changed

ext/json/json_encoder.c

Lines changed: 80 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -526,14 +526,90 @@ typedef int (*php_json_compute_escape_intersection_t)(const __m128i mask, const
526526

527527
ZEND_NO_SANITIZE_ADDRESS
528528
ZEND_ATTRIBUTE_UNUSED /* clang mistakenly warns about this */
529-
static php_json_compute_escape_intersection_t resolve_json_escape_intersection(void) {
529+
static php_json_compute_escape_intersection_t resolve_json_escape_intersection(void) { // TODO: rename
530530
if (zend_cpu_supports_sse42()) {
531531
return php_json_sse42_compute_escape_intersection_real;
532532
}
533533
return php_json_sse2_compute_escape_intersection;
534534
}
535535
#endif
536536

537+
#ifdef JSON_USE_SIMD
538+
typedef enum php_json_simd_result {
539+
PHP_JSON_STOP,
540+
PHP_JSON_SLOW,
541+
PHP_JSON_NON_ASCII,
542+
} php_json_simd_result;
543+
544+
static zend_always_inline php_json_simd_result php_json_process_simd_block(smart_str *buf, const __m128i sse_escape_mask, const char **s, size_t *pos, size_t *len, int options)
545+
{
546+
while (*len >= sizeof(__m128i)) {
547+
const __m128i input = _mm_loadu_si128((const __m128i *) (*s + *pos));
548+
/* signed compare, so checks for unsigned bytes >= 0x80 as well */
549+
const __m128i input_range = _mm_cmplt_epi8(input, _mm_set1_epi8(32));
550+
551+
int max_shift = sizeof(__m128i);
552+
553+
int input_range_mask = _mm_movemask_epi8(input_range);
554+
if (input_range_mask != 0) {
555+
if (UNEXPECTED(input_range_mask & 1)) {
556+
/* not worth it */
557+
return PHP_JSON_NON_ASCII;
558+
}
559+
max_shift = zend_ulong_ntz(input_range_mask);
560+
}
561+
562+
#ifdef ZEND_INTRIN_SSE4_2_NATIVE
563+
int mask = php_json_sse42_compute_escape_intersection_real(sse_escape_mask, input);
564+
#elif defined(ZEND_INTRIN_SSE4_2_FUNC_PROTO)
565+
int mask = php_json_sse42_compute_escape_intersection(sse_escape_mask, input);
566+
#else
567+
int mask = php_json_sse2_compute_escape_intersection(_mm_setzero_si128(), input);
568+
#endif
569+
if (mask != 0) {
570+
if (UNEXPECTED(max_shift < sizeof(__m128i))) {
571+
int shift = zend_ulong_ntz(mask); /* first offending character */
572+
*pos += MIN(max_shift, shift);
573+
*len -= MIN(max_shift, shift);
574+
return PHP_JSON_SLOW;
575+
}
576+
577+
php_json_append(buf, *s, *pos);
578+
*s += *pos;
579+
const char *s_backup = *s;
580+
581+
do {
582+
/* Note that we shift the input forward, so we have to shift the mask as well,
583+
* beyond the to-be-escaped character */
584+
int len = zend_ulong_ntz(mask);
585+
mask >>= len + 1;
586+
587+
php_json_append(buf, *s, len);
588+
589+
unsigned char us = (unsigned char)(*s)[len];
590+
*s += len + 1; /* skip 'us' too */
591+
592+
bool handled = php_json_printable_ascii_escape(buf, us, options);
593+
ZEND_ASSERT(handled == true);
594+
} while (mask != 0);
595+
596+
*pos = sizeof(__m128i) - (*s - s_backup);
597+
} else {
598+
if (UNEXPECTED(max_shift < sizeof(__m128i))) {
599+
*pos += max_shift;
600+
*len -= max_shift;
601+
return PHP_JSON_SLOW;
602+
}
603+
*pos += sizeof(__m128i);
604+
}
605+
606+
*len -= sizeof(__m128i);
607+
}
608+
609+
return !*len ? PHP_JSON_STOP : PHP_JSON_SLOW;
610+
}
611+
#endif
612+
537613
zend_result php_json_escape_string(
538614
smart_str *buf, const char *s, size_t len,
539615
int options, php_json_encoder *encoder) /* {{{ */
@@ -581,81 +657,17 @@ zend_result php_json_escape_string(
581657
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
582658

583659
#ifdef JSON_USE_SIMD
584-
while (len >= sizeof(__m128i)) {
585-
const __m128i input = _mm_loadu_si128((__m128i *) (s + pos));
586-
/* signed compare, so checks for unsigned bytes >= 0x80 as well */
587-
const __m128i input_range = _mm_cmplt_epi8(input, _mm_set1_epi8(32));
588-
589-
int max_shift = sizeof(__m128i);
590-
591-
int input_range_mask = _mm_movemask_epi8(input_range);
592-
if (input_range_mask != 0) {
593-
if (UNEXPECTED(input_range_mask & 1)) {
594-
/* not worth it */
595-
us = (unsigned char)s[pos];
596-
goto fallback;
597-
}
598-
max_shift = zend_ulong_ntz(input_range_mask);
599-
}
600-
601-
#ifdef ZEND_INTRIN_SSE4_2_NATIVE
602-
int mask = php_json_sse42_compute_escape_intersection_real(sse_escape_mask, input);
603-
#elif defined(ZEND_INTRIN_SSE4_2_FUNC_PROTO)
604-
int mask = php_json_sse42_compute_escape_intersection(sse_escape_mask, input);
605-
#else
606-
int mask = php_json_sse2_compute_escape_intersection(_mm_setzero_si128(), input);
607-
#endif
608-
if (mask != 0) {
609-
if (UNEXPECTED(max_shift < sizeof(__m128i))) {
610-
int shift = zend_ulong_ntz(mask); /* first offending character */
611-
pos += MIN(max_shift, shift);
612-
len -= MIN(max_shift, shift);
613-
break;
614-
}
615-
616-
php_json_append(buf, s, pos);
617-
s += pos;
618-
const char *s_backup = s;
619-
620-
do {
621-
/* Note that we shift the input forward, so we have to shift the mask as well,
622-
* beyond the to-be-escaped character */
623-
int len = zend_ulong_ntz(mask);
624-
mask >>= len + 1;
625-
626-
php_json_append(buf, s, len);
627-
628-
us = (unsigned char)s[len];
629-
s += len + 1; /* skip 'us' too */
630-
631-
bool handled = php_json_printable_ascii_escape(buf, us, options);
632-
ZEND_ASSERT(handled == true);
633-
} while (mask != 0);
634-
635-
pos = sizeof(__m128i) - (s - s_backup);
636-
} else {
637-
if (UNEXPECTED(max_shift < sizeof(__m128i))) {
638-
pos += max_shift;
639-
len -= max_shift;
640-
break;
641-
}
642-
pos += sizeof(__m128i);
643-
}
644-
645-
len -= sizeof(__m128i);
646-
}
647-
648-
if (!len) {
660+
php_json_simd_result result = php_json_process_simd_block(buf, sse_escape_mask, &s, &pos, &len, options);
661+
if (UNEXPECTED(result == PHP_JSON_STOP)) {
649662
break;
650663
}
651664
#endif
652665

653666
us = (unsigned char)s[pos];
654-
if (EXPECTED(!ZEND_BIT_TEST(charmap, us))) {
667+
if (EXPECTED(result != PHP_JSON_NON_ASCII && !ZEND_BIT_TEST(charmap, us))) {
655668
pos++;
656669
len--;
657670
} else {
658-
fallback:;
659671
if (UNEXPECTED(us >= 0x80)) {
660672
zend_result status;
661673
size_t pos_old = pos;

0 commit comments

Comments
 (0)