@@ -3007,23 +3007,69 @@ static void php_strtr_array(zval *return_value, zend_string *input, HashTable *p
30073007}
30083008/* }}} */
30093009
3010+ /* {{{ count_chars */
3011+ static zend_always_inline zend_long count_chars (const char * p , zend_long length , char ch )
3012+ {
3013+ zend_long count = 0 ;
3014+ const char * endp ;
3015+
3016+ #ifdef __SSE2__
3017+ if (length >= sizeof (__m128i )) {
3018+ __m128i search = _mm_set1_epi8 (ch );
3019+
3020+ do {
3021+ __m128i src = _mm_loadu_si128 ((__m128i * )(p ));
3022+ uint32_t mask = _mm_movemask_epi8 (_mm_cmpeq_epi8 (src , search ));
3023+ // TODO: It would be great to use POPCNT, but it's available only with SSE4.1
3024+ #if 1
3025+ while (mask != 0 ) {
3026+ count ++ ;
3027+ mask = mask & (mask - 1 );
3028+ }
3029+ #else
3030+ if (mask ) {
3031+ mask = mask - ((mask >> 1 ) & 0x5555 );
3032+ mask = (mask & 0x3333 ) + ((mask >> 2 ) & 0x3333 );
3033+ mask = (mask + (mask >> 4 )) & 0x0F0F ;
3034+ mask = (mask + (mask >> 8 )) & 0x00ff ;
3035+ count += mask ;
3036+ }
3037+ #endif
3038+ p += sizeof (__m128i );
3039+ length -= sizeof (__m128i );
3040+ } while (length >= sizeof (__m128i ));
3041+ }
3042+ endp = p + length ;
3043+ while (p != endp ) {
3044+ count += (* p == ch );
3045+ p ++ ;
3046+ }
3047+ #else
3048+ endp = p + length ;
3049+ while ((p = memchr (p , ch , endp - p ))) {
3050+ count ++ ;
3051+ p ++ ;
3052+ }
3053+ #endif
3054+ return count ;
3055+ }
3056+ /* }}} */
3057+
30103058/* {{{ php_char_to_str_ex */
30113059static zend_string * php_char_to_str_ex (zend_string * str , char from , char * to , size_t to_len , int case_sensitivity , zend_long * replace_count )
30123060{
30133061 zend_string * result ;
3014- size_t char_count = 0 ;
3062+ size_t char_count ;
30153063 int lc_from = 0 ;
3016- const char * source , * source_end = ZSTR_VAL ( str ) + ZSTR_LEN ( str ) ;
3064+ const char * source , * source_end ;
30173065 char * target ;
30183066
30193067 if (case_sensitivity ) {
3020- char * p = ZSTR_VAL (str ), * e = p + ZSTR_LEN (str );
3021- while ((p = memchr (p , from , (e - p )))) {
3022- char_count ++ ;
3023- p ++ ;
3024- }
3068+ char_count = count_chars (ZSTR_VAL (str ), ZSTR_LEN (str ), from );
30253069 } else {
30263070 lc_from = tolower (from );
3071+ char_count = 0 ;
3072+ source_end = ZSTR_VAL (str ) + ZSTR_LEN (str );
30273073 for (source = ZSTR_VAL (str ); source < source_end ; source ++ ) {
30283074 if (tolower (* source ) == lc_from ) {
30293075 char_count ++ ;
@@ -3035,6 +3081,10 @@ static zend_string* php_char_to_str_ex(zend_string *str, char from, char *to, si
30353081 return zend_string_copy (str );
30363082 }
30373083
3084+ if (replace_count ) {
3085+ * replace_count += char_count ;
3086+ }
3087+
30383088 if (to_len > 0 ) {
30393089 result = zend_string_safe_alloc (char_count , to_len - 1 , ZSTR_LEN (str ), 0 );
30403090 } else {
@@ -3044,27 +3094,24 @@ static zend_string* php_char_to_str_ex(zend_string *str, char from, char *to, si
30443094
30453095 if (case_sensitivity ) {
30463096 char * p = ZSTR_VAL (str ), * e = p + ZSTR_LEN (str ), * s = ZSTR_VAL (str );
3097+
30473098 while ((p = memchr (p , from , (e - p )))) {
30483099 memcpy (target , s , (p - s ));
30493100 target += p - s ;
30503101 memcpy (target , to , to_len );
30513102 target += to_len ;
30523103 p ++ ;
30533104 s = p ;
3054- if (replace_count ) {
3055- * replace_count += 1 ;
3056- }
3105+ if (-- char_count == 0 ) break ;
30573106 }
30583107 if (s < e ) {
30593108 memcpy (target , s , (e - s ));
30603109 target += e - s ;
30613110 }
30623111 } else {
3112+ source_end = ZSTR_VAL (str ) + ZSTR_LEN (str );
30633113 for (source = ZSTR_VAL (str ); source < source_end ; source ++ ) {
30643114 if (tolower (* source ) == lc_from ) {
3065- if (replace_count ) {
3066- * replace_count += 1 ;
3067- }
30683115 memcpy (target , to , to_len );
30693116 target += to_len ;
30703117 } else {
@@ -5550,10 +5597,9 @@ PHP_FUNCTION(substr_count)
55505597 char * haystack , * needle ;
55515598 zend_long offset = 0 , length = 0 ;
55525599 bool length_is_null = 1 ;
5553- zend_long count = 0 ;
5600+ zend_long count ;
55545601 size_t haystack_len , needle_len ;
55555602 const char * p , * endp ;
5556- char cmp ;
55575603
55585604 ZEND_PARSE_PARAMETERS_START (2 , 4 )
55595605 Z_PARAM_STRING (haystack , haystack_len )
@@ -5569,37 +5615,36 @@ PHP_FUNCTION(substr_count)
55695615 }
55705616
55715617 p = haystack ;
5572- endp = p + haystack_len ;
55735618
5574- if (offset < 0 ) {
5575- offset += (zend_long )haystack_len ;
5576- }
5577- if ((offset < 0 ) || ((size_t )offset > haystack_len )) {
5578- zend_argument_value_error (3 , "must be contained in argument #1 ($haystack)" );
5579- RETURN_THROWS ();
5619+ if (offset ) {
5620+ if (offset < 0 ) {
5621+ offset += (zend_long )haystack_len ;
5622+ }
5623+ if ((offset < 0 ) || ((size_t )offset > haystack_len )) {
5624+ zend_argument_value_error (3 , "must be contained in argument #1 ($haystack)" );
5625+ RETURN_THROWS ();
5626+ }
5627+ p += offset ;
5628+ haystack_len -= offset ;
55805629 }
5581- p += offset ;
55825630
55835631 if (!length_is_null ) {
5584-
55855632 if (length < 0 ) {
5586- length += ( haystack_len - offset ) ;
5633+ length += haystack_len ;
55875634 }
5588- if (length < 0 || ((size_t )length > ( haystack_len - offset ) )) {
5635+ if (length < 0 || ((size_t )length > haystack_len )) {
55895636 zend_argument_value_error (4 , "must be contained in argument #1 ($haystack)" );
55905637 RETURN_THROWS ();
55915638 }
5592- endp = p + length ;
5639+ } else {
5640+ length = haystack_len ;
55935641 }
55945642
55955643 if (needle_len == 1 ) {
5596- cmp = needle [0 ];
5597-
5598- while ((p = memchr (p , cmp , endp - p ))) {
5599- count ++ ;
5600- p ++ ;
5601- }
5644+ count = count_chars (p , length , needle [0 ]);
56025645 } else {
5646+ count = 0 ;
5647+ endp = p + length ;
56035648 while ((p = (char * )php_memnstr (p , needle , needle_len , endp ))) {
56045649 p += needle_len ;
56055650 count ++ ;
0 commit comments