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, ", "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);
More information about the svn-src-head
mailing list