Collect a bunch of digits into a uint64_t first...

This commit is contained in:
K. Lange 2022-06-28 07:48:10 +09:00
parent 21cd0c8fbe
commit 107919024b

View File

@ -81,6 +81,47 @@ static int krk_long_init_si(KrkLong * num, int64_t val) {
return 0;
}
/**
* @brief Initialize an untouched KrkLong with an unsigned 64-bit value.
*/
static int krk_long_init_ui(KrkLong * num, uint64_t val) {
/* Special-case for 0: width = 0, no digits. */
if (val == 0) {
num->width = 0;
num->digits = NULL;
return 0;
}
if (val <= DIGIT_MAX) {
num->width = 1;
num->digits = malloc(sizeof(uint32_t));
num->digits[0] = val;
return 0;
}
/* Figure out how many digits we need. */
uint64_t tmp = val;
uint64_t cnt = 1;
while (tmp > DIGIT_MAX) {
cnt++;
tmp >>= DIGIT_SHIFT;
}
/* Allocate space */
num->width = cnt;
num->digits = malloc(sizeof(uint32_t) * cnt);
/* Extract digits. */
for (uint64_t i = 0; i < cnt; ++i) {
num->digits[i] = (val & DIGIT_MAX);
val >>= DIGIT_SHIFT;
}
return 0;
}
/**
* @brief Variadic initializer. All values become 0.
*
@ -1069,21 +1110,30 @@ static int krk_long_parse_string(const char * str, KrkLong * num, unsigned int b
krk_long_init_si(num, 0);
KrkLong _base, scratch;
krk_long_init_si(&_base, base);
krk_long_init_si(&_base, 0);
krk_long_init_si(&scratch, 0);
while (c < end && *c) {
uint64_t accum = 0;
uint64_t basediv = 1;
while (c < end && *c && (basediv * base < 0x10000000000000UL)) {
if (*c == '_') c++;
if (c == (str + len) || !is_valid(base, *c)) {
krk_long_clear_many(&_base, &scratch, num, NULL);
return 1;
}
krk_long_mul(num, num, &_base);
krk_long_clear(&scratch);
krk_long_init_si(&scratch, convert_digit(*c));
krk_long_add(num, num, &scratch);
basediv *= base;
accum *= base;
accum += convert_digit(*c);
c++;
}
krk_long_init_ui(&_base, basediv);
krk_long_mul(num, num, &_base);
krk_long_clear_many(&scratch, &_base, NULL);
krk_long_init_ui(&scratch, accum);
krk_long_add(num, num, &scratch);
}
if (sign == -1) {
krk_long_set_sign(num, -1);