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