Re-use identical vtables

This commit is contained in:
Andrew Noyes 2019-06-19 14:21:31 -07:00 committed by Alex Miller
parent 4c5ebd7609
commit 9894d928a1
2 changed files with 24 additions and 20 deletions

View File

@ -34,17 +34,16 @@ bool TraverseMessageTypes::vtableGeneratedBefore(const std::type_index& idx) {
return !f.known_types.insert(idx).second;
}
VTable generate_vtable(size_t numMembers, const std::vector<unsigned>& members,
const std::vector<unsigned>& alignments) {
VTable generate_vtable(size_t numMembers, const std::vector<unsigned>& sizesAlignments) {
if (numMembers == 0) {
return VTable{ 4, 4 };
}
// first is index, second is size
std::vector<std::pair<unsigned, unsigned>> indexed;
indexed.reserve(members.size());
for (unsigned i = 0; i < members.size(); ++i) {
if (members[i] > 0) {
indexed.emplace_back(i, members[i]);
indexed.reserve(numMembers);
for (unsigned i = 0; i < numMembers; ++i) {
if (sizesAlignments[i] > 0) {
indexed.emplace_back(i, sizesAlignments[i]);
}
}
std::stable_sort(indexed.begin(), indexed.end(),
@ -52,15 +51,15 @@ VTable generate_vtable(size_t numMembers, const std::vector<unsigned>& members,
return lhs.second > rhs.second;
});
VTable result;
result.resize(members.size() + 2);
result.resize(numMembers + 2);
// size of the vtable is
// - 2 bytes per member +
// - 2 bytes for the size entry +
// - 2 bytes for the size of the object
result[0] = 2 * members.size() + 4;
result[0] = 2 * numMembers + 4;
int offset = 0;
for (auto p : indexed) {
auto align = alignments[p.first];
auto align = sizesAlignments[numMembers + p.first];
auto& res = result[p.first + 2];
res = offset % align == 0 ? offset : ((offset / align) + 1) * align;
offset = res + p.second;
@ -78,8 +77,10 @@ TEST_CASE("flow/FlatBuffers/test") {
auto* vtable1 = detail::get_vtable<int>();
auto* vtable2 = detail::get_vtable<uint8_t, uint8_t, int, int64_t, int>();
auto* vtable3 = detail::get_vtable<uint8_t, uint8_t, int, int64_t, int>();
auto* vtable4 = detail::get_vtable<uint32_t>();
ASSERT(vtable1 != vtable2);
ASSERT(vtable2 == vtable3);
ASSERT(vtable1 == vtable4); // Different types, but same vtable! Saves space in encoded messages
ASSERT(vtable1->size() == 3);
ASSERT(vtable2->size() == 7);
ASSERT((*vtable2)[0] == 14);
@ -166,7 +167,6 @@ TEST_CASE("flow/FlatBuffers/collectVTables") {
Root root;
const auto* vtables = detail::get_vtableset(root);
ASSERT(vtables == detail::get_vtableset(root));
ASSERT(vtables->offsets.size() == 3);
const auto& root_vtable = *detail::get_vtable<uint8_t, std::vector<Nested2>, Nested>();
const auto& nested_vtable = *detail::get_vtable<uint8_t, std::vector<std::string>, int>();
int root_offset = vtables->offsets.at(&root_vtable);

View File

@ -459,24 +459,28 @@ constexpr auto fields_helper() {
template <class Member>
using Fields = decltype(fields_helper<Member>());
// TODO(anoyes): Make this `template <int... offsets>` so we can re-use
// identical vtables even if they have different types.
// Also, it's important that get_vtable always returns the same VTable pointer
// It's important that get_vtable always returns the same VTable pointer
// so that we can decide equality by comparing the pointers.
extern VTable generate_vtable(size_t numMembers, const std::vector<unsigned>& members,
const std::vector<unsigned>& alignments);
// First |numMembers| elements of sizesAndAlignments are sizes, the second
// |numMembers| elements are alignments.
extern VTable generate_vtable(size_t numMembers, const std::vector<unsigned>& sizesAndAlignments);
template <unsigned... MembersAndAlignments>
const VTable* gen_vtable3() {
static VTable table =
generate_vtable(sizeof...(MembersAndAlignments) / 2, std::vector<unsigned>{ MembersAndAlignments... });
return &table;
}
template <class... Members>
VTable gen_vtable(pack<Members...> p) {
return generate_vtable(sizeof...(Members), std::vector<unsigned>{ { _SizeOf<Members>::size... } },
std::vector<unsigned>{ { _SizeOf<Members>::align... } });
const VTable* gen_vtable2(pack<Members...> p) {
return gen_vtable3<_SizeOf<Members>::size..., _SizeOf<Members>::align...>();
}
template <class... Members>
const VTable* get_vtable() {
static VTable table = gen_vtable(concat_t<Fields<Members>...>{});
return &table;
return gen_vtable2(concat_t<Fields<Members>...>{});
}
template <class F, class... Members>