diff --git a/.gitignore b/.gitignore index 6f3894d06..e07fbae9a 100755 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,8 @@ flatsamplebinary flatsamplebinary.exe flatsampletext flatsampletext.exe +grpctest +grpctest.exe snapshot.sh tests/go_gen tests/monsterdata_java_wire.mon @@ -55,9 +57,10 @@ build/Xcode/FlatBuffers.xcodeproj/xcuserdata/** FlatBuffers.xcodeproj/ java/.idea java/*.iml -java/target -**/*.pyc .idea +*.iml +target +**/*.pyc build/VS2010/FlatBuffers.sdf build/VS2010/FlatBuffers.opensdf build/VS2010/ipch/**/*.ipch diff --git a/CMakeLists.txt b/CMakeLists.txt index c2dd95085..a72b62715 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,10 @@ project(FlatBuffers) option(FLATBUFFERS_CODE_COVERAGE "Enable the code coverage build option." OFF) option(FLATBUFFERS_BUILD_TESTS "Enable the build of tests and samples." ON) option(FLATBUFFERS_INSTALL "Enable the installation of targets." ON) -option(FLATBUFFERS_BUILD_FLATLIB "Enable the build of the flatbuffers library" ON) -option(FLATBUFFERS_BUILD_FLATC "Enable the build of the flatbuffers compiler" ON) +option(FLATBUFFERS_BUILD_FLATLIB "Enable the build of the flatbuffers library" + ON) +option(FLATBUFFERS_BUILD_FLATC "Enable the build of the flatbuffers compiler" + ON) option(FLATBUFFERS_BUILD_FLATHASH "Enable the build of flathash" ON) option(FLATBUFFERS_BUILD_GRPCTEST "Enable the build of grpctest" OFF) @@ -95,7 +97,8 @@ set(FlatBuffers_GRPCTest_SRCS if(APPLE) set(CMAKE_CXX_FLAGS - "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++ -Wall -pedantic -Werror -Wextra") + "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++ -Wall -pedantic -Werror \ + -Wextra") elseif(CMAKE_COMPILER_IS_GNUCXX) if(CYGWIN) set(CMAKE_CXX_FLAGS @@ -118,7 +121,8 @@ elseif(CMAKE_COMPILER_IS_GNUCXX) elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") set(CMAKE_CXX_FLAGS - "${CMAKE_CXX_FLAGS} -std=c++0x -stdlib=libc++ -Wall -pedantic -Werror -Wextra") + "${CMAKE_CXX_FLAGS} -std=c++0x -stdlib=libc++ -Wall -pedantic -Werror \ + -Wextra") if(NOT "${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lc++abi") @@ -165,7 +169,9 @@ function(compile_flatbuffers_schema_to_cpp SRC_FBS) string(REGEX REPLACE "\\.fbs$" "_generated.h" GEN_HEADER ${SRC_FBS}) add_custom_command( OUTPUT ${GEN_HEADER} - COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -c --no-includes --gen-mutable -o "${SRC_FBS_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}" + COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -c --no-includes --gen-mutable + --gen-object-api -o "${SRC_FBS_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}" DEPENDS flatc) endfunction() @@ -174,7 +180,8 @@ function(compile_flatbuffers_schema_to_binary SRC_FBS) string(REGEX REPLACE "\\.fbs$" ".bfbs" GEN_BINARY_SCHEMA ${SRC_FBS}) add_custom_command( OUTPUT ${GEN_BINARY_SCHEMA} - COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -b --schema -o "${SRC_FBS_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}" + COMMAND "${FLATBUFFERS_FLATC_EXECUTABLE}" -b --schema -o "${SRC_FBS_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FBS}" DEPENDS flatc) endfunction() diff --git a/CONTRIBUTING b/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING rename to CONTRIBUTING.md diff --git a/build_ide/VS2010/flatc.vcxproj b/build_ide/VS2010/flatc.vcxproj index 31cd01345..5aef2384a 100755 --- a/build_ide/VS2010/flatc.vcxproj +++ b/build_ide/VS2010/flatc.vcxproj @@ -81,7 +81,7 @@ - ../../include;%(AdditionalIncludeDirectories) + ../../include;../../grpc;%(AdditionalIncludeDirectories) EnableFastChecks CompileAsCpp ProgramDatabase @@ -127,7 +127,7 @@ - ../../include;%(AdditionalIncludeDirectories) + ../../include;../../grpc;%(AdditionalIncludeDirectories) EnableFastChecks CompileAsCpp ProgramDatabase @@ -173,7 +173,7 @@ - ../../include;%(AdditionalIncludeDirectories) + ../../include;../../grpc;%(AdditionalIncludeDirectories) CompileAsCpp Sync AnySuitable @@ -219,7 +219,7 @@ - ../../include;%(AdditionalIncludeDirectories) + ../../include;../../grpc;%(AdditionalIncludeDirectories) CompileAsCpp Sync AnySuitable @@ -263,6 +263,8 @@ + + diff --git a/build_ide/VS2010/flatc.vcxproj.user b/build_ide/VS2010/flatc.vcxproj.user index 5ef05f7e3..1132b8ab9 100755 --- a/build_ide/VS2010/flatc.vcxproj.user +++ b/build_ide/VS2010/flatc.vcxproj.user @@ -3,7 +3,7 @@ ..\..\tests WindowsLocalDebugger - -j -c -n -g --php --no-includes --gen-mutable monster_test.fbs + -j -c -n -g --php --no-includes --gen-mutable --gen-object-api monster_test.fbs ..\.. diff --git a/build_ide/Xcode/FlatBuffers.xcodeproj/project.pbxproj b/build_ide/Xcode/FlatBuffers.xcodeproj/project.pbxproj index ee18e9e29..e67f73565 100644 --- a/build_ide/Xcode/FlatBuffers.xcodeproj/project.pbxproj +++ b/build_ide/Xcode/FlatBuffers.xcodeproj/project.pbxproj @@ -11,10 +11,12 @@ 5AC48C391ACA9A0A008132C5 /* idl_gen_general.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD8717A19CB937D0012A827 /* idl_gen_general.cpp */; }; 61823BBC53544106B6DBC38E /* idl_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3709AC883348409592530AE6 /* idl_parser.cpp */; settings = {COMPILER_FLAGS = ""; }; }; 61FF3C34FBEC4819A1C30F92 /* sample_text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ECCEBFFA6977404F858F9739 /* sample_text.cpp */; settings = {COMPILER_FLAGS = ""; }; }; - 8C2AAE0A1CB338A8000CC78D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2AAE091CB338A8000CC78D /* util.cpp */; settings = {ASSET_TAGS = (); }; }; - 8C2AAE0B1CB338CD000CC78D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2AAE091CB338A8000CC78D /* util.cpp */; settings = {ASSET_TAGS = (); }; }; - 8C2AAE0C1CB338CE000CC78D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2AAE091CB338A8000CC78D /* util.cpp */; settings = {ASSET_TAGS = (); }; }; + 8C2AAE0A1CB338A8000CC78D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2AAE091CB338A8000CC78D /* util.cpp */; }; + 8C2AAE0B1CB338CD000CC78D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2AAE091CB338A8000CC78D /* util.cpp */; }; + 8C2AAE0C1CB338CE000CC78D /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C2AAE091CB338A8000CC78D /* util.cpp */; }; 8C303C591975D6A700D7C1C5 /* idl_gen_go.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C303C581975D6A700D7C1C5 /* idl_gen_go.cpp */; }; + 8C547D661D3FF05C00AE7A25 /* idl_gen_grpc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C547D651D3FF05C00AE7A25 /* idl_gen_grpc.cpp */; }; + 8C547D681D3FF07D00AE7A25 /* cpp_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8C547D671D3FF07D00AE7A25 /* cpp_generator.cc */; }; 8C6905FD19F835B400CB8866 /* idl_gen_fbs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C6905EC19F8357300CB8866 /* idl_gen_fbs.cpp */; }; 8C78573E1BD5AE2C00C53C34 /* idl_gen_js.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C78573D1BD5AE2C00C53C34 /* idl_gen_js.cpp */; }; 8C8774631B703D4800E693F5 /* reflection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C8774621B703D4800E693F5 /* reflection.cpp */; }; @@ -43,6 +45,8 @@ 6AD24EEB3D024825A37741FF /* test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = test.cpp; path = tests/test.cpp; sourceTree = SOURCE_ROOT; }; 8C2AAE091CB338A8000CC78D /* util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = util.cpp; path = src/util.cpp; sourceTree = ""; }; 8C303C581975D6A700D7C1C5 /* idl_gen_go.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_gen_go.cpp; path = src/idl_gen_go.cpp; sourceTree = ""; }; + 8C547D651D3FF05C00AE7A25 /* idl_gen_grpc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_gen_grpc.cpp; path = src/idl_gen_grpc.cpp; sourceTree = ""; }; + 8C547D671D3FF07D00AE7A25 /* cpp_generator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cpp_generator.cc; path = grpc/src/compiler/cpp_generator.cc; sourceTree = ""; }; 8C6905EC19F8357300CB8866 /* idl_gen_fbs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_gen_fbs.cpp; path = src/idl_gen_fbs.cpp; sourceTree = ""; }; 8C78573D1BD5AE2C00C53C34 /* idl_gen_js.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_gen_js.cpp; path = src/idl_gen_js.cpp; sourceTree = ""; }; 8C8774621B703D4800E693F5 /* reflection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = reflection.cpp; path = src/reflection.cpp; sourceTree = ""; }; @@ -62,6 +66,8 @@ 28237E300FE042DEADA302D3 /* Source Files */ = { isa = PBXGroup; children = ( + 8C547D671D3FF07D00AE7A25 /* cpp_generator.cc */, + 8C547D651D3FF05C00AE7A25 /* idl_gen_grpc.cpp */, 8C2AAE091CB338A8000CC78D /* util.cpp */, D2DA271C1BFFBC06000F9168 /* idl_gen_php.cpp */, 8C78573D1BD5AE2C00C53C34 /* idl_gen_js.cpp */, @@ -267,12 +273,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8C547D681D3FF07D00AE7A25 /* cpp_generator.cc in Sources */, 8C303C591975D6A700D7C1C5 /* idl_gen_go.cpp in Sources */, AA9BACF55EB3456BA2F633BB /* flatc.cpp in Sources */, BE03D7B0C9584DD58B50ED34 /* idl_gen_cpp.cpp in Sources */, AD71FEBEE4E846529002C1F0 /* idl_gen_text.cpp in Sources */, 8C2AAE0A1CB338A8000CC78D /* util.cpp in Sources */, 8C8774641B703E1200E693F5 /* idl_gen_fbs.cpp in Sources */, + 8C547D661D3FF05C00AE7A25 /* idl_gen_grpc.cpp in Sources */, A9C9A99F719A4ED58DC2D2FC /* idl_parser.cpp in Sources */, 8CA854B31B04244A00040A06 /* idl_gen_python.cpp in Sources */, 8CD8717B19CB937D0012A827 /* idl_gen_general.cpp in Sources */, @@ -715,7 +723,10 @@ GCC_OPTIMIZATION_LEVEL = 3; GCC_PREPROCESSOR_DEFINITIONS = "'CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\"'"; GCC_SYMBOLS_PRIVATE_EXTERN = NO; - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include"; + HEADER_SEARCH_PATHS = ( + "$(PROJECT_DIR)/include", + "$(PROJECT_DIR)/grpc", + ); INSTALL_PATH = ""; LIBRARY_SEARCH_PATHS = ""; OTHER_CFLAGS = ( @@ -754,7 +765,10 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = "'CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\"'"; GCC_SYMBOLS_PRIVATE_EXTERN = NO; - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include"; + HEADER_SEARCH_PATHS = ( + "$(PROJECT_DIR)/include", + "$(PROJECT_DIR)/grpc", + ); INSTALL_PATH = ""; LIBRARY_SEARCH_PATHS = ""; OTHER_CFLAGS = " -std=c++0x"; @@ -787,7 +801,10 @@ GCC_OPTIMIZATION_LEVEL = 2; GCC_PREPROCESSOR_DEFINITIONS = "'CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\"'"; GCC_SYMBOLS_PRIVATE_EXTERN = NO; - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include"; + HEADER_SEARCH_PATHS = ( + "$(PROJECT_DIR)/include", + "$(PROJECT_DIR)/grpc", + ); INSTALL_PATH = ""; LIBRARY_SEARCH_PATHS = ""; OTHER_CFLAGS = ( @@ -919,7 +936,10 @@ GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = "'CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\"'"; GCC_SYMBOLS_PRIVATE_EXTERN = NO; - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/include"; + HEADER_SEARCH_PATHS = ( + "$(PROJECT_DIR)/include", + "$(PROJECT_DIR)/grpc", + ); INSTALL_PATH = ""; LIBRARY_SEARCH_PATHS = ""; OTHER_CFLAGS = ( diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md index ad584c739..b71b8c46d 100755 --- a/docs/source/Compiler.md +++ b/docs/source/Compiler.md @@ -81,6 +81,11 @@ Additional options: - `--gen-mutable` : Generate additional non-const accessors for mutating FlatBuffers in-place. + `--gen-object-api` : Generate an additional object-based API. This API is + more convenient for object construction and mutation than the base API, + at the cost of efficiency (object allocation). Recommended only to be used + if other options are insufficient. + - `--gen-onefile` : Generate single output file (useful for C#) - `--gen-all`: Generate not just code for the current schema files, but @@ -103,5 +108,9 @@ Additional options: to the reflection/reflection.fbs schema. Loading this binary file is the basis for reflection functionality. +- `--conform FILE` : Specify a schema the following schemas should be + an evolution of. Gives errors if not. Useful to check if schema + modifications don't break schema evolution rules. + NOTE: short-form options for generators are deprecated, use the long form whenever possible. diff --git a/docs/source/CppUsage.md b/docs/source/CppUsage.md index d4abe66e4..060432b01 100755 --- a/docs/source/CppUsage.md +++ b/docs/source/CppUsage.md @@ -85,6 +85,27 @@ convenient accessors for all fields, e.g. `hp()`, `mana()`, etc: *Note: That we never stored a `mana` value, so it will return the default.* +## Object based API. + +FlatBuffers is all about memory efficiency, which is why its base API is written +around using as little as possible of it. This does make the API clumsier +(requiring pre-order construction of all data, and making mutation harder). + +For times when efficiency is less important a more convenient object based API +can be used (through `--gen-object-api`) that is able to unpack & pack a +FlatBuffer into objects and standard STL containers, allowing for convenient +construction, access and mutation. + +To use: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + auto monsterobj = GetMonster(buffer)->UnPack(); + cout << monsterobj->name; // This is now a std::string! + monsterobj->name = "Bob"; // Change the name. + FlatBufferBuilder fbb; + monsterobj->Pack(fbb); // Serialize into new buffer. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ## Reflection (& Resizing) There is experimental support for reflection in FlatBuffers, allowing you to diff --git a/docs/source/GoUsage.md b/docs/source/GoUsage.md index a3a0e928a..ab6ddbd82 100644 --- a/docs/source/GoUsage.md +++ b/docs/source/GoUsage.md @@ -67,6 +67,29 @@ Now you can access values like this: pos := monster.Pos(nil) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In some cases it's necessary to modify values in an existing FlatBuffer in place (without creating a copy). For this reason, scalar fields of a Flatbuffer table or struct can be mutated. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.go} + monster := example.GetRootAsMonster(buf, 0) + + // Set table field. + if ok := monster.MutateHp(10); !ok { + panic("failed to mutate Hp") + } + + // Set struct field. + monster.Pos().MutateZ(4) + + // This mutation will fail because the mana field is not available in + // the buffer. It should be set when creating the buffer. + if ok := monster.MutateMana(20); !ok { + panic("failed to mutate Hp") + } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The term `mutate` is used instead of `set` to indicate that this is a special use case. All mutate functions return a boolean value which is false if the field we're trying to mutate is not available in the buffer. + ## Text Parsing There currently is no support for parsing text (Schema's and JSON) directly diff --git a/docs/source/Tutorial.md b/docs/source/Tutorial.md index 6f6ac9a51..100191d7a 100644 --- a/docs/source/Tutorial.md +++ b/docs/source/Tutorial.md @@ -727,8 +727,8 @@ offsets. // Place the two weapons into an array, and pass it to the `createWeaponsVector()` method to // create a FlatBuffer vector. int[] weaps = new int[2]; - weaps[1] = sword; - weaps[2] = axe; + weaps[0] = sword; + weaps[1] = axe; // Pass the `weaps` array into the `createWeaponsVector()` method to create a FlatBuffer vector. int weapons = Monster.createWeaponsVector(builder, weaps); @@ -1881,6 +1881,9 @@ One way to solve this is to call `ForceDefaults` on a FlatBufferBuilder to force all fields you set to actually be written. This, of course, increases the size of the buffer somewhat, but this may be acceptable for a mutable buffer. +If this is not sufficient, other ways of mutating FlatBuffers may be supported +in your language through an object based API (`--gen-object-api`) or reflection. +See the individual language documents for support. ## JSON with FlatBuffers diff --git a/go/table.go b/go/table.go index 976a7dba1..01d3db122 100644 --- a/go/table.go +++ b/go/table.go @@ -293,3 +293,213 @@ func (t *Table) GetVOffsetTSlot(slot VOffsetT, d VOffsetT) VOffsetT { } return VOffsetT(off) } + +// MutateBool updates a bool at the given offset. +func (t *Table) MutateBool(off UOffsetT, n bool) bool { + WriteBool(t.Bytes[off:], n) + return true +} + +// MutateByte updates a Byte at the given offset. +func (t *Table) MutateByte(off UOffsetT, n byte) bool { + WriteByte(t.Bytes[off:], n) + return true +} + +// MutateUint8 updates a Uint8 at the given offset. +func (t *Table) MutateUint8(off UOffsetT, n uint8) bool { + WriteUint8(t.Bytes[off:], n) + return true +} + +// MutateUint16 updates a Uint16 at the given offset. +func (t *Table) MutateUint16(off UOffsetT, n uint16) bool { + WriteUint16(t.Bytes[off:], n) + return true +} + +// MutateUint32 updates a Uint32 at the given offset. +func (t *Table) MutateUint32(off UOffsetT, n uint32) bool { + WriteUint32(t.Bytes[off:], n) + return true +} + +// MutateUint64 updates a Uint64 at the given offset. +func (t *Table) MutateUint64(off UOffsetT, n uint64) bool { + WriteUint64(t.Bytes[off:], n) + return true +} + +// MutateInt8 updates a Int8 at the given offset. +func (t *Table) MutateInt8(off UOffsetT, n int8) bool { + WriteInt8(t.Bytes[off:], n) + return true +} + +// MutateInt16 updates a Int16 at the given offset. +func (t *Table) MutateInt16(off UOffsetT, n int16) bool { + WriteInt16(t.Bytes[off:], n) + return true +} + +// MutateInt32 updates a Int32 at the given offset. +func (t *Table) MutateInt32(off UOffsetT, n int32) bool { + WriteInt32(t.Bytes[off:], n) + return true +} + +// MutateInt64 updates a Int64 at the given offset. +func (t *Table) MutateInt64(off UOffsetT, n int64) bool { + WriteInt64(t.Bytes[off:], n) + return true +} + +// MutateFloat32 updates a Float32 at the given offset. +func (t *Table) MutateFloat32(off UOffsetT, n float32) bool { + WriteFloat32(t.Bytes[off:], n) + return true +} + +// MutateFloat64 updates a Float64 at the given offset. +func (t *Table) MutateFloat64(off UOffsetT, n float64) bool { + WriteFloat64(t.Bytes[off:], n) + return true +} + +// MutateUOffsetT updates a UOffsetT at the given offset. +func (t *Table) MutateUOffsetT(off UOffsetT, n UOffsetT) bool { + WriteUOffsetT(t.Bytes[off:], n) + return true +} + +// MutateVOffsetT updates a VOffsetT at the given offset. +func (t *Table) MutateVOffsetT(off UOffsetT, n VOffsetT) bool { + WriteVOffsetT(t.Bytes[off:], n) + return true +} + +// MutateSOffsetT updates a SOffsetT at the given offset. +func (t *Table) MutateSOffsetT(off UOffsetT, n SOffsetT) bool { + WriteSOffsetT(t.Bytes[off:], n) + return true +} + +// MutateBoolSlot updates the bool at given vtable location +func (t *Table) MutateBoolSlot(slot VOffsetT, n bool) bool { + if off := t.Offset(slot); off != 0 { + t.MutateBool(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateByteSlot updates the byte at given vtable location +func (t *Table) MutateByteSlot(slot VOffsetT, n byte) bool { + if off := t.Offset(slot); off != 0 { + t.MutateByte(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateInt8Slot updates the int8 at given vtable location +func (t *Table) MutateInt8Slot(slot VOffsetT, n int8) bool { + if off := t.Offset(slot); off != 0 { + t.MutateInt8(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateUint8Slot updates the uint8 at given vtable location +func (t *Table) MutateUint8Slot(slot VOffsetT, n uint8) bool { + if off := t.Offset(slot); off != 0 { + t.MutateUint8(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateInt16Slot updates the int16 at given vtable location +func (t *Table) MutateInt16Slot(slot VOffsetT, n int16) bool { + if off := t.Offset(slot); off != 0 { + t.MutateInt16(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateUint16Slot updates the uint16 at given vtable location +func (t *Table) MutateUint16Slot(slot VOffsetT, n uint16) bool { + if off := t.Offset(slot); off != 0 { + t.MutateUint16(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateInt32Slot updates the int32 at given vtable location +func (t *Table) MutateInt32Slot(slot VOffsetT, n int32) bool { + if off := t.Offset(slot); off != 0 { + t.MutateInt32(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateUint32Slot updates the uint32 at given vtable location +func (t *Table) MutateUint32Slot(slot VOffsetT, n uint32) bool { + if off := t.Offset(slot); off != 0 { + t.MutateUint32(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateInt64Slot updates the int64 at given vtable location +func (t *Table) MutateInt64Slot(slot VOffsetT, n int64) bool { + if off := t.Offset(slot); off != 0 { + t.MutateInt64(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateUint64Slot updates the uint64 at given vtable location +func (t *Table) MutateUint64Slot(slot VOffsetT, n uint64) bool { + if off := t.Offset(slot); off != 0 { + t.MutateUint64(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateFloat32Slot updates the float32 at given vtable location +func (t *Table) MutateFloat32Slot(slot VOffsetT, n float32) bool { + if off := t.Offset(slot); off != 0 { + t.MutateFloat32(t.Pos+UOffsetT(off), n) + return true + } + + return false +} + +// MutateFloat64Slot updates the float64 at given vtable location +func (t *Table) MutateFloat64Slot(slot VOffsetT, n float64) bool { + if off := t.Offset(slot); off != 0 { + t.MutateFloat64(t.Pos+UOffsetT(off), n) + return true + } + + return false +} diff --git a/include/flatbuffers/code_generators.h b/include/flatbuffers/code_generators.h index e7450a7a0..8b097ef7e 100644 --- a/include/flatbuffers/code_generators.h +++ b/include/flatbuffers/code_generators.h @@ -39,8 +39,8 @@ class BaseGenerator { protected: BaseGenerator(const Parser &parser, const std::string &path, - const std::string &file_name, - const std::string qualifying_start, + const std::string &file_name, + const std::string qualifying_start, const std::string qualifying_separator) : parser_(parser), path_(path), @@ -86,15 +86,18 @@ class BaseGenerator { const std::string LastNamespacePart(const Namespace &ns) { auto &namespaces = ns.components; - if (namespaces.size()) return *(namespaces.end() - 1); else return std::string(""); + if (namespaces.size()) + return *(namespaces.end() - 1); + else + return std::string(""); } // tracks the current namespace for early exit in WrapInNameSpace - // c++, java and csharp returns a different namespace from - // the following default (no early exit, always fully qualify), + // c++, java and csharp returns a different namespace from + // the following default (no early exit, always fully qualify), // which works for js and php virtual const Namespace *CurrentNameSpace() { return nullptr; } - + // Ensure that a type is prefixed with its namespace whenever it is used // outside of its namespace. std::string WrapInNameSpace(const Namespace *ns, const std::string &name) { diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index 6755ec279..8cab3cd36 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -24,13 +24,20 @@ #include #include #include +#include #include #include #include #include -#include #include +#ifdef _STLPORT_VERSION + #define FLATBUFFERS_CPP98_STL +#endif +#ifndef FLATBUFFERS_CPP98_STL + #include +#endif + /// @cond FLATBUFFERS_INTERNAL #if __cplusplus <= 199711L && \ (!defined(_MSC_VER) || _MSC_VER < 1600) && \ @@ -123,9 +130,11 @@ typedef uintmax_t largest_scalar_t; // In 32bits, this evaluates to 2GB - 1 #define FLATBUFFERS_MAX_BUFFER_SIZE ((1ULL << (sizeof(soffset_t) * 8 - 1)) - 1) +#ifndef FLATBUFFERS_CPP98_STL // Pointer to relinquished memory. typedef std::unique_ptr> unique_ptr_t; +#endif // Wrapper for uoffset_t to allow safe template specialization. template struct Offset { @@ -234,23 +243,19 @@ template struct IndirectHelper { // An STL compatible iterator implementation for Vector below, effectively // calling Get() for every element. -template -struct VectorIterator : public - std::iterator < std::input_iterator_tag, - typename std::conditional < bConst, - const typename IndirectHelper::return_type, - typename IndirectHelper::return_type > ::type, uoffset_t > { +template +struct VectorIterator + : public std::iterator { - typedef std::iterator::return_type, - typename IndirectHelper::return_type>::type, uoffset_t> super_type; + typedef std::iterator super_type; public: VectorIterator(const uint8_t *data, uoffset_t i) : data_(data + IndirectHelper::element_stride * i) {}; VectorIterator(const VectorIterator &other) : data_(other.data_) {} + #ifndef FLATBUFFERS_CPP98_STL VectorIterator(VectorIterator &&other) : data_(std::move(other.data_)) {} + #endif VectorIterator &operator=(const VectorIterator &other) { data_ = other.data_; @@ -288,7 +293,7 @@ public: } VectorIterator operator++(int) { - VectorIterator temp(data_); + VectorIterator temp(data_,0); data_ += IndirectHelper::element_stride; return temp; } @@ -301,8 +306,10 @@ private: // Vector::data() assumes the vector elements start after the length field. template class Vector { public: - typedef VectorIterator iterator; - typedef VectorIterator const_iterator; + typedef VectorIterator::mutable_return_type> + iterator; + typedef VectorIterator::return_type> + const_iterator; uoffset_t size() const { return EndianScalar(length_); } @@ -471,6 +478,7 @@ class vector_downward { cur_ = buf_ + reserved_; } + #ifndef FLATBUFFERS_CPP98_STL // Relinquish the pointer to the caller. unique_ptr_t release() { // Actually deallocate from the start of the allocated memory. @@ -486,6 +494,7 @@ class vector_downward { return retval; } + #endif size_t growth_policy(size_t bytes) { return (bytes / 2) & ~(sizeof(largest_scalar_t) - 1); @@ -562,6 +571,10 @@ inline voffset_t FieldIndexToOffset(voffset_t field_id) { inline size_t PaddingBytes(size_t buf_size, size_t scalar_size) { return ((~buf_size) + 1) & (scalar_size - 1); } + +template const T* data(const std::vector &v) { + return v.empty() ? nullptr : &v.front(); +} /// @endcond /// @addtogroup flatbuffers_cpp_api @@ -627,6 +640,7 @@ FLATBUFFERS_FINAL_CLASS /// @return Returns a `uint8_t` pointer to the unfinished buffer. uint8_t *GetCurrentBufferPointer() const { return buf_.data(); } + #ifndef FLATBUFFERS_CPP98_STL /// @brief Get the released pointer to the serialized buffer. /// @warning Do NOT attempt to use this FlatBufferBuilder afterwards! /// @return The `unique_ptr` returned has a special allocator that knows how @@ -637,6 +651,7 @@ FLATBUFFERS_FINAL_CLASS Finished(); return buf_.release(); } + #endif /// @cond FLATBUFFERS_INTERNAL void Finished() const { @@ -674,11 +689,13 @@ FLATBUFFERS_FINAL_CLASS void PopBytes(size_t amount) { buf_.pop(amount); } template void AssertScalarT() { + #ifndef FLATBUFFERS_CPP98_STL // The code assumes power of 2 sizes and endian-swap-ability. static_assert(std::is_scalar::value // The Offset type is essentially a scalar but fails is_scalar. || sizeof(T) == sizeof(Offset), "T must be a scalar type"); + #endif } // Write a single aligned scalar to the buffer @@ -876,7 +893,7 @@ FLATBUFFERS_FINAL_CLASS /// @param[in] str A const pointer to a `String` struct to add to the buffer. /// @return Returns the offset in the buffer where the string starts Offset CreateString(const String *str) { - return CreateString(str->c_str(), str->Length()); + return str ? CreateString(str->c_str(), str->Length()) : 0; } /// @brief Store a string in the buffer, which can contain any binary data. @@ -981,9 +998,21 @@ FLATBUFFERS_FINAL_CLASS /// @return Returns a typed `Offset` into the serialized data indicating /// where the vector is stored. template Offset> CreateVector(const std::vector &v) { - return CreateVector(v.data(), v.size()); + return CreateVector(data(v), v.size()); } + // vector may be implemented using a bit-set, so we can't access it as + // an array. Instead, read elements manually. + // Background: https://isocpp.org/blog/2012/11/on-vectorbool + Offset> CreateVector(const std::vector &v) { + StartVector(v.size(), sizeof(uint8_t)); + for (auto i = v.size(); i > 0; ) { + PushElement(static_cast(v[--i])); + } + return Offset>(EndVector(v.size())); + } + + #ifndef FLATBUFFERS_CPP98_STL /// @brief Serialize values returned by a function into a FlatBuffer `vector`. /// This is a convenience function that takes care of iteration for you. /// @tparam T The data type of the `std::vector` elements. @@ -995,8 +1024,9 @@ FLATBUFFERS_FINAL_CLASS const std::function &f) { std::vector elems(vector_size); for (size_t i = 0; i < vector_size; i++) elems[i] = f(i); - return CreateVector(elems.data(), elems.size()); + return CreateVector(elems); } + #endif /// @brief Serialize a `std::vector` into a FlatBuffer `vector`. /// This is a convenience function for a common case. @@ -1008,7 +1038,7 @@ FLATBUFFERS_FINAL_CLASS const std::vector &v) { std::vector> offsets(v.size()); for (size_t i = 0; i < v.size(); i++) offsets[i] = CreateString(v[i]); - return CreateVector(offsets.data(), offsets.size()); + return CreateVector(offsets); } /// @brief Serialize an array of structs into a FlatBuffer `vector`. @@ -1033,7 +1063,7 @@ FLATBUFFERS_FINAL_CLASS /// where the vector is stored. template Offset> CreateVectorOfStructs( const std::vector &v) { - return CreateVectorOfStructs(v.data(), v.size()); + return CreateVectorOfStructs(data(v), v.size()); } /// @cond FLATBUFFERS_INTERNAL @@ -1216,6 +1246,9 @@ class Verifier FLATBUFFERS_FINAL_CLASS { size_t _max_tables = 1000000) : buf_(buf), end_(buf + buf_len), depth_(0), max_depth_(_max_depth), num_tables_(0), max_tables_(_max_tables) + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + , upper_bound_(buf) + #endif {} // Central location where any verification failures register. @@ -1223,11 +1256,20 @@ class Verifier FLATBUFFERS_FINAL_CLASS { #ifdef FLATBUFFERS_DEBUG_VERIFICATION_FAILURE assert(ok); #endif + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + if (!ok) + upper_bound_ = buf_; + #endif return ok; } // Verify any range within the buffer. bool Verify(const void *elem, size_t elem_len) const { + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + auto upper_bound = reinterpret_cast(elem) + elem_len; + if (upper_bound_ < upper_bound) + upper_bound_ = upper_bound; + #endif return Check(elem_len <= (size_t) (end_ - buf_) && elem >= buf_ && elem <= end_ - elem_len); @@ -1302,11 +1344,20 @@ class Verifier FLATBUFFERS_FINAL_CLASS { } // Verify this whole buffer, starting with root type T. - template bool VerifyBuffer() { + template bool VerifyBuffer(const char *identifier) { + if (identifier && (size_t(end_ - buf_) < 2 * sizeof(flatbuffers::uoffset_t) || + !BufferHasIdentifier(buf_, identifier))) { + return false; + } + // Call T::Verify, which must be in the generated code for this type. return Verify(buf_) && reinterpret_cast(buf_ + ReadScalar(buf_))-> - Verify(*this); + Verify(*this) + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + && GetComputedSize() + #endif + ; } // Called at the start of a table to increase counters measuring data @@ -1325,6 +1376,16 @@ class Verifier FLATBUFFERS_FINAL_CLASS { return true; } + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + // Returns the message size in bytes + size_t GetComputedSize() const { + uintptr_t size = upper_bound_ - buf_; + // Align the size to uoffset_t + size = (size - 1 + sizeof(uoffset_t)) & ~(sizeof(uoffset_t) - 1); + return (buf_ + size > end_) ? 0 : size; + } + #endif + private: const uint8_t *buf_; const uint8_t *end_; @@ -1332,6 +1393,9 @@ class Verifier FLATBUFFERS_FINAL_CLASS { size_t max_depth_; size_t num_tables_; size_t max_tables_; + #ifdef FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE + mutable const uint8_t *upper_bound_; + #endif }; // Convenient way to bundle a buffer and its length, to pass it around @@ -1489,6 +1553,12 @@ class Table { uint8_t data_[1]; }; +// Base class for native objects (FlatBuffer data de-serialized into native +// C++ data structures). +// Contains no functionality, purely documentative. +struct NativeTable { +}; + // Helper function to test if a field is present, using any of the field // enums in the generated code. // `table` must be a generated table type. Since this is a template parameter, diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index dddfab25c..1f4501629 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -311,6 +311,14 @@ struct EnumDef : public Definition { Type underlying_type; }; +inline bool EqualByName(const Type &a, const Type &b) { + return a.base_type == b.base_type && a.element == b.element && + (a.struct_def == b.struct_def || + a.struct_def->name == b.struct_def->name) && + (a.enum_def == b.enum_def || + a.enum_def->name == b.enum_def->name); +} + struct RPCCall { std::string name; SymbolTable attributes; @@ -338,7 +346,8 @@ struct IDLOptions { bool skip_unexpected_fields_in_json; bool generate_name_strings; bool escape_proto_identifiers; - + bool generate_object_based_api; + // Possible options for the more general generator below. enum Language { kJava, kCSharp, kGo, kMAX }; @@ -358,6 +367,7 @@ struct IDLOptions { skip_unexpected_fields_in_json(false), generate_name_strings(false), escape_proto_identifiers(false), + generate_object_based_api(false), lang(IDLOptions::kJava) {} }; @@ -471,6 +481,10 @@ class Parser : public ParserState { // See reflection/reflection.fbs void Serialize(); + // Checks that the schema represented by this parser is a safe evolution + // of the schema provided. Returns non-empty error on any problems. + std::string ConformTo(const Parser &base); + FLATBUFFERS_CHECKED_ERROR CheckBitsFit(int64_t val, size_t bits); private: diff --git a/include/flatbuffers/reflection.h b/include/flatbuffers/reflection.h index 87091438a..ae331ce23 100644 --- a/include/flatbuffers/reflection.h +++ b/include/flatbuffers/reflection.h @@ -370,6 +370,7 @@ uint8_t *ResizeAnyVector(const reflection::Schema &schema, uoffset_t newsize, uoffset_t elem_size, std::vector *flatbuf, const reflection::Object *root_table = nullptr); +#ifndef FLATBUFFERS_CPP98_STL template void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val, const Vector *vec, std::vector *flatbuf, @@ -391,6 +392,7 @@ void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val, } } } +#endif // 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 diff --git a/include/flatbuffers/reflection_generated.h b/include/flatbuffers/reflection_generated.h index da1124939..eb981857b 100644 --- a/include/flatbuffers/reflection_generated.h +++ b/include/flatbuffers/reflection_generated.h @@ -478,12 +478,12 @@ inline flatbuffers::Offset CreateSchema(flatbuffers::FlatBufferBuilder & inline const reflection::Schema *GetSchema(const void *buf) { return flatbuffers::GetRoot(buf); } -inline bool VerifySchemaBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer(); } - inline const char *SchemaIdentifier() { return "BFBS"; } inline bool SchemaBufferHasIdentifier(const void *buf) { return flatbuffers::BufferHasIdentifier(buf, SchemaIdentifier()); } +inline bool VerifySchemaBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer(SchemaIdentifier()); } + inline const char *SchemaExtension() { return "bfbs"; } inline void FinishSchemaBuffer(flatbuffers::FlatBufferBuilder &fbb, flatbuffers::Offset root) { fbb.Finish(root, SchemaIdentifier()); } diff --git a/js/flatbuffers.js b/js/flatbuffers.js index 6c3d15cc8..511c9a216 100644 --- a/js/flatbuffers.js +++ b/js/flatbuffers.js @@ -2,6 +2,10 @@ /// @addtogroup flatbuffers_javascript_api /// @{ /// @cond FLATBUFFERS_INTERNAL +/** + * @const + * @namespace + */ var flatbuffers = {}; /** diff --git a/php/FlatbufferBuilder.php b/php/FlatbufferBuilder.php index 3738582e2..6f0ee4831 100644 --- a/php/FlatbufferBuilder.php +++ b/php/FlatbufferBuilder.php @@ -233,7 +233,7 @@ class FlatbufferBuilder public function putUint($x) { if ($x > PHP_INT_MAX) { - throw new \InvalidArgumentException("your platform can't handling uint correctly. use 64bit machine."); + throw new \InvalidArgumentException("your platform can't handle uint correctly. use 64bit machine."); } $this->bb->putUint($this->space -= 4, $x); @@ -245,7 +245,7 @@ class FlatbufferBuilder public function putLong($x) { if ($x > PHP_INT_MAX) { - throw new \InvalidArgumentException("your platform can't handling long correctly. use 64bit machine."); + throw new \InvalidArgumentException("Your platform can't handle long correctly. Use a 64bit machine."); } $this->bb->putLong($this->space -= 8, $x); @@ -257,7 +257,7 @@ class FlatbufferBuilder public function putUlong($x) { if ($x > PHP_INT_MAX) { - throw new \InvalidArgumentException("your platform can't handling ulong correctly. this is php limitations. please wait extension release."); + throw new \InvalidArgumentException("Your platform can't handle ulong correctly. This is a php limitation. Please wait for the extension release."); } $this->bb->putUlong($this->space -= 8, $x); diff --git a/java/pom.xml b/pom.xml similarity index 89% rename from java/pom.xml rename to pom.xml index dd92b9d55..8e32feec3 100644 --- a/java/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.google.flatbuffers flatbuffers-java 1.3.0-SNAPSHOT - jar + bundle FlatBuffers Java API Memory Efficient Serialization Library @@ -33,7 +33,7 @@ - ./ + java maven-compiler-plugin @@ -78,6 +78,12 @@ + + org.apache.felix + maven-bundle-plugin + 3.0.1 + true + diff --git a/readme.md b/readme.md index 15dd41b11..3a7f929ef 100755 --- a/readme.md +++ b/readme.md @@ -46,7 +46,7 @@ you would leave it in.
- [CONTRIBUTING]: http://github.com/google/flatbuffers/blob/master/CONTRIBUTING + [CONTRIBUTING]: http://github.com/google/flatbuffers/blob/master/CONTRIBUTING.md [`flatbuffers` tag]: https://stackoverflow.com/questions/tagged/flatbuffers [FlatBuffers Google Group]: https://groups.google.com/forum/#!forum/flatbuffers [FlatBuffers Issues Tracker]: http://github.com/google/flatbuffers/issues diff --git a/samples/monster_generated.h b/samples/monster_generated.h index 1a16126a5..636aa3771 100644 --- a/samples/monster_generated.h +++ b/samples/monster_generated.h @@ -11,8 +11,10 @@ namespace Sample { struct Vec3; struct Monster; +struct MonsterT; struct Weapon; +struct WeaponT; enum Color { Color_Red = 0, @@ -36,6 +38,21 @@ enum Equipment { Equipment_MAX = Equipment_Weapon }; +struct EquipmentUnion { + Equipment type; + + flatbuffers::NativeTable *table; + EquipmentUnion() : type(Equipment_NONE), table(nullptr) {} + EquipmentUnion(const EquipmentUnion &); + EquipmentUnion &operator=(const EquipmentUnion &); + ~EquipmentUnion(); + + static flatbuffers::NativeTable *UnPack(const void *union_obj, Equipment type); + flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb) const; + + WeaponT *AsWeapon() { return type == Equipment_Weapon ? reinterpret_cast(table) : nullptr; } +}; + inline const char **EnumNamesEquipment() { static const char *names[] = { "NONE", "Weapon", nullptr }; return names; @@ -52,6 +69,8 @@ MANUALLY_ALIGNED_STRUCT(4) Vec3 FLATBUFFERS_FINAL_CLASS { float z_; public: + Vec3() { memset(this, 0, sizeof(Vec3)); } + Vec3(const Vec3 &_o) { memcpy(this, &_o, sizeof(Vec3)); } Vec3(float _x, float _y, float _z) : x_(flatbuffers::EndianScalar(_x)), y_(flatbuffers::EndianScalar(_y)), z_(flatbuffers::EndianScalar(_z)) { } @@ -64,6 +83,17 @@ MANUALLY_ALIGNED_STRUCT(4) Vec3 FLATBUFFERS_FINAL_CLASS { }; STRUCT_END(Vec3, 12); +struct MonsterT : public flatbuffers::NativeTable { + std::unique_ptr pos; + int16_t mana; + int16_t hp; + std::string name; + std::vector inventory; + Color color; + std::vector> weapons; + EquipmentUnion equipped; +}; + struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_POS = 4, @@ -112,6 +142,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VerifyEquipment(verifier, equipped(), equipped_type()) && verifier.EndTable(); } + std::unique_ptr UnPack() const; }; struct MonsterBuilder { @@ -135,15 +166,15 @@ struct MonsterBuilder { }; inline flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, - const Vec3 *pos = 0, - int16_t mana = 150, - int16_t hp = 100, - flatbuffers::Offset name = 0, - flatbuffers::Offset> inventory = 0, - Color color = Color_Blue, - flatbuffers::Offset>> weapons = 0, - Equipment equipped_type = Equipment_NONE, - flatbuffers::Offset equipped = 0) { + const Vec3 *pos = 0, + int16_t mana = 150, + int16_t hp = 100, + flatbuffers::Offset name = 0, + flatbuffers::Offset> inventory = 0, + Color color = Color_Blue, + flatbuffers::Offset>> weapons = 0, + Equipment equipped_type = Equipment_NONE, + flatbuffers::Offset equipped = 0) { MonsterBuilder builder_(_fbb); builder_.add_equipped(equipped); builder_.add_weapons(weapons); @@ -157,6 +188,26 @@ inline flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder return builder_.Finish(); } +inline flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, + const Vec3 *pos = 0, + int16_t mana = 150, + int16_t hp = 100, + const char *name = nullptr, + const std::vector *inventory = nullptr, + Color color = Color_Blue, + const std::vector> *weapons = nullptr, + Equipment equipped_type = Equipment_NONE, + flatbuffers::Offset equipped = 0) { + return CreateMonster(_fbb, pos, mana, hp, name ? 0 : _fbb.CreateString(name), inventory ? 0 : _fbb.CreateVector(*inventory), color, weapons ? 0 : _fbb.CreateVector>(*weapons), equipped_type, equipped); +} + +inline flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o); + +struct WeaponT : public flatbuffers::NativeTable { + std::string name; + int16_t damage; +}; + struct Weapon FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_NAME = 4, @@ -173,6 +224,7 @@ struct Weapon FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VerifyField(verifier, VT_DAMAGE) && verifier.EndTable(); } + std::unique_ptr UnPack() const; }; struct WeaponBuilder { @@ -189,14 +241,62 @@ struct WeaponBuilder { }; inline flatbuffers::Offset CreateWeapon(flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset name = 0, - int16_t damage = 0) { + flatbuffers::Offset name = 0, + int16_t damage = 0) { WeaponBuilder builder_(_fbb); builder_.add_name(name); builder_.add_damage(damage); return builder_.Finish(); } +inline flatbuffers::Offset CreateWeapon(flatbuffers::FlatBufferBuilder &_fbb, + const char *name = nullptr, + int16_t damage = 0) { + return CreateWeapon(_fbb, name ? 0 : _fbb.CreateString(name), damage); +} + +inline flatbuffers::Offset CreateWeapon(flatbuffers::FlatBufferBuilder &_fbb, const WeaponT *_o); + +inline std::unique_ptr Monster::UnPack() const { + auto _o = new MonsterT(); + { auto _e = pos(); if (_e) _o->pos = std::unique_ptr(new Vec3(*_e)); }; + { auto _e = mana(); _o->mana = _e; }; + { auto _e = hp(); _o->hp = _e; }; + { auto _e = name(); if (_e) _o->name = _e->str(); }; + { auto _e = inventory(); if (_e) { for (size_t _i = 0; _i < _e->size(); _i++) { _o->inventory.push_back(_e->Get(_i)); } } }; + { auto _e = color(); _o->color = _e; }; + { auto _e = weapons(); if (_e) { for (size_t _i = 0; _i < _e->size(); _i++) { _o->weapons.push_back(_e->Get(_i)->UnPack()); } } }; + { auto _e = equipped_type(); _o->equipped.type = _e; }; + { auto _e = equipped(); if (_e) _o->equipped.table = EquipmentUnion::UnPack(_e, equipped_type()); }; + return std::unique_ptr(_o); +} + +inline flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o) { + return CreateMonster(_fbb, + _o->pos ? _o->pos.get() : 0, + _o->mana, + _o->hp, + _o->name.size() ? _fbb.CreateString(_o->name) : 0, + _o->inventory.size() ? _fbb.CreateVector(_o->inventory) : 0, + _o->color, + _o->weapons.size() ? _fbb.CreateVector>(_o->weapons.size(), [&](size_t i) { return CreateWeapon(_fbb, _o->weapons[i].get()); }) : 0, + _o->equipped.type, + _o->equipped.Pack(_fbb)); +} + +inline std::unique_ptr Weapon::UnPack() const { + auto _o = new WeaponT(); + { auto _e = name(); if (_e) _o->name = _e->str(); }; + { auto _e = damage(); _o->damage = _e; }; + return std::unique_ptr(_o); +} + +inline flatbuffers::Offset CreateWeapon(flatbuffers::FlatBufferBuilder &_fbb, const WeaponT *_o) { + return CreateWeapon(_fbb, + _o->name.size() ? _fbb.CreateString(_o->name) : 0, + _o->damage); +} + inline bool VerifyEquipment(flatbuffers::Verifier &verifier, const void *union_obj, Equipment type) { switch (type) { case Equipment_NONE: return true; @@ -205,11 +305,34 @@ inline bool VerifyEquipment(flatbuffers::Verifier &verifier, const void *union_o } } +inline flatbuffers::NativeTable *EquipmentUnion::UnPack(const void *union_obj, Equipment type) { + switch (type) { + case Equipment_NONE: return nullptr; + case Equipment_Weapon: return reinterpret_cast(union_obj)->UnPack().release(); + default: return nullptr; + } +} + +inline flatbuffers::Offset EquipmentUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb) const { + switch (type) { + case Equipment_NONE: return 0; + case Equipment_Weapon: return CreateWeapon(_fbb, reinterpret_cast(table)).Union(); + default: return 0; + } +} + +inline EquipmentUnion::~EquipmentUnion() { + switch (type) { + case Equipment_Weapon: delete reinterpret_cast(table); break; + default:; + } +} + inline const MyGame::Sample::Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot(buf); } inline Monster *GetMutableMonster(void *buf) { return flatbuffers::GetMutableRoot(buf); } -inline bool VerifyMonsterBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer(); } +inline bool VerifyMonsterBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer(nullptr); } inline void FinishMonsterBuffer(flatbuffers::FlatBufferBuilder &fbb, flatbuffers::Offset root) { fbb.Finish(root); } diff --git a/src/flatc.cpp b/src/flatc.cpp index d410718e3..a568bbb24 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -84,14 +84,14 @@ const Generator generators[] = { flatbuffers::CPPMakeRule }, }; -const char *program_name = nullptr; -flatbuffers::Parser *parser = nullptr; +const char *g_program_name = nullptr; +flatbuffers::Parser *g_parser = nullptr; static void Error(const std::string &err, bool usage, bool show_exe_name) { - if (show_exe_name) printf("%s: ", program_name); + if (show_exe_name) printf("%s: ", g_program_name); printf("%s\n", err.c_str()); if (usage) { - printf("usage: %s [OPTION]... FILE... [-- FILE...]\n", program_name); + printf("usage: %s [OPTION]... FILE... [-- FILE...]\n", g_program_name); for (size_t i = 0; i < sizeof(generators) / sizeof(generators[0]); ++i) printf(" %-12s %s %s.\n", generators[i].generator_opt_long, @@ -123,23 +123,39 @@ static void Error(const std::string &err, bool usage, bool show_exe_name) { " --gen-onefile Generate single output file for C#.\n" " --gen-name-strings Generate type name functions for C++.\n" " --escape-proto-ids Disable appending '_' in namespaces names.\n" + " --gen-object-api Generate an additional object-based API\n" " --raw-binary Allow binaries without file_indentifier to be read.\n" " This may crash flatc given a mismatched schema.\n" " --proto Input is a .proto, translate to .fbs.\n" " --schema Serialize schemas instead of JSON (use with -b)\n" + " --conform FILE Specify a schema the following schemas should be\n" + " an evolution of. Gives errors if not.\n" "FILEs may be schemas, or JSON files (conforming to preceding schema)\n" "FILEs after the -- must be binary flatbuffer format files.\n" "Output files are named using the base file name of the input,\n" "and written to the current directory or the path given by -o.\n" "example: %s -c -b schema1.fbs schema2.fbs data.json\n", - program_name); + g_program_name); } - if (parser) delete parser; + if (g_parser) delete g_parser; exit(1); } +static void ParseFile(flatbuffers::Parser &parser, const std::string &filename, + const std::string &contents, + std::vector &include_directories) { + auto local_include_directory = flatbuffers::StripFileName(filename); + include_directories.push_back(local_include_directory.c_str()); + include_directories.push_back(nullptr); + if (!parser.Parse(contents.c_str(), &include_directories[0], + filename.c_str())) + Error(parser.error_, false, false); + include_directories.pop_back(); + include_directories.pop_back(); +} + int main(int argc, const char *argv[]) { - program_name = argv[0]; + g_program_name = argv[0]; flatbuffers::IDLOptions opts; std::string output_path; const size_t num_generators = sizeof(generators) / sizeof(generators[0]); @@ -151,6 +167,7 @@ int main(int argc, const char *argv[]) { std::vector filenames; std::vector include_directories; size_t binary_files_from = std::numeric_limits::max(); + std::string conform_to_schema; for (int argi = 1; argi < argc; argi++) { std::string arg = argv[argi]; if (arg[0] == '-') { @@ -162,6 +179,9 @@ int main(int argc, const char *argv[]) { } else if(arg == "-I") { if (++argi >= argc) Error("missing path following" + arg, true); include_directories.push_back(argv[argi]); + } else if(arg == "--conform") { + if (++argi >= argc) Error("missing path following" + arg, true); + conform_to_schema = argv[argi]; } else if(arg == "--strict-json") { opts.strict_json = true; } else if(arg == "--no-js-exports") { @@ -179,6 +199,8 @@ int main(int argc, const char *argv[]) { opts.mutable_buffer = true; } else if(arg == "--gen-name-strings") { opts.generate_name_strings = true; + } else if(arg == "--gen-object-api") { + opts.generate_object_based_api = true; } else if(arg == "--gen-all") { opts.generate_all = true; opts.include_dependence_headers = false; @@ -227,12 +249,20 @@ int main(int argc, const char *argv[]) { if (opts.proto_mode) { if (any_generator) Error("cannot generate code directly from .proto files", true); - } else if (!any_generator) { + } else if (!any_generator && conform_to_schema.empty()) { Error("no options: specify at least one generator.", true); } + flatbuffers::Parser conform_parser; + if (!conform_to_schema.empty()) { + std::string contents; + if (!flatbuffers::LoadFile(conform_to_schema.c_str(), true, &contents)) + Error("unable to load schema: " + conform_to_schema); + ParseFile(conform_parser, conform_to_schema, contents, include_directories); + } + // Now process the files: - parser = new flatbuffers::Parser(opts); + g_parser = new flatbuffers::Parser(opts); for (auto file_it = filenames.begin(); file_it != filenames.end(); ++file_it) { @@ -243,8 +273,8 @@ int main(int argc, const char *argv[]) { bool is_binary = static_cast(file_it - filenames.begin()) >= binary_files_from; if (is_binary) { - parser->builder_.Clear(); - parser->builder_.PushFlatBuffer( + g_parser->builder_.Clear(); + g_parser->builder_.PushFlatBuffer( reinterpret_cast(contents.c_str()), contents.length()); if (!raw_binary) { @@ -253,17 +283,17 @@ int main(int argc, const char *argv[]) { // does not contain a file identifier. // We'd expect that typically any binary used as a file would have // such an identifier, so by default we require them to match. - if (!parser->file_identifier_.length()) { + if (!g_parser->file_identifier_.length()) { Error("current schema has no file_identifier: cannot test if \"" + *file_it + "\" matches the schema, use --raw-binary to read this file" " anyway."); } else if (!flatbuffers::BufferHasIdentifier(contents.c_str(), - parser->file_identifier_.c_str())) { + g_parser->file_identifier_.c_str())) { Error("binary \"" + *file_it + "\" does not have expected file_identifier \"" + - parser->file_identifier_ + + g_parser->file_identifier_ + "\", use --raw-binary to read this file anyway."); } } @@ -272,36 +302,34 @@ int main(int argc, const char *argv[]) { if (contents.length() != strlen(contents.c_str())) { Error("input file appears to be binary: " + *file_it, true); } - if (flatbuffers::GetExtension(*file_it) == "fbs") { + auto is_schema = flatbuffers::GetExtension(*file_it) == "fbs"; + if (is_schema) { // If we're processing multiple schemas, make sure to start each // one from scratch. If it depends on previous schemas it must do // so explicitly using an include. - delete parser; - parser = new flatbuffers::Parser(opts); + delete g_parser; + g_parser = new flatbuffers::Parser(opts); + } + ParseFile(*g_parser, *file_it, contents, include_directories); + if (is_schema && !conform_to_schema.empty()) { + auto err = g_parser->ConformTo(conform_parser); + if (!err.empty()) Error("schemas don\'t conform: " + err); } - auto local_include_directory = flatbuffers::StripFileName(*file_it); - include_directories.push_back(local_include_directory.c_str()); - include_directories.push_back(nullptr); - if (!parser->Parse(contents.c_str(), &include_directories[0], - file_it->c_str())) - Error(parser->error_, false, false); if (schema_binary) { - parser->Serialize(); - parser->file_extension_ = reflection::SchemaExtension(); + g_parser->Serialize(); + g_parser->file_extension_ = reflection::SchemaExtension(); } - include_directories.pop_back(); - include_directories.pop_back(); } std::string filebase = flatbuffers::StripPath( flatbuffers::StripExtension(*file_it)); for (size_t i = 0; i < num_generators; ++i) { - parser->opts.lang = generators[i].lang; + g_parser->opts.lang = generators[i].lang; if (generator_enabled[i]) { if (!print_make_rules) { flatbuffers::EnsureDirExists(output_path); - if (!generators[i].generate(*parser, output_path, filebase)) { + if (!generators[i].generate(*g_parser, output_path, filebase)) { Error(std::string("Unable to generate ") + generators[i].lang_name + " for " + @@ -309,7 +337,7 @@ int main(int argc, const char *argv[]) { } } else { std::string make_rule = generators[i].make_rule( - *parser, output_path, *file_it); + *g_parser, output_path, *file_it); if (!make_rule.empty()) printf("%s\n", flatbuffers::WordWrap( make_rule, 80, " ", " \\").c_str()); @@ -317,13 +345,13 @@ int main(int argc, const char *argv[]) { } } - if (opts.proto_mode) GenerateFBS(*parser, output_path, filebase); + if (opts.proto_mode) GenerateFBS(*g_parser, output_path, filebase); // We do not want to generate code for the definitions in this file // in any files coming up next. - parser->MarkGenerated(); + g_parser->MarkGenerated(); } - delete parser; + delete g_parser; return 0; } diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index c690240d6..ad20b36cb 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -24,9 +24,7 @@ namespace flatbuffers { struct IsAlnum { - bool operator()(char c) { - return !isalnum(c); - } + bool operator()(char c) { return !isalnum(c); } }; static std::string GeneratedFileName(const std::string &path, @@ -39,7 +37,8 @@ class CppGenerator : public BaseGenerator { public: CppGenerator(const Parser &parser, const std::string &path, const std::string &file_name) - : BaseGenerator(parser, path, file_name, "", "::"){}; + : BaseGenerator(parser, path, file_name, "", "::"), + cur_name_space_(nullptr){}; // Iterate through all definitions we haven't generate code for (enums, // structs, // and tables) and output them to a single file. @@ -95,7 +94,11 @@ class CppGenerator : public BaseGenerator { auto &struct_def = **it; if (!struct_def.generated) { SetNameSpace(struct_def.defined_namespace, &code); - code += "struct " + struct_def.name + ";\n\n"; + code += "struct " + struct_def.name + ";\n"; + if (parser_.opts.generate_object_based_api && !struct_def.fixed) { + code += "struct " + NativeName(struct_def.name) + ";\n"; + } + code += "\n"; } } @@ -126,6 +129,14 @@ class CppGenerator : public BaseGenerator { GenTable(struct_def, &code); } } + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + auto &struct_def = **it; + if (!struct_def.fixed && !struct_def.generated) { + SetNameSpace(struct_def.defined_namespace, &code); + GenTablePost(struct_def, &code); + } + } // Generate code for union verifiers. for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); @@ -133,7 +144,7 @@ class CppGenerator : public BaseGenerator { auto &enum_def = **it; if (enum_def.is_union && !enum_def.generated) { SetNameSpace(enum_def.defined_namespace, &code); - GenEnumPost(enum_def, &code); + GenUnionPost(enum_def, &code); } } @@ -157,14 +168,6 @@ class CppGenerator : public BaseGenerator { code += name + ">(buf); }\n\n"; } - // The root verifier: - code += "inline bool Verify"; - code += name; - code += - "Buffer(flatbuffers::Verifier &verifier) { " - "return verifier.VerifyBuffer<"; - code += cpp_qualified_name + ">(); }\n\n"; - if (parser_.file_identifier_.length()) { // Return the identifier code += "inline const char *" + name; @@ -178,6 +181,19 @@ class CppGenerator : public BaseGenerator { code += name + "Identifier()); }\n\n"; } + // The root verifier: + code += "inline bool Verify"; + code += name; + code += + "Buffer(flatbuffers::Verifier &verifier) { " + "return verifier.VerifyBuffer<"; + code += cpp_qualified_name + ">("; + if (parser_.file_identifier_.length()) + code += name + "Identifier()"; + else + code += "nullptr"; + code += "); }\n\n"; + if (parser_.file_extension_.length()) { // Return the extension code += "inline const char *" + name; @@ -206,7 +222,7 @@ class CppGenerator : public BaseGenerator { private: // This tracks the current namespace so we can insert namespace declarations. - const Namespace *cur_name_space_ = nullptr; + const Namespace *cur_name_space_; const Namespace *CurrentNameSpace() { return cur_name_space_; } @@ -225,9 +241,10 @@ class CppGenerator : public BaseGenerator { // Return a C++ type from the table in idl.h std::string GenTypeBasic(const Type &type, bool user_facing_type) { static const char *ctypename[] = { -#define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) #CTYPE, + #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE) \ + #CTYPE, FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) -#undef FLATBUFFERS_TD + #undef FLATBUFFERS_TD }; if (user_facing_type) { if (type.enum_def) return WrapInNameSpace(*type.enum_def); @@ -245,9 +262,8 @@ class CppGenerator : public BaseGenerator { case BASE_TYPE_VECTOR: return "flatbuffers::Vector<" + GenTypeWire(type.VectorType(), "", false) + ">"; - case BASE_TYPE_STRUCT: { + case BASE_TYPE_STRUCT: return WrapInNameSpace(*type.struct_def); - } case BASE_TYPE_UNION: // fall through default: @@ -257,30 +273,56 @@ class CppGenerator : public BaseGenerator { // Return a C++ type for any type (scalar/pointer) specifically for // building a flatbuffer. - std::string GenTypeWire(const Type &type, - const char *postfix, bool user_facing_type) { + std::string GenTypeWire(const Type &type, const char *postfix, + bool user_facing_type) { return IsScalar(type.base_type) ? GenTypeBasic(type, user_facing_type) + postfix - : IsStruct(type) - ? "const " + GenTypePointer(type) + " *" - : "flatbuffers::Offset<" + GenTypePointer(type) + - ">" + postfix; + : IsStruct(type) ? "const " + GenTypePointer(type) + " *" + : "flatbuffers::Offset<" + + GenTypePointer(type) + ">" + postfix; } // Return a C++ type for any type (scalar/pointer) that reflects its // serialized size. std::string GenTypeSize(const Type &type) { - return IsScalar(type.base_type) - ? GenTypeBasic(type, false) - : IsStruct(type) ? GenTypePointer(type) - : "flatbuffers::uoffset_t"; + return IsScalar(type.base_type) ? GenTypeBasic(type, false) + : IsStruct(type) ? GenTypePointer(type) + : "flatbuffers::uoffset_t"; + } + + // TODO(wvo): make this configurable. + std::string NativeName(const std::string &name) { return name + "T"; } + + std::string GenTypeNative(const Type &type, bool invector) { + switch (type.base_type) { + case BASE_TYPE_STRING: + return "std::string"; + case BASE_TYPE_VECTOR: + return "std::vector<" + GenTypeNative(type.VectorType(), true) + ">"; + case BASE_TYPE_STRUCT: + if (IsStruct(type)) { + if (invector) { + return WrapInNameSpace(*type.struct_def); + } else { + return "std::unique_ptr<" + + WrapInNameSpace(*type.struct_def) + ">"; + } + } else { + return "std::unique_ptr<" + + NativeName(WrapInNameSpace(*type.struct_def)) + ">"; + } + case BASE_TYPE_UNION: + return type.enum_def->name + "Union"; + default: + return GenTypeBasic(type, true); + } } // Return a C++ type for any type (scalar/pointer) specifically for // using a flatbuffer. - std::string GenTypeGet(const Type &type, - const char *afterbasic, const char *beforeptr, - const char *afterptr, bool user_facing_type) { + std::string GenTypeGet(const Type &type, const char *afterbasic, + const char *beforeptr, const char *afterptr, + bool user_facing_type) { return IsScalar(type.base_type) ? GenTypeBasic(type, user_facing_type) + afterbasic : beforeptr + GenTypePointer(type) + afterptr; @@ -309,12 +351,37 @@ class CppGenerator : public BaseGenerator { } } - std::string EnumSignature(EnumDef &enum_def) { + std::string UnionVerifySignature(EnumDef &enum_def) { return "inline bool Verify" + enum_def.name + - "(flatbuffers::Verifier &verifier, " + "const void *union_obj, " + + "(flatbuffers::Verifier &verifier, const void *union_obj, " + enum_def.name + " type)"; } + std::string UnionUnPackSignature(EnumDef &enum_def, bool inclass) { + return (inclass ? "static " : "") + + std::string("flatbuffers::NativeTable *") + + (inclass ? "" : enum_def.name + "Union::") + + "UnPack(const void *union_obj, " + enum_def.name + " type)"; + } + + std::string UnionPackSignature(EnumDef &enum_def, bool inclass) { + return "flatbuffers::Offset " + + (inclass ? "" : enum_def.name + "Union::") + + "Pack(flatbuffers::FlatBufferBuilder &_fbb) const"; + } + + std::string TableCreateSignature(StructDef &struct_def) { + return "inline flatbuffers::Offset<" + struct_def.name + "> Create" + + struct_def.name + + "(flatbuffers::FlatBufferBuilder &_fbb, const " + + NativeName(struct_def.name) + " *_o)"; + } + + std::string TableUnPackSignature(StructDef &struct_def, bool inclass) { + return "std::unique_ptr<" + NativeName(struct_def.name) + "> " + + (inclass ? "" : struct_def.name + "::") + "UnPack() const"; + } + // Generate an enum declaration and an enum string lookup table. void GenEnum(EnumDef &enum_def, std::string *code_ptr) { std::string &code = *code_ptr; @@ -356,6 +423,36 @@ class CppGenerator : public BaseGenerator { GenTypeBasic(enum_def.underlying_type, false) + ")\n"; code += "\n"; + if (parser_.opts.generate_object_based_api && enum_def.is_union) { + // Generate a union type + code += "struct " + enum_def.name + "Union {\n"; + code += " " + enum_def.name + " type;\n\n"; + code += " flatbuffers::NativeTable *table;\n"; + code += " " + enum_def.name + "Union() : type("; + code += GenEnumVal(enum_def, "NONE", parser_.opts); + code += "), table(nullptr) {}\n"; + code += " " + enum_def.name + "Union(const "; + code += enum_def.name + "Union &);\n"; + code += " " + enum_def.name + "Union &operator=(const "; + code += enum_def.name + "Union &);\n"; + code += " ~" + enum_def.name + "Union();\n\n"; + code += " " + UnionUnPackSignature(enum_def, true) + ";\n"; + code += " " + UnionPackSignature(enum_def, true) + ";\n\n"; + for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); + ++it) { + auto &ev = **it; + if (ev.value) { + auto native_name = NativeName(WrapInNameSpace(*ev.struct_def)); + code += " " + native_name + " *As"; + code += ev.name + "() { return type == "; + code += GetEnumVal(enum_def, ev, parser_.opts); + code += " ? reinterpret_cast<" + native_name; + code += " *>(table) : nullptr; }\n"; + } + } + code += "};\n\n"; + } + // Generate a generate string table for enum values. // Problem is, if values are very sparse that could generate really big // tables. Ideally in that case we generate a map lookup instead, but for @@ -381,38 +478,89 @@ class CppGenerator : public BaseGenerator { code += "()[static_cast(e)"; if (enum_def.vals.vec.front()->value) { code += " - static_cast("; - code += - GetEnumVal(enum_def, *enum_def.vals.vec.front(), parser_.opts) + ")"; + code += GetEnumVal(enum_def, *enum_def.vals.vec.front(), parser_.opts) + + ")"; } code += "]; }\n\n"; } if (enum_def.is_union) { - code += EnumSignature(enum_def) + ";\n\n"; + code += UnionVerifySignature(enum_def) + ";\n\n"; } } - void GenEnumPost(EnumDef &enum_def, std::string *code_ptr_post) { + void GenUnionPost(EnumDef &enum_def, std::string *code_ptr) { // Generate a verifier function for this union that can be called by the // table verifier functions. It uses a switch case to select a specific // verifier function to call, this should be safe even if the union type // has been corrupted, since the verifiers will simply fail when called // on the wrong type. - std::string &code_post = *code_ptr_post; - code_post += EnumSignature(enum_def) + " {\n switch (type) {\n"; + std::string &code = *code_ptr; + code += UnionVerifySignature(enum_def) + " {\n switch (type) {\n"; for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); ++it) { auto &ev = **it; - code_post += " case " + GetEnumVal(enum_def, ev, parser_.opts); + code += " case " + GetEnumVal(enum_def, ev, parser_.opts); if (!ev.value) { - code_post += ": return true;\n"; // "NONE" enum value. + code += ": return true;\n"; // "NONE" enum value. } else { - code_post += ": return verifier.VerifyTable(reinterpret_castname; + code += "(_fbb, reinterpret_cast(offset, defaultval); } GenComment(struct_def.doc_comment, code_ptr, nullptr); @@ -498,8 +683,7 @@ class CppGenerator : public BaseGenerator { if (!field.deprecated) { // Deprecated fields won't be accessible. auto is_scalar = IsScalar(field.value.type.base_type); GenComment(field.doc_comment, code_ptr, nullptr, " "); - code += " " + - GenTypeGet(field.value.type, " ", "const ", " *", true); + code += " " + GenTypeGet(field.value.type, " ", "const ", " *", true); code += field.name + "() const { return "; // Call a different accessor for pointers, that indirects. auto accessor = @@ -507,8 +691,8 @@ class CppGenerator : public BaseGenerator { ? "GetField<" : (IsStruct(field.value.type) ? "GetStruct<" : "GetPointer<"); auto offsetstr = GenFieldOffsetName(field); - auto call = accessor + GenTypeGet(field.value.type, "", - "const ", " *", false) + + auto call = accessor + + GenTypeGet(field.value.type, "", "const ", " *", false) + ">(" + offsetstr; // Default value as second arg for non-pointer types. if (IsScalar(field.value.type.base_type)) @@ -525,8 +709,7 @@ class CppGenerator : public BaseGenerator { code += GenUnderlyingCast(field, false, "_" + field.name); code += "); }\n"; } else { - auto type = - GenTypeGet(field.value.type, " ", "", " *", true); + auto type = GenTypeGet(field.value.type, " ", "", " *", true); code += " " + type + "mutable_" + field.name + "() { return "; code += GenUnderlyingCast(field, true, accessor + type + ">(" + offsetstr + ")"); @@ -627,7 +810,13 @@ class CppGenerator : public BaseGenerator { } code += prefix + "verifier.EndTable()"; code += ";\n }\n"; - code += "};\n\n"; + + if (parser_.opts.generate_object_based_api) { + // Generate the UnPack() pre declaration. + code += " " + TableUnPackSignature(struct_def, true) + ";\n"; + } + + code += "};\n\n"; // End of table. // Generate a builder struct, with methods of the form: // void add_name(type name) { fbb_.AddElement(offset, name, default); @@ -679,6 +868,7 @@ class CppGenerator : public BaseGenerator { // Generate a convenient CreateX function that uses the above builder // to create a table in one go. + bool gen_vector_pars = false; code += "inline flatbuffers::Offset<" + struct_def.name + "> Create"; code += struct_def.name; code += "(flatbuffers::FlatBufferBuilder &_fbb"; @@ -686,24 +876,11 @@ class CppGenerator : public BaseGenerator { it != struct_def.fields.vec.end(); ++it) { auto &field = **it; if (!field.deprecated) { - code += ",\n " + GenTypeWire(field.value.type, " ", true); - code += field.name + " = "; - if (field.value.type.enum_def && IsScalar(field.value.type.base_type)) { - auto ev = field.value.type.enum_def->ReverseLookup( - static_cast(StringToInt(field.value.constant.c_str())), - false); - if (ev) { - code += WrapInNameSpace( - field.value.type.enum_def->defined_namespace, - GetEnumVal(*field.value.type.enum_def, *ev, parser_.opts)); - } else { - code += GenUnderlyingCast(field, true, field.value.constant); - } - } else if (field.value.type.base_type == BASE_TYPE_BOOL) { - code += field.value.constant == "0" ? "false" : "true"; - } else { - code += GenDefaultConstant(field); + if (field.value.type.base_type == BASE_TYPE_STRING || + field.value.type.base_type == BASE_TYPE_VECTOR) { + gen_vector_pars = true; } + GenSimpleParam(code, field); } } code += ") {\n " + struct_def.name + "Builder builder_(_fbb);\n"; @@ -719,6 +896,212 @@ class CppGenerator : public BaseGenerator { } } code += " return builder_.Finish();\n}\n\n"; + + // Generate a CreateX function with vector types as parameters + if (gen_vector_pars) { + code += "inline flatbuffers::Offset<" + struct_def.name + "> Create"; + code += struct_def.name; + code += "(flatbuffers::FlatBufferBuilder &_fbb"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated) { + if (field.value.type.base_type == BASE_TYPE_STRING) { + code += ",\n const char *"; + code += field.name + " = nullptr"; + } else if (field.value.type.base_type == BASE_TYPE_VECTOR) { + code += ",\n const std::vector<"; + code += GenTypeWire(field.value.type.VectorType(), "", false); + code += "> *" + field.name + " = nullptr"; + } else { + GenSimpleParam(code, field); + } + } + } + code += ") {\n "; + code += "return Create"; + code += struct_def.name; + code += "(_fbb"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated) { + if (field.value.type.base_type == BASE_TYPE_STRING) { + code += ", " + field.name + " ? 0 : "; + code += "_fbb.CreateString(" + field.name + ")"; + } else if (field.value.type.base_type == BASE_TYPE_VECTOR) { + code += ", " + field.name + " ? 0 : "; + code += "_fbb.CreateVector<"; + code += GenTypeWire(field.value.type.VectorType(), "", false); + code += ">(*" + field.name + ")"; + } else + code += ", " + field.name; + } + } + code += ");\n}\n\n"; + } + + if (parser_.opts.generate_object_based_api) { + // Generate a pre-declaration for a CreateX method that works with an + // unpacked C++ object. + code += TableCreateSignature(struct_def) + ";\n\n"; + } + } + + // Generate code for tables that needs to come after the regular definition. + void GenTablePost(StructDef &struct_def, std::string *code_ptr) { + std::string &code = *code_ptr; + + if (parser_.opts.generate_object_based_api) { + // Generate the UnPack() method. + code += "inline " + TableUnPackSignature(struct_def, false) + " {\n"; + code += " auto _o = new " + NativeName(struct_def.name) + "();\n"; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated) { + auto prefix = " { auto _e = " + field.name + "(); "; + if (!IsScalar(field.value.type.base_type)) prefix += "if (_e) "; + auto deref = "_o->"; + auto dest = deref + field.name; + auto assign = prefix + dest + " = "; + auto gen_unpack_val = [&](const Type &type, const std::string &val, + bool invector) -> std::string { + switch (type.base_type) { + case BASE_TYPE_STRING: + return val + "->str()"; + case BASE_TYPE_STRUCT: + if (IsStruct(type)) { + if (invector) { + return "*" + val; + } else { + return "std::unique_ptr<" + type.struct_def->name + + ">(new " + type.struct_def->name + "(*" + val + "))"; + } + } else { + return val + "->UnPack()"; + } + default: + return val; + break; + } + }; + switch (field.value.type.base_type) { + case BASE_TYPE_VECTOR: { + code += prefix; + code += "{ for (flatbuffers::uoffset_t _i = 0;"; + code += " _i < _e->size(); _i++) { "; + code += dest + ".push_back("; + std::string indexing = "_e->Get(_i)"; + if (field.value.type.element == BASE_TYPE_BOOL) + indexing += "!=0"; + code += gen_unpack_val(field.value.type.VectorType(), + indexing, true); + code += "); } }"; + break; + } + case BASE_TYPE_UTYPE: { + auto &union_field = **(it + 1); + assert(union_field.value.type.base_type == BASE_TYPE_UNION); + code += prefix + deref + union_field.name + ".type = _e;"; + break; + } + case BASE_TYPE_UNION: + code += prefix + dest + ".table = "; + code += field.value.type.enum_def->name; + code += "Union::UnPack(_e, "; + code += field.name + UnionTypeFieldSuffix() + "());"; + break; + default: + code += assign + gen_unpack_val(field.value.type, "_e", false); + code += ";"; + break; + } + code += " };\n"; + } + } + code += " return std::unique_ptr<" + NativeName(struct_def.name); + code += ">(_o);\n}\n\n"; + + // Generate a CreateX method that works with an unpacked C++ object. + code += TableCreateSignature(struct_def) + " {\n"; + auto before_return_statement = code.size(); + code += " return Create"; + code += struct_def.name + "(_fbb"; + bool any_fields = false; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (!field.deprecated) { + any_fields = true; + auto field_name = field.name; + if (field.value.type.base_type == BASE_TYPE_UTYPE) { + field_name = field_name.substr(0, field_name.size() - + strlen(UnionTypeFieldSuffix())); + field_name += ".type"; + } + auto accessor = "_o->" + field_name; + auto ptrprefix = accessor + " ? "; + auto stlprefix = accessor + ".size() ? "; + auto postfix = " : 0"; + if (field.required && + (field.value.type.base_type == BASE_TYPE_STRING || + field.value.type.base_type == BASE_TYPE_VECTOR)) { + stlprefix = ""; + postfix = ""; + } + code += ",\n "; + switch (field.value.type.base_type) { + case BASE_TYPE_STRING: + code += stlprefix + "_fbb.CreateString(" + accessor + ")"; + code += postfix; + break; + case BASE_TYPE_VECTOR: { + auto vector_type = field.value.type.VectorType(); + code += stlprefix; + switch (vector_type.base_type) { + case BASE_TYPE_STRING: + code += "_fbb.CreateVectorOfStrings(" + accessor + ")"; + break; + case BASE_TYPE_STRUCT: + if (IsStruct(vector_type)) { + code += "_fbb.CreateVectorOfStructs(" + accessor + ")"; + } else { + code += "_fbb.CreateVectorname + ">>(" + accessor; + code += ".size(), [&](size_t i) { return Create"; + code += vector_type.struct_def->name + "(_fbb, " + accessor; + code += "[i].get()); })"; + } + break; + default: + code += "_fbb.CreateVector(" + accessor + ")"; + break; + } + code += postfix; + break; + } + case BASE_TYPE_UNION: + code += accessor + ".Pack(_fbb)"; + break; + case BASE_TYPE_STRUCT: + if (IsStruct(field.value.type)) { + code += ptrprefix + accessor + ".get()" + postfix; + } else { + code += ptrprefix + "Create"; + code += field.value.type.struct_def->name; + code += "(_fbb, " + accessor + ".get())" + postfix; + } + break; + default: + code += accessor; + break; + } + } + } + code += ");\n}\n\n"; + if (!any_fields) code.insert(before_return_statement, " (void)_o;\n"); + } } static void GenPadding(const FieldDef &field, std::string &code, @@ -775,6 +1158,15 @@ class CppGenerator : public BaseGenerator { code += "\n public:\n"; GenFullyQualifiedNameGetter(struct_def.name, code); + // Generate a default constructor. + code += " " + struct_def.name + "() { memset(this, 0, sizeof("; + code += struct_def.name + ")); }\n"; + + // Generate a copy constructor. + code += " " + struct_def.name + "(const " + struct_def.name; + code += " &_o) { memcpy(this, &_o, sizeof("; + code += struct_def.name + ")); }\n"; + // Generate a constructor that takes all fields as arguments. code += " " + struct_def.name + "("; for (auto it = struct_def.fields.vec.begin(); @@ -817,8 +1209,7 @@ class CppGenerator : public BaseGenerator { auto &field = **it; GenComment(field.doc_comment, code_ptr, nullptr, " "); auto is_scalar = IsScalar(field.value.type.base_type); - code += " " + - GenTypeGet(field.value.type, " ", "const ", " &", true); + code += " " + GenTypeGet(field.value.type, " ", "const ", " &", true); code += field.name + "() const { return "; code += GenUnderlyingCast( field, true, is_scalar @@ -878,6 +1269,7 @@ class CppGenerator : public BaseGenerator { cur_name_space_ = ns; } }; + } // namespace cpp bool GenerateCPP(const Parser &parser, const std::string &path, @@ -886,19 +1278,16 @@ bool GenerateCPP(const Parser &parser, const std::string &path, return generator.generate(); } -std::string CPPMakeRule(const Parser &parser, - const std::string &path, +std::string CPPMakeRule(const Parser &parser, const std::string &path, const std::string &file_name) { - std::string filebase = flatbuffers::StripPath( - flatbuffers::StripExtension(file_name)); + std::string filebase = + flatbuffers::StripPath(flatbuffers::StripExtension(file_name)); std::string make_rule = GeneratedFileName(path, filebase) + ": "; auto included_files = parser.GetIncludedFilesRecursive(file_name); - for (auto it = included_files.begin(); - it != included_files.end(); ++it) { + for (auto it = included_files.begin(); it != included_files.end(); ++it) { make_rule += " " + *it; } return make_rule; } } // namespace flatbuffers - diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index a96609c54..7e8e8c577 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -193,9 +193,11 @@ class GeneralGenerator : public BaseGenerator { public: GeneralGenerator(const Parser &parser, const std::string &path, const std::string &file_name) - : BaseGenerator(parser, path, file_name, "", "."){ - assert(parser_.opts.lang <= IDLOptions::kMAX); + : BaseGenerator(parser, path, file_name, "", "."), + lang_(language_parameters[parser_.opts.lang]) { + assert(parser_.opts.lang <= IDLOptions::kMAX); }; + GeneralGenerator &operator=(const GeneralGenerator &); bool generate() { std::string one_file_code; @@ -234,7 +236,7 @@ class GeneralGenerator : public BaseGenerator { // Save out the generated code for a single class while adding // declaration boilerplate. - bool SaveType(const std::string &defname, const Namespace &ns, + bool SaveType(const std::string &defname, const Namespace &ns, const std::string &classcode, bool needs_includes) { if (!classcode.length()) return true; @@ -544,7 +546,7 @@ void GenEnum(EnumDef &enum_def, std::string *code_ptr) { // "too sparse". Change at will. static const int kMaxSparseness = 5; if (range / static_cast(enum_def.vals.vec.size()) < kMaxSparseness) { - code += "\n private static"; + code += "\n public static"; code += lang_.const_decl; code += lang_.string_type; code += "[] names = { "; @@ -567,7 +569,10 @@ void GenEnum(EnumDef &enum_def, std::string *code_ptr) { } // Close the class - code += "};\n\n"; + code += "}"; + // Java does not need the closing semi-colon on class definitions. + code += (lang_.language != IDLOptions::kJava) ? ";" : ""; + code += "\n\n"; } // Returns the function name that is able to read a value of the given type. @@ -1125,9 +1130,12 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { code += "); }\n"; } } - code += "};\n\n"; -} - const LanguageParameters & lang_ = language_parameters[parser_.opts.lang]; + code += "}"; + // Java does not need the closing semi-colon on class definitions. + code += (lang_.language != IDLOptions::kJava) ? ";" : ""; + code += "\n\n"; +} + const LanguageParameters & lang_; }; } // namespace general diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp index a04b6ba20..1f6b103ea 100644 --- a/src/idl_gen_go.cpp +++ b/src/idl_gen_go.cpp @@ -439,7 +439,7 @@ static void GenReceiver(const StructDef &struct_def, std::string *code_ptr) { code += "func (rcv *" + struct_def.name + ")"; } -// Generate a struct field, conditioned on its child type(s). +// Generate a struct field getter, conditioned on its child type(s). static void GenStructAccessor(const StructDef &struct_def, const FieldDef &field, std::string *code_ptr) { @@ -486,6 +486,48 @@ static void GenStructAccessor(const StructDef &struct_def, } } +// Mutate the value of a struct's scalar. +static void MutateScalarFieldOfStruct(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string type = MakeCamel(GenTypeBasic(field.value.type)); + std::string setter = "rcv._tab.Mutate" + type; + GenReceiver(struct_def, code_ptr); + code += " Mutate" + MakeCamel(field.name); + code += "(n " + TypeName(field) + ") bool { return " + setter; + code += "(rcv._tab.Pos + flatbuffers.UOffsetT("; + code += NumToString(field.value.offset) + "), n) }\n\n"; +} + +// Mutate the value of a table's scalar. +static void MutateScalarFieldOfTable(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + std::string &code = *code_ptr; + std::string type = MakeCamel(GenTypeBasic(field.value.type)); + std::string setter = "rcv._tab.Mutate" + type + "Slot"; + GenReceiver(struct_def, code_ptr); + code += " Mutate" + MakeCamel(field.name); + code += "(n " + TypeName(field) + ") bool {\n\treturn "; + code += setter + "(" + NumToString(field.value.offset) + ", n)\n"; + code += "}\n\n"; +} + +// Generate a struct field setter, conditioned on its child type(s). +static void GenStructMutator(const StructDef &struct_def, + const FieldDef &field, + std::string *code_ptr) { + GenComment(field.doc_comment, code_ptr, nullptr, ""); + if (IsScalar(field.value.type.base_type)) { + if (struct_def.fixed) { + MutateScalarFieldOfStruct(struct_def, field, code_ptr); + } else { + MutateScalarFieldOfTable(struct_def, field, code_ptr); + } + } +} + // Generate table constructors, conditioned on its members' types. static void GenTableBuilders(const StructDef &struct_def, std::string *code_ptr) { @@ -509,13 +551,12 @@ static void GenTableBuilders(const StructDef &struct_def, // Generate struct or table methods. static void GenStruct(const StructDef &struct_def, - std::string *code_ptr, - StructDef *root_struct_def) { + std::string *code_ptr) { if (struct_def.generated) return; GenComment(struct_def.doc_comment, code_ptr, nullptr); BeginClass(struct_def, code_ptr); - if (&struct_def == root_struct_def) { + if (!struct_def.fixed) { // Generate a special accessor for the table that has been declared as // the root type. NewRootTypeFromBuffer(struct_def, code_ptr); @@ -530,6 +571,7 @@ static void GenStruct(const StructDef &struct_def, if (field.deprecated) continue; GenStructAccessor(struct_def, field, code_ptr); + GenStructMutator(struct_def, field, code_ptr); } if (struct_def.fixed) { @@ -638,7 +680,7 @@ class GoGenerator : public BaseGenerator { for (auto it = parser_.structs_.vec.begin(); it != parser_.structs_.vec.end(); ++it) { std::string declcode; - go::GenStruct(**it, &declcode, parser_.root_struct_def_); + go::GenStruct(**it, &declcode); if (!SaveType(**it, declcode, true)) return false; } @@ -681,4 +723,3 @@ bool GenerateGo(const Parser &parser, const std::string &path, } } // namespace flatbuffers - diff --git a/src/idl_gen_grpc.cpp b/src/idl_gen_grpc.cpp index 2bf0e911d..6ada3e873 100644 --- a/src/idl_gen_grpc.cpp +++ b/src/idl_gen_grpc.cpp @@ -137,6 +137,7 @@ class FlatBufFile : public grpc_cpp_generator::File { public: FlatBufFile(const Parser &parser, const std::string &file_name) : parser_(parser), file_name_(file_name) {} + FlatBufFile &operator=(const FlatBufFile &); std::string filename() const { return file_name_; } std::string filename_without_ext() const { diff --git a/src/idl_gen_js.cpp b/src/idl_gen_js.cpp index 32a06f1ef..d7c913e50 100644 --- a/src/idl_gen_js.cpp +++ b/src/idl_gen_js.cpp @@ -110,7 +110,7 @@ class JsGenerator : public BaseGenerator { std::string &exports = *exports_ptr; for (auto it = sorted_namespaces.begin(); it != sorted_namespaces.end(); it++) { - code += "/**\n * @const\n*/\n"; + code += "/**\n * @const\n * @namespace\n */\n"; if (it->find('.') == std::string::npos) { code += "var "; exports += "this." + *it + " = " + *it + ";\n"; diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp index 7e8dc0ebd..52944bfca 100644 --- a/src/idl_gen_python.cpp +++ b/src/idl_gen_python.cpp @@ -94,7 +94,7 @@ static void NewRootTypeFromBuffer(const StructDef &struct_def, code += Indent + Indent + "x = " + struct_def.name + "()\n"; code += Indent + Indent + "x.Init(buf, n + offset)\n"; code += Indent + Indent + "return x\n"; - code += "\n\n"; + code += "\n"; } // Initialize an existing object with other data, to avoid an allocation. @@ -478,13 +478,12 @@ static void GenTableBuilders(const StructDef &struct_def, // Generate struct or table methods. static void GenStruct(const StructDef &struct_def, - std::string *code_ptr, - StructDef *root_struct_def) { + std::string *code_ptr) { if (struct_def.generated) return; GenComment(struct_def.doc_comment, code_ptr, nullptr, "# "); BeginClass(struct_def, code_ptr); - if (&struct_def == root_struct_def) { + if (!struct_def.fixed) { // Generate a special accessor for the table that has been declared as // the root type. NewRootTypeFromBuffer(struct_def, code_ptr); @@ -621,7 +620,7 @@ class PythonGenerator : public BaseGenerator { it != parser_.structs_.vec.end(); ++it) { auto &struct_def = **it; std::string declcode; - GenStruct(struct_def, &declcode, parser_.root_struct_def_); + GenStruct(struct_def, &declcode); if (!SaveType(struct_def, declcode, true)) return false; } return true; diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index a5f325de4..fc2136757 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -2093,4 +2093,57 @@ flatbuffers::OffsetGetFullyQualifiedName(struct_def.name); + auto struct_def_base = base.structs_.Lookup(qualified_name); + if (!struct_def_base) continue; + for (auto fit = struct_def.fields.vec.begin(); + fit != struct_def.fields.vec.end(); ++fit) { + auto &field = **fit; + auto field_base = struct_def_base->fields.Lookup(field.name); + if (field_base) { + if (field.value.offset != field_base->value.offset) + return "offsets differ for field: " + field.name; + if (field.value.constant != field_base->value.constant) + return "defaults differ for field: " + field.name; + if (!EqualByName(field.value.type, field_base->value.type)) + return "types differ for field: " + field.name; + } else { + // Doesn't have to exist, deleting fields is fine. + // But we should check if there is a field that has the same offset + // but is incompatible (in the case of field renaming). + for (auto fbit = struct_def_base->fields.vec.begin(); + fbit != struct_def_base->fields.vec.end(); ++fbit) { + field_base = *fbit; + if (field.value.offset == field_base->value.offset) { + if (!EqualByName(field.value.type, field_base->value.type)) + return "field renamed to different type: " + field.name; + break; + } + } + } + } + } + for (auto eit = enums_.vec.begin(); eit != enums_.vec.end(); ++eit) { + auto &enum_def = **eit; + auto qualified_name = + enum_def.defined_namespace->GetFullyQualifiedName(enum_def.name); + auto enum_def_base = base.enums_.Lookup(qualified_name); + if (!enum_def_base) continue; + for (auto evit = enum_def.vals.vec.begin(); + evit != enum_def.vals.vec.end(); ++evit) { + auto &enum_val = **evit; + auto enum_val_base = enum_def_base->vals.Lookup(enum_val.name); + if (enum_val_base) { + if (enum_val.value != enum_val_base->value) + return "values differ for enum: " + enum_val.name; + } + } + } + return ""; +} + } // namespace flatbuffers diff --git a/tests/MyGame/Example/Any.java b/tests/MyGame/Example/Any.java index 25d74af55..6e4fb76ca 100644 --- a/tests/MyGame/Example/Any.java +++ b/tests/MyGame/Example/Any.java @@ -9,8 +9,8 @@ public final class Any { public static final byte TestSimpleTableWithEnum = 2; public static final byte MyGame_Example2_Monster = 3; - private static final String[] names = { "NONE", "Monster", "TestSimpleTableWithEnum", "MyGame_Example2_Monster", }; + public static final String[] names = { "NONE", "Monster", "TestSimpleTableWithEnum", "MyGame_Example2_Monster", }; public static String name(int e) { return names[e]; } -}; +} diff --git a/tests/MyGame/Example/Color.java b/tests/MyGame/Example/Color.java index 502ec9fbf..7c113b72f 100644 --- a/tests/MyGame/Example/Color.java +++ b/tests/MyGame/Example/Color.java @@ -8,8 +8,8 @@ public final class Color { public static final byte Green = 2; public static final byte Blue = 8; - private static final String[] names = { "Red", "Green", "", "", "", "", "", "Blue", }; + public static final String[] names = { "Red", "Green", "", "", "", "", "", "Blue", }; public static String name(int e) { return names[e - Red]; } -}; +} diff --git a/tests/MyGame/Example/Monster.go b/tests/MyGame/Example/Monster.go index 0c42f45a5..f4cd18c18 100644 --- a/tests/MyGame/Example/Monster.go +++ b/tests/MyGame/Example/Monster.go @@ -43,6 +43,10 @@ func (rcv *Monster) Mana() int16 { return 150 } +func (rcv *Monster) MutateMana(n int16) bool { + return rcv._tab.MutateInt16Slot(6, n) +} + func (rcv *Monster) Hp() int16 { o := flatbuffers.UOffsetT(rcv._tab.Offset(8)) if o != 0 { @@ -51,6 +55,10 @@ func (rcv *Monster) Hp() int16 { return 100 } +func (rcv *Monster) MutateHp(n int16) bool { + return rcv._tab.MutateInt16Slot(8, n) +} + func (rcv *Monster) Name() []byte { o := flatbuffers.UOffsetT(rcv._tab.Offset(10)) if o != 0 { @@ -92,6 +100,10 @@ func (rcv *Monster) Color() int8 { return 8 } +func (rcv *Monster) MutateColor(n int8) bool { + return rcv._tab.MutateInt8Slot(16, n) +} + func (rcv *Monster) TestType() byte { o := flatbuffers.UOffsetT(rcv._tab.Offset(18)) if o != 0 { @@ -100,6 +112,10 @@ func (rcv *Monster) TestType() byte { return 0 } +func (rcv *Monster) MutateTestType(n byte) bool { + return rcv._tab.MutateByteSlot(18, n) +} + func (rcv *Monster) Test(obj *flatbuffers.Table) bool { o := flatbuffers.UOffsetT(rcv._tab.Offset(20)) if o != 0 { @@ -173,6 +189,8 @@ func (rcv *Monster) TestarrayoftablesLength() int { return 0 } +/// an example documentation comment: this will end up in the generated code +/// multiline too func (rcv *Monster) Enemy(obj *Monster) *Monster { o := flatbuffers.UOffsetT(rcv._tab.Offset(28)) if o != 0 { @@ -232,6 +250,10 @@ func (rcv *Monster) Testbool() byte { return 0 } +func (rcv *Monster) MutateTestbool(n byte) bool { + return rcv._tab.MutateByteSlot(34, n) +} + func (rcv *Monster) Testhashs32Fnv1() int32 { o := flatbuffers.UOffsetT(rcv._tab.Offset(36)) if o != 0 { @@ -240,6 +262,10 @@ func (rcv *Monster) Testhashs32Fnv1() int32 { return 0 } +func (rcv *Monster) MutateTesthashs32Fnv1(n int32) bool { + return rcv._tab.MutateInt32Slot(36, n) +} + func (rcv *Monster) Testhashu32Fnv1() uint32 { o := flatbuffers.UOffsetT(rcv._tab.Offset(38)) if o != 0 { @@ -248,6 +274,10 @@ func (rcv *Monster) Testhashu32Fnv1() uint32 { return 0 } +func (rcv *Monster) MutateTesthashu32Fnv1(n uint32) bool { + return rcv._tab.MutateUint32Slot(38, n) +} + func (rcv *Monster) Testhashs64Fnv1() int64 { o := flatbuffers.UOffsetT(rcv._tab.Offset(40)) if o != 0 { @@ -256,6 +286,10 @@ func (rcv *Monster) Testhashs64Fnv1() int64 { return 0 } +func (rcv *Monster) MutateTesthashs64Fnv1(n int64) bool { + return rcv._tab.MutateInt64Slot(40, n) +} + func (rcv *Monster) Testhashu64Fnv1() uint64 { o := flatbuffers.UOffsetT(rcv._tab.Offset(42)) if o != 0 { @@ -264,6 +298,10 @@ func (rcv *Monster) Testhashu64Fnv1() uint64 { return 0 } +func (rcv *Monster) MutateTesthashu64Fnv1(n uint64) bool { + return rcv._tab.MutateUint64Slot(42, n) +} + func (rcv *Monster) Testhashs32Fnv1a() int32 { o := flatbuffers.UOffsetT(rcv._tab.Offset(44)) if o != 0 { @@ -272,6 +310,10 @@ func (rcv *Monster) Testhashs32Fnv1a() int32 { return 0 } +func (rcv *Monster) MutateTesthashs32Fnv1a(n int32) bool { + return rcv._tab.MutateInt32Slot(44, n) +} + func (rcv *Monster) Testhashu32Fnv1a() uint32 { o := flatbuffers.UOffsetT(rcv._tab.Offset(46)) if o != 0 { @@ -280,6 +322,10 @@ func (rcv *Monster) Testhashu32Fnv1a() uint32 { return 0 } +func (rcv *Monster) MutateTesthashu32Fnv1a(n uint32) bool { + return rcv._tab.MutateUint32Slot(46, n) +} + func (rcv *Monster) Testhashs64Fnv1a() int64 { o := flatbuffers.UOffsetT(rcv._tab.Offset(48)) if o != 0 { @@ -288,6 +334,10 @@ func (rcv *Monster) Testhashs64Fnv1a() int64 { return 0 } +func (rcv *Monster) MutateTesthashs64Fnv1a(n int64) bool { + return rcv._tab.MutateInt64Slot(48, n) +} + func (rcv *Monster) Testhashu64Fnv1a() uint64 { o := flatbuffers.UOffsetT(rcv._tab.Offset(50)) if o != 0 { @@ -296,6 +346,10 @@ func (rcv *Monster) Testhashu64Fnv1a() uint64 { return 0 } +func (rcv *Monster) MutateTesthashu64Fnv1a(n uint64) bool { + return rcv._tab.MutateUint64Slot(50, n) +} + func (rcv *Monster) Testarrayofbools(j int) byte { o := flatbuffers.UOffsetT(rcv._tab.Offset(52)) if o != 0 { @@ -321,6 +375,10 @@ func (rcv *Monster) Testf() float32 { return 3.14159 } +func (rcv *Monster) MutateTestf(n float32) bool { + return rcv._tab.MutateFloat32Slot(54, n) +} + func (rcv *Monster) Testf2() float32 { o := flatbuffers.UOffsetT(rcv._tab.Offset(56)) if o != 0 { @@ -329,6 +387,10 @@ func (rcv *Monster) Testf2() float32 { return 3.0 } +func (rcv *Monster) MutateTestf2(n float32) bool { + return rcv._tab.MutateFloat32Slot(56, n) +} + func (rcv *Monster) Testf3() float32 { o := flatbuffers.UOffsetT(rcv._tab.Offset(58)) if o != 0 { @@ -337,6 +399,10 @@ func (rcv *Monster) Testf3() float32 { return 0.0 } +func (rcv *Monster) MutateTestf3(n float32) bool { + return rcv._tab.MutateFloat32Slot(58, n) +} + func (rcv *Monster) Testarrayofstring2(j int) []byte { o := flatbuffers.UOffsetT(rcv._tab.Offset(60)) if o != 0 { diff --git a/tests/MyGame/Example/Monster.java b/tests/MyGame/Example/Monster.java index bfd4be1c9..dc27f8447 100644 --- a/tests/MyGame/Example/Monster.java +++ b/tests/MyGame/Example/Monster.java @@ -135,5 +135,5 @@ public final class Monster extends Table { return o; } public static void finishMonsterBuffer(FlatBufferBuilder builder, int offset) { builder.finish(offset, "MONS"); } -}; +} diff --git a/tests/MyGame/Example/Monster.py b/tests/MyGame/Example/Monster.py index 9d2909e89..a7a783633 100644 --- a/tests/MyGame/Example/Monster.py +++ b/tests/MyGame/Example/Monster.py @@ -15,7 +15,6 @@ class Monster(object): x.Init(buf, n + offset) return x - # Monster def Init(self, buf, pos): self._tab = flatbuffers.table.Table(buf, pos) diff --git a/tests/MyGame/Example/Stat.go b/tests/MyGame/Example/Stat.go index 7c33716b4..8c56bfb07 100644 --- a/tests/MyGame/Example/Stat.go +++ b/tests/MyGame/Example/Stat.go @@ -9,6 +9,13 @@ type Stat struct { _tab flatbuffers.Table } +func GetRootAsStat(buf []byte, offset flatbuffers.UOffsetT) *Stat { + n := flatbuffers.GetUOffsetT(buf[offset:]) + x := &Stat{} + x.Init(buf, n + offset) + return x +} + func (rcv *Stat) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i @@ -30,6 +37,10 @@ func (rcv *Stat) Val() int64 { return 0 } +func (rcv *Stat) MutateVal(n int64) bool { + return rcv._tab.MutateInt64Slot(6, n) +} + func (rcv *Stat) Count() uint16 { o := flatbuffers.UOffsetT(rcv._tab.Offset(8)) if o != 0 { @@ -38,6 +49,10 @@ func (rcv *Stat) Count() uint16 { return 0 } +func (rcv *Stat) MutateCount(n uint16) bool { + return rcv._tab.MutateUint16Slot(8, n) +} + func StatStart(builder *flatbuffers.Builder) { builder.StartObject(3) } func StatAddId(builder *flatbuffers.Builder, id flatbuffers.UOffsetT) { builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(id), 0) } func StatAddVal(builder *flatbuffers.Builder, val int64) { builder.PrependInt64Slot(1, val, 0) } diff --git a/tests/MyGame/Example/Stat.java b/tests/MyGame/Example/Stat.java index 3cac509b7..cd339c62d 100644 --- a/tests/MyGame/Example/Stat.java +++ b/tests/MyGame/Example/Stat.java @@ -39,5 +39,5 @@ public final class Stat extends Table { int o = builder.endObject(); return o; } -}; +} diff --git a/tests/MyGame/Example/Stat.py b/tests/MyGame/Example/Stat.py index 71577e868..b0e251d46 100644 --- a/tests/MyGame/Example/Stat.py +++ b/tests/MyGame/Example/Stat.py @@ -7,6 +7,13 @@ import flatbuffers class Stat(object): __slots__ = ['_tab'] + @classmethod + def GetRootAsStat(cls, buf, offset): + n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset) + x = Stat() + x.Init(buf, n + offset) + return x + # Stat def Init(self, buf, pos): self._tab = flatbuffers.table.Table(buf, pos) diff --git a/tests/MyGame/Example/Test.go b/tests/MyGame/Example/Test.go index e849488d9..ee0d9aaec 100644 --- a/tests/MyGame/Example/Test.go +++ b/tests/MyGame/Example/Test.go @@ -15,7 +15,11 @@ func (rcv *Test) Init(buf []byte, i flatbuffers.UOffsetT) { } func (rcv *Test) A() int16 { return rcv._tab.GetInt16(rcv._tab.Pos + flatbuffers.UOffsetT(0)) } +func (rcv *Test) MutateA(n int16) bool { return rcv._tab.MutateInt16(rcv._tab.Pos + flatbuffers.UOffsetT(0), n) } + func (rcv *Test) B() int8 { return rcv._tab.GetInt8(rcv._tab.Pos + flatbuffers.UOffsetT(2)) } +func (rcv *Test) MutateB(n int8) bool { return rcv._tab.MutateInt8(rcv._tab.Pos + flatbuffers.UOffsetT(2), n) } + func CreateTest(builder *flatbuffers.Builder, a int16, b int8) flatbuffers.UOffsetT { builder.Prep(2, 4) diff --git a/tests/MyGame/Example/Test.java b/tests/MyGame/Example/Test.java index 52474152f..6e33da9b3 100644 --- a/tests/MyGame/Example/Test.java +++ b/tests/MyGame/Example/Test.java @@ -23,5 +23,5 @@ public final class Test extends Struct { builder.putShort(a); return builder.offset(); } -}; +} diff --git a/tests/MyGame/Example/TestSimpleTableWithEnum.go b/tests/MyGame/Example/TestSimpleTableWithEnum.go index fe9cf2dbb..96218e58b 100644 --- a/tests/MyGame/Example/TestSimpleTableWithEnum.go +++ b/tests/MyGame/Example/TestSimpleTableWithEnum.go @@ -9,6 +9,13 @@ type TestSimpleTableWithEnum struct { _tab flatbuffers.Table } +func GetRootAsTestSimpleTableWithEnum(buf []byte, offset flatbuffers.UOffsetT) *TestSimpleTableWithEnum { + n := flatbuffers.GetUOffsetT(buf[offset:]) + x := &TestSimpleTableWithEnum{} + x.Init(buf, n + offset) + return x +} + func (rcv *TestSimpleTableWithEnum) Init(buf []byte, i flatbuffers.UOffsetT) { rcv._tab.Bytes = buf rcv._tab.Pos = i @@ -22,6 +29,10 @@ func (rcv *TestSimpleTableWithEnum) Color() int8 { return 2 } +func (rcv *TestSimpleTableWithEnum) MutateColor(n int8) bool { + return rcv._tab.MutateInt8Slot(4, n) +} + func TestSimpleTableWithEnumStart(builder *flatbuffers.Builder) { builder.StartObject(1) } func TestSimpleTableWithEnumAddColor(builder *flatbuffers.Builder, color int8) { builder.PrependInt8Slot(0, color, 2) } func TestSimpleTableWithEnumEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() } diff --git a/tests/MyGame/Example/TestSimpleTableWithEnum.java b/tests/MyGame/Example/TestSimpleTableWithEnum.java index 85e18c2b3..a1de020ea 100644 --- a/tests/MyGame/Example/TestSimpleTableWithEnum.java +++ b/tests/MyGame/Example/TestSimpleTableWithEnum.java @@ -29,5 +29,5 @@ public final class TestSimpleTableWithEnum extends Table { int o = builder.endObject(); return o; } -}; +} diff --git a/tests/MyGame/Example/TestSimpleTableWithEnum.py b/tests/MyGame/Example/TestSimpleTableWithEnum.py index 5b9509791..8d64e971d 100644 --- a/tests/MyGame/Example/TestSimpleTableWithEnum.py +++ b/tests/MyGame/Example/TestSimpleTableWithEnum.py @@ -7,6 +7,13 @@ import flatbuffers class TestSimpleTableWithEnum(object): __slots__ = ['_tab'] + @classmethod + def GetRootAsTestSimpleTableWithEnum(cls, buf, offset): + n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset) + x = TestSimpleTableWithEnum() + x.Init(buf, n + offset) + return x + # TestSimpleTableWithEnum def Init(self, buf, pos): self._tab = flatbuffers.table.Table(buf, pos) diff --git a/tests/MyGame/Example/Vec3.go b/tests/MyGame/Example/Vec3.go index 082945ed3..8bf97da32 100644 --- a/tests/MyGame/Example/Vec3.go +++ b/tests/MyGame/Example/Vec3.go @@ -15,10 +15,20 @@ func (rcv *Vec3) Init(buf []byte, i flatbuffers.UOffsetT) { } func (rcv *Vec3) X() float32 { return rcv._tab.GetFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(0)) } +func (rcv *Vec3) MutateX(n float32) bool { return rcv._tab.MutateFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(0), n) } + func (rcv *Vec3) Y() float32 { return rcv._tab.GetFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(4)) } +func (rcv *Vec3) MutateY(n float32) bool { return rcv._tab.MutateFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(4), n) } + func (rcv *Vec3) Z() float32 { return rcv._tab.GetFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(8)) } +func (rcv *Vec3) MutateZ(n float32) bool { return rcv._tab.MutateFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(8), n) } + func (rcv *Vec3) Test1() float64 { return rcv._tab.GetFloat64(rcv._tab.Pos + flatbuffers.UOffsetT(16)) } +func (rcv *Vec3) MutateTest1(n float64) bool { return rcv._tab.MutateFloat64(rcv._tab.Pos + flatbuffers.UOffsetT(16), n) } + func (rcv *Vec3) Test2() int8 { return rcv._tab.GetInt8(rcv._tab.Pos + flatbuffers.UOffsetT(24)) } +func (rcv *Vec3) MutateTest2(n int8) bool { return rcv._tab.MutateInt8(rcv._tab.Pos + flatbuffers.UOffsetT(24), n) } + func (rcv *Vec3) Test3(obj *Test) *Test { if obj == nil { obj = new(Test) diff --git a/tests/MyGame/Example/Vec3.java b/tests/MyGame/Example/Vec3.java index 7ae1ce209..261947cc3 100644 --- a/tests/MyGame/Example/Vec3.java +++ b/tests/MyGame/Example/Vec3.java @@ -40,5 +40,5 @@ public final class Vec3 extends Struct { builder.putFloat(x); return builder.offset(); } -}; +} diff --git a/tests/MyGame/Example2/Monster.java b/tests/MyGame/Example2/Monster.java index 6432494f3..968eee563 100644 --- a/tests/MyGame/Example2/Monster.java +++ b/tests/MyGame/Example2/Monster.java @@ -19,5 +19,5 @@ public final class Monster extends Table { int o = builder.endObject(); return o; } -}; +} diff --git a/tests/generate_code.bat b/tests/generate_code.bat index 53d54707b..4b4a53531 100644 --- a/tests/generate_code.bat +++ b/tests/generate_code.bat @@ -12,6 +12,6 @@ :: See the License for the specific language governing permissions and :: limitations under the License. -..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --no-includes monster_test.fbs monsterdata_test.json +..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --gen-object-api --no-includes monster_test.fbs monsterdata_test.json ..\flatc.exe --cpp --java --csharp --go --binary --python --js --php --gen-mutable -o namespace_test namespace_test\namespace_test1.fbs namespace_test\namespace_test2.fbs ..\flatc.exe --binary --schema monster_test.fbs diff --git a/tests/generate_code.sh b/tests/generate_code.sh index 3436d8586..1b347ba79 100644 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -../flatc --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --no-includes monster_test.fbs monsterdata_test.json +../flatc --cpp --java --csharp --go --binary --python --js --php --grpc --gen-mutable --gen-object-api --no-includes monster_test.fbs monsterdata_test.json ../flatc --cpp --java --csharp --go --binary --python --js --php --gen-mutable -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs ../flatc --binary --schema monster_test.fbs diff --git a/tests/go_test.go b/tests/go_test.go index 9d94277f7..e4e96de8c 100644 --- a/tests/go_test.go +++ b/tests/go_test.go @@ -67,6 +67,7 @@ func TestAll(t *testing.T) { // Verify that the Go FlatBuffers runtime library generates the // expected bytes (does not use any schema): CheckByteLayout(t.Fatalf) + CheckMutateMethods(t.Fatalf) // Verify that panics are raised during exceptional conditions: CheckNotInObjectError(t.Fatalf) @@ -75,6 +76,9 @@ func TestAll(t *testing.T) { CheckStructIsNotInlineError(t.Fatalf) CheckFinishedBytesError(t.Fatalf) + // Verify that GetRootAs works for non-root tables + CheckGetRootAsForNonRootTable(t.Fatalf) + // Verify that using the generated Go code builds a buffer without // returning errors: generated, off := CheckGeneratedBuild(t.Fatalf) @@ -82,6 +86,7 @@ func TestAll(t *testing.T) { // Verify that the buffer generated by Go code is readable by the // generated Go code: CheckReadBuffer(generated, off, t.Fatalf) + CheckMutateBuffer(generated, off, t.Fatalf) // Verify that the buffer generated by C++ code is readable by the // generated Go code: @@ -90,6 +95,7 @@ func TestAll(t *testing.T) { t.Fatal(err) } CheckReadBuffer(monsterDataCpp, 0, t.Fatalf) + CheckMutateBuffer(monsterDataCpp, 0, t.Fatalf) // Verify that vtables are deduplicated when written: CheckVtableDeduplication(t.Fatalf) @@ -280,6 +286,120 @@ func CheckReadBuffer(buf []byte, offset flatbuffers.UOffsetT, fail func(string, } } +// CheckMutateBuffer checks that the given buffer can be mutated correctly +// as the example Monster. Only available scalar values are mutated. +func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, fail func(string, ...interface{})) { + // make a copy to mutate + buf := make([]byte, len(org)) + copy(buf, org) + + // load monster data from the buffer + monster := example.GetRootAsMonster(buf, offset) + + // test case struct + type testcase struct { + field string + testfn func() bool + } + + testForOriginalValues := []testcase{ + testcase{"Hp", func() bool { return monster.Hp() == 80 }}, + testcase{"Mana", func() bool { return monster.Mana() == 150 }}, + testcase{"Pos.X'", func() bool { return monster.Pos(nil).X() == float32(1.0) }}, + testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(2.0) }}, + testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(3.0) }}, + testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(3.0) }}, + testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == int8(2) }}, + testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(5) }}, + testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(6) }}, + } + + testMutability := []testcase{ + testcase{"Hp", func() bool { return monster.MutateHp(70) }}, + testcase{"Mana", func() bool { return !monster.MutateMana(140) }}, + testcase{"Pos.X", func() bool { return monster.Pos(nil).MutateX(10.0) }}, + testcase{"Pos.Y", func() bool { return monster.Pos(nil).MutateY(20.0) }}, + testcase{"Pos.Z", func() bool { return monster.Pos(nil).MutateZ(30.0) }}, + testcase{"Pos.Test1", func() bool { return monster.Pos(nil).MutateTest1(30.0) }}, + testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(20) }}, + testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).MutateA(50) }}, + testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).MutateB(60) }}, + } + + testForMutatedValues := []testcase{ + testcase{"Hp", func() bool { return monster.Hp() == 70 }}, + testcase{"Mana", func() bool { return monster.Mana() == 150 }}, + testcase{"Pos.X'", func() bool { return monster.Pos(nil).X() == float32(10.0) }}, + testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(20.0) }}, + testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(30.0) }}, + testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(30.0) }}, + testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == int8(20) }}, + testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(50) }}, + testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(60) }}, + } + + // make sure original values are okay + for _, t := range testForOriginalValues { + if !t.testfn() { + fail("field '" + t.field + "' doesn't have the expected original value") + } + } + + // try to mutate fields and check mutability + for _, t := range testMutability { + if !t.testfn() { + fail(FailString("field '"+t.field+"' failed mutability test", true, false)) + } + } + + // test whether values have changed + for _, t := range testForMutatedValues { + if !t.testfn() { + fail("field '" + t.field + "' doesn't have the expected mutated value") + } + } + + // make sure the buffer has changed + if reflect.DeepEqual(buf, org) { + fail("mutate buffer failed") + } + + // To make sure the buffer has changed accordingly + // Read data from the buffer and verify all fields + monster = example.GetRootAsMonster(buf, offset) + for _, t := range testForMutatedValues { + if !t.testfn() { + fail("field '" + t.field + "' doesn't have the expected mutated value") + } + } + + // reverting all fields to original values should + // re-create the original buffer. Mutate all fields + // back to their original values and compare buffers. + // This test is done to make sure mutations do not do + // any unnecessary changes to the buffer. + monster = example.GetRootAsMonster(buf, offset) + monster.MutateHp(80) + monster.Pos(nil).MutateX(1.0) + monster.Pos(nil).MutateY(2.0) + monster.Pos(nil).MutateZ(3.0) + monster.Pos(nil).MutateTest1(3.0) + monster.Pos(nil).MutateTest2(2) + monster.Pos(nil).Test3(nil).MutateA(5) + monster.Pos(nil).Test3(nil).MutateB(6) + + for _, t := range testForOriginalValues { + if !t.testfn() { + fail("field '" + t.field + "' doesn't have the expected original value") + } + } + + // buffer should have original values + if !reflect.DeepEqual(buf, org) { + fail("revert changes failed") + } +} + // Low level stress/fuzz test: serialize/deserialize a variety of // different kinds of data in different combinations func checkFuzz(fuzzFields, fuzzObjects int, fail func(string, ...interface{})) { @@ -964,6 +1084,31 @@ func CheckManualBuild(fail func(string, ...interface{})) ([]byte, flatbuffers.UO return b.Bytes, b.Head() } +func CheckGetRootAsForNonRootTable(fail func(string, ...interface{})) { + b := flatbuffers.NewBuilder(0) + str := b.CreateString("MyStat") + example.StatStart(b) + example.StatAddId(b, str) + example.StatAddVal(b, 12345678) + example.StatAddCount(b, 12345) + stat_end := example.StatEnd(b) + b.Finish(stat_end) + + stat := example.GetRootAsStat(b.Bytes, b.Head()) + + if got := stat.Id(); !bytes.Equal([]byte("MyStat"), got) { + fail(FailString("stat.Id()", "MyStat", got)) + } + + if got := stat.Val(); 12345678 != got { + fail(FailString("stat.Val()", 12345678, got)) + } + + if got := stat.Count(); 12345 != got { + fail(FailString("stat.Count()", 12345, got)) + } +} + // CheckGeneratedBuild uses generated code to build the example Monster. func CheckGeneratedBuild(fail func(string, ...interface{})) ([]byte, flatbuffers.UOffsetT) { b := flatbuffers.NewBuilder(0) @@ -1248,6 +1393,151 @@ func CheckByteEquality(a, b []byte, fail func(string, ...interface{})) { } } +// CheckMutateMethods checks all mutate methods one by one +func CheckMutateMethods(fail func(string, ...interface{})) { + b := flatbuffers.NewBuilder(0) + b.StartObject(15) + b.PrependBoolSlot(0, true, false) + b.PrependByteSlot(1, 1, 0) + b.PrependUint8Slot(2, 2, 0) + b.PrependUint16Slot(3, 3, 0) + b.PrependUint32Slot(4, 4, 0) + b.PrependUint64Slot(5, 5, 0) + b.PrependInt8Slot(6, 6, 0) + b.PrependInt16Slot(7, 7, 0) + b.PrependInt32Slot(8, 8, 0) + b.PrependInt64Slot(9, 9, 0) + b.PrependFloat32Slot(10, 10, 0) + b.PrependFloat64Slot(11, 11, 0) + + b.PrependUOffsetTSlot(12, 12, 0) + uoVal := b.Offset() - 12 + + b.PrependVOffsetT(13) + b.Slot(13) + + b.PrependSOffsetT(14) + b.Slot(14) + soVal := flatbuffers.SOffsetT(b.Offset() - 14) + + offset := b.EndObject() + + t := &flatbuffers.Table{ + Bytes: b.Bytes, + Pos: flatbuffers.UOffsetT(len(b.Bytes)) - offset, + } + + calcVOffsetT := func(slot int) (vtableOffset flatbuffers.VOffsetT) { + return flatbuffers.VOffsetT((flatbuffers.VtableMetadataFields + slot) * flatbuffers.SizeVOffsetT) + } + calcUOffsetT := func(vtableOffset flatbuffers.VOffsetT) (valueOffset flatbuffers.UOffsetT) { + return t.Pos + flatbuffers.UOffsetT(t.Offset(vtableOffset)) + } + + type testcase struct { + field string + testfn func() bool + } + + testForOriginalValues := []testcase{ + testcase{"BoolSlot", func() bool { return t.GetBoolSlot(calcVOffsetT(0), true) == true }}, + testcase{"ByteSlot", func() bool { return t.GetByteSlot(calcVOffsetT(1), 1) == 1 }}, + testcase{"Uint8Slot", func() bool { return t.GetUint8Slot(calcVOffsetT(2), 2) == 2 }}, + testcase{"Uint16Slot", func() bool { return t.GetUint16Slot(calcVOffsetT(3), 3) == 3 }}, + testcase{"Uint32Slot", func() bool { return t.GetUint32Slot(calcVOffsetT(4), 4) == 4 }}, + testcase{"Uint64Slot", func() bool { return t.GetUint64Slot(calcVOffsetT(5), 5) == 5 }}, + testcase{"Int8Slot", func() bool { return t.GetInt8Slot(calcVOffsetT(6), 6) == 6 }}, + testcase{"Int16Slot", func() bool { return t.GetInt16Slot(calcVOffsetT(7), 7) == 7 }}, + testcase{"Int32Slot", func() bool { return t.GetInt32Slot(calcVOffsetT(8), 8) == 8 }}, + testcase{"Int64Slot", func() bool { return t.GetInt64Slot(calcVOffsetT(9), 9) == 9 }}, + testcase{"Float32Slot", func() bool { return t.GetFloat32Slot(calcVOffsetT(10), 10) == 10 }}, + testcase{"Float64Slot", func() bool { return t.GetFloat64Slot(calcVOffsetT(11), 11) == 11 }}, + testcase{"UOffsetTSlot", func() bool { return t.GetUOffsetT(calcUOffsetT(calcVOffsetT(12))) == uoVal }}, + testcase{"VOffsetTSlot", func() bool { return t.GetVOffsetT(calcUOffsetT(calcVOffsetT(13))) == 13 }}, + testcase{"SOffsetTSlot", func() bool { return t.GetSOffsetT(calcUOffsetT(calcVOffsetT(14))) == soVal }}, + } + + testMutability := []testcase{ + testcase{"BoolSlot", func() bool { return t.MutateBoolSlot(calcVOffsetT(0), false) }}, + testcase{"ByteSlot", func() bool { return t.MutateByteSlot(calcVOffsetT(1), 2) }}, + testcase{"Uint8Slot", func() bool { return t.MutateUint8Slot(calcVOffsetT(2), 4) }}, + testcase{"Uint16Slot", func() bool { return t.MutateUint16Slot(calcVOffsetT(3), 6) }}, + testcase{"Uint32Slot", func() bool { return t.MutateUint32Slot(calcVOffsetT(4), 8) }}, + testcase{"Uint64Slot", func() bool { return t.MutateUint64Slot(calcVOffsetT(5), 10) }}, + testcase{"Int8Slot", func() bool { return t.MutateInt8Slot(calcVOffsetT(6), 12) }}, + testcase{"Int16Slot", func() bool { return t.MutateInt16Slot(calcVOffsetT(7), 14) }}, + testcase{"Int32Slot", func() bool { return t.MutateInt32Slot(calcVOffsetT(8), 16) }}, + testcase{"Int64Slot", func() bool { return t.MutateInt64Slot(calcVOffsetT(9), 18) }}, + testcase{"Float32Slot", func() bool { return t.MutateFloat32Slot(calcVOffsetT(10), 20) }}, + testcase{"Float64Slot", func() bool { return t.MutateFloat64Slot(calcVOffsetT(11), 22) }}, + testcase{"UOffsetTSlot", func() bool { return t.MutateUOffsetT(calcUOffsetT(calcVOffsetT(12)), 24) }}, + testcase{"VOffsetTSlot", func() bool { return t.MutateVOffsetT(calcUOffsetT(calcVOffsetT(13)), 26) }}, + testcase{"SOffsetTSlot", func() bool { return t.MutateSOffsetT(calcUOffsetT(calcVOffsetT(14)), 28) }}, + } + + testMutabilityWithoutSlot := []testcase{ + testcase{"BoolSlot", func() bool { return t.MutateBoolSlot(calcVOffsetT(16), false) }}, + testcase{"ByteSlot", func() bool { return t.MutateByteSlot(calcVOffsetT(16), 2) }}, + testcase{"Uint8Slot", func() bool { return t.MutateUint8Slot(calcVOffsetT(16), 2) }}, + testcase{"Uint16Slot", func() bool { return t.MutateUint16Slot(calcVOffsetT(16), 2) }}, + testcase{"Uint32Slot", func() bool { return t.MutateUint32Slot(calcVOffsetT(16), 2) }}, + testcase{"Uint64Slot", func() bool { return t.MutateUint64Slot(calcVOffsetT(16), 2) }}, + testcase{"Int8Slot", func() bool { return t.MutateInt8Slot(calcVOffsetT(16), 2) }}, + testcase{"Int16Slot", func() bool { return t.MutateInt16Slot(calcVOffsetT(16), 2) }}, + testcase{"Int32Slot", func() bool { return t.MutateInt32Slot(calcVOffsetT(16), 2) }}, + testcase{"Int64Slot", func() bool { return t.MutateInt64Slot(calcVOffsetT(16), 2) }}, + testcase{"Float32Slot", func() bool { return t.MutateFloat32Slot(calcVOffsetT(16), 2) }}, + testcase{"Float64Slot", func() bool { return t.MutateFloat64Slot(calcVOffsetT(16), 2) }}, + } + + testForMutatedValues := []testcase{ + testcase{"BoolSlot", func() bool { return t.GetBoolSlot(calcVOffsetT(0), true) == false }}, + testcase{"ByteSlot", func() bool { return t.GetByteSlot(calcVOffsetT(1), 1) == 2 }}, + testcase{"Uint8Slot", func() bool { return t.GetUint8Slot(calcVOffsetT(2), 1) == 4 }}, + testcase{"Uint16Slot", func() bool { return t.GetUint16Slot(calcVOffsetT(3), 1) == 6 }}, + testcase{"Uint32Slot", func() bool { return t.GetUint32Slot(calcVOffsetT(4), 1) == 8 }}, + testcase{"Uint64Slot", func() bool { return t.GetUint64Slot(calcVOffsetT(5), 1) == 10 }}, + testcase{"Int8Slot", func() bool { return t.GetInt8Slot(calcVOffsetT(6), 1) == 12 }}, + testcase{"Int16Slot", func() bool { return t.GetInt16Slot(calcVOffsetT(7), 1) == 14 }}, + testcase{"Int32Slot", func() bool { return t.GetInt32Slot(calcVOffsetT(8), 1) == 16 }}, + testcase{"Int64Slot", func() bool { return t.GetInt64Slot(calcVOffsetT(9), 1) == 18 }}, + testcase{"Float32Slot", func() bool { return t.GetFloat32Slot(calcVOffsetT(10), 1) == 20 }}, + testcase{"Float64Slot", func() bool { return t.GetFloat64Slot(calcVOffsetT(11), 1) == 22 }}, + testcase{"UOffsetTSlot", func() bool { return t.GetUOffsetT(calcUOffsetT(calcVOffsetT(12))) == 24 }}, + testcase{"VOffsetTSlot", func() bool { return t.GetVOffsetT(calcUOffsetT(calcVOffsetT(13))) == 26 }}, + testcase{"SOffsetTSlot", func() bool { return t.GetSOffsetT(calcUOffsetT(calcVOffsetT(14))) == 28 }}, + } + + // make sure original values are okay + for _, t := range testForOriginalValues { + if !t.testfn() { + fail(t.field + "' field doesn't have the expected original value") + } + } + + // try to mutate fields and check mutability + for _, t := range testMutability { + if !t.testfn() { + fail(FailString(t.field+"' field failed mutability test", "passed", "failed")) + } + } + + // try to mutate fields and check mutability + // these have wrong slots so should fail + for _, t := range testMutabilityWithoutSlot { + if t.testfn() { + fail(FailString(t.field+"' field failed no slot mutability test", "failed", "passed")) + } + } + + // test whether values have changed + for _, t := range testForMutatedValues { + if !t.testfn() { + fail(t.field + "' field doesn't have the expected mutated value") + } + } +} + // BenchmarkVtableDeduplication measures the speed of vtable deduplication // by creating prePop vtables, then populating b.N objects with a // different single vtable. diff --git a/tests/monster_test.bfbs b/tests/monster_test.bfbs index e131ac8c2..1e8a270d8 100644 Binary files a/tests/monster_test.bfbs and b/tests/monster_test.bfbs differ diff --git a/tests/monster_test_generated.h b/tests/monster_test_generated.h index 50fbc28ce..49aba1133 100644 --- a/tests/monster_test_generated.h +++ b/tests/monster_test_generated.h @@ -9,6 +9,7 @@ namespace MyGame { namespace Example2 { struct Monster; +struct MonsterT; } // namespace Example2 @@ -17,12 +18,15 @@ namespace Example { struct Test; struct TestSimpleTableWithEnum; +struct TestSimpleTableWithEnumT; struct Vec3; struct Stat; +struct StatT; struct Monster; +struct MonsterT; enum Color { Color_Red = 1, @@ -48,6 +52,23 @@ enum Any { Any_MAX = Any_MyGame_Example2_Monster }; +struct AnyUnion { + Any type; + + flatbuffers::NativeTable *table; + AnyUnion() : type(Any_NONE), table(nullptr) {} + AnyUnion(const AnyUnion &); + AnyUnion &operator=(const AnyUnion &); + ~AnyUnion(); + + static flatbuffers::NativeTable *UnPack(const void *union_obj, Any type); + flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb) const; + + MonsterT *AsMonster() { return type == Any_Monster ? reinterpret_cast(table) : nullptr; } + TestSimpleTableWithEnumT *AsTestSimpleTableWithEnum() { return type == Any_TestSimpleTableWithEnum ? reinterpret_cast(table) : nullptr; } + MyGame::Example2::MonsterT *AsMyGame_Example2_Monster() { return type == Any_MyGame_Example2_Monster ? reinterpret_cast(table) : nullptr; } +}; + inline const char **EnumNamesAny() { static const char *names[] = { "NONE", "Monster", "TestSimpleTableWithEnum", "MyGame_Example2_Monster", nullptr }; return names; @@ -64,6 +85,8 @@ MANUALLY_ALIGNED_STRUCT(2) Test FLATBUFFERS_FINAL_CLASS { int8_t __padding0; public: + Test() { memset(this, 0, sizeof(Test)); } + Test(const Test &_o) { memcpy(this, &_o, sizeof(Test)); } Test(int16_t _a, int8_t _b) : a_(flatbuffers::EndianScalar(_a)), b_(flatbuffers::EndianScalar(_b)), __padding0(0) { (void)__padding0; } @@ -87,6 +110,8 @@ MANUALLY_ALIGNED_STRUCT(16) Vec3 FLATBUFFERS_FINAL_CLASS { int16_t __padding2; public: + Vec3() { memset(this, 0, sizeof(Vec3)); } + Vec3(const Vec3 &_o) { memcpy(this, &_o, sizeof(Vec3)); } Vec3(float _x, float _y, float _z, double _test1, Color _test2, const Test &_test3) : x_(flatbuffers::EndianScalar(_x)), y_(flatbuffers::EndianScalar(_y)), z_(flatbuffers::EndianScalar(_z)), __padding0(0), test1_(flatbuffers::EndianScalar(_test1)), test2_(flatbuffers::EndianScalar(static_cast(_test2))), __padding1(0), test3_(_test3), __padding2(0) { (void)__padding0; (void)__padding1; (void)__padding2; } @@ -109,11 +134,15 @@ STRUCT_END(Vec3, 32); namespace Example2 { +struct MonsterT : public flatbuffers::NativeTable { +}; + struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && verifier.EndTable(); } + std::unique_ptr UnPack() const; }; struct MonsterBuilder { @@ -132,10 +161,16 @@ inline flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder return builder_.Finish(); } +inline flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o); + } // namespace Example2 namespace Example { +struct TestSimpleTableWithEnumT : public flatbuffers::NativeTable { + Color color; +}; + struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_COLOR = 4 @@ -147,6 +182,7 @@ struct TestSimpleTableWithEnum FLATBUFFERS_FINAL_CLASS : private flatbuffers::Ta VerifyField(verifier, VT_COLOR) && verifier.EndTable(); } + std::unique_ptr UnPack() const; }; struct TestSimpleTableWithEnumBuilder { @@ -162,12 +198,20 @@ struct TestSimpleTableWithEnumBuilder { }; inline flatbuffers::Offset CreateTestSimpleTableWithEnum(flatbuffers::FlatBufferBuilder &_fbb, - Color color = Color_Green) { + Color color = Color_Green) { TestSimpleTableWithEnumBuilder builder_(_fbb); builder_.add_color(color); return builder_.Finish(); } +inline flatbuffers::Offset CreateTestSimpleTableWithEnum(flatbuffers::FlatBufferBuilder &_fbb, const TestSimpleTableWithEnumT *_o); + +struct StatT : public flatbuffers::NativeTable { + std::string id; + int64_t val; + uint16_t count; +}; + struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_ID = 4, @@ -188,6 +232,7 @@ struct Stat FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VerifyField(verifier, VT_COUNT) && verifier.EndTable(); } + std::unique_ptr UnPack() const; }; struct StatBuilder { @@ -205,9 +250,9 @@ struct StatBuilder { }; inline flatbuffers::Offset CreateStat(flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset id = 0, - int64_t val = 0, - uint16_t count = 0) { + flatbuffers::Offset id = 0, + int64_t val = 0, + uint16_t count = 0) { StatBuilder builder_(_fbb); builder_.add_val(val); builder_.add_id(id); @@ -215,6 +260,45 @@ inline flatbuffers::Offset CreateStat(flatbuffers::FlatBufferBuilder &_fbb return builder_.Finish(); } +inline flatbuffers::Offset CreateStat(flatbuffers::FlatBufferBuilder &_fbb, + const char *id = nullptr, + int64_t val = 0, + uint16_t count = 0) { + return CreateStat(_fbb, id ? 0 : _fbb.CreateString(id), val, count); +} + +inline flatbuffers::Offset CreateStat(flatbuffers::FlatBufferBuilder &_fbb, const StatT *_o); + +struct MonsterT : public flatbuffers::NativeTable { + std::unique_ptr pos; + int16_t mana; + int16_t hp; + std::string name; + std::vector inventory; + Color color; + AnyUnion test; + std::vector test4; + std::vector testarrayofstring; + std::vector> testarrayoftables; + std::unique_ptr enemy; + std::vector testnestedflatbuffer; + std::unique_ptr testempty; + bool testbool; + int32_t testhashs32_fnv1; + uint32_t testhashu32_fnv1; + int64_t testhashs64_fnv1; + uint64_t testhashu64_fnv1; + int32_t testhashs32_fnv1a; + uint32_t testhashu32_fnv1a; + int64_t testhashs64_fnv1a; + uint64_t testhashu64_fnv1a; + std::vector testarrayofbools; + float testf; + float testf2; + float testf3; + std::vector testarrayofstring2; +}; + /// an example documentation comment: monster object struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { @@ -354,6 +438,7 @@ struct Monster FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { verifier.VerifyVectorOfStrings(testarrayofstring2()) && verifier.EndTable(); } + std::unique_ptr UnPack() const; }; struct MonsterBuilder { @@ -397,34 +482,34 @@ struct MonsterBuilder { }; inline flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, - const Vec3 *pos = 0, - int16_t mana = 150, - int16_t hp = 100, - flatbuffers::Offset name = 0, - flatbuffers::Offset> inventory = 0, - Color color = Color_Blue, - Any test_type = Any_NONE, - flatbuffers::Offset test = 0, - flatbuffers::Offset> test4 = 0, - flatbuffers::Offset>> testarrayofstring = 0, - flatbuffers::Offset>> testarrayoftables = 0, - flatbuffers::Offset enemy = 0, - flatbuffers::Offset> testnestedflatbuffer = 0, - flatbuffers::Offset testempty = 0, - bool testbool = false, - int32_t testhashs32_fnv1 = 0, - uint32_t testhashu32_fnv1 = 0, - int64_t testhashs64_fnv1 = 0, - uint64_t testhashu64_fnv1 = 0, - int32_t testhashs32_fnv1a = 0, - uint32_t testhashu32_fnv1a = 0, - int64_t testhashs64_fnv1a = 0, - uint64_t testhashu64_fnv1a = 0, - flatbuffers::Offset> testarrayofbools = 0, - float testf = 3.14159f, - float testf2 = 3.0f, - float testf3 = 0.0f, - flatbuffers::Offset>> testarrayofstring2 = 0) { + const Vec3 *pos = 0, + int16_t mana = 150, + int16_t hp = 100, + flatbuffers::Offset name = 0, + flatbuffers::Offset> inventory = 0, + Color color = Color_Blue, + Any test_type = Any_NONE, + flatbuffers::Offset test = 0, + flatbuffers::Offset> test4 = 0, + flatbuffers::Offset>> testarrayofstring = 0, + flatbuffers::Offset>> testarrayoftables = 0, + flatbuffers::Offset enemy = 0, + flatbuffers::Offset> testnestedflatbuffer = 0, + flatbuffers::Offset testempty = 0, + bool testbool = false, + int32_t testhashs32_fnv1 = 0, + uint32_t testhashu32_fnv1 = 0, + int64_t testhashs64_fnv1 = 0, + uint64_t testhashu64_fnv1 = 0, + int32_t testhashs32_fnv1a = 0, + uint32_t testhashu32_fnv1a = 0, + int64_t testhashs64_fnv1a = 0, + uint64_t testhashu64_fnv1a = 0, + flatbuffers::Offset> testarrayofbools = 0, + float testf = 3.14159f, + float testf2 = 3.0f, + float testf3 = 0.0f, + flatbuffers::Offset>> testarrayofstring2 = 0) { MonsterBuilder builder_(_fbb); builder_.add_testhashu64_fnv1a(testhashu64_fnv1a); builder_.add_testhashs64_fnv1a(testhashs64_fnv1a); @@ -457,6 +542,149 @@ inline flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder return builder_.Finish(); } +inline flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, + const Vec3 *pos = 0, + int16_t mana = 150, + int16_t hp = 100, + const char *name = nullptr, + const std::vector *inventory = nullptr, + Color color = Color_Blue, + Any test_type = Any_NONE, + flatbuffers::Offset test = 0, + const std::vector *test4 = nullptr, + const std::vector> *testarrayofstring = nullptr, + const std::vector> *testarrayoftables = nullptr, + flatbuffers::Offset enemy = 0, + const std::vector *testnestedflatbuffer = nullptr, + flatbuffers::Offset testempty = 0, + bool testbool = false, + int32_t testhashs32_fnv1 = 0, + uint32_t testhashu32_fnv1 = 0, + int64_t testhashs64_fnv1 = 0, + uint64_t testhashu64_fnv1 = 0, + int32_t testhashs32_fnv1a = 0, + uint32_t testhashu32_fnv1a = 0, + int64_t testhashs64_fnv1a = 0, + uint64_t testhashu64_fnv1a = 0, + const std::vector *testarrayofbools = nullptr, + float testf = 3.14159f, + float testf2 = 3.0f, + float testf3 = 0.0f, + const std::vector> *testarrayofstring2 = nullptr) { + return CreateMonster(_fbb, pos, mana, hp, name ? 0 : _fbb.CreateString(name), inventory ? 0 : _fbb.CreateVector(*inventory), color, test_type, test, test4 ? 0 : _fbb.CreateVector(*test4), testarrayofstring ? 0 : _fbb.CreateVector>(*testarrayofstring), testarrayoftables ? 0 : _fbb.CreateVector>(*testarrayoftables), enemy, testnestedflatbuffer ? 0 : _fbb.CreateVector(*testnestedflatbuffer), testempty, testbool, testhashs32_fnv1, testhashu32_fnv1, testhashs64_fnv1, testhashu64_fnv1, testhashs32_fnv1a, testhashu32_fnv1a, testhashs64_fnv1a, testhashu64_fnv1a, testarrayofbools ? 0 : _fbb.CreateVector(*testarrayofbools), testf, testf2, testf3, testarrayofstring2 ? 0 : _fbb.CreateVector>(*testarrayofstring2)); +} + +inline flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o); + +} // namespace Example + +namespace Example2 { + +inline std::unique_ptr Monster::UnPack() const { + auto _o = new MonsterT(); + return std::unique_ptr(_o); +} + +inline flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o) { + (void)_o; + return CreateMonster(_fbb); +} + +} // namespace Example2 + +namespace Example { + +inline std::unique_ptr TestSimpleTableWithEnum::UnPack() const { + auto _o = new TestSimpleTableWithEnumT(); + { auto _e = color(); _o->color = _e; }; + return std::unique_ptr(_o); +} + +inline flatbuffers::Offset CreateTestSimpleTableWithEnum(flatbuffers::FlatBufferBuilder &_fbb, const TestSimpleTableWithEnumT *_o) { + return CreateTestSimpleTableWithEnum(_fbb, + _o->color); +} + +inline std::unique_ptr Stat::UnPack() const { + auto _o = new StatT(); + { auto _e = id(); if (_e) _o->id = _e->str(); }; + { auto _e = val(); _o->val = _e; }; + { auto _e = count(); _o->count = _e; }; + return std::unique_ptr(_o); +} + +inline flatbuffers::Offset CreateStat(flatbuffers::FlatBufferBuilder &_fbb, const StatT *_o) { + return CreateStat(_fbb, + _o->id.size() ? _fbb.CreateString(_o->id) : 0, + _o->val, + _o->count); +} + +inline std::unique_ptr Monster::UnPack() const { + auto _o = new MonsterT(); + { auto _e = pos(); if (_e) _o->pos = std::unique_ptr(new Vec3(*_e)); }; + { auto _e = mana(); _o->mana = _e; }; + { auto _e = hp(); _o->hp = _e; }; + { auto _e = name(); if (_e) _o->name = _e->str(); }; + { auto _e = inventory(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->inventory.push_back(_e->Get(_i)); } } }; + { auto _e = color(); _o->color = _e; }; + { auto _e = test_type(); _o->test.type = _e; }; + { auto _e = test(); if (_e) _o->test.table = AnyUnion::UnPack(_e, test_type()); }; + { auto _e = test4(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->test4.push_back(*_e->Get(_i)); } } }; + { auto _e = testarrayofstring(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testarrayofstring.push_back(_e->Get(_i)->str()); } } }; + { auto _e = testarrayoftables(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testarrayoftables.push_back(_e->Get(_i)->UnPack()); } } }; + { auto _e = enemy(); if (_e) _o->enemy = _e->UnPack(); }; + { auto _e = testnestedflatbuffer(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testnestedflatbuffer.push_back(_e->Get(_i)); } } }; + { auto _e = testempty(); if (_e) _o->testempty = _e->UnPack(); }; + { auto _e = testbool(); _o->testbool = _e; }; + { auto _e = testhashs32_fnv1(); _o->testhashs32_fnv1 = _e; }; + { auto _e = testhashu32_fnv1(); _o->testhashu32_fnv1 = _e; }; + { auto _e = testhashs64_fnv1(); _o->testhashs64_fnv1 = _e; }; + { auto _e = testhashu64_fnv1(); _o->testhashu64_fnv1 = _e; }; + { auto _e = testhashs32_fnv1a(); _o->testhashs32_fnv1a = _e; }; + { auto _e = testhashu32_fnv1a(); _o->testhashu32_fnv1a = _e; }; + { auto _e = testhashs64_fnv1a(); _o->testhashs64_fnv1a = _e; }; + { auto _e = testhashu64_fnv1a(); _o->testhashu64_fnv1a = _e; }; + { auto _e = testarrayofbools(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testarrayofbools.push_back(_e->Get(_i)!=0); } } }; + { auto _e = testf(); _o->testf = _e; }; + { auto _e = testf2(); _o->testf2 = _e; }; + { auto _e = testf3(); _o->testf3 = _e; }; + { auto _e = testarrayofstring2(); if (_e) { for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->testarrayofstring2.push_back(_e->Get(_i)->str()); } } }; + return std::unique_ptr(_o); +} + +inline flatbuffers::Offset CreateMonster(flatbuffers::FlatBufferBuilder &_fbb, const MonsterT *_o) { + return CreateMonster(_fbb, + _o->pos ? _o->pos.get() : 0, + _o->mana, + _o->hp, + _fbb.CreateString(_o->name), + _o->inventory.size() ? _fbb.CreateVector(_o->inventory) : 0, + _o->color, + _o->test.type, + _o->test.Pack(_fbb), + _o->test4.size() ? _fbb.CreateVectorOfStructs(_o->test4) : 0, + _o->testarrayofstring.size() ? _fbb.CreateVectorOfStrings(_o->testarrayofstring) : 0, + _o->testarrayoftables.size() ? _fbb.CreateVector>(_o->testarrayoftables.size(), [&](size_t i) { return CreateMonster(_fbb, _o->testarrayoftables[i].get()); }) : 0, + _o->enemy ? CreateMonster(_fbb, _o->enemy.get()) : 0, + _o->testnestedflatbuffer.size() ? _fbb.CreateVector(_o->testnestedflatbuffer) : 0, + _o->testempty ? CreateStat(_fbb, _o->testempty.get()) : 0, + _o->testbool, + _o->testhashs32_fnv1, + _o->testhashu32_fnv1, + _o->testhashs64_fnv1, + _o->testhashu64_fnv1, + _o->testhashs32_fnv1a, + _o->testhashu32_fnv1a, + _o->testhashs64_fnv1a, + _o->testhashu64_fnv1a, + _o->testarrayofbools.size() ? _fbb.CreateVector(_o->testarrayofbools) : 0, + _o->testf, + _o->testf2, + _o->testf3, + _o->testarrayofstring2.size() ? _fbb.CreateVectorOfStrings(_o->testarrayofstring2) : 0); +} + inline bool VerifyAny(flatbuffers::Verifier &verifier, const void *union_obj, Any type) { switch (type) { case Any_NONE: return true; @@ -467,16 +695,45 @@ inline bool VerifyAny(flatbuffers::Verifier &verifier, const void *union_obj, An } } +inline flatbuffers::NativeTable *AnyUnion::UnPack(const void *union_obj, Any type) { + switch (type) { + case Any_NONE: return nullptr; + case Any_Monster: return reinterpret_cast(union_obj)->UnPack().release(); + case Any_TestSimpleTableWithEnum: return reinterpret_cast(union_obj)->UnPack().release(); + case Any_MyGame_Example2_Monster: return reinterpret_cast(union_obj)->UnPack().release(); + default: return nullptr; + } +} + +inline flatbuffers::Offset AnyUnion::Pack(flatbuffers::FlatBufferBuilder &_fbb) const { + switch (type) { + case Any_NONE: return 0; + case Any_Monster: return CreateMonster(_fbb, reinterpret_cast(table)).Union(); + case Any_TestSimpleTableWithEnum: return CreateTestSimpleTableWithEnum(_fbb, reinterpret_cast(table)).Union(); + case Any_MyGame_Example2_Monster: return CreateMonster(_fbb, reinterpret_cast(table)).Union(); + default: return 0; + } +} + +inline AnyUnion::~AnyUnion() { + switch (type) { + case Any_Monster: delete reinterpret_cast(table); break; + case Any_TestSimpleTableWithEnum: delete reinterpret_cast(table); break; + case Any_MyGame_Example2_Monster: delete reinterpret_cast(table); break; + default:; + } +} + inline const MyGame::Example::Monster *GetMonster(const void *buf) { return flatbuffers::GetRoot(buf); } inline Monster *GetMutableMonster(void *buf) { return flatbuffers::GetMutableRoot(buf); } -inline bool VerifyMonsterBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer(); } - inline const char *MonsterIdentifier() { return "MONS"; } inline bool MonsterBufferHasIdentifier(const void *buf) { return flatbuffers::BufferHasIdentifier(buf, MonsterIdentifier()); } +inline bool VerifyMonsterBuffer(flatbuffers::Verifier &verifier) { return verifier.VerifyBuffer(MonsterIdentifier()); } + inline const char *MonsterExtension() { return "mon"; } inline void FinishMonsterBuffer(flatbuffers::FlatBufferBuilder &fbb, flatbuffers::Offset root) { fbb.Finish(root, MonsterIdentifier()); } diff --git a/tests/monster_test_generated.js b/tests/monster_test_generated.js index f67e84802..7c4c28f82 100644 --- a/tests/monster_test_generated.js +++ b/tests/monster_test_generated.js @@ -2,22 +2,26 @@ /** * @const -*/ + * @namespace + */ var MyGame = MyGame || {}; /** * @const -*/ + * @namespace + */ MyGame.Example = MyGame.Example || {}; /** * @const -*/ + * @namespace + */ MyGame.Example2 = MyGame.Example2 || {}; /** * @const -*/ + * @namespace + */ MyGame.OtherNameSpace = MyGame.OtherNameSpace || {}; /** diff --git a/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.java b/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.java index ff5559544..e23cecc06 100644 --- a/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.java +++ b/tests/namespace_test/NamespaceA/NamespaceB/EnumInNestedNS.java @@ -8,8 +8,8 @@ public final class EnumInNestedNS { public static final byte B = 1; public static final byte C = 2; - private static final String[] names = { "A", "B", "C", }; + public static final String[] names = { "A", "B", "C", }; public static String name(int e) { return names[e]; } -}; +} diff --git a/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.go b/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.go index f3684623b..f834a721a 100644 --- a/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.go +++ b/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.go @@ -15,7 +15,11 @@ func (rcv *StructInNestedNS) Init(buf []byte, i flatbuffers.UOffsetT) { } func (rcv *StructInNestedNS) A() int32 { return rcv._tab.GetInt32(rcv._tab.Pos + flatbuffers.UOffsetT(0)) } +func (rcv *StructInNestedNS) MutateA(n int32) bool { return rcv._tab.MutateInt32(rcv._tab.Pos + flatbuffers.UOffsetT(0), n) } + func (rcv *StructInNestedNS) B() int32 { return rcv._tab.GetInt32(rcv._tab.Pos + flatbuffers.UOffsetT(4)) } +func (rcv *StructInNestedNS) MutateB(n int32) bool { return rcv._tab.MutateInt32(rcv._tab.Pos + flatbuffers.UOffsetT(4), n) } + func CreateStructInNestedNS(builder *flatbuffers.Builder, a int32, b int32) flatbuffers.UOffsetT { builder.Prep(4, 8) diff --git a/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.java b/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.java index 93f805224..fede07a02 100644 --- a/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.java +++ b/tests/namespace_test/NamespaceA/NamespaceB/StructInNestedNS.java @@ -22,5 +22,5 @@ public final class StructInNestedNS extends Struct { builder.putInt(a); return builder.offset(); } -}; +} diff --git a/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.go b/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.go index cf2b557c6..c956fb42c 100644 --- a/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.go +++ b/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.go @@ -22,6 +22,10 @@ func (rcv *TableInNestedNS) Foo() int32 { return 0 } +func (rcv *TableInNestedNS) MutateFoo(n int32) bool { + return rcv._tab.MutateInt32Slot(4, n) +} + func TableInNestedNSStart(builder *flatbuffers.Builder) { builder.StartObject(1) } func TableInNestedNSAddFoo(builder *flatbuffers.Builder, foo int32) { builder.PrependInt32Slot(0, foo, 0) } func TableInNestedNSEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() } diff --git a/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.java b/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.java index 2cfb1bacc..fc518563d 100644 --- a/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.java +++ b/tests/namespace_test/NamespaceA/NamespaceB/TableInNestedNS.java @@ -29,5 +29,5 @@ public final class TableInNestedNS extends Table { int o = builder.endObject(); return o; } -}; +} diff --git a/tests/namespace_test/NamespaceA/SecondTableInA.java b/tests/namespace_test/NamespaceA/SecondTableInA.java index acabc3931..e6f390a9f 100644 --- a/tests/namespace_test/NamespaceA/SecondTableInA.java +++ b/tests/namespace_test/NamespaceA/SecondTableInA.java @@ -29,5 +29,5 @@ public final class SecondTableInA extends Table { int o = builder.endObject(); return o; } -}; +} diff --git a/tests/namespace_test/NamespaceA/TableInFirstNS.go b/tests/namespace_test/NamespaceA/TableInFirstNS.go index 5580d5d58..0a002b3b7 100644 --- a/tests/namespace_test/NamespaceA/TableInFirstNS.go +++ b/tests/namespace_test/NamespaceA/TableInFirstNS.go @@ -35,6 +35,10 @@ func (rcv *TableInFirstNS) FooEnum() int8 { return 0 } +func (rcv *TableInFirstNS) MutateFooEnum(n int8) bool { + return rcv._tab.MutateInt8Slot(6, n) +} + func (rcv *TableInFirstNS) FooStruct(obj *StructInNestedNS) *StructInNestedNS { o := flatbuffers.UOffsetT(rcv._tab.Offset(8)) if o != 0 { diff --git a/tests/namespace_test/NamespaceA/TableInFirstNS.java b/tests/namespace_test/NamespaceA/TableInFirstNS.java index 873979307..b44df9786 100644 --- a/tests/namespace_test/NamespaceA/TableInFirstNS.java +++ b/tests/namespace_test/NamespaceA/TableInFirstNS.java @@ -28,5 +28,5 @@ public final class TableInFirstNS extends Table { int o = builder.endObject(); return o; } -}; +} diff --git a/tests/namespace_test/NamespaceC/TableInC.java b/tests/namespace_test/NamespaceC/TableInC.java index cf1bca18c..19bb4cd57 100644 --- a/tests/namespace_test/NamespaceC/TableInC.java +++ b/tests/namespace_test/NamespaceC/TableInC.java @@ -34,5 +34,5 @@ public final class TableInC extends Table { int o = builder.endObject(); return o; } -}; +} diff --git a/tests/namespace_test/namespace_test1_generated.h b/tests/namespace_test/namespace_test1_generated.h index 9e10eb4c1..59d4030a1 100644 --- a/tests/namespace_test/namespace_test1_generated.h +++ b/tests/namespace_test/namespace_test1_generated.h @@ -33,6 +33,8 @@ MANUALLY_ALIGNED_STRUCT(4) StructInNestedNS FLATBUFFERS_FINAL_CLASS { int32_t b_; public: + StructInNestedNS() { memset(this, 0, sizeof(StructInNestedNS)); } + StructInNestedNS(const StructInNestedNS &_o) { memcpy(this, &_o, sizeof(StructInNestedNS)); } StructInNestedNS(int32_t _a, int32_t _b) : a_(flatbuffers::EndianScalar(_a)), b_(flatbuffers::EndianScalar(_b)) { } @@ -69,7 +71,7 @@ struct TableInNestedNSBuilder { }; inline flatbuffers::Offset CreateTableInNestedNS(flatbuffers::FlatBufferBuilder &_fbb, - int32_t foo = 0) { + int32_t foo = 0) { TableInNestedNSBuilder builder_(_fbb); builder_.add_foo(foo); return builder_.Finish(); diff --git a/tests/namespace_test/namespace_test1_generated.js b/tests/namespace_test/namespace_test1_generated.js index 769b523e3..e6390567f 100644 --- a/tests/namespace_test/namespace_test1_generated.js +++ b/tests/namespace_test/namespace_test1_generated.js @@ -2,12 +2,14 @@ /** * @const -*/ + * @namespace + */ var NamespaceA = NamespaceA || {}; /** * @const -*/ + * @namespace + */ NamespaceA.NamespaceB = NamespaceA.NamespaceB || {}; /** diff --git a/tests/namespace_test/namespace_test2_generated.h b/tests/namespace_test/namespace_test2_generated.h index 8e6b3ada8..77578bc60 100644 --- a/tests/namespace_test/namespace_test2_generated.h +++ b/tests/namespace_test/namespace_test2_generated.h @@ -60,9 +60,9 @@ struct TableInFirstNSBuilder { }; inline flatbuffers::Offset CreateTableInFirstNS(flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset foo_table = 0, - NamespaceA::NamespaceB::EnumInNestedNS foo_enum = NamespaceA::NamespaceB::EnumInNestedNS_A, - const NamespaceA::NamespaceB::StructInNestedNS *foo_struct = 0) { + flatbuffers::Offset foo_table = 0, + NamespaceA::NamespaceB::EnumInNestedNS foo_enum = NamespaceA::NamespaceB::EnumInNestedNS_A, + const NamespaceA::NamespaceB::StructInNestedNS *foo_struct = 0) { TableInFirstNSBuilder builder_(_fbb); builder_.add_foo_struct(foo_struct); builder_.add_foo_table(foo_table); @@ -107,8 +107,8 @@ struct TableInCBuilder { }; inline flatbuffers::Offset CreateTableInC(flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset refer_to_a1 = 0, - flatbuffers::Offset refer_to_a2 = 0) { + flatbuffers::Offset refer_to_a1 = 0, + flatbuffers::Offset refer_to_a2 = 0) { TableInCBuilder builder_(_fbb); builder_.add_refer_to_a2(refer_to_a2); builder_.add_refer_to_a1(refer_to_a1); @@ -146,7 +146,7 @@ struct SecondTableInABuilder { }; inline flatbuffers::Offset CreateSecondTableInA(flatbuffers::FlatBufferBuilder &_fbb, - flatbuffers::Offset refer_to_c = 0) { + flatbuffers::Offset refer_to_c = 0) { SecondTableInABuilder builder_(_fbb); builder_.add_refer_to_c(refer_to_c); return builder_.Finish(); @@ -154,4 +154,12 @@ inline flatbuffers::Offset CreateSecondTableInA(flatbuffers::Fla } // namespace NamespaceA +namespace NamespaceC { + +} // namespace NamespaceC + +namespace NamespaceA { + +} // namespace NamespaceA + #endif // FLATBUFFERS_GENERATED_NAMESPACETEST2_NAMESPACEA_H_ diff --git a/tests/namespace_test/namespace_test2_generated.js b/tests/namespace_test/namespace_test2_generated.js index c1953705e..f76d5a95a 100644 --- a/tests/namespace_test/namespace_test2_generated.js +++ b/tests/namespace_test/namespace_test2_generated.js @@ -2,17 +2,20 @@ /** * @const -*/ + * @namespace + */ var NamespaceA = NamespaceA || {}; /** * @const -*/ + * @namespace + */ NamespaceA.NamespaceB = NamespaceA.NamespaceB || {}; /** * @const -*/ + * @namespace + */ var NamespaceC = NamespaceC || {}; /** diff --git a/tests/py_test.py b/tests/py_test.py index 82f839005..e66094492 100644 --- a/tests/py_test.py +++ b/tests/py_test.py @@ -1034,6 +1034,23 @@ class TestAllCodePathsOfExampleSchema(unittest.TestCase): self.assertEqual(7, mon2.Testhashs64Fnv1a()) self.assertEqual(8, mon2.Testhashu64Fnv1a()) + def test_getrootas_for_nonroot_table(self): + b = flatbuffers.Builder(0) + string = b.CreateString("MyStat") + + MyGame.Example.Stat.StatStart(b) + MyGame.Example.Stat.StatAddId(b, string) + MyGame.Example.Stat.StatAddVal(b, 12345678) + MyGame.Example.Stat.StatAddCount(b, 12345) + stat = MyGame.Example.Stat.StatEnd(b) + b.Finish(stat) + + stat2 = MyGame.Example.Stat.Stat.GetRootAsStat(b.Bytes, b.Head()) + + self.assertEqual(b"MyStat", stat2.Id()) + self.assertEqual(12345678, stat2.Val()) + self.assertEqual(12345, stat2.Count()) + class TestVtableDeduplication(unittest.TestCase): ''' TestVtableDeduplication verifies that vtables are deduplicated. ''' diff --git a/tests/test.cpp b/tests/test.cpp index 6f3b06299..6ec4e678c 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -15,6 +15,7 @@ */ #define FLATBUFFERS_DEBUG_VERIFICATION_FAILURE 1 +#define FLATBUFFERS_TRACK_VERIFIER_BUFFER_SIZE #include "flatbuffers/flatbuffers.h" #include "flatbuffers/idl.h" @@ -24,7 +25,9 @@ #include "namespace_test/namespace_test1_generated.h" #include "namespace_test/namespace_test2_generated.h" -#include +#ifndef FLATBUFFERS_CPP98_STL + #include +#endif using namespace MyGame::Example; @@ -123,7 +126,9 @@ flatbuffers::unique_ptr_t CreateFlatBufferTest(std::string &buffer) { }); // Creating vectors of strings in one convenient call. - std::vector names2 = { "jane", "mary" }; + std::vector names2; + names2.push_back("jane"); + names2.push_back("mary"); auto vecofstrings2 = builder.CreateVectorOfStrings(names2); // Create an array of sorted tables, can be used with binary search when read: @@ -154,12 +159,30 @@ flatbuffers::unique_ptr_t CreateFlatBufferTest(std::string &buffer) { } // example of accessing a buffer loaded in memory: -void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length) { +void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length, + bool pooled = true) { // First, verify the buffers integrity (optional) flatbuffers::Verifier verifier(flatbuf, length); TEST_EQ(VerifyMonsterBuffer(verifier), true); + std::vector test_buff; + test_buff.resize(length * 2); + std::memcpy(&test_buff[0], flatbuf , length); + std::memcpy(&test_buff[length], flatbuf , length); + + flatbuffers::Verifier verifierl(&test_buff[0], length - 1); + TEST_EQ(VerifyMonsterBuffer(verifierl), false); + TEST_EQ(verifierl.GetComputedSize(), 0); + + flatbuffers::Verifier verifier1(&test_buff[0], length); + TEST_EQ(VerifyMonsterBuffer(verifier1), true); + TEST_EQ(verifier1.GetComputedSize(), length); + + flatbuffers::Verifier verifier2(&test_buff[length], length); + TEST_EQ(VerifyMonsterBuffer(verifier2), true); + TEST_EQ(verifier2.GetComputedSize(), length); + TEST_EQ(strcmp(MonsterIdentifier(), "MONS"), 0); TEST_EQ(MonsterBufferHasIdentifier(flatbuf), true); TEST_EQ(strcmp(MonsterExtension(), "mon"), 0); @@ -200,9 +223,11 @@ void AccessFlatBufferTest(const uint8_t *flatbuf, size_t length) { TEST_EQ(vecofstrings->Length(), 4U); TEST_EQ_STR(vecofstrings->Get(0)->c_str(), "bob"); TEST_EQ_STR(vecofstrings->Get(1)->c_str(), "fred"); - // These should have pointer equality because of string pooling. - TEST_EQ(vecofstrings->Get(0)->c_str(), vecofstrings->Get(2)->c_str()); - TEST_EQ(vecofstrings->Get(1)->c_str(), vecofstrings->Get(3)->c_str()); + if (pooled) { + // These should have pointer equality because of string pooling. + TEST_EQ(vecofstrings->Get(0)->c_str(), vecofstrings->Get(2)->c_str()); + TEST_EQ(vecofstrings->Get(1)->c_str(), vecofstrings->Get(3)->c_str()); + } auto vecofstrings2 = monster->testarrayofstring2(); if (vecofstrings2) { @@ -287,6 +312,77 @@ void MutateFlatBuffersTest(uint8_t *flatbuf, std::size_t length) { AccessFlatBufferTest(flatbuf, length); } +// Unpack a FlatBuffer into objects. +void ObjectFlatBuffersTest(uint8_t *flatbuf) { + // Turn a buffer into C++ objects. + auto monster1 = GetMonster(flatbuf)->UnPack(); + + // Re-serialize the data. + flatbuffers::FlatBufferBuilder fbb1; + fbb1.Finish(CreateMonster(fbb1, monster1.get()), MonsterIdentifier()); + + // Unpack again, and re-serialize again. + auto monster2 = GetMonster(fbb1.GetBufferPointer())->UnPack(); + flatbuffers::FlatBufferBuilder fbb2; + fbb2.Finish(CreateMonster(fbb2, monster2.get()), MonsterIdentifier()); + + // Now we've gone full round-trip, the two buffers should match. + auto len1 = fbb1.GetSize(); + auto len2 = fbb2.GetSize(); + TEST_EQ(len1, len2); + TEST_EQ(memcmp(fbb1.GetBufferPointer(), fbb2.GetBufferPointer(), + len1), 0); + + // Test it with the original buffer test to make sure all data survived. + AccessFlatBufferTest(fbb2.GetBufferPointer(), len2, false); + + // Test accessing fields, similar to AccessFlatBufferTest above. + TEST_EQ(monster2->hp, 80); + TEST_EQ(monster2->mana, 150); // default + TEST_EQ_STR(monster2->name.c_str(), "MyMonster"); + + auto &pos = monster2->pos; + TEST_NOTNULL(pos); + TEST_EQ(pos->z(), 3); + TEST_EQ(pos->test3().a(), 10); + TEST_EQ(pos->test3().b(), 20); + + auto &inventory = monster2->inventory; + TEST_EQ(inventory.size(), 10UL); + unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + for (auto it = inventory.begin(); it != inventory.end(); ++it) + TEST_EQ(*it, inv_data[it - inventory.begin()]); + + TEST_EQ(monster2->color, Color_Blue); + + auto monster3 = monster2->test.AsMonster(); + TEST_NOTNULL(monster3); + TEST_EQ_STR(monster3->name.c_str(), "Fred"); + + auto &vecofstrings = monster2->testarrayofstring; + TEST_EQ(vecofstrings.size(), 4U); + TEST_EQ_STR(vecofstrings[0].c_str(), "bob"); + TEST_EQ_STR(vecofstrings[1].c_str(), "fred"); + + auto &vecofstrings2 = monster2->testarrayofstring2; + TEST_EQ(vecofstrings2.size(), 2U); + TEST_EQ_STR(vecofstrings2[0].c_str(), "jane"); + TEST_EQ_STR(vecofstrings2[1].c_str(), "mary"); + + auto &vecoftables = monster2->testarrayoftables; + TEST_EQ(vecoftables.size(), 3U); + TEST_EQ_STR(vecoftables[0]->name.c_str(), "Barney"); + TEST_EQ(vecoftables[0]->hp, 1000); + TEST_EQ_STR(vecoftables[1]->name.c_str(), "Fred"); + TEST_EQ_STR(vecoftables[2]->name.c_str(), "Wilma"); + + auto &tests = monster2->test4; + TEST_EQ(tests[0].a(), 10); + TEST_EQ(tests[0].b(), 20); + TEST_EQ(tests[1].a(), 30); + TEST_EQ(tests[1].b(), 40); +} + // example of parsing text straight into a buffer, and generating // text back from it: void ParseAndGenerateTextTest() { @@ -814,14 +910,14 @@ void ErrorTest() { TestError("table X { Y:byte; } root_type X; { Y:1, Y:2 }", "more than once"); } -float TestValue(const char *json) { +template T TestValue(const char *json, const char *type_name) { flatbuffers::Parser parser; // Simple schema. - TEST_EQ(parser.Parse("table X { Y:float; } root_type X;"), true); + TEST_EQ(parser.Parse(std::string("table X { Y:" + std::string(type_name) + "; } root_type X;").c_str()), true); TEST_EQ(parser.Parse(json), true); - auto root = flatbuffers::GetRoot(parser.builder_.GetBufferPointer()); + auto root = flatbuffers::GetRoot(parser.builder_.GetBufferPointer()); // root will point to the table, which is a 32bit vtable offset followed // by a float: TEST_EQ(sizeof(flatbuffers::soffset_t), 4); // Test assumes 32bit offsets @@ -833,10 +929,13 @@ bool FloatCompare(float a, float b) { return fabs(a - b) < 0.001; } // Additional parser testing not covered elsewhere. void ValueTest() { // Test scientific notation numbers. - TEST_EQ(FloatCompare(TestValue("{ Y:0.0314159e+2 }"), 3.14159), true); + TEST_EQ(FloatCompare(TestValue("{ Y:0.0314159e+2 }","float"), (float)3.14159), true); // Test conversion functions. - TEST_EQ(FloatCompare(TestValue("{ Y:cos(rad(180)) }"), -1), true); + TEST_EQ(FloatCompare(TestValue("{ Y:cos(rad(180)) }","float"), -1), true); + + // Test negative hex constant. + TEST_EQ(TestValue("{ Y:-0x80 }","int") == -128, true); } void EnumStringsTest() { @@ -961,6 +1060,24 @@ void ParseUnionTest() { "{ X:{ A:1 }, X_type: T }"), true); } +void ConformTest() { + flatbuffers::Parser parser; + TEST_EQ(parser.Parse("table T { A:int; } enum E:byte { A }"), true); + + auto test_conform = [&](const char *test, const char *expected_err) { + flatbuffers::Parser parser2; + TEST_EQ(parser2.Parse(test), true); + auto err = parser2.ConformTo(parser); + TEST_NOTNULL(strstr(err.c_str(), expected_err)); + }; + + test_conform("table T { A:byte; }", "types differ for field"); + test_conform("table T { B:int; A:int; }", "offsets differ for field"); + test_conform("table T { A:int = 1; }", "defaults differ for field"); + test_conform("table T { B:float; }", "field renamed to different type"); + test_conform("enum E:byte { B, A }", "values differ for enum"); +} + int main(int /*argc*/, const char * /*argv*/[]) { // Run our various test suites: @@ -972,6 +1089,8 @@ int main(int /*argc*/, const char * /*argv*/[]) { MutateFlatBuffersTest(flatbuf.get(), rawbuf.length()); + ObjectFlatBuffersTest(flatbuf.get()); + #ifndef FLATBUFFERS_NO_FILE_TESTS ParseAndGenerateTextTest(); ReflectionTest(flatbuf.get(), rawbuf.length()); @@ -990,6 +1109,7 @@ int main(int /*argc*/, const char * /*argv*/[]) { UnicodeInvalidSurrogatesTest(); UnknownFieldsTest(); ParseUnionTest(); + ConformTest(); if (!testing_fails) { TEST_OUTPUT_LINE("ALL TESTS PASSED");