#include #include #include #include #include #include __inline long long tvToLL( struct timeval t ) { return t.tv_usec + (1000000LL * t.tv_sec); } __inline long long llmin( long long a, long long b ) { if ( a < b ) return a; return b; } long long interpBitsPerSecond( char* arg ) { char* pos; int toret = strtol( arg, &pos, 10 ); if ( (pos == arg) || (pos == NULL) ) { return -1; } if ( *pos == '\0' ) { return toret*8; } switch ( pos[0] ) { case 'k': case 'K': if ( pos[1] == 'b' ) { return toret * 1024; } else { return toret * 1024*8; } break; case 'm': case 'M': if ( pos[1] == 'b' ) { return toret * 1024*1024; } else { return toret * 1024*1024*8; } break; case 'g': case 'G': if ( pos[1] == 'b' ) { return toret * 1024*1024*1024; } else { return toret * 1024*1024*1024*8; } break; default: return toret; } } int main( int argc, char** argv ) { long long bps = 1024*8; int chunk = 1024; void* buffer; struct timespec tenth = { 0, 1000000000/30 }, toSleep, remainder; struct timeval nowtv; long long now, then; int err = 1; // bresenham long long epsilon = 0; long long total = 0; int i; int dryrun = 0; int verbose = 0; for ( i = 1; i < argc; i++ ) { if ( !strcmp( argv[i], "-d" ) ) { dryrun = 1; verbose = 1; } else if ( !strcmp( argv[i], "-v" ) ) { verbose = 1; } else if ( !strcmp( argv[i], "-r" ) ) { long long tbps; i++; tbps = interpBitsPerSecond( argv[i] ); if ( tbps > 0 ) { bps = tbps; } else { fprintf( stderr, "bogus rate \"%s\"\n", argv[i] ); exit(1); } } else if ( !strcmp( argv[i], "-c" ) ) { // set chunk size i++; chunk = atoi( argv[i] ); } else if ( !strcmp( argv[i], "-t" ) ) { // set tick rate i++; tenth.tv_nsec = 1000000000/atoi( argv[i] ); } else { long long tbps = interpBitsPerSecond( argv[i] ); if ( tbps > 0 ) { bps = tbps; } else { fprintf( stderr, "bogus arg \"%s\"\n", argv[i] ); exit(1); } } } if ( dryrun ) { err = chunk; } if ( verbose ) { fprintf(stderr, "chunk %d, rate %lld Bps\n", chunk, bps/8 ); } gettimeofday( &nowtv, NULL ); now = tvToLL( nowtv ); then = now; buffer = malloc( chunk ); while ( err > 0 ) { if ( (epsilon > 0) && (!dryrun) ) { err = read( STDIN_FILENO, buffer, chunk ); if ( err < 0 ) { perror("read"); exit(1); } else if ( err > 0 ) { err = write( STDOUT_FILENO, buffer, err ); if ( err <= 0 ) { perror("write"); exit(1); } } else if ( verbose ) { fprintf(stderr,"read closes normally\n"); } } gettimeofday( &nowtv, NULL ); now = tvToLL( nowtv ); if ( epsilon > 0 ) { total += err; if ( dryrun && 0 ) { fprintf(stderr,"now %-15lld epsilon %-15lld\n", now/1000000, epsilon ); } if ( verbose ) { fprintf(stderr,"moved %9d bytes, total %lld\n", err, total ); } epsilon -= err * 8 * 1000000;//((now - then) * chunk) / 1000000; } /* Step by at most a tick. This caps max rate and doesn't let read stalls accrue bandwidth time to use. Also, doesn't let short sleeps run ticks too fast thus inflating bandwidth. */ epsilon += (llmin( (tenth.tv_nsec / 1000), now - then ) * bps); then = now; toSleep = tenth; nanosleep( &toSleep, &remainder ); } return 0; }