Commit 178b5fdd authored by Kenton Varda's avatar Kenton Varda

Snappy serialization.

parent 2bcb6e69
......@@ -4,13 +4,13 @@ all:
echo "You probably accidentally told Eclipse to build. Stopping."
once:
CXX=g++-4.7 CXXFLAGS='-std=gnu++0x -O2 -Wall' LIBS='-lz -pthread' ekam -j6
CXX=g++-4.7 CXXFLAGS='-std=gnu++0x -O2 -DNDEBUG -Wall' LIBS='-lz -pthread' ekam -j6
continuous:
CXX=g++-4.7 CXXFLAGS='-std=gnu++0x -g -Wall' LIBS='-lz -pthread' ekam -j6 -c -n :51315
continuous-opt:
CXX=g++-4.7 CXXFLAGS='-std=gnu++0x -O2 -Wall' LIBS='-lz -pthread' ekam -j6 -c -n :51315
CXX=g++-4.7 CXXFLAGS='-std=gnu++0x -O2 -DNDEBUG -Wall' LIBS='-lz -pthread' ekam -j6 -c -n :51315
clean:
rm -rf bin lib tmp
......
// Copyright (c) 2013, 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 "message.h"
#include <gtest/gtest.h>
namespace capnproto {
namespace internal {
namespace {
TEST(Message, MallocBuilderWithFirstSegment) {
word scratch[16];
MallocMessageBuilder builder(arrayPtr(scratch, 16), AllocationStrategy::FIXED_SIZE);
ArrayPtr<word> segment = builder.allocateSegment(1);
EXPECT_EQ(scratch, segment.begin());
EXPECT_EQ(16u, segment.size());
segment = builder.allocateSegment(1);
EXPECT_NE(scratch, segment.begin());
EXPECT_EQ(16u, segment.size());
segment = builder.allocateSegment(1);
EXPECT_NE(scratch, segment.begin());
EXPECT_EQ(16u, segment.size());
}
// TODO: More tests.
} // namespace
} // namespace internal
} // namespace capnproto
// Copyright (c) 2013, 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 "test.capnp.h"
#include "serialize-snappy.h"
#include <gtest/gtest.h>
#include <string>
#include <stdlib.h>
#include "test-util.h"
namespace capnproto {
namespace internal {
namespace {
class TestMessageBuilder: public MallocMessageBuilder {
// A MessageBuilder that tries to allocate an exact number of total segments, by allocating
// minimum-size segments until it reaches the number, then allocating one large segment to
// finish.
public:
explicit TestMessageBuilder(uint desiredSegmentCount)
: MallocMessageBuilder(0, AllocationStrategy::FIXED_SIZE),
desiredSegmentCount(desiredSegmentCount) {}
~TestMessageBuilder() {
EXPECT_EQ(0u, desiredSegmentCount);
}
ArrayPtr<word> allocateSegment(uint minimumSize) override {
if (desiredSegmentCount <= 1) {
if (desiredSegmentCount < 1) {
ADD_FAILURE() << "Allocated more segments than desired.";
} else {
--desiredSegmentCount;
}
return MallocMessageBuilder::allocateSegment(SUGGESTED_FIRST_SEGMENT_WORDS);
} else {
--desiredSegmentCount;
return MallocMessageBuilder::allocateSegment(minimumSize);
}
}
private:
uint desiredSegmentCount;
};
class TestPipe: public InputStream, public OutputStream {
public:
TestPipe(bool lazy)
: lazy(lazy), readPos(0) {}
~TestPipe() {}
void write(const void* buffer, size_t size) override {
data.append(reinterpret_cast<const char*>(buffer), size);
}
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override {
CAPNPROTO_ASSERT(maxBytes <= data.size() - readPos, "Overran end of stream.");
size_t amount = lazy ? minBytes : maxBytes;
memcpy(buffer, data.data() + readPos, amount);
readPos += amount;
return amount;
}
private:
bool lazy;
std::string data;
std::string::size_type readPos;
};
TEST(Snappy, RoundTrip) {
TestMessageBuilder builder(1);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(false);
writeSnappyMessage(pipe, builder);
SnappyMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Snappy, RoundTripScratchSpace) {
TestMessageBuilder builder(1);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(false);
writeSnappyMessage(pipe, builder);
word scratch[1024];
SnappyMessageReader reader(pipe, ReaderOptions(), ArrayPtr<word>(scratch, 1024));
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Snappy, RoundTripLazy) {
TestMessageBuilder builder(1);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(true);
writeSnappyMessage(pipe, builder);
SnappyMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Snappy, RoundTripOddSegmentCount) {
TestMessageBuilder builder(7);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(false);
writeSnappyMessage(pipe, builder);
SnappyMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Snappy, RoundTripOddSegmentCountLazy) {
TestMessageBuilder builder(7);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(true);
writeSnappyMessage(pipe, builder);
SnappyMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Snappy, RoundTripEvenSegmentCount) {
TestMessageBuilder builder(10);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(false);
writeSnappyMessage(pipe, builder);
SnappyMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Snappy, RoundTripEvenSegmentCountLazy) {
TestMessageBuilder builder(10);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(true);
writeSnappyMessage(pipe, builder);
SnappyMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Snappy, RoundTripTwoMessages) {
TestMessageBuilder builder(1);
initTestMessage(builder.initRoot<TestAllTypes>());
TestMessageBuilder builder2(1);
builder2.initRoot<TestAllTypes>().setTextField("Second message.");
TestPipe pipe(false);
writeSnappyMessage(pipe, builder);
writeSnappyMessage(pipe, builder2);
{
SnappyMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
{
SnappyMessageReader reader(pipe);
EXPECT_EQ("Second message.", reader.getRoot<TestAllTypes>().getTextField());
}
}
// TODO: Test error cases.
} // namespace
} // namespace internal
} // namespace capnproto
// Copyright (c) 2013, 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 "serialize-snappy.h"
#include "wire-format.h"
#include <snappy/snappy.h>
#include <snappy/snappy-sinksource.h>
#include <vector>
namespace capnproto {
namespace {
class InputStreamSource: public snappy::Source {
public:
inline InputStreamSource(InputStream& inputStream, size_t available)
: inputStream(inputStream), available(available), pos(nullptr), end(nullptr) {
// Read at least 10 bytes (or all available bytes if less than 10), and at most the whole buffer
// (unless less is available).
size_t firstSize = inputStream.read(buffer,
std::min<size_t>(available, 10),
std::min<size_t>(available, sizeof(buffer)));
CAPNPROTO_ASSERT(snappy::GetUncompressedLength(buffer, firstSize, &uncompressedSize),
"Invalid snappy-compressed data.");
pos = buffer;
end = pos + firstSize;
}
inline ~InputStreamSource() {};
inline size_t getUncompressedSize() { return uncompressedSize; }
// implements snappy::Source ---------------------------------------
size_t Available() const override {
return available;
}
const char* Peek(size_t* len) override {
*len = end - pos;
return pos;
}
void Skip(size_t n) override {
pos += n;
available -= n;
if (pos == end && available > 0) {
// Read more from the input.
pos = buffer;
end = pos + inputStream.read(buffer, 1, std::min<size_t>(available, sizeof(buffer)));
}
}
private:
InputStream& inputStream;
size_t available;
size_t uncompressedSize;
char* pos;
char* end;
char buffer[8192];
};
} // namespcae
SnappyMessageReader::SnappyMessageReader(
InputStream& inputStream, ReaderOptions options, ArrayPtr<word> scratchSpace)
: MessageReader(options), inputStream(inputStream) {
internal::WireValue<uint32_t> wireCompressedSize;
inputStream.read(&wireCompressedSize, sizeof(wireCompressedSize));
size_t compressedSize = wireCompressedSize.get();
InputStreamSource source(inputStream, compressedSize);
CAPNPROTO_ASSERT(source.getUncompressedSize() % sizeof(word) == 0,
"Uncompressed size was not a whole number of words.");
size_t uncompressedWords = source.getUncompressedSize() / sizeof(word);
if (scratchSpace.size() < uncompressedWords) {
space = newArray<word>(uncompressedWords);
scratchSpace = space;
}
CAPNPROTO_ASSERT(
snappy::RawUncompress(&source, reinterpret_cast<char*>(scratchSpace.begin())),
"Snappy decompression failed.");
new(&underlyingReader) FlatArrayMessageReader(scratchSpace, options);
}
SnappyMessageReader::~SnappyMessageReader() {
underlyingReader.~FlatArrayMessageReader();
}
ArrayPtr<const word> SnappyMessageReader::getSegment(uint id) {
return underlyingReader.getSegment(id);
}
SnappyFdMessageReader::~SnappyFdMessageReader() {}
// =======================================================================================
namespace {
class SegmentArraySource: public snappy::Source {
public:
SegmentArraySource(ArrayPtr<const ArrayPtr<const byte>> pieces)
: pieces(pieces), offset(0), available(0) {
for (auto& piece: pieces) {
available += piece.size();
}
Skip(0); // Skip leading zero-sized pieces, if any.
}
~SegmentArraySource() {}
// implements snappy::Source ---------------------------------------
size_t Available() const override {
return available;
}
const char* Peek(size_t* len) override {
if (pieces.size() == 0) {
*len = 0;
return nullptr;
} else {
*len = pieces[0].size() - offset;
return reinterpret_cast<const char*>(pieces[0].begin()) + offset;
}
}
void Skip(size_t n) override {
available -= n;
while (pieces.size() > 0 && n >= pieces[0].size() - offset) {
n -= pieces[0].size() - offset;
offset = 0;
pieces = pieces.slice(1, pieces.size());
}
offset += n;
}
private:
ArrayPtr<const ArrayPtr<const byte>> pieces;
size_t offset;
size_t available;
};
class AccumulatingSink: public snappy::Sink {
public:
AccumulatingSink()
: pos(firstPiece + sizeof(compressedSize)),
end(firstPiece + sizeof(firstPiece)), firstEnd(firstPiece) {}
~AccumulatingSink() {}
void writeTo(size_t size, OutputStream& output) {
finalizePiece();
compressedSize.set(size);
ArrayPtr<const byte> pieces[morePieces.size() + 1];
pieces[0] = arrayPtr(reinterpret_cast<byte*>(firstPiece),
reinterpret_cast<byte*>(firstEnd));
for (uint i = 0; i < morePieces.size(); i++) {
auto& piece = morePieces[i];
pieces[i + 1] = arrayPtr(reinterpret_cast<byte*>(piece.start.get()),
reinterpret_cast<byte*>(piece.end));
}
output.write(arrayPtr(pieces, morePieces.size() + 1));
}
// implements snappy::Sink -----------------------------------------
void Append(const char* bytes, size_t n) override {
if (bytes == pos) {
pos += n;
} else {
size_t a = available();
if (n > a) {
memcpy(pos, bytes, a);
bytes += a;
addPiece(n);
}
memcpy(pos, bytes, n);
pos += n;
}
}
char* GetAppendBuffer(size_t length, char* scratch) override {
if (length > available()) {
addPiece(length);
}
return pos;
}
private:
char* pos;
char* end;
// Stand and end point of the current available space.
char* firstEnd;
// End point of the used portion of the first piece.
struct Piece {
std::unique_ptr<char[]> start;
// Start point of the piece.
char* end;
// End point of the used portion of the piece.
Piece() = default;
Piece(std::unique_ptr<char[]> start, char* end): start(std::move(start)), end(end) {}
};
std::vector<Piece> morePieces;
union {
internal::WireValue<uint32_t> compressedSize;
char firstPiece[8192];
};
inline size_t available() {
return end - pos;
}
inline void finalizePiece() {
if (morePieces.empty()) {
firstEnd = pos;
} else {
morePieces.back().end = pos;
}
}
void addPiece(size_t minSize) {
finalizePiece();
std::unique_ptr<char[]> newPiece(new char[minSize]);
pos = newPiece.get();
end = pos + minSize;
morePieces.emplace_back(std::move(newPiece), pos);
}
};
class SnappyOutputStream: public OutputStream {
public:
SnappyOutputStream(OutputStream& output): output(output), sawWrite(false) {}
// implements OutputStream -----------------------------------------
void write(const void* buffer, size_t size) override {
CAPNPROTO_ASSERT(false, "writeMessage() was not expected to call this.");
}
void write(ArrayPtr<const ArrayPtr<const byte>> pieces) {
CAPNPROTO_ASSERT(!sawWrite, "writeMessage() was expected to issue exactly one write.");
sawWrite = true;
SegmentArraySource source(pieces);
AccumulatingSink sink;
size_t size = snappy::Compress(&source, &sink);
sink.writeTo(size, output);
}
private:
OutputStream& output;
bool sawWrite;
};
} // namespace
void writeSnappyMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> segments) {
SnappyOutputStream snappyOutput(output);
writeMessage(snappyOutput, segments);
}
void writeSnappyMessageToFd(int fd, ArrayPtr<const ArrayPtr<const word>> segments) {
FdOutputStream output(fd);
writeSnappyMessage(output, segments);
}
} // namespace capnproto
// Copyright (c) 2013, 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.
#ifndef CAPNPROTO_SERIALIZE_SNAPPY_H_
#define CAPNPROTO_SERIALIZE_SNAPPY_H_
#include "serialize.h"
namespace capnproto {
class SnappyMessageReader: public MessageReader {
public:
SnappyMessageReader(InputStream& inputStream, ReaderOptions options = ReaderOptions(),
ArrayPtr<word> scratchSpace = nullptr);
~SnappyMessageReader();
ArrayPtr<const word> getSegment(uint id) override;
private:
InputStream& inputStream;
Array<word> space;
union {
FlatArrayMessageReader underlyingReader;
};
};
class SnappyFdMessageReader: private FdInputStream, public SnappyMessageReader {
public:
SnappyFdMessageReader(int fd, ReaderOptions options = ReaderOptions(),
ArrayPtr<word> scratchSpace = nullptr)
: FdInputStream(fd), SnappyMessageReader(*this, options, scratchSpace) {}
// Read message from a file descriptor, without taking ownership of the descriptor.
SnappyFdMessageReader(AutoCloseFd fd, ReaderOptions options = ReaderOptions(),
ArrayPtr<word> scratchSpace = nullptr)
: FdInputStream(move(fd)), SnappyMessageReader(*this, options, scratchSpace) {}
// Read a message from a file descriptor, taking ownership of the descriptor.
~SnappyFdMessageReader();
};
void writeSnappyMessage(OutputStream& output, MessageBuilder& builder);
void writeSnappyMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> segments);
void writeSnappyMessageToFd(int fd, MessageBuilder& builder);
void writeSnappyMessageToFd(int fd, ArrayPtr<const ArrayPtr<const word>> segments);
// =======================================================================================
// inline stuff
inline void writeSnappyMessage(OutputStream& output, MessageBuilder& builder) {
writeSnappyMessage(output, builder.getSegmentsForOutput());
}
inline void writeSnappyMessageToFd(int fd, MessageBuilder& builder) {
writeSnappyMessageToFd(fd, builder.getSegmentsForOutput());
}
} // namespace capnproto
#endif // CAPNPROTO_SERIALIZE_SNAPPY_H_
......@@ -152,7 +152,7 @@ InputStreamMessageReader::InputStreamMessageReader(
: MessageReader(options), inputStream(inputStream), readPos(nullptr) {
internal::WireValue<uint32_t> firstWord[2];
inputStream.read(firstWord, sizeof(firstWord), sizeof(firstWord));
inputStream.read(firstWord, sizeof(firstWord));
uint segmentCount = firstWord[0].get();
uint segment0Size = segmentCount == 0 ? 0 : firstWord[1].get();
......@@ -162,7 +162,7 @@ InputStreamMessageReader::InputStreamMessageReader(
// Read sizes for all segments except the first. Include padding if necessary.
internal::WireValue<uint32_t> moreSizes[segmentCount & ~1];
if (segmentCount > 1) {
inputStream.read(moreSizes, sizeof(moreSizes), sizeof(moreSizes));
inputStream.read(moreSizes, sizeof(moreSizes));
for (uint i = 0; i < segmentCount - 1; i++) {
totalWords += moreSizes[i].get();
}
......@@ -188,7 +188,7 @@ InputStreamMessageReader::InputStreamMessageReader(
}
if (segmentCount == 1) {
inputStream.read(scratchSpace.begin(), totalWords * sizeof(word), totalWords * sizeof(word));
inputStream.read(scratchSpace.begin(), totalWords * sizeof(word));
} else if (segmentCount > 1) {
readPos = reinterpret_cast<byte*>(scratchSpace.begin());
readPos += inputStream.read(readPos, segment0Size * sizeof(word), totalWords * sizeof(word));
......@@ -200,7 +200,16 @@ InputStreamMessageReader::~InputStreamMessageReader() {
// Note that lazy reads only happen when we have multiple segments, so moreSegments.back() is
// valid.
const byte* allEnd = reinterpret_cast<const byte*>(moreSegments.back().end());
inputStream.skip(allEnd - readPos);
if (std::uncaught_exception()) {
try {
inputStream.skip(allEnd - readPos);
} catch (...) {
// TODO: Devise some way to report secondary errors during unwind.
}
} else {
inputStream.skip(allEnd - readPos);
}
}
}
......@@ -328,7 +337,12 @@ void FdOutputStream::write(const void* buffer, size_t size) {
ssize_t n = ::write(fd, pos, size);
if (n <= 0) {
CAPNPROTO_ASSERT(n < 0, "write() returned zero.");
throw OsException("write", errno);
int error = errno;
if (error == EINTR) {
continue;
} else {
throw OsException("write", error);
}
}
pos += n;
size -= n;
......
......@@ -91,6 +91,9 @@ public:
// message. If it can't even reach minBytes, it MUST throw an exception, as the caller is not
// expected to understand how to deal with partial reads.
inline void read(void* buffer, size_t bytes) { read(buffer, bytes, bytes); }
// Convenience method for reading an exact number of bytes.
virtual void skip(size_t bytes);
// Skips past the given number of bytes, discarding them. The default implementation read()s
// into a scratch buffer.
......@@ -210,18 +213,11 @@ public:
ArrayPtr<word> scratchSpace = nullptr)
: FdInputStream(fd), InputStreamMessageReader(*this, options, scratchSpace) {}
// Read message from a file descriptor, without taking ownership of the descriptor.
//
// Since this version implies that the caller intends to read more data from the fd later on, the
// default is to read the entire message eagerly in the constructor, so that the fd will be
// deterministically positioned just past the end of the message.
StreamFdMessageReader(AutoCloseFd fd, ReaderOptions options = ReaderOptions(),
ArrayPtr<word> scratchSpace = nullptr)
: FdInputStream(move(fd)), InputStreamMessageReader(*this, options, scratchSpace) {}
// Read a message from a file descriptor, taking ownership of the descriptor.
//
// Since this version implies that the caller does not intend to read any more data from the fd,
// the default is to read the message lazily as needed.
~StreamFdMessageReader();
};
......
......@@ -116,7 +116,7 @@ class Array {
public:
inline Array(): ptr(nullptr), size_(0) {}
inline Array(std::nullptr_t): ptr(nullptr), size_(0) {}
inline Array(Array&& other): ptr(other.ptr), size_(other.size_) {
inline Array(Array&& other) noexcept: ptr(other.ptr), size_(other.size_) {
other.ptr = nullptr;
other.size_ = 0;
}
......
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