Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/lib/libc/gen PR lib/54053



details:   https://anonhg.NetBSD.org/src/rev/50bc87eab58e
branches:  trunk
changeset: 449578:50bc87eab58e
user:      kre <kre%NetBSD.org@localhost>
date:      Mon Mar 11 15:10:51 2019 +0000

description:
PR lib/54053

When auto scaling, and the buffer is bigger than big enough
for the biggest possible number, don't try and calculate
the max value that will fit in the buffer - that calc
will overflow (guaranteed) and is useless, the value
we're formatting cannot possibly be bigger.  So simply
use the unscaled value (the raw number).

While here, also avoid returning values that are larger
than the buffer len ... while it would be nice to be able
to find out how big the buffer should be so the data will
fit, the interface doesn't really allow that (the buffer
length passed in controls the scaling - at least when
auto scaling) and the code already does "return -1" when
it detects the buffer length is too small, even before
it works out how much would have been needed.  So, rather
than returning a value > len (while truncating the result
to fit in len ... all courtesy of snprintf()) return -1
in this case as well.

Also, allow suffix==NULL (meaning "") - there's no reason
not to, and requiring users to pass in an explicit "" is
not useful.

diffstat:

 lib/libc/gen/humanize_number.3 |   45 +++++++++++----
 lib/libc/gen/humanize_number.c |  115 +++++++++++++++++++++++++++-------------
 2 files changed, 111 insertions(+), 49 deletions(-)

diffs (274 lines):

diff -r cdeb22c1d4c6 -r 50bc87eab58e lib/libc/gen/humanize_number.3
--- a/lib/libc/gen/humanize_number.3    Mon Mar 11 14:35:22 2019 +0000
+++ b/lib/libc/gen/humanize_number.3    Mon Mar 11 15:10:51 2019 +0000
@@ -1,4 +1,4 @@
-.\"    $NetBSD: humanize_number.3,v 1.11 2011/08/20 21:35:32 wiz Exp $
+.\"    $NetBSD: humanize_number.3,v 1.12 2019/03/11 15:10:51 kre Exp $
 .\"
 .\" Copyright (c) 1999, 2002, 2008 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -27,7 +27,7 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd January 14, 2011
+.Dd March 11, 2019
 .Dt HUMANIZE_NUMBER 3
 .Os
 .Sh NAME
@@ -47,21 +47,21 @@
 .Fa number
 into
 .Fa buffer .
-A space and then
+A space and then the
 .Fa suffix
+.Pq "if not null"
 is appended to the end.
-.Fa buffer
-must be at least
 .Fa len
-bytes long.
+gives the size of the
+.Fa buffer .
 .Pp
 If the formatted number (including
 .Fa suffix )
 would be too long to fit into
 .Fa buffer ,
-then divide
+then repeatedly divide
 .Fa number
-by 1024 until it will.
+by 1024 until it will fit.
 In this case, prefix
 .Fa suffix
 with the appropriate SI designator.
@@ -85,11 +85,11 @@
 To use a specific prefix, specify this as
 .Fa scale
 (Multiplier = 1024 ^ scale).
-This can not be combined with any of the
+The
 .Fa scale
-flags below.
+must be at least 0 and no more than 6.
 .Pp
-The following flags may be passed in
+Alternatively, one of fhe following special values may be given as
 .Pa scale :
 .Bl -tag -width Dv -offset indent
 .It Dv HN_AUTOSCALE
@@ -98,13 +98,20 @@
 Return the prefix index number (the number of times
 .Fa number
 must be divided to fit) instead of formatting it to the buffer.
+That is, the
+.Fa scale
+that would have been used if
+.Dv HN_AUTOSCALE
+had been used.
 .El
 .Pp
 The following flags may be passed in
 .Pa flags :
 .Bl -tag -width Dv -offset indent
 .It Dv HN_DECIMAL
-If the final result is less than 10, display it using one digit.
+If the final numeric result is less than 10,
+and is not the same as the original value (that is, it has been scaled)
+display it using a decimal radix character, and one following digit.
 .It Dv HN_NOSPACE
 Do not put a space between
 .Fa number
@@ -115,8 +122,22 @@
 Divide
 .Fa number
 with 1000 instead of 1024.
+That is, use decimal scaling instead of binary.
 .El
 .Pp
+To generate the shortest meaningful value,
+a buffer length
+.Pq Fa len
+that is 6 greater the length of the
+.Fa suffix
+along with
+.Dv HN_AUTOSCALE
+will ensure the highest meaningful scale is used.
+Allow one extra byte for the sign if the number is negative,
+and one less if the
+.Dv HN_NOSPACE
+flag is used.
+.Pp
 The
 .Fn dehumanize_number
 function parses the string representing an integral value given in
diff -r cdeb22c1d4c6 -r 50bc87eab58e lib/libc/gen/humanize_number.c
--- a/lib/libc/gen/humanize_number.c    Mon Mar 11 14:35:22 2019 +0000
+++ b/lib/libc/gen/humanize_number.c    Mon Mar 11 15:10:51 2019 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: humanize_number.c,v 1.17 2017/04/13 17:45:56 christos Exp $    */
+/*     $NetBSD: humanize_number.c,v 1.18 2019/03/11 15:10:51 kre Exp $ */
 
 /*
  * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
@@ -32,7 +32,7 @@
 
 #include <sys/cdefs.h>
 #if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: humanize_number.c,v 1.17 2017/04/13 17:45:56 christos Exp $");
+__RCSID("$NetBSD: humanize_number.c,v 1.18 2019/03/11 15:10:51 kre Exp $");
 #endif /* LIBC_SCCS and not lint */
 
 #include "namespace.h"
@@ -48,14 +48,17 @@
     const char *suffix, int scale, int flags)
 {
        const char *prefixes, *sep;
-       int     b, r, s1, s2, sign;
+       int     b, i, r, s1, s2, sign;
        int64_t divisor, max, post = 1;
-       size_t  i, baselen, maxscale;
+       size_t  baselen;
+       int     maxscale;
 
        _DIAGASSERT(buf != NULL);
-       _DIAGASSERT(suffix != NULL);
        _DIAGASSERT(scale >= 0);
 
+       if (suffix == NULL)
+               suffix = "";
+
        if (flags & HN_DIVISOR_1000) {
                /* SI for decimal multiplies */
                divisor = 1000;
@@ -78,15 +81,16 @@
 #define        SCALE2PREFIX(scale)     (&prefixes[(scale) << 1])
        maxscale = 6;
 
-       if ((size_t)scale > maxscale &&
-           (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0)
+       if (scale < 0 || (scale > maxscale &&
+           (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0))
                return (-1);
 
-       if (buf == NULL || suffix == NULL)
+       if (buf == NULL)
                return (-1);
 
        if (len > 0)
                buf[0] = '\0';
+
        if (bytes < 0) {
                sign = -1;
                baselen = 3;            /* sign, digit, prefix */
@@ -120,42 +124,79 @@
                return (-1);
 
        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;
+               /*
+                * 19 is number of digits in biggest possible int64_t
+                * If we don't do this, the calc of max just below can
+                * overflow, leading to absurd results.   If the buffer
+                * is big enough for the number, simply use it, no scaling.
+                */
+               if (len - baselen > 19)
+                       i = 0;
+               else {
+                       /* See if there are additional columns to 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 avail buffer.
+                        * If there will be an overflow by the rounding below,
+                        * (the "-50") divide once more.
+                        */
+                       for (i = 0; bytes >= max - 50 && i < maxscale; i++)
+                               bytes /= divisor;
+               }
                if (scale & HN_GETSCALE) {
                        _DIAGASSERT(__type_fit(int, i));
-                       return (int)i;
+                       return i;
                }
-       } else
-               for (i = 0; i < (size_t)scale && i < maxscale; i++)
+       } else {
+               /* XXX
+                * we already know scale <= maxscale, so
+                * i < scale ==> i < maxscale
+                */
+               for (i = 0; i < scale && i < maxscale; i++)
                        bytes /= divisor;
-       bytes *= post;
+       }
+
+       if (i == 0) {
+               /*
+                * Cannot go the bytes *= post route, as
+                * that can cause overflow of bytes
+                *
+                * but if we already scaled up, undo that.
+                */
+               if (post == 1)
+                       bytes /= 100;
 
-       /* If a value <= 9.9 after rounding and ... */
-       if (bytes < 995 && 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,
-                   sep, SCALE2PREFIX(i), suffix);
-       } else
                r = snprintf(buf, len, "%" PRId64 "%s%s%s",
-                   sign * ((bytes + 50) / 100),
-                   sep, SCALE2PREFIX(i), suffix);
+                   sign * bytes, sep, SCALE2PREFIX(0), suffix);
+       } else {
+               /*
+                * Here this is safe, as if i > 0, we have already
+                * divided bytes by at least 1000, post <= 100, so ...
+                */
+               bytes *= post;
+
+               /* If a value <= 9.9 after rounding and ... */
+               if (bytes < 995 && i > 0 && flags & HN_DECIMAL) {
+                       /* baselen + \0 + .N */
+                       if (len < baselen + 1 + 1 +
+                           strlen(localeconv()->decimal_point))
+                               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,
+                           sep, SCALE2PREFIX(i), suffix);
+               } else
+                       r = snprintf(buf, len, "%" PRId64 "%s%s%s",
+                           sign * ((bytes + 50) / 100),
+                           sep, SCALE2PREFIX(i), suffix);
+       }
+
+       if ((size_t)r > len)
+               r = -1;
 
        return (r);
 }



Home | Main Index | Thread Index | Old Index