/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef nsGkAtoms_h___
#define nsGkAtoms_h___

#include "nsAtom.h"

// Static atoms are structured carefully to satisfy a lot of constraints.
//
// - We have ~2300 static atoms.
//
// - We want them to be constexpr so they end up in .rodata, and thus shared
//   between processes, minimizing memory usage.
//
// - We need them to be in an array, so we can iterate over them (for
//   registration and lookups).
//
// - Each static atom has a string literal associated with it. We can't use a
//   pointer to the string literal because then the atoms won't end up in
//   .rodata. Therefore the string literals and the atoms must be arranged in a
//   way such that a numeric index can be used instead. This numeric index
//   (nsStaticAtom::mStringOffset) must be computable at compile-time to keep
//   the static atom constexpr. It should also not be too large (a uint32_t is
//   reasonable).
//
// - Each static atom stores the hash value of its associated string literal;
//   it's used in various ways. The hash value must be specified at
//   compile-time, to keep the static atom constexpr.
//
// - As well as accessing each static atom via array indexing, we need an
//   individual pointer, e.g. nsGkAtoms::foo. We want this to be constexpr so
//   it doesn't take up any space in memory.
//
// - The array of static atoms can't be in a .h file, because it's a huge
//   constexpr expression, which would blow out compile times. But the
//   individual pointers for the static atoms must be in a .h file so they are
//   public.
//
// nsGkAtoms below defines static atoms in a way that satisfies these
// constraints. It uses nsGkAtomList.h, which defines the names and values of
// the atoms. nsGkAtomList.h is generated by StaticAtoms.py and has entries
// that look like this:
//
//   GK_ATOM(a, "a", 0x01234567, nsStaticAtom, Atom)
//   GK_ATOM(bb, "bb", 0x12345678, nsCSSPseudoElementStaticAtom,
//           PseudoElementAtom)
//   GK_ATOM(Ccc, "Ccc", 0x23456789, nsCSSAnonBoxPseudoStaticAtom,
//           InheritingAnonBoxAtom)
//
// Comments throughout this file and nsGkAtoms.cpp show how these entries get
// expanded by macros.

// Trivial subclasses of nsStaticAtom so that function signatures can require
// an atom from a specific atom list.
#define DEFINE_STATIC_ATOM_SUBCLASS(name_)                              \
  class name_ : public nsStaticAtom {                                   \
   public:                                                              \
    constexpr name_(uint32_t aLength, uint32_t aHash, uint32_t aOffset, \
                    bool aIsAsciiLowercase)                             \
        : nsStaticAtom(aLength, aHash, aOffset, aIsAsciiLowercase) {}   \
  };

DEFINE_STATIC_ATOM_SUBCLASS(nsCSSAnonBoxPseudoStaticAtom)
DEFINE_STATIC_ATOM_SUBCLASS(nsCSSPseudoElementStaticAtom)

#undef DEFINE_STATIC_ATOM_SUBCLASS

namespace mozilla {
namespace detail {

// This `detail` class contains the atom strings and the atom objects. Because
// they are together in a class, the `mStringOffset` field of the atoms will be
// small and can be initialized at compile time.
//
// A `detail` namespace is used because the things within it aren't directly
// referenced by external users of these static atoms.
struct GkAtoms {
// The declaration of each atom's string.
//
// Expansion of the example GK_ATOM entries from above:
//
//   const char16_t a_string[sizeof("a")];
//   const char16_t bb_string[sizeof("bb")];
//   const char16_t Ccc_string[sizeof("Ccc")];
//
#define GK_ATOM(name_, value_, hash_, is_ascii_lower_, type_, atom_type_) \
  const char16_t name_##_string[sizeof(value_)];
#include "nsGkAtomList.h"
#undef GK_ATOM

  // The enum value for each atom.
  enum class Atoms {
// Expansion of the example GK_ATOM entries above:
//
//   a,
//   bb,
//   Ccc,
//
#define GK_ATOM(name_, value_, hash_, is_ascii_lower_, type_, atom_type_) name_,
#include "nsGkAtomList.h"
#undef GK_ATOM
    AtomsCount
  };

  const nsStaticAtom mAtoms[static_cast<size_t>(Atoms::AtomsCount)];
};

// The offset from the start of the GkAtoms object to the start of the
// nsStaticAtom array inside it.  This is used in Rust to avoid problems
// with lld-link.exe on Windows when rust-bindgen generates a non-opaque
// version of GkAtoms.
//
// https://bugzilla.mozilla.org/show_bug.cgi?id=1517685
const ptrdiff_t kGkAtomsArrayOffset = offsetof(GkAtoms, mAtoms);

// The GkAtoms instance is `extern const` so it can be defined in a .cpp file.
//
// XXX: The NS_EXTERNAL_VIS is necessary to work around an apparent GCC bug:
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87494
#if defined(__GNUC__) && !defined(__clang__)
extern NS_EXTERNAL_VIS const GkAtoms gGkAtoms;
#else
extern const GkAtoms gGkAtoms;
#endif

}  // namespace detail
}  // namespace mozilla

// This class holds the pointers to the individual atoms.
class nsGkAtoms {
 private:
  friend void NS_InitAtomTable();

  // This is a useful handle to the array of atoms, used below and also
  // possibly by Rust code.
  static const nsStaticAtom* const sAtoms;

  // The number of atoms, used below.
  static constexpr size_t sAtomsLen =
      static_cast<size_t>(mozilla::detail::GkAtoms::Atoms::AtomsCount);

 public:
  static nsStaticAtom* GetAtomByIndex(size_t aIndex) {
    MOZ_ASSERT(aIndex < sAtomsLen);
    return const_cast<nsStaticAtom*>(&sAtoms[aIndex]);
  }

  static size_t IndexOf(const nsStaticAtom* atom) {
    nsStaticAtom* firstAtom = GetAtomByIndex(0);
    size_t ret = atom - firstAtom;
    MOZ_ASSERT(ret < sAtomsLen);
    return ret;
  }

// The definition of the pointer to each static atom.
//
// These types are not `static constexpr <type>* const` -- even though these
// atoms are immutable -- because they are often passed to functions with
// `nsAtom*` parameters that can be passed both dynamic and static atoms.
//
// Expansion of the example GK_ATOM entries above:
//
//   static constexpr nsStaticAtom* a =
//     const_cast<nsStaticAtom*>(
//       &mozilla::detail::gGkAtoms.mAtoms[
//         static_cast<size_t>(mozilla::detail::GkAtoms::Atoms::a)]);
//
//   static constexpr nsStaticAtom* bb =
//     const_cast<nsStaticAtom*>(
//       &mozilla::detail::gGkAtoms.mAtoms[
//         static_cast<size_t>(mozilla::detail::GkAtoms::Atoms::bb)]);
//
//   static constexpr nsStaticAtom* Ccc =
//     const_cast<nsStaticAtom*>(
//       &mozilla::detail::gGkAtoms.mAtoms[
//         static_cast<size_t>(mozilla::detail::GkAtoms::Atoms::Ccc)]);
//
#define GK_ATOM(name_, value_, hash_, is_ascii_lower_, type_, atom_type_) \
  static constexpr nsStaticAtom* name_ = const_cast<nsStaticAtom*>(       \
      &mozilla::detail::gGkAtoms.mAtoms[static_cast<size_t>(              \
          mozilla::detail::GkAtoms::Atoms::name_)]);
#include "nsGkAtomList.h"
#undef GK_ATOM
};

inline bool nsAtom::IsEmpty() const { return this == nsGkAtoms::_empty; }

#endif /* nsGkAtoms_h___ */
