Commit 203a16cc authored by Kenton Varda's avatar Kenton Varda

Basic serialization.

parent 9c3cb05f
......@@ -24,234 +24,38 @@
#include "test.capnp.h"
#include "message.h"
#include <gtest/gtest.h>
#include <initializer_list>
#include "test-util.h"
namespace capnproto {
namespace internal {
namespace {
template <typename Builder>
void initMessage(Builder builder) {
builder.setVoidField(Void::VOID);
builder.setBoolField(true);
builder.setInt8Field(-123);
builder.setInt16Field(-12345);
builder.setInt32Field(-12345678);
builder.setInt64Field(-123456789012345ll);
builder.setUInt8Field(234u);
builder.setUInt16Field(45678u);
builder.setUInt32Field(3456789012u);
builder.setUInt64Field(12345678901234567890ull);
builder.setFloat32Field(1234.5);
builder.setFloat64Field(-123e45);
builder.setTextField("foo");
builder.setDataField("bar");
{
auto subBuilder = builder.initStructField();
subBuilder.setVoidField(Void::VOID);
subBuilder.setBoolField(true);
subBuilder.setInt8Field(-12);
subBuilder.setInt16Field(3456);
subBuilder.setInt32Field(-78901234);
subBuilder.setInt64Field(56789012345678ll);
subBuilder.setUInt8Field(90u);
subBuilder.setUInt16Field(1234u);
subBuilder.setUInt32Field(56789012u);
subBuilder.setUInt64Field(345678901234567890ull);
subBuilder.setFloat32Field(-1.25e-10);
subBuilder.setFloat64Field(345);
subBuilder.setTextField("baz");
subBuilder.setDataField("qux");
{
auto subSubBuilder = subBuilder.initStructField();
subSubBuilder.setTextField("nested");
subSubBuilder.initStructField().setTextField("really nested");
}
subBuilder.setVoidList({Void::VOID, Void::VOID, Void::VOID});
subBuilder.setBoolList({false, true, false, true, true});
subBuilder.setInt8List({12, -34, -0x80, 0x7f});
subBuilder.setInt16List({1234, -5678, -0x8000, 0x7fff});
subBuilder.setInt32List({12345678, -90123456, -0x8000000, 0x7ffffff});
// gcc warns on -0x800...ll and the only work-around I could find was to do -0x7ff...ll-1.
subBuilder.setInt64List({123456789012345ll, -678901234567890ll, -0x7fffffffffffffffll-1, 0x7fffffffffffffffll});
subBuilder.setUInt8List({12u, 34u, 0u, 0xffu});
subBuilder.setUInt16List({1234u, 5678u, 0u, 0xffffu});
subBuilder.setUInt32List({12345678u, 90123456u, 0u, 0xffffffffu});
subBuilder.setUInt64List({123456789012345ull, 678901234567890ull, 0ull, 0xffffffffffffffffull});
subBuilder.setFloat32List({0, 1234567, 1e37, -1e37, 1e-37, -1e-37});
subBuilder.setFloat64List({0, 123456789012345, 1e306, -1e306, 1e-306, -1e-306});
subBuilder.setTextList({"quux", "corge", "grault"});
subBuilder.setDataList({"garply", "waldo", "fred"});
{
auto listBuilder = subBuilder.initStructList(3);
listBuilder[0].setTextField("x structlist 1");
listBuilder[1].setTextField("x structlist 2");
listBuilder[2].setTextField("x structlist 3");
}
}
builder.initVoidList(6);
builder.setBoolList({true, false, false, true});
builder.setInt8List({111, -111});
builder.setInt16List({11111, -11111});
builder.setInt32List({111111111, -111111111});
builder.setInt64List({1111111111111111111ll, -1111111111111111111ll});
builder.setUInt8List({111u, 222u});
builder.setUInt16List({33333u, 44444u});
builder.setUInt32List({3333333333u});
builder.setUInt64List({11111111111111111111ull});
builder.setFloat32List({5555.5, 2222.25});
builder.setFloat64List({7777.75, 1111.125});
builder.setTextList({"plugh", "xyzzy", "thud"});
builder.setDataList({"oops", "exhausted", "rfc3092"});
{
auto listBuilder = builder.initStructList(3);
listBuilder[0].setTextField("structlist 1");
listBuilder[1].setTextField("structlist 2");
listBuilder[2].setTextField("structlist 3");
}
}
template <typename T, typename U>
void checkList(T reader, std::initializer_list<U> expected) {
ASSERT_EQ(expected.size(), reader.size());
for (uint i = 0; i < expected.size(); i++) {
EXPECT_EQ(expected.begin()[i], reader[i]);
}
}
template <typename T>
void checkList(T reader, std::initializer_list<float> expected) {
ASSERT_EQ(expected.size(), reader.size());
for (uint i = 0; i < expected.size(); i++) {
EXPECT_FLOAT_EQ(expected.begin()[i], reader[i]);
}
}
template <typename T>
void checkList(T reader, std::initializer_list<double> expected) {
ASSERT_EQ(expected.size(), reader.size());
for (uint i = 0; i < expected.size(); i++) {
EXPECT_DOUBLE_EQ(expected.begin()[i], reader[i]);
}
}
template <typename Reader>
void checkMessage(Reader reader) {
EXPECT_EQ(Void::VOID, reader.getVoidField());
EXPECT_EQ(true, reader.getBoolField());
EXPECT_EQ(-123, reader.getInt8Field());
EXPECT_EQ(-12345, reader.getInt16Field());
EXPECT_EQ(-12345678, reader.getInt32Field());
EXPECT_EQ(-123456789012345ll, reader.getInt64Field());
EXPECT_EQ(234u, reader.getUInt8Field());
EXPECT_EQ(45678u, reader.getUInt16Field());
EXPECT_EQ(3456789012u, reader.getUInt32Field());
EXPECT_EQ(12345678901234567890ull, reader.getUInt64Field());
EXPECT_FLOAT_EQ(1234.5f, reader.getFloat32Field());
EXPECT_DOUBLE_EQ(-123e45, reader.getFloat64Field());
EXPECT_EQ("foo", reader.getTextField());
EXPECT_EQ("bar", reader.getDataField());
{
auto subReader = reader.getStructField();
EXPECT_EQ(Void::VOID, subReader.getVoidField());
EXPECT_EQ(true, subReader.getBoolField());
EXPECT_EQ(-12, subReader.getInt8Field());
EXPECT_EQ(3456, subReader.getInt16Field());
EXPECT_EQ(-78901234, subReader.getInt32Field());
EXPECT_EQ(56789012345678ll, subReader.getInt64Field());
EXPECT_EQ(90u, subReader.getUInt8Field());
EXPECT_EQ(1234u, subReader.getUInt16Field());
EXPECT_EQ(56789012u, subReader.getUInt32Field());
EXPECT_EQ(345678901234567890ull, subReader.getUInt64Field());
EXPECT_FLOAT_EQ(-1.25e-10f, subReader.getFloat32Field());
EXPECT_DOUBLE_EQ(345, subReader.getFloat64Field());
EXPECT_EQ("baz", subReader.getTextField());
EXPECT_EQ("qux", subReader.getDataField());
{
auto subSubReader = subReader.getStructField();
EXPECT_EQ("nested", subSubReader.getTextField());
EXPECT_EQ("really nested", subSubReader.getStructField().getTextField());
}
checkList(subReader.getVoidList(), {Void::VOID, Void::VOID, Void::VOID});
checkList(subReader.getBoolList(), {false, true, false, true, true});
checkList(subReader.getInt8List(), {12, -34, -0x80, 0x7f});
checkList(subReader.getInt16List(), {1234, -5678, -0x8000, 0x7fff});
checkList(subReader.getInt32List(), {12345678, -90123456, -0x8000000, 0x7ffffff});
// gcc warns on -0x800...ll and the only work-around I could find was to do -0x7ff...ll-1.
checkList(subReader.getInt64List(), {123456789012345ll, -678901234567890ll, -0x7fffffffffffffffll-1, 0x7fffffffffffffffll});
checkList(subReader.getUInt8List(), {12u, 34u, 0u, 0xffu});
checkList(subReader.getUInt16List(), {1234u, 5678u, 0u, 0xffffu});
checkList(subReader.getUInt32List(), {12345678u, 90123456u, 0u, 0xffffffffu});
checkList(subReader.getUInt64List(), {123456789012345ull, 678901234567890ull, 0ull, 0xffffffffffffffffull});
checkList(subReader.getFloat32List(), {0.0f, 1234567.0f, 1e37f, -1e37f, 1e-37f, -1e-37f});
checkList(subReader.getFloat64List(), {0.0, 123456789012345.0, 1e306, -1e306, 1e-306, -1e-306});
checkList(subReader.getTextList(), {"quux", "corge", "grault"});
checkList(subReader.getDataList(), {"garply", "waldo", "fred"});
{
auto listReader = subReader.getStructList();
ASSERT_EQ(3u, listReader.size());
EXPECT_EQ("x structlist 1", listReader[0].getTextField());
EXPECT_EQ("x structlist 2", listReader[1].getTextField());
EXPECT_EQ("x structlist 3", listReader[2].getTextField());
}
}
EXPECT_EQ(6u, reader.getVoidList().size());
checkList(reader.getBoolList(), {true, false, false, true});
checkList(reader.getInt8List(), {111, -111});
checkList(reader.getInt16List(), {11111, -11111});
checkList(reader.getInt32List(), {111111111, -111111111});
checkList(reader.getInt64List(), {1111111111111111111ll, -1111111111111111111ll});
checkList(reader.getUInt8List(), {111u, 222u});
checkList(reader.getUInt16List(), {33333u, 44444u});
checkList(reader.getUInt32List(), {3333333333u});
checkList(reader.getUInt64List(), {11111111111111111111ull});
checkList(reader.getFloat32List(), {5555.5f, 2222.25f});
checkList(reader.getFloat64List(), {7777.75, 1111.125});
checkList(reader.getTextList(), {"plugh", "xyzzy", "thud"});
checkList(reader.getDataList(), {"oops", "exhausted", "rfc3092"});
{
auto listReader = reader.getStructList();
ASSERT_EQ(3u, listReader.size());
EXPECT_EQ("structlist 1", listReader[0].getTextField());
EXPECT_EQ("structlist 2", listReader[1].getTextField());
EXPECT_EQ("structlist 3", listReader[2].getTextField());
}
}
TEST(Encoding, AllTypes) {
MallocMessageBuilder builder;
initMessage(builder.initRoot<TestAllTypes>());
checkMessage(builder.getRoot<TestAllTypes>());
checkMessage(builder.getRoot<TestAllTypes>().asReader());
initTestMessage(builder.initRoot<TestAllTypes>());
checkTestMessage(builder.getRoot<TestAllTypes>());
checkTestMessage(builder.getRoot<TestAllTypes>().asReader());
SegmentArrayMessageReader reader(builder.getSegmentsForOutput());
checkMessage(reader.getRoot<TestAllTypes>());
checkTestMessage(reader.getRoot<TestAllTypes>());
ASSERT_EQ(1u, builder.getSegmentsForOutput().size());
checkMessage(readMessageTrusted<TestAllTypes>(builder.getSegmentsForOutput()[0].begin()));
checkTestMessage(readMessageTrusted<TestAllTypes>(builder.getSegmentsForOutput()[0].begin()));
}
TEST(Encoding, AllTypesMultiSegment) {
MallocMessageBuilder builder(0, AllocationStrategy::FIXED_SIZE);
initMessage(builder.initRoot<TestAllTypes>());
checkMessage(builder.getRoot<TestAllTypes>());
checkMessage(builder.getRoot<TestAllTypes>().asReader());
initTestMessage(builder.initRoot<TestAllTypes>());
checkTestMessage(builder.getRoot<TestAllTypes>());
checkTestMessage(builder.getRoot<TestAllTypes>().asReader());
SegmentArrayMessageReader reader(builder.getSegmentsForOutput());
checkMessage(reader.getRoot<TestAllTypes>());
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Encoding, Defaults) {
......@@ -259,38 +63,38 @@ TEST(Encoding, Defaults) {
ArrayPtr<const word> segments[1] = {arrayPtr(nullRoot.words, 1)};
SegmentArrayMessageReader reader(arrayPtr(segments, 1));
checkMessage(reader.getRoot<TestDefaults>());
checkMessage(readMessageTrusted<TestDefaults>(nullRoot.words));
checkTestMessage(reader.getRoot<TestDefaults>());
checkTestMessage(readMessageTrusted<TestDefaults>(nullRoot.words));
}
TEST(Encoding, DefaultInitialization) {
MallocMessageBuilder builder;
checkMessage(builder.getRoot<TestDefaults>()); // first pass initializes to defaults
checkMessage(builder.getRoot<TestDefaults>().asReader());
checkTestMessage(builder.getRoot<TestDefaults>()); // first pass initializes to defaults
checkTestMessage(builder.getRoot<TestDefaults>().asReader());
checkMessage(builder.getRoot<TestDefaults>()); // second pass just reads the initialized structure
checkMessage(builder.getRoot<TestDefaults>().asReader());
checkTestMessage(builder.getRoot<TestDefaults>()); // second pass just reads the initialized structure
checkTestMessage(builder.getRoot<TestDefaults>().asReader());
SegmentArrayMessageReader reader(builder.getSegmentsForOutput());
checkMessage(reader.getRoot<TestDefaults>());
checkTestMessage(reader.getRoot<TestDefaults>());
}
TEST(Encoding, DefaultInitializationMultiSegment) {
MallocMessageBuilder builder(0, AllocationStrategy::FIXED_SIZE);
// first pass initializes to defaults
checkMessage(builder.getRoot<TestDefaults>());
checkMessage(builder.getRoot<TestDefaults>().asReader());
checkTestMessage(builder.getRoot<TestDefaults>());
checkTestMessage(builder.getRoot<TestDefaults>().asReader());
// second pass just reads the initialized structure
checkMessage(builder.getRoot<TestDefaults>());
checkMessage(builder.getRoot<TestDefaults>().asReader());
checkTestMessage(builder.getRoot<TestDefaults>());
checkTestMessage(builder.getRoot<TestDefaults>().asReader());
SegmentArrayMessageReader reader(builder.getSegmentsForOutput());
checkMessage(reader.getRoot<TestDefaults>());
checkTestMessage(reader.getRoot<TestDefaults>());
}
TEST(Encoding, DefaultsFromEmptyMessage) {
......@@ -299,8 +103,8 @@ TEST(Encoding, DefaultsFromEmptyMessage) {
ArrayPtr<const word> segments[1] = {arrayPtr(emptyMessage.words, 1)};
SegmentArrayMessageReader reader(arrayPtr(segments, 1));
checkMessage(reader.getRoot<TestDefaults>());
checkMessage(readMessageTrusted<TestDefaults>(emptyMessage.words));
checkTestMessage(reader.getRoot<TestDefaults>());
checkTestMessage(readMessageTrusted<TestDefaults>(emptyMessage.words));
}
} // namespace
......
// 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.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;
};
TEST(Serialize, FlatArray) {
TestMessageBuilder builder(1);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
FlatArrayMessageReader reader(serialized.asPtr());
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Serialize, FlatArrayOddSegmentCount) {
TestMessageBuilder builder(7);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
FlatArrayMessageReader reader(serialized.asPtr());
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Serialize, FlatArrayEventSegmentCount) {
TestMessageBuilder builder(10);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
FlatArrayMessageReader reader(serialized.asPtr());
checkTestMessage(reader.getRoot<TestAllTypes>());
}
class TestInputStream: public InputStream {
public:
TestInputStream(ArrayPtr<const word> data)
: pos(reinterpret_cast<const char*>(data.begin())),
end(reinterpret_cast<const char*>(data.end())) {}
~TestInputStream() {}
bool read(void* buffer, size_t size) override {
if (size_t(end - pos) < size) {
ADD_FAILURE() << "Overran end of stream.";
return false;
} else {
memcpy(buffer, pos, size);
pos += size;
return true;
}
}
private:
const char* pos;
const char* end;
};
TEST(Serialize, InputStream) {
TestMessageBuilder builder(1);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestInputStream stream(serialized.asPtr());
InputStreamMessageReader reader(&stream, ReaderOptions(), InputStrategy::EAGER);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Serialize, InputStreamLazy) {
TestMessageBuilder builder(1);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestInputStream stream(serialized.asPtr());
InputStreamMessageReader reader(&stream, ReaderOptions(), InputStrategy::LAZY);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Serialize, InputStreamOddSegmentCount) {
TestMessageBuilder builder(7);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestInputStream stream(serialized.asPtr());
InputStreamMessageReader reader(&stream, ReaderOptions(), InputStrategy::EAGER);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Serialize, InputStreamOddSegmentCountLazy) {
TestMessageBuilder builder(7);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestInputStream stream(serialized.asPtr());
InputStreamMessageReader reader(&stream, ReaderOptions(), InputStrategy::LAZY);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Serialize, InputStreamEventSegmentCount) {
TestMessageBuilder builder(10);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestInputStream stream(serialized.asPtr());
InputStreamMessageReader reader(&stream, ReaderOptions(), InputStrategy::EAGER);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Serialize, InputStreamEventSegmentCountLazy) {
TestMessageBuilder builder(10);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestInputStream stream(serialized.asPtr());
InputStreamMessageReader reader(&stream, ReaderOptions(), InputStrategy::LAZY);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
class TestInputFile: public InputFile {
public:
TestInputFile(ArrayPtr<const word> data)
: begin(reinterpret_cast<const char*>(data.begin())),
size_(data.size() * sizeof(word)) {}
~TestInputFile() {}
bool read(size_t offset, void* buffer, size_t size) override {
if (size_ < offset + size) {
ADD_FAILURE() << "Overran end of file.";
return false;
} else {
memcpy(buffer, begin + offset, size);
return true;
}
}
private:
const char* begin;
size_t size_;
};
TEST(Serialize, InputFile) {
TestMessageBuilder builder(1);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestInputFile file(serialized.asPtr());
InputFileMessageReader reader(&file, ReaderOptions(), InputStrategy::EAGER);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Serialize, InputFileLazy) {
TestMessageBuilder builder(1);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestInputFile file(serialized.asPtr());
InputFileMessageReader reader(&file, ReaderOptions(), InputStrategy::LAZY);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Serialize, InputFileOddSegmentCount) {
TestMessageBuilder builder(7);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestInputFile file(serialized.asPtr());
InputFileMessageReader reader(&file, ReaderOptions(), InputStrategy::EAGER);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Serialize, InputFileOddSegmentCountLazy) {
TestMessageBuilder builder(7);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestInputFile file(serialized.asPtr());
InputFileMessageReader reader(&file, ReaderOptions(), InputStrategy::LAZY);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Serialize, InputFileEventSegmentCount) {
TestMessageBuilder builder(10);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestInputFile file(serialized.asPtr());
InputFileMessageReader reader(&file, ReaderOptions(), InputStrategy::EAGER);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
TEST(Serialize, InputFileEventSegmentCountLazy) {
TestMessageBuilder builder(10);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestInputFile file(serialized.asPtr());
InputFileMessageReader reader(&file, ReaderOptions(), InputStrategy::LAZY);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
class TestOutputStream: public OutputStream {
public:
TestOutputStream() {}
~TestOutputStream() {}
void write(const void* buffer, size_t size) override {
data.append(reinterpret_cast<const char*>(buffer), size);
}
const bool dataEquals(ArrayPtr<const word> other) {
return data ==
std::string(reinterpret_cast<const char*>(other.begin()), other.size() * sizeof(word));
}
private:
std::string data;
};
TEST(Serialize, WriteMessage) {
TestMessageBuilder builder(1);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestOutputStream output;
writeMessage(output, builder);
EXPECT_TRUE(output.dataEquals(serialized.asPtr()));
}
TEST(Serialize, WriteMessageOddSegmentCount) {
TestMessageBuilder builder(7);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestOutputStream output;
writeMessage(output, builder);
EXPECT_TRUE(output.dataEquals(serialized.asPtr()));
}
TEST(Serialize, WriteMessageEventSegmentCount) {
TestMessageBuilder builder(10);
initTestMessage(builder.initRoot<TestAllTypes>());
Array<word> serialized = messageToFlatArray(builder);
TestOutputStream output;
writeMessage(output, builder);
EXPECT_TRUE(output.dataEquals(serialized.asPtr()));
}
TEST(Serialize, FileDescriptors) {
char filename[] = "/tmp/capnproto-serialize-test-XXXXXX";
AutoCloseFd tmpfile(mkstemp(filename));
ASSERT_GE(tmpfile.get(), 0);
// Unlink the file so that it will be deleted on close.
EXPECT_EQ(0, unlink(filename));
{
TestMessageBuilder builder(7);
initTestMessage(builder.initRoot<TestAllTypes>());
writeMessageToFd(tmpfile.get(), builder);
}
{
TestMessageBuilder builder(1);
builder.initRoot<TestAllTypes>().setTextField("second message in file");
writeMessageToFd(tmpfile.get(), builder);
}
lseek(tmpfile, 0, SEEK_SET);
{
StreamFdMessageReader reader(tmpfile.get());
checkTestMessage(reader.getRoot<TestAllTypes>());
}
{
FileFdMessageReader reader(tmpfile.get(), 0);
checkTestMessage(reader.getRoot<TestAllTypes>());
}
size_t secondStart = lseek(tmpfile, 0, SEEK_CUR);
{
StreamFdMessageReader reader(tmpfile.get());
EXPECT_EQ("second message in file", reader.getRoot<TestAllTypes>().getTextField());
}
{
FileFdMessageReader reader(move(tmpfile), secondStart);
EXPECT_EQ("second message in file", 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.h"
#include "wire-format.h"
#include <string.h>
#include <errno.h>
#include <unistd.h>
namespace capnproto {
FlatArrayMessageReader::FlatArrayMessageReader(ArrayPtr<const word> array, ReaderOptions options)
: MessageReader(options) {
if (array.size() < 1) {
// Assume empty message.
return;
}
const internal::WireValue<uint32_t>* table =
reinterpret_cast<const internal::WireValue<uint32_t>*>(array.begin());
uint segmentCount = table[0].get();
size_t offset = segmentCount / 2u + 1u;
if (array.size() < offset) {
options.errorReporter->reportError("Message ends prematurely in segment table.");
return;
}
if (segmentCount == 0) {
return;
}
uint segmentSize = table[1].get();
if (array.size() < offset + segmentSize) {
options.errorReporter->reportError("Message ends prematurely in first segment.");
return;
}
segment0 = array.slice(offset, offset + segmentSize);
offset += segmentSize;
if (segmentCount > 1) {
moreSegments = newArray<ArrayPtr<const word>>(segmentCount - 1);
for (uint i = 1; i < segmentCount; i++) {
uint segmentSize = table[i + 1].get();
if (array.size() < offset + segmentSize) {
moreSegments = nullptr;
options.errorReporter->reportError("Message ends prematurely.");
return;
}
moreSegments[i - 1] = array.slice(offset, offset + segmentSize);
offset += segmentSize;
}
}
}
ArrayPtr<const word> FlatArrayMessageReader::getSegment(uint id) {
if (id == 0) {
return segment0;
} else if (id <= moreSegments.size()) {
return moreSegments[id - 1];
} else {
return nullptr;
}
}
Array<word> messageToFlatArray(ArrayPtr<const ArrayPtr<const word>> segments) {
size_t totalSize = segments.size() / 2 + 1;
for (auto& segment: segments) {
totalSize += segment.size();
}
Array<word> result = newArray<word>(totalSize);
internal::WireValue<uint32_t>* table =
reinterpret_cast<internal::WireValue<uint32_t>*>(result.begin());
table[0].set(segments.size());
for (uint i = 0; i < segments.size(); i++) {
table[i + 1].set(segments[i].size());
}
if (segments.size() % 2 == 0) {
// Set padding byte.
table[segments.size() + 1].set(0);
}
word* dst = result.begin() + segments.size() / 2 + 1;
for (auto& segment: segments) {
memcpy(dst, segment.begin(), segment.size() * sizeof(word));
dst += segment.size();
}
CAPNPROTO_DEBUG_ASSERT(dst == result.end(), "Buffer overrun/underrun bug in code above.");
return move(result);
}
// =======================================================================================
InputStream::~InputStream() {}
InputFile::~InputFile() {}
OutputStream::~OutputStream() {}
// -------------------------------------------------------------------
InputStreamMessageReader::InputStreamMessageReader(
InputStream* inputStream, ReaderOptions options, InputStrategy inputStrategy)
: MessageReader(options), inputStream(inputStream), segmentsReadSoFar(0) {
internal::WireValue<uint32_t> firstWord[2];
if (!inputStream->read(firstWord, sizeof(firstWord))) return;
uint segmentCount = firstWord[0].get();
segment0.size = segmentCount == 0 ? 0 : firstWord[1].get();
if (segmentCount > 1) {
internal::WireValue<uint32_t> sizes[segmentCount - 1];
if (!inputStream->read(sizes, sizeof(sizes))) return;
moreSegments = newArray<LazySegment>(segmentCount - 1);
for (uint i = 1; i < segmentCount; i++) {
moreSegments[i - 1].size = sizes[i - 1].get();
}
}
if (segmentCount % 2 == 0) {
// Read the padding.
uint32_t pad;
if (!inputStream->read(&pad, sizeof(pad))) return;
}
if (inputStrategy == InputStrategy::EAGER) {
getSegment(segmentCount - 1);
inputStream = nullptr;
}
}
ArrayPtr<const word> InputStreamMessageReader::getSegment(uint id) {
if (id > moreSegments.size()) {
return nullptr;
}
while (segmentsReadSoFar <= id && inputStream != nullptr) {
LazySegment& segment = segmentsReadSoFar == 0 ? segment0 : moreSegments[segmentsReadSoFar - 1];
Array<word> words = newArray<word>(segment.size);
if (!inputStream->read(words.begin(), words.size() * sizeof(word))) {
// There was an error but no exception was thrown, so we're supposed to plod along with
// default values. Discard the broken stream.
inputStream = nullptr;
break;
}
segment.words = move(words);
++segmentsReadSoFar;
}
return id == 0 ? segment0.words.asPtr() : moreSegments[id - 1].words.asPtr();
}
// -------------------------------------------------------------------
InputFileMessageReader::InputFileMessageReader(
InputFile* inputFile, ReaderOptions options, InputStrategy inputStrategy)
: MessageReader(options), inputFile(inputFile) {
internal::WireValue<uint32_t> firstWord[2];
if (!inputFile->read(0, firstWord, sizeof(firstWord))) return;
uint segmentCount = firstWord[0].get();
segment0.size = segmentCount == 0 ? 0 : firstWord[1].get();
if (segmentCount > 1) {
internal::WireValue<uint32_t> sizes[segmentCount - 1];
if (!inputFile->read(sizeof(firstWord), sizes, sizeof(sizes))) return;
uint64_t offset = (segmentCount / 2 + 1) * sizeof(word);
segment0.offset = offset;
offset += segment0.size * sizeof(word);
moreSegments = newArray<LazySegment>(segmentCount - 1);
for (uint i = 1; i < segmentCount; i++) {
uint segmentSize = sizes[i - 1].get();
moreSegments[i - 1].size = segmentSize;
moreSegments[i - 1].offset = offset;
offset += segmentSize * sizeof(word);
}
} else {
segment0.offset = sizeof(firstWord);
}
if (inputStrategy == InputStrategy::EAGER) {
for (uint i = 0; i < segmentCount; i++) {
getSegment(segmentCount);
}
inputFile = nullptr;
}
}
ArrayPtr<const word> InputFileMessageReader::getSegment(uint id) {
if (id > moreSegments.size()) {
return nullptr;
}
LazySegment& segment = id == 0 ? segment0 : moreSegments[id - 1];
if (segment.words == nullptr && segment.size > 0 && inputFile != nullptr) {
Array<word> words = newArray<word>(segment.size);
if (!inputFile->read(segment.offset, words.begin(), words.size() * sizeof(word))) {
// There was an error but no exception was thrown, so we're supposed to plod along with
// default values. Discard the broken stream.
inputFile = nullptr;
} else {
segment.words = move(words);
}
}
return segment.words.asPtr();
}
// -------------------------------------------------------------------
void writeMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> segments) {
internal::WireValue<uint32_t> table[(segments.size() + 2) & ~size_t(1)];
table[0].set(segments.size());
for (uint i = 0; i < segments.size(); i++) {
table[i + 1].set(segments[i].size());
}
if (segments.size() % 2 == 0) {
// Set padding byte.
table[segments.size() + 1].set(0);
}
output.write(table, sizeof(table));
for (auto& segment: segments) {
output.write(segment.begin(), segment.size() * sizeof(word));
}
}
// =======================================================================================
class OsException: public std::exception {
public:
OsException(int error): error(error) {}
~OsException() noexcept {}
const char* what() const noexcept override {
// TODO: Use strerror_r or whatever for thread-safety. Ugh.
return strerror(error);
}
private:
int error;
};
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(errno);
}
}
}
FdInputStream::~FdInputStream() {}
bool FdInputStream::read(void* buffer, size_t size) {
char* pos = reinterpret_cast<char*>(buffer);
while (size > 0) {
ssize_t n = ::read(fd, pos, size);
if (n <= 0) {
if (n < 0) {
// TODO: Use strerror_r or whatever for thread-safety. Ugh.
errorReporter->reportError(strerror(errno));
} else if (n == 0) {
errorReporter->reportError("Stream ended prematurely.");
}
return false;
}
pos += n;
size -= n;
}
return true;
}
FdInputFile::~FdInputFile() {}
bool FdInputFile::read(size_t offset, void* buffer, size_t size) {
char* pos = reinterpret_cast<char*>(buffer);
offset += this->offset;
while (size > 0) {
ssize_t n = ::pread(fd, pos, size, offset);
if (n <= 0) {
if (n < 0) {
// TODO: Use strerror_r or whatever for thread-safety. Ugh.
errorReporter->reportError(strerror(errno));
} else if (n == 0) {
errorReporter->reportError("Stream ended prematurely.");
}
return false;
}
pos += n;
offset += n;
size -= n;
}
return true;
}
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) {
throw OsException(n == 0 ? EIO : errno);
}
pos += n;
size -= n;
}
}
StreamFdMessageReader::~StreamFdMessageReader() {}
FileFdMessageReader::~FileFdMessageReader() {}
void writeMessageToFd(int fd, ArrayPtr<const ArrayPtr<const word>> segments) {
FdOutputStream stream(fd);
writeMessage(stream, 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.
// This file implements a simple serialization format for Cap'n Proto messages. The format
// is as follows:
//
// * 32-bit little-endian segment count (4 bytes).
// * 32-bit little-endian size of each segment (4*(segment count) bytes).
// * Padding so that subsequent data is 64-bit-aligned (0 or 4 bytes). (I.e., if there are an even
// number of segments, there are 4 bytes of zeros here, otherwise there is no padding.)
// * Data from each segment, in order (8*sum(segment sizes) bytes)
//
// This format has some important properties:
// - It is self-delimiting, so multiple messages may be written to a stream without any external
// delimiter.
// - The total size and position of each segment can be determined by reading only the first part
// of the message, allowing lazy and random-access reading of the segment data.
// - A message is always at least 8 bytes.
// - A single-segment message can be read entirely in two system calls with no buffering.
// - A multi-segment message can be read entirely in three system calls with no buffering.
// - The format is appropriate for mmap()ing since all data is aligned.
#ifndef CAPNPROTO_SERIALIZE_H_
#define CAPNPROTO_SERIALIZE_H_
#include "message.h"
namespace capnproto {
class FlatArrayMessageReader: public MessageReader {
// Parses a message from a flat array. Note that it makes sense to use this together with mmap()
// for extremely fast parsing.
public:
FlatArrayMessageReader(ArrayPtr<const word> array, ReaderOptions options = ReaderOptions());
// The array must remain valid until the MessageReader is destroyed.
ArrayPtr<const word> getSegment(uint id) override;
private:
// Optimize for single-segment case.
ArrayPtr<const word> segment0;
Array<ArrayPtr<const word>> moreSegments;
};
Array<word> messageToFlatArray(MessageBuilder& builder);
// Constructs a flat array containing the entire content of the given message.
Array<word> messageToFlatArray(ArrayPtr<const ArrayPtr<const word>> segments);
// Version of messageToFlatArray that takes a raw segment array.
// =======================================================================================
class InputStream {
public:
virtual ~InputStream();
virtual bool read(void* buffer, size_t size) = 0;
// Always reads the full size requested. Returns true if successful. May throw an exception
// on failure, or report the error through some side channel and return false.
};
class InputFile {
public:
virtual ~InputFile();
virtual bool read(size_t offset, void* buffer, size_t size) = 0;
// Always reads the full size requested. Returns true if successful. May throw an exception
// on failure, or report the error through some side channel and return false.
};
class OutputStream {
public:
virtual ~OutputStream();
virtual void write(const void* buffer, size_t size) = 0;
// Throws exception on error, or reports errors via some side channel and returns.
};
enum class InputStrategy {
EAGER,
// Read the whole message into RAM upfront, in the MessageReader constructor. When reading from
// an InputStream, the stream will then be positioned at the byte immediately after the end of
// the message, and will not be accessed again.
LAZY
// Read segments of the message into RAM as needed while the message structure is being traversed.
// When reading from an InputStream, segments must be read in order, so segments up to the
// required segment will also be read. No guarantee is made about the position of the InputStream
// after reading. When using an InputFile, only the exact segments desired are read.
};
class InputStreamMessageReader: public MessageReader {
public:
InputStreamMessageReader(InputStream* inputStream,
ReaderOptions options = ReaderOptions(),
InputStrategy inputStrategy = InputStrategy::EAGER);
// implements MessageReader ----------------------------------------
ArrayPtr<const word> getSegment(uint id) override;
private:
InputStream* inputStream;
uint segmentsReadSoFar;
struct LazySegment {
uint size;
Array<word> words; // null until actually read
inline LazySegment(): size(0), words(nullptr) {}
};
// Optimize for single-segment case.
LazySegment segment0;
Array<LazySegment> moreSegments;
};
class InputFileMessageReader: public MessageReader {
public:
InputFileMessageReader(InputFile* inputFile,
ReaderOptions options = ReaderOptions(),
InputStrategy inputStrategy = InputStrategy::EAGER);
// implements MessageReader ----------------------------------------
ArrayPtr<const word> getSegment(uint id) override;
private:
InputFile* inputFile;
struct LazySegment {
uint size;
size_t offset;
Array<word> words; // null until actually read
inline LazySegment(): size(0), offset(0), words(nullptr) {}
};
// Optimize for single-segment case.
LazySegment segment0;
Array<LazySegment> moreSegments;
};
void writeMessage(OutputStream& output, MessageBuilder& builder);
// Write the message to the given output stream.
void writeMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> segments);
// Write the segment array to the given output stream.
// =======================================================================================
// 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, ErrorReporter* errorReporter = getThrowingErrorReporter())
: fd(fd), errorReporter(errorReporter) {};
FdInputStream(AutoCloseFd fd, ErrorReporter* errorReporter = getThrowingErrorReporter())
: fd(fd), autoclose(move(fd)), errorReporter(errorReporter) {}
~FdInputStream();
bool read(void* buffer, size_t size) override;
private:
int fd;
AutoCloseFd autoclose;
ErrorReporter* errorReporter;
};
class FdInputFile: public InputFile {
// An InputFile wrapping a file descriptor. The file descriptor must be seekable. This
// implementation uses pread(), so the stream pointer will not be modified.
public:
FdInputFile(int fd, size_t offset, ErrorReporter* errorReporter = getThrowingErrorReporter())
: fd(fd), offset(offset), errorReporter(errorReporter) {};
FdInputFile(AutoCloseFd fd, size_t offset,
ErrorReporter* errorReporter = getThrowingErrorReporter())
: fd(fd), autoclose(move(fd)), offset(offset), errorReporter(errorReporter) {}
~FdInputFile();
bool read(size_t offset, void* buffer, size_t size) override;
private:
int fd;
AutoCloseFd autoclose;
size_t offset;
ErrorReporter* errorReporter;
};
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;
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.
public:
StreamFdMessageReader(int fd, ReaderOptions options = ReaderOptions(),
InputStrategy inputStrategy = InputStrategy::EAGER)
: FdInputStream(fd), InputStreamMessageReader(this, options, inputStrategy) {}
// 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(),
InputStrategy inputStrategy = InputStrategy::LAZY)
: FdInputStream(move(fd)), InputStreamMessageReader(this, options, inputStrategy) {}
// 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();
};
class FileFdMessageReader: private FdInputFile, public InputFileMessageReader {
// A MessageReader that reads from a seekable file descriptor, e.g. disk files. For non-seekable
// file descriptors, use FdStreamMessageReader. This implementation uses pread(), so the file
// descriptor's stream pointer will not be modified.
public:
FileFdMessageReader(int fd, size_t offset, ReaderOptions options = ReaderOptions(),
InputStrategy inputStrategy = InputStrategy::LAZY)
: FdInputFile(fd, offset, options.errorReporter),
InputFileMessageReader(this, options, inputStrategy) {}
// Read message from a file descriptor, without taking ownership of the descriptor.
//
// All reads use pread(), so the file descriptor's stream pointer will not be modified.
FileFdMessageReader(AutoCloseFd fd, size_t offset, ReaderOptions options = ReaderOptions(),
InputStrategy inputStrategy = InputStrategy::LAZY)
: FdInputFile(move(fd), offset, options.errorReporter),
InputFileMessageReader(this, options, inputStrategy) {}
// Read a message from a file descriptor, taking ownership of the descriptor.
//
// All reads use pread(), so the file descriptor's stream pointer will not be modified.
~FileFdMessageReader();
};
void writeMessageToFd(int fd, MessageBuilder& builder);
// Write the message to the given file descriptor.
//
// This function throws an exception on any I/O error. If your code is not exception-safe, be sure
// you catch this exception at the call site. If throwing an exception is not acceptable, you
// can implement your own OutputStream with arbitrary error handling and then use writeMessage().
void writeMessageToFd(int fd, ArrayPtr<const ArrayPtr<const word>> segments);
// Write the segment array to the given file descriptor.
//
// This function throws an exception on any I/O error. If your code is not exception-safe, be sure
// you catch this exception at the call site. If throwing an exception is not acceptable, you
// can implement your own OutputStream with arbitrary error handling and then use writeMessage().
// =======================================================================================
// inline stuff
inline Array<word> messageToFlatArray(MessageBuilder& builder) {
return messageToFlatArray(builder.getSegmentsForOutput());
}
inline void writeMessage(OutputStream& output, MessageBuilder& builder) {
writeMessage(output, builder.getSegmentsForOutput());
}
inline void writeMessageToFd(int fd, MessageBuilder& builder) {
writeMessageToFd(fd, builder.getSegmentsForOutput());
}
} // namespace capnproto
#endif // SERIALIZE_H_
// 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-util.h"
#include <gtest/gtest.h>
namespace capnproto {
namespace internal {
namespace {
template <typename Builder>
void genericInitTestMessage(Builder builder) {
builder.setVoidField(Void::VOID);
builder.setBoolField(true);
builder.setInt8Field(-123);
builder.setInt16Field(-12345);
builder.setInt32Field(-12345678);
builder.setInt64Field(-123456789012345ll);
builder.setUInt8Field(234u);
builder.setUInt16Field(45678u);
builder.setUInt32Field(3456789012u);
builder.setUInt64Field(12345678901234567890ull);
builder.setFloat32Field(1234.5);
builder.setFloat64Field(-123e45);
builder.setTextField("foo");
builder.setDataField("bar");
{
auto subBuilder = builder.initStructField();
subBuilder.setVoidField(Void::VOID);
subBuilder.setBoolField(true);
subBuilder.setInt8Field(-12);
subBuilder.setInt16Field(3456);
subBuilder.setInt32Field(-78901234);
subBuilder.setInt64Field(56789012345678ll);
subBuilder.setUInt8Field(90u);
subBuilder.setUInt16Field(1234u);
subBuilder.setUInt32Field(56789012u);
subBuilder.setUInt64Field(345678901234567890ull);
subBuilder.setFloat32Field(-1.25e-10);
subBuilder.setFloat64Field(345);
subBuilder.setTextField("baz");
subBuilder.setDataField("qux");
{
auto subSubBuilder = subBuilder.initStructField();
subSubBuilder.setTextField("nested");
subSubBuilder.initStructField().setTextField("really nested");
}
subBuilder.setVoidList({Void::VOID, Void::VOID, Void::VOID});
subBuilder.setBoolList({false, true, false, true, true});
subBuilder.setInt8List({12, -34, -0x80, 0x7f});
subBuilder.setInt16List({1234, -5678, -0x8000, 0x7fff});
subBuilder.setInt32List({12345678, -90123456, -0x8000000, 0x7ffffff});
// gcc warns on -0x800...ll and the only work-around I could find was to do -0x7ff...ll-1.
subBuilder.setInt64List({123456789012345ll, -678901234567890ll, -0x7fffffffffffffffll-1, 0x7fffffffffffffffll});
subBuilder.setUInt8List({12u, 34u, 0u, 0xffu});
subBuilder.setUInt16List({1234u, 5678u, 0u, 0xffffu});
subBuilder.setUInt32List({12345678u, 90123456u, 0u, 0xffffffffu});
subBuilder.setUInt64List({123456789012345ull, 678901234567890ull, 0ull, 0xffffffffffffffffull});
subBuilder.setFloat32List({0, 1234567, 1e37, -1e37, 1e-37, -1e-37});
subBuilder.setFloat64List({0, 123456789012345, 1e306, -1e306, 1e-306, -1e-306});
subBuilder.setTextList({"quux", "corge", "grault"});
subBuilder.setDataList({"garply", "waldo", "fred"});
{
auto listBuilder = subBuilder.initStructList(3);
listBuilder[0].setTextField("x structlist 1");
listBuilder[1].setTextField("x structlist 2");
listBuilder[2].setTextField("x structlist 3");
}
}
builder.initVoidList(6);
builder.setBoolList({true, false, false, true});
builder.setInt8List({111, -111});
builder.setInt16List({11111, -11111});
builder.setInt32List({111111111, -111111111});
builder.setInt64List({1111111111111111111ll, -1111111111111111111ll});
builder.setUInt8List({111u, 222u});
builder.setUInt16List({33333u, 44444u});
builder.setUInt32List({3333333333u});
builder.setUInt64List({11111111111111111111ull});
builder.setFloat32List({5555.5, 2222.25});
builder.setFloat64List({7777.75, 1111.125});
builder.setTextList({"plugh", "xyzzy", "thud"});
builder.setDataList({"oops", "exhausted", "rfc3092"});
{
auto listBuilder = builder.initStructList(3);
listBuilder[0].setTextField("structlist 1");
listBuilder[1].setTextField("structlist 2");
listBuilder[2].setTextField("structlist 3");
}
}
template <typename T, typename U>
void checkList(T reader, std::initializer_list<U> expected) {
ASSERT_EQ(expected.size(), reader.size());
for (uint i = 0; i < expected.size(); i++) {
EXPECT_EQ(expected.begin()[i], reader[i]);
}
}
template <typename T>
void checkList(T reader, std::initializer_list<float> expected) {
ASSERT_EQ(expected.size(), reader.size());
for (uint i = 0; i < expected.size(); i++) {
EXPECT_FLOAT_EQ(expected.begin()[i], reader[i]);
}
}
template <typename T>
void checkList(T reader, std::initializer_list<double> expected) {
ASSERT_EQ(expected.size(), reader.size());
for (uint i = 0; i < expected.size(); i++) {
EXPECT_DOUBLE_EQ(expected.begin()[i], reader[i]);
}
}
template <typename Reader>
void genericCheckTestMessage(Reader reader) {
EXPECT_EQ(Void::VOID, reader.getVoidField());
EXPECT_EQ(true, reader.getBoolField());
EXPECT_EQ(-123, reader.getInt8Field());
EXPECT_EQ(-12345, reader.getInt16Field());
EXPECT_EQ(-12345678, reader.getInt32Field());
EXPECT_EQ(-123456789012345ll, reader.getInt64Field());
EXPECT_EQ(234u, reader.getUInt8Field());
EXPECT_EQ(45678u, reader.getUInt16Field());
EXPECT_EQ(3456789012u, reader.getUInt32Field());
EXPECT_EQ(12345678901234567890ull, reader.getUInt64Field());
EXPECT_FLOAT_EQ(1234.5f, reader.getFloat32Field());
EXPECT_DOUBLE_EQ(-123e45, reader.getFloat64Field());
EXPECT_EQ("foo", reader.getTextField());
EXPECT_EQ("bar", reader.getDataField());
{
auto subReader = reader.getStructField();
EXPECT_EQ(Void::VOID, subReader.getVoidField());
EXPECT_EQ(true, subReader.getBoolField());
EXPECT_EQ(-12, subReader.getInt8Field());
EXPECT_EQ(3456, subReader.getInt16Field());
EXPECT_EQ(-78901234, subReader.getInt32Field());
EXPECT_EQ(56789012345678ll, subReader.getInt64Field());
EXPECT_EQ(90u, subReader.getUInt8Field());
EXPECT_EQ(1234u, subReader.getUInt16Field());
EXPECT_EQ(56789012u, subReader.getUInt32Field());
EXPECT_EQ(345678901234567890ull, subReader.getUInt64Field());
EXPECT_FLOAT_EQ(-1.25e-10f, subReader.getFloat32Field());
EXPECT_DOUBLE_EQ(345, subReader.getFloat64Field());
EXPECT_EQ("baz", subReader.getTextField());
EXPECT_EQ("qux", subReader.getDataField());
{
auto subSubReader = subReader.getStructField();
EXPECT_EQ("nested", subSubReader.getTextField());
EXPECT_EQ("really nested", subSubReader.getStructField().getTextField());
}
checkList(subReader.getVoidList(), {Void::VOID, Void::VOID, Void::VOID});
checkList(subReader.getBoolList(), {false, true, false, true, true});
checkList(subReader.getInt8List(), {12, -34, -0x80, 0x7f});
checkList(subReader.getInt16List(), {1234, -5678, -0x8000, 0x7fff});
checkList(subReader.getInt32List(), {12345678, -90123456, -0x8000000, 0x7ffffff});
// gcc warns on -0x800...ll and the only work-around I could find was to do -0x7ff...ll-1.
checkList(subReader.getInt64List(), {123456789012345ll, -678901234567890ll, -0x7fffffffffffffffll-1, 0x7fffffffffffffffll});
checkList(subReader.getUInt8List(), {12u, 34u, 0u, 0xffu});
checkList(subReader.getUInt16List(), {1234u, 5678u, 0u, 0xffffu});
checkList(subReader.getUInt32List(), {12345678u, 90123456u, 0u, 0xffffffffu});
checkList(subReader.getUInt64List(), {123456789012345ull, 678901234567890ull, 0ull, 0xffffffffffffffffull});
checkList(subReader.getFloat32List(), {0.0f, 1234567.0f, 1e37f, -1e37f, 1e-37f, -1e-37f});
checkList(subReader.getFloat64List(), {0.0, 123456789012345.0, 1e306, -1e306, 1e-306, -1e-306});
checkList(subReader.getTextList(), {"quux", "corge", "grault"});
checkList(subReader.getDataList(), {"garply", "waldo", "fred"});
{
auto listReader = subReader.getStructList();
ASSERT_EQ(3u, listReader.size());
EXPECT_EQ("x structlist 1", listReader[0].getTextField());
EXPECT_EQ("x structlist 2", listReader[1].getTextField());
EXPECT_EQ("x structlist 3", listReader[2].getTextField());
}
}
EXPECT_EQ(6u, reader.getVoidList().size());
checkList(reader.getBoolList(), {true, false, false, true});
checkList(reader.getInt8List(), {111, -111});
checkList(reader.getInt16List(), {11111, -11111});
checkList(reader.getInt32List(), {111111111, -111111111});
checkList(reader.getInt64List(), {1111111111111111111ll, -1111111111111111111ll});
checkList(reader.getUInt8List(), {111u, 222u});
checkList(reader.getUInt16List(), {33333u, 44444u});
checkList(reader.getUInt32List(), {3333333333u});
checkList(reader.getUInt64List(), {11111111111111111111ull});
checkList(reader.getFloat32List(), {5555.5f, 2222.25f});
checkList(reader.getFloat64List(), {7777.75, 1111.125});
checkList(reader.getTextList(), {"plugh", "xyzzy", "thud"});
checkList(reader.getDataList(), {"oops", "exhausted", "rfc3092"});
{
auto listReader = reader.getStructList();
ASSERT_EQ(3u, listReader.size());
EXPECT_EQ("structlist 1", listReader[0].getTextField());
EXPECT_EQ("structlist 2", listReader[1].getTextField());
EXPECT_EQ("structlist 3", listReader[2].getTextField());
}
}
} // namespace
void initTestMessage(TestAllTypes::Builder builder) { genericInitTestMessage(builder); }
void initTestMessage(TestDefaults::Builder builder) { genericInitTestMessage(builder); }
void checkTestMessage(TestAllTypes::Builder builder) { genericCheckTestMessage(builder); }
void checkTestMessage(TestDefaults::Builder builder) { genericCheckTestMessage(builder); }
void checkTestMessage(TestAllTypes::Reader reader) { genericCheckTestMessage(reader); }
void checkTestMessage(TestDefaults::Reader reader) { genericCheckTestMessage(reader); }
} // 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.
#ifndef CAPNPROTO_TEST_UTIL_H_
#define CAPNPROTO_TEST_UTIL_H_
#include "test.capnp.h"
namespace capnproto {
namespace internal {
void initTestMessage(TestAllTypes::Builder builder);
void initTestMessage(TestDefaults::Builder builder);
void checkTestMessage(TestAllTypes::Builder builder);
void checkTestMessage(TestDefaults::Builder builder);
void checkTestMessage(TestAllTypes::Reader reader);
void checkTestMessage(TestDefaults::Reader reader);
} // namespace internal
} // namespace capnproto
#endif // TEST_UTIL_H_
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