// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_PARSE_FUNCTION_GENERATOR_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_PARSE_FUNCTION_GENERATOR_H__

#include <memory>
#include <string>
#include <vector>

#include "absl/container/flat_hash_map.h"
#include "absl/functional/function_ref.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "absl/types/span.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/compiler/cpp/options.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/generated_message_tctable_gen.h"
#include "google/protobuf/io/printer.h"

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

// Returns the fields of the descriptor ordered by increasing tag number.
std::vector<const FieldDescriptor*> GetOrderedFields(
    const Descriptor* descriptor);

// ParseFunctionGenerator generates the _InternalParse function for a message
// (and any associated supporting members).
class ParseFunctionGenerator {
 public:
  // When presence probability is not present, we're not sure how likely "field"
  // is present. Assign a 50% probability to avoid pessimizing it.
  static constexpr float kUnknownPresenceProbability = 0.5f;

  // We can't directly depend on FieldLayout because the layout optimizers
  // construct "virtual" fast parse tables using this class. Instead, we allow
  // callers to provide a function that returns the hasbit index for a given
  // field.
  using GetHasBitIndex = absl::FunctionRef<absl::optional<int>(
      const FieldDescriptor* field) const>;

  ParseFunctionGenerator(
      const Descriptor* descriptor, bool has_hasbits,
      GetHasBitIndex get_has_bit_index, const Options& options,
      const absl::flat_hash_map<absl::string_view, std::string>& vars,
      int index_in_file_messages);

  // Construct a ParseFunctionGenerator ignoring the effect of hasbits.
  ParseFunctionGenerator(
      const Descriptor* descriptor, const Options& options,
      const absl::flat_hash_map<absl::string_view, std::string>& vars,
      int index_in_file_messages)
      : ParseFunctionGenerator(
            descriptor, /*has_hasbits=*/false,
            [](const FieldDescriptor*) { return absl::nullopt; }, options, vars,
            index_in_file_messages) {}

  static std::vector<internal::TailCallTableInfo::FieldOptions>
  BuildFieldOptions(const Descriptor* descriptor,
                    absl::Span<const FieldDescriptor* const> ordered_fields,
                    GetHasBitIndex get_has_bit_index, const Options& options);

  static internal::TailCallTableInfo BuildTcTableInfoFromDescriptor(
      const Descriptor* descriptor, const Options& options,
      absl::Span<const internal::TailCallTableInfo::FieldOptions>
          field_options);

  // Emit alias parse table type.
  void GenerateAliasParseTableType(io::Printer* printer);

  // Emits the helper function definition to `printer`:
  void GenerateParseTableHelperDefinition(io::Printer* printer);

 private:
  friend class TailCallTableInfoTest;

  class GeneratedOptionProvider;

  // Generates the tail-call table definition.
  void GenerateTailCallTable(io::Printer* printer);
  void GenerateFastFieldEntries(io::Printer* printer);
  void GenerateFieldEntries(io::Printer* p);
  void GenerateFieldNames(Formatter& format);

  const Descriptor* descriptor_;
  const Options& options_;
  absl::flat_hash_map<absl::string_view, std::string> variables_;
  std::unique_ptr<internal::TailCallTableInfo> tc_table_info_;
  const std::vector<const FieldDescriptor*> ordered_fields_;
  bool has_hasbits_;
  int index_in_file_messages_;
};

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

#endif  // GOOGLE_PROTOBUF_COMPILER_CPP_PARSE_FUNCTION_GENERATOR_H__
