AnDiXL commented on code in PR #3558:
URL: https://github.com/apache/brpc/pull/3558#discussion_r4129138640


##########
tools/flatbuffers/brpc_flatc.cpp:
##########
@@ -0,0 +1,495 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 <cstdint>
+#include <exception>
+#include <fstream>
+#include <iostream>
+#include <iterator>
+#include <limits>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <flatbuffers/idl.h>
+
+namespace {
+
+const char kLicense[] = R"(// Licensed to the Apache Software Foundation (ASF) 
under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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.
+
+// Generated by brpc_flatc. Do not edit.
+
+)";
+
+const char kArguments[] =
+    "::google::protobuf::RpcController* controller,\n"
+    "        const ::brpc::flatbuffers::Message* request,\n"
+    "        ::brpc::flatbuffers::Message* response,\n"
+    "        ::google::protobuf::Closure* done";
+
+struct Service {
+    const flatbuffers::ServiceDef* definition;
+    std::vector<int32_t> ids;
+};
+
+std::string JoinNamespace(const flatbuffers::Definition& definition,
+                          const std::string& separator) {
+    std::string result;
+    if (definition.defined_namespace) {
+        for (const auto& component : definition.defined_namespace->components) 
{
+            if (!result.empty()) {
+                result += separator;
+            }
+            result += component;
+        }
+    }
+    return result;
+}
+
+std::string Qualified(const flatbuffers::Definition& definition) {
+    const std::string ns = JoinNamespace(definition, "::");
+    return "::" + (ns.empty() ? "" : ns + "::") + definition.name;
+}
+
+std::string GuardComponent(const std::string& value) {
+    static const char digits[] = "0123456789ABCDEF";
+    std::string result;
+    for (unsigned char c : value) {
+        result += digits[c >> 4];
+        result += digits[c & 15];
+    }
+    return result;
+}
+
+bool IsCppIdentifier(const std::string& name) {
+    static const std::set<std::string> keywords = {
+        "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand",
+        "bitor", "bool", "break", "case", "catch", "char", "char16_t",
+        "char32_t", "class", "compl", "concept", "const", "const_cast",
+        "consteval", "constexpr", "constinit", "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",
+        "inline", "int", "long", "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", "template",
+        "this", "thread_local", "throw", "true", "try", "typedef",
+        "typeid", "typename", "union", "unsigned", "using", "virtual",
+        "void", "volatile", "wchar_t", "while", "xor", "xor_eq"
+    };
+    return !name.empty() && keywords.count(name) == 0;
+}
+
+bool ValidateName(const flatbuffers::Definition& definition,
+                  std::string* error) {
+    if (!IsCppIdentifier(definition.name)) {
+        *error = "C++ keyword is not supported: " + definition.name;
+        return false;
+    }
+    if (definition.defined_namespace) {
+        for (const auto& component : definition.defined_namespace->components) 
{
+            if (!IsCppIdentifier(component)) {
+                *error = "C++ keyword namespace is not supported: " + 
component;
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool ParseId(const flatbuffers::Value& value, int32_t* id) {
+    if (value.constant.empty() || 
!flatbuffers::IsInteger(value.type.base_type)) {
+        return false;
+    }
+    int64_t number = 0;
+    for (char c : value.constant) {
+        if (c < '0' || c > '9') {
+            return false;
+        }
+        number = number * 10 + (c - '0');
+        if (number > std::numeric_limits<int32_t>::max()) {
+            return false;
+        }
+    }
+    *id = static_cast<int32_t>(number);
+    return true;
+}
+
+bool CollectServices(const flatbuffers::Parser& parser,
+                     std::vector<Service>* services, std::string* error) {
+    for (const auto* definition : parser.services_.vec) {
+        // Included schemas are generated separately, just as with flatc --cpp.
+        if (definition->generated) {
+            continue;
+        }
+        if (!ValidateName(*definition, error)) {
+            return false;
+        }
+        Service service = {definition, {}};
+        std::set<int32_t> ids;
+        if (definition->calls.vec.empty()) {
+            *error = "rpc_service must contain at least one method: " +
+                     definition->name;
+            return false;
+        }
+        for (const auto* call : definition->calls.vec) {
+            if (!ValidateName(*call, error) ||
+                !ValidateName(*call->request, error) ||
+                !ValidateName(*call->response, error)) {
+                return false;
+            }
+            if (call->name == definition->name ||
+                call->name == definition->name + "_Stub" ||
+                call->name == "descriptor" || call->name == "GetDescriptor" ||
+                call->name == "FBCallMethod" || call->name == "Stub" ||
+                call->name == "channel" || call->name == "channel_" ||
+                call->name == "owned_channel_") {
+                *error = "method name collides with generated API: " + 
call->name;
+                return false;
+            }
+            const auto* attribute = call->attributes.Lookup("id");
+            int32_t id = 0;
+            if (!attribute || !ParseId(*attribute, &id)) {
+                *error = definition->name + "." + call->name +
+                         " requires an explicit nonnegative int32 (id: N)";
+                return false;
+            }
+            if (!ids.insert(id).second) {
+                *error = "duplicate method id " + std::to_string(id) +
+                         " in " + definition->name;
+                return false;
+            }
+            if (call->attributes.Lookup("streaming")) {
+                *error = "streaming RPC is not supported: " + call->name;
+                return false;
+            }
+            service.ids.push_back(id);
+        }
+        services->push_back(service);
+    }
+    if (services->empty()) {
+        *error = "input schema contains no rpc_service to generate";
+        return false;
+    }
+    return true;
+}
+
+void OpenNamespace(const flatbuffers::Definition& definition,
+                   std::ostream& out) {
+    if (definition.defined_namespace) {
+        for (const auto& component : definition.defined_namespace->components) 
{
+            out << "namespace " << component << " {\n";
+        }
+    }
+    out << '\n';
+}
+
+void CloseNamespace(const flatbuffers::Definition& definition,
+                    std::ostream& out) {
+    if (definition.defined_namespace) {
+        const auto& components = definition.defined_namespace->components;
+        for (auto it = components.rbegin(); it != components.rend(); ++it) {
+            out << "}  // namespace " << *it << '\n';
+        }
+    }
+    out << '\n';
+}
+
+void GenerateHeader(const Service& service, std::ostream& out) {
+    const auto& definition = *service.definition;
+    const std::string& name = definition.name;
+    OpenNamespace(definition, out);
+    out << "class " << name << "_Stub;\n\n"
+        << "class " << name << " : public ::brpc::flatbuffers::Service {\n"
+        << "public:\n"
+        << "    typedef " << name << "_Stub Stub;\n"
+        << "    static const ::brpc::flatbuffers::ServiceDescriptor* 
descriptor();\n"
+        << "    const ::brpc::flatbuffers::ServiceDescriptor* GetDescriptor() 
override;\n"
+        << "    void FBCallMethod(\n"
+        << "        const ::brpc::flatbuffers::MethodDescriptor* method,\n"
+        << "        " << kArguments << ") override;\n";
+    for (const auto* call : definition.calls.vec) {
+        out << "    virtual void " << call->name << "(\n"
+            << "        " << kArguments << ");\n";
+    }
+    out << "};\n\n"
+        << "class " << name << "_Stub : public " << name << " {\n"
+        << "public:\n"
+        << "    explicit " << name << "_Stub(\n"
+        << "        ::brpc::flatbuffers::RpcChannel* channel,\n"
+        << "        ::brpc::flatbuffers::Service::ChannelOwnership ownership 
=\n"
+        << "            
::brpc::flatbuffers::Service::STUB_DOESNT_OWN_CHANNEL);\n"
+        << "    ::brpc::flatbuffers::RpcChannel* channel() const { return 
channel_; }\n";
+    for (const auto* call : definition.calls.vec) {
+        out << "    void " << call->name << "(\n"
+            << "        " << kArguments << ") override;\n";
+    }
+    out << "\nprivate:\n"
+        << "    ::brpc::flatbuffers::RpcChannel* channel_;\n"
+        << "    ::std::unique_ptr<::brpc::flatbuffers::RpcChannel> 
owned_channel_;\n"
+        << "};\n\n";
+    CloseNamespace(definition, out);
+}
+
+void GenerateSource(const Service& service, std::ostream& out) {
+    const auto& definition = *service.definition;
+    const std::string& name = definition.name;
+    OpenNamespace(definition, out);
+    out << "const ::brpc::flatbuffers::ServiceDescriptor* " << name
+        << "::descriptor() {\n"
+        << "    struct Holder {\n"
+        << "        ::brpc::flatbuffers::ServiceDescriptor value;\n"
+        << "        Holder() {\n"
+        << "            const ::brpc::flatbuffers::BrpcDescriptorTable table = 
{\n"
+        << "                \"" << JoinNamespace(definition, ".") << "\", \""
+        << name << "\",\n                \"";
+    for (size_t i = 0; i < definition.calls.vec.size(); ++i) {
+        out << (i == 0 ? "" : " ") << definition.calls.vec[i]->name;
+    }
+    out << "\", {";
+    for (size_t i = 0; i < service.ids.size(); ++i) {
+        out << (i == 0 ? "" : ", ") << service.ids[i];
+    }
+    out << "}\n            };\n"
+        << "            if (value.init(table) != 0) {\n"
+        << "                throw ::std::runtime_error(\"invalid generated 
service descriptor\");\n"
+        << "            }\n"
+        << "        }\n"
+        << "    };\n"
+        << "    static const Holder holder;\n"
+        << "    return &holder.value;\n"
+        << "}\n\n"
+        << "const ::brpc::flatbuffers::ServiceDescriptor* " << name
+        << "::GetDescriptor() {\n"
+        << "    return descriptor();\n"
+        << "}\n\n"
+        << "void " << name << "::FBCallMethod(\n"
+        << "        const ::brpc::flatbuffers::MethodDescriptor* method,\n"
+        << "        " << kArguments << ") {\n"
+        << "    if (!method || method->service() != descriptor() ||\n"
+        << "        descriptor()->FindMethodByIndex(method->index()) != 
method) {\n"
+        << "        ::BrpcFlatbuffersFail(controller, done, \"invalid service 
method\");\n"
+        << "        return;\n"
+        << "    }\n"
+        << "    if (!request || !response) {\n"
+        << "        ::BrpcFlatbuffersFail(controller, done, \"null request or 
response\");\n"
+        << "        return;\n"
+        << "    }\n"
+        << "    switch (method->index()) {\n";
+    for (size_t i = 0; i < definition.calls.vec.size(); ++i) {
+        const auto& call = *definition.calls.vec[i];
+        out << "    case " << service.ids[i] << ":\n"
+            << "        if (!request->Verify<" << Qualified(*call.request)
+            << ">()) {\n"
+            << "            ::BrpcFlatbuffersFail(controller, done, \"invalid "
+            << call.request->name << " request\");\n"
+            << "            return;\n"
+            << "        }\n"
+            << "        this->" << call.name << "(controller, request, 
response, done);\n"
+            << "        return;\n";
+    }
+    out << "    default:\n"
+        << "        ::BrpcFlatbuffersFail(controller, done, \"unknown method 
id\");\n"
+        << "        return;\n"
+        << "    }\n"
+        << "}\n\n";
+    for (const auto* call : definition.calls.vec) {
+        out << "void " << name << "::" << call->name << "(\n"
+            << "        " << kArguments << ") {\n"
+            << "    (void)request;\n"
+            << "    (void)response;\n"
+            << "    ::BrpcFlatbuffersFail(controller, done, \"method not 
implemented: "
+            << name << "." << call->name << "\");\n"
+            << "}\n\n";
+    }
+    out << name << "_Stub::" << name << "_Stub(\n"
+        << "        ::brpc::flatbuffers::RpcChannel* channel,\n"
+        << "        ::brpc::flatbuffers::Service::ChannelOwnership 
ownership)\n"
+        << "    : channel_(channel),\n"
+        << "      owned_channel_(ownership == 
::brpc::flatbuffers::Service::STUB_OWNS_CHANNEL\n"
+        << "                         ? channel : nullptr) {}\n\n";
+    for (size_t i = 0; i < definition.calls.vec.size(); ++i) {
+        out << "void " << name << "_Stub::" << definition.calls.vec[i]->name
+            << "(\n        " << kArguments << ") {\n"
+            << "    if (!channel_) {\n"
+            << "        ::BrpcFlatbuffersFail(controller, done, \"null RPC 
channel\");\n"
+            << "        return;\n"
+            << "    }\n"
+            << "    channel_->FBCallMethod(descriptor()->method(" << i
+            << "), controller, request, response, done);\n"
+            << "}\n\n";
+    }
+    CloseNamespace(definition, out);
+}
+
+bool WriteFile(const std::string& path, const std::string& contents) {
+    std::ofstream stream(path.c_str(), std::ios::binary | std::ios::trunc);
+    stream << contents;
+    stream.close();
+    return !stream.fail();
+}
+
+void Usage(std::ostream& out) {
+    out << "Usage: brpc_flatc [-I include_dir]... [-o existing_output_dir] 
schema.fbs\n"
+        << "Run official flatc --cpp separately to produce 
schema_generated.h.\n";
+}
+
+int Run(int argc, char** argv) {
+    std::string input;
+    std::string output_dir = ".";
+    std::vector<std::string> include_dirs;
+    for (int i = 1; i < argc; ++i) {
+        const std::string arg = argv[i];
+        if (arg == "--help" || arg == "-h") {
+            Usage(std::cout);
+            return 0;
+        }
+        if (arg == "-o" || arg == "-I") {
+            if (++i == argc) {
+                Usage(std::cerr);
+                return 1;
+            }
+            if (arg == "-o") {
+                output_dir = argv[i];
+            } else {
+                include_dirs.push_back(argv[i]);
+            }
+        } else if (arg.compare(0, 2, "-I") == 0 && arg.size() > 2) {
+            include_dirs.push_back(arg.substr(2));
+        } else if (arg.empty() || arg[0] == '-' || !input.empty()) {
+            Usage(std::cerr);
+            return 1;
+        } else {
+            input = arg;
+        }
+    }
+    if (input.size() < 5 || input.substr(input.size() - 4) != ".fbs" ||
+        output_dir.empty()) {
+        Usage(std::cerr);
+        return 1;
+    }
+    const size_t slash = input.find_last_of("/\\");
+    const std::string basename = input.substr(slash == std::string::npos ? 0 : 
slash + 1);
+    const std::string stem = basename.substr(0, basename.size() - 4);
+    if (stem.empty() || stem.find_first_not_of(
+            
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.-") !=
+            std::string::npos) {
+        std::cerr << "brpc_flatc: unsupported schema filename\n";
+        return 1;
+    }
+    include_dirs.push_back(slash == std::string::npos ? "." :
+                           input.substr(0, slash == 0 ? 1 : slash));
+    std::vector<const char*> include_paths;
+    for (const auto& directory : include_dirs) {
+        include_paths.push_back(directory.c_str());
+    }
+    include_paths.push_back(nullptr);
+    std::ifstream stream(input.c_str(), std::ios::binary);
+    if (!stream) {
+        std::cerr << "brpc_flatc: cannot read " << input << '\n';
+        return 1;
+    }
+    const std::string schema((std::istreambuf_iterator<char>(stream)),
+                             std::istreambuf_iterator<char>());
+    if (stream.bad() || schema.find('\0') != std::string::npos) {
+        std::cerr << "brpc_flatc: invalid schema input\n";
+        return 1;
+    }
+    flatbuffers::Parser parser;
+    if (!parser.Parse(schema.c_str(), include_paths.data(), input.c_str())) {
+        std::cerr << parser.error_ << '\n';
+        return 1;
+    }
+    std::vector<Service> services;
+    std::string error;
+    if (!CollectServices(parser, &services, &error)) {
+        std::cerr << "brpc_flatc: " << error << '\n';
+        return 1;
+    }
+    // Coexisting service headers cannot define the same qualified service.
+    // Preserve filename bytes and that service identity, without tying output
+    // to checkout paths or folding punctuation and case into the same guard.
+    const std::string guard = "BRPC_FLATBUFFERS_GENERATED_" + 
GuardComponent(stem) +
+        "_" + GuardComponent(Qualified(*services.front().definition)) + "_H_";
+    std::ostringstream header;
+    header << kLicense << "#ifndef " << guard << "\n#define " << guard << 
"\n\n"
+           << "#include <memory>\n"
+           << "#include <google/protobuf/service.h>\n"
+           << "#include \"brpc/flatbuffers/message.h\"\n"
+           << "#include \"brpc/flatbuffers/service.h\"\n"
+           << "#include \"" << stem << "_generated.h\"\n\n"
+           << "#if !BRPC_WITH_FLATBUFFERS\n"
+           << "#error \"Generated services require BRPC_WITH_FLATBUFFERS\"\n"
+           << "#endif\n\n";
+    std::ostringstream source;
+    source << kLicense << "#include \"" << stem << ".brpc.fb.h\"\n\n"
+           << "#include <stdexcept>\n\n"
+           << "namespace {\n"
+           << "void BrpcFlatbuffersFail(::google::protobuf::RpcController* 
controller,\n"
+           << "                         ::google::protobuf::Closure* done,\n"
+           << "                         const char* reason) {\n"
+           << "    if (controller) {\n"
+           << "        controller->SetFailed(reason);\n"
+           << "    }\n"
+           << "    if (done) {\n"
+           << "        done->Run();\n"
+           << "    }\n"
+           << "}\n"
+           << "}  // namespace\n\n";
+    for (const auto& service : services) {
+        GenerateHeader(service, header);
+        GenerateSource(service, source);
+    }
+    header << "#endif  // " << guard << '\n';
+    if (output_dir.back() != '/') {
+        output_dir += '/';
+    }
+    if (!WriteFile(output_dir + stem + ".brpc.fb.h", header.str()) ||
+        !WriteFile(output_dir + stem + ".brpc.fb.cpp", source.str())) {

Review Comment:
   Addressed in `6847fa1c`. `brpc_flatc` now stages both outputs while holding 
an advisory lock on the output directory, preserves existing regular files as 
backups, and restores the previous pair when staging, backup, or publication 
fails. If restoration itself fails, it attempts to remove both final paths and 
reports when manual cleanup is required.
   
   The acceptance suite uses a test-only generator target with failpoints for 
source staging, source backup, header/source publication, and header/source 
restoration failures. It verifies byte-for-byte restoration or removal of both 
final paths as appropriate, retains recoverable backups, and also runs two 
concurrent publishers and compiles the resulting pair. The production target 
does not contain the failpoints. These cases passed on macOS and Linux.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to