From: hniksic Date: Sun, 21 Sep 2003 04:05:12 +0000 (-0700) Subject: [svn] Adjust bandwidth limitation sleep for the error of previous sleeps. X-Git-Tag: v1.13~1682 X-Git-Url: http://sjero.net/git/?p=wget;a=commitdiff_plain;h=b8e416c6c70912973e8873369599aa1bc37ba6c8 [svn] Adjust bandwidth limitation sleep for the error of previous sleeps. Allow decimal numbers in bandwidth limit specification. --- diff --git a/NEWS b/NEWS index 191f1663..dcae65fc 100644 --- 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. * Wget 1.8.2 is a bugfix release with no user-visible changes. diff --git a/src/ChangeLog b/src/ChangeLog index d7e088cf..49627335 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,13 @@ +2003-09-21 Hrvoje Niksic + + * 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 * main.c (main): Use setoptval() for setting the options. Use diff --git a/src/init.c b/src/init.c index 609761ed..62488fd2 100644 --- a/src/init.c +++ b/src/init.c @@ -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 .. 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; } diff --git a/src/retr.c b/src/retr.c index 05111562..fa69be0c 100644 --- a/src/retr.c +++ b/src/retr.c @@ -67,15 +67,16 @@ int global_download_count; 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);