diff options
author | Xin LI <delphij@FreeBSD.org> | 2011-03-23 22:08:01 +0000 |
---|---|---|
committer | Xin LI <delphij@FreeBSD.org> | 2011-03-23 22:08:01 +0000 |
commit | a699e14f45bce95043f32955578d3190f9f90ce0 (patch) | |
tree | f7277fe9ba7302c849010cbfe0797d26a5561479 /lib/libutil | |
parent | 784ae1fdd1a31b442efdb37abcc45373911e7576 (diff) | |
download | src-a699e14f45bce95043f32955578d3190f9f90ce0.tar.gz src-a699e14f45bce95043f32955578d3190f9f90ce0.zip |
humanize_number(3) multiply the input number by 100, which could cause an
integer overflow when the input is very large (for example, 100 Pi would
become about 10 Ei which exceeded signed int64_t).
Solve this issue by splitting the division into two parts and avoid the
multiplication.
PR: bin/146205
Reviewed by: arundel
MFC after: 1 month
Notes
Notes:
svn path=/head/; revision=219939
Diffstat (limited to 'lib/libutil')
-rw-r--r-- | lib/libutil/humanize_number.c | 40 |
1 files changed, 24 insertions, 16 deletions
diff --git a/lib/libutil/humanize_number.c b/lib/libutil/humanize_number.c index de985870ff37..75bcb460b5d6 100644 --- a/lib/libutil/humanize_number.c +++ b/lib/libutil/humanize_number.c @@ -43,11 +43,11 @@ __FBSDID("$FreeBSD$"); #include <libutil.h> int -humanize_number(char *buf, size_t len, int64_t bytes, +humanize_number(char *buf, size_t len, int64_t quotient, const char *suffix, int scale, int flags) { const char *prefixes, *sep; - int b, i, r, maxscale, s1, s2, sign; + int i, r, remainder, maxscale, s1, s2, sign; int64_t divisor, max; size_t baselen; @@ -55,6 +55,8 @@ humanize_number(char *buf, size_t len, int64_t bytes, assert(suffix != NULL); assert(scale >= 0); + remainder = 0; + if (flags & HN_DIVISOR_1000) { /* SI for decimal multiplies */ divisor = 1000; @@ -86,13 +88,12 @@ humanize_number(char *buf, size_t len, int64_t bytes, if (len > 0) buf[0] = '\0'; - if (bytes < 0) { + if (quotient < 0) { sign = -1; - bytes *= -100; + quotient = -quotient; baselen = 3; /* sign, digit, prefix */ } else { sign = 1; - bytes *= 100; baselen = 2; /* digit, prefix */ } if (flags & HN_NOSPACE) @@ -109,7 +110,7 @@ humanize_number(char *buf, size_t len, int64_t bytes, if (scale & (HN_AUTOSCALE | HN_GETSCALE)) { /* See if there is additional columns can be used. */ - for (max = 100, i = len - baselen; i-- > 0;) + for (max = 1, i = len - baselen; i-- > 0;) max *= 10; /* @@ -117,30 +118,37 @@ humanize_number(char *buf, size_t len, int64_t bytes, * If there will be an overflow by the rounding below, * divide once more. */ - for (i = 0; bytes >= max - 50 && i < maxscale; i++) - bytes /= divisor; + for (i = 0; + (quotient >= max || (quotient == max - 1 && remainder >= 950)) && + i < maxscale; i++) { + remainder = quotient % divisor; + quotient /= divisor; + } if (scale & HN_GETSCALE) return (i); - } else - for (i = 0; i < scale && i < maxscale; i++) - bytes /= divisor; + } else { + for (i = 0; i < scale && i < maxscale; i++) { + remainder = quotient % divisor; + quotient /= divisor; + } + } /* If a value <= 9.9 after rounding and ... */ - if (bytes < 995 && i > 0 && flags & HN_DECIMAL) { + if (quotient <= 9 && remainder < 950 && i > 0 && flags & HN_DECIMAL) { /* baselen + \0 + .N */ if (len < baselen + 1 + 2) return (-1); - b = ((int)bytes + 5) / 10; - s1 = b / 10; - s2 = b % 10; + s1 = (int)quotient + ((remainder + 50) / 1000); + s2 = ((remainder + 50) / 100) % 10; r = snprintf(buf, len, "%d%s%d%s%s%s", sign * s1, localeconv()->decimal_point, s2, sep, SCALE2PREFIX(i), suffix); } else r = snprintf(buf, len, "%" PRId64 "%s%s%s", - sign * ((bytes + 50) / 100), + sign * (quotient + (remainder + 50) / 1000), sep, SCALE2PREFIX(i), suffix); return (r); } + |