Allow the rate limit to be specified as a fractional number (#193).
This commit is contained in:
@@ -70,7 +70,7 @@ struct opts_s {
|
||||
/*@keep@*/ /*@null@*/ int *watchfd_fd; /* array of fds to watch in each one (0=all) */
|
||||
/*@keep@*/ /*@null@*/ argv_string *argv; /* array of non-option arguments */
|
||||
size_t lastwritten; /* show N bytes last written */
|
||||
off_t rate_limit; /* rate limit, in bytes per second */
|
||||
long double rate_limit; /* rate limit, in bytes per second */
|
||||
size_t buffer_size; /* buffer size, in bytes (0=default) */
|
||||
size_t pipe_buffer_size; /* pipe buffer size, in bytes (0=default) */
|
||||
off_t size; /* total size of data */
|
||||
@@ -114,6 +114,7 @@ struct opts_s {
|
||||
bool show_stats; /* set to write statistics at the end */
|
||||
bool width_set_manually; /* width was set manually, not detected */
|
||||
bool height_set_manually; /* height was set manually, not detected */
|
||||
bool rate_limit_active; /* whether a rate limit was set */
|
||||
};
|
||||
|
||||
/*@-exportlocal@*/
|
||||
|
||||
@@ -202,7 +202,7 @@ struct pvstate_s {
|
||||
/*@null@*/ char *output_name; /* name of the output, for diagnostics */
|
||||
/*@null@*/ char *default_bar_style; /* which bar style to use by default */
|
||||
off_t error_skip_block; /* skip block size, 0 for adaptive */
|
||||
off_t rate_limit; /* rate limit, in bytes per second */
|
||||
long double rate_limit; /* rate limit, in bytes per second */
|
||||
size_t target_buffer_size; /* transfer buffer size (0=default) */
|
||||
size_t pipe_buffer_size; /* pipe buffer size (0=default) */
|
||||
off_t size; /* total size of data */
|
||||
@@ -237,6 +237,7 @@ struct pvstate_s {
|
||||
bool show_stats; /* show statistics on exit */
|
||||
bool width_set_manually; /* width was set manually, not detected */
|
||||
bool height_set_manually; /* height was set manually, not detected */
|
||||
bool rate_limit_active; /* whether a rate limit is in effect */
|
||||
} control;
|
||||
|
||||
/*******************
|
||||
|
||||
+4
-2
@@ -72,8 +72,10 @@ extern double pv_getnum_interval(const char *);
|
||||
* Return the numeric value of a string representing a size, interpreting
|
||||
* suffixes in decimal units (multiples of 1000) instead of multiples of
|
||||
* 1024 if the second argument is true.
|
||||
*
|
||||
* Optionally also places the result in a long double.
|
||||
*/
|
||||
extern off_t pv_getnum_size(const char *, bool);
|
||||
extern off_t pv_getnum_size(const char *, bool, /*@null@ */ long double *);
|
||||
|
||||
/*
|
||||
* Return the numeric value of a string representing a count such as screen
|
||||
@@ -264,7 +266,7 @@ extern void pv_state_stop_at_size_set(pvstate_t, bool);
|
||||
extern void pv_state_sync_after_write_set(pvstate_t, bool);
|
||||
extern void pv_state_direct_io_set(pvstate_t, bool);
|
||||
extern void pv_state_sparse_output_set(pvstate_t, bool);
|
||||
extern void pv_state_rate_limit_set(pvstate_t, off_t);
|
||||
extern void pv_state_rate_limit_set(pvstate_t, long double, bool);
|
||||
extern void pv_state_target_buffer_size_set(pvstate_t, size_t);
|
||||
extern void pv_state_no_splice_set(pvstate_t, bool);
|
||||
extern void pv_state_pipe_buffer_size_set(pvstate_t, size_t);
|
||||
|
||||
+1
-1
@@ -903,7 +903,7 @@ int main(int argc, char **argv)
|
||||
pv_state_sync_after_write_set(state, opts->sync_after_write);
|
||||
pv_state_direct_io_set(state, opts->direct_io);
|
||||
pv_state_discard_input_set(state, opts->discard_input);
|
||||
pv_state_rate_limit_set(state, opts->rate_limit);
|
||||
pv_state_rate_limit_set(state, opts->rate_limit, opts->rate_limit_active);
|
||||
pv_state_target_buffer_size_set(state, opts->buffer_size);
|
||||
pv_state_no_splice_set(state, opts->no_splice);
|
||||
pv_state_pipe_buffer_size_set(state, opts->pipe_buffer_size);
|
||||
|
||||
+22
-6
@@ -35,6 +35,9 @@
|
||||
#ifdef HAVE_FTW_H
|
||||
#include <ftw.h>
|
||||
#endif
|
||||
#if HAVE_MATH_H
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
|
||||
void display_help(void);
|
||||
@@ -1005,7 +1008,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
case 's':
|
||||
if ('@' != *optarg) {
|
||||
/* A number was passed, not "@<filename>". */
|
||||
opts->size = pv_getnum_size(optarg, opts->decimal_units);
|
||||
opts->size = pv_getnum_size(optarg, opts->decimal_units, NULL);
|
||||
} else {
|
||||
/* Permit "@<filename>". */
|
||||
const char *size_file = 1 + optarg;
|
||||
@@ -1054,10 +1057,23 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
case 'L':
|
||||
opts->rate_limit = pv_getnum_size(optarg, opts->decimal_units);
|
||||
(void) pv_getnum_size(optarg, opts->decimal_units, &(opts->rate_limit));
|
||||
#if HAVE_MATH_H
|
||||
/*@-unrecog@ *//* splint doesn't know nextafterl(). */
|
||||
if (opts->rate_limit < nextafterl(0, INFINITY)) {
|
||||
opts->rate_limit_active = false;
|
||||
} else {
|
||||
opts->rate_limit_active = true;
|
||||
}
|
||||
/*@+unrecog@ */
|
||||
#else
|
||||
opts->rate_limit_active = false;
|
||||
if (opts->rate_limit > 0.00001)
|
||||
opts->rate_limit_active = true;
|
||||
#endif
|
||||
break;
|
||||
case 'B':
|
||||
opts->buffer_size = (size_t) pv_getnum_size(optarg, opts->decimal_units);
|
||||
opts->buffer_size = (size_t) pv_getnum_size(optarg, opts->decimal_units, NULL);
|
||||
opts->no_splice = true;
|
||||
break;
|
||||
case 'C':
|
||||
@@ -1067,10 +1083,10 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
opts->skip_errors++;
|
||||
break;
|
||||
case 'Z':
|
||||
opts->error_skip_block = pv_getnum_size(optarg, opts->decimal_units);
|
||||
opts->error_skip_block = pv_getnum_size(optarg, opts->decimal_units, NULL);
|
||||
break;
|
||||
case 'J':
|
||||
opts->pipe_buffer_size = (size_t) pv_getnum_size(optarg, opts->decimal_units);
|
||||
opts->pipe_buffer_size = (size_t) pv_getnum_size(optarg, opts->decimal_units, NULL);
|
||||
break;
|
||||
case 'S':
|
||||
opts->stop_at_size = true;
|
||||
@@ -1214,7 +1230,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
if (PV_ACTION_WATCHFD == opts->action) {
|
||||
if (opts->linemode || opts->null_terminated_lines || opts->stop_at_size
|
||||
|| (opts->skip_errors > 0) || (opts->buffer_size > 0)
|
||||
|| (opts->rate_limit > 0)) {
|
||||
|| (opts->rate_limit_active)) {
|
||||
/*@-mustfreefresh@ *//* see above */
|
||||
pv_error("%s",
|
||||
_("cannot use line mode or transfer modifier options when watching file descriptors"));
|
||||
|
||||
+4
-4
@@ -572,7 +572,7 @@ int pv_main_loop(pvstate_t state)
|
||||
if (1 == state->flags.trigger_exit)
|
||||
break;
|
||||
|
||||
if (state->control.rate_limit > 0) {
|
||||
if (state->control.rate_limit_active) {
|
||||
pv_elapsedtime_read(&cur_time);
|
||||
if (pv_elapsedtime_compare(&cur_time, &next_ratecheck) > 0) {
|
||||
target +=
|
||||
@@ -595,7 +595,7 @@ int pv_main_loop(pvstate_t state)
|
||||
if ((0 < state->control.size) && (state->control.stop_at_size)) {
|
||||
if ((state->control.size < (state->transfer.total_written + cansend))
|
||||
|| ((0 == cansend)
|
||||
&& (0 == state->control.rate_limit))) {
|
||||
&& (!state->control.rate_limit_active))) {
|
||||
cansend = state->control.size - state->transfer.total_written;
|
||||
if (0 >= cansend) {
|
||||
debug("%s", "write limit reached (size explicitly set) - setting EOF flags");
|
||||
@@ -622,11 +622,11 @@ int pv_main_loop(pvstate_t state)
|
||||
|
||||
if (state->control.linemode) {
|
||||
state->transfer.total_written += lineswritten;
|
||||
if (state->control.rate_limit > 0)
|
||||
if (state->control.rate_limit_active)
|
||||
target -= lineswritten;
|
||||
} else {
|
||||
state->transfer.total_written += written;
|
||||
if (state->control.rate_limit > 0)
|
||||
if (state->control.rate_limit_active)
|
||||
target -= written;
|
||||
}
|
||||
|
||||
|
||||
+19
-4
@@ -29,8 +29,11 @@ bool pv_isdigit(char c)
|
||||
* a fractional part, optionally followed by a units suffix such as "K" for
|
||||
* kibibytes. If "decimal_units" is true, suffixes are interpreted as
|
||||
* multiples of 1000, rather than multiples of 1024.
|
||||
*
|
||||
* If resultptr is not NULL, it is populated with the result as a long
|
||||
* double.
|
||||
*/
|
||||
off_t pv_getnum_size(const char *str, bool decimal_units)
|
||||
off_t pv_getnum_size(const char *str, bool decimal_units, /*@null@ */ long double *resultptr)
|
||||
{
|
||||
off_t integral_part = 0;
|
||||
off_t fractional_part = 0;
|
||||
@@ -39,8 +42,11 @@ off_t pv_getnum_size(const char *str, bool decimal_units)
|
||||
off_t decimal_multiplier = 0;
|
||||
size_t readpos = 0;
|
||||
|
||||
if (NULL == str)
|
||||
if (NULL == str) {
|
||||
if (NULL != resultptr)
|
||||
*resultptr = 0.0;
|
||||
return (off_t) 0;
|
||||
}
|
||||
|
||||
/* Skip any non-numeric leading characters. */
|
||||
while (str[readpos] != '\0' && (!pv_isdigit(str[readpos])))
|
||||
@@ -156,10 +162,19 @@ off_t pv_getnum_size(const char *str, bool decimal_units)
|
||||
* Add the fractional part, divided by its divisor, to the integral
|
||||
* part, now that the multiplier for the units has been applied.
|
||||
*/
|
||||
if (NULL != resultptr) {
|
||||
*resultptr = ((long double) fractional_part) / (long double) fractional_divisor;
|
||||
*resultptr += (long double) integral_part;
|
||||
}
|
||||
fractional_part = fractional_part / fractional_divisor;
|
||||
integral_part += fractional_part;
|
||||
|
||||
debug("%s [%s] = %lld", str, decimal_units ? "decimal" : "binary", (long long) integral_part);
|
||||
if (NULL != resultptr) {
|
||||
debug("%s [%s] = %lld = %Lf", str, decimal_units ? "decimal" : "binary", (long long) integral_part,
|
||||
*resultptr);
|
||||
} else {
|
||||
debug("%s [%s] = %lld", str, decimal_units ? "decimal" : "binary", (long long) integral_part);
|
||||
}
|
||||
|
||||
return integral_part;
|
||||
}
|
||||
@@ -211,7 +226,7 @@ double pv_getnum_interval(const char *str)
|
||||
*/
|
||||
unsigned int pv_getnum_count(const char *str, bool decimal_units)
|
||||
{
|
||||
return (unsigned int) pv_getnum_size(str, decimal_units);
|
||||
return (unsigned int) pv_getnum_size(str, decimal_units, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-3
@@ -34,7 +34,7 @@ struct remote_msg {
|
||||
bool bytes; /* bytes transferred flag */
|
||||
bool bufpercent; /* transfer buffer percentage flag */
|
||||
size_t lastwritten; /* last-written bytes count */
|
||||
off_t rate_limit; /* rate limit, in bytes per second */
|
||||
long double rate_limit; /* rate limit, in bytes per second */
|
||||
size_t buffer_size; /* buffer size, in bytes (0=default) */
|
||||
off_t size; /* total size of data */
|
||||
double interval; /* interval between updates */
|
||||
@@ -42,6 +42,7 @@ struct remote_msg {
|
||||
unsigned int height; /* screen height */
|
||||
bool width_set_manually; /* width was set manually, not detected */
|
||||
bool height_set_manually; /* height was set manually, not detected */
|
||||
bool rate_limit_active; /* whether rate limiting is in effect */
|
||||
char name[256]; /* flawfinder: ignore */
|
||||
char format[256]; /* flawfinder: ignore */
|
||||
char extra_display[256]; /* flawfinder: ignore */
|
||||
@@ -106,6 +107,7 @@ int pv_remote_set(pvstate_t state, pid_t remote)
|
||||
msgbuf.bufpercent = state->control.format_option.bufpercent;
|
||||
msgbuf.lastwritten = state->control.format_option.lastwritten;
|
||||
msgbuf.rate_limit = state->control.rate_limit;
|
||||
msgbuf.rate_limit_active = state->control.rate_limit_active;
|
||||
msgbuf.buffer_size = state->control.target_buffer_size;
|
||||
msgbuf.size = state->control.size;
|
||||
msgbuf.interval = state->control.interval;
|
||||
@@ -322,8 +324,7 @@ static bool pv__rxsignal_usr2(pvstate_t state)
|
||||
|
||||
pv_state_set_format_options(state, format_options);
|
||||
|
||||
if (msgbuf.rate_limit > 0)
|
||||
pv_state_rate_limit_set(state, msgbuf.rate_limit);
|
||||
pv_state_rate_limit_set(state, msgbuf.rate_limit, msgbuf.rate_limit_active);
|
||||
if (msgbuf.buffer_size > 0) {
|
||||
pv_state_target_buffer_size_set(state, msgbuf.buffer_size);
|
||||
}
|
||||
|
||||
+2
-1
@@ -649,9 +649,10 @@ void pv_state_discard_input_set(pvstate_t state, bool val)
|
||||
state->control.discard_input = val;
|
||||
}
|
||||
|
||||
void pv_state_rate_limit_set(pvstate_t state, off_t val)
|
||||
void pv_state_rate_limit_set(pvstate_t state, long double val, bool is_active)
|
||||
{
|
||||
state->control.rate_limit = val;
|
||||
state->control.rate_limit_active = is_active;
|
||||
}
|
||||
|
||||
void pv_state_target_buffer_size_set(pvstate_t state, size_t val)
|
||||
|
||||
+11
-11
@@ -300,11 +300,11 @@ static ssize_t pv__transfer__write_repeated(int fd, char *buf, size_t count, boo
|
||||
*
|
||||
* At most, the number of bytes read will be the number of bytes remaining
|
||||
* in the input buffer, capped to the number of bytes left until
|
||||
* state->control.size is reached if state->control.stop_at_size is true.
|
||||
* If state->control.rate_limit is >0, and/or "max_to_write" is >0, and
|
||||
* splice() is used, then the maximum number of bytes read will be further
|
||||
* capped to the value of "max_to_write", since splice() writes as well as
|
||||
* reads.
|
||||
* state->control.size is reached if state->control.stop_at_size is true.
|
||||
* If state->control.rate_limit_active is true, and/or "max_to_write" is >0,
|
||||
* and splice() is used, then the maximum number of bytes read will be
|
||||
* further capped to the value of "max_to_write", since splice() writes as
|
||||
* well as reads.
|
||||
*
|
||||
* If splice() was successfully used, sets state->transfer.splice_used to
|
||||
* true; if it was unsuccessfully used, then
|
||||
@@ -380,7 +380,7 @@ static bool pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_o
|
||||
&& (0 == state->transfer.to_write)) {
|
||||
size_t bytes_to_splice;
|
||||
|
||||
if (state->control.rate_limit > 0 || max_to_write != 0) {
|
||||
if (state->control.rate_limit_active || max_to_write != 0) {
|
||||
bytes_to_splice = (size_t) max_to_write;
|
||||
} else {
|
||||
bytes_to_splice = bytes_can_read;
|
||||
@@ -1163,10 +1163,10 @@ static char *pv__allocate_aligned_buffer(int outfd, int infd, size_t target_size
|
||||
|
||||
/*
|
||||
* Transfer some data from "fd" to standard output, timing out after 9/100
|
||||
* of a second. If state->control.rate_limit is >0, and/or "allowed" is >0,
|
||||
* only up to "allowed" bytes can be written. The variables that "eof_in"
|
||||
* and "eof_out" point to are used to flag that we've finished reading and
|
||||
* writing respectively.
|
||||
* of a second. If state->control.rate_limit_active is true, and/or
|
||||
* "allowed" is >0, only up to "allowed" bytes can be written. The
|
||||
* variables that "eof_in" and "eof_out" point to are used to flag that
|
||||
* we've finished reading and writing respectively.
|
||||
*
|
||||
* Returns the number of bytes written, or negative on error (in which case
|
||||
* state->status.exit_status is updated). In line mode, the number of lines
|
||||
@@ -1293,7 +1293,7 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t
|
||||
* limiting is active or if "allowed" is > 0.
|
||||
*/
|
||||
state->transfer.to_write = (ssize_t) (state->transfer.read_position - state->transfer.write_position);
|
||||
if ((state->control.rate_limit > 0) || (allowed > 0)) {
|
||||
if ((state->control.rate_limit_active) || (allowed > 0)) {
|
||||
if ((off_t) (state->transfer.to_write) > allowed) {
|
||||
state->transfer.to_write = (ssize_t) allowed;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user