diff options
author | tira-misu <tmisu@gmx.de> | 2018-12-13 20:59:27 +0100 |
---|---|---|
committer | Wouter van Oortmerssen <aardappel@gmail.com> | 2018-12-13 11:59:27 -0800 |
commit | dba962ebb836739e002049a6d484f54a176e9afe (patch) | |
tree | faf803f2d8b826a77be2b3b4368ade225486161b | |
parent | 60a0f35fbc43e578ffa2ea06c2956380a4bff10b (diff) | |
download | flatbuffers-dba962ebb836739e002049a6d484f54a176e9afe.tar.gz flatbuffers-dba962ebb836739e002049a6d484f54a176e9afe.tar.bz2 flatbuffers-dba962ebb836739e002049a6d484f54a176e9afe.zip |
Enable flatbuffer to initialize Parser from bfbs (#4283) (#5077)
* Enable flatbuffer to initialize Parser from bfbs (#4283)
Now its possible to generate json data from bfbs data type and flatbuffers data
and visa versa.
* add deserialize functionality in parser from bfbs
* add small usage sample
* Fix build break
* Merge branch 'pr/1' into fix-issue4283
* Fix buildbreak
* Build monster_test.bfbs with --bfbs-builtins
Attribute flexbuffer has be included in bfbs. Only with this attribute test
will run. By initialization a parser by a bfbs the attribute has to be known
for this filed. monsterdata_test.golden has a flexbuffer field so parse would
fail.
* Fix generate_code.sh
* Revert automatic indent changes by IDE
* Auto detect size prefixed binary schema files
* Use identifier (bfbs) to detect schema files
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | CMakeLists.txt | 9 | ||||
-rw-r--r-- | include/flatbuffers/flatc.h | 3 | ||||
-rw-r--r-- | include/flatbuffers/idl.h | 28 | ||||
-rw-r--r-- | samples/monster.bfbs | bin | 0 -> 1768 bytes | |||
-rw-r--r-- | samples/sample_bfbs.cpp | 77 | ||||
-rw-r--r-- | src/flatc.cpp | 50 | ||||
-rw-r--r-- | src/idl_parser.cpp | 313 | ||||
-rw-r--r-- | tests/generate_code.bat | 3 | ||||
-rwxr-xr-x | tests/generate_code.sh | 3 | ||||
-rw-r--r-- | tests/monster_test.bfbs | bin | 7280 -> 11624 bytes | |||
-rw-r--r-- | tests/test.cpp | 29 |
12 files changed, 493 insertions, 24 deletions
@@ -46,6 +46,8 @@ flatsamplebinary flatsamplebinary.exe flatsampletext flatsampletext.exe +flatsamplebfbs +flatsamplebfbs.exe grpctest grpctest.exe snapshot.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 389596e4..b42c4d14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,6 +123,14 @@ set(FlatBuffers_Sample_Text_SRCS ${CMAKE_CURRENT_BINARY_DIR}/samples/monster_generated.h ) +set(FlatBuffers_Sample_BFBS_SRCS + ${FlatBuffers_Library_SRCS} + src/idl_gen_general.cpp + samples/sample_bfbs.cpp + # file generated by running compiler on samples/monster.fbs + ${CMAKE_CURRENT_BINARY_DIR}/samples/monster_generated.h +) + set(FlatBuffers_GRPCTest_SRCS include/flatbuffers/flatbuffers.h include/flatbuffers/grpc.h @@ -316,6 +324,7 @@ if(FLATBUFFERS_BUILD_TESTS) include_directories(${CMAKE_CURRENT_BINARY_DIR}/samples) add_executable(flatsamplebinary ${FlatBuffers_Sample_Binary_SRCS}) add_executable(flatsampletext ${FlatBuffers_Sample_Text_SRCS}) + add_executable(flatsamplebfbs ${FlatBuffers_Sample_BFBS_SRCS}) endif() if(FLATBUFFERS_BUILD_GRPCTEST) diff --git a/include/flatbuffers/flatc.h b/include/flatbuffers/flatc.h index c7b60feb..f2765d23 100644 --- a/include/flatbuffers/flatc.h +++ b/include/flatbuffers/flatc.h @@ -80,6 +80,9 @@ class FlatCompiler { const std::string &contents, std::vector<const char *> &include_directories) const; + void LoadBinarySchema(Parser &parser, const std::string &filename, + const std::string &contents); + void Warn(const std::string &warn, bool show_exe_name = true) const; void Error(const std::string &err, bool usage = true, diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 18e1db9d..00e6af5e 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -153,6 +153,8 @@ struct Type { Offset<reflection::Type> Serialize(FlatBufferBuilder *builder) const; + bool Deserialize(const Parser &parser, const reflection::Type *type); + BaseType base_type; BaseType element; // only set if t == BASE_TYPE_VECTOR StructDef *struct_def; // only set if t or element == BASE_TYPE_STRUCT @@ -235,6 +237,9 @@ struct Definition { flatbuffers::Vector<flatbuffers::Offset<reflection::KeyValue>>> SerializeAttributes(FlatBufferBuilder *builder, const Parser &parser) const; + bool DeserializeAttributes(Parser &parser, + const Vector<Offset<reflection::KeyValue>> *attrs); + std::string name; std::string file; std::vector<std::string> doc_comment; @@ -261,6 +266,8 @@ struct FieldDef : public Definition { Offset<reflection::Field> Serialize(FlatBufferBuilder *builder, uint16_t id, const Parser &parser) const; + bool Deserialize(Parser &parser, const reflection::Field *field); + Value value; bool deprecated; // Field is allowed to be present in old data, but can't be. // written in new data nor accessed in new code. @@ -291,6 +298,8 @@ struct StructDef : public Definition { Offset<reflection::Object> Serialize(FlatBufferBuilder *builder, const Parser &parser) const; + bool Deserialize(Parser &parser, const reflection::Object *object); + SymbolTable<FieldDef> fields; bool fixed; // If it's struct, not a table. @@ -317,9 +326,12 @@ inline size_t InlineAlignment(const Type &type) { struct EnumVal { EnumVal(const std::string &_name, int64_t _val) : name(_name), value(_val) {} + EnumVal(){}; Offset<reflection::EnumVal> Serialize(FlatBufferBuilder *builder, const Parser &parser) const; + bool Deserialize(const Parser &parser, const reflection::EnumVal *val); + std::string name; std::vector<std::string> doc_comment; int64_t value; @@ -340,6 +352,8 @@ struct EnumDef : public Definition { Offset<reflection::Enum> Serialize(FlatBufferBuilder *builder, const Parser &parser) const; + bool Deserialize(Parser &parser, const reflection::Enum *values); + SymbolTable<EnumVal> vals; bool is_union; // Type is a union which uses type aliases where at least one type is @@ -358,11 +372,14 @@ inline bool EqualByName(const Type &a, const Type &b) { struct RPCCall : public Definition { Offset<reflection::RPCCall> Serialize(FlatBufferBuilder *builder, const Parser &parser) const; + bool Deserialize(Parser &parser, const reflection::RPCCall *call); + StructDef *request, *response; }; struct ServiceDef : public Definition { Offset<reflection::Service> Serialize(FlatBufferBuilder *builder, const Parser &parser) const; + bool Deserialize(Parser &parser, const reflection::Service *service); SymbolTable<RPCCall> calls; }; @@ -647,6 +664,15 @@ class Parser : public ParserState { // See reflection/reflection.fbs void Serialize(); + // Deserialize a schema buffer + bool Deserialize(const uint8_t *buf, const size_t size); + + // Fills internal structure as if the schema passed had been loaded by parsing + // with Parse except that included filenames will not be populated. + bool Deserialize(const reflection::Schema* schema); + + Type* DeserializeType(const reflection::Type* type); + // Checks that the schema represented by this parser is a safe evolution // of the schema provided. Returns non-empty error on any problems. std::string ConformTo(const Parser &base); @@ -661,6 +687,8 @@ class Parser : public ParserState { StructDef *LookupStruct(const std::string &id) const; + std::string UnqualifiedName(std::string fullQualifiedName); + private: void Message(const std::string &msg); void Warning(const std::string &msg); diff --git a/samples/monster.bfbs b/samples/monster.bfbs Binary files differnew file mode 100644 index 00000000..95a977a3 --- /dev/null +++ b/samples/monster.bfbs diff --git a/samples/sample_bfbs.cpp b/samples/sample_bfbs.cpp new file mode 100644 index 00000000..e2424de4 --- /dev/null +++ b/samples/sample_bfbs.cpp @@ -0,0 +1,77 @@ +/* + * 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/idl.h" +#include "flatbuffers/util.h" + +#include "monster_test_generated.h" +#include "monster_generated.h" // Already includes "flatbuffers/flatbuffers.h". + +using namespace MyGame::Sample; + +// This is an example of parsing text straight into a buffer and then +// generating flatbuffer (JSON) text from the buffer. +int main(int /*argc*/, const char * /*argv*/[]) { + // load FlatBuffer schema (.fbs) and JSON from disk + std::string schema_file; + std::string json_file; + std::string bfbs_file; + bool ok = + flatbuffers::LoadFile("tests/monster_test.fbs", false, &schema_file) && + flatbuffers::LoadFile("tests/monsterdata_test.golden", false, &json_file) && + flatbuffers::LoadFile("tests/monster_test.bfbs", true, &bfbs_file); + if (!ok) { + printf("couldn't load files!\n"); + return 1; + } + + const char *include_directories[] = { "samples", "tests", + "tests/include_test", nullptr }; + // parse fbs schema + flatbuffers::Parser parser1; + ok = parser1.Parse(schema_file.c_str(), include_directories); + assert(ok); + + // inizialize parser by deserializing bfbs schema + flatbuffers::Parser parser2; + ok = parser2.Deserialize((uint8_t *)bfbs_file.c_str(), bfbs_file.length()); + assert(ok); + + // parse json in parser from fbs and bfbs + ok = parser1.Parse(json_file.c_str(), include_directories); + ok = parser2.Parse(json_file.c_str(), include_directories); + assert(ok); + + // to ensure it is correct, we now generate text back from the binary, + // and compare the two: + std::string jsongen1; + if (!GenerateText(parser1, parser1.builder_.GetBufferPointer(), &jsongen1)) { + printf("Couldn't serialize parsed data to JSON!\n"); + return 1; + } + + std::string jsongen2; + if (!GenerateText(parser2, parser2.builder_.GetBufferPointer(), &jsongen2)) { + printf("Couldn't serialize parsed data to JSON!\n"); + return 1; + } + + if (jsongen1 != jsongen2) { + printf("%s----------------\n%s", jsongen1.c_str(), jsongen2.c_str()); + } + + printf("The FlatBuffer has been parsed from JSON successfully.\n"); +} diff --git a/src/flatc.cpp b/src/flatc.cpp index 88ace63e..d42a5852 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -36,6 +36,15 @@ void FlatCompiler::ParseFile( 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); } @@ -127,8 +136,9 @@ 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" - "FILEs may be schemas (must end in .fbs), or JSON files (conforming to preceding\n" - "schema). FILEs after the -- must be binary flatbuffer format files.\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"; @@ -323,8 +333,14 @@ int FlatCompiler::Compile(int argc, const char **argv) { std::string contents; if (!flatbuffers::LoadFile(conform_to_schema.c_str(), true, &contents)) Error("unable to load schema: " + conform_to_schema); - ParseFile(conform_parser, conform_to_schema, contents, - conform_include_directories); + + 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)); @@ -340,6 +356,7 @@ int FlatCompiler::Compile(int argc, const char **argv) { 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( @@ -366,7 +383,7 @@ int FlatCompiler::Compile(int argc, const char **argv) { } } else { // Check if file contains 0 bytes. - if (contents.length() != strlen(contents.c_str())) { + if (!is_binary_schema && contents.length() != strlen(contents.c_str())) { Error("input file appears to be binary: " + filename, true); } if (is_schema) { @@ -375,15 +392,19 @@ int FlatCompiler::Compile(int argc, const char **argv) { // so explicitly using an include. parser.reset(new flatbuffers::Parser(opts)); } - 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_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 && !conform_to_schema.empty()) { + 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); } @@ -401,7 +422,8 @@ int FlatCompiler::Compile(int argc, const char **argv) { if (generator_enabled[i]) { if (!print_make_rules) { flatbuffers::EnsureDirExists(output_path); - if ((!params_.generators[i].schema_only || is_schema) && + 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 ") + diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 357337a7..9e607ca6 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -91,6 +91,13 @@ std::string MakeCamel(const std::string &in, bool first) { 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->Length(); index++) + doc.push_back(documentation->Get(index)->str()); +} + void Parser::Message(const std::string &msg) { error_ = file_being_parsed_.length() ? AbsolutePath(file_being_parsed_) : ""; // clang-format off @@ -1763,6 +1770,21 @@ Namespace *Parser::UniqueNamespace(Namespace *ns) { return ns; } +std::string Parser::UnqualifiedName(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()); @@ -2675,6 +2697,32 @@ void Parser::Serialize() { } } +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; @@ -2695,6 +2743,45 @@ Offset<reflection::Object> StructDef::Serialize(FlatBufferBuilder *builder, 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()); + fixed = object->is_struct(); + minalign = object->minalign(); + predecl = false; + sortbysize = attributes.Lookup("original_order") == nullptr && !fixed; + std::vector<uoffset_t> indexes = + std::vector<uoffset_t>(object->fields()->Length()); + for (uoffset_t i = 0; i < object->fields()->Length(); i++) + indexes[object->fields()->Get(i)->id()] = i; + for (size_t i = 0; i < indexes.size(); i++) { + auto field = object->fields()->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() + ? object->fields()->Get(indexes[i+1]) + : nullptr; + bytesize += size; + field_def->padding = + next_field ? (next_field->offset() - field_def->value.offset) - size + : PaddingBytes(bytesize, minalign); + bytesize += field_def->padding; + } + } + FLATBUFFERS_ASSERT(static_cast<int>(bytesize) == object->bytesize()); + return true; +} + Offset<reflection::Field> FieldDef::Serialize(FlatBufferBuilder *builder, uint16_t id, const Parser &parser) const { @@ -2715,6 +2802,38 @@ Offset<reflection::Field> FieldDef::Serialize(FlatBufferBuilder *builder, // space by sharing it. Same for common values of value.type. } +bool FieldDef::Deserialize(Parser &parser, const reflection::Field *field) { + name = parser.UnqualifiedName(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; + } + DeserializeDoc(doc_comment, field->documentation()); + return true; +} + Offset<reflection::RPCCall> RPCCall::Serialize(FlatBufferBuilder *builder, const Parser &parser) const { auto name__ = builder->CreateString(name); @@ -2728,6 +2847,17 @@ Offset<reflection::RPCCall> RPCCall::Serialize(FlatBufferBuilder *builder, 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; @@ -2744,6 +2874,25 @@ Offset<reflection::Service> ServiceDef::Serialize(FlatBufferBuilder *builder, 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; @@ -2762,6 +2911,26 @@ Offset<reflection::Enum> EnumDef::Serialize(FlatBufferBuilder *builder, 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); @@ -2774,6 +2943,16 @@ Offset<reflection::EnumVal> EnumVal::Serialize(FlatBufferBuilder *builder, 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, @@ -2782,6 +2961,31 @@ Offset<reflection::Type> Type::Serialize(FlatBufferBuilder *builder) const { struct_def ? struct_def->index : (enum_def ? enum_def->index : -1)); } +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()); + if (type->index() >= 0) { + if (type->base_type() == reflection::Obj || + (type->base_type() == reflection::Vector && + 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, @@ -2803,6 +3007,115 @@ Definition::SerializeAttributes(FlatBufferBuilder *builder, } } +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(); + 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; diff --git a/tests/generate_code.bat b/tests/generate_code.bat index fedb08e9..304fecec 100644 --- a/tests/generate_code.bat +++ b/tests/generate_code.bat @@ -18,10 +18,11 @@ if "%1"=="-b" set buildtype=%2 ..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --lobster --lua --js --rust --ts --php --grpc --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json || goto FAIL ..\%buildtype%\flatc.exe --cpp --java --csharp --go --binary --python --lobster --lua --js --rust --ts --php --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs || goto FAIL ..\%buildtype%\flatc.exe --cpp --js --ts --php --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs || goto FAIL -..\%buildtype%\flatc.exe -b --schema --bfbs-comments -I include_test monster_test.fbs || goto FAIL +..\%buildtype%\flatc.exe -b --schema --bfbs-comments --bfbs-builtins -I include_test monster_test.fbs || goto FAIL ..\%buildtype%\flatc.exe --jsonschema --schema -I include_test monster_test.fbs || goto FAIL cd ../samples ..\%buildtype%\flatc.exe --cpp --lobster --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr monster.fbs || goto FAIL +..\%buildtype%\flatc.exe -b --schema --bfbs-comments --bfbs-builtins monster.fbs || goto FAIL cd ../reflection call generate_code.bat %1 %2 || goto FAIL diff --git a/tests/generate_code.sh b/tests/generate_code.sh index 89b868fd..d5ade21a 100755 --- a/tests/generate_code.sh +++ b/tests/generate_code.sh @@ -18,9 +18,10 @@ set -e ../flatc --cpp --java --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --grpc --gen-mutable --reflect-names --gen-object-api --gen-compare --no-includes --cpp-ptr-type flatbuffers::unique_ptr --no-fb-import -I include_test monster_test.fbs monsterdata_test.json ../flatc --cpp --java --csharp --dart --go --binary --lobster --lua --python --js --ts --php --rust --gen-mutable --reflect-names --no-fb-import --cpp-ptr-type flatbuffers::unique_ptr -o namespace_test namespace_test/namespace_test1.fbs namespace_test/namespace_test2.fbs ../flatc --cpp --js --ts --php --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr -o union_vector ./union_vector/union_vector.fbs -../flatc -b --schema --bfbs-comments -I include_test monster_test.fbs +../flatc -b --schema --bfbs-comments --bfbs-builtins -I include_test monster_test.fbs ../flatc --jsonschema --schema -I include_test monster_test.fbs cd ../samples ../flatc --cpp --lobster --gen-mutable --reflect-names --gen-object-api --gen-compare --cpp-ptr-type flatbuffers::unique_ptr monster.fbs +../flatc -b --schema --bfbs-comments --bfbs-builtins monster.fbs cd ../reflection ./generate_code.sh diff --git a/tests/monster_test.bfbs b/tests/monster_test.bfbs Binary files differindex 2bfda219..f96b9fd2 100644 --- a/tests/monster_test.bfbs +++ b/tests/monster_test.bfbs diff --git a/tests/test.cpp b/tests/test.cpp index b8a8c56e..656849a4 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -569,25 +569,37 @@ void JsonDefaultTest() { // example of parsing text straight into a buffer, and generating // text back from it: -void ParseAndGenerateTextTest() { +void ParseAndGenerateTextTest(bool binary) { // load FlatBuffer schema (.fbs) and JSON from disk std::string schemafile; std::string jsonfile; - TEST_EQ(flatbuffers::LoadFile((test_data_path + "monster_test.fbs").c_str(), - false, &schemafile), + TEST_EQ(flatbuffers::LoadFile( + (test_data_path + "monster_test." + (binary ? "bfbs" : "fbs")) + .c_str(), + binary, &schemafile), true); TEST_EQ(flatbuffers::LoadFile( (test_data_path + "monsterdata_test.golden").c_str(), false, &jsonfile), true); - // parse schema first, so we can use it to parse the data after - flatbuffers::Parser parser; auto include_test_path = - flatbuffers::ConCatPathFileName(test_data_path, "include_test"); + flatbuffers::ConCatPathFileName(test_data_path, "include_test"); const char *include_directories[] = { test_data_path.c_str(), include_test_path.c_str(), nullptr }; - TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true); + + // parse schema first, so we can use it to parse the data after + flatbuffers::Parser parser; + if (binary) { + flatbuffers::Verifier verifier( + reinterpret_cast<const uint8_t *>(schemafile.c_str()), + schemafile.size()); + TEST_EQ(reflection::VerifySchemaBuffer(verifier), true); + //auto schema = reflection::GetSchema(schemafile.c_str()); + TEST_EQ(parser.Deserialize((const uint8_t *)schemafile.c_str(), schemafile.size()), true); + } else { + TEST_EQ(parser.Parse(schemafile.c_str(), include_directories), true); + } TEST_EQ(parser.Parse(jsonfile.c_str(), include_directories), true); // here, parser.builder_ contains a binary buffer that is the parsed data. @@ -2467,7 +2479,8 @@ int FlatBufferTests() { test_data_path = FLATBUFFERS_STRING(FLATBUFFERS_TEST_PATH_PREFIX) + test_data_path; #endif - ParseAndGenerateTextTest(); + ParseAndGenerateTextTest(false); + ParseAndGenerateTextTest(true); ReflectionTest(flatbuf.data(), flatbuf.size()); ParseProtoTest(); UnionVectorTest(); |