mirror of
https://github.com/php/php-src.git
synced 2024-09-21 18:07:23 +00:00
Refactor BCMath (#14076)
Optimized the order of structure members and the process of converting strings to bc_num structures. closes #14076
This commit is contained in:
parent
378b015360
commit
a481556d31
@ -134,17 +134,7 @@ PHP_MINFO_FUNCTION(bcmath)
|
||||
Convert to bc_num detecting scale */
|
||||
static zend_result php_str2num(bc_num *num, char *str)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (!(p = strchr(str, '.'))) {
|
||||
if (!bc_str2num(num, str, 0)) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (!bc_str2num(num, str, strlen(p+1))) {
|
||||
if (!bc_str2num(num, str, 0, true)) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
@ -624,12 +614,12 @@ PHP_FUNCTION(bccomp)
|
||||
bc_init_num(&first);
|
||||
bc_init_num(&second);
|
||||
|
||||
if (!bc_str2num(&first, ZSTR_VAL(left), scale)) {
|
||||
if (!bc_str2num(&first, ZSTR_VAL(left), scale, false)) {
|
||||
zend_argument_value_error(1, "is not well-formed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!bc_str2num(&second, ZSTR_VAL(right), scale)) {
|
||||
if (!bc_str2num(&second, ZSTR_VAL(right), scale, false)) {
|
||||
zend_argument_value_error(2, "is not well-formed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -39,16 +39,16 @@ typedef enum {PLUS, MINUS} sign;
|
||||
typedef struct bc_struct *bc_num;
|
||||
|
||||
typedef struct bc_struct {
|
||||
sign n_sign;
|
||||
size_t n_len; /* The number of digits before the decimal point. */
|
||||
size_t n_scale; /* The number of digits after the decimal point. */
|
||||
int n_refs; /* The number of pointers to this number. */
|
||||
char *n_ptr; /* The pointer to the actual storage.
|
||||
If NULL, n_value points to the inside of another number
|
||||
(bc_multiply...) and should not be "freed." */
|
||||
char *n_value; /* The number. Not zero char terminated.
|
||||
May not point to the same place as n_ptr as
|
||||
in the case of leading zeros generated. */
|
||||
int n_refs; /* The number of pointers to this number. */
|
||||
sign n_sign;
|
||||
} bc_struct;
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
@ -97,7 +97,7 @@ bc_num bc_copy_num(bc_num num);
|
||||
|
||||
void bc_init_num(bc_num *num);
|
||||
|
||||
bool bc_str2num(bc_num *num, char *str, size_t scale);
|
||||
bool bc_str2num(bc_num *num, char *str, size_t scale, bool auto_scale);
|
||||
|
||||
zend_string *bc_num2str_ex(bc_num num, size_t scale);
|
||||
|
||||
|
@ -35,20 +35,19 @@
|
||||
|
||||
/* Convert strings to bc numbers. Base 10 only.*/
|
||||
|
||||
bool bc_str2num(bc_num *num, char *str, size_t scale)
|
||||
bool bc_str2num(bc_num *num, char *str, size_t scale, bool auto_scale)
|
||||
{
|
||||
size_t digits = 0;
|
||||
size_t strscale = 0;
|
||||
char *ptr, *nptr;
|
||||
size_t trailing_zeros = 0;
|
||||
size_t str_scale = 0;
|
||||
char *ptr = str;
|
||||
char *fractional_ptr = NULL;
|
||||
char *fractional_end = NULL;
|
||||
bool zero_int = false;
|
||||
|
||||
/* Prepare num. */
|
||||
bc_free_num (num);
|
||||
|
||||
/* Check for valid number and count digits. */
|
||||
ptr = str;
|
||||
|
||||
if ((*ptr == '+') || (*ptr == '-')) {
|
||||
/* Skip Sign */
|
||||
ptr++;
|
||||
@ -57,77 +56,102 @@ bool bc_str2num(bc_num *num, char *str, size_t scale)
|
||||
while (*ptr == '0') {
|
||||
ptr++;
|
||||
}
|
||||
const char *integer_ptr = ptr;
|
||||
/* digits before the decimal point */
|
||||
while (*ptr >= '0' && *ptr <= '9') {
|
||||
ptr++;
|
||||
digits++;
|
||||
}
|
||||
/* decimal point */
|
||||
if (*ptr == '.') {
|
||||
ptr++;
|
||||
}
|
||||
/* digits after the decimal point */
|
||||
while (*ptr >= '0' && *ptr <= '9') {
|
||||
if (*ptr == '0') {
|
||||
trailing_zeros++;
|
||||
} else {
|
||||
trailing_zeros = 0;
|
||||
}
|
||||
ptr++;
|
||||
strscale++;
|
||||
char *decimal_point = (*ptr == '.') ? ptr : NULL;
|
||||
|
||||
/* If a non-digit and non-decimal-point indicator is in the string, i.e. an invalid character */
|
||||
if (!decimal_point && *ptr != '\0') {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (trailing_zeros > 0) {
|
||||
/* Trailing zeros should not take part in the computation of the overall scale, as it is pointless. */
|
||||
strscale = strscale - trailing_zeros;
|
||||
/* search and validate fractional end if exists */
|
||||
if (decimal_point) {
|
||||
/* search */
|
||||
fractional_ptr = fractional_end = decimal_point + 1;
|
||||
if (*fractional_ptr == '\0') {
|
||||
goto after_fractional;
|
||||
}
|
||||
|
||||
/* validate */
|
||||
while (*fractional_ptr >= '0' && *fractional_ptr <= '9') {
|
||||
fractional_end = *fractional_ptr != '0' ? fractional_ptr + 1 : fractional_end;
|
||||
fractional_ptr++;
|
||||
}
|
||||
if (*fractional_ptr != '\0') {
|
||||
/* invalid num */
|
||||
goto fail;
|
||||
}
|
||||
/* Move the pointer to the beginning of the fraction. */
|
||||
fractional_ptr = decimal_point + 1;
|
||||
|
||||
/* Calculate the length of the fraction excluding trailing zero. */
|
||||
str_scale = fractional_end - fractional_ptr;
|
||||
|
||||
/*
|
||||
* If set the scale manually and it is smaller than the automatically calculated scale,
|
||||
* adjust it to match the manual setting.
|
||||
*/
|
||||
if (str_scale > scale && !auto_scale) {
|
||||
fractional_end -= str_scale - scale;
|
||||
str_scale = scale;
|
||||
}
|
||||
}
|
||||
if ((*ptr != '\0') || (digits + strscale == 0)) {
|
||||
*num = bc_copy_num(BCG(_zero_));
|
||||
return *ptr == '\0';
|
||||
|
||||
after_fractional:
|
||||
|
||||
if (digits + str_scale == 0) {
|
||||
goto zero;
|
||||
}
|
||||
|
||||
/* Adjust numbers and allocate storage and initialize fields. */
|
||||
strscale = MIN(strscale, scale);
|
||||
if (digits == 0) {
|
||||
zero_int = true;
|
||||
digits = 1;
|
||||
}
|
||||
*num = bc_new_num (digits, strscale);
|
||||
*num = bc_new_num (digits, str_scale);
|
||||
(*num)->n_sign = *str == '-' ? MINUS : PLUS;
|
||||
char *nptr = (*num)->n_value;
|
||||
|
||||
/* Build the whole number. */
|
||||
ptr = str;
|
||||
if (*ptr == '-') {
|
||||
(*num)->n_sign = MINUS;
|
||||
ptr++;
|
||||
} else {
|
||||
(*num)->n_sign = PLUS;
|
||||
if (*ptr == '+') ptr++;
|
||||
}
|
||||
/* Skip leading zeros. */
|
||||
while (*ptr == '0') {
|
||||
ptr++;
|
||||
}
|
||||
nptr = (*num)->n_value;
|
||||
if (zero_int) {
|
||||
*nptr++ = 0;
|
||||
digits = 0;
|
||||
}
|
||||
for (; digits > 0; digits--) {
|
||||
*nptr++ = CH_VAL(*ptr++);
|
||||
}
|
||||
|
||||
/* Build the fractional part. */
|
||||
if (strscale > 0) {
|
||||
/* skip the decimal point! */
|
||||
ptr++;
|
||||
for (; strscale > 0; strscale--) {
|
||||
*nptr++ = CH_VAL(*ptr++);
|
||||
nptr++;
|
||||
/*
|
||||
* If zero_int is true and the str_scale is 0, there is an early return,
|
||||
* so here str_scale is always greater than 0.
|
||||
*/
|
||||
while (fractional_ptr < fractional_end) {
|
||||
*nptr = CH_VAL(*fractional_ptr);
|
||||
nptr++;
|
||||
fractional_ptr++;
|
||||
}
|
||||
} else {
|
||||
const char *integer_end = integer_ptr + digits;
|
||||
while (integer_ptr < integer_end) {
|
||||
*nptr = CH_VAL(*integer_ptr);
|
||||
nptr++;
|
||||
integer_ptr++;
|
||||
}
|
||||
if (str_scale > 0) {
|
||||
while (fractional_ptr < fractional_end) {
|
||||
*nptr = CH_VAL(*fractional_ptr);
|
||||
nptr++;
|
||||
fractional_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bc_is_zero(*num)) {
|
||||
(*num)->n_sign = PLUS;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
zero:
|
||||
*num = bc_copy_num(BCG(_zero_));
|
||||
return true;
|
||||
|
||||
fail:
|
||||
*num = bc_copy_num(BCG(_zero_));
|
||||
return false;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user