Introduce enum n2s_unit
[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;
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 46 switch (units) {
41a87019
BVA
47 case N2S_NONE:
48 break;
d694a6a7
RE
49 case N2S_PERSEC:
50 unit_index = 1;
51 break;
52 case N2S_BYTE:
53 unit_index = 2;
54 break;
55 case N2S_BIT:
56 unit_index = 3;
73798eb2
SN
57 num *= 8;
58 break;
d694a6a7
RE
59 case N2S_BYTEPERSEC:
60 unit_index = 4;
61 break;
62 case N2S_BITPERSEC:
63 unit_index = 5;
64 num *= 8;
73798eb2
SN
65 break;
66 }
67
9e55f2c7
TK
68 /*
69 * Divide by K/Ki until string length of num <= maxlen.
70 */
1ec3d69b 71 modulo = -1U;
d694a6a7 72 while (post_index < sizeof(sistr)) {
ca09be4b 73 sprintf(tmp, "%llu", (unsigned long long) num);
1ec3d69b
JA
74 if (strlen(tmp) <= maxlen)
75 break;
76
77 modulo = num % thousand[!!pow2];
78 num /= thousand[!!pow2];
05463816 79 carry = modulo >= thousand[!!pow2] / 2;
1ec3d69b
JA
80 post_index++;
81 }
82
9e55f2c7
TK
83 /*
84 * If no modulo, then we're done.
85 */
1ec3d69b
JA
86 if (modulo == -1U) {
87done:
b920b29b 88 if (post_index >= ARRAY_SIZE(sistr))
b7e147d1
JA
89 post_index = 0;
90
ca09be4b 91 sprintf(buf, "%llu%s%s", (unsigned long long) num,
d694a6a7 92 unitprefix[post_index], unitstr[unit_index]);
1ec3d69b
JA
93 return buf;
94 }
95
9e55f2c7
TK
96 /*
97 * If no room for decimals, then we're done.
98 */
ca09be4b 99 sprintf(tmp, "%llu", (unsigned long long) num);
9e55f2c7 100 if ((int)(maxlen - strlen(tmp)) <= 1) {
05463816
JA
101 if (carry)
102 num++;
1ec3d69b 103 goto done;
05463816
JA
104 }
105
9e55f2c7
TK
106 /*
107 * Fill in everything and return the result.
108 */
109 assert(maxlen - strlen(tmp) - 1 > 0);
110 assert(modulo < thousand[!!pow2]);
111 sprintf(fmt, "%%.%df", (int)(maxlen - strlen(tmp) - 1));
112 sprintf(tmp, fmt, (double)modulo / (double)thousand[!!pow2]);
1ec3d69b 113
9e55f2c7 114 sprintf(buf, "%llu.%s%s%s", (unsigned long long) num, &tmp[2],
d694a6a7 115 unitprefix[post_index], unitstr[unit_index]);
1ec3d69b
JA
116 return buf;
117}