Loading...
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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | /* vi: set sw=4 ts=4: */
/*
* June 30, 2001 Manuel Novoa III
*
* All-integer version (hey, not everyone has floating point) of
* make_human_readable_str, modified from similar code I had written
* for busybox several months ago.
*
* Notes:
* 1) I'm using an unsigned long long to hold the product size * block_size,
* as df (which calls this routine) could request a representation of a
* partition size in bytes > max of unsigned long. If long longs aren't
* available, it would be possible to do what's needed using polynomial
* representations (say, powers of 1024) and manipulating coefficients.
* The base ten "bytes" output could be handled similarly.
*
* 2) This routine always outputs a decimal point and a tenths digit when
* display_unit != 0. Hence, it isn't uncommon for the returned string
* to have a length of 5 or 6.
*
* It might be nice to add a flag to indicate no decimal digits in
* that case. This could be either an additional parameter, or a
* special value of display_unit. Such a flag would also be nice for du.
*
* Some code to omit the decimal point and tenths digit is sketched out
* and "#if 0"'d below.
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
#include "libbb.h"
const char* FAST_FUNC make_human_readable_str(unsigned long long val,
unsigned long block_size, unsigned long display_unit)
{
static const char unit_chars[] ALIGN1 = {
'\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'
};
static char *str;
unsigned frac; /* 0..9 - the fractional digit */
const char *u;
const char *fmt;
if (val == 0)
return "0";
fmt = "%llu";
if (block_size > 1)
val *= block_size;
frac = 0;
u = unit_chars;
if (display_unit) {
val += display_unit/2; /* Deal with rounding */
val /= display_unit; /* Don't combine with the line above! */
/* will just print it as ulonglong (below) */
} else {
while ((val >= 1024)
/* && (u < unit_chars + sizeof(unit_chars) - 1) - always true */
) {
fmt = "%llu.%u%c";
u++;
frac = (((unsigned)val % 1024) * 10 + 1024/2) / 1024;
val /= 1024;
}
if (frac >= 10) { /* we need to round up here */
++val;
frac = 0;
}
#if 1
/* If block_size is 0, dont print fractional part */
if (block_size == 0) {
if (frac >= 5) {
++val;
}
fmt = "%llu%*c";
frac = 1;
}
#endif
}
if (!str) {
/* sufficient for any width of val */
str = xmalloc(sizeof(val)*3 + 2 + 3);
}
sprintf(str, fmt, val, frac, *u);
return str;
}
/* vda's implementations of the similar idea */
/* Convert unsigned long long value into compact 5-char representation.
* String is not terminated (buf[5] is untouched) */
void FAST_FUNC smart_ulltoa5(unsigned long long ul, char buf[5], const char *scale)
{
const char *fmt;
char c;
unsigned v, u, idx = 0;
if (ul > 99999) { // do not scale if 99999 or less
ul *= 10;
do {
ul /= 1024;
idx++;
} while (ul >= 100000);
}
v = ul; // ullong divisions are expensive, avoid them
fmt = " 123456789";
u = v / 10;
v = v % 10;
if (!idx) {
// 99999 or less: use "12345" format
// u is value/10, v is last digit
c = buf[0] = " 123456789"[u/1000];
if (c != ' ') fmt = "0123456789";
c = buf[1] = fmt[u/100%10];
if (c != ' ') fmt = "0123456789";
c = buf[2] = fmt[u/10%10];
if (c != ' ') fmt = "0123456789";
buf[3] = fmt[u%10];
buf[4] = "0123456789"[v];
} else {
// value has been scaled into 0..9999.9 range
// u is value, v is 1/10ths (allows for 92.1M format)
if (u >= 100) {
// value is >= 100: use "1234M', " 123M" formats
c = buf[0] = " 123456789"[u/1000];
if (c != ' ') fmt = "0123456789";
c = buf[1] = fmt[u/100%10];
if (c != ' ') fmt = "0123456789";
v = u % 10;
u = u / 10;
buf[2] = fmt[u%10];
} else {
// value is < 100: use "92.1M" format
c = buf[0] = " 123456789"[u/10];
if (c != ' ') fmt = "0123456789";
buf[1] = fmt[u%10];
buf[2] = '.';
}
buf[3] = "0123456789"[v];
buf[4] = scale[idx]; /* typically scale = " kmgt..." */
}
}
/* Convert unsigned long long value into compact 4-char
* representation. Examples: "1234", "1.2k", " 27M", "123T"
* String is not terminated (buf[4] is untouched) */
void FAST_FUNC smart_ulltoa4(unsigned long long ul, char buf[4], const char *scale)
{
const char *fmt;
char c;
unsigned v, u, idx = 0;
if (ul > 9999) { // do not scale if 9999 or less
ul *= 10;
do {
ul /= 1024;
idx++;
} while (ul >= 10000);
}
v = ul; // ullong divisions are expensive, avoid them
fmt = " 123456789";
u = v / 10;
v = v % 10;
if (!idx) {
// 9999 or less: use "1234" format
// u is value/10, v is last digit
c = buf[0] = " 123456789"[u/100];
if (c != ' ') fmt = "0123456789";
c = buf[1] = fmt[u/10%10];
if (c != ' ') fmt = "0123456789";
buf[2] = fmt[u%10];
buf[3] = "0123456789"[v];
} else {
// u is value, v is 1/10ths (allows for 9.2M format)
if (u >= 10) {
// value is >= 10: use "123M', " 12M" formats
c = buf[0] = " 123456789"[u/100];
if (c != ' ') fmt = "0123456789";
v = u % 10;
u = u / 10;
buf[1] = fmt[u%10];
} else {
// value is < 10: use "9.2M" format
buf[0] = "0123456789"[u];
buf[1] = '.';
}
buf[2] = "0123456789"[v];
buf[3] = scale[idx]; /* typically scale = " kmgt..." */
}
}
|