]> git.sesse.net Git - bcachefs-tools-debian/blob - linux/string_helpers.c
Update bcachefs sources to 24c6361e20 bcachefs: Fix a trans path overflow in bch2_btr...
[bcachefs-tools-debian] / linux / string_helpers.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Helpers for formatting and printing strings
4  *
5  * Copyright 31 August 2008 James Bottomley
6  * Copyright (C) 2013, Intel Corporation
7  */
8 #include <linux/bug.h>
9 #include <linux/kernel.h>
10 #include <linux/math64.h>
11 #include <linux/export.h>
12 #include <linux/ctype.h>
13 #include <linux/device.h>
14 #include <linux/errno.h>
15 #include <linux/fs.h>
16 #include <linux/limits.h>
17 #include <linux/printbuf.h>
18 #include <linux/slab.h>
19 #include <linux/string.h>
20 #include <linux/string_helpers.h>
21
22 /**
23  * string_get_size - get the size in the specified units
24  * @size:       The size to be converted in blocks
25  * @blk_size:   Size of the block (use 1 for size in bytes)
26  * @units:      units to use (powers of 1000 or 1024)
27  * @buf:        buffer to format to
28  * @len:        length of buffer
29  *
30  * This function returns a string formatted to 3 significant figures
31  * giving the size in the required units.  @buf should have room for
32  * at least 9 bytes and will always be zero terminated.
33  *
34  */
35 int string_get_size(u64 size, u64 blk_size, const enum string_size_units units,
36                     char *buf, int len)
37 {
38         static const char *const units_10[] = {
39                 "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
40         };
41         static const char *const units_2[] = {
42                 "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"
43         };
44         static const char *const *const units_str[] = {
45                 [STRING_UNITS_10] = units_10,
46                 [STRING_UNITS_2] = units_2,
47         };
48         static const unsigned int divisor[] = {
49                 [STRING_UNITS_10] = 1000,
50                 [STRING_UNITS_2] = 1024,
51         };
52         static const unsigned int rounding[] = { 500, 50, 5 };
53         int i = 0, j;
54         u32 remainder = 0, sf_cap;
55         char tmp[8];
56         const char *unit;
57
58         tmp[0] = '\0';
59
60         if (blk_size == 0)
61                 size = 0;
62         if (size == 0)
63                 goto out;
64
65         /* This is Napier's algorithm.  Reduce the original block size to
66          *
67          * coefficient * divisor[units]^i
68          *
69          * we do the reduction so both coefficients are just under 32 bits so
70          * that multiplying them together won't overflow 64 bits and we keep
71          * as much precision as possible in the numbers.
72          *
73          * Note: it's safe to throw away the remainders here because all the
74          * precision is in the coefficients.
75          */
76         while (blk_size >> 32) {
77                 do_div(blk_size, divisor[units]);
78                 i++;
79         }
80
81         while (size >> 32) {
82                 do_div(size, divisor[units]);
83                 i++;
84         }
85
86         /* now perform the actual multiplication keeping i as the sum of the
87          * two logarithms */
88         size *= blk_size;
89
90         /* and logarithmically reduce it until it's just under the divisor */
91         while (size >= divisor[units]) {
92                 remainder = do_div(size, divisor[units]);
93                 i++;
94         }
95
96         /* work out in j how many digits of precision we need from the
97          * remainder */
98         sf_cap = size;
99         for (j = 0; sf_cap*10 < 1000; j++)
100                 sf_cap *= 10;
101
102         if (units == STRING_UNITS_2) {
103                 /* express the remainder as a decimal.  It's currently the
104                  * numerator of a fraction whose denominator is
105                  * divisor[units], which is 1 << 10 for STRING_UNITS_2 */
106                 remainder *= 1000;
107                 remainder >>= 10;
108         }
109
110         /* add a 5 to the digit below what will be printed to ensure
111          * an arithmetical round up and carry it through to size */
112         remainder += rounding[j];
113         if (remainder >= 1000) {
114                 remainder -= 1000;
115                 size += 1;
116         }
117
118         if (j) {
119                 snprintf(tmp, sizeof(tmp), ".%03u", remainder);
120                 tmp[j+1] = '\0';
121         }
122
123  out:
124         if (i >= ARRAY_SIZE(units_2))
125                 unit = "UNK";
126         else
127                 unit = units_str[units][i];
128
129         return snprintf(buf, len, "%u%s %s", (u32)size, tmp, unit);
130 }
131 EXPORT_SYMBOL(string_get_size);