diff --git a/BUILD b/BUILD index 210e104ed..b69760238 100644 --- a/BUILD +++ b/BUILD @@ -139,6 +139,8 @@ cc_test( "src/idl_parser.cpp", "src/reflection.cpp", "src/util.cpp", + "tests/evolution_test/evolution_v1_generated.h", + "tests/evolution_test/evolution_v2_generated.h", "tests/namespace_test/namespace_test1_generated.h", "tests/namespace_test/namespace_test2_generated.h", "tests/native_type_test_impl.cpp", @@ -159,6 +161,10 @@ cc_test( ":tests/arrays_test.bfbs", ":tests/arrays_test.fbs", ":tests/arrays_test.golden", + ":tests/evolution_test/evolution_v1.fbs", + ":tests/evolution_test/evolution_v1.json", + ":tests/evolution_test/evolution_v2.fbs", + ":tests/evolution_test/evolution_v2.json", ":tests/include_test/include_test1.fbs", ":tests/include_test/sub/include_test2.fbs", ":tests/monster_extra.fbs", diff --git a/tests/evolution_test/evolution_v1.fbs b/tests/evolution_test/evolution_v1.fbs new file mode 100644 index 000000000..2576c8791 --- /dev/null +++ b/tests/evolution_test/evolution_v1.fbs @@ -0,0 +1,39 @@ +namespace Evolution.V1; + +table TableA { + a:float; + b:int; +} + +table TableB { + a:int; +} + +enum Enum : byte { + King, + Queen +} + +union Union { + TableA, + TableB +} + +struct Struct { + a:int; + b:double; +} + +table Root { + a:int; + b:bool; + c:Union; + d:Enum; + e:TableA; + f:Struct; + g:[int]; + h:[TableB]; + i:int = 1234; +} + +root_type Root; \ No newline at end of file diff --git a/tests/evolution_test/evolution_v1.json b/tests/evolution_test/evolution_v1.json new file mode 100644 index 000000000..8b4b35222 --- /dev/null +++ b/tests/evolution_test/evolution_v1.json @@ -0,0 +1,26 @@ +{ + "a": 42, + "b": true, + "c_type": "TableB", + "c": { + "a": 15 + }, + "d": "King", + "e": { + "a": 3.1452, + "b": 325 + }, + "f":{ + "a": 16, + "b": 243.980943 + }, + "g": [ 7, 8, 9], + "h": [ + { + "a": 212 + }, + { + "a": 459 + } + ] +} \ No newline at end of file diff --git a/tests/evolution_test/evolution_v1_generated.h b/tests/evolution_test/evolution_v1_generated.h new file mode 100644 index 000000000..1d447ffc1 --- /dev/null +++ b/tests/evolution_test/evolution_v1_generated.h @@ -0,0 +1,453 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_EVOLUTIONV1_EVOLUTION_V1_H_ +#define FLATBUFFERS_GENERATED_EVOLUTIONV1_EVOLUTION_V1_H_ + +#include "flatbuffers/flatbuffers.h" + +namespace Evolution { +namespace V1 { + +struct TableA; + +struct TableB; + +struct Struct; + +struct Root; + +enum class Enum : int8_t { + King = 0, + Queen = 1, + MIN = King, + MAX = Queen +}; + +inline const Enum (&EnumValuesEnum())[2] { + static const Enum values[] = { + Enum::King, + Enum::Queen + }; + return values; +} + +inline const char * const *EnumNamesEnum() { + static const char * const names[3] = { + "King", + "Queen", + nullptr + }; + return names; +} + +inline const char *EnumNameEnum(Enum e) { + if (e < Enum::King || e > Enum::Queen) return ""; + const size_t index = static_cast(e); + return EnumNamesEnum()[index]; +} + +enum class Union : uint8_t { + NONE = 0, + TableA = 1, + TableB = 2, + MIN = NONE, + MAX = TableB +}; + +inline const Union (&EnumValuesUnion())[3] { + static const Union values[] = { + Union::NONE, + Union::TableA, + Union::TableB + }; + return values; +} + +inline const char * const *EnumNamesUnion() { + static const char * const names[4] = { + "NONE", + "TableA", + "TableB", + nullptr + }; + return names; +} + +inline const char *EnumNameUnion(Union e) { + if (e < Union::NONE || e > Union::TableB) return ""; + const size_t index = static_cast(e); + return EnumNamesUnion()[index]; +} + +template struct UnionTraits { + static const Union enum_value = Union::NONE; +}; + +template<> struct UnionTraits { + static const Union enum_value = Union::TableA; +}; + +template<> struct UnionTraits { + static const Union enum_value = Union::TableB; +}; + +bool VerifyUnion(flatbuffers::Verifier &verifier, const void *obj, Union type); +bool VerifyUnionVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector> *values, const flatbuffers::Vector *types); + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Struct FLATBUFFERS_FINAL_CLASS { + private: + int32_t a_; + int32_t padding0__; + double b_; + + public: + Struct() { + memset(static_cast(this), 0, sizeof(Struct)); + } + Struct(int32_t _a, double _b) + : a_(flatbuffers::EndianScalar(_a)), + padding0__(0), + b_(flatbuffers::EndianScalar(_b)) { + (void)padding0__; + } + int32_t a() const { + return flatbuffers::EndianScalar(a_); + } + double b() const { + return flatbuffers::EndianScalar(b_); + } +}; +FLATBUFFERS_STRUCT_END(Struct, 16); + +struct TableA FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_A = 4, + VT_B = 6 + }; + float a() const { + return GetField(VT_A, 0.0f); + } + int32_t b() const { + return GetField(VT_B, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_A) && + VerifyField(verifier, VT_B) && + verifier.EndTable(); + } +}; + +struct TableABuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_a(float a) { + fbb_.AddElement(TableA::VT_A, a, 0.0f); + } + void add_b(int32_t b) { + fbb_.AddElement(TableA::VT_B, b, 0); + } + explicit TableABuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + TableABuilder &operator=(const TableABuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateTableA( + flatbuffers::FlatBufferBuilder &_fbb, + float a = 0.0f, + int32_t b = 0) { + TableABuilder builder_(_fbb); + builder_.add_b(b); + builder_.add_a(a); + return builder_.Finish(); +} + +struct TableB FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_A = 4 + }; + int32_t a() const { + return GetField(VT_A, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_A) && + verifier.EndTable(); + } +}; + +struct TableBBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_a(int32_t a) { + fbb_.AddElement(TableB::VT_A, a, 0); + } + explicit TableBBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + TableBBuilder &operator=(const TableBBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateTableB( + flatbuffers::FlatBufferBuilder &_fbb, + int32_t a = 0) { + TableBBuilder builder_(_fbb); + builder_.add_a(a); + return builder_.Finish(); +} + +struct Root FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_A = 4, + VT_B = 6, + VT_C_TYPE = 8, + VT_C = 10, + VT_D = 12, + VT_E = 14, + VT_F = 16, + VT_G = 18, + VT_H = 20, + VT_I = 22 + }; + int32_t a() const { + return GetField(VT_A, 0); + } + bool b() const { + return GetField(VT_B, 0) != 0; + } + Evolution::V1::Union c_type() const { + return static_cast(GetField(VT_C_TYPE, 0)); + } + const void *c() const { + return GetPointer(VT_C); + } + template const T *c_as() const; + const Evolution::V1::TableA *c_as_TableA() const { + return c_type() == Evolution::V1::Union::TableA ? static_cast(c()) : nullptr; + } + const Evolution::V1::TableB *c_as_TableB() const { + return c_type() == Evolution::V1::Union::TableB ? static_cast(c()) : nullptr; + } + Evolution::V1::Enum d() const { + return static_cast(GetField(VT_D, 0)); + } + const Evolution::V1::TableA *e() const { + return GetPointer(VT_E); + } + const Evolution::V1::Struct *f() const { + return GetStruct(VT_F); + } + const flatbuffers::Vector *g() const { + return GetPointer *>(VT_G); + } + const flatbuffers::Vector> *h() const { + return GetPointer> *>(VT_H); + } + int32_t i() const { + return GetField(VT_I, 1234); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_A) && + VerifyField(verifier, VT_B) && + VerifyField(verifier, VT_C_TYPE) && + VerifyOffset(verifier, VT_C) && + VerifyUnion(verifier, c(), c_type()) && + VerifyField(verifier, VT_D) && + VerifyOffset(verifier, VT_E) && + verifier.VerifyTable(e()) && + VerifyField(verifier, VT_F) && + VerifyOffset(verifier, VT_G) && + verifier.VerifyVector(g()) && + VerifyOffset(verifier, VT_H) && + verifier.VerifyVector(h()) && + verifier.VerifyVectorOfTables(h()) && + VerifyField(verifier, VT_I) && + verifier.EndTable(); + } +}; + +template<> inline const Evolution::V1::TableA *Root::c_as() const { + return c_as_TableA(); +} + +template<> inline const Evolution::V1::TableB *Root::c_as() const { + return c_as_TableB(); +} + +struct RootBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_a(int32_t a) { + fbb_.AddElement(Root::VT_A, a, 0); + } + void add_b(bool b) { + fbb_.AddElement(Root::VT_B, static_cast(b), 0); + } + void add_c_type(Evolution::V1::Union c_type) { + fbb_.AddElement(Root::VT_C_TYPE, static_cast(c_type), 0); + } + void add_c(flatbuffers::Offset c) { + fbb_.AddOffset(Root::VT_C, c); + } + void add_d(Evolution::V1::Enum d) { + fbb_.AddElement(Root::VT_D, static_cast(d), 0); + } + void add_e(flatbuffers::Offset e) { + fbb_.AddOffset(Root::VT_E, e); + } + void add_f(const Evolution::V1::Struct *f) { + fbb_.AddStruct(Root::VT_F, f); + } + void add_g(flatbuffers::Offset> g) { + fbb_.AddOffset(Root::VT_G, g); + } + void add_h(flatbuffers::Offset>> h) { + fbb_.AddOffset(Root::VT_H, h); + } + void add_i(int32_t i) { + fbb_.AddElement(Root::VT_I, i, 1234); + } + explicit RootBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + RootBuilder &operator=(const RootBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRoot( + flatbuffers::FlatBufferBuilder &_fbb, + int32_t a = 0, + bool b = false, + Evolution::V1::Union c_type = Evolution::V1::Union::NONE, + flatbuffers::Offset c = 0, + Evolution::V1::Enum d = Evolution::V1::Enum::King, + flatbuffers::Offset e = 0, + const Evolution::V1::Struct *f = 0, + flatbuffers::Offset> g = 0, + flatbuffers::Offset>> h = 0, + int32_t i = 1234) { + RootBuilder builder_(_fbb); + builder_.add_i(i); + builder_.add_h(h); + builder_.add_g(g); + builder_.add_f(f); + builder_.add_e(e); + builder_.add_c(c); + builder_.add_a(a); + builder_.add_d(d); + builder_.add_c_type(c_type); + builder_.add_b(b); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateRootDirect( + flatbuffers::FlatBufferBuilder &_fbb, + int32_t a = 0, + bool b = false, + Evolution::V1::Union c_type = Evolution::V1::Union::NONE, + flatbuffers::Offset c = 0, + Evolution::V1::Enum d = Evolution::V1::Enum::King, + flatbuffers::Offset e = 0, + const Evolution::V1::Struct *f = 0, + const std::vector *g = nullptr, + const std::vector> *h = nullptr, + int32_t i = 1234) { + auto g__ = g ? _fbb.CreateVector(*g) : 0; + auto h__ = h ? _fbb.CreateVector>(*h) : 0; + return Evolution::V1::CreateRoot( + _fbb, + a, + b, + c_type, + c, + d, + e, + f, + g__, + h__, + i); +} + +inline bool VerifyUnion(flatbuffers::Verifier &verifier, const void *obj, Union type) { + switch (type) { + case Union::NONE: { + return true; + } + case Union::TableA: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Union::TableB: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + default: return true; + } +} + +inline bool VerifyUnionVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector> *values, const flatbuffers::Vector *types) { + if (!values || !types) return !values && !types; + if (values->size() != types->size()) return false; + for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { + if (!VerifyUnion( + verifier, values->Get(i), types->GetEnum(i))) { + return false; + } + } + return true; +} + +inline const Evolution::V1::Root *GetRoot(const void *buf) { + return flatbuffers::GetRoot(buf); +} + +inline const Evolution::V1::Root *GetSizePrefixedRoot(const void *buf) { + return flatbuffers::GetSizePrefixedRoot(buf); +} + +inline bool VerifyRootBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(nullptr); +} + +inline bool VerifySizePrefixedRootBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(nullptr); +} + +inline void FinishRootBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) { + fbb.Finish(root); +} + +inline void FinishSizePrefixedRootBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root); +} + +} // namespace V1 +} // namespace Evolution + +#endif // FLATBUFFERS_GENERATED_EVOLUTIONV1_EVOLUTION_V1_H_ diff --git a/tests/evolution_test/evolution_v2.fbs b/tests/evolution_test/evolution_v2.fbs new file mode 100644 index 000000000..5f4601e6e --- /dev/null +++ b/tests/evolution_test/evolution_v2.fbs @@ -0,0 +1,50 @@ +namespace Evolution.V2; + +table TableA { + b:int (id: 1); // swapped with 'a' + a:float (id: 0); // swapped with 'b' + c:string (id: 2); // new in v2 +} + +table TableB { + a:int; +} + +table TableC { // new in v2 + a:double; + b:string; +} + +enum Enum : byte { + King, + Queen, + Rook, // new in v2 + Bishop // new in v2 +} + +union Union { + TableA, + TableB, + TableC +} + +struct Struct { + a:int; + b:double; +} + +table Root { + a:int (deprecated); // deprecated in v2 + b:bool; + c:Union; + d:Enum; + e:TableA; + ff:Struct; // renamed from 'f' in v1 + g:[int]; + h:[TableB]; + i:uint = 1234; + j:TableC; // new in v2 + k:uint8 = 56; // new in v2 +} + +root_type Root; \ No newline at end of file diff --git a/tests/evolution_test/evolution_v2.json b/tests/evolution_test/evolution_v2.json new file mode 100644 index 000000000..625f50600 --- /dev/null +++ b/tests/evolution_test/evolution_v2.json @@ -0,0 +1,34 @@ +{ + "b": false, + "c_type": "TableC", + "c": { + "a": 984.2494 + }, + "d": "Bishop", + "e": { + "a": 3.1452, + "b": 435, + "c": "yummy yummy fig bar bar" + }, + "ff":{ + "a": 35, + "b": 243.980943 + }, + "g": [ 7, 8, 10], + "h": [ + { + "a": 212 + }, + { + "a": 459 + }, + { + "a": 333 + } + ], + "i": 4321, + "j": { + "a": 9874.342, + "b": "more please" + } +} diff --git a/tests/evolution_test/evolution_v2_generated.h b/tests/evolution_test/evolution_v2_generated.h new file mode 100644 index 000000000..4e1b8404d --- /dev/null +++ b/tests/evolution_test/evolution_v2_generated.h @@ -0,0 +1,578 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_EVOLUTIONV2_EVOLUTION_V2_H_ +#define FLATBUFFERS_GENERATED_EVOLUTIONV2_EVOLUTION_V2_H_ + +#include "flatbuffers/flatbuffers.h" + +namespace Evolution { +namespace V2 { + +struct TableA; + +struct TableB; + +struct TableC; + +struct Struct; + +struct Root; + +enum class Enum : int8_t { + King = 0, + Queen = 1, + Rook = 2, + Bishop = 3, + MIN = King, + MAX = Bishop +}; + +inline const Enum (&EnumValuesEnum())[4] { + static const Enum values[] = { + Enum::King, + Enum::Queen, + Enum::Rook, + Enum::Bishop + }; + return values; +} + +inline const char * const *EnumNamesEnum() { + static const char * const names[5] = { + "King", + "Queen", + "Rook", + "Bishop", + nullptr + }; + return names; +} + +inline const char *EnumNameEnum(Enum e) { + if (e < Enum::King || e > Enum::Bishop) return ""; + const size_t index = static_cast(e); + return EnumNamesEnum()[index]; +} + +enum class Union : uint8_t { + NONE = 0, + TableA = 1, + TableB = 2, + TableC = 3, + MIN = NONE, + MAX = TableC +}; + +inline const Union (&EnumValuesUnion())[4] { + static const Union values[] = { + Union::NONE, + Union::TableA, + Union::TableB, + Union::TableC + }; + return values; +} + +inline const char * const *EnumNamesUnion() { + static const char * const names[5] = { + "NONE", + "TableA", + "TableB", + "TableC", + nullptr + }; + return names; +} + +inline const char *EnumNameUnion(Union e) { + if (e < Union::NONE || e > Union::TableC) return ""; + const size_t index = static_cast(e); + return EnumNamesUnion()[index]; +} + +template struct UnionTraits { + static const Union enum_value = Union::NONE; +}; + +template<> struct UnionTraits { + static const Union enum_value = Union::TableA; +}; + +template<> struct UnionTraits { + static const Union enum_value = Union::TableB; +}; + +template<> struct UnionTraits { + static const Union enum_value = Union::TableC; +}; + +bool VerifyUnion(flatbuffers::Verifier &verifier, const void *obj, Union type); +bool VerifyUnionVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector> *values, const flatbuffers::Vector *types); + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Struct FLATBUFFERS_FINAL_CLASS { + private: + int32_t a_; + int32_t padding0__; + double b_; + + public: + Struct() { + memset(static_cast(this), 0, sizeof(Struct)); + } + Struct(int32_t _a, double _b) + : a_(flatbuffers::EndianScalar(_a)), + padding0__(0), + b_(flatbuffers::EndianScalar(_b)) { + (void)padding0__; + } + int32_t a() const { + return flatbuffers::EndianScalar(a_); + } + double b() const { + return flatbuffers::EndianScalar(b_); + } +}; +FLATBUFFERS_STRUCT_END(Struct, 16); + +struct TableA FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_A = 4, + VT_B = 6, + VT_C = 8 + }; + float a() const { + return GetField(VT_A, 0.0f); + } + int32_t b() const { + return GetField(VT_B, 0); + } + const flatbuffers::String *c() const { + return GetPointer(VT_C); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_A) && + VerifyField(verifier, VT_B) && + VerifyOffset(verifier, VT_C) && + verifier.VerifyString(c()) && + verifier.EndTable(); + } +}; + +struct TableABuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_a(float a) { + fbb_.AddElement(TableA::VT_A, a, 0.0f); + } + void add_b(int32_t b) { + fbb_.AddElement(TableA::VT_B, b, 0); + } + void add_c(flatbuffers::Offset c) { + fbb_.AddOffset(TableA::VT_C, c); + } + explicit TableABuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + TableABuilder &operator=(const TableABuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateTableA( + flatbuffers::FlatBufferBuilder &_fbb, + float a = 0.0f, + int32_t b = 0, + flatbuffers::Offset c = 0) { + TableABuilder builder_(_fbb); + builder_.add_c(c); + builder_.add_b(b); + builder_.add_a(a); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateTableADirect( + flatbuffers::FlatBufferBuilder &_fbb, + float a = 0.0f, + int32_t b = 0, + const char *c = nullptr) { + auto c__ = c ? _fbb.CreateString(c) : 0; + return Evolution::V2::CreateTableA( + _fbb, + a, + b, + c__); +} + +struct TableB FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_A = 4 + }; + int32_t a() const { + return GetField(VT_A, 0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_A) && + verifier.EndTable(); + } +}; + +struct TableBBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_a(int32_t a) { + fbb_.AddElement(TableB::VT_A, a, 0); + } + explicit TableBBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + TableBBuilder &operator=(const TableBBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateTableB( + flatbuffers::FlatBufferBuilder &_fbb, + int32_t a = 0) { + TableBBuilder builder_(_fbb); + builder_.add_a(a); + return builder_.Finish(); +} + +struct TableC FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_A = 4, + VT_B = 6 + }; + double a() const { + return GetField(VT_A, 0.0); + } + const flatbuffers::String *b() const { + return GetPointer(VT_B); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_A) && + VerifyOffset(verifier, VT_B) && + verifier.VerifyString(b()) && + verifier.EndTable(); + } +}; + +struct TableCBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_a(double a) { + fbb_.AddElement(TableC::VT_A, a, 0.0); + } + void add_b(flatbuffers::Offset b) { + fbb_.AddOffset(TableC::VT_B, b); + } + explicit TableCBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + TableCBuilder &operator=(const TableCBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateTableC( + flatbuffers::FlatBufferBuilder &_fbb, + double a = 0.0, + flatbuffers::Offset b = 0) { + TableCBuilder builder_(_fbb); + builder_.add_a(a); + builder_.add_b(b); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateTableCDirect( + flatbuffers::FlatBufferBuilder &_fbb, + double a = 0.0, + const char *b = nullptr) { + auto b__ = b ? _fbb.CreateString(b) : 0; + return Evolution::V2::CreateTableC( + _fbb, + a, + b__); +} + +struct Root FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_B = 6, + VT_C_TYPE = 8, + VT_C = 10, + VT_D = 12, + VT_E = 14, + VT_FF = 16, + VT_G = 18, + VT_H = 20, + VT_I = 22, + VT_J = 24, + VT_K = 26 + }; + bool b() const { + return GetField(VT_B, 0) != 0; + } + Evolution::V2::Union c_type() const { + return static_cast(GetField(VT_C_TYPE, 0)); + } + const void *c() const { + return GetPointer(VT_C); + } + template const T *c_as() const; + const Evolution::V2::TableA *c_as_TableA() const { + return c_type() == Evolution::V2::Union::TableA ? static_cast(c()) : nullptr; + } + const Evolution::V2::TableB *c_as_TableB() const { + return c_type() == Evolution::V2::Union::TableB ? static_cast(c()) : nullptr; + } + const Evolution::V2::TableC *c_as_TableC() const { + return c_type() == Evolution::V2::Union::TableC ? static_cast(c()) : nullptr; + } + Evolution::V2::Enum d() const { + return static_cast(GetField(VT_D, 0)); + } + const Evolution::V2::TableA *e() const { + return GetPointer(VT_E); + } + const Evolution::V2::Struct *ff() const { + return GetStruct(VT_FF); + } + const flatbuffers::Vector *g() const { + return GetPointer *>(VT_G); + } + const flatbuffers::Vector> *h() const { + return GetPointer> *>(VT_H); + } + uint32_t i() const { + return GetField(VT_I, 1234); + } + const Evolution::V2::TableC *j() const { + return GetPointer(VT_J); + } + uint8_t k() const { + return GetField(VT_K, 56); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_B) && + VerifyField(verifier, VT_C_TYPE) && + VerifyOffset(verifier, VT_C) && + VerifyUnion(verifier, c(), c_type()) && + VerifyField(verifier, VT_D) && + VerifyOffset(verifier, VT_E) && + verifier.VerifyTable(e()) && + VerifyField(verifier, VT_FF) && + VerifyOffset(verifier, VT_G) && + verifier.VerifyVector(g()) && + VerifyOffset(verifier, VT_H) && + verifier.VerifyVector(h()) && + verifier.VerifyVectorOfTables(h()) && + VerifyField(verifier, VT_I) && + VerifyOffset(verifier, VT_J) && + verifier.VerifyTable(j()) && + VerifyField(verifier, VT_K) && + verifier.EndTable(); + } +}; + +template<> inline const Evolution::V2::TableA *Root::c_as() const { + return c_as_TableA(); +} + +template<> inline const Evolution::V2::TableB *Root::c_as() const { + return c_as_TableB(); +} + +template<> inline const Evolution::V2::TableC *Root::c_as() const { + return c_as_TableC(); +} + +struct RootBuilder { + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_b(bool b) { + fbb_.AddElement(Root::VT_B, static_cast(b), 0); + } + void add_c_type(Evolution::V2::Union c_type) { + fbb_.AddElement(Root::VT_C_TYPE, static_cast(c_type), 0); + } + void add_c(flatbuffers::Offset c) { + fbb_.AddOffset(Root::VT_C, c); + } + void add_d(Evolution::V2::Enum d) { + fbb_.AddElement(Root::VT_D, static_cast(d), 0); + } + void add_e(flatbuffers::Offset e) { + fbb_.AddOffset(Root::VT_E, e); + } + void add_ff(const Evolution::V2::Struct *ff) { + fbb_.AddStruct(Root::VT_FF, ff); + } + void add_g(flatbuffers::Offset> g) { + fbb_.AddOffset(Root::VT_G, g); + } + void add_h(flatbuffers::Offset>> h) { + fbb_.AddOffset(Root::VT_H, h); + } + void add_i(uint32_t i) { + fbb_.AddElement(Root::VT_I, i, 1234); + } + void add_j(flatbuffers::Offset j) { + fbb_.AddOffset(Root::VT_J, j); + } + void add_k(uint8_t k) { + fbb_.AddElement(Root::VT_K, k, 56); + } + explicit RootBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + RootBuilder &operator=(const RootBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateRoot( + flatbuffers::FlatBufferBuilder &_fbb, + bool b = false, + Evolution::V2::Union c_type = Evolution::V2::Union::NONE, + flatbuffers::Offset c = 0, + Evolution::V2::Enum d = Evolution::V2::Enum::King, + flatbuffers::Offset e = 0, + const Evolution::V2::Struct *ff = 0, + flatbuffers::Offset> g = 0, + flatbuffers::Offset>> h = 0, + uint32_t i = 1234, + flatbuffers::Offset j = 0, + uint8_t k = 56) { + RootBuilder builder_(_fbb); + builder_.add_j(j); + builder_.add_i(i); + builder_.add_h(h); + builder_.add_g(g); + builder_.add_ff(ff); + builder_.add_e(e); + builder_.add_c(c); + builder_.add_k(k); + builder_.add_d(d); + builder_.add_c_type(c_type); + builder_.add_b(b); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateRootDirect( + flatbuffers::FlatBufferBuilder &_fbb, + bool b = false, + Evolution::V2::Union c_type = Evolution::V2::Union::NONE, + flatbuffers::Offset c = 0, + Evolution::V2::Enum d = Evolution::V2::Enum::King, + flatbuffers::Offset e = 0, + const Evolution::V2::Struct *ff = 0, + const std::vector *g = nullptr, + const std::vector> *h = nullptr, + uint32_t i = 1234, + flatbuffers::Offset j = 0, + uint8_t k = 56) { + auto g__ = g ? _fbb.CreateVector(*g) : 0; + auto h__ = h ? _fbb.CreateVector>(*h) : 0; + return Evolution::V2::CreateRoot( + _fbb, + b, + c_type, + c, + d, + e, + ff, + g__, + h__, + i, + j, + k); +} + +inline bool VerifyUnion(flatbuffers::Verifier &verifier, const void *obj, Union type) { + switch (type) { + case Union::NONE: { + return true; + } + case Union::TableA: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Union::TableB: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case Union::TableC: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + default: return true; + } +} + +inline bool VerifyUnionVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector> *values, const flatbuffers::Vector *types) { + if (!values || !types) return !values && !types; + if (values->size() != types->size()) return false; + for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) { + if (!VerifyUnion( + verifier, values->Get(i), types->GetEnum(i))) { + return false; + } + } + return true; +} + +inline const Evolution::V2::Root *GetRoot(const void *buf) { + return flatbuffers::GetRoot(buf); +} + +inline const Evolution::V2::Root *GetSizePrefixedRoot(const void *buf) { + return flatbuffers::GetSizePrefixedRoot(buf); +} + +inline bool VerifyRootBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(nullptr); +} + +inline bool VerifySizePrefixedRootBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(nullptr); +} + +inline void FinishRootBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) { + fbb.Finish(root); +} + +inline void FinishSizePrefixedRootBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root); +} + +} // namespace V2 +} // namespace Evolution + +#endif // FLATBUFFERS_GENERATED_EVOLUTIONV2_EVOLUTION_V2_H_ diff --git a/tests/generate_code.bat b/tests/generate_code.bat index 8b78a5d89..29a029c67 100644 --- a/tests/generate_code.bat +++ b/tests/generate_code.bat @@ -18,6 +18,7 @@ if "%1"=="-b" set buildtype=%2 ..\%buildtype%\flatc.exe --cpp --java --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --grpc --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json || goto FAIL ..\%buildtype%\flatc.exe --cpp --java --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs || goto FAIL ..\%buildtype%\flatc.exe --cpp --java --csharp --js --ts --php --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs || goto FAIL +..\%buildtype%\flatc.exe --cpp --scoped-enums -o evolution_test ./evolution_test/evolution_v1.fbs ./evolution_test/evolution_v2.fbs|| goto FAIL ..\%buildtype%\flatc.exe -b --schema --bfbs-comments --bfbs-builtins -I include_test monster_test.fbs || goto FAIL ..\%buildtype%\flatc.exe -b --schema --bfbs-comments --bfbs-builtins -I include_test arrays_test.fbs || goto FAIL ..\%buildtype%\flatc.exe --jsonschema --schema -I include_test monster_test.fbs || goto FAIL diff --git a/tests/generate_code.sh b/tests/generate_code.sh index 37b405851..14b621ccf 100755 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -18,6 +18,7 @@ set -e ../flatc --cpp --java --kotlin --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --grpc --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json ../flatc --cpp --java --kotlin --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs ../flatc --cpp --java --kotlin --csharp --js --ts --php --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs +../flatc --cpp --scoped-enums -o evolution_test ./evolution_test/evolution_v*.fbs ../flatc -b --schema --bfbs-comments --bfbs-builtins -I include_test monster_test.fbs ../flatc -b --schema --bfbs-comments --bfbs-builtins -I include_test arrays_test.fbs ../flatc --jsonschema --schema -I include_test monster_test.fbs diff --git a/tests/test.cpp b/tests/test.cpp index 361b83547..68716c67b 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -37,6 +37,8 @@ #include "monster_extra_generated.h" #if !defined(_MSC_VER) || _MSC_VER >= 1700 # include "arrays_test_generated.h" +# include "evolution_test/evolution_v1_generated.h" +# include "evolution_test/evolution_v2_generated.h" #endif #include "native_type_test_generated.h" @@ -2310,6 +2312,76 @@ void InvalidNestedFlatbufferTest() { false); } +void EvolutionTest() { + // VS10 does not support typed enums, exclude from tests +#if !defined(_MSC_VER) || _MSC_VER >= 1700 + const int NUM_VERSIONS = 2; + std::string schemas[NUM_VERSIONS]; + std::string jsonfiles[NUM_VERSIONS]; + std::vector binaries[NUM_VERSIONS]; + flatbuffers::Verifier *verifiers[NUM_VERSIONS]; + + flatbuffers::IDLOptions idl_opts; + idl_opts.lang_to_generate |= flatbuffers::IDLOptions::kBinary; + flatbuffers::Parser parser(idl_opts); + + // Load all the schema versions and their associated data. + for (int i = 0; i < NUM_VERSIONS; ++i) { + std::string schema = test_data_path + "evolution_test/evolution_v" + + flatbuffers::NumToString(i + 1) + ".fbs"; + TEST_ASSERT(flatbuffers::LoadFile(schema.c_str(), false, &schemas[i])); + std::string json = test_data_path + "evolution_test/evolution_v" + + flatbuffers::NumToString(i + 1) + ".json"; + TEST_ASSERT(flatbuffers::LoadFile(json.c_str(), false, &jsonfiles[i])); + + TEST_ASSERT(parser.Parse(schemas[i].c_str())); + TEST_ASSERT(parser.Parse(jsonfiles[i].c_str())); + + auto bufLen = parser.builder_.GetSize(); + auto buf = parser.builder_.GetBufferPointer(); + binaries[i].reserve(bufLen); + std::copy(buf, buf + bufLen, std::back_inserter(binaries[i])); + + verifiers[i] = new flatbuffers::Verifier(&binaries[i].front(), bufLen); + } + + // Assert that all the verifiers for the different schema versions properly verify any version data. + for (int i = 0; i < NUM_VERSIONS; ++i) { + TEST_ASSERT(Evolution::V1::VerifyRootBuffer(*verifiers[i])); + TEST_ASSERT(Evolution::V2::VerifyRootBuffer(*verifiers[i])); + } + + // Test backwards compatibility by reading old data with an evolved schema. + auto root_v1_viewed_from_v2 = Evolution::V2::GetRoot(&binaries[0].front()); + // field 'j' is new in version 2, so it should be null. + TEST_EQ(root_v1_viewed_from_v2->j(), NULL); + // field 'k' is new in version 2 with a default of 56. + TEST_EQ(root_v1_viewed_from_v2->k(), 56); + // field 'c' of 'TableA' is new in version 2, so it should be null. + TEST_EQ(root_v1_viewed_from_v2->e()->c(), NULL); + // 'TableC' was added to field 'c' union in version 2, so it should be null. + TEST_EQ(root_v1_viewed_from_v2->c_as_TableC(), NULL); + // The field 'c' union should be of type 'TableB' regardless of schema version + TEST_ASSERT(root_v1_viewed_from_v2->c_type() == Evolution::V2::Union::TableB); + // The field 'f' was renamed to 'ff' in version 2, it should still be readable. + TEST_EQ(root_v1_viewed_from_v2->ff()->a(), 16); + + // Test forwards compatibility by reading new data with an old schema. + auto root_v2_viewed_from_v1 = Evolution::V1::GetRoot(&binaries[1].front()); + // The field 'c' union in version 2 is a new table (index = 3) and should still be accessible, + // but not interpretable. + TEST_EQ(static_cast(root_v2_viewed_from_v1->c_type()), 3); + TEST_NOTNULL(root_v2_viewed_from_v1->c()); + // The field 'd' enum in verison 2 has new members and should still be accessible, but not interpretable. + TEST_EQ(static_cast(root_v2_viewed_from_v1->d()), 3); + // The field 'a' in version 2 is deprecated and should return the default value (0) instead of the value stored in + // the in the buffer (42). + TEST_EQ(root_v2_viewed_from_v1->a(), 0); + // The field 'ff' was originally named 'f' in version 1, it should still be readable. + TEST_EQ(root_v2_viewed_from_v1->f()->a(), 35); +#endif +} + void UnionVectorTest() { // load FlatBuffer fbs schema and json. std::string schemafile, jsonfile; @@ -3070,6 +3142,7 @@ int FlatBufferTests() { FixedLengthArrayJsonTest(true); ReflectionTest(flatbuf.data(), flatbuf.size()); ParseProtoTest(); + EvolutionTest(); UnionVectorTest(); LoadVerifyBinaryTest(); GenerateTableTextTest();