@@ -58,12 +58,68 @@ static inline void bc_digits_adjustment(BC_UINT_T *prod_uint, size_t prod_arr_si
58
58
}
59
59
}
60
60
61
+ /* This is based on the technique described in https://kholdstare.github.io/technical/2020/05/26/faster-integer-parsing.html.
62
+ * This function transforms AABBCCDD into 1000 * AA + 100 * BB + 10 * CC + DD,
63
+ * with the caveat that all components must be in the interval [0, 25] to prevent overflow
64
+ * due to the multiplication by power of 10 (10 * 25 = 250 is the largest number that fits in a byte).
65
+ * The advantage of this method instead of using shifts + 3 multiplications is that this is cheaper
66
+ * due to its divide-and-conquer nature.
67
+ */
68
+ #if SIZEOF_SIZE_T == 4
69
+ static uint32_t bc_parse_chunk_chars (const char * str )
70
+ {
71
+ uint32_t tmp ;
72
+ memcpy (& tmp , str , sizeof (tmp ));
73
+ #if !BC_LITTLE_ENDIAN
74
+ tmp = BC_BSWAP (tmp );
75
+ #endif
76
+
77
+ uint32_t lower_digits = (tmp & 0x0f000f00 ) >> 8 ;
78
+ uint32_t upper_digits = (tmp & 0x000f000f ) * 10 ;
79
+
80
+ tmp = lower_digits + upper_digits ;
81
+
82
+ lower_digits = (tmp & 0x00ff0000 ) >> 16 ;
83
+ upper_digits = (tmp & 0x000000ff ) * 100 ;
84
+
85
+ return lower_digits + upper_digits ;
86
+ }
87
+ #elif SIZEOF_SIZE_T == 8
88
+ static uint64_t bc_parse_chunk_chars (const char * str )
89
+ {
90
+ uint64_t tmp ;
91
+ memcpy (& tmp , str , sizeof (tmp ));
92
+ #if !BC_LITTLE_ENDIAN
93
+ tmp = BC_BSWAP (tmp );
94
+ #endif
95
+
96
+ uint64_t lower_digits = (tmp & 0x0f000f000f000f00 ) >> 8 ;
97
+ uint64_t upper_digits = (tmp & 0x000f000f000f000f ) * 10 ;
98
+
99
+ tmp = lower_digits + upper_digits ;
100
+
101
+ lower_digits = (tmp & 0x00ff000000ff0000 ) >> 16 ;
102
+ upper_digits = (tmp & 0x000000ff000000ff ) * 100 ;
103
+
104
+ tmp = lower_digits + upper_digits ;
105
+
106
+ lower_digits = (tmp & 0x0000ffff00000000 ) >> 32 ;
107
+ upper_digits = (tmp & 0x000000000000ffff ) * 10000 ;
108
+
109
+ return lower_digits + upper_digits ;
110
+ }
111
+ #endif
112
+
61
113
/*
62
114
* Converts BCD to uint, going backwards from pointer n by the number of
63
115
* characters specified by len.
64
116
*/
65
117
static inline BC_UINT_T bc_partial_convert_to_uint (const char * n , size_t len )
66
118
{
119
+ if (len == BC_MUL_UINT_DIGITS ) {
120
+ return bc_parse_chunk_chars (n - BC_MUL_UINT_DIGITS + 1 );
121
+ }
122
+
67
123
BC_UINT_T num = 0 ;
68
124
BC_UINT_T base = 1 ;
69
125
@@ -226,14 +282,14 @@ static void bc_standard_mul(bc_num n1, size_t n1len, bc_num n2, size_t n2len, bc
226
282
char * pend = pptr + prodlen - 1 ;
227
283
i = 0 ;
228
284
while (i < prod_arr_size - 1 ) {
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 ;
236
- }
285
+ # if BC_MUL_UINT_DIGITS == 4
286
+ bc_write_bcd_representation (prod_uint [i ], pend - 3 );
287
+ pend -= 4 ;
288
+ # else
289
+ bc_write_bcd_representation (prod_uint [i ] / 10000 , pend - 7 );
290
+ bc_write_bcd_representation (prod_uint [i ] % 10000 , pend - 3 );
291
+ pend -= 8 ;
292
+ #endif
237
293
i ++ ;
238
294
}
239
295
0 commit comments