diff options
Diffstat (limited to 'Zend/zend_operators.c')
| -rw-r--r-- | Zend/zend_operators.c | 233 |
1 files changed, 164 insertions, 69 deletions
diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 5dcd0c62e4..8047aa6019 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -135,52 +135,6 @@ ZEND_API zend_long zend_atol(const char *str, int str_len) /* {{{ */ } /* }}} */ -ZEND_API double zend_string_to_double(const char *number, uint32_t length) /* {{{ */ -{ - double divisor = 10.0; - double result = 0.0; - double exponent; - const char *end = number+length; - const char *digit = number; - - if (!length) { - return result; - } - - while (digit < end) { - if ((*digit <= '9' && *digit >= '0')) { - result *= 10; - result += *digit - '0'; - } else if (*digit == '.') { - digit++; - break; - } else if (toupper(*digit) == 'E') { - exponent = (double) atoi(digit+1); - result *= pow(10.0, exponent); - return result; - } else { - return result; - } - digit++; - } - - while (digit < end) { - if ((*digit <= '9' && *digit >= '0')) { - result += (*digit - '0') / divisor; - divisor *= 10; - } else if (toupper(*digit) == 'E') { - exponent = (double) atoi(digit+1); - result *= pow(10.0, exponent); - return result; - } else { - return result; - } - digit++; - } - return result; -} -/* }}} */ - ZEND_API void convert_scalar_to_number(zval *op TSRMLS_DC) /* {{{ */ { try_again: @@ -405,7 +359,7 @@ ZEND_API void convert_to_long_base(zval *op, int base) /* {{{ */ { zend_string *str = Z_STR_P(op); - ZVAL_LONG(op, strtol(str->val, NULL, base)); + ZVAL_LONG(op, ZEND_STRTOL(str->val, NULL, base)); zend_string_release(str); } break; @@ -586,6 +540,7 @@ ZEND_API void _convert_to_cstring(zval *op ZEND_FILE_LINE_DC) /* {{{ */ ZEND_API void _convert_to_string(zval *op ZEND_FILE_LINE_DC) /* {{{ */ { switch (Z_TYPE_P(op)) { + case IS_UNDEF: case IS_NULL: case IS_FALSE: { TSRMLS_FETCH(); @@ -868,6 +823,7 @@ ZEND_API zend_string *_zval_get_string_func(zval *op TSRMLS_DC) /* {{{ */ { try_again: switch (Z_TYPE_P(op)) { + case IS_UNDEF: case IS_NULL: case IS_FALSE: return STR_EMPTY_ALLOC(); @@ -1575,29 +1531,33 @@ ZEND_API int concat_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) /* {{ op2 = &op2_copy; } - if (result==op1 && !IS_INTERNED(Z_STR_P(op1))) { /* special case, perform operations on result */ + { size_t op1_len = Z_STRLEN_P(op1); size_t op2_len = Z_STRLEN_P(op2); - size_t res_len = op1_len + op2_len; + size_t result_len = op1_len + op2_len; + zend_string *result_str; - if (Z_STRLEN_P(result) < 0 || (size_t) (op1_len + op2_len) < 0) { - ZVAL_EMPTY_STRING(result); - zend_error(E_ERROR, "String size overflow"); + if (op1_len > SIZE_MAX - op2_len) { + zend_error_noreturn(E_ERROR, "String size overflow"); } - Z_STR_P(result) = zend_string_realloc(Z_STR_P(result), res_len, 0 ); - Z_TYPE_INFO_P(result) = IS_STRING_EX; - memcpy(Z_STRVAL_P(result) + op1_len, Z_STRVAL_P(op2), op2_len); - Z_STRVAL_P(result)[res_len]=0; - } else { - size_t length = Z_STRLEN_P(op1) + Z_STRLEN_P(op2); - zend_string *buf = zend_string_alloc(length, 0); + if (result == op1 && Z_REFCOUNTED_P(result)) { + /* special case, perform operations on result */ + result_str = zend_string_realloc(Z_STR_P(result), result_len, 0); + } else { + result_str = zend_string_alloc(result_len, 0); + memcpy(result_str->val, Z_STRVAL_P(op1), op1_len); + } - memcpy(buf->val, Z_STRVAL_P(op1), Z_STRLEN_P(op1)); - memcpy(buf->val + Z_STRLEN_P(op1), Z_STRVAL_P(op2), Z_STRLEN_P(op2)); - buf->val[length] = 0; - ZVAL_NEW_STR(result, buf); + /* This has to happen first to account for the cases where result == op1 == op2 and + * the realloc is done. In this case this line will also update Z_STRVAL_P(op2) to + * point to the new string. The first op2_len bytes of result will still be the same. */ + ZVAL_NEW_STR(result, result_str); + + memcpy(result_str->val + op1_len, Z_STRVAL_P(op2), op2_len); + result_str->val[result_len] = '\0'; } + if (UNEXPECTED(use_copy1)) { zval_dtor(op1); } @@ -2059,7 +2019,7 @@ static void increment_string(zval *str) /* {{{ */ return; } - if (IS_INTERNED(Z_STR_P(str))) { + if (!Z_REFCOUNTED_P(str)) { Z_STR_P(str) = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0); Z_TYPE_INFO_P(str) = IS_STRING_EX; } else if (Z_REFCOUNT_P(str) > 1) { @@ -2070,7 +2030,7 @@ static void increment_string(zval *str) /* {{{ */ } s = Z_STRVAL_P(str); - while (pos >= 0) { + do { ch = s[pos]; if (ch >= 'a' && ch <= 'z') { if (ch == 'z') { @@ -2106,8 +2066,7 @@ static void increment_string(zval *str) /* {{{ */ if (carry == 0) { break; } - pos--; - } + } while (pos-- > 0); if (carry) { t = zend_string_alloc(Z_STRLEN_P(str)+1, 0); @@ -2561,8 +2520,7 @@ ZEND_API void zend_locale_sprintf_double(zval *op ZEND_FILE_LINE_DC) /* {{{ */ ZEND_API zend_string *zend_long_to_str(zend_long num) /* {{{ */ { char buf[MAX_LENGTH_OF_LONG + 1]; - char *res; - _zend_print_signed_to_buf(buf + sizeof(buf) - 1, num, zend_ulong, res); + char *res = zend_print_long_to_buf(buf + sizeof(buf) - 1, num); return zend_string_init(res, buf + sizeof(buf) - 1 - res, 0); } /* }}} */ @@ -2571,6 +2529,143 @@ ZEND_API zend_uchar is_numeric_str_function(const zend_string *str, zend_long *l return is_numeric_string_ex(str->val, str->len, lval, dval, -1, NULL); } +ZEND_API zend_uchar _is_numeric_string_ex(const char *str, size_t length, zend_long *lval, double *dval, int allow_errors, int *oflow_info) +{ + const char *ptr; + int base = 10, digits = 0, dp_or_e = 0; + double local_dval = 0.0; + zend_uchar type; + + if (!length) { + return 0; + } + + if (oflow_info != NULL) { + *oflow_info = 0; + } + + /* Skip any whitespace + * This is much faster than the isspace() function */ + while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r' || *str == '\v' || *str == '\f') { + str++; + length--; + } + ptr = str; + + if (*ptr == '-' || *ptr == '+') { + ptr++; + } + + if (ZEND_IS_DIGIT(*ptr)) { + /* Handle hex numbers + * str is used instead of ptr to disallow signs and keep old behavior */ + if (length > 2 && *str == '0' && (str[1] == 'x' || str[1] == 'X')) { + base = 16; + ptr += 2; + } + + /* Skip any leading 0s */ + while (*ptr == '0') { + ptr++; + } + + /* Count the number of digits. If a decimal point/exponent is found, + * it's a double. Otherwise, if there's a dval or no need to check for + * a full match, stop when there are too many digits for a long */ + for (type = IS_LONG; !(digits >= MAX_LENGTH_OF_LONG && (dval || allow_errors == 1)); digits++, ptr++) { +check_digits: + if (ZEND_IS_DIGIT(*ptr) || (base == 16 && ZEND_IS_XDIGIT(*ptr))) { + continue; + } else if (base == 10) { + if (*ptr == '.' && dp_or_e < 1) { + goto process_double; + } else if ((*ptr == 'e' || *ptr == 'E') && dp_or_e < 2) { + const char *e = ptr + 1; + + if (*e == '-' || *e == '+') { + ptr = e++; + } + if (ZEND_IS_DIGIT(*e)) { + goto process_double; + } + } + } + + break; + } + + if (base == 10) { + if (digits >= MAX_LENGTH_OF_LONG) { + if (oflow_info != NULL) { + *oflow_info = *str == '-' ? -1 : 1; + } + dp_or_e = -1; + goto process_double; + } + } else if (!(digits < SIZEOF_ZEND_LONG * 2 || (digits == SIZEOF_ZEND_LONG * 2 && ptr[-digits] <= '7'))) { + if (dval) { + local_dval = zend_hex_strtod(str, &ptr); + } + if (oflow_info != NULL) { + *oflow_info = 1; + } + type = IS_DOUBLE; + } + } else if (*ptr == '.' && ZEND_IS_DIGIT(ptr[1])) { +process_double: + type = IS_DOUBLE; + + /* If there's a dval, do the conversion; else continue checking + * the digits if we need to check for a full match */ + if (dval) { + local_dval = zend_strtod(str, &ptr); + } else if (allow_errors != 1 && dp_or_e != -1) { + dp_or_e = (*ptr++ == '.') ? 1 : 2; + goto check_digits; + } + } else { + return 0; + } + + if (ptr != str + length) { + if (!allow_errors) { + return 0; + } + if (allow_errors == -1) { + zend_error(E_NOTICE, "A non well formed numeric value encountered"); + } + } + + if (type == IS_LONG) { + if (digits == MAX_LENGTH_OF_LONG - 1) { + int cmp = strcmp(&ptr[-digits], long_min_digits); + + if (!(cmp < 0 || (cmp == 0 && *str == '-'))) { + if (dval) { + *dval = zend_strtod(str, NULL); + } + if (oflow_info != NULL) { + *oflow_info = *str == '-' ? -1 : 1; + } + + return IS_DOUBLE; + } + } + + if (lval) { + *lval = ZEND_STRTOL(str, NULL, base); + } + + return IS_LONG; + } else { + if (dval) { + *dval = local_dval; + } + + return IS_DOUBLE; + } +} + /* * Local variables: * tab-width: 4 |
