client: use temp buffer for single output flush for json/disk util
[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
41a87019 17 * @units: select units - N2S_* constants defined in num2str.h
d694a6a7 18 * @returns a malloc'd buffer containing "number[<unit prefix>][<units>]"
1ec3d69b 19 */
41a87019 20char *num2str(uint64_t num, int maxlen, int base, int pow2, enum n2s_unit 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;
cbb8289d
BVA
25 static const char *const unitstr[] = {
26 [N2S_NONE] = "",
27 [N2S_PERSEC] = "/s",
28 [N2S_BYTE] = "B",
29 [N2S_BIT] = "bit",
30 [N2S_BYTEPERSEC]= "B/s",
31 [N2S_BITPERSEC] = "bit/s"
32 };
73798eb2 33 const unsigned int thousand[] = { 1000, 1024 };
9e55f2c7 34 unsigned int modulo;
cbb8289d 35 int post_index, carry = 0;
9e55f2c7 36 char tmp[32], fmt[32];
1ec3d69b
JA
37 char *buf;
38
d694a6a7 39 compiletime_assert(sizeof(sistr) == sizeof(iecstr), "unit prefix arrays must be identical sizes");
cbb8289d 40 assert(units < ARRAY_SIZE(unitstr));
d694a6a7 41
1ec3d69b 42 buf = malloc(128);
d694a6a7
RE
43 if (!buf)
44 return NULL;
45
46 if (pow2)
47 unitprefix = iecstr;
48 else
49 unitprefix = sistr;
1ec3d69b
JA
50
51 for (post_index = 0; base > 1; post_index++)
52 base /= thousand[!!pow2];
53
d694a6a7 54 switch (units) {
41a87019
BVA
55 case N2S_NONE:
56 break;
d694a6a7 57 case N2S_PERSEC:
d694a6a7
RE
58 break;
59 case N2S_BYTE:
d694a6a7
RE
60 break;
61 case N2S_BIT:
73798eb2
SN
62 num *= 8;
63 break;
d694a6a7 64 case N2S_BYTEPERSEC:
d694a6a7
RE
65 break;
66 case N2S_BITPERSEC:
d694a6a7 67 num *= 8;
73798eb2
SN
68 break;
69 }
70
9e55f2c7
TK
71 /*
72 * Divide by K/Ki until string length of num <= maxlen.
73 */
1ec3d69b 74 modulo = -1U;
d694a6a7 75 while (post_index < sizeof(sistr)) {
ca09be4b 76 sprintf(tmp, "%llu", (unsigned long long) num);
1ec3d69b
JA
77 if (strlen(tmp) <= maxlen)
78 break;
79
80 modulo = num % thousand[!!pow2];
81 num /= thousand[!!pow2];
05463816 82 carry = modulo >= thousand[!!pow2] / 2;
1ec3d69b
JA
83 post_index++;
84 }
85
9e55f2c7
TK
86 /*
87 * If no modulo, then we're done.
88 */
1ec3d69b
JA
89 if (modulo == -1U) {
90done:
b920b29b 91 if (post_index >= ARRAY_SIZE(sistr))
b7e147d1
JA
92 post_index = 0;
93
ca09be4b 94 sprintf(buf, "%llu%s%s", (unsigned long long) num,
cbb8289d 95 unitprefix[post_index], unitstr[units]);
1ec3d69b
JA
96 return buf;
97 }
98
9e55f2c7
TK
99 /*
100 * If no room for decimals, then we're done.
101 */
ca09be4b 102 sprintf(tmp, "%llu", (unsigned long long) num);
9e55f2c7 103 if ((int)(maxlen - strlen(tmp)) <= 1) {
05463816
JA
104 if (carry)
105 num++;
1ec3d69b 106 goto done;
05463816
JA
107 }
108
9e55f2c7
TK
109 /*
110 * Fill in everything and return the result.
111 */
112 assert(maxlen - strlen(tmp) - 1 > 0);
113 assert(modulo < thousand[!!pow2]);
114 sprintf(fmt, "%%.%df", (int)(maxlen - strlen(tmp) - 1));
115 sprintf(tmp, fmt, (double)modulo / (double)thousand[!!pow2]);
1ec3d69b 116
9e55f2c7 117 sprintf(buf, "%llu.%s%s%s", (unsigned long long) num, &tmp[2],
cbb8289d 118 unitprefix[post_index], unitstr[units]);
1ec3d69b
JA
119 return buf;
120}