/*
 * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

#ifndef SHARE_JFR_SUPPORT_JFRSYMBOLTABLE_HPP
#define SHARE_JFR_SUPPORT_JFRSYMBOLTABLE_HPP

#include "jfr/utilities/jfrHashtable.hpp"
#include "jfr/utilities/jfrTypes.hpp"

template <typename T, typename IdType>
class ListEntry : public JfrHashtableEntry<T, IdType> {
 public:
  ListEntry(uintptr_t hash, const T& data) : JfrHashtableEntry<T, IdType>(hash, data),
    _list_next(nullptr), _serialized(false), _unloading(false), _leakp(false) {}
  const ListEntry<T, IdType>* list_next() const { return _list_next; }
  void reset() const {
    _list_next = nullptr; _serialized = false; _unloading = false; _leakp = false;
  }
  void set_list_next(const ListEntry<T, IdType>* next) const { _list_next = next; }
  bool is_serialized() const { return _serialized; }
  void set_serialized() const { _serialized = true; }
  bool is_unloading() const { return _unloading; }
  void set_unloading() const { _unloading = true; }
  bool is_leakp() const { return _leakp; }
  void set_leakp() const { _leakp = true; }
 private:
  mutable const ListEntry<T, IdType>* _list_next;
  mutable bool _serialized;
  mutable bool _unloading;
  mutable bool _leakp;
};

/*
 * This table maps an oop/Symbol* or a char* to the Jfr type 'Symbol'.
 *
 * It provides an interface over the corresponding constant pool (TYPE_SYMBOL),
 * which is represented in the binary format as a sequence of checkpoint events.
 * The returned id can be used as a foreign key, but please note that the id is
 * epoch-relative, and is therefore only valid in the current epoch / chunk.
 * The table is cleared as part of rotation.
 *
 * Caller must ensure mutual exclusion by means of the ClassLoaderDataGraph_lock or by safepointing.
 */
class JfrSymbolTable : public JfrCHeapObj {
  template <typename, typename, template<typename, typename> class, typename, size_t>
  friend class HashTableHost;
  typedef HashTableHost<const Symbol*, traceid, ListEntry, JfrSymbolTable> Symbols;
  typedef HashTableHost<const char*, traceid, ListEntry, JfrSymbolTable> Strings;
  friend class JfrArtifactSet;

 public:
  typedef Symbols::HashEntry SymbolEntry;
  typedef Strings::HashEntry StringEntry;

  static traceid add(const Symbol* sym);
  static traceid add(const char* str);

 private:
  Symbols* _symbols;
  Strings* _strings;
  const SymbolEntry* _symbol_list;
  const StringEntry* _string_list;
  const Symbol* _symbol_query;
  const char* _string_query;
  traceid _id_counter;
  bool _class_unload;

  JfrSymbolTable();
  ~JfrSymbolTable();
  static JfrSymbolTable* create();
  static void destroy();

  void clear();
  void increment_checkpoint_id();
  void set_class_unload(bool class_unload);

  traceid mark(uintptr_t hash, const Symbol* sym, bool leakp);
  traceid mark(const Klass* k, bool leakp);
  traceid mark(const Symbol* sym, bool leakp = false);
  traceid mark(const char* str, bool leakp = false);
  traceid mark(uintptr_t hash, const char* str, bool leakp);
  traceid mark_hidden_klass_name(const Klass* k, bool leakp);
  traceid bootstrap_name(bool leakp);

  bool has_entries() const { return has_symbol_entries() || has_string_entries(); }
  bool has_symbol_entries() const { return _symbol_list != nullptr; }
  bool has_string_entries() const { return _string_list != nullptr; }

  // hashtable(s) callbacks
  void on_link(const SymbolEntry* entry);
  bool on_equals(uintptr_t hash, const SymbolEntry* entry);
  void on_unlink(const SymbolEntry* entry);
  void on_link(const StringEntry* entry);
  bool on_equals(uintptr_t hash, const StringEntry* entry);
  void on_unlink(const StringEntry* entry);

  template <typename T>
  static traceid add_impl(const T* sym);

  template <typename T>
  void assign_id(T* entry);

  template <typename Functor>
  void iterate_symbols(Functor& functor) {
    iterate(functor, _symbol_list);
  }

  template <typename Functor>
  void iterate_strings(Functor& functor) {
    iterate(functor, _string_list);
  }

  template <typename Functor, typename T>
  void iterate(Functor& functor, const T* list) {
    const T* symbol = list;
    while (symbol != nullptr) {
      const T* next = symbol->list_next();
      functor(symbol);
      symbol = next;
    }
  }
};

#endif // SHARE_JFR_SUPPORT_JFRSYMBOLTABLE_HPP
