Squashed 'third_party/flatbuffers/' content from commit acc9990ab
Change-Id: I48550d40d78fea996ebe74e9723a5d1f910de491
git-subtree-dir: third_party/flatbuffers
git-subtree-split: acc9990abd2206491480291b0f85f925110102ea
diff --git a/src/clang-format.sh b/src/clang-format.sh
new file mode 100644
index 0000000..fbce6b9
--- /dev/null
+++ b/src/clang-format.sh
@@ -0,0 +1,2 @@
+clang-format -i -style=file include/flatbuffers/* src/*.cpp tests/test.cpp samples/*.cpp grpc/src/compiler/schema_interface.h grpc/tests/*.cpp
+git checkout include/flatbuffers/reflection_generated.h
diff --git a/src/code_generators.cpp b/src/code_generators.cpp
new file mode 100644
index 0000000..52ca305
--- /dev/null
+++ b/src/code_generators.cpp
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2016 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "flatbuffers/code_generators.h"
+#include <assert.h>
+#include "flatbuffers/base.h"
+#include "flatbuffers/util.h"
+
+#include <cmath>
+
+#if defined(_MSC_VER)
+# pragma warning(push)
+# pragma warning(disable : 4127) // C4127: conditional expression is constant
+#endif
+
+namespace flatbuffers {
+
+void CodeWriter::operator+=(std::string text) {
+ if (!ignore_ident_ && !text.empty()) AppendIdent(stream_);
+
+ while (true) {
+ auto begin = text.find("{{");
+ if (begin == std::string::npos) { break; }
+
+ auto end = text.find("}}");
+ if (end == std::string::npos || end < begin) { break; }
+
+ // Write all the text before the first {{ into the stream.
+ stream_.write(text.c_str(), begin);
+
+ // The key is between the {{ and }}.
+ const std::string key = text.substr(begin + 2, end - begin - 2);
+
+ // Find the value associated with the key. If it exists, write the
+ // value into the stream, otherwise write the key itself into the stream.
+ auto iter = value_map_.find(key);
+ if (iter != value_map_.end()) {
+ const std::string &value = iter->second;
+ stream_ << value;
+ } else {
+ FLATBUFFERS_ASSERT(false && "could not find key");
+ stream_ << key;
+ }
+
+ // Update the text to everything after the }}.
+ text = text.substr(end + 2);
+ }
+ if (!text.empty() && string_back(text) == '\\') {
+ text.pop_back();
+ ignore_ident_ = true;
+ stream_ << text;
+ } else {
+ ignore_ident_ = false;
+ stream_ << text << std::endl;
+ }
+}
+
+void CodeWriter::AppendIdent(std::stringstream &stream) {
+ int lvl = cur_ident_lvl_;
+ while (lvl--) {
+ stream.write(pad_.c_str(), static_cast<std::streamsize>(pad_.size()));
+ }
+}
+
+const char *BaseGenerator::FlatBuffersGeneratedWarning() {
+ return "automatically generated by the FlatBuffers compiler,"
+ " do not modify";
+}
+
+std::string BaseGenerator::NamespaceDir(const Parser &parser,
+ const std::string &path,
+ const Namespace &ns) {
+ EnsureDirExists(path);
+ if (parser.opts.one_file) return path;
+ std::string namespace_dir = path; // Either empty or ends in separator.
+ auto &namespaces = ns.components;
+ for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
+ namespace_dir += *it + kPathSeparator;
+ EnsureDirExists(namespace_dir);
+ }
+ return namespace_dir;
+}
+
+std::string BaseGenerator::NamespaceDir(const Namespace &ns) const {
+ return BaseGenerator::NamespaceDir(parser_, path_, ns);
+}
+
+std::string BaseGenerator::FullNamespace(const char *separator,
+ const Namespace &ns) {
+ std::string namespace_name;
+ auto &namespaces = ns.components;
+ for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
+ if (namespace_name.length()) namespace_name += separator;
+ namespace_name += *it;
+ }
+ return namespace_name;
+}
+
+std::string BaseGenerator::LastNamespacePart(const Namespace &ns) {
+ if (!ns.components.empty())
+ return ns.components.back();
+ else
+ return std::string("");
+}
+
+// Ensure that a type is prefixed with its namespace.
+std::string BaseGenerator::WrapInNameSpace(const Namespace *ns,
+ const std::string &name) const {
+ 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 BaseGenerator::WrapInNameSpace(const Definition &def) const {
+ return WrapInNameSpace(def.defined_namespace, def.name);
+}
+
+std::string BaseGenerator::GetNameSpace(const Definition &def) const {
+ const Namespace *ns = def.defined_namespace;
+ if (CurrentNameSpace() == ns) return "";
+ std::string qualified_name = qualifying_start_;
+ for (auto it = ns->components.begin(); it != ns->components.end(); ++it) {
+ qualified_name += *it;
+ if ((it + 1) != ns->components.end()) {
+ qualified_name += qualifying_separator_;
+ }
+ }
+
+ return qualified_name;
+}
+
+// Generate a documentation comment, if available.
+void GenComment(const std::vector<std::string> &dc, std::string *code_ptr,
+ const CommentConfig *config, const char *prefix) {
+ if (dc.begin() == dc.end()) {
+ // Don't output empty comment blocks with 0 lines of comment content.
+ return;
+ }
+
+ std::string &code = *code_ptr;
+ if (config != nullptr && config->first_line != nullptr) {
+ code += std::string(prefix) + std::string(config->first_line) + "\n";
+ }
+ std::string line_prefix =
+ std::string(prefix) +
+ ((config != nullptr && config->content_line_prefix != nullptr)
+ ? config->content_line_prefix
+ : "///");
+ for (auto it = dc.begin(); it != dc.end(); ++it) {
+ code += line_prefix + *it + "\n";
+ }
+ if (config != nullptr && config->last_line != nullptr) {
+ code += std::string(prefix) + std::string(config->last_line) + "\n";
+ }
+}
+
+template<typename T>
+std::string FloatConstantGenerator::GenFloatConstantImpl(
+ const FieldDef &field) const {
+ const auto &constant = field.value.constant;
+ T v;
+ auto done = StringToNumber(constant.c_str(), &v);
+ FLATBUFFERS_ASSERT(done);
+ if (done) {
+#if (!defined(_MSC_VER) || (_MSC_VER >= 1800))
+ if (std::isnan(v)) return NaN(v);
+ if (std::isinf(v)) return Inf(v);
+#endif
+ return Value(v, constant);
+ }
+ return "#"; // compile time error
+}
+
+std::string FloatConstantGenerator::GenFloatConstant(
+ const FieldDef &field) const {
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_FLOAT: return GenFloatConstantImpl<float>(field);
+ case BASE_TYPE_DOUBLE: return GenFloatConstantImpl<double>(field);
+ default: {
+ FLATBUFFERS_ASSERT(false);
+ return "INVALID_BASE_TYPE";
+ }
+ };
+}
+
+TypedFloatConstantGenerator::TypedFloatConstantGenerator(
+ const char *double_prefix, const char *single_prefix,
+ const char *nan_number, const char *pos_inf_number,
+ const char *neg_inf_number)
+ : double_prefix_(double_prefix),
+ single_prefix_(single_prefix),
+ nan_number_(nan_number),
+ pos_inf_number_(pos_inf_number),
+ neg_inf_number_(neg_inf_number) {}
+
+std::string TypedFloatConstantGenerator::MakeNaN(
+ const std::string &prefix) const {
+ return prefix + nan_number_;
+}
+std::string TypedFloatConstantGenerator::MakeInf(
+ bool neg, const std::string &prefix) const {
+ if (neg)
+ return !neg_inf_number_.empty() ? (prefix + neg_inf_number_)
+ : ("-" + prefix + pos_inf_number_);
+ else
+ return prefix + pos_inf_number_;
+}
+
+std::string TypedFloatConstantGenerator::Value(double v,
+ const std::string &src) const {
+ (void)v;
+ return src;
+}
+
+std::string TypedFloatConstantGenerator::Inf(double v) const {
+ return MakeInf(v < 0, double_prefix_);
+}
+
+std::string TypedFloatConstantGenerator::NaN(double v) const {
+ (void)v;
+ return MakeNaN(double_prefix_);
+}
+
+std::string TypedFloatConstantGenerator::Value(float v,
+ const std::string &src) const {
+ (void)v;
+ return src + "f";
+}
+
+std::string TypedFloatConstantGenerator::Inf(float v) const {
+ return MakeInf(v < 0, single_prefix_);
+}
+
+std::string TypedFloatConstantGenerator::NaN(float v) const {
+ (void)v;
+ return MakeNaN(single_prefix_);
+}
+
+SimpleFloatConstantGenerator::SimpleFloatConstantGenerator(
+ const char *nan_number, const char *pos_inf_number,
+ const char *neg_inf_number)
+ : nan_number_(nan_number),
+ pos_inf_number_(pos_inf_number),
+ neg_inf_number_(neg_inf_number) {}
+
+std::string SimpleFloatConstantGenerator::Value(double v,
+ const std::string &src) const {
+ (void)v;
+ return src;
+}
+
+std::string SimpleFloatConstantGenerator::Inf(double v) const {
+ return (v < 0) ? neg_inf_number_ : pos_inf_number_;
+}
+
+std::string SimpleFloatConstantGenerator::NaN(double v) const {
+ (void)v;
+ return nan_number_;
+}
+
+std::string SimpleFloatConstantGenerator::Value(float v,
+ const std::string &src) const {
+ return this->Value(static_cast<double>(v), src);
+}
+
+std::string SimpleFloatConstantGenerator::Inf(float v) const {
+ return this->Inf(static_cast<double>(v));
+}
+
+std::string SimpleFloatConstantGenerator::NaN(float v) const {
+ return this->NaN(static_cast<double>(v));
+}
+
+} // namespace flatbuffers
+
+#if defined(_MSC_VER)
+# pragma warning(pop)
+#endif
diff --git a/src/flatc.cpp b/src/flatc.cpp
new file mode 100644
index 0000000..e1236bd
--- /dev/null
+++ b/src/flatc.cpp
@@ -0,0 +1,489 @@
+/*
+ * 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 "flatbuffers/flatc.h"
+
+#include <list>
+
+namespace flatbuffers {
+
+const char *FLATC_VERSION() { return FLATBUFFERS_VERSION(); }
+
+void FlatCompiler::ParseFile(
+ flatbuffers::Parser &parser, const std::string &filename,
+ const std::string &contents,
+ std::vector<const char *> &include_directories) const {
+ 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);
+ }
+ if (!parser.error_.empty()) { Warn(parser.error_, false); }
+ include_directories.pop_back();
+ include_directories.pop_back();
+}
+
+void FlatCompiler::LoadBinarySchema(flatbuffers::Parser &parser,
+ const std::string &filename,
+ const std::string &contents) {
+ if (!parser.Deserialize(reinterpret_cast<const uint8_t *>(contents.c_str()),
+ contents.size())) {
+ Error("failed to load binary schema: " + filename, false, false);
+ }
+}
+
+void FlatCompiler::Warn(const std::string &warn, bool show_exe_name) const {
+ params_.warn_fn(this, warn, show_exe_name);
+}
+
+void FlatCompiler::Error(const std::string &err, bool usage,
+ bool show_exe_name) const {
+ params_.error_fn(this, err, usage, show_exe_name);
+}
+
+std::string FlatCompiler::GetUsageString(const char *program_name) const {
+ std::stringstream ss;
+ ss << "Usage: " << program_name << " [OPTION]... FILE... [-- FILE...]\n";
+ for (size_t i = 0; i < params_.num_generators; ++i) {
+ const Generator &g = params_.generators[i];
+
+ std::stringstream full_name;
+ full_name << std::setw(12) << std::left << g.generator_opt_long;
+ const char *name = g.generator_opt_short ? g.generator_opt_short : " ";
+ const char *help = g.generator_help;
+
+ ss << " " << full_name.str() << " " << name << " " << help << ".\n";
+ }
+ // clang-format off
+ ss <<
+ " -o PATH Prefix PATH to all generated files.\n"
+ " -I PATH Search for includes in the specified path.\n"
+ " -M Print make rules for generated files.\n"
+ " --version Print the version number of flatc and exit.\n"
+ " --strict-json Strict JSON: field names must be / will be quoted,\n"
+ " no trailing commas in tables/vectors.\n"
+ " --allow-non-utf8 Pass non-UTF-8 input through parser and emit nonstandard\n"
+ " \\x escapes in JSON. (Default is to raise parse error on\n"
+ " non-UTF-8 input.)\n"
+ " --natural-utf8 Output strings with UTF-8 as human-readable strings.\n"
+ " By default, UTF-8 characters are printed as \\uXXXX escapes.\n"
+ " --defaults-json Output fields whose value is the default when\n"
+ " writing JSON\n"
+ " --unknown-json Allow fields in JSON that are not defined in the\n"
+ " schema. These fields will be discared when generating\n"
+ " binaries.\n"
+ " --no-prefix Don\'t prefix enum values with the enum type in C++.\n"
+ " --scoped-enums Use C++11 style scoped and strongly typed enums.\n"
+ " also implies --no-prefix.\n"
+ " --gen-includes (deprecated), this is the default behavior.\n"
+ " If the original behavior is required (no include\n"
+ " statements) use --no-includes.\n"
+ " --no-includes Don\'t generate include statements for included\n"
+ " schemas the generated file depends on (C++).\n"
+ " --gen-mutable Generate accessors that can mutate buffers in-place.\n"
+ " --gen-onefile Generate single output file for C# and Go.\n"
+ " --gen-name-strings Generate type name functions for C++.\n"
+ " --gen-object-api Generate an additional object-based API.\n"
+ " --gen-compare Generate operator== for object-based API types.\n"
+ " --gen-nullable Add Clang _Nullable for C++ pointer. or @Nullable for Java\n"
+ " --gen-generated Add @Generated annotation for Java\n"
+ " --gen-all Generate not just code for the current schema files,\n"
+ " but for all files it includes as well.\n"
+ " If the language uses a single file for output (by default\n"
+ " the case for C++ and JS), all code will end up in this one\n"
+ " file.\n"
+ " --cpp-include Adds an #include in generated file.\n"
+ " --cpp-ptr-type T Set object API pointer type (default std::unique_ptr).\n"
+ " --cpp-str-type T Set object API string type (default std::string).\n"
+ " T::c_str(), T::length() and T::empty() must be supported.\n"
+ " The custom type also needs to be constructible from std::string\n"
+ " (see the --cpp-str-flex-ctor option to change this behavior).\n"
+ " --cpp-str-flex-ctor Don't construct custom string types by passing std::string\n"
+ " from Flatbuffers, but (char* + length).\n"
+ " --object-prefix Customise class prefix for C++ object-based API.\n"
+ " --object-suffix Customise class suffix for C++ object-based API.\n"
+ " Default value is \"T\".\n"
+ " --no-js-exports Removes Node.js style export lines in JS.\n"
+ " --goog-js-export Uses goog.exports* for closure compiler exporting in JS.\n"
+ " --es6-js-export Uses ECMAScript 6 export style lines in JS.\n"
+ " --go-namespace Generate the overrided namespace in Golang.\n"
+ " --go-import Generate the overrided import for flatbuffers in Golang\n"
+ " (default is \"github.com/google/flatbuffers/go\").\n"
+ " --raw-binary Allow binaries without file_indentifier to be read.\n"
+ " This may crash flatc given a mismatched schema.\n"
+ " --size-prefixed Input binaries are size prefixed buffers.\n"
+ " --proto Input is a .proto, translate to .fbs.\n"
+ " --oneof-union Translate .proto oneofs to flatbuffer unions.\n"
+ " --grpc Generate GRPC interfaces for the specified languages.\n"
+ " --schema Serialize schemas instead of JSON (use with -b).\n"
+ " --bfbs-comments Add doc comments to the binary schema files.\n"
+ " --bfbs-builtins Add builtin attributes to the binary schema files.\n"
+ " --conform FILE Specify a schema the following schemas should be\n"
+ " an evolution of. Gives errors if not.\n"
+ " --conform-includes Include path for the schema given with --conform PATH\n"
+ " --include-prefix Prefix this path to any generated include statements.\n"
+ " PATH\n"
+ " --keep-prefix Keep original prefix of schema include statement.\n"
+ " --no-fb-import Don't include flatbuffers import statement for TypeScript.\n"
+ " --no-ts-reexport Don't re-export imported dependencies for TypeScript.\n"
+ " --short-names Use short function names for JS and TypeScript.\n"
+ " --reflect-types Add minimal type reflection to code generation.\n"
+ " --reflect-names Add minimal type/name reflection.\n"
+ " --root-type T Select or override the default root_type\n"
+ " --force-defaults Emit default values in binary output from JSON\n"
+ " --force-empty When serializing from object API representation,\n"
+ " force strings and vectors to empty rather than null.\n"
+ "FILEs may be schemas (must end in .fbs), binary schemas (must end in .bfbs),\n"
+ "or JSON files (conforming to preceding schema). FILEs after the -- must be\n"
+ "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: " << program_name << " -c -b schema1.fbs schema2.fbs data.json\n";
+ // clang-format on
+ return ss.str();
+}
+
+int FlatCompiler::Compile(int argc, const char **argv) {
+ if (params_.generators == nullptr || params_.num_generators == 0) {
+ return 0;
+ }
+
+ flatbuffers::IDLOptions opts;
+ std::string output_path;
+
+ bool any_generator = false;
+ bool print_make_rules = false;
+ bool raw_binary = false;
+ bool schema_binary = false;
+ bool grpc_enabled = false;
+ std::vector<std::string> filenames;
+ std::list<std::string> include_directories_storage;
+ std::vector<const char *> include_directories;
+ std::vector<const char *> conform_include_directories;
+ std::vector<bool> generator_enabled(params_.num_generators, false);
+ size_t binary_files_from = std::numeric_limits<size_t>::max();
+ std::string conform_to_schema;
+
+ for (int argi = 0; argi < argc; argi++) {
+ std::string arg = argv[argi];
+ if (arg[0] == '-') {
+ if (filenames.size() && arg[1] != '-')
+ Error("invalid option location: " + arg, true);
+ if (arg == "-o") {
+ if (++argi >= argc) Error("missing path following: " + arg, true);
+ output_path = flatbuffers::ConCatPathFileName(
+ flatbuffers::PosixPath(argv[argi]), "");
+ } else if (arg == "-I") {
+ if (++argi >= argc) Error("missing path following" + arg, true);
+ include_directories_storage.push_back(
+ flatbuffers::PosixPath(argv[argi]));
+ include_directories.push_back(
+ include_directories_storage.back().c_str());
+ } else if (arg == "--conform") {
+ if (++argi >= argc) Error("missing path following" + arg, true);
+ conform_to_schema = flatbuffers::PosixPath(argv[argi]);
+ } else if (arg == "--conform-includes") {
+ if (++argi >= argc) Error("missing path following" + arg, true);
+ include_directories_storage.push_back(
+ flatbuffers::PosixPath(argv[argi]));
+ conform_include_directories.push_back(
+ include_directories_storage.back().c_str());
+ } else if (arg == "--include-prefix") {
+ if (++argi >= argc) Error("missing path following" + arg, true);
+ opts.include_prefix = flatbuffers::ConCatPathFileName(
+ flatbuffers::PosixPath(argv[argi]), "");
+ } else if (arg == "--keep-prefix") {
+ opts.keep_include_path = true;
+ } else if (arg == "--strict-json") {
+ opts.strict_json = true;
+ } else if (arg == "--allow-non-utf8") {
+ opts.allow_non_utf8 = true;
+ } else if (arg == "--natural-utf8") {
+ opts.natural_utf8 = true;
+ } else if (arg == "--no-js-exports") {
+ opts.skip_js_exports = true;
+ } else if (arg == "--goog-js-export") {
+ opts.use_goog_js_export_format = true;
+ opts.use_ES6_js_export_format = false;
+ } else if (arg == "--es6-js-export") {
+ opts.use_goog_js_export_format = false;
+ opts.use_ES6_js_export_format = true;
+ } else if (arg == "--go-namespace") {
+ if (++argi >= argc) Error("missing golang namespace" + arg, true);
+ opts.go_namespace = argv[argi];
+ } else if (arg == "--go-import") {
+ if (++argi >= argc) Error("missing golang import" + arg, true);
+ opts.go_import = argv[argi];
+ } else if (arg == "--defaults-json") {
+ opts.output_default_scalars_in_json = true;
+ } else if (arg == "--unknown-json") {
+ opts.skip_unexpected_fields_in_json = true;
+ } else if (arg == "--no-prefix") {
+ opts.prefixed_enums = false;
+ } else if (arg == "--scoped-enums") {
+ opts.prefixed_enums = false;
+ opts.scoped_enums = true;
+ } else if (arg == "--no-union-value-namespacing") {
+ opts.union_value_namespacing = false;
+ } else if (arg == "--gen-mutable") {
+ 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-compare") {
+ opts.gen_compare = true;
+ } else if (arg == "--cpp-include") {
+ if (++argi >= argc) Error("missing include following" + arg, true);
+ opts.cpp_includes.push_back(argv[argi]);
+ } else if (arg == "--cpp-ptr-type") {
+ if (++argi >= argc) Error("missing type following" + arg, true);
+ opts.cpp_object_api_pointer_type = argv[argi];
+ } else if (arg == "--cpp-str-type") {
+ if (++argi >= argc) Error("missing type following" + arg, true);
+ opts.cpp_object_api_string_type = argv[argi];
+ } else if (arg == "--cpp-str-flex-ctor") {
+ opts.cpp_object_api_string_flexible_constructor = true;
+ } else if (arg == "--gen-nullable") {
+ opts.gen_nullable = true;
+ } else if (arg == "--gen-generated") {
+ opts.gen_generated = true;
+ } else if (arg == "--object-prefix") {
+ if (++argi >= argc) Error("missing prefix following" + arg, true);
+ opts.object_prefix = argv[argi];
+ } else if (arg == "--object-suffix") {
+ if (++argi >= argc) Error("missing suffix following" + arg, true);
+ opts.object_suffix = argv[argi];
+ } else if (arg == "--gen-all") {
+ opts.generate_all = true;
+ opts.include_dependence_headers = false;
+ } else if (arg == "--gen-includes") {
+ // Deprecated, remove this option some time in the future.
+ printf("warning: --gen-includes is deprecated (it is now default)\n");
+ } else if (arg == "--no-includes") {
+ opts.include_dependence_headers = false;
+ } else if (arg == "--gen-onefile") {
+ opts.one_file = true;
+ } else if (arg == "--raw-binary") {
+ raw_binary = true;
+ } else if (arg == "--size-prefixed") {
+ opts.size_prefixed = true;
+ } else if (arg == "--") { // Separator between text and binary inputs.
+ binary_files_from = filenames.size();
+ } else if (arg == "--proto") {
+ opts.proto_mode = true;
+ } else if (arg == "--oneof-union") {
+ opts.proto_oneof_union = true;
+ } else if (arg == "--schema") {
+ schema_binary = true;
+ } else if (arg == "-M") {
+ print_make_rules = true;
+ } else if (arg == "--version") {
+ printf("flatc version %s\n", FLATC_VERSION());
+ exit(0);
+ } else if (arg == "--grpc") {
+ grpc_enabled = true;
+ } else if (arg == "--bfbs-comments") {
+ opts.binary_schema_comments = true;
+ } else if (arg == "--bfbs-builtins") {
+ opts.binary_schema_builtins = true;
+ } else if (arg == "--no-fb-import") {
+ opts.skip_flatbuffers_import = true;
+ } else if (arg == "--no-ts-reexport") {
+ opts.reexport_ts_modules = false;
+ } else if (arg == "--short-names") {
+ opts.js_ts_short_names = true;
+ } else if (arg == "--reflect-types") {
+ opts.mini_reflect = IDLOptions::kTypes;
+ } else if (arg == "--reflect-names") {
+ opts.mini_reflect = IDLOptions::kTypesAndNames;
+ } else if (arg == "--root-type") {
+ if (++argi >= argc) Error("missing type following" + arg, true);
+ opts.root_type = argv[argi];
+ } else if (arg == "--force-defaults") {
+ opts.force_defaults = true;
+ } else if (arg == "--force-empty") {
+ opts.set_empty_to_null = false;
+ } else {
+ for (size_t i = 0; i < params_.num_generators; ++i) {
+ if (arg == params_.generators[i].generator_opt_long ||
+ (params_.generators[i].generator_opt_short &&
+ arg == params_.generators[i].generator_opt_short)) {
+ generator_enabled[i] = true;
+ any_generator = true;
+ opts.lang_to_generate |= params_.generators[i].lang;
+ goto found;
+ }
+ }
+ Error("unknown commandline argument: " + arg, true);
+ found:;
+ }
+ } else {
+ filenames.push_back(flatbuffers::PosixPath(argv[argi]));
+ }
+ }
+
+ if (!filenames.size()) Error("missing input files", false, true);
+
+ if (opts.proto_mode) {
+ if (any_generator)
+ Error("cannot generate code directly from .proto files", true);
+ } 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);
+
+ if (flatbuffers::GetExtension(conform_to_schema) ==
+ reflection::SchemaExtension()) {
+ LoadBinarySchema(conform_parser, conform_to_schema, contents);
+ } else {
+ ParseFile(conform_parser, conform_to_schema, contents,
+ conform_include_directories);
+ }
+ }
+
+ std::unique_ptr<flatbuffers::Parser> parser(new flatbuffers::Parser(opts));
+
+ for (auto file_it = filenames.begin(); file_it != filenames.end();
+ ++file_it) {
+ auto &filename = *file_it;
+ std::string contents;
+ if (!flatbuffers::LoadFile(filename.c_str(), true, &contents))
+ Error("unable to load file: " + filename);
+
+ bool is_binary =
+ static_cast<size_t>(file_it - filenames.begin()) >= binary_files_from;
+ auto ext = flatbuffers::GetExtension(filename);
+ auto is_schema = ext == "fbs" || ext == "proto";
+ auto is_binary_schema = ext == reflection::SchemaExtension();
+ if (is_binary) {
+ parser->builder_.Clear();
+ parser->builder_.PushFlatBuffer(
+ reinterpret_cast<const uint8_t *>(contents.c_str()),
+ contents.length());
+ if (!raw_binary) {
+ // Generally reading binaries that do not correspond to the schema
+ // will crash, and sadly there's no way around that when the binary
+ // 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()) {
+ Error("current schema has no file_identifier: cannot test if \"" +
+ filename +
+ "\" matches the schema, use --raw-binary to read this file"
+ " anyway.");
+ } else if (!flatbuffers::BufferHasIdentifier(
+ contents.c_str(), parser->file_identifier_.c_str(), opts.size_prefixed)) {
+ Error("binary \"" + filename +
+ "\" does not have expected file_identifier \"" +
+ parser->file_identifier_ +
+ "\", use --raw-binary to read this file anyway.");
+ }
+ }
+ } else {
+ // Check if file contains 0 bytes.
+ if (!is_binary_schema && contents.length() != strlen(contents.c_str())) {
+ Error("input file appears to be binary: " + filename, true);
+ }
+ 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.
+ parser.reset(new flatbuffers::Parser(opts));
+ }
+ if (is_binary_schema) {
+ LoadBinarySchema(*parser.get(), filename, contents);
+ } else {
+ ParseFile(*parser.get(), filename, contents, include_directories);
+ if (!is_schema && !parser->builder_.GetSize()) {
+ // If a file doesn't end in .fbs, it must be json/binary. Ensure we
+ // didn't just parse a schema with a different extension.
+ Error("input file is neither json nor a .fbs (schema) file: " +
+ filename,
+ true);
+ }
+ }
+ if ((is_schema || is_binary_schema) && !conform_to_schema.empty()) {
+ auto err = parser->ConformTo(conform_parser);
+ if (!err.empty()) Error("schemas don\'t conform: " + err);
+ }
+ if (schema_binary) {
+ parser->Serialize();
+ parser->file_extension_ = reflection::SchemaExtension();
+ }
+ }
+
+ std::string filebase =
+ flatbuffers::StripPath(flatbuffers::StripExtension(filename));
+
+ for (size_t i = 0; i < params_.num_generators; ++i) {
+ parser->opts.lang = params_.generators[i].lang;
+ if (generator_enabled[i]) {
+ if (!print_make_rules) {
+ flatbuffers::EnsureDirExists(output_path);
+ if ((!params_.generators[i].schema_only ||
+ (is_schema || is_binary_schema)) &&
+ !params_.generators[i].generate(*parser.get(), output_path,
+ filebase)) {
+ Error(std::string("Unable to generate ") +
+ params_.generators[i].lang_name + " for " + filebase);
+ }
+ } else {
+ std::string make_rule = params_.generators[i].make_rule(
+ *parser.get(), output_path, filename);
+ if (!make_rule.empty())
+ printf("%s\n",
+ flatbuffers::WordWrap(make_rule, 80, " ", " \\").c_str());
+ }
+ if (grpc_enabled) {
+ if (params_.generators[i].generateGRPC != nullptr) {
+ if (!params_.generators[i].generateGRPC(*parser.get(), output_path,
+ filebase)) {
+ Error(std::string("Unable to generate GRPC interface for") +
+ params_.generators[i].lang_name);
+ }
+ } else {
+ Warn(std::string("GRPC interface generator not implemented for ") +
+ params_.generators[i].lang_name);
+ }
+ }
+ }
+ }
+
+ if (!opts.root_type.empty()) {
+ if (!parser->SetRootType(opts.root_type.c_str()))
+ Error("unknown root type: " + opts.root_type);
+ else if (parser->root_struct_def_->fixed)
+ Error("root type must be a table");
+ }
+
+ if (opts.proto_mode) GenerateFBS(*parser.get(), output_path, filebase);
+
+ // We do not want to generate code for the definitions in this file
+ // in any files coming up next.
+ parser->MarkGenerated();
+ }
+ return 0;
+}
+
+} // namespace flatbuffers
diff --git a/src/flatc_main.cpp b/src/flatc_main.cpp
new file mode 100644
index 0000000..72bb4a2
--- /dev/null
+++ b/src/flatc_main.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "flatbuffers/flatc.h"
+#include "flatbuffers/util.h"
+
+static const char *g_program_name = nullptr;
+
+static void Warn(const flatbuffers::FlatCompiler *flatc,
+ const std::string &warn, bool show_exe_name) {
+ (void)flatc;
+ if (show_exe_name) { printf("%s: ", g_program_name); }
+ printf("warning: %s\n", warn.c_str());
+}
+
+static void Error(const flatbuffers::FlatCompiler *flatc,
+ const std::string &err, bool usage, bool show_exe_name) {
+ if (show_exe_name) { printf("%s: ", g_program_name); }
+ printf("error: %s\n", err.c_str());
+ if (usage) { printf("%s", flatc->GetUsageString(g_program_name).c_str()); }
+ exit(1);
+}
+
+int main(int argc, const char *argv[]) {
+ // Prevent Appveyor-CI hangs.
+ flatbuffers::SetupDefaultCRTReportMode();
+
+ g_program_name = argv[0];
+
+ const flatbuffers::FlatCompiler::Generator generators[] = {
+ { flatbuffers::GenerateBinary, "-b", "--binary", "binary", false, nullptr,
+ flatbuffers::IDLOptions::kBinary,
+ "Generate wire format binaries for any data definitions",
+ flatbuffers::BinaryMakeRule },
+ { flatbuffers::GenerateTextFile, "-t", "--json", "text", false, nullptr,
+ flatbuffers::IDLOptions::kJson,
+ "Generate text output for any data definitions",
+ flatbuffers::TextMakeRule },
+ { flatbuffers::GenerateCPP, "-c", "--cpp", "C++", true,
+ flatbuffers::GenerateCppGRPC, flatbuffers::IDLOptions::kCpp,
+ "Generate C++ headers for tables/structs", flatbuffers::CPPMakeRule },
+ { flatbuffers::GenerateGo, "-g", "--go", "Go", true,
+ flatbuffers::GenerateGoGRPC, flatbuffers::IDLOptions::kGo,
+ "Generate Go files for tables/structs", flatbuffers::GeneralMakeRule },
+ { flatbuffers::GenerateGeneral, "-j", "--java", "Java", true,
+ flatbuffers::GenerateJavaGRPC, flatbuffers::IDLOptions::kJava,
+ "Generate Java classes for tables/structs",
+ flatbuffers::GeneralMakeRule },
+ { flatbuffers::GenerateJSTS, "-s", "--js", "JavaScript", true, nullptr,
+ flatbuffers::IDLOptions::kJs,
+ "Generate JavaScript code for tables/structs", flatbuffers::JSTSMakeRule },
+ { flatbuffers::GenerateDart, "-d", "--dart", "Dart", true, nullptr,
+ flatbuffers::IDLOptions::kDart,
+ "Generate Dart classes for tables/structs", flatbuffers::DartMakeRule },
+ { flatbuffers::GenerateJSTS, "-T", "--ts", "TypeScript", true, nullptr,
+ flatbuffers::IDLOptions::kTs,
+ "Generate TypeScript code for tables/structs", flatbuffers::JSTSMakeRule },
+ { flatbuffers::GenerateGeneral, "-n", "--csharp", "C#", true, nullptr,
+ flatbuffers::IDLOptions::kCSharp,
+ "Generate C# classes for tables/structs", flatbuffers::GeneralMakeRule },
+ { flatbuffers::GeneratePython, "-p", "--python", "Python", true, nullptr,
+ flatbuffers::IDLOptions::kPython,
+ "Generate Python files for tables/structs",
+ flatbuffers::GeneralMakeRule },
+ { flatbuffers::GenerateLobster, nullptr, "--lobster", "Lobster", true, nullptr,
+ flatbuffers::IDLOptions::kLobster,
+ "Generate Lobster files for tables/structs",
+ flatbuffers::GeneralMakeRule },
+ { flatbuffers::GenerateLua, "-l", "--lua", "Lua", true, nullptr,
+ flatbuffers::IDLOptions::kLua,
+ "Generate Lua files for tables/structs",
+ flatbuffers::GeneralMakeRule },
+ { flatbuffers::GenerateRust, "-r", "--rust", "Rust", true, nullptr,
+ flatbuffers::IDLOptions::kRust,
+ "Generate Rust files for tables/structs",
+ flatbuffers::RustMakeRule },
+ { flatbuffers::GeneratePhp, nullptr, "--php", "PHP", true, nullptr,
+ flatbuffers::IDLOptions::kPhp, "Generate PHP files for tables/structs",
+ flatbuffers::GeneralMakeRule },
+ { flatbuffers::GenerateKotlin, nullptr, "--kotlin", "Kotlin", true, nullptr,
+ flatbuffers::IDLOptions::kKotlin, "Generate Kotlin classes for tables/structs",
+ flatbuffers::GeneralMakeRule },
+ { flatbuffers::GenerateJsonSchema, nullptr, "--jsonschema", "JsonSchema",
+ true, nullptr, flatbuffers::IDLOptions::kJsonSchema,
+ "Generate Json schema", flatbuffers::GeneralMakeRule },
+ };
+
+ flatbuffers::FlatCompiler::InitParams params;
+ params.generators = generators;
+ params.num_generators = sizeof(generators) / sizeof(generators[0]);
+ params.warn_fn = Warn;
+ params.error_fn = Error;
+
+ flatbuffers::FlatCompiler flatc(params);
+ return flatc.Compile(argc - 1, argv + 1);
+}
diff --git a/src/flathash.cpp b/src/flathash.cpp
new file mode 100644
index 0000000..bc3d2df
--- /dev/null
+++ b/src/flathash.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2015 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include "flatbuffers/hash.h"
+
+enum OutputFormat { kDecimal, kHexadecimal, kHexadecimal0x };
+
+int main(int argc, char *argv[]) {
+ const char *name = argv[0];
+ if (argc <= 1) {
+ printf("%s HASH [OPTION]... [--] STRING...\n", name);
+ printf("Available hashing algorithms:\n");
+ printf(" 16 bit:\n");
+ size_t size = sizeof(flatbuffers::kHashFunctions16) /
+ sizeof(flatbuffers::kHashFunctions16[0]);
+ for (size_t i = 0; i < size; ++i) {
+ printf(" * %s\n", flatbuffers::kHashFunctions16[i].name);
+ }
+ printf(" 32 bit:\n");
+ size = sizeof(flatbuffers::kHashFunctions32) /
+ sizeof(flatbuffers::kHashFunctions32[0]);
+ for (size_t i = 0; i < size; ++i) {
+ printf(" * %s\n", flatbuffers::kHashFunctions32[i].name);
+ }
+ printf(" 64 bit:\n");
+ size = sizeof(flatbuffers::kHashFunctions64) /
+ sizeof(flatbuffers::kHashFunctions64[0]);
+ for (size_t i = 0; i < size; ++i) {
+ printf(" * %s\n", flatbuffers::kHashFunctions64[i].name);
+ }
+ printf(
+ " -d Output hash in decimal.\n"
+ " -x Output hash in hexadecimal.\n"
+ " -0x Output hash in hexadecimal and prefix with 0x.\n"
+ " -c Append the string to the output in a c-style comment.\n");
+ return 1;
+ }
+
+ const char *hash_algorithm = argv[1];
+
+ flatbuffers::NamedHashFunction<uint16_t>::HashFunction hash_function16 =
+ flatbuffers::FindHashFunction16(hash_algorithm);
+ flatbuffers::NamedHashFunction<uint32_t>::HashFunction hash_function32 =
+ flatbuffers::FindHashFunction32(hash_algorithm);
+ flatbuffers::NamedHashFunction<uint64_t>::HashFunction hash_function64 =
+ flatbuffers::FindHashFunction64(hash_algorithm);
+
+ if (!hash_function16 && !hash_function32 && !hash_function64) {
+ printf("\"%s\" is not a known hash algorithm.\n", hash_algorithm);
+ return 1;
+ }
+
+ OutputFormat output_format = kHexadecimal;
+ bool annotate = false;
+ bool escape_dash = false;
+ for (int i = 2; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!escape_dash && arg[0] == '-') {
+ std::string opt = arg;
+ if (opt == "-d")
+ output_format = kDecimal;
+ else if (opt == "-x")
+ output_format = kHexadecimal;
+ else if (opt == "-0x")
+ output_format = kHexadecimal0x;
+ else if (opt == "-c")
+ annotate = true;
+ else if (opt == "--")
+ escape_dash = true;
+ else
+ printf("Unrecognized argument: \"%s\"\n", arg);
+ } else {
+ std::stringstream ss;
+ if (output_format == kDecimal) {
+ ss << std::dec;
+ } else if (output_format == kHexadecimal) {
+ ss << std::hex;
+ } else if (output_format == kHexadecimal0x) {
+ ss << std::hex;
+ ss << "0x";
+ }
+ if (hash_function16)
+ ss << hash_function16(arg);
+ else if (hash_function32)
+ ss << hash_function32(arg);
+ else if (hash_function64)
+ ss << hash_function64(arg);
+
+ if (annotate) ss << " /* \"" << arg << "\" */";
+
+ ss << "\n";
+
+ std::cout << ss.str();
+ }
+ }
+ return 0;
+}
diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp
new file mode 100644
index 0000000..b667ea4
--- /dev/null
+++ b/src/idl_gen_cpp.cpp
@@ -0,0 +1,2972 @@
+/*
+ * 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.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+#include <unordered_set>
+
+namespace flatbuffers {
+
+// Pedantic warning free version of toupper().
+inline char ToUpper(char c) { return static_cast<char>(::toupper(c)); }
+
+// Make numerical literal with type-suffix.
+// This function is only needed for C++! Other languages do not need it.
+static inline std::string NumToStringCpp(std::string val, BaseType type) {
+ // Avoid issues with -2147483648, -9223372036854775808.
+ switch (type) {
+ case BASE_TYPE_INT:
+ return (val != "-2147483648") ? val : ("(-2147483647 - 1)");
+ case BASE_TYPE_ULONG: return (val == "0") ? val : (val + "ULL");
+ case BASE_TYPE_LONG:
+ if (val == "-9223372036854775808")
+ return "(-9223372036854775807LL - 1LL)";
+ else
+ return (val == "0") ? val : (val + "LL");
+ default: return val;
+ }
+}
+
+static std::string GeneratedFileName(const std::string &path,
+ const std::string &file_name) {
+ return path + file_name + "_generated.h";
+}
+
+namespace cpp {
+class CppGenerator : public BaseGenerator {
+ public:
+ CppGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "", "::"),
+ cur_name_space_(nullptr),
+ float_const_gen_("std::numeric_limits<double>::",
+ "std::numeric_limits<float>::", "quiet_NaN()",
+ "infinity()") {
+ static const char *const keywords[] = {
+ "alignas",
+ "alignof",
+ "and",
+ "and_eq",
+ "asm",
+ "atomic_cancel",
+ "atomic_commit",
+ "atomic_noexcept",
+ "auto",
+ "bitand",
+ "bitor",
+ "bool",
+ "break",
+ "case",
+ "catch",
+ "char",
+ "char16_t",
+ "char32_t",
+ "class",
+ "compl",
+ "concept",
+ "const",
+ "constexpr",
+ "const_cast",
+ "continue",
+ "co_await",
+ "co_return",
+ "co_yield",
+ "decltype",
+ "default",
+ "delete",
+ "do",
+ "double",
+ "dynamic_cast",
+ "else",
+ "enum",
+ "explicit",
+ "export",
+ "extern",
+ "false",
+ "float",
+ "for",
+ "friend",
+ "goto",
+ "if",
+ "import",
+ "inline",
+ "int",
+ "long",
+ "module",
+ "mutable",
+ "namespace",
+ "new",
+ "noexcept",
+ "not",
+ "not_eq",
+ "nullptr",
+ "operator",
+ "or",
+ "or_eq",
+ "private",
+ "protected",
+ "public",
+ "register",
+ "reinterpret_cast",
+ "requires",
+ "return",
+ "short",
+ "signed",
+ "sizeof",
+ "static",
+ "static_assert",
+ "static_cast",
+ "struct",
+ "switch",
+ "synchronized",
+ "template",
+ "this",
+ "thread_local",
+ "throw",
+ "true",
+ "try",
+ "typedef",
+ "typeid",
+ "typename",
+ "union",
+ "unsigned",
+ "using",
+ "virtual",
+ "void",
+ "volatile",
+ "wchar_t",
+ "while",
+ "xor",
+ "xor_eq",
+ nullptr,
+ };
+ for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
+ }
+
+ std::string GenIncludeGuard() const {
+ // Generate include guard.
+ std::string guard = file_name_;
+ // Remove any non-alpha-numeric characters that may appear in a filename.
+ struct IsAlnum {
+ bool operator()(char c) const { return !is_alnum(c); }
+ };
+ guard.erase(std::remove_if(guard.begin(), guard.end(), IsAlnum()),
+ guard.end());
+ guard = "FLATBUFFERS_GENERATED_" + guard;
+ guard += "_";
+ // For further uniqueness, also add the namespace.
+ auto name_space = parser_.current_namespace_;
+ for (auto it = name_space->components.begin();
+ it != name_space->components.end(); ++it) {
+ guard += *it + "_";
+ }
+ guard += "H_";
+ std::transform(guard.begin(), guard.end(), guard.begin(), ToUpper);
+ return guard;
+ }
+
+ void GenIncludeDependencies() {
+ int num_includes = 0;
+ for (auto it = parser_.native_included_files_.begin();
+ it != parser_.native_included_files_.end(); ++it) {
+ code_ += "#include \"" + *it + "\"";
+ num_includes++;
+ }
+ for (auto it = parser_.included_files_.begin();
+ it != parser_.included_files_.end(); ++it) {
+ if (it->second.empty()) continue;
+ auto noext = flatbuffers::StripExtension(it->second);
+ auto basename = flatbuffers::StripPath(noext);
+
+ code_ += "#include \"" + parser_.opts.include_prefix +
+ (parser_.opts.keep_include_path ? noext : basename) +
+ "_generated.h\"";
+ num_includes++;
+ }
+ if (num_includes) code_ += "";
+ }
+
+ void GenExtraIncludes() {
+ for(std::size_t i = 0; i < parser_.opts.cpp_includes.size(); ++i) {
+ code_ += "#include \"" + parser_.opts.cpp_includes[i] + "\"";
+ }
+ if (!parser_.opts.cpp_includes.empty()) {
+ code_ += "";
+ }
+ }
+
+ std::string EscapeKeyword(const std::string &name) const {
+ return keywords_.find(name) == keywords_.end() ? name : name + "_";
+ }
+
+ std::string Name(const Definition &def) const {
+ return EscapeKeyword(def.name);
+ }
+
+ std::string Name(const EnumVal &ev) const { return EscapeKeyword(ev.name); }
+
+ // Iterate through all definitions we haven't generate code for (enums,
+ // structs, and tables) and output them to a single file.
+ bool generate() {
+ code_.Clear();
+ code_ += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
+
+ const auto include_guard = GenIncludeGuard();
+ code_ += "#ifndef " + include_guard;
+ code_ += "#define " + include_guard;
+ code_ += "";
+
+ if (parser_.opts.gen_nullable) {
+ code_ += "#pragma clang system_header\n\n";
+ }
+
+ code_ += "#include \"flatbuffers/flatbuffers.h\"";
+ if (parser_.uses_flexbuffers_) {
+ code_ += "#include \"flatbuffers/flexbuffers.h\"";
+ }
+ code_ += "";
+
+ if (parser_.opts.include_dependence_headers) { GenIncludeDependencies(); }
+ GenExtraIncludes();
+
+ FLATBUFFERS_ASSERT(!cur_name_space_);
+
+ // Generate forward declarations for all structs/tables, since they may
+ // have circular references.
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ const auto &struct_def = **it;
+ if (!struct_def.generated) {
+ SetNameSpace(struct_def.defined_namespace);
+ code_ += "struct " + Name(struct_def) + ";";
+ if (parser_.opts.generate_object_based_api) {
+ auto nativeName =
+ NativeName(Name(struct_def), &struct_def, parser_.opts);
+ if (!struct_def.fixed) { code_ += "struct " + nativeName + ";"; }
+ }
+ code_ += "";
+ }
+ }
+
+ // Generate forward declarations for all equal operators
+ if (parser_.opts.generate_object_based_api && parser_.opts.gen_compare) {
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ const auto &struct_def = **it;
+ if (!struct_def.generated) {
+ SetNameSpace(struct_def.defined_namespace);
+ auto nativeName =
+ NativeName(Name(struct_def), &struct_def, parser_.opts);
+ code_ += "bool operator==(const " + nativeName + " &lhs, const " +
+ nativeName + " &rhs);";
+ code_ += "bool operator!=(const " + nativeName + " &lhs, const " +
+ nativeName + " &rhs);";
+ }
+ }
+ code_ += "";
+ }
+
+ // Generate preablmle code for mini reflection.
+ if (parser_.opts.mini_reflect != IDLOptions::kNone) {
+ // To break cyclic dependencies, first pre-declare all tables/structs.
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ const auto &struct_def = **it;
+ if (!struct_def.generated) {
+ SetNameSpace(struct_def.defined_namespace);
+ GenMiniReflectPre(&struct_def);
+ }
+ }
+ }
+
+ // Generate code for all the enum declarations.
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ const auto &enum_def = **it;
+ if (!enum_def.generated) {
+ SetNameSpace(enum_def.defined_namespace);
+ GenEnum(enum_def);
+ }
+ }
+
+ // Generate code for all structs, then all tables.
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ const auto &struct_def = **it;
+ if (struct_def.fixed && !struct_def.generated) {
+ SetNameSpace(struct_def.defined_namespace);
+ GenStruct(struct_def);
+ }
+ }
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ const auto &struct_def = **it;
+ if (!struct_def.fixed && !struct_def.generated) {
+ SetNameSpace(struct_def.defined_namespace);
+ GenTable(struct_def);
+ }
+ }
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ const auto &struct_def = **it;
+ if (!struct_def.fixed && !struct_def.generated) {
+ SetNameSpace(struct_def.defined_namespace);
+ GenTablePost(struct_def);
+ }
+ }
+
+ // Generate code for union verifiers.
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ const auto &enum_def = **it;
+ if (enum_def.is_union && !enum_def.generated) {
+ SetNameSpace(enum_def.defined_namespace);
+ GenUnionPost(enum_def);
+ }
+ }
+
+ // Generate code for mini reflection.
+ if (parser_.opts.mini_reflect != IDLOptions::kNone) {
+ // Then the unions/enums that may refer to them.
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ const auto &enum_def = **it;
+ if (!enum_def.generated) {
+ SetNameSpace(enum_def.defined_namespace);
+ GenMiniReflect(nullptr, &enum_def);
+ }
+ }
+ // Then the full tables/structs.
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ const auto &struct_def = **it;
+ if (!struct_def.generated) {
+ SetNameSpace(struct_def.defined_namespace);
+ GenMiniReflect(&struct_def, nullptr);
+ }
+ }
+ }
+
+ // Generate convenient global helper functions:
+ if (parser_.root_struct_def_) {
+ auto &struct_def = *parser_.root_struct_def_;
+ SetNameSpace(struct_def.defined_namespace);
+ auto name = Name(struct_def);
+ auto qualified_name = cur_name_space_->GetFullyQualifiedName(name);
+ auto cpp_name = TranslateNameSpace(qualified_name);
+
+ code_.SetValue("STRUCT_NAME", name);
+ code_.SetValue("CPP_NAME", cpp_name);
+ code_.SetValue("NULLABLE_EXT", NullableExtension());
+
+ // The root datatype accessor:
+ code_ += "inline \\";
+ code_ +=
+ "const {{CPP_NAME}} *{{NULLABLE_EXT}}Get{{STRUCT_NAME}}(const void "
+ "*buf) {";
+ code_ += " return flatbuffers::GetRoot<{{CPP_NAME}}>(buf);";
+ code_ += "}";
+ code_ += "";
+
+ code_ += "inline \\";
+ code_ +=
+ "const {{CPP_NAME}} "
+ "*{{NULLABLE_EXT}}GetSizePrefixed{{STRUCT_NAME}}(const void "
+ "*buf) {";
+ code_ += " return flatbuffers::GetSizePrefixedRoot<{{CPP_NAME}}>(buf);";
+ code_ += "}";
+ code_ += "";
+
+ if (parser_.opts.mutable_buffer) {
+ code_ += "inline \\";
+ code_ += "{{STRUCT_NAME}} *GetMutable{{STRUCT_NAME}}(void *buf) {";
+ code_ += " return flatbuffers::GetMutableRoot<{{STRUCT_NAME}}>(buf);";
+ code_ += "}";
+ code_ += "";
+ }
+
+ if (parser_.file_identifier_.length()) {
+ // Return the identifier
+ code_ += "inline const char *{{STRUCT_NAME}}Identifier() {";
+ code_ += " return \"" + parser_.file_identifier_ + "\";";
+ code_ += "}";
+ code_ += "";
+
+ // Check if a buffer has the identifier.
+ code_ += "inline \\";
+ code_ += "bool {{STRUCT_NAME}}BufferHasIdentifier(const void *buf) {";
+ code_ += " return flatbuffers::BufferHasIdentifier(";
+ code_ += " buf, {{STRUCT_NAME}}Identifier());";
+ code_ += "}";
+ code_ += "";
+ }
+
+ // The root verifier.
+ if (parser_.file_identifier_.length()) {
+ code_.SetValue("ID", name + "Identifier()");
+ } else {
+ code_.SetValue("ID", "nullptr");
+ }
+
+ code_ += "inline bool Verify{{STRUCT_NAME}}Buffer(";
+ code_ += " flatbuffers::Verifier &verifier) {";
+ code_ += " return verifier.VerifyBuffer<{{CPP_NAME}}>({{ID}});";
+ code_ += "}";
+ code_ += "";
+
+ code_ += "inline bool VerifySizePrefixed{{STRUCT_NAME}}Buffer(";
+ code_ += " flatbuffers::Verifier &verifier) {";
+ code_ +=
+ " return verifier.VerifySizePrefixedBuffer<{{CPP_NAME}}>({{ID}});";
+ code_ += "}";
+ code_ += "";
+
+ if (parser_.file_extension_.length()) {
+ // Return the extension
+ code_ += "inline const char *{{STRUCT_NAME}}Extension() {";
+ code_ += " return \"" + parser_.file_extension_ + "\";";
+ code_ += "}";
+ code_ += "";
+ }
+
+ // Finish a buffer with a given root object:
+ code_ += "inline void Finish{{STRUCT_NAME}}Buffer(";
+ code_ += " flatbuffers::FlatBufferBuilder &fbb,";
+ code_ += " flatbuffers::Offset<{{CPP_NAME}}> root) {";
+ if (parser_.file_identifier_.length())
+ code_ += " fbb.Finish(root, {{STRUCT_NAME}}Identifier());";
+ else
+ code_ += " fbb.Finish(root);";
+ code_ += "}";
+ code_ += "";
+
+ code_ += "inline void FinishSizePrefixed{{STRUCT_NAME}}Buffer(";
+ code_ += " flatbuffers::FlatBufferBuilder &fbb,";
+ code_ += " flatbuffers::Offset<{{CPP_NAME}}> root) {";
+ if (parser_.file_identifier_.length())
+ code_ += " fbb.FinishSizePrefixed(root, {{STRUCT_NAME}}Identifier());";
+ else
+ code_ += " fbb.FinishSizePrefixed(root);";
+ code_ += "}";
+ code_ += "";
+
+ if (parser_.opts.generate_object_based_api) {
+ // A convenient root unpack function.
+ auto native_name =
+ NativeName(WrapInNameSpace(struct_def), &struct_def, parser_.opts);
+ code_.SetValue("UNPACK_RETURN",
+ GenTypeNativePtr(native_name, nullptr, false));
+ code_.SetValue("UNPACK_TYPE",
+ GenTypeNativePtr(native_name, nullptr, true));
+
+ code_ += "inline {{UNPACK_RETURN}} UnPack{{STRUCT_NAME}}(";
+ code_ += " const void *buf,";
+ code_ += " const flatbuffers::resolver_function_t *res = nullptr) {";
+ code_ += " return {{UNPACK_TYPE}}\\";
+ code_ += "(Get{{STRUCT_NAME}}(buf)->UnPack(res));";
+ code_ += "}";
+ code_ += "";
+
+ code_ += "inline {{UNPACK_RETURN}} UnPackSizePrefixed{{STRUCT_NAME}}(";
+ code_ += " const void *buf,";
+ code_ += " const flatbuffers::resolver_function_t *res = nullptr) {";
+ code_ += " return {{UNPACK_TYPE}}\\";
+ code_ += "(GetSizePrefixed{{STRUCT_NAME}}(buf)->UnPack(res));";
+ code_ += "}";
+ code_ += "";
+ }
+ }
+
+ if (cur_name_space_) SetNameSpace(nullptr);
+
+ // Close the include guard.
+ code_ += "#endif // " + include_guard;
+
+ const auto file_path = GeneratedFileName(path_, file_name_);
+ const auto final_code = code_.ToString();
+ return SaveFile(file_path.c_str(), final_code, false);
+ }
+
+ private:
+ CodeWriter code_;
+
+ std::unordered_set<std::string> keywords_;
+
+ // This tracks the current namespace so we can insert namespace declarations.
+ const Namespace *cur_name_space_;
+
+ const Namespace *CurrentNameSpace() const { return cur_name_space_; }
+
+ // Translates a qualified name in flatbuffer text format to the same name in
+ // the equivalent C++ namespace.
+ static std::string TranslateNameSpace(const std::string &qualified_name) {
+ std::string cpp_qualified_name = qualified_name;
+ size_t start_pos = 0;
+ while ((start_pos = cpp_qualified_name.find('.', start_pos)) !=
+ std::string::npos) {
+ cpp_qualified_name.replace(start_pos, 1, "::");
+ }
+ return cpp_qualified_name;
+ }
+
+ void GenComment(const std::vector<std::string> &dc, const char *prefix = "") {
+ std::string text;
+ ::flatbuffers::GenComment(dc, &text, nullptr, prefix);
+ code_ += text + "\\";
+ }
+
+ // Return a C++ type from the table in idl.h
+ std::string GenTypeBasic(const Type &type, bool user_facing_type) const {
+ // clang-format off
+ static const char *const ctypename[] = {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
+ RTYPE, KTYPE) \
+ #CTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ };
+ // clang-format on
+ if (user_facing_type) {
+ if (type.enum_def) return WrapInNameSpace(*type.enum_def);
+ if (type.base_type == BASE_TYPE_BOOL) return "bool";
+ }
+ return ctypename[type.base_type];
+ }
+
+ // Return a C++ pointer type, specialized to the actual struct/table types,
+ // and vector element types.
+ std::string GenTypePointer(const Type &type) const {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: {
+ return "flatbuffers::String";
+ }
+ case BASE_TYPE_VECTOR: {
+ const auto type_name = GenTypeWire(type.VectorType(), "", false);
+ return "flatbuffers::Vector<" + type_name + ">";
+ }
+ case BASE_TYPE_STRUCT: {
+ return WrapInNameSpace(*type.struct_def);
+ }
+ case BASE_TYPE_UNION:
+ // fall through
+ default: { return "void"; }
+ }
+ }
+
+ // 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) const {
+ if (IsScalar(type.base_type)) {
+ return GenTypeBasic(type, user_facing_type) + postfix;
+ } else if (IsStruct(type)) {
+ return "const " + GenTypePointer(type) + " *";
+ } else {
+ return "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) const {
+ if (IsScalar(type.base_type)) {
+ return GenTypeBasic(type, false);
+ } else if (IsStruct(type)) {
+ return GenTypePointer(type);
+ } else {
+ return "flatbuffers::uoffset_t";
+ }
+ }
+
+ std::string NullableExtension() {
+ return parser_.opts.gen_nullable ? " _Nullable " : "";
+ }
+
+ static std::string NativeName(const std::string &name, const StructDef *sd,
+ const IDLOptions &opts) {
+ return sd && !sd->fixed ? opts.object_prefix + name + opts.object_suffix
+ : name;
+ }
+
+ const std::string &PtrType(const FieldDef *field) {
+ auto attr = field ? field->attributes.Lookup("cpp_ptr_type") : nullptr;
+ return attr ? attr->constant : parser_.opts.cpp_object_api_pointer_type;
+ }
+
+ const std::string NativeString(const FieldDef *field) {
+ auto attr = field ? field->attributes.Lookup("cpp_str_type") : nullptr;
+ auto &ret = attr ? attr->constant : parser_.opts.cpp_object_api_string_type;
+ if (ret.empty()) { return "std::string"; }
+ return ret;
+ }
+
+ bool FlexibleStringConstructor(const FieldDef *field) {
+ auto attr = field
+ ? (field->attributes.Lookup("cpp_str_flex_ctor") != nullptr)
+ : false;
+ auto ret =
+ attr ? attr : parser_.opts.cpp_object_api_string_flexible_constructor;
+ return ret && NativeString(field) !=
+ "std::string"; // Only for custom string types.
+ }
+
+ std::string GenTypeNativePtr(const std::string &type, const FieldDef *field,
+ bool is_constructor) {
+ auto &ptr_type = PtrType(field);
+ if (ptr_type != "naked") {
+ return (ptr_type != "default_ptr_type"
+ ? ptr_type
+ : parser_.opts.cpp_object_api_pointer_type) +
+ "<" + type + ">";
+ } else if (is_constructor) {
+ return "";
+ } else {
+ return type + " *";
+ }
+ }
+
+ std::string GenPtrGet(const FieldDef &field) {
+ auto cpp_ptr_type_get = field.attributes.Lookup("cpp_ptr_type_get");
+ if (cpp_ptr_type_get) return cpp_ptr_type_get->constant;
+ auto &ptr_type = PtrType(&field);
+ return ptr_type == "naked" ? "" : ".get()";
+ }
+
+ std::string GenTypeNative(const Type &type, bool invector,
+ const FieldDef &field) {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: {
+ return NativeString(&field);
+ }
+ case BASE_TYPE_VECTOR: {
+ const auto type_name = GenTypeNative(type.VectorType(), true, field);
+ if (type.struct_def &&
+ type.struct_def->attributes.Lookup("native_custom_alloc")) {
+ auto native_custom_alloc =
+ type.struct_def->attributes.Lookup("native_custom_alloc");
+ return "std::vector<" + type_name + "," +
+ native_custom_alloc->constant + "<" + type_name + ">>";
+ } else
+ return "std::vector<" + type_name + ">";
+ }
+ case BASE_TYPE_STRUCT: {
+ auto type_name = WrapInNameSpace(*type.struct_def);
+ if (IsStruct(type)) {
+ auto native_type = type.struct_def->attributes.Lookup("native_type");
+ if (native_type) { type_name = native_type->constant; }
+ if (invector || field.native_inline) {
+ return type_name;
+ } else {
+ return GenTypeNativePtr(type_name, &field, false);
+ }
+ } else {
+ return GenTypeNativePtr(
+ NativeName(type_name, type.struct_def, parser_.opts), &field,
+ false);
+ }
+ }
+ 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) {
+ if (IsScalar(type.base_type)) {
+ return GenTypeBasic(type, user_facing_type) + afterbasic;
+ } else if (IsArray(type)) {
+ auto element_type = type.VectorType();
+ return beforeptr +
+ (IsScalar(element_type.base_type)
+ ? GenTypeBasic(element_type, user_facing_type)
+ : GenTypePointer(element_type)) +
+ afterptr;
+ } else {
+ return beforeptr + GenTypePointer(type) + afterptr;
+ }
+ }
+
+ std::string GenEnumDecl(const EnumDef &enum_def) const {
+ const IDLOptions &opts = parser_.opts;
+ return (opts.scoped_enums ? "enum class " : "enum ") + Name(enum_def);
+ }
+
+ std::string GenEnumValDecl(const EnumDef &enum_def,
+ const std::string &enum_val) const {
+ const IDLOptions &opts = parser_.opts;
+ return opts.prefixed_enums ? Name(enum_def) + "_" + enum_val : enum_val;
+ }
+
+ std::string GetEnumValUse(const EnumDef &enum_def,
+ const EnumVal &enum_val) const {
+ const IDLOptions &opts = parser_.opts;
+ if (opts.scoped_enums) {
+ return Name(enum_def) + "::" + Name(enum_val);
+ } else if (opts.prefixed_enums) {
+ return Name(enum_def) + "_" + Name(enum_val);
+ } else {
+ return Name(enum_val);
+ }
+ }
+
+ std::string StripUnionType(const std::string &name) {
+ return name.substr(0, name.size() - strlen(UnionTypeFieldSuffix()));
+ }
+
+ std::string GetUnionElement(const EnumVal &ev, bool wrap, bool actual_type,
+ bool native_type = false) {
+ if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
+ auto name = actual_type ? ev.union_type.struct_def->name : Name(ev);
+ return wrap ? WrapInNameSpace(ev.union_type.struct_def->defined_namespace,
+ name)
+ : name;
+ } else if (ev.union_type.base_type == BASE_TYPE_STRING) {
+ return actual_type ? (native_type ? "std::string" : "flatbuffers::String")
+ : Name(ev);
+ } else {
+ FLATBUFFERS_ASSERT(false);
+ return Name(ev);
+ }
+ }
+
+ std::string UnionVerifySignature(const EnumDef &enum_def) {
+ return "bool Verify" + Name(enum_def) +
+ "(flatbuffers::Verifier &verifier, const void *obj, " +
+ Name(enum_def) + " type)";
+ }
+
+ std::string UnionVectorVerifySignature(const EnumDef &enum_def) {
+ return "bool Verify" + Name(enum_def) + "Vector" +
+ "(flatbuffers::Verifier &verifier, " +
+ "const flatbuffers::Vector<flatbuffers::Offset<void>> *values, " +
+ "const flatbuffers::Vector<uint8_t> *types)";
+ }
+
+ std::string UnionUnPackSignature(const EnumDef &enum_def, bool inclass) {
+ return (inclass ? "static " : "") + std::string("void *") +
+ (inclass ? "" : Name(enum_def) + "Union::") +
+ "UnPack(const void *obj, " + Name(enum_def) +
+ " type, const flatbuffers::resolver_function_t *resolver)";
+ }
+
+ std::string UnionPackSignature(const EnumDef &enum_def, bool inclass) {
+ return "flatbuffers::Offset<void> " +
+ (inclass ? "" : Name(enum_def) + "Union::") +
+ "Pack(flatbuffers::FlatBufferBuilder &_fbb, " +
+ "const flatbuffers::rehasher_function_t *_rehasher" +
+ (inclass ? " = nullptr" : "") + ") const";
+ }
+
+ std::string TableCreateSignature(const StructDef &struct_def, bool predecl,
+ const IDLOptions &opts) {
+ return "flatbuffers::Offset<" + Name(struct_def) + "> Create" +
+ Name(struct_def) + "(flatbuffers::FlatBufferBuilder &_fbb, const " +
+ NativeName(Name(struct_def), &struct_def, opts) +
+ " *_o, const flatbuffers::rehasher_function_t *_rehasher" +
+ (predecl ? " = nullptr" : "") + ")";
+ }
+
+ std::string TablePackSignature(const StructDef &struct_def, bool inclass,
+ const IDLOptions &opts) {
+ return std::string(inclass ? "static " : "") + "flatbuffers::Offset<" +
+ Name(struct_def) + "> " + (inclass ? "" : Name(struct_def) + "::") +
+ "Pack(flatbuffers::FlatBufferBuilder &_fbb, " + "const " +
+ NativeName(Name(struct_def), &struct_def, opts) + "* _o, " +
+ "const flatbuffers::rehasher_function_t *_rehasher" +
+ (inclass ? " = nullptr" : "") + ")";
+ }
+
+ std::string TableUnPackSignature(const StructDef &struct_def, bool inclass,
+ const IDLOptions &opts) {
+ return NativeName(Name(struct_def), &struct_def, opts) + " *" +
+ (inclass ? "" : Name(struct_def) + "::") +
+ "UnPack(const flatbuffers::resolver_function_t *_resolver" +
+ (inclass ? " = nullptr" : "") + ") const";
+ }
+
+ std::string TableUnPackToSignature(const StructDef &struct_def, bool inclass,
+ const IDLOptions &opts) {
+ return "void " + (inclass ? "" : Name(struct_def) + "::") + "UnPackTo(" +
+ NativeName(Name(struct_def), &struct_def, opts) + " *" +
+ "_o, const flatbuffers::resolver_function_t *_resolver" +
+ (inclass ? " = nullptr" : "") + ") const";
+ }
+
+ void GenMiniReflectPre(const StructDef *struct_def) {
+ code_.SetValue("NAME", struct_def->name);
+ code_ += "inline const flatbuffers::TypeTable *{{NAME}}TypeTable();";
+ code_ += "";
+ }
+
+ void GenMiniReflect(const StructDef *struct_def, const EnumDef *enum_def) {
+ code_.SetValue("NAME", struct_def ? struct_def->name : enum_def->name);
+ code_.SetValue("SEQ_TYPE",
+ struct_def ? (struct_def->fixed ? "ST_STRUCT" : "ST_TABLE")
+ : (enum_def->is_union ? "ST_UNION" : "ST_ENUM"));
+ auto num_fields =
+ struct_def ? struct_def->fields.vec.size() : enum_def->size();
+ code_.SetValue("NUM_FIELDS", NumToString(num_fields));
+ std::vector<std::string> names;
+ std::vector<Type> types;
+
+ if (struct_def) {
+ for (auto it = struct_def->fields.vec.begin();
+ it != struct_def->fields.vec.end(); ++it) {
+ const auto &field = **it;
+ names.push_back(Name(field));
+ types.push_back(field.value.type);
+ }
+ } else {
+ for (auto it = enum_def->Vals().begin(); it != enum_def->Vals().end();
+ ++it) {
+ const auto &ev = **it;
+ names.push_back(Name(ev));
+ types.push_back(enum_def->is_union ? ev.union_type
+ : Type(enum_def->underlying_type));
+ }
+ }
+ std::string ts;
+ std::vector<std::string> type_refs;
+ for (auto it = types.begin(); it != types.end(); ++it) {
+ auto &type = *it;
+ if (!ts.empty()) ts += ",\n ";
+ auto is_vector = type.base_type == BASE_TYPE_VECTOR;
+ auto bt = is_vector ? type.element : type.base_type;
+ auto et = IsScalar(bt) || bt == BASE_TYPE_STRING
+ ? bt - BASE_TYPE_UTYPE + ET_UTYPE
+ : ET_SEQUENCE;
+ int ref_idx = -1;
+ std::string ref_name =
+ type.struct_def
+ ? WrapInNameSpace(*type.struct_def)
+ : type.enum_def ? WrapInNameSpace(*type.enum_def) : "";
+ if (!ref_name.empty()) {
+ auto rit = type_refs.begin();
+ for (; rit != type_refs.end(); ++rit) {
+ if (*rit == ref_name) {
+ ref_idx = static_cast<int>(rit - type_refs.begin());
+ break;
+ }
+ }
+ if (rit == type_refs.end()) {
+ ref_idx = static_cast<int>(type_refs.size());
+ type_refs.push_back(ref_name);
+ }
+ }
+ ts += "{ flatbuffers::" + std::string(ElementaryTypeNames()[et]) + ", " +
+ NumToString(is_vector) + ", " + NumToString(ref_idx) + " }";
+ }
+ std::string rs;
+ for (auto it = type_refs.begin(); it != type_refs.end(); ++it) {
+ if (!rs.empty()) rs += ",\n ";
+ rs += *it + "TypeTable";
+ }
+ std::string ns;
+ for (auto it = names.begin(); it != names.end(); ++it) {
+ if (!ns.empty()) ns += ",\n ";
+ ns += "\"" + *it + "\"";
+ }
+ std::string vs;
+ const auto consecutive_enum_from_zero =
+ enum_def && enum_def->MinValue()->IsZero() &&
+ ((enum_def->size() - 1) == enum_def->Distance());
+ if (enum_def && !consecutive_enum_from_zero) {
+ for (auto it = enum_def->Vals().begin(); it != enum_def->Vals().end();
+ ++it) {
+ const auto &ev = **it;
+ if (!vs.empty()) vs += ", ";
+ vs += NumToStringCpp(enum_def->ToString(ev),
+ enum_def->underlying_type.base_type);
+ }
+ } else if (struct_def && struct_def->fixed) {
+ for (auto it = struct_def->fields.vec.begin();
+ it != struct_def->fields.vec.end(); ++it) {
+ const auto &field = **it;
+ vs += NumToString(field.value.offset);
+ vs += ", ";
+ }
+ vs += NumToString(struct_def->bytesize);
+ }
+ code_.SetValue("TYPES", ts);
+ code_.SetValue("REFS", rs);
+ code_.SetValue("NAMES", ns);
+ code_.SetValue("VALUES", vs);
+ code_ += "inline const flatbuffers::TypeTable *{{NAME}}TypeTable() {";
+ if (num_fields) {
+ code_ += " static const flatbuffers::TypeCode type_codes[] = {";
+ code_ += " {{TYPES}}";
+ code_ += " };";
+ }
+ if (!type_refs.empty()) {
+ code_ += " static const flatbuffers::TypeFunction type_refs[] = {";
+ code_ += " {{REFS}}";
+ code_ += " };";
+ }
+ if (!vs.empty()) {
+ // Problem with uint64_t values greater than 9223372036854775807ULL.
+ code_ += " static const int64_t values[] = { {{VALUES}} };";
+ }
+ auto has_names =
+ num_fields && parser_.opts.mini_reflect == IDLOptions::kTypesAndNames;
+ if (has_names) {
+ code_ += " static const char * const names[] = {";
+ code_ += " {{NAMES}}";
+ code_ += " };";
+ }
+ code_ += " static const flatbuffers::TypeTable tt = {";
+ code_ += std::string(" flatbuffers::{{SEQ_TYPE}}, {{NUM_FIELDS}}, ") +
+ (num_fields ? "type_codes, " : "nullptr, ") +
+ (!type_refs.empty() ? "type_refs, " : "nullptr, ") +
+ (!vs.empty() ? "values, " : "nullptr, ") +
+ (has_names ? "names" : "nullptr");
+ code_ += " };";
+ code_ += " return &tt;";
+ code_ += "}";
+ code_ += "";
+ }
+
+ // Generate an enum declaration,
+ // an enum string lookup table,
+ // and an enum array of values
+
+ void GenEnum(const EnumDef &enum_def) {
+ code_.SetValue("ENUM_NAME", Name(enum_def));
+ code_.SetValue("BASE_TYPE", GenTypeBasic(enum_def.underlying_type, false));
+
+ GenComment(enum_def.doc_comment);
+ code_ += GenEnumDecl(enum_def) + "\\";
+ // MSVC doesn't support int64/uint64 enum without explicitly declared enum
+ // type. The value 4611686018427387904ULL is truncated to zero with warning:
+ // "warning C4309: 'initializing': truncation of constant value".
+ auto add_type = parser_.opts.scoped_enums;
+ add_type |= (enum_def.underlying_type.base_type == BASE_TYPE_LONG);
+ add_type |= (enum_def.underlying_type.base_type == BASE_TYPE_ULONG);
+ if (add_type) code_ += " : {{BASE_TYPE}}\\";
+ code_ += " {";
+
+ code_.SetValue("SEP", ",");
+ auto add_sep = false;
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ const auto &ev = **it;
+ if (add_sep) code_ += "{{SEP}}";
+ GenComment(ev.doc_comment, " ");
+ code_.SetValue("KEY", GenEnumValDecl(enum_def, Name(ev)));
+ code_.SetValue("VALUE",
+ NumToStringCpp(enum_def.ToString(ev),
+ enum_def.underlying_type.base_type));
+ code_ += " {{KEY}} = {{VALUE}}\\";
+ add_sep = true;
+ }
+ const EnumVal *minv = enum_def.MinValue();
+ const EnumVal *maxv = enum_def.MaxValue();
+
+ if (parser_.opts.scoped_enums || parser_.opts.prefixed_enums) {
+ FLATBUFFERS_ASSERT(minv && maxv);
+
+ code_.SetValue("SEP", ",\n");
+ if (enum_def.attributes.Lookup("bit_flags")) {
+ code_.SetValue("KEY", GenEnumValDecl(enum_def, "NONE"));
+ code_.SetValue("VALUE", "0");
+ code_ += "{{SEP}} {{KEY}} = {{VALUE}}\\";
+
+ code_.SetValue("KEY", GenEnumValDecl(enum_def, "ANY"));
+ code_.SetValue("VALUE",
+ NumToStringCpp(enum_def.AllFlags(),
+ enum_def.underlying_type.base_type));
+ code_ += "{{SEP}} {{KEY}} = {{VALUE}}\\";
+ } else { // MIN & MAX are useless for bit_flags
+ code_.SetValue("KEY", GenEnumValDecl(enum_def, "MIN"));
+ code_.SetValue("VALUE", GenEnumValDecl(enum_def, minv->name));
+ code_ += "{{SEP}} {{KEY}} = {{VALUE}}\\";
+
+ code_.SetValue("KEY", GenEnumValDecl(enum_def, "MAX"));
+ code_.SetValue("VALUE", GenEnumValDecl(enum_def, maxv->name));
+ code_ += "{{SEP}} {{KEY}} = {{VALUE}}\\";
+ }
+ }
+ code_ += "";
+ code_ += "};";
+
+ if (parser_.opts.scoped_enums && enum_def.attributes.Lookup("bit_flags")) {
+ code_ +=
+ "FLATBUFFERS_DEFINE_BITMASK_OPERATORS({{ENUM_NAME}}, {{BASE_TYPE}})";
+ }
+ code_ += "";
+
+ // Generate an array of all enumeration values
+ auto num_fields = NumToString(enum_def.size());
+ code_ += "inline const {{ENUM_NAME}} (&EnumValues{{ENUM_NAME}}())[" +
+ num_fields + "] {";
+ code_ += " static const {{ENUM_NAME}} values[] = {";
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ const auto &ev = **it;
+ auto value = GetEnumValUse(enum_def, ev);
+ auto suffix = *it != enum_def.Vals().back() ? "," : "";
+ code_ += " " + value + suffix;
+ }
+ code_ += " };";
+ code_ += " return values;";
+ code_ += "}";
+ code_ += "";
+
+ // 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
+ // the moment we simply don't output a table at all.
+ auto range = enum_def.Distance();
+ // Average distance between values above which we consider a table
+ // "too sparse". Change at will.
+ static const uint64_t kMaxSparseness = 5;
+ if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
+ code_ += "inline const char * const *EnumNames{{ENUM_NAME}}() {";
+ code_ += " static const char * const names[" +
+ NumToString(range + 1 + 1) + "] = {";
+
+ auto val = enum_def.Vals().front();
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ auto ev = *it;
+ for (auto k = enum_def.Distance(val, ev); k > 1; --k) {
+ code_ += " \"\",";
+ }
+ val = ev;
+ code_ += " \"" + Name(*ev) + "\",";
+ }
+ code_ += " nullptr";
+ code_ += " };";
+
+ code_ += " return names;";
+ code_ += "}";
+ code_ += "";
+
+ code_ += "inline const char *EnumName{{ENUM_NAME}}({{ENUM_NAME}} e) {";
+
+ code_ += " if (e < " + GetEnumValUse(enum_def, *enum_def.MinValue()) +
+ " || e > " + GetEnumValUse(enum_def, *enum_def.MaxValue()) +
+ ") return \"\";";
+
+ code_ += " const size_t index = static_cast<size_t>(e)\\";
+ if (enum_def.MinValue()->IsNonZero()) {
+ auto vals = GetEnumValUse(enum_def, *enum_def.MinValue());
+ code_ += " - static_cast<size_t>(" + vals + ")\\";
+ }
+ code_ += ";";
+
+ code_ += " return EnumNames{{ENUM_NAME}}()[index];";
+ code_ += "}";
+ code_ += "";
+ } else {
+ code_ += "inline const char *EnumName{{ENUM_NAME}}({{ENUM_NAME}} e) {";
+
+ code_ += " switch (e) {";
+
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ const auto &ev = **it;
+ code_ += " case " + GetEnumValUse(enum_def, ev) + ": return \"" +
+ Name(ev) + "\";";
+ }
+
+ code_ += " default: return \"\";";
+ code_ += " }";
+
+ code_ += "}";
+ code_ += "";
+ }
+
+ // Generate type traits for unions to map from a type to union enum value.
+ if (enum_def.is_union && !enum_def.uses_multiple_type_instances) {
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ const auto &ev = **it;
+
+ if (it == enum_def.Vals().begin()) {
+ code_ += "template<typename T> struct {{ENUM_NAME}}Traits {";
+ } else {
+ auto name = GetUnionElement(ev, true, true);
+ code_ += "template<> struct {{ENUM_NAME}}Traits<" + name + "> {";
+ }
+
+ auto value = GetEnumValUse(enum_def, ev);
+ code_ += " static const {{ENUM_NAME}} enum_value = " + value + ";";
+ code_ += "};";
+ code_ += "";
+ }
+ }
+
+ if (parser_.opts.generate_object_based_api && enum_def.is_union) {
+ // Generate a union type
+ code_.SetValue("NAME", Name(enum_def));
+ FLATBUFFERS_ASSERT(enum_def.Lookup("NONE"));
+ code_.SetValue("NONE", GetEnumValUse(enum_def, *enum_def.Lookup("NONE")));
+
+ code_ += "struct {{NAME}}Union {";
+ code_ += " {{NAME}} type;";
+ code_ += " void *value;";
+ code_ += "";
+ code_ += " {{NAME}}Union() : type({{NONE}}), value(nullptr) {}";
+ code_ += " {{NAME}}Union({{NAME}}Union&& u) FLATBUFFERS_NOEXCEPT :";
+ code_ += " type({{NONE}}), value(nullptr)";
+ code_ += " { std::swap(type, u.type); std::swap(value, u.value); }";
+ code_ += " {{NAME}}Union(const {{NAME}}Union &) FLATBUFFERS_NOEXCEPT;";
+ code_ +=
+ " {{NAME}}Union &operator=(const {{NAME}}Union &u) "
+ "FLATBUFFERS_NOEXCEPT";
+ code_ +=
+ " { {{NAME}}Union t(u); std::swap(type, t.type); std::swap(value, "
+ "t.value); return *this; }";
+ code_ +=
+ " {{NAME}}Union &operator=({{NAME}}Union &&u) FLATBUFFERS_NOEXCEPT";
+ code_ +=
+ " { std::swap(type, u.type); std::swap(value, u.value); return "
+ "*this; }";
+ code_ += " ~{{NAME}}Union() { Reset(); }";
+ code_ += "";
+ code_ += " void Reset();";
+ code_ += "";
+ if (!enum_def.uses_multiple_type_instances) {
+ code_ += "#ifndef FLATBUFFERS_CPP98_STL";
+ code_ += " template <typename T>";
+ code_ += " void Set(T&& val) {";
+ code_ += " using RT = typename std::remove_reference<T>::type;";
+ code_ += " Reset();";
+ code_ += " type = {{NAME}}Traits<typename RT::TableType>::enum_value;";
+ code_ += " if (type != {{NONE}}) {";
+ code_ += " value = new RT(std::forward<T>(val));";
+ code_ += " }";
+ code_ += " }";
+ code_ += "#endif // FLATBUFFERS_CPP98_STL";
+ code_ += "";
+ }
+ code_ += " " + UnionUnPackSignature(enum_def, true) + ";";
+ code_ += " " + UnionPackSignature(enum_def, true) + ";";
+ code_ += "";
+
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ const auto &ev = **it;
+ if (ev.IsZero()) { continue; }
+
+ const auto native_type =
+ NativeName(GetUnionElement(ev, true, true, true),
+ ev.union_type.struct_def, parser_.opts);
+ code_.SetValue("NATIVE_TYPE", native_type);
+ code_.SetValue("NATIVE_NAME", Name(ev));
+ code_.SetValue("NATIVE_ID", GetEnumValUse(enum_def, ev));
+
+ code_ += " {{NATIVE_TYPE}} *As{{NATIVE_NAME}}() {";
+ code_ += " return type == {{NATIVE_ID}} ?";
+ code_ += " reinterpret_cast<{{NATIVE_TYPE}} *>(value) : nullptr;";
+ code_ += " }";
+
+ code_ += " const {{NATIVE_TYPE}} *As{{NATIVE_NAME}}() const {";
+ code_ += " return type == {{NATIVE_ID}} ?";
+ code_ +=
+ " reinterpret_cast<const {{NATIVE_TYPE}} *>(value) : nullptr;";
+ code_ += " }";
+ }
+ code_ += "};";
+ code_ += "";
+
+ if (parser_.opts.gen_compare) {
+ code_ += "";
+ code_ +=
+ "inline bool operator==(const {{NAME}}Union &lhs, const "
+ "{{NAME}}Union &rhs) {";
+ code_ += " if (lhs.type != rhs.type) return false;";
+ code_ += " switch (lhs.type) {";
+
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ const auto &ev = **it;
+ code_.SetValue("NATIVE_ID", GetEnumValUse(enum_def, ev));
+ if (ev.IsNonZero()) {
+ const auto native_type =
+ NativeName(GetUnionElement(ev, true, true, true),
+ ev.union_type.struct_def, parser_.opts);
+ code_.SetValue("NATIVE_TYPE", native_type);
+ code_ += " case {{NATIVE_ID}}: {";
+ code_ +=
+ " return *(reinterpret_cast<const {{NATIVE_TYPE}} "
+ "*>(lhs.value)) ==";
+ code_ +=
+ " *(reinterpret_cast<const {{NATIVE_TYPE}} "
+ "*>(rhs.value));";
+ code_ += " }";
+ } else {
+ code_ += " case {{NATIVE_ID}}: {";
+ code_ += " return true;"; // "NONE" enum value.
+ code_ += " }";
+ }
+ }
+ code_ += " default: {";
+ code_ += " return false;";
+ code_ += " }";
+ code_ += " }";
+ code_ += "}";
+
+ code_ += "";
+ code_ +=
+ "inline bool operator!=(const {{NAME}}Union &lhs, const "
+ "{{NAME}}Union &rhs) {";
+ code_ += " return !(lhs == rhs);";
+ code_ += "}";
+ code_ += "";
+ }
+ }
+
+ if (enum_def.is_union) {
+ code_ += UnionVerifySignature(enum_def) + ";";
+ code_ += UnionVectorVerifySignature(enum_def) + ";";
+ code_ += "";
+ }
+ }
+
+ void GenUnionPost(const EnumDef &enum_def) {
+ // 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.
+ code_.SetValue("ENUM_NAME", Name(enum_def));
+
+ code_ += "inline " + UnionVerifySignature(enum_def) + " {";
+ code_ += " switch (type) {";
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ const auto &ev = **it;
+ code_.SetValue("LABEL", GetEnumValUse(enum_def, ev));
+
+ if (ev.IsNonZero()) {
+ code_.SetValue("TYPE", GetUnionElement(ev, true, true));
+ code_ += " case {{LABEL}}: {";
+ auto getptr =
+ " auto ptr = reinterpret_cast<const {{TYPE}} *>(obj);";
+ if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
+ if (ev.union_type.struct_def->fixed) {
+ code_ += " return verifier.Verify<{{TYPE}}>(static_cast<const "
+ "uint8_t *>(obj), 0);";
+ } else {
+ code_ += getptr;
+ code_ += " return verifier.VerifyTable(ptr);";
+ }
+ } else if (ev.union_type.base_type == BASE_TYPE_STRING) {
+ code_ += getptr;
+ code_ += " return verifier.VerifyString(ptr);";
+ } else {
+ FLATBUFFERS_ASSERT(false);
+ }
+ code_ += " }";
+ } else {
+ code_ += " case {{LABEL}}: {";
+ code_ += " return true;"; // "NONE" enum value.
+ code_ += " }";
+ }
+ }
+ code_ += " default: return false;";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+
+ code_ += "inline " + UnionVectorVerifySignature(enum_def) + " {";
+ code_ += " if (!values || !types) return !values && !types;";
+ code_ += " if (values->size() != types->size()) return false;";
+ code_ += " for (flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {";
+ code_ += " if (!Verify" + Name(enum_def) + "(";
+ code_ += " verifier, values->Get(i), types->GetEnum<" +
+ Name(enum_def) + ">(i))) {";
+ code_ += " return false;";
+ code_ += " }";
+ code_ += " }";
+ code_ += " return true;";
+ code_ += "}";
+ code_ += "";
+
+ if (parser_.opts.generate_object_based_api) {
+ // Generate union Unpack() and Pack() functions.
+ code_ += "inline " + UnionUnPackSignature(enum_def, false) + " {";
+ code_ += " switch (type) {";
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ const auto &ev = **it;
+ if (ev.IsZero()) { continue; }
+
+ code_.SetValue("LABEL", GetEnumValUse(enum_def, ev));
+ code_.SetValue("TYPE", GetUnionElement(ev, true, true));
+ code_ += " case {{LABEL}}: {";
+ code_ += " auto ptr = reinterpret_cast<const {{TYPE}} *>(obj);";
+ if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
+ if (ev.union_type.struct_def->fixed) {
+ code_ += " return new " +
+ WrapInNameSpace(*ev.union_type.struct_def) + "(*ptr);";
+ } else {
+ code_ += " return ptr->UnPack(resolver);";
+ }
+ } else if (ev.union_type.base_type == BASE_TYPE_STRING) {
+ code_ += " return new std::string(ptr->c_str(), ptr->size());";
+ } else {
+ FLATBUFFERS_ASSERT(false);
+ }
+ code_ += " }";
+ }
+ code_ += " default: return nullptr;";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+
+ code_ += "inline " + UnionPackSignature(enum_def, false) + " {";
+ code_ += " switch (type) {";
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ auto &ev = **it;
+ if (ev.IsZero()) { continue; }
+
+ code_.SetValue("LABEL", GetEnumValUse(enum_def, ev));
+ code_.SetValue("TYPE",
+ NativeName(GetUnionElement(ev, true, true, true),
+ ev.union_type.struct_def, parser_.opts));
+ code_.SetValue("NAME", GetUnionElement(ev, false, true));
+ code_ += " case {{LABEL}}: {";
+ code_ += " auto ptr = reinterpret_cast<const {{TYPE}} *>(value);";
+ if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
+ if (ev.union_type.struct_def->fixed) {
+ code_ += " return _fbb.CreateStruct(*ptr).Union();";
+ } else {
+ code_ +=
+ " return Create{{NAME}}(_fbb, ptr, _rehasher).Union();";
+ }
+ } else if (ev.union_type.base_type == BASE_TYPE_STRING) {
+ code_ += " return _fbb.CreateString(*ptr).Union();";
+ } else {
+ FLATBUFFERS_ASSERT(false);
+ }
+ code_ += " }";
+ }
+ code_ += " default: return 0;";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+
+ // Union copy constructor
+ code_ +=
+ "inline {{ENUM_NAME}}Union::{{ENUM_NAME}}Union(const "
+ "{{ENUM_NAME}}Union &u) FLATBUFFERS_NOEXCEPT : type(u.type), "
+ "value(nullptr) {";
+ code_ += " switch (type) {";
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ const auto &ev = **it;
+ if (ev.IsZero()) { continue; }
+ code_.SetValue("LABEL", GetEnumValUse(enum_def, ev));
+ code_.SetValue("TYPE",
+ NativeName(GetUnionElement(ev, true, true, true),
+ ev.union_type.struct_def, parser_.opts));
+ code_ += " case {{LABEL}}: {";
+ bool copyable = true;
+ if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
+ // Don't generate code to copy if table is not copyable.
+ // TODO(wvo): make tables copyable instead.
+ for (auto fit = ev.union_type.struct_def->fields.vec.begin();
+ fit != ev.union_type.struct_def->fields.vec.end(); ++fit) {
+ const auto &field = **fit;
+ if (!field.deprecated && field.value.type.struct_def &&
+ !field.native_inline) {
+ copyable = false;
+ break;
+ }
+ }
+ }
+ if (copyable) {
+ code_ +=
+ " value = new {{TYPE}}(*reinterpret_cast<{{TYPE}} *>"
+ "(u.value));";
+ } else {
+ code_ +=
+ " FLATBUFFERS_ASSERT(false); // {{TYPE}} not copyable.";
+ }
+ code_ += " break;";
+ code_ += " }";
+ }
+ code_ += " default:";
+ code_ += " break;";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+
+ // Union Reset() function.
+ FLATBUFFERS_ASSERT(enum_def.Lookup("NONE"));
+ code_.SetValue("NONE", GetEnumValUse(enum_def, *enum_def.Lookup("NONE")));
+
+ code_ += "inline void {{ENUM_NAME}}Union::Reset() {";
+ code_ += " switch (type) {";
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ const auto &ev = **it;
+ if (ev.IsZero()) { continue; }
+ code_.SetValue("LABEL", GetEnumValUse(enum_def, ev));
+ code_.SetValue("TYPE",
+ NativeName(GetUnionElement(ev, true, true, true),
+ ev.union_type.struct_def, parser_.opts));
+ code_ += " case {{LABEL}}: {";
+ code_ += " auto ptr = reinterpret_cast<{{TYPE}} *>(value);";
+ code_ += " delete ptr;";
+ code_ += " break;";
+ code_ += " }";
+ }
+ code_ += " default: break;";
+ code_ += " }";
+ code_ += " value = nullptr;";
+ code_ += " type = {{NONE}};";
+ code_ += "}";
+ code_ += "";
+ }
+ }
+
+ // Generates a value with optionally a cast applied if the field has a
+ // different underlying type from its interface type (currently only the
+ // case for enums. "from" specify the direction, true meaning from the
+ // underlying type to the interface type.
+ std::string GenUnderlyingCast(const FieldDef &field, bool from,
+ const std::string &val) {
+ if (from && field.value.type.base_type == BASE_TYPE_BOOL) {
+ return val + " != 0";
+ } else if ((field.value.type.enum_def &&
+ IsScalar(field.value.type.base_type)) ||
+ field.value.type.base_type == BASE_TYPE_BOOL) {
+ return "static_cast<" + GenTypeBasic(field.value.type, from) + ">(" +
+ val + ")";
+ } else {
+ return val;
+ }
+ }
+
+ std::string GenFieldOffsetName(const FieldDef &field) {
+ std::string uname = Name(field);
+ std::transform(uname.begin(), uname.end(), uname.begin(), ToUpper);
+ return "VT_" + uname;
+ }
+
+ void GenFullyQualifiedNameGetter(const StructDef &struct_def,
+ const std::string &name) {
+ if (!parser_.opts.generate_name_strings) { return; }
+ auto fullname = struct_def.defined_namespace->GetFullyQualifiedName(name);
+ code_.SetValue("NAME", fullname);
+ code_.SetValue("CONSTEXPR", "FLATBUFFERS_CONSTEXPR");
+ code_ += " static {{CONSTEXPR}} const char *GetFullyQualifiedName() {";
+ code_ += " return \"{{NAME}}\";";
+ code_ += " }";
+ }
+
+ std::string GenDefaultConstant(const FieldDef &field) {
+ if (IsFloat(field.value.type.base_type))
+ return float_const_gen_.GenFloatConstant(field);
+ else
+ return NumToStringCpp(field.value.constant, field.value.type.base_type);
+ }
+
+ std::string GetDefaultScalarValue(const FieldDef &field, bool is_ctor) {
+ if (field.value.type.enum_def && IsScalar(field.value.type.base_type)) {
+ auto ev = field.value.type.enum_def->FindByValue(field.value.constant);
+ if (ev) {
+ return WrapInNameSpace(field.value.type.enum_def->defined_namespace,
+ GetEnumValUse(*field.value.type.enum_def, *ev));
+ } else {
+ return GenUnderlyingCast(
+ field, true,
+ NumToStringCpp(field.value.constant, field.value.type.base_type));
+ }
+ } else if (field.value.type.base_type == BASE_TYPE_BOOL) {
+ return field.value.constant == "0" ? "false" : "true";
+ } else if (field.attributes.Lookup("cpp_type")) {
+ if (is_ctor) {
+ if (PtrType(&field) == "naked") {
+ return "nullptr";
+ } else {
+ return "";
+ }
+ } else {
+ return "0";
+ }
+ } else {
+ return GenDefaultConstant(field);
+ }
+ }
+
+ void GenParam(const FieldDef &field, bool direct, const char *prefix) {
+ code_.SetValue("PRE", prefix);
+ code_.SetValue("PARAM_NAME", Name(field));
+ if (direct && field.value.type.base_type == BASE_TYPE_STRING) {
+ code_.SetValue("PARAM_TYPE", "const char *");
+ code_.SetValue("PARAM_VALUE", "nullptr");
+ } else if (direct && field.value.type.base_type == BASE_TYPE_VECTOR) {
+ const auto vtype = field.value.type.VectorType();
+ std::string type;
+ if (IsStruct(vtype)) {
+ type = WrapInNameSpace(*vtype.struct_def);
+ } else {
+ type = GenTypeWire(vtype, "", false);
+ }
+ code_.SetValue("PARAM_TYPE", "const std::vector<" + type + "> *");
+ code_.SetValue("PARAM_VALUE", "nullptr");
+ } else {
+ code_.SetValue("PARAM_TYPE", GenTypeWire(field.value.type, " ", true));
+ code_.SetValue("PARAM_VALUE", GetDefaultScalarValue(field, false));
+ }
+ code_ += "{{PRE}}{{PARAM_TYPE}}{{PARAM_NAME}} = {{PARAM_VALUE}}\\";
+ }
+
+ // Generate a member, including a default value for scalars and raw pointers.
+ void GenMember(const FieldDef &field) {
+ if (!field.deprecated && // Deprecated fields won't be accessible.
+ field.value.type.base_type != BASE_TYPE_UTYPE &&
+ (field.value.type.base_type != BASE_TYPE_VECTOR ||
+ field.value.type.element != BASE_TYPE_UTYPE)) {
+ auto type = GenTypeNative(field.value.type, false, field);
+ auto cpp_type = field.attributes.Lookup("cpp_type");
+ auto full_type =
+ (cpp_type
+ ? (field.value.type.base_type == BASE_TYPE_VECTOR
+ ? "std::vector<" +
+ GenTypeNativePtr(cpp_type->constant, &field,
+ false) +
+ "> "
+ : GenTypeNativePtr(cpp_type->constant, &field, false))
+ : type + " ");
+ code_.SetValue("FIELD_TYPE", full_type);
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_ += " {{FIELD_TYPE}}{{FIELD_NAME}};";
+ }
+ }
+
+ // Generate the default constructor for this struct. Properly initialize all
+ // scalar members with default values.
+ void GenDefaultConstructor(const StructDef &struct_def) {
+ std::string initializer_list;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated && // Deprecated fields won't be accessible.
+ field.value.type.base_type != BASE_TYPE_UTYPE) {
+ auto cpp_type = field.attributes.Lookup("cpp_type");
+ auto native_default = field.attributes.Lookup("native_default");
+ // Scalar types get parsed defaults, raw pointers get nullptrs.
+ if (IsScalar(field.value.type.base_type)) {
+ if (!initializer_list.empty()) { initializer_list += ",\n "; }
+ initializer_list += Name(field);
+ initializer_list +=
+ "(" +
+ (native_default ? std::string(native_default->constant)
+ : GetDefaultScalarValue(field, true)) +
+ ")";
+ } else if (field.value.type.base_type == BASE_TYPE_STRUCT) {
+ if (IsStruct(field.value.type)) {
+ if (native_default) {
+ if (!initializer_list.empty()) {
+ initializer_list += ",\n ";
+ }
+ initializer_list +=
+ Name(field) + "(" + native_default->constant + ")";
+ }
+ }
+ } else if (cpp_type && field.value.type.base_type != BASE_TYPE_VECTOR) {
+ if (!initializer_list.empty()) { initializer_list += ",\n "; }
+ initializer_list += Name(field) + "(0)";
+ }
+ }
+ }
+ if (!initializer_list.empty()) {
+ initializer_list = "\n : " + initializer_list;
+ }
+
+ code_.SetValue("NATIVE_NAME",
+ NativeName(Name(struct_def), &struct_def, parser_.opts));
+ code_.SetValue("INIT_LIST", initializer_list);
+
+ code_ += " {{NATIVE_NAME}}(){{INIT_LIST}} {";
+ code_ += " }";
+ }
+
+ void GenCompareOperator(const StructDef &struct_def,
+ std::string accessSuffix = "") {
+ std::string compare_op;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated && // Deprecated fields won't be accessible.
+ field.value.type.base_type != BASE_TYPE_UTYPE &&
+ (field.value.type.base_type != BASE_TYPE_VECTOR ||
+ field.value.type.element != BASE_TYPE_UTYPE)) {
+ if (!compare_op.empty()) { compare_op += " &&\n "; }
+ auto accessor = Name(field) + accessSuffix;
+ compare_op += "(lhs." + accessor + " == rhs." + accessor + ")";
+ }
+ }
+
+ std::string cmp_lhs;
+ std::string cmp_rhs;
+ if (compare_op.empty()) {
+ cmp_lhs = "";
+ cmp_rhs = "";
+ compare_op = " return true;";
+ } else {
+ cmp_lhs = "lhs";
+ cmp_rhs = "rhs";
+ compare_op = " return\n " + compare_op + ";";
+ }
+
+ code_.SetValue("CMP_OP", compare_op);
+ code_.SetValue("CMP_LHS", cmp_lhs);
+ code_.SetValue("CMP_RHS", cmp_rhs);
+ code_ += "";
+ code_ +=
+ "inline bool operator==(const {{NATIVE_NAME}} &{{CMP_LHS}}, const "
+ "{{NATIVE_NAME}} &{{CMP_RHS}}) {";
+ code_ += "{{CMP_OP}}";
+ code_ += "}";
+
+ code_ += "";
+ code_ +=
+ "inline bool operator!=(const {{NATIVE_NAME}} &lhs, const "
+ "{{NATIVE_NAME}} &rhs) {";
+ code_ += " return !(lhs == rhs);";
+ code_ += "}";
+ code_ += "";
+ }
+
+ void GenOperatorNewDelete(const StructDef &struct_def) {
+ if (auto native_custom_alloc =
+ struct_def.attributes.Lookup("native_custom_alloc")) {
+ code_ += " inline void *operator new (std::size_t count) {";
+ code_ += " return " + native_custom_alloc->constant +
+ "<{{NATIVE_NAME}}>().allocate(count / sizeof({{NATIVE_NAME}}));";
+ code_ += " }";
+ code_ += " inline void operator delete (void *ptr) {";
+ code_ += " return " + native_custom_alloc->constant +
+ "<{{NATIVE_NAME}}>().deallocate(static_cast<{{NATIVE_NAME}}*>("
+ "ptr),1);";
+ code_ += " }";
+ }
+ }
+
+ void GenNativeTable(const StructDef &struct_def) {
+ const auto native_name =
+ NativeName(Name(struct_def), &struct_def, parser_.opts);
+ code_.SetValue("STRUCT_NAME", Name(struct_def));
+ code_.SetValue("NATIVE_NAME", native_name);
+
+ // Generate a C++ object that can hold an unpacked version of this table.
+ code_ += "struct {{NATIVE_NAME}} : public flatbuffers::NativeTable {";
+ code_ += " typedef {{STRUCT_NAME}} TableType;";
+ GenFullyQualifiedNameGetter(struct_def, native_name);
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ GenMember(**it);
+ }
+ GenOperatorNewDelete(struct_def);
+ GenDefaultConstructor(struct_def);
+ code_ += "};";
+ if (parser_.opts.gen_compare) GenCompareOperator(struct_def);
+ code_ += "";
+ }
+
+ // Generate the code to call the appropriate Verify function(s) for a field.
+ void GenVerifyCall(const FieldDef &field, const char *prefix) {
+ code_.SetValue("PRE", prefix);
+ code_.SetValue("NAME", Name(field));
+ code_.SetValue("REQUIRED", field.required ? "Required" : "");
+ code_.SetValue("SIZE", GenTypeSize(field.value.type));
+ code_.SetValue("OFFSET", GenFieldOffsetName(field));
+ if (IsScalar(field.value.type.base_type) || IsStruct(field.value.type)) {
+ code_ +=
+ "{{PRE}}VerifyField{{REQUIRED}}<{{SIZE}}>(verifier, {{OFFSET}})\\";
+ } else {
+ code_ += "{{PRE}}VerifyOffset{{REQUIRED}}(verifier, {{OFFSET}})\\";
+ }
+
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_UNION: {
+ code_.SetValue("ENUM_NAME", field.value.type.enum_def->name);
+ code_.SetValue("SUFFIX", UnionTypeFieldSuffix());
+ code_ +=
+ "{{PRE}}Verify{{ENUM_NAME}}(verifier, {{NAME}}(), "
+ "{{NAME}}{{SUFFIX}}())\\";
+ break;
+ }
+ case BASE_TYPE_STRUCT: {
+ if (!field.value.type.struct_def->fixed) {
+ code_ += "{{PRE}}verifier.VerifyTable({{NAME}}())\\";
+ }
+ break;
+ }
+ case BASE_TYPE_STRING: {
+ code_ += "{{PRE}}verifier.VerifyString({{NAME}}())\\";
+ break;
+ }
+ case BASE_TYPE_VECTOR: {
+ code_ += "{{PRE}}verifier.VerifyVector({{NAME}}())\\";
+
+ switch (field.value.type.element) {
+ case BASE_TYPE_STRING: {
+ code_ += "{{PRE}}verifier.VerifyVectorOfStrings({{NAME}}())\\";
+ break;
+ }
+ case BASE_TYPE_STRUCT: {
+ if (!field.value.type.struct_def->fixed) {
+ code_ += "{{PRE}}verifier.VerifyVectorOfTables({{NAME}}())\\";
+ }
+ break;
+ }
+ case BASE_TYPE_UNION: {
+ code_.SetValue("ENUM_NAME", field.value.type.enum_def->name);
+ code_ +=
+ "{{PRE}}Verify{{ENUM_NAME}}Vector(verifier, {{NAME}}(), "
+ "{{NAME}}_type())\\";
+ break;
+ }
+ default: break;
+ }
+ break;
+ }
+ default: { break; }
+ }
+ }
+
+ // Generate CompareWithValue method for a key field.
+ void GenKeyFieldMethods(const FieldDef &field) {
+ FLATBUFFERS_ASSERT(field.key);
+ const bool is_string = (field.value.type.base_type == BASE_TYPE_STRING);
+
+ code_ += " bool KeyCompareLessThan(const {{STRUCT_NAME}} *o) const {";
+ if (is_string) {
+ // use operator< of flatbuffers::String
+ code_ += " return *{{FIELD_NAME}}() < *o->{{FIELD_NAME}}();";
+ } else {
+ code_ += " return {{FIELD_NAME}}() < o->{{FIELD_NAME}}();";
+ }
+ code_ += " }";
+
+ if (is_string) {
+ code_ += " int KeyCompareWithValue(const char *val) const {";
+ code_ += " return strcmp({{FIELD_NAME}}()->c_str(), val);";
+ code_ += " }";
+ } else {
+ FLATBUFFERS_ASSERT(IsScalar(field.value.type.base_type));
+ auto type = GenTypeBasic(field.value.type, false);
+ if (parser_.opts.scoped_enums && field.value.type.enum_def &&
+ IsScalar(field.value.type.base_type)) {
+ type = GenTypeGet(field.value.type, " ", "const ", " *", true);
+ }
+ // Returns {field<val: -1, field==val: 0, field>val: +1}.
+ code_.SetValue("KEY_TYPE", type);
+ code_ += " int KeyCompareWithValue({{KEY_TYPE}} val) const {";
+ code_ +=
+ " return static_cast<int>({{FIELD_NAME}}() > val) - "
+ "static_cast<int>({{FIELD_NAME}}() < val);";
+ code_ += " }";
+ }
+ }
+
+ // Generate an accessor struct, builder structs & function for a table.
+ void GenTable(const StructDef &struct_def) {
+ if (parser_.opts.generate_object_based_api) { GenNativeTable(struct_def); }
+
+ // Generate an accessor struct, with methods of the form:
+ // type name() const { return GetField<type>(offset, defaultval); }
+ GenComment(struct_def.doc_comment);
+
+ code_.SetValue("STRUCT_NAME", Name(struct_def));
+ code_ +=
+ "struct {{STRUCT_NAME}} FLATBUFFERS_FINAL_CLASS"
+ " : private flatbuffers::Table {";
+ if (parser_.opts.generate_object_based_api) {
+ code_ += " typedef {{NATIVE_NAME}} NativeTableType;";
+ }
+ if (parser_.opts.mini_reflect != IDLOptions::kNone) {
+ code_ +=
+ " static const flatbuffers::TypeTable *MiniReflectTypeTable() {";
+ code_ += " return {{STRUCT_NAME}}TypeTable();";
+ code_ += " }";
+ }
+
+ GenFullyQualifiedNameGetter(struct_def, Name(struct_def));
+
+ // Generate field id constants.
+ if (struct_def.fields.vec.size() > 0) {
+ // We need to add a trailing comma to all elements except the last one as
+ // older versions of gcc complain about this.
+ code_.SetValue("SEP", "");
+ code_ +=
+ " enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated) {
+ // Deprecated fields won't be accessible.
+ continue;
+ }
+
+ code_.SetValue("OFFSET_NAME", GenFieldOffsetName(field));
+ code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset));
+ code_ += "{{SEP}} {{OFFSET_NAME}} = {{OFFSET_VALUE}}\\";
+ code_.SetValue("SEP", ",\n");
+ }
+ code_ += "";
+ code_ += " };";
+ }
+
+ // Generate the accessors.
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated) {
+ // Deprecated fields won't be accessible.
+ continue;
+ }
+
+ const bool is_struct = IsStruct(field.value.type);
+ const bool is_scalar = IsScalar(field.value.type.base_type);
+ code_.SetValue("FIELD_NAME", Name(field));
+
+ // Call a different accessor for pointers, that indirects.
+ std::string accessor = "";
+ if (is_scalar) {
+ accessor = "GetField<";
+ } else if (is_struct) {
+ accessor = "GetStruct<";
+ } else {
+ accessor = "GetPointer<";
+ }
+ auto offset_str = GenFieldOffsetName(field);
+ auto offset_type =
+ GenTypeGet(field.value.type, "", "const ", " *", false);
+
+ auto call = accessor + offset_type + ">(" + offset_str;
+ // Default value as second arg for non-pointer types.
+ if (is_scalar) { call += ", " + GenDefaultConstant(field); }
+ call += ")";
+
+ std::string afterptr = " *" + NullableExtension();
+ GenComment(field.doc_comment, " ");
+ code_.SetValue("FIELD_TYPE", GenTypeGet(field.value.type, " ", "const ",
+ afterptr.c_str(), true));
+ code_.SetValue("FIELD_VALUE", GenUnderlyingCast(field, true, call));
+ code_.SetValue("NULLABLE_EXT", NullableExtension());
+
+ code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}() const {";
+ code_ += " return {{FIELD_VALUE}};";
+ code_ += " }";
+
+ if (field.value.type.base_type == BASE_TYPE_UNION) {
+ auto u = field.value.type.enum_def;
+
+ if (!field.value.type.enum_def->uses_multiple_type_instances)
+ code_ +=
+ " template<typename T> "
+ "const T *{{NULLABLE_EXT}}{{FIELD_NAME}}_as() const;";
+
+ for (auto u_it = u->Vals().begin(); u_it != u->Vals().end(); ++u_it) {
+ auto &ev = **u_it;
+ if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; }
+ auto full_struct_name = GetUnionElement(ev, true, true);
+
+ // @TODO: Mby make this decisions more universal? How?
+ code_.SetValue("U_GET_TYPE",
+ EscapeKeyword(field.name + UnionTypeFieldSuffix()));
+ code_.SetValue(
+ "U_ELEMENT_TYPE",
+ WrapInNameSpace(u->defined_namespace, GetEnumValUse(*u, ev)));
+ code_.SetValue("U_FIELD_TYPE", "const " + full_struct_name + " *");
+ code_.SetValue("U_FIELD_NAME", Name(field) + "_as_" + Name(ev));
+ code_.SetValue("U_NULLABLE", NullableExtension());
+
+ // `const Type *union_name_asType() const` accessor.
+ code_ += " {{U_FIELD_TYPE}}{{U_NULLABLE}}{{U_FIELD_NAME}}() const {";
+ code_ +=
+ " return {{U_GET_TYPE}}() == {{U_ELEMENT_TYPE}} ? "
+ "static_cast<{{U_FIELD_TYPE}}>({{FIELD_NAME}}()) "
+ ": nullptr;";
+ code_ += " }";
+ }
+ }
+
+ if (parser_.opts.mutable_buffer) {
+ if (is_scalar) {
+ const auto type = GenTypeWire(field.value.type, "", false);
+ code_.SetValue("SET_FN", "SetField<" + type + ">");
+ code_.SetValue("OFFSET_NAME", offset_str);
+ code_.SetValue("FIELD_TYPE", GenTypeBasic(field.value.type, true));
+ code_.SetValue("FIELD_VALUE",
+ GenUnderlyingCast(field, false, "_" + Name(field)));
+ code_.SetValue("DEFAULT_VALUE", GenDefaultConstant(field));
+
+ code_ +=
+ " bool mutate_{{FIELD_NAME}}({{FIELD_TYPE}} "
+ "_{{FIELD_NAME}}) {";
+ code_ +=
+ " return {{SET_FN}}({{OFFSET_NAME}}, {{FIELD_VALUE}}, "
+ "{{DEFAULT_VALUE}});";
+ code_ += " }";
+ } else {
+ auto postptr = " *" + NullableExtension();
+ auto type =
+ GenTypeGet(field.value.type, " ", "", postptr.c_str(), true);
+ auto underlying = accessor + type + ">(" + offset_str + ")";
+ code_.SetValue("FIELD_TYPE", type);
+ code_.SetValue("FIELD_VALUE",
+ GenUnderlyingCast(field, true, underlying));
+
+ code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {";
+ code_ += " return {{FIELD_VALUE}};";
+ code_ += " }";
+ }
+ }
+
+ auto nested = field.attributes.Lookup("nested_flatbuffer");
+ if (nested) {
+ std::string qualified_name = nested->constant;
+ auto nested_root = parser_.LookupStruct(nested->constant);
+ if (nested_root == nullptr) {
+ qualified_name = parser_.current_namespace_->GetFullyQualifiedName(
+ nested->constant);
+ nested_root = parser_.LookupStruct(qualified_name);
+ }
+ FLATBUFFERS_ASSERT(nested_root); // Guaranteed to exist by parser.
+ (void)nested_root;
+ code_.SetValue("CPP_NAME", TranslateNameSpace(qualified_name));
+
+ code_ += " const {{CPP_NAME}} *{{FIELD_NAME}}_nested_root() const {";
+ code_ +=
+ " return "
+ "flatbuffers::GetRoot<{{CPP_NAME}}>({{FIELD_NAME}}()->Data());";
+ code_ += " }";
+ }
+
+ if (field.flexbuffer) {
+ code_ +=
+ " flexbuffers::Reference {{FIELD_NAME}}_flexbuffer_root()"
+ " const {";
+ // Both Data() and size() are const-methods, therefore call order
+ // doesn't matter.
+ code_ +=
+ " return flexbuffers::GetRoot({{FIELD_NAME}}()->Data(), "
+ "{{FIELD_NAME}}()->size());";
+ code_ += " }";
+ }
+
+ // Generate a comparison function for this field if it is a key.
+ if (field.key) { GenKeyFieldMethods(field); }
+ }
+
+ // Generate a verifier function that can check a buffer from an untrusted
+ // source will never cause reads outside the buffer.
+ code_ += " bool Verify(flatbuffers::Verifier &verifier) const {";
+ code_ += " return VerifyTableStart(verifier)\\";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated) { continue; }
+ GenVerifyCall(field, " &&\n ");
+ }
+
+ code_ += " &&\n verifier.EndTable();";
+ code_ += " }";
+
+ if (parser_.opts.generate_object_based_api) {
+ // Generate the UnPack() pre declaration.
+ code_ +=
+ " " + TableUnPackSignature(struct_def, true, parser_.opts) + ";";
+ code_ +=
+ " " + TableUnPackToSignature(struct_def, true, parser_.opts) + ";";
+ code_ += " " + TablePackSignature(struct_def, true, parser_.opts) + ";";
+ }
+
+ code_ += "};"; // End of table.
+ code_ += "";
+
+ // Explicit specializations for union accessors
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated || field.value.type.base_type != BASE_TYPE_UNION) {
+ continue;
+ }
+
+ auto u = field.value.type.enum_def;
+ if (u->uses_multiple_type_instances) continue;
+
+ code_.SetValue("FIELD_NAME", Name(field));
+
+ for (auto u_it = u->Vals().begin(); u_it != u->Vals().end(); ++u_it) {
+ auto &ev = **u_it;
+ if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; }
+
+ auto full_struct_name = GetUnionElement(ev, true, true);
+
+ code_.SetValue(
+ "U_ELEMENT_TYPE",
+ WrapInNameSpace(u->defined_namespace, GetEnumValUse(*u, ev)));
+ code_.SetValue("U_FIELD_TYPE", "const " + full_struct_name + " *");
+ code_.SetValue("U_ELEMENT_NAME", full_struct_name);
+ code_.SetValue("U_FIELD_NAME", Name(field) + "_as_" + Name(ev));
+
+ // `template<> const T *union_name_as<T>() const` accessor.
+ code_ +=
+ "template<> "
+ "inline {{U_FIELD_TYPE}}{{STRUCT_NAME}}::{{FIELD_NAME}}_as"
+ "<{{U_ELEMENT_NAME}}>() const {";
+ code_ += " return {{U_FIELD_NAME}}();";
+ code_ += "}";
+ code_ += "";
+ }
+ }
+
+ GenBuilders(struct_def);
+
+ 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, true, parser_.opts) + ";";
+ code_ += "";
+ }
+ }
+
+ void GenBuilders(const StructDef &struct_def) {
+ code_.SetValue("STRUCT_NAME", Name(struct_def));
+
+ // Generate a builder struct:
+ code_ += "struct {{STRUCT_NAME}}Builder {";
+ code_ += " flatbuffers::FlatBufferBuilder &fbb_;";
+ code_ += " flatbuffers::uoffset_t start_;";
+
+ bool has_string_or_vector_fields = false;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) {
+ const bool is_scalar = IsScalar(field.value.type.base_type);
+ const bool is_string = field.value.type.base_type == BASE_TYPE_STRING;
+ const bool is_vector = field.value.type.base_type == BASE_TYPE_VECTOR;
+ if (is_string || is_vector) { has_string_or_vector_fields = true; }
+
+ std::string offset = GenFieldOffsetName(field);
+ std::string name = GenUnderlyingCast(field, false, Name(field));
+ std::string value = is_scalar ? GenDefaultConstant(field) : "";
+
+ // Generate accessor functions of the form:
+ // void add_name(type name) {
+ // fbb_.AddElement<type>(offset, name, default);
+ // }
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_.SetValue("FIELD_TYPE", GenTypeWire(field.value.type, " ", true));
+ code_.SetValue("ADD_OFFSET", Name(struct_def) + "::" + offset);
+ code_.SetValue("ADD_NAME", name);
+ code_.SetValue("ADD_VALUE", value);
+ if (is_scalar) {
+ const auto type = GenTypeWire(field.value.type, "", false);
+ code_.SetValue("ADD_FN", "AddElement<" + type + ">");
+ } else if (IsStruct(field.value.type)) {
+ code_.SetValue("ADD_FN", "AddStruct");
+ } else {
+ code_.SetValue("ADD_FN", "AddOffset");
+ }
+
+ code_ += " void add_{{FIELD_NAME}}({{FIELD_TYPE}}{{FIELD_NAME}}) {";
+ code_ += " fbb_.{{ADD_FN}}(\\";
+ if (is_scalar) {
+ code_ += "{{ADD_OFFSET}}, {{ADD_NAME}}, {{ADD_VALUE}});";
+ } else {
+ code_ += "{{ADD_OFFSET}}, {{ADD_NAME}});";
+ }
+ code_ += " }";
+ }
+ }
+
+ // Builder constructor
+ code_ +=
+ " explicit {{STRUCT_NAME}}Builder(flatbuffers::FlatBufferBuilder "
+ "&_fbb)";
+ code_ += " : fbb_(_fbb) {";
+ code_ += " start_ = fbb_.StartTable();";
+ code_ += " }";
+
+ // Assignment operator;
+ code_ +=
+ " {{STRUCT_NAME}}Builder &operator="
+ "(const {{STRUCT_NAME}}Builder &);";
+
+ // Finish() function.
+ code_ += " flatbuffers::Offset<{{STRUCT_NAME}}> Finish() {";
+ code_ += " const auto end = fbb_.EndTable(start_);";
+ code_ += " auto o = flatbuffers::Offset<{{STRUCT_NAME}}>(end);";
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated && field.required) {
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_.SetValue("OFFSET_NAME", GenFieldOffsetName(field));
+ code_ += " fbb_.Required(o, {{STRUCT_NAME}}::{{OFFSET_NAME}});";
+ }
+ }
+ code_ += " return o;";
+ code_ += " }";
+ code_ += "};";
+ code_ += "";
+
+ // Generate a convenient CreateX function that uses the above builder
+ // to create a table in one go.
+ code_ +=
+ "inline flatbuffers::Offset<{{STRUCT_NAME}}> "
+ "Create{{STRUCT_NAME}}(";
+ code_ += " flatbuffers::FlatBufferBuilder &_fbb\\";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) { GenParam(field, false, ",\n "); }
+ }
+ code_ += ") {";
+
+ code_ += " {{STRUCT_NAME}}Builder builder_(_fbb);";
+ for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
+ size; size /= 2) {
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated && (!struct_def.sortbysize ||
+ size == SizeOf(field.value.type.base_type))) {
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_ += " builder_.add_{{FIELD_NAME}}({{FIELD_NAME}});";
+ }
+ }
+ }
+ code_ += " return builder_.Finish();";
+ code_ += "}";
+ code_ += "";
+
+ // Generate a CreateXDirect function with vector types as parameters
+ if (has_string_or_vector_fields) {
+ code_ +=
+ "inline flatbuffers::Offset<{{STRUCT_NAME}}> "
+ "Create{{STRUCT_NAME}}Direct(";
+ code_ += " flatbuffers::FlatBufferBuilder &_fbb\\";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) { GenParam(field, true, ",\n "); }
+ }
+ // Need to call "Create" with the struct namespace.
+ const auto qualified_create_name =
+ struct_def.defined_namespace->GetFullyQualifiedName("Create");
+ code_.SetValue("CREATE_NAME", TranslateNameSpace(qualified_create_name));
+ code_ += ") {";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) {
+ code_.SetValue("FIELD_NAME", Name(field));
+ if (field.value.type.base_type == BASE_TYPE_STRING) {
+ if (!field.shared) {
+ code_.SetValue("CREATE_STRING", "CreateString");
+ } else {
+ code_.SetValue("CREATE_STRING", "CreateSharedString");
+ }
+ code_ +=
+ " auto {{FIELD_NAME}}__ = {{FIELD_NAME}} ? "
+ "_fbb.{{CREATE_STRING}}({{FIELD_NAME}}) : 0;";
+ } else if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ code_ += " auto {{FIELD_NAME}}__ = {{FIELD_NAME}} ? \\";
+ const auto vtype = field.value.type.VectorType();
+ if (IsStruct(vtype)) {
+ const auto type = WrapInNameSpace(*vtype.struct_def);
+ code_ += "_fbb.CreateVectorOfStructs<" + type + ">\\";
+ } else {
+ const auto type = GenTypeWire(vtype, "", false);
+ code_ += "_fbb.CreateVector<" + type + ">\\";
+ }
+ code_ += "(*{{FIELD_NAME}}) : 0;";
+ }
+ }
+ }
+ code_ += " return {{CREATE_NAME}}{{STRUCT_NAME}}(";
+ code_ += " _fbb\\";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) {
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_ += ",\n {{FIELD_NAME}}\\";
+ if (field.value.type.base_type == BASE_TYPE_STRING ||
+ field.value.type.base_type == BASE_TYPE_VECTOR) {
+ code_ += "__\\";
+ }
+ }
+ }
+ code_ += ");";
+ code_ += "}";
+ code_ += "";
+ }
+ }
+
+ std::string GenUnionUnpackVal(const FieldDef &afield,
+ const char *vec_elem_access,
+ const char *vec_type_access) {
+ return afield.value.type.enum_def->name + "Union::UnPack(" + "_e" +
+ vec_elem_access + ", " +
+ EscapeKeyword(afield.name + UnionTypeFieldSuffix()) + "()" +
+ vec_type_access + ", _resolver)";
+ }
+
+ std::string GenUnpackVal(const Type &type, const std::string &val,
+ bool invector, const FieldDef &afield) {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: {
+ if (FlexibleStringConstructor(&afield)) {
+ return NativeString(&afield) + "(" + val + "->c_str(), " + val +
+ "->size())";
+ } else {
+ return val + "->str()";
+ }
+ }
+ case BASE_TYPE_STRUCT: {
+ const auto name = WrapInNameSpace(*type.struct_def);
+ if (IsStruct(type)) {
+ auto native_type = type.struct_def->attributes.Lookup("native_type");
+ if (native_type) {
+ return "flatbuffers::UnPack(*" + val + ")";
+ } else if (invector || afield.native_inline) {
+ return "*" + val;
+ } else {
+ const auto ptype = GenTypeNativePtr(name, &afield, true);
+ return ptype + "(new " + name + "(*" + val + "))";
+ }
+ } else {
+ const auto ptype = GenTypeNativePtr(
+ NativeName(name, type.struct_def, parser_.opts), &afield, true);
+ return ptype + "(" + val + "->UnPack(_resolver))";
+ }
+ }
+ case BASE_TYPE_UNION: {
+ return GenUnionUnpackVal(
+ afield, invector ? "->Get(_i)" : "",
+ invector ? ("->GetEnum<" + type.enum_def->name + ">(_i)").c_str()
+ : "");
+ }
+ default: {
+ return val;
+ break;
+ }
+ }
+ }
+
+ std::string GenUnpackFieldStatement(const FieldDef &field,
+ const FieldDef *union_field) {
+ std::string code;
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_VECTOR: {
+ auto cpp_type = field.attributes.Lookup("cpp_type");
+ std::string indexing;
+ if (field.value.type.enum_def) {
+ indexing += "static_cast<" +
+ WrapInNameSpace(*field.value.type.enum_def) + ">(";
+ }
+ indexing += "_e->Get(_i)";
+ if (field.value.type.enum_def) { indexing += ")"; }
+ if (field.value.type.element == BASE_TYPE_BOOL) { indexing += " != 0"; }
+
+ // Generate code that pushes data from _e to _o in the form:
+ // for (uoffset_t i = 0; i < _e->size(); ++i) {
+ // _o->field.push_back(_e->Get(_i));
+ // }
+ auto name = Name(field);
+ if (field.value.type.element == BASE_TYPE_UTYPE) {
+ name = StripUnionType(Name(field));
+ }
+ auto access =
+ field.value.type.element == BASE_TYPE_UTYPE
+ ? ".type"
+ : (field.value.type.element == BASE_TYPE_UNION ? ".value" : "");
+ code += "{ _o->" + name + ".resize(_e->size()); ";
+ code += "for (flatbuffers::uoffset_t _i = 0;";
+ code += " _i < _e->size(); _i++) { ";
+ if (cpp_type) {
+ // Generate code that resolves the cpp pointer type, of the form:
+ // if (resolver)
+ // (*resolver)(&_o->field, (hash_value_t)(_e));
+ // else
+ // _o->field = nullptr;
+ code += "//vector resolver, " + PtrType(&field) + "\n";
+ code += "if (_resolver) ";
+ code += "(*_resolver)";
+ code += "(reinterpret_cast<void **>(&_o->" + name + "[_i]" + access +
+ "), ";
+ code += "static_cast<flatbuffers::hash_value_t>(" + indexing + "));";
+ if (PtrType(&field) == "naked") {
+ code += " else ";
+ code += "_o->" + name + "[_i]" + access + " = nullptr";
+ } else {
+ // code += " else ";
+ // code += "_o->" + name + "[_i]" + access + " = " +
+ // GenTypeNativePtr(cpp_type->constant, &field, true) + "();";
+ code += "/* else do nothing */";
+ }
+ } else {
+ code += "_o->" + name + "[_i]" + access + " = ";
+ code += GenUnpackVal(field.value.type.VectorType(), indexing, true,
+ field);
+ }
+ code += "; } }";
+ break;
+ }
+ case BASE_TYPE_UTYPE: {
+ FLATBUFFERS_ASSERT(union_field->value.type.base_type ==
+ BASE_TYPE_UNION);
+ // Generate code that sets the union type, of the form:
+ // _o->field.type = _e;
+ code += "_o->" + union_field->name + ".type = _e;";
+ break;
+ }
+ case BASE_TYPE_UNION: {
+ // Generate code that sets the union value, of the form:
+ // _o->field.value = Union::Unpack(_e, field_type(), resolver);
+ code += "_o->" + Name(field) + ".value = ";
+ code += GenUnionUnpackVal(field, "", "");
+ code += ";";
+ break;
+ }
+ default: {
+ auto cpp_type = field.attributes.Lookup("cpp_type");
+ if (cpp_type) {
+ // Generate code that resolves the cpp pointer type, of the form:
+ // if (resolver)
+ // (*resolver)(&_o->field, (hash_value_t)(_e));
+ // else
+ // _o->field = nullptr;
+ code += "//scalar resolver, " + PtrType(&field) + " \n";
+ code += "if (_resolver) ";
+ code += "(*_resolver)";
+ code += "(reinterpret_cast<void **>(&_o->" + Name(field) + "), ";
+ code += "static_cast<flatbuffers::hash_value_t>(_e));";
+ if (PtrType(&field) == "naked") {
+ code += " else ";
+ code += "_o->" + Name(field) + " = nullptr;";
+ } else {
+ // code += " else ";
+ // code += "_o->" + Name(field) + " = " +
+ // GenTypeNativePtr(cpp_type->constant, &field, true) + "();";
+ code += "/* else do nothing */;";
+ }
+ } else {
+ // Generate code for assigning the value, of the form:
+ // _o->field = value;
+ code += "_o->" + Name(field) + " = ";
+ code += GenUnpackVal(field.value.type, "_e", false, field) + ";";
+ }
+ break;
+ }
+ }
+ return code;
+ }
+
+ std::string GenCreateParam(const FieldDef &field) {
+ const IDLOptions &opts = parser_.opts;
+
+ std::string value = "_o->";
+ if (field.value.type.base_type == BASE_TYPE_UTYPE) {
+ value += StripUnionType(Name(field));
+ value += ".type";
+ } else {
+ value += Name(field);
+ }
+ if (field.value.type.base_type != BASE_TYPE_VECTOR &&
+ field.attributes.Lookup("cpp_type")) {
+ auto type = GenTypeBasic(field.value.type, false);
+ value =
+ "_rehasher ? "
+ "static_cast<" +
+ type + ">((*_rehasher)(" + value + GenPtrGet(field) + ")) : 0";
+ }
+
+ std::string code;
+ switch (field.value.type.base_type) {
+ // String fields are of the form:
+ // _fbb.CreateString(_o->field)
+ // or
+ // _fbb.CreateSharedString(_o->field)
+ case BASE_TYPE_STRING: {
+ if (!field.shared) {
+ code += "_fbb.CreateString(";
+ } else {
+ code += "_fbb.CreateSharedString(";
+ }
+ code += value;
+ code.push_back(')');
+
+ // For optional fields, check to see if there actually is any data
+ // in _o->field before attempting to access it. If there isn't,
+ // depending on set_empty_to_null either set it to 0 or an empty string.
+ if (!field.required) {
+ auto empty_value =
+ opts.set_empty_to_null ? "0" : "_fbb.CreateSharedString(\"\")";
+ code = value + ".empty() ? " + empty_value + " : " + code;
+ }
+ break;
+ }
+ // Vector fields come in several flavours, of the forms:
+ // _fbb.CreateVector(_o->field);
+ // _fbb.CreateVector((const utype*)_o->field.data(), _o->field.size());
+ // _fbb.CreateVectorOfStrings(_o->field)
+ // _fbb.CreateVectorOfStructs(_o->field)
+ // _fbb.CreateVector<Offset<T>>(_o->field.size() [&](size_t i) {
+ // return CreateT(_fbb, _o->Get(i), rehasher);
+ // });
+ case BASE_TYPE_VECTOR: {
+ auto vector_type = field.value.type.VectorType();
+ switch (vector_type.base_type) {
+ case BASE_TYPE_STRING: {
+ if (NativeString(&field) == "std::string") {
+ code += "_fbb.CreateVectorOfStrings(" + value + ")";
+ } else {
+ // Use by-function serialization to emulate
+ // CreateVectorOfStrings(); this works also with non-std strings.
+ code +=
+ "_fbb.CreateVector<flatbuffers::Offset<flatbuffers::String>>"
+ " ";
+ code += "(" + value + ".size(), ";
+ code += "[](size_t i, _VectorArgs *__va) { ";
+ code +=
+ "return __va->__fbb->CreateString(__va->_" + value + "[i]);";
+ code += " }, &_va )";
+ }
+ break;
+ }
+ case BASE_TYPE_STRUCT: {
+ if (IsStruct(vector_type)) {
+ auto native_type =
+ field.value.type.struct_def->attributes.Lookup("native_type");
+ if (native_type) {
+ code += "_fbb.CreateVectorOfNativeStructs<";
+ code += WrapInNameSpace(*vector_type.struct_def) + ">";
+ } else {
+ code += "_fbb.CreateVectorOfStructs";
+ }
+ code += "(" + value + ")";
+ } else {
+ code += "_fbb.CreateVector<flatbuffers::Offset<";
+ code += WrapInNameSpace(*vector_type.struct_def) + ">> ";
+ code += "(" + value + ".size(), ";
+ code += "[](size_t i, _VectorArgs *__va) { ";
+ code += "return Create" + vector_type.struct_def->name;
+ code += "(*__va->__fbb, __va->_" + value + "[i]" +
+ GenPtrGet(field) + ", ";
+ code += "__va->__rehasher); }, &_va )";
+ }
+ break;
+ }
+ case BASE_TYPE_BOOL: {
+ code += "_fbb.CreateVector(" + value + ")";
+ break;
+ }
+ case BASE_TYPE_UNION: {
+ code +=
+ "_fbb.CreateVector<flatbuffers::"
+ "Offset<void>>(" +
+ value +
+ ".size(), [](size_t i, _VectorArgs *__va) { "
+ "return __va->_" +
+ value + "[i].Pack(*__va->__fbb, __va->__rehasher); }, &_va)";
+ break;
+ }
+ case BASE_TYPE_UTYPE: {
+ value = StripUnionType(value);
+ code += "_fbb.CreateVector<uint8_t>(" + value +
+ ".size(), [](size_t i, _VectorArgs *__va) { "
+ "return static_cast<uint8_t>(__va->_" +
+ value + "[i].type); }, &_va)";
+ break;
+ }
+ default: {
+ if (field.value.type.enum_def) {
+ // For enumerations, we need to get access to the array data for
+ // the underlying storage type (eg. uint8_t).
+ const auto basetype = GenTypeBasic(
+ field.value.type.enum_def->underlying_type, false);
+ code += "_fbb.CreateVectorScalarCast<" + basetype +
+ ">(flatbuffers::data(" + value + "), " + value +
+ ".size())";
+ } else if (field.attributes.Lookup("cpp_type")) {
+ auto type = GenTypeBasic(vector_type, false);
+ code += "_fbb.CreateVector<" + type + ">(" + value + ".size(), ";
+ code += "[](size_t i, _VectorArgs *__va) { ";
+ code += "return __va->__rehasher ? ";
+ code += "static_cast<" + type + ">((*__va->__rehasher)";
+ code += "(__va->_" + value + "[i]" + GenPtrGet(field) + ")) : 0";
+ code += "; }, &_va )";
+ } else {
+ code += "_fbb.CreateVector(" + value + ")";
+ }
+ break;
+ }
+ }
+
+ // If set_empty_to_null option is enabled, for optional fields, check to
+ // see if there actually is any data in _o->field before attempting to
+ // access it.
+ if (opts.set_empty_to_null && !field.required) {
+ code = value + ".size() ? " + code + " : 0";
+ }
+ break;
+ }
+ case BASE_TYPE_UNION: {
+ // _o->field.Pack(_fbb);
+ code += value + ".Pack(_fbb)";
+ break;
+ }
+ case BASE_TYPE_STRUCT: {
+ if (IsStruct(field.value.type)) {
+ auto native_type =
+ field.value.type.struct_def->attributes.Lookup("native_type");
+ if (native_type) {
+ code += "flatbuffers::Pack(" + value + ")";
+ } else if (field.native_inline) {
+ code += "&" + value;
+ } else {
+ code += value + " ? " + value + GenPtrGet(field) + " : 0";
+ }
+ } else {
+ // _o->field ? CreateT(_fbb, _o->field.get(), _rehasher);
+ const auto type = field.value.type.struct_def->name;
+ code += value + " ? Create" + type;
+ code += "(_fbb, " + value + GenPtrGet(field) + ", _rehasher)";
+ code += " : 0";
+ }
+ break;
+ }
+ default: {
+ code += value;
+ break;
+ }
+ }
+ return code;
+ }
+
+ // Generate code for tables that needs to come after the regular definition.
+ void GenTablePost(const StructDef &struct_def) {
+ code_.SetValue("STRUCT_NAME", Name(struct_def));
+ code_.SetValue("NATIVE_NAME",
+ NativeName(Name(struct_def), &struct_def, parser_.opts));
+
+ if (parser_.opts.generate_object_based_api) {
+ // Generate the X::UnPack() method.
+ code_ += "inline " +
+ TableUnPackSignature(struct_def, false, parser_.opts) + " {";
+ code_ += " auto _o = new {{NATIVE_NAME}}();";
+ code_ += " UnPackTo(_o, _resolver);";
+ code_ += " return _o;";
+ code_ += "}";
+ code_ += "";
+
+ code_ += "inline " +
+ TableUnPackToSignature(struct_def, false, parser_.opts) + " {";
+ code_ += " (void)_o;";
+ code_ += " (void)_resolver;";
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated) { continue; }
+
+ // Assign a value from |this| to |_o|. Values from |this| are stored
+ // in a variable |_e| by calling this->field_type(). The value is then
+ // assigned to |_o| using the GenUnpackFieldStatement.
+ const bool is_union = field.value.type.base_type == BASE_TYPE_UTYPE;
+ const auto statement =
+ GenUnpackFieldStatement(field, is_union ? *(it + 1) : nullptr);
+
+ code_.SetValue("FIELD_NAME", Name(field));
+ auto prefix = " { auto _e = {{FIELD_NAME}}(); ";
+ auto check = IsScalar(field.value.type.base_type) ? "" : "if (_e) ";
+ auto postfix = " };";
+ code_ += std::string(prefix) + check + statement + postfix;
+ }
+ code_ += "}";
+ code_ += "";
+
+ // Generate the X::Pack member function that simply calls the global
+ // CreateX function.
+ code_ += "inline " + TablePackSignature(struct_def, false, parser_.opts) +
+ " {";
+ code_ += " return Create{{STRUCT_NAME}}(_fbb, _o, _rehasher);";
+ code_ += "}";
+ code_ += "";
+
+ // Generate a CreateX method that works with an unpacked C++ object.
+ code_ += "inline " +
+ TableCreateSignature(struct_def, false, parser_.opts) + " {";
+ code_ += " (void)_rehasher;";
+ code_ += " (void)_o;";
+
+ code_ +=
+ " struct _VectorArgs "
+ "{ flatbuffers::FlatBufferBuilder *__fbb; "
+ "const " +
+ NativeName(Name(struct_def), &struct_def, parser_.opts) +
+ "* __o; "
+ "const flatbuffers::rehasher_function_t *__rehasher; } _va = { "
+ "&_fbb, _o, _rehasher}; (void)_va;";
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) { continue; }
+ code_ += " auto _" + Name(field) + " = " + GenCreateParam(field) + ";";
+ }
+ // Need to call "Create" with the struct namespace.
+ const auto qualified_create_name =
+ struct_def.defined_namespace->GetFullyQualifiedName("Create");
+ code_.SetValue("CREATE_NAME", TranslateNameSpace(qualified_create_name));
+
+ code_ += " return {{CREATE_NAME}}{{STRUCT_NAME}}(";
+ code_ += " _fbb\\";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) { continue; }
+
+ bool pass_by_address = false;
+ if (field.value.type.base_type == BASE_TYPE_STRUCT) {
+ if (IsStruct(field.value.type)) {
+ auto native_type =
+ field.value.type.struct_def->attributes.Lookup("native_type");
+ if (native_type) { pass_by_address = true; }
+ }
+ }
+
+ // Call the CreateX function using values from |_o|.
+ if (pass_by_address) {
+ code_ += ",\n &_" + Name(field) + "\\";
+ } else {
+ code_ += ",\n _" + Name(field) + "\\";
+ }
+ }
+ code_ += ");";
+ code_ += "}";
+ code_ += "";
+ }
+ }
+
+ static void GenPadding(
+ const FieldDef &field, std::string *code_ptr, int *id,
+ const std::function<void(int bits, std::string *code_ptr, int *id)> &f) {
+ if (field.padding) {
+ for (int i = 0; i < 4; i++) {
+ if (static_cast<int>(field.padding) & (1 << i)) {
+ f((1 << i) * 8, code_ptr, id);
+ }
+ }
+ FLATBUFFERS_ASSERT(!(field.padding & ~0xF));
+ }
+ }
+
+ static void PaddingDefinition(int bits, std::string *code_ptr, int *id) {
+ *code_ptr += " int" + NumToString(bits) + "_t padding" +
+ NumToString((*id)++) + "__;";
+ }
+
+ static void PaddingInitializer(int bits, std::string *code_ptr, int *id) {
+ (void)bits;
+ if (*code_ptr != "") *code_ptr += ",\n ";
+ *code_ptr += "padding" + NumToString((*id)++) + "__(0)";
+ }
+
+ static void PaddingNoop(int bits, std::string *code_ptr, int *id) {
+ (void)bits;
+ *code_ptr += " (void)padding" + NumToString((*id)++) + "__;";
+ }
+
+ // Generate an accessor struct with constructor for a flatbuffers struct.
+ void GenStruct(const StructDef &struct_def) {
+ // Generate an accessor struct, with private variables of the form:
+ // type name_;
+ // Generates manual padding and alignment.
+ // Variables are private because they contain little endian data on all
+ // platforms.
+ GenComment(struct_def.doc_comment);
+ code_.SetValue("ALIGN", NumToString(struct_def.minalign));
+ code_.SetValue("STRUCT_NAME", Name(struct_def));
+
+ code_ +=
+ "FLATBUFFERS_MANUALLY_ALIGNED_STRUCT({{ALIGN}}) "
+ "{{STRUCT_NAME}} FLATBUFFERS_FINAL_CLASS {";
+ code_ += " private:";
+
+ int padding_id = 0;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ const auto &field_type = field.value.type;
+ code_.SetValue("FIELD_TYPE", GenTypeGet(field_type, " ", "", " ", false));
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_.SetValue("ARRAY",
+ IsArray(field_type)
+ ? "[" + NumToString(field_type.fixed_length) + "]"
+ : "");
+ code_ += (" {{FIELD_TYPE}}{{FIELD_NAME}}_{{ARRAY}};");
+
+ if (field.padding) {
+ std::string padding;
+ GenPadding(field, &padding, &padding_id, PaddingDefinition);
+ code_ += padding;
+ }
+ }
+
+ // Generate GetFullyQualifiedName
+ code_ += "";
+ code_ += " public:";
+
+ // Make TypeTable accessible via the generated struct.
+ if (parser_.opts.mini_reflect != IDLOptions::kNone) {
+ code_ +=
+ " static const flatbuffers::TypeTable *MiniReflectTypeTable() {";
+ code_ += " return {{STRUCT_NAME}}TypeTable();";
+ code_ += " }";
+ }
+
+ GenFullyQualifiedNameGetter(struct_def, Name(struct_def));
+
+ // Generate a default constructor.
+ code_ += " {{STRUCT_NAME}}() {";
+ code_ +=
+ " memset(static_cast<void *>(this), 0, sizeof({{STRUCT_NAME}}));";
+ code_ += " }";
+
+ // Generate a constructor that takes all fields as arguments,
+ // excluding arrays
+ std::string arg_list;
+ std::string init_list;
+ padding_id = 0;
+ auto first = struct_def.fields.vec.begin();
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (IsArray(field.value.type)) {
+ first++;
+ continue;
+ }
+ const auto member_name = Name(field) + "_";
+ const auto arg_name = "_" + Name(field);
+ const auto arg_type =
+ GenTypeGet(field.value.type, " ", "const ", " &", true);
+
+ if (it != first) { arg_list += ", "; }
+ arg_list += arg_type;
+ arg_list += arg_name;
+ if (!IsArray(field.value.type)) {
+ if (it != first && init_list != "") { init_list += ",\n "; }
+ init_list += member_name;
+ if (IsScalar(field.value.type.base_type)) {
+ auto type = GenUnderlyingCast(field, false, arg_name);
+ init_list += "(flatbuffers::EndianScalar(" + type + "))";
+ } else {
+ init_list += "(" + arg_name + ")";
+ }
+ }
+ if (field.padding) {
+ GenPadding(field, &init_list, &padding_id, PaddingInitializer);
+ }
+ }
+
+ if (!arg_list.empty()) {
+ code_.SetValue("ARG_LIST", arg_list);
+ code_.SetValue("INIT_LIST", init_list);
+ if (!init_list.empty()) {
+ code_ += " {{STRUCT_NAME}}({{ARG_LIST}})";
+ code_ += " : {{INIT_LIST}} {";
+ } else {
+ code_ += " {{STRUCT_NAME}}({{ARG_LIST}}) {";
+ }
+ padding_id = 0;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (IsArray(field.value.type)) {
+ const auto &member = Name(field) + "_";
+ code_ +=
+ " std::memset(" + member + ", 0, sizeof(" + member + "));";
+ }
+ if (field.padding) {
+ std::string padding;
+ GenPadding(field, &padding, &padding_id, PaddingNoop);
+ code_ += padding;
+ }
+ }
+ code_ += " }";
+ }
+
+ // Generate accessor methods of the form:
+ // type name() const { return flatbuffers::EndianScalar(name_); }
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+
+ auto field_type = GenTypeGet(field.value.type, " ",
+ IsArray(field.value.type) ? "" : "const ",
+ IsArray(field.value.type) ? "" : " &", true);
+ auto is_scalar = IsScalar(field.value.type.base_type);
+ auto member = Name(field) + "_";
+ auto value =
+ is_scalar ? "flatbuffers::EndianScalar(" + member + ")" : member;
+
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_.SetValue("FIELD_TYPE", field_type);
+ code_.SetValue("FIELD_VALUE", GenUnderlyingCast(field, true, value));
+
+ GenComment(field.doc_comment, " ");
+
+ // Generate a const accessor function.
+ if (IsArray(field.value.type)) {
+ auto underlying = GenTypeGet(field.value.type, "", "", "", false);
+ code_ += " const flatbuffers::Array<" + field_type + ", " +
+ NumToString(field.value.type.fixed_length) + "> *" +
+ "{{FIELD_NAME}}() const {";
+ code_ += " return reinterpret_cast<const flatbuffers::Array<" +
+ field_type + ", " +
+ NumToString(field.value.type.fixed_length) +
+ "> *>({{FIELD_VALUE}});";
+ code_ += " }";
+ } else {
+ code_ += " {{FIELD_TYPE}}{{FIELD_NAME}}() const {";
+ code_ += " return {{FIELD_VALUE}};";
+ code_ += " }";
+ }
+
+ // Generate a mutable accessor function.
+ if (parser_.opts.mutable_buffer) {
+ auto mut_field_type =
+ GenTypeGet(field.value.type, " ", "",
+ IsArray(field.value.type) ? "" : " &", true);
+ code_.SetValue("FIELD_TYPE", mut_field_type);
+ if (is_scalar) {
+ code_.SetValue("ARG", GenTypeBasic(field.value.type, true));
+ code_.SetValue("FIELD_VALUE",
+ GenUnderlyingCast(field, false, "_" + Name(field)));
+
+ code_ += " void mutate_{{FIELD_NAME}}({{ARG}} _{{FIELD_NAME}}) {";
+ code_ +=
+ " flatbuffers::WriteScalar(&{{FIELD_NAME}}_, "
+ "{{FIELD_VALUE}});";
+ code_ += " }";
+ } else if (IsArray(field.value.type)) {
+ auto underlying = GenTypeGet(field.value.type, "", "", "", false);
+ code_ += " flatbuffers::Array<" + mut_field_type + ", " +
+ NumToString(field.value.type.fixed_length) +
+ "> *" + "mutable_{{FIELD_NAME}}() {";
+ code_ += " return reinterpret_cast<flatbuffers::Array<" +
+ mut_field_type + ", " +
+ NumToString(field.value.type.fixed_length) +
+ "> *>({{FIELD_VALUE}});";
+ code_ += " }";
+ } else {
+ code_ += " {{FIELD_TYPE}}mutable_{{FIELD_NAME}}() {";
+ code_ += " return {{FIELD_VALUE}};";
+ code_ += " }";
+ }
+ }
+
+ // Generate a comparison function for this field if it is a key.
+ if (field.key) { GenKeyFieldMethods(field); }
+ }
+ code_.SetValue("NATIVE_NAME", Name(struct_def));
+ GenOperatorNewDelete(struct_def);
+ code_ += "};";
+
+ code_.SetValue("STRUCT_BYTE_SIZE", NumToString(struct_def.bytesize));
+ code_ += "FLATBUFFERS_STRUCT_END({{STRUCT_NAME}}, {{STRUCT_BYTE_SIZE}});";
+ if (parser_.opts.gen_compare) GenCompareOperator(struct_def, "()");
+ code_ += "";
+ }
+
+ // Set up the correct namespace. Only open a namespace if the existing one is
+ // different (closing/opening only what is necessary).
+ //
+ // The file must start and end with an empty (or null) namespace so that
+ // namespaces are properly opened and closed.
+ void SetNameSpace(const Namespace *ns) {
+ if (cur_name_space_ == ns) { return; }
+
+ // Compute the size of the longest common namespace prefix.
+ // If cur_name_space is A::B::C::D and ns is A::B::E::F::G,
+ // the common prefix is A::B:: and we have old_size = 4, new_size = 5
+ // and common_prefix_size = 2
+ size_t old_size = cur_name_space_ ? cur_name_space_->components.size() : 0;
+ size_t new_size = ns ? ns->components.size() : 0;
+
+ size_t common_prefix_size = 0;
+ while (common_prefix_size < old_size && common_prefix_size < new_size &&
+ ns->components[common_prefix_size] ==
+ cur_name_space_->components[common_prefix_size]) {
+ common_prefix_size++;
+ }
+
+ // Close cur_name_space in reverse order to reach the common prefix.
+ // In the previous example, D then C are closed.
+ for (size_t j = old_size; j > common_prefix_size; --j) {
+ code_ += "} // namespace " + cur_name_space_->components[j - 1];
+ }
+ if (old_size != common_prefix_size) { code_ += ""; }
+
+ // open namespace parts to reach the ns namespace
+ // in the previous example, E, then F, then G are opened
+ for (auto j = common_prefix_size; j != new_size; ++j) {
+ code_ += "namespace " + ns->components[j] + " {";
+ }
+ if (new_size != common_prefix_size) { code_ += ""; }
+
+ cur_name_space_ = ns;
+ }
+
+ const TypedFloatConstantGenerator float_const_gen_;
+};
+
+} // namespace cpp
+
+bool GenerateCPP(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ cpp::CppGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+
+std::string CPPMakeRule(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ const auto filebase =
+ flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
+ const auto included_files = parser.GetIncludedFilesRecursive(file_name);
+ std::string make_rule = GeneratedFileName(path, filebase) + ": ";
+ 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_dart.cpp b/src/idl_gen_dart.cpp
new file mode 100644
index 0000000..2346a85
--- /dev/null
+++ b/src/idl_gen_dart.cpp
@@ -0,0 +1,914 @@
+/*
+ * Copyright 2018 Dan Field
+ *
+ * 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.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+#include <cassert>
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+
+static std::string GeneratedFileName(const std::string &path,
+ const std::string &file_name) {
+ return path + file_name + "_generated.dart";
+}
+
+namespace dart {
+
+const std::string _kFb = "fb";
+// see https://www.dartlang.org/guides/language/language-tour#keywords
+// yeild*, async*, and sync* shouldn't be problems anyway but keeping them in
+static const char *keywords[] = {
+ "abstract", "deferred", "if", "super", "as", "do",
+ "implements", "switch", "assert", "dynamic", "import", "sync*",
+ "async", "else", "in", "this", "async*", "enum",
+ "is", "throw", "await", "export", "library", "true",
+ "break", "external", "new", "try", "case", "extends",
+ "null", "typedef", "catch", "factory", "operator", "var",
+ "class", "false", "part", "void", "const", "final",
+ "rethrow", "while", "continue", "finally", "return", "with",
+ "covariant", "for", "set", "yield", "default", "get",
+ "static", "yield*"
+};
+
+// Iterate through all definitions we haven't generate code for (enums, structs,
+// and tables) and output them to a single file.
+class DartGenerator : public BaseGenerator {
+ public:
+ typedef std::map<std::string, std::string> namespace_code_map;
+
+ DartGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "", ".") {}
+ // Iterate through all definitions we haven't generate code for (enums,
+ // structs, and tables) and output them to a single file.
+ bool generate() {
+ std::string code;
+ namespace_code_map namespace_code;
+ GenerateEnums(&namespace_code);
+ GenerateStructs(&namespace_code);
+
+ for (auto kv = namespace_code.begin(); kv != namespace_code.end(); ++kv) {
+ code.clear();
+ code = code + "// " + FlatBuffersGeneratedWarning() + "\n";
+ code = code +
+ "// ignore_for_file: unused_import, unused_field, "
+ "unused_local_variable\n\n";
+
+ code += "library " + kv->first + ";\n\n";
+
+ code += "import 'dart:typed_data' show Uint8List;\n";
+ code += "import 'package:flat_buffers/flat_buffers.dart' as " + _kFb +
+ ";\n\n";
+
+ if (parser_.opts.include_dependence_headers) {
+ GenIncludeDependencies(&code, kv->first);
+ }
+
+ for (auto kv2 = namespace_code.begin(); kv2 != namespace_code.end();
+ ++kv2) {
+ if (kv2->first != kv->first) {
+ code += "import '" +
+ GeneratedFileName("./", file_name_ + "_" + kv2->first) +
+ "' as " + ImportAliasName(kv2->first) + ";\n";
+ }
+ }
+ code += "\n";
+ code += kv->second;
+
+ if (!SaveFile(
+ GeneratedFileName(path_, file_name_ + "_" + kv->first).c_str(),
+ code, false)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ static std::string ImportAliasName(const std::string &ns) {
+ std::string ret;
+ ret.assign(ns);
+ size_t pos = ret.find('.');
+ while (pos != std::string::npos) {
+ ret.replace(pos, 1, "_");
+ pos = ret.find('.', pos + 1);
+ }
+
+ return ret;
+ }
+
+ static std::string BuildNamespaceName(const Namespace &ns) {
+ std::stringstream sstream;
+ std::copy(ns.components.begin(), ns.components.end() - 1,
+ std::ostream_iterator<std::string>(sstream, "."));
+
+ auto ret = sstream.str() + ns.components.back();
+ for (size_t i = 0; i < ret.size(); i++) {
+ auto lower = tolower(ret[i]);
+ if (lower != ret[i]) {
+ ret[i] = static_cast<char>(lower);
+ if (i != 0 && ret[i - 1] != '.') {
+ ret.insert(i, "_");
+ i++;
+ }
+ }
+ }
+ // std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
+ return ret;
+ }
+
+ void GenIncludeDependencies(std::string* code, const std::string& the_namespace) {
+ for (auto it = parser_.included_files_.begin();
+ it != parser_.included_files_.end(); ++it) {
+ if (it->second.empty()) continue;
+
+ auto noext = flatbuffers::StripExtension(it->second);
+ auto basename = flatbuffers::StripPath(noext);
+
+ *code += "import '" + GeneratedFileName("", basename + "_" + the_namespace) + "';\n";
+ }
+ }
+
+ static std::string EscapeKeyword(const std::string &name) {
+ for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
+ if (name == keywords[i]) { return MakeCamel(name + "_", false); }
+ }
+
+ return MakeCamel(name, false);
+ }
+
+ void GenerateEnums(namespace_code_map *namespace_code) {
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ auto &enum_def = **it;
+ GenEnum(enum_def, namespace_code); // enum_code_ptr);
+ }
+ }
+
+ void GenerateStructs(namespace_code_map *namespace_code) {
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ auto &struct_def = **it;
+ GenStruct(struct_def, namespace_code);
+ }
+ }
+
+ // Generate a documentation comment, if available.
+ static void GenDocComment(const std::vector<std::string> &dc,
+ std::string *code_ptr,
+ const std::string &extra_lines,
+ const char *indent = nullptr) {
+ if (dc.empty() && extra_lines.empty()) {
+ // Don't output empty comment blocks with 0 lines of comment content.
+ return;
+ }
+
+ auto &code = *code_ptr;
+
+ for (auto it = dc.begin(); it != dc.end(); ++it) {
+ if (indent) code += indent;
+ code += "/// " + *it + "\n";
+ }
+ if (!extra_lines.empty()) {
+ if (!dc.empty()) {
+ if (indent) code += indent;
+ code += "///\n";
+ }
+ if (indent) code += indent;
+ std::string::size_type start = 0;
+ for (;;) {
+ auto end = extra_lines.find('\n', start);
+ if (end != std::string::npos) {
+ code += "/// " + extra_lines.substr(start, end - start) + "\n";
+ start = end + 1;
+ } else {
+ code += "/// " + extra_lines.substr(start) + "\n";
+ break;
+ }
+ }
+ }
+ }
+
+ static void GenDocComment(std::string *code_ptr,
+ const std::string &extra_lines) {
+ GenDocComment(std::vector<std::string>(), code_ptr, extra_lines);
+ }
+
+ // Generate an enum declaration and an enum string lookup table.
+ void GenEnum(EnumDef &enum_def, namespace_code_map *namespace_code) {
+ if (enum_def.generated) return;
+ auto ns = BuildNamespaceName(*enum_def.defined_namespace);
+ std::string code;
+ GenDocComment(enum_def.doc_comment, &code, "");
+
+ auto name = enum_def.is_union ? enum_def.name + "TypeId" : enum_def.name;
+ auto is_bit_flags = enum_def.attributes.Lookup("bit_flags");
+
+ code += "class " + name + " {\n";
+ code += " final int value;\n";
+ code += " const " + name + "._(this.value);\n\n";
+ code += " factory " + name + ".fromValue(int value) {\n";
+ code += " if (value == null) value = 0;\n";
+
+ code += " if (!values.containsKey(value)) {\n";
+ code +=
+ " throw new StateError('Invalid value $value for bit flag enum ";
+ code += name + "');\n";
+ code += " }\n";
+
+ code += " return values[value];\n";
+ code += " }\n\n";
+
+ // this is meaningless for bit_flags
+ // however, note that unlike "regular" dart enums this enum can still have
+ // holes.
+ if (!is_bit_flags) {
+ code += " static const int minValue = " +
+ enum_def.ToString(*enum_def.MinValue()) + ";\n";
+ code += " static const int maxValue = " +
+ enum_def.ToString(*enum_def.MaxValue()) + ";\n";
+ }
+
+ code +=
+ " static bool containsValue(int value) =>"
+ " values.containsKey(value);\n\n";
+
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ auto &ev = **it;
+
+ if (!ev.doc_comment.empty()) {
+ if (it != enum_def.Vals().begin()) { code += '\n'; }
+ GenDocComment(ev.doc_comment, &code, "", " ");
+ }
+ code += " static const " + name + " " + ev.name + " = ";
+ code += "const " + name + "._(" + enum_def.ToString(ev) + ");\n";
+ }
+
+ code += " static get values => {";
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ auto &ev = **it;
+ code += enum_def.ToString(ev) + ": " + ev.name + ",";
+ }
+ code += "};\n\n";
+
+ code += " static const " + _kFb + ".Reader<" + name +
+ "> reader = const _" + name + "Reader();\n\n";
+ code += " @override\n";
+ code += " String toString() {\n";
+ code += " return '" + name + "{value: $value}';\n";
+ code += " }\n";
+ code += "}\n\n";
+
+ GenEnumReader(enum_def, name, &code);
+ (*namespace_code)[ns] += code;
+ }
+
+ void GenEnumReader(EnumDef &enum_def, const std::string &name,
+ std::string *code_ptr) {
+ auto &code = *code_ptr;
+
+ code += "class _" + name + "Reader extends " + _kFb + ".Reader<" + name +
+ "> {\n";
+ code += " const _" + name + "Reader();\n\n";
+ code += " @override\n";
+ code += " int get size => 1;\n\n";
+ code += " @override\n";
+ code +=
+ " " + name + " read(" + _kFb + ".BufferContext bc, int offset) =>\n";
+ code += " new " + name + ".fromValue(const " + _kFb + "." +
+ GenType(enum_def.underlying_type) + "Reader().read(bc, offset));\n";
+ code += "}\n\n";
+ }
+
+ static std::string GenType(const Type &type) {
+ switch (type.base_type) {
+ case BASE_TYPE_BOOL: return "Bool";
+ case BASE_TYPE_CHAR: return "Int8";
+ case BASE_TYPE_UTYPE:
+ case BASE_TYPE_UCHAR: return "Uint8";
+ case BASE_TYPE_SHORT: return "Int16";
+ case BASE_TYPE_USHORT: return "Uint16";
+ case BASE_TYPE_INT: return "Int32";
+ case BASE_TYPE_UINT: return "Uint32";
+ case BASE_TYPE_LONG: return "Int64";
+ case BASE_TYPE_ULONG: return "Uint64";
+ case BASE_TYPE_FLOAT: return "Float32";
+ case BASE_TYPE_DOUBLE: return "Float64";
+ case BASE_TYPE_STRING: return "String";
+ case BASE_TYPE_VECTOR: return GenType(type.VectorType());
+ case BASE_TYPE_STRUCT: return type.struct_def->name;
+ case BASE_TYPE_UNION: return type.enum_def->name + "TypeId";
+ default: return "Table";
+ }
+ }
+
+ std::string GenReaderTypeName(const Type &type, Namespace *current_namespace,
+ const FieldDef &def,
+ bool parent_is_vector = false) {
+ if (type.base_type == BASE_TYPE_BOOL) {
+ return "const " + _kFb + ".BoolReader()";
+ } else if (type.base_type == BASE_TYPE_VECTOR) {
+ return "const " + _kFb + ".ListReader<" +
+ GenDartTypeName(type.VectorType(), current_namespace, def) + ">(" +
+ GenReaderTypeName(type.VectorType(), current_namespace, def,
+ true) +
+ ")";
+ } else if (type.base_type == BASE_TYPE_STRING) {
+ return "const " + _kFb + ".StringReader()";
+ }
+ if (IsScalar(type.base_type)) {
+ if (type.enum_def && parent_is_vector) {
+ return GenDartTypeName(type, current_namespace, def) + ".reader";
+ }
+ return "const " + _kFb + "." + GenType(type) + "Reader()";
+ } else {
+ return GenDartTypeName(type, current_namespace, def) + ".reader";
+ }
+ }
+
+ std::string GenDartTypeName(const Type &type, Namespace *current_namespace,
+ const FieldDef &def, bool addBuilder = false) {
+ if (type.enum_def) {
+ if (type.enum_def->is_union && type.base_type != BASE_TYPE_UNION) {
+ return type.enum_def->name + "TypeId";
+ } else if (type.enum_def->is_union) {
+ return "dynamic";
+ } else if (type.base_type != BASE_TYPE_VECTOR) {
+ return type.enum_def->name;
+ }
+ }
+
+ switch (type.base_type) {
+ case BASE_TYPE_BOOL: return "bool";
+ case BASE_TYPE_LONG:
+ case BASE_TYPE_ULONG:
+ case BASE_TYPE_INT:
+ case BASE_TYPE_UINT:
+ case BASE_TYPE_SHORT:
+ case BASE_TYPE_USHORT:
+ case BASE_TYPE_CHAR:
+ case BASE_TYPE_UCHAR: return "int";
+ case BASE_TYPE_FLOAT:
+ case BASE_TYPE_DOUBLE: return "double";
+ case BASE_TYPE_STRING: return "String";
+ case BASE_TYPE_STRUCT:
+ return MaybeWrapNamespace(
+ type.struct_def->name + (addBuilder ? "ObjectBuilder" : ""),
+ current_namespace, def);
+ case BASE_TYPE_VECTOR:
+ return "List<" +
+ GenDartTypeName(type.VectorType(), current_namespace, def,
+ addBuilder) +
+ ">";
+ default: assert(0); return "dynamic";
+ }
+ }
+
+ static const std::string MaybeWrapNamespace(const std::string &type_name,
+ Namespace *current_ns,
+ const FieldDef &field) {
+ auto curr_ns_str = BuildNamespaceName(*current_ns);
+ std::string field_ns_str = "";
+ if (field.value.type.struct_def) {
+ field_ns_str +=
+ BuildNamespaceName(*field.value.type.struct_def->defined_namespace);
+ } else if (field.value.type.enum_def) {
+ field_ns_str +=
+ BuildNamespaceName(*field.value.type.enum_def->defined_namespace);
+ }
+
+ if (field_ns_str != "" && field_ns_str != curr_ns_str) {
+ return ImportAliasName(field_ns_str) + "." + type_name;
+ } else {
+ return type_name;
+ }
+ }
+
+ // Generate an accessor struct with constructor for a flatbuffers struct.
+ void GenStruct(const StructDef &struct_def,
+ namespace_code_map *namespace_code) {
+ if (struct_def.generated) return;
+
+ auto object_namespace = BuildNamespaceName(*struct_def.defined_namespace);
+ std::string code;
+
+ const auto &object_name = struct_def.name;
+
+ // Emit constructor
+
+ GenDocComment(struct_def.doc_comment, &code, "");
+
+ auto reader_name = "_" + object_name + "Reader";
+ auto builder_name = object_name + "Builder";
+ auto object_builder_name = object_name + "ObjectBuilder";
+
+ std::string reader_code, builder_code;
+
+ code += "class " + object_name + " {\n";
+
+ code += " " + object_name + "._(this._bc, this._bcOffset);\n";
+ if (!struct_def.fixed) {
+ code += " factory " + object_name + "(List<int> bytes) {\n";
+ code += " " + _kFb + ".BufferContext rootRef = new " + _kFb +
+ ".BufferContext.fromBytes(bytes);\n";
+ code += " return reader.read(rootRef, 0);\n";
+ code += " }\n";
+ }
+
+ code += "\n";
+ code += " static const " + _kFb + ".Reader<" + object_name +
+ "> reader = const " + reader_name + "();\n\n";
+
+ code += " final " + _kFb + ".BufferContext _bc;\n";
+ code += " final int _bcOffset;\n\n";
+
+ GenImplementationGetters(struct_def, &code);
+
+ code += "}\n\n";
+
+ GenReader(struct_def, &reader_name, &reader_code);
+ GenBuilder(struct_def, &builder_name, &builder_code);
+ GenObjectBuilder(struct_def, &object_builder_name, &builder_code);
+
+ code += reader_code;
+ code += builder_code;
+
+ (*namespace_code)[object_namespace] += code;
+ }
+
+ std::string NamespaceAliasFromUnionType(const std::string &in) {
+ if (in.find('_') == std::string::npos) { return in; }
+
+ std::stringstream ss(in);
+ std::string item;
+ std::vector<std::string> parts;
+ std::string ns;
+
+ while (std::getline(ss, item, '_')) { parts.push_back(item); }
+
+ for (auto it = parts.begin(); it != parts.end() - 1; ++it) {
+ auto &part = *it;
+
+ for (size_t i = 0; i < part.length(); i++) {
+ if (i && !isdigit(part[i]) &&
+ part[i] == static_cast<char>(toupper(part[i]))) {
+ ns += "_";
+ ns += static_cast<char>(tolower(part[i]));
+ } else {
+ ns += static_cast<char>(tolower(part[i]));
+ }
+ }
+ if (it != parts.end() - 2) { ns += "_"; }
+ }
+
+ return ns + "." + parts.back();
+ }
+
+ void GenImplementationGetters(const StructDef &struct_def,
+ std::string *code_ptr) {
+ auto &code = *code_ptr;
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+
+ std::string field_name = MakeCamel(field.name, false);
+ std::string type_name = GenDartTypeName(
+ field.value.type, struct_def.defined_namespace, field, false);
+
+ GenDocComment(field.doc_comment, &code, "", " ");
+
+ code += " " + type_name + " get " + field_name;
+ if (field.value.type.base_type == BASE_TYPE_UNION) {
+ code += " {\n";
+ code += " switch (" + field_name + "Type?.value) {\n";
+ auto &enum_def = *field.value.type.enum_def;
+ for (auto en_it = enum_def.Vals().begin() + 1;
+ en_it != enum_def.Vals().end(); ++en_it) {
+ auto &ev = **en_it;
+
+ auto enum_name = NamespaceAliasFromUnionType(ev.name);
+ code += " case " + enum_def.ToString(ev) + ": return " +
+ enum_name + ".reader.vTableGet(_bc, _bcOffset, " +
+ NumToString(field.value.offset) + ", null);\n";
+ }
+ code += " default: return null;\n";
+ code += " }\n";
+ code += " }\n";
+ } else {
+ code += " => ";
+ if (field.value.type.enum_def &&
+ field.value.type.base_type != BASE_TYPE_VECTOR) {
+ code += "new " +
+ GenDartTypeName(field.value.type,
+ struct_def.defined_namespace, field) +
+ ".fromValue(";
+ }
+
+ code += GenReaderTypeName(field.value.type,
+ struct_def.defined_namespace, field);
+ if (struct_def.fixed) {
+ code +=
+ ".read(_bc, _bcOffset + " + NumToString(field.value.offset) + ")";
+ } else {
+ code += ".vTableGet(_bc, _bcOffset, " +
+ NumToString(field.value.offset) + ", ";
+ if (!field.value.constant.empty() && field.value.constant != "0") {
+ if (IsBool(field.value.type.base_type)) {
+ code += "true";
+ } else {
+ code += field.value.constant;
+ }
+ } else {
+ if (IsBool(field.value.type.base_type)) {
+ code += "false";
+ } else if (IsScalar(field.value.type.base_type)) {
+ code += "0";
+ } else {
+ code += "null";
+ }
+ }
+ code += ")";
+ }
+ if (field.value.type.enum_def &&
+ field.value.type.base_type != BASE_TYPE_VECTOR) {
+ code += ")";
+ }
+ code += ";\n";
+ }
+ }
+
+ code += "\n";
+
+ code += " @override\n";
+ code += " String toString() {\n";
+ code += " return '" + struct_def.name + "{";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ code +=
+ MakeCamel(field.name, false) + ": $" + MakeCamel(field.name, false);
+ if (it != struct_def.fields.vec.end() - 1) { code += ", "; }
+ }
+ code += "}';\n";
+ code += " }\n";
+ }
+
+ void GenReader(const StructDef &struct_def, std::string *reader_name_ptr,
+ std::string *code_ptr) {
+ auto &code = *code_ptr;
+ auto &reader_name = *reader_name_ptr;
+ auto &impl_name = struct_def.name;
+
+ code += "class " + reader_name + " extends " + _kFb;
+ if (struct_def.fixed) {
+ code += ".StructReader<";
+ } else {
+ code += ".TableReader<";
+ }
+ code += impl_name + "> {\n";
+ code += " const " + reader_name + "();\n\n";
+
+ if (struct_def.fixed) {
+ code += " @override\n";
+ code += " int get size => " + NumToString(struct_def.bytesize) + ";\n\n";
+ }
+ code += " @override\n";
+ code += " " + impl_name +
+ " createObject(fb.BufferContext bc, int offset) => \n new " +
+ impl_name + "._(bc, offset);\n";
+ code += "}\n\n";
+ }
+
+ void GenBuilder(const StructDef &struct_def, std::string *builder_name_ptr,
+ std::string *code_ptr) {
+ if (struct_def.fields.vec.size() == 0) { return; }
+ auto &code = *code_ptr;
+ auto &builder_name = *builder_name_ptr;
+
+ code += "class " + builder_name + " {\n";
+ code += " " + builder_name + "(this.fbBuilder) {\n";
+ code += " assert(fbBuilder != null);\n";
+ code += " }\n\n";
+ code += " final " + _kFb + ".Builder fbBuilder;\n\n";
+
+ if (struct_def.fixed) {
+ StructBuilderBody(struct_def, code_ptr);
+ } else {
+ TableBuilderBody(struct_def, code_ptr);
+ }
+
+ code += "}\n\n";
+ }
+
+ void StructBuilderBody(const StructDef &struct_def, std::string *code_ptr) {
+ auto &code = *code_ptr;
+
+ code += " int finish(";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+
+ if (IsStruct(field.value.type)) {
+ code += "fb.StructBuilder";
+ } else {
+ code += GenDartTypeName(field.value.type, struct_def.defined_namespace,
+ field);
+ }
+ code += " " + field.name;
+ if (it != struct_def.fields.vec.end() - 1) { code += ", "; }
+ }
+ code += ") {\n";
+
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ auto &field = **it;
+
+ if (field.deprecated) continue;
+
+ if (field.padding) {
+ code += " fbBuilder.pad(" + NumToString(field.padding) + ");\n";
+ }
+
+ if (IsStruct(field.value.type)) {
+ code += " " + field.name + "();\n";
+ } else {
+ code += " fbBuilder.put" + GenType(field.value.type) + "(";
+ code += field.name;
+ if (field.value.type.enum_def) { code += "?.value"; }
+ code += ");\n";
+ }
+ }
+ code += " return fbBuilder.offset;\n";
+ code += " }\n\n";
+ }
+
+ void TableBuilderBody(const StructDef &struct_def, std::string *code_ptr) {
+ auto &code = *code_ptr;
+
+ code += " void begin() {\n";
+ code += " fbBuilder.startTable();\n";
+ code += " }\n\n";
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+
+ auto offset = it - struct_def.fields.vec.begin();
+
+ if (IsScalar(field.value.type.base_type)) {
+ code += " int add" + MakeCamel(field.name) + "(";
+ code += GenDartTypeName(field.value.type, struct_def.defined_namespace,
+ field);
+ code += " " + MakeCamel(field.name, false) + ") {\n";
+ code += " fbBuilder.add" + GenType(field.value.type) + "(" +
+ NumToString(offset) + ", ";
+ code += MakeCamel(field.name, false);
+ if (field.value.type.enum_def) { code += "?.value"; }
+ code += ");\n";
+ } else if (IsStruct(field.value.type)) {
+ code += " int add" + MakeCamel(field.name) + "(int offset) {\n";
+ code +=
+ " fbBuilder.addStruct(" + NumToString(offset) + ", offset);\n";
+ } else {
+ code += " int add" + MakeCamel(field.name) + "Offset(int offset) {\n";
+ code +=
+ " fbBuilder.addOffset(" + NumToString(offset) + ", offset);\n";
+ }
+ code += " return fbBuilder.offset;\n";
+ code += " }\n";
+ }
+
+ code += "\n";
+ code += " int finish() {\n";
+ code += " return fbBuilder.endTable();\n";
+ code += " }\n";
+ }
+
+ void GenObjectBuilder(const StructDef &struct_def,
+ std::string *builder_name_ptr, std::string *code_ptr) {
+ auto &code = *code_ptr;
+ auto &builder_name = *builder_name_ptr;
+
+ code += "class " + builder_name + " extends " + _kFb + ".ObjectBuilder {\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ code += " final " +
+ GenDartTypeName(field.value.type, struct_def.defined_namespace,
+ field, true) +
+ " _" + MakeCamel(field.name, false) + ";\n";
+ }
+ code += "\n";
+ code += " " + builder_name + "(";
+ if (struct_def.fields.vec.size() != 0) {
+ code +=
+
+ "{\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ code += " " +
+ GenDartTypeName(field.value.type, struct_def.defined_namespace,
+ field, true) +
+ " " + MakeCamel(field.name, false) + ",\n";
+ }
+ code += " })\n";
+ code += " : ";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ code += "_" + MakeCamel(field.name, false) + " = " +
+ MakeCamel(field.name, false);
+ if (it == struct_def.fields.vec.end() - 1) {
+ code += ";\n\n";
+ } else {
+ code += ",\n ";
+ }
+ }
+ } else {
+ code += ");\n\n";
+ }
+
+ code += " /// Finish building, and store into the [fbBuilder].\n";
+ code += " @override\n";
+ code += " int finish(\n";
+ code += " " + _kFb + ".Builder fbBuilder) {\n";
+ code += " assert(fbBuilder != null);\n";
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ if (IsScalar(field.value.type.base_type) || IsStruct(field.value.type))
+ continue;
+
+ code += " final int " + MakeCamel(field.name, false) + "Offset";
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ code +=
+ " = _" + MakeCamel(field.name, false) + "?.isNotEmpty == true\n";
+ code += " ? fbBuilder.writeList";
+ switch (field.value.type.VectorType().base_type) {
+ case BASE_TYPE_STRING:
+ code += "(_" + MakeCamel(field.name, false) +
+ ".map((b) => fbBuilder.writeString(b)).toList())";
+ break;
+ case BASE_TYPE_STRUCT:
+ if (field.value.type.struct_def->fixed) {
+ code += "OfStructs(_" + MakeCamel(field.name, false) + ")";
+ } else {
+ code += "(_" + MakeCamel(field.name, false) +
+ ".map((b) => b.getOrCreateOffset(fbBuilder)).toList())";
+ }
+ break;
+ default:
+ code += GenType(field.value.type.VectorType()) + "(_" +
+ MakeCamel(field.name, false);
+ if (field.value.type.enum_def) { code += ".map((f) => f.value)"; }
+ code += ")";
+ }
+ code += "\n : null;\n";
+ } else if (field.value.type.base_type == BASE_TYPE_STRING) {
+ code += " = fbBuilder.writeString(_" + MakeCamel(field.name, false) + ");\n";
+ } else {
+ code += " = _" + MakeCamel(field.name, false) +
+ "?.getOrCreateOffset(fbBuilder);\n";
+ }
+ }
+
+ code += "\n";
+ if (struct_def.fixed) {
+ StructObjectBuilderBody(struct_def, code_ptr);
+ } else {
+ TableObjectBuilderBody(struct_def, code_ptr);
+ }
+ code += " }\n\n";
+
+ code += " /// Convenience method to serialize to byte list.\n";
+ code += " @override\n";
+ code += " Uint8List toBytes([String fileIdentifier]) {\n";
+ code += " " + _kFb + ".Builder fbBuilder = new ";
+ code += _kFb + ".Builder();\n";
+ code += " int offset = finish(fbBuilder);\n";
+ code += " return fbBuilder.finish(offset, fileIdentifier);\n";
+ code += " }\n";
+ code += "}\n";
+ }
+
+ void StructObjectBuilderBody(const StructDef &struct_def,
+ std::string *code_ptr,
+ bool prependUnderscore = true) {
+ auto &code = *code_ptr;
+
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ auto &field = **it;
+
+ if (field.deprecated) continue;
+
+ if (field.padding) {
+ code += " fbBuilder.pad(" + NumToString(field.padding) + ");\n";
+ }
+
+ if (IsStruct(field.value.type)) {
+ code += " ";
+ if (prependUnderscore) { code += "_"; }
+ code += field.name + ".finish(fbBuilder);\n";
+ } else {
+ code += " fbBuilder.put" + GenType(field.value.type) + "(";
+ if (prependUnderscore) { code += "_"; }
+ code += field.name;
+ if (field.value.type.enum_def) { code += "?.value"; }
+ code += ");\n";
+ }
+ }
+
+ code += " return fbBuilder.offset;\n";
+ }
+
+ void TableObjectBuilderBody(const StructDef &struct_def,
+ std::string *code_ptr,
+ bool prependUnderscore = true) {
+ std::string &code = *code_ptr;
+ code += " fbBuilder.startTable();\n";
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+
+ if (field.deprecated) continue;
+
+ auto offset = it - struct_def.fields.vec.begin();
+ if (IsScalar(field.value.type.base_type)) {
+ code += " fbBuilder.add" + GenType(field.value.type) + "(" +
+ NumToString(offset) + ", ";
+ if (prependUnderscore) { code += "_"; }
+ code += MakeCamel(field.name, false);
+ if (field.value.type.enum_def) { code += "?.value"; }
+ code += ");\n";
+ } else if (IsStruct(field.value.type)) {
+ code += " if (";
+ if (prependUnderscore) { code += "_"; }
+ code += MakeCamel(field.name, false) + " != null) {\n";
+ code += " fbBuilder.addStruct(" + NumToString(offset) + ", ";
+ code += "_" + MakeCamel(field.name, false) + ".finish(fbBuilder));\n";
+ code += " }\n";
+ } else {
+ code +=
+ " if (" + MakeCamel(field.name, false) + "Offset != null) {\n";
+ code += " fbBuilder.addOffset(" + NumToString(offset) + ", " +
+ MakeCamel(field.name, false) + "Offset);\n";
+ code += " }\n";
+ }
+ }
+ code += " return fbBuilder.endTable();\n";
+ }
+};
+} // namespace dart
+
+bool GenerateDart(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ dart::DartGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+
+std::string DartMakeRule(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ assert(parser.opts.lang <= IDLOptions::kMAX);
+
+ auto filebase =
+ flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
+ auto make_rule = GeneratedFileName(path, filebase) + ": ";
+
+ auto included_files = parser.GetIncludedFilesRecursive(file_name);
+ 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_fbs.cpp b/src/idl_gen_fbs.cpp
new file mode 100644
index 0000000..e5f3723
--- /dev/null
+++ b/src/idl_gen_fbs.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+
+static std::string GenType(const Type &type, bool underlying = false) {
+ switch (type.base_type) {
+ case BASE_TYPE_STRUCT:
+ return type.struct_def->defined_namespace->GetFullyQualifiedName(
+ type.struct_def->name);
+ case BASE_TYPE_VECTOR: return "[" + GenType(type.VectorType()) + "]";
+ default:
+ if (type.enum_def && !underlying) {
+ return type.enum_def->defined_namespace->GetFullyQualifiedName(
+ type.enum_def->name);
+ } else {
+ return kTypeNames[type.base_type];
+ }
+ }
+}
+
+static void GenNameSpace(const Namespace &name_space, std::string *_schema,
+ const Namespace **last_namespace) {
+ if (*last_namespace == &name_space) return;
+ *last_namespace = &name_space;
+ auto &schema = *_schema;
+ schema += "namespace ";
+ for (auto it = name_space.components.begin();
+ it != name_space.components.end(); ++it) {
+ if (it != name_space.components.begin()) schema += ".";
+ schema += *it;
+ }
+ schema += ";\n\n";
+}
+
+// Generate a flatbuffer schema from the Parser's internal representation.
+std::string GenerateFBS(const Parser &parser, const std::string &file_name) {
+ // Proto namespaces may clash with table names, escape the ones that were
+ // generated from a table:
+ for (auto it = parser.namespaces_.begin(); it != parser.namespaces_.end();
+ ++it) {
+ auto &ns = **it;
+ for (size_t i = 0; i < ns.from_table; i++) {
+ ns.components[ns.components.size() - 1 - i] += "_";
+ }
+ }
+
+ std::string schema;
+ schema += "// Generated from " + file_name + ".proto\n\n";
+ if (parser.opts.include_dependence_headers) {
+ // clang-format off
+ #ifdef FBS_GEN_INCLUDES // TODO: currently all in one file.
+ int num_includes = 0;
+ for (auto it = parser.included_files_.begin();
+ it != parser.included_files_.end(); ++it) {
+ if (it->second.empty())
+ continue;
+ auto basename = flatbuffers::StripPath(
+ flatbuffers::StripExtension(it->second));
+ schema += "include \"" + basename + ".fbs\";\n";
+ num_includes++;
+ }
+ if (num_includes) schema += "\n";
+ #endif
+ // clang-format on
+ }
+ // Generate code for all the enum declarations.
+ const Namespace *last_namespace = nullptr;
+ for (auto enum_def_it = parser.enums_.vec.begin();
+ enum_def_it != parser.enums_.vec.end(); ++enum_def_it) {
+ EnumDef &enum_def = **enum_def_it;
+ GenNameSpace(*enum_def.defined_namespace, &schema, &last_namespace);
+ GenComment(enum_def.doc_comment, &schema, nullptr);
+ if (enum_def.is_union)
+ schema += "union " + enum_def.name;
+ else
+ schema += "enum " + enum_def.name + " : ";
+ schema += GenType(enum_def.underlying_type, true) + " {\n";
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ auto &ev = **it;
+ GenComment(ev.doc_comment, &schema, nullptr, " ");
+ if (enum_def.is_union)
+ schema += " " + GenType(ev.union_type) + ",\n";
+ else
+ schema += " " + ev.name + " = " + enum_def.ToString(ev) + ",\n";
+ }
+ schema += "}\n\n";
+ }
+ // Generate code for all structs/tables.
+ for (auto it = parser.structs_.vec.begin(); it != parser.structs_.vec.end();
+ ++it) {
+ StructDef &struct_def = **it;
+ GenNameSpace(*struct_def.defined_namespace, &schema, &last_namespace);
+ GenComment(struct_def.doc_comment, &schema, nullptr);
+ schema += "table " + struct_def.name + " {\n";
+ for (auto field_it = struct_def.fields.vec.begin();
+ field_it != struct_def.fields.vec.end(); ++field_it) {
+ auto &field = **field_it;
+ if (field.value.type.base_type != BASE_TYPE_UTYPE) {
+ GenComment(field.doc_comment, &schema, nullptr, " ");
+ schema += " " + field.name + ":" + GenType(field.value.type);
+ if (field.value.constant != "0") schema += " = " + field.value.constant;
+ if (field.required) schema += " (required)";
+ schema += ";\n";
+ }
+ }
+ schema += "}\n\n";
+ }
+ return schema;
+}
+
+bool GenerateFBS(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ return SaveFile((path + file_name + ".fbs").c_str(),
+ GenerateFBS(parser, file_name), false);
+}
+
+} // namespace flatbuffers
diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp
new file mode 100644
index 0000000..8dca792
--- /dev/null
+++ b/src/idl_gen_general.cpp
@@ -0,0 +1,1667 @@
+/*
+ * 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.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+#if defined(FLATBUFFERS_CPP98_STL)
+# include <cctype>
+#endif // defined(FLATBUFFERS_CPP98_STL)
+
+namespace flatbuffers {
+
+// These arrays need to correspond to the IDLOptions::k enum.
+
+struct LanguageParameters {
+ IDLOptions::Language language;
+ // Whether function names in the language typically start with uppercase.
+ bool first_camel_upper;
+ std::string file_extension;
+ std::string string_type;
+ std::string bool_type;
+ std::string open_curly;
+ std::string accessor_type;
+ std::string const_decl;
+ std::string unsubclassable_decl;
+ std::string enum_decl;
+ std::string enum_separator;
+ std::string getter_prefix;
+ std::string getter_suffix;
+ std::string inheritance_marker;
+ std::string namespace_ident;
+ std::string namespace_begin;
+ std::string namespace_end;
+ std::string set_bb_byteorder;
+ std::string get_bb_position;
+ std::string get_fbb_offset;
+ std::string accessor_prefix;
+ std::string accessor_prefix_static;
+ std::string optional_suffix;
+ std::string includes;
+ std::string class_annotation;
+ std::string generated_type_annotation;
+ CommentConfig comment_config;
+ const FloatConstantGenerator *float_gen;
+};
+
+const LanguageParameters &GetLangParams(IDLOptions::Language lang) {
+ static TypedFloatConstantGenerator CSharpFloatGen(
+ "Double.", "Single.", "NaN", "PositiveInfinity", "NegativeInfinity");
+
+ static TypedFloatConstantGenerator JavaFloatGen(
+ "Double.", "Float.", "NaN", "POSITIVE_INFINITY", "NEGATIVE_INFINITY");
+
+ static const LanguageParameters language_parameters[] = {
+ {
+ IDLOptions::kJava,
+ false,
+ ".java",
+ "String",
+ "boolean ",
+ " {\n",
+ "class ",
+ " final ",
+ "final ",
+ "final class ",
+ ";\n",
+ "()",
+ "",
+ " extends ",
+ "package ",
+ ";",
+ "",
+ "_bb.order(ByteOrder.LITTLE_ENDIAN); ",
+ "position()",
+ "offset()",
+ "",
+ "",
+ "",
+ "import java.nio.*;\nimport java.lang.*;\nimport "
+ "java.util.*;\nimport com.google.flatbuffers.*;\n",
+ "\n@SuppressWarnings(\"unused\")\n",
+ "\n@javax.annotation.Generated(value=\"flatc\")\n",
+ {
+ "/**",
+ " *",
+ " */",
+ },
+ &JavaFloatGen
+ },
+ {
+ IDLOptions::kCSharp,
+ true,
+ ".cs",
+ "string",
+ "bool ",
+ "\n{\n",
+ "struct ",
+ " readonly ",
+ "",
+ "enum ",
+ ",\n",
+ " { get",
+ "} ",
+ " : ",
+ "namespace ",
+ "\n{",
+ "\n}\n",
+ "",
+ "Position",
+ "Offset",
+ "__p.",
+ "Table.",
+ "?",
+ "using global::System;\nusing global::FlatBuffers;\n\n",
+ "",
+ "",
+ {
+ nullptr,
+ "///",
+ nullptr,
+ },
+ &CSharpFloatGen
+ },
+ };
+
+ if (lang == IDLOptions::kJava) {
+ return language_parameters[0];
+ } else {
+ FLATBUFFERS_ASSERT(lang == IDLOptions::kCSharp);
+ return language_parameters[1];
+ }
+}
+
+namespace general {
+class GeneralGenerator : public BaseGenerator {
+ public:
+ GeneralGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "", "."),
+ lang_(GetLangParams(parser_.opts.lang)),
+ cur_name_space_(nullptr) {}
+
+ GeneralGenerator &operator=(const GeneralGenerator &);
+ bool generate() {
+ std::string one_file_code;
+ cur_name_space_ = parser_.current_namespace_;
+
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ std::string enumcode;
+ auto &enum_def = **it;
+ if (!parser_.opts.one_file) cur_name_space_ = enum_def.defined_namespace;
+ GenEnum(enum_def, &enumcode);
+ if (parser_.opts.one_file) {
+ one_file_code += enumcode;
+ } else {
+ if (!SaveType(enum_def.name, *enum_def.defined_namespace, enumcode,
+ false))
+ return false;
+ }
+ }
+
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ std::string declcode;
+ auto &struct_def = **it;
+ if (!parser_.opts.one_file)
+ cur_name_space_ = struct_def.defined_namespace;
+ GenStruct(struct_def, &declcode);
+ if (parser_.opts.one_file) {
+ one_file_code += declcode;
+ } else {
+ if (!SaveType(struct_def.name, *struct_def.defined_namespace, declcode,
+ true))
+ return false;
+ }
+ }
+
+ if (parser_.opts.one_file) {
+ return SaveType(file_name_, *parser_.current_namespace_, one_file_code,
+ true);
+ }
+ return true;
+ }
+
+ // Save out the generated code for a single class while adding
+ // declaration boilerplate.
+ bool SaveType(const std::string &defname, const Namespace &ns,
+ const std::string &classcode, bool needs_includes) const {
+ if (!classcode.length()) return true;
+
+ std::string code;
+ if (lang_.language == IDLOptions::kCSharp) {
+ code =
+ "// <auto-generated>\n"
+ "// " +
+ std::string(FlatBuffersGeneratedWarning()) +
+ "\n"
+ "// </auto-generated>\n\n";
+ } else {
+ code = "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
+ }
+
+ std::string namespace_name = FullNamespace(".", ns);
+ if (!namespace_name.empty()) {
+ code += lang_.namespace_ident + namespace_name + lang_.namespace_begin;
+ code += "\n\n";
+ }
+ if (needs_includes) {
+ code += lang_.includes;
+ if (parser_.opts.gen_nullable) {
+ code += "\nimport javax.annotation.Nullable;\n";
+ }
+ code += lang_.class_annotation;
+ }
+ if (parser_.opts.gen_generated) {
+ code += lang_.generated_type_annotation;
+ }
+ code += classcode;
+ if (!namespace_name.empty()) code += lang_.namespace_end;
+ auto filename = NamespaceDir(ns) + defname + lang_.file_extension;
+ return SaveFile(filename.c_str(), code, false);
+ }
+
+ const Namespace *CurrentNameSpace() const { return cur_name_space_; }
+
+ std::string FunctionStart(char upper) const {
+ return std::string() + (lang_.language == IDLOptions::kJava
+ ? static_cast<char>(tolower(upper))
+ : upper);
+ }
+
+ std::string GenNullableAnnotation(const Type &t) const {
+ return lang_.language == IDLOptions::kJava && parser_.opts.gen_nullable &&
+ !IsScalar(DestinationType(t, true).base_type)
+ ? " @Nullable "
+ : "";
+ }
+
+ std::string GenTypeBasic(const Type &type, bool enableLangOverrides) const {
+ // clang-format off
+ static const char * const java_typename[] = {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ #JTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ };
+
+ static const char * const csharp_typename[] = {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ #NTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ };
+ // clang-format on
+
+ if (enableLangOverrides) {
+ if (lang_.language == IDLOptions::kCSharp) {
+ if (IsEnum(type)) return WrapInNameSpace(*type.enum_def);
+ if (type.base_type == BASE_TYPE_STRUCT) {
+ return "Offset<" + WrapInNameSpace(*type.struct_def) + ">";
+ }
+ }
+ }
+
+ if (lang_.language == IDLOptions::kJava) {
+ return java_typename[type.base_type];
+ } else {
+ FLATBUFFERS_ASSERT(lang_.language == IDLOptions::kCSharp);
+ return csharp_typename[type.base_type];
+ }
+ }
+
+ std::string GenTypeBasic(const Type &type) const {
+ return GenTypeBasic(type, true);
+ }
+
+ std::string GenTypePointer(const Type &type) const {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return lang_.string_type;
+ case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
+ case BASE_TYPE_STRUCT: return WrapInNameSpace(*type.struct_def);
+ case BASE_TYPE_UNION:
+ // Unions in C# use a generic Table-derived type for better type safety
+ if (lang_.language == IDLOptions::kCSharp) return "TTable";
+ FLATBUFFERS_FALLTHROUGH(); // else fall thru
+ default: return "Table";
+ }
+ }
+
+ std::string GenTypeGet(const Type &type) const {
+ return IsScalar(type.base_type)
+ ? GenTypeBasic(type)
+ : (IsArray(type) ? GenTypeGet(type.VectorType())
+ : GenTypePointer(type));
+ }
+
+ // Find the destination type the user wants to receive the value in (e.g.
+ // one size higher signed types for unsigned serialized values in Java).
+ Type DestinationType(const Type &type, bool vectorelem) const {
+ if (lang_.language != IDLOptions::kJava) return type;
+ switch (type.base_type) {
+ // We use int for both uchar/ushort, since that generally means less
+ // casting than using short for uchar.
+ case BASE_TYPE_UCHAR: return Type(BASE_TYPE_INT);
+ case BASE_TYPE_USHORT: return Type(BASE_TYPE_INT);
+ case BASE_TYPE_UINT: return Type(BASE_TYPE_LONG);
+ case BASE_TYPE_ARRAY:
+ case BASE_TYPE_VECTOR:
+ if (vectorelem) return DestinationType(type.VectorType(), vectorelem);
+ FLATBUFFERS_FALLTHROUGH(); // else fall thru
+ default: return type;
+ }
+ }
+
+ std::string GenOffsetType(const StructDef &struct_def) const {
+ if (lang_.language == IDLOptions::kCSharp) {
+ return "Offset<" + WrapInNameSpace(struct_def) + ">";
+ } else {
+ return "int";
+ }
+ }
+
+ std::string GenOffsetConstruct(const StructDef &struct_def,
+ const std::string &variable_name) const {
+ if (lang_.language == IDLOptions::kCSharp) {
+ return "new Offset<" + WrapInNameSpace(struct_def) + ">(" +
+ variable_name + ")";
+ }
+ return variable_name;
+ }
+
+ std::string GenVectorOffsetType() const {
+ if (lang_.language == IDLOptions::kCSharp) {
+ return "VectorOffset";
+ } else {
+ return "int";
+ }
+ }
+
+ // Generate destination type name
+ std::string GenTypeNameDest(const Type &type) const {
+ return GenTypeGet(DestinationType(type, true));
+ }
+
+ // Mask to turn serialized value into destination type value.
+ std::string DestinationMask(const Type &type, bool vectorelem) const {
+ if (lang_.language != IDLOptions::kJava) return "";
+ switch (type.base_type) {
+ case BASE_TYPE_UCHAR: return " & 0xFF";
+ case BASE_TYPE_USHORT: return " & 0xFFFF";
+ case BASE_TYPE_UINT: return " & 0xFFFFFFFFL";
+ case BASE_TYPE_VECTOR:
+ if (vectorelem) return DestinationMask(type.VectorType(), vectorelem);
+ FLATBUFFERS_FALLTHROUGH(); // else fall thru
+ default: return "";
+ }
+ }
+
+ // Casts necessary to correctly read serialized data
+ std::string DestinationCast(const Type &type) const {
+ if (IsSeries(type)) {
+ return DestinationCast(type.VectorType());
+ } else {
+ switch (lang_.language) {
+ case IDLOptions::kJava:
+ // Cast necessary to correctly read serialized unsigned values.
+ if (type.base_type == BASE_TYPE_UINT) return "(long)";
+ break;
+
+ case IDLOptions::kCSharp:
+ // Cast from raw integral types to enum.
+ if (IsEnum(type)) return "(" + WrapInNameSpace(*type.enum_def) + ")";
+ break;
+
+ default: break;
+ }
+ }
+ return "";
+ }
+
+ // Cast statements for mutator method parameters.
+ // In Java, parameters representing unsigned numbers need to be cast down to
+ // their respective type. For example, a long holding an unsigned int value
+ // would be cast down to int before being put onto the buffer. In C#, one cast
+ // directly cast an Enum to its underlying type, which is essential before
+ // putting it onto the buffer.
+ std::string SourceCast(const Type &type, bool castFromDest) const {
+ if (IsSeries(type)) {
+ return SourceCast(type.VectorType(), castFromDest);
+ } else {
+ switch (lang_.language) {
+ case IDLOptions::kJava:
+ if (castFromDest) {
+ if (type.base_type == BASE_TYPE_UINT)
+ return "(int)";
+ else if (type.base_type == BASE_TYPE_USHORT)
+ return "(short)";
+ else if (type.base_type == BASE_TYPE_UCHAR)
+ return "(byte)";
+ }
+ break;
+ case IDLOptions::kCSharp:
+ if (IsEnum(type)) return "(" + GenTypeBasic(type, false) + ")";
+ break;
+ default: break;
+ }
+ }
+ return "";
+ }
+
+ std::string SourceCast(const Type &type) const { return SourceCast(type, true); }
+
+ std::string SourceCastBasic(const Type &type, bool castFromDest) const {
+ return IsScalar(type.base_type) ? SourceCast(type, castFromDest) : "";
+ }
+
+ std::string SourceCastBasic(const Type &type) const {
+ return SourceCastBasic(type, true);
+ }
+
+ std::string GenEnumDefaultValue(const FieldDef &field) const {
+ auto &value = field.value;
+ FLATBUFFERS_ASSERT(value.type.enum_def);
+ auto &enum_def = *value.type.enum_def;
+ auto enum_val = enum_def.FindByValue(value.constant);
+ return enum_val ? (WrapInNameSpace(enum_def) + "." + enum_val->name)
+ : value.constant;
+ }
+
+ std::string GenDefaultValue(const FieldDef &field, bool enableLangOverrides) const {
+ auto& value = field.value;
+ if (enableLangOverrides) {
+ // handles both enum case and vector of enum case
+ if (lang_.language == IDLOptions::kCSharp &&
+ value.type.enum_def != nullptr &&
+ value.type.base_type != BASE_TYPE_UNION) {
+ return GenEnumDefaultValue(field);
+ }
+ }
+
+ auto longSuffix = lang_.language == IDLOptions::kJava ? "L" : "";
+ switch (value.type.base_type) {
+ case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
+ case BASE_TYPE_ULONG: {
+ if (lang_.language != IDLOptions::kJava) return value.constant;
+ // Converts the ulong into its bits signed equivalent
+ uint64_t defaultValue = StringToUInt(value.constant.c_str());
+ return NumToString(static_cast<int64_t>(defaultValue)) + longSuffix;
+ }
+ case BASE_TYPE_UINT:
+ case BASE_TYPE_LONG: return value.constant + longSuffix;
+ default:
+ if(IsFloat(value.type.base_type))
+ return lang_.float_gen->GenFloatConstant(field);
+ else
+ return value.constant;
+ }
+ }
+
+ std::string GenDefaultValue(const FieldDef &field) const {
+ return GenDefaultValue(field, true);
+ }
+
+ std::string GenDefaultValueBasic(const FieldDef &field,
+ bool enableLangOverrides) const {
+ auto& value = field.value;
+ if (!IsScalar(value.type.base_type)) {
+ if (enableLangOverrides) {
+ if (lang_.language == IDLOptions::kCSharp) {
+ switch (value.type.base_type) {
+ case BASE_TYPE_STRING: return "default(StringOffset)";
+ case BASE_TYPE_STRUCT:
+ return "default(Offset<" +
+ WrapInNameSpace(*value.type.struct_def) + ">)";
+ case BASE_TYPE_VECTOR: return "default(VectorOffset)";
+ default: break;
+ }
+ }
+ }
+ return "0";
+ }
+ return GenDefaultValue(field, enableLangOverrides);
+ }
+
+ std::string GenDefaultValueBasic(const FieldDef &field) const {
+ return GenDefaultValueBasic(field, true);
+ }
+
+ void GenEnum(EnumDef &enum_def, std::string *code_ptr) const {
+ std::string &code = *code_ptr;
+ if (enum_def.generated) return;
+
+ // Generate enum definitions of the form:
+ // public static (final) int name = value;
+ // In Java, we use ints rather than the Enum feature, because we want them
+ // to map directly to how they're used in C/C++ and file formats.
+ // That, and Java Enums are expensive, and not universally liked.
+ GenComment(enum_def.doc_comment, code_ptr, &lang_.comment_config);
+
+ // In C# this indicates enumeration values can be treated as bit flags.
+ if (lang_.language == IDLOptions::kCSharp && enum_def.attributes.Lookup("bit_flags")) {
+ code += "[System.FlagsAttribute]\n";
+ }
+ if (enum_def.attributes.Lookup("private")) {
+ // For Java, we leave the enum unmarked to indicate package-private
+ // For C# we mark the enum as internal
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += "internal ";
+ }
+ } else {
+ code += "public ";
+ }
+ code += lang_.enum_decl + enum_def.name;
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += lang_.inheritance_marker +
+ GenTypeBasic(enum_def.underlying_type, false);
+ }
+ code += lang_.open_curly;
+ if (lang_.language == IDLOptions::kJava) {
+ code += " private " + enum_def.name + "() { }\n";
+ }
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ auto &ev = **it;
+ GenComment(ev.doc_comment, code_ptr, &lang_.comment_config, " ");
+ if (lang_.language != IDLOptions::kCSharp) {
+ code += " public static";
+ code += lang_.const_decl;
+ code += GenTypeBasic(enum_def.underlying_type, false);
+ }
+ code += (lang_.language == IDLOptions::kJava) ? " " : " ";
+ code += ev.name + " = ";
+ code += enum_def.ToString(ev);
+ code += lang_.enum_separator;
+ }
+
+ // Generate a generate string table for enum values.
+ // We do not do that for C# where this functionality is native.
+ if (lang_.language != IDLOptions::kCSharp) {
+ // 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
+ // the moment we simply don't output a table at all.
+ auto range = enum_def.Distance();
+ // Average distance between values above which we consider a table
+ // "too sparse". Change at will.
+ static const uint64_t kMaxSparseness = 5;
+ if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
+ code += "\n public static";
+ code += lang_.const_decl;
+ code += lang_.string_type;
+ code += "[] names = { ";
+ auto val = enum_def.Vals().front();
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ auto ev = *it;
+ for (auto k = enum_def.Distance(val, ev); k > 1; --k)
+ code += "\"\", ";
+ val = ev;
+ code += "\"" + (*it)->name + "\", ";
+ }
+ code += "};\n\n";
+ code += " public static ";
+ code += lang_.string_type;
+ code += " " + MakeCamel("name", lang_.first_camel_upper);
+ code += "(int e) { return names[e";
+ if (enum_def.MinValue()->IsNonZero())
+ code += " - " + enum_def.MinValue()->name;
+ code += "]; }\n";
+ }
+ }
+
+ // Close the class
+ 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.
+ std::string GenGetter(const Type &type) const {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return lang_.accessor_prefix + "__string";
+ case BASE_TYPE_STRUCT: return lang_.accessor_prefix + "__struct";
+ case BASE_TYPE_UNION: return lang_.accessor_prefix + "__union";
+ case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
+ case BASE_TYPE_ARRAY: return GenGetter(type.VectorType());
+ default: {
+ std::string getter =
+ lang_.accessor_prefix + "bb." + FunctionStart('G') + "et";
+ if (type.base_type == BASE_TYPE_BOOL) {
+ getter = "0!=" + getter;
+ } else if (GenTypeBasic(type, false) != "byte") {
+ getter += MakeCamel(GenTypeBasic(type, false));
+ }
+ return getter;
+ }
+ }
+ }
+
+ // Returns the function name that is able to read a value of the given type.
+ std::string GenGetterForLookupByKey(flatbuffers::FieldDef *key_field,
+ const std::string &data_buffer,
+ const char *num = nullptr) const {
+ auto type = key_field->value.type;
+ auto dest_mask = DestinationMask(type, true);
+ auto dest_cast = DestinationCast(type);
+ auto getter = data_buffer + "." + FunctionStart('G') + "et";
+ if (GenTypeBasic(type, false) != "byte") {
+ getter += MakeCamel(GenTypeBasic(type, false));
+ }
+ getter = dest_cast + getter + "(" + GenOffsetGetter(key_field, num) + ")" +
+ dest_mask;
+ return getter;
+ }
+
+ // Direct mutation is only allowed for scalar fields.
+ // Hence a setter method will only be generated for such fields.
+ std::string GenSetter(const Type &type) const {
+ if (IsScalar(type.base_type)) {
+ std::string setter =
+ lang_.accessor_prefix + "bb." + FunctionStart('P') + "ut";
+ if (GenTypeBasic(type, false) != "byte" &&
+ type.base_type != BASE_TYPE_BOOL) {
+ setter += MakeCamel(GenTypeBasic(type, false));
+ }
+ return setter;
+ } else {
+ return "";
+ }
+ }
+
+ // Returns the method name for use with add/put calls.
+ std::string GenMethod(const Type &type) const {
+ return IsScalar(type.base_type) ? MakeCamel(GenTypeBasic(type, false))
+ : (IsStruct(type) ? "Struct" : "Offset");
+ }
+
+ // Recursively generate arguments for a constructor, to deal with nested
+ // structs.
+ void GenStructArgs(const StructDef &struct_def, std::string *code_ptr,
+ const char *nameprefix, size_t array_count = 0) const {
+ std::string &code = *code_ptr;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ const auto &field_type = field.value.type;
+ const auto array_field = IsArray(field_type);
+ const auto &type = array_field ? field_type.VectorType()
+ : DestinationType(field_type, false);
+ const auto array_cnt = array_field ? (array_count + 1) : array_count;
+ if (IsStruct(type)) {
+ // Generate arguments for a struct inside a struct. To ensure names
+ // don't clash, and to make it obvious these arguments are constructing
+ // a nested struct, prefix the name with the field name.
+ GenStructArgs(*field_type.struct_def, code_ptr,
+ (nameprefix + (field.name + "_")).c_str(), array_cnt);
+ } else {
+ code += ", ";
+ code += GenTypeBasic(type);
+ if (lang_.language == IDLOptions::kJava) {
+ for (size_t i = 0; i < array_cnt; i++) code += "[]";
+ } else if (lang_.language == IDLOptions::kCSharp) {
+ if (array_cnt > 0) {
+ code += "[";
+ for (size_t i = 1; i < array_cnt; i++) code += ",";
+ code += "]";
+ }
+ } else {
+ FLATBUFFERS_ASSERT(0);
+ }
+ code += " ";
+ code += nameprefix;
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ }
+ }
+ }
+
+ // Recusively generate struct construction statements of the form:
+ // builder.putType(name);
+ // and insert manual padding.
+ void GenStructBody(const StructDef &struct_def, std::string *code_ptr,
+ const char *nameprefix, size_t index = 0,
+ bool in_array = false) const {
+ std::string &code = *code_ptr;
+ std::string indent((index + 1) * 2, ' ');
+ code += indent + " builder." + FunctionStart('P') + "rep(";
+ code += NumToString(struct_def.minalign) + ", ";
+ code += NumToString(struct_def.bytesize) + ");\n";
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ auto &field = **it;
+ const auto &field_type = field.value.type;
+ if (field.padding) {
+ code += indent + " builder." + FunctionStart('P') + "ad(";
+ code += NumToString(field.padding) + ");\n";
+ }
+ if (IsStruct(field_type)) {
+ GenStructBody(*field_type.struct_def, code_ptr,
+ (nameprefix + (field.name + "_")).c_str(), index,
+ in_array);
+ } else {
+ const auto &type =
+ IsArray(field_type) ? field_type.VectorType() : field_type;
+ const auto index_var = "_idx" + NumToString(index);
+ if (IsArray(field_type)) {
+ code += indent + " for (int " + index_var + " = ";
+ code += NumToString(field_type.fixed_length);
+ code += "; " + index_var + " > 0; " + index_var + "--) {\n";
+ in_array = true;
+ }
+ if (IsStruct(type)) {
+ GenStructBody(*field_type.struct_def, code_ptr,
+ (nameprefix + (field.name + "_")).c_str(), index + 1,
+ in_array);
+ } else {
+ code += IsArray(field_type) ? " " : "";
+ code += indent + " builder." + FunctionStart('P') + "ut";
+ code += GenMethod(type) + "(";
+ code += SourceCast(type);
+ auto argname =
+ nameprefix + MakeCamel(field.name, lang_.first_camel_upper);
+ code += argname;
+ size_t array_cnt = index + (IsArray(field_type) ? 1 : 0);
+ if (lang_.language == IDLOptions::kJava) {
+ for (size_t i = 0; in_array && i < array_cnt; i++) {
+ code += "[_idx" + NumToString(i) + "-1]";
+ }
+ } else if (lang_.language == IDLOptions::kCSharp) {
+ if (array_cnt > 0) {
+ code += "[";
+ for (size_t i = 0; in_array && i < array_cnt; i++) {
+ code += "_idx" + NumToString(i) + "-1";
+ if (i != (array_cnt - 1)) code += ",";
+ }
+ code += "]";
+ }
+ } else {
+ FLATBUFFERS_ASSERT(0);
+ }
+ code += ");\n";
+ }
+ if (IsArray(field_type)) { code += indent + " }\n"; }
+ }
+ }
+ }
+
+ std::string GenByteBufferLength(const char *bb_name) const {
+ std::string bb_len = bb_name;
+ if (lang_.language == IDLOptions::kCSharp)
+ bb_len += ".Length";
+ else
+ bb_len += ".capacity()";
+ return bb_len;
+ }
+
+ std::string GenOffsetGetter(flatbuffers::FieldDef *key_field,
+ const char *num = nullptr) const {
+ std::string key_offset = "";
+ key_offset += lang_.accessor_prefix_static + "__offset(" +
+ NumToString(key_field->value.offset) + ", ";
+ if (num) {
+ key_offset += num;
+ key_offset +=
+ (lang_.language == IDLOptions::kCSharp ? ".Value, builder.DataBuffer)"
+ : ", _bb)");
+ } else {
+ key_offset += GenByteBufferLength("bb");
+ key_offset += " - tableOffset, bb)";
+ }
+ return key_offset;
+ }
+
+ std::string GenLookupKeyGetter(flatbuffers::FieldDef *key_field) const {
+ std::string key_getter = " ";
+ key_getter += "int tableOffset = " + lang_.accessor_prefix_static;
+ key_getter += "__indirect(vectorLocation + 4 * (start + middle)";
+ key_getter += ", bb);\n ";
+ if (key_field->value.type.base_type == BASE_TYPE_STRING) {
+ key_getter += "int comp = " + lang_.accessor_prefix_static;
+ key_getter += FunctionStart('C') + "ompareStrings(";
+ key_getter += GenOffsetGetter(key_field);
+ key_getter += ", byteKey, bb);\n";
+ } else {
+ auto get_val = GenGetterForLookupByKey(key_field, "bb");
+ if (lang_.language == IDLOptions::kCSharp) {
+ key_getter += "int comp = " + get_val + ".CompareTo(key);\n";
+ } else {
+ key_getter += GenTypeNameDest(key_field->value.type) + " val = ";
+ key_getter += get_val + ";\n";
+ key_getter += " int comp = val > key ? 1 : val < key ? -1 : 0;\n";
+ }
+ }
+ return key_getter;
+ }
+
+ std::string GenKeyGetter(flatbuffers::FieldDef *key_field) const {
+ std::string key_getter = "";
+ auto data_buffer =
+ (lang_.language == IDLOptions::kCSharp) ? "builder.DataBuffer" : "_bb";
+ if (key_field->value.type.base_type == BASE_TYPE_STRING) {
+ if (lang_.language == IDLOptions::kJava) key_getter += " return ";
+ key_getter += lang_.accessor_prefix_static;
+ key_getter += FunctionStart('C') + "ompareStrings(";
+ key_getter += GenOffsetGetter(key_field, "o1") + ", ";
+ key_getter += GenOffsetGetter(key_field, "o2") + ", " + data_buffer + ")";
+ if (lang_.language == IDLOptions::kJava) key_getter += ";";
+ } else {
+ auto field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o1");
+ if (lang_.language == IDLOptions::kCSharp) {
+ key_getter += field_getter;
+ field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2");
+ key_getter += ".CompareTo(" + field_getter + ")";
+ } else {
+ key_getter +=
+ "\n " + GenTypeNameDest(key_field->value.type) + " val_1 = ";
+ key_getter +=
+ field_getter + ";\n " + GenTypeNameDest(key_field->value.type);
+ key_getter += " val_2 = ";
+ field_getter = GenGetterForLookupByKey(key_field, data_buffer, "o2");
+ key_getter += field_getter + ";\n";
+ key_getter +=
+ " return val_1 > val_2 ? 1 : val_1 < val_2 ? -1 : 0;\n ";
+ }
+ }
+ return key_getter;
+ }
+
+ void GenStruct(StructDef &struct_def, std::string *code_ptr) const {
+ if (struct_def.generated) return;
+ std::string &code = *code_ptr;
+
+ // Generate a struct accessor class, with methods of the form:
+ // public type name() { return bb.getType(i + offset); }
+ // or for tables of the form:
+ // public type name() {
+ // int o = __offset(offset); return o != 0 ? bb.getType(o + i) : default;
+ // }
+ GenComment(struct_def.doc_comment, code_ptr, &lang_.comment_config);
+ if (struct_def.attributes.Lookup("private")) {
+ // For Java, we leave the struct unmarked to indicate package-private
+ // For C# we mark the struct as internal
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += "internal ";
+ }
+ } else {
+ code += "public ";
+ }
+ if (lang_.language == IDLOptions::kCSharp &&
+ struct_def.attributes.Lookup("csharp_partial")) {
+ // generate a partial class for this C# struct/table
+ code += "partial ";
+ } else {
+ code += lang_.unsubclassable_decl;
+ }
+ code += lang_.accessor_type + struct_def.name;
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += " : IFlatbufferObject";
+ code += lang_.open_curly;
+ code += " private ";
+ code += struct_def.fixed ? "Struct" : "Table";
+ code += " __p;\n";
+
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += " public ByteBuffer ByteBuffer { get { return __p.bb; } }\n";
+ }
+
+ } else {
+ code += lang_.inheritance_marker;
+ code += struct_def.fixed ? "Struct" : "Table";
+ code += lang_.open_curly;
+ }
+
+ if (!struct_def.fixed) {
+ // Generate verson check method.
+ // Force compile time error if not using the same version runtime.
+ code += " public static void ValidateVersion() {";
+ if (lang_.language == IDLOptions::kCSharp)
+ code += " FlatBufferConstants.";
+ else
+ code += " Constants.";
+ code += "FLATBUFFERS_1_11_1(); ";
+ code += "}\n";
+
+ // Generate a special accessor for the table that when used as the root
+ // of a FlatBuffer
+ std::string method_name =
+ FunctionStart('G') + "etRootAs" + struct_def.name;
+ std::string method_signature =
+ " public static " + struct_def.name + " " + method_name;
+
+ // create convenience method that doesn't require an existing object
+ code += method_signature + "(ByteBuffer _bb) ";
+ code += "{ return " + method_name + "(_bb, new " + struct_def.name +
+ "()); }\n";
+
+ // create method that allows object reuse
+ code +=
+ method_signature + "(ByteBuffer _bb, " + struct_def.name + " obj) { ";
+ code += lang_.set_bb_byteorder;
+ code += "return (obj.__assign(_bb." + FunctionStart('G') + "etInt(_bb.";
+ code += lang_.get_bb_position;
+ code += ") + _bb.";
+ code += lang_.get_bb_position;
+ code += ", _bb)); }\n";
+ if (parser_.root_struct_def_ == &struct_def) {
+ if (parser_.file_identifier_.length()) {
+ // Check if a buffer has the identifier.
+ code += " public static ";
+ code += lang_.bool_type + struct_def.name;
+ code += "BufferHasIdentifier(ByteBuffer _bb) { return ";
+ code += lang_.accessor_prefix_static + "__has_identifier(_bb, \"";
+ code += parser_.file_identifier_;
+ code += "\"); }\n";
+ }
+ }
+ }
+ // Generate the __init method that sets the field in a pre-existing
+ // accessor object. This is to allow object reuse.
+ code += " public void __init(int _i, ByteBuffer _bb) ";
+ code += "{ ";
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += "__p = new ";
+ code += struct_def.fixed ? "Struct" : "Table";
+ code += "(_i, _bb); ";
+ } else {
+ code += "__reset(_i, _bb); ";
+ }
+ code += "}\n";
+ code +=
+ " public " + struct_def.name + " __assign(int _i, ByteBuffer _bb) ";
+ code += "{ __init(_i, _bb); return this; }\n\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ GenComment(field.doc_comment, code_ptr, &lang_.comment_config, " ");
+ std::string type_name = GenTypeGet(field.value.type);
+ std::string type_name_dest = GenTypeNameDest(field.value.type);
+ std::string conditional_cast = "";
+ std::string optional = "";
+ if (lang_.language == IDLOptions::kCSharp && !struct_def.fixed &&
+ (field.value.type.base_type == BASE_TYPE_STRUCT ||
+ field.value.type.base_type == BASE_TYPE_UNION ||
+ (field.value.type.base_type == BASE_TYPE_VECTOR &&
+ (field.value.type.element == BASE_TYPE_STRUCT ||
+ field.value.type.element == BASE_TYPE_UNION)))) {
+ optional = lang_.optional_suffix;
+ conditional_cast = "(" + type_name_dest + optional + ")";
+ }
+ std::string dest_mask = DestinationMask(field.value.type, true);
+ std::string dest_cast = DestinationCast(field.value.type);
+ std::string src_cast = SourceCast(field.value.type);
+ std::string method_start = " public " +
+ (field.required ? "" : GenNullableAnnotation(field.value.type)) +
+ type_name_dest + optional + " " +
+ MakeCamel(field.name, lang_.first_camel_upper);
+ std::string obj = lang_.language == IDLOptions::kCSharp
+ ? "(new " + type_name + "())"
+ : "obj";
+
+ // Most field accessors need to retrieve and test the field offset first,
+ // this is the prefix code for that:
+ auto offset_prefix =
+ IsArray(field.value.type)
+ ? " { return "
+ : (" { int o = " + lang_.accessor_prefix + "__offset(" +
+ NumToString(field.value.offset) + "); return o != 0 ? ");
+ // Generate the accessors that don't do object reuse.
+ if (field.value.type.base_type == BASE_TYPE_STRUCT) {
+ // Calls the accessor that takes an accessor object with a new object.
+ if (lang_.language != IDLOptions::kCSharp) {
+ code += method_start + "() { return ";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "(new ";
+ code += type_name + "()); }\n";
+ }
+ } else if (field.value.type.base_type == BASE_TYPE_VECTOR &&
+ field.value.type.element == BASE_TYPE_STRUCT) {
+ // Accessors for vectors of structs also take accessor objects, this
+ // generates a variant without that argument.
+ if (lang_.language != IDLOptions::kCSharp) {
+ code += method_start + "(int j) { return ";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "(new " + type_name + "(), j); }\n";
+ }
+ } else if (field.value.type.base_type == BASE_TYPE_UNION ||
+ (field.value.type.base_type == BASE_TYPE_VECTOR &&
+ field.value.type.VectorType().base_type == BASE_TYPE_UNION)) {
+ if (lang_.language == IDLOptions::kCSharp) {
+ // Union types in C# use generic Table-derived type for better type
+ // safety.
+ method_start += "<TTable>";
+ type_name = type_name_dest;
+ }
+ }
+ std::string getter = dest_cast + GenGetter(field.value.type);
+ code += method_start;
+ std::string default_cast = "";
+ // only create default casts for c# scalars or vectors of scalars
+ if (lang_.language == IDLOptions::kCSharp &&
+ (IsScalar(field.value.type.base_type) ||
+ (field.value.type.base_type == BASE_TYPE_VECTOR &&
+ IsScalar(field.value.type.element)))) {
+ // For scalars, default value will be returned by GetDefaultValue().
+ // If the scalar is an enum, GetDefaultValue() returns an actual c# enum
+ // that doesn't need to be casted. However, default values for enum
+ // elements of vectors are integer literals ("0") and are still casted
+ // for clarity.
+ if (field.value.type.enum_def == nullptr ||
+ field.value.type.base_type == BASE_TYPE_VECTOR) {
+ default_cast = "(" + type_name_dest + ")";
+ }
+ }
+ std::string member_suffix = "; ";
+ if (IsScalar(field.value.type.base_type)) {
+ code += lang_.getter_prefix;
+ member_suffix += lang_.getter_suffix;
+ if (struct_def.fixed) {
+ code += " { return " + getter;
+ code += "(" + lang_.accessor_prefix + "bb_pos + ";
+ code += NumToString(field.value.offset) + ")";
+ code += dest_mask;
+ } else {
+ code += offset_prefix + getter;
+ code += "(o + " + lang_.accessor_prefix + "bb_pos)" + dest_mask;
+ code += " : " + default_cast;
+ code += GenDefaultValue(field);
+ }
+ } else {
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_STRUCT:
+ if (lang_.language != IDLOptions::kCSharp) {
+ code += "(" + type_name + " obj" + ")";
+ } else {
+ code += lang_.getter_prefix;
+ member_suffix += lang_.getter_suffix;
+ }
+ if (struct_def.fixed) {
+ code += " { return " + obj + ".__assign(" + lang_.accessor_prefix;
+ code += "bb_pos + " + NumToString(field.value.offset) + ", ";
+ code += lang_.accessor_prefix + "bb)";
+ } else {
+ code += offset_prefix + conditional_cast;
+ code += obj + ".__assign(";
+ code += field.value.type.struct_def->fixed
+ ? "o + " + lang_.accessor_prefix + "bb_pos"
+ : lang_.accessor_prefix + "__indirect(o + " +
+ lang_.accessor_prefix + "bb_pos)";
+ code += ", " + lang_.accessor_prefix + "bb) : null";
+ }
+ break;
+ case BASE_TYPE_STRING:
+ code += lang_.getter_prefix;
+ member_suffix += lang_.getter_suffix;
+ code += offset_prefix + getter + "(o + " + lang_.accessor_prefix;
+ code += "bb_pos) : null";
+ break;
+ case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru
+ case BASE_TYPE_VECTOR: {
+ auto vectortype = field.value.type.VectorType();
+ if (vectortype.base_type == BASE_TYPE_UNION &&
+ lang_.language == IDLOptions::kCSharp) {
+ conditional_cast = "(TTable?)";
+ getter += "<TTable>";
+ }
+ code += "(";
+ if (vectortype.base_type == BASE_TYPE_STRUCT) {
+ if (lang_.language != IDLOptions::kCSharp)
+ code += type_name + " obj, ";
+ getter = obj + ".__assign";
+ } else if (vectortype.base_type == BASE_TYPE_UNION) {
+ if (lang_.language != IDLOptions::kCSharp)
+ code += type_name + " obj, ";
+ }
+ code += "int j)";
+ const auto body = offset_prefix + conditional_cast + getter + "(";
+ if (vectortype.base_type == BASE_TYPE_UNION) {
+ if (lang_.language != IDLOptions::kCSharp)
+ code += body + "obj, ";
+ else
+ code += " where TTable : struct, IFlatbufferObject" + body;
+ } else {
+ code += body;
+ }
+ auto index = lang_.accessor_prefix;
+ if (IsArray(field.value.type)) {
+ index += "bb_pos + " + NumToString(field.value.offset) + " + ";
+ } else {
+ index += "__vector(o) + ";
+ }
+ index += "j * " + NumToString(InlineSize(vectortype));
+ if (vectortype.base_type == BASE_TYPE_STRUCT) {
+ code += vectortype.struct_def->fixed
+ ? index
+ : lang_.accessor_prefix + "__indirect(" + index + ")";
+ code += ", " + lang_.accessor_prefix + "bb";
+ } else if (vectortype.base_type == BASE_TYPE_UNION) {
+ code += index + " - " + lang_.accessor_prefix + "bb_pos";
+ } else {
+ code += index;
+ }
+ code += ")" + dest_mask;
+ if (!IsArray(field.value.type)) {
+ code += " : ";
+ code +=
+ field.value.type.element == BASE_TYPE_BOOL
+ ? "false"
+ : (IsScalar(field.value.type.element) ? default_cast + "0"
+ : "null");
+ }
+
+ break;
+ }
+ case BASE_TYPE_UNION:
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += "() where TTable : struct, IFlatbufferObject";
+ code += offset_prefix + "(TTable?)" + getter;
+ code += "<TTable>(o) : null";
+ } else {
+ code += "(" + type_name + " obj)" + offset_prefix + getter;
+ code += "(obj, o) : null";
+ }
+ break;
+ default: FLATBUFFERS_ASSERT(0);
+ }
+ }
+ code += member_suffix;
+ code += "}\n";
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ code +=
+ " public int " + MakeCamel(field.name, lang_.first_camel_upper);
+ code += "Length";
+ code += lang_.getter_prefix;
+ code += offset_prefix;
+ code += lang_.accessor_prefix + "__vector_len(o) : 0; ";
+ code += lang_.getter_suffix;
+ code += "}\n";
+ // See if we should generate a by-key accessor.
+ if (field.value.type.element == BASE_TYPE_STRUCT &&
+ !field.value.type.struct_def->fixed) {
+ auto &sd = *field.value.type.struct_def;
+ auto &fields = sd.fields.vec;
+ for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
+ auto &key_field = **kit;
+ if (key_field.key) {
+ auto qualified_name = WrapInNameSpace(sd);
+ code += " public " + qualified_name + lang_.optional_suffix + " ";
+ code += MakeCamel(field.name, lang_.first_camel_upper) + "ByKey(";
+ code += GenTypeNameDest(key_field.value.type) + " key)";
+ code += offset_prefix;
+ code += qualified_name + ".__lookup_by_key(";
+ if (lang_.language == IDLOptions::kJava)
+ code += "null, ";
+ code += lang_.accessor_prefix + "__vector(o), key, ";
+ code += lang_.accessor_prefix + "bb) : null; ";
+ code += "}\n";
+ if (lang_.language == IDLOptions::kJava) {
+ code += " public " + qualified_name + lang_.optional_suffix + " ";
+ code += MakeCamel(field.name, lang_.first_camel_upper) + "ByKey(";
+ code += qualified_name + lang_.optional_suffix + " obj, ";
+ code += GenTypeNameDest(key_field.value.type) + " key)";
+ code += offset_prefix;
+ code += qualified_name + ".__lookup_by_key(obj, ";
+ code += lang_.accessor_prefix + "__vector(o), key, ";
+ code += lang_.accessor_prefix + "bb) : null; ";
+ code += "}\n";
+ }
+ break;
+ }
+ }
+ }
+ }
+ // Generate a ByteBuffer accessor for strings & vectors of scalars.
+ if ((field.value.type.base_type == BASE_TYPE_VECTOR &&
+ IsScalar(field.value.type.VectorType().base_type)) ||
+ field.value.type.base_type == BASE_TYPE_STRING) {
+ switch (lang_.language) {
+ case IDLOptions::kJava:
+ code += " public ByteBuffer ";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "AsByteBuffer() { return ";
+ code += lang_.accessor_prefix + "__vector_as_bytebuffer(";
+ code += NumToString(field.value.offset) + ", ";
+ code +=
+ NumToString(field.value.type.base_type == BASE_TYPE_STRING
+ ? 1
+ : InlineSize(field.value.type.VectorType()));
+ code += "); }\n";
+ code += " public ByteBuffer ";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "InByteBuffer(ByteBuffer _bb) { return ";
+ code += lang_.accessor_prefix + "__vector_in_bytebuffer(_bb, ";
+ code += NumToString(field.value.offset) + ", ";
+ code +=
+ NumToString(field.value.type.base_type == BASE_TYPE_STRING
+ ? 1
+ : InlineSize(field.value.type.VectorType()));
+ code += "); }\n";
+ break;
+ case IDLOptions::kCSharp:
+ code += "#if ENABLE_SPAN_T\n";
+ code += " public Span<byte> Get";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "Bytes() { return ";
+ code += lang_.accessor_prefix + "__vector_as_span(";
+ code += NumToString(field.value.offset);
+ code += "); }\n";
+ code += "#else\n";
+ code += " public ArraySegment<byte>? Get";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "Bytes() { return ";
+ code += lang_.accessor_prefix + "__vector_as_arraysegment(";
+ code += NumToString(field.value.offset);
+ code += "); }\n";
+ code += "#endif\n";
+
+ // For direct blockcopying the data into a typed array
+ code += " public ";
+ code += GenTypeBasic(field.value.type.VectorType());
+ code += "[] Get";
+ code += MakeCamel(field.name, lang_.first_camel_upper);
+ code += "Array() { return ";
+ code += lang_.accessor_prefix + "__vector_as_array<";
+ code += GenTypeBasic(field.value.type.VectorType());
+ code += ">(";
+ code += NumToString(field.value.offset);
+ code += "); }\n";
+ break;
+ default: break;
+ }
+ }
+ // generate object accessors if is nested_flatbuffer
+ if (field.nested_flatbuffer) {
+ auto nested_type_name = WrapInNameSpace(*field.nested_flatbuffer);
+ auto nested_method_name =
+ MakeCamel(field.name, lang_.first_camel_upper) + "As" +
+ field.nested_flatbuffer->name;
+ auto get_nested_method_name = nested_method_name;
+ if (lang_.language == IDLOptions::kCSharp) {
+ get_nested_method_name = "Get" + nested_method_name;
+ conditional_cast =
+ "(" + nested_type_name + lang_.optional_suffix + ")";
+ }
+ if (lang_.language != IDLOptions::kCSharp) {
+ code += " public " + nested_type_name + lang_.optional_suffix + " ";
+ code += nested_method_name + "() { return ";
+ code +=
+ get_nested_method_name + "(new " + nested_type_name + "()); }\n";
+ } else {
+ obj = "(new " + nested_type_name + "())";
+ }
+ code += " public " + nested_type_name + lang_.optional_suffix + " ";
+ code += get_nested_method_name + "(";
+ if (lang_.language != IDLOptions::kCSharp)
+ code += nested_type_name + " obj";
+ code += ") { int o = " + lang_.accessor_prefix + "__offset(";
+ code += NumToString(field.value.offset) + "); ";
+ code += "return o != 0 ? " + conditional_cast + obj + ".__assign(";
+ code += lang_.accessor_prefix;
+ code += "__indirect(" + lang_.accessor_prefix + "__vector(o)), ";
+ code += lang_.accessor_prefix + "bb) : null; }\n";
+ }
+ // Generate mutators for scalar fields or vectors of scalars.
+ if (parser_.opts.mutable_buffer) {
+ auto is_series = (IsSeries(field.value.type));
+ const auto &underlying_type =
+ is_series ? field.value.type.VectorType() : field.value.type;
+ // Boolean parameters have to be explicitly converted to byte
+ // representation.
+ auto setter_parameter = underlying_type.base_type == BASE_TYPE_BOOL
+ ? "(byte)(" + field.name + " ? 1 : 0)"
+ : field.name;
+ auto mutator_prefix = MakeCamel("mutate", lang_.first_camel_upper);
+ // A vector mutator also needs the index of the vector element it should
+ // mutate.
+ auto mutator_params = (is_series ? "(int j, " : "(") +
+ GenTypeNameDest(underlying_type) + " " +
+ field.name + ") { ";
+ auto setter_index =
+ is_series
+ ? lang_.accessor_prefix +
+ (IsArray(field.value.type)
+ ? "bb_pos + " + NumToString(field.value.offset)
+ : "__vector(o)") +
+ +" + j * " + NumToString(InlineSize(underlying_type))
+ : (struct_def.fixed
+ ? lang_.accessor_prefix + "bb_pos + " +
+ NumToString(field.value.offset)
+ : "o + " + lang_.accessor_prefix + "bb_pos");
+ if (IsScalar(underlying_type.base_type)) {
+ code += " public ";
+ code += struct_def.fixed ? "void " : lang_.bool_type;
+ code += mutator_prefix + MakeCamel(field.name, true);
+ code += mutator_params;
+ if (struct_def.fixed) {
+ code += GenSetter(underlying_type) + "(" + setter_index + ", ";
+ code += src_cast + setter_parameter + "); }\n";
+ } else {
+ code += "int o = " + lang_.accessor_prefix + "__offset(";
+ code += NumToString(field.value.offset) + ");";
+ code += " if (o != 0) { " + GenSetter(underlying_type);
+ code += "(" + setter_index + ", " + src_cast + setter_parameter +
+ "); return true; } else { return false; } }\n";
+ }
+ }
+ }
+ }
+ code += "\n";
+ flatbuffers::FieldDef *key_field = nullptr;
+ if (struct_def.fixed) {
+ // create a struct constructor function
+ code += " public static " + GenOffsetType(struct_def) + " ";
+ code += FunctionStart('C') + "reate";
+ code += struct_def.name + "(FlatBufferBuilder builder";
+ GenStructArgs(struct_def, code_ptr, "");
+ code += ") {\n";
+ GenStructBody(struct_def, code_ptr, "");
+ code += " return ";
+ code += GenOffsetConstruct(
+ struct_def, "builder." + std::string(lang_.get_fbb_offset));
+ code += ";\n }\n";
+ } else {
+ // Generate a method that creates a table in one go. This is only possible
+ // when the table has no struct fields, since those have to be created
+ // inline, and there's no way to do so in Java.
+ bool has_no_struct_fields = true;
+ int num_fields = 0;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ if (IsStruct(field.value.type)) {
+ has_no_struct_fields = false;
+ } else {
+ num_fields++;
+ }
+ }
+ // JVM specifications restrict default constructor params to be < 255.
+ // Longs and doubles take up 2 units, so we set the limit to be < 127.
+ if (has_no_struct_fields && num_fields && num_fields < 127) {
+ // Generate a table constructor of the form:
+ // public static int createName(FlatBufferBuilder builder, args...)
+ code += " public static " + GenOffsetType(struct_def) + " ";
+ code += FunctionStart('C') + "reate" + struct_def.name;
+ code += "(FlatBufferBuilder builder";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ code += ",\n ";
+ code += GenTypeBasic(DestinationType(field.value.type, false));
+ code += " ";
+ code += field.name;
+ if (!IsScalar(field.value.type.base_type)) code += "Offset";
+
+ // Java doesn't have defaults, which means this method must always
+ // supply all arguments, and thus won't compile when fields are added.
+ if (lang_.language != IDLOptions::kJava) {
+ code += " = ";
+ code += GenDefaultValueBasic(field);
+ }
+ }
+ code += ") {\n builder.";
+ code += FunctionStart('S') + "tartTable(";
+ code += NumToString(struct_def.fields.vec.size()) + ");\n";
+ for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
+ size; size /= 2) {
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ auto &field = **it;
+ if (!field.deprecated &&
+ (!struct_def.sortbysize ||
+ size == SizeOf(field.value.type.base_type))) {
+ code += " " + struct_def.name + ".";
+ code += FunctionStart('A') + "dd";
+ code += MakeCamel(field.name) + "(builder, " + field.name;
+ if (!IsScalar(field.value.type.base_type)) code += "Offset";
+ code += ");\n";
+ }
+ }
+ }
+ code += " return " + struct_def.name + ".";
+ code += FunctionStart('E') + "nd" + struct_def.name;
+ code += "(builder);\n }\n\n";
+ }
+ // Generate a set of static methods that allow table construction,
+ // of the form:
+ // public static void addName(FlatBufferBuilder builder, short name)
+ // { builder.addShort(id, name, default); }
+ // Unlike the Create function, these always work.
+ code += " public static void " + FunctionStart('S') + "tart";
+ code += struct_def.name;
+ code += "(FlatBufferBuilder builder) { builder.";
+ code += FunctionStart('S') + "tartTable(";
+ code += NumToString(struct_def.fields.vec.size()) + "); }\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ if (field.key) key_field = &field;
+ code += " public static void " + FunctionStart('A') + "dd";
+ code += MakeCamel(field.name);
+ code += "(FlatBufferBuilder builder, ";
+ code += GenTypeBasic(DestinationType(field.value.type, false));
+ auto argname = MakeCamel(field.name, false);
+ if (!IsScalar(field.value.type.base_type)) argname += "Offset";
+ code += " " + argname + ") { builder." + FunctionStart('A') + "dd";
+ code += GenMethod(field.value.type) + "(";
+ code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
+ code += SourceCastBasic(field.value.type);
+ code += argname;
+ if (!IsScalar(field.value.type.base_type) &&
+ field.value.type.base_type != BASE_TYPE_UNION &&
+ lang_.language == IDLOptions::kCSharp) {
+ code += ".Value";
+ }
+ code += ", ";
+ if (lang_.language == IDLOptions::kJava)
+ code += SourceCastBasic(field.value.type);
+ code += GenDefaultValue(field, false);
+ code += "); }\n";
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ auto vector_type = field.value.type.VectorType();
+ auto alignment = InlineAlignment(vector_type);
+ auto elem_size = InlineSize(vector_type);
+ if (!IsStruct(vector_type)) {
+ // Generate a method to create a vector from a Java array.
+ code += " public static " + GenVectorOffsetType() + " ";
+ code += FunctionStart('C') + "reate";
+ code += MakeCamel(field.name);
+ code += "Vector(FlatBufferBuilder builder, ";
+ code += GenTypeBasic(vector_type) + "[] data) ";
+ code += "{ builder." + FunctionStart('S') + "tartVector(";
+ code += NumToString(elem_size);
+ code += ", data." + FunctionStart('L') + "ength, ";
+ code += NumToString(alignment);
+ code += "); for (int i = data.";
+ code += FunctionStart('L') + "ength - 1; i >= 0; i--) builder.";
+ code += FunctionStart('A') + "dd";
+ code += GenMethod(vector_type);
+ code += "(";
+ code += SourceCastBasic(vector_type, false);
+ code += "data[i]";
+ if (lang_.language == IDLOptions::kCSharp &&
+ (vector_type.base_type == BASE_TYPE_STRUCT ||
+ vector_type.base_type == BASE_TYPE_STRING))
+ code += ".Value";
+ code += "); return ";
+ code += "builder." + FunctionStart('E') + "ndVector(); }\n";
+ // For C#, include a block copy method signature.
+ if (lang_.language == IDLOptions::kCSharp) {
+ code += " public static " + GenVectorOffsetType() + " ";
+ code += FunctionStart('C') + "reate";
+ code += MakeCamel(field.name);
+ code += "VectorBlock(FlatBufferBuilder builder, ";
+ code += GenTypeBasic(vector_type) + "[] data) ";
+ code += "{ builder." + FunctionStart('S') + "tartVector(";
+ code += NumToString(elem_size);
+ code += ", data." + FunctionStart('L') + "ength, ";
+ code += NumToString(alignment);
+ code += "); builder.Add(data); return builder.EndVector(); }\n";
+ }
+ }
+ // Generate a method to start a vector, data to be added manually
+ // after.
+ code += " public static void " + FunctionStart('S') + "tart";
+ code += MakeCamel(field.name);
+ code += "Vector(FlatBufferBuilder builder, int numElems) ";
+ code += "{ builder." + FunctionStart('S') + "tartVector(";
+ code += NumToString(elem_size);
+ code += ", numElems, " + NumToString(alignment);
+ code += "); }\n";
+ }
+ }
+ code += " public static " + GenOffsetType(struct_def) + " ";
+ code += FunctionStart('E') + "nd" + struct_def.name;
+ code += "(FlatBufferBuilder builder) {\n int o = builder.";
+ code += FunctionStart('E') + "ndTable();\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (!field.deprecated && field.required) {
+ code += " builder." + FunctionStart('R') + "equired(o, ";
+ code += NumToString(field.value.offset);
+ code += "); // " + field.name + "\n";
+ }
+ }
+ code += " return " + GenOffsetConstruct(struct_def, "o") + ";\n }\n";
+ if (parser_.root_struct_def_ == &struct_def) {
+ std::string size_prefix[] = { "", "SizePrefixed" };
+ for (int i = 0; i < 2; ++i) {
+ code += " public static void ";
+ code += FunctionStart('F') + "inish" + size_prefix[i] +
+ struct_def.name;
+ code += "Buffer(FlatBufferBuilder builder, " +
+ GenOffsetType(struct_def);
+ code += " offset) {";
+ code += " builder." + FunctionStart('F') + "inish" + size_prefix[i] +
+ "(offset";
+ if (lang_.language == IDLOptions::kCSharp) { code += ".Value"; }
+
+ if (parser_.file_identifier_.length())
+ code += ", \"" + parser_.file_identifier_ + "\"";
+ code += "); }\n";
+ }
+ }
+ }
+ // Only generate key compare function for table,
+ // because `key_field` is not set for struct
+ if (struct_def.has_key && !struct_def.fixed) {
+ FLATBUFFERS_ASSERT(key_field);
+ if (lang_.language == IDLOptions::kJava) {
+ code += "\n @Override\n protected int keysCompare(";
+ code += "Integer o1, Integer o2, ByteBuffer _bb) {";
+ code += GenKeyGetter(key_field);
+ code += " }\n";
+ } else {
+ code += "\n public static VectorOffset ";
+ code += "CreateSortedVectorOf" + struct_def.name;
+ code += "(FlatBufferBuilder builder, ";
+ code += "Offset<" + struct_def.name + ">";
+ code += "[] offsets) {\n";
+ code += " Array.Sort(offsets, (Offset<" + struct_def.name +
+ "> o1, Offset<" + struct_def.name + "> o2) => " +
+ GenKeyGetter(key_field);
+ code += ");\n";
+ code += " return builder.CreateVectorOfTables(offsets);\n }\n";
+ }
+
+ code += "\n public static " + struct_def.name + lang_.optional_suffix;
+ code += " __lookup_by_key(";
+ if (lang_.language == IDLOptions::kJava)
+ code += struct_def.name + " obj, ";
+ code += "int vectorLocation, ";
+ code += GenTypeNameDest(key_field->value.type);
+ code += " key, ByteBuffer bb) {\n";
+ if (key_field->value.type.base_type == BASE_TYPE_STRING) {
+ code += " byte[] byteKey = ";
+ if (lang_.language == IDLOptions::kJava)
+ code += "key.getBytes(Table.UTF8_CHARSET.get());\n";
+ else
+ code += "System.Text.Encoding.UTF8.GetBytes(key);\n";
+ }
+ code += " int span = ";
+ code += "bb." + FunctionStart('G') + "etInt(vectorLocation - 4);\n";
+ code += " int start = 0;\n";
+ code += " while (span != 0) {\n";
+ code += " int middle = span / 2;\n";
+ code += GenLookupKeyGetter(key_field);
+ code += " if (comp > 0) {\n";
+ code += " span = middle;\n";
+ code += " } else if (comp < 0) {\n";
+ code += " middle++;\n";
+ code += " start += middle;\n";
+ code += " span -= middle;\n";
+ code += " } else {\n";
+ code += " return ";
+ if (lang_.language == IDLOptions::kJava)
+ code += "(obj == null ? new " + struct_def.name + "() : obj)";
+ else
+ code += "new " + struct_def.name + "()";
+ code += ".__assign(tableOffset, bb);\n";
+ code += " }\n }\n";
+ code += " return null;\n";
+ code += " }\n";
+ }
+ code += "}";
+ // Java does not need the closing semi-colon on class definitions.
+ code += (lang_.language != IDLOptions::kJava) ? ";" : "";
+ code += "\n\n";
+ }
+ const LanguageParameters &lang_;
+ // This tracks the current namespace used to determine if a type need to be
+ // prefixed by its namespace
+ const Namespace *cur_name_space_;
+};
+} // namespace general
+
+bool GenerateGeneral(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ general::GeneralGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+
+std::string GeneralMakeRule(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ FLATBUFFERS_ASSERT(parser.opts.lang <= IDLOptions::kMAX);
+ const auto &lang = GetLangParams(parser.opts.lang);
+
+ std::string make_rule;
+
+ for (auto it = parser.enums_.vec.begin(); it != parser.enums_.vec.end();
+ ++it) {
+ auto &enum_def = **it;
+ if (!make_rule.empty()) make_rule += " ";
+ std::string directory =
+ BaseGenerator::NamespaceDir(parser, path, *enum_def.defined_namespace);
+ make_rule += directory + enum_def.name + lang.file_extension;
+ }
+
+ for (auto it = parser.structs_.vec.begin(); it != parser.structs_.vec.end();
+ ++it) {
+ auto &struct_def = **it;
+ if (!make_rule.empty()) make_rule += " ";
+ std::string directory = BaseGenerator::NamespaceDir(
+ parser, path, *struct_def.defined_namespace);
+ make_rule += directory + struct_def.name + lang.file_extension;
+ }
+
+ make_rule += ": ";
+ auto included_files = parser.GetIncludedFilesRecursive(file_name);
+ for (auto it = included_files.begin(); it != included_files.end(); ++it) {
+ make_rule += " " + *it;
+ }
+ return make_rule;
+}
+
+std::string BinaryFileName(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ auto ext = parser.file_extension_.length() ? parser.file_extension_ : "bin";
+ return path + file_name + "." + ext;
+}
+
+bool GenerateBinary(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ return !parser.builder_.GetSize() ||
+ flatbuffers::SaveFile(
+ BinaryFileName(parser, path, file_name).c_str(),
+ reinterpret_cast<char *>(parser.builder_.GetBufferPointer()),
+ parser.builder_.GetSize(), true);
+}
+
+std::string BinaryMakeRule(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ if (!parser.builder_.GetSize()) return "";
+ std::string filebase =
+ flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
+ std::string make_rule =
+ BinaryFileName(parser, path, filebase) + ": " + file_name;
+ auto included_files =
+ parser.GetIncludedFilesRecursive(parser.root_struct_def_->file);
+ 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_go.cpp b/src/idl_gen_go.cpp
new file mode 100644
index 0000000..5e62b61
--- /dev/null
+++ b/src/idl_gen_go.cpp
@@ -0,0 +1,1009 @@
+/*
+ * 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.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+
+#include <sstream>
+#include <string>
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+#ifdef _WIN32
+# include <direct.h>
+# define PATH_SEPARATOR "\\"
+# define mkdir(n, m) _mkdir(n)
+#else
+# include <sys/stat.h>
+# define PATH_SEPARATOR "/"
+#endif
+
+namespace flatbuffers {
+
+static std::string GeneratedFileName(const std::string &path,
+ const std::string &file_name) {
+ return path + file_name + "_generated.go";
+}
+
+namespace go {
+
+// see https://golang.org/ref/spec#Keywords
+static const char * const g_golang_keywords[] = {
+ "break", "default", "func", "interface", "select", "case", "defer",
+ "go", "map", "struct", "chan", "else", "goto", "package",
+ "switch", "const", "fallthrough", "if", "range", "type", "continue",
+ "for", "import", "return", "var",
+};
+
+static std::string GoIdentity(const std::string &name) {
+ for (size_t i = 0;
+ i < sizeof(g_golang_keywords) / sizeof(g_golang_keywords[0]); i++) {
+ if (name == g_golang_keywords[i]) { return MakeCamel(name + "_", false); }
+ }
+
+ return MakeCamel(name, false);
+}
+
+class GoGenerator : public BaseGenerator {
+ public:
+ GoGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name, const std::string &go_namespace)
+ : BaseGenerator(parser, path, file_name, "" /* not used*/,
+ "" /* not used */),
+ cur_name_space_(nullptr) {
+ std::istringstream iss(go_namespace);
+ std::string component;
+ while (std::getline(iss, component, '.')) {
+ go_namespace_.components.push_back(component);
+ }
+ }
+
+ bool generate() {
+ std::string one_file_code;
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ tracked_imported_namespaces_.clear();
+ std::string enumcode;
+ GenEnum(**it, &enumcode);
+ if (parser_.opts.one_file) {
+ one_file_code += enumcode;
+ } else {
+ if (!SaveType(**it, enumcode, false, true)) return false;
+ }
+ }
+
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ tracked_imported_namespaces_.clear();
+ std::string declcode;
+ GenStruct(**it, &declcode);
+ if (parser_.opts.one_file) {
+ one_file_code += declcode;
+ } else {
+ if (!SaveType(**it, declcode, true, false)) return false;
+ }
+ }
+
+ if (parser_.opts.one_file) {
+ std::string code = "";
+ const bool is_enum = !parser_.enums_.vec.empty();
+ BeginFile(LastNamespacePart(go_namespace_), true, is_enum, &code);
+ code += one_file_code;
+ const std::string filename = GeneratedFileName(path_, file_name_);
+ return SaveFile(filename.c_str(), code, false);
+ }
+
+ return true;
+ }
+
+ private:
+ Namespace go_namespace_;
+ Namespace *cur_name_space_;
+
+ struct NamespacePtrLess {
+ bool operator()(const Namespace *a, const Namespace *b) const {
+ return *a < *b;
+ }
+ };
+ std::set<const Namespace *, NamespacePtrLess> tracked_imported_namespaces_;
+
+ // Most field accessors need to retrieve and test the field offset first,
+ // this is the prefix code for that.
+ std::string OffsetPrefix(const FieldDef &field) {
+ return "{\n\to := flatbuffers.UOffsetT(rcv._tab.Offset(" +
+ NumToString(field.value.offset) + "))\n\tif o != 0 {\n";
+ }
+
+ // Begin a class declaration.
+ void BeginClass(const StructDef &struct_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += "type " + struct_def.name + " struct {\n\t";
+
+ // _ is reserved in flatbuffers field names, so no chance of name conflict:
+ code += "_tab ";
+ code += struct_def.fixed ? "flatbuffers.Struct" : "flatbuffers.Table";
+ code += "\n}\n\n";
+ }
+
+ // Construct the name of the type for this enum.
+ std::string GetEnumTypeName(const EnumDef &enum_def) {
+ return WrapInNameSpaceAndTrack(enum_def.defined_namespace, GoIdentity(enum_def.name));
+ }
+
+ // Create a type for the enum values.
+ void GenEnumType(const EnumDef &enum_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "type " + GetEnumTypeName(enum_def) + " ";
+ code += GenTypeBasic(enum_def.underlying_type) + "\n\n";
+ }
+
+ // Begin enum code with a class declaration.
+ void BeginEnum(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "const (\n";
+ }
+
+ // A single enum member.
+ void EnumMember(const EnumDef &enum_def, const EnumVal &ev,
+ size_t max_name_length, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "\t";
+ code += enum_def.name;
+ code += ev.name;
+ code += " ";
+ code += std::string(max_name_length - ev.name.length(), ' ');
+ code += GetEnumTypeName(enum_def);
+ code += " = ";
+ code += enum_def.ToString(ev) + "\n";
+ }
+
+ // End enum code.
+ void EndEnum(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += ")\n\n";
+ }
+
+ // Begin enum name map.
+ void BeginEnumNames(const EnumDef &enum_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "var EnumNames";
+ code += enum_def.name;
+ code += " = map[" + GetEnumTypeName(enum_def) + "]string{\n";
+ }
+
+ // A single enum name member.
+ void EnumNameMember(const EnumDef &enum_def, const EnumVal &ev,
+ size_t max_name_length, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "\t";
+ code += enum_def.name;
+ code += ev.name;
+ code += ": ";
+ code += std::string(max_name_length - ev.name.length(), ' ');
+ code += "\"";
+ code += ev.name;
+ code += "\",\n";
+ }
+
+ // End enum name map.
+ void EndEnumNames(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "}\n\n";
+ }
+
+ // Generate String() method on enum type.
+ void EnumStringer(const EnumDef &enum_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "func (v " + enum_def.name + ") String() string {\n";
+ code += "\tif s, ok := EnumNames" + enum_def.name + "[v]; ok {\n";
+ code += "\t\treturn s\n";
+ code += "\t}\n";
+ code += "\treturn \""+ enum_def.name;
+ code += "(\" + strconv.FormatInt(int64(v), 10) + \")\"\n";
+ code += "}\n\n";
+ }
+
+ // Begin enum value map.
+ void BeginEnumValues(const EnumDef &enum_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "var EnumValues";
+ code += enum_def.name;
+ code += " = map[string]" + GetEnumTypeName(enum_def) + "{\n";
+ }
+
+ // A single enum value member.
+ void EnumValueMember(const EnumDef &enum_def, const EnumVal &ev,
+ size_t max_name_length, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "\t\"";
+ code += ev.name;
+ code += "\": ";
+ code += std::string(max_name_length - ev.name.length(), ' ');
+ code += enum_def.name;
+ code += ev.name;
+ code += ",\n";
+ }
+
+ // End enum value map.
+ void EndEnumValues(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "}\n\n";
+ }
+
+ // Initialize a new struct or table from existing data.
+ void NewRootTypeFromBuffer(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += "func GetRootAs";
+ code += struct_def.name;
+ code += "(buf []byte, offset flatbuffers.UOffsetT) ";
+ code += "*" + struct_def.name + "";
+ code += " {\n";
+ code += "\tn := flatbuffers.GetUOffsetT(buf[offset:])\n";
+ code += "\tx := &" + struct_def.name + "{}\n";
+ code += "\tx.Init(buf, n+offset)\n";
+ code += "\treturn x\n";
+ code += "}\n\n";
+ }
+
+ // Initialize an existing object with other data, to avoid an allocation.
+ void InitializeExisting(const StructDef &struct_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ GenReceiver(struct_def, code_ptr);
+ code += " Init(buf []byte, i flatbuffers.UOffsetT) ";
+ code += "{\n";
+ code += "\trcv._tab.Bytes = buf\n";
+ code += "\trcv._tab.Pos = i\n";
+ code += "}\n\n";
+ }
+
+ // Implement the table accessor
+ void GenTableAccessor(const StructDef &struct_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ GenReceiver(struct_def, code_ptr);
+ code += " Table() flatbuffers.Table ";
+ code += "{\n";
+
+ if (struct_def.fixed) {
+ code += "\treturn rcv._tab.Table\n";
+ } else {
+ code += "\treturn rcv._tab\n";
+ }
+ code += "}\n\n";
+ }
+
+ // Get the length of a vector.
+ void GetVectorLen(const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ GenReceiver(struct_def, code_ptr);
+ code += " " + MakeCamel(field.name) + "Length(";
+ code += ") int " + OffsetPrefix(field);
+ code += "\t\treturn rcv._tab.VectorLen(o)\n\t}\n";
+ code += "\treturn 0\n}\n\n";
+ }
+
+ // Get a [ubyte] vector as a byte slice.
+ void GetUByteSlice(const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ GenReceiver(struct_def, code_ptr);
+ code += " " + MakeCamel(field.name) + "Bytes(";
+ code += ") []byte " + OffsetPrefix(field);
+ code += "\t\treturn rcv._tab.ByteVector(o + rcv._tab.Pos)\n\t}\n";
+ code += "\treturn nil\n}\n\n";
+ }
+
+ // Get the value of a struct's scalar.
+ void GetScalarFieldOfStruct(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ std::string getter = GenGetter(field.value.type);
+ GenReceiver(struct_def, code_ptr);
+ code += " " + MakeCamel(field.name);
+ code += "() " + TypeName(field) + " {\n";
+ code += "\treturn " + CastToEnum(
+ field.value.type,
+ getter + "(rcv._tab.Pos + flatbuffers.UOffsetT(" +
+ NumToString(field.value.offset) + "))");
+ code += "\n}\n";
+ }
+
+ // Get the value of a table's scalar.
+ void GetScalarFieldOfTable(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ std::string getter = GenGetter(field.value.type);
+ GenReceiver(struct_def, code_ptr);
+ code += " " + MakeCamel(field.name);
+ code += "() " + TypeName(field) + " ";
+ code += OffsetPrefix(field) + "\t\treturn ";
+ code += CastToEnum(field.value.type, getter + "(o + rcv._tab.Pos)");
+ code += "\n\t}\n";
+ code += "\treturn " + GenConstant(field) + "\n";
+ code += "}\n\n";
+ }
+
+ // Get a struct by initializing an existing struct.
+ // Specific to Struct.
+ void GetStructFieldOfStruct(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ GenReceiver(struct_def, code_ptr);
+ code += " " + MakeCamel(field.name);
+ code += "(obj *" + TypeName(field);
+ code += ") *" + TypeName(field);
+ code += " {\n";
+ code += "\tif obj == nil {\n";
+ code += "\t\tobj = new(" + TypeName(field) + ")\n";
+ code += "\t}\n";
+ code += "\tobj.Init(rcv._tab.Bytes, rcv._tab.Pos+";
+ code += NumToString(field.value.offset) + ")";
+ code += "\n\treturn obj\n";
+ code += "}\n";
+ }
+
+ // Get a struct by initializing an existing struct.
+ // Specific to Table.
+ void GetStructFieldOfTable(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ GenReceiver(struct_def, code_ptr);
+ code += " " + MakeCamel(field.name);
+ code += "(obj *";
+ code += TypeName(field);
+ code += ") *" + TypeName(field) + " " + OffsetPrefix(field);
+ if (field.value.type.struct_def->fixed) {
+ code += "\t\tx := o + rcv._tab.Pos\n";
+ } else {
+ code += "\t\tx := rcv._tab.Indirect(o + rcv._tab.Pos)\n";
+ }
+ code += "\t\tif obj == nil {\n";
+ code += "\t\t\tobj = new(" + TypeName(field) + ")\n";
+ code += "\t\t}\n";
+ code += "\t\tobj.Init(rcv._tab.Bytes, x)\n";
+ code += "\t\treturn obj\n\t}\n\treturn nil\n";
+ code += "}\n\n";
+ }
+
+ // Get the value of a string.
+ void GetStringField(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ GenReceiver(struct_def, code_ptr);
+ code += " " + MakeCamel(field.name);
+ code += "() " + TypeName(field) + " ";
+ code += OffsetPrefix(field) + "\t\treturn " + GenGetter(field.value.type);
+ code += "(o + rcv._tab.Pos)\n\t}\n\treturn nil\n";
+ code += "}\n\n";
+ }
+
+ // Get the value of a union from an object.
+ void GetUnionField(const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ GenReceiver(struct_def, code_ptr);
+ code += " " + MakeCamel(field.name) + "(";
+ code += "obj " + GenTypePointer(field.value.type) + ") bool ";
+ code += OffsetPrefix(field);
+ code += "\t\t" + GenGetter(field.value.type);
+ code += "(obj, o)\n\t\treturn true\n\t}\n";
+ code += "\treturn false\n";
+ code += "}\n\n";
+ }
+
+ // Get the value of a vector's struct member.
+ void GetMemberOfVectorOfStruct(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ auto vectortype = field.value.type.VectorType();
+
+ GenReceiver(struct_def, code_ptr);
+ code += " " + MakeCamel(field.name);
+ code += "(obj *" + TypeName(field);
+ code += ", j int) bool " + OffsetPrefix(field);
+ code += "\t\tx := rcv._tab.Vector(o)\n";
+ code += "\t\tx += flatbuffers.UOffsetT(j) * ";
+ code += NumToString(InlineSize(vectortype)) + "\n";
+ if (!(vectortype.struct_def->fixed)) {
+ code += "\t\tx = rcv._tab.Indirect(x)\n";
+ }
+ code += "\t\tobj.Init(rcv._tab.Bytes, x)\n";
+ code += "\t\treturn true\n\t}\n";
+ code += "\treturn false\n";
+ code += "}\n\n";
+ }
+
+ // Get the value of a vector's non-struct member.
+ void GetMemberOfVectorOfNonStruct(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ auto vectortype = field.value.type.VectorType();
+
+ GenReceiver(struct_def, code_ptr);
+ code += " " + MakeCamel(field.name);
+ code += "(j int) " + TypeName(field) + " ";
+ code += OffsetPrefix(field);
+ code += "\t\ta := rcv._tab.Vector(o)\n";
+ code += "\t\treturn " + CastToEnum(
+ field.value.type,
+ GenGetter(field.value.type) + "(a + flatbuffers.UOffsetT(j*" +
+ NumToString(InlineSize(vectortype)) + "))");
+ code += "\n\t}\n";
+ if (vectortype.base_type == BASE_TYPE_STRING) {
+ code += "\treturn nil\n";
+ } else if (vectortype.base_type == BASE_TYPE_BOOL) {
+ code += "\treturn false\n";
+ } else {
+ code += "\treturn 0\n";
+ }
+ code += "}\n\n";
+ }
+
+ // Begin the creator function signature.
+ void BeginBuilderArgs(const StructDef &struct_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ if (code.substr(code.length() - 2) != "\n\n") {
+ // a previous mutate has not put an extra new line
+ code += "\n";
+ }
+ code += "func Create" + struct_def.name;
+ code += "(builder *flatbuffers.Builder";
+ }
+
+ // Recursively generate arguments for a constructor, to deal with nested
+ // structs.
+ void StructBuilderArgs(const StructDef &struct_def, const char *nameprefix,
+ std::string *code_ptr) {
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (IsStruct(field.value.type)) {
+ // Generate arguments for a struct inside a struct. To ensure names
+ // don't clash, and to make it obvious these arguments are constructing
+ // a nested struct, prefix the name with the field name.
+ StructBuilderArgs(*field.value.type.struct_def,
+ (nameprefix + (field.name + "_")).c_str(), code_ptr);
+ } else {
+ std::string &code = *code_ptr;
+ code += std::string(", ") + nameprefix;
+ code += GoIdentity(field.name);
+ code += " " + TypeName(field);
+ }
+ }
+ }
+
+ // End the creator function signature.
+ void EndBuilderArgs(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += ") flatbuffers.UOffsetT {\n";
+ }
+
+ // Recursively generate struct construction statements and instert manual
+ // padding.
+ void StructBuilderBody(const StructDef &struct_def,
+ const char *nameprefix, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "\tbuilder.Prep(" + NumToString(struct_def.minalign) + ", ";
+ code += NumToString(struct_def.bytesize) + ")\n";
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ auto &field = **it;
+ if (field.padding)
+ code += "\tbuilder.Pad(" + NumToString(field.padding) + ")\n";
+ if (IsStruct(field.value.type)) {
+ StructBuilderBody(*field.value.type.struct_def,
+ (nameprefix + (field.name + "_")).c_str(), code_ptr);
+ } else {
+ code += "\tbuilder.Prepend" + GenMethod(field) + "(";
+ code += CastToBaseType(field.value.type, nameprefix + GoIdentity(field.name)) + ")\n";
+ }
+ }
+ }
+
+ void EndBuilderBody(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "\treturn builder.Offset()\n";
+ code += "}\n";
+ }
+
+ // Get the value of a table's starting offset.
+ void GetStartOfTable(const StructDef &struct_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "func " + struct_def.name + "Start";
+ code += "(builder *flatbuffers.Builder) {\n";
+ code += "\tbuilder.StartObject(";
+ code += NumToString(struct_def.fields.vec.size());
+ code += ")\n}\n";
+ }
+
+ // Set the value of a table's field.
+ void BuildFieldOfTable(const StructDef &struct_def, const FieldDef &field,
+ const size_t offset, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "func " + struct_def.name + "Add" + MakeCamel(field.name);
+ code += "(builder *flatbuffers.Builder, ";
+ code += GoIdentity(field.name) + " ";
+ if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) {
+ code += "flatbuffers.UOffsetT";
+ } else {
+ code += TypeName(field);
+ }
+ code += ") {\n";
+ code += "\tbuilder.Prepend";
+ code += GenMethod(field) + "Slot(";
+ code += NumToString(offset) + ", ";
+ if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) {
+ code += "flatbuffers.UOffsetT";
+ code += "(";
+ code += GoIdentity(field.name) + ")";
+ } else {
+ code += CastToBaseType(field.value.type, GoIdentity(field.name));
+ }
+ code += ", " + GenConstant(field);
+ code += ")\n}\n";
+ }
+
+ // Set the value of one of the members of a table's vector.
+ void BuildVectorOfTable(const StructDef &struct_def,
+ const FieldDef &field, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "func " + struct_def.name + "Start";
+ code += MakeCamel(field.name);
+ code += "Vector(builder *flatbuffers.Builder, numElems int) ";
+ code += "flatbuffers.UOffsetT {\n\treturn builder.StartVector(";
+ auto vector_type = field.value.type.VectorType();
+ auto alignment = InlineAlignment(vector_type);
+ auto elem_size = InlineSize(vector_type);
+ code += NumToString(elem_size);
+ code += ", numElems, " + NumToString(alignment);
+ code += ")\n}\n";
+ }
+
+ // Get the offset of the end of a table.
+ void GetEndOffsetOnTable(const StructDef &struct_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "func " + struct_def.name + "End";
+ code += "(builder *flatbuffers.Builder) flatbuffers.UOffsetT ";
+ code += "{\n\treturn builder.EndObject()\n}\n";
+ }
+
+ // Generate the receiver for function signatures.
+ void GenReceiver(const StructDef &struct_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "func (rcv *" + struct_def.name + ")";
+ }
+
+ // Generate a struct field getter, conditioned on its child type(s).
+ void GenStructAccessor(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) {
+ GetScalarFieldOfStruct(struct_def, field, code_ptr);
+ } else {
+ GetScalarFieldOfTable(struct_def, field, code_ptr);
+ }
+ } else {
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_STRUCT:
+ if (struct_def.fixed) {
+ GetStructFieldOfStruct(struct_def, field, code_ptr);
+ } else {
+ GetStructFieldOfTable(struct_def, field, code_ptr);
+ }
+ break;
+ case BASE_TYPE_STRING: GetStringField(struct_def, field, code_ptr); break;
+ case BASE_TYPE_VECTOR: {
+ auto vectortype = field.value.type.VectorType();
+ if (vectortype.base_type == BASE_TYPE_STRUCT) {
+ GetMemberOfVectorOfStruct(struct_def, field, code_ptr);
+ } else {
+ GetMemberOfVectorOfNonStruct(struct_def, field, code_ptr);
+ }
+ break;
+ }
+ case BASE_TYPE_UNION: GetUnionField(struct_def, field, code_ptr); break;
+ default: FLATBUFFERS_ASSERT(0);
+ }
+ }
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ GetVectorLen(struct_def, field, code_ptr);
+ if (field.value.type.element == BASE_TYPE_UCHAR) {
+ GetUByteSlice(struct_def, field, code_ptr);
+ }
+ }
+ }
+
+ // Mutate the value of a struct's scalar.
+ 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 {\n\treturn " + setter;
+ code += "(rcv._tab.Pos+flatbuffers.UOffsetT(";
+ code += NumToString(field.value.offset) + "), ";
+ code += CastToBaseType(field.value.type, "n") + ")\n}\n\n";
+ }
+
+ // Mutate the value of a table's scalar.
+ 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) + ", ";
+ code += CastToBaseType(field.value.type, "n") + ")\n";
+ code += "}\n\n";
+ }
+
+ // Mutate an element of a vector of scalars.
+ void MutateElementOfVectorOfNonStruct(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ auto vectortype = field.value.type.VectorType();
+ std::string type = MakeCamel(GenTypeBasic(vectortype));
+ std::string setter = "rcv._tab.Mutate" + type;
+ GenReceiver(struct_def, code_ptr);
+ code += " Mutate" + MakeCamel(field.name);
+ code += "(j int, n " + TypeName(field) + ") bool ";
+ code += OffsetPrefix(field);
+ code += "\t\ta := rcv._tab.Vector(o)\n";
+ code += "\t\treturn " + setter + "(";
+ code += "a+flatbuffers.UOffsetT(j*";
+ code += NumToString(InlineSize(vectortype)) + "), ";
+ code += CastToBaseType(vectortype, "n") + ")\n";
+ code += "\t}\n";
+ code += "\treturn false\n";
+ code += "}\n\n";
+ }
+
+ // Generate a struct field setter, conditioned on its child type(s).
+ 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);
+ }
+ } else if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ if (IsScalar(field.value.type.element)) {
+ MutateElementOfVectorOfNonStruct(struct_def, field, code_ptr);
+ }
+ }
+ }
+
+ // Generate table constructors, conditioned on its members' types.
+ void GenTableBuilders(const StructDef &struct_def, std::string *code_ptr) {
+ GetStartOfTable(struct_def, code_ptr);
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+
+ auto offset = it - struct_def.fields.vec.begin();
+ BuildFieldOfTable(struct_def, field, offset, code_ptr);
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ BuildVectorOfTable(struct_def, field, code_ptr);
+ }
+ }
+
+ GetEndOffsetOnTable(struct_def, code_ptr);
+ }
+
+ // Generate struct or table methods.
+ void GenStruct(const StructDef &struct_def, std::string *code_ptr) {
+ if (struct_def.generated) return;
+
+ cur_name_space_ = struct_def.defined_namespace;
+
+ GenComment(struct_def.doc_comment, code_ptr, nullptr);
+ BeginClass(struct_def, code_ptr);
+ if (!struct_def.fixed) {
+ // Generate a special accessor for the table that has been declared as
+ // the root type.
+ NewRootTypeFromBuffer(struct_def, code_ptr);
+ }
+ // Generate the Init method that sets the field in a pre-existing
+ // accessor object. This is to allow object reuse.
+ InitializeExisting(struct_def, code_ptr);
+ // Generate _tab accessor
+ GenTableAccessor(struct_def, code_ptr);
+
+ // Generate struct fields accessors
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+
+ GenStructAccessor(struct_def, field, code_ptr);
+ GenStructMutator(struct_def, field, code_ptr);
+ }
+
+ // Generate builders
+ if (struct_def.fixed) {
+ // create a struct constructor function
+ GenStructBuilder(struct_def, code_ptr);
+ } else {
+ // Create a set of functions that allow table construction.
+ GenTableBuilders(struct_def, code_ptr);
+ }
+ }
+
+ // Generate enum declarations.
+ void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
+ if (enum_def.generated) return;
+
+ auto max_name_length = MaxNameLength(enum_def);
+ cur_name_space_ = enum_def.defined_namespace;
+
+ GenComment(enum_def.doc_comment, code_ptr, nullptr);
+ GenEnumType(enum_def, code_ptr);
+ BeginEnum(code_ptr);
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ auto &ev = **it;
+ GenComment(ev.doc_comment, code_ptr, nullptr, "\t");
+ EnumMember(enum_def, ev, max_name_length, code_ptr);
+ }
+ EndEnum(code_ptr);
+
+ BeginEnumNames(enum_def, code_ptr);
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ auto &ev = **it;
+ EnumNameMember(enum_def, ev, max_name_length, code_ptr);
+ }
+ EndEnumNames(code_ptr);
+
+ BeginEnumValues(enum_def, code_ptr);
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ auto &ev = **it;
+ EnumValueMember(enum_def, ev, max_name_length, code_ptr);
+ }
+ EndEnumValues(code_ptr);
+
+ EnumStringer(enum_def, code_ptr);
+ }
+
+ // Returns the function name that is able to read a value of the given type.
+ std::string GenGetter(const Type &type) {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return "rcv._tab.ByteVector";
+ case BASE_TYPE_UNION: return "rcv._tab.Union";
+ case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
+ default: return "rcv._tab.Get" + MakeCamel(GenTypeBasic(type));
+ }
+ }
+
+ // Returns the method name for use with add/put calls.
+ std::string GenMethod(const FieldDef &field) {
+ return IsScalar(field.value.type.base_type)
+ ? MakeCamel(GenTypeBasic(field.value.type))
+ : (IsStruct(field.value.type) ? "Struct" : "UOffsetT");
+ }
+
+ std::string GenTypeBasic(const Type &type) {
+ static const char *ctypename[] = {
+ // clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ #GTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ };
+ return ctypename[type.base_type];
+ }
+
+ std::string GenTypePointer(const Type &type) {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return "[]byte";
+ case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
+ case BASE_TYPE_STRUCT: return WrapInNameSpaceAndTrack(*type.struct_def);
+ case BASE_TYPE_UNION:
+ // fall through
+ default: return "*flatbuffers.Table";
+ }
+ }
+
+ std::string GenTypeGet(const Type &type) {
+ if (type.enum_def != nullptr) {
+ return GetEnumTypeName(*type.enum_def);
+ }
+ return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type);
+ }
+
+ std::string TypeName(const FieldDef &field) {
+ return GenTypeGet(field.value.type);
+ }
+
+ // If type is an enum, returns value with a cast to the enum type, otherwise
+ // returns value as-is.
+ std::string CastToEnum(const Type &type, std::string value) {
+ if (type.enum_def == nullptr) {
+ return value;
+ } else {
+ return GenTypeGet(type) + "(" + value + ")";
+ }
+ }
+
+ // If type is an enum, returns value with a cast to the enum base type,
+ // otherwise returns value as-is.
+ std::string CastToBaseType(const Type &type, std::string value) {
+ if (type.enum_def == nullptr) {
+ return value;
+ } else {
+ return GenTypeBasic(type) + "(" + value + ")";
+ }
+ }
+
+ std::string GenConstant(const FieldDef &field) {
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_BOOL: return field.value.constant == "0" ? "false" : "true";;
+ default: return field.value.constant;
+ }
+ }
+
+ // Create a struct with a builder and the struct's arguments.
+ void GenStructBuilder(const StructDef &struct_def, std::string *code_ptr) {
+ BeginBuilderArgs(struct_def, code_ptr);
+ StructBuilderArgs(struct_def, "", code_ptr);
+ EndBuilderArgs(code_ptr);
+
+ StructBuilderBody(struct_def, "", code_ptr);
+ EndBuilderBody(code_ptr);
+ }
+ // Begin by declaring namespace and imports.
+ void BeginFile(const std::string &name_space_name, const bool needs_imports,
+ const bool is_enum, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code = code + "// Code generated by the FlatBuffers compiler. DO NOT EDIT.\n\n";
+ code += "package " + name_space_name + "\n\n";
+ if (needs_imports) {
+ code += "import (\n";
+ if (is_enum) {
+ code += "\t\"strconv\"\n\n";
+ }
+ if (!parser_.opts.go_import.empty()) {
+ code += "\tflatbuffers \"" + parser_.opts.go_import + "\"\n";
+ } else {
+ code += "\tflatbuffers \"github.com/google/flatbuffers/go\"\n";
+ }
+ if (tracked_imported_namespaces_.size() > 0) {
+ code += "\n";
+ for (auto it = tracked_imported_namespaces_.begin();
+ it != tracked_imported_namespaces_.end();
+ ++it) {
+ code += "\t" + NamespaceImportName(*it) + " \"" + \
+ NamespaceImportPath(*it) + "\"\n";
+ }
+ }
+ code += ")\n\n";
+ } else {
+ if (is_enum) {
+ code += "import \"strconv\"\n\n";
+ }
+ }
+ }
+
+ // Save out the generated code for a Go Table type.
+ bool SaveType(const Definition &def, const std::string &classcode,
+ const bool needs_imports, const bool is_enum) {
+ if (!classcode.length()) return true;
+
+ Namespace &ns = go_namespace_.components.empty() ? *def.defined_namespace
+ : go_namespace_;
+ std::string code = "";
+ BeginFile(LastNamespacePart(ns), needs_imports, is_enum, &code);
+ code += classcode;
+ // Strip extra newlines at end of file to make it gofmt-clean.
+ while (code.length() > 2 && code.substr(code.length() - 2) == "\n\n") {
+ code.pop_back();
+ }
+ std::string filename = NamespaceDir(ns) + def.name + ".go";
+ return SaveFile(filename.c_str(), code, false);
+ }
+
+ // Create the full name of the imported namespace (format: A__B__C).
+ std::string NamespaceImportName(const Namespace *ns) {
+ std::string s = "";
+ for (auto it = ns->components.begin(); it != ns->components.end(); ++it) {
+ if (s.size() == 0) {
+ s += *it;
+ } else {
+ s += "__" + *it;
+ }
+ }
+ return s;
+ }
+
+ // Create the full path for the imported namespace (format: A/B/C).
+ std::string NamespaceImportPath(const Namespace *ns) {
+ std::string s = "";
+ for (auto it = ns->components.begin(); it != ns->components.end(); ++it) {
+ if (s.size() == 0) {
+ s += *it;
+ } else {
+ s += "/" + *it;
+ }
+ }
+ return s;
+ }
+
+ // Ensure that a type is prefixed with its go package import name if it is
+ // used outside of its namespace.
+ std::string WrapInNameSpaceAndTrack(const Namespace *ns,
+ const std::string &name) {
+ if (CurrentNameSpace() == ns) return name;
+
+ tracked_imported_namespaces_.insert(ns);
+
+ std::string import_name = NamespaceImportName(ns);
+ return import_name + "." + name;
+ }
+
+ std::string WrapInNameSpaceAndTrack(const Definition &def) {
+ return WrapInNameSpaceAndTrack(def.defined_namespace, def.name);
+ }
+
+ const Namespace *CurrentNameSpace() const { return cur_name_space_; }
+
+ static size_t MaxNameLength(const EnumDef &enum_def) {
+ size_t max = 0;
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ max = std::max((*it)->name.length(), max);
+ }
+ return max;
+ }
+};
+} // namespace go
+
+bool GenerateGo(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ go::GoGenerator generator(parser, path, file_name, parser.opts.go_namespace);
+ return generator.generate();
+}
+
+} // namespace flatbuffers
diff --git a/src/idl_gen_grpc.cpp b/src/idl_gen_grpc.cpp
new file mode 100644
index 0000000..1d5e8e5
--- /dev/null
+++ b/src/idl_gen_grpc.cpp
@@ -0,0 +1,371 @@
+/*
+ * 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.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+#include "src/compiler/cpp_generator.h"
+#include "src/compiler/go_generator.h"
+#include "src/compiler/java_generator.h"
+
+#if defined(_MSC_VER)
+# pragma warning(push)
+# pragma warning(disable : 4512) // C4512: 'class' : assignment operator could
+// not be generated
+#endif
+
+namespace flatbuffers {
+
+class FlatBufMethod : public grpc_generator::Method {
+ public:
+ enum Streaming {
+ kNone, kClient, kServer, kBiDi
+ };
+
+ FlatBufMethod(const RPCCall *method) : method_(method) {
+ streaming_ = kNone;
+ auto val = method_->attributes.Lookup("streaming");
+ if (val) {
+ if (val->constant == "client") streaming_ = kClient;
+ if (val->constant == "server") streaming_ = kServer;
+ if (val->constant == "bidi") streaming_ = kBiDi;
+ }
+ }
+
+ grpc::string GetLeadingComments(const grpc::string) const { return ""; }
+
+ grpc::string GetTrailingComments(const grpc::string) const { return ""; }
+
+ std::vector<grpc::string> GetAllComments() const {
+ return method_->doc_comment;
+ }
+
+ std::string name() const { return method_->name; }
+
+ std::string GRPCType(const StructDef &sd) const {
+ return "flatbuffers::grpc::Message<" + sd.name + ">";
+ }
+
+ std::string get_input_type_name() const { return (*method_->request).name; }
+
+ std::string get_output_type_name() const { return (*method_->response).name; }
+
+ bool get_module_and_message_path_input(grpc::string * /*str*/,
+ grpc::string /*generator_file_name*/,
+ bool /*generate_in_pb2_grpc*/,
+ grpc::string /*import_prefix*/) const {
+ return true;
+ }
+
+ bool get_module_and_message_path_output(
+ grpc::string * /*str*/, grpc::string /*generator_file_name*/,
+ bool /*generate_in_pb2_grpc*/, grpc::string /*import_prefix*/) const {
+ return true;
+ }
+
+ std::string input_type_name() const { return GRPCType(*method_->request); }
+
+ std::string output_type_name() const { return GRPCType(*method_->response); }
+
+ bool NoStreaming() const { return streaming_ == kNone; }
+
+ bool ClientStreaming() const { return streaming_ == kClient; }
+
+ bool ServerStreaming() const { return streaming_ == kServer; }
+
+ bool BidiStreaming() const { return streaming_ == kBiDi; }
+
+ private:
+ const RPCCall *method_;
+ Streaming streaming_;
+};
+
+class FlatBufService : public grpc_generator::Service {
+ public:
+ FlatBufService(const ServiceDef *service) : service_(service) {}
+
+ grpc::string GetLeadingComments(const grpc::string) const { return ""; }
+
+ grpc::string GetTrailingComments(const grpc::string) const { return ""; }
+
+ std::vector<grpc::string> GetAllComments() const {
+ return service_->doc_comment;
+ }
+
+ std::string name() const { return service_->name; }
+
+ int method_count() const {
+ return static_cast<int>(service_->calls.vec.size());
+ }
+
+ std::unique_ptr<const grpc_generator::Method> method(int i) const {
+ return std::unique_ptr<const grpc_generator::Method>(
+ new FlatBufMethod(service_->calls.vec[i]));
+ }
+
+ private:
+ const ServiceDef *service_;
+};
+
+class FlatBufPrinter : public grpc_generator::Printer {
+ public:
+ FlatBufPrinter(std::string *str) : str_(str), escape_char_('$'), indent_(0) {}
+
+ void Print(const std::map<std::string, std::string> &vars,
+ const char *string_template) {
+ std::string s = string_template;
+ // Replace any occurrences of strings in "vars" that are surrounded
+ // by the escape character by what they're mapped to.
+ size_t pos;
+ while ((pos = s.find(escape_char_)) != std::string::npos) {
+ // Found an escape char, must also find the closing one.
+ size_t pos2 = s.find(escape_char_, pos + 1);
+ // If placeholder not closed, ignore.
+ if (pos2 == std::string::npos) break;
+ auto it = vars.find(s.substr(pos + 1, pos2 - pos - 1));
+ // If unknown placeholder, ignore.
+ if (it == vars.end()) break;
+ // Subtitute placeholder.
+ s.replace(pos, pos2 - pos + 1, it->second);
+ }
+ Print(s.c_str());
+ }
+
+ void Print(const char *s) {
+ if (s == nullptr || *s == '\0') { return; }
+ // Add this string, but for each part separated by \n, add indentation.
+ for (;;) {
+ // Current indentation.
+ str_->insert(str_->end(), indent_ * 2, ' ');
+ // See if this contains more than one line.
+ const char *lf = strchr(s, '\n');
+ if (lf) {
+ (*str_) += std::string(s, lf + 1);
+ s = lf + 1;
+ if (!*s) break; // Only continue if there's more lines.
+ } else {
+ (*str_) += s;
+ break;
+ }
+ }
+ }
+
+ void Indent() { indent_++; }
+
+ void Outdent() {
+ indent_--;
+ FLATBUFFERS_ASSERT(indent_ >= 0);
+ }
+
+ private:
+ std::string *str_;
+ char escape_char_;
+ int indent_;
+};
+
+class FlatBufFile : public grpc_generator::File {
+ public:
+ enum Language {
+ kLanguageGo, kLanguageCpp, kLanguageJava
+ };
+
+ FlatBufFile(const Parser &parser, const std::string &file_name,
+ Language language)
+ : parser_(parser), file_name_(file_name), language_(language) {}
+
+ FlatBufFile &operator=(const FlatBufFile &);
+
+ grpc::string GetLeadingComments(const grpc::string) const { return ""; }
+
+ grpc::string GetTrailingComments(const grpc::string) const { return ""; }
+
+ std::vector<grpc::string> GetAllComments() const {
+ return std::vector<grpc::string>();
+ }
+
+ std::string filename() const { return file_name_; }
+
+ std::string filename_without_ext() const {
+ return StripExtension(file_name_);
+ }
+
+ std::string message_header_ext() const { return "_generated.h"; }
+
+ std::string service_header_ext() const { return ".grpc.fb.h"; }
+
+ std::string package() const {
+ return parser_.current_namespace_->GetFullyQualifiedName("");
+ }
+
+ std::vector<std::string> package_parts() const {
+ return parser_.current_namespace_->components;
+ }
+
+ std::string additional_headers() const {
+ switch (language_) {
+ case kLanguageCpp: {
+ return "#include \"flatbuffers/grpc.h\"\n";
+ }
+ case kLanguageGo: {
+ return "import \"github.com/google/flatbuffers/go\"";
+ }
+ case kLanguageJava: {
+ return "import com.google.flatbuffers.grpc.FlatbuffersUtils;";
+ }
+ }
+ return "";
+ }
+
+ int service_count() const {
+ return static_cast<int>(parser_.services_.vec.size());
+ }
+
+ std::unique_ptr<const grpc_generator::Service> service(int i) const {
+ return std::unique_ptr<const grpc_generator::Service>(
+ new FlatBufService(parser_.services_.vec[i]));
+ }
+
+ std::unique_ptr<grpc_generator::Printer> CreatePrinter(
+ std::string *str) const {
+ return std::unique_ptr<grpc_generator::Printer>(new FlatBufPrinter(str));
+ }
+
+ private:
+ const Parser &parser_;
+ const std::string &file_name_;
+ const Language language_;
+};
+
+class GoGRPCGenerator : public flatbuffers::BaseGenerator {
+ public:
+ GoGRPCGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "", "" /*Unused*/),
+ parser_(parser),
+ path_(path),
+ file_name_(file_name) {}
+
+ bool generate() {
+ FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageGo);
+ grpc_go_generator::Parameters p;
+ p.custom_method_io_type = "flatbuffers.Builder";
+ for (int i = 0; i < file.service_count(); i++) {
+ auto service = file.service(i);
+ const Definition *def = parser_.services_.vec[i];
+ p.package_name = LastNamespacePart(*(def->defined_namespace));
+ p.service_prefix = def->defined_namespace->GetFullyQualifiedName(""); // file.package();
+ std::string output =
+ grpc_go_generator::GenerateServiceSource(&file, service.get(), &p);
+ std::string filename =
+ NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go";
+ if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
+ }
+ return true;
+ }
+
+ protected:
+ const Parser &parser_;
+ const std::string &path_, &file_name_;
+};
+
+bool GenerateGoGRPC(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ int nservices = 0;
+ for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
+ ++it) {
+ if (!(*it)->generated) nservices++;
+ }
+ if (!nservices) return true;
+ return GoGRPCGenerator(parser, path, file_name).generate();
+}
+
+bool GenerateCppGRPC(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ int nservices = 0;
+ for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
+ ++it) {
+ if (!(*it)->generated) nservices++;
+ }
+ if (!nservices) return true;
+
+ grpc_cpp_generator::Parameters generator_parameters;
+ // TODO(wvo): make the other parameters in this struct configurable.
+ generator_parameters.use_system_headers = true;
+
+ FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguageCpp);
+
+ std::string header_code =
+ grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) +
+ grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) +
+ grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) +
+ grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters);
+
+ std::string source_code =
+ grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) +
+ grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) +
+ grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) +
+ grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters);
+
+ return flatbuffers::SaveFile((path + file_name + ".grpc.fb.h").c_str(),
+ header_code, false) &&
+ flatbuffers::SaveFile((path + file_name + ".grpc.fb.cc").c_str(),
+ source_code, false);
+}
+
+class JavaGRPCGenerator : public flatbuffers::BaseGenerator {
+ public:
+ JavaGRPCGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "", "." /*separator*/) {}
+
+ bool generate() {
+ FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageJava);
+ grpc_java_generator::Parameters p;
+ for (int i = 0; i < file.service_count(); i++) {
+ auto service = file.service(i);
+ const Definition *def = parser_.services_.vec[i];
+ p.package_name =
+ def->defined_namespace->GetFullyQualifiedName(""); // file.package();
+ std::string output =
+ grpc_java_generator::GenerateServiceSource(&file, service.get(), &p);
+ std::string filename =
+ NamespaceDir(*def->defined_namespace) + def->name + "Grpc.java";
+ if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
+ }
+ return true;
+ }
+};
+
+bool GenerateJavaGRPC(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ int nservices = 0;
+ for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
+ ++it) {
+ if (!(*it)->generated) nservices++;
+ }
+ if (!nservices) return true;
+ return JavaGRPCGenerator(parser, path, file_name).generate();
+}
+
+} // namespace flatbuffers
+
+#if defined(_MSC_VER)
+# pragma warning(pop)
+#endif
diff --git a/src/idl_gen_js_ts.cpp b/src/idl_gen_js_ts.cpp
new file mode 100644
index 0000000..9c89c1a
--- /dev/null
+++ b/src/idl_gen_js_ts.cpp
@@ -0,0 +1,1405 @@
+/*
+ * 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.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+#include <cassert>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+
+const std::string kGeneratedFileNamePostfix = "_generated";
+
+struct JsTsLanguageParameters {
+ IDLOptions::Language language;
+ std::string file_extension;
+};
+
+struct ReexportDescription {
+ std::string symbol;
+ std::string source_namespace;
+ std::string target_namespace;
+};
+
+enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 };
+
+const JsTsLanguageParameters &GetJsLangParams(IDLOptions::Language lang) {
+ static JsTsLanguageParameters js_language_parameters[] = {
+ {
+ IDLOptions::kJs,
+ ".js",
+ },
+ {
+ IDLOptions::kTs,
+ ".ts",
+ },
+ };
+
+ if (lang == IDLOptions::kJs) {
+ return js_language_parameters[0];
+ } else {
+ FLATBUFFERS_ASSERT(lang == IDLOptions::kTs);
+ return js_language_parameters[1];
+ }
+}
+
+static std::string GeneratedFileName(const std::string &path,
+ const std::string &file_name,
+ const JsTsLanguageParameters &lang) {
+ return path + file_name + kGeneratedFileNamePostfix + lang.file_extension;
+}
+
+namespace jsts {
+// Iterate through all definitions we haven't generate code for (enums, structs,
+// and tables) and output them to a single file.
+class JsTsGenerator : public BaseGenerator {
+ public:
+ typedef std::unordered_set<std::string> imported_fileset;
+ typedef std::unordered_multimap<std::string, ReexportDescription>
+ reexport_map;
+
+ JsTsGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "", "."),
+ lang_(GetJsLangParams(parser_.opts.lang)) {}
+ // Iterate through all definitions we haven't generate code for (enums,
+ // structs, and tables) and output them to a single file.
+ bool generate() {
+ imported_fileset imported_files;
+ reexport_map reexports;
+
+ std::string enum_code, struct_code, import_code, exports_code, code;
+ generateEnums(&enum_code, &exports_code, reexports);
+ generateStructs(&struct_code, &exports_code, imported_files);
+ generateImportDependencies(&import_code, imported_files);
+ generateReexports(&import_code, reexports, imported_files);
+
+ code = code + "// " + FlatBuffersGeneratedWarning() + "\n\n";
+
+ // Generate code for all the namespace declarations.
+ GenNamespaces(&code, &exports_code);
+
+ // Output the main declaration code from above.
+ code += import_code;
+
+ code += enum_code;
+ code += struct_code;
+
+ if (lang_.language == IDLOptions::kJs && !exports_code.empty() &&
+ !parser_.opts.skip_js_exports) {
+ if (parser_.opts.use_ES6_js_export_format)
+ code += "// Exports for ECMAScript6 Modules\n";
+ else
+ code += "// Exports for Node.js and RequireJS\n";
+ code += exports_code;
+ }
+
+ return SaveFile(GeneratedFileName(path_, file_name_, lang_).c_str(), code,
+ false);
+ }
+
+ private:
+ JsTsLanguageParameters lang_;
+
+ // Generate code for imports
+ void generateImportDependencies(std::string *code_ptr,
+ const imported_fileset &imported_files) {
+ std::string &code = *code_ptr;
+ for (auto it = imported_files.begin(); it != imported_files.end(); ++it) {
+ const auto &file = *it;
+ const auto basename =
+ flatbuffers::StripPath(flatbuffers::StripExtension(file));
+ if (basename != file_name_) {
+ code += GenPrefixedImport(file, basename);
+ }
+ }
+ }
+
+ // Generate reexports, which might not have been explicitly imported using the
+ // "export import" trick
+ void generateReexports(std::string *code_ptr, const reexport_map &reexports,
+ imported_fileset imported_files) {
+ if (!parser_.opts.reexport_ts_modules ||
+ lang_.language != IDLOptions::kTs) {
+ return;
+ }
+
+ std::string &code = *code_ptr;
+ for (auto it = reexports.begin(); it != reexports.end(); ++it) {
+ const auto &file = *it;
+ const auto basename =
+ flatbuffers::StripPath(flatbuffers::StripExtension(file.first));
+ if (basename != file_name_) {
+ if (imported_files.find(file.first) == imported_files.end()) {
+ code += GenPrefixedImport(file.first, basename);
+ imported_files.emplace(file.first);
+ }
+
+ code += "export namespace " + file.second.target_namespace + " { \n";
+ code += "export import " + file.second.symbol + " = ";
+ code += GenFileNamespacePrefix(file.first) + "." +
+ file.second.source_namespace + "." + file.second.symbol +
+ "; }\n";
+ }
+ }
+ }
+
+ // Generate code for all enums.
+ void generateEnums(std::string *enum_code_ptr, std::string *exports_code_ptr,
+ reexport_map &reexports) {
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ auto &enum_def = **it;
+ GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports, false);
+ GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports, true);
+ }
+ }
+
+ // Generate code for all structs.
+ void generateStructs(std::string *decl_code_ptr,
+ std::string *exports_code_ptr,
+ imported_fileset &imported_files) {
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ auto &struct_def = **it;
+ GenStruct(parser_, struct_def, decl_code_ptr, exports_code_ptr,
+ imported_files);
+ }
+ }
+ void GenNamespaces(std::string *code_ptr, std::string *exports_ptr) {
+ if (lang_.language == IDLOptions::kTs &&
+ parser_.opts.skip_flatbuffers_import) {
+ return;
+ }
+
+ std::set<std::string> namespaces;
+
+ for (auto it = parser_.namespaces_.begin(); it != parser_.namespaces_.end();
+ ++it) {
+ std::string namespace_so_far;
+
+ // Gather all parent namespaces for this namespace
+ for (auto component = (*it)->components.begin();
+ component != (*it)->components.end(); ++component) {
+ if (!namespace_so_far.empty()) { namespace_so_far += '.'; }
+ namespace_so_far += *component;
+ namespaces.insert(namespace_so_far);
+ }
+ }
+
+ // Make sure parent namespaces come before child namespaces
+ std::vector<std::string> sorted_namespaces(namespaces.begin(),
+ namespaces.end());
+ std::sort(sorted_namespaces.begin(), sorted_namespaces.end());
+
+ // Emit namespaces in a form that Closure Compiler can optimize
+ std::string &code = *code_ptr;
+ std::string &exports = *exports_ptr;
+ for (auto it = sorted_namespaces.begin(); it != sorted_namespaces.end();
+ ++it) {
+ if (lang_.language == IDLOptions::kTs) {
+ if (it->find('.') == std::string::npos) {
+ code += "import { flatbuffers } from \"./flatbuffers\"\n";
+ break;
+ }
+ } else {
+ code += "/**\n * @const\n * @namespace\n */\n";
+ if (it->find('.') == std::string::npos) {
+ code += "var ";
+ if (parser_.opts.use_goog_js_export_format) {
+ exports += "goog.exportSymbol('" + *it + "', " + *it + ");\n";
+ } else if (parser_.opts.use_ES6_js_export_format) {
+ exports += "export {" + *it + "};\n";
+ } else {
+ exports += "this." + *it + " = " + *it + ";\n";
+ }
+ }
+ code += *it + " = " + *it + " || {};\n\n";
+ }
+ }
+ }
+
+ // Generate a documentation comment, if available.
+ static void GenDocComment(const std::vector<std::string> &dc,
+ std::string *code_ptr,
+ const std::string &extra_lines,
+ const char *indent = nullptr) {
+ if (dc.empty() && extra_lines.empty()) {
+ // Don't output empty comment blocks with 0 lines of comment content.
+ return;
+ }
+
+ std::string &code = *code_ptr;
+ if (indent) code += indent;
+ code += "/**\n";
+ for (auto it = dc.begin(); it != dc.end(); ++it) {
+ if (indent) code += indent;
+ code += " *" + *it + "\n";
+ }
+ if (!extra_lines.empty()) {
+ if (!dc.empty()) {
+ if (indent) code += indent;
+ code += " *\n";
+ }
+ if (indent) code += indent;
+ std::string::size_type start = 0;
+ for (;;) {
+ auto end = extra_lines.find('\n', start);
+ if (end != std::string::npos) {
+ code += " * " + extra_lines.substr(start, end - start) + "\n";
+ start = end + 1;
+ } else {
+ code += " * " + extra_lines.substr(start) + "\n";
+ break;
+ }
+ }
+ }
+ if (indent) code += indent;
+ code += " */\n";
+ }
+
+ static void GenDocComment(std::string *code_ptr,
+ const std::string &extra_lines) {
+ GenDocComment(std::vector<std::string>(), code_ptr, extra_lines);
+ }
+
+ std::string GenTypeAnnotation(AnnotationType annotation_type,
+ const std::string &type_name,
+ const std::string &arg_name,
+ bool include_newline = true) {
+ std::string result = "";
+ switch (annotation_type) {
+ case kParam: {
+ result += "@param";
+ break;
+ }
+ case kType: {
+ if (lang_.language != IDLOptions::kTs) {
+ result += "@type";
+ } else {
+ return "";
+ }
+ break;
+ }
+ case kReturns: {
+ result += "@returns";
+ break;
+ }
+ }
+ switch (lang_.language) {
+ case IDLOptions::kTs: {
+ result += " " + type_name;
+ break;
+ }
+ default: { result += " {" + type_name + "}"; }
+ }
+ if (!arg_name.empty()) {
+ result += " " + arg_name;
+ }
+ if (include_newline) {
+ result += "\n";
+ }
+
+ return result;
+ }
+
+ // Generate an enum declaration and an enum string lookup table.
+ void GenEnum(EnumDef &enum_def, std::string *code_ptr,
+ std::string *exports_ptr, reexport_map &reexports,
+ bool reverse) {
+ if (enum_def.generated) return;
+ if (reverse && lang_.language == IDLOptions::kTs) return; // FIXME.
+ std::string &code = *code_ptr;
+ std::string &exports = *exports_ptr;
+ GenDocComment(enum_def.doc_comment, code_ptr,
+ reverse ? "@enum {string}" : "@enum {number}");
+ std::string ns = GetNameSpace(enum_def);
+ std::string enum_def_name = enum_def.name + (reverse ? "Name" : "");
+ if (lang_.language == IDLOptions::kTs) {
+ if (!ns.empty()) { code += "export namespace " + ns + "{\n"; }
+ code += "export enum " + enum_def.name + "{\n";
+ } else {
+ if (enum_def.defined_namespace->components.empty()) {
+ code += "var ";
+ if (parser_.opts.use_goog_js_export_format) {
+ exports += "goog.exportSymbol('" + enum_def_name + "', " +
+ enum_def.name + ");\n";
+ } else if (parser_.opts.use_ES6_js_export_format) {
+ exports += "export {" + enum_def_name + "};\n";
+ } else {
+ exports += "this." + enum_def_name + " = " + enum_def_name + ";\n";
+ }
+ }
+ code += WrapInNameSpace(enum_def) + (reverse ? "Name" : "") + " = {\n";
+ }
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ auto &ev = **it;
+ if (!ev.doc_comment.empty()) {
+ if (it != enum_def.Vals().begin()) { code += '\n'; }
+ GenDocComment(ev.doc_comment, code_ptr, "", " ");
+ }
+
+ // Generate mapping between EnumName: EnumValue(int)
+ if (reverse) {
+ code += " " + enum_def.ToString(ev);
+ code += lang_.language == IDLOptions::kTs ? "= " : ": ";
+ code += "'" + ev.name + "'";
+ } else {
+ code += " " + ev.name;
+ code += lang_.language == IDLOptions::kTs ? "= " : ": ";
+ code += enum_def.ToString(ev);
+ }
+
+ code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n";
+
+ if (ev.union_type.struct_def) {
+ ReexportDescription desc = { ev.name,
+ GetNameSpace(*ev.union_type.struct_def),
+ GetNameSpace(enum_def) };
+ reexports.insert(
+ std::make_pair(ev.union_type.struct_def->file, std::move(desc)));
+ }
+ }
+
+ if (lang_.language == IDLOptions::kTs && !ns.empty()) { code += "}"; }
+ code += "};\n\n";
+ }
+
+ static std::string GenType(const Type &type) {
+ switch (type.base_type) {
+ case BASE_TYPE_BOOL:
+ case BASE_TYPE_CHAR: return "Int8";
+ case BASE_TYPE_UTYPE:
+ case BASE_TYPE_UCHAR: return "Uint8";
+ case BASE_TYPE_SHORT: return "Int16";
+ case BASE_TYPE_USHORT: return "Uint16";
+ case BASE_TYPE_INT: return "Int32";
+ case BASE_TYPE_UINT: return "Uint32";
+ case BASE_TYPE_LONG: return "Int64";
+ case BASE_TYPE_ULONG: return "Uint64";
+ case BASE_TYPE_FLOAT: return "Float32";
+ case BASE_TYPE_DOUBLE: return "Float64";
+ case BASE_TYPE_STRING: return "String";
+ case BASE_TYPE_VECTOR: return GenType(type.VectorType());
+ case BASE_TYPE_STRUCT: return type.struct_def->name;
+ default: return "Table";
+ }
+ }
+
+ std::string GenGetter(const Type &type, const std::string &arguments) {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return GenBBAccess() + ".__string" + arguments;
+ case BASE_TYPE_STRUCT: return GenBBAccess() + ".__struct" + arguments;
+ case BASE_TYPE_UNION: return GenBBAccess() + ".__union" + arguments;
+ case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
+ default: {
+ auto getter =
+ GenBBAccess() + ".read" + MakeCamel(GenType(type)) + arguments;
+ if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; }
+ if (type.enum_def) {
+ getter = "/** " +
+ GenTypeAnnotation(kType, WrapInNameSpace(*type.enum_def), "",
+ false) +
+ " */ (" + getter + ")";
+ }
+ return getter;
+ }
+ }
+ }
+
+ std::string GenBBAccess() {
+ return lang_.language == IDLOptions::kTs ? "this.bb!" : "this.bb";
+ }
+
+ std::string GenDefaultValue(const Value &value, const std::string &context) {
+ if (value.type.enum_def) {
+ if (auto val = value.type.enum_def->FindByValue(value.constant)) {
+ if (lang_.language == IDLOptions::kTs) {
+ return GenPrefixedTypeName(WrapInNameSpace(*value.type.enum_def),
+ value.type.enum_def->file) +
+ "." + val->name;
+ } else {
+ return WrapInNameSpace(*value.type.enum_def) + "." + val->name;
+ }
+ } else {
+ return "/** " +
+ GenTypeAnnotation(kType, WrapInNameSpace(*value.type.enum_def),
+ "", false) +
+ "} */ (" + value.constant + ")";
+ }
+ }
+
+ switch (value.type.base_type) {
+ case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
+
+ case BASE_TYPE_STRING: return "null";
+
+ case BASE_TYPE_LONG:
+ case BASE_TYPE_ULONG: {
+ int64_t constant = StringToInt(value.constant.c_str());
+ return context + ".createLong(" +
+ NumToString(static_cast<int32_t>(constant)) + ", " +
+ NumToString(static_cast<int32_t>(constant >> 32)) + ")";
+ }
+
+ default: return value.constant;
+ }
+ }
+
+ std::string GenTypeName(const Type &type, bool input,
+ bool allowNull = false) {
+ if (!input) {
+ if (type.base_type == BASE_TYPE_STRING ||
+ type.base_type == BASE_TYPE_STRUCT) {
+ std::string name;
+ if (type.base_type == BASE_TYPE_STRING) {
+ name = "string|Uint8Array";
+ } else {
+ name = WrapInNameSpace(*type.struct_def);
+ }
+ return (allowNull) ? (name + "|null") : (name);
+ }
+ }
+
+ switch (type.base_type) {
+ case BASE_TYPE_BOOL: return "boolean";
+ case BASE_TYPE_LONG:
+ case BASE_TYPE_ULONG: return "flatbuffers.Long";
+ default:
+ if (IsScalar(type.base_type)) {
+ if (type.enum_def) { return WrapInNameSpace(*type.enum_def); }
+ return "number";
+ }
+ return "flatbuffers.Offset";
+ }
+ }
+
+ // Returns the method name for use with add/put calls.
+ static std::string GenWriteMethod(const Type &type) {
+ // Forward to signed versions since unsigned versions don't exist
+ switch (type.base_type) {
+ case BASE_TYPE_UTYPE:
+ case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR));
+ case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT));
+ case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT));
+ case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG));
+ default: break;
+ }
+
+ return IsScalar(type.base_type) ? MakeCamel(GenType(type))
+ : (IsStruct(type) ? "Struct" : "Offset");
+ }
+
+ template<typename T> static std::string MaybeAdd(T value) {
+ return value != 0 ? " + " + NumToString(value) : "";
+ }
+
+ template<typename T> static std::string MaybeScale(T value) {
+ return value != 1 ? " * " + NumToString(value) : "";
+ }
+
+ static std::string GenFileNamespacePrefix(const std::string &file) {
+ return "NS" + std::to_string(HashFnv1a<uint64_t>(file.c_str()));
+ }
+
+ std::string GenPrefixedImport(const std::string &full_file_name,
+ const std::string &base_name) {
+ // Either keep the include path as it was
+ // or use only the base_name + kGeneratedFileNamePostfix
+ std::string path;
+ if (parser_.opts.keep_include_path) {
+ auto it = parser_.included_files_.find(full_file_name);
+ FLATBUFFERS_ASSERT(it != parser_.included_files_.end());
+ path =
+ flatbuffers::StripExtension(it->second) + kGeneratedFileNamePostfix;
+ } else {
+ path = base_name + kGeneratedFileNamePostfix;
+ }
+
+ // Add the include prefix and make the path always relative
+ path = flatbuffers::ConCatPathFileName(parser_.opts.include_prefix, path);
+ path = std::string(".") + kPathSeparator + path;
+
+ return "import * as " + GenFileNamespacePrefix(full_file_name) +
+ " from \"" + path + "\";\n";
+ }
+
+ // Adds a source-dependent prefix, for of import * statements.
+ std::string GenPrefixedTypeName(const std::string &typeName,
+ const std::string &file) {
+ const auto basename =
+ flatbuffers::StripPath(flatbuffers::StripExtension(file));
+ if (basename == file_name_ || parser_.opts.generate_all) {
+ return typeName;
+ }
+ return GenFileNamespacePrefix(file) + "." + typeName;
+ }
+
+ void GenStructArgs(const StructDef &struct_def, std::string *annotations,
+ std::string *arguments, const std::string &nameprefix) {
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (IsStruct(field.value.type)) {
+ // Generate arguments for a struct inside a struct. To ensure names
+ // don't clash, and to make it obvious these arguments are constructing
+ // a nested struct, prefix the name with the field name.
+ GenStructArgs(*field.value.type.struct_def, annotations, arguments,
+ nameprefix + field.name + "_");
+ } else {
+ *annotations +=
+ GenTypeAnnotation(kParam, GenTypeName(field.value.type, true),
+ nameprefix + field.name);
+ if (lang_.language == IDLOptions::kTs) {
+ *arguments += ", " + nameprefix + field.name + ": " +
+ GenTypeName(field.value.type, true);
+ } else {
+ *arguments += ", " + nameprefix + field.name;
+ }
+ }
+ }
+ }
+
+ static void GenStructBody(const StructDef &struct_def, std::string *body,
+ const std::string &nameprefix) {
+ *body += " builder.prep(";
+ *body += NumToString(struct_def.minalign) + ", ";
+ *body += NumToString(struct_def.bytesize) + ");\n";
+
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ auto &field = **it;
+ if (field.padding) {
+ *body += " builder.pad(" + NumToString(field.padding) + ");\n";
+ }
+ if (IsStruct(field.value.type)) {
+ // Generate arguments for a struct inside a struct. To ensure names
+ // don't clash, and to make it obvious these arguments are constructing
+ // a nested struct, prefix the name with the field name.
+ GenStructBody(*field.value.type.struct_def, body,
+ nameprefix + field.name + "_");
+ } else {
+ *body += " builder.write" + GenWriteMethod(field.value.type) + "(";
+ if (field.value.type.base_type == BASE_TYPE_BOOL) { *body += "+"; }
+ *body += nameprefix + field.name + ");\n";
+ }
+ }
+ }
+
+ void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr,
+ std::string &code, std::string &object_name, bool size_prefixed) {
+ if (!struct_def.fixed) {
+ GenDocComment(code_ptr,
+ GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") +
+ GenTypeAnnotation(kParam, object_name + "=", "obj") +
+ GenTypeAnnotation(kReturns, object_name, "", false));
+ std::string sizePrefixed("SizePrefixed");
+ if (lang_.language == IDLOptions::kTs) {
+ code += "static get" + (size_prefixed ? sizePrefixed : "") + "Root" + Verbose(struct_def, "As");
+ code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name +
+ "):" + object_name + " {\n";
+ } else {
+ code += object_name + ".get" + (size_prefixed ? sizePrefixed : "") + "Root" + Verbose(struct_def, "As");
+ code += " = function(bb, obj) {\n";
+ }
+ code += " return (obj || new " + object_name;
+ code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
+ code += "};\n\n";
+ }
+ }
+
+ void GenerateFinisher(StructDef &struct_def, std::string *code_ptr,
+ std::string &code, std::string &object_name, bool size_prefixed) {
+ if (parser_.root_struct_def_ == &struct_def) {
+ std::string sizePrefixed("SizePrefixed");
+ GenDocComment(
+ code_ptr,
+ GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
+ GenTypeAnnotation(kParam, "flatbuffers.Offset", "offset",
+ false));
+
+ if (lang_.language == IDLOptions::kTs) {
+ code += "static finish" + (size_prefixed ? sizePrefixed : "") + Verbose(struct_def) + "Buffer";
+ code +=
+ "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n";
+ } else {
+ code += object_name + ".finish" + (size_prefixed ? sizePrefixed : "") + Verbose(struct_def) + "Buffer";
+ code += " = function(builder, offset) {\n";
+ }
+
+ code += " builder.finish(offset";
+ if (!parser_.file_identifier_.empty()) {
+ code += ", '" + parser_.file_identifier_ + "'";
+ }
+ if (size_prefixed) {
+ if (parser_.file_identifier_.empty()) {
+ code += ", undefined";
+ }
+ code += ", true";
+ }
+ code += ");\n";
+ code += "};\n\n";
+ }
+ }
+
+ // Generate an accessor struct with constructor for a flatbuffers struct.
+ void GenStruct(const Parser &parser, StructDef &struct_def,
+ std::string *code_ptr, std::string *exports_ptr,
+ imported_fileset &imported_files) {
+ if (struct_def.generated) return;
+ std::string &code = *code_ptr;
+ std::string &exports = *exports_ptr;
+
+ std::string object_name;
+ std::string object_namespace = GetNameSpace(struct_def);
+
+ // Emit constructor
+ if (lang_.language == IDLOptions::kTs) {
+ object_name = struct_def.name;
+ GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
+ if (!object_namespace.empty()) {
+ code += "export namespace " + object_namespace + "{\n";
+ }
+ code += "export class " + struct_def.name;
+ code += " {\n";
+ if (lang_.language != IDLOptions::kTs) {
+ code += " /**\n";
+ code += " * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", "");
+ code += " */\n";
+ }
+ code += " bb: flatbuffers.ByteBuffer|null = null;\n";
+ code += "\n";
+ if (lang_.language != IDLOptions::kTs) {
+ code += " /**\n";
+ code += " * " + GenTypeAnnotation(kType, "number", "");
+ code += " */\n";
+ }
+ code += " bb_pos:number = 0;\n";
+ } else {
+ bool isStatement = struct_def.defined_namespace->components.empty();
+ object_name = WrapInNameSpace(struct_def);
+ GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
+ if (isStatement) {
+ if (parser_.opts.use_goog_js_export_format) {
+ exports += "goog.exportSymbol('" + struct_def.name + "', " +
+ struct_def.name + ");\n";
+ } else if (parser_.opts.use_ES6_js_export_format) {
+ exports += "export {" + struct_def.name + "};\n";
+ } else {
+ exports +=
+ "this." + struct_def.name + " = " + struct_def.name + ";\n";
+ }
+ code += "function " + object_name;
+ } else {
+ code += object_name + " = function";
+ }
+ code += "() {\n";
+ code += " /**\n";
+ code += " * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", "");
+ code += " */\n";
+ code += " this.bb = null;\n";
+ code += "\n";
+ code += " /**\n";
+ code += " * " + GenTypeAnnotation(kType, "number", "");
+ code += " */\n";
+ code += " this.bb_pos = 0;\n";
+ code += isStatement ? "}\n\n" : "};\n\n";
+ }
+
+ // Generate the __init method that sets the field in a pre-existing
+ // accessor object. This is to allow object reuse.
+ code += "/**\n";
+ code += " * " + GenTypeAnnotation(kParam, "number", "i");
+ code += " * " + GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb");
+ code += " * " + GenTypeAnnotation(kReturns, object_name, "");
+ code += " */\n";
+
+ if (lang_.language == IDLOptions::kTs) {
+ code +=
+ "__init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
+ } else {
+ code += object_name + ".prototype.__init = function(i, bb) {\n";
+ }
+
+ code += " this.bb_pos = i;\n";
+ code += " this.bb = bb;\n";
+ code += " return this;\n";
+ code += "};\n\n";
+
+ // Generate special accessors for the table that when used as the root of a
+ // FlatBuffer
+ GenerateRootAccessor(struct_def, code_ptr, code, object_name, false);
+ GenerateRootAccessor(struct_def, code_ptr, code, object_name, true);
+
+ // Generate the identifier check method
+ if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def &&
+ !parser_.file_identifier_.empty()) {
+ GenDocComment(
+ code_ptr,
+ GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") +
+ GenTypeAnnotation(kReturns, "boolean", "", false));
+ if (lang_.language == IDLOptions::kTs) {
+ code +=
+ "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean "
+ "{\n";
+ } else {
+ code += object_name + ".bufferHasIdentifier = function(bb) {\n";
+ }
+
+ code += " return bb.__has_identifier('" + parser_.file_identifier_;
+ code += "');\n};\n\n";
+ }
+
+ // Emit field accessors
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ auto offset_prefix =
+ " var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
+ NumToString(field.value.offset) + ");\n return offset ? ";
+
+ // Emit a scalar field
+ if (IsScalar(field.value.type.base_type) ||
+ field.value.type.base_type == BASE_TYPE_STRING) {
+ GenDocComment(
+ field.doc_comment, code_ptr,
+ std::string(field.value.type.base_type == BASE_TYPE_STRING
+ ? GenTypeAnnotation(kParam, "flatbuffers.Encoding=",
+ "optionalEncoding")
+ : "") +
+ GenTypeAnnotation(kReturns,
+ GenTypeName(field.value.type, false, true),
+ "", false));
+ if (lang_.language == IDLOptions::kTs) {
+ std::string prefix = MakeCamel(field.name, false) + "(";
+ if (field.value.type.base_type == BASE_TYPE_STRING) {
+ code += prefix + "):string|null\n";
+ code += prefix + "optionalEncoding:flatbuffers.Encoding" +
+ "):" + GenTypeName(field.value.type, false, true) + "\n";
+ code += prefix + "optionalEncoding?:any";
+ } else {
+ code += prefix;
+ }
+ if (field.value.type.enum_def) {
+ code +=
+ "):" +
+ GenPrefixedTypeName(GenTypeName(field.value.type, false, true),
+ field.value.type.enum_def->file) +
+ " {\n";
+
+ if (!parser_.opts.generate_all) {
+ imported_files.insert(field.value.type.enum_def->file);
+ }
+ } else {
+ code += "):" + GenTypeName(field.value.type, false, true) + " {\n";
+ }
+ } else {
+ code += object_name + ".prototype." + MakeCamel(field.name, false);
+ code += " = function(";
+ if (field.value.type.base_type == BASE_TYPE_STRING) {
+ code += "optionalEncoding";
+ }
+ code += ") {\n";
+ }
+
+ if (struct_def.fixed) {
+ code +=
+ " return " +
+ GenGetter(field.value.type,
+ "(this.bb_pos" + MaybeAdd(field.value.offset) + ")") +
+ ";\n";
+ } else {
+ std::string index = "this.bb_pos + offset";
+ if (field.value.type.base_type == BASE_TYPE_STRING) {
+ index += ", optionalEncoding";
+ }
+ code += offset_prefix +
+ GenGetter(field.value.type, "(" + index + ")") + " : " +
+ GenDefaultValue(field.value, GenBBAccess());
+ code += ";\n";
+ }
+ }
+
+ // Emit an object field
+ else {
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_STRUCT: {
+ auto type = WrapInNameSpace(*field.value.type.struct_def);
+ GenDocComment(
+ field.doc_comment, code_ptr,
+ GenTypeAnnotation(kParam, type + "=", "obj") +
+ GenTypeAnnotation(kReturns, type + "|null", "", false));
+ if (lang_.language == IDLOptions::kTs) {
+ type =
+ GenPrefixedTypeName(type, field.value.type.struct_def->file);
+ code += MakeCamel(field.name, false);
+ code += "(obj?:" + type + "):" + type + "|null {\n";
+ } else {
+ code +=
+ object_name + ".prototype." + MakeCamel(field.name, false);
+ code += " = function(obj) {\n";
+ }
+
+ if (struct_def.fixed) {
+ code += " return (obj || new " + type;
+ code += ").__init(this.bb_pos";
+ code +=
+ MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
+ } else {
+ code += offset_prefix + "(obj || new " + type + ").__init(";
+ code += field.value.type.struct_def->fixed
+ ? "this.bb_pos + offset"
+ : GenBBAccess() + ".__indirect(this.bb_pos + offset)";
+ code += ", " + GenBBAccess() + ") : null;\n";
+ }
+
+ if (lang_.language == IDLOptions::kTs && !parser_.opts.generate_all) {
+ imported_files.insert(field.value.type.struct_def->file);
+ }
+
+ break;
+ }
+
+ case BASE_TYPE_VECTOR: {
+ auto vectortype = field.value.type.VectorType();
+ auto vectortypename = GenTypeName(vectortype, false);
+ auto inline_size = InlineSize(vectortype);
+ auto index = GenBBAccess() +
+ ".__vector(this.bb_pos + offset) + index" +
+ MaybeScale(inline_size);
+ std::string args = GenTypeAnnotation(kParam, "number", "index");
+ std::string ret_type;
+ bool is_union = false;
+ switch (vectortype.base_type) {
+ case BASE_TYPE_STRUCT:
+ args += GenTypeAnnotation(kParam, vectortypename + "=", "obj");
+ ret_type = vectortypename;
+ break;
+ case BASE_TYPE_STRING:
+ args += GenTypeAnnotation(
+ kParam, "flatbuffers.Encoding=", "optionalEncoding");
+ ret_type = vectortypename;
+ break;
+ case BASE_TYPE_UNION:
+ args += GenTypeAnnotation(kParam, "flatbuffers.Table=", "obj");
+ ret_type = "?flatbuffers.Table";
+ is_union = true;
+ break;
+ default: ret_type = vectortypename;
+ }
+ GenDocComment(
+ field.doc_comment, code_ptr,
+ args + GenTypeAnnotation(kReturns, ret_type, "", false));
+ if (lang_.language == IDLOptions::kTs) {
+ std::string prefix = MakeCamel(field.name, false);
+ if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
+ prefix += "(index: number";
+ if (is_union) {
+ vectortypename = "T";
+ code += prefix + ", obj:T";
+ } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
+ vectortypename = GenPrefixedTypeName(
+ vectortypename, vectortype.struct_def->file);
+ code += prefix + ", obj?:" + vectortypename;
+
+ if (!parser_.opts.generate_all) {
+ imported_files.insert(vectortype.struct_def->file);
+ }
+ } else if (vectortype.base_type == BASE_TYPE_STRING) {
+ code += prefix + "):string\n";
+ code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
+ "):" + vectortypename + "\n";
+ code += prefix + ",optionalEncoding?:any";
+ } else {
+ code += prefix;
+ }
+ code += "):" + vectortypename + "|null {\n";
+ } else {
+ code +=
+ object_name + ".prototype." + MakeCamel(field.name, false);
+ code += " = function(index";
+ if (vectortype.base_type == BASE_TYPE_STRUCT || is_union) {
+ code += ", obj";
+ } else if (vectortype.base_type == BASE_TYPE_STRING) {
+ code += ", optionalEncoding";
+ }
+ code += ") {\n";
+ }
+
+ if (vectortype.base_type == BASE_TYPE_STRUCT) {
+ code += offset_prefix + "(obj || new " + vectortypename;
+ code += ").__init(";
+ code += vectortype.struct_def->fixed
+ ? index
+ : GenBBAccess() + ".__indirect(" + index + ")";
+ code += ", " + GenBBAccess() + ")";
+ } else {
+ if (is_union) {
+ index = "obj, " + index;
+ } else if (vectortype.base_type == BASE_TYPE_STRING) {
+ index += ", optionalEncoding";
+ }
+ code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
+ }
+ code += " : ";
+ if (field.value.type.element == BASE_TYPE_BOOL) {
+ code += "false";
+ } else if (field.value.type.element == BASE_TYPE_LONG ||
+ field.value.type.element == BASE_TYPE_ULONG) {
+ code += GenBBAccess() + ".createLong(0, 0)";
+ } else if (IsScalar(field.value.type.element)) {
+ if (field.value.type.enum_def) {
+ code += "/** " +
+ GenTypeAnnotation(
+ kType, WrapInNameSpace(*field.value.type.enum_def),
+ "", false) +
+ " */ (" + field.value.constant + ")";
+ } else {
+ code += "0";
+ }
+ } else {
+ code += "null";
+ }
+ code += ";\n";
+ break;
+ }
+
+ case BASE_TYPE_UNION:
+ GenDocComment(
+ field.doc_comment, code_ptr,
+ GenTypeAnnotation(kParam, "flatbuffers.Table", "obj") +
+ GenTypeAnnotation(kReturns, "?flatbuffers.Table", "",
+ false));
+ if (lang_.language == IDLOptions::kTs) {
+ code += MakeCamel(field.name, false);
+ code += "<T extends flatbuffers.Table>(obj:T):T|null {\n";
+ } else {
+ code +=
+ object_name + ".prototype." + MakeCamel(field.name, false);
+ code += " = function(obj) {\n";
+ }
+
+ code += offset_prefix +
+ GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
+ " : null;\n";
+ break;
+
+ default: FLATBUFFERS_ASSERT(0);
+ }
+ }
+ code += "};\n\n";
+
+ if (parser_.opts.use_goog_js_export_format) {
+ exports += "goog.exportProperty(" + object_name + ".prototype, '" +
+ MakeCamel(field.name, false) + "', " + object_name +
+ ".prototype." + MakeCamel(field.name, false) + ");\n";
+ }
+
+ // Adds the mutable scalar value to the output
+ if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer) {
+ std::string annotations = GenTypeAnnotation(
+ kParam, GenTypeName(field.value.type, true), "value");
+ GenDocComment(
+ code_ptr,
+ annotations + GenTypeAnnotation(kReturns, "boolean", "", false));
+
+ if (lang_.language == IDLOptions::kTs) {
+ std::string type;
+ if (field.value.type.enum_def) {
+ type = GenPrefixedTypeName(GenTypeName(field.value.type, true),
+ field.value.type.enum_def->file);
+ } else {
+ type = GenTypeName(field.value.type, true);
+ }
+
+ code += "mutate_" + field.name + "(value:" + type + "):boolean {\n";
+ } else {
+ code += object_name + ".prototype.mutate_" + field.name +
+ " = function(value) {\n";
+ }
+
+ code += " var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
+ NumToString(field.value.offset) + ");\n\n";
+ code += " if (offset === 0) {\n";
+ code += " return false;\n";
+ code += " }\n\n";
+
+ // special case for bools, which are treated as uint8
+ code += " " + GenBBAccess() + ".write" +
+ MakeCamel(GenType(field.value.type)) +
+ "(this.bb_pos + offset, ";
+ if (field.value.type.base_type == BASE_TYPE_BOOL &&
+ lang_.language == IDLOptions::kTs) {
+ code += "+";
+ }
+
+ code += "value);\n";
+ code += " return true;\n";
+ code += "};\n\n";
+
+ if (parser_.opts.use_goog_js_export_format) {
+ exports += "goog.exportProperty(" + object_name +
+ ".prototype, 'mutate_" + field.name + "', " + object_name +
+ ".prototype.mutate_" + field.name + ");\n";
+ }
+ }
+
+ // Emit vector helpers
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ // Emit a length helper
+ GenDocComment(code_ptr,
+ GenTypeAnnotation(kReturns, "number", "", false));
+ if (lang_.language == IDLOptions::kTs) {
+ code += MakeCamel(field.name, false);
+ code += "Length():number {\n" + offset_prefix;
+ } else {
+ code += object_name + ".prototype." + MakeCamel(field.name, false);
+ code += "Length = function() {\n" + offset_prefix;
+ }
+
+ code +=
+ GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n};\n\n";
+
+ if (parser_.opts.use_goog_js_export_format) {
+ exports += "goog.exportProperty(" + object_name + ".prototype, '" +
+ MakeCamel(field.name, false) + "Length', " + object_name +
+ ".prototype." + MakeCamel(field.name, false) +
+ "Length);\n";
+ }
+
+ // For scalar types, emit a typed array helper
+ auto vectorType = field.value.type.VectorType();
+ if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
+ GenDocComment(code_ptr, GenTypeAnnotation(
+ kReturns, GenType(vectorType) + "Array",
+ "", false));
+
+ if (lang_.language == IDLOptions::kTs) {
+ code += MakeCamel(field.name, false);
+ code += "Array():" + GenType(vectorType) + "Array|null {\n" +
+ offset_prefix;
+ } else {
+ code += object_name + ".prototype." + MakeCamel(field.name, false);
+ code += "Array = function() {\n" + offset_prefix;
+ }
+
+ code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
+ ".bytes().buffer, " + GenBBAccess() +
+ ".bytes().byteOffset + " + GenBBAccess() +
+ ".__vector(this.bb_pos + offset), " + GenBBAccess() +
+ ".__vector_len(this.bb_pos + offset)) : null;\n};\n\n";
+
+ if (parser_.opts.use_goog_js_export_format) {
+ exports += "goog.exportProperty(" + object_name + ".prototype, '" +
+ MakeCamel(field.name, false) + "Array', " + object_name +
+ ".prototype." + MakeCamel(field.name, false) +
+ "Array);\n";
+ }
+ }
+ }
+ }
+
+ // Emit a factory constructor
+ if (struct_def.fixed) {
+ std::string annotations =
+ GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder");
+ std::string arguments;
+ GenStructArgs(struct_def, &annotations, &arguments, "");
+ GenDocComment(code_ptr, annotations + GenTypeAnnotation(
+ kReturns, "flatbuffers.Offset",
+ "", false));
+
+ if (lang_.language == IDLOptions::kTs) {
+ code += "static create" + Verbose(struct_def) +
+ "(builder:flatbuffers.Builder";
+ code += arguments + "):flatbuffers.Offset {\n";
+ } else {
+ code += object_name + ".create" + Verbose(struct_def);
+ code += " = function(builder";
+ code += arguments + ") {\n";
+ }
+
+ GenStructBody(struct_def, &code, "");
+ code += " return builder.offset();\n};\n\n";
+ } else {
+ // Generate a method to start building a new object
+ GenDocComment(code_ptr, GenTypeAnnotation(kParam, "flatbuffers.Builder",
+ "builder", false));
+
+ if (lang_.language == IDLOptions::kTs) {
+ code += "static start" + Verbose(struct_def) +
+ "(builder:flatbuffers.Builder) {\n";
+ } else {
+ code += object_name + ".start" + Verbose(struct_def);
+ code += " = function(builder) {\n";
+ }
+
+ code += " builder.startObject(" +
+ NumToString(struct_def.fields.vec.size()) + ");\n";
+ code += "};\n\n";
+
+ // Generate a set of static methods that allow table construction
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ const auto argname = GetArgName(field);
+
+ // Generate the field insertion method
+ GenDocComment(
+ code_ptr,
+ GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
+ GenTypeAnnotation(kParam, GenTypeName(field.value.type, true),
+ argname, false));
+
+ if (lang_.language == IDLOptions::kTs) {
+ code += "static add" + MakeCamel(field.name);
+ code += "(builder:flatbuffers.Builder, " + argname + ":" +
+ GetArgType(field) + ") {\n";
+ } else {
+ code += object_name + ".add" + MakeCamel(field.name);
+ code += " = function(builder, " + argname + ") {\n";
+ }
+
+ code += " builder.addField" + GenWriteMethod(field.value.type) + "(";
+ code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
+ if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
+ code += argname + ", ";
+ if (!IsScalar(field.value.type.base_type)) {
+ code += "0";
+ } else {
+ if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
+ code += GenDefaultValue(field.value, "builder");
+ }
+ code += ");\n};\n\n";
+
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ auto vector_type = field.value.type.VectorType();
+ auto alignment = InlineAlignment(vector_type);
+ auto elem_size = InlineSize(vector_type);
+
+ // Generate a method to create a vector from a JavaScript array
+ if (!IsStruct(vector_type)) {
+ GenDocComment(
+ code_ptr,
+ GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
+ GenTypeAnnotation(
+ kParam,
+ "Array.<" + GenTypeName(vector_type, true) + ">",
+ "data") +
+ GenTypeAnnotation(kReturns, "flatbuffers.Offset", "",
+ false));
+
+ if (lang_.language == IDLOptions::kTs) {
+ code += "static create" + MakeCamel(field.name);
+ std::string type = GenTypeName(vector_type, true) + "[]";
+ if (type == "number[]") { type += " | Uint8Array"; }
+ code += "Vector(builder:flatbuffers.Builder, data:" + type +
+ "):flatbuffers.Offset {\n";
+ } else {
+ code += object_name + ".create" + MakeCamel(field.name);
+ code += "Vector = function(builder, data) {\n";
+ }
+
+ code += " builder.startVector(" + NumToString(elem_size);
+ code += ", data.length, " + NumToString(alignment) + ");\n";
+ code += " for (var i = data.length - 1; i >= 0; i--) {\n";
+ code += " builder.add" + GenWriteMethod(vector_type) + "(";
+ if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+"; }
+ code += "data[i]);\n";
+ code += " }\n";
+ code += " return builder.endVector();\n";
+ code += "};\n\n";
+ }
+
+ // Generate a method to start a vector, data to be added manually
+ // after
+ GenDocComment(
+ code_ptr,
+ GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
+ GenTypeAnnotation(kParam, "number", "numElems", false));
+
+ if (lang_.language == IDLOptions::kTs) {
+ code += "static start" + MakeCamel(field.name);
+ code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n";
+ } else {
+ code += object_name + ".start" + MakeCamel(field.name);
+ code += "Vector = function(builder, numElems) {\n";
+ }
+
+ code += " builder.startVector(" + NumToString(elem_size);
+ code += ", numElems, " + NumToString(alignment) + ");\n";
+ code += "};\n\n";
+ }
+ }
+
+ // Generate a method to stop building a new object
+ GenDocComment(
+ code_ptr,
+ GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
+ GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", false));
+
+ if (lang_.language == IDLOptions::kTs) {
+ code += "static end" + Verbose(struct_def);
+ code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n";
+ } else {
+ code += object_name + ".end" + Verbose(struct_def);
+ code += " = function(builder) {\n";
+ }
+
+ code += " var offset = builder.endObject();\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (!field.deprecated && field.required) {
+ code += " builder.requiredField(offset, ";
+ code += NumToString(field.value.offset);
+ code += "); // " + field.name + "\n";
+ }
+ }
+ code += " return offset;\n";
+ code += "};\n\n";
+
+ // Generate the methods to complete buffer construction
+ GenerateFinisher(struct_def, code_ptr, code, object_name, false);
+ GenerateFinisher(struct_def, code_ptr, code, object_name, true);
+
+ // Generate a convenient CreateX function
+ if (lang_.language == IDLOptions::kJs) {
+ std::string paramDoc =
+ GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder");
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated)
+ continue;
+ paramDoc +=
+ GenTypeAnnotation(kParam, GetArgType(field), GetArgName(field));
+ }
+ paramDoc +=
+ GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", false);
+
+ GenDocComment(code_ptr, paramDoc);
+ }
+
+ if (lang_.language == IDLOptions::kTs) {
+ code += "static create" + Verbose(struct_def);
+ code += "(builder:flatbuffers.Builder";
+ } else {
+ code += object_name + ".create" + Verbose(struct_def);
+ code += " = function(builder";
+ }
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated)
+ continue;
+
+ if (lang_.language == IDLOptions::kTs) {
+ code += ", " + GetArgName(field) + ":" + GetArgType(field);
+ } else {
+ code += ", " + GetArgName(field);
+ }
+ }
+
+ if (lang_.language == IDLOptions::kTs) {
+ code += "):flatbuffers.Offset {\n";
+ code += " " + struct_def.name + ".start" + Verbose(struct_def) +
+ "(builder);\n";
+ } else {
+ code += ") {\n";
+ code += " " + object_name + ".start" + Verbose(struct_def) +
+ "(builder);\n";
+ }
+
+ std::string methodPrefix =
+ lang_.language == IDLOptions::kTs ? struct_def.name : object_name;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated)
+ continue;
+
+ code += " " + methodPrefix + ".add" + MakeCamel(field.name) + "(";
+ code += "builder, " + GetArgName(field) + ");\n";
+ }
+
+ code += " return " + methodPrefix + ".end" + Verbose(struct_def) +
+ "(builder);\n";
+ code += "}\n";
+ if (lang_.language == IDLOptions::kJs)
+ code += "\n";
+ }
+
+ if (lang_.language == IDLOptions::kTs) {
+ if (!object_namespace.empty()) {
+ code += "}\n";
+ }
+ code += "}\n";
+ }
+ }
+
+ std::string GetArgType(const FieldDef &field) {
+ if (field.value.type.enum_def)
+ return GenPrefixedTypeName(GenTypeName(field.value.type, true),
+ field.value.type.enum_def->file);
+ return GenTypeName(field.value.type, true);
+ }
+
+ static std::string GetArgName(const FieldDef &field) {
+ auto argname = MakeCamel(field.name, false);
+ if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; }
+
+ return argname;
+ }
+
+ std::string Verbose(const StructDef &struct_def,
+ const char* prefix = "")
+ {
+ return parser_.opts.js_ts_short_names ? "" : prefix + struct_def.name;
+ }
+};
+} // namespace jsts
+
+bool GenerateJSTS(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ jsts::JsTsGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+
+std::string JSTSMakeRule(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ FLATBUFFERS_ASSERT(parser.opts.lang <= IDLOptions::kMAX);
+ const auto &lang = GetJsLangParams(parser.opts.lang);
+
+ std::string filebase =
+ flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
+ std::string make_rule = GeneratedFileName(path, filebase, lang) + ": ";
+
+ auto included_files = parser.GetIncludedFilesRecursive(file_name);
+ 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_json_schema.cpp b/src/idl_gen_json_schema.cpp
new file mode 100644
index 0000000..27e2cd4
--- /dev/null
+++ b/src/idl_gen_json_schema.cpp
@@ -0,0 +1,216 @@
+/*
+ * 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 <iostream>
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+
+static std::string GeneratedFileName(const std::string &path,
+ const std::string &file_name) {
+ return path + file_name + ".schema.json";
+}
+
+namespace jsons {
+
+std::string GenNativeType(BaseType type) {
+ switch (type) {
+ case BASE_TYPE_BOOL: return "boolean";
+ case BASE_TYPE_CHAR:
+ case BASE_TYPE_UCHAR:
+ case BASE_TYPE_SHORT:
+ case BASE_TYPE_USHORT:
+ case BASE_TYPE_INT:
+ case BASE_TYPE_UINT:
+ case BASE_TYPE_LONG:
+ case BASE_TYPE_ULONG:
+ case BASE_TYPE_FLOAT:
+ case BASE_TYPE_DOUBLE: return "number";
+ case BASE_TYPE_STRING: return "string";
+ case BASE_TYPE_ARRAY: return "array";
+ default: return "";
+ }
+}
+
+template<class T> std::string GenFullName(const T *enum_def) {
+ std::string full_name;
+ const auto &name_spaces = enum_def->defined_namespace->components;
+ for (auto ns = name_spaces.cbegin(); ns != name_spaces.cend(); ++ns) {
+ full_name.append(*ns + "_");
+ }
+ full_name.append(enum_def->name);
+ return full_name;
+}
+
+template<class T> std::string GenTypeRef(const T *enum_def) {
+ return "\"$ref\" : \"#/definitions/" + GenFullName(enum_def) + "\"";
+}
+
+std::string GenType(const std::string &name) {
+ return "\"type\" : \"" + name + "\"";
+}
+
+std::string GenType(const Type &type) {
+ if (type.enum_def != nullptr && !type.enum_def->is_union) {
+ // it is a reference to an enum type
+ return GenTypeRef(type.enum_def);
+ }
+ switch (type.base_type) {
+ case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru
+ case BASE_TYPE_VECTOR: {
+ std::string typeline;
+ typeline.append("\"type\" : \"array\", \"items\" : { ");
+ if (type.element == BASE_TYPE_STRUCT) {
+ typeline.append(GenTypeRef(type.struct_def));
+ } else {
+ typeline.append(GenType(GenNativeType(type.element)));
+ }
+ typeline.append(" }");
+ return typeline;
+ }
+ case BASE_TYPE_STRUCT: {
+ return GenTypeRef(type.struct_def);
+ }
+ case BASE_TYPE_UNION: {
+ std::string union_type_string("\"anyOf\": [");
+ const auto &union_types = type.enum_def->Vals();
+ for (auto ut = union_types.cbegin(); ut < union_types.cend(); ++ut) {
+ auto &union_type = *ut;
+ if (union_type->union_type.base_type == BASE_TYPE_NONE) { continue; }
+ if (union_type->union_type.base_type == BASE_TYPE_STRUCT) {
+ union_type_string.append(
+ "{ " + GenTypeRef(union_type->union_type.struct_def) + " }");
+ }
+ if (union_type != *type.enum_def->Vals().rbegin()) {
+ union_type_string.append(",");
+ }
+ }
+ union_type_string.append("]");
+ return union_type_string;
+ }
+ case BASE_TYPE_UTYPE: return GenTypeRef(type.enum_def);
+ default: return GenType(GenNativeType(type.base_type));
+ }
+}
+
+class JsonSchemaGenerator : public BaseGenerator {
+ private:
+ CodeWriter code_;
+
+ public:
+ JsonSchemaGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "", "") {}
+
+ explicit JsonSchemaGenerator(const BaseGenerator &base_generator)
+ : BaseGenerator(base_generator) {}
+
+ bool generate() {
+ code_.Clear();
+ code_ += "{";
+ code_ += " \"$schema\": \"http://json-schema.org/draft-04/schema#\",";
+ code_ += " \"definitions\": {";
+ for (auto e = parser_.enums_.vec.cbegin(); e != parser_.enums_.vec.cend();
+ ++e) {
+ code_ += " \"" + GenFullName(*e) + "\" : {";
+ code_ += " " + GenType("string") + ",";
+ std::string enumdef(" \"enum\": [");
+ for (auto enum_value = (*e)->Vals().begin();
+ enum_value != (*e)->Vals().end(); ++enum_value) {
+ enumdef.append("\"" + (*enum_value)->name + "\"");
+ if (*enum_value != (*e)->Vals().back()) { enumdef.append(", "); }
+ }
+ enumdef.append("]");
+ code_ += enumdef;
+ code_ += " },"; // close type
+ }
+ for (auto s = parser_.structs_.vec.cbegin();
+ s != parser_.structs_.vec.cend(); ++s) {
+ const auto &structure = *s;
+ code_ += " \"" + GenFullName(structure) + "\" : {";
+ code_ += " " + GenType("object") + ",";
+ std::string comment;
+ const auto &comment_lines = structure->doc_comment;
+ for (auto comment_line = comment_lines.cbegin();
+ comment_line != comment_lines.cend(); ++comment_line) {
+ comment.append(*comment_line);
+ }
+ if (comment.size() > 0) {
+ code_ += " \"description\" : \"" + comment + "\",";
+ }
+ code_ += " \"properties\" : {";
+
+ const auto &properties = structure->fields.vec;
+ for (auto prop = properties.cbegin(); prop != properties.cend(); ++prop) {
+ const auto &property = *prop;
+ std::string arrayInfo = "";
+ if (IsArray(property->value.type)) {
+ arrayInfo = ",\n \"minItems\": " +
+ NumToString(property->value.type.fixed_length) +
+ ",\n \"maxItems\": " +
+ NumToString(property->value.type.fixed_length);
+ }
+ std::string typeLine =
+ " \"" + property->name + "\" : {\n" + " " +
+ GenType(property->value.type) + arrayInfo + "\n }";
+ if (property != properties.back()) { typeLine.append(","); }
+ code_ += typeLine;
+ }
+ code_ += " },"; // close properties
+
+ std::vector<FieldDef *> requiredProperties;
+ std::copy_if(properties.begin(), properties.end(),
+ back_inserter(requiredProperties),
+ [](FieldDef const *prop) { return prop->required; });
+ if (requiredProperties.size() > 0) {
+ std::string required_string(" \"required\" : [");
+ for (auto req_prop = requiredProperties.cbegin();
+ req_prop != requiredProperties.cend(); ++req_prop) {
+ required_string.append("\"" + (*req_prop)->name + "\"");
+ if (*req_prop != requiredProperties.back()) {
+ required_string.append(", ");
+ }
+ }
+ required_string.append("],");
+ code_ += required_string;
+ }
+ code_ += " \"additionalProperties\" : false";
+ std::string closeType(" }");
+ if (*s != parser_.structs_.vec.back()) { closeType.append(","); }
+ code_ += closeType; // close type
+ }
+ code_ += " },"; // close definitions
+
+ // mark root type
+ code_ += " \"$ref\" : \"#/definitions/" +
+ GenFullName(parser_.root_struct_def_) + "\"";
+
+ code_ += "}"; // close schema root
+ const std::string file_path = GeneratedFileName(path_, file_name_);
+ const std::string final_code = code_.ToString();
+ return SaveFile(file_path.c_str(), final_code, false);
+ }
+};
+} // namespace jsons
+
+bool GenerateJsonSchema(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ jsons::JsonSchemaGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+} // namespace flatbuffers
diff --git a/src/idl_gen_kotlin.cpp b/src/idl_gen_kotlin.cpp
new file mode 100644
index 0000000..3ced7b3
--- /dev/null
+++ b/src/idl_gen_kotlin.cpp
@@ -0,0 +1,1527 @@
+/*
+ * 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.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+
+#include <functional>
+#include <unordered_set>
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+#if defined(FLATBUFFERS_CPP98_STL)
+#include <cctype>
+#endif // defined(FLATBUFFERS_CPP98_STL)
+
+namespace flatbuffers {
+
+namespace kotlin {
+
+typedef std::map<std::string, std::pair<std::string, std::string> > FbbParamMap;
+static TypedFloatConstantGenerator KotlinFloatGen("Double.", "Float.", "NaN",
+ "POSITIVE_INFINITY",
+ "NEGATIVE_INFINITY");
+
+static const CommentConfig comment_config = {"/**", " *", " */"};
+static const std::string ident_pad = " ";
+static const char *keywords[] = {
+ "package", "as", "typealias", "class", "this", "super",
+ "val", "var", "fun", "for", "null", "true",
+ "false", "is", "in", "throw", "return", "break",
+ "continue", "object", "if", "try", "else", "while",
+ "do", "when", "interface", "typeof", "Any", "Character"};
+
+// Escape Keywords
+static std::string Esc(const std::string &name) {
+ for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
+ if (name == keywords[i]) {
+ return MakeCamel(name + "_", false);
+ }
+ }
+
+ return MakeCamel(name, false);
+}
+
+class KotlinGenerator : public BaseGenerator {
+ public:
+ KotlinGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "", "."),
+ cur_name_space_(nullptr) {}
+
+ KotlinGenerator &operator=(const KotlinGenerator &);
+ bool generate() FLATBUFFERS_OVERRIDE {
+ std::string one_file_code;
+
+ cur_name_space_ = parser_.current_namespace_;
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ CodeWriter enumWriter(ident_pad);
+ auto &enum_def = **it;
+ if (!parser_.opts.one_file) cur_name_space_ = enum_def.defined_namespace;
+ GenEnum(enum_def, enumWriter);
+ if (parser_.opts.one_file) {
+ one_file_code += enumWriter.ToString();
+ } else {
+ if (!SaveType(enum_def.name, *enum_def.defined_namespace,
+ enumWriter.ToString(), false))
+ return false;
+ }
+ }
+
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ CodeWriter structWriter(ident_pad);
+ auto &struct_def = **it;
+ if (!parser_.opts.one_file)
+ cur_name_space_ = struct_def.defined_namespace;
+ GenStruct(struct_def, structWriter);
+ if (parser_.opts.one_file) {
+ one_file_code += structWriter.ToString();
+ } else {
+ if (!SaveType(struct_def.name, *struct_def.defined_namespace,
+ structWriter.ToString(), true))
+ return false;
+ }
+ }
+
+ if (parser_.opts.one_file) {
+ return SaveType(file_name_, *parser_.current_namespace_, one_file_code,
+ true);
+ }
+ return true;
+ }
+
+ // Save out the generated code for a single class while adding
+ // declaration boilerplate.
+ bool SaveType(const std::string &defname, const Namespace &ns,
+ const std::string &classcode, bool needs_includes) const {
+ if (!classcode.length()) return true;
+
+ std::string code =
+ "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
+
+ std::string namespace_name = FullNamespace(".", ns);
+ if (!namespace_name.empty()) {
+ code += "package " + namespace_name;
+ code += "\n\n";
+ }
+ if (needs_includes) {
+ code += "import java.nio.*\n";
+ code += "import kotlin.math.sign\n";
+ code += "import com.google.flatbuffers.*\n\n";
+ }
+ code += classcode;
+ auto filename = NamespaceDir(ns) + defname + ".kt";
+ return SaveFile(filename.c_str(), code, false);
+ }
+
+ const Namespace *CurrentNameSpace() const FLATBUFFERS_OVERRIDE {
+ return cur_name_space_;
+ }
+
+ static bool IsEnum(const Type &type) {
+ return type.enum_def != nullptr && IsInteger(type.base_type);
+ }
+
+ static std::string GenTypeBasic(const BaseType &type) {
+ // clang-format off
+ static const char * const kotlin_typename[] = {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ #KTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ };
+ return kotlin_typename[type];
+
+ }
+
+ std::string GenTypePointer(const Type &type) const {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING:
+ return "String";
+ case BASE_TYPE_VECTOR:
+ return GenTypeGet(type.VectorType());
+ case BASE_TYPE_STRUCT:
+ return WrapInNameSpace(*type.struct_def);
+ default:
+ return "Table";
+ }
+ }
+
+ std::string GenTypeGet(const Type &type) const {
+ return IsScalar(type.base_type) ? GenTypeBasic(type.base_type)
+ : GenTypePointer(type);
+ }
+
+ std::string GenEnumDefaultValue(const FieldDef &field) const {
+ auto &value = field.value;
+ FLATBUFFERS_ASSERT(value.type.enum_def);
+ auto &enum_def = *value.type.enum_def;
+ auto enum_val = enum_def.FindByValue(value.constant);
+ return enum_val ? (WrapInNameSpace(enum_def) + "." + enum_val->name)
+ : value.constant;
+ }
+
+
+ // Generate default values to compare against a default value when
+ // `force_defaults` is `false`.
+ // Main differences are:
+ // - Floats are upcasted to doubles
+ // - Unsigned are casted to signed
+ std::string GenFBBDefaultValue(const FieldDef &field) const {
+ auto out = GenDefaultValue(field, true);
+ // All FlatBufferBuilder default floating point values are doubles
+ if (field.value.type.base_type == BASE_TYPE_FLOAT) {
+ if (out.find("Float") != std::string::npos) {
+ out.replace(0, 5, "Double");
+ }
+ }
+ //Guarantee all values are doubles
+ if (out.back() == 'f')
+ out.pop_back();
+ return out;
+ }
+
+
+ // FlatBufferBuilder only store signed types, so this function
+ // returns a cast for unsigned values
+ std::string GenFBBValueCast(const FieldDef &field) const {
+ if (IsUnsigned(field.value.type.base_type)) {
+ return CastToSigned(field.value.type);
+ }
+ return "";
+ }
+
+ std::string GenDefaultValue(const FieldDef &field,
+ bool force_signed = false) const {
+ auto &value = field.value;
+ auto base_type = field.value.type.base_type;
+ if (IsFloat(base_type)) {
+ auto val = KotlinFloatGen.GenFloatConstant(field);
+ if (base_type == BASE_TYPE_DOUBLE &&
+ val.back() == 'f') {
+ val.pop_back();
+ }
+ return val;
+ }
+
+ if (base_type == BASE_TYPE_BOOL) {
+ return value.constant == "0" ? "false" : "true";
+ }
+
+ std::string suffix = "";
+
+ if (base_type == BASE_TYPE_LONG || !force_signed) {
+ suffix = LiteralSuffix(base_type);
+ }
+ return value.constant + suffix;
+ }
+
+ void GenEnum(EnumDef &enum_def, CodeWriter &writer) const {
+ if (enum_def.generated) return;
+
+ GenerateComment(enum_def.doc_comment, writer, &comment_config);
+
+ writer += "@Suppress(\"unused\")";
+ writer += "@ExperimentalUnsignedTypes";
+ writer += "class " + Esc(enum_def.name) + " private constructor() {";
+ writer.IncrementIdentLevel();
+
+ GenerateCompanionObject(writer, [&](){
+ // Write all properties
+ auto vals = enum_def.Vals();
+ for (auto it = vals.begin(); it != vals.end(); ++it) {
+ auto &ev = **it;
+ auto field_type = GenTypeBasic(enum_def.underlying_type.base_type);
+ auto val = enum_def.ToString(ev);
+ auto suffix = LiteralSuffix(enum_def.underlying_type.base_type);
+ writer.SetValue("name", Esc(ev.name));
+ writer.SetValue("type", field_type);
+ writer.SetValue("val", val + suffix);
+ GenerateComment(ev.doc_comment, writer, &comment_config);
+ writer += "const val {{name}}: {{type}} = {{val}}";
+ }
+
+ // 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 the moment we simply don't output a table at all.
+ auto range = enum_def.Distance();
+ // Average distance between values above which we consider a table
+ // "too sparse". Change at will.
+ static const uint64_t kMaxSparseness = 5;
+ if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
+ GeneratePropertyOneLine(writer, "names", "Array<String>",
+ [&](){
+ writer += "arrayOf(\\";
+ auto val = enum_def.Vals().front();
+ for (auto it = vals.begin(); it != vals.end(); ++it) {
+ auto ev = *it;
+ for (auto k = enum_def.Distance(val, ev); k > 1; --k)
+ writer += "\"\", \\";
+ val = ev;
+ writer += "\"" + (*it)->name + "\"\\";
+ if (it+1 != vals.end()) {
+ writer += ", \\";
+ }
+ }
+ writer += ")";
+ });
+ GenerateFunOneLine(writer, "name", "e: Int", "String", [&](){
+ writer += "names[e\\";
+ if (enum_def.MinValue()->IsNonZero())
+ writer += " - " + enum_def.MinValue()->name + ".toInt()\\";
+ writer += "]";
+ });
+ }
+ });
+ writer.DecrementIdentLevel();
+ writer += "}";
+ }
+
+ // Returns the function name that is able to read a value of the given type.
+ std::string ByteBufferGetter(const Type &type, std::string bb_var_name) const {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING:
+ return "__string";
+ case BASE_TYPE_STRUCT:
+ return "__struct";
+ case BASE_TYPE_UNION:
+ return "__union";
+ case BASE_TYPE_VECTOR:
+ return ByteBufferGetter(type.VectorType(), bb_var_name);
+ case BASE_TYPE_INT:
+ case BASE_TYPE_UINT:
+ return bb_var_name + ".getInt";
+ case BASE_TYPE_SHORT:
+ case BASE_TYPE_USHORT:
+ return bb_var_name + ".getShort";
+ case BASE_TYPE_ULONG:
+ case BASE_TYPE_LONG:
+ return bb_var_name + ".getLong";
+ case BASE_TYPE_FLOAT:
+ return bb_var_name + ".getFloat";
+ case BASE_TYPE_DOUBLE:
+ return bb_var_name + ".getDouble";
+ case BASE_TYPE_CHAR:
+ case BASE_TYPE_UCHAR:
+ case BASE_TYPE_NONE:
+ case BASE_TYPE_UTYPE:
+ return bb_var_name + ".get";
+ case BASE_TYPE_BOOL:
+ return "0.toByte() != " + bb_var_name + ".get";
+ default:
+ return bb_var_name + ".get" + MakeCamel(GenTypeBasic(type.base_type));
+ }
+ }
+
+ std::string ByteBufferSetter(const Type &type) const {
+ if (IsScalar(type.base_type)) {
+ switch (type.base_type) {
+ case BASE_TYPE_INT:
+ case BASE_TYPE_UINT:
+ return "bb.putInt";
+ case BASE_TYPE_SHORT:
+ case BASE_TYPE_USHORT:
+ return "bb.putShort";
+ case BASE_TYPE_ULONG:
+ case BASE_TYPE_LONG:
+ return "bb.putLong";
+ case BASE_TYPE_FLOAT:
+ return "bb.putFloat";
+ case BASE_TYPE_DOUBLE:
+ return "bb.putDouble";
+ case BASE_TYPE_CHAR:
+ case BASE_TYPE_UCHAR:
+ case BASE_TYPE_BOOL:
+ case BASE_TYPE_NONE:
+ case BASE_TYPE_UTYPE:
+ return "bb.put";
+ default:
+ return "bb.put" + MakeCamel(GenTypeBasic(type.base_type));
+ }
+ }
+ return "";
+ }
+
+ // Returns the function name that is able to read a value of the given type.
+ std::string GenLookupByKey(flatbuffers::FieldDef *key_field,
+ const std::string &bb_var_name,
+ const char *num = nullptr) const {
+ auto type = key_field->value.type;
+ return ByteBufferGetter(type, bb_var_name) + "(" + GenOffsetGetter(key_field, num) + ")";
+
+ }
+
+ // Returns the method name for use with add/put calls.
+ static std::string GenMethod(const Type &type) {
+ return IsScalar(type.base_type) ? ToSignedType(type)
+ : (IsStruct(type) ? "Struct" : "Offset");
+ }
+
+ // Recursively generate arguments for a constructor, to deal with nested
+ // structs.
+ static void GenStructArgs(const StructDef &struct_def, CodeWriter &writer,
+ const char *nameprefix) {
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (IsStruct(field.value.type)) {
+ // Generate arguments for a struct inside a struct. To ensure
+ // names don't clash, and to make it obvious these arguments are
+ // constructing a nested struct, prefix the name with the field
+ // name.
+ GenStructArgs(*field.value.type.struct_def, writer,
+ (nameprefix + (field.name + "_")).c_str());
+ } else {
+ writer += std::string(", ") + nameprefix + "\\";
+ writer += MakeCamel(field.name) + ": \\";
+ writer += GenTypeBasic(field.value.type.base_type) + "\\";
+ }
+ }
+ }
+
+ // Recusively generate struct construction statements of the form:
+ // builder.putType(name);
+ // and insert manual padding.
+ static void GenStructBody(const StructDef &struct_def, CodeWriter &writer,
+ const char *nameprefix) {
+ writer.SetValue("align", NumToString(struct_def.minalign));
+ writer.SetValue("size", NumToString(struct_def.bytesize));
+ writer += "builder.prep({{align}}, {{size}})";
+ auto fields_vec = struct_def.fields.vec;
+ for (auto it = fields_vec.rbegin(); it != fields_vec.rend(); ++it) {
+ auto &field = **it;
+
+ if (field.padding) {
+ writer.SetValue("pad", NumToString(field.padding));
+ writer += "builder.pad({{pad}})";
+ }
+ if (IsStruct(field.value.type)) {
+ GenStructBody(*field.value.type.struct_def, writer,
+ (nameprefix + (field.name + "_")).c_str());
+ } else {
+ writer.SetValue("type", GenMethod(field.value.type));
+ writer.SetValue("argname", nameprefix +
+ MakeCamel(field.name, false));
+ writer.SetValue("cast", CastToSigned(field.value.type));
+ writer += "builder.put{{type}}({{argname}}{{cast}})";
+ }
+ }
+ }
+
+ std::string GenByteBufferLength(const char *bb_name) const {
+ std::string bb_len = bb_name;
+ bb_len += ".capacity()";
+ return bb_len;
+ }
+
+ std::string GenOffsetGetter(flatbuffers::FieldDef *key_field,
+ const char *num = nullptr) const {
+ std::string key_offset = "__offset(" +
+ NumToString(key_field->value.offset) + ", ";
+ if (num) {
+ key_offset += num;
+ key_offset += ", _bb)";
+ } else {
+ key_offset += GenByteBufferLength("bb");
+ key_offset += " - tableOffset, bb)";
+ }
+ return key_offset;
+ }
+
+ void GenStruct(StructDef &struct_def, CodeWriter &writer) const {
+ if (struct_def.generated) return;
+
+ GenerateComment(struct_def.doc_comment, writer, &comment_config);
+ auto fixed = struct_def.fixed;
+
+ writer.SetValue("struct_name", Esc(struct_def.name));
+ writer.SetValue("superclass", fixed ? "Struct" : "Table");
+
+ writer += "@Suppress(\"unused\")";
+ writer += "@ExperimentalUnsignedTypes";
+ writer += "class {{struct_name}} : {{superclass}}() {\n";
+
+ writer.IncrementIdentLevel();
+
+ {
+ // Generate the __init() method that sets the field in a pre-existing
+ // accessor object. This is to allow object reuse.
+ GenerateFun(writer, "__init", "_i: Int, _bb: ByteBuffer", "", [&]() {
+ writer += "__reset(_i, _bb)";
+ });
+
+ // Generate assign method
+ GenerateFun(writer, "__assign", "_i: Int, _bb: ByteBuffer",
+ Esc(struct_def.name), [&]() {
+ writer += "__init(_i, _bb)";
+ writer += "return this";
+ });
+
+ // Generate all getters
+ GenerateStructGetters(struct_def, writer);
+
+ // Generate Static Fields
+ GenerateCompanionObject(writer, [&](){
+
+ if (!struct_def.fixed) {
+ FieldDef *key_field = nullptr;
+
+ // Generate verson check method.
+ // Force compile time error if not using the same version
+ // runtime.
+ GenerateFunOneLine(writer, "validateVersion", "", "", [&](){
+ writer += "Constants.FLATBUFFERS_1_11_1()";
+ });
+
+ GenerateGetRootAsAccessors(Esc(struct_def.name), writer);
+ GenerateBufferHasIdentifier(struct_def, writer);
+ GenerateTableCreator(struct_def, writer);
+
+ GenerateStartStructMethod(struct_def, writer);
+
+ // Static Add for fields
+ auto fields = struct_def.fields.vec;
+ int field_pos = -1;
+ for (auto it = fields.begin(); it != fields.end(); ++it) {
+ auto &field = **it;
+ field_pos++;
+ if (field.deprecated) continue;
+ if (field.key) key_field = &field;
+ GenerateAddField(NumToString(field_pos), field, writer);
+
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ auto vector_type = field.value.type.VectorType();
+ if (!IsStruct(vector_type)) {
+ GenerateCreateVectorField(field, writer);
+ }
+ GenerateStartVectorField(field, writer);
+ }
+ }
+
+ GenerateEndStructMethod(struct_def, writer);
+ auto file_identifier = parser_.file_identifier_;
+ if (parser_.root_struct_def_ == &struct_def) {
+ GenerateFinishStructBuffer(struct_def,
+ file_identifier,
+ writer);
+ GenerateFinishSizePrefixed(struct_def,
+ file_identifier,
+ writer);
+ }
+
+ if (struct_def.has_key) {
+ GenerateLookupByKey(key_field, struct_def, writer);
+ }
+ } else {
+ GenerateStaticConstructor(struct_def, writer);
+ }
+ });
+ }
+
+ // class closing
+ writer.DecrementIdentLevel();
+ writer += "}";
+ }
+
+ // TODO: move key_field to reference instead of pointer
+ void GenerateLookupByKey(FieldDef *key_field, StructDef &struct_def,
+ CodeWriter &writer) const {
+ std::stringstream params;
+ params << "obj: " << Esc(struct_def.name) << "?" << ", ";
+ params << "vectorLocation: Int, ";
+ params << "key: " << GenTypeGet(key_field->value.type) << ", ";
+ params << "bb: ByteBuffer";
+
+ auto statements = [&]() {
+ auto base_type = key_field->value.type.base_type;
+ writer.SetValue("struct_name", Esc(struct_def.name));
+ if (base_type == BASE_TYPE_STRING) {
+ writer += "val byteKey = key."
+ "toByteArray(Table.UTF8_CHARSET.get()!!)";
+ }
+ writer += "var span = bb.getInt(vectorLocation - 4)";
+ writer += "var start = 0";
+ writer += "while (span != 0) {";
+ writer.IncrementIdentLevel();
+ writer += "var middle = span / 2";
+ writer += "val tableOffset = __indirect(vector"
+ "Location + 4 * (start + middle), bb)";
+ if (key_field->value.type.base_type == BASE_TYPE_STRING) {
+ writer += "val comp = compareStrings(\\";
+ writer += GenOffsetGetter(key_field) + "\\";
+ writer += ", byteKey, bb)";
+ } else {
+ auto cast = CastToUsigned(key_field->value.type);
+ auto get_val = GenLookupByKey(key_field, "bb");
+ writer += "val value = " + get_val + cast;
+ writer += "val comp = value.compareTo(key)";
+ }
+ writer += "when {";
+ writer.IncrementIdentLevel();
+ writer += "comp > 0 -> span = middle";
+ writer += "comp < 0 -> {";
+ writer.IncrementIdentLevel();
+ writer += "middle++";
+ writer += "start += middle";
+ writer += "span -= middle";
+ writer.DecrementIdentLevel();
+ writer += "}"; // end comp < 0
+ writer += "else -> {";
+ writer.IncrementIdentLevel();
+ writer += "return (obj ?: {{struct_name}}()).__assign(tableOffset, bb)";
+ writer.DecrementIdentLevel();
+ writer += "}"; // end else
+ writer.DecrementIdentLevel();
+ writer += "}"; // end when
+ writer.DecrementIdentLevel();
+ writer += "}"; // end while
+ writer += "return null";
+ };
+ GenerateFun(writer, "__lookup_by_key",
+ params.str(),
+ Esc(struct_def.name) + "?",
+ statements);
+ }
+
+ void GenerateFinishSizePrefixed(StructDef &struct_def,
+ const std::string &identifier,
+ CodeWriter &writer) const {
+ auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : "";
+ auto params = "builder: FlatBufferBuilder, offset: Int";
+ auto method_name = "finishSizePrefixed" + Esc(struct_def.name) + "Buffer";
+ GenerateFunOneLine(writer, method_name, params, "", [&]() {
+ writer += "builder.finishSizePrefixed(offset" + id + ")";
+ });
+ }
+ void GenerateFinishStructBuffer(StructDef &struct_def,
+ const std::string &identifier,
+ CodeWriter &writer) const {
+ auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : "";
+ auto params = "builder: FlatBufferBuilder, offset: Int";
+ auto method_name = "finish" + Esc(struct_def.name) + "Buffer";
+ GenerateFunOneLine(writer, method_name, params, "", [&]() {
+ writer += "builder.finish(offset" + id + ")";
+ });
+ }
+
+ void GenerateEndStructMethod(StructDef &struct_def, CodeWriter &writer) const {
+ // Generate end{{TableName}}(builder: FlatBufferBuilder) method
+ auto name = "end" + Esc(struct_def.name);
+ auto params = "builder: FlatBufferBuilder";
+ auto returns = "Int";
+ auto field_vec = struct_def.fields.vec;
+
+ GenerateFun(writer, name, params, returns, [&](){
+ writer += "val o = builder.endTable()";
+ writer.IncrementIdentLevel();
+ for (auto it = field_vec.begin(); it != field_vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated || !field.required) {
+ continue;
+ }
+ writer.SetValue("offset", NumToString(field.value.offset));
+ writer += "builder.required(o, {{offset}})";
+ }
+ writer.DecrementIdentLevel();
+ writer += "return o";
+ });
+ }
+
+ // Generate a method to create a vector from a Kotlin array.
+ void GenerateCreateVectorField(FieldDef &field, CodeWriter &writer) const {
+ auto vector_type = field.value.type.VectorType();
+ auto method_name = "create" + MakeCamel(Esc(field.name)) + "Vector";
+ auto params = "builder: FlatBufferBuilder, data: " +
+ GenTypeBasic(vector_type.base_type) + "Array";
+ writer.SetValue("size", NumToString(InlineSize(vector_type)));
+ writer.SetValue("align", NumToString(InlineAlignment(vector_type)));
+ writer.SetValue("root", GenMethod(vector_type));
+ writer.SetValue("cast", CastToSigned(vector_type));
+
+ GenerateFun(writer, method_name, params, "Int", [&](){
+ writer += "builder.startVector({{size}}, data.size, {{align}})";
+ writer += "for (i in data.size - 1 downTo 0) {";
+ writer.IncrementIdentLevel();
+ writer += "builder.add{{root}}(data[i]{{cast}})";
+ writer.DecrementIdentLevel();
+ writer += "}";
+ writer += "return builder.endVector()";
+ });
+ }
+
+ void GenerateStartVectorField(FieldDef &field, CodeWriter &writer) const {
+ // Generate a method to start a vector, data to be added manually
+ // after.
+ auto vector_type = field.value.type.VectorType();
+ auto params = "builder: FlatBufferBuilder, numElems: Int";
+ writer.SetValue("size", NumToString(InlineSize(vector_type)));
+ writer.SetValue("align", NumToString(InlineAlignment(vector_type)));
+
+ GenerateFunOneLine(writer,
+ "start" + MakeCamel(Esc(field.name) + "Vector", true),
+ params,
+ "",
+ [&]() {
+ writer += "builder.startVector({{size}}, numElems, {{align}})";
+ });
+ }
+
+ void GenerateAddField(std::string field_pos, FieldDef &field,
+ CodeWriter &writer) const {
+ auto field_type = GenTypeBasic(field.value.type.base_type);
+ auto secondArg = MakeCamel(Esc(field.name), false) + ": " + field_type;
+ GenerateFunOneLine(writer, "add" + MakeCamel(Esc(field.name), true),
+ "builder: FlatBufferBuilder, " + secondArg, "", [&](){
+ auto method = GenMethod(field.value.type);
+ writer.SetValue("field_name", MakeCamel(Esc(field.name), false));
+ writer.SetValue("method_name", method);
+ writer.SetValue("pos", field_pos);
+ writer.SetValue("default", GenFBBDefaultValue(field));
+ writer.SetValue("cast", GenFBBValueCast(field));
+
+ writer += "builder.add{{method_name}}({{pos}}, \\";
+ writer += "{{field_name}}{{cast}}, {{default}})";
+ });
+ }
+
+ static std::string ToSignedType(const Type & type) {
+ switch(type.base_type) {
+ case BASE_TYPE_UINT:
+ return GenTypeBasic(BASE_TYPE_INT);
+ case BASE_TYPE_ULONG:
+ return GenTypeBasic(BASE_TYPE_LONG);
+ case BASE_TYPE_UCHAR:
+ case BASE_TYPE_NONE:
+ case BASE_TYPE_UTYPE:
+ return GenTypeBasic(BASE_TYPE_CHAR);
+ case BASE_TYPE_USHORT:
+ return GenTypeBasic(BASE_TYPE_SHORT);
+ case BASE_TYPE_VECTOR:
+ return ToSignedType(type.VectorType());
+ default:
+ return GenTypeBasic(type.base_type);
+ }
+ }
+
+ static std::string FlexBufferBuilderCast(const std::string &method,
+ FieldDef &field,
+ bool isFirst) {
+ auto field_type = GenTypeBasic(field.value.type.base_type);
+ std::string to_type;
+ if (method == "Boolean")
+ to_type = "Boolean";
+ else if (method == "Long")
+ to_type = "Long";
+ else if (method == "Int" || method == "Offset" || method == "Struct")
+ to_type = "Int";
+ else if (method == "Byte" || method.empty())
+ to_type = isFirst ? "Byte" : "Int";
+ else if (method == "Short")
+ to_type = isFirst ? "Short" : "Int";
+ else if (method == "Double")
+ to_type = "Double";
+ else if (method == "Float")
+ to_type = isFirst ? "Float" : "Double";
+ else if (method == "UByte")
+
+ if (field_type != to_type)
+ return ".to" + to_type + "()";
+ return "";
+ }
+
+ // fun startMonster(builder: FlatBufferBuilder) = builder.startTable(11)
+ void GenerateStartStructMethod(StructDef &struct_def, CodeWriter &code) const {
+ GenerateFunOneLine(code, "start" + Esc(struct_def.name),
+ "builder: FlatBufferBuilder", "", [&] () {
+ code += "builder.startTable("+ NumToString(struct_def.fields.vec.size()) + ")";
+ });
+ }
+
+ void GenerateTableCreator(StructDef &struct_def, CodeWriter &writer) const {
+ // Generate a method that creates a table in one go. This is only possible
+ // when the table has no struct fields, since those have to be created
+ // inline, and there's no way to do so in Java.
+ bool has_no_struct_fields = true;
+ int num_fields = 0;
+ auto fields_vec = struct_def.fields.vec;
+
+ for (auto it = fields_vec.begin(); it != fields_vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ if (IsStruct(field.value.type)) {
+ has_no_struct_fields = false;
+ } else {
+ num_fields++;
+ }
+ }
+ // JVM specifications restrict default constructor params to be < 255.
+ // Longs and doubles take up 2 units, so we set the limit to be < 127.
+ if (has_no_struct_fields && num_fields && num_fields < 127) {
+ // Generate a table constructor of the form:
+ // public static int createName(FlatBufferBuilder builder, args...)
+
+ auto name = "create" + Esc(struct_def.name);
+ std::stringstream params;
+ params << "builder: FlatBufferBuilder";
+ for (auto it = fields_vec.begin(); it != fields_vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ params << ", " << MakeCamel(Esc(field.name), false);
+ if (!IsScalar(field.value.type.base_type)){
+ params << "Offset: ";
+ } else {
+ params << ": ";
+ }
+ params << GenTypeBasic(field.value.type.base_type);
+ }
+
+ GenerateFun(writer, name, params.str(), "Int", [&]() {
+ writer.SetValue("vec_size", NumToString(fields_vec.size()));
+
+ writer += "builder.startTable({{vec_size}})";
+
+ auto sortbysize = struct_def.sortbysize;
+ auto largest = sortbysize ? sizeof(largest_scalar_t) : 1;
+ for (size_t size = largest; size; size /= 2) {
+ for (auto it = fields_vec.rbegin(); it != fields_vec.rend();
+ ++it) {
+ auto &field = **it;
+ auto base_type_size = SizeOf(field.value.type.base_type);
+ if (!field.deprecated &&
+ (!sortbysize || size == base_type_size)) {
+ writer.SetValue("camel_field_name",
+ MakeCamel(Esc(field.name), true));
+ writer.SetValue("field_name",
+ MakeCamel(Esc(field.name), false));
+
+ writer += "add{{camel_field_name}}(builder, {{field_name}}\\";
+ if (!IsScalar(field.value.type.base_type)){
+ writer += "Offset\\";
+ }
+ writer += ")";
+ }
+ }
+ }
+ writer += "return end{{struct_name}}(builder)";
+ });
+ }
+
+ }
+ void GenerateBufferHasIdentifier(StructDef &struct_def,
+ CodeWriter &writer) const {
+ auto file_identifier = parser_.file_identifier_;
+ // Check if a buffer has the identifier.
+ if (parser_.root_struct_def_ != &struct_def || !file_identifier.length())
+ return;
+ auto name = MakeCamel(Esc(struct_def.name), false);
+ GenerateFunOneLine(writer, name + "BufferHasIdentifier",
+ "_bb: ByteBuffer",
+ "Boolean",
+ [&]() {
+ writer += "__has_identifier(_bb, \"" + file_identifier + "\")";
+ });
+ }
+
+ void GenerateStructGetters(StructDef &struct_def, CodeWriter &writer) const {
+ auto fields_vec = struct_def.fields.vec;
+ FieldDef *key_field = nullptr;
+ for (auto it = fields_vec.begin(); it != fields_vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ if (field.key) key_field = &field;
+
+ GenerateComment(field.doc_comment, writer, &comment_config);
+
+ auto field_name = MakeCamel(Esc(field.name), false);
+ auto field_type = GenTypeGet(field.value.type);
+ auto field_default_value = GenDefaultValue(field);
+ auto return_type = GenTypeGet(field.value.type);
+ auto bbgetter = ByteBufferGetter(field.value.type, "bb");
+ auto ucast = CastToUsigned(field);
+ auto offset_val = NumToString(field.value.offset);
+ auto offset_prefix = "val o = __offset(" + offset_val
+ + "); return o != 0 ? ";
+ auto value_base_type = field.value.type.base_type;
+ // Most field accessors need to retrieve and test the field offset
+ // first, this is the offset value for that:
+ writer.SetValue("offset", NumToString(field.value.offset));
+ writer.SetValue("return_type", return_type);
+ writer.SetValue("field_type", field_type);
+ writer.SetValue("field_name", field_name);
+ writer.SetValue("field_default", field_default_value);
+ writer.SetValue("bbgetter", bbgetter);
+ writer.SetValue("ucast", ucast);
+
+ auto opt_ret_type = return_type + "?";
+ // Generate the accessors that don't do object reuse.
+ if (value_base_type == BASE_TYPE_STRUCT) {
+ // Calls the accessor that takes an accessor object with a
+ // new object.
+ // val pos
+ // get() = pos(Vec3())
+ GenerateGetterOneLine(writer, field_name, opt_ret_type, [&](){
+ writer += "{{field_name}}({{field_type}}())";
+ });
+ } else if (value_base_type == BASE_TYPE_VECTOR &&
+ field.value.type.element == BASE_TYPE_STRUCT) {
+ // Accessors for vectors of structs also take accessor objects,
+ // this generates a variant without that argument.
+ // ex: fun weapons(j: Int) = weapons(Weapon(), j)
+ GenerateFunOneLine(writer, field_name, "j: Int", opt_ret_type, [&](){
+ writer += "{{field_name}}({{return_type}}(), j)";
+ });
+ }
+
+ if (IsScalar(value_base_type)) {
+ if (struct_def.fixed) {
+ GenerateGetterOneLine(writer, field_name, return_type, [&](){
+ writer += "{{bbgetter}}(bb_pos + {{offset}}){{ucast}}";
+ });
+ } else {
+ GenerateGetter(writer, field_name, return_type, [&](){
+ writer += "val o = __offset({{offset}})";
+ writer += "return if(o != 0) {{bbgetter}}"
+ "(o + bb_pos){{ucast}} else "
+ "{{field_default}}";
+ });
+ }
+ } else {
+ switch (value_base_type) {
+ case BASE_TYPE_STRUCT:
+ if (struct_def.fixed) {
+ // create getter with object reuse
+ // ex:
+ // fun pos(obj: Vec3) : Vec3? = obj.__assign(bb_pos + 4, bb)
+ // ? adds nullability annotation
+ GenerateFunOneLine(writer,
+ field_name, "obj: " + field_type ,
+ return_type + "?", [&](){
+ writer += "obj.__assign(bb_pos + {{offset}}, bb)";
+ });
+ } else {
+ // create getter with object reuse
+ // ex:
+ // fun pos(obj: Vec3) : Vec3? {
+ // val o = __offset(4)
+ // return if(o != 0) {
+ // obj.__assign(o + bb_pos, bb)
+ // else {
+ // null
+ // }
+ // }
+ // ? adds nullability annotation
+ GenerateFun(writer, field_name, "obj: " + field_type,
+ return_type + "?", [&](){
+ auto fixed = field.value.type.struct_def->fixed;
+
+ writer.SetValue("seek", Indirect("o + bb_pos", fixed));
+ OffsetWrapper(writer,
+ offset_val,
+ [&]() { writer += "obj.__assign({{seek}}, bb)"; },
+ [&]() { writer += "null"; });
+ });
+ }
+ break;
+ case BASE_TYPE_STRING:
+ // create string getter
+ // e.g.
+ // val Name : String?
+ // get() = {
+ // val o = __offset(10)
+ // return if (o != 0) __string(o + bb_pos) else null
+ // }
+ // ? adds nullability annotation
+ GenerateGetter(writer, field_name, return_type + "?", [&](){
+
+ writer += "val o = __offset({{offset}})";
+ writer += "return if (o != 0) __string(o + bb_pos) else null";
+ });
+ break;
+ case BASE_TYPE_VECTOR: {
+ // e.g.
+ // fun inventory(j: Int) : UByte {
+ // val o = __offset(14)
+ // return if (o != 0) {
+ // bb.get(__vector(o) + j * 1).toUByte()
+ // } else {
+ // 0
+ // }
+ // }
+
+ auto vectortype = field.value.type.VectorType();
+ std::string params = "j: Int";
+ std::string nullable = IsScalar(vectortype.base_type) ? ""
+ : "?";
+
+ if (vectortype.base_type == BASE_TYPE_STRUCT ||
+ vectortype.base_type == BASE_TYPE_UNION) {
+ params = "obj: " + field_type + ", j: Int";
+ }
+
+
+ writer.SetValue("toType", "YYYYY");
+
+ auto ret_type = return_type + nullable;
+ GenerateFun(writer, field_name, params, ret_type, [&](){
+ auto inline_size = NumToString(InlineSize(vectortype));
+ auto index = "__vector(o) + j * " + inline_size;
+ auto not_found = NotFoundReturn(field.value.type.element);
+ auto found = "";
+ writer.SetValue("index", index);
+ switch(vectortype.base_type) {
+ case BASE_TYPE_STRUCT: {
+ bool fixed = vectortype.struct_def->fixed;
+ writer.SetValue("index", Indirect(index, fixed));
+ found = "obj.__assign({{index}}, bb)";
+ break;
+ }
+ case BASE_TYPE_UNION:
+ found = "{{bbgetter}}(obj, {{index}} - bb_pos){{ucast}}";
+ break;
+ default:
+ found = "{{bbgetter}}({{index}}){{ucast}}";
+ }
+ OffsetWrapper(writer, offset_val,
+ [&]() { writer += found; } ,
+ [&]() { writer += not_found; });
+ });
+ break;
+ }
+ case BASE_TYPE_UNION:
+ GenerateFun(writer, field_name, "obj: " + field_type,
+ return_type + "?", [&](){
+ writer += OffsetWrapperOneLine(offset_val,
+ bbgetter + "(obj, o)",
+ "null");
+ });
+ break;
+ default:
+ FLATBUFFERS_ASSERT(0);
+ }
+ }
+
+ if (value_base_type == BASE_TYPE_VECTOR) {
+ // Generate Lenght functions for vectors
+ GenerateGetter(writer, field_name + "Length", "Int", [&](){
+ writer += OffsetWrapperOneLine(offset_val,
+ "__vector_len(o)", "0");
+ });
+
+ // See if we should generate a by-key accessor.
+ if (field.value.type.element == BASE_TYPE_STRUCT &&
+ !field.value.type.struct_def->fixed) {
+ auto &sd = *field.value.type.struct_def;
+ auto &fields = sd.fields.vec;
+ for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
+ auto &kfield = **kit;
+ if (kfield.key) {
+ auto qualified_name = WrapInNameSpace(sd);
+ auto name = MakeCamel(Esc(field.name), false) + "ByKey";
+ auto params = "key: " + GenTypeGet(kfield.value.type);
+ auto rtype = qualified_name + "?";
+ GenerateFun(writer, name, params, rtype, [&] () {
+ OffsetWrapper(writer, offset_val,
+ [&] () {
+ writer += qualified_name +
+ ".__lookup_by_key(null, __vector(o), key, bb)";
+ },
+ [&] () {
+ writer += "null";
+ });
+ });
+
+ auto param2 = "obj: " + qualified_name +
+ ", key: " +
+ GenTypeGet(kfield.value.type);
+ GenerateFun(writer, name, param2, rtype, [&](){
+ OffsetWrapper(writer, offset_val,
+ [&] () {
+ writer += qualified_name +
+ ".__lookup_by_key(obj, __vector(o), key, bb)";
+ },
+ [&]() { writer += "null"; });
+ });
+
+ break;
+ }
+ }
+ }
+ }
+
+ if ((value_base_type == BASE_TYPE_VECTOR &&
+ IsScalar(field.value.type.VectorType().base_type)) ||
+ value_base_type == BASE_TYPE_STRING) {
+
+ auto end_idx = NumToString(value_base_type == BASE_TYPE_STRING
+ ? 1
+ : InlineSize(field.value.type.VectorType()));
+ // Generate a ByteBuffer accessor for strings & vectors of scalars.
+ // e.g.
+ // val inventoryByteBuffer: ByteBuffer
+ // get = __vector_as_bytebuffer(14, 1)
+
+ GenerateGetterOneLine(writer, field_name + "AsByteBuffer",
+ "ByteBuffer", [&](){
+ writer.SetValue("end", end_idx);
+ writer += "__vector_as_bytebuffer({{offset}}, {{end}})";
+ });
+
+ // Generate a ByteBuffer accessor for strings & vectors of scalars.
+ // e.g.
+ // fun inventoryInByteBuffer(_bb: Bytebuffer):
+ // ByteBuffer = __vector_as_bytebuffer(_bb, 14, 1)
+ GenerateFunOneLine(writer, field_name + "InByteBuffer",
+ "_bb: ByteBuffer", "ByteBuffer", [&](){
+ writer.SetValue("end", end_idx);
+ writer += "__vector_in_bytebuffer(_bb, {{offset}}, {{end}})";
+ });
+ }
+
+ // generate object accessors if is nested_flatbuffer
+ //fun testnestedflatbufferAsMonster() : Monster?
+ //{ return testnestedflatbufferAsMonster(new Monster()); }
+
+ if (field.nested_flatbuffer) {
+ auto nested_type_name = WrapInNameSpace(*field.nested_flatbuffer);
+ auto nested_method_name =
+ field_name + "As" +
+ field.nested_flatbuffer->name;
+
+ GenerateGetterOneLine(writer,
+ nested_method_name,
+ nested_type_name + "?", [&](){
+ writer += nested_method_name + "(" + nested_type_name + "())";
+ });
+
+ GenerateFun(writer,
+ nested_method_name,
+ "obj: " + nested_type_name,
+ nested_type_name + "?", [&](){
+ OffsetWrapper(writer, offset_val,
+ [&]() { writer += "obj.__assign(__indirect(__vector(o)), bb)"; },
+ [&]() { writer += "null";});
+ });
+ }
+
+ // Generate mutators for scalar fields or vectors of scalars.
+ if (parser_.opts.mutable_buffer) {
+ auto value_type = field.value.type;
+ auto underlying_type = value_base_type == BASE_TYPE_VECTOR
+ ? value_type.VectorType()
+ : value_type;
+ auto name = "mutate" + MakeCamel(Esc(field.name), true);
+ auto size = NumToString(InlineSize(underlying_type));
+ auto params = Esc(field.name) + ": " + GenTypeGet(underlying_type);
+ // A vector mutator also needs the index of the vector element it should
+ // mutate.
+ if (value_base_type == BASE_TYPE_VECTOR)
+ params.insert(0, "j: Int, ");
+
+ // Boolean parameters have to be explicitly converted to byte
+ // representation.
+ auto setter_parameter = underlying_type.base_type == BASE_TYPE_BOOL
+ ? "(if(" + Esc(field.name) + ") 1 else 0).toByte()"
+ : Esc(field.name);
+
+ auto setter_index = value_base_type == BASE_TYPE_VECTOR
+ ? "__vector(o) + j * " + size
+ : (struct_def.fixed
+ ? "bb_pos + " + offset_val
+ : "o + bb_pos");
+ if (IsScalar(value_base_type) || (value_base_type == BASE_TYPE_VECTOR &&
+ IsScalar(value_type.VectorType().base_type))) {
+
+ auto statements = [&] () {
+ writer.SetValue("bbsetter", ByteBufferSetter(underlying_type));
+ writer.SetValue("index", setter_index);
+ writer.SetValue("params", setter_parameter);
+ writer.SetValue("cast", CastToSigned(field));
+ if (struct_def.fixed) {
+ writer += "{{bbsetter}}({{index}}, {{params}}{{cast}})";
+ } else {
+ OffsetWrapper(writer, offset_val, [&](){
+ writer += "{{bbsetter}}({{index}}, {{params}}{{cast}})";
+ writer += "true";
+ }, [&](){ writer += "false";});
+ }
+ };
+
+ if (struct_def.fixed) {
+ GenerateFunOneLine(writer, name, params, "ByteBuffer",
+ statements);
+ } else {
+ GenerateFun(writer, name, params, "Boolean",
+ statements);
+ }
+ }
+ }
+ }
+ if (struct_def.has_key && !struct_def.fixed) {
+ // Key Comparison method
+ GenerateOverrideFun(
+ writer,
+ "keysCompare",
+ "o1: Int, o2: Int, _bb: ByteBuffer", "Int", [&]() {
+ if (key_field->value.type.base_type == BASE_TYPE_STRING) {
+ writer.SetValue("offset", NumToString(key_field->value.offset));
+ writer += " return compareStrings(__offset({{offset}}, o1, "
+ "_bb), __offset({{offset}}, o2, _bb), _bb)";
+
+ } else {
+ auto getter1 = GenLookupByKey(key_field, "_bb", "o1");
+ auto getter2 = GenLookupByKey(key_field, "_bb", "o2");
+ writer += "val val_1 = " + getter1;
+ writer += "val val_2 = " + getter2;
+ writer += "return (val_1 - val_2).sign";
+ }
+ });
+ }
+ }
+
+ static std::string CastToUsigned(const FieldDef &field) {
+ return CastToUsigned(field.value.type);
+ }
+
+ static std::string CastToUsigned(const Type type) {
+ switch (type.base_type) {
+ case BASE_TYPE_UINT:
+ return ".toUInt()";
+ case BASE_TYPE_UCHAR:
+ case BASE_TYPE_UTYPE:
+ return ".toUByte()";
+ case BASE_TYPE_USHORT:
+ return ".toUShort()";
+ case BASE_TYPE_ULONG:
+ return ".toULong()";
+ case BASE_TYPE_VECTOR:
+ return CastToUsigned(type.VectorType());
+ default:
+ return "";
+ }
+ }
+
+ static std::string CastToSigned(const FieldDef &field) {
+ return CastToSigned(field.value.type);
+ }
+
+ static std::string CastToSigned(const Type type) {
+ switch (type.base_type) {
+ case BASE_TYPE_UINT:
+ return ".toInt()";
+ case BASE_TYPE_UCHAR:
+ case BASE_TYPE_UTYPE:
+ return ".toByte()";
+ case BASE_TYPE_USHORT:
+ return ".toShort()";
+ case BASE_TYPE_ULONG:
+ return ".toLong()";
+ case BASE_TYPE_VECTOR:
+ return CastToSigned(type.VectorType());
+ default:
+ return "";
+ }
+ }
+
+ static std::string LiteralSuffix(const BaseType type) {
+ switch (type) {
+ case BASE_TYPE_UINT:
+ case BASE_TYPE_UCHAR:
+ case BASE_TYPE_UTYPE:
+ case BASE_TYPE_USHORT:
+ return "u";
+ case BASE_TYPE_ULONG:
+ return "UL";
+ case BASE_TYPE_LONG:
+ return "L";
+ default:
+ return "";
+ }
+ }
+
+ void GenerateCompanionObject(CodeWriter &code,
+ const std::function<void()> &callback) const {
+ code += "companion object {";
+ code.IncrementIdentLevel();
+ callback();
+ code.DecrementIdentLevel();
+ code += "}";
+ }
+
+ // Generate a documentation comment, if available.
+ void GenerateComment(const std::vector<std::string> &dc, CodeWriter &writer,
+ const CommentConfig *config) const {
+ if (dc.begin() == dc.end()) {
+ // Don't output empty comment blocks with 0 lines of comment content.
+ return;
+ }
+
+ if (config != nullptr && config->first_line != nullptr) {
+ writer += std::string(config->first_line);
+ }
+ std::string line_prefix =
+ ((config != nullptr && config->content_line_prefix != nullptr)
+ ? config->content_line_prefix
+ : "///");
+ for (auto it = dc.begin(); it != dc.end(); ++it) {
+ writer += line_prefix + *it;
+ }
+ if (config != nullptr && config->last_line != nullptr) {
+ writer += std::string(config->last_line);
+ }
+ }
+
+ static void GenerateGetRootAsAccessors(const std::string &struct_name,
+ CodeWriter &writer) {
+ // Generate a special accessor for the table that when used as the root
+ // ex: fun getRootAsMonster(_bb: ByteBuffer): Monster {...}
+ writer.SetValue("gr_name", struct_name);
+ writer.SetValue("gr_method", "getRootAs" + struct_name);
+
+ // create convenience method that doesn't require an existing object
+ writer += "fun {{gr_method}}(_bb: ByteBuffer): {{gr_name}} = \\";
+ writer += "{{gr_method}}(_bb, {{gr_name}}())";
+
+ // create method that allows object reuse
+ // ex: fun Monster getRootAsMonster(_bb: ByteBuffer, obj: Monster) {...}
+ writer += "fun {{gr_method}}"
+ "(_bb: ByteBuffer, obj: {{gr_name}}): {{gr_name}} {";
+ writer.IncrementIdentLevel();
+ writer += "_bb.order(ByteOrder.LITTLE_ENDIAN)";
+ writer += "return (obj.__assign(_bb.getInt(_bb.position())"
+ " + _bb.position(), _bb))";
+ writer.DecrementIdentLevel();
+ writer += "}";
+ }
+
+ static void GenerateStaticConstructor(const StructDef &struct_def,
+ CodeWriter &code) {
+ // create a struct constructor function
+ auto params = StructConstructorParams(struct_def);
+ GenerateFun(code, "create" + Esc(struct_def.name), params, "Int", [&](){
+ GenStructBody(struct_def, code, "");
+ code += "return builder.offset()";
+ });
+ }
+
+ static std::string StructConstructorParams(const StructDef &struct_def,
+ const std::string &prefix = "") {
+ //builder: FlatBufferBuilder
+ std::stringstream out;
+ auto field_vec = struct_def.fields.vec;
+ if (prefix.empty()) {
+ out << "builder: FlatBufferBuilder";
+ }
+ for (auto it = field_vec.begin(); it != field_vec.end(); ++it) {
+ auto &field = **it;
+ if (IsStruct(field.value.type)) {
+ // Generate arguments for a struct inside a struct. To ensure
+ // names don't clash, and to make it obvious these arguments are
+ // constructing a nested struct, prefix the name with the field
+ // name.
+ out << StructConstructorParams(*field.value.type.struct_def,
+ prefix + (Esc(field.name) + "_"));
+ } else {
+ out << ", " << prefix << MakeCamel(Esc(field.name), false)
+ << ": "
+ << GenTypeBasic(field.value.type.base_type);
+ }
+ }
+ return out.str();
+ }
+
+ static void GeneratePropertyOneLine(CodeWriter &writer,
+ const std::string &name,
+ const std::string &type,
+ const std::function<void()> &body) {
+ // Generates Kotlin getter for properties
+ // e.g.:
+ // val prop: Mytype = x
+ writer.SetValue("_name", name);
+ writer.SetValue("_type", type);
+ writer += "val {{_name}} : {{_type}} = \\";
+ body();
+ }
+ static void GenerateGetterOneLine(CodeWriter &writer,
+ const std::string &name,
+ const std::string &type,
+ const std::function<void()> &body) {
+ // Generates Kotlin getter for properties
+ // e.g.:
+ // val prop: Mytype get() = x
+ writer.SetValue("_name", name);
+ writer.SetValue("_type", type);
+ writer += "val {{_name}} : {{_type}} get() = \\";
+ body();
+ }
+
+ static void GenerateGetter(CodeWriter &writer,
+ const std::string &name,
+ const std::string &type,
+ const std::function<void()> &body) {
+ // Generates Kotlin getter for properties
+ // e.g.:
+ // val prop: Mytype
+ // get() = {
+ // return x
+ // }
+ writer.SetValue("name", name);
+ writer.SetValue("type", type);
+ writer += "val {{name}} : {{type}}";
+ writer.IncrementIdentLevel();
+ writer += "get() {";
+ writer.IncrementIdentLevel();
+ body();
+ writer.DecrementIdentLevel();
+ writer += "}";
+ writer.DecrementIdentLevel();
+ }
+
+ static void GenerateFun(CodeWriter &writer,
+ const std::string &name,
+ const std::string ¶ms,
+ const std::string &returnType,
+ const std::function<void()> &body) {
+ // Generates Kotlin function
+ // e.g.:
+ // fun path(j: Int): Vec3 {
+ // return path(Vec3(), j)
+ // }
+ auto noreturn = returnType.empty();
+ writer.SetValue("name", name);
+ writer.SetValue("params", params);
+ writer.SetValue("return_type", noreturn ? "" : ": " + returnType);
+ writer += "fun {{name}}({{params}}) {{return_type}} {";
+ writer.IncrementIdentLevel();
+ body();
+ writer.DecrementIdentLevel();
+ writer += "}";
+ }
+
+ static void GenerateFunOneLine(CodeWriter &writer,
+ const std::string &name,
+ const std::string ¶ms,
+ const std::string &returnType,
+ const std::function<void()> &body) {
+ // Generates Kotlin function
+ // e.g.:
+ // fun path(j: Int): Vec3 = return path(Vec3(), j)
+ writer.SetValue("name", name);
+ writer.SetValue("params", params);
+ writer.SetValue("return_type_p", returnType.empty() ? "" :
+ " : " + returnType);
+ writer += "fun {{name}}({{params}}){{return_type_p}} = \\";
+ body();
+ }
+
+ static void GenerateOverrideFun(CodeWriter &writer,
+ const std::string &name,
+ const std::string ¶ms,
+ const std::string &returnType,
+ const std::function<void()> &body) {
+ // Generates Kotlin function
+ // e.g.:
+ // override fun path(j: Int): Vec3 = return path(Vec3(), j)
+ writer += "override \\";
+ GenerateFun(writer, name, params, returnType, body);
+ }
+
+ static void GenerateOverrideFunOneLine(CodeWriter &writer,
+ const std::string &name,
+ const std::string ¶ms,
+ const std::string &returnType,
+ const std::string &statement) {
+ // Generates Kotlin function
+ // e.g.:
+ // override fun path(j: Int): Vec3 = return path(Vec3(), j)
+ writer.SetValue("name", name);
+ writer.SetValue("params", params);
+ writer.SetValue("return_type", returnType.empty() ? "" :
+ " : " + returnType);
+ writer += "override fun {{name}}({{params}}){{return_type}} = \\";
+ writer += statement;
+ }
+
+ static std::string OffsetWrapperOneLine(const std::string &offset,
+ const std::string &found,
+ const std::string ¬_found) {
+ return "val o = __offset(" + offset + "); return if (o != 0) " + found +
+ " else " + not_found;
+ }
+
+ static void OffsetWrapper(CodeWriter &code,
+ const std::string &offset,
+ const std::function<void()> &found,
+ const std::function<void()> ¬_found) {
+ code += "val o = __offset(" + offset + ")";
+ code +="return if (o != 0) {";
+ code.IncrementIdentLevel();
+ found();
+ code.DecrementIdentLevel();
+ code += "} else {";
+ code.IncrementIdentLevel();
+ not_found();
+ code.DecrementIdentLevel();
+ code += "}";
+ }
+
+ static std::string Indirect(const std::string &index, bool fixed) {
+ // We apply __indirect() and struct is not fixed.
+ if (!fixed)
+ return "__indirect(" + index + ")";
+ return index;
+ }
+
+ static std::string NotFoundReturn(BaseType el) {
+ switch (el) {
+ case BASE_TYPE_FLOAT:
+ return "0.0f";
+ case BASE_TYPE_DOUBLE:
+ return "0.0";
+ case BASE_TYPE_BOOL:
+ return "false";
+ case BASE_TYPE_LONG:
+ case BASE_TYPE_INT:
+ case BASE_TYPE_CHAR:
+ case BASE_TYPE_SHORT:
+ return "0";
+ case BASE_TYPE_UINT:
+ case BASE_TYPE_UCHAR:
+ case BASE_TYPE_USHORT:
+ case BASE_TYPE_UTYPE:
+ return "0u";
+ case BASE_TYPE_ULONG:
+ return "0uL";
+ default:
+ return "null";
+ }
+ }
+
+ // This tracks the current namespace used to determine if a type need to be
+ // prefixed by its namespace
+ const Namespace *cur_name_space_;
+};
+} // namespace kotlin
+
+bool GenerateKotlin(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ kotlin::KotlinGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+} // namespace flatbuffers
diff --git a/src/idl_gen_lobster.cpp b/src/idl_gen_lobster.cpp
new file mode 100644
index 0000000..ef9e474
--- /dev/null
+++ b/src/idl_gen_lobster.cpp
@@ -0,0 +1,383 @@
+/*
+ * Copyright 2018 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 <string>
+#include <unordered_set>
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+namespace lobster {
+
+class LobsterGenerator : public BaseGenerator {
+ public:
+ LobsterGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "" /* not used */, "_") {
+ static const char * const keywords[] = {
+ "nil", "true", "false", "return", "struct", "class", "import", "int",
+ "float", "string", "any", "def", "is", "from", "program", "private",
+ "coroutine", "resource", "enum", "typeof", "var", "let", "pakfile",
+ "switch", "case", "default", "namespace", "not", "and", "or", "bool",
+ };
+ keywords_.insert(std::begin(keywords), std::end(keywords));
+ }
+
+ std::string EscapeKeyword(const std::string &name) const {
+ return keywords_.find(name) == keywords_.end() ? name : name + "_";
+ }
+
+ std::string NormalizedName(const Definition &definition) const {
+ return EscapeKeyword(definition.name);
+ }
+
+ std::string NormalizedName(const EnumVal &ev) const {
+ return EscapeKeyword(ev.name);
+ }
+
+ std::string NamespacedName(const Definition &def) {
+ return WrapInNameSpace(def.defined_namespace, NormalizedName(def));
+ }
+
+ std::string GenTypeName(const Type &type) {
+ auto bits = NumToString(SizeOf(type.base_type) * 8);
+ if (IsInteger(type.base_type)) return "int" + bits;
+ if (IsFloat(type.base_type)) return "float" + bits;
+ if (type.base_type == BASE_TYPE_STRING) return "string";
+ if (type.base_type == BASE_TYPE_STRUCT) return "table";
+ return "none";
+ }
+
+ std::string LobsterType(const Type &type) {
+ if (IsFloat(type.base_type)) return "float";
+ if (IsScalar(type.base_type) && type.enum_def) return NormalizedName(*type.enum_def);
+ if (!IsScalar(type.base_type)) return "flatbuffers_offset";
+ return "int";
+ }
+
+ // Returns the method name for use with add/put calls.
+ std::string GenMethod(const Type &type) {
+ return IsScalar(type.base_type)
+ ? MakeCamel(GenTypeBasic(type))
+ : (IsStruct(type) ? "Struct" : "UOffsetTRelative");
+ }
+
+ // This uses Python names for now..
+ std::string GenTypeBasic(const Type &type) {
+ static const char *ctypename[] = {
+ // clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ #PTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ };
+ return ctypename[type.base_type];
+ }
+
+ // Generate a struct field, conditioned on its child type(s).
+ void GenStructAccessor(const StructDef &struct_def,
+ const FieldDef &field, std::string *code_ptr) {
+ GenComment(field.doc_comment, code_ptr, nullptr, " ");
+ std::string &code = *code_ptr;
+ auto offsets = NumToString(field.value.offset);
+ auto def = " def " + NormalizedName(field);
+ if (IsScalar(field.value.type.base_type)) {
+ std::string acc;
+ if (struct_def.fixed) {
+ acc = "buf_.read_" + GenTypeName(field.value.type) +
+ "_le(pos_ + " + offsets + ")";
+
+ } else {
+ acc = "buf_.flatbuffers_field_" +
+ GenTypeName(field.value.type) + "(pos_, " + offsets + ", " +
+ field.value.constant + ")";
+ }
+ if (field.value.type.enum_def)
+ acc = NormalizedName(*field.value.type.enum_def) + "(" + acc + ")";
+ code += def + "():\n return " + acc + "\n";
+ return;
+ }
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_STRUCT: {
+ auto name = NamespacedName(*field.value.type.struct_def);
+ code += def + "():\n ";
+ if (struct_def.fixed) {
+ code += "return " + name + "{ buf_, pos_ + " + offsets + " }\n";
+ } else {
+ code += std::string("let o = buf_.flatbuffers_field_") +
+ (field.value.type.struct_def->fixed ? "struct" : "table") +
+ "(pos_, " + offsets + ")\n return if o: " + name +
+ " { buf_, o } else: nil\n";
+ }
+ break;
+ }
+ case BASE_TYPE_STRING:
+ code += def + "():\n return buf_.flatbuffers_field_string(pos_, " +
+ offsets + ")\n";
+ break;
+ case BASE_TYPE_VECTOR: {
+ auto vectortype = field.value.type.VectorType();
+ code += def + "(i:int):\n return ";
+ if (vectortype.base_type == BASE_TYPE_STRUCT) {
+ auto start = "buf_.flatbuffers_field_vector(pos_, " + offsets +
+ ") + i * " + NumToString(InlineSize(vectortype));
+ if (!(vectortype.struct_def->fixed)) {
+ start = "buf_.flatbuffers_indirect(" + start + ")";
+ }
+ code += NamespacedName(*field.value.type.struct_def) + " { buf_, " +
+ start + " }\n";
+ } else {
+ if (vectortype.base_type == BASE_TYPE_STRING)
+ code += "buf_.flatbuffers_string";
+ else
+ code += "buf_.read_" + GenTypeName(vectortype) + "_le";
+ code += "(buf_.flatbuffers_field_vector(pos_, " + offsets +
+ ") + i * " + NumToString(InlineSize(vectortype)) + ")\n";
+ }
+ break;
+ }
+ case BASE_TYPE_UNION: {
+ for (auto it = field.value.type.enum_def->Vals().begin();
+ it != field.value.type.enum_def->Vals().end(); ++it) {
+ auto &ev = **it;
+ if (ev.IsNonZero()) {
+ code += def + "_as_" + ev.name + "():\n return " +
+ NamespacedName(*ev.union_type.struct_def) +
+ " { buf_, buf_.flatbuffers_field_table(pos_, " + offsets +
+ ") }\n";
+ }
+ }
+ break;
+ }
+ default: FLATBUFFERS_ASSERT(0);
+ }
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ code += def +
+ "_length():\n return buf_.flatbuffers_field_vector_len(pos_, " +
+ offsets + ")\n";
+ }
+ }
+
+ // Generate table constructors, conditioned on its members' types.
+ void GenTableBuilders(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "struct " + NormalizedName(struct_def) +
+ "Builder:\n b_:flatbuffers_builder\n";
+ code += " def start():\n b_.StartObject(" +
+ NumToString(struct_def.fields.vec.size()) + ")\n return this\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ auto offset = it - struct_def.fields.vec.begin();
+ code += " def add_" + NormalizedName(field) + "(" +
+ NormalizedName(field) + ":" + LobsterType(field.value.type) +
+ "):\n b_.Prepend" + GenMethod(field.value.type) + "Slot(" +
+ NumToString(offset) + ", " + NormalizedName(field);
+ if (IsScalar(field.value.type.base_type))
+ code += ", " + field.value.constant;
+ code += ")\n return this\n";
+ }
+ code += " def end():\n return b_.EndObject()\n\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ code += "def " + NormalizedName(struct_def) + "Start" +
+ MakeCamel(NormalizedName(field)) +
+ "Vector(b_:flatbuffers_builder, n_:int):\n b_.StartVector(";
+ auto vector_type = field.value.type.VectorType();
+ auto alignment = InlineAlignment(vector_type);
+ auto elem_size = InlineSize(vector_type);
+ code += NumToString(elem_size) + ", n_, " + NumToString(alignment) +
+ ")\n";
+ if (vector_type.base_type != BASE_TYPE_STRUCT ||
+ !vector_type.struct_def->fixed) {
+ code += "def " + NormalizedName(struct_def) + "Create" +
+ MakeCamel(NormalizedName(field)) +
+ "Vector(b_:flatbuffers_builder, v_:[" +
+ LobsterType(vector_type) + "]):\n b_.StartVector(" +
+ NumToString(elem_size) + ", v_.length, " +
+ NumToString(alignment) +
+ ")\n reverse(v_) e_: b_.Prepend" +
+ GenMethod(vector_type) +
+ "(e_)\n return b_.EndVector(v_.length)\n";
+ }
+ code += "\n";
+ }
+ }
+ }
+
+ void GenStructPreDecl(const StructDef &struct_def, std::string *code_ptr) {
+ if (struct_def.generated) return;
+ std::string &code = *code_ptr;
+ CheckNameSpace(struct_def, &code);
+ code += "class " + NormalizedName(struct_def) + "\n\n";
+ }
+
+ // Generate struct or table methods.
+ void GenStruct(const StructDef &struct_def, std::string *code_ptr) {
+ if (struct_def.generated) return;
+ std::string &code = *code_ptr;
+ CheckNameSpace(struct_def, &code);
+ GenComment(struct_def.doc_comment, code_ptr, nullptr, "");
+ code += "class " + NormalizedName(struct_def) + " : flatbuffers_handle\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+ GenStructAccessor(struct_def, field, code_ptr);
+ }
+ code += "\n";
+ if (!struct_def.fixed) {
+ // Generate a special accessor for the table that has been declared as
+ // the root type.
+ code += "def GetRootAs" + NormalizedName(struct_def) + "(buf:string): return " +
+ NormalizedName(struct_def) +
+ " { buf, buf.flatbuffers_indirect(0) }\n\n";
+ }
+ if (struct_def.fixed) {
+ // create a struct constructor function
+ GenStructBuilder(struct_def, code_ptr);
+ } else {
+ // Create a set of functions that allow table construction.
+ GenTableBuilders(struct_def, code_ptr);
+ }
+ }
+
+ // Generate enum declarations.
+ void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
+ if (enum_def.generated) return;
+ std::string &code = *code_ptr;
+ CheckNameSpace(enum_def, &code);
+ GenComment(enum_def.doc_comment, code_ptr, nullptr, "");
+ code += "enum " + NormalizedName(enum_def) + ":\n";
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ auto &ev = **it;
+ GenComment(ev.doc_comment, code_ptr, nullptr, " ");
+ code += " " + enum_def.name + "_" + NormalizedName(ev) + " = " +
+ enum_def.ToString(ev) + "\n";
+ }
+ code += "\n";
+ }
+
+ // Recursively generate arguments for a constructor, to deal with nested
+ // structs.
+ void StructBuilderArgs(const StructDef &struct_def,
+ const char *nameprefix, std::string *code_ptr) {
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (IsStruct(field.value.type)) {
+ // Generate arguments for a struct inside a struct. To ensure names
+ // don't clash, and to make it obvious these arguments are constructing
+ // a nested struct, prefix the name with the field name.
+ StructBuilderArgs(*field.value.type.struct_def,
+ (nameprefix + (NormalizedName(field) + "_")).c_str(), code_ptr);
+ } else {
+ std::string &code = *code_ptr;
+ code += ", " + (nameprefix + NormalizedName(field)) + ":" +
+ LobsterType(field.value.type);
+ }
+ }
+ }
+
+ // Recursively generate struct construction statements and instert manual
+ // padding.
+ void StructBuilderBody(const StructDef &struct_def,
+ const char *nameprefix, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += " b_.Prep(" + NumToString(struct_def.minalign) + ", " +
+ NumToString(struct_def.bytesize) + ")\n";
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ auto &field = **it;
+ if (field.padding)
+ code += " b_.Pad(" + NumToString(field.padding) + ")\n";
+ if (IsStruct(field.value.type)) {
+ StructBuilderBody(*field.value.type.struct_def,
+ (nameprefix + (NormalizedName(field) + "_")).c_str(), code_ptr);
+ } else {
+ code += " b_.Prepend" + GenMethod(field.value.type) + "(" +
+ nameprefix + NormalizedName(field) + ")\n";
+ }
+ }
+ }
+
+ // Create a struct with a builder and the struct's arguments.
+ void GenStructBuilder(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "def Create" + NormalizedName(struct_def) +
+ "(b_:flatbuffers_builder";
+ StructBuilderArgs(struct_def, "", code_ptr);
+ code += "):\n";
+ StructBuilderBody(struct_def, "", code_ptr);
+ code += " return b_.Offset()\n\n";
+ }
+
+ void CheckNameSpace(const Definition &def, std::string *code_ptr) {
+ auto ns = GetNameSpace(def);
+ if (ns == current_namespace_) return;
+ current_namespace_ = ns;
+ std::string &code = *code_ptr;
+ code += "namespace " + ns + "\n\n";
+ }
+
+ bool generate() {
+ std::string code;
+ code += std::string("// ") + FlatBuffersGeneratedWarning() +
+ "\nimport flatbuffers\n\n";
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ auto &enum_def = **it;
+ GenEnum(enum_def, &code);
+ }
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ auto &struct_def = **it;
+ GenStructPreDecl(struct_def, &code);
+ }
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ auto &struct_def = **it;
+ GenStruct(struct_def, &code);
+ }
+ return SaveFile((path_ + file_name_ + "_generated.lobster").c_str(),
+ code, false);
+ }
+
+ private:
+ std::unordered_set<std::string> keywords_;
+ std::string current_namespace_;
+};
+
+} // namespace lobster
+
+bool GenerateLobster(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ lobster::LobsterGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+
+} // namespace flatbuffers
diff --git a/src/idl_gen_lua.cpp b/src/idl_gen_lua.cpp
new file mode 100644
index 0000000..10df231
--- /dev/null
+++ b/src/idl_gen_lua.cpp
@@ -0,0 +1,731 @@
+/*
+ * 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.
+ */
+
+ // independent from idl_parser, since this code is not needed for most clients
+
+#include <string>
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+#include <unordered_set>
+
+namespace flatbuffers {
+namespace lua {
+
+ // Hardcode spaces per indentation.
+ const CommentConfig def_comment = { nullptr, "--", nullptr };
+ const char * Indent = " ";
+ const char * Comment = "-- ";
+ const char * End = "end\n";
+ const char * EndFunc = "end\n";
+ const char * SelfData = "self.view";
+ const char * SelfDataPos = "self.view.pos";
+ const char * SelfDataBytes = "self.view.bytes";
+
+ class LuaGenerator : public BaseGenerator {
+ public:
+ LuaGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "" /* not used */,
+ "" /* not used */) {
+ static const char * const keywords[] = {
+ "and",
+ "break",
+ "do",
+ "else",
+ "elseif",
+ "end",
+ "false",
+ "for",
+ "function",
+ "goto",
+ "if",
+ "in",
+ "local",
+ "nil",
+ "not",
+ "or",
+ "repeat",
+ "return",
+ "then",
+ "true",
+ "until",
+ "while"
+ };
+ keywords_.insert(std::begin(keywords), std::end(keywords));
+ }
+
+ // Most field accessors need to retrieve and test the field offset first,
+ // this is the prefix code for that.
+ std::string OffsetPrefix(const FieldDef &field) {
+ return std::string(Indent) +
+ "local o = " + SelfData + ":Offset(" + NumToString(field.value.offset) + ")\n" +
+ Indent + "if o ~= 0 then\n";
+ }
+
+ // Begin a class declaration.
+ void BeginClass(const StructDef &struct_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "local " + NormalizedName(struct_def) + " = {} -- the module\n";
+ code += "local " + NormalizedMetaName(struct_def) + " = {} -- the class metatable\n";
+ code += "\n";
+ }
+
+ // Begin enum code with a class declaration.
+ void BeginEnum(const std::string &class_name, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "local " + class_name + " = {\n";
+ }
+
+ std::string EscapeKeyword(const std::string &name) const {
+ return keywords_.find(name) == keywords_.end() ? name : "_" + name;
+ }
+
+ std::string NormalizedName(const Definition &definition) const {
+ return EscapeKeyword(definition.name);
+ }
+
+ std::string NormalizedName(const EnumVal &ev) const {
+ return EscapeKeyword(ev.name);
+ }
+
+ std::string NormalizedMetaName(const Definition &definition) const {
+ return EscapeKeyword(definition.name) + "_mt";
+ }
+
+ // A single enum member.
+ void EnumMember(const EnumDef &enum_def, const EnumVal &ev, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += std::string(Indent) + NormalizedName(ev) + " = " +
+ enum_def.ToString(ev) + ",\n";
+ }
+
+ // End enum code.
+ void EndEnum(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "}\n";
+ }
+
+ void GenerateNewObjectPrototype(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += "function " + NormalizedName(struct_def) + ".New()\n";
+ code += std::string(Indent) + "local o = {}\n";
+ code += std::string(Indent) + "setmetatable(o, {__index = " + NormalizedMetaName(struct_def) + "})\n";
+ code += std::string(Indent) + "return o\n";
+ code += EndFunc;
+ }
+
+ // Initialize a new struct or table from existing data.
+ void NewRootTypeFromBuffer(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += "function " + NormalizedName(struct_def) + ".GetRootAs" + NormalizedName(struct_def) + "(buf, offset)\n";
+ code += std::string(Indent) + "local n = flatbuffers.N.UOffsetT:Unpack(buf, offset)\n";
+ code += std::string(Indent) + "local o = " + NormalizedName(struct_def) + ".New()\n";
+ code += std::string(Indent) + "o:Init(buf, n + offset)\n";
+ code += std::string(Indent) + "return o\n";
+ code += EndFunc;
+ }
+
+ // Initialize an existing object with other data, to avoid an allocation.
+ void InitializeExisting(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ GenReceiver(struct_def, code_ptr);
+ code += "Init(buf, pos)\n";
+ code += std::string(Indent) + SelfData + " = flatbuffers.view.New(buf, pos)\n";
+ code += EndFunc;
+ }
+
+ // Get the length of a vector.
+ void GetVectorLen(const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field)) + "Length()\n";
+ code += OffsetPrefix(field);
+ code += std::string(Indent) + Indent + "return " + SelfData + ":VectorLen(o)\n";
+ code += std::string(Indent) + End;
+ code += std::string(Indent) + "return 0\n";
+ code += EndFunc;
+ }
+
+ // Get the value of a struct's scalar.
+ void GetScalarFieldOfStruct(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ std::string getter = GenGetter(field.value.type);
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "()\n";
+ code += std::string(Indent) + "return " + getter;
+ code += std::string(SelfDataPos) + " + " + NumToString(field.value.offset) + ")\n";
+ code += EndFunc;
+ }
+
+ // Get the value of a table's scalar.
+ void GetScalarFieldOfTable(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ std::string getter = GenGetter(field.value.type);
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "()\n";
+ code += OffsetPrefix(field);
+ getter += std::string("o + ") + SelfDataPos + ")";
+ auto is_bool = field.value.type.base_type == BASE_TYPE_BOOL;
+ if (is_bool) {
+ getter = "(" + getter + " ~= 0)";
+ }
+ code += std::string(Indent) + Indent + "return " + getter + "\n";
+ code += std::string(Indent) + End;
+ std::string default_value;
+ if (is_bool) {
+ default_value = field.value.constant == "0" ? "false" : "true";
+ }
+ else {
+ default_value = field.value.constant;
+ }
+ code += std::string(Indent) + "return " + default_value + "\n";
+ code += EndFunc;
+ }
+
+ // Get a struct by initializing an existing struct.
+ // Specific to Struct.
+ void GetStructFieldOfStruct(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "(obj)\n";
+ code += std::string(Indent) + "obj:Init(" + SelfDataBytes + ", " + SelfDataPos + " + ";
+ code += NumToString(field.value.offset) + ")\n";
+ code += std::string(Indent) + "return obj\n";
+ code += EndFunc;
+ }
+
+ // Get a struct by initializing an existing struct.
+ // Specific to Table.
+ void GetStructFieldOfTable(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "()\n";
+ code += OffsetPrefix(field);
+ if (field.value.type.struct_def->fixed) {
+ code += std::string(Indent) + Indent + "local x = o + " + SelfDataPos + "\n";
+ }
+ else {
+ code += std::string(Indent) + Indent + "local x = " + SelfData + ":Indirect(o + " + SelfDataPos + ")\n";
+ }
+ code += std::string(Indent) + Indent + "local obj = require('" + TypeNameWithNamespace(field) + "').New()\n";
+ code += std::string(Indent) + Indent + "obj:Init(" + SelfDataBytes + ", x)\n";
+ code += std::string(Indent) + Indent + "return obj\n";
+ code += std::string(Indent) + End;
+ code += EndFunc;
+ }
+
+ // Get the value of a string.
+ void GetStringField(const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "()\n";
+ code += OffsetPrefix(field);
+ code += std::string(Indent) + Indent + "return " + GenGetter(field.value.type);
+ code += std::string("o + ") + SelfDataPos + ")\n";
+ code += std::string(Indent) + End;
+ code += EndFunc;
+ }
+
+ // Get the value of a union from an object.
+ void GetUnionField(const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field)) + "()\n";
+ code += OffsetPrefix(field);
+
+ // TODO(rw): this works and is not the good way to it:
+ //bool is_native_table = TypeName(field) == "*flatbuffers.Table";
+ //if (is_native_table) {
+ // code += std::string(Indent) + Indent + "from flatbuffers.table import Table\n";
+ //} else {
+ // code += std::string(Indent) + Indent +
+ // code += "from ." + TypeName(field) + " import " + TypeName(field) + "\n";
+ //}
+ code += std::string(Indent) + Indent + "local obj = flatbuffers.view.New(require('flatbuffers.binaryarray').New(0), 0)\n";
+ code += std::string(Indent) + Indent + GenGetter(field.value.type) + "obj, o)\n";
+ code += std::string(Indent) + Indent + "return obj\n";
+ code += std::string(Indent) + End;
+ code += EndFunc;
+ }
+
+ // Get the value of a vector's struct member.
+ void GetMemberOfVectorOfStruct(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ auto vectortype = field.value.type.VectorType();
+
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "(j)\n";
+ code += OffsetPrefix(field);
+ code += std::string(Indent) + Indent + "local x = " + SelfData + ":Vector(o)\n";
+ code += std::string(Indent) + Indent + "x = x + ((j-1) * ";
+ code += NumToString(InlineSize(vectortype)) + ")\n";
+ if (!(vectortype.struct_def->fixed)) {
+ code += std::string(Indent) + Indent + "x = " + SelfData + ":Indirect(x)\n";
+ }
+ code += std::string(Indent) + Indent + "local obj = require('" + TypeNameWithNamespace(field) + "').New()\n";
+ code += std::string(Indent) + Indent + "obj:Init(" + SelfDataBytes + ", x)\n";
+ code += std::string(Indent) + Indent + "return obj\n";
+ code += std::string(Indent) + End;
+ code += EndFunc;
+ }
+
+ // Get the value of a vector's non-struct member. Uses a named return
+ // argument to conveniently set the zero value for the result.
+ void GetMemberOfVectorOfNonStruct(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ auto vectortype = field.value.type.VectorType();
+
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "(j)\n";
+ code += OffsetPrefix(field);
+ code += std::string(Indent) + Indent + "local a = " + SelfData + ":Vector(o)\n";
+ code += std::string(Indent) + Indent;
+ code += "return " + GenGetter(field.value.type);
+ code += "a + ((j-1) * ";
+ code += NumToString(InlineSize(vectortype)) + "))\n";
+ code += std::string(Indent) + End;
+ if (vectortype.base_type == BASE_TYPE_STRING) {
+ code += std::string(Indent) + "return ''\n";
+ }
+ else {
+ code += std::string(Indent) + "return 0\n";
+ }
+ code += EndFunc;
+ }
+
+ // Begin the creator function signature.
+ void BeginBuilderArgs(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += "function " + NormalizedName(struct_def) + ".Create" + NormalizedName(struct_def);
+ code += "(builder";
+ }
+
+ // Recursively generate arguments for a constructor, to deal with nested
+ // structs.
+ void StructBuilderArgs(const StructDef &struct_def,
+ const char *nameprefix, std::string *code_ptr) {
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (IsStruct(field.value.type)) {
+ // Generate arguments for a struct inside a struct. To ensure names
+ // don't clash, and to make it obvious these arguments are constructing
+ // a nested struct, prefix the name with the field name.
+ StructBuilderArgs(*field.value.type.struct_def,
+ (nameprefix + (NormalizedName(field) + "_")).c_str(), code_ptr);
+ }
+ else {
+ std::string &code = *code_ptr;
+ code += std::string(", ") + nameprefix;
+ code += MakeCamel(NormalizedName(field), false);
+ }
+ }
+ }
+
+ // End the creator function signature.
+ void EndBuilderArgs(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += ")\n";
+ }
+
+ // Recursively generate struct construction statements and instert manual
+ // padding.
+ void StructBuilderBody(const StructDef &struct_def,
+ const char *nameprefix, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += std::string(Indent) + "builder:Prep(" + NumToString(struct_def.minalign) + ", ";
+ code += NumToString(struct_def.bytesize) + ")\n";
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ auto &field = **it;
+ if (field.padding)
+ code += std::string(Indent) + "builder:Pad(" + NumToString(field.padding) + ")\n";
+ if (IsStruct(field.value.type)) {
+ StructBuilderBody(*field.value.type.struct_def,
+ (nameprefix + (NormalizedName(field) + "_")).c_str(), code_ptr);
+ }
+ else {
+ code += std::string(Indent) + "builder:Prepend" + GenMethod(field) + "(";
+ code += nameprefix + MakeCamel(NormalizedName(field), false) + ")\n";
+ }
+ }
+ }
+
+ void EndBuilderBody(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += std::string(Indent) + "return builder:Offset()\n";
+ code += EndFunc;
+ }
+
+ // Get the value of a table's starting offset.
+ void GetStartOfTable(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "function " + NormalizedName(struct_def) + ".Start";
+ code += "(builder) ";
+ code += "builder:StartObject(";
+ code += NumToString(struct_def.fields.vec.size());
+ code += ") end\n";
+ }
+
+ // Set the value of a table's field.
+ void BuildFieldOfTable(const StructDef &struct_def,
+ const FieldDef &field, const size_t offset,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "function " + NormalizedName(struct_def) + ".Add" + MakeCamel(NormalizedName(field));
+ code += "(builder, ";
+ code += MakeCamel(NormalizedName(field), false);
+ code += ") ";
+ code += "builder:Prepend";
+ code += GenMethod(field) + "Slot(";
+ code += NumToString(offset) + ", ";
+ // todo: i don't need to cast in Lua, but am I missing something?
+ // if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) {
+ // code += "flatbuffers.N.UOffsetTFlags.py_type";
+ // code += "(";
+ // code += MakeCamel(NormalizedName(field), false) + ")";
+ // } else {
+ code += MakeCamel(NormalizedName(field), false);
+ // }
+ code += ", " + field.value.constant;
+ code += ") end\n";
+ }
+
+ // Set the value of one of the members of a table's vector.
+ void BuildVectorOfTable(const StructDef &struct_def,
+ const FieldDef &field, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "function " + NormalizedName(struct_def) + ".Start";
+ code += MakeCamel(NormalizedName(field));
+ code += "Vector(builder, numElems) return builder:StartVector(";
+ auto vector_type = field.value.type.VectorType();
+ auto alignment = InlineAlignment(vector_type);
+ auto elem_size = InlineSize(vector_type);
+ code += NumToString(elem_size);
+ code += ", numElems, " + NumToString(alignment);
+ code += ") end\n";
+ }
+
+ // Get the offset of the end of a table.
+ void GetEndOffsetOnTable(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "function " + NormalizedName(struct_def) + ".End";
+ code += "(builder) ";
+ code += "return builder:EndObject() end\n";
+ }
+
+ // Generate the receiver for function signatures.
+ void GenReceiver(const StructDef &struct_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "function " + NormalizedMetaName(struct_def) + ":";
+ }
+
+ // Generate a struct field, conditioned on its child type(s).
+ void GenStructAccessor(const StructDef &struct_def,
+ const FieldDef &field, std::string *code_ptr) {
+ GenComment(field.doc_comment, code_ptr, &def_comment);
+ if (IsScalar(field.value.type.base_type)) {
+ if (struct_def.fixed) {
+ GetScalarFieldOfStruct(struct_def, field, code_ptr);
+ }
+ else {
+ GetScalarFieldOfTable(struct_def, field, code_ptr);
+ }
+ }
+ else {
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_STRUCT:
+ if (struct_def.fixed) {
+ GetStructFieldOfStruct(struct_def, field, code_ptr);
+ }
+ else {
+ GetStructFieldOfTable(struct_def, field, code_ptr);
+ }
+ break;
+ case BASE_TYPE_STRING: GetStringField(struct_def, field, code_ptr); break;
+ case BASE_TYPE_VECTOR: {
+ auto vectortype = field.value.type.VectorType();
+ if (vectortype.base_type == BASE_TYPE_STRUCT) {
+ GetMemberOfVectorOfStruct(struct_def, field, code_ptr);
+ }
+ else {
+ GetMemberOfVectorOfNonStruct(struct_def, field, code_ptr);
+ }
+ break;
+ }
+ case BASE_TYPE_UNION: GetUnionField(struct_def, field, code_ptr); break;
+ default: FLATBUFFERS_ASSERT(0);
+ }
+ }
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ GetVectorLen(struct_def, field, code_ptr);
+ }
+ }
+
+ // Generate table constructors, conditioned on its members' types.
+ void GenTableBuilders(const StructDef &struct_def,
+ std::string *code_ptr) {
+ GetStartOfTable(struct_def, code_ptr);
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+
+ auto offset = it - struct_def.fields.vec.begin();
+ BuildFieldOfTable(struct_def, field, offset, code_ptr);
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ BuildVectorOfTable(struct_def, field, code_ptr);
+ }
+ }
+
+ GetEndOffsetOnTable(struct_def, code_ptr);
+ }
+
+ // Generate struct or table methods.
+ void GenStruct(const StructDef &struct_def, std::string *code_ptr) {
+ if (struct_def.generated) return;
+
+ GenComment(struct_def.doc_comment, code_ptr, &def_comment);
+ BeginClass(struct_def, code_ptr);
+
+ GenerateNewObjectPrototype(struct_def, code_ptr);
+
+ if (!struct_def.fixed) {
+ // Generate a special accessor for the table that has been declared as
+ // the root type.
+ NewRootTypeFromBuffer(struct_def, code_ptr);
+ }
+
+ // Generate the Init method that sets the field in a pre-existing
+ // accessor object. This is to allow object reuse.
+ InitializeExisting(struct_def, code_ptr);
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+
+ GenStructAccessor(struct_def, field, code_ptr);
+ }
+
+ if (struct_def.fixed) {
+ // create a struct constructor function
+ GenStructBuilder(struct_def, code_ptr);
+ }
+ else {
+ // Create a set of functions that allow table construction.
+ GenTableBuilders(struct_def, code_ptr);
+ }
+ }
+
+ // Generate enum declarations.
+ void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
+ if (enum_def.generated) return;
+
+ GenComment(enum_def.doc_comment, code_ptr, &def_comment);
+ BeginEnum(NormalizedName(enum_def), code_ptr);
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ auto &ev = **it;
+ GenComment(ev.doc_comment, code_ptr, &def_comment, Indent);
+ EnumMember(enum_def, ev, code_ptr);
+ }
+ EndEnum(code_ptr);
+ }
+
+ // Returns the function name that is able to read a value of the given type.
+ std::string GenGetter(const Type &type) {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return std::string(SelfData) + ":String(";
+ case BASE_TYPE_UNION: return std::string(SelfData) + ":Union(";
+ case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
+ default:
+ return std::string(SelfData) + ":Get(flatbuffers.N." +
+ MakeCamel(GenTypeGet(type)) + ", ";
+ }
+ }
+
+ // Returns the method name for use with add/put calls.
+ std::string GenMethod(const FieldDef &field) {
+ return IsScalar(field.value.type.base_type)
+ ? MakeCamel(GenTypeBasic(field.value.type))
+ : (IsStruct(field.value.type) ? "Struct" : "UOffsetTRelative");
+ }
+
+ std::string GenTypeBasic(const Type &type) {
+ static const char *ctypename[] = {
+ // clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ #PTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ };
+ return ctypename[type.base_type];
+ }
+
+ std::string GenTypePointer(const Type &type) {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return "string";
+ case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
+ case BASE_TYPE_STRUCT: return type.struct_def->name;
+ case BASE_TYPE_UNION:
+ // fall through
+ default: return "*flatbuffers.Table";
+ }
+ }
+
+ std::string GenTypeGet(const Type &type) {
+ return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type);
+ }
+
+ std::string GetNamespace(const Type &type) {
+ return type.struct_def->defined_namespace->GetFullyQualifiedName(type.struct_def->name);
+ }
+
+ std::string TypeName(const FieldDef &field) {
+ return GenTypeGet(field.value.type);
+ }
+
+ std::string TypeNameWithNamespace(const FieldDef &field) {
+ return GetNamespace(field.value.type);
+ }
+
+ // Create a struct with a builder and the struct's arguments.
+ void GenStructBuilder(const StructDef &struct_def,
+ std::string *code_ptr) {
+ BeginBuilderArgs(struct_def, code_ptr);
+ StructBuilderArgs(struct_def, "", code_ptr);
+ EndBuilderArgs(code_ptr);
+
+ StructBuilderBody(struct_def, "", code_ptr);
+ EndBuilderBody(code_ptr);
+ }
+
+ bool generate() {
+ if (!generateEnums()) return false;
+ if (!generateStructs()) return false;
+ return true;
+ }
+
+ private:
+ bool generateEnums() {
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ auto &enum_def = **it;
+ std::string enumcode;
+ GenEnum(enum_def, &enumcode);
+ if (!SaveType(enum_def, enumcode, false)) return false;
+ }
+ return true;
+ }
+
+ bool generateStructs() {
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ auto &struct_def = **it;
+ std::string declcode;
+ GenStruct(struct_def, &declcode);
+ if (!SaveType(struct_def, declcode, true)) return false;
+ }
+ return true;
+ }
+
+ // Begin by declaring namespace and imports.
+ void BeginFile(const std::string &name_space_name, const bool needs_imports,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += std::string(Comment) + FlatBuffersGeneratedWarning() + "\n\n";
+ code += std::string(Comment) + "namespace: " + name_space_name + "\n\n";
+ if (needs_imports) {
+ code += "local flatbuffers = require('flatbuffers')\n\n";
+ }
+ }
+
+ // Save out the generated code for a Lua Table type.
+ bool SaveType(const Definition &def, const std::string &classcode,
+ bool needs_imports) {
+ if (!classcode.length()) return true;
+
+ std::string namespace_dir = path_;
+ auto &namespaces = def.defined_namespace->components;
+ for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
+ if (it != namespaces.begin()) namespace_dir += kPathSeparator;
+ namespace_dir += *it;
+ //std::string init_py_filename = namespace_dir + "/__init__.py";
+ //SaveFile(init_py_filename.c_str(), "", false);
+ }
+
+ std::string code = "";
+ BeginFile(LastNamespacePart(*def.defined_namespace), needs_imports, &code);
+ code += classcode;
+ code += "\n";
+ code += "return " + NormalizedName(def) + " " + Comment + "return the module";
+ std::string filename =
+ NamespaceDir(*def.defined_namespace) + NormalizedName(def) + ".lua";
+ return SaveFile(filename.c_str(), code, false);
+ }
+ private:
+ std::unordered_set<std::string> keywords_;
+ };
+
+} // namespace lua
+
+bool GenerateLua(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ lua::LuaGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+
+} // namespace flatbuffers
diff --git a/src/idl_gen_php.cpp b/src/idl_gen_php.cpp
new file mode 100644
index 0000000..9d81415
--- /dev/null
+++ b/src/idl_gen_php.cpp
@@ -0,0 +1,940 @@
+/*
+ * 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.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+
+#include <string>
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+namespace php {
+// Hardcode spaces per indentation.
+const std::string Indent = " ";
+class PhpGenerator : public BaseGenerator {
+ public:
+ PhpGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "\\", "\\") {}
+ bool generate() {
+ if (!GenerateEnums()) return false;
+ if (!GenerateStructs()) return false;
+ return true;
+ }
+
+ private:
+ bool GenerateEnums() {
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ auto &enum_def = **it;
+ std::string enumcode;
+ GenEnum(enum_def, &enumcode);
+ if (!SaveType(enum_def, enumcode, false)) return false;
+ }
+ return true;
+ }
+
+ bool GenerateStructs() {
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ auto &struct_def = **it;
+ std::string declcode;
+ GenStruct(struct_def, &declcode);
+ if (!SaveType(struct_def, declcode, true)) return false;
+ }
+ return true;
+ }
+
+ // Begin by declaring namespace and imports.
+ void BeginFile(const std::string &name_space_name, const bool needs_imports,
+ std::string *code_ptr) {
+ auto &code = *code_ptr;
+ code += "<?php\n";
+ code = code + "// " + FlatBuffersGeneratedWarning() + "\n\n";
+
+ if (!name_space_name.empty()) {
+ code += "namespace " + name_space_name + ";\n\n";
+ }
+
+ if (needs_imports) {
+ code += "use \\Google\\FlatBuffers\\Struct;\n";
+ code += "use \\Google\\FlatBuffers\\Table;\n";
+ code += "use \\Google\\FlatBuffers\\ByteBuffer;\n";
+ code += "use \\Google\\FlatBuffers\\FlatBufferBuilder;\n";
+ code += "\n";
+ }
+ }
+
+ // Save out the generated code for a Php Table type.
+ bool SaveType(const Definition &def, const std::string &classcode,
+ bool needs_imports) {
+ if (!classcode.length()) return true;
+
+ std::string code = "";
+ BeginFile(FullNamespace("\\", *def.defined_namespace), needs_imports,
+ &code);
+ code += classcode;
+
+ std::string filename =
+ NamespaceDir(*def.defined_namespace) + def.name + ".php";
+ return SaveFile(filename.c_str(), code, false);
+ }
+
+ // Begin a class declaration.
+ static void BeginClass(const StructDef &struct_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ if (struct_def.fixed) {
+ code += "class " + struct_def.name + " extends Struct\n";
+ } else {
+ code += "class " + struct_def.name + " extends Table\n";
+ }
+ code += "{\n";
+ }
+
+ static void EndClass(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "}\n";
+ }
+
+ // Begin enum code with a class declaration.
+ static void BeginEnum(const std::string &class_name, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "class " + class_name + "\n{\n";
+ }
+
+ // A single enum member.
+ static void EnumMember(const EnumDef &enum_def, const EnumVal &ev,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += Indent + "const ";
+ code += ev.name;
+ code += " = ";
+ code += enum_def.ToString(ev) + ";\n";
+ }
+
+ // End enum code.
+ static void EndEnum(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "}\n";
+ }
+
+ // Initialize a new struct or table from existing data.
+ static void NewRootTypeFromBuffer(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += Indent + "/**\n";
+ code += Indent + " * @param ByteBuffer $bb\n";
+ code += Indent + " * @return " + struct_def.name + "\n";
+ code += Indent + " */\n";
+ code += Indent + "public static function getRootAs";
+ code += struct_def.name;
+ code += "(ByteBuffer $bb)\n";
+ code += Indent + "{\n";
+
+ code += Indent + Indent + "$obj = new " + struct_def.name + "();\n";
+ code += Indent + Indent;
+ code += "return ($obj->init($bb->getInt($bb->getPosition())";
+ code += " + $bb->getPosition(), $bb));\n";
+ code += Indent + "}\n\n";
+ }
+
+ // Initialize an existing object with other data, to avoid an allocation.
+ static void InitializeExisting(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += Indent + "/**\n";
+ code += Indent + " * @param int $_i offset\n";
+ code += Indent + " * @param ByteBuffer $_bb\n";
+ code += Indent + " * @return " + struct_def.name + "\n";
+ code += Indent + " **/\n";
+ code += Indent + "public function init($_i, ByteBuffer $_bb)\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$this->bb_pos = $_i;\n";
+ code += Indent + Indent + "$this->bb = $_bb;\n";
+ code += Indent + Indent + "return $this;\n";
+ code += Indent + "}\n\n";
+ }
+
+ // Get the length of a vector.
+ static void GetVectorLen(const FieldDef &field, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += Indent + "/**\n";
+ code += Indent + " * @return int\n";
+ code += Indent + " */\n";
+ code += Indent + "public function get";
+ code += MakeCamel(field.name) + "Length()\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$o = $this->__offset(";
+ code += NumToString(field.value.offset) + ");\n";
+ code += Indent + Indent;
+ code += "return $o != 0 ? $this->__vector_len($o) : 0;\n";
+ code += Indent + "}\n\n";
+ }
+
+ // Get a [ubyte] vector as a byte array.
+ static void GetUByte(const FieldDef &field, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += Indent + "/**\n";
+ code += Indent + " * @return string\n";
+ code += Indent + " */\n";
+ code += Indent + "public function get";
+ code += MakeCamel(field.name) + "Bytes()\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "return $this->__vector_as_bytes(";
+ code += NumToString(field.value.offset) + ");\n";
+ code += Indent + "}\n\n";
+ }
+
+ // Get the value of a struct's scalar.
+ static void GetScalarFieldOfStruct(const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ std::string getter = GenGetter(field.value.type);
+
+ code += Indent + "/**\n";
+ code += Indent + " * @return ";
+ code += GenTypeGet(field.value.type) + "\n";
+ code += Indent + " */\n";
+ code += Indent + "public function " + getter;
+ code += MakeCamel(field.name) + "()\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "return ";
+
+ code += "$this->bb->get";
+ code += MakeCamel(GenTypeGet(field.value.type));
+ code += "($this->bb_pos + ";
+ code += NumToString(field.value.offset) + ")";
+ code += ";\n";
+
+ code += Indent + "}\n\n";
+ }
+
+ // Get the value of a table's scalar.
+ void GetScalarFieldOfTable(const FieldDef &field, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += Indent + "/**\n";
+ code += Indent + " * @return " + GenTypeGet(field.value.type) + "\n";
+ code += Indent + " */\n";
+ code += Indent + "public function get";
+ code += MakeCamel(field.name);
+ code += "()\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$o = $this->__offset(" +
+ NumToString(field.value.offset) + ");\n" + Indent + Indent +
+ "return $o != 0 ? ";
+ code += "$this->bb->get";
+ code += MakeCamel(GenTypeGet(field.value.type)) + "($o + $this->bb_pos)";
+ code += " : " + GenDefaultValue(field.value) + ";\n";
+ code += Indent + "}\n\n";
+ }
+
+ // Get a struct by initializing an existing struct.
+ // Specific to Struct.
+ void GetStructFieldOfStruct(const FieldDef &field, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += Indent + "/**\n";
+ code += Indent + " * @return " + GenTypeGet(field.value.type) + "\n";
+ code += Indent + " */\n";
+ code += Indent + "public function get";
+ code += MakeCamel(field.name) + "()\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$obj = new ";
+ code += GenTypeGet(field.value.type) + "();\n";
+ code += Indent + Indent + "$obj->init($this->bb_pos + ";
+ code += NumToString(field.value.offset) + ", $this->bb);";
+ code += "\n" + Indent + Indent + "return $obj;\n";
+ code += Indent + "}\n\n";
+ }
+
+ // Get a struct by initializing an existing struct.
+ // Specific to Table.
+ void GetStructFieldOfTable(const FieldDef &field, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += Indent + "public function get";
+ code += MakeCamel(field.name);
+ code += "()\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$obj = new ";
+ code += MakeCamel(GenTypeGet(field.value.type)) + "();\n";
+ code += Indent + Indent + "$o = $this->__offset(" +
+ NumToString(field.value.offset) + ");\n";
+ code += Indent + Indent;
+ code += "return $o != 0 ? $obj->init(";
+ if (field.value.type.struct_def->fixed) {
+ code += "$o + $this->bb_pos, $this->bb) : ";
+ } else {
+ code += "$this->__indirect($o + $this->bb_pos), $this->bb) : ";
+ }
+ code += GenDefaultValue(field.value) + ";\n";
+ code += Indent + "}\n\n";
+ }
+
+ // Get the value of a string.
+ void GetStringField(const FieldDef &field, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += Indent + "public function get";
+ code += MakeCamel(field.name);
+ code += "()\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$o = $this->__offset(" +
+ NumToString(field.value.offset) + ");\n";
+ code += Indent + Indent;
+ code += "return $o != 0 ? $this->__string($o + $this->bb_pos) : ";
+ code += GenDefaultValue(field.value) + ";\n";
+ code += Indent + "}\n\n";
+ }
+
+ // Get the value of a union from an object.
+ void GetUnionField(const FieldDef &field, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += Indent + "/**\n";
+ code += Indent + " * @return" + GenTypeBasic(field.value.type) + "\n";
+ code += Indent + " */\n";
+ code += Indent + "public function get";
+ code += MakeCamel(field.name) + "($obj)\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$o = $this->__offset(" +
+ NumToString(field.value.offset) + ");\n";
+ code += Indent + Indent;
+ code += "return $o != 0 ? $this->__union($obj, $o) : null;\n";
+ code += Indent + "}\n\n";
+ }
+
+ // Get the value of a vector's struct member.
+ void GetMemberOfVectorOfStruct(const StructDef &struct_def,
+ const FieldDef &field, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ auto vectortype = field.value.type.VectorType();
+
+ code += Indent + "/**\n";
+ code += Indent + " * @return" + GenTypeBasic(field.value.type) + "\n";
+ code += Indent + " */\n";
+ code += Indent + "public function get";
+ code += MakeCamel(field.name);
+ code += "($j)\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$o = $this->__offset(" +
+ NumToString(field.value.offset) + ");\n";
+ code += Indent + Indent + "$obj = new ";
+ code += MakeCamel(GenTypeGet(field.value.type)) + "();\n";
+
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_STRUCT:
+ if (struct_def.fixed) {
+ code += Indent + Indent;
+ code += "return $o != 0 ? $obj->init($this->bb_pos +" +
+ NumToString(field.value.offset) + ", $this->bb) : null;\n";
+ } else {
+ code += Indent + Indent + "return $o != 0 ? $obj->init(";
+ code += field.value.type.struct_def->fixed
+ ? "$o + $this->bb_pos"
+ : "$this->__indirect($o + $this->bb_pos)";
+ code += ", $this->bb) : null;\n";
+ }
+ break;
+ case BASE_TYPE_STRING:
+ code += "// base_type_string\n";
+ // TODO(chobie): do we need this?
+ break;
+ case BASE_TYPE_VECTOR:
+ if (vectortype.base_type == BASE_TYPE_STRUCT) {
+ code += Indent + Indent + "return $o != 0 ? $obj->init(";
+ if (vectortype.struct_def->fixed) {
+ code += "$this->__vector($o) + $j *";
+ code += NumToString(InlineSize(vectortype));
+ } else {
+ code += "$this->__indirect($this->__vector($o) + $j * ";
+ code += NumToString(InlineSize(vectortype)) + ")";
+ }
+ code += ", $this->bb) : null;\n";
+ }
+ break;
+ case BASE_TYPE_UNION:
+ code += Indent + Indent + "return $o != 0 ? $this->";
+ code += GenGetter(field.value.type) + "($obj, $o); null;\n";
+ break;
+ default: break;
+ }
+
+ code += Indent + "}\n\n";
+ }
+
+ // Get the value of a vector's non-struct member. Uses a named return
+ // argument to conveniently set the zero value for the result.
+ void GetMemberOfVectorOfNonStruct(const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ auto vectortype = field.value.type.VectorType();
+
+ code += Indent + "/**\n";
+ code += Indent + " * @param int offset\n";
+ code += Indent + " * @return " + GenTypeGet(field.value.type) + "\n";
+ code += Indent + " */\n";
+ code += Indent + "public function get";
+ code += MakeCamel(field.name);
+ code += "($j)\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$o = $this->__offset(" +
+ NumToString(field.value.offset) + ");\n";
+
+ if (field.value.type.VectorType().base_type == BASE_TYPE_STRING) {
+ code += Indent + Indent;
+ code += "return $o != 0 ? $this->__string($this->__vector($o) + $j * ";
+ code += NumToString(InlineSize(vectortype)) + ") : ";
+ code += GenDefaultValue(field.value) + ";\n";
+ } else {
+ code += Indent + Indent + "return $o != 0 ? $this->bb->get";
+ code += MakeCamel(GenTypeGet(field.value.type));
+ code += "($this->__vector($o) + $j * ";
+ code += NumToString(InlineSize(vectortype)) + ") : ";
+ code += GenDefaultValue(field.value) + ";\n";
+ }
+ code += Indent + "}\n\n";
+ }
+
+ // Get the value of a vector's union member. Uses a named return
+ // argument to conveniently set the zero value for the result.
+ void GetMemberOfVectorOfUnion(const FieldDef &field, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ auto vectortype = field.value.type.VectorType();
+
+ code += Indent + "/**\n";
+ code += Indent + " * @param int offset\n";
+ code += Indent + " * @return " + GenTypeGet(field.value.type) + "\n";
+ code += Indent + " */\n";
+ code += Indent + "public function get";
+ code += MakeCamel(field.name);
+ code += "($j, $obj)\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$o = $this->__offset(" +
+ NumToString(field.value.offset) + ");\n";
+ code += Indent + Indent + "return $o != 0 ? ";
+ code += "$this->__union($obj, $this->__vector($o) + $j * ";
+ code += NumToString(InlineSize(vectortype)) + " - $this->bb_pos) : null;\n";
+ code += Indent + "}\n\n";
+ }
+
+ // Recursively generate arguments for a constructor, to deal with nested
+ // structs.
+ static void StructBuilderArgs(const StructDef &struct_def,
+ const char *nameprefix, std::string *code_ptr) {
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (IsStruct(field.value.type)) {
+ // Generate arguments for a struct inside a struct. To ensure names
+ // don't clash, and to make it obvious
+ // these arguments are constructing
+ // a nested struct, prefix the name with the field name.
+ StructBuilderArgs(*field.value.type.struct_def,
+ (nameprefix + (field.name + "_")).c_str(), code_ptr);
+ } else {
+ std::string &code = *code_ptr;
+ code += std::string(", $") + nameprefix;
+ code += MakeCamel(field.name, false);
+ }
+ }
+ }
+
+ // Recursively generate struct construction statements and instert manual
+ // padding.
+ static void StructBuilderBody(const StructDef &struct_def,
+ const char *nameprefix, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += Indent + Indent + "$builder->prep(";
+ code += NumToString(struct_def.minalign) + ", ";
+ code += NumToString(struct_def.bytesize) + ");\n";
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ auto &field = **it;
+ if (field.padding) {
+ code += Indent + Indent + "$builder->pad(";
+ code += NumToString(field.padding) + ");\n";
+ }
+ if (IsStruct(field.value.type)) {
+ StructBuilderBody(*field.value.type.struct_def,
+ (nameprefix + (field.name + "_")).c_str(), code_ptr);
+ } else {
+ code += Indent + Indent + "$builder->put" + GenMethod(field) + "($";
+ code += nameprefix + MakeCamel(field.name, false) + ");\n";
+ }
+ }
+ }
+
+ // Get the value of a table's starting offset.
+ static void GetStartOfTable(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += Indent + "/**\n";
+ code += Indent + " * @param FlatBufferBuilder $builder\n";
+ code += Indent + " * @return void\n";
+ code += Indent + " */\n";
+ code += Indent + "public static function start" + struct_def.name;
+ code += "(FlatBufferBuilder $builder)\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$builder->StartObject(";
+ code += NumToString(struct_def.fields.vec.size());
+ code += ");\n";
+ code += Indent + "}\n\n";
+
+ code += Indent + "/**\n";
+ code += Indent + " * @param FlatBufferBuilder $builder\n";
+ code += Indent + " * @return " + struct_def.name + "\n";
+ code += Indent + " */\n";
+ code += Indent + "public static function create" + struct_def.name;
+ code += "(FlatBufferBuilder $builder, ";
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+
+ if (field.deprecated) continue;
+ code += "$" + field.name;
+ if (!(it == (--struct_def.fields.vec.end()))) { code += ", "; }
+ }
+ code += ")\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$builder->startObject(";
+ code += NumToString(struct_def.fields.vec.size());
+ code += ");\n";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+
+ code += Indent + Indent + "self::add";
+ code += MakeCamel(field.name) + "($builder, $" + field.name + ");\n";
+ }
+
+ code += Indent + Indent + "$o = $builder->endObject();\n";
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (!field.deprecated && field.required) {
+ code += Indent + Indent + "$builder->required($o, ";
+ code += NumToString(field.value.offset);
+ code += "); // " + field.name + "\n";
+ }
+ }
+ code += Indent + Indent + "return $o;\n";
+ code += Indent + "}\n\n";
+ }
+
+ // Set the value of a table's field.
+ static void BuildFieldOfTable(const FieldDef &field, const size_t offset,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += Indent + "/**\n";
+ code += Indent + " * @param FlatBufferBuilder $builder\n";
+ code += Indent + " * @param " + GenTypeBasic(field.value.type) + "\n";
+ code += Indent + " * @return void\n";
+ code += Indent + " */\n";
+ code += Indent + "public static function ";
+ code += "add" + MakeCamel(field.name);
+ code += "(FlatBufferBuilder $builder, ";
+ code += "$" + MakeCamel(field.name, false);
+ code += ")\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$builder->add";
+ code += GenMethod(field) + "X(";
+ code += NumToString(offset) + ", ";
+
+ code += "$" + MakeCamel(field.name, false);
+ code += ", ";
+
+ if (field.value.type.base_type == BASE_TYPE_BOOL) {
+ code += "false";
+ } else {
+ code += field.value.constant;
+ }
+ code += ");\n";
+ code += Indent + "}\n\n";
+ }
+
+ // Set the value of one of the members of a table's vector.
+ static void BuildVectorOfTable(const FieldDef &field, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ auto vector_type = field.value.type.VectorType();
+ auto alignment = InlineAlignment(vector_type);
+ auto elem_size = InlineSize(vector_type);
+ code += Indent + "/**\n";
+ code += Indent + " * @param FlatBufferBuilder $builder\n";
+ code += Indent + " * @param array offset array\n";
+ code += Indent + " * @return int vector offset\n";
+ code += Indent + " */\n";
+ code += Indent + "public static function create";
+ code += MakeCamel(field.name);
+ code += "Vector(FlatBufferBuilder $builder, array $data)\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$builder->startVector(";
+ code += NumToString(elem_size);
+ code += ", count($data), " + NumToString(alignment);
+ code += ");\n";
+ code += Indent + Indent;
+ code += "for ($i = count($data) - 1; $i >= 0; $i--) {\n";
+ if (IsScalar(field.value.type.VectorType().base_type)) {
+ code += Indent + Indent + Indent;
+ code += "$builder->put";
+ code += MakeCamel(GenTypeBasic(field.value.type.VectorType()));
+ code += "($data[$i]);\n";
+ } else {
+ code += Indent + Indent + Indent;
+ code += "$builder->putOffset($data[$i]);\n";
+ }
+ code += Indent + Indent + "}\n";
+ code += Indent + Indent + "return $builder->endVector();\n";
+ code += Indent + "}\n\n";
+
+ code += Indent + "/**\n";
+ code += Indent + " * @param FlatBufferBuilder $builder\n";
+ code += Indent + " * @param int $numElems\n";
+ code += Indent + " * @return void\n";
+ code += Indent + " */\n";
+ code += Indent + "public static function start";
+ code += MakeCamel(field.name);
+ code += "Vector(FlatBufferBuilder $builder, $numElems)\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$builder->startVector(";
+ code += NumToString(elem_size);
+ code += ", $numElems, " + NumToString(alignment);
+ code += ");\n";
+ code += Indent + "}\n\n";
+ }
+
+ // Get the offset of the end of a table.
+ void GetEndOffsetOnTable(const StructDef &struct_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += Indent + "/**\n";
+ code += Indent + " * @param FlatBufferBuilder $builder\n";
+ code += Indent + " * @return int table offset\n";
+ code += Indent + " */\n";
+ code += Indent + "public static function end" + struct_def.name;
+ code += "(FlatBufferBuilder $builder)\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$o = $builder->endObject();\n";
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (!field.deprecated && field.required) {
+ code += Indent + Indent + "$builder->required($o, ";
+ code += NumToString(field.value.offset);
+ code += "); // " + field.name + "\n";
+ }
+ }
+ code += Indent + Indent + "return $o;\n";
+ code += Indent + "}\n";
+
+ if (parser_.root_struct_def_ == &struct_def) {
+ code += "\n";
+ code += Indent + "public static function finish";
+ code += struct_def.name;
+ code += "Buffer(FlatBufferBuilder $builder, $offset)\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$builder->finish($offset";
+
+ if (parser_.file_identifier_.length())
+ code += ", \"" + parser_.file_identifier_ + "\"";
+ code += ");\n";
+ code += Indent + "}\n";
+ }
+ }
+
+ // Generate a struct field, conditioned on its child type(s).
+ void GenStructAccessor(const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr) {
+ GenComment(field.doc_comment, code_ptr, nullptr, Indent.c_str());
+
+ if (IsScalar(field.value.type.base_type)) {
+ if (struct_def.fixed) {
+ GetScalarFieldOfStruct(field, code_ptr);
+ } else {
+ GetScalarFieldOfTable(field, code_ptr);
+ }
+ } else {
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_STRUCT:
+ if (struct_def.fixed) {
+ GetStructFieldOfStruct(field, code_ptr);
+ } else {
+ GetStructFieldOfTable(field, code_ptr);
+ }
+ break;
+ case BASE_TYPE_STRING: GetStringField(field, code_ptr); break;
+ case BASE_TYPE_VECTOR: {
+ auto vectortype = field.value.type.VectorType();
+ if (vectortype.base_type == BASE_TYPE_UNION) {
+ GetMemberOfVectorOfUnion(field, code_ptr);
+ } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
+ GetMemberOfVectorOfStruct(struct_def, field, code_ptr);
+ } else {
+ GetMemberOfVectorOfNonStruct(field, code_ptr);
+ }
+ break;
+ }
+ case BASE_TYPE_UNION: GetUnionField(field, code_ptr); break;
+ default: FLATBUFFERS_ASSERT(0);
+ }
+ }
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ GetVectorLen(field, code_ptr);
+ if (field.value.type.element == BASE_TYPE_UCHAR) {
+ GetUByte(field, code_ptr);
+ }
+ }
+ }
+
+ // Generate table constructors, conditioned on its members' types.
+ void GenTableBuilders(const StructDef &struct_def, std::string *code_ptr) {
+ GetStartOfTable(struct_def, code_ptr);
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+
+ auto offset = it - struct_def.fields.vec.begin();
+ if (field.value.type.base_type == BASE_TYPE_UNION) {
+ std::string &code = *code_ptr;
+ code += Indent + "public static function add";
+ code += MakeCamel(field.name);
+ code += "(FlatBufferBuilder $builder, $offset)\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "$builder->addOffsetX(";
+ code += NumToString(offset) + ", $offset, 0);\n";
+ code += Indent + "}\n\n";
+ } else {
+ BuildFieldOfTable(field, offset, code_ptr);
+ }
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ BuildVectorOfTable(field, code_ptr);
+ }
+ }
+
+ GetEndOffsetOnTable(struct_def, code_ptr);
+ }
+
+ // Generate struct or table methods.
+ void GenStruct(const StructDef &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.fixed) {
+ // Generate a special accessor for the table that has been declared as
+ // the root type.
+ NewRootTypeFromBuffer(struct_def, code_ptr);
+ }
+
+ std::string &code = *code_ptr;
+ if (!struct_def.fixed) {
+ if (parser_.file_identifier_.length()) {
+ // Return the identifier
+ code += Indent + "public static function " + struct_def.name;
+ code += "Identifier()\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "return \"";
+ code += parser_.file_identifier_ + "\";\n";
+ code += Indent + "}\n\n";
+
+ // Check if a buffer has the identifier.
+ code += Indent + "public static function " + struct_def.name;
+ code += "BufferHasIdentifier(ByteBuffer $buf)\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "return self::";
+ code += "__has_identifier($buf, self::";
+ code += struct_def.name + "Identifier());\n";
+ code += Indent + "}\n\n";
+ }
+
+ if (parser_.file_extension_.length()) {
+ // Return the extension
+ code += Indent + "public static function " + struct_def.name;
+ code += "Extension()\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "return \"" + parser_.file_extension_;
+ code += "\";\n";
+ code += Indent + "}\n\n";
+ }
+ }
+
+ // Generate the Init method that sets the field in a pre-existing
+ // accessor object. This is to allow object reuse.
+ InitializeExisting(struct_def, code_ptr);
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+
+ GenStructAccessor(struct_def, field, code_ptr);
+ }
+
+ if (struct_def.fixed) {
+ // create a struct constructor function
+ GenStructBuilder(struct_def, code_ptr);
+ } else {
+ // Create a set of functions that allow table construction.
+ GenTableBuilders(struct_def, code_ptr);
+ }
+ EndClass(code_ptr);
+ }
+
+ // Generate enum declarations.
+ static void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
+ if (enum_def.generated) return;
+
+ GenComment(enum_def.doc_comment, code_ptr, nullptr);
+ BeginEnum(enum_def.name, code_ptr);
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ auto &ev = **it;
+ GenComment(ev.doc_comment, code_ptr, nullptr, Indent.c_str());
+ EnumMember(enum_def, ev, code_ptr);
+ }
+
+ std::string &code = *code_ptr;
+ code += "\n";
+ code += Indent + "private static $names = array(\n";
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ auto &ev = **it;
+ code += Indent + Indent + enum_def.name + "::" + ev.name + "=>" + "\"" + ev.name + "\",\n";
+ }
+
+ code += Indent + ");\n\n";
+ code += Indent + "public static function Name($e)\n";
+ code += Indent + "{\n";
+ code += Indent + Indent + "if (!isset(self::$names[$e])) {\n";
+ code += Indent + Indent + Indent + "throw new \\Exception();\n";
+ code += Indent + Indent + "}\n";
+ code += Indent + Indent + "return self::$names[$e];\n";
+ code += Indent + "}\n";
+ EndEnum(code_ptr);
+ }
+
+ // Returns the function name that is able to read a value of the given type.
+ static std::string GenGetter(const Type &type) {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return "__string";
+ case BASE_TYPE_STRUCT: return "__struct";
+ case BASE_TYPE_UNION: return "__union";
+ case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
+ default: return "Get";
+ }
+ }
+
+ // Returns the method name for use with add/put calls.
+ static std::string GenMethod(const FieldDef &field) {
+ return IsScalar(field.value.type.base_type)
+ ? MakeCamel(GenTypeBasic(field.value.type))
+ : (IsStruct(field.value.type) ? "Struct" : "Offset");
+ }
+
+ static std::string GenTypeBasic(const Type &type) {
+ static const char *ctypename[] = {
+ // clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ #NTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ };
+ return ctypename[type.base_type];
+ }
+
+ std::string GenDefaultValue(const Value &value) {
+ if (value.type.enum_def) {
+ if (auto val = value.type.enum_def->FindByValue(value.constant)) {
+ return WrapInNameSpace(*value.type.enum_def) + "::" + val->name;
+ }
+ }
+
+ switch (value.type.base_type) {
+ case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
+
+ case BASE_TYPE_STRING: return "null";
+
+ case BASE_TYPE_LONG:
+ case BASE_TYPE_ULONG:
+ if (value.constant != "0") {
+ int64_t constant = StringToInt(value.constant.c_str());
+ return NumToString(constant);
+ }
+ return "0";
+
+ default: return value.constant;
+ }
+ }
+
+ static std::string GenTypePointer(const Type &type) {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return "string";
+ case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
+ case BASE_TYPE_STRUCT: return type.struct_def->name;
+ case BASE_TYPE_UNION:
+ // fall through
+ default: return "Table";
+ }
+ }
+
+ static std::string GenTypeGet(const Type &type) {
+ return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type);
+ }
+
+ // Create a struct with a builder and the struct's arguments.
+ static void GenStructBuilder(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "\n";
+ code += Indent + "/**\n";
+ code += Indent + " * @return int offset\n";
+ code += Indent + " */\n";
+ code += Indent + "public static function create" + struct_def.name;
+ code += "(FlatBufferBuilder $builder";
+ StructBuilderArgs(struct_def, "", code_ptr);
+ code += ")\n";
+ code += Indent + "{\n";
+
+ StructBuilderBody(struct_def, "", code_ptr);
+
+ code += Indent + Indent + "return $builder->offset();\n";
+ code += Indent + "}\n";
+ }
+};
+} // namespace php
+
+bool GeneratePhp(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ php::PhpGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+} // namespace flatbuffers
diff --git a/src/idl_gen_python.cpp b/src/idl_gen_python.cpp
new file mode 100644
index 0000000..c8db359
--- /dev/null
+++ b/src/idl_gen_python.cpp
@@ -0,0 +1,823 @@
+/*
+ * 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.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+
+#include <string>
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+#include <unordered_set>
+
+namespace flatbuffers {
+namespace python {
+
+// Hardcode spaces per indentation.
+const CommentConfig def_comment = { nullptr, "#", nullptr };
+const std::string Indent = " ";
+
+class PythonGenerator : public BaseGenerator {
+ public:
+ PythonGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "" /* not used */,
+ "" /* not used */),
+ float_const_gen_("float('nan')", "float('inf')", "float('-inf')") {
+ static const char * const keywords[] = {
+ "False",
+ "None",
+ "True",
+ "and",
+ "as",
+ "assert",
+ "break",
+ "class",
+ "continue",
+ "def",
+ "del",
+ "elif",
+ "else",
+ "except",
+ "finally",
+ "for",
+ "from",
+ "global",
+ "if",
+ "import",
+ "in",
+ "is",
+ "lambda",
+ "nonlocal",
+ "not",
+ "or",
+ "pass",
+ "raise",
+ "return",
+ "try",
+ "while",
+ "with",
+ "yield"
+ };
+ keywords_.insert(std::begin(keywords), std::end(keywords));
+ }
+
+ // Most field accessors need to retrieve and test the field offset first,
+ // this is the prefix code for that.
+ std::string OffsetPrefix(const FieldDef &field) {
+ return "\n" + Indent + Indent +
+ "o = flatbuffers.number_types.UOffsetTFlags.py_type" +
+ "(self._tab.Offset(" + NumToString(field.value.offset) + "))\n" +
+ Indent + Indent + "if o != 0:\n";
+ }
+
+ // Begin a class declaration.
+ void BeginClass(const StructDef &struct_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "class " + NormalizedName(struct_def) + "(object):\n";
+ code += Indent + "__slots__ = ['_tab']";
+ code += "\n\n";
+ }
+
+ // Begin enum code with a class declaration.
+ void BeginEnum(const std::string &class_name, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "class " + class_name + "(object):\n";
+ }
+
+ std::string EscapeKeyword(const std::string &name) const {
+ return keywords_.find(name) == keywords_.end() ? name : name + "_";
+ }
+
+ std::string NormalizedName(const Definition &definition) const {
+ return EscapeKeyword(definition.name);
+ }
+
+ std::string NormalizedName(const EnumVal &ev) const {
+ return EscapeKeyword(ev.name);
+ }
+
+ // A single enum member.
+ void EnumMember(const EnumDef &enum_def, const EnumVal &ev,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += Indent;
+ code += NormalizedName(ev);
+ code += " = ";
+ code += enum_def.ToString(ev) + "\n";
+ }
+
+ // End enum code.
+ void EndEnum(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "\n";
+ }
+
+ // Initialize a new struct or table from existing data.
+ void NewRootTypeFromBuffer(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += Indent + "@classmethod\n";
+ code += Indent + "def GetRootAs";
+ code += NormalizedName(struct_def);
+ code += "(cls, buf, offset):";
+ code += "\n";
+ code += Indent + Indent;
+ code += "n = flatbuffers.encode.Get";
+ code += "(flatbuffers.packer.uoffset, buf, offset)\n";
+ code += Indent + Indent + "x = " + NormalizedName(struct_def) + "()\n";
+ code += Indent + Indent + "x.Init(buf, n + offset)\n";
+ code += Indent + Indent + "return x\n";
+ code += "\n";
+ }
+
+ // Initialize an existing object with other data, to avoid an allocation.
+ void InitializeExisting(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ GenReceiver(struct_def, code_ptr);
+ code += "Init(self, buf, pos):\n";
+ code += Indent + Indent + "self._tab = flatbuffers.table.Table(buf, pos)\n";
+ code += "\n";
+ }
+
+ // Get the length of a vector.
+ void GetVectorLen(const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field)) + "Length(self";
+ code += "):" + OffsetPrefix(field);
+ code += Indent + Indent + Indent + "return self._tab.VectorLen(o)\n";
+ code += Indent + Indent + "return 0\n\n";
+ }
+
+ // Get the value of a struct's scalar.
+ void GetScalarFieldOfStruct(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ std::string getter = GenGetter(field.value.type);
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "(self): return " + getter;
+ code += "self._tab.Pos + flatbuffers.number_types.UOffsetTFlags.py_type(";
+ code += NumToString(field.value.offset) + "))\n";
+ }
+
+ // Get the value of a table's scalar.
+ void GetScalarFieldOfTable(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ std::string getter = GenGetter(field.value.type);
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "(self):";
+ code += OffsetPrefix(field);
+ getter += "o + self._tab.Pos)";
+ auto is_bool = IsBool(field.value.type.base_type);
+ if (is_bool) {
+ getter = "bool(" + getter + ")";
+ }
+ code += Indent + Indent + Indent + "return " + getter + "\n";
+ std::string default_value;
+ if (is_bool) {
+ default_value = field.value.constant == "0" ? "False" : "True";
+ } else {
+ default_value = IsFloat(field.value.type.base_type)
+ ? float_const_gen_.GenFloatConstant(field)
+ : field.value.constant;
+ }
+ code += Indent + Indent + "return " + default_value + "\n\n";
+ }
+
+ // Get a struct by initializing an existing struct.
+ // Specific to Struct.
+ void GetStructFieldOfStruct(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "(self, obj):\n";
+ code += Indent + Indent + "obj.Init(self._tab.Bytes, self._tab.Pos + ";
+ code += NumToString(field.value.offset) + ")";
+ code += "\n" + Indent + Indent + "return obj\n\n";
+ }
+
+ // Get the value of a fixed size array.
+ void GetArrayOfStruct(const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ const auto vec_type = field.value.type.VectorType();
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ if (IsStruct(vec_type)) {
+ code += "(self, obj, i):\n";
+ code += Indent + Indent + "obj.Init(self._tab.Bytes, self._tab.Pos + ";
+ code += NumToString(field.value.offset) + " + i * ";
+ code += NumToString(InlineSize(vec_type));
+ code += ")\n" + Indent + Indent + "return obj\n\n";
+ } else {
+ auto getter = GenGetter(vec_type);
+ code += "(self): return [" + getter;
+ code += "self._tab.Pos + flatbuffers.number_types.UOffsetTFlags.py_type(";
+ code += NumToString(field.value.offset) + " + i * ";
+ code += NumToString(InlineSize(vec_type));
+ code += ")) for i in range(";
+ code += NumToString(field.value.type.fixed_length) + ")]\n";
+ }
+ }
+
+ // Get a struct by initializing an existing struct.
+ // Specific to Table.
+ void GetStructFieldOfTable(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "(self):";
+ code += OffsetPrefix(field);
+ if (field.value.type.struct_def->fixed) {
+ code += Indent + Indent + Indent + "x = o + self._tab.Pos\n";
+ } else {
+ code += Indent + Indent + Indent;
+ code += "x = self._tab.Indirect(o + self._tab.Pos)\n";
+ }
+ code += Indent + Indent + Indent;
+ code += "from ." + TypeName(field) + " import " + TypeName(field) + "\n";
+ code += Indent + Indent + Indent + "obj = " + TypeName(field) + "()\n";
+ code += Indent + Indent + Indent + "obj.Init(self._tab.Bytes, x)\n";
+ code += Indent + Indent + Indent + "return obj\n";
+ code += Indent + Indent + "return None\n\n";
+ }
+
+ // Get the value of a string.
+ void GetStringField(const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "(self):";
+ code += OffsetPrefix(field);
+ code += Indent + Indent + Indent + "return " + GenGetter(field.value.type);
+ code += "o + self._tab.Pos)\n";
+ code += Indent + Indent + "return None\n\n";
+ }
+
+ // Get the value of a union from an object.
+ void GetUnionField(const StructDef &struct_def, const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field)) + "(self):";
+ code += OffsetPrefix(field);
+
+ // TODO(rw): this works and is not the good way to it:
+ bool is_native_table = TypeName(field) == "*flatbuffers.Table";
+ if (is_native_table) {
+ code += Indent + Indent + Indent + "from flatbuffers.table import Table\n";
+ } else {
+ code += Indent + Indent + Indent;
+ code += "from ." + TypeName(field) + " import " + TypeName(field) + "\n";
+ }
+ code += Indent + Indent + Indent + "obj = Table(bytearray(), 0)\n";
+ code += Indent + Indent + Indent + GenGetter(field.value.type);
+ code += "obj, o)\n" + Indent + Indent + Indent + "return obj\n";
+ code += Indent + Indent + "return None\n\n";
+ }
+
+ // Get the value of a vector's struct member.
+ void GetMemberOfVectorOfStruct(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ auto vectortype = field.value.type.VectorType();
+
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "(self, j):" + OffsetPrefix(field);
+ code += Indent + Indent + Indent + "x = self._tab.Vector(o)\n";
+ code += Indent + Indent + Indent;
+ code += "x += flatbuffers.number_types.UOffsetTFlags.py_type(j) * ";
+ code += NumToString(InlineSize(vectortype)) + "\n";
+ if (!(vectortype.struct_def->fixed)) {
+ code += Indent + Indent + Indent + "x = self._tab.Indirect(x)\n";
+ }
+ code += Indent + Indent + Indent;
+ code += "from ." + TypeName(field) + " import " + TypeName(field) + "\n";
+ code += Indent + Indent + Indent + "obj = " + TypeName(field) + "()\n";
+ code += Indent + Indent + Indent + "obj.Init(self._tab.Bytes, x)\n";
+ code += Indent + Indent + Indent + "return obj\n";
+ code += Indent + Indent + "return None\n\n";
+ }
+
+ // Get the value of a vector's non-struct member. Uses a named return
+ // argument to conveniently set the zero value for the result.
+ void GetMemberOfVectorOfNonStruct(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ auto vectortype = field.value.type.VectorType();
+
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field));
+ code += "(self, j):";
+ code += OffsetPrefix(field);
+ code += Indent + Indent + Indent + "a = self._tab.Vector(o)\n";
+ code += Indent + Indent + Indent;
+ code += "return " + GenGetter(field.value.type);
+ code += "a + flatbuffers.number_types.UOffsetTFlags.py_type(j * ";
+ code += NumToString(InlineSize(vectortype)) + "))\n";
+ if (vectortype.base_type == BASE_TYPE_STRING) {
+ code += Indent + Indent + "return \"\"\n";
+ } else {
+ code += Indent + Indent + "return 0\n";
+ }
+ code += "\n";
+ }
+
+ // Returns a non-struct vector as a numpy array. Much faster
+ // than iterating over the vector element by element.
+ void GetVectorOfNonStructAsNumpy(const StructDef &struct_def,
+ const FieldDef &field,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ auto vectortype = field.value.type.VectorType();
+
+ // Currently, we only support accessing as numpy array if
+ // the vector type is a scalar.
+ if (!(IsScalar(vectortype.base_type))) { return; }
+
+ GenReceiver(struct_def, code_ptr);
+ code += MakeCamel(NormalizedName(field)) + "AsNumpy(self):";
+ code += OffsetPrefix(field);
+
+ code += Indent + Indent + Indent;
+ code += "return ";
+ code += "self._tab.GetVectorAsNumpy(flatbuffers.number_types.";
+ code += MakeCamel(GenTypeGet(field.value.type));
+ code += "Flags, o)\n";
+
+ if (vectortype.base_type == BASE_TYPE_STRING) {
+ code += Indent + Indent + "return \"\"\n";
+ } else {
+ code += Indent + Indent + "return 0\n";
+ }
+ code += "\n";
+ }
+
+ // Begin the creator function signature.
+ void BeginBuilderArgs(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+
+ code += "\n";
+ code += "def Create" + NormalizedName(struct_def);
+ code += "(builder";
+ }
+
+ // Recursively generate arguments for a constructor, to deal with nested
+ // structs.
+ void StructBuilderArgs(const StructDef &struct_def,
+ const char *nameprefix, std::string *code_ptr) {
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ const auto &field_type = field.value.type;
+ const auto &type =
+ IsArray(field_type) ? field_type.VectorType() : field_type;
+ if (IsStruct(type)) {
+ // Generate arguments for a struct inside a struct. To ensure names
+ // don't clash, and to make it obvious these arguments are constructing
+ // a nested struct, prefix the name with the field name.
+ StructBuilderArgs(*field_type.struct_def,
+ (nameprefix + (NormalizedName(field) + "_")).c_str(),
+ code_ptr);
+ } else {
+ std::string &code = *code_ptr;
+ code += std::string(", ") + nameprefix;
+ code += MakeCamel(NormalizedName(field), false);
+ }
+ }
+ }
+
+ // End the creator function signature.
+ void EndBuilderArgs(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "):\n";
+ }
+
+ // Recursively generate struct construction statements and instert manual
+ // padding.
+ void StructBuilderBody(const StructDef &struct_def, const char *nameprefix,
+ std::string *code_ptr, size_t index = 0,
+ bool in_array = false) {
+ std::string &code = *code_ptr;
+ std::string indent(index * 4, ' ');
+ code +=
+ indent + " builder.Prep(" + NumToString(struct_def.minalign) + ", ";
+ code += NumToString(struct_def.bytesize) + ")\n";
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ auto &field = **it;
+ const auto &field_type = field.value.type;
+ const auto &type =
+ IsArray(field_type) ? field_type.VectorType() : field_type;
+ if (field.padding)
+ code +=
+ indent + " builder.Pad(" + NumToString(field.padding) + ")\n";
+ if (IsStruct(field_type)) {
+ StructBuilderBody(*field_type.struct_def,
+ (nameprefix + (NormalizedName(field) + "_")).c_str(),
+ code_ptr, index, in_array);
+ } else {
+ const auto index_var = "_idx" + NumToString(index);
+ if (IsArray(field_type)) {
+ code += indent + " for " + index_var + " in range(";
+ code += NumToString(field_type.fixed_length);
+ code += " , 0, -1):\n";
+ in_array = true;
+ }
+ if (IsStruct(type)) {
+ StructBuilderBody(
+ *field_type.struct_def,
+ (nameprefix + (NormalizedName(field) + "_")).c_str(), code_ptr,
+ index + 1, in_array);
+ } else {
+ code += IsArray(field_type) ? " " : "";
+ code += indent + " builder.Prepend" + GenMethod(field) + "(";
+ code += nameprefix + MakeCamel(NormalizedName(field), false);
+ size_t array_cnt = index + (IsArray(field_type) ? 1 : 0);
+ for (size_t i = 0; in_array && i < array_cnt; i++) {
+ code += "[_idx" + NumToString(i) + "-1]";
+ }
+ code += ")\n";
+ }
+ }
+ }
+ }
+
+ void EndBuilderBody(std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += " return builder.Offset()\n";
+ }
+
+ // Get the value of a table's starting offset.
+ void GetStartOfTable(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "def " + NormalizedName(struct_def) + "Start";
+ code += "(builder): ";
+ code += "builder.StartObject(";
+ code += NumToString(struct_def.fields.vec.size());
+ code += ")\n";
+ }
+
+ // Set the value of a table's field.
+ void BuildFieldOfTable(const StructDef &struct_def,
+ const FieldDef &field, const size_t offset,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "def " + NormalizedName(struct_def) + "Add" + MakeCamel(NormalizedName(field));
+ code += "(builder, ";
+ code += MakeCamel(NormalizedName(field), false);
+ code += "): ";
+ code += "builder.Prepend";
+ code += GenMethod(field) + "Slot(";
+ code += NumToString(offset) + ", ";
+ if (!IsScalar(field.value.type.base_type) && (!struct_def.fixed)) {
+ code += "flatbuffers.number_types.UOffsetTFlags.py_type";
+ code += "(";
+ code += MakeCamel(NormalizedName(field), false) + ")";
+ } else {
+ code += MakeCamel(NormalizedName(field), false);
+ }
+ code += ", ";
+ code += IsFloat(field.value.type.base_type)
+ ? float_const_gen_.GenFloatConstant(field)
+ : field.value.constant;
+ code += ")\n";
+ }
+
+ // Set the value of one of the members of a table's vector.
+ void BuildVectorOfTable(const StructDef &struct_def,
+ const FieldDef &field, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "def " + NormalizedName(struct_def) + "Start";
+ code += MakeCamel(NormalizedName(field));
+ code += "Vector(builder, numElems): return builder.StartVector(";
+ auto vector_type = field.value.type.VectorType();
+ auto alignment = InlineAlignment(vector_type);
+ auto elem_size = InlineSize(vector_type);
+ code += NumToString(elem_size);
+ code += ", numElems, " + NumToString(alignment);
+ code += ")\n";
+ }
+
+ // Get the offset of the end of a table.
+ void GetEndOffsetOnTable(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += "def " + NormalizedName(struct_def) + "End";
+ code += "(builder): ";
+ code += "return builder.EndObject()\n";
+ }
+
+ // Generate the receiver for function signatures.
+ void GenReceiver(const StructDef &struct_def, std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code += Indent + "# " + NormalizedName(struct_def) + "\n";
+ code += Indent + "def ";
+ }
+
+ // Generate a struct field, conditioned on its child type(s).
+ void GenStructAccessor(const StructDef &struct_def,
+ const FieldDef &field, std::string *code_ptr) {
+ GenComment(field.doc_comment, code_ptr, &def_comment, Indent.c_str());
+ if (IsScalar(field.value.type.base_type)) {
+ if (struct_def.fixed) {
+ GetScalarFieldOfStruct(struct_def, field, code_ptr);
+ } else {
+ GetScalarFieldOfTable(struct_def, field, code_ptr);
+ }
+ } else if (IsArray(field.value.type)) {
+ GetArrayOfStruct(struct_def, field, code_ptr);
+ } else {
+ switch (field.value.type.base_type) {
+ case BASE_TYPE_STRUCT:
+ if (struct_def.fixed) {
+ GetStructFieldOfStruct(struct_def, field, code_ptr);
+ } else {
+ GetStructFieldOfTable(struct_def, field, code_ptr);
+ }
+ break;
+ case BASE_TYPE_STRING: GetStringField(struct_def, field, code_ptr); break;
+ case BASE_TYPE_VECTOR: {
+ auto vectortype = field.value.type.VectorType();
+ if (vectortype.base_type == BASE_TYPE_STRUCT) {
+ GetMemberOfVectorOfStruct(struct_def, field, code_ptr);
+ } else {
+ GetMemberOfVectorOfNonStruct(struct_def, field, code_ptr);
+ GetVectorOfNonStructAsNumpy(struct_def, field, code_ptr);
+ }
+ break;
+ }
+ case BASE_TYPE_UNION: GetUnionField(struct_def, field, code_ptr); break;
+ default: FLATBUFFERS_ASSERT(0);
+ }
+ }
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ GetVectorLen(struct_def, field, code_ptr);
+ }
+ }
+
+ // Generate table constructors, conditioned on its members' types.
+ void GenTableBuilders(const StructDef &struct_def,
+ std::string *code_ptr) {
+ GetStartOfTable(struct_def, code_ptr);
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+
+ auto offset = it - struct_def.fields.vec.begin();
+ BuildFieldOfTable(struct_def, field, offset, code_ptr);
+ if (field.value.type.base_type == BASE_TYPE_VECTOR) {
+ BuildVectorOfTable(struct_def, field, code_ptr);
+ }
+ }
+
+ GetEndOffsetOnTable(struct_def, code_ptr);
+ }
+
+ // Generate function to check for proper file identifier
+ void GenHasFileIdentifier(const StructDef &struct_def,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ std::string escapedID;
+ // In the event any of file_identifier characters are special(NULL, \, etc),
+ // problems occur. To prevent this, convert all chars to their hex-escaped
+ // equivalent.
+ for (auto it = parser_.file_identifier_.begin();
+ it != parser_.file_identifier_.end(); ++it) {
+ escapedID += "\\x" + IntToStringHex(*it, 2);
+ }
+
+ code += Indent + "@classmethod\n";
+ code += Indent + "def " + NormalizedName(struct_def);
+ code += "BufferHasIdentifier(cls, buf, offset, size_prefixed=False):";
+ code += "\n";
+ code += Indent + Indent;
+ code += "return flatbuffers.util.BufferHasIdentifier(buf, offset, b\"";
+ code += escapedID;
+ code += "\", size_prefixed=size_prefixed)\n";
+ code += "\n";
+ }
+
+ // Generate struct or table methods.
+ void GenStruct(const StructDef &struct_def, std::string *code_ptr) {
+ if (struct_def.generated) return;
+
+ GenComment(struct_def.doc_comment, code_ptr, &def_comment);
+ BeginClass(struct_def, code_ptr);
+ if (!struct_def.fixed) {
+ // Generate a special accessor for the table that has been declared as
+ // the root type.
+ NewRootTypeFromBuffer(struct_def, code_ptr);
+ if (parser_.file_identifier_.length()){
+ // Generate a special function to test file_identifier
+ GenHasFileIdentifier(struct_def, code_ptr);
+ }
+ }
+ // Generate the Init method that sets the field in a pre-existing
+ // accessor object. This is to allow object reuse.
+ InitializeExisting(struct_def, code_ptr);
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ auto &field = **it;
+ if (field.deprecated) continue;
+
+ GenStructAccessor(struct_def, field, code_ptr);
+ }
+
+ if (struct_def.fixed) {
+ // create a struct constructor function
+ GenStructBuilder(struct_def, code_ptr);
+ } else {
+ // Create a set of functions that allow table construction.
+ GenTableBuilders(struct_def, code_ptr);
+ }
+ }
+
+ // Generate enum declarations.
+ void GenEnum(const EnumDef &enum_def, std::string *code_ptr) {
+ if (enum_def.generated) return;
+
+ GenComment(enum_def.doc_comment, code_ptr, &def_comment);
+ BeginEnum(NormalizedName(enum_def), code_ptr);
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ auto &ev = **it;
+ GenComment(ev.doc_comment, code_ptr, &def_comment, Indent.c_str());
+ EnumMember(enum_def, ev, code_ptr);
+ }
+ EndEnum(code_ptr);
+ }
+
+ // Returns the function name that is able to read a value of the given type.
+ std::string GenGetter(const Type &type) {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return "self._tab.String(";
+ case BASE_TYPE_UNION: return "self._tab.Union(";
+ case BASE_TYPE_VECTOR: return GenGetter(type.VectorType());
+ default:
+ return "self._tab.Get(flatbuffers.number_types." +
+ MakeCamel(GenTypeGet(type)) + "Flags, ";
+ }
+ }
+
+ // Returns the method name for use with add/put calls.
+ std::string GenMethod(const FieldDef &field) {
+ return (IsScalar(field.value.type.base_type) || IsArray(field.value.type))
+ ? MakeCamel(GenTypeBasic(field.value.type))
+ : (IsStruct(field.value.type) ? "Struct" : "UOffsetTRelative");
+ }
+
+ std::string GenTypeBasic(const Type &type) {
+ static const char *ctypename[] = {
+ // clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ #PTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ };
+ return ctypename[IsArray(type) ? type.VectorType().base_type
+ : type.base_type];
+ }
+
+ std::string GenTypePointer(const Type &type) {
+ switch (type.base_type) {
+ case BASE_TYPE_STRING: return "string";
+ case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
+ case BASE_TYPE_STRUCT: return type.struct_def->name;
+ case BASE_TYPE_UNION:
+ // fall through
+ default: return "*flatbuffers.Table";
+ }
+ }
+
+ std::string GenTypeGet(const Type &type) {
+ return IsScalar(type.base_type) ? GenTypeBasic(type) : GenTypePointer(type);
+ }
+
+ std::string TypeName(const FieldDef &field) {
+ return GenTypeGet(field.value.type);
+ }
+
+ // Create a struct with a builder and the struct's arguments.
+ void GenStructBuilder(const StructDef &struct_def,
+ std::string *code_ptr) {
+ BeginBuilderArgs(struct_def, code_ptr);
+ StructBuilderArgs(struct_def, "", code_ptr);
+ EndBuilderArgs(code_ptr);
+
+ StructBuilderBody(struct_def, "", code_ptr);
+ EndBuilderBody(code_ptr);
+ }
+
+ bool generate() {
+ if (!generateEnums()) return false;
+ if (!generateStructs()) return false;
+ return true;
+ }
+
+ private:
+ bool generateEnums() {
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ auto &enum_def = **it;
+ std::string enumcode;
+ GenEnum(enum_def, &enumcode);
+ if (!SaveType(enum_def, enumcode, false)) return false;
+ }
+ return true;
+ }
+
+ bool generateStructs() {
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ auto &struct_def = **it;
+ std::string declcode;
+ GenStruct(struct_def, &declcode);
+ if (!SaveType(struct_def, declcode, true)) return false;
+ }
+ return true;
+ }
+
+ // Begin by declaring namespace and imports.
+ void BeginFile(const std::string &name_space_name, const bool needs_imports,
+ std::string *code_ptr) {
+ std::string &code = *code_ptr;
+ code = code + "# " + FlatBuffersGeneratedWarning() + "\n\n";
+ code += "# namespace: " + name_space_name + "\n\n";
+ if (needs_imports) { code += "import flatbuffers\n\n"; }
+ }
+
+ // Save out the generated code for a Python Table type.
+ bool SaveType(const Definition &def, const std::string &classcode,
+ bool needs_imports) {
+ if (!classcode.length()) return true;
+
+ std::string namespace_dir = path_;
+ auto &namespaces = def.defined_namespace->components;
+ for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
+ if (it != namespaces.begin()) namespace_dir += kPathSeparator;
+ namespace_dir += *it;
+ std::string init_py_filename = namespace_dir + "/__init__.py";
+ SaveFile(init_py_filename.c_str(), "", false);
+ }
+
+ std::string code = "";
+ BeginFile(LastNamespacePart(*def.defined_namespace), needs_imports, &code);
+ code += classcode;
+ std::string filename =
+ NamespaceDir(*def.defined_namespace) + NormalizedName(def) + ".py";
+ return SaveFile(filename.c_str(), code, false);
+ }
+ private:
+ std::unordered_set<std::string> keywords_;
+ const SimpleFloatConstantGenerator float_const_gen_;
+};
+
+} // namespace python
+
+bool GeneratePython(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ python::PythonGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+
+} // namespace flatbuffers
diff --git a/src/idl_gen_rust.cpp b/src/idl_gen_rust.cpp
new file mode 100644
index 0000000..936ac83
--- /dev/null
+++ b/src/idl_gen_rust.cpp
@@ -0,0 +1,1825 @@
+/*
+ * Copyright 2018 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.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+
+#include "flatbuffers/code_generators.h"
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+
+static std::string GeneratedFileName(const std::string &path,
+ const std::string &file_name) {
+ return path + file_name + "_generated.rs";
+}
+
+// Convert a camelCaseIdentifier or CamelCaseIdentifier to a
+// snake_case_indentifier.
+std::string MakeSnakeCase(const std::string &in) {
+ std::string s;
+ for (size_t i = 0; i < in.length(); i++) {
+ if (i == 0) {
+ s += static_cast<char>(tolower(in[0]));
+ } else if (in[i] == '_') {
+ s += '_';
+ } else if (!islower(in[i])) {
+ // Prevent duplicate underscores for Upper_Snake_Case strings
+ // and UPPERCASE strings.
+ if (islower(in[i - 1])) {
+ s += '_';
+ }
+ s += static_cast<char>(tolower(in[i]));
+ } else {
+ s += in[i];
+ }
+ }
+ return s;
+}
+
+// Convert a string to all uppercase.
+std::string MakeUpper(const std::string &in) {
+ std::string s;
+ for (size_t i = 0; i < in.length(); i++) {
+ s += static_cast<char>(toupper(in[i]));
+ }
+ return s;
+}
+
+// Encapsulate all logical field types in this enum. This allows us to write
+// field logic based on type switches, instead of branches on the properties
+// set on the Type.
+// TODO(rw): for backwards compatibility, we can't use a strict `enum class`
+// declaration here. could we use the `-Wswitch-enum` warning to
+// achieve the same effect?
+enum FullType {
+ ftInteger = 0,
+ ftFloat = 1,
+ ftBool = 2,
+
+ ftStruct = 3,
+ ftTable = 4,
+
+ ftEnumKey = 5,
+ ftUnionKey = 6,
+
+ ftUnionValue = 7,
+
+ // TODO(rw): bytestring?
+ ftString = 8,
+
+ ftVectorOfInteger = 9,
+ ftVectorOfFloat = 10,
+ ftVectorOfBool = 11,
+ ftVectorOfEnumKey = 12,
+ ftVectorOfStruct = 13,
+ ftVectorOfTable = 14,
+ ftVectorOfString = 15,
+ ftVectorOfUnionValue = 16,
+};
+
+// Convert a Type to a FullType (exhaustive).
+FullType GetFullType(const Type &type) {
+ // N.B. The order of these conditionals matters for some types.
+
+ if (type.base_type == BASE_TYPE_STRING) {
+ return ftString;
+ } else if (type.base_type == BASE_TYPE_STRUCT) {
+ if (type.struct_def->fixed) {
+ return ftStruct;
+ } else {
+ return ftTable;
+ }
+ } else if (type.base_type == BASE_TYPE_VECTOR) {
+ switch (GetFullType(type.VectorType())) {
+ case ftInteger: {
+ return ftVectorOfInteger;
+ }
+ case ftFloat: {
+ return ftVectorOfFloat;
+ }
+ case ftBool: {
+ return ftVectorOfBool;
+ }
+ case ftStruct: {
+ return ftVectorOfStruct;
+ }
+ case ftTable: {
+ return ftVectorOfTable;
+ }
+ case ftString: {
+ return ftVectorOfString;
+ }
+ case ftEnumKey: {
+ return ftVectorOfEnumKey;
+ }
+ case ftUnionKey:
+ case ftUnionValue: {
+ FLATBUFFERS_ASSERT(false && "vectors of unions are unsupported");
+ break;
+ }
+ default: {
+ FLATBUFFERS_ASSERT(false && "vector of vectors are unsupported");
+ }
+ }
+ } else if (type.enum_def != nullptr) {
+ if (type.enum_def->is_union) {
+ if (type.base_type == BASE_TYPE_UNION) {
+ return ftUnionValue;
+ } else if (IsInteger(type.base_type)) {
+ return ftUnionKey;
+ } else {
+ FLATBUFFERS_ASSERT(false && "unknown union field type");
+ }
+ } else {
+ return ftEnumKey;
+ }
+ } else if (IsScalar(type.base_type)) {
+ if (IsBool(type.base_type)) {
+ return ftBool;
+ } else if (IsInteger(type.base_type)) {
+ return ftInteger;
+ } else if (IsFloat(type.base_type)) {
+ return ftFloat;
+ } else {
+ FLATBUFFERS_ASSERT(false && "unknown number type");
+ }
+ }
+
+ FLATBUFFERS_ASSERT(false && "completely unknown type");
+
+ // this is only to satisfy the compiler's return analysis.
+ return ftBool;
+}
+
+// If the second parameter is false then wrap the first with Option<...>
+std::string WrapInOptionIfNotRequired(std::string s, bool required) {
+ if (required) {
+ return s;
+ } else {
+ return "Option<" + s + ">";
+ }
+}
+
+// If the second parameter is false then add .unwrap()
+std::string AddUnwrapIfRequired(std::string s, bool required) {
+ if (required) {
+ return s + ".unwrap()";
+ } else {
+ return s;
+ }
+}
+
+namespace rust {
+
+class RustGenerator : public BaseGenerator {
+ public:
+ RustGenerator(const Parser &parser, const std::string &path,
+ const std::string &file_name)
+ : BaseGenerator(parser, path, file_name, "", "::"),
+ cur_name_space_(nullptr) {
+ const char *keywords[] = {
+ // list taken from:
+ // https://doc.rust-lang.org/book/second-edition/appendix-01-keywords.html
+ //
+ // we write keywords one per line so that we can easily compare them with
+ // changes to that webpage in the future.
+
+ // currently-used keywords
+ "as",
+ "break",
+ "const",
+ "continue",
+ "crate",
+ "else",
+ "enum",
+ "extern",
+ "false",
+ "fn",
+ "for",
+ "if",
+ "impl",
+ "in",
+ "let",
+ "loop",
+ "match",
+ "mod",
+ "move",
+ "mut",
+ "pub",
+ "ref",
+ "return",
+ "Self",
+ "self",
+ "static",
+ "struct",
+ "super",
+ "trait",
+ "true",
+ "type",
+ "unsafe",
+ "use",
+ "where",
+ "while",
+
+ // future possible keywords
+ "abstract",
+ "alignof",
+ "become",
+ "box",
+ "do",
+ "final",
+ "macro",
+ "offsetof",
+ "override",
+ "priv",
+ "proc",
+ "pure",
+ "sizeof",
+ "typeof",
+ "unsized",
+ "virtual",
+ "yield",
+
+ // other rust terms we should not use
+ "std",
+ "usize",
+ "isize",
+ "u8",
+ "i8",
+ "u16",
+ "i16",
+ "u32",
+ "i32",
+ "u64",
+ "i64",
+ "u128",
+ "i128",
+ "f32",
+ "f64",
+
+ // These are terms the code generator can implement on types.
+ //
+ // In Rust, the trait resolution rules (as described at
+ // https://github.com/rust-lang/rust/issues/26007) mean that, as long
+ // as we impl table accessors as inherent methods, we'll never create
+ // conflicts with these keywords. However, that's a fairly nuanced
+ // implementation detail, and how we implement methods could change in
+ // the future. as a result, we proactively block these out as reserved
+ // words.
+ "follow",
+ "push",
+ "size",
+ "alignment",
+ "to_little_endian",
+ "from_little_endian",
+ nullptr };
+ for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
+ }
+
+ // Iterate through all definitions we haven't generated code for (enums,
+ // structs, and tables) and output them to a single file.
+ bool generate() {
+ code_.Clear();
+ code_ += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
+
+ assert(!cur_name_space_);
+
+ // Generate imports for the global scope in case no namespace is used
+ // in the schema file.
+ GenNamespaceImports(0);
+ code_ += "";
+
+ // Generate all code in their namespaces, once, because Rust does not
+ // permit re-opening modules.
+ //
+ // TODO(rw): Use a set data structure to reduce namespace evaluations from
+ // O(n**2) to O(n).
+ for (auto ns_it = parser_.namespaces_.begin();
+ ns_it != parser_.namespaces_.end();
+ ++ns_it) {
+ const auto &ns = *ns_it;
+
+ // Generate code for all the enum declarations.
+ for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
+ ++it) {
+ const auto &enum_def = **it;
+ if (enum_def.defined_namespace != ns) { continue; }
+ if (!enum_def.generated) {
+ SetNameSpace(enum_def.defined_namespace);
+ GenEnum(enum_def);
+ }
+ }
+
+ // Generate code for all structs.
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ const auto &struct_def = **it;
+ if (struct_def.defined_namespace != ns) { continue; }
+ if (struct_def.fixed && !struct_def.generated) {
+ SetNameSpace(struct_def.defined_namespace);
+ GenStruct(struct_def);
+ }
+ }
+
+ // Generate code for all tables.
+ for (auto it = parser_.structs_.vec.begin();
+ it != parser_.structs_.vec.end(); ++it) {
+ const auto &struct_def = **it;
+ if (struct_def.defined_namespace != ns) { continue; }
+ if (!struct_def.fixed && !struct_def.generated) {
+ SetNameSpace(struct_def.defined_namespace);
+ GenTable(struct_def);
+ }
+ }
+
+ // Generate global helper functions.
+ if (parser_.root_struct_def_) {
+ auto &struct_def = *parser_.root_struct_def_;
+ if (struct_def.defined_namespace != ns) { continue; }
+ SetNameSpace(struct_def.defined_namespace);
+ GenRootTableFuncs(struct_def);
+ }
+ }
+ if (cur_name_space_) SetNameSpace(nullptr);
+
+ const auto file_path = GeneratedFileName(path_, file_name_);
+ const auto final_code = code_.ToString();
+ return SaveFile(file_path.c_str(), final_code, false);
+ }
+
+ private:
+ CodeWriter code_;
+
+ std::set<std::string> keywords_;
+
+ // This tracks the current namespace so we can insert namespace declarations.
+ const Namespace *cur_name_space_;
+
+ const Namespace *CurrentNameSpace() const { return cur_name_space_; }
+
+ // Determine if a Type needs a lifetime template parameter when used in the
+ // Rust builder args.
+ bool TableBuilderTypeNeedsLifetime(const Type &type) const {
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool:
+ case ftEnumKey:
+ case ftUnionKey:
+ case ftUnionValue: { return false; }
+ default: { return true; }
+ }
+ }
+
+ // Determine if a table args rust type needs a lifetime template parameter.
+ bool TableBuilderArgsNeedsLifetime(const StructDef &struct_def) const {
+ FLATBUFFERS_ASSERT(!struct_def.fixed);
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated) {
+ continue;
+ }
+
+ if (TableBuilderTypeNeedsLifetime(field.value.type)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Determine if a Type needs to be copied (for endian safety) when used in a
+ // Struct.
+ bool StructMemberAccessNeedsCopy(const Type &type) const {
+ switch (GetFullType(type)) {
+ case ftInteger: // requires endian swap
+ case ftFloat: // requires endian swap
+ case ftBool: // no endian-swap, but do the copy for UX consistency
+ case ftEnumKey: { return true; } // requires endian swap
+ case ftStruct: { return false; } // no endian swap
+ default: {
+ // logic error: no other types can be struct members.
+ FLATBUFFERS_ASSERT(false && "invalid struct member type");
+ return false; // only to satisfy compiler's return analysis
+ }
+ }
+ }
+
+ std::string EscapeKeyword(const std::string &name) const {
+ return keywords_.find(name) == keywords_.end() ? name : name + "_";
+ }
+
+ std::string Name(const Definition &def) const {
+ return EscapeKeyword(def.name);
+ }
+
+ std::string Name(const EnumVal &ev) const { return EscapeKeyword(ev.name); }
+
+ std::string WrapInNameSpace(const Definition &def) const {
+ return WrapInNameSpace(def.defined_namespace, Name(def));
+ }
+ std::string WrapInNameSpace(const Namespace *ns,
+ const std::string &name) const {
+ if (CurrentNameSpace() == ns) return name;
+ std::string prefix = GetRelativeNamespaceTraversal(CurrentNameSpace(), ns);
+ return prefix + name;
+ }
+
+ // Determine the namespace traversal needed from the Rust crate root.
+ // This may be useful in the future for referring to included files, but is
+ // currently unused.
+ std::string GetAbsoluteNamespaceTraversal(const Namespace *dst) const {
+ std::stringstream stream;
+
+ stream << "::";
+ for (auto d = dst->components.begin(); d != dst->components.end(); ++d) {
+ stream << MakeSnakeCase(*d) + "::";
+ }
+ return stream.str();
+ }
+
+ // Determine the relative namespace traversal needed to reference one
+ // namespace from another namespace. This is useful because it does not force
+ // the user to have a particular file layout. (If we output absolute
+ // namespace paths, that may require users to organize their Rust crates in a
+ // particular way.)
+ std::string GetRelativeNamespaceTraversal(const Namespace *src,
+ const Namespace *dst) const {
+ // calculate the path needed to reference dst from src.
+ // example: f(A::B::C, A::B::C) -> (none)
+ // example: f(A::B::C, A::B) -> super::
+ // example: f(A::B::C, A::B::D) -> super::D
+ // example: f(A::B::C, A) -> super::super::
+ // example: f(A::B::C, D) -> super::super::super::D
+ // example: f(A::B::C, D::E) -> super::super::super::D::E
+ // example: f(A, D::E) -> super::D::E
+ // does not include leaf object (typically a struct type).
+
+ size_t i = 0;
+ std::stringstream stream;
+
+ auto s = src->components.begin();
+ auto d = dst->components.begin();
+ for(;;) {
+ if (s == src->components.end()) { break; }
+ if (d == dst->components.end()) { break; }
+ if (*s != *d) { break; }
+ ++s;
+ ++d;
+ ++i;
+ }
+
+ for (; s != src->components.end(); ++s) {
+ stream << "super::";
+ }
+ for (; d != dst->components.end(); ++d) {
+ stream << MakeSnakeCase(*d) + "::";
+ }
+ return stream.str();
+ }
+
+ // Generate a comment from the schema.
+ void GenComment(const std::vector<std::string> &dc, const char *prefix = "") {
+ std::string text;
+ ::flatbuffers::GenComment(dc, &text, nullptr, prefix);
+ code_ += text + "\\";
+ }
+
+ // Return a Rust type from the table in idl.h.
+ std::string GetTypeBasic(const Type &type) const {
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool:
+ case ftEnumKey:
+ case ftUnionKey: { break; }
+ default: { FLATBUFFERS_ASSERT(false && "incorrect type given");}
+ }
+
+ // clang-format off
+ static const char * const ctypename[] = {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
+ RTYPE, KTYPE) \
+ #RTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ };
+
+ if (type.enum_def) { return WrapInNameSpace(*type.enum_def); }
+ return ctypename[type.base_type];
+ }
+
+ // Look up the native type for an enum. This will always be an integer like
+ // u8, i32, etc.
+ std::string GetEnumTypeForDecl(const Type &type) {
+ const auto ft = GetFullType(type);
+ if (!(ft == ftEnumKey || ft == ftUnionKey)) {
+ FLATBUFFERS_ASSERT(false && "precondition failed in GetEnumTypeForDecl");
+ }
+
+ static const char *ctypename[] = {
+ // clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, \
+ RTYPE, KTYPE) \
+ #RTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ };
+
+ // Enums can be bools, but their Rust representation must be a u8, as used
+ // in the repr attribute (#[repr(bool)] is an invalid attribute).
+ if (type.base_type == BASE_TYPE_BOOL) return "u8";
+ return ctypename[type.base_type];
+ }
+
+ // Return a Rust type for any type (scalar, table, struct) specifically for
+ // using a FlatBuffer.
+ std::string GetTypeGet(const Type &type) const {
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool:
+ case ftEnumKey:
+ case ftUnionKey: {
+ return GetTypeBasic(type); }
+ case ftTable: {
+ return WrapInNameSpace(type.struct_def->defined_namespace,
+ type.struct_def->name) + "<'a>"; }
+ default: {
+ return WrapInNameSpace(type.struct_def->defined_namespace,
+ type.struct_def->name); }
+ }
+ }
+
+ std::string GetEnumValUse(const EnumDef &enum_def,
+ const EnumVal &enum_val) const {
+ return Name(enum_def) + "::" + Name(enum_val);
+ }
+
+ // Generate an enum declaration,
+ // an enum string lookup table,
+ // an enum match function,
+ // and an enum array of values
+ void GenEnum(const EnumDef &enum_def) {
+ code_.SetValue("ENUM_NAME", Name(enum_def));
+ code_.SetValue("BASE_TYPE", GetEnumTypeForDecl(enum_def.underlying_type));
+
+ GenComment(enum_def.doc_comment);
+ code_ += "#[allow(non_camel_case_types)]";
+ code_ += "#[repr({{BASE_TYPE}})]";
+ code_ += "#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]";
+ code_ += "pub enum " + Name(enum_def) + " {";
+
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ const auto &ev = **it;
+
+ GenComment(ev.doc_comment, " ");
+ code_.SetValue("KEY", Name(ev));
+ code_.SetValue("VALUE", enum_def.ToString(ev));
+ code_ += " {{KEY}} = {{VALUE}},";
+ }
+ const EnumVal *minv = enum_def.MinValue();
+ const EnumVal *maxv = enum_def.MaxValue();
+ FLATBUFFERS_ASSERT(minv && maxv);
+
+ code_ += "";
+ code_ += "}";
+ code_ += "";
+
+ code_.SetValue("ENUM_NAME", Name(enum_def));
+ code_.SetValue("ENUM_NAME_SNAKE", MakeSnakeCase(Name(enum_def)));
+ code_.SetValue("ENUM_NAME_CAPS", MakeUpper(MakeSnakeCase(Name(enum_def))));
+ code_.SetValue("ENUM_MIN_BASE_VALUE", enum_def.ToString(*minv));
+ code_.SetValue("ENUM_MAX_BASE_VALUE", enum_def.ToString(*maxv));
+
+ // Generate enum constants, and impls for Follow, EndianScalar, and Push.
+ code_ += "const ENUM_MIN_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\";
+ code_ += "{{ENUM_MIN_BASE_VALUE}};";
+ code_ += "const ENUM_MAX_{{ENUM_NAME_CAPS}}: {{BASE_TYPE}} = \\";
+ code_ += "{{ENUM_MAX_BASE_VALUE}};";
+ code_ += "";
+ code_ += "impl<'a> flatbuffers::Follow<'a> for {{ENUM_NAME}} {";
+ code_ += " type Inner = Self;";
+ code_ += " #[inline]";
+ code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
+ code_ += " flatbuffers::read_scalar_at::<Self>(buf, loc)";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ code_ += "impl flatbuffers::EndianScalar for {{ENUM_NAME}} {";
+ code_ += " #[inline]";
+ code_ += " fn to_little_endian(self) -> Self {";
+ code_ += " let n = {{BASE_TYPE}}::to_le(self as {{BASE_TYPE}});";
+ code_ += " let p = &n as *const {{BASE_TYPE}} as *const {{ENUM_NAME}};";
+ code_ += " unsafe { *p }";
+ code_ += " }";
+ code_ += " #[inline]";
+ code_ += " fn from_little_endian(self) -> Self {";
+ code_ += " let n = {{BASE_TYPE}}::from_le(self as {{BASE_TYPE}});";
+ code_ += " let p = &n as *const {{BASE_TYPE}} as *const {{ENUM_NAME}};";
+ code_ += " unsafe { *p }";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ code_ += "impl flatbuffers::Push for {{ENUM_NAME}} {";
+ code_ += " type Output = {{ENUM_NAME}};";
+ code_ += " #[inline]";
+ code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
+ code_ += " flatbuffers::emplace_scalar::<{{ENUM_NAME}}>"
+ "(dst, *self);";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+
+ // Generate an array of all enumeration values.
+ auto num_fields = NumToString(enum_def.size());
+ code_ += "#[allow(non_camel_case_types)]";
+ code_ += "const ENUM_VALUES_{{ENUM_NAME_CAPS}}:[{{ENUM_NAME}}; " +
+ num_fields + "] = [";
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
+ const auto &ev = **it;
+ auto value = GetEnumValUse(enum_def, ev);
+ auto suffix = *it != enum_def.Vals().back() ? "," : "";
+ code_ += " " + value + suffix;
+ }
+ code_ += "];";
+ code_ += "";
+
+ // Generate a 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
+ // the moment we simply don't output a table at all.
+ auto range = enum_def.Distance();
+ // Average distance between values above which we consider a table
+ // "too sparse". Change at will.
+ static const uint64_t kMaxSparseness = 5;
+ if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
+ code_ += "#[allow(non_camel_case_types)]";
+ code_ += "const ENUM_NAMES_{{ENUM_NAME_CAPS}}:[&'static str; " +
+ NumToString(range + 1) + "] = [";
+
+ auto val = enum_def.Vals().front();
+ for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end();
+ ++it) {
+ auto ev = *it;
+ for (auto k = enum_def.Distance(val, ev); k > 1; --k) {
+ code_ += " \"\",";
+ }
+ val = ev;
+ auto suffix = *it != enum_def.Vals().back() ? "," : "";
+ code_ += " \"" + Name(*ev) + "\"" + suffix;
+ }
+ code_ += "];";
+ code_ += "";
+
+ code_ +=
+ "pub fn enum_name_{{ENUM_NAME_SNAKE}}(e: {{ENUM_NAME}}) -> "
+ "&'static str {";
+
+ code_ += " let index = e as {{BASE_TYPE}}\\";
+ if (enum_def.MinValue()->IsNonZero()) {
+ auto vals = GetEnumValUse(enum_def, *enum_def.MinValue());
+ code_ += " - " + vals + " as {{BASE_TYPE}}\\";
+ }
+ code_ += ";";
+
+ code_ += " ENUM_NAMES_{{ENUM_NAME_CAPS}}[index as usize]";
+ code_ += "}";
+ code_ += "";
+ }
+
+ if (enum_def.is_union) {
+ // Generate tyoesafe offset(s) for unions
+ code_.SetValue("NAME", Name(enum_def));
+ code_.SetValue("UNION_OFFSET_NAME", Name(enum_def) + "UnionTableOffset");
+ code_ += "pub struct {{UNION_OFFSET_NAME}} {}";
+ }
+ }
+
+ std::string GetFieldOffsetName(const FieldDef &field) {
+ return "VT_" + MakeUpper(Name(field));
+ }
+
+ std::string GetDefaultConstant(const FieldDef &field) {
+ return field.value.type.base_type == BASE_TYPE_FLOAT
+ ? field.value.constant + ""
+ : field.value.constant;
+ }
+
+ std::string GetDefaultScalarValue(const FieldDef &field) {
+ switch (GetFullType(field.value.type)) {
+ case ftInteger: { return GetDefaultConstant(field); }
+ case ftFloat: { return GetDefaultConstant(field); }
+ case ftBool: {
+ return field.value.constant == "0" ? "false" : "true";
+ }
+ case ftUnionKey:
+ case ftEnumKey: {
+ auto ev = field.value.type.enum_def->FindByValue(field.value.constant);
+ assert(ev);
+ return WrapInNameSpace(field.value.type.enum_def->defined_namespace,
+ GetEnumValUse(*field.value.type.enum_def, *ev));
+ }
+
+ // All pointer-ish types have a default value of None, because they are
+ // wrapped in Option.
+ default: { return "None"; }
+ }
+ }
+
+ // Create the return type for fields in the *BuilderArgs structs that are
+ // used to create Tables.
+ //
+ // Note: we could make all inputs to the BuilderArgs be an Option, as well
+ // as all outputs. But, the UX of Flatbuffers is that the user doesn't get to
+ // know if the value is default or not, because there are three ways to
+ // return a default value:
+ // 1) return a stored value that happens to be the default,
+ // 2) return a hardcoded value because the relevant vtable field is not in
+ // the vtable, or
+ // 3) return a hardcoded value because the vtable field value is set to zero.
+ std::string TableBuilderArgsDefnType(const FieldDef &field,
+ const std::string &lifetime) {
+ const Type& type = field.value.type;
+
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool: {
+ const auto typname = GetTypeBasic(type);
+ return typname;
+ }
+ case ftStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option<&" + lifetime + " " + typname + ">";
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option<flatbuffers::WIPOffset<" + typname + "<" + lifetime + \
+ ">>>";
+ }
+ case ftString: {
+ return "Option<flatbuffers::WIPOffset<&" + lifetime + " str>>";
+ }
+ case ftEnumKey:
+ case ftUnionKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return typname;
+ }
+ case ftUnionValue: {
+ return "Option<flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>>";
+ }
+
+ case ftVectorOfInteger:
+ case ftVectorOfFloat: {
+ const auto typname = GetTypeBasic(type.VectorType());
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", " + typname + ">>>";
+ }
+ case ftVectorOfBool: {
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", bool>>>";
+ }
+ case ftVectorOfEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", " + typname + ">>>";
+ }
+ case ftVectorOfStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", " + typname + ">>>";
+ }
+ case ftVectorOfTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", flatbuffers::ForwardsUOffset<" + typname + \
+ "<" + lifetime + ">>>>>";
+ }
+ case ftVectorOfString: {
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", flatbuffers::ForwardsUOffset<&" + lifetime + \
+ " str>>>>";
+ }
+ case ftVectorOfUnionValue: {
+ const auto typname = WrapInNameSpace(*type.enum_def) + \
+ "UnionTableOffset";
+ return "Option<flatbuffers::WIPOffset<flatbuffers::Vector<" + \
+ lifetime + ", flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Table<" + lifetime + ">>>>";
+ }
+ }
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ std::string TableBuilderArgsDefaultValue(const FieldDef &field) {
+ return GetDefaultScalarValue(field);
+ }
+ std::string TableBuilderAddFuncDefaultValue(const FieldDef &field) {
+ // All branches of switch do the same action!
+ switch (GetFullType(field.value.type)) {
+ case ftUnionKey:
+ case ftEnumKey: {
+ const std::string basetype = GetTypeBasic(field.value.type); //<- never used
+ return GetDefaultScalarValue(field);
+ }
+
+ default: { return GetDefaultScalarValue(field); }
+ }
+ }
+
+ std::string TableBuilderArgsAddFuncType(const FieldDef &field,
+ const std::string &lifetime) {
+ const Type& type = field.value.type;
+
+ switch (GetFullType(field.value.type)) {
+ case ftVectorOfStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", " + typname + ">>";
+ }
+ case ftVectorOfTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", flatbuffers::ForwardsUOffset<" + typname + \
+ "<" + lifetime + ">>>>";
+ }
+ case ftVectorOfInteger:
+ case ftVectorOfFloat: {
+ const auto typname = GetTypeBasic(type.VectorType());
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", " + typname + ">>";
+ }
+ case ftVectorOfBool: {
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", bool>>";
+ }
+ case ftVectorOfString: {
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", flatbuffers::ForwardsUOffset<&" + lifetime + " str>>>";
+ }
+ case ftVectorOfEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", " + typname + ">>";
+ }
+ case ftVectorOfUnionValue: {
+ return "flatbuffers::WIPOffset<flatbuffers::Vector<" + lifetime + \
+ ", flatbuffers::ForwardsUOffset<flatbuffers::Table<" + \
+ lifetime + ">>>";
+ }
+ case ftEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return typname;
+ }
+ case ftStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "&" + lifetime + " " + typname + "";
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "flatbuffers::WIPOffset<" + typname + "<" + lifetime + ">>";
+ }
+ case ftInteger:
+ case ftFloat: {
+ const auto typname = GetTypeBasic(type);
+ return typname;
+ }
+ case ftBool: {
+ return "bool";
+ }
+ case ftString: {
+ return "flatbuffers::WIPOffset<&" + lifetime + " str>";
+ }
+ case ftUnionKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return typname;
+ }
+ case ftUnionValue: {
+ return "flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>";
+ }
+ }
+
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ std::string TableBuilderArgsAddFuncBody(const FieldDef &field) {
+ const Type& type = field.value.type;
+
+ switch (GetFullType(field.value.type)) {
+ case ftInteger:
+ case ftFloat: {
+ const auto typname = GetTypeBasic(field.value.type);
+ return "self.fbb_.push_slot::<" + typname + ">";
+ }
+ case ftBool: {
+ return "self.fbb_.push_slot::<bool>";
+ }
+
+ case ftEnumKey:
+ case ftUnionKey: {
+ const auto underlying_typname = GetTypeBasic(type);
+ return "self.fbb_.push_slot::<" + underlying_typname + ">";
+ }
+
+ case ftStruct: {
+ const std::string typname = WrapInNameSpace(*type.struct_def);
+ return "self.fbb_.push_slot_always::<&" + typname + ">";
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return "self.fbb_.push_slot_always::<flatbuffers::WIPOffset<" + \
+ typname + ">>";
+ }
+
+ case ftUnionValue:
+ case ftString:
+ case ftVectorOfInteger:
+ case ftVectorOfFloat:
+ case ftVectorOfBool:
+ case ftVectorOfEnumKey:
+ case ftVectorOfStruct:
+ case ftVectorOfTable:
+ case ftVectorOfString:
+ case ftVectorOfUnionValue: {
+ return "self.fbb_.push_slot_always::<flatbuffers::WIPOffset<_>>";
+ }
+ }
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ std::string GenTableAccessorFuncReturnType(const FieldDef &field,
+ const std::string &lifetime) {
+ const Type& type = field.value.type;
+
+ switch (GetFullType(field.value.type)) {
+ case ftInteger:
+ case ftFloat: {
+ const auto typname = GetTypeBasic(type);
+ return typname;
+ }
+ case ftBool: {
+ return "bool";
+ }
+ case ftStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return WrapInOptionIfNotRequired("&" + lifetime + " " + typname, field.required);
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return WrapInOptionIfNotRequired(typname + "<" + lifetime + ">", field.required);
+ }
+ case ftEnumKey:
+ case ftUnionKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return typname;
+ }
+
+ case ftUnionValue: {
+ return WrapInOptionIfNotRequired("flatbuffers::Table<" + lifetime + ">", field.required);
+ }
+ case ftString: {
+ return WrapInOptionIfNotRequired("&" + lifetime + " str", field.required);
+ }
+ case ftVectorOfInteger:
+ case ftVectorOfFloat: {
+ const auto typname = GetTypeBasic(type.VectorType());
+ if (IsOneByte(type.VectorType().base_type)) {
+ return WrapInOptionIfNotRequired("&" + lifetime + " [" + typname + "]", field.required);
+ }
+ return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime + ", " + typname + ">", field.required);
+ }
+ case ftVectorOfBool: {
+ return WrapInOptionIfNotRequired("&" + lifetime + " [bool]", field.required);
+ }
+ case ftVectorOfEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime + ", " + typname + ">", field.required);
+ }
+ case ftVectorOfStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return WrapInOptionIfNotRequired("&" + lifetime + " [" + typname + "]", field.required);
+ }
+ case ftVectorOfTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime + ", flatbuffers::ForwardsUOffset<" + \
+ typname + "<" + lifetime + ">>>", field.required);
+ }
+ case ftVectorOfString: {
+ return WrapInOptionIfNotRequired("flatbuffers::Vector<" + lifetime + ", flatbuffers::ForwardsUOffset<&" + \
+ lifetime + " str>>", field.required);
+ }
+ case ftVectorOfUnionValue: {
+ FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported");
+ // TODO(rw): when we do support these, we should consider using the
+ // Into trait to convert tables to typesafe union values.
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+ }
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ std::string GenTableAccessorFuncBody(const FieldDef &field,
+ const std::string &lifetime,
+ const std::string &offset_prefix) {
+ const std::string offset_name = offset_prefix + "::" + \
+ GetFieldOffsetName(field);
+ const Type& type = field.value.type;
+
+ switch (GetFullType(field.value.type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool: {
+ const auto typname = GetTypeBasic(type);
+ const auto default_value = GetDefaultScalarValue(field);
+ return "self._tab.get::<" + typname + ">(" + offset_name + ", Some(" + \
+ default_value + ")).unwrap()";
+ }
+ case ftStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return AddUnwrapIfRequired("self._tab.get::<" + typname + ">(" + offset_name + ", None)", field.required);
+ }
+ case ftTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<" + \
+ typname + "<" + lifetime + ">>>(" + offset_name + ", None)", field.required);
+ }
+ case ftUnionValue: {
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Table<" + lifetime + ">>>(" + offset_name + \
+ ", None)", field.required);
+ }
+ case ftUnionKey:
+ case ftEnumKey: {
+ const auto underlying_typname = GetTypeBasic(type); //<- never used
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ const auto default_value = GetDefaultScalarValue(field);
+ return "self._tab.get::<" + typname + ">(" + offset_name + \
+ ", Some(" + default_value + ")).unwrap()";
+ }
+ case ftString: {
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<&str>>(" + \
+ offset_name + ", None)", field.required);
+ }
+
+ case ftVectorOfInteger:
+ case ftVectorOfFloat: {
+ const auto typname = GetTypeBasic(type.VectorType());
+ std::string s = "self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Vector<" + lifetime + ", " + typname + \
+ ">>>(" + offset_name + ", None)";
+ // single-byte values are safe to slice
+ if (IsOneByte(type.VectorType().base_type)) {
+ s += ".map(|v| v.safe_slice())";
+ }
+ return AddUnwrapIfRequired(s, field.required);
+ }
+ case ftVectorOfBool: {
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Vector<" + lifetime + ", bool>>>(" + \
+ offset_name + ", None).map(|v| v.safe_slice())", field.required);
+ }
+ case ftVectorOfEnumKey: {
+ const auto typname = WrapInNameSpace(*type.enum_def);
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Vector<" + lifetime + ", " + typname + ">>>(" + \
+ offset_name + ", None)", field.required);
+ }
+ case ftVectorOfStruct: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Vector<" + typname + ">>>(" + \
+ offset_name + ", None).map(|v| v.safe_slice() )", field.required);
+ }
+ case ftVectorOfTable: {
+ const auto typname = WrapInNameSpace(*type.struct_def);
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Vector<flatbuffers::ForwardsUOffset<" + typname + \
+ "<" + lifetime + ">>>>>(" + offset_name + ", None)", field.required);
+ }
+ case ftVectorOfString: {
+ return AddUnwrapIfRequired("self._tab.get::<flatbuffers::ForwardsUOffset<"
+ "flatbuffers::Vector<flatbuffers::ForwardsUOffset<&" + \
+ lifetime + " str>>>>(" + offset_name + ", None)", field.required);
+ }
+ case ftVectorOfUnionValue: {
+ FLATBUFFERS_ASSERT(false && "vectors of unions are not yet supported");
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+ }
+ return "INVALID_CODE_GENERATION"; // for return analysis
+ }
+
+ bool TableFieldReturnsOption(const Type& type) {
+ switch (GetFullType(type)) {
+ case ftInteger:
+ case ftFloat:
+ case ftBool:
+ case ftEnumKey:
+ case ftUnionKey:
+ return false;
+ default: return true;
+ }
+ }
+
+ // Generate an accessor struct, builder struct, and create function for a
+ // table.
+ void GenTable(const StructDef &struct_def) {
+ code_.SetValue("STRUCT_NAME", Name(struct_def));
+ code_.SetValue("OFFSET_TYPELABEL", Name(struct_def) + "Offset");
+ code_.SetValue("STRUCT_NAME_SNAKECASE", MakeSnakeCase(Name(struct_def)));
+
+ // Generate an offset type, the base type, the Follow impl, and the
+ // init_from_table impl.
+ code_ += "pub enum {{OFFSET_TYPELABEL}} {}";
+ code_ += "#[derive(Copy, Clone, Debug, PartialEq)]";
+ code_ += "";
+
+ GenComment(struct_def.doc_comment);
+
+ code_ += "pub struct {{STRUCT_NAME}}<'a> {";
+ code_ += " pub _tab: flatbuffers::Table<'a>,";
+ code_ += "}";
+ code_ += "";
+ code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_NAME}}<'a> {";
+ code_ += " type Inner = {{STRUCT_NAME}}<'a>;";
+ code_ += " #[inline]";
+ code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
+ code_ += " Self {";
+ code_ += " _tab: flatbuffers::Table { buf: buf, loc: loc },";
+ code_ += " }";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ code_ += "impl<'a> {{STRUCT_NAME}}<'a> {";
+ code_ += " #[inline]";
+ code_ += " pub fn init_from_table(table: flatbuffers::Table<'a>) -> "
+ "Self {";
+ code_ += " {{STRUCT_NAME}} {";
+ code_ += " _tab: table,";
+ code_ += " }";
+ code_ += " }";
+
+ // Generate a convenient create* function that uses the above builder
+ // to create a table in one function call.
+ code_.SetValue("MAYBE_US",
+ struct_def.fields.vec.size() == 0 ? "_" : "");
+ code_.SetValue("MAYBE_LT",
+ TableBuilderArgsNeedsLifetime(struct_def) ? "<'args>" : "");
+ code_ += " #[allow(unused_mut)]";
+ code_ += " pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>(";
+ code_ += " _fbb: "
+ "&'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>,";
+ code_ += " {{MAYBE_US}}args: &'args {{STRUCT_NAME}}Args{{MAYBE_LT}})"
+ " -> flatbuffers::WIPOffset<{{STRUCT_NAME}}<'bldr>> {";
+
+ code_ += " let mut builder = {{STRUCT_NAME}}Builder::new(_fbb);";
+ for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1;
+ size; size /= 2) {
+ for (auto it = struct_def.fields.vec.rbegin();
+ it != struct_def.fields.vec.rend(); ++it) {
+ const auto &field = **it;
+ // TODO(rw): fully understand this sortbysize usage
+ if (!field.deprecated && (!struct_def.sortbysize ||
+ size == SizeOf(field.value.type.base_type))) {
+ code_.SetValue("FIELD_NAME", Name(field));
+ if (TableFieldReturnsOption(field.value.type)) {
+ code_ += " if let Some(x) = args.{{FIELD_NAME}} "
+ "{ builder.add_{{FIELD_NAME}}(x); }";
+ } else {
+ code_ += " builder.add_{{FIELD_NAME}}(args.{{FIELD_NAME}});";
+ }
+ }
+ }
+ }
+ code_ += " builder.finish()";
+ code_ += " }";
+ code_ += "";
+
+ // Generate field id constants.
+ if (struct_def.fields.vec.size() > 0) {
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated) {
+ // Deprecated fields won't be accessible.
+ continue;
+ }
+
+ code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field));
+ code_.SetValue("OFFSET_VALUE", NumToString(field.value.offset));
+ code_ += " pub const {{OFFSET_NAME}}: flatbuffers::VOffsetT = "
+ "{{OFFSET_VALUE}};";
+ }
+ code_ += "";
+ }
+
+ // Generate the accessors. Each has one of two forms:
+ //
+ // If a value can be None:
+ // pub fn name(&'a self) -> Option<user_facing_type> {
+ // self._tab.get::<internal_type>(offset, defaultval)
+ // }
+ //
+ // If a value is always Some:
+ // pub fn name(&'a self) -> user_facing_type {
+ // self._tab.get::<internal_type>(offset, defaultval).unwrap()
+ // }
+ const auto offset_prefix = Name(struct_def);
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated) {
+ // Deprecated fields won't be accessible.
+ continue;
+ }
+
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_.SetValue("RETURN_TYPE",
+ GenTableAccessorFuncReturnType(field, "'a"));
+ code_.SetValue("FUNC_BODY",
+ GenTableAccessorFuncBody(field, "'a", offset_prefix));
+
+ GenComment(field.doc_comment, " ");
+ code_ += " #[inline]";
+ code_ += " pub fn {{FIELD_NAME}}(&self) -> {{RETURN_TYPE}} {";
+ code_ += " {{FUNC_BODY}}";
+ code_ += " }";
+
+ // Generate a comparison function for this field if it is a key.
+ if (field.key) {
+ GenKeyFieldMethods(field);
+ }
+
+ // Generate a nested flatbuffer field, if applicable.
+ auto nested = field.attributes.Lookup("nested_flatbuffer");
+ if (nested) {
+ std::string qualified_name = nested->constant;
+ auto nested_root = parser_.LookupStruct(nested->constant);
+ if (nested_root == nullptr) {
+ qualified_name = parser_.current_namespace_->GetFullyQualifiedName(
+ nested->constant);
+ nested_root = parser_.LookupStruct(qualified_name);
+ }
+ FLATBUFFERS_ASSERT(nested_root); // Guaranteed to exist by parser.
+ (void)nested_root;
+
+ code_.SetValue("OFFSET_NAME",
+ offset_prefix + "::" + GetFieldOffsetName(field));
+ code_ += " pub fn {{FIELD_NAME}}_nested_flatbuffer(&'a self) -> "
+ " Option<{{STRUCT_NAME}}<'a>> {";
+ code_ += " match self.{{FIELD_NAME}}() {";
+ code_ += " None => { None }";
+ code_ += " Some(data) => {";
+ code_ += " use self::flatbuffers::Follow;";
+ code_ += " Some(<flatbuffers::ForwardsUOffset"
+ "<{{STRUCT_NAME}}<'a>>>::follow(data, 0))";
+ code_ += " },";
+ code_ += " }";
+ code_ += " }";
+ }
+ }
+
+ // Explicit specializations for union accessors
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.deprecated || field.value.type.base_type != BASE_TYPE_UNION) {
+ continue;
+ }
+
+ auto u = field.value.type.enum_def;
+
+ code_.SetValue("FIELD_NAME", Name(field));
+
+ for (auto u_it = u->Vals().begin(); u_it != u->Vals().end(); ++u_it) {
+ auto &ev = **u_it;
+ if (ev.union_type.base_type == BASE_TYPE_NONE) { continue; }
+
+ auto table_init_type = WrapInNameSpace(
+ ev.union_type.struct_def->defined_namespace,
+ ev.union_type.struct_def->name);
+
+ code_.SetValue("U_ELEMENT_ENUM_TYPE",
+ WrapInNameSpace(u->defined_namespace, GetEnumValUse(*u, ev)));
+ code_.SetValue("U_ELEMENT_TABLE_TYPE", table_init_type);
+ code_.SetValue("U_ELEMENT_NAME", MakeSnakeCase(Name(ev)));
+
+ code_ += " #[inline]";
+ code_ += " #[allow(non_snake_case)]";
+ code_ += " pub fn {{FIELD_NAME}}_as_{{U_ELEMENT_NAME}}(&self) -> "
+ "Option<{{U_ELEMENT_TABLE_TYPE}}<'a>> {";
+ code_ += " if self.{{FIELD_NAME}}_type() == {{U_ELEMENT_ENUM_TYPE}} {";
+ code_ += " self.{{FIELD_NAME}}().map(|u| "
+ "{{U_ELEMENT_TABLE_TYPE}}::init_from_table(u))";
+ code_ += " } else {";
+ code_ += " None";
+ code_ += " }";
+ code_ += " }";
+ code_ += "";
+ }
+ }
+
+ code_ += "}"; // End of table impl.
+ code_ += "";
+
+ // Generate an args struct:
+ code_.SetValue("MAYBE_LT",
+ TableBuilderArgsNeedsLifetime(struct_def) ? "<'a>" : "");
+ code_ += "pub struct {{STRUCT_NAME}}Args{{MAYBE_LT}} {";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) {
+ code_.SetValue("PARAM_NAME", Name(field));
+ code_.SetValue("PARAM_TYPE", TableBuilderArgsDefnType(field, "'a "));
+ code_ += " pub {{PARAM_NAME}}: {{PARAM_TYPE}},";
+ }
+ }
+ code_ += "}";
+
+ // Generate an impl of Default for the *Args type:
+ code_ += "impl<'a> Default for {{STRUCT_NAME}}Args{{MAYBE_LT}} {";
+ code_ += " #[inline]";
+ code_ += " fn default() -> Self {";
+ code_ += " {{STRUCT_NAME}}Args {";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) {
+ code_.SetValue("PARAM_VALUE", TableBuilderArgsDefaultValue(field));
+ code_.SetValue("REQ", field.required ? " // required field" : "");
+ code_.SetValue("PARAM_NAME", Name(field));
+ code_ += " {{PARAM_NAME}}: {{PARAM_VALUE}},{{REQ}}";
+ }
+ }
+ code_ += " }";
+ code_ += " }";
+ code_ += "}";
+
+ // Generate a builder struct:
+ code_ += "pub struct {{STRUCT_NAME}}Builder<'a: 'b, 'b> {";
+ code_ += " fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>,";
+ code_ += " start_: flatbuffers::WIPOffset<"
+ "flatbuffers::TableUnfinishedWIPOffset>,";
+ code_ += "}";
+
+ // Generate builder functions:
+ code_ += "impl<'a: 'b, 'b> {{STRUCT_NAME}}Builder<'a, 'b> {";
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated) {
+ const bool is_scalar = IsScalar(field.value.type.base_type);
+
+ std::string offset = GetFieldOffsetName(field);
+
+ // Generate functions to add data, which take one of two forms.
+ //
+ // If a value has a default:
+ // fn add_x(x_: type) {
+ // fbb_.push_slot::<type>(offset, x_, Some(default));
+ // }
+ //
+ // If a value does not have a default:
+ // fn add_x(x_: type) {
+ // fbb_.push_slot_always::<type>(offset, x_);
+ // }
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_.SetValue("FIELD_OFFSET", Name(struct_def) + "::" + offset);
+ code_.SetValue("FIELD_TYPE", TableBuilderArgsAddFuncType(field, "'b "));
+ code_.SetValue("FUNC_BODY", TableBuilderArgsAddFuncBody(field));
+ code_ += " #[inline]";
+ code_ += " pub fn add_{{FIELD_NAME}}(&mut self, {{FIELD_NAME}}: "
+ "{{FIELD_TYPE}}) {";
+ if (is_scalar) {
+ code_.SetValue("FIELD_DEFAULT_VALUE",
+ TableBuilderAddFuncDefaultValue(field));
+ code_ += " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}}, "
+ "{{FIELD_DEFAULT_VALUE}});";
+ } else {
+ code_ += " {{FUNC_BODY}}({{FIELD_OFFSET}}, {{FIELD_NAME}});";
+ }
+ code_ += " }";
+ }
+ }
+
+ // Struct initializer (all fields required);
+ code_ += " #[inline]";
+ code_ +=
+ " pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> "
+ "{{STRUCT_NAME}}Builder<'a, 'b> {";
+ code_.SetValue("NUM_FIELDS", NumToString(struct_def.fields.vec.size()));
+ code_ += " let start = _fbb.start_table();";
+ code_ += " {{STRUCT_NAME}}Builder {";
+ code_ += " fbb_: _fbb,";
+ code_ += " start_: start,";
+ code_ += " }";
+ code_ += " }";
+
+ // finish() function.
+ code_ += " #[inline]";
+ code_ += " pub fn finish(self) -> "
+ "flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>> {";
+ code_ += " let o = self.fbb_.end_table(self.start_);";
+
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (!field.deprecated && field.required) {
+ code_.SetValue("FIELD_NAME", MakeSnakeCase(Name(field)));
+ code_.SetValue("OFFSET_NAME", GetFieldOffsetName(field));
+ code_ += " self.fbb_.required(o, {{STRUCT_NAME}}::{{OFFSET_NAME}},"
+ "\"{{FIELD_NAME}}\");";
+ }
+ }
+ code_ += " flatbuffers::WIPOffset::new(o.value())";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ }
+
+ // Generate functions to compare tables and structs by key. This function
+ // must only be called if the field key is defined.
+ void GenKeyFieldMethods(const FieldDef &field) {
+ FLATBUFFERS_ASSERT(field.key);
+
+ code_.SetValue("KEY_TYPE", GenTableAccessorFuncReturnType(field, ""));
+
+ code_ += " #[inline]";
+ code_ += " pub fn key_compare_less_than(&self, o: &{{STRUCT_NAME}}) -> "
+ " bool {";
+ code_ += " self.{{FIELD_NAME}}() < o.{{FIELD_NAME}}()";
+ code_ += " }";
+ code_ += "";
+ code_ += " #[inline]";
+ code_ += " pub fn key_compare_with_value(&self, val: {{KEY_TYPE}}) -> "
+ " ::std::cmp::Ordering {";
+ code_ += " let key = self.{{FIELD_NAME}}();";
+ code_ += " key.cmp(&val)";
+ code_ += " }";
+ }
+
+ // Generate functions for accessing the root table object. This function
+ // must only be called if the root table is defined.
+ void GenRootTableFuncs(const StructDef &struct_def) {
+ FLATBUFFERS_ASSERT(parser_.root_struct_def_ && "root table not defined");
+ auto name = Name(struct_def);
+
+ code_.SetValue("STRUCT_NAME", name);
+ code_.SetValue("STRUCT_NAME_SNAKECASE", MakeSnakeCase(name));
+ code_.SetValue("STRUCT_NAME_CAPS", MakeUpper(MakeSnakeCase(name)));
+
+ // The root datatype accessors:
+ code_ += "#[inline]";
+ code_ +=
+ "pub fn get_root_as_{{STRUCT_NAME_SNAKECASE}}<'a>(buf: &'a [u8])"
+ " -> {{STRUCT_NAME}}<'a> {";
+ code_ += " flatbuffers::get_root::<{{STRUCT_NAME}}<'a>>(buf)";
+ code_ += "}";
+ code_ += "";
+
+ code_ += "#[inline]";
+ code_ += "pub fn get_size_prefixed_root_as_{{STRUCT_NAME_SNAKECASE}}"
+ "<'a>(buf: &'a [u8]) -> {{STRUCT_NAME}}<'a> {";
+ code_ += " flatbuffers::get_size_prefixed_root::<{{STRUCT_NAME}}<'a>>"
+ "(buf)";
+ code_ += "}";
+ code_ += "";
+
+ if (parser_.file_identifier_.length()) {
+ // Declare the identifier
+ code_ += "pub const {{STRUCT_NAME_CAPS}}_IDENTIFIER: &'static str\\";
+ code_ += " = \"" + parser_.file_identifier_ + "\";";
+ code_ += "";
+
+ // Check if a buffer has the identifier.
+ code_ += "#[inline]";
+ code_ += "pub fn {{STRUCT_NAME_SNAKECASE}}_buffer_has_identifier\\";
+ code_ += "(buf: &[u8]) -> bool {";
+ code_ += " return flatbuffers::buffer_has_identifier(buf, \\";
+ code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, false);";
+ code_ += "}";
+ code_ += "";
+ code_ += "#[inline]";
+ code_ += "pub fn {{STRUCT_NAME_SNAKECASE}}_size_prefixed\\";
+ code_ += "_buffer_has_identifier(buf: &[u8]) -> bool {";
+ code_ += " return flatbuffers::buffer_has_identifier(buf, \\";
+ code_ += "{{STRUCT_NAME_CAPS}}_IDENTIFIER, true);";
+ code_ += "}";
+ code_ += "";
+ }
+
+ if (parser_.file_extension_.length()) {
+ // Return the extension
+ code_ += "pub const {{STRUCT_NAME_CAPS}}_EXTENSION: &'static str = \\";
+ code_ += "\"" + parser_.file_extension_ + "\";";
+ code_ += "";
+ }
+
+ // Finish a buffer with a given root object:
+ code_.SetValue("OFFSET_TYPELABEL", Name(struct_def) + "Offset");
+ code_ += "#[inline]";
+ code_ += "pub fn finish_{{STRUCT_NAME_SNAKECASE}}_buffer<'a, 'b>(";
+ code_ += " fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>,";
+ code_ += " root: flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>>) {";
+ if (parser_.file_identifier_.length()) {
+ code_ += " fbb.finish(root, Some({{STRUCT_NAME_CAPS}}_IDENTIFIER));";
+ } else {
+ code_ += " fbb.finish(root, None);";
+ }
+ code_ += "}";
+ code_ += "";
+ code_ += "#[inline]";
+ code_ += "pub fn finish_size_prefixed_{{STRUCT_NAME_SNAKECASE}}_buffer"
+ "<'a, 'b>("
+ "fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, "
+ "root: flatbuffers::WIPOffset<{{STRUCT_NAME}}<'a>>) {";
+ if (parser_.file_identifier_.length()) {
+ code_ += " fbb.finish_size_prefixed(root, "
+ "Some({{STRUCT_NAME_CAPS}}_IDENTIFIER));";
+ } else {
+ code_ += " fbb.finish_size_prefixed(root, None);";
+ }
+ code_ += "}";
+ }
+
+ static void GenPadding(
+ const FieldDef &field, std::string *code_ptr, int *id,
+ const std::function<void(int bits, std::string *code_ptr, int *id)> &f) {
+ if (field.padding) {
+ for (int i = 0; i < 4; i++) {
+ if (static_cast<int>(field.padding) & (1 << i)) {
+ f((1 << i) * 8, code_ptr, id);
+ }
+ }
+ assert(!(field.padding & ~0xF));
+ }
+ }
+
+ static void PaddingDefinition(int bits, std::string *code_ptr, int *id) {
+ *code_ptr += " padding" + NumToString((*id)++) + "__: u" + \
+ NumToString(bits) + ",";
+ }
+
+ static void PaddingInitializer(int bits, std::string *code_ptr, int *id) {
+ (void)bits;
+ *code_ptr += "padding" + NumToString((*id)++) + "__: 0,";
+ }
+
+ // Generate an accessor struct with constructor for a flatbuffers struct.
+ void GenStruct(const StructDef &struct_def) {
+ // Generates manual padding and alignment.
+ // Variables are private because they contain little endian data on all
+ // platforms.
+ GenComment(struct_def.doc_comment);
+ code_.SetValue("ALIGN", NumToString(struct_def.minalign));
+ code_.SetValue("STRUCT_NAME", Name(struct_def));
+
+ code_ += "// struct {{STRUCT_NAME}}, aligned to {{ALIGN}}";
+ code_ += "#[repr(C, align({{ALIGN}}))]";
+
+ // PartialEq is useful to derive because we can correctly compare structs
+ // for equality by just comparing their underlying byte data. This doesn't
+ // hold for PartialOrd/Ord.
+ code_ += "#[derive(Clone, Copy, Debug, PartialEq)]";
+ code_ += "pub struct {{STRUCT_NAME}} {";
+
+ int padding_id = 0;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ code_.SetValue("FIELD_TYPE", GetTypeGet(field.value.type));
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_ += " {{FIELD_NAME}}_: {{FIELD_TYPE}},";
+
+ if (field.padding) {
+ std::string padding;
+ GenPadding(field, &padding, &padding_id, PaddingDefinition);
+ code_ += padding;
+ }
+ }
+
+ code_ += "} // pub struct {{STRUCT_NAME}}";
+
+ // Generate impls for SafeSliceAccess (because all structs are endian-safe),
+ // Follow for the value type, Follow for the reference type, Push for the
+ // value type, and Push for the reference type.
+ code_ += "impl flatbuffers::SafeSliceAccess for {{STRUCT_NAME}} {}";
+ code_ += "impl<'a> flatbuffers::Follow<'a> for {{STRUCT_NAME}} {";
+ code_ += " type Inner = &'a {{STRUCT_NAME}};";
+ code_ += " #[inline]";
+ code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
+ code_ += " <&'a {{STRUCT_NAME}}>::follow(buf, loc)";
+ code_ += " }";
+ code_ += "}";
+ code_ += "impl<'a> flatbuffers::Follow<'a> for &'a {{STRUCT_NAME}} {";
+ code_ += " type Inner = &'a {{STRUCT_NAME}};";
+ code_ += " #[inline]";
+ code_ += " fn follow(buf: &'a [u8], loc: usize) -> Self::Inner {";
+ code_ += " flatbuffers::follow_cast_ref::<{{STRUCT_NAME}}>(buf, loc)";
+ code_ += " }";
+ code_ += "}";
+ code_ += "impl<'b> flatbuffers::Push for {{STRUCT_NAME}} {";
+ code_ += " type Output = {{STRUCT_NAME}};";
+ code_ += " #[inline]";
+ code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
+ code_ += " let src = unsafe {";
+ code_ += " ::std::slice::from_raw_parts("
+ "self as *const {{STRUCT_NAME}} as *const u8, Self::size())";
+ code_ += " };";
+ code_ += " dst.copy_from_slice(src);";
+ code_ += " }";
+ code_ += "}";
+ code_ += "impl<'b> flatbuffers::Push for &'b {{STRUCT_NAME}} {";
+ code_ += " type Output = {{STRUCT_NAME}};";
+ code_ += "";
+ code_ += " #[inline]";
+ code_ += " fn push(&self, dst: &mut [u8], _rest: &[u8]) {";
+ code_ += " let src = unsafe {";
+ code_ += " ::std::slice::from_raw_parts("
+ "*self as *const {{STRUCT_NAME}} as *const u8, Self::size())";
+ code_ += " };";
+ code_ += " dst.copy_from_slice(src);";
+ code_ += " }";
+ code_ += "}";
+ code_ += "";
+ code_ += "";
+
+ // Generate a constructor that takes all fields as arguments.
+ code_ += "impl {{STRUCT_NAME}} {";
+ std::string arg_list;
+ std::string init_list;
+ padding_id = 0;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ const auto member_name = Name(field) + "_";
+ const auto reference = StructMemberAccessNeedsCopy(field.value.type)
+ ? "" : "&'a ";
+ const auto arg_name = "_" + Name(field);
+ const auto arg_type = reference + GetTypeGet(field.value.type);
+
+ if (it != struct_def.fields.vec.begin()) {
+ arg_list += ", ";
+ }
+ arg_list += arg_name + ": ";
+ arg_list += arg_type;
+ init_list += " " + member_name;
+ if (StructMemberAccessNeedsCopy(field.value.type)) {
+ init_list += ": " + arg_name + ".to_little_endian(),\n";
+ } else {
+ init_list += ": *" + arg_name + ",\n";
+ }
+ }
+
+ code_.SetValue("ARG_LIST", arg_list);
+ code_.SetValue("INIT_LIST", init_list);
+ code_ += " pub fn new<'a>({{ARG_LIST}}) -> Self {";
+ code_ += " {{STRUCT_NAME}} {";
+ code_ += "{{INIT_LIST}}";
+ padding_id = 0;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+ if (field.padding) {
+ std::string padding;
+ GenPadding(field, &padding, &padding_id, PaddingInitializer);
+ code_ += " " + padding;
+ }
+ }
+ code_ += " }";
+ code_ += " }";
+
+ // Generate accessor methods for the struct.
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ const auto &field = **it;
+
+ auto field_type = TableBuilderArgsAddFuncType(field, "'a");
+ auto member = "self." + Name(field) + "_";
+ auto value = StructMemberAccessNeedsCopy(field.value.type) ?
+ member + ".from_little_endian()" : member;
+
+ code_.SetValue("FIELD_NAME", Name(field));
+ code_.SetValue("FIELD_TYPE", field_type);
+ code_.SetValue("FIELD_VALUE", value);
+ code_.SetValue("REF", IsStruct(field.value.type) ? "&" : "");
+
+ GenComment(field.doc_comment, " ");
+ code_ += " pub fn {{FIELD_NAME}}<'a>(&'a self) -> {{FIELD_TYPE}} {";
+ code_ += " {{REF}}{{FIELD_VALUE}}";
+ code_ += " }";
+
+ // Generate a comparison function for this field if it is a key.
+ if (field.key) {
+ GenKeyFieldMethods(field);
+ }
+ }
+ code_ += "}";
+ code_ += "";
+ }
+
+ void GenNamespaceImports(const int white_spaces) {
+ std::string indent = std::string(white_spaces, ' ');
+ code_ += "";
+ code_ += indent + "use std::mem;";
+ code_ += indent + "use std::cmp::Ordering;";
+ code_ += "";
+ code_ += indent + "extern crate flatbuffers;";
+ code_ += indent + "use self::flatbuffers::EndianScalar;";
+ }
+
+ // Set up the correct namespace. This opens a namespace if the current
+ // namespace is different from the target namespace. This function
+ // closes and opens the namespaces only as necessary.
+ //
+ // The file must start and end with an empty (or null) namespace so that
+ // namespaces are properly opened and closed.
+ void SetNameSpace(const Namespace *ns) {
+ if (cur_name_space_ == ns) { return; }
+
+ // Compute the size of the longest common namespace prefix.
+ // If cur_name_space is A::B::C::D and ns is A::B::E::F::G,
+ // the common prefix is A::B:: and we have old_size = 4, new_size = 5
+ // and common_prefix_size = 2
+ size_t old_size = cur_name_space_ ? cur_name_space_->components.size() : 0;
+ size_t new_size = ns ? ns->components.size() : 0;
+
+ size_t common_prefix_size = 0;
+ while (common_prefix_size < old_size && common_prefix_size < new_size &&
+ ns->components[common_prefix_size] ==
+ cur_name_space_->components[common_prefix_size]) {
+ common_prefix_size++;
+ }
+
+ // Close cur_name_space in reverse order to reach the common prefix.
+ // In the previous example, D then C are closed.
+ for (size_t j = old_size; j > common_prefix_size; --j) {
+ code_ += "} // pub mod " + cur_name_space_->components[j - 1];
+ }
+ if (old_size != common_prefix_size) { code_ += ""; }
+
+ // open namespace parts to reach the ns namespace
+ // in the previous example, E, then F, then G are opened
+ for (auto j = common_prefix_size; j != new_size; ++j) {
+ code_ += "#[allow(unused_imports, dead_code)]";
+ code_ += "pub mod " + MakeSnakeCase(ns->components[j]) + " {";
+ // Generate local namespace imports.
+ GenNamespaceImports(2);
+ }
+ if (new_size != common_prefix_size) { code_ += ""; }
+
+ cur_name_space_ = ns;
+ }
+};
+
+} // namespace rust
+
+bool GenerateRust(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ rust::RustGenerator generator(parser, path, file_name);
+ return generator.generate();
+}
+
+std::string RustMakeRule(const Parser &parser, const std::string &path,
+ const std::string &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) {
+ make_rule += " " + *it;
+ }
+ return make_rule;
+}
+
+} // namespace flatbuffers
+
+// TODO(rw): Generated code should import other generated files.
+// TODO(rw): Generated code should refer to namespaces in included files in a
+// way that makes them referrable.
+// TODO(rw): Generated code should indent according to nesting level.
+// TODO(rw): Generated code should generate endian-safe Debug impls.
+// TODO(rw): Generated code could use a Rust-only enum type to access unions,
+// instead of making the user use _type() to manually switch.
diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp
new file mode 100644
index 0000000..9825dce
--- /dev/null
+++ b/src/idl_gen_text.cpp
@@ -0,0 +1,378 @@
+/*
+ * 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.
+ */
+
+// independent from idl_parser, since this code is not needed for most clients
+
+#include "flatbuffers/flatbuffers.h"
+#include "flatbuffers/flexbuffers.h"
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+
+static bool GenStruct(const StructDef &struct_def, const Table *table,
+ int indent, const IDLOptions &opts, std::string *_text);
+
+// If indentation is less than 0, that indicates we don't want any newlines
+// either.
+const char *NewLine(const IDLOptions &opts) {
+ return opts.indent_step >= 0 ? "\n" : "";
+}
+
+int Indent(const IDLOptions &opts) { return std::max(opts.indent_step, 0); }
+
+// Output an identifier with or without quotes depending on strictness.
+void OutputIdentifier(const std::string &name, const IDLOptions &opts,
+ std::string *_text) {
+ std::string &text = *_text;
+ if (opts.strict_json) text += "\"";
+ text += name;
+ if (opts.strict_json) text += "\"";
+}
+
+// Print (and its template specialization below for pointers) generate text
+// for a single FlatBuffer value into JSON format.
+// The general case for scalars:
+template<typename T>
+bool Print(T val, Type type, int /*indent*/, Type * /*union_type*/,
+ const IDLOptions &opts, std::string *_text) {
+ std::string &text = *_text;
+ if (type.enum_def && opts.output_enum_identifiers) {
+ std::vector<EnumVal const *> enum_values;
+ if (auto ev = type.enum_def->ReverseLookup(static_cast<int64_t>(val))) {
+ enum_values.push_back(ev);
+ } else if (val && type.enum_def->attributes.Lookup("bit_flags")) {
+ for (auto it = type.enum_def->Vals().begin(),
+ e = type.enum_def->Vals().end();
+ it != e; ++it) {
+ if ((*it)->GetAsUInt64() & static_cast<uint64_t>(val))
+ enum_values.push_back(*it);
+ }
+ }
+ if (!enum_values.empty()) {
+ text += '\"';
+ for (auto it = enum_values.begin(), e = enum_values.end(); it != e; ++it)
+ text += (*it)->name + ' ';
+ text[text.length() - 1] = '\"';
+ return true;
+ }
+ }
+
+ if (type.base_type == BASE_TYPE_BOOL) {
+ text += val != 0 ? "true" : "false";
+ } else {
+ text += NumToString(val);
+ }
+
+ return true;
+}
+
+// Print a vector or an array of JSON values, comma seperated, wrapped in "[]".
+template<typename T, typename Container>
+bool PrintContainer(const Container &c, size_t size, Type type, int indent,
+ const IDLOptions &opts, std::string *_text) {
+ std::string &text = *_text;
+ text += "[";
+ text += NewLine(opts);
+ for (uoffset_t i = 0; i < size; i++) {
+ if (i) {
+ if (!opts.protobuf_ascii_alike) text += ",";
+ text += NewLine(opts);
+ }
+ text.append(indent + Indent(opts), ' ');
+ if (IsStruct(type)) {
+ if (!Print(reinterpret_cast<const void *>(c.Data() +
+ i * type.struct_def->bytesize),
+ type, indent + Indent(opts), nullptr, opts, _text)) {
+ return false;
+ }
+ } else {
+ if (!Print(c[i], type, indent + Indent(opts), nullptr, opts, _text)) {
+ return false;
+ }
+ }
+ }
+ text += NewLine(opts);
+ text.append(indent, ' ');
+ text += "]";
+ return true;
+}
+
+template<typename T>
+bool PrintVector(const Vector<T> &v, Type type, int indent,
+ const IDLOptions &opts, std::string *_text) {
+ return PrintContainer<T, Vector<T>>(v, v.size(), type, indent, opts, _text);
+}
+
+// Print an array a sequence of JSON values, comma separated, wrapped in "[]".
+template<typename T>
+bool PrintArray(const Array<T, 0xFFFF> &a, size_t size, Type type, int indent,
+ const IDLOptions &opts, std::string *_text) {
+ return PrintContainer<T, Array<T, 0xFFFF>>(a, size, type, indent, opts,
+ _text);
+}
+
+// Specialization of Print above for pointer types.
+template<>
+bool Print<const void *>(const void *val, Type type, int indent,
+ Type *union_type, const IDLOptions &opts,
+ std::string *_text) {
+ switch (type.base_type) {
+ case BASE_TYPE_UNION:
+ // If this assert hits, you have an corrupt buffer, a union type field
+ // was not present or was out of range.
+ FLATBUFFERS_ASSERT(union_type);
+ return Print<const void *>(val, *union_type, indent, nullptr, opts,
+ _text);
+ case BASE_TYPE_STRUCT:
+ if (!GenStruct(*type.struct_def, reinterpret_cast<const Table *>(val),
+ indent, opts, _text)) {
+ return false;
+ }
+ break;
+ case BASE_TYPE_STRING: {
+ auto s = reinterpret_cast<const String *>(val);
+ if (!EscapeString(s->c_str(), s->size(), _text, opts.allow_non_utf8,
+ opts.natural_utf8)) {
+ return false;
+ }
+ break;
+ }
+ case BASE_TYPE_VECTOR: {
+ const auto vec_type = type.VectorType();
+ // Call PrintVector above specifically for each element type:
+ // clang-format off
+ switch (vec_type.base_type) {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ case BASE_TYPE_ ## ENUM: \
+ if (!PrintVector<CTYPE>( \
+ *reinterpret_cast<const Vector<CTYPE> *>(val), \
+ vec_type, indent, opts, _text)) { \
+ return false; \
+ } \
+ break;
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ }
+ // clang-format on
+ break;
+ }
+ case BASE_TYPE_ARRAY: {
+ const auto vec_type = type.VectorType();
+ // Call PrintArray above specifically for each element type:
+ // clang-format off
+ switch (vec_type.base_type) {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ case BASE_TYPE_ ## ENUM: \
+ if (!PrintArray<CTYPE>( \
+ *reinterpret_cast<const Array<CTYPE, 0xFFFF> *>(val), \
+ type.fixed_length, \
+ vec_type, indent, opts, _text)) { \
+ return false; \
+ } \
+ break;
+ FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
+ FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ case BASE_TYPE_ARRAY: FLATBUFFERS_ASSERT(0);
+ }
+ // clang-format on
+ break;
+ }
+ default: FLATBUFFERS_ASSERT(0);
+ }
+ return true;
+}
+
+template<typename T> static T GetFieldDefault(const FieldDef &fd) {
+ T val;
+ auto check = StringToNumber(fd.value.constant.c_str(), &val);
+ (void)check;
+ FLATBUFFERS_ASSERT(check);
+ return val;
+}
+
+// Generate text for a scalar field.
+template<typename T>
+static bool GenField(const FieldDef &fd, const Table *table, bool fixed,
+ const IDLOptions &opts, int indent, std::string *_text) {
+ return Print(
+ fixed ? reinterpret_cast<const Struct *>(table)->GetField<T>(
+ fd.value.offset)
+ : table->GetField<T>(fd.value.offset, GetFieldDefault<T>(fd)),
+ fd.value.type, indent, nullptr, opts, _text);
+}
+
+static bool GenStruct(const StructDef &struct_def, const Table *table,
+ int indent, const IDLOptions &opts, std::string *_text);
+
+// Generate text for non-scalar field.
+static bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed,
+ int indent, Type *union_type, const IDLOptions &opts,
+ std::string *_text) {
+ const void *val = nullptr;
+ if (fixed) {
+ // The only non-scalar fields in structs are structs or arrays.
+ FLATBUFFERS_ASSERT(IsStruct(fd.value.type) || IsArray(fd.value.type));
+ val = reinterpret_cast<const Struct *>(table)->GetStruct<const void *>(
+ fd.value.offset);
+ } else if (fd.flexbuffer) {
+ auto vec = table->GetPointer<const Vector<uint8_t> *>(fd.value.offset);
+ auto root = flexbuffers::GetRoot(vec->data(), vec->size());
+ root.ToString(true, opts.strict_json, *_text);
+ return true;
+ } else if (fd.nested_flatbuffer) {
+ auto vec = table->GetPointer<const Vector<uint8_t> *>(fd.value.offset);
+ auto root = GetRoot<Table>(vec->data());
+ return GenStruct(*fd.nested_flatbuffer, root, indent, opts, _text);
+ } else {
+ val = IsStruct(fd.value.type)
+ ? table->GetStruct<const void *>(fd.value.offset)
+ : table->GetPointer<const void *>(fd.value.offset);
+ }
+ return Print(val, fd.value.type, indent, union_type, opts, _text);
+}
+
+// Generate text for a struct or table, values separated by commas, indented,
+// and bracketed by "{}"
+static bool GenStruct(const StructDef &struct_def, const Table *table,
+ int indent, const IDLOptions &opts, std::string *_text) {
+ std::string &text = *_text;
+ text += "{";
+ int fieldout = 0;
+ Type *union_type = nullptr;
+ for (auto it = struct_def.fields.vec.begin();
+ it != struct_def.fields.vec.end(); ++it) {
+ FieldDef &fd = **it;
+ auto is_present = struct_def.fixed || table->CheckField(fd.value.offset);
+ auto output_anyway = opts.output_default_scalars_in_json &&
+ IsScalar(fd.value.type.base_type) && !fd.deprecated;
+ if (is_present || output_anyway) {
+ if (fieldout++) {
+ if (!opts.protobuf_ascii_alike) text += ",";
+ }
+ text += NewLine(opts);
+ text.append(indent + Indent(opts), ' ');
+ OutputIdentifier(fd.name, opts, _text);
+ if (!opts.protobuf_ascii_alike ||
+ (fd.value.type.base_type != BASE_TYPE_STRUCT &&
+ fd.value.type.base_type != BASE_TYPE_VECTOR))
+ text += ":";
+ text += " ";
+ switch (fd.value.type.base_type) {
+ // clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ case BASE_TYPE_ ## ENUM: \
+ if (!GenField<CTYPE>(fd, table, struct_def.fixed, \
+ opts, indent + Indent(opts), _text)) { \
+ return false; \
+ } \
+ break;
+ FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // Generate drop-thru case statements for all pointer types:
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ case BASE_TYPE_ ## ENUM:
+ FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD)
+ FLATBUFFERS_GEN_TYPE_ARRAY(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ if (!GenFieldOffset(fd, table, struct_def.fixed, indent + Indent(opts),
+ union_type, opts, _text)) {
+ return false;
+ }
+ break;
+ // clang-format on
+ }
+ if (fd.value.type.base_type == BASE_TYPE_UTYPE) {
+ auto enum_val = fd.value.type.enum_def->ReverseLookup(
+ table->GetField<uint8_t>(fd.value.offset, 0), true);
+ union_type = enum_val ? &enum_val->union_type : nullptr;
+ }
+ }
+ }
+ text += NewLine(opts);
+ text.append(indent, ' ');
+ text += "}";
+ return true;
+}
+
+// Generate a text representation of a flatbuffer in JSON format.
+bool GenerateTextFromTable(const Parser &parser, const void *table,
+ const std::string &table_name, std::string *_text) {
+ auto struct_def = parser.LookupStruct(table_name);
+ if (struct_def == nullptr) {
+ return false;
+ }
+ auto &text = *_text;
+ text.reserve(1024); // Reduce amount of inevitable reallocs.
+ auto root = static_cast<const Table *>(table);
+ if (!GenStruct(*struct_def, root, 0, parser.opts, &text)) {
+ return false;
+ }
+ text += NewLine(parser.opts);
+ return true;
+}
+
+// Generate a text representation of a flatbuffer in JSON format.
+bool GenerateText(const Parser &parser, const void *flatbuffer,
+ std::string *_text) {
+ std::string &text = *_text;
+ FLATBUFFERS_ASSERT(parser.root_struct_def_); // call SetRootType()
+ text.reserve(1024); // Reduce amount of inevitable reallocs.
+ auto root = parser.opts.size_prefixed ?
+ GetSizePrefixedRoot<Table>(flatbuffer) : GetRoot<Table>(flatbuffer);
+ if (!GenStruct(*parser.root_struct_def_, root, 0, parser.opts, _text)) {
+ return false;
+ }
+ text += NewLine(parser.opts);
+ return true;
+}
+
+std::string TextFileName(const std::string &path,
+ const std::string &file_name) {
+ return path + file_name + ".json";
+}
+
+bool GenerateTextFile(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ if (!parser.builder_.GetSize() || !parser.root_struct_def_) return true;
+ std::string text;
+ if (!GenerateText(parser, parser.builder_.GetBufferPointer(), &text)) {
+ return false;
+ }
+ return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(), text,
+ false);
+}
+
+std::string TextMakeRule(const Parser &parser, const std::string &path,
+ const std::string &file_name) {
+ if (!parser.builder_.GetSize() || !parser.root_struct_def_) return "";
+ std::string filebase =
+ flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
+ std::string make_rule = TextFileName(path, filebase) + ": " + file_name;
+ auto included_files =
+ parser.GetIncludedFilesRecursive(parser.root_struct_def_->file);
+ for (auto it = included_files.begin(); it != included_files.end(); ++it) {
+ make_rule += " " + *it;
+ }
+ return make_rule;
+}
+
+} // namespace flatbuffers
diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp
new file mode 100644
index 0000000..31b315c
--- /dev/null
+++ b/src/idl_parser.cpp
@@ -0,0 +1,3518 @@
+/*
+ * 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 <algorithm>
+#include <list>
+#include <string>
+#include <utility>
+
+#include <cmath>
+
+#include "flatbuffers/idl.h"
+#include "flatbuffers/util.h"
+
+namespace flatbuffers {
+
+// Reflects the version at the compiling time of binary(lib/dll/so).
+const char *FLATBUFFERS_VERSION() {
+ // clang-format off
+ return
+ FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MAJOR) "."
+ FLATBUFFERS_STRING(FLATBUFFERS_VERSION_MINOR) "."
+ FLATBUFFERS_STRING(FLATBUFFERS_VERSION_REVISION);
+ // clang-format on
+}
+
+const double kPi = 3.14159265358979323846;
+
+const char *const kTypeNames[] = {
+// clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ IDLTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ nullptr
+};
+
+const char kTypeSizes[] = {
+// clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ sizeof(CTYPE),
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+};
+
+// The enums in the reflection schema should match the ones we use internally.
+// Compare the last element to check if these go out of sync.
+static_assert(BASE_TYPE_UNION == static_cast<BaseType>(reflection::Union),
+ "enums don't match");
+
+// Any parsing calls have to be wrapped in this macro, which automates
+// handling of recursive error checking a bit. It will check the received
+// CheckedError object, and return straight away on error.
+#define ECHECK(call) \
+ { \
+ auto ce = (call); \
+ if (ce.Check()) return ce; \
+ }
+
+// These two functions are called hundreds of times below, so define a short
+// form:
+#define NEXT() ECHECK(Next())
+#define EXPECT(tok) ECHECK(Expect(tok))
+
+static bool ValidateUTF8(const std::string &str) {
+ const char *s = &str[0];
+ const char *const sEnd = s + str.length();
+ while (s < sEnd) {
+ if (FromUTF8(&s) < 0) { return false; }
+ }
+ return true;
+}
+
+// Convert an underscore_based_indentifier in to camelCase.
+// Also uppercases the first character if first is true.
+std::string MakeCamel(const std::string &in, bool first) {
+ std::string s;
+ for (size_t i = 0; i < in.length(); i++) {
+ if (!i && first)
+ s += static_cast<char>(toupper(in[0]));
+ else if (in[i] == '_' && i + 1 < in.length())
+ s += static_cast<char>(toupper(in[++i]));
+ else
+ s += in[i];
+ }
+ return s;
+}
+
+void DeserializeDoc( std::vector<std::string> &doc,
+ const Vector<Offset<String>> *documentation) {
+ if (documentation == nullptr) return;
+ for (uoffset_t index = 0; index < documentation->size(); index++)
+ doc.push_back(documentation->Get(index)->str());
+}
+
+void Parser::Message(const std::string &msg) {
+ if (!error_.empty()) error_ += "\n"; // log all warnings and errors
+ error_ += file_being_parsed_.length() ? AbsolutePath(file_being_parsed_) : "";
+ // clang-format off
+
+ #ifdef _WIN32 // MSVC alike
+ error_ +=
+ "(" + NumToString(line_) + ", " + NumToString(CursorPosition()) + ")";
+ #else // gcc alike
+ if (file_being_parsed_.length()) error_ += ":";
+ error_ += NumToString(line_) + ": " + NumToString(CursorPosition());
+ #endif
+ // clang-format on
+ error_ += ": " + msg;
+}
+
+void Parser::Warning(const std::string &msg) { Message("warning: " + msg); }
+
+CheckedError Parser::Error(const std::string &msg) {
+ Message("error: " + msg);
+ return CheckedError(true);
+}
+
+inline CheckedError NoError() { return CheckedError(false); }
+
+CheckedError Parser::RecurseError() {
+ return Error("maximum parsing recursion of " +
+ NumToString(FLATBUFFERS_MAX_PARSING_DEPTH) + " reached");
+}
+
+template<typename F> CheckedError Parser::Recurse(F f) {
+ if (recurse_protection_counter >= (FLATBUFFERS_MAX_PARSING_DEPTH))
+ return RecurseError();
+ recurse_protection_counter++;
+ auto ce = f();
+ recurse_protection_counter--;
+ return ce;
+}
+
+template<typename T> std::string TypeToIntervalString() {
+ return "[" + NumToString((flatbuffers::numeric_limits<T>::lowest)()) + "; " +
+ NumToString((flatbuffers::numeric_limits<T>::max)()) + "]";
+}
+
+// atot: template version of atoi/atof: convert a string to an instance of T.
+template<typename T>
+inline CheckedError atot(const char *s, Parser &parser, T *val) {
+ auto done = StringToNumber(s, val);
+ if (done) return NoError();
+ if (0 == *val)
+ return parser.Error("invalid number: \"" + std::string(s) + "\"");
+ else
+ return parser.Error("invalid number: \"" + std::string(s) + "\"" +
+ ", constant does not fit " + TypeToIntervalString<T>());
+}
+template<>
+inline CheckedError atot<Offset<void>>(const char *s, Parser &parser,
+ Offset<void> *val) {
+ (void)parser;
+ *val = Offset<void>(atoi(s));
+ return NoError();
+}
+
+std::string Namespace::GetFullyQualifiedName(const std::string &name,
+ size_t max_components) const {
+ // Early exit if we don't have a defined namespace.
+ if (components.empty() || !max_components) { return name; }
+ std::string stream_str;
+ for (size_t i = 0; i < std::min(components.size(), max_components); i++) {
+ if (i) { stream_str += '.'; }
+ stream_str += std::string(components[i]);
+ }
+ if (name.length()) {
+ stream_str += '.';
+ stream_str += name;
+ }
+ return stream_str;
+}
+
+// Declare tokens we'll use. Single character tokens are represented by their
+// ascii character code (e.g. '{'), others above 256.
+// clang-format off
+#define FLATBUFFERS_GEN_TOKENS(TD) \
+ TD(Eof, 256, "end of file") \
+ TD(StringConstant, 257, "string constant") \
+ TD(IntegerConstant, 258, "integer constant") \
+ TD(FloatConstant, 259, "float constant") \
+ TD(Identifier, 260, "identifier")
+#ifdef __GNUC__
+__extension__ // Stop GCC complaining about trailing comma with -Wpendantic.
+#endif
+enum {
+ #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) kToken ## NAME = VALUE,
+ FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
+ #undef FLATBUFFERS_TOKEN
+};
+
+static std::string TokenToString(int t) {
+ static const char * const tokens[] = {
+ #define FLATBUFFERS_TOKEN(NAME, VALUE, STRING) STRING,
+ FLATBUFFERS_GEN_TOKENS(FLATBUFFERS_TOKEN)
+ #undef FLATBUFFERS_TOKEN
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ IDLTYPE,
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ };
+ if (t < 256) { // A single ascii char token.
+ std::string s;
+ s.append(1, static_cast<char>(t));
+ return s;
+ } else { // Other tokens.
+ return tokens[t - 256];
+ }
+}
+// clang-format on
+
+std::string Parser::TokenToStringId(int t) const {
+ return t == kTokenIdentifier ? attribute_ : TokenToString(t);
+}
+
+// Parses exactly nibbles worth of hex digits into a number, or error.
+CheckedError Parser::ParseHexNum(int nibbles, uint64_t *val) {
+ FLATBUFFERS_ASSERT(nibbles > 0);
+ for (int i = 0; i < nibbles; i++)
+ if (!is_xdigit(cursor_[i]))
+ return Error("escape code must be followed by " + NumToString(nibbles) +
+ " hex digits");
+ std::string target(cursor_, cursor_ + nibbles);
+ *val = StringToUInt(target.c_str(), 16);
+ cursor_ += nibbles;
+ return NoError();
+}
+
+CheckedError Parser::SkipByteOrderMark() {
+ if (static_cast<unsigned char>(*cursor_) != 0xef) return NoError();
+ cursor_++;
+ if (static_cast<unsigned char>(*cursor_) != 0xbb)
+ return Error("invalid utf-8 byte order mark");
+ cursor_++;
+ if (static_cast<unsigned char>(*cursor_) != 0xbf)
+ return Error("invalid utf-8 byte order mark");
+ cursor_++;
+ return NoError();
+}
+
+static inline bool IsIdentifierStart(char c) {
+ return is_alpha(c) || (c == '_');
+}
+
+CheckedError Parser::Next() {
+ doc_comment_.clear();
+ bool seen_newline = cursor_ == source_;
+ attribute_.clear();
+ attr_is_trivial_ascii_string_ = true;
+ for (;;) {
+ char c = *cursor_++;
+ token_ = c;
+ switch (c) {
+ case '\0':
+ cursor_--;
+ token_ = kTokenEof;
+ return NoError();
+ case ' ':
+ case '\r':
+ case '\t': break;
+ case '\n':
+ MarkNewLine();
+ seen_newline = true;
+ break;
+ case '{':
+ case '}':
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ case ',':
+ case ':':
+ case ';':
+ case '=': return NoError();
+ case '\"':
+ case '\'': {
+ int unicode_high_surrogate = -1;
+
+ while (*cursor_ != c) {
+ if (*cursor_ < ' ' && static_cast<signed char>(*cursor_) >= 0)
+ return Error("illegal character in string constant");
+ if (*cursor_ == '\\') {
+ attr_is_trivial_ascii_string_ = false; // has escape sequence
+ cursor_++;
+ if (unicode_high_surrogate != -1 && *cursor_ != 'u') {
+ return Error(
+ "illegal Unicode sequence (unpaired high surrogate)");
+ }
+ switch (*cursor_) {
+ case 'n':
+ attribute_ += '\n';
+ cursor_++;
+ break;
+ case 't':
+ attribute_ += '\t';
+ cursor_++;
+ break;
+ case 'r':
+ attribute_ += '\r';
+ cursor_++;
+ break;
+ case 'b':
+ attribute_ += '\b';
+ cursor_++;
+ break;
+ case 'f':
+ attribute_ += '\f';
+ cursor_++;
+ break;
+ case '\"':
+ attribute_ += '\"';
+ cursor_++;
+ break;
+ case '\'':
+ attribute_ += '\'';
+ cursor_++;
+ break;
+ case '\\':
+ attribute_ += '\\';
+ cursor_++;
+ break;
+ case '/':
+ attribute_ += '/';
+ cursor_++;
+ break;
+ case 'x': { // Not in the JSON standard
+ cursor_++;
+ uint64_t val;
+ ECHECK(ParseHexNum(2, &val));
+ attribute_ += static_cast<char>(val);
+ break;
+ }
+ case 'u': {
+ cursor_++;
+ uint64_t val;
+ ECHECK(ParseHexNum(4, &val));
+ if (val >= 0xD800 && val <= 0xDBFF) {
+ if (unicode_high_surrogate != -1) {
+ return Error(
+ "illegal Unicode sequence (multiple high surrogates)");
+ } else {
+ unicode_high_surrogate = static_cast<int>(val);
+ }
+ } else if (val >= 0xDC00 && val <= 0xDFFF) {
+ if (unicode_high_surrogate == -1) {
+ return Error(
+ "illegal Unicode sequence (unpaired low surrogate)");
+ } else {
+ int code_point = 0x10000 +
+ ((unicode_high_surrogate & 0x03FF) << 10) +
+ (val & 0x03FF);
+ ToUTF8(code_point, &attribute_);
+ unicode_high_surrogate = -1;
+ }
+ } else {
+ if (unicode_high_surrogate != -1) {
+ return Error(
+ "illegal Unicode sequence (unpaired high surrogate)");
+ }
+ ToUTF8(static_cast<int>(val), &attribute_);
+ }
+ break;
+ }
+ default: return Error("unknown escape code in string constant");
+ }
+ } else { // printable chars + UTF-8 bytes
+ if (unicode_high_surrogate != -1) {
+ return Error(
+ "illegal Unicode sequence (unpaired high surrogate)");
+ }
+ // reset if non-printable
+ attr_is_trivial_ascii_string_ &= check_ascii_range(*cursor_, ' ', '~');
+
+ attribute_ += *cursor_++;
+ }
+ }
+ if (unicode_high_surrogate != -1) {
+ return Error("illegal Unicode sequence (unpaired high surrogate)");
+ }
+ cursor_++;
+ if (!attr_is_trivial_ascii_string_ && !opts.allow_non_utf8 &&
+ !ValidateUTF8(attribute_)) {
+ return Error("illegal UTF-8 sequence");
+ }
+ token_ = kTokenStringConstant;
+ return NoError();
+ }
+ case '/':
+ if (*cursor_ == '/') {
+ const char *start = ++cursor_;
+ while (*cursor_ && *cursor_ != '\n' && *cursor_ != '\r') cursor_++;
+ if (*start == '/') { // documentation comment
+ if (!seen_newline)
+ return Error(
+ "a documentation comment should be on a line on its own");
+ doc_comment_.push_back(std::string(start + 1, cursor_));
+ }
+ break;
+ } else if (*cursor_ == '*') {
+ cursor_++;
+ // TODO: make nested.
+ while (*cursor_ != '*' || cursor_[1] != '/') {
+ if (*cursor_ == '\n') MarkNewLine();
+ if (!*cursor_) return Error("end of file in comment");
+ cursor_++;
+ }
+ cursor_ += 2;
+ break;
+ }
+ FLATBUFFERS_FALLTHROUGH(); // else fall thru
+ default:
+ const auto has_sign = (c == '+') || (c == '-');
+ // '-'/'+' and following identifier - can be a predefined constant like:
+ // NAN, INF, PI, etc.
+ if (IsIdentifierStart(c) || (has_sign && IsIdentifierStart(*cursor_))) {
+ // Collect all chars of an identifier:
+ const char *start = cursor_ - 1;
+ while (IsIdentifierStart(*cursor_) || is_digit(*cursor_)) cursor_++;
+ attribute_.append(start, cursor_);
+ token_ = has_sign ? kTokenStringConstant : kTokenIdentifier;
+ return NoError();
+ }
+
+ auto dot_lvl = (c == '.') ? 0 : 1; // dot_lvl==0 <=> exactly one '.' seen
+ if (!dot_lvl && !is_digit(*cursor_)) return NoError(); // enum?
+ // Parser accepts hexadecimal-floating-literal (see C++ 5.13.4).
+ if (is_digit(c) || has_sign || !dot_lvl) {
+ const auto start = cursor_ - 1;
+ auto start_digits = !is_digit(c) ? cursor_ : cursor_ - 1;
+ if (!is_digit(c) && is_digit(*cursor_)){
+ start_digits = cursor_; // see digit in cursor_ position
+ c = *cursor_++;
+ }
+ // hex-float can't begind with '.'
+ auto use_hex = dot_lvl && (c == '0') && is_alpha_char(*cursor_, 'X');
+ if (use_hex) start_digits = ++cursor_; // '0x' is the prefix, skip it
+ // Read an integer number or mantisa of float-point number.
+ do {
+ if (use_hex) {
+ while (is_xdigit(*cursor_)) cursor_++;
+ } else {
+ while (is_digit(*cursor_)) cursor_++;
+ }
+ } while ((*cursor_ == '.') && (++cursor_) && (--dot_lvl >= 0));
+ // Exponent of float-point number.
+ if ((dot_lvl >= 0) && (cursor_ > start_digits)) {
+ // The exponent suffix of hexadecimal float number is mandatory.
+ if (use_hex && !dot_lvl) start_digits = cursor_;
+ if ((use_hex && is_alpha_char(*cursor_, 'P')) ||
+ is_alpha_char(*cursor_, 'E')) {
+ dot_lvl = 0; // Emulate dot to signal about float-point number.
+ cursor_++;
+ if (*cursor_ == '+' || *cursor_ == '-') cursor_++;
+ start_digits = cursor_; // the exponent-part has to have digits
+ // Exponent is decimal integer number
+ while (is_digit(*cursor_)) cursor_++;
+ if (*cursor_ == '.') {
+ cursor_++; // If see a dot treat it as part of invalid number.
+ dot_lvl = -1; // Fall thru to Error().
+ }
+ }
+ }
+ // Finalize.
+ if ((dot_lvl >= 0) && (cursor_ > start_digits)) {
+ attribute_.append(start, cursor_);
+ token_ = dot_lvl ? kTokenIntegerConstant : kTokenFloatConstant;
+ return NoError();
+ } else {
+ return Error("invalid number: " + std::string(start, cursor_));
+ }
+ }
+ std::string ch;
+ ch = c;
+ if (false == check_ascii_range(c, ' ', '~')) ch = "code: " + NumToString(c);
+ return Error("illegal character: " + ch);
+ }
+ }
+}
+
+// Check if a given token is next.
+bool Parser::Is(int t) const { return t == token_; }
+
+bool Parser::IsIdent(const char *id) const {
+ return token_ == kTokenIdentifier && attribute_ == id;
+}
+
+// Expect a given token to be next, consume it, or error if not present.
+CheckedError Parser::Expect(int t) {
+ if (t != token_) {
+ return Error("expecting: " + TokenToString(t) +
+ " instead got: " + TokenToStringId(token_));
+ }
+ NEXT();
+ return NoError();
+}
+
+CheckedError Parser::ParseNamespacing(std::string *id, std::string *last) {
+ while (Is('.')) {
+ NEXT();
+ *id += ".";
+ *id += attribute_;
+ if (last) *last = attribute_;
+ EXPECT(kTokenIdentifier);
+ }
+ return NoError();
+}
+
+EnumDef *Parser::LookupEnum(const std::string &id) {
+ // Search thru parent namespaces.
+ for (int components = static_cast<int>(current_namespace_->components.size());
+ components >= 0; components--) {
+ auto ed = enums_.Lookup(
+ current_namespace_->GetFullyQualifiedName(id, components));
+ if (ed) return ed;
+ }
+ return nullptr;
+}
+
+StructDef *Parser::LookupStruct(const std::string &id) const {
+ auto sd = structs_.Lookup(id);
+ if (sd) sd->refcount++;
+ return sd;
+}
+
+CheckedError Parser::ParseTypeIdent(Type &type) {
+ std::string id = attribute_;
+ EXPECT(kTokenIdentifier);
+ ECHECK(ParseNamespacing(&id, nullptr));
+ auto enum_def = LookupEnum(id);
+ if (enum_def) {
+ type = enum_def->underlying_type;
+ if (enum_def->is_union) type.base_type = BASE_TYPE_UNION;
+ } else {
+ type.base_type = BASE_TYPE_STRUCT;
+ type.struct_def = LookupCreateStruct(id);
+ }
+ return NoError();
+}
+
+// Parse any IDL type.
+CheckedError Parser::ParseType(Type &type) {
+ if (token_ == kTokenIdentifier) {
+ if (IsIdent("bool")) {
+ type.base_type = BASE_TYPE_BOOL;
+ NEXT();
+ } else if (IsIdent("byte") || IsIdent("int8")) {
+ type.base_type = BASE_TYPE_CHAR;
+ NEXT();
+ } else if (IsIdent("ubyte") || IsIdent("uint8")) {
+ type.base_type = BASE_TYPE_UCHAR;
+ NEXT();
+ } else if (IsIdent("short") || IsIdent("int16")) {
+ type.base_type = BASE_TYPE_SHORT;
+ NEXT();
+ } else if (IsIdent("ushort") || IsIdent("uint16")) {
+ type.base_type = BASE_TYPE_USHORT;
+ NEXT();
+ } else if (IsIdent("int") || IsIdent("int32")) {
+ type.base_type = BASE_TYPE_INT;
+ NEXT();
+ } else if (IsIdent("uint") || IsIdent("uint32")) {
+ type.base_type = BASE_TYPE_UINT;
+ NEXT();
+ } else if (IsIdent("long") || IsIdent("int64")) {
+ type.base_type = BASE_TYPE_LONG;
+ NEXT();
+ } else if (IsIdent("ulong") || IsIdent("uint64")) {
+ type.base_type = BASE_TYPE_ULONG;
+ NEXT();
+ } else if (IsIdent("float") || IsIdent("float32")) {
+ type.base_type = BASE_TYPE_FLOAT;
+ NEXT();
+ } else if (IsIdent("double") || IsIdent("float64")) {
+ type.base_type = BASE_TYPE_DOUBLE;
+ NEXT();
+ } else if (IsIdent("string")) {
+ type.base_type = BASE_TYPE_STRING;
+ NEXT();
+ } else {
+ ECHECK(ParseTypeIdent(type));
+ }
+ } else if (token_ == '[') {
+ NEXT();
+ Type subtype;
+ ECHECK(Recurse([&]() { return ParseType(subtype); }));
+ if (IsSeries(subtype)) {
+ // We could support this, but it will complicate things, and it's
+ // easier to work around with a struct around the inner vector.
+ return Error("nested vector types not supported (wrap in table first)");
+ }
+ if (token_ == ':') {
+ NEXT();
+ if (token_ != kTokenIntegerConstant) {
+ return Error("length of fixed-length array must be an integer value");
+ }
+ uint16_t fixed_length = 0;
+ bool check = StringToNumber(attribute_.c_str(), &fixed_length);
+ if (!check || fixed_length < 1) {
+ return Error(
+ "length of fixed-length array must be positive and fit to "
+ "uint16_t type");
+ }
+ // Check if enum arrays are used in C++ without specifying --scoped-enums
+ if ((opts.lang_to_generate & IDLOptions::kCpp) && !opts.scoped_enums &&
+ IsEnum(subtype)) {
+ return Error(
+ "--scoped-enums must be enabled to use enum arrays in C++\n");
+ }
+ type = Type(BASE_TYPE_ARRAY, subtype.struct_def, subtype.enum_def,
+ fixed_length);
+ NEXT();
+ } else {
+ type = Type(BASE_TYPE_VECTOR, subtype.struct_def, subtype.enum_def);
+ }
+ type.element = subtype.base_type;
+ EXPECT(']');
+ } else {
+ return Error("illegal type syntax");
+ }
+ return NoError();
+}
+
+CheckedError Parser::AddField(StructDef &struct_def, const std::string &name,
+ const Type &type, FieldDef **dest) {
+ auto &field = *new FieldDef();
+ field.value.offset =
+ FieldIndexToOffset(static_cast<voffset_t>(struct_def.fields.vec.size()));
+ field.name = name;
+ field.file = struct_def.file;
+ field.value.type = type;
+ if (struct_def.fixed) { // statically compute the field offset
+ auto size = InlineSize(type);
+ auto alignment = InlineAlignment(type);
+ // structs_ need to have a predictable format, so we need to align to
+ // the largest scalar
+ struct_def.minalign = std::max(struct_def.minalign, alignment);
+ struct_def.PadLastField(alignment);
+ field.value.offset = static_cast<voffset_t>(struct_def.bytesize);
+ struct_def.bytesize += size;
+ }
+ if (struct_def.fields.Add(name, &field))
+ return Error("field already exists: " + name);
+ *dest = &field;
+ return NoError();
+}
+
+CheckedError Parser::ParseField(StructDef &struct_def) {
+ std::string name = attribute_;
+
+ if (LookupStruct(name))
+ return Error("field name can not be the same as table/struct name");
+
+ std::vector<std::string> dc = doc_comment_;
+ EXPECT(kTokenIdentifier);
+ EXPECT(':');
+ Type type;
+ ECHECK(ParseType(type));
+
+ if (struct_def.fixed && !IsScalar(type.base_type) && !IsStruct(type) &&
+ !IsArray(type))
+ return Error("structs_ may contain only scalar or struct fields");
+
+ if (!struct_def.fixed && IsArray(type))
+ return Error("fixed-length array in table must be wrapped in struct");
+
+ if (IsArray(type) && !SupportsAdvancedArrayFeatures()) {
+ return Error(
+ "Arrays are not yet supported in all "
+ "the specified programming languages.");
+ }
+
+ FieldDef *typefield = nullptr;
+ if (type.base_type == BASE_TYPE_UNION) {
+ // For union fields, add a second auto-generated field to hold the type,
+ // with a special suffix.
+ ECHECK(AddField(struct_def, name + UnionTypeFieldSuffix(),
+ type.enum_def->underlying_type, &typefield));
+ } else if (type.base_type == BASE_TYPE_VECTOR &&
+ type.element == BASE_TYPE_UNION) {
+ // Only cpp, js and ts supports the union vector feature so far.
+ if (!SupportsAdvancedUnionFeatures()) {
+ return Error(
+ "Vectors of unions are not yet supported in all "
+ "the specified programming languages.");
+ }
+ // For vector of union fields, add a second auto-generated vector field to
+ // hold the types, with a special suffix.
+ Type union_vector(BASE_TYPE_VECTOR, nullptr, type.enum_def);
+ union_vector.element = BASE_TYPE_UTYPE;
+ ECHECK(AddField(struct_def, name + UnionTypeFieldSuffix(), union_vector,
+ &typefield));
+ }
+
+ FieldDef *field;
+ ECHECK(AddField(struct_def, name, type, &field));
+
+ if (token_ == '=') {
+ NEXT();
+ ECHECK(ParseSingleValue(&field->name, field->value, true));
+ if (!IsScalar(type.base_type) ||
+ (struct_def.fixed && field->value.constant != "0"))
+ return Error(
+ "default values currently only supported for scalars in tables");
+ }
+ // Append .0 if the value has not it (skip hex and scientific floats).
+ // This suffix needed for generated C++ code.
+ if (IsFloat(type.base_type)) {
+ auto &text = field->value.constant;
+ FLATBUFFERS_ASSERT(false == text.empty());
+ auto s = text.c_str();
+ while(*s == ' ') s++;
+ if (*s == '-' || *s == '+') s++;
+ // 1) A float constants (nan, inf, pi, etc) is a kind of identifier.
+ // 2) A float number needn't ".0" at the end if it has exponent.
+ if ((false == IsIdentifierStart(*s)) &&
+ (std::string::npos == field->value.constant.find_first_of(".eEpP"))) {
+ field->value.constant += ".0";
+ }
+ }
+ if (type.enum_def) {
+ // The type.base_type can only be scalar, union, array or vector.
+ // Table, struct or string can't have enum_def.
+ // Default value of union and vector in NONE, NULL translated to "0".
+ FLATBUFFERS_ASSERT(IsInteger(type.base_type) ||
+ (type.base_type == BASE_TYPE_UNION) ||
+ (type.base_type == BASE_TYPE_VECTOR) ||
+ (type.base_type == BASE_TYPE_ARRAY));
+ if (type.base_type == BASE_TYPE_VECTOR) {
+ // Vector can't use initialization list.
+ FLATBUFFERS_ASSERT(field->value.constant == "0");
+ } else {
+ // All unions should have the NONE ("0") enum value.
+ auto in_enum = type.enum_def->attributes.Lookup("bit_flags") ||
+ type.enum_def->FindByValue(field->value.constant);
+ if (false == in_enum)
+ return Error("default value of " + field->value.constant +
+ " for field " + name + " is not part of enum " +
+ type.enum_def->name);
+ }
+ }
+
+ field->doc_comment = dc;
+ ECHECK(ParseMetaData(&field->attributes));
+ field->deprecated = field->attributes.Lookup("deprecated") != nullptr;
+ auto hash_name = field->attributes.Lookup("hash");
+ if (hash_name) {
+ switch ((type.base_type == BASE_TYPE_VECTOR) ? type.element : type.base_type) {
+ case BASE_TYPE_SHORT:
+ case BASE_TYPE_USHORT: {
+ if (FindHashFunction16(hash_name->constant.c_str()) == nullptr)
+ return Error("Unknown hashing algorithm for 16 bit types: " +
+ hash_name->constant);
+ break;
+ }
+ case BASE_TYPE_INT:
+ case BASE_TYPE_UINT: {
+ if (FindHashFunction32(hash_name->constant.c_str()) == nullptr)
+ return Error("Unknown hashing algorithm for 32 bit types: " +
+ hash_name->constant);
+ break;
+ }
+ case BASE_TYPE_LONG:
+ case BASE_TYPE_ULONG: {
+ if (FindHashFunction64(hash_name->constant.c_str()) == nullptr)
+ return Error("Unknown hashing algorithm for 64 bit types: " +
+ hash_name->constant);
+ break;
+ }
+ default:
+ return Error(
+ "only short, ushort, int, uint, long and ulong data types support hashing.");
+ }
+ }
+ auto cpp_type = field->attributes.Lookup("cpp_type");
+ if (cpp_type) {
+ if (!hash_name)
+ return Error("cpp_type can only be used with a hashed field");
+ /// forcing cpp_ptr_type to 'naked' if unset
+ auto cpp_ptr_type = field->attributes.Lookup("cpp_ptr_type");
+ if (!cpp_ptr_type) {
+ auto val = new Value();
+ val->type = cpp_type->type;
+ val->constant = "naked";
+ field->attributes.Add("cpp_ptr_type", val);
+ }
+ }
+ if (field->deprecated && struct_def.fixed)
+ return Error("can't deprecate fields in a struct");
+ field->required = field->attributes.Lookup("required") != nullptr;
+ if (field->required &&
+ (struct_def.fixed || IsScalar(type.base_type)))
+ return Error("only non-scalar fields in tables may be 'required'");
+ field->key = field->attributes.Lookup("key") != nullptr;
+ if (field->key) {
+ if (struct_def.has_key) return Error("only one field may be set as 'key'");
+ struct_def.has_key = true;
+ if (!IsScalar(type.base_type)) {
+ field->required = true;
+ if (type.base_type != BASE_TYPE_STRING)
+ return Error("'key' field must be string or scalar type");
+ }
+ }
+ field->shared = field->attributes.Lookup("shared") != nullptr;
+ if (field->shared && field->value.type.base_type != BASE_TYPE_STRING)
+ return Error("shared can only be defined on strings");
+
+ auto field_native_custom_alloc =
+ field->attributes.Lookup("native_custom_alloc");
+ if (field_native_custom_alloc)
+ return Error(
+ "native_custom_alloc can only be used with a table or struct "
+ "definition");
+
+ field->native_inline = field->attributes.Lookup("native_inline") != nullptr;
+ if (field->native_inline && !IsStruct(field->value.type))
+ return Error("native_inline can only be defined on structs");
+
+ auto nested = field->attributes.Lookup("nested_flatbuffer");
+ if (nested) {
+ if (nested->type.base_type != BASE_TYPE_STRING)
+ return Error(
+ "nested_flatbuffer attribute must be a string (the root type)");
+ if (type.base_type != BASE_TYPE_VECTOR || type.element != BASE_TYPE_UCHAR)
+ return Error(
+ "nested_flatbuffer attribute may only apply to a vector of ubyte");
+ // This will cause an error if the root type of the nested flatbuffer
+ // wasn't defined elsewhere.
+ field->nested_flatbuffer = LookupCreateStruct(nested->constant);
+ }
+
+ if (field->attributes.Lookup("flexbuffer")) {
+ field->flexbuffer = true;
+ uses_flexbuffers_ = true;
+ if (type.base_type != BASE_TYPE_VECTOR ||
+ type.element != BASE_TYPE_UCHAR)
+ return Error("flexbuffer attribute may only apply to a vector of ubyte");
+ }
+
+ if (typefield) {
+ if (!IsScalar(typefield->value.type.base_type)) {
+ // this is a union vector field
+ typefield->required = field->required;
+ }
+ // If this field is a union, and it has a manually assigned id,
+ // the automatically added type field should have an id as well (of N - 1).
+ auto attr = field->attributes.Lookup("id");
+ if (attr) {
+ auto id = atoi(attr->constant.c_str());
+ auto val = new Value();
+ val->type = attr->type;
+ val->constant = NumToString(id - 1);
+ typefield->attributes.Add("id", val);
+ }
+ }
+
+ EXPECT(';');
+ return NoError();
+}
+
+CheckedError Parser::ParseString(Value &val) {
+ auto s = attribute_;
+ EXPECT(kTokenStringConstant);
+ val.constant = NumToString(builder_.CreateString(s).o);
+ return NoError();
+}
+
+CheckedError Parser::ParseComma() {
+ if (!opts.protobuf_ascii_alike) EXPECT(',');
+ return NoError();
+}
+
+CheckedError Parser::ParseAnyValue(Value &val, FieldDef *field,
+ size_t parent_fieldn,
+ const StructDef *parent_struct_def,
+ uoffset_t count,
+ bool inside_vector) {
+ switch (val.type.base_type) {
+ case BASE_TYPE_UNION: {
+ FLATBUFFERS_ASSERT(field);
+ std::string constant;
+ Vector<uint8_t> *vector_of_union_types = nullptr;
+ // Find corresponding type field we may have already parsed.
+ for (auto elem = field_stack_.rbegin() + count;
+ elem != field_stack_.rbegin() + parent_fieldn + count; ++elem) {
+ auto &type = elem->second->value.type;
+ if (type.enum_def == val.type.enum_def) {
+ if (inside_vector) {
+ if (type.base_type == BASE_TYPE_VECTOR &&
+ type.element == BASE_TYPE_UTYPE) {
+ // Vector of union type field.
+ uoffset_t offset;
+ ECHECK(atot(elem->first.constant.c_str(), *this, &offset));
+ vector_of_union_types = reinterpret_cast<Vector<uint8_t> *>(
+ builder_.GetCurrentBufferPointer() +
+ builder_.GetSize() - offset);
+ break;
+ }
+ } else {
+ if (type.base_type == BASE_TYPE_UTYPE) {
+ // Union type field.
+ constant = elem->first.constant;
+ break;
+ }
+ }
+ }
+ }
+ if (constant.empty() && !inside_vector) {
+ // We haven't seen the type field yet. Sadly a lot of JSON writers
+ // output these in alphabetical order, meaning it comes after this
+ // value. So we scan past the value to find it, then come back here.
+ // We currently don't do this for vectors of unions because the
+ // scanning/serialization logic would get very complicated.
+ auto type_name = field->name + UnionTypeFieldSuffix();
+ FLATBUFFERS_ASSERT(parent_struct_def);
+ auto type_field = parent_struct_def->fields.Lookup(type_name);
+ FLATBUFFERS_ASSERT(type_field); // Guaranteed by ParseField().
+ // Remember where we are in the source file, so we can come back here.
+ auto backup = *static_cast<ParserState *>(this);
+ ECHECK(SkipAnyJsonValue()); // The table.
+ ECHECK(ParseComma());
+ auto next_name = attribute_;
+ if (Is(kTokenStringConstant)) {
+ NEXT();
+ } else {
+ EXPECT(kTokenIdentifier);
+ }
+ if (next_name == type_name) {
+ EXPECT(':');
+ Value type_val = type_field->value;
+ ECHECK(ParseAnyValue(type_val, type_field, 0, nullptr, 0));
+ constant = type_val.constant;
+ // Got the information we needed, now rewind:
+ *static_cast<ParserState *>(this) = backup;
+ }
+ }
+ if (constant.empty() && !vector_of_union_types) {
+ return Error("missing type field for this union value: " +
+ field->name);
+ }
+ uint8_t enum_idx;
+ if (vector_of_union_types) {
+ enum_idx = vector_of_union_types->Get(count);
+ } else {
+ ECHECK(atot(constant.c_str(), *this, &enum_idx));
+ }
+ auto enum_val = val.type.enum_def->ReverseLookup(enum_idx, true);
+ if (!enum_val) return Error("illegal type id for: " + field->name);
+ if (enum_val->union_type.base_type == BASE_TYPE_STRUCT) {
+ ECHECK(ParseTable(*enum_val->union_type.struct_def, &val.constant,
+ nullptr));
+ if (enum_val->union_type.struct_def->fixed) {
+ // All BASE_TYPE_UNION values are offsets, so turn this into one.
+ SerializeStruct(*enum_val->union_type.struct_def, val);
+ builder_.ClearOffsets();
+ val.constant = NumToString(builder_.GetSize());
+ }
+ } else if (enum_val->union_type.base_type == BASE_TYPE_STRING) {
+ ECHECK(ParseString(val));
+ } else {
+ FLATBUFFERS_ASSERT(false);
+ }
+ break;
+ }
+ case BASE_TYPE_STRUCT:
+ ECHECK(ParseTable(*val.type.struct_def, &val.constant, nullptr));
+ break;
+ case BASE_TYPE_STRING: {
+ ECHECK(ParseString(val));
+ break;
+ }
+ case BASE_TYPE_VECTOR: {
+ uoffset_t off;
+ ECHECK(ParseVector(val.type.VectorType(), &off, field, parent_fieldn));
+ val.constant = NumToString(off);
+ break;
+ }
+ case BASE_TYPE_ARRAY: {
+ ECHECK(ParseArray(val));
+ break;
+ }
+ case BASE_TYPE_INT:
+ case BASE_TYPE_UINT:
+ case BASE_TYPE_LONG:
+ case BASE_TYPE_ULONG: {
+ if (field && field->attributes.Lookup("hash") &&
+ (token_ == kTokenIdentifier || token_ == kTokenStringConstant)) {
+ ECHECK(ParseHash(val, field));
+ } else {
+ ECHECK(ParseSingleValue(field ? &field->name : nullptr, val, false));
+ }
+ break;
+ }
+ default:
+ ECHECK(ParseSingleValue(field ? &field->name : nullptr, val, false));
+ break;
+ }
+ return NoError();
+}
+
+void Parser::SerializeStruct(const StructDef &struct_def, const Value &val) {
+ SerializeStruct(builder_, struct_def, val);
+}
+
+void Parser::SerializeStruct(FlatBufferBuilder &builder,
+ const StructDef &struct_def, const Value &val) {
+ FLATBUFFERS_ASSERT(val.constant.length() == struct_def.bytesize);
+ builder.Align(struct_def.minalign);
+ builder.PushBytes(reinterpret_cast<const uint8_t *>(val.constant.c_str()),
+ struct_def.bytesize);
+ builder.AddStructOffset(val.offset, builder.GetSize());
+}
+
+template <typename F>
+CheckedError Parser::ParseTableDelimiters(size_t &fieldn,
+ const StructDef *struct_def,
+ F body) {
+ // We allow tables both as JSON object{ .. } with field names
+ // or vector[..] with all fields in order
+ char terminator = '}';
+ bool is_nested_vector = struct_def && Is('[');
+ if (is_nested_vector) {
+ NEXT();
+ terminator = ']';
+ } else {
+ EXPECT('{');
+ }
+ for (;;) {
+ if ((!opts.strict_json || !fieldn) && Is(terminator)) break;
+ std::string name;
+ if (is_nested_vector) {
+ if (fieldn >= struct_def->fields.vec.size()) {
+ return Error("too many unnamed fields in nested array");
+ }
+ name = struct_def->fields.vec[fieldn]->name;
+ } else {
+ name = attribute_;
+ if (Is(kTokenStringConstant)) {
+ NEXT();
+ } else {
+ EXPECT(opts.strict_json ? kTokenStringConstant : kTokenIdentifier);
+ }
+ if (!opts.protobuf_ascii_alike || !(Is('{') || Is('['))) EXPECT(':');
+ }
+ ECHECK(body(name, fieldn, struct_def));
+ if (Is(terminator)) break;
+ ECHECK(ParseComma());
+ }
+ NEXT();
+ if (is_nested_vector && fieldn != struct_def->fields.vec.size()) {
+ return Error("wrong number of unnamed fields in table vector");
+ }
+ return NoError();
+}
+
+CheckedError Parser::ParseTable(const StructDef &struct_def, std::string *value,
+ uoffset_t *ovalue) {
+ size_t fieldn_outer = 0;
+ auto err = ParseTableDelimiters(
+ fieldn_outer, &struct_def,
+ [&](const std::string &name, size_t &fieldn,
+ const StructDef *struct_def_inner) -> CheckedError {
+ if (name == "$schema") {
+ ECHECK(Expect(kTokenStringConstant));
+ return NoError();
+ }
+ auto field = struct_def_inner->fields.Lookup(name);
+ if (!field) {
+ if (!opts.skip_unexpected_fields_in_json) {
+ return Error("unknown field: " + name);
+ } else {
+ ECHECK(SkipAnyJsonValue());
+ }
+ } else {
+ if (IsIdent("null") && !IsScalar(field->value.type.base_type)) {
+ ECHECK(Next()); // Ignore this field.
+ } else {
+ Value val = field->value;
+ if (field->flexbuffer) {
+ flexbuffers::Builder builder(1024,
+ flexbuffers::BUILDER_FLAG_SHARE_ALL);
+ ECHECK(ParseFlexBufferValue(&builder));
+ builder.Finish();
+ // Force alignment for nested flexbuffer
+ builder_.ForceVectorAlignment(builder.GetSize(), sizeof(uint8_t),
+ sizeof(largest_scalar_t));
+ auto off = builder_.CreateVector(builder.GetBuffer());
+ val.constant = NumToString(off.o);
+ } else if (field->nested_flatbuffer) {
+ ECHECK(
+ ParseNestedFlatbuffer(val, field, fieldn, struct_def_inner));
+ } else {
+ ECHECK(Recurse([&]() {
+ return ParseAnyValue(val, field, fieldn, struct_def_inner, 0);
+ }));
+ }
+ // Hardcoded insertion-sort with error-check.
+ // If fields are specified in order, then this loop exits
+ // immediately.
+ auto elem = field_stack_.rbegin();
+ for (; elem != field_stack_.rbegin() + fieldn; ++elem) {
+ auto existing_field = elem->second;
+ if (existing_field == field)
+ return Error("field set more than once: " + field->name);
+ if (existing_field->value.offset < field->value.offset) break;
+ }
+ // Note: elem points to before the insertion point, thus .base()
+ // points to the correct spot.
+ field_stack_.insert(elem.base(), std::make_pair(val, field));
+ fieldn++;
+ }
+ }
+ return NoError();
+ });
+ ECHECK(err);
+
+ // Check if all required fields are parsed.
+ for (auto field_it = struct_def.fields.vec.begin();
+ field_it != struct_def.fields.vec.end(); ++field_it) {
+ auto required_field = *field_it;
+ if (!required_field->required) { continue; }
+ bool found = false;
+ for (auto pf_it = field_stack_.end() - fieldn_outer;
+ pf_it != field_stack_.end(); ++pf_it) {
+ auto parsed_field = pf_it->second;
+ if (parsed_field == required_field) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return Error("required field is missing: " + required_field->name +
+ " in " + struct_def.name);
+ }
+ }
+
+ if (struct_def.fixed && fieldn_outer != struct_def.fields.vec.size())
+ return Error("struct: wrong number of initializers: " + struct_def.name);
+
+ auto start = struct_def.fixed ? builder_.StartStruct(struct_def.minalign)
+ : builder_.StartTable();
+
+ for (size_t size = struct_def.sortbysize ? sizeof(largest_scalar_t) : 1; size;
+ size /= 2) {
+ // Go through elements in reverse, since we're building the data backwards.
+ for (auto it = field_stack_.rbegin();
+ it != field_stack_.rbegin() + fieldn_outer; ++it) {
+ auto &field_value = it->first;
+ auto field = it->second;
+ if (!struct_def.sortbysize ||
+ size == SizeOf(field_value.type.base_type)) {
+ switch (field_value.type.base_type) {
+ // clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ case BASE_TYPE_ ## ENUM: \
+ builder_.Pad(field->padding); \
+ if (struct_def.fixed) { \
+ CTYPE val; \
+ ECHECK(atot(field_value.constant.c_str(), *this, &val)); \
+ builder_.PushElement(val); \
+ } else { \
+ CTYPE val, valdef; \
+ ECHECK(atot(field_value.constant.c_str(), *this, &val)); \
+ ECHECK(atot(field->value.constant.c_str(), *this, &valdef)); \
+ builder_.AddElement(field_value.offset, val, valdef); \
+ } \
+ break;
+ FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD);
+ #undef FLATBUFFERS_TD
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ case BASE_TYPE_ ## ENUM: \
+ builder_.Pad(field->padding); \
+ if (IsStruct(field->value.type)) { \
+ SerializeStruct(*field->value.type.struct_def, field_value); \
+ } else { \
+ CTYPE val; \
+ ECHECK(atot(field_value.constant.c_str(), *this, &val)); \
+ builder_.AddOffset(field_value.offset, val); \
+ } \
+ break;
+ FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD);
+ #undef FLATBUFFERS_TD
+ case BASE_TYPE_ARRAY:
+ builder_.Pad(field->padding);
+ builder_.PushBytes(
+ reinterpret_cast<const uint8_t*>(field_value.constant.c_str()),
+ InlineSize(field_value.type));
+ break;
+ // clang-format on
+ }
+ }
+ }
+ }
+ for (size_t i = 0; i < fieldn_outer; i++) field_stack_.pop_back();
+
+ if (struct_def.fixed) {
+ builder_.ClearOffsets();
+ builder_.EndStruct();
+ FLATBUFFERS_ASSERT(value);
+ // Temporarily store this struct in the value string, since it is to
+ // be serialized in-place elsewhere.
+ value->assign(
+ reinterpret_cast<const char *>(builder_.GetCurrentBufferPointer()),
+ struct_def.bytesize);
+ builder_.PopBytes(struct_def.bytesize);
+ FLATBUFFERS_ASSERT(!ovalue);
+ } else {
+ auto val = builder_.EndTable(start);
+ if (ovalue) *ovalue = val;
+ if (value) *value = NumToString(val);
+ }
+ return NoError();
+}
+
+template <typename F>
+CheckedError Parser::ParseVectorDelimiters(uoffset_t &count, F body) {
+ EXPECT('[');
+ for (;;) {
+ if ((!opts.strict_json || !count) && Is(']')) break;
+ ECHECK(body(count));
+ count++;
+ if (Is(']')) break;
+ ECHECK(ParseComma());
+ }
+ NEXT();
+ return NoError();
+}
+
+CheckedError Parser::ParseVector(const Type &type, uoffset_t *ovalue,
+ FieldDef *field, size_t fieldn) {
+ uoffset_t count = 0;
+ auto err = ParseVectorDelimiters(count, [&](uoffset_t &) -> CheckedError {
+ Value val;
+ val.type = type;
+ ECHECK(Recurse([&]() {
+ return ParseAnyValue(val, field, fieldn, nullptr, count, true);
+ }));
+ field_stack_.push_back(std::make_pair(val, nullptr));
+ return NoError();
+ });
+ ECHECK(err);
+
+ builder_.StartVector(count * InlineSize(type) / InlineAlignment(type),
+ InlineAlignment(type));
+ for (uoffset_t i = 0; i < count; i++) {
+ // start at the back, since we're building the data backwards.
+ auto &val = field_stack_.back().first;
+ switch (val.type.base_type) {
+ // clang-format off
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ case BASE_TYPE_ ## ENUM: \
+ if (IsStruct(val.type)) SerializeStruct(*val.type.struct_def, val); \
+ else { \
+ CTYPE elem; \
+ ECHECK(atot(val.constant.c_str(), *this, &elem)); \
+ builder_.PushElement(elem); \
+ } \
+ break;
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ // clang-format on
+ }
+ field_stack_.pop_back();
+ }
+
+ builder_.ClearOffsets();
+ *ovalue = builder_.EndVector(count);
+ return NoError();
+}
+
+CheckedError Parser::ParseArray(Value &array) {
+ std::vector<Value> stack;
+ FlatBufferBuilder builder;
+ const auto &type = array.type.VectorType();
+ auto length = array.type.fixed_length;
+ uoffset_t count = 0;
+ auto err = ParseVectorDelimiters(count, [&](uoffset_t &) -> CheckedError {
+ vector_emplace_back(&stack, Value());
+ auto &val = stack.back();
+ val.type = type;
+ if (IsStruct(type)) {
+ ECHECK(ParseTable(*val.type.struct_def, &val.constant, nullptr));
+ } else {
+ ECHECK(ParseSingleValue(nullptr, val, false));
+ }
+ return NoError();
+ });
+ ECHECK(err);
+ if (length != count) return Error("Fixed-length array size is incorrect.");
+
+ for (auto it = stack.rbegin(); it != stack.rend(); ++it) {
+ auto &val = *it;
+ // clang-format off
+ switch (val.type.base_type) {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ case BASE_TYPE_ ## ENUM: \
+ if (IsStruct(val.type)) { \
+ SerializeStruct(builder, *val.type.struct_def, val); \
+ } else { \
+ CTYPE elem; \
+ ECHECK(atot(val.constant.c_str(), *this, &elem)); \
+ builder.PushElement(elem); \
+ } \
+ break;
+ FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
+ #undef FLATBUFFERS_TD
+ default: FLATBUFFERS_ASSERT(0);
+ }
+ // clang-format on
+ }
+
+ array.constant.assign(
+ reinterpret_cast<const char *>(builder.GetCurrentBufferPointer()),
+ InlineSize(array.type));
+ return NoError();
+}
+
+CheckedError Parser::ParseNestedFlatbuffer(Value &val, FieldDef *field,
+ size_t fieldn,
+ const StructDef *parent_struct_def) {
+ if (token_ == '[') { // backwards compat for 'legacy' ubyte buffers
+ ECHECK(ParseAnyValue(val, field, fieldn, parent_struct_def, 0));
+ } else {
+ auto cursor_at_value_begin = cursor_;
+ ECHECK(SkipAnyJsonValue());
+ std::string substring(cursor_at_value_begin - 1, cursor_ - 1);
+
+ // Create and initialize new parser
+ Parser nested_parser;
+ FLATBUFFERS_ASSERT(field->nested_flatbuffer);
+ nested_parser.root_struct_def_ = field->nested_flatbuffer;
+ nested_parser.enums_ = enums_;
+ nested_parser.opts = opts;
+ nested_parser.uses_flexbuffers_ = uses_flexbuffers_;
+
+ // Parse JSON substring into new flatbuffer builder using nested_parser
+ bool ok = nested_parser.Parse(substring.c_str(), nullptr, nullptr);
+
+ // Clean nested_parser to avoid deleting the elements in
+ // the SymbolTables on destruction
+ nested_parser.enums_.dict.clear();
+ nested_parser.enums_.vec.clear();
+
+ if (!ok) {
+ ECHECK(Error(nested_parser.error_));
+ }
+ // Force alignment for nested flatbuffer
+ builder_.ForceVectorAlignment(nested_parser.builder_.GetSize(), sizeof(uint8_t),
+ nested_parser.builder_.GetBufferMinAlignment());
+
+ auto off = builder_.CreateVector(nested_parser.builder_.GetBufferPointer(),
+ nested_parser.builder_.GetSize());
+ val.constant = NumToString(off.o);
+ }
+ return NoError();
+}
+
+CheckedError Parser::ParseMetaData(SymbolTable<Value> *attributes) {
+ if (Is('(')) {
+ NEXT();
+ for (;;) {
+ auto name = attribute_;
+ if (false == (Is(kTokenIdentifier) || Is(kTokenStringConstant)))
+ return Error("attribute name must be either identifier or string: " +
+ name);
+ if (known_attributes_.find(name) == known_attributes_.end())
+ return Error("user define attributes must be declared before use: " +
+ name);
+ NEXT();
+ auto e = new Value();
+ attributes->Add(name, e);
+ if (Is(':')) {
+ NEXT();
+ ECHECK(ParseSingleValue(&name, *e, true));
+ }
+ if (Is(')')) {
+ NEXT();
+ break;
+ }
+ EXPECT(',');
+ }
+ }
+ return NoError();
+}
+
+CheckedError Parser::TryTypedValue(const std::string *name, int dtoken,
+ bool check, Value &e, BaseType req,
+ bool *destmatch) {
+ bool match = dtoken == token_;
+ if (match) {
+ FLATBUFFERS_ASSERT(*destmatch == false);
+ *destmatch = true;
+ e.constant = attribute_;
+ // Check token match
+ if (!check) {
+ if (e.type.base_type == BASE_TYPE_NONE) {
+ e.type.base_type = req;
+ } else {
+ return Error(
+ std::string("type mismatch: expecting: ") +
+ kTypeNames[e.type.base_type] + ", found: " + kTypeNames[req] +
+ ", name: " + (name ? *name : "") + ", value: " + e.constant);
+ }
+ }
+ // The exponent suffix of hexadecimal float-point number is mandatory.
+ // A hex-integer constant is forbidden as an initializer of float number.
+ if ((kTokenFloatConstant != dtoken) && IsFloat(e.type.base_type)) {
+ const auto &s = e.constant;
+ const auto k = s.find_first_of("0123456789.");
+ if ((std::string::npos != k) && (s.length() > (k + 1)) &&
+ (s[k] == '0' && is_alpha_char(s[k + 1], 'X')) &&
+ (std::string::npos == s.find_first_of("pP", k + 2))) {
+ return Error(
+ "invalid number, the exponent suffix of hexadecimal "
+ "floating-point literals is mandatory: \"" +
+ s + "\"");
+ }
+ }
+
+ NEXT();
+ }
+ return NoError();
+}
+
+CheckedError Parser::ParseEnumFromString(const Type &type,
+ std::string *result) {
+ const auto base_type =
+ type.enum_def ? type.enum_def->underlying_type.base_type : type.base_type;
+ if (!IsInteger(base_type)) return Error("not a valid value for this field");
+ uint64_t u64 = 0;
+ for (size_t pos = 0; pos != std::string::npos;) {
+ const auto delim = attribute_.find_first_of(' ', pos);
+ const auto last = (std::string::npos == delim);
+ auto word = attribute_.substr(pos, !last ? delim - pos : std::string::npos);
+ pos = !last ? delim + 1 : std::string::npos;
+ const EnumVal *ev = nullptr;
+ if (type.enum_def) {
+ ev = type.enum_def->Lookup(word);
+ } else {
+ auto dot = word.find_first_of('.');
+ if (std::string::npos == dot)
+ return Error("enum values need to be qualified by an enum type");
+ auto enum_def_str = word.substr(0, dot);
+ const auto enum_def = LookupEnum(enum_def_str);
+ if (!enum_def) return Error("unknown enum: " + enum_def_str);
+ auto enum_val_str = word.substr(dot + 1);
+ ev = enum_def->Lookup(enum_val_str);
+ }
+ if (!ev) return Error("unknown enum value: " + word);
+ u64 |= ev->GetAsUInt64();
+ }
+ *result = IsUnsigned(base_type) ? NumToString(u64)
+ : NumToString(static_cast<int64_t>(u64));
+ return NoError();
+}
+
+CheckedError Parser::ParseHash(Value &e, FieldDef *field) {
+ FLATBUFFERS_ASSERT(field);
+ Value *hash_name = field->attributes.Lookup("hash");
+ switch (e.type.base_type) {
+ case BASE_TYPE_SHORT: {
+ auto hash = FindHashFunction16(hash_name->constant.c_str());
+ int16_t hashed_value = static_cast<int16_t>(hash(attribute_.c_str()));
+ e.constant = NumToString(hashed_value);
+ break;
+ }
+ case BASE_TYPE_USHORT: {
+ auto hash = FindHashFunction16(hash_name->constant.c_str());
+ uint16_t hashed_value = hash(attribute_.c_str());
+ e.constant = NumToString(hashed_value);
+ break;
+ }
+ case BASE_TYPE_INT: {
+ auto hash = FindHashFunction32(hash_name->constant.c_str());
+ int32_t hashed_value = static_cast<int32_t>(hash(attribute_.c_str()));
+ e.constant = NumToString(hashed_value);
+ break;
+ }
+ case BASE_TYPE_UINT: {
+ auto hash = FindHashFunction32(hash_name->constant.c_str());
+ uint32_t hashed_value = hash(attribute_.c_str());
+ e.constant = NumToString(hashed_value);
+ break;
+ }
+ case BASE_TYPE_LONG: {
+ auto hash = FindHashFunction64(hash_name->constant.c_str());
+ int64_t hashed_value = static_cast<int64_t>(hash(attribute_.c_str()));
+ e.constant = NumToString(hashed_value);
+ break;
+ }
+ case BASE_TYPE_ULONG: {
+ auto hash = FindHashFunction64(hash_name->constant.c_str());
+ uint64_t hashed_value = hash(attribute_.c_str());
+ e.constant = NumToString(hashed_value);
+ break;
+ }
+ default: FLATBUFFERS_ASSERT(0);
+ }
+ NEXT();
+ return NoError();
+}
+
+CheckedError Parser::TokenError() {
+ return Error("cannot parse value starting with: " + TokenToStringId(token_));
+}
+
+// Re-pack helper (ParseSingleValue) to normalize defaults of scalars.
+template<typename T> inline void SingleValueRepack(Value &e, T val) {
+ // Remove leading zeros.
+ if (IsInteger(e.type.base_type)) { e.constant = NumToString(val); }
+}
+#if defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0)
+// Normilaze defaults NaN to unsigned quiet-NaN(0).
+static inline void SingleValueRepack(Value& e, float val) {
+ if (val != val) e.constant = "nan";
+}
+static inline void SingleValueRepack(Value& e, double val) {
+ if (val != val) e.constant = "nan";
+}
+#endif
+
+CheckedError Parser::ParseSingleValue(const std::string *name, Value &e,
+ bool check_now) {
+ // First see if this could be a conversion function:
+ if (token_ == kTokenIdentifier && *cursor_ == '(') {
+ // todo: Extract processing of conversion functions to ParseFunction.
+ const auto functionname = attribute_;
+ if (!IsFloat(e.type.base_type)) {
+ return Error(functionname + ": type of argument mismatch, expecting: " +
+ kTypeNames[BASE_TYPE_DOUBLE] +
+ ", found: " + kTypeNames[e.type.base_type] +
+ ", name: " + (name ? *name : "") + ", value: " + e.constant);
+ }
+ NEXT();
+ EXPECT('(');
+ ECHECK(Recurse([&]() { return ParseSingleValue(name, e, false); }));
+ EXPECT(')');
+ // calculate with double precision
+ double x, y = 0.0;
+ ECHECK(atot(e.constant.c_str(), *this, &x));
+ auto func_match = false;
+ // clang-format off
+ #define FLATBUFFERS_FN_DOUBLE(name, op) \
+ if (!func_match && functionname == name) { y = op; func_match = true; }
+ FLATBUFFERS_FN_DOUBLE("deg", x / kPi * 180);
+ FLATBUFFERS_FN_DOUBLE("rad", x * kPi / 180);
+ FLATBUFFERS_FN_DOUBLE("sin", sin(x));
+ FLATBUFFERS_FN_DOUBLE("cos", cos(x));
+ FLATBUFFERS_FN_DOUBLE("tan", tan(x));
+ FLATBUFFERS_FN_DOUBLE("asin", asin(x));
+ FLATBUFFERS_FN_DOUBLE("acos", acos(x));
+ FLATBUFFERS_FN_DOUBLE("atan", atan(x));
+ // TODO(wvo): add more useful conversion functions here.
+ #undef FLATBUFFERS_FN_DOUBLE
+ // clang-format on
+ if (true != func_match) {
+ return Error(std::string("Unknown conversion function: ") + functionname +
+ ", field name: " + (name ? *name : "") +
+ ", value: " + e.constant);
+ }
+ e.constant = NumToString(y);
+ return NoError();
+ }
+
+ auto match = false;
+ const auto in_type = e.type.base_type;
+ // clang-format off
+ #define IF_ECHECK_(force, dtoken, check, req) \
+ if (!match && ((check) || IsConstTrue(force))) \
+ ECHECK(TryTypedValue(name, dtoken, check, e, req, &match))
+ #define TRY_ECHECK(dtoken, check, req) IF_ECHECK_(false, dtoken, check, req)
+ #define FORCE_ECHECK(dtoken, check, req) IF_ECHECK_(true, dtoken, check, req)
+ // clang-format on
+
+ if (token_ == kTokenStringConstant || token_ == kTokenIdentifier) {
+ const auto kTokenStringOrIdent = token_;
+ // The string type is a most probable type, check it first.
+ TRY_ECHECK(kTokenStringConstant, in_type == BASE_TYPE_STRING,
+ BASE_TYPE_STRING);
+
+ // avoid escaped and non-ascii in the string
+ if (!match && (token_ == kTokenStringConstant) && IsScalar(in_type) &&
+ !attr_is_trivial_ascii_string_) {
+ return Error(
+ std::string("type mismatch or invalid value, an initializer of "
+ "non-string field must be trivial ASCII string: type: ") +
+ kTypeNames[in_type] + ", name: " + (name ? *name : "") +
+ ", value: " + attribute_);
+ }
+
+ // A boolean as true/false. Boolean as Integer check below.
+ if (!match && IsBool(in_type)) {
+ auto is_true = attribute_ == "true";
+ if (is_true || attribute_ == "false") {
+ attribute_ = is_true ? "1" : "0";
+ // accepts both kTokenStringConstant and kTokenIdentifier
+ TRY_ECHECK(kTokenStringOrIdent, IsBool(in_type), BASE_TYPE_BOOL);
+ }
+ }
+ // Check if this could be a string/identifier enum value.
+ // Enum can have only true integer base type.
+ if (!match && IsInteger(in_type) && !IsBool(in_type) &&
+ IsIdentifierStart(*attribute_.c_str())) {
+ ECHECK(ParseEnumFromString(e.type, &e.constant));
+ NEXT();
+ match = true;
+ }
+ // Parse a float/integer number from the string.
+ if (!match) check_now = true; // Re-pack if parsed from string literal.
+ if (!match && (token_ == kTokenStringConstant) && IsScalar(in_type)) {
+ // remove trailing whitespaces from attribute_
+ auto last = attribute_.find_last_not_of(' ');
+ if (std::string::npos != last) // has non-whitespace
+ attribute_.resize(last + 1);
+ }
+ // Float numbers or nan, inf, pi, etc.
+ TRY_ECHECK(kTokenStringOrIdent, IsFloat(in_type), BASE_TYPE_FLOAT);
+ // An integer constant in string.
+ TRY_ECHECK(kTokenStringOrIdent, IsInteger(in_type), BASE_TYPE_INT);
+ // Unknown tokens will be interpreted as string type.
+ // An attribute value may be a scalar or string constant.
+ FORCE_ECHECK(kTokenStringConstant, in_type == BASE_TYPE_STRING,
+ BASE_TYPE_STRING);
+ } else {
+ // Try a float number.
+ TRY_ECHECK(kTokenFloatConstant, IsFloat(in_type), BASE_TYPE_FLOAT);
+ // Integer token can init any scalar (integer of float).
+ FORCE_ECHECK(kTokenIntegerConstant, IsScalar(in_type), BASE_TYPE_INT);
+ }
+#undef FORCE_ECHECK
+#undef TRY_ECHECK
+#undef IF_ECHECK_
+
+ if (!match) {
+ std::string msg;
+ msg += "Cannot assign token starting with '" + TokenToStringId(token_) +
+ "' to value of <" + std::string(kTypeNames[in_type]) + "> type.";
+ return Error(msg);
+ }
+ const auto match_type = e.type.base_type; // may differ from in_type
+ // The check_now flag must be true when parse a fbs-schema.
+ // This flag forces to check default scalar values or metadata of field.
+ // For JSON parser the flag should be false.
+ // If it is set for JSON each value will be checked twice (see ParseTable).
+ if (check_now && IsScalar(match_type)) {
+ // clang-format off
+ switch (match_type) {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
+ CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE) \
+ case BASE_TYPE_ ## ENUM: {\
+ CTYPE val; \
+ ECHECK(atot(e.constant.c_str(), *this, &val)); \
+ SingleValueRepack(e, val); \
+ break; }
+ FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD);
+ #undef FLATBUFFERS_TD
+ default: break;
+ }
+ // clang-format on
+ }
+ return NoError();
+}
+
+StructDef *Parser::LookupCreateStruct(const std::string &name,
+ bool create_if_new, bool definition) {
+ std::string qualified_name = current_namespace_->GetFullyQualifiedName(name);
+ // See if it exists pre-declared by an unqualified use.
+ auto struct_def = LookupStruct(name);
+ if (struct_def && struct_def->predecl) {
+ if (definition) {
+ // Make sure it has the current namespace, and is registered under its
+ // qualified name.
+ struct_def->defined_namespace = current_namespace_;
+ structs_.Move(name, qualified_name);
+ }
+ return struct_def;
+ }
+ // See if it exists pre-declared by an qualified use.
+ struct_def = LookupStruct(qualified_name);
+ if (struct_def && struct_def->predecl) {
+ if (definition) {
+ // Make sure it has the current namespace.
+ struct_def->defined_namespace = current_namespace_;
+ }
+ return struct_def;
+ }
+ if (!definition) {
+ // Search thru parent namespaces.
+ for (size_t components = current_namespace_->components.size();
+ components && !struct_def; components--) {
+ struct_def = LookupStruct(
+ current_namespace_->GetFullyQualifiedName(name, components - 1));
+ }
+ }
+ if (!struct_def && create_if_new) {
+ struct_def = new StructDef();
+ if (definition) {
+ structs_.Add(qualified_name, struct_def);
+ struct_def->name = name;
+ struct_def->defined_namespace = current_namespace_;
+ } else {
+ // Not a definition.
+ // Rather than failing, we create a "pre declared" StructDef, due to
+ // circular references, and check for errors at the end of parsing.
+ // It is defined in the current namespace, as the best guess what the
+ // final namespace will be.
+ structs_.Add(name, struct_def);
+ struct_def->name = name;
+ struct_def->defined_namespace = current_namespace_;
+ struct_def->original_location.reset(
+ new std::string(file_being_parsed_ + ":" + NumToString(line_)));
+ }
+ }
+ return struct_def;
+}
+
+const EnumVal *EnumDef::MinValue() const {
+ return vals.vec.empty() ? nullptr : vals.vec.front();
+}
+const EnumVal *EnumDef::MaxValue() const {
+ return vals.vec.empty() ? nullptr : vals.vec.back();
+}
+
+template<typename T> static uint64_t EnumDistanceImpl(T e1, T e2) {
+ if (e1 < e2) { std::swap(e1, e2); } // use std for scalars
+ // Signed overflow may occur, use unsigned calculation.
+ // The unsigned overflow is well-defined by C++ standard (modulo 2^n).
+ return static_cast<uint64_t>(e1) - static_cast<uint64_t>(e2);
+}
+
+uint64_t EnumDef::Distance(const EnumVal *v1, const EnumVal *v2) const {
+ return IsUInt64() ? EnumDistanceImpl(v1->GetAsUInt64(), v2->GetAsUInt64())
+ : EnumDistanceImpl(v1->GetAsInt64(), v2->GetAsInt64());
+}
+
+std::string EnumDef::AllFlags() const {
+ FLATBUFFERS_ASSERT(attributes.Lookup("bit_flags"));
+ uint64_t u64 = 0;
+ for (auto it = Vals().begin(); it != Vals().end(); ++it) {
+ u64 |= (*it)->GetAsUInt64();
+ }
+ return IsUInt64() ? NumToString(u64) : NumToString(static_cast<int64_t>(u64));
+}
+
+EnumVal *EnumDef::ReverseLookup(int64_t enum_idx,
+ bool skip_union_default) const {
+ auto skip_first = static_cast<int>(is_union && skip_union_default);
+ for (auto it = Vals().begin() + skip_first; it != Vals().end(); ++it) {
+ if ((*it)->GetAsInt64() == enum_idx) { return *it; }
+ }
+ return nullptr;
+}
+
+EnumVal *EnumDef::FindByValue(const std::string &constant) const {
+ int64_t i64;
+ auto done = false;
+ if (IsUInt64()) {
+ uint64_t u64; // avoid reinterpret_cast of pointers
+ done = StringToNumber(constant.c_str(), &u64);
+ i64 = static_cast<int64_t>(u64);
+ } else {
+ done = StringToNumber(constant.c_str(), &i64);
+ }
+ FLATBUFFERS_ASSERT(done);
+ if (!done) return nullptr;
+ return ReverseLookup(i64, false);
+}
+
+void EnumDef::SortByValue() {
+ auto &v = vals.vec;
+ if (IsUInt64())
+ std::sort(v.begin(), v.end(), [](const EnumVal *e1, const EnumVal *e2) {
+ return e1->GetAsUInt64() < e2->GetAsUInt64();
+ });
+ else
+ std::sort(v.begin(), v.end(), [](const EnumVal *e1, const EnumVal *e2) {
+ return e1->GetAsInt64() < e2->GetAsInt64();
+ });
+}
+
+void EnumDef::RemoveDuplicates() {
+ // This method depends form SymbolTable implementation!
+ // 1) vals.vec - owner (raw pointer)
+ // 2) vals.dict - access map
+ auto first = vals.vec.begin();
+ auto last = vals.vec.end();
+ if (first == last) return;
+ auto result = first;
+ while (++first != last) {
+ if ((*result)->value != (*first)->value) {
+ *(++result) = *first;
+ } else {
+ auto ev = *first;
+ for (auto it = vals.dict.begin(); it != vals.dict.end(); ++it) {
+ if (it->second == ev) it->second = *result; // reassign
+ }
+ delete ev; // delete enum value
+ *first = nullptr;
+ }
+ }
+ vals.vec.erase(++result, last);
+}
+
+template<typename T> void EnumDef::ChangeEnumValue(EnumVal *ev, T new_value) {
+ ev->value = static_cast<int64_t>(new_value);
+}
+
+namespace EnumHelper {
+template<BaseType E> struct EnumValType { typedef int64_t type; };
+template<> struct EnumValType<BASE_TYPE_ULONG> { typedef uint64_t type; };
+} // namespace EnumHelper
+
+struct EnumValBuilder {
+ EnumVal *CreateEnumerator(const std::string &ev_name) {
+ FLATBUFFERS_ASSERT(!temp);
+ auto first = enum_def.vals.vec.empty();
+ user_value = first;
+ temp = new EnumVal(ev_name, first ? 0 : enum_def.vals.vec.back()->value);
+ return temp;
+ }
+
+ EnumVal *CreateEnumerator(const std::string &ev_name, int64_t val) {
+ FLATBUFFERS_ASSERT(!temp);
+ user_value = true;
+ temp = new EnumVal(ev_name, val);
+ return temp;
+ }
+
+ FLATBUFFERS_CHECKED_ERROR AcceptEnumerator(const std::string &name) {
+ FLATBUFFERS_ASSERT(temp);
+ ECHECK(ValidateValue(&temp->value, false == user_value));
+ FLATBUFFERS_ASSERT((temp->union_type.enum_def == nullptr) ||
+ (temp->union_type.enum_def == &enum_def));
+ auto not_unique = enum_def.vals.Add(name, temp);
+ temp = nullptr;
+ if (not_unique) return parser.Error("enum value already exists: " + name);
+ return NoError();
+ }
+
+ FLATBUFFERS_CHECKED_ERROR AcceptEnumerator() {
+ return AcceptEnumerator(temp->name);
+ }
+
+ FLATBUFFERS_CHECKED_ERROR AssignEnumeratorValue(const std::string &value) {
+ user_value = true;
+ auto fit = false;
+ auto ascending = false;
+ if (enum_def.IsUInt64()) {
+ uint64_t u64;
+ fit = StringToNumber(value.c_str(), &u64);
+ ascending = u64 > temp->GetAsUInt64();
+ temp->value = static_cast<int64_t>(u64); // well-defined since C++20.
+ } else {
+ int64_t i64;
+ fit = StringToNumber(value.c_str(), &i64);
+ ascending = i64 > temp->GetAsInt64();
+ temp->value = i64;
+ }
+ if (!fit) return parser.Error("enum value does not fit, \"" + value + "\"");
+ if (!ascending && strict_ascending && !enum_def.vals.vec.empty())
+ return parser.Error("enum values must be specified in ascending order");
+ return NoError();
+ }
+
+ template<BaseType E, typename CTYPE>
+ inline FLATBUFFERS_CHECKED_ERROR ValidateImpl(int64_t *ev, int m) {
+ typedef typename EnumHelper::EnumValType<E>::type T; // int64_t or uint64_t
+ static_assert(sizeof(T) == sizeof(int64_t), "invalid EnumValType");
+ const auto v = static_cast<T>(*ev);
+ auto up = static_cast<T>((flatbuffers::numeric_limits<CTYPE>::max)());
+ auto dn = static_cast<T>((flatbuffers::numeric_limits<CTYPE>::lowest)());
+ if (v < dn || v > (up - m)) {
+ return parser.Error("enum value does not fit, \"" + NumToString(v) +
+ (m ? " + 1\"" : "\"") + " out of " +
+ TypeToIntervalString<CTYPE>());
+ }
+ *ev = static_cast<int64_t>(v + m); // well-defined since C++20.
+ return NoError();
+ }
+
+ FLATBUFFERS_CHECKED_ERROR ValidateValue(int64_t *ev, bool next) {
+ // clang-format off
+ switch (enum_def.underlying_type.base_type) {
+ #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \
+ PTYPE, RTYPE, KTYPE) \
+ case BASE_TYPE_##ENUM: { \
+ if (!IsInteger(BASE_TYPE_##ENUM)) break; \
+ return ValidateImpl<BASE_TYPE_##ENUM, CTYPE>(ev, next ? 1 : 0); \
+ }
+ FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD);
+ #undef FLATBUFFERS_TD
+ default: break;
+ }
+ // clang-format on
+ return parser.Error("fatal: invalid enum underlying type");
+ }
+
+ EnumValBuilder(Parser &_parser, EnumDef &_enum_def, bool strict_order = true)
+ : parser(_parser),
+ enum_def(_enum_def),
+ temp(nullptr),
+ strict_ascending(strict_order),
+ user_value(false) {}
+
+ ~EnumValBuilder() { delete temp; }
+
+ Parser &parser;
+ EnumDef &enum_def;
+ EnumVal *temp;
+ const bool strict_ascending;
+ bool user_value;
+};
+
+CheckedError Parser::ParseEnum(const bool is_union, EnumDef **dest) {
+ std::vector<std::string> enum_comment = doc_comment_;
+ NEXT();
+ std::string enum_name = attribute_;
+ EXPECT(kTokenIdentifier);
+ EnumDef *enum_def;
+ ECHECK(StartEnum(enum_name, is_union, &enum_def));
+ enum_def->doc_comment = enum_comment;
+ if (!is_union && !opts.proto_mode) {
+ // Give specialized error message, since this type spec used to
+ // be optional in the first FlatBuffers release.
+ if (!Is(':')) {
+ return Error(
+ "must specify the underlying integer type for this"
+ " enum (e.g. \': short\', which was the default).");
+ } else {
+ NEXT();
+ }
+ // Specify the integer type underlying this enum.
+ ECHECK(ParseType(enum_def->underlying_type));
+ if (!IsInteger(enum_def->underlying_type.base_type) ||
+ IsBool(enum_def->underlying_type.base_type))
+ return Error("underlying enum type must be integral");
+ // Make this type refer back to the enum it was derived from.
+ enum_def->underlying_type.enum_def = enum_def;
+ }
+ ECHECK(ParseMetaData(&enum_def->attributes));
+ const auto underlying_type = enum_def->underlying_type.base_type;
+ if (enum_def->attributes.Lookup("bit_flags") &&
+ !IsUnsigned(underlying_type)) {
+ // todo: Convert to the Error in the future?
+ Warning("underlying type of bit_flags enum must be unsigned");
+ }
+ // Protobuf allows them to be specified in any order, so sort afterwards.
+ const auto strict_ascending = (false == opts.proto_mode);
+ EnumValBuilder evb(*this, *enum_def, strict_ascending);
+ EXPECT('{');
+ // A lot of code generatos expect that an enum is not-empty.
+ if ((is_union || Is('}')) && !opts.proto_mode) {
+ evb.CreateEnumerator("NONE");
+ ECHECK(evb.AcceptEnumerator());
+ }
+ std::set<std::pair<BaseType, StructDef *>> union_types;
+ while (!Is('}')) {
+ if (opts.proto_mode && attribute_ == "option") {
+ ECHECK(ParseProtoOption());
+ } else {
+ auto &ev = *evb.CreateEnumerator(attribute_);
+ auto full_name = ev.name;
+ ev.doc_comment = doc_comment_;
+ EXPECT(kTokenIdentifier);
+ if (is_union) {
+ ECHECK(ParseNamespacing(&full_name, &ev.name));
+ if (opts.union_value_namespacing) {
+ // Since we can't namespace the actual enum identifiers, turn
+ // namespace parts into part of the identifier.
+ ev.name = full_name;
+ std::replace(ev.name.begin(), ev.name.end(), '.', '_');
+ }
+ if (Is(':')) {
+ NEXT();
+ ECHECK(ParseType(ev.union_type));
+ if (ev.union_type.base_type != BASE_TYPE_STRUCT &&
+ ev.union_type.base_type != BASE_TYPE_STRING)
+ return Error("union value type may only be table/struct/string");
+ } else {
+ ev.union_type = Type(BASE_TYPE_STRUCT, LookupCreateStruct(full_name));
+ }
+ if (!enum_def->uses_multiple_type_instances) {
+ auto ins = union_types.insert(std::make_pair(
+ ev.union_type.base_type, ev.union_type.struct_def));
+ enum_def->uses_multiple_type_instances = (false == ins.second);
+ }
+ }
+
+ if (Is('=')) {
+ NEXT();
+ ECHECK(evb.AssignEnumeratorValue(attribute_));
+ EXPECT(kTokenIntegerConstant);
+ } else if (false == strict_ascending) {
+ // The opts.proto_mode flag is active.
+ return Error("Protobuf mode doesn't allow implicit enum values.");
+ }
+
+ ECHECK(evb.AcceptEnumerator());
+
+ if (opts.proto_mode && Is('[')) {
+ NEXT();
+ // ignore attributes on enums.
+ while (token_ != ']') NEXT();
+ NEXT();
+ }
+ }
+ if (!Is(opts.proto_mode ? ';' : ',')) break;
+ NEXT();
+ }
+ EXPECT('}');
+
+ // At this point, the enum can be empty if input is invalid proto-file.
+ if (!enum_def->size())
+ return Error("incomplete enum declaration, values not found");
+
+ if (enum_def->attributes.Lookup("bit_flags")) {
+ const auto base_width = static_cast<uint64_t>(8 * SizeOf(underlying_type));
+ for (auto it = enum_def->Vals().begin(); it != enum_def->Vals().end();
+ ++it) {
+ auto ev = *it;
+ const auto u = ev->GetAsUInt64();
+ // Stop manipulations with the sign.
+ if (!IsUnsigned(underlying_type) && u == (base_width - 1))
+ return Error("underlying type of bit_flags enum must be unsigned");
+ if (u >= base_width)
+ return Error("bit flag out of range of underlying integral type");
+ enum_def->ChangeEnumValue(ev, 1ULL << u);
+ }
+ }
+
+ if (false == strict_ascending)
+ enum_def->SortByValue(); // Must be sorted to use MinValue/MaxValue.
+
+ if (dest) *dest = enum_def;
+ types_.Add(current_namespace_->GetFullyQualifiedName(enum_def->name),
+ new Type(BASE_TYPE_UNION, nullptr, enum_def));
+ return NoError();
+}
+
+CheckedError Parser::StartStruct(const std::string &name, StructDef **dest) {
+ auto &struct_def = *LookupCreateStruct(name, true, true);
+ if (!struct_def.predecl) return Error("datatype already exists: " + name);
+ struct_def.predecl = false;
+ struct_def.name = name;
+ struct_def.file = file_being_parsed_;
+ // Move this struct to the back of the vector just in case it was predeclared,
+ // to preserve declaration order.
+ *std::remove(structs_.vec.begin(), structs_.vec.end(), &struct_def) =
+ &struct_def;
+ *dest = &struct_def;
+ return NoError();
+}
+
+CheckedError Parser::CheckClash(std::vector<FieldDef *> &fields,
+ StructDef *struct_def, const char *suffix,
+ BaseType basetype) {
+ auto len = strlen(suffix);
+ for (auto it = fields.begin(); it != fields.end(); ++it) {
+ auto &fname = (*it)->name;
+ if (fname.length() > len &&
+ fname.compare(fname.length() - len, len, suffix) == 0 &&
+ (*it)->value.type.base_type != BASE_TYPE_UTYPE) {
+ auto field =
+ struct_def->fields.Lookup(fname.substr(0, fname.length() - len));
+ if (field && field->value.type.base_type == basetype)
+ return Error("Field " + fname +
+ " would clash with generated functions for field " +
+ field->name);
+ }
+ }
+ return NoError();
+}
+
+bool Parser::SupportsAdvancedUnionFeatures() const {
+ return opts.lang_to_generate != 0 &&
+ (opts.lang_to_generate & ~(IDLOptions::kCpp | IDLOptions::kJs |
+ IDLOptions::kTs | IDLOptions::kPhp |
+ IDLOptions::kJava | IDLOptions::kCSharp |
+ IDLOptions::kKotlin |
+ IDLOptions::kBinary)) == 0;
+}
+
+bool Parser::SupportsAdvancedArrayFeatures() const {
+ return (opts.lang_to_generate &
+ ~(IDLOptions::kCpp | IDLOptions::kPython | IDLOptions::kJava |
+ IDLOptions::kCSharp | IDLOptions::kJsonSchema | IDLOptions::kJson |
+ IDLOptions::kBinary)) == 0;
+}
+
+Namespace *Parser::UniqueNamespace(Namespace *ns) {
+ for (auto it = namespaces_.begin(); it != namespaces_.end(); ++it) {
+ if (ns->components == (*it)->components) {
+ delete ns;
+ return *it;
+ }
+ }
+ namespaces_.push_back(ns);
+ return ns;
+}
+
+std::string Parser::UnqualifiedName(const std::string &full_qualified_name) {
+ Namespace *ns = new Namespace();
+
+ std::size_t current, previous = 0;
+ current = full_qualified_name.find('.');
+ while (current != std::string::npos) {
+ ns->components.push_back(
+ full_qualified_name.substr(previous, current - previous));
+ previous = current + 1;
+ current = full_qualified_name.find('.', previous);
+ }
+ current_namespace_ = UniqueNamespace(ns);
+ return full_qualified_name.substr(previous, current - previous);
+}
+
+static bool compareFieldDefs(const FieldDef *a, const FieldDef *b) {
+ auto a_id = atoi(a->attributes.Lookup("id")->constant.c_str());
+ auto b_id = atoi(b->attributes.Lookup("id")->constant.c_str());
+ return a_id < b_id;
+}
+
+CheckedError Parser::ParseDecl() {
+ std::vector<std::string> dc = doc_comment_;
+ bool fixed = IsIdent("struct");
+ if (!fixed && !IsIdent("table")) return Error("declaration expected");
+ NEXT();
+ std::string name = attribute_;
+ EXPECT(kTokenIdentifier);
+ StructDef *struct_def;
+ ECHECK(StartStruct(name, &struct_def));
+ struct_def->doc_comment = dc;
+ struct_def->fixed = fixed;
+ ECHECK(ParseMetaData(&struct_def->attributes));
+ struct_def->sortbysize =
+ struct_def->attributes.Lookup("original_order") == nullptr && !fixed;
+ EXPECT('{');
+ while (token_ != '}') ECHECK(ParseField(*struct_def));
+ auto force_align = struct_def->attributes.Lookup("force_align");
+ if (fixed) {
+ if (force_align) {
+ auto align = static_cast<size_t>(atoi(force_align->constant.c_str()));
+ if (force_align->type.base_type != BASE_TYPE_INT ||
+ align < struct_def->minalign || align > FLATBUFFERS_MAX_ALIGNMENT ||
+ align & (align - 1))
+ return Error(
+ "force_align must be a power of two integer ranging from the"
+ "struct\'s natural alignment to " +
+ NumToString(FLATBUFFERS_MAX_ALIGNMENT));
+ struct_def->minalign = align;
+ }
+ if (!struct_def->bytesize) return Error("size 0 structs not allowed");
+ }
+ struct_def->PadLastField(struct_def->minalign);
+ // Check if this is a table that has manual id assignments
+ auto &fields = struct_def->fields.vec;
+ if (!fixed && fields.size()) {
+ size_t num_id_fields = 0;
+ for (auto it = fields.begin(); it != fields.end(); ++it) {
+ if ((*it)->attributes.Lookup("id")) num_id_fields++;
+ }
+ // If any fields have ids..
+ if (num_id_fields) {
+ // Then all fields must have them.
+ if (num_id_fields != fields.size())
+ return Error(
+ "either all fields or no fields must have an 'id' attribute");
+ // Simply sort by id, then the fields are the same as if no ids had
+ // been specified.
+ std::sort(fields.begin(), fields.end(), compareFieldDefs);
+ // Verify we have a contiguous set, and reassign vtable offsets.
+ for (int i = 0; i < static_cast<int>(fields.size()); i++) {
+ if (i != atoi(fields[i]->attributes.Lookup("id")->constant.c_str()))
+ return Error("field id\'s must be consecutive from 0, id " +
+ NumToString(i) + " missing or set twice");
+ fields[i]->value.offset = FieldIndexToOffset(static_cast<voffset_t>(i));
+ }
+ }
+ }
+
+ ECHECK(
+ CheckClash(fields, struct_def, UnionTypeFieldSuffix(), BASE_TYPE_UNION));
+ ECHECK(CheckClash(fields, struct_def, "Type", BASE_TYPE_UNION));
+ ECHECK(CheckClash(fields, struct_def, "_length", BASE_TYPE_VECTOR));
+ ECHECK(CheckClash(fields, struct_def, "Length", BASE_TYPE_VECTOR));
+ ECHECK(CheckClash(fields, struct_def, "_byte_vector", BASE_TYPE_STRING));
+ ECHECK(CheckClash(fields, struct_def, "ByteVector", BASE_TYPE_STRING));
+ EXPECT('}');
+ types_.Add(current_namespace_->GetFullyQualifiedName(struct_def->name),
+ new Type(BASE_TYPE_STRUCT, struct_def, nullptr));
+ return NoError();
+}
+
+CheckedError Parser::ParseService() {
+ std::vector<std::string> service_comment = doc_comment_;
+ NEXT();
+ auto service_name = attribute_;
+ EXPECT(kTokenIdentifier);
+ auto &service_def = *new ServiceDef();
+ service_def.name = service_name;
+ service_def.file = file_being_parsed_;
+ service_def.doc_comment = service_comment;
+ service_def.defined_namespace = current_namespace_;
+ if (services_.Add(current_namespace_->GetFullyQualifiedName(service_name),
+ &service_def))
+ return Error("service already exists: " + service_name);
+ ECHECK(ParseMetaData(&service_def.attributes));
+ EXPECT('{');
+ do {
+ std::vector<std::string> doc_comment = doc_comment_;
+ auto rpc_name = attribute_;
+ EXPECT(kTokenIdentifier);
+ EXPECT('(');
+ Type reqtype, resptype;
+ ECHECK(ParseTypeIdent(reqtype));
+ EXPECT(')');
+ EXPECT(':');
+ ECHECK(ParseTypeIdent(resptype));
+ if (reqtype.base_type != BASE_TYPE_STRUCT || reqtype.struct_def->fixed ||
+ resptype.base_type != BASE_TYPE_STRUCT || resptype.struct_def->fixed)
+ return Error("rpc request and response types must be tables");
+ auto &rpc = *new RPCCall();
+ rpc.name = rpc_name;
+ rpc.request = reqtype.struct_def;
+ rpc.response = resptype.struct_def;
+ rpc.doc_comment = doc_comment;
+ if (service_def.calls.Add(rpc_name, &rpc))
+ return Error("rpc already exists: " + rpc_name);
+ ECHECK(ParseMetaData(&rpc.attributes));
+ EXPECT(';');
+ } while (token_ != '}');
+ NEXT();
+ return NoError();
+}
+
+bool Parser::SetRootType(const char *name) {
+ root_struct_def_ = LookupStruct(name);
+ if (!root_struct_def_)
+ root_struct_def_ =
+ LookupStruct(current_namespace_->GetFullyQualifiedName(name));
+ return root_struct_def_ != nullptr;
+}
+
+void Parser::MarkGenerated() {
+ // This function marks all existing definitions as having already
+ // been generated, which signals no code for included files should be
+ // generated.
+ for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) {
+ (*it)->generated = true;
+ }
+ for (auto it = structs_.vec.begin(); it != structs_.vec.end(); ++it) {
+ if (!(*it)->predecl) { (*it)->generated = true; }
+ }
+ for (auto it = services_.vec.begin(); it != services_.vec.end(); ++it) {
+ (*it)->generated = true;
+ }
+}
+
+CheckedError Parser::ParseNamespace() {
+ NEXT();
+ auto ns = new Namespace();
+ namespaces_.push_back(ns); // Store it here to not leak upon error.
+ if (token_ != ';') {
+ for (;;) {
+ ns->components.push_back(attribute_);
+ EXPECT(kTokenIdentifier);
+ if (Is('.')) NEXT() else break;
+ }
+ }
+ namespaces_.pop_back();
+ current_namespace_ = UniqueNamespace(ns);
+ EXPECT(';');
+ return NoError();
+}
+
+// Best effort parsing of .proto declarations, with the aim to turn them
+// in the closest corresponding FlatBuffer equivalent.
+// We parse everything as identifiers instead of keywords, since we don't
+// want protobuf keywords to become invalid identifiers in FlatBuffers.
+CheckedError Parser::ParseProtoDecl() {
+ bool isextend = IsIdent("extend");
+ if (IsIdent("package")) {
+ // These are identical in syntax to FlatBuffer's namespace decl.
+ ECHECK(ParseNamespace());
+ } else if (IsIdent("message") || isextend) {
+ std::vector<std::string> struct_comment = doc_comment_;
+ NEXT();
+ StructDef *struct_def = nullptr;
+ Namespace *parent_namespace = nullptr;
+ if (isextend) {
+ if (Is('.')) NEXT(); // qualified names may start with a . ?
+ auto id = attribute_;
+ EXPECT(kTokenIdentifier);
+ ECHECK(ParseNamespacing(&id, nullptr));
+ struct_def = LookupCreateStruct(id, false);
+ if (!struct_def)
+ return Error("cannot extend unknown message type: " + id);
+ } else {
+ std::string name = attribute_;
+ EXPECT(kTokenIdentifier);
+ ECHECK(StartStruct(name, &struct_def));
+ // Since message definitions can be nested, we create a new namespace.
+ auto ns = new Namespace();
+ // Copy of current namespace.
+ *ns = *current_namespace_;
+ // But with current message name.
+ ns->components.push_back(name);
+ ns->from_table++;
+ parent_namespace = current_namespace_;
+ current_namespace_ = UniqueNamespace(ns);
+ }
+ struct_def->doc_comment = struct_comment;
+ ECHECK(ParseProtoFields(struct_def, isextend, false));
+ if (!isextend) { current_namespace_ = parent_namespace; }
+ if (Is(';')) NEXT();
+ } else if (IsIdent("enum")) {
+ // These are almost the same, just with different terminator:
+ EnumDef *enum_def;
+ ECHECK(ParseEnum(false, &enum_def));
+ if (Is(';')) NEXT();
+ // Temp: remove any duplicates, as .fbs files can't handle them.
+ enum_def->RemoveDuplicates();
+ } else if (IsIdent("syntax")) { // Skip these.
+ NEXT();
+ EXPECT('=');
+ EXPECT(kTokenStringConstant);
+ EXPECT(';');
+ } else if (IsIdent("option")) { // Skip these.
+ ECHECK(ParseProtoOption());
+ EXPECT(';');
+ } else if (IsIdent("service")) { // Skip these.
+ NEXT();
+ EXPECT(kTokenIdentifier);
+ ECHECK(ParseProtoCurliesOrIdent());
+ } else {
+ return Error("don\'t know how to parse .proto declaration starting with " +
+ TokenToStringId(token_));
+ }
+ return NoError();
+}
+
+CheckedError Parser::StartEnum(const std::string &enum_name, bool is_union,
+ EnumDef **dest) {
+ auto &enum_def = *new EnumDef();
+ enum_def.name = enum_name;
+ enum_def.file = file_being_parsed_;
+ enum_def.doc_comment = doc_comment_;
+ enum_def.is_union = is_union;
+ enum_def.defined_namespace = current_namespace_;
+ if (enums_.Add(current_namespace_->GetFullyQualifiedName(enum_name),
+ &enum_def))
+ return Error("enum already exists: " + enum_name);
+ enum_def.underlying_type.base_type = is_union ? BASE_TYPE_UTYPE
+ : BASE_TYPE_INT;
+ enum_def.underlying_type.enum_def = &enum_def;
+ if (dest) *dest = &enum_def;
+ return NoError();
+}
+
+CheckedError Parser::ParseProtoFields(StructDef *struct_def, bool isextend,
+ bool inside_oneof) {
+ EXPECT('{');
+ while (token_ != '}') {
+ if (IsIdent("message") || IsIdent("extend") || IsIdent("enum")) {
+ // Nested declarations.
+ ECHECK(ParseProtoDecl());
+ } else if (IsIdent("extensions")) { // Skip these.
+ NEXT();
+ EXPECT(kTokenIntegerConstant);
+ if (Is(kTokenIdentifier)) {
+ NEXT(); // to
+ NEXT(); // num
+ }
+ EXPECT(';');
+ } else if (IsIdent("option")) { // Skip these.
+ ECHECK(ParseProtoOption());
+ EXPECT(';');
+ } else if (IsIdent("reserved")) { // Skip these.
+ NEXT();
+ while (!Is(';')) { NEXT(); } // A variety of formats, just skip.
+ NEXT();
+ } else {
+ std::vector<std::string> field_comment = doc_comment_;
+ // Parse the qualifier.
+ bool required = false;
+ bool repeated = false;
+ bool oneof = false;
+ if (!inside_oneof) {
+ if (IsIdent("optional")) {
+ // This is the default.
+ NEXT();
+ } else if (IsIdent("required")) {
+ required = true;
+ NEXT();
+ } else if (IsIdent("repeated")) {
+ repeated = true;
+ NEXT();
+ } else if (IsIdent("oneof")) {
+ oneof = true;
+ NEXT();
+ } else {
+ // can't error, proto3 allows decls without any of the above.
+ }
+ }
+ StructDef *anonymous_struct = nullptr;
+ EnumDef *oneof_union = nullptr;
+ Type type;
+ if (IsIdent("group") || oneof) {
+ if (!oneof) NEXT();
+ if (oneof && opts.proto_oneof_union) {
+ auto name = MakeCamel(attribute_, true) + "Union";
+ ECHECK(StartEnum(name, true, &oneof_union));
+ type = Type(BASE_TYPE_UNION, nullptr, oneof_union);
+ } else {
+ auto name = "Anonymous" + NumToString(anonymous_counter++);
+ ECHECK(StartStruct(name, &anonymous_struct));
+ type = Type(BASE_TYPE_STRUCT, anonymous_struct);
+ }
+ } else {
+ ECHECK(ParseTypeFromProtoType(&type));
+ }
+ // Repeated elements get mapped to a vector.
+ if (repeated) {
+ type.element = type.base_type;
+ type.base_type = BASE_TYPE_VECTOR;
+ if (type.element == BASE_TYPE_VECTOR) {
+ // We have a vector or vectors, which FlatBuffers doesn't support.
+ // For now make it a vector of string (since the source is likely
+ // "repeated bytes").
+ // TODO(wvo): A better solution would be to wrap this in a table.
+ type.element = BASE_TYPE_STRING;
+ }
+ }
+ std::string name = attribute_;
+ EXPECT(kTokenIdentifier);
+ if (!oneof) {
+ // Parse the field id. Since we're just translating schemas, not
+ // any kind of binary compatibility, we can safely ignore these, and
+ // assign our own.
+ EXPECT('=');
+ EXPECT(kTokenIntegerConstant);
+ }
+ FieldDef *field = nullptr;
+ if (isextend) {
+ // We allow a field to be re-defined when extending.
+ // TODO: are there situations where that is problematic?
+ field = struct_def->fields.Lookup(name);
+ }
+ if (!field) ECHECK(AddField(*struct_def, name, type, &field));
+ field->doc_comment = field_comment;
+ if (!IsScalar(type.base_type)) field->required = required;
+ // See if there's a default specified.
+ if (Is('[')) {
+ NEXT();
+ for (;;) {
+ auto key = attribute_;
+ ECHECK(ParseProtoKey());
+ EXPECT('=');
+ auto val = attribute_;
+ ECHECK(ParseProtoCurliesOrIdent());
+ if (key == "default") {
+ // Temp: skip non-numeric defaults (enums).
+ auto numeric = strpbrk(val.c_str(), "0123456789-+.");
+ if (IsScalar(type.base_type) && numeric == val.c_str())
+ field->value.constant = val;
+ } else if (key == "deprecated") {
+ field->deprecated = val == "true";
+ }
+ if (!Is(',')) break;
+ NEXT();
+ }
+ EXPECT(']');
+ }
+ if (anonymous_struct) {
+ ECHECK(ParseProtoFields(anonymous_struct, false, oneof));
+ if (Is(';')) NEXT();
+ } else if (oneof_union) {
+ // Parse into a temporary StructDef, then transfer fields into an
+ // EnumDef describing the oneof as a union.
+ StructDef oneof_struct;
+ ECHECK(ParseProtoFields(&oneof_struct, false, oneof));
+ if (Is(';')) NEXT();
+ for (auto field_it = oneof_struct.fields.vec.begin();
+ field_it != oneof_struct.fields.vec.end(); ++field_it) {
+ const auto &oneof_field = **field_it;
+ const auto &oneof_type = oneof_field.value.type;
+ if (oneof_type.base_type != BASE_TYPE_STRUCT ||
+ !oneof_type.struct_def || oneof_type.struct_def->fixed)
+ return Error("oneof '" + name +
+ "' cannot be mapped to a union because member '" +
+ oneof_field.name + "' is not a table type.");
+ EnumValBuilder evb(*this, *oneof_union);
+ auto ev = evb.CreateEnumerator(oneof_type.struct_def->name);
+ ev->union_type = oneof_type;
+ ev->doc_comment = oneof_field.doc_comment;
+ ECHECK(evb.AcceptEnumerator(oneof_field.name));
+ }
+ } else {
+ EXPECT(';');
+ }
+ }
+ }
+ NEXT();
+ return NoError();
+}
+
+CheckedError Parser::ParseProtoKey() {
+ if (token_ == '(') {
+ NEXT();
+ // Skip "(a.b)" style custom attributes.
+ while (token_ == '.' || token_ == kTokenIdentifier) NEXT();
+ EXPECT(')');
+ while (Is('.')) {
+ NEXT();
+ EXPECT(kTokenIdentifier);
+ }
+ } else {
+ EXPECT(kTokenIdentifier);
+ }
+ return NoError();
+}
+
+CheckedError Parser::ParseProtoCurliesOrIdent() {
+ if (Is('{')) {
+ NEXT();
+ for (int nesting = 1; nesting;) {
+ if (token_ == '{')
+ nesting++;
+ else if (token_ == '}')
+ nesting--;
+ NEXT();
+ }
+ } else {
+ NEXT(); // Any single token.
+ }
+ return NoError();
+}
+
+CheckedError Parser::ParseProtoOption() {
+ NEXT();
+ ECHECK(ParseProtoKey());
+ EXPECT('=');
+ ECHECK(ParseProtoCurliesOrIdent());
+ return NoError();
+}
+
+// Parse a protobuf type, and map it to the corresponding FlatBuffer one.
+CheckedError Parser::ParseTypeFromProtoType(Type *type) {
+ struct type_lookup {
+ const char *proto_type;
+ BaseType fb_type, element;
+ };
+ static type_lookup lookup[] = {
+ { "float", BASE_TYPE_FLOAT, BASE_TYPE_NONE },
+ { "double", BASE_TYPE_DOUBLE, BASE_TYPE_NONE },
+ { "int32", BASE_TYPE_INT, BASE_TYPE_NONE },
+ { "int64", BASE_TYPE_LONG, BASE_TYPE_NONE },
+ { "uint32", BASE_TYPE_UINT, BASE_TYPE_NONE },
+ { "uint64", BASE_TYPE_ULONG, BASE_TYPE_NONE },
+ { "sint32", BASE_TYPE_INT, BASE_TYPE_NONE },
+ { "sint64", BASE_TYPE_LONG, BASE_TYPE_NONE },
+ { "fixed32", BASE_TYPE_UINT, BASE_TYPE_NONE },
+ { "fixed64", BASE_TYPE_ULONG, BASE_TYPE_NONE },
+ { "sfixed32", BASE_TYPE_INT, BASE_TYPE_NONE },
+ { "sfixed64", BASE_TYPE_LONG, BASE_TYPE_NONE },
+ { "bool", BASE_TYPE_BOOL, BASE_TYPE_NONE },
+ { "string", BASE_TYPE_STRING, BASE_TYPE_NONE },
+ { "bytes", BASE_TYPE_VECTOR, BASE_TYPE_UCHAR },
+ { nullptr, BASE_TYPE_NONE, BASE_TYPE_NONE }
+ };
+ for (auto tl = lookup; tl->proto_type; tl++) {
+ if (attribute_ == tl->proto_type) {
+ type->base_type = tl->fb_type;
+ type->element = tl->element;
+ NEXT();
+ return NoError();
+ }
+ }
+ if (Is('.')) NEXT(); // qualified names may start with a . ?
+ ECHECK(ParseTypeIdent(*type));
+ return NoError();
+}
+
+CheckedError Parser::SkipAnyJsonValue() {
+ switch (token_) {
+ case '{': {
+ size_t fieldn_outer = 0;
+ return ParseTableDelimiters(
+ fieldn_outer, nullptr,
+ [&](const std::string &, size_t &fieldn,
+ const StructDef *) -> CheckedError {
+ ECHECK(Recurse([&]() { return SkipAnyJsonValue(); }));
+ fieldn++;
+ return NoError();
+ });
+ }
+ case '[': {
+ uoffset_t count = 0;
+ return ParseVectorDelimiters(count, [&](uoffset_t &) -> CheckedError {
+ return Recurse([&]() { return SkipAnyJsonValue(); });
+ });
+ }
+ case kTokenStringConstant:
+ case kTokenIntegerConstant:
+ case kTokenFloatConstant: NEXT(); break;
+ default:
+ if (IsIdent("true") || IsIdent("false") || IsIdent("null")) {
+ NEXT();
+ } else
+ return TokenError();
+ }
+ return NoError();
+}
+
+CheckedError Parser::ParseFlexBufferValue(flexbuffers::Builder *builder) {
+ switch (token_) {
+ case '{': {
+ auto start = builder->StartMap();
+ size_t fieldn_outer = 0;
+ auto err =
+ ParseTableDelimiters(fieldn_outer, nullptr,
+ [&](const std::string &name, size_t &fieldn,
+ const StructDef *) -> CheckedError {
+ builder->Key(name);
+ ECHECK(ParseFlexBufferValue(builder));
+ fieldn++;
+ return NoError();
+ });
+ ECHECK(err);
+ builder->EndMap(start);
+ break;
+ }
+ case '[': {
+ auto start = builder->StartVector();
+ uoffset_t count = 0;
+ ECHECK(ParseVectorDelimiters(count, [&](uoffset_t &) -> CheckedError {
+ return ParseFlexBufferValue(builder);
+ }));
+ builder->EndVector(start, false, false);
+ break;
+ }
+ case kTokenStringConstant:
+ builder->String(attribute_);
+ EXPECT(kTokenStringConstant);
+ break;
+ case kTokenIntegerConstant:
+ builder->Int(StringToInt(attribute_.c_str()));
+ EXPECT(kTokenIntegerConstant);
+ break;
+ case kTokenFloatConstant:
+ builder->Double(strtod(attribute_.c_str(), nullptr));
+ EXPECT(kTokenFloatConstant);
+ break;
+ default:
+ if (IsIdent("true")) {
+ builder->Bool(true);
+ NEXT();
+ } else if (IsIdent("false")) {
+ builder->Bool(false);
+ NEXT();
+ } else if (IsIdent("null")) {
+ builder->Null();
+ NEXT();
+ } else
+ return TokenError();
+ }
+ return NoError();
+}
+
+bool Parser::ParseFlexBuffer(const char *source, const char *source_filename,
+ flexbuffers::Builder *builder) {
+ auto ok = !StartParseFile(source, source_filename).Check() &&
+ !ParseFlexBufferValue(builder).Check();
+ if (ok) builder->Finish();
+ return ok;
+}
+
+bool Parser::Parse(const char *source, const char **include_paths,
+ const char *source_filename) {
+ FLATBUFFERS_ASSERT(0 == recurse_protection_counter);
+ auto r = !ParseRoot(source, include_paths, source_filename).Check();
+ FLATBUFFERS_ASSERT(0 == recurse_protection_counter);
+ return r;
+}
+
+CheckedError Parser::StartParseFile(const char *source,
+ const char *source_filename) {
+ file_being_parsed_ = source_filename ? source_filename : "";
+ source_ = source;
+ ResetState(source_);
+ error_.clear();
+ ECHECK(SkipByteOrderMark());
+ NEXT();
+ if (Is(kTokenEof)) return Error("input file is empty");
+ return NoError();
+}
+
+CheckedError Parser::ParseRoot(const char *source, const char **include_paths,
+ const char *source_filename) {
+ ECHECK(DoParse(source, include_paths, source_filename, nullptr));
+
+ // Check that all types were defined.
+ for (auto it = structs_.vec.begin(); it != structs_.vec.end();) {
+ auto &struct_def = **it;
+ if (struct_def.predecl) {
+ if (opts.proto_mode) {
+ // Protos allow enums to be used before declaration, so check if that
+ // is the case here.
+ EnumDef *enum_def = nullptr;
+ for (size_t components =
+ struct_def.defined_namespace->components.size() + 1;
+ components && !enum_def; components--) {
+ auto qualified_name =
+ struct_def.defined_namespace->GetFullyQualifiedName(
+ struct_def.name, components - 1);
+ enum_def = LookupEnum(qualified_name);
+ }
+ if (enum_def) {
+ // This is pretty slow, but a simple solution for now.
+ auto initial_count = struct_def.refcount;
+ for (auto struct_it = structs_.vec.begin();
+ struct_it != structs_.vec.end(); ++struct_it) {
+ auto &sd = **struct_it;
+ for (auto field_it = sd.fields.vec.begin();
+ field_it != sd.fields.vec.end(); ++field_it) {
+ auto &field = **field_it;
+ if (field.value.type.struct_def == &struct_def) {
+ field.value.type.struct_def = nullptr;
+ field.value.type.enum_def = enum_def;
+ auto &bt = field.value.type.base_type == BASE_TYPE_VECTOR
+ ? field.value.type.element
+ : field.value.type.base_type;
+ FLATBUFFERS_ASSERT(bt == BASE_TYPE_STRUCT);
+ bt = enum_def->underlying_type.base_type;
+ struct_def.refcount--;
+ enum_def->refcount++;
+ }
+ }
+ }
+ if (struct_def.refcount)
+ return Error("internal: " + NumToString(struct_def.refcount) + "/" +
+ NumToString(initial_count) +
+ " use(s) of pre-declaration enum not accounted for: " +
+ enum_def->name);
+ structs_.dict.erase(structs_.dict.find(struct_def.name));
+ it = structs_.vec.erase(it);
+ delete &struct_def;
+ continue; // Skip error.
+ }
+ }
+ auto err = "type referenced but not defined (check namespace): " +
+ struct_def.name;
+ if (struct_def.original_location)
+ err += ", originally at: " + *struct_def.original_location;
+ return Error(err);
+ }
+ ++it;
+ }
+
+ // This check has to happen here and not earlier, because only now do we
+ // know for sure what the type of these are.
+ for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) {
+ auto &enum_def = **it;
+ if (enum_def.is_union) {
+ for (auto val_it = enum_def.Vals().begin();
+ val_it != enum_def.Vals().end(); ++val_it) {
+ auto &val = **val_it;
+ if (!SupportsAdvancedUnionFeatures() && val.union_type.struct_def &&
+ val.union_type.struct_def->fixed)
+ return Error(
+ "only tables can be union elements in the generated language: " +
+ val.name);
+ }
+ }
+ }
+ return NoError();
+}
+
+CheckedError Parser::DoParse(const char *source, const char **include_paths,
+ const char *source_filename,
+ const char *include_filename) {
+ if (source_filename) {
+ if (included_files_.find(source_filename) == included_files_.end()) {
+ included_files_[source_filename] =
+ include_filename ? include_filename : "";
+ files_included_per_file_[source_filename] = std::set<std::string>();
+ } else {
+ return NoError();
+ }
+ }
+ if (!include_paths) {
+ static const char *current_directory[] = { "", nullptr };
+ include_paths = current_directory;
+ }
+ field_stack_.clear();
+ builder_.Clear();
+ // Start with a blank namespace just in case this file doesn't have one.
+ current_namespace_ = empty_namespace_;
+
+ ECHECK(StartParseFile(source, source_filename));
+
+ // Includes must come before type declarations:
+ for (;;) {
+ // Parse pre-include proto statements if any:
+ if (opts.proto_mode && (attribute_ == "option" || attribute_ == "syntax" ||
+ attribute_ == "package")) {
+ ECHECK(ParseProtoDecl());
+ } else if (IsIdent("native_include")) {
+ NEXT();
+ vector_emplace_back(&native_included_files_, attribute_);
+ EXPECT(kTokenStringConstant);
+ EXPECT(';');
+ } else if (IsIdent("include") || (opts.proto_mode && IsIdent("import"))) {
+ NEXT();
+ if (opts.proto_mode && attribute_ == "public") NEXT();
+ auto name = flatbuffers::PosixPath(attribute_.c_str());
+ EXPECT(kTokenStringConstant);
+ // Look for the file in include_paths.
+ std::string filepath;
+ for (auto paths = include_paths; paths && *paths; paths++) {
+ filepath = flatbuffers::ConCatPathFileName(*paths, name);
+ if (FileExists(filepath.c_str())) break;
+ }
+ if (filepath.empty())
+ return Error("unable to locate include file: " + name);
+ if (source_filename)
+ files_included_per_file_[source_filename].insert(filepath);
+ if (included_files_.find(filepath) == included_files_.end()) {
+ // We found an include file that we have not parsed yet.
+ // Load it and parse it.
+ std::string contents;
+ if (!LoadFile(filepath.c_str(), true, &contents))
+ return Error("unable to load include file: " + name);
+ ECHECK(DoParse(contents.c_str(), include_paths, filepath.c_str(),
+ name.c_str()));
+ // We generally do not want to output code for any included files:
+ if (!opts.generate_all) MarkGenerated();
+ // Reset these just in case the included file had them, and the
+ // parent doesn't.
+ root_struct_def_ = nullptr;
+ file_identifier_.clear();
+ file_extension_.clear();
+ // This is the easiest way to continue this file after an include:
+ // instead of saving and restoring all the state, we simply start the
+ // file anew. This will cause it to encounter the same include
+ // statement again, but this time it will skip it, because it was
+ // entered into included_files_.
+ // This is recursive, but only go as deep as the number of include
+ // statements.
+ if (source_filename) {
+ included_files_.erase(source_filename);
+ }
+ return DoParse(source, include_paths, source_filename,
+ include_filename);
+ }
+ EXPECT(';');
+ } else {
+ break;
+ }
+ }
+ // Now parse all other kinds of declarations:
+ while (token_ != kTokenEof) {
+ if (opts.proto_mode) {
+ ECHECK(ParseProtoDecl());
+ } else if (IsIdent("namespace")) {
+ ECHECK(ParseNamespace());
+ } else if (token_ == '{') {
+ if (!root_struct_def_)
+ return Error("no root type set to parse json with");
+ if (builder_.GetSize()) {
+ return Error("cannot have more than one json object in a file");
+ }
+ uoffset_t toff;
+ ECHECK(ParseTable(*root_struct_def_, nullptr, &toff));
+ if (opts.size_prefixed) {
+ builder_.FinishSizePrefixed(Offset<Table>(toff), file_identifier_.length()
+ ? file_identifier_.c_str()
+ : nullptr);
+ } else {
+ builder_.Finish(Offset<Table>(toff), file_identifier_.length()
+ ? file_identifier_.c_str()
+ : nullptr);
+ }
+ // Check that JSON file doesn't contain more objects or IDL directives.
+ // Comments after JSON are allowed.
+ EXPECT(kTokenEof);
+ } else if (IsIdent("enum")) {
+ ECHECK(ParseEnum(false, nullptr));
+ } else if (IsIdent("union")) {
+ ECHECK(ParseEnum(true, nullptr));
+ } else if (IsIdent("root_type")) {
+ NEXT();
+ auto root_type = attribute_;
+ EXPECT(kTokenIdentifier);
+ ECHECK(ParseNamespacing(&root_type, nullptr));
+ if (opts.root_type.empty()) {
+ if (!SetRootType(root_type.c_str()))
+ return Error("unknown root type: " + root_type);
+ if (root_struct_def_->fixed)
+ return Error("root type must be a table");
+ }
+ EXPECT(';');
+ } else if (IsIdent("file_identifier")) {
+ NEXT();
+ file_identifier_ = attribute_;
+ EXPECT(kTokenStringConstant);
+ if (file_identifier_.length() != FlatBufferBuilder::kFileIdentifierLength)
+ return Error("file_identifier must be exactly " +
+ NumToString(FlatBufferBuilder::kFileIdentifierLength) +
+ " characters");
+ EXPECT(';');
+ } else if (IsIdent("file_extension")) {
+ NEXT();
+ file_extension_ = attribute_;
+ EXPECT(kTokenStringConstant);
+ EXPECT(';');
+ } else if (IsIdent("include")) {
+ return Error("includes must come before declarations");
+ } else if (IsIdent("attribute")) {
+ NEXT();
+ auto name = attribute_;
+ if (Is(kTokenIdentifier)) {
+ NEXT();
+ } else {
+ EXPECT(kTokenStringConstant);
+ }
+ EXPECT(';');
+ known_attributes_[name] = false;
+ } else if (IsIdent("rpc_service")) {
+ ECHECK(ParseService());
+ } else {
+ ECHECK(ParseDecl());
+ }
+ }
+ return NoError();
+}
+
+std::set<std::string> Parser::GetIncludedFilesRecursive(
+ const std::string &file_name) const {
+ std::set<std::string> included_files;
+ std::list<std::string> to_process;
+
+ if (file_name.empty()) return included_files;
+ to_process.push_back(file_name);
+
+ while (!to_process.empty()) {
+ std::string current = to_process.front();
+ to_process.pop_front();
+ included_files.insert(current);
+
+ // Workaround the lack of const accessor in C++98 maps.
+ auto &new_files =
+ (*const_cast<std::map<std::string, std::set<std::string>> *>(
+ &files_included_per_file_))[current];
+ for (auto it = new_files.begin(); it != new_files.end(); ++it) {
+ if (included_files.find(*it) == included_files.end())
+ to_process.push_back(*it);
+ }
+ }
+
+ return included_files;
+}
+
+// Schema serialization functionality:
+
+template<typename T> bool compareName(const T *a, const T *b) {
+ return a->defined_namespace->GetFullyQualifiedName(a->name) <
+ b->defined_namespace->GetFullyQualifiedName(b->name);
+}
+
+template<typename T> void AssignIndices(const std::vector<T *> &defvec) {
+ // Pre-sort these vectors, such that we can set the correct indices for them.
+ auto vec = defvec;
+ std::sort(vec.begin(), vec.end(), compareName<T>);
+ for (int i = 0; i < static_cast<int>(vec.size()); i++) vec[i]->index = i;
+}
+
+void Parser::Serialize() {
+ builder_.Clear();
+ AssignIndices(structs_.vec);
+ AssignIndices(enums_.vec);
+ std::vector<Offset<reflection::Object>> object_offsets;
+ for (auto it = structs_.vec.begin(); it != structs_.vec.end(); ++it) {
+ auto offset = (*it)->Serialize(&builder_, *this);
+ object_offsets.push_back(offset);
+ (*it)->serialized_location = offset.o;
+ }
+ std::vector<Offset<reflection::Enum>> enum_offsets;
+ for (auto it = enums_.vec.begin(); it != enums_.vec.end(); ++it) {
+ auto offset = (*it)->Serialize(&builder_, *this);
+ enum_offsets.push_back(offset);
+ (*it)->serialized_location = offset.o;
+ }
+ std::vector<Offset<reflection::Service>> service_offsets;
+ for (auto it = services_.vec.begin(); it != services_.vec.end(); ++it) {
+ auto offset = (*it)->Serialize(&builder_, *this);
+ service_offsets.push_back(offset);
+ (*it)->serialized_location = offset.o;
+ }
+ auto objs__ = builder_.CreateVectorOfSortedTables(&object_offsets);
+ auto enum__ = builder_.CreateVectorOfSortedTables(&enum_offsets);
+ auto fiid__ = builder_.CreateString(file_identifier_);
+ auto fext__ = builder_.CreateString(file_extension_);
+ auto serv__ = builder_.CreateVectorOfSortedTables(&service_offsets);
+ auto schema_offset =
+ reflection::CreateSchema(builder_, objs__, enum__, fiid__, fext__,
+ (root_struct_def_ ? root_struct_def_->serialized_location : 0),
+ serv__);
+ if (opts.size_prefixed) {
+ builder_.FinishSizePrefixed(schema_offset, reflection::SchemaIdentifier());
+ } else {
+ builder_.Finish(schema_offset, reflection::SchemaIdentifier());
+ }
+}
+
+static Namespace *GetNamespace(
+ const std::string &qualified_name, std::vector<Namespace *> &namespaces,
+ std::map<std::string, Namespace *> &namespaces_index) {
+ size_t dot = qualified_name.find_last_of('.');
+ std::string namespace_name = (dot != std::string::npos)
+ ? std::string(qualified_name.c_str(), dot)
+ : "";
+ Namespace *&ns = namespaces_index[namespace_name];
+
+ if (!ns) {
+ ns = new Namespace();
+ namespaces.push_back(ns);
+
+ size_t pos = 0;
+
+ for (;;) {
+ dot = qualified_name.find('.', pos);
+ if (dot == std::string::npos) { break; }
+ ns->components.push_back(qualified_name.substr(pos, dot - pos));
+ pos = dot + 1;
+ }
+ }
+
+ return ns;
+}
+
+Offset<reflection::Object> StructDef::Serialize(FlatBufferBuilder *builder,
+ const Parser &parser) const {
+ std::vector<Offset<reflection::Field>> field_offsets;
+ for (auto it = fields.vec.begin(); it != fields.vec.end(); ++it) {
+ field_offsets.push_back((*it)->Serialize(
+ builder, static_cast<uint16_t>(it - fields.vec.begin()), parser));
+ }
+ auto qualified_name = defined_namespace->GetFullyQualifiedName(name);
+ auto name__ = builder->CreateString(qualified_name);
+ auto flds__ = builder->CreateVectorOfSortedTables(&field_offsets);
+ auto attr__ = SerializeAttributes(builder, parser);
+ auto docs__ = parser.opts.binary_schema_comments
+ ? builder->CreateVectorOfStrings(doc_comment)
+ : 0;
+ return reflection::CreateObject(*builder, name__, flds__, fixed,
+ static_cast<int>(minalign),
+ static_cast<int>(bytesize),
+ attr__, docs__);
+}
+
+bool StructDef::Deserialize(Parser &parser, const reflection::Object *object) {
+ if (!DeserializeAttributes(parser, object->attributes()))
+ return false;
+ DeserializeDoc(doc_comment, object->documentation());
+ name = parser.UnqualifiedName(object->name()->str());
+ predecl = false;
+ sortbysize = attributes.Lookup("original_order") == nullptr && !fixed;
+ const auto& of = *(object->fields());
+ auto indexes = std::vector<uoffset_t>(of.size());
+ for (uoffset_t i = 0; i < of.size(); i++) indexes[of.Get(i)->id()] = i;
+ size_t tmp_struct_size = 0;
+ for (size_t i = 0; i < indexes.size(); i++) {
+ auto field = of.Get(indexes[i]);
+ auto field_def = new FieldDef();
+ if (!field_def->Deserialize(parser, field) ||
+ fields.Add(field_def->name, field_def)) {
+ delete field_def;
+ return false;
+ }
+ if (fixed) {
+ // Recompute padding since that's currently not serialized.
+ auto size = InlineSize(field_def->value.type);
+ auto next_field =
+ i + 1 < indexes.size()
+ ? of.Get(indexes[i+1])
+ : nullptr;
+ tmp_struct_size += size;
+ field_def->padding =
+ next_field ? (next_field->offset() - field_def->value.offset) - size
+ : PaddingBytes(tmp_struct_size, minalign);
+ tmp_struct_size += field_def->padding;
+ }
+ }
+ FLATBUFFERS_ASSERT(static_cast<int>(tmp_struct_size) == object->bytesize());
+ return true;
+}
+
+Offset<reflection::Field> FieldDef::Serialize(FlatBufferBuilder *builder,
+ uint16_t id,
+ const Parser &parser) const {
+ auto name__ = builder->CreateString(name);
+ auto type__ = value.type.Serialize(builder);
+ auto attr__ = SerializeAttributes(builder, parser);
+ auto docs__ = parser.opts.binary_schema_comments
+ ? builder->CreateVectorOfStrings(doc_comment)
+ : 0;
+ return reflection::CreateField(*builder, name__, type__, id, value.offset,
+ // Is uint64>max(int64) tested?
+ IsInteger(value.type.base_type) ? StringToInt(value.constant.c_str()) : 0,
+ // result may be platform-dependent if underlying is float (not double)
+ IsFloat(value.type.base_type) ? strtod(value.constant.c_str(), nullptr)
+ : 0.0,
+ deprecated, required, key, attr__, docs__);
+ // TODO: value.constant is almost always "0", we could save quite a bit of
+ // space by sharing it. Same for common values of value.type.
+}
+
+bool FieldDef::Deserialize(Parser &parser, const reflection::Field *field) {
+ name = field->name()->str();
+ defined_namespace = parser.current_namespace_;
+ if (!value.type.Deserialize(parser, field->type()))
+ return false;
+ value.offset = field->offset();
+ if (IsInteger(value.type.base_type)) {
+ value.constant = NumToString(field->default_integer());
+ } else if (IsFloat(value.type.base_type)) {
+ value.constant = FloatToString(field->default_real(), 16);
+ size_t last_zero = value.constant.find_last_not_of('0');
+ if (last_zero != std::string::npos && last_zero != 0) {
+ value.constant.erase(last_zero, std::string::npos);
+ }
+ }
+ deprecated = field->deprecated();
+ required = field->required();
+ key = field->key();
+ if (!DeserializeAttributes(parser, field->attributes()))
+ return false;
+ // TODO: this should probably be handled by a separate attribute
+ if (attributes.Lookup("flexbuffer")) {
+ flexbuffer = true;
+ parser.uses_flexbuffers_ = true;
+ if (value.type.base_type != BASE_TYPE_VECTOR ||
+ value.type.element != BASE_TYPE_UCHAR)
+ return false;
+ }
+ if (auto nested = attributes.Lookup("nested_flatbuffer")) {
+ auto nested_qualified_name =
+ parser.current_namespace_->GetFullyQualifiedName(nested->constant);
+ nested_flatbuffer = parser.LookupStruct(nested_qualified_name);
+ if (!nested_flatbuffer) return false;
+ }
+ DeserializeDoc(doc_comment, field->documentation());
+ return true;
+}
+
+Offset<reflection::RPCCall> RPCCall::Serialize(FlatBufferBuilder *builder,
+ const Parser &parser) const {
+ auto name__ = builder->CreateString(name);
+ auto attr__ = SerializeAttributes(builder, parser);
+ auto docs__ = parser.opts.binary_schema_comments
+ ? builder->CreateVectorOfStrings(doc_comment)
+ : 0;
+ return reflection::CreateRPCCall(*builder, name__,
+ request->serialized_location,
+ response->serialized_location,
+ attr__, docs__);
+}
+
+bool RPCCall::Deserialize(Parser &parser, const reflection::RPCCall *call) {
+ name = call->name()->str();
+ if (!DeserializeAttributes(parser, call->attributes()))
+ return false;
+ DeserializeDoc(doc_comment, call->documentation());
+ request = parser.structs_.Lookup(call->request()->name()->str());
+ response = parser.structs_.Lookup(call->response()->name()->str());
+ if (!request || !response) { return false; }
+ return true;
+}
+
+Offset<reflection::Service> ServiceDef::Serialize(FlatBufferBuilder *builder,
+ const Parser &parser) const {
+ std::vector<Offset<reflection::RPCCall>> servicecall_offsets;
+ for (auto it = calls.vec.begin(); it != calls.vec.end(); ++it) {
+ servicecall_offsets.push_back((*it)->Serialize(builder, parser));
+ }
+ auto qualified_name = defined_namespace->GetFullyQualifiedName(name);
+ auto name__ = builder->CreateString(qualified_name);
+ auto call__ = builder->CreateVector(servicecall_offsets);
+ auto attr__ = SerializeAttributes(builder, parser);
+ auto docs__ = parser.opts.binary_schema_comments
+ ? builder->CreateVectorOfStrings(doc_comment)
+ : 0;
+ return reflection::CreateService(*builder, name__, call__, attr__, docs__);
+}
+
+bool ServiceDef::Deserialize(Parser &parser,
+ const reflection::Service *service) {
+ name = parser.UnqualifiedName(service->name()->str());
+ if (service->calls()) {
+ for (uoffset_t i = 0; i < service->calls()->size(); ++i) {
+ auto call = new RPCCall();
+ if (!call->Deserialize(parser, service->calls()->Get(i)) ||
+ calls.Add(call->name, call)) {
+ delete call;
+ return false;
+ }
+ }
+ }
+ if (!DeserializeAttributes(parser, service->attributes()))
+ return false;
+ DeserializeDoc(doc_comment, service->documentation());
+ return true;
+}
+
+Offset<reflection::Enum> EnumDef::Serialize(FlatBufferBuilder *builder,
+ const Parser &parser) const {
+ std::vector<Offset<reflection::EnumVal>> enumval_offsets;
+ for (auto it = vals.vec.begin(); it != vals.vec.end(); ++it) {
+ enumval_offsets.push_back((*it)->Serialize(builder, parser));
+ }
+ auto qualified_name = defined_namespace->GetFullyQualifiedName(name);
+ auto name__ = builder->CreateString(qualified_name);
+ auto vals__ = builder->CreateVector(enumval_offsets);
+ auto type__ = underlying_type.Serialize(builder);
+ auto attr__ = SerializeAttributes(builder, parser);
+ auto docs__ = parser.opts.binary_schema_comments
+ ? builder->CreateVectorOfStrings(doc_comment)
+ : 0;
+ return reflection::CreateEnum(*builder, name__, vals__, is_union, type__,
+ attr__, docs__);
+}
+
+bool EnumDef::Deserialize(Parser &parser, const reflection::Enum *_enum) {
+ name = parser.UnqualifiedName(_enum->name()->str());
+ for (uoffset_t i = 0; i < _enum->values()->size(); ++i) {
+ auto val = new EnumVal();
+ if (!val->Deserialize(parser, _enum->values()->Get(i)) ||
+ vals.Add(val->name, val)) {
+ delete val;
+ return false;
+ }
+ }
+ is_union = _enum->is_union();
+ if (!underlying_type.Deserialize(parser, _enum->underlying_type())) {
+ return false;
+ }
+ if (!DeserializeAttributes(parser, _enum->attributes()))
+ return false;
+ DeserializeDoc(doc_comment, _enum->documentation());
+ return true;
+}
+
+Offset<reflection::EnumVal> EnumVal::Serialize(FlatBufferBuilder *builder,
+ const Parser &parser) const {
+ auto name__ = builder->CreateString(name);
+ auto type__ = union_type.Serialize(builder);
+ auto docs__ = parser.opts.binary_schema_comments
+ ? builder->CreateVectorOfStrings(doc_comment)
+ : 0;
+ return reflection::CreateEnumVal(*builder, name__, value,
+ union_type.struct_def ? union_type.struct_def->serialized_location : 0,
+ type__, docs__);
+}
+
+bool EnumVal::Deserialize(const Parser &parser,
+ const reflection::EnumVal *val) {
+ name = val->name()->str();
+ value = val->value();
+ if (!union_type.Deserialize(parser, val->union_type()))
+ return false;
+ DeserializeDoc(doc_comment, val->documentation());
+ return true;
+}
+
+Offset<reflection::Type> Type::Serialize(FlatBufferBuilder *builder) const {
+ return reflection::CreateType(
+ *builder, static_cast<reflection::BaseType>(base_type),
+ static_cast<reflection::BaseType>(element),
+ struct_def ? struct_def->index : (enum_def ? enum_def->index : -1),
+ fixed_length);
+}
+
+bool Type::Deserialize(const Parser &parser, const reflection::Type *type) {
+ if (type == nullptr) return true;
+ base_type = static_cast<BaseType>(type->base_type());
+ element = static_cast<BaseType>(type->element());
+ fixed_length = type->fixed_length();
+ if (type->index() >= 0) {
+ bool is_series = type->base_type() == reflection::Vector ||
+ type->base_type() == reflection::Array;
+ if (type->base_type() == reflection::Obj ||
+ (is_series &&
+ type->element() == reflection::Obj)) {
+ if (static_cast<size_t>(type->index()) < parser.structs_.vec.size()) {
+ struct_def = parser.structs_.vec[type->index()];
+ struct_def->refcount++;
+ } else {
+ return false;
+ }
+ } else {
+ if (static_cast<size_t>(type->index()) < parser.enums_.vec.size()) {
+ enum_def = parser.enums_.vec[type->index()];
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+flatbuffers::Offset<
+ flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>>>
+Definition::SerializeAttributes(FlatBufferBuilder *builder,
+ const Parser &parser) const {
+ std::vector<flatbuffers::Offset<reflection::KeyValue>> attrs;
+ for (auto kv = attributes.dict.begin(); kv != attributes.dict.end(); ++kv) {
+ auto it = parser.known_attributes_.find(kv->first);
+ FLATBUFFERS_ASSERT(it != parser.known_attributes_.end());
+ if (parser.opts.binary_schema_builtins || !it->second) {
+ auto key = builder->CreateString(kv->first);
+ auto val = builder->CreateString(kv->second->constant);
+ attrs.push_back(reflection::CreateKeyValue(*builder, key, val));
+ }
+ }
+ if (attrs.size()) {
+ return builder->CreateVectorOfSortedTables(&attrs);
+ } else {
+ return 0;
+ }
+}
+
+bool Definition::DeserializeAttributes(
+ Parser &parser, const Vector<Offset<reflection::KeyValue>> *attrs) {
+ if (attrs == nullptr)
+ return true;
+ for (uoffset_t i = 0; i < attrs->size(); ++i) {
+ auto kv = attrs->Get(i);
+ auto value = new Value();
+ if (kv->value()) { value->constant = kv->value()->str(); }
+ if (attributes.Add(kv->key()->str(), value)) {
+ delete value;
+ return false;
+ }
+ parser.known_attributes_[kv->key()->str()];
+ }
+ return true;
+}
+
+/************************************************************************/
+/* DESERIALIZATION */
+/************************************************************************/
+bool Parser::Deserialize(const uint8_t *buf, const size_t size) {
+ flatbuffers::Verifier verifier(reinterpret_cast<const uint8_t *>(buf), size);
+ bool size_prefixed = false;
+ if(!reflection::SchemaBufferHasIdentifier(buf)) {
+ if (!flatbuffers::BufferHasIdentifier(buf, reflection::SchemaIdentifier(),
+ true))
+ return false;
+ else
+ size_prefixed = true;
+ }
+ auto verify_fn = size_prefixed ? &reflection::VerifySizePrefixedSchemaBuffer
+ : &reflection::VerifySchemaBuffer;
+ if (!verify_fn(verifier)) {
+ return false;
+ }
+ auto schema = size_prefixed ? reflection::GetSizePrefixedSchema(buf)
+ : reflection::GetSchema(buf);
+ return Deserialize(schema);
+}
+
+bool Parser::Deserialize(const reflection::Schema *schema) {
+ file_identifier_ = schema->file_ident() ? schema->file_ident()->str() : "";
+ file_extension_ = schema->file_ext() ? schema->file_ext()->str() : "";
+ std::map<std::string, Namespace *> namespaces_index;
+
+ // Create defs without deserializing so references from fields to structs and
+ // enums can be resolved.
+ for (auto it = schema->objects()->begin(); it != schema->objects()->end();
+ ++it) {
+ auto struct_def = new StructDef();
+ struct_def->bytesize = it->bytesize();
+ struct_def->fixed = it->is_struct();
+ struct_def->minalign = it->minalign();
+ if (structs_.Add(it->name()->str(), struct_def)) {
+ delete struct_def;
+ return false;
+ }
+ auto type = new Type(BASE_TYPE_STRUCT, struct_def, nullptr);
+ if (types_.Add(it->name()->str(), type)) {
+ delete type;
+ return false;
+ }
+ }
+ for (auto it = schema->enums()->begin(); it != schema->enums()->end(); ++it) {
+ auto enum_def = new EnumDef();
+ if (enums_.Add(it->name()->str(), enum_def)) {
+ delete enum_def;
+ return false;
+ }
+ auto type = new Type(BASE_TYPE_UNION, nullptr, enum_def);
+ if (types_.Add(it->name()->str(), type)) {
+ delete type;
+ return false;
+ }
+ }
+
+ // Now fields can refer to structs and enums by index.
+ for (auto it = schema->objects()->begin(); it != schema->objects()->end();
+ ++it) {
+ std::string qualified_name = it->name()->str();
+ auto struct_def = structs_.Lookup(qualified_name);
+ struct_def->defined_namespace =
+ GetNamespace(qualified_name, namespaces_, namespaces_index);
+ if (!struct_def->Deserialize(*this, * it)) { return false; }
+ if (schema->root_table() == *it) { root_struct_def_ = struct_def; }
+ }
+ for (auto it = schema->enums()->begin(); it != schema->enums()->end(); ++it) {
+ std::string qualified_name = it->name()->str();
+ auto enum_def = enums_.Lookup(qualified_name);
+ enum_def->defined_namespace =
+ GetNamespace(qualified_name, namespaces_, namespaces_index);
+ if (!enum_def->Deserialize(*this, *it)) { return false; }
+ }
+
+ if (schema->services()) {
+ for (auto it = schema->services()->begin(); it != schema->services()->end();
+ ++it) {
+ std::string qualified_name = it->name()->str();
+ auto service_def = new ServiceDef();
+ service_def->defined_namespace =
+ GetNamespace(qualified_name, namespaces_, namespaces_index);
+ if (!service_def->Deserialize(*this, *it) ||
+ services_.Add(qualified_name, service_def)) {
+ delete service_def;
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+std::string Parser::ConformTo(const Parser &base) {
+ for (auto sit = structs_.vec.begin(); sit != structs_.vec.end(); ++sit) {
+ auto &struct_def = **sit;
+ auto qualified_name =
+ struct_def.defined_namespace->GetFullyQualifiedName(struct_def.name);
+ auto struct_def_base = base.LookupStruct(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().begin(); evit != enum_def.Vals().end();
+ ++evit) {
+ auto &enum_val = **evit;
+ auto enum_val_base = enum_def_base->Lookup(enum_val.name);
+ if (enum_val_base) {
+ if (enum_val != *enum_val_base)
+ return "values differ for enum: " + enum_val.name;
+ }
+ }
+ }
+ return "";
+}
+
+} // namespace flatbuffers
diff --git a/src/reflection.cpp b/src/reflection.cpp
new file mode 100644
index 0000000..89ce783
--- /dev/null
+++ b/src/reflection.cpp
@@ -0,0 +1,683 @@
+/*
+ * Copyright 2015 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "flatbuffers/reflection.h"
+#include "flatbuffers/util.h"
+
+// Helper functionality for reflection.
+
+namespace flatbuffers {
+
+int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data) {
+ // clang-format off
+ #define FLATBUFFERS_GET(T) static_cast<int64_t>(ReadScalar<T>(data))
+ switch (type) {
+ case reflection::UType:
+ case reflection::Bool:
+ case reflection::UByte: return FLATBUFFERS_GET(uint8_t);
+ case reflection::Byte: return FLATBUFFERS_GET(int8_t);
+ case reflection::Short: return FLATBUFFERS_GET(int16_t);
+ case reflection::UShort: return FLATBUFFERS_GET(uint16_t);
+ case reflection::Int: return FLATBUFFERS_GET(int32_t);
+ case reflection::UInt: return FLATBUFFERS_GET(uint32_t);
+ case reflection::Long: return FLATBUFFERS_GET(int64_t);
+ case reflection::ULong: return FLATBUFFERS_GET(uint64_t);
+ case reflection::Float: return FLATBUFFERS_GET(float);
+ case reflection::Double: return FLATBUFFERS_GET(double);
+ case reflection::String: {
+ auto s = reinterpret_cast<const String *>(ReadScalar<uoffset_t>(data) +
+ data);
+ return s ? StringToInt(s->c_str()) : 0;
+ }
+ default: return 0; // Tables & vectors do not make sense.
+ }
+ #undef FLATBUFFERS_GET
+ // clang-format on
+}
+
+double GetAnyValueF(reflection::BaseType type, const uint8_t *data) {
+ switch (type) {
+ case reflection::Float: return static_cast<double>(ReadScalar<float>(data));
+ case reflection::Double: return ReadScalar<double>(data);
+ case reflection::String: {
+ auto s =
+ reinterpret_cast<const String *>(ReadScalar<uoffset_t>(data) + data);
+ return s ? strtod(s->c_str(), nullptr) : 0.0;
+ }
+ default: return static_cast<double>(GetAnyValueI(type, data));
+ }
+}
+
+std::string GetAnyValueS(reflection::BaseType type, const uint8_t *data,
+ const reflection::Schema *schema, int type_index) {
+ switch (type) {
+ case reflection::Float:
+ case reflection::Double: return NumToString(GetAnyValueF(type, data));
+ case reflection::String: {
+ auto s =
+ reinterpret_cast<const String *>(ReadScalar<uoffset_t>(data) + data);
+ return s ? s->c_str() : "";
+ }
+ case reflection::Obj:
+ if (schema) {
+ // Convert the table to a string. This is mostly for debugging purposes,
+ // and does NOT promise to be JSON compliant.
+ // Also prefixes the type.
+ auto &objectdef = *schema->objects()->Get(type_index);
+ auto s = objectdef.name()->str();
+ if (objectdef.is_struct()) {
+ s += "(struct)"; // TODO: implement this as well.
+ } else {
+ auto table_field = reinterpret_cast<const Table *>(
+ ReadScalar<uoffset_t>(data) + data);
+ s += " { ";
+ auto fielddefs = objectdef.fields();
+ for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
+ auto &fielddef = **it;
+ if (!table_field->CheckField(fielddef.offset())) continue;
+ auto val = GetAnyFieldS(*table_field, fielddef, schema);
+ if (fielddef.type()->base_type() == reflection::String) {
+ std::string esc;
+ flatbuffers::EscapeString(val.c_str(), val.length(), &esc, true,
+ false);
+ val = esc;
+ }
+ s += fielddef.name()->str();
+ s += ": ";
+ s += val;
+ s += ", ";
+ }
+ s += "}";
+ }
+ return s;
+ } else {
+ return "(table)";
+ }
+ case reflection::Vector:
+ return "[(elements)]"; // TODO: implement this as well.
+ case reflection::Union: return "(union)"; // TODO: implement this as well.
+ default: return NumToString(GetAnyValueI(type, data));
+ }
+}
+
+void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val) {
+ // clang-format off
+ #define FLATBUFFERS_SET(T) WriteScalar(data, static_cast<T>(val))
+ switch (type) {
+ case reflection::UType:
+ case reflection::Bool:
+ case reflection::UByte: FLATBUFFERS_SET(uint8_t ); break;
+ case reflection::Byte: FLATBUFFERS_SET(int8_t ); break;
+ case reflection::Short: FLATBUFFERS_SET(int16_t ); break;
+ case reflection::UShort: FLATBUFFERS_SET(uint16_t); break;
+ case reflection::Int: FLATBUFFERS_SET(int32_t ); break;
+ case reflection::UInt: FLATBUFFERS_SET(uint32_t); break;
+ case reflection::Long: FLATBUFFERS_SET(int64_t ); break;
+ case reflection::ULong: FLATBUFFERS_SET(uint64_t); break;
+ case reflection::Float: FLATBUFFERS_SET(float ); break;
+ case reflection::Double: FLATBUFFERS_SET(double ); break;
+ // TODO: support strings
+ default: break;
+ }
+ #undef FLATBUFFERS_SET
+ // clang-format on
+}
+
+void SetAnyValueF(reflection::BaseType type, uint8_t *data, double val) {
+ switch (type) {
+ case reflection::Float: WriteScalar(data, static_cast<float>(val)); break;
+ case reflection::Double: WriteScalar(data, val); break;
+ // TODO: support strings.
+ default: SetAnyValueI(type, data, static_cast<int64_t>(val)); break;
+ }
+}
+
+void SetAnyValueS(reflection::BaseType type, uint8_t *data, const char *val) {
+ switch (type) {
+ case reflection::Float:
+ case reflection::Double:
+ SetAnyValueF(type, data, strtod(val, nullptr));
+ break;
+ // TODO: support strings.
+ default: SetAnyValueI(type, data, StringToInt(val)); break;
+ }
+}
+
+// Resize a FlatBuffer in-place by iterating through all offsets in the buffer
+// and adjusting them by "delta" if they straddle the start offset.
+// Once that is done, bytes can now be inserted/deleted safely.
+// "delta" may be negative (shrinking).
+// Unless "delta" is a multiple of the largest alignment, you'll create a small
+// amount of garbage space in the buffer (usually 0..7 bytes).
+// If your FlatBuffer's root table is not the schema's root table, you should
+// pass in your root_table type as well.
+class ResizeContext {
+ public:
+ ResizeContext(const reflection::Schema &schema, uoffset_t start, int delta,
+ std::vector<uint8_t> *flatbuf,
+ const reflection::Object *root_table = nullptr)
+ : schema_(schema),
+ startptr_(vector_data(*flatbuf) + start),
+ delta_(delta),
+ buf_(*flatbuf),
+ dag_check_(flatbuf->size() / sizeof(uoffset_t), false) {
+ auto mask = static_cast<int>(sizeof(largest_scalar_t) - 1);
+ delta_ = (delta_ + mask) & ~mask;
+ if (!delta_) return; // We can't shrink by less than largest_scalar_t.
+ // Now change all the offsets by delta_.
+ auto root = GetAnyRoot(vector_data(buf_));
+ Straddle<uoffset_t, 1>(vector_data(buf_), root, vector_data(buf_));
+ ResizeTable(root_table ? *root_table : *schema.root_table(), root);
+ // We can now add or remove bytes at start.
+ if (delta_ > 0)
+ buf_.insert(buf_.begin() + start, delta_, 0);
+ else
+ buf_.erase(buf_.begin() + start, buf_.begin() + start - delta_);
+ }
+
+ // Check if the range between first (lower address) and second straddles
+ // the insertion point. If it does, change the offset at offsetloc (of
+ // type T, with direction D).
+ template<typename T, int D>
+ void Straddle(const void *first, const void *second, void *offsetloc) {
+ if (first <= startptr_ && second >= startptr_) {
+ WriteScalar<T>(offsetloc, ReadScalar<T>(offsetloc) + delta_ * D);
+ DagCheck(offsetloc) = true;
+ }
+ }
+
+ // This returns a boolean that records if the corresponding offset location
+ // has been modified already. If so, we can't even read the corresponding
+ // offset, since it is pointing to a location that is illegal until the
+ // resize actually happens.
+ // This must be checked for every offset, since we can't know which offsets
+ // will straddle and which won't.
+ uint8_t &DagCheck(const void *offsetloc) {
+ auto dag_idx = reinterpret_cast<const uoffset_t *>(offsetloc) -
+ reinterpret_cast<const uoffset_t *>(vector_data(buf_));
+ return dag_check_[dag_idx];
+ }
+
+ void ResizeTable(const reflection::Object &objectdef, Table *table) {
+ if (DagCheck(table)) return; // Table already visited.
+ auto vtable = table->GetVTable();
+ // Early out: since all fields inside the table must point forwards in
+ // memory, if the insertion point is before the table we can stop here.
+ auto tableloc = reinterpret_cast<uint8_t *>(table);
+ if (startptr_ <= tableloc) {
+ // Check if insertion point is between the table and a vtable that
+ // precedes it. This can't happen in current construction code, but check
+ // just in case we ever change the way flatbuffers are built.
+ Straddle<soffset_t, -1>(vtable, table, table);
+ } else {
+ // Check each field.
+ auto fielddefs = objectdef.fields();
+ for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
+ auto &fielddef = **it;
+ auto base_type = fielddef.type()->base_type();
+ // Ignore scalars.
+ if (base_type <= reflection::Double) continue;
+ // Ignore fields that are not stored.
+ auto offset = table->GetOptionalFieldOffset(fielddef.offset());
+ if (!offset) continue;
+ // Ignore structs.
+ auto subobjectdef =
+ base_type == reflection::Obj
+ ? schema_.objects()->Get(fielddef.type()->index())
+ : nullptr;
+ if (subobjectdef && subobjectdef->is_struct()) continue;
+ // Get this fields' offset, and read it if safe.
+ auto offsetloc = tableloc + offset;
+ if (DagCheck(offsetloc)) continue; // This offset already visited.
+ auto ref = offsetloc + ReadScalar<uoffset_t>(offsetloc);
+ Straddle<uoffset_t, 1>(offsetloc, ref, offsetloc);
+ // Recurse.
+ switch (base_type) {
+ case reflection::Obj: {
+ ResizeTable(*subobjectdef, reinterpret_cast<Table *>(ref));
+ break;
+ }
+ case reflection::Vector: {
+ auto elem_type = fielddef.type()->element();
+ if (elem_type != reflection::Obj && elem_type != reflection::String)
+ break;
+ auto vec = reinterpret_cast<Vector<uoffset_t> *>(ref);
+ auto elemobjectdef =
+ elem_type == reflection::Obj
+ ? schema_.objects()->Get(fielddef.type()->index())
+ : nullptr;
+ if (elemobjectdef && elemobjectdef->is_struct()) break;
+ for (uoffset_t i = 0; i < vec->size(); i++) {
+ auto loc = vec->Data() + i * sizeof(uoffset_t);
+ if (DagCheck(loc)) continue; // This offset already visited.
+ auto dest = loc + vec->Get(i);
+ Straddle<uoffset_t, 1>(loc, dest, loc);
+ if (elemobjectdef)
+ ResizeTable(*elemobjectdef, reinterpret_cast<Table *>(dest));
+ }
+ break;
+ }
+ case reflection::Union: {
+ ResizeTable(GetUnionType(schema_, objectdef, fielddef, *table),
+ reinterpret_cast<Table *>(ref));
+ break;
+ }
+ case reflection::String: break;
+ default: FLATBUFFERS_ASSERT(false);
+ }
+ }
+ // Check if the vtable offset points beyond the insertion point.
+ // Must do this last, since GetOptionalFieldOffset above still reads
+ // this value.
+ Straddle<soffset_t, -1>(table, vtable, table);
+ }
+ }
+
+ void operator=(const ResizeContext &rc);
+
+ private:
+ const reflection::Schema &schema_;
+ uint8_t *startptr_;
+ int delta_;
+ std::vector<uint8_t> &buf_;
+ std::vector<uint8_t> dag_check_;
+};
+
+void SetString(const reflection::Schema &schema, const std::string &val,
+ const String *str, std::vector<uint8_t> *flatbuf,
+ const reflection::Object *root_table) {
+ auto delta = static_cast<int>(val.size()) - static_cast<int>(str->size());
+ auto str_start = static_cast<uoffset_t>(
+ reinterpret_cast<const uint8_t *>(str) - vector_data(*flatbuf));
+ auto start = str_start + static_cast<uoffset_t>(sizeof(uoffset_t));
+ if (delta) {
+ // Clear the old string, since we don't want parts of it remaining.
+ memset(vector_data(*flatbuf) + start, 0, str->size());
+ // Different size, we must expand (or contract).
+ ResizeContext(schema, start, delta, flatbuf, root_table);
+ // Set the new length.
+ WriteScalar(vector_data(*flatbuf) + str_start,
+ static_cast<uoffset_t>(val.size()));
+ }
+ // Copy new data. Safe because we created the right amount of space.
+ memcpy(vector_data(*flatbuf) + start, val.c_str(), val.size() + 1);
+}
+
+uint8_t *ResizeAnyVector(const reflection::Schema &schema, uoffset_t newsize,
+ const VectorOfAny *vec, uoffset_t num_elems,
+ uoffset_t elem_size, std::vector<uint8_t> *flatbuf,
+ const reflection::Object *root_table) {
+ auto delta_elem = static_cast<int>(newsize) - static_cast<int>(num_elems);
+ auto delta_bytes = delta_elem * static_cast<int>(elem_size);
+ auto vec_start =
+ reinterpret_cast<const uint8_t *>(vec) - vector_data(*flatbuf);
+ auto start = static_cast<uoffset_t>(vec_start + sizeof(uoffset_t) +
+ elem_size * num_elems);
+ if (delta_bytes) {
+ if (delta_elem < 0) {
+ // Clear elements we're throwing away, since some might remain in the
+ // buffer.
+ auto size_clear = -delta_elem * elem_size;
+ memset(vector_data(*flatbuf) + start - size_clear, 0, size_clear);
+ }
+ ResizeContext(schema, start, delta_bytes, flatbuf, root_table);
+ WriteScalar(vector_data(*flatbuf) + vec_start, newsize); // Length field.
+ // Set new elements to 0.. this can be overwritten by the caller.
+ if (delta_elem > 0) {
+ memset(vector_data(*flatbuf) + start, 0, delta_elem * elem_size);
+ }
+ }
+ return vector_data(*flatbuf) + start;
+}
+
+const uint8_t *AddFlatBuffer(std::vector<uint8_t> &flatbuf,
+ const uint8_t *newbuf, size_t newlen) {
+ // Align to sizeof(uoffset_t) past sizeof(largest_scalar_t) since we're
+ // going to chop off the root offset.
+ while ((flatbuf.size() & (sizeof(uoffset_t) - 1)) ||
+ !(flatbuf.size() & (sizeof(largest_scalar_t) - 1))) {
+ flatbuf.push_back(0);
+ }
+ auto insertion_point = static_cast<uoffset_t>(flatbuf.size());
+ // Insert the entire FlatBuffer minus the root pointer.
+ flatbuf.insert(flatbuf.end(), newbuf + sizeof(uoffset_t), newbuf + newlen);
+ auto root_offset = ReadScalar<uoffset_t>(newbuf) - sizeof(uoffset_t);
+ return vector_data(flatbuf) + insertion_point + root_offset;
+}
+
+void CopyInline(FlatBufferBuilder &fbb, const reflection::Field &fielddef,
+ const Table &table, size_t align, size_t size) {
+ fbb.Align(align);
+ fbb.PushBytes(table.GetStruct<const uint8_t *>(fielddef.offset()), size);
+ fbb.TrackField(fielddef.offset(), fbb.GetSize());
+}
+
+Offset<const Table *> CopyTable(FlatBufferBuilder &fbb,
+ const reflection::Schema &schema,
+ const reflection::Object &objectdef,
+ const Table &table, bool use_string_pooling) {
+ // Before we can construct the table, we have to first generate any
+ // subobjects, and collect their offsets.
+ std::vector<uoffset_t> offsets;
+ auto fielddefs = objectdef.fields();
+ for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
+ auto &fielddef = **it;
+ // Skip if field is not present in the source.
+ if (!table.CheckField(fielddef.offset())) continue;
+ uoffset_t offset = 0;
+ switch (fielddef.type()->base_type()) {
+ case reflection::String: {
+ offset = use_string_pooling
+ ? fbb.CreateSharedString(GetFieldS(table, fielddef)).o
+ : fbb.CreateString(GetFieldS(table, fielddef)).o;
+ break;
+ }
+ case reflection::Obj: {
+ auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index());
+ if (!subobjectdef.is_struct()) {
+ offset =
+ CopyTable(fbb, schema, subobjectdef, *GetFieldT(table, fielddef))
+ .o;
+ }
+ break;
+ }
+ case reflection::Union: {
+ auto &subobjectdef = GetUnionType(schema, objectdef, fielddef, table);
+ offset =
+ CopyTable(fbb, schema, subobjectdef, *GetFieldT(table, fielddef)).o;
+ break;
+ }
+ case reflection::Vector: {
+ auto vec =
+ table.GetPointer<const Vector<Offset<Table>> *>(fielddef.offset());
+ auto element_base_type = fielddef.type()->element();
+ auto elemobjectdef =
+ element_base_type == reflection::Obj
+ ? schema.objects()->Get(fielddef.type()->index())
+ : nullptr;
+ switch (element_base_type) {
+ case reflection::String: {
+ std::vector<Offset<const String *>> elements(vec->size());
+ auto vec_s = reinterpret_cast<const Vector<Offset<String>> *>(vec);
+ for (uoffset_t i = 0; i < vec_s->size(); i++) {
+ elements[i] = use_string_pooling
+ ? fbb.CreateSharedString(vec_s->Get(i)).o
+ : fbb.CreateString(vec_s->Get(i)).o;
+ }
+ offset = fbb.CreateVector(elements).o;
+ break;
+ }
+ case reflection::Obj: {
+ if (!elemobjectdef->is_struct()) {
+ std::vector<Offset<const Table *>> elements(vec->size());
+ for (uoffset_t i = 0; i < vec->size(); i++) {
+ elements[i] =
+ CopyTable(fbb, schema, *elemobjectdef, *vec->Get(i));
+ }
+ offset = fbb.CreateVector(elements).o;
+ break;
+ }
+ }
+ FLATBUFFERS_FALLTHROUGH(); // fall thru
+ default: { // Scalars and structs.
+ auto element_size = GetTypeSize(element_base_type);
+ if (elemobjectdef && elemobjectdef->is_struct())
+ element_size = elemobjectdef->bytesize();
+ fbb.StartVector(vec->size(), element_size);
+ fbb.PushBytes(vec->Data(), element_size * vec->size());
+ offset = fbb.EndVector(vec->size());
+ break;
+ }
+ }
+ break;
+ }
+ default: // Scalars.
+ break;
+ }
+ if (offset) { offsets.push_back(offset); }
+ }
+ // Now we can build the actual table from either offsets or scalar data.
+ auto start = objectdef.is_struct() ? fbb.StartStruct(objectdef.minalign())
+ : fbb.StartTable();
+ size_t offset_idx = 0;
+ for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) {
+ auto &fielddef = **it;
+ if (!table.CheckField(fielddef.offset())) continue;
+ auto base_type = fielddef.type()->base_type();
+ switch (base_type) {
+ case reflection::Obj: {
+ auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index());
+ if (subobjectdef.is_struct()) {
+ CopyInline(fbb, fielddef, table, subobjectdef.minalign(),
+ subobjectdef.bytesize());
+ break;
+ }
+ }
+ FLATBUFFERS_FALLTHROUGH(); // fall thru
+ case reflection::Union:
+ case reflection::String:
+ case reflection::Vector:
+ fbb.AddOffset(fielddef.offset(), Offset<void>(offsets[offset_idx++]));
+ break;
+ default: { // Scalars.
+ auto size = GetTypeSize(base_type);
+ CopyInline(fbb, fielddef, table, size, size);
+ break;
+ }
+ }
+ }
+ FLATBUFFERS_ASSERT(offset_idx == offsets.size());
+ if (objectdef.is_struct()) {
+ fbb.ClearOffsets();
+ return fbb.EndStruct();
+ } else {
+ return fbb.EndTable(start);
+ }
+}
+
+bool VerifyStruct(flatbuffers::Verifier &v,
+ const flatbuffers::Table &parent_table,
+ voffset_t field_offset, const reflection::Object &obj,
+ bool required) {
+ auto offset = parent_table.GetOptionalFieldOffset(field_offset);
+ if (required && !offset) { return false; }
+
+ return !offset ||
+ v.Verify(reinterpret_cast<const uint8_t *>(&parent_table), offset,
+ obj.bytesize());
+}
+
+bool VerifyVectorOfStructs(flatbuffers::Verifier &v,
+ const flatbuffers::Table &parent_table,
+ voffset_t field_offset,
+ const reflection::Object &obj, bool required) {
+ auto p = parent_table.GetPointer<const uint8_t *>(field_offset);
+ if (required && !p) { return false; }
+
+ return !p || v.VerifyVectorOrString(p, obj.bytesize());
+}
+
+// forward declare to resolve cyclic deps between VerifyObject and VerifyVector
+bool VerifyObject(flatbuffers::Verifier &v, const reflection::Schema &schema,
+ const reflection::Object &obj,
+ const flatbuffers::Table *table, bool required);
+
+bool VerifyVector(flatbuffers::Verifier &v, const reflection::Schema &schema,
+ const flatbuffers::Table &table,
+ const reflection::Field &vec_field) {
+ FLATBUFFERS_ASSERT(vec_field.type()->base_type() == reflection::Vector);
+ if (!table.VerifyField<uoffset_t>(v, vec_field.offset())) return false;
+
+ switch (vec_field.type()->element()) {
+ case reflection::None: FLATBUFFERS_ASSERT(false); break;
+ case reflection::UType:
+ return v.VerifyVector(flatbuffers::GetFieldV<uint8_t>(table, vec_field));
+ case reflection::Bool:
+ case reflection::Byte:
+ case reflection::UByte:
+ return v.VerifyVector(flatbuffers::GetFieldV<int8_t>(table, vec_field));
+ case reflection::Short:
+ case reflection::UShort:
+ return v.VerifyVector(flatbuffers::GetFieldV<int16_t>(table, vec_field));
+ case reflection::Int:
+ case reflection::UInt:
+ return v.VerifyVector(flatbuffers::GetFieldV<int32_t>(table, vec_field));
+ case reflection::Long:
+ case reflection::ULong:
+ return v.VerifyVector(flatbuffers::GetFieldV<int64_t>(table, vec_field));
+ case reflection::Float:
+ return v.VerifyVector(flatbuffers::GetFieldV<float>(table, vec_field));
+ case reflection::Double:
+ return v.VerifyVector(flatbuffers::GetFieldV<double>(table, vec_field));
+ case reflection::String: {
+ auto vec_string =
+ flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::String>>(
+ table, vec_field);
+ if (v.VerifyVector(vec_string) && v.VerifyVectorOfStrings(vec_string)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ case reflection::Vector: FLATBUFFERS_ASSERT(false); break;
+ case reflection::Obj: {
+ auto obj = schema.objects()->Get(vec_field.type()->index());
+ if (obj->is_struct()) {
+ if (!VerifyVectorOfStructs(v, table, vec_field.offset(), *obj,
+ vec_field.required())) {
+ return false;
+ }
+ } else {
+ auto vec =
+ flatbuffers::GetFieldV<flatbuffers::Offset<flatbuffers::Table>>(
+ table, vec_field);
+ if (!v.VerifyVector(vec)) return false;
+ if (vec) {
+ for (uoffset_t j = 0; j < vec->size(); j++) {
+ if (!VerifyObject(v, schema, *obj, vec->Get(j), true)) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+ case reflection::Union: FLATBUFFERS_ASSERT(false); break;
+ default: FLATBUFFERS_ASSERT(false); break;
+ }
+
+ return false;
+}
+
+bool VerifyObject(flatbuffers::Verifier &v, const reflection::Schema &schema,
+ const reflection::Object &obj,
+ const flatbuffers::Table *table, bool required) {
+ if (!table) {
+ if (!required)
+ return true;
+ else
+ return false;
+ }
+
+ if (!table->VerifyTableStart(v)) return false;
+
+ for (uoffset_t i = 0; i < obj.fields()->size(); i++) {
+ auto field_def = obj.fields()->Get(i);
+ switch (field_def->type()->base_type()) {
+ case reflection::None: FLATBUFFERS_ASSERT(false); break;
+ case reflection::UType:
+ if (!table->VerifyField<uint8_t>(v, field_def->offset())) return false;
+ break;
+ case reflection::Bool:
+ case reflection::Byte:
+ case reflection::UByte:
+ if (!table->VerifyField<int8_t>(v, field_def->offset())) return false;
+ break;
+ case reflection::Short:
+ case reflection::UShort:
+ if (!table->VerifyField<int16_t>(v, field_def->offset())) return false;
+ break;
+ case reflection::Int:
+ case reflection::UInt:
+ if (!table->VerifyField<int32_t>(v, field_def->offset())) return false;
+ break;
+ case reflection::Long:
+ case reflection::ULong:
+ if (!table->VerifyField<int64_t>(v, field_def->offset())) return false;
+ break;
+ case reflection::Float:
+ if (!table->VerifyField<float>(v, field_def->offset())) return false;
+ break;
+ case reflection::Double:
+ if (!table->VerifyField<double>(v, field_def->offset())) return false;
+ break;
+ case reflection::String:
+ if (!table->VerifyField<uoffset_t>(v, field_def->offset()) ||
+ !v.VerifyString(flatbuffers::GetFieldS(*table, *field_def))) {
+ return false;
+ }
+ break;
+ case reflection::Vector:
+ if (!VerifyVector(v, schema, *table, *field_def)) return false;
+ break;
+ case reflection::Obj: {
+ auto child_obj = schema.objects()->Get(field_def->type()->index());
+ if (child_obj->is_struct()) {
+ if (!VerifyStruct(v, *table, field_def->offset(), *child_obj,
+ field_def->required())) {
+ return false;
+ }
+ } else {
+ if (!VerifyObject(v, schema, *child_obj,
+ flatbuffers::GetFieldT(*table, *field_def),
+ field_def->required())) {
+ return false;
+ }
+ }
+ break;
+ }
+ case reflection::Union: {
+ // get union type from the prev field
+ voffset_t utype_offset = field_def->offset() - sizeof(voffset_t);
+ auto utype = table->GetField<uint8_t>(utype_offset, 0);
+ if (utype != 0) {
+ // Means we have this union field present
+ auto fb_enum = schema.enums()->Get(field_def->type()->index());
+ auto child_obj = fb_enum->values()->Get(utype)->object();
+ if (!VerifyObject(v, schema, *child_obj,
+ flatbuffers::GetFieldT(*table, *field_def),
+ field_def->required())) {
+ return false;
+ }
+ }
+ break;
+ }
+ default: FLATBUFFERS_ASSERT(false); break;
+ }
+ }
+
+ if (!v.EndTable()) return false;
+
+ return true;
+}
+
+bool Verify(const reflection::Schema &schema, const reflection::Object &root,
+ const uint8_t *buf, size_t length) {
+ Verifier v(buf, length);
+ return VerifyObject(v, schema, root, flatbuffers::GetAnyRoot(buf), true);
+}
+
+} // namespace flatbuffers
diff --git a/src/util.cpp b/src/util.cpp
new file mode 100644
index 0000000..5483cee
--- /dev/null
+++ b/src/util.cpp
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2016 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.
+ */
+
+// clang-format off
+// Dont't remove `format off`, it prevent reordering of win-includes.
+#ifdef _WIN32
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# ifndef NOMINMAX
+# define NOMINMAX
+# endif
+# ifdef _MSC_VER
+# include <crtdbg.h>
+# endif
+# include <windows.h> // Must be included before <direct.h>
+# include <direct.h>
+# include <winbase.h>
+# undef interface // This is also important because of reasons
+#else
+# include <limits.h>
+#endif
+// clang-format on
+
+#include "flatbuffers/base.h"
+#include "flatbuffers/util.h"
+
+#include <sys/stat.h>
+#include <clocale>
+#include <fstream>
+
+namespace flatbuffers {
+
+bool FileExistsRaw(const char *name) {
+ std::ifstream ifs(name);
+ return ifs.good();
+}
+
+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);
+ auto size = ifs.tellg();
+ (*buf).resize(static_cast<size_t>(size));
+ ifs.seekg(0, std::ios::beg);
+ ifs.read(&(*buf)[0], (*buf).size());
+ } else {
+ // This is slower, but works correctly on all platforms for text files.
+ std::ostringstream oss;
+ oss << ifs.rdbuf();
+ *buf = oss.str();
+ }
+ return !ifs.bad();
+}
+
+static LoadFileFunction g_load_file_function = LoadFileRaw;
+static FileExistsFunction g_file_exists_function = FileExistsRaw;
+
+bool LoadFile(const char *name, bool binary, std::string *buf) {
+ FLATBUFFERS_ASSERT(g_load_file_function);
+ return g_load_file_function(name, binary, buf);
+}
+
+bool FileExists(const char *name) {
+ FLATBUFFERS_ASSERT(g_file_exists_function);
+ return g_file_exists_function(name);
+}
+
+bool DirExists(const char *name) {
+ // clang-format off
+
+ #ifdef _WIN32
+ #define flatbuffers_stat _stat
+ #define FLATBUFFERS_S_IFDIR _S_IFDIR
+ #else
+ #define flatbuffers_stat stat
+ #define FLATBUFFERS_S_IFDIR S_IFDIR
+ #endif
+ // clang-format on
+ 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;
+ return previous_function;
+}
+
+FileExistsFunction SetFileExistsFunction(
+ FileExistsFunction file_exists_function) {
+ FileExistsFunction previous_function = g_file_exists_function;
+ g_file_exists_function =
+ file_exists_function ? file_exists_function : FileExistsRaw;
+ return previous_function;
+}
+
+bool SaveFile(const char *name, const char *buf, size_t len, bool binary) {
+ std::ofstream ofs(name, binary ? std::ofstream::binary : std::ofstream::out);
+ if (!ofs.is_open()) return false;
+ ofs.write(buf, len);
+ return !ofs.bad();
+}
+
+// We internally store paths in posix format ('/'). Paths supplied
+// by the user should go through PosixPath to ensure correct behavior
+// on Windows when paths are string-compared.
+
+static const char kPathSeparatorWindows = '\\';
+static const char *PathSeparatorSet = "\\/"; // Intentionally no ':'
+
+std::string StripExtension(const std::string &filepath) {
+ size_t i = filepath.find_last_of('.');
+ return i != std::string::npos ? filepath.substr(0, i) : filepath;
+}
+
+std::string GetExtension(const std::string &filepath) {
+ size_t i = filepath.find_last_of('.');
+ return i != std::string::npos ? filepath.substr(i + 1) : "";
+}
+
+std::string StripPath(const std::string &filepath) {
+ size_t i = filepath.find_last_of(PathSeparatorSet);
+ return i != std::string::npos ? filepath.substr(i + 1) : filepath;
+}
+
+std::string StripFileName(const std::string &filepath) {
+ size_t i = filepath.find_last_of(PathSeparatorSet);
+ return i != std::string::npos ? filepath.substr(0, i) : "";
+}
+
+std::string ConCatPathFileName(const std::string &path,
+ const std::string &filename) {
+ std::string filepath = path;
+ if (filepath.length()) {
+ char &filepath_last_character = string_back(filepath);
+ if (filepath_last_character == kPathSeparatorWindows) {
+ filepath_last_character = kPathSeparator;
+ } else if (filepath_last_character != kPathSeparator) {
+ filepath += kPathSeparator;
+ }
+ }
+ filepath += filename;
+ // Ignore './' at the start of filepath.
+ if (filepath[0] == '.' && filepath[1] == kPathSeparator) {
+ filepath.erase(0, 2);
+ }
+ return filepath;
+}
+
+std::string PosixPath(const char *path) {
+ std::string p = path;
+ std::replace(p.begin(), p.end(), '\\', '/');
+ return p;
+}
+
+void EnsureDirExists(const std::string &filepath) {
+ auto parent = StripFileName(filepath);
+ if (parent.length()) EnsureDirExists(parent);
+ // clang-format off
+
+ #ifdef _WIN32
+ (void)_mkdir(filepath.c_str());
+ #else
+ mkdir(filepath.c_str(), S_IRWXU|S_IRGRP|S_IXGRP);
+ #endif
+ // clang-format on
+}
+
+std::string AbsolutePath(const std::string &filepath) {
+ // clang-format off
+
+ #ifdef FLATBUFFERS_NO_ABSOLUTE_PATH_RESOLUTION
+ return filepath;
+ #else
+ #ifdef _WIN32
+ char abs_path[MAX_PATH];
+ return GetFullPathNameA(filepath.c_str(), MAX_PATH, abs_path, nullptr)
+ #else
+ char abs_path[PATH_MAX];
+ return realpath(filepath.c_str(), abs_path)
+ #endif
+ ? abs_path
+ : filepath;
+ #endif // FLATBUFFERS_NO_ABSOLUTE_PATH_RESOLUTION
+ // clang-format on
+}
+
+// Locale-independent code.
+#if defined(FLATBUFFERS_LOCALE_INDEPENDENT) && \
+ (FLATBUFFERS_LOCALE_INDEPENDENT > 0)
+
+// clang-format off
+// Allocate locale instance at startup of application.
+ClassicLocale ClassicLocale::instance_;
+
+#ifdef _MSC_VER
+ ClassicLocale::ClassicLocale()
+ : locale_(_create_locale(LC_ALL, "C")) {}
+ ClassicLocale::~ClassicLocale() { _free_locale(locale_); }
+#else
+ ClassicLocale::ClassicLocale()
+ : locale_(newlocale(LC_ALL, "C", nullptr)) {}
+ ClassicLocale::~ClassicLocale() { freelocale(locale_); }
+#endif
+// clang-format on
+
+#endif // !FLATBUFFERS_LOCALE_INDEPENDENT
+
+std::string RemoveStringQuotes(const std::string &s) {
+ auto ch = *s.c_str();
+ return ((s.size() >= 2) && (ch == '\"' || ch == '\'') &&
+ (ch == string_back(s)))
+ ? s.substr(1, s.length() - 2)
+ : s;
+}
+
+bool SetGlobalTestLocale(const char *locale_name, std::string *_value) {
+ const auto the_locale = setlocale(LC_ALL, locale_name);
+ if (!the_locale) return false;
+ if (_value) *_value = std::string(the_locale);
+ return true;
+}
+
+bool ReadEnvironmentVariable(const char *var_name, std::string *_value) {
+ #ifdef _MSC_VER
+ __pragma(warning(disable : 4996)); // _CRT_SECURE_NO_WARNINGS
+ #endif
+ auto env_str = std::getenv(var_name);
+ if (!env_str) return false;
+ if (_value) *_value = std::string(env_str);
+ return true;
+}
+
+void SetupDefaultCRTReportMode() {
+ // clang-format off
+
+ #ifdef _MSC_VER
+ // By default, send all reports to STDOUT to prevent CI hangs.
+ // Enable assert report box [Abort|Retry|Ignore] if a debugger is present.
+ const int dbg_mode = (_CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG) |
+ (IsDebuggerPresent() ? _CRTDBG_MODE_WNDW : 0);
+ (void)dbg_mode; // release mode fix
+ // CrtDebug reports to _CRT_WARN channel.
+ _CrtSetReportMode(_CRT_WARN, dbg_mode);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
+ // The assert from <assert.h> reports to _CRT_ERROR channel
+ _CrtSetReportMode(_CRT_ERROR, dbg_mode);
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT);
+ // Internal CRT assert channel?
+ _CrtSetReportMode(_CRT_ASSERT, dbg_mode);
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT);
+ #endif
+
+ // clang-format on
+}
+
+} // namespace flatbuffers