On 2010-08-16 10:51, Dag-Erling Smørgrav wrote: > Bruce Cran <br...@cran.org.uk> writes: >> Somewhat related, there are overflow bugs in humanize_number - for >> example df(1) fails to display space from a 100PB filesystem >> correctly. > > Patch? :)
Attached. This makes humanize_number() work properly for all possible int64_t values. It has one significant behaviour change, though: with the original version of humanize_number(), if you used a fixed scale parameter, and the number was larger than the buffer could contain, it would simply stomp over the end of the buffer. With this fix, it will return -1 instead. I hope you agree that is better. :)
diff --git a/lib/libutil/humanize_number.c b/lib/libutil/humanize_number.c index de98587..a304137 100644 --- a/lib/libutil/humanize_number.c +++ b/lib/libutil/humanize_number.c @@ -42,13 +42,34 @@ __FBSDID("$FreeBSD$"); #include <locale.h> #include <libutil.h> +static inline void +divide(int64_t num, int64_t den, int64_t *quotp, int64_t *quotrp, int *fracrp) +{ + int frac; + + if (den == 1) { + *quotp = *quotrp = num; + *fracrp = 0; + } else { + *quotp = *quotrp = num / den; + frac = (5 * (num % den)) / (den / 2); + if (frac >= 5) + ++*quotp; + *fracrp = (5 * (num % den) + den / 4) / (den / 2); + if (*fracrp == 10) { + ++*quotrp; + *fracrp = 0; + } + } +} + int humanize_number(char *buf, size_t len, int64_t bytes, const char *suffix, int scale, int flags) { const char *prefixes, *sep; - int b, i, r, maxscale, s1, s2, sign; - int64_t divisor, max; + int i, r, maxscale, sign, fracr; + int64_t divisor, max, fulldiv, quot, quotr; size_t baselen; assert(buf != NULL); @@ -88,11 +109,10 @@ humanize_number(char *buf, size_t len, int64_t bytes, buf[0] = '\0'; if (bytes < 0) { sign = -1; - bytes *= -100; + bytes = -bytes; baselen = 3; /* sign, digit, prefix */ } else { sign = 1; - bytes *= 100; baselen = 2; /* digit, prefix */ } if (flags & HN_NOSPACE) @@ -107,39 +127,42 @@ humanize_number(char *buf, size_t len, int64_t bytes, if (len < baselen + 1) return (-1); + /* Determine the maximum number that fits. */ + for (max = 1, i = len - baselen; i-- > 0;) + max *= 10; + if (scale & (HN_AUTOSCALE | HN_GETSCALE)) { - /* See if there is additional columns can be used. */ - for (max = 100, i = len - baselen; i-- > 0;) - max *= 10; - - /* - * Divide the number until it fits the given column. - * If there will be an overflow by the rounding below, - * divide once more. - */ - for (i = 0; bytes >= max - 50 && i < maxscale; i++) - bytes /= divisor; + /* Divide the number until it fits the given length. */ + for (i = 0, fulldiv = 1; i < maxscale; i++) { + divide(bytes, fulldiv, ", "r, &fracr); + if ((quotr < 10 && i > 0 && flags & HN_DECIMAL && + quotr * 100 < max) || quot < max) + break; + fulldiv *= divisor; + } if (scale & HN_GETSCALE) return (i); - } else - for (i = 0; i < scale && i < maxscale; i++) - bytes /= divisor; + } else { + for (i = 0, fulldiv = 1; i < scale; i++) + fulldiv *= divisor; + divide(bytes, fulldiv, ", "r, &fracr); + if ((quotr < 10 && i > 0 && flags & HN_DECIMAL && + quotr * 100 >= max) || quot >= max) + return (-1); + } - /* If a value <= 9.9 after rounding and ... */ - if (bytes < 995 && i > 0 && flags & HN_DECIMAL) { + /* If quotient < 10 after rounding and ... */ + if (quotr < 10 && 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; r = snprintf(buf, len, "%d%s%d%s%s%s", - sign * s1, localeconv()->decimal_point, s2, + sign * (int) quotr, localeconv()->decimal_point, fracr, sep, SCALE2PREFIX(i), suffix); } else r = snprintf(buf, len, "%" PRId64 "%s%s%s", - sign * ((bytes + 50) / 100), + sign * quot, sep, SCALE2PREFIX(i), suffix); return (r);
_______________________________________________ svn-src-head@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-head To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"