]> git.ipfire.org Git - thirdparty/libbsd.git/commitdiff
Update humanize_number() from FreeBSD
authorGuillem Jover <guillem@hadrons.org>
Sun, 20 May 2018 17:22:16 +0000 (19:22 +0200)
committerGuillem Jover <guillem@hadrons.org>
Mon, 21 May 2018 02:47:28 +0000 (04:47 +0200)
Implements HN_IEC_PREFIXES.

COPYING
include/bsd/libutil.h
src/humanize_number.c

diff --git a/COPYING b/COPYING
index b768093d9a27859175e0aa17f5355c0b23d09d5d..c38a56b1ab5ec4f62bd4b5c030674436c3e0dcbb 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -273,6 +273,7 @@ Files:
  src/stringlist.c
 Copyright:
  Copyright © 1994, 1997-2000, 2002, 2008, 2010 The NetBSD Foundation, Inc.
+ Copyright © 2013 John-Mark Gurney <jmg@FreeBSD.org>
  All rights reserved.
  .
  Some code was contributed to The NetBSD Foundation by Allen Briggs.
index e4efd6bdf1afda98eb6a18287636c85efb30155d..9bdcc7f3775798737195f9ec7be026a36411894e 100644 (file)
@@ -67,14 +67,16 @@ int pidfile_remove(struct pidfh *pfh);
 char   *fparseln(FILE *, size_t *, size_t *, const char[3], int);
 __END_DECLS
 
-/* humanize_number(3) */
-#define HN_DECIMAL              0x01
-#define HN_NOSPACE              0x02
-#define HN_B                    0x04
-#define HN_DIVISOR_1000         0x08
+/* Values for humanize_number(3)'s flags parameter. */
+#define HN_DECIMAL             0x01
+#define HN_NOSPACE             0x02
+#define HN_B                   0x04
+#define HN_DIVISOR_1000                0x08
+#define HN_IEC_PREFIXES                0x10
 
-#define HN_GETSCALE             0x10
-#define HN_AUTOSCALE            0x20
+/* Values for humanize_number(3)'s scale parameter. */
+#define HN_GETSCALE            0x10
+#define HN_AUTOSCALE           0x20
 
 /*
  * fparseln() specific operation flags.
index f8cf633a6c132e136c7043d6d0cb223660cc0327..e2e4703ee3d98af6acb04a62738b6ebfde301c36 100644 (file)
@@ -1,7 +1,10 @@
 /*     $NetBSD: humanize_number.c,v 1.14 2008/04/28 20:22:59 martin Exp $      */
 
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
  * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
+ * Copyright 2013 John-Mark Gurney <jmg@FreeBSD.org>
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -31,6 +34,7 @@
  */
 
 #include <sys/cdefs.h>
+#include <sys/types.h>
 #include <assert.h>
 #include <inttypes.h>
 #include <stdio.h>
 #include <locale.h>
 #include <libutil.h>
 
+static const int maxscale = 6;
+
 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, s1, s2, sign;
+       int     divisordeccut;
        int64_t divisor, max;
        size_t  baselen;
 
-       assert(buf != NULL);
-       assert(suffix != NULL);
-       assert(scale >= 0);
+       /* Since so many callers don't check -1, NUL terminate the buffer */
+       if (len > 0)
+               buf[0] = '\0';
 
-       if (flags & HN_DIVISOR_1000) {
-               /* SI for decimal multiplies */
-               divisor = 1000;
-               if (flags & HN_B)
-                       prefixes = "B\0k\0M\0G\0T\0P\0E";
-               else
-                       prefixes = "\0\0k\0M\0G\0T\0P\0E";
-       } else {
+       /* validate args */
+       if (buf == NULL || suffix == NULL)
+               return (-1);
+       if (scale < 0)
+               return (-1);
+       else if (scale > maxscale &&
+           ((scale & ~(HN_AUTOSCALE|HN_GETSCALE)) != 0))
+               return (-1);
+       if ((flags & HN_DIVISOR_1000) && (flags & HN_IEC_PREFIXES))
+               return (-1);
+
+       /* setup parameters */
+       remainder = 0;
+
+       if (flags & HN_IEC_PREFIXES) {
+               baselen = 2;
                /*
-                * binary multiplies
-                * XXX IEC 60027-2 recommends Ki, Mi, Gi...
+                * Use the prefixes for power of two recommended by
+                * the International Electrotechnical Commission
+                * (IEC) in IEC 80000-3 (i.e. Ki, Mi, Gi...).
+                *
+                * HN_IEC_PREFIXES implies a divisor of 1024 here
+                * (use of HN_DIVISOR_1000 would have triggered
+                * an assertion earlier).
                 */
                divisor = 1024;
+               divisordeccut = 973;    /* ceil(.95 * 1024) */
                if (flags & HN_B)
-                       prefixes = "B\0K\0M\0G\0T\0P\0E";
+                       prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
                else
-                       prefixes = "\0\0K\0M\0G\0T\0P\0E";
+                       prefixes = "\0\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
+       } else {
+               baselen = 1;
+               if (flags & HN_DIVISOR_1000) {
+                       divisor = 1000;
+                       divisordeccut = 950;
+                       if (flags & HN_B)
+                               prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
+                       else
+                               prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
+               } else {
+                       divisor = 1024;
+                       divisordeccut = 973;    /* ceil(.95 * 1024) */
+                       if (flags & HN_B)
+                               prefixes = "B\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
+                       else
+                               prefixes = "\0\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
+               }
        }
 
-#define        SCALE2PREFIX(scale)     (&prefixes[(scale) << 1])
-       maxscale = 7;
-
-       if (scale >= maxscale &&
-           (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0)
-               return (-1);
-
-       if (buf == NULL || suffix == NULL)
-               return (-1);
+#define        SCALE2PREFIX(scale)     (&prefixes[(scale) * 3])
 
-       if (len > 0)
-               buf[0] = '\0';
-       if (bytes < 0) {
+       if (quotient < 0) {
                sign = -1;
-               bytes *= -100;
-               baselen = 3;            /* sign, digit, prefix */
+               quotient = -quotient;
+               baselen += 2;           /* sign, digit */
        } else {
                sign = 1;
-               bytes *= 100;
-               baselen = 2;            /* digit, prefix */
+               baselen += 1;           /* digit */
        }
        if (flags & HN_NOSPACE)
                sep = "";
@@ -106,7 +133,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;
 
                /*
@@ -114,29 +141,39 @@ 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 >= divisordeccut || remainder >=
+                   divisor / 2))) && 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) {
-               /* baselen + \0 + .N */
-               if (len < baselen + 1 + 2)
-                       return (-1);
-               b = ((int)bytes + 5) / 10;
-               s1 = b / 10;
-               s2 = b % 10;
+       /*
+        * XXX - should we make sure there is enough space for the decimal
+        * place and if not, don't do HN_DECIMAL?
+        */
+       if (((quotient == 9 && remainder < divisordeccut) || quotient < 9) &&
+           i > 0 && flags & HN_DECIMAL) {
+               s1 = (int)quotient + ((remainder * 10 + divisor / 2) /
+                   divisor / 10);
+               s2 = ((remainder * 10 + divisor / 2) / divisor) % 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 + divisor / 2) / divisor),
                    sep, SCALE2PREFIX(i), suffix);
 
        return (r);