Commit 53cc6ec8 authored by Kenton Varda's avatar Kenton Varda

Packed serialization -- removes the zeros.

parent 7b330237
......@@ -251,6 +251,9 @@ uint64_t doBenchmark3(const std::string& mode, const std::string& reuse,
if (compression == "none") {
return doBenchmark2<BenchmarkTypes, TestCase, typename BenchmarkTypes::Uncompressed>(
mode, reuse, iters);
} else if (compression == "packed") {
return doBenchmark2<BenchmarkTypes, TestCase, typename BenchmarkTypes::Packed>(
mode, reuse, iters);
} else if (compression == "snappy") {
return doBenchmark2<BenchmarkTypes, TestCase, typename BenchmarkTypes::SnappyCompressed>(
mode, reuse, iters);
......
......@@ -147,8 +147,9 @@ struct BenchmarkMethods {
};
struct BenchmarkTypes {
typedef void SnappyCompressed;
typedef void Uncompressed;
typedef void Packed;
typedef void SnappyCompressed;
typedef void ReusableResources;
typedef void SingleUseResources;
......
......@@ -335,8 +335,9 @@ struct BenchmarkMethods {
};
struct BenchmarkTypes {
typedef protobuf::SnappyCompressed SnappyCompressed;
typedef protobuf::Uncompressed Uncompressed;
typedef protobuf::Uncompressed Packed;
typedef protobuf::SnappyCompressed SnappyCompressed;
typedef protobuf::ReusableMessages ReusableResources;
typedef protobuf::SingleUseMessages SingleUseResources;
......
......@@ -121,8 +121,9 @@ enum class Reuse {
};
enum class Compression {
SNAPPY,
NONE
NONE,
PACKED,
SNAPPY
};
TestResult runTest(Product product, TestCase testCase, Mode mode, Reuse reuse,
......@@ -174,12 +175,15 @@ TestResult runTest(Product product, TestCase testCase, Mode mode, Reuse reuse,
}
switch (compression) {
case Compression::SNAPPY:
argv[3] = strdup("snappy");
break;
case Compression::NONE:
argv[3] = strdup("none");
break;
case Compression::PACKED:
argv[3] = strdup("packed");
break;
case Compression::SNAPPY:
argv[3] = strdup("snappy");
break;
}
char itersStr[64];
......@@ -295,7 +299,7 @@ ostream& operator<<(ostream& os, Gain gain) {
}
void reportComparison(const char* name, double base, double protobuf, double capnproto,
double iters) {
uint64_t iters) {
cout << setw(35) << left << name
<< setw(14) << right << Gain(base, protobuf)
<< setw(14) << right << Gain(base, capnproto);
......@@ -305,7 +309,7 @@ void reportComparison(const char* name, double base, double protobuf, double cap
}
void reportComparison(const char* name, const char* unit, double protobuf, double capnproto,
double iters) {
uint64_t iters) {
cout << setw(35) << left << name
<< setw(15-strlen(unit)) << right << setprecision(2) << (protobuf / iters) << unit
<< setw(15-strlen(unit)) << right << setprecision(2) << (capnproto / iters) << unit;
......@@ -356,6 +360,8 @@ int main(int argc, char* argv[]) {
testCase = TestCase::CARSALES;
} else if (arg == "no-reuse") {
reuse = Reuse::NO;
} else if (arg == "packed") {
compression = Compression::PACKED;
} else if (arg == "snappy") {
compression = Compression::SNAPPY;
} else {
......@@ -417,12 +423,16 @@ int main(int argc, char* argv[]) {
break;
}
switch (compression) {
case Compression::SNAPPY:
cout << "* Snappy compression" << endl;
break;
case Compression::NONE:
cout << "* no compression" << endl;
break;
case Compression::PACKED:
cout << "* de-zero packing for Cap'n Proto" << endl;
cout << "* standard packing for Protobuf" << endl;
break;
case Compression::SNAPPY:
cout << "* Snappy compression" << endl;
break;
}
reportTableHeader();
......@@ -458,10 +468,12 @@ int main(int argc, char* argv[]) {
nullCase.throughput, protobufBase.throughput, capnpBase.throughput, iters);
reportComparison("object manipulation",
nullCase.time.cpu(), protobufBase.time.cpu(), capnpBase.time.cpu(), iters);
reportComparison("I/O", "us",
reportComparison("I/O time", "us",
((int64_t)protobuf.time.cpu() - (int64_t)protobufBase.time.cpu()) / 1000.0,
((int64_t)capnp.time.cpu() - (int64_t)capnpBase.time.cpu()) / 1000.0, iters);
reportComparison("bandwidth", "B", protobuf.throughput, capnp.throughput, iters);
reportComparison("binary size", "kB",
fileSize("protobuf-" + std::string(testCaseName(testCase))) / 1024.0,
fileSize("capnproto-" + std::string(testCaseName(testCase))) / 1024.0, 1);
......
This diff is collapsed.
This diff is collapsed.
......@@ -60,6 +60,17 @@ void assertionFailure(const char* file, int line, const char* expectation, const
#define CAPNPROTO_DEBUG_ASSERT(condition, message) CAPNPROTO_ASSERT(condition, message)
#endif
// Allocate an array, preferably on the stack, unless it is too big. On GCC this will use
// variable-sized arrays. For other compilers we could just use a fixed-size array.
#define CAPNPROTO_STACK_ARRAY(type, name, size, maxStack) \
size_t name##_size = (size); \
bool name##_isOnStack = name##_size <= (maxStack); \
type name##_stack[name##_isOnStack ? size : 0]; \
::capnproto::Array<type> name##_heap = name##_isOnStack ? \
nullptr : newArray<type>(name##_size); \
::capnproto::ArrayPtr<type> name = name##_isOnStack ? \
arrayPtr(name##_stack, name##_size) : name##_heap
} // namespace internal
template <typename T, typename U>
......
......@@ -50,7 +50,7 @@ internal::StructReader MessageReader::getRoot(const word* defaultValue) {
internal::SegmentReader* segment = arena()->tryGetSegment(SegmentId(0));
if (segment == nullptr ||
!segment->containsInterval(segment->getStartPtr(), segment->getStartPtr() + 1)) {
segment->getArena()->reportInvalidData("Message did not contain a root pointer.");
arena()->reportInvalidData("Message did not contain a root pointer.");
return internal::StructReader::readRootTrusted(defaultValue, defaultValue);
} else {
return internal::StructReader::readRoot(
......
This diff is collapsed.
This diff is collapsed.
// 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_PACKED_H_
#define CAPNPROTO_SERIALIZE_PACKED_H_
#include "serialize.h"
namespace capnproto {
namespace internal {
class PackedInputStream: public InputStream {
// An input stream that unpacks packed data with a picky constraint: The caller must read data
// in the exact same size and sequence as the data was written to PackedOutputStream.
public:
explicit PackedInputStream(BufferedInputStream& inner);
CAPNPROTO_DISALLOW_COPY(PackedInputStream);
~PackedInputStream();
// implements InputStream ------------------------------------------
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override;
void skip(size_t bytes) override;
private:
BufferedInputStream& inner;
};
class PackedOutputStream: public OutputStream {
public:
explicit PackedOutputStream(BufferedOutputStream& inner);
CAPNPROTO_DISALLOW_COPY(PackedOutputStream);
~PackedOutputStream();
// implements OutputStream -----------------------------------------
void write(const void* buffer, size_t bytes) override;
private:
BufferedOutputStream& inner;
};
} // namespace internal
class PackedMessageReader: private internal::PackedInputStream, public InputStreamMessageReader {
public:
PackedMessageReader(BufferedInputStream& inputStream, ReaderOptions options = ReaderOptions(),
ArrayPtr<word> scratchSpace = nullptr);
CAPNPROTO_DISALLOW_COPY(PackedMessageReader);
~PackedMessageReader();
};
class PackedFdMessageReader: private FdInputStream, private BufferedInputStreamWrapper,
public PackedMessageReader {
public:
PackedFdMessageReader(int fd, ReaderOptions options = ReaderOptions(),
ArrayPtr<word> scratchSpace = nullptr);
// Read message from a file descriptor, without taking ownership of the descriptor.
// Note that if you want to reuse the descriptor after the reader is destroyed, you'll need to
// seek it, since otherwise the position is unspecified.
PackedFdMessageReader(AutoCloseFd fd, ReaderOptions options = ReaderOptions(),
ArrayPtr<word> scratchSpace = nullptr);
// Read a message from a file descriptor, taking ownership of the descriptor.
CAPNPROTO_DISALLOW_COPY(PackedFdMessageReader);
~PackedFdMessageReader();
};
void writePackedMessage(BufferedOutputStream& output, MessageBuilder& builder);
void writePackedMessage(BufferedOutputStream& output,
ArrayPtr<const ArrayPtr<const word>> segments);
// Write a packed message to a buffered output stream.
void writePackedMessage(OutputStream& output, MessageBuilder& builder);
void writePackedMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> segments);
// Write a packed message to an unbuffered output stream. If you intend to write multiple messages
// in succession, consider wrapping your output in a buffered stream in order to reduce system
// call overhead.
void writePackedMessageToFd(int fd, MessageBuilder& builder);
void writePackedMessageToFd(int fd, ArrayPtr<const ArrayPtr<const word>> segments);
// Write a single packed message to the file descriptor.
// =======================================================================================
// inline stuff
inline void writePackedMessage(BufferedOutputStream& output, MessageBuilder& builder) {
writePackedMessage(output, builder.getSegmentsForOutput());
}
inline void writePackedMessage(OutputStream& output, MessageBuilder& builder) {
writePackedMessage(output, builder.getSegmentsForOutput());
}
inline void writePackedMessageToFd(int fd, MessageBuilder& builder) {
writePackedMessageToFd(fd, builder.getSegmentsForOutput());
}
} // namespace capnproto
#endif // CAPNPROTO_SERIALIZE_PACKED_H_
......@@ -63,106 +63,166 @@ private:
uint desiredSegmentCount;
};
class TestPipe: public InputStream, public OutputStream {
class TestPipe: public BufferedInputStream, public OutputStream {
public:
TestPipe(bool lazy)
: lazy(lazy), readPos(0) {}
TestPipe()
: preferredReadSize(std::numeric_limits<size_t>::max()), readPos(0) {}
explicit TestPipe(size_t preferredReadSize)
: preferredReadSize(preferredReadSize), readPos(0) {}
~TestPipe() {}
const std::string& getData() { return data; }
std::string getUnreadData() { return data.substr(readPos); }
std::string::size_type getReadPos() { return readPos; }
void resetRead(size_t preferredReadSize = std::numeric_limits<size_t>::max()) {
readPos = 0;
this->preferredReadSize = preferredReadSize;
}
bool allRead() {
return readPos == data.size();
}
void clear(size_t preferredReadSize = std::numeric_limits<size_t>::max()) {
resetRead(preferredReadSize);
data.clear();
}
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;
size_t amount = std::min(maxBytes, std::max(minBytes, preferredReadSize));
memcpy(buffer, data.data() + readPos, amount);
readPos += amount;
return amount;
}
void skip(size_t bytes) override {
CAPNPROTO_ASSERT(bytes <= data.size() - readPos, "Overran end of stream.");
readPos += bytes;
}
ArrayPtr<const byte> getReadBuffer() override {
size_t amount = std::min(data.size() - readPos, preferredReadSize);
return arrayPtr(reinterpret_cast<const byte*>(data.data() + readPos), amount);
}
private:
bool lazy;
size_t preferredReadSize;
std::string data;
std::string::size_type readPos;
};
struct DisplayByteArray {
DisplayByteArray(const std::string& str)
: data(reinterpret_cast<const uint8_t*>(str.data())), size(str.size()) {}
DisplayByteArray(const std::initializer_list<uint8_t>& list)
: data(list.begin()), size(list.size()) {}
const uint8_t* data;
size_t size;
};
std::ostream& operator<<(std::ostream& os, const DisplayByteArray& bytes) {
os << "{ ";
for (size_t i = 0; i < bytes.size; i++) {
if (i > 0) {
os << ", ";
}
os << (uint)bytes.data[i];
}
os << " }";
return os;
}
TEST(Snappy, RoundTrip) {
TestMessageBuilder builder(1);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(false);
writeSnappyMessage(pipe, builder);
TestPipe pipe;
writeSnappyPackedMessage(pipe, builder);
SnappyMessageReader reader(pipe);
SnappyPackedMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
EXPECT_TRUE(pipe.allRead());
}
TEST(Snappy, RoundTripScratchSpace) {
TestMessageBuilder builder(1);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(false);
writeSnappyMessage(pipe, builder);
TestPipe pipe;
writeSnappyPackedMessage(pipe, builder);
word scratch[1024];
SnappyMessageReader reader(pipe, ReaderOptions(), ArrayPtr<word>(scratch, 1024));
SnappyPackedMessageReader reader(pipe, ReaderOptions(), ArrayPtr<word>(scratch, 1024));
checkTestMessage(reader.getRoot<TestAllTypes>());
EXPECT_TRUE(pipe.allRead());
}
TEST(Snappy, RoundTripLazy) {
TestMessageBuilder builder(1);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(true);
writeSnappyMessage(pipe, builder);
TestPipe pipe(1);
writeSnappyPackedMessage(pipe, builder);
SnappyMessageReader reader(pipe);
SnappyPackedMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
EXPECT_TRUE(pipe.allRead());
}
TEST(Snappy, RoundTripOddSegmentCount) {
TestMessageBuilder builder(7);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(false);
writeSnappyMessage(pipe, builder);
TestPipe pipe;
writeSnappyPackedMessage(pipe, builder);
SnappyMessageReader reader(pipe);
SnappyPackedMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
EXPECT_TRUE(pipe.allRead());
}
TEST(Snappy, RoundTripOddSegmentCountLazy) {
TestMessageBuilder builder(7);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(true);
writeSnappyMessage(pipe, builder);
TestPipe pipe(1);
writeSnappyPackedMessage(pipe, builder);
SnappyMessageReader reader(pipe);
SnappyPackedMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
EXPECT_TRUE(pipe.allRead());
}
TEST(Snappy, RoundTripEvenSegmentCount) {
TestMessageBuilder builder(10);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(false);
writeSnappyMessage(pipe, builder);
TestPipe pipe;
writeSnappyPackedMessage(pipe, builder);
SnappyMessageReader reader(pipe);
SnappyPackedMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
EXPECT_TRUE(pipe.allRead());
}
TEST(Snappy, RoundTripEvenSegmentCountLazy) {
TestMessageBuilder builder(10);
initTestMessage(builder.initRoot<TestAllTypes>());
TestPipe pipe(true);
writeSnappyMessage(pipe, builder);
TestPipe pipe(1);
writeSnappyPackedMessage(pipe, builder);
SnappyMessageReader reader(pipe);
SnappyPackedMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
EXPECT_TRUE(pipe.allRead());
}
TEST(Snappy, RoundTripTwoMessages) {
......@@ -172,19 +232,23 @@ TEST(Snappy, RoundTripTwoMessages) {
TestMessageBuilder builder2(1);
builder2.initRoot<TestAllTypes>().setTextField("Second message.");
TestPipe pipe(false);
writeSnappyMessage(pipe, builder);
writeSnappyMessage(pipe, builder2);
TestPipe pipe(1);
writeSnappyPackedMessage(pipe, builder);
size_t firstSize = pipe.getData().size();
writeSnappyPackedMessage(pipe, builder2);
{
SnappyMessageReader reader(pipe);
SnappyPackedMessageReader reader(pipe);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
EXPECT_EQ(firstSize, pipe.getReadPos());
{
SnappyMessageReader reader(pipe);
SnappyPackedMessageReader reader(pipe);
EXPECT_EQ("Second message.", reader.getRoot<TestAllTypes>().getTextField());
}
EXPECT_TRUE(pipe.allRead());
}
// TODO: Test error cases.
......
This diff is collapsed.
......@@ -25,56 +25,84 @@
#define CAPNPROTO_SERIALIZE_SNAPPY_H_
#include "serialize.h"
#include "serialize-packed.h"
namespace capnproto {
class SnappyMessageReader: public MessageReader {
constexpr size_t SNAPPY_BUFFER_SIZE = 65536;
constexpr size_t SNAPPY_COMPRESSED_BUFFER_SIZE = 76490;
class SnappyInputStream: public BufferedInputStream {
public:
SnappyMessageReader(InputStream& inputStream, ReaderOptions options = ReaderOptions(),
ArrayPtr<word> scratchSpace = nullptr);
~SnappyMessageReader();
explicit SnappyInputStream(BufferedInputStream& inner, ArrayPtr<byte> buffer = nullptr);
CAPNPROTO_DISALLOW_COPY(SnappyInputStream);
~SnappyInputStream();
ArrayPtr<const word> getSegment(uint id) override;
// implements BufferedInputStream ----------------------------------
ArrayPtr<const byte> getReadBuffer() override;
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override;
void skip(size_t bytes) override;
private:
InputStream& inputStream;
Array<word> space;
class InputStreamSnappySource;
BufferedInputStream& inner;
Array<byte> ownedBuffer;
ArrayPtr<byte> buffer;
ArrayPtr<byte> bufferAvailable;
union {
FlatArrayMessageReader underlyingReader;
};
void refill();
};
class SnappyFdMessageReader: private FdInputStream, public SnappyMessageReader {
class SnappyOutputStream: public BufferedOutputStream {
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.
explicit SnappyOutputStream(OutputStream& inner,
ArrayPtr<byte> buffer = nullptr,
ArrayPtr<byte> compressedBuffer = nullptr);
CAPNPROTO_DISALLOW_COPY(SnappyOutputStream);
~SnappyOutputStream();
void flush();
// Force the stream to write any remaining bytes in its buffer to the inner stream. This will
// hurt compression, of course, by forcing the current block to end prematurely.
// implements BufferedOutputStream ---------------------------------
ArrayPtr<byte> getWriteBuffer() override;
void write(const void* buffer, size_t size) override;
private:
OutputStream& inner;
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.
Array<byte> ownedBuffer;
ArrayPtr<byte> buffer;
byte* bufferPos;
~SnappyFdMessageReader();
Array<byte> ownedCompressedBuffer;
ArrayPtr<byte> compressedBuffer;
};
void writeSnappyMessage(OutputStream& output, MessageBuilder& builder);
void writeSnappyMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> segments);
class SnappyPackedMessageReader: private SnappyInputStream, public PackedMessageReader {
public:
SnappyPackedMessageReader(
BufferedInputStream& inputStream, ReaderOptions options = ReaderOptions(),
ArrayPtr<word> scratchSpace = nullptr, ArrayPtr<byte> buffer = nullptr);
~SnappyPackedMessageReader();
};
void writeSnappyMessageToFd(int fd, MessageBuilder& builder);
void writeSnappyMessageToFd(int fd, ArrayPtr<const ArrayPtr<const word>> segments);
void writeSnappyPackedMessage(OutputStream& output, MessageBuilder& builder,
ArrayPtr<byte> buffer = nullptr,
ArrayPtr<byte> compressedBuffer = nullptr);
void writeSnappyPackedMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> segments,
ArrayPtr<byte> buffer = nullptr,
ArrayPtr<byte> compressedBuffer = nullptr);
// =======================================================================================
// inline stuff
inline void writeSnappyMessage(OutputStream& output, MessageBuilder& builder) {
writeSnappyMessage(output, builder.getSegmentsForOutput());
}
inline void writeSnappyMessageToFd(int fd, MessageBuilder& builder) {
writeSnappyMessageToFd(fd, builder.getSegmentsForOutput());
inline void writeSnappyPackedMessage(OutputStream& output, MessageBuilder& builder,
ArrayPtr<byte> buffer,
ArrayPtr<byte> compressedBuffer) {
writeSnappyPackedMessage(output, builder.getSegmentsForOutput(), buffer, compressedBuffer);
}
} // namespace capnproto
......
......@@ -23,11 +23,6 @@
#include "serialize.h"
#include "wire-format.h"
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <string>
#include <sys/uio.h>
namespace capnproto {
......@@ -41,7 +36,7 @@ FlatArrayMessageReader::FlatArrayMessageReader(ArrayPtr<const word> array, Reade
const internal::WireValue<uint32_t>* table =
reinterpret_cast<const internal::WireValue<uint32_t>*>(array.begin());
uint segmentCount = table[0].get();
uint segmentCount = table[0].get() + 1;
size_t offset = segmentCount / 2u + 1u;
if (array.size() < offset) {
......@@ -92,6 +87,8 @@ ArrayPtr<const word> FlatArrayMessageReader::getSegment(uint id) {
}
Array<word> messageToFlatArray(ArrayPtr<const ArrayPtr<const word>> segments) {
CAPNPROTO_ASSERT(segments.size() > 0, "Tried to serialize uninitialized message.");
size_t totalSize = segments.size() / 2 + 1;
for (auto& segment: segments) {
......@@ -103,7 +100,7 @@ Array<word> messageToFlatArray(ArrayPtr<const ArrayPtr<const word>> segments) {
internal::WireValue<uint32_t>* table =
reinterpret_cast<internal::WireValue<uint32_t>*>(result.begin());
table[0].set(segments.size());
table[0].set(segments.size() - 1);
for (uint i = 0; i < segments.size(); i++) {
table[i + 1].set(segments[i].size());
......@@ -128,25 +125,6 @@ Array<word> messageToFlatArray(ArrayPtr<const ArrayPtr<const word>> segments) {
// =======================================================================================
InputStream::~InputStream() {}
OutputStream::~OutputStream() {}
void InputStream::skip(size_t bytes) {
char scratch[8192];
while (bytes > 0) {
size_t amount = std::min(bytes, sizeof(scratch));
bytes -= read(scratch, amount, amount);
}
}
void OutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) {
for (auto piece: pieces) {
write(piece.begin(), piece.size());
}
}
// -------------------------------------------------------------------
InputStreamMessageReader::InputStreamMessageReader(
InputStream& inputStream, ReaderOptions options, ArrayPtr<word> scratchSpace)
: MessageReader(options), inputStream(inputStream), readPos(nullptr) {
......@@ -154,7 +132,7 @@ InputStreamMessageReader::InputStreamMessageReader(
inputStream.read(firstWord, sizeof(firstWord));
uint segmentCount = firstWord[0].get();
uint segmentCount = firstWord[0].get() + 1;
uint segment0Size = segmentCount == 0 ? 0 : firstWord[1].get();
size_t totalWords = segment0Size;
......@@ -237,9 +215,11 @@ ArrayPtr<const word> InputStreamMessageReader::getSegment(uint id) {
// -------------------------------------------------------------------
void writeMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> segments) {
CAPNPROTO_ASSERT(segments.size() > 0, "Tried to serialize uninitialized message.");
internal::WireValue<uint32_t> table[(segments.size() + 2) & ~size_t(1)];
table[0].set(segments.size());
table[0].set(segments.size() - 1);
for (uint i = 0; i < segments.size(); i++) {
table[i + 1].set(segments[i].size());
}
......@@ -260,133 +240,6 @@ void writeMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> seg
}
// =======================================================================================
class OsException: public std::exception {
public:
OsException(const char* function, int error) {
char buffer[256];
message = function;
message += ": ";
message.append(strerror_r(error, buffer, sizeof(buffer)));
}
~OsException() noexcept {}
const char* what() const noexcept override {
return message.c_str();
}
private:
std::string message;
};
class PrematureEofException: public std::exception {
public:
PrematureEofException() {}
~PrematureEofException() noexcept {}
const char* what() const noexcept override {
return "Stream ended prematurely.";
}
};
AutoCloseFd::~AutoCloseFd() {
if (fd >= 0 && close(fd) < 0) {
if (std::uncaught_exception()) {
// TODO: Devise some way to report secondary errors during unwind.
} else {
throw OsException("close", errno);
}
}
}
FdInputStream::~FdInputStream() {}
size_t FdInputStream::read(void* buffer, size_t minBytes, size_t maxBytes) {
byte* pos = reinterpret_cast<byte*>(buffer);
byte* min = pos + minBytes;
byte* max = pos + maxBytes;
while (pos < min) {
ssize_t n = ::read(fd, pos, max - pos);
if (n <= 0) {
if (n < 0) {
int error = errno;
if (error == EINTR) {
continue;
} else {
throw OsException("read", error);
}
} else if (n == 0) {
throw PrematureEofException();
}
return false;
}
pos += n;
}
return pos - reinterpret_cast<byte*>(buffer);
}
FdOutputStream::~FdOutputStream() {}
void FdOutputStream::write(const void* buffer, size_t size) {
const char* pos = reinterpret_cast<const char*>(buffer);
while (size > 0) {
ssize_t n = ::write(fd, pos, size);
if (n <= 0) {
CAPNPROTO_ASSERT(n < 0, "write() returned zero.");
int error = errno;
if (error == EINTR) {
continue;
} else {
throw OsException("write", error);
}
}
pos += n;
size -= n;
}
}
void FdOutputStream::write(ArrayPtr<const ArrayPtr<const byte>> pieces) {
struct iovec iov[pieces.size()];
for (uint i = 0; i < pieces.size(); i++) {
// writev() interface is not const-correct. :(
iov[i].iov_base = const_cast<byte*>(pieces[i].begin());
iov[i].iov_len = pieces[i].size();
}
struct iovec* current = iov;
struct iovec* end = iov + pieces.size();
// Make sure we don't do anything on an empty write.
while (current < end && current->iov_len == 0) {
++current;
}
while (current < end) {
ssize_t n = ::writev(fd, iov, end - current);
if (n <= 0) {
if (n <= 0) {
CAPNPROTO_ASSERT(n < 0, "write() returned zero.");
throw OsException("writev", errno);
}
}
while (static_cast<size_t>(n) >= current->iov_len) {
n -= current->iov_len;
++current;
}
if (n > 0) {
current->iov_base = reinterpret_cast<byte*>(current->iov_base) + n;
current->iov_len -= n;
}
}
}
StreamFdMessageReader::~StreamFdMessageReader() {}
void writeMessageToFd(int fd, ArrayPtr<const ArrayPtr<const word>> segments) {
......
......@@ -44,6 +44,7 @@
#define CAPNPROTO_SERIALIZE_H_
#include "message.h"
#include "io.h"
namespace capnproto {
......@@ -71,47 +72,6 @@ Array<word> messageToFlatArray(ArrayPtr<const ArrayPtr<const word>> segments);
// =======================================================================================
class InputStream {
public:
virtual ~InputStream();
virtual size_t read(void* buffer, size_t minBytes, size_t maxBytes) = 0;
// Reads at least minBytes and at most maxBytes, copying them into the given buffer. Returns
// the size read. Throws an exception on errors.
//
// maxBytes is the number of bytes the caller really wants, but minBytes is the minimum amount
// needed by the caller before it can start doing useful processing. If the stream returns less
// than maxBytes, the caller will usually call read() again later to get the rest. Returning
// less than maxBytes is useful when it makes sense for the caller to parallelize processing
// with I/O.
//
// Cap'n Proto never asks for more bytes than it knows are part of the message. Therefore, if
// the InputStream happens to know that the stream will never reach maxBytes -- even if it has
// reached minBytes -- it should throw an exception to avoid wasting time processing an incomplete
// 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.
};
class OutputStream {
public:
virtual ~OutputStream();
virtual void write(const void* buffer, size_t size) = 0;
// Always writes the full size. Throws exception on error.
virtual void write(ArrayPtr<const ArrayPtr<const byte>> pieces);
// Equivalent to write()ing each byte array in sequence, which is what the default implementation
// does. Override if you can do something better, e.g. use writev() to do the write in a single
// syscall.
};
class InputStreamMessageReader: public MessageReader {
public:
InputStreamMessageReader(InputStream& inputStream,
......@@ -143,67 +103,6 @@ void writeMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> seg
// =======================================================================================
// Specializations for reading from / writing to file descriptors.
class AutoCloseFd {
// A wrapper around a file descriptor which automatically closes the descriptor when destroyed.
// The wrapper supports move construction for transferring ownership of the descriptor. If
// close() returns an error, the destructor throws an exception, UNLESS the destructor is being
// called during unwind from another exception, in which case the close error is ignored.
//
// If your code is not exception-safe, you should not use AutoCloseFd. In this case you will
// have to call close() yourself and handle errors appropriately.
//
// TODO: Create a general helper library for reporting/detecting secondary exceptions that
// occurred during unwind of some primary exception.
public:
inline AutoCloseFd(): fd(-1) {}
inline AutoCloseFd(std::nullptr_t): fd(-1) {}
inline explicit AutoCloseFd(int fd): fd(fd) {}
inline AutoCloseFd(AutoCloseFd&& other): fd(other.fd) { other.fd = -1; }
CAPNPROTO_DISALLOW_COPY(AutoCloseFd);
~AutoCloseFd();
inline operator int() { return fd; }
inline int get() { return fd; }
inline bool operator==(std::nullptr_t) { return fd < 0; }
inline bool operator!=(std::nullptr_t) { return fd >= 0; }
private:
int fd;
};
class FdInputStream: public InputStream {
// An InputStream wrapping a file descriptor.
public:
FdInputStream(int fd): fd(fd) {};
FdInputStream(AutoCloseFd fd): fd(fd), autoclose(move(fd)) {}
~FdInputStream();
size_t read(void* buffer, size_t minBytes, size_t maxBytes) override;
private:
int fd;
AutoCloseFd autoclose;
};
class FdOutputStream: public OutputStream {
// An OutputStream wrapping a file descriptor.
public:
FdOutputStream(int fd): fd(fd) {};
FdOutputStream(AutoCloseFd fd): fd(fd), autoclose(move(fd)) {}
~FdOutputStream();
void write(const void* buffer, size_t size) override;
void write(ArrayPtr<const ArrayPtr<const byte>> pieces) override;
private:
int fd;
AutoCloseFd autoclose;
};
class StreamFdMessageReader: private FdInputStream, public InputStreamMessageReader {
// A MessageReader that reads from a steam-based file descriptor. For seekable file descriptors
// (e.g. actual disk files), FdFileMessageReader is better, but this will still work.
......
......@@ -225,6 +225,78 @@ void genericCheckTestMessage(Reader reader) {
}
}
template <typename Reader>
void genericCheckTestMessageAllZero(Reader reader) {
EXPECT_EQ(Void::VOID, reader.getVoidField());
EXPECT_EQ(false, reader.getBoolField());
EXPECT_EQ(0, reader.getInt8Field());
EXPECT_EQ(0, reader.getInt16Field());
EXPECT_EQ(0, reader.getInt32Field());
EXPECT_EQ(0, reader.getInt64Field());
EXPECT_EQ(0u, reader.getUInt8Field());
EXPECT_EQ(0u, reader.getUInt16Field());
EXPECT_EQ(0u, reader.getUInt32Field());
EXPECT_EQ(0u, reader.getUInt64Field());
EXPECT_FLOAT_EQ(0, reader.getFloat32Field());
EXPECT_DOUBLE_EQ(0, reader.getFloat64Field());
EXPECT_EQ("", reader.getTextField());
EXPECT_EQ("", reader.getDataField());
{
auto subReader = reader.getStructField();
EXPECT_EQ(Void::VOID, subReader.getVoidField());
EXPECT_EQ(false, subReader.getBoolField());
EXPECT_EQ(0, subReader.getInt8Field());
EXPECT_EQ(0, subReader.getInt16Field());
EXPECT_EQ(0, subReader.getInt32Field());
EXPECT_EQ(0, subReader.getInt64Field());
EXPECT_EQ(0u, subReader.getUInt8Field());
EXPECT_EQ(0u, subReader.getUInt16Field());
EXPECT_EQ(0u, subReader.getUInt32Field());
EXPECT_EQ(0u, subReader.getUInt64Field());
EXPECT_FLOAT_EQ(0, subReader.getFloat32Field());
EXPECT_DOUBLE_EQ(0, subReader.getFloat64Field());
EXPECT_EQ("", subReader.getTextField());
EXPECT_EQ("", subReader.getDataField());
{
auto subSubReader = subReader.getStructField();
EXPECT_EQ("", subSubReader.getTextField());
EXPECT_EQ("", subSubReader.getStructField().getTextField());
}
EXPECT_EQ(0u, subReader.getVoidList().size());
EXPECT_EQ(0u, subReader.getBoolList().size());
EXPECT_EQ(0u, subReader.getInt8List().size());
EXPECT_EQ(0u, subReader.getInt16List().size());
EXPECT_EQ(0u, subReader.getInt32List().size());
EXPECT_EQ(0u, subReader.getInt64List().size());
EXPECT_EQ(0u, subReader.getUInt8List().size());
EXPECT_EQ(0u, subReader.getUInt16List().size());
EXPECT_EQ(0u, subReader.getUInt32List().size());
EXPECT_EQ(0u, subReader.getUInt64List().size());
EXPECT_EQ(0u, subReader.getFloat32List().size());
EXPECT_EQ(0u, subReader.getFloat64List().size());
EXPECT_EQ(0u, subReader.getTextList().size());
EXPECT_EQ(0u, subReader.getDataList().size());
EXPECT_EQ(0u, subReader.getStructList().size());
}
EXPECT_EQ(0u, reader.getVoidList().size());
EXPECT_EQ(0u, reader.getBoolList().size());
EXPECT_EQ(0u, reader.getInt8List().size());
EXPECT_EQ(0u, reader.getInt16List().size());
EXPECT_EQ(0u, reader.getInt32List().size());
EXPECT_EQ(0u, reader.getInt64List().size());
EXPECT_EQ(0u, reader.getUInt8List().size());
EXPECT_EQ(0u, reader.getUInt16List().size());
EXPECT_EQ(0u, reader.getUInt32List().size());
EXPECT_EQ(0u, reader.getUInt64List().size());
EXPECT_EQ(0u, reader.getFloat32List().size());
EXPECT_EQ(0u, reader.getFloat64List().size());
EXPECT_EQ(0u, reader.getTextList().size());
EXPECT_EQ(0u, reader.getDataList().size());
EXPECT_EQ(0u, reader.getStructList().size());
}
} // namespace
void initTestMessage(TestAllTypes::Builder builder) { genericInitTestMessage(builder); }
......@@ -236,5 +308,12 @@ void checkTestMessage(TestDefaults::Builder builder) { genericCheckTestMessage(b
void checkTestMessage(TestAllTypes::Reader reader) { genericCheckTestMessage(reader); }
void checkTestMessage(TestDefaults::Reader reader) { genericCheckTestMessage(reader); }
void checkTestMessageAllZero(TestAllTypes::Builder builder) {
genericCheckTestMessageAllZero(builder);
}
void checkTestMessageAllZero(TestAllTypes::Reader reader) {
genericCheckTestMessageAllZero(reader);
}
} // namespace internal
} // namespace capnproto
......@@ -38,6 +38,9 @@ void checkTestMessage(TestDefaults::Builder builder);
void checkTestMessage(TestAllTypes::Reader reader);
void checkTestMessage(TestDefaults::Reader reader);
void checkTestMessageAllZero(TestAllTypes::Builder builder);
void checkTestMessageAllZero(TestAllTypes::Reader reader);
} // namespace internal
} // namespace capnproto
......
......@@ -88,8 +88,8 @@ public:
return ArrayPtr(ptr + start, end - start);
}
inline bool operator==(std::nullptr_t) { return ptr == nullptr; }
inline bool operator!=(std::nullptr_t) { return ptr != nullptr; }
inline bool operator==(std::nullptr_t) { return size_ == 0; }
inline bool operator!=(std::nullptr_t) { return size_ != 0; }
private:
T* ptr;
......
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