Commit 9f6e055f authored by Kenton Varda's avatar Kenton Varda

Add ability to truncate a list post-allocation and reclaim space. Currently…

Add ability to truncate a list post-allocation and reclaim space. Currently limited to blobs and can only be accessed through the Orphan interface.
parent 4f888f92
......@@ -157,6 +157,10 @@ public:
inline bool isWritable() { return !readOnly; }
inline void tryTruncate(word* from, word* to);
// If `from` points just past the current end of the segment, then move the end back to `to`.
// Otherwise, do nothing.
private:
word* pos;
// Pointer to a pointer to the current end point of the segment, i.e. the location where the
......@@ -399,6 +403,10 @@ inline void SegmentBuilder::reset() {
pos = start;
}
inline void SegmentBuilder::tryTruncate(word* from, word* to) {
if (pos == from) pos = to;
}
} // namespace _ (private)
} // namespace capnp
......
......@@ -2707,6 +2707,40 @@ 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;
WirePointer* ref = tagAsPtr();
SegmentBuilder* segment = this->segment;
word* target = WireHelpers::followFars(ref, location, segment);
KJ_REQUIRE(ref->kind() == WirePointer::LIST, "Can't truncate non-list.") {
return;
}
// TODO(soon): Implement truncation of all sizes.
KJ_ASSERT(ref->listRef.elementSize() == FieldSize::BYTE,
"Not implemented: truncate non-blob.");
auto oldSize = ref->listRef.elementCount();
KJ_REQUIRE(size <= oldSize, "Truncate size must be smaller than existing size.") {
return;
}
ref->listRef.set(ref->listRef.elementSize(), size);
byte* begin = reinterpret_cast<byte*>(target);
byte* truncPoint = begin + size * (1 * BYTES / ELEMENTS);
byte* end = begin + oldSize * (1 * BYTES / ELEMENTS);
memset(truncPoint - isText, 0, end - truncPoint + isText);
word* truncWord = target + WireHelpers::roundBytesUpToWords(size * (1 * BYTES / ELEMENTS));
word* endWord = target + WireHelpers::roundBytesUpToWords(oldSize * (1 * BYTES / ELEMENTS));
segment->tryTruncate(endWord, truncWord);
}
void OrphanBuilder::euthanize() {
// Carefully catch any exceptions and rethrow them as recoverable exceptions since we may be in
// a destructor.
......
......@@ -778,6 +778,8 @@ public:
Text::Reader asTextReader() const;
Data::Reader asDataReader() const;
void truncate(ElementCount size, bool isText);
private:
static_assert(1 * POINTERS * WORDS_PER_POINTER == 1 * WORDS,
"This struct assumes a pointer is one word.");
......
......@@ -990,6 +990,48 @@ TEST(Orphans, ReferenceExternalData_NoZeroImmediateAbandon) {
}
}
TEST(Orphans, Truncate) {
MallocMessageBuilder message;
auto orphan = message.getOrphanage().newOrphan<Data>(17);
auto builder = orphan.get();
memset(builder.begin(), 123, builder.size());
EXPECT_EQ(4, message.getSegmentsForOutput()[0].size());
orphan.truncate(2);
EXPECT_EQ(2, message.getSegmentsForOutput()[0].size());
auto reader = orphan.getReader();
EXPECT_EQ(2, reader.size());
EXPECT_EQ(builder.begin(), reader.begin());
EXPECT_EQ(123, builder[0]);
EXPECT_EQ(123, builder[1]);
EXPECT_EQ(0, builder[2]);
EXPECT_EQ(0, builder[3]);
EXPECT_EQ(0, builder[16]);
}
TEST(Orphans, TruncateText) {
MallocMessageBuilder message;
auto orphan = message.getOrphanage().newOrphan<Text>(17);
auto builder = orphan.get();
memset(builder.begin(), 'a', builder.size());
EXPECT_EQ(4, message.getSegmentsForOutput()[0].size());
orphan.truncate(2);
EXPECT_EQ(2, message.getSegmentsForOutput()[0].size());
auto reader = orphan.getReader();
EXPECT_EQ(2, reader.size());
EXPECT_EQ(builder.begin(), reader.begin());
EXPECT_EQ('a', builder[0]);
EXPECT_EQ('a', builder[1]);
EXPECT_EQ('\0', builder[2]);
EXPECT_EQ('\0', builder[3]);
EXPECT_EQ('\0', builder[16]);
}
} // namespace
} // namespace _ (private)
} // namespace capnp
......@@ -64,6 +64,16 @@ public:
inline bool operator==(decltype(nullptr)) const { return builder == nullptr; }
inline bool operator!=(decltype(nullptr)) const { return builder != nullptr; }
inline void truncate(uint size);
// Truncate the object (which must be a list or a blob) down to the given size. The object's
// current size must be larger than this. The object stays in its current position. If the object
// is the last object in its segment (which is always true if the object is the last thing that
// was allocated in the message) then the truncated space can be reclaimed. Otherwise, the space
// is zero'd out but otherwise lost, like an abandoned orphan.
//
// Any existing readers or builders pointing at the object are invalidated by this call. You
// must call `get()` or `getReader()` again to get the new, valid pointer.
private:
_::OrphanBuilder builder;
......@@ -237,6 +247,16 @@ inline ReaderFor<T> Orphan<T>::getReader() const {
return _::OrphanGetImpl<T>::applyReader(builder);
}
template <typename T>
inline void Orphan<T>::truncate(uint size) {
builder.truncate(size * ELEMENTS, false);
}
template <>
inline void Orphan<Text>::truncate(uint size) {
builder.truncate(size * ELEMENTS, true);
}
template <typename T>
struct Orphanage::GetInnerBuilder<T, Kind::STRUCT> {
static inline _::StructBuilder apply(typename T::Builder& t) {
......
......@@ -160,7 +160,11 @@ public:
inline bool operator!=(decltype(nullptr)) const { return content.size() > 1; }
inline bool operator==(const StringPtr& other) const { return StringPtr(*this) == other; }
inline bool operator!=(const StringPtr& other) const { return !(*this == other); }
inline bool operator!=(const StringPtr& other) const { return StringPtr(*this) != other; }
inline bool operator< (const StringPtr& other) const { return StringPtr(*this) < other; }
inline bool operator> (const StringPtr& other) const { return StringPtr(*this) > other; }
inline bool operator<=(const StringPtr& other) const { return StringPtr(*this) <= other; }
inline bool operator>=(const StringPtr& other) const { return StringPtr(*this) >= other; }
inline bool startsWith(const StringPtr& other) const { return StringPtr(*this).startsWith(other);}
inline bool endsWith(const StringPtr& other) const { return StringPtr(*this).endsWith(other); }
......
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