delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp-workers/2003/01/22/07:55:01

Date: Wed, 22 Jan 2003 12:52:20 +0000
From: "Richard Dawe" <rich AT phekda DOT freeserve DOT co DOT uk>
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: <E18bKL9-0000WW-00@phekda.freeserve.co.uk>
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 <sys/djtypes.h>
+ 
+ #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 <libc/stubs.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <errno.h>
+ #include <limits.h>
+ #include <sys/uio.h>
+ 
+ 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 <sys/uio.h>
+ 
+ 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 <libc/stubs.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <errno.h>
+ #include <limits.h>
+ #include <sys/uio.h>
+ 
+ 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 <sys/uio.h>
+ 
+ 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 <assert.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <sys/uio.h>
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ 
+ #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 <assert.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <sys/uio.h>
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <libc/unconst.h>
+ 
+ #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

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019