From 0e66d88fa280fce9ac6099bf1b593ce0e633e5e8 Mon Sep 17 00:00:00 2001 From: Kevin Ryde Date: Wed, 8 Dec 2004 22:50:09 +0000 Subject: [PATCH] (scm_istr2int): Correction to bignum size calculation for bases other than 2, 10, 16. This avoids overflows, in particular in octal string->number and constants (other bases are undocumented, but reach here so may as well work). --- libguile/numbers.c | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/libguile/numbers.c b/libguile/numbers.c index e5357b9ff..c17b94645 100644 --- a/libguile/numbers.c +++ b/libguile/numbers.c @@ -2331,12 +2331,42 @@ scm_istr2int (char *str, long len, long radix) if (len < 6) return scm_small_istr2int (str, len, radix); - if (16 == radix) - j = 1 + (4 * len * sizeof (char)) / (SCM_BITSPERDIG); - else if (10 <= radix) - j = 1 + (84 * len * sizeof (char)) / (SCM_BITSPERDIG * 25); - else - j = 1 + (len * sizeof (char)) / (SCM_BITSPERDIG); + /* table[] is the number of bits used by each digit in the given base, + ie. log(base)/log(2). A scale factor of 25 is applied, so eg. base 8 + has 75 for 3 bits per digit. When the number is not exact (any non + power-of-2 base) it's rounded up, ensuring the size calculated will be + no less than what's needed. Eg. 25*log(10)/log(2) is 83.04 which gets + rounded up to 84. The following spot of perl generates the table + + use POSIX; + foreach $i (2 .. 16) { + print POSIX::ceil(log($i)/log(2)*25),", /","* $i *","/\n"; + } + + The factor 25 is more or less arbitrary, it gives enough precision and + is what the code had in the past for base 10. */ + { + static const unsigned table[] = { + 25, /* 2 */ + 40, /* 3 */ + 50, /* 4 */ + 59, /* 5 */ + 65, /* 6 */ + 71, /* 7 */ + 75, /* 8 */ + 80, /* 9 */ + 84, /* 10 */ + 87, /* 11 */ + 90, /* 12 */ + 93, /* 13 */ + 96, /* 14 */ + 98, /* 15 */ + 100, /* 16 */ + }; + /* FIXME: What is sizeof(char) for? */ + j = 1 + (table[radix-2] * len * sizeof (char)) / (SCM_BITSPERDIG * 25); + } + switch (str[0]) { /* leading sign */ case '-':