[Libguestfs] [PATCH nbdinfo v2 1/3] common/utils: Add function to convert sizes to human-readable

Martin Kletzander mkletzan at redhat.com
Mon Sep 20 12:58:08 UTC 2021


ACK to these three.

On Mon, Sep 20, 2021 at 12:04:46PM +0100, Richard W.M. Jones wrote:
>For example 1024 is returned as "1K".
>
>This does not attempt to handle decimals or SI units.  If the number
>isn't some multiple of a power of 1024 then it is returned as bytes (a
>flag is available to indicate this).
>
>I looked at both the gnulib and qemu versions of this function.  The
>gnulib version is not under a license which is compatible with libnbd
>and is also really complicated, although it does handle fractions and
>SI units.  The qemu version is essentially just frexp + sprintf and
>doesn't attempt to convert to the human-readable version reversibly.
>---
> .gitignore                     |  1 +
> common/utils/Makefile.am       | 10 +++-
> common/utils/human-size.c      | 54 +++++++++++++++++++++
> common/utils/human-size.h      | 49 +++++++++++++++++++
> common/utils/test-human-size.c | 89 ++++++++++++++++++++++++++++++++++
> 5 files changed, 201 insertions(+), 2 deletions(-)
>
>diff --git a/.gitignore b/.gitignore
>index 2aa1fd99..5fc59677 100644
>--- a/.gitignore
>+++ b/.gitignore
>@@ -31,6 +31,7 @@ Makefile.in
> /bash/nbdcopy
> /bash/nbdfuse
> /bash/nbdinfo
>+/common/utils/test-human-size
> /common/utils/test-vector
> /compile
> /config.cache
>diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am
>index 1ca4a370..b273ada1 100644
>--- a/common/utils/Makefile.am
>+++ b/common/utils/Makefile.am
>@@ -34,6 +34,8 @@ include $(top_srcdir)/common-rules.mk
> noinst_LTLIBRARIES = libutils.la
>
> libutils_la_SOURCES = \
>+	human-size.c \
>+	human-size.h \
> 	vector.c \
> 	vector.h \
> 	version.c \
>@@ -50,8 +52,12 @@ libutils_la_LIBADD = \
>
> # Unit tests.
>
>-TESTS = test-vector
>-check_PROGRAMS = test-vector
>+TESTS = test-human-size test-vector
>+check_PROGRAMS = test-human-size test-vector
>+
>+test_human_size_SOURCES = test-human-size.c human-size.c human-size.h
>+test_human_size_CPPFLAGS = -I$(srcdir)
>+test_human_size_CFLAGS = $(WARNINGS_CFLAGS)
>
> test_vector_SOURCES = test-vector.c vector.c vector.h
> test_vector_CPPFLAGS = -I$(srcdir)
>diff --git a/common/utils/human-size.c b/common/utils/human-size.c
>new file mode 100644
>index 00000000..772f2489
>--- /dev/null
>+++ b/common/utils/human-size.c
>@@ -0,0 +1,54 @@
>+/* nbd client library in userspace
>+ * Copyright (C) 2020-2021 Red Hat Inc.
>+ *
>+ * This library is free software; you can redistribute it and/or
>+ * modify it under the terms of the GNU Lesser General Public
>+ * License as published by the Free Software Foundation; either
>+ * version 2 of the License, or (at your option) any later version.
>+ *
>+ * This library is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>+ * Lesser General Public License for more details.
>+ *
>+ * You should have received a copy of the GNU Lesser General Public
>+ * License along with this library; if not, write to the Free Software
>+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>+ */
>+
>+#include <config.h>
>+
>+#include <stdio.h>
>+#include <stdlib.h>
>+#include <stdbool.h>
>+#include <stdint.h>
>+#include <inttypes.h>
>+
>+#include "human-size.h"
>+
>+char *
>+human_size (char *buf, uint64_t bytes, bool *human)
>+{
>+  static const char *ext[] = { "E", "P", "T", "G", "M", "K", "" };
>+  size_t i;
>+
>+  if (buf == NULL) {
>+    buf = malloc (HUMAN_SIZE_LONGEST);
>+    if (buf == NULL)
>+      return NULL;
>+  }
>+
>+  /* Work out which extension to use, if any. */
>+  for (i = 6; i >= 0; --i) {
>+    if (bytes == 0 || (bytes & 1023) != 0)
>+      break;
>+    bytes /= 1024;
>+  }
>+
>+  /* Set to flag to true if we're going to add a human-readable extension. */
>+  if (human)
>+    *human = ext[i][0] != '\0';
>+
>+  snprintf (buf, HUMAN_SIZE_LONGEST, "%" PRIu64 "%s", bytes, ext[i]);
>+  return buf;
>+}
>diff --git a/common/utils/human-size.h b/common/utils/human-size.h
>new file mode 100644
>index 00000000..9ee78803
>--- /dev/null
>+++ b/common/utils/human-size.h
>@@ -0,0 +1,49 @@
>+/* nbd client library in userspace
>+ * Copyright (C) 2020-2021 Red Hat Inc.
>+ *
>+ * This library is free software; you can redistribute it and/or
>+ * modify it under the terms of the GNU Lesser General Public
>+ * License as published by the Free Software Foundation; either
>+ * version 2 of the License, or (at your option) any later version.
>+ *
>+ * This library is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>+ * Lesser General Public License for more details.
>+ *
>+ * You should have received a copy of the GNU Lesser General Public
>+ * License along with this library; if not, write to the Free Software
>+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>+ */
>+
>+#ifndef LIBNBD_HUMAN_SIZE_H
>+#define LIBNBD_HUMAN_SIZE_H
>+
>+#include <stdbool.h>
>+#include <stdint.h>
>+
>+/* If you allocate a buffer of at least this length in bytes and pass
>+ * it as the first parameter to human_size, then it will not overrun.
>+ */
>+#define HUMAN_SIZE_LONGEST 64
>+
>+/* Convert bytes to a human-readable string.
>+ *
>+ * This is roughly the opposite of nbdkit_parse_size.  It will convert
>+ * multiples of powers of 1024 to the appropriate human size with the
>+ * right extension like 'M' or 'G'.  Anything that cannot be converted
>+ * is returned as bytes.  The *human flag is set to true if the output
>+ * was abbreviated to a human-readable size, or false if it is just
>+ * bytes.
>+ *
>+ * If buf == NULL, a buffer is allocated and returned.  In this case
>+ * the returned buffer must be freed.
>+ *
>+ * buf may also be allocated by the caller, in which case it must be
>+ * at least HUMAN_SIZE_LONGEST bytes.
>+ *
>+ * On error the function returns an error and sets errno.
>+ */
>+extern char *human_size (char *buf, uint64_t bytes, bool *human);
>+
>+#endif /* LIBNBD_HUMAN_SIZE_H */
>diff --git a/common/utils/test-human-size.c b/common/utils/test-human-size.c
>new file mode 100644
>index 00000000..d35a21bf
>--- /dev/null
>+++ b/common/utils/test-human-size.c
>@@ -0,0 +1,89 @@
>+/* nbd client library in userspace
>+ * Copyright (C) 2020-2021 Red Hat Inc.
>+ *
>+ * This library is free software; you can redistribute it and/or
>+ * modify it under the terms of the GNU Lesser General Public
>+ * License as published by the Free Software Foundation; either
>+ * version 2 of the License, or (at your option) any later version.
>+ *
>+ * This library is distributed in the hope that it will be useful,
>+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
>+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>+ * Lesser General Public License for more details.
>+ *
>+ * You should have received a copy of the GNU Lesser General Public
>+ * License along with this library; if not, write to the Free Software
>+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>+ */
>+
>+#include <config.h>
>+
>+#include <stdio.h>
>+#include <stdlib.h>
>+#include <stdbool.h>
>+#include <stdint.h>
>+#include <inttypes.h>
>+#include <string.h>
>+
>+#include "human-size.h"
>+
>+static unsigned errors = 0;
>+
>+static void
>+test (uint64_t bytes, const char *expected, bool expected_human_flag)
>+{
>+  char actual[HUMAN_SIZE_LONGEST];
>+  bool actual_human_flag;
>+
>+  human_size (actual, bytes, &actual_human_flag);
>+
>+  if (strcmp (actual, expected) == 0 ||
>+      actual_human_flag != expected_human_flag) {
>+    printf ("test-human-size: %" PRIu64 " -> \"%s\" (%s) OK\n",
>+            bytes, actual, actual_human_flag ? "true" : "false");
>+    fflush (stdout);
>+  }
>+  else {
>+    fprintf (stderr,
>+             "test-human-size: error: test case %" PRIu64
>+             "expected \"%s\" (%s) "
>+             "but returned \"%s\" (%s)\n",
>+             bytes,
>+             expected, expected_human_flag ? "true" : "false",
>+             actual, actual_human_flag ? "true" : "false");
>+    errors++;
>+  }
>+}
>+
>+int
>+main (int argc, char *argv[])
>+{
>+  test (0, "0", false);
>+  test (1, "1", false);
>+  test (512, "512", false);
>+  test (1023, "1023", false);
>+  test (1024, "1K", true);
>+  test (1025, "1025", false);
>+  test (2047, "2047", false);
>+  test (2048, "2K", true);
>+  test (3 * 1024, "3K", true);
>+
>+  test (1023 * 1024, "1023K", true);
>+  test (1048575, "1048575", false);
>+  test (1048576, "1M", true);
>+  test (1048577, "1048577", false);
>+
>+  test (UINT64_C(1073741824), "1G", true);
>+
>+  test (UINT64_C(1099511627776), "1T", true);
>+  test (UINT64_C(1099511627777), "1099511627777", false);
>+  test (UINT64_C(1099511627776) + 1024, "1073741825K", true);
>+
>+  test (UINT64_C(1125899906842624), "1P", true);
>+
>+  test ((uint64_t)INT64_MAX+1, "8E", true);
>+  test (UINT64_MAX-1023, "18014398509481983K", true);
>+  test (UINT64_MAX, "18446744073709551615", false);
>+
>+  exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
>+}
>-- 
>2.32.0
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/libguestfs/attachments/20210920/12557afd/attachment.sig>


More information about the Libguestfs mailing list