|
48 | 48 |
|
49 | 49 | /* Multiply utility routines */
|
50 | 50 |
|
| 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 | + |
51 | 103 | /*
|
52 | 104 | * Converts BCD to uint, going backwards from pointer n by the number of
|
53 | 105 | * characters specified by len.
|
54 | 106 | */
|
55 | 107 | static inline BC_UINT_T bc_partial_convert_to_uint(const char *n, size_t len)
|
56 | 108 | {
|
| 109 | + if (len == BC_MUL_UINT_DIGITS) { |
| 110 | + return bc_parse_chunk_chars(n - BC_MUL_UINT_DIGITS + 1); |
| 111 | + } |
| 112 | + |
57 | 113 | BC_UINT_T num = 0;
|
58 | 114 | BC_UINT_T base = 1;
|
59 | 115 |
|
|
0 commit comments