diff --git a/include/flatbuffers/flexbuffers.h b/include/flatbuffers/flexbuffers.h index 35c3e0bb9..e1183b670 100644 --- a/include/flatbuffers/flexbuffers.h +++ b/include/flatbuffers/flexbuffers.h @@ -349,6 +349,12 @@ void AppendToString(std::string &s, T &&v, bool keys_quoted) { class Reference { public: + Reference() + : data_(nullptr), + parent_width_(0), + byte_width_(BIT_WIDTH_8), + type_(FBT_NULL) {} + Reference(const uint8_t *data, uint8_t parent_width, uint8_t byte_width, Type type) : data_(data), diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 506a717de..5dce91a78 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -491,6 +491,8 @@ struct ServiceDef : public Definition { // Container of options that may apply to any of the source/text generators. struct IDLOptions { + // Use flexbuffers instead for binary and text generation + bool use_flexbuffers; bool strict_json; bool skip_js_exports; bool use_goog_js_export_format; @@ -573,7 +575,8 @@ struct IDLOptions { bool set_empty_to_null; IDLOptions() - : strict_json(false), + : use_flexbuffers(false), + strict_json(false), skip_js_exports(false), use_goog_js_export_format(false), use_ES6_js_export_format(false), @@ -707,6 +710,7 @@ class Parser : public ParserState { explicit Parser(const IDLOptions &options = IDLOptions()) : current_namespace_(nullptr), empty_namespace_(nullptr), + flex_builder_(256, flexbuffers::BUILDER_FLAG_SHARE_ALL), root_struct_def_(nullptr), opts(options), uses_flexbuffers_(false), @@ -908,6 +912,8 @@ class Parser : public ParserState { std::string error_; // User readable error_ if Parse() == false FlatBufferBuilder builder_; // any data contained in the file + flexbuffers::Builder flex_builder_; + flexbuffers::Reference flex_root_; StructDef *root_struct_def_; std::string file_identifier_; std::string file_extension_; diff --git a/src/flatc.cpp b/src/flatc.cpp index 2f48c85fd..e52dad415 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -149,6 +149,8 @@ std::string FlatCompiler::GetUsageString(const char *program_name) const { " --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" + " --flexbuffers Used with \"binary\" and \"json\" options, it generates\n" + " data using schema-less FlexBuffers.\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" @@ -324,6 +326,8 @@ int FlatCompiler::Compile(int argc, const char **argv) { opts.set_empty_to_null = false; } else if (arg == "--java-primitive-has-method") { opts.java_primitive_has_method = true; + } else if (arg == "--flexbuffers") { + opts.use_flexbuffers = true; } else { for (size_t i = 0; i < params_.num_generators; ++i) { if (arg == params_.generators[i].generator_opt_long || @@ -407,7 +411,8 @@ int FlatCompiler::Compile(int argc, const char **argv) { } } else { // Check if file contains 0 bytes. - if (!is_binary_schema && contents.length() != strlen(contents.c_str())) { + if (!opts.use_flexbuffers && !is_binary_schema && + contents.length() != strlen(contents.c_str())) { Error("input file appears to be binary: " + filename, true); } if (is_schema) { @@ -418,9 +423,20 @@ int FlatCompiler::Compile(int argc, const char **argv) { } if (is_binary_schema) { LoadBinarySchema(*parser.get(), filename, contents); + } + if (opts.use_flexbuffers) { + if (opts.lang_to_generate == IDLOptions::kJson) { + parser->flex_root_ = flexbuffers::GetRoot( + reinterpret_cast(contents.c_str()), + contents.size()); + } else { + parser->flex_builder_.Clear(); + ParseFile(*parser.get(), filename, contents, include_directories); + } } else { ParseFile(*parser.get(), filename, contents, include_directories); - if (!is_schema && !parser->builder_.GetSize()) { + if (!opts.use_flexbuffers && !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: " + diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index 76cc5edee..2f78ae8c0 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -1782,6 +1782,14 @@ std::string BinaryFileName(const Parser &parser, const std::string &path, bool GenerateBinary(const Parser &parser, const std::string &path, const std::string &file_name) { + if (parser.opts.use_flexbuffers) { + auto data_vec = parser.flex_builder_.GetBuffer(); + auto data_ptr = reinterpret_cast(data_vec.data()); + return !parser.flex_builder_.GetSize() || + flatbuffers::SaveFile( + BinaryFileName(parser, path, file_name).c_str(), data_ptr, + parser.flex_builder_.GetSize(), true); + } return !parser.builder_.GetSize() || flatbuffers::SaveFile( BinaryFileName(parser, path, file_name).c_str(), diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp index 8be4230ed..66926a605 100644 --- a/src/idl_gen_text.cpp +++ b/src/idl_gen_text.cpp @@ -365,6 +365,12 @@ std::string TextFileName(const std::string &path, bool GenerateTextFile(const Parser &parser, const std::string &path, const std::string &file_name) { + if (parser.opts.use_flexbuffers) { + std::string json; + parser.flex_root_.ToString(true, parser.opts.strict_json, json); + return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(), + json.c_str(), json.size(), true); + } if (!parser.builder_.GetSize() || !parser.root_struct_def_) return true; std::string text; if (!GenerateText(parser, parser.builder_.GetBufferPointer(), &text)) { diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index c906d821a..50d59a225 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -2710,7 +2710,13 @@ bool Parser::ParseFlexBuffer(const char *source, const char *source_filename, 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(); + bool r; + + if (opts.use_flexbuffers) { + r = ParseFlexBuffer(source, source_filename, &flex_builder_); + } else { + r = !ParseRoot(source, include_paths, source_filename).Check(); + } FLATBUFFERS_ASSERT(0 == recurse_protection_counter); return r; }