Make it much more understandable, and add comments to it.
This is in order to prepare similar function for writev_readv().

The new implementation has been extensively tested by
splitting a large buffer into many small randomly-sized
chunks, sending it over socket to another, slow process
and verifying the receiving data is the same.

Signed-off-by: Michael Tokarev <m...@tls.msk.ru>
---
 cutils.c      |  119 +++++++++++++++++++++++++++++---------------------------
 qemu-common.h |    2 +-
 2 files changed, 63 insertions(+), 58 deletions(-)

diff --git a/cutils.c b/cutils.c
index 6d9175f..e77bbda 100644
--- a/cutils.c
+++ b/cutils.c
@@ -426,83 +426,88 @@ int qemu_parse_fd(const char *param)
  * The first `offset' bytes in the iovec buffer are skipped and next
  * `bytes' bytes are used.
  */
-int qemu_sendv_recvv(int sockfd, struct iovec *iov, size_t bytes, size_t 
offset,
-                    bool do_sendv)
+int qemu_sendv_recvv(int sockfd, struct iovec *iov,
+                    size_t bytes, size_t offset, bool do_send)
 {
-    int ret, iovlen;
-    size_t diff;
-    struct iovec *last_iov;
-
-    /* last_iov is inclusive, so count from one.  */
-    iovlen = 1;
-    last_iov = iov;
-    bytes += offset;
-
-    while (last_iov->iov_len < bytes) {
-        bytes -= last_iov->iov_len;
-
-        last_iov++;
-        iovlen++;
+    int ret;
+    struct iovec *end;
+
+    /* Find the start position, skipping `offset' bytes:
+     * first, skip all full-sized vector elements, */
+    while(offset >= iov->iov_len) {
+       offset -= iov->iov_len;
+       ++iov;
     }
-
-    diff = last_iov->iov_len - bytes;
-    last_iov->iov_len -= diff;
-
-    while (iov->iov_len <= offset) {
-        offset -= iov->iov_len;
-
-        iov++;
-        iovlen--;
+    if (offset) {
+       /* second, skip `offset' bytes from the (now) first element,
+        * undo it on exit */
+       iov->iov_base += offset;
+       iov->iov_len -= offset;
+    }
+    /* Find the end position skipping `bytes' bytes: */
+    /* first, skip all full-sized elements */
+    end = iov;
+    while(end->iov_len <= bytes) {
+       bytes -= end->iov_len;
+       ++end;
+    }
+    if (bytes) {
+       /* second, fixup the last element, and remember
+        * the length we've cut from the end of it in `bytes' */
+       size_t tail = end->iov_len - bytes;
+       end->iov_len = bytes;
+       ++end;
+       bytes = tail;
     }
-
-    iov->iov_base = (char *) iov->iov_base + offset;
-    iov->iov_len -= offset;
 
     {
 #if defined CONFIG_IOVEC && defined CONFIG_POSIX
         struct msghdr msg;
         memset(&msg, 0, sizeof(msg));
         msg.msg_iov = iov;
-        msg.msg_iovlen = iovlen;
-
+        msg.msg_iovlen = end - iov;;
         do {
-            if (do_sendv) {
-                ret = sendmsg(sockfd, &msg, 0);
-            } else {
-                ret = recvmsg(sockfd, &msg, 0);
-            }
-        } while (ret == -1 && errno == EINTR);
+           ret = do_send
+               ? sendmsg(sockfd, &msg, 0)
+                : recvmsg(sockfd, &msg, 0);
+        } while (ret < 0 && errno == EINTR);
 #else
-        struct iovec *p = iov;
+       /* else send piece-by-piece */
+       /*XXX Note: windows has WSASend() and WSARecv() */
+       struct iovec *p = iov;
         ret = 0;
-        while (iovlen > 0) {
-            int rc;
-            if (do_sendv) {
-                rc = send(sockfd, p->iov_base, p->iov_len, 0);
-            } else {
-                rc = qemu_recv(sockfd, p->iov_base, p->iov_len, 0);
-            }
-            if (rc == -1) {
-                if (errno == EINTR) {
-                    continue;
-                }
+       while(p < end) {
+           int r = do_send
+               ? send(sockfd, p->iov_base, p->iov_len, 0)
+               : qemu_recv(sockfd, p->iov_base, p->iov_len, 0);
+           if (r > 0) {
+               ret += r;
+               ++i;
+           } else if (!r) {
+               break;
+           } else if (errno == EINTR) {
+               continue;
+           } else {
+               /* else it is some "other" error,
+                * only return if there was no data processed. */
                 if (ret == 0) {
                     ret = -1;
                 }
                 break;
-            }
-            if (rc == 0) {
-                break;
-            }
-            ret += rc;
-            iovlen--, p++;
+           }
         }
 #endif
     }
 
     /* Undo the changes above */
-    iov->iov_base = (char *) iov->iov_base - offset;
-    iov->iov_len += offset;
-    last_iov->iov_len += diff;
+    if (offset) {
+       iov->iov_base -= offset;
+       iov->iov_len += offset;
+    }
+    if (bytes) {
+       --end;
+       end->iov_len += bytes;
+    }
+
     return ret;
 }
diff --git a/qemu-common.h b/qemu-common.h
index 2bf630d..17d5321 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -206,7 +206,7 @@ int qemu_pipe(int pipefd[2]);
  * the beginning of iovec but at byte position `offset'.
  */
 int qemu_sendv_recvv(int sockfd, struct iovec *iov,
-                    size_t bytes, size_t offset, bool do_sendv);
+                    size_t bytes, size_t offset, bool do_send);
 #define qemu_recvv(sockfd, iov, bytes, offset) \
   qemu_sendv_recvv(sockfd, iov, bytes, offset, false)
 #define qemu_sendv(sockfd, iov, bytes, offset) \
-- 
1.7.9.1


Reply via email to