[SCSI] lib: add generic helper to print sizes rounded to the correct SI range
[linux-3.10.git] / lib / string_helpers.c
1 /*
2  * Helpers for formatting and printing strings
3  *
4  * Copyright 31 August 2008 James Bottomley
5  */
6 #include <linux/kernel.h>
7 #include <linux/math64.h>
8 #include <linux/module.h>
9 #include <linux/string_helpers.h>
10
11 /**
12  * string_get_size - get the size in the specified units
13  * @size:       The size to be converted
14  * @units:      units to use (powers of 1000 or 1024)
15  * @buf:        buffer to format to
16  * @len:        length of buffer
17  *
18  * This function returns a string formatted to 3 significant figures
19  * giving the size in the required units.  Returns 0 on success or
20  * error on failure.  @buf is always zero terminated.
21  *
22  */
23 int string_get_size(u64 size, const enum string_size_units units,
24                     char *buf, int len)
25 {
26         const char *units_10[] = { "B", "KB", "MB", "GB", "TB", "PB",
27                                    "EB", "ZB", "YB", NULL};
28         const char *units_2[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB",
29                                  "EiB", "ZiB", "YiB", NULL };
30         const char **units_str[] = {
31                 [STRING_UNITS_10] =  units_10,
32                 [STRING_UNITS_2] = units_2,
33         };
34         const int divisor[] = {
35                 [STRING_UNITS_10] = 1000,
36                 [STRING_UNITS_2] = 1024,
37         };
38         int i, j;
39         u64 remainder = 0, sf_cap;
40         char tmp[8];
41
42         tmp[0] = '\0';
43
44         for (i = 0; size > divisor[units] && units_str[units][i]; i++)
45                 remainder = do_div(size, divisor[units]);
46
47         sf_cap = size;
48         for (j = 0; sf_cap*10 < 1000; j++)
49                 sf_cap *= 10;
50
51         if (j) {
52                 remainder *= 1000;
53                 do_div(remainder, divisor[units]);
54                 snprintf(tmp, sizeof(tmp), ".%03lld",
55                          (unsigned long long)remainder);
56                 tmp[j+1] = '\0';
57         }
58
59         snprintf(buf, len, "%lld%s%s", (unsigned long long)size,
60                  tmp, units_str[units][i]);
61
62         return 0;
63 }
64 EXPORT_SYMBOL(string_get_size);