Commit 8536e962 authored by Kenton Varda's avatar Kenton Varda

Implement orphans -- the ability to transfer ownership of sub-objects.

parent 0360b3d1
......@@ -119,6 +119,7 @@ includecapnp_HEADERS = \
src/capnp/blob.h \
src/capnp/endian.h \
src/capnp/layout.h \
src/capnp/orphan.h \
src/capnp/list.h \
src/capnp/message.h \
src/capnp/schema.h \
......@@ -203,6 +204,7 @@ capnp_test_SOURCES = \
src/capnp/dynamic-test.c++ \
src/capnp/stringify-test.c++ \
src/capnp/encoding-test.c++ \
src/capnp/orphan-test.c++ \
src/capnp/serialize-test.c++ \
src/capnp/serialize-packed-test.c++ \
src/capnp/test-util.c++ \
......
......@@ -104,7 +104,7 @@ public:
KJ_IREQUIRE(value[size] == '\0', "StringPtr must be NUL-terminated.");
}
inline Reader asReader() const { return Reader(content.begin(), content.size()); }
inline Reader asReader() const { return Reader(content.begin(), content.size() - 1); }
inline operator kj::ArrayPtr<char>();
inline kj::ArrayPtr<char> asArray();
......
......@@ -43,6 +43,84 @@ enum class Void {
template <typename T>
inline T& operator<<(T& os, Void) { return os << "void"; }
struct Text;
struct Data;
enum class Kind: uint8_t {
PRIMITIVE,
BLOB,
ENUM,
STRUCT,
UNION,
INTERFACE,
LIST,
UNKNOWN
};
namespace _ { // private
template <typename T> struct Kind_ { static constexpr Kind kind = Kind::UNKNOWN; };
template <> struct Kind_<Void> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<bool> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<int8_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<int16_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<int32_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<int64_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<uint8_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<uint16_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<uint32_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<uint64_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<float> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<double> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<Text> { static constexpr Kind kind = Kind::BLOB; };
template <> struct Kind_<Data> { static constexpr Kind kind = Kind::BLOB; };
} // namespace _ (private)
template <typename T>
inline constexpr Kind kind() {
return _::Kind_<T>::kind;
}
template <typename T, Kind k = kind<T>()>
struct List;
namespace _ { // private
template <typename T, Kind k> struct Kind_<List<T, k>> { static constexpr Kind kind = Kind::LIST; };
} // namespace _ (private)
template <typename T, Kind k = kind<T>()> struct ReaderFor_ { typedef typename T::Reader Type; };
template <typename T> struct ReaderFor_<T, Kind::PRIMITIVE> { typedef T Type; };
template <typename T> struct ReaderFor_<T, Kind::ENUM> { typedef T Type; };
template <typename T> using ReaderFor = typename ReaderFor_<T>::Type;
// The type returned by List<T>::Reader::operator[].
template <typename T, Kind k = kind<T>()> struct BuilderFor_ { typedef typename T::Builder Type; };
template <typename T> struct BuilderFor_<T, Kind::PRIMITIVE> { typedef T Type; };
template <typename T> struct BuilderFor_<T, Kind::ENUM> { typedef T Type; };
template <typename T> using BuilderFor = typename BuilderFor_<T>::Type;
// The type returned by List<T>::Builder::operator[].
template <typename T, Kind k = kind<T>()> struct TypeIfEnum_;
template <typename T> struct TypeIfEnum_<T, Kind::ENUM> { typedef T Type; };
template <typename T>
using TypeIfEnum = typename TypeIfEnum_<kj::Decay<T>>::Type;
template <typename T>
using FromReader = typename kj::Decay<T>::Reads;
// FromReader<MyType::Reader> = MyType (for any Cap'n Proto type).
template <typename T>
using FromBuilder = typename kj::Decay<T>::Builds;
// FromBuilder<MyType::Builder> = MyType (for any Cap'n Proto type).
namespace _ { // private
template <typename T, Kind k = kind<T>()>
struct PointerHelpers;
} // namespace _ (private)
// =======================================================================================
// Raw memory types and measures
......
......@@ -1557,4 +1557,35 @@ DynamicList::Builder PointerHelpers<DynamicList, Kind::UNKNOWN>::init(
} // namespace _ (private)
// -------------------------------------------------------------------
Orphan<DynamicStruct> Orphanage::newOrphan(StructSchema schema) {
return Orphan<DynamicStruct>(
schema, _::OrphanBuilder::initStruct(arena, structSizeFromSchema(schema)));
}
Orphan<DynamicList> Orphanage::newOrphan(ListSchema schema, uint size) {
if (schema.whichElementType() == schema::Type::Body::STRUCT_TYPE) {
return Orphan<DynamicList>(schema, _::OrphanBuilder::initStructList(
arena, size * ELEMENTS, structSizeFromSchema(schema.getStructElementType())));
} else {
return Orphan<DynamicList>(schema, _::OrphanBuilder::initList(
arena, size * ELEMENTS, elementSizeFor(schema.whichElementType())));
}
}
DynamicStruct::Builder Orphan<DynamicStruct>::get() {
return DynamicStruct::Builder(schema, builder.asStruct(structSizeFromSchema(schema)));
}
DynamicList::Builder Orphan<DynamicList>::get() {
if (schema.whichElementType() == schema::Type::Body::STRUCT_TYPE) {
return DynamicList::Builder(
schema, builder.asStructList(structSizeFromSchema(schema.getStructElementType())));
} else {
return DynamicList::Builder(
schema, builder.asList(elementSizeFor(schema.whichElementType())));
}
}
} // namespace capnp
......@@ -104,6 +104,14 @@ BuilderFor<DynamicTypeFor<FromBuilder<T>>> toDynamic(T&& value);
template <typename T>
DynamicTypeFor<TypeIfEnum<T>> toDynamic(T&& value);
template <typename T> struct EnableIfNotDynamic_ { typedef T Type; };
template <> struct EnableIfNotDynamic_<DynamicUnion> {};
template <> struct EnableIfNotDynamic_<DynamicStruct> {};
template <> struct EnableIfNotDynamic_<DynamicList> {};
template <> struct EnableIfNotDynamic_<DynamicValue> {};
template <typename T>
using EnableIfNotDynamic = typename EnableIfNotDynamic_<T>::Type;
// -------------------------------------------------------------------
class DynamicEnum {
......@@ -176,6 +184,8 @@ private:
class DynamicUnion::Reader {
public:
typedef DynamicUnion Reads;
Reader() = default;
inline StructSchema::Union getSchema() const { return schema; }
......@@ -203,6 +213,8 @@ private:
class DynamicUnion::Builder {
public:
typedef DynamicUnion Builds;
Builder() = default;
inline StructSchema::Union getSchema() const { return schema; }
......@@ -255,9 +267,11 @@ private:
class DynamicStruct::Reader {
public:
typedef DynamicStruct Reads;
Reader() = default;
template <typename T, typename = FromReader<T>>
template <typename T, typename = EnableIfNotDynamic<FromReader<T>>>
inline Reader(T&& value): Reader(toDynamic(value)) {}
template <typename T>
......@@ -299,13 +313,16 @@ private:
friend struct ::capnp::ToDynamic_;
friend kj::String _::structString(
_::StructReader reader, const _::RawSchema& schema);
friend class Orphanage;
};
class DynamicStruct::Builder {
public:
typedef DynamicStruct Builds;
Builder() = default;
template <typename T, typename = FromBuilder<T>>
template <typename T, typename = EnableIfNotDynamic<FromBuilder<T>>>
inline Builder(T&& value): Builder(toDynamic(value)) {}
template <typename T>
......@@ -329,6 +346,8 @@ public:
DynamicValue::Builder init(StructSchema::Member member, uint size);
// Init a struct, list, or blob field.
// TODO(someday): Implement adopt() and disown().
DynamicStruct::Builder getObject(StructSchema::Member member, StructSchema type);
DynamicList::Builder getObject(StructSchema::Member member, ListSchema type);
Text::Builder getObjectAsText(StructSchema::Member member);
......@@ -402,15 +421,19 @@ private:
friend class MessageBuilder;
template <typename T, ::capnp::Kind k>
friend struct ::capnp::ToDynamic_;
friend class Orphanage;
friend class Orphan<DynamicStruct>;
};
// -------------------------------------------------------------------
class DynamicList::Reader {
public:
typedef DynamicList Reads;
Reader() = default;
template <typename T, typename = FromReader<T>>
template <typename T, typename = EnableIfNotDynamic<FromReader<T>>>
inline Reader(T&& value): Reader(toDynamic(value)) {}
template <typename T>
......@@ -440,13 +463,16 @@ private:
friend class DynamicList::Builder;
template <typename T, ::capnp::Kind k>
friend struct ::capnp::ToDynamic_;
friend class Orphanage;
};
class DynamicList::Builder {
public:
typedef DynamicList Builds;
Builder() = default;
template <typename T, typename = FromBuilder<T>>
template <typename T, typename = EnableIfNotDynamic<FromBuilder<T>>>
inline Builder(T&& value): Builder(toDynamic(value)) {}
template <typename T>
......@@ -460,6 +486,7 @@ public:
DynamicValue::Builder operator[](uint index);
void set(uint index, const DynamicValue::Reader& value);
DynamicValue::Builder init(uint index, uint size);
// TODO(someday): Implement adopt() and disown().
typedef _::IndexingIterator<Builder, DynamicStruct::Builder> Iterator;
inline Iterator begin() { return Iterator(this, 0); }
......@@ -480,6 +507,10 @@ private:
friend struct DynamicStruct;
template <typename T, ::capnp::Kind k>
friend struct ::capnp::ToDynamic_;
friend class Orphanage;
template <typename T, Kind k>
friend struct _::OrphanGetImpl;
friend class Orphan<DynamicList>;
};
// -------------------------------------------------------------------
......@@ -498,6 +529,8 @@ template <> struct BuilderFor_<DynamicList, Kind::UNKNOWN> { typedef DynamicList
class DynamicValue::Reader {
public:
typedef DynamicValue Reads;
inline Reader(std::nullptr_t n = nullptr); // UNKNOWN
inline Reader(Void value);
inline Reader(bool value);
......@@ -576,6 +609,8 @@ private:
class DynamicValue::Builder {
public:
typedef DynamicValue Builds;
inline Builder(std::nullptr_t n = nullptr); // UNKNOWN
inline Builder(Void value);
inline Builder(bool value);
......@@ -646,6 +681,88 @@ kj::String KJ_STRINGIFY(const DynamicStruct::Builder& value);
kj::String KJ_STRINGIFY(const DynamicList::Reader& value);
kj::String KJ_STRINGIFY(const DynamicList::Builder& value);
// -------------------------------------------------------------------
// Orphan <-> Dynamic glue
template <>
class Orphan<DynamicStruct> {
public:
Orphan() = default;
KJ_DISALLOW_COPY(Orphan);
Orphan(Orphan&&) = default;
Orphan& operator=(Orphan&&) = default;
DynamicStruct::Builder get();
inline bool operator==(decltype(nullptr)) { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) { return builder == nullptr; }
private:
StructSchema schema;
_::OrphanBuilder builder;
inline Orphan(StructSchema schema, _::OrphanBuilder&& builder)
: schema(schema), builder(kj::mv(builder)) {}
template <typename, Kind>
friend struct _::PointerHelpers;
friend struct DynamicList;
friend class Orphanage;
};
template <>
class Orphan<DynamicList> {
public:
Orphan() = default;
KJ_DISALLOW_COPY(Orphan);
Orphan(Orphan&&) = default;
Orphan& operator=(Orphan&&) = default;
DynamicList::Builder get();
inline bool operator==(decltype(nullptr)) { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) { return builder == nullptr; }
private:
ListSchema schema;
_::OrphanBuilder builder;
inline Orphan(ListSchema schema, _::OrphanBuilder&& builder)
: schema(schema), builder(kj::mv(builder)) {}
template <typename, Kind>
friend struct _::PointerHelpers;
friend struct DynamicList;
friend class Orphanage;
};
template <>
struct Orphanage::GetInnerBuilder<DynamicStruct, Kind::UNKNOWN> {
static inline _::StructBuilder apply(DynamicStruct::Builder& t) {
return t.builder;
}
};
template <>
struct Orphanage::GetInnerBuilder<DynamicList, Kind::UNKNOWN> {
static inline _::ListBuilder apply(DynamicList::Builder& t) {
return t.builder;
}
};
template <>
inline Orphan<DynamicStruct> Orphanage::newOrphanCopy<DynamicStruct::Reader>(
const DynamicStruct::Reader& copyFrom) {
return Orphan<DynamicStruct>(
copyFrom.getSchema(), _::OrphanBuilder::copy(arena, copyFrom.reader));
}
template <>
inline Orphan<DynamicList> Orphanage::newOrphanCopy<DynamicList::Reader>(
const DynamicList::Reader& copyFrom) {
return Orphan<DynamicList>(copyFrom.getSchema(), _::OrphanBuilder::copy(arena, copyFrom.reader));
}
// -------------------------------------------------------------------
// Inject the ability to use DynamicStruct for message roots and Dynamic{Struct,List} for
// generated Object accessors.
......@@ -672,6 +789,14 @@ struct PointerHelpers<DynamicStruct, Kind::UNKNOWN> {
StructBuilder builder, WirePointerCount index, const DynamicStruct::Reader& value);
static DynamicStruct::Builder init(
StructBuilder builder, WirePointerCount index, StructSchema schema);
static inline void adopt(StructBuilder builder, WirePointerCount index,
Orphan<DynamicStruct>&& value) {
builder.adopt(index, kj::mv(value.builder));
}
static inline Orphan<DynamicStruct> disown(StructBuilder builder, WirePointerCount index,
StructSchema schema) {
return Orphan<DynamicStruct>(schema, builder.disown(index));
}
};
template <>
......@@ -687,6 +812,14 @@ struct PointerHelpers<DynamicList, Kind::UNKNOWN> {
StructBuilder builder, WirePointerCount index, const DynamicList::Reader& value);
static DynamicList::Builder init(
StructBuilder builder, WirePointerCount index, ListSchema schema, uint size);
static inline void adopt(StructBuilder builder, WirePointerCount index,
Orphan<DynamicList>&& value) {
builder.adopt(index, kj::mv(value.builder));
}
static inline Orphan<DynamicList> disown(StructBuilder builder, WirePointerCount index,
ListSchema schema) {
return Orphan<DynamicList>(schema, builder.disown(index));
}
};
} // namespace _ (private)
......
......@@ -28,6 +28,7 @@
#include "layout.h"
#include "list.h"
#include "orphan.h"
#include <kj/string.h>
namespace capnp {
......@@ -58,6 +59,12 @@ struct PointerHelpers<T, Kind::STRUCT> {
static inline typename T::Builder init(StructBuilder builder, WirePointerCount index) {
return typename T::Builder(builder.initStructField(index, structSize<T>()));
}
static inline void adopt(StructBuilder builder, WirePointerCount index, Orphan<T>&& value) {
builder.adopt(index, kj::mv(value.builder));
}
static inline Orphan<T> disown(StructBuilder builder, WirePointerCount index) {
return Orphan<T>(builder.disown(index));
}
};
template <typename T>
......@@ -86,6 +93,12 @@ struct PointerHelpers<List<T>, Kind::LIST> {
StructBuilder builder, WirePointerCount index, uint size) {
return typename List<T>::Builder(List<T>::initAsFieldOf(builder, index, size));
}
static inline void adopt(StructBuilder builder, WirePointerCount index, Orphan<List<T>>&& value) {
builder.adopt(index, kj::mv(value.builder));
}
static inline Orphan<List<T>> disown(StructBuilder builder, WirePointerCount index) {
return Orphan<List<T>>(builder.disown(index));
}
};
template <typename T>
......@@ -106,6 +119,12 @@ struct PointerHelpers<T, Kind::BLOB> {
static inline typename T::Builder init(StructBuilder builder, WirePointerCount index, uint size) {
return builder.initBlobField<T>(index, size * BYTES);
}
static inline void adopt(StructBuilder builder, WirePointerCount index, Orphan<T>&& value) {
builder.adopt(index, kj::mv(value.builder));
}
static inline Orphan<T> disown(StructBuilder builder, WirePointerCount index) {
return Orphan<T>(builder.disown(index));
}
};
struct UncheckedMessage {
......
......@@ -195,11 +195,6 @@ static_assert(POINTERS * BITS_PER_POINTER / BITS_PER_BYTE / BYTES == sizeof(Wire
// =======================================================================================
struct WireHelpers {
static KJ_ALWAYS_INLINE(WordCount roundBitsUpToWords(BitCount64 bits)) {
static_assert(sizeof(word) == 8, "This code assumes 64-bit words.");
return (bits + 63 * BITS) / BITS_PER_WORD;
}
static KJ_ALWAYS_INLINE(WordCount roundBytesUpToWords(ByteCount bytes)) {
static_assert(sizeof(word) == 8, "This code assumes 64-bit words.");
return (bytes + 7 * BYTES) / BYTES_PER_WORD;
......@@ -209,6 +204,19 @@ struct WireHelpers {
return (bits + 7 * BITS) / BITS_PER_BYTE;
}
// The maximum object size is 4GB - 1 byte. If measured in bits, this would overflow a 32-bit
// counter, so we need to accept BitCount64. However, 32 bits is enough for the returned
// ByteCounts and WordCounts.
static KJ_ALWAYS_INLINE(WordCount roundBitsUpToWords(BitCount64 bits)) {
static_assert(sizeof(word) == 8, "This code assumes 64-bit words.");
return (bits + 63 * BITS) / BITS_PER_WORD;
}
static KJ_ALWAYS_INLINE(ByteCount roundBitsUpToBytes(BitCount64 bits)) {
return (bits + 7 * BITS) / BITS_PER_BYTE;
}
static KJ_ALWAYS_INLINE(bool boundsCheck(
SegmentReader* segment, const word* start, const word* end)) {
// If segment is null, this is an unchecked message, so we don't do bounds checks.
......@@ -246,7 +254,17 @@ struct WireHelpers {
}
}
static KJ_ALWAYS_INLINE(word* followFars(WirePointer*& ref, SegmentBuilder*& segment)) {
static KJ_ALWAYS_INLINE(word* followFars(
WirePointer*& ref, word* refTarget, SegmentBuilder*& segment)) {
// If `ref` is a far pointer, follow it. On return, `ref` will have been updated to point at
// a WirePointer that contains the type information about the target object, and a pointer to
// the object contents is returned. The caller must NOT use `ref->target()` as this may or may
// not actually return a valid pointer. `segment` is also updated to point at the segment which
// actually contains the object.
//
// If `ref` is not a far pointer, this simply returns `refTarget`. Usually, `refTarget` should
// be the same as `ref->target()`, but may not be in cases where `ref` is only a tag.
if (ref->kind() == WirePointer::FAR) {
segment = segment->getArena()->getSegment(ref->farRef.segmentId.get());
WirePointer* pad =
......@@ -263,12 +281,15 @@ struct WireHelpers {
segment = segment->getArena()->getSegment(pad->farRef.segmentId.get());
return segment->getPtrUnchecked(pad->farPositionInSegment());
} else {
return ref->target();
return refTarget;
}
}
static KJ_ALWAYS_INLINE(
const word* followFars(const WirePointer*& ref, SegmentReader*& segment)) {
// Like the other followFars() but operates on readers. There is no `refTarget` parameter
// because `ref->target()` is valid for all use cases of this method.
// If the segment is null, this is an unchecked message, so there are no FAR pointers.
if (segment != nullptr && ref->kind() == WirePointer::FAR) {
// Look up the segment containing the landing pad.
......@@ -672,18 +693,33 @@ struct WireHelpers {
SegmentBuilder* srcSegment, WirePointer* src) {
// Make *dst point to the same object as *src. Both must reside in the same message, but can
// be in different segments. Not always-inline because this is rarely used.
//
// Caller MUST zero out the source pointer after calling this, to make sure no later code
// mistakenly thinks the source location still owns the object. transferPointer() doesn't do
// this zeroing itself because many callers transfer several pointers in a loop then zero out
// the whole section.
if (src->isNull()) {
memset(dst, 0, sizeof(WirePointer));
} else if (src->kind() == WirePointer::FAR) {
// Far pointers are position-independent, so we can just copy.
memcpy(dst, src, sizeof(WirePointer));
} else if (dstSegment == srcSegment) {
} else {
transferPointer(dstSegment, dst, srcSegment, src, src->target());
}
}
static void transferPointer(SegmentBuilder* dstSegment, WirePointer* dst,
SegmentBuilder* srcSegment, const WirePointer* srcTag,
word* srcPtr) {
// Like the other overload, but splits src into a tag and a target. Particularly useful for
// OrphanBuilder.
if (dstSegment == srcSegment) {
// Same segment, so create a direct pointer.
dst->setKindAndTarget(src->kind(), src->target());
dst->setKindAndTarget(srcTag->kind(), srcPtr);
// We can just copy the upper 32 bits. (Use memcpy() to comply with aliasing rules.)
memcpy(&dst->upper32Bits, &src->upper32Bits, sizeof(src->upper32Bits));
memcpy(&dst->upper32Bits, &srcTag->upper32Bits, sizeof(srcTag->upper32Bits));
} else {
// Need to create a far pointer. Try to allocate it in the same segment as the source, so
// that it doesn't need to be a double-far.
......@@ -697,18 +733,18 @@ struct WireHelpers {
KJ_DASSERT(landingPad != nullptr,
"getSegmentWithAvailable() returned segment without space available.");
landingPad[0].setFar(false, srcSegment->getOffsetTo(src->target()));
landingPad[0].setFar(false, srcSegment->getOffsetTo(srcPtr));
landingPad[0].farRef.segmentId.set(srcSegment->getSegmentId());
landingPad[1].setKindWithZeroOffset(src->kind());
memcpy(&landingPad[1].upper32Bits, &src->upper32Bits, sizeof(src->upper32Bits));
landingPad[1].setKindWithZeroOffset(srcTag->kind());
memcpy(&landingPad[1].upper32Bits, &srcTag->upper32Bits, sizeof(srcTag->upper32Bits));
dst->setFar(true, farSegment->getOffsetTo(reinterpret_cast<word*>(landingPad)));
dst->farRef.set(farSegment->getSegmentId());
} else {
// Simple landing pad is just a pointer.
landingPad->setKindAndTarget(src->kind(), src->target());
memcpy(&landingPad->upper32Bits, &src->upper32Bits, sizeof(src->upper32Bits));
landingPad->setKindAndTarget(srcTag->kind(), srcPtr);
memcpy(&landingPad->upper32Bits, &srcTag->upper32Bits, sizeof(srcTag->upper32Bits));
dst->setFar(false, srcSegment->getOffsetTo(reinterpret_cast<word*>(landingPad)));
dst->farRef.set(srcSegment->getSegmentId());
......@@ -733,19 +769,25 @@ struct WireHelpers {
static KJ_ALWAYS_INLINE(StructBuilder getWritableStructPointer(
WirePointer* ref, SegmentBuilder* segment, StructSize size, const word* defaultValue)) {
return getWritableStructPointer(ref, ref->target(), segment, size, defaultValue);
}
static KJ_ALWAYS_INLINE(StructBuilder getWritableStructPointer(
WirePointer* ref, word* refTarget, SegmentBuilder* segment, StructSize size,
const word* defaultValue)) {
if (ref->isNull()) {
useDefault:
if (defaultValue == nullptr ||
reinterpret_cast<const WirePointer*>(defaultValue)->isNull()) {
return initStructPointer(ref, segment, size);
}
copyMessage(segment, ref, reinterpret_cast<const WirePointer*>(defaultValue));
refTarget = copyMessage(segment, ref, reinterpret_cast<const WirePointer*>(defaultValue));
defaultValue = nullptr; // If the default value is itself invalid, don't use it again.
}
WirePointer* oldRef = ref;
SegmentBuilder* oldSegment = segment;
word* oldPtr = followFars(oldRef, oldSegment);
word* oldPtr = followFars(oldRef, refTarget, oldSegment);
KJ_REQUIRE(oldRef->kind() == WirePointer::STRUCT,
"Message contains non-struct pointer where struct pointer was expected.") {
......@@ -853,6 +895,13 @@ struct WireHelpers {
static KJ_ALWAYS_INLINE(ListBuilder getWritableListPointer(
WirePointer* origRef, SegmentBuilder* origSegment, FieldSize elementSize,
const word* defaultValue)) {
return getWritableListPointer(origRef, origRef->target(), origSegment, elementSize,
defaultValue);
}
static KJ_ALWAYS_INLINE(ListBuilder getWritableListPointer(
WirePointer* origRef, word* origRefTarget, SegmentBuilder* origSegment, FieldSize elementSize,
const word* defaultValue)) {
KJ_DREQUIRE(elementSize != FieldSize::INLINE_COMPOSITE,
"Use getStructList{Element,Field}() for structs.");
......@@ -862,7 +911,8 @@ struct WireHelpers {
reinterpret_cast<const WirePointer*>(defaultValue)->isNull()) {
return ListBuilder();
}
copyMessage(origSegment, origRef, reinterpret_cast<const WirePointer*>(defaultValue));
origRefTarget = copyMessage(
origSegment, origRef, reinterpret_cast<const WirePointer*>(defaultValue));
defaultValue = nullptr; // If the default value is itself invalid, don't use it again.
}
......@@ -873,7 +923,7 @@ struct WireHelpers {
WirePointer* ref = origRef;
SegmentBuilder* segment = origSegment;
word* ptr = followFars(ref, segment);
word* ptr = followFars(ref, origRefTarget, segment);
KJ_REQUIRE(ref->kind() == WirePointer::LIST,
"Called getList{Field,Element}() but existing pointer is not a list.") {
......@@ -956,13 +1006,20 @@ struct WireHelpers {
static KJ_ALWAYS_INLINE(ListBuilder getWritableStructListPointer(
WirePointer* origRef, SegmentBuilder* origSegment, StructSize elementSize,
const word* defaultValue)) {
return getWritableStructListPointer(origRef, origRef->target(), origSegment, elementSize,
defaultValue);
}
static KJ_ALWAYS_INLINE(ListBuilder getWritableStructListPointer(
WirePointer* origRef, word* origRefTarget, SegmentBuilder* origSegment,
StructSize elementSize, const word* defaultValue)) {
if (origRef->isNull()) {
useDefault:
if (defaultValue == nullptr ||
reinterpret_cast<const WirePointer*>(defaultValue)->isNull()) {
return ListBuilder();
}
copyMessage(origSegment, origRef, reinterpret_cast<const WirePointer*>(defaultValue));
origRefTarget = copyMessage(
origSegment, origRef, reinterpret_cast<const WirePointer*>(defaultValue));
defaultValue = nullptr; // If the default value is itself invalid, don't use it again.
}
......@@ -970,7 +1027,7 @@ struct WireHelpers {
WirePointer* oldRef = origRef;
SegmentBuilder* oldSegment = origSegment;
word* oldPtr = followFars(oldRef, oldSegment);
word* oldPtr = followFars(oldRef, origRefTarget, oldSegment);
KJ_REQUIRE(oldRef->kind() == WirePointer::LIST,
"Called getList{Field,Element}() but existing pointer is not a list.") {
......@@ -1233,12 +1290,22 @@ struct WireHelpers {
static KJ_ALWAYS_INLINE(Text::Builder getWritableTextPointer(
WirePointer* ref, SegmentBuilder* segment,
const void* defaultValue, ByteCount defaultSize)) {
return getWritableTextPointer(ref, ref->target(), segment, defaultValue, defaultSize);
}
static KJ_ALWAYS_INLINE(Text::Builder getWritableTextPointer(
WirePointer* ref, word* refTarget, SegmentBuilder* segment,
const void* defaultValue, ByteCount defaultSize)) {
if (ref->isNull()) {
Text::Builder builder = initTextPointer(ref, segment, defaultSize);
memcpy(builder.begin(), defaultValue, defaultSize / BYTES);
return builder;
if (defaultSize == 0 * BYTES) {
return nullptr;
} else {
Text::Builder builder = initTextPointer(ref, segment, defaultSize);
memcpy(builder.begin(), defaultValue, defaultSize / BYTES);
return builder;
}
} else {
word* ptr = followFars(ref, segment);
word* ptr = followFars(ref, refTarget, segment);
KJ_REQUIRE(ref->kind() == WirePointer::LIST,
"Called getText{Field,Element}() but existing pointer is not a list.");
......@@ -1271,12 +1338,22 @@ struct WireHelpers {
static KJ_ALWAYS_INLINE(Data::Builder getWritableDataPointer(
WirePointer* ref, SegmentBuilder* segment,
const void* defaultValue, ByteCount defaultSize)) {
return getWritableDataPointer(ref, ref->target(), segment, defaultValue, defaultSize);
}
static KJ_ALWAYS_INLINE(Data::Builder getWritableDataPointer(
WirePointer* ref, word* refTarget, SegmentBuilder* segment,
const void* defaultValue, ByteCount defaultSize)) {
if (ref->isNull()) {
Data::Builder builder = initDataPointer(ref, segment, defaultSize);
memcpy(builder.begin(), defaultValue, defaultSize / BYTES);
return builder;
if (defaultSize == 0 * BYTES) {
return nullptr;
} else {
Data::Builder builder = initDataPointer(ref, segment, defaultSize);
memcpy(builder.begin(), defaultValue, defaultSize / BYTES);
return builder;
}
} else {
word* ptr = followFars(ref, segment);
word* ptr = followFars(ref, refTarget, segment);
KJ_REQUIRE(ref->kind() == WirePointer::LIST,
"Called getData{Field,Element}() but existing pointer is not a list.");
......@@ -1289,6 +1366,11 @@ struct WireHelpers {
static KJ_ALWAYS_INLINE(ObjectBuilder getWritableObjectPointer(
SegmentBuilder* segment, WirePointer* ref, const word* defaultValue)) {
return getWritableObjectPointer(segment, ref, ref->target(), defaultValue);
}
static KJ_ALWAYS_INLINE(ObjectBuilder getWritableObjectPointer(
SegmentBuilder* segment, WirePointer* ref, word* refTarget, const word* defaultValue)) {
word* ptr;
if (ref->isNull()) {
......@@ -1299,7 +1381,7 @@ struct WireHelpers {
ptr = copyMessage(segment, ref, reinterpret_cast<const WirePointer*>(defaultValue));
}
} else {
ptr = followFars(ref, segment);
ptr = followFars(ref, refTarget, segment);
}
if (ref->kind() == WirePointer::LIST) {
......@@ -1333,7 +1415,7 @@ struct WireHelpers {
}
}
static void setStructPointer(SegmentBuilder* segment, WirePointer* ref, StructReader value) {
static word* setStructPointer(SegmentBuilder* segment, WirePointer* ref, StructReader value) {
WordCount dataSize = roundBitsUpToWords(value.dataSize);
WordCount totalSize = dataSize + value.pointerCount * WORDS_PER_POINTER;
......@@ -1351,9 +1433,11 @@ struct WireHelpers {
setObjectPointer(segment, pointerSection + i, readObjectPointer(
value.segment, value.pointers + i, nullptr, value.nestingLimit));
}
return ptr;
}
static void setListPointer(SegmentBuilder* segment, WirePointer* ref, ListReader value) {
static word* setListPointer(SegmentBuilder* segment, WirePointer* ref, ListReader value) {
WordCount totalSize = roundBitsUpToWords(value.elementCount * value.step);
if (value.step * ELEMENTS <= BITS_PER_WORD * WORDS) {
......@@ -1386,6 +1470,8 @@ struct WireHelpers {
ref->listRef.set(elementSize, value.elementCount);
memcpy(ptr, value.ptr, totalSize * BYTES_PER_WORD / BYTES);
}
return ptr;
} else {
// List of structs.
word* ptr = allocate(ref, segment, totalSize + POINTER_SIZE_IN_WORDS, WirePointer::LIST);
......@@ -1413,6 +1499,8 @@ struct WireHelpers {
src += POINTER_SIZE_IN_WORDS;
}
}
return ptr;
}
}
......@@ -1431,6 +1519,37 @@ struct WireHelpers {
}
}
static void adopt(SegmentBuilder* segment, WirePointer* ref, OrphanBuilder&& value) {
KJ_REQUIRE(value.segment->getArena() == segment->getArena(),
"Adopted object must live in the same message.");
if (!ref->isNull()) {
zeroObject(segment, ref);
}
if (value.segment == nullptr) {
// Set null.
memset(ref, 0, sizeof(*ref));
} else if (value.tagAsPtr()->kind() == WirePointer::FAR) {
// FAR pointers are position-independent, so we can just copy.
memcpy(ref, value.tagAsPtr(), sizeof(WirePointer));
} else {
WireHelpers::transferPointer(segment, ref, value.segment, value.tagAsPtr(), value.location);
}
// Take ownership away from the OrphanBuilder.
memset(value.tagAsPtr(), 0, sizeof(WirePointer));
value.location = nullptr;
value.segment = nullptr;
}
static OrphanBuilder disown(SegmentBuilder* segment, WirePointer* ref) {
OrphanBuilder result(ref, segment,
ref->kind() == WirePointer::FAR ? nullptr : ref->target());
memset(ref, 0, sizeof(*ref));
return result;
}
// -----------------------------------------------------------------
static KJ_ALWAYS_INLINE(StructReader readStructPointer(
......@@ -1815,7 +1934,7 @@ StructBuilder StructBuilder::initRoot(
}
void StructBuilder::setRoot(SegmentBuilder* segment, word* location, StructReader value) {
return WireHelpers::setStructPointer(segment, reinterpret_cast<WirePointer*>(location), value);
WireHelpers::setStructPointer(segment, reinterpret_cast<WirePointer*>(location), value);
}
StructBuilder StructBuilder::getRoot(
......@@ -1896,17 +2015,25 @@ ObjectBuilder StructBuilder::getObjectField(
}
void StructBuilder::setStructField(WirePointerCount ptrIndex, StructReader value) {
return WireHelpers::setStructPointer(segment, pointers + ptrIndex, value);
WireHelpers::setStructPointer(segment, pointers + ptrIndex, value);
}
void StructBuilder::setListField(WirePointerCount ptrIndex, ListReader value) {
return WireHelpers::setListPointer(segment, pointers + ptrIndex, value);
WireHelpers::setListPointer(segment, pointers + ptrIndex, value);
}
void StructBuilder::setObjectField(WirePointerCount ptrIndex, ObjectReader value) {
return WireHelpers::setObjectPointer(segment, pointers + ptrIndex, value);
}
void StructBuilder::adopt(WirePointerCount ptrIndex, OrphanBuilder&& value) {
WireHelpers::adopt(segment, pointers + ptrIndex, kj::mv(value));
}
OrphanBuilder StructBuilder::disown(WirePointerCount ptrIndex) {
return WireHelpers::disown(segment, pointers + ptrIndex);
}
bool StructBuilder::isPointerFieldNull(WirePointerCount ptrIndex) {
return (pointers + ptrIndex)->isNull();
}
......@@ -1916,6 +2043,10 @@ StructReader StructBuilder::asReader() const {
dataSize, pointerCount, bit0Offset, std::numeric_limits<int>::max());
}
BuilderArena* StructBuilder::getArena() {
return segment->getArena();
}
// =======================================================================================
// StructReader
......@@ -2100,7 +2231,7 @@ ObjectBuilder ListBuilder::getObjectElement(ElementCount index) {
}
void ListBuilder::setListElement(ElementCount index, ListReader value) {
return WireHelpers::setListPointer(
WireHelpers::setListPointer(
segment, reinterpret_cast<WirePointer*>(ptr + index * step / BITS_PER_BYTE), value);
}
......@@ -2109,11 +2240,25 @@ void ListBuilder::setObjectElement(ElementCount index, ObjectReader value) {
segment, reinterpret_cast<WirePointer*>(ptr + index * step / BITS_PER_BYTE), value);
}
void ListBuilder::adopt(ElementCount index, OrphanBuilder&& value) {
WireHelpers::adopt(
segment, reinterpret_cast<WirePointer*>(ptr + index * step / BITS_PER_BYTE), kj::mv(value));
}
OrphanBuilder ListBuilder::disown(ElementCount index) {
return WireHelpers::disown(
segment, reinterpret_cast<WirePointer*>(ptr + index * step / BITS_PER_BYTE));
}
ListReader ListBuilder::asReader() const {
return ListReader(segment, ptr, elementCount, step, structDataSize, structPointerCount,
std::numeric_limits<int>::max());
}
BuilderArena* ListBuilder::getArena() {
return segment->getArena();
}
// =======================================================================================
// ListReader
......@@ -2202,5 +2347,225 @@ ObjectReader ListReader::getObjectElement(ElementCount index) const {
segment, checkAlignment(ptr + index * step / BITS_PER_BYTE), nullptr, nestingLimit);
}
// =======================================================================================
// OrphanBuilder
// TODO(cleanup): This is hacky. In order to reuse WireHelpers in OrphanBuilder::init*() and
// OrphanBuilder::copy*(), we actually pass them pointers to WirePointers allocated on the stack.
// When this pointer is initialized, the offset may be truncated and thus end up being garbage.
// This is OK because we define the offset to be ignored in this case, but there is a fair amount
// of non-local reasoning going on. Additionally, in order to select a segment, these methods
// manually compute the size that they expect WireHelpers to allocate. This is redundant and
// could theoretically get out-of-sync with the way WireHelpers computes them, leading to subtle
// bugs. Some refactoring could make this cleaner, perhaps, but I couldn't think of a reasonably
// non-invasive approach.
OrphanBuilder OrphanBuilder::initStruct(BuilderArena* arena, StructSize size) {
OrphanBuilder result;
result.segment = arena->getSegmentWithAvailable(size.total());
StructBuilder builder = WireHelpers::initStructPointer(result.tagAsPtr(), result.segment, size);
KJ_ASSERT(builder.segment == result.segment,
"Orphan was unexpectedly allocated in a different segment.");
result.location = reinterpret_cast<word*>(builder.data);
return result;
}
OrphanBuilder OrphanBuilder::initList(
BuilderArena* arena, ElementCount elementCount, FieldSize elementSize) {
KJ_DREQUIRE(elementSize != FieldSize::INLINE_COMPOSITE,
"Use OrphanBuilder::initStructList() instead.");
decltype(BITS / ELEMENTS) bitsPerElement = dataBitsPerElement(elementSize) +
pointersPerElement(elementSize) * BITS_PER_POINTER;
OrphanBuilder result;
result.segment = arena->getSegmentWithAvailable(
WireHelpers::roundBitsUpToWords(bitsPerElement * ElementCount64(elementCount)));
ListBuilder builder =
WireHelpers::initListPointer(result.tagAsPtr(), result.segment, elementCount, elementSize);
KJ_ASSERT(builder.segment == result.segment,
"Orphan was unexpectedly allocated in a different segment.");
result.location = reinterpret_cast<word*>(builder.ptr);
return result;
}
OrphanBuilder OrphanBuilder::initStructList(
BuilderArena* arena, ElementCount elementCount, StructSize elementSize) {
if (elementSize.preferredListEncoding != FieldSize::INLINE_COMPOSITE) {
// Small data-only struct. Allocate a list of primitives instead.
return initList(arena, elementCount, elementSize.preferredListEncoding);
} else {
OrphanBuilder result;
result.segment = arena->getSegmentWithAvailable(
elementCount * (elementSize.total() / ELEMENTS) + POINTER_SIZE_IN_WORDS);
ListBuilder builder = WireHelpers::initStructListPointer(
result.tagAsPtr(), result.segment, elementCount, elementSize);
KJ_ASSERT(builder.segment == result.segment,
"Orphan was unexpectedly allocated in a different segment.");
result.location = reinterpret_cast<word*>(builder.ptr);
return result;
}
}
OrphanBuilder OrphanBuilder::initText(BuilderArena* arena, ByteCount size) {
OrphanBuilder result;
result.segment = arena->getSegmentWithAvailable(
WireHelpers::roundBytesUpToWords(size + 1 * BYTES));
Text::Builder builder = WireHelpers::initTextPointer(result.tagAsPtr(), result.segment, size);
result.location = reinterpret_cast<word*>(builder.begin());
KJ_ASSERT(result.segment->getOffsetTo(result.location) <= result.segment->getSize(),
"Orphan was unexpectedly allocated in a different segment.");
return result;
}
OrphanBuilder OrphanBuilder::initData(BuilderArena* arena, ByteCount size) {
OrphanBuilder result;
result.segment = arena->getSegmentWithAvailable(WireHelpers::roundBytesUpToWords(size));
Data::Builder builder = WireHelpers::initDataPointer(result.tagAsPtr(), result.segment, size);
result.location = reinterpret_cast<word*>(builder.begin());
KJ_ASSERT(result.segment->getOffsetTo(result.location) <= result.segment->getSize(),
"Orphan was unexpectedly allocated in a different segment.");
return result;
}
OrphanBuilder OrphanBuilder::copy(BuilderArena* arena, StructReader copyFrom) {
OrphanBuilder result;
result.segment = arena->getSegmentWithAvailable(
WireHelpers::roundBitsUpToWords(copyFrom.getDataSectionSize()) +
copyFrom.getPointerSectionSize() * WORDS_PER_POINTER);
word* ptr = WireHelpers::setStructPointer(result.segment, result.tagAsPtr(), copyFrom);
KJ_ASSERT(result.segment->getOffsetTo(ptr) <= result.segment->getSize(),
"Orphan was unexpectedly allocated in a different segment.");
result.location = reinterpret_cast<word*>(ptr);
return result;
}
OrphanBuilder OrphanBuilder::copy(BuilderArena* arena, ListReader copyFrom) {
OrphanBuilder result;
WordCount wordCount = WireHelpers::roundBitsUpToWords(
copyFrom.step * ElementCount64(copyFrom.elementCount));
if (copyFrom.step * ELEMENTS > BITS_PER_WORD * WORDS) {
// This is a struct list.
wordCount += 1 * WORDS;
}
result.segment = arena->getSegmentWithAvailable(wordCount);
word* ptr = WireHelpers::setListPointer(result.segment, result.tagAsPtr(), copyFrom);
KJ_ASSERT(result.segment->getOffsetTo(ptr) <= result.segment->getSize(),
"Orphan was unexpectedly allocated in a different segment.");
result.location = reinterpret_cast<word*>(ptr);
return result;
}
OrphanBuilder OrphanBuilder::copy(BuilderArena* arena, Text::Reader copyFrom) {
OrphanBuilder result;
result.segment = arena->getSegmentWithAvailable(
WireHelpers::roundBytesUpToWords((copyFrom.size() + 1) * BYTES));
Text::Builder text = WireHelpers::initTextPointer(
result.tagAsPtr(), result.segment, copyFrom.size() * BYTES);
result.location = reinterpret_cast<word*>(text.begin());
KJ_ASSERT(result.segment->getOffsetTo(result.location) <= result.segment->getSize(),
"Orphan was unexpectedly allocated in a different segment.");
memcpy(text.begin(), copyFrom.begin(), copyFrom.size());
return result;
}
OrphanBuilder OrphanBuilder::copy(BuilderArena* arena, Data::Reader copyFrom) {
OrphanBuilder result;
result.segment = arena->getSegmentWithAvailable(
WireHelpers::roundBytesUpToWords(copyFrom.size() * BYTES));
Data::Builder data = WireHelpers::initDataPointer(
result.tagAsPtr(), result.segment, copyFrom.size() * BYTES);
result.location = reinterpret_cast<word*>(data.begin());
KJ_ASSERT(result.segment->getOffsetTo(result.location) <= result.segment->getSize(),
"Orphan was unexpectedly allocated in a different segment.");
memcpy(data.begin(), copyFrom.begin(), copyFrom.size());
return result;
}
StructBuilder OrphanBuilder::asStruct(StructSize size) {
StructBuilder result = WireHelpers::getWritableStructPointer(
tagAsPtr(), location, segment, size, nullptr);
// Watch out, the pointer could have been updated if the object had to be relocated.
if (tagAsPtr()->kind() == WirePointer::FAR) {
location = nullptr;
} else {
location = reinterpret_cast<word*>(result.data);
}
return result;
}
ListBuilder OrphanBuilder::asList(FieldSize elementSize) {
ListBuilder result = WireHelpers::getWritableListPointer(
tagAsPtr(), location, segment, elementSize, nullptr);
// Watch out, the pointer could have been updated if the object had to be relocated.
if (tagAsPtr()->kind() == WirePointer::FAR) {
location = nullptr;
} else {
location = reinterpret_cast<word*>(result.ptr);
}
return result;
}
ListBuilder OrphanBuilder::asStructList(StructSize elementSize) {
ListBuilder result = WireHelpers::getWritableStructListPointer(
tagAsPtr(), location, segment, elementSize, nullptr);
// Watch out, the pointer could have been updated if the object had to be relocated.
if (tagAsPtr()->kind() == WirePointer::FAR) {
location = nullptr;
} else {
location = reinterpret_cast<word*>(result.ptr);
}
return result;
}
Text::Builder OrphanBuilder::asText() {
// Never relocates.
return WireHelpers::getWritableTextPointer(tagAsPtr(), location, segment, nullptr, 0 * BYTES);
}
Data::Builder OrphanBuilder::asData() {
// Never relocates.
return WireHelpers::getWritableDataPointer(tagAsPtr(), location, segment, nullptr, 0 * BYTES);
}
ObjectBuilder OrphanBuilder::asObject() {
ObjectBuilder result = WireHelpers::getWritableObjectPointer(
segment, tagAsPtr(), location, nullptr);
// Watch out, the pointer could have been updated if the object had to be relocated.
if (tagAsPtr()->kind() == WirePointer::FAR) {
location = nullptr;
} else {
switch (result.kind) {
case ObjectKind::STRUCT:
location = reinterpret_cast<word*>(result.structBuilder.data);
break;
case ObjectKind::LIST:
location = reinterpret_cast<word*>(result.listBuilder.ptr);
break;
case ObjectKind::NULL_POINTER:
location = nullptr;
break;
}
}
return result;
}
void OrphanBuilder::euthanize() {
WireHelpers::zeroObject(segment, reinterpret_cast<WirePointer*>(&tag), location);
memset(&tag, 0, sizeof(tag)); // Use memset to comply with aliasing rules.
segment = nullptr;
location = nullptr;
}
} // namespace _ (private)
} // namespace capnp
......@@ -43,12 +43,14 @@ class StructBuilder;
class StructReader;
class ListBuilder;
class ListReader;
class OrphanBuilder;
struct ObjectBuilder;
struct ObjectReader;
struct WirePointer;
struct WireHelpers;
class SegmentReader;
class SegmentBuilder;
class BuilderArena;
// =============================================================================
......@@ -120,45 +122,40 @@ inline constexpr PointersPerElement pointersPerElement(FieldSize size) {
return size == FieldSize::POINTER ? 1 * POINTERS / ELEMENTS : 0 * POINTERS / ELEMENTS;
}
} // namespace _ (private)
enum class Kind: uint8_t {
PRIMITIVE,
BLOB,
ENUM,
STRUCT,
UNION,
INTERFACE,
LIST,
UNKNOWN
template <size_t size> struct ElementSizeForByteSize;
template <> struct ElementSizeForByteSize<1> { static constexpr FieldSize value = FieldSize::BYTE; };
template <> struct ElementSizeForByteSize<2> { static constexpr FieldSize value = FieldSize::TWO_BYTES; };
template <> struct ElementSizeForByteSize<4> { static constexpr FieldSize value = FieldSize::FOUR_BYTES; };
template <> struct ElementSizeForByteSize<8> { static constexpr FieldSize value = FieldSize::EIGHT_BYTES; };
template <typename T> struct ElementSizeForType {
static constexpr FieldSize value =
// Primitive types that aren't special-cased below can be determined from sizeof().
kind<T>() == Kind::PRIMITIVE ? ElementSizeForByteSize<sizeof(T)>::value :
kind<T>() == Kind::ENUM ? FieldSize::TWO_BYTES :
kind<T>() == Kind::STRUCT ? FieldSize::INLINE_COMPOSITE :
// Everything else is a pointer.
FieldSize::POINTER;
};
namespace _ { // private
// Void and bool are special.
template <> struct ElementSizeForType<Void> { static constexpr FieldSize value = FieldSize::VOID; };
template <> struct ElementSizeForType<bool> { static constexpr FieldSize value = FieldSize::BIT; };
template <typename T> struct Kind_ { static constexpr Kind kind = Kind::UNKNOWN; };
template <> struct Kind_<Void> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<bool> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<int8_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<int16_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<int32_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<int64_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<uint8_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<uint16_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<uint32_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<uint64_t> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<float> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<double> { static constexpr Kind kind = Kind::PRIMITIVE; };
template <> struct Kind_<Text> { static constexpr Kind kind = Kind::BLOB; };
template <> struct Kind_<Data> { static constexpr Kind kind = Kind::BLOB; };
// Lists and blobs are pointers, not structs.
template <typename T, bool b> struct ElementSizeForType<List<T, b>> {
static constexpr FieldSize value = FieldSize::POINTER;
};
template <> struct ElementSizeForType<Text> {
static constexpr FieldSize value = FieldSize::POINTER;
};
template <> struct ElementSizeForType<Data> {
static constexpr FieldSize value = FieldSize::POINTER;
};
} // namespace _ (private)
template <typename T>
inline constexpr Kind kind() {
return _::Kind_<T>::kind;
}
// =============================================================================
namespace _ { // private
......@@ -359,11 +356,22 @@ public:
void setObjectField(WirePointerCount ptrIndex, ObjectReader value);
// Sets a pointer field to a deep copy of the given value.
void adopt(WirePointerCount ptrIndex, OrphanBuilder&& orphan);
// Adopt the orphaned object as the value of the given pointer field. The orphan must reside in
// the same message as the new parent. No copying occurs.
OrphanBuilder disown(WirePointerCount ptrIndex);
// Detach the given pointer field from this object. The pointer becomes null, and the child
// object is returned as an orphan.
bool isPointerFieldNull(WirePointerCount ptrIndex);
StructReader asReader() const;
// Gets a StructReader pointing at the same memory.
BuilderArena* getArena();
// Gets the arena in which this object is allocated.
private:
SegmentBuilder* segment; // Memory segment in which the struct resides.
void* data; // Pointer to the encoded data.
......@@ -387,6 +395,7 @@ private:
friend class ListBuilder;
friend struct WireHelpers;
friend class OrphanBuilder;
};
class StructReader {
......@@ -553,9 +562,20 @@ public:
void setObjectElement(ElementCount index, ObjectReader value);
// Sets a pointer element to a deep copy of the given value.
void adopt(ElementCount index, OrphanBuilder&& orphan);
// Adopt the orphaned object as the value of the given pointer field. The orphan must reside in
// the same message as the new parent. No copying occurs.
OrphanBuilder disown(ElementCount index);
// Detach the given pointer field from this object. The pointer becomes null, and the child
// object is returned as an orphan.
ListReader asReader() const;
// Get a ListReader pointing at the same memory.
BuilderArena* getArena();
// Gets the arena in which this object is allocated.
private:
SegmentBuilder* segment; // Memory segment in which the list resides.
......@@ -580,6 +600,7 @@ private:
friend class StructBuilder;
friend struct WireHelpers;
friend class OrphanBuilder;
};
class ListReader {
......@@ -642,6 +663,7 @@ private:
friend class StructReader;
friend class ListBuilder;
friend struct WireHelpers;
friend class OrphanBuilder;
};
// -------------------------------------------------------------------
......@@ -686,6 +708,84 @@ struct ObjectReader {
: kind(ObjectKind::LIST), listReader(listReader) {}
};
// -------------------------------------------------------------------
class OrphanBuilder {
public:
inline OrphanBuilder(): segment(nullptr), location(nullptr) { memset(&tag, 0, sizeof(tag)); }
OrphanBuilder(const OrphanBuilder& other) = delete;
inline OrphanBuilder(OrphanBuilder&& other);
inline ~OrphanBuilder();
static OrphanBuilder initStruct(BuilderArena* arena, StructSize size);
static OrphanBuilder initList(BuilderArena* arena, ElementCount elementCount,
FieldSize elementSize);
static OrphanBuilder initStructList(BuilderArena* arena, ElementCount elementCount,
StructSize elementSize);
static OrphanBuilder initText(BuilderArena* arena, ByteCount size);
static OrphanBuilder initData(BuilderArena* arena, ByteCount size);
static OrphanBuilder copy(BuilderArena* arena, StructReader copyFrom);
static OrphanBuilder copy(BuilderArena* arena, ListReader copyFrom);
static OrphanBuilder copy(BuilderArena* arena, Text::Reader copyFrom);
static OrphanBuilder copy(BuilderArena* arena, Data::Reader copyFrom);
OrphanBuilder& operator=(const OrphanBuilder& other) = delete;
inline OrphanBuilder& operator=(OrphanBuilder&& other);
inline bool operator==(decltype(nullptr)) { return location == nullptr; }
inline bool operator!=(decltype(nullptr)) { return location != nullptr; }
StructBuilder asStruct(StructSize size);
// Interpret as a struct, or throw an exception if not a struct.
ListBuilder asList(FieldSize elementSize);
// Interpret as a list, or throw an exception if not a list. elementSize cannot be
// INLINE_COMPOSITE -- use asStructList() instead.
ListBuilder asStructList(StructSize elementSize);
// Interpret as a struct list, or throw an exception if not a list.
Text::Builder asText();
Data::Builder asData();
// Interpret as a blob, or throw an exception if not a blob.
ObjectBuilder asObject();
// Interpret as an arbitrary object.
private:
static_assert(1 * POINTERS * WORDS_PER_POINTER == 1 * WORDS,
"This struct assumes a pointer is one word.");
word tag;
// Contains an encoded WirePointer representing this object. WirePointer is defined in
// layout.c++, but fits in a word.
//
// If the pointer is a FAR pointer, then the tag is a complete pointer, `location` is null, and
// `segment` is any arbitrary segment in the message. Otherwise, the tag's offset is garbage,
// `location` points at the actual object, and `segment` points at the segment where `location`
// resides.
SegmentBuilder* segment;
// Segment in which the object resides, or an arbitrary segment in the message if the tag is a
// FAR pointer.
word* location;
// Pointer to the object, or null if the tag is a FAR pointer.
inline OrphanBuilder(const void* tagPtr, SegmentBuilder* segment, word* location)
: segment(segment), location(location) {
memcpy(&tag, tagPtr, sizeof(tag));
}
inline WirePointer* tagAsPtr() { return reinterpret_cast<WirePointer*>(&tag); }
void euthanize();
// Erase the target object, zeroing it out and possibly reclaiming the memory. Called when
// the OrphanBuilder is being destroyed or overwritten and it is non-null.
friend struct WireHelpers;
};
// =======================================================================================
// Internal implementation details...
......@@ -869,6 +969,35 @@ template <> void ListBuilder::setBlobElement<Data>(ElementCount index, typename
template <> typename Data::Builder ListBuilder::getBlobElement<Data>(ElementCount index);
template <> typename Data::Reader ListReader::getBlobElement<Data>(ElementCount index) const;
// -------------------------------------------------------------------
inline OrphanBuilder::OrphanBuilder(OrphanBuilder&& other)
: segment(other.segment), location(other.location) {
memcpy(&tag, &other.tag, sizeof(tag)); // Needs memcpy to comply with aliasing rules.
other.segment = nullptr;
other.location = nullptr;
}
inline OrphanBuilder::~OrphanBuilder() {
if (segment != nullptr) euthanize();
}
inline OrphanBuilder& OrphanBuilder::operator=(OrphanBuilder&& other) {
// With normal smart pointers, it's important to handle the case where the incoming pointer
// is actually transitively owned by this one. In this case, euthanize() would destroy `other`
// before we copied it. This isn't possible in the case of `OrphanBuilder` because it only
// owns message objects, and `other` is not itself a message object, therefore cannot possibly
// be transitively owned by `this`.
if (segment != nullptr) euthanize();
segment = other.segment;
location = other.location;
memcpy(&tag, &other.tag, sizeof(tag)); // Needs memcpy to comply with aliasing rules.
other.segment = nullptr;
other.location = nullptr;
return *this;
}
} // namespace _ (private)
} // namespace capnp
......
......@@ -25,81 +25,12 @@
#define CAPNP_LIST_H_
#include "layout.h"
#include "orphan.h"
#include <initializer_list>
namespace capnp {
namespace _ { // private
template <typename T, Kind k = kind<T>()>
struct PointerHelpers;
} // namespace _ (private)
template <typename T, Kind k = kind<T>()>
struct List;
template <typename T, Kind k = kind<T>()> struct ReaderFor_ { typedef typename T::Reader Type; };
template <typename T> struct ReaderFor_<T, Kind::PRIMITIVE> { typedef T Type; };
template <typename T> struct ReaderFor_<T, Kind::ENUM> { typedef T Type; };
template <typename T> using ReaderFor = typename ReaderFor_<T>::Type;
// The type returned by List<T>::Reader::operator[].
template <typename T, Kind k = kind<T>()> struct BuilderFor_ { typedef typename T::Builder Type; };
template <typename T> struct BuilderFor_<T, Kind::PRIMITIVE> { typedef T Type; };
template <typename T> struct BuilderFor_<T, Kind::ENUM> { typedef T Type; };
template <typename T> using BuilderFor = typename BuilderFor_<T>::Type;
// The type returned by List<T>::Builder::operator[].
template <typename T>
using FromReader = typename kj::Decay<T>::Reads;
// FromReader<MyType::Reader> = MyType (for any Cap'n Proto type).
template <typename T>
using FromBuilder = typename kj::Decay<T>::Builds;
// FromBuilder<MyType::Builder> = MyType (for any Cap'n Proto type).
template <typename T, Kind k = kind<T>()> struct TypeIfEnum_;
template <typename T> struct TypeIfEnum_<T, Kind::ENUM> { typedef T Type; };
template <typename T>
using TypeIfEnum = typename TypeIfEnum_<kj::Decay<T>>::Type;
namespace _ { // private
template <typename T, Kind k> struct Kind_<List<T, k>> { static constexpr Kind kind = Kind::LIST; };
template <size_t size> struct FieldSizeForByteSize;
template <> struct FieldSizeForByteSize<1> { static constexpr FieldSize value = FieldSize::BYTE; };
template <> struct FieldSizeForByteSize<2> { static constexpr FieldSize value = FieldSize::TWO_BYTES; };
template <> struct FieldSizeForByteSize<4> { static constexpr FieldSize value = FieldSize::FOUR_BYTES; };
template <> struct FieldSizeForByteSize<8> { static constexpr FieldSize value = FieldSize::EIGHT_BYTES; };
template <typename T> struct FieldSizeForType {
static constexpr FieldSize value =
// Primitive types that aren't special-cased below can be determined from sizeof().
kind<T>() == Kind::PRIMITIVE ? FieldSizeForByteSize<sizeof(T)>::value :
kind<T>() == Kind::ENUM ? FieldSize::TWO_BYTES :
kind<T>() == Kind::STRUCT ? FieldSize::INLINE_COMPOSITE :
// Everything else is a pointer.
FieldSize::POINTER;
};
// Void and bool are special.
template <> struct FieldSizeForType<Void> { static constexpr FieldSize value = FieldSize::VOID; };
template <> struct FieldSizeForType<bool> { static constexpr FieldSize value = FieldSize::BIT; };
// Lists and blobs are pointers, not structs.
template <typename T, bool b> struct FieldSizeForType<List<T, b>> {
static constexpr FieldSize value = FieldSize::POINTER;
};
template <> struct FieldSizeForType<Text> {
static constexpr FieldSize value = FieldSize::POINTER;
};
template <> struct FieldSizeForType<Data> {
static constexpr FieldSize value = FieldSize::POINTER;
};
template <typename T>
class TemporaryPointer {
// This class is a little hack which lets us define operator->() in cases where it needs to
......@@ -193,6 +124,9 @@ struct List<T, Kind::PRIMITIVE> {
friend struct _::PointerHelpers;
template <typename U, Kind K>
friend struct List;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
class Builder {
......@@ -225,34 +159,37 @@ struct List<T, Kind::PRIMITIVE> {
private:
_::ListBuilder builder;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
private:
inline static _::ListBuilder initAsElementOf(
_::ListBuilder& builder, uint index, uint size) {
return builder.initListElement(
index * ELEMENTS, _::FieldSizeForType<T>::value, size * ELEMENTS);
index * ELEMENTS, _::ElementSizeForType<T>::value, size * ELEMENTS);
}
inline static _::ListBuilder getAsElementOf(
_::ListBuilder& builder, uint index) {
return builder.getListElement(index * ELEMENTS, _::FieldSizeForType<T>::value);
return builder.getListElement(index * ELEMENTS, _::ElementSizeForType<T>::value);
}
inline static _::ListReader getAsElementOf(
const _::ListReader& reader, uint index) {
return reader.getListElement(index * ELEMENTS, _::FieldSizeForType<T>::value);
return reader.getListElement(index * ELEMENTS, _::ElementSizeForType<T>::value);
}
inline static _::ListBuilder initAsFieldOf(
_::StructBuilder& builder, WirePointerCount index, uint size) {
return builder.initListField(index, _::FieldSizeForType<T>::value, size * ELEMENTS);
return builder.initListField(index, _::ElementSizeForType<T>::value, size * ELEMENTS);
}
inline static _::ListBuilder getAsFieldOf(
_::StructBuilder& builder, WirePointerCount index, const word* defaultValue) {
return builder.getListField(index, _::FieldSizeForType<T>::value, defaultValue);
return builder.getListField(index, _::ElementSizeForType<T>::value, defaultValue);
}
inline static _::ListReader getAsFieldOf(
const _::StructReader& reader, WirePointerCount index, const word* defaultValue) {
return reader.getListField(index, _::FieldSizeForType<T>::value, defaultValue);
return reader.getListField(index, _::ElementSizeForType<T>::value, defaultValue);
}
template <typename U, Kind k>
......@@ -292,6 +229,9 @@ struct List<T, Kind::STRUCT> {
friend struct _::PointerHelpers;
template <typename U, Kind K>
friend struct List;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
class Builder {
......@@ -309,10 +249,10 @@ struct List<T, Kind::STRUCT> {
return typename T::Builder(builder.getStructElement(index * ELEMENTS));
}
// There are no init() or set() methods for lists of structs because the elements of the list
// are inlined and are initialized when the list is initialized. This means that init() would
// be redundant, and set() would risk data loss if the input struct were from a newer version
// of teh protocol.
// There are no init(), set(), adopt(), or disown() methods for lists of structs because the
// elements of the list are inlined and are initialized when the list is initialized. This
// means that init() would be redundant, and set() would risk data loss if the input struct
// were from a newer version of the protocol.
typedef _::IndexingIterator<Builder, typename T::Builder> Iterator;
inline Iterator begin() { return Iterator(this, 0); }
......@@ -320,6 +260,9 @@ struct List<T, Kind::STRUCT> {
private:
_::ListBuilder builder;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
private:
......@@ -384,6 +327,9 @@ struct List<List<T>, Kind::LIST> {
friend struct _::PointerHelpers;
template <typename U, Kind K>
friend struct List;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
class Builder {
......@@ -413,6 +359,12 @@ struct List<List<T>, Kind::LIST> {
l.set(i++, element);
}
}
inline void adopt(uint index, Orphan<T>&& value) {
builder.adopt(index * ELEMENTS, kj::mv(value));
}
inline Orphan<T> disown(uint index) {
return Orphan<T>(builder.disown(index * ELEMENTS));
}
typedef _::IndexingIterator<Builder, typename List<T>::Builder> Iterator;
inline Iterator begin() { return Iterator(this, 0); }
......@@ -420,6 +372,9 @@ struct List<List<T>, Kind::LIST> {
private:
_::ListBuilder builder;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
private:
......@@ -482,6 +437,9 @@ struct List<T, Kind::BLOB> {
friend struct _::PointerHelpers;
template <typename U, Kind K>
friend struct List;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
class Builder {
......@@ -504,6 +462,12 @@ struct List<T, Kind::BLOB> {
inline typename T::Builder init(uint index, uint size) {
return builder.initBlobElement<T>(index * ELEMENTS, size * BYTES);
}
inline void adopt(uint index, Orphan<T>&& value) {
builder.adopt(index * ELEMENTS, kj::mv(value));
}
inline Orphan<T> disown(uint index) {
return Orphan<T>(builder.disown(index * ELEMENTS));
}
typedef _::IndexingIterator<Builder, typename T::Builder> Iterator;
inline Iterator begin() { return Iterator(this, 0); }
......@@ -511,6 +475,9 @@ struct List<T, Kind::BLOB> {
private:
_::ListBuilder builder;
friend class Orphanage;
template <typename U, Kind K>
friend struct ToDynamic_;
};
private:
......
......@@ -116,6 +116,14 @@ kj::ArrayPtr<const kj::ArrayPtr<const word>> MessageBuilder::getSegmentsForOutpu
}
}
Orphanage MessageBuilder::getOrphanage() {
// We must ensure that the arena and root pointer have been allocated before the Orphanage
// can be used.
if (!allocatedArena) getRootSegment();
return Orphanage(arena());
}
// =======================================================================================
SegmentArrayMessageReader::SegmentArrayMessageReader(
......
......@@ -156,6 +156,8 @@ public:
kj::ArrayPtr<const kj::ArrayPtr<const word>> getSegmentsForOutput();
Orphanage getOrphanage();
private:
// Space in which we can construct a BuilderArena. We don't use BuilderArena directly here
// because we don't want clients to have to #include arena.h, which itself includes a bunch of
......
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "message.h"
#include <kj/debug.h>
#include <gtest/gtest.h>
#include "test-util.h"
namespace capnp {
namespace _ { // private
namespace {
TEST(Orphans, Structs) {
MallocMessageBuilder builder;
auto root = builder.initRoot<TestAllTypes>();
initTestMessage(root.initStructField());
EXPECT_TRUE(root.hasStructField());
Orphan<TestAllTypes> orphan = root.disownStructField();
EXPECT_FALSE(orphan == nullptr);
checkTestMessage(orphan.get().asReader());
EXPECT_FALSE(root.hasStructField());
root.adoptStructField(kj::mv(orphan));
EXPECT_TRUE(orphan == nullptr);
EXPECT_TRUE(root.hasStructField());
checkTestMessage(root.asReader().getStructField());
}
TEST(Orphans, Lists) {
MallocMessageBuilder builder;
auto root = builder.initRoot<TestAllTypes>();
root.setUInt32List({12, 34, 56});
EXPECT_TRUE(root.hasUInt32List());
Orphan<List<uint32_t>> orphan = root.disownUInt32List();
EXPECT_FALSE(orphan == nullptr);
checkList(orphan.get().asReader(), {12, 34, 56});
EXPECT_FALSE(root.hasUInt32List());
root.adoptUInt32List(kj::mv(orphan));
EXPECT_TRUE(orphan == nullptr);
EXPECT_TRUE(root.hasUInt32List());
checkList(root.asReader().getUInt32List(), {12, 34, 56});
}
TEST(Orphans, Text) {
MallocMessageBuilder builder;
auto root = builder.initRoot<TestAllTypes>();
root.setTextField("foo");
EXPECT_TRUE(root.hasTextField());
Orphan<Text> orphan = root.disownTextField();
EXPECT_FALSE(orphan == nullptr);
EXPECT_EQ("foo", orphan.get());
EXPECT_FALSE(root.hasTextField());
root.adoptTextField(kj::mv(orphan));
EXPECT_TRUE(orphan == nullptr);
EXPECT_TRUE(root.hasTextField());
EXPECT_EQ("foo", root.getTextField());
}
TEST(Orphans, Data) {
MallocMessageBuilder builder;
auto root = builder.initRoot<TestAllTypes>();
root.setDataField(data("foo"));
EXPECT_TRUE(root.hasDataField());
Orphan<Data> orphan = root.disownDataField();
EXPECT_FALSE(orphan == nullptr);
EXPECT_EQ(data("foo"), orphan.get());
EXPECT_FALSE(root.hasDataField());
root.adoptDataField(kj::mv(orphan));
EXPECT_TRUE(orphan == nullptr);
EXPECT_TRUE(root.hasDataField());
EXPECT_EQ(data("foo"), root.getDataField());
}
#if KJ_NO_EXCEPTIONS
#undef EXPECT_ANY_THROW
#define EXPECT_ANY_THROW(code) EXPECT_DEATH(code, ".")
#define EXPECT_NONFATAL_FAILURE(code) code
#else
#define EXPECT_NONFATAL_FAILURE EXPECT_ANY_THROW
#endif
TEST(Orphans, NoCrossMessageTransfers) {
MallocMessageBuilder builder1;
MallocMessageBuilder builder2;
auto root1 = builder1.initRoot<TestAllTypes>();
auto root2 = builder2.initRoot<TestAllTypes>();
initTestMessage(root1.initStructField());
EXPECT_ANY_THROW(root2.adoptStructField(root1.disownStructField()));
}
TEST(Orphans, OrphanageStruct) {
MallocMessageBuilder builder;
Orphan<TestAllTypes> orphan = builder.getOrphanage().newOrphan<TestAllTypes>();
initTestMessage(orphan.get());
checkTestMessage(orphan.get().asReader());
auto root = builder.initRoot<TestAllTypes>();
root.adoptStructField(kj::mv(orphan));
}
TEST(Orphans, OrphanageList) {
MallocMessageBuilder builder;
Orphan<List<uint32_t>> orphan = builder.getOrphanage().newOrphan<List<uint32_t>>(2);
orphan.get().set(0, 123);
orphan.get().set(1, 456);
List<uint32_t>::Reader reader = orphan.get().asReader();
ASSERT_EQ(2u, reader.size());
EXPECT_EQ(123u, reader[0]);
EXPECT_EQ(456u, reader[1]);
auto root = builder.initRoot<TestAllTypes>();
root.adoptUInt32List(kj::mv(orphan));
}
TEST(Orphans, OrphanageText) {
MallocMessageBuilder builder;
Orphan<Text> orphan = builder.getOrphanage().newOrphan<Text>(8);
ASSERT_EQ(8u, orphan.get().size());
memcpy(orphan.get().begin(), "12345678", 8);
auto root = builder.initRoot<TestAllTypes>();
root.adoptTextField(kj::mv(orphan));
EXPECT_EQ("12345678", root.getTextField());
}
TEST(Orphans, OrphanageData) {
MallocMessageBuilder builder;
Orphan<Data> orphan = builder.getOrphanage().newOrphan<Data>(2);
ASSERT_EQ(2u, orphan.get().size());
orphan.get()[0] = 123;
orphan.get()[1] = 45;
auto root = builder.initRoot<TestAllTypes>();
root.adoptDataField(kj::mv(orphan));
ASSERT_EQ(2u, root.getDataField().size());
EXPECT_EQ(123u, root.getDataField()[0]);
EXPECT_EQ(45u, root.getDataField()[1]);
}
TEST(Orphans, OrphanageStructCopy) {
MallocMessageBuilder builder1;
MallocMessageBuilder builder2;
auto root1 = builder1.initRoot<TestAllTypes>();
initTestMessage(root1);
Orphan<TestAllTypes> orphan = builder2.getOrphanage().newOrphanCopy(root1.asReader());
checkTestMessage(orphan.get().asReader());
auto root2 = builder2.initRoot<TestAllTypes>();
root2.adoptStructField(kj::mv(orphan));
}
TEST(Orphans, OrphanageListCopy) {
MallocMessageBuilder builder1;
MallocMessageBuilder builder2;
auto root1 = builder1.initRoot<TestAllTypes>();
root1.setUInt32List({12, 34, 56});
Orphan<List<uint32_t>> orphan = builder2.getOrphanage().newOrphanCopy(
root1.asReader().getUInt32List());
checkList(orphan.get().asReader(), {12, 34, 56});
auto root2 = builder2.initRoot<TestAllTypes>();
root2.adoptUInt32List(kj::mv(orphan));
}
TEST(Orphans, OrphanageTextCopy) {
MallocMessageBuilder builder;
Orphan<Text> orphan = builder.getOrphanage().newOrphanCopy(Text::Reader("foobarba"));
EXPECT_EQ("foobarba", orphan.get().asReader());
auto root = builder.initRoot<TestAllTypes>();
root.adoptTextField(kj::mv(orphan));
}
TEST(Orphans, OrphanageDataCopy) {
MallocMessageBuilder builder;
Orphan<Data> orphan = builder.getOrphanage().newOrphanCopy(data("foo"));
EXPECT_EQ(data("foo"), orphan.get().asReader());
auto root = builder.initRoot<TestAllTypes>();
root.adoptDataField(kj::mv(orphan));
}
TEST(Orphans, ZeroOut) {
MallocMessageBuilder builder;
TestAllTypes::Reader orphanReader;
{
Orphan<TestAllTypes> orphan = builder.getOrphanage().newOrphan<TestAllTypes>();
orphanReader = orphan.get().asReader();
initTestMessage(orphan.get());
checkTestMessage(orphan.get().asReader());
}
// Once the Orphan destructor is called, the message should be zero'd out.
checkTestMessageAllZero(orphanReader);
}
TEST(Orphans, StructObject) {
MallocMessageBuilder builder;
auto root = builder.initRoot<test::TestObject>();
initTestMessage(root.initObjectField<TestAllTypes>());
EXPECT_TRUE(root.hasObjectField());
Orphan<TestAllTypes> orphan = root.disownObjectField<TestAllTypes>();
EXPECT_FALSE(orphan == nullptr);
checkTestMessage(orphan.get().asReader());
EXPECT_FALSE(root.hasObjectField());
root.adoptObjectField(kj::mv(orphan));
EXPECT_TRUE(orphan == nullptr);
EXPECT_TRUE(root.hasObjectField());
checkTestMessage(root.asReader().getObjectField<TestAllTypes>());
}
TEST(Orphans, ListObject) {
MallocMessageBuilder builder;
auto root = builder.initRoot<test::TestObject>();
root.setObjectField<List<uint32_t>>({12, 34, 56});
EXPECT_TRUE(root.hasObjectField());
Orphan<List<uint32_t>> orphan = root.disownObjectField<List<uint32_t>>();
EXPECT_FALSE(orphan == nullptr);
checkList(orphan.get().asReader(), {12, 34, 56});
EXPECT_FALSE(root.hasObjectField());
root.adoptObjectField(kj::mv(orphan));
EXPECT_TRUE(orphan == nullptr);
EXPECT_TRUE(root.hasObjectField());
checkList(root.asReader().getObjectField<List<uint32_t>>(), {12, 34, 56});
}
TEST(Orphans, DynamicStruct) {
MallocMessageBuilder builder;
auto root = builder.initRoot<test::TestObject>();
initTestMessage(root.initObjectField<TestAllTypes>());
EXPECT_TRUE(root.hasObjectField());
Orphan<DynamicStruct> orphan =
root.disownObjectField<DynamicStruct>(Schema::from<TestAllTypes>());
EXPECT_FALSE(orphan == nullptr);
EXPECT_TRUE(orphan.get().getSchema() == Schema::from<TestAllTypes>());
checkDynamicTestMessage(orphan.get().asReader());
EXPECT_FALSE(root.hasObjectField());
root.adoptObjectField(kj::mv(orphan));
EXPECT_TRUE(orphan == nullptr);
EXPECT_TRUE(root.hasObjectField());
checkTestMessage(root.asReader().getObjectField<TestAllTypes>());
}
TEST(Orphans, DynamicList) {
MallocMessageBuilder builder;
auto root = builder.initRoot<test::TestObject>();
root.setObjectField<List<uint32_t>>({12, 34, 56});
EXPECT_TRUE(root.hasObjectField());
Orphan<DynamicList> orphan = root.disownObjectField<DynamicList>(Schema::from<List<uint32_t>>());
EXPECT_FALSE(orphan == nullptr);
checkList<uint32_t>(orphan.get().asReader(), {12, 34, 56});
EXPECT_FALSE(root.hasObjectField());
root.adoptObjectField(kj::mv(orphan));
EXPECT_TRUE(orphan == nullptr);
EXPECT_TRUE(root.hasObjectField());
checkList(root.asReader().getObjectField<List<uint32_t>>(), {12, 34, 56});
}
TEST(Orphans, OrphanageDynamicStruct) {
MallocMessageBuilder builder;
Orphan<DynamicStruct> orphan = builder.getOrphanage().newOrphan(Schema::from<TestAllTypes>());
initDynamicTestMessage(orphan.get());
checkDynamicTestMessage(orphan.get().asReader());
auto root = builder.initRoot<test::TestObject>();
root.adoptObjectField(kj::mv(orphan));
checkTestMessage(root.asReader().getObjectField<TestAllTypes>());
}
TEST(Orphans, OrphanageDynamicList) {
MallocMessageBuilder builder;
Orphan<DynamicList> orphan = builder.getOrphanage().newOrphan(Schema::from<List<uint32_t>>(), 2);
orphan.get().set(0, 123);
orphan.get().set(1, 456);
checkList<uint32_t>(orphan.get().asReader(), {123, 456});
auto root = builder.initRoot<test::TestObject>();
root.adoptObjectField(kj::mv(orphan));
checkList(root.getObjectField<List<uint32_t>>(), {123, 456});
}
TEST(Orphans, OrphanageDynamicStructCopy) {
MallocMessageBuilder builder1;
MallocMessageBuilder builder2;
auto root1 = builder1.initRoot<test::TestObject>();
initTestMessage(root1.initObjectField<TestAllTypes>());
Orphan<DynamicStruct> orphan = builder2.getOrphanage().newOrphanCopy(
root1.asReader().getObjectField<DynamicStruct>(Schema::from<TestAllTypes>()));
checkDynamicTestMessage(orphan.get().asReader());
auto root2 = builder2.initRoot<test::TestObject>();
root2.adoptObjectField(kj::mv(orphan));
checkTestMessage(root2.asReader().getObjectField<TestAllTypes>());
}
TEST(Orphans, OrphanageDynamicListCopy) {
MallocMessageBuilder builder1;
MallocMessageBuilder builder2;
auto root1 = builder1.initRoot<test::TestObject>();
root1.setObjectField<List<uint32_t>>({12, 34, 56});
Orphan<DynamicList> orphan = builder2.getOrphanage().newOrphanCopy(
root1.asReader().getObjectField<DynamicList>(Schema::from<List<uint32_t>>()));
checkList<uint32_t>(orphan.get().asReader(), {12, 34, 56});
auto root2 = builder2.initRoot<test::TestObject>();
root2.adoptObjectField(kj::mv(orphan));
checkList(root2.getObjectField<List<uint32_t>>(), {12, 34, 56});
}
TEST(Orphans, OrphanageFromBuilder) {
MallocMessageBuilder builder;
auto root = builder.initRoot<TestAllTypes>();
{
Orphanage orphanage = Orphanage::getForMessageContaining(root);
Orphan<TestAllTypes> orphan = orphanage.newOrphan<TestAllTypes>();
initTestMessage(orphan.get());
root.adoptStructField(kj::mv(orphan));
checkTestMessage(root.asReader().getStructField());
}
{
Orphanage orphanage = Orphanage::getForMessageContaining(root.initBoolList(3));
Orphan<TestAllTypes> orphan = orphanage.newOrphan<TestAllTypes>();
initTestMessage(orphan.get());
root.adoptStructField(kj::mv(orphan));
checkTestMessage(root.asReader().getStructField());
}
{
Orphanage orphanage = Orphanage::getForMessageContaining(toDynamic(root));
Orphan<TestAllTypes> orphan = orphanage.newOrphan<TestAllTypes>();
initTestMessage(orphan.get());
root.adoptStructField(kj::mv(orphan));
checkTestMessage(root.asReader().getStructField());
}
{
Orphanage orphanage = Orphanage::getForMessageContaining(toDynamic(root.initBoolList(3)));
Orphan<TestAllTypes> orphan = orphanage.newOrphan<TestAllTypes>();
initTestMessage(orphan.get());
root.adoptStructField(kj::mv(orphan));
checkTestMessage(root.asReader().getStructField());
}
}
} // namespace
} // namespace _ (private)
} // namespace capnp
// 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 CAPNP_ORPHAN_H_
#define CAPNP_ORPHAN_H_
#include "layout.h"
namespace capnp {
class StructSchema;
class ListSchema;
struct DynamicStruct;
struct DynamicList;
template <typename T>
class Orphan {
// Represents an object which is allocated within some message builder but has no pointers
// pointing at it. An Orphan can later be "adopted" by some other object as one of that object's
// fields, without having to copy the orphan. For a field `foo` of pointer type, the generated
// code will define builder methods `void adoptFoo(Orphan<T>)` and `Orphan<T> disownFoo()`.
// Orphans can also be created independently of any parent using an Orphanage.
//
// `Orphan<T>` can be moved but not copied, like `Own<T>`, so that it is impossible for one
// orphan to be adopted multiple times. If an orphan is destroyed without being adopted, its
// contents are zero'd out (and possibly reused, if we ever implement the ability to reuse space
// in a message arena).
public:
Orphan() = default;
KJ_DISALLOW_COPY(Orphan);
Orphan(Orphan&&) = default;
Orphan& operator=(Orphan&&) = default;
inline typename T::Builder get();
inline bool operator==(decltype(nullptr)) { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) { return builder == nullptr; }
private:
_::OrphanBuilder builder;
inline Orphan(_::OrphanBuilder&& builder): builder(kj::mv(builder)) {}
template <typename, Kind>
friend struct _::PointerHelpers;
template <typename, Kind>
friend struct List;
friend class Orphanage;
};
class Orphanage: private kj::DisallowConstCopy {
// Use to directly allocate Orphan objects, without having a parent object allocate and then
// disown the object.
public:
inline Orphanage(): arena(nullptr) {}
template <typename BuilderType>
static Orphanage getForMessageContaining(BuilderType builder);
// Construct an Orphanage that allocates within the message containing the given Builder. This
// allows the constructed Orphans to be adopted by objects within said message.
//
// This constructor takes the builder rather than having the builder have a getOrphanage() method
// because this is an advanced feature and we don't want to pollute the builder APIs with it.
//
// Note that if you have a direct pointer to the `MessageBuilder`, you can simply call its
// `getOrphanage()` method.
template <typename RootType>
Orphan<RootType> newOrphan();
// Allocate a new orphaned struct.
template <typename RootType>
Orphan<RootType> newOrphan(uint size);
// Allocate a new orphaned list or blob.
Orphan<DynamicStruct> newOrphan(StructSchema schema);
// Dynamically create an orphan struct with the given schema. You must
// #include <capnp/dynamic.h> to use this.
Orphan<DynamicList> newOrphan(ListSchema schema, uint size);
// Dynamically create an orphan list with the given schema. You must #include <capnp/dynamic.h>
// to use this.
template <typename Reader>
Orphan<FromReader<Reader>> newOrphanCopy(const Reader& copyFrom);
// Allocate a new orphaned object (struct, list, or blob) and initialize it as a copy of the
// given object.
private:
_::BuilderArena* arena;
inline explicit Orphanage(_::BuilderArena* arena): arena(arena) {}
template <typename T, Kind = kind<T>()>
struct GetInnerBuilder;
template <typename T, Kind = kind<T>()>
struct GetInnerReader;
template <typename T>
struct NewOrphanListImpl;
friend class MessageBuilder;
};
// =======================================================================================
// Inline implementation details.
namespace _ { // private
template <typename T, Kind = kind<T>()>
struct OrphanGetImpl;
template <typename T>
struct OrphanGetImpl<T, Kind::STRUCT> {
static inline typename T::Builder apply(_::OrphanBuilder& builder) {
return typename T::Builder(builder.asStruct(_::structSize<T>()));
}
};
template <typename T>
struct OrphanGetImpl<List<T>, Kind::LIST> {
static inline typename List<T>::Builder apply(_::OrphanBuilder& builder) {
return typename List<T>::Builder(builder.asList(_::ElementSizeForType<T>::value));
}
};
template <typename T>
struct OrphanGetImpl<List<T, Kind::STRUCT>, Kind::LIST> {
static inline typename T::Builder apply(_::OrphanBuilder& builder) {
return typename List<T>::Builder(builder.asStructList(_::structSize<T>()));
}
};
template <>
struct OrphanGetImpl<Text, Kind::BLOB> {
static inline Text::Builder apply(_::OrphanBuilder& builder) {
return Text::Builder(builder.asText());
}
};
template <>
struct OrphanGetImpl<Data, Kind::BLOB> {
static inline Data::Builder apply(_::OrphanBuilder& builder) {
return Data::Builder(builder.asData());
}
};
} // namespace _ (private)
template <typename T>
inline typename T::Builder Orphan<T>::get() {
return _::OrphanGetImpl<T>::apply(builder);
}
template <typename T>
struct Orphanage::GetInnerBuilder<T, Kind::STRUCT> {
static inline _::StructBuilder apply(typename T::Builder& t) {
return t._builder;
}
};
template <typename T>
struct Orphanage::GetInnerBuilder<T, Kind::LIST> {
static inline _::ListBuilder apply(typename T::Builder& t) {
return t.builder;
}
};
template <typename BuilderType>
Orphanage Orphanage::getForMessageContaining(BuilderType builder) {
return Orphanage(GetInnerBuilder<FromBuilder<BuilderType>>::apply(builder).getArena());
}
template <typename RootType>
Orphan<RootType> Orphanage::newOrphan() {
return Orphan<RootType>(_::OrphanBuilder::initStruct(arena, _::structSize<RootType>()));
}
template <typename T, Kind k>
struct Orphanage::NewOrphanListImpl<List<T, k>> {
static inline _::OrphanBuilder apply(_::BuilderArena* arena, uint size) {
return _::OrphanBuilder::initList(arena, size * ELEMENTS, _::ElementSizeForType<T>::value);
}
};
template <typename T>
struct Orphanage::NewOrphanListImpl<List<T, Kind::STRUCT>> {
static inline _::OrphanBuilder apply(_::BuilderArena* arena, uint size) {
return _::OrphanBuilder::initList(arena, size * ELEMENTS, _::structSize<T>());
}
};
template <>
struct Orphanage::NewOrphanListImpl<Text> {
static inline _::OrphanBuilder apply(_::BuilderArena* arena, uint size) {
return _::OrphanBuilder::initText(arena, size * BYTES);
}
};
template <>
struct Orphanage::NewOrphanListImpl<Data> {
static inline _::OrphanBuilder apply(_::BuilderArena* arena, uint size) {
return _::OrphanBuilder::initData(arena, size * BYTES);
}
};
template <typename RootType>
Orphan<RootType> Orphanage::newOrphan(uint size) {
return Orphan<RootType>(NewOrphanListImpl<RootType>::apply(arena, size));
}
template <typename T>
struct Orphanage::GetInnerReader<T, Kind::STRUCT> {
static inline _::StructReader apply(const typename T::Reader& t) {
return t._reader;
}
};
template <typename T>
struct Orphanage::GetInnerReader<T, Kind::LIST> {
static inline _::ListReader apply(const typename T::Reader& t) {
return t.reader;
}
};
template <typename T>
struct Orphanage::GetInnerReader<T, Kind::BLOB> {
static inline const typename T::Reader& apply(const typename T::Reader& t) {
return t;
}
};
template <typename Reader>
Orphan<FromReader<Reader>> Orphanage::newOrphanCopy(const Reader& copyFrom) {
return Orphan<FromReader<Reader>>(_::OrphanBuilder::copy(
arena, GetInnerReader<FromReader<Reader>>::apply(copyFrom)));
}
} // namespace capnp
#endif // CAPNP_ORPHAN_H_
......@@ -214,30 +214,6 @@ void dynamicInitTestMessage(DynamicStruct::Builder builder) {
builder.set("enumList", {"foo", "garply"});
}
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]);
}
}
inline bool isNaN(float f) { return f != f; }
inline bool isNaN(double f) { return f != f; }
......@@ -347,27 +323,6 @@ void genericCheckTestMessage(Reader reader) {
// Hack because as<>() is a template-parameter-dependent lookup everywhere below...
#define as template as
template <typename T> void expectPrimitiveEq(T a, T b) { EXPECT_EQ(a, b); }
void expectPrimitiveEq(float a, float b) { EXPECT_FLOAT_EQ(a, b); }
void expectPrimitiveEq(double a, double b) { EXPECT_DOUBLE_EQ(a, b); }
void expectPrimitiveEq(Text::Reader a, Text::Builder b) { EXPECT_EQ(a, b); }
void expectPrimitiveEq(Data::Reader a, Data::Builder b) { EXPECT_EQ(a, b); }
template <typename Element, typename T>
void checkList(T reader, std::initializer_list<ReaderFor<Element>> expected) {
auto list = reader.as<DynamicList>();
ASSERT_EQ(expected.size(), list.size());
for (uint i = 0; i < expected.size(); i++) {
expectPrimitiveEq(expected.begin()[i], list[i].as<Element>());
}
auto typed = reader.as<List<Element>>();
ASSERT_EQ(expected.size(), typed.size());
for (uint i = 0; i < expected.size(); i++) {
expectPrimitiveEq(expected.begin()[i], typed[i]);
}
}
Text::Reader name(DynamicEnum e) {
KJ_IF_MAYBE(schema, e.getEnumerant()) {
return schema->getProto().getName();
......
......@@ -28,6 +28,7 @@
#include <iostream>
#include "blob.h"
#include "dynamic.h"
#include <gtest/gtest.h>
namespace capnp {
......@@ -90,6 +91,56 @@ void checkDynamicTestLists(DynamicStruct::Reader reader);
void checkDynamicTestMessageAllZero(DynamicStruct::Builder builder);
void checkDynamicTestMessageAllZero(DynamicStruct::Reader reader);
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]);
}
}
// Hack because as<>() is a template-parameter-dependent lookup everywhere below...
#define as template as
template <typename T> void expectPrimitiveEq(T a, T b) { EXPECT_EQ(a, b); }
inline void expectPrimitiveEq(float a, float b) { EXPECT_FLOAT_EQ(a, b); }
inline void expectPrimitiveEq(double a, double b) { EXPECT_DOUBLE_EQ(a, b); }
inline void expectPrimitiveEq(Text::Reader a, Text::Builder b) { EXPECT_EQ(a, b); }
inline void expectPrimitiveEq(Data::Reader a, Data::Builder b) { EXPECT_EQ(a, b); }
template <typename Element, typename T>
void checkList(T reader, std::initializer_list<ReaderFor<Element>> expected) {
auto list = reader.as<DynamicList>();
ASSERT_EQ(expected.size(), list.size());
for (uint i = 0; i < expected.size(); i++) {
expectPrimitiveEq(expected.begin()[i], list[i].as<Element>());
}
auto typed = reader.as<List<Element>>();
ASSERT_EQ(expected.size(), typed.size());
for (uint i = 0; i < expected.size(); i++) {
expectPrimitiveEq(expected.begin()[i], typed[i]);
}
}
#undef as
} // namespace _ (private)
} // namespace capnp
......
......@@ -190,6 +190,7 @@ private:
template <typename T, ::capnp::Kind k>
friend struct ::capnp::_::PointerHelpers;
friend class ::capnp::MessageBuilder;
friend class ::capnp::Orphanage;
friend ::kj::String KJ_STRINGIFY({{typeFullName}}::Reader reader);
};
......@@ -247,6 +248,8 @@ public:
{{#fieldIsStruct}}
inline {{fieldType}}::Builder init{{fieldTitleCase}}();
{{/fieldIsStruct}}
inline void adopt{{fieldTitleCase}}(::capnp::Orphan<{{fieldType}}>&& value);
inline ::capnp::Orphan<{{fieldType}}> disown{{fieldTitleCase}}();
{{/fieldIsGenericObject}}
{{/fieldIsPrimitive}}
{{#fieldIsGenericObject}}
......@@ -258,12 +261,16 @@ public:
set{{fieldTitleCase}}(std::initializer_list<U> value);
template <typename T, typename... Params> inline typename T::Builder
init{{fieldTitleCase}}(Params&&... params);
template <typename T> void adopt{{fieldTitleCase}}(::capnp::Orphan<T>&& value);
template <typename T, typename... Params> ::capnp::Orphan<T>
disown{{fieldTitleCase}}(Params&&... params);
{{/fieldIsGenericObject}}
{{/typeFields}}
private:
::capnp::_::StructBuilder _builder;
template <typename T, ::capnp::Kind k>
friend struct ::capnp::ToDynamic_;
friend class ::capnp::Orphanage;
friend ::kj::String KJ_STRINGIFY({{typeFullName}}::Builder builder);
};
......@@ -409,6 +416,26 @@ inline {{fieldType}}::Builder {{typeFullName}}::Builder::init{{fieldTitleCase}}(
}
{{/fieldIsStruct}}
inline void {{typeFullName}}::Builder::adopt{{fieldTitleCase}}(
::capnp::Orphan<{{fieldType}}>&& value) {
{{#fieldUnion}}
_builder.setDataField<{{unionTitleCase}}::Which>(
{{unionTagOffset}} * ::capnp::ELEMENTS, {{unionTitleCase}}::{{fieldUpperCase}});
{{/fieldUnion}}
::capnp::_::PointerHelpers<{{fieldType}}>::adopt(
_builder, {{fieldOffset}} * ::capnp::POINTERS, kj::mv(value));
}
inline ::capnp::Orphan<{{fieldType}}> {{typeFullName}}::Builder::disown{{fieldTitleCase}}() {
{{#fieldUnion}}
KJ_IREQUIRE(which() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
{{/fieldUnion}}
return ::capnp::_::PointerHelpers<{{fieldType}}>::disown(
_builder, {{fieldOffset}} * ::capnp::POINTERS);
}
{{/fieldIsGenericObject}}
{{! ------------------------------------------------------------------------------------------- }}
{{#fieldIsGenericObject}}
......@@ -482,6 +509,26 @@ inline typename T::Builder {{typeFullName}}::Builder::init{{fieldTitleCase}}(Par
_builder, {{fieldOffset}} * ::capnp::POINTERS, ::kj::fwd<Params>(params)...);
}
template <typename T>
void {{typeFullName}}::Builder::adopt{{fieldTitleCase}}(::capnp::Orphan<T>&& value) {
{{#fieldUnion}}
_builder.setDataField<{{unionTitleCase}}::Which>(
{{unionTagOffset}} * ::capnp::ELEMENTS, {{unionTitleCase}}::{{fieldUpperCase}});
{{/fieldUnion}}
::capnp::_::PointerHelpers<T>::adopt(
_builder, {{fieldOffset}} * ::capnp::POINTERS, kj::mv(value));
}
template <typename T, typename... Params>
::capnp::Orphan<T> {{typeFullName}}::Builder::disown{{fieldTitleCase}}(Params&&... params) {
{{#fieldUnion}}
KJ_IREQUIRE(which() == {{unionTitleCase}}::{{fieldUpperCase}},
"Must check which() before get()ing a union member.");
{{/fieldUnion}}
return ::capnp::_::PointerHelpers<T>::disown(
_builder, {{fieldOffset}} * ::capnp::POINTERS, ::kj::fwd<Params>(params)...);
}
{{/fieldIsGenericObject}}
{{/fieldIsPrimitive}}
{{/typeFields}}
......
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