// 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.

#include <google/protobuf/compiler/cpp/cpp_parse_function_generator.h>

#include <algorithm>
#include <limits>
#include <string>

#include <google/protobuf/compiler/cpp/cpp_helpers.h>
#include <google/protobuf/wire_format.h>

namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {

namespace {
using google::protobuf::internal::TcFieldData;
using google::protobuf::internal::WireFormat;
using google::protobuf::internal::WireFormatLite;

std::vector<const FieldDescriptor*> GetOrderedFields(
    const Descriptor* descriptor, const Options& options) {
  std::vector<const FieldDescriptor*> ordered_fields;
  for (auto field : FieldRange(descriptor)) {
    if (!IsFieldStripped(field, options)) {
      ordered_fields.push_back(field);
    }
  }
  std::sort(ordered_fields.begin(), ordered_fields.end(),
            [](const FieldDescriptor* a, const FieldDescriptor* b) {
              return a->number() < b->number();
            });
  return ordered_fields;
}

bool HasInternalAccessors(const FieldOptions::CType ctype) {
  return ctype == FieldOptions::STRING || ctype == FieldOptions::CORD;
}

int TagSize(uint32_t field_number) {
  if (field_number < 16) return 1;
  GOOGLE_CHECK_LT(field_number, (1 << 14))
      << "coded tag for " << field_number << " too big for uint16_t";
  return 2;
}

const char* CodedTagType(int tag_size) {
  return tag_size == 1 ? "uint8_t" : "uint16_t";
}

const char* TagType(const FieldDescriptor* field) {
  return CodedTagType(TagSize(field->number()));
}

std::string TcParserName(const Options& options) {
  return StrCat("::", ProtobufNamespace(options),
                      "::internal::TcParser::");
}

std::string MessageTcParseFunctionName(const FieldDescriptor* field,
                                       const Options& options) {
  if (field->message_type()->field_count() == 0 ||
      !HasGeneratedMethods(field->message_type()->file(), options)) {
    // For files with `option optimize_for = CODE_SIZE`, or which derive from
    // `ZeroFieldsBase`, we need to call the `_InternalParse` function, because
    // there is no generated tailcall function. For tailcall parsing, this is
    // done by helpers in TcParser.
    return StrCat(TcParserName(options),
                        (field->is_repeated() ? "Repeated" : "Singular"),
                        "ParseMessage<",
                        QualifiedClassName(field->message_type()),  //
                        ", ", TagType(field), ">");
  }
  // This matches macros in generated_message_tctable_impl.h:
  return StrCat("PROTOBUF_TC_PARSE_",
                      (field->is_repeated() ? "REPEATED" : "SINGULAR"),
                      TagSize(field->number()), "(",
                      QualifiedClassName(field->message_type()), ")");
}

std::string FieldParseFunctionName(const FieldDescriptor* field,
                                   const Options& options);

}  // namespace

TailCallTableInfo::TailCallTableInfo(const Descriptor* descriptor,
                                     const Options& options,
                                     const std::vector<int>& has_bit_indices,
                                     MessageSCCAnalyzer* scc_analyzer) {
  std::vector<const FieldDescriptor*> ordered_fields =
      GetOrderedFields(descriptor, options);

  // The table size is rounded up to the nearest power of 2, clamping at 2^5.
  // Note that this is a naive approach: a better approach should only consider
  // table-eligible fields. We may also want to push rarely-encountered fields
  // into the fallback, to make the table smaller.
  table_size_log2 = ordered_fields.size() >= 16  ? 5
                    : ordered_fields.size() >= 8 ? 4
                    : ordered_fields.size() >= 4 ? 3
                    : ordered_fields.size() >= 2 ? 2
                                                 : 1;
  const unsigned table_size = 1 << table_size_log2;

  // Construct info for each possible entry. Fields that do not use table-driven
  // parsing will still have an entry that nominates the fallback function.
  fast_path_fields.resize(table_size);

  for (const auto* field : ordered_fields) {
    // Eagerly assume slow path. If we can handle this field on the fast path,
    // we will pop its entry from `fallback_fields`.
    fallback_fields.push_back(field);

    // Anything difficult slow path:
    if (field->is_map()) continue;
    if (field->real_containing_oneof()) continue;
    if (field->options().weak()) continue;
    if (IsImplicitWeakField(field, options, scc_analyzer)) continue;
    if (IsLazy(field, options, scc_analyzer)) continue;

    // The largest tag that can be read by the tailcall parser is two bytes
    // when varint-coded. This allows 14 bits for the numeric tag value:
    //   byte 0   byte 1
    //   1nnnnttt 0nnnnnnn
    //    ^^^^^^^  ^^^^^^^
    uint32_t tag = WireFormat::MakeTag(field);
    if (tag >= 1 << 14) {
      continue;
    } else if (tag >= 1 << 7) {
      tag = ((tag << 1) & 0x7F00) | 0x80 | (tag & 0x7F);
    }
    // The field index is determined by the low bits of the field number, where
    // the table size determines the width of the mask. The largest table
    // supported is 32 entries. The parse loop uses these bits directly, so that
    // the dispatch does not require arithmetic:
    //   byte 0   byte 1
    //   1nnnnttt 0nnnnnnn
    //   ^^^^^
    // This means that any field number that does not fit in the lower 4 bits
    // will always have the top bit of its table index asserted:
    uint32_t idx = (tag >> 3) & (table_size - 1);
    // If this entry in the table is already used, then this field will be
    // handled by the generated fallback function.
    if (!fast_path_fields[idx].func_name.empty()) continue;

    // Determine the hasbit mask for this field, if needed. (Note that fields
    // without hasbits use different parse functions.)
    int hasbit_idx;
    if (HasHasbit(field)) {
      hasbit_idx = has_bit_indices[field->index()];
      GOOGLE_CHECK_NE(-1, hasbit_idx) << field->DebugString();
      // The tailcall parser can only update the first 32 hasbits. If this
      // field's has-bit is beyond that, then it will need to be handled by the
      // fallback parse function.
      if (hasbit_idx >= 32) continue;
    } else {
      // The tailcall parser only ever syncs 32 has-bits, so if there is no
      // presence, set a bit that will not be used.
      hasbit_idx = 63;
    }

    // Determine the name of the fastpath parse function to use for this field.
    std::string name;

    switch (field->type()) {
      case FieldDescriptor::TYPE_MESSAGE:
        name = MessageTcParseFunctionName(field, options);
        break;

      case FieldDescriptor::TYPE_FIXED64:
      case FieldDescriptor::TYPE_FIXED32:
      case FieldDescriptor::TYPE_SFIXED64:
      case FieldDescriptor::TYPE_SFIXED32:
      case FieldDescriptor::TYPE_DOUBLE:
      case FieldDescriptor::TYPE_FLOAT:
      case FieldDescriptor::TYPE_INT64:
      case FieldDescriptor::TYPE_INT32:
      case FieldDescriptor::TYPE_UINT64:
      case FieldDescriptor::TYPE_UINT32:
      case FieldDescriptor::TYPE_SINT64:
      case FieldDescriptor::TYPE_SINT32:
      case FieldDescriptor::TYPE_BOOL:
        name = FieldParseFunctionName(field, options);
        break;

      case FieldDescriptor::TYPE_BYTES:
        if (field->options().ctype() == FieldOptions::STRING &&
            field->default_value_string().empty() &&
            !IsStringInlined(field, options)) {
          name = FieldParseFunctionName(field, options);
        }
        break;

      default:
        break;
    }

    if (name.empty()) {
      continue;
    }
    // This field made it into the fast path, so remove it from the fallback
    // fields and fill in the table entry.
    fallback_fields.pop_back();
    fast_path_fields[idx].func_name = name;
    fast_path_fields[idx].bits = TcFieldData(tag, hasbit_idx, 0);
    fast_path_fields[idx].field = field;
  }

  // If there are no fallback fields, and at most one extension range, the
  // parser can use a generic fallback function. Otherwise, a message-specific
  // fallback routine is needed.
  use_generated_fallback =
      !fallback_fields.empty() || descriptor->extension_range_count() > 1;
}

ParseFunctionGenerator::ParseFunctionGenerator(
    const Descriptor* descriptor, int max_has_bit_index,
    const std::vector<int>& has_bit_indices,
    const std::vector<int>& inlined_string_indices, const Options& options,
    MessageSCCAnalyzer* scc_analyzer,
    const std::map<std::string, std::string>& vars)
    : descriptor_(descriptor),
      scc_analyzer_(scc_analyzer),
      options_(options),
      variables_(vars),
      inlined_string_indices_(inlined_string_indices),
      num_hasbits_(max_has_bit_index) {
  if (should_generate_tctable()) {
    tc_table_info_.reset(new TailCallTableInfo(descriptor_, options_,
                                               has_bit_indices, scc_analyzer));
  }
  SetCommonVars(options_, &variables_);
  SetUnknownFieldsVariable(descriptor_, options_, &variables_);
  variables_["classname"] = ClassName(descriptor, false);
}

void ParseFunctionGenerator::GenerateMethodDecls(io::Printer* printer) {
  Formatter format(printer, variables_);
  if (should_generate_tctable()) {
    auto declare_function = [&format](const char* name,
                                      const std::string& guard) {
      if (!guard.empty()) {
        format.Outdent();
        format("#if $1$\n", guard);
        format.Indent();
      }
      format("static const char* $1$(PROTOBUF_TC_PARAM_DECL);\n", name);
      if (!guard.empty()) {
        format.Outdent();
        format("#endif  // $1$\n", guard);
        format.Indent();
      }
    };
    if (should_generate_guarded_tctable()) {
      format.Outdent();
      format("#ifdef PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n");
      format.Indent();
    }
    format("// The Tct_* functions are internal to the protobuf runtime:\n");
    // These guards are defined in port_def.inc:
    declare_function("Tct_ParseS1", "PROTOBUF_TC_STATIC_PARSE_SINGULAR1");
    declare_function("Tct_ParseS2", "PROTOBUF_TC_STATIC_PARSE_SINGULAR2");
    declare_function("Tct_ParseR1", "PROTOBUF_TC_STATIC_PARSE_REPEATED1");
    declare_function("Tct_ParseR2", "PROTOBUF_TC_STATIC_PARSE_REPEATED2");
    if (tc_table_info_->use_generated_fallback) {
      format.Outdent();
      format(
          " private:\n"
          "  ");
      declare_function("Tct_ParseFallback", "");
      format(" public:\n");
      format.Indent();
    }
    if (should_generate_guarded_tctable()) {
      format.Outdent();
      format("#endif\n");
      format.Indent();
    }
  }
  format(
      "const char* _InternalParse(const char* ptr, "
      "::$proto_ns$::internal::ParseContext* ctx) final;\n");
}

void ParseFunctionGenerator::GenerateMethodImpls(io::Printer* printer) {
  Formatter format(printer, variables_);
  bool need_parse_function = true;
  if (descriptor_->options().message_set_wire_format()) {
    // Special-case MessageSet.
    need_parse_function = false;
    format(
        "const char* $classname$::_InternalParse(const char* ptr,\n"
        "                  ::$proto_ns$::internal::ParseContext* ctx) {\n"
        "$annotate_deserialize$"
        "  return _extensions_.ParseMessageSet(ptr, \n"
        "      internal_default_instance(), &_internal_metadata_, ctx);\n"
        "}\n");
  }
  if (!should_generate_tctable()) {
    if (need_parse_function) {
      GenerateLoopingParseFunction(format);
    }
    return;
  }
  if (should_generate_guarded_tctable()) {
    format("#ifdef PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n\n");
  }
  if (need_parse_function) {
    GenerateTailcallParseFunction(format);
  }
  if (tc_table_info_->use_generated_fallback) {
    GenerateTailcallFallbackFunction(format);
  }
  GenerateTailcallFieldParseFunctions(format);
  if (should_generate_guarded_tctable()) {
    if (need_parse_function) {
      format("\n#else  // PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n\n");
      GenerateLoopingParseFunction(format);
    }
    format("\n#endif  // PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n");
  }
}

bool ParseFunctionGenerator::should_generate_tctable() const {
  if (options_.tctable_mode == Options::kTCTableNever) {
    return false;
  }
  return true;
}

void ParseFunctionGenerator::GenerateTailcallParseFunction(Formatter& format) {
  GOOGLE_CHECK(should_generate_tctable());

  // Generate an `_InternalParse` that starts the tail-calling loop.
  format(
      "const char* $classname$::_InternalParse(\n"
      "    const char* ptr, ::$proto_ns$::internal::ParseContext* ctx) {\n"
      "$annotate_deserialize$"
      "  ptr = ::$proto_ns$::internal::TcParser::ParseLoop(\n"
      "      this, ptr, ctx, &_table_.header);\n");
  format(
      "  return ptr;\n"
      "}\n\n");
}

void ParseFunctionGenerator::GenerateTailcallFallbackFunction(
    Formatter& format) {
  GOOGLE_CHECK(should_generate_tctable());
  format(
      "const char* $classname$::Tct_ParseFallback(PROTOBUF_TC_PARAM_DECL) {\n"
      "#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) return nullptr\n");
  format.Indent();
  format("auto* typed_msg = static_cast<$classname$*>(msg);\n");

  if (num_hasbits_ > 0) {
    // Sync hasbits
    format("typed_msg->_has_bits_[0] = hasbits;\n");
  }

  format.Set("msg", "typed_msg->");
  format.Set("this", "typed_msg");
  format.Set("has_bits", "typed_msg->_has_bits_");
  format.Set("next_tag", "goto next_tag");
  GenerateParseIterationBody(format, descriptor_,
                             tc_table_info_->fallback_fields);

  format.Outdent();
  format(
      "next_tag:\n"
      "message_done:\n"
      "  return ptr;\n"
      "#undef CHK_\n"
      "}\n");
}

void ParseFunctionGenerator::GenerateTailcallFieldParseFunctions(
    Formatter& format) {
  GOOGLE_CHECK(should_generate_tctable());
  // There are four cases where a tailcall target are needed for messages:
  //   {singular, repeated} x {1, 2}-byte tag
  struct {
    const char* type;
    int size;
  } const kTagLayouts[] = {
      {"uint8_t", 1},
      {"uint16_t", 2},
  };
  // Singular:
  for (const auto& layout : kTagLayouts) {
    // Guard macros are defined in port_def.inc.
    format(
        "#if PROTOBUF_TC_STATIC_PARSE_SINGULAR$1$\n"
        "const char* $classname$::Tct_ParseS$1$(PROTOBUF_TC_PARAM_DECL) {\n"
        "  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<$2$>() != 0))\n"
        "    PROTOBUF_MUSTTAIL "
        "return table->fallback(PROTOBUF_TC_PARAM_PASS);\n"
        "  ptr += $1$;\n"
        "  hasbits |= (uint64_t{1} << data.hasbit_idx());\n"
        "  ::$proto_ns$::internal::TcParser::SyncHasbits"
        "(msg, hasbits, table);\n"
        "  auto& field = ::$proto_ns$::internal::TcParser::"
        "RefAt<$classtype$*>(msg, data.offset());\n"
        "  if (field == nullptr)\n"
        "    field = CreateMaybeMessage<$classtype$>(ctx->data().arena);\n"
        "  return ctx->ParseMessage(field, ptr);\n"
        "}\n"
        "#endif  // PROTOBUF_TC_STATIC_PARSE_SINGULAR$1$\n",
        layout.size, layout.type);
  }
  // Repeated:
  for (const auto& layout : kTagLayouts) {
    // Guard macros are defined in port_def.inc.
    format(
        "#if PROTOBUF_TC_STATIC_PARSE_REPEATED$1$\n"
        "const char* $classname$::Tct_ParseR$1$(PROTOBUF_TC_PARAM_DECL) {\n"
        "  if (PROTOBUF_PREDICT_FALSE(data.coded_tag<$2$>() != 0)) {\n"
        "    PROTOBUF_MUSTTAIL "
        "return table->fallback(PROTOBUF_TC_PARAM_PASS);\n"
        "  }\n"
        "  ptr += $1$;\n"
        "  auto& field = ::$proto_ns$::internal::TcParser::RefAt<"
        "::$proto_ns$::RepeatedPtrField<$classname$>>(msg, data.offset());\n"
        "  ::$proto_ns$::internal::TcParser::SyncHasbits"
        "(msg, hasbits, table);\n"
        "  ptr = ctx->ParseMessage(field.Add(), ptr);\n"
        "  return ptr;\n"
        "}\n"
        "#endif  // PROTOBUF_TC_STATIC_PARSE_REPEATED$1$\n",
        layout.size, layout.type);
  }
}

void ParseFunctionGenerator::GenerateDataDecls(io::Printer* printer) {
  if (!should_generate_tctable()) {
    return;
  }
  Formatter format(printer, variables_);
  if (should_generate_guarded_tctable()) {
    format.Outdent();
    format("#ifdef PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n");
    format.Indent();
  }
  format(
      "static const ::$proto_ns$::internal::TcParseTable<$1$>\n"
      "    _table_;\n",
      tc_table_info_->table_size_log2);
  if (should_generate_guarded_tctable()) {
    format.Outdent();
    format("#endif  // PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n");
    format.Indent();
  }
}

void ParseFunctionGenerator::GenerateDataDefinitions(io::Printer* printer) {
  if (!should_generate_tctable()) {
    return;
  }
  Formatter format(printer, variables_);
  if (should_generate_guarded_tctable()) {
    format("#ifdef PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n");
  }
  GenerateTailCallTable(format);
  if (should_generate_guarded_tctable()) {
    format("#endif  // PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED\n");
  }
}

void ParseFunctionGenerator::GenerateLoopingParseFunction(Formatter& format) {
  format(
      "const char* $classname$::_InternalParse(const char* ptr, "
      "::$proto_ns$::internal::ParseContext* ctx) {\n"
      "$annotate_deserialize$"
      "#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure\n");
  format.Indent();
  format.Set("msg", "");
  format.Set("this", "this");
  int hasbits_size = 0;
  if (num_hasbits_ > 0) {
    hasbits_size = (num_hasbits_ + 31) / 32;
  }
  // For now only optimize small hasbits.
  if (hasbits_size != 1) hasbits_size = 0;
  if (hasbits_size) {
    format("_Internal::HasBits has_bits{};\n");
    format.Set("has_bits", "has_bits");
  } else {
    format.Set("has_bits", "_has_bits_");
  }
  format.Set("next_tag", "continue");
  format("while (!ctx->Done(&ptr)) {\n");
  format.Indent();

  GenerateParseIterationBody(format, descriptor_,
                             GetOrderedFields(descriptor_, options_));

  format.Outdent();
  format("}  // while\n");

  format.Outdent();
  format("message_done:\n");
  if (hasbits_size) format("  _has_bits_.Or(has_bits);\n");

  format(
      "  return ptr;\n"
      "failure:\n"
      "  ptr = nullptr;\n"
      "  goto message_done;\n"
      "#undef CHK_\n"
      "}\n");
}

void ParseFunctionGenerator::GenerateTailCallTable(Formatter& format) {
  GOOGLE_CHECK(should_generate_tctable());
  // All entries without a fast-path parsing function need a fallback.
  std::string fallback;
  if (tc_table_info_->use_generated_fallback) {
    fallback = ClassName(descriptor_) + "::Tct_ParseFallback";
  } else {
    fallback = TcParserName(options_) + "GenericFallback";
    if (GetOptimizeFor(descriptor_->file(), options_) ==
        FileOptions::LITE_RUNTIME) {
      fallback += "Lite";
    }
  }

  // For simplicity and speed, the table is not covering all proto
  // configurations. This model uses a fallback to cover all situations that
  // the table can't accommodate, together with unknown fields or extensions.
  // These are number of fields over 32, fields with 3 or more tag bytes,
  // maps, weak fields, lazy, more than 1 extension range. In the cases
  // the table is sufficient we can use a generic routine, that just handles
  // unknown fields and potentially an extension range.
  format(
      "const ::$proto_ns$::internal::TcParseTable<$1$>\n"
      "    $classname$::_table_ = {\n",
      tc_table_info_->table_size_log2);
  {
    auto table_scope = format.ScopedIndent();
    format("{\n");
    {
      auto header_scope = format.ScopedIndent();
      if (num_hasbits_ > 0 || IsMapEntryMessage(descriptor_)) {
        format("PROTOBUF_FIELD_OFFSET($classname$, _has_bits_),\n");
      } else {
        format("0,  // no _has_bits_\n");
      }
      if (descriptor_->extension_range_count() == 1) {
        format(
            "PROTOBUF_FIELD_OFFSET($classname$, _extensions_),\n"
            "$1$, $2$,  // extension_range_{low,high}\n",
            descriptor_->extension_range(0)->start,
            descriptor_->extension_range(0)->end);
      } else {
        format("0, 0, 0,  // no _extensions_\n");
      }
      format(
          "$1$, 0, $2$,  // fast_idx_mask, reserved, num_fields\n"
          "&$3$._instance,\n"
          "$4$  // fallback\n",
          (((1 << tc_table_info_->table_size_log2) - 1) << 3),
          descriptor_->field_count(),
          DefaultInstanceName(descriptor_, options_), fallback);
    }
    format("}, {\n");
    {
      auto fast_scope = format.ScopedIndent();
      GenerateFastFieldEntries(format, fallback);
    }
    format("},\n");  // entries[]
  }
  format("};\n\n");  // _table_
}

void ParseFunctionGenerator::GenerateFastFieldEntries(
    Formatter& format, const std::string& fallback) {
  for (const auto& info : tc_table_info_->fast_path_fields) {
    if (info.field != nullptr) {
      PrintFieldComment(format, info.field);
    }
    format("{$1$, ", info.func_name.empty() ? fallback : info.func_name);
    if (info.bits.data) {
      GOOGLE_DCHECK_NE(nullptr, info.field);
      format(
          "{$1$, $2$, "
          "static_cast<uint16_t>(PROTOBUF_FIELD_OFFSET($classname$, $3$_))}",
          info.bits.coded_tag(), info.bits.hasbit_idx(), FieldName(info.field));
    } else {
      format("{}");
    }
    format("},\n");
  }
}

void ParseFunctionGenerator::GenerateArenaString(Formatter& format,
                                                 const FieldDescriptor* field) {
  if (HasHasbit(field)) {
    format("_Internal::set_has_$1$(&$has_bits$);\n", FieldName(field));
  }
  std::string default_string =
      field->default_value_string().empty()
          ? "::" + ProtobufNamespace(options_) +
                "::internal::GetEmptyStringAlreadyInited()"
          : QualifiedClassName(field->containing_type(), options_) +
                "::" + MakeDefaultName(field) + ".get()";
  format(
      "if (arena != nullptr) {\n"
      "  ptr = ctx->ReadArenaString(ptr, &$msg$$name$_, arena");
  if (IsStringInlined(field, options_)) {
    GOOGLE_DCHECK(!inlined_string_indices_.empty());
    int inlined_string_index = inlined_string_indices_[field->index()];
    GOOGLE_DCHECK_GE(inlined_string_index, 0);
    format(
        ", $msg$_internal_$name$_donated()"
        ", &$msg$_inlined_string_donated_[$1$]"
        ", ~0x$2$u",
        inlined_string_index / 32,
        strings::Hex(1u << (inlined_string_index % 32), strings::ZERO_PAD_8));
  } else {
    GOOGLE_DCHECK(field->default_value_string().empty());
  }
  format(
      ");\n"
      "} else {\n"
      "  ptr = ::$proto_ns$::internal::InlineGreedyStringParser("
      "$msg$$name$_.MutableNoArenaNoDefault(&$1$), ptr, ctx);\n"
      "}\n"
      "const std::string* str = &$msg$$name$_.Get(); (void)str;\n",
      default_string);
}

void ParseFunctionGenerator::GenerateStrings(Formatter& format,
                                             const FieldDescriptor* field,
                                             bool check_utf8) {
  FieldOptions::CType ctype = FieldOptions::STRING;
  if (!options_.opensource_runtime) {
    // Open source doesn't support other ctypes;
    ctype = field->options().ctype();
  }
  if (!field->is_repeated() && !options_.opensource_runtime &&
      GetOptimizeFor(field->file(), options_) != FileOptions::LITE_RUNTIME &&
      // For now only use arena string for strings with empty defaults.
      field->default_value_string().empty() &&
      !field->real_containing_oneof() && ctype == FieldOptions::STRING) {
    GenerateArenaString(format, field);
  } else {
    std::string parser_name;
    switch (ctype) {
      case FieldOptions::STRING:
        parser_name = "GreedyStringParser";
        break;
      case FieldOptions::CORD:
        parser_name = "CordParser";
        break;
      case FieldOptions::STRING_PIECE:
        parser_name = "StringPieceParser";
        break;
    }
    format(
        "auto str = $msg$$1$$2$_$name$();\n"
        "ptr = ::$proto_ns$::internal::Inline$3$(str, ptr, ctx);\n",
        HasInternalAccessors(ctype) ? "_internal_" : "",
        field->is_repeated() && !field->is_packable() ? "add" : "mutable",
        parser_name);
  }
  if (!check_utf8) return;  // return if this is a bytes field
  auto level = GetUtf8CheckMode(field, options_);
  switch (level) {
    case Utf8CheckMode::kNone:
      return;
    case Utf8CheckMode::kVerify:
      format("#ifndef NDEBUG\n");
      break;
    case Utf8CheckMode::kStrict:
      format("CHK_(");
      break;
  }
  std::string field_name;
  field_name = "nullptr";
  if (HasDescriptorMethods(field->file(), options_)) {
    field_name = StrCat("\"", field->full_name(), "\"");
  }
  format("::$proto_ns$::internal::VerifyUTF8(str, $1$)", field_name);
  switch (level) {
    case Utf8CheckMode::kNone:
      return;
    case Utf8CheckMode::kVerify:
      format(
          ";\n"
          "#endif  // !NDEBUG\n");
      break;
    case Utf8CheckMode::kStrict:
      format(");\n");
      break;
  }
}

void ParseFunctionGenerator::GenerateLengthDelim(Formatter& format,
                                                 const FieldDescriptor* field) {
  if (field->is_packable()) {
    if (field->type() == FieldDescriptor::TYPE_ENUM &&
        !HasPreservingUnknownEnumSemantics(field)) {
      std::string enum_type = QualifiedClassName(field->enum_type(), options_);
      format(
          "ptr = "
          "::$proto_ns$::internal::Packed$1$Parser<$unknown_fields_type$>("
          "$msg$_internal_mutable_$name$(), ptr, ctx, $2$_IsValid, "
          "&$msg$_internal_metadata_, $3$);\n",
          DeclaredTypeMethodName(field->type()), enum_type, field->number());
    } else {
      format(
          "ptr = ::$proto_ns$::internal::Packed$1$Parser("
          "$msg$_internal_mutable_$name$(), ptr, ctx);\n",
          DeclaredTypeMethodName(field->type()));
    }
  } else {
    auto field_type = field->type();
    switch (field_type) {
      case FieldDescriptor::TYPE_STRING:
        GenerateStrings(format, field, true /* utf8 */);
        break;
      case FieldDescriptor::TYPE_BYTES:
        GenerateStrings(format, field, false /* utf8 */);
        break;
      case FieldDescriptor::TYPE_MESSAGE: {
        if (field->is_map()) {
          const FieldDescriptor* val =
              field->message_type()->FindFieldByName("value");
          GOOGLE_CHECK(val);
          if (val->type() == FieldDescriptor::TYPE_ENUM &&
              !HasPreservingUnknownEnumSemantics(field)) {
            format(
                "auto object = "
                "::$proto_ns$::internal::InitEnumParseWrapper<"
                "$unknown_fields_type$>(&$msg$$name$_, $1$_IsValid, "
                "$2$, &$msg$_internal_metadata_);\n"
                "ptr = ctx->ParseMessage(&object, ptr);\n",
                QualifiedClassName(val->enum_type(), options_),
                field->number());
          } else {
            format("ptr = ctx->ParseMessage(&$msg$$name$_, ptr);\n");
          }
        } else if (IsLazy(field, options_, scc_analyzer_)) {
          if (field->real_containing_oneof()) {
            format(
                "if (!$msg$_internal_has_$name$()) {\n"
                "  $msg$clear_$1$();\n"
                "  $msg$$1$_.$name$_ = ::$proto_ns$::Arena::CreateMessage<\n"
                "      ::$proto_ns$::internal::LazyField>("
                "$msg$GetArenaForAllocation());\n"
                "  $msg$set_has_$name$();\n"
                "}\n"
                "auto* lazy_field = $msg$$1$_.$name$_;\n",
                field->containing_oneof()->name());
          } else if (HasHasbit(field)) {
            format(
                "_Internal::set_has_$name$(&$has_bits$);\n"
                "auto* lazy_field = &$msg$$name$_;\n");
          } else {
            format("auto* lazy_field = &$msg$$name$_;\n");
          }
          format(
              "::$proto_ns$::internal::LazyFieldParseHelper<\n"
              "  ::$proto_ns$::internal::LazyField> parse_helper(\n"
              "    $1$::default_instance(),\n"
              "    $msg$GetArenaForAllocation(), lazy_field);\n"
              "ptr = ctx->ParseMessage(&parse_helper, ptr);\n",
              FieldMessageTypeName(field, options_));
        } else if (IsImplicitWeakField(field, options_, scc_analyzer_)) {
          if (!field->is_repeated()) {
            format(
                "ptr = ctx->ParseMessage(_Internal::mutable_$name$($this$), "
                "ptr);\n");
          } else {
            format(
                "ptr = ctx->ParseMessage($msg$$name$_.AddWeak("
                "reinterpret_cast<const ::$proto_ns$::MessageLite*>($1$ptr_)"
                "), ptr);\n",
                QualifiedDefaultInstanceName(field->message_type(), options_));
          }
        } else if (IsWeak(field, options_)) {
          format(
              "{\n"
              "  auto* default_ = &reinterpret_cast<const Message&>($1$);\n"
              "  ptr = ctx->ParseMessage($msg$_weak_field_map_.MutableMessage("
              "$2$, default_), ptr);\n"
              "}\n",
              QualifiedDefaultInstanceName(field->message_type(), options_),
              field->number());
        } else {
          format(
              "ptr = ctx->ParseMessage($msg$_internal_$mutable_field$(), "
              "ptr);\n");
        }
        break;
      }
      default:
        GOOGLE_LOG(FATAL) << "Illegal combination for length delimited wiretype "
                   << " filed type is " << field->type();
    }
  }
}

static bool ShouldRepeat(const FieldDescriptor* descriptor,
                         WireFormatLite::WireType wiretype) {
  constexpr int kMaxTwoByteFieldNumber = 16 * 128;
  return descriptor->number() < kMaxTwoByteFieldNumber &&
         descriptor->is_repeated() &&
         (!descriptor->is_packable() ||
          wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED);
}

void ParseFunctionGenerator::GenerateFieldBody(
    Formatter& format, WireFormatLite::WireType wiretype,
    const FieldDescriptor* field) {
  Formatter::SaveState formatter_state(&format);
  format.AddMap(
      {{"name", FieldName(field)},
       {"primitive_type", PrimitiveTypeName(options_, field->cpp_type())}});
  if (field->is_repeated()) {
    format.AddMap({{"put_field", StrCat("add_", FieldName(field))},
                   {"mutable_field", StrCat("add_", FieldName(field))}});
  } else {
    format.AddMap(
        {{"put_field", StrCat("set_", FieldName(field))},
         {"mutable_field", StrCat("mutable_", FieldName(field))}});
  }
  uint32_t tag = WireFormatLite::MakeTag(field->number(), wiretype);
  switch (wiretype) {
    case WireFormatLite::WIRETYPE_VARINT: {
      std::string type = PrimitiveTypeName(options_, field->cpp_type());
      if (field->type() == FieldDescriptor::TYPE_ENUM) {
        format.Set("enum_type",
                   QualifiedClassName(field->enum_type(), options_));
        format(
            "$uint64$ val = ::$proto_ns$::internal::ReadVarint64(&ptr);\n"
            "CHK_(ptr);\n");
        if (!HasPreservingUnknownEnumSemantics(field)) {
          format("if (PROTOBUF_PREDICT_TRUE($enum_type$_IsValid(val))) {\n");
          format.Indent();
        }
        format("$msg$_internal_$put_field$(static_cast<$enum_type$>(val));\n");
        if (!HasPreservingUnknownEnumSemantics(field)) {
          format.Outdent();
          format(
              "} else {\n"
              "  ::$proto_ns$::internal::WriteVarint("
              "$1$, val, $msg$mutable_unknown_fields());\n"
              "}\n",
              field->number());
        }
      } else {
        std::string size = (field->type() == FieldDescriptor::TYPE_INT32 ||
                            field->type() == FieldDescriptor::TYPE_SINT32 ||
                            field->type() == FieldDescriptor::TYPE_UINT32)
                               ? "32"
                               : "64";
        std::string zigzag;
        if ((field->type() == FieldDescriptor::TYPE_SINT32 ||
             field->type() == FieldDescriptor::TYPE_SINT64)) {
          zigzag = "ZigZag";
        }
        if (field->is_repeated() || field->real_containing_oneof()) {
          format(
              "$msg$_internal_$put_field$("
              "::$proto_ns$::internal::ReadVarint$1$$2$(&ptr));\n"
              "CHK_(ptr);\n",
              zigzag, size);
        } else {
          if (HasHasbit(field)) {
            format("_Internal::set_has_$name$(&$has_bits$);\n");
          }
          format(
              "$msg$$name$_ = ::$proto_ns$::internal::ReadVarint$1$$2$(&ptr);\n"
              "CHK_(ptr);\n",
              zigzag, size);
        }
      }
      break;
    }
    case WireFormatLite::WIRETYPE_FIXED32:
    case WireFormatLite::WIRETYPE_FIXED64: {
      if (field->is_repeated() || field->real_containing_oneof()) {
        format(
            "$msg$_internal_$put_field$("
            "::$proto_ns$::internal::UnalignedLoad<$primitive_type$>(ptr));\n"
            "ptr += sizeof($primitive_type$);\n");
      } else {
        if (HasHasbit(field)) {
          format("_Internal::set_has_$name$(&$has_bits$);\n");
        }
        format(
            "$msg$$name$_ = "
            "::$proto_ns$::internal::UnalignedLoad<$primitive_type$>(ptr);\n"
            "ptr += sizeof($primitive_type$);\n");
      }
      break;
    }
    case WireFormatLite::WIRETYPE_LENGTH_DELIMITED: {
      GenerateLengthDelim(format, field);
      format("CHK_(ptr);\n");
      break;
    }
    case WireFormatLite::WIRETYPE_START_GROUP: {
      format(
          "ptr = ctx->ParseGroup($msg$_internal_$mutable_field$(), ptr, $1$);\n"
          "CHK_(ptr);\n",
          tag);
      break;
    }
    case WireFormatLite::WIRETYPE_END_GROUP: {
      GOOGLE_LOG(FATAL) << "Can't have end group field\n";
      break;
    }
  }  // switch (wire_type)
}

// Returns the tag for this field and in case of repeated packable fields,
// sets a fallback tag in fallback_tag_ptr.
static uint32_t ExpectedTag(const FieldDescriptor* field,
                            uint32_t* fallback_tag_ptr) {
  uint32_t expected_tag;
  if (field->is_packable()) {
    auto expected_wiretype = WireFormat::WireTypeForFieldType(field->type());
    expected_tag = WireFormatLite::MakeTag(field->number(), expected_wiretype);
    GOOGLE_CHECK(expected_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED);
    auto fallback_wiretype = WireFormatLite::WIRETYPE_LENGTH_DELIMITED;
    uint32_t fallback_tag =
        WireFormatLite::MakeTag(field->number(), fallback_wiretype);

    if (field->is_packed()) std::swap(expected_tag, fallback_tag);
    *fallback_tag_ptr = fallback_tag;
  } else {
    auto expected_wiretype = WireFormat::WireTypeForField(field);
    expected_tag = WireFormatLite::MakeTag(field->number(), expected_wiretype);
  }
  return expected_tag;
}

// These variables are used by the generated parse iteration, and must already
// be defined in the generated code:
// - `const char* ptr`: the input buffer.
// - `ParseContext* ctx`: the associated context for `ptr`.
// - implicit `this`: i.e., we must be in a non-static member function.
//
// The macro `CHK_(x)` must be defined. It should return an error condition if
// the macro parameter is false.
//
// Whenever an END_GROUP tag was read, or tag 0 was read, the generated code
// branches to the label `message_done`.
//
// These formatter variables are used:
// - `next_tag`: a single statement to begin parsing the next tag.
//
// At the end of the generated code, the enclosing function should proceed to
// parse the next tag in the stream.
void ParseFunctionGenerator::GenerateParseIterationBody(
    Formatter& format, const Descriptor* descriptor,
    const std::vector<const FieldDescriptor*>& ordered_fields) {
  format(
      "$uint32$ tag;\n"
      "ptr = ::$proto_ns$::internal::ReadTag(ptr, &tag);\n");

  if (!ordered_fields.empty()) {
    GenerateFieldSwitch(format, ordered_fields);
    // Each field `case` only considers field number. Field numbers that are
    // not defined in the message, or tags with an incompatible wire type, are
    // considered "unusual" cases. They will be handled by the logic below.
    format.Outdent();
    format("handle_unusual:\n");
    format.Indent();
  }

  // Unusual/extension/unknown case:
  format(
      "if ((tag == 0) || ((tag & 7) == 4)) {\n"
      "  CHK_(ptr);\n"
      "  ctx->SetLastTag(tag);\n"
      "  goto message_done;\n"
      "}\n");
  if (IsMapEntryMessage(descriptor)) {
    format("$next_tag$;\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_t start_tag = WireFormatLite::MakeTag(
            range->start, static_cast<WireFormatLite::WireType>(0));
        uint32_t 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"
          "  ptr = $msg$_extensions_.ParseField(tag, ptr, "
          "internal_default_instance(), &$msg$_internal_metadata_, ctx);\n"
          "  CHK_(ptr != nullptr);\n"
          "  $next_tag$;\n"
          "}\n");
    }
    format(
        "ptr = UnknownFieldParse(\n"
        "    tag,\n"
        "    $msg$_internal_metadata_.mutable_unknown_fields<"
        "$unknown_fields_type$>(),\n"
        "    ptr, ctx);\n"
        "CHK_(ptr != nullptr);\n");
  }
}

void ParseFunctionGenerator::GenerateFieldSwitch(
    Formatter& format,
    const std::vector<const FieldDescriptor*>& ordered_fields) {
  format("switch (tag >> 3) {\n");
  format.Indent();

  for (const auto* field : ordered_fields) {
    PrintFieldComment(format, field);
    format("case $1$:\n", field->number());
    format.Indent();
    uint32_t fallback_tag = 0;
    uint32_t expected_tag = ExpectedTag(field, &fallback_tag);
    format("if (PROTOBUF_PREDICT_TRUE(static_cast<$uint8$>(tag) == $1$)) {\n",
           expected_tag & 0xFF);
    format.Indent();
    auto wiretype = WireFormatLite::GetTagWireType(expected_tag);
    uint32_t tag = WireFormatLite::MakeTag(field->number(), wiretype);
    int tag_size = io::CodedOutputStream::VarintSize32(tag);
    bool is_repeat = ShouldRepeat(field, wiretype);
    if (is_repeat) {
      format(
          "ptr -= $1$;\n"
          "do {\n"
          "  ptr += $1$;\n",
          tag_size);
      format.Indent();
    }
    GenerateFieldBody(format, wiretype, field);
    if (is_repeat) {
      format.Outdent();
      format(
          "  if (!ctx->DataAvailable(ptr)) break;\n"
          "} while (::$proto_ns$::internal::ExpectTag<$1$>(ptr));\n",
          tag);
    }
    format.Outdent();
    if (fallback_tag) {
      format("} else if (static_cast<$uint8$>(tag) == $1$) {\n",
             fallback_tag & 0xFF);
      format.Indent();
      GenerateFieldBody(format, WireFormatLite::GetTagWireType(fallback_tag),
                        field);
      format.Outdent();
    }
    format(
        "} else\n"
        "  goto handle_unusual;\n"
        "$next_tag$;\n");
    format.Outdent();
  }  // for loop over ordered fields

  format(
      "default:\n"
      "  goto handle_unusual;\n");
  format.Outdent();
  format("}  // switch\n");
}

