File: num2str.c

package info (click to toggle)
fio 3.41-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 13,012 kB
  • sloc: ansic: 82,290; python: 9,862; sh: 6,067; makefile: 813; yacc: 204; lex: 184
file content (150 lines) | stat: -rw-r--r-- 3,720 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "../compiler/compiler.h"
#include "../oslib/asprintf.h"
#include "num2str.h"


static const char *iecstr[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" };

/**
 * bytes2str_simple - Converts a byte value to a human-readable string.
 * @buf:      buffer to store the resulting string
 * @bufsize:  size of the buffer
 * @bytes:    number of bytes to convert
 * @returns : pointer to the buf containing the formatted string.
 * Converts the given byte value into a human-readable string using IEC units
 * (e.g., KiB, MiB, GiB), and stores the result in the provided buffer.
 * The output is formatted with two decimal places of precision.
 */
const char *bytes2str_simple(char *buf, size_t bufsize, uint64_t bytes)
{
	int unit = 0;
	double size = (double)bytes;

	buf[0] = '\0';

	while (size >= 1024.0 && unit < FIO_ARRAY_SIZE(iecstr) - 1) {
		size /= 1024.0;
		unit++;
	}

	snprintf(buf, bufsize, "%.2f %sB", size, iecstr[unit]);

	return buf;
}


/**
 * num2str() - Cheesy number->string conversion, complete with carry rounding error.
 * @num: quantity (e.g., number of blocks, bytes or bits)
 * @maxlen: max number of digits in the output string (not counting prefix and units, but counting .)
 * @base: multiplier for num (e.g., if num represents Ki, use 1024)
 * @pow2: select unit prefix - 0=power-of-10 decimal SI, nonzero=power-of-2 binary IEC
 * @units: select units - N2S_* constants defined in num2str.h
 * @returns a malloc'd buffer containing "number[<unit prefix>][<units>]"
 */
char *num2str(uint64_t num, int maxlen, int base, int pow2, enum n2s_unit units)
{
	const char *sistr[] = { "", "k", "M", "G", "T", "P", "E" };
	const char **unitprefix;
	static const char *const unitstr[] = {
		[N2S_NONE]	= "",
		[N2S_PERSEC]	= "/s",
		[N2S_BYTE]	= "B",
		[N2S_BIT]	= "bit",
		[N2S_BYTEPERSEC]= "B/s",
		[N2S_BITPERSEC]	= "bit/s"
	};
	const unsigned int thousand = pow2 ? 1024 : 1000;
	unsigned int modulo;
	int post_index, carry = 0;
	char tmp[32];
	char *buf;

	compiletime_assert(sizeof(sistr) == sizeof(iecstr), "unit prefix arrays must be identical sizes");
	assert(units < FIO_ARRAY_SIZE(unitstr));

	if (pow2)
		unitprefix = iecstr;
	else
		unitprefix = sistr;

	for (post_index = 0; base > 1; post_index++)
		base /= thousand;

	switch (units) {
	case N2S_NONE:
		break;
	case N2S_PERSEC:
		break;
	case N2S_BYTE:
		break;
	case N2S_BIT:
		num *= 8;
		break;
	case N2S_BYTEPERSEC:
		break;
	case N2S_BITPERSEC:
		num *= 8;
		break;
	}

	/*
	 * Divide by K/Ki until string length of num <= maxlen.
	 */
	modulo = -1U;
	while (post_index < FIO_ARRAY_SIZE(sistr)) {
		sprintf(tmp, "%llu", (unsigned long long) num);
		if (strlen(tmp) <= maxlen)
			break;

		modulo = num % thousand;
		num /= thousand;
		carry = modulo >= thousand / 2;
		post_index++;
	}

	if (post_index >= FIO_ARRAY_SIZE(sistr))
		post_index = 0;

	/*
	 * If no modulo, then we're done.
	 */
	if (modulo == -1U) {
done:
		if (asprintf(&buf, "%llu%s%s", (unsigned long long) num,
			     unitprefix[post_index], unitstr[units]) < 0)
			buf = NULL;
		return buf;
	}

	/*
	 * If no room for decimals, then we're done.
	 */
	sprintf(tmp, "%llu", (unsigned long long) num);
	if ((int)(maxlen - strlen(tmp)) <= 1) {
		if (carry)
			num++;
		goto done;
	}

	/*
	 * Fill in everything and return the result.
	 */
	assert(maxlen - strlen(tmp) - 1 > 0);
	assert(modulo < thousand);
	sprintf(tmp, "%.*f", (int)(maxlen - strlen(tmp) - 1),
		(double)modulo / (double)thousand);

	if (tmp[0] == '1')
		num++;

	if (asprintf(&buf, "%llu.%s%s%s", (unsigned long long) num, &tmp[2],
		     unitprefix[post_index], unitstr[units]) < 0)
		buf = NULL;
	return buf;
}