//===- ASTReaderInternals.h - AST Reader Internals --------------*- 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
//
//===----------------------------------------------------------------------===//
//
//  This file provides internal definitions used in the AST reader.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_LIB_SERIALIZATION_ASTREADERINTERNALS_H
#define LLVM_CLANG_LIB_SERIALIZATION_ASTREADERINTERNALS_H

#include "MultiOnDiskHashTable.h"
#include "clang/AST/DeclarationName.h"
#include "clang/Basic/LLVM.h"
#include "clang/Serialization/ASTBitCodes.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/OnDiskHashTable.h"
#include <ctime>
#include <utility>

namespace clang {

class ASTReader;
class FileEntry;
struct HeaderFileInfo;
class HeaderSearch;
class ObjCMethodDecl;
class Module;

namespace serialization {

class ModuleFile;

namespace reader {

class ASTDeclContextNameLookupTraitBase {
protected:
  ASTReader &Reader;
  ModuleFile &F;

public:
  // Maximum number of lookup tables we allow before condensing the tables.
  static const int MaxTables = 4;

  /// The lookup result is a list of global declaration IDs.
  using data_type = SmallVector<GlobalDeclID, 4>;

  struct data_type_builder {
    data_type &Data;
    llvm::DenseSet<GlobalDeclID> Found;

    data_type_builder(data_type &D) : Data(D) {}

    void insert(GlobalDeclID ID) {
      // Just use a linear scan unless we have more than a few IDs.
      if (Found.empty() && !Data.empty()) {
        if (Data.size() <= 4) {
          for (auto I : Found)
            if (I == ID)
              return;
          Data.push_back(ID);
          return;
        }

        // Switch to tracking found IDs in the set.
        Found.insert_range(Data);
      }

      if (Found.insert(ID).second)
        Data.push_back(ID);
    }
  };
  using hash_value_type = unsigned;
  using offset_type = unsigned;
  using file_type = ModuleFile *;

protected:
  explicit ASTDeclContextNameLookupTraitBase(ASTReader &Reader, ModuleFile &F)
      : Reader(Reader), F(F) {}

public:
  static std::pair<unsigned, unsigned>
  ReadKeyDataLength(const unsigned char *&d);

  void ReadDataIntoImpl(const unsigned char *d, unsigned DataLen,
                        data_type_builder &Val);

  static void MergeDataInto(const data_type &From, data_type_builder &To) {
    To.Data.reserve(To.Data.size() + From.size());
    for (GlobalDeclID ID : From)
      To.insert(ID);
  }

  file_type ReadFileRef(const unsigned char *&d);

  DeclarationNameKey ReadKeyBase(const unsigned char *&d);
};

/// Class that performs name lookup into a DeclContext stored
/// in an AST file.
class ASTDeclContextNameLookupTrait : public ASTDeclContextNameLookupTraitBase {
public:
  explicit ASTDeclContextNameLookupTrait(ASTReader &Reader, ModuleFile &F)
      : ASTDeclContextNameLookupTraitBase(Reader, F) {}

  using external_key_type = DeclarationName;
  using internal_key_type = DeclarationNameKey;

  static bool EqualKey(const internal_key_type &a, const internal_key_type &b) {
    return a == b;
  }

  static hash_value_type ComputeHash(const internal_key_type &Key) {
    return Key.getHash();
  }

  static internal_key_type GetInternalKey(const external_key_type &Name) {
    return Name;
  }

  internal_key_type ReadKey(const unsigned char *d, unsigned);

  void ReadDataInto(internal_key_type, const unsigned char *d,
                    unsigned DataLen, data_type_builder &Val);
};

struct DeclContextLookupTable {
  MultiOnDiskHashTable<ASTDeclContextNameLookupTrait> Table;
};

class ModuleLocalNameLookupTrait : public ASTDeclContextNameLookupTraitBase {
public:
  explicit ModuleLocalNameLookupTrait(ASTReader &Reader, ModuleFile &F)
      : ASTDeclContextNameLookupTraitBase(Reader, F) {}

  using external_key_type = std::pair<DeclarationName, const Module *>;
  using internal_key_type = std::pair<DeclarationNameKey, unsigned>;

  static bool EqualKey(const internal_key_type &a, const internal_key_type &b) {
    return a == b;
  }

