]> granicus.if.org Git - php/commitdiff
Avoid string reallocations in preg_quote()
authorDmitry Stogov <dmitry@zend.com>
Tue, 6 Jun 2017 10:56:19 +0000 (13:56 +0300)
committerDmitry Stogov <dmitry@zend.com>
Tue, 6 Jun 2017 10:56:19 +0000 (13:56 +0300)
ext/opcache/Optimizer/zend_func_info.c
ext/pcre/php_pcre.c

index 6cb55a11a87293f12e050ad2f1d3de543c3ed658..33138ff6f4f881c689fec8acd0f7775fcaf13ec1 100644 (file)
@@ -928,7 +928,7 @@ static const func_info_t func_infos[] = {
        FN("preg_replace_callback",                     MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
        F1("preg_filter",                                           MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
        F1("preg_split",                                            MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
-       F1("preg_quote",                                            MAY_BE_NULL | MAY_BE_STRING),
+       FN("preg_quote",                                            MAY_BE_NULL | MAY_BE_STRING),
        F1("preg_grep",                                         MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
        F0("preg_last_error",                               MAY_BE_NULL | MAY_BE_LONG),
 
index 808036b7d8b6cffd790c2cdc8ed8be7e83ad41e0..d21dd52380e62acdb6030cd49883e464b38103fd 100644 (file)
@@ -2037,43 +2037,90 @@ PHPAPI void php_pcre_split_impl(pcre_cache_entry *pce, char *subject, int subjec
    Quote regular expression characters plus an optional character */
 static PHP_FUNCTION(preg_quote)
 {
-       size_t           in_str_len;
-       char    *in_str;                /* Input string argument */
-       char    *in_str_end;    /* End of the input string */
-       size_t           delim_len = 0;
-       char    *delim = NULL;  /* Additional delimiter argument */
-       zend_string     *out_str;       /* Output string with quoted characters */
-       char    *p,                             /* Iterator for input string */
-                       *q,                             /* Iterator for output string */
-                        delim_char=0,  /* Delimiter character to be quoted */
-                        c;                             /* Current character */
-       zend_bool quote_delim = 0; /* Whether to quote additional delim char */
+       zend_string *str;                       /* Input string argument */
+       zend_string     *delim = NULL;          /* Additional delimiter argument */
+       char            *in_str;                        /* Input string */
+       char            *in_str_end;            /* End of the input string */
+       zend_string     *out_str;                       /* Output string with quoted characters */
+       size_t       extra_len;         /* Number of additional characters */
+       char            *p,                                     /* Iterator for input string */
+                               *q,                                     /* Iterator for output string */
+                                delim_char = '\0',     /* Delimiter character to be quoted */
+                                c;                                     /* Current character */
 
        /* Get the arguments and check for errors */
        ZEND_PARSE_PARAMETERS_START(1, 2)
-               Z_PARAM_STRING(in_str, in_str_len)
+               Z_PARAM_STR(str)
                Z_PARAM_OPTIONAL
-               Z_PARAM_STRING(delim, delim_len)
+               Z_PARAM_STR(delim)
        ZEND_PARSE_PARAMETERS_END();
 
-       in_str_end = in_str + in_str_len;
-
        /* Nothing to do if we got an empty string */
-       if (in_str == in_str_end) {
+       if (ZSTR_LEN(str) == 0) {
                RETURN_EMPTY_STRING();
        }
 
-       if (delim && *delim) {
-               delim_char = delim[0];
-               quote_delim = 1;
+       in_str = ZSTR_VAL(str);
+       in_str_end = in_str + ZSTR_LEN(str);
+
+       if (delim) {
+               delim_char = ZSTR_VAL(delim)[0];
+       }
+
+       /* Go through the string and quote necessary characters */
+       extra_len = 0;
+       p = in_str;
+       do {
+               c = *p;
+               switch(c) {
+                       case '.':
+                       case '\\':
+                       case '+':
+                       case '*':
+                       case '?':
+                       case '[':
+                       case '^':
+                       case ']':
+                       case '$':
+                       case '(':
+                       case ')':
+                       case '{':
+                       case '}':
+                       case '=':
+                       case '!':
+                       case '>':
+                       case '<':
+                       case '|':
+                       case ':':
+                       case '-':
+                               extra_len++;
+                               break;
+
+                       case '\0':
+                               extra_len+=3;
+                               break;
+
+                       default:
+                               if (c == delim_char) {
+                                       extra_len++;
+                               }
+                               break;
+               }
+               p++;
+       } while (p != in_str_end);
+
+       if (extra_len == 0) {
+               RETURN_STR_COPY(str);
        }
 
+do_quote:
        /* Allocate enough memory so that even if each character
           is quoted, we won't run out of room */
-       out_str = zend_string_safe_alloc(4, in_str_len, 0, 0);
+       out_str = zend_string_safe_alloc(1, ZSTR_LEN(str), extra_len, 0);
+       q = ZSTR_VAL(out_str);
+       p = in_str;
 
-       /* Go through the string and quote necessary characters */
-       for (p = in_str, q = ZSTR_VAL(out_str); p != in_str_end; p++) {
+       do {
                c = *p;
                switch(c) {
                        case '.':
@@ -2108,16 +2155,16 @@ static PHP_FUNCTION(preg_quote)
                                break;
 
                        default:
-                               if (quote_delim && c == delim_char)
+                               if (c == delim_char) {
                                        *q++ = '\\';
+                               }
                                *q++ = c;
                                break;
                }
-       }
+               p++;
+       } while (p != in_str_end);
        *q = '\0';
 
-       /* Reallocate string and return it */
-       out_str = zend_string_truncate(out_str, q - ZSTR_VAL(out_str), 0);
        RETURN_NEW_STR(out_str);
 }
 /* }}} */