Skip to content

Commit a3dd262

Browse files
committed
wip
1 parent 4373c60 commit a3dd262

File tree

1 file changed

+143
-65
lines changed

1 file changed

+143
-65
lines changed

ext/json/json_encoder.c

Lines changed: 143 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
#include "zend_property_hooks.h"
3131
#include "zend_lazy_objects.h"
3232

33+
#include <nmmintrin.h>
34+
# pragma GCC push_options
35+
# pragma GCC target ("sse4.2")
36+
3337
static const char digits[] = "0123456789abcdef";
3438

3539
static zend_always_inline bool php_json_check_stack_limit(void)
@@ -366,6 +370,68 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
366370
}
367371
/* }}} */
368372

373+
static zend_always_inline bool php_json_printable_ascii_escape(smart_str *buf, unsigned char us, int options)
374+
{
375+
switch (us) {
376+
case '"':
377+
if (options & PHP_JSON_HEX_QUOT) {
378+
smart_str_appendl(buf, "\\u0022", 6);
379+
} else {
380+
smart_str_appendl(buf, "\\\"", 2);
381+
}
382+
break;
383+
384+
case '\\':
385+
smart_str_appendl(buf, "\\\\", 2);
386+
break;
387+
388+
case '/':
389+
if (options & PHP_JSON_UNESCAPED_SLASHES) {
390+
smart_str_appendc(buf, '/');
391+
} else {
392+
smart_str_appendl(buf, "\\/", 2);
393+
}
394+
break;
395+
396+
case '<':
397+
if (options & PHP_JSON_HEX_TAG) {
398+
smart_str_appendl(buf, "\\u003C", 6);
399+
} else {
400+
smart_str_appendc(buf, '<');
401+
}
402+
break;
403+
404+
case '>':
405+
if (options & PHP_JSON_HEX_TAG) {
406+
smart_str_appendl(buf, "\\u003E", 6);
407+
} else {
408+
smart_str_appendc(buf, '>');
409+
}
410+
break;
411+
412+
case '&':
413+
if (options & PHP_JSON_HEX_AMP) {
414+
smart_str_appendl(buf, "\\u0026", 6);
415+
} else {
416+
smart_str_appendc(buf, '&');
417+
}
418+
break;
419+
420+
case '\'':
421+
if (options & PHP_JSON_HEX_APOS) {
422+
smart_str_appendl(buf, "\\u0027", 6);
423+
} else {
424+
smart_str_appendc(buf, '\'');
425+
}
426+
break;
427+
428+
default:
429+
return false;
430+
}
431+
432+
return true;
433+
}
434+
369435
zend_result php_json_escape_string(
370436
smart_str *buf, const char *s, size_t len,
371437
int options, php_json_encoder *encoder) /* {{{ */
@@ -408,21 +474,79 @@ zend_result php_json_escape_string(
408474
0xffffffff, 0x500080c4, 0x10000000, 0x00000000,
409475
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
410476

477+
while (len >= sizeof(__m128i)) {
478+
const __m128i input = _mm_loadu_si128((__m128i *) (s + pos));
479+
const __m128i input_range = _mm_cmpgt_epi8(input, _mm_set1_epi8(31));
480+
481+
int input_range_mask = _mm_movemask_epi8(input_range);
482+
if (input_range_mask != 0xffff) {
483+
int shift = __builtin_clz(~input_range_mask);
484+
pos += shift;
485+
len -= shift;
486+
break;
487+
}
488+
489+
#if 0
490+
const __m128i result_34 = _mm_cmpeq_epi8(input, _mm_set1_epi8(34));
491+
const __m128i result_38 = _mm_cmpeq_epi8(input, _mm_set1_epi8(38));
492+
const __m128i result_39 = _mm_cmpeq_epi8(input, _mm_set1_epi8(39));
493+
const __m128i result_47 = _mm_cmpeq_epi8(input, _mm_set1_epi8(47));
494+
const __m128i result_60 = _mm_cmpeq_epi8(input, _mm_set1_epi8(60));
495+
const __m128i result_62 = _mm_cmpeq_epi8(input, _mm_set1_epi8(62));
496+
const __m128i result_92 = _mm_cmpeq_epi8(input, _mm_set1_epi8(92));
497+
498+
const __m128i result_34_38 = _mm_or_si128(result_34, result_38);
499+
const __m128i result_39_47 = _mm_or_si128(result_39, result_47);
500+
const __m128i result_60_62 = _mm_or_si128(result_60, result_62);
501+
502+
const __m128i result_34_38_39_47 = _mm_or_si128(result_34_38, result_39_47);
503+
const __m128i result_60_62_92 = _mm_or_si128(result_60_62, result_92);
504+
505+
const __m128i result_individual_bytes = _mm_or_si128(result_34_38_39_47, result_60_62_92);
506+
int mask = _mm_movemask_epi8(result_individual_bytes);
507+
#else
508+
const __m128i result_individual_bytes = _mm_cmpistrm(_mm_setr_epi8(34, 38, 39, 47, 60, 62, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0), input, _SIDD_SBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK);
509+
int mask = _mm_cvtsi128_si32(result_individual_bytes);
510+
#endif
511+
int acc = 0;
512+
if (mask != 0) {
513+
do {
514+
int toggle = mask & -mask;
515+
int bit = __builtin_ctz(mask);
516+
mask ^= toggle;
517+
518+
int len = bit - acc;
519+
smart_str_appendl(buf, s, len + pos);
520+
521+
acc += len + 1;
522+
pos += len;
523+
us = (unsigned char) s[pos++];
524+
s += pos;
525+
pos = 0;
526+
527+
bool handled = php_json_printable_ascii_escape(buf, us, options);
528+
ZEND_ASSERT(handled == true);
529+
} while (mask != 0);
530+
}
531+
532+
len -= sizeof(__m128i);
533+
pos += sizeof(__m128i) - acc;
534+
}
535+
536+
if (!len) {
537+
break;
538+
}
539+
411540
us = (unsigned char)s[pos];
412541
if (EXPECTED(!ZEND_BIT_TEST(charmap, us))) {
413542
pos++;
414543
len--;
415-
if (len == 0) {
416-
smart_str_appendl(buf, s, pos);
417-
break;
418-
}
419544
} else {
420545
if (pos) {
421546
smart_str_appendl(buf, s, pos);
422547
s += pos;
423548
pos = 0;
424549
}
425-
us = (unsigned char)s[0];
426550
if (UNEXPECTED(us >= 0x80)) {
427551
zend_result status;
428552
us = php_next_utf8_char((unsigned char *)s, len, &pos, &status);
@@ -485,26 +609,6 @@ zend_result php_json_escape_string(
485609
} else {
486610
s++;
487611
switch (us) {
488-
case '"':
489-
if (options & PHP_JSON_HEX_QUOT) {
490-
smart_str_appendl(buf, "\\u0022", 6);
491-
} else {
492-
smart_str_appendl(buf, "\\\"", 2);
493-
}
494-
break;
495-
496-
case '\\':
497-
smart_str_appendl(buf, "\\\\", 2);
498-
break;
499-
500-
case '/':
501-
if (options & PHP_JSON_UNESCAPED_SLASHES) {
502-
smart_str_appendc(buf, '/');
503-
} else {
504-
smart_str_appendl(buf, "\\/", 2);
505-
}
506-
break;
507-
508612
case '\b':
509613
smart_str_appendl(buf, "\\b", 2);
510614
break;
@@ -525,54 +629,28 @@ zend_result php_json_escape_string(
525629
smart_str_appendl(buf, "\\t", 2);
526630
break;
527631

528-
case '<':
529-
if (options & PHP_JSON_HEX_TAG) {
530-
smart_str_appendl(buf, "\\u003C", 6);
531-
} else {
532-
smart_str_appendc(buf, '<');
533-
}
534-
break;
535-
536-
case '>':
537-
if (options & PHP_JSON_HEX_TAG) {
538-
smart_str_appendl(buf, "\\u003E", 6);
539-
} else {
540-
smart_str_appendc(buf, '>');
541-
}
542-
break;
543-
544-
case '&':
545-
if (options & PHP_JSON_HEX_AMP) {
546-
smart_str_appendl(buf, "\\u0026", 6);
547-
} else {
548-
smart_str_appendc(buf, '&');
549-
}
550-
break;
551-
552-
case '\'':
553-
if (options & PHP_JSON_HEX_APOS) {
554-
smart_str_appendl(buf, "\\u0027", 6);
555-
} else {
556-
smart_str_appendc(buf, '\'');
557-
}
558-
break;
559-
560632
default:
561-
ZEND_ASSERT(us < ' ');
562-
dst = smart_str_extend(buf, 6);
563-
dst[0] = '\\';
564-
dst[1] = 'u';
565-
dst[2] = '0';
566-
dst[3] = '0';
567-
dst[4] = digits[(us >> 4) & 0xf];
568-
dst[5] = digits[us & 0xf];
633+
if (!php_json_printable_ascii_escape(buf, us, options)) {
634+
ZEND_ASSERT(us < ' ');
635+
dst = smart_str_extend(buf, 6);
636+
dst[0] = '\\';
637+
dst[1] = 'u';
638+
dst[2] = '0';
639+
dst[3] = '0';
640+
dst[4] = digits[(us >> 4) & 0xf];
641+
dst[5] = digits[us & 0xf];
642+
}
569643
break;
570644
}
571645
len--;
572646
}
573647
}
574648
} while (len);
575649

650+
if (pos) {
651+
smart_str_appendl(buf, s, pos);
652+
}
653+
576654
smart_str_appendc(buf, '"');
577655

578656
return SUCCESS;

0 commit comments

Comments
 (0)