namespace {

std::string FieldParseFunctionName(const FieldDescriptor* field,
                                   const Options& options) {
  ParseCardinality card =  //
      field->is_packed()               ? ParseCardinality::kPacked
      : field->is_repeated()           ? ParseCardinality::kRepeated
      : field->real_containing_oneof() ? ParseCardinality::kOneof
                                       : ParseCardinality::kSingular;

  TypeFormat type_format;
  switch (field->type()) {
    case FieldDescriptor::TYPE_FIXED64:
    case FieldDescriptor::TYPE_SFIXED64:
    case FieldDescriptor::TYPE_DOUBLE:
      type_format = TypeFormat::kFixed64;
      break;

    case FieldDescriptor::TYPE_FIXED32:
    case FieldDescriptor::TYPE_SFIXED32:
    case FieldDescriptor::TYPE_FLOAT:
      type_format = TypeFormat::kFixed32;
      break;

    case FieldDescriptor::TYPE_INT64:
    case FieldDescriptor::TYPE_UINT64:
      type_format = TypeFormat::kVar64;
      break;

    case FieldDescriptor::TYPE_INT32:
    case FieldDescriptor::TYPE_UINT32:
      type_format = TypeFormat::kVar32;
      break;

    case FieldDescriptor::TYPE_SINT64:
      type_format = TypeFormat::kSInt64;
      break;

    case FieldDescriptor::TYPE_SINT32:
      type_format = TypeFormat::kSInt32;
      break;

    case FieldDescriptor::TYPE_BOOL:
      type_format = TypeFormat::kBool;
      break;

    case FieldDescriptor::TYPE_BYTES:
      type_format = TypeFormat::kBytes;
      break;

    case FieldDescriptor::TYPE_STRING:
      switch (GetUtf8CheckMode(field, options)) {
        case Utf8CheckMode::kNone:
          type_format = TypeFormat::kBytes;
          break;
        case Utf8CheckMode::kStrict:
          type_format = TypeFormat::kString;
          break;
        case Utf8CheckMode::kVerify:
          type_format = TypeFormat::kStringValidateOnly;
          break;
        default:
          GOOGLE_LOG(DFATAL) << "Mode not handled: "
                      << static_cast<int>(GetUtf8CheckMode(field, options));
          return "";
      }
      break;

    default:
      GOOGLE_LOG(DFATAL) << "Type not handled: " << field->DebugString();
      return "";
  }

  return "::" + ProtobufNamespace(options) + "::internal::TcParser::" +
         GetTailCallFieldHandlerName(card, type_format,
                                     TagSize(field->number()), options);
}

}  // namespace

