Commit 5480b2aa authored by Kenton Varda's avatar Kenton Varda

Introduce capnp::clone(reader) which makes an owned copy of any capnp object.

This is frequently needed. Sandstorm had `OwnCapnp` for this purpose:

https://github.com/sandstorm-io/sandstorm/blob/4d86a8144cdb43120ea12845738d0fe4a6ffcda1/src/sandstorm/util.h#L495-L525

The Workers codebase has some ad-hoc copies of this logic too, and multiple people have requested something similar on the mailing list.
parent f861809b
......@@ -288,6 +288,10 @@ public:
return &localCapTable;
}
kj::Own<_::CapTableBuilder> releaseLocalCapTable() {
return kj::heap<LocalCapTable>(kj::mv(localCapTable));
}
SegmentBuilder* getSegment(SegmentId id);
// Get the segment with the given id. Crashes or throws an exception if no such segment exists.
......
......@@ -1080,6 +1080,27 @@ KJ_TEST("Promise<RemotePromise<T>> automatically reduces to RemotePromise<T> wit
EXPECT_EQ(1, chainedCallCount);
}
KJ_TEST("clone() with caps") {
int dummy = 0;
MallocMessageBuilder builder(2048);
auto root = builder.getRoot<AnyPointer>().initAs<List<test::TestInterface>>(3);
root.set(0, kj::heap<TestInterfaceImpl>(dummy));
root.set(1, kj::heap<TestInterfaceImpl>(dummy));
root.set(2, kj::heap<TestInterfaceImpl>(dummy));
auto copyPtr = clone(root.asReader());
auto& copy = *copyPtr;
KJ_ASSERT(copy.size() == 3);
KJ_EXPECT(ClientHook::from(copy[0]).get() == ClientHook::from(root[0]).get());
KJ_EXPECT(ClientHook::from(copy[1]).get() == ClientHook::from(root[1]).get());
KJ_EXPECT(ClientHook::from(copy[2]).get() == ClientHook::from(root[2]).get());
KJ_EXPECT(ClientHook::from(copy[0]).get() != ClientHook::from(root[1]).get());
KJ_EXPECT(ClientHook::from(copy[1]).get() != ClientHook::from(root[2]).get());
KJ_EXPECT(ClientHook::from(copy[2]).get() != ClientHook::from(root[0]).get());
}
} // namespace
} // namespace _
} // namespace capnp
......@@ -168,6 +168,14 @@ TEST(Message, ReadWriteDataStruct) {
checkTestMessageAllZero(defaultValue<TestAllTypes>());
}
KJ_TEST("clone()") {
MallocMessageBuilder builder(2048);
initTestMessage(builder.getRoot<TestAllTypes>());
auto copy = clone(builder.getRoot<TestAllTypes>().asReader());
checkTestMessage(*copy);
}
// TODO(test): More tests.
} // namespace
......
......@@ -178,6 +178,10 @@ bool MessageBuilder::isCanonical() {
.isCanonical(&readHead);
}
kj::Own<_::CapTableBuilder> MessageBuilder::releaseBuiltinCapTable() {
return arena()->releaseLocalCapTable();
}
// =======================================================================================
SegmentArrayMessageReader::SegmentArrayMessageReader(
......
......@@ -254,6 +254,12 @@ private:
_::BuilderArena* arena() { return reinterpret_cast<_::BuilderArena*>(arenaSpace); }
_::SegmentBuilder* getRootSegment();
AnyPointer::Builder getRootInternal();
kj::Own<_::CapTableBuilder> releaseBuiltinCapTable();
// Hack for clone() to extract the cap table.
template <typename Reader, typename>
friend kj::Own<kj::Decay<Reader>> clone(Reader&& reader);
};
template <typename RootType>
......@@ -315,6 +321,10 @@ static typename Type::Reader defaultValue();
//
// TODO(cleanup): Find a better home for this function?
template <typename Reader, typename = FromReader<Reader>>
kj::Own<kj::Decay<Reader>> clone(Reader&& reader);
// Make a deep copy of the given Reader on the heap, producing an owned pointer.
// =======================================================================================
class SegmentArrayMessageReader: public MessageReader {
......@@ -506,6 +516,25 @@ static typename Type::Reader defaultValue() {
return typename Type::Reader(_::StructReader());
}
template <typename Reader, typename>
kj::Own<kj::Decay<Reader>> clone(Reader&& reader) {
auto size = reader.totalSize();
auto buffer = kj::heapArray<capnp::word>(size.wordCount + 1);
memset(buffer.asBytes().begin(), 0, buffer.asBytes().size());
if (size.capCount == 0) {
copyToUnchecked(reader, buffer);
auto result = readMessageUnchecked<FromReader<Reader>>(buffer.begin());
return kj::attachVal(result, kj::mv(buffer));
} else {
FlatMessageBuilder builder(buffer);
builder.setRoot(kj::fwd<Reader>(reader));
builder.requireFilled();
auto capTable = builder.releaseBuiltinCapTable();
AnyPointer::Reader raw(_::PointerReader::getRootUnchecked(buffer.begin()).imbue(capTable));
return kj::attachVal(raw.getAs<FromReader<Reader>>(), kj::mv(buffer), kj::mv(capTable));
}
}
template <typename T>
kj::Array<word> canonicalize(T&& reader) {
return _::PointerHelpers<FromReader<T>>::getInternalReader(reader).canonicalize();
......
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