Implementations of readv and writev

This commit is contained in:
Erik van der Kouwe 2010-01-08 13:40:34 +00:00
parent aec561acc5
commit f025e5f06b
7 changed files with 287 additions and 43 deletions

View file

@ -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 */

View file

@ -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 */

View file

@ -100,6 +100,6 @@ libc_FILES=" \
v8regerror.c \
v8regexp.c \
v8regsub.c \
writev.c"
vectorio.c"
TYPE=both

128
lib/other/vectorio.c Normal file
View file

@ -0,0 +1,128 @@
#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);
}

View file

@ -1,39 +0,0 @@
#include <errno.h>
#include <stdio.h>
#include <sys/uio.h>
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<iovcnt; i++)
{
p= iov[i].iov_base;
l= iov[i].iov_len;
while (l > 0)
{
r= write(fildes, p, l);
if (r <= 0)
{
assert(sum == 0);
return r;
}
p += r;
l -= r;
sum += r;
}
}
return sum;
#endif
}

28
man/man3/readv.3 Normal file
View file

@ -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 <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)

View file

@ -14,6 +14,8 @@
#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() */
@ -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 *
**********************************************************************/