std::string GetTailCallFieldHandlerName(ParseCardinality card,
                                        TypeFormat type_format,
                                        int tag_length_bytes,
                                        const Options& options) {
  std::string name;

  // The field implementation functions are prefixed by cardinality:
  //   `Singular` for optional or implicit fields.
  //   `Repeated` for non-packed repeated.
  //   `Packed` for packed repeated.
  switch (card) {
    case ParseCardinality::kSingular:
      name.append("Singular");
      break;
    case ParseCardinality::kOneof:
      name.append("Oneof");
      break;
    case ParseCardinality::kRepeated:
      name.append("Repeated");
      break;
    case ParseCardinality::kPacked:
      name.append("Packed");
      break;
  }

  // Next in the function name is the TypeFormat-specific name.
  switch (type_format) {
    case TypeFormat::kFixed64:
    case TypeFormat::kFixed32:
      name.append("Fixed");
      break;

    case TypeFormat::kVar64:
    case TypeFormat::kVar32:
    case TypeFormat::kSInt64:
    case TypeFormat::kSInt32:
    case TypeFormat::kBool:
      name.append("Varint");
      break;

    case TypeFormat::kBytes:
    case TypeFormat::kString:
    case TypeFormat::kStringValidateOnly:
      name.append("String");
      break;

    default:
      break;
  }

  name.append("<");

  // Determine the numeric layout type for the parser to use, independent of
  // the specific parsing logic used.
  switch (type_format) {
    case TypeFormat::kVar64:
    case TypeFormat::kFixed64:
      name.append("uint64_t, ");
      break;

    case TypeFormat::kSInt64:
      name.append("int64_t, ");
      break;

    case TypeFormat::kVar32:
    case TypeFormat::kFixed32:
      name.append("uint32_t, ");
      break;

    case TypeFormat::kSInt32:
      name.append("int32_t, ");
      break;

    case TypeFormat::kBool:
      name.append("bool, ");
      break;

    default:
      break;
  }

  name.append(CodedTagType(tag_length_bytes));

  switch (type_format) {
    case TypeFormat::kVar64:
    case TypeFormat::kVar32:
    case TypeFormat::kBool:
      StrAppend(&name, ", ", TcParserName(options), "kNoConversion");
      break;

    case TypeFormat::kSInt64:
    case TypeFormat::kSInt32:
      StrAppend(&name, ", ", TcParserName(options), "kZigZag");
      break;

    case TypeFormat::kBytes:
      StrAppend(&name, ", ", TcParserName(options), "kNoUtf8");
      break;

    case TypeFormat::kString:
      StrAppend(&name, ", ", TcParserName(options), "kUtf8");
      break;

    case TypeFormat::kStringValidateOnly:
      StrAppend(&name, ", ", TcParserName(options), "kUtf8ValidateOnly");
      break;

    default:
      break;
  }

  name.append(">");
  return name;
}

}  // namespace cpp
}  // namespace compiler
}  // namespace protobuf
}  // namespace google
