Refactor #includes and headers
[fio.git] / lib / num2str.c
CommitLineData
3d2d14bc 1#include <assert.h>
1ec3d69b
JA
2#include <stdlib.h>
3#include <stdio.h>
4#include <string.h>
5
5e746820
TK
6#include "../compiler/compiler.h"
7#include "num2str.h"
8
9#define ARRAY_SIZE(x) (sizeof((x)) / (sizeof((x)[0])))
10aa136b 10
d694a6a7
RE
11/**
12 * num2str() - Cheesy number->string conversion, complete with carry rounding error.
13 * @num: quantity (e.g., number of blocks, bytes or bits)
9e55f2c7 14 * @maxlen: max number of digits in the output string (not counting prefix and units, but counting .)
d694a6a7
RE
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
5e746820 17 * @units: select units - N2S_* macros defined in num2str.h
d694a6a7 18 * @returns a malloc'd buffer containing "number[<unit prefix>][<units>]"
1ec3d69b 19 */
d694a6a7 20char *num2str(uint64_t num, int maxlen, int base, int pow2, int units)
1ec3d69b 21{
d694a6a7
RE
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" };
73798eb2 26 const unsigned int thousand[] = { 1000, 1024 };
9e55f2c7 27 unsigned int modulo;
d694a6a7 28 int unit_index = 0, post_index, carry = 0;
9e55f2c7 29 char tmp[32], fmt[32];
1ec3d69b
JA
30 char *buf;
31
d694a6a7
RE
32 compiletime_assert(sizeof(sistr) == sizeof(iecstr), "unit prefix arrays must be identical sizes");
33
1ec3d69b 34 buf = malloc(128);
d694a6a7
RE
35 if (!buf)
36 return NULL;
37
38 if (pow2)
39 unitprefix = iecstr;
40 else
41 unitprefix = sistr;
1ec3d69b
JA
42
43 for (post_index = 0; base > 1; post_index++)
44 base /= thousand[!!pow2];
45
d694a6a7
RE
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;
73798eb2
SN
55 num *= 8;
56 break;
d694a6a7
RE
57 case N2S_BYTEPERSEC:
58 unit_index = 4;
59 break;
60 case N2S_BITPERSEC:
61 unit_index = 5;
62 num *= 8;
73798eb2
SN
63 break;
64 }
65
9e55f2c7
TK
66 /*
67 * Divide by K/Ki until string length of num <= maxlen.
68 */
1ec3d69b 69 modulo = -1U;
d694a6a7 70 while (post_index < sizeof(sistr)) {
ca09be4b 71 sprintf(tmp, "%llu", (unsigned long long) num);
1ec3d69b
JA
72 if (strlen(tmp) <= maxlen)
73 break;
74
75 modulo = num % thousand[!!pow2];
76 num /= thousand[!!pow2];
05463816 77 carry = modulo >= thousand[!!pow2] / 2;
1ec3d69b
JA
78 post_index++;
79 }
80
9e55f2c7
TK
81 /*
82 * If no modulo, then we're done.
83 */
1ec3d69b
JA
84 if (modulo == -1U) {
85done:
b920b29b 86 if (post_index >= ARRAY_SIZE(sistr))
b7e147d1
JA
87 post_index = 0;
88
ca09be4b 89 sprintf(buf, "%llu%s%s", (unsigned long long) num,
d694a6a7 90 unitprefix[post_index], unitstr[unit_index]);
1ec3d69b
JA
91 return buf;
92 }
93
9e55f2c7
TK
94 /*
95 * If no room for decimals, then we're done.
96 */
ca09be4b 97 sprintf(tmp, "%llu", (unsigned long long) num);
9e55f2c7 98 if ((int)(maxlen - strlen(tmp)) <= 1) {
05463816
JA
99 if (carry)
100 num++;
1ec3d69b 101 goto done;
05463816
JA
102 }
103
9e55f2c7
TK
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]);
1ec3d69b 111
9e55f2c7 112 sprintf(buf, "%llu.%s%s%s", (unsigned long long) num, &tmp[2],
d694a6a7 113 unitprefix[post_index], unitstr[unit_index]);
1ec3d69b
JA
114 return buf;
115}