Commit f850a556 authored by Kenton Varda's avatar Kenton Varda

Fix bug when writing an array of buffers and the last buffer has zero size.

parent 24dc6ae7
...@@ -348,6 +348,7 @@ capnp_test_SOURCES = \ ...@@ -348,6 +348,7 @@ capnp_test_SOURCES = \
src/kj/tuple-test.c++ \ src/kj/tuple-test.c++ \
src/kj/one-of-test.c++ \ src/kj/one-of-test.c++ \
src/kj/function-test.c++ \ src/kj/function-test.c++ \
src/kj/io-test.c++ \
src/kj/mutex-test.c++ \ src/kj/mutex-test.c++ \
src/kj/threadlocal-test.c++ \ src/kj/threadlocal-test.c++ \
src/kj/threadlocal-pthread-test.c++ \ src/kj/threadlocal-pthread-test.c++ \
......
// Copyright (c) 2014, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "io.h"
#include "debug.h"
#include <gtest/gtest.h>
#include <unistd.h>
namespace kj {
namespace {
TEST(Io, WriteVec) {
// Check that writing an array of arrays works even when some of the arrays are empty. (This
// used to not work in some cases.)
int fds[2];
KJ_SYSCALL(pipe(fds));
FdInputStream in((AutoCloseFd(fds[0])));
FdOutputStream out((AutoCloseFd(fds[1])));
ArrayPtr<const byte> pieces[5] = {
arrayPtr(implicitCast<const byte*>(nullptr), 0),
arrayPtr(reinterpret_cast<const byte*>("foo"), 3),
arrayPtr(implicitCast<const byte*>(nullptr), 0),
arrayPtr(reinterpret_cast<const byte*>("bar"), 3),
arrayPtr(implicitCast<const byte*>(nullptr), 0)
};
out.write(pieces);
char buf[7];
in.read(buf, 6);
buf[6] = '\0';
EXPECT_STREQ("foobar", buf);
}
} // namespace
} // namespace kj
...@@ -301,21 +301,26 @@ void FdOutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) { ...@@ -301,21 +301,26 @@ void FdOutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) {
struct iovec* current = iov.begin(); struct iovec* current = iov.begin();
// Make sure we don't do anything on an empty write. // Advance past any leading empty buffers so that a write full of only empty buffers does not
// cause a syscall at all.
while (current < iov.end() && current->iov_len == 0) { while (current < iov.end() && current->iov_len == 0) {
++current; ++current;
} }
while (current < iov.end()) { while (current < iov.end()) {
// Issue the write.
ssize_t n = 0; ssize_t n = 0;
KJ_SYSCALL(n = ::writev(fd, current, iov.end() - current), fd); KJ_SYSCALL(n = ::writev(fd, current, iov.end() - current), fd);
KJ_ASSERT(n > 0, "writev() returned zero."); KJ_ASSERT(n > 0, "writev() returned zero.");
while (n > 0 && static_cast<size_t>(n) >= current->iov_len) { // Advance past all buffers that were fully-written.
while (current < iov.end() && static_cast<size_t>(n) >= current->iov_len) {
n -= current->iov_len; n -= current->iov_len;
++current; ++current;
} }
// If we only partially-wrote one of the buffers, adjust the pointer and size to include only
// the unwritten part.
if (n > 0) { if (n > 0) {
current->iov_base = reinterpret_cast<byte*>(current->iov_base) + n; current->iov_base = reinterpret_cast<byte*>(current->iov_base) + n;
current->iov_len -= n; current->iov_len -= n;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment