Commit 6a72d324 authored by Kenton Varda's avatar Kenton Varda

Rename the 'Message' interfaces to Arena and make them internal. Make a new,…

Rename the 'Message' interfaces to Arena and make them internal.  Make a new, intuitive 'Message' interface for creating / reading messages.
parent 20b1ea55
// 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 "arena.h"
#include "message.h"
#include <vector>
#include <string.h>
#include <iostream>
namespace capnproto {
namespace internal {
Arena::~Arena() {}
// =======================================================================================
ReaderArena::ReaderArena(
ArrayPtr<const ArrayPtr<const word>> segments,
ErrorReporter* errorReporter,
WordCount64 readLimit)
: segments(segments),
errorReporter(errorReporter),
readLimiter(readLimit) {
segmentReaders.reserve(segments.size());
uint i = 0;
for (auto segment: segments) {
segmentReaders.emplace_back(new SegmentReader(this, SegmentId(i++), segment, &readLimiter));
}
}
ReaderArena::~ReaderArena() {}
ArrayPtr<const ArrayPtr<const word>> ReaderArena::getSegmentsForOutput() {
return segments;
}
SegmentReader* ReaderArena::tryGetSegment(SegmentId id) {
if (id.value >= segments.size()) {
return nullptr;
} else {
return segmentReaders[id.value].get();
}
}
void ReaderArena::reportInvalidData(const char* description) {
errorReporter->reportError(description);
}
void ReaderArena::reportReadLimitReached() {
errorReporter->reportError("Exceeded read limit.");
}
// =======================================================================================
BuilderArena::BuilderArena(Allocator* allocator): allocator(allocator) {}
BuilderArena::~BuilderArena() {
// TODO: This is wrong because we aren't taking into account how much of each segment is actually
// allocated.
uint i = 0;
for (ArrayPtr<word> ptr: memory) {
// The memory array contains Array<const word> only to ease implementation of getSegmentsForOutput().
// We actually own this space and can de-constify it.
allocator->free(SegmentId(i++), ptr);
}
}
SegmentBuilder* BuilderArena::getSegment(SegmentId id) {
return segments[id.value].get();
}
SegmentBuilder* BuilderArena::getSegmentWithAvailable(WordCount minimumAvailable) {
if (segments.empty() || segments.back()->available() < minimumAvailable) {
ArrayPtr<word> array = allocator->allocate(
SegmentId(segments.size()), minimumAvailable / WORDS);
memory.push_back(array);
segments.push_back(std::unique_ptr<SegmentBuilder>(new SegmentBuilder(
this, SegmentId(segments.size()), array, &dummyLimiter)));
}
return segments.back().get();
}
ArrayPtr<const ArrayPtr<const word>> BuilderArena::getSegmentsForOutput() {
segmentsForOutput.resize(segments.size());
for (uint i = 0; i < segments.size(); i++) {
segmentsForOutput[i] = segments[i]->currentlyAllocated();
}
return arrayPtr(&*segmentsForOutput.begin(), segmentsForOutput.size());
}
SegmentReader* BuilderArena::tryGetSegment(SegmentId id) {
if (id.value >= segments.size()) {
return nullptr;
} else {
return segments[id.value].get();
}
}
void BuilderArena::reportInvalidData(const char* description) {
// TODO: Better error reporting.
std::cerr << "BuilderArena: Parse error: " << description << std::endl;
}
void BuilderArena::reportReadLimitReached() {
// TODO: Better error reporting.
std::cerr << "BuilderArena: Exceeded read limit." << std::endl;
}
} // namespace internal
} // namespace capnproto
This diff is collapsed.
...@@ -227,56 +227,86 @@ void checkMessage(Reader reader) { ...@@ -227,56 +227,86 @@ void checkMessage(Reader reader) {
} }
TEST(Encoding, AllTypes) { TEST(Encoding, AllTypes) {
auto root = newMallocMessageRoot<TestAllTypes>(); Message<TestAllTypes>::Builder builder;
initMessage(root.builder); initMessage(builder.initRoot());
checkMessage(root.builder); checkMessage(builder.getRoot());
checkMessage(root.builder.asReader()); checkMessage(builder.getRoot().asReader());
Message<TestAllTypes>::Reader reader(
builder.getSegmentsForOutput(), 64, 1 << 30, ThrowingErrorReporter::getDefaultInstance());
checkMessage(reader.getRoot());
ASSERT_EQ(1u, builder.getSegmentsForOutput().size());
checkMessage(Message<TestAllTypes>::readTrusted(builder.getSegmentsForOutput()[0].begin()));
} }
TEST(Encoding, AllTypesMultiSegment) { TEST(Encoding, AllTypesMultiSegment) {
auto root = newMallocMessageRoot<TestAllTypes>(0 * WORDS); MallocAllocator allocator(0);
Message<TestAllTypes>::Builder builder(&allocator);
initMessage(root.builder); initMessage(builder.initRoot());
checkMessage(root.builder); checkMessage(builder.getRoot());
checkMessage(root.builder.asReader()); checkMessage(builder.getRoot().asReader());
Message<TestAllTypes>::Reader reader(
builder.getSegmentsForOutput(), 64, 1 << 30, ThrowingErrorReporter::getDefaultInstance());
checkMessage(reader.getRoot());
} }
TEST(Encoding, Defaults) { TEST(Encoding, Defaults) {
auto root = newMallocMessageRoot<TestDefaults>(); AlignedData<1> nullRoot = {{0, 0, 0, 0, 0, 0, 0, 0}};
ArrayPtr<const word> segments[1] = {arrayPtr(nullRoot.words, 1)};
Message<TestDefaults>::Reader reader(arrayPtr(segments, 1), 64, 1 << 30,
ThrowingErrorReporter::getDefaultInstance());
checkMessage(root.builder.asReader()); checkMessage(reader.getRoot());
checkMessage(Message<TestDefaults>::readTrusted(nullRoot.words));
} }
TEST(Encoding, DefaultInitialization) { TEST(Encoding, DefaultInitialization) {
auto root = newMallocMessageRoot<TestDefaults>(); Message<TestDefaults>::Builder builder;
checkMessage(builder.getRoot()); // first pass initializes to defaults
checkMessage(builder.getRoot().asReader());
checkMessage(root.builder); checkMessage(builder.getRoot()); // second pass just reads the initialized structure
checkMessage(root.builder.asReader()); checkMessage(builder.getRoot().asReader());
Message<TestDefaults>::Reader reader(
builder.getSegmentsForOutput(), 64, 1 << 30, ThrowingErrorReporter::getDefaultInstance());
checkMessage(reader.getRoot());
} }
TEST(Encoding, DefaultInitializationMultiSegment) { TEST(Encoding, DefaultInitializationMultiSegment) {
auto root = newMallocMessageRoot<TestDefaults>(0 * WORDS); MallocAllocator allocator(0);
Message<TestDefaults>::Builder builder(&allocator);
checkMessage(builder.getRoot()); // first pass initializes to defaults
checkMessage(builder.getRoot().asReader());
checkMessage(root.builder); checkMessage(builder.getRoot()); // second pass just reads the initialized structure
checkMessage(root.builder.asReader()); checkMessage(builder.getRoot().asReader());
Message<TestDefaults>::Reader reader(
builder.getSegmentsForOutput(), 64, 1 << 30, ThrowingErrorReporter::getDefaultInstance());
checkMessage(reader.getRoot());
} }
TEST(Encoding, DefaultsNotOnWire) { TEST(Encoding, DefaultsFromEmptyMessage) {
AlignedData<1> emptyMessage = {{4, 0, 0, 0, 0, 0, 0, 0}}; AlignedData<1> emptyMessage = {{4, 0, 0, 0, 0, 0, 0, 0}};
std::unique_ptr<MessageBuilder> message = newMallocMessage(512 * WORDS); ArrayPtr<const word> segments[1] = {arrayPtr(emptyMessage.words, 1)};
SegmentBuilder* segment = message->getSegmentWithAvailable(1 * WORDS); Message<TestDefaults>::Reader reader(arrayPtr(segments, 1), 64, 1 << 30,
word* rootLocation = segment->allocate(1 * WORDS); ThrowingErrorReporter::getDefaultInstance());
memcpy(rootLocation, emptyMessage.words, sizeof(word));
TestDefaults::Reader reader(StructReader::readRoot(
emptyMessage.words, TestDefaults::DEFAULT.words, segment, 64));
checkMessage(reader);
TestDefaults::Reader reader2(StructReader::readRootTrusted( checkMessage(reader.getRoot());
emptyMessage.words, TestDefaults::DEFAULT.words)); checkMessage(Message<TestDefaults>::readTrusted(emptyMessage.words));
checkMessage(reader2);
} }
} // namespace } // namespace
......
...@@ -22,10 +22,9 @@ ...@@ -22,10 +22,9 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "macros.h" #include "macros.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <exception> #include <exception>
#include <string>
#include <unistd.h>
namespace capnproto { namespace capnproto {
namespace internal { namespace internal {
...@@ -35,39 +34,33 @@ class Exception: public std::exception { ...@@ -35,39 +34,33 @@ class Exception: public std::exception {
public: public:
Exception(const char* file, int line, const char* expectation, const char* message); Exception(const char* file, int line, const char* expectation, const char* message);
virtual ~Exception() noexcept; ~Exception() noexcept;
const char* getFile() { return file; } const char* what() const noexcept override;
int getLine() { return line; }
const char* getExpectation() { return expectation; }
const char* getMessage() { return message; }
virtual const char* what();
private: private:
const char* file; std::string description;
int line;
const char* expectation;
const char* message;
char* whatBuffer;
}; };
Exception::Exception( Exception::Exception(
const char* file, int line, const char* expectation, const char* message) const char* file, int line, const char* expectation, const char* message) {
: file(file), line(line), expectation(expectation), message(message), whatBuffer(nullptr) { description = "Captain Proto debug assertion failed:\n ";
fprintf(stderr, "Captain Proto debug assertion failed:\n %s:%d: %s\n %s", description += file;
file, line, expectation, message); description += ':';
} description += line;
description += ": ";
description += expectation;
description += "\n ";
description += message;
description += "\n";
Exception::~Exception() noexcept { write(STDERR_FILENO, description.data(), description.size());
delete [] whatBuffer;
} }
const char* Exception::what() { Exception::~Exception() noexcept {}
whatBuffer = new char[strlen(file) + strlen(expectation) + strlen(message) + 256];
sprintf(whatBuffer, "Captain Proto debug assertion failed:\n %s:%d: %s\n %s", const char* Exception::what() const noexcept {
file, line, expectation, message); return description.c_str();
return whatBuffer;
} }
void assertionFailure(const char* file, int line, const char* expectation, const char* message) { void assertionFailure(const char* file, int line, const char* expectation, const char* message) {
......
// 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 header contains internal interfaces relied upon by message.h and implemented in message.c++.
// These declarations should be thought of as being part of message.h, but I moved them here to make
// message.h more readable. The problem is that the interface people really care about in
// message.h -- namely, Message -- has to be declared after internal::Message. Having an internal
// interface appear in the middle of the header ahead of the public interface was distracting and
// confusing.
#ifndef CAPNPROTO_MESSAGE_INTERNAL_H_
#define CAPNPROTO_MESSAGE_INTERNAL_H_
#include <cstddef>
#include <memory>
#include "type-safety.h"
#include "wire-format.h"
namespace capnproto {
class Allocator;
class ErrorReporter;
}
namespace capnproto {
namespace internal {
// TODO: Move to message-internal.h so that this header looks nicer?
class Arena;
class BuilderArena;
struct MessageImpl {
// Underlying implementation of capnproto::Message. All the parts that don't need to be templated
// are implemented by this class, so that they can be shared and non-inline.
MessageImpl() = delete;
class Reader {
public:
Reader(ArrayPtr<const ArrayPtr<const word>> segments,
uint recursionLimit, uint64_t readLimit, ErrorReporter* errorReporter);
Reader(Reader&& other) = default;
CAPNPROTO_DISALLOW_COPY(Reader);
~Reader();
StructReader getRoot(const word* defaultValue);
private:
std::unique_ptr<Arena> arena;
uint recursionLimit;
};
class Builder {
public:
Builder();
Builder(Allocator* allocator);
Builder(Builder&& other) = default;
CAPNPROTO_DISALLOW_COPY(Builder);
~Builder();
StructBuilder initRoot(const word* defaultValue);
StructBuilder getRoot(const word* defaultValue);
ArrayPtr<const ArrayPtr<const word>> getSegmentsForOutput();
private:
std::unique_ptr<BuilderArena> arena;
SegmentBuilder* rootSegment;
static SegmentBuilder* allocateRoot(BuilderArena* arena);
};
};
} // namespace internal
} // namespace capnproto
#endif // CAPNPROTO_MESSAGE_INTERNAL_H_
...@@ -22,75 +22,131 @@ ...@@ -22,75 +22,131 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "message.h" #include "message.h"
#include <vector> #include "arena.h"
#include <string.h> #include "stdlib.h"
#include <iostream> #include <exception>
#include <stdlib.h> #include <string>
#include <unistd.h>
namespace capnproto { namespace capnproto {
MessageReader::~MessageReader() {} Allocator::~Allocator() {}
MessageBuilder::~MessageBuilder() {} ErrorReporter::~ErrorReporter() {}
class MallocMessage: public MessageBuilder { MallocAllocator::MallocAllocator(uint preferredSegmentSizeWords)
: preferredSegmentSizeWords(preferredSegmentSizeWords) {}
MallocAllocator::~MallocAllocator() {}
MallocAllocator* MallocAllocator::getDefaultInstance() {
static MallocAllocator defaultInstance(1024);
return &defaultInstance;
}
ArrayPtr<word> MallocAllocator::allocate(SegmentId id, uint minimumSize) {
uint size = std::max(minimumSize, preferredSegmentSizeWords);
return arrayPtr(reinterpret_cast<word*>(calloc(size, sizeof(word))), size);
}
void MallocAllocator::free(SegmentId id, ArrayPtr<word> ptr) {
::free(ptr.begin());
}
StderrErrorReporter::~StderrErrorReporter() {}
StderrErrorReporter* StderrErrorReporter::getDefaultInstance() {
static StderrErrorReporter defaultInstance;
return &defaultInstance;
}
void StderrErrorReporter::reportError(const char* description) {
std::string message("ERROR: Cap'n Proto parse error: ");
message += description;
message += '\n';
write(STDERR_FILENO, message.data(), message.size());
}
class ParseException: public std::exception {
public: public:
MallocMessage(WordCount preferredSegmentSize); ParseException(const char* description);
~MallocMessage(); ~ParseException() noexcept;
SegmentReader* tryGetSegment(SegmentId id); const char* what() const noexcept override;
void reportInvalidData(const char* description);
void reportReadLimitReached();
SegmentBuilder* getSegment(SegmentId id);
SegmentBuilder* getSegmentWithAvailable(WordCount minimumAvailable);
private: private:
WordCount preferredSegmentSize; std::string description;
std::vector<std::unique_ptr<SegmentBuilder>> segments;
std::vector<word*> memory;
}; };
MallocMessage::MallocMessage(WordCount preferredSegmentSize) ParseException::ParseException(const char* description)
: preferredSegmentSize(preferredSegmentSize) {} : description(description) {}
MallocMessage::~MallocMessage() {
for (word* ptr: memory) { ParseException::~ParseException() noexcept {}
free(ptr);
} const char* ParseException::what() const noexcept {
return description.c_str();
}
ThrowingErrorReporter::~ThrowingErrorReporter() {}
ThrowingErrorReporter* ThrowingErrorReporter::getDefaultInstance() {
static ThrowingErrorReporter defaultInstance;
return &defaultInstance;
}
void ThrowingErrorReporter::reportError(const char* description) {
throw ParseException(description);
} }
SegmentReader* MallocMessage::tryGetSegment(SegmentId id) { // =======================================================================================
if (id.value >= segments.size()) {
return nullptr; namespace internal {
MessageImpl::Reader::Reader(ArrayPtr<const ArrayPtr<const word>> segments,
uint recursionLimit, uint64_t readLimit, ErrorReporter* errorReporter)
: arena(new ReaderArena(segments, errorReporter, readLimit * WORDS)),
recursionLimit(recursionLimit) {}
MessageImpl::Reader::~Reader() {}
StructReader MessageImpl::Reader::getRoot(const word* defaultValue) {
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.");
return StructReader::readRootTrusted(defaultValue, defaultValue);
} else { } else {
return segments[id.value].get(); return StructReader::readRoot(segment->getStartPtr(), defaultValue, segment, recursionLimit);
} }
} }
void MallocMessage::reportInvalidData(const char* description) { MessageImpl::Builder::Builder()
// TODO: Better error reporting. : arena(new BuilderArena(MallocAllocator::getDefaultInstance())),
std::cerr << "MallocMessage: Parse error: " << description << std::endl; rootSegment(allocateRoot(arena.get())) {}
} MessageImpl::Builder::Builder(Allocator* allocator)
: arena(new BuilderArena(allocator)),
rootSegment(allocateRoot(arena.get())) {}
MessageImpl::Builder::~Builder() {}
void MallocMessage::reportReadLimitReached() { StructBuilder MessageImpl::Builder::initRoot(const word* defaultValue) {
// TODO: Better error reporting. return StructBuilder::initRoot(
std::cerr << "MallocMessage: Exceeded read limit." << std::endl; rootSegment, rootSegment->getPtrUnchecked(0 * WORDS), defaultValue);
} }
SegmentBuilder* MallocMessage::getSegment(SegmentId id) { StructBuilder MessageImpl::Builder::getRoot(const word* defaultValue) {
return segments[id.value].get(); return StructBuilder::getRoot(rootSegment, rootSegment->getPtrUnchecked(0 * WORDS), defaultValue);
} }
SegmentBuilder* MallocMessage::getSegmentWithAvailable(WordCount minimumAvailable) { ArrayPtr<const ArrayPtr<const word>> MessageImpl::Builder::getSegmentsForOutput() {
if (segments.empty() || segments.back()->available() < minimumAvailable) { return arena->getSegmentsForOutput();
WordCount newSize = std::max(minimumAvailable, preferredSegmentSize);
memory.push_back(reinterpret_cast<word*>(calloc(newSize / WORDS, sizeof(word))));
segments.push_back(std::unique_ptr<SegmentBuilder>(new SegmentBuilder(
this, SegmentId(segments.size()), memory.back(), newSize)));
}
return segments.back().get();
} }
std::unique_ptr<MessageBuilder> newMallocMessage(WordCount preferredSegmentSize) { SegmentBuilder* MessageImpl::Builder::allocateRoot(BuilderArena* arena) {
return std::unique_ptr<MessageBuilder>(new MallocMessage(preferredSegmentSize)); WordCount refSize = 1 * REFERENCES * WORDS_PER_REFERENCE;
SegmentBuilder* segment = arena->getSegmentWithAvailable(refSize);
CAPNPROTO_ASSERT(segment->getSegmentId() == SegmentId(0),
"First allocated word of new arena was not in segment ID 0.");
word* location = segment->allocate(refSize);
CAPNPROTO_ASSERT(location == segment->getPtrUnchecked(0 * WORDS),
"First allocated word of new arena was not the first word in its segment.");
return segment;
} }
} // namespace internal
} // namespace capnproto } // namespace capnproto
This diff is collapsed.
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#define CAPNPROTO_TYPE_SAFETY_H_ #define CAPNPROTO_TYPE_SAFETY_H_
#include "macros.h" #include "macros.h"
#include <cstddef>
namespace capnproto { namespace capnproto {
...@@ -50,6 +51,55 @@ struct NoInfer { ...@@ -50,6 +51,55 @@ struct NoInfer {
typedef T Type; typedef T Type;
}; };
// =======================================================================================
// ArrayPtr
template <typename T>
class ArrayPtr {
// A pointer to an array. Includes a size. Like any pointer, it doesn't own the target data,
// and passing by value only copies the pointer, not the target.
public:
inline ArrayPtr(): ptr(nullptr), size_(0) {}
inline ArrayPtr(std::nullptr_t): ptr(nullptr), size_(0) {}
inline ArrayPtr(T* ptr, std::size_t size): ptr(ptr), size_(size) {}
inline ArrayPtr(T* begin, T* end): ptr(begin), size_(end - begin) {}
inline operator ArrayPtr<const T>() {
return ArrayPtr<const T>(ptr, size_);
}
inline std::size_t size() const { return size_; }
inline T& operator[](std::size_t index) const {
CAPNPROTO_DEBUG_ASSERT(index < size_, "Out-of-bounds ArrayPtr access.");
return ptr[index];
}
inline T* begin() const { return ptr; }
inline T* end() const { return ptr + size_; }
inline ArrayPtr slice(size_t start, size_t end) {
CAPNPROTO_DEBUG_ASSERT(start <= end && end <= size_, "Out-of-bounds ArrayPtr::slice().");
return ArrayPtr(ptr + start, end - start);
}
private:
T* ptr;
std::size_t size_;
};
template <typename T>
inline ArrayPtr<T> arrayPtr(T* ptr, size_t size) {
// Use this function to construct ArrayPtrs without writing out the type name.
return ArrayPtr<T>(ptr, size);
}
template <typename T>
inline ArrayPtr<T> arrayPtr(T* begin, T* end) {
// Use this function to construct ArrayPtrs without writing out the type name.
return ArrayPtr<T>(begin, end);
}
// ======================================================================================= // =======================================================================================
// IDs // IDs
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "wire-format.h" #include "wire-format.h"
#include "descriptor.h" #include "descriptor.h"
#include "message.h" #include "message.h"
#include "arena.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
namespace capnproto { namespace capnproto {
...@@ -263,8 +264,8 @@ static void checkStruct(StructReader reader) { ...@@ -263,8 +264,8 @@ static void checkStruct(StructReader reader) {
} }
TEST(WireFormat, StructRoundTrip_OneSegment) { TEST(WireFormat, StructRoundTrip_OneSegment) {
std::unique_ptr<MessageBuilder> message = newMallocMessage(512 * WORDS); BuilderArena arena(MallocAllocator::getDefaultInstance());
SegmentBuilder* segment = message->getSegmentWithAvailable(1 * WORDS); SegmentBuilder* segment = arena.getSegmentWithAvailable(1 * WORDS);
word* rootLocation = segment->allocate(1 * WORDS); word* rootLocation = segment->allocate(1 * WORDS);
StructBuilder builder = StructBuilder::initRoot(segment, rootLocation, STRUCT_DEFAULT.words); StructBuilder builder = StructBuilder::initRoot(segment, rootLocation, STRUCT_DEFAULT.words);
...@@ -286,7 +287,9 @@ TEST(WireFormat, StructRoundTrip_OneSegment) { ...@@ -286,7 +287,9 @@ TEST(WireFormat, StructRoundTrip_OneSegment) {
// 6 sub-lists (4x 1 word, 1x 2 words) // 6 sub-lists (4x 1 word, 1x 2 words)
// ----- // -----
// 34 // 34
EXPECT_EQ(34 * WORDS, segment->getSize()); ArrayPtr<const ArrayPtr<const word>> segments = arena.getSegmentsForOutput();
ASSERT_EQ(1u, segments.size());
EXPECT_EQ(34u, segments[0].size());
checkStruct(builder); checkStruct(builder);
checkStruct(builder.asReader()); checkStruct(builder.asReader());
...@@ -295,34 +298,35 @@ TEST(WireFormat, StructRoundTrip_OneSegment) { ...@@ -295,34 +298,35 @@ TEST(WireFormat, StructRoundTrip_OneSegment) {
} }
TEST(WireFormat, StructRoundTrip_OneSegmentPerAllocation) { TEST(WireFormat, StructRoundTrip_OneSegmentPerAllocation) {
std::unique_ptr<MessageBuilder> message = newMallocMessage(1 * WORDS); MallocAllocator allocator(1);
SegmentBuilder* segment = message->getSegmentWithAvailable(1 * WORDS); BuilderArena arena(&allocator);
SegmentBuilder* segment = arena.getSegmentWithAvailable(1 * WORDS);
word* rootLocation = segment->allocate(1 * WORDS); word* rootLocation = segment->allocate(1 * WORDS);
StructBuilder builder = StructBuilder::initRoot(segment, rootLocation, STRUCT_DEFAULT.words); StructBuilder builder = StructBuilder::initRoot(segment, rootLocation, STRUCT_DEFAULT.words);
setupStruct(builder); setupStruct(builder);
// Verify that we made 15 segments. // Verify that we made 15 segments.
ASSERT_TRUE(message->tryGetSegment(SegmentId(14)) != nullptr); ArrayPtr<const ArrayPtr<const word>> segments = arena.getSegmentsForOutput();
EXPECT_EQ(nullptr, message->tryGetSegment(SegmentId(15))); ASSERT_EQ(15u, segments.size());
// Check that each segment has the expected size. Recall that the first word of each segment will // Check that each segment has the expected size. Recall that the first word of each segment will
// actually be a reference to the first thing allocated within that segment. // actually be a reference to the first thing allocated within that segment.
EXPECT_EQ( 1 * WORDS, message->getSegment(SegmentId( 0))->getSize()); // root ref EXPECT_EQ( 1u, segments[ 0].size()); // root ref
EXPECT_EQ( 7 * WORDS, message->getSegment(SegmentId( 1))->getSize()); // root struct EXPECT_EQ( 7u, segments[ 1].size()); // root struct
EXPECT_EQ( 2 * WORDS, message->getSegment(SegmentId( 2))->getSize()); // sub-struct EXPECT_EQ( 2u, segments[ 2].size()); // sub-struct
EXPECT_EQ( 3 * WORDS, message->getSegment(SegmentId( 3))->getSize()); // 3-element int32 list EXPECT_EQ( 3u, segments[ 3].size()); // 3-element int32 list
EXPECT_EQ(10 * WORDS, message->getSegment(SegmentId( 4))->getSize()); // struct list EXPECT_EQ(10u, segments[ 4].size()); // struct list
EXPECT_EQ( 2 * WORDS, message->getSegment(SegmentId( 5))->getSize()); // struct list substruct 1 EXPECT_EQ( 2u, segments[ 5].size()); // struct list substruct 1
EXPECT_EQ( 2 * WORDS, message->getSegment(SegmentId( 6))->getSize()); // struct list substruct 2 EXPECT_EQ( 2u, segments[ 6].size()); // struct list substruct 2
EXPECT_EQ( 2 * WORDS, message->getSegment(SegmentId( 7))->getSize()); // struct list substruct 3 EXPECT_EQ( 2u, segments[ 7].size()); // struct list substruct 3
EXPECT_EQ( 2 * WORDS, message->getSegment(SegmentId( 8))->getSize()); // struct list substruct 4 EXPECT_EQ( 2u, segments[ 8].size()); // struct list substruct 4
EXPECT_EQ( 6 * WORDS, message->getSegment(SegmentId( 9))->getSize()); // list list EXPECT_EQ( 6u, segments[ 9].size()); // list list
EXPECT_EQ( 2 * WORDS, message->getSegment(SegmentId(10))->getSize()); // list list sublist 1 EXPECT_EQ( 2u, segments[10].size()); // list list sublist 1
EXPECT_EQ( 2 * WORDS, message->getSegment(SegmentId(11))->getSize()); // list list sublist 2 EXPECT_EQ( 2u, segments[11].size()); // list list sublist 2
EXPECT_EQ( 2 * WORDS, message->getSegment(SegmentId(12))->getSize()); // list list sublist 3 EXPECT_EQ( 2u, segments[12].size()); // list list sublist 3
EXPECT_EQ( 2 * WORDS, message->getSegment(SegmentId(13))->getSize()); // list list sublist 4 EXPECT_EQ( 2u, segments[13].size()); // list list sublist 4
EXPECT_EQ( 3 * WORDS, message->getSegment(SegmentId(14))->getSize()); // list list sublist 5 EXPECT_EQ( 3u, segments[14].size()); // list list sublist 5
checkStruct(builder); checkStruct(builder);
checkStruct(builder.asReader()); checkStruct(builder.asReader());
...@@ -330,25 +334,26 @@ TEST(WireFormat, StructRoundTrip_OneSegmentPerAllocation) { ...@@ -330,25 +334,26 @@ TEST(WireFormat, StructRoundTrip_OneSegmentPerAllocation) {
} }
TEST(WireFormat, StructRoundTrip_MultipleSegmentsWithMultipleAllocations) { TEST(WireFormat, StructRoundTrip_MultipleSegmentsWithMultipleAllocations) {
std::unique_ptr<MessageBuilder> message = newMallocMessage(8 * WORDS); MallocAllocator allocator(8);
SegmentBuilder* segment = message->getSegmentWithAvailable(1 * WORDS); BuilderArena arena(&allocator);
SegmentBuilder* segment = arena.getSegmentWithAvailable(1 * WORDS);
word* rootLocation = segment->allocate(1 * WORDS); word* rootLocation = segment->allocate(1 * WORDS);
StructBuilder builder = StructBuilder::initRoot(segment, rootLocation, STRUCT_DEFAULT.words); StructBuilder builder = StructBuilder::initRoot(segment, rootLocation, STRUCT_DEFAULT.words);
setupStruct(builder); setupStruct(builder);
// Verify that we made 6 segments. // Verify that we made 6 segments.
ASSERT_TRUE(message->tryGetSegment(SegmentId(5)) != nullptr); ArrayPtr<const ArrayPtr<const word>> segments = arena.getSegmentsForOutput();
EXPECT_EQ(nullptr, message->tryGetSegment(SegmentId(6))); ASSERT_EQ(6u, segments.size());
// Check that each segment has the expected size. Recall that each object will be prefixed by an // Check that each segment has the expected size. Recall that each object will be prefixed by an
// extra word if its parent is in a different segment. // extra word if its parent is in a different segment.
EXPECT_EQ( 8 * WORDS, message->getSegment(SegmentId(0))->getSize()); // root ref + struct + sub EXPECT_EQ( 8u, segments[0].size()); // root ref + struct + sub
EXPECT_EQ( 3 * WORDS, message->getSegment(SegmentId(1))->getSize()); // 3-element int32 list EXPECT_EQ( 3u, segments[1].size()); // 3-element int32 list
EXPECT_EQ(10 * WORDS, message->getSegment(SegmentId(2))->getSize()); // struct list EXPECT_EQ(10u, segments[2].size()); // struct list
EXPECT_EQ( 8 * WORDS, message->getSegment(SegmentId(3))->getSize()); // struct list substructs EXPECT_EQ( 8u, segments[3].size()); // struct list substructs
EXPECT_EQ( 8 * WORDS, message->getSegment(SegmentId(4))->getSize()); // list list + sublist 1,2 EXPECT_EQ( 8u, segments[4].size()); // list list + sublist 1,2
EXPECT_EQ( 7 * WORDS, message->getSegment(SegmentId(5))->getSize()); // list list sublist 3,4,5 EXPECT_EQ( 7u, segments[5].size()); // list list sublist 3,4,5
checkStruct(builder); checkStruct(builder);
checkStruct(builder.asReader()); checkStruct(builder.asReader());
......
This diff is collapsed.
...@@ -34,11 +34,6 @@ ...@@ -34,11 +34,6 @@
#include "type-safety.h" #include "type-safety.h"
#include "blob.h" #include "blob.h"
namespace capnproto {
class SegmentReader;
class SegmentBuilder;
}
namespace capnproto { namespace capnproto {
namespace internal { namespace internal {
...@@ -52,6 +47,8 @@ class ListBuilder; ...@@ -52,6 +47,8 @@ class ListBuilder;
class ListReader; class ListReader;
struct WireReference; struct WireReference;
struct WireHelpers; struct WireHelpers;
class SegmentReader;
class SegmentBuilder;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
...@@ -85,6 +82,7 @@ public: ...@@ -85,6 +82,7 @@ public:
inline StructBuilder(): segment(nullptr), data(nullptr), references(nullptr) {} inline StructBuilder(): segment(nullptr), data(nullptr), references(nullptr) {}
static StructBuilder initRoot(SegmentBuilder* segment, word* location, const word* defaultValue); static StructBuilder initRoot(SegmentBuilder* segment, word* location, const word* defaultValue);
static StructBuilder getRoot(SegmentBuilder* segment, word* location, const word* defaultValue);
template <typename T> template <typename T>
CAPNPROTO_ALWAYS_INLINE(T getDataField(ElementCount offset) const); CAPNPROTO_ALWAYS_INLINE(T getDataField(ElementCount offset) const);
......
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