Trimmed vtables of trailing zeroes.

This is something the format supports, but none of the builders
were doing. Can save 10-20% on FlatBuffer binary size!

Also fixed the Go tests.

Change-Id: I616c56ce9bbcfcaee23aa24f0532fcb60b6a8c75
Tested: on Linux.
This commit is contained in:
Wouter van Oortmerssen
2017-08-21 13:44:23 -07:00
parent 513958ea72
commit ac1015e3c4
25 changed files with 129 additions and 62 deletions

View File

@@ -704,8 +704,8 @@ class FlatBufferBuilder
explicit FlatBufferBuilder(size_t initial_size = 1024,
Allocator *allocator = nullptr,
bool own_allocator = false)
: buf_(initial_size, allocator, own_allocator), nested(false),
finished(false), minalign_(1), force_defaults_(false),
: buf_(initial_size, allocator, own_allocator), max_voffset_(0),
nested(false), finished(false), minalign_(1), force_defaults_(false),
dedup_vtables_(true), string_pool(nullptr) {
offsetbuf_.reserve(16); // Avoid first few reallocs.
vtables_.reserve(16);
@@ -725,7 +725,7 @@ class FlatBufferBuilder
/// to construct another buffer.
void Clear() {
buf_.clear();
offsetbuf_.clear();
ClearOffsets();
nested = false;
finished = false;
vtables_.clear();
@@ -839,6 +839,7 @@ class FlatBufferBuilder
void TrackField(voffset_t field, uoffset_t off) {
FieldLoc fl = { off, field };
offsetbuf_.push_back(fl);
max_voffset_ = (std::max)(max_voffset_, field);
}
// Like PushElement, but additionally tracks the field this represents.
@@ -899,7 +900,7 @@ class FlatBufferBuilder
// This finishes one serialized object by generating the vtable if it's a
// table, comparing it against existing vtables, and writing the
// resulting vtable offset.
uoffset_t EndTable(uoffset_t start, voffset_t numfields) {
uoffset_t EndTable(uoffset_t start) {
// If you get this assert, a corresponding StartTable wasn't called.
assert(nested);
// Write the vtable offset, which is the start of any Table.
@@ -908,11 +909,17 @@ class FlatBufferBuilder
// Write a vtable, which consists entirely of voffset_t elements.
// It starts with the number of offsets, followed by a type id, followed
// by the offsets themselves. In reverse:
buf_.fill_big(numfields * sizeof(voffset_t));
// Include space for the last offset and ensure empty tables have a
// minimum size.
max_voffset_ = (std::max)(static_cast<voffset_t>(max_voffset_ +
sizeof(voffset_t)),
FieldIndexToOffset(0));
buf_.fill_big(max_voffset_);
auto table_object_size = vtableoffsetloc - start;
assert(table_object_size < 0x10000); // Vtable use 16bit offsets.
PushElement<voffset_t>(static_cast<voffset_t>(table_object_size));
PushElement<voffset_t>(FieldIndexToOffset(numfields));
WriteScalar<voffset_t>(buf_.data() + sizeof(voffset_t),
static_cast<voffset_t>(table_object_size));
WriteScalar<voffset_t>(buf_.data(), max_voffset_);
// Write the offsets into the table
for (auto field_location = offsetbuf_.begin();
field_location != offsetbuf_.end();
@@ -922,7 +929,7 @@ class FlatBufferBuilder
assert(!ReadScalar<voffset_t>(buf_.data() + field_location->id));
WriteScalar<voffset_t>(buf_.data() + field_location->id, pos);
}
offsetbuf_.clear();
ClearOffsets();
auto vt1 = reinterpret_cast<voffset_t *>(buf_.data());
auto vt1_size = ReadScalar<voffset_t>(vt1);
auto vt_use = GetSize();
@@ -955,6 +962,11 @@ class FlatBufferBuilder
return vtableoffsetloc;
}
// DEPRECATED: call the version above instead.
uoffset_t EndTable(uoffset_t start, voffset_t /*numfields*/) {
return EndTable(start);
}
// This checks a required field has been set in a given table that has
// just been constructed.
template<typename T> void Required(Offset<T> table, voffset_t field) {
@@ -973,7 +985,10 @@ class FlatBufferBuilder
uoffset_t EndStruct() { return GetSize(); }
void ClearOffsets() { offsetbuf_.clear(); }
void ClearOffsets() {
offsetbuf_.clear();
max_voffset_ = 0;
}
// Aligns such that when "len" bytes are written, an object can be written
// after it with "alignment" without padding.
@@ -1510,6 +1525,9 @@ class FlatBufferBuilder
// Accumulating offsets of table members while it is being built.
std::vector<FieldLoc> offsetbuf_;
// Track how much of the vtable is in use, so we can output the most compact
// possible vtable.
voffset_t max_voffset_;
// Ensure objects are not nested.
bool nested;

View File

@@ -42,6 +42,29 @@ enum BaseType {
Union = 16
};
inline BaseType (&EnumValuesBaseType())[17] {
static BaseType values[] = {
None,
UType,
Bool,
Byte,
UByte,
Short,
UShort,
Int,
UInt,
Long,
ULong,
Float,
Double,
String,
Vector,
Obj,
Union
};
return values;
}
inline const char **EnumNamesBaseType() {
static const char *names[] = {
"None",
@@ -113,7 +136,7 @@ struct TypeBuilder {
}
TypeBuilder &operator=(const TypeBuilder &);
flatbuffers::Offset<Type> Finish() {
const auto end = fbb_.EndTable(start_, 3);
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<Type>(end);
return o;
}
@@ -173,7 +196,7 @@ struct KeyValueBuilder {
}
KeyValueBuilder &operator=(const KeyValueBuilder &);
flatbuffers::Offset<KeyValue> Finish() {
const auto end = fbb_.EndTable(start_, 2);
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<KeyValue>(end);
fbb_.Required(o, KeyValue::VT_KEY);
return o;
@@ -266,7 +289,7 @@ struct EnumValBuilder {
}
EnumValBuilder &operator=(const EnumValBuilder &);
flatbuffers::Offset<EnumVal> Finish() {
const auto end = fbb_.EndTable(start_, 4);
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<EnumVal>(end);
fbb_.Required(o, EnumVal::VT_NAME);
return o;
@@ -381,7 +404,7 @@ struct EnumBuilder {
}
EnumBuilder &operator=(const EnumBuilder &);
flatbuffers::Offset<Enum> Finish() {
const auto end = fbb_.EndTable(start_, 6);
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<Enum>(end);
fbb_.Required(o, Enum::VT_NAME);
fbb_.Required(o, Enum::VT_VALUES);
@@ -544,7 +567,7 @@ struct FieldBuilder {
}
FieldBuilder &operator=(const FieldBuilder &);
flatbuffers::Offset<Field> Finish() {
const auto end = fbb_.EndTable(start_, 11);
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<Field>(end);
fbb_.Required(o, Field::VT_NAME);
fbb_.Required(o, Field::VT_TYPE);
@@ -695,7 +718,7 @@ struct ObjectBuilder {
}
ObjectBuilder &operator=(const ObjectBuilder &);
flatbuffers::Offset<Object> Finish() {
const auto end = fbb_.EndTable(start_, 7);
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<Object>(end);
fbb_.Required(o, Object::VT_NAME);
fbb_.Required(o, Object::VT_FIELDS);
@@ -808,7 +831,7 @@ struct SchemaBuilder {
}
SchemaBuilder &operator=(const SchemaBuilder &);
flatbuffers::Offset<Schema> Finish() {
const auto end = fbb_.EndTable(start_, 5);
const auto end = fbb_.EndTable(start_);
auto o = flatbuffers::Offset<Schema>(end);
fbb_.Required(o, Schema::VT_OBJECTS);
fbb_.Required(o, Schema::VT_ENUMS);