Skip to content

Commit a73d289

Browse files
committed
Faster BCD into integer parsing
1 parent 5674443 commit a73d289

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

ext/bcmath/libbcmath/src/recmul.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,68 @@
4848

4949
/* Multiply utility routines */
5050

51+
/* This is based on the technique described in https://kholdstare.github.io/technical/2020/05/26/faster-integer-parsing.html.
52+
* This function transforms AABBCCDD into 1000 * AA + 100 * BB + 10 * CC + DD,
53+
* with the caveat that all components must be in the interval [0, 25] to prevent overflow
54+
* due to the multiplication by power of 10 (10 * 25 = 250 is the largest number that fits in a byte).
55+
* The advantage of this method instead of using shifts + 3 multiplications is that this is cheaper
56+
* due to its divide-and-conquer nature.
57+
*/
58+
#if SIZEOF_SIZE_T == 4
59+
static uint32_t bc_parse_chunk_chars(const char *str)
60+
{
61+
uint32_t tmp;
62+
memcpy(&tmp, str, sizeof(tmp));
63+
#if !BC_LITTLE_ENDIAN
64+
tmp = BC_BSWAP(tmp);
65+
#endif
66+
67+
uint32_t lower_digits = (tmp & 0x0f000f00) >> 8;
68+
uint32_t upper_digits = (tmp & 0x000f000f) * 10;
69+
70+
tmp = lower_digits + upper_digits;
71+
72+
lower_digits = (tmp & 0x00ff0000) >> 16;
73+
upper_digits = (tmp & 0x000000ff) * 100;
74+
75+
return lower_digits + upper_digits;
76+
}
77+
#elif SIZEOF_SIZE_T == 8
78+
static uint64_t bc_parse_chunk_chars(const char *str)
79+
{
80+
uint64_t tmp;
81+
memcpy(&tmp, str, sizeof(tmp));
82+
#if !BC_LITTLE_ENDIAN
83+
tmp = BC_BSWAP(tmp);
84+
#endif
85+
86+
uint64_t lower_digits = (tmp & 0x0f000f000f000f00) >> 8;
87+
uint64_t upper_digits = (tmp & 0x000f000f000f000f) * 10;
88+
89+
tmp = lower_digits + upper_digits;
90+
91+
lower_digits = (tmp & 0x00ff000000ff0000) >> 16;
92+
upper_digits = (tmp & 0x000000ff000000ff) * 100;
93+
94+
tmp = lower_digits + upper_digits;
95+
96+
lower_digits = (tmp & 0x0000ffff00000000) >> 32;
97+
upper_digits = (tmp & 0x000000000000ffff) * 10000;
98+
99+
return lower_digits + upper_digits;
100+
}
101+
#endif
102+
51103
/*
52104
* Converts BCD to uint, going backwards from pointer n by the number of
53105
* characters specified by len.
54106
*/
55107
static inline BC_UINT_T bc_partial_convert_to_uint(const char *n, size_t len)
56108
{
109+
if (len == BC_MUL_UINT_DIGITS) {
110+
return bc_parse_chunk_chars(n - BC_MUL_UINT_DIGITS + 1);
111+
}
112+
57113
BC_UINT_T num = 0;
58114
BC_UINT_T base = 1;
59115

0 commit comments

Comments
 (0)