]> Zhao Yanbai Git Server - minix.git/commitdiff
Implementations of readv and writev
authorErik van der Kouwe <erik@minix3.org>
Fri, 8 Jan 2010 13:40:34 +0000 (13:40 +0000)
committerErik van der Kouwe <erik@minix3.org>
Fri, 8 Jan 2010 13:40:34 +0000 (13:40 +0000)
include/limits.h
include/sys/uio.h
lib/other/Makefile.in
lib/other/vectorio.c [new file with mode: 0644]
lib/other/writev.c [deleted file]
man/man3/readv.3 [new file with mode: 0644]
test/test18.c

index 3fdc11d667eff05da9339828abd9ea2cdd009e72..60503af3ff414deb287209288c17617024dbd56e 100644 (file)
                                 * 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 */
index 0d4303f07bb329219fdf55b0975ef58340c990a4..d4dba19a3878b09c9eb5638ef73478bb992ba5fd 100644 (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 */
index bab340a1a9a195a0a33595869ee66fb41461ed46..dcb5032eb463649dc9e11dbf6e2891ee3b735ec7 100644 (file)
@@ -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 (file)
index 0000000..20d0074
--- /dev/null
@@ -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);   
+}
+
diff --git a/lib/other/writev.c b/lib/other/writev.c
deleted file mode 100644 (file)
index 9bbf9a6..0000000
+++ /dev/null
@@ -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
-}
diff --git a/man/man3/readv.3 b/man/man3/readv.3
new file mode 100644 (file)
index 0000000..073d55d
--- /dev/null
@@ -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)
index 67224b76651ff965ebc03f99cade1fb925772746..17fbf4aa05b78d551ac61aeda5df37589dd65447 100644 (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                         *
  **********************************************************************/