From f025e5f06b13fe092991326173fc54336c9b8f11 Mon Sep 17 00:00:00 2001 From: Erik van der Kouwe Date: Fri, 8 Jan 2010 13:40:34 +0000 Subject: [PATCH] Implementations of readv and writev --- include/limits.h | 2 +- include/sys/uio.h | 2 - lib/other/Makefile.in | 2 +- lib/other/vectorio.c | 128 +++++++++++++++++++++++++++++++++++++++++ lib/other/writev.c | 39 ------------- man/man3/readv.3 | 28 +++++++++ test/test18.c | 129 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 287 insertions(+), 43 deletions(-) create mode 100644 lib/other/vectorio.c delete mode 100644 lib/other/writev.c create mode 100644 man/man3/readv.3 diff --git a/include/limits.h b/include/limits.h index 3fdc11d66..60503af3f 100644 --- a/include/limits.h +++ b/include/limits.h @@ -104,7 +104,7 @@ * be reliably traversed in the resolution of * a pathname in the absence of a loop. */ - +#define IOV_MAX INT_MAX /* maximum number of buffers for readv/writev */ #endif /* _POSIX_SOURCE */ #endif /* _LIMITS_H */ diff --git a/include/sys/uio.h b/include/sys/uio.h index 0d4303f07..d4dba19a3 100644 --- a/include/sys/uio.h +++ b/include/sys/uio.h @@ -15,11 +15,9 @@ struct iovec size_t iov_len; }; -#if 0 _PROTOTYPE(ssize_t readv, (int _fildes, const struct iovec *_iov, int _iovcnt) ); _PROTOTYPE(ssize_t writev, (int _fildes, const struct iovec *_iov, int iovcnt) ); -#endif #endif /* _SYS_UIO_H */ diff --git a/lib/other/Makefile.in b/lib/other/Makefile.in index bab340a1a..dcb5032eb 100644 --- a/lib/other/Makefile.in +++ b/lib/other/Makefile.in @@ -100,6 +100,6 @@ libc_FILES=" \ v8regerror.c \ v8regexp.c \ v8regsub.c \ - writev.c" + vectorio.c" TYPE=both diff --git a/lib/other/vectorio.c b/lib/other/vectorio.c new file mode 100644 index 000000000..20d007464 --- /dev/null +++ b/lib/other/vectorio.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define VECTORIO_READ 1 +#define VECTORIO_WRITE 2 + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +static ssize_t vectorio_buffer(int fildes, const struct iovec *iov, + int iovcnt, int readwrite, ssize_t totallen) +{ + char *buffer; + int iovidx, errno_saved; + ssize_t copied, len, r; + + /* allocate buffer */ + buffer = (char *) malloc(totallen); + if (!buffer) + return -1; + + /* perform the actual read/write for the entire buffer */ + switch (readwrite) + { + case VECTORIO_READ: + /* first read, then copy buffers (only part read) */ + r = read(fildes, buffer, totallen); + + copied = 0; + iovidx = 0; + while (copied < r) + { + assert(iovidx < iovcnt); + len = MIN(r - copied, iov[iovidx].iov_len); + memcpy(iov[iovidx++].iov_base, buffer + copied, len); + copied += len; + } + assert(r < 0 || r == copied); + break; + + case VECTORIO_WRITE: + /* first copy buffers, then write */ + copied = 0; + for (iovidx = 0; iovidx < iovcnt; iovidx++) + { + memcpy(buffer + copied, iov[iovidx].iov_base, + iov[iovidx].iov_len); + copied += iov[iovidx].iov_len; + } + assert(copied == totallen); + + r = write(fildes, buffer, totallen); + break; + + default: + assert(0); + errno = EINVAL; + r = -1; + } + + /* free the buffer, keeping errno unchanged */ + errno_saved = errno; + free(buffer); + errno = errno_saved; + + return r; +} + +static ssize_t vectorio(int fildes, const struct iovec *iov, + int iovcnt, int readwrite) +{ + int i; + struct stat statbuf; + ssize_t totallen; + + /* parameter sanity checks */ + if (iovcnt > IOV_MAX) + { + errno = EINVAL; + return -1; + } + + totallen = 0; + for (i = 0; i < iovcnt; i++) + { + /* don't read/write anything in case of possible overflow */ + if ((ssize_t) (totallen + iov[i].iov_len) < totallen) + { + errno = EINVAL; + return -1; + } + totallen += iov[i].iov_len; + + /* report on NULL pointers */ + if (iov[i].iov_len && !iov[i].iov_base) + { + errno = EFAULT; + return -1; + } + } + + /* anything to do? */ + if (totallen == 0) + return 0; + + /* + * there aught to be a system call here; instead we use an intermediate + * buffer; this is preferred over multiple read/write calls because + * this function has to be atomic + */ + return vectorio_buffer(fildes, iov, iovcnt, readwrite, totallen); +} + +ssize_t readv(int fildes, const struct iovec *iov, int iovcnt) +{ + return vectorio(fildes, iov, iovcnt, VECTORIO_READ); +} + +ssize_t writev(int fildes, const struct iovec *iov, int iovcnt) +{ + return vectorio(fildes, iov, iovcnt, VECTORIO_WRITE); +} + diff --git a/lib/other/writev.c b/lib/other/writev.c deleted file mode 100644 index 9bbf9a683..000000000 --- a/lib/other/writev.c +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include - -ssize_t writev(int fildes, const struct iovec *iov, int iovcnt) -{ -#if DEBUG - fprintf(stderr, "bind: not implemented for fd %d\n", socket); -#endif - errno= ENOSYS; - return -1; - -#if 0 - int i, r; - char *p; - ssize_t l, sum; - - /* We should buffer */ - sum= 0; - for (i= 0; i 0) - { - r= write(fildes, p, l); - if (r <= 0) - { - assert(sum == 0); - return r; - } - p += r; - l -= r; - sum += r; - } - } - return sum; -#endif -} diff --git a/man/man3/readv.3 b/man/man3/readv.3 new file mode 100644 index 000000000..073d55de7 --- /dev/null +++ b/man/man3/readv.3 @@ -0,0 +1,28 @@ +.TH READV 3 "January 6, 2009" +.UC 4 +.SH NAME +readv, writev \- vector-based IO +.SH SYNOPSIS +.nf +.ft B +#include + +ssize_t readv(int \fIfildes\fP, const struct iovec *\fIiov\fP, int \fIiovcnt\fP); +ssize_t writev(int \fIfildes\fP, const struct iovec *\fIiov\fP, int \fIiovcnt\fP); +.fi +.SH DESCRIPTION +The \fBreadv\fP and \fBwritev\fP functions allow one to use multiple buffers +when reading from or writing to files. The \fIfildes\fP parameter specifies the +file descriptor as with the \fBread\fP and \fBwrite\fP functions. \fIiov\fP +specifies an array of buffers to be read into or written from. For each element +of this array, the iov_base member specifies the address of the buffer and +iov_len specifies its size in bytes. The number of buffers is specified by +\fIiovcnt\fP. At most IOV_MAX buffers may be specified and their total size may +not exceed SSIZE_MAX (both constants are defined in limits.h). +.SH "RETURN VALUE" +In case of success, the total number of bytes read or written is returned. +Zero may be returned if no buffers were specified or each buffer has size zero. +In the case of writev, a return value zero may also indicate an end of file +condition. If the functions fail, -1 is returned. +.SH "SEE ALSO" +read(2), write(2) diff --git a/test/test18.c b/test/test18.c index 67224b766..17fbf4aa0 100644 --- a/test/test18.c +++ b/test/test18.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #define NOCRASH 1 /* test11(), 2nd pipe */ #define PDPNOHANG 1 /* test03(), write_standards() */ @@ -45,7 +47,9 @@ #define UMASK "umask" #define CREAT "creat" #define WRITE "write" +#define WRITEV "writev" #define READ "read" +#define READV "readv" #define OPEN "open" #define CLOSE "close" #define LSEEK "lseek" @@ -92,6 +96,12 @@ _PROTOTYPE(void try_open, (char *fname, int mode, int test)); _PROTOTYPE(void test06, (void)); _PROTOTYPE(void test07, (void)); _PROTOTYPE(void access_standards, (void)); +_PROTOTYPE(void test08, (void)); +_PROTOTYPE(static int iovec_is_equal, + (struct iovec *x, struct iovec *y, size_t size)); +_PROTOTYPE(static size_t iovec_setup, + (int pattern, struct iovec *iovec, char *buffer, int count)); +_PROTOTYPE(static int power, (int base, int exponent)); _PROTOTYPE(void try_access, (char *fname, int mode, int test)); _PROTOTYPE(void e, (char *string)); _PROTOTYPE(void nlcr, (void)); @@ -159,6 +169,7 @@ void test() test05(); test06(); test07(); + test08(); umask(022); } /* test */ @@ -855,6 +866,124 @@ char *fname; close_alot, clean_up_the_mess. */ +/***************************************************************************** + * test READV/WRITEV * + ****************************************************************************/ +#define TEST8_BUFSZCOUNT 3 +#define TEST8_BUFSZMAX 65536 +#define TEST8_IOVCOUNT 4 + +void test08() +{ + char buffer_read[TEST8_IOVCOUNT * TEST8_BUFSZMAX]; + char buffer_write[TEST8_IOVCOUNT * TEST8_BUFSZMAX]; + struct iovec iovec_read[TEST8_IOVCOUNT]; + struct iovec iovec_write[TEST8_IOVCOUNT]; + int fd, i, j, k, l, m; + ssize_t sz_read, sz_write; + size_t sz_read_exp, sz_read_sum, sz_write_sum; + + /* try various combinations of buffer sizes */ + for (i = 0; i <= TEST8_IOVCOUNT; i++) + for (j = 0; j < power(TEST8_BUFSZCOUNT, i); j++) + for (k = 0; k <= TEST8_IOVCOUNT; k++) + for (l = 0; l < power(TEST8_BUFSZCOUNT, k); l++) + { + /* put data in the buffers */ + for (m = 0; m < sizeof(buffer_write); m++) + { + buffer_write[m] = m ^ (m >> 8); + buffer_read[m] = ~buffer_write[m]; + } + + /* set up the vectors to point to the buffers */ + sz_read_sum = iovec_setup(j, iovec_read, buffer_read, i); + sz_write_sum = iovec_setup(l, iovec_write, buffer_write, k); + sz_read_exp = (sz_read_sum < sz_write_sum) ? + sz_read_sum : sz_write_sum; + + /* test reading and writing */ + if ((fd = open("file08", O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) + err(13, OPEN, "'file08'"); + else { + sz_write = writev(fd, iovec_write, k); + if (sz_write != sz_write_sum) err(5, WRITEV, "'file08'"); + if (lseek(fd, 0, SEEK_SET) != 0) err(5, LSEEK, "'file08'"); + sz_read = readv(fd, iovec_read, i); + if (sz_read != sz_read_exp) + err(5, READV, "'file08'"); + else + { + if (!iovec_is_equal(iovec_read, iovec_write, sz_read)) + err(8, READV, "'file08'"); + } + + /* Remove testfile */ + Remove(fd, "file08"); + } + } +} /* test08 */ + +static int iovec_is_equal(struct iovec *x, struct iovec *y, size_t size) +{ + int xpos = 0, xvec = 0, ypos = 0, yvec = 0; + + /* compare byte by byte */ + while (size-- > 0) + { + /* skip over zero-byte buffers and those that have been completed */ + while (xpos >= x[xvec].iov_len) + { + xpos -= x[xvec++].iov_len; + assert(xvec < TEST8_IOVCOUNT); + } + while (ypos >= y[yvec].iov_len) + { + ypos -= y[yvec++].iov_len; + assert(yvec < TEST8_IOVCOUNT); + } + + /* compare */ + if (((char *) x[xvec].iov_base)[xpos++] != + ((char *) y[yvec].iov_base)[ypos++]) + return 0; + } + + /* no difference found */ + return 1; +} + +static size_t iovec_setup(int pattern, struct iovec *iovec, char *buffer, int count) +{ + static const size_t bufsizes[TEST8_BUFSZCOUNT] = { 0, 1, TEST8_BUFSZMAX }; + int i; + size_t sum = 0; + + /* the pattern specifies each buffer */ + for (i = 0; i < TEST8_IOVCOUNT; i++) + { + iovec->iov_base = buffer; + sum += iovec->iov_len = bufsizes[pattern % TEST8_BUFSZCOUNT]; + + iovec++; + buffer += TEST8_BUFSZMAX; + pattern /= TEST8_BUFSZCOUNT; + } + + return sum; +} + +static int power(int base, int exponent) +{ + int result = 1; + + /* compute base^exponent */ + while (exponent-- > 0) + result *= base; + + return result; +} + /*********************************************************************** * EXTENDED FIONS * **********************************************************************/