Commit b730a74a authored by Romain Gilles's avatar Romain Gilles

Merge branch 'master' of github.com:google/flatbuffers

parents cc2b04ce 68a83edd
......@@ -63,6 +63,13 @@ function(build_flatbuffers flatbuffers_schemas
set(FLATC_TARGET flatc)
set(FLATC flatc)
endif()
set(FLATC_SCHEMA_ARGS --gen-mutable)
if(FLATBUFFERS_FLATC_SCHEMA_EXTRA_ARGS)
set(FLATC_SCHEMA_ARGS
${FLATBUFFERS_FLATC_SCHEMA_EXTRA_ARGS}
${FLATC_SCHEMA_ARGS}
)
endif()
set(schema_glob "*.fbs")
# Generate the include files parameters.
......@@ -86,7 +93,7 @@ function(build_flatbuffers flatbuffers_schemas
set(generated_include ${generated_includes_dir}/${filename}_generated.h)
add_custom_command(
OUTPUT ${generated_include}
COMMAND ${FLATC} --gen-mutable
COMMAND ${FLATC} ${FLATC_SCHEMA_ARGS}
-o ${generated_includes_dir}
${include_params}
-c ${schema}
......
......@@ -9,6 +9,7 @@ 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_FLATHASH "Enable the build of flathash" ON)
option(FLATBUFFERS_BUILD_GRPCTEST "Enable the build of grpctest" OFF)
if(NOT FLATBUFFERS_BUILD_FLATC AND FLATBUFFERS_BUILD_TESTS)
message(WARNING
......@@ -39,7 +40,10 @@ set(FlatBuffers_Compiler_SRCS
src/idl_gen_php.cpp
src/idl_gen_python.cpp
src/idl_gen_fbs.cpp
src/idl_gen_grpc.cpp
src/flatc.cpp
grpc/src/compiler/cpp_generator.h
grpc/src/compiler/cpp_generator.cc
)
set(FlatHash_SRCS
......@@ -76,6 +80,16 @@ set(FlatBuffers_Sample_Text_SRCS
${CMAKE_CURRENT_BINARY_DIR}/samples/monster_generated.h
)
set(FlatBuffers_GRPCTest_SRCS
include/flatbuffers/flatbuffers.h
include/flatbuffers/grpc.h
tests/monster_test.grpc.fb.h
tests/monster_test.grpc.fb.cc
grpc/tests/grpctest.cpp
# file generated by running compiler on samples/monster.fbs
${CMAKE_CURRENT_BINARY_DIR}/samples/monster_generated.h
)
# source_group(Compiler FILES ${FlatBuffers_Compiler_SRCS})
# source_group(Tests FILES ${FlatBuffers_Tests_SRCS})
......@@ -129,6 +143,7 @@ if(BIICODE)
endif()
include_directories(include)
include_directories(grpc)
if(FLATBUFFERS_BUILD_FLATLIB)
add_library(flatbuffers STATIC ${FlatBuffers_Library_SRCS})
......@@ -174,6 +189,14 @@ if(FLATBUFFERS_BUILD_TESTS)
add_executable(flatsampletext ${FlatBuffers_Sample_Text_SRCS})
endif()
if(FLATBUFFERS_BUILD_GRPCTEST)
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter")
endif()
add_executable(grpctest ${FlatBuffers_GRPCTest_SRCS})
target_link_libraries(grpctest grpc++_unsecure grpc pthread dl)
endif()
if(FLATBUFFERS_INSTALL)
install(DIRECTORY include/flatbuffers DESTINATION include)
if(FLATBUFFERS_BUILD_FLATLIB)
......
......@@ -18,5 +18,5 @@ APP_PROJECT_PATH := $(call my-dir)/..
APP_STL := gnustl_static
APP_ABI := armeabi-v7a
NDK_TOOLCHAIN_VERSION := 4.8
APP_CPPFLAGS += -std=c++11
......@@ -33,6 +33,8 @@ For any schema input files, one or more generators can be specified:
- `--php`: Generate PHP code.
- `--grpc`: Generate RPC stub code for GRPC.
For any data input files:
- `--binary`, `-b` : If data is contained in this file, generate a
......
......@@ -237,7 +237,8 @@ as the response (both of which must be table types):
}
What code this produces and how it is used depends on language and RPC system
used, FlatBuffers itself does not offer this functionality.
used, there is preliminary support for GRPC through the `--grpc` code generator,
see `grpc/tests` for an example.
### Comments & documentation
......@@ -271,7 +272,7 @@ Current understood attributes:
the union field should have id 8, and the unions type field will
implicitly be 7.
IDs allow the fields to be placed in any order in the schema.
When a new field is added to the schema is must use the next available ID.
When a new field is added to the schema it must use the next available ID.
- `deprecated` (on a field): do not generate accessors for this field
anymore, code should stop using this data.
- `required` (on a non-scalar table field): this field must always be set.
......@@ -333,6 +334,10 @@ JSON:
- A field that has the value `null` (e.g. `field: null`) is intended to
have the default value for that field (thus has the same effect as if
that field wasn't specified at all).
- It has some built in conversion functions, so you can write for example
`rad(180)` where ever you'd normally write `3.14159`.
Currently supports the following functions: `rad`, `deg`, `cos`, `sin`,
`tan`, `acos`, `asin`, `atan`.
When parsing JSON, it recognizes the following escape codes in strings:
......
......@@ -790,6 +790,14 @@ offsets.
~~~
</div>
<div class="language-cpp">
<br>
Note there's additional convenience overloads of `CreateVector`, allowing you
to work with data that's not in a `std::vector`, or allowing you to generate
elements by calling a lambda. For the common case of `std::vector<std::string>`
there's also `CreateVectorOfStrings`.
</div>
To create a `struct`, use the `Vec3` class/struct that was generated by
the schema compiler:
......@@ -1075,7 +1083,7 @@ Here is a repetition these lines, to help highlight them more clearly:
<div class="language-c">
~~~{.c}
// Add union type and data simultanously.
ns(Monster_equipped_Weapon_add(B, axe));
ns(Monster_equipped_Weapon_add(B, axe));
~~~
</div>
......
GRPC implementation and test
============================
NOTE: files in `src/` are shared with the GRPC project, and maintained there
(any changes should be submitted to GRPC instead). These files are copied
from GRPC, and work with both the Protobuf and FlatBuffers code generator.
`tests/` contains a GRPC specific test, you need to have built and installed
the GRPC libraries for this to compile. This test will build using the
`FLATBUFFERS_BUILD_GRPCTEST` option to the main FlatBuffers CMake project.
This diff is collapsed.
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H
#define GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H
// cpp_generator.h/.cc do not directly depend on GRPC/ProtoBuf, such that they
// can be used to generate code for other serialization systems, such as
// FlatBuffers.
#include <memory>
#include <vector>
#ifndef GRPC_CUSTOM_STRING
#include <string>
#define GRPC_CUSTOM_STRING std::string
#endif
namespace grpc {
typedef GRPC_CUSTOM_STRING string;
} // namespace grpc
namespace grpc_cpp_generator {
// Contains all the parameters that are parsed from the command line.
struct Parameters {
// Puts the service into a namespace
grpc::string services_namespace;
// Use system includes (<>) or local includes ("")
bool use_system_headers;
// Prefix to any grpc include
grpc::string grpc_search_path;
};
// An abstract interface representing a method.
struct Method {
virtual ~Method() {}
virtual grpc::string name() const = 0;
virtual grpc::string input_type_name() const = 0;
virtual grpc::string output_type_name() const = 0;
virtual bool NoStreaming() const = 0;
virtual bool ClientOnlyStreaming() const = 0;
virtual bool ServerOnlyStreaming() const = 0;
virtual bool BidiStreaming() const = 0;
};
// An abstract interface representing a service.
struct Service {
virtual ~Service() {}
virtual grpc::string name() const = 0;
virtual int method_count() const = 0;
virtual std::unique_ptr<const Method> method(int i) const = 0;
};
struct Printer {
virtual ~Printer() {}
virtual void Print(const std::map<grpc::string, grpc::string> &vars,
const char *template_string) = 0;
virtual void Print(const char *string) = 0;
virtual void Indent() = 0;
virtual void Outdent() = 0;
};
// An interface that allows the source generated to be output using various
// libraries/idls/serializers.
struct File {
virtual ~File() {}
virtual grpc::string filename() const = 0;
virtual grpc::string filename_without_ext() const = 0;
virtual grpc::string message_header_ext() const = 0;
virtual grpc::string service_header_ext() const = 0;
virtual grpc::string package() const = 0;
virtual std::vector<grpc::string> package_parts() const = 0;
virtual grpc::string additional_headers() const = 0;
virtual int service_count() const = 0;
virtual std::unique_ptr<const Service> service(int i) const = 0;
virtual std::unique_ptr<Printer> CreatePrinter(grpc::string *str) const = 0;
};
// Return the prologue of the generated header file.
grpc::string GetHeaderPrologue(File *file, const Parameters &params);
// Return the includes needed for generated header file.
grpc::string GetHeaderIncludes(File *file, const Parameters &params);
// Return the includes needed for generated source file.
grpc::string GetSourceIncludes(File *file, const Parameters &params);
// Return the epilogue of the generated header file.
grpc::string GetHeaderEpilogue(File *file, const Parameters &params);
// Return the prologue of the generated source file.
grpc::string GetSourcePrologue(File *file, const Parameters &params);
// Return the services for generated header file.
grpc::string GetHeaderServices(File *file, const Parameters &params);
// Return the services for generated source file.
grpc::string GetSourceServices(File *file, const Parameters &params);
// Return the epilogue of the generated source file.
grpc::string GetSourceEpilogue(File *file, const Parameters &params);
} // namespace grpc_cpp_generator
#endif // GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <thread>
#include <grpc++/grpc++.h>
#include "monster_test_generated.h"
#include "monster_test.grpc.fb.h"
using namespace MyGame::Example;
// The callback implementation of our server, that derives from the generated
// code. It implements all rpcs specified in the FlatBuffers schema.
class ServiceImpl final : public MyGame::Example::MonsterStorage::Service {
virtual ::grpc::Status Store(::grpc::ServerContext* context,
const flatbuffers::BufferRef<Monster> *request,
flatbuffers::BufferRef<Stat> *response)
override {
// Create a response from the incoming request name.
fbb_.Clear();
auto stat_offset = CreateStat(fbb_, fbb_.CreateString("Hello, " +
request->GetRoot()->name()->str()));
fbb_.Finish(stat_offset);
// Since we keep reusing the same FlatBufferBuilder, the memory it owns
// remains valid until the next call (this BufferRef doesn't own the
// memory it points to).
*response = flatbuffers::BufferRef<Stat>(fbb_.GetBufferPointer(),
fbb_.GetSize());
return grpc::Status::OK;
}
virtual ::grpc::Status Retrieve(::grpc::ServerContext *context,
const flatbuffers::BufferRef<Stat> *request,
flatbuffers::BufferRef<Monster> *response)
override {
assert(false); // We're not actually using this RPC.
return grpc::Status::CANCELLED;
}
private:
flatbuffers::FlatBufferBuilder fbb_;
};
// Track the server instance, so we can terminate it later.
grpc::Server *server_instance = nullptr;
// Mutex to protec this variable.
std::mutex wait_for_server;
std::condition_variable server_instance_cv;
// This function implements the server thread.
void RunServer() {
auto server_address = "0.0.0.0:50051";
// Callback interface we implemented above.
ServiceImpl service;
grpc::ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
// Start the server. Lock to change the variable we're changing.
wait_for_server.lock();
server_instance = builder.BuildAndStart().release();
wait_for_server.unlock();
server_instance_cv.notify_one();
std::cout << "Server listening on " << server_address << std::endl;
// This will block the thread and serve requests.
server_instance->Wait();
}
int main(int /*argc*/, const char * /*argv*/[]) {
// Launch server.
std::thread server_thread(RunServer);
// wait for server to spin up.
std::unique_lock<std::mutex> lock(wait_for_server);
while (!server_instance) server_instance_cv.wait(lock);
// Now connect the client.
auto channel = grpc::CreateChannel("localhost:50051",
grpc::InsecureChannelCredentials());
auto stub = MyGame::Example::MonsterStorage::NewStub(channel);
grpc::ClientContext context;
// Build a request with the name set.
flatbuffers::FlatBufferBuilder fbb;
auto monster_offset = CreateMonster(fbb, 0, 0, 0, fbb.CreateString("Fred"));
fbb.Finish(monster_offset);
auto request = flatbuffers::BufferRef<Monster>(fbb.GetBufferPointer(),
fbb.GetSize());
flatbuffers::BufferRef<Stat> response;
// The actual RPC.
auto status = stub->Store(&context, request, &response);
if (status.ok()) {
auto resp = response.GetRoot()->id();
std::cout << "RPC response: " << resp->str() << std::endl;
} else {
std::cout << "RPC failed" << std::endl;
}
server_instance->Shutdown();
server_thread.join();
delete server_instance;
return 0;
}
......@@ -24,11 +24,12 @@ class BaseGenerator {
virtual bool generate() = 0;
static const std::string NamespaceDir(const Parser &parser,
const std::string &path) {
const std::string &path,
const Namespace &ns) {
EnsureDirExists(path.c_str());
if (parser.opts.one_file) return path;
std::string namespace_dir = path; // Either empty or ends in separator.
auto &namespaces = parser.namespaces_.back()->components;
auto &namespaces = ns.components;
for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
namespace_dir += *it + kPathSeparator;
EnsureDirExists(namespace_dir.c_str());
......@@ -38,17 +39,24 @@ class BaseGenerator {
protected:
BaseGenerator(const Parser &parser, const std::string &path,
const std::string &file_name)
const std::string &file_name,
const std::string qualifying_start,
const std::string qualifying_separator)
: parser_(parser),
path_(path),
file_name_(file_name),
namespace_dir_(BaseGenerator::NamespaceDir(parser, path)){};
qualifying_start_(qualifying_start),
qualifying_separator_(qualifying_separator){};
virtual ~BaseGenerator(){};
// No copy/assign.
BaseGenerator &operator=(const BaseGenerator &);
BaseGenerator(const BaseGenerator &);
const std::string NamespaceDir(const Namespace &ns) {
return BaseGenerator::NamespaceDir(parser_, path_, ns);
}
const char *FlatBuffersGeneratedWarning() {
return "automatically generated by the FlatBuffers compiler,"
" do not modify\n\n";
......@@ -66,9 +74,9 @@ class BaseGenerator {
return true;
}
std::string FullNamespace(const char *separator) {
std::string FullNamespace(const char *separator, const Namespace &ns) {
std::string namespace_name;
auto &namespaces = parser_.namespaces_.back()->components;
auto &namespaces = ns.components;
for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
if (namespace_name.length()) namespace_name += separator;
namespace_name += *it;
......@@ -76,15 +84,36 @@ class BaseGenerator {
return namespace_name;
}
const std::string LastNamespacePart() {
auto &namespaces = parser_.namespaces_.back()->components;
const std::string LastNamespacePart(const Namespace &ns) {
auto &namespaces = ns.components;
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),
// 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) {
if (CurrentNameSpace() == ns) return name;
std::string qualified_name = qualifying_start_;
for (auto it = ns->components.begin(); it != ns->components.end(); ++it)
qualified_name += *it + qualifying_separator_;
return qualified_name + name;
}
std::string WrapInNameSpace(const Definition &def) {
return WrapInNameSpace(def.defined_namespace, def.name);
}
const Parser &parser_;
const std::string &path_;
const std::string &file_name_;
const std::string namespace_dir_;
const std::string qualifying_start_;
const std::string qualifying_separator_;
};
} // namespace flatbuffers
......
......@@ -36,16 +36,17 @@
(!defined(_MSC_VER) || _MSC_VER < 1600) && \
(!defined(__GNUC__) || \
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40400))
#error A C++11 compatible compiler with support for the auto typing is required for FlatBuffers.
#error A C++11 compatible compiler with support for the auto typing is \
required for FlatBuffers.
#error __cplusplus _MSC_VER __GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__
#endif
#if !defined(__clang__) && \
defined(__GNUC__) && \
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40600)
// Backwards compatability for g++ 4.4, and 4.5 which don't have the nullptr and constexpr
// keywords. Note the __clang__ check is needed, because clang presents itself as an older GNUC
// compiler.
// Backwards compatability for g++ 4.4, and 4.5 which don't have the nullptr
// and constexpr keywords. Note the __clang__ check is needed, because clang
// presents itself as an older GNUC compiler.
#ifndef nullptr_t
const class nullptr_t {
public:
......@@ -207,6 +208,7 @@ template<typename T> size_t AlignOf() {
// (avoiding the need for a trailing return decltype)
template<typename T> struct IndirectHelper {
typedef T return_type;
typedef T mutable_return_type;
static const size_t element_stride = sizeof(T);
static return_type Read(const uint8_t *p, uoffset_t i) {
return EndianScalar((reinterpret_cast<const T *>(p))[i]);
......@@ -214,6 +216,7 @@ template<typename T> struct IndirectHelper {
};
template<typename T> struct IndirectHelper<Offset<T>> {
typedef const T *return_type;
typedef T *mutable_return_type;
static const size_t element_stride = sizeof(uoffset_t);
static return_type Read(const uint8_t *p, uoffset_t i) {
p += i * sizeof(uoffset_t);
......@@ -222,6 +225,7 @@ template<typename T> struct IndirectHelper<Offset<T>> {
};
template<typename T> struct IndirectHelper<const T *> {
typedef const T *return_type;
typedef T *mutable_return_type;
static const size_t element_stride = sizeof(T);
static return_type Read(const uint8_t *p, uoffset_t i) {
return reinterpret_cast<const T *>(p + i * sizeof(T));
......@@ -306,6 +310,7 @@ public:
uoffset_t Length() const { return size(); }
typedef typename IndirectHelper<T>::return_type return_type;
typedef typename IndirectHelper<T>::mutable_return_type mutable_return_type;
return_type Get(uoffset_t i) const {
assert(i < size());
......@@ -347,6 +352,12 @@ public:
WriteScalar(data() + i, val - (Data() + i * sizeof(uoffset_t)));
}
// Get a mutable pointer to tables/strings inside this vector.
mutable_return_type GetMutableObject(uoffset_t i) const {
assert(i < size());
return const_cast<mutable_return_type>(IndirectHelper<T>::Read(Data(), i));
}
// The raw data in little endian format. Use with care.
const uint8_t *Data() const {
return reinterpret_cast<const uint8_t *>(&length_ + 1);
......@@ -973,6 +984,33 @@ FLATBUFFERS_FINAL_CLASS
return CreateVector(v.data(), v.size());
}
/// @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.
/// @param f A function that takes the current iteration 0..vector_size-1 and
/// returns any type that you can construct a FlatBuffers vector out of.
/// @return Returns a typed `Offset` into the serialized data indicating
/// where the vector is stored.
template<typename T> Offset<Vector<T>> CreateVector(size_t vector_size,
const std::function<T (size_t i)> &f) {
std::vector<T> elems(vector_size);
for (size_t i = 0; i < vector_size; i++) elems[i] = f(i);
return CreateVector(elems.data(), elems.size());
}
/// @brief Serialize a `std::vector<std::string>` into a FlatBuffer `vector`.
/// This is a convenience function for a common case.
/// @param v A const reference to the `std::vector` to serialize into the
/// buffer as a `vector`.
/// @return Returns a typed `Offset` into the serialized data indicating
/// where the vector is stored.
Offset<Vector<Offset<String>>> CreateVectorOfStrings(
const std::vector<std::string> &v) {
std::vector<Offset<String>> offsets(v.size());
for (size_t i = 0; i < v.size(); i++) offsets[i] = CreateString(v[i]);
return CreateVector(offsets.data(), offsets.size());
}
/// @brief Serialize an array of structs into a FlatBuffer `vector`.
/// @tparam T The data type of the struct array elements.
/// @param[in] v A pointer to the array of type `T` to serialize into the
......@@ -981,7 +1019,7 @@ FLATBUFFERS_FINAL_CLASS
/// @return Returns a typed `Offset` into the serialized data indicating
/// where the vector is stored.
template<typename T> Offset<Vector<const T *>> CreateVectorOfStructs(
const T *v, size_t len) {
const T *v, size_t len) {
StartVector(len * sizeof(T) / AlignOf<T>(), AlignOf<T>());
PushBytes(reinterpret_cast<const uint8_t *>(v), sizeof(T) * len);
return Offset<Vector<const T *>>(EndVector(len));
......@@ -994,7 +1032,7 @@ FLATBUFFERS_FINAL_CLASS
/// @return Returns a typed `Offset` into the serialized data indicating
/// where the vector is stored.
template<typename T> Offset<Vector<const T *>> CreateVectorOfStructs(
const std::vector<T> &v) {
const std::vector<T> &v) {
return CreateVectorOfStructs(v.data(), v.size());
}
......@@ -1023,7 +1061,7 @@ FLATBUFFERS_FINAL_CLASS
/// @return Returns a typed `Offset` into the serialized data indicating
/// where the vector is stored.
template<typename T> Offset<Vector<Offset<T>>> CreateVectorOfSortedTables(
Offset<T> *v, size_t len) {
Offset<T> *v, size_t len) {
std::sort(v, v + len, TableKeyComparator<T>(buf_));
return CreateVector(v, len);
}
......@@ -1036,7 +1074,7 @@ FLATBUFFERS_FINAL_CLASS
/// @return Returns a typed `Offset` into the serialized data indicating
/// where the vector is stored.
template<typename T> Offset<Vector<Offset<T>>> CreateVectorOfSortedTables(
std::vector<Offset<T>> *v) {
std::vector<Offset<T>> *v) {
return CreateVectorOfSortedTables(v->data(), v->size());
}
......@@ -1067,7 +1105,7 @@ FLATBUFFERS_FINAL_CLASS
/// written to at a later time to serialize the data into a `vector`
/// in the buffer.
template<typename T> Offset<Vector<T>> CreateUninitializedVector(
size_t len, T **buf) {
size_t len, T **buf) {
return CreateUninitializedVector(len, sizeof(T),
reinterpret_cast<uint8_t **>(buf));
}
......@@ -1152,13 +1190,16 @@ template<typename T> const T *GetRoot(const void *buf) {
}
/// Helpers to get a typed pointer to objects that are currently beeing built.
/// @warning Creating new objects will lead to reallocations and invalidates the pointer!
template<typename T> T *GetMutableTemporaryPointer(FlatBufferBuilder &fbb, Offset<T> offset) {
/// @warning Creating new objects will lead to reallocations and invalidates
/// the pointer!
template<typename T> T *GetMutableTemporaryPointer(FlatBufferBuilder &fbb,
Offset<T> offset) {
return reinterpret_cast<T *>(fbb.GetCurrentBufferPointer() +
fbb.GetSize() - offset.o);
}
template<typename T> const T *GetTemporaryPointer(FlatBufferBuilder &fbb, Offset<T> offset) {
template<typename T> const T *GetTemporaryPointer(FlatBufferBuilder &fbb,
Offset<T> offset) {
return GetMutableTemporaryPointer<T>(fbb, offset);
}
......@@ -1293,6 +1334,29 @@ class Verifier FLATBUFFERS_FINAL_CLASS {
size_t max_tables_;
};
// Convenient way to bundle a buffer and its length, to pass it around
// typed by its root.
// A BufferRef does not own its buffer.
struct BufferRefBase {}; // for std::is_base_of
template<typename T> struct BufferRef : BufferRefBase {
BufferRef() : buf(nullptr), len(0), must_free(false) {}
BufferRef(uint8_t *_buf, uoffset_t _len)
: buf(_buf), len(_len), must_free(false) {}
~BufferRef() { if (must_free) free(buf); }
const T *GetRoot() const { return flatbuffers::GetRoot<T>(buf); }
bool Verify() {
Verifier verifier(buf, len);
return verifier.VerifyBuffer<T>();
}
uint8_t *buf;
uoffset_t len;
bool must_free;
};
// "structs" are flat structures that do not have an offset table, thus
// always have all members present and do not support forwards/backwards
// compatible extensions.
......
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FLATBUFFERS_GRPC_H_
#define FLATBUFFERS_GRPC_H_
// Helper functionality to glue FlatBuffers and GRPC.
#include "grpc++/support/byte_buffer.h"
#include "grpc/byte_buffer_reader.h"
namespace grpc {
template <class T>
class SerializationTraits<T, typename std::enable_if<std::is_base_of<
flatbuffers::BufferRefBase, T>::value>::type> {
public:
// The type we're passing here is a BufferRef, which is already serialized
// FlatBuffer data, which then gets passed to GRPC.
static grpc::Status Serialize(const T& msg,
grpc_byte_buffer **buffer,
bool *own_buffer) {
// TODO(wvo): make this work without copying.
auto slice = gpr_slice_from_copied_buffer(
reinterpret_cast<const char *>(msg.buf), msg.len);
*buffer = grpc_raw_byte_buffer_create(&slice, 1);
*own_buffer = true;
return grpc::Status();
}
// There is no de-serialization step in FlatBuffers, so we just receive
// the data from GRPC.
static grpc::Status Deserialize(grpc_byte_buffer *buffer,
T *msg,
int max_message_size) {
// TODO(wvo): make this more efficient / zero copy when possible.
auto len = grpc_byte_buffer_length(buffer);
msg->buf = reinterpret_cast<uint8_t *>(malloc(len));
msg->len = static_cast<flatbuffers::uoffset_t>(len);
msg->must_free = true;
uint8_t *current = msg->buf;
grpc_byte_buffer_reader reader;
grpc_byte_buffer_reader_init(&reader, buffer);
gpr_slice slice;
while (grpc_byte_buffer_reader_next(&reader, &slice)) {
memcpy(current, GPR_SLICE_START_PTR(slice), GPR_SLICE_LENGTH(slice));
current += GPR_SLICE_LENGTH(slice);
gpr_slice_unref(slice);
}
GPR_ASSERT(current == msg->buf + msg->len);
grpc_byte_buffer_reader_destroy(&reader);
grpc_byte_buffer_destroy(buffer);
return grpc::Status();
}
};
} // namespace grpc;
#endif // FLATBUFFERS_GRPC_H_
......@@ -359,6 +359,19 @@ struct IDLOptions {
lang(IDLOptions::kJava) {}
};
// This encapsulates where the parser is in the current source file.
struct ParserState {
ParserState() : cursor_(nullptr), line_(1), token_(-1) {}
protected:
const char *cursor_;
int line_; // the current line being parsed
int token_;
std::string attribute_;
std::vector<std::string> doc_comment_;
};
// A way to make error propagation less error prone by requiring values to be
// checked.
// Once you create a value of this type you must either:
......@@ -400,14 +413,12 @@ class CheckedError {
#define FLATBUFFERS_CHECKED_ERROR CheckedError
#endif
class Parser {
class Parser : public ParserState {
public:
explicit Parser(const IDLOptions &options = IDLOptions())
: root_struct_def_(nullptr),
opts(options),
source_(nullptr),
cursor_(nullptr),
line_(1),
anonymous_counter(0) {
// Just in case none are declared:
namespaces_.push_back(new Namespace());
......@@ -421,7 +432,7 @@ class Parser {
known_attributes_["original_order"] = true;
known_attributes_["nested_flatbuffer"] = true;
known_attributes_["csharp_partial"] = true;
known_attributes_["stream"] = true;
known_attributes_["streaming"] = true;
known_attributes_["idempotent"] = true;
}
......@@ -478,7 +489,8 @@ private:
FieldDef **dest);
FLATBUFFERS_CHECKED_ERROR ParseField(StructDef &struct_def);
FLATBUFFERS_CHECKED_ERROR ParseAnyValue(Value &val, FieldDef *field,
size_t parent_fieldn);
size_t parent_fieldn,
const StructDef *parent_struct_def);
FLATBUFFERS_CHECKED_ERROR ParseTable(const StructDef &struct_def,
std::string *value, uoffset_t *ovalue);
void SerializeStruct(const StructDef &struct_def, const Value &val);
......@@ -538,13 +550,9 @@ private:
IDLOptions opts;
private:
const char *source_, *cursor_;
int line_; // the current line being parsed
int token_;
std::string file_being_parsed_;
const char *source_;
std::string attribute_;
std::vector<std::string> doc_comment_;
std::string file_being_parsed_;
std::vector<std::pair<Value, FieldDef *>> field_stack_;
......@@ -671,6 +679,12 @@ extern std::string BinaryMakeRule(const Parser &parser,
const std::string &path,
const std::string &file_name);
// Generate GRPC interfaces.
// See idl_gen_grpc.cpp.
bool GenerateGRPC(const Parser &parser,
const std::string &path,
const std::string &file_name);
} // namespace flatbuffers
#endif // FLATBUFFERS_IDL_H_
......
......@@ -335,6 +335,8 @@ template<typename T, typename U> pointer_inside_vector<T, U> piv(T *ptr,
return pointer_inside_vector<T, U>(ptr, vec);
}
inline const char *UnionTypeFieldSuffix() { return "_type"; }
// Helper to figure out the actual table type a union refers to.
inline const reflection::Object &GetUnionType(
const reflection::Schema &schema, const reflection::Object &parent,
......@@ -342,7 +344,7 @@ inline const reflection::Object &GetUnionType(
auto enumdef = schema.enums()->Get(unionfield.type()->index());
// TODO: this is clumsy and slow, but no other way to find it?
auto type_field = parent.fields()->LookupByKey(
(unionfield.name()->str() + "_type").c_str());
(unionfield.name()->str() + UnionTypeFieldSuffix()).c_str());
assert(type_field);
auto union_type = GetFieldI<uint8_t>(table, *type_field);
auto enumval = enumdef->values()->LookupByKey(union_type);
......
......@@ -35,9 +35,10 @@
#include <winbase.h>
#include <direct.h>
#else
#include <sys/stat.h>
#include <limits.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include "flatbuffers/flatbuffers.h"
......@@ -124,6 +125,9 @@ FileExistsFunction SetFileExistsFunction(FileExistsFunction
// Check if file "name" exists.
bool FileExists(const char *name);
// Check if "name" exists and it is also a directory.
bool DirExists(const char *name);
// Load file "name" into "buf" returning true if successful
// false otherwise. If "binary" is false data is read
// using ifstream's text mode, otherwise data is read with
......
......@@ -23,10 +23,18 @@ package com.google.flatbuffers;
*/
public class Constants {
// Java doesn't seem to have these.
/** The number of bytes in an `byte`. */
static final int SIZEOF_BYTE = 1;
/** The number of bytes in a `short`. */
static final int SIZEOF_SHORT = 2;
/** The number of bytes in an `int`. */
static final int SIZEOF_INT = 4;
/** The number of bytes in an `float`. */
static final int SIZEOF_FLOAT = 4;
/** The number of bytes in an `long`. */
static final int SIZEOF_LONG = 8;
/** The number of bytes in an `double`. */
static final int SIZEOF_DOUBLE = 8;
/** The number of bytes in a file identifier. */
static final int FILE_IDENTIFIER_LENGTH = 4;
}
......
......@@ -188,7 +188,7 @@ public class FlatBufferBuilder {
*
* @param x A `boolean` to put into the buffer.
*/
public void putBoolean(boolean x) { bb.put (space -= 1, (byte)(x ? 1 : 0)); }
public void putBoolean(boolean x) { bb.put (space -= Constants.SIZEOF_BYTE, (byte)(x ? 1 : 0)); }
/**
* Add a `byte` to the buffer, backwards from the current location. Doesn't align nor
......@@ -196,7 +196,7 @@ public class FlatBufferBuilder {
*
* @param x A `byte` to put into the buffer.
*/
public void putByte (byte x) { bb.put (space -= 1, x); }
public void putByte (byte x) { bb.put (space -= Constants.SIZEOF_BYTE, x); }
/**
* Add a `short` to the buffer, backwards from the current location. Doesn't align nor
......@@ -204,7 +204,7 @@ public class FlatBufferBuilder {
*
* @param x A `short` to put into the buffer.
*/
public void putShort (short x) { bb.putShort (space -= 2, x); }
public void putShort (short x) { bb.putShort (space -= Constants.SIZEOF_SHORT, x); }
/**
* Add an `int` to the buffer, backwards from the current location. Doesn't align nor
......@@ -212,7 +212,7 @@ public class FlatBufferBuilder {
*
* @param x An `int` to put into the buffer.
*/
public void putInt (int x) { bb.putInt (space -= 4, x); }
public void putInt (int x) { bb.putInt (space -= Constants.SIZEOF_INT, x); }
/**
* Add a `long` to the buffer, backwards from the current location. Doesn't align nor
......@@ -220,7 +220,7 @@ public class FlatBufferBuilder {
*
* @param x A `long` to put into the buffer.
*/
public void putLong (long x) { bb.putLong (space -= 8, x); }
public void putLong (long x) { bb.putLong (space -= Constants.SIZEOF_LONG, x); }
/**
* Add a `float` to the buffer, backwards from the current location. Doesn't align nor
......@@ -228,7 +228,7 @@ public class FlatBufferBuilder {
*
* @param x A `float` to put into the buffer.
*/
public void putFloat (float x) { bb.putFloat (space -= 4, x); }
public void putFloat (float x) { bb.putFloat (space -= Constants.SIZEOF_FLOAT, x); }
/**
* Add a `double` to the buffer, backwards from the current location. Doesn't align nor
......@@ -236,7 +236,7 @@ public class FlatBufferBuilder {
*
* @param x A `double` to put into the buffer.
*/
public void putDouble (double x) { bb.putDouble(space -= 8, x); }
public void putDouble (double x) { bb.putDouble(space -= Constants.SIZEOF_DOUBLE, x); }
/// @endcond
/**
......@@ -244,49 +244,49 @@ public class FlatBufferBuilder {
*
* @param x A `boolean` to put into the buffer.
*/
public void addBoolean(boolean x) { prep(1, 0); putBoolean(x); }
public void addBoolean(boolean x) { prep(Constants.SIZEOF_BYTE, 0); putBoolean(x); }
/**
* Add a `byte` to the buffer, properly aligned, and grows the buffer (if necessary).
*
* @param x A `byte` to put into the buffer.
*/
public void addByte (byte x) { prep(1, 0); putByte (x); }
public void addByte (byte x) { prep(Constants.SIZEOF_BYTE, 0); putByte (x); }
/**
* Add a `short` to the buffer, properly aligned, and grows the buffer (if necessary).
*
* @param x A `short` to put into the buffer.
*/
public void addShort (short x) { prep(2, 0); putShort (x); }
public void addShort (short x) { prep(Constants.SIZEOF_SHORT, 0); putShort (x); }
/**
* Add an `int` to the buffer, properly aligned, and grows the buffer (if necessary).
*
* @param x An `int` to put into the buffer.
*/
public void addInt (int x) { prep(4, 0); putInt (x); }
public void addInt (int x) { prep(Constants.SIZEOF_INT, 0); putInt (x); }
/**
* Add a `long` to the buffer, properly aligned, and grows the buffer (if necessary).
*
* @param x A `long` to put into the buffer.
*/
public void addLong (long x) { prep(8, 0); putLong (x); }
public void addLong (long x) { prep(Constants.SIZEOF_LONG, 0); putLong (x); }
/**
* Add a `float` to the buffer, properly aligned, and grows the buffer (if necessary).
*
* @param x A `float` to put into the buffer.
*/
public void addFloat (float x) { prep(4, 0); putFloat (x); }
public void addFloat (float x) { prep(Constants.SIZEOF_FLOAT, 0); putFloat (x); }
/**
* Add a `double` to the buffer, properly aligned, and grows the buffer (if necessary).
*
* @param x A `double` to put into the buffer.
*/
public void addDouble (double x) { prep(8, 0); putDouble (x); }
public void addDouble (double x) { prep(Constants.SIZEOF_DOUBLE, 0); putDouble (x); }
/**
* Adds on offset, relative to where it will be written.
......
......@@ -18,5 +18,5 @@ APP_PROJECT_PATH := $(call my-dir)/..
APP_STL := gnustl_static
APP_ABI := armeabi-v7a
NDK_TOOLCHAIN_VERSION := 4.8
APP_CPPFLAGS += -std=c++11
......@@ -74,10 +74,14 @@ const Generator generators[] = {
flatbuffers::IDLOptions::kMAX,
"Generate Python files for tables/structs",
flatbuffers::GeneralMakeRule },
{ flatbuffers::GeneratePhp, nullptr, "--php", "PHP",
{ flatbuffers::GeneratePhp, nullptr, "--php", "PHP",
flatbuffers::IDLOptions::kMAX,
"Generate PHP files for tables/structs",
flatbuffers::GeneralMakeRule },
{ flatbuffers::GenerateGRPC, nullptr, "--grpc", "GRPC",
flatbuffers::IDLOptions::kMAX,
"Generate GRPC interfaces",
flatbuffers::CPPMakeRule },
};
const char *program_name = nullptr;
......
This diff is collapsed.
This diff is collapsed.
......@@ -625,7 +625,8 @@ class GoGenerator : public BaseGenerator {
public:
GoGenerator(const Parser &parser, const std::string &path,
const std::string &file_name)
: BaseGenerator(parser, path, file_name){};
: BaseGenerator(parser, path, file_name, "" /* not used*/,
"" /* not used */){};
bool generate() {
for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
++it) {
......@@ -664,9 +665,10 @@ class GoGenerator : public BaseGenerator {
if (!classcode.length()) return true;
std::string code = "";
BeginFile(LastNamespacePart(), needs_imports, &code);
BeginFile(LastNamespacePart(*def.defined_namespace), needs_imports, &code);
code += classcode;
std::string filename = namespace_dir_ + def.name + ".go";
std::string filename =
NamespaceDir(*def.defined_namespace) + def.name + ".go";
return SaveFile(filename.c_str(), code, false);
}
};
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -596,7 +596,8 @@ class PythonGenerator : public BaseGenerator {
public:
PythonGenerator(const Parser &parser, const std::string &path,
const std::string &file_name)
: BaseGenerator(parser, path, file_name){};
: BaseGenerator(parser, path, file_name, "" /* not used */,
"" /* not used */){};
bool generate() {
if (!generateEnums()) return false;
if (!generateStructs()) return false;
......@@ -652,9 +653,10 @@ class PythonGenerator : public BaseGenerator {
}
std::string code = "";
BeginFile(LastNamespacePart(), needs_imports, &code);
BeginFile(LastNamespacePart(*def.defined_namespace), needs_imports, &code);
code += classcode;
std::string filename = namespace_dir_ + kPathSeparator + def.name + ".py";
std::string filename = NamespaceDir(*def.defined_namespace) +
kPathSeparator + def.name + ".py";
return SaveFile(filename.c_str(), code, false);
}
};
......
This diff is collapsed.
......@@ -24,12 +24,14 @@ bool FileExistsRaw(const char *name) {
}
bool LoadFileRaw(const char *name, bool binary, std::string *buf) {
if (DirExists(name)) return false;
std::ifstream ifs(name, binary ? std::ifstream::binary : std::ifstream::in);
if (!ifs.is_open()) return false;
if (binary) {
// The fastest way to read a file into a string.
ifs.seekg(0, std::ios::end);
(*buf).resize(static_cast<size_t>(ifs.tellg()));
auto size = ifs.tellg();
(*buf).resize(static_cast<size_t>(size));
ifs.seekg(0, std::ios::beg);
ifs.read(&(*buf)[0], (*buf).size());
} else {
......@@ -54,6 +56,19 @@ bool FileExists(const char *name) {
return g_file_exists_function(name);
}
bool DirExists(const char *name) {
#ifdef _WIN32
#define flatbuffers_stat _stat
#define FLATBUFFERS_S_IFDIR _S_IFDIR
#else
#define flatbuffers_stat stat
#define FLATBUFFERS_S_IFDIR S_IFDIR
#endif
struct flatbuffers_stat file_info;
if (flatbuffers_stat(name, &file_info) != 0) return false;
return (file_info.st_mode & FLATBUFFERS_S_IFDIR) != 0;
}
LoadFileFunction SetLoadFileFunction(LoadFileFunction load_file_function) {
LoadFileFunction previous_function = g_load_file_function;
g_load_file_function = load_file_function ? load_file_function : LoadFileRaw;
......
......@@ -8,6 +8,7 @@ public enum Any : byte
NONE = 0,
Monster = 1,
TestSimpleTableWithEnum = 2,
MyGame_Example2_Monster = 3,
};
......
......@@ -6,4 +6,5 @@ const (
AnyNONE = 0
AnyMonster = 1
AnyTestSimpleTableWithEnum = 2
AnyMyGame_Example2_Monster = 3
)
......@@ -7,8 +7,9 @@ public final class Any {
public static final byte NONE = 0;
public static final byte Monster = 1;
public static final byte TestSimpleTableWithEnum = 2;
public static final byte MyGame_Example2_Monster = 3;
private static final String[] names = { "NONE", "Monster", "TestSimpleTableWithEnum", };
private static final String[] names = { "NONE", "Monster", "TestSimpleTableWithEnum", "MyGame_Example2_Monster", };
public static String name(int e) { return names[e]; }
};
......
......@@ -8,11 +8,13 @@ class Any
const NONE = 0;
const Monster = 1;
const TestSimpleTableWithEnum = 2;
const MyGame_Example2_Monster = 3;
private static $names = array(
"NONE",
"Monster",
"TestSimpleTableWithEnum",
"MyGame_Example2_Monster",
);
public static function Name($e)
......
......@@ -6,4 +6,5 @@ class Any(object):
NONE = 0
Monster = 1
TestSimpleTableWithEnum = 2
MyGame_Example2_Monster = 3
......@@ -78,8 +78,10 @@ public sealed class Monster : Table {
public bool MutateTestf2(float testf2) { int o = __offset(56); if (o != 0) { bb.PutFloat(o + bb_pos, testf2); return true; } else { return false; } }
public float Testf3 { get { int o = __offset(58); return o != 0 ? bb.GetFloat(o + bb_pos) : (float)0.0f; } }
public bool MutateTestf3(float testf3) { int o = __offset(58); if (o != 0) { bb.PutFloat(o + bb_pos, testf3); return true; } else { return false; } }
public string GetTestarrayofstring2(int j) { int o = __offset(60); return o != 0 ? __string(__vector(o) + j * 4) : null; }
public int Testarrayofstring2Length { get { int o = __offset(60); return o != 0 ? __vector_len(o) : 0; } }
public static void StartMonster(FlatBufferBuilder builder) { builder.StartObject(28); }
public static void StartMonster(FlatBufferBuilder builder) { builder.StartObject(29); }
public static void AddPos(FlatBufferBuilder builder, Offset<Vec3> posOffset) { builder.AddStruct(0, posOffset.Value, 0); }
public static void AddMana(FlatBufferBuilder builder, short mana) { builder.AddShort(1, mana, 150); }
public static void AddHp(FlatBufferBuilder builder, short hp) { builder.AddShort(2, hp, 100); }
......@@ -118,6 +120,9 @@ public sealed class Monster : Table {
public static void AddTestf(FlatBufferBuilder builder, float testf) { builder.AddFloat(25, testf, 3.14159f); }
public static void AddTestf2(FlatBufferBuilder builder, float testf2) { builder.AddFloat(26, testf2, 3.0f); }
public static void AddTestf3(FlatBufferBuilder builder, float testf3) { builder.AddFloat(27, testf3, 0.0f); }
public static void AddTestarrayofstring2(FlatBufferBuilder builder, VectorOffset testarrayofstring2Offset) { builder.AddOffset(28, testarrayofstring2Offset.Value, 0); }
public static VectorOffset CreateTestarrayofstring2Vector(FlatBufferBuilder builder, StringOffset[] data) { builder.StartVector(4, data.Length, 4); for (int i = data.Length - 1; i >= 0; i--) builder.AddOffset(data[i].Value); return builder.EndVector(); }
public static void StartTestarrayofstring2Vector(FlatBufferBuilder builder, int numElems) { builder.StartVector(4, numElems, 4); }
public static Offset<Monster> EndMonster(FlatBufferBuilder builder) {
int o = builder.EndObject();
builder.Required(o, 10); // name
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// automatically generated by the FlatBuffers compiler, do not modify
package Example2
import (
flatbuffers "github.com/google/flatbuffers/go"
)
type Monster struct {
_tab flatbuffers.Table
}
func (rcv *Monster) Init(buf []byte, i flatbuffers.UOffsetT) {
rcv._tab.Bytes = buf
rcv._tab.Pos = i
}
func MonsterStart(builder *flatbuffers.Builder) { builder.StartObject(0) }
func MonsterEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT { return builder.EndObject() }
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
No preview for this file type
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment