diff --git a/ext/bcmath/config.m4 b/ext/bcmath/config.m4 index 6053b72ec1fda..eab12133fa853 100644 --- a/ext/bcmath/config.m4 +++ b/ext/bcmath/config.m4 @@ -8,7 +8,7 @@ if test "$PHP_BCMATH" != "no"; then libbcmath/src/add.c libbcmath/src/div.c libbcmath/src/init.c libbcmath/src/neg.c libbcmath/src/raisemod.c libbcmath/src/sub.c \ libbcmath/src/compare.c libbcmath/src/divmod.c libbcmath/src/int2num.c libbcmath/src/num2long.c libbcmath/src/output.c libbcmath/src/recmul.c \ libbcmath/src/sqrt.c libbcmath/src/zero.c libbcmath/src/doaddsub.c libbcmath/src/floor_or_ceil.c libbcmath/src/nearzero.c libbcmath/src/num2str.c \ -libbcmath/src/raise.c libbcmath/src/rmzero.c libbcmath/src/round.c libbcmath/src/str2num.c, +libbcmath/src/raise.c libbcmath/src/rmzero.c libbcmath/src/round.c libbcmath/src/str2num.c libbcmath/src/convert.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) PHP_ADD_BUILD_DIR($ext_builddir/libbcmath/src) AC_DEFINE(HAVE_BCMATH, 1, [Whether you have bcmath]) diff --git a/ext/bcmath/config.w32 b/ext/bcmath/config.w32 index a0c2aa51728ad..7f2c4bfda5093 100644 --- a/ext/bcmath/config.w32 +++ b/ext/bcmath/config.w32 @@ -8,7 +8,7 @@ if (PHP_BCMATH == "yes") { raisemod.c sub.c compare.c divmod.c int2num.c \ num2long.c output.c recmul.c sqrt.c zero.c doaddsub.c \ floor_or_ceil.c nearzero.c num2str.c raise.c rmzero.c str2num.c \ - round.c", "bcmath"); + round.c convert.c", "bcmath"); AC_DEFINE('HAVE_BCMATH', 1, 'Have BCMATH library'); } diff --git a/ext/bcmath/libbcmath/src/convert.c b/ext/bcmath/libbcmath/src/convert.c new file mode 100644 index 0000000000000..6217583a0ad4d --- /dev/null +++ b/ext/bcmath/libbcmath/src/convert.c @@ -0,0 +1,66 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Niels Dossche | + +----------------------------------------------------------------------+ +*/ + +#include "bcmath.h" +#include "convert.h" + +/* This will be 0x01010101 for 32-bit and 0x0101010101010101 */ +#define SWAR_ONES (~((size_t) 0) / 0xFF) +/* This repeats a byte `x` into an entire 32/64-bit word. + * Example: SWAR_REPEAT(0xAB) will be 0xABABABAB for 32-bit and 0xABABABABABABABAB for 64-bit. */ +#define SWAR_REPEAT(x) (SWAR_ONES * (x)) + +static char *bc_copy_and_shift_numbers(char *restrict dest, const char *source, const char *source_end, unsigned char shift, bool add) +{ + size_t bulk_shift = SWAR_REPEAT(shift); + if (!add) { + bulk_shift = -bulk_shift; + shift = -shift; + } + + /* Handle sizeof(size_t) (i.e. 4/8) bytes at once. + * We know that adding/subtracting an individual byte cannot overflow, + * so it is possible to add/subtract an entire word of bytes at once + * by using SWAR_REPEAT. */ + while (source + sizeof(size_t) <= source_end) { + size_t bytes; + memcpy(&bytes, source, sizeof(bytes)); + + bytes += bulk_shift; + memcpy(dest, &bytes, sizeof(bytes)); + + source += sizeof(size_t); + dest += sizeof(size_t); + } + + while (source < source_end) { + *dest = *source + shift; + dest++; + source++; + } + + return dest; +} + +char *bc_copy_ch_val(char *restrict dest, const char *source, const char *source_end) +{ + return bc_copy_and_shift_numbers(dest, source, source_end, '0', false); +} + +char *bc_copy_bcd_val(char *restrict dest, const char *source, const char *source_end) +{ + return bc_copy_and_shift_numbers(dest, source, source_end, '0', true); +} diff --git a/ext/bcmath/libbcmath/src/convert.h b/ext/bcmath/libbcmath/src/convert.h new file mode 100644 index 0000000000000..ab74ce03ea057 --- /dev/null +++ b/ext/bcmath/libbcmath/src/convert.h @@ -0,0 +1,23 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Niels Dossche | + +----------------------------------------------------------------------+ +*/ + +#ifndef BCMATH_CONVERT_H +#define BCMATH_CONVERT_H + +char *bc_copy_ch_val(char *restrict dest, const char *source, const char *source_end); +char *bc_copy_bcd_val(char *restrict dest, const char *source, const char *source_end); + +#endif diff --git a/ext/bcmath/libbcmath/src/num2str.c b/ext/bcmath/libbcmath/src/num2str.c index 904de2795af31..07ded20ab27ad 100644 --- a/ext/bcmath/libbcmath/src/num2str.c +++ b/ext/bcmath/libbcmath/src/num2str.c @@ -30,7 +30,7 @@ *************************************************************************/ #include "bcmath.h" -#include +#include "convert.h" #include "zend_string.h" /* Convert a numbers to a string. Base 10 only.*/ @@ -40,9 +40,10 @@ zend_string *bc_num2str_ex(bc_num num, size_t scale) char *sptr; size_t index; bool signch; + size_t min_scale = MIN(num->n_scale, scale); /* Number of sign chars. */ - signch = num->n_sign != PLUS && !bc_is_zero_for_scale(num, MIN(num->n_scale, scale)); + signch = num->n_sign != PLUS && !bc_is_zero_for_scale(num, min_scale); /* Allocate the string memory. */ if (scale > 0) { str = zend_string_alloc(num->n_len + scale + signch + 1, 0); @@ -56,16 +57,13 @@ zend_string *bc_num2str_ex(bc_num num, size_t scale) /* Load the whole number. */ const char *nptr = num->n_value; - for (index = num->n_len; index > 0; index--) { - *sptr++ = BCD_CHAR(*nptr++); - } + sptr = bc_copy_bcd_val(sptr, nptr, nptr + num->n_len); + nptr += num->n_len; /* Now the fraction. */ if (scale > 0) { *sptr++ = '.'; - for (index = 0; index < scale && index < num->n_scale; index++) { - *sptr++ = BCD_CHAR(*nptr++); - } + sptr = bc_copy_bcd_val(sptr, nptr, nptr + min_scale); for (index = num->n_scale; index < scale; index++) { *sptr++ = BCD_CHAR(0); } diff --git a/ext/bcmath/libbcmath/src/str2num.c b/ext/bcmath/libbcmath/src/str2num.c index 2f2f55377e83b..7a00139fcb254 100644 --- a/ext/bcmath/libbcmath/src/str2num.c +++ b/ext/bcmath/libbcmath/src/str2num.c @@ -30,6 +30,7 @@ *************************************************************************/ #include "bcmath.h" +#include "convert.h" #include #include @@ -124,24 +125,12 @@ bool bc_str2num(bc_num *num, char *str, size_t scale, bool auto_scale) * 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++; - } + nptr = bc_copy_ch_val(nptr, fractional_ptr, fractional_end); } else { const char *integer_end = integer_ptr + digits; - while (integer_ptr < integer_end) { - *nptr = CH_VAL(*integer_ptr); - nptr++; - integer_ptr++; - } + nptr = bc_copy_ch_val(nptr, integer_ptr, integer_end); if (str_scale > 0) { - while (fractional_ptr < fractional_end) { - *nptr = CH_VAL(*fractional_ptr); - nptr++; - fractional_ptr++; - } + nptr = bc_copy_ch_val(nptr, fractional_ptr, fractional_end); } }