]> sjero.net Git - wget/commitdiff
[svn] Adjust bandwidth limitation sleep for the error of previous sleeps.
authorhniksic <devnull@localhost>
Sun, 21 Sep 2003 04:05:12 +0000 (21:05 -0700)
committerhniksic <devnull@localhost>
Sun, 21 Sep 2003 04:05:12 +0000 (21:05 -0700)
Allow decimal numbers in bandwidth limit specification.

NEWS
src/ChangeLog
src/init.c
src/retr.c

diff --git a/NEWS b/NEWS
index 191f16639a457b05403246855870bea34b5cefe8..dcae65fcee2daa240fdcdfb56e648797fcf24e7d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -52,7 +52,8 @@ as "//img.foo.com/foo.jpg".
 values "yes" and "no" along with the traditional "on" and "off".
 
 ** It is now possible to specify decimal values for --timeout, --wait,
-and --waitretry.  For instance, `--wait=0.5' now works as expected.
+--waitretry, and --limit-rate.  For instance, `--wait=0.5' now
+works as expected, and so does --limit-rate=2.5k.
 \f
 * Wget 1.8.2 is a bugfix release with no user-visible changes.
 \f
index d7e088cf801d8991bf64ff01ab88e5596f8a3bea..49627335f0bf330427caa15ae6382403d54ce759 100644 (file)
@@ -1,3 +1,13 @@
+2003-09-21  Hrvoje Niksic  <hniksic@xemacs.org>
+
+       * init.c (simple_atof): New function.
+       (cmd_time): Use it.
+       (cmd_bytes): Accept things like "1.5k" and such.  Use simple_atof
+       to parse decimals.
+
+       * retr.c (limit_bandwidth): Adjust each sleep by the error of the
+       previous one.
+
 2003-09-21  Hrvoje Niksic  <hniksic@xemacs.org>
 
        * main.c (main): Use setoptval() for setting the options.  Use
index 609761eddaef7c2fe8965dd08a803ec48ff2ac64..62488fd20a4f03f190f1ce1eea65f89c678d3f2e 100644 (file)
@@ -839,64 +839,115 @@ cmd_directory_vector (const char *com, const char *val, void *closure)
   return 1;
 }
 
-/* Set the value stored in VAL to CLOSURE (which should point to a
-   long int), allowing several postfixes, with the following syntax
-   (regexp):
+/* Poor man's atof: handles only <digits>.<digits>.  Returns 1 on
+   success, 0 on failure.  In case of success, stores its result to
+   *DEST.  */
 
-   [0-9]+       -> bytes
-   [0-9]+[kK]   -> bytes * 1024
-   [0-9]+[mM]   -> bytes * 1024 * 1024
-   inf          -> 0
+static int
+simple_atof (const char *beg, const char *end, double *dest)
+{
+  double result = 0;
+
+  int seen_dot = 0;
+  int seen_digit = 0;
+  double divider = 1;
+
+  const char *p = beg;
+
+  while (p < end)
+    {
+      char ch = *p++;
+      if (ISDIGIT (ch))
+       {
+         if (!seen_dot)
+           result = (10 * result) + (ch - '0');
+         else
+           result += (ch - '0') / (divider *= 10);
+         seen_digit = 1;
+       }
+      else if (ch == '.')
+       {
+         if (!seen_dot)
+           seen_dot = 1;
+         else
+           return 0;
+       }
+    }
+  if (!seen_digit)
+    return 0;
+
+  *dest = result;
+  return 1;
+}
+
+/* Parse VAL as a number and set its value to CLOSURE (which should
+   point to a long int).
+
+   By default, the value is assumed to be in bytes.  If "K", "M", or
+   "G" are appended, the value is multiplied with 1<<10, 1<<20, or
+   1<<30, respectively.  Floating point values are allowed and are
+   cast to integer before use.  The idea is to be able to use things
+   like 1.5k instead of "1536".
+
+   The string "inf" is returned as 0.
+
+   In case of error, 0 is returned and memory pointed to by CLOSURE
+   remains unmodified.  */
 
