Commit 2cbd458a authored by Kenton Varda's avatar Kenton Varda

Add simple functions for interpreting a word array as just the data section of a struct.

Requested by jo_liss on Twitter:

https://twitter.com/jo_liss/status/664884038252027905

(Also implement defaultValue<>() helper which was defined but apparently never implemented.)
parent bfffde6a
......@@ -551,6 +551,9 @@ public:
inline StructReader()
: segment(nullptr), capTable(nullptr), data(nullptr), pointers(nullptr), dataSize(0),
pointerCount(0), nestingLimit(0x7fffffff) {}
inline StructReader(kj::ArrayPtr<const word> data)
: segment(nullptr), capTable(nullptr), data(data.begin()), pointers(nullptr),
dataSize(data.size() * WORDS * BITS_PER_WORD), pointerCount(0), nestingLimit(0x7fffffff) {}
const void* getLocation() const { return data; }
......
......@@ -151,6 +151,23 @@ TEST(Message, MessageBuilderInitSpaceAvailable) {
kj::implicitCast<void*>(builder2.getRoot<TestAllTypes>().getTextField().begin()));
}
TEST(Message, ReadWriteDataStruct) {
MallocMessageBuilder builder;
auto root = builder.getRoot<TestAllTypes>();
root.setUInt32Field(123);
root.setFloat64Field(1.5);
root.setTextField("foo");
auto copy = readDataStruct<TestAllTypes>(writeDataStruct(root));
EXPECT_EQ(123, copy.getUInt32Field());
EXPECT_EQ(1.5, copy.getFloat64Field());
EXPECT_FALSE(copy.hasTextField());
checkTestMessageAllZero(readDataStruct<TestAllTypes>(nullptr));
checkTestMessageAllZero(defaultValue<TestAllTypes>());
}
// TODO(test): More tests.
} // namespace
......
......@@ -271,6 +271,27 @@ void copyToUnchecked(Reader&& reader, kj::ArrayPtr<word> uncheckedBuffer);
// readMessageUnchecked(). The buffer's size must be exactly reader.totalSizeInWords() + 1,
// otherwise an exception will be thrown. The buffer must be zero'd before calling.
template <typename RootType>
typename RootType::Reader readDataStruct(kj::ArrayPtr<const word> data);
// Interprets the given data as a single, data-only struct. Only primitive fields (booleans,
// numbers, and enums) will be readable; all pointers will be null. This is useful if you want
// to use Cap'n Proto as a language/platform-neutral way to pack some bits.
//
// The input is a word array rather than a byte array to enforce alignment. If you have a byte
// array which you know is word-aligned (or if your platform supports unaligned reads and you don't
// mind the performance penalty), then you can use `reinterpret_cast` to convert a byte array into
// a word array:
//
// kj::arrayPtr(reinterpret_cast<const word*>(bytes.begin()),
// reinterpret_cast<const word*>(bytes.end()))
template <typename BuilderType>
typename kj::ArrayPtr<const word> writeDataStruct(BuilderType builder);
// Given a struct builder, get the underlying data section as a word array, suitable for passing
// to `readDataStruct()`.
//
// Note that you may call `.toBytes()` on the returned value to convert to `ArrayPtr<const byte>`.
template <typename Type>
static typename Type::Reader defaultValue();
// Get a default instance of the given struct or list type.
......@@ -452,6 +473,24 @@ void copyToUnchecked(Reader&& reader, kj::ArrayPtr<word> uncheckedBuffer) {
builder.requireFilled();
}
template <typename RootType>
typename RootType::Reader readDataStruct(kj::ArrayPtr<const word> data) {
return typename RootType::Reader(_::StructReader(data));
}
template <typename BuilderType>
typename kj::ArrayPtr<const word> writeDataStruct(BuilderType builder) {
auto bytes = _::PointerHelpers<FromBuilder<BuilderType>>::getInternalBuilder(kj::mv(builder))
.getDataSectionAsBlob();
return kj::arrayPtr(reinterpret_cast<word*>(bytes.begin()),
reinterpret_cast<word*>(bytes.end()));
}
template <typename Type>
static typename Type::Reader defaultValue() {
return typename Type::Reader(_::StructReader());
}
} // namespace capnp
#endif // CAPNP_MESSAGE_H_
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