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;
}
|