From a8e99c291ad1f061a22129aad8f4f1ff52db621e Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Tue, 21 Mar 2023 10:13:47 +0100 Subject: [PATCH 1/6] human-readable: calculate numbers in a loop This drops a lot of `else if` blocks and extends units by "E", "Z" & "Y". --- lib/compat.c | 34 ++++++++++++++++------------------ rsync.1.md | 6 +++--- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/lib/compat.c b/lib/compat.c index 513d79b23..2f7f79c1b 100644 --- a/lib/compat.c +++ b/lib/compat.c @@ -181,26 +181,24 @@ char *do_big_num(int64 num, int human_flag, const char *fract) if (human_flag > 1) { int mult = human_flag == 2 ? 1000 : 1024; + if (num >= mult || num <= -mult) { - double dnum = (double)num / mult; - char units; - if (num < 0) - dnum = -dnum; - if (dnum < mult) - units = 'K'; - else if ((dnum /= mult) < mult) - units = 'M'; - else if ((dnum /= mult) < mult) - units = 'G'; - else if ((dnum /= mult) < mult) - units = 'T'; - else { - dnum /= mult; - units = 'P'; + const char* units = " KMGTPEZY"; + int64 powi = 1; + + for (;;) { + if (labs(num / mult) < powi) + break; + + if (units[1] == '\0') + break; + + powi *= mult; + ++units; } - if (num < 0) - dnum = -dnum; - snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units); + + snprintf(bufs[n], sizeof bufs[0], "%.2f%c", + (double) num / powi, *units); return bufs[n]; } } diff --git a/rsync.1.md b/rsync.1.md index 7e40e3617..10452a7ad 100644 --- a/rsync.1.md +++ b/rsync.1.md @@ -3309,9 +3309,9 @@ expand it. digits) by specifying the `--no-human-readable` (`--no-h`) option. The unit letters that are appended in levels 2 and 3 are: `K` (kilo), `M` - (mega), `G` (giga), `T` (tera), or `P` (peta). For example, a 1234567-byte - file would output as 1.23M in level-2 (assuming that a period is your local - decimal point). + (mega), `G` (giga), `T` (tera), `P` (peta), `E` (exa), `Z` (zetta) or `Y` + (yotta). For example, a 1234567-byte file would output as 1.23M in level-2 + (assuming that a period is your local decimal point). Backward compatibility note: versions of rsync prior to 3.1.0 do not support human-readable level 1, and they default to level 0. Thus, From 86d02460af0401b7f93572cc3b1868abe5a2d79b Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Tue, 21 Mar 2023 10:17:09 +0100 Subject: [PATCH 2/6] human-readable: use dynamic precision length Let's lower precision for huge numbers. The output used to be: 3.45M -> 46.73M -> 523.11M -> 1.24G -> ... With this change the code always gives the three most significant digits: 3.45M -> 46.7M -> 523M -> 1.24G -> ... --- lib/compat.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/compat.c b/lib/compat.c index 2f7f79c1b..29d445faf 100644 --- a/lib/compat.c +++ b/lib/compat.c @@ -185,6 +185,7 @@ char *do_big_num(int64 num, int human_flag, const char *fract) if (num >= mult || num <= -mult) { const char* units = " KMGTPEZY"; int64 powi = 1; + int powj = 1, precision = 2; for (;;) { if (labs(num / mult) < powi) @@ -197,8 +198,14 @@ char *do_big_num(int64 num, int human_flag, const char *fract) ++units; } - snprintf(bufs[n], sizeof bufs[0], "%.2f%c", - (double) num / powi, *units); + for (; precision > 0; precision--) { + powj *= 10; + if (labs(num / powi) < powj) + break; + } + + snprintf(bufs[n], sizeof bufs[0], "%.*f%c", + precision, (double) num / powi, *units); return bufs[n]; } } From 5e3b83a9f3ec5a6fb9102cc6f97e4f5a996c9523 Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Wed, 22 Mar 2023 11:07:15 +0100 Subject: [PATCH 3/6] human-readable: also handle num < mult with the same code ... just make sure no precision is added. --- lib/compat.c | 51 ++++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/lib/compat.c b/lib/compat.c index 29d445faf..02527dbdb 100644 --- a/lib/compat.c +++ b/lib/compat.c @@ -182,32 +182,33 @@ char *do_big_num(int64 num, int human_flag, const char *fract) if (human_flag > 1) { int mult = human_flag == 2 ? 1000 : 1024; - if (num >= mult || num <= -mult) { - const char* units = " KMGTPEZY"; - int64 powi = 1; - int powj = 1, precision = 2; - - for (;;) { - if (labs(num / mult) < powi) - break; - - if (units[1] == '\0') - break; - - powi *= mult; - ++units; - } - - for (; precision > 0; precision--) { - powj *= 10; - if (labs(num / powi) < powj) - break; - } - - snprintf(bufs[n], sizeof bufs[0], "%.*f%c", - precision, (double) num / powi, *units); - return bufs[n]; + const char* units = " KMGTPEZY"; + int64 powi = 1; + int powj = 1, precision = 2; + + for (;;) { + if (labs(num / mult) < powi) + break; + + if (units[1] == '\0') + break; + + powi *= mult; + ++units; } + + if (powi == 1) + precision = 0; + + for (; precision > 0; precision--) { + powj *= 10; + if (labs(num / powi) < powj) + break; + } + + snprintf(bufs[n], sizeof bufs[0], "%.*f%c", precision, + (double) num / powi, *units); + return bufs[n]; } s = bufs[n] + sizeof bufs[0] - 1; From 27892aa7c0007abca622251416f7cf02feb33bc2 Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Thu, 23 Mar 2023 10:33:33 +0100 Subject: [PATCH 4/6] human-readable: add an "i" to unit to indicate binary (1024) base --- lib/compat.c | 4 ++-- rsync.1.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/compat.c b/lib/compat.c index 02527dbdb..a36cebd99 100644 --- a/lib/compat.c +++ b/lib/compat.c @@ -206,8 +206,8 @@ char *do_big_num(int64 num, int human_flag, const char *fract) break; } - snprintf(bufs[n], sizeof bufs[0], "%.*f%c", precision, - (double) num / powi, *units); + snprintf(bufs[n], sizeof bufs[0], "%.*f%c%s", precision, + (double) num / powi, *units, num > mult && mult == 1024 ? "i" : ""); return bufs[n]; } diff --git a/rsync.1.md b/rsync.1.md index 10452a7ad..c2ce30eda 100644 --- a/rsync.1.md +++ b/rsync.1.md @@ -3312,6 +3312,8 @@ expand it. (mega), `G` (giga), `T` (tera), `P` (peta), `E` (exa), `Z` (zetta) or `Y` (yotta). For example, a 1234567-byte file would output as 1.23M in level-2 (assuming that a period is your local decimal point). + Additionally an `i` is appended in level-3 to indicate the binary base. + The same file would output as 1.17Mi in level-3. Backward compatibility note: versions of rsync prior to 3.1.0 do not support human-readable level 1, and they default to level 0. Thus, From ecb3f0054ed1470f887e63ec66935103d077a6df Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Tue, 9 Apr 2024 09:56:39 +0200 Subject: [PATCH 5/6] human-readable: also use it to format rate Let's also simplify the code for rate in progress, and benefit from same functionality. --- progress.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/progress.c b/progress.c index 87207fbfa..98a01c84c 100644 --- a/progress.c +++ b/progress.c @@ -69,9 +69,8 @@ static unsigned long msdiff(struct timeval *t1, struct timeval *t2) static void rprint_progress(OFF_T ofs, OFF_T size, struct timeval *now, int is_last) { char rembuf[64], eol[128]; - const char *units; unsigned long diff; - double rate, remain; + int64 rate, remain; int pct; if (is_last) { @@ -93,41 +92,31 @@ static void rprint_progress(OFF_T ofs, OFF_T size, struct timeval *now, int is_l /* Compute stats based on the starting info. */ if (!ph_start.time.tv_sec || !(diff = msdiff(&ph_start.time, now))) diff = 1; - rate = (double) (ofs - ph_start.ofs) * 1000.0 / diff / 1024.0; + rate = (ofs - ph_start.ofs) * 1000 / diff; /* Switch to total time taken for our last update. */ - remain = (double) diff / 1000.0; + remain = diff; } else { strlcpy(eol, " ", sizeof eol); /* Compute stats based on recent progress. */ if (!(diff = msdiff(&ph_list[oldest_hpos].time, now))) diff = 1; - rate = (double) (ofs - ph_list[oldest_hpos].ofs) * 1000.0 / diff / 1024.0; - remain = rate ? (double) (size - ofs) / rate / 1000.0 : 0.0; + rate = (ofs - ph_list[oldest_hpos].ofs) * 1000 / diff; + remain = rate ? (size - ofs) / rate : 0; } - if (rate > 1024*1024) { - rate /= 1024.0 * 1024.0; - units = "GB/s"; - } else if (rate > 1024) { - rate /= 1024.0; - units = "MB/s"; - } else { - units = "kB/s"; - } - - if (remain < 0 || remain > 9999.0 * 3600.0) + if (remain < 0 || remain > (int64) 9999999 * 3600) strlcpy(rembuf, " ??:??:??", sizeof rembuf); else { snprintf(rembuf, sizeof rembuf, "%4u:%02u:%02u", - (unsigned int) (remain / 3600.0), - (unsigned int) (remain / 60.0) % 60, + (unsigned int) (remain / 3600), + (unsigned int) (remain / 60) % 60, (unsigned int) remain % 60); } output_needs_newline = 0; pct = ofs == size ? 100 : (int) (100.0 * ofs / size); - rprintf(FCLIENT, "\r%15s %3d%% %7.2f%s %s%s", - human_num(ofs), pct, rate, units, rembuf, eol); + rprintf(FCLIENT, "\r%15s %3d%% %7sB/s %s%s", + human_num(ofs), pct, human_num(rate), rembuf, eol); if (!is_last && !quiet) { output_needs_newline = 1; rflush(FCLIENT); From c91621480c57aa2a3d60822623c0e3bf131b3183 Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Tue, 9 Apr 2024 10:50:20 +0200 Subject: [PATCH 6/6] human-readable: append suffix "B" for byte to ofs --- progress.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/progress.c b/progress.c index 98a01c84c..5e20fb40d 100644 --- a/progress.c +++ b/progress.c @@ -115,7 +115,7 @@ static void rprint_progress(OFF_T ofs, OFF_T size, struct timeval *now, int is_l output_needs_newline = 0; pct = ofs == size ? 100 : (int) (100.0 * ofs / size); - rprintf(FCLIENT, "\r%15s %3d%% %7sB/s %s%s", + rprintf(FCLIENT, "\r%15sB %3d%% %7sB/s %s%s", human_num(ofs), pct, human_num(rate), rembuf, eol); if (!is_last && !quiet) { output_needs_newline = 1;