  static hash_value_type ComputeHash(const internal_key_type &Key);
  static internal_key_type GetInternalKey(const external_key_type &Key);

  internal_key_type ReadKey(const unsigned char *d, unsigned);

  void ReadDataInto(internal_key_type, const unsigned char *d, unsigned DataLen,
                    data_type_builder &Val);
};

struct ModuleLocalLookupTable {
  MultiOnDiskHashTable<ModuleLocalNameLookupTrait> Table;
};

using LazySpecializationInfo = GlobalDeclID;

/// Class that performs lookup to specialized decls.
class LazySpecializationInfoLookupTrait {
  ASTReader &Reader;
  ModuleFile &F;

public:
  // Maximum number of lookup tables we allow before condensing the tables.
  static const int MaxTables = 4;

  /// The lookup result is a list of global declaration IDs.
  using data_type = SmallVector<LazySpecializationInfo, 4>;

  struct data_type_builder {
    data_type &Data;
    llvm::DenseSet<LazySpecializationInfo> Found;

    data_type_builder(data_type &D) : Data(D) {}

    void insert(LazySpecializationInfo Info) {
      // Just use a linear scan unless we have more than a few IDs.
      if (Found.empty() && !Data.empty()) {
        if (Data.size() <= 4) {
          for (auto I : Found)
            if (I == Info)
              return;
          Data.push_back(Info);
          return;
        }

        // Switch to tracking found IDs in the set.
        Found.insert_range(Data);
      }

      if (Found.insert(Info).second)
        Data.push_back(Info);
    }
  };
  using hash_value_type = unsigned;
  using offset_type = unsigned;
  using file_type = ModuleFile *;

  using external_key_type = unsigned;
  using internal_key_type = unsigned;

  explicit LazySpecializationInfoLookupTrait(ASTReader &Reader, ModuleFile &F)
      : Reader(Reader), F(F) {}

  static bool EqualKey(const internal_key_type &a, const internal_key_type &b) {
    return a == b;
  }

  static hash_value_type ComputeHash(const internal_key_type &Key) {
    return Key;
  }

  static internal_key_type GetInternalKey(const external_key_type &Name) {
    return Name;
  }

  static std::pair<unsigned, unsigned>
  ReadKeyDataLength(const unsigned char *&d);

  internal_key_type ReadKey(const unsigned char *d, unsigned);

  void ReadDataInto(internal_key_type, const unsigned char *d, unsigned DataLen,
                    data_type_builder &Val);

  static void MergeDataInto(const data_type &From, data_type_builder &To) {
    To.Data.reserve(To.Data.size() + From.size());
    for (LazySpecializationInfo Info : From)
      To.insert(Info);
  }

  file_type ReadFileRef(const unsigned char *&d);
};

struct LazySpecializationInfoLookupTable {
  MultiOnDiskHashTable<LazySpecializationInfoLookupTrait> Table;
};

/// Base class for the trait describing the on-disk hash table for the
/// identifiers in an AST file.
///
/// This class is not useful by itself; rather, it provides common
/// functionality for accessing the on-disk hash table of identifiers
/// in an AST file. Different subclasses customize that functionality
/// based on what information they are interested in. Those subclasses
/// must provide the \c data_type type and the ReadData operation, only.
class ASTIdentifierLookupTraitBase {
public:
  using external_key_type = StringRef;
  using internal_key_type = StringRef;
  using hash_value_type = unsigned;
  using offset_type = unsigned;

  static bool EqualKey(const internal_key_type& a, const internal_key_type& b) {
    return a == b;
  }

  static hash_value_type ComputeHash(const internal_key_type& a);

  static std::pair<unsigned, unsigned>
  ReadKeyDataLength(const unsigned char*& d);

  // This hopefully will just get inlined and removed by the optimizer.
  static const internal_key_type&
  GetInternalKey(const external_key_type& x) { return x; }

  // This hopefully will just get inlined and removed by the optimizer.
  static const external_key_type&
  GetExternalKey(const internal_key_type& x) { return x; }

  static internal_key_type ReadKey(const unsigned char* d, unsigned n);
};

/// Class that performs lookup for an identifier stored in an AST file.
class ASTIdentifierLookupTrait : public ASTIdentifierLookupTraitBase {
  ASTReader &Reader;
  ModuleFile &F;

