Re:A Little Story About the `yes` Unix Command
by jwilk on Thursday October 12, 2017 @05:08AM
(#15456650)
Show us the source of your programs. :)
[ Parent
| on HN ]
Re:A Little Story About the `yes` Unix Command
by wahern on Thursday October 12, 2017 @04:06PM
(#15460895)
Here's a cleanup version:
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#include <errno.h> /* ERANGE errno */
#include <inttypes.h> /* strtoumax(3) */
#include <limits.h> /* LONG_MAX */
#include <stdint.h> /* uintmax_t */
#include <stdio.h> /* fflush(3) fprintf(3) fread(3) fwrite(3) tmpfile(3) */
#include <stdlib.h> /* EXIT_FAILURE */
#include <string.h> /* strlen(3) */
#include <err.h> /* err(3) errx(3) */
#include <fcntl.h> /* loff_t splice(2) */
#include <sys/time.h> /* struct timeval gettimeofday(2) */
#include <unistd.h> /* _SC_PAGESIZE pread(2) sysconf(3) write(2) */
#ifndef HAVE_SPLICE
#define HAVE_SPLICE __linux__
#endif
#define MIN(a, b) (((a) < (b))? (a) : (b))
#define MAX(a, b) (((a) > (b))? (a) : (b))
#ifndef howmany
#define howmany(x, y) (((x) + ((y) - 1)) / (y))
#endif
#define UMAX_PREC (sizeof (uintmax_t) * CHAR_BIT) /* NB: assumes no padding */
#define UMAX_HALF ((UMAX_PREC + 1) / 2)
#define UMAX_LO(n) ((n) & ((UINTMAX_C(1) << UMAX_HALF) - 1))
#define UMAX_HI(n) ((n) >> UMAX_HALF)
static inline _Bool
add_overflow(uintmax_t *r, const uintmax_t a, const uintmax_t b)
{
if (~a < b)
return 1;
*r = a + b;
return 0;
}
/*
* Implement multiplication using a polynomial with four multiplications and
* three additions, except we can optimize out some operations.
*/
static inline _Bool
mul_overflow(uintmax_t *_r, const uintmax_t _a, const uintmax_t _b)
{
uintmax_t a[2] = { UMAX_LO(_a), UMAX_HI(_a) };
uintmax_t b[2] = { UMAX_LO(_b), UMAX_HI(_b) };
uintmax_t r[2];
/* if both are non-0, we'd always overflow */
if (a[1] && b[1])
return 1;
/* either a[1] or b[1] must be 0 here, so no intermediate overflow */
r[1] = (a[1] * b[0]) + (a[0] * b[1]);
/* if the result has MSW bits set, we'd overflow */
if (UMAX_HI(r[1]))
return 1;
r[0] = a[0] * b[0];
return add_overflow(_r, r[0], r[1] << UMAX_HALF);
}
#if 0
static uintmax_t
add(uintmax_t a, uintmax_t b)
{
uintmax_t r;
if (add_overflow(&r, a, b))
errx(1, "arithmetic overflow (%ju * %ju)", a, b);
return r;
}
#endif
static uintmax_t
mul(uintmax_t a, uintmax_t b)
{
uintmax_t r;
if (mul_overflow(&r, a, b))
errx(1, "arithmetic overflow (%ju * %ju)", a, b);
return r;
}
static uintmax_t
toumax(const char *p)
{
char *pe;
uintmax_t n;
errno = 0;
if (UINTMAX_MAX == (n = strtoumax(p, &pe, 10)) && errno != 0)
err(1, "%s", p);
if (*pe != '\0' || p == pe)
errx(1, "%s: invalid number", p);
return n;
}
static uintmax_t
gcd(uintmax_t a, uintmax_t b)
{
uintmax_t c;
while (a != 0) {
c = a;
a = b % a;
b = c;
}
return b;
}
static uintmax_t
lcm(uintmax_t a, uintmax_t b)
{
return mul(a, b) / gcd(a, b);
}
static size_t
xgetpagesize(void)
{
long n = sysconf(_SC_PAGESIZE);
if (n <= 0)
err(1, "sysconf");
return n;
}
static double
gtod(void)
{
struct timeval now;
gettimeofday(&now, NULL);
return now.tv_sec + (now.tv_usec / 1000000.0);
}
int
main(int argc, char **argv)
{
_Bool cflag = 0, nflag = 0;
uintmax_t count = UINTMAX_MAX, lines = 0, total = 0;
int optc;
while (-1 != (optc = getopt(argc, argv, "c:n:"))) {
switch (optc) {
case 'c':
if (nflag)
errx(1, "-c and -n mutually exclusive");
count = toumax(optarg);
cflag = 1;
break;
case 'n':
if (cflag)
errx(1, "-c and -n mutually exclusive");
lines = toumax(optarg);
nflag = 1;
break;
default:
return EXIT_FAILURE;
}
}
argc -= optind;
argv += optind;
const char *const line = (argc > 0)? argv[0] : "y";
const size_t linesize = strlen(line) + 1;
const size_t pagesize = xgetpagesize();
const size_t blocksize = MAX(mul(pagesize, 16), lcm(linesize, pagesize));
const uintmax_t filesize = mul(blocksize, howmany((1UL << 20), blocksize));
if (nflag)
count = mul(linesize, lines);
// fprintf(stderr, "linesize: %zu\n", linesize);
// fprintf(stderr, "pagesize: %zu\n", xgetpagesize());
// fprintf(stderr, "blocksize: %ju\n", blocksize);
// fprintf(stderr, "filesize: %ju\n", filesize);
FILE *fh;
if (!(fh = tmpfile()))
err(1, "tmpfile");
for (size_t i = 0; i < filesize; i += linesize) {
int n = fprintf(fh, "%s\n", line);
if (n == -1)
err(1, "fprintf");
if ((size_t)n != linesize)
errx(1, "wrote %d bytes, expected %zu", n, linesize);
}
if (0 != fflush(fh))
err(1, "fflush");
double begin = gtod();
#if HAVE_SPLICE
int fd = fileno(fh);
_Bool eof = 0;
do {
loff_t p = 0;
size_t r;
ssize_t n;
/* NB: no LOFF_MAX available */
_Static_assert(sizeof p <= sizeof (long), "unexpected type for loff_t");
if (filesize > LONG_MAX)
errx(1, "filesize too large (%ju > %jd)", filesize, (intmax_t)LONG_MAX);
while ((size_t)p < filesize && count >= blocksize && !eof) {
r = MIN(blocksize, count);
r -= r % blocksize;
n = splice(fd, &p, STDOUT_FILENO, NULL, r, SPLICE_F_MOVE);
if (n == -1)
err(1, "splice");
p += n;
count -= n;
total += n;
eof = n == 0;
}
} while (!eof && (count || (!cflag && !nflag)));
#endif
{
char buf[BUFSIZ], *p, *pe;
size_t n;
while (count) {
rewind(fh);
if (!(n = fread(buf, 1, MIN(sizeof buf, count), fh))) {
if (ferror(fh))
err(1, "fread");
break;
}
p = buf;
pe = &buf[n];
while (p < pe) {
if (!(n = fwrite(p, 1, (size_t)(pe - p), stdout)))
err(1, "fwrite");
p += n;
count -= n;
total += n;
}
}
}
if (0 != fflush(stdout))
err(1, "fflush");
double elapsed = gtod() - begin;
fprintf(stderr, "%.3f GB/s (%zu bytes in %.3f seconds)\n", ((double)total / elapsed) / (1UL<<30), total, elapsed);
return 0;
}