-   Anything else is flagged as incorrect, and CLOSURE is unchanged.  */
 static int
 cmd_bytes (const char *com, const char *val, void *closure)
 {
-  long result;
-  long *out = (long *)closure;
-  const char *p;
+  long mult;
+  double number;
+  const char *end = val + strlen (val);
 
-  result = 0;
-  p = val;
   /* Check for "inf".  */
-  if (p[0] == 'i' && p[1] == 'n' && p[2] == 'f' && p[3] == '\0')
+  if (0 == strcmp (val, "inf"))
     {
-      *out = 0;
+      *(long *)closure = 0;
       return 1;
     }
-  /* Search for digits and construct result.  */
-  for (; *p && ISDIGIT (*p); p++)
-    result = (10 * result) + (*p - '0');
-  /* If no digits were found, or more than one character is following
-     them, bail out.  */
-  if (p == val || (*p != '\0' && *(p + 1) != '\0'))
+
+  /* Strip trailing whitespace.  */
+  while (val < end && ISSPACE (end[-1]))
+    --end;
+
+  if (val == end)
     {
-      printf (_("%s: Invalid specification `%s'\n"), com, val);
+    err:
+      fprintf (stderr, _("%s: Invalid byte value `%s'\n"), com, val);
       return 0;
     }
-  /* Search for a designator.  */
-  switch (TOLOWER (*p))
+
+  switch (TOLOWER (end[-1]))
     {
-    case '\0':
-      /* None */
-      break;
     case 'k':
-      /* Kilobytes */
-      result *= 1024;
+      --end, mult = 1L<<10;
       break;
     case 'm':
-      /* Megabytes */
-      result *= (long)1024 * 1024;
+      --end, mult = 1L<<20;
       break;
     case 'g':
-      /* Gigabytes */
-      result *= (long)1024 * 1024 * 1024;
+      --end, mult = 1L<<30;
       break;
     default:
-      printf (_("%s: Invalid specification `%s'\n"), com, val);
-      return 0;
+      /* Not a recognized suffix: assume it belongs to the number.
+        (If not, atof simple_atof will raise an error.)  */
+      mult = 1;
     }
-  *out = result;
+
+  /* Skip leading and trailing whitespace. */
+  while (val < end && ISSPACE (*val))
+    ++val;
+  while (val < end && ISSPACE (end[-1]))
+    --end;
+  if (val == end)
+    goto err;
+
+  if (!simple_atof (val, end, &number))
+    goto err;
+
+  *(long *)closure = (long)(number * mult);
   return 1;
 }
 
@@ -907,14 +958,11 @@ cmd_bytes (const char *com, const char *val, void *closure)
 static int
 cmd_time (const char *com, const char *val, void *closure)
 {
-  double result, mult, divider;
-  int seen_dot, seen_digit;
-
-  const char *p;
+  double number, mult;
   const char *end = val + strlen (val);
 
-  /* Skip trailing whitespace.  */
-  while (end > val && ISSPACE (end[-1]))
+  /* Strip trailing whitespace.  */
+  while (val < end && ISSPACE (end[-1]))
     --end;
 
   if (val == end)
@@ -936,54 +984,29 @@ cmd_time (const char *com, const char *val, void *closure)
       --end, mult = 3600;      /* hours */
       break;
     case 'd':
-      --end, mult = 86400;     /* days */
+      --end, mult = 86400.0;   /* days */
       break;
     case 'w':
-      --end, mult = 604800;    /* weeks */
+      --end, mult = 604800.0;  /* weeks */
       break;
     default:
-      /* Not a recognized suffix: treat it as part of number, and
-        assume seconds. */
+      /* Not a recognized suffix: assume it belongs to the number.
+        (If not, atof simple_atof will raise an error.)  */
       mult = 1;
     }
 
   /* Skip leading and trailing whitespace. */
   while (val < end && ISSPACE (*val))
     ++val;
-  while (val > end && ISSPACE (end[-1]))
+  while (val < end && ISSPACE (end[-1]))
     --end;
   if (val == end)
     goto err;
 
-  /* Poor man's strtod: */
-  result = 0;
-  seen_dot = seen_digit = 0;
-  divider = 1;
-
-  p = val;
-  while (p < end)
-    {
-      char ch = *p++;
-      if (ISDIGIT (ch))
-       {
-         if (!seen_dot)
-           result = (10 * result) + (ch - '0');
-         else
-           result += (ch - '0') / (divider *= 10);
-         seen_digit = 1;
-       }
-      else if (ch == '.')
-       {
-         if (!seen_dot)
-           seen_dot = 1;
-         else
-           goto err;
-       }
-    }
-  if (!seen_digit)
+  if (!simple_atof (val, end, &number))
     goto err;
