]> sjero.net Git - wget/blobdiff - src/progress.c
[svn] Keep updating a subinterval until it reaches or exceeds a watermark.
[wget] / src / progress.c
index c4dd737e6d269df90baaef1ed951aee777663917..82e40c7a0cb33b0a58eb386fe326bf29e635e17a 100644 (file)
@@ -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));
@@ -453,7 +481,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
@@ -465,21 +495,69 @@ bar_update (void *progress, long howmuch, long dltime)
        equal to the expected size doesn't abort.  */
     bp->total_length = bp->count + bp->initial_length;
 
+  /* 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,10 +565,12 @@ bar_finish (void *progress, long dltime)
 {
   struct bar_progress *bp = progress;
 
+  /* 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.  */
+  if (bp->hist.summed_times == 0)
+    bp->hist.summed_times = 1;
   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;
 
   create_image (bp, dltime);
@@ -512,7 +592,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 +600,9 @@ create_image (struct bar_progress *bp, long dltime)
   char *size_legible = legible (size);
   int size_legible_len = strlen (size_legible);
 
+  long recent_time = bp->hist.summed_times;
+  long recent_bytes = bp->hist.summed_bytes;
+
   /* The progress bar should look like this:
      xx% [=======>             ] nn,nnn 12.34K/s ETA 00:00
 
@@ -617,11 +700,11 @@ create_image (struct bar_progress *bp, long dltime)
   p += strlen (p);
 
   /* " 1012.45K/s" */
-  if (dltime && bp->count)
+  if (recent_time && recent_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 = calc_rate (recent_bytes, recent_time, &units);
       sprintf (p, " %7.2f%s", dlrate, short_units[units]);
       p += strlen (p);
     }
@@ -629,13 +712,25 @@ 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 && recent_bytes > 0)
     {
-      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
+       {
+         double tm_sofar = (double)recent_time / 1000;
+         long bytes_remaining = bp->total_length - size;
+         eta = (long) (tm_sofar * bytes_remaining / recent_bytes);
+         bp->last_eta_value = eta;
+         bp->last_eta_time = dl_total_time;
+       }
 
       eta_hrs = eta / 3600, eta %= 3600;
       eta_min = eta / 60,   eta %= 60;
@@ -647,7 +742,7 @@ create_image (struct bar_progress *bp, long dltime)
        /* Hours not printed: pad with three spaces (two digits and
           colon). */
        APPEND_LITERAL ("   ");
-      else if (eta_hrs >= 10)
+      else if (eta_hrs < 10)
        /* Hours printed with one digit: pad with one space. */
        *p++ = ' ';
       else
@@ -683,8 +778,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