]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - c_src/linux/string_helpers.c
move Rust sources to top level, C sources into c_src
[bcachefs-tools-debian] / c_src / linux / string_helpers.c
diff --git a/c_src/linux/string_helpers.c b/c_src/linux/string_helpers.c
new file mode 100644 (file)
index 0000000..0810ca1
--- /dev/null
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Helpers for formatting and printing strings
+ *
+ * Copyright 31 August 2008 James Bottomley
+ * Copyright (C) 2013, Intel Corporation
+ */
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/export.h>
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/limits.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/string_helpers.h>
+
+/**
+ * string_get_size - get the size in the specified units
+ * @size:      The size to be converted in blocks
+ * @blk_size:  Size of the block (use 1 for size in bytes)
+ * @units:     units to use (powers of 1000 or 1024)
+ * @buf:       buffer to format to
+ * @len:       length of buffer
+ *
+ * This function returns a string formatted to 3 significant figures
+ * giving the size in the required units.  @buf should have room for
+ * at least 9 bytes and will always be zero terminated.
+ *
+ */
+int string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
+                   char *buf, int len)
+{
+       static const char *const units_10[] = {
+               "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
+       };
+       static const char *const units_2[] = {
+               "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"
+       };
+       static const char *const *const units_str[] = {
+               [STRING_UNITS_10] = units_10,
+               [STRING_UNITS_2] = units_2,
+       };
+       static const unsigned int divisor[] = {
+               [STRING_UNITS_10] = 1000,
+               [STRING_UNITS_2] = 1024,
+       };
+       static const unsigned int rounding[] = { 500, 50, 5 };
+       int i = 0, j;
+       u32 remainder = 0, sf_cap;
+       char tmp[12];
+       const char *unit;
+
+       tmp[0] = '\0';
+
+       if (blk_size == 0)
+               size = 0;
+       if (size == 0)
+               goto out;
+
+       /* This is Napier's algorithm.  Reduce the original block size to
+        *
+        * coefficient * divisor[units]^i
+        *
+        * we do the reduction so both coefficients are just under 32 bits so
+        * that multiplying them together won't overflow 64 bits and we keep
+        * as much precision as possible in the numbers.
+        *
+        * Note: it's safe to throw away the remainders here because all the
+        * precision is in the coefficients.
+        */
+       while (blk_size >> 32) {
+               do_div(blk_size, divisor[units]);
+               i++;
+       }
+
+       while (size >> 32) {
+               do_div(size, divisor[units]);
+               i++;
+       }
+
+       /* now perform the actual multiplication keeping i as the sum of the
+        * two logarithms */
+       size *= blk_size;
+
+       /* and logarithmically reduce it until it's just under the divisor */
+       while (size >= divisor[units]) {
+               remainder = do_div(size, divisor[units]);
+               i++;
+       }
+
+       /* work out in j how many digits of precision we need from the
+        * remainder */
+       sf_cap = size;
+       for (j = 0; sf_cap*10 < 1000; j++)
+               sf_cap *= 10;
+
+       if (units == STRING_UNITS_2) {
+               /* express the remainder as a decimal.  It's currently the
+                * numerator of a fraction whose denominator is
+                * divisor[units], which is 1 << 10 for STRING_UNITS_2 */
+               remainder *= 1000;
+               remainder >>= 10;
+       }
+
+       /* add a 5 to the digit below what will be printed to ensure
+        * an arithmetical round up and carry it through to size */
+       remainder += rounding[j];
+       if (remainder >= 1000) {
+               remainder -= 1000;
+               size += 1;
+       }
+
+       if (j) {
+               snprintf(tmp, sizeof(tmp), ".%03u", remainder);
+               tmp[j+1] = '\0';
+       }
+
+ out:
+       if (i >= ARRAY_SIZE(units_2))
+               unit = "UNK";
+       else
+               unit = units_str[units][i];
+
+       return snprintf(buf, len, "%u%s %s", (u32)size, tmp, unit);
+}
+EXPORT_SYMBOL(string_get_size);