mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-10 23:17:27 +00:00
Added support for adding new tables/strings to an existing FlatBuffer.
As part of the reflection support. Change-Id: Ie0a8e233bca7dffa4cff7e564660035d97ff8902 Tested: on Linux. Bug:22637258
This commit is contained in:
@@ -293,11 +293,21 @@ public:
|
||||
const_iterator end() const { return const_iterator(Data(), size()); }
|
||||
|
||||
// Change elements if you have a non-const pointer to this object.
|
||||
// Scalars only. See reflection.h, and the documentation.
|
||||
void Mutate(uoffset_t i, T val) {
|
||||
assert(i < size());
|
||||
WriteScalar(data() + i, val);
|
||||
}
|
||||
|
||||
// Change an element of a vector of tables (or strings).
|
||||
// "val" points to the new table/string, as you can obtain from
|
||||
// e.g. reflection::AddFlatBuffer().
|
||||
void MutateOffset(uoffset_t i, const uint8_t *val) {
|
||||
assert(i < size());
|
||||
assert(sizeof(T) == sizeof(uoffset_t));
|
||||
WriteScalar(data() + i, val - (Data() + i * sizeof(uoffset_t)));
|
||||
}
|
||||
|
||||
// The raw data in little endian format. Use with care.
|
||||
const uint8_t *Data() const {
|
||||
return reinterpret_cast<const uint8_t *>(&length_ + 1);
|
||||
@@ -1035,6 +1045,13 @@ class Table {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetPointer(voffset_t field, const uint8_t *val) {
|
||||
auto field_offset = GetOptionalFieldOffset(field);
|
||||
if (!field_offset) return false;
|
||||
WriteScalar(data_ + field_offset, val - (data_ + field_offset));
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t *GetVTable() { return data_ - ReadScalar<soffset_t>(data_); }
|
||||
|
||||
bool CheckField(voffset_t field) const {
|
||||
|
||||
@@ -68,19 +68,19 @@ inline const String *GetFieldS(const Table &table,
|
||||
}
|
||||
|
||||
// Get a field, if you know it's a vector.
|
||||
template<typename T> const Vector<T> *GetFieldV(const Table &table,
|
||||
const reflection::Field &field) {
|
||||
template<typename T> Vector<T> *GetFieldV(const Table &table,
|
||||
const reflection::Field &field) {
|
||||
assert(field.type()->base_type() == reflection::Vector &&
|
||||
sizeof(T) == GetTypeSize(field.type()->element()));
|
||||
return table.GetPointer<const Vector<T> *>(field.offset());
|
||||
return table.GetPointer<Vector<T> *>(field.offset());
|
||||
}
|
||||
|
||||
// Get a field, if you know it's a table.
|
||||
inline const Table *GetFieldT(const Table &table,
|
||||
const reflection::Field &field) {
|
||||
inline Table *GetFieldT(const Table &table,
|
||||
const reflection::Field &field) {
|
||||
assert(field.type()->base_type() == reflection::Obj ||
|
||||
field.type()->base_type() == reflection::Union);
|
||||
return table.GetPointer<const Table *>(field.offset());
|
||||
return table.GetPointer<Table *>(field.offset());
|
||||
}
|
||||
|
||||
// Get any field as a 64bit int, regardless of what it is (bool/int/float/str).
|
||||
@@ -227,27 +227,27 @@ inline void SetAnyFieldS(Table *table, const reflection::Field &field,
|
||||
// a vector into a relative offset, such that it is not affected by resizes.
|
||||
template<typename T, typename U> class pointer_inside_vector {
|
||||
public:
|
||||
pointer_inside_vector(const T *ptr, const std::vector<U> &vec)
|
||||
: offset_(reinterpret_cast<const uint8_t *>(ptr) -
|
||||
reinterpret_cast<const uint8_t *>(vec.data())),
|
||||
pointer_inside_vector(T *ptr, std::vector<U> &vec)
|
||||
: offset_(reinterpret_cast<uint8_t *>(ptr) -
|
||||
reinterpret_cast<uint8_t *>(vec.data())),
|
||||
vec_(vec) {}
|
||||
|
||||
const T *operator*() const {
|
||||
return reinterpret_cast<const T *>(
|
||||
reinterpret_cast<const uint8_t *>(vec_.data()) + offset_);
|
||||
T *operator*() const {
|
||||
return reinterpret_cast<T *>(
|
||||
reinterpret_cast<uint8_t *>(vec_.data()) + offset_);
|
||||
}
|
||||
const T *operator->() const {
|
||||
T *operator->() const {
|
||||
return operator*();
|
||||
}
|
||||
void operator=(const pointer_inside_vector &piv);
|
||||
private:
|
||||
size_t offset_;
|
||||
const std::vector<U> &vec_;
|
||||
std::vector<U> &vec_;
|
||||
};
|
||||
|
||||
// Helper to create the above easily without specifying template args.
|
||||
template<typename T, typename U> pointer_inside_vector<T, U> piv(
|
||||
const T *ptr, const std::vector<U> &vec) {
|
||||
template<typename T, typename U> pointer_inside_vector<T, U> piv(T *ptr,
|
||||
std::vector<U> &vec) {
|
||||
return pointer_inside_vector<T, U>(ptr, vec);
|
||||
}
|
||||
|
||||
@@ -412,12 +412,10 @@ inline void SetString(const reflection::Schema &schema, const std::string &val,
|
||||
flatbuf->data() +
|
||||
sizeof(uoffset_t));
|
||||
if (delta) {
|
||||
// Clear the old string, since we don't want parts of it remaining.
|
||||
memset(flatbuf->data() + start, 0, str->Length());
|
||||
// Different size, we must expand (or contract).
|
||||
ResizeContext(schema, start, delta, flatbuf, root_table);
|
||||
if (delta < 0) {
|
||||
// Clear the old string, since we don't want parts of it remaining.
|
||||
memset(flatbuf->data() + start, 0, str->Length());
|
||||
}
|
||||
}
|
||||
// Copy new data. Safe because we created the right amount of space.
|
||||
memcpy(flatbuf->data() + start, val.c_str(), val.size() + 1);
|
||||
@@ -438,6 +436,12 @@ void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val,
|
||||
auto start = static_cast<uoffset_t>(vec_start + sizeof(uoffset_t) +
|
||||
sizeof(T) * vec->size());
|
||||
if (delta_bytes) {
|
||||
if (delta_elem < 0) {
|
||||
// Clear elements we're throwing away, since some might remain in the
|
||||
// buffer.
|
||||
memset(flatbuf->data() + start + delta_elem * sizeof(T), 0,
|
||||
-delta_elem * sizeof(T));
|
||||
}
|
||||
ResizeContext(schema, start, delta_bytes, flatbuf, root_table);
|
||||
WriteScalar(flatbuf->data() + vec_start, newsize); // Length field.
|
||||
// Set new elements to "val".
|
||||
@@ -453,6 +457,36 @@ void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val,
|
||||
}
|
||||
}
|
||||
|
||||
// Adds any new data (in the form of a new FlatBuffer) to an existing
|
||||
// FlatBuffer. This can be used when any of the above methods are not
|
||||
// sufficient, in particular for adding new tables and new fields.
|
||||
// This is potentially slightly less efficient than a FlatBuffer constructed
|
||||
// in one piece, since the new FlatBuffer doesn't share any vtables with the
|
||||
// existing one.
|
||||
// The return value can now be set using Vector::MutateOffset or SetFieldT
|
||||
// below.
|
||||
inline const uint8_t *AddFlatBuffer(std::vector<uint8_t> &flatbuf,
|
||||
const uint8_t *newbuf, size_t newlen) {
|
||||
// Align to sizeof(uoffset_t) past sizeof(largest_scalar_t) since we're
|
||||
// going to chop off the root offset.
|
||||
while ((flatbuf.size() & (sizeof(uoffset_t) - 1)) ||
|
||||
!(flatbuf.size() & (sizeof(largest_scalar_t) - 1))) {
|
||||
flatbuf.push_back(0);
|
||||
}
|
||||
auto insertion_point = static_cast<uoffset_t>(flatbuf.size());
|
||||
// Insert the entire FlatBuffer minus the root pointer.
|
||||
flatbuf.insert(flatbuf.end(), newbuf + sizeof(uoffset_t),
|
||||
newbuf + newlen - sizeof(uoffset_t));
|
||||
auto root_offset = ReadScalar<uoffset_t>(newbuf) - sizeof(uoffset_t);
|
||||
return flatbuf.data() + insertion_point + root_offset;
|
||||
}
|
||||
|
||||
inline bool SetFieldT(Table *table, const reflection::Field &field,
|
||||
const uint8_t *val) {
|
||||
assert(sizeof(uoffset_t) == GetTypeSize(field.type()->base_type()));
|
||||
return table->SetPointer(field.offset(), val);
|
||||
}
|
||||
|
||||
// Generic copying of tables from a FlatBuffer into a FlatBuffer builder.
|
||||
// Can be used to do any kind of merging/selecting you may want to do out
|
||||
// of existing buffers. Also useful to reconstruct a whole buffer if the
|
||||
|
||||
@@ -370,8 +370,44 @@ void ReflectionTest(uint8_t *flatbuf, size_t length) {
|
||||
// rinventory still valid, so lets read from it.
|
||||
TEST_EQ(rinventory->Get(10), 50);
|
||||
|
||||
// Using reflection, we can also copy tables and other things out of
|
||||
// other FlatBuffers into a new one, either part or whole.
|
||||
// For reflection uses not covered already, there is a more powerful way:
|
||||
// we can simply generate whatever object we want to add/modify in a
|
||||
// FlatBuffer of its own, then add that to an existing FlatBuffer:
|
||||
// As an example, let's add a string to an array of strings.
|
||||
// First, find our field:
|
||||
auto &testarrayofstring_field = *fields->LookupByKey("testarrayofstring");
|
||||
// Find the vector value:
|
||||
auto rtestarrayofstring = flatbuffers::piv(
|
||||
flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::String>>(
|
||||
**rroot, testarrayofstring_field),
|
||||
resizingbuf);
|
||||
// It's a vector of 2 strings, to which we add one more, initialized to
|
||||
// offset 0.
|
||||
flatbuffers::ResizeVector<flatbuffers::Offset<flatbuffers::String>>(
|
||||
schema, 3, 0, *rtestarrayofstring, &resizingbuf);
|
||||
// Here we just create a buffer that contans a single string, but this
|
||||
// could also be any complex set of tables and other values.
|
||||
flatbuffers::FlatBufferBuilder stringfbb;
|
||||
stringfbb.Finish(stringfbb.CreateString("hank"));
|
||||
// Add the contents of it to our existing FlatBuffer.
|
||||
// We do this last, so the pointer doesn't get invalidated (since it is
|
||||
// at the end of the buffer):
|
||||
auto string_ptr = flatbuffers::AddFlatBuffer(resizingbuf,
|
||||
stringfbb.GetBufferPointer(),
|
||||
stringfbb.GetSize());
|
||||
// Finally, set the new value in the vector.
|
||||
rtestarrayofstring->MutateOffset(2, string_ptr);
|
||||
TEST_EQ_STR(rtestarrayofstring->Get(0)->c_str(), "bob");
|
||||
TEST_EQ_STR(rtestarrayofstring->Get(2)->c_str(), "hank");
|
||||
// As an additional test, also set it on the name field.
|
||||
// Note: unlike the name change above, this just overwrites the offset,
|
||||
// rather than changing the string in-place.
|
||||
SetFieldT(*rroot, name_field, string_ptr);
|
||||
TEST_EQ_STR(GetFieldS(**rroot, name_field)->c_str(), "hank");
|
||||
|
||||
// Using reflection, rather than mutating binary FlatBuffers, we can also copy
|
||||
// tables and other things out of other FlatBuffers into a FlatBufferBuilder,
|
||||
// either part or whole.
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
auto root_offset = flatbuffers::CopyTable(fbb, schema, *root_table,
|
||||
*flatbuffers::GetAnyRoot(flatbuf));
|
||||
|
||||
Reference in New Issue
Block a user