libbb/human_readable.c: shrink; and reduce bss usage
No binary (build failed)
—
(log yok)
| @@ -92,7 +92,10 @@ int df_main(int argc, char **argv) | |
| if (disp_units_hdr == NULL) { | |
| #if ENABLE_FEATURE_HUMAN_READABLE | |
| disp_units_hdr = xasprintf("%s-blocks", | |
| - | make_human_readable_str(df_disp_hr, 0, !!(opt & OPT_POSIX))); |
| + | /* print df_disp_hr, show no fractionals, |
| + | * use suffixes if OPT_POSIX is set in opt */ |
| + | make_human_readable_str(df_disp_hr, 0, !!(opt & OPT_POSIX)) |
| + | ); |
| #else | |
| disp_units_hdr = xasprintf("%lu-blocks", df_disp_hr); | |
| #endif | |
| @@ -189,21 +192,27 @@ int df_main(int argc, char **argv) | |
| #if ENABLE_FEATURE_HUMAN_READABLE | |
| printf(" %9s ", | |
| + | /* f_blocks x f_bsize / df_disp_hr, show one fractional, |
| + | * use suffixes if df_disp_hr == 0 */ |
| make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr)); | |
| printf(" %9s " + 1, | |
| + | /* EXPR x f_bsize / df_disp_hr, show one fractional, |
| + | * use suffixes if df_disp_hr == 0 */ |
| make_human_readable_str((s.f_blocks - s.f_bfree), | |
| s.f_bsize, df_disp_hr)); | |
| printf("%9s %3u%% %s\n", | |
| - | make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr), |
| - | blocks_percent_used, mount_point); |
| + | /* f_bavail x f_bsize / df_disp_hr, show one fractional, |
| + | * use suffixes if df_disp_hr == 0 */ |
| + | make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr), |
| + | blocks_percent_used, mount_point); |
| #else | |
| printf(" %9lu %9lu %9lu %3u%% %s\n", | |
| - | kscale(s.f_blocks, s.f_bsize), |
| - | kscale(s.f_blocks - s.f_bfree, s.f_bsize), |
| - | kscale(s.f_bavail, s.f_bsize), |
| - | blocks_percent_used, mount_point); |
| + | kscale(s.f_blocks, s.f_bsize), |
| + | kscale(s.f_blocks - s.f_bfree, s.f_bsize), |
| + | kscale(s.f_bavail, s.f_bsize), |
| + | blocks_percent_used, mount_point); |
| #endif | |
| } | |
| } | |
| @@ -58,14 +58,17 @@ static void print(unsigned long size, const char *filename) | |
| { | |
| /* TODO - May not want to defer error checking here. */ | |
| #if ENABLE_FEATURE_HUMAN_READABLE | |
| - | printf("%s\t%s\n", make_human_readable_str(size, 512, G.disp_hr), |
| + | printf("%s\t%s\n", |
| + | /* size x 512 / G.disp_hr, show one fractional, |
| + | * use suffixes if G.disp_hr == 0 */ |
| + | make_human_readable_str(size, 512, G.disp_hr), |
| filename); | |
| #else | |
| if (G.disp_k) { | |
| size++; | |
| size >>= 1; | |
| } | |
| - | printf("%ld\t%s\n", size, filename); |
| + | printf("%lu\t%s\n", size, filename); |
| #endif | |
| } | |
| @@ -829,7 +829,9 @@ static NOINLINE unsigned list_single(const struct dnode *dn) | |
| } else { | |
| if (all_fmt & LS_DISP_HR) { | |
| column += printf("%9s ", | |
| - | make_human_readable_str(dn->dstat.st_size, 1, 0)); |
| + | /* print st_size, show one fractional, use suffixes */ |
| + | make_human_readable_str(dn->dstat.st_size, 1, 0) |
| + | ); |
| } else { | |
| column += printf("%9"OFF_FMT"u ", (off_t) dn->dstat.st_size); | |
| } | |
| @@ -691,6 +691,10 @@ char *itoa_to_buf(int n, char *buf, unsigned buflen) FAST_FUNC; | |
| /* Intelligent formatters of bignums */ | |
| void smart_ulltoa4(unsigned long long ul, char buf[5], const char *scale) FAST_FUNC; | |
| void smart_ulltoa5(unsigned long long ul, char buf[5], const char *scale) FAST_FUNC; | |
| + | /* If block_size == 0, display size without fractional part, |
| + | * else display (size * block_size) with one decimal digit. |
| + | * If display_unit == 0, add suffix (K,M,G...), |
| + | * else divide by display_unit and do not use suffix. */ |
| //TODO: provide pointer to buf (avoid statics)? | |
| const char *make_human_readable_str(unsigned long long size, | |
| unsigned long block_size, unsigned long display_unit) FAST_FUNC; | |
| @@ -30,70 +30,168 @@ | |
| #include "libbb.h" | |
| - | const char* FAST_FUNC make_human_readable_str(unsigned long long size, |
| + | const char* FAST_FUNC make_human_readable_str(unsigned long long val, |
| unsigned long block_size, unsigned long display_unit) | |
| { | |
| - | /* The code will adjust for additional (appended) units */ |
| static const char unit_chars[] ALIGN1 = { | |
| - | '\0', 'K', 'M', 'G', 'T', 'P', 'E' |
| + | '\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' |
| }; | |
| - | static const char fmt[] ALIGN1 = "%llu"; |
| - | static const char fmt_tenths[] ALIGN1 = "%llu.%d%c"; |
| - | static char str[21] ALIGN1; /* Sufficient for 64 bit unsigned integers */ |
| + | static char *str; |
| - | unsigned long long val; |
| - | int frac; |
| + | unsigned frac; /* 0..9 - the fractional digit */ |
| const char *u; | |
| - | const char *f; |
| - | smallint no_tenths; |
| + | const char *fmt; |
| - | if (size == 0) |
| + | if (val == 0) |
| return "0"; | |
| - | /* If block_size is 0 then do not print tenths */ |
| - | no_tenths = 0; |
| - | if (block_size == 0) { |
| - | no_tenths = 1; |
| - | block_size = 1; |
| - | } |
| - | |
| - | u = unit_chars; |
| - | val = size * block_size; |
| - | f = fmt; |
| + | 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!!! */ |
| + | 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) |
| + | /* && (u < unit_chars + sizeof(unit_chars) - 1) - never happens */ |
| ) { | |
| - | f = fmt_tenths; |
| + | fmt = "%llu.%u%c"; |
| u++; | |
| - | frac = (((int)(val % 1024)) * 10 + 1024/2) / 1024; |
| + | frac = (((unsigned)val % 1024) * 10 + 1024/2) / 1024; |
| val /= 1024; | |
| } | |
| - | if (frac >= 10) { /* We need to round up here. */ |
| + | if (frac >= 10) { /* we need to round up here */ |
| ++val; | |
| frac = 0; | |
| } | |
| #if 1 | |
| - | /* Sample code to omit decimal point and tenths digit. */ |
| - | if (no_tenths) { |
| + | /* If block_size is 0, dont print fractional part */ |
| + | if (block_size == 0) { |
| if (frac >= 5) { | |
| ++val; | |
| } | |
| - | f = "%llu%*c" /* fmt_no_tenths */; |
| + | fmt = "%llu%*c"; |
| frac = 1; | |
| } | |
| #endif | |
| } | |
| - | /* If f==fmt then 'frac' and 'u' are ignored. */ |
| - | snprintf(str, sizeof(str), f, val, frac, *u); |
| - | |
| + | 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[6], 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[5], 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..." */ |
| + | } |
| + | } |
| @@ -48,110 +48,6 @@ char* FAST_FUNC strncpy_IFNAMSIZ(char *dst, const char *src) | |
| return strncpy(dst, src, IFNAMSIZ); | |
| } | |
| - | /* 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[5], 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..." */ |
| - | } |
| - | } |
| - | |
| - | /* 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[6], 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 integer to ascii, writing into supplied buffer. | |
| // A truncated result contains the first few digits of the result ala strncpy. | |