svn commit: r211304 - head/lib/libutil

Dimitry Andric dimitry at andric.com
Sun Aug 22 21:21:39 UTC 2010


On 2010-08-16 10:51, Dag-Erling Smørgrav wrote:
> Bruce Cran <bruce at 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. :)
-------------- next part --------------
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, &quot, &quotr, &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, &quot, &quotr, &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);


More information about the svn-src-head mailing list