Skip to content

Commit 04a34c3

Browse files
committed
Faster writing of BCD representation
1 parent aef0ead commit 04a34c3

File tree

1 file changed

+53
-3
lines changed

1 file changed

+53
-3
lines changed

ext/bcmath/libbcmath/src/recmul.c

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,52 @@ static inline void bc_fast_mul(bc_num n1, size_t n1len, bc_num n2, size_t n2len,
112112
}
113113
}
114114

115+
#if BC_LITTLE_ENDIAN
116+
# define BC_ENCODE_LUT(A, B) ((A) | (B) << 4)
117+
#else
118+
# define BC_ENCODE_LUT(A, B) ((B) | (A) << 4)
119+
#endif
120+
121+
#define LUT_ITERATE(_, A) \
122+
_(A, 0), _(A, 1), _(A, 2), _(A, 3), _(A, 4), _(A, 5), _(A, 6), _(A, 7), _(A, 8), _(A, 9)
123+
124+
/* This LUT encodes the decimal representation of numbers 0-100
125+
* such that we can avoid taking modulos and divisions which would be slow. */
126+
static const unsigned char LUT[100] = {
127+
LUT_ITERATE(BC_ENCODE_LUT, 0),
128+
LUT_ITERATE(BC_ENCODE_LUT, 1),
129+
LUT_ITERATE(BC_ENCODE_LUT, 2),
130+
LUT_ITERATE(BC_ENCODE_LUT, 3),
131+
LUT_ITERATE(BC_ENCODE_LUT, 4),
132+
LUT_ITERATE(BC_ENCODE_LUT, 5),
133+
LUT_ITERATE(BC_ENCODE_LUT, 6),
134+
LUT_ITERATE(BC_ENCODE_LUT, 7),
135+
LUT_ITERATE(BC_ENCODE_LUT, 8),
136+
LUT_ITERATE(BC_ENCODE_LUT, 9),
137+
};
138+
139+
static inline unsigned short bc_expand_lut(unsigned char c)
140+
{
141+
return (c & 0x0f) | (c & 0xf0) << 4;
142+
}
143+
144+
/* Writes the character representation of the number encoded in value.
145+
* E.g. if value = 1234, then the string "1234" will be written to str. */
146+
static void bc_write_bcd_representation(uint32_t value, char *str)
147+
{
148+
uint32_t upper = value / 100; /* e.g. 12 */
149+
uint32_t lower = value % 100; /* e.g. 34 */
150+
151+
#if BC_LITTLE_ENDIAN
152+
/* Note: little endian, so `lower` comes before `upper`! */
153+
uint32_t digits = bc_expand_lut(LUT[lower]) << 16 | bc_expand_lut(LUT[upper]);
154+
#else
155+
/* Note: big endian, so `upper` comes before `lower`! */
156+
uint32_t digits = bc_expand_lut(LUT[upper]) << 16 | bc_expand_lut(LUT[lower]);
157+
#endif
158+
memcpy(str, &digits, sizeof(digits));
159+
}
160+
115161
/*
116162
* Converts the BCD of bc_num by 4 (32 bits) or 8 (64 bits) digits to an array of BC_UINT_Ts.
117163
* The array is generated starting with the smaller digits.
@@ -180,9 +226,13 @@ static void bc_standard_mul(bc_num n1, size_t n1len, bc_num n2, size_t n2len, bc
180226
char *pend = pptr + prodlen - 1;
181227
i = 0;
182228
while (i < prod_arr_size - 1) {
183-
for (size_t j = 0; j < BC_MUL_UINT_DIGITS; j++) {
184-
*pend-- = prod_uint[i] % BASE;
185-
prod_uint[i] /= BASE;
229+
if (BC_MUL_UINT_DIGITS == 4) {
230+
bc_write_bcd_representation(prod_uint[i], pend - 3);
231+
pend -= 4;
232+
} else {
233+
bc_write_bcd_representation(prod_uint[i] / 10000, pend - 7);
234+
bc_write_bcd_representation(prod_uint[i] % 10000, pend - 3);
235+
pend -= 8;
186236
}
187237
i++;
188238
}

0 commit comments

Comments
 (0)