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 {
......
This diff is collapsed.
This diff is collapsed.
......@@ -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
......
This diff is collapsed.
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef 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