aboutsummaryrefslogblamecommitdiff
path: root/src/google/protobuf/compiler/cpp/cpp_helpers.cc
blob: 472c55f4528330cea5b05afd486036753f01d1e6 (plain) (tree)
1
2
3
4
5
6
7
8
                                                      
                                                   
                                                  
  


                                                                         
  








                                                                         
  










                                                                        




                                                
                 
              
                
                        
                 
 
                                          
                                         
                                                     
                                         
                                       
                                                

                                             
                                          
                                             
 
 
                                       

 

                                       






                    

                                                                
                                                               
 








                                              


                                                                               

                                                                           
                                                                        





                                                                          

  

                                              





                                                            
                                                         
 













                                                                              























                                                                         
















                                                                        

               





























                                                                                
























                                                                          




                                                                             




















                                                                  


   









                                                           
                                                   
                                   
          

                                                                


   







                                                    




                                         





                                                                           




                                                                       
 
                                                            
                                                             

 
                                                                             


                                                                               

 








                                                







                                                             























                                                         

                                                                  











                                                                            

 





                                                                              




























                                                                               































                                                                       





























                                                                     
                                  
                            


                                                            




                              
                                                                
                            



                                                                 
   




                                                                  

 

                                                   










                                                                           
                                        
                                                         


                                                             
                                                                     
                                         
                                                                       

                                                   
                                                             
                                                         
                                                                     
                                                          
                                  
                                                          



                                 
                                        
       
                                                   
                                                              
                                                          
                                                                      
                                                           
                                    
                                                           








                                                                         
         
       







                                                                              
                                                                
                                         


                                                 
                                          

                                                







                                                                           








                                                                         
                                                                             




                


                                                             

 







                                                                            




                                                        



















                                                                               























































































































                                                                               
 


















                                                              















                                                                      



















                                                      

                                                                      
                                                                      






                                                               

 








                                                     



                                                                   





                                                                          









                                                            
                                                           

                                                                   

                                                                 
                  
                                                      

                                                            





                  











                                                                            
                                                               
                                                                         


                                                              
                                                            
                                             

                      
                       
       


                                                                                
                      
                                                                   
              
                                                                       
       
                                             
                      
                    
       

                       


                  


                                                                            
                      
                                                               
              
                                                                   
       

                                                







                                                                 
                                                                           
                                                           

                                                              
                                                                         
                                


                                                               
                                                                         
                                                         


                                                                                

 










                                                           

                                                                    
                                                        
                                           
   

 



                                                                          


               



                                                                        


               





                                                                    
                                                                              
                                                            

                                                           
                                                     





                                                                               

 
                                                                    
                                                              
                           



                                                       




                                                                          








                                                          
                                                            






                                                               

                                                                     



























                                                                             







































                                                                     

                                                                         
                                                                        













































































































                                                                                


























































































































































































                                                                                      















                                                                           
                                                     
                                                                  


                                                            










































                                                                              
                                                                





                                                    

                                                       
                                                           
                                                   





































                                                                      

                                                                             











































































                                                                                 

                                                   































                                                                           


                                                  


















































                                                                            











                                                                             
 



                        
// 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 <limits>
#include <map>
#include <queue>
#include <unordered_set>
#include <vector>

#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/compiler/cpp/cpp_helpers.h>
#include <google/protobuf/compiler/scc.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/wire_format_lite.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>


#include <google/protobuf/stubs/hash.h>


#include <google/protobuf/port_def.inc>

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<string> MakeKeywordsMap() {
  std::unordered_set<string> result;
  for (int i = 0; i < GOOGLE_ARRAYSIZE(kKeywordList); i++) {
    result.insert(kKeywordList[i]);
  }
  return result;
}

std::unordered_set<string> 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 <typename I>
string Base63(I n, int k) {
  string res;
  while (k-- > 0) {
    res += Base63Char(static_cast<int>(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<string, string>* variables) {
  (*variables)[type] = IntTypeName(options, type);
}

}  // namespace

void SetCommonVars(const Options& options,
                   std::map<string, string>* 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<double>::infinity()) {
        return "std::numeric_limits<double>::infinity()";
      } else if (value == -std::numeric_limits<double>::infinity()) {
        return "-std::numeric_limits<double>::infinity()";
      } else if (value != value) {
        return "std::numeric_limits<double>::quiet_NaN()";
      } else {
        return SimpleDtoa(value);
      }
    }
    case FieldDescriptor::CPPTYPE_FLOAT:
      {
        float value = field->default_value_float();
        if (value == std::numeric_limits<float>::infinity()) {
          return "std::numeric_limits<float>::infinity()";
        } else if (value == -std::numeric_limits<float>::infinity()) {
          return "-std::numeric_limits<float>::infinity()";
        } else if (value != value) {
          return "std::numeric_limits<float>::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<uint8>(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<const Descriptor*>* 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<const Descriptor*>* 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<const FieldDescriptor*>* 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<const FieldDescriptor*>* 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<const Descriptor*>* 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<string, string> 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<io::ZeroCopyOutputStream> 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<io::ZeroCopyOutputStream> 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<io::ZeroCopyOutputStream> output(
          generator_context->Open(*basename + ".pb.cc"));
      io::Printer printer(output.get(), '$', nullptr);
      printer.Print("\n");
    }

    {
      std::unique_ptr<io::ZeroCopyOutputStream> output(
          generator_context->Open(*basename + ".pb.h.meta"));
    }

    {
      std::unique_ptr<io::ZeroCopyOutputStream> 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<const "
                "::google::protobuf::MessageLite*>(&$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<uint64>((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<string, string> vars;
  SetCommonVars(options, &vars);
  format.AddMap(vars);

  std::vector<const FieldDescriptor*> 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<WireFormatLite::WireType>(0));
        uint32 end_tag = WireFormatLite::MakeTag(
            range->end, static_cast<WireFormatLite::WireType>(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