Commit f017105e authored by Kenton Varda's avatar Kenton Varda

Various stuff in support of dynamic API.

parent 5ab5e2a3
......@@ -30,6 +30,9 @@
#include "list.h"
namespace capnproto {
class DynamicStruct; // So that it can be declared a friend.
namespace internal {
template <typename T>
......@@ -91,8 +94,26 @@ struct PointerHelpers<Data> {
}
};
} // namespace internal
#ifdef CAPNPROTO_PRIVATE
struct TrustedMessage {
typedef const word* Reader;
};
template <>
struct PointerHelpers<TrustedMessage> {
// Reads an Object field as a trusted message pointer. Requires that the containing message is
// itself trusted. This hack is currently private. It is used to locate default values within
// encoded schemas.
static inline const word* get(StructReader reader, WireReferenceCount index) {
return reader.getTrustedPointer(index);
}
};
#endif
} // namespace internal
} // namespace capnproto
#endif // CAPNPROTO_GENERATED_HEADER_SUPPORT_H_
......@@ -908,6 +908,10 @@ struct WireHelpers {
goto useDefault;
}
VALIDATE_INPUT(size > 0, "Message contains text that is not NUL-terminated.") {
goto useDefault;
}
const char* cptr = reinterpret_cast<const char*>(ptr);
--size; // NUL terminator
......@@ -1144,6 +1148,11 @@ ObjectBuilder StructBuilder::getObjectField(
return WireHelpers::getWritableObjectReference(segment, references + refIndex, defaultValue);
}
const word* StructBuilder::getTrustedPointer(WireReferenceCount refIndex) const {
PRECOND(segment == nullptr, "getTrustedPointer() only allowed on trusted messages.");
return reinterpret_cast<const word*>(references + refIndex);
}
StructReader StructBuilder::asReader() const {
return StructReader(segment, data, references,
dataSize, referenceCount, bit0Offset, std::numeric_limits<int>::max());
......@@ -1202,6 +1211,37 @@ ObjectReader StructReader::getObjectField(
// =======================================================================================
// ListBuilder
Text::Builder ListBuilder::asText() {
VALIDATE_INPUT(structDataSize == 8 * BITS && structReferenceCount == 0 * REFERENCES,
"Expected Text, got list of non-bytes.") {
return Text::Builder();
}
size_t size = elementCount / ELEMENTS;
VALIDATE_INPUT(size > 0, "Message contains text that is not NUL-terminated.") {
return Text::Builder();
}
char* cptr = reinterpret_cast<char*>(ptr);
--size; // NUL terminator
VALIDATE_INPUT(cptr[size] == '\0', "Message contains text that is not NUL-terminated.") {
return Text::Builder();
}
return Text::Builder(cptr, size);
}
Data::Builder ListBuilder::asData() {
VALIDATE_INPUT(structDataSize == 8 * BITS && structReferenceCount == 0 * REFERENCES,
"Expected Text, got list of non-bytes.") {
return Data::Builder();
}
return Data::Builder(reinterpret_cast<char*>(ptr), elementCount / ELEMENTS);
}
StructBuilder ListBuilder::getStructElement(ElementCount index, StructSize elementSize) const {
BitCount64 indexBit = ElementCount64(index) * step;
byte* structData = ptr + indexBit / BITS_PER_BYTE;
......@@ -1269,6 +1309,37 @@ ListReader ListBuilder::asReader() const {
// =======================================================================================
// ListReader
Text::Reader ListReader::asText() {
VALIDATE_INPUT(structDataSize == 8 * BITS && structReferenceCount == 0 * REFERENCES,
"Expected Text, got list of non-bytes.") {
return Text::Reader();
}
size_t size = elementCount / ELEMENTS;
VALIDATE_INPUT(size > 0, "Message contains text that is not NUL-terminated.") {
return Text::Reader();
}
const char* cptr = reinterpret_cast<const char*>(ptr);
--size; // NUL terminator
VALIDATE_INPUT(cptr[size] == '\0', "Message contains text that is not NUL-terminated.") {
return Text::Reader();
}
return Text::Reader(cptr, size);
}
Data::Reader ListReader::asData() {
VALIDATE_INPUT(structDataSize == 8 * BITS && structReferenceCount == 0 * REFERENCES,
"Expected Text, got list of non-bytes.") {
return Data::Reader();
}
return Data::Reader(reinterpret_cast<const char*>(ptr), elementCount / ELEMENTS);
}
StructReader ListReader::getStructElement(ElementCount index) const {
VALIDATE_INPUT((segment == nullptr) | (nestingLimit > 0),
"Message is too deeply-nested or contains cycles. See capnproto::ReadOptions.") {
......
......@@ -251,6 +251,10 @@ public:
static StructBuilder initRoot(SegmentBuilder* segment, word* location, StructSize size);
static StructBuilder getRoot(SegmentBuilder* segment, word* location, StructSize size);
inline BitCount getDataSectionSize() const { return dataSize; }
inline WireReferenceCount getPointerSectionSize() const { return referenceCount; }
inline Data::Builder getDataSectionAsBlob();
template <typename T>
CAPNPROTO_ALWAYS_INLINE(T getDataField(ElementCount offset) const);
// Gets the data field value of the given type at the given offset. The offset is measured in
......@@ -320,6 +324,11 @@ public:
ObjectBuilder getObjectField(WireReferenceCount refIndex, const word* defaultValue) const;
// Read a pointer of arbitrary type.
const word* getTrustedPointer(WireReferenceCount refIndex) const;
// If this is a trusted message, get a word* pointing at the location of the pointer. This
// word* can actually be passed to readTrusted() to read the designated sub-object later. If
// this isn't a trusted message, throws an exception.
StructReader asReader() const;
// Gets a StructReader pointing at the same memory.
......@@ -357,6 +366,10 @@ public:
static StructReader readRootTrusted(const word* location);
static StructReader readRoot(const word* location, SegmentReader* segment, int nestingLimit);
inline BitCount getDataSectionSize() const { return dataSize; }
inline WireReferenceCount getPointerSectionSize() const { return referenceCount; }
inline Data::Reader getDataSectionAsBlob();
template <typename T>
CAPNPROTO_ALWAYS_INLINE(T getDataField(ElementCount offset) const);
// Get the data field value of the given type at the given offset. The offset is measured in
......@@ -443,6 +456,10 @@ public:
inline ElementCount size();
// The number of elements in the list.
Text::Builder asText();
Data::Builder asData();
// Reinterpret the list as a blob. Throws an exception if the elements are not byte-sized.
template <typename T>
CAPNPROTO_ALWAYS_INLINE(T getDataElement(ElementCount index) const);
// Get the element of the given type at the given index.
......@@ -525,6 +542,10 @@ public:
inline ElementCount size();
// The number of elements in the list.
Text::Reader asText();
Data::Reader asData();
// Reinterpret the list as a blob. Throws an exception if the elements are not byte-sized.
template <typename T>
CAPNPROTO_ALWAYS_INLINE(T getDataElement(ElementCount index) const);
// Get the element of the given type at the given index.
......@@ -578,55 +599,53 @@ private:
// -------------------------------------------------------------------
enum class ObjectKind {
NULL_POINTER, // Object was read from a null pointer.
STRUCT,
LIST
};
struct ObjectBuilder {
// A reader for any kind of object.
enum Kind {
NULL_POINTER, // Object was read from a null pointer.
STRUCT,
LIST
};
Kind kind;
ObjectKind kind;
union {
StructBuilder structBuilder;
ListBuilder listBuilder;
};
ObjectBuilder(): kind(NULL_POINTER), structBuilder() {}
ObjectBuilder(): kind(ObjectKind::NULL_POINTER), structBuilder() {}
ObjectBuilder(StructBuilder structBuilder)
: kind(STRUCT), structBuilder(structBuilder) {}
: kind(ObjectKind::STRUCT), structBuilder(structBuilder) {}
ObjectBuilder(ListBuilder listBuilderBuilder)
: kind(LIST), listBuilder(listBuilder) {}
: kind(ObjectKind::LIST), listBuilder(listBuilder) {}
};
struct ObjectReader {
// A reader for any kind of object.
enum Kind {
NULL_POINTER, // Object was read from a null pointer.
STRUCT,
LIST
};
Kind kind;
ObjectKind kind;
union {
StructReader structReader;
ListReader listReader;
};
ObjectReader(): kind(NULL_POINTER), structReader() {}
ObjectReader(): kind(ObjectKind::NULL_POINTER), structReader() {}
ObjectReader(StructReader structReader)
: kind(STRUCT), structReader(structReader) {}
: kind(ObjectKind::STRUCT), structReader(structReader) {}
ObjectReader(ListReader listReader)
: kind(LIST), listReader(listReader) {}
: kind(ObjectKind::LIST), listReader(listReader) {}
};
// =======================================================================================
// Internal implementation details...
inline Data::Builder StructBuilder::getDataSectionAsBlob() {
return Data::Builder(reinterpret_cast<char*>(data), dataSize / BITS_PER_BYTE / BYTES);
}
template <typename T>
inline T StructBuilder::getDataField(ElementCount offset) const {
return reinterpret_cast<WireValue<T>*>(data)[offset / ELEMENTS].get();
......@@ -679,6 +698,10 @@ inline void StructBuilder::setDataField(
// -------------------------------------------------------------------
inline Data::Reader StructReader::getDataSectionAsBlob() {
return Data::Reader(reinterpret_cast<const char*>(data), dataSize / BITS_PER_BYTE / BYTES);
}
template <typename T>
T StructReader::getDataField(ElementCount offset) const {
if ((offset + 1 * ELEMENTS) * capnproto::bitsPerElement<T>() <= dataSize) {
......
......@@ -72,7 +72,7 @@ using ReaderFor = typename internal::MaybeReaderBuilder<T>::Reader;
// The type returned by List<T>::Reader::operator[].
template <typename T>
using BuilderFor = typename internal::MaybeReaderBuilder<T>::Reader;
using BuilderFor = typename internal::MaybeReaderBuilder<T>::Builder;
// The type returned by List<T>::Builder::operator[].
namespace internal {
......
......@@ -76,6 +76,26 @@ T instance() noexcept;
// Like std::declval, but doesn't transform T into an rvalue reference. If you want that, specify
// instance<T&&>().
// #including <new> pulls in a lot of crap, but we want placement news. But operator new cannot
// be defined in a namespace, and defining it globally conflicts with the standard library
// definition. So...
namespace internal {
struct PlacementNew {} placementNew;
} // namespace internal;
} // namespace capnproto
inline void* operator new(std::size_t, capnproto::internal::PlacementNew, void* __p) noexcept {
return __p;
}
namespace capnproto {
template <typename T, typename... Params>
void constructAt(T* location, Params&&... params) {
new (internal::placementNew, location) T(capnproto::forward<Params>(params)...);
}
// =======================================================================================
// Maybe
......@@ -85,22 +105,22 @@ public:
Maybe(): isSet(false) {}
Maybe(T&& t)
: isSet(true) {
new (&value) T(move(t));
constructAt(&value, capnproto::move(t));
}
Maybe(const T& t)
: isSet(true) {
new (&value) T(t);
constructAt(&value, t);
}
Maybe(Maybe&& other) noexcept(noexcept(T(capnproto::move(other.value))))
: isSet(other.isSet) {
if (isSet) {
new (&value) T(move(other.value));
constructAt(&value, capnproto::move(other.value));
}
}
Maybe(const Maybe& other)
: isSet(other.isSet) {
if (isSet) {
new (&value) T(other.value);
constructAt(&value, other.value);
}
}
Maybe(std::nullptr_t): isSet(false) {}
......@@ -117,7 +137,7 @@ public:
value.~T();
}
isSet = true;
new (&value) T(capnproto::forward(params)...);
constructAt(&value, capnproto::forward(params)...);
}
inline T& operator*() { return value; }
......@@ -132,7 +152,7 @@ public:
}
isSet = other.isSet;
if (isSet) {
new (&value) T(move(other.value));
constructAt(&value, capnproto::move(other.value));
}
}
return *this;
......@@ -145,7 +165,7 @@ public:
}
isSet = other.isSet;
if (isSet) {
new (&value) T(other.value);
constructAt(&value, other.value);
}
}
return *this;
......
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