--- /dev/null
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#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);
+}
+
--- /dev/null
+.TH READV 3 "January 6, 2009"
+.UC 4
+.SH NAME
+readv, writev \- vector-based IO
+.SH SYNOPSIS
+.nf
+.ft B
+#include <sys/uio.h>
+
+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)
#include <string.h>
#include <stdio.h>
#include <limits.h>
+#include <assert.h>
+#include <sys/uio.h>
#define NOCRASH 1 /* test11(), 2nd pipe */
#define PDPNOHANG 1 /* test03(), write_standards() */
#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"
_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));
test05();
test06();
test07();
+ test08();
umask(022);
} /* test */
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 *
**********************************************************************/