Skip to content

Commit 7bc7a5a

Browse files
committed
Optimize zend scalar comparison helpers
Avoid temporary zend_string allocations in `compare_long_to_string()` and `compare_double_to_string()` by using stack buffers, and streamline `zendi_smart_strcmp()` with non-numeric short-circuits plus single-pass numeric parsing to cut redundant work in general comparisons.
1 parent 785bbde commit 7bc7a5a

File tree

1 file changed

+68
-44
lines changed

1 file changed

+68
-44
lines changed

Zend/zend_operators.c

Lines changed: 68 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2264,17 +2264,18 @@ static int compare_long_to_string(zend_long lval, zend_string *str) /* {{{ */
22642264
uint8_t type = is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &str_lval, &str_dval, 0);
22652265

22662266
if (type == IS_LONG) {
2267-
return lval > str_lval ? 1 : lval < str_lval ? -1 : 0;
2267+
return ZEND_THREEWAY_COMPARE(lval, str_lval);
22682268
}
22692269

22702270
if (type == IS_DOUBLE) {
22712271
return ZEND_THREEWAY_COMPARE((double) lval, str_dval);
22722272
}
22732273

2274-
zend_string *lval_as_str = zend_long_to_str(lval);
2274+
char buf[MAX_LENGTH_OF_LONG + 1];
2275+
char *tmp = zend_print_long_to_buf(buf + sizeof(buf) - 1, lval);
2276+
size_t tmp_len = buf + sizeof(buf) - 1 - tmp;
22752277
int cmp_result = zend_binary_strcmp(
2276-
ZSTR_VAL(lval_as_str), ZSTR_LEN(lval_as_str), ZSTR_VAL(str), ZSTR_LEN(str));
2277-
zend_string_release(lval_as_str);
2278+
tmp, tmp_len, ZSTR_VAL(str), ZSTR_LEN(str));
22782279
return ZEND_NORMALIZE_BOOL(cmp_result);
22792280
}
22802281
/* }}} */
@@ -2295,10 +2296,12 @@ static int compare_double_to_string(double dval, zend_string *str) /* {{{ */
22952296
return ZEND_THREEWAY_COMPARE(dval, str_dval);
22962297
}
22972298

2298-
zend_string *dval_as_str = zend_double_to_str(dval);
2299+
char buf[ZEND_DOUBLE_MAX_LENGTH];
2300+
int precision = (int) EG(precision);
2301+
zend_gcvt(dval, precision ? precision : 1, '.', 'E', buf);
2302+
size_t tmp_len = strlen(buf);
22992303
int cmp_result = zend_binary_strcmp(
2300-
ZSTR_VAL(dval_as_str), ZSTR_LEN(dval_as_str), ZSTR_VAL(str), ZSTR_LEN(str));
2301-
zend_string_release(dval_as_str);
2304+
buf, tmp_len, ZSTR_VAL(str), ZSTR_LEN(str));
23022305
return ZEND_NORMALIZE_BOOL(cmp_result);
23032306
}
23042307
/* }}} */
@@ -3420,52 +3423,73 @@ ZEND_API bool ZEND_FASTCALL zendi_smart_streq(zend_string *s1, zend_string *s2)
34203423

