blob: fe47fb33fae5399b9b2b8a01dff9c7ce7d3d38ea [file] [log] [blame]
// 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.
// mcpack2pb - Make protobuf be front-end of mcpack/compack
// Date: Mon Oct 19 17:17:36 CST 2015
#include <set>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/plugin.h>
#include "butil/file_util.h"
#include "butil/string_printf.h"
#include "butil/strings/string_util.h"
#include "idl_options.pb.h"
#include "mcpack2pb/mcpack2pb.h"
namespace mcpack2pb {
const std::string get_idl_name(const google::protobuf::FieldDescriptor* f) {
const std::string real_name = butil::EnsureString(f->options().GetExtension(idl_name));
return real_name.empty() ? butil::EnsureString(f->name()) : real_name;
}
bool is_integral_type(ConvertibleIdlType type) {
switch (type) {
case IDL_INT8:
case IDL_INT16:
case IDL_INT32:
case IDL_INT64:
case IDL_UINT8:
case IDL_UINT16:
case IDL_UINT32:
case IDL_UINT64:
return true;
default:
return false;
}
}
const std::string field_to_string(const google::protobuf::FieldDescriptor* f) {
switch (f->type()) {
case google::protobuf::FieldDescriptor::TYPE_DOUBLE: return "double";
case google::protobuf::FieldDescriptor::TYPE_FLOAT: return "float";
case google::protobuf::FieldDescriptor::TYPE_INT64: return "int64";
case google::protobuf::FieldDescriptor::TYPE_UINT64: return "uint64";
case google::protobuf::FieldDescriptor::TYPE_INT32: return "int32";
case google::protobuf::FieldDescriptor::TYPE_FIXED64: return "fixed64";
case google::protobuf::FieldDescriptor::TYPE_FIXED32: return "fixed32";
case google::protobuf::FieldDescriptor::TYPE_BOOL: return "bool";
case google::protobuf::FieldDescriptor::TYPE_STRING: return "string";
case google::protobuf::FieldDescriptor::TYPE_GROUP:
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
return butil::EnsureString(f->message_type()->name());
case google::protobuf::FieldDescriptor::TYPE_BYTES: return "bytes";
case google::protobuf::FieldDescriptor::TYPE_UINT32: return "uint32";
case google::protobuf::FieldDescriptor::TYPE_ENUM:
return butil::EnsureString(f->enum_type()->name());
case google::protobuf::FieldDescriptor::TYPE_SFIXED32: return "sfixed32";
case google::protobuf::FieldDescriptor::TYPE_SFIXED64: return "sfixed64";
case google::protobuf::FieldDescriptor::TYPE_SINT32: return "sint32";
case google::protobuf::FieldDescriptor::TYPE_SINT64: return "sint64";
}
return "unknown_protobuf_type";
}
const char* to_mcpack_typestr(const google::protobuf::FieldDescriptor* f) {
switch (f->type()) {
case google::protobuf::FieldDescriptor::TYPE_DOUBLE: return "double";
case google::protobuf::FieldDescriptor::TYPE_FLOAT: return "float";
case google::protobuf::FieldDescriptor::TYPE_INT64: return "int64";
case google::protobuf::FieldDescriptor::TYPE_UINT64: return "uint64";
case google::protobuf::FieldDescriptor::TYPE_INT32: return "int32";
case google::protobuf::FieldDescriptor::TYPE_FIXED64: return "uint64";
case google::protobuf::FieldDescriptor::TYPE_FIXED32: return "uint32";
case google::protobuf::FieldDescriptor::TYPE_BOOL: return "bool";
case google::protobuf::FieldDescriptor::TYPE_STRING: return "string";
case google::protobuf::FieldDescriptor::TYPE_GROUP: return "object";
case google::protobuf::FieldDescriptor::TYPE_MESSAGE: return "object";
case google::protobuf::FieldDescriptor::TYPE_BYTES: return "binary";
case google::protobuf::FieldDescriptor::TYPE_UINT32: return "uint32";
case google::protobuf::FieldDescriptor::TYPE_ENUM: return "int32";
case google::protobuf::FieldDescriptor::TYPE_SFIXED32: return "int32";
case google::protobuf::FieldDescriptor::TYPE_SFIXED64: return "int64";
case google::protobuf::FieldDescriptor::TYPE_SINT32: return "int32";
case google::protobuf::FieldDescriptor::TYPE_SINT64: return "int64";
}
return "unknown_protobuf_type";
}
const char* to_mcpack_typestr(ConvertibleIdlType type,
const google::protobuf::FieldDescriptor* f) {
switch (type) {
case IDL_AUTO: return to_mcpack_typestr(f);
case IDL_INT8: return "int8";
case IDL_INT16: return "int16";
case IDL_INT32: return "int32";
case IDL_INT64: return "int64";
case IDL_UINT8: return "uint8";
case IDL_UINT16: return "uint16";
case IDL_UINT32: return "uint32";
case IDL_UINT64: return "uint64";
case IDL_BOOL: return "bool";
case IDL_FLOAT: return "float";
case IDL_DOUBLE: return "double";
case IDL_BINARY: return "binary";
case IDL_STRING: return "string";
}
return "unknown";
}
const char* to_mcpack_typestr_uppercase(
ConvertibleIdlType type,
const google::protobuf::FieldDescriptor* f) {
const char* s = to_mcpack_typestr(type, f);
static char tempbuf[32];
char* p = tempbuf;
for (; *s; ++s, ++p) {
*p = ::toupper(*s);
}
*p = 0;
return tempbuf;
}
const char* describe_idl_type(ConvertibleIdlType type) {
switch (type) {
case IDL_AUTO: return "IDL_AUTO";
case IDL_INT8: return "IDL_INT8";
case IDL_INT16: return "IDL_INT16";
case IDL_INT32: return "IDL_INT32";
case IDL_INT64: return "IDL_INT64";
case IDL_UINT8: return "IDL_UINT8";
case IDL_UINT16: return "IDL_UINT16";
case IDL_UINT32: return "IDL_UINT32";
case IDL_UINT64: return "IDL_UINT64";
case IDL_BOOL: return "IDL_BOOL";
case IDL_FLOAT: return "IDL_FLOAT";
case IDL_DOUBLE: return "IDL_DOUBLE";
case IDL_BINARY: return "IDL_BINARY";
case IDL_STRING: return "IDL_STRING";
}
return "Bad ConvertibleIdlType";
}
static std::string to_var_name(const std::string& name) {
std::string result = name;
for (size_t i = 0; i < result.size(); ++i) {
if (result[i] == '.') {
result[i] = '_';
}
}
return result;
}
static std::string to_cpp_name(const std::string& full_name) {
std::string cname;
cname.reserve(full_name.size() + 8);
for (size_t i = 0; i < full_name.size(); ++i) {
if (full_name[i] == '.') {
cname.append("::", 2);
} else {
cname.push_back(full_name[i]);
}
}
return cname;
}
bool generate_declarations(const std::set<std::string>& ref_msgs,
const std::set<std::string>& ref_maps,
google::protobuf::io::Printer& decl) {
for (std::set<std::string>::const_iterator
it = ref_msgs.begin(); it != ref_msgs.end(); ++it) {
decl.Print(
"extern bool parse_$vmsg$_body_internal(\n"
" ::google::protobuf::Message* msg,\n"
" ::mcpack2pb::UnparsedValue& value);\n"
"extern void serialize_$vmsg$_body(\n"
" const ::google::protobuf::Message& msg,\n"
" ::mcpack2pb::Serializer& serializer,\n"
" ::mcpack2pb::SerializationFormat format);\n"
"extern ::mcpack2pb::FieldMap* g_$vmsg$_fields;\n"
, "vmsg", *it);
}
for (std::set<std::string>::const_iterator
it = ref_maps.begin(); it != ref_maps.end(); ++it) {
decl.Print(
"extern bool set_$vmsg$_value(::google::protobuf::Message* msg,\n"
" ::mcpack2pb::UnparsedValue& value);\n"
, "vmsg", *it);
}
return !decl.failed();
}
#define TEMPLATE_OF_SET_FUNC_SIGNATURE() \
"bool set_$vmsg$_$lcfield$(::google::protobuf::Message* msg_base,\n" \
" ::mcpack2pb::UnparsedValue& value) "
#define TEMPLATE_OF_SET_FUNC_BODY(fntype) \
"{\n" \
" static_cast<$msg$*>(msg_base)->set_$lcfield$(value.as_"#fntype"(\"$field$\"));\n" \
" return value.stream()->good();\n" \
"}\n"
#define TEMPLATE_OF_ADD_FUNC_SIGNATURE() \
"bool set_$vmsg$_$lcfield$(::google::protobuf::Message* msg_base,\n" \
" ::mcpack2pb::UnparsedValue& value) " \
#define TEMPLATE_OF_ADD_FUNC_BODY(fntype, pbtype) \
"{\n" \
" $msg$* const msg = static_cast<$msg$*>(msg_base);\n" \
" if (value.type() == ::mcpack2pb::FIELD_ISOARRAY) {\n" \
" ::mcpack2pb::ISOArrayIterator it(value);\n" \
" msg->mutable_$lcfield$()->Reserve(it.item_count());\n" \
" for (; it != NULL; ++it) {\n" \
" msg->add_$lcfield$(it.as_"#fntype "());\n" \
" }\n" \
" return value.stream()->good();\n" \
" } else if (value.type() == ::mcpack2pb::FIELD_ARRAY) {\n" \
" ::mcpack2pb::ArrayIterator it(value);\n" \
" msg->mutable_$lcfield$()->Reserve(it.item_count());\n" \
" for (; it != NULL; ++it) {\n" \
" msg->add_$lcfield$(it->as_"#fntype "(\"$field$\"));\n" \
" }\n" \
" return value.stream()->good();\n" \
" }\n" \
" LOG(ERROR) << \"Can't set \" << value << \" to $field$ (repeated " #pbtype ")\";\n" \
" return false;\n" \
"}\n"
static bool is_map_entry(const google::protobuf::Descriptor* d) {
if (d->field_count() != 2 ||
d->field(0)->name() != "key" ||
d->field(0)->number() != 1 ||
d->field(1)->name() != "value" ||
d->field(1)->number() != 2) {
return false;
}
if (d->field(0)->is_repeated()) {
LOG(ERROR) << d->field(0)->full_name() << " must be required or optional";
return false;
}
if (d->field(1)->is_repeated()) {
LOG(ERROR) << d->field(1)->full_name() << " must be required or optional";
return false;
}
if (d->field(0)->cpp_type() != google::protobuf::FieldDescriptor::CPPTYPE_STRING) {
LOG(ERROR) << "key of idl map must be string";
return false;
}
return true;
}
static bool generate_parsing(const google::protobuf::Descriptor* d,
std::set<std::string> & ref_msgs,
std::set<std::string> & ref_maps,
google::protobuf::io::Printer& impl) {
std::string var_name = mcpack2pb::to_var_name(butil::EnsureString(d->full_name()));
std::string cpp_name = mcpack2pb::to_cpp_name(butil::EnsureString(d->full_name()));
ref_msgs.insert(var_name);
impl.Print("\n// $msg$ from mcpack\n", "msg", d->full_name());
for (int i = 0; i < d->field_count(); ++i) {
const google::protobuf::FieldDescriptor* f = d->field(i);
if (f->is_repeated()) {
impl.Print("// repeated $type$ $name$ = $number$;\n"
, "type", field_to_string(f)
, "name", f->name()
, "number", butil::string_printf("%d", f->number()));
impl.Print(TEMPLATE_OF_ADD_FUNC_SIGNATURE()
, "vmsg", var_name
, "lcfield", f->lowercase_name());
switch (f->cpp_type()) {
case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
impl.Print(TEMPLATE_OF_ADD_FUNC_BODY(int32, int32)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
impl.Print(TEMPLATE_OF_ADD_FUNC_BODY(int64, int64)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
impl.Print(TEMPLATE_OF_ADD_FUNC_BODY(uint32, uint32)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
impl.Print(TEMPLATE_OF_ADD_FUNC_BODY(uint64, uint64)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
impl.Print(TEMPLATE_OF_ADD_FUNC_BODY(bool, bool)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
impl.Print(
"{\n"
" $msg$* const msg = static_cast<$msg$*>(msg_base);\n"
" if (value.type() == ::mcpack2pb::FIELD_ISOARRAY) {\n"
" ::mcpack2pb::ISOArrayIterator it(value);\n"
" msg->mutable_$lcfield$()->Reserve(it.item_count());\n"
" for (; it != NULL; ++it) {\n"
" msg->add_$lcfield$(($enum$)it.as_int32());\n"
" }\n"
" return value.stream()->good();\n"
" } else if (value.type() == ::mcpack2pb::FIELD_ARRAY) {\n"
" ::mcpack2pb::ArrayIterator it(value);\n"
" msg->mutable_$lcfield$()->Reserve(it.item_count());\n"
" for (; it != NULL; ++it) {\n"
" msg->add_$lcfield$(($enum$)it->as_int32(\"$enum$\"));\n"
" }\n"
" return value.stream()->good();\n"
" }\n"
" LOG(ERROR) << \"Can't set \" << value << \" to repeated $enum$\";\n"
" return false;\n"
"}\n"
, "msg", cpp_name
, "enum", to_cpp_name(butil::EnsureString(f->enum_type()->full_name()))
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
impl.Print(TEMPLATE_OF_ADD_FUNC_BODY(float, float)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
impl.Print(TEMPLATE_OF_ADD_FUNC_BODY(double, double)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
impl.Print(
"{\n"
" $msg$* const msg = static_cast<$msg$*>(msg_base);\n"
" if (value.type() == ::mcpack2pb::FIELD_ARRAY) {\n"
" ::mcpack2pb::ArrayIterator it(value);\n"
" msg->mutable_$lcfield$()->Reserve(it.item_count());\n"
" for (; it != NULL; ++it) {\n"
" if (it->type() == ::mcpack2pb::FIELD_STRING) {\n"
" it->as_string(msg->add_$lcfield$(), \"$field$\");\n"
" } else if (it->type() == ::mcpack2pb::FIELD_BINARY) {\n"
" it->as_binary(msg->add_$lcfield$(), \"$field$\");\n"
" } else {\n"
" LOG(ERROR) << \"Can't add \" << *it << \" to $field$ (repeated string)\";\n"
" return false;\n"
" }\n"
" }\n"
" return value.stream()->good();\n"
" }\n"
" LOG(ERROR) << \"Can't set \" << value << \" to $field$ (repeated string)\";\n"
" return false;\n"
"}\n"
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
std::string var_name2 = mcpack2pb::to_var_name(butil::EnsureString(f->message_type()->full_name()));
std::string cpp_name2 = mcpack2pb::to_cpp_name(butil::EnsureString(f->message_type()->full_name()));
if (is_map_entry(f->message_type())) {
ref_maps.insert(var_name2);
impl.Print(
"{\n"
" $msg$* const msg = static_cast<$msg$*>(msg_base);\n"
" if (value.type() == ::mcpack2pb::FIELD_OBJECT) {\n"
" ::mcpack2pb::ObjectIterator it(value);\n"
" for (; it != NULL; ++it) {\n"
" $msg2$* sub = msg->add_$lcfield$();\n"
" sub->set_key(it->name.data(), it->name.size());\n"
, "msg", cpp_name
, "msg2", cpp_name2
, "lcfield", f->lowercase_name());
impl.Print(
" if (!set_$vmsg2$_value(sub, it->value)) {\n"
" return false;\n"
" }\n"
" }\n"
" return value.stream()->good();\n"
" }\n"
" LOG(ERROR) << \"Can't set \" << value << \" to $field$ (repeated $msg2$)\";\n"
" return false;\n"
"}\n"
, "vmsg2", var_name2
, "msg2", f->message_type()->full_name()
, "field", f->full_name());
break;
}
ref_msgs.insert(var_name2);
impl.Print(
"{\n"
" $msg$* const msg = static_cast<$msg$*>(msg_base);\n"
" if (value.type() == ::mcpack2pb::FIELD_OBJECTISOARRAY) {\n"
" ::mcpack2pb::ObjectIterator it(value);\n"
" for (; it != NULL; ++it) {\n"
" ::mcpack2pb::SetFieldFn* fn = g_$vmsg2$_fields->seek(it->name);\n"
" if (!fn) {\n"
" if (!FLAGS_mcpack2pb_absent_field_is_error) {\n"
" continue;\n"
" } else {\n"
" LOG(ERROR) << \"No field=\" << it->name << \" (\"\n"
" << value << \") in $msg$\";\n"
" return false;\n"
" }\n"
" }\n"
" if (it->value.type() == ::mcpack2pb::FIELD_ARRAY) {\n"
" ::mcpack2pb::ArrayIterator it2(it->value);\n"
" int i = 0;\n"
" for (; it2 != NULL; ++it2, ++i) {\n"
" ::google::protobuf::Message* sub_msg = NULL;\n"
" if (i < msg->$lcfield$_size()) {\n"
" sub_msg = msg->mutable_$lcfield$(i);\n"
" } else {\n"
" sub_msg = msg->add_$lcfield$();\n"
" }\n"
" if (it2->type() != ::mcpack2pb::FIELD_NULL) {\n"
" if (!(*fn)(sub_msg, *it2)) {\n"
" LOG(ERROR) << \"Fail to set item of \" << it->name;\n"
" return false;\n"
" }\n"
" }\n"
" }\n"
" } else if (it->value.type() == ::mcpack2pb::FIELD_ISOARRAY) {\n"
" LOG(ERROR) << \"Shouldn't be a iso array, name=\" << it->name;\n"
" return false;\n"
" } else {\n"
" LOG(ERROR) << \"Can't add \" << value << \" to repeated $msg$\";\n"
" return false;\n"
" }\n"
" }\n"
" return value.stream()->good();\n"
" } else if (value.type() == ::mcpack2pb::FIELD_ARRAY) {\n"
" ::mcpack2pb::ArrayIterator it(value);\n"
" msg->mutable_$lcfield$()->Reserve(it.item_count());\n"
" for (; it != NULL; ++it) {\n"
" if (it->type() == ::mcpack2pb::FIELD_OBJECT) {\n"
" if (!parse_$vmsg2$_body_internal(msg->add_$lcfield$(), *it)) {\n"
" return false;\n"
" }\n"
" } else {\n"
" LOG(ERROR) << \"Can't add \" << *it << \" to repeated $msg$\";\n"
" return false;\n"
" }\n"
" }\n"
" return value.stream()->good();\n"
" }\n"
, "vmsg2", var_name2
, "msg", cpp_name
, "lcfield", f->lowercase_name());
impl.Print(
" LOG(ERROR) << \"Can't set \" << value << \" to $field$ (repeated $msg2$)\";\n"
" return false;\n"
"}\n"
, "msg2", f->message_type()->full_name()
, "field", f->full_name());
} break;
} // switch
} else {
if (f->is_optional()) {
impl.Print("// optional $type$ $name$ = $number$;\n"
, "type", field_to_string(f)
, "name", f->name()
, "number", butil::string_printf("%d", f->number()));
} else {
impl.Print("// required $type$ $name$ = $number$;\n"
, "type", field_to_string(f)
, "name", f->name()
, "number", butil::string_printf("%d", f->number()));
}
impl.Print(TEMPLATE_OF_SET_FUNC_SIGNATURE()
, "vmsg", var_name
, "lcfield", f->lowercase_name());
switch (f->cpp_type()) {
case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
impl.Print(TEMPLATE_OF_SET_FUNC_BODY(int32)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
impl.Print(TEMPLATE_OF_SET_FUNC_BODY(int64)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
impl.Print(TEMPLATE_OF_SET_FUNC_BODY(uint32)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
impl.Print(TEMPLATE_OF_SET_FUNC_BODY(uint64)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
impl.Print(TEMPLATE_OF_SET_FUNC_BODY(bool)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
impl.Print(TEMPLATE_OF_SET_FUNC_BODY(float)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
impl.Print(TEMPLATE_OF_SET_FUNC_BODY(double)
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
impl.Print(
"{\n"
" static_cast<$msg$*>(msg_base)->set_$lcfield$(static_cast<$enum$>(value.as_int32(\"$enum$\")));\n"
" return value.stream()->good();\n"
"}\n"
, "msg", cpp_name
, "enum", to_cpp_name(butil::EnsureString(f->enum_type()->full_name()))
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
// TODO: Encoding checking/conversion for pb strings(utf8).
impl.Print(
"{\n"
" if (value.type() == ::mcpack2pb::FIELD_STRING) {\n"
" value.as_string(static_cast<$msg$*>(msg_base)->mutable_$lcfield$(), \"$field$\");\n"
" return value.stream()->good();\n"
" } else if (value.type() == ::mcpack2pb::FIELD_BINARY) {\n"
" value.as_binary(static_cast<$msg$*>(msg_base)->mutable_$lcfield$(), \"$field$\");\n"
" return value.stream()->good();\n"
" }\n"
" LOG(ERROR) << \"Can't set \" << value << \" to $field$ (string)\";\n"
" return false;\n"
"}\n"
, "msg", cpp_name
, "field", f->full_name()
, "lcfield", f->lowercase_name());
break;
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
std::string var_name2 = mcpack2pb::to_var_name(butil::EnsureString(f->message_type()->full_name()));
ref_msgs.insert(var_name2);
impl.Print(
"{\n"
" if (value.type() == ::mcpack2pb::FIELD_OBJECT) {\n"
" return parse_$vmsg2$_body_internal(static_cast<$msg$*>(msg_base)->mutable_$lcfield$(), value);\n"
" }\n"
, "vmsg2", var_name2
, "msg", cpp_name
, "lcfield", f->lowercase_name());
impl.Print(
// FIXME: describe type.
" LOG(ERROR) << \"Can't set \" << value << \" to $field$\";\n"
" return false;\n"
"}\n"
, "field", f->full_name());
} break;
} // switch
} // else
}
impl.Print(
"bool parse_$vmsg$_body_internal(\n"
" ::google::protobuf::Message* msg,\n"
" ::mcpack2pb::UnparsedValue& value) {\n"
" ::mcpack2pb::ObjectIterator it(value);\n"
" for (; it != NULL; ++it) {\n"
" ::mcpack2pb::SetFieldFn* fn = g_$vmsg$_fields->seek(it->name);\n"
" if (!fn) {\n"
" if (!FLAGS_mcpack2pb_absent_field_is_error) {\n"
" continue;\n"
" } else {\n"
" LOG(ERROR) << \"No field=\" << it->name << \" (\"\n"
" << it->value << \") in $msg$\";\n"
" return false;\n"
" }\n"
" }\n"
" if (!(*fn)(msg, it->value)) {\n"
" return false;\n"
" }\n"
" }\n"
" return value.stream()->good();\n"
"}\n"
"bool parse_$vmsg$_body(\n"
" ::google::protobuf::Message* msg,\n"
" ::google::protobuf::io::ZeroCopyInputStream* input,\n"
" size_t size) {\n"
" ::mcpack2pb::InputStream mc_stream(input);\n"
" ::mcpack2pb::UnparsedValue value(::mcpack2pb::FIELD_OBJECT, &mc_stream, size);\n"
" if (!parse_$vmsg$_body_internal(msg, value)) {\n"
" return false;\n"
" }\n"
" if (!msg->IsInitialized()) {\n"
" LOG(ERROR) << \"Missing required fields: \" << msg->InitializationErrorString();\n"
" return false;\n"
" }\n"
" return true;\n"
"}\n"
"size_t parse_$vmsg$(\n"
" ::google::protobuf::Message* msg,\n"
" ::google::protobuf::io::ZeroCopyInputStream* input) {\n"
" ::mcpack2pb::InputStream mc_stream(input);\n"
" const size_t value_size = ::mcpack2pb::unbox(&mc_stream);\n"
" if (!value_size) {\n"
" LOG(ERROR) << \"Fail to unbox\";\n"
" return 0;\n"
" }\n"
" ::mcpack2pb::UnparsedValue value(::mcpack2pb::FIELD_OBJECT, &mc_stream, value_size);\n"
" if (!parse_$vmsg$_body_internal(msg, value)) {\n"
" return 0;\n"
" }\n"
" if (!msg->IsInitialized()) {\n"
" LOG(ERROR) << \"Missing required fields: \" << msg->InitializationErrorString();\n"
" return 0;\n"
" }\n"
" return 6/*sizeof(FieldLongHead)*/ + value_size;\n"
"}\n"
, "vmsg", var_name
, "msg", d->full_name());
return !impl.failed();
}
#define TEMPLATE_SERIALIZE_REPEATED(cit, printer, field, strict_cond, looser_cond) \
if (strict_cond) { \
(printer).Print( \
"if (msg.$lcfield$_size()) {\n" \
" if (format == ::mcpack2pb::FORMAT_COMPACK) {\n" \
" serializer.begin_compack_array(\"$field$\", ::mcpack2pb::FIELD_$TYPE$);\n" \
" } else {\n" \
" serializer.begin_mcpack_array(\"$field$\", ::mcpack2pb::FIELD_$TYPE$);\n" \
" }\n" \
, "lcfield", (field)->lowercase_name() \
, "field", get_idl_name(field) \
, "TYPE", to_mcpack_typestr_uppercase(cit, (field))); \
(printer).Print( \
" serializer.add_multiple_$type$(msg.$lcfield$().data(), msg.$lcfield$_size());\n" \
" serializer.end_array();\n" \
"}" \
, "type", to_mcpack_typestr(cit, (field)) \
, "field", get_idl_name(field) \
, "lcfield", (field)->lowercase_name()); \
if ((field)->options().GetExtension(idl_on)) { \
(printer).Print( \
" else {\n" \
" serializer.add_empty_array(\"$field$\");\n" \
"}\n" \
, "field", get_idl_name(field)); \
} else { \
(printer).Print("\n"); \
} \
} else if (looser_cond) { \
(printer).Print( \
"if (msg.$lcfield$_size()) {\n" \
" if (format == ::mcpack2pb::FORMAT_COMPACK) {\n" \
" serializer.begin_compack_array(\"$field$\", ::mcpack2pb::FIELD_$TYPE$);\n" \
" } else {\n" \
" serializer.begin_mcpack_array(\"$field$\", ::mcpack2pb::FIELD_$TYPE$);\n" \
" }\n" \
, "lcfield", (field)->lowercase_name() \
, "field", get_idl_name(field) \
, "TYPE", to_mcpack_typestr_uppercase(cit, (field))); \
(printer).Print( \
" for (int i = 0; i < msg.$lcfield$_size(); ++i) {\n" \
" serializer.add_$type$(msg.$lcfield$(i));\n" \
" }\n" \
" serializer.end_array();\n" \
"}" \
, "type", to_mcpack_typestr(cit, (field)) \
, "field", get_idl_name(field) \
, "lcfield", (field)->lowercase_name()); \
if ((field)->options().GetExtension(idl_on)) { \
(printer).Print( \
" else {\n" \
" serializer.add_empty_array(\"$field$\");\n" \
"}\n" \
, "field", get_idl_name(field)); \
} else { \
(printer).Print("\n"); \
} \
} else { \
if ((field)->type() == google::protobuf::FieldDescriptor::TYPE_ENUM) { \
LOG(ERROR) << "Disallow converting " << (field)->full_name() \
<< " (" << (field)->enum_type()->full_name() << ") to " \
<< to_mcpack_typestr(cit, (field)) << " (idl)"; \
} else { \
LOG(ERROR) << "Disallow converting " << (field)->full_name() \
<< " (" << field_to_string(field) << ") to " \
<< to_mcpack_typestr(cit, (field)) << " (idl)"; \
} \
return false; \
} \
#define TEMPLATE_SERIALIZE_REPEATED_INTEGRAL(cit, printer, field, strict_cond) \
TEMPLATE_SERIALIZE_REPEATED(cit, printer, field, ((cit) == IDL_AUTO || (strict_cond)), \
is_integral_type(cit))
#define TEMPLATE_SERIALIZE(cit, printer, field, cond) \
if (!(cond)) { \
if ((field)->type() == google::protobuf::FieldDescriptor::TYPE_ENUM) { \
LOG(ERROR) << "Disallow converting " << (field)->full_name() \
<< " (" << (field)->enum_type()->full_name() << ") to " \
<< to_mcpack_typestr(cit, (field)) << " (idl)"; \
} else { \
LOG(ERROR) << "Disallow converting " << (field)->full_name() \
<< " (" << field_to_string(field) << ") to " \
<< to_mcpack_typestr(cit, (field)) << " (idl)"; \
} \
return false; \
} \
(printer).Print( \
"if (msg.has_$lcfield$()) {\n" \
" serializer.add_$type$(\"$field$\", msg.$lcfield$());\n" \
"}" \
, "type", to_mcpack_typestr(cit, (field)) \
, "field", get_idl_name(field) \
, "lcfield", (field)->lowercase_name()); \
if ((field)->options().GetExtension(idl_on)) { \
(printer).Print( \
" else {\n" \
" serializer.add_empty_array(\"$field$\");\n" \
"}\n" \
, "field", get_idl_name(field)); \
}
#define TEMPLATE_SERIALIZE_INTEGRAL(cit, printer, field) \
TEMPLATE_SERIALIZE(cit, printer, field, \
((cit) == IDL_AUTO || is_integral_type(cit)))
#define TEMPLATE_INNER_SERIALIZE_REPEATED(msg, cit, printer, field, \
strict_cond, looser_cond) \
if ((strict_cond)) { \
(printer).Print( \
"if ($msg$.$lcfield$_size()) {\n" \
" serializer.begin_compack_array(::mcpack2pb::FIELD_$TYPE$);\n" \
, "msg", msg \
, "TYPE", to_mcpack_typestr_uppercase(cit, (field)) \
, "lcfield", (field)->lowercase_name()); \
(printer).Print( \
" serializer.add_multiple_$type$($msg$.$lcfield$().data(), $msg$.$lcfield$_size());\n" \
" serializer.end_array();\n" \
"}" \
, "msg", msg \
, "type", to_mcpack_typestr(cit, (field)) \
, "lcfield", (field)->lowercase_name()); \
if ((field)->options().GetExtension(idl_on)) { \
(printer).Print( \
" else {\n" \
" serializer.add_empty_array();\n" \
"}\n"); \
} else { \
(printer).Print( \
" else {\n" \
" serializer.add_null();\n" \
"}\n"); \
} \
} else if (looser_cond) { \
(printer).Print( \
"if ($msg$.$lcfield$_size()) {\n" \
" serializer.begin_compack_array(::mcpack2pb::FIELD_$TYPE$);\n" \
, "msg", msg \
, "TYPE", to_mcpack_typestr_uppercase(cit, (field)) \
, "lcfield", (field)->lowercase_name()); \
(printer).Print( \
" for (int j = 0; j < $msg$.$lcfield$_size(); ++j) {\n" \
" serializer.add_$type$($msg$.$lcfield$(j));\n" \
" }\n" \
" serializer.end_array();\n" \
"}" \
, "msg", msg \
, "type", to_mcpack_typestr(cit, (field)) \
, "lcfield", (field)->lowercase_name()); \
if ((field)->options().GetExtension(idl_on)) { \
(printer).Print( \
" else {\n" \
" serializer.add_empty_array();\n" \
"}\n"); \
} else { \
(printer).Print( \
" else {\n" \
" serializer.add_null();\n" \
"}\n"); \
} \
} else { \
if ((field)->type() == google::protobuf::FieldDescriptor::TYPE_ENUM) { \
LOG(ERROR) << "Disallow converting " << (field)->full_name() \
<< " (" << (field)->enum_type()->full_name() << ") to " \
<< to_mcpack_typestr(cit, (field)) << " (idl)"; \
} else { \
LOG(ERROR) << "Disallow converting " << (field)->full_name() \
<< " (" << field_to_string(field) << ") to " \
<< to_mcpack_typestr(cit, (field)) << " (idl)"; \
} \
return false; \
} \
#define TEMPLATE_INNER_SERIALIZE_REPEATED_INTEGRAL(msg, cit, printer, field, strict_cond) \
TEMPLATE_INNER_SERIALIZE_REPEATED(msg, cit, printer, field, ((cit) == IDL_AUTO || (strict_cond)), \
is_integral_type(cit))
#define TEMPLATE_INNER_SERIALIZE(msg, cit, printer, field, cond) \
if (!(cond)) { \
if ((field)->type() == google::protobuf::FieldDescriptor::TYPE_ENUM) { \
LOG(ERROR) << "Disallow converting " << (field)->full_name() \
<< " (" << (field)->enum_type()->full_name() << ") to " \
<< to_mcpack_typestr(cit, (field)) << " (idl)"; \
} else { \
LOG(ERROR) << "Disallow converting " << (field)->full_name() \
<< " (" << field_to_string(field) << ") to " \
<< to_mcpack_typestr(cit, (field)) << " (idl)"; \
} \
return false; \
} \
(printer).Print("if ($msg$.has_$lcfield$()) {\n" \
" serializer.add_$type$($msg$.$lcfield$());\n" \
"} else {\n" \
" serializer.add_null();\n" \
"}\n" \
, "msg", msg \
, "type", to_mcpack_typestr(cit, (field)) \
, "lcfield", (field)->lowercase_name())
#define TEMPLATE_INNER_SERIALIZE_INTEGRAL(msg, cit, printer, field) \
TEMPLATE_INNER_SERIALIZE(msg, cit, printer, field, \
((cit) == IDL_AUTO || is_integral_type(cit)))
static bool generate_serializing(const google::protobuf::Descriptor* d,
std::set<std::string> & ref_msgs,
std::set<std::string> & ref_maps,
google::protobuf::io::Printer & impl) {
std::string var_name = mcpack2pb::to_var_name(butil::EnsureString(d->full_name()));
std::string cpp_name = mcpack2pb::to_cpp_name(butil::EnsureString(d->full_name()));
ref_msgs.insert(var_name);
impl.Print(
"void serialize_$vmsg$_body(\n"
" const ::google::protobuf::Message& msg_base,\n"
" ::mcpack2pb::Serializer& serializer,\n"
" ::mcpack2pb::SerializationFormat format) {\n"
" (void)format; // suppress compiler warning when it's not used\n"
, "vmsg", var_name);
if (d->field_count()) {
impl.Print(
" const $msg$& msg = static_cast<const $msg$&>(msg_base);\n"
, "msg", cpp_name);
} else {
impl.Print(" (void)msg_base; // ^\n"
" (void)serializer; // ^\n");
}
impl.Indent();
for (int i = 0; i < d->field_count(); ++i) {
const google::protobuf::FieldDescriptor* f = d->field(i);
ConvertibleIdlType cit = f->options().GetExtension(idl_type);
// Print the field as comment.
std::string comment_template;
if (cit == IDL_AUTO) {
butil::string_printf(&comment_template,
"// %s $type$ $name$ = $number$;\n",
(f->is_repeated() ? "repeated" :
(f->is_optional() ? "optional" : "required")));
} else {
butil::string_printf(&comment_template,
"// %s $type$ $name$ = $number$ [(idl_type)=%s];\n",
(f->is_repeated() ? "repeated" :
(f->is_optional() ? "optional" : "required")),
describe_idl_type(cit));
}
impl.Print(comment_template.c_str()
, "type", field_to_string(f)
, "name", f->name()
, "number", butil::string_printf("%d", f->number()));
if (f->is_repeated()) {
switch (f->cpp_type()) {
case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
TEMPLATE_SERIALIZE_REPEATED_INTEGRAL(
cit, impl, f, (cit == IDL_INT32 || cit == IDL_UINT32));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
TEMPLATE_SERIALIZE_REPEATED_INTEGRAL(
cit, impl, f, (cit == IDL_INT64 || cit == IDL_UINT64));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
TEMPLATE_SERIALIZE_REPEATED(
cit, impl, f, (cit == IDL_AUTO || cit == IDL_BOOL), false);
break;
case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
TEMPLATE_SERIALIZE_REPEATED(
cit, impl, f,
(cit == IDL_AUTO || cit == IDL_FLOAT), (cit == IDL_DOUBLE));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
TEMPLATE_SERIALIZE_REPEATED(
cit, impl, f,
(cit == IDL_AUTO || cit == IDL_DOUBLE), (cit == IDL_FLOAT));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
if (f->type() == google::protobuf::FieldDescriptor::TYPE_STRING) {
TEMPLATE_SERIALIZE_REPEATED(
cit, impl, f, false, (cit == IDL_AUTO || cit == IDL_STRING));
} else if (f->type() == google::protobuf::FieldDescriptor::TYPE_BYTES) {
TEMPLATE_SERIALIZE_REPEATED(
cit, impl, f, false,
(cit == IDL_AUTO || cit == IDL_BINARY || cit == IDL_STRING));
} else {
LOG(ERROR) << "Unknown pb type=" << f->type();
return false;
}
break;
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
if (cit != IDL_AUTO) {
LOG(ERROR) << "Disallow converting " << f->full_name()
<< " (" << f->message_type()->full_name() << ") to "
<< to_mcpack_typestr(cit, f) << " (idl)";
return false;
}
const google::protobuf::Descriptor* msg2 = f->message_type();
std::string var_name2 = mcpack2pb::to_var_name(butil::EnsureString(msg2->full_name()));
std::string cpp_name2 = mcpack2pb::to_cpp_name(butil::EnsureString(msg2->full_name()));
if (is_map_entry(msg2)) {
ref_maps.insert(var_name2);
impl.Print(
"serializer.begin_object(\"$field$\");\n"
"for (int i = 0; i < msg.$lcfield$_size(); ++i) {\n"
" const $msg2$& pair = msg.$lcfield$(i);\n"
, "field", get_idl_name(f)
, "lcfield", f->lowercase_name()
, "msg2", cpp_name2);
const google::protobuf::FieldDescriptor* value_desc = msg2->field(1);
switch (value_desc->cpp_type()) {
case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
impl.Print(" serializer.add_int32(pair.key(), pair.value());\n");
break;
case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
impl.Print(" serializer.add_uint32(pair.key(), pair.value());\n");
break;
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
impl.Print(" serializer.add_int32(pair.key(), (int32_t)pair.value());\n");
break;
case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
impl.Print(" serializer.add_int64(pair.key(), pair.value());\n");
break;
case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
impl.Print(" serializer.add_uint64(pair.key(), pair.value());\n");
break;
case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
impl.Print(" serializer.add_bool(pair.key(), pair.value());\n");
break;
case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
impl.Print(" serializer.add_float(pair.key(), pair.value());\n");
break;
case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
impl.Print(" serializer.add_double(pair.key(), pair.value());\n");
break;
case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
impl.Print(" serializer.add_string(pair.key(), pair.value());\n");
break;
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
std::string var_name3 = mcpack2pb::to_var_name(
butil::EnsureString(value_desc->message_type()->full_name()));
ref_msgs.insert(var_name3);
impl.Print(
" serializer.begin_object(pair.key());\n"
" serialize_$vmsg3$_body(pair.value(), serializer, format);\n"
" serializer.end_object();\n"
, "vmsg3", var_name3);
} break;
} // switch
impl.Print(
"}\n"
"serializer.end_object();\n");
break;
}
ref_msgs.insert(var_name2);
impl.Print(
"if (format == ::mcpack2pb::FORMAT_MCPACK_V2) {\n"
" if (msg.$lcfield$_size()) {\n"
" serializer.begin_mcpack_array(\"$field$\", ::mcpack2pb::FIELD_OBJECT);\n"
" for (int i = 0; i < msg.$lcfield$_size(); ++i) {\n"
" serializer.begin_object();\n"
" serialize_$vmsg2$_body(msg.$lcfield$(i), serializer, format);\n"
" serializer.end_object();\n"
" }\n"
" serializer.end_array();\n"
" }"
, "field", get_idl_name(f)
, "lcfield", f->lowercase_name()
, "vmsg2", var_name2);
if (f->options().GetExtension(idl_on)) {
impl.Print(
" else {\n"
" serializer.add_empty_array(\"$field$\");\n"
" }\n", "field", get_idl_name(f));
} else {
impl.Print("\n");
}
impl.Print("} else if (msg.$lcfield$_size()) {\n"
, "lcfield", f->lowercase_name());
impl.Indent();
impl.Print("serializer.begin_object(\"$field$\");\n"
, "field", get_idl_name(f));
for (int j = 0; j < msg2->field_count(); ++j) {
const google::protobuf::FieldDescriptor* f2 = msg2->field(j);
ConvertibleIdlType cit2 = f2->options().GetExtension(idl_type);
impl.Print(
"serializer.begin_mcpack_array(\"$f2$\", ::mcpack2pb::FIELD_$TYPE$);\n"
"for (int i = 0; i < msg.$lcfield$_size(); ++i) {\n"
, "f2", get_idl_name(f2)
, "TYPE", (f2->is_repeated() ? "ARRAY" : to_mcpack_typestr_uppercase(cit2, (f2)))
, "lcfield", f->lowercase_name());
impl.Indent();
if (f2->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
if (cit2 != IDL_AUTO) {
LOG(ERROR) << "Disallow converting " << f2->full_name()
<< " (" << f2->message_type()->full_name() << ") to "
<< to_mcpack_typestr(cit2, f2) << " (idl)";
return false;
}
std::string var_name3 = mcpack2pb::to_var_name(butil::EnsureString(f2->message_type()->full_name()));
ref_msgs.insert(var_name3);
if (f2->is_repeated()) {
impl.Print(
"const int $lcfield2$_size = msg.$lcfield$(i).$lcfield2$_size();\n"
"if ($lcfield2$_size) {\n"
" serializer.begin_mcpack_array(::mcpack2pb::FIELD_OBJECT);\n"
" for (int j = 0; j < $lcfield2$_size; ++j) {\n"
" serializer.begin_object();\n"
" serialize_$vmsg3$_body(msg.$lcfield$(i).$lcfield2$(j), serializer, format);\n"
" serializer.end_object();\n"
" }\n"
" serializer.end_array();\n"
"}"
, "vmsg3", var_name3
, "lcfield", f->lowercase_name()
, "lcfield2", f2->lowercase_name());
if (f2->options().GetExtension(idl_on)) {
impl.Print(
" else {\n"
" serializer.add_empty_array();\n"
"}\n");
} else {
impl.Print(
" else {\n"
" serializer.add_null();\n"
"}\n");
}
} else {
impl.Print(
"if (msg.$lcfield$(i).has_$lcfield2$()) {\n"
" serializer.begin_object();\n"
" serialize_$vmsg3$_body(msg.$lcfield$(i).$lcfield2$(), serializer, format);\n"
" serializer.end_object();\n"
"} else {\n"
" serializer.add_null();\n"
"}\n"
, "vmsg3", var_name3
, "lcfield", f->lowercase_name()
, "lcfield2", f2->lowercase_name());
}
} else if (f2->is_repeated()) {
const std::string msgstr = butil::string_printf(
"msg.%s(i)", butil::EnsureString(f->lowercase_name()).c_str());
switch (f2->cpp_type()) {
case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
TEMPLATE_INNER_SERIALIZE_REPEATED_INTEGRAL(
msgstr.c_str(), cit2, impl, f2,
(cit2 == IDL_INT32 || cit2 == IDL_UINT32));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
TEMPLATE_INNER_SERIALIZE_REPEATED_INTEGRAL(
msgstr.c_str(), cit2, impl, f2,
(cit2 == IDL_INT64 || cit2 == IDL_UINT64));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
TEMPLATE_INNER_SERIALIZE_REPEATED(
msgstr.c_str(), cit2, impl, f2,
(cit2 == IDL_AUTO || cit2 == IDL_BOOL), false);
break;
case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
TEMPLATE_INNER_SERIALIZE_REPEATED(
msgstr.c_str(), cit2, impl, f2,
(cit2 == IDL_AUTO || cit2 == IDL_FLOAT), (cit2 == IDL_DOUBLE));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
TEMPLATE_INNER_SERIALIZE_REPEATED(
msgstr.c_str(), cit2, impl, f2,
(cit2 == IDL_AUTO || cit2 == IDL_DOUBLE), (cit2 == IDL_FLOAT));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
if (f2->type() == google::protobuf::FieldDescriptor::TYPE_STRING) {
TEMPLATE_INNER_SERIALIZE_REPEATED(
msgstr.c_str(), cit2, impl, f2, false,
(cit2 == IDL_AUTO || cit2 == IDL_STRING));
} else if (f2->type() == google::protobuf::FieldDescriptor::TYPE_BYTES) {
TEMPLATE_INNER_SERIALIZE_REPEATED(
msgstr.c_str(), cit2, impl, f2, false,
(cit2 == IDL_AUTO || cit2 == IDL_BINARY || cit2 == IDL_STRING));
} else {
LOG(ERROR) << "Unknown pb type=" << f2->type();
return false;
}
break;
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
CHECK(false) << "Impossible";
return false;
}
} else {
const std::string msgstr = butil::string_printf(
"msg.%s(i)", butil::EnsureString(f->lowercase_name()).c_str());
switch (f2->cpp_type()) {
case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
TEMPLATE_INNER_SERIALIZE_INTEGRAL(msgstr.c_str(), cit2, impl, f2);
break;
case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
TEMPLATE_INNER_SERIALIZE(
msgstr.c_str(), cit2, impl, f2,
(cit2 == IDL_AUTO || cit2 == IDL_BOOL));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
TEMPLATE_INNER_SERIALIZE(
msgstr.c_str(), cit2, impl, f2,
(cit2 == IDL_AUTO || cit2 == IDL_FLOAT));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
TEMPLATE_INNER_SERIALIZE(
msgstr.c_str(), cit2, impl, f2,
(cit2 == IDL_AUTO || cit2 == IDL_FLOAT || cit2 == IDL_DOUBLE));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
if (f2->type() == google::protobuf::FieldDescriptor::TYPE_STRING) {
TEMPLATE_INNER_SERIALIZE(
msgstr.c_str(), cit2, impl, f2,
(cit2 == IDL_AUTO || cit2 == IDL_STRING));
} else if (f2->type() == google::protobuf::FieldDescriptor::TYPE_BYTES) {
TEMPLATE_INNER_SERIALIZE(
msgstr.c_str(), cit2, impl, f2,
(cit2 == IDL_AUTO || cit2 == IDL_BINARY || cit2 == IDL_STRING));
} else {
LOG(ERROR) << "Unknown pb type=" << f2->type();
return false;
}
break;
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
LOG(ERROR) << "Impossible";
return false;
}
}
impl.Outdent();
impl.Print("}\n"
"serializer.end_array();\n");
}
impl.Print("serializer.end_object_iso();\n");
impl.Outdent();
impl.Print("} else {\n"
" serializer.begin_object(\"$field$\");\n"
" serializer.end_object_iso();\n"
"}\n"
, "field", get_idl_name(f));
} break;
} // switch
} else {
switch (f->cpp_type()) {
case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
TEMPLATE_SERIALIZE_INTEGRAL(cit, impl, f);
break;
case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
TEMPLATE_SERIALIZE(cit, impl, f,
(cit == IDL_AUTO || cit == IDL_BOOL));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
TEMPLATE_SERIALIZE(cit, impl, f,
(cit == IDL_AUTO || cit == IDL_FLOAT));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
TEMPLATE_SERIALIZE(
cit, impl, f,
(cit == IDL_AUTO || cit == IDL_FLOAT || cit == IDL_DOUBLE));
break;
case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
if (f->type() == google::protobuf::FieldDescriptor::TYPE_STRING) {
TEMPLATE_SERIALIZE(
cit, impl, f, (cit == IDL_AUTO || cit == IDL_STRING));
} else if (f->type() == google::protobuf::FieldDescriptor::TYPE_BYTES) {
TEMPLATE_SERIALIZE(
cit, impl, f,
(cit == IDL_AUTO || cit == IDL_BINARY || cit == IDL_STRING));
} else {
LOG(ERROR) << "Unknown pb type=" << f->type();
return false;
}
break;
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
if (cit != IDL_AUTO) {
LOG(ERROR) << "Disallow converting " << f->full_name()
<< " (" << f->message_type()->full_name() << ") to "
<< to_mcpack_typestr(cit, f) << " (idl)";
return false;
}
std::string var_name2 = mcpack2pb::to_var_name(butil::EnsureString(f->message_type()->full_name()));
ref_msgs.insert(var_name2);
impl.Print("if (msg.has_$lcfield$()) {\n"
" serializer.begin_object(\"$field$\");\n"
" serialize_$vmsg$_body(msg.$lcfield$(), serializer, format);\n"
" serializer.end_object();\n"
"}"
, "field", get_idl_name(f)
, "lcfield", f->lowercase_name()
, "vmsg", var_name2);
} break;
} // switch
if (f->is_required()) {
impl.Print(" else {\n"
" LOG(ERROR) << \"Missing required $field$\";\n"
" return serializer.set_bad();\n"
"}\n", "field", f->full_name());
} else {
impl.Print("\n");
}
} // else
}
impl.Outdent();
impl.Print(
"}\n"
"bool serialize_$vmsg$(\n"
" const ::google::protobuf::Message& msg_base,\n"
" ::google::protobuf::io::ZeroCopyOutputStream* output,\n"
" ::mcpack2pb::SerializationFormat format) {\n"
" ::mcpack2pb::OutputStream ostream(output);\n"
" ::mcpack2pb::Serializer serializer(&ostream);\n"
" serializer.begin_object();\n"
" serialize_$vmsg$_body(msg_base, serializer, format);\n"
" serializer.end_object();\n"
" ostream.done();\n"
" return serializer.good();\n"
"}\n"
, "vmsg", var_name);
return !impl.failed();
}
static std::string protobuf_style_normalize_filename(const std::string & fname) {
std::string norm_fname;
norm_fname.reserve(fname.size() + 10);
for (size_t i = 0; i < fname.size(); ++i) {
if (fname[i] == '_' || isdigit(fname[i]) || isalpha(fname[i])) {
norm_fname.push_back(fname[i]);
} else {
char symbol[4];
snprintf(symbol, sizeof(symbol), "_%02x", (int)fname[i]);
norm_fname.append(symbol, 3);
}
}
return norm_fname;
}
static bool generate_registration(
const google::protobuf::FileDescriptor* file,
google::protobuf::io::Printer & impl) {
const std::string cpp_ns = to_cpp_name(butil::EnsureString(file->package()));
std::string norm_fname = protobuf_style_normalize_filename(butil::EnsureString(file->name()));
impl.Print(
"\n// register all message handlers\n"
"struct RegisterMcpackFunctions_$norm_fname$ {\n"
" RegisterMcpackFunctions_$norm_fname$() {\n"
, "norm_fname", norm_fname);
impl.Indent();
impl.Indent();
for (int i = 0; i < file->message_type_count(); ++i) {
const google::protobuf::Descriptor* d = file->message_type(i);
std::string var_name = mcpack2pb::to_var_name(butil::EnsureString(d->full_name()));
impl.Print(
"\n"
"g_$vmsg$_fields = new ::mcpack2pb::FieldMap;\n"
"CHECK_EQ(0, g_$vmsg$_fields->init(std::max($field_count$, 1), 30));\n"
, "vmsg", var_name
, "field_count", ::butil::string_printf("%d", d->field_count()));
for (int i = 0; i < d->field_count(); ++i) {
const google::protobuf::FieldDescriptor* f = d->field(i);
impl.Print("(*g_$vmsg$_fields)[\"$field$\"] = ::set_$vmsg$_$lcfield$;\n"
, "vmsg", var_name
, "field", get_idl_name(f)
, "lcfield", f->lowercase_name());
}
impl.Print(
"::mcpack2pb::MessageHandler $vmsg$_handler = {\n"
" parse_$vmsg$,\n"
" parse_$vmsg$_body,\n"
" serialize_$vmsg$,\n"
" serialize_$vmsg$_body\n"
"};\n"
"::mcpack2pb::register_message_handler_or_die(\"$fmsg$\", $vmsg$_handler);\n"
, "fmsg", d->full_name()
, "vmsg", var_name);
}
impl.Outdent();
impl.Outdent();
impl.Print(" }\n"
"} static_init_mcpack_$suffix$;\n"
, "suffix", norm_fname);
return !impl.failed();
}
class McpackToProtobuf : public google::protobuf::compiler::CodeGenerator {
public:
bool Generate(const google::protobuf::FileDescriptor* file,
const std::string& parameter,
google::protobuf::compiler::GeneratorContext*,
std::string* error) const override;
};
bool McpackToProtobuf::Generate(const google::protobuf::FileDescriptor* file,
const std::string& /*parameter*/,
google::protobuf::compiler::GeneratorContext* ctx,
std::string* error) const {
if (!file->options().GetExtension(idl_support)) {
// skip the file.
return true;
}
std::string cpp_name = butil::EnsureString(file->name());
const size_t pos = cpp_name.find_last_of('.');
if (pos == std::string::npos) {
::butil::string_printf(error, "Bad filename=%s", cpp_name.c_str());
return false;
}
cpp_name.resize(pos);
cpp_name.append(".pb.cc");
google::protobuf::io::Printer inc_printer(
ctx->OpenForInsert(cpp_name, "includes"), '$');
inc_printer.Print("#include <butil/logging.h>\n"
"#include <mcpack2pb/mcpack2pb.h>\n"
"#include <gflags/gflags.h>\n");
google::protobuf::io::Printer gdecl_printer(
ctx->OpenForInsert(cpp_name, "includes"), '$');
google::protobuf::io::Printer gimpl_printer(
ctx->OpenForInsert(cpp_name, "global_scope"), '$');
gdecl_printer.Print(
"\n// ==== declarations generated by brpc/mcpack2pb/protoc-gen-mcpack ====\n"
"DECLARE_bool(mcpack2pb_absent_field_is_error);\n");
std::set<std::string> ref_msgs;
std::set<std::string> ref_maps;
for (int i = 0; i < file->message_type_count(); ++i) {
const google::protobuf::Descriptor* d = file->message_type(i);
if (!generate_parsing(d, ref_msgs, ref_maps, gimpl_printer)) {
::butil::string_printf(
error, "Fail to generate parsing code for %s",
butil::EnsureString(d->full_name()).c_str());
return false;
}
if (!generate_serializing(d, ref_msgs, ref_maps, gimpl_printer)) {
::butil::string_printf(
error, "Fail to generate serializing code for %s",
butil::EnsureString(d->full_name()).c_str());
return false;
}
std::string var_name = mcpack2pb::to_var_name(butil::EnsureString(d->full_name()));
gdecl_printer.Print(
"::mcpack2pb::FieldMap* g_$vmsg$_fields = NULL;\n"
, "vmsg", var_name);
}
if (!generate_declarations(ref_msgs, ref_maps, gdecl_printer)) {
::butil::string_printf(
error, "Fail to generate declarations for %s",
cpp_name.c_str());
return false;
}
if (!generate_registration(file, gimpl_printer)) {
::butil::string_printf(
error, "Fail to generate registration code for %s",
cpp_name.c_str());
return false;
}
return true;
}
} // namespace mcpack2pb
int main(int argc, char* argv[]) {
::mcpack2pb::McpackToProtobuf generator;
return google::protobuf::compiler::PluginMain(argc, argv, &generator);
}