  // If we know the IdentifierInfo in advance, it is here and we will
  // not build a new one. Used when deserializing information about an
  // identifier that was constructed before the AST file was read.
  IdentifierInfo *KnownII;

  bool hasMacroDefinitionInDependencies = false;

public:
  using data_type = IdentifierInfo *;

  ASTIdentifierLookupTrait(ASTReader &Reader, ModuleFile &F,
                           IdentifierInfo *II = nullptr)
      : Reader(Reader), F(F), KnownII(II) {}

  data_type ReadData(const internal_key_type& k,
                     const unsigned char* d,
                     unsigned DataLen);

  IdentifierID ReadIdentifierID(const unsigned char *d);

  ASTReader &getReader() const { return Reader; }

  bool hasMoreInformationInDependencies() const {
    return hasMacroDefinitionInDependencies;
  }
};

/// The on-disk hash table used to contain information about
/// all of the identifiers in the program.
using ASTIdentifierLookupTable =
    llvm::OnDiskIterableChainedHashTable<ASTIdentifierLookupTrait>;

/// Class that performs lookup for a selector's entries in the global
/// method pool stored in an AST file.
class ASTSelectorLookupTrait {
  ASTReader &Reader;
  ModuleFile &F;

public:
  struct data_type {
    SelectorID ID;
    unsigned InstanceBits;
    unsigned FactoryBits;
    bool InstanceHasMoreThanOneDecl;
    bool FactoryHasMoreThanOneDecl;
    SmallVector<ObjCMethodDecl *, 2> Instance;
    SmallVector<ObjCMethodDecl *, 2> Factory;
  };

  using external_key_type = Selector;
  using internal_key_type = external_key_type;
  using hash_value_type = unsigned;
  using offset_type = unsigned;

  ASTSelectorLookupTrait(ASTReader &Reader, ModuleFile &F)
      : Reader(Reader), F(F) {}

  static bool EqualKey(const internal_key_type& a,
                       const internal_key_type& b) {
    return a == b;
  }

  static hash_value_type ComputeHash(Selector Sel);

  static const internal_key_type&
  GetInternalKey(const external_key_type& x) { return x; }

  static std::pair<unsigned, unsigned>
  ReadKeyDataLength(const unsigned char*& d);

  internal_key_type ReadKey(const unsigned char* d, unsigned);
  data_type ReadData(Selector, const unsigned char* d, unsigned DataLen);
};

/// The on-disk hash table used for the global method pool.
using ASTSelectorLookupTable =
    llvm::OnDiskChainedHashTable<ASTSelectorLookupTrait>;

/// Trait class used to search the on-disk hash table containing all of
/// the header search information.
///
/// The on-disk hash table contains a mapping from each header path to
/// information about that header (how many times it has been included, its
/// controlling macro, etc.). Note that we actually hash based on the size
/// and mtime, and support "deep" comparisons of file names based on current
/// inode numbers, so that the search can cope with non-normalized path names
/// and symlinks.
class HeaderFileInfoTrait {
  ASTReader &Reader;
  ModuleFile &M;

public:
  using external_key_type = FileEntryRef;

  struct internal_key_type {
    off_t Size;
    time_t ModTime;
    StringRef Filename;
    bool Imported;
  };

  using internal_key_ref = const internal_key_type &;

  using data_type = HeaderFileInfo;
  using hash_value_type = unsigned;
  using offset_type = unsigned;

  HeaderFileInfoTrait(ASTReader &Reader, ModuleFile &M)
      : Reader(Reader), M(M) {}

  static hash_value_type ComputeHash(internal_key_ref ikey);
  internal_key_type GetInternalKey(external_key_type ekey);
  bool EqualKey(internal_key_ref a, internal_key_ref b);

  static std::pair<unsigned, unsigned>
  ReadKeyDataLength(const unsigned char*& d);

  static internal_key_type ReadKey(const unsigned char *d, unsigned);

  data_type ReadData(internal_key_ref,const unsigned char *d, unsigned DataLen);

private:
  OptionalFileEntryRef getFile(const internal_key_type &Key);
};

/// The on-disk hash table used for known header files.
using HeaderFileInfoLookupTable =
    llvm::OnDiskChainedHashTable<HeaderFileInfoTrait>;

} // namespace reader

} // namespace serialization

} // namespace clang

#endif // LLVM_CLANG_LIB_SERIALIZATION_ASTREADERINTERNALS_H
