30
30
#include "zend_property_hooks.h"
31
31
#include "zend_lazy_objects.h"
32
32
33
+ #include <nmmintrin.h>
34
+ # pragma GCC push_options
35
+ # pragma GCC target ("sse4.2")
36
+
33
37
static const char digits [] = "0123456789abcdef" ;
34
38
35
39
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,
366
370
}
367
371
/* }}} */
368
372
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
+
369
435
zend_result php_json_escape_string (
370
436
smart_str * buf , const char * s , size_t len ,
371
437
int options , php_json_encoder * encoder ) /* {{{ */
@@ -408,21 +474,79 @@ zend_result php_json_escape_string(
408
474
0xffffffff , 0x500080c4 , 0x10000000 , 0x00000000 ,
409
475
0xffffffff , 0xffffffff , 0xffffffff , 0xffffffff };
410
476
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
+
411
540
us = (unsigned char )s [pos ];
412
541
if (EXPECTED (!ZEND_BIT_TEST (charmap , us ))) {
413
542
pos ++ ;
414
543
len -- ;
415
- if (len == 0 ) {
416
- smart_str_appendl (buf , s , pos );
417
- break ;
418
- }
419
544
} else {
420
545
if (pos ) {
421
546
smart_str_appendl (buf , s , pos );
422
547
s += pos ;
423
548
pos = 0 ;
424
549
}
425
- us = (unsigned char )s [0 ];
426
550
if (UNEXPECTED (us >= 0x80 )) {
427
551
zend_result status ;
428
552
us = php_next_utf8_char ((unsigned char * )s , len , & pos , & status );
@@ -485,26 +609,6 @@ zend_result php_json_escape_string(
485
609
} else {
486
610
s ++ ;
487
611
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
-
508
612
case '\b' :
509
613
smart_str_appendl (buf , "\\b" , 2 );
510
614
break ;
@@ -525,54 +629,28 @@ zend_result php_json_escape_string(
525
629
smart_str_appendl (buf , "\\t" , 2 );
526
630
break ;
527
631
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
-
560
632
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
+ }
569
643
break ;
570
644
}
571
645
len -- ;
572
646
}
573
647
}
574
648
} while (len );
575
649
650
+ if (pos ) {
651
+ smart_str_appendl (buf , s , pos );
652
+ }
653
+
576
654
smart_str_appendc (buf , '"' );
577
655
578
656
return SUCCESS ;
0 commit comments