Date: Wed, 22 Jan 2003 12:52:20 +0000 From: "Richard Dawe" Sender: rich AT phekda DOT freeserve DOT co DOT uk To: djgpp-workers AT delorie DOT com X-Mailer: Emacs 21.3.50 (via feedmail 8.3.emacs20_6 I) and Blat ver 1.8.6 Subject: readv, writev [PATCH] Message-Id: Reply-To: djgpp-workers AT delorie DOT com Hello. I wrote an implementation of readv & writev for libsocket a few years ago. I've tidied it up, written a test program, et voila`, here it is. The calls allocate a buffer big enough to store all the data for the I/O vector, so that a single read or write call can be used. This seemed like the simplest and most robust way of doing it, since there are no readv or writev system calls. OK to commit? Bye, Rich =] *** /dev/null Wed Jan 22 12:51:39 2003 --- include/sys/uio.h Wed Jan 22 12:05:12 2003 *************** *** 0 **** --- 1,49 ---- + /* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */ + #ifndef __dj_include_sys_uio_h__ + #define __dj_include_sys_uio_h__ + + #ifdef __cplusplus + extern "C" + { + #endif + + #ifndef __dj_ENFORCE_ANSI_FREESTANDING + + #include + + #ifndef __STRICT_ANSI__ + + #ifndef _SIZE_T + __DJ_size_t + #define _SIZE_T + #endif + + #ifndef _SSIZE_T + __DJ_ssize_t + #define _SSIZE_T + #endif + + #define IOV_MAX 16 + + struct iovec { + void *iov_base; /* Base address of a memory region for I/O */ + size_t iov_len; /* Size of memory region */ + }; + + extern ssize_t readv (int _fd, const struct iovec *_iov, int _iovcnt); + extern ssize_t writev (int _fd, const struct iovec *_iov, int _iovcnt); + + #ifndef _POSIX_SOURCE + + #endif /* !_POSIX_SOURCE */ + #endif /* !__STRICT_ANSI__ */ + #endif /* !__dj_ENFORCE_ANSI_FREESTANDING */ + + #ifndef __dj_ENFORCE_FUNCTION_CALLS + #endif /* !__dj_ENFORCE_FUNCTION_CALLS */ + + #ifdef __cplusplus + } + #endif + + #endif /* __dj_include_sys_uio_h__ */ *** /dev/null Wed Jan 22 12:51:39 2003 --- src/libc/posix/sys/uio/makefile Wed Jan 22 11:54:48 2003 *************** *** 0 **** --- 1,7 ---- + # Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details + TOP=../../.. + + SRC += readv.c + SRC += writev.c + + include $(TOP)/../makefile.inc *** /dev/null Wed Jan 22 12:51:39 2003 --- src/libc/posix/sys/uio/readv.c Wed Jan 22 12:12:24 2003 *************** *** 0 **** --- 1,73 ---- + /* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */ + #include + #include + #include + #include + #include + #include + #include + + ssize_t + readv (int fd, const struct iovec *iov, int iovcnt) + { + void *buf = NULL; + size_t maxbytes, nbytes, pos, remainder, len; + ssize_t ret; + int i; + + /* Check args */ + if ((iovcnt <= 0) || (iovcnt > IOV_MAX)) { + errno = EINVAL; + return(-1); + } + + /* Calculate total number of bytes that can be read. */ + for (maxbytes = 0, i = 0; i < iovcnt; i++) { + maxbytes += iov[i].iov_len; + if (maxbytes > SSIZE_MAX) + break; + } + + /* If we read the maximum number of bytes, we may overflow the return + * value. Return an error if this is the case. */ + if (maxbytes > SSIZE_MAX) { + errno = EINVAL; + return(-1); + } + + /* Allocate a buffer to store the data in. This buffer will be read + * in all at once. If the buffer is read all at once, we avoid + * the problem of dealing with an error from read() half-way through. */ + buf = malloc(maxbytes); + if (buf == NULL) { + errno = ENOMEM; + return(-1); + } + + /* Read in the data. */ + ret = read(fd, buf, maxbytes); + + /* Pass through any errors that occur. */ + if (ret < 0) { + free(buf); + return(ret); + } + + /* Slice'n'dice the data according to the iovecs. */ + nbytes = (size_t) ret; + + for (remainder = nbytes, pos = 0, i = 0; i < iovcnt; i++) { + /* If we have more data than we can store in this iovec, + * do a partial copy. Otherwise, copy the remaining data. */ + len = (iov[i].iov_len < remainder) ? iov[i].iov_len : remainder; + + memcpy(iov[i].iov_base, (char *) buf + pos, len); + pos += len; + remainder -= len; + } + + /* Tidy up */ + free(buf); + + return(nbytes); + } *** /dev/null Wed Jan 22 12:51:39 2003 --- src/libc/posix/sys/uio/readv.txh Wed Jan 22 12:51:12 2003 *************** *** 0 **** --- 1,52 ---- + @node readv, io + @findex readv + + @subheading Syntax + + @example + #include + + ssize_t readv(int fd, const struct iovec *iov, int iovcnt); + @end example + + @subheading Description + + @code{readv} performs a scatter-gather read from the specified file + descriptor @var{fd}. The data is written into a group of buffers described + by the array @var{iov} with @var{iovcnt} entries in a similar way to + @code{read} (@pxref{read, , read, libc}). + + @code{struct iovec} is defined as follows: + + @example + struct iovec @{ + void *iov_base; /* Base address of a memory region for I/O */ + size_t iov_len; /* Size of memory region */ + @}; + @end example + + @subheading Return Value + + On successful completion the function returns the number of bytes read. + Otherwise, a value of -1 is returned and @var{errno} is set appropriately. + + @table @samp + + @item EINVAL + One of the following conditions is true: + + @itemize @bullet + @item + The total length to read could overflow a @code{ssize_t}. + @item + @var{iovcnt} was negative, zero or larger than @code{IOV_MAX}. + @end itemize + + @item ENOMEM + There was not enough free memory, to allocate temporary buffers. + + @end table + + @subheading Portability + + @portability posix *** /dev/null Wed Jan 22 12:51:39 2003 --- src/libc/posix/sys/uio/writev.c Wed Jan 22 12:11:58 2003 *************** *** 0 **** --- 1,58 ---- + /* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */ + #include + #include + #include + #include + #include + #include + #include + + ssize_t + writev (int fd, const struct iovec *iov, int iovcnt) + { + void *buf = NULL; + size_t nbytes, pos; + ssize_t ret; + int i; + + /* Check args */ + if ((iovcnt <= 0) || (iovcnt > IOV_MAX)) { + errno = EINVAL; + return(-1); + } + + /* Calculate the total number of bytes needed to write data. */ + for (nbytes = 0, i = 0; i < iovcnt; i++) { + nbytes += iov[i].iov_len; + if (nbytes > SSIZE_MAX) + break; + } + + /* If we write the maximum number of bytes, we may overflow the return + * value. Return an error if this is the case. */ + if (nbytes > SSIZE_MAX) { + errno = EINVAL; + return(-1); + } + + /* Allocate a buffer to store data in. This buffer will be written + * out all at once. If the buffer is written all at once, we avoid + * the problem of dealing with an error from write() half-way through. */ + buf = malloc(nbytes); + if (buf == NULL) { + errno = ENOMEM; + return(-1); + } + + /* Copy all the data into the buffer. */ + for (pos = 0, i = 0; i < iovcnt; i++) { + memcpy((char *) buf + pos, iov[i].iov_base, iov[i].iov_len); + pos += iov[i].iov_len; + } + + /* Write and free the buffer. */ + ret = write(fd, buf, nbytes); + free(buf); + + return(ret); + } *** /dev/null Wed Jan 22 12:51:39 2003 --- src/libc/posix/sys/uio/writev.txh Wed Jan 22 12:51:08 2003 *************** *** 0 **** --- 1,52 ---- + @node writev, io + @findex writev + + @subheading Syntax + + @example + #include + + ssize_t writev(int fd, const struct iovec *iov, int iovcnt); + @end example + + @subheading Description + + @code{writev} performs a scatter-gather write to the specified file + descriptor @var{fd}. A group of buffers described by the array @var{iov}, + with @var{iovcnt} entries, is written to @var{fd} in a similar + way to @code{write} (@pxref{write, , write, libc}). + + @code{struct iovec} is defined as follows: + + @example + struct iovec @{ + void *iov_base; /* Base address of a memory region for I/O */ + size_t iov_len; /* Size of memory region */ + @}; + @end example + + @subheading Return Value + + On successful completion the function returns the number of bytes written. + Otherwise, a value of -1 is returned and @var{errno} is set appropriately. + + @table @samp + + @item EINVAL + One of the following conditions is true: + + @itemize @bullet + @item + The total length to write would overflow a @code{ssize_t}. + @item + @var{iovcnt} was negative, zero or larger than @code{IOV_MAX}. + @end itemize + + @item ENOMEM + There was not enough free memory, to allocate temporary buffers. + + @end table + + @subheading Portability + + @portability posix *** /dev/null Wed Jan 22 12:51:39 2003 --- tests/libc/posix/sys/uio/makefile Wed Jan 22 12:16:14 2003 *************** *** 0 **** --- 1,6 ---- + TOP=../../.. + + SRC += t-readv.c + SRC += t-writev.c + + include $(TOP)/../makefile.inc *** /dev/null Wed Jan 22 12:51:39 2003 --- tests/libc/posix/sys/uio/t-readv.c Wed Jan 22 12:41:40 2003 *************** *** 0 **** --- 1,80 ---- + #include + #include + #include + #include + #include + #include + #include + #include + + #define DATA_FILENAME "t-readv.dat" + + void + fail (const char *argv0) + { + perror(argv0); + puts("FAIL"); + exit(EXIT_FAILURE); + } + + int + main (int argc, char *argv[]) + { + char data[] = "somedata"; + char bufs[5][4]; + const size_t n_bufs = 5; + struct iovec iov[IOV_MAX]; + char *p = NULL; + char allbuf[128]; + int fd; + int ret; + int i; + + assert(n_bufs <= IOV_MAX); + + /* Construct the iovec. */ + iov[0].iov_base = (void *) bufs[0]; + iov[0].iov_len = 4; + + for (i = 1; i < 5; i++) { + iov[i].iov_base = (void *) bufs[i]; + iov[i].iov_len = 1; + } + + /* Create the data file. */ + fd = open(DATA_FILENAME, O_RDWR|O_TEXT|O_TRUNC|O_CREAT, S_IRUSR|S_IWUSR); + + if (fd < 0) + fail(argv[0]); + + /* Don't write the nul to the file. */ + ret = write(fd, data, strlen(data)); + + close(fd); + + /* Read the data back in. */ + fd = open(DATA_FILENAME, O_RDONLY|O_TEXT); + + if (fd < 0) + fail(argv[0]); + + ret = readv(fd, iov, n_bufs); + if (ret < 0) + fail(argv[0]); + + close(fd); + + /* Reconstruct the string in bufs. */ + for (p = allbuf, i = 0; i < n_bufs; p += iov[i].iov_len, i++) { + memcpy(p, bufs[i], iov[i].iov_len); + } + *p = '\0'; + + if (strcmp(allbuf, data) != 0) { + printf("Expected '%s' - got '%s'\nFAIL\n", data, allbuf); + return(EXIT_FAILURE); + } + + puts("PASS"); + return(EXIT_SUCCESS); + } *** /dev/null Wed Jan 22 12:51:39 2003 --- tests/libc/posix/sys/uio/t-writev.c Wed Jan 22 12:38:30 2003 *************** *** 0 **** --- 1,76 ---- + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #define DATA_FILENAME "t-writev.dat" + + void + fail (const char *argv0) + { + perror(argv0); + puts("FAIL"); + exit(EXIT_FAILURE); + } + + int + main (int argc, char *argv[]) + { + const char *data[] = { "some", "d", "a", "t", "a" }; + /* NB: data[0] is a pointer. */ + const size_t n_data = sizeof(data) / sizeof(data[0]); + struct iovec iov[IOV_MAX]; + char alldata[128]; + int fd; + int ret; + char buf[128]; + int i; + + assert(n_data <= IOV_MAX); + + /* Construct the iovec & collapse data into one string. */ + alldata[0] = '\0'; + for (i = 0; i < n_data; i++) { + iov[i].iov_base = (void *) unconst(data[i], char *); + iov[i].iov_len = strlen(data[i]); + + strcat(alldata, data[i]); + } + + /* Write out the data. */ + fd = open(DATA_FILENAME, O_RDWR|O_TEXT|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); + if (fd < 0) + fail(argv[0]); + + ret = writev(fd, iov, n_data); + if (ret < 0) + fail(argv[0]); + + close(fd); + + /* Read back the data and check it. */ + fd = open(DATA_FILENAME, O_RDONLY|O_TEXT); + if (fd < 0) + fail(argv[0]); + + ret = read(fd, buf, sizeof(buf) - 1 /* leave space for nul */); + if (ret < 0) + fail(argv[0]); + + close(fd); + + buf[ret] = '\0'; + + if (strcmp(buf, alldata) != 0) { + printf("Expected '%s' - got '%s'\nFAIL\n", alldata, buf); + return(EXIT_FAILURE); + } + + puts("PASS"); + return(EXIT_SUCCESS); + } *** /dev/null Wed Jan 22 12:51:39 2003 --- tests/libc/posix/sys/uio/.cvsignore Wed Jan 22 12:15:30 2003 *************** *** 0 **** --- 1 ---- + *.d