X-Git-Url: http://sjero.net/git/?p=wget;a=blobdiff_plain;f=src%2Fprogress.c;h=644c1b2b9dc1571bb3d3dbabbbc5c4bad27f6dbc;hp=3ae5b38d3510e9996f4f7704004d7e88bf1af672;hb=c77a16309f0d1903961f102d21917f2b882896a5;hpb=0a89fc1a101423eeee0f55d15afa78c7ab05c70e diff --git a/src/progress.c b/src/progress.c index 3ae5b38d..644c1b2b 100644 --- a/src/progress.c +++ b/src/progress.c @@ -41,10 +41,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ struct progress_implementation { char *name; - void *(*create) (long, long); - void (*update) (void *, long, long); - void (*finish) (void *, long); - void (*set_params) (const char *); + void *(*create) PARAMS ((long, long)); + void (*update) PARAMS ((void *, long, long)); + void (*finish) PARAMS ((void *, long)); + void (*set_params) PARAMS ((const char *)); }; /* Necessary forward declarations. */ @@ -403,6 +403,14 @@ dot_set_params (const char *params) static int screen_width = DEFAULT_SCREEN_WIDTH; +/* Size of the history table for download speeds. */ +#define DLSPEED_HISTORY_SIZE 30 + +/* The time interval in milliseconds below which we increase old + history entries rather than overwriting them. That interval + represents the scope of the download speed history. */ +#define DLSPEED_HISTORY_MAX_INTERVAL 3000 + struct bar_progress { long initial_length; /* how many bytes have been downloaded previously. */ @@ -410,7 +418,7 @@ struct bar_progress { download finishes */ long count; /* bytes downloaded so far */ - long last_update; /* time of the last screen update. */ + long last_screen_update; /* time of the last screen update. */ int width; /* screen width we're using at the time the progress gauge was @@ -420,7 +428,27 @@ struct bar_progress { signal. */ char *buffer; /* buffer where the bar "image" is stored. */ - int tick; + int tick; /* counter used for drawing the + progress bar where the total size + is not known. */ + + /* The following variables (kept in a struct for namespace reasons) + keep track of recent download speeds. See bar_update() for + details. */ + struct bar_progress_hist { + int pos; + long times[DLSPEED_HISTORY_SIZE]; + long bytes[DLSPEED_HISTORY_SIZE]; + long summed_times; + long summed_bytes; + long previous_time; + } hist; + + /* create_image() uses these to make sure that ETA information + doesn't flash. */ + long last_eta_time; /* time of the last update to download + speed and ETA. */ + long last_eta_value; }; static void create_image PARAMS ((struct bar_progress *, long)); @@ -433,6 +461,11 @@ bar_create (long initial, long total) memset (bp, 0, sizeof (*bp)); + /* In theory, our callers should take care of this pathological + case, but it can sometimes happen. */ + if (initial > total) + total = initial; + bp->initial_length = initial; bp->total_length = total; @@ -453,7 +486,9 @@ static void bar_update (void *progress, long howmuch, long dltime) { struct bar_progress *bp = progress; - int force_update = 0; + struct bar_progress_hist *hist = &bp->hist; + int force_screen_update = 0; + long delta_time = dltime - hist->previous_time; bp->count += howmuch; if (bp->total_length > 0 @@ -463,23 +498,71 @@ bar_update (void *progress, long howmuch, long dltime) adjust bp->total_length to the new reality, so that the code in create_image() that depends on total size being smaller or equal to the expected size doesn't abort. */ - bp->total_length = bp->count + bp->initial_length; + bp->total_length = bp->initial_length + bp->count; + + /* This code attempts to determine the current download speed. We + measure the speed over the interval of approximately three + seconds, in subintervals no smaller than 0.1s. In other words, + we maintain and use the history of 30 most recent reads, where a + "read" consists of one or more network reads, up until the point + where a subinterval is filled. */ + + if (hist->times[hist->pos] + >= DLSPEED_HISTORY_MAX_INTERVAL / DLSPEED_HISTORY_SIZE) + { + /* The subinterval at POS has been used up. Move on to the next + position. */ + if (++hist->pos == DLSPEED_HISTORY_SIZE) + hist->pos = 0; + + /* Invalidate old data (from the previous cycle) at this + position. */ + hist->summed_times -= hist->times[hist->pos]; + hist->summed_bytes -= hist->bytes[hist->pos]; + hist->times[hist->pos] = delta_time; + hist->bytes[hist->pos] = howmuch; + } + else + { + /* Increment the data at POS. */ + hist->times[hist->pos] += delta_time; + hist->bytes[hist->pos] += howmuch; + } + + hist->summed_times += delta_time; + hist->summed_bytes += howmuch; + hist->previous_time = dltime; + +#if 0 + /* Sledgehammer check that summed_times and summed_bytes are + accurate. */ + { + int i; + long sumt = 0, sumb = 0; + for (i = 0; i < DLSPEED_HISTORY_SIZE; i++) + { + sumt += hist->times[i]; + sumb += hist->bytes[i]; + } + assert (sumt == hist->summed_times); + assert (sumb == hist->summed_bytes); + } +#endif if (screen_width - 1 != bp->width) { bp->width = screen_width - 1; bp->buffer = xrealloc (bp->buffer, bp->width + 1); - force_update = 1; + force_screen_update = 1; } - if (dltime - bp->last_update < 200 && !force_update) + if (dltime - bp->last_screen_update < 200 && !force_screen_update) /* Don't update more often than five times per second. */ return; - bp->last_update = dltime; - create_image (bp, dltime); display_image (bp->buffer); + bp->last_screen_update = dltime; } static void @@ -487,11 +570,10 @@ bar_finish (void *progress, long dltime) { struct bar_progress *bp = progress; - if (dltime == 0) - /* If the download was faster than the granularity of the timer, - fake some output so that we don't get the ugly "----.--" rate - at the download finish. */ - dltime = 1; + if (bp->total_length > 0 + && bp->count + bp->initial_length > bp->total_length) + /* See bar_update() for explanation. */ + bp->total_length = bp->initial_length + bp->count; create_image (bp, dltime); display_image (bp->buffer); @@ -512,7 +594,7 @@ bar_finish (void *progress, long dltime) #endif static void -create_image (struct bar_progress *bp, long dltime) +create_image (struct bar_progress *bp, long dl_total_time) { char *p = bp->buffer; long size = bp->initial_length + bp->count; @@ -520,6 +602,8 @@ create_image (struct bar_progress *bp, long dltime) char *size_legible = legible (size); int size_legible_len = strlen (size_legible); + struct bar_progress_hist *hist = &bp->hist; + /* The progress bar should look like this: xx% [=======> ] nn,nnn 12.34K/s ETA 00:00 @@ -617,11 +701,12 @@ create_image (struct bar_progress *bp, long dltime) p += strlen (p); /* " 1012.45K/s" */ - if (dltime && bp->count) + if (hist->summed_times && hist->summed_bytes) { static char *short_units[] = { "B/s", "K/s", "M/s", "G/s" }; int units = 0; - double dlrate = calc_rate (bp->count, dltime, &units); + double dlrate; + dlrate = calc_rate (hist->summed_bytes, hist->summed_times, &units); sprintf (p, " %7.2f%s", dlrate, short_units[units]); p += strlen (p); } @@ -629,46 +714,59 @@ create_image (struct bar_progress *bp, long dltime) APPEND_LITERAL (" --.--K/s"); /* " ETA xx:xx:xx" */ - if (bp->total_length > 0 && bp->count > 0) + if (bp->total_length > 0 && dl_total_time > 3000) { - int eta, eta_hrs, eta_min, eta_sec; - double tm_sofar = (double)dltime / 1000; - long bytes_remaining = bp->total_length - size; - - eta = (int) (tm_sofar * bytes_remaining / bp->count); + long eta; + int eta_hrs, eta_min, eta_sec; + + /* Don't change the value of ETA more than approximately once + per second; doing so would cause flashing without providing + any value to the user. */ + if (dl_total_time - bp->last_eta_time < 900 + && bp->last_eta_value != 0) + eta = bp->last_eta_value; + else + { + /* Calculate ETA using the average download speed to predict + the future speed. If you want to use the current speed + instead, replace dl_total_time with hist->summed_times + and bp->count with hist->summed_bytes. I found that + doing that results in a very jerky and ultimately + unreliable ETA. */ + double time_sofar = (double)dl_total_time / 1000; + long bytes_remaining = bp->total_length - size; + eta = (long) (time_sofar * bytes_remaining / bp->count); + bp->last_eta_value = eta; + bp->last_eta_time = dl_total_time; + } eta_hrs = eta / 3600, eta %= 3600; eta_min = eta / 60, eta %= 60; eta_sec = eta; - /* Pad until the end of screen. The padding is dependent on the - hour value. */ - if (eta_hrs == 0 || eta_hrs > 99) - /* Hours not printed: pad with three spaces (two digits and - colon). */ - APPEND_LITERAL (" "); - else if (eta_hrs < 10) - /* Hours printed with one digit: pad with one space. */ - *p++ = ' '; - else - /* Hours printed with two digits: we're using maximum width, - don't pad. */ - ; - - APPEND_LITERAL (" ETA "); - if (eta_hrs > 99) - /* Bogus value, probably due to a calculation overflow. Print - something safe to avoid overstepping the buffer bounds. */ - sprintf (p, "--:--"); - else if (eta_hrs > 0) - sprintf (p, "%d:%02d:%02d", eta_hrs, eta_min, eta_sec); + goto no_eta; + + if (eta_hrs == 0) + { + /* Hours not printed: pad with three spaces. */ + APPEND_LITERAL (" "); + sprintf (p, " ETA %02d:%02d", eta_min, eta_sec); + } else - sprintf (p, "%02d:%02d", eta_min, eta_sec); + { + if (eta_hrs < 10) + /* Hours printed with one digit: pad with one space. */ + *p++ = ' '; + sprintf (p, " ETA %d:%02d:%02d", eta_hrs, eta_min, eta_sec); + } p += strlen (p); } else if (bp->total_length > 0) - APPEND_LITERAL (" ETA --:--"); + { + no_eta: + APPEND_LITERAL (" "); + } assert (p - bp->buffer <= bp->width); @@ -683,8 +781,10 @@ create_image (struct bar_progress *bp, long dltime) static void display_image (char *buf) { + int old = log_set_save_context (0); logputs (LOG_VERBOSE, "\r"); logputs (LOG_VERBOSE, buf); + log_set_save_context (old); } static void