// Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. // https://developers.google.com/protocol-buffers/ // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Author: kenton@google.com (Kenton Varda) // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace google { namespace protobuf { namespace compiler { namespace cpp { namespace { static const char kAnyMessageName[] = "Any"; static const char kAnyProtoFile[] = "google/protobuf/any.proto"; static const char kGoogleProtobufPrefix[] = "google/protobuf/"; string DotsToUnderscores(const string& name) { return StringReplace(name, ".", "_", true); } string DotsToColons(const string& name) { return StringReplace(name, ".", "::", true); } const char* const kKeywordList[] = { "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "class", "compl", "const", "constexpr", "const_cast", "continue", "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", "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" }; std::unordered_set MakeKeywordsMap() { std::unordered_set result; for (int i = 0; i < GOOGLE_ARRAYSIZE(kKeywordList); i++) { result.insert(kKeywordList[i]); } return result; } std::unordered_set kKeywords = MakeKeywordsMap(); // Returns whether the provided descriptor has an extension. This includes its // nested types. bool HasExtension(const Descriptor* descriptor) { if (descriptor->extension_count() > 0) { return true; } for (int i = 0; i < descriptor->nested_type_count(); ++i) { if (HasExtension(descriptor->nested_type(i))) { return true; } } return false; } // Encode [0..63] as 'A'-'Z', 'a'-'z', '0'-'9', '_' char Base63Char(int value) { GOOGLE_CHECK_GE(value, 0); if (value < 26) return 'A' + value; value -= 26; if (value < 26) return 'a' + value; value -= 26; if (value < 10) return '0' + value; GOOGLE_CHECK_EQ(value, 10); return '_'; } // Given a c identifier has 63 legal characters we can't implement base64 // encoding. So we return the k least significant "digits" in base 63. template string Base63(I n, int k) { string res; while (k-- > 0) { res += Base63Char(static_cast(n % 63)); n /= 63; } return res; } string IntTypeName(const Options& options, const string& type) { if (options.opensource_runtime) { return "::google::protobuf::" + type; } else { return "::" + type; } } string StringTypeName(const Options& options) { return options.opensource_runtime ? "::std::string" : "::std::string"; } void SetIntVar(const Options& options, const string& type, std::map* variables) { (*variables)[type] = IntTypeName(options, type); } } // namespace void SetCommonVars(const Options& options, std::map* variables) { (*variables)["proto_ns"] = ProtobufNamespace(options); // Warning: there is some clever naming/splitting here to avoid extract script // rewrites. The names of these variables must not be things that the extract // script will rewrite. That's why we use "CHK" (for example) instead of // "GOOGLE_CHECK". if (options.opensource_runtime) { (*variables)["GOOGLE_PROTOBUF"] = "GOOGLE_PROTOBUF"; (*variables)["CHK"] = "GOOGLE_CHECK"; (*variables)["DCHK"] = "GOOGLE_DCHECK"; } else { // These values are things the extract script would rewrite if we did not // split them. It might not strictly matter since we don't generate google3 // code in open-source. But it's good to prevent surprising things from // happening. (*variables)["GOOGLE_PROTOBUF"] = "GOOGLE3" "_PROTOBUF"; (*variables)["CHK"] = "CH" "ECK"; (*variables)["DCHK"] = "DCH" "ECK"; } SetIntVar(options, "uint8", variables); SetIntVar(options, "uint32", variables); SetIntVar(options, "uint64", variables); SetIntVar(options, "int32", variables); SetIntVar(options, "int64", variables); (*variables)["string"] = StringTypeName(options); } string UnderscoresToCamelCase(const string& input, bool cap_next_letter) { string result; // Note: I distrust ctype.h due to locales. for (int i = 0; i < input.size(); i++) { if ('a' <= input[i] && input[i] <= 'z') { if (cap_next_letter) { result += input[i] + ('A' - 'a'); } else { result += input[i]; } cap_next_letter = false; } else if ('A' <= input[i] && input[i] <= 'Z') { // Capital letters are left as-is. result += input[i]; cap_next_letter = false; } else if ('0' <= input[i] && input[i] <= '9') { result += input[i]; cap_next_letter = true; } else { cap_next_letter = true; } } return result; } const char kThickSeparator[] = "// ===================================================================\n"; const char kThinSeparator[] = "// -------------------------------------------------------------------\n"; bool CanInitializeByZeroing(const FieldDescriptor* field) { if (field->is_repeated() || field->is_extension()) return false; switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_ENUM: return field->default_value_enum()->number() == 0; case FieldDescriptor::CPPTYPE_INT32: return field->default_value_int32() == 0; case FieldDescriptor::CPPTYPE_INT64: return field->default_value_int64() == 0; case FieldDescriptor::CPPTYPE_UINT32: return field->default_value_uint32() == 0; case FieldDescriptor::CPPTYPE_UINT64: return field->default_value_uint64() == 0; case FieldDescriptor::CPPTYPE_FLOAT: return field->default_value_float() == 0; case FieldDescriptor::CPPTYPE_DOUBLE: return field->default_value_double() == 0; case FieldDescriptor::CPPTYPE_BOOL: return field->default_value_bool() == false; default: return false; } } string ClassName(const Descriptor* descriptor) { const Descriptor* parent = descriptor->containing_type(); string res; if (parent) res += ClassName(parent) + "_"; res += descriptor->name(); if (IsMapEntryMessage(descriptor)) res += "_DoNotUse"; return res; } string ClassName(const EnumDescriptor* enum_descriptor) { if (enum_descriptor->containing_type() == NULL) { return enum_descriptor->name(); } else { return ClassName(enum_descriptor->containing_type()) + "_" + enum_descriptor->name(); } } string QualifiedClassName(const Descriptor* d) { return Namespace(d) + "::" + ClassName(d); } string QualifiedClassName(const EnumDescriptor* d) { return Namespace(d) + "::" + ClassName(d); } string Namespace(const string& package) { if (package.empty()) return ""; return "::" + DotsToColons(package); } string Namespace(const Descriptor* d) { return Namespace(d->file()); } string Namespace(const FieldDescriptor* d) { return Namespace(d->file()); } string Namespace(const EnumDescriptor* d) { return Namespace(d->file()); } string DefaultInstanceName(const Descriptor* descriptor) { string prefix = descriptor->file()->package().empty() ? "" : "::"; return prefix + DotsToColons(descriptor->file()->package()) + "::_" + ClassName(descriptor, false) + "_default_instance_"; } string ReferenceFunctionName(const Descriptor* descriptor) { return QualifiedClassName(descriptor) + "_ReferenceStrong"; } string SuperClassName(const Descriptor* descriptor, const Options& options) { return "::" + ProtobufNamespace(options) + (HasDescriptorMethods(descriptor->file(), options) ? "::Message" : "::MessageLite"); } string FieldName(const FieldDescriptor* field) { string result = field->name(); LowerString(&result); if (kKeywords.count(result) > 0) { result.append("_"); } return result; } string EnumValueName(const EnumValueDescriptor* enum_value) { string result = enum_value->name(); if (kKeywords.count(result) > 0) { result.append("_"); } return result; } int EstimateAlignmentSize(const FieldDescriptor* field) { if (field == NULL) return 0; if (field->is_repeated()) return 8; switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_BOOL: return 1; case FieldDescriptor::CPPTYPE_INT32: case FieldDescriptor::CPPTYPE_UINT32: case FieldDescriptor::CPPTYPE_ENUM: case FieldDescriptor::CPPTYPE_FLOAT: return 4; case FieldDescriptor::CPPTYPE_INT64: case FieldDescriptor::CPPTYPE_UINT64: case FieldDescriptor::CPPTYPE_DOUBLE: case FieldDescriptor::CPPTYPE_STRING: case FieldDescriptor::CPPTYPE_MESSAGE: return 8; } GOOGLE_LOG(FATAL) << "Can't get here."; return -1; // Make compiler happy. } string FieldConstantName(const FieldDescriptor *field) { string field_name = UnderscoresToCamelCase(field->name(), true); string result = "k" + field_name + "FieldNumber"; if (!field->is_extension() && field->containing_type()->FindFieldByCamelcaseName( field->camelcase_name()) != field) { // This field's camelcase name is not unique. As a hack, add the field // number to the constant name. This makes the constant rather useless, // but what can we do? result += "_" + SimpleItoa(field->number()); } return result; } string FieldMessageTypeName(const FieldDescriptor* field) { // Note: The Google-internal version of Protocol Buffers uses this function // as a hook point for hacks to support legacy code. return ClassName(field->message_type(), true); } string StripProto(const string& filename) { if (HasSuffixString(filename, ".protodevel")) { return StripSuffixString(filename, ".protodevel"); } else { return StripSuffixString(filename, ".proto"); } } const char* PrimitiveTypeName(FieldDescriptor::CppType type) { switch (type) { case FieldDescriptor::CPPTYPE_INT32 : return "::google::protobuf::int32"; case FieldDescriptor::CPPTYPE_INT64 : return "::google::protobuf::int64"; case FieldDescriptor::CPPTYPE_UINT32 : return "::google::protobuf::uint32"; case FieldDescriptor::CPPTYPE_UINT64 : return "::google::protobuf::uint64"; case FieldDescriptor::CPPTYPE_DOUBLE : return "double"; case FieldDescriptor::CPPTYPE_FLOAT : return "float"; case FieldDescriptor::CPPTYPE_BOOL : return "bool"; case FieldDescriptor::CPPTYPE_ENUM : return "int"; case FieldDescriptor::CPPTYPE_STRING : return "::std::string"; case FieldDescriptor::CPPTYPE_MESSAGE: return NULL; // No default because we want the compiler to complain if any new // CppTypes are added. } GOOGLE_LOG(FATAL) << "Can't get here."; return NULL; } string PrimitiveTypeName(const Options& options, FieldDescriptor::CppType type) { switch (type) { case FieldDescriptor::CPPTYPE_INT32: return IntTypeName(options, "int32"); case FieldDescriptor::CPPTYPE_INT64: return IntTypeName(options, "int64"); case FieldDescriptor::CPPTYPE_UINT32: return IntTypeName(options, "uint32"); case FieldDescriptor::CPPTYPE_UINT64: return IntTypeName(options, "uint64"); case FieldDescriptor::CPPTYPE_DOUBLE: return "double"; case FieldDescriptor::CPPTYPE_FLOAT: return "float"; case FieldDescriptor::CPPTYPE_BOOL: return "bool"; case FieldDescriptor::CPPTYPE_ENUM: return "int"; case FieldDescriptor::CPPTYPE_STRING: return StringTypeName(options); case FieldDescriptor::CPPTYPE_MESSAGE: return ""; // No default because we want the compiler to complain if any new // CppTypes are added. } GOOGLE_LOG(FATAL) << "Can't get here."; return ""; } const char* DeclaredTypeMethodName(FieldDescriptor::Type type) { switch (type) { case FieldDescriptor::TYPE_INT32 : return "Int32"; case FieldDescriptor::TYPE_INT64 : return "Int64"; case FieldDescriptor::TYPE_UINT32 : return "UInt32"; case FieldDescriptor::TYPE_UINT64 : return "UInt64"; case FieldDescriptor::TYPE_SINT32 : return "SInt32"; case FieldDescriptor::TYPE_SINT64 : return "SInt64"; case FieldDescriptor::TYPE_FIXED32 : return "Fixed32"; case FieldDescriptor::TYPE_FIXED64 : return "Fixed64"; case FieldDescriptor::TYPE_SFIXED32: return "SFixed32"; case FieldDescriptor::TYPE_SFIXED64: return "SFixed64"; case FieldDescriptor::TYPE_FLOAT : return "Float"; case FieldDescriptor::TYPE_DOUBLE : return "Double"; case FieldDescriptor::TYPE_BOOL : return "Bool"; case FieldDescriptor::TYPE_ENUM : return "Enum"; case FieldDescriptor::TYPE_STRING : return "String"; case FieldDescriptor::TYPE_BYTES : return "Bytes"; case FieldDescriptor::TYPE_GROUP : return "Group"; case FieldDescriptor::TYPE_MESSAGE : return "Message"; // No default because we want the compiler to complain if any new // types are added. } GOOGLE_LOG(FATAL) << "Can't get here."; return ""; } string Int32ToString(int number) { if (number == kint32min) { // This needs to be special-cased, see explanation here: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52661 return SimpleItoa(number + 1) + " - 1"; } else { return SimpleItoa(number); } } string Int64ToString(const string& macro_prefix, int64 number) { if (number == kint64min) { // This needs to be special-cased, see explanation here: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52661 return macro_prefix + "_LONGLONG(" + SimpleItoa(number + 1) + ") - 1"; } return macro_prefix + "_LONGLONG(" + SimpleItoa(number) + ")"; } string UInt64ToString(const string& macro_prefix, uint64 number) { return macro_prefix + "_ULONGLONG(" + SimpleItoa(number) + ")"; } string DefaultValue(const FieldDescriptor* field) { switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_INT64: return Int64ToString("GG", field->default_value_int64()); case FieldDescriptor::CPPTYPE_UINT64: return UInt64ToString("GG", field->default_value_uint64()); default: return DefaultValue(Options(), field); } } string DefaultValue(const Options& options, const FieldDescriptor* field) { switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_INT32: return Int32ToString(field->default_value_int32()); case FieldDescriptor::CPPTYPE_UINT32: return SimpleItoa(field->default_value_uint32()) + "u"; case FieldDescriptor::CPPTYPE_INT64: return Int64ToString("PROTOBUF", field->default_value_int64()); case FieldDescriptor::CPPTYPE_UINT64: return UInt64ToString("PROTOBUF", field->default_value_uint64()); case FieldDescriptor::CPPTYPE_DOUBLE: { double value = field->default_value_double(); if (value == std::numeric_limits::infinity()) { return "std::numeric_limits::infinity()"; } else if (value == -std::numeric_limits::infinity()) { return "-std::numeric_limits::infinity()"; } else if (value != value) { return "std::numeric_limits::quiet_NaN()"; } else { return SimpleDtoa(value); } } case FieldDescriptor::CPPTYPE_FLOAT: { float value = field->default_value_float(); if (value == std::numeric_limits::infinity()) { return "std::numeric_limits::infinity()"; } else if (value == -std::numeric_limits::infinity()) { return "-std::numeric_limits::infinity()"; } else if (value != value) { return "std::numeric_limits::quiet_NaN()"; } else { string float_value = SimpleFtoa(value); // If floating point value contains a period (.) or an exponent // (either E or e), then append suffix 'f' to make it a float // literal. if (float_value.find_first_of(".eE") != string::npos) { float_value.push_back('f'); } return float_value; } } case FieldDescriptor::CPPTYPE_BOOL: return field->default_value_bool() ? "true" : "false"; case FieldDescriptor::CPPTYPE_ENUM: // Lazy: Generate a static_cast because we don't have a helper function // that constructs the full name of an enum value. return strings::Substitute( "static_cast< $0 >($1)", ClassName(field->enum_type(), true), Int32ToString(field->default_value_enum()->number())); case FieldDescriptor::CPPTYPE_STRING: return "\"" + EscapeTrigraphs( CEscape(field->default_value_string())) + "\""; case FieldDescriptor::CPPTYPE_MESSAGE: return "*" + FieldMessageTypeName(field) + "::internal_default_instance()"; } // Can't actually get here; make compiler happy. (We could add a default // case above but then we wouldn't get the nice compiler warning when a // new type is added.) GOOGLE_LOG(FATAL) << "Can't get here."; return ""; } // Convert a file name into a valid identifier. string FilenameIdentifier(const string& filename) { string result; for (int i = 0; i < filename.size(); i++) { if (ascii_isalnum(filename[i])) { result.push_back(filename[i]); } else { // Not alphanumeric. To avoid any possibility of name conflicts we // use the hex code for the character. StrAppend(&result, "_", strings::Hex(static_cast(filename[i]))); } } return result; } string UniqueName(const string& name, const string& filename, const Options& options) { return name + "_" + FilenameIdentifier(filename); } // Return the qualified C++ name for a file level symbol. string QualifiedFileLevelSymbol(const string& package, const string& name) { if (package.empty()) { return StrCat("::", name); } return StrCat("::", DotsToColons(package), "::", name); } // Escape C++ trigraphs by escaping question marks to \? string EscapeTrigraphs(const string& to_escape) { return StringReplace(to_escape, "?", "\\?", true); } // Escaped function name to eliminate naming conflict. string SafeFunctionName(const Descriptor* descriptor, const FieldDescriptor* field, const string& prefix) { // Do not use FieldName() since it will escape keywords. string name = field->name(); LowerString(&name); string function_name = prefix + name; if (descriptor->FindFieldByName(function_name)) { // Single underscore will also make it conflicting with the private data // member. We use double underscore to escape function names. function_name.append("__"); } else if (kKeywords.count(name) > 0) { // If the field name is a keyword, we append the underscore back to keep it // consistent with other function names. function_name.append("_"); } return function_name; } static bool HasLazyFields(const Descriptor* descriptor, const Options& options) { for (int field_idx = 0; field_idx < descriptor->field_count(); field_idx++) { if (IsLazy(descriptor->field(field_idx), options)) { return true; } } for (int idx = 0; idx < descriptor->extension_count(); idx++) { if (IsLazy(descriptor->extension(idx), options)) { return true; } } for (int idx = 0; idx < descriptor->nested_type_count(); idx++) { if (HasLazyFields(descriptor->nested_type(idx), options)) { return true; } } return false; } // Does the given FileDescriptor use lazy fields? bool HasLazyFields(const FileDescriptor* file, const Options& options) { for (int i = 0; i < file->message_type_count(); i++) { const Descriptor* descriptor(file->message_type(i)); if (HasLazyFields(descriptor, options)) { return true; } } for (int field_idx = 0; field_idx < file->extension_count(); field_idx++) { if (IsLazy(file->extension(field_idx), options)) { return true; } } return false; } static bool HasRepeatedFields(const Descriptor* descriptor) { for (int i = 0; i < descriptor->field_count(); ++i) { if (descriptor->field(i)->label() == FieldDescriptor::LABEL_REPEATED) { return true; } } for (int i = 0; i < descriptor->nested_type_count(); ++i) { if (HasRepeatedFields(descriptor->nested_type(i))) return true; } return false; } bool HasRepeatedFields(const FileDescriptor* file) { for (int i = 0; i < file->message_type_count(); ++i) { if (HasRepeatedFields(file->message_type(i))) return true; } return false; } static bool IsStringPieceField(const FieldDescriptor* field, const Options& options) { return field->cpp_type() == FieldDescriptor::CPPTYPE_STRING && EffectiveStringCType(field, options) == FieldOptions::STRING_PIECE; } static bool HasStringPieceFields(const Descriptor* descriptor, const Options& options) { for (int i = 0; i < descriptor->field_count(); ++i) { if (IsStringPieceField(descriptor->field(i), options)) return true; } for (int i = 0; i < descriptor->nested_type_count(); ++i) { if (HasStringPieceFields(descriptor->nested_type(i), options)) return true; } return false; } bool HasStringPieceFields(const FileDescriptor* file, const Options& options) { for (int i = 0; i < file->message_type_count(); ++i) { if (HasStringPieceFields(file->message_type(i), options)) return true; } return false; } static bool IsCordField(const FieldDescriptor* field, const Options& options) { return field->cpp_type() == FieldDescriptor::CPPTYPE_STRING && EffectiveStringCType(field, options) == FieldOptions::CORD; } static bool HasCordFields(const Descriptor* descriptor, const Options& options) { for (int i = 0; i < descriptor->field_count(); ++i) { if (IsCordField(descriptor->field(i), options)) return true; } for (int i = 0; i < descriptor->nested_type_count(); ++i) { if (HasCordFields(descriptor->nested_type(i), options)) return true; } return false; } bool HasCordFields(const FileDescriptor* file, const Options& options) { for (int i = 0; i < file->message_type_count(); ++i) { if (HasCordFields(file->message_type(i), options)) return true; } return false; } static bool HasExtensionsOrExtendableMessage(const Descriptor* descriptor) { if (descriptor->extension_range_count() > 0) return true; if (descriptor->extension_count() > 0) return true; for (int i = 0; i < descriptor->nested_type_count(); ++i) { if (HasExtensionsOrExtendableMessage(descriptor->nested_type(i))) { return true; } } return false; } bool HasExtensionsOrExtendableMessage(const FileDescriptor* file) { if (file->extension_count() > 0) return true; for (int i = 0; i < file->message_type_count(); ++i) { if (HasExtensionsOrExtendableMessage(file->message_type(i))) return true; } return false; } static bool HasMapFields(const Descriptor* descriptor) { for (int i = 0; i < descriptor->field_count(); ++i) { if (descriptor->field(i)->is_map()) { return true; } } for (int i = 0; i < descriptor->nested_type_count(); ++i) { if (HasMapFields(descriptor->nested_type(i))) return true; } return false; } bool HasMapFields(const FileDescriptor* file) { for (int i = 0; i < file->message_type_count(); ++i) { if (HasMapFields(file->message_type(i))) return true; } return false; } static bool HasEnumDefinitions(const Descriptor* message_type) { if (message_type->enum_type_count() > 0) return true; for (int i = 0; i < message_type->nested_type_count(); ++i) { if (HasEnumDefinitions(message_type->nested_type(i))) return true; } return false; } bool HasEnumDefinitions(const FileDescriptor* file) { if (file->enum_type_count() > 0) return true; for (int i = 0; i < file->message_type_count(); ++i) { if (HasEnumDefinitions(file->message_type(i))) return true; } return false; } bool IsStringOrMessage(const FieldDescriptor* field) { switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_INT32: case FieldDescriptor::CPPTYPE_INT64: case FieldDescriptor::CPPTYPE_UINT32: case FieldDescriptor::CPPTYPE_UINT64: case FieldDescriptor::CPPTYPE_DOUBLE: case FieldDescriptor::CPPTYPE_FLOAT: case FieldDescriptor::CPPTYPE_BOOL: case FieldDescriptor::CPPTYPE_ENUM: return false; case FieldDescriptor::CPPTYPE_STRING: case FieldDescriptor::CPPTYPE_MESSAGE: return true; } GOOGLE_LOG(FATAL) << "Can't get here."; return false; } FieldOptions::CType EffectiveStringCType(const FieldDescriptor* field, const Options& options) { GOOGLE_DCHECK(field->cpp_type() == FieldDescriptor::CPPTYPE_STRING); if (options.opensource_runtime) { // Open-source protobuf release only supports STRING ctype. return FieldOptions::STRING; } else { // Google-internal supports all ctypes. return field->options().ctype(); } } bool IsAnyMessage(const FileDescriptor* descriptor) { return descriptor->name() == kAnyProtoFile; } bool IsAnyMessage(const Descriptor* descriptor) { return descriptor->name() == kAnyMessageName && descriptor->file()->name() == kAnyProtoFile; } bool IsWellKnownMessage(const FileDescriptor* descriptor) { return !descriptor->name().compare(0, 16, kGoogleProtobufPrefix); } enum Utf8CheckMode { STRICT = 0, // Parsing will fail if non UTF-8 data is in string fields. VERIFY = 1, // Only log an error but parsing will succeed. NONE = 2, // No UTF-8 check. }; static bool FieldEnforceUtf8(const FieldDescriptor* field, const Options& options) { return true; } static bool FileUtf8Verification(const FileDescriptor* file, const Options& options) { return true; } // Which level of UTF-8 enforcemant is placed on this file. static Utf8CheckMode GetUtf8CheckMode(const FieldDescriptor* field, const Options& options) { if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 && FieldEnforceUtf8(field, options)) { return STRICT; } else if (GetOptimizeFor(field->file(), options) != FileOptions::LITE_RUNTIME && FileUtf8Verification(field->file(), options)) { return VERIFY; } else { return NONE; } } string GetUtf8Suffix(const FieldDescriptor* field, const Options& options) { switch (GetUtf8CheckMode(field, options)) { case STRICT: return "UTF8"; case VERIFY: return "UTF8Verify"; case NONE: default: // Some build configs warn on missing return without default. return ""; } } static void GenerateUtf8CheckCode(const FieldDescriptor* field, const Options& options, bool for_parse, const char* parameters, const char* strict_function, const char* verify_function, const Formatter& format) { switch (GetUtf8CheckMode(field, options)) { case STRICT: { if (for_parse) { format("DO_("); } format("::$proto_ns$::internal::WireFormatLite::$1$(\n", strict_function); format.Indent(); format(parameters); if (for_parse) { format("::$proto_ns$::internal::WireFormatLite::PARSE,\n"); } else { format("::$proto_ns$::internal::WireFormatLite::SERIALIZE,\n"); } format("\"$1$\")", field->full_name()); if (for_parse) { format(")"); } format(";\n"); format.Outdent(); break; } case VERIFY: { format("::$proto_ns$::internal::WireFormat::$1$(\n", verify_function); format.Indent(); format(parameters); if (for_parse) { format("::$proto_ns$::internal::WireFormat::PARSE,\n"); } else { format("::$proto_ns$::internal::WireFormat::SERIALIZE,\n"); } format("\"$1$\");\n", field->full_name()); format.Outdent(); break; } case NONE: break; } } void GenerateUtf8CheckCodeForString(const FieldDescriptor* field, const Options& options, bool for_parse, const char* parameters, const Formatter& format) { GenerateUtf8CheckCode(field, options, for_parse, parameters, "VerifyUtf8String", "VerifyUTF8StringNamedField", format); } void GenerateUtf8CheckCodeForCord(const FieldDescriptor* field, const Options& options, bool for_parse, const char* parameters, const Formatter& format) { GenerateUtf8CheckCode(field, options, for_parse, parameters, "VerifyUtf8Cord", "VerifyUTF8CordNamedField", format); } namespace { void Flatten(const Descriptor* descriptor, std::vector* flatten) { for (int i = 0; i < descriptor->nested_type_count(); i++) Flatten(descriptor->nested_type(i), flatten); flatten->push_back(descriptor); } } // namespace void FlattenMessagesInFile(const FileDescriptor* file, std::vector* result) { for (int i = 0; i < file->message_type_count(); i++) { Flatten(file->message_type(i), result); } } bool HasWeakFields(const Descriptor* descriptor, const Options& options) { for (int i = 0; i < descriptor->field_count(); i++) { if (IsWeak(descriptor->field(i), options)) return true; } return false; } bool HasWeakFields(const FileDescriptor* file, const Options& options) { for (int i = 0; i < file->message_type_count(); ++i) { if (HasWeakFields(file->message_type(i), options)) return true; } return false; } bool UsingImplicitWeakFields(const FileDescriptor* file, const Options& options) { return options.lite_implicit_weak_fields && GetOptimizeFor(file, options) == FileOptions::LITE_RUNTIME; } bool IsImplicitWeakField(const FieldDescriptor* field, const Options& options, MessageSCCAnalyzer* scc_analyzer) { return UsingImplicitWeakFields(field->file(), options) && field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_required() && !field->is_map() && field->containing_oneof() == NULL && !IsWellKnownMessage(field->message_type()->file()) && // We do not support implicit weak fields between messages in the same // strongly-connected component. scc_analyzer->GetSCC(field->containing_type()) != scc_analyzer->GetSCC(field->message_type()); } MessageAnalysis MessageSCCAnalyzer::GetSCCAnalysis(const SCC* scc) { if (analysis_cache_.count(scc)) return analysis_cache_[scc]; MessageAnalysis result{}; for (int i = 0; i < scc->descriptors.size(); i++) { const Descriptor* descriptor = scc->descriptors[i]; if (descriptor->extension_range_count() > 0) { result.contains_extension = true; // Extensions are found by looking up default_instance and extension // number in a map. So you'd maybe expect here // result.constructor_requires_initialization = true; // However the extension registration mechanism already makes sure // the default will be initialized. } for (int i = 0; i < descriptor->field_count(); i++) { const FieldDescriptor* field = descriptor->field(i); if (field->is_required()) { result.contains_required = true; } switch (field->type()) { case FieldDescriptor::TYPE_STRING: case FieldDescriptor::TYPE_BYTES: { result.constructor_requires_initialization = true; if (field->options().ctype() == FieldOptions::CORD) { result.contains_cord = true; } break; } case FieldDescriptor::TYPE_GROUP: case FieldDescriptor::TYPE_MESSAGE: { result.constructor_requires_initialization = true; const SCC* child = analyzer_.GetSCC(field->message_type()); if (child != scc) { MessageAnalysis analysis = GetSCCAnalysis(child); result.contains_cord |= analysis.contains_cord; result.contains_extension |= analysis.contains_extension; if (!ShouldIgnoreRequiredFieldCheck(field, options_)) { result.contains_required |= analysis.contains_required; } } else { // This field points back into the same SCC hence the messages // in the SCC are recursive. Note if SCC contains more than two // nodes it has to be recursive, however this test also works for // a single node that is recursive. result.is_recursive = true; } break; } default: break; } } } // We deliberately only insert the result here. After we contracted the SCC // in the graph, the graph should be a DAG. Hence we shouldn't need to mark // nodes visited as we can never return to them. By inserting them here // we will go in an infinite loop if the SCC is not correct. return analysis_cache_[scc] = result; } void ListAllFields(const Descriptor* d, std::vector* fields) { // Collect sub messages for (int i = 0; i < d->nested_type_count(); i++) { ListAllFields(d->nested_type(i), fields); } // Collect message level extensions. for (int i = 0; i < d->extension_count(); i++) { fields->push_back(d->extension(i)); } // Add types of fields necessary for (int i = 0; i < d->field_count(); i++) { fields->push_back(d->field(i)); } } void ListAllFields(const FileDescriptor* d, std::vector* fields) { // Collect file level message. for (int i = 0; i < d->message_type_count(); i++) { ListAllFields(d->message_type(i), fields); } // Collect message level extensions. for (int i = 0; i < d->extension_count(); i++) { fields->push_back(d->extension(i)); } } void ListAllTypesForServices(const FileDescriptor* fd, std::vector* types) { for (int i = 0; i < fd->service_count(); i++) { const ServiceDescriptor* sd = fd->service(i); for (int j = 0; j < sd->method_count(); j++) { const MethodDescriptor* method = sd->method(j); types->push_back(method->input_type()); types->push_back(method->output_type()); } } } bool GetBootstrapBasename(const Options& options, const string& basename, string* bootstrap_basename) { if (options.opensource_runtime || options.lite_implicit_weak_fields) { return false; } std::unordered_map bootstrap_mapping{ {"net/proto2/proto/descriptor", "net/proto2/internal/descriptor"}, {"net/proto2/compiler/proto/plugin", "net/proto2/compiler/proto/plugin"}, {"net/proto2/compiler/proto/profile", "net/proto2/compiler/proto/profile_bootstrap"}, }; auto iter = bootstrap_mapping.find(basename); if (iter == bootstrap_mapping.end()) { *bootstrap_basename = basename; return false; } else { *bootstrap_basename = iter->second; return true; } } bool IsBootstrapProto(const Options& options, const FileDescriptor* file) { string my_name = StripProto(file->name()); return GetBootstrapBasename(options, my_name, &my_name); } bool MaybeBootstrap(const Options& options, GeneratorContext* generator_context, bool bootstrap_flag, string* basename) { string bootstrap_basename; if (!GetBootstrapBasename(options, *basename, &bootstrap_basename)) { return false; } if (bootstrap_flag) { // Adjust basename, but don't abort code generation. *basename = bootstrap_basename; return false; } else { string forward_to_basename = bootstrap_basename; // Generate forwarding headers and empty .pb.cc. { std::unique_ptr output( generator_context->Open(*basename + ".pb.h")); io::Printer printer(output.get(), '$', nullptr); printer.Print( "#ifndef PROTOBUF_INCLUDED_$filename_identifier$_FORWARD_PB_H\n" "#define PROTOBUF_INCLUDED_$filename_identifier$_FORWARD_PB_H\n" "#include \"$forward_to_basename$.pb.h\" // IWYU pragma: export\n" "#endif // PROTOBUF_INCLUDED_$filename_identifier$_FORWARD_PB_H\n", "forward_to_basename", forward_to_basename, "filename_identifier", FilenameIdentifier(*basename)); if (!options.opensource_runtime) { // HACK HACK HACK, tech debt from the deeps of proto1 and SWIG // protocoltype is SWIG'ed and we need to forward if (*basename == "net/proto/protocoltype") { printer.Print( "#ifdef SWIG\n" "%include \"$forward_to_basename$.pb.h\"\n" "#endif // SWIG\n", "forward_to_basename", forward_to_basename); } } } { std::unique_ptr output( generator_context->Open(*basename + ".proto.h")); io::Printer printer(output.get(), '$', nullptr); printer.Print( "#ifndef PROTOBUF_INCLUDED_$filename_identifier$_FORWARD_PROTO_H\n" "#define PROTOBUF_INCLUDED_$filename_identifier$_FORWARD_PROTO_H\n" "#include \"$forward_to_basename$.proto.h\" // IWYU pragma: " "export\n" "#endif // " "PROTOBUF_INCLUDED_$filename_identifier$_FORWARD_PROTO_H\n", "forward_to_basename", forward_to_basename, "filename_identifier", FilenameIdentifier(*basename)); } { std::unique_ptr output( generator_context->Open(*basename + ".pb.cc")); io::Printer printer(output.get(), '$', nullptr); printer.Print("\n"); } { std::unique_ptr output( generator_context->Open(*basename + ".pb.h.meta")); } { std::unique_ptr output( generator_context->Open(*basename + ".proto.h.meta")); } // Abort code generation. return true; } } bool ShouldRepeat(const FieldDescriptor* descriptor, internal::WireFormatLite::WireType wiretype) { return descriptor->is_repeated() && (!descriptor->is_packable() || wiretype != internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED); } void GenerateLengthDelim( const FieldDescriptor* field, const Options& options, MessageSCCAnalyzer* scc_analyzer, const Formatter& format) { format( "ptr = Varint::Parse32Inline(ptr, &size);\n" "$GOOGLE_PROTOBUF$_PARSER_ASSERT(ptr);\n"); if (!IsProto1(field->file(), options) && field->is_packable()) { if (!HasPreservingUnknownEnumSemantics(field->file()) && field->type() == FieldDescriptor::TYPE_ENUM) { format( "ctx->extra_parse_data().SetEnumValidator($1$_IsValid, " "msg->mutable_unknown_fields(), $2$);\n" "parser_till_end = " "::$proto_ns$::internal::PackedValidEnumParser$3$;\n" "object = msg->mutable_$4$();\n", QualifiedClassName(field->enum_type()), field->number(), UseUnknownFieldSet(field->file(), options) ? "" : "Lite", FieldName(field)); } else { format( "parser_till_end = ::$proto_ns$::internal::Packed$1$Parser;\n" "object = msg->mutable_$2$();\n", DeclaredTypeMethodName(field->type()), FieldName(field)); } format( "if (size > end - ptr) goto len_delim_till_end;\n" "auto newend = ptr + size;\n" "if (size) ptr = parser_till_end(ptr, newend, object, ctx);\n" "$GOOGLE_PROTOBUF$_PARSER_ASSERT(ptr == newend);\n"); } else { auto field_type = field->type(); if (IsProto1(field->file(), options)) { if (field->is_packable()) { // Sigh ... packed fields endup as a string in proto1 field_type = FieldDescriptor::TYPE_BYTES; } if (field_type == FieldDescriptor::TYPE_STRING) { // In proto1 strings are treated as bytes field_type = FieldDescriptor::TYPE_BYTES; } } string utf8 = ""; switch (field_type) { case FieldDescriptor::TYPE_STRING: utf8 = GetUtf8Suffix(field, options); if (!utf8.empty()) { string name = "nullptr"; if (HasDescriptorMethods(field->file(), options)) { name = "\"" + field->full_name() + "\""; } format("ctx->extra_parse_data().SetFieldName($1$);\n", name); } PROTOBUF_FALLTHROUGH_INTENDED; case FieldDescriptor::TYPE_BYTES: { if (field->options().ctype() == FieldOptions::STRING || (IsProto1(field->file(), options) && field->options().ctype() == FieldOptions::STRING_PIECE)) { format( "parser_till_end = ::$proto_ns$::internal::StringParser$1$;\n" "$string$* str = msg->$2$_$3$();\n" "str->clear();\n", utf8, field->is_repeated() && !field->is_map() && !field->is_packable() ? "add" : "mutable", FieldName(field)); if (utf8.empty()) { // special case if there is no utf8 verification. format( "object = str;\n" "if (size > end - ptr) goto len_delim_till_end;\n" "str->append(ptr, size);\n" "ptr += size;\n"); return; } } else if (field->options().ctype() == FieldOptions::CORD) { string cord_parser = "CordParser" + utf8; format( "parser_till_end = ::$proto_ns$::internal::$1$;\n" "auto* str = msg->$2$_$3$();\n" "str->Clear();\n", cord_parser, field->is_repeated() && !field->is_map() ? "add" : "mutable", FieldName(field)); } else if (field->options().ctype() == FieldOptions::STRING_PIECE) { format( "parser_till_end = " "::$proto_ns$::internal::StringPieceParser$1$;\n" "::$proto_ns$::internal::StringPieceField* str = " "msg->$2$_$3$();\n" "str->Clear();\n", utf8, field->is_repeated() && !field->is_map() ? "add" : "mutable", FieldName(field)); } format( "object = str;\n" "if (size > end - ptr) goto len_delim_till_end;\n" "auto newend = ptr + size;\n" "if (size) ptr = parser_till_end(ptr, newend, object, ctx);\n"); if (!utf8.empty()) { // If utf8 verification is on this can fail. format("$GOOGLE_PROTOBUF$_PARSER_ASSERT(ptr == newend);\n"); } break; } case FieldDescriptor::TYPE_MESSAGE: { GOOGLE_CHECK(field->message_type()); if (!IsProto1(field->file(), options) && field->is_map()) { const FieldDescriptor* val = field->message_type()->FindFieldByName("value"); GOOGLE_CHECK(val); if (HasFieldPresence(field->file()) && val->type() == FieldDescriptor::TYPE_ENUM) { format( "ctx->extra_parse_data().field_number = $1$;\n" "ctx->extra_parse_data().unknown_fields = " "&msg->_internal_metadata_;\n", field->number()); } format( "parser_till_end = ::$proto_ns$::internal::SlowMapEntryParser;\n" "auto parse_map = $1$::_ParseMap;\n" "ctx->extra_parse_data().payload.clear();\n" "ctx->extra_parse_data().parse_map = parse_map;\n" "object = &msg->$2$_;\n" "if (size > end - ptr) goto len_delim_till_end;\n" "auto newend = ptr + size;\n" "GOOGLE_PROTOBUF_PARSER_ASSERT(parse_map(ptr, newend, " "object, ctx));\n" "ptr = newend;\n", QualifiedClassName(field->message_type()), FieldName(field)); break; } if (IsImplicitWeakField(field, options, scc_analyzer)) { if (!field->is_repeated()) { format("object = HasBitSetters::mutable_$1$(msg);\n", FieldName(field)); } else { format( "object = " "CastToBase(&msg->$1$_)->AddWeak(reinterpret_cast(&$2$::_$3$_default_instance_));\n", FieldName(field), Namespace(field->message_type()), ClassName(field->message_type())); } format( "parser_till_end = static_cast<::$proto_ns$::MessageLite*>(" "object)->_ParseFunc();\n"); } else if (IsWeak(field, options)) { if (IsProto1(field->file(), options)) { format("object = msg->internal_mutable_$1$();\n", FieldName(field)); } else { format( "object = msg->_weak_field_map_.MutableMessage($1$, " "_$classname$_default_instance_.$2$_);\n", field->number(), FieldName(field)); } format( "parser_till_end = static_cast<::$proto_ns$::MessageLite*>(" "object)->_ParseFunc();\n"); } else { format( "parser_till_end = $1$::_InternalParse;\n" "object = msg->$2$_$3$();\n", QualifiedClassName(field->message_type()), field->is_repeated() ? "add" : "mutable", FieldName(field)); } format( "if (size > end - ptr) goto len_delim_till_end;\n" "auto newend = ptr + size;\n" "bool ok = ctx->ParseExactRange({parser_till_end, object},\n" " ptr, newend);\n" "$GOOGLE_PROTOBUF$_PARSER_ASSERT(ok);\n" "ptr = newend;\n"); break; } default: GOOGLE_LOG(FATAL) << "Illegal combination for length delimited wiretype " << " filed type is " << field->type(); } } } void GenerateCaseBody(internal::WireFormatLite::WireType wiretype, const FieldDescriptor* field, const Options& options, MessageSCCAnalyzer* scc_analyzer, const Formatter& format) { using internal::WireFormat; using internal::WireFormatLite; if (ShouldRepeat(field, wiretype)) { format("do {\n"); format.Indent(); } switch (wiretype) { case WireFormatLite::WIRETYPE_VARINT: { format( "$uint64$ val;\n" "ptr = Varint::Parse64(ptr, &val);\n" "$GOOGLE_PROTOBUF$_PARSER_ASSERT(ptr);\n"); string type = PrimitiveTypeName(options, field->cpp_type()); if ((field->type() == FieldDescriptor::TYPE_SINT32 || field->type() == FieldDescriptor::TYPE_SINT64) && !IsProto1(field->file(), options)) { int size = EstimateAlignmentSize(field) * 8; format( "$1$ value = " "::$proto_ns$::internal::WireFormatLite::ZigZagDecode$2$(val);\n", type, size); } else if (field->type() == FieldDescriptor::TYPE_ENUM && !IsProto1(field->file(), options)) { if (!HasPreservingUnknownEnumSemantics(field->file())) { format( "if (!$1$_IsValid(val)) {\n" " ::$proto_ns$::internal::WriteVarint($2$, val, " "msg->mutable_unknown_fields());\n" " break;\n" "}\n", QualifiedClassName(field->enum_type()), field->number()); } format("$1$ value = static_cast<$1$>(val);\n", QualifiedClassName(field->enum_type())); } else { format("$1$ value = val;\n", type); } if (field->is_repeated()) { format("msg->add_$1$(value);\n", FieldName(field)); } else { format("msg->set_$1$(value);\n", FieldName(field)); } break; } case WireFormatLite::WIRETYPE_FIXED64: { string type = PrimitiveTypeName(options, field->cpp_type()); format( "$1$ val;\n" "::std::memcpy(&val, ptr, 8);\n" "ptr += 8;\n", type); if (field->is_repeated()) { format("msg->add_$1$(val);\n", FieldName(field)); } else { format("msg->set_$1$(val);\n", FieldName(field)); } break; } case WireFormatLite::WIRETYPE_LENGTH_DELIMITED: { GenerateLengthDelim(field, options, scc_analyzer, format); break; } case WireFormatLite::WIRETYPE_START_GROUP: { format( "parser_till_end = $1$::_InternalParse;\n" "object = msg->$2$_$3$();\n" "bool ok = ctx->PrepareGroup(tag, &depth);\n" "$GOOGLE_PROTOBUF$_PARSER_ASSERT(ok);\n" "ptr = parser_till_end(ptr, end, object, ctx);\n" "$GOOGLE_PROTOBUF$_PARSER_ASSERT(ptr);\n" "if (ctx->GroupContinues(depth)) goto group_continues;\n", QualifiedClassName(field->message_type()), field->is_repeated() ? "add" : "mutable", FieldName(field)); break; } case WireFormatLite::WIRETYPE_END_GROUP: { GOOGLE_LOG(FATAL) << "Can't have end group field\n"; break; } case WireFormatLite::WIRETYPE_FIXED32: { string type = PrimitiveTypeName(options, field->cpp_type()); format( "$1$ val;\n" "std::memcpy(&val, ptr, 4);\n" "ptr += 4;\n", type); if (field->is_repeated()) { format("msg->add_$1$(val);\n", FieldName(field)); } else { format("msg->set_$1$(val);\n", FieldName(field)); } break; } } // switch (wire_type) if (ShouldRepeat(field, wiretype)) { format("if (ptr >= end) break;\n"); uint32 x = field->number() * 8 + wiretype; uint64 y = 0; int cnt = 0; do { y += static_cast((x & 0x7F) + (x >= 128 ? 128 : 0)) << (cnt++ * 8); x >>= 7; } while (x); uint64 mask = (1ull << (cnt * 8)) - 1; format.Outdent(); format( "} while ((::$proto_ns$::io::UnalignedLoad<$uint64$>(ptr) & $1$) == " "$2$ && (ptr += $3$));\n", mask, y, cnt); } format("break;\n"); } void GenerateCaseBody(const FieldDescriptor* field, const Options& options, MessageSCCAnalyzer* scc_analyzer, const Formatter& format) { using internal::WireFormat; using internal::WireFormatLite; if (!IsProto1(field->file(), options) && field->is_packable()) { auto expected_wiretype = WireFormat::WireTypeForFieldType(field->type()); GOOGLE_CHECK(expected_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED); uint32 expected_tag = WireFormatLite::MakeTag(field->number(), expected_wiretype); auto fallback_wiretype = WireFormatLite::WIRETYPE_LENGTH_DELIMITED; uint32 fallback_tag = WireFormatLite::MakeTag(field->number(), fallback_wiretype); if (field->is_packed()) { std::swap(expected_tag, fallback_tag); std::swap(expected_wiretype, fallback_wiretype); } format("if (static_cast<$uint8$>(tag) == $1$) {\n", expected_tag & 0xFF); format.Indent(); GenerateCaseBody(expected_wiretype, field, options, scc_analyzer, format); format.Outdent(); format( "} else if (static_cast<$uint8$>(tag) != $1$) goto handle_unusual;\n", fallback_tag & 0xFF); GenerateCaseBody(fallback_wiretype, field, options, scc_analyzer, format); } else { auto wiretype = WireFormat::WireTypeForField(field); format("if (static_cast<$uint8$>(tag) != $1$) goto handle_unusual;\n", WireFormat::MakeTag(field) & 0xFF); GenerateCaseBody(wiretype, field, options, scc_analyzer, format); } } void GenerateParserLoop(const Descriptor* descriptor, const Options& options, MessageSCCAnalyzer* scc_analyzer, io::Printer* printer) { using internal::WireFormat; using internal::WireFormatLite; Formatter format(printer); format.Set("classname", ClassName(descriptor)); format.Set("proto_ns", ProtobufNamespace(options)); std::map vars; SetCommonVars(options, &vars); format.AddMap(vars); std::vector ordered_fields; for (auto field : FieldRange(descriptor)) { ordered_fields.push_back(field); } std::sort(ordered_fields.begin(), ordered_fields.end(), [](const FieldDescriptor* a, const FieldDescriptor* b) { return a->number() < b->number(); }); format( "const char* $classname$::_InternalParse(const char* begin, const char* " "end, void* object,\n" " ::$proto_ns$::internal::ParseContext* ctx) {\n" " auto msg = static_cast<$classname$*>(object);\n" " $uint32$ size; (void)size;\n" " int depth; (void)depth;\n" " ::$proto_ns$::internal::ParseFunc parser_till_end; " "(void)parser_till_end;\n" " auto ptr = begin;\n" " while (ptr < end) {\n" " $uint32$ tag;\n" " ptr = Varint::Parse32Inline(ptr, &tag);\n" " $GOOGLE_PROTOBUF$_PARSER_ASSERT(ptr);\n" " switch (tag >> 3) {\n"); format.Indent(); format.Indent(); format.Indent(); for (const auto* field : ordered_fields) { if (IsProto1(descriptor->file(), options)) { if (field->number() >= (1 << 14)) continue; } // Print the field's (or oneof's) proto-syntax definition as a comment. // We don't want to print group bodies so we cut off after the first // line. string def; { DebugStringOptions options; options.elide_group_body = true; options.elide_oneof_body = true; def = field->DebugStringWithOptions(options); def = def.substr(0, def.find_first_of('\n')); } format( "// $1$\n" "case $2$: {\n", def, field->number()); format.Indent(); GenerateCaseBody(field, options, scc_analyzer, format); format.Outdent(); format("}\n"); // case } // for fields format( "default: {\n" "handle_unusual: (void)&&handle_unusual;\n" " if ((tag & 7) == 4 || tag == 0) {\n" " bool ok = ctx->ValidEndGroup(tag);\n" " $GOOGLE_PROTOBUF$_PARSER_ASSERT(ok);\n" " return ptr;\n" " }\n"); if (IsMapEntryMessage(descriptor)) { format( " break;\n" "}\n"); } else { if (descriptor->extension_range_count() > 0) { format("if ("); for (int i = 0; i < descriptor->extension_range_count(); i++) { const Descriptor::ExtensionRange* range = descriptor->extension_range(i); if (i > 0) format(" ||\n "); uint32 start_tag = WireFormatLite::MakeTag( range->start, static_cast(0)); uint32 end_tag = WireFormatLite::MakeTag( range->end, static_cast(0)); if (range->end > FieldDescriptor::kMaxNumber) { format("($1$u <= tag)", start_tag); } else { format("($1$u <= tag && tag < $2$u)", start_tag, end_tag); } } format(") {\n"); format( " auto res = msg->_extensions_.ParseField(tag, {_InternalParse, " "msg}, ptr, end,\n" " internal_default_instance(), &msg->_internal_metadata_, " "ctx);\n" " ptr = res.first;\n" " if (res.second) return ptr;\n" " continue;\n" "}\n"); } format( " auto res = UnknownFieldParse(tag, {_InternalParse, msg},\n" " ptr, end, msg->_internal_metadata_.mutable_unknown_fields(), " "ctx);\n" " ptr = res.first;\n" " if (res.second) return ptr;\n" "}\n"); // default case } format.Outdent(); format.Outdent(); format.Outdent(); format( " } // switch\n" " } // while\n" " return ptr;\n" "len_delim_till_end: (void)&&len_delim_till_end;\n" " return ctx->StoreAndTailCall(ptr, end, {_InternalParse, msg},\n" " {parser_till_end, object}, size);\n" "group_continues: (void)&&group_continues;\n" " $DCHK$(ptr >= end);\n" // Group crossed end and must be continued. Either this a parse failure // or we need to resume on the next chunk and thus save the state. " ctx->StoreGroup({_InternalParse, msg}, {parser_till_end, object}, " "depth);\n" " return ptr;\n" "}\n"); } } // namespace cpp } // namespace compiler } // namespace protobuf } // namespace google