34213424
ZEND_API int ZEND_FASTCALL zendi_smart_strcmp(zend_string *s1, zend_string *s2) /* {{{ */
34223425
{
3423-
uint8_t ret1, ret2;
3424-
int oflow1, oflow2;
3426+
uint8_t ret1 = 0, ret2 = 0;
3427+
int oflow1 = 0, oflow2 = 0;
34253428
zend_long lval1 = 0, lval2 = 0;
34263429
double dval1 = 0.0, dval2 = 0.0;
34273430

3428-
if ((ret1 = is_numeric_string_ex(s1->val, s1->len, &lval1, &dval1, false, &oflow1, NULL)) &&
3429-
(ret2 = is_numeric_string_ex(s2->val, s2->len, &lval2, &dval2, false, &oflow2, NULL))) {
3431+
if ((unsigned char)s1->val[0] > '9' && (unsigned char)s2->val[0] > '9') {
3432+
goto string_cmp;
3433+
}
3434+
3435+
if (UNEXPECTED(s1->len == 0 || s2->len == 0)) {
3436+
goto string_cmp;
3437+
}
3438+
3439+
if ((unsigned char)s1->val[0] <= '9') {
3440+
ret1 = is_numeric_string_ex(s1->val, s1->len, &lval1, &dval1, false, &oflow1, NULL);
3441+
}
3442+
3443+
if (!ret1) {
3444+
goto string_cmp;
3445+
}
3446+
3447+
ret2 = ((unsigned char)s2->val[0] <= '9')
3448+
? is_numeric_string_ex(s2->val, s2->len, &lval2, &dval2, false, &oflow2, NULL)
3449+
: 0;
3450+
3451+
if (!ret2) {
3452+
goto string_cmp;
3453+
}
3454+
34303455
#if ZEND_ULONG_MAX == 0xFFFFFFFF
3431-
if (oflow1 != 0 && oflow1 == oflow2 && dval1 - dval2 == 0. &&
3432-
((oflow1 == 1 && dval1 > 9007199254740991. /*0x1FFFFFFFFFFFFF*/)
3433-
|| (oflow1 == -1 && dval1 < -9007199254740991.))) {
3456+
if (oflow1 != 0 && oflow1 == oflow2 && dval1 - dval2 == 0. &&
3457+
((oflow1 == 1 && dval1 > 9007199254740991. /*0x1FFFFFFFFFFFFF*/)
3458+
|| (oflow1 == -1 && dval1 < -9007199254740991.))) {
34343459
#else
3435-
if (oflow1 != 0 && oflow1 == oflow2 && dval1 - dval2 == 0.) {
3460+
if (oflow1 != 0 && oflow1 == oflow2 && dval1 - dval2 == 0.) {
34363461
#endif
3437-
/* both values are integers overflowed to the same side, and the
3438-
* double comparison may have resulted in crucial accuracy lost */
3462+
/* both values are integers overflowed to the same side, and the
3463+
* double comparison may have resulted in crucial accuracy lost */
3464+
goto string_cmp;
3465+
}
3466+
3467+
if ((ret1 == IS_DOUBLE) || (ret2 == IS_DOUBLE)) {
3468+
if (ret1 != IS_DOUBLE) {
3469+
if (oflow2) {
3470+
/* 2nd operand is integer > LONG_MAX (oflow2==1) or < LONG_MIN (-1) */
3471+
return -1 * oflow2;
3472+
}
3473+
dval1 = (double) lval1;
3474+
} else if (ret2 != IS_DOUBLE) {
3475+
if (oflow1) {
3476+
return oflow1;
3477+
}
3478+
dval2 = (double) lval2;
3479+
} else if (dval1 == dval2 && !zend_finite(dval1)) {
3480+
/* Both values overflowed and have the same sign,
3481+
* so a numeric comparison would be inaccurate */
34393482
goto string_cmp;
34403483
}
3441-
if ((ret1 == IS_DOUBLE) || (ret2 == IS_DOUBLE)) {
3442-
if (ret1 != IS_DOUBLE) {
3443-
if (oflow2) {
3444-
/* 2nd operand is integer > LONG_MAX (oflow2==1) or < LONG_MIN (-1) */
3445-
return -1 * oflow2;
3446-
}
3447-
dval1 = (double) lval1;
3448-
} else if (ret2 != IS_DOUBLE) {
3449-
if (oflow1) {
3450-
return oflow1;
3451-
}
3452-
dval2 = (double) lval2;
3453-
} else if (dval1 == dval2 && !zend_finite(dval1)) {
3454-
/* Both values overflowed and have the same sign,
3455-
* so a numeric comparison would be inaccurate */
3456-
goto string_cmp;
3457-
}
3458-
dval1 = dval1 - dval2;
3459-
return ZEND_NORMALIZE_BOOL(dval1);
3460-
} else { /* they both have to be long's */
3461-
return lval1 > lval2 ? 1 : (lval1 < lval2 ? -1 : 0);
3462-
}
3463-
} else {
3464-
int strcmp_ret;
3465-
string_cmp:
3466-
strcmp_ret = zend_binary_strcmp(s1->val, s1->len, s2->val, s2->len);
3467-
return ZEND_NORMALIZE_BOOL(strcmp_ret);
3484+
return ZEND_THREEWAY_COMPARE(dval1, dval2);
34683485
}
3486+
3487+
return ZEND_THREEWAY_COMPARE(lval1, lval2);
3488+
3489+
int strcmp_ret;
3490+
string_cmp:
3491+
strcmp_ret = zend_binary_strcmp(s1->val, s1->len, s2->val, s2->len);
3492+
return ZEND_NORMALIZE_BOOL(strcmp_ret);
34693493
}
34703494
/* }}} */
34713495

0 commit comments

Comments
 (0)