//===-- HashedNameToDIE.h ---------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_HASHEDNAMETODIE_H
#define LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_HASHEDNAMETODIE_H

#include <vector>

#include "lldb/Core/MappedHash.h"
#include "lldb/Core/dwarf.h"
#include "lldb/Utility/RegularExpression.h"
#include "lldb/lldb-defines.h"

#include "DWARFDefines.h"
#include "DWARFFormValue.h"
#include "NameToDIE.h"

class DWARFMappedHash {
public:
  enum AtomType : uint16_t {
    eAtomTypeNULL = 0u,
    /// DIE offset, check form for encoding.
    eAtomTypeDIEOffset = 1u,
    /// DIE offset of the compiler unit header that contains the item in
    /// question.
    eAtomTypeCUOffset = 2u,
    /// DW_TAG_xxx value, should be encoded as DW_FORM_data1 (if no tags exceed
    /// 255) or DW_FORM_data2.
    eAtomTypeTag = 3u,
    // Flags from enum NameFlags.
    eAtomTypeNameFlags = 4u,
    // Flags from enum TypeFlags.
    eAtomTypeTypeFlags = 5u,
    /// A 32 bit hash of the full qualified name (since all hash entries are
    /// basename only) For example a type like "std::vector<int>::iterator"
    /// would have a name of "iterator" and a 32 bit hash for
    /// "std::vector<int>::iterator" to allow us to not have to pull in debug
    /// info for a type when we know the fully qualified name.
    eAtomTypeQualNameHash = 6u
  };

  /// Bit definitions for the eAtomTypeTypeFlags flags.
  enum TypeFlags {
    /// Always set for C++, only set for ObjC if this is the
    /// @implementation for class.
    eTypeFlagClassIsImplementation = (1u << 1)
  };

  struct DIEInfo {
    dw_offset_t die_offset = DW_INVALID_OFFSET;
    dw_tag_t tag = llvm::dwarf::DW_TAG_null;

    /// Any flags for this DIEInfo.
    uint32_t type_flags = 0;

    /// A 32 bit hash of the fully qualified name.
    uint32_t qualified_name_hash = 0;

    DIEInfo() = default;
    DIEInfo(dw_offset_t o, dw_tag_t t, uint32_t f, uint32_t h);

    explicit operator DIERef() const {
      return DIERef(llvm::None, DIERef::Section::DebugInfo, die_offset);
    }
  };

  struct Atom {
    AtomType type;
    dw_form_t form;
  };

  typedef std::vector<DIEInfo> DIEInfoArray;
  typedef std::vector<Atom> AtomArray;

  class Prologue {
  public:
    Prologue(dw_offset_t _die_base_offset = 0);

    void ClearAtoms();

    bool ContainsAtom(AtomType atom_type) const;

    void Clear();

    void AppendAtom(AtomType type, dw_form_t form);

    lldb::offset_t Read(const lldb_private::DataExtractor &data,
                        lldb::offset_t offset);

    size_t GetByteSize() const;

    size_t GetMinimumHashDataByteSize() const;

    bool HashDataHasFixedByteSize() const;

    /// DIE offset base so die offsets in hash_data can be CU relative.
    dw_offset_t die_base_offset;
    AtomArray atoms;
    uint32_t atom_mask = 0;
    size_t min_hash_data_byte_size = 0;
    bool hash_data_has_fixed_byte_size = true;
  };

  class Header : public MappedHash::Header<Prologue> {
  public:
    size_t GetByteSize(const HeaderData &header_data) override;

    lldb::offset_t Read(lldb_private::DataExtractor &data,
                        lldb::offset_t offset) override;

    bool Read(const lldb_private::DWARFDataExtractor &data,
              lldb::offset_t *offset_ptr, DIEInfo &hash_data) const;
  };

  /// A class for reading and using a saved hash table from a block of data in
  /// memory.
  class MemoryTable
      : public MappedHash::MemoryTable<uint32_t, DWARFMappedHash::Header,
                                       DIEInfoArray> {
  public:
    MemoryTable(lldb_private::DWARFDataExtractor &table_data,
                const lldb_private::DWARFDataExtractor &string_table,
                const char *name);

    const char *GetStringForKeyType(KeyType key) const override;

    bool ReadHashData(uint32_t hash_data_offset,
                      HashData &hash_data) const override;

    void
    AppendAllDIEsThatMatchingRegex(const lldb_private::RegularExpression &regex,
                                   DIEInfoArray &die_info_array) const;

    void AppendAllDIEsInRange(const uint32_t die_offset_start,
                              const uint32_t die_offset_end,
                              DIEInfoArray &die_info_array) const;

    bool FindByName(llvm::StringRef name,
                    llvm::function_ref<bool(DIERef ref)> callback);

    void FindByNameAndTag(llvm::StringRef name, const dw_tag_t tag,
                          llvm::function_ref<bool(DIERef ref)> callback);

    void FindByNameAndTagAndQualifiedNameHash(
        llvm::StringRef name, const dw_tag_t tag,
        const uint32_t qualified_name_hash,
        llvm::function_ref<bool(DIERef ref)> callback);

    void
    FindCompleteObjCClassByName(llvm::StringRef name,
                                llvm::function_ref<bool(DIERef ref)> callback,
                                bool must_be_implementation);

  protected:
    Result AppendHashDataForRegularExpression(
        const lldb_private::RegularExpression &regex,
        lldb::offset_t *hash_data_offset_ptr, Pair &pair) const;

    void FindByName(llvm::StringRef name, DIEInfoArray &die_info_array);

    Result GetHashDataForName(llvm::StringRef name,
                              lldb::offset_t *hash_data_offset_ptr,
                              Pair &pair) const override;

    lldb_private::DWARFDataExtractor m_data;
    lldb_private::DWARFDataExtractor m_string_table;
    std::string m_name;
  };

  static bool ExtractDIEArray(const DIEInfoArray &die_info_array,
                              llvm::function_ref<bool(DIERef ref)> callback);

protected:
  static void ExtractDIEArray(const DIEInfoArray &die_info_array,
                              const dw_tag_t tag,
                              llvm::function_ref<bool(DIERef ref)> callback);

  static void ExtractDIEArray(const DIEInfoArray &die_info_array,
                              const dw_tag_t tag,
                              const uint32_t qualified_name_hash,
                              llvm::function_ref<bool(DIERef ref)> callback);

  static void
  ExtractClassOrStructDIEArray(const DIEInfoArray &die_info_array,
                               bool return_implementation_only_if_available,
                               llvm::function_ref<bool(DIERef ref)> callback);

  static void
  ExtractTypesFromDIEArray(const DIEInfoArray &die_info_array,
                           uint32_t type_flag_mask, uint32_t type_flag_value,
                           llvm::function_ref<bool(DIERef ref)> callback);

  static const char *GetAtomTypeName(uint16_t atom);
};

#endif // LLDB_SOURCE_PLUGINS_SYMBOLFILE_DWARF_HASHEDNAMETODIE_H
