24 |
* |
* |
25 |
* Some code to omit the decimal point and tenths digit is sketched out |
* Some code to omit the decimal point and tenths digit is sketched out |
26 |
* and "#if 0"'d below. |
* and "#if 0"'d below. |
27 |
|
* |
28 |
|
* Licensed under GPLv2, see file LICENSE in this tarball for details. |
29 |
*/ |
*/ |
30 |
|
|
|
#include <stdio.h> |
|
31 |
#include "libbb.h" |
#include "libbb.h" |
32 |
|
|
33 |
const char *make_human_readable_str(unsigned long long size, |
const char* FAST_FUNC make_human_readable_str(unsigned long long val, |
34 |
unsigned long block_size, unsigned long display_unit) |
unsigned long block_size, unsigned long display_unit) |
35 |
{ |
{ |
36 |
/* The code will adjust for additional (appended) units. */ |
static const char unit_chars[] ALIGN1 = { |
37 |
static const char zero_and_units[] = { '0', 0, 'k', 'M', 'G', 'T' }; |
'\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' |
38 |
static const char fmt[] = "%llu"; |
}; |
|
static const char fmt_tenths[] = "%llu.%d%c"; |
|
39 |
|
|
40 |
static char str[21]; /* Sufficient for 64 bit unsigned integers. */ |
static char *str; |
41 |
|
|
42 |
unsigned long long val; |
unsigned frac; /* 0..9 - the fractional digit */ |
|
int frac; |
|
43 |
const char *u; |
const char *u; |
44 |
const char *f; |
const char *fmt; |
45 |
|
|
46 |
u = zero_and_units; |
if (val == 0) |
47 |
f = fmt; |
return "0"; |
|
frac = 0; |
|
48 |
|
|
49 |
val = size * block_size; |
fmt = "%llu"; |
50 |
if (val == 0) { |
if (block_size > 1) |
51 |
return u; |
val *= block_size; |
52 |
} |
frac = 0; |
53 |
|
u = unit_chars; |
54 |
|
|
55 |
if (display_unit) { |
if (display_unit) { |
56 |
val += display_unit/2; /* Deal with rounding. */ |
val += display_unit/2; /* Deal with rounding */ |
57 |
val /= display_unit; /* Don't combine with the line above!!! */ |
val /= display_unit; /* Don't combine with the line above! */ |
58 |
|
/* will just print it as ulonglong (below) */ |
59 |
} else { |
} else { |
|
++u; |
|
60 |
while ((val >= 1024) |
while ((val >= 1024) |
61 |
&& (u < zero_and_units + sizeof(zero_and_units) - 1) |
/* && (u < unit_chars + sizeof(unit_chars) - 1) - always true */ |
62 |
) { |
) { |
63 |
f = fmt_tenths; |
fmt = "%llu.%u%c"; |
64 |
++u; |
u++; |
65 |
frac = (((int)(val % 1024)) * 10 + 1024/2) / 1024; |
frac = (((unsigned)val % 1024) * 10 + 1024/2) / 1024; |
66 |
val /= 1024; |
val /= 1024; |
67 |
} |
} |
68 |
if (frac >= 10) { /* We need to round up here. */ |
if (frac >= 10) { /* we need to round up here */ |
69 |
++val; |
++val; |
70 |
frac = 0; |
frac = 0; |
71 |
} |
} |
72 |
#if 0 |
#if 1 |
73 |
/* Sample code to omit decimal point and tenths digit. */ |
/* If block_size is 0, dont print fractional part */ |
74 |
if ( /* no_tenths */ 1 ) { |
if (block_size == 0) { |
75 |
if ( frac >= 5 ) { |
if (frac >= 5) { |
76 |
++val; |
++val; |
77 |
} |
} |
78 |
f = "%llu%*c" /* fmt_no_tenths */ ; |
fmt = "%llu%*c"; |
79 |
frac = 1; |
frac = 1; |
80 |
} |
} |
81 |
#endif |
#endif |
82 |
} |
} |
83 |
|
|
84 |
/* If f==fmt then 'frac' and 'u' are ignored. */ |
if (!str) { |
85 |
snprintf(str, sizeof(str), f, val, frac, *u); |
/* sufficient for any width of val */ |
86 |
|
str = xmalloc(sizeof(val)*3 + 2 + 3); |
87 |
|
} |
88 |
|
sprintf(str, fmt, val, frac, *u); |
89 |
return str; |
return str; |
90 |
} |
} |
91 |
|
|
92 |
|
|
93 |
|
/* vda's implementations of the similar idea */ |
94 |
|
|
95 |
|
/* Convert unsigned long long value into compact 5-char representation. |
96 |
|
* String is not terminated (buf[5] is untouched) */ |
97 |
|
void FAST_FUNC smart_ulltoa5(unsigned long long ul, char buf[6], const char *scale) |
98 |
|
{ |
99 |
|
const char *fmt; |
100 |
|
char c; |
101 |
|
unsigned v, u, idx = 0; |
102 |
|
|
103 |
|
if (ul > 99999) { // do not scale if 99999 or less |
104 |
|
ul *= 10; |
105 |
|
do { |
106 |
|
ul /= 1024; |
107 |
|
idx++; |
108 |
|
} while (ul >= 100000); |
109 |
|
} |
110 |
|
v = ul; // ullong divisions are expensive, avoid them |
111 |
|
|
112 |
|
fmt = " 123456789"; |
113 |
|
u = v / 10; |
114 |
|
v = v % 10; |
115 |
|
if (!idx) { |
116 |
|
// 99999 or less: use "12345" format |
117 |
|
// u is value/10, v is last digit |
118 |
|
c = buf[0] = " 123456789"[u/1000]; |
119 |
|
if (c != ' ') fmt = "0123456789"; |
120 |
|
c = buf[1] = fmt[u/100%10]; |
121 |
|
if (c != ' ') fmt = "0123456789"; |
122 |
|
c = buf[2] = fmt[u/10%10]; |
123 |
|
if (c != ' ') fmt = "0123456789"; |
124 |
|
buf[3] = fmt[u%10]; |
125 |
|
buf[4] = "0123456789"[v]; |
126 |
|
} else { |
127 |
|
// value has been scaled into 0..9999.9 range |
128 |
|
// u is value, v is 1/10ths (allows for 92.1M format) |
129 |
|
if (u >= 100) { |
130 |
|
// value is >= 100: use "1234M', " 123M" formats |
131 |
|
c = buf[0] = " 123456789"[u/1000]; |
132 |
|
if (c != ' ') fmt = "0123456789"; |
133 |
|
c = buf[1] = fmt[u/100%10]; |
134 |
|
if (c != ' ') fmt = "0123456789"; |
135 |
|
v = u % 10; |
136 |
|
u = u / 10; |
137 |
|
buf[2] = fmt[u%10]; |
138 |
|
} else { |
139 |
|
// value is < 100: use "92.1M" format |
140 |
|
c = buf[0] = " 123456789"[u/10]; |
141 |
|
if (c != ' ') fmt = "0123456789"; |
142 |
|
buf[1] = fmt[u%10]; |
143 |
|
buf[2] = '.'; |
144 |
|
} |
145 |
|
buf[3] = "0123456789"[v]; |
146 |
|
buf[4] = scale[idx]; /* typically scale = " kmgt..." */ |
147 |
|
} |
148 |
|
} |
149 |
|
|
150 |
|
/* Convert unsigned long long value into compact 4-char |
151 |
|
* representation. Examples: "1234", "1.2k", " 27M", "123T" |
152 |
|
* String is not terminated (buf[4] is untouched) */ |
153 |
|
void FAST_FUNC smart_ulltoa4(unsigned long long ul, char buf[5], const char *scale) |
154 |
|
{ |
155 |
|
const char *fmt; |
156 |
|
char c; |
157 |
|
unsigned v, u, idx = 0; |
158 |
|
|
159 |
|
if (ul > 9999) { // do not scale if 9999 or less |
160 |
|
ul *= 10; |
161 |
|
do { |
162 |
|
ul /= 1024; |
163 |
|
idx++; |
164 |
|
} while (ul >= 10000); |
165 |
|
} |
166 |
|
v = ul; // ullong divisions are expensive, avoid them |
167 |
|
|
168 |
|
fmt = " 123456789"; |
169 |
|
u = v / 10; |
170 |
|
v = v % 10; |
171 |
|
if (!idx) { |
172 |
|
// 9999 or less: use "1234" format |
173 |
|
// u is value/10, v is last digit |
174 |
|
c = buf[0] = " 123456789"[u/100]; |
175 |
|
if (c != ' ') fmt = "0123456789"; |
176 |
|
c = buf[1] = fmt[u/10%10]; |
177 |
|
if (c != ' ') fmt = "0123456789"; |
178 |
|
buf[2] = fmt[u%10]; |
179 |
|
buf[3] = "0123456789"[v]; |
180 |
|
} else { |
181 |
|
// u is value, v is 1/10ths (allows for 9.2M format) |
182 |
|
if (u >= 10) { |
183 |
|
// value is >= 10: use "123M', " 12M" formats |
184 |
|
c = buf[0] = " 123456789"[u/100]; |
185 |
|
if (c != ' ') fmt = "0123456789"; |
186 |
|
v = u % 10; |
187 |
|
u = u / 10; |
188 |
|
buf[1] = fmt[u%10]; |
189 |
|
} else { |
190 |
|
// value is < 10: use "9.2M" format |
191 |
|
buf[0] = "0123456789"[u]; |
192 |
|
buf[1] = '.'; |
193 |
|
} |
194 |
|
buf[2] = "0123456789"[v]; |
195 |
|
buf[3] = scale[idx]; /* typically scale = " kmgt..." */ |
196 |
|
} |
197 |
|
} |