Commit cf8c0e00 authored by Kenton Varda's avatar Kenton Varda

Fix Orphan::truncate() to work on null pointers.

parent b51e1b61
......@@ -854,6 +854,8 @@ public:
// the original Orphan<DynamicStruct> is no longer valid after this call; ownership is
// transferred to the returned Orphan<T>.
// TODO(someday): Support truncate().
inline bool operator==(decltype(nullptr)) const { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; }
......
......@@ -2840,18 +2840,23 @@ Data::Reader OrphanBuilder::asDataReader() const {
return WireHelpers::readDataPointer(segment, tagAsPtr(), location, nullptr, 0 * BYTES);
}
void OrphanBuilder::truncate(ElementCount size, bool isText) {
if (isText) size += 1 * ELEMENTS;
bool OrphanBuilder::truncate(ElementCount size, bool isText) {
WirePointer* ref = tagAsPtr();
SegmentBuilder* segment = this->segment;
word* target = WireHelpers::followFars(ref, location, segment);
if (ref->isNull()) {
// We don't know the right element size, so we can't resize this list.
return size == 0 * ELEMENTS;
}
KJ_REQUIRE(ref->kind() == WirePointer::LIST, "Can't truncate non-list.") {
return;
return false;
}
if (isText) size += 1 * ELEMENTS;
ElementSize elementSize = ref->listRef.elementSize();
if (elementSize == ElementSize::INLINE_COMPOSITE) {
......@@ -2861,7 +2866,7 @@ void OrphanBuilder::truncate(ElementCount size, bool isText) {
++target;
KJ_REQUIRE(tag->kind() == WirePointer::STRUCT,
"INLINE_COMPOSITE lists of non-STRUCT type are not supported.") {
return;
return false;
}
StructSize structSize(tag->structRef.dataSize.get(), tag->structRef.ptrCount.get());
WordCount elementWordCount = structSize.total();
......@@ -2969,6 +2974,26 @@ void OrphanBuilder::truncate(ElementCount size, bool isText) {
}
}
}
return true;
}
void OrphanBuilder::truncate(ElementCount size, ElementSize elementSize) {
if (!truncate(size, false)) {
*this = initList(segment->getArena(), size, elementSize);
}
}
void OrphanBuilder::truncate(ElementCount size, StructSize elementSize) {
if (!truncate(size, false)) {
*this = initStructList(segment->getArena(), size, elementSize);
}
}
void OrphanBuilder::truncateText(ElementCount size) {
if (!truncate(size, true)) {
*this = initText(segment->getArena(), size * (1 * BYTES / ELEMENTS));
}
}
void OrphanBuilder::euthanize() {
......
......@@ -759,7 +759,14 @@ public:
Text::Reader asTextReader() const;
Data::Reader asDataReader() const;
void truncate(ElementCount size, bool isText);
bool truncate(ElementCount size, bool isText) KJ_WARN_UNUSED_RESULT;
// Resize the orphan list to the given size. Returns false if the list is currently empty but
// the requested size is non-zero, in which case the caller will need to allocate a new list.
void truncate(ElementCount size, ElementSize elementSize);
void truncate(ElementCount size, StructSize elementSize);
void truncateText(ElementCount size);
// Versions of truncate() that know how to allocate a new list if needed.
private:
static_assert(1 * POINTERS * WORDS_PER_POINTER == 1 * WORDS,
......
......@@ -1061,6 +1061,20 @@ TEST(Orphans, ExtendDataCopy) {
EXPECT_EQ(32, orphan2.getReader()[0]);
}
TEST(Orphans, ExtendDataFromEmpty) {
MallocMessageBuilder message;
auto orphan = message.initRoot<TestAllTypes>().disownDataField();
orphan.truncate(3);
auto reader = orphan.getReader();
EXPECT_EQ(3, reader.size());
for (uint i = 0; i < 3; i++) {
EXPECT_EQ(0, reader[i]);
}
}
TEST(Orphans, TruncateText) {
MallocMessageBuilder message;
auto orphan = message.getOrphanage().newOrphan<Text>(17);
......@@ -1130,6 +1144,20 @@ TEST(Orphans, ExtendTextCopy) {
EXPECT_EQ(32, orphan2.getReader()[0]);
}
TEST(Orphans, ExtendTextFromEmpty) {
MallocMessageBuilder message;
auto orphan = message.initRoot<TestAllTypes>().disownTextField();
orphan.truncate(3);
auto reader = orphan.getReader();
EXPECT_EQ(3, reader.size());
for (uint i = 0; i < 3; i++) {
EXPECT_EQ('\0', reader[i]);
}
}
TEST(Orphans, TruncatePrimitiveList) {
MallocMessageBuilder message;
auto orphan = message.getOrphanage().newOrphan<List<uint32_t>>(7);
......@@ -1220,6 +1248,20 @@ TEST(Orphans, ExtendPrimitiveListCopy) {
EXPECT_EQ(32, orphan2.getReader()[0]);
}
TEST(Orphans, ExtendPointerListFromEmpty) {
MallocMessageBuilder message;
auto orphan = message.initRoot<TestAllTypes>().disownUInt32List();
orphan.truncate(3);
auto reader = orphan.getReader();
EXPECT_EQ(3, reader.size());
for (uint i = 0; i < 3; i++) {
EXPECT_EQ(0, reader[i]);
}
}
TEST(Orphans, TruncatePointerList) {
MallocMessageBuilder message;
......@@ -1333,6 +1375,20 @@ TEST(Orphans, ExtendPointerListCopy) {
EXPECT_EQ(32, orphan2.getReader()[0]);
}
TEST(Orphans, ExtendPointerListFromEmpty) {
MallocMessageBuilder message;
auto orphan = message.initRoot<TestAllTypes>().disownTextList();
orphan.truncate(3);
auto reader = orphan.getReader();
EXPECT_EQ(3, reader.size());
for (uint i = 0; i < 3; i++) {
EXPECT_EQ("", reader[i]);
}
}
TEST(Orphans, TruncateStructList) {
MallocMessageBuilder message;
......@@ -1470,6 +1526,20 @@ TEST(Orphans, ExtendStructListCopy) {
EXPECT_EQ(32, orphan2.getReader()[0]);
}
TEST(Orphans, ExtendStructListFromEmpty) {
MallocMessageBuilder message;
auto orphan = message.initRoot<TestAllTypes>().disownStructList();
orphan.truncate(3);
auto reader = orphan.getReader();
EXPECT_EQ(3, reader.size());
for (uint i = 0; i < 3; i++) {
checkTestMessageAllZero(reader[i]);
}
}
} // namespace
} // namespace _ (private)
} // namespace capnp
......@@ -187,6 +187,13 @@ namespace _ { // private
template <typename T, Kind = CAPNP_KIND(T)>
struct OrphanGetImpl;
template <typename T>
struct OrphanGetImpl<T, Kind::PRIMITIVE> {
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, _::elementSizeForType<T>());
}
};
template <typename T>
struct OrphanGetImpl<T, Kind::STRUCT> {
static inline typename T::Builder apply(_::OrphanBuilder& builder) {
......@@ -195,6 +202,9 @@ struct OrphanGetImpl<T, Kind::STRUCT> {
static inline typename T::Reader applyReader(const _::OrphanBuilder& builder) {
return typename T::Reader(builder.asStructReader(_::structSize<T>()));
}
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, _::structSize<T>());
}
};
#if !CAPNP_LITE
......@@ -206,6 +216,9 @@ struct OrphanGetImpl<T, Kind::INTERFACE> {
static inline typename T::Client applyReader(const _::OrphanBuilder& builder) {
return typename T::Client(builder.asCapability());
}
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, ElementSize::POINTER);
}
};
#endif // !CAPNP_LITE
......@@ -217,6 +230,9 @@ struct OrphanGetImpl<List<T, k>, Kind::LIST> {
static inline typename List<T>::Reader applyReader(const _::OrphanBuilder& builder) {
return typename List<T>::Reader(builder.asListReader(_::ElementSizeForType<T>::value));
}
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, ElementSize::POINTER);
}
};
template <typename T>
......@@ -227,6 +243,9 @@ struct OrphanGetImpl<List<T, Kind::STRUCT>, Kind::LIST> {
static inline typename List<T>::Reader applyReader(const _::OrphanBuilder& builder) {
return typename List<T>::Reader(builder.asListReader(_::ElementSizeForType<T>::value));
}
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, ElementSize::POINTER);
}
};
template <>
......@@ -237,6 +256,9 @@ struct OrphanGetImpl<Text, Kind::BLOB> {
static inline Text::Reader applyReader(const _::OrphanBuilder& builder) {
return Text::Reader(builder.asTextReader());
}
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, ElementSize::POINTER);
}
};
template <>
......@@ -247,6 +269,9 @@ struct OrphanGetImpl<Data, Kind::BLOB> {
static inline Data::Reader applyReader(const _::OrphanBuilder& builder) {
return Data::Reader(builder.asDataReader());
}
static inline void truncateListOf(_::OrphanBuilder& builder, ElementCount size) {
builder.truncate(size, ElementSize::POINTER);
}
};
} // namespace _ (private)
......@@ -263,12 +288,17 @@ inline ReaderFor<T> Orphan<T>::getReader() const {
template <typename T>
inline void Orphan<T>::truncate(uint size) {
builder.truncate(size * ELEMENTS, false);
_::OrphanGetImpl<ListElementType<T>>::truncateListOf(builder, size * ELEMENTS);
}
template <>
inline void Orphan<Text>::truncate(uint size) {
builder.truncate(size * ELEMENTS, true);
builder.truncateText(size * ELEMENTS);
}
template <>
inline void Orphan<Data>::truncate(uint size) {
builder.truncate(size * ELEMENTS, ElementSize::BYTE);
}
template <typename T>
......
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