(&data_[o]);
}
+ const uint8_t *GetAddressOf(uoffset_t o) const { return &data_[o]; }
+ uint8_t *GetAddressOf(uoffset_t o) { return &data_[o]; }
+
private:
uint8_t data_[1];
};
@@ -1017,7 +1048,6 @@ class Table {
? reinterpret_cast(p + ReadScalar(p))
: nullptr;
}
-
template P GetPointer(voffset_t field) const {
return const_cast(this)->GetPointer(field);
}
@@ -1035,6 +1065,21 @@ 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 *GetAddressOf(voffset_t field) {
+ auto field_offset = GetOptionalFieldOffset(field);
+ return field_offset ? data_ + field_offset : nullptr;
+ }
+ const uint8_t *GetAddressOf(voffset_t field) const {
+ return const_cast
(this)->GetAddressOf(field);
+ }
+
uint8_t *GetVTable() { return data_ - ReadScalar(data_); }
bool CheckField(voffset_t field) const {
diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h
index a9082fd8a..8a912de5c 100644
--- a/include/flatbuffers/idl.h
+++ b/include/flatbuffers/idl.h
@@ -50,8 +50,8 @@ namespace flatbuffers {
TD(FLOAT, "float", float, float, float32, float, float32) /* begin float */ \
TD(DOUBLE, "double", double, double, float64, double, float64) /* end float/scalar */
#define FLATBUFFERS_GEN_TYPES_POINTER(TD) \
- TD(STRING, "string", Offset, int, int, int, int) \
- TD(VECTOR, "", Offset, int, int, int, int) \
+ TD(STRING, "string", Offset, int, int, StringOffset, int) \
+ TD(VECTOR, "", Offset, int, int, VectorOffset, int) \
TD(STRUCT, "", Offset, int, int, int, int) \
TD(UNION, "", Offset, int, int, int, int)
diff --git a/include/flatbuffers/reflection.h b/include/flatbuffers/reflection.h
index 60e2426cc..cb18b1437 100644
--- a/include/flatbuffers/reflection.h
+++ b/include/flatbuffers/reflection.h
@@ -17,8 +17,6 @@
#ifndef FLATBUFFERS_REFLECTION_H_
#define FLATBUFFERS_REFLECTION_H_
-#include "flatbuffers/util.h"
-
// This is somewhat of a circular dependency because flatc (and thus this
// file) is needed to generate this header in the first place.
// Should normally not be a problem since it can be generated by the
@@ -30,12 +28,28 @@
namespace flatbuffers {
+// ------------------------- GETTERS -------------------------
+
+// Size of a basic type, don't use with structs.
inline size_t GetTypeSize(reflection::BaseType base_type) {
// This needs to correspond to the BaseType enum.
static size_t sizes[] = { 0, 1, 1, 1, 1, 2, 2, 4, 4, 8, 8, 4, 8, 4, 4, 4, 4 };
return sizes[base_type];
}
+// Same as above, but now correctly returns the size of a struct if
+// the field (or vector element) is a struct.
+inline size_t GetTypeSizeInline(reflection::BaseType base_type,
+ int type_index,
+ const reflection::Schema &schema) {
+ if (base_type == reflection::Obj &&
+ schema.objects()->Get(type_index)->is_struct()) {
+ return schema.objects()->Get(type_index)->bytesize();
+ } else {
+ return GetTypeSize(base_type);
+ }
+}
+
// Get the root, regardless of what type it is.
inline Table *GetAnyRoot(uint8_t *flatbuf) {
return GetMutableRoot(flatbuf);
@@ -68,108 +82,147 @@ inline const String *GetFieldS(const Table &table,
}
// Get a field, if you know it's a vector.
-template const Vector *GetFieldV(const Table &table,
- const reflection::Field &field) {
+template Vector *GetFieldV(const Table &table,
+ const reflection::Field &field) {
assert(field.type()->base_type() == reflection::Vector &&
sizeof(T) == GetTypeSize(field.type()->element()));
- return table.GetPointer *>(field.offset());
+ return table.GetPointer *>(field.offset());
+}
+
+// Get a field, if you know it's a vector, generically.
+// To actually access elements, use the return value together with
+// field.type()->element() in any of GetAnyVectorElemI below etc.
+inline VectorOfAny *GetFieldAnyV(const Table &table,
+ const reflection::Field &field) {
+ return table.GetPointer(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(field.offset());
+ return table.GetPointer(field.offset());
}
-// Get any field as a 64bit int, regardless of what it is (bool/int/float/str).
+// Raw helper functions used below: get any value in memory as a 64bit int, a
+// double or a string.
+// All scalars get static_cast to an int64_t, strings use strtoull, every other
+// data type returns 0.
+int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data);
+// All scalars static cast to double, strings use strtod, every other data
+// type is 0.0.
+double GetAnyValueF(reflection::BaseType type, const uint8_t *data);
+// All scalars converted using stringstream, strings as-is, and all other
+// data types provide some level of debug-pretty-printing.
+std::string GetAnyValueS(reflection::BaseType type, const uint8_t *data,
+ const reflection::Schema *schema,
+ int type_index);
+
+// Get any table field as a 64bit int, regardless of what type it is.
inline int64_t GetAnyFieldI(const Table &table,
const reflection::Field &field) {
-# define FLATBUFFERS_GET(C, T) \
- static_cast(GetField##C(table, field))
- switch (field.type()->base_type()) {
- case reflection::UType:
- case reflection::Bool:
- case reflection::UByte: return FLATBUFFERS_GET(I, uint8_t);
- case reflection::Byte: return FLATBUFFERS_GET(I, int8_t);
- case reflection::Short: return FLATBUFFERS_GET(I, int16_t);
- case reflection::UShort: return FLATBUFFERS_GET(I, uint16_t);
- case reflection::Int: return FLATBUFFERS_GET(I, int32_t);
- case reflection::UInt: return FLATBUFFERS_GET(I, uint32_t);
- case reflection::Long: return FLATBUFFERS_GET(I, int64_t);
- case reflection::ULong: return FLATBUFFERS_GET(I, uint64_t);
- case reflection::Float: return FLATBUFFERS_GET(F, float);
- case reflection::Double: return FLATBUFFERS_GET(F, double);
- case reflection::String: {
- auto s = GetFieldS(table, field);
- return s ? StringToInt(s->c_str()) : 0;
- }
- default: return 0;
- }
-# undef FLATBUFFERS_GET
+ auto field_ptr = table.GetAddressOf(field.offset());
+ return field_ptr ? GetAnyValueI(field.type()->base_type(), field_ptr)
+ : field.default_integer();
}
-// Get any field as a double, regardless of what it is (bool/int/float/str).
+// Get any table field as a double, regardless of what type it is.
inline double GetAnyFieldF(const Table &table,
const reflection::Field &field) {
- switch (field.type()->base_type()) {
- case reflection::Float: return GetFieldF(table, field);
- case reflection::Double: return GetFieldF(table, field);
- case reflection::String: {
- auto s = GetFieldS(table, field);
- return s ? strtod(s->c_str(), nullptr) : 0.0;
- }
- default: return static_cast(GetAnyFieldI(table, field));
- }
+ auto field_ptr = table.GetAddressOf(field.offset());
+ return field_ptr ? GetAnyValueF(field.type()->base_type(), field_ptr)
+ : field.default_real();
}
-// Get any field as a string, regardless of what it is (bool/int/float/str).
+
+// Get any table field as a string, regardless of what type it is.
+// You may pass nullptr for the schema if you don't care to have fields that
+// are of table type pretty-printed.
inline std::string GetAnyFieldS(const Table &table,
const reflection::Field &field,
- const reflection::Schema &schema) {
- switch (field.type()->base_type()) {
- case reflection::Float:
- case reflection::Double: return NumToString(GetAnyFieldF(table, field));
- case reflection::String: {
- auto s = GetFieldS(table, field);
- return s ? s->c_str() : "";
- }
- case reflection::Obj: {
- // Convert the table to a string. This is mostly for debugging purposes,
- // and does NOT promise to be JSON compliant.
- // Also prefixes the type.
- auto &objectdef = *schema.objects()->Get(field.type()->index());
- auto s = objectdef.name()->str();
- if (objectdef.is_struct()) {
- s += "(struct)"; // TODO: implement this as well.
- } else {
- auto table_field = GetFieldT(table, field);
- s += " { ";
- auto fielddefs = objectdef.fields();
- for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
- auto &fielddef = **it;
- if (!table.CheckField(fielddef.offset())) continue;
- auto val = GetAnyFieldS(*table_field, fielddef, schema);
- if (fielddef.type()->base_type() == reflection::String)
- val = "\"" + val + "\""; // Doesn't deal with escape codes etc.
- s += fielddef.name()->str();
- s += ": ";
- s += val;
- s += ", ";
- }
- s += "}";
- }
- return s;
- }
- case reflection::Vector:
- return "[(elements)]"; // TODO: implement this as well.
- case reflection::Union:
- return "(union)"; // TODO: implement this as well.
- default: return NumToString(GetAnyFieldI(table, field));
- }
+ const reflection::Schema *schema) {
+ auto field_ptr = table.GetAddressOf(field.offset());
+ return field_ptr ? GetAnyValueS(field.type()->base_type(), field_ptr, schema,
+ field.type()->index())
+ : "";
}
+// Get any struct field as a 64bit int, regardless of what type it is.
+inline int64_t GetAnyFieldI(const Struct &st,
+ const reflection::Field &field) {
+ return GetAnyValueI(field.type()->base_type(),
+ st.GetAddressOf(field.offset()));
+}
+
+// Get any struct field as a double, regardless of what type it is.
+inline double GetAnyFieldF(const Struct &st,
+ const reflection::Field &field) {
+ return GetAnyValueF(field.type()->base_type(),
+ st.GetAddressOf(field.offset()));
+}
+
+// Get any struct field as a string, regardless of what type it is.
+inline std::string GetAnyFieldS(const Struct &st,
+ const reflection::Field &field) {
+ return GetAnyValueS(field.type()->base_type(),
+ st.GetAddressOf(field.offset()), nullptr, -1);
+}
+
+// Get any vector element as a 64bit int, regardless of what type it is.
+inline int64_t GetAnyVectorElemI(const VectorOfAny *vec,
+ reflection::BaseType elem_type, size_t i) {
+ return GetAnyValueI(elem_type, vec->Data() + GetTypeSize(elem_type) * i);
+}
+
+// Get any vector element as a double, regardless of what type it is.
+inline double GetAnyVectorElemF(const VectorOfAny *vec,
+ reflection::BaseType elem_type, size_t i) {
+ return GetAnyValueF(elem_type, vec->Data() + GetTypeSize(elem_type) * i);
+}
+
+// Get any vector element as a string, regardless of what type it is.
+inline std::string GetAnyVectorElemS(const VectorOfAny *vec,
+ reflection::BaseType elem_type, size_t i) {
+ return GetAnyValueS(elem_type, vec->Data() + GetTypeSize(elem_type) * i,
+ nullptr, -1);
+}
+
+// Get a vector element that's a table/string/vector from a generic vector.
+// Pass Table/String/VectorOfAny as template parameter.
+// Warning: does no typechecking.
+template T *GetAnyVectorElemPointer(const VectorOfAny *vec,
+ size_t i) {
+ auto elem_ptr = vec->Data() + sizeof(uoffset_t) * i;
+ return (T *)(elem_ptr + ReadScalar(elem_ptr));
+}
+
+// Get the inline-address of a vector element. Useful for Structs (pass Struct
+// as template arg), or being able to address a range of scalars in-line.
+// Get elem_size from GetTypeSizeInline().
+// Note: little-endian data on all platforms, use EndianScalar() instead of
+// raw pointer access with scalars).
+template T *GetAnyVectorElemAddressOf(const VectorOfAny *vec,
+ size_t i,
+ size_t elem_size) {
+ // C-cast to allow const conversion.
+ return (T *)(vec->Data() + elem_size * i);
+}
+
+// Similarly, for elements of tables.
+template T *GetAnyFieldAddressOf(const Table &table,
+ const reflection::Field &field) {
+ return (T *)table.GetAddressOf(field.offset());
+}
+
+// Similarly, for elements of structs.
+template T *GetAnyFieldAddressOf(const Struct &st,
+ const reflection::Field &field) {
+ return (T *)st.GetAddressOf(field.offset());
+}
+
+// ------------------------- SETTERS -------------------------
+
// Set any scalar field, if you know its exact type.
template bool SetField(Table *table, const reflection::Field &field,
T val) {
@@ -177,77 +230,108 @@ template bool SetField(Table *table, const reflection::Field &field,
return table->SetField(field.offset(), val);
}
-// Set any field as a 64bit int, regardless of what it is (bool/int/float/str).
-inline void SetAnyFieldI(Table *table, const reflection::Field &field,
+// Raw helper functions used below: set any value in memory as a 64bit int, a
+// double or a string.
+// These work for all scalar values, but do nothing for other data types.
+// To set a string, see SetString below.
+void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val);
+void SetAnyValueF(reflection::BaseType type, uint8_t *data, double val);
+void SetAnyValueS(reflection::BaseType type, uint8_t *data, const char *val);
+
+// Set any table field as a 64bit int, regardless of type what it is.
+inline bool SetAnyFieldI(Table *table, const reflection::Field &field,
int64_t val) {
-# define FLATBUFFERS_SET(T) SetField(table, field, static_cast(val))
- switch (field.type()->base_type()) {
- case reflection::UType:
- case reflection::Bool:
- case reflection::UByte: FLATBUFFERS_SET(uint8_t ); break;
- case reflection::Byte: FLATBUFFERS_SET(int8_t ); break;
- case reflection::Short: FLATBUFFERS_SET(int16_t ); break;
- case reflection::UShort: FLATBUFFERS_SET(uint16_t ); break;
- case reflection::Int: FLATBUFFERS_SET(int32_t ); break;
- case reflection::UInt: FLATBUFFERS_SET(uint32_t ); break;
- case reflection::Long: FLATBUFFERS_SET(int64_t ); break;
- case reflection::ULong: FLATBUFFERS_SET(uint64_t ); break;
- case reflection::Float: FLATBUFFERS_SET(float ); break;
- case reflection::Double: FLATBUFFERS_SET(double ); break;
- // TODO: support strings
- default: break;
- }
-# undef FLATBUFFERS_SET
+ auto field_ptr = table->GetAddressOf(field.offset());
+ if (!field_ptr) return false;
+ SetAnyValueI(field.type()->base_type(), field_ptr, val);
+ return true;
}
-// Set any field as a double, regardless of what it is (bool/int/float/str).
-inline void SetAnyFieldF(Table *table, const reflection::Field &field,
+// Set any table field as a double, regardless of what type it is.
+inline bool SetAnyFieldF(Table *table, const reflection::Field &field,
double val) {
- switch (field.type()->base_type()) {
- case reflection::Float: SetField (table, field,
- static_cast(val)); break;
- case reflection::Double: SetField(table, field, val); break;
- // TODO: support strings.
- default: SetAnyFieldI(table, field, static_cast(val)); break;
- }
+ auto field_ptr = table->GetAddressOf(field.offset());
+ if (!field_ptr) return false;
+ SetAnyValueF(field.type()->base_type(), field_ptr, val);
+ return true;
}
-// Set any field as a string, regardless of what it is (bool/int/float/str).
-inline void SetAnyFieldS(Table *table, const reflection::Field &field,
- const char *val) {
- switch (field.type()->base_type()) {
- case reflection::Float:
- case reflection::Double: SetAnyFieldF(table, field, strtod(val, nullptr));
- // TODO: support strings.
- default: SetAnyFieldI(table, field, StringToInt(val)); break;
- }
+// Set any table field as a string, regardless of what type it is.
+inline bool SetAnyFieldS(Table *table, const reflection::Field &field,
+ const char *val) {
+ auto field_ptr = table->GetAddressOf(field.offset());
+ if (!field_ptr) return false;
+ SetAnyValueS(field.type()->base_type(), field_ptr, val);
+ return true;
}
+// Set any struct field as a 64bit int, regardless of type what it is.
+inline void SetAnyFieldI(Struct *st, const reflection::Field &field,
+ int64_t val) {
+ SetAnyValueI(field.type()->base_type(), st->GetAddressOf(field.offset()),
+ val);
+}
+
+// Set any struct field as a double, regardless of type what it is.
+inline void SetAnyFieldF(Struct *st, const reflection::Field &field,
+ double val) {
+ SetAnyValueF(field.type()->base_type(), st->GetAddressOf(field.offset()),
+ val);
+}
+
+// Set any struct field as a string, regardless of type what it is.
+inline void SetAnyFieldS(Struct *st, const reflection::Field &field,
+ const char *val) {
+ SetAnyValueS(field.type()->base_type(), st->GetAddressOf(field.offset()),
+ val);
+}
+
+// Set any vector element as a 64bit int, regardless of type what it is.
+inline void SetAnyVectorElemI(VectorOfAny *vec, reflection::BaseType elem_type,
+ size_t i, int64_t val) {
+ SetAnyValueI(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val);
+}
+
+// Set any vector element as a double, regardless of type what it is.
+inline void SetAnyVectorElemF(VectorOfAny *vec, reflection::BaseType elem_type,
+ size_t i, double val) {
+ SetAnyValueF(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val);
+}
+
+// Set any vector element as a string, regardless of type what it is.
+inline void SetAnyVectorElemS(VectorOfAny *vec, reflection::BaseType elem_type,
+ size_t i, const char *val) {
+ SetAnyValueS(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val);
+}
+
+
+// ------------------------- RESIZING SETTERS -------------------------
+
// "smart" pointer for use with resizing vectors: turns a pointer inside
// a vector into a relative offset, such that it is not affected by resizes.
template class pointer_inside_vector {
public:
- pointer_inside_vector(const T *ptr, const std::vector &vec)
- : offset_(reinterpret_cast(ptr) -
- reinterpret_cast(vec.data())),
+ pointer_inside_vector(T *ptr, std::vector &vec)
+ : offset_(reinterpret_cast(ptr) -
+ reinterpret_cast(vec.data())),
vec_(vec) {}
- const T *operator*() const {
- return reinterpret_cast(
- reinterpret_cast(vec_.data()) + offset_);
+ T *operator*() const {
+ return reinterpret_cast(
+ reinterpret_cast(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 &vec_;
+ std::vector &vec_;
};
// Helper to create the above easily without specifying template args.
-template pointer_inside_vector piv(
- const T *ptr, const std::vector &vec) {
+template pointer_inside_vector piv(T *ptr,
+ std::vector &vec) {
return pointer_inside_vector(ptr, vec);
}
@@ -265,194 +349,66 @@ inline const reflection::Object &GetUnionType(
return *enumval->object();
}
-// Resize a FlatBuffer in-place by iterating through all offsets in the buffer
-// and adjusting them by "delta" if they straddle the start offset.
-// Once that is done, bytes can now be inserted/deleted safely.
-// "delta" may be negative (shrinking).
-// Unless "delta" is a multiple of the largest alignment, you'll create a small
-// amount of garbage space in the buffer (usually 0..7 bytes).
-// If your FlatBuffer's root table is not the schema's root table, you should
-// pass in your root_table type as well.
-class ResizeContext {
- public:
- ResizeContext(const reflection::Schema &schema, uoffset_t start, int delta,
- std::vector *flatbuf,
- const reflection::Object *root_table = nullptr)
- : schema_(schema), startptr_(flatbuf->data() + start),
- delta_(delta), buf_(*flatbuf),
- dag_check_(flatbuf->size() / sizeof(uoffset_t), false) {
- auto mask = static_cast(sizeof(largest_scalar_t) - 1);
- delta_ = (delta_ + mask) & ~mask;
- if (!delta_) return; // We can't shrink by less than largest_scalar_t.
- // Now change all the offsets by delta_.
- auto root = GetAnyRoot(buf_.data());
- Straddle(buf_.data(), root, buf_.data());
- ResizeTable(root_table ? *root_table : *schema.root_table(), root);
- // We can now add or remove bytes at start.
- if (delta_ > 0) buf_.insert(buf_.begin() + start, delta_, 0);
- else buf_.erase(buf_.begin() + start, buf_.begin() + start - delta_);
- }
-
- // Check if the range between first (lower address) and second straddles
- // the insertion point. If it does, change the offset at offsetloc (of
- // type T, with direction D).
- template void Straddle(void *first, void *second,
- void *offsetloc) {
- if (first <= startptr_ && second >= startptr_) {
- WriteScalar(offsetloc, ReadScalar(offsetloc) + delta_ * D);
- DagCheck(offsetloc) = true;
- }
- }
-
- // This returns a boolean that records if the corresponding offset location
- // has been modified already. If so, we can't even read the corresponding
- // offset, since it is pointing to a location that is illegal until the
- // resize actually happens.
- // This must be checked for every offset, since we can't know which offsets
- // will straddle and which won't.
- uint8_t &DagCheck(void *offsetloc) {
- auto dag_idx = reinterpret_cast(offsetloc) -
- reinterpret_cast(buf_.data());
- return dag_check_[dag_idx];
- }
-
- void ResizeTable(const reflection::Object &objectdef, Table *table) {
- if (DagCheck(table))
- return; // Table already visited.
- auto vtable = table->GetVTable();
- // Check if the vtable offset points beyond the insertion point.
- Straddle(table, vtable, table);
- // This direction shouldn't happen because vtables that sit before tables
- // are always directly adjacent, but check just in case we ever change the
- // way flatbuffers are built.
- Straddle(vtable, table, table);
- // Early out: since all fields inside the table must point forwards in
- // memory, if the insertion point is before the table we can stop here.
- auto tableloc = reinterpret_cast(table);
- if (startptr_ <= tableloc) return;
- // Check each field.
- auto fielddefs = objectdef.fields();
- for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
- auto &fielddef = **it;
- auto base_type = fielddef.type()->base_type();
- // Ignore scalars.
- if (base_type <= reflection::Double) continue;
- // Ignore fields that are not stored.
- auto offset = table->GetOptionalFieldOffset(fielddef.offset());
- if (!offset) continue;
- // Ignore structs.
- auto subobjectdef = base_type == reflection::Obj ?
- schema_.objects()->Get(fielddef.type()->index()) : nullptr;
- if (subobjectdef && subobjectdef->is_struct()) continue;
- // Get this fields' offset, and read it if safe.
- auto offsetloc = tableloc + offset;
- if (DagCheck(offsetloc))
- continue; // This offset already visited.
- auto ref = offsetloc + ReadScalar(offsetloc);
- Straddle(offsetloc, ref, offsetloc);
- // Recurse.
- switch (base_type) {
- case reflection::Obj: {
- ResizeTable(*subobjectdef, reinterpret_cast(ref));
- break;
- }
- case reflection::Vector: {
- auto elem_type = fielddef.type()->element();
- if (elem_type != reflection::Obj && elem_type != reflection::String)
- break;
- auto vec = reinterpret_cast *>(ref);
- auto elemobjectdef = elem_type == reflection::Obj
- ? schema_.objects()->Get(fielddef.type()->index())
- : nullptr;
- if (elemobjectdef && elemobjectdef->is_struct()) break;
- for (uoffset_t i = 0; i < vec->size(); i++) {
- auto loc = vec->Data() + i * sizeof(uoffset_t);
- if (DagCheck(loc))
- continue; // This offset already visited.
- auto dest = loc + vec->Get(i);
- Straddle(loc, dest ,loc);
- if (elemobjectdef)
- ResizeTable(*elemobjectdef, reinterpret_cast(dest));
- }
- break;
- }
- case reflection::Union: {
- ResizeTable(GetUnionType(schema_, objectdef, fielddef, *table),
- reinterpret_cast(ref));
- break;
- }
- case reflection::String:
- break;
- default:
- assert(false);
- }
- }
- }
-
- void operator=(const ResizeContext &rc);
-
- private:
- const reflection::Schema &schema_;
- uint8_t *startptr_;
- int delta_;
- std::vector &buf_;
- std::vector dag_check_;
-};
-
// Changes the contents of a string inside a FlatBuffer. FlatBuffer must
// live inside a std::vector so we can resize the buffer if needed.
// "str" must live inside "flatbuf" and may be invalidated after this call.
// If your FlatBuffer's root table is not the schema's root table, you should
// pass in your root_table type as well.
-inline void SetString(const reflection::Schema &schema, const std::string &val,
- const String *str, std::vector *flatbuf,
- const reflection::Object *root_table = nullptr) {
- auto delta = static_cast(val.size()) - static_cast(str->Length());
- auto start = static_cast(reinterpret_cast(str) -
- flatbuf->data() +
- sizeof(uoffset_t));
- if (delta) {
- // 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);
-}
+void SetString(const reflection::Schema &schema, const std::string &val,
+ const String *str, std::vector *flatbuf,
+ const reflection::Object *root_table = nullptr);
// Resizes a flatbuffers::Vector inside a FlatBuffer. FlatBuffer must
// live inside a std::vector so we can resize the buffer if needed.
// "vec" must live inside "flatbuf" and may be invalidated after this call.
// If your FlatBuffer's root table is not the schema's root table, you should
// pass in your root_table type as well.
+uint8_t *ResizeAnyVector(const reflection::Schema &schema, uoffset_t newsize,
+ const VectorOfAny *vec, uoffset_t num_elems,
+ uoffset_t elem_size, std::vector *flatbuf,
+ const reflection::Object *root_table = nullptr);
+
template
void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val,
const Vector *vec, std::vector *flatbuf,
const reflection::Object *root_table = nullptr) {
auto delta_elem = static_cast(newsize) - static_cast(vec->size());
- auto delta_bytes = delta_elem * static_cast(sizeof(T));
- auto vec_start = reinterpret_cast(vec) - flatbuf->data();
- auto start = static_cast(vec_start + sizeof(uoffset_t) +
- sizeof(T) * vec->size());
- if (delta_bytes) {
- ResizeContext(schema, start, delta_bytes, flatbuf, root_table);
- WriteScalar(flatbuf->data() + vec_start, newsize); // Length field.
- // Set new elements to "val".
- for (int i = 0; i < delta_elem; i++) {
- auto loc = flatbuf->data() + start + i * sizeof(T);
- auto is_scalar = std::is_scalar::value;
- if (is_scalar) {
- WriteScalar(loc, val);
- } else { // struct
- *reinterpret_cast(loc) = val;
- }
+ auto newelems = ResizeAnyVector(schema, newsize,
+ reinterpret_cast(vec),
+ vec->size(),
+ static_cast(sizeof(T)), flatbuf,
+ root_table);
+ // Set new elements to "val".
+ for (int i = 0; i < delta_elem; i++) {
+ auto loc = newelems + i * sizeof(T);
+ auto is_scalar = std::is_scalar::value;
+ if (is_scalar) {
+ WriteScalar(loc, val);
+ } else { // struct
+ *reinterpret_cast(loc) = 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.
+const uint8_t *AddFlatBuffer(std::vector &flatbuf,
+ const uint8_t *newbuf, size_t newlen);
+
+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);
+}
+
+// ------------------------- COPYING -------------------------
+
// 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
@@ -461,134 +417,10 @@ void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val,
// Note: this does not deal with DAGs correctly. If the table passed forms a
// DAG, the copy will be a tree instead (with duplicates).
-inline void CopyInline(FlatBufferBuilder &fbb,
- const reflection::Field &fielddef,
- const Table &table,
- size_t align, size_t size) {
- fbb.Align(align);
- fbb.PushBytes(table.GetStruct(fielddef.offset()), size);
- fbb.TrackField(fielddef.offset(), fbb.GetSize());
-}
-
-inline Offset CopyTable(FlatBufferBuilder &fbb,
- const reflection::Schema &schema,
- const reflection::Object &objectdef,
- const Table &table) {
- // Before we can construct the table, we have to first generate any
- // subobjects, and collect their offsets.
- std::vector offsets;
- auto fielddefs = objectdef.fields();
- for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
- auto &fielddef = **it;
- // Skip if field is not present in the source.
- if (!table.CheckField(fielddef.offset())) continue;
- uoffset_t offset = 0;
- switch (fielddef.type()->base_type()) {
- case reflection::String: {
- offset = fbb.CreateString(GetFieldS(table, fielddef)).o;
- break;
- }
- case reflection::Obj: {
- auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index());
- if (!subobjectdef.is_struct()) {
- offset = CopyTable(fbb, schema, subobjectdef,
- *GetFieldT(table, fielddef)).o;
- }
- break;
- }
- case reflection::Union: {
- auto &subobjectdef = GetUnionType(schema, objectdef, fielddef, table);
- offset = CopyTable(fbb, schema, subobjectdef,
- *GetFieldT(table, fielddef)).o;
- break;
- }
- case reflection::Vector: {
- auto vec = table.GetPointer> *>(
- fielddef.offset());
- auto element_base_type = fielddef.type()->element();
- auto elemobjectdef = element_base_type == reflection::Obj
- ? schema.objects()->Get(fielddef.type()->index())
- : nullptr;
- switch (element_base_type) {
- case reflection::String: {
- std::vector> elements(vec->size());
- auto vec_s = reinterpret_cast> *>(vec);
- for (uoffset_t i = 0; i < vec_s->size(); i++) {
- elements[i] = fbb.CreateString(vec_s->Get(i)).o;
- }
- offset = fbb.CreateVector(elements).o;
- break;
- }
- case reflection::Obj: {
- if (!elemobjectdef->is_struct()) {
- std::vector> elements(vec->size());
- for (uoffset_t i = 0; i < vec->size(); i++) {
- elements[i] =
- CopyTable(fbb, schema, *elemobjectdef, *vec->Get(i));
- }
- offset = fbb.CreateVector(elements).o;
- break;
- }
- // FALL-THRU:
- }
- default: { // Scalars and structs.
- auto element_size = GetTypeSize(element_base_type);
- if (elemobjectdef && elemobjectdef->is_struct())
- element_size = elemobjectdef->bytesize();
- fbb.StartVector(element_size, vec->size());
- fbb.PushBytes(vec->Data(), element_size * vec->size());
- offset = fbb.EndVector(vec->size());
- break;
- }
- }
- break;
- }
- default: // Scalars.
- break;
- }
- if (offset) {
- offsets.push_back(offset);
- }
- }
- // Now we can build the actual table from either offsets or scalar data.
- auto start = objectdef.is_struct()
- ? fbb.StartStruct(objectdef.minalign())
- : fbb.StartTable();
- size_t offset_idx = 0;
- for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
- auto &fielddef = **it;
- if (!table.CheckField(fielddef.offset())) continue;
- auto base_type = fielddef.type()->base_type();
- switch (base_type) {
- case reflection::Obj: {
- auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index());
- if (subobjectdef.is_struct()) {
- CopyInline(fbb, fielddef, table, subobjectdef.minalign(),
- subobjectdef.bytesize());
- break;
- }
- // else: FALL-THRU:
- }
- case reflection::Union:
- case reflection::String:
- case reflection::Vector:
- fbb.AddOffset(fielddef.offset(), Offset(offsets[offset_idx++]));
- break;
- default: { // Scalars.
- auto size = GetTypeSize(base_type);
- CopyInline(fbb, fielddef, table, size, size);
- break;
- }
- }
- }
- assert(offset_idx == offsets.size());
- if (objectdef.is_struct()) {
- fbb.ClearOffsets();
- return fbb.EndStruct();
- } else {
- return fbb.EndTable(start, static_cast(fielddefs->size()));
- }
-}
+Offset CopyTable(FlatBufferBuilder &fbb,
+ const reflection::Schema &schema,
+ const reflection::Object &objectdef,
+ const Table &table);
} // namespace flatbuffers
diff --git a/net/FlatBuffers/ByteBuffer.cs b/net/FlatBuffers/ByteBuffer.cs
index fcdf8a1da..50c6fa960 100755
--- a/net/FlatBuffers/ByteBuffer.cs
+++ b/net/FlatBuffers/ByteBuffer.cs
@@ -48,6 +48,11 @@ namespace FlatBuffers
set { _pos = value; }
}
+ public void Reset()
+ {
+ _pos = 0;
+ }
+
// Pre-allocated helper arrays for convertion.
private float[] floathelper = new[] { 0.0f };
private int[] inthelper = new[] { 0 };
diff --git a/net/FlatBuffers/FlatBufferBuilder.cs b/net/FlatBuffers/FlatBufferBuilder.cs
index 8008f5ce7..39d386f40 100644
--- a/net/FlatBuffers/FlatBufferBuilder.cs
+++ b/net/FlatBuffers/FlatBufferBuilder.cs
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -49,6 +49,17 @@ namespace FlatBuffers
_bb = new ByteBuffer(new byte[initialSize]);
}
+ public void Clear()
+ {
+ _space = _bb.Length;
+ _bb.Reset();
+ _minAlign = 1;
+ _vtable = null;
+ _objectStart = 0;
+ _vtables = new int[16];
+ _numVtables = 0;
+ _vectorNumElems = 0;
+ }
public int Offset { get { return _bb.Length - _space; } }
@@ -195,10 +206,10 @@ namespace FlatBuffers
Prep(alignment, elemSize * count); // Just in case alignment > int.
}
- public int EndVector()
+ public VectorOffset EndVector()
{
PutInt(_vectorNumElems);
- return Offset;
+ return new VectorOffset(Offset);
}
public void Nested(int obj)
@@ -249,7 +260,7 @@ namespace FlatBuffers
public void AddDouble(int o, double x, double d) { if (x != d) { AddDouble(x); Slot(o); } }
public void AddOffset(int o, int x, int d) { if (x != d) { AddOffset(x); Slot(o); } }
- public int CreateString(string s)
+ public StringOffset CreateString(string s)
{
NotNested();
byte[] utf8 = Encoding.UTF8.GetBytes(s);
@@ -257,7 +268,7 @@ namespace FlatBuffers
StartVector(1, utf8.Length, 1);
Buffer.BlockCopy(utf8, 0, _bb.Data, _space -= utf8.Length,
utf8.Length);
- return EndVector();
+ return new StringOffset(EndVector().Value);
}
// Structs are stored inline, so nothing additional is being added.
diff --git a/net/FlatBuffers/FlatBuffers.csproj b/net/FlatBuffers/FlatBuffers.csproj
index 9307c33f9..3ae938a15 100644
--- a/net/FlatBuffers/FlatBuffers.csproj
+++ b/net/FlatBuffers/FlatBuffers.csproj
@@ -37,6 +37,7 @@
+
diff --git a/net/FlatBuffers/Offset.cs b/net/FlatBuffers/Offset.cs
new file mode 100644
index 000000000..a1524df0b
--- /dev/null
+++ b/net/FlatBuffers/Offset.cs
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace FlatBuffers
+{
+ ///
+ /// Offset class for typesafe assignments.
+ ///
+ public struct Offset where T : class
+ {
+ public int Value;
+ public Offset(int value)
+ {
+ Value = value;
+ }
+ }
+
+ public struct StringOffset
+ {
+ public int Value;
+ public StringOffset(int value)
+ {
+ Value = value;
+ }
+ }
+
+ public struct VectorOffset
+ {
+ public int Value;
+ public VectorOffset(int value)
+ {
+ Value = value;
+ }
+ }
+}
diff --git a/src/flatc.cpp b/src/flatc.cpp
index 91ecb25b5..9b8b50d70 100755
--- a/src/flatc.cpp
+++ b/src/flatc.cpp
@@ -90,7 +90,9 @@ static void Error(const std::string &err, bool usage, bool show_exe_name) {
" --defaults-json Output fields whose value is the default when\n"
" writing JSON\n"
" --no-prefix Don\'t prefix enum values with the enum type in C++.\n"
- " --gen-includes (deprecated), instead use:\n"
+ " --gen-includes (deprecated), this is the default behavior.\n"
+ " If the original behavior is required (no include\n"
+ " statements) use --no-includes.\n"
" --no-includes Don\'t generate include statements for included\n"
" schemas the generated file depends on (C++).\n"
" --gen-mutable Generate accessors that can mutate buffers in-place.\n"
diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp
index faa97059a..fb55a80cf 100644
--- a/src/idl_gen_general.cpp
+++ b/src/idl_gen_general.cpp
@@ -19,6 +19,7 @@
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/idl.h"
#include "flatbuffers/util.h"
+#include
namespace flatbuffers {
@@ -202,6 +203,10 @@ static std::string GenTypeBasic(const LanguageParameters &lang,
#undef FLATBUFFERS_TD
};
+ if(lang.language == GeneratorOptions::kCSharp && type.base_type == BASE_TYPE_STRUCT) {
+ return "Offset<" + type.struct_def->name + ">";
+ }
+
return gtypename[type.base_type * GeneratorOptions::kMAX + lang.language];
}
@@ -258,6 +263,32 @@ static Type DestinationType(const LanguageParameters &lang, const Type &type,
}
}
+static std::string GenOffsetType(const LanguageParameters &lang, const StructDef &struct_def) {
+ if(lang.language == GeneratorOptions::kCSharp) {
+ return "Offset<" + struct_def.name + ">";
+ } else {
+ return "int";
+ }
+}
+
+static std::string GenOffsetConstruct(const LanguageParameters &lang,
+ const StructDef &struct_def,
+ const std::string &variable_name)
+{
+ if(lang.language == GeneratorOptions::kCSharp) {
+ return "new Offset<" + struct_def.name + ">(" + variable_name + ")";
+ }
+ return variable_name;
+}
+
+static std::string GenVectorOffsetType(const LanguageParameters &lang) {
+ if(lang.language == GeneratorOptions::kCSharp) {
+ return "VectorOffset";
+ } else {
+ return "int";
+ }
+}
+
// Generate destination type name
static std::string GenTypeNameDest(const LanguageParameters &lang, const Type &type)
{
@@ -353,7 +384,20 @@ static std::string SourceCast(const LanguageParameters &lang,
return "";
}
-static std::string GenDefaultValue(const Value &value) {
+static std::string GenDefaultValue(const LanguageParameters &lang, const Value &value, bool for_buffer) {
+ if(lang.language == GeneratorOptions::kCSharp && !for_buffer) {
+ switch(value.type.base_type) {
+ case BASE_TYPE_STRING:
+ return "default(StringOffset)";
+ case BASE_TYPE_STRUCT:
+ return "default(Offset<" + value.type.struct_def->name + ">)";
+ case BASE_TYPE_VECTOR:
+ return "default(VectorOffset)";
+ default:
+ break;
+ }
+ }
+
return value.type.base_type == BASE_TYPE_BOOL
? (value.constant == "0" ? "false" : "true")
: value.constant;
@@ -658,7 +702,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
} else {
code += offset_prefix + getter;
code += "(o + bb_pos)" + dest_mask + " : " + default_cast;
- code += GenDefaultValue(field.value);
+ code += GenDefaultValue(lang, field.value, false);
}
} else {
switch (field.value.type.base_type) {
@@ -765,13 +809,14 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
code += "\n";
if (struct_def.fixed) {
// create a struct constructor function
- code += " public static int " + FunctionStart(lang, 'C') + "reate";
+ code += " public static " + GenOffsetType(lang, struct_def) + " ";
+ code += FunctionStart(lang, 'C') + "reate";
code += struct_def.name + "(FlatBufferBuilder builder";
GenStructArgs(lang, struct_def, code_ptr, "");
code += ") {\n";
GenStructBody(lang, struct_def, code_ptr, "");
- code += " return builder.";
- code += lang.get_fbb_offset;
+ code += " return ";
+ code += GenOffsetConstruct(lang, struct_def, "builder." + std::string(lang.get_fbb_offset));
code += ";\n }\n";
} else {
// Generate a method that creates a table in one go. This is only possible
@@ -791,9 +836,9 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
}
if (has_no_struct_fields && num_fields) {
// Generate a table constructor of the form:
- // public static void createName(FlatBufferBuilder builder, args...)
- code += " public static int " + FunctionStart(lang, 'C') + "reate";
- code += struct_def.name;
+ // public static int createName(FlatBufferBuilder builder, args...)
+ code += " public static " + GenOffsetType(lang, struct_def) + " ";
+ code += FunctionStart(lang, 'C') + "reate" + struct_def.name;
code += "(FlatBufferBuilder builder";
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
@@ -807,7 +852,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
// Java doesn't have defaults, which means this method must always
// supply all arguments, and thus won't compile when fields are added.
if (lang.language != GeneratorOptions::kJava) {
- code += " = " + GenDefaultValue(field.value);
+ code += " = " + GenDefaultValue(lang, field.value, false);
}
}
code += ") {\n builder.";
@@ -857,7 +902,10 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
code += GenMethod(lang, field.value.type) + "(";
code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
code += DestinationValue(lang, argname, field.value.type);
- code += ", " + GenDefaultValue(field.value);
+ if(!IsScalar(field.value.type.base_type) && field.value.type.base_type != BASE_TYPE_UNION && lang.language == GeneratorOptions::kCSharp) {
+ code += ".Value";
+ }
+ code += ", " + GenDefaultValue(lang, field.value, true);
code += "); }\n";
if (field.value.type.base_type == BASE_TYPE_VECTOR) {
auto vector_type = field.value.type.VectorType();
@@ -865,7 +913,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
auto elem_size = InlineSize(vector_type);
if (!IsStruct(vector_type)) {
// Generate a method to create a vector from a Java array.
- code += " public static int " + FunctionStart(lang, 'C') + "reate";
+ code += " public static " + GenVectorOffsetType(lang) + " " + FunctionStart(lang, 'C') + "reate";
code += MakeCamel(field.name);
code += "Vector(FlatBufferBuilder builder, ";
code += GenTypeBasic(lang, vector_type) + "[] data) ";
@@ -877,8 +925,12 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
code += FunctionStart(lang, 'L') + "ength - 1; i >= 0; i--) builder.";
code += FunctionStart(lang, 'A') + "dd";
code += GenMethod(lang, vector_type);
- code += "(data[i]); return builder.";
- code += FunctionStart(lang, 'E') + "ndVector(); }\n";
+ code += "(data[i]";
+ if(lang.language == GeneratorOptions::kCSharp &&
+ (vector_type.base_type == BASE_TYPE_STRUCT || vector_type.base_type == BASE_TYPE_STRING))
+ code += ".Value";
+ code += "); return ";
+ code += "builder." + FunctionStart(lang, 'E') + "ndVector(); }\n";
}
// Generate a method to start a vector, data to be added manually after.
code += " public static void " + FunctionStart(lang, 'S') + "tart";
@@ -890,7 +942,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
code += "); }\n";
}
}
- code += " public static int ";
+ code += " public static " + GenOffsetType(lang, struct_def) + " ";
code += FunctionStart(lang, 'E') + "nd" + struct_def.name;
code += "(FlatBufferBuilder builder) {\n int o = builder.";
code += FunctionStart(lang, 'E') + "ndObject();\n";
@@ -904,12 +956,16 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser,
code += "); // " + field.name + "\n";
}
}
- code += " return o;\n }\n";
+ code += " return " + GenOffsetConstruct(lang, struct_def, "o") + ";\n }\n";
if (parser.root_struct_def_ == &struct_def) {
code += " public static void ";
code += FunctionStart(lang, 'F') + "inish" + struct_def.name;
- code += "Buffer(FlatBufferBuilder builder, int offset) { ";
- code += "builder." + FunctionStart(lang, 'F') + "inish(offset";
+ code += "Buffer(FlatBufferBuilder builder, " + GenOffsetType(lang, struct_def) + " offset) {";
+ code += " builder." + FunctionStart(lang, 'F') + "inish(offset";
+ if (lang.language == GeneratorOptions::kCSharp) {
+ code += ".Value";
+ }
+
if (parser.file_identifier_.length())
code += ", \"" + parser.file_identifier_ + "\"";
code += "); }\n";
diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp
index bf4b931db..ad5ff2ac6 100644
--- a/src/idl_parser.cpp
+++ b/src/idl_parser.cpp
@@ -918,10 +918,10 @@ void Parser::ParseDecl() {
auto align = static_cast(atoi(force_align->constant.c_str()));
if (force_align->type.base_type != BASE_TYPE_INT ||
align < struct_def.minalign ||
- align > 256 ||
+ align > 16 ||
align & (align - 1))
Error("force_align must be a power of two integer ranging from the"
- "struct\'s natural alignment to 256");
+ "struct\'s natural alignment to 16");
struct_def.minalign = align;
}
struct_def.PadLastField(struct_def.minalign);
diff --git a/src/reflection.cpp b/src/reflection.cpp
new file mode 100644
index 000000000..35ae16feb
--- /dev/null
+++ b/src/reflection.cpp
@@ -0,0 +1,471 @@
+/*
+ * Copyright 2015 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "flatbuffers/reflection.h"
+#include "flatbuffers/util.h"
+
+// Helper functionality for reflection.
+
+namespace flatbuffers {
+
+int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data) {
+# define FLATBUFFERS_GET(T) static_cast(ReadScalar(data))
+ switch (type) {
+ case reflection::UType:
+ case reflection::Bool:
+ case reflection::UByte: return FLATBUFFERS_GET(uint8_t);
+ case reflection::Byte: return FLATBUFFERS_GET(int8_t);
+ case reflection::Short: return FLATBUFFERS_GET(int16_t);
+ case reflection::UShort: return FLATBUFFERS_GET(uint16_t);
+ case reflection::Int: return FLATBUFFERS_GET(int32_t);
+ case reflection::UInt: return FLATBUFFERS_GET(uint32_t);
+ case reflection::Long: return FLATBUFFERS_GET(int64_t);
+ case reflection::ULong: return FLATBUFFERS_GET(uint64_t);
+ case reflection::Float: return FLATBUFFERS_GET(float);
+ case reflection::Double: return FLATBUFFERS_GET(double);
+ case reflection::String: {
+ auto s = reinterpret_cast(ReadScalar(data) +
+ data);
+ return s ? StringToInt(s->c_str()) : 0;
+ }
+ default: return 0; // Tables & vectors do not make sense.
+ }
+# undef FLATBUFFERS_GET
+}
+
+double GetAnyValueF(reflection::BaseType type, const uint8_t *data) {
+ switch (type) {
+ case reflection::Float: return static_cast(ReadScalar(data));
+ case reflection::Double: return ReadScalar(data);
+ case reflection::String: {
+ auto s = reinterpret_cast(ReadScalar(data) +
+ data);
+ return s ? strtod(s->c_str(), nullptr) : 0.0;
+ }
+ default: return static_cast(GetAnyValueI(type, data));
+ }
+}
+
+std::string GetAnyValueS(reflection::BaseType type, const uint8_t *data,
+ const reflection::Schema *schema, int type_index) {
+ switch (type) {
+ case reflection::Float:
+ case reflection::Double: return NumToString(GetAnyValueF(type, data));
+ case reflection::String: {
+ auto s = reinterpret_cast(ReadScalar(data) +
+ data);
+ return s ? s->c_str() : "";
+ }
+ case reflection::Obj:
+ if (schema) {
+ // Convert the table to a string. This is mostly for debugging purposes,
+ // and does NOT promise to be JSON compliant.
+ // Also prefixes the type.
+ auto &objectdef = *schema->objects()->Get(type_index);
+ auto s = objectdef.name()->str();
+ if (objectdef.is_struct()) {
+ s += "(struct)"; // TODO: implement this as well.
+ } else {
+ auto table_field = reinterpret_cast(
+ ReadScalar(data) + data);
+ s += " { ";
+ auto fielddefs = objectdef.fields();
+ for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
+ auto &fielddef = **it;
+ if (!table_field->CheckField(fielddef.offset())) continue;
+ auto val = GetAnyFieldS(*table_field, fielddef, schema);
+ if (fielddef.type()->base_type() == reflection::String)
+ val = "\"" + val + "\""; // Doesn't deal with escape codes etc.
+ s += fielddef.name()->str();
+ s += ": ";
+ s += val;
+ s += ", ";
+ }
+ s += "}";
+ }
+ return s;
+ } else {
+ return "(table)";
+ }
+ case reflection::Vector:
+ return "[(elements)]"; // TODO: implement this as well.
+ case reflection::Union:
+ return "(union)"; // TODO: implement this as well.
+ default: return NumToString(GetAnyValueI(type, data));
+ }
+}
+
+void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val) {
+# define FLATBUFFERS_SET(T) WriteScalar(data, static_cast(val))
+ switch (type) {
+ case reflection::UType:
+ case reflection::Bool:
+ case reflection::UByte: FLATBUFFERS_SET(uint8_t ); break;
+ case reflection::Byte: FLATBUFFERS_SET(int8_t ); break;
+ case reflection::Short: FLATBUFFERS_SET(int16_t ); break;
+ case reflection::UShort: FLATBUFFERS_SET(uint16_t); break;
+ case reflection::Int: FLATBUFFERS_SET(int32_t ); break;
+ case reflection::UInt: FLATBUFFERS_SET(uint32_t); break;
+ case reflection::Long: FLATBUFFERS_SET(int64_t ); break;
+ case reflection::ULong: FLATBUFFERS_SET(uint64_t); break;
+ case reflection::Float: FLATBUFFERS_SET(float ); break;
+ case reflection::Double: FLATBUFFERS_SET(double ); break;
+ // TODO: support strings
+ default: break;
+ }
+# undef FLATBUFFERS_SET
+}
+
+void SetAnyValueF(reflection::BaseType type, uint8_t *data, double val) {
+ switch (type) {
+ case reflection::Float: WriteScalar(data, static_cast(val)); break;
+ case reflection::Double: WriteScalar(data, val); break;
+ // TODO: support strings.
+ default: SetAnyValueI(type, data, static_cast(val)); break;
+ }
+}
+
+void SetAnyValueS(reflection::BaseType type, uint8_t *data, const char *val) {
+ switch (type) {
+ case reflection::Float:
+ case reflection::Double:
+ SetAnyValueF(type, data, strtod(val, nullptr));
+ break;
+ // TODO: support strings.
+ default: SetAnyValueI(type, data, StringToInt(val)); break;
+ }
+}
+
+// Resize a FlatBuffer in-place by iterating through all offsets in the buffer
+// and adjusting them by "delta" if they straddle the start offset.
+// Once that is done, bytes can now be inserted/deleted safely.
+// "delta" may be negative (shrinking).
+// Unless "delta" is a multiple of the largest alignment, you'll create a small
+// amount of garbage space in the buffer (usually 0..7 bytes).
+// If your FlatBuffer's root table is not the schema's root table, you should
+// pass in your root_table type as well.
+class ResizeContext {
+ public:
+ ResizeContext(const reflection::Schema &schema, uoffset_t start, int delta,
+ std::vector *flatbuf,
+ const reflection::Object *root_table = nullptr)
+ : schema_(schema), startptr_(flatbuf->data() + start),
+ delta_(delta), buf_(*flatbuf),
+ dag_check_(flatbuf->size() / sizeof(uoffset_t), false) {
+ auto mask = static_cast(sizeof(largest_scalar_t) - 1);
+ delta_ = (delta_ + mask) & ~mask;
+ if (!delta_) return; // We can't shrink by less than largest_scalar_t.
+ // Now change all the offsets by delta_.
+ auto root = GetAnyRoot(buf_.data());
+ Straddle(buf_.data(), root, buf_.data());
+ ResizeTable(root_table ? *root_table : *schema.root_table(), root);
+ // We can now add or remove bytes at start.
+ if (delta_ > 0) buf_.insert(buf_.begin() + start, delta_, 0);
+ else buf_.erase(buf_.begin() + start, buf_.begin() + start - delta_);
+ }
+
+ // Check if the range between first (lower address) and second straddles
+ // the insertion point. If it does, change the offset at offsetloc (of
+ // type T, with direction D).
+ template void Straddle(void *first, void *second,
+ void *offsetloc) {
+ if (first <= startptr_ && second >= startptr_) {
+ WriteScalar(offsetloc, ReadScalar(offsetloc) + delta_ * D);
+ DagCheck(offsetloc) = true;
+ }
+ }
+
+ // This returns a boolean that records if the corresponding offset location
+ // has been modified already. If so, we can't even read the corresponding
+ // offset, since it is pointing to a location that is illegal until the
+ // resize actually happens.
+ // This must be checked for every offset, since we can't know which offsets
+ // will straddle and which won't.
+ uint8_t &DagCheck(void *offsetloc) {
+ auto dag_idx = reinterpret_cast(offsetloc) -
+ reinterpret_cast(buf_.data());
+ return dag_check_[dag_idx];
+ }
+
+ void ResizeTable(const reflection::Object &objectdef, Table *table) {
+ if (DagCheck(table))
+ return; // Table already visited.
+ auto vtable = table->GetVTable();
+ // Check if the vtable offset points beyond the insertion point.
+ Straddle(table, vtable, table);
+ // This direction shouldn't happen because vtables that sit before tables
+ // are always directly adjacent, but check just in case we ever change the
+ // way flatbuffers are built.
+ Straddle(vtable, table, table);
+ // Early out: since all fields inside the table must point forwards in
+ // memory, if the insertion point is before the table we can stop here.
+ auto tableloc = reinterpret_cast(table);
+ if (startptr_ <= tableloc) return;
+ // Check each field.
+ auto fielddefs = objectdef.fields();
+ for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
+ auto &fielddef = **it;
+ auto base_type = fielddef.type()->base_type();
+ // Ignore scalars.
+ if (base_type <= reflection::Double) continue;
+ // Ignore fields that are not stored.
+ auto offset = table->GetOptionalFieldOffset(fielddef.offset());
+ if (!offset) continue;
+ // Ignore structs.
+ auto subobjectdef = base_type == reflection::Obj ?
+ schema_.objects()->Get(fielddef.type()->index()) : nullptr;
+ if (subobjectdef && subobjectdef->is_struct()) continue;
+ // Get this fields' offset, and read it if safe.
+ auto offsetloc = tableloc + offset;
+ if (DagCheck(offsetloc))
+ continue; // This offset already visited.
+ auto ref = offsetloc + ReadScalar(offsetloc);
+ Straddle(offsetloc, ref, offsetloc);
+ // Recurse.
+ switch (base_type) {
+ case reflection::Obj: {
+ ResizeTable(*subobjectdef, reinterpret_cast(ref));
+ break;
+ }
+ case reflection::Vector: {
+ auto elem_type = fielddef.type()->element();
+ if (elem_type != reflection::Obj && elem_type != reflection::String)
+ break;
+ auto vec = reinterpret_cast *>(ref);
+ auto elemobjectdef = elem_type == reflection::Obj
+ ? schema_.objects()->Get(fielddef.type()->index())
+ : nullptr;
+ if (elemobjectdef && elemobjectdef->is_struct()) break;
+ for (uoffset_t i = 0; i < vec->size(); i++) {
+ auto loc = vec->Data() + i * sizeof(uoffset_t);
+ if (DagCheck(loc))
+ continue; // This offset already visited.
+ auto dest = loc + vec->Get(i);
+ Straddle(loc, dest ,loc);
+ if (elemobjectdef)
+ ResizeTable(*elemobjectdef, reinterpret_cast(dest));
+ }
+ break;
+ }
+ case reflection::Union: {
+ ResizeTable(GetUnionType(schema_, objectdef, fielddef, *table),
+ reinterpret_cast(ref));
+ break;
+ }
+ case reflection::String:
+ break;
+ default:
+ assert(false);
+ }
+ }
+ }
+
+ void operator=(const ResizeContext &rc);
+
+ private:
+ const reflection::Schema &schema_;
+ uint8_t *startptr_;
+ int delta_;
+ std::vector &buf_;
+ std::vector dag_check_;
+};
+
+void SetString(const reflection::Schema &schema, const std::string &val,
+ const String *str, std::vector *flatbuf,
+ const reflection::Object *root_table) {
+ auto delta = static_cast(val.size()) - static_cast(str->Length());
+ auto start = static_cast(reinterpret_cast(str) -
+ 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);
+ }
+ // Copy new data. Safe because we created the right amount of space.
+ memcpy(flatbuf->data() + start, val.c_str(), val.size() + 1);
+}
+
+uint8_t *ResizeAnyVector(const reflection::Schema &schema, uoffset_t newsize,
+ const VectorOfAny *vec, uoffset_t num_elems,
+ uoffset_t elem_size, std::vector *flatbuf,
+ const reflection::Object *root_table) {
+ auto delta_elem = static_cast(newsize) - static_cast(num_elems);
+ auto delta_bytes = delta_elem * static_cast(elem_size);
+ auto vec_start = reinterpret_cast(vec) - flatbuf->data();
+ auto start = static_cast(vec_start + sizeof(uoffset_t) +
+ elem_size * num_elems);
+ if (delta_bytes) {
+ if (delta_elem < 0) {
+ // Clear elements we're throwing away, since some might remain in the
+ // buffer.
+ auto size_clear = -delta_elem * elem_size;
+ memset(flatbuf->data() + start - size_clear, 0, size_clear);
+ }
+ ResizeContext(schema, start, delta_bytes, flatbuf, root_table);
+ WriteScalar(flatbuf->data() + vec_start, newsize); // Length field.
+ // Set new elements to 0.. this can be overwritten by the caller.
+ if (delta_elem > 0) {
+ memset(flatbuf->data() + start, 0, delta_elem * elem_size);
+ }
+ }
+ return flatbuf->data() + start;
+}
+
+const uint8_t *AddFlatBuffer(std::vector &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