Skip to content

Commit a481556

Browse files
committed
Refactor BCMath (php#14076)
Optimized the order of structure members and the process of converting strings to bc_num structures. closes php#14076
1 parent 378b015 commit a481556

File tree

3 files changed

+85
-71
lines changed

3 files changed

+85
-71
lines changed

ext/bcmath/bcmath.c

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -134,17 +134,7 @@ PHP_MINFO_FUNCTION(bcmath)
134134
Convert to bc_num detecting scale */
135135
static zend_result php_str2num(bc_num *num, char *str)
136136
{
137-
char *p;
138-
139-
if (!(p = strchr(str, '.'))) {
140-
if (!bc_str2num(num, str, 0)) {
141-
return FAILURE;
142-
}
143-
144-
return SUCCESS;
145-
}
146-
147-
if (!bc_str2num(num, str, strlen(p+1))) {
137+
if (!bc_str2num(num, str, 0, true)) {
148138
return FAILURE;
149139
}
150140

@@ -624,12 +614,12 @@ PHP_FUNCTION(bccomp)
624614
bc_init_num(&first);
625615
bc_init_num(&second);
626616

627-
if (!bc_str2num(&first, ZSTR_VAL(left), scale)) {
617+
if (!bc_str2num(&first, ZSTR_VAL(left), scale, false)) {
628618
zend_argument_value_error(1, "is not well-formed");
629619
goto cleanup;
630620
}
631621

632-
if (!bc_str2num(&second, ZSTR_VAL(right), scale)) {
622+
if (!bc_str2num(&second, ZSTR_VAL(right), scale, false)) {
633623
zend_argument_value_error(2, "is not well-formed");
634624
goto cleanup;
635625
}

ext/bcmath/libbcmath/src/bcmath.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,16 @@ typedef enum {PLUS, MINUS} sign;
3939
typedef struct bc_struct *bc_num;
4040

4141
typedef struct bc_struct {
42-
sign n_sign;
4342
size_t n_len; /* The number of digits before the decimal point. */
4443
size_t n_scale; /* The number of digits after the decimal point. */
45-
int n_refs; /* The number of pointers to this number. */
4644
char *n_ptr; /* The pointer to the actual storage.
4745
If NULL, n_value points to the inside of another number
4846
(bc_multiply...) and should not be "freed." */
4947
char *n_value; /* The number. Not zero char terminated.
5048
May not point to the same place as n_ptr as
5149
in the case of leading zeros generated. */
50+
int n_refs; /* The number of pointers to this number. */
51+
sign n_sign;
5252
} bc_struct;
5353

5454
#ifdef HAVE_CONFIG_H
@@ -97,7 +97,7 @@ bc_num bc_copy_num(bc_num num);
9797

9898
void bc_init_num(bc_num *num);
9999

100-
bool bc_str2num(bc_num *num, char *str, size_t scale);
100+
bool bc_str2num(bc_num *num, char *str, size_t scale, bool auto_scale);
101101

102102
zend_string *bc_num2str_ex(bc_num num, size_t scale);
103103

ext/bcmath/libbcmath/src/str2num.c

Lines changed: 79 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,19 @@
3535

3636
/* Convert strings to bc numbers. Base 10 only.*/
3737

38-
bool bc_str2num(bc_num *num, char *str, size_t scale)
38+
bool bc_str2num(bc_num *num, char *str, size_t scale, bool auto_scale)
3939
{
4040
size_t digits = 0;
41-
size_t strscale = 0;
42-
char *ptr, *nptr;
43-
size_t trailing_zeros = 0;
41+
size_t str_scale = 0;
42+
char *ptr = str;
43+
char *fractional_ptr = NULL;
44+
char *fractional_end = NULL;
4445
bool zero_int = false;
4546

4647
/* Prepare num. */
4748
bc_free_num (num);
4849

4950
/* Check for valid number and count digits. */
50-
ptr = str;
51-
5251
if ((*ptr == '+') || (*ptr == '-')) {
5352
/* Skip Sign */
5453
ptr++;
@@ -57,77 +56,102 @@ bool bc_str2num(bc_num *num, char *str, size_t scale)
5756
while (*ptr == '0') {
5857
ptr++;
5958
}
59+
const char *integer_ptr = ptr;
6060
/* digits before the decimal point */
6161
while (*ptr >= '0' && *ptr <= '9') {
6262
ptr++;
6363
digits++;
6464
}
6565
/* decimal point */
66-
if (*ptr == '.') {
67-
ptr++;
66+
char *decimal_point = (*ptr == '.') ? ptr : NULL;
67+
68+
/* If a non-digit and non-decimal-point indicator is in the string, i.e. an invalid character */
69+
if (!decimal_point && *ptr != '\0') {
70+
goto fail;
6871
}
69-
/* digits after the decimal point */
70-
while (*ptr >= '0' && *ptr <= '9') {
71-
if (*ptr == '0') {
72-
trailing_zeros++;
73-
} else {
74-
trailing_zeros = 0;
72+
73+
/* search and validate fractional end if exists */
74+
if (decimal_point) {
75+
/* search */
76+
fractional_ptr = fractional_end = decimal_point + 1;
77+
if (*fractional_ptr == '\0') {
78+
goto after_fractional;
7579
}
76-
ptr++;
77-
strscale++;
78-
}
7980

80-
if (trailing_zeros > 0) {
81-
/* Trailing zeros should not take part in the computation of the overall scale, as it is pointless. */
82-
strscale = strscale - trailing_zeros;
81+
/* validate */
82+
while (*fractional_ptr >= '0' && *fractional_ptr <= '9') {
83+
fractional_end = *fractional_ptr != '0' ? fractional_ptr + 1 : fractional_end;
84+
fractional_ptr++;
85+
}
86+
if (*fractional_ptr != '\0') {
87+
/* invalid num */
88+
goto fail;
89+
}
90+
/* Move the pointer to the beginning of the fraction. */
91+
fractional_ptr = decimal_point + 1;
92+
93+
/* Calculate the length of the fraction excluding trailing zero. */
94+
str_scale = fractional_end - fractional_ptr;
95+
96+
/*
97+
* If set the scale manually and it is smaller than the automatically calculated scale,
98+
* adjust it to match the manual setting.
99+
*/
100+
if (str_scale > scale && !auto_scale) {
101+
fractional_end -= str_scale - scale;
102+
str_scale = scale;
103+
}
83104
}
84-
if ((*ptr != '\0') || (digits + strscale == 0)) {
85-
*num = bc_copy_num(BCG(_zero_));
86-
return *ptr == '\0';
105+
106+
after_fractional:
107+
108+
if (digits + str_scale == 0) {
109+
goto zero;
87110
}
88111

89112
/* Adjust numbers and allocate storage and initialize fields. */
90-
strscale = MIN(strscale, scale);
91113
if (digits == 0) {
92114
zero_int = true;
93115
digits = 1;
94116
}
95-
*num = bc_new_num (digits, strscale);
117+
*num = bc_new_num (digits, str_scale);
118+
(*num)->n_sign = *str == '-' ? MINUS : PLUS;
119+
char *nptr = (*num)->n_value;
96120

97-
/* Build the whole number. */
98-
ptr = str;
99-
if (*ptr == '-') {
100-
(*num)->n_sign = MINUS;
101-
ptr++;
102-
} else {
103-
(*num)->n_sign = PLUS;
104-
if (*ptr == '+') ptr++;
105-
}
106-
/* Skip leading zeros. */
107-
while (*ptr == '0') {
108-
ptr++;
109-
}
110-
nptr = (*num)->n_value;
111121
if (zero_int) {
112-
*nptr++ = 0;
113-
digits = 0;
114-
}
115-
for (; digits > 0; digits--) {
116-
*nptr++ = CH_VAL(*ptr++);
117-
}
118-
119-
/* Build the fractional part. */
120-
if (strscale > 0) {
121-
/* skip the decimal point! */
122-
ptr++;
123-
for (; strscale > 0; strscale--) {
124-
*nptr++ = CH_VAL(*ptr++);
122+
nptr++;
123+
/*
124+
* If zero_int is true and the str_scale is 0, there is an early return,
125+
* so here str_scale is always greater than 0.
126+
*/
127+
while (fractional_ptr < fractional_end) {
128+
*nptr = CH_VAL(*fractional_ptr);
129+
nptr++;
130+
fractional_ptr++;
131+
}
132+
} else {
133+
const char *integer_end = integer_ptr + digits;
134+
while (integer_ptr < integer_end) {
135+
*nptr = CH_VAL(*integer_ptr);
136+
nptr++;
137+
integer_ptr++;
138+
}
139+
if (str_scale > 0) {
140+
while (fractional_ptr < fractional_end) {
141+
*nptr = CH_VAL(*fractional_ptr);
142+
nptr++;
143+
fractional_ptr++;
144+
}
125145
}
126146
}
127147

128-
if (bc_is_zero(*num)) {
129-
(*num)->n_sign = PLUS;
130-
}
148+
return true;
131149

150+
zero:
151+
*num = bc_copy_num(BCG(_zero_));
132152
return true;
153+
154+
fail:
155+
*num = bc_copy_num(BCG(_zero_));
156+
return false;
133157
}

0 commit comments

Comments
 (0)