-  result *= mult;
-  *(double *)closure = result;
+
+  *(double *)closure = number * mult;
   return 1;
 }
 \f
index 051115626ddde5c7f3ce2170959e33fae1a3af54..fa69be0ca90081de77fd3416ba7eb7a9f8c6f3c1 100644 (file)
@@ -67,15 +67,16 @@ int global_download_count;
 
 \f
 static struct {
-  long bytes;
-  double dltime;
+  long chunk_bytes;
+  double chunk_start;
+  double sleep_adjust;
 } limit_data;
 
 static void
 limit_bandwidth_reset (void)
 {
-  limit_data.bytes  = 0;
-  limit_data.dltime = 0;
+  limit_data.chunk_bytes = 0;
+  limit_data.chunk_start = 0;
 }
 
 /* Limit the bandwidth by pausing the download for an amount of time.
@@ -83,30 +84,48 @@ limit_bandwidth_reset (void)
    is the number of milliseconds it took to receive them.  */
 
 static void
-limit_bandwidth (long bytes, double delta)
+limit_bandwidth (long bytes, double *dltime, struct wget_timer *timer)
 {
+  double delta_t = *dltime - limit_data.chunk_start;
   double expected;
 
-  limit_data.bytes += bytes;
-  limit_data.dltime += delta;
+  limit_data.chunk_bytes += bytes;
 
-  expected = 1000.0 * limit_data.bytes / opt.limit_rate;
+  /* Calculate the amount of time we expect downloading the chunk
+     should take.  If in reality it took less time, sleep to
+     compensate for the difference.  */
+  expected = 1000.0 * limit_data.chunk_bytes / opt.limit_rate;
 
-  if (expected > limit_data.dltime)
+  if (expected > delta_t)
     {
-      double slp = expected - limit_data.dltime;
+      double slp = expected - delta_t + limit_data.sleep_adjust;
+      double t0, t1;
       if (slp < 200)
        {
          DEBUGP (("deferring a %.2f ms sleep (%ld/%.2f).\n",
-                  slp, limit_data.bytes, limit_data.dltime));
+                  slp, limit_data.chunk_bytes, delta_t));
          return;
        }
-      DEBUGP (("sleeping %.2f ms\n", slp));
+      DEBUGP (("\nsleeping %.2f ms for %ld bytes, adjust %.2f ms\n",
+              slp, limit_data.chunk_bytes, limit_data.sleep_adjust));
+
+      t0 = *dltime;
       usleep ((unsigned long) (1000 * slp));
+      t1 = wtimer_elapsed (timer);
+
+      /* Due to scheduling, we probably slept slightly longer (or
+        shorter) than desired.  Calculate the difference between the
+        desired and the actual sleep, and adjust the next sleep by
+        that amount.  */
+      limit_data.sleep_adjust = slp - (t1 - t0);
+
+      /* Since we've called wtimer_elapsed, we might as well update
+        the caller's dltime. */
+      *dltime = t1;
     }
 
-  limit_data.bytes = 0;
-  limit_data.dltime = 0;
+  limit_data.chunk_bytes = 0;
+  limit_data.chunk_start = *dltime;
 }
 
 #define MIN(i, j) ((i) <= (j) ? (i) : (j))
@@ -143,7 +162,7 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
 
   void *progress = NULL;
   struct wget_timer *timer = wtimer_allocate ();
-  double dltime = 0, last_dltime = 0;
+  double dltime = 0;
 
   *len = restval;
 
@@ -210,15 +229,9 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
          goto out;
        }
 
-      /* If bandwidth is not limited, one call to wtimer_elapsed is
-        sufficient.  */
       dltime = wtimer_elapsed (timer);
       if (opt.limit_rate)
-       {
-         limit_bandwidth (res, dltime - last_dltime);
-         dltime = wtimer_elapsed (timer);
-         last_dltime = dltime;
-       }
+       limit_bandwidth (res, &dltime, timer);
 
       if (progress)
        progress_update (progress, res, dltime);