Make it much more understandable, and add comments to it. 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 | 106 ++++++++++++++++++++++++++++++++------------------------------ 1 files changed, 55 insertions(+), 51 deletions(-) diff --git a/cutils.c b/cutils.c index be2173f..7a19874 100644 --- a/cutils.c +++ b/cutils.c @@ -396,81 +396,85 @@ ssize_t qemu_sendv_recvv(int sockfd, struct iovec *iov, size_t offset, size_t bytes, bool do_sendv) { - int iovlen; ssize_t ret; - size_t diff; - struct iovec *last_iov; + struct iovec *end; - /* 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++; - } - - diff = last_iov->iov_len - bytes; - last_iov->iov_len -= diff; - - while (iov->iov_len <= offset) { + /* Find the start position, skipping `offset' bytes: + * first, skip all full-sized vector elements, */ + while(offset >= iov->iov_len) { offset -= iov->iov_len; - - iov++; - iovlen--; + ++iov; + } + 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_sendv + ? sendmsg(sockfd, &msg, 0) + : recvmsg(sockfd, &msg, 0); + } while (ret < 0 && errno == EINTR); #else + /* 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); + while(p < end) { + int r = do_sendv + ? send(sockfd, p->iov_base, p->iov_len, 0) + : 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 { - rc = qemu_recv(sockfd, p->iov_base, p->iov_len, 0); - } - if (rc == -1) { - if (errno == EINTR) { - continue; - } + /* 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; } -- 1.7.9.1