/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "nsISupports.idl"

%{C++
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsStringBuffer.h"
%}

native MallocSizeOf(mozilla::MallocSizeOf);

/*
 * Should this really be scriptable?  Using atoms from script or proxies
 * could be dangerous since double-wrapping could lead to loss of
 * pointer identity.
 */

[scriptable, builtinclass, uuid(8b8c11d4-3ed5-4079-8974-73c7576cdb34)]
interface nsIAtom : nsISupports
{
  /**
   * Get the Unicode or UTF8 value for the string
   */
  [binaryname(ScriptableToString)] AString toString();
  [noscript] AUTF8String toUTF8String();

  /**
   * Compare the atom to a specific string value
   * Note that this will NEVER return/throw an error condition.
   */
  [binaryname(ScriptableEquals)] boolean equals(in AString aString);

  [noscript, notxpcom]
  size_t SizeOfIncludingThis(in MallocSizeOf aMallocSizeOf);

%{C++
  // The kind of atom we have, in order to be able to devirtualize hot stuff
  // looking at mKind.
  enum class AtomKind : uint8_t {
    DynamicAtom = 0,
    StaticAtom = 1,
    HTML5Atom = 2,
  };

  // note these are NOT virtual so they won't muck with the vtable!
  inline bool Equals(char16ptr_t aString, uint32_t aLength) const
  {
    return mLength == aLength &&
           memcmp(mString, aString, mLength * sizeof(char16_t)) == 0;
  }

  inline bool Equals(const nsAString& aString) const {
    return Equals(aString.BeginReading(), aString.Length());
  }

  inline void SetKind(AtomKind aKind) {
    mKind = static_cast<uint32_t>(aKind);
    MOZ_ASSERT(Kind() == aKind);
  }

  inline AtomKind Kind() const {
    return static_cast<AtomKind>(mKind);
  }

  inline bool IsDynamicAtom() const {
    return Kind() == AtomKind::DynamicAtom;
  }

  inline bool IsHTML5Atom() const {
    return Kind() == AtomKind::HTML5Atom;
  }

  inline bool IsStaticAtom() const {
    return Kind() == AtomKind::StaticAtom;
  }

  inline char16ptr_t GetUTF16String() const {
    return mString;
  }

  inline uint32_t GetLength() const {
    return mLength;
  }

  inline void ToString(nsAString& aString) const {
    // See the comment on |mString|'s declaration.
    if (IsStaticAtom()) {
      // AssignLiteral() lets us assign without copying. This isn't a string
      // literal, but it's a static atom and thus has an unbounded lifetime,
      // which is what's important.
      aString.AssignLiteral(mString, mLength);
    } else {
      nsStringBuffer::FromData(mString)->ToString(mLength, aString);
    }
  }

  // This is only valid for dynamic atoms.
  inline nsStringBuffer* GetStringBuffer() const {
    // See the comment on |mString|'s declaration.
    MOZ_ASSERT(IsDynamicAtom());
    return nsStringBuffer::FromData(mString);
  }

  NS_IMETHOD_(MozExternalRefCountType) AddRef() final;
  NS_IMETHOD_(MozExternalRefCountType) Release() final;

  /**
   * A hashcode that is better distributed than the actual atom
   * pointer, for use in situations that need a well-distributed
   * hashcode.
   */
  inline uint32_t hash() const {
    MOZ_ASSERT(!IsHTML5Atom());
    return mHash;
  }

protected:
  uint32_t mLength: 30;
  uint32_t mKind: 2; // nsIAtom::AtomKind
  uint32_t mHash;
  /**
   * WARNING! For static atoms, this is a pointer to a static char buffer. For
   * non-static atoms it points to the chars in an nsStringBuffer. This means
   * that nsStringBuffer::FromData(mString) calls are only valid for non-static
   * atoms.
   */
  char16_t* mString;
%}
};


%{C++
class nsAtom final : public nsIAtom
{
public:
  NS_DECL_NSIATOM
  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) final;
  typedef mozilla::TrueType HasThreadSafeRefCnt;

private:
  friend class nsIAtom;
  friend class nsAtomFriend;
  friend class nsHtml5AtomEntry;

  // Construction and destruction is done entirely by |friend|s.
  nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash);
  nsAtom(const char16_t* aString, uint32_t aLength, uint32_t aHash);
  ~nsAtom();

  MozExternalRefCountType DynamicAddRef();
  MozExternalRefCountType DynamicRelease();

  mozilla::ThreadSafeAutoRefCnt mRefCnt;
  NS_DECL_OWNINGTHREAD
};

%}


%{C++
/*
 * The four forms of NS_Atomize (for use with |nsCOMPtr<nsIAtom>|) return the
 * atom for the string given. At any given time there will always be one atom
 * representing a given string. Atoms are intended to make string comparison
 * cheaper by simplifying it to pointer equality. A pointer to the atom that
 * does not own a reference is not guaranteed to be valid.
 */


/**
 * Find an atom that matches the given UTF-8 string.
 * The string is assumed to be zero terminated.  Never returns null.
 */
extern already_AddRefed<nsIAtom> NS_Atomize(const char* aUTF8String);

/**
 * Find an atom that matches the given UTF-8 string.  Never returns null.
 */
extern already_AddRefed<nsIAtom> NS_Atomize(const nsACString& aUTF8String);

/**
 * Find an atom that matches the given UTF-16 string.
 * The string is assumed to be zero terminated.  Never returns null.
 */
extern already_AddRefed<nsIAtom> NS_Atomize(const char16_t* aUTF16String);

/**
 * Find an atom that matches the given UTF-16 string.  Never returns null.
 */
extern already_AddRefed<nsIAtom> NS_Atomize(const nsAString& aUTF16String);

/**
 * An optimized version of the method above for the main thread.
 */
extern already_AddRefed<nsIAtom> NS_AtomizeMainThread(const nsAString& aUTF16String);

/**
 * Return a count of the total number of atoms currently
 * alive in the system.
 */
extern nsrefcnt NS_GetNumberOfAtoms(void);

/**
 * Return a pointer for a static atom for the string or null if there's
 * no static atom for this string.
 */
extern nsIAtom* NS_GetStaticAtom(const nsAString& aUTF16String);

/**
 * Seal the static atom table
 */
extern void NS_SealStaticAtomTable();

class nsAtomString : public nsString
{
public:
  explicit nsAtomString(const nsIAtom* aAtom)
  {
    aAtom->ToString(*this);
  }
};

class nsAtomCString : public nsCString
{
public:
  explicit nsAtomCString(nsIAtom* aAtom)
  {
    aAtom->ToUTF8String(*this);
  }
};

class nsDependentAtomString : public nsDependentString
{
public:
  explicit nsDependentAtomString(const nsIAtom* aAtom)
    : nsDependentString(aAtom->GetUTF16String(), aAtom->GetLength())
  {
  }
};

%}
