Refactor #includes and headers
[fio.git] / lib / num2str.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5
6 #include "../compiler/compiler.h"
7 #include "num2str.h"
8
9 #define ARRAY_SIZE(x)    (sizeof((x)) / (sizeof((x)[0])))
10
11 /**
12  * num2str() - Cheesy number->string conversion, complete with carry rounding error.
13  * @num: quantity (e.g., number of blocks, bytes or bits)
14  * @maxlen: max number of digits in the output string (not counting prefix and units, but counting .)
15  * @base: multiplier for num (e.g., if num represents Ki, use 1024)
16  * @pow2: select unit prefix - 0=power-of-10 decimal SI, nonzero=power-of-2 binary IEC
17  * @units: select units - N2S_* macros defined in num2str.h
18  * @returns a malloc'd buffer containing "number[<unit prefix>][<units>]"
19  */
20 char *num2str(uint64_t num, int maxlen, int base, int pow2, int units)
21 {
22         const char *sistr[] = { "", "k", "M", "G", "T", "P" };
23         const char *iecstr[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi" };
24         const char **unitprefix;
25         const char *unitstr[] = { "", "/s", "B", "bit", "B/s", "bit/s" };
26         const unsigned int thousand[] = { 1000, 1024 };
27         unsigned int modulo;
28         int unit_index = 0, post_index, carry = 0;
29         char tmp[32], fmt[32];
30         char *buf;
31
32         compiletime_assert(sizeof(sistr) == sizeof(iecstr), "unit prefix arrays must be identical sizes");
33
34         buf = malloc(128);
35         if (!buf)
36                 return NULL;
37
38         if (pow2)
39                 unitprefix = iecstr;
40         else
41                 unitprefix = sistr;
42
43         for (post_index = 0; base > 1; post_index++)
44                 base /= thousand[!!pow2];
45
46         switch (units) {
47         case N2S_PERSEC:
48                 unit_index = 1;
49                 break;
50         case N2S_BYTE:
51                 unit_index = 2;
52                 break;
53         case N2S_BIT:
54                 unit_index = 3;
55                 num *= 8;
56                 break;
57         case N2S_BYTEPERSEC:
58                 unit_index = 4;
59                 break;
60         case N2S_BITPERSEC:
61                 unit_index = 5;
62                 num *= 8;
63                 break;
64         }
65
66         /*
67          * Divide by K/Ki until string length of num <= maxlen.
68          */
69         modulo = -1U;
70         while (post_index < sizeof(sistr)) {
71                 sprintf(tmp, "%llu", (unsigned long long) num);
72                 if (strlen(tmp) <= maxlen)
73                         break;
74
75                 modulo = num % thousand[!!pow2];
76                 num /= thousand[!!pow2];
77                 carry = modulo >= thousand[!!pow2] / 2;
78                 post_index++;
79         }
80
81         /*
82          * If no modulo, then we're done.
83          */
84         if (modulo == -1U) {
85 done:
86                 if (post_index >= ARRAY_SIZE(sistr))
87                         post_index = 0;
88
89                 sprintf(buf, "%llu%s%s", (unsigned long long) num,
90                         unitprefix[post_index], unitstr[unit_index]);
91                 return buf;
92         }
93
94         /*
95          * If no room for decimals, then we're done.
96          */
97         sprintf(tmp, "%llu", (unsigned long long) num);
98         if ((int)(maxlen - strlen(tmp)) <= 1) {
99                 if (carry)
100                         num++;
101                 goto done;
102         }
103
104         /*
105          * Fill in everything and return the result.
106          */
107         assert(maxlen - strlen(tmp) - 1 > 0);
108         assert(modulo < thousand[!!pow2]);
109         sprintf(fmt, "%%.%df", (int)(maxlen - strlen(tmp) - 1));
110         sprintf(tmp, fmt, (double)modulo / (double)thousand[!!pow2]);
111
112         sprintf(buf, "%llu.%s%s%s", (unsigned long long) num, &tmp[2],
113                         unitprefix[post_index], unitstr[unit_index]);
114         return buf;
115 }