From 2d258a30d8315f2c0dbcfd4e91fe143fed6f08ec Mon Sep 17 00:00:00 2001 From: Bernhard Nortmann Date: Thu, 26 Nov 2015 15:56:09 +0100 Subject: [PATCH] fel: extend progress display with transfer rate and ETA Signed-off-by: Bernhard Nortmann Reviewed-by: Siarhei Siamashka --- progress.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++--- progress.h | 2 ++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/progress.c b/progress.c index 5b24198..05d8f8b 100644 --- a/progress.c +++ b/progress.c @@ -28,16 +28,48 @@ inline double gettime(void) return tv.tv_sec + (double)tv.tv_usec / 1000000.; } +/* Calculate transfer rate (in bytes per second) */ +inline double rate(size_t transferred, double elapsed) +{ + if (elapsed > 0) + return (double)transferred / elapsed; + return 0.; +} + +/* Estimate remaining time ("ETA") for given transfer rate */ +inline double estimate(size_t remaining, double rate) +{ + if (rate > 0) + return (double)remaining / rate; + return 0.; +} + +/* Return ETA (in seconds) as string, formatted to minutes and seconds */ +const char *format_ETA(double remaining) +{ + static char result[6] = ""; + + int seconds = remaining + 0.5; /* simplistic round() */ + if (seconds >= 0 && seconds < 6000) { + snprintf(result, sizeof(result), + "%02d:%02d", seconds / 60, seconds % 60); + return result; + } + return "--:--"; +} + /* Private progress state variable */ typedef struct { progress_cb_t callback; size_t total; size_t done; + double start; /* start point (timestamp) for rate and ETA calculation */ } progress_private_t; static progress_private_t progress = { .callback = NULL, + .start = 0. }; /* 'External' API */ @@ -47,6 +79,7 @@ void progress_start(progress_cb_t callback, size_t expected_total) progress.callback = callback; progress.total = expected_total; progress.done = 0; + progress.start = gettime(); /* reset start time */ } /* Update progress status, passing information to the callback function. */ @@ -57,19 +90,32 @@ void progress_update(size_t bytes_done) progress.callback(progress.total, progress.done); } +/* Return relative / "elapsed" time, since progress_start() */ +static inline double progress_elapsed(void) +{ + if (progress.start != 0.) + return gettime() - progress.start; + return 0.; +} + /* Callback function implementing a simple progress bar written to stdout */ void progress_bar(size_t total, size_t done) { - static const int WIDTH = 60; /* # of characters to use for progress bar */ + static const int WIDTH = 48; /* # of characters to use for progress bar */ float ratio = total > 0 ? (float)done / total : 0; int i, pos = WIDTH * ratio; + double speed = rate(done, progress_elapsed()); + double eta = estimate(total - done, speed); printf("\r%3.0f%% [", ratio * 100); /* current percentage */ for (i = 0; i < pos; i++) putchar('='); for (i = pos; i < WIDTH; i++) putchar(' '); - printf("] "); + if (done < total) + printf("]%6.1f kB/s, ETA %s ", kilo(speed), format_ETA(eta)); + else + /* transfer complete, output totals plus a newline */ + printf("] %5.0f kB, %6.1f kB/s\n", kilo(done), kilo(speed)); - if (done >= total) putchar('\n'); /* output newline when complete */ fflush(stdout); } diff --git a/progress.h b/progress.h index 6ea501c..b072cc0 100644 --- a/progress.h +++ b/progress.h @@ -27,6 +27,8 @@ typedef void (*progress_cb_t)(size_t total, size_t done); #define kibi(value) ((double)(value) / 1024.) /* binary prefix "Ki", "K" */ double gettime(void); +double rate(size_t transferred, double elapsed); +double estimate(size_t remaining, double rate); void progress_start(progress_cb_t callback, size_t expected_total); void progress_update(size_